docs: 添加创建项目功能设计文档

This commit is contained in:
wkc
2026-02-26 16:31:24 +08:00
parent 2531c69d29
commit e82060a8c8
3 changed files with 902 additions and 0 deletions

View File

@@ -0,0 +1,902 @@
# 创建项目功能设计文档
**文档版本:** v1.0
**创建日期:** 2026-02-26
**设计人员:** Claude Code
---
## 1. 概述
### 1.1 功能描述
新增"创建项目"功能,允许用户在首页点击"新建项目"按钮后,通过弹窗表单创建新的纪检初核项目。
### 1.2 核心需求
- 弹窗包含3个字段项目名称、项目描述、配置方式
- 配置方式为单选按钮:全局默认模型参数配置 / 自定义项目规则参数配置
- 项目列表展示项目名称和描述(上下排列)、状态、目标人数、预警人数、创建人、创建时间
- 预警人数为各级别风险人数之和,悬停显示详细分布
---
## 2. 数据库设计
### 2.1 表结构
**表名:** `ccdi_project`
**字段列表:**
| 字段名 | 类型 | 长度 | 必填 | 默认值 | 说明 |
|--------|------|------|------|--------|------|
| project_id | BIGINT | - | 是 | 自增 | 项目ID主键 |
| project_name | VARCHAR | 100 | 是 | - | 项目名称 |
| project_desc | VARCHAR | 500 | 否 | NULL | 项目描述 |
| config_type | VARCHAR | 20 | 是 | 'default' | 配置方式default-全局默认custom-自定义 |
| project_status | CHAR | 1 | 是 | '0' | 项目状态0-进行中1-已完成2-已归档 |
| target_count | INT | - | 是 | 0 | 目标人数 |
| high_risk_count | INT | - | 是 | 0 | 高风险人数 |
| medium_risk_count | INT | - | 是 | 0 | 中风险人数 |
| low_risk_count | INT | - | 是 | 0 | 低风险人数 |
| create_by | VARCHAR | 64 | 否 | '' | 创建者 |
| create_time | DATETIME | - | 否 | CURRENT_TIMESTAMP | 创建时间 |
| update_by | VARCHAR | 64 | 否 | '' | 更新者 |
| update_time | DATETIME | - | 否 | CURRENT_TIMESTAMP | 更新时间 |
| remark | VARCHAR | 500 | 否 | NULL | 备注 |
**索引设计:**
- 主键索引:`PRIMARY KEY (project_id)`
- 项目名称索引:`INDEX idx_project_name (project_name)`
- 项目状态索引:`INDEX idx_project_status (project_status)`
- 创建时间索引:`INDEX idx_create_time (create_time)`
### 2.2 SQL 脚本
```sql
-- 创建项目表
CREATE TABLE `ccdi_project` (
`project_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '项目ID',
`project_name` VARCHAR(100) NOT NULL COMMENT '项目名称',
`project_desc` VARCHAR(500) DEFAULT NULL COMMENT '项目描述',
`config_type` VARCHAR(20) NOT NULL DEFAULT 'default' COMMENT '配置方式default-全局默认custom-自定义',
`project_status` CHAR(1) NOT NULL DEFAULT '0' COMMENT '项目状态0-进行中1-已完成2-已归档',
`target_count` INT NOT NULL DEFAULT 0 COMMENT '目标人数',
`high_risk_count` INT NOT NULL DEFAULT 0 COMMENT '高风险人数',
`medium_risk_count` INT NOT NULL DEFAULT 0 COMMENT '中风险人数',
`low_risk_count` INT NOT NULL DEFAULT 0 COMMENT '低风险人数',
`create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`remark` VARCHAR(500) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`project_id`),
INDEX `idx_project_name` (`project_name`),
INDEX `idx_project_status` (`project_status`),
INDEX `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='纪检初核项目表';
-- 插入项目状态字典
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
VALUES ('项目状态', 'ccdi_project_status', '0', 'admin', NOW(), '纪检初核项目状态');
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time)
VALUES
(1, '进行中', '0', 'ccdi_project_status', '', 'primary', 'Y', '0', 'admin', NOW()),
(2, '已完成', '1', 'ccdi_project_status', '', 'success', 'N', '0', 'admin', NOW()),
(3, '已归档', '2', 'ccdi_project_status', '', 'info', 'N', '0', 'admin', NOW());
-- 插入配置方式字典
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
VALUES ('配置方式', 'ccdi_config_type', '0', 'admin', NOW(), '项目配置方式');
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time)
VALUES
(1, '全局默认模型参数配置', 'default', 'ccdi_config_type', '', 'primary', 'Y', '0', 'admin', NOW()),
(2, '自定义项目规则参数配置', 'custom', 'ccdi_config_type', '', 'warning', 'N', '0', 'admin', NOW());
-- 插入菜单权限
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time)
VALUES ('纪检初核管理', 0, 1, 'ccdi', NULL, 'M', '0', '0', '', 'monitor', 'admin', NOW());
SET @parent_id = LAST_INSERT_ID();
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, menu_type, visible, status, perms, icon, create_by, create_time)
VALUES ('项目管理', @parent_id, 1, 'project', 'ccdiProject/index', 'C', '0', '0', 'ccdi:project:list', 'project', 'admin', NOW());
SET @menu_id = LAST_INSERT_ID();
INSERT INTO sys_menu (menu_name, parent_id, order_num, menu_type, visible, status, perms, create_by, create_time)
VALUES
('创建项目', @menu_id, 1, 'F', '0', '0', 'ccdi:project:add', 'admin', NOW()),
('编辑项目', @menu_id, 2, 'F', '0', '0', 'ccdi:project:edit', 'admin', NOW()),
('删除项目', @menu_id, 3, 'F', '0', '0', 'ccdi:project:remove', 'admin', NOW()),
('查询项目', @menu_id, 4, 'F', '0', '0', 'ccdi:project:query', 'admin', NOW());
-- 为管理员角色分配权限
INSERT INTO sys_role_menu (role_id, menu_id)
SELECT 1, menu_id FROM sys_menu WHERE perms LIKE 'ccdi:project:%' OR perms = 'ccdi:project:list';
```
---
## 3. 后端架构设计
### 3.1 实体类
**类名:** `CcdiProject`
**位置:** `ruoyi-info-collection/src/main/java/com/ruoyi/info/collection/domain/`
```java
@Data
public class CcdiProject {
/** 项目ID */
private Long projectId;
/** 项目名称 */
private String projectName;
/** 项目描述 */
private String projectDesc;
/** 配置方式default-全局默认custom-自定义 */
private String configType;
/** 项目状态0-进行中1-已完成2-已归档 */
private String projectStatus;
/** 目标人数 */
private Integer targetCount;
/** 高风险人数 */
private Integer highRiskCount;
/** 中风险人数 */
private Integer mediumRiskCount;
/** 低风险人数 */
private Integer lowRiskCount;
/** 创建者 */
private String createBy;
/** 创建时间 */
private Date createTime;
/** 更新者 */
private String updateBy;
/** 更新时间 */
private Date updateTime;
/** 备注 */
private String remark;
}
```
### 3.2 DTO 设计
**类名:** `CcdiProjectSaveDTO`
**位置:** `ruoyi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/`
```java
@Data
public class CcdiProjectSaveDTO {
/** 项目名称(必填) */
@NotBlank(message = "项目名称不能为空")
@Length(max = 100, message = "项目名称长度不能超过100个字符")
private String projectName;
/** 项目描述(可选) */
@Length(max = 500, message = "项目描述长度不能超过500个字符")
private String projectDesc;
/** 配置方式必填default-全局默认custom-自定义 */
@NotBlank(message = "配置方式不能为空")
private String configType;
}
```
### 3.3 VO 设计
**类名:** `CcdiProjectVO`
**位置:** `ruoyi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/`
```java
@Data
public class CcdiProjectVO {
/** 项目ID */
private Long projectId;
/** 项目名称 */
private String projectName;
/** 项目描述 */
private String projectDesc;
/** 配置方式 */
private String configType;
/** 项目状态 */
private String projectStatus;
/** 目标人数 */
private Integer targetCount;
/** 高风险人数 */
private Integer highRiskCount;
/** 中风险人数 */
private Integer mediumRiskCount;
/** 低风险人数 */
private Integer lowRiskCount;
/** 创建时间 */
private Date createTime;
/** 创建者 */
private String createBy;
}
```
### 3.4 Controller 接口
**类名:** `CcdiProjectController`
**位置:** `ruoyi-info-collection/src/main/java/com/ruoyi/info/collection/controller/`
**接口列表:**
| 接口路径 | 方法 | 说明 | 权限标识 |
|---------|------|------|---------|
| `/ccdi/project` | POST | 创建项目 | `ccdi:project:add` |
| `/ccdi/project` | PUT | 更新项目 | `ccdi:project:edit` |
| `/ccdi/project/{projectId}` | DELETE | 删除项目 | `ccdi:project:remove` |
| `/ccdi/project/{projectId}` | GET | 查询项目详情 | `ccdi:project:query` |
| `/ccdi/project/list` | GET | 查询项目列表(分页) | `ccdi:project:list` |
**示例代码:**
```java
@RestController
@RequestMapping("/ccdi/project")
@Api(tags = "纪检初核项目管理")
public class CcdiProjectController extends BaseController {
@Resource
private ICcdiProjectService projectService;
@PostMapping
@ApiOperation("创建项目")
@PreAuthorize("@ss.hasPermi('ccdi:project:add')")
public AjaxResult createProject(@Validated @RequestBody CcdiProjectSaveDTO dto) {
CcdiProjectVO vo = projectService.createProject(dto);
return AjaxResult.success("项目创建成功", vo);
}
@GetMapping("/list")
@ApiOperation("查询项目列表")
@PreAuthorize("@ss.hasPermi('ccdi:project:list')")
public TableDataInfo listProject(CcdiProjectQueryDTO queryDTO) {
PageDomain pageDomain = TableSupport.buildPageRequest();
Page<CcdiProjectVO> page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize());
Page<CcdiProjectVO> result = projectService.selectProjectPage(page, queryDTO);
return getDataTable(result.getRecords(), result.getTotal());
}
}
```
### 3.5 Service 层
**接口名:** `ICcdiProjectService`
**实现类名:** `CcdiProjectServiceImpl`
**位置:** `ruoyi-info-collection/src/main/java/com/ruoyi/info/collection/service/`
```java
public interface ICcdiProjectService {
/**
* 创建项目
* @param dto 项目保存DTO
* @return 项目VO
*/
CcdiProjectVO createProject(CcdiProjectSaveDTO dto);
/**
* 更新项目
* @param dto 项目更新DTO
* @return 项目VO
*/
CcdiProjectVO updateProject(CcdiProjectSaveDTO dto);
/**
* 删除项目
* @param projectId 项目ID
* @return 是否成功
*/
boolean deleteProject(Long projectId);
/**
* 查询项目详情
* @param projectId 项目ID
* @return 项目VO
*/
CcdiProjectVO getProjectById(Long projectId);
/**
* 分页查询项目列表
* @param page 分页对象
* @param queryDTO 查询条件
* @return 分页结果
*/
Page<CcdiProjectVO> selectProjectPage(Page<CcdiProjectVO> page, CcdiProjectQueryDTO queryDTO);
}
```
**实现类示例:**
```java
@Service
public class CcdiProjectServiceImpl implements ICcdiProjectService {
@Resource
private CcdiProjectMapper projectMapper;
@Override
public CcdiProjectVO createProject(CcdiProjectSaveDTO dto) {
CcdiProject project = new CcdiProject();
BeanUtils.copyProperties(dto, project);
// 设置默认值
project.setProjectStatus("0"); // 进行中
project.setTargetCount(0);
project.setHighRiskCount(0);
project.setMediumRiskCount(0);
project.setLowRiskCount(0);
projectMapper.insert(project);
CcdiProjectVO vo = new CcdiProjectVO();
BeanUtils.copyProperties(project, vo);
return vo;
}
}
```
### 3.6 Mapper 层
**接口名:** `CcdiProjectMapper`
**位置:** `ruoyi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/`
```java
public interface CcdiProjectMapper extends BaseMapper<CcdiProject> {
/**
* 分页查询项目列表
* @param page 分页对象
* @param queryDTO 查询条件
* @return 分页结果
*/
Page<CcdiProjectVO> selectProjectPage(Page<CcdiProjectVO> page, @Param("queryDTO") CcdiProjectQueryDTO queryDTO);
}
```
**XML 文件:** `CcdiProjectMapper.xml`
**位置:** `ruoyi-info-collection/src/main/resources/mapper/info/collection/`
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.info.collection.mapper.CcdiProjectMapper">
<resultMap id="ProjectVOResultMap" type="com.ruoyi.info.collection.domain.vo.CcdiProjectVO">
<id property="projectId" column="project_id"/>
<result property="projectName" column="project_name"/>
<result property="projectDesc" column="project_desc"/>
<result property="configType" column="config_type"/>
<result property="projectStatus" column="project_status"/>
<result property="targetCount" column="target_count"/>
<result property="highRiskCount" column="high_risk_count"/>
<result property="mediumRiskCount" column="medium_risk_count"/>
<result property="lowRiskCount" column="low_risk_count"/>
<result property="createTime" column="create_time"/>
<result property="createBy" column="create_by"/>
</resultMap>
<!-- 分页查询项目列表 -->
<select id="selectProjectPage" resultMap="ProjectVOResultMap">
SELECT
project_id, project_name, project_desc, config_type,
project_status, target_count, high_risk_count,
medium_risk_count, low_risk_count, create_time, create_by
FROM ccdi_project
<where>
<if test="queryDTO.projectName != null and queryDTO.projectName != ''">
AND project_name LIKE CONCAT('%', #{queryDTO.projectName}, '%')
</if>
<if test="queryDTO.projectStatus != null and queryDTO.projectStatus != ''">
AND project_status = #{queryDTO.projectStatus}
</if>
</where>
ORDER BY create_time DESC
</select>
</mapper>
```
---
## 4. 前端架构设计
### 4.1 组件修改
**组件名称:** `AddProjectDialog.vue`
**位置:** `ruoyi-ui/src/views/ccdiProject/components/`
**修改内容:**
1. **简化表单字段**只保留项目名称、项目描述、配置方式3个字段
2. **移除字段**:目标人员、开始日期、结束日期、目标人数、高级设置
3. **默认值**:配置方式默认为 `'default'`
**关键代码:**
```vue
<template>
<el-dialog
:visible.sync="dialogVisible"
:title="title"
width="600px"
:close-on-click-modal="false"
:close-on-press-escape="false"
@close="handleClose"
>
<el-form
ref="projectForm"
:model="formData"
:rules="rules"
label-width="100px"
label-position="right"
>
<!-- 项目名称 -->
<el-form-item label="项目名称" prop="projectName">
<el-input
v-model="formData.projectName"
placeholder="请输入项目名称"
maxlength="100"
show-word-limit
/>
</el-form-item>
<!-- 项目描述 -->
<el-form-item label="项目描述" prop="projectDesc">
<el-input
v-model="formData.projectDesc"
type="textarea"
:rows="4"
placeholder="请输入项目描述"
maxlength="500"
show-word-limit
/>
</el-form-item>
<!-- 配置方式 -->
<el-form-item label="配置方式" prop="configType">
<el-radio-group v-model="formData.configType">
<el-radio label="default">全局默认模型参数配置</el-radio>
<el-radio label="custom">自定义项目规则参数配置</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="handleClose"> </el-button>
<el-button type="primary" :loading="submitting" @click="handleSubmit">
创建项目
</el-button>
</div>
</el-dialog>
</template>
<script>
import { createProject } from '@/api/ccdiProject'
export default {
name: 'AddProjectDialog',
props: {
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: '新建项目'
}
},
data() {
return {
submitting: false,
formData: {
projectName: '',
projectDesc: '',
configType: 'default'
},
rules: {
projectName: [
{ required: true, message: '请输入项目名称', trigger: 'blur' },
{ min: 2, max: 100, message: '长度在 2 到 100 个字符', trigger: 'blur' }
],
configType: [
{ required: true, message: '请选择配置方式', trigger: 'change' }
]
}
}
},
computed: {
dialogVisible: {
get() {
return this.visible
},
set(val) {
if (!val) {
this.handleClose()
}
}
}
},
methods: {
handleSubmit() {
this.$refs.projectForm.validate(valid => {
if (valid) {
this.submitting = true
createProject(this.formData).then(response => {
this.$message.success('项目创建成功')
this.submitting = false
this.$emit('submit', response.data)
this.handleClose()
}).catch(() => {
this.submitting = false
})
}
})
},
handleClose() {
this.$emit('close')
this.$refs.projectForm.resetFields()
this.formData = {
projectName: '',
projectDesc: '',
configType: 'default'
}
}
}
}
</script>
<style lang="scss" scoped>
.dialog-footer {
text-align: right;
.el-button + .el-button {
margin-left: 8px;
}
}
:deep(.el-radio-group) {
display: flex;
flex-direction: column;
gap: 12px;
.el-radio {
line-height: 32px;
}
}
</style>
```
### 4.2 项目列表表格
**组件名称:** `ProjectTable.vue`
**位置:** `ruoyi-ui/src/views/ccdiProject/components/`
**关键特性:**
1. **项目名称和描述上下排列**:同一单元格内,项目名称加粗深色,项目描述常规浅色
2. **预警人数悬停提示**:显示高、中、低风险人数详细分布
3. **预警人数样式**:根据风险级别自动调整颜色
**表格列配置:**
| 列名 | 宽度 | 对齐方式 | 说明 |
|------|------|---------|------|
| 项目名称 | 最小300px | 左对齐 | 包含项目名称(上)+项目描述(下),自适应 |
| 项目状态 | 100px | 居中对齐 | 固定宽度 |
| 目标人数 | 100px | 居中对齐 | 固定宽度 |
| 预警人数 | 120px | 居中对齐 | 悬停显示详细风险分布 |
| 创建人 | 120px | 居中对齐 | 固定宽度 |
| 创建时间 | 160px | 居中对齐 | 格式化显示 |
| 操作 | 280px | 居中对齐 | 固定在右侧 |
**关键代码:**
```vue
<!-- 项目名称含描述 -->
<el-table-column label="项目名称" min-width="300" align="left">
<template slot-scope="scope">
<div class="project-info-cell">
<div class="project-name">{{ scope.row.projectName }}</div>
<div class="project-desc">{{ scope.row.projectDesc || '暂无描述' }}</div>
</div>
</template>
</el-table-column>
<!-- 预警人数带悬停详情 -->
<el-table-column label="预警人数" width="120" align="center">
<template slot-scope="scope">
<el-tooltip placement="top" effect="light">
<div slot="content">
<div style="padding: 8px;">
<div style="margin-bottom: 8px; font-weight: bold; color: #303133;">
风险人数统计
</div>
<div style="margin-bottom: 6px;">
<span style="color: #f56c6c;"> 高风险</span>
<span style="font-weight: bold;">{{ scope.row.highRiskCount }} </span>
</div>
<div style="margin-bottom: 6px;">
<span style="color: #e6a23c;"> 中风险</span>
<span style="font-weight: bold;">{{ scope.row.mediumRiskCount }} </span>
</div>
<div>
<span style="color: #909399;"> 低风险</span>
<span style="font-weight: bold;">{{ scope.row.lowRiskCount }} </span>
</div>
</div>
</div>
<div class="warning-count-wrapper">
<span :class="getWarningClass(scope.row)" style="cursor: pointer;">
{{ scope.row.highRiskCount + scope.row.mediumRiskCount + scope.row.lowRiskCount }}
</span>
</div>
</el-tooltip>
</template>
</el-table-column>
```
**样式代码:**
```scss
.project-info-cell {
padding: 8px 0;
line-height: 1.5;
.project-name {
font-size: 14px;
font-weight: 600;
color: #303133;
margin-bottom: 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.project-desc {
font-size: 12px;
color: #909399;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.text-danger {
color: #f56c6c;
}
.text-warning {
color: #e6a23c;
}
.text-info {
color: #909399;
}
.text-bold {
font-weight: bold;
}
```
**预警人数样式规则:**
- 高风险 > 0红色加粗
- 中风险 > 0橙色加粗
- 低风险 > 0灰色
- 无预警:普通黑色
### 4.3 API 接口
**文件名:** `ccdiProject.js`
**位置:** `ruoyi-ui/src/api/`
```javascript
import request from '@/utils/request'
// 创建初核项目
export function createProject(data) {
return request({
url: '/ccdi/project',
method: 'post',
data: data
})
}
// 查询初核项目列表(分页)
export function listProject(query) {
return request({
url: '/ccdi/project/list',
method: 'get',
params: query
})
}
// 查询初核项目详细
export function getProject(projectId) {
return request({
url: '/ccdi/project/' + projectId,
method: 'get'
})
}
// 修改初核项目
export function updateProject(data) {
return request({
url: '/ccdi/project',
method: 'put',
data: data
})
}
// 删除初核项目
export function delProject(projectId) {
return request({
url: '/ccdi/project/' + projectId,
method: 'delete'
})
}
```
---
## 5. 实施计划
### 5.1 实施步骤
#### 阶段一:数据库与后端开发(预计 2.5 小时)
1. **创建数据库表**15 分钟)
- 执行 `ccdi_project` 表创建脚本
- 插入字典数据和菜单数据
2. **后端开发**2 小时)
- 创建实体类 `CcdiProject`
- 创建 DTO `CcdiProjectSaveDTO`
- 创建 VO `CcdiProjectVO`
- 创建 Mapper 接口和 XML
- 创建 Service 接口和实现类
- 创建 Controller 接口
- 添加 Swagger 注解
3. **后端测试**30 分钟)
- 使用 Swagger 测试创建项目接口
- 使用 Swagger 测试查询项目列表接口
- 验证数据字典显示
#### 阶段二:前端开发(预计 2.5 小时)
4. **前端组件开发**1.5 小时)
- 修改 `AddProjectDialog.vue` 组件
- 修改 `ProjectTable.vue` 组件
- 更新 API 接口文件 `ccdiProject.js`
- 修改父组件调用逻辑
5. **前端联调**1 小时)
- 测试创建项目功能
- 测试项目列表显示
- 测试预警人数悬停提示
- 测试字典数据展示
---
## 6. 注意事项
### 6.1 数据完整性
- 创建项目时,`project_status` 默认为 `'0'`(进行中)
- 创建项目时,风险计数字段默认为 `0`
- `config_type` 默认为 `'default'`
- 项目名称和描述不能为空
### 6.2 权限控制
- 创建项目需要 `ccdi:project:add` 权限
- 编辑项目需要 `ccdi:project:edit` 权限
- 删除项目需要 `ccdi:project:remove` 权限
- 查询项目需要 `ccdi:project:list` 权限
### 6.3 前端验证
- 项目名称必填2-100字符
- 项目描述可选最多500字符
- 配置方式:必填,只能选择 `default``custom`
### 6.4 后端验证
- 使用 `@Validated` 注解进行参数校验
- 项目名称长度校验
- 配置方式枚举值校验
### 6.5 性能优化
- 项目列表分页查询
- 项目名称和状态字段添加索引
- 字典数据使用缓存
### 6.6 用户体验
- 提交按钮显示 loading 状态
- 创建成功后自动刷新列表
- 预警人数悬停提示详细信息
- 项目名称和描述上下排列,层次分明
---
## 7. 测试清单
### 7.1 后端测试
- [ ] 创建项目成功(必填字段)
- [ ] 创建项目失败(缺少必填字段)
- [ ] 创建项目失败(字段长度超限)
- [ ] 查询项目列表(分页)
- [ ] 查询项目详情
- [ ] 更新项目
- [ ] 删除项目
- [ ] 字典数据正确返回
- [ ] 权限验证正确
### 7.2 前端测试
- [ ] 弹窗显示正确3个字段
- [ ] 表单验证正常(必填项)
- [ ] 表单验证正常(长度限制)
- [ ] 项目名称和描述上下排列
- [ ] 项目名称样式正确(加粗深色)
- [ ] 项目描述样式正确(常规浅色)
- [ ] 项目状态标签正确显示
- [ ] 预警人数计算正确(高+中+低)
- [ ] 预警人数悬停提示显示
- [ ] 预警人数颜色根据风险级别变化
- [ ] 创建人正确显示
- [ ] 创建时间格式化正确
- [ ] 操作按钮权限控制
- [ ] 提交按钮 loading 状态
- [ ] 创建成功后列表刷新
---
## 8. 附录
### 8.1 参考文档
- 若依框架官方文档
- Element UI 组件库文档
- MyBatis Plus 官方文档
### 8.2 相关文件
- 数据库脚本:`sql/ccdi_project.sql`
- 设计截图:`doc/创建项目功能/ScreenShot_2026-02-26_153149_900.png`
- 设计截图:`doc/创建项目功能/ScreenShot_2026-02-26_162233_965.png`
---
**文档结束**

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File