完善结果总览模型区卡片展示与联动修复

This commit is contained in:
wkc
2026-03-20 14:02:18 +08:00
parent 726265fb70
commit 538fb9c9f3
10 changed files with 128 additions and 18 deletions

View File

@@ -43,4 +43,12 @@ public class CcdiProjectRiskModelPeopleQueryDTO {
.distinct()
.collect(Collectors.joining(","));
}
public Integer getModelCodesCount() {
String modelCodesCsv = getModelCodesCsv();
if (modelCodesCsv == null || modelCodesCsv.isBlank()) {
return 0;
}
return modelCodesCsv.split(",").length;
}
}

View File

@@ -199,14 +199,28 @@
<select id="selectRiskModelCardsByProjectId" resultType="com.ruoyi.ccdi.project.domain.vo.CcdiProjectRiskModelCardVO">
select
base.model_code,
max(base.model_name) as model_name,
count(1) as warning_count,
count(distinct base.staff_id_card) as people_count
models.model_code,
models.model_name,
coalesce(stats.warning_count, 0) as warning_count,
coalesce(stats.people_count, 0) as people_count
from (
<include refid="resolvedEmployeeRiskBaseSql"/>
) base
group by base.model_code
select
rule.model_code,
max(rule.model_name) as model_name
from ccdi_bank_tag_rule rule
where enabled = 1
group by rule.model_code
) models
left join (
select
base.model_code,
count(1) as warning_count,
count(distinct base.staff_id_card) as people_count
from (
<include refid="resolvedEmployeeRiskBaseSql"/>
) base
group by base.model_code
) stats on models.model_code = stats.model_code
order by warning_count desc, model_code asc
</select>
@@ -241,7 +255,7 @@
</if>
group by base.project_id, base.staff_id_card
<if test="query.modelCodes != null and query.modelCodes.size() > 0 and query.matchMode == 'ALL'">
having count(distinct base.model_code) = #{query.modelCodes.size}
having count(distinct base.model_code) = #{query.modelCodesCount}
</if>
order by max(base.staff_name) asc, base.staff_id_card asc
</select>

View File

@@ -21,9 +21,14 @@ class CcdiProjectOverviewMapperRiskModelCardsTest {
String xml = Files.readString(Path.of("src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml"));
assertTrue(xml.contains("<select id=\"selectRiskModelCardsByProjectId\""));
assertTrue(xml.contains("from ("));
assertTrue(xml.contains("from ccdi_bank_tag_rule"));
assertTrue(xml.contains("where enabled = 1"));
assertTrue(xml.contains("left join ("));
assertTrue(xml.contains("<include refid=\"resolvedEmployeeRiskBaseSql\"/>"));
assertTrue(xml.contains("coalesce(stats.warning_count, 0) as warning_count"));
assertTrue(xml.contains("coalesce(stats.people_count, 0) as people_count"));
assertTrue(xml.contains("count(1) as warning_count"));
assertTrue(xml.contains("count(distinct base.staff_id_card) as people_count"));
assertTrue(xml.contains("order by warning_count desc, model_code asc"));
}
}

View File

@@ -33,7 +33,8 @@ class CcdiProjectOverviewMapperRiskModelPeopleTest {
assertTrue(xml.contains("<select id=\"selectRiskModelPeoplePage\""));
assertTrue(xml.contains("query.modelCodes != null and query.modelCodes.size() > 0"));
assertTrue(xml.contains("query.matchMode == 'ALL'"));
assertTrue(xml.contains("count(distinct base.model_code) = #{query.modelCodes.size}"));
assertFalse(xml.contains("#{query.modelCodes.size}"));
assertTrue(xml.contains("count(distinct base.model_code) = #{query.modelCodesCount}"));
assertTrue(xml.contains("<bind name=\"projectId\" value=\"query.projectId\"/>"));
assertTrue(xml.contains("base.staff_name like concat('%', trim(#{query.keyword}), '%')"));
assertTrue(xml.contains("cast(base.staff_code as char) like concat('%', trim(#{query.keyword}), '%')"));

View File

@@ -9,16 +9,19 @@
- 新增或扩展 [`CcdiProjectOverviewControllerContractTest.java`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiProjectOverviewControllerContractTest.java)、[`CcdiProjectOverviewMapperRiskModelCardsTest.java`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperRiskModelCardsTest.java)、[`CcdiProjectOverviewMapperRiskModelPeopleTest.java`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperRiskModelPeopleTest.java)、[`CcdiProjectOverviewServiceImplTest.java`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectOverviewServiceImplTest.java) 以覆盖契约、SQL 和服务封装。
- 根据 2026-03-20 线上分页异常,在 [`CcdiProjectOverviewMapper.xml`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml) 的 `selectRiskModelPeoplePage` 增加 `<bind name="projectId" value="query.projectId"/>`,并在 [`CcdiProjectOverviewMapperRiskModelPeopleTest.java`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperRiskModelPeopleTest.java) 补充对应断言,防止再次出现参数绑定异常。
- 根据 2026-03-20 线上 MySQL 3065 异常,将 [`CcdiProjectOverviewMapper.xml`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml) 中 `selectRiskModelNamesByScope``select distinct` 调整为 `group by scoped.model_code, scoped.model_name`,并在 [`CcdiProjectOverviewMapperRiskModelPeopleTest.java`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperRiskModelPeopleTest.java) 补充兼容性断言。
- 根据 2026-03-20 线上 `matchMode=ALL``UnsupportedOperationException`,在 [`CcdiProjectRiskModelPeopleQueryDTO.java`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiProjectRiskModelPeopleQueryDTO.java) 新增显式只读字段 `modelCodesCount`,并将 [`CcdiProjectOverviewMapper.xml`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml) 中 `having count(distinct base.model_code) = #{query.modelCodes.size}` 改为 `#{query.modelCodesCount}`,同时更新 [`CcdiProjectOverviewMapperRiskModelPeopleTest.java`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperRiskModelPeopleTest.java) 回归断言。
- 根据“模型触发次数为 0 也要展示卡片”的新要求,将 [`CcdiProjectOverviewMapper.xml`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectOverviewMapper.xml) 中 `selectRiskModelCardsByProjectId` 从命中结果直聚合改为以 `ccdi_bank_tag_rule` 启用模型为主表、左连项目命中聚合,并更新 [`CcdiProjectOverviewMapperRiskModelCardsTest.java`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectOverviewMapperRiskModelCardsTest.java) 回归断言。
## 处理说明
- 继续复用现有 `CcdiProjectOverviewController + Service + Mapper` 结果总览链路,没有新增平行模块或补丁式接口。
- 模型卡片统计与人员分页统一建立在“员工本人 + 亲属归并到员工名下”的基础归并 SQL 上,确保与既有风险人员口径一致。
- 模型卡片展示口径以 `ccdi_bank_tag_rule` 中启用的全量模型定义为准,不再只返回已命中的模型;卡片命中统计与人员分页统一建立在“员工本人 + 亲属归并到员工名下”的基础归并 SQL 上,确保与既有风险人员口径一致。
- 人员分页中的员工工号使用 `ccdi_base_staff.staff_id` 作为真实字段来源,并在 SQL 中转换为字符后映射到 `staffCode`
- `ANY` 模式通过模型范围过滤直接返回并集结果;`ALL` 模式通过 `having count(distinct base.model_code) = #{query.modelCodes.size}` 约束交集结果。
- `ANY` 模式通过模型范围过滤直接返回并集结果;`ALL` 模式通过 `having count(distinct base.model_code) = #{query.modelCodesCount}` 约束交集结果,避免在 MyBatis 参数绑定阶段直接读取集合包装器属性
- `modelNames``hitTagList` 使用同上下文子查询回填,只返回当前筛选模型范围内的数据。
- 风险模型人员分页复用公共归并 SQL 时,必须先把 `query.projectId` 绑定到顶层 `projectId`,否则 `resolvedEmployeeRiskBaseSql` 中的 `#{projectId}` 会在 MyBatis 分页查询阶段直接报绑定异常。
- 风险模型名称子查询需要按 `model_code` 保序返回,但不能使用 `distinct + order by 非 select 列` 组合;这里改为 `group by scoped.model_code, scoped.model_name` 后再排序,以兼容当前 MySQL 配置。
- 风险模型卡片查询改为从 `ccdi_bank_tag_rule` 先聚合出启用模型,再左连项目命中统计,`warningCount/peopleCount` 统一通过 `coalesce(..., 0)` 回填,确保 0 次模型卡片也能展示。
- 服务层对 `matchMode` 缺省值统一收口为 `ANY`,并确保空列表返回空数组、分页行统一附加“查看详情”。
## 验证情况
@@ -38,4 +41,12 @@ mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewServiceImplTest
mvn test -pl ccdi-project '-Dtest=CcdiProjectOverview*'
```
- 总体验证结果22 个测试全部通过0 failure0 error。
- 已执行重新打包与真实接口回归:
```bash
mvn -pl ruoyi-admin -am package -DskipTests
java -jar ruoyi-admin/target/ruoyi-admin.jar --server.port=62319
```
- 真实接口回归覆盖 `/risk-models/cards`、人员默认分页、单模型 `ANY`、单模型 `ALL`,项目 `42` 的实际返回均为 `code=200`,其中卡片接口返回 `10` 张模型卡片,仅 `1` 张存在真实命中,其余 `9` 张为 `warningCount=0``peopleCount=0`
- 总体验证结果22 个测试全部通过0 failure0 error临时启动的 `62319` 实例已在验证完成后关闭。

View File

@@ -6,7 +6,7 @@
- 在 [`PreliminaryCheck.vue`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue) 接入模型卡片真实请求,并把模型区列表查询职责下沉到 [`RiskModelSection.vue`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/src/views/ccdiProject/components/detail/RiskModelSection.vue)。
- 在 [`preliminaryCheck.mock.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/src/views/ccdiProject/components/detail/preliminaryCheck.mock.js) 清理旧的模型筛选 mock 依赖,补齐模型卡片归一化逻辑和空态结构。
- 在 [`RiskModelSection.vue`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/src/views/ccdiProject/components/detail/RiskModelSection.vue) 实现多卡片选中/取消、`ANY / ALL` 匹配方式、关键词与部门筛选、分页请求、模型摘要和异常标签展示。
- 新增或更新 [`preliminary-check-model-api.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-model-api.test.js)、[`preliminary-check-model-data-loading.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-model-data-loading.test.js)、[`preliminary-check-model-multiselect.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-model-multiselect.test.js)、[`preliminary-check-model-match-mode.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-model-match-mode.test.js)、[`preliminary-check-model-filters.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-model-filters.test.js)、[`preliminary-check-model-table-columns.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-model-table-columns.test.js)、[`preliminary-check-model-linkage-flow.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-model-linkage-flow.test.js)[`preliminary-check-model-and-detail.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-model-and-detail.test.js),覆盖接口、状态、筛选联动流。
- 新增或更新 [`preliminary-check-model-api.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-model-api.test.js)、[`preliminary-check-model-data-loading.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-model-data-loading.test.js)、[`preliminary-check-model-multiselect.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-model-multiselect.test.js)、[`preliminary-check-model-match-mode.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-model-match-mode.test.js)、[`preliminary-check-model-filters.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-model-filters.test.js)、[`preliminary-check-model-table-columns.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-model-table-columns.test.js)、[`preliminary-check-model-linkage-flow.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-model-linkage-flow.test.js)[`preliminary-check-model-and-detail.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-model-and-detail.test.js) 与 [`preliminary-check-model-card-grid.test.js`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/tests/unit/preliminary-check-model-card-grid.test.js),覆盖接口、状态、筛选联动流和卡片布局断点
## 处理说明
@@ -15,6 +15,7 @@
- 模型卡片支持多选,再次点击可取消;请求参数统一携带 `modelCodes + matchMode + keyword + deptId + pageNum + pageSize`
- 模型区人员列表改为展示姓名、工号、身份证号、所属部门、命中模型摘要和异常标签;异常标签复用 [`DetailQuery.vue`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue) 的风险等级颜色映射。
- 部门选项复用系统现有的 `deptTreeSelect` 接口,并在前端展开为可筛选下拉项,保持最短路径实现。
- 模型卡片网格在桌面端调整为固定 5 列,使 10 张卡片按两行展示;平板端降为 2 列,手机端降为 1 列,避免窄屏下卡片挤压。
## 验证情况
@@ -30,6 +31,7 @@ node tests/unit/preliminary-check-model-filters.test.js
node tests/unit/preliminary-check-model-table-columns.test.js
node tests/unit/preliminary-check-model-linkage-flow.test.js
node tests/unit/preliminary-check-model-and-detail.test.js
node tests/unit/preliminary-check-model-card-grid.test.js
```
- 总体验证结果:8 个前端单测命令全部通过0 failure0 error。
- 总体验证结果:9 个前端单测命令全部通过0 failure0 error。

View File

@@ -15,6 +15,8 @@ mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewMapperRiskModelCardsTest
mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewMapperRiskModelPeopleTest
mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewServiceImplTest
mvn test -pl ccdi-project '-Dtest=CcdiProjectOverview*'
mvn -pl ruoyi-admin -am package -DskipTests
java -jar ruoyi-admin/target/ruoyi-admin.jar --server.port=62319
```
## 验证结果
@@ -34,6 +36,21 @@ mvn test -pl ccdi-project '-Dtest=CcdiProjectOverview*'
- 2026-03-20 12:05 根据线上异常日志补充 `CcdiProjectOverviewMapperRiskModelPeopleTest` 红灯断言,确认 `selectRiskModelNamesByScope` 存在 `distinct + order by 非 select 列` 的 MySQL 兼容性问题。
- 2026-03-20 12:05 将 `selectRiskModelNamesByScope` 调整为 `group by scoped.model_code, scoped.model_name` 后重新执行 `CcdiProjectOverviewMapperRiskModelPeopleTest`2 个测试全部通过。
- 2026-03-20 12:05 再次执行结果总览相关总体验证命令 `mvn test -pl ccdi-project '-Dtest=CcdiProjectOverview*'`,共 22 个测试全部通过0 failure0 error。
- 2026-03-20 12:19 根据真实接口 `matchMode=ALL` 的 500 日志补充 `CcdiProjectOverviewMapperRiskModelPeopleTest` 红灯断言,确认 `having count(distinct base.model_code) = #{query.modelCodes.size}` 会在 MyBatis `CollectionWrapper.get("size")` 阶段触发 `UnsupportedOperationException`
- 2026-03-20 12:20 将 `ALL` 条件改为绑定 `#{query.modelCodesCount}` 后重新执行 `mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewMapperRiskModelPeopleTest`2 个测试全部通过。
- 2026-03-20 12:20 再次执行结果总览相关总体验证命令 `mvn test -pl ccdi-project '-Dtest=CcdiProjectOverview*'`,共 22 个测试全部通过0 failure0 error。
- 2026-03-20 12:21 执行 `mvn -pl ruoyi-admin -am package -DskipTests`,主应用重新打包成功。
- 2026-03-20 12:56 启动临时后端实例 `java -jar ruoyi-admin/target/ruoyi-admin.jar --server.port=62319` 后,实际调用以下接口均返回 `code=200`
- `/ccdi/project/overview/risk-models/cards?projectId=42`
- `/ccdi/project/overview/risk-models/people?projectId=42&pageNum=1&pageSize=5`
- `/ccdi/project/overview/risk-models/people?projectId=42&modelCodes=LARGE_TRANSACTION&matchMode=ANY&pageNum=1&pageSize=5`
- `/ccdi/project/overview/risk-models/people?projectId=42&modelCodes=LARGE_TRANSACTION&matchMode=ALL&pageNum=1&pageSize=5`
- 2026-03-20 13:33 根据“模型触发次数为 0 也要展示卡片”的新要求,先修改 `CcdiProjectOverviewMapperRiskModelCardsTest` 让其红灯,锁定卡片 SQL 必须以 `ccdi_bank_tag_rule` 为全量模型源并对 `warningCount/peopleCount``0` 值回填。
- 2026-03-20 13:34 将 `selectRiskModelCardsByProjectId` 改为 `ccdi_bank_tag_rule` 主表左连当前项目命中聚合后,重新执行 `mvn test -pl ccdi-project -Dtest=CcdiProjectOverviewMapperRiskModelCardsTest`2 个测试全部通过。
- 2026-03-20 13:34 再次执行结果总览相关总体验证命令 `mvn test -pl ccdi-project '-Dtest=CcdiProjectOverview*'`,共 22 个测试全部通过0 failure0 error。
- 2026-03-20 13:34 查询数据库确认 `ccdi_bank_tag_rule` 启用模型数为 `10`,项目 `42` 命中过的模型数为 `1`具备“0 次卡片”真实回归条件。
- 2026-03-20 13:34 执行 `mvn -pl ruoyi-admin -am package -DskipTests`,主应用重新打包成功。
- 2026-03-20 13:34 启动临时后端实例 `java -jar ruoyi-admin/target/ruoyi-admin.jar --server.port=62319`,调用 `/ccdi/project/overview/risk-models/cards?projectId=42` 返回 `10` 张卡片,其中 `LARGE_TRANSACTION``warningCount=2552, peopleCount=1`,其余 `9` 张卡片均为 `warningCount=0, peopleCount=0`
## 结论
@@ -41,5 +58,7 @@ mvn test -pl ccdi-project '-Dtest=CcdiProjectOverview*'
- 人员分页查询已覆盖多模型 `ANY / ALL`、姓名/工号关键字、部门筛选,以及筛选上下文内的 `modelNames`/`hitTagList` 聚合。
- 风险模型人员分页 SQL 已补齐 `projectId` 绑定桥接,避免公共 SQL 片段在分页查询场景下触发 MyBatis `Parameter 'projectId' not found` 异常。
- 风险模型名称子查询已移除 `distinct + 非 select 列排序` 组合,避免 MySQL 8 在 `ONLY_FULL_GROUP_BY` 兼容校验下抛出 3065 异常。
- 风险模型人员分页 `ALL` 条件已改为绑定 DTO 显式计数字段 `modelCodesCount`,避免 MyBatis 在读取 `query.modelCodes.size` 时触发 `UnsupportedOperationException`
- 风险模型卡片统计已改为从 `ccdi_bank_tag_rule` 的启用模型定义出发,再左连项目命中聚合,因此命中次数为 `0` 的模型也会展示卡片并返回 `warningCount=0``peopleCount=0`
- 服务层已统一完成项目存在性校验、空列表封装和“查看详情”动作文案赋值。
- 本轮验证仅执行 Maven 单元测试,未启动额外前后端进程,因此无需执行进程清理
- 本轮验证 Maven 测试外,还启动了临时后端实例 `62319` 做真实接口回归;验证结束后已关闭该临时进程

View File

@@ -7,6 +7,7 @@
- 模型卡片多选与 `ANY / ALL` 触发方式切换
- 员工姓名或工号、部门、分页联动查询
- 人员列表工号、模型摘要与异常标签展示
- 模型卡片桌面端两行布局与响应式断点
## 验证命令
@@ -20,6 +21,7 @@ node tests/unit/preliminary-check-model-filters.test.js
node tests/unit/preliminary-check-model-table-columns.test.js
node tests/unit/preliminary-check-model-linkage-flow.test.js
node tests/unit/preliminary-check-model-and-detail.test.js
node tests/unit/preliminary-check-model-card-grid.test.js
```
## 验证结果
@@ -34,11 +36,15 @@ node tests/unit/preliminary-check-model-and-detail.test.js
- 2026-03-20 完成员工姓名或工号、部门筛选以及工号列、异常标签列改造后,重新执行上述 2 个测试,断言通过。
- 2026-03-20 新增并执行 `preliminary-check-model-linkage-flow.test.js`,确认默认全部模型、卡片增删、`ANY / ALL` 切换、关键词/部门/分页参数以及重置逻辑均已落到组件状态与请求参数构造中。
- 2026-03-20 追加执行 `preliminary-check-model-and-detail.test.js`,确认模型区与风险明细区的静态文案断言与当前实现一致。
- 2026-03-20 执行上述 8 个前端单测命令总结果全部通过0 failure0 error
- 2026-03-20 新增并执行 `preliminary-check-model-card-grid.test.js` 红灯验证,确认模型卡片网格仍为旧的 3 列布局
- 2026-03-20 将 `RiskModelSection.vue` 的模型卡片网格改为桌面端 5 列、平板端 2 列、手机端 1 列后,重新执行 `preliminary-check-model-card-grid.test.js`,断言通过。
- 2026-03-20 重新执行 `preliminary-check-model-and-detail.test.js``preliminary-check-model-linkage-flow.test.js`,确认布局调整未影响模型区静态结构和联动逻辑。
- 2026-03-20 执行上述 9 个前端单测命令总结果全部通过0 failure0 error。
## 结论
- 结果总览模型区前端已切换为真实接口驱动,入口页负责模型卡片数据,模型区组件负责联动查询。
- 模型区已支持多卡片联动、`任意触发 / 同时触发` 切换,以及姓名或工号、部门、分页组合筛选。
- 模型卡片区桌面端已改为固定两行展示 10 张卡片,窄屏继续按 2 列 / 1 列自适应收缩。
- 人员列表已展示工号、命中模型摘要和异常标签,并复用风险等级颜色映射。
- 本轮验证仅执行前端静态单测,未启动额外前后端进程,因此无需执行进程清理。

View File

@@ -374,7 +374,7 @@ export default {
.model-card-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 16px;
}
@@ -506,4 +506,16 @@ export default {
justify-content: flex-end;
margin-top: 16px;
}
@media (max-width: 1200px) {
.model-card-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 768px) {
.model-card-grid {
grid-template-columns: 1fr;
}
}
</style>

View File

@@ -0,0 +1,32 @@
const assert = require("assert");
const fs = require("fs");
const path = require("path");
const source = fs.readFileSync(
path.resolve(
__dirname,
"../../src/views/ccdiProject/components/detail/RiskModelSection.vue"
),
"utf8"
);
assert(
source.includes("grid-template-columns: repeat(5, minmax(0, 1fr));"),
"桌面端模型卡片应固定为每行 5 张"
);
assert(
source.includes("@media (max-width: 1200px)"),
"应存在平板断点"
);
assert(
source.includes("grid-template-columns: repeat(2, minmax(0, 1fr));"),
"平板端模型卡片应降为 2 列"
);
assert(
source.includes("@media (max-width: 768px)"),
"应存在手机断点"
);
assert(
source.includes("grid-template-columns: 1fr;"),
"手机端模型卡片应降为 1 列"
);