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
+17 -2
View File
@@ -399,8 +399,17 @@ export default {
if (submitRow.cargoValue !== undefined && submitRow.cargoValue !== null) {
submitRow.cargoValue = String(submitRow.cargoValue).trim();
}
if (!submitRow.cargoValue || String(submitRow.cargoValue) === '-1') {
submitRow.cargoValue = null;
}
return submitRow;
},
normalizeCargoValue(row) {
if (row && String(row.cargoValue) === '-1') {
row.cargoValue = null;
}
return row;
},
validateRow(row) {
if (!row.firstCargoTypeName || !row.firstCargoTypeCode) {
this.$message.warning('请选择一级货物类型');
@@ -539,7 +548,7 @@ export default {
}
if (['edit', 'view'].includes(type)) {
api.getDetail(this.form.id).then(res => {
this.form = res.data.data || {};
this.form = this.normalizeCargoValue(res.data.data || {});
this.ensureCargoTypeOption('firstCargoTypeOptions', {
cargoName: this.form.firstCargoTypeName,
cargoCode: this.form.firstCargoTypeCode,
@@ -593,7 +602,7 @@ export default {
.then(res => {
const result = res.data.data;
this.page.total = result.total;
this.data = result.records;
this.data = (result.records || []).map(row => this.normalizeCargoValue(row));
this.selectionClear();
})
.finally(() => {
@@ -654,6 +663,12 @@ export default {
<style lang="scss" scoped>
.common-cargo-page {
// 仅缩短本页面筛选栏中的输入框和下拉框,避免影响表单及其他页面。
:deep(.avue-crud__search .el-form-item__content > .el-input),
:deep(.avue-crud__search .el-form-item__content > .el-select) {
width: 70%;
}
&__value-unit {
display: flex;
align-items: center;
File diff suppressed because it is too large Load Diff
@@ -2105,7 +2105,7 @@
</template>
<template #menu="{ row, index }">
<template>
<div class="business-crud-page__row-actions">
<el-link type="primary" v-if="showDetailButton(row)" @click="openDetail(row)"
>详情</el-link
>
@@ -2144,7 +2144,7 @@
{{ operation.label }}
</el-link>
<el-link type="danger" v-if="canDelete(row)" @click="rowDel(row)"> 删除 </el-link>
</template>
</div>
</template>
</component>
@@ -13281,6 +13281,13 @@ export default {
}
}
&__row-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
}
:deep(.business-crud-page__full-form-item) {
width: 100%;
max-width: 100%;
@@ -1705,6 +1705,10 @@ export default {
departureAddress: this.form.arrivalAddress,
departureContact: this.form.arrivalContact,
departurePhone: this.form.arrivalPhone,
departureLongitude: this.form.arrivalLongitude,
departureLatitude: this.form.arrivalLatitude,
departureRegionCode: this.form.arrivalRegionCode,
departureSiteCode: this.form.arrivalSiteCode,
transportType: this.form.finalTransportType,
},
],
+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) {
+1 -1
View File
@@ -2175,7 +2175,7 @@ export default {
if (draftMode) {
return true;
}
if (this.dialogMode === 'add' && this.selectedWaybillRows.length < 2) {
if (this.selectedWaybillRows.length < 2) {
ElMessage.warning('请至少选择两条待配载运单');
return false;
}
+26 -6
View File
@@ -797,11 +797,25 @@ export default {
result.push(
`是否接单:${node.confirmMode === 'no_confirm_accept' ? '无需确认接单' : '是'}`
);
result.push(
`确认接单人(司机):${node.confirmMode === 'yes' && node.confirmDriver ? '是' : '否'}`
);
return result;
}
if (node.key === 'return') {
if (node.uploadVoucher && node.voucherTypes.length) {
result.push(`上传凭证:${node.voucherTypes.join('、')}`);
result.push(
`是否确认:${node.confirmMode === 'no_confirm_complete' ? '无需确认完成' : '是'}`
);
result.push(
`确认完成(内部人员):${
node.confirmMode === 'yes' && node.confirmInternal ? '是' : '否'
}`
);
if (node.supportVoucher) {
result.push(`上传凭证:${node.uploadVoucher ? '是' : '否'}`);
if (node.uploadVoucher && node.voucherTypes.length) {
result.push(`凭证类型:${node.voucherTypes.join('、')}`);
}
}
return result;
}
@@ -816,11 +830,17 @@ export default {
result.push(`打卡操作:${node.punch ? '是' : '否'}`);
result.push(`打卡定位:${node.location ? '是' : '否'}`);
}
if (node.supportCargo && node.uploadCargo && node.cargoTypes.length) {
result.push(`上传货量:${node.cargoTypes.join('')}`);
if (node.supportCargo) {
result.push(`上传货量:${node.uploadCargo ? '是' : ''}`);
if (node.uploadCargo && node.cargoTypes.length) {
result.push(`上传货量类型:${node.cargoTypes.join('、')}`);
}
}
if (node.supportVoucher && node.uploadVoucher && node.voucherTypes.length) {
result.push(`上传凭证:${node.voucherTypes.join('')}`);
if (node.supportVoucher) {
result.push(`上传凭证:${node.uploadVoucher ? '是' : ''}`);
if (node.uploadVoucher && node.voucherTypes.length) {
result.push(`凭证类型:${node.voucherTypes.join('、')}`);
}
}
return result;
},