diff --git a/src/views/business/contract-manage-change.vue b/src/views/business/contract-manage-change.vue
index bc76ee6..2dae27a 100644
--- a/src/views/business/contract-manage-change.vue
+++ b/src/views/business/contract-manage-change.vue
@@ -66,11 +66,41 @@
-
-
+
+ visible && loadBusinessDeptOptions()"
+ @change="handleOrganizationChange"
+ >
+
+
-
-
+
+
+
+
import * as contractApi from '@/api/business/contract-manage';
import * as changeApi from '@/api/business/contract-change';
+import { getDetail as getProjectDetail } from '@/api/business/project-apply';
+import { getBusinessDeptSelect, getPlatformCompanySelect } from '@/api/system/dept';
import { getMkPublicDetail } from '@/api/mk-process';
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
+import { normalizeBusinessDeptOptions } from '@/utils/dept';
import BillingPlanEditor from './components/billing-plan-editor.vue';
import ContractAttachmentSection, {
isContractSealMaterialLocation,
@@ -740,6 +773,10 @@ export default {
settlementCurrencyOptions: [],
settlementModeDictOptions: [],
contractFormatDictOptions: [],
+ selectedOrganizationId: '',
+ businessDeptOptions: [],
+ businessDeptLoading: false,
+ platformCompanyOptions: [],
planDialogVisible: false,
planEditor: { planName: '', remark: '', rules: [] },
planEditorIndex: -1,
@@ -772,6 +809,34 @@ export default {
},
],
contractName: [{ required: true, message: '请输入合同名称', trigger: 'blur' }],
+ organizationName: [
+ {
+ validator: (rule, value, callback) => {
+ if (this.isSettlementRuleChange) {
+ return callback();
+ }
+ if (!String(value || '').trim()) {
+ return callback(new Error('请选择合同负责部门'));
+ }
+ callback();
+ },
+ trigger: 'change',
+ },
+ ],
+ platformCompanyName: [
+ {
+ validator: (rule, value, callback) => {
+ if (this.isSettlementRuleChange) {
+ return callback();
+ }
+ if (!String(value || '').trim()) {
+ return callback(new Error('请选择平台公司'));
+ }
+ callback();
+ },
+ trigger: 'change',
+ },
+ ],
contractAmount: [
{ required: true, message: '请输入合同金额', trigger: 'blur' },
{
@@ -924,6 +989,136 @@ export default {
formatContractAmountField() {
this.form.contractAmount = formatContractAmountInput(this.form.contractAmount);
},
+ handleOrganizationChange(id) {
+ const organizationId = Array.isArray(id) ? id.at(-1) : id;
+ const organization = this.businessDeptOptions.find(
+ item => String(item.id) === String(organizationId)
+ );
+ Object.assign(
+ this.form,
+ organization
+ ? {
+ organizationId: organization.id,
+ organizationName: organization.rawLabel || organization.label,
+ }
+ : { organizationId: '', organizationName: '' }
+ );
+ this.selectedOrganizationId = this.form.organizationId
+ ? String(this.form.organizationId)
+ : '';
+ },
+ handlePlatformCompanyChange(value) {
+ const matched = this.platformCompanyOptions.find(item => String(item.id) === String(value));
+ this.form.platformCompanyId = matched ? matched.id : '';
+ this.form.platformCompanyName = matched ? matched.label : '';
+ },
+ loadBusinessDeptOptions() {
+ if (this.businessDeptLoading) return;
+ this.businessDeptLoading = true;
+ getBusinessDeptSelect()
+ .then(res => {
+ this.businessDeptOptions = normalizeBusinessDeptOptions(res.data?.data || []);
+ this.syncCurrentBusinessDeptOption();
+ })
+ .finally(() => {
+ this.businessDeptLoading = false;
+ });
+ },
+ syncCurrentBusinessDeptOption() {
+ if (!this.form.organizationId || !this.form.organizationName) return;
+ if (
+ this.businessDeptOptions.some(item => String(item.id) === String(this.form.organizationId))
+ ) {
+ return;
+ }
+ this.businessDeptOptions.unshift({
+ id: String(this.form.organizationId),
+ value: String(this.form.organizationId),
+ label: this.form.organizationName,
+ rawLabel: this.form.organizationName,
+ });
+ },
+ parseUndertakeDeptIds(value) {
+ if (Array.isArray(value)) {
+ return value.map(item => String(item || '').trim()).filter(Boolean);
+ }
+ return String(value || '')
+ .split(',')
+ .map(item => item.trim())
+ .filter(Boolean);
+ },
+ parseUndertakeDeptNames(value) {
+ if (Array.isArray(value)) {
+ return value.map(item => String(item || '').trim()).filter(Boolean);
+ }
+ return String(value || '')
+ .split(/[、,,]/)
+ .map(item => item.trim())
+ .filter(Boolean);
+ },
+ loadProjectPlatformCompanies() {
+ const projectId = this.form.projectId;
+ if (!projectId || String(projectId) === '-1') {
+ this.syncCurrentPlatformCompanyOption();
+ return Promise.resolve([]);
+ }
+ return getProjectDetail(projectId)
+ .then(res => {
+ const project = res.data?.data || res.data || {};
+ const ids = this.parseUndertakeDeptIds(project.undertakeDeptId);
+ const names = this.parseUndertakeDeptNames(project.undertakeDeptName);
+ return this.resolveProjectPlatformCompanyOptions(ids, names);
+ })
+ .then(options => {
+ this.platformCompanyOptions = options;
+ this.syncCurrentPlatformCompanyOption();
+ return options;
+ })
+ .catch(() => {
+ this.syncCurrentPlatformCompanyOption();
+ return this.platformCompanyOptions;
+ });
+ },
+ resolveProjectPlatformCompanyOptions(ids, names) {
+ const fallback = ids.map((id, index) => ({
+ id: String(id),
+ label: names[index] || String(id),
+ }));
+ if (!ids.length) return Promise.resolve([]);
+ return getPlatformCompanySelect()
+ .then(res => {
+ const liveList = (res.data?.data || []).map(item => ({
+ id: String(item.id),
+ label: item.deptName || item.fullName || String(item.id),
+ }));
+ return ids.map((id, index) => {
+ const name = String(names[index] || '').trim();
+ const byId = liveList.find(item => String(item.id) === String(id));
+ if (byId) return byId;
+ const byName = name ? liveList.find(item => item.label === name) : null;
+ if (byName) return byName;
+ return {
+ id: String(id),
+ label: name || String(id),
+ };
+ });
+ })
+ .catch(() => fallback);
+ },
+ syncCurrentPlatformCompanyOption() {
+ if (!this.form.platformCompanyId || !this.form.platformCompanyName) return;
+ if (
+ this.platformCompanyOptions.some(
+ item => String(item.id) === String(this.form.platformCompanyId)
+ )
+ ) {
+ return;
+ }
+ this.platformCompanyOptions.unshift({
+ id: String(this.form.platformCompanyId),
+ label: this.form.platformCompanyName,
+ });
+ },
loadDictionaries() {
Promise.all([
getSystemDictionary({ code: 'currency_type' }),
@@ -972,6 +1167,10 @@ export default {
...data,
// 独立变更单保留 contractId;旧路由以合同 id 作为主键
contractId: data.contractId || (isChangeRecord ? data.contractId : data.id),
+ organizationId: data.organizationId ? String(data.organizationId) : '',
+ organizationName: data.organizationName || '',
+ platformCompanyId: data.platformCompanyId ? String(data.platformCompanyId) : '',
+ platformCompanyName: data.platformCompanyName || '',
changeContent: isChangeRecord ? data.changeContent || '' : '',
changeReason: isChangeRecord ? data.changeReason || '' : '',
changeAttachmentsJson: data.changeAttachmentsJson || '',
@@ -984,6 +1183,7 @@ export default {
archiveStatus: data.archiveStatus || '未归档',
changeType: data.changeType || '合同信息变更',
};
+ this.selectedOrganizationId = this.form.organizationId ? String(this.form.organizationId) : '';
this.changeMaterials = this.parse(data.changeAttachmentsJson);
this.period = data.startDate && data.endDate ? [data.startDate, data.endDate] : [];
this.partyARows = normalizePartyRows(data.partyAJson, data.partyA, true);
@@ -1033,6 +1233,8 @@ export default {
this.feeGenerationMode =
data.feeGenerationMode || (Number(data.billingEnabled) === 0 ? 'manual' : 'system');
this.paymentRatioRows = this.parse(data.paymentRatioJson);
+ this.loadBusinessDeptOptions();
+ this.loadProjectPlatformCompanies();
},
goBack() {
if (this.isContractChangeModule) {
@@ -1231,6 +1433,14 @@ export default {
return;
}
this.form.contractNo = contractNo;
+ if (!String(this.form.organizationName || '').trim()) {
+ this.$message.warning('请选择合同负责部门');
+ return;
+ }
+ if (!String(this.form.platformCompanyName || '').trim()) {
+ this.$message.warning('请选择平台公司');
+ return;
+ }
const total = this.paymentRatioRows.reduce((sum, row) => sum + Number(row.ratioLimit || 0), 0);
if (this.paymentRatioRows.length && Math.abs(total - 100) > 0.0001) {
this.$message.warning('付款比例上限合计必须等于100%');