27 KiB
创建项目功能设计文档
文档版本: 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 脚本
-- 创建项目表
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/
@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/
@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/
@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 |
示例代码:
@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/
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);
}
实现类示例:
@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/
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 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/
修改内容:
- 简化表单字段:只保留项目名称、项目描述、配置方式3个字段
- 移除字段:目标人员、开始日期、结束日期、目标人数、高级设置
- 默认值:配置方式默认为
'default'
关键代码:
<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/
关键特性:
- 项目名称和描述上下排列:同一单元格内,项目名称加粗深色,项目描述常规浅色
- 预警人数悬停提示:显示高、中、低风险人数详细分布
- 预警人数样式:根据风险级别自动调整颜色
表格列配置:
| 列名 | 宽度 | 对齐方式 | 说明 |
|---|---|---|---|
| 项目名称 | 最小300px | 左对齐 | 包含项目名称(上)+项目描述(下),自适应 |
| 项目状态 | 100px | 居中对齐 | 固定宽度 |
| 目标人数 | 100px | 居中对齐 | 固定宽度 |
| 预警人数 | 120px | 居中对齐 | 悬停显示详细风险分布 |
| 创建人 | 120px | 居中对齐 | 固定宽度 |
| 创建时间 | 160px | 居中对齐 | 格式化显示 |
| 操作 | 280px | 居中对齐 | 固定在右侧 |
关键代码:
<!-- 项目名称(含描述) -->
<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>
样式代码:
.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/
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 小时)
-
创建数据库表(15 分钟)
- 执行
ccdi_project表创建脚本 - 插入字典数据和菜单数据
- 执行
-
后端开发(2 小时)
- 创建实体类
CcdiProject - 创建 DTO
CcdiProjectSaveDTO - 创建 VO
CcdiProjectVO - 创建 Mapper 接口和 XML
- 创建 Service 接口和实现类
- 创建 Controller 接口
- 添加 Swagger 注解
- 创建实体类
-
后端测试(30 分钟)
- 使用 Swagger 测试创建项目接口
- 使用 Swagger 测试查询项目列表接口
- 验证数据字典显示
阶段二:前端开发(预计 2.5 小时)
-
前端组件开发(1.5 小时)
- 修改
AddProjectDialog.vue组件 - 修改
ProjectTable.vue组件 - 更新 API 接口文件
ccdiProject.js - 修改父组件调用逻辑
- 修改
-
前端联调(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
文档结束