{{
row.transportModeLabel || row.transportMode || '-'
}}
@@ -1003,6 +1077,7 @@
:value="billingPlanForm"
:index="billingPlanIndex"
:settlement-currency="form.settlementCurrency"
+ :party-options="billingPartyOptions"
@save="saveBillingPlan"
/>
@@ -1104,8 +1179,11 @@ import {
import { Position } from '@element-plus/icons-vue';
import { getMkPublicDetail } from '@/api/mk-process';
import { getList as getProjectList } from '@/api/business/project-apply';
-import { getList as getCustomerArchiveList } from '@/api/vehicle/customer-archive';
-import { getDeptTree } from '@/api/system/dept';
+import {
+ getDetail as getCustomerArchiveDetail,
+ getList as getCustomerArchiveList,
+} from '@/api/vehicle/customer-archive';
+import { getBusinessDeptSelect, getDeptTree } from '@/api/system/dept';
import { getDictionary as getSystemDictionary } from '@/api/system/dict';
import { getDictionary as getBizDictionary } from '@/api/system/dictbiz';
import { config, option } from '@/option/business/contract-manage';
@@ -1195,10 +1273,16 @@ const defaultForm = () => ({
projectName: '',
organizationId: '',
organizationName: '',
+ platformCompanyId: '',
+ platformCompanyName: '',
signType: '',
settlementMode: '',
partyA: '',
partyB: '',
+ partyC: '',
+ partyAJson: '',
+ partyBJson: '',
+ partyCJson: '',
handlerUserName: '',
signDate: '',
applyDate: '',
@@ -1243,6 +1327,9 @@ const createCopiedDetail = (detail = {}) => {
contractFileJson: stripSpecAttachments(detail.contractFileJson),
attachmentsJson: stripSpecAttachments(detail.attachmentsJson),
billingPlanJson: detail.billingPlanJson || '',
+ partyAJson: detail.partyAJson || '',
+ partyBJson: detail.partyBJson || '',
+ partyCJson: detail.partyCJson || '',
paymentRatioJson: detail.paymentRatioJson || '',
settlementRuleJson: detail.settlementRuleJson || '',
preSettlementConfigJson: detail.preSettlementConfigJson || '',
@@ -1250,6 +1337,35 @@ const createCopiedDetail = (detail = {}) => {
changeRecordJson: '',
};
};
+const createPartyRow = (row = {}) => ({
+ id: row.id || `${Date.now()}${Math.floor(Math.random() * 1000)}`,
+ customerId: row.customerId || '',
+ customerName: row.customerName || '',
+ creditCode: row.creditCode || row.unifiedCreditCode || '',
+ contactId: row.contactId || '',
+ contactName: row.contactName || '',
+ contactPhone: row.contactPhone || '',
+ contactOptions: row.contactOptions || [],
+ contactLoading: false,
+});
+const serializePartyRows = rows =>
+ JSON.stringify(
+ (rows || []).map(row => ({
+ id: row.id,
+ customerId: row.customerId || null,
+ customerName: row.customerName || '',
+ creditCode: row.creditCode || '',
+ contactId: row.contactId || null,
+ contactName: row.contactName || '',
+ contactPhone: row.contactPhone || '',
+ }))
+ );
+const partyRoleLabel = role => {
+ if (role === 'A') return '甲方';
+ if (role === 'B') return '乙方';
+ if (role === 'C') return '丙方';
+ return role || '';
+};
const parseArray = value => {
if (Array.isArray(value)) return value;
if (!value) return [];
@@ -1350,8 +1466,7 @@ export default {
settlementCurrency: [{ required: true, message: '请选择结算币种', trigger: 'change' }],
projectName: [{ required: true, message: '请选择所属项目', trigger: 'change' }],
organizationName: [{ required: true, message: '请选择合同负责部门', trigger: 'change' }],
- partyA: [{ required: true, message: '请选择甲方', trigger: 'change' }],
- partyB: [{ required: true, message: '请选择乙方', trigger: 'change' }],
+ platformCompanyName: [{ required: true, message: '请选择平台公司', trigger: 'change' }],
signDate: [{ required: true, message: '请选择签订日期', trigger: 'change' }],
settlementMode: [{ required: true, message: '请选择结算方式', trigger: 'change' }],
contractFormat: [{ required: true, message: '请选择合同格式', trigger: 'change' }],
@@ -1394,9 +1509,11 @@ export default {
settlementModeDictOptions: [],
contractFormatDictOptions: [],
contractCategoryDictOptions: [],
- contractPartyAOptions: [],
- contractPartyBOptions: [],
- // 合规客商风险(C101-1 展示用):甲方/乙方选择后即时查询
+ contractPartyOptions: [],
+ partyARows: [createPartyRow()],
+ partyBRows: [createPartyRow()],
+ partyCRows: [],
+ // 合规客商风险(C101-1 展示用):客商选择后即时查询
contractPartyCodeMap: {},
partyRisk: {},
contractPartyLoading: false,
@@ -1405,6 +1522,9 @@ export default {
organizationFlatOptions: [],
selectedOrganizationId: '',
organizationLoading: false,
+ businessDeptOptions: [],
+ businessDeptLoading: false,
+ platformCompanyOptions: [],
contractFileRows: [],
attachmentRows: [],
listUploadContractFileRows: [],
@@ -1558,6 +1678,29 @@ export default {
? this.contractFormatDictOptions
: this.columnOptions('contractFormat');
},
+ partySections() {
+ return [
+ { role: 'A', label: '甲方', rows: this.partyARows },
+ { role: 'B', label: '乙方', rows: this.partyBRows },
+ { role: 'C', label: '丙方', rows: this.partyCRows },
+ ];
+ },
+ billingPartyOptions() {
+ return [
+ ...this.partyARows.map(row => ({ ...row, partyRole: 'A' })),
+ ...this.partyBRows.map(row => ({ ...row, partyRole: 'B' })),
+ ...this.partyCRows.map(row => ({ ...row, partyRole: 'C' })),
+ ]
+ .filter(row => row.customerName || row.customerId)
+ .map(row => ({
+ value: row.id,
+ label: `${partyRoleLabel(row.partyRole)}-${row.customerName || row.customerId}`,
+ partyRole: row.partyRole,
+ partyRowId: row.id,
+ customerId: row.customerId,
+ customerName: row.customerName,
+ }));
+ },
organizationCascaderProps() {
return {
label: 'label',
@@ -1619,14 +1762,10 @@ export default {
// 高风险/黑名单客商签约提示(C101-1,签约前提示;强制拦截在后端提交校验与推送链路)
partyRiskText() {
const parts = [];
- ['partyA', 'partyB'].forEach(prop => {
- const risk = this.partyRisk[prop];
+ Object.keys(this.partyRisk || {}).forEach(key => {
+ const risk = this.partyRisk[key];
if (!risk?.risky) return;
- // riskTip 由后端按风险项拼装(【公司名称】:黑名单;涉诉;被执行人等),缺失时兜底通用提示
- parts.push(
- risk.riskTip ||
- `${prop === 'partyA' ? '甲方' : '乙方'}[${this.form[prop] || ''}]为高风险/黑名单客商`
- );
+ parts.push(risk.riskTip || `[${risk.customerName || key}]为高风险/黑名单客商`);
});
if (!parts.length) return '';
return `${parts.join(';')},按集团规定不可直接签约,提交前须上传「特批附件」类型附件,审批通过后将自动发起合同特批`;
@@ -1969,6 +2108,8 @@ export default {
},
initFormPage() {
this.resetFormState();
+ this.loadBusinessDeptOptions();
+ this.loadProjectOptions();
if (this.formMode === 'add') {
const copyId = this.$route.query.copyId;
if (copyId) {
@@ -1991,8 +2132,12 @@ export default {
};
this.selectedProjectId = '';
this.selectedOrganizationId = '';
- this.contractPartyAOptions = [];
- this.contractPartyBOptions = [];
+ this.platformCompanyOptions = [];
+ this.contractPartyOptions = [];
+ this.partyARows = [createPartyRow()];
+ this.partyBRows = [createPartyRow()];
+ this.partyCRows = [];
+ this.partyRisk = {};
this.contractFileRows = [];
this.attachmentRows = [];
this.billingPlanRows = [];
@@ -2021,9 +2166,30 @@ export default {
detail.feeGenerationMode || (Number(detail.billingEnabled) === 0 ? 'manual' : 'system'),
};
this.selectedProjectId = projectId;
- this.selectedOrganizationId = detail.organizationId || '';
+ this.selectedOrganizationId = detail.organizationId ? String(detail.organizationId) : '';
+ this.form.platformCompanyId = detail.platformCompanyId
+ ? String(detail.platformCompanyId)
+ : '';
+ this.form.platformCompanyName = detail.platformCompanyName || '';
+ this.loadBusinessDeptOptions();
+ const syncPlatform = () => {
+ const project = this.projectOptions.find(item => String(item.id) === String(projectId));
+ if (project) {
+ this.applyProjectPlatformCompanies(project, false);
+ } else {
+ this.syncCurrentPlatformCompanyOption();
+ }
+ };
+ if (!this.projectOptions.length && projectId) {
+ this.loadProjectOptions().then(syncPlatform);
+ } else {
+ syncPlatform();
+ }
this.contractFileRows = normalizeContractFileRows(parseArray(detail.contractFileJson));
this.attachmentRows = parseArray(detail.attachmentsJson);
+ this.partyARows = this.normalizePartyRows(detail.partyAJson, detail.partyA, true);
+ this.partyBRows = this.normalizePartyRows(detail.partyBJson, detail.partyB, true);
+ this.partyCRows = this.normalizePartyRows(detail.partyCJson, detail.partyC, false);
this.billingPlanRows = parseArray(detail.billingPlanJson);
this.paymentRatioRows = parseArray(detail.paymentRatioJson);
this.changeRecordRows = parseArray(detail.changeRecordJson);
@@ -2080,8 +2246,7 @@ export default {
['contractCategory', '合同类型'],
['projectName', '所属项目'],
['organizationName', '合同负责部门'],
- ['partyA', '甲方'],
- ['partyB', '乙方'],
+ ['platformCompanyName', '平台公司'],
['contractAmount', '合同金额'],
];
const empty = required.find(([prop]) => {
@@ -2100,6 +2265,8 @@ export default {
this.$message.warning('合同金额不能小于0');
return false;
}
+ if (!this.validatePartyRows(true)) return false;
+ if (!this.validateBillingPlanBindings()) return false;
return true;
}
if (!this.form.contractName?.trim()) {
@@ -2123,8 +2290,7 @@ export default {
const required = [
['projectName', '所属项目'],
['organizationName', '合同负责部门'],
- ['partyA', '甲方'],
- ['partyB', '乙方'],
+ ['platformCompanyName', '平台公司'],
['startDate', '合同开始日期'],
['endDate', '合同结束日期'],
['signDate', '签订日期'],
@@ -2146,7 +2312,9 @@ export default {
this.$message.warning('合同金额不能小于0');
return false;
}
+ if (!this.validatePartyRows(true)) return false;
return (
+ this.validateBillingPlanBindings() &&
this.validateBillingPlanDefault() &&
this.validateSettlementRules() &&
this.validatePaymentRatios()
@@ -2156,11 +2324,29 @@ export default {
this.syncSettlementConfig();
const projectId = normalizeProjectValue(this.form.projectId);
const projectName = projectId ? normalizeProjectValue(this.form.projectName) : '';
+ const partyAJson = serializePartyRows(this.partyARows);
+ const partyBJson = serializePartyRows(this.partyBRows);
+ const partyCJson = this.partyCRows.length ? serializePartyRows(this.partyCRows) : '';
return {
...this.form,
contractNo: String(this.form.contractNo || '').trim(),
projectId,
projectName,
+ partyAJson,
+ partyBJson,
+ partyCJson,
+ partyA: this.partyARows
+ .map(row => row.customerName)
+ .filter(Boolean)
+ .join('、'),
+ partyB: this.partyBRows
+ .map(row => row.customerName)
+ .filter(Boolean)
+ .join('、'),
+ partyC: this.partyCRows
+ .map(row => row.customerName)
+ .filter(Boolean)
+ .join('、'),
copyCount: normalizeOptionalInteger(this.form.copyCount),
invoiceCycle: normalizeOptionalInteger(this.form.invoiceCycle),
paymentDays: normalizeOptionalInteger(this.form.paymentDays),
@@ -2346,12 +2532,15 @@ export default {
}
},
loadProjectOptions() {
- if (this.projectLoading) return;
+ if (this.projectLoading) {
+ return Promise.resolve(this.projectOptions);
+ }
this.projectLoading = true;
- getProjectList(1, 9999, this.config.projectQueryParams || {})
+ return getProjectList(1, 9999, this.config.projectQueryParams || {})
.then(res => {
this.projectOptions = extractRecords(res);
this.syncCurrentProjectOption();
+ return this.projectOptions;
})
.finally(() => {
this.projectLoading = false;
@@ -2413,7 +2602,15 @@ export default {
handleProjectChange(projectId) {
const project = this.projectOptions.find(item => String(item.id) === String(projectId));
if (!project) {
- Object.assign(this.form, { projectId: '', projectName: '', settlementMode: '' });
+ Object.assign(this.form, {
+ projectId: '',
+ projectName: '',
+ settlementMode: '',
+ platformCompanyId: '',
+ platformCompanyName: '',
+ });
+ this.platformCompanyOptions = [];
+ if (this.formMode === 'add') this.refreshPreviewContractNo();
return;
}
Object.assign(this.form, {
@@ -2421,6 +2618,71 @@ export default {
projectName: project.projectName || '',
settlementMode: project.settlementMode || this.form.settlementMode || '',
});
+ this.applyProjectPlatformCompanies(project, true);
+ },
+ 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);
+ },
+ applyProjectPlatformCompanies(project, autoSelect = false) {
+ const ids = this.parseUndertakeDeptIds(project?.undertakeDeptId);
+ const names = this.parseUndertakeDeptNames(project?.undertakeDeptName);
+ this.platformCompanyOptions = ids.map((id, index) => ({
+ id: String(id),
+ label: names[index] || String(id),
+ }));
+ if (!autoSelect) {
+ this.syncCurrentPlatformCompanyOption();
+ return;
+ }
+ if (this.platformCompanyOptions.length === 1) {
+ this.form.platformCompanyId = this.platformCompanyOptions[0].id;
+ this.form.platformCompanyName = this.platformCompanyOptions[0].label;
+ } else if (
+ !this.platformCompanyOptions.some(
+ item => String(item.id) === String(this.form.platformCompanyId)
+ )
+ ) {
+ this.form.platformCompanyId = '';
+ this.form.platformCompanyName = '';
+ } else {
+ this.handlePlatformCompanyChange(this.form.platformCompanyId);
+ }
+ if (this.formMode === 'add') this.refreshPreviewContractNo();
+ },
+ 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 : '';
+ if (this.formMode === 'add') this.refreshPreviewContractNo();
+ },
+ 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,
+ });
},
loadContractPartyOptions() {
if (this.contractPartyLoading) return;
@@ -2434,42 +2696,170 @@ export default {
.then(responses => {
if (requestId !== this.contractPartyRequestId) return;
const records = responses.flatMap(extractRecords);
- // 名称→客商编码映射,供 C101-1 风险查询使用(§9-U6 编码体系联调确认)
const codeMap = {};
+ const options = [];
+ const optionIds = new Set();
records.forEach(item => {
+ const id = item.id;
+ if (!id || optionIds.has(String(id))) return;
+ optionIds.add(String(id));
+ const label = String(
+ item.fullName || item.shortName || item.customerName || item.customerCode || ''
+ ).trim();
+ if (!label) return;
const code = item.customerCode;
- if (!code) return;
- [item.fullName, item.shortName, item.customerName]
- .map(name => String(name || '').trim())
- .filter(Boolean)
- .forEach(name => {
- codeMap[name] = code;
- });
+ if (code) {
+ [item.fullName, item.shortName, item.customerName, label]
+ .map(name => String(name || '').trim())
+ .filter(Boolean)
+ .forEach(name => {
+ codeMap[name] = code;
+ });
+ codeMap[String(id)] = code;
+ }
+ options.push({
+ id,
+ label,
+ value: id,
+ customerName: label,
+ creditCode: item.unifiedCreditCode || '',
+ customerCode: code || '',
+ });
});
this.contractPartyCodeMap = codeMap;
- const names = records
- .map(item => item.fullName || item.shortName || item.customerName || item.customerCode)
- .map(name => String(name || '').trim())
- .filter(Boolean);
- [this.form.partyA, this.form.partyB].filter(Boolean).forEach(name => names.unshift(name));
- const options = [...new Set(names)].map(name => ({ label: name, value: name }));
- this.contractPartyAOptions = options;
- this.contractPartyBOptions = options;
- // 复制/编辑回填的甲乙方不会触发下拉 change,加载选项后主动查一次 C101-1 风险
- ['partyA', 'partyB'].forEach(prop => {
- if (this.form[prop]) this.queryPartyRisk(prop);
+ this.contractPartyOptions = options;
+ this.syncPartyRowsWithOptions();
+ [...this.partyARows, ...this.partyBRows, ...this.partyCRows].forEach(row => {
+ if (row.customerId || row.customerName) this.queryPartyRiskByRow(row);
});
})
.finally(() => {
if (requestId === this.contractPartyRequestId) this.contractPartyLoading = false;
});
},
- // C101-1 客商风险查询(展示用,强校验在后端提交/推送链路):失败静默不阻塞填写
- queryPartyRisk(prop) {
- const name = this.form[prop];
- const code = name ? this.contractPartyCodeMap[name] : null;
+ syncPartyRowsWithOptions() {
+ const fill = rows => {
+ rows.forEach(row => {
+ if (row.customerId) {
+ const matched = this.contractPartyOptions.find(
+ item => String(item.id) === String(row.customerId)
+ );
+ if (matched) {
+ row.customerName = matched.customerName;
+ row.creditCode = matched.creditCode || row.creditCode || '';
+ return;
+ }
+ }
+ if (row.customerName) {
+ const matched = this.contractPartyOptions.find(
+ item => item.customerName === row.customerName
+ );
+ if (matched) {
+ row.customerId = matched.id;
+ row.creditCode = matched.creditCode || row.creditCode || '';
+ }
+ }
+ });
+ };
+ fill(this.partyARows);
+ fill(this.partyBRows);
+ fill(this.partyCRows);
+ },
+ normalizePartyRows(partyJson, fallbackName, requireOne) {
+ const rows = parseArray(partyJson).map(item => createPartyRow(item));
+ if (rows.length) return rows;
+ if (fallbackName) {
+ return String(fallbackName)
+ .split(/[、,]/)
+ .map(name => name.trim())
+ .filter(Boolean)
+ .map(name => createPartyRow({ customerName: name }));
+ }
+ return requireOne ? [createPartyRow()] : [];
+ },
+ partyRowsByRole(role) {
+ if (role === 'A') return this.partyARows;
+ if (role === 'B') return this.partyBRows;
+ return this.partyCRows;
+ },
+ setPartyRowsByRole(role, rows) {
+ if (role === 'A') this.partyARows = rows;
+ else if (role === 'B') this.partyBRows = rows;
+ else this.partyCRows = rows;
+ },
+ addPartyRow(role) {
+ const rows = [...this.partyRowsByRole(role), createPartyRow()];
+ this.setPartyRowsByRole(role, rows);
+ },
+ canRemovePartyRow(role) {
+ if (role === 'C') return true;
+ return this.partyRowsByRole(role).length > 1;
+ },
+ removePartyRow(role, index) {
+ if (!this.canRemovePartyRow(role)) return;
+ const rows = [...this.partyRowsByRole(role)];
+ rows.splice(index, 1);
+ this.setPartyRowsByRole(role, rows);
+ },
+ handlePartyCustomerChange(role, row, customerId) {
+ const matched = this.contractPartyOptions.find(item => String(item.id) === String(customerId));
+ row.customerId = matched?.id || '';
+ row.customerName = matched?.customerName || '';
+ row.creditCode = matched?.creditCode || '';
+ row.contactId = '';
+ row.contactName = '';
+ row.contactPhone = '';
+ row.contactOptions = [];
+ this.queryPartyRiskByRow(row);
+ if (row.customerId) this.ensurePartyContacts(row);
+ },
+ ensurePartyContacts(row) {
+ if (!row.customerId) return Promise.resolve();
+ if ((row.contactOptions || []).length) return Promise.resolve();
+ row.contactLoading = true;
+ return getCustomerArchiveDetail(row.customerId)
+ .then(res => {
+ const detail = res?.data?.data || res?.data || {};
+ const contacts = Array.isArray(detail.contacts) ? detail.contacts : [];
+ row.contactOptions = contacts
+ .map(item => ({
+ id: item.id,
+ label: item.contactName || item.name || '',
+ contactName: item.contactName || item.name || '',
+ contactPhone: item.contactPhone || item.phone || '',
+ }))
+ .filter(item => item.id && item.label);
+ if (row.contactId) {
+ const matched = row.contactOptions.find(
+ item => String(item.id) === String(row.contactId)
+ );
+ if (matched) {
+ row.contactName = matched.contactName;
+ row.contactPhone = matched.contactPhone;
+ }
+ }
+ if (!row.creditCode && detail.unifiedCreditCode) {
+ row.creditCode = detail.unifiedCreditCode;
+ }
+ })
+ .finally(() => {
+ row.contactLoading = false;
+ });
+ },
+ handlePartyContactChange(row, contactId) {
+ const matched = (row.contactOptions || []).find(item => String(item.id) === String(contactId));
+ row.contactId = matched?.id || '';
+ row.contactName = matched?.contactName || '';
+ row.contactPhone = matched?.contactPhone || '';
+ },
+ queryPartyRiskByRow(row) {
+ const key = String(row.customerId || row.customerName || '');
+ if (!key) return;
+ const code =
+ this.contractPartyCodeMap[String(row.customerId)] ||
+ this.contractPartyCodeMap[row.customerName];
if (!code) {
- this.partyRisk = { ...this.partyRisk, [prop]: null };
+ this.partyRisk = { ...this.partyRisk, [key]: null };
return;
}
getRisk(code)
@@ -2477,18 +2867,65 @@ export default {
const risk = res?.data?.data ?? res?.data ?? {};
this.partyRisk = {
...this.partyRisk,
- [prop]: {
+ [key]: {
+ customerName: row.customerName || '',
customerKind: risk.customerKind || '',
- // risky 由后端统一判定:外部客商高风险(等级=2)或黑名单;内部组织客商默认无风险恒为 false
risky: risk.risky === true,
riskTip: risk.riskTip || '',
},
};
})
.catch(() => {
- this.partyRisk = { ...this.partyRisk, [prop]: null };
+ this.partyRisk = { ...this.partyRisk, [key]: null };
});
},
+ validatePartyRows(requireCustomerId) {
+ const validate = (label, rows, required) => {
+ if (required && !rows.length) {
+ this.$message.warning(`请至少添加一个${label}客商`);
+ return false;
+ }
+ for (let index = 0; index < rows.length; index += 1) {
+ const row = rows[index];
+ if (!row.customerName) {
+ this.$message.warning(`${label}第${index + 1}行请选择客商`);
+ return false;
+ }
+ if (requireCustomerId && !row.customerId) {
+ this.$message.warning(`${label}第${index + 1}行请选择有效客商`);
+ return false;
+ }
+ if (row.contactName && !row.contactPhone) {
+ this.$message.warning(`${label}第${index + 1}行联系人需带出联系电话`);
+ return false;
+ }
+ }
+ return true;
+ };
+ return (
+ validate('甲方', this.partyARows, true) &&
+ validate('乙方', this.partyBRows, true) &&
+ validate('丙方', this.partyCRows, false)
+ );
+ },
+ validateBillingPlanBindings() {
+ if (!this.billingPlanRows.length) return true;
+ const partyIds = new Set(this.billingPartyOptions.map(item => String(item.partyRowId)));
+ for (let index = 0; index < this.billingPlanRows.length; index += 1) {
+ const plan = this.billingPlanRows[index];
+ if (!plan.partyRowId || !partyIds.has(String(plan.partyRowId))) {
+ this.$message.warning(`计费方案第${index + 1}个请选择适用客商`);
+ return false;
+ }
+ }
+ return true;
+ },
+ billingPlanCustomerLabel(row) {
+ const matched = this.billingPartyOptions.find(
+ item => String(item.partyRowId) === String(row.partyRowId)
+ );
+ return matched?.label || row.customerName || '';
+ },
loadOrganizationOptions() {
if (this.organizationLoading) return;
this.organizationLoading = true;
@@ -2558,7 +2995,10 @@ export default {
return null;
},
handleOrganizationChange(id) {
- const organization = this.findOrganization(id);
+ const organizationId = Array.isArray(id) ? id.at(-1) : id;
+ const organization =
+ this.businessDeptOptions.find(item => String(item.id) === String(organizationId)) ||
+ this.findOrganization(organizationId);
Object.assign(
this.form,
organization
@@ -2568,6 +3008,9 @@ export default {
}
: { organizationId: '', organizationName: '' }
);
+ this.selectedOrganizationId = this.form.organizationId
+ ? String(this.form.organizationId)
+ : '';
if (this.formMode === 'add') {
this.refreshPreviewContractNo();
}
@@ -2578,23 +3021,58 @@ export default {
refreshPreviewContractNo() {
if (this.formMode !== 'add') return;
const organizationId = this.form.organizationId;
- if (!organizationId) {
+ const platformCompanyId = this.form.platformCompanyId;
+ if (!organizationId || !platformCompanyId) {
if (!this.contractNoManual) this.form.contractNo = '';
return;
}
const requestId = (this.contractNoPreviewRequestId =
(this.contractNoPreviewRequestId || 0) + 1);
api
- .nextContractNo(organizationId)
+ .nextContractNo(organizationId, platformCompanyId)
.then(res => {
if (requestId !== this.contractNoPreviewRequestId || this.contractNoManual) return;
this.form.contractNo = res.data?.data || '';
})
- .catch(() => {
+ .catch(error => {
if (requestId !== this.contractNoPreviewRequestId || this.contractNoManual) return;
this.form.contractNo = '';
+ const message =
+ error?.response?.data?.msg ||
+ error?.message ||
+ '部门合同前缀未配置,无法自动生成,请手动输入合同编号';
+ this.$message.warning(message);
});
},
+ loadBusinessDeptOptions() {
+ if (this.businessDeptLoading) return;
+ this.businessDeptLoading = true;
+ getBusinessDeptSelect()
+ .then(res => {
+ this.businessDeptOptions = (res.data?.data || []).map(item => ({
+ id: String(item.id),
+ label: item.deptName || item.fullName || String(item.id),
+ rawLabel: item.deptName || item.fullName || String(item.id),
+ }));
+ 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),
+ label: this.form.organizationName,
+ rawLabel: this.form.organizationName,
+ });
+ },
syncCurrentOrganizationOption() {
if (!this.form.organizationId || !this.form.organizationName) return;
if (this.findOrganization(this.form.organizationId)) return;
@@ -3617,6 +4095,7 @@ export default {
}
&__billing-head,
+ &__party-head,
&__attachment-head {
display: flex;
align-items: center;
diff --git a/src/views/business/project-apply.vue b/src/views/business/project-apply.vue
index 19d4af2..35ed640 100644
--- a/src/views/business/project-apply.vue
+++ b/src/views/business/project-apply.vue
@@ -258,15 +258,21 @@
{{ businessDeptTooltipText }}
-
+ @change="handleBusinessDeptChange"
+ >
+
+