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.

997 lines
28 KiB

4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months 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="main-content form-section">
  15. <!-- 第一行栈板扫描 -->
  16. <div class="input-group">
  17. <label class="input-label">栈板编码</label>
  18. <div style="display: flex; gap: 8px;">
  19. <el-input
  20. v-model="palletCode"
  21. placeholder="请扫描栈板编码"
  22. class="form-input"
  23. style="flex: 0.75;"
  24. clearable
  25. @keyup.enter.native="handlePalletScan"
  26. ref="palletInput"
  27. />
  28. <button
  29. class="action-btn secondary"
  30. style="flex: 0.25; margin: 0;"
  31. @click="handleCallPallet"
  32. >
  33. Call栈板
  34. </button>
  35. </div>
  36. </div>
  37. <!-- 第二行筛选条件 (扫描栈板后显示) -->
  38. <div v-if="palletScanned" class="input-group">
  39. <div style="display: flex; gap: 8px; align-items: end;">
  40. <div style="flex: 1;">
  41. <label class="input-label">位置</label>
  42. <el-select
  43. v-model="selectedPosition"
  44. placeholder="请选择位置"
  45. style="width: 100%;"
  46. @change="handlePositionChange"
  47. >
  48. <el-option label="ALL" value=""></el-option>
  49. <el-option
  50. v-for="position in positionOptions"
  51. :key="position"
  52. :label="position"
  53. :value="position"
  54. />
  55. </el-select>
  56. </div>
  57. <div style="flex: 1;">
  58. <label class="input-label">层数</label>
  59. <el-select
  60. v-model="selectedLayer"
  61. placeholder="请选择层数"
  62. style="width: 100%;"
  63. >
  64. <el-option label="ALL" value=""></el-option>
  65. <el-option
  66. v-for="layer in layerOptions"
  67. :key="layer"
  68. :label="`第${layer}层`"
  69. :value="layer"
  70. />
  71. </el-select>
  72. </div>
  73. <button
  74. class="action-btn secondary"
  75. style="margin: 0; white-space: nowrap;"
  76. @click="refreshTable"
  77. >
  78. 刷新
  79. </button>
  80. </div>
  81. </div>
  82. <!-- 第三行扫进/扫出选择 (扫描栈板后显示) -->
  83. <div v-if="palletScanned" class="input-group">
  84. <div style="display: flex; gap: 8px; align-items: center; flex-wrap: nowrap;">
  85. <div style="flex: 1; min-width: 0;">
  86. <el-radio-group v-model="operationType" style="display: flex; flex-wrap: nowrap;">
  87. <el-radio label="in" style="margin-right: 0px; white-space: nowrap;">扫进</el-radio>
  88. <el-radio label="out" style="white-space: nowrap;">扫出</el-radio>
  89. </el-radio-group>
  90. </div>
  91. <button
  92. class="action-btn secondary"
  93. style="margin: 0; white-space: nowrap; flex-shrink: 0;"
  94. @click="showScanModal"
  95. >
  96. 扫描二维码
  97. </button>
  98. </div>
  99. </div>
  100. </div>
  101. <!-- 栈板明细表格 (扫描栈板后显示) -->
  102. <div v-if="palletScanned" class="rma-list">
  103. <div class="list-title-row">
  104. <div class="list-title">栈板明细</div>
  105. <button class="action-btn secondary" style="margin-left: 10px;" @click="handleTransportOrder">运输指令</button>
  106. </div>
  107. <div class="detail-table">
  108. <div class="table-header">
  109. <div class="col-position">位置</div>
  110. <div class="col-layer">层数</div>
  111. <div class="col-serial">标签号</div>
  112. <div class="col-part">物料编码</div>
  113. </div>
  114. <div
  115. v-for="(detail, index) in detailList"
  116. :key="index"
  117. class="table-row"
  118. @click="handleRowDblClick(detail, index)"
  119. >
  120. <div class="col-position">{{ detail.position }}</div>
  121. <div class="col-layer">{{ detail.layer }}</div>
  122. <div class="col-serial">{{ detail.serialNo }}</div>
  123. <div class="col-part">{{ detail.partNo }}</div>
  124. </div>
  125. <!-- 暂无数据提示 -->
  126. <div v-if="detailList.length === 0" class="table-row empty-row">
  127. <div class="empty-hint">暂无栈板明细数据</div>
  128. </div>
  129. </div>
  130. </div>
  131. </div>
  132. <!-- 扫码模态框 -->
  133. <el-dialog
  134. title="扫描标签"
  135. :visible.sync="scanModalVisible"
  136. width="90%"
  137. :close-on-click-modal="false"
  138. :show-close="false"
  139. :modal="true"
  140. :modal-append-to-body="true"
  141. :append-to-body="true"
  142. >
  143. <div class="scan-modal-content">
  144. <!-- 扫进时显示位置和层数选择 -->
  145. <div v-if="operationType === 'in'" class="modal-form">
  146. <div class="input-group">
  147. <label class="input-label">位置</label>
  148. <el-select
  149. v-model="scanPosition"
  150. placeholder="请选择位置"
  151. style="width: 100%;"
  152. @change="handleScanPositionChange"
  153. >
  154. <el-option
  155. v-for="position in positionOptions"
  156. :key="position"
  157. :label="position"
  158. :value="position"
  159. />
  160. </el-select>
  161. </div>
  162. <div class="input-group">
  163. <label class="input-label">层数</label>
  164. <el-select
  165. v-model="scanLayer"
  166. placeholder="请选择层数"
  167. @change="moveFocusToScanInput"
  168. style="width: 100%;"
  169. >
  170. <el-option
  171. v-for="layer in scanLayerOptions"
  172. :key="layer"
  173. :label="`第${layer}层`"
  174. :value="layer"
  175. />
  176. </el-select>
  177. </div>
  178. </div>
  179. <!-- 标签扫描 -->
  180. <div class="input-group">
  181. <label class="input-label">标签二维码</label>
  182. <el-input
  183. v-model="scanCode"
  184. placeholder="请扫描标签二维码"
  185. class="form-input"
  186. clearable
  187. @keyup.enter.native="handleLabelScan"
  188. ref="scanInput"
  189. />
  190. </div>
  191. </div>
  192. <div slot="footer" class="dialog-footer">
  193. <button class="action-btn secondary" style="margin-left: 10px;" @click="closeScanModal">取消</button>
  194. </div>
  195. </el-dialog>
  196. <!-- 修改位置模态框 -->
  197. <el-dialog
  198. title="修改标签位置"
  199. :visible.sync="editPositionModalVisible"
  200. width="90%"
  201. :close-on-click-modal="false"
  202. :show-close="false"
  203. :modal="true"
  204. :modal-append-to-body="true"
  205. :append-to-body="true"
  206. >
  207. <div class="edit-modal-content">
  208. <!-- 标签号只读 -->
  209. <div class="input-group">
  210. <label class="input-label">标签号</label>
  211. <el-input
  212. v-model="editSerialNo"
  213. placeholder="标签号"
  214. class="form-input"
  215. readonly
  216. />
  217. </div>
  218. <!-- 位置选择 -->
  219. <div class="input-group">
  220. <label class="input-label">位置</label>
  221. <el-select
  222. v-model="editPosition"
  223. placeholder="请选择位置"
  224. style="width: 100%;"
  225. @change="handleEditPositionChange"
  226. >
  227. <el-option
  228. v-for="position in positionOptions"
  229. :key="position"
  230. :label="position"
  231. :value="position"
  232. />
  233. </el-select>
  234. </div>
  235. <!-- 层数选择 -->
  236. <div class="input-group">
  237. <label class="input-label">层数</label>
  238. <el-select
  239. v-model="editLayer"
  240. placeholder="请选择层数"
  241. style="width: 100%;"
  242. >
  243. <el-option
  244. v-for="layer in editLayerOptions"
  245. :key="layer"
  246. :label="`第${layer}层`"
  247. :value="layer"
  248. />
  249. </el-select>
  250. </div>
  251. </div>
  252. <div slot="footer" class="dialog-footer">
  253. <button class="action-btn primary" @click="confirmEditPosition">确定</button>
  254. <button class="action-btn secondary" style="margin-left: 10px;" @click="closeEditPositionModal">取消</button>
  255. </div>
  256. </el-dialog>
  257. <!-- 运输任务模态框 -->
  258. <el-dialog
  259. title="创建运输任务"
  260. :visible.sync="transportModalVisible"
  261. width="90%"
  262. :close-on-click-modal="false"
  263. :show-close="false"
  264. :modal="true"
  265. :modal-append-to-body="true"
  266. :append-to-body="true"
  267. >
  268. <div class="transport-modal-content">
  269. <!-- 栈板号只读 -->
  270. <div class="input-group">
  271. <label class="input-label">栈板号</label>
  272. <el-input
  273. v-model="palletCode"
  274. placeholder="栈板号"
  275. class="form-input"
  276. readonly
  277. />
  278. </div>
  279. <!-- 起点站点显示只读从栈板位置自动获取 -->
  280. <div class="input-group">
  281. <label class="input-label">起点站点</label>
  282. <el-input
  283. v-model="currentPalletStation"
  284. placeholder="当前栈板位置"
  285. class="form-input"
  286. readonly
  287. />
  288. </div>
  289. <!-- 目标站点选择 -->
  290. <div class="input-group">
  291. <label class="input-label">目标站点</label>
  292. <el-select
  293. v-model="selectedTargetStation"
  294. placeholder="请选择目标站点"
  295. style="width: 100%;"
  296. >
  297. <el-option
  298. v-for="station in transportStationOptions"
  299. :key="station.stationCode"
  300. :label="`${station.stationCode} - ${station.stationName}`"
  301. :value="station.stationCode"
  302. />
  303. </el-select>
  304. </div>
  305. </div>
  306. <div slot="footer" class="dialog-footer">
  307. <button class="action-btn primary" @click="confirmTransportTask">确定</button>
  308. <button class="action-btn secondary" style="margin-left: 10px;" @click="closeTransportModal">取消</button>
  309. </div>
  310. </el-dialog>
  311. <!-- Call栈板模态框 -->
  312. <el-dialog
  313. title="调用空托盘"
  314. :visible.sync="callPalletModalVisible"
  315. width="90%"
  316. :close-on-click-modal="false"
  317. :show-close="false"
  318. :modal="true"
  319. :modal-append-to-body="true"
  320. :append-to-body="true"
  321. >
  322. <div class="call-modal-content">
  323. <!-- 起始站点选择 -->
  324. <div class="input-group">
  325. <label class="input-label">起始站点</label>
  326. <el-select
  327. v-model="selectedCallStartStation"
  328. placeholder="请选择起始站点"
  329. style="width: 100%;"
  330. >
  331. <el-option
  332. v-for="station in callStationOptions"
  333. :key="station.stationCode"
  334. :label="`${station.stationCode} - ${station.stationName}`"
  335. :value="station.stationCode"
  336. />
  337. </el-select>
  338. </div>
  339. <!-- 目标站点选择 -->
  340. <div class="input-group">
  341. <label class="input-label">目标站点</label>
  342. <el-select
  343. v-model="selectedCallTargetStation"
  344. placeholder="请选择目标站点"
  345. style="width: 100%;"
  346. >
  347. <el-option
  348. v-for="station in callStationOptions"
  349. :key="station.stationCode"
  350. :label="`${station.stationCode} - ${station.stationName}`"
  351. :value="station.stationCode"
  352. />
  353. </el-select>
  354. </div>
  355. </div>
  356. <div slot="footer" class="dialog-footer">
  357. <button class="action-btn primary" @click="confirmCallPallet">确定</button>
  358. <button class="action-btn secondary" style="margin-left: 10px;" @click="closeCallPalletModal">取消</button>
  359. </div>
  360. </el-dialog>
  361. </div>
  362. </template>
  363. <script>
  364. import {
  365. checkPalletExists,
  366. getPalletPositions,
  367. getPalletDetails,
  368. getLayersByPosition,
  369. validateLabel,
  370. savePalletDetail,
  371. deletePalletDetail,
  372. getLayersForEdit,
  373. updatePalletDetailPosition,
  374. getAgvStations,
  375. callPalletToStation,
  376. callPalletToStationWithUpdateZuPan
  377. } from '../../../api/automatedWarehouse/palletPacking'
  378. export default {
  379. data() {
  380. return {
  381. site: localStorage.getItem('site'),
  382. palletCode: '',
  383. palletScanned: false,
  384. operationType: 'in', // 'in' 或 'out'
  385. // 筛选条件
  386. selectedPosition: '',
  387. selectedLayer: '',
  388. positionOptions: [],
  389. layerOptions: [],
  390. // 扫码模态框
  391. scanModalVisible: false,
  392. scanCode: '',
  393. scanPosition: '',
  394. scanLayer: '',
  395. scanLayerOptions: [],
  396. needRefreshOnClose: false, // 标记是否需要在关闭模态框时刷新
  397. // 栈板明细
  398. detailList: [],
  399. // 运输任务模态框
  400. transportModalVisible: false,
  401. transportStationOptions: [],
  402. currentPalletStation: '',
  403. selectedTargetStation: '',
  404. // Call栈板模态框
  405. callPalletModalVisible: false,
  406. callStationOptions: [],
  407. selectedCallStartStation: '',
  408. selectedCallTargetStation: '',
  409. // 修改位置模态框
  410. editPositionModalVisible: false,
  411. editSerialNo: '',
  412. editPosition: '',
  413. editLayer: '',
  414. editLayerOptions: [],
  415. editOriginalPosition: '',
  416. editOriginalLayer: '',
  417. };
  418. },
  419. methods: {
  420. handleBack() {
  421. this.$router.back();
  422. },
  423. // 扫描栈板
  424. handlePalletScan() {
  425. if (!this.palletCode.trim()) {
  426. this.$message.error('请输入栈板编码');
  427. return;
  428. }
  429. checkPalletExists({
  430. site: this.site,
  431. palletId: this.palletCode
  432. }).then(({ data }) => {
  433. if (data.code === 0) {
  434. this.palletScanned = true;
  435. this.positionOptions = data.positions || [];
  436. this.refreshTable();
  437. } else {
  438. this.$message.error(data.msg || '栈板不存在');
  439. }
  440. }).catch(error => {
  441. console.error('验证栈板失败:', error);
  442. this.$message.error('验证栈板失败');
  443. });
  444. },
  445. // Call栈板 - 调用空托盘
  446. handleCallPallet() {
  447. this.callPalletModalVisible = true;
  448. this.selectedCallStartStation = '';
  449. this.selectedCallTargetStation = '';
  450. // 获取AGV站点列表
  451. getAgvStations({}).then(({ data }) => {
  452. if (data.code === 0) {
  453. this.callStationOptions = data.stations || [];
  454. } else {
  455. this.$message.error(data.msg || '获取站点列表失败');
  456. }
  457. }).catch(error => {
  458. console.error('获取站点列表失败:', error);
  459. this.$message.error('获取站点列表失败');
  460. });
  461. },
  462. // 位置选择变化
  463. handlePositionChange() {
  464. if (this.selectedPosition) {
  465. getLayersByPosition({
  466. site: this.site,
  467. palletId: this.palletCode,
  468. position: this.selectedPosition
  469. }).then(({ data }) => {
  470. if (data.code === 0) {
  471. this.layerOptions = data.layers || [];
  472. }
  473. }).catch(error => {
  474. console.error('获取层数失败:', error);
  475. });
  476. } else {
  477. this.layerOptions = [];
  478. }
  479. this.selectedLayer = '';
  480. },
  481. // 刷新表格
  482. refreshTable() {
  483. getPalletDetails({
  484. site: this.site,
  485. palletId: this.palletCode,
  486. position: this.selectedPosition,
  487. layer: this.selectedLayer
  488. }).then(({ data }) => {
  489. if (data.code === 0) {
  490. this.detailList = data.details || [];
  491. } else {
  492. this.detailList = [];
  493. }
  494. }).catch(error => {
  495. console.error('获取栈板明细失败:', error);
  496. this.detailList = [];
  497. });
  498. },
  499. // 显示扫码模态框
  500. showScanModal() {
  501. this.scanModalVisible = true;
  502. this.scanCode = '';
  503. this.scanPosition = '';
  504. this.scanLayer = '';
  505. this.scanLayerOptions = [];
  506. this.needRefreshOnClose = false; // 重置刷新标记
  507. this.$nextTick(() => {
  508. if (this.$refs.scanInput) {
  509. this.$refs.scanInput.focus();
  510. }
  511. });
  512. },
  513. moveFocusToScanInput(){
  514. this.$nextTick(() => {
  515. if (this.$refs.scanInput) {
  516. this.$refs.scanInput.focus();
  517. }
  518. });
  519. },
  520. // 关闭扫码模态框
  521. closeScanModal() {
  522. this.scanModalVisible = false;
  523. // 如果有操作成功,则在关闭时刷新外面的列表
  524. if (this.needRefreshOnClose) {
  525. this.refreshTable();
  526. this.needRefreshOnClose = false;
  527. }
  528. },
  529. // 扫码模态框中位置变化
  530. handleScanPositionChange() {
  531. if (this.scanPosition) {
  532. getLayersByPosition({
  533. site: this.site,
  534. palletId: this.palletCode,
  535. position: this.scanPosition
  536. }).then(({ data }) => {
  537. if (data.code === 0) {
  538. const maxLayer = data.layers && data.layers.length > 0
  539. ? Math.max(...data.layers)
  540. : 0;
  541. this.scanLayerOptions = Array.from({ length: maxLayer+1 }, (_, i) => i + 1)
  542. }
  543. }).catch(error => {
  544. console.error('获取层数失败:', error);
  545. this.scanLayerOptions = [1];
  546. });
  547. } else {
  548. this.scanLayerOptions = [];
  549. }
  550. this.scanLayer = '';
  551. },
  552. // 处理标签扫描
  553. handleLabelScan() {
  554. if (!this.scanCode.trim()) {
  555. this.$message.error('请输入标签编码');
  556. return;
  557. }
  558. if (this.operationType === 'in') {
  559. // 扫进操作
  560. if (!this.scanPosition) {
  561. this.$message.error('请选择位置');
  562. return;
  563. }
  564. if (!this.scanLayer) {
  565. this.$message.error('请选择层数');
  566. return;
  567. }
  568. savePalletDetail({
  569. site: this.site,
  570. palletId: this.palletCode,
  571. position: this.scanPosition,
  572. layer: this.scanLayer,
  573. serialNo: this.scanCode
  574. }).then(({ data }) => {
  575. if (data.code === 0) {
  576. this.$message.success('扫进成功');
  577. this.needRefreshOnClose = true; // 标记需要在关闭时刷新
  578. this.scanCode = ''; // 清空扫描码,准备下次扫描
  579. this.$refs.scanInput.focus();
  580. } else {
  581. this.$message.error(data.msg || '扫进失败');
  582. }
  583. }).catch(error => {
  584. console.error('扫进失败:', error);
  585. this.$message.error('扫进失败');
  586. });
  587. } else {
  588. // 扫出操作
  589. deletePalletDetail({
  590. site: this.site,
  591. palletId: this.palletCode,
  592. serialNo: this.scanCode
  593. }).then(({ data }) => {
  594. if (data.code === 0) {
  595. this.$message.success('扫出成功');
  596. this.needRefreshOnClose = true; // 标记需要在关闭时刷新
  597. this.scanCode = ''; // 清空扫描码,准备下次扫描
  598. this.$refs.scanInput.focus();
  599. } else {
  600. this.$message.error(data.msg || '扫出失败');
  601. }
  602. }).catch(error => {
  603. console.error('扫出失败:', error);
  604. this.$message.error('扫出失败');
  605. });
  606. }
  607. },
  608. // 双击行事件 - 修改位置
  609. handleRowDblClick(detail, index) {
  610. this.editSerialNo = detail.serialNo;
  611. this.editPosition = detail.position;
  612. this.editLayer = detail.layer;
  613. this.editOriginalPosition = detail.position;
  614. this.editOriginalLayer = detail.layer;
  615. // 获取当前位置的层数选项(排除自己)
  616. this.handleEditPositionChange();
  617. this.editPositionModalVisible = true;
  618. },
  619. // 编辑位置选择变化
  620. handleEditPositionChange() {
  621. if (this.editPosition) {
  622. getLayersForEdit({
  623. site: this.site,
  624. palletId: this.palletCode,
  625. position: this.editPosition,
  626. excludeSerialNo: this.editSerialNo
  627. }).then(({ data }) => {
  628. if (data.code === 0) {
  629. this.editLayerOptions = data.layers || [];
  630. // 如果当前选择的层数不在新的选项中,清空选择
  631. // if (!this.editLayerOptions.includes(this.editLayer)) {
  632. // this.editLayer = '';
  633. // }
  634. }
  635. }).catch(error => {
  636. console.error('获取层数失败:', error);
  637. this.editLayerOptions = [];
  638. });
  639. } else {
  640. this.editLayerOptions = [];
  641. this.editLayer = '';
  642. }
  643. },
  644. // 确定修改位置
  645. confirmEditPosition() {
  646. if (!this.editPosition) {
  647. this.$message.error('请选择位置');
  648. return;
  649. }
  650. if (!this.editLayer) {
  651. this.$message.error('请选择层数');
  652. return;
  653. }
  654. // 检查是否有变化
  655. if (this.editPosition === this.editOriginalPosition && this.editLayer === this.editOriginalLayer) {
  656. this.$message.warning('位置没有变化');
  657. return;
  658. }
  659. updatePalletDetailPosition({
  660. site: this.site,
  661. palletId: this.palletCode,
  662. serialNo: this.editSerialNo,
  663. newPosition: this.editPosition,
  664. newLayer: this.editLayer
  665. }).then(({ data }) => {
  666. if (data.code === 0) {
  667. this.$message.success('位置修改成功');
  668. this.closeEditPositionModal();
  669. this.refreshTable();
  670. } else {
  671. this.$message.error(data.msg || '位置修改失败');
  672. }
  673. }).catch(error => {
  674. console.error('位置修改失败:', error);
  675. this.$message.error('位置修改失败');
  676. });
  677. },
  678. // 关闭修改位置模态框
  679. closeEditPositionModal() {
  680. this.editPositionModalVisible = false;
  681. this.editSerialNo = '';
  682. this.editPosition = '';
  683. this.editLayer = '';
  684. this.editLayerOptions = [];
  685. this.editOriginalPosition = '';
  686. this.editOriginalLayer = '';
  687. },
  688. // 运输指令按钮点击事件
  689. handleTransportOrder() {
  690. if (!this.palletCode) {
  691. this.$message.error('请先扫描栈板');
  692. return;
  693. }
  694. // 首先获取当前栈板信息
  695. checkPalletExists({
  696. site: this.site,
  697. palletId: this.palletCode
  698. }).then(({ data }) => {
  699. if (data.code == 0) {
  700. // 从返回数据中获取栈板位置信息
  701. this.currentPalletStation = data.locationCode || '';
  702. if (!this.currentPalletStation) {
  703. this.$message.error('无法获取当前栈板位置');
  704. return;
  705. }
  706. this.transportModalVisible = true;
  707. this.selectedTargetStation = '';
  708. this.transportStationOptions = [];
  709. // 获取所有AGV站点列表(和Call栈板一样的逻辑)
  710. getAgvStations({}).then(({ data }) => {
  711. if (data.code === 0) {
  712. this.transportStationOptions = data.stations || [];
  713. } else {
  714. this.$message.error(data.msg || '获取站点列表失败');
  715. }
  716. }).catch(error => {
  717. console.error('获取站点列表失败:', error);
  718. this.$message.error('获取站点列表失败');
  719. });
  720. } else {
  721. this.$message.error(data.msg || '获取栈板信息失败');
  722. }
  723. }).catch(error => {
  724. console.error('获取栈板信息失败:', error);
  725. this.$message.error('获取栈板信息失败');
  726. });
  727. },
  728. // 确认创建运输任务
  729. confirmTransportTask() {
  730. if (!this.currentPalletStation) {
  731. this.$message.error('无法获取当前栈板位置');
  732. return;
  733. }
  734. if (!this.selectedTargetStation) {
  735. this.$message.error('请选择目标站点');
  736. return;
  737. }
  738. // 前端验证:起始站点和目标站点不能一样
  739. if (this.currentPalletStation === this.selectedTargetStation) {
  740. this.$message.error('起始站点和目标站点不能相同');
  741. return;
  742. }
  743. // 调用包含组盘处理的接口
  744. callPalletToStationWithUpdateZuPan({
  745. site: this.site,
  746. startStation: this.currentPalletStation,
  747. targetStation: this.selectedTargetStation
  748. }).then(({ data }) => {
  749. if (data.code === 0) {
  750. this.$message.success('栈板运输任务创建成功');
  751. this.closeTransportModal();
  752. // 可能需要刷新栈板明细来反映组盘状态的变化
  753. this.refreshTable();
  754. } else {
  755. this.$message.error(data.msg || '创建运输任务失败');
  756. }
  757. }).catch(error => {
  758. console.error('创建运输任务失败:', error);
  759. this.$message.error('创建运输任务失败');
  760. });
  761. },
  762. // 关闭运输任务模态框
  763. closeTransportModal() {
  764. this.transportModalVisible = false;
  765. this.currentPalletStation = '';
  766. this.selectedTargetStation = '';
  767. this.transportStationOptions = [];
  768. },
  769. // 确认Call栈板
  770. confirmCallPallet() {
  771. if (!this.selectedCallStartStation) {
  772. this.$message.error('请选择起始站点');
  773. return;
  774. }
  775. if (!this.selectedCallTargetStation) {
  776. this.$message.error('请选择目标站点');
  777. return;
  778. }
  779. // 前端验证:两个站点不能一样
  780. if (this.selectedCallStartStation === this.selectedCallTargetStation) {
  781. this.$message.error('起始站点和目标站点不能相同');
  782. return;
  783. }
  784. callPalletToStation({
  785. site: this.site,
  786. startStation: this.selectedCallStartStation,
  787. targetStation: this.selectedCallTargetStation
  788. }).then(({ data }) => {
  789. if (data.code === 0) {
  790. this.$message.success('空托盘调用任务创建成功');
  791. this.closeCallPalletModal();
  792. } else {
  793. this.$message.error(data.msg || '调用空托盘失败');
  794. }
  795. }).catch(error => {
  796. console.error('调用空托盘失败:', error);
  797. this.$message.error('异常:'+error);
  798. });
  799. },
  800. // 关闭Call栈板模态框
  801. closeCallPalletModal() {
  802. this.callPalletModalVisible = false;
  803. this.selectedCallStartStation = '';
  804. this.selectedCallTargetStation = '';
  805. this.callStationOptions = [];
  806. },
  807. },
  808. mounted() {
  809. this.$nextTick(() => {
  810. if (this.$refs.palletInput) {
  811. this.$refs.palletInput.focus();
  812. }
  813. });
  814. }
  815. };
  816. </script>
  817. <style scoped>
  818. /* 表格样式 */
  819. .detail-table {
  820. background: white;
  821. border-radius: 6px;
  822. overflow: hidden;
  823. border: 1px solid #e0e0e0;
  824. }
  825. .table-header,
  826. .table-row {
  827. display: flex;
  828. align-items: center;
  829. padding: 8px;
  830. border-bottom: 1px solid #e0e0e0;
  831. }
  832. .table-header {
  833. background: #f5f5f5;
  834. font-weight: bold;
  835. font-size: 14px;
  836. }
  837. .table-row {
  838. font-size: 13px;
  839. }
  840. .table-row:last-child {
  841. border-bottom: none;
  842. }
  843. .col-position {
  844. flex: 1;
  845. text-align: center;
  846. }
  847. .col-layer {
  848. flex: 1;
  849. text-align: center;
  850. }
  851. .col-serial {
  852. flex: 2;
  853. text-align: center;
  854. }
  855. .col-part {
  856. flex: 2;
  857. text-align: center;
  858. }
  859. /* 空数据提示 */
  860. .empty-hint {
  861. text-align: center;
  862. color: #999;
  863. padding: 20px;
  864. background: white;
  865. border-radius: 6px;
  866. margin-top: 16px;
  867. }
  868. /* 模态框样式 */
  869. .scan-modal-content {
  870. padding: 10px 0;
  871. }
  872. .modal-form {
  873. margin-bottom: 16px;
  874. }
  875. .dialog-footer {
  876. text-align: center;
  877. }
  878. /* 修复模态框层级问题 */
  879. ::v-deep .el-dialog__wrapper {
  880. z-index: 2000 !important;
  881. }
  882. ::v-deep .el-overlay {
  883. z-index: 2000 !important;
  884. }
  885. /* 修复单选框样式 */
  886. ::v-deep .el-radio {
  887. margin-right: 8px !important;
  888. }
  889. ::v-deep .el-radio__label {
  890. font-size: 14px;
  891. }
  892. /* 标题行样式 */
  893. .list-title-row {
  894. display: flex;
  895. justify-content: space-between;
  896. align-items: center;
  897. margin-bottom: 8px;
  898. }
  899. .list-title-row .list-title {
  900. margin: 0;
  901. flex: 1;
  902. }
  903. /* 空数据行样式 */
  904. .empty-row {
  905. justify-content: center;
  906. align-items: center;
  907. padding: 20px;
  908. border-bottom: none;
  909. }
  910. .empty-row .empty-hint {
  911. text-align: center;
  912. color: #999;
  913. width: 100%;
  914. }
  915. </style>