diff --git a/src/api/sampleTracking/sampleTracking.js b/src/api/sampleTracking/sampleTracking.js index 1bf6899..2d65644 100644 --- a/src/api/sampleTracking/sampleTracking.js +++ b/src/api/sampleTracking/sampleTracking.js @@ -31,3 +31,5 @@ export const queryProofTrackingProcessColumns = data => createAPI('/sampleTracki export const queryProofTrackingProcessColumnsManage = data => createAPI('/sampleTracking/proofTracking/processColumns/manageList', 'POST', data) export const saveProofTrackingProcessColumnsManage = data => createAPI('/sampleTracking/proofTracking/processColumns/manageSave', 'POST', data) export const deleteProofTrackingProcessColumnsManage = data => createAPI('/sampleTracking/proofTracking/processColumns/manageDelete', 'POST', data) +export const queryProofTrackingProcessCategories = data => createAPI('/sampleTracking/proofTracking/processCategories/list', 'POST', data) +export const saveProofTrackingProcessCategoriesManage = data => createAPI('/sampleTracking/proofTracking/processCategories/manageSave', 'POST', data) diff --git a/src/views/modules/sampleTracking/projectProofTracking.vue b/src/views/modules/sampleTracking/projectProofTracking.vue index 9549b8b..b04adea 100644 --- a/src/views/modules/sampleTracking/projectProofTracking.vue +++ b/src/views/modules/sampleTracking/projectProofTracking.vue @@ -256,17 +256,17 @@ - 拖动可调整顺序(按序号从左到右),勾选控制可见列 + 拖动可调整顺序(按序号从左到右),勾选控制可见列;工序分类按 BU 独立维护 - - + + {{ processCodeIndex + 1 }}. {{ getProcessLabelByCode(processCode) }} + + + + + + 维护分类 + + - 取消 + 关闭 保存设置 @@ -364,6 +387,50 @@ + + + 新增一行 + 删除选中 + + + + + + + + + + + 当前没有分类,请点击“新增一行”后填写并保存。 + + + 关闭 + 保存分类 + + + @@ -998,10 +1065,12 @@ import { oneKeyUpdateProofTracking, queryProofTrackingBuProcessConfig, queryProofTrackingProcessColumns, + queryProofTrackingProcessCategories, deleteProofTrackingProcessColumnsManage, queryProofTrackingProcessColumnsManage, queryProofTrackingProcessHistory, saveProofTrackingBuProcessConfig, + saveProofTrackingProcessCategoriesManage, saveProofTrackingProcessColumnsManage, searchProjectInfoTracking, searchProjectPartTracking, @@ -1055,19 +1124,19 @@ function focusCommentsNativeInput (el, vnode) { } const DEFAULT_PROCESS_COLUMNS = [ - { label: 'Raw Material', code: 'rawMaterial', planField: 'rawMaterialPlanDate', actualField: 'rawMaterialActualDate', statusField: 'rawMaterialStatus' }, - { label: 'Cylinder/Cutter', code: 'cylinderCutter', planField: 'cylinderCutterPlanDate', actualField: 'cylinderCutterActualDate', statusField: 'cylinderCutterStatus' }, - { label: 'Lamination', code: 'lamination', planField: 'laminationPlanDate', actualField: 'laminationActualDate', statusField: 'laminationStatus' }, - { label: 'Printing', code: 'printing', planField: 'printingPlanDate', actualField: 'printingActualDate', statusField: 'printingStatus' }, - { label: 'Etching', code: 'etching', planField: 'etchingPlanDate', actualField: 'etchingActualDate', statusField: 'etchingStatus' }, - { label: 'Slitting&Packing', code: 'slittingPacking', planField: 'slittingPackingPlanDate', actualField: 'slittingPackingActualDate', statusField: 'slittingPackingStatus' }, - { label: 'Bonding', code: 'bonding', planField: 'bondingPlanDate', actualField: 'bondingActualDate', statusField: 'bondingStatus' }, - { label: 'CL60/CL822', code: 'cl60Cl822', planField: 'cl60Cl822PlanDate', actualField: 'cl60Cl822ActualDate', statusField: 'cl60Cl822Status' }, - { label: 'Spotting', code: 'spotting', planField: 'spottingPlanDate', actualField: 'spottingActualDate', statusField: 'spottingStatus' }, - { label: 'Encoding', code: 'encoding', planField: 'encodingPlanDate', actualField: 'encodingActualDate', statusField: 'encodingStatus' }, - { label: 'Die cut', code: 'dieCut', planField: 'dieCutPlanDate', actualField: 'dieCutActualDate', statusField: 'dieCutStatus' }, - { label: 'Inspection', code: 'inspection', planField: 'inspectionPlanDate', actualField: 'inspectionActualDate', statusField: 'inspectionStatus' }, - { label: 'Delivery&Package', code: 'deliveryPackage', planField: 'deliveryPackagePlanDate', actualField: 'deliveryPackageActualDate', statusField: 'deliveryPackageStatus' } + { label: 'Raw Material', code: 'rawMaterial', planField: 'rawMaterialPlanDate', actualField: 'rawMaterialActualDate', statusField: 'rawMaterialStatus', processCategoryCode: '', processCategoryName: '' }, + { label: 'Cylinder/Cutter', code: 'cylinderCutter', planField: 'cylinderCutterPlanDate', actualField: 'cylinderCutterActualDate', statusField: 'cylinderCutterStatus', processCategoryCode: '', processCategoryName: '' }, + { label: 'Lamination', code: 'lamination', planField: 'laminationPlanDate', actualField: 'laminationActualDate', statusField: 'laminationStatus', processCategoryCode: '', processCategoryName: '' }, + { label: 'Printing', code: 'printing', planField: 'printingPlanDate', actualField: 'printingActualDate', statusField: 'printingStatus', processCategoryCode: '', processCategoryName: '' }, + { label: 'Etching', code: 'etching', planField: 'etchingPlanDate', actualField: 'etchingActualDate', statusField: 'etchingStatus', processCategoryCode: '', processCategoryName: '' }, + { label: 'Slitting&Packing', code: 'slittingPacking', planField: 'slittingPackingPlanDate', actualField: 'slittingPackingActualDate', statusField: 'slittingPackingStatus', processCategoryCode: '', processCategoryName: '' }, + { label: 'Bonding', code: 'bonding', planField: 'bondingPlanDate', actualField: 'bondingActualDate', statusField: 'bondingStatus', processCategoryCode: '', processCategoryName: '' }, + { label: 'CL60/CL822', code: 'cl60Cl822', planField: 'cl60Cl822PlanDate', actualField: 'cl60Cl822ActualDate', statusField: 'cl60Cl822Status', processCategoryCode: '', processCategoryName: '' }, + { label: 'Spotting', code: 'spotting', planField: 'spottingPlanDate', actualField: 'spottingActualDate', statusField: 'spottingStatus', processCategoryCode: '', processCategoryName: '' }, + { label: 'Encoding', code: 'encoding', planField: 'encodingPlanDate', actualField: 'encodingActualDate', statusField: 'encodingStatus', processCategoryCode: '', processCategoryName: '' }, + { label: 'Die cut', code: 'dieCut', planField: 'dieCutPlanDate', actualField: 'dieCutActualDate', statusField: 'dieCutStatus', processCategoryCode: '', processCategoryName: '' }, + { label: 'Inspection', code: 'inspection', planField: 'inspectionPlanDate', actualField: 'inspectionActualDate', statusField: 'inspectionStatus', processCategoryCode: '', processCategoryName: '' }, + { label: 'Delivery&Package', code: 'deliveryPackage', planField: 'deliveryPackagePlanDate', actualField: 'deliveryPackageActualDate', statusField: 'deliveryPackageStatus', processCategoryCode: '', processCategoryName: '' } ] function cloneDefaultProcessColumns () { @@ -1355,6 +1424,7 @@ export default { hasUserTrackingColumnConfig: false, processColumns: cloneDefaultProcessColumns(), processCodeLabelMap: buildProcessCodeLabelMapByRows(cloneDefaultProcessColumns()), + buProcessCategoryOptionsMap: {}, buProcessConfigDialogVisible: false, buProcessConfigRows: [], buProcessConfigMap: {}, @@ -1364,6 +1434,13 @@ export default { defaultProcessConfigDeleteLoading: false, defaultProcessConfigRowSeed: 0, defaultProcessConfigSortable: null, + processCategoryDialogVisible: false, + processCategoryDialogBuNo: '', + processCategoryDialogBuDesc: '', + processCategoryRows: [], + processCategorySelectionRows: [], + processCategorySaveLoading: false, + processCategoryRowSeed: 0, processOrderDragState: { buNo: '', fromIndex: -1 @@ -1866,6 +1943,8 @@ export default { const planField = item.planField == null ? '' : String(item.planField).trim() const actualField = item.actualField == null ? '' : String(item.actualField).trim() const statusField = item.statusField == null ? '' : String(item.statusField).trim() + const processCategoryCode = item.processCategoryCode == null ? '' : String(item.processCategoryCode).trim() + const processCategoryName = item.processCategoryName == null ? '' : String(item.processCategoryName).trim() if (!code || !label || !planField || !actualField || !statusField || usedCodeMap[code]) { return } @@ -1876,6 +1955,8 @@ export default { planField: planField, actualField: actualField, statusField: statusField, + processCategoryCode: processCategoryCode, + processCategoryName: processCategoryName, sortNo: sortNo === Number.MAX_SAFE_INTEGER ? null : sortNo }) }) @@ -1988,6 +2069,216 @@ export default { }) this.processCodeLabelMap = nextMap }, + buildProcessCategoryOptionsByRows (rows) { + const sourceRows = Array.isArray(rows) ? rows : [] + const nextOptions = [] + const usedCodeMap = {} + sourceRows.forEach((item, index) => { + const categoryCode = item && item.categoryCode != null ? String(item.categoryCode).trim() : '' + const categoryName = item && item.categoryName != null ? String(item.categoryName).trim() : '' + if (!categoryCode || !categoryName) { + return + } + const codeKey = categoryCode.toLowerCase() + if (usedCodeMap[codeKey]) { + return + } + usedCodeMap[codeKey] = true + const parsedSortNo = parseInt(item && item.sortNo, 10) + nextOptions.push({ + categoryCode: categoryCode, + categoryName: categoryName, + sortNo: Number.isNaN(parsedSortNo) ? (index + 1) * 10 : parsedSortNo + }) + }) + nextOptions.sort((a, b) => { + if (a.sortNo !== b.sortNo) { + return a.sortNo - b.sortNo + } + return String(a.categoryCode).localeCompare(String(b.categoryCode)) + }) + return nextOptions + }, + getProcessCategoryOptionsByBuNo (buNo) { + const normalizedBuNo = this.normalizeBuNo(buNo, this.$store.state.user.site) + if (!normalizedBuNo) { + return [] + } + const optionMap = this.buProcessCategoryOptionsMap || {} + return Array.isArray(optionMap[normalizedBuNo]) ? optionMap[normalizedBuNo] : [] + }, + getBuProcessCategoryNameByCode (buNo, categoryCode) { + if (!categoryCode) { + return '' + } + const normalizedCategoryCode = String(categoryCode).trim() + if (!normalizedCategoryCode) { + return '' + } + const lowerCategoryCode = normalizedCategoryCode.toLowerCase() + const configEntry = this.getBuProcessConfigEntry(buNo, this.$store.state.user.site) + const categoryNameMap = configEntry && configEntry.config && configEntry.config.processCategoryNameMap + ? configEntry.config.processCategoryNameMap + : {} + if (categoryNameMap && typeof categoryNameMap === 'object') { + if (categoryNameMap[normalizedCategoryCode]) { + return categoryNameMap[normalizedCategoryCode] + } + if (categoryNameMap[lowerCategoryCode]) { + return categoryNameMap[lowerCategoryCode] + } + const matchedNameCode = Object.keys(categoryNameMap).find(code => { + return String(code).trim().toLowerCase() === lowerCategoryCode + }) + if (matchedNameCode) { + return categoryNameMap[matchedNameCode] || '' + } + } + const options = this.getProcessCategoryOptionsByBuNo(buNo) + const matched = options.find(item => { + if (!item || !item.categoryCode) { + return false + } + return String(item.categoryCode).trim().toLowerCase() === lowerCategoryCode + }) + return matched && matched.categoryName ? matched.categoryName : '' + }, + getBuProcessCategoryCodeByProcessCode (buNo, processCode) { + const configEntry = this.getBuProcessConfigEntry(buNo, this.$store.state.user.site) + const config = configEntry ? configEntry.config : null + if (!config || !config.processCategoryMap || typeof config.processCategoryMap !== 'object' || !processCode) { + return '' + } + const sourceMap = config.processCategoryMap + const normalizedProcessCode = String(processCode).trim() + if (!normalizedProcessCode) { + return '' + } + if (sourceMap[normalizedProcessCode] != null) { + return String(sourceMap[normalizedProcessCode]).trim() + } + const lowerProcessCode = normalizedProcessCode.toLowerCase() + const matchedKey = Object.keys(sourceMap).find(code => { + return String(code).trim().toLowerCase() === lowerProcessCode + }) + return matchedKey ? String(sourceMap[matchedKey] || '').trim() : '' + }, + getBuProcessCategoryCode (row, processCode) { + if (!row || !processCode) { + return '' + } + const map = row.processCategoryMap && typeof row.processCategoryMap === 'object' + ? row.processCategoryMap + : {} + const directValue = map[processCode] + if (directValue != null) { + return String(directValue).trim() + } + const normalizedCode = String(processCode).trim().toLowerCase() + const matchedKey = Object.keys(map).find(code => String(code).trim().toLowerCase() === normalizedCode) + if (!matchedKey) { + return '' + } + const matchedValue = map[matchedKey] + return matchedValue == null ? '' : String(matchedValue).trim() + }, + normalizeProcessCategoryMapForRow (row) { + const resultMap = {} + if (!row) { + return resultMap + } + const validOrderCodes = this.getValidProcessOrderCodes(row.processOrderCodes) + validOrderCodes.forEach(code => { + const categoryCode = this.getBuProcessCategoryCode(row, code) + resultMap[code] = categoryCode || '' + }) + return resultMap + }, + handleBuProcessCategoryChange (row, processCode, categoryCode) { + if (!row || !processCode) { + return + } + const normalizedCode = String(processCode).trim() + if (!normalizedCode) { + return + } + if (!row.processCategoryMap || typeof row.processCategoryMap !== 'object') { + this.$set(row, 'processCategoryMap', {}) + } + const categoryCodeVal = categoryCode == null ? '' : String(categoryCode).trim() + this.$set(row.processCategoryMap, normalizedCode, categoryCodeVal) + }, + loadBuProcessCategoryOptionsByBuNo (buNo, silent = true) { + const normalizedBuNo = this.normalizeBuNo(buNo, this.$store.state.user.site) + if (!normalizedBuNo) { + return Promise.resolve([]) + } + const inData = { + site: this.$store.state.user.site, + buNo: normalizedBuNo, + userName: this.$store.state.user.name + } + return queryProofTrackingProcessCategories(inData).then(({ data }) => { + if (!(data && data.code === 0)) { + if (!silent) { + this.$message.error((data && data.msg) || `加载事业部【${normalizedBuNo}】工序分类失败`) + } + this.$set(this.buProcessCategoryOptionsMap, normalizedBuNo, []) + if (this.buProcessConfigMap && this.buProcessConfigMap[normalizedBuNo]) { + this.$set(this.buProcessConfigMap, normalizedBuNo, Object.assign({}, this.buProcessConfigMap[normalizedBuNo], { + processCategoryNameMap: {} + })) + } + return [] + } + const options = this.buildProcessCategoryOptionsByRows(data.rows || []) + this.$set(this.buProcessCategoryOptionsMap, normalizedBuNo, options) + if (this.buProcessConfigMap && this.buProcessConfigMap[normalizedBuNo]) { + const processCategoryNameMap = {} + options.forEach(item => { + const categoryCode = item && item.categoryCode ? String(item.categoryCode).trim() : '' + const categoryName = item && item.categoryName ? String(item.categoryName).trim() : '' + if (!categoryCode || !categoryName) { + return + } + processCategoryNameMap[categoryCode] = categoryName + processCategoryNameMap[categoryCode.toLowerCase()] = categoryName + }) + this.$set(this.buProcessConfigMap, normalizedBuNo, Object.assign({}, this.buProcessConfigMap[normalizedBuNo], { + processCategoryNameMap: processCategoryNameMap + })) + } + return options + }).catch(() => { + if (!silent) { + this.$message.error(`加载事业部【${normalizedBuNo}】工序分类异常`) + } + this.$set(this.buProcessCategoryOptionsMap, normalizedBuNo, []) + if (this.buProcessConfigMap && this.buProcessConfigMap[normalizedBuNo]) { + this.$set(this.buProcessConfigMap, normalizedBuNo, Object.assign({}, this.buProcessConfigMap[normalizedBuNo], { + processCategoryNameMap: {} + })) + } + return [] + }) + }, + loadAllBuProcessCategoryOptions (sourceRows, silent = true) { + const rows = Array.isArray(sourceRows) ? sourceRows : [] + const requestBuNos = [] + const usedBuMap = {} + rows.forEach(item => { + const buNo = this.normalizeBuNo(item && item.buNo ? item.buNo : '', this.$store.state.user.site) + if (!buNo || usedBuMap[buNo]) { + return + } + usedBuMap[buNo] = true + requestBuNos.push(buNo) + }) + if (requestBuNos.length === 0) { + return Promise.resolve([]) + } + return Promise.all(requestBuNos.map(buNo => this.loadBuProcessCategoryOptionsByBuNo(buNo, silent))) + }, getProcessLabelByCode (processCode) { if (!processCode) { return '' @@ -2113,7 +2404,7 @@ export default { } return validCodes }, - getProcessColumnWidth(item) { + getProcessColumnWidth (item) { const rows = this.dataList.filter(row => this.isProcessColumnVisibleForRow(row, item) ) @@ -2179,15 +2470,52 @@ export default { processCodes.forEach(code => { selectedCodeMap[code] = true }) + const processCategoryMap = {} + const sourceCategoryMap = item && item.processCategoryMap && typeof item.processCategoryMap === 'object' + ? item.processCategoryMap + : {} + const processCategoryNameMap = {} + const sourceCategoryNameMap = item && item.processCategoryNameMap && typeof item.processCategoryNameMap === 'object' + ? item.processCategoryNameMap + : {} + Object.keys(sourceCategoryNameMap).forEach(rawCategoryCode => { + const categoryCode = rawCategoryCode == null ? '' : String(rawCategoryCode).trim() + const categoryName = sourceCategoryNameMap[rawCategoryCode] == null + ? '' + : String(sourceCategoryNameMap[rawCategoryCode]).trim() + if (!categoryCode || !categoryName) { + return + } + processCategoryNameMap[categoryCode] = categoryName + processCategoryNameMap[categoryCode.toLowerCase()] = categoryName + }) + processOrderCodes.forEach(code => { + if (!code) { + return + } + const directVal = sourceCategoryMap[code] + if (directVal != null) { + processCategoryMap[code] = String(directVal).trim() + return + } + const lowerCode = String(code).trim().toLowerCase() + const matchedKey = Object.keys(sourceCategoryMap).find(rawCode => { + return String(rawCode).trim().toLowerCase() === lowerCode + }) + processCategoryMap[code] = matchedKey ? String(sourceCategoryMap[matchedKey] || '').trim() : '' + }) nextMap[buNo] = { showAll: showAll, processOrderCodes: processOrderCodes, - processCodes: showAll ? processOrderCodes.slice() : processOrderCodes.filter(code => selectedCodeMap[code]) + processCodes: showAll ? processOrderCodes.slice() : processOrderCodes.filter(code => selectedCodeMap[code]), + processCategoryMap: processCategoryMap, + processCategoryNameMap: processCategoryNameMap } }) this.mergeProcessCodeLabelMapByObject(processLabelMap) this.buProcessConfigMap = nextMap this.ensureBuProcessConfigRows() + this.loadAllBuProcessCategoryOptions(this.buProcessConfigRows, true) this.refreshTrackingTableLayout() }).catch(() => { this.$message.error('加载事业部进度展示配置异常') @@ -2216,7 +2544,8 @@ export default { buDesc: buDesc || key, processOrderCodes: allCodes.slice(), // 新增默认工序默认不勾选:仅预勾选系统内置工序,自定义新增项需事业部手工勾选。 - processCodes: (defaultSelectedCodes.length > 0 ? defaultSelectedCodes : allCodes).slice() + processCodes: (defaultSelectedCodes.length > 0 ? defaultSelectedCodes : allCodes).slice(), + processCategoryMap: {} } if (cfg) { const validOrderCodes = this.getValidProcessOrderCodes(cfg.processOrderCodes) @@ -2235,7 +2564,11 @@ export default { }) row.processCodes = validOrderCodes.filter(code => defaultSelectedCodeMap[code]) } + row.processCategoryMap = cfg.processCategoryMap && typeof cfg.processCategoryMap === 'object' + ? Object.assign({}, cfg.processCategoryMap) + : {} } + row.processCategoryMap = this.normalizeProcessCategoryMapForRow(row) rows.push(row) } this.userBuList.forEach(item => { @@ -2256,6 +2589,7 @@ export default { fromIndex: -1 } this.buProcessConfigDialogVisible = true + this.loadAllBuProcessCategoryOptions(this.buProcessConfigRows, false) }, buildDefaultProcessConfigRow (item) { const parsedSortNo = parseInt(item && item.sortNo, 10) @@ -2438,6 +2772,248 @@ export default { this.defaultProcessConfigDialogVisible = true this.queryDefaultProcessConfig() }, + buildProcessCategoryRow (item) { + const parsedSortNo = parseInt(item && item.sortNo, 10) + const categoryCode = item && item.categoryCode ? String(item.categoryCode).trim() : '' + return { + _rowKey: `process_category_${Date.now()}_${this.processCategoryRowSeed++}`, + _persistedCode: categoryCode, + sortNo: Number.isNaN(parsedSortNo) || parsedSortNo <= 0 ? (this.processCategoryRows.length + 1) * 10 : parsedSortNo, + categoryCode: categoryCode, + categoryName: item && item.categoryName ? String(item.categoryName).trim() : '' + } + }, + queryProcessCategoryConfig (buNo, silent = false) { + const normalizedBuNo = this.normalizeBuNo(buNo, this.$store.state.user.site) + if (!normalizedBuNo) { + return Promise.resolve([]) + } + const inData = { + site: this.$store.state.user.site, + buNo: normalizedBuNo, + userName: this.$store.state.user.name + } + return queryProofTrackingProcessCategories(inData).then(({ data }) => { + if (!(data && data.code === 0)) { + if (!silent) { + this.$message.error((data && data.msg) || '加载工序分类失败') + } + this.processCategoryRows = [] + this.processCategorySelectionRows = [] + this.$set(this.buProcessCategoryOptionsMap, normalizedBuNo, []) + if (this.buProcessConfigMap && this.buProcessConfigMap[normalizedBuNo]) { + this.$set(this.buProcessConfigMap, normalizedBuNo, Object.assign({}, this.buProcessConfigMap[normalizedBuNo], { + processCategoryNameMap: {} + })) + } + return [] + } + const rows = Array.isArray(data.rows) ? data.rows : [] + const categoryOptions = this.buildProcessCategoryOptionsByRows(rows) + this.$set(this.buProcessCategoryOptionsMap, normalizedBuNo, categoryOptions) + if (this.buProcessConfigMap && this.buProcessConfigMap[normalizedBuNo]) { + const processCategoryNameMap = {} + categoryOptions.forEach(item => { + const categoryCode = item && item.categoryCode ? String(item.categoryCode).trim() : '' + const categoryName = item && item.categoryName ? String(item.categoryName).trim() : '' + if (!categoryCode || !categoryName) { + return + } + processCategoryNameMap[categoryCode] = categoryName + processCategoryNameMap[categoryCode.toLowerCase()] = categoryName + }) + this.$set(this.buProcessConfigMap, normalizedBuNo, Object.assign({}, this.buProcessConfigMap[normalizedBuNo], { + processCategoryNameMap: processCategoryNameMap + })) + } + const sortedRows = rows + .map((item, index) => { + const parsedSortNo = parseInt(item && item.sortNo, 10) + return { + sourceIndex: index, + sortNo: Number.isNaN(parsedSortNo) ? Number.MAX_SAFE_INTEGER : parsedSortNo, + item: item + } + }) + .sort((a, b) => { + if (a.sortNo !== b.sortNo) { + return a.sortNo - b.sortNo + } + return a.sourceIndex - b.sourceIndex + }) + this.processCategoryRows = sortedRows.map(({ item }) => this.buildProcessCategoryRow(item)) + this.processCategorySelectionRows = [] + return this.processCategoryRows + }).catch(() => { + if (!silent) { + this.$message.error('加载工序分类异常') + } + this.processCategoryRows = [] + this.processCategorySelectionRows = [] + this.$set(this.buProcessCategoryOptionsMap, normalizedBuNo, []) + if (this.buProcessConfigMap && this.buProcessConfigMap[normalizedBuNo]) { + this.$set(this.buProcessConfigMap, normalizedBuNo, Object.assign({}, this.buProcessConfigMap[normalizedBuNo], { + processCategoryNameMap: {} + })) + } + return [] + }) + }, + openProcessCategoryDialog (row) { + const buNo = this.normalizeBuNo(row && row.buNo ? row.buNo : '', this.$store.state.user.site) + if (!buNo) { + this.$message.warning('请在事业部进度设置中指定事业部后再维护分类') + return + } + this.processCategoryDialogBuNo = buNo + this.processCategoryDialogBuDesc = row && row.buDesc ? row.buDesc : buNo + this.processCategoryDialogVisible = true + this.processCategorySaveLoading = false + this.queryProcessCategoryConfig(buNo) + }, + handleProcessCategoryDialogClosed () { + this.processCategoryRows = [] + this.processCategorySelectionRows = [] + this.processCategoryDialogBuNo = '' + this.processCategoryDialogBuDesc = '' + this.processCategorySaveLoading = false + }, + handleProcessCategorySelectionChange (rows) { + this.processCategorySelectionRows = Array.isArray(rows) ? rows : [] + }, + addProcessCategoryRow () { + this.processCategoryRows.push(this.buildProcessCategoryRow({ + sortNo: (this.processCategoryRows.length + 1) * 10 + })) + }, + deleteProcessCategoryRows () { + if (!Array.isArray(this.processCategorySelectionRows) || this.processCategorySelectionRows.length === 0) { + this.$message.warning('请先勾选要删除的分类') + return + } + const selectedRows = this.processCategorySelectionRows.slice() + this.$confirm(`确认删除选中的 ${selectedRows.length} 条分类吗?`, '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }).then(() => { + const selectedKeyMap = {} + selectedRows.forEach(item => { + if (item && item._rowKey) { + selectedKeyMap[item._rowKey] = true + } + }) + const nextRows = this.processCategoryRows.filter(item => !(item && selectedKeyMap[item._rowKey])) + const submitRows = this.normalizeProcessCategoryRowsForSave(nextRows) + if (!submitRows) { + return + } + const inData = { + site: this.$store.state.user.site, + buNo: this.processCategoryDialogBuNo, + updateBy: this.$store.state.user.name, + rows: submitRows + } + this.processCategorySaveLoading = true + saveProofTrackingProcessCategoriesManage(inData).then(({ data }) => { + if (!(data && data.code === 0)) { + this.$message.error((data && data.msg) || '删除工序分类失败') + return + } + this.handleProcessCategoryConfigSaved(data.msg || '删除成功') + }).catch(() => { + this.$message.error('删除工序分类异常') + }).finally(() => { + this.processCategorySaveLoading = false + }) + }).catch(() => { + // 用户取消删除时无需额外提示。 + }) + }, + normalizeProcessCategoryRowsForSave (sourceRowsInput) { + const sourceRows = Array.isArray(sourceRowsInput) + ? sourceRowsInput + : (Array.isArray(this.processCategoryRows) ? this.processCategoryRows : []) + const normalizedRows = [] + const usedNameMap = {} + for (let i = 0; i < sourceRows.length; i++) { + const row = sourceRows[i] || {} + const rowNo = i + 1 + const categoryCode = row.categoryCode == null ? '' : String(row.categoryCode).trim() + const categoryName = row.categoryName == null ? '' : String(row.categoryName).trim() + if (!categoryName) { + this.$message.warning(`第${rowNo}行的分类名称不能为空`) + return null + } + const nameKey = categoryName.toLowerCase() + if (usedNameMap[nameKey]) { + this.$message.warning(`分类名称【${categoryName}】重复,请检查`) + return null + } + usedNameMap[nameKey] = true + normalizedRows.push({ + // 旧分类携带隐藏编码回传,便于后端识别“保留/新增/删除”并做使用校验。 + categoryCode: categoryCode || null, + categoryName: categoryName, + sortNo: rowNo * 10 + }) + } + return normalizedRows + }, + handleProcessCategoryConfigSaved (successMsg) { + this.$message.success(successMsg || '工序分类已保存') + this.queryProcessCategoryConfig(this.processCategoryDialogBuNo, true).then(() => { + const buNo = this.processCategoryDialogBuNo + const validCategoryCodeMap = {} + this.getProcessCategoryOptionsByBuNo(buNo).forEach(item => { + if (item && item.categoryCode) { + validCategoryCodeMap[String(item.categoryCode).trim()] = true + } + }) + this.buProcessConfigRows.forEach(configRow => { + const rowBuNo = this.normalizeBuNo(configRow && configRow.buNo ? configRow.buNo : '', this.$store.state.user.site) + if (!rowBuNo || rowBuNo !== buNo) { + return + } + const nextCategoryMap = this.normalizeProcessCategoryMapForRow(configRow) + Object.keys(nextCategoryMap).forEach(processCode => { + const categoryCode = nextCategoryMap[processCode] + if (!categoryCode || validCategoryCodeMap[categoryCode]) { + return + } + nextCategoryMap[processCode] = '' + }) + this.$set(configRow, 'processCategoryMap', nextCategoryMap) + }) + this.queryProcessColumns().then(() => { + this.refreshTrackingTableLayout() + }) + }) + }, + saveProcessCategoryConfig () { + const submitRows = this.normalizeProcessCategoryRowsForSave() + if (!submitRows) { + return + } + const inData = { + site: this.$store.state.user.site, + buNo: this.processCategoryDialogBuNo, + updateBy: this.$store.state.user.name, + rows: submitRows + } + this.processCategorySaveLoading = true + saveProofTrackingProcessCategoriesManage(inData).then(({ data }) => { + if (!(data && data.code === 0)) { + this.$message.error((data && data.msg) || '工序分类保存失败') + return + } + this.handleProcessCategoryConfigSaved(data.msg || '工序分类已保存') + }).catch(() => { + this.$message.error('工序分类保存异常') + }).finally(() => { + this.processCategorySaveLoading = false + }) + }, addDefaultProcessConfigRow () { this.defaultProcessConfigRows.push(this.buildDefaultProcessConfigRow({ sortNo: this.getNextDefaultProcessSortNo(), @@ -2547,6 +3123,8 @@ export default { planField: fieldData.planField, actualField: fieldData.actualField, statusField: fieldData.statusField, + // 用户要求:默认进度设置不维护工序分类。 + processCategoryCode: null, sortNo: rowNo * 10, activeFlag: String(row.activeFlag || 'Y').toUpperCase() === 'N' ? 'N' : 'Y' }) @@ -2616,6 +3194,7 @@ export default { }) row.processOrderCodes = validOrderCodes row.processCodes = validOrderCodes.filter(code => selectedCodeMap[code]) + row.processCategoryMap = this.normalizeProcessCategoryMapForRow(row) }, isProcessOrderDraggingItem (row, index) { if (!row || index == null) { @@ -2694,17 +3273,33 @@ export default { this.$message.warning(`事业部【${row.buDesc || buNo}】至少需要选择一个可见进度`) return } + const processCategoryMap = this.normalizeProcessCategoryMapForRow(Object.assign({}, row, { + processOrderCodes: validOrderCodes + })) + const processCategoryNameMap = {} + this.getProcessCategoryOptionsByBuNo(buNo).forEach(item => { + const categoryCode = item && item.categoryCode ? String(item.categoryCode).trim() : '' + const categoryName = item && item.categoryName ? String(item.categoryName).trim() : '' + if (!categoryCode || !categoryName) { + return + } + processCategoryNameMap[categoryCode] = categoryName + processCategoryNameMap[categoryCode.toLowerCase()] = categoryName + }) const showAll = validCodes.length >= validOrderCodes.length nextMap[buNo] = { showAll: showAll, processOrderCodes: validOrderCodes, - processCodes: showAll ? validOrderCodes.slice() : validCodes + processCodes: showAll ? validOrderCodes.slice() : validCodes, + processCategoryMap: processCategoryMap, + processCategoryNameMap: processCategoryNameMap } submitRows.push({ buNo: buNo, showAll: showAll, processCodes: validCodes, - processOrderCodes: validOrderCodes + processOrderCodes: validOrderCodes, + processCategoryMap: processCategoryMap }) } const inData = { @@ -2719,7 +3314,6 @@ export default { } this.buProcessConfigMap = nextMap this.ensureBuProcessConfigRows() - this.buProcessConfigDialogVisible = false this.$message.success(data.msg || '事业部进度展示设置已保存') // 保存后重新拉取工序列和列表,确保页面立即按最新可见性与顺序刷新。 this.queryProcessColumns().then(() => { @@ -3090,7 +3684,9 @@ export default { label: item.label, planField: item.planField, actualField: item.actualField, - statusField: item.statusField + statusField: item.statusField, + processCategoryCode: item.processCategoryCode, + processCategoryName: item.processCategoryName })) if (exportTrackingColumns.length === 0 && exportProcessColumns.length === 0) { this.$message.warning('当前没有可导出的列') @@ -5064,15 +5660,55 @@ export default { }) return result }, - getFirstPendingProcessLabel (row) { + getTrackingStatusProcessLabel (row, processCol) { + if (!row || !processCol) { + return '' + } + const processCategoryName = processCol.processCategoryName == null + ? '' + : String(processCol.processCategoryName).trim() + if (processCategoryName) { + return processCategoryName + } + const processCategoryCode = this.getBuProcessCategoryCodeByProcessCode(row.buNo, processCol.code) + const buCategoryName = this.getBuProcessCategoryNameByCode(row.buNo, processCategoryCode) + if (buCategoryName) { + return buCategoryName + } + return processCol.label || processCol.code + }, + getPendingProcessColumnForTrackingStatus (row) { const processColumns = this.getTrackingProcessColumnsByRow(row) + let firstPendingWithDate = null + let firstPendingDate = '' + let firstPendingWithoutDate = null for (let i = 0; i < processColumns.length; i += 1) { const processCol = processColumns[i] - if (!this.isProcessComplete(row, processCol)) { - return processCol.label || processCol.code + if (this.isProcessComplete(row, processCol)) { + continue + } + const actualDate = this.formatDate(row[processCol.actualField]) + if (!actualDate) { + // 无日期的未完成工序仅作为兜底:优先展示“有日期的未完成工序”。 + if (!firstPendingWithoutDate) { + firstPendingWithoutDate = processCol + } + continue + } + // 同日期按工序顺序兜底:仅在日期更小时替换,日期相同保留先命中的工序。 + if (!firstPendingWithDate || actualDate < firstPendingDate) { + firstPendingWithDate = processCol + firstPendingDate = actualDate } } - return '已全部完成' + return firstPendingWithDate || firstPendingWithoutDate + }, + getFirstPendingProcessLabel (row) { + const processCol = this.getPendingProcessColumnForTrackingStatus(row) + if (!processCol) { + return '已全部完成' + } + return this.getTrackingStatusProcessLabel(row, processCol) }, getTrackingStatusType (row) { if (!row) { @@ -5169,7 +5805,7 @@ export default { .bu-process-config-dialog .bu-process-order-list { display: grid; - grid-template-columns: repeat(5, minmax(110px, 1fr)); + grid-template-columns: repeat(3, minmax(220px, 1fr)); column-gap: 12px; row-gap: 8px; align-items: center; @@ -5191,6 +5827,7 @@ export default { .bu-process-config-dialog .bu-process-order-item { display: flex; align-items: center; + gap: 6px; min-height: 28px; padding: 2px 6px; border: 1px solid transparent; @@ -5235,6 +5872,16 @@ export default { white-space: nowrap; } +.bu-process-config-dialog .bu-process-order-item .el-checkbox { + flex: 1; + min-width: 0; +} + +.bu-process-config-dialog .bu-process-category-select { + width: 100px; + flex-shrink: 0; +} + .bu-process-config-dialog .bu-process-config-tip { display: flex; align-items: center; @@ -5305,6 +5952,14 @@ export default { cursor: pointer; } +.process-category-config-dialog .default-process-config-actions { + margin-bottom: 8px; +} + +.process-category-config-dialog .el-table__body-wrapper tbody tr .el-input__inner { + cursor: text; +} + .default-process-sortable-ghost > td { background: #f0f9ff !important; }