Files
ccdi/docs/plans/2026-02-27-project-management-improvements-design.md
wkc 2ecb66c4c9 docs: 添加项目管理页面改进设计文档
- 搜索框添加内嵌搜索按钮
- 标签页状态计数改为后端统计接口
- 状态标签改为简约小圆点样式
2026-02-27 15:25:56 +08:00

587 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 项目管理页面改进设计文档
**日期:** 2026-02-27
**作者:** Claude Code
**状态:** 待实施
---
## 一、需求概述
项目管理页面存在以下三个问题需要改进:
1. **搜索框缺少搜索按钮** - 用户只能通过回车或清空触发搜索,缺少显式的搜索按钮
2. **标签页状态计数不准确** - 当前只统计当前页的数据,无法反映全局状态分布
3. **状态标签样式不够简约** - 当前使用带背景色的标签,视觉上较重
---
## 二、技术方案
### 2.1 方案选择
经过对比分析,选择 **方案1独立统计接口 + 精准样式改造**
**理由:**
- 接口职责单一,易于维护
- 统计数据准确,不受分页影响
- 性能最优统计数据量小仅4个数字
- 符合 RESTful 设计规范
---
## 三、后端设计
### 3.1 新增统计接口
**接口定义**
```
GET /ccdi/project/statusCounts
```
**权限要求**
```
ccdi:project:list
```
**Controller 层实现**
文件:`ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectController.java`
```java
/**
* 获取项目状态统计
*/
@GetMapping("/statusCounts")
@Operation(summary = "获取项目状态统计")
@PreAuthorize("@ss.hasPermi('ccdi:project:list')")
public AjaxResult getStatusCounts() {
Map<String, Long> counts = projectService.getStatusCounts();
return AjaxResult.success(counts);
}
```
**Service 层接口**
文件:`ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectService.java`
```java
/**
* 获取各状态的项目数量
* @return 返回格式:{"all": 总数, "0": 进行中数量, "1": 已完成数量, "2": 已归档数量}
*/
Map<String, Long> getStatusCounts();
```
**Service 层实现**
文件:`ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java`
```java
@Override
public Map<String, Long> getStatusCounts() {
Map<String, Long> counts = new HashMap<>();
// 使用 MyBatis Plus 分组查询
QueryWrapper<CcdiProject> wrapper = new QueryWrapper<>();
wrapper.select("status", "COUNT(*) as count")
.groupBy("status");
List<Map<String, Object>> results = baseMapper.selectMaps(wrapper);
// 初始化各状态计数
Long totalCount = 0L;
Long inProgressCount = 0L;
Long completedCount = 0L;
Long archivedCount = 0L;
// 遍历结果统计
for (Map<String, Object> result : results) {
String status = (String) result.get("status");
Long count = (Long) result.get("count");
totalCount += count;
if ("0".equals(status)) {
inProgressCount = count;
} else if ("1".equals(status)) {
completedCount = count;
} else if ("2".equals(status)) {
archivedCount = count;
}
}
counts.put("all", totalCount);
counts.put("0", inProgressCount);
counts.put("1", completedCount);
counts.put("2", archivedCount);
return counts;
}
```
**响应示例**
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"all": 15,
"0": 8,
"1": 5,
"2": 2
}
}
```
---
## 四、前端设计
### 4.1 API 接口定义
**文件:** `ruoyi-ui/src/api/ccdiProject.js`
```javascript
// 获取项目状态统计
export function getProjectStatusCounts() {
return request({
url: '/ccdi/project/statusCounts',
method: 'get'
})
}
```
### 4.2 SearchBar 组件改造
**文件:** `ruoyi-ui/src/views/ccdiProject/components/SearchBar.vue`
**改动说明:**
- 使用 Element UI 的 slot 功能在输入框右侧添加搜索按钮
- 保持回车和清空触发搜索的功能
- 调整输入框宽度以容纳按钮
**实现代码:**
```vue
<el-input
v-model="searchKeyword"
placeholder="请输入关键词搜索项目"
clearable
size="small"
class="search-input"
@keyup.enter.native="handleSearch"
@clear="handleSearch"
>
<el-button
slot="append"
icon="el-icon-search"
@click="handleSearch"
/>
</el-input>
```
**样式调整:**
```scss
.search-input {
width: 300px; // 从 240px 调整为 300px
height: 40px;
}
```
### 4.3 ProjectTable 组件改造
**文件:** `ruoyi-ui/src/views/ccdiProject/components/ProjectTable.vue`
**改动说明:**
- 替换 `<el-tag>` + `<dict-tag>` 组合为简约的小圆点样式
- 使用不同颜色的小圆点标识不同状态
- 文字统一使用黑色,降低视觉干扰
**实现代码:**
```vue
<!-- 状态列 -->
<el-table-column
prop="status"
label="状态"
width="120"
align="center"
>
<template slot-scope="scope">
<span class="status-badge" :class="'status-' + scope.row.status">
<span class="status-dot"></span>
<span class="status-text">{{ getStatusText(scope.row.status) }}</span>
</span>
</template>
</el-table-column>
```
**新增方法:**
```javascript
getStatusText(status) {
const statusMap = {
'0': '进行中',
'1': '已完成',
'2': '已归档'
}
return statusMap[status] || '未知'
}
```
**移除方法:**
```javascript
// 删除不再使用的 getStatusType 方法
```
**样式设计:**
```scss
.status-badge {
display: inline-flex;
align-items: center;
gap: 6px;
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;
}
.status-text {
color: #333;
font-size: 14px;
}
// 进行中 - 蓝色
&.status-0 .status-dot {
background-color: #1890ff;
}
// 已完成 - 绿色
&.status-1 .status-dot {
background-color: #52c41a;
}
// 已归档 - 灰色
&.status-2 .status-dot {
background-color: #8c8c8c;
}
}
```
### 4.4 主页面集成
**文件:** `ruoyi-ui/src/views/ccdiProject/index.vue`
**改动说明:**
- 引入统计接口
- created 生命周期并发加载统计和列表数据
- 状态变更后同时刷新统计和列表
- 优雅的错误处理
**引入接口:**
```javascript
import { listProject, getProjectStatusCounts } from '@/api/ccdiProject'
```
**新增方法:**
```javascript
/** 获取状态统计 */
getStatusCounts() {
return getProjectStatusCounts().then(response => {
this.tabCounts = response.data
}).catch(() => {
// 统计接口失败时使用默认值,不影响列表显示
this.tabCounts = {
all: 0,
'0': 0,
'1': 0,
'2': 0
}
})
}
```
**修改 created 生命周期:**
```javascript
created() {
// 并发加载统计和列表
Promise.all([
this.getStatusCounts(),
this.getList()
])
}
```
**优化 calculateTabCounts 方法:**
```javascript
/** 计算标签页数量 - 改为从统计接口获取 */
calculateTabCounts() {
// 方法保留但不执行计算逻辑
// 统计数据已从接口获取,直接使用 this.tabCounts
}
```
**修改状态变更处理:**
```javascript
/** 确认归档 */
handleConfirmArchive(data) {
console.log('确认归档:', data)
this.$modal.msgSuccess('项目已归档')
this.archiveDialogVisible = false
// 同时刷新统计和列表
Promise.all([
this.getStatusCounts(),
this.getList()
])
}
/** 提交项目表单 */
handleSubmitProject(data) {
this.addDialogVisible = false
// 同时刷新统计和列表
Promise.all([
this.getStatusCounts(),
this.getList()
])
}
```
**优化 getList 方法:**
```javascript
/** 查询项目列表 */
getList() {
this.loading = true
listProject(this.queryParams).then(response => {
this.projectList = response.rows
this.total = response.total
this.loading = false
// 不再需要调用 calculateTabCounts
}).catch(() => {
this.loading = false
this.$modal.msgError('加载项目列表失败')
})
}
```
---
## 五、数据流设计
### 5.1 数据加载时机
| 场景 | 刷新统计 | 刷新列表 | 说明 |
|------|---------|---------|------|
| 页面首次加载 | ✓ | ✓ | 并发请求 |
| 标签页切换 | - | ✓ | 仅列表变化 |
| 搜索触发 | - | ✓ | 仅列表变化 |
| 项目归档 | ✓ | ✓ | 状态变化 |
| 新建项目 | ✓ | ✓ | 总数增加 |
| 删除项目 | ✓ | ✓ | 总数减少 |
| 重新分析 | - | ✓ | 状态不变 |
### 5.2 并发加载优化
```javascript
// 使用 Promise.all 并发请求,提升加载速度
Promise.all([
this.getStatusCounts(), // 统计接口
this.getList() // 列表接口
])
```
### 5.3 错误处理策略
**统计接口失败:**
- 不阻塞页面加载
- 标签页显示 0但列表正常显示
- 控制台记录错误日志
**列表接口失败:**
- 显示错误提示
- 保持现有数据不变
- Loading 状态正常关闭
---
## 六、性能考虑
### 6.1 接口性能
**统计接口:**
- 使用 COUNT + GROUP BY无需查询详细字段
- 数据量极小仅4个数字
- 执行速度快,可在 100ms 内完成
**列表接口:**
- 保持现有分页逻辑
- 不受统计接口影响
### 6.2 前端性能
**并发请求:**
- 统计和列表同时发起,不串行等待
- 总耗时 = max(统计耗时, 列表耗时)
**缓存策略:**
- 统计数据在前端内存中保留
- 仅在状态变更时重新获取
---
## 七、测试要点
### 7.1 后端测试
**单元测试:**
- 测试统计接口返回数据格式
- 测试空数据情况(无项目时)
- 测试各状态的数据准确性
**集成测试:**
- 使用 Swagger 测试接口响应
- 验证权限控制
### 7.2 前端测试
**功能测试:**
- 搜索按钮点击触发搜索
- 回车键触发搜索
- 清空输入框触发搜索
- 标签页显示正确的统计数据
- 状态标签显示正确的颜色和文字
**UI 测试:**
- 搜索按钮样式与输入框融合
- 状态标签小圆点样式正确
- 不同状态的颜色区分明显
**错误场景测试:**
- 统计接口失败时页面正常显示
- 列表接口失败时错误提示正确
- 网络异常时的降级处理
---
## 八、改动文件清单
### 8.1 后端文件
1. `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiProjectController.java`
- 新增统计接口方法
2. `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiProjectService.java`
- 新增服务方法定义
3. `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java`
- 实现统计逻辑
### 8.2 前端文件
1. `ruoyi-ui/src/api/ccdiProject.js`
- 新增统计接口调用
2. `ruoyi-ui/src/views/ccdiProject/components/SearchBar.vue`
- 添加内嵌搜索按钮
- 调整输入框宽度
3. `ruoyi-ui/src/views/ccdiProject/components/ProjectTable.vue`
- 改造状态标签样式
- 新增 getStatusText 方法
- 删除 getStatusType 方法
4. `ruoyi-ui/src/views/ccdiProject/index.vue`
- 引入统计接口
- 修改 created 生命周期
- 新增 getStatusCounts 方法
- 优化状态变更处理
---
## 九、实施计划
### 9.1 实施顺序
1. **后端开发**(优先)
- 实现统计接口
- Swagger 测试验证
2. **前端开发**
- API 接口定义
- SearchBar 组件改造
- ProjectTable 组件改造
- 主页面集成
3. **联调测试**
- 前后端联调
- 功能测试
- UI 测试
### 9.2 预估工时
- 后端开发1 小时
- 前端开发2 小时
- 联调测试1 小时
- **总计4 小时**
---
## 十、风险评估
### 10.1 技术风险
**风险:** 统计接口性能问题
**影响:**
**缓解措施:**
- 使用 COUNT + GROUP BY 优化查询
- 添加数据库索引status 字段)
- 监控接口响应时间
**风险:** 前端样式兼容性
**影响:**
**缓解措施:**
- 使用标准的 CSS 属性
- 测试主流浏览器
### 10.2 业务风险
**风险:** 统计数据与实际不符
**影响:**
**缓解措施:**
- 使用数据库事务保证一致性
- 状态变更时立即刷新统计
- 添加数据校验
---
## 十一、后续优化
### 11.1 短期优化
- 添加统计数据的本地缓存5秒过期
- 优化错误提示文案
### 11.2 长期优化
- 支持按时间范围统计
- 添加趋势图表
- 支持自定义状态
---
**设计完成日期:** 2026-02-27
**预计实施日期:** 待定