From 438d2b4cf57681791e63dc506a512929cf5c5224 Mon Sep 17 00:00:00 2001 From: "han\\hanst" Date: Mon, 17 Aug 2026 15:34:45 +0800 Subject: [PATCH] =?UTF-8?q?Sample=20Tracking=E4=B8=80=E9=94=AE=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E5=8A=9F=E8=83=BD=EF=BC=8C=E9=A1=B9=E7=9B=AE=E7=BC=96?= =?UTF-8?q?=E7=A0=81=E5=8F=AF=E4=BB=A5=E6=89=8B=E5=B7=A5=E8=BE=93=E5=85=A5?= =?UTF-8?q?=EF=BC=8C=E4=B9=9F=E5=8F=AF=E4=BB=A5=E6=A0=B9=E6=8D=AEBU?= =?UTF-8?q?=E5=B0=86NPI=E9=87=8C=E7=9A=84=E9=A1=B9=E7=9B=AE=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E6=9F=A5=E5=87=BA=E6=9D=A5=EF=BC=8C=E6=8C=89=E7=85=A7?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E6=97=A5=E6=9C=9F=E5=80=92=E6=8E=92=EF=BC=8C?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E5=A5=BD=E9=A1=B9=E7=9B=AE=E5=90=8E=EF=BC=8C?= =?UTF-8?q?=E9=9C=80=E8=A6=81=E5=B0=86=E5=AF=B9=E5=BA=94=E7=9A=84=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E5=90=8D=E7=A7=B0=E3=80=81=E5=AE=A2=E6=88=B7=E3=80=81?= =?UTF-8?q?=E7=AB=8B=E9=A1=B9=E6=97=A5=E6=9C=9F=E3=80=81=E9=A2=84=E8=AE=A1?= =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=97=A5=E6=9C=9F=E3=80=81=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E5=88=86=E7=B1=BB=E3=80=81=E5=8C=BA=E5=9F=9F=E3=80=81=E4=BC=98?= =?UTF-8?q?=E5=85=88=E7=BA=A7=EF=BC=8C=E4=BB=A5=E5=8F=8A=E8=A7=92=E8=89=B2?= =?UTF-8?q?=E5=AF=B9=E5=BA=94=E7=9A=84=E8=B4=A6=E5=8F=B7=E5=85=A8=E9=83=A8?= =?UTF-8?q?=E5=B8=A6=E5=87=BA=E6=9D=A5=20=E9=80=89=E5=AE=8C=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E7=BC=96=E7=A0=81=E5=90=8E=EF=BC=8C=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E6=96=99=E5=8F=B7=E5=8F=AF=E4=BB=A5=E6=89=8B=E5=B7=A5=E8=BE=93?= =?UTF-8?q?=E5=85=A5=EF=BC=8C=E4=B9=9F=E5=8F=AF=E4=BB=A5=E6=A0=B9=E6=8D=AE?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E7=BC=96=E7=A0=81=E5=B0=86=E5=AF=B9=E5=BA=94?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E6=96=99=E5=8F=B7=E6=95=B0=E6=8D=AE=E6=9F=A5?= =?UTF-8?q?=E5=87=BA=E6=9D=A5=EF=BC=8C=E5=A6=82=E6=9E=9C=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E6=96=99=E5=8F=B7=E7=9A=84=E8=A7=92=E8=89=B2=E4=BA=BA=E5=91=98?= =?UTF-8?q?=E5=AF=B9=E6=AF=94=E9=A1=B9=E7=9B=AE=E5=B1=82=E7=BA=A7=E4=B8=8B?= =?UTF-8?q?=E7=9A=84=E8=A7=92=E8=89=B2=E4=BA=BA=E5=91=98=E6=9C=89=E5=8F=98?= =?UTF-8?q?=E5=8A=A8=EF=BC=8C=E5=88=99=E9=9C=80=E8=A6=81=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E6=96=99=E5=8F=B7=E7=9A=84=E8=A7=92=E8=89=B2?= =?UTF-8?q?=E4=BA=BA=E5=91=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sampleTracking/projectProofTracking.vue | 431 +++++++++++++++++- 1 file changed, 428 insertions(+), 3 deletions(-) diff --git a/src/views/modules/sampleTracking/projectProofTracking.vue b/src/views/modules/sampleTracking/projectProofTracking.vue index 45007de..fd275e6 100644 --- a/src/views/modules/sampleTracking/projectProofTracking.vue +++ b/src/views/modules/sampleTracking/projectProofTracking.vue @@ -362,7 +362,21 @@ - + + + @@ -443,7 +457,19 @@ - + + + @@ -947,7 +973,7 @@ import { updateProofTrackingProcess } from '@/api/sampleTracking/sampleTracking' import { searchExpApplyList } from '@/api/erf/erf' -import { getCustomerNo, saveNewCustomer } from '@/api/eam/eamProject.js' +import { eamProjectInfoSearch, eamProjectPartSearch, getCustomerNo, saveNewCustomer } from '@/api/eam/eamProject.js' import { getSiteAndBuByUserName } from '@/api/eam/eam.js' import { searchBusinessInfo, @@ -1009,6 +1035,23 @@ function cloneDefaultProcessColumns () { return DEFAULT_PROCESS_COLUMNS.map(item => Object.assign({}, item)) } +const ONE_KEY_ROLE_FIELDS = [ + 'projectManager', + 'projectOwner', + 'engineer', + 'cManufactureEngineer', + 'cQualityEngineer1', + 'cQualityEngineer2', + 'cQualityEngineer3', + 'cQualityEngineer4', + 'cQualityEngineer5', + 'cQualityEngineer6', + 'cQualityEngineer7', + 'ipqcHardTag', + 'docEngineer', + 'docEngineer2' +] + const DEFAULT_TRACKING_COLUMNS = [ { serialNumber: 'proofTrackingTable1ProjectNo', @@ -1349,6 +1392,10 @@ export default { oneKeyProofingApplyOptions: [], oneKeyProofingApplyLoading: false, oneKeyProofingApplyQueryToken: 0, + oneKeyProjectQueryToken: 0, + oneKeyPartQueryToken: 0, + oneKeyProjectNoSnapshot: '', + oneKeyProjectRoleBaseline: {}, proofDialogVisible: false, proofSaveLoading: false, @@ -3003,14 +3050,27 @@ export default { }, handleOneKeyProjectNoBlur () { const projectNo = this.normalizeProjectNo(this.oneKeyForm.projectNo) + const previousProjectNo = this.normalizeProjectNo(this.oneKeyProjectNoSnapshot) + this.oneKeyForm.projectNo = projectNo if (!projectNo) { this.oneKeyProofingApplyQueryToken += 1 this.oneKeyProofingApplyLoading = false this.oneKeyProofingApplyOptions = [] this.oneKeyForm.proofingNo = '' this.oneKeyForm.requiredDeliveryDate = '' + this.oneKeyProjectNoSnapshot = '' + this.oneKeyForm.testPartNo = '' + this.oneKeyForm.partDesc = '' + this.oneKeyForm.partType = '' return } + if (previousProjectNo.toUpperCase() !== projectNo.toUpperCase()) { + // 项目编码变化后需清理旧料号,避免跨项目保留旧料号与角色信息。 + this.oneKeyForm.testPartNo = '' + this.oneKeyForm.partDesc = '' + this.oneKeyForm.partType = '' + } + this.oneKeyProjectNoSnapshot = projectNo this.loadOneKeyProofingApplyOptions(projectNo, { keepCurrentIfMissing: false }) }, handleOneKeyProofingNoChange (proofingNo) { @@ -3162,6 +3222,351 @@ export default { const splitIdx = strVal.indexOf('-') return splitIdx > -1 ? strVal.substring(splitIdx + 1) : strVal }, + normalizeRoleValue (value) { + if (this.isBlankValue(value)) { + return '' + } + return String(value).trim() + }, + getSiteByJoinedBuNo (buNo) { + const rawBuNo = buNo == null ? '' : String(buNo).trim() + if (!rawBuNo) { + return this.$store.state.user.site + } + const splitIdx = rawBuNo.indexOf('_') + if (splitIdx > 0) { + return rawBuNo.substring(0, splitIdx) + } + return this.$store.state.user.site + }, + getOneKeyBuQueryContext () { + const currentBuNo = this.oneKeyForm && this.oneKeyForm.buNo + ? String(this.oneKeyForm.buNo).trim() + : '' + const normalizedBuNo = this.normalizeBuNo(currentBuNo, this.$store.state.user.site) + if (!normalizedBuNo) { + return null + } + const matchedBu = this.userBuList.find(item => { + if (!item || !item.buNo) { + return false + } + const itemBuNo = this.normalizeBuNo(item.buNo, this.$store.state.user.site) + return itemBuNo.toUpperCase() === normalizedBuNo.toUpperCase() + }) || null + const joinedBuNo = matchedBu && matchedBu.buNo ? matchedBu.buNo : currentBuNo + return { + normalizedBuNo: normalizedBuNo, + buDesc: matchedBu && matchedBu.buDesc ? String(matchedBu.buDesc).trim() : '', + site: this.getSiteByJoinedBuNo(joinedBuNo) + } + }, + getRowDateSortTime (row, fieldList) { + if (!row || !Array.isArray(fieldList)) { + return 0 + } + for (let i = 0; i < fieldList.length; i += 1) { + const fieldKey = fieldList[i] + const rawVal = row[fieldKey] + if (this.isBlankValue(rawVal)) { + continue + } + const date = new Date(rawVal) + if (isNaN(date.getTime())) { + continue + } + return date.getTime() + } + return 0 + }, + sortRowsByDateDesc (rows, fieldList) { + const sourceRows = Array.isArray(rows) ? rows : [] + return sourceRows.slice().sort((a, b) => { + const diff = this.getRowDateSortTime(b, fieldList) - this.getRowDateSortTime(a, fieldList) + if (diff !== 0) { + return diff + } + const leftKey = this.isBlankValue(a && a.projectNo) ? (a && a.testPartNo ? String(a.testPartNo) : '') : String(a.projectNo) + const rightKey = this.isBlankValue(b && b.projectNo) ? (b && b.testPartNo ? String(b.testPartNo) : '') : String(b.projectNo) + return leftKey.localeCompare(rightKey) + }) + }, + queryOneKeyProjectOptions (projectNoKeyword) { + const buContext = this.getOneKeyBuQueryContext() + if (!buContext) { + return Promise.resolve([]) + } + const queryKeyword = this.isBlankValue(projectNoKeyword) ? '' : String(projectNoKeyword).trim() + const inData = { + site: buContext.site, + userName: this.$store.state.user.name, + buDesc: buContext.buDesc, + projectNo: queryKeyword, + projectDesc: '', + customerNo: '', + customerDesc: '', + projectCategory: '', + status: '', + projectManager: '', + projectOwner: '', + engineer: '', + cProjectRegion: '', + startDate: '', + endDate: '', + page: 1, + limit: 300 + } + return eamProjectInfoSearch(inData).then(({ data }) => { + if (!(data && data.code === 0)) { + return [] + } + const sourceRows = (data.page && data.page.list) || [] + const targetBuNo = buContext.normalizedBuNo.toUpperCase() + const targetBuDesc = this.isBlankValue(buContext.buDesc) ? '' : String(buContext.buDesc).trim().toUpperCase() + const normalizedKeyword = queryKeyword.toUpperCase() + const filteredRows = sourceRows.filter(item => { + if (!item || this.isBlankValue(item.projectNo)) { + return false + } + const rowBuNo = this.normalizeBuNo(item.buNo || item.bu || '', buContext.site).toUpperCase() + const rowBuDesc = this.isBlankValue(item.buDesc) ? '' : String(item.buDesc).trim().toUpperCase() + const matchedBu = (targetBuNo && rowBuNo && rowBuNo === targetBuNo) || (targetBuDesc && rowBuDesc && rowBuDesc === targetBuDesc) + const hasRowBuInfo = !this.isBlankValue(item.buNo || item.bu || item.buDesc) + if (hasRowBuInfo && !matchedBu) { + return false + } + if (!normalizedKeyword) { + return true + } + return String(item.projectNo).toUpperCase().indexOf(normalizedKeyword) > -1 + }) + return this.sortRowsByDateDesc(filteredRows, ['createDate', 'projectCreationDate', 'updateDate']) + }).catch(() => { + return [] + }) + }, + queryOneKeyPartOptions (projectNo, partNoKeyword) { + const normalizedProjectNo = this.normalizeProjectNo(projectNo) + if (!normalizedProjectNo) { + return Promise.resolve([]) + } + const buContext = this.getOneKeyBuQueryContext() + const queryKeyword = this.isBlankValue(partNoKeyword) ? '' : String(partNoKeyword).trim() + const inData = { + site: buContext ? buContext.site : this.$store.state.user.site, + userName: this.$store.state.user.name, + buDesc: buContext ? buContext.buDesc : '', + projectNo: normalizedProjectNo, + projectDesc: '', + testPartNo: queryKeyword, + partDesc: '', + customerNo: '', + customerDesc: '', + projectCategory: '', + status: '', + startDate: '', + endDate: '', + projectManager: '', + projectOwner: '', + engineer: '', + cProjectRegion: '', + finalPartNo: '', + massProductionStartDate: '', + massProductionEndDate: '', + feedingFlag: '0', + page: 1, + limit: 300 + } + return eamProjectPartSearch(inData).then(({ data }) => { + if (!(data && data.code === 0)) { + return [] + } + const sourceRows = (data.page && data.page.list) || [] + const targetProjectNo = normalizedProjectNo.toUpperCase() + const normalizedKeyword = queryKeyword.toUpperCase() + const filteredRows = sourceRows.filter(item => { + const rowPartNo = item && item.testPartNo ? String(item.testPartNo).trim() : '' + const rowProjectNo = this.normalizeProjectNo(item && item.projectNo ? item.projectNo : '').toUpperCase() + if (!rowPartNo || rowProjectNo !== targetProjectNo) { + return false + } + if (!normalizedKeyword) { + return true + } + return rowPartNo.toUpperCase().indexOf(normalizedKeyword) > -1 + }) + return this.sortRowsByDateDesc(filteredRows, ['createDate', 'updateDate', 'buildDate']) + }).catch(() => { + return [] + }) + }, + queryOneKeyProjectSuggestions (queryString, callback) { + const queryToken = this.oneKeyProjectQueryToken + 1 + this.oneKeyProjectQueryToken = queryToken + this.queryOneKeyProjectOptions(queryString).then(rows => { + if (queryToken !== this.oneKeyProjectQueryToken) { + return + } + const suggestionRows = rows.map(item => { + const projectNo = this.normalizeProjectNo(item.projectNo) + if (!projectNo) { + return null + } + return { + value: projectNo, + projectDesc: item.projectDesc || item.projectName || '', + customerDesc: item.customerDesc || '', + createDateText: this.formatDate(item.createDate || item.projectCreationDate || item.updateDate), + source: item + } + }).filter(item => !!item) + callback(suggestionRows) + }).catch(() => { + if (queryToken === this.oneKeyProjectQueryToken) { + callback([]) + } + }) + }, + queryOneKeyPartSuggestions (queryString, callback) { + const projectNo = this.normalizeProjectNo(this.oneKeyForm.projectNo) + if (!projectNo) { + callback([]) + return + } + const queryToken = this.oneKeyPartQueryToken + 1 + this.oneKeyPartQueryToken = queryToken + this.queryOneKeyPartOptions(projectNo, queryString).then(rows => { + if (queryToken !== this.oneKeyPartQueryToken) { + return + } + const suggestionRows = rows.map(item => { + const partNo = item && item.testPartNo ? String(item.testPartNo).trim() : '' + if (!partNo) { + return null + } + return { + value: partNo, + partDesc: item.partDesc || '', + createDateText: this.formatDate(item.createDate || item.updateDate || item.buildDate), + source: item + } + }).filter(item => !!item) + callback(suggestionRows) + }).catch(() => { + if (queryToken === this.oneKeyPartQueryToken) { + callback([]) + } + }) + }, + setOneKeyRoleFieldValue (fieldKey, roleValue) { + const normalizedRoleValue = this.normalizeRoleValue(roleValue) + this.oneKeyForm[fieldKey] = normalizedRoleValue + const roleCfg = this.roleConfig[fieldKey] + if (roleCfg && roleCfg.nameField) { + this.oneKeyForm[roleCfg.nameField] = this.getRoleDisplayName(normalizedRoleValue) + } + }, + setOneKeyRoleFieldsFromSource (source) { + const roleSource = source || {} + ONE_KEY_ROLE_FIELDS.forEach(fieldKey => { + this.setOneKeyRoleFieldValue(fieldKey, roleSource[fieldKey]) + }) + }, + snapshotOneKeyProjectRoles (source) { + const roleSource = source || {} + const roleBaseline = {} + ONE_KEY_ROLE_FIELDS.forEach(fieldKey => { + roleBaseline[fieldKey] = this.normalizeRoleValue(roleSource[fieldKey]) + }) + this.oneKeyProjectRoleBaseline = roleBaseline + }, + syncOneKeyPartRoleFields (partRow) { + const changedRoleFields = [] + ONE_KEY_ROLE_FIELDS.forEach(fieldKey => { + const partRoleValue = this.normalizeRoleValue(partRow && partRow[fieldKey]) + if (!partRoleValue) { + return + } + const baselineRoleValue = this.normalizeRoleValue(this.oneKeyProjectRoleBaseline[fieldKey]) + if (baselineRoleValue === partRoleValue) { + return + } + const currentRoleValue = this.normalizeRoleValue(this.oneKeyForm[fieldKey]) + if (currentRoleValue === partRoleValue) { + return + } + this.setOneKeyRoleFieldValue(fieldKey, partRoleValue) + changedRoleFields.push(fieldKey) + }) + if (changedRoleFields.length > 0) { + this.$message.info('项目料号角色人员有变动,已同步角色账号') + } + }, + applyOneKeyProjectSelection (projectRow) { + const selectedProject = projectRow || {} + const projectNo = this.normalizeProjectNo(selectedProject.projectNo) + if (!projectNo) { + return + } + const previousProjectNo = this.normalizeProjectNo(this.oneKeyProjectNoSnapshot) + const projectChanged = previousProjectNo.toUpperCase() !== projectNo.toUpperCase() + this.oneKeyForm.projectNo = projectNo + this.oneKeyProjectNoSnapshot = projectNo + this.oneKeyForm.projectName = selectedProject.projectName || selectedProject.projectDesc || '' + this.oneKeyForm.projectDesc = selectedProject.projectDesc || selectedProject.projectName || '' + this.oneKeyForm.customerNo = this.isBlankValue(selectedProject.customerNo) ? '' : String(selectedProject.customerNo).trim() + this.oneKeyForm.customerDesc = selectedProject.customerDesc || '' + this.oneKeyForm.projectCreationDate = this.formatDate(selectedProject.projectCreationDate || selectedProject.createDate) + this.oneKeyForm.needDate = this.formatDate(selectedProject.needDate) + this.oneKeyForm.projectCategory = this.getProjectCategoryValue(selectedProject) + this.oneKeyForm.cProjectRegion = this.isBlankValue(selectedProject.cProjectRegion) ? '' : String(selectedProject.cProjectRegion).trim() + const rawPriority = this.isBlankValue(selectedProject.priorityLevel) ? selectedProject.priority : selectedProject.priorityLevel + this.oneKeyForm.priorityLevel = this.isBlankValue(rawPriority) ? '' : String(rawPriority).trim() + this.setOneKeyRoleFieldsFromSource(selectedProject) + this.snapshotOneKeyProjectRoles(selectedProject) + if (projectChanged) { + // 项目切换时清理料号相关字段,防止旧料号角色残留到新项目。 + this.oneKeyForm.testPartNo = '' + this.oneKeyForm.partDesc = '' + this.oneKeyForm.partType = '' + } + this.loadOneKeyProofingApplyOptions(projectNo, { keepCurrentIfMissing: false }) + }, + applyOneKeyPartSelection (partRow) { + const selectedPart = partRow || {} + const partNo = this.isBlankValue(selectedPart.testPartNo) ? '' : String(selectedPart.testPartNo).trim() + if (!partNo) { + return + } + this.oneKeyForm.testPartNo = partNo + if (!this.isBlankValue(selectedPart.partDesc)) { + this.oneKeyForm.partDesc = String(selectedPart.partDesc).trim() + } + if (!this.isBlankValue(selectedPart.partType)) { + this.oneKeyForm.partType = String(selectedPart.partType).trim() + } + // 业务要求:料号角色与项目角色存在差异时,界面需优先回填料号角色账号。 + this.syncOneKeyPartRoleFields(selectedPart) + }, + handleOneKeyProjectSelect (item) { + const selectedRow = item && item.source ? item.source : null + if (!selectedRow) { + return + } + this.applyOneKeyProjectSelection(selectedRow) + }, + handleOneKeyPartSelect (item) { + const selectedRow = item && item.source ? item.source : null + if (!selectedRow) { + return + } + this.applyOneKeyPartSelection(selectedRow) + }, + handleOneKeyPartNoBlur () { + this.oneKeyForm.testPartNo = this.isBlankValue(this.oneKeyForm.testPartNo) + ? '' + : String(this.oneKeyForm.testPartNo).trim() + }, buildOneKeySubmitPayload () { const inData = Object.assign({}, this.oneKeyForm, { createBy: this.$store.state.user.name, @@ -3318,6 +3723,10 @@ export default { this.oneKeyProofingApplyQueryToken += 1 this.oneKeyProofingApplyLoading = false this.oneKeyProofingApplyOptions = [] + this.oneKeyProjectQueryToken += 1 + this.oneKeyPartQueryToken += 1 + this.oneKeyProjectNoSnapshot = '' + this.oneKeyProjectRoleBaseline = {} this.oneKeyForm = this.getEmptyOneKeyForm() if (this.userBuList.length > 0) { this.oneKeyForm.buNo = this.userBuList[0].buNo || '' @@ -3373,9 +3782,13 @@ export default { throw new Error('未找到对应的项目物料信息,无法修改') } this.oneKeyForm = this.buildOneKeyEditForm(current, projectRows[0], partRows[0]) + this.snapshotOneKeyProjectRoles(projectRows[0]) + this.oneKeyProjectNoSnapshot = this.normalizeProjectNo(this.oneKeyForm.projectNo) this.oneKeyProofingApplyQueryToken += 1 this.oneKeyProofingApplyLoading = false this.oneKeyProofingApplyOptions = [] + this.oneKeyProjectQueryToken += 1 + this.oneKeyPartQueryToken += 1 this.oneKeyDialogVisible = true this.loadOneKeyProofingApplyOptions(this.oneKeyForm.projectNo, { keepCurrentIfMissing: true }) }).catch((e) => { @@ -4468,6 +4881,18 @@ export default { margin-bottom: 10px; } +.one-key-suggest-main { + color: #303133; + font-size: 12px; + line-height: 18px; +} + +.one-key-suggest-sub { + color: #909399; + font-size: 12px; + line-height: 16px; +} + .proof-grid-form { padding: 0 8px; }