You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

606 lines
16 KiB

1 month ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
1 month ago
​
​
​
​
1 month ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
1 month ago
1 month ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
1 month ago
​
​
​
​
​
​
​
1 month ago
​
1 month ago
​
​
1 month ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
1 month ago
1 month ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
1 month ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
1 month ago
​
​
​
​
1 month ago
​
​
​
​
​
​
​
​
1 month ago
​
​
1 month ago
​
1 month ago
​
1 month ago
​
​
1 month ago
1 month ago
​
​
1 month ago
​
​
​
​
​
​
​
​
​
1 month ago
​
​
​
​
​
​
​
1 month ago
1 month ago
​
1 month ago
​
​
1 month ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
1 month ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
  1. <template>
  2. <div>
  3. <div class="pda-container">
  4. <!-- 头部栏 -->
  5. <div class="header-bar">
  6. <div class="header-left" @click="handleBack">
  7. <i class="el-icon-arrow-left"></i>
  8. <span>查看托盘</span>
  9. </div>
  10. <div class="header-right" @click="$router.push({ path: '/' })">
  11. 首页
  12. </div>
  13. </div>
  14. <div class="table-body" style="max-height: 500px; overflow-y: auto;">
  15. <div class="main-content form-section">
  16. <!-- 栈板扫描 - rqrq -->
  17. <div class="input-group">
  18. <label class="input-label">栈板编码</label>
  19. <el-input
  20. v-model="palletCode"
  21. placeholder="请扫描栈板编码"
  22. class="form-input"
  23. clearable
  24. @keyup.enter.native="handlePalletScan"
  25. ref="palletInput"
  26. />
  27. </div>
  28. <!-- 筛选条件 (扫描栈板后显示) - rqrq -->
  29. <div v-if="palletScanned" class="input-group">
  30. <label class="input-label">位置</label>
  31. <div style="display: flex; gap: 8px;">
  32. <el-select
  33. v-model="selectedPosition"
  34. placeholder="请选择位置"
  35. style="flex: 0.75;"
  36. >
  37. <el-option label="ALL" value=""></el-option>
  38. <el-option
  39. v-for="position in positionOptions"
  40. :key="position"
  41. :label="position"
  42. :value="position"
  43. />
  44. </el-select>
  45. <button
  46. class="action-btn secondary"
  47. style="flex: 0.25; margin: 0; white-space: nowrap;"
  48. @click="refreshTable"
  49. >
  50. 刷新
  51. </button>
  52. </div>
  53. </div>
  54. </div>
  55. <!-- 栈板明细表格 (扫描栈板后显示) - rqrq -->
  56. <div v-if="palletScanned" class="rma-list">
  57. <div class="list-title-row" style="display: flex; gap: 8px; align-items: center; padding: 0;">
  58. <div class="list-title" style="flex: 1; margin: 0; display: flex; align-items: center; flex-wrap: wrap; gap: 5px;">
  59. <button class="action-btn primary" style="margin: 0;" @click="showDetailModal" >
  60. {{ '浏览明细' }}
  61. </button>
  62. <label style="margin-left: 5px;">条码数:{{detailList.length}}</label>
  63. <!-- 预定清单按钮 - rqrq -->
  64. <button
  65. class="action-btn"
  66. :class="reserveCount > 0 ? 'warning' : 'secondary'"
  67. style="margin: 0; margin-left: 10px;"
  68. @click="showReserveModal">
  69. 预定清单({{ reserveCount }})
  70. </button>
  71. </div>
  72. </div>
  73. <div class="detail-table">
  74. <div class="table-header">
  75. <div class="col-position">位置</div>
  76. <div class="col-layer">层数</div>
  77. <div class="col-serial">标签号</div>
  78. </div>
  79. <div
  80. v-for="(detail, index) in detailList"
  81. :key="index"
  82. class="table-row"
  83. >
  84. <div class="col-position">{{ detail.position }}</div>
  85. <div class="col-layer">{{ detail.layer }}</div>
  86. <div class="col-serial">{{ detail.serialNo }}</div>
  87. </div>
  88. <!-- 暂无数据提示 -->
  89. <div v-if="detailList.length === 0" class="table-row empty-row">
  90. <div class="empty-hint">暂无栈板明细数据</div>
  91. </div>
  92. </div>
  93. </div>
  94. </div>
  95. </div>
  96. <!-- 浏览明细弹窗 - rqrq -->
  97. <el-dialog
  98. :title="'栈板明细 (共'+detailList.length+'条)'"
  99. :visible.sync="detailModalVisible"
  100. width="90%"
  101. :close-on-click-modal="false"
  102. :show-close="false"
  103. :modal="true"
  104. :modal-append-to-body="true"
  105. :append-to-body="true"
  106. >
  107. <div class="table-body" style="max-height: 400px; overflow-y: auto;">
  108. <div class="detail-table">
  109. <div class="table-header">
  110. <div class="col-position">位置</div>
  111. <div class="col-layer">层数</div>
  112. <div class="col-serial">标签号</div>
  113. </div>
  114. <div
  115. v-for="(detail, index) in detailList"
  116. :key="index"
  117. class="table-row"
  118. >
  119. <div class="col-position">{{ detail.position }}</div>
  120. <div class="col-layer">{{ detail.layer }}</div>
  121. <div class="col-serial">{{ detail.serialNo }}</div>
  122. </div>
  123. <!-- 暂无数据提示 -->
  124. <div v-if="detailList.length === 0" class="table-row empty-row">
  125. <div class="empty-hint">暂无栈板明细数据</div>
  126. </div>
  127. </div>
  128. </div>
  129. <div slot="footer" class="dialog-footer">
  130. <button class="action-btn secondary" @click="detailModalVisible=false">关闭</button>
  131. </div>
  132. </el-dialog>
  133. <!-- 预定清单弹窗 - rqrq -->
  134. <el-dialog
  135. :title="'预定清单 (共'+reserveCount+'条)'"
  136. :visible.sync="reserveModalVisible"
  137. width="95%"
  138. :close-on-click-modal="false"
  139. :show-close="false"
  140. :modal="true"
  141. :modal-append-to-body="true"
  142. :append-to-body="true"
  143. >
  144. <div class="table-body" style="max-height: 350px; overflow-y: auto;">
  145. <div class="detail-table">
  146. <div class="table-header reserve-header">
  147. <div class="col-serial-reserve">标签号</div>
  148. <div class="col-ref">申请单号</div>
  149. <div class="col-ref">订单号</div>
  150. </div>
  151. <div
  152. v-for="(item, index) in reservedLabels"
  153. :key="index"
  154. class="table-row"
  155. >
  156. <div class="col-serial-reserve">{{ item.serialNo || '-' }}</div>
  157. <div class="col-ref">{{ item.reserveOrderRef1 || '-' }}</div>
  158. <div class="col-ref">{{ item.reserveOrderRef3 || '-' }}</div>
  159. </div>
  160. <!-- 暂无数据提示 -->
  161. <div v-if="reservedLabels.length === 0" class="table-row empty-row">
  162. <div class="empty-hint">暂无预定标签</div>
  163. </div>
  164. </div>
  165. </div>
  166. <div slot="footer" class="dialog-footer" style="display: flex; gap: 10px; justify-content: center;">
  167. <button class="action-btn secondary" @click="reserveModalVisible=false" :disabled="cancelReserveLoading">关闭</button>
  168. <button
  169. class="action-btn danger"
  170. @click="handleBatchCancelReserve"
  171. :disabled="cancelReserveLoading || reservedLabels.length === 0 || errorStationFlag"
  172. v-if="reservedLabels.length > 0"
  173. >
  174. {{ cancelReserveLoading ? '取消中...' : '一键取消预定' }}
  175. </button>
  176. </div>
  177. <!-- 异常位提示:以后端 errorStationFlag 为准,不再写死 1101 - rqrq -->
  178. <div v-if="errorStationFlag" class="reserve-warning">
  179. <i class="el-icon-warning"></i> 栈板在立库异常位上,不允许取消预定
  180. </div>
  181. </el-dialog>
  182. </div>
  183. </template>
  184. <script>
  185. import {
  186. getPalletInfoSimple,
  187. getPalletDetails,
  188. getPalletReservedLabels,
  189. batchCancelReserve
  190. } from '../../../api/haianAutoWarehouse/palletPacking'
  191. export default {
  192. name: 'haianPalletSearch',
  193. data() {
  194. return {
  195. site: this.$store.state.user.site,
  196. palletCode: '',
  197. palletScanned: false,
  198. detailModalVisible: false,
  199. // 筛选条件 - rqrq
  200. selectedPosition: '',
  201. positionOptions: [],
  202. // 栈板明细 - rqrq
  203. detailList: [],
  204. // 栈板库位 - rqrq
  205. locationCode: '',
  206. // 是否异常位:由后端 ERROR_STATIONS 判定,禁止前端写死 1101 - rqrq
  207. errorStationFlag: false,
  208. // 预定清单相关 - rqrq
  209. reserveModalVisible: false,
  210. reservedLabels: [],
  211. reserveCount: 0,
  212. cancelReserveLoading: false
  213. };
  214. },
  215. methods: {
  216. // 返回上一页 - rqrq
  217. handleBack() {
  218. // 返回菜单前清空托盘查询状态,避免keep-alive缓存导致数据残留 - rqrq
  219. this.resetPage(false)
  220. this.$router.back();
  221. },
  222. // 重置页面到初始状态 - rqrq
  223. resetPage(needFocus = true) {
  224. this.site = this.$store.state.user.site;
  225. this.palletCode = '';
  226. this.palletScanned = false;
  227. this.selectedPosition = '';
  228. this.positionOptions = [];
  229. this.detailList = [];
  230. this.detailModalVisible = false;
  231. this.locationCode = '';
  232. this.reserveModalVisible = false;
  233. this.reservedLabels = [];
  234. this.reserveCount = 0;
  235. this.cancelReserveLoading = false;
  236. // 聚焦到栈板输入框 - rqrq
  237. if (needFocus) {
  238. this.$nextTick(() => {
  239. if (this.$refs.palletInput) {
  240. this.$refs.palletInput.focus();
  241. }
  242. });
  243. }
  244. },
  245. // 扫描栈板(使用无校验的简单查询)- rqrq
  246. handlePalletScan() {
  247. if (!this.site) {
  248. this.$alert('当前用户缺少site,无法执行海安托盘查询', '提示', {
  249. confirmButtonText: '确定'
  250. });
  251. return;
  252. }
  253. if (!this.palletCode.trim()) {
  254. this.$alert('请输入栈板编码', '提示', {
  255. confirmButtonText: '确定'
  256. });
  257. return;
  258. }
  259. getPalletInfoSimple({
  260. site: this.site,
  261. palletId: this.palletCode
  262. }).then(({ data }) => {
  263. if (data.code === 0) {
  264. this.palletCode = data.palletId;
  265. this.palletScanned = true;
  266. this.positionOptions = data.positions || [];
  267. this.locationCode = data.locationCode || '';
  268. this.refreshTable();
  269. this.loadReservedLabels(); // 加载预定标签数量 - rqrq
  270. } else {
  271. let errorMsg = data.msg || '栈板不存在';
  272. if (errorMsg.length > 100) {
  273. errorMsg = errorMsg.substring(0, 100) + '...';
  274. }
  275. this.$alert(errorMsg, '错误', {
  276. confirmButtonText: '确定',
  277. callback: () => {
  278. this.palletCode = '';
  279. this.$nextTick(() => {
  280. if (this.$refs.palletInput) {
  281. this.$refs.palletInput.focus();
  282. }
  283. });
  284. }
  285. });
  286. }
  287. }).catch(error => {
  288. console.error('查询栈板失败:', error);
  289. let errorMsg = error.message || '查询栈板失败';
  290. if (errorMsg.length > 100) {
  291. errorMsg = errorMsg.substring(0, 100) + '...';
  292. }
  293. this.$alert(errorMsg, '错误', {
  294. confirmButtonText: '确定',
  295. callback: () => {
  296. this.palletCode = '';
  297. this.$nextTick(() => {
  298. if (this.$refs.palletInput) {
  299. this.$refs.palletInput.focus();
  300. }
  301. });
  302. }
  303. });
  304. });
  305. },
  306. // 刷新表格 - rqrq
  307. refreshTable() {
  308. getPalletDetails({
  309. site: this.site,
  310. palletId: this.palletCode,
  311. position: this.selectedPosition,
  312. layer: ''
  313. }).then(({ data }) => {
  314. if (data.code === 0) {
  315. this.detailList = data.details || [];
  316. } else {
  317. this.detailList = [];
  318. }
  319. }).catch(error => {
  320. console.error('获取栈板明细失败:', error);
  321. this.detailList = [];
  322. });
  323. },
  324. // 显示明细弹窗 - rqrq
  325. showDetailModal() {
  326. this.detailModalVisible = true;
  327. },
  328. // 显示预定清单弹窗 - rqrq
  329. showReserveModal() {
  330. this.loadReservedLabels();
  331. this.reserveModalVisible = true;
  332. },
  333. // 加载预定标签列表 - rqrq
  334. loadReservedLabels() {
  335. getPalletReservedLabels({
  336. site: this.site,
  337. palletId: this.palletCode
  338. }).then(({ data }) => {
  339. if (data.code === 0 && data.data) {
  340. this.reservedLabels = data.data.reservedLabels || [];
  341. this.reserveCount = data.data.reserveCount || 0;
  342. this.locationCode = data.data.locationCode || '';
  343. this.errorStationFlag = data.data.errorStationFlag === true;
  344. } else {
  345. this.reservedLabels = [];
  346. this.reserveCount = 0;
  347. this.errorStationFlag = false;
  348. }
  349. }).catch(error => {
  350. console.error('获取预定标签列表失败:', error);
  351. this.reservedLabels = [];
  352. this.reserveCount = 0;
  353. this.errorStationFlag = false;
  354. });
  355. },
  356. // 一键取消预定 - rqrq
  357. handleBatchCancelReserve() {
  358. if (this.errorStationFlag) {
  359. this.$alert('栈板在立库异常位上,不允许取消预定', '错误', { confirmButtonText: '确定' });
  360. return;
  361. }
  362. this.$confirm('确定要取消所有预定标签吗?', '提示', {
  363. confirmButtonText: '确定',
  364. cancelButtonText: '取消',
  365. type: 'warning'
  366. }).then(() => {
  367. this.doBatchCancelReserve();
  368. }).catch(() => {
  369. // 用户取消
  370. });
  371. },
  372. // 执行批量取消预定 - rqrq
  373. doBatchCancelReserve() {
  374. this.cancelReserveLoading = true;
  375. batchCancelReserve({
  376. site: this.site,
  377. palletId: this.palletCode
  378. }).then(({ data }) => {
  379. if (data.code === 0) {
  380. this.$message.success('取消预定成功');
  381. this.reserveModalVisible = false;
  382. // 重新加载预定标签列表 - rqrq
  383. this.loadReservedLabels();
  384. } else {
  385. this.$alert(data.msg || '取消预定失败', '错误', { confirmButtonText: '确定' });
  386. }
  387. }).catch(error => {
  388. console.error('批量取消预定失败:', error);
  389. this.$alert('取消预定异常', '错误', { confirmButtonText: '确定' });
  390. }).finally(() => {
  391. this.cancelReserveLoading = false;
  392. });
  393. }
  394. },
  395. // keep-alive页面每次激活统一重置,保证海安“退出再进入”无历史残留 - rqrq
  396. activated() {
  397. this.resetPage(true);
  398. },
  399. // 离开页面时主动清理缓存状态,避免下次进入展示旧查询结果 - rqrq
  400. beforeRouteLeave(to, from, next) {
  401. this.resetPage(false);
  402. next();
  403. },
  404. mounted() {
  405. this.resetPage(true);
  406. }
  407. };
  408. </script>
  409. <style scoped>
  410. /* 表格样式 */
  411. .detail-table {
  412. background: white;
  413. border-radius: 6px;
  414. overflow: hidden;
  415. border: 1px solid #e0e0e0;
  416. }
  417. .table-header,
  418. .table-row {
  419. display: flex;
  420. align-items: center;
  421. padding: 8px;
  422. border-bottom: 1px solid #e0e0e0;
  423. }
  424. .table-header {
  425. background: #f5f5f5;
  426. font-weight: bold;
  427. font-size: 14px;
  428. }
  429. .table-row {
  430. font-size: 13px;
  431. }
  432. .table-row:last-child {
  433. border-bottom: none;
  434. }
  435. .col-position {
  436. flex: 1;
  437. text-align: center;
  438. }
  439. .col-layer {
  440. flex: 1;
  441. text-align: center;
  442. }
  443. .col-serial {
  444. flex: 4;
  445. text-align: center;
  446. word-break: break-all;
  447. }
  448. /* 空数据提示 */
  449. .empty-hint {
  450. text-align: center;
  451. color: #999;
  452. padding: 20px;
  453. background: white;
  454. border-radius: 6px;
  455. margin-top: 16px;
  456. }
  457. /* 模态框样式 */
  458. .scan-modal-content {
  459. padding: 10px 0;
  460. }
  461. .modal-form {
  462. margin-bottom: 16px;
  463. }
  464. .dialog-footer {
  465. text-align: center;
  466. }
  467. /* 修复模态框层级问题 */
  468. ::v-deep .el-dialog__wrapper {
  469. z-index: 2000 !important;
  470. }
  471. ::v-deep .el-overlay {
  472. z-index: 2000 !important;
  473. }
  474. /* 修复单选框样式 */
  475. ::v-deep .el-radio {
  476. margin-right: 8px !important;
  477. }
  478. ::v-deep .el-radio__label {
  479. font-size: 14px;
  480. }
  481. /* 标题行样式 */
  482. .list-title-row {
  483. display: flex;
  484. justify-content: space-between;
  485. align-items: center;
  486. margin-bottom: 8px;
  487. }
  488. .list-title-row .list-title {
  489. margin: 0;
  490. flex: 1;
  491. }
  492. /* 空数据行样式 */
  493. .empty-row {
  494. justify-content: center;
  495. align-items: center;
  496. padding: 20px;
  497. border-bottom: none;
  498. }
  499. .empty-row .empty-hint {
  500. text-align: center;
  501. color: #999;
  502. width: 100%;
  503. }
  504. /* 预定清单相关样式 - rqrq */
  505. .action-btn.warning {
  506. background: #E6A23C;
  507. color: white;
  508. border: none;
  509. }
  510. .action-btn.warning:hover {
  511. background: #d99429;
  512. }
  513. .action-btn.danger {
  514. background: #F56C6C;
  515. color: white;
  516. border: none;
  517. }
  518. .action-btn.danger:hover:not(:disabled) {
  519. background: #e45656;
  520. }
  521. .action-btn.danger:disabled {
  522. background: #ccc;
  523. cursor: not-allowed;
  524. opacity: 0.6;
  525. }
  526. .reserve-header {
  527. font-size: 12px;
  528. }
  529. .col-serial-reserve {
  530. flex: 2;
  531. text-align: center;
  532. word-break: break-all;
  533. font-size: 12px;
  534. }
  535. .col-ref {
  536. flex: 1.5;
  537. text-align: center;
  538. word-break: break-all;
  539. font-size: 12px;
  540. }
  541. .reserve-warning {
  542. text-align: center;
  543. color: #E6A23C;
  544. padding: 10px;
  545. font-size: 14px;
  546. background: #fdf6ec;
  547. border-radius: 4px;
  548. margin-top: 10px;
  549. }
  550. .reserve-warning i {
  551. margin-right: 5px;
  552. }
  553. </style>