7c6ac2e5db
审核对象由「司机行 / 车辆行」改为「一条认证记录」,故新增两个待审列表页
(挂在运力管理下,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 与权限点外完全相同,故抽出通用列表组件承接分页/搜索,
避免两页逻辑各自演化。
247 lines
6.8 KiB
Vue
247 lines
6.8 KiB
Vue
<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>
|