16 KiB
招投标详情弹窗供应商企业信息查看 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: 编写失败中的源码断言测试
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:
source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && node ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js
Expected:
FAIL with "招投标供应商企业详情缺少关键结构"
- Step 3: 在页面脚本中补齐企业详情最小状态与方法骨架
在 ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue 先补齐以下最小结构:
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:
source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && node ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js
Expected:
purchase-transaction-enterprise-detail-ui test passed
- Step 5: 提交第一阶段改动
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 追加以下断言:
[
'label="操作"',
">详情</el-button>",
"企业信息详情",
"统一社会信用代码",
"企业名称",
"企业类型",
"企业性质",
"行业分类",
"所属行业",
"法定代表人",
"风险等级",
"企业来源",
"数据来源",
"股东5"
].forEach((token) => {
assert(source.includes(token), `招投标供应商企业详情模板缺少关键结构: ${token}`);
});
- Step 2: 运行扩展后的断言测试,保持实现前的失败信号
Run:
source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && node ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js
Expected:
FAIL with missing template token such as "企业信息详情" or field labels
- Step 3: 在脚本中补齐企业详情请求与枚举格式化
按以下口径实现:
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: 在详情弹窗供应商明细表中新增操作列,并补齐企业详情二级弹窗
模板按以下结构接线:
<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
- Step 4: 跑源码断言测试,确认按钮、方法、字段和“无权限显隐”口径都已到位
- Step 5: 跑源码断言测试,确认按钮、方法、字段和“无权限显隐”口径都已到位
Run:
source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && node ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js
Expected:
purchase-transaction-enterprise-detail-ui test passed
- Step 5: 提交主实现
- Step 6: 提交主实现
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:
source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && node ruoyi-ui/tests/unit/purchase-transaction-enterprise-detail-ui.test.js
Expected:
purchase-transaction-enterprise-detail-ui test passed
- Step 2: 运行前端生产构建
Run:
source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && cd ruoyi-ui && npm run build:prod
Expected:
BUILD SUCCESS
允许出现既有 bundle size warning,但不能有新的编译错误。
- Step 3: 启动真实页面并用 Playwright 完成页面验证
如果本地后端未运行,先启动后端:
sh bin/restart_java_backend.sh
启动前端:
source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && cd ruoyi-ui && npm run dev -- --port 8080
Playwright 打开真实页面:
http://localhost:8080/maintain/purchaseTransaction
先准备最小可复现场景数据,避免验证阶段卡住:
- 进入
http://localhost:8080/maintain/baseStaff/enterpriseBaseInfo或现有“实体库管理”页面,任选一条现有企业记录,记下它的socialCreditCode,作为“可命中”样本。- 如果页面没有现成企业数据,先通过现有“新增实体库”流程补一条样本企业,再继续后续验证。
- 进入
http://localhost:8080/maintain/purchaseTransaction,新增或编辑一条测试用招投标记录,在供应商明细中准备三行:- 供应商 A:
supplierUscc填上步骤 1 记录下来的实体库统一社会信用代码,用于“可命中”场景。 - 供应商 B:
supplierUscc填TESTNOENTERPRISE01,用于“查无数据”场景。
- 供应商 A:
- 保存后进入该条记录的详情弹窗,先验证供应商 A 和供应商 B。
- “无统一信用代码”场景不通过新增/编辑表单构造,而是在真实页面打开详情弹窗前,对
**/ccdi/purchaseTransaction/*做一次性 Playwright 路由拦截:- 先
route.fetch()获取真实详情响应 - 将返回体中任意一条
supplierList[*].supplierUscc改成空字符串 - 再
route.fulfill({ response, json: patchedBody }) - 重新打开同一条招投标详情弹窗,点击被补空的供应商“详情”按钮,验证提示“暂无企业信息”
- 先
- “接口异常”场景通过 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,至少包含以下内容:
# 招投标详情弹窗供应商企业信息查看实施记录
## 本次修改
- 在招投标详情弹窗供应商明细中新增企业详情按钮
- 复用实体库详情接口展示企业全部字段
- 无统一信用代码、查无数据、接口异常统一提示“暂无企业信息”
## 影响范围
- 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: 提交验证与文档
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 真实页面验证完成
- 测试过程中启动的前后端进程已关闭