Files
tms-erp-web/src/views/transportCapacity/components/cert-audit-diff/main.vue
T
weicw-dshsa 7c6ac2e5db ✨ 新增认证审核-司机/车辆页,审核详情按字段级 diff 对比待审值与生效值
审核对象由「司机行 / 车辆行」改为「一条认证记录」,故新增两个待审列表页
(挂在运力管理下,path /transportCapacity/cert-audit-driver|vehicle,与页面
文件名一一对应)与审核详情弹窗:

- api/transportCapacity/certification-audit.js:pending-page / detail / audit 三个接口
- utils/cert-audit.js:快照字段元数据(中文名、控件类型、分组)与两侧快照的
  字段级 diff 计算。两侧字段名一致(同一套快照结构),故逐键比较;日期归一化后
  判等、长期有效联动展示、未登记字段兜底进「其他字段」
- components/cert-audit-diff:当前生效值与待审提交值并排展示,差异行高亮并标注
  新增 / 已修改 / 已清空,支持切换只看变更项;从未认证通过(生效快照为空)时
  显示「无对比基准」,不报错
- components/cert-audit-detail:审核详情弹窗(提交信息 + diff + 审核意见),
  通过 / 驳回动作。驳回原因前端先拦一道(必填且不超过 200 字),通过时不提交原因;
  已终态记录不可再审
- components/cert-audit-list + 两个菜单页:分页与按展示键(司机姓名 / 车牌号)筛选,
  审核链接按 cert_certification_audit_{driver,vehicle}_audit 权限点控制

检索过的现有实现:driver.vue / vehicle.vue 的列表与弹窗写法(Avue option + search、
权限点 hasPermission 写法)、image-upload-field、section-card、utils/table-menu.js。
未找到可复用的快照或 diff 实现,故新增 utils/cert-audit.js 纯函数承担比对;
两个列表页除 bizType 与权限点外完全相同,故抽出通用列表组件承接分页/搜索,
避免两页逻辑各自演化。
2026-10-02 17:15:03 +08:00

247 lines
6.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="cert-audit-diff">
<el-alert
v-if="diff.effectiveInvalid"
type="error"
:closable="false"
show-icon
title="当前生效快照内容无法解析,本次不展示对比结果"
class="cert-audit-diff__notice"
/>
<el-alert
v-else-if="!diff.hasBaseline"
type="warning"
:closable="false"
show-icon
title="该认证对象从未审核通过,没有生效快照可对比(无对比基准)"
class="cert-audit-diff__notice"
>
下方为本次提交的全部内容,审核通过后即成为该对象的当前生效值。
</el-alert>
<el-alert
v-else-if="diff.changedCount === 0"
type="success"
:closable="false"
show-icon
title="本次提交内容与当前生效值完全一致,没有任何字段发生变化"
class="cert-audit-diff__notice"
/>
<el-alert
v-else
type="info"
:closable="false"
show-icon
:title="`共 ${diff.changedCount} / ${diff.totalCount} 项内容与当前生效值不同,已在下方高亮标记`"
class="cert-audit-diff__notice"
/>
<div class="cert-audit-diff__toolbar">
<el-switch
v-model="onlyChanged"
:disabled="!diff.hasBaseline"
active-text="仅看变更项"
inactive-text="展示全部"
/>
</div>
<section-card
v-for="group in visibleGroups"
:key="group.title"
:title="group.title"
class="cert-audit-diff__group"
>
<template #extra>
<span v-if="diff.hasBaseline" class="cert-audit-diff__extra">
变更 {{ group.changedCount }} 项
</span>
</template>
<el-table
:data="group.rows"
border
size="small"
:row-class-name="rowClassName"
class="cert-audit-diff__table"
>
<el-table-column label="字段" prop="label" width="190" />
<el-table-column :label="effectiveColumnLabel" min-width="240">
<template #default="{ row }">
<el-image
v-if="diff.hasBaseline && row.type === 'image' && row.effective.raw"
:src="row.effective.raw"
:preview-src-list="[row.effective.raw]"
preview-teleported
:z-index="3000"
fit="contain"
class="cert-audit-diff__image"
/>
<span
v-else-if="diff.hasBaseline && row.type !== 'image'"
class="cert-audit-diff__value"
>
{{ row.effective.text }}
</span>
<span v-else class="cert-audit-diff__absent">—</span>
</template>
</el-table-column>
<el-table-column label="待审提交值" min-width="240">
<template #default="{ row }">
<el-image
v-if="row.type === 'image' && row.pending.raw"
:src="row.pending.raw"
:preview-src-list="[row.pending.raw]"
preview-teleported
:z-index="3000"
fit="contain"
class="cert-audit-diff__image"
/>
<span v-else-if="row.type === 'image'" class="cert-audit-diff__absent">—</span>
<span
v-else
class="cert-audit-diff__value"
:class="{ 'cert-audit-diff__value--changed': row.changed }"
>
{{ row.pending.text }}
</span>
</template>
</el-table-column>
<el-table-column label="差异" width="96" align="center">
<template #default="{ row }">
<el-tag v-if="row.changeType" :type="changeTag(row.changeType).type" size="small">
{{ changeTag(row.changeType).label }}
</el-tag>
<span v-else class="cert-audit-diff__absent">—</span>
</template>
</el-table-column>
</el-table>
</section-card>
<el-empty v-if="!visibleGroups.length" :description="emptyText" />
</div>
</template>
<script>
import { buildSnapshotDiff, CERT_CHANGE_TAG } from '@/utils/cert-audit';
export default {
name: 'CertAuditDiff',
props: {
// 认证对象类型:driver / vehicle
bizType: {
type: String,
default: 'driver',
},
// 本次待审快照 JSON
pendingJson: {
type: [String, Object],
default: '',
},
// 该对象当前生效快照 JSON,从未认证通过时为空
effectiveJson: {
type: [String, Object],
default: '',
},
},
data() {
return {
// 默认并排展示两侧全部字段(差异行高亮),审核员可切换为只看变更项
onlyChanged: false,
};
},
computed: {
diff() {
return buildSnapshotDiff(this.bizType, this.pendingJson, this.effectiveJson);
},
visibleGroups() {
// 无对比基准时不存在「变更项」,强制展示全部内容
const onlyChanged = this.onlyChanged && this.diff.hasBaseline;
return this.diff.groups
.map(group => ({
...group,
rows: onlyChanged ? group.rows.filter(row => row.changed) : group.rows,
}))
.filter(group => group.rows.length);
},
emptyText() {
if (this.diff.hasBaseline && this.onlyChanged && this.diff.changedCount === 0) {
return '本次提交与当前生效值完全一致,切换为「展示全部」可查看明细';
}
return '本次提交没有可对比的内容';
},
// 无对比基准时在列头直接写明,避免逐行重复「无对比基准」
effectiveColumnLabel() {
return this.diff.hasBaseline ? '当前生效值' : '当前生效值(无对比基准)';
},
},
watch: {
pendingJson() {
this.onlyChanged = false;
},
effectiveJson() {
this.onlyChanged = false;
},
},
methods: {
changeTag(changeType) {
return CERT_CHANGE_TAG[changeType] || { label: changeType, type: 'info' };
},
rowClassName({ row }) {
return row.changed ? 'cert-audit-diff__row--changed' : '';
},
},
};
</script>
<style lang="scss" scoped>
.cert-audit-diff {
&__notice {
margin-bottom: 8px;
}
&__toolbar {
display: flex;
justify-content: flex-end;
padding: 0 0 8px;
}
&__group {
margin-bottom: 8px;
}
&__extra {
color: #909399;
font-size: 12px;
}
&__value {
word-break: break-all;
&--changed {
color: #e6a23c;
font-weight: 600;
}
}
&__absent {
color: #c0c4cc;
}
&__image {
width: 56px;
height: 56px;
border: 1px solid #eff1f7;
border-radius: 4px;
cursor: pointer;
}
&__table {
:deep(.el-table__row.cert-audit-diff__row--changed) {
background-color: #fdf6ec;
}
:deep(.el-table__row.cert-audit-diff__row--changed td.el-table__cell) {
background-color: #fdf6ec;
}
}
}
</style>