377 lines
11 KiB
Markdown
377 lines
11 KiB
Markdown
# Project Detail Risk Overview Risk People Pagination Frontend Implementation Plan
|
||
|
||
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||
>
|
||
> **Repo note:** 本仓库 `AGENTS.md` 明确禁止开启 subagent,执行本计划时请在当前会话使用 `superpowers:executing-plans`。
|
||
|
||
**Goal:** 让项目详情“风险总览”的员工列表改为固定每页 5 条的真实后端分页,并保证翻页时只刷新该列表,不重载结果总览内其他区块。
|
||
|
||
**Architecture:** 前端继续以 `PreliminaryCheck.vue` 作为结果总览入口,首屏加载第一页风险员工数据并透传给 `RiskPeopleSection.vue`。`RiskPeopleSection.vue` 接管分页状态、请求 loading 和翻页动作,直接调用 `projectOverview.js` 中改造后的 `getOverviewRiskPeople(params)`,避免在翻页时重新触发整页 `Promise.all`。
|
||
|
||
**Tech Stack:** Vue 2, Element UI, SCSS, axios request 封装, Node `assert` 结构测试
|
||
|
||
---
|
||
|
||
### Task 1: 改造风险员工分页 API 封装
|
||
|
||
**Files:**
|
||
- Modify: `ruoyi-ui/src/api/ccdi/projectOverview.js`
|
||
- Test: `ruoyi-ui/tests/unit/project-overview-api.test.js`
|
||
|
||
- [ ] **Step 1: 先写失败测试,锁定 `getOverviewRiskPeople` 改为参数对象透传**
|
||
|
||
```javascript
|
||
[
|
||
"getOverviewRiskPeople(params)",
|
||
"/ccdi/project/overview/risk-people",
|
||
"projectId: params.projectId",
|
||
"pageNum: params.pageNum",
|
||
"pageSize: params.pageSize",
|
||
].forEach((token) => assert(source.includes(token), token));
|
||
```
|
||
|
||
- [ ] **Step 2: 运行 API 契约测试,确认当前仍是旧签名**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
node ruoyi-ui/tests/unit/project-overview-api.test.js
|
||
```
|
||
|
||
Expected:
|
||
|
||
- FAIL,提示 `getOverviewRiskPeople(projectId)` 仍是旧写法
|
||
|
||
- [ ] **Step 3: 写最小 API 封装**
|
||
|
||
```javascript
|
||
export function getOverviewRiskPeople(params) {
|
||
return request({
|
||
url: "/ccdi/project/overview/risk-people",
|
||
method: "get",
|
||
params: {
|
||
projectId: params.projectId,
|
||
pageNum: params.pageNum,
|
||
pageSize: params.pageSize,
|
||
},
|
||
});
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 4: 回跑 API 测试**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
node ruoyi-ui/tests/unit/project-overview-api.test.js
|
||
```
|
||
|
||
Expected: PASS
|
||
|
||
- [ ] **Step 5: 提交本任务**
|
||
|
||
```bash
|
||
git add ruoyi-ui/src/api/ccdi/projectOverview.js \
|
||
ruoyi-ui/tests/unit/project-overview-api.test.js
|
||
git commit -m "改造风险总览员工列表前端分页接口"
|
||
```
|
||
|
||
### Task 2: 让结果总览首屏按分页结构装配风险员工数据
|
||
|
||
**Files:**
|
||
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue`
|
||
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/preliminaryCheck.mock.js`
|
||
- Modify: `ruoyi-ui/tests/unit/preliminary-check-api-integration.test.js`
|
||
- Create: `ruoyi-ui/tests/unit/preliminary-check-risk-people-pagination-load.test.js`
|
||
|
||
- [ ] **Step 1: 先写失败测试,锁定父组件首屏请求第一页且固定 5 条**
|
||
|
||
```javascript
|
||
[
|
||
"getOverviewRiskPeople({",
|
||
"projectId: this.projectId",
|
||
"pageNum: 1",
|
||
"pageSize: 5",
|
||
"riskPeopleData.rows",
|
||
].forEach((token) => assert(source.includes(token), token));
|
||
```
|
||
|
||
```javascript
|
||
assert(source.includes("pageNum"), "riskPeople 应保存首屏页码");
|
||
assert(source.includes("pageSize"), "riskPeople 应保存首屏页长");
|
||
assert(source.includes("total"), "riskPeople 应保存总数");
|
||
```
|
||
|
||
- [ ] **Step 2: 运行入口页测试,确认当前还在读取 `overviewList`**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
node ruoyi-ui/tests/unit/preliminary-check-api-integration.test.js
|
||
node ruoyi-ui/tests/unit/preliminary-check-risk-people-pagination-load.test.js
|
||
```
|
||
|
||
Expected:
|
||
|
||
- FAIL,提示首屏没有传 `pageNum/pageSize`
|
||
- FAIL,提示 `riskPeopleData.rows` 未被使用
|
||
|
||
- [ ] **Step 3: 写最小首屏加载与 mock 归一化**
|
||
|
||
```javascript
|
||
const [dashboardRes, riskPeopleRes, riskModelCardsRes, suspiciousRes, creditNegativeRes] = await Promise.all([
|
||
getOverviewDashboard(this.projectId),
|
||
getOverviewRiskPeople({ projectId: this.projectId, pageNum: 1, pageSize: 5 }),
|
||
getOverviewRiskModelCards(this.projectId),
|
||
getOverviewSuspiciousTransactions({ projectId: this.projectId, suspiciousType: "ALL", pageNum: 1, pageSize: 5 }),
|
||
getOverviewEmployeeCreditNegative({ projectId: this.projectId, pageNum: 1, pageSize: 5 }),
|
||
]);
|
||
```
|
||
|
||
```javascript
|
||
riskPeople: {
|
||
...mockOverviewData.riskPeople,
|
||
rows: Array.isArray(riskPeopleData && riskPeopleData.rows) ? riskPeopleData.rows : [],
|
||
total: riskPeopleData && riskPeopleData.total ? riskPeopleData.total : 0,
|
||
pageNum: riskPeopleData && riskPeopleData.pageNum ? riskPeopleData.pageNum : 1,
|
||
pageSize: riskPeopleData && riskPeopleData.pageSize ? riskPeopleData.pageSize : 5,
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 4: 回跑入口页测试**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
node ruoyi-ui/tests/unit/preliminary-check-api-integration.test.js
|
||
node ruoyi-ui/tests/unit/preliminary-check-risk-people-pagination-load.test.js
|
||
```
|
||
|
||
Expected: PASS
|
||
|
||
- [ ] **Step 5: 提交本任务**
|
||
|
||
```bash
|
||
git add ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue \
|
||
ruoyi-ui/src/views/ccdiProject/components/detail/preliminaryCheck.mock.js \
|
||
ruoyi-ui/tests/unit/preliminary-check-api-integration.test.js \
|
||
ruoyi-ui/tests/unit/preliminary-check-risk-people-pagination-load.test.js
|
||
git commit -m "接通风险总览员工列表首屏分页数据"
|
||
```
|
||
|
||
### Task 3: 在 `RiskPeopleSection` 中接管独立翻页与局部刷新
|
||
|
||
**Files:**
|
||
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue`
|
||
- Modify: `ruoyi-ui/src/views/ccdiProject/components/detail/RiskPeopleSection.vue`
|
||
- Modify: `ruoyi-ui/tests/unit/preliminary-check-risk-people-binding.test.js`
|
||
- Create: `ruoyi-ui/tests/unit/preliminary-check-risk-people-pagination.test.js`
|
||
|
||
- [ ] **Step 1: 先写失败测试,锁定组件读取 `rows`、存在分页条、固定 5 条一页**
|
||
|
||
```javascript
|
||
[
|
||
"sectionData.rows",
|
||
"pageNum",
|
||
"pageSize",
|
||
"total",
|
||
"<pagination",
|
||
':page-sizes="[5]"',
|
||
"handlePageChange",
|
||
"loadRiskPeoplePage",
|
||
"getOverviewRiskPeople",
|
||
].forEach((token) => assert(source.includes(token), token));
|
||
```
|
||
|
||
```javascript
|
||
assert(!source.includes("sectionData.overviewList"), "风险人员列表不应再绑定 overviewList");
|
||
```
|
||
|
||
- [ ] **Step 2: 运行风险人员组件测试,确认当前没有分页状态和独立翻页逻辑**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
node ruoyi-ui/tests/unit/preliminary-check-risk-people-binding.test.js
|
||
node ruoyi-ui/tests/unit/preliminary-check-risk-people-pagination.test.js
|
||
```
|
||
|
||
Expected:
|
||
|
||
- FAIL,提示缺少 `rows / total / pageNum / pageSize`
|
||
- FAIL,提示缺少分页组件和翻页方法
|
||
|
||
- [ ] **Step 3: 写最小局部分页实现**
|
||
|
||
在 `PreliminaryCheck.vue` 里给子组件补传 `project-id`:
|
||
|
||
```vue
|
||
<risk-people-section
|
||
:project-id="projectId"
|
||
:section-data="currentData.riskPeople"
|
||
@view-project-analysis="handleRiskPeopleProjectAnalysis"
|
||
/>
|
||
```
|
||
|
||
在 `RiskPeopleSection.vue` 里维护局部分页状态与请求:
|
||
|
||
```javascript
|
||
data() {
|
||
return {
|
||
pageNum: 1,
|
||
pageSize: 5,
|
||
total: 0,
|
||
tableLoading: false,
|
||
localRows: [],
|
||
};
|
||
},
|
||
```
|
||
|
||
```javascript
|
||
watch: {
|
||
sectionData: {
|
||
immediate: true,
|
||
deep: true,
|
||
handler(data) {
|
||
this.localRows = normalizeOverviewRows(data && data.rows);
|
||
this.total = (data && data.total) || 0;
|
||
this.pageNum = (data && data.pageNum) || 1;
|
||
this.pageSize = (data && data.pageSize) || 5;
|
||
},
|
||
},
|
||
},
|
||
```
|
||
|
||
```javascript
|
||
async loadRiskPeoplePage(pageNum) {
|
||
this.tableLoading = true;
|
||
const response = await getOverviewRiskPeople({
|
||
projectId: this.projectId,
|
||
pageNum,
|
||
pageSize: 5,
|
||
});
|
||
const data = (response && response.data) || {};
|
||
this.localRows = normalizeOverviewRows(data.rows);
|
||
this.total = data.total || 0;
|
||
this.pageNum = data.pageNum || pageNum;
|
||
this.pageSize = data.pageSize || 5;
|
||
this.tableLoading = false;
|
||
}
|
||
```
|
||
|
||
```javascript
|
||
handlePageChange({ page }) {
|
||
if (page === this.pageNum) {
|
||
return;
|
||
}
|
||
this.loadRiskPeoplePage(page);
|
||
}
|
||
```
|
||
|
||
```vue
|
||
<pagination
|
||
v-show="total > 0"
|
||
:total="total"
|
||
:page.sync="pageNum"
|
||
:limit.sync="pageSize"
|
||
:page-sizes="[5]"
|
||
layout="total, prev, pager, next, jumper"
|
||
@pagination="handlePageChange"
|
||
/>
|
||
```
|
||
|
||
- [ ] **Step 4: 回跑风险人员组件测试**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
node ruoyi-ui/tests/unit/preliminary-check-risk-people-binding.test.js
|
||
node ruoyi-ui/tests/unit/preliminary-check-risk-people-pagination.test.js
|
||
```
|
||
|
||
Expected:
|
||
|
||
- PASS
|
||
- 核心异常点标签逻辑、风险等级标签逻辑和操作列逻辑不回归
|
||
|
||
- [ ] **Step 5: 提交本任务**
|
||
|
||
```bash
|
||
git add ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue \
|
||
ruoyi-ui/src/views/ccdiProject/components/detail/RiskPeopleSection.vue \
|
||
ruoyi-ui/tests/unit/preliminary-check-risk-people-binding.test.js \
|
||
ruoyi-ui/tests/unit/preliminary-check-risk-people-pagination.test.js
|
||
git commit -m "支持风险总览员工列表独立分页翻页"
|
||
```
|
||
|
||
### Task 4: 补齐前端验证记录与实施记录
|
||
|
||
**Files:**
|
||
- Create: `docs/tests/records/2026-03-29-project-detail-risk-overview-risk-people-pagination-frontend-verification.md`
|
||
- Create: `docs/reports/implementation/2026-03-29-project-detail-risk-overview-risk-people-pagination-frontend-implementation.md`
|
||
- Verify: `docs/design/2026-03-29-project-detail-risk-overview-risk-people-pagination-design.md`
|
||
|
||
- [ ] **Step 1: 运行本需求前端最小回归**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
node ruoyi-ui/tests/unit/project-overview-api.test.js
|
||
node ruoyi-ui/tests/unit/preliminary-check-api-integration.test.js
|
||
node ruoyi-ui/tests/unit/preliminary-check-risk-people-pagination-load.test.js
|
||
node ruoyi-ui/tests/unit/preliminary-check-risk-people-binding.test.js
|
||
node ruoyi-ui/tests/unit/preliminary-check-risk-people-pagination.test.js
|
||
```
|
||
|
||
Expected:
|
||
|
||
- 全部 PASS
|
||
- 风险人员区首屏读取分页结构,翻页逻辑存在
|
||
|
||
- [ ] **Step 2: 写前端验证记录**
|
||
|
||
在 `docs/tests/records/2026-03-29-project-detail-risk-overview-risk-people-pagination-frontend-verification.md` 记录:
|
||
|
||
- 执行命令
|
||
- 执行日期
|
||
- 测试结果
|
||
- 结论:风险总览员工列表已固定 5 条一页,翻页仅刷新当前列表
|
||
|
||
- [ ] **Step 3: 写前端实施记录**
|
||
|
||
在 `docs/reports/implementation/2026-03-29-project-detail-risk-overview-risk-people-pagination-frontend-implementation.md` 记录:
|
||
|
||
- API 封装如何改造
|
||
- `PreliminaryCheck.vue` 首屏如何注入分页结构
|
||
- `RiskPeopleSection.vue` 如何局部翻页
|
||
- 本次未改动风险模型区和风险明细区
|
||
|
||
- [ ] **Step 4: 检查暂存区仅包含本任务文件**
|
||
|
||
Run:
|
||
|
||
```bash
|
||
git status --short
|
||
git diff --cached --name-only
|
||
```
|
||
|
||
Expected:
|
||
|
||
- 暂存区只出现本任务相关前端源码、测试和文档
|
||
|
||
- [ ] **Step 5: 提交本任务**
|
||
|
||
```bash
|
||
git add docs/tests/records/2026-03-29-project-detail-risk-overview-risk-people-pagination-frontend-verification.md \
|
||
docs/reports/implementation/2026-03-29-project-detail-risk-overview-risk-people-pagination-frontend-implementation.md
|
||
git commit -m "补充风险总览员工列表前端分页实施记录"
|
||
```
|
||
|
||
## Done When
|
||
|
||
- 首屏通过 `getOverviewRiskPeople({ projectId, pageNum: 1, pageSize: 5 })` 获取第一页
|
||
- `riskPeople` 数据结构改为 `rows + total + pageNum + pageSize`
|
||
- `RiskPeopleSection.vue` 存在固定 5 条分页条
|
||
- 翻页只刷新风险总览员工列表,不重载其他结果总览区块
|
||
- 前端验证记录与实施记录已补齐
|