完善招投标供应商企业详情设计与计划
This commit is contained in:
@@ -0,0 +1,479 @@
|
||||
# 招投标详情弹窗供应商企业信息查看 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.
|
||||
|
||||
**Goal:** 在招投标信息维护详情弹窗的供应商明细中新增企业详情按钮,点击后复用实体库详情接口并以二级弹窗展示企业全部字段;无统一信用代码、查无数据或接口异常时统一提示“暂无企业信息”。
|
||||
|
||||
**Architecture:** 保持 `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue` 作为唯一页面入口,在现有招投标详情弹窗内部补充“操作”列、企业详情请求状态和二级弹窗,不新增后端接口、独立组件或权限显隐逻辑。字段展示直接对齐 `ruoyi-ui/src/views/ccdiEnterpriseBaseInfo/index.vue` 的详情顺序与格式化口径,按钮固定显示,不添加 `v-hasPermi`,所有失败分支统一收敛到“暂无企业信息”。
|
||||
|
||||
**Tech Stack:** Vue 2, Element UI, RuoYi `request`, `@/api/ccdiEnterpriseBaseInfo`, `@/api/ccdiEnum`, Node source-assert tests, Playwright real-page verification
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
- Modify: `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue`
|
||||
- 在现有招投标详情弹窗中新增供应商操作列、企业详情请求流程、企业详情弹窗、枚举格式化和关闭重置逻辑。
|
||||
- Create: `ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js`
|
||||
- 用源码断言锁定按钮、状态字段、错误提示、字段展示结构和“不做权限显隐”的实现契约。
|
||||
- Create: `docs/reports/implementation/2026-04-23-bidding-supplier-enterprise-detail-implementation.md`
|
||||
- 记录实施结果、验证命令、真实页面验证结论与测试进程清理。
|
||||
|
||||
## Reference Files
|
||||
|
||||
- Reference only: `ruoyi-ui/src/views/ccdiEnterpriseBaseInfo/index.vue`
|
||||
- 复用企业详情字段顺序与格式化口径。
|
||||
- Reference only: `ruoyi-ui/src/api/ccdiEnterpriseBaseInfo.js`
|
||||
- 复用 `getEnterpriseBaseInfo(socialCreditCode)`。
|
||||
- Reference only: `ruoyi-ui/src/api/ccdiEnum.js`
|
||||
- 复用风险等级、企业来源、数据来源枚举选项加载。
|
||||
|
||||
### Task 1: 锁定供应商企业详情 UI 契约
|
||||
|
||||
**Files:**
|
||||
- Create: `ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js`
|
||||
- Modify: `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue`
|
||||
|
||||
- [ ] **Step 1: 编写失败中的源码断言测试**
|
||||
|
||||
```js
|
||||
const assert = require("assert");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const componentPath = path.resolve(
|
||||
__dirname,
|
||||
"../../src/views/ccdiPurchaseTransaction/index.vue"
|
||||
);
|
||||
const source = fs.readFileSync(componentPath, "utf8");
|
||||
|
||||
[
|
||||
'from "@/api/ccdiEnterpriseBaseInfo"',
|
||||
'from "@/api/ccdiEnum"',
|
||||
"enterpriseDetailOpen",
|
||||
"enterpriseDetailLoading",
|
||||
"enterpriseDetailData",
|
||||
"handleSupplierEnterpriseDetail(row)",
|
||||
"resetEnterpriseDetail()",
|
||||
"暂无企业信息"
|
||||
].forEach((token) => {
|
||||
assert(source.includes(token), `招投标供应商企业详情缺少关键结构: ${token}`);
|
||||
});
|
||||
|
||||
[
|
||||
"v-hasPermi=\\\"['ccdi:enterpriseBaseInfo:query']\\\"",
|
||||
"v-hasPermi=\"['ccdi:enterpriseBaseInfo:query']\"",
|
||||
"ccdi:enterpriseBaseInfo:query"
|
||||
].forEach((token) => {
|
||||
assert(!source.includes(token), `本次不应新增实体库权限显隐控制: ${token}`);
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行测试,确认当前实现失败**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && node ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
FAIL with "招投标供应商企业详情缺少关键结构"
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 在页面脚本中补齐企业详情最小状态与方法骨架**
|
||||
|
||||
在 `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue` 先补齐以下最小结构:
|
||||
|
||||
```js
|
||||
import { getEnterpriseBaseInfo } from "@/api/ccdiEnterpriseBaseInfo";
|
||||
import {
|
||||
getDataSourceOptions,
|
||||
getEnterpriseRiskLevelOptions,
|
||||
getEnterpriseSourceOptions
|
||||
} from "@/api/ccdiEnum";
|
||||
|
||||
data() {
|
||||
return {
|
||||
enterpriseDetailOpen: false,
|
||||
enterpriseDetailLoading: false,
|
||||
enterpriseDetailData: {},
|
||||
enterpriseRiskLevelOptions: [],
|
||||
enterpriseSourceOptions: [],
|
||||
enterpriseDataSourceOptions: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
resetEnterpriseDetail() {
|
||||
this.enterpriseDetailOpen = false;
|
||||
this.enterpriseDetailLoading = false;
|
||||
this.enterpriseDetailData = {};
|
||||
},
|
||||
async handleSupplierEnterpriseDetail(row) {
|
||||
const socialCreditCode = row && row.supplierUscc ? row.supplierUscc.trim() : "";
|
||||
if (!socialCreditCode) {
|
||||
this.$message.warning("暂无企业信息");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
先让组件具备后续接线的状态容器和方法入口,不要在这一步引入额外组件拆分。
|
||||
|
||||
- [ ] **Step 4: 再跑一次测试,确认基础契约已转绿或仅剩模板断言失败**
|
||||
- [ ] **Step 4: 再跑一次测试,确认脚本侧契约已转绿**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && node ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
purchase-transaction-enterprise-detail-ui test passed
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 提交第一阶段改动**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js
|
||||
git commit -m "补充招投标供应商企业详情状态骨架"
|
||||
```
|
||||
|
||||
### Task 2: 接通供应商企业详情请求流与弹窗展示
|
||||
|
||||
**Files:**
|
||||
- Modify: `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue`
|
||||
- Test: `ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js`
|
||||
- Reference: `ruoyi-ui/src/views/ccdiEnterpriseBaseInfo/index.vue`
|
||||
|
||||
- [ ] **Step 1: 重新运行断言测试,保持实现前的失败信号**
|
||||
|
||||
- [ ] **Step 1: 扩展源码断言测试,锁定模板结构、字段顺序和无权限显隐口径**
|
||||
|
||||
在 `ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js` 追加以下断言:
|
||||
|
||||
```js
|
||||
[
|
||||
'label="操作"',
|
||||
">详情</el-button>",
|
||||
"企业信息详情",
|
||||
"统一社会信用代码",
|
||||
"企业名称",
|
||||
"企业类型",
|
||||
"企业性质",
|
||||
"行业分类",
|
||||
"所属行业",
|
||||
"法定代表人",
|
||||
"风险等级",
|
||||
"企业来源",
|
||||
"数据来源",
|
||||
"股东5"
|
||||
].forEach((token) => {
|
||||
assert(source.includes(token), `招投标供应商企业详情模板缺少关键结构: ${token}`);
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行扩展后的断言测试,保持实现前的失败信号**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && node ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
FAIL with missing template token such as "企业信息详情" or field labels
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 在脚本中补齐企业详情请求与枚举格式化**
|
||||
|
||||
按以下口径实现:
|
||||
|
||||
```js
|
||||
created() {
|
||||
this.getList();
|
||||
this.restoreImportState();
|
||||
this.loadEnterpriseDetailOptions();
|
||||
},
|
||||
methods: {
|
||||
loadEnterpriseDetailOptions() {
|
||||
return Promise.all([
|
||||
getEnterpriseRiskLevelOptions(),
|
||||
getEnterpriseSourceOptions(),
|
||||
getDataSourceOptions()
|
||||
]).then(([riskRes, sourceRes, dataSourceRes]) => {
|
||||
this.enterpriseRiskLevelOptions = riskRes.data || [];
|
||||
this.enterpriseSourceOptions = sourceRes.data || [];
|
||||
this.enterpriseDataSourceOptions = dataSourceRes.data || [];
|
||||
});
|
||||
},
|
||||
getEnterpriseOptionLabel(options, value) {
|
||||
if (!value) return "-";
|
||||
const matched = (options || []).find(item => item.value === value);
|
||||
return matched ? matched.label : value;
|
||||
},
|
||||
formatEnterpriseRiskLevel(value) {
|
||||
return this.getEnterpriseOptionLabel(this.enterpriseRiskLevelOptions, value);
|
||||
},
|
||||
formatEnterpriseSource(value) {
|
||||
return this.getEnterpriseOptionLabel(this.enterpriseSourceOptions, value);
|
||||
},
|
||||
formatEnterpriseDataSource(value) {
|
||||
return this.getEnterpriseOptionLabel(this.enterpriseDataSourceOptions, value);
|
||||
},
|
||||
formatEnterpriseStatus(value) {
|
||||
return value || "-";
|
||||
},
|
||||
async handleSupplierEnterpriseDetail(row) {
|
||||
const socialCreditCode = row && row.supplierUscc ? row.supplierUscc.trim() : "";
|
||||
if (!socialCreditCode) {
|
||||
this.$message.warning("暂无企业信息");
|
||||
return;
|
||||
}
|
||||
this.enterpriseDetailLoading = true;
|
||||
try {
|
||||
const response = await getEnterpriseBaseInfo(socialCreditCode);
|
||||
if (!response || !response.data) {
|
||||
this.$message.warning("暂无企业信息");
|
||||
return;
|
||||
}
|
||||
this.enterpriseDetailData = response.data;
|
||||
this.enterpriseDetailOpen = true;
|
||||
} catch (error) {
|
||||
this.$message.warning("暂无企业信息");
|
||||
} finally {
|
||||
this.enterpriseDetailLoading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
固定遵守这三个口径:
|
||||
|
||||
- 只按 `supplierUscc` 查询
|
||||
- 不做按钮权限显隐
|
||||
- 403 / 404 / 空数据 / 普通异常全部统一提示“暂无企业信息”
|
||||
|
||||
- [ ] **Step 3: 在详情弹窗供应商明细表中新增操作列,并补齐企业详情二级弹窗**
|
||||
- [ ] **Step 4: 在详情弹窗供应商明细表中新增操作列,并补齐企业详情二级弹窗**
|
||||
|
||||
模板按以下结构接线:
|
||||
|
||||
```vue
|
||||
<el-table-column label="操作" width="100" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
type="text"
|
||||
size="mini"
|
||||
:loading="enterpriseDetailLoading"
|
||||
@click="handleSupplierEnterpriseDetail(scope.row)"
|
||||
>详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-dialog
|
||||
title="企业信息详情"
|
||||
:visible.sync="enterpriseDetailOpen"
|
||||
width="1000px"
|
||||
append-to-body
|
||||
@close="resetEnterpriseDetail"
|
||||
>
|
||||
<el-descriptions :column="2" border v-loading="enterpriseDetailLoading">
|
||||
<el-descriptions-item label="统一社会信用代码">{{ enterpriseDetailData.socialCreditCode || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="企业名称">{{ enterpriseDetailData.enterpriseName || "-" }}</el-descriptions-item>
|
||||
<el-descriptions-item label="风险等级">{{ formatEnterpriseRiskLevel(enterpriseDetailData.riskLevel) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="企业来源">{{ formatEnterpriseSource(enterpriseDetailData.entSource) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="数据来源">{{ formatEnterpriseDataSource(enterpriseDetailData.dataSource) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
```
|
||||
|
||||
字段顺序严格对齐 `ruoyi-ui/src/views/ccdiEnterpriseBaseInfo/index.vue` 现有详情弹窗:
|
||||
|
||||
1. 统一社会信用代码
|
||||
2. 企业名称
|
||||
3. 企业类型
|
||||
4. 企业性质
|
||||
5. 行业分类
|
||||
6. 所属行业
|
||||
7. 成立日期
|
||||
8. 注册地址
|
||||
9. 法定代表人
|
||||
10. 法定代表人证件类型
|
||||
11. 法定代表人证件号码
|
||||
12. 经营状态
|
||||
13. 风险等级
|
||||
14. 企业来源
|
||||
15. 数据来源
|
||||
16. 创建时间
|
||||
17. 股东1
|
||||
18. 股东2
|
||||
19. 股东3
|
||||
20. 股东4
|
||||
21. 股东5
|
||||
|
||||
- [ ] **Step 4: 跑源码断言测试,确认按钮、方法、字段和“无权限显隐”口径都已到位**
|
||||
- [ ] **Step 5: 跑源码断言测试,确认按钮、方法、字段和“无权限显隐”口径都已到位**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && node ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
purchase-transaction-enterprise-detail-ui test passed
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 提交主实现**
|
||||
- [ ] **Step 6: 提交主实现**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js
|
||||
git commit -m "补充招投标供应商企业详情查看"
|
||||
```
|
||||
|
||||
### Task 3: 完成构建验证、真实页面验证与实施记录
|
||||
|
||||
**Files:**
|
||||
- Modify: `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue`
|
||||
- Test: `ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js`
|
||||
- Create: `docs/reports/implementation/2026-04-23-bidding-supplier-enterprise-detail-implementation.md`
|
||||
|
||||
- [ ] **Step 1: 运行源码断言测试,确认静态契约稳定**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && node ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
purchase-transaction-enterprise-detail-ui test passed
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 运行前端生产构建**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && cd ruoyi-ui && npm run build:prod
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
```text
|
||||
BUILD SUCCESS
|
||||
```
|
||||
|
||||
允许出现既有 bundle size warning,但不能有新的编译错误。
|
||||
|
||||
- [ ] **Step 3: 启动真实页面并用 Playwright 完成页面验证**
|
||||
|
||||
如果本地后端未运行,先启动后端:
|
||||
|
||||
```bash
|
||||
sh bin/restart_java_backend.sh
|
||||
```
|
||||
|
||||
启动前端:
|
||||
|
||||
```bash
|
||||
source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && cd ruoyi-ui && npm run dev -- --port 8080
|
||||
```
|
||||
|
||||
Playwright 打开真实页面:
|
||||
|
||||
```text
|
||||
http://localhost:8080/maintain/purchaseTransaction
|
||||
```
|
||||
|
||||
先准备最小可复现场景数据,避免验证阶段卡住:
|
||||
|
||||
1. 进入 `http://localhost:8080/maintain/baseStaff/enterpriseBaseInfo` 或现有“实体库管理”页面,任选一条现有企业记录,记下它的 `socialCreditCode`,作为“可命中”样本。
|
||||
- 如果页面没有现成企业数据,先通过现有“新增实体库”流程补一条样本企业,再继续后续验证。
|
||||
2. 进入 `http://localhost:8080/maintain/purchaseTransaction`,新增或编辑一条测试用招投标记录,在供应商明细中准备三行:
|
||||
- 供应商 A:`supplierUscc` 填上步骤 1 记录下来的实体库统一社会信用代码,用于“可命中”场景。
|
||||
- 供应商 B:`supplierUscc` 填 `TESTNOENTERPRISE01`,用于“查无数据”场景。
|
||||
3. 保存后进入该条记录的详情弹窗,先验证供应商 A 和供应商 B。
|
||||
4. “无统一信用代码”场景不通过新增/编辑表单构造,而是在真实页面打开详情弹窗前,对 `**/ccdi/purchaseTransaction/*` 做一次性 Playwright 路由拦截:
|
||||
- 先 `route.fetch()` 获取真实详情响应
|
||||
- 将返回体中任意一条 `supplierList[*].supplierUscc` 改成空字符串
|
||||
- 再 `route.fulfill({ response, json: patchedBody })`
|
||||
- 重新打开同一条招投标详情弹窗,点击被补空的供应商“详情”按钮,验证提示“暂无企业信息”
|
||||
5. “接口异常”场景通过 Playwright 对 `**/ccdi/enterpriseBaseInfo/**` 做一次性路由拦截,返回 `500` 或直接 `abort`,仍在真实业务页面中点击供应商“详情”按钮完成验证。
|
||||
|
||||
必须覆盖以下场景:
|
||||
|
||||
- 存在 `supplierUscc` 且实体库可命中时,点击供应商行“详情”打开企业详情弹窗
|
||||
- 企业详情弹窗字段顺序、日期格式、枚举中文标签与实体库详情页一致
|
||||
- `supplierUscc` 为空时,点击“详情”提示“暂无企业信息”
|
||||
- 接口异常或查无数据时,点击“详情”仍提示“暂无企业信息”
|
||||
- “详情”按钮固定显示,不因为实体库权限加前端显隐
|
||||
- 关闭企业详情弹窗后再次查看另一家供应商,不残留上一条详情数据
|
||||
|
||||
- [ ] **Step 4: 关闭测试过程中启动的进程**
|
||||
|
||||
需要主动关闭:
|
||||
|
||||
- 前端 `npm run dev` 进程
|
||||
- 若为本次验证启动了后端,则同步关闭对应后端进程
|
||||
- Playwright 浏览器会话
|
||||
|
||||
- [ ] **Step 5: 编写实施记录**
|
||||
|
||||
新增 `docs/reports/implementation/2026-04-23-bidding-supplier-enterprise-detail-implementation.md`,至少包含以下内容:
|
||||
|
||||
```md
|
||||
# 招投标详情弹窗供应商企业信息查看实施记录
|
||||
|
||||
## 本次修改
|
||||
- 在招投标详情弹窗供应商明细中新增企业详情按钮
|
||||
- 复用实体库详情接口展示企业全部字段
|
||||
- 无统一信用代码、查无数据、接口异常统一提示“暂无企业信息”
|
||||
|
||||
## 影响范围
|
||||
- ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue
|
||||
- ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js
|
||||
|
||||
## 验证方式
|
||||
- Node 源码断言测试
|
||||
- npm run build:prod
|
||||
- Playwright 真实页面验证
|
||||
|
||||
## 测试进程清理
|
||||
- 已关闭前端 dev 进程
|
||||
- 已关闭本次启动的后端进程
|
||||
- 已关闭浏览器会话
|
||||
```
|
||||
|
||||
- [ ] **Step 6: 提交验证与文档**
|
||||
|
||||
```bash
|
||||
git add docs/reports/implementation/2026-04-23-bidding-supplier-enterprise-detail-implementation.md
|
||||
git commit -m "记录招投标供应商企业详情实现结果"
|
||||
```
|
||||
|
||||
## Final Verification Checklist
|
||||
|
||||
- [ ] `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue` 只按 `supplierUscc` 查询企业详情
|
||||
- [ ] 供应商“详情”按钮未新增 `v-hasPermi`
|
||||
- [ ] 企业详情字段顺序与 `ruoyi-ui/src/views/ccdiEnterpriseBaseInfo/index.vue` 一致
|
||||
- [ ] 缺少统一信用代码、查无数据、403/异常均统一提示“暂无企业信息”
|
||||
- [ ] 关闭企业详情弹窗后会清空 `enterpriseDetailData`
|
||||
- [ ] Node 断言测试通过
|
||||
- [ ] 前端构建通过
|
||||
- [ ] Playwright 真实页面验证完成
|
||||
- [ ] 测试过程中启动的前后端进程已关闭
|
||||
Reference in New Issue
Block a user