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.

1744 lines
51 KiB

3 months ago
3 months ago
5 months ago
5 months ago
5 months ago
3 months ago
5 months ago
3 months ago
5 months ago
3 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
3 months ago
5 months ago
3 months ago
  1. <template>
  2. <div class="packing-detail-tab">
  3. <!-- 批量编辑按钮 -->
  4. <div class="batch-edit-toolbar" v-if="showActions">
  5. <el-button type="primary" size="small" icon="el-icon-edit" @click="openBatchEditDialog">
  6. 批量编辑
  7. </el-button>
  8. </div>
  9. <el-table
  10. :data="dataListBoxes"
  11. :height="height"
  12. border
  13. v-loading="dataListLoading"
  14. style="width: 100%;"
  15. :expand-row-keys="expandedBoxRows"
  16. :row-key="getBoxRowKey"
  17. @expand-change="handleBoxExpand"
  18. show-summary
  19. :summary-method="getSummaries">
  20. <el-table-column type="expand">
  21. <template slot-scope="props">
  22. <!-- 加载状态 -->
  23. <div v-if="props.row.loading" class="expand-loading">
  24. <i class="el-icon-loading"></i>
  25. <span>正在加载明细数据...</span>
  26. </div>
  27. <!-- 明细内容区域 -->
  28. <div v-else class="expand-detail-container">
  29. <!-- 明细表格 -->
  30. <el-table
  31. v-if="props.row.palletDetails && props.row.palletDetails.length > 0"
  32. :data="props.row.palletDetails"
  33. :max-height="300"
  34. size="mini"
  35. stripe
  36. class="expand-detail-table">
  37. <el-table-column
  38. v-for="(item,index) in columnList3" :key="index"
  39. :sortable="item.columnSortable"
  40. :prop="item.columnProp"
  41. :header-align="item.headerAlign"
  42. :show-overflow-tooltip="item.showOverflowTooltip"
  43. :align="item.align"
  44. :min-width="item.columnWidth"
  45. :label="item.columnLabel">
  46. <template slot-scope="scope">
  47. <span v-if="!item.columnHidden">
  48. {{ formatDetailValue(scope.row[item.columnProp], item.columnProp) }}
  49. </span>
  50. <span v-if="item.columnImage">
  51. <img :src="scope.row[item.columnProp]" style="width: 100px; height: 80px"/>
  52. </span>
  53. </template>
  54. </el-table-column>
  55. <!-- 明细操作列 -->
  56. <el-table-column
  57. label="操作"
  58. width="120"
  59. align="center"
  60. fixed="right"
  61. v-if="showActions">
  62. <template slot-scope="scope">
  63. <a type="text" size="small" @click="handleEditDetail(scope.row, props.row)">修改</a>
  64. <a type="text" size="small" @click="handleDeleteDetail(scope.row, props.row)">删除</a>
  65. </template>
  66. </el-table-column>
  67. </el-table>
  68. <!-- 空数据状态 -->
  69. <div v-else class="expand-empty-state">
  70. <i class="el-icon-box"></i>
  71. <p>暂无明细数据</p>
  72. <span>该栈板下暂时没有装箱明细信息</span>
  73. </div>
  74. </div>
  75. </template>
  76. </el-table-column>
  77. <el-table-column
  78. v-for="(item,index) in columnListBoxes" :key="index"
  79. :sortable="item.columnSortable"
  80. :prop="item.columnProp"
  81. :header-align="item.headerAlign"
  82. :show-overflow-tooltip="item.showOverflowTooltip"
  83. :align="item.align"
  84. :fixed="item.fixed==''?false:item.fixed"
  85. :min-width="item.columnWidth"
  86. :label="item.columnLabel">
  87. <template slot-scope="scope">
  88. <span v-if="!item.columnHidden"> {{ scope.row[item.columnProp] }}</span>
  89. <span v-if="item.columnImage"><img :src="scope.row[item.columnProp]"
  90. style="width: 100px; height: 80px"/></span>
  91. </template>
  92. </el-table-column>
  93. <!-- 主表格操作列 -->
  94. <el-table-column
  95. label="操作"
  96. width="120"
  97. align="center"
  98. fixed="right"
  99. v-if="showActions">
  100. <template slot-scope="scope">
  101. <a type="text" size="small" @click="handleEditBox(scope.row)">修改</a>
  102. <a type="text" size="small" @click="handleDeleteBox(scope.row)">删除</a>
  103. </template>
  104. </el-table-column>
  105. </el-table>
  106. <!-- 修改箱信息弹窗 -->
  107. <el-dialog
  108. title="修改箱信息"
  109. :visible.sync="editBoxDialogVisible"
  110. width="200px"
  111. :close-on-click-modal="false">
  112. <el-form :model="editBoxForm" ref="editBoxForm" label-position="top" label-width="100px">
  113. <el-row :gutter="20">
  114. <el-col :span="24">
  115. <el-form-item label="箱数" prop="box_qty">
  116. <el-input v-model="editBoxForm.box_qty" @input="onBoxQtyChange" :min="1" :precision="0" style="width: 100%"></el-input>
  117. </el-form-item>
  118. </el-col>
  119. <el-col :span="24">
  120. <el-form-item label="毛重" prop="grossWeight">
  121. <el-input v-model="editBoxForm.grossWeight" @input="onGrossWeightChange" :min="0" :precision="2" style="width: 100%"></el-input>
  122. </el-form-item>
  123. </el-col>
  124. <el-col :span="24">
  125. <el-form-item label="净重" prop="netWeight">
  126. <el-input v-model="editBoxForm.netWeight" @input="onNetWeightChange" :min="0" :precision="2" style="width: 100%"></el-input>
  127. </el-form-item>
  128. </el-col>
  129. </el-row>
  130. </el-form>
  131. <div slot="footer" class="dialog-footer">
  132. <el-button @click="editBoxDialogVisible = false">取消</el-button>
  133. <el-button type="primary" @click="submitEditBox" >确定</el-button>
  134. </div>
  135. </el-dialog>
  136. <!-- 修改明细弹窗 -->
  137. <el-dialog
  138. title="修改明细信息"
  139. :visible.sync="editDetailDialogVisible"
  140. width="400px"
  141. :close-on-click-modal="false">
  142. <el-form :model="editDetailForm" ref="editDetailForm" label-position="top" label-width="100px">
  143. <el-row :gutter="20">
  144. <el-col :span="12">
  145. <el-form-item label="PO" prop="poNo">
  146. <el-input v-model="editDetailForm.poNo" disabled></el-input>
  147. </el-form-item>
  148. </el-col>
  149. <el-col :span="12">
  150. <el-form-item label="PN" prop="pn">
  151. <el-input v-model="editDetailForm.pn" disabled></el-input>
  152. </el-form-item>
  153. </el-col>
  154. <el-col :span="12">
  155. <el-form-item label="数量" prop="qty">
  156. <el-input v-model="editDetailForm.qty" style="width: 100%"></el-input>
  157. </el-form-item>
  158. </el-col>
  159. <el-col :span="12">
  160. <el-form-item label="Rolls" prop="rolls">
  161. <el-input v-model="editDetailForm.rolls" style="width: 100%"></el-input>
  162. </el-form-item>
  163. </el-col>
  164. </el-row>
  165. </el-form>
  166. <div slot="footer" class="dialog-footer">
  167. <el-button @click="editDetailDialogVisible = false">取消</el-button>
  168. <el-button type="primary" @click="submitEditDetail" >确定</el-button>
  169. </div>
  170. </el-dialog>
  171. <!-- 批量编辑弹窗 -->
  172. <el-dialog
  173. title="批量编辑装箱明细"
  174. :visible.sync="batchEditDialogVisible"
  175. width="60%"
  176. top="5vh"
  177. custom-class="batch-edit-dialog"
  178. :close-on-click-modal="false"
  179. @open="loadBatchEditData">
  180. <div class="batch-edit-container" v-loading="batchEditLoading">
  181. <!-- 操作提示 -->
  182. <div class="batch-edit-tips">
  183. <el-alert
  184. type="info"
  185. :closable="false"
  186. show-icon>
  187. <template slot="title">
  188. <span>直接在表格中编辑数据修改后点击"保存所有修改"按钮提交</span>
  189. <span v-if="batchEditModifiedCount > 0" class="modified-count">
  190. 已修改 <b>{{ batchEditModifiedCount }}</b>
  191. </span>
  192. </template>
  193. </el-alert>
  194. </div>
  195. <!-- 行合并表格 -->
  196. <el-table
  197. :data="batchEditTableData"
  198. :span-method="batchEditSpanMethod"
  199. border
  200. size="small"
  201. max-height="60vh"
  202. style="width: 100%"
  203. :row-class-name="getBatchEditRowClassName"
  204. :header-cell-style="{background:'#f5f7fa', color:'#606266', fontWeight:'600'}">
  205. <!-- Box信息列会合并 -->
  206. <el-table-column label="序号" prop="item_no" width="60" align="center">
  207. <template slot-scope="scope">
  208. <span class="box-info-cell">{{ scope.row.item_no }}</span>
  209. </template>
  210. </el-table-column>
  211. <el-table-column label="箱数" prop="box_qty" width="80" align="center">
  212. <template slot-scope="scope">
  213. <el-input
  214. v-model="scope.row.box_qty"
  215. size="mini"
  216. type="number"
  217. :class="{'modified-input': isBoxFieldModified(scope.row, 'box_qty')}"
  218. @input="onBatchBoxQtyInput(scope.row)"
  219. @change="onBatchBoxFieldChange(scope.row, 'box_qty')">
  220. </el-input>
  221. </template>
  222. </el-table-column>
  223. <el-table-column label="毛重" prop="grossWeight" width="100" align="center">
  224. <template slot-scope="scope">
  225. <el-input
  226. v-model="scope.row.grossWeight"
  227. size="mini"
  228. type="number"
  229. :class="{'modified-input': isBoxFieldModified(scope.row, 'grossWeight')}"
  230. @input="onBatchGrossWeightInput(scope.row)"
  231. @change="onBatchBoxFieldChange(scope.row, 'grossWeight')">
  232. </el-input>
  233. </template>
  234. </el-table-column>
  235. <el-table-column label="净重" prop="netWeight" width="100" align="center">
  236. <template slot-scope="scope">
  237. <el-input
  238. v-model="scope.row.netWeight"
  239. size="mini"
  240. type="number"
  241. :class="{'modified-input': isBoxFieldModified(scope.row, 'netWeight')}"
  242. @input="onBatchNetWeightInput(scope.row)"
  243. @change="onBatchBoxFieldChange(scope.row, 'netWeight')">
  244. </el-input>
  245. </template>
  246. </el-table-column>
  247. <!-- 明细信息列不合并 -->
  248. <el-table-column label="PO" prop="poNo" min-width="120" align="left">
  249. <template slot-scope="scope">
  250. <span>{{ scope.row.poNo || '-' }}</span>
  251. </template>
  252. </el-table-column>
  253. <el-table-column label="PN" prop="pn" min-width="120" align="left">
  254. <template slot-scope="scope">
  255. <span>{{ scope.row.pn || '-' }}</span>
  256. </template>
  257. </el-table-column>
  258. <el-table-column label="数量" prop="qty" width="100" align="center">
  259. <template slot-scope="scope">
  260. <el-input
  261. v-model="scope.row.qty"
  262. size="mini"
  263. type="number"
  264. :class="{'modified-input': isDetailFieldModified(scope.row, 'qty')}"
  265. @change="onBatchDetailFieldChange(scope.row, 'qty')">
  266. </el-input>
  267. </template>
  268. </el-table-column>
  269. <el-table-column label="Rolls" prop="rolls" width="80" align="center">
  270. <template slot-scope="scope">
  271. <el-input
  272. v-model="scope.row.rolls"
  273. size="mini"
  274. type="number"
  275. :class="{'modified-input': isDetailFieldModified(scope.row, 'rolls')}"
  276. @change="onBatchDetailFieldChange(scope.row, 'rolls')">
  277. </el-input>
  278. </template>
  279. </el-table-column>
  280. </el-table>
  281. </div>
  282. <div slot="footer" class="dialog-footer">
  283. <el-button @click="batchEditDialogVisible = false">取消</el-button>
  284. <el-button
  285. type="primary"
  286. :loading="batchEditSaving"
  287. :disabled="batchEditModifiedCount === 0"
  288. @click="saveBatchEdit">
  289. 保存所有修改 ({{ batchEditModifiedCount }})
  290. </el-button>
  291. </div>
  292. </el-dialog>
  293. </div>
  294. </template>
  295. <script>
  296. import { selectBoxList, searchCoDelPalletDataNew, updateBoxInfo, deleteBoxInfo, updateDetailInfo, deleteDetailInfo, batchUpdatePackingInfo } from "@/api/ecss/ecss.js"
  297. export default {
  298. name: "PackingDetailTab",
  299. props: {
  300. currentRow: {
  301. type: Object,
  302. default: () => ({})
  303. },
  304. height: {
  305. type: Number,
  306. default: 200
  307. },
  308. showActions: {
  309. type: Boolean,
  310. default: true
  311. }
  312. },
  313. data() {
  314. return {
  315. dataListBoxes: [], // 箱数据列表
  316. expandedBoxRows: [], // 展开的行
  317. dataListLoading: false,
  318. // 修改箱信息弹窗相关
  319. editBoxDialogVisible: false,
  320. editBoxForm: {
  321. item_no: '',
  322. box_qty: 1,
  323. grossWeight: 0,
  324. netWeight: 0
  325. },
  326. editBoxSubmitting: false,
  327. currentEditBox: null,
  328. // 修改明细弹窗相关
  329. editDetailDialogVisible: false,
  330. editDetailForm: {
  331. itemNo: '',
  332. poNo: '',
  333. pn: '',
  334. qty: 1,
  335. rolls: 0
  336. },
  337. editDetailSubmitting: false,
  338. currentEditDetail: null,
  339. currentEditBoxForDetail: null,
  340. // 批量编辑弹窗相关
  341. batchEditDialogVisible: false,
  342. batchEditLoading: false,
  343. batchEditSaving: false,
  344. batchEditTableData: [], // 扁平化的表格数据
  345. batchEditOriginalData: {}, // 原始数据快照(用于对比是否修改)
  346. batchEditModifiedBoxes: {}, // 修改过的Box记录
  347. batchEditModifiedDetails: {}, // 修改过的明细记录
  348. batchEditSpanInfo: [], // 行合并信息
  349. // 装箱明细列定义
  350. columnList3: [
  351. {
  352. userId: this.$store.state.user.name,
  353. functionId: 801002,
  354. serialNumber: '801002Table3ItemNo',
  355. tableId: "801002Table3",
  356. tableName: "装箱明细",
  357. columnProp: "itemNo",
  358. headerAlign: "center",
  359. align: "right",
  360. columnLabel: "序号",
  361. columnHidden: false,
  362. columnImage: false,
  363. columnSortable: false,
  364. sortLv: 0,
  365. status: true,
  366. fixed: '',
  367. columnWidth: 40
  368. },
  369. {
  370. userId: this.$store.state.user.name,
  371. functionId: 801002,
  372. serialNumber: '801002Table3PoNo',
  373. tableId: "801002Table3",
  374. tableName: "装箱明细",
  375. columnProp: "poNo",
  376. headerAlign: "center",
  377. align: "left",
  378. columnLabel: "PO",
  379. columnHidden: false,
  380. columnImage: false,
  381. columnSortable: false,
  382. sortLv: 0,
  383. status: true,
  384. fixed: '',
  385. columnWidth: 100
  386. },
  387. {
  388. userId: this.$store.state.user.name,
  389. functionId: 801002,
  390. serialNumber: '801002Table3PN',
  391. tableId: "801002Table3",
  392. tableName: "装箱明细",
  393. columnProp: "pn",
  394. headerAlign: "center",
  395. align: "left",
  396. columnLabel: "PN",
  397. columnHidden: false,
  398. columnImage: false,
  399. columnSortable: false,
  400. sortLv: 0,
  401. status: true,
  402. fixed: '',
  403. columnWidth: 100
  404. },
  405. {
  406. userId: this.$store.state.user.name,
  407. functionId: 801002,
  408. serialNumber: '801002Table3Qty',
  409. tableId: "801002Table3",
  410. tableName: "装箱明细",
  411. columnProp: "qty",
  412. headerAlign: "center",
  413. align: "right",
  414. columnLabel: "数量",
  415. columnHidden: false,
  416. columnImage: false,
  417. columnSortable: false,
  418. sortLv: 0,
  419. status: true,
  420. fixed: '',
  421. columnWidth: 50
  422. },
  423. {
  424. userId: this.$store.state.user.name,
  425. functionId: 801002,
  426. serialNumber: '801002Table3Rolls',
  427. tableId: "801002Table3",
  428. tableName: "装箱明细",
  429. columnProp: "rolls",
  430. headerAlign: "center",
  431. align: "right",
  432. columnLabel: "Rolls",
  433. columnHidden: false,
  434. columnImage: false,
  435. columnSortable: false,
  436. sortLv: 0,
  437. status: true,
  438. fixed: '',
  439. columnWidth: 50
  440. },
  441. ],
  442. // 箱数据列定义
  443. columnListBoxes: [
  444. {
  445. userId: this.$store.state.user.name,
  446. functionId: 801002,
  447. serialNumber: '801002Table3ItemNo',
  448. tableId: "801002Table3",
  449. tableName: "装箱明细",
  450. columnProp: "item_no",
  451. headerAlign: "center",
  452. align: "right",
  453. columnLabel: "序号",
  454. columnHidden: false,
  455. columnImage: false,
  456. columnSortable: false,
  457. sortLv: 0,
  458. status: true,
  459. fixed: '',
  460. columnWidth: 40
  461. },
  462. {
  463. userId: this.$store.state.user.name,
  464. functionId: 801002,
  465. serialNumber: '801002Table3BoxQty',
  466. tableId: "801002Table3",
  467. tableName: "装箱明细",
  468. columnProp: "box_qty",
  469. headerAlign: "center",
  470. align: "right",
  471. columnLabel: "箱数",
  472. columnHidden: false,
  473. columnImage: false,
  474. columnSortable: false,
  475. sortLv: 0,
  476. status: true,
  477. fixed: '',
  478. columnWidth: 50
  479. },
  480. {
  481. userId: this.$store.state.user.name,
  482. functionId: 801002,
  483. serialNumber: '801002TableGrossWeight',
  484. tableId: "801002Table3",
  485. tableName: "装箱明细",
  486. columnProp: "grossWeight",
  487. headerAlign: "center",
  488. align: "right",
  489. columnLabel: "毛重",
  490. columnHidden: false,
  491. columnImage: false,
  492. columnSortable: false,
  493. sortLv: 0,
  494. status: true,
  495. fixed: '',
  496. columnWidth: 50
  497. },
  498. {
  499. userId: this.$store.state.user.name,
  500. functionId: 801002,
  501. serialNumber: '801002TableNetWeight',
  502. tableId: "801002Table3",
  503. tableName: "装箱明细",
  504. columnProp: "netWeight",
  505. headerAlign: "center",
  506. align: "right",
  507. columnLabel: "净重",
  508. columnHidden: false,
  509. columnImage: false,
  510. columnSortable: false,
  511. sortLv: 0,
  512. status: true,
  513. fixed: '',
  514. columnWidth: 50
  515. },
  516. ]
  517. }
  518. },
  519. computed: {
  520. // 批量编辑修改数量
  521. batchEditModifiedCount() {
  522. return Object.keys(this.batchEditModifiedBoxes).length +
  523. Object.keys(this.batchEditModifiedDetails).length;
  524. }
  525. },
  526. watch: {
  527. currentRow: {
  528. handler(newVal) {
  529. if (newVal && newVal.site) {
  530. this.loadBoxList();
  531. }
  532. },
  533. deep: true,
  534. immediate: true
  535. }
  536. },
  537. methods: {
  538. // 装箱明细相关方法
  539. loadBoxList() {
  540. if (!this.currentRow || !this.currentRow.site) {
  541. this.dataListBoxes = [];
  542. return;
  543. }
  544. // 清理之前的展开状态
  545. this.expandedBoxRows = [];
  546. this.dataListLoading = true;
  547. // 获取ecss_CoDelBoxList数据
  548. selectBoxList(this.currentRow).then(({data}) => {
  549. if (data && data.code == 0) {
  550. this.dataListBoxes = data.rows.map(row => ({
  551. ...row,
  552. palletDetails: [], // 初始化明细数据为空
  553. loading: false, // 初始化加载状态
  554. hadDetails: false // 初始化是否有明细标记
  555. }))
  556. } else {
  557. this.dataListBoxes = [];
  558. }
  559. }).catch(error => {
  560. console.error('加载箱数据失败:', error);
  561. this.dataListBoxes = [];
  562. }).finally(() => {
  563. this.dataListLoading = false;
  564. });
  565. },
  566. // 获取行的唯一标识
  567. getBoxRowKey(row) {
  568. // 使用多个字段组合确保唯一性
  569. return `${row.item_no || ''}_${row.palletRemark || ''}_${row.box_qty || ''}`;
  570. },
  571. // 处理行展开事件
  572. handleBoxExpand(row, expandedRows) {
  573. // 判断当前操作是展开还是收起
  574. const isExpanding = expandedRows.includes(row);
  575. if (isExpanding) {
  576. // 如果是展开操作,则展开所有行
  577. this.expandAll();
  578. } else {
  579. // 如果是收起操作,则收起所有行
  580. this.collapseAll();
  581. }
  582. },
  583. // 加载栈板明细数据
  584. loadPalletDetails(boxRow) {
  585. if (boxRow.palletDetails && boxRow.palletDetails.length > 0) {
  586. // 如果已经加载过,直接返回
  587. return;
  588. }
  589. // 设置加载状态
  590. this.$set(boxRow, 'loading', true);
  591. // 构造查询参数
  592. let detailParams = {
  593. site: this.currentRow.site,
  594. buNo: this.currentRow.buNo,
  595. delNo: this.currentRow.delNo,
  596. seqNo: boxRow.item_no,
  597. palletRemark: boxRow.palletRemark
  598. };
  599. // 调用API获取明细数据
  600. searchCoDelPalletDataNew(detailParams).then(({data}) => {
  601. if (data && data.code == 0) {
  602. // 将明细数据赋值给当前行
  603. this.$set(boxRow, 'palletDetails', data.rows);
  604. // 如果明细为空,且之前有明细数据,说明明细被删除了,需要检查box是否也应该被删除
  605. if (data.rows.length === 0 && boxRow.hadDetails) {
  606. // 延迟刷新列表,让后端有时间处理删除逻辑
  607. setTimeout(() => {
  608. this.loadBoxList();
  609. }, 500);
  610. }
  611. // 标记是否有过明细数据
  612. this.$set(boxRow, 'hadDetails', data.rows.length > 0);
  613. } else {
  614. this.$set(boxRow, 'palletDetails', []);
  615. this.$set(boxRow, 'hadDetails', false);
  616. }
  617. }).catch(error => {
  618. console.error('加载栈板明细数据失败:', error);
  619. this.$set(boxRow, 'palletDetails', []);
  620. this.$set(boxRow, 'hadDetails', false);
  621. }).finally(() => {
  622. // 清除加载状态
  623. this.$set(boxRow, 'loading', false);
  624. });
  625. },
  626. // 格式化明细值显示
  627. formatDetailValue(value, columnProp) {
  628. if (value === null || value === undefined || value === '') {
  629. return '-';
  630. }
  631. // 数字类型格式化
  632. if (columnProp === 'qty' || columnProp === 'rolls') {
  633. return parseInt(value).toLocaleString();
  634. }
  635. return value;
  636. },
  637. // 展开所有行
  638. expandAll() {
  639. // 获取所有行的key
  640. this.expandedBoxRows = this.dataListBoxes.map(row => this.getBoxRowKey(row));
  641. // 为每个行加载明细数据
  642. this.dataListBoxes.forEach(row => {
  643. this.loadPalletDetails(row);
  644. });
  645. },
  646. // 收起所有行
  647. collapseAll() {
  648. this.expandedBoxRows = [];
  649. },
  650. // 刷新数据的公共方法
  651. refresh() {
  652. this.loadBoxList();
  653. },
  654. /*
  655. * 删除逻辑说明
  656. * 1. 删除明细时
  657. * - 先删除明细数据
  658. * - 检查该明细所属的box是否还有其他明细
  659. * - 如果没有明细了自动删除对应的box
  660. *
  661. * 2. 删除box时
  662. * - 先检查是否有明细数据
  663. * - 如果有明细提示用户并同时删除所有明细和box
  664. * - 如果没有明细直接删除box
  665. */
  666. // 处理修改箱信息
  667. handleEditBox(boxRow) {
  668. this.currentEditBox = boxRow;
  669. this.editBoxForm.item_no = boxRow.item_no;
  670. this.editBoxForm.box_qty = boxRow.box_qty;
  671. this.editBoxForm.grossWeight = boxRow.grossWeight;
  672. this.editBoxForm.netWeight = boxRow.netWeight;
  673. this.editBoxDialogVisible = true;
  674. },
  675. // 处理删除箱信息
  676. handleDeleteBox(boxRow) {
  677. // 直接删除box及其所有明细,无需校验
  678. this.deleteBoxWithDetails(boxRow);
  679. },
  680. // 删除箱信息(包含明细)
  681. deleteBoxWithDetails(boxRow) {
  682. this.$confirm(`确定要删除序号为 ${boxRow.item_no} 的箱信息吗?删除后无法恢复!`, '警告', {
  683. confirmButtonText: '确定',
  684. cancelButtonText: '取消',
  685. type: 'warning'
  686. }).then(() => {
  687. // 构造删除参数
  688. const deleteParams = {
  689. site: this.currentRow.site,
  690. buNo: this.currentRow.buNo,
  691. delNo: this.currentRow.delNo,
  692. item_no: boxRow.item_no,
  693. palletRemark: boxRow.palletRemark
  694. };
  695. deleteBoxInfo(deleteParams).then(({data}) => {
  696. if (data && data.code === 0) {
  697. this.loadBoxList()
  698. this.$message({
  699. message: '操作成功',
  700. type: 'success',
  701. duration: 1500,
  702. onClose: () => {}
  703. })
  704. } else {
  705. this.$alert(data.msg, '错误', {
  706. confirmButtonText: '确定'
  707. })
  708. }
  709. })
  710. });
  711. },
  712. // 处理修改明细
  713. handleEditDetail(detailRow, boxRow) {
  714. this.currentEditDetail = detailRow;
  715. this.currentEditBoxForDetail = boxRow;
  716. this.editDetailForm.itemNo = detailRow.itemNo;
  717. this.editDetailForm.poNo = detailRow.poNo;
  718. this.editDetailForm.pn = detailRow.pn;
  719. this.editDetailForm.qty = detailRow.qty;
  720. this.editDetailForm.rolls = detailRow.rolls;
  721. this.editDetailDialogVisible = true;
  722. },
  723. /**
  724. * 箱数改变时自动计算净重
  725. * 保持毛重不变重新计算净重
  726. * 计算公式净重 = 毛重 - box_qty/2
  727. */
  728. onBoxQtyChange(value) {
  729. // 防止循环触发,使用标志位
  730. if (this._isCalculating) {
  731. return;
  732. }
  733. this._isCalculating = true;
  734. try {
  735. const boxQty = parseFloat(value) || 0;
  736. const grossWeight = parseFloat(this.editBoxForm.grossWeight) || 0;
  737. // 计算净重:净重 = 毛重 - box_qty/2
  738. const netWeight = grossWeight - (boxQty / 2);
  739. // 保留2位小数
  740. this.editBoxForm.netWeight = netWeight.toFixed(2);
  741. } finally {
  742. this._isCalculating = false;
  743. }
  744. },
  745. /**
  746. * 毛重改变时自动计算净重
  747. * 计算公式净重 = 毛重 - box_qty/2
  748. */
  749. onGrossWeightChange(value) {
  750. // 防止循环触发,使用标志位
  751. if (this._isCalculating) {
  752. return;
  753. }
  754. this._isCalculating = true;
  755. try {
  756. const grossWeight = parseFloat(value) || 0;
  757. const boxQty = parseFloat(this.editBoxForm.box_qty) || 0;
  758. // 计算净重:净重 = 毛重 - box_qty/2
  759. const netWeight = grossWeight - (boxQty / 2);
  760. // 保留2位小数
  761. this.editBoxForm.netWeight = netWeight.toFixed(2);
  762. } finally {
  763. this._isCalculating = false;
  764. }
  765. },
  766. /**
  767. * 净重改变时自动计算毛重
  768. * 反向计算公式毛重 = 净重 + box_qty/2
  769. */
  770. onNetWeightChange(value) {
  771. // 防止循环触发,使用标志位
  772. if (this._isCalculating) {
  773. return;
  774. }
  775. this._isCalculating = true;
  776. try {
  777. const netWeight = parseFloat(value) || 0;
  778. const boxQty = parseFloat(this.editBoxForm.box_qty) || 0;
  779. // 反向计算毛重:毛重 = 净重 + box_qty/2
  780. const grossWeight = netWeight + (boxQty / 2);
  781. // 保留2位小数
  782. this.editBoxForm.grossWeight = grossWeight.toFixed(2);
  783. } finally {
  784. this._isCalculating = false;
  785. }
  786. },
  787. // 提交修改箱信息
  788. submitEditBox() {
  789. this.$refs.editBoxForm.validate(valid => {
  790. if (valid) {
  791. this.editBoxSubmitting = true;
  792. // 构造更新参数
  793. const updateParams = {
  794. site: this.currentRow.site,
  795. buNo: this.currentRow.buNo,
  796. delNo: this.currentRow.delNo,
  797. item_no: this.currentEditBox.item_no,
  798. palletRemark: this.currentEditBox.palletRemark,
  799. box_qty: this.editBoxForm.box_qty,
  800. grossWeight: this.editBoxForm.grossWeight,
  801. netWeight: this.editBoxForm.netWeight,
  802. updateBy: this.$store.state.user.name
  803. };
  804. updateBoxInfo(updateParams).then(({ data }) => {
  805. if (data && data.code === 0) {
  806. this.editBoxDialogVisible = false;
  807. this.loadBoxList()
  808. this.$message({
  809. message: '操作成功',
  810. type: 'success',
  811. duration: 1500,
  812. onClose: () => {}
  813. })
  814. } else {
  815. this.$alert(data.msg, '错误', {
  816. confirmButtonText: '确定'
  817. })
  818. }
  819. });
  820. }
  821. });
  822. },
  823. // 提交修改明细
  824. submitEditDetail() {
  825. this.$refs.editDetailForm.validate(valid => {
  826. if (valid) {
  827. this.editDetailSubmitting = true;
  828. // 构造更新参数
  829. const updateParams = {
  830. site: this.currentRow.site,
  831. buNo: this.currentRow.buNo,
  832. delNo: this.currentRow.delNo,
  833. seqNo: this.currentEditDetail.seqNo,
  834. itemNo: this.currentEditDetail.itemNo,
  835. notifyDetailItemNo: this.currentEditDetail.notifyDetailItemNo,
  836. poNo: this.editDetailForm.poNo,
  837. pn: this.editDetailForm.pn,
  838. qty: this.editDetailForm.qty,
  839. oldQty: this.currentEditDetail.qty,
  840. rolls: this.editDetailForm.rolls,
  841. updateBy: this.$store.state.user.name
  842. };
  843. updateDetailInfo(updateParams).then(({ data }) => {
  844. if (data && data.code === 0) {
  845. this.editDetailDialogVisible = false;
  846. this.loadBoxList()
  847. this.$message({
  848. message: '操作成功',
  849. type: 'success',
  850. duration: 1500,
  851. onClose: () => {}
  852. })
  853. } else {
  854. this.$alert(data.msg, '错误', {
  855. confirmButtonText: '确定'
  856. })
  857. }
  858. });
  859. }
  860. });
  861. },
  862. // 删除明细
  863. handleDeleteDetail(detailRow, boxRow) {
  864. this.$confirm(`确定要删除这个明细信息吗?删除后无法恢复!`, '警告', {
  865. confirmButtonText: '确定',
  866. cancelButtonText: '取消',
  867. type: 'warning'
  868. }).then(() => {
  869. // 构造删除参数
  870. const deleteParams = {
  871. site: this.currentRow.site,
  872. buNo: this.currentRow.buNo,
  873. delNo: this.currentRow.delNo,
  874. seqNo: detailRow.seqNo,
  875. itemNo: detailRow.itemNo,
  876. notifyDetailItemNo: detailRow.notifyDetailItemNo,
  877. partNo: detailRow.part_no,
  878. qty: detailRow.qty
  879. };
  880. deleteDetailInfo(deleteParams).then(({data}) => {
  881. if (data && data.code === 0) {
  882. this.loadBoxList()
  883. this.$message({
  884. message: '操作成功',
  885. type: 'success',
  886. duration: 1500,
  887. onClose: () => {}
  888. })
  889. } else {
  890. this.$alert(data.msg, '错误', {
  891. confirmButtonText: '确定'
  892. })
  893. }
  894. })
  895. });
  896. },
  897. // 计算合计行数据
  898. getSummaries(param) {
  899. const { columns, data } = param;
  900. const sums = [];
  901. columns.forEach((column, index) => {
  902. if (index === 0) {
  903. // 第一列(展开列)显示"合计"
  904. sums[index] = '合计';
  905. return;
  906. }
  907. const values = data.map(item => Number(item[column.property]));
  908. // 根据列属性计算合计
  909. if (column.property === 'box_qty') {
  910. // 箱数合计
  911. const sum = values.reduce((prev, curr) => {
  912. const value = Number(curr);
  913. if (!isNaN(value)) {
  914. return prev + value;
  915. } else {
  916. return prev;
  917. }
  918. }, 0);
  919. sums[index] = sum;
  920. } else if (column.property === 'grossWeight') {
  921. // 毛重合计
  922. const sum = values.reduce((prev, curr) => {
  923. const value = Number(curr);
  924. if (!isNaN(value)) {
  925. return prev + value;
  926. } else {
  927. return prev;
  928. }
  929. }, 0);
  930. sums[index] = sum.toFixed(3);
  931. } else if (column.property === 'netWeight') {
  932. // 净重合计
  933. const sum = values.reduce((prev, curr) => {
  934. const value = Number(curr);
  935. if (!isNaN(value)) {
  936. return prev + value;
  937. } else {
  938. return prev;
  939. }
  940. }, 0);
  941. sums[index] = sum.toFixed(3);
  942. } else if (column.property === 'item_no') {
  943. // 序号列显示空
  944. sums[index] = '';
  945. } else {
  946. // 其他列显示空
  947. sums[index] = '';
  948. }
  949. });
  950. return sums;
  951. },
  952. // ========== 批量编辑相关方法 ==========
  953. /**
  954. * 打开批量编辑弹窗
  955. */
  956. openBatchEditDialog() {
  957. this.batchEditDialogVisible = true;
  958. },
  959. /**
  960. * 加载批量编辑数据
  961. * 将Box和明细数据扁平化为一个表格数据
  962. */
  963. async loadBatchEditData() {
  964. this.batchEditLoading = true;
  965. this.batchEditTableData = [];
  966. this.batchEditOriginalData = {};
  967. this.batchEditModifiedBoxes = {};
  968. this.batchEditModifiedDetails = {};
  969. this.batchEditSpanInfo = [];
  970. try {
  971. // 先获取Box列表
  972. const boxResponse = await selectBoxList(this.currentRow);
  973. if (!boxResponse.data || boxResponse.data.code !== 0) {
  974. this.$message.error('加载箱数据失败');
  975. return;
  976. }
  977. const boxes = boxResponse.data.rows || [];
  978. const tableData = [];
  979. const spanInfo = [];
  980. // 遍历每个Box,获取其明细
  981. for (const box of boxes) {
  982. const detailParams = {
  983. site: this.currentRow.site,
  984. buNo: this.currentRow.buNo,
  985. delNo: this.currentRow.delNo,
  986. seqNo: box.item_no,
  987. palletRemark: box.palletRemark
  988. };
  989. const detailResponse = await searchCoDelPalletDataNew(detailParams);
  990. const details = (detailResponse.data && detailResponse.data.code === 0)
  991. ? detailResponse.data.rows || []
  992. : [];
  993. // 记录该Box的起始行索引和跨行数
  994. const startIndex = tableData.length;
  995. const rowCount = details.length > 0 ? details.length : 1;
  996. spanInfo.push({ startIndex, rowCount, boxKey: this.getBoxRowKey(box) });
  997. if (details.length > 0) {
  998. // 有明细时,为每个明细创建一行
  999. details.forEach((detail, idx) => {
  1000. const rowData = {
  1001. // Box信息
  1002. _boxKey: this.getBoxRowKey(box),
  1003. _isFirstRowOfBox: idx === 0,
  1004. _rowSpan: idx === 0 ? details.length : 0,
  1005. item_no: box.item_no,
  1006. palletRemark: box.palletRemark,
  1007. box_qty: box.box_qty,
  1008. grossWeight: box.grossWeight,
  1009. netWeight: box.netWeight,
  1010. // 明细信息
  1011. _detailKey: `${detail.seqNo}_${detail.itemNo}_${detail.notifyDetailItemNo}`,
  1012. _hasDetail: true,
  1013. seqNo: detail.seqNo,
  1014. itemNo: detail.itemNo,
  1015. notifyDetailItemNo: detail.notifyDetailItemNo,
  1016. poNo: detail.poNo,
  1017. pn: detail.pn,
  1018. qty: detail.qty,
  1019. rolls: detail.rolls
  1020. };
  1021. tableData.push(rowData);
  1022. // 保存原始数据
  1023. this.batchEditOriginalData[rowData._detailKey] = {
  1024. qty: detail.qty,
  1025. rolls: detail.rolls
  1026. };
  1027. });
  1028. // 保存Box原始数据
  1029. this.batchEditOriginalData[this.getBoxRowKey(box)] = {
  1030. box_qty: box.box_qty,
  1031. grossWeight: box.grossWeight,
  1032. netWeight: box.netWeight
  1033. };
  1034. } else {
  1035. // 没有明细时,创建一行空明细的数据
  1036. const rowData = {
  1037. _boxKey: this.getBoxRowKey(box),
  1038. _isFirstRowOfBox: true,
  1039. _rowSpan: 1,
  1040. item_no: box.item_no,
  1041. palletRemark: box.palletRemark,
  1042. box_qty: box.box_qty,
  1043. grossWeight: box.grossWeight,
  1044. netWeight: box.netWeight,
  1045. _hasDetail: false,
  1046. poNo: '',
  1047. pn: '',
  1048. qty: '',
  1049. rolls: ''
  1050. };
  1051. tableData.push(rowData);
  1052. // 保存Box原始数据
  1053. this.batchEditOriginalData[this.getBoxRowKey(box)] = {
  1054. box_qty: box.box_qty,
  1055. grossWeight: box.grossWeight,
  1056. netWeight: box.netWeight
  1057. };
  1058. }
  1059. }
  1060. this.batchEditTableData = tableData;
  1061. this.batchEditSpanInfo = spanInfo;
  1062. } catch (error) {
  1063. console.error('加载批量编辑数据失败:', error);
  1064. this.$message.error('加载数据失败');
  1065. } finally {
  1066. this.batchEditLoading = false;
  1067. }
  1068. },
  1069. /**
  1070. * 表格行合并方法
  1071. */
  1072. batchEditSpanMethod({ row, column, rowIndex, columnIndex }) {
  1073. // Box信息列(前4列:序号、箱数、毛重、净重)需要合并
  1074. if (columnIndex < 4) {
  1075. if (row._isFirstRowOfBox) {
  1076. return {
  1077. rowspan: row._rowSpan,
  1078. colspan: 1
  1079. };
  1080. } else {
  1081. return {
  1082. rowspan: 0,
  1083. colspan: 0
  1084. };
  1085. }
  1086. }
  1087. return {
  1088. rowspan: 1,
  1089. colspan: 1
  1090. };
  1091. },
  1092. /**
  1093. * 检查Box字段是否被修改
  1094. */
  1095. isBoxFieldModified(row, field) {
  1096. const boxKey = row._boxKey;
  1097. return this.batchEditModifiedBoxes[boxKey] &&
  1098. this.batchEditModifiedBoxes[boxKey][field] !== undefined;
  1099. },
  1100. /**
  1101. * 检查明细字段是否被修改
  1102. */
  1103. isDetailFieldModified(row, field) {
  1104. if (!row._hasDetail) return false;
  1105. const detailKey = row._detailKey;
  1106. return this.batchEditModifiedDetails[detailKey] &&
  1107. this.batchEditModifiedDetails[detailKey][field] !== undefined;
  1108. },
  1109. /**
  1110. * 批量编辑-箱数输入时联动计算净重
  1111. * 计算公式净重 = 毛重 - box_qty/2
  1112. */
  1113. onBatchBoxQtyInput(row) {
  1114. if (this._isBatchCalculating) return;
  1115. this._isBatchCalculating = true;
  1116. try {
  1117. const boxQty = parseFloat(row.box_qty) || 0;
  1118. const grossWeight = parseFloat(row.grossWeight) || 0;
  1119. // 计算净重:净重 = 毛重 - box_qty/2
  1120. const netWeight = grossWeight - (boxQty / 2);
  1121. row.netWeight = netWeight.toFixed(2);
  1122. // 同步更新所有同Box的行
  1123. const boxKey = row._boxKey;
  1124. this.batchEditTableData.forEach(r => {
  1125. if (r._boxKey === boxKey) {
  1126. r.box_qty = row.box_qty;
  1127. r.netWeight = row.netWeight;
  1128. }
  1129. });
  1130. // 触发净重的变化检测
  1131. this.onBatchBoxFieldChange(row, 'netWeight');
  1132. } finally {
  1133. this._isBatchCalculating = false;
  1134. }
  1135. },
  1136. /**
  1137. * 批量编辑-毛重输入时联动计算净重
  1138. * 计算公式净重 = 毛重 - box_qty/2
  1139. */
  1140. onBatchGrossWeightInput(row) {
  1141. if (this._isBatchCalculating) return;
  1142. this._isBatchCalculating = true;
  1143. try {
  1144. const grossWeight = parseFloat(row.grossWeight) || 0;
  1145. const boxQty = parseFloat(row.box_qty) || 0;
  1146. // 计算净重:净重 = 毛重 - box_qty/2
  1147. const netWeight = grossWeight - (boxQty / 2);
  1148. row.netWeight = netWeight.toFixed(2);
  1149. // 同步更新所有同Box的行
  1150. const boxKey = row._boxKey;
  1151. this.batchEditTableData.forEach(r => {
  1152. if (r._boxKey === boxKey) {
  1153. r.grossWeight = row.grossWeight;
  1154. r.netWeight = row.netWeight;
  1155. }
  1156. });
  1157. // 触发净重的变化检测
  1158. this.onBatchBoxFieldChange(row, 'netWeight');
  1159. } finally {
  1160. this._isBatchCalculating = false;
  1161. }
  1162. },
  1163. /**
  1164. * 批量编辑-净重输入时联动计算毛重
  1165. * 计算公式毛重 = 净重 + box_qty/2
  1166. */
  1167. onBatchNetWeightInput(row) {
  1168. if (this._isBatchCalculating) return;
  1169. this._isBatchCalculating = true;
  1170. try {
  1171. const netWeight = parseFloat(row.netWeight) || 0;
  1172. const boxQty = parseFloat(row.box_qty) || 0;
  1173. // 计算毛重:毛重 = 净重 + box_qty/2
  1174. const grossWeight = netWeight + (boxQty / 2);
  1175. row.grossWeight = grossWeight.toFixed(2);
  1176. // 同步更新所有同Box的行
  1177. const boxKey = row._boxKey;
  1178. this.batchEditTableData.forEach(r => {
  1179. if (r._boxKey === boxKey) {
  1180. r.netWeight = row.netWeight;
  1181. r.grossWeight = row.grossWeight;
  1182. }
  1183. });
  1184. // 触发毛重的变化检测
  1185. this.onBatchBoxFieldChange(row, 'grossWeight');
  1186. } finally {
  1187. this._isBatchCalculating = false;
  1188. }
  1189. },
  1190. /**
  1191. * Box字段变化处理
  1192. */
  1193. onBatchBoxFieldChange(row, field) {
  1194. const boxKey = row._boxKey;
  1195. const original = this.batchEditOriginalData[boxKey];
  1196. if (!original) return;
  1197. // 检查是否与原始值不同
  1198. const currentValue = String(row[field]);
  1199. const originalValue = String(original[field]);
  1200. if (currentValue !== originalValue) {
  1201. // 有变化,记录修改
  1202. if (!this.batchEditModifiedBoxes[boxKey]) {
  1203. this.$set(this.batchEditModifiedBoxes, boxKey, {});
  1204. }
  1205. this.$set(this.batchEditModifiedBoxes[boxKey], field, {
  1206. oldValue: original[field],
  1207. newValue: row[field]
  1208. });
  1209. // 同步更新所有同Box的行
  1210. this.batchEditTableData.forEach(r => {
  1211. if (r._boxKey === boxKey) {
  1212. r[field] = row[field];
  1213. }
  1214. });
  1215. } else {
  1216. // 恢复原值,移除修改记录
  1217. if (this.batchEditModifiedBoxes[boxKey]) {
  1218. this.$delete(this.batchEditModifiedBoxes[boxKey], field);
  1219. if (Object.keys(this.batchEditModifiedBoxes[boxKey]).length === 0) {
  1220. this.$delete(this.batchEditModifiedBoxes, boxKey);
  1221. }
  1222. }
  1223. }
  1224. },
  1225. /**
  1226. * 明细字段变化处理
  1227. */
  1228. onBatchDetailFieldChange(row, field) {
  1229. if (!row._hasDetail) return;
  1230. const detailKey = row._detailKey;
  1231. const original = this.batchEditOriginalData[detailKey];
  1232. if (!original) return;
  1233. const currentValue = String(row[field]);
  1234. const originalValue = String(original[field]);
  1235. if (currentValue !== originalValue) {
  1236. // 有变化,记录修改
  1237. if (!this.batchEditModifiedDetails[detailKey]) {
  1238. this.$set(this.batchEditModifiedDetails, detailKey, {
  1239. row: row // 保存行引用,方便后续提交
  1240. });
  1241. }
  1242. this.$set(this.batchEditModifiedDetails[detailKey], field, {
  1243. oldValue: original[field],
  1244. newValue: row[field]
  1245. });
  1246. } else {
  1247. // 恢复原值,移除修改记录
  1248. if (this.batchEditModifiedDetails[detailKey]) {
  1249. this.$delete(this.batchEditModifiedDetails[detailKey], field);
  1250. // 检查是否还有其他字段被修改
  1251. const remainingFields = Object.keys(this.batchEditModifiedDetails[detailKey])
  1252. .filter(k => k !== 'row');
  1253. if (remainingFields.length === 0) {
  1254. this.$delete(this.batchEditModifiedDetails, detailKey);
  1255. }
  1256. }
  1257. }
  1258. },
  1259. /**
  1260. * 获取批量编辑行样式
  1261. */
  1262. getBatchEditRowClassName({ row, rowIndex }) {
  1263. const classes = [];
  1264. // 检查该行是否有Box修改
  1265. if (this.batchEditModifiedBoxes[row._boxKey]) {
  1266. classes.push('box-modified-row');
  1267. }
  1268. // 检查该行是否有明细修改
  1269. if (row._hasDetail && this.batchEditModifiedDetails[row._detailKey]) {
  1270. classes.push('detail-modified-row');
  1271. }
  1272. return classes.join(' ');
  1273. },
  1274. /**
  1275. * 保存批量编辑
  1276. */
  1277. async saveBatchEdit() {
  1278. if (this.batchEditModifiedCount === 0) {
  1279. this.$message.warning('没有需要保存的修改');
  1280. return;
  1281. }
  1282. this.batchEditSaving = true;
  1283. try {
  1284. // 构造Box修改列表
  1285. const boxList = [];
  1286. for (const boxKey of Object.keys(this.batchEditModifiedBoxes)) {
  1287. const row = this.batchEditTableData.find(r => r._boxKey === boxKey);
  1288. if (!row) continue;
  1289. boxList.push({
  1290. item_no: row.item_no,
  1291. palletRemark: row.palletRemark,
  1292. box_qty: row.box_qty,
  1293. grossWeight: row.grossWeight,
  1294. netWeight: row.netWeight
  1295. });
  1296. }
  1297. // 构造明细修改列表
  1298. const detailList = [];
  1299. for (const detailKey of Object.keys(this.batchEditModifiedDetails)) {
  1300. const detailMod = this.batchEditModifiedDetails[detailKey];
  1301. const row = detailMod.row;
  1302. if (!row) continue;
  1303. detailList.push({
  1304. seqNo: row.seqNo,
  1305. itemNo: row.itemNo,
  1306. notifyDetailItemNo: row.notifyDetailItemNo,
  1307. poNo: row.poNo,
  1308. pn: row.pn,
  1309. qty: row.qty,
  1310. rolls: row.rolls
  1311. });
  1312. }
  1313. // 调用批量修改API
  1314. const batchParams = {
  1315. site: this.currentRow.site,
  1316. buNo: this.currentRow.buNo,
  1317. delNo: this.currentRow.delNo,
  1318. updateBy: this.$store.state.user.name,
  1319. boxList: boxList,
  1320. detailList: detailList
  1321. };
  1322. const response = await batchUpdatePackingInfo(batchParams);
  1323. if (response.data && response.data.code === 0) {
  1324. this.$message.success(`成功保存 ${this.batchEditModifiedCount} 处修改`);
  1325. this.batchEditDialogVisible = false;
  1326. this.loadBoxList(); // 刷新主表格
  1327. } else {
  1328. this.$alert(response.data.msg || '保存失败', '错误', {
  1329. confirmButtonText: '确定'
  1330. });
  1331. }
  1332. } catch (error) {
  1333. console.error('批量保存失败:', error);
  1334. this.$message.error('保存失败');
  1335. } finally {
  1336. this.batchEditSaving = false;
  1337. }
  1338. }
  1339. }
  1340. }
  1341. </script>
  1342. <style scoped>
  1343. .packing-detail-tab {
  1344. width: 100%;
  1345. }
  1346. /* 展开明细容器样式 */
  1347. .expand-detail-container {
  1348. background: #fafafa;
  1349. border-radius: 6px;
  1350. overflow: visible;
  1351. box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  1352. }
  1353. /* 加载状态样式 */
  1354. .expand-loading {
  1355. text-align: center;
  1356. padding: 30px;
  1357. color: #666;
  1358. background: #fafafa;
  1359. margin: 8px 16px;
  1360. border-radius: 6px;
  1361. }
  1362. .expand-loading i {
  1363. font-size: 18px;
  1364. margin-right: 8px;
  1365. color: #409eff;
  1366. }
  1367. /* 明细表格样式 */
  1368. .expand-detail-table {
  1369. margin: 0;
  1370. border: none;
  1371. }
  1372. /deep/ .expand-detail-table .el-table__body-wrapper {
  1373. overflow-y: auto;
  1374. }
  1375. /deep/ .expand-detail-table .el-table__header {
  1376. background: #f8f9fa;
  1377. }
  1378. /deep/ .expand-detail-table .el-table__header th {
  1379. background: #eef7f0;
  1380. color: #333;
  1381. font-weight: 500;
  1382. border-bottom: 2px solid #e9ecef;
  1383. }
  1384. /deep/ .expand-detail-table .el-table__row:hover > td {
  1385. background-color: #f0f9ff;
  1386. }
  1387. /deep/ .expand-detail-table .el-table__row.el-table__row--striped {
  1388. background: #fbfcfd;
  1389. }
  1390. /deep/ .expand-detail-table .el-table__row.el-table__row--striped:hover > td {
  1391. background-color: #f0f9ff;
  1392. }
  1393. /* 空数据状态样式 */
  1394. .expand-empty-state {
  1395. text-align: center;
  1396. padding: 40px 20px;
  1397. color: #999;
  1398. }
  1399. .expand-empty-state i {
  1400. font-size: 48px;
  1401. color: #ddd;
  1402. margin-bottom: 12px;
  1403. display: block;
  1404. }
  1405. .expand-empty-state p {
  1406. font-size: 16px;
  1407. margin: 0 0 8px 0;
  1408. color: #666;
  1409. }
  1410. .expand-empty-state span {
  1411. font-size: 13px;
  1412. color: #999;
  1413. }
  1414. /* 响应式调整 */
  1415. @media (max-width: 768px) {
  1416. .expand-detail-container {
  1417. margin: 8px 8px;
  1418. padding: 6px;
  1419. }
  1420. }
  1421. /* 确保主表格容器的滚动行为 */
  1422. /deep/ .el-table__expanded-cell {
  1423. padding: 0 !important;
  1424. }
  1425. /deep/ .el-table__expand-column .cell {
  1426. padding: 0;
  1427. }
  1428. /* 确保表格滚动条正常显示 */
  1429. /deep/ .el-table .el-table__body-wrapper {
  1430. overflow-y: auto;
  1431. }
  1432. /deep/ .el-table__body-wrapper::-webkit-scrollbar {
  1433. width: 6px;
  1434. }
  1435. /deep/ .el-table__body-wrapper::-webkit-scrollbar-track {
  1436. background: #f1f1f1;
  1437. border-radius: 3px;
  1438. }
  1439. /deep/ .el-table__body-wrapper::-webkit-scrollbar-thumb {
  1440. background: #c1c1c1;
  1441. border-radius: 3px;
  1442. }
  1443. /deep/ .el-table__body-wrapper::-webkit-scrollbar-thumb:hover {
  1444. background: #a8a8a8;
  1445. }
  1446. /* 批量编辑工具栏 */
  1447. .batch-edit-toolbar {
  1448. margin-bottom: 10px;
  1449. display: flex;
  1450. justify-content: flex-end;
  1451. }
  1452. /* 批量编辑弹窗样式 */
  1453. /deep/ .batch-edit-dialog {
  1454. border-radius: 8px;
  1455. }
  1456. /deep/ .batch-edit-dialog .el-dialog__header {
  1457. background-color: #f5f7fa;
  1458. border-bottom: 1px solid #e4e7ed;
  1459. padding: 15px 20px;
  1460. border-radius: 8px 8px 0 0;
  1461. }
  1462. /deep/ .batch-edit-dialog .el-dialog__title {
  1463. font-size: 16px;
  1464. font-weight: 600;
  1465. color: #303133;
  1466. }
  1467. /deep/ .batch-edit-dialog .el-dialog__body {
  1468. padding: 15px 20px;
  1469. }
  1470. /deep/ .batch-edit-dialog .el-dialog__footer {
  1471. border-top: 1px solid #e4e7ed;
  1472. padding: 12px 20px;
  1473. }
  1474. /* 批量编辑弹窗容器 */
  1475. .batch-edit-container {
  1476. min-height: 300px;
  1477. }
  1478. /* 批量编辑提示 */
  1479. .batch-edit-tips {
  1480. margin-bottom: 15px;
  1481. }
  1482. .batch-edit-tips .modified-count {
  1483. color: #e6a23c;
  1484. margin-left: 10px;
  1485. }
  1486. .batch-edit-tips .modified-count b {
  1487. color: #f56c6c;
  1488. font-size: 16px;
  1489. }
  1490. /* 批量编辑表格样式 */
  1491. /deep/ .batch-edit-container .el-table {
  1492. border-radius: 4px;
  1493. border: 1px solid #ebeef5;
  1494. }
  1495. /* 表头样式 - 确保表头可见 */
  1496. /deep/ .batch-edit-container .el-table__header-wrapper {
  1497. background-color: #f5f7fa;
  1498. }
  1499. /deep/ .batch-edit-container .el-table th {
  1500. background-color: #f5f7fa !important;
  1501. color: #606266 !important;
  1502. font-weight: 600;
  1503. font-size: 13px;
  1504. padding: 8px 0;
  1505. }
  1506. /deep/ .batch-edit-container .el-table th .cell {
  1507. color: #606266 !important;
  1508. font-weight: 600;
  1509. }
  1510. /* Box信息单元格样式 */
  1511. .box-info-cell {
  1512. font-weight: 600;
  1513. color: #409eff;
  1514. font-size: 13px;
  1515. }
  1516. /* 修改过的输入框样式 */
  1517. /deep/ .batch-edit-container .modified-input .el-input__inner {
  1518. background-color: #fff7e6;
  1519. border-color: #e6a23c;
  1520. color: #e6a23c;
  1521. }
  1522. /* 修改过的行样式 */
  1523. /deep/ .batch-edit-container .box-modified-row td:nth-child(-n+4) {
  1524. background-color: #fdf6ec !important;
  1525. }
  1526. /deep/ .batch-edit-container .detail-modified-row td:nth-child(n+5) {
  1527. background-color: #fff7e6 !important;
  1528. }
  1529. /* 输入框样式 */
  1530. /deep/ .batch-edit-container .el-input--mini .el-input__inner {
  1531. text-align: center;
  1532. padding: 0 8px;
  1533. border-color: #dcdfe6;
  1534. }
  1535. /deep/ .batch-edit-container .el-input--mini .el-input__inner:focus {
  1536. border-color: #409eff;
  1537. }
  1538. /* 表格单元格内边距 */
  1539. /deep/ .batch-edit-container .el-table td {
  1540. padding: 6px 0;
  1541. }
  1542. /deep/ .batch-edit-container .el-table .cell {
  1543. padding: 0 8px;
  1544. }
  1545. /* 数字输入框隐藏上下箭头 */
  1546. /deep/ .batch-edit-container input[type="number"]::-webkit-inner-spin-button,
  1547. /deep/ .batch-edit-container input[type="number"]::-webkit-outer-spin-button {
  1548. -webkit-appearance: none;
  1549. margin: 0;
  1550. }
  1551. /deep/ .batch-edit-container input[type="number"] {
  1552. -moz-appearance: textfield;
  1553. }
  1554. /* 合并单元格的垂直居中 */
  1555. /deep/ .batch-edit-container .el-table__body td[rowspan] {
  1556. vertical-align: middle;
  1557. }
  1558. /deep/ .batch-edit-container .el-table__body td[rowspan] .cell {
  1559. display: flex;
  1560. align-items: center;
  1561. justify-content: center;
  1562. height: 100%;
  1563. }
  1564. /* 文字样式 */
  1565. /deep/ .batch-edit-container .el-table .cell span {
  1566. font-size: 13px;
  1567. color: #606266;
  1568. }
  1569. </style>