diff --git a/src/components/ContractSignField.vue b/src/components/ContractSignField.vue index 8c9e5e4..46fcb56 100644 --- a/src/components/ContractSignField.vue +++ b/src/components/ContractSignField.vue @@ -353,6 +353,7 @@ export default { purchaseCategoryOptions: [], contractTypeOptions: [], purchaseMethodOptions: [], + suppressFundSourceYearWatcher: false, form: { contract_no: "", contract_no_without_prefix: "", @@ -425,13 +426,17 @@ export default { 'form.fund_source_year_id'(newVal, oldVal) { // 当预算年度变化时,自动加载项目经费来源选项 if (newVal) { - this.fetchFundSourceOptions(newVal); + if (!this.suppressFundSourceYearWatcher) { + this.fetchFundSourceOptions(newVal); + } } else { this.fundSourceOptions = []; - this.form.fund_source_budget_data_id = null; + if (!this.suppressFundSourceYearWatcher) { + this.form.fund_source_budget_data_id = null; + } } // 如果年度变化了,清空之前选择的经费来源 - if (newVal !== oldVal) { + if (newVal !== oldVal && !this.suppressFundSourceYearWatcher) { this.form.fund_source_budget_data_id = null; } }, @@ -757,6 +762,34 @@ export default { this.fundSourceOptions = []; } }, + async fetchBudgetDataDetail(budgetDataId) { + if (!budgetDataId) { + return null; + } + try { + const response = await request.get(`/api/budget/budget-data/${budgetDataId}`); + return response?.data || response || null; + } catch (e) { + console.error('获取项目经费来源详情失败', e); + return null; + } + }, + async restoreFundSourceByBudgetDataId(budgetDataId) { + if (!budgetDataId) { + return; + } + const budgetData = await this.fetchBudgetDataDetail(budgetDataId); + const yearId = budgetData?.year_id || budgetData?.budget_year?.id || budgetData?.budgetYear?.id; + if (yearId) { + this.suppressFundSourceYearWatcher = true; + this.form.fund_source_year_id = yearId; + await this.fetchFundSourceOptions(yearId); + this.form.fund_source_budget_data_id = budgetDataId; + this.$nextTick(() => { + this.suppressFundSourceYearWatcher = false; + }); + } + }, async fetchPurchaseCategoryOptions() { try { // 注意:响应拦截器已经返回了 res.data,所以 response 直接就是数据 @@ -909,7 +942,12 @@ export default { const contractExists = await this.loadExistingContract(); // 如果找不到已有合同,且提供了flowId,则从流程数据预填 if (!contractExists && this.flowId) { - this.loadFlowDataAndPrefill(); + await this.loadFlowDataAndPrefill(); + } else if (contractExists && this.flowId && this.isEditMode()) { + await this.loadFlowDataAndPrefill({ + onlyEmpty: true, + fields: this.getResignSupplementFields(), + }); } // 重新初始化校验规则(因为可能已经加载了合同数据,需要区分新建/编辑) this.initRules(); @@ -1060,13 +1098,13 @@ export default { this.form.fund_source = contractData.fund_source || ""; this.form.fund_source_year_id = contractData.fund_source_year_id || null; this.form.fund_source_budget_data_id = contractData.fund_source_budget_data_id || null; - this.form.is_government_purchase = contractData.is_government_purchase || false; + this.form.is_government_purchase = contractData.is_government_purchase ?? null; this.form.tender_agent = contractData.tender_agent || ""; // 优先使用ID字段,如果不存在则使用旧字段(兼容历史数据) this.form.purchase_method_id = contractData.purchase_method_id || null; this.form.purchase_method = contractData.purchase_method || ""; // 保留旧字段,用于兼容 this.form.perform_status = contractData.perform_status || ""; - this.form.is_accepted = contractData.is_accepted || false; + this.form.is_accepted = contractData.is_accepted ?? null; this.form.purchase_category = contractData.purchase_category || ""; this.form.purchase_category_id = contractData.purchase_category_id || null; this.form.apply_handler_id = contractData.apply_handler_id || ""; @@ -1075,8 +1113,10 @@ export default { this.form.remark = contractData.remark || ""; this.form.attachment_id = contractData.attachment_id || null; - // 如果设置了预算年度和经费来源,需要加载经费来源选项 - if (this.form.fund_source_year_id) { + // 项目经费来源优先:可从 budget_data_id 反推预算年度,预算年度只用于加载下拉选项。 + if (this.form.fund_source_budget_data_id) { + await this.restoreFundSourceByBudgetDataId(this.form.fund_source_budget_data_id); + } else if (this.form.fund_source_year_id) { await this.fetchFundSourceOptions(this.form.fund_source_year_id); } @@ -1145,7 +1185,134 @@ export default { this.loading = false; } }, - async loadFlowDataAndPrefill() { + getResignSupplementFields() { + return [ + 'fund_source', + 'fund_source_year_id', + 'fund_source_budget_data_id', + 'is_government_purchase', + 'tender_agent', + 'purchase_method_id', + 'purchase_method', + 'perform_status', + 'is_accepted', + 'purchase_category', + 'purchase_category_id', + 'handler_admin_ids', + 'apply_handler_id', + 'purchase_handler_id', + 'owner_department_id', + 'owner_department_ids', + 'remark', + 'attachment_id', + 'pay_plans', + ]; + }, + isEmptyPrefillValue(value) { + if (value === null || value === undefined || value === '') { + return true; + } + if (Array.isArray(value)) { + return value.length === 0; + } + return false; + }, + applyMappingTransform(value, map) { + if (!map || map.value_transform !== 'json_field') { + return value; + } + + const jsonField = map.json_field || 'id'; + let parsed = value; + if (typeof parsed === 'string') { + try { + parsed = JSON.parse(parsed); + } catch (e) { + return null; + } + } + + if (Array.isArray(parsed)) { + parsed = parsed[0] || null; + } + if (!parsed || typeof parsed !== 'object') { + return null; + } + + return parsed[jsonField] ?? null; + }, + getFirstJsonObject(value) { + let parsed = value; + if (typeof parsed === 'string') { + try { + parsed = JSON.parse(parsed); + } catch (e) { + return null; + } + } + if (Array.isArray(parsed)) { + parsed = parsed[0] || null; + } + return parsed && typeof parsed === 'object' ? parsed : null; + }, + resolveMappedValue(rawValue, map, budgetField) { + const transformed = this.applyMappingTransform(rawValue, map); + if (budgetField === 'attachment_id' && (transformed === rawValue || transformed === null || transformed === undefined)) { + const fileInfo = this.getFirstJsonObject(rawValue); + return fileInfo?.id ?? transformed; + } + return transformed; + }, + async setPrefillValue(field, value, onlyEmpty = false, rawValue = null) { + if (!Object.prototype.hasOwnProperty.call(this.form, field) || value === undefined || value === null || value === '') { + return; + } + if (onlyEmpty && !this.isEmptyPrefillValue(this.form[field])) { + return; + } + + if (field === 'fund_source_budget_data_id') { + await this.restoreFundSourceByBudgetDataId(value); + return; + } + + this.form[field] = value; + + if (field === 'owner_department_ids') { + const ids = typeof value === 'string' + ? value.split(',').filter(id => id) + : (Array.isArray(value) ? value : []); + this.form.owner_department_ids_array = ids.map(id => String(id)); + if (!this.form.owner_department_id && ids.length > 0) { + this.form.owner_department_id = parseInt(ids[0]); + } + } + if (field === 'handler_admin_ids') { + const ids = typeof value === 'string' + ? value.split(',').filter(id => id) + : (Array.isArray(value) ? value : []); + this.form.handler_admin_ids_array = ids.map(id => String(id)); + } + if (field === 'apply_handler_id') { + const ids = typeof value === 'string' + ? value.split(',').filter(id => id) + : (Array.isArray(value) ? value : []); + this.form.apply_handler_id_array = ids.map(id => String(id)); + } + if (field === 'attachment_id') { + const fileInfo = this.getFirstJsonObject(rawValue); + this.attachmentFileList = fileInfo ? [{ + name: fileInfo.original_name || fileInfo.name || '文件', + url: fileInfo.url || (fileInfo.folder && fileInfo.name ? `${fileInfo.folder}/${fileInfo.name}` : ''), + uid: fileInfo.id || value, + id: fileInfo.id || value + }] : []; + } + if (field === 'fund_source_year_id' && value) { + await this.fetchFundSourceOptions(value); + } + }, + async loadFlowDataAndPrefill(options = {}) { if (!this.flowId) { return; } @@ -1167,20 +1334,24 @@ export default { const flowData = flowDetail.flow.data; const contractMapping = (settings?.contract_field_mapping || []); const payplanMapping = (settings?.payplan_field_mapping || []); + const onlyEmpty = !!options.onlyEmpty; + const allowedFields = Array.isArray(options.fields) ? options.fields : null; if (contractMapping && contractMapping.length > 0) { - contractMapping.forEach((map) => { + for (const map of contractMapping) { const budgetField = map.budget_field; const oaField = map.oa_field; + if (allowedFields && !allowedFields.includes(budgetField)) { + continue; + } if (budgetField && oaField && flowData[oaField] !== undefined) { - if (this.form.hasOwnProperty(budgetField)) { - this.form[budgetField] = flowData[oaField]; - } + await this.setPrefillValue(budgetField, this.resolveMappedValue(flowData[oaField], map, budgetField), onlyEmpty, flowData[oaField]); } - }); + } } - if (settings.oa_custom_model_id_for_payplan && flowDetail.customModel) { + const shouldFillPayPlans = !allowedFields || allowedFields.includes('pay_plans'); + if (shouldFillPayPlans && (!onlyEmpty || this.isEmptyPrefillValue(this.form.pay_plans)) && settings.oa_custom_model_id_for_payplan && flowDetail.customModel) { const payplanField = flowDetail.customModel.fields.find( (f) => f.type === "relation" && f.sub_custom_model_id === settings.oa_custom_model_id_for_payplan ); @@ -1197,7 +1368,7 @@ export default { this.form.pay_plans = this.mapPayPlans(payplanRows, payplanMapping); } } - } else if (flowData.pay_plans && Array.isArray(flowData.pay_plans)) { + } else if (shouldFillPayPlans && (!onlyEmpty || this.isEmptyPrefillValue(this.form.pay_plans)) && flowData.pay_plans && Array.isArray(flowData.pay_plans)) { this.form.pay_plans = this.mapPayPlans(flowData.pay_plans, payplanMapping); } } catch (e) { @@ -1218,7 +1389,7 @@ export default { const budgetField = map.budget_field; const oaField = map.oa_field; if (budgetField && oaField && row[oaField] !== undefined) { - item[budgetField] = row[oaField]; + item[budgetField] = this.applyMappingTransform(row[oaField], map); } }); if (!item.phase_no) {