Compare commits
2 Commits
3a8442e522
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 6212d68a77 | |||
| ce1ce02551 |
@@ -24,7 +24,7 @@ export const syncKingdeeBatch = ids =>
|
||||
export const paymentTypeOptions = [
|
||||
{ label: '项目预付', value: 'project_advance' },
|
||||
{ label: '进度预付', value: 'progress_advance' },
|
||||
{ label: '结算付款', value: 'settlement_payment' },
|
||||
{ label: '尾款付款', value: 'settlement_payment' },
|
||||
];
|
||||
export const approvalStatusOptions = [
|
||||
{ label: '草稿', value: 'draft' },
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:model-value="modelValue"
|
||||
title="变更记录详情"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
align-center
|
||||
width="1100px"
|
||||
class="change-record-detail-dialog"
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
@open="resetPage"
|
||||
>
|
||||
<el-table
|
||||
:data="pageRows"
|
||||
border
|
||||
max-height="60vh"
|
||||
:show-overflow-tooltip="false"
|
||||
>
|
||||
<el-table-column prop="field" label="变更字段" min-width="180" />
|
||||
<el-table-column
|
||||
prop="before"
|
||||
label="变更前"
|
||||
min-width="360"
|
||||
class-name="change-record-detail-value"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="after"
|
||||
label="变更后"
|
||||
min-width="500"
|
||||
class-name="change-record-detail-value"
|
||||
/>
|
||||
</el-table>
|
||||
<el-empty v-if="!total" description="暂无变更内容" :image-size="60" />
|
||||
<div class="change-record-detail-dialog__pager">
|
||||
<el-pagination
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
background
|
||||
:total="total"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
/>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="$emit('update:modelValue', false)">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ChangeRecordDetailDialog',
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rows: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
data() {
|
||||
return {
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
total() {
|
||||
return this.rows.length;
|
||||
},
|
||||
pageRows() {
|
||||
const start = (this.currentPage - 1) * this.pageSize;
|
||||
return this.rows.slice(start, start + this.pageSize);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
rows() {
|
||||
const maxPage = Math.max(1, Math.ceil(this.total / this.pageSize) || 1);
|
||||
if (this.currentPage > maxPage) this.currentPage = 1;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
resetPage() {
|
||||
this.currentPage = 1;
|
||||
this.pageSize = 10;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.change-record-detail-dialog__pager {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
:deep(.change-record-detail-value .cell) {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,17 @@
|
||||
<template>
|
||||
<div v-loading="loading" class="master-detail">
|
||||
<section v-if="master" class="detail-overview">
|
||||
<div class="detail-heading"><div><h2>多联总单详情 <span>|</span> {{ master.masterNo }}</h2><el-tag :type="statusType(master.businessStatus)" class="status-text">{{ statusName(master.businessStatus) }}</el-tag><el-tag v-if="transportFlowLabel" type="info" class="transport-flow-tag">{{ transportFlowLabel }}</el-tag></div></div>
|
||||
<div class="detail-heading">
|
||||
<div>
|
||||
<h2>多联总单详情 <span>|</span> {{ master.masterNo }}</h2>
|
||||
<el-tag :type="statusType(master.businessStatus)">{{
|
||||
statusName(master.businessStatus)
|
||||
}}</el-tag>
|
||||
<el-tag v-if="transportFlowLabel" type="primary" class="transport-flow-tag">{{
|
||||
transportFlowLabel
|
||||
}}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<dl class="detail-meta">
|
||||
<div>
|
||||
<dt>客户</dt>
|
||||
@@ -132,6 +142,7 @@ export default {
|
||||
dateRange() { return this.master?.planStartTime && this.master?.planEndTime ? `${String(this.master.planStartTime).slice(0, 10)} ~ ${String(this.master.planEndTime).slice(0, 10)}` : '-'; },
|
||||
},
|
||||
async mounted() {
|
||||
if (!this.id) return;
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await api.getDetail(this.id);
|
||||
@@ -282,8 +293,36 @@ export default {
|
||||
<style scoped lang="scss">
|
||||
.master-detail { padding-bottom: 20px; color: #303133; }
|
||||
.detail-overview, .execution-detail-card { margin-bottom: 8px; border: 1px solid #eff1f7; background: #fff; }
|
||||
.detail-heading { display: flex; align-items: center; justify-content: space-between; padding: 18px 24px; h2 { display: inline-block; margin: 0 16px 0 0; font-size: 20px; } h2 span { margin: 0 8px; color: #909399; font-weight: 400; } }
|
||||
.transport-flow-tag { margin-left: 8px; }
|
||||
.detail-heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 18px 24px;
|
||||
|
||||
h2 {
|
||||
display: inline-block;
|
||||
margin: 0 16px 0 0;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
h2 span {
|
||||
margin: 0 8px;
|
||||
color: #909399;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
:deep(.el-tag) {
|
||||
height: 28px;
|
||||
padding: 0 12px;
|
||||
border: 0;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
line-height: 28px;
|
||||
}
|
||||
}
|
||||
.transport-flow-tag {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.detail-meta { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 18px 24px; margin: 0; padding: 0 24px 20px; dt { margin-bottom: 6px; color: #909399; font-size: 13px; } dd { display: flex; flex-wrap: wrap; gap: 8px 16px; margin: 0; color: #303133; font-size: 14px; word-break: break-all; } :deep(.el-link) { font-size: 14px; } }
|
||||
.route-map { display: flex; align-items: flex-start; padding: 16px 24px 20px; border-top: 1px solid #eff1f7; overflow-x: auto; }
|
||||
.route-map__node { display: grid; flex: 0 0 150px; justify-items: center; gap: 6px; text-align: center; strong { font-size: 16px; white-space: nowrap; } small { color: #606266; white-space: nowrap; } }
|
||||
|
||||
@@ -295,6 +295,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async load() {
|
||||
if (!this.id) return;
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await api.getDetail(this.id);
|
||||
|
||||
@@ -113,6 +113,12 @@
|
||||
label="导入方式"
|
||||
width="100"
|
||||
/>
|
||||
<el-table-column
|
||||
v-if="columnVisible.statusName"
|
||||
prop="statusName"
|
||||
label="状态"
|
||||
width="100"
|
||||
/>
|
||||
<el-table-column
|
||||
v-if="columnVisible.createUserName"
|
||||
prop="createUserName"
|
||||
@@ -121,12 +127,6 @@
|
||||
>
|
||||
<template #default="{ row }">{{ row.createUserName || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-if="columnVisible.statusName"
|
||||
prop="statusName"
|
||||
label="状态"
|
||||
width="100"
|
||||
/>
|
||||
<el-table-column
|
||||
v-if="columnVisible.createTime"
|
||||
prop="createTime"
|
||||
@@ -621,8 +621,8 @@ const columnOptions = [
|
||||
{ prop: 'carrierType', label: '承运类型' },
|
||||
{ prop: 'waybillCount', label: '运单数' },
|
||||
{ prop: 'importTypeName', label: '导入方式' },
|
||||
{ prop: 'createUserName', label: '创建人' },
|
||||
{ prop: 'statusName', label: '状态' },
|
||||
{ prop: 'createUserName', label: '创建人' },
|
||||
{ prop: 'createTime', label: '创建时间' },
|
||||
{ prop: 'updateTime', label: '更新时间' },
|
||||
];
|
||||
|
||||
@@ -1774,12 +1774,17 @@
|
||||
<div class="waybill-manage-page__waybill-heading">
|
||||
<strong>运单详情</strong>
|
||||
<span>{{ detailRow.waybillNo || '-' }}</span>
|
||||
<span class="detail-status-text status-text-success">{{
|
||||
displayStatus(detailRow, 'businessStatus')
|
||||
}}</span>
|
||||
<span class="detail-status-text detail-transport-type">
|
||||
{{ getTransportTypeLabel(waybillTransportMode(detailRow)) || '-' }}
|
||||
</span>
|
||||
<el-tag :type="statusTagType(detailRow.businessStatus)">
|
||||
{{ displayStatus(detailRow, 'businessStatus') }}
|
||||
</el-tag>
|
||||
<el-tag type="primary">
|
||||
{{
|
||||
detailRow.transportTypeName ||
|
||||
getTransportTypeLabel(detailRow.transportType) ||
|
||||
waybillTransportMode(detailRow) ||
|
||||
'-'
|
||||
}}
|
||||
</el-tag>
|
||||
<el-link
|
||||
v-if="detailRow.id"
|
||||
class="waybill-manage-page__waybill-route-change-link"
|
||||
@@ -2184,7 +2189,11 @@
|
||||
/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="司机上传" name="driverUpload">
|
||||
<el-tab-pane
|
||||
v-if="waybillDriverUploads.length"
|
||||
label="司机上传"
|
||||
name="driverUpload"
|
||||
>
|
||||
<div
|
||||
v-loading="waybillPunchRecordsLoading"
|
||||
class="waybill-manage-page__driver-upload-grid"
|
||||
@@ -2201,11 +2210,6 @@
|
||||
{{ photo.label || '凭证' }}
|
||||
</div>
|
||||
</div>
|
||||
<el-empty
|
||||
v-if="!waybillPunchRecordsLoading && !waybillDriverUploads.length"
|
||||
description="暂无司机上传数据"
|
||||
:image-size="50"
|
||||
/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
@@ -5265,7 +5269,9 @@ export default {
|
||||
: defaultText;
|
||||
},
|
||||
statusTagType(status) {
|
||||
if ([1, 'pending', 'processing', 'approved', 'change_approved'].includes(status)) {
|
||||
if (
|
||||
[1, 'pending', 'processing', 'running', 'approved', 'change_approved'].includes(status)
|
||||
) {
|
||||
return 'success';
|
||||
}
|
||||
if ([2, 'cancelled', 'withdrawn', 'draft'].includes(status)) {
|
||||
@@ -9763,6 +9769,15 @@ export default {
|
||||
strong {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
:deep(.el-tag) {
|
||||
height: 28px;
|
||||
padding: 0 12px;
|
||||
border: 0;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
line-height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
&__waybill-route-change-link {
|
||||
|
||||
@@ -966,44 +966,10 @@
|
||||
@close="attachmentImagePreviewVisible = false"
|
||||
/>
|
||||
|
||||
<el-dialog
|
||||
<change-record-detail-dialog
|
||||
v-model="detailChangeRecordVisible"
|
||||
title="变更记录详情"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
width="1100px"
|
||||
top="10px"
|
||||
class="contract-change-record-detail-dialog"
|
||||
>
|
||||
<div v-if="detailChangeRecord" class="contract-change-record-detail-meta">
|
||||
<span>经办人:{{ detailChangeRecord.handlerUserName || '-' }}</span>
|
||||
<span>变更类型:{{ detailChangeRecord.changeType || '-' }}</span>
|
||||
<span>状态:{{ detailChangeRecord.statusName || detailChangeRecord.status || '-' }}</span>
|
||||
</div>
|
||||
<el-table :data="detailChangeRecordDetailRows" border :show-overflow-tooltip="false">
|
||||
<el-table-column prop="field" label="变更字段" min-width="180" />
|
||||
<el-table-column
|
||||
prop="before"
|
||||
label="变更前"
|
||||
min-width="360"
|
||||
class-name="contract-change-record-detail-value"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="after"
|
||||
label="变更后"
|
||||
min-width="500"
|
||||
class-name="contract-change-record-detail-value"
|
||||
/>
|
||||
</el-table>
|
||||
<el-empty
|
||||
v-if="!detailChangeRecordDetailRows.length"
|
||||
description="暂无变更内容"
|
||||
:image-size="60"
|
||||
/>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="detailChangeRecordVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
:rows="detailChangeRecordDetailRows"
|
||||
/>
|
||||
|
||||
<billing-plan-editor
|
||||
v-model="detailBillingPlanBox"
|
||||
@@ -1042,6 +1008,7 @@ import { getToken } from '@/utils/auth';
|
||||
import { downloadFileByUrl, downloadXls } from '@/utils/util';
|
||||
import { submitMkApprovalFlow } from '@/utils/mk-approval';
|
||||
import BillingPlanEditor from './components/billing-plan-editor.vue';
|
||||
import ChangeRecordDetailDialog from '@/components/change-record-detail-dialog/main.vue';
|
||||
import ContractAttachmentSection, {
|
||||
attachmentName as sharedAttachmentName,
|
||||
attachmentUrl as sharedAttachmentUrl,
|
||||
@@ -1194,7 +1161,14 @@ const attachmentViewerPlugins = [
|
||||
|
||||
export default {
|
||||
name: 'ContractManage',
|
||||
components: { ContractAttachmentSection, BillingPlanEditor, ElImageViewer, OpenFileViewer, PdfPreview },
|
||||
components: {
|
||||
ContractAttachmentSection,
|
||||
BillingPlanEditor,
|
||||
ChangeRecordDetailDialog,
|
||||
ElImageViewer,
|
||||
OpenFileViewer,
|
||||
PdfPreview,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
api,
|
||||
@@ -2969,25 +2943,6 @@ export default {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.contract-change-record-detail-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px 32px;
|
||||
margin-bottom: 16px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
:deep(.contract-change-record-detail-dialog .el-dialog__body) {
|
||||
max-height: 65vh;
|
||||
overflow: auto;
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
:deep(.contract-change-record-detail-dialog .contract-change-record-detail-value .cell) {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
:global(.avue--collapse .contract-manage-page__footer) {
|
||||
left: 60px;
|
||||
}
|
||||
|
||||
@@ -395,14 +395,8 @@
|
||||
<div class="loading-detail__summary">
|
||||
<div class="loading-detail__heading">配载单详情</div>
|
||||
<div>{{ dialogForm.loadingNo || '-' }}</div>
|
||||
<div>
|
||||
<el-tag :type="detailStatusType" class="status-text">{{
|
||||
statusText(dialogForm)
|
||||
}}</el-tag>
|
||||
</div>
|
||||
<div>
|
||||
<el-tag type="warning">{{ transportTypeLabel(dialogForm.transportType) }}</el-tag>
|
||||
</div>
|
||||
<el-tag :type="detailStatusType">{{ statusText(dialogForm) }}</el-tag>
|
||||
<el-tag type="primary">{{ transportTypeLabel(dialogForm.transportType) }}</el-tag>
|
||||
</div>
|
||||
<div class="loading-detail__task-row">
|
||||
<div class="loading-detail__field">
|
||||
@@ -1683,11 +1677,12 @@ export default {
|
||||
const typeMap = {
|
||||
pending: 'success',
|
||||
running: 'success',
|
||||
completed: 'info',
|
||||
processing: 'success',
|
||||
completed: 'primary',
|
||||
cancelled: 'info',
|
||||
draft: 'warning',
|
||||
draft: 'info',
|
||||
};
|
||||
return typeMap[this.dialogForm.businessStatus] || 'info';
|
||||
return typeMap[this.dialogForm.businessStatus] || 'warning';
|
||||
},
|
||||
taskSummary() {
|
||||
return (
|
||||
@@ -3604,8 +3599,17 @@ export default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 24px;
|
||||
gap: 12px;
|
||||
padding: 4px 0 16px;
|
||||
|
||||
:deep(.el-tag) {
|
||||
height: 28px;
|
||||
padding: 0 12px;
|
||||
border: 0;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
line-height: 28px;
|
||||
}
|
||||
}
|
||||
.loading-detail__heading {
|
||||
font-size: 20px;
|
||||
|
||||
@@ -132,7 +132,7 @@
|
||||
/>
|
||||
</template>
|
||||
<master-order-dispatch v-else-if="mode === 'dispatch'" :id="routeId" @back="goList" />
|
||||
<master-order-detail v-else :id="routeId" />
|
||||
<master-order-detail v-else-if="mode === 'detail'" :id="routeId" />
|
||||
<el-dialog v-model="confirm.visible" title="提示" width="400px"
|
||||
><span>{{ confirm.message }}</span
|
||||
><template #footer
|
||||
@@ -153,6 +153,10 @@ export default {
|
||||
components: { MasterOrderEditor, MasterOrderDispatch, MasterOrderDetail },
|
||||
data() {
|
||||
return {
|
||||
// 实例创建时锁定所属路由。keep-alive 缓存后 $route 会变成其它页面,
|
||||
// 不能再用全局 query.mode / query.id 决定本页形态,否则打开「新增项目管理」
|
||||
//(mode=add 且无 id)会落入详情分支并请求缺少 id 的详情接口。
|
||||
routePathLocked: this.$route.path,
|
||||
searchExpanded: false,
|
||||
loading: false,
|
||||
records: [],
|
||||
@@ -180,10 +184,15 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isOwnedRoute() {
|
||||
return this.$route.path === this.routePathLocked;
|
||||
},
|
||||
mode() {
|
||||
if (!this.isOwnedRoute) return 'list';
|
||||
return this.$route.query.mode || 'list';
|
||||
},
|
||||
routeId() {
|
||||
if (!this.isOwnedRoute) return '';
|
||||
return this.$route.query.id;
|
||||
},
|
||||
masterEditorTitle() {
|
||||
@@ -194,6 +203,7 @@ export default {
|
||||
'$route.query': {
|
||||
immediate: true,
|
||||
handler() {
|
||||
if (!this.isOwnedRoute) return;
|
||||
this.syncTagTitle();
|
||||
if (this.mode === 'list') this.load();
|
||||
},
|
||||
|
||||
@@ -196,7 +196,7 @@
|
||||
<el-form-item label="项目编号" prop="projectCode">
|
||||
<el-input
|
||||
v-model="form.projectCode"
|
||||
placeholder="请输入"
|
||||
:placeholder="dialogType === 'add' ? '若不填写,系统自动生成' : '请输入'"
|
||||
:disabled="dialogType === 'edit'"
|
||||
/>
|
||||
</el-form-item>
|
||||
@@ -703,6 +703,7 @@
|
||||
label="变更内容"
|
||||
min-width="180"
|
||||
align="center"
|
||||
:show-overflow-tooltip="false"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tooltip placement="top" :show-after="200">
|
||||
@@ -730,43 +731,10 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog
|
||||
<change-record-detail-dialog
|
||||
v-model="changeRecordDetailVisible"
|
||||
title="变更记录详情"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
width="1100px"
|
||||
top="10px"
|
||||
class="project-change-record-detail-dialog"
|
||||
>
|
||||
<div v-if="changeRecordDetail" class="project-change-record-detail-meta">
|
||||
<span>变更日期:{{ changeRecordDetail.changeDate || '-' }}</span>
|
||||
<span>变更账号:{{ changeRecordDetail.handler || '-' }}</span>
|
||||
</div>
|
||||
<el-table :data="changeRecordDetailRows" border :show-overflow-tooltip="false">
|
||||
<el-table-column prop="field" label="变更字段" min-width="180" />
|
||||
<el-table-column
|
||||
prop="before"
|
||||
label="变更前"
|
||||
min-width="360"
|
||||
class-name="project-change-record-detail-value"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="after"
|
||||
label="变更后"
|
||||
min-width="500"
|
||||
class-name="project-change-record-detail-value"
|
||||
/>
|
||||
</el-table>
|
||||
<el-empty
|
||||
v-if="!changeRecordDetailRows.length"
|
||||
description="暂无变更内容"
|
||||
:image-size="60"
|
||||
/>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="changeRecordDetailVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
:rows="changeRecordDetailRows"
|
||||
/>
|
||||
|
||||
<template v-if="isChangeDialog">
|
||||
<div class="dialog-section-title">变更原因</div>
|
||||
@@ -947,6 +915,7 @@ import { submitMkApprovalFlow } from '@/utils/mk-approval';
|
||||
import { ElImageViewer } from 'element-plus';
|
||||
import { OpenFileViewer } from '@open-file-viewer/vue';
|
||||
import PdfPreview from '@/components/pdf-preview/main.vue';
|
||||
import ChangeRecordDetailDialog from '@/components/change-record-detail-dialog/main.vue';
|
||||
import {
|
||||
fallbackPlugin,
|
||||
imagePlugin,
|
||||
@@ -1066,6 +1035,7 @@ export default {
|
||||
name: 'ProjectApply',
|
||||
components: {
|
||||
CustomerArchive: defineAsyncComponent(() => import('@/views/vehicle/customer-archive.vue')),
|
||||
ChangeRecordDetailDialog,
|
||||
ElImageViewer,
|
||||
OpenFileViewer,
|
||||
PdfPreview,
|
||||
@@ -1520,6 +1490,7 @@ export default {
|
||||
this.fillDefaultUsers();
|
||||
return;
|
||||
}
|
||||
if (!id) return;
|
||||
this.api.getDetail(id).then(res => {
|
||||
const detail = res.data.data || {};
|
||||
const isChange = type === 'change';
|
||||
@@ -2999,27 +2970,6 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.project-change-record-detail-dialog .el-dialog__body) {
|
||||
max-height: 65vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:deep(.project-change-record-detail-dialog .project-change-record-detail-value .cell) {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.project-change-record-detail-meta {
|
||||
display: flex;
|
||||
gap: 32px;
|
||||
margin-bottom: 16px;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:global(.project-apply-dialog .el-dialog__body) {
|
||||
max-height: 76vh;
|
||||
overflow-y: auto;
|
||||
|
||||
@@ -610,7 +610,12 @@ export default {
|
||||
return value;
|
||||
},
|
||||
applyDetail(detail) {
|
||||
this.form = { ...detail };
|
||||
this.form = {
|
||||
...detail,
|
||||
projectFundLimit: this.blankSentinelAmount(detail.projectFundLimit),
|
||||
usedFundLimit: this.blankSentinelAmount(detail.usedFundLimit),
|
||||
remainingFundLimit: this.blankSentinelAmount(detail.remainingFundLimit),
|
||||
};
|
||||
this.selectedProjectId = detail.projectId || '';
|
||||
this.attachmentRows = this.parseJsonArray(detail.attachmentsJson);
|
||||
this.selectedAttachments = [];
|
||||
@@ -620,6 +625,9 @@ export default {
|
||||
const { projectQuotaInfoTitle, temporaryCreditInfoTitle, ...payload } = row || {};
|
||||
return {
|
||||
...payload,
|
||||
projectFundLimit: this.toOptionalAmount(payload.projectFundLimit),
|
||||
usedFundLimit: this.toOptionalAmount(payload.usedFundLimit),
|
||||
remainingFundLimit: this.toOptionalAmount(payload.remainingFundLimit),
|
||||
applyLimit: payload.applyLimit === '' ? '' : Number(payload.applyLimit),
|
||||
attachmentsJson: JSON.stringify(this.attachmentRows),
|
||||
};
|
||||
@@ -765,8 +773,8 @@ export default {
|
||||
projectName: project.projectName || '',
|
||||
projectCode: project.projectCode || '',
|
||||
undertakeDeptName: project.undertakeDeptName || '',
|
||||
projectFundLimit: project.fundLimit || project.projectFundLimit || 0,
|
||||
usedFundLimit: project.usedFundLimit || 0,
|
||||
projectFundLimit: this.blankSentinelAmount(project.fundLimit ?? project.projectFundLimit),
|
||||
usedFundLimit: this.blankSentinelAmount(project.usedFundLimit),
|
||||
});
|
||||
this.form.remainingFundLimit = this.calculateRemainingFundLimit();
|
||||
getProjectDetail(project.id).then(res => {
|
||||
@@ -774,18 +782,32 @@ export default {
|
||||
Object.assign(this.form, {
|
||||
projectCode: detail.projectCode || this.form.projectCode,
|
||||
undertakeDeptName: detail.undertakeDeptName || this.form.undertakeDeptName,
|
||||
projectFundLimit:
|
||||
detail.fundLimit ?? detail.projectFundLimit ?? this.form.projectFundLimit,
|
||||
usedFundLimit: detail.usedFundLimit ?? this.form.usedFundLimit,
|
||||
projectFundLimit: this.blankSentinelAmount(
|
||||
detail.fundLimit ?? detail.projectFundLimit ?? this.form.projectFundLimit
|
||||
),
|
||||
usedFundLimit: this.blankSentinelAmount(
|
||||
detail.usedFundLimit ?? this.form.usedFundLimit
|
||||
),
|
||||
});
|
||||
this.form.remainingFundLimit = this.calculateRemainingFundLimit();
|
||||
});
|
||||
},
|
||||
blankSentinelAmount(value) {
|
||||
if (value === undefined || value === null || value === '') return '';
|
||||
return Number(value) === -1 ? '' : value;
|
||||
},
|
||||
toOptionalAmount(value) {
|
||||
const amount = this.blankSentinelAmount(value);
|
||||
return amount === '' ? null : amount;
|
||||
},
|
||||
calculateRemainingFundLimit() {
|
||||
const total = Number(this.form.projectFundLimit);
|
||||
const used = Number(this.form.usedFundLimit);
|
||||
if (!Number.isFinite(total) || !Number.isFinite(used)) return '';
|
||||
return Math.round((total - used + Number.EPSILON) * 100) / 100;
|
||||
const total = this.blankSentinelAmount(this.form.projectFundLimit);
|
||||
const used = this.blankSentinelAmount(this.form.usedFundLimit);
|
||||
if (total === '' || used === '') return '';
|
||||
const totalAmount = Number(total);
|
||||
const usedAmount = Number(used);
|
||||
if (!Number.isFinite(totalAmount) || !Number.isFinite(usedAmount)) return '';
|
||||
return Math.round((totalAmount - usedAmount + Number.EPSILON) * 100) / 100;
|
||||
},
|
||||
handleAmountInput(value) {
|
||||
const text = String(value || '').replace(/[^\d.]/g, '');
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -326,7 +326,7 @@ export default {
|
||||
openProjectAdvance() {
|
||||
this.$router.push({
|
||||
path: '/payment/payment-application/form',
|
||||
query: { mode: 'add', paymentType: 'project_advance' },
|
||||
query: { mode: 'add', paymentType: 'progress_advance' },
|
||||
});
|
||||
},
|
||||
openEdit(row) {
|
||||
|
||||
@@ -66,6 +66,9 @@
|
||||
<span v-else-if="field.prop === 'settlementType'" class="form-readonly">
|
||||
{{ settlementTypeName }}
|
||||
</span>
|
||||
<span v-else-if="field.prop === 'preSettlementNo'" class="form-readonly">
|
||||
{{ form.preSettlementNo || (!form.id ? '系统自动生成' : '-') }}
|
||||
</span>
|
||||
<span v-else-if="field.prop === 'createTime'" class="form-readonly">
|
||||
{{ formatCreateDate(form.createTime) }}
|
||||
</span>
|
||||
@@ -850,35 +853,10 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
<change-record-detail-dialog
|
||||
v-model="adjustChangeRecordVisible"
|
||||
title="变更记录详情"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
width="1100px"
|
||||
top="10px"
|
||||
class="pre-settlement-change-record-detail-dialog"
|
||||
>
|
||||
<div v-if="adjustChangeRecord" class="pre-settlement-change-record-detail-meta">
|
||||
<span>变更日期:{{ adjustChangeRecord.changeTime || '-' }}</span>
|
||||
<span>经办人:{{ adjustChangeRecord.operatorName || '-' }}</span>
|
||||
<span>变更类型:{{ adjustChangeRecord.changeType || '-' }}</span>
|
||||
<span>状态:已生效</span>
|
||||
</div>
|
||||
<el-table :data="adjustChangeRecordDetailRows" border :show-overflow-tooltip="false">
|
||||
<el-table-column prop="field" label="变更字段" min-width="220" />
|
||||
<el-table-column prop="before" label="变更前" min-width="330" />
|
||||
<el-table-column prop="after" label="变更后" min-width="420" />
|
||||
</el-table>
|
||||
<el-empty
|
||||
v-if="!adjustChangeRecordDetailRows.length"
|
||||
description="暂无变更内容"
|
||||
:image-size="60"
|
||||
/>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="adjustChangeRecordVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
:rows="adjustChangeRecordDetailRows"
|
||||
/>
|
||||
|
||||
<el-dialog
|
||||
v-model="billingRuleDialog.visible"
|
||||
@@ -950,6 +928,7 @@
|
||||
import { h } from 'vue';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { InfoFilled } from '@element-plus/icons-vue';
|
||||
import ChangeRecordDetailDialog from '@/components/change-record-detail-dialog/main.vue';
|
||||
import { getMkPublicDetail } from '@/api/mk-process';
|
||||
import {
|
||||
adjustDetail,
|
||||
@@ -989,7 +968,7 @@ import * as XLSX from 'xlsx';
|
||||
|
||||
export default {
|
||||
name: 'PreSettlementEditor',
|
||||
components: { InfoFilled },
|
||||
components: { InfoFilled, ChangeRecordDetailDialog },
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
@@ -2990,23 +2969,6 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.pre-settlement-change-record-detail-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px 32px;
|
||||
margin-bottom: 16px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
:deep(.pre-settlement-change-record-detail-dialog .el-dialog__body) {
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
:deep(.pre-settlement-change-record-detail-dialog .el-table .cell) {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
:deep(.pre-settlement-editor .el-dialog__body) {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
@@ -497,6 +497,7 @@ export default {
|
||||
path: '/payment/payment-application/form',
|
||||
query: {
|
||||
mode: 'add',
|
||||
paymentType: 'progress_advance',
|
||||
transferToken,
|
||||
preSettlementIds: sourcePreSettlements
|
||||
.map(row => row.preSettlementId)
|
||||
|
||||
@@ -183,6 +183,7 @@
|
||||
title="变更记录详情"
|
||||
width="88%"
|
||||
append-to-body
|
||||
align-center
|
||||
>
|
||||
<el-table v-loading="changeRecordsDialog.loading" :data="changeRows" border>
|
||||
<el-table-column type="index" label="序号" width="64" align="center" />
|
||||
@@ -1197,10 +1198,24 @@ export default {
|
||||
await this.loadTable();
|
||||
await this.openRouteDetail();
|
||||
},
|
||||
// 标签切走(keep-alive 缓存)或销毁时,收起 teleport/append-to-body 弹层,避免残留盖住其它页面
|
||||
deactivated() {
|
||||
this.closeInnerDialogs();
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.clearAdjustCalculations();
|
||||
this.closeInnerDialogs();
|
||||
},
|
||||
methods: {
|
||||
closeInnerDialogs() {
|
||||
this.closeDetailPanel();
|
||||
this.closeAdjustPanel();
|
||||
this.changeRecordsDialog.visible = false;
|
||||
this.updateFeeDialog.visible = false;
|
||||
this.transferDialog.visible = false;
|
||||
this.generateDialog.visible = false;
|
||||
this.previewDialog.visible = false;
|
||||
this.generateContractDialog.visible = false;
|
||||
},
|
||||
contractCategoryName(value) {
|
||||
return (
|
||||
this.contractCategoryOptions.find(item => String(item.value) === String(value))?.label ||
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
>
|
||||
<el-form
|
||||
label-position="right"
|
||||
label-width="calc(7em + 24px)"
|
||||
label-width="calc(4em + 12px)"
|
||||
class="certification-audit archive-form"
|
||||
>
|
||||
<section-card title="基础信息">
|
||||
@@ -145,19 +145,25 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="外廓尺寸(毫米)">
|
||||
<el-form-item label="外廓尺寸">
|
||||
<div class="dimension-group">
|
||||
<div class="dimension-group__item">
|
||||
<span class="dimension-group__label">长</span>
|
||||
<span class="certification-audit__value">{{ auditValue('outerLength') }}</span>
|
||||
<span class="certification-audit__value">
|
||||
{{ auditValueWithUnit('outerLength', 'mm') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="dimension-group__item">
|
||||
<span class="dimension-group__label">宽</span>
|
||||
<span class="certification-audit__value">{{ auditValue('outerWidth') }}</span>
|
||||
<span class="certification-audit__value">
|
||||
{{ auditValueWithUnit('outerWidth', 'mm') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="dimension-group__item">
|
||||
<span class="dimension-group__label">高</span>
|
||||
<span class="certification-audit__value">{{ auditValue('outerHeight') }}</span>
|
||||
<span class="certification-audit__value">
|
||||
{{ auditValueWithUnit('outerHeight', 'mm') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
@@ -165,13 +171,17 @@
|
||||
</el-row>
|
||||
<el-row :gutter="18">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="核定载质量(KG)">
|
||||
<span class="certification-audit__value">{{ auditValue('approvedLoadKg') }}</span>
|
||||
<el-form-item label="核定载质量">
|
||||
<span class="certification-audit__value">
|
||||
{{ auditValueWithUnit('approvedLoadKg', 'KG') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="准牵引总质量(KG)">
|
||||
<span class="certification-audit__value">{{ auditValue('tractionMassKg') }}</span>
|
||||
<el-form-item label="准牵引总质量">
|
||||
<span class="certification-audit__value">
|
||||
{{ auditValueWithUnit('tractionMassKg', 'KG') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
@@ -357,7 +367,7 @@
|
||||
v-if="showRejectReason"
|
||||
class="certification-audit__reason"
|
||||
label-position="right"
|
||||
label-width="calc(7em + 24px)"
|
||||
label-width="calc(4em + 24px)"
|
||||
>
|
||||
<el-form-item label="驳回原因" required>
|
||||
<el-input
|
||||
@@ -427,7 +437,7 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="外廓尺寸(毫米)">
|
||||
<el-form-item label="外廓尺寸">
|
||||
<div class="dimension-group">
|
||||
<div class="dimension-group__item">
|
||||
<span class="dimension-group__label">长</span>
|
||||
@@ -436,7 +446,9 @@
|
||||
inputmode="numeric"
|
||||
placeholder="请输入"
|
||||
@input="vehicleForm.outerLength = digitsOnly($event)"
|
||||
/>
|
||||
>
|
||||
<template #suffix>mm</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="dimension-group__item">
|
||||
<span class="dimension-group__label">宽</span>
|
||||
@@ -445,7 +457,9 @@
|
||||
inputmode="numeric"
|
||||
placeholder="请输入"
|
||||
@input="vehicleForm.outerWidth = digitsOnly($event)"
|
||||
/>
|
||||
>
|
||||
<template #suffix>mm</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="dimension-group__item">
|
||||
<span class="dimension-group__label">高</span>
|
||||
@@ -454,7 +468,9 @@
|
||||
inputmode="numeric"
|
||||
placeholder="请输入"
|
||||
@input="vehicleForm.outerHeight = digitsOnly($event)"
|
||||
/>
|
||||
>
|
||||
<template #suffix>mm</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
@@ -462,23 +478,27 @@
|
||||
</el-row>
|
||||
<el-row :gutter="18">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="核定载质量(KG)" prop="approvedLoadKg">
|
||||
<el-form-item label="核定载质量" prop="approvedLoadKg">
|
||||
<el-input
|
||||
v-model="vehicleForm.approvedLoadKg"
|
||||
inputmode="numeric"
|
||||
placeholder="请输入"
|
||||
@input="vehicleForm.approvedLoadKg = digitsOnly($event)"
|
||||
/>
|
||||
>
|
||||
<template #suffix>KG</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="准牵引总质量(KG)" prop="tractionMassKg">
|
||||
<el-form-item label="准牵引总质量" prop="tractionMassKg">
|
||||
<el-input
|
||||
v-model="vehicleForm.tractionMassKg"
|
||||
inputmode="numeric"
|
||||
placeholder="请输入"
|
||||
@input="vehicleForm.tractionMassKg = digitsOnly($event)"
|
||||
/>
|
||||
>
|
||||
<template #suffix>KG</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
@@ -896,6 +916,7 @@ export default {
|
||||
energyTypeOptions: ['柴油', '汽油', '新能源', '天然气'],
|
||||
baseRules: {
|
||||
organizationName: [{ required: true, message: '请选择所属组织', trigger: 'change' }],
|
||||
useDepartment: [{ required: true, message: '请选择使用部门', trigger: 'change' }],
|
||||
plateNo: [
|
||||
{ required: true, message: '请输入车牌号', trigger: 'change' },
|
||||
{ validator: this.validatePlateNo, trigger: 'change' },
|
||||
@@ -986,6 +1007,9 @@ export default {
|
||||
if (this.vehicleBox && !this.vehicleForm.id && !this.vehicleForm.organizationName) {
|
||||
this.vehicleForm.organizationName = this.getCurrentOrganizationName();
|
||||
}
|
||||
if (this.vehicleBox && !this.vehicleForm.useDepartment) {
|
||||
this.vehicleForm.useDepartment = this.getCurrentOrganizationName();
|
||||
}
|
||||
});
|
||||
},
|
||||
flattenDeptOptions(tree = [], level = 0) {
|
||||
@@ -1061,6 +1085,7 @@ export default {
|
||||
if (!row?.id) {
|
||||
this.vehicleForm = emptyForm();
|
||||
this.vehicleForm.organizationName = this.getCurrentOrganizationName();
|
||||
this.vehicleForm.useDepartment = this.getCurrentOrganizationName();
|
||||
this.syncPlateNo(false);
|
||||
this.vehicleBox = true;
|
||||
return;
|
||||
@@ -1070,6 +1095,9 @@ export default {
|
||||
...emptyForm(),
|
||||
...(res.data.data || {}),
|
||||
};
|
||||
if (!this.vehicleForm.useDepartment) {
|
||||
this.vehicleForm.useDepartment = this.getCurrentOrganizationName();
|
||||
}
|
||||
this.splitPlateNo();
|
||||
this.syncPlateNo(false);
|
||||
this.vehicleBox = true;
|
||||
@@ -1102,6 +1130,10 @@ export default {
|
||||
const value = this.certificationAuditForm[prop];
|
||||
return value === undefined || value === null || value === '' ? '-' : value;
|
||||
},
|
||||
auditValueWithUnit(prop, unit) {
|
||||
const value = this.auditValue(prop);
|
||||
return value === '-' ? value : `${value}${unit}`;
|
||||
},
|
||||
handleCertificationApprove() {
|
||||
auditCertification(this.certificationAuditForm.id, 1).then(() => {
|
||||
this.$message.success('认证已通过');
|
||||
@@ -1693,11 +1725,19 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.certification-audit,
|
||||
.certification-audit {
|
||||
:deep(.el-form-item__label) {
|
||||
flex: 0 0 calc(4em + 12px) !important;
|
||||
width: calc(4em + 12px) !important;
|
||||
max-width: calc(4em + 12px);
|
||||
}
|
||||
}
|
||||
|
||||
.certification-audit__reason {
|
||||
:deep(.el-form-item__label) {
|
||||
flex: 0 0 calc(7em + 24px) !important;
|
||||
width: calc(7em + 24px) !important;
|
||||
flex: 0 0 calc(4em + 24px) !important;
|
||||
width: calc(4em + 24px) !important;
|
||||
max-width: calc(4em + 24px);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -144,9 +144,24 @@
|
||||
>
|
||||
撤回
|
||||
</el-link>
|
||||
<el-link
|
||||
type="primary"
|
||||
v-if="
|
||||
hasPermission('customer_type_re_cert') &&
|
||||
row.approvalStatus === 'reviewing' &&
|
||||
row.reviewStatus !== '已完成'
|
||||
"
|
||||
@click="openReview(row)"
|
||||
>
|
||||
复评
|
||||
</el-link>
|
||||
<el-link
|
||||
type="success"
|
||||
v-if="hasPermission('customer_archive_approve') && row.approvalStatus === 'reviewing'"
|
||||
v-if="
|
||||
hasPermission('customer_archive_approve') &&
|
||||
row.approvalStatus === 'reviewing' &&
|
||||
row.reviewStatus === '已完成'
|
||||
"
|
||||
@click="handleApprove(row)"
|
||||
>
|
||||
审核通过
|
||||
@@ -517,7 +532,7 @@
|
||||
<span>{{ formatScoreValue(row.selfScore) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="复评得分" min-width="145" align="center">
|
||||
<el-table-column v-if="archiveForm.id" label="复评得分" min-width="145" align="center">
|
||||
<template #default="{ row }">
|
||||
<span>{{ formatScoreValue(row.reviewScore) }}</span>
|
||||
</template>
|
||||
@@ -553,11 +568,18 @@
|
||||
<span>{{ row.reviewStatus }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="135" fixed="right" align="center">
|
||||
<el-table-column label="操作" width="160" fixed="right" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-link type="primary" @click="openScore(row.__raw, row.__rawIndex)">
|
||||
详情
|
||||
</el-link>
|
||||
<el-link
|
||||
v-if="showScoreDelete"
|
||||
type="danger"
|
||||
@click="deleteScore(row.__rawIndex)"
|
||||
>
|
||||
删除
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -876,43 +898,10 @@
|
||||
/>
|
||||
</section-card>
|
||||
|
||||
<el-dialog
|
||||
<change-record-detail-dialog
|
||||
v-model="changeRecordDetailVisible"
|
||||
title="变更记录详情"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
width="1100px"
|
||||
top="10px"
|
||||
class="change-record-detail-dialog"
|
||||
>
|
||||
<div v-if="changeRecordDetail" class="change-record-detail-meta">
|
||||
<span>变更日期:{{ changeRecordDetail.changeTime || '-' }}</span>
|
||||
<span>变更账号:{{ changeRecordDetail.changeUserName || '-' }}</span>
|
||||
</div>
|
||||
<el-table :data="changeRecordDetailRows" border :show-overflow-tooltip="false">
|
||||
<el-table-column prop="field" label="变更字段" min-width="180" />
|
||||
<el-table-column
|
||||
prop="before"
|
||||
label="变更前"
|
||||
min-width="360"
|
||||
class-name="change-record-detail-value"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="after"
|
||||
label="变更后"
|
||||
min-width="500"
|
||||
class-name="change-record-detail-value"
|
||||
/>
|
||||
</el-table>
|
||||
<el-empty
|
||||
v-if="!changeRecordDetailRows.length"
|
||||
description="暂无变更内容"
|
||||
:image-size="60"
|
||||
/>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="changeRecordDetailVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
:rows="changeRecordDetailRows"
|
||||
/>
|
||||
|
||||
<div class="archive-form__footer" v-if="!isPublicViewPage">
|
||||
<!-- 次要:独立页返回 / 只读关闭 / 弹窗取消 -->
|
||||
@@ -1323,7 +1312,25 @@
|
||||
class="score-detail-table score-detail-table--inline"
|
||||
max-height="520"
|
||||
>
|
||||
<el-table-column label="评分项目" prop="itemName" width="150" align="center" />
|
||||
<el-table-column
|
||||
label="评分项目"
|
||||
width="150"
|
||||
align="center"
|
||||
:show-overflow-tooltip="false"
|
||||
>
|
||||
<template #default="{ row: detail }">
|
||||
<el-tooltip
|
||||
:content="detail.itemName"
|
||||
placement="top"
|
||||
effect="dark"
|
||||
append-to="body"
|
||||
:show-after="200"
|
||||
:disabled="!detail.itemName"
|
||||
>
|
||||
<span class="score-item-name">{{ detail.itemName }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="评分标准" min-width="430">
|
||||
<template #default="{ row: detail }">
|
||||
<div class="score-standard-cell">
|
||||
@@ -1333,6 +1340,7 @@
|
||||
inputmode="decimal"
|
||||
:disabled="readonly"
|
||||
@input="value => handleScoreValueInput(detail, value)"
|
||||
@blur="handleScoreValueBlur(detail)"
|
||||
>
|
||||
<template #append>{{ getScoreRule(detail)?.changeUnit || '' }}</template>
|
||||
</el-input>
|
||||
@@ -1362,13 +1370,15 @@
|
||||
<span>{{ formatScoreValue(getSignedScore(detail, 'selfScore')) }}分</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="复评得分" width="150" align="center">
|
||||
<el-table-column v-if="!isNewScore" label="复评得分" width="150" align="center">
|
||||
<template #default="{ row: detail }">
|
||||
<el-input
|
||||
v-model="detail.reviewScore"
|
||||
maxlength="10"
|
||||
inputmode="decimal"
|
||||
:disabled="readonly || !hasPermission('customer_type_re_cert')"
|
||||
:disabled="
|
||||
(!reviewMode && readonly) || !hasPermission('customer_type_re_cert')
|
||||
"
|
||||
@input="value => handleScoreDetailScoreInput(detail, 'reviewScore', value)"
|
||||
>
|
||||
<template
|
||||
@@ -1389,7 +1399,7 @@
|
||||
<template #default="{ row }">
|
||||
<span class="score-category-table__name">{{ row.categoryName }}</span>
|
||||
<span>自评:{{ getSignedScore(row, 'selfScore') }}分</span>
|
||||
<span>复评:{{ getSignedScore(row, 'reviewScore') }}分</span>
|
||||
<span v-if="!isNewScore">复评:{{ getSignedScore(row, 'reviewScore') }}分</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="60" align="center">
|
||||
@@ -1463,12 +1473,15 @@
|
||||
<div class="score-detail-dialog__total">
|
||||
<span>总分合计</span>
|
||||
<span>自评:{{ formatScoreValue(currentScore.selfScore) }}分</span>
|
||||
<span>复评:{{ formatScoreValue(currentScore.reviewScore) }}分</span>
|
||||
<span v-if="!isNewScore">复评:{{ formatScoreValue(currentScore.reviewScore) }}分</span>
|
||||
</div>
|
||||
<el-button @click="scoreBox = false">取消</el-button>
|
||||
<el-button type="primary" plain v-if="!readonly" @click="saveScoreDetail">保存</el-button>
|
||||
<el-button v-if="isNewScore" type="primary" @click="submitSelfScore">提交自评</el-button>
|
||||
<el-button type="primary" plain v-if="!readonly && !isNewScore" @click="saveScoreDetail">
|
||||
保存
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="!readonly && hasPermission('customer_type_re_cert')"
|
||||
v-if="!isNewScore && (!readonly || reviewMode) && hasPermission('customer_type_re_cert')"
|
||||
type="primary"
|
||||
@click="confirmReviewScore"
|
||||
>
|
||||
@@ -1523,6 +1536,7 @@ import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
import SectionCard from '@/components/section-card/main.vue';
|
||||
import PdfPreview from '@/components/pdf-preview/main.vue';
|
||||
import ChangeRecordDetailDialog from '@/components/change-record-detail-dialog/main.vue';
|
||||
|
||||
const createTimeRangeMap = {
|
||||
createTimeRange: ['createTimeStart', 'createTimeEnd'],
|
||||
@@ -1538,6 +1552,7 @@ const qualificationViewerPlugins = [
|
||||
export default {
|
||||
components: {
|
||||
SectionCard,
|
||||
ChangeRecordDetailDialog,
|
||||
ArrowRight,
|
||||
ElImageViewer,
|
||||
OpenFileViewer,
|
||||
@@ -1631,6 +1646,8 @@ export default {
|
||||
receiptBox: false,
|
||||
invoiceBox: false,
|
||||
readonly: false,
|
||||
reviewMode: false,
|
||||
listActivated: false,
|
||||
originalAccessType: '',
|
||||
activeTab: 'scores',
|
||||
archiveForm: this.emptyArchive(),
|
||||
@@ -1825,7 +1842,7 @@ export default {
|
||||
editBtn: false,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 280,
|
||||
menuWidth: 320,
|
||||
column: [
|
||||
{
|
||||
label: '客商编号',
|
||||
@@ -2019,10 +2036,20 @@ export default {
|
||||
this.initBusinessDictionaries();
|
||||
this.initScoreQuantificationOptions();
|
||||
if (this.isArchivePage) {
|
||||
const readonly = this.$route.query.view === '1' || this.$route.query.view === 'true';
|
||||
this.reviewMode = this.$route.query.review === '1';
|
||||
const readonly =
|
||||
this.$route.query.view === '1' || this.$route.query.view === 'true' || this.reviewMode;
|
||||
this.openArchive(this.$route.query.id ? { id: this.$route.query.id } : null, readonly);
|
||||
}
|
||||
},
|
||||
activated() {
|
||||
if (this.isArchivePage || this.isPublicViewPage) return;
|
||||
if (!this.listActivated) {
|
||||
this.listActivated = true;
|
||||
return;
|
||||
}
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
isAdmin() {
|
||||
@@ -2108,6 +2135,7 @@ export default {
|
||||
return ids.join(',');
|
||||
},
|
||||
dialogTitle() {
|
||||
if (this.reviewMode) return '客商复评';
|
||||
if (this.readonly) return '查看客商档案';
|
||||
return this.archiveForm.id ? '编辑客商档案' : '新增客商档案';
|
||||
},
|
||||
@@ -2151,6 +2179,14 @@ export default {
|
||||
scoreList() {
|
||||
return this.archiveForm.scores || [];
|
||||
},
|
||||
showScoreDelete() {
|
||||
if (this.readonly) return false;
|
||||
const status = this.archiveForm.approvalStatus || 'draft';
|
||||
return status === 'draft' || status === 'rejected';
|
||||
},
|
||||
isNewScore() {
|
||||
return this.scoreRecordIndex < 0 && !this.reviewMode;
|
||||
},
|
||||
scorePageCount() {
|
||||
return Math.max(Math.ceil(this.scoreList.length / this.scorePage.pageSize), 1);
|
||||
},
|
||||
@@ -3962,6 +3998,12 @@ export default {
|
||||
query: { id: row.id, name: '查看客商档案', view: '1' },
|
||||
});
|
||||
},
|
||||
openReview(row) {
|
||||
this.$router.push({
|
||||
path: '/vehicle/customer-archive/form',
|
||||
query: { id: row.id, name: '客商复评', review: '1' },
|
||||
});
|
||||
},
|
||||
closeArchive() {
|
||||
if (this.isPublicViewPage) return;
|
||||
this.archiveBox = false;
|
||||
@@ -3993,6 +4035,7 @@ export default {
|
||||
if (this.isPublicViewPage) this.ensurePublicViewOptions(this.archiveForm);
|
||||
this.archiveBox = true;
|
||||
this.loadChangeRecords();
|
||||
if (this.reviewMode) this.openReviewScore();
|
||||
})
|
||||
.catch(() => {
|
||||
this.archiveBox = true;
|
||||
@@ -4034,14 +4077,15 @@ export default {
|
||||
this.invoiceForm = this.emptyInvoice();
|
||||
this.$refs.archiveForm?.clearValidate();
|
||||
},
|
||||
// 客商材料不做提交校验,仅在页面上提示未上传项
|
||||
validateFormalForApproval(archive) {
|
||||
if (archive.accessType !== 'formal') return true;
|
||||
const hasCompletedScore = (archive.scores || []).some(
|
||||
item => item.selfStatus === '已完成' || item.reviewStatus === '已完成' || item.finalScore
|
||||
);
|
||||
if (!hasCompletedScore) {
|
||||
this.$message.warning('正式客商提交审核前必须完成信用评分');
|
||||
// 客商材料不做提交校验,仅在页面上提示未上传项。提交审批前必须完成自评,且评分记录只能有一条。
|
||||
validateScoreForApproval(archive) {
|
||||
const scores = archive.scores || [];
|
||||
if (scores.length > 1) {
|
||||
this.$message.warning('评分记录只能有一条');
|
||||
return false;
|
||||
}
|
||||
if (scores.length !== 1 || scores[0].selfStatus !== '已完成') {
|
||||
this.$message.warning('提交审批前必须完成自评');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -4151,7 +4195,7 @@ export default {
|
||||
const archive = this.normalizeArchive();
|
||||
// 仅提交时落变更记录;保存不记录
|
||||
archive.recordChange = true;
|
||||
if (!this.validateFormalForApproval(archive)) return;
|
||||
if (!this.validateScoreForApproval(archive)) return;
|
||||
submit(archive).then(res => {
|
||||
const data = res.data.data;
|
||||
const id = (data && typeof data === 'object' ? data.id : data) || this.archiveForm.id;
|
||||
@@ -4189,9 +4233,21 @@ export default {
|
||||
approvalStatus,
|
||||
}).then(() => submitApproval(id));
|
||||
},
|
||||
openReviewScore() {
|
||||
const scores = this.archiveForm.scores || [];
|
||||
if (!scores.length) {
|
||||
this.$message.warning('暂无评分记录,无法复评');
|
||||
return;
|
||||
}
|
||||
this.$nextTick(() => this.openScore(scores[0], 0));
|
||||
},
|
||||
addScore() {
|
||||
if ((this.archiveForm.scores || []).length) {
|
||||
this.$message.warning('请勿重复添加');
|
||||
return;
|
||||
}
|
||||
this.scoreRecordIndex = -1;
|
||||
this.currentScore = this.normalizeScore({});
|
||||
this.currentScore = this.normalizeScore({ applyCreditLimit: '' });
|
||||
this.scoreAttachmentFiles = [];
|
||||
this.expandedScoreCategoryKeys = [];
|
||||
this.scoreBox = true;
|
||||
@@ -4273,7 +4329,7 @@ export default {
|
||||
JSON.stringify(
|
||||
(item.options || []).map(option => ({
|
||||
label: option.label || option.optionName || option.value,
|
||||
value: option.value || option.optionName || option.label,
|
||||
value: option.value ?? option.optionName ?? option.label,
|
||||
score: option.score,
|
||||
changeType: option.changeType,
|
||||
changeValue: option.changeValue,
|
||||
@@ -4363,20 +4419,21 @@ export default {
|
||||
const isScoreOption = item.changeType || item.changeValue || item.changeUnit;
|
||||
const changeLabel = item.changeType === 'decrease' ? '每减少' : '每增加';
|
||||
const scoreLabel = item.scoreType === 'subtract' ? '减' : '加';
|
||||
const rawScore = item.score;
|
||||
const generatedLabel = isScoreOption
|
||||
? `${changeLabel}${item.changeValue || ''}${item.changeUnit || ''}${scoreLabel}${
|
||||
item.score || ''
|
||||
this.isBlankScore(rawScore) ? '' : rawScore
|
||||
}分`
|
||||
: '';
|
||||
const label = item.label || item.optionName || item.value || generatedLabel;
|
||||
return {
|
||||
...item,
|
||||
label,
|
||||
value: item.value || item.optionName || item.label || generatedLabel,
|
||||
value: item.value ?? item.optionName ?? item.label ?? generatedLabel,
|
||||
score:
|
||||
item.scoreType === 'subtract' && item.score !== undefined
|
||||
? -Math.abs(Number(item.score))
|
||||
: item.score,
|
||||
item.scoreType === 'subtract' && !this.isBlankScore(rawScore)
|
||||
? -Math.abs(Number(rawScore))
|
||||
: rawScore,
|
||||
};
|
||||
});
|
||||
if (Array.isArray(value)) return normalize(value);
|
||||
@@ -4440,23 +4497,35 @@ export default {
|
||||
const valid = increase ? input >= base : input <= base;
|
||||
if (!valid) {
|
||||
detail.selfScore = '';
|
||||
this.$message.warning(`输入值${increase ? '不能小于' : '不能大于'}基准数值${base}`);
|
||||
this.calculateScore(this.currentScore);
|
||||
return;
|
||||
}
|
||||
detail.selfScore = this.getScoreTypeCalculatedScore(detail);
|
||||
this.calculateScore(this.currentScore);
|
||||
},
|
||||
handleScoreValueBlur(detail) {
|
||||
const normalized = String(detail.scoreInput ?? '').trim();
|
||||
if (!normalized || normalized === '-' || normalized === '.') return;
|
||||
const rule = this.getScoreRule(detail);
|
||||
const base = Number(detail.baseValue);
|
||||
const input = Number(normalized);
|
||||
if (!rule || !Number.isFinite(base) || !Number.isFinite(input)) return;
|
||||
const increase = rule.changeType !== 'decrease';
|
||||
const valid = increase ? input >= base : input <= base;
|
||||
if (!valid) {
|
||||
this.$message.warning(`输入值${increase ? '不能小于' : '不能大于'}基准数值${base}`);
|
||||
}
|
||||
},
|
||||
scoreOptionChange(detail, optionValue) {
|
||||
const option = this.parseScoreOptions(detail.optionsJson).find(
|
||||
item => item.value === optionValue
|
||||
);
|
||||
const score = Number(option?.score || 0);
|
||||
detail.selfScore = score;
|
||||
const rawScore = option?.score;
|
||||
detail.selfScore = this.isBlankScore(rawScore) ? 0 : Number(rawScore);
|
||||
this.calculateScore(this.currentScore);
|
||||
},
|
||||
handleScoreDetailScoreInput(detail, prop, value) {
|
||||
const normalized = String(value || '')
|
||||
const normalized = String(value ?? '')
|
||||
.replace(/[^\d.]/g, '')
|
||||
.replace(/^\./, '')
|
||||
.replace(/(\..*)\./g, '$1')
|
||||
@@ -4588,8 +4657,7 @@ export default {
|
||||
sumScore(plusDetails, 'reviewScore') -
|
||||
sumScore(minusDetails, 'reviewScore');
|
||||
const hasReviewScore = details.some(
|
||||
item =>
|
||||
item.reviewScore !== undefined && item.reviewScore !== null && item.reviewScore !== ''
|
||||
item => !this.isBlankScore(item.reviewScore)
|
||||
);
|
||||
const finalScore = hasReviewScore ? reviewScore : selfScore;
|
||||
const fullMark = this.getScoreFullMark(details);
|
||||
@@ -4609,13 +4677,17 @@ export default {
|
||||
}
|
||||
return score;
|
||||
},
|
||||
isBlankScore(value) {
|
||||
return value === undefined || value === null || String(value).trim() === '';
|
||||
},
|
||||
hasIncompleteBasicScoreDetail(details = []) {
|
||||
return this.getScoreCategoryDetailsByList(details, 'basic').some(item => {
|
||||
if (this.isScoreTypeDetail(item)) {
|
||||
const input = String(item.scoreInput ?? '').trim();
|
||||
return !input || !Number.isFinite(Number(input)) || item.selfScore === '';
|
||||
const inputBlank = input === '' || input === '-' || input === '.';
|
||||
return inputBlank || !Number.isFinite(Number(input)) || this.isBlankScore(item.selfScore);
|
||||
}
|
||||
return !item.selectedOption;
|
||||
return this.isBlankScore(item.selectedOption);
|
||||
});
|
||||
},
|
||||
finishScore(score) {
|
||||
@@ -4632,10 +4704,13 @@ export default {
|
||||
score.reviewStatus = '已完成';
|
||||
this.$message.success('评分已完成');
|
||||
},
|
||||
saveScoreDetail() {
|
||||
saveScoreDetail(successMessage) {
|
||||
if (!this.validateScoreForm(false)) return;
|
||||
this.currentScore.attachments = JSON.parse(JSON.stringify(this.scoreAttachmentFiles || []));
|
||||
this.currentScore.proofAttachments = this.stringifyAttachments(this.scoreAttachmentFiles);
|
||||
this.currentScore.selfStatus = this.hasIncompleteBasicScoreDetail(this.currentScore.details)
|
||||
? '未完成'
|
||||
: '已完成';
|
||||
this.calculateScore(this.currentScore);
|
||||
const score = JSON.parse(JSON.stringify(this.currentScore));
|
||||
if (this.scoreRecordIndex > -1) {
|
||||
@@ -4647,23 +4722,62 @@ export default {
|
||||
this.syncArchiveCreditFromScores();
|
||||
this.activeTab = 'scores';
|
||||
this.scoreBox = false;
|
||||
this.saveScoreArchive();
|
||||
},
|
||||
saveScoreArchive() {
|
||||
if (!this.archiveForm.id) {
|
||||
this.$message.success('评分记录已添加,请保存客商档案后生效');
|
||||
return;
|
||||
const pending = this.saveScoreArchive(successMessage);
|
||||
if (this.reviewMode && successMessage && pending && pending.then) {
|
||||
pending.then(() => this.closeArchive());
|
||||
}
|
||||
submit(this.normalizeArchive()).then(() => {
|
||||
this.$message.success('评分记录已保存');
|
||||
return pending;
|
||||
},
|
||||
deleteScore(index) {
|
||||
this.$confirm('确定删除该评分记录?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
if (!Array.isArray(this.archiveForm.scores)) this.archiveForm.scores = [];
|
||||
this.archiveForm.scores.splice(index, 1);
|
||||
if (this.scoreRecordIndex === index) {
|
||||
this.scoreBox = false;
|
||||
} else if (this.scoreRecordIndex > index) {
|
||||
this.scoreRecordIndex -= 1;
|
||||
}
|
||||
const latestScore = this.getLatestCreditScore(this.archiveForm.scores);
|
||||
if (latestScore) {
|
||||
this.syncArchiveCreditFromScores();
|
||||
} else {
|
||||
this.archiveForm.customerLevel = '';
|
||||
this.archiveForm.maxCreditLimit = '';
|
||||
this.archiveForm.applyCreditLimit = '';
|
||||
}
|
||||
this.handleScoreCurrentChange(this.scorePage.currentPage);
|
||||
this.saveScoreArchive('评分记录已删除', '评分记录已删除,请保存客商档案后生效');
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
saveScoreArchive(
|
||||
successMessage = '评分记录已保存',
|
||||
draftMessage = '评分记录已添加,请保存客商档案后生效'
|
||||
) {
|
||||
if (!this.archiveForm.id) {
|
||||
this.$message.success(draftMessage);
|
||||
return Promise.resolve();
|
||||
}
|
||||
return submit(this.normalizeArchive()).then(() => {
|
||||
this.$message.success(successMessage);
|
||||
});
|
||||
},
|
||||
submitSelfScore() {
|
||||
if (!this.validateScoreForm(true)) return;
|
||||
this.currentScore.selfStatus = '已完成';
|
||||
this.currentScore.reviewStatus = '未完成';
|
||||
this.saveScoreDetail('自评已提交');
|
||||
},
|
||||
confirmReviewScore() {
|
||||
if (!this.validateScoreForm(true)) return;
|
||||
this.currentScore.selfStatus = '已完成';
|
||||
this.currentScore.reviewStatus = '已完成';
|
||||
this.saveScoreDetail();
|
||||
this.$message.success('复评确认成功');
|
||||
this.saveScoreDetail('复评确认成功');
|
||||
},
|
||||
validateScoreForm(requireComplete) {
|
||||
if (!this.currentScore.scoreDate) {
|
||||
@@ -4701,7 +4815,7 @@ export default {
|
||||
if (
|
||||
item.reviewScore === undefined ||
|
||||
item.reviewScore === null ||
|
||||
item.reviewScore === ''
|
||||
String(item.reviewScore).trim() === ''
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -4718,7 +4832,7 @@ export default {
|
||||
return false;
|
||||
}
|
||||
if (requireComplete && this.hasIncompleteBasicScoreDetail(this.currentScore.details)) {
|
||||
this.$message.warning('请完整填写基础得分项评分明细');
|
||||
this.$message.warning('请完整填写所有基础项目后再提交自评');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -4773,7 +4887,7 @@ export default {
|
||||
getDetail(row.id).then(res => {
|
||||
const archive = this.normalizeDetail(res.data.data);
|
||||
archive.qualificationAttachments = archive.qualificationAttachments || '';
|
||||
if (!this.validateFormalForApproval(archive)) return;
|
||||
if (!this.validateScoreForApproval(archive)) return;
|
||||
this.$confirm('是否提交客商准入审批?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
@@ -5260,10 +5374,16 @@ export default {
|
||||
}
|
||||
|
||||
:deep(.el-table__body td) {
|
||||
//min-height: 112px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
:deep(.score-item-name) {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
:deep(.el-select),
|
||||
:deep(.el-input),
|
||||
:deep(.el-select) {
|
||||
@@ -5446,27 +5566,6 @@ export default {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
:deep(.change-record-detail-dialog .el-dialog__body) {
|
||||
max-height: 65vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:deep(.change-record-detail-dialog .change-record-detail-value .cell) {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.change-record-detail-meta {
|
||||
display: flex;
|
||||
gap: 32px;
|
||||
margin-bottom: 16px;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.customer-archive-page.is-public-view {
|
||||
:deep(.archive-page-form .archive-form__footer) {
|
||||
left: 0;
|
||||
|
||||
+7
-4
@@ -570,19 +570,22 @@
|
||||
|
||||
- 功能点:
|
||||
- 任意状态的运单均支持复制。
|
||||
- 复制后生成新的草稿运单,运单号重新生成。
|
||||
- 复制时保留运单基础信息、货物信息、承运信息和过程配置相关信息,状态与过程记录重新初始化。
|
||||
- 复制成功后跳转编辑页面。
|
||||
- 复制后运单号重新生成,业务状态与原运单保持一致。
|
||||
- 复制时保留运单基础信息、货物信息、承运信息和过程配置相关信息;过程打卡记录重新初始化。
|
||||
- 若原运单为已完成,复制保存后按合同系统计费规则自动生成对应应收、应付明细。
|
||||
- 复制成功后跳转编辑页面(独立表单模式)或刷新列表(弹窗模式)。
|
||||
|
||||
- 异常与边界:
|
||||
- 原运单不存在时提示数据不存在。
|
||||
- 复制后名称、编号或关联字段触发唯一性校验时应重新处理。
|
||||
- 复制失败时不影响原运单数据。
|
||||
- 合同未开启系统计费、无匹配计费方案或自有运输等场景下,按既有规则跳过对应应收/应付生成。
|
||||
|
||||
- 反例:
|
||||
- 不允许复制后沿用原运单号。
|
||||
- 不允许复制后直接生成进行中或已完成状态。
|
||||
- 不允许复制后状态与原运单不一致。
|
||||
- 不允许复制后保留原过程节点完成记录。
|
||||
- 不允许已完成运单复制成功后遗漏按规则应生成的应收应付明细。
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user