diff --git a/package.json b/package.json index a411807..0f0f9ac 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "pubsub-js": "^1.9.3", "qrcode": "^1.5.3", "sass-loader": "6.0.6", + "sortablejs": "^1.15.0", "svg-sprite-loader": "3.7.3", "v-viewer": "^1.6.4", "vue": "2.6.10", diff --git a/src/views/modules/part/bom_create.vue b/src/views/modules/part/bom_create.vue index 7f1f8ec..096fece 100644 --- a/src/views/modules/part/bom_create.vue +++ b/src/views/modules/part/bom_create.vue @@ -92,6 +92,7 @@
{ + this.initTableRowDrag() + }) + }, + updated() { + // 组件更新后重新初始化拖拽功能 + // 只在非拖拽状态下初始化,避免拖拽操作触发重复初始化 + if (!this.isDragging) { + this.$nextTick(() => { + this.initTableRowDrag() + }) + } + }, methods: { + /** + * 初始化表格行拖拽排序功能 + * 拖拽后自动重新计算序号并保存到数据库 + */ + initTableRowDrag() { + // 如果正在拖拽,不重新初始化 + if (this.isDragging) { + return + } + + // 销毁旧的 Sortable 实例 + if (this.sortableInstance) { + this.sortableInstance.destroy() + this.sortableInstance = null + } + + const el = document.querySelector('.rq .el-table__body-wrapper tbody') + if (!el) { + console.warn('未找到表格DOM元素,拖拽功能初始化失败') + return + } + + // 创建新的 Sortable 实例 + this.sortableInstance = Sortable.create(el, { + animation: 150, + handle: 'tr', // 整行可拖拽 + ghostClass: 'sortable-ghost', + chosenClass: 'sortable-chosen', + dragClass: 'sortable-drag', + onStart: () => { + this.isDragging = true + }, + onEnd: (evt) => { + const { oldIndex, newIndex } = evt + this.isDragging = false + + if (oldIndex === newIndex) return + + console.log(`🔄 拖拽: 从位置 ${oldIndex} (序号${this.subDetailList[oldIndex].lineSequence}) 移动到位置 ${newIndex}`) + + // 1. 更新本地数据顺序 + const movedItem = this.subDetailList.splice(oldIndex, 1)[0] + this.subDetailList.splice(newIndex, 0, movedItem) + + // 2. 重新计算所有序号(从1开始)- 使用 Vue.set 确保响应式更新 + this.subDetailList.forEach((item, index) => { + this.$set(item, 'lineSequence', index + 1) + }) + + console.log('📝 拖拽后新序号:', this.subDetailList.map((item, idx) => + `位置${idx}: ${item.componentPart} (序号${item.lineSequence})` + ).join(', ')) + + // 3. 强制刷新视图 - 使用多种方式确保更新 + this.tableKey++ // 强制刷新表格 + this.$forceUpdate() + + // 4. 使用 nextTick 确保 DOM 更新后再保存 + this.$nextTick(() => { + // 5. 保存到数据库 + this.saveBomComponentSequence() + }) + } + }) + + console.log('✅ 拖拽功能初始化成功') + }, + + /** + * 保存BOM子物料序号到数据库 + * 将整个列表的数据(包含新序号)提交到后端保存 + */ + saveBomComponentSequence() { + if (this.subDetailList.length === 0) { + return + } + + console.log('💾 === 拖拽排序保存开始 ===') + console.log('准备保存的数据:') + this.subDetailList.forEach((item, index) => { + console.log(` 位置${index}: ${item.componentPart} → 新序号 ${item.lineSequence}`) + }) + + // 构建保存数据 + const tempData = { + site: this.modalData.site, + buNo: this.modalData.buNo, + partNo: this.modalData.partNo, + partDesc: this.modalData.partDesc, + engChgLevel: this.modalData.engChgLevel, + bomType: this.modalData.bomType, + noteText: this.modalData.noteText, + effPhaseInDate: this.modalData.effPhaseInDate, + effPhaseOutDate: this.modalData.effPhaseOutDate, + engRevision: this.modalData.engRevision, + typeFlag: this.modalData.typeFlag, + netWeight: this.modalData.netWeight, + alternativeNo: this.detailData.alternativeNo, + alternativeDescription: this.detailData.alternativeDescription, + minLotQty: this.detailData.minLotQty, + defaultFlag: this.detailData.defaultFlag, + detailNoteText: this.detailData.detailNoteText, + status: this.detailData.status, + createBy: this.$store.state.user.name, + updateBy: this.$store.state.user.name, + informationList: this.subDetailList, + processUnit: this.modalData.processUnit + } + + // 显示loading + const loadingInstance = this.$loading({ + lock: true, + text: '正在保存序号排序...', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.3)' + }) + + // 调用保存API + bomManagementSave(tempData).then(({data}) => { + loadingInstance.close() + if (data && data.code === 0) { + this.$message({ + message: '序号排序保存成功', + type: 'success', + duration: 1500 + }) + // ✅ 保存成功后,保持前端已排序的数据,不使用后端返回的数据 + // 因为后端返回的数据是按旧的 line_sequence 排序的,会覆盖我们的新顺序 + console.log('✅ 序号保存成功,当前顺序:', this.subDetailList.map(item => ({ + componentPart: item.componentPart, + lineSequence: item.lineSequence + }))) + + // 如果需要更新其他字段(如计算字段),可以从后端数据中提取并合并 + if (data.rows && data.rows.subDetailList) { + const backendList = data.rows.subDetailList + // 按照前端的顺序,更新后端返回的其他字段 + this.subDetailList = this.subDetailList.map(frontItem => { + const backendItem = backendList.find(b => + b.componentPart === frontItem.componentPart && + b.lineItemNo === frontItem.lineItemNo + ) + // 保留前端的 lineSequence,更新其他字段 + return backendItem ? { + ...backendItem, + lineSequence: frontItem.lineSequence + } : frontItem + }) + } + + // 强制刷新表格显示 + this.tableKey++ + this.$forceUpdate() + } else { + this.$message.error(data.msg || '保存失败') + // 保存失败时刷新数据,恢复原序号 + this.alternativeChange() + } + }).catch((error) => { + loadingInstance.close() + console.error('保存失败:', error) + this.$message.error('保存失败,请重试') + // 保存失败时刷新数据,恢复原序号 + this.alternativeChange() + }) + }, + // 初始化组件的参数 async init (tempData) { await getBomInformationByPartNo(tempData).then(({data}) => { @@ -4066,13 +4254,13 @@ export default { // 然后只更新该行的计算字段,保留用户在弹窗中修改的其他行数据 const returnedList = data.rows.subDetailList || [] const currentRow = this.batchUpdateList[i] - + // 在返回的列表中找到对应的行 - const updatedRow = returnedList.find(item => - item.componentPart === currentRow.componentPart && + const updatedRow = returnedList.find(item => + item.componentPart === currentRow.componentPart && item.lineSequence === currentRow.lineSequence ) - + if (updatedRow) { // 只更新计算相关的字段,保留用户修改的其他字段 this.$set(this.batchUpdateList, i, { @@ -4105,7 +4293,7 @@ export default { if(flag) { // 保存成功后,将修改后的数据同步回原数据 this.subDetailList = JSON.parse(JSON.stringify(this.batchUpdateList)) - + this.$message({ message: '批量修改并计算完成', type: 'success', @@ -4277,6 +4465,32 @@ export default {