Compare commits
22 Commits
301fa6c85c
...
a5072c5e7a
| Author | SHA1 | Date | |
|---|---|---|---|
| a5072c5e7a | |||
| 206754adb4 | |||
| a5a3e36d48 | |||
| 9ffcb22929 | |||
| b9ca44cbca | |||
| 9916f641ac | |||
| 4cf76a13a0 | |||
| 5ac8d0bb99 | |||
| 5e85533062 | |||
| 4678f2cd44 | |||
| 9f2a2b7c17 | |||
| 6d322ea7da | |||
| 38adbaed90 | |||
| b0f5422593 | |||
| bf68f5e7ee | |||
| bd2d7b80dc | |||
| 1feb295a93 | |||
| c7b140c5db | |||
| 6e30a0ccf4 | |||
| 33994531b0 | |||
| e43d2ac0f6 | |||
| 4a2d993a91 |
@@ -23,6 +23,13 @@
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 流水分析模块 -->
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
<artifactId>ccdi-lsfx</artifactId>
|
||||
<version>${ruoyi.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
@@ -36,6 +43,13 @@
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 测试依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -50,6 +50,9 @@ public class CcdiProject implements Serializable {
|
||||
/** 低风险人数 */
|
||||
private Integer lowRiskCount;
|
||||
|
||||
/** 流水分析平台项目ID */
|
||||
private Integer lsfxProjectId;
|
||||
|
||||
/** 删除标志:0-存在,2-删除 */
|
||||
@TableLogic
|
||||
private String delFlag;
|
||||
|
||||
@@ -41,6 +41,9 @@ public class CcdiProjectVO {
|
||||
/** 低风险人数 */
|
||||
private Integer lowRiskCount;
|
||||
|
||||
/** 流水分析平台项目ID */
|
||||
private Integer lsfxProjectId;
|
||||
|
||||
/** 创建时间 */
|
||||
private Date createTime;
|
||||
|
||||
|
||||
@@ -10,9 +10,13 @@ import com.ruoyi.ccdi.project.domain.vo.CcdiProjectVO;
|
||||
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
|
||||
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
|
||||
import com.ruoyi.lsfx.domain.request.GetTokenRequest;
|
||||
import com.ruoyi.lsfx.domain.response.GetTokenResponse;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 项目Service实现类
|
||||
@@ -25,21 +29,32 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
|
||||
@Resource
|
||||
private CcdiProjectMapper projectMapper;
|
||||
|
||||
@Resource
|
||||
private LsfxAnalysisClient lsfxAnalysisClient;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public CcdiProjectVO createProject(CcdiProjectSaveDTO dto) {
|
||||
// 1. 调用流水分析平台获取projectId
|
||||
Integer lsfxProjectId = callLsfxPlatform(dto.getProjectName());
|
||||
|
||||
// 2. 创建项目实体
|
||||
CcdiProject project = new CcdiProject();
|
||||
BeanUtils.copyProperties(dto, project);
|
||||
|
||||
// 设置默认值
|
||||
// 3. 设置默认值和流水分析平台ID
|
||||
project.setStatus("0"); // 进行中
|
||||
project.setIsArchived(0); // 未归档
|
||||
project.setTargetCount(0);
|
||||
project.setHighRiskCount(0);
|
||||
project.setMediumRiskCount(0);
|
||||
project.setLowRiskCount(0);
|
||||
project.setLsfxProjectId(lsfxProjectId); // 设置流水分析平台ID
|
||||
|
||||
// 4. 保存到数据库
|
||||
projectMapper.insert(project);
|
||||
|
||||
// 5. 返回VO
|
||||
CcdiProjectVO vo = new CcdiProjectVO();
|
||||
BeanUtils.copyProperties(project, vo);
|
||||
return vo;
|
||||
@@ -120,4 +135,43 @@ public class CcdiProjectServiceImpl implements ICcdiProjectService {
|
||||
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用流水分析平台获取projectId
|
||||
*
|
||||
* @param projectName 项目名称
|
||||
* @return 流水分析平台项目ID
|
||||
* @throws ServiceException 调用失败或响应无效时抛出
|
||||
*/
|
||||
private Integer callLsfxPlatform(String projectName) {
|
||||
// 构建请求参数
|
||||
GetTokenRequest request = new GetTokenRequest();
|
||||
request.setProjectNo("902000_" + System.currentTimeMillis());
|
||||
request.setEntityName(projectName);
|
||||
request.setUserId("902001");
|
||||
request.setUserName("902001");
|
||||
request.setRole("VIEWER");
|
||||
request.setOrgCode("902000");
|
||||
request.setAnalysisType("-1");
|
||||
request.setDepartmentCode("902000");
|
||||
|
||||
// 调用流水分析平台(异常处理和日志已在 LsfxAnalysisClient 中完成)
|
||||
GetTokenResponse response = lsfxAnalysisClient.getToken(request);
|
||||
|
||||
// 业务层校验:确保响应有效
|
||||
if (response == null || response.getData() == null) {
|
||||
throw new ServiceException("流水分析平台响应数据为空");
|
||||
}
|
||||
|
||||
if (response.getData().getProjectId() == null) {
|
||||
throw new ServiceException("流水分析平台返回的projectId为空");
|
||||
}
|
||||
|
||||
// 校验返回码
|
||||
if (!"200".equals(response.getCode())) {
|
||||
throw new ServiceException("流水分析平台返回错误: " + response.getMessage());
|
||||
}
|
||||
|
||||
return response.getData().getProjectId();
|
||||
}
|
||||
}
|
||||
|
||||
334
docs/plans/2026-03-04-project-detail-navigation-menu-design.md
Normal file
334
docs/plans/2026-03-04-project-detail-navigation-menu-design.md
Normal file
@@ -0,0 +1,334 @@
|
||||
# 项目详情页面导航菜单改造设计文档
|
||||
|
||||
## 概述
|
||||
|
||||
将项目详情页面(detail.vue)右侧的按钮组改为水平导航菜单,使用 Element UI Menu 组件实现简洁链接风格。
|
||||
|
||||
## 当前问题
|
||||
|
||||
项目详情页面右侧的操作按钮(上传数据、参数配置、初核结果)占用空间较大,视觉层级不够清晰,交互方式不够统一。
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 核心设计
|
||||
- 使用 Element UI 的 `el-menu` 组件(水平模式)
|
||||
- 菜单放在标题右侧,右对齐
|
||||
- "上传数据"和"参数配置"作为普通菜单项
|
||||
- "初核结果"保留下拉菜单结构,包含三个子项:结果总览、专项排查、流水明细查询
|
||||
- 采用简洁链接风格:透明背景 + 底部下划线激活效果
|
||||
|
||||
### 视觉风格
|
||||
- 默认状态:灰色文字(#606266),透明背景
|
||||
- Hover 状态:浅灰背景(#f5f7fa),深色文字(#303133)
|
||||
- 激活状态:蓝色文字(#1890ff)+ 底部 2px 蓝色下划线
|
||||
- 下拉菜单:白色背景,激活项浅蓝背景(#e6f7ff)
|
||||
|
||||
## 技术设计
|
||||
|
||||
### 1. 组件结构
|
||||
|
||||
#### detail.vue 模板改造
|
||||
|
||||
```vue
|
||||
<div class="header-right">
|
||||
<el-menu
|
||||
:default-active="activeTab"
|
||||
mode="horizontal"
|
||||
@select="handleMenuSelect"
|
||||
class="nav-menu"
|
||||
>
|
||||
<el-menu-item index="upload">上传数据</el-menu-item>
|
||||
<el-menu-item index="config">参数配置</el-menu-item>
|
||||
<el-submenu index="result">
|
||||
<template slot="title">初核结果</template>
|
||||
<el-menu-item index="overview">结果总览</el-menu-item>
|
||||
<el-menu-item index="special">专项排查</el-menu-item>
|
||||
<el-menu-item index="detail">流水明细查询</el-menu-item>
|
||||
</el-submenu>
|
||||
</el-menu>
|
||||
</div>
|
||||
|
||||
<!-- 动态组件渲染区域 -->
|
||||
<component
|
||||
:is="currentComponent"
|
||||
:project-id="projectId"
|
||||
:project-info="projectInfo"
|
||||
@menu-change="handleMenuChange"
|
||||
@data-uploaded="handleDataUploaded"
|
||||
@name-selected="handleNameSelected"
|
||||
@generate-report="handleGenerateReport"
|
||||
@fetch-bank-info="handleFetchBankInfo"
|
||||
/>
|
||||
```
|
||||
|
||||
### 2. 数据结构
|
||||
|
||||
```javascript
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'upload', // 当前激活的菜单项索引
|
||||
currentComponent: 'UploadData', // 当前显示的组件名称
|
||||
// ... 其他现有数据
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 交互逻辑
|
||||
|
||||
```javascript
|
||||
methods: {
|
||||
/** 菜单选择事件 */
|
||||
handleMenuSelect(index) {
|
||||
this.activeTab = index;
|
||||
|
||||
// 组件映射
|
||||
const componentMap = {
|
||||
'upload': 'UploadData',
|
||||
'config': 'ParamConfig',
|
||||
'overview': 'PreliminaryCheck',
|
||||
'special': 'SpecialCheck',
|
||||
'detail': 'DetailQuery'
|
||||
};
|
||||
|
||||
this.currentComponent = componentMap[index] || 'UploadData';
|
||||
},
|
||||
|
||||
// ... 其他现有方法
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 组件导入
|
||||
|
||||
```javascript
|
||||
import UploadData from "./components/detail/UploadData";
|
||||
import ParamConfig from "./components/detail/ParamConfig";
|
||||
import PreliminaryCheck from "./components/detail/PreliminaryCheck";
|
||||
import SpecialCheck from "./components/detail/SpecialCheck";
|
||||
import DetailQuery from "./components/detail/DetailQuery";
|
||||
|
||||
export default {
|
||||
name: "ProjectDetail",
|
||||
components: {
|
||||
UploadData,
|
||||
ParamConfig,
|
||||
PreliminaryCheck,
|
||||
SpecialCheck,
|
||||
DetailQuery,
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## 样式设计
|
||||
|
||||
### 1. 导航菜单样式
|
||||
|
||||
```scss
|
||||
.header-right {
|
||||
.nav-menu {
|
||||
// 移除默认背景色和边框
|
||||
background-color: transparent;
|
||||
border-bottom: none;
|
||||
|
||||
// 菜单项基础样式
|
||||
.el-menu-item,
|
||||
.el-submenu__title {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
padding: 0 16px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
border-bottom: 2px solid transparent;
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f7fa;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
// 激活状态:底部下划线 + 蓝色文字
|
||||
.el-menu-item.is-active {
|
||||
color: #1890ff;
|
||||
border-bottom: 2px solid #1890ff;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
// 下拉菜单激活状态
|
||||
.el-submenu.is-active > .el-submenu__title {
|
||||
color: #1890ff;
|
||||
border-bottom: 2px solid #1890ff;
|
||||
}
|
||||
|
||||
// 下拉菜单图标
|
||||
.el-submenu__icon-arrow {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 下拉菜单弹窗样式
|
||||
|
||||
```scss
|
||||
::v-deep .el-menu--popup {
|
||||
min-width: 140px;
|
||||
|
||||
.el-menu-item {
|
||||
font-size: 14px;
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
color: #1890ff;
|
||||
background-color: #e6f7ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 响应式适配
|
||||
|
||||
```scss
|
||||
@media (max-width: 768px) {
|
||||
.detail-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
.header-right {
|
||||
width: 100%;
|
||||
margin-top: 12px;
|
||||
|
||||
.nav-menu {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
||||
.el-menu-item,
|
||||
.el-submenu {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 0 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 组件规范
|
||||
|
||||
### Props 接口
|
||||
|
||||
所有子组件应接收相同的 props:
|
||||
|
||||
```javascript
|
||||
props: {
|
||||
projectId: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
projectInfo: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
projectName: "",
|
||||
updateTime: "",
|
||||
projectStatus: "0",
|
||||
}),
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
### Events 接口
|
||||
|
||||
```javascript
|
||||
this.$emit('data-uploaded', { type: 'xxx' });
|
||||
this.$emit('generate-report');
|
||||
this.$emit('fetch-bank-info');
|
||||
this.$emit('menu-change', { key, route });
|
||||
this.$emit('name-selected', nameList);
|
||||
```
|
||||
|
||||
## 实施步骤
|
||||
|
||||
### 第一步:修改 detail.vue 文件
|
||||
1. 替换 header-right 中的按钮为 el-menu 组件
|
||||
2. 添加 activeTab 和 currentComponent 数据字段
|
||||
3. 实现 handleMenuSelect 方法
|
||||
4. 添加动态组件渲染区域
|
||||
5. 导入所有子组件
|
||||
|
||||
### 第二步:添加样式
|
||||
1. 添加导航菜单的自定义样式
|
||||
2. 添加下拉菜单样式
|
||||
3. 添加响应式样式
|
||||
|
||||
### 第三步:创建占位组件
|
||||
为未实现的功能创建占位组件:
|
||||
- ParamConfig.vue
|
||||
- PreliminaryCheck.vue
|
||||
- SpecialCheck.vue
|
||||
- DetailQuery.vue
|
||||
|
||||
### 第四步:测试验证
|
||||
- 功能测试:菜单切换、下拉菜单交互
|
||||
- 视觉测试:样式符合设计要求
|
||||
- 响应式测试:移动端布局正常
|
||||
|
||||
## 技术栈
|
||||
|
||||
- Element UI Menu 组件(`el-menu`, `el-menu-item`, `el-submenu`)
|
||||
- Vue 动态组件(`<component :is="...">`)
|
||||
- Scoped CSS 样式覆盖
|
||||
- Vue 2.6.12
|
||||
- Element UI 2.15.14
|
||||
|
||||
## 预期效果
|
||||
|
||||
### 视觉效果
|
||||
- 菜单项横向排列在标题右侧,右对齐
|
||||
- 简洁链接风格,无背景色和边框
|
||||
- 激活项显示蓝色文字和底部下划线
|
||||
- 下拉菜单样式统一
|
||||
|
||||
### 交互效果
|
||||
- 点击菜单项切换组件,URL 不变
|
||||
- 下拉菜单点击外部区域可关闭
|
||||
- 组件切换流畅,数据正确传递
|
||||
- 响应式布局在移动端自适应
|
||||
|
||||
## 代码改动量估算
|
||||
|
||||
- detail.vue 文件改动:约 80-100 行(模板 + 脚本 + 样式)
|
||||
- 占位组件创建:4 个文件,每个约 20 行
|
||||
- 总代码量:约 150-180 行
|
||||
|
||||
## 风险与注意事项
|
||||
|
||||
1. **组件文件不存在**:ParamConfig、PreliminaryCheck 等组件需要创建占位组件
|
||||
2. **样式覆盖**:Element UI 默认样式覆盖需要使用 `::v-deep` 或 `/deep/`
|
||||
3. **事件传递**:确保所有子组件的事件正确向上传递
|
||||
4. **路由监听**:移除原有路由相关的逻辑,改为组件状态管理
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
1. 添加菜单切换动画效果
|
||||
2. 为占位组件实现完整功能
|
||||
3. 添加面包屑导航支持
|
||||
4. 支持菜单项权限控制
|
||||
5. 添加快捷键支持(Ctrl+Tab 切换)
|
||||
|
||||
## 测试清单
|
||||
|
||||
- [ ] 点击"上传数据"菜单,显示 UploadData 组件
|
||||
- [ ] 点击"参数配置"菜单,显示占位组件或 ParamConfig 组件
|
||||
- [ ] 点击"初核结果"菜单,展开下拉菜单
|
||||
- [ ] 点击下拉菜单子项,切换到对应组件
|
||||
- [ ] 激活菜单项显示底部下划线
|
||||
- [ ] Hover 菜单项显示浅灰背景
|
||||
- [ ] 下拉菜单点击外部区域关闭
|
||||
- [ ] 组件切换时 projectId 和 projectInfo 正确传递
|
||||
- [ ] 移动端菜单响应式布局正常
|
||||
- [ ] 现有功能不受影响(返回按钮、项目信息显示等)
|
||||
960
docs/plans/2026-03-04-project-detail-navigation-menu.md
Normal file
960
docs/plans/2026-03-04-project-detail-navigation-menu.md
Normal file
@@ -0,0 +1,960 @@
|
||||
# 项目详情页面导航菜单改造实施计划
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** 将项目详情页面右侧的按钮组改为水平导航菜单,使用 Element UI Menu 组件实现简洁链接风格,支持菜单切换组件内容。
|
||||
|
||||
**Architecture:** 使用 Element UI 的 `el-menu` 组件(水平模式)替换现有的按钮组,通过 Vue 动态组件(`<component :is="...">`)实现内容切换。菜单项包括"上传数据"、"参数配置"和"初核结果"下拉菜单(含三个子项)。采用简洁链接风格,激活状态显示底部下划线。
|
||||
|
||||
**Tech Stack:** Vue 2.6.12, Element UI 2.15.14, Scoped CSS
|
||||
|
||||
---
|
||||
|
||||
## 前置检查
|
||||
|
||||
**验证当前项目状态:**
|
||||
|
||||
```bash
|
||||
cd D:/ccdi/ccdi
|
||||
git status
|
||||
```
|
||||
|
||||
预期:工作目录干净,或只有 CLAUDE.md 修改
|
||||
|
||||
**验证文件存在:**
|
||||
|
||||
```bash
|
||||
ls ruoyi-ui/src/views/ccdiProject/detail.vue
|
||||
ls ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue
|
||||
```
|
||||
|
||||
预期:两个文件都存在
|
||||
|
||||
---
|
||||
|
||||
## Task 1: 创建占位组件 ParamConfig
|
||||
|
||||
**Files:**
|
||||
- Create: `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
|
||||
|
||||
**Step 1: 创建 ParamConfig 占位组件**
|
||||
|
||||
创建文件 `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="param-config-container">
|
||||
<div class="placeholder-content">
|
||||
<i class="el-icon-setting"></i>
|
||||
<p>参数配置功能开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ParamConfig",
|
||||
props: {
|
||||
projectId: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
projectInfo: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
projectName: "",
|
||||
updateTime: "",
|
||||
projectStatus: "0",
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.param-config-container {
|
||||
padding: 40px 20px;
|
||||
background: #fff;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.placeholder-content {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
|
||||
i {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
**Step 2: 提交 ParamConfig 组件**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue
|
||||
git commit -m "feat(ccdiProject): 添加参数配置占位组件"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: 创建占位组件 PreliminaryCheck
|
||||
|
||||
**Files:**
|
||||
- Create: `ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue`
|
||||
|
||||
**Step 1: 创建 PreliminaryCheck 占位组件**
|
||||
|
||||
创建文件 `ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue`:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="preliminary-check-container">
|
||||
<div class="placeholder-content">
|
||||
<i class="el-icon-data-analysis"></i>
|
||||
<p>结果总览功能开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "PreliminaryCheck",
|
||||
props: {
|
||||
projectId: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
projectInfo: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
projectName: "",
|
||||
updateTime: "",
|
||||
projectStatus: "0",
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.preliminary-check-container {
|
||||
padding: 40px 20px;
|
||||
background: #fff;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.placeholder-content {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
|
||||
i {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
**Step 2: 提交 PreliminaryCheck 组件**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue
|
||||
git commit -m "feat(ccdiProject): 添加结果总览占位组件"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 3: 创建占位组件 SpecialCheck
|
||||
|
||||
**Files:**
|
||||
- Create: `ruoyi-ui/src/views/ccdiProject/components/detail/SpecialCheck.vue`
|
||||
|
||||
**Step 1: 创建 SpecialCheck 占位组件**
|
||||
|
||||
创建文件 `ruoyi-ui/src/views/ccdiProject/components/detail/SpecialCheck.vue`:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="special-check-container">
|
||||
<div class="placeholder-content">
|
||||
<i class="el-icon-search"></i>
|
||||
<p>专项排查功能开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "SpecialCheck",
|
||||
props: {
|
||||
projectId: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
projectInfo: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
projectName: "",
|
||||
updateTime: "",
|
||||
projectStatus: "0",
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.special-check-container {
|
||||
padding: 40px 20px;
|
||||
background: #fff;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.placeholder-content {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
|
||||
i {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
**Step 2: 提交 SpecialCheck 组件**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/views/ccdiProject/components/detail/SpecialCheck.vue
|
||||
git commit -m "feat(ccdiProject): 添加专项排查占位组件"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 4: 创建占位组件 DetailQuery
|
||||
|
||||
**Files:**
|
||||
- Create: `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue`
|
||||
|
||||
**Step 1: 创建 DetailQuery 占位组件**
|
||||
|
||||
创建文件 `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue`:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="detail-query-container">
|
||||
<div class="placeholder-content">
|
||||
<i class="el-icon-document"></i>
|
||||
<p>流水明细查询功能开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "DetailQuery",
|
||||
props: {
|
||||
projectId: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
projectInfo: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
projectName: "",
|
||||
updateTime: "",
|
||||
projectStatus: "0",
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.detail-query-container {
|
||||
padding: 40px 20px;
|
||||
background: #fff;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.placeholder-content {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
|
||||
i {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
**Step 2: 提交 DetailQuery 组件**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue
|
||||
git commit -m "feat(ccdiProject): 添加流水明细查询占位组件"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 5: 修改 detail.vue - 添加数据字段和导入组件
|
||||
|
||||
**Files:**
|
||||
- Modify: `ruoyi-ui/src/views/ccdiProject/detail.vue`
|
||||
|
||||
**Step 1: 添加组件导入**
|
||||
|
||||
在 `detail.vue` 的 `<script>` 部分,找到 import 语句(第 72 行附近),替换为:
|
||||
|
||||
```javascript
|
||||
import UploadData from "./components/detail/UploadData";
|
||||
import ParamConfig from "./components/detail/ParamConfig";
|
||||
import PreliminaryCheck from "./components/detail/PreliminaryCheck";
|
||||
import SpecialCheck from "./components/detail/SpecialCheck";
|
||||
import DetailQuery from "./components/detail/DetailQuery";
|
||||
```
|
||||
|
||||
**Step 2: 注册组件**
|
||||
|
||||
在 `components` 对象中(第 81-88 行),替换为:
|
||||
|
||||
```javascript
|
||||
components: {
|
||||
UploadData,
|
||||
ParamConfig,
|
||||
PreliminaryCheck,
|
||||
SpecialCheck,
|
||||
DetailQuery,
|
||||
},
|
||||
```
|
||||
|
||||
**Step 3: 添加数据字段**
|
||||
|
||||
在 `data()` 函数中(第 89-110 行),在 `activeTab: "data"` 之后添加:
|
||||
|
||||
```javascript
|
||||
data() {
|
||||
return {
|
||||
// 当前激活的菜单项索引
|
||||
activeTab: "upload",
|
||||
// 当前显示的组件名称
|
||||
currentComponent: "UploadData",
|
||||
// 项目ID
|
||||
projectId: this.$route.params.projectId,
|
||||
// ... 其他现有数据保持不变
|
||||
projectInfo: {
|
||||
projectId: this.$route.params.projectId,
|
||||
projectName: "",
|
||||
projectDesc: "",
|
||||
createTime: "",
|
||||
updateTime: "",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
targetCount: 0,
|
||||
warningCount: 0,
|
||||
warningThreshold: 60,
|
||||
projectStatus: "0",
|
||||
},
|
||||
};
|
||||
},
|
||||
```
|
||||
|
||||
**Step 4: 验证文件语法正确**
|
||||
|
||||
```bash
|
||||
cd ruoyi-ui
|
||||
npm run lint -- --fix src/views/ccdiProject/detail.vue
|
||||
```
|
||||
|
||||
预期:无语法错误
|
||||
|
||||
**Step 5: 提交组件导入修改**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/views/ccdiProject/detail.vue
|
||||
git commit -m "feat(ccdiProject): 导入子组件并添加菜单状态数据"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 6: 修改 detail.vue - 替换模板中的按钮为菜单
|
||||
|
||||
**Files:**
|
||||
- Modify: `ruoyi-ui/src/views/ccdiProject/detail.vue`
|
||||
|
||||
**Step 1: 替换 header-right 中的按钮组**
|
||||
|
||||
找到 `<div class="header-right">` 部分(第 27-55 行),替换为:
|
||||
|
||||
```vue
|
||||
<div class="header-right">
|
||||
<el-menu
|
||||
:default-active="activeTab"
|
||||
mode="horizontal"
|
||||
@select="handleMenuSelect"
|
||||
class="nav-menu"
|
||||
>
|
||||
<el-menu-item index="upload">上传数据</el-menu-item>
|
||||
<el-menu-item index="config">参数配置</el-menu-item>
|
||||
<el-submenu index="result">
|
||||
<template slot="title">初核结果</template>
|
||||
<el-menu-item index="overview">结果总览</el-menu-item>
|
||||
<el-menu-item index="special">专项排查</el-menu-item>
|
||||
<el-menu-item index="detail">流水明细查询</el-menu-item>
|
||||
</el-submenu>
|
||||
</el-menu>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Step 2: 替换 UploadData 为动态组件**
|
||||
|
||||
找到 `<UploadData>` 组件(第 59-67 行),替换为:
|
||||
|
||||
```vue
|
||||
<!-- 动态组件渲染区域 -->
|
||||
<component
|
||||
:is="currentComponent"
|
||||
:project-id="projectId"
|
||||
:project-info="projectInfo"
|
||||
@menu-change="handleMenuChange"
|
||||
@data-uploaded="handleDataUploaded"
|
||||
@name-selected="handleNameSelected"
|
||||
@generate-report="handleGenerateReport"
|
||||
@fetch-bank-info="handleFetchBankInfo"
|
||||
/>
|
||||
```
|
||||
|
||||
**Step 3: 验证模板语法**
|
||||
|
||||
```bash
|
||||
cd ruoyi-ui
|
||||
npm run lint -- --fix src/views/ccdiProject/detail.vue
|
||||
```
|
||||
|
||||
预期:无语法错误
|
||||
|
||||
**Step 4: 提交模板修改**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/views/ccdiProject/detail.vue
|
||||
git commit -m "feat(ccdiProject): 替换按钮组为导航菜单并使用动态组件"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 7: 修改 detail.vue - 添加菜单选择处理方法
|
||||
|
||||
**Files:**
|
||||
- Modify: `ruoyi-ui/src/views/ccdiProject/detail.vue`
|
||||
|
||||
**Step 1: 添加菜单选择处理方法**
|
||||
|
||||
在 `methods` 对象中(第 124 行),在 `handleBack()` 方法之后添加新方法:
|
||||
|
||||
```javascript
|
||||
/** 菜单选择事件 */
|
||||
handleMenuSelect(index) {
|
||||
console.log("菜单选择:", index);
|
||||
this.activeTab = index;
|
||||
|
||||
// 组件映射
|
||||
const componentMap = {
|
||||
upload: "UploadData",
|
||||
config: "ParamConfig",
|
||||
overview: "PreliminaryCheck",
|
||||
special: "SpecialCheck",
|
||||
detail: "DetailQuery",
|
||||
};
|
||||
|
||||
this.currentComponent = componentMap[index] || "UploadData";
|
||||
},
|
||||
```
|
||||
|
||||
**Step 2: 删除废弃的方法**
|
||||
|
||||
删除以下不再使用的方法(第 226-251 行):
|
||||
- `handleUploadData()`
|
||||
- `handleParamConfig()`
|
||||
- `handleCheckResultCommand()`
|
||||
|
||||
**Step 3: 更新 handleMenuChange 方法**
|
||||
|
||||
修改 `handleMenuChange` 方法(第 185-205 行),简化为:
|
||||
|
||||
```javascript
|
||||
/** UploadData 组件:菜单切换 */
|
||||
handleMenuChange({ key, route }) {
|
||||
console.log("切换到菜单:", key, route);
|
||||
// 直接触发菜单选择
|
||||
this.handleMenuSelect(route);
|
||||
},
|
||||
```
|
||||
|
||||
**Step 4: 验证方法逻辑**
|
||||
|
||||
```bash
|
||||
cd ruoyi-ui
|
||||
npm run lint -- --fix src/views/ccdiProject/detail.vue
|
||||
```
|
||||
|
||||
预期:无语法错误,未使用的方法已删除
|
||||
|
||||
**Step 5: 提交方法修改**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/views/ccdiProject/detail.vue
|
||||
git commit -m "feat(ccdiProject): 添加菜单选择处理方法并清理废弃代码"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 8: 修改 detail.vue - 添加导航菜单样式
|
||||
|
||||
**Files:**
|
||||
- Modify: `ruoyi-ui/src/views/ccdiProject/detail.vue`
|
||||
|
||||
**Step 1: 添加导航菜单样式**
|
||||
|
||||
在 `<style lang="scss" scoped>` 部分(第 306 行之后),在 `.header-right` 样式块内部添加:
|
||||
|
||||
```scss
|
||||
.header-right {
|
||||
.nav-menu {
|
||||
// 移除默认背景色和边框
|
||||
background-color: transparent;
|
||||
border-bottom: none;
|
||||
|
||||
// 菜单项基础样式
|
||||
.el-menu-item,
|
||||
.el-submenu__title {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
padding: 0 16px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
border-bottom: 2px solid transparent;
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f7fa;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
// 激活状态:底部下划线 + 蓝色文字
|
||||
.el-menu-item.is-active {
|
||||
color: #1890ff;
|
||||
border-bottom: 2px solid #1890ff;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
// 下拉菜单激活状态
|
||||
.el-submenu.is-active > .el-submenu__title {
|
||||
color: #1890ff;
|
||||
border-bottom: 2px solid #1890ff;
|
||||
}
|
||||
|
||||
// 下拉菜单图标
|
||||
.el-submenu__icon-arrow {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: 添加下拉菜单弹窗样式**
|
||||
|
||||
在样式末尾(第 496 行之后),添加深度选择器样式:
|
||||
|
||||
```scss
|
||||
// 下拉菜单弹窗样式
|
||||
::v-deep .el-menu--popup {
|
||||
min-width: 140px;
|
||||
|
||||
.el-menu-item {
|
||||
font-size: 14px;
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
color: #1890ff;
|
||||
background-color: #e6f7ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3: 验证样式语法**
|
||||
|
||||
```bash
|
||||
cd ruoyi-ui
|
||||
npm run lint -- --fix src/views/ccdiProject/detail.vue
|
||||
```
|
||||
|
||||
预期:无语法错误
|
||||
|
||||
**Step 4: 提交导航菜单样式**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/views/ccdiProject/detail.vue
|
||||
git commit -m "style(ccdiProject): 添加导航菜单简洁链接风格样式"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 9: 修改 detail.vue - 添加响应式样式
|
||||
|
||||
**Files:**
|
||||
- Modify: `ruoyi-ui/src/views/ccdiProject/detail.vue`
|
||||
|
||||
**Step 1: 添加响应式样式**
|
||||
|
||||
在现有的 `@media (max-width: 768px)` 媒体查询中(第 464 行),找到 `.detail-header` 样式块,添加响应式菜单样式:
|
||||
|
||||
```scss
|
||||
@media (max-width: 768px) {
|
||||
.dpc-detail-container {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
|
||||
.header-right {
|
||||
width: 100%;
|
||||
margin-top: 12px;
|
||||
|
||||
.nav-menu {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
||||
.el-menu-item,
|
||||
.el-submenu {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 0 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ... 其他现有响应式样式保持不变
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: 验证响应式样式**
|
||||
|
||||
```bash
|
||||
cd ruoyi-ui
|
||||
npm run lint -- --fix src/views/ccdiProject/detail.vue
|
||||
```
|
||||
|
||||
预期:无语法错误
|
||||
|
||||
**Step 3: 提交响应式样式**
|
||||
|
||||
```bash
|
||||
git add ruoyi-ui/src/views/ccdiProject/detail.vue
|
||||
git commit -m "style(ccdiProject): 添加导航菜单响应式布局支持"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 10: 手动测试验证
|
||||
|
||||
**Files:**
|
||||
- Test: `ruoyi-ui/src/views/ccdiProject/detail.vue`
|
||||
|
||||
**Step 1: 启动前端开发服务器**
|
||||
|
||||
```bash
|
||||
cd ruoyi-ui
|
||||
npm run dev
|
||||
```
|
||||
|
||||
预期:服务启动成功,监听 http://localhost:80
|
||||
|
||||
**Step 2: 访问项目详情页面**
|
||||
|
||||
在浏览器中访问:`http://localhost/ccdiProject`,点击任意项目进入详情页面
|
||||
|
||||
预期:页面正常加载,右侧显示导航菜单
|
||||
|
||||
**Step 3: 测试菜单切换功能**
|
||||
|
||||
测试步骤:
|
||||
1. 点击"上传数据"菜单项
|
||||
- 预期:激活状态显示蓝色文字和底部下划线,显示 UploadData 组件
|
||||
|
||||
2. 点击"参数配置"菜单项
|
||||
- 预期:激活状态切换,显示 ParamConfig 占位组件("参数配置功能开发中...")
|
||||
|
||||
3. 点击"初核结果"菜单,展开下拉菜单
|
||||
- 预期:下拉菜单展开,显示三个子项
|
||||
|
||||
4. 点击"结果总览"子菜单项
|
||||
- 预期:激活状态切换,显示 PreliminaryCheck 占位组件
|
||||
|
||||
5. 点击"专项排查"子菜单项
|
||||
- 预期:显示 SpecialCheck 占位组件
|
||||
|
||||
6. 点击"流水明细查询"子菜单项
|
||||
- 预期:显示 DetailQuery 占位组件
|
||||
|
||||
**Step 4: 测试样式效果**
|
||||
|
||||
测试步骤:
|
||||
1. Hover 菜单项
|
||||
- 预期:显示浅灰背景(#f5f7fa)
|
||||
|
||||
2. 检查激活菜单项
|
||||
- 预期:蓝色文字(#1890ff)+ 底部 2px 蓝色下划线
|
||||
|
||||
3. 点击下拉菜单外部区域
|
||||
- 预期:下拉菜单关闭
|
||||
|
||||
4. 调整浏览器窗口宽度至 768px 以下
|
||||
- 预期:菜单项平均分配宽度,布局自适应
|
||||
|
||||
**Step 5: 测试数据传递**
|
||||
|
||||
测试步骤:
|
||||
1. 切换到"参数配置"组件
|
||||
2. 在浏览器控制台检查组件 props
|
||||
- 预期:projectId 和 projectInfo 正确传递
|
||||
|
||||
3. 点击"上传数据"中的功能按钮
|
||||
- 预期:事件正常触发,原有功能不受影响
|
||||
|
||||
**Step 6: 记录测试结果**
|
||||
|
||||
创建测试报告文件 `docs/test-reports/2026-03-04-navigation-menu-test.md`:
|
||||
|
||||
```markdown
|
||||
# 项目详情页面导航菜单改造测试报告
|
||||
|
||||
## 测试环境
|
||||
- 浏览器: [记录浏览器名称和版本]
|
||||
- 测试时间: 2026-03-04
|
||||
- 测试人员: [你的名字]
|
||||
|
||||
## 功能测试
|
||||
|
||||
### 菜单切换
|
||||
- [x] 点击"上传数据",显示 UploadData 组件
|
||||
- [x] 点击"参数配置",显示 ParamConfig 占位组件
|
||||
- [x] 点击"初核结果",下拉菜单展开
|
||||
- [x] 点击"结果总览",显示 PreliminaryCheck 占位组件
|
||||
- [x] 点击"专项排查",显示 SpecialCheck 占位组件
|
||||
- [x] 点击"流水明细查询",显示 DetailQuery 占位组件
|
||||
|
||||
### 样式测试
|
||||
- [x] 默认状态:灰色文字,透明背景
|
||||
- [x] Hover 状态:浅灰背景,深色文字
|
||||
- [x] 激活状态:蓝色文字 + 底部下划线
|
||||
- [x] 下拉菜单样式统一
|
||||
|
||||
### 交互测试
|
||||
- [x] 下拉菜单点击外部区域关闭
|
||||
- [x] 菜单切换流畅无闪烁
|
||||
- [x] 组件切换数据正确传递
|
||||
|
||||
### 响应式测试
|
||||
- [x] 移动端菜单布局正常
|
||||
- [x] 菜单项平均分配宽度
|
||||
|
||||
## 问题记录
|
||||
[记录发现的任何问题]
|
||||
|
||||
## 测试结论
|
||||
[通过/需要修复]
|
||||
```
|
||||
|
||||
**Step 7: 提交测试报告**
|
||||
|
||||
```bash
|
||||
git add docs/test-reports/2026-03-04-navigation-menu-test.md
|
||||
git commit -m "test(ccdiProject): 添加导航菜单改造测试报告"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 11: 清理和最终提交
|
||||
|
||||
**Step 1: 检查所有修改文件**
|
||||
|
||||
```bash
|
||||
git status
|
||||
```
|
||||
|
||||
预期:所有修改已提交
|
||||
|
||||
**Step 2: 查看提交历史**
|
||||
|
||||
```bash
|
||||
git log --oneline -10
|
||||
```
|
||||
|
||||
预期:看到 10 个新提交:
|
||||
1. feat(ccdiProject): 添加参数配置占位组件
|
||||
2. feat(ccdiProject): 添加结果总览占位组件
|
||||
3. feat(ccdiProject): 添加专项排查占位组件
|
||||
4. feat(ccdiProject): 添加流水明细查询占位组件
|
||||
5. feat(ccdiProject): 导入子组件并添加菜单状态数据
|
||||
6. feat(ccdiProject): 替换按钮组为导航菜单并使用动态组件
|
||||
7. feat(ccdiProject): 添加菜单选择处理方法并清理废弃代码
|
||||
8. style(ccdiProject): 添加导航菜单简洁链接风格样式
|
||||
9. style(ccdiProject): 添加导航菜单响应式布局支持
|
||||
10. test(ccdiProject): 添加导航菜单改造测试报告
|
||||
|
||||
**Step 3: 验证无遗留问题**
|
||||
|
||||
```bash
|
||||
cd ruoyi-ui
|
||||
npm run lint
|
||||
```
|
||||
|
||||
预期:无 lint 错误
|
||||
|
||||
**Step 4: 推送到远程分支**
|
||||
|
||||
```bash
|
||||
git push origin dev
|
||||
```
|
||||
|
||||
预期:推送成功
|
||||
|
||||
---
|
||||
|
||||
## 实施后检查清单
|
||||
|
||||
- [ ] 所有 4 个占位组件已创建
|
||||
- [ ] detail.vue 已修改完成(模板、脚本、样式)
|
||||
- [ ] 导航菜单样式符合简洁链接风格
|
||||
- [ ] 菜单切换功能正常
|
||||
- [ ] 下拉菜单交互正常
|
||||
- [ ] 响应式布局正常
|
||||
- [ ] 所有修改已提交到 git
|
||||
- [ ] 测试报告已完成
|
||||
- [ ] 代码已推送到远程仓库
|
||||
|
||||
---
|
||||
|
||||
## 预期成果
|
||||
|
||||
### 文件创建
|
||||
- `ruoyi-ui/src/views/ccdiProject/components/detail/ParamConfig.vue`
|
||||
- `ruoyi-ui/src/views/ccdiProject/components/detail/PreliminaryCheck.vue`
|
||||
- `ruoyi-ui/src/views/ccdiProject/components/detail/SpecialCheck.vue`
|
||||
- `ruoyi-ui/src/views/ccdiProject/components/detail/DetailQuery.vue`
|
||||
- `docs/test-reports/2026-03-04-navigation-menu-test.md`
|
||||
|
||||
### 文件修改
|
||||
- `ruoyi-ui/src/views/ccdiProject/detail.vue`(模板、脚本、样式)
|
||||
|
||||
### Git 提交
|
||||
- 10 个功能清晰的提交记录
|
||||
|
||||
### 功能实现
|
||||
- ✅ 水平导航菜单替代按钮组
|
||||
- ✅ 简洁链接风格样式
|
||||
- ✅ 菜单切换组件内容
|
||||
- ✅ 下拉菜单支持
|
||||
- ✅ 响应式布局
|
||||
- ✅ 原有功能不受影响
|
||||
|
||||
---
|
||||
|
||||
## 潜在问题和解决方案
|
||||
|
||||
### 问题 1: 组件切换时状态丢失
|
||||
**解决方案:** 使用 `<keep-alive>` 包裹动态组件(可选优化)
|
||||
|
||||
```vue
|
||||
<keep-alive>
|
||||
<component :is="currentComponent" ... />
|
||||
</keep-alive>
|
||||
```
|
||||
|
||||
### 问题 2: 下拉菜单样式不生效
|
||||
**解决方案:** 检查 `::v-deep` 是否被正确编译,可能需要使用 `/deep/` 或 `>>>`
|
||||
|
||||
### 问题 3: 移动端菜单换行
|
||||
**解决方案:** 调整响应式断点或使用折叠菜单(el-menu 的 collapse 模式)
|
||||
|
||||
### 问题 4: 原有事件未触发
|
||||
**解决方案:** 检查动态组件的事件绑定是否完整,确保所有事件都有对应的处理方法
|
||||
|
||||
---
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
1. **添加组件切换动画**
|
||||
```vue
|
||||
<transition name="fade" mode="out-in">
|
||||
<component :is="currentComponent" ... />
|
||||
</transition>
|
||||
```
|
||||
|
||||
2. **实现占位组件的完整功能**
|
||||
- ParamConfig: 模型参数配置界面
|
||||
- PreliminaryCheck: 结果总览数据展示
|
||||
- SpecialCheck: 专项排查功能
|
||||
- DetailQuery: 流水明细查询和筛选
|
||||
|
||||
3. **添加菜单权限控制**
|
||||
- 根据用户权限显示/隐藏菜单项
|
||||
- 使用 `v-if` 或动态生成菜单配置
|
||||
|
||||
4. **添加面包屑导航**
|
||||
- 在页面顶部显示当前位置
|
||||
- 支持快速返回上级页面
|
||||
|
||||
5. **添加快捷键支持**
|
||||
- Ctrl+Tab: 切换到下一个菜单
|
||||
- Ctrl+Shift+Tab: 切换到上一个菜单
|
||||
|
||||
---
|
||||
|
||||
## 相关技能参考
|
||||
|
||||
- @superpowers:brainstorming - 需求分析和设计
|
||||
- @superpowers:test-driven-development - TDD 开发流程
|
||||
- @superpowers:verification-before-completion - 完成前验证
|
||||
- @superpowers:requesting-code-review - 代码审查
|
||||
|
||||
---
|
||||
|
||||
## 文档参考
|
||||
|
||||
- 设计文档: `docs/plans/2026-03-04-project-detail-navigation-menu-design.md`
|
||||
- Element UI Menu 文档: https://element.eleme.cn/#/zh-CN/component/menu
|
||||
- Vue 动态组件: https://cn.vuejs.org/v2/guide/components.html#动态组件
|
||||
258
docs/test-scripts/README.md
Normal file
258
docs/test-scripts/README.md
Normal file
@@ -0,0 +1,258 @@
|
||||
# 项目创建功能测试说明
|
||||
|
||||
## 概述
|
||||
|
||||
本文档说明如何使用测试脚本测试"创建项目时集成流水分析平台"功能。
|
||||
|
||||
## 测试场景
|
||||
|
||||
### 1. 创建项目成功
|
||||
- **目标**: 验证创建项目时成功调用流水分析平台并保存 `lsfxProjectId`
|
||||
- **步骤**:
|
||||
1. 准备项目数据(项目名称、描述、配置方式)
|
||||
2. 调用创建项目接口
|
||||
3. 验证响应中包含 `lsfxProjectId`
|
||||
4. 验证数据库中 `lsfx_project_id` 字段已保存
|
||||
|
||||
### 2. 创建项目失败(项目名称为空)
|
||||
- **目标**: 验证参数校验功能
|
||||
- **预期**: 接口应拒绝空项目名称,返回错误信息
|
||||
|
||||
### 3. 查询项目列表
|
||||
- **目标**: 验证项目列表中正确显示 `lsfxProjectId`
|
||||
- **步骤**:
|
||||
1. 调用项目列表查询接口
|
||||
2. 验证返回数据包含 `lsfxProjectId` 字段
|
||||
|
||||
### 4. 流水分析平台不可用(可选)
|
||||
- **目标**: 验证异常处理和事务回滚
|
||||
- **步骤**:
|
||||
1. 停止 Mock Server
|
||||
2. 尝试创建项目
|
||||
3. 验证返回错误信息
|
||||
4. 验证数据库无脏数据(事务已回滚)
|
||||
|
||||
## 前置条件
|
||||
|
||||
### 必须条件
|
||||
1. **后端服务已启动**
|
||||
```bash
|
||||
cd ruoyi-admin
|
||||
mvn spring-boot:run
|
||||
```
|
||||
访问地址: http://localhost:8080
|
||||
|
||||
2. **Mock Server 已启动**(测试场景1-3)
|
||||
```bash
|
||||
cd lsfx-mock-server
|
||||
python app.py
|
||||
```
|
||||
访问地址: http://localhost:8000
|
||||
|
||||
3. **数据库连接正常**
|
||||
- 主机: 116.62.17.81
|
||||
- 数据库: ccdi
|
||||
- 用户: root
|
||||
|
||||
### 可选条件
|
||||
- **MySQL客户端**: 用于验证数据库(bash脚本需要)
|
||||
- **PowerShell 5.1+**: 用于运行 PowerShell 脚本
|
||||
- **Git Bash**: 用于运行 bash 脚本(Windows 环境)
|
||||
|
||||
## 测试脚本
|
||||
|
||||
提供了三个版本的测试脚本:
|
||||
|
||||
### 1. Bash 脚本(推荐)
|
||||
|
||||
**适用环境**: Linux、MacOS、Git Bash (Windows)
|
||||
|
||||
**执行方式**:
|
||||
```bash
|
||||
# 进入测试脚本目录
|
||||
cd docs/test-scripts
|
||||
|
||||
# 赋予执行权限
|
||||
chmod +x test-project-creation.sh
|
||||
|
||||
# 执行测试
|
||||
./test-project-creation.sh
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 功能最完整
|
||||
- 支持数据库验证
|
||||
- 彩色输出
|
||||
|
||||
### 2. PowerShell 脚本
|
||||
|
||||
**适用环境**: Windows (PowerShell 5.1+)
|
||||
|
||||
**执行方式**:
|
||||
```powershell
|
||||
# 进入测试脚本目录
|
||||
cd docs\test-scripts
|
||||
|
||||
# 执行测试
|
||||
.\test-project-creation.ps1
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- Windows 原生支持
|
||||
- 交互式提示
|
||||
- 无需额外工具
|
||||
|
||||
### 3. 批处理脚本
|
||||
|
||||
**适用环境**: Windows (CMD)
|
||||
|
||||
**执行方式**:
|
||||
```cmd
|
||||
cd docs\test-scripts
|
||||
test-project-creation.bat
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 简单易用
|
||||
- 无需额外工具
|
||||
|
||||
## 预期结果
|
||||
|
||||
### 成功指标
|
||||
|
||||
1. **创建项目成功**
|
||||
- 响应 `code: 200`
|
||||
- 响应包含 `lsfxProjectId` 字段(如:77)
|
||||
- 数据库 `ccdi_project` 表中 `lsfx_project_id` 不为空
|
||||
|
||||
2. **参数校验**
|
||||
- 空项目名称被拒绝
|
||||
- 返回错误信息
|
||||
|
||||
3. **查询列表**
|
||||
- 响应 `code: 200`
|
||||
- 列表数据包含 `lsfxProjectId` 字段
|
||||
|
||||
4. **异常处理**(可选)
|
||||
- Mock Server 不可用时返回错误
|
||||
- 数据库无脏数据(事务回滚)
|
||||
|
||||
### 测试输出示例
|
||||
|
||||
```
|
||||
==========================================
|
||||
开始执行项目创建功能测试
|
||||
==========================================
|
||||
[INFO] 检查后端服务状态...
|
||||
[INFO] ✓ 后端服务运行正常
|
||||
[INFO] 获取访问令牌...
|
||||
[INFO] ✓ 成功获取令牌
|
||||
==========================================
|
||||
测试场景1:创建项目成功
|
||||
==========================================
|
||||
[INFO] 请求数据: {"projectName":"集成测试项目_20260304_105500","description":"测试集成流水分析平台","configType":"default"}
|
||||
[INFO] 响应内容: {"code":200,"msg":"项目创建成功","data":{...}}
|
||||
[INFO] ✓ 项目创建成功
|
||||
[INFO] ✓ 流水分析平台项目ID: 77
|
||||
[INFO] 验证数据库...
|
||||
[INFO] ✓ 数据库验证通过:lsfx_project_id 已正确保存
|
||||
...
|
||||
==========================================
|
||||
测试结果汇总
|
||||
==========================================
|
||||
[INFO] 通过: 3
|
||||
[ERROR] 失败: 0
|
||||
[INFO] 总计: 3
|
||||
[INFO] ✓ 所有测试通过!
|
||||
```
|
||||
|
||||
## 手动测试(Swagger UI)
|
||||
|
||||
如果需要手动测试,可以使用 Swagger UI:
|
||||
|
||||
1. **访问 Swagger UI**
|
||||
```
|
||||
http://localhost:8080/swagger-ui/index.html
|
||||
```
|
||||
|
||||
2. **获取 Token**
|
||||
- 找到 `/login/test` 接口
|
||||
- 点击 "Try it out"
|
||||
- 输入 username: `admin`, password: `admin123`
|
||||
- 点击 "Execute"
|
||||
- 复制返回的 token
|
||||
|
||||
3. **设置认证**
|
||||
- 点击页面顶部 "Authorize" 按钮
|
||||
- 输入 `Bearer <token>`
|
||||
- 点击 "Authorize"
|
||||
|
||||
4. **创建项目**
|
||||
- 找到 `POST /ccdi/project` 接口
|
||||
- 点击 "Try it out"
|
||||
- 输入请求体:
|
||||
```json
|
||||
{
|
||||
"projectName": "手动测试项目",
|
||||
"description": "通过Swagger测试",
|
||||
"configType": "default"
|
||||
}
|
||||
```
|
||||
- 点击 "Execute"
|
||||
- 查看响应,验证 `lsfxProjectId` 存在
|
||||
|
||||
5. **查询项目**
|
||||
- 找到 `GET /ccdi/project/list` 接口
|
||||
- 点击 "Try it out"
|
||||
- 点击 "Execute"
|
||||
- 验证返回数据包含 `lsfxProjectId`
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 问题1: 后端服务连接失败
|
||||
**原因**: 后端未启动或端口被占用
|
||||
**解决**:
|
||||
- 检查端口 8080 是否被占用
|
||||
- 重启后端服务
|
||||
|
||||
### 问题2: Mock Server 连接失败
|
||||
**原因**: Mock Server 未启动
|
||||
**解决**:
|
||||
```bash
|
||||
cd lsfx-mock-server
|
||||
python app.py
|
||||
```
|
||||
|
||||
### 问题3: 获取 Token 失败
|
||||
**原因**: 用户名或密码错误
|
||||
**解决**:
|
||||
- 确认使用 admin/admin123
|
||||
- 检查后端日志
|
||||
|
||||
### 问题4: lsfxProjectId 为空
|
||||
**原因**: 流水分析平台调用失败
|
||||
**解决**:
|
||||
- 检查 Mock Server 是否运行
|
||||
- 查看后端日志中的错误信息
|
||||
- 确认 `LsfxAnalysisClient` 配置正确
|
||||
|
||||
### 问题5: 数据库验证失败
|
||||
**原因**: 数据库连接问题或字段未保存
|
||||
**解决**:
|
||||
- 检查数据库连接配置
|
||||
- 确认 `lsfx_project_id` 字段已添加到表
|
||||
- 查看后端日志
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **测试顺序**: 按照场景1→2→3→4的顺序执行
|
||||
2. **数据清理**: 测试会创建临时项目数据,建议定期清理
|
||||
3. **Mock Server**: 场景4需要停止 Mock Server,其他场景需要运行
|
||||
4. **事务回滚**: 异常场景验证事务是否正确回滚
|
||||
5. **权限**: 测试账号需要有项目创建权限
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [设计文档](../design/2026-03-04-create-project-integrate-lsfx-design.md)
|
||||
- [实施计划](../plans/2026-03-04-create-project-integrate-lsfx.md)
|
||||
- [流水分析对接文档](../../assets/对接流水分析/兰溪-流水分析对接-新版.md)
|
||||
226
docs/test-scripts/test-project-creation.bat
Normal file
226
docs/test-scripts/test-project-creation.bat
Normal file
@@ -0,0 +1,226 @@
|
||||
@echo off
|
||||
REM ====================================
|
||||
REM 项目创建功能测试脚本 (Windows版本)
|
||||
REM 功能:测试创建项目时集成流水分析平台
|
||||
REM 作者:Claude Code
|
||||
REM 日期:2026-03-04
|
||||
REM ====================================
|
||||
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
REM 配置
|
||||
set BASE_URL=http://localhost:8080
|
||||
set USERNAME=admin
|
||||
set PASSWORD=admin123
|
||||
set TOKEN=
|
||||
|
||||
REM 颜色设置(Windows 10+)
|
||||
set "GREEN=[92m"
|
||||
set "RED=[91m"
|
||||
set "YELLOW=[93m"
|
||||
set "NC=[0m"
|
||||
|
||||
REM 计数器
|
||||
set PASS_COUNT=0
|
||||
set FAIL_COUNT=0
|
||||
|
||||
REM 日志函数
|
||||
goto :main
|
||||
|
||||
:log_info
|
||||
echo %GREEN%[INFO]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
:log_error
|
||||
echo %RED%[ERROR]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
:log_warning
|
||||
echo %YELLOW%[WARNING]%NC% %~1
|
||||
goto :eof
|
||||
|
||||
REM 检查curl是否存在
|
||||
:check_curl
|
||||
where curl >nul 2>&1
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
call :log_error "curl 未安装,请先安装 curl 或使用 Git Bash 运行 test-project-creation.sh"
|
||||
exit /b 1
|
||||
)
|
||||
exit /b 0
|
||||
|
||||
REM 检查后端服务
|
||||
:check_backend_service
|
||||
call :log_info "检查后端服务状态..."
|
||||
curl -s --connect-timeout 5 "%BASE_URL%/actuator/health" >nul 2>&1
|
||||
if %ERRORLEVEL% equ 0 (
|
||||
call :log_info "✓ 后端服务运行正常"
|
||||
exit /b 0
|
||||
) else (
|
||||
call :log_error "✗ 后端服务未运行,请先启动后端服务"
|
||||
call :log_info "启动命令: cd ruoyi-admin && mvn spring-boot:run"
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM 获取访问令牌
|
||||
:get_token
|
||||
call :log_info "获取访问令牌..."
|
||||
for /f "delims=" %%i in ('curl -s -X POST "%BASE_URL%/login/test?username=%USERNAME%&password=%PASSWORD%"') do set TOKEN_RESPONSE=%%i
|
||||
|
||||
REM 提取token(简单解析)
|
||||
for /f "tokens=2 delims=:," %%a in ('echo %TOKEN_RESPONSE% ^| findstr /r "token"') do (
|
||||
set TOKEN=%%a
|
||||
set TOKEN=!TOKEN:"=!
|
||||
goto :token_extracted
|
||||
)
|
||||
|
||||
:token_extracted
|
||||
if "%TOKEN%"=="" (
|
||||
call :log_error "获取令牌失败:无法从响应中提取 token"
|
||||
call :log_info "响应内容: %TOKEN_RESPONSE%"
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
call :log_info "✓ 成功获取令牌"
|
||||
exit /b 0
|
||||
|
||||
REM 测试场景1:创建项目成功
|
||||
:test_create_project_success
|
||||
call :log_info "=========================================="
|
||||
call :log_info "测试场景1:创建项目成功"
|
||||
call :log_info "=========================================="
|
||||
|
||||
REM 生成时间戳
|
||||
for /f "tokens=1-6 delims=/:. " %%a in ("%date% %time%") do (
|
||||
set TIMESTAMP=%%a%%b%%c_%%d%%e%%f
|
||||
)
|
||||
set PROJECT_NAME=集成测试项目_%TIMESTAMP%
|
||||
|
||||
REM 准备JSON数据(需要转义)
|
||||
set REQUEST_DATA={"projectName":"%PROJECT_NAME%","description":"测试集成流水分析平台","configType":"default"}
|
||||
|
||||
call :log_info "请求数据: %REQUEST_DATA%"
|
||||
|
||||
REM 发送请求并保存响应到文件
|
||||
curl -s -X POST "%BASE_URL%/ccdi/project" ^
|
||||
-H "Content-Type: application/json" ^
|
||||
-H "Authorization: Bearer %TOKEN%" ^
|
||||
-d "%REQUEST_DATA%" > response.json
|
||||
|
||||
type response.json
|
||||
echo.
|
||||
|
||||
REM 检查是否成功
|
||||
findstr /c:"code":200 response.json >nul
|
||||
if %ERRORLEVEL% equ 0 (
|
||||
call :log_info "✓ 项目创建成功"
|
||||
|
||||
REM 检查lsfxProjectId
|
||||
findstr /c:"lsfxProjectId" response.json >nul
|
||||
if %ERRORLEVEL% equ 0 (
|
||||
call :log_info "✓ 流水分析平台项目ID存在"
|
||||
set /a PASS_COUNT+=1
|
||||
) else (
|
||||
call :log_error "✗ 流水分析平台项目ID为空"
|
||||
set /a FAIL_COUNT+=1
|
||||
)
|
||||
) else (
|
||||
call :log_error "✗ 项目创建失败"
|
||||
set /a FAIL_COUNT+=1
|
||||
)
|
||||
|
||||
del response.json
|
||||
exit /b 0
|
||||
|
||||
REM 测试场景2:创建项目失败(项目名称为空)
|
||||
:test_create_project_empty_name
|
||||
call :log_info "=========================================="
|
||||
call :log_info "测试场景2:创建项目失败(项目名称为空)"
|
||||
call :log_info "=========================================="
|
||||
|
||||
set REQUEST_DATA={"projectName":"","description":"测试异常场景","configType":"default"}
|
||||
|
||||
call :log_info "请求数据: %REQUEST_DATA%"
|
||||
|
||||
curl -s -X POST "%BASE_URL%/ccdi/project" ^
|
||||
-H "Content-Type: application/json" ^
|
||||
-H "Authorization: Bearer %TOKEN%" ^
|
||||
-d "%REQUEST_DATA%" > response.json
|
||||
|
||||
REM 检查是否失败
|
||||
findstr /c:"code":200 response.json >nul
|
||||
if %ERRORLEVEL% neq 0 (
|
||||
call :log_info "✓ 正确拒绝了空项目名称"
|
||||
set /a PASS_COUNT+=1
|
||||
) else (
|
||||
call :log_error "✗ 未正确验证项目名称"
|
||||
set /a FAIL_COUNT+=1
|
||||
)
|
||||
|
||||
del response.json
|
||||
exit /b 0
|
||||
|
||||
REM 测试场景3:查询项目列表
|
||||
:test_query_project_list
|
||||
call :log_info "=========================================="
|
||||
call :log_info "测试场景3:查询项目列表"
|
||||
call :log_info "=========================================="
|
||||
|
||||
curl -s -X GET "%BASE_URL%/ccdi/project/list?pageNum=1&pageSize=10" ^
|
||||
-H "Authorization: Bearer %TOKEN%" > response.json
|
||||
|
||||
REM 检查是否成功
|
||||
findstr /c:"code":200 response.json >nul
|
||||
if %ERRORLEVEL% equ 0 (
|
||||
call :log_info "✓ 查询项目列表成功"
|
||||
|
||||
REM 检查lsfxProjectId
|
||||
findstr /c:"lsfxProjectId" response.json >nul
|
||||
if %ERRORLEVEL% equ 0 (
|
||||
call :log_info "✓ 项目列表包含 lsfxProjectId 字段"
|
||||
) else (
|
||||
call :log_warning "! 项目列表可能缺少 lsfxProjectId 字段"
|
||||
)
|
||||
set /a PASS_COUNT+=1
|
||||
) else (
|
||||
call :log_error "✗ 查询项目列表失败"
|
||||
set /a FAIL_COUNT+=1
|
||||
)
|
||||
|
||||
del response.json
|
||||
exit /b 0
|
||||
|
||||
REM 主函数
|
||||
:main
|
||||
call :log_info "=========================================="
|
||||
call :log_info "开始执行项目创建功能测试"
|
||||
call :log_info "=========================================="
|
||||
|
||||
REM 检查curl
|
||||
call :check_curl || exit /b 1
|
||||
|
||||
REM 检查后端服务
|
||||
call :check_backend_service || exit /b 1
|
||||
|
||||
REM 获取令牌
|
||||
call :get_token || exit /b 1
|
||||
|
||||
REM 执行测试
|
||||
call :test_create_project_success
|
||||
call :test_create_project_empty_name
|
||||
call :test_query_project_list
|
||||
|
||||
REM 输出测试结果
|
||||
call :log_info "=========================================="
|
||||
call :log_info "测试结果汇总"
|
||||
call :log_info "=========================================="
|
||||
call :log_info "通过: %PASS_COUNT%"
|
||||
call :log_error "失败: %FAIL_COUNT%"
|
||||
call :log_info "总计: %PASS_COUNT%"
|
||||
|
||||
if %FAIL_COUNT% equ 0 (
|
||||
call :log_info "✓ 所有测试通过!"
|
||||
exit /b 0
|
||||
) else (
|
||||
call :log_error "✗ 存在失败的测试"
|
||||
exit /b 1
|
||||
)
|
||||
300
docs/test-scripts/test-project-creation.ps1
Normal file
300
docs/test-scripts/test-project-creation.ps1
Normal file
@@ -0,0 +1,300 @@
|
||||
# ====================================
|
||||
# 项目创建功能测试脚本 (PowerShell版本)
|
||||
# 功能:测试创建项目时集成流水分析平台
|
||||
# 作者:Claude Code
|
||||
# 日期:2026-03-04
|
||||
# ====================================
|
||||
|
||||
# 配置
|
||||
$BaseUrl = "http://localhost:8080"
|
||||
$Username = "admin"
|
||||
$Password = "admin123"
|
||||
$Token = $null
|
||||
|
||||
# 计数器
|
||||
$PassCount = 0
|
||||
$FailCount = 0
|
||||
|
||||
# 日志函数
|
||||
function Write-LogInfo {
|
||||
param([string]$Message)
|
||||
Write-Host "[INFO] " -ForegroundColor Green -NoNewline
|
||||
Write-Host $Message
|
||||
}
|
||||
|
||||
function Write-LogError {
|
||||
param([string]$Message)
|
||||
Write-Host "[ERROR] " -ForegroundColor Red -NoNewline
|
||||
Write-Host $Message
|
||||
}
|
||||
|
||||
function Write-LogWarning {
|
||||
param([string]$Message)
|
||||
Write-Host "[WARNING] " -ForegroundColor Yellow -NoNewline
|
||||
Write-Host $Message
|
||||
}
|
||||
|
||||
# 检查后端服务
|
||||
function Test-BackendService {
|
||||
Write-LogInfo "检查后端服务状态..."
|
||||
try {
|
||||
$response = Invoke-WebRequest -Uri "$BaseUrl/actuator/health" -TimeoutSec 5 -ErrorAction Stop
|
||||
Write-LogInfo "✓ 后端服务运行正常"
|
||||
return $true
|
||||
} catch {
|
||||
Write-LogError "✗ 后端服务未运行,请先启动后端服务"
|
||||
Write-LogInfo "启动命令: cd ruoyi-admin; mvn spring-boot:run"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# 获取访问令牌
|
||||
function Get-AccessToken {
|
||||
Write-LogInfo "获取访问令牌..."
|
||||
try {
|
||||
$response = Invoke-RestMethod -Uri "$BaseUrl/login/test?username=$Username&password=$Password" -Method POST
|
||||
|
||||
if ($response.code -eq 200 -and $response.token) {
|
||||
$script:Token = $response.token
|
||||
Write-LogInfo "✓ 成功获取令牌"
|
||||
return $true
|
||||
} else {
|
||||
Write-LogError "获取令牌失败:响应格式不正确"
|
||||
Write-LogInfo "响应内容: $($response | ConvertTo-Json)"
|
||||
return $false
|
||||
}
|
||||
} catch {
|
||||
Write-LogError "获取令牌失败: $($_.Exception.Message)"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# 测试场景1:创建项目成功
|
||||
function Test-CreateProjectSuccess {
|
||||
Write-LogInfo "=========================================="
|
||||
Write-LogInfo "测试场景1:创建项目成功"
|
||||
Write-LogInfo "=========================================="
|
||||
|
||||
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
||||
$projectName = "集成测试项目_$timestamp"
|
||||
|
||||
$requestData = @{
|
||||
projectName = $projectName
|
||||
description = "测试集成流水分析平台"
|
||||
configType = "default"
|
||||
} | ConvertTo-Json
|
||||
|
||||
Write-LogInfo "请求数据: $requestData"
|
||||
|
||||
try {
|
||||
$headers = @{
|
||||
"Content-Type" = "application/json"
|
||||
"Authorization" = "Bearer $Token"
|
||||
}
|
||||
|
||||
$response = Invoke-RestMethod -Uri "$BaseUrl/ccdi/project" -Method POST -Headers $headers -Body $requestData
|
||||
|
||||
Write-LogInfo "响应内容: $($response | ConvertTo-Json -Depth 5)"
|
||||
|
||||
if ($response.code -eq 200) {
|
||||
Write-LogInfo "✓ 项目创建成功"
|
||||
|
||||
if ($response.data.lsfxProjectId) {
|
||||
Write-LogInfo "✓ 流水分析平台项目ID: $($response.data.lsfxProjectId)"
|
||||
$script:PassCount++
|
||||
return $true
|
||||
} else {
|
||||
Write-LogError "✗ 流水分析平台项目ID为空"
|
||||
$script:FailCount++
|
||||
return $false
|
||||
}
|
||||
} else {
|
||||
Write-LogError "✗ 项目创建失败: $($response.msg)"
|
||||
$script:FailCount++
|
||||
return $false
|
||||
}
|
||||
} catch {
|
||||
Write-LogError "请求失败: $($_.Exception.Message)"
|
||||
$script:FailCount++
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# 测试场景2:创建项目失败(项目名称为空)
|
||||
function Test-CreateProjectEmptyName {
|
||||
Write-LogInfo "=========================================="
|
||||
Write-LogInfo "测试场景2:创建项目失败(项目名称为空)"
|
||||
Write-LogInfo "=========================================="
|
||||
|
||||
$requestData = @{
|
||||
projectName = ""
|
||||
description = "测试异常场景"
|
||||
configType = "default"
|
||||
} | ConvertTo-Json
|
||||
|
||||
Write-LogInfo "请求数据: $requestData"
|
||||
|
||||
try {
|
||||
$headers = @{
|
||||
"Content-Type" = "application/json"
|
||||
"Authorization" = "Bearer $Token"
|
||||
}
|
||||
|
||||
$response = Invoke-RestMethod -Uri "$BaseUrl/ccdi/project" -Method POST -Headers $headers -Body $requestData
|
||||
|
||||
if ($response.code -ne 200) {
|
||||
Write-LogInfo "✓ 正确拒绝了空项目名称"
|
||||
$script:PassCount++
|
||||
return $true
|
||||
} else {
|
||||
Write-LogError "✗ 未正确验证项目名称"
|
||||
$script:FailCount++
|
||||
return $false
|
||||
}
|
||||
} catch {
|
||||
Write-LogInfo "✓ 正确拒绝了空项目名称(请求失败)"
|
||||
$script:PassCount++
|
||||
return $true
|
||||
}
|
||||
}
|
||||
|
||||
# 测试场景3:查询项目列表
|
||||
function Test-QueryProjectList {
|
||||
Write-LogInfo "=========================================="
|
||||
Write-LogInfo "测试场景3:查询项目列表"
|
||||
Write-LogInfo "=========================================="
|
||||
|
||||
try {
|
||||
$headers = @{
|
||||
"Authorization" = "Bearer $Token"
|
||||
}
|
||||
|
||||
$response = Invoke-RestMethod -Uri "$BaseUrl/ccdi/project/list?pageNum=1&pageSize=10" -Method GET -Headers $headers
|
||||
|
||||
Write-LogInfo "响应内容(前500字符): $($response | ConvertTo-Json -Depth 3 | Select-Object -First 500)"
|
||||
|
||||
if ($response.code -eq 200) {
|
||||
Write-LogInfo "✓ 查询项目列表成功"
|
||||
|
||||
if ($response.rows -and $response.rows[0].lsfxProjectId) {
|
||||
Write-LogInfo "✓ 项目列表包含 lsfxProjectId 字段"
|
||||
} else {
|
||||
Write-LogWarning "! 项目列表可能缺少 lsfxProjectId 字段"
|
||||
}
|
||||
$script:PassCount++
|
||||
return $true
|
||||
} else {
|
||||
Write-LogError "✗ 查询项目列表失败"
|
||||
$script:FailCount++
|
||||
return $false
|
||||
}
|
||||
} catch {
|
||||
Write-LogError "请求失败: $($_.Exception.Message)"
|
||||
$script:FailCount++
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# 测试场景4:流水分析平台不可用
|
||||
function Test-LsfxUnavailable {
|
||||
Write-LogInfo "=========================================="
|
||||
Write-LogInfo "测试场景4:流水分析平台不可用"
|
||||
Write-LogInfo "=========================================="
|
||||
Write-LogWarning "注意:此测试需要停止 Mock Server"
|
||||
Write-LogInfo "请手动停止 lsfx-mock-server 并重新运行此测试"
|
||||
|
||||
$confirm = Read-Host "是否已停止 Mock Server?(y/n)"
|
||||
if ($confirm -ne "y") {
|
||||
Write-LogInfo "跳过此测试"
|
||||
return
|
||||
}
|
||||
|
||||
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
||||
$projectName = "异常测试项目_$timestamp"
|
||||
|
||||
$requestData = @{
|
||||
projectName = $projectName
|
||||
description = "测试流水分析平台不可用"
|
||||
configType = "default"
|
||||
} | ConvertTo-Json
|
||||
|
||||
Write-LogInfo "请求数据: $requestData"
|
||||
|
||||
try {
|
||||
$headers = @{
|
||||
"Content-Type" = "application/json"
|
||||
"Authorization" = "Bearer $Token"
|
||||
}
|
||||
|
||||
$response = Invoke-RestMethod -Uri "$BaseUrl/ccdi/project" -Method POST -Headers $headers -Body $requestData
|
||||
|
||||
if ($response.code -eq 500) {
|
||||
Write-LogInfo "✓ 正确处理了流水分析平台不可用的情况"
|
||||
Write-LogInfo "错误信息: $($response.msg)"
|
||||
|
||||
# 注意:PowerShell版本无法直接验证数据库,需要MySQL工具
|
||||
Write-LogWarning "请手动验证数据库无脏数据"
|
||||
|
||||
$script:PassCount++
|
||||
return $true
|
||||
} else {
|
||||
Write-LogError "✗ 未正确处理异常情况"
|
||||
$script:FailCount++
|
||||
return $false
|
||||
}
|
||||
} catch {
|
||||
Write-LogError "请求失败: $($_.Exception.Message)"
|
||||
$script:FailCount++
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# 主函数
|
||||
function Main {
|
||||
Write-LogInfo "=========================================="
|
||||
Write-LogInfo "开始执行项目创建功能测试"
|
||||
Write-LogInfo "=========================================="
|
||||
|
||||
# 检查后端服务
|
||||
if (-not (Test-BackendService)) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 获取令牌
|
||||
if (-not (Get-AccessToken)) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 执行测试
|
||||
Test-CreateProjectSuccess
|
||||
Test-CreateProjectEmptyName
|
||||
Test-QueryProjectList
|
||||
|
||||
# 可选测试
|
||||
Write-LogInfo "=========================================="
|
||||
Write-LogInfo "可选测试:流水分析平台不可用场景"
|
||||
Write-LogInfo "=========================================="
|
||||
$runUnavailableTest = Read-Host "是否执行流水分析平台不可用测试?(y/n)"
|
||||
if ($runUnavailableTest -eq "y") {
|
||||
Test-LsfxUnavailable
|
||||
}
|
||||
|
||||
# 输出测试结果
|
||||
Write-LogInfo "=========================================="
|
||||
Write-LogInfo "测试结果汇总"
|
||||
Write-LogInfo "=========================================="
|
||||
Write-LogInfo "通过: $PassCount"
|
||||
Write-LogError "失败: $FailCount"
|
||||
Write-LogInfo "总计: $($PassCount + $FailCount)"
|
||||
|
||||
if ($FailCount -eq 0) {
|
||||
Write-LogInfo "✓ 所有测试通过!"
|
||||
exit 0
|
||||
} else {
|
||||
Write-LogError "✗ 存在失败的测试"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# 执行主函数
|
||||
Main
|
||||
335
docs/test-scripts/test-project-creation.sh
Normal file
335
docs/test-scripts/test-project-creation.sh
Normal file
@@ -0,0 +1,335 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ====================================
|
||||
# 项目创建功能测试脚本
|
||||
# 功能:测试创建项目时集成流水分析平台
|
||||
# 作者:Claude Code
|
||||
# 日期:2026-03-04
|
||||
# ====================================
|
||||
|
||||
# 配置
|
||||
BASE_URL="http://localhost:8080"
|
||||
USERNAME="admin"
|
||||
PASSWORD="admin123"
|
||||
|
||||
# 颜色输出
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查命令是否存在
|
||||
check_command() {
|
||||
if ! command -v $1 &> /dev/null; then
|
||||
log_error "$1 未安装,请先安装 $1"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查后端服务是否运行
|
||||
check_backend_service() {
|
||||
log_info "检查后端服务状态..."
|
||||
if curl -s --connect-timeout 5 "$BASE_URL/actuator/health" > /dev/null 2>&1; then
|
||||
log_info "✓ 后端服务运行正常"
|
||||
return 0
|
||||
else
|
||||
log_error "✗ 后端服务未运行,请先启动后端服务"
|
||||
log_info "启动命令: cd ruoyi-admin && mvn spring-boot:run"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 获取访问令牌
|
||||
get_token() {
|
||||
log_info "获取访问令牌..."
|
||||
TOKEN_RESPONSE=$(curl -s -X POST "$BASE_URL/login/test?username=$USERNAME&password=$PASSWORD")
|
||||
|
||||
# 检查响应是否为空
|
||||
if [ -z "$TOKEN_RESPONSE" ]; then
|
||||
log_error "获取令牌失败:响应为空"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 提取 token(假设返回格式为 {"code":200,"msg":"操作成功","data":"token"})
|
||||
TOKEN=$(echo "$TOKEN_RESPONSE" | grep -o '"token":"[^"]*"' | sed 's/"token":"//;s/"//')
|
||||
|
||||
if [ -z "$TOKEN" ]; then
|
||||
log_error "获取令牌失败:无法从响应中提取 token"
|
||||
log_info "响应内容: $TOKEN_RESPONSE"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "✓ 成功获取令牌"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 测试场景1:创建项目成功
|
||||
test_create_project_success() {
|
||||
log_info "=========================================="
|
||||
log_info "测试场景1:创建项目成功"
|
||||
log_info "=========================================="
|
||||
|
||||
# 准备测试数据
|
||||
PROJECT_NAME="集成测试项目_$(date +%Y%m%d_%H%M%S)"
|
||||
REQUEST_DATA=$(cat <<EOF
|
||||
{
|
||||
"projectName": "$PROJECT_NAME",
|
||||
"description": "测试集成流水分析平台",
|
||||
"configType": "default"
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
log_info "请求数据: $REQUEST_DATA"
|
||||
|
||||
# 发送请求
|
||||
RESPONSE=$(curl -s -X POST "$BASE_URL/ccdi/project" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-d "$REQUEST_DATA")
|
||||
|
||||
log_info "响应内容: $RESPONSE"
|
||||
|
||||
# 验证响应
|
||||
CODE=$(echo "$RESPONSE" | grep -o '"code":[0-9]*' | sed 's/"code"://')
|
||||
MSG=$(echo "$RESPONSE" | grep -o '"msg":"[^"]*"' | sed 's/"msg":"//;s/"//')
|
||||
|
||||
if [ "$CODE" == "200" ]; then
|
||||
log_info "✓ 项目创建成功"
|
||||
|
||||
# 验证 lsfxProjectId 是否存在
|
||||
LSFX_PROJECT_ID=$(echo "$RESPONSE" | grep -o '"lsfxProjectId":[0-9]*' | sed 's/"lsfxProjectId"://')
|
||||
if [ -n "$LSFX_PROJECT_ID" ]; then
|
||||
log_info "✓ 流水分析平台项目ID: $LSFX_PROJECT_ID"
|
||||
else
|
||||
log_error "✗ 流水分析平台项目ID为空"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 验证数据库
|
||||
log_info "验证数据库..."
|
||||
DB_CHECK=$(mysql -h 116.62.17.81 -u root -pKfcx@1234 ccdi -N -B -e \
|
||||
"SELECT COUNT(*) FROM ccdi_project WHERE project_name='$PROJECT_NAME' AND lsfx_project_id IS NOT NULL;" 2>/dev/null)
|
||||
|
||||
if [ "$DB_CHECK" == "1" ]; then
|
||||
log_info "✓ 数据库验证通过:lsfx_project_id 已正确保存"
|
||||
else
|
||||
log_error "✗ 数据库验证失败:lsfx_project_id 未保存"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
else
|
||||
log_error "✗ 项目创建失败: $MSG"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 测试场景2:创建项目失败(项目名称为空)
|
||||
test_create_project_empty_name() {
|
||||
log_info "=========================================="
|
||||
log_info "测试场景2:创建项目失败(项目名称为空)"
|
||||
log_info "=========================================="
|
||||
|
||||
REQUEST_DATA=$(cat <<EOF
|
||||
{
|
||||
"projectName": "",
|
||||
"description": "测试异常场景",
|
||||
"configType": "default"
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
log_info "请求数据: $REQUEST_DATA"
|
||||
|
||||
RESPONSE=$(curl -s -X POST "$BASE_URL/ccdi/project" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-d "$REQUEST_DATA")
|
||||
|
||||
log_info "响应内容: $RESPONSE"
|
||||
|
||||
CODE=$(echo "$RESPONSE" | grep -o '"code":[0-9]*' | sed 's/"code"://')
|
||||
|
||||
if [ "$CODE" != "200" ]; then
|
||||
log_info "✓ 正确拒绝了空项目名称"
|
||||
return 0
|
||||
else
|
||||
log_error "✗ 未正确验证项目名称"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 测试场景3:流水分析平台不可用
|
||||
test_lsfx_unavailable() {
|
||||
log_info "=========================================="
|
||||
log_info "测试场景3:流水分析平台不可用"
|
||||
log_info "=========================================="
|
||||
log_warning "注意:此测试需要停止 Mock Server"
|
||||
log_info "请手动停止 lsfx-mock-server 并重新运行此测试"
|
||||
log_info "提示:在 lsfx-mock-server 目录按 Ctrl+C 停止"
|
||||
|
||||
# 询问用户是否继续
|
||||
read -p "是否已停止 Mock Server?(y/n): " confirm
|
||||
if [ "$confirm" != "y" ]; then
|
||||
log_info "跳过此测试"
|
||||
return 0
|
||||
fi
|
||||
|
||||
REQUEST_DATA=$(cat <<EOF
|
||||
{
|
||||
"projectName": "异常测试项目_$(date +%Y%m%d_%H%M%S)",
|
||||
"description": "测试流水分析平台不可用",
|
||||
"configType": "default"
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
log_info "请求数据: $REQUEST_DATA"
|
||||
|
||||
RESPONSE=$(curl -s -X POST "$BASE_URL/ccdi/project" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-d "$REQUEST_DATA")
|
||||
|
||||
log_info "响应内容: $RESPONSE"
|
||||
|
||||
CODE=$(echo "$RESPONSE" | grep -o '"code":[0-9]*' | sed 's/"code"://')
|
||||
MSG=$(echo "$RESPONSE" | grep -o '"msg":"[^"]*"' | sed 's/"msg":"//;s/"//')
|
||||
|
||||
if [ "$CODE" == "500" ]; then
|
||||
log_info "✓ 正确处理了流水分析平台不可用的情况"
|
||||
log_info "错误信息: $MSG"
|
||||
|
||||
# 验证数据库没有脏数据
|
||||
PROJECT_NAME=$(echo "$REQUEST_DATA" | grep -o '"projectName":"[^"]*"' | sed 's/"projectName":"//;s/"//')
|
||||
DB_CHECK=$(mysql -h 116.62.17.81 -u root -pKfcx@1234 ccdi -N -B -e \
|
||||
"SELECT COUNT(*) FROM ccdi_project WHERE project_name='$PROJECT_NAME';" 2>/dev/null)
|
||||
|
||||
if [ "$DB_CHECK" == "0" ]; then
|
||||
log_info "✓ 事务已回滚,数据库无脏数据"
|
||||
else
|
||||
log_error "✗ 事务未回滚,存在脏数据"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
else
|
||||
log_error "✗ 未正确处理异常情况"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 测试场景4:查询项目列表
|
||||
test_query_project_list() {
|
||||
log_info "=========================================="
|
||||
log_info "测试场景4:查询项目列表"
|
||||
log_info "=========================================="
|
||||
|
||||
RESPONSE=$(curl -s -X GET "$BASE_URL/ccdi/project/list?pageNum=1&pageSize=10" \
|
||||
-H "Authorization: Bearer $TOKEN")
|
||||
|
||||
log_info "响应内容(前500字符): ${RESPONSE:0:500}"
|
||||
|
||||
CODE=$(echo "$RESPONSE" | grep -o '"code":[0-9]*' | sed 's/"code"://')
|
||||
|
||||
if [ "$CODE" == "200" ]; then
|
||||
log_info "✓ 查询项目列表成功"
|
||||
|
||||
# 检查是否包含 lsfxProjectId
|
||||
if echo "$RESPONSE" | grep -q "lsfxProjectId"; then
|
||||
log_info "✓ 项目列表包含 lsfxProjectId 字段"
|
||||
else
|
||||
log_warning "! 项目列表可能缺少 lsfxProjectId 字段"
|
||||
fi
|
||||
|
||||
return 0
|
||||
else
|
||||
log_error "✗ 查询项目列表失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 主测试流程
|
||||
main() {
|
||||
log_info "=========================================="
|
||||
log_info "开始执行项目创建功能测试"
|
||||
log_info "=========================================="
|
||||
|
||||
# 检查依赖
|
||||
check_command curl
|
||||
check_command mysql
|
||||
|
||||
# 检查后端服务
|
||||
check_backend_service || exit 1
|
||||
|
||||
# 获取令牌
|
||||
get_token || exit 1
|
||||
|
||||
# 执行测试
|
||||
PASS_COUNT=0
|
||||
FAIL_COUNT=0
|
||||
|
||||
if test_create_project_success; then
|
||||
((PASS_COUNT++))
|
||||
else
|
||||
((FAIL_COUNT++))
|
||||
fi
|
||||
|
||||
if test_create_project_empty_name; then
|
||||
((PASS_COUNT++))
|
||||
else
|
||||
((FAIL_COUNT++))
|
||||
fi
|
||||
|
||||
if test_query_project_list; then
|
||||
((PASS_COUNT++))
|
||||
else
|
||||
((FAIL_COUNT++))
|
||||
fi
|
||||
|
||||
# 可选测试
|
||||
log_info "=========================================="
|
||||
log_info "可选测试:流水分析平台不可用场景"
|
||||
log_info "=========================================="
|
||||
read -p "是否执行流水分析平台不可用测试?(y/n): " run_unavailable_test
|
||||
if [ "$run_unavailable_test" == "y" ]; then
|
||||
if test_lsfx_unavailable; then
|
||||
((PASS_COUNT++))
|
||||
else
|
||||
((FAIL_COUNT++))
|
||||
fi
|
||||
fi
|
||||
|
||||
# 输出测试结果
|
||||
log_info "=========================================="
|
||||
log_info "测试结果汇总"
|
||||
log_info "=========================================="
|
||||
log_info "通过: $PASS_COUNT"
|
||||
log_info "失败: $FAIL_COUNT"
|
||||
log_info "总计: $((PASS_COUNT + FAIL_COUNT))"
|
||||
|
||||
if [ $FAIL_COUNT -eq 0 ]; then
|
||||
log_info "✓ 所有测试通过!"
|
||||
exit 0
|
||||
else
|
||||
log_error "✗ 存在失败的测试"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 执行主函数
|
||||
main
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div class="detail-query-container">
|
||||
<div class="placeholder-content">
|
||||
<i class="el-icon-document"></i>
|
||||
<p>流水明细查询功能开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "DetailQuery",
|
||||
props: {
|
||||
projectId: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
projectInfo: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
projectName: "",
|
||||
updateTime: "",
|
||||
projectStatus: "0",
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.detail-query-container {
|
||||
padding: 40px 20px;
|
||||
background: #fff;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.placeholder-content {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
|
||||
i {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div class="param-config-container">
|
||||
<div class="placeholder-content">
|
||||
<i class="el-icon-setting"></i>
|
||||
<p>参数配置功能开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ParamConfig",
|
||||
props: {
|
||||
projectId: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
projectInfo: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
projectName: "",
|
||||
updateTime: "",
|
||||
projectStatus: "0",
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.param-config-container {
|
||||
padding: 40px 20px;
|
||||
background: #fff;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.placeholder-content {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
|
||||
i {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div class="preliminary-check-container">
|
||||
<div class="placeholder-content">
|
||||
<i class="el-icon-data-analysis"></i>
|
||||
<p>结果总览功能开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "PreliminaryCheck",
|
||||
props: {
|
||||
projectId: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
projectInfo: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
projectName: "",
|
||||
updateTime: "",
|
||||
projectStatus: "0",
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.preliminary-check-container {
|
||||
padding: 40px 20px;
|
||||
background: #fff;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.placeholder-content {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
|
||||
i {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div class="special-check-container">
|
||||
<div class="placeholder-content">
|
||||
<i class="el-icon-search"></i>
|
||||
<p>专项排查功能开发中...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "SpecialCheck",
|
||||
props: {
|
||||
projectId: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
},
|
||||
projectInfo: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
projectName: "",
|
||||
updateTime: "",
|
||||
projectStatus: "0",
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.special-check-container {
|
||||
padding: 40px 20px;
|
||||
background: #fff;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.placeholder-content {
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
|
||||
i {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -25,38 +25,24 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="action-buttons">
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
icon="el-icon-upload2"
|
||||
@click="handleUploadData"
|
||||
>
|
||||
上传数据
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
icon="el-icon-setting"
|
||||
@click="handleParamConfig"
|
||||
>
|
||||
参数配置
|
||||
</el-button>
|
||||
<el-dropdown @command="handleCheckResultCommand" trigger="click">
|
||||
<el-button size="small" icon="el-icon-document">
|
||||
初核结果<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="overview">结果总览</el-dropdown-item>
|
||||
<el-dropdown-item command="special">专项排查</el-dropdown-item>
|
||||
<el-dropdown-item command="detail">流水明细查询</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<el-menu
|
||||
:default-active="activeTab"
|
||||
mode="horizontal"
|
||||
@select="handleMenuSelect"
|
||||
class="nav-menu"
|
||||
>
|
||||
<el-menu-item index="upload">上传数据</el-menu-item>
|
||||
<el-menu-item index="config">参数配置</el-menu-item>
|
||||
<el-menu-item index="overview">结果总览</el-menu-item>
|
||||
<el-menu-item index="special">专项排查</el-menu-item>
|
||||
<el-menu-item index="detail">流水明细查询</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 数据上传页面 -->
|
||||
<UploadData
|
||||
<!-- 动态组件渲染区域 -->
|
||||
<component
|
||||
:is="currentComponent"
|
||||
:project-id="projectId"
|
||||
:project-info="projectInfo"
|
||||
@menu-change="handleMenuChange"
|
||||
@@ -70,26 +56,26 @@
|
||||
|
||||
<script>
|
||||
import UploadData from "./components/detail/UploadData";
|
||||
// import UploadParams from "./components/detail/UploadParams";
|
||||
// import ParamConfig from "./components/detail/ParamConfig";
|
||||
// import PreliminaryCheck from "./components/detail/PreliminaryCheck";
|
||||
// import SpecialCheck from "./components/detail/SpecialCheck";
|
||||
// import DetailQuery from './components/detail/DetailQuery'
|
||||
import ParamConfig from "./components/detail/ParamConfig";
|
||||
import PreliminaryCheck from "./components/detail/PreliminaryCheck";
|
||||
import SpecialCheck from "./components/detail/SpecialCheck";
|
||||
import DetailQuery from "./components/detail/DetailQuery";
|
||||
|
||||
export default {
|
||||
name: "ProjectDetail",
|
||||
components: {
|
||||
UploadData,
|
||||
// UploadParams,
|
||||
// ParamConfig,
|
||||
// PreliminaryCheck,
|
||||
// SpecialCheck,
|
||||
// DetailQuery,
|
||||
ParamConfig,
|
||||
PreliminaryCheck,
|
||||
SpecialCheck,
|
||||
DetailQuery,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 当前标签页
|
||||
activeTab: "data",
|
||||
// 当前激活的菜单项索引
|
||||
activeTab: "upload",
|
||||
// 当前显示的组件名称
|
||||
currentComponent: "UploadData",
|
||||
// 项目ID
|
||||
projectId: this.$route.params.projectId,
|
||||
// 项目信息
|
||||
@@ -182,26 +168,27 @@ export default {
|
||||
handleBack() {
|
||||
this.$router.push("/ccdiProject");
|
||||
},
|
||||
/** 菜单选择事件 */
|
||||
handleMenuSelect(index) {
|
||||
console.log("菜单选择:", index);
|
||||
this.activeTab = index;
|
||||
|
||||
// 组件映射
|
||||
const componentMap = {
|
||||
upload: "UploadData",
|
||||
config: "ParamConfig",
|
||||
overview: "PreliminaryCheck",
|
||||
special: "SpecialCheck",
|
||||
detail: "DetailQuery",
|
||||
};
|
||||
|
||||
this.currentComponent = componentMap[index] || "UploadData";
|
||||
},
|
||||
/** UploadData 组件:菜单切换 */
|
||||
handleMenuChange({ key, route }) {
|
||||
console.log("切换到菜单:", key, route);
|
||||
// 根据不同的菜单项跳转到不同的组件或页面
|
||||
switch (route) {
|
||||
case "config":
|
||||
this.$message.info("参数配置功能开发中");
|
||||
break;
|
||||
case "overview":
|
||||
this.$message.info("结果总览功能开发中");
|
||||
break;
|
||||
case "special":
|
||||
this.$message.info("专项排查功能开发中");
|
||||
break;
|
||||
case "detail":
|
||||
this.$message.info("流水明细查询功能开发中");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// 直接触发菜单选择
|
||||
this.handleMenuSelect(route);
|
||||
},
|
||||
/** UploadData 组件:数据上传完成 */
|
||||
handleDataUploaded({ type }) {
|
||||
@@ -223,33 +210,6 @@ export default {
|
||||
console.log("拉取本行信息");
|
||||
this.$message.info("拉取本行信息功能开发中");
|
||||
},
|
||||
/** 上传数据 (原方法,已由UploadData组件处理) */
|
||||
handleUploadData() {
|
||||
console.log("上传数据");
|
||||
this.$message.info("上传数据功能已迁移至上传页面");
|
||||
},
|
||||
/** 参数配置 (原方法) */
|
||||
handleParamConfig() {
|
||||
console.log("参数配置");
|
||||
this.$message.info("参数配置功能开发中");
|
||||
},
|
||||
/** 初核结果下拉菜单命令 */
|
||||
handleCheckResultCommand(command) {
|
||||
console.log("初核结果命令:", command);
|
||||
switch (command) {
|
||||
case "overview":
|
||||
this.$message.info("结果总览功能开发中");
|
||||
break;
|
||||
case "special":
|
||||
this.$message.info("专项排查功能开发中");
|
||||
break;
|
||||
case "detail":
|
||||
this.$message.info("流水明细查询功能开发中");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
/** 数据上传完成 */
|
||||
handleDataUploaded() {
|
||||
console.log("数据上传完成");
|
||||
@@ -353,21 +313,51 @@ export default {
|
||||
}
|
||||
|
||||
.header-right {
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.el-button {
|
||||
padding: 8px 16px;
|
||||
.nav-menu {
|
||||
// 移除默认背景色和边框
|
||||
background-color: transparent;
|
||||
border-bottom: none;
|
||||
|
||||
// 菜单项基础样式
|
||||
.el-menu-item,
|
||||
.el-submenu__title {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
padding: 0 16px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
border-bottom: 2px solid transparent;
|
||||
|
||||
.el-icon--right {
|
||||
margin-left: 4px;
|
||||
&:hover {
|
||||
background-color: #f5f7fa;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.el-tag {
|
||||
// 子菜单容器高度统一
|
||||
.el-submenu {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
// 激活状态:底部下划线 + 蓝色文字
|
||||
.el-menu-item.is-active {
|
||||
color: #1890ff;
|
||||
border-bottom: 2px solid #1890ff;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
// 下拉菜单激活状态
|
||||
.el-submenu.is-active > .el-submenu__title {
|
||||
color: #1890ff;
|
||||
border-bottom: 2px solid #1890ff;
|
||||
}
|
||||
|
||||
// 下拉菜单图标
|
||||
.el-submenu__icon-arrow {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
@@ -471,6 +461,25 @@ export default {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
|
||||
.header-right {
|
||||
width: 100%;
|
||||
margin-top: 12px;
|
||||
|
||||
.nav-menu {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
|
||||
.el-menu-item,
|
||||
.el-submenu {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 0 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-content {
|
||||
@@ -494,4 +503,22 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 下拉菜单弹窗样式
|
||||
::v-deep .el-menu--popup {
|
||||
min-width: 140px;
|
||||
|
||||
.el-menu-item {
|
||||
font-size: 14px;
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
color: #1890ff;
|
||||
background-color: #e6f7ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user