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.

554 lines
15 KiB

4 weeks ago
3 weeks ago
4 weeks ago
3 weeks ago
4 weeks ago
3 weeks ago
4 weeks ago
3 weeks ago
4 weeks ago
3 weeks ago
4 weeks ago
3 weeks ago
4 weeks ago
3 weeks ago
4 weeks ago
3 weeks ago
4 weeks ago
3 weeks ago
4 weeks ago
3 weeks ago
4 weeks ago
  1. <template>
  2. <div>
  3. <div class="pda-container">
  4. <!-- 头部栏 - rqrq -->
  5. <div class="header-bar">
  6. <div class="header-left" @click="handleBack">
  7. <i class="el-icon-arrow-left"></i>
  8. <span>取消WCS组盘任务</span>
  9. </div>
  10. <div class="header-right" @click="goHome">
  11. 首页
  12. </div>
  13. </div>
  14. <div class="table-body" style="max-height: 600px; 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. inputmode="none"
  26. autocomplete="off"
  27. autocorrect="off"
  28. spellcheck="false"
  29. ref="palletInput"
  30. />
  31. </div>
  32. <!-- 栈板信息显示扫描栈板后显示- rqrq -->
  33. <div v-if="palletScanned" class="pallet-info-section">
  34. <div class="info-row">
  35. <label class="info-label">是否被调用:</label>
  36. <span class="info-value" :style="{color: palletInfo.callingFlag === 'Y' ? '#F56C6C' : '#67C23A'}">
  37. {{ palletInfo.callingFlag === 'Y' ? '是' : '否' }}
  38. </span>
  39. </div>
  40. <!-- 是否发送组盘任务 - rqrq -->
  41. <div class="info-row">
  42. <label class="info-label">是否已组盘:</label>
  43. <span class="info-value" :style="{color: hasSentWcsTask ? '#F56C6C' : '#67C23A'}">
  44. {{ hasSentWcsTask ? '是' : '否' }}
  45. </span>
  46. </div>
  47. <!-- 浏览明细按钮 - rqrq -->
  48. <div class="button-row">
  49. <button class="action-btn primary" @click="showDetailModal" style="width: 100%;">
  50. 浏览明细 ({{ detailList.length }})
  51. </button>
  52. </div>
  53. <!-- 取消组盘按钮已组盘且不在wcs_location/输送线禁操作状态时显示- rqrq -->
  54. <div v-if="showCancelButton" class="button-row">
  55. <button
  56. class="action-btn primary"
  57. @click="handleCancelWcsPallet"
  58. style="width: 100%;">
  59. {{ cancelLoading ? '取消中...' : '取消组盘' }}
  60. </button>
  61. </div>
  62. <!-- 移出全部物料按钮未组盘+未调用+有明细且不在wcs_location/输送线时显示- rqrq -->
  63. <div v-if="showRemoveAllButton" class="button-row">
  64. <button
  65. class="action-btn warning"
  66. @click="handleRemoveAllMaterials"
  67. :disabled="removeAllLoading"
  68. style="width: 100%;">
  69. {{ removeAllLoading ? '移出中...' : '移出全部物料' }}
  70. </button>
  71. </div>
  72. </div>
  73. </div>
  74. </div>
  75. </div>
  76. <!-- 浏览明细弹窗 - rqrq -->
  77. <el-dialog
  78. :title="'栈板明细 (共'+detailList.length+'条)'"
  79. :visible.sync="detailModalVisible"
  80. width="90%"
  81. :close-on-click-modal="false"
  82. :show-close="false"
  83. :modal="true"
  84. :modal-append-to-body="true"
  85. :append-to-body="true"
  86. >
  87. <div class="table-body" style="max-height: 400px; overflow-y: auto;">
  88. <div class="detail-table">
  89. <div class="table-header">
  90. <div class="col-position">位置</div>
  91. <div class="col-layer">层数</div>
  92. <div class="col-serial">标签号</div>
  93. </div>
  94. <div
  95. v-for="(detail, index) in detailList"
  96. :key="index"
  97. class="table-row"
  98. >
  99. <div class="col-position">{{ detail.position }}</div>
  100. <div class="col-layer">{{ detail.layer }}</div>
  101. <div class="col-serial">{{ detail.serialNo }}</div>
  102. </div>
  103. <!-- 暂无数据提示 -->
  104. <div v-if="detailList.length === 0" class="table-row empty-row">
  105. <div class="empty-hint">暂无栈板明细数据</div>
  106. </div>
  107. </div>
  108. </div>
  109. <div slot="footer" class="dialog-footer">
  110. <button class="action-btn secondary" @click="detailModalVisible=false">关闭</button>
  111. </div>
  112. </el-dialog>
  113. </div>
  114. </template>
  115. <script>
  116. import {
  117. getPalletDetails
  118. } from '../../../api/haianAutoWarehouse/palletPacking'
  119. import {
  120. checkPalletWcsStatus,
  121. cancelWcsPallet,
  122. removeAllPalletDetails
  123. } from '../../../api/haianAutoWarehouse/cancelWcsPallet'
  124. export default {
  125. data() {
  126. return {
  127. // 海安PDA前端site唯一来源为store用户对象,兼容旧缓存兜底 - rqrq
  128. site: (this.$store && this.$store.state && this.$store.state.user && this.$store.state.user.site) || localStorage.getItem('site'),
  129. // 栈板信息 - rqrq
  130. palletCode: '',
  131. palletScanned: false,
  132. palletInfo: {
  133. locationCode: '',
  134. wcsLocation: '',
  135. area: '',
  136. callingFlag: '',
  137. canOperate: '',
  138. isEmpty: '',
  139. hasSentWcsTask: ''
  140. },
  141. // 按钮显示控制 - rqrq
  142. hasSentWcsTask: false,
  143. showCancelButton: false,
  144. showRemoveAllButton: false,
  145. // 栈板明细 - rqrq
  146. detailList: [],
  147. detailModalVisible: false,
  148. // 加载状态 - rqrq
  149. cancelLoading: false,
  150. removeAllLoading: false
  151. };
  152. },
  153. methods: {
  154. // 返回上一页并清空页面状态,避免keep-alive导致脏数据残留 - rqrq
  155. handleBack() {
  156. this.resetPage(false);
  157. this.$router.back();
  158. },
  159. // 回首页前清理状态,保证再次进入页面从干净态开始 - rqrq
  160. goHome() {
  161. this.resetPage(false);
  162. this.$router.push({ path: '/' });
  163. },
  164. // 扫描栈板 - rqrq
  165. handlePalletScan() {
  166. if (!this.palletCode.trim()) {
  167. this.$alert('请输入栈板编码', '提示', {
  168. confirmButtonText: '确定'
  169. });
  170. return;
  171. }
  172. checkPalletWcsStatus({
  173. site: this.site,
  174. palletId: this.palletCode
  175. }).then(({ data }) => {
  176. if (data.code === 0) {
  177. // 后端返回正确的6位栈板码,赋值给输入框 - rqrq
  178. this.palletCode = data.row.palletId;
  179. // 保存栈板信息 - rqrq
  180. this.palletInfo = data.row;
  181. this.palletScanned = true;
  182. // 解析按钮显示逻辑 - rqrq
  183. // canOperate字段存储showCancelButton(是否显示取消组盘按钮)- rqrq
  184. this.showCancelButton = data.row.canOperate === 'Y';
  185. // isEmpty字段存储showRemoveAllButton(是否显示移出全部物料按钮)- rqrq
  186. this.showRemoveAllButton = data.row.isEmpty === 'Y';
  187. // hasSentWcsTask优先使用后端原始标记,避免按钮被隐藏时误显示“未组盘” - rqrq
  188. this.hasSentWcsTask = data.row.hasSentWcsTask
  189. ? data.row.hasSentWcsTask === 'Y'
  190. : this.showCancelButton;
  191. // 加载栈板明细 - rqrq
  192. this.loadPalletDetails();
  193. } else {
  194. this.$alert(data.msg || '栈板不存在', '错误', {
  195. confirmButtonText: '确定',
  196. callback: () => {
  197. this.palletCode = '';
  198. this.$nextTick(() => {
  199. if (this.$refs.palletInput) {
  200. this.$refs.palletInput.focus();
  201. }
  202. });
  203. }
  204. });
  205. }
  206. }).catch(error => {
  207. console.error('查询栈板失败:', error);
  208. this.$alert(error.message || '查询栈板失败', '错误', {
  209. confirmButtonText: '确定',
  210. callback: () => {
  211. this.palletCode = '';
  212. this.$nextTick(() => {
  213. if (this.$refs.palletInput) {
  214. this.$refs.palletInput.focus();
  215. }
  216. });
  217. }
  218. });
  219. });
  220. },
  221. // 加载栈板明细 - rqrq
  222. loadPalletDetails() {
  223. getPalletDetails({
  224. site: this.site,
  225. palletId: this.palletCode,
  226. position: '',
  227. layer: ''
  228. }).then(({ data }) => {
  229. if (data.code === 0) {
  230. this.detailList = data.details || [];
  231. } else {
  232. this.detailList = [];
  233. }
  234. }).catch(error => {
  235. console.error('获取栈板明细失败:', error);
  236. this.detailList = [];
  237. });
  238. },
  239. // 显示明细弹窗 - rqrq
  240. showDetailModal() {
  241. this.detailModalVisible = true;
  242. },
  243. // 取消组盘/一键移出的前端防呆提示:与后端同口径拦截wcs_location与输送线状态 - rqrq
  244. resolveManualOperationBlockedMessage() {
  245. const wcsLocation = (this.palletInfo.wcsLocation || '').trim();
  246. if (wcsLocation) {
  247. return `当前栈板wcs_location有值(${wcsLocation}),不允许取消组盘或一键移出。`;
  248. }
  249. const locationCode = (this.palletInfo.locationCode || '').trim();
  250. const isConveyor = locationCode.toUpperCase() === 'VIRTUAL_AS' || locationCode === '立库输送线';
  251. if (isConveyor) {
  252. return '当前栈板位于输送线,不允许取消组盘或一键移出。';
  253. }
  254. return '';
  255. },
  256. // 取消组盘 - rqrq
  257. handleCancelWcsPallet() {
  258. const blockedMessage = this.resolveManualOperationBlockedMessage();
  259. if (blockedMessage) {
  260. this.$alert(blockedMessage, '提示', {
  261. confirmButtonText: '确定'
  262. });
  263. return;
  264. }
  265. this.$confirm('确定要取消组盘吗?取消后需要重新组盘才能推送到WCS。', '确认操作', {
  266. confirmButtonText: '确定',
  267. cancelButtonText: '取消',
  268. type: 'warning'
  269. }).then(() => {
  270. this.cancelLoading = true;
  271. cancelWcsPallet({
  272. site: this.site,
  273. palletId: this.palletCode
  274. }).then(({ data }) => {
  275. if (data && data.code === 0) {
  276. this.$message.success('取消组盘成功');
  277. // 重新扫描刷新信息 - rqrq
  278. this.handlePalletScan();
  279. } else {
  280. this.$alert(data.msg || '取消组盘失败', '错误', {
  281. confirmButtonText: '确定'
  282. });
  283. }
  284. }).catch(error => {
  285. console.error('取消组盘失败:', error);
  286. // 网络错误也弹出提示框 - rqrq
  287. this.$alert(error.message || '取消组盘失败', '错误', {
  288. confirmButtonText: '确定'
  289. });
  290. }).finally(() => {
  291. this.cancelLoading = false;
  292. });
  293. }).catch(() => {
  294. // 用户取消操作
  295. });
  296. },
  297. // 移出全部物料 - rqrq
  298. handleRemoveAllMaterials() {
  299. const blockedMessage = this.resolveManualOperationBlockedMessage();
  300. if (blockedMessage) {
  301. this.$alert(blockedMessage, '提示', {
  302. confirmButtonText: '确定'
  303. });
  304. return;
  305. }
  306. this.$confirm('确定要移出栈板上的全部物料吗?此操作不可恢复!', '确认操作', {
  307. confirmButtonText: '确定',
  308. cancelButtonText: '取消',
  309. type: 'warning'
  310. }).then(() => {
  311. this.removeAllLoading = true;
  312. removeAllPalletDetails({
  313. site: this.site,
  314. palletId: this.palletCode
  315. }).then(({ data }) => {
  316. if (data && data.code === 0) {
  317. this.$message.success('移出全部物料成功');
  318. // 重新扫描刷新信息 - rqrq
  319. this.handlePalletScan();
  320. } else {
  321. this.$alert(data.msg || '移出全部物料失败', '错误', {
  322. confirmButtonText: '确定'
  323. });
  324. }
  325. }).catch(error => {
  326. console.error('移出全部物料失败:', error);
  327. // 网络错误也弹出提示框 - rqrq
  328. this.$alert(error.message || '移出全部物料失败', '错误', {
  329. confirmButtonText: '确定'
  330. });
  331. }).finally(() => {
  332. this.removeAllLoading = false;
  333. });
  334. }).catch(() => {
  335. // 用户取消操作
  336. });
  337. },
  338. // 重置页面并按需恢复扫码焦点,统一海安PDA页面生命周期清理行为 - rqrq
  339. resetPage(needFocus) {
  340. this.palletCode = '';
  341. this.palletScanned = false;
  342. this.palletInfo = {
  343. locationCode: '',
  344. wcsLocation: '',
  345. area: '',
  346. callingFlag: '',
  347. canOperate: '',
  348. isEmpty: '',
  349. hasSentWcsTask: ''
  350. };
  351. this.hasSentWcsTask = false;
  352. this.showCancelButton = false;
  353. this.showRemoveAllButton = false;
  354. this.detailList = [];
  355. this.detailModalVisible = false;
  356. if (needFocus) {
  357. this.$nextTick(() => {
  358. if (this.$refs.palletInput) {
  359. this.$refs.palletInput.focus();
  360. }
  361. });
  362. }
  363. }
  364. },
  365. mounted() {
  366. this.$nextTick(() => {
  367. if (this.$refs.palletInput) {
  368. this.$refs.palletInput.focus();
  369. }
  370. });
  371. },
  372. activated() {
  373. this.resetPage(true);
  374. },
  375. beforeRouteLeave(to, from, next) {
  376. this.resetPage(false);
  377. next();
  378. }
  379. };
  380. </script>
  381. <style scoped>
  382. /* 栈板信息区域 - rqrq */
  383. .pallet-info-section {
  384. margin-top: 16px;
  385. padding: 12px;
  386. background: #f5f7fa;
  387. border-radius: 6px;
  388. }
  389. .info-row {
  390. display: flex;
  391. align-items: center;
  392. padding: 8px 0;
  393. border-bottom: 1px solid #e4e7ed;
  394. }
  395. .info-row:last-child {
  396. border-bottom: none;
  397. }
  398. .info-label {
  399. font-weight: bold;
  400. color: #606266;
  401. width: 140px;
  402. flex-shrink: 0;
  403. }
  404. .info-value {
  405. color: #303133;
  406. font-size: 14px;
  407. font-weight: bold;
  408. }
  409. .button-row {
  410. margin-top: 12px;
  411. }
  412. /* 警告按钮 - rqrq */
  413. .action-btn.warning {
  414. background-color: #E6A23C;
  415. color: white;
  416. border: none;
  417. padding: 10px 20px;
  418. border-radius: 4px;
  419. cursor: pointer;
  420. font-size: 14px;
  421. }
  422. .action-btn.warning:hover {
  423. background-color: #d99525;
  424. }
  425. .action-btn.warning:disabled {
  426. background-color: #f5dab1;
  427. cursor: not-allowed;
  428. }
  429. /* 表格样式 - rqrq */
  430. .detail-table {
  431. background: white;
  432. border-radius: 6px;
  433. overflow: hidden;
  434. border: 1px solid #e0e0e0;
  435. }
  436. .table-header,
  437. .table-row {
  438. display: flex;
  439. align-items: center;
  440. padding: 8px;
  441. border-bottom: 1px solid #e0e0e0;
  442. }
  443. .table-header {
  444. background: #f5f5f5;
  445. font-weight: bold;
  446. font-size: 14px;
  447. }
  448. .table-row {
  449. font-size: 13px;
  450. }
  451. .table-row:last-child {
  452. border-bottom: none;
  453. }
  454. .col-position {
  455. flex: 1;
  456. text-align: center;
  457. }
  458. .col-layer {
  459. flex: 1;
  460. text-align: center;
  461. }
  462. .col-serial {
  463. flex: 4;
  464. text-align: center;
  465. word-break: break-all;
  466. }
  467. /* 空数据提示 - rqrq */
  468. .empty-hint {
  469. text-align: center;
  470. color: #999;
  471. padding: 20px;
  472. background: white;
  473. border-radius: 6px;
  474. }
  475. /* 空数据行样式 - rqrq */
  476. .empty-row {
  477. justify-content: center;
  478. align-items: center;
  479. padding: 20px;
  480. border-bottom: none;
  481. }
  482. .empty-row .empty-hint {
  483. text-align: center;
  484. color: #999;
  485. width: 100%;
  486. }
  487. .dialog-footer {
  488. display: flex;
  489. gap: 8px;
  490. text-align: center;
  491. }
  492. /* 修复模态框层级问题 - rqrq */
  493. ::v-deep .el-dialog__wrapper {
  494. z-index: 2000 !important;
  495. }
  496. ::v-deep .el-overlay {
  497. z-index: 2000 !important;
  498. }
  499. </style>