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.

488 lines
15 KiB

17 hours ago
1 hour ago
17 hours ago
1 hour ago
17 hours ago
1 hour ago
17 hours ago
1 hour ago
17 hours 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>空托盘组盘</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. ref="palletInput"
  26. />
  27. </div>
  28. <!-- 托盘信息显示扫描托盘后显示- rqrq -->
  29. <div v-if="palletScanned" class="pallet-info-section">
  30. <!-- 托盘类型选择 - rqrq -->
  31. <div class="input-group">
  32. <label class="input-label">托盘类型</label>
  33. <el-select
  34. v-model="currentPalletType"
  35. placeholder="请选择托盘类型"
  36. style="width: 100%;"
  37. @change="handlePalletTypeChange"
  38. >
  39. <el-option
  40. v-for="type in palletTypeOptions"
  41. :key="type.palletType"
  42. :label="`${type.palletType} - ${type.typeDesc}`"
  43. :value="type.palletType"
  44. />
  45. </el-select>
  46. </div>
  47. <div class="input-group">
  48. <label class="input-label">空托数量</label>
  49. <el-select v-model="count" disabled>
  50. <el-option value=1 label=1></el-option>
  51. <el-option value=2 label=2></el-option>
  52. <el-option value=3 label=3></el-option>
  53. <el-option value=4 label=4></el-option>
  54. <el-option value=5 label=5></el-option>
  55. <el-option value=6 label=6></el-option>
  56. </el-select>
  57. </div>
  58. <!-- 当前站点信息 - rqrq -->
  59. <div class="info-row">
  60. <label class="info-label">当前站点ID:</label>
  61. <span class="info-value">{{ palletInfo.currentStationId || '-' }}</span>
  62. </div>
  63. <div class="info-row">
  64. <label class="info-label">当前站点编码:</label>
  65. <span class="info-value">{{ palletInfo.currentStationCode || '-' }}</span>
  66. </div>
  67. <!-- 操作按钮 - rqrq -->
  68. <div class="button-row" style="display: flex; gap: 8px; margin-top: 16px;">
  69. <button
  70. class="action-btn primary"
  71. @click="handleNotifyInbound"
  72. :disabled="notifyLoading || !currentPalletType"
  73. style="flex: 1;">
  74. {{ notifyLoading ? '处理中...' : '通知入库' }}
  75. </button>
  76. <button
  77. class="action-btn primary"
  78. @click="handleInboundAndTransport"
  79. :disabled="transportLoading || !currentPalletType"
  80. style="flex: 1;">
  81. {{ transportLoading ? '处理中...' : '入库并运输' }}
  82. </button>
  83. </div>
  84. </div>
  85. </div>
  86. </div>
  87. </div>
  88. <!-- 通知入库口选择弹窗必须明确选择入库口1/2后才允许提交 - rqrq -->
  89. <el-dialog
  90. title="请选择入库口"
  91. :visible.sync="inboundPortDialogVisible"
  92. width="90%"
  93. :close-on-click-modal="false"
  94. :show-close="false"
  95. :append-to-body="true"
  96. >
  97. <div class="input-group">
  98. <label class="input-label">入库口</label>
  99. <el-select v-model="selectedInboundPort" placeholder="请选择入库口" style="width: 100%;">
  100. <el-option
  101. v-for="option in inboundPortOptions"
  102. :key="option.value"
  103. :label="option.label"
  104. :value="option.value"
  105. />
  106. </el-select>
  107. </div>
  108. <div slot="footer" class="dialog-footer">
  109. <button class="action-btn secondary" @click="cancelInboundPortDialog">取消</button>
  110. <button class="action-btn primary" @click="confirmNotifyInbound">确认</button>
  111. </div>
  112. </el-dialog>
  113. </div>
  114. </template>
  115. <script>
  116. import {
  117. checkEmptyPallet,
  118. getPalletTypes,
  119. updatePalletTypeAndAutoSort,
  120. notifyEmptyPalletInbound
  121. } from '../../../api/haianAutoWarehouse/emptyPalletAssembly'
  122. export default {
  123. data() {
  124. return {
  125. // 海安PDA前端site唯一来源为store用户对象,兼容旧缓存兜底 - rqrq
  126. site: (this.$store && this.$store.state && this.$store.state.user && this.$store.state.user.site) || localStorage.getItem('site'),
  127. // 托盘信息 - rqrq
  128. palletCode: '',
  129. palletScanned: false,
  130. palletInfo: {
  131. currentStationId: '',
  132. currentStationCode: '',
  133. palletType: '',
  134. area: ''
  135. },
  136. // 托盘类型 - rqrq
  137. currentPalletType: '',
  138. palletTypeOptions: [],
  139. count: 1,
  140. // 通知入库口选择 - rqrq
  141. inboundPortDialogVisible: false,
  142. selectedInboundPort: '',
  143. inboundPortOptions: [
  144. { label: '入库口1', value: 'INBOUND_PORT_1' },
  145. { label: '入库口2', value: 'INBOUND_PORT_2' }
  146. ],
  147. // 加载状态 - rqrq
  148. notifyLoading: false,
  149. transportLoading: false
  150. };
  151. },
  152. methods: {
  153. // 返回上一页并清空页面状态,避免keep-alive导致脏数据残留 - rqrq
  154. handleBack() {
  155. this.resetPage(false);
  156. this.$router.back();
  157. },
  158. // 回首页前清理状态,保证再次进入页面从干净态开始 - rqrq
  159. goHome() {
  160. this.resetPage(false);
  161. this.$router.push({ path: '/' });
  162. },
  163. // 扫描托盘 - rqrq
  164. handlePalletScan() {
  165. if (!this.palletCode.trim()) {
  166. this.$alert('请输入托盘编码', '提示', {
  167. confirmButtonText: '确定'
  168. });
  169. return;
  170. }
  171. checkEmptyPallet({
  172. site: this.site,
  173. palletId: this.palletCode
  174. }).then(({ data }) => {
  175. if (data.code === 0) {
  176. // 后端返回正确的6位栈板码,赋值给输入框 - rqrq
  177. this.palletCode = data.row.palletId;
  178. // 托盘信息 - rqrq
  179. this.palletInfo = data.row;
  180. this.currentPalletType = data.row.palletType || '';
  181. this.palletScanned = true;
  182. // 根据托盘的palletFamily加载托盘类型选项 - rqrq
  183. this.loadPalletTypes(data.row.palletFamily);
  184. } else {
  185. this.$alert(data.msg || '托盘验证失败', '错误', {
  186. confirmButtonText: '确定',
  187. callback: () => {
  188. this.palletCode = '';
  189. this.$nextTick(() => {
  190. if (this.$refs.palletInput) {
  191. this.$refs.palletInput.focus();
  192. }
  193. });
  194. }
  195. });
  196. }
  197. }).catch(error => {
  198. console.error('查询托盘失败:', error);
  199. this.$alert(error.message || '查询托盘失败', '错误', {
  200. confirmButtonText: '确定',
  201. callback: () => {
  202. this.palletCode = '';
  203. this.$nextTick(() => {
  204. if (this.$refs.palletInput) {
  205. this.$refs.palletInput.focus();
  206. }
  207. });
  208. }
  209. });
  210. });
  211. },
  212. // 加载托盘类型选项(根据palletFamily过滤)- rqrq
  213. loadPalletTypes(palletFamily) {
  214. getPalletTypes({
  215. site: this.site,
  216. palletFamily: palletFamily || ''
  217. }).then(({ data }) => {
  218. if (data.code === 0) {
  219. this.palletTypeOptions = data.rows || [];
  220. } else {
  221. this.palletTypeOptions = [];
  222. this.$alert('获取托盘类型失败', '错误', {
  223. confirmButtonText: '确定'
  224. });
  225. }
  226. }).catch(error => {
  227. console.error('获取托盘类型失败:', error);
  228. this.palletTypeOptions = [];
  229. });
  230. },
  231. // 托盘类型变更事件 - rqrq
  232. handlePalletTypeChange() {
  233. // 保存到数据库 - rqrq
  234. updatePalletTypeAndAutoSort({
  235. site: this.site,
  236. palletId: this.palletCode,
  237. palletType: this.currentPalletType,
  238. autoSort: 'N',
  239. forceFullPalletOut: false
  240. }).then(({ data }) => {
  241. if (data.code === 0) {
  242. this.$message.success('更新成功');
  243. } else {
  244. this.$alert(data.msg || '更新失败', '错误', {
  245. confirmButtonText: '确定'
  246. });
  247. }
  248. }).catch(error => {
  249. console.error('更新失败:', error);
  250. this.$alert(error.message || '更新失败', '错误', {
  251. confirmButtonText: '确定'
  252. });
  253. });
  254. },
  255. // 通知入库前置校验:仅允许“外部回库”空托盘(area和location_code都为空)继续操作 - rqrq
  256. validateNotifyInboundPalletStatus() {
  257. const areaCode = this.palletInfo && this.palletInfo.area ? String(this.palletInfo.area).trim() : ''
  258. const locationCode = this.palletInfo && this.palletInfo.locationCode
  259. ? String(this.palletInfo.locationCode).trim()
  260. : (this.palletInfo && this.palletInfo.currentStationCode ? String(this.palletInfo.currentStationCode).trim() : '')
  261. if (areaCode || locationCode) {
  262. this.$alert('通知入库仅允许未绑定站点的空托盘', '提示', {
  263. confirmButtonText: '确定'
  264. })
  265. return false
  266. }
  267. return true
  268. },
  269. // 通知入库 - rqrq
  270. handleNotifyInbound() {
  271. if (!this.currentPalletType) {
  272. this.$alert('请先选择托盘类型', '提示', {
  273. confirmButtonText: '确定'
  274. });
  275. return;
  276. }
  277. if (!this.validateNotifyInboundPalletStatus()) {
  278. return;
  279. }
  280. // 左按钮“通知入库”必须先选入库口,后端再把入库口映射为固定站点号 - rqrq
  281. this.selectedInboundPort = '';
  282. this.inboundPortDialogVisible = true;
  283. },
  284. // 关闭入库口弹窗并清理选择状态,避免上次选择串到本次提交 - rqrq
  285. cancelInboundPortDialog() {
  286. this.inboundPortDialogVisible = false;
  287. this.selectedInboundPort = '';
  288. },
  289. // 确认通知入库:先校验入库口,再调用通知入库接口 - rqrq
  290. confirmNotifyInbound() {
  291. if (!this.selectedInboundPort) {
  292. this.$alert('请选择入库口', '提示', {
  293. confirmButtonText: '确定'
  294. });
  295. return;
  296. }
  297. if (!this.validateNotifyInboundPalletStatus()) {
  298. return;
  299. }
  300. this.inboundPortDialogVisible = false;
  301. this.$confirm('确定要执行通知入库操作吗?', '确认操作', {
  302. confirmButtonText: '确定',
  303. cancelButtonText: '取消',
  304. type: 'warning'
  305. }).then(() => {
  306. this.notifyLoading = true;
  307. notifyEmptyPalletInbound({
  308. site: this.site,
  309. palletId: this.palletCode,
  310. transportFlag: 'N',
  311. count: Number(this.count),
  312. inboundPort: this.selectedInboundPort,
  313. }).then(({ data }) => {
  314. if (data && data.code === 0) {
  315. this.$message.success('通知入库成功');
  316. // 清空页面重新扫描 - rqrq
  317. this.resetPage(true);
  318. } else {
  319. this.$alert(data.msg || '通知入库失败', '错误', {
  320. confirmButtonText: '确定'
  321. });
  322. }
  323. }).catch(error => {
  324. console.error('通知入库失败:', error);
  325. this.$alert(error.message || '通知入库失败', '错误', {
  326. confirmButtonText: '确定'
  327. });
  328. }).finally(() => {
  329. this.notifyLoading = false;
  330. });
  331. }).catch(() => {
  332. // 用户取消操作,不提交通知入库 - rqrq
  333. });
  334. },
  335. // 入库并运输 - rqrq
  336. handleInboundAndTransport() {
  337. if (!this.currentPalletType) {
  338. this.$alert('请先选择托盘类型', '提示', {
  339. confirmButtonText: '确定'
  340. });
  341. return;
  342. }
  343. // 右按钮“入库并运输”前置校验:栈板必须绑定area,否则当前位置禁止空托入库 - rqrq
  344. if (!this.palletInfo.area || !String(this.palletInfo.area).trim()) {
  345. this.$alert('当前位置不允许空托入库', '提示', {
  346. confirmButtonText: '确定'
  347. });
  348. return;
  349. }
  350. this.$confirm('确定要执行入库并运输操作吗?', '确认操作', {
  351. confirmButtonText: '确定',
  352. cancelButtonText: '取消',
  353. type: 'warning'
  354. }).then(() => {
  355. this.transportLoading = true;
  356. notifyEmptyPalletInbound({
  357. site: this.site,
  358. palletId: this.palletCode,
  359. transportFlag: 'Y',
  360. count: Number(this.count),
  361. }).then(({ data }) => {
  362. if (data && data.code === 0) {
  363. this.$message.success('入库并运输成功');
  364. // 清空页面重新扫描 - rqrq
  365. this.resetPage(true);
  366. } else {
  367. this.$alert(data.msg || '入库并运输失败', '错误', {
  368. confirmButtonText: '确定'
  369. });
  370. }
  371. }).catch(error => {
  372. console.error('入库并运输失败:', error);
  373. this.$alert(error.message || '入库并运输失败', '错误', {
  374. confirmButtonText: '确定'
  375. });
  376. }).finally(() => {
  377. this.transportLoading = false;
  378. });
  379. }).catch(() => {
  380. // 用户取消操作
  381. });
  382. },
  383. // 重置页面并按需恢复扫码焦点,统一海安PDA页面生命周期清理行为 - rqrq
  384. resetPage(needFocus) {
  385. this.palletCode = '';
  386. this.palletScanned = false;
  387. this.palletInfo = {
  388. currentStationId: '',
  389. currentStationCode: '',
  390. palletType: '',
  391. area: ''
  392. };
  393. this.currentPalletType = '';
  394. this.palletTypeOptions = [];
  395. this.count = 1;
  396. this.inboundPortDialogVisible = false;
  397. this.selectedInboundPort = '';
  398. if (needFocus) {
  399. this.$nextTick(() => {
  400. if (this.$refs.palletInput) {
  401. this.$refs.palletInput.focus();
  402. }
  403. });
  404. }
  405. }
  406. },
  407. mounted() {
  408. this.$nextTick(() => {
  409. if (this.$refs.palletInput) {
  410. this.$refs.palletInput.focus();
  411. }
  412. });
  413. },
  414. activated() {
  415. this.resetPage(true);
  416. },
  417. beforeRouteLeave(to, from, next) {
  418. this.resetPage(false);
  419. next();
  420. }
  421. };
  422. </script>
  423. <style scoped>
  424. /* 托盘信息区域 - rqrq */
  425. .pallet-info-section {
  426. margin-top: 16px;
  427. padding: 12px;
  428. background: #f5f7fa;
  429. border-radius: 6px;
  430. }
  431. .info-row {
  432. display: flex;
  433. align-items: center;
  434. padding: 8px 0;
  435. border-bottom: 1px solid #e4e7ed;
  436. }
  437. .info-row:last-child {
  438. border-bottom: none;
  439. }
  440. .info-label {
  441. font-weight: bold;
  442. color: #606266;
  443. width: 120px;
  444. flex-shrink: 0;
  445. }
  446. .info-value {
  447. color: #303133;
  448. font-size: 14px;
  449. }
  450. .button-row {
  451. margin-top: 12px;
  452. }
  453. </style>