调整风险人员总览核心异常点标签展示
This commit is contained in:
@@ -0,0 +1,30 @@
|
|||||||
|
# 风险人员总览核心异常点标签化展示实施记录
|
||||||
|
|
||||||
|
## 本次改动
|
||||||
|
|
||||||
|
- 将风险人员总览中的“核心异常点”从纯文本展示改为标签列表展示,样式与“命中模型涉及人员”的异常标签保持一致。
|
||||||
|
- 调整 [`RiskPeopleSection.vue`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/src/views/ccdiProject/components/detail/RiskPeopleSection.vue):
|
||||||
|
- 新增 `riskPointTagList` 归一化逻辑。
|
||||||
|
- 兼容后端返回标签数组、字符串数组、以及历史 `riskPoint` 拼接字符串三种输入形式。
|
||||||
|
- 按风险等级映射 `el-tag` 颜色,空值场景显示 `-`。
|
||||||
|
- 调整 [`preliminaryCheck.mock.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/src/views/ccdiProject/components/detail/preliminaryCheck.mock.js),补充标签列 mock 数据。
|
||||||
|
- 新增 [`preliminary-check-risk-people-hit-tags.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-risk-people-hit-tags.test.js) 锁定核心异常点标签化渲染结构。
|
||||||
|
|
||||||
|
## 实现说明
|
||||||
|
|
||||||
|
- 不修改后端接口口径,展示层在前端做最小归一化处理。
|
||||||
|
- 若接口继续返回 `riskPoint` 字符串,则按 `、`、`,`、`,`、`;`、`;` 拆分为多个标签。
|
||||||
|
- 若接口后续直接返回 `riskPointTagList`,则优先使用该字段,避免重复拆分。
|
||||||
|
|
||||||
|
## 验证情况
|
||||||
|
|
||||||
|
- 前端单测:
|
||||||
|
- `node ruoyi-ui/tests/unit/preliminary-check-risk-people-hit-tags.test.js`
|
||||||
|
- `node ruoyi-ui/tests/unit/preliminary-check-risk-people-binding.test.js`
|
||||||
|
- `node ruoyi-ui/tests/unit/preliminary-check-summary-and-people.test.js`
|
||||||
|
|
||||||
|
## 未包含内容
|
||||||
|
|
||||||
|
- 未调整风险人员总览接口返回结构
|
||||||
|
- 未改动风险等级口径与统计逻辑
|
||||||
|
- 未改动“命中模型涉及人员”区块的接口或交互
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<el-button size="mini" type="text">导出</el-button>
|
<el-button size="mini" type="text">导出</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table :data="sectionData.overviewList || []" class="people-table">
|
<el-table :data="overviewList" class="people-table">
|
||||||
<el-table-column type="index" label="序号" width="60" />
|
<el-table-column type="index" label="序号" width="60" />
|
||||||
<el-table-column prop="name" label="姓名" min-width="100" />
|
<el-table-column prop="name" label="姓名" min-width="100" />
|
||||||
<el-table-column prop="idNo" label="身份证号" min-width="180" />
|
<el-table-column prop="idNo" label="身份证号" min-width="180" />
|
||||||
@@ -24,7 +24,25 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="modelCount" label="命中模型数" min-width="110" />
|
<el-table-column prop="modelCount" label="命中模型数" min-width="110" />
|
||||||
<el-table-column prop="riskPoint" label="核心异常点" min-width="220" />
|
<el-table-column label="核心异常点" min-width="220">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<div
|
||||||
|
v-if="scope.row.riskPointTagList && scope.row.riskPointTagList.length"
|
||||||
|
class="risk-point-tag-list"
|
||||||
|
>
|
||||||
|
<el-tag
|
||||||
|
v-for="(tag, index) in scope.row.riskPointTagList"
|
||||||
|
:key="`${scope.row.idNo || scope.row.name || index}-risk-point-${index}`"
|
||||||
|
size="mini"
|
||||||
|
effect="plain"
|
||||||
|
:type="mapRiskLevelToTagType(tag.riskLevel)"
|
||||||
|
>
|
||||||
|
{{ tag.ruleName }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
<span v-else class="empty-text">-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="100" align="right">
|
<el-table-column label="操作" width="100" align="right">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button type="text" size="mini">{{ scope.row.actionLabel || "查看详情" }}</el-button>
|
<el-button type="text" size="mini">{{ scope.row.actionLabel || "查看详情" }}</el-button>
|
||||||
@@ -37,6 +55,52 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
function normalizeRiskPointTags(tags, riskPoint, riskLevel) {
|
||||||
|
if (Array.isArray(tags) && tags.length) {
|
||||||
|
return tags
|
||||||
|
.map((item) => {
|
||||||
|
if (typeof item === "string") {
|
||||||
|
return {
|
||||||
|
ruleName: item.trim(),
|
||||||
|
riskLevel,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (item && typeof item === "object") {
|
||||||
|
return {
|
||||||
|
ruleName: item.ruleName || item.label || item.name || "",
|
||||||
|
riskLevel: item.riskLevel || riskLevel,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter((item) => item && item.ruleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!riskPoint) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return String(riskPoint)
|
||||||
|
.split(/[、,,;;]/)
|
||||||
|
.map((item) => item.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((item) => ({
|
||||||
|
ruleName: item,
|
||||||
|
riskLevel,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeOverviewRows(rows) {
|
||||||
|
if (!Array.isArray(rows)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows.map((item) => ({
|
||||||
|
...item,
|
||||||
|
riskPointTagList: normalizeRiskPointTags(item.riskPointTagList, item.riskPoint, item.riskLevelType),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "RiskPeopleSection",
|
name: "RiskPeopleSection",
|
||||||
props: {
|
props: {
|
||||||
@@ -45,6 +109,23 @@ export default {
|
|||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
overviewList() {
|
||||||
|
return normalizeOverviewRows(this.sectionData.overviewList);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
mapRiskLevelToTagType(riskLevel) {
|
||||||
|
const level = String(riskLevel || "").toUpperCase();
|
||||||
|
if (level === "HIGH" || level === "DANGER") {
|
||||||
|
return "danger";
|
||||||
|
}
|
||||||
|
if (level === "MEDIUM" || level === "WARNING") {
|
||||||
|
return "warning";
|
||||||
|
}
|
||||||
|
return "info";
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -88,4 +169,14 @@ export default {
|
|||||||
background: #f8fafc;
|
background: #f8fafc;
|
||||||
color: #64748b;
|
color: #64748b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.risk-point-tag-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
color: #94a3b8;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ export const mockOverviewData = {
|
|||||||
riskLevelType: "danger",
|
riskLevelType: "danger",
|
||||||
modelCount: 3,
|
modelCount: 3,
|
||||||
riskPoint: "跨地域转账频繁交易",
|
riskPoint: "跨地域转账频繁交易",
|
||||||
|
riskPointTagList: [
|
||||||
|
{ ruleName: "跨地域转账", riskLevel: "HIGH" },
|
||||||
|
{ ruleName: "频繁交易", riskLevel: "HIGH" },
|
||||||
|
],
|
||||||
actionLabel: "查看详情",
|
actionLabel: "查看详情",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -33,6 +37,10 @@ export const mockOverviewData = {
|
|||||||
riskLevelType: "warning",
|
riskLevelType: "warning",
|
||||||
modelCount: 2,
|
modelCount: 2,
|
||||||
riskPoint: "多工资转入频繁交易",
|
riskPoint: "多工资转入频繁交易",
|
||||||
|
riskPointTagList: [
|
||||||
|
{ ruleName: "多工资转入", riskLevel: "MEDIUM" },
|
||||||
|
{ ruleName: "频繁交易", riskLevel: "MEDIUM" },
|
||||||
|
],
|
||||||
actionLabel: "查看详情",
|
actionLabel: "查看详情",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -44,6 +52,9 @@ export const mockOverviewData = {
|
|||||||
riskLevelType: "info",
|
riskLevelType: "info",
|
||||||
modelCount: 1,
|
modelCount: 1,
|
||||||
riskPoint: "频繁小额转账",
|
riskPoint: "频繁小额转账",
|
||||||
|
riskPointTagList: [
|
||||||
|
{ ruleName: "频繁小额转账", riskLevel: "LOW" },
|
||||||
|
],
|
||||||
actionLabel: "查看详情",
|
actionLabel: "查看详情",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
const assert = require("assert");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const source = fs.readFileSync(
|
||||||
|
path.resolve(__dirname, "../../src/views/ccdiProject/components/detail/RiskPeopleSection.vue"),
|
||||||
|
"utf8"
|
||||||
|
);
|
||||||
|
|
||||||
|
[
|
||||||
|
"risk-point-tag-list",
|
||||||
|
"scope.row.riskPointTagList",
|
||||||
|
"normalizeRiskPointTags",
|
||||||
|
":type=\"mapRiskLevelToTagType(tag.riskLevel)\"",
|
||||||
|
].forEach((token) => assert(source.includes(token), token));
|
||||||
|
|
||||||
|
assert(!source.includes('<el-table-column prop="riskPoint" label="核心异常点" min-width="220" />'));
|
||||||
Reference in New Issue
Block a user