1、调整合同
2、调整项目 3、调整付款管理
This commit is contained in:
@@ -148,6 +148,16 @@ export const config = {
|
||||
stage: ['formal'],
|
||||
permission: 'contract_manage_reject',
|
||||
},
|
||||
{
|
||||
action: 'submitFormal',
|
||||
label: '转正式合同',
|
||||
statusProp: 'approvalStatus',
|
||||
status: ['approved'],
|
||||
stage: ['temporary'],
|
||||
permission: 'contract_manage_formal',
|
||||
confirmMessage: '确定将该临时合同提交正式合同审批吗?',
|
||||
successMessage: '已提交正式合同审批',
|
||||
},
|
||||
{
|
||||
action: 'submitFormal',
|
||||
label: '重新提交转正',
|
||||
|
||||
@@ -4552,7 +4552,7 @@
|
||||
<el-cascader
|
||||
v-model="billingMatchForm.cargoTypePath"
|
||||
:options="billingCargoTypeOptions"
|
||||
:props="billingCargoTypeCascaderProps"
|
||||
:props="billingMatchCargoTypeCascaderProps"
|
||||
:placeholder="billingMatchForm.cargoType || '请选择货物类型'"
|
||||
:loading="billingCargoTypeLoading"
|
||||
:disabled="dialogReadonly"
|
||||
@@ -4564,6 +4564,32 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="货物名称">
|
||||
<el-select
|
||||
v-model="billingMatchForm.cargoNames"
|
||||
multiple
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
clearable
|
||||
filterable
|
||||
:loading="billingMatchCargoNameLoading"
|
||||
:disabled="dialogReadonly || !billingMatchForm.cargoTypePath?.length"
|
||||
:placeholder="
|
||||
billingMatchForm.cargoTypePath?.length
|
||||
? '请选择货物名称(可多选)'
|
||||
: '请先选择货物类型'
|
||||
"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in billingMatchCargoNameOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -5304,6 +5330,7 @@ const defaultBillingRule = () => ({
|
||||
cargoType: '',
|
||||
cargoTypeCode: '',
|
||||
cargoTypePath: [],
|
||||
cargoNames: [],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -5685,6 +5712,9 @@ export default {
|
||||
billingMatchRegionOptions: [],
|
||||
billingMatchRegionLoading: false,
|
||||
billingMatchRegionRequest: null,
|
||||
billingMatchCargoNameOptions: [],
|
||||
billingMatchCargoNameLoading: false,
|
||||
billingMatchCargoNameRequestId: 0,
|
||||
billingCargoTypeOptions: [],
|
||||
billingCargoTypeFlatOptions: [],
|
||||
billingCargoTypeLoading: false,
|
||||
@@ -6240,6 +6270,16 @@ export default {
|
||||
emitPath: true,
|
||||
};
|
||||
},
|
||||
billingMatchCargoTypeCascaderProps() {
|
||||
return {
|
||||
label: 'cargoName',
|
||||
value: 'id',
|
||||
children: 'children',
|
||||
leaf: 'leaf',
|
||||
checkStrictly: true,
|
||||
emitPath: true,
|
||||
};
|
||||
},
|
||||
billingCargoTypeCascaderProps() {
|
||||
return {
|
||||
label: 'cargoName',
|
||||
@@ -10476,6 +10516,33 @@ export default {
|
||||
null
|
||||
);
|
||||
},
|
||||
findBillingMatchCargoTypeByPath(path = []) {
|
||||
const values = this.normalizeBillingMatchCargoTypePath(path);
|
||||
if (!values.length) return null;
|
||||
return (
|
||||
this.billingCargoTypeFlatOptions.find(
|
||||
item =>
|
||||
item.path?.length === values.length &&
|
||||
item.path.every((value, index) => String(value) === values[index])
|
||||
) || null
|
||||
);
|
||||
},
|
||||
findBillingMatchCargoTypeByCodeOrName(condition = {}) {
|
||||
const code = String(condition.cargoTypeCode || '').trim();
|
||||
const name = String(condition.cargoType || '').trim();
|
||||
return (
|
||||
this.billingCargoTypeFlatOptions.find(item => {
|
||||
const cargoCode = String(item.cargoCode || item.code || item.id || '');
|
||||
return code && cargoCode === code;
|
||||
}) ||
|
||||
this.billingCargoTypeFlatOptions.find(item => {
|
||||
const cargoName = this.formatBillingCargoTypeLabel(item);
|
||||
const pathName = this.getBillingCargoTypePathLabels(item.path).join('/');
|
||||
return name && (cargoName === name || pathName === name);
|
||||
}) ||
|
||||
null
|
||||
);
|
||||
},
|
||||
resolveCommonCargoTypePath(row = {}) {
|
||||
const path = this.normalizeBillingCargoTypePath(row.cargoTypePath);
|
||||
if (path.length) return path;
|
||||
@@ -10490,17 +10557,17 @@ export default {
|
||||
});
|
||||
return cargoType?.path || [];
|
||||
},
|
||||
handleBillingMatchCargoTypeChange(value) {
|
||||
const path =
|
||||
Array.isArray(value) && value.length >= 2
|
||||
? value.slice(0, 2).map(item => String(item))
|
||||
: [];
|
||||
const cargoType = this.findBillingCargoTypeByPath(path);
|
||||
handleBillingMatchCargoTypeChange(value, resetCargoNames = true) {
|
||||
const path = this.normalizeBillingMatchCargoTypePath(value);
|
||||
const cargoType = this.findBillingMatchCargoTypeByPath(path);
|
||||
const labels = this.getBillingCargoTypePathLabels(path);
|
||||
this.billingMatchForm.cargoTypePath = path;
|
||||
if (resetCargoNames) this.billingMatchForm.cargoNames = [];
|
||||
if (!path.length || !cargoType) {
|
||||
this.billingMatchForm.cargoType = '';
|
||||
this.billingMatchForm.cargoTypeCode = '';
|
||||
this.billingMatchCargoNameOptions = [];
|
||||
this.billingMatchCargoNameRequestId += 1;
|
||||
return;
|
||||
}
|
||||
this.billingMatchForm.cargoType =
|
||||
@@ -10509,16 +10576,81 @@ export default {
|
||||
: labels[0] || this.formatBillingCargoTypeLabel(cargoType || {});
|
||||
this.billingMatchForm.cargoTypeCode =
|
||||
cargoType?.cargoCode || cargoType?.code || cargoType?.id || '';
|
||||
this.loadBillingMatchCargoNameOptions(path, !resetCargoNames);
|
||||
},
|
||||
loadBillingMatchCargoNameOptions(path = [], preserveSelected = false) {
|
||||
const normalizedPath = this.normalizeBillingMatchCargoTypePath(path);
|
||||
if (!normalizedPath.length) {
|
||||
this.billingMatchCargoNameOptions = [];
|
||||
this.billingMatchCargoNameLoading = false;
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
const requestId = ++this.billingMatchCargoNameRequestId;
|
||||
const selected = preserveSelected
|
||||
? this.normalizeBillingMatchCargoNames(this.billingMatchForm)
|
||||
: [];
|
||||
const firstCargoType = this.billingCargoTypeFlatOptions.find(
|
||||
item => String(item.id) === normalizedPath[0] && item.path?.length === 1
|
||||
);
|
||||
const secondCargoType = this.findBillingCargoTypeByPath(normalizedPath);
|
||||
const firstCargoTypeCode = firstCargoType?.cargoCode || firstCargoType?.code || '';
|
||||
const secondCargoTypeCode = secondCargoType?.cargoCode || secondCargoType?.code || '';
|
||||
this.billingMatchCargoNameLoading = true;
|
||||
return getCommonCargoList(1, 9999, {
|
||||
...(firstCargoTypeCode
|
||||
? { firstCargoTypeCode }
|
||||
: { firstCargoTypeId: normalizedPath[0] }),
|
||||
...(normalizedPath[1]
|
||||
? secondCargoTypeCode
|
||||
? { secondCargoTypeCode }
|
||||
: { secondCargoTypeId: normalizedPath[1] }
|
||||
: {}),
|
||||
allDept: 0,
|
||||
})
|
||||
.then(res => {
|
||||
if (requestId !== this.billingMatchCargoNameRequestId) return [];
|
||||
const names = extractRecords(res)
|
||||
.map(item => String(item.cargoName || '').trim())
|
||||
.filter(Boolean);
|
||||
const options = [...new Set([...selected, ...names])];
|
||||
this.billingMatchCargoNameOptions = options.map(value => ({ label: value, value }));
|
||||
return this.billingMatchCargoNameOptions;
|
||||
})
|
||||
.catch(error => {
|
||||
if (requestId === this.billingMatchCargoNameRequestId) {
|
||||
this.billingMatchCargoNameOptions = selected.map(value => ({ label: value, value }));
|
||||
}
|
||||
window.console.log(error);
|
||||
return [];
|
||||
})
|
||||
.finally(() => {
|
||||
if (requestId === this.billingMatchCargoNameRequestId) {
|
||||
this.billingMatchCargoNameLoading = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
normalizeBillingCargoTypePath(path) {
|
||||
return Array.isArray(path) && path.length >= 2
|
||||
? path.slice(0, 2).map(value => String(value))
|
||||
: [];
|
||||
},
|
||||
normalizeBillingMatchCargoTypePath(path) {
|
||||
return Array.isArray(path) && path.length
|
||||
? path.slice(0, 2).map(value => String(value))
|
||||
: [];
|
||||
},
|
||||
normalizeBillingMatchCargoNames(condition = {}) {
|
||||
const values = Array.isArray(condition.cargoNames)
|
||||
? condition.cargoNames
|
||||
: condition.cargoName
|
||||
? [condition.cargoName]
|
||||
: [];
|
||||
return [...new Set(values.map(value => String(value || '').trim()).filter(Boolean))];
|
||||
},
|
||||
resolveBillingMatchCargoTypePath(condition = {}) {
|
||||
const path = this.normalizeBillingCargoTypePath(condition.cargoTypePath);
|
||||
const path = this.normalizeBillingMatchCargoTypePath(condition.cargoTypePath);
|
||||
if (path.length) return path;
|
||||
const cargoType = this.findBillingCargoTypeByCodeOrName(condition);
|
||||
const cargoType = this.findBillingMatchCargoTypeByCodeOrName(condition);
|
||||
return cargoType?.path || [];
|
||||
},
|
||||
normalizeBillingMatchRegionPath(path) {
|
||||
@@ -10536,7 +10668,10 @@ export default {
|
||||
nextCondition.destinationPath = this.normalizeBillingMatchRegionPath(
|
||||
condition?.destinationPath
|
||||
);
|
||||
nextCondition.cargoTypePath = this.normalizeBillingCargoTypePath(condition?.cargoTypePath);
|
||||
nextCondition.cargoTypePath = this.normalizeBillingMatchCargoTypePath(
|
||||
condition?.cargoTypePath
|
||||
);
|
||||
nextCondition.cargoNames = this.normalizeBillingMatchCargoNames(condition);
|
||||
return nextCondition;
|
||||
},
|
||||
ensureBillingFeeCategoryOptions() {
|
||||
@@ -10892,12 +11027,13 @@ export default {
|
||||
openBillingMatch(row, index) {
|
||||
this.billingMatchRuleIndex = index;
|
||||
this.billingMatchForm = this.normalizeBillingMatchCondition(row.matchCondition);
|
||||
this.billingMatchCargoNameOptions = [];
|
||||
this.billingMatchBox = true;
|
||||
this.ensureBillingMatchRegionOptions();
|
||||
this.ensureBillingCargoTypeOptions().then(() => {
|
||||
const path = this.resolveBillingMatchCargoTypePath(this.billingMatchForm);
|
||||
if (path.length) {
|
||||
this.handleBillingMatchCargoTypeChange(path);
|
||||
this.handleBillingMatchCargoTypeChange(path, false);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -10906,7 +11042,7 @@ export default {
|
||||
if (row) {
|
||||
this.handleBillingMatchRegionChange('origin', this.billingMatchForm.originPath);
|
||||
this.handleBillingMatchRegionChange('destination', this.billingMatchForm.destinationPath);
|
||||
this.handleBillingMatchCargoTypeChange(this.billingMatchForm.cargoTypePath);
|
||||
this.handleBillingMatchCargoTypeChange(this.billingMatchForm.cargoTypePath, false);
|
||||
row.matchCondition = this.cloneData(this.billingMatchForm);
|
||||
}
|
||||
this.billingMatchBox = false;
|
||||
|
||||
@@ -103,6 +103,11 @@ const viewerPlugins = [
|
||||
textPlugin(),
|
||||
fallbackPlugin(),
|
||||
];
|
||||
const normalizeOptionalInteger = value => {
|
||||
if (value === undefined || value === null || value === '' || Number(value) < 0) return null;
|
||||
const number = Number(value);
|
||||
return Number.isFinite(number) ? Math.trunc(number) : null;
|
||||
};
|
||||
|
||||
export default {
|
||||
components: { BillingPlanEditor, ElImageViewer, OpenFileViewer },
|
||||
@@ -111,7 +116,7 @@ export default {
|
||||
mounted() { this.load(); },
|
||||
watch: { settlementConfigTab(tab, oldTab) { if (tab === oldTab) return; if (oldTab === 'pre') this.preSettlementConfig = { ...this.settlementRule }; else this.formalSettlementConfig = { ...this.settlementRule }; this.settlementRule = { ...(tab === 'pre' ? this.preSettlementConfig : this.formalSettlementConfig) }; } },
|
||||
methods: {
|
||||
async load() { const id = this.$route.query.id; if (!id) return; const res = await api.getDetail(id); const data = res.data?.data || res.data || {}; this.form = { ...data, changeType: '合同信息变更' }; this.period = data.startDate && data.endDate ? [data.startDate, data.endDate] : []; this.plans = this.parse(data.billingPlanJson); this.attachments = this.parse(data.attachmentsJson); this.contractFileRows = this.parse(data.contractFileJson); const rules = this.parseObject(data.settlementRuleJson); const pre = this.parseObject(data.preSettlementConfigJson); const formal = this.parseObject(data.formalSettlementConfigJson); const legacy = Object.keys(rules).some(key => !['preSettlementConfig', 'formalSettlementConfig'].includes(key)) ? rules : {}; this.preSettlementConfig = rules.preSettlementConfig || (Object.keys(pre).length ? pre : legacy); this.formalSettlementConfig = rules.formalSettlementConfig || (Object.keys(formal).length ? formal : legacy); this.settlementRule = { ...this.preSettlementConfig }; this.feeGenerationMode = data.feeGenerationMode || (Number(data.billingEnabled) === 0 ? 'manual' : 'system'); this.paymentRatioRows = this.parse(data.paymentRatioJson); },
|
||||
async load() { const id = this.$route.query.id; if (!id) return; const res = await api.getDetail(id); const data = res.data?.data || res.data || {}; this.form = { ...data, copyCount: normalizeOptionalInteger(data.copyCount), paymentDays: normalizeOptionalInteger(data.paymentDays), changeType: '合同信息变更' }; this.period = data.startDate && data.endDate ? [data.startDate, data.endDate] : []; this.plans = this.parse(data.billingPlanJson); this.attachments = this.parse(data.attachmentsJson); this.contractFileRows = this.parse(data.contractFileJson); const rules = this.parseObject(data.settlementRuleJson); const pre = this.parseObject(data.preSettlementConfigJson); const formal = this.parseObject(data.formalSettlementConfigJson); const legacy = Object.keys(rules).some(key => !['preSettlementConfig', 'formalSettlementConfig'].includes(key)) ? rules : {}; this.preSettlementConfig = rules.preSettlementConfig || (Object.keys(pre).length ? pre : legacy); this.formalSettlementConfig = rules.formalSettlementConfig || (Object.keys(formal).length ? formal : legacy); this.settlementRule = { ...this.preSettlementConfig }; this.feeGenerationMode = data.feeGenerationMode || (Number(data.billingEnabled) === 0 ? 'manual' : 'system'); this.paymentRatioRows = this.parse(data.paymentRatioJson); },
|
||||
parse(value) { try { const result = JSON.parse(value || '[]'); return Array.isArray(result) ? result : []; } catch { return []; } },
|
||||
parseObject(value) { try { return { autoGenerate: 1, settlementType: '月结', billCycleType: '固定截单日', billCutoffDay: 25, cycleDays: 15, ...(JSON.parse(value || '{}') || {}) }; } catch { return { autoGenerate: 1, settlementType: '月结', billCycleType: '固定截单日', billCutoffDay: 25, cycleDays: 15 }; } },
|
||||
addPlan() { this.planEditorIndex = -1; this.planEditor = { planName: `计费方案${this.plans.length + 1}`, defaultPlan: !this.plans.length, remark: '', rules: [{}] }; this.planDialogVisible = true; },
|
||||
@@ -134,7 +139,7 @@ export default {
|
||||
formatFileSize(value) { const size = Number(value || 0); return size > 1024 * 1024 ? `${(size / 1024 / 1024).toFixed(2)}MB` : `${Math.max(1, Math.ceil(size / 1024))}KB`; },
|
||||
handleChangeMaterial(event) { if (event.raw) this.changeMaterials.push(event.raw); },
|
||||
addPaymentRatioRow() { this.paymentRatioRows.push({ paymentTerm: `第${this.paymentRatioRows.length + 1}笔`, ratioLimit: '', remark: '' }); },
|
||||
async submit() { await this.$refs.formRef.validate(); 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%'); return; } const settlementRule = { preSettlementConfig: this.preSettlementConfig, formalSettlementConfig: this.formalSettlementConfig }; await api.submitChange({ ...this.form, startDate: this.period[0], endDate: this.period[1], feeGenerationMode: this.feeGenerationMode, billingEnabled: this.feeGenerationMode === 'system' ? 1 : 0, billingPlanJson: JSON.stringify(this.plans), settlementRuleJson: JSON.stringify(settlementRule), preSettlementConfigJson: JSON.stringify(this.preSettlementConfig), formalSettlementConfigJson: JSON.stringify(this.formalSettlementConfig), paymentRatioJson: JSON.stringify(this.paymentRatioRows), contractFileJson: JSON.stringify(this.contractFileRows), attachmentsJson: JSON.stringify(this.attachments), changeContent: this.form.changeReason, changeReason: this.form.changeReason }); this.$message.success('变更已提交'); this.$router.back(); },
|
||||
async submit() { await this.$refs.formRef.validate(); 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%'); return; } const settlementRule = { preSettlementConfig: this.preSettlementConfig, formalSettlementConfig: this.formalSettlementConfig }; await api.submitChange({ ...this.form, copyCount: normalizeOptionalInteger(this.form.copyCount), paymentDays: normalizeOptionalInteger(this.form.paymentDays), startDate: this.period[0], endDate: this.period[1], feeGenerationMode: this.feeGenerationMode, billingEnabled: this.feeGenerationMode === 'system' ? 1 : 0, billingPlanJson: JSON.stringify(this.plans), settlementRuleJson: JSON.stringify(settlementRule), preSettlementConfigJson: JSON.stringify(this.preSettlementConfig), formalSettlementConfigJson: JSON.stringify(this.formalSettlementConfig), paymentRatioJson: JSON.stringify(this.paymentRatioRows), contractFileJson: JSON.stringify(this.contractFileRows), attachmentsJson: JSON.stringify(this.attachments), changeContent: this.form.changeReason, changeReason: this.form.changeReason }); this.$message.success('变更已提交'); this.$router.back(); },
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -804,6 +804,13 @@ import '@open-file-viewer/core/style.css';
|
||||
import pdfWorkerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url';
|
||||
|
||||
const clone = value => JSON.parse(JSON.stringify(value));
|
||||
const normalizeOptionalInteger = (value, emptyValue = null) => {
|
||||
if (value === undefined || value === null || value === '' || Number(value) < 0) {
|
||||
return emptyValue;
|
||||
}
|
||||
const number = Number(value);
|
||||
return Number.isFinite(number) ? Math.trunc(number) : emptyValue;
|
||||
};
|
||||
const defaultSettlementRule = () => ({
|
||||
autoGenerate: 1,
|
||||
billStartDate: '',
|
||||
@@ -1480,6 +1487,8 @@ export default {
|
||||
this.form = {
|
||||
...defaultForm(),
|
||||
...detail,
|
||||
copyCount: normalizeOptionalInteger(detail.copyCount, ''),
|
||||
paymentDays: normalizeOptionalInteger(detail.paymentDays, ''),
|
||||
feeGenerationMode:
|
||||
detail.feeGenerationMode || (Number(detail.billingEnabled) === 0 ? 'manual' : 'system'),
|
||||
};
|
||||
@@ -1554,6 +1563,8 @@ export default {
|
||||
this.syncSettlementConfig();
|
||||
return {
|
||||
...this.form,
|
||||
copyCount: normalizeOptionalInteger(this.form.copyCount),
|
||||
paymentDays: normalizeOptionalInteger(this.form.paymentDays),
|
||||
billingEnabled: this.form.feeGenerationMode === 'manual' ? 0 : 1,
|
||||
contractFileJson: JSON.stringify(this.contractFileRows),
|
||||
attachmentsJson: JSON.stringify(this.attachmentRows),
|
||||
@@ -1914,7 +1925,11 @@ export default {
|
||||
});
|
||||
},
|
||||
applyDetailState(detail = {}) {
|
||||
this.detailRow = { ...detail };
|
||||
this.detailRow = {
|
||||
...detail,
|
||||
copyCount: normalizeOptionalInteger(detail.copyCount),
|
||||
paymentDays: normalizeOptionalInteger(detail.paymentDays),
|
||||
};
|
||||
this.detailContractFileRows = parseArray(detail.contractFileJson);
|
||||
this.detailAttachmentRows = parseArray(detail.attachmentsJson);
|
||||
this.detailBillingPlanRows = parseArray(detail.billingPlanJson);
|
||||
@@ -1946,7 +1961,9 @@ export default {
|
||||
},
|
||||
detailUnitValue(prop, unit) {
|
||||
const value = this.detailRow[prop];
|
||||
return value === undefined || value === null || value === '' ? '-' : `${value}${unit}`;
|
||||
return value === undefined || value === null || value === '' || Number(value) < 0
|
||||
? ''
|
||||
: `${value}${unit}`;
|
||||
},
|
||||
detailObjectUnitValue(row, prop, unit) {
|
||||
const value = row?.[prop];
|
||||
@@ -1985,7 +2002,7 @@ export default {
|
||||
const run = value => {
|
||||
const args = operation.prompt ? [row.id, value] : [row.id];
|
||||
this.api[operation.action](...args).then(() => {
|
||||
this.$message.success(`${operation.label}成功`);
|
||||
this.$message.success(operation.successMessage || `${operation.label}成功`);
|
||||
this.onLoad(this.page, this.query);
|
||||
});
|
||||
};
|
||||
@@ -1997,9 +2014,9 @@ export default {
|
||||
inputErrorMessage: '请输入内容',
|
||||
}).then(({ value }) => run(value));
|
||||
} else {
|
||||
this.$confirm(`确定${operation.label}该合同?`, '提示', { type: 'warning' }).then(() =>
|
||||
run()
|
||||
);
|
||||
this.$confirm(operation.confirmMessage || `确定${operation.label}该合同?`, '提示', {
|
||||
type: 'warning',
|
||||
}).then(() => run());
|
||||
}
|
||||
},
|
||||
removeRow(row) {
|
||||
|
||||
@@ -391,7 +391,11 @@
|
||||
<el-table :data="customerRows" border class="project-apply-form__table">
|
||||
<el-table-column type="index" label="序号" width="80" align="center" />
|
||||
<el-table-column prop="customer" label="客户" min-width="160" align="center" />
|
||||
<el-table-column prop="credit" label="客户信用额度" min-width="150" align="center" />
|
||||
<el-table-column prop="credit" label="客户信用额度" min-width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ normalizeReadonlyAmount(row.credit) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="companyNature" label="企业性质" min-width="140" align="center" />
|
||||
<el-table-column prop="legalPerson" label="法定代表人" min-width="140" align="center" />
|
||||
<el-table-column
|
||||
@@ -505,7 +509,11 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="文件名" min-width="220" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.originalName || row.name }}</template>
|
||||
<template #default="{ row }">
|
||||
<el-link type="primary" @click="previewAttachment(row)">
|
||||
{{ attachmentName(row) }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="附件描述" min-width="240" align="center">
|
||||
<template #default="{ row }">
|
||||
@@ -536,6 +544,7 @@
|
||||
:readonly="dialogReadonly"
|
||||
:file-types="attachmentFileTypes"
|
||||
:max-size="500"
|
||||
:show-file-list="false"
|
||||
button-text="上传附件"
|
||||
tip="支持pdf、bmp、jpeg、png、jpg、doc、docx、ppt、pptx、xlsx、xls、eml、msg、zip的文件格式,单个文件不超过500M"
|
||||
@change="handleAttachmentChange"
|
||||
@@ -642,6 +651,38 @@
|
||||
</div>
|
||||
</component>
|
||||
|
||||
<el-dialog
|
||||
v-model="attachmentDocumentPreviewVisible"
|
||||
:title="attachmentPreviewFile.name || '附件预览'"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
width="90%"
|
||||
top="4vh"
|
||||
class="project-apply-form__attachment-viewer-dialog"
|
||||
>
|
||||
<open-file-viewer
|
||||
v-if="attachmentDocumentPreviewVisible && attachmentPreviewFile.url"
|
||||
:file="attachmentPreviewFile.url"
|
||||
:file-name="attachmentPreviewFile.name"
|
||||
:mime-type="attachmentPreviewFile.mimeType"
|
||||
width="100%"
|
||||
height="72vh"
|
||||
fit="contain"
|
||||
theme="auto"
|
||||
locale="zh-CN"
|
||||
:toolbar="attachmentViewerToolbar"
|
||||
:plugins="attachmentViewerPlugins"
|
||||
@unsupported="handleAttachmentPreviewUnsupported"
|
||||
@error="handleAttachmentPreviewError"
|
||||
/>
|
||||
</el-dialog>
|
||||
<el-image-viewer
|
||||
v-if="attachmentImagePreviewVisible"
|
||||
:url-list="attachmentImagePreviewUrls"
|
||||
:initial-index="attachmentImagePreviewIndex"
|
||||
@close="attachmentImagePreviewVisible = false"
|
||||
/>
|
||||
|
||||
<el-dialog
|
||||
v-model="userBox"
|
||||
title="选择用户"
|
||||
@@ -705,6 +746,17 @@ import { getDictionary as getBizDictionary } from '@/api/system/dictbiz';
|
||||
import { getList as getUserList } from '@/api/system/user';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { downloadFileByUrl, downloadXls } from '@/utils/util';
|
||||
import { ElImageViewer } from 'element-plus';
|
||||
import { OpenFileViewer } from '@open-file-viewer/vue';
|
||||
import {
|
||||
fallbackPlugin,
|
||||
imagePlugin,
|
||||
officePlugin,
|
||||
pdfPlugin,
|
||||
textPlugin,
|
||||
} from '@open-file-viewer/core';
|
||||
import '@open-file-viewer/core/style.css';
|
||||
import pdfWorkerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url';
|
||||
import { mapGetters } from 'vuex';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
@@ -767,8 +819,15 @@ const changeTypeOptions = [
|
||||
{ label: '项目备案调整', value: '项目备案调整' },
|
||||
];
|
||||
|
||||
const attachmentViewerPlugins = [
|
||||
imagePlugin(),
|
||||
pdfPlugin({ workerSrc: pdfWorkerSrc, useFetchData: true }),
|
||||
officePlugin({ pdf: { workerSrc: pdfWorkerSrc, useFetchData: true } }),
|
||||
textPlugin(),
|
||||
fallbackPlugin(),
|
||||
];
|
||||
|
||||
const normalProjectAttachmentTypeOptions = [
|
||||
'项目立项报告',
|
||||
'利润测算表',
|
||||
'单一来源供应商申请表/三方比价表',
|
||||
'合同模板',
|
||||
@@ -778,7 +837,6 @@ const normalProjectAttachmentTypeOptions = [
|
||||
].map(item => ({ label: item, value: item }));
|
||||
|
||||
const majorProjectAttachmentTypeOptions = [
|
||||
'项目立项报告',
|
||||
'会议纪要',
|
||||
'评审表决票',
|
||||
'利润测算表',
|
||||
@@ -793,6 +851,10 @@ const majorProjectAttachmentTypeOptions = [
|
||||
].map(item => ({ label: item, value: item }));
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ElImageViewer,
|
||||
OpenFileViewer,
|
||||
},
|
||||
data() {
|
||||
const validateAmount = (rule, value, callback) => {
|
||||
if (value === '' || value === undefined || value === null) {
|
||||
@@ -945,6 +1007,19 @@ export default {
|
||||
carrierRows: [],
|
||||
attachmentRows: [],
|
||||
selectedAttachmentRows: [],
|
||||
attachmentImagePreviewVisible: false,
|
||||
attachmentImagePreviewUrls: [],
|
||||
attachmentImagePreviewIndex: 0,
|
||||
attachmentDocumentPreviewVisible: false,
|
||||
attachmentPreviewFile: {},
|
||||
attachmentViewerPlugins,
|
||||
attachmentViewerToolbar: {
|
||||
download: true,
|
||||
fullscreen: true,
|
||||
print: true,
|
||||
rotate: true,
|
||||
zoom: true,
|
||||
},
|
||||
attachmentFileTypes: [
|
||||
'pdf',
|
||||
'bmp',
|
||||
@@ -1532,12 +1607,41 @@ export default {
|
||||
return submitRow;
|
||||
},
|
||||
normalizeAttachmentFileTypes() {
|
||||
const values = this.projectAttachmentTypeOptions.map(item => item.value);
|
||||
this.attachmentRows = this.attachmentRows.map(row => ({
|
||||
...row,
|
||||
fileType: values.includes(row.fileType) ? row.fileType : '',
|
||||
fileType: this.resolveAttachmentFileType(row),
|
||||
}));
|
||||
},
|
||||
normalizeAttachmentTypeText(value) {
|
||||
let text = String(value || '').split(/[?#]/)[0];
|
||||
try {
|
||||
text = decodeURIComponent(text);
|
||||
} catch (error) {
|
||||
// 文件名可能包含不完整的转义字符,直接使用原始名称继续匹配。
|
||||
}
|
||||
return text
|
||||
.replace(/\.[^.\\/]+$/, '')
|
||||
.toLowerCase()
|
||||
.replace(/[\s_\-—–·•()()\[\]【】{}《》<>“”"'、,,。.]/g, '');
|
||||
},
|
||||
attachmentTypeKeywords(fileType) {
|
||||
if (fileType === '单一来源供应商申请表/三方比价表') {
|
||||
return ['单一来源供应商申请表', '三方比价表'];
|
||||
}
|
||||
return [fileType];
|
||||
},
|
||||
resolveAttachmentFileType(row = {}) {
|
||||
const values = this.projectAttachmentTypeOptions.map(item => item.value);
|
||||
if (values.includes(row.fileType)) return row.fileType;
|
||||
const fileName = this.normalizeAttachmentTypeText(this.attachmentName(row));
|
||||
if (!fileName) return '';
|
||||
const option = this.projectAttachmentTypeOptions.find(item =>
|
||||
this.attachmentTypeKeywords(item.value).some(keyword =>
|
||||
fileName.includes(this.normalizeAttachmentTypeText(keyword))
|
||||
)
|
||||
);
|
||||
return option?.value || '';
|
||||
},
|
||||
validateAttachmentFileTypes() {
|
||||
const values = this.projectAttachmentTypeOptions.map(item => item.value);
|
||||
const invalidIndex = this.attachmentRows.findIndex(row => !values.includes(row.fileType));
|
||||
@@ -1840,10 +1944,9 @@ export default {
|
||||
handleAttachmentChange(list) {
|
||||
const userName = this.userInfo?.realName || this.userInfo?.userName || '';
|
||||
const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||
const values = this.projectAttachmentTypeOptions.map(item => item.value);
|
||||
this.attachmentRows = (list || []).map(item => ({
|
||||
...item,
|
||||
fileType: values.includes(item.fileType) ? item.fileType : '',
|
||||
fileType: this.resolveAttachmentFileType(item),
|
||||
description: item.description || '',
|
||||
uploadUserName: item.uploadUserName || userName,
|
||||
uploadTime: item.uploadTime || uploadTime,
|
||||
@@ -1863,19 +1966,74 @@ export default {
|
||||
if (number < 1024 * 1024) return `${(number / 1024).toFixed(1)}KB`;
|
||||
return `${(number / 1024 / 1024).toFixed(1)}MB`;
|
||||
},
|
||||
attachmentUrl(row = {}) {
|
||||
return row.url || row.link || row.fileUrl || row.src || row.domain || '';
|
||||
},
|
||||
attachmentName(row = {}) {
|
||||
return row.originalName || row.name || row.fileName || '附件';
|
||||
},
|
||||
attachmentExtension(row = {}) {
|
||||
const source = String(this.attachmentName(row) || this.attachmentUrl(row)).split('?')[0];
|
||||
const index = source.lastIndexOf('.');
|
||||
return index > -1 ? source.slice(index + 1).toLowerCase() : '';
|
||||
},
|
||||
isAttachmentImage(row) {
|
||||
return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(
|
||||
this.attachmentExtension(row)
|
||||
);
|
||||
},
|
||||
previewAttachment(row) {
|
||||
const url = this.attachmentUrl(row);
|
||||
if (!url) {
|
||||
this.$message.warning('附件地址为空,无法预览');
|
||||
return;
|
||||
}
|
||||
if (this.isAttachmentImage(row)) {
|
||||
this.attachmentImagePreviewUrls = this.attachmentRows
|
||||
.filter(item => this.isAttachmentImage(item) && this.attachmentUrl(item))
|
||||
.map(item => this.attachmentUrl(item));
|
||||
this.attachmentImagePreviewIndex = Math.max(
|
||||
this.attachmentImagePreviewUrls.indexOf(url),
|
||||
0
|
||||
);
|
||||
this.attachmentImagePreviewVisible = true;
|
||||
return;
|
||||
}
|
||||
this.attachmentPreviewFile = {
|
||||
name: this.attachmentName(row),
|
||||
url,
|
||||
mimeType: row.mimeType || row.contentType || '',
|
||||
};
|
||||
this.attachmentDocumentPreviewVisible = true;
|
||||
},
|
||||
handleAttachmentPreviewUnsupported() {
|
||||
this.$message.warning('当前文件暂不支持在线预览');
|
||||
},
|
||||
handleAttachmentPreviewError() {
|
||||
this.$message.error('附件预览失败');
|
||||
},
|
||||
downloadAttachment(row) {
|
||||
const url = row.url || row.link;
|
||||
const url = this.attachmentUrl(row);
|
||||
if (!url) {
|
||||
this.$message.warning('附件地址为空');
|
||||
return;
|
||||
}
|
||||
downloadFileByUrl(url, row.originalName || row.name || '附件');
|
||||
downloadFileByUrl(url, this.attachmentName(row));
|
||||
},
|
||||
handleBatchDownload() {
|
||||
const rows = this.selectedAttachmentRows.length
|
||||
? this.selectedAttachmentRows
|
||||
: this.attachmentRows;
|
||||
rows.forEach(row => this.downloadAttachment(row));
|
||||
const downloadableRows = rows.filter(row => this.attachmentUrl(row));
|
||||
if (!downloadableRows.length) {
|
||||
this.$message.warning(
|
||||
this.selectedAttachmentRows.length ? '所选附件暂无可下载地址' : '暂无可下载附件'
|
||||
);
|
||||
return;
|
||||
}
|
||||
downloadableRows.forEach((row, index) => {
|
||||
window.setTimeout(() => this.downloadAttachment(row), index * 300);
|
||||
});
|
||||
},
|
||||
handleChangeRecordAction() {
|
||||
this.$message.info('变更记录详情由后端明细能力生成后展示');
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||
<el-table-column label="文件名" min-width="240" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-link type="primary" @click="downloadAttachment(row)">
|
||||
<el-link type="primary" @click="previewAttachment(row)">
|
||||
{{ attachmentName(row) }}
|
||||
</el-link>
|
||||
</template>
|
||||
@@ -169,6 +169,38 @@
|
||||
@load="onLoad(page, query)"
|
||||
/>
|
||||
|
||||
<el-dialog
|
||||
v-model="attachmentDocumentPreviewVisible"
|
||||
:title="attachmentPreviewFile.name || '附件预览'"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
width="90%"
|
||||
top="4vh"
|
||||
class="temporary-credit-limit-page__attachment-viewer-dialog"
|
||||
>
|
||||
<open-file-viewer
|
||||
v-if="attachmentDocumentPreviewVisible && attachmentPreviewFile.url"
|
||||
:file="attachmentPreviewFile.url"
|
||||
:file-name="attachmentPreviewFile.name"
|
||||
:mime-type="attachmentPreviewFile.mimeType"
|
||||
width="100%"
|
||||
height="72vh"
|
||||
fit="contain"
|
||||
theme="auto"
|
||||
locale="zh-CN"
|
||||
:toolbar="attachmentViewerToolbar"
|
||||
:plugins="attachmentViewerPlugins"
|
||||
@unsupported="handleAttachmentPreviewUnsupported"
|
||||
@error="handleAttachmentPreviewError"
|
||||
/>
|
||||
</el-dialog>
|
||||
<el-image-viewer
|
||||
v-if="attachmentImagePreviewVisible"
|
||||
:url-list="attachmentImagePreviewUrls"
|
||||
:initial-index="attachmentImagePreviewIndex"
|
||||
@close="attachmentImagePreviewVisible = false"
|
||||
/>
|
||||
|
||||
<flow-design-step
|
||||
v-if="website.design.designMode"
|
||||
v-model:is-display="flowBox"
|
||||
@@ -191,6 +223,17 @@ import {
|
||||
import { config, option } from '@/option/business/temporary-credit-limit';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { downloadFileByUrl, downloadXls } from '@/utils/util';
|
||||
import { ElImageViewer } from 'element-plus';
|
||||
import { OpenFileViewer } from '@open-file-viewer/vue';
|
||||
import {
|
||||
fallbackPlugin,
|
||||
imagePlugin,
|
||||
officePlugin,
|
||||
pdfPlugin,
|
||||
textPlugin,
|
||||
} from '@open-file-viewer/core';
|
||||
import '@open-file-viewer/core/style.css';
|
||||
import pdfWorkerSrc from 'pdfjs-dist/build/pdf.worker.mjs?url';
|
||||
import { mapGetters } from 'vuex';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
@@ -212,6 +255,14 @@ const attachmentFileTypes = [
|
||||
'zip',
|
||||
];
|
||||
|
||||
const attachmentViewerPlugins = [
|
||||
imagePlugin(),
|
||||
pdfPlugin({ workerSrc: pdfWorkerSrc, useFetchData: true }),
|
||||
officePlugin({ pdf: { workerSrc: pdfWorkerSrc, useFetchData: true } }),
|
||||
textPlugin(),
|
||||
fallbackPlugin(),
|
||||
];
|
||||
|
||||
const extractRecords = res => {
|
||||
const data = res?.data?.data || res?.data || {};
|
||||
return Array.isArray(data) ? data : data.records || [];
|
||||
@@ -219,6 +270,10 @@ const extractRecords = res => {
|
||||
|
||||
export default {
|
||||
name: 'TemporaryCreditLimit',
|
||||
components: {
|
||||
ElImageViewer,
|
||||
OpenFileViewer,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
api,
|
||||
@@ -242,6 +297,19 @@ export default {
|
||||
projectLoading: false,
|
||||
attachmentRows: [],
|
||||
selectedAttachments: [],
|
||||
attachmentImagePreviewVisible: false,
|
||||
attachmentImagePreviewUrls: [],
|
||||
attachmentImagePreviewIndex: 0,
|
||||
attachmentDocumentPreviewVisible: false,
|
||||
attachmentPreviewFile: {},
|
||||
attachmentViewerPlugins,
|
||||
attachmentViewerToolbar: {
|
||||
download: true,
|
||||
fullscreen: true,
|
||||
print: true,
|
||||
rotate: true,
|
||||
zoom: true,
|
||||
},
|
||||
attachmentFileTypes,
|
||||
flowBox: false,
|
||||
flowUrl: '',
|
||||
@@ -587,6 +655,46 @@ export default {
|
||||
attachmentUrl(row = {}) {
|
||||
return row.url || row.link || row.fileUrl || row.downloadUrl || row.domain || '';
|
||||
},
|
||||
attachmentExtension(row = {}) {
|
||||
const source = String(this.attachmentName(row) || this.attachmentUrl(row)).split('?')[0];
|
||||
const index = source.lastIndexOf('.');
|
||||
return index > -1 ? source.slice(index + 1).toLowerCase() : '';
|
||||
},
|
||||
isAttachmentImage(row) {
|
||||
return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(
|
||||
this.attachmentExtension(row)
|
||||
);
|
||||
},
|
||||
previewAttachment(row) {
|
||||
const url = this.attachmentUrl(row);
|
||||
if (!url) {
|
||||
this.$message.warning('附件地址为空,无法预览');
|
||||
return;
|
||||
}
|
||||
if (this.isAttachmentImage(row)) {
|
||||
this.attachmentImagePreviewUrls = this.attachmentRows
|
||||
.filter(item => this.isAttachmentImage(item) && this.attachmentUrl(item))
|
||||
.map(item => this.attachmentUrl(item));
|
||||
this.attachmentImagePreviewIndex = Math.max(
|
||||
this.attachmentImagePreviewUrls.indexOf(url),
|
||||
0
|
||||
);
|
||||
this.attachmentImagePreviewVisible = true;
|
||||
return;
|
||||
}
|
||||
this.attachmentPreviewFile = {
|
||||
name: this.attachmentName(row),
|
||||
url,
|
||||
mimeType: row.mimeType || row.contentType || '',
|
||||
};
|
||||
this.attachmentDocumentPreviewVisible = true;
|
||||
},
|
||||
handleAttachmentPreviewUnsupported() {
|
||||
this.$message.warning('当前文件暂不支持在线预览');
|
||||
},
|
||||
handleAttachmentPreviewError() {
|
||||
this.$message.error('附件预览失败');
|
||||
},
|
||||
downloadAttachment(row) {
|
||||
const url = this.attachmentUrl(row);
|
||||
if (!url) {
|
||||
@@ -597,7 +705,16 @@ export default {
|
||||
},
|
||||
batchDownload() {
|
||||
const rows = this.selectedAttachments.length ? this.selectedAttachments : this.attachmentRows;
|
||||
rows.forEach(this.downloadAttachment);
|
||||
const downloadableRows = rows.filter(row => this.attachmentUrl(row));
|
||||
if (!downloadableRows.length) {
|
||||
this.$message.warning(
|
||||
this.selectedAttachments.length ? '所选附件暂无可下载地址' : '暂无可下载附件'
|
||||
);
|
||||
return;
|
||||
}
|
||||
downloadableRows.forEach((row, index) => {
|
||||
window.setTimeout(() => this.downloadAttachment(row), index * 300);
|
||||
});
|
||||
},
|
||||
formatFileSize(size) {
|
||||
const value = Number(size);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
class="bill-ledger-form-page__form"
|
||||
>
|
||||
<section-card title="基本信息">
|
||||
<el-row :gutter="24">
|
||||
<el-row :gutter="24" class="bill-ledger-form-page__basic-grid">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="票据号码" prop="billNo">
|
||||
<el-input
|
||||
@@ -23,6 +23,18 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="汇票类型" prop="billType">
|
||||
<el-select v-model="form.billType" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in billTypeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="出票单位" prop="issuerId">
|
||||
<el-select
|
||||
@@ -43,6 +55,24 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="收票单位" prop="receiverName">
|
||||
<el-select
|
||||
v-model="form.receiverName"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
placeholder="请选择或输入"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in receiverOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="票面金额" prop="faceAmount">
|
||||
<el-input-number
|
||||
@@ -54,6 +84,11 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="可用余额">
|
||||
<el-input :model-value="formatMoney(projectedAvailableBalance)" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="出票日期" prop="issueDate">
|
||||
<el-date-picker
|
||||
@@ -64,6 +99,16 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="到期日期" prop="maturityDate">
|
||||
<el-date-picker
|
||||
v-model="form.maturityDate"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="可用部门" prop="availableDeptIds">
|
||||
<el-tree-select
|
||||
@@ -80,6 +125,19 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="出票行" prop="issuingBank">
|
||||
<el-select
|
||||
v-model="form.issuingBank"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
placeholder="请选择或输入"
|
||||
>
|
||||
<el-option v-for="item in bankOptions" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="费用承担方" prop="feeBearerId">
|
||||
<el-select
|
||||
@@ -101,9 +159,9 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="双方确认贴现率" prop="confirmedDiscountRate">
|
||||
<el-form-item label="银行贴现参考率" prop="bankDiscountReferenceRate">
|
||||
<el-input-number
|
||||
v-model="form.confirmedDiscountRate"
|
||||
v-model="form.bankDiscountReferenceRate"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:precision="4"
|
||||
@@ -115,67 +173,9 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="汇票类型" prop="billType">
|
||||
<el-select v-model="form.billType" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in billTypeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="收票单位" prop="receiverName">
|
||||
<el-select
|
||||
v-model="form.receiverName"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
placeholder="请选择或输入"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in receiverOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="可用余额">
|
||||
<el-input :model-value="formatMoney(projectedAvailableBalance)" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="到期日期" prop="maturityDate">
|
||||
<el-date-picker
|
||||
v-model="form.maturityDate"
|
||||
type="date"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="出票行" prop="issuingBank">
|
||||
<el-select
|
||||
v-model="form.issuingBank"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
placeholder="请选择或输入"
|
||||
>
|
||||
<el-option v-for="item in bankOptions" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="银行贴现参考率" prop="bankDiscountReferenceRate">
|
||||
<el-form-item label="双方确认贴现率" prop="confirmedDiscountRate">
|
||||
<el-input-number
|
||||
v-model="form.bankDiscountReferenceRate"
|
||||
v-model="form.confirmedDiscountRate"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:precision="4"
|
||||
@@ -560,11 +560,19 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
|
||||
.bill-ledger-form-page__form :deep(.el-form-item) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.bill-ledger-form-page__basic-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
grid-auto-flow: row;
|
||||
}
|
||||
.bill-ledger-form-page__basic-grid > .el-col {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
flex: none;
|
||||
}
|
||||
.bill-ledger-form-page__form :deep(.el-input),
|
||||
.bill-ledger-form-page__form :deep(.el-select),
|
||||
.bill-ledger-form-page__form :deep(.el-input-number),
|
||||
@@ -591,9 +599,10 @@ export default {
|
||||
background: #fafafa;
|
||||
}
|
||||
@media (max-width: 1024px) {
|
||||
.bill-ledger-form-page :deep(.el-col-6) {
|
||||
max-width: 100%;
|
||||
flex: 0 0 100%;
|
||||
.bill-ledger-form-page__basic-grid {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
grid-template-rows: none;
|
||||
grid-auto-flow: row;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="结算单" prop="settlementIds">
|
||||
<el-input :model-value="settlementLabel" readonly placeholder="请选择正式结算单">
|
||||
<el-input :model-value="settlementLabel" readonly placeholder="请选择应付正式结算单">
|
||||
<template v-if="!readonly" #append>
|
||||
<el-button @click="openSettlementDialog">选择</el-button>
|
||||
</template>
|
||||
@@ -429,7 +429,12 @@
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<el-dialog v-model="settlementDialog.visible" title="选择正式结算单" width="86%" append-to-body>
|
||||
<el-dialog
|
||||
v-model="settlementDialog.visible"
|
||||
title="选择应付正式结算单"
|
||||
width="86%"
|
||||
append-to-body
|
||||
>
|
||||
<div class="invoice-form-page__dialog-search">
|
||||
<el-input
|
||||
v-model="settlementDialog.keyword"
|
||||
@@ -723,8 +728,10 @@ export default {
|
||||
async loadSettlementCandidates() {
|
||||
this.settlementDialog.loading = true;
|
||||
try {
|
||||
this.settlementDialog.rows =
|
||||
this.unwrapData(await api.getSettlementCandidates(this.settlementDialog.keyword)) || [];
|
||||
const data = this.unwrapData(
|
||||
await api.getSettlementCandidates(this.settlementDialog.keyword)
|
||||
);
|
||||
this.settlementDialog.rows = Array.isArray(data) ? data : data?.records || [];
|
||||
} finally {
|
||||
this.settlementDialog.loading = false;
|
||||
}
|
||||
@@ -732,7 +739,7 @@ export default {
|
||||
async confirmSettlements() {
|
||||
const rows = this.settlementDialog.selection;
|
||||
if (!rows.length) {
|
||||
this.$message.warning('请至少选择一张正式结算单');
|
||||
this.$message.warning('请至少选择一张应付正式结算单');
|
||||
return;
|
||||
}
|
||||
const first = rows[0];
|
||||
|
||||
@@ -123,12 +123,25 @@
|
||||
><el-input v-model="form.payeeName" disabled /></el-form-item
|
||||
></el-col>
|
||||
<el-col :span="6"
|
||||
><el-form-item label="收款账号"
|
||||
><el-input v-model="form.bankAccount" :disabled="readonly" /></el-form-item
|
||||
><el-form-item label="收款账号" prop="receiptAccountId"
|
||||
><el-select
|
||||
v-model="form.receiptAccountId"
|
||||
:disabled="readonly || !form.payeeName"
|
||||
:loading="receiptAccountLoading"
|
||||
filterable
|
||||
clearable
|
||||
placeholder="请选择收款方的收款账号"
|
||||
no-data-text="该收款方未维护可用收款信息"
|
||||
@change="handleReceiptAccountChange"
|
||||
><el-option
|
||||
v-for="item in receiptAccountOptions"
|
||||
:key="item.id"
|
||||
:label="receiptAccountLabel(item)"
|
||||
:value="item.id" /></el-select></el-form-item
|
||||
></el-col>
|
||||
<el-col :span="6"
|
||||
><el-form-item label="开户银行"
|
||||
><el-input v-model="form.bankName" :disabled="readonly" /></el-form-item
|
||||
><el-input v-model="form.bankName" disabled /></el-form-item
|
||||
></el-col>
|
||||
<el-col :span="6"
|
||||
><el-form-item label="申请人"
|
||||
@@ -244,7 +257,24 @@
|
||||
min-width="160"
|
||||
/></el-table>
|
||||
</section-card>
|
||||
<section-card title="附件">
|
||||
<section-card>
|
||||
<template #title>
|
||||
<span>附件</span>
|
||||
<span v-if="missingAttachmentMaterials.length" class="payment-form-page__missing-text">
|
||||
未上传:{{ missingAttachmentMaterials.map(item => item.label).join('、') }}
|
||||
</span>
|
||||
</template>
|
||||
<div v-loading="attachmentRuleLoading" class="payment-form-page__attachment-checklist">
|
||||
<span class="payment-form-page__attachment-checklist-label">材料清单</span>
|
||||
<el-tag
|
||||
v-for="item in requiredAttachmentMaterials"
|
||||
:key="item.key"
|
||||
:type="hasAttachmentMaterial(item) ? 'success' : 'danger'"
|
||||
effect="plain"
|
||||
>
|
||||
{{ item.label }}({{ hasAttachmentMaterial(item) ? '已上传' : '未上传' }})
|
||||
</el-tag>
|
||||
</div>
|
||||
<el-table :data="form.attachments" border
|
||||
><el-table-column type="index" label="序号" width="70" /><el-table-column
|
||||
label="附件类型"
|
||||
@@ -252,8 +282,10 @@
|
||||
><template #default="{ row }"
|
||||
><el-select v-model="row.attachmentType" :disabled="readonly"
|
||||
><el-option label="磅单" value="weighing_slip" /><el-option
|
||||
label="结算单"
|
||||
value="settlement" /><el-option label="合同签章文件" value="contract" /><el-option
|
||||
label="委托单"
|
||||
value="entrust_order" /><el-option label="结算单" value="settlement" /><el-option
|
||||
label="合同签章文件"
|
||||
value="contract" /><el-option
|
||||
label="特批附件"
|
||||
value="special_approval" /><el-option
|
||||
label="其他"
|
||||
@@ -297,7 +329,7 @@
|
||||
</section-card>
|
||||
<div class="payment-form-page__actions">
|
||||
<el-button @click="goBack">返回</el-button
|
||||
><el-button v-if="!readonly && canSave" @click="saveDraft">保存</el-button
|
||||
><el-button v-if="!readonly && canSave" @click="saveDraft(false)">保存</el-button
|
||||
><el-button
|
||||
v-if="!readonly && canSave && hasPermission('payment_application_submit')"
|
||||
type="primary"
|
||||
@@ -347,8 +379,38 @@
|
||||
import { mapGetters } from 'vuex';
|
||||
import * as api from '@/api/payment/paymentApplication';
|
||||
import * as billLedgerApi from '@/api/payment/billLedger';
|
||||
import { getDetail as getContractDetail } from '@/api/business/contract-manage';
|
||||
import * as formalApi from '@/api/settlement/formalSettlement';
|
||||
import * as preApi from '@/api/settlement/preSettlement';
|
||||
import {
|
||||
getDetail as getCustomerArchiveDetail,
|
||||
getList as getCustomerArchiveList,
|
||||
} from '@/api/vehicle/customer-archive';
|
||||
import { readSettlementTransfer, removeSettlementTransfer } from '@/utils/settlement-transfer';
|
||||
|
||||
const ATTACHMENT_MATERIALS = {
|
||||
weighingSlip: { key: 'weighingSlip', label: '磅单', keywords: ['磅单'] },
|
||||
entrustOrder: { key: 'entrustOrder', label: '委托单', keywords: ['委托单'] },
|
||||
settlement: { key: 'settlement', label: '结算单', keywords: ['结算单'] },
|
||||
contract: {
|
||||
key: 'contract',
|
||||
label: '合同签章文件',
|
||||
keywords: ['合同签章文件', '合同签章', '签章'],
|
||||
},
|
||||
specialApproval: {
|
||||
key: 'specialApproval',
|
||||
label: '特批附件',
|
||||
keywords: ['特批附件', '特批'],
|
||||
},
|
||||
};
|
||||
|
||||
const MATERIAL_TYPE_MAP = {
|
||||
weighingSlip: 'weighing_slip',
|
||||
entrustOrder: 'entrust_order',
|
||||
settlement: 'settlement',
|
||||
contract: 'contract',
|
||||
specialApproval: 'special_approval',
|
||||
};
|
||||
|
||||
const emptyForm = () => ({
|
||||
id: null,
|
||||
@@ -356,6 +418,7 @@ const emptyForm = () => ({
|
||||
paymentType: 'project_advance',
|
||||
settlementId: null,
|
||||
preSettlementId: null,
|
||||
preSettlementIds: [],
|
||||
projectId: null,
|
||||
projectName: '',
|
||||
deptId: null,
|
||||
@@ -389,6 +452,8 @@ export default {
|
||||
return {
|
||||
form: emptyForm(),
|
||||
amountSyncing: false,
|
||||
transferPayload: null,
|
||||
referenceRows: [],
|
||||
paymentRecords: [],
|
||||
referenceVisible: false,
|
||||
referenceTab: 'formal',
|
||||
@@ -397,6 +462,11 @@ export default {
|
||||
contractRows: [],
|
||||
billOptions: [],
|
||||
billLoading: false,
|
||||
receiptAccountOptions: [],
|
||||
receiptAccountLoading: false,
|
||||
firstContractPayment: false,
|
||||
payeeCustomerLevel: '',
|
||||
attachmentRuleLoading: false,
|
||||
paymentTypeOptions: api.paymentTypeOptions,
|
||||
paymentMethodOptions: api.paymentMethodOptions,
|
||||
attachmentFileTypes: [
|
||||
@@ -420,6 +490,7 @@ export default {
|
||||
paymentType: [{ required: true, message: '请选择付款类型' }],
|
||||
referenceId: [{ validator: this.validateReference, trigger: 'change' }],
|
||||
paymentMethod: [{ required: true, message: '请选择付款方式' }],
|
||||
receiptAccountId: [{ required: true, message: '请选择收款账号', trigger: 'change' }],
|
||||
billLedgerId: [{ validator: this.validateBillLedger, trigger: 'change' }],
|
||||
appliedAmount: [{ validator: this.validateAppliedAmount, trigger: 'change' }],
|
||||
},
|
||||
@@ -441,9 +512,26 @@ export default {
|
||||
return ['bank_draft', 'commercial_draft'].includes(this.form.paymentMethod);
|
||||
},
|
||||
referenceLabel() {
|
||||
if (this.referenceRows.length) {
|
||||
return this.referenceRows
|
||||
.map(item => item.settlementNo)
|
||||
.filter(Boolean)
|
||||
.join('、');
|
||||
}
|
||||
return this.form.settlementNo || this.form.preSettlementNo || '';
|
||||
},
|
||||
settlementRows() {
|
||||
if (this.referenceRows.length) {
|
||||
return this.referenceRows.map(item => ({
|
||||
...item,
|
||||
paymentRatio: this.form.paymentRatio,
|
||||
appliedAmount: Number(
|
||||
((Number(item.payableAmount || 0) * Number(this.form.paymentRatio || 0)) / 100).toFixed(
|
||||
2
|
||||
)
|
||||
),
|
||||
}));
|
||||
}
|
||||
if (!this.referenceLabel) return [];
|
||||
return [
|
||||
{
|
||||
@@ -455,8 +543,32 @@ export default {
|
||||
},
|
||||
];
|
||||
},
|
||||
highRiskPayee() {
|
||||
const level = String(this.payeeCustomerLevel || '')
|
||||
.trim()
|
||||
.toUpperCase();
|
||||
return ['D', 'D级', 'E', 'E级', 'F', 'F级'].includes(level) || level.includes('高风险');
|
||||
},
|
||||
requiredAttachmentMaterials() {
|
||||
const materials = [ATTACHMENT_MATERIALS.weighingSlip, ATTACHMENT_MATERIALS.settlement];
|
||||
if (this.form.paymentType === 'project_advance') {
|
||||
materials.splice(1, 0, ATTACHMENT_MATERIALS.entrustOrder);
|
||||
}
|
||||
if (this.firstContractPayment) materials.push(ATTACHMENT_MATERIALS.contract);
|
||||
if (this.highRiskPayee) materials.push(ATTACHMENT_MATERIALS.specialApproval);
|
||||
return materials;
|
||||
},
|
||||
missingAttachmentMaterials() {
|
||||
return this.requiredAttachmentMaterials.filter(item => !this.hasAttachmentMaterial(item));
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'$route.query.transferToken': async function (token, oldToken) {
|
||||
if (!this.recordId && token && token !== oldToken) {
|
||||
this.transferPayload = readSettlementTransfer(token);
|
||||
await this.initialize();
|
||||
}
|
||||
},
|
||||
'form.paymentRatio'(value) {
|
||||
if (this.amountSyncing || !Number(this.form.payableAmount)) return;
|
||||
this.amountSyncing = true;
|
||||
@@ -479,6 +591,7 @@ export default {
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.transferPayload = readSettlementTransfer(this.$route.query.transferToken);
|
||||
this.initialize();
|
||||
},
|
||||
methods: {
|
||||
@@ -486,7 +599,11 @@ export default {
|
||||
return this.permission?.[code] !== false;
|
||||
},
|
||||
validateReference(rule, value, callback) {
|
||||
if (this.form.paymentType === 'progress_advance' && !this.form.preSettlementId) {
|
||||
if (
|
||||
this.form.paymentType === 'progress_advance' &&
|
||||
!this.form.preSettlementId &&
|
||||
!this.form.preSettlementIds?.length
|
||||
) {
|
||||
callback(new Error('请选择预结算单'));
|
||||
return;
|
||||
}
|
||||
@@ -556,17 +673,133 @@ export default {
|
||||
this.form = {
|
||||
...emptyForm(),
|
||||
...data,
|
||||
preSettlementIds: data.preSettlementIds?.length
|
||||
? data.preSettlementIds
|
||||
: data.preSettlementId
|
||||
? [data.preSettlementId]
|
||||
: [],
|
||||
attachments: this.parse(data.attachmentsJson),
|
||||
invoices: data.invoices || [],
|
||||
};
|
||||
const referenceRow = this.createReferenceRow(data);
|
||||
this.referenceRows = referenceRow.settlementNo ? [referenceRow] : [];
|
||||
this.paymentRecords = data.paymentRecords || [];
|
||||
await this.loadReceiptAccountOptions(false);
|
||||
await this.loadAttachmentRuleData(await this.loadCurrentSettlementDetails());
|
||||
if (this.billPayment) await this.loadBillOptions('', data.billLedgerId);
|
||||
} else {
|
||||
this.form = emptyForm();
|
||||
this.referenceRows = [];
|
||||
this.form.applyDate = this.$dayjs().format('YYYY-MM-DD');
|
||||
this.form.applicantName =
|
||||
this.$store.getters.userInfo?.realName || this.$store.getters.userInfo?.userName || '';
|
||||
await this.loadTransferredPreSettlements();
|
||||
}
|
||||
},
|
||||
getTransferredPreSettlementIds() {
|
||||
const payloadIds = (this.transferPayload?.sourcePreSettlements || []).map(
|
||||
item => item.preSettlementId || item.id
|
||||
);
|
||||
const queryIds = String(this.$route.query.preSettlementIds || '')
|
||||
.split(',')
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean);
|
||||
return [...new Set([...payloadIds, ...queryIds].map(String))];
|
||||
},
|
||||
async loadTransferredPreSettlements() {
|
||||
const fallbackRows = this.transferPayload?.sourcePreSettlements || [];
|
||||
const ids = this.getTransferredPreSettlementIds();
|
||||
if (!ids.length) {
|
||||
this.applyTransferredPreSettlements(fallbackRows);
|
||||
await Promise.all([
|
||||
this.loadReceiptAccountOptions(),
|
||||
this.loadAttachmentRuleData(fallbackRows),
|
||||
]);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const responses = await Promise.all(ids.map(id => preApi.getDetail(id)));
|
||||
const rows = responses.map(response => this.unwrapData(response)).filter(item => item?.id);
|
||||
const sourceRows = rows.length ? rows : fallbackRows;
|
||||
this.applyTransferredPreSettlements(sourceRows);
|
||||
await Promise.all([
|
||||
this.loadReceiptAccountOptions(),
|
||||
this.loadAttachmentRuleData(sourceRows),
|
||||
]);
|
||||
} catch (error) {
|
||||
if (fallbackRows.length) {
|
||||
this.applyTransferredPreSettlements(fallbackRows);
|
||||
await Promise.all([
|
||||
this.loadReceiptAccountOptions(),
|
||||
this.loadAttachmentRuleData(fallbackRows),
|
||||
]);
|
||||
return;
|
||||
}
|
||||
this.$message.error('预结算信息加载失败,请返回后重新发起预付申请');
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
createReferenceRow(row) {
|
||||
const settlementAmount = Number(row.settlementAmount || 0);
|
||||
const appliedAmount = Number(row.advanceAppliedAmount || row.appliedPaymentAmount || 0);
|
||||
return {
|
||||
id: row.preSettlementId || row.settlementId || row.id,
|
||||
settlementNo: row.preSettlementNo || row.formalSettlementNo || row.settlementNo || '',
|
||||
settlementAmount,
|
||||
payableAmount:
|
||||
row.payableAmount === undefined || row.payableAmount === null
|
||||
? Math.max(0, settlementAmount - appliedAmount)
|
||||
: Number(row.payableAmount || 0),
|
||||
};
|
||||
},
|
||||
applyTransferredPreSettlements(rows = []) {
|
||||
if (!rows.length) return;
|
||||
const contractIds = new Set(rows.map(item => String(item.contractId || '')));
|
||||
if (contractIds.size > 1 || contractIds.has('')) {
|
||||
this.$message.warning('所选预结算单合同信息不一致,请返回后重新选择');
|
||||
return;
|
||||
}
|
||||
const first = rows[0];
|
||||
this.referenceRows = rows.map(item => this.createReferenceRow(item));
|
||||
const settlementAmount = this.referenceRows.reduce(
|
||||
(total, item) => total + Number(item.settlementAmount || 0),
|
||||
0
|
||||
);
|
||||
const payableAmount = this.referenceRows.reduce(
|
||||
(total, item) => total + Number(item.payableAmount || 0),
|
||||
0
|
||||
);
|
||||
const paymentRatio = Number(this.form.paymentRatio || 0);
|
||||
this.amountSyncing = true;
|
||||
Object.assign(this.form, {
|
||||
paymentType: 'progress_advance',
|
||||
settlementId: null,
|
||||
settlementNo: '',
|
||||
preSettlementId: first.preSettlementId || first.id,
|
||||
preSettlementIds: rows.map(item => item.preSettlementId || item.id),
|
||||
preSettlementNo: this.referenceRows
|
||||
.map(item => item.settlementNo)
|
||||
.filter(Boolean)
|
||||
.join('、'),
|
||||
projectId: first.projectId,
|
||||
projectName: first.projectName,
|
||||
deptId: first.deptId,
|
||||
deptName: first.deptName,
|
||||
contractId: first.contractId,
|
||||
contractNo: first.contractNo,
|
||||
contractName: first.contractName,
|
||||
payerName: first.payerName,
|
||||
payeeName: first.payeeName,
|
||||
settlementAmount: Number(settlementAmount.toFixed(2)),
|
||||
payableAmount: Number(payableAmount.toFixed(2)),
|
||||
billType: '预结算单',
|
||||
appliedAmount: Number(((payableAmount * paymentRatio) / 100).toFixed(2)),
|
||||
});
|
||||
this.$nextTick(() => {
|
||||
this.amountSyncing = false;
|
||||
this.$refs.formRef?.clearValidate();
|
||||
});
|
||||
},
|
||||
parse(value) {
|
||||
if (!value) return [];
|
||||
if (Array.isArray(value)) return value;
|
||||
@@ -576,11 +809,218 @@ export default {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
attachmentFileName(file) {
|
||||
return String(file?.originalName || file?.name || file?.fileName || '').trim();
|
||||
},
|
||||
attachmentFileUrl(file) {
|
||||
return file?.url || file?.link || file?.src || file?.domain || '';
|
||||
},
|
||||
attachmentMatchesMaterial(file, material) {
|
||||
const fileName = this.attachmentFileName(file).toLocaleLowerCase();
|
||||
return (
|
||||
Boolean(this.attachmentFileUrl(file)) &&
|
||||
material.keywords.some(keyword => fileName.includes(keyword.toLocaleLowerCase()))
|
||||
);
|
||||
},
|
||||
hasAttachmentMaterial(material) {
|
||||
return (this.form.attachments || []).some(file =>
|
||||
this.attachmentMatchesMaterial(file, material)
|
||||
);
|
||||
},
|
||||
resolveAttachmentMaterial(file, materials = Object.values(ATTACHMENT_MATERIALS)) {
|
||||
return [...materials]
|
||||
.sort(
|
||||
(a, b) =>
|
||||
Math.max(...b.keywords.map(String.length)) - Math.max(...a.keywords.map(String.length))
|
||||
)
|
||||
.find(material => this.attachmentMatchesMaterial(file, material));
|
||||
},
|
||||
mergeAutomaticAttachments(files, materials, sourceName) {
|
||||
const existingUrls = new Set(
|
||||
(this.form.attachments || []).map(file => this.attachmentFileUrl(file)).filter(Boolean)
|
||||
);
|
||||
const imported = (files || [])
|
||||
.map(file => ({ file, material: this.resolveAttachmentMaterial(file, materials) }))
|
||||
.filter(item => item.material && !existingUrls.has(this.attachmentFileUrl(item.file)))
|
||||
.map(({ file, material }) => {
|
||||
existingUrls.add(this.attachmentFileUrl(file));
|
||||
return {
|
||||
...file,
|
||||
originalName: this.attachmentFileName(file),
|
||||
name: this.attachmentFileName(file),
|
||||
url: this.attachmentFileUrl(file),
|
||||
link: file.link || this.attachmentFileUrl(file),
|
||||
attachmentType: MATERIAL_TYPE_MAP[material.key],
|
||||
description: file.description || `自动取自${sourceName}`,
|
||||
sourceImported: true,
|
||||
};
|
||||
});
|
||||
if (imported.length) this.form.attachments = [...this.form.attachments, ...imported];
|
||||
},
|
||||
async loadCurrentSettlementDetails() {
|
||||
const requests = [];
|
||||
if (this.form.preSettlementId) requests.push(preApi.getDetail(this.form.preSettlementId));
|
||||
if (this.form.settlementId) requests.push(formalApi.getDetail(this.form.settlementId));
|
||||
if (!requests.length) return [];
|
||||
const responses = await Promise.all(requests);
|
||||
return responses.map(response => this.unwrapData(response)).filter(item => item?.id);
|
||||
},
|
||||
async loadAttachmentRuleData(settlementRows = []) {
|
||||
this.attachmentRuleLoading = true;
|
||||
this.firstContractPayment = false;
|
||||
try {
|
||||
const settlementMaterials = [
|
||||
ATTACHMENT_MATERIALS.weighingSlip,
|
||||
ATTACHMENT_MATERIALS.entrustOrder,
|
||||
ATTACHMENT_MATERIALS.settlement,
|
||||
];
|
||||
const settlementFiles = (settlementRows || []).flatMap(row =>
|
||||
this.parse(row.attachmentsJson)
|
||||
);
|
||||
this.mergeAutomaticAttachments(settlementFiles, settlementMaterials, '结算单');
|
||||
if (!this.form.contractId) {
|
||||
this.firstContractPayment = false;
|
||||
return;
|
||||
}
|
||||
const [contractResponse, paymentResponse] = await Promise.all([
|
||||
getContractDetail(this.form.contractId),
|
||||
api.getList(1, 9999, { contractId: this.form.contractId }),
|
||||
]);
|
||||
const contract = this.unwrapData(contractResponse) || {};
|
||||
const payments = this.unwrapData(paymentResponse)?.records || [];
|
||||
this.firstContractPayment = !payments.some(
|
||||
item =>
|
||||
String(item.contractId || '') === String(this.form.contractId) &&
|
||||
String(item.id || '') !== String(this.form.id || '') &&
|
||||
item.approvalStatus !== 'voided'
|
||||
);
|
||||
if (this.firstContractPayment) {
|
||||
this.mergeAutomaticAttachments(
|
||||
this.parse(contract.contractFileJson),
|
||||
[ATTACHMENT_MATERIALS.contract],
|
||||
'合同'
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.warning('附件清单来源材料加载失败,请核对后手工补充');
|
||||
} finally {
|
||||
this.attachmentRuleLoading = false;
|
||||
}
|
||||
},
|
||||
validateRequiredAttachments() {
|
||||
if (!this.missingAttachmentMaterials.length) return true;
|
||||
this.$message.warning(
|
||||
`请先上传付款申请所需材料:${this.missingAttachmentMaterials
|
||||
.map(item => item.label)
|
||||
.join('、')}`
|
||||
);
|
||||
return false;
|
||||
},
|
||||
handleTypeChange() {
|
||||
if (this.form.paymentType === 'project_advance') {
|
||||
this.form.settlementId = null;
|
||||
this.form.preSettlementId = null;
|
||||
this.form.preSettlementIds = [];
|
||||
this.form.settlementNo = '';
|
||||
this.form.preSettlementNo = '';
|
||||
this.referenceRows = [];
|
||||
}
|
||||
this.loadAttachmentRuleData([]);
|
||||
},
|
||||
clearReceiptAccount() {
|
||||
Object.assign(this.form, {
|
||||
receiptAccountId: null,
|
||||
receiptAccountName: '',
|
||||
bankName: '',
|
||||
bankAccount: '',
|
||||
});
|
||||
},
|
||||
customerRecords(response) {
|
||||
return this.unwrapData(response)?.records || [];
|
||||
},
|
||||
async loadReceiptAccountOptions(autoSelectDefault = true) {
|
||||
const payeeName = String(this.form.payeeName || '').trim();
|
||||
const preserveCurrentValue = autoSelectDefault === false;
|
||||
this.receiptAccountOptions = [];
|
||||
this.payeeCustomerLevel = '';
|
||||
if (!payeeName) {
|
||||
if (!preserveCurrentValue) this.clearReceiptAccount();
|
||||
return;
|
||||
}
|
||||
this.receiptAccountLoading = true;
|
||||
try {
|
||||
const [fullNameResponse, shortNameResponse] = await Promise.all([
|
||||
getCustomerArchiveList(1, 20, {
|
||||
fullName: payeeName,
|
||||
approvalStatus: 'approved',
|
||||
status: 1,
|
||||
}),
|
||||
getCustomerArchiveList(1, 20, {
|
||||
shortName: payeeName,
|
||||
approvalStatus: 'approved',
|
||||
status: 1,
|
||||
}),
|
||||
]);
|
||||
const customers = [
|
||||
...this.customerRecords(fullNameResponse),
|
||||
...this.customerRecords(shortNameResponse),
|
||||
];
|
||||
const customer = customers.find(
|
||||
item => item.fullName === payeeName || item.shortName === payeeName
|
||||
);
|
||||
if (!customer?.id) {
|
||||
if (!preserveCurrentValue) this.clearReceiptAccount();
|
||||
return;
|
||||
}
|
||||
const detail = this.unwrapData(await getCustomerArchiveDetail(customer.id));
|
||||
this.payeeCustomerLevel = detail.customerLevel || '';
|
||||
this.receiptAccountOptions = (detail.receiptAccounts || []).map(item => ({
|
||||
...item,
|
||||
id: String(item.id),
|
||||
}));
|
||||
const selected = this.receiptAccountOptions.find(
|
||||
item => String(item.id) === String(this.form.receiptAccountId || '')
|
||||
);
|
||||
if (selected) {
|
||||
this.applyReceiptAccount(selected);
|
||||
return;
|
||||
}
|
||||
if (preserveCurrentValue) return;
|
||||
this.clearReceiptAccount();
|
||||
if (autoSelectDefault && !this.readonly) {
|
||||
const defaultAccount =
|
||||
this.receiptAccountOptions.find(item => Number(item.isDefault) === 1) ||
|
||||
(this.receiptAccountOptions.length === 1 ? this.receiptAccountOptions[0] : null);
|
||||
if (defaultAccount) this.applyReceiptAccount(defaultAccount);
|
||||
}
|
||||
} catch (error) {
|
||||
if (!preserveCurrentValue) this.clearReceiptAccount();
|
||||
this.$message.warning('收款方的客商收款信息加载失败,请稍后重试');
|
||||
} finally {
|
||||
this.receiptAccountLoading = false;
|
||||
}
|
||||
},
|
||||
receiptAccountLabel(item) {
|
||||
return [item.accountName || item.accountHolderName, item.bankName, item.bankAccount]
|
||||
.filter(Boolean)
|
||||
.join('|');
|
||||
},
|
||||
applyReceiptAccount(account) {
|
||||
Object.assign(this.form, {
|
||||
receiptAccountId: account.id,
|
||||
receiptAccountName: account.accountName || account.accountHolderName || '',
|
||||
bankName: account.bankName || '',
|
||||
bankAccount: account.bankAccount || '',
|
||||
});
|
||||
this.$nextTick(() => this.$refs.formRef?.validateField('receiptAccountId').catch(() => {}));
|
||||
},
|
||||
handleReceiptAccountChange(id) {
|
||||
const account = this.receiptAccountOptions.find(item => String(item.id) === String(id || ''));
|
||||
if (account) {
|
||||
this.applyReceiptAccount(account);
|
||||
return;
|
||||
}
|
||||
this.clearReceiptAccount();
|
||||
},
|
||||
async handlePaymentMethodChange() {
|
||||
if (!this.billPayment) {
|
||||
@@ -624,7 +1064,10 @@ export default {
|
||||
await this.loadReferences();
|
||||
this.referenceVisible = true;
|
||||
},
|
||||
selectContract(row) {
|
||||
async selectContract(row) {
|
||||
this.referenceRows = [];
|
||||
this.clearReceiptAccount();
|
||||
const settlementType = row.settlementType || 'payable';
|
||||
Object.assign(this.form, {
|
||||
projectId: row.projectId,
|
||||
projectName: row.projectName,
|
||||
@@ -633,21 +1076,31 @@ export default {
|
||||
contractId: row.id,
|
||||
contractNo: row.contractNo,
|
||||
contractName: row.contractName,
|
||||
payerName: row.payerName,
|
||||
payeeName: row.payeeName,
|
||||
payerName:
|
||||
row.payerName || (settlementType === 'receivable' ? row.partyB : row.partyA) || '',
|
||||
payeeName:
|
||||
row.payeeName || (settlementType === 'receivable' ? row.partyA : row.partyB) || '',
|
||||
billType: '项目预付',
|
||||
});
|
||||
this.referenceVisible = false;
|
||||
await Promise.all([this.loadReceiptAccountOptions(), this.loadAttachmentRuleData([])]);
|
||||
if (this.billPayment) this.loadBillOptions();
|
||||
},
|
||||
selectFormal(row) {
|
||||
async selectFormal(row) {
|
||||
this.referenceRows = [this.createReferenceRow(row)];
|
||||
this.clearReceiptAccount();
|
||||
Object.assign(this.form, {
|
||||
settlementId: row.id,
|
||||
preSettlementId: null,
|
||||
preSettlementIds: [],
|
||||
settlementNo: row.formalSettlementNo,
|
||||
preSettlementNo: '',
|
||||
projectId: row.projectId,
|
||||
projectName: row.projectName,
|
||||
deptId: row.deptId,
|
||||
deptName: row.deptName,
|
||||
contractId: row.contractId,
|
||||
contractNo: row.contractNo,
|
||||
contractName: row.contractName,
|
||||
settlementAmount: row.settlementAmount,
|
||||
payableAmount: Math.max(
|
||||
@@ -655,19 +1108,32 @@ export default {
|
||||
Number(row.settlementAmount || 0) - Number(row.appliedPaymentAmount || 0)
|
||||
),
|
||||
billType: '正式结算单',
|
||||
payerName: row.payerName,
|
||||
payeeName: row.payeeName,
|
||||
});
|
||||
this.referenceVisible = false;
|
||||
const detail = this.unwrapData(await formalApi.getDetail(row.id));
|
||||
await Promise.all([
|
||||
this.loadReceiptAccountOptions(),
|
||||
this.loadAttachmentRuleData(detail?.id ? [detail] : []),
|
||||
]);
|
||||
if (this.billPayment) this.loadBillOptions();
|
||||
},
|
||||
selectPre(row) {
|
||||
async selectPre(row) {
|
||||
this.referenceRows = [this.createReferenceRow(row)];
|
||||
this.clearReceiptAccount();
|
||||
Object.assign(this.form, {
|
||||
preSettlementId: row.id,
|
||||
preSettlementIds: [row.id],
|
||||
settlementId: null,
|
||||
preSettlementNo: row.preSettlementNo,
|
||||
settlementNo: '',
|
||||
projectId: row.projectId,
|
||||
projectName: row.projectName,
|
||||
deptId: row.deptId,
|
||||
deptName: row.deptName,
|
||||
contractId: row.contractId,
|
||||
contractNo: row.contractNo,
|
||||
contractName: row.contractName,
|
||||
settlementAmount: row.settlementAmount,
|
||||
payableAmount: Math.max(
|
||||
@@ -675,9 +1141,15 @@ export default {
|
||||
Number(row.settlementAmount || 0) - Number(row.advanceAppliedAmount || 0)
|
||||
),
|
||||
billType: '预结算单',
|
||||
payerName: row.payerName,
|
||||
payeeName: row.payeeName,
|
||||
});
|
||||
this.referenceVisible = false;
|
||||
const detail = this.unwrapData(await preApi.getDetail(row.id));
|
||||
await Promise.all([
|
||||
this.loadReceiptAccountOptions(),
|
||||
this.loadAttachmentRuleData(detail?.id ? [detail] : []),
|
||||
]);
|
||||
if (this.billPayment) this.loadBillOptions();
|
||||
},
|
||||
addInvoice() {
|
||||
@@ -698,7 +1170,10 @@ export default {
|
||||
const time = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||
this.form.attachments = (files || []).map(file => ({
|
||||
...file,
|
||||
attachmentType: file.attachmentType || 'other',
|
||||
attachmentType:
|
||||
file.attachmentType ||
|
||||
MATERIAL_TYPE_MAP[this.resolveAttachmentMaterial(file)?.key] ||
|
||||
'other',
|
||||
description: file.description || '',
|
||||
uploadUserName: file.uploadUserName || userName,
|
||||
uploadTime: file.uploadTime || time,
|
||||
@@ -710,6 +1185,7 @@ export default {
|
||||
paymentType,
|
||||
settlementId,
|
||||
preSettlementId,
|
||||
preSettlementIds,
|
||||
projectId,
|
||||
projectName,
|
||||
deptId,
|
||||
@@ -740,6 +1216,7 @@ export default {
|
||||
paymentType,
|
||||
settlementId,
|
||||
preSettlementId,
|
||||
preSettlementIds,
|
||||
projectId,
|
||||
projectName,
|
||||
deptId,
|
||||
@@ -766,12 +1243,13 @@ export default {
|
||||
invoices,
|
||||
};
|
||||
},
|
||||
async saveDraft() {
|
||||
async saveDraft(validateMaterials = false) {
|
||||
await this.$refs.formRef.validate();
|
||||
if (this.form.paymentType === 'project_advance' && !this.form.projectId) {
|
||||
this.$message.warning('请选择所属项目');
|
||||
return false;
|
||||
}
|
||||
if (validateMaterials && !this.validateRequiredAttachments()) return false;
|
||||
try {
|
||||
this.validateInvoices();
|
||||
} catch (error) {
|
||||
@@ -784,13 +1262,14 @@ export default {
|
||||
return true;
|
||||
},
|
||||
async submitForm() {
|
||||
const saved = await this.saveDraft();
|
||||
const saved = await this.saveDraft(true);
|
||||
if (!saved) return;
|
||||
await api.submit({ id: this.form.id });
|
||||
this.$message.success('提交成功');
|
||||
this.goBack();
|
||||
},
|
||||
goBack() {
|
||||
removeSettlementTransfer(this.$route.query.transferToken);
|
||||
this.$router.push('/payment/payment-application');
|
||||
},
|
||||
formatMoney(value) {
|
||||
@@ -818,6 +1297,23 @@ export default {
|
||||
justify-content: flex-start;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.payment-form-page__missing-text {
|
||||
margin-left: 12px;
|
||||
color: var(--el-color-danger);
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.payment-form-page__attachment-checklist {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.payment-form-page__attachment-checklist-label {
|
||||
color: var(--el-text-color-regular);
|
||||
font-weight: 600;
|
||||
}
|
||||
.payment-form-page__attachment-upload .vehicle-attachment-upload {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
@@ -1056,6 +1056,7 @@ export default {
|
||||
id: row.id || undefined,
|
||||
feeType: row.feeType || '',
|
||||
feeItem: row.feeItem,
|
||||
originalAmount: Number(row.originalAmount || 0),
|
||||
adjustAmount: Number(row.adjustAmount || 0),
|
||||
remark: row.remark,
|
||||
manualFlag: row.manualFlag || 0,
|
||||
|
||||
@@ -141,7 +141,17 @@
|
||||
</section-card>
|
||||
|
||||
<section-card title="附件材料">
|
||||
<el-table :data="attachments" border>
|
||||
<div class="settlement-adjustment-editor__attachment-actions">
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="!attachments.length"
|
||||
@click="batchDownloadAttachments"
|
||||
>
|
||||
批量下载
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="attachments" border @selection-change="selectedAttachments = $event">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||
<el-table-column label="文件名" min-width="220" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
@@ -264,6 +274,7 @@ export default {
|
||||
fields: settlementAdjustmentFormFields,
|
||||
details: [],
|
||||
attachments: [],
|
||||
selectedAttachments: [],
|
||||
attachmentFileTypes: [
|
||||
'pdf',
|
||||
'bmp',
|
||||
@@ -320,6 +331,7 @@ export default {
|
||||
this.form = createSettlementAdjustmentForm();
|
||||
this.details = [];
|
||||
this.attachments = [];
|
||||
this.selectedAttachments = [];
|
||||
this.candidates = [];
|
||||
this.feeRows = [];
|
||||
if (!this.recordId) {
|
||||
@@ -347,6 +359,22 @@ export default {
|
||||
this.candidates = data?.data || data || [];
|
||||
},
|
||||
async handleFormalChange(id) {
|
||||
if (!id) {
|
||||
Object.assign(this.form, {
|
||||
formalSettlementId: null,
|
||||
formalSettlementNo: '',
|
||||
settlementType: '',
|
||||
projectName: '',
|
||||
deptName: '',
|
||||
customerName: '',
|
||||
contractNo: '',
|
||||
contractName: '',
|
||||
originalSettlementAmount: 0,
|
||||
});
|
||||
this.details = [];
|
||||
this.recalculate();
|
||||
return;
|
||||
}
|
||||
const item = this.candidates.find(row => String(row.id) === String(id));
|
||||
if (!item) return;
|
||||
const { id: formalSettlementId, ...formalSettlement } = item;
|
||||
@@ -358,6 +386,21 @@ export default {
|
||||
});
|
||||
this.details = [];
|
||||
this.recalculate();
|
||||
this.loading = true;
|
||||
try {
|
||||
const { data } = await api.getFormalDetails(formalSettlementId);
|
||||
if (String(this.form.formalSettlementId) !== String(formalSettlementId)) return;
|
||||
this.details = (data?.data || data || []).map(fee => ({
|
||||
...fee,
|
||||
originalAmountTax: Number(fee.originalAmountTax || 0),
|
||||
adjustmentAmountTax: 0,
|
||||
adjustmentAmountNoTax: null,
|
||||
remark: '',
|
||||
}));
|
||||
this.recalculate();
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
async openFeeDialog() {
|
||||
const { data } = await api.getFormalDetails(this.form.formalSettlementId);
|
||||
@@ -432,6 +475,7 @@ export default {
|
||||
uploadUserName: file.uploadUserName || uploadUserName,
|
||||
uploadTime: file.uploadTime || uploadTime,
|
||||
}));
|
||||
this.selectedAttachments = [];
|
||||
},
|
||||
parseAttachments(value) {
|
||||
if (!value) return [];
|
||||
@@ -467,8 +511,22 @@ export default {
|
||||
}
|
||||
downloadFileByUrl(url, this.attachmentName(file));
|
||||
},
|
||||
batchDownloadAttachments() {
|
||||
const files = this.selectedAttachments.length ? this.selectedAttachments : this.attachments;
|
||||
const downloadableFiles = files.filter(file => this.attachmentUrl(file));
|
||||
if (!downloadableFiles.length) {
|
||||
this.$message.warning(
|
||||
this.selectedAttachments.length ? '所选附件暂无可下载地址' : '暂无可下载附件'
|
||||
);
|
||||
return;
|
||||
}
|
||||
downloadableFiles.forEach((file, index) => {
|
||||
window.setTimeout(() => this.downloadAttachment(file), index * 200);
|
||||
});
|
||||
},
|
||||
removeAttachment(index) {
|
||||
this.attachments.splice(index, 1);
|
||||
const [removed] = this.attachments.splice(index, 1);
|
||||
this.selectedAttachments = this.selectedAttachments.filter(file => file !== removed);
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -484,6 +542,11 @@ export default {
|
||||
padding: 12px 0;
|
||||
text-align: right;
|
||||
}
|
||||
.settlement-adjustment-editor__attachment-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.settlement-adjustment-editor__attachment-upload {
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@
|
||||
v-if="hasPermission('pre_settlement_advance')"
|
||||
type="primary"
|
||||
plain
|
||||
disabled
|
||||
@click="openAdvanceDialog"
|
||||
:disabled="!selection.length"
|
||||
@click="handleAdvanceApplication"
|
||||
>
|
||||
预付申请
|
||||
</el-button>
|
||||
@@ -215,52 +215,6 @@
|
||||
@success="loadTable"
|
||||
/>
|
||||
|
||||
<el-dialog v-model="advanceDialog.visible" title="预付申请" width="520px" append-to-body>
|
||||
<el-form
|
||||
ref="advanceFormRef"
|
||||
:model="advanceForm"
|
||||
:rules="advanceRules"
|
||||
label-position="right"
|
||||
label-width="auto"
|
||||
>
|
||||
<el-form-item label="预结算单号">
|
||||
<span>{{ advanceDialog.row.preSettlementNo || '-' }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="结算金额">
|
||||
<span>
|
||||
{{ formatMoney(advanceDialog.row.settlementAmount, advanceDialog.row.currency) }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="已申请预付金额">
|
||||
<span>
|
||||
{{ formatMoney(advanceDialog.row.advanceAppliedAmount, advanceDialog.row.currency) }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="申请预付金额" prop="appliedAmount">
|
||||
<el-input-number
|
||||
v-model="advanceForm.appliedAmount"
|
||||
:min="0"
|
||||
:max="advanceAvailableAmount"
|
||||
:precision="2"
|
||||
:controls="false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="金蝶预付单号">
|
||||
<el-input
|
||||
v-model="advanceForm.kingdeeAdvanceNo"
|
||||
maxlength="100"
|
||||
placeholder="外部系统回写时可填写"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="advanceDialog.visible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="advanceDialog.submitting" @click="submitAdvance">
|
||||
提交
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="printDialog.visible"
|
||||
title="打印"
|
||||
@@ -316,7 +270,6 @@
|
||||
import { ArrowDown, ArrowUp, Refresh } from '@element-plus/icons-vue';
|
||||
import { mapGetters } from 'vuex';
|
||||
import {
|
||||
applyAdvance,
|
||||
approve,
|
||||
exportList,
|
||||
getDetail,
|
||||
@@ -370,34 +323,6 @@ export default {
|
||||
id: '',
|
||||
readonly: false,
|
||||
},
|
||||
advanceDialog: {
|
||||
visible: false,
|
||||
submitting: false,
|
||||
row: {},
|
||||
},
|
||||
advanceForm: {
|
||||
appliedAmount: null,
|
||||
kingdeeAdvanceNo: '',
|
||||
},
|
||||
advanceRules: {
|
||||
appliedAmount: [
|
||||
{ required: true, message: '请输入申请预付金额', trigger: 'blur' },
|
||||
{
|
||||
validator: (_rule, value, callback) => {
|
||||
if (!value || Number(value) <= 0) {
|
||||
callback(new Error('申请预付金额必须大于0'));
|
||||
return;
|
||||
}
|
||||
if (Number(value) > this.advanceAvailableAmount) {
|
||||
callback(new Error('申请预付金额不能超过剩余可申请金额'));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
},
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
],
|
||||
},
|
||||
printDialog: {
|
||||
visible: false,
|
||||
loading: false,
|
||||
@@ -422,13 +347,6 @@ export default {
|
||||
visibleSearchFields() {
|
||||
return this.searchExpanded ? this.searchFields : this.searchFields.slice(0, 4);
|
||||
},
|
||||
advanceAvailableAmount() {
|
||||
return Math.max(
|
||||
Number(this.advanceDialog.row.settlementAmount || 0) -
|
||||
Number(this.advanceDialog.row.advanceAppliedAmount || 0),
|
||||
0
|
||||
);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'$route.query.detailNo'(detailNo) {
|
||||
@@ -525,37 +443,49 @@ export default {
|
||||
}
|
||||
return this.selection[0];
|
||||
},
|
||||
openAdvanceDialog() {
|
||||
const row = this.selectedOne('预付申请');
|
||||
if (!row) return;
|
||||
handleAdvanceApplication() {
|
||||
if (!this.selection.length) {
|
||||
this.$message.warning('请选择需要发起预付申请的预结算单');
|
||||
return;
|
||||
}
|
||||
if (this.selection.some(row => !row.contractId)) {
|
||||
this.$message.warning('所选预结算单缺少合同ID,无法发起预付申请');
|
||||
return;
|
||||
}
|
||||
const contractIds = new Set(this.selection.map(row => String(row.contractId)));
|
||||
if (contractIds.size > 1) {
|
||||
this.$message.warning('预付申请只能选择同一合同下的预结算单');
|
||||
return;
|
||||
}
|
||||
if (
|
||||
this.selection.some(
|
||||
row =>
|
||||
row.approvalStatus !== 'approved' ||
|
||||
row.settlementType !== 'payable' ||
|
||||
row.formalSettlementNo
|
||||
)
|
||||
) {
|
||||
this.$message.warning('仅审批通过、未转正式结算的应付预结算单可发起预付');
|
||||
return;
|
||||
}
|
||||
this.advanceDialog.row = row;
|
||||
this.advanceDialog.visible = true;
|
||||
this.advanceForm = { appliedAmount: null, kingdeeAdvanceNo: '' };
|
||||
this.$nextTick(() => this.$refs.advanceFormRef?.clearValidate());
|
||||
},
|
||||
async submitAdvance() {
|
||||
await this.$refs.advanceFormRef?.validate();
|
||||
this.advanceDialog.submitting = true;
|
||||
try {
|
||||
await applyAdvance({
|
||||
preSettlementId: this.advanceDialog.row.id,
|
||||
appliedAmount: this.advanceForm.appliedAmount,
|
||||
kingdeeAdvanceNo: this.advanceForm.kingdeeAdvanceNo,
|
||||
const sourcePreSettlements = this.selection.map(row => ({
|
||||
...row,
|
||||
preSettlementId: row.preSettlementId || row.id,
|
||||
}));
|
||||
const transferToken = createSettlementTransfer({
|
||||
sourcePreSettlements,
|
||||
});
|
||||
this.$router.push({
|
||||
path: '/payment/payment-application/form',
|
||||
query: {
|
||||
mode: 'add',
|
||||
transferToken,
|
||||
preSettlementIds: sourcePreSettlements
|
||||
.map(row => row.preSettlementId)
|
||||
.filter(Boolean)
|
||||
.join(','),
|
||||
},
|
||||
});
|
||||
this.$message.success('预付申请提交成功');
|
||||
this.advanceDialog.visible = false;
|
||||
this.loadTable();
|
||||
} finally {
|
||||
this.advanceDialog.submitting = false;
|
||||
}
|
||||
},
|
||||
handleFormalSettlement() {
|
||||
if (!this.selection.length) {
|
||||
|
||||
@@ -170,7 +170,7 @@
|
||||
:disabled="readonly"
|
||||
class="archive-form dialog-form-label-fixed"
|
||||
>
|
||||
<section-card title="工商信息">
|
||||
<section-card title="基础信息">
|
||||
<el-row :gutter="18">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="客商编号" prop="customerCode">
|
||||
|
||||
Reference in New Issue
Block a user