This commit is contained in:
2026-08-25 00:10:45 +08:00
parent e1bade226f
commit 1ef050dda6
35 changed files with 2041 additions and 263 deletions
+326 -46
View File
@@ -580,29 +580,205 @@
title="合同详情"
append-to-body
destroy-on-close
width="1200px"
class="contract-manage-detail-dialog"
width="92%"
top="4vh"
>
<div v-loading="detailLoading" class="contract-manage-detail">
<section v-for="section in config.detailSections" :key="section.title">
<div class="dialog-section-title">{{ section.title }}</div>
<el-descriptions :column="3" border>
<el-descriptions-item
v-for="field in section.fields"
:key="field[0]"
:label="field[1]"
:span="field[2] || 1"
>
{{ detailValue(field[0]) }}
</el-descriptions-item>
<div v-loading="detailLoading" class="contract-manage-form contract-manage-detail">
<section class="contract-manage-form__section contract-manage-form__section--panel">
<div class="dialog-section-title">基本信息</div>
<el-descriptions :column="3" class="contract-manage-detail__descriptions">
<el-descriptions-item label="签约类型">{{
detailValue('signType')
}}</el-descriptions-item>
<el-descriptions-item label="合同编号">{{
detailValue('contractNo')
}}</el-descriptions-item>
<el-descriptions-item label="合同名称">{{
detailValue('contractName')
}}</el-descriptions-item>
<el-descriptions-item label="合同类型">{{
detailValue('contractCategory')
}}</el-descriptions-item>
<el-descriptions-item label="项目">{{
detailValue('projectName')
}}</el-descriptions-item>
<el-descriptions-item label="甲方">{{ detailValue('partyA') }}</el-descriptions-item>
<el-descriptions-item label="乙方">{{ detailValue('partyB') }}</el-descriptions-item>
<el-descriptions-item label="合同期限">{{ detailContractPeriod }}</el-descriptions-item>
<el-descriptions-item label="所属组织">{{
detailValue('organizationName')
}}</el-descriptions-item>
<el-descriptions-item label="经办人">{{
detailValue('handlerUserName')
}}</el-descriptions-item>
<el-descriptions-item label="签订日期">{{
detailValue('signDate')
}}</el-descriptions-item>
<el-descriptions-item label="结算类型">{{
detailValue('settlementMode')
}}</el-descriptions-item>
<el-descriptions-item label="合同格式">{{
detailValue('contractFormat')
}}</el-descriptions-item>
<el-descriptions-item label="是否需要加盖法人章">{{
detailLegalSealText
}}</el-descriptions-item>
<el-descriptions-item label="一式">{{
detailUnitValue('copyCount', '份')
}}</el-descriptions-item>
<el-descriptions-item label="回款账期">{{
detailUnitValue('paymentDays', '天')
}}</el-descriptions-item>
<el-descriptions-item label="备注" :span="3">{{
detailValue('remark')
}}</el-descriptions-item>
</el-descriptions>
</section>
<attachment-section
title="合同文件"
readonly
:rows="detailContractFileRows"
:preview="previewAttachment"
/>
<section class="contract-manage-form__section contract-manage-form__section--panel">
<div class="dialog-section-title">计费信息</div>
<el-descriptions :column="3" class="contract-manage-detail__descriptions">
<el-descriptions-item label="费用生成模式">{{
detailFeeGenerationMode
}}</el-descriptions-item>
</el-descriptions>
<el-table :data="detailBillingPlanRows" border>
<el-table-column type="index" label="序号" width="80" align="center" />
<el-table-column prop="planName" label="方案名称" min-width="220" align="center" />
<el-table-column label="默认方案" width="140" align="center">
<template #default="{ row }">{{ isDefaultBillingPlan(row) ? '是' : '' }}</template>
</el-table-column>
<el-table-column
prop="remark"
label="备注"
min-width="260"
align="center"
show-overflow-tooltip
/>
<el-table-column label="操作" width="120" align="center" fixed="right">
<template #default="{ row, $index }">
<el-link type="primary" @click="openDetailBillingPlan(row, $index)"
>查看详情</el-link
>
</template>
</el-table-column>
</el-table>
</section>
<section class="contract-manage-form__section contract-manage-form__section--panel">
<div class="dialog-section-title">结算单规则</div>
<el-tabs v-model="detailSettlementConfigTab">
<el-tab-pane label="预结算配置" name="pre" />
<el-tab-pane label="正式结算配置" name="formal" />
</el-tabs>
<el-descriptions :column="3" class="contract-manage-detail__descriptions">
<el-descriptions-item label="自动生成结算单">{{
Number(detailSettlementRule.autoGenerate) === 1 ? '开启' : '关闭'
}}</el-descriptions-item>
<template v-if="Number(detailSettlementRule.autoGenerate) === 1">
<el-descriptions-item label="账单起始日期">{{
displayValue(detailSettlementRule.billStartDate)
}}</el-descriptions-item>
<el-descriptions-item label="结算类型">{{
displayValue(detailSettlementRule.settlementType)
}}</el-descriptions-item>
<el-descriptions-item
v-if="detailSettlementRule.settlementType === '月结'"
label="账单周期类型"
>{{ displayValue(detailSettlementRule.billCycleType) }}</el-descriptions-item
>
<el-descriptions-item
v-if="
detailSettlementRule.settlementType === '月结' &&
detailSettlementRule.billCycleType === '固定截单日'
"
label="账单截单日"
>{{
detailObjectUnitValue(detailSettlementRule, 'billCutoffDay', '日')
}}</el-descriptions-item
>
<el-descriptions-item
v-if="detailSettlementRule.settlementType === '固定天数周期结算'"
label="周期天数"
>{{
detailObjectUnitValue(detailSettlementRule, 'cycleDays', '天')
}}</el-descriptions-item
>
</template>
</el-descriptions>
</section>
<section class="contract-manage-form__section contract-manage-form__section--panel">
<div class="dialog-section-title">付款比例设置</div>
<el-table :data="detailPaymentRatioRows" border>
<el-table-column type="index" label="序号" width="80" align="center" />
<el-table-column prop="paymentTerm" label="付款笔数" min-width="180" align="center" />
<el-table-column label="付款比例上限" min-width="220" align="center">
<template #default="{ row }">{{
detailObjectUnitValue(row, 'ratioLimit', '%')
}}</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="220" />
</el-table>
</section>
<attachment-section
title="其它附件"
description
readonly
:rows="detailAttachmentRows"
:preview="previewAttachment"
/>
<section class="contract-manage-form__section contract-manage-form__section--panel">
<div class="dialog-section-title">变更记录</div>
<el-table :data="detailChangeRecordRows" border>
<el-table-column type="index" label="序号" width="70" align="center" />
<el-table-column
prop="changeDate"
label="变更日期"
min-width="150"
align="center"
sortable
/>
<el-table-column prop="handlerUserName" label="经办人" min-width="140" align="center" />
<el-table-column prop="changeType" label="变更类型" min-width="160" align="center" />
<el-table-column
prop="changeReason"
label="变更原因"
min-width="240"
align="center"
show-overflow-tooltip
/>
<el-table-column prop="statusName" label="状态" min-width="140" align="center" />
<el-table-column label="操作" width="120" align="center" fixed="right">
<template #default="{ row }"
><el-link type="primary" @click="openFlow(row)">流程</el-link></template
>
</el-table-column>
</el-table>
</section>
</div>
<template #footer
><el-button type="primary" @click="detailBox = false">关闭</el-button></template
>
</el-dialog>
<billing-plan-editor
v-model="detailBillingPlanBox"
:value="detailBillingPlanForm"
:index="detailBillingPlanIndex"
readonly
/>
<flow-design
v-if="website.design.designMode"
is-dialog
@@ -743,6 +919,7 @@ const AttachmentSection = defineComponent({
title: { type: String, required: true },
rows: { type: Array, default: () => [] },
description: Boolean,
readonly: Boolean,
preview: { type: Function, default: null },
},
emits: ['update:rows'],
@@ -797,7 +974,9 @@ const AttachmentSection = defineComponent({
const ElTableColumn = resolveComponent('el-table-column');
const VehicleAttachmentUpload = resolveComponent('vehicle-attachment-upload');
const columns = [
h(ElTableColumn, { type: 'selection', width: 55, align: 'center' }),
...(!this.readonly
? [h(ElTableColumn, { type: 'selection', width: 55, align: 'center' })]
: []),
h(ElTableColumn, { type: 'index', label: '序号', width: 70, align: 'center' }),
h(
ElTableColumn,
@@ -828,14 +1007,16 @@ const AttachmentSection = defineComponent({
{ label: '附件描述', minWidth: 220 },
{
default: ({ row }) =>
h(ElInput, {
modelValue: row.description,
maxlength: 200,
'onUpdate:modelValue': value => {
row.description = value;
this.update([...this.rows]);
},
}),
this.readonly
? row.description || '-'
: h(ElInput, {
modelValue: row.description,
maxlength: 200,
'onUpdate:modelValue': value => {
row.description = value;
this.update([...this.rows]);
},
}),
}
)
);
@@ -855,16 +1036,20 @@ const AttachmentSection = defineComponent({
width: 180,
align: 'center',
sortable: true,
}),
h(
ElTableColumn,
{ label: '操作', width: 100, align: 'center', fixed: 'right' },
{
default: ({ $index }) =>
h(ElLink, { type: 'danger', onClick: () => this.remove($index) }, () => '删除'),
}
)
})
);
if (!this.readonly) {
columns.push(
h(
ElTableColumn,
{ label: '操作', width: 100, align: 'center', fixed: 'right' },
{
default: ({ $index }) =>
h(ElLink, { type: 'danger', onClick: () => this.remove($index) }, () => '删除'),
}
)
);
}
return h(
'section',
{
@@ -874,16 +1059,20 @@ const AttachmentSection = defineComponent({
h('div', { class: 'dialog-section-title' }, this.title),
h('div', { class: 'contract-manage-form__attachment-head' }, [
h('div', { class: 'contract-manage-form__attachment-actions' }, [
h(VehicleAttachmentUpload, {
modelValue: this.rows,
fileTypes: this.attachmentFileTypes,
maxSize: 500,
showTip: false,
showFileList: false,
buttonText: '上传附件',
'onUpdate:modelValue': this.handleChange,
onChange: this.handleChange,
}),
...(!this.readonly
? [
h(VehicleAttachmentUpload, {
modelValue: this.rows,
fileTypes: this.attachmentFileTypes,
maxSize: 500,
showTip: false,
showFileList: false,
buttonText: '上传附件',
'onUpdate:modelValue': this.handleChange,
onChange: this.handleChange,
}),
]
: []),
h(
ElButton,
{ type: 'primary', disabled: !this.rows.length, onClick: this.batchDownload },
@@ -990,6 +1179,17 @@ export default {
detailBox: false,
detailLoading: false,
detailRow: {},
detailContractFileRows: [],
detailAttachmentRows: [],
detailBillingPlanRows: [],
detailBillingPlanBox: false,
detailBillingPlanIndex: -1,
detailBillingPlanForm: {},
detailSettlementConfigTab: 'pre',
detailPreSettlementRuleForm: defaultSettlementRule(),
detailFormalSettlementRuleForm: defaultSettlementRule(),
detailPaymentRatioRows: [],
detailChangeRecordRows: [],
flowBox: false,
flowUrl: '',
processInstanceId: '',
@@ -1069,6 +1269,27 @@ export default {
value: index + 1,
}));
},
detailContractPeriod() {
const startDate = this.detailRow.startDate || '-';
const endDate = this.detailRow.endDate || '-';
return `${startDate} 至 ${endDate}`;
},
detailLegalSealText() {
const value = this.detailRow.legalSealFlag;
if (value === undefined || value === null || value === '') return '-';
return Number(value) === 1 ? '是' : '否';
},
detailFeeGenerationMode() {
const value =
this.detailRow.feeGenerationMode ||
(Number(this.detailRow.billingEnabled) === 0 ? 'manual' : 'system');
return value === 'manual' ? '手动生成' : '系统生成';
},
detailSettlementRule() {
return this.detailSettlementConfigTab === 'formal'
? this.detailFormalSettlementRuleForm
: this.detailPreSettlementRuleForm;
},
},
watch: {
'$route.fullPath'() {
@@ -1718,16 +1939,55 @@ export default {
openDetail(row) {
this.detailBox = true;
this.detailLoading = true;
this.detailRow = { ...row };
this.applyDetailState(row);
this.api
.getDetail(row.id)
.then(res => {
this.detailRow = res.data?.data || row;
this.applyDetailState(res.data?.data || row);
})
.finally(() => {
this.detailLoading = false;
});
},
applyDetailState(detail = {}) {
this.detailRow = { ...detail };
this.detailContractFileRows = parseArray(detail.contractFileJson);
this.detailAttachmentRows = parseArray(detail.attachmentsJson);
this.detailBillingPlanRows = parseArray(detail.billingPlanJson);
this.detailPaymentRatioRows = parseArray(detail.paymentRatioJson);
this.detailChangeRecordRows = parseArray(detail.changeRecordJson);
const payload = parseObject(detail.settlementRuleJson);
const legacy = Object.keys(payload).some(
key => !['preSettlementConfig', 'formalSettlementConfig'].includes(key)
)
? payload
: {};
const preConfig = parseObject(detail.preSettlementConfigJson);
const formalConfig = parseObject(detail.formalSettlementConfigJson);
this.detailPreSettlementRuleForm = this.normalizeSettlementRule(
payload.preSettlementConfig || (Object.keys(preConfig).length ? preConfig : legacy)
);
this.detailFormalSettlementRuleForm = this.normalizeSettlementRule(
payload.formalSettlementConfig || (Object.keys(formalConfig).length ? formalConfig : legacy)
);
this.detailSettlementConfigTab = 'pre';
},
openDetailBillingPlan(row, index) {
this.detailBillingPlanForm = clone(row);
this.detailBillingPlanIndex = index;
this.detailBillingPlanBox = true;
},
displayValue(value) {
return value === undefined || value === null || value === '' ? '-' : value;
},
detailUnitValue(prop, unit) {
const value = this.detailRow[prop];
return value === undefined || value === null || value === '' ? '-' : `${value}${unit}`;
},
detailObjectUnitValue(row, prop, unit) {
const value = row?.[prop];
return value === undefined || value === null || value === '' ? '-' : `${value}${unit}`;
},
detailValue(prop) {
const value = this.detailRow[prop];
if (
@@ -1743,7 +2003,7 @@ export default {
if (prop === 'settlementRuleJson') return value ? '已配置' : '-';
if (prop === 'paymentRatioJson') return `${parseArray(value).length}项付款比例`;
if (prop === 'feeGenerationMode') return value === 'manual' ? '手动生成' : '系统生成';
return value === undefined || value === null || value === '' ? '-' : value;
return this.displayValue(value);
},
handleOperation(operation, row) {
if (operation.action === 'startChange') {
@@ -2107,8 +2367,28 @@ export default {
display: inline-flex;
}
.contract-manage-detail section + section {
margin-top: 20px;
.contract-manage-detail {
max-height: 74vh;
overflow-y: auto;
&__descriptions {
:deep(.el-descriptions__label) {
display: inline-block;
min-width: 96px;
padding-right: 12px;
color: #606266;
text-align: right;
}
:deep(.el-descriptions__content) {
color: #303133;
word-break: break-word;
}
:deep(.el-descriptions__cell) {
padding-bottom: 16px;
}
}
}
@media (max-width: 1200px) {