903 lines
26 KiB
Markdown
903 lines
26 KiB
Markdown
# 创建项目功能设计文档
|
||
|
||
**文档版本:** 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`
|
||
|
||
---
|
||
|
||
**文档结束**
|