save code
This commit is contained in:
@@ -0,0 +1,748 @@
|
|||||||
|
# 项目信息管理系统 - 设计文档
|
||||||
|
|
||||||
|
## 1. 项目概述
|
||||||
|
|
||||||
|
### 1.1 项目背景
|
||||||
|
开发一个基于BS架构的项目信息管理系统,用于管理项目的合同、编号、名称、预算、付款等信息。系统支持多部门协作,市场部用户可以新建项目,其他部门用户可以填写和更新项目信息。
|
||||||
|
|
||||||
|
### 1.2 技术选型
|
||||||
|
|
||||||
|
| 层级 | 技术 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 前端 | React + React Router | 前后端分离架构 |
|
||||||
|
| UI组件库 | Ant Design | 企业级UI组件 |
|
||||||
|
| HTTP客户端 | Axios | API请求 |
|
||||||
|
| 状态管理 | Context API | 简单状态管理 |
|
||||||
|
| 后端 | Python Flask | 轻量级Web框架 |
|
||||||
|
| ORM | SQLAlchemy | 数据库ORM |
|
||||||
|
| 数据库 | MySQL | 关系型数据库 |
|
||||||
|
| 认证 | JWT Token | 无状态认证 |
|
||||||
|
| 开发工具 | Create React App, pipenv | 前后端开发环境 |
|
||||||
|
|
||||||
|
### 1.3 系统特点
|
||||||
|
- 轻量级架构,适合小并发场景
|
||||||
|
- 无需Redis缓存,降低部署复杂度
|
||||||
|
- 基于角色的权限控制(RBAC)
|
||||||
|
- 前后端分离,易于维护和扩展
|
||||||
|
|
||||||
|
## 2. 功能需求
|
||||||
|
|
||||||
|
### 2.1 用户管理
|
||||||
|
|
||||||
|
#### 2.1.1 登录功能
|
||||||
|
- 用户名/密码登录
|
||||||
|
- JWT Token认证
|
||||||
|
- 自动登录(Token存储在LocalStorage)
|
||||||
|
|
||||||
|
#### 2.1.2 用户管理(管理员)
|
||||||
|
- 创建用户:设置用户名、密码、部门、角色
|
||||||
|
- 编辑用户信息
|
||||||
|
- 删除用户
|
||||||
|
- 重置用户密码
|
||||||
|
- 查看用户列表
|
||||||
|
|
||||||
|
#### 2.1.3 权限控制
|
||||||
|
- **管理员**:所有权限
|
||||||
|
- **市场部用户**:创建项目、查看项目、编辑自己的项目
|
||||||
|
- **其他部门用户**:查看项目、编辑项目信息(如付款、状态等)
|
||||||
|
|
||||||
|
### 2.2 项目管理
|
||||||
|
|
||||||
|
#### 2.2.1 市场部权限
|
||||||
|
- **创建项目**:填写项目基本信息
|
||||||
|
- **查看项目**:浏览所有项目列表和详情
|
||||||
|
- **编辑项目**:修改自己创建的项目
|
||||||
|
|
||||||
|
#### 2.2.2 其他部门权限
|
||||||
|
- **查看项目**:浏览所有项目列表和详情
|
||||||
|
- **更新项目**:填写和更新项目信息(预算、付款、状态等)
|
||||||
|
|
||||||
|
#### 2.2.3 项目信息字段
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| project_no | String | 是 | 项目编号(唯一) |
|
||||||
|
| contract_no | String | 是 | 合同编号 |
|
||||||
|
| name | String | 是 | 项目名称 |
|
||||||
|
| budget | Decimal | 是 | 项目预算 |
|
||||||
|
| payment_amount | Decimal | 否 | 已付款金额 |
|
||||||
|
| status | String | 是 | 项目状态(灵活状态) |
|
||||||
|
| start_date | Date | 否 | 开始日期 |
|
||||||
|
| end_date | Date | 否 | 结束日期 |
|
||||||
|
| created_by | Integer | 是 | 创建人ID(外键) |
|
||||||
|
| department | String | 是 | 所属部门 |
|
||||||
|
| description | Text | 否 | 项目描述 |
|
||||||
|
| created_at | DateTime | 是 | 创建时间 |
|
||||||
|
| updated_at | DateTime | 是 | 更新时间 |
|
||||||
|
|
||||||
|
#### 2.2.4 项目状态示例
|
||||||
|
- 新建
|
||||||
|
- 进行中
|
||||||
|
- 已完成
|
||||||
|
- 已暂停
|
||||||
|
- 已取消
|
||||||
|
|
||||||
|
(支持灵活状态,可由用户自定义)
|
||||||
|
|
||||||
|
## 3. 系统架构
|
||||||
|
|
||||||
|
### 3.1 架构图
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────┐
|
||||||
|
│ 浏览器 │
|
||||||
|
│ (React + Ant Design + React Router + Axios) │
|
||||||
|
└─────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
│ HTTP/HTTPS
|
||||||
|
│ JWT Token
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────┐
|
||||||
|
│ Flask 后端服务 │
|
||||||
|
│ ┌─────────────────────────────────────────┐ │
|
||||||
|
│ │ API Layer (Flask Routes) │ │
|
||||||
|
│ ├─────────────────────────────────────────┤ │
|
||||||
|
│ │ Business Logic (Services) │ │
|
||||||
|
│ ├─────────────────────────────────────────┤ │
|
||||||
|
│ │ Data Access (SQLAlchemy ORM) │ │
|
||||||
|
│ └─────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
│ SQL
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────┐
|
||||||
|
│ MySQL 数据库 │
|
||||||
|
│ ┌─────────────────────────────────────────┐ │
|
||||||
|
│ │ users │ │
|
||||||
|
│ │ projects │ │
|
||||||
|
│ └─────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 项目目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
ocean_project_manager/
|
||||||
|
├── backend/ # 后端代码
|
||||||
|
│ ├── app/
|
||||||
|
│ │ ├── __init__.py # Flask应用初始化
|
||||||
|
│ │ ├── config.py # 配置文件
|
||||||
|
│ │ ├── models/ # 数据库模型
|
||||||
|
│ │ │ ├── __init__.py
|
||||||
|
│ │ │ ├── user.py
|
||||||
|
│ │ │ └── project.py
|
||||||
|
│ │ ├── routes/ # API路由
|
||||||
|
│ │ │ ├── __init__.py
|
||||||
|
│ │ │ ├── auth.py # 认证相关
|
||||||
|
│ │ │ ├── users.py # 用户管理
|
||||||
|
│ │ │ └── projects.py # 项目管理
|
||||||
|
│ │ ├── services/ # 业务逻辑
|
||||||
|
│ │ │ ├── __init__.py
|
||||||
|
│ │ │ ├── auth_service.py
|
||||||
|
│ │ │ ├── user_service.py
|
||||||
|
│ │ │ └── project_service.py
|
||||||
|
│ │ └── utils/ # 工具函数
|
||||||
|
│ │ ├── __init__.py
|
||||||
|
│ │ ├── jwt_utils.py
|
||||||
|
│ │ └── decorators.py
|
||||||
|
│ ├── requirements.txt # Python依赖
|
||||||
|
│ ├── run.py # 启动文件
|
||||||
|
│ └── .env # 环境变量
|
||||||
|
│
|
||||||
|
├── frontend/ # 前端代码
|
||||||
|
│ ├── public/
|
||||||
|
│ │ └── index.html
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── components/ # 公共组件
|
||||||
|
│ │ │ ├── Layout.js
|
||||||
|
│ │ │ ├── Header.js
|
||||||
|
│ │ │ └── Sidebar.js
|
||||||
|
│ │ ├── pages/ # 页面组件
|
||||||
|
│ │ │ ├── Login.js
|
||||||
|
│ │ │ ├── Dashboard.js
|
||||||
|
│ │ │ ├── UserList.js
|
||||||
|
│ │ │ ├── UserForm.js
|
||||||
|
│ │ │ ├── ProjectList.js
|
||||||
|
│ │ │ ├── ProjectForm.js
|
||||||
|
│ │ │ └── ProjectDetail.js
|
||||||
|
│ │ ├── services/ # API服务
|
||||||
|
│ │ │ ├── api.js # Axios配置
|
||||||
|
│ │ │ ├── auth.js
|
||||||
|
│ │ │ ├── user.js
|
||||||
|
│ │ │ └── project.js
|
||||||
|
│ │ ├── contexts/ # Context
|
||||||
|
│ │ │ └── AuthContext.js
|
||||||
|
│ │ ├── utils/ # 工具函数
|
||||||
|
│ │ │ └── auth.js
|
||||||
|
│ │ ├── App.js # 根组件
|
||||||
|
│ │ ├── index.js # 入口文件
|
||||||
|
│ │ └── App.css
|
||||||
|
│ ├── package.json
|
||||||
|
│ └── .env # 环境变量
|
||||||
|
│
|
||||||
|
├── docs/ # 文档
|
||||||
|
│ ├── plans/ # 设计文档
|
||||||
|
│ │ └── 2026-01-24-project-management-system-design.md
|
||||||
|
│ └── api/ # API文档
|
||||||
|
│
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 数据库设计
|
||||||
|
|
||||||
|
### 4.1 数据库表结构
|
||||||
|
|
||||||
|
#### 4.1.1 用户表 (users)
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 约束 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | 用户ID |
|
||||||
|
| username | VARCHAR(50) | UNIQUE, NOT NULL | 用户名 |
|
||||||
|
| password_hash | VARCHAR(255) | NOT NULL | 密码哈希 |
|
||||||
|
| real_name | VARCHAR(100) | NOT NULL | 真实姓名 |
|
||||||
|
| department | VARCHAR(50) | NOT NULL | 部门(市场部/技术部/财务部等) |
|
||||||
|
| role | ENUM | NOT NULL | 角色(admin/market/other) |
|
||||||
|
| email | VARCHAR(100) | UNIQUE | 邮箱 |
|
||||||
|
| phone | VARCHAR(20) | | 电话 |
|
||||||
|
| is_active | BOOLEAN | DEFAULT TRUE | 是否激活 |
|
||||||
|
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
|
||||||
|
| updated_at | DATETIME | DEFAULT CURRENT_TIMESTAMP ON UPDATE | 更新时间 |
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE users (
|
||||||
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
username VARCHAR(50) UNIQUE NOT NULL,
|
||||||
|
password_hash VARCHAR(255) NOT NULL,
|
||||||
|
real_name VARCHAR(100) NOT NULL,
|
||||||
|
department VARCHAR(50) NOT NULL,
|
||||||
|
role ENUM('admin', 'market', 'other') NOT NULL,
|
||||||
|
email VARCHAR(100) UNIQUE,
|
||||||
|
phone VARCHAR(20),
|
||||||
|
is_active BOOLEAN DEFAULT TRUE,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 索引
|
||||||
|
CREATE INDEX idx_users_username ON users(username);
|
||||||
|
CREATE INDEX idx_users_department ON users(department);
|
||||||
|
CREATE INDEX idx_users_role ON users(role);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4.1.2 项目表 (projects)
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 约束 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | 项目ID |
|
||||||
|
| project_no | VARCHAR(50) | UNIQUE, NOT NULL | 项目编号 |
|
||||||
|
| contract_no | VARCHAR(50) | NOT NULL | 合同编号 |
|
||||||
|
| name | VARCHAR(200) | NOT NULL | 项目名称 |
|
||||||
|
| budget | DECIMAL(15,2) | NOT NULL | 项目预算 |
|
||||||
|
| payment_amount | DECIMAL(15,2) | DEFAULT 0 | 已付款金额 |
|
||||||
|
| status | VARCHAR(50) | NOT NULL | 项目状态 |
|
||||||
|
| start_date | DATE | | 开始日期 |
|
||||||
|
| end_date | DATE | | 结束日期 |
|
||||||
|
| created_by | INT | NOT NULL, FOREIGN KEY | 创建人ID |
|
||||||
|
| department | VARCHAR(50) | NOT NULL | 所属部门 |
|
||||||
|
| description | TEXT | | 项目描述 |
|
||||||
|
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
|
||||||
|
| updated_at | DATETIME | DEFAULT CURRENT_TIMESTAMP ON UPDATE | 更新时间 |
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE projects (
|
||||||
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
project_no VARCHAR(50) UNIQUE NOT NULL,
|
||||||
|
contract_no VARCHAR(50) NOT NULL,
|
||||||
|
name VARCHAR(200) NOT NULL,
|
||||||
|
budget DECIMAL(15,2) NOT NULL,
|
||||||
|
payment_amount DECIMAL(15,2) DEFAULT 0,
|
||||||
|
status VARCHAR(50) NOT NULL,
|
||||||
|
start_date DATE,
|
||||||
|
end_date DATE,
|
||||||
|
created_by INT NOT NULL,
|
||||||
|
department VARCHAR(50) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE RESTRICT
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 索引
|
||||||
|
CREATE INDEX idx_projects_project_no ON projects(project_no);
|
||||||
|
CREATE INDEX idx_projects_contract_no ON projects(contract_no);
|
||||||
|
CREATE INDEX idx_projects_status ON projects(status);
|
||||||
|
CREATE INDEX idx_projects_department ON projects(department);
|
||||||
|
CREATE INDEX idx_projects_created_by ON projects(created_by);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 角色权限矩阵
|
||||||
|
|
||||||
|
| 功能 | admin | market | other |
|
||||||
|
|------|-------|--------|-------|
|
||||||
|
| 登录 | ✓ | ✓ | ✓ |
|
||||||
|
| 创建用户 | ✓ | ✗ | ✗ |
|
||||||
|
| 编辑用户 | ✓ | ✗ | ✗ |
|
||||||
|
| 删除用户 | ✓ | ✗ | ✗ |
|
||||||
|
| 查看用户列表 | ✓ | ✗ | ✗ |
|
||||||
|
| 创建项目 | ✓ | ✓ | ✗ |
|
||||||
|
| 编辑项目信息 | ✓ | ✓ | ✓ |
|
||||||
|
| 删除项目 | ✓ | ✓ | ✗ |
|
||||||
|
| 查看所有项目 | ✓ | ✓ | ✓ |
|
||||||
|
| 查看项目详情 | ✓ | ✓ | ✓ |
|
||||||
|
|
||||||
|
## 5. API设计
|
||||||
|
|
||||||
|
### 5.1 API规范
|
||||||
|
|
||||||
|
- **Base URL**: `/api/v1`
|
||||||
|
- **Content-Type**: `application/json`
|
||||||
|
- **认证方式**: JWT Token(在Header中传递:`Authorization: Bearer <token>`)
|
||||||
|
- **响应格式**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "操作成功",
|
||||||
|
"data": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 认证相关API
|
||||||
|
|
||||||
|
#### 5.2.1 用户登录
|
||||||
|
- **URL**: `POST /api/v1/auth/login`
|
||||||
|
- **请求体**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"username": "admin",
|
||||||
|
"password": "password123"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "登录成功",
|
||||||
|
"data": {
|
||||||
|
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||||
|
"user": {
|
||||||
|
"id": 1,
|
||||||
|
"username": "admin",
|
||||||
|
"real_name": "管理员",
|
||||||
|
"department": "管理部",
|
||||||
|
"role": "admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.2.2 获取当前用户信息
|
||||||
|
- **URL**: `GET /api/v1/auth/me`
|
||||||
|
- **Header**: `Authorization: Bearer <token>`
|
||||||
|
- **响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"id": 1,
|
||||||
|
"username": "admin",
|
||||||
|
"real_name": "管理员",
|
||||||
|
"department": "管理部",
|
||||||
|
"role": "admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.2.3 登出
|
||||||
|
- **URL**: `POST /api/v1/auth/logout`
|
||||||
|
- **Header**: `Authorization: Bearer <token>`
|
||||||
|
- **响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "登出成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.3 用户管理API
|
||||||
|
|
||||||
|
#### 5.3.1 获取用户列表
|
||||||
|
- **URL**: `GET /api/v1/users`
|
||||||
|
- **Header**: `Authorization: Bearer <token>`
|
||||||
|
- **权限**: admin
|
||||||
|
- **查询参数**:
|
||||||
|
- `page`: 页码(默认1)
|
||||||
|
- `page_size`: 每页数量(默认10)
|
||||||
|
- `department`: 部门筛选
|
||||||
|
- `role`: 角色筛选
|
||||||
|
- **响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"username": "admin",
|
||||||
|
"real_name": "管理员",
|
||||||
|
"department": "管理部",
|
||||||
|
"role": "admin",
|
||||||
|
"email": "admin@example.com",
|
||||||
|
"is_active": true,
|
||||||
|
"created_at": "2026-01-24T10:00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 1,
|
||||||
|
"page": 1,
|
||||||
|
"page_size": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.3.2 创建用户
|
||||||
|
- **URL**: `POST /api/v1/users`
|
||||||
|
- **Header**: `Authorization: Bearer <token>`
|
||||||
|
- **权限**: admin
|
||||||
|
- **请求体**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"username": "zhangsan",
|
||||||
|
"password": "password123",
|
||||||
|
"real_name": "张三",
|
||||||
|
"department": "市场部",
|
||||||
|
"role": "market",
|
||||||
|
"email": "zhangsan@example.com",
|
||||||
|
"phone": "13800138000"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "用户创建成功",
|
||||||
|
"data": {
|
||||||
|
"id": 2,
|
||||||
|
"username": "zhangsan",
|
||||||
|
"real_name": "张三",
|
||||||
|
"department": "市场部",
|
||||||
|
"role": "market"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.3.3 更新用户
|
||||||
|
- **URL**: `PUT /api/v1/users/{id}`
|
||||||
|
- **Header**: `Authorization: Bearer <token>`
|
||||||
|
- **权限**: admin
|
||||||
|
- **请求体**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"real_name": "张三",
|
||||||
|
"email": "zhangsan2@example.com",
|
||||||
|
"phone": "13900139000"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.3.4 删除用户
|
||||||
|
- **URL**: `DELETE /api/v1/users/{id}`
|
||||||
|
- **Header**: `Authorization: Bearer <token>`
|
||||||
|
- **权限**: admin
|
||||||
|
|
||||||
|
#### 5.3.5 重置用户密码
|
||||||
|
- **URL**: `POST /api/v1/users/{id}/reset-password`
|
||||||
|
- **Header**: `Authorization: Bearer <token>`
|
||||||
|
- **权限**: admin
|
||||||
|
- **请求体**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"new_password": "newpassword123"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.4 项目管理API
|
||||||
|
|
||||||
|
#### 5.4.1 获取项目列表
|
||||||
|
- **URL**: `GET /api/v1/projects`
|
||||||
|
- **Header**: `Authorization: Bearer <token>`
|
||||||
|
- **权限**: 所有用户
|
||||||
|
- **查询参数**:
|
||||||
|
- `page`: 页码(默认1)
|
||||||
|
- `page_size`: 每页数量(默认10)
|
||||||
|
- `status`: 状态筛选
|
||||||
|
- `department`: 部门筛选
|
||||||
|
- **响应**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"project_no": "PRJ2026001",
|
||||||
|
"contract_no": "CT2026001",
|
||||||
|
"name": "某公司官网开发",
|
||||||
|
"budget": 50000.00,
|
||||||
|
"payment_amount": 25000.00,
|
||||||
|
"status": "进行中",
|
||||||
|
"start_date": "2026-01-01",
|
||||||
|
"end_date": "2026-03-31",
|
||||||
|
"department": "市场部",
|
||||||
|
"created_by": {
|
||||||
|
"id": 2,
|
||||||
|
"real_name": "张三"
|
||||||
|
},
|
||||||
|
"created_at": "2026-01-24T10:00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 1,
|
||||||
|
"page": 1,
|
||||||
|
"page_size": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.4.2 获取项目详情
|
||||||
|
- **URL**: `GET /api/v1/projects/{id}`
|
||||||
|
- **Header**: `Authorization: Bearer <token>`
|
||||||
|
- **权限**: 所有用户
|
||||||
|
|
||||||
|
#### 5.4.3 创建项目
|
||||||
|
- **URL**: `POST /api/v1/projects`
|
||||||
|
- **Header**: `Authorization: Bearer <token>`
|
||||||
|
- **权限**: admin, market
|
||||||
|
- **请求体**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"project_no": "PRJ2026002",
|
||||||
|
"contract_no": "CT2026002",
|
||||||
|
"name": "电商平台开发",
|
||||||
|
"budget": 100000.00,
|
||||||
|
"status": "新建",
|
||||||
|
"start_date": "2026-02-01",
|
||||||
|
"end_date": "2026-06-30",
|
||||||
|
"department": "市场部",
|
||||||
|
"description": "电商平台开发项目"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.4.4 更新项目
|
||||||
|
- **URL**: `PUT /api/v1/projects/{id}`
|
||||||
|
- **Header**: `Authorization: Bearer <token>`
|
||||||
|
- **权限**: 所有用户
|
||||||
|
- **请求体**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"payment_amount": 50000.00,
|
||||||
|
"status": "进行中",
|
||||||
|
"description": "项目更新描述"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.4.5 删除项目
|
||||||
|
- **URL**: `DELETE /api/v1/projects/{id}`
|
||||||
|
- **Header**: `Authorization: Bearer <token>`
|
||||||
|
- **权限**: admin, market(只能删除自己创建的)
|
||||||
|
|
||||||
|
## 6. 前端设计
|
||||||
|
|
||||||
|
### 6.1 页面结构
|
||||||
|
|
||||||
|
```
|
||||||
|
登录页 (Login)
|
||||||
|
└─ 登录表单
|
||||||
|
|
||||||
|
主布局 (Layout)
|
||||||
|
├─ 顶部导航栏 (Header)
|
||||||
|
│ ├─ Logo
|
||||||
|
│ ├─ 用户信息
|
||||||
|
│ └─ 登出按钮
|
||||||
|
│
|
||||||
|
└─ 侧边栏 (Sidebar)
|
||||||
|
└─ 菜单
|
||||||
|
├─ 仪表盘 (Dashboard)
|
||||||
|
├─ 用户管理 (UserList) - 仅管理员
|
||||||
|
└─ 项目管理 (ProjectList)
|
||||||
|
├─ 项目列表
|
||||||
|
├─ 创建项目
|
||||||
|
└─ 项目详情
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 核心组件
|
||||||
|
|
||||||
|
#### 6.2.1 AuthContext
|
||||||
|
全局认证状态管理:
|
||||||
|
```javascript
|
||||||
|
const AuthContext = createContext({
|
||||||
|
user: null,
|
||||||
|
token: null,
|
||||||
|
login: () => {},
|
||||||
|
logout: () => {},
|
||||||
|
isAuthenticated: false
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6.2.2 ProtectedRoute
|
||||||
|
路由守卫组件,保护需要登录的页面。
|
||||||
|
|
||||||
|
#### 6.2.3 API Service
|
||||||
|
统一的API调用封装:
|
||||||
|
- 统一的错误处理
|
||||||
|
- 自动添加Token
|
||||||
|
- 请求拦截器
|
||||||
|
- 响应拦截器
|
||||||
|
|
||||||
|
### 6.3 页面设计
|
||||||
|
|
||||||
|
#### 6.3.1 登录页
|
||||||
|
- 简洁的登录表单
|
||||||
|
- 用户名/密码输入
|
||||||
|
- 记住我选项
|
||||||
|
- 错误提示
|
||||||
|
|
||||||
|
#### 6.3.2 仪表盘
|
||||||
|
- 项目统计卡片(总项目数、进行中、已完成等)
|
||||||
|
- 最近项目列表
|
||||||
|
- 快速操作入口
|
||||||
|
|
||||||
|
#### 6.3.3 用户管理页
|
||||||
|
- 用户列表表格
|
||||||
|
- 搜索和筛选功能
|
||||||
|
- 创建/编辑/删除/重置密码操作
|
||||||
|
- 分页功能
|
||||||
|
|
||||||
|
#### 6.3.4 项目列表页
|
||||||
|
- 项目列表表格
|
||||||
|
- 搜索和筛选(状态、部门)
|
||||||
|
- 创建项目按钮
|
||||||
|
- 分页功能
|
||||||
|
|
||||||
|
#### 6.3.5 项目表单
|
||||||
|
- 项目信息表单
|
||||||
|
- 表单验证
|
||||||
|
- 保存/取消按钮
|
||||||
|
|
||||||
|
#### 6.3.6 项目详情页
|
||||||
|
- 项目详细信息展示
|
||||||
|
- 编辑功能
|
||||||
|
- 操作日志(可选)
|
||||||
|
|
||||||
|
### 6.4 UI风格
|
||||||
|
- 使用Ant Design默认主题
|
||||||
|
- 响应式布局
|
||||||
|
- 简洁、专业的企业级UI
|
||||||
|
|
||||||
|
## 7. 安全设计
|
||||||
|
|
||||||
|
### 7.1 认证安全
|
||||||
|
- 密码使用bcrypt加密存储
|
||||||
|
- JWT Token有效期设置(如24小时)
|
||||||
|
- Token过期自动刷新机制
|
||||||
|
|
||||||
|
### 7.2 权限控制
|
||||||
|
- 后端基于装饰器的权限验证
|
||||||
|
- 前端路由级别的权限控制
|
||||||
|
- 前端组件级别的权限控制
|
||||||
|
|
||||||
|
### 7.3 数据安全
|
||||||
|
- SQL注入防护(使用SQLAlchemy ORM)
|
||||||
|
- XSS防护(React自动转义)
|
||||||
|
- 输入验证(前后端双重验证)
|
||||||
|
|
||||||
|
## 8. 部署方案
|
||||||
|
|
||||||
|
### 8.1 开发环境
|
||||||
|
|
||||||
|
**后端**:
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
pipenv install
|
||||||
|
pipenv shell
|
||||||
|
python run.py
|
||||||
|
# 服务运行在 http://localhost:5000
|
||||||
|
```
|
||||||
|
|
||||||
|
**前端**:
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
npm install
|
||||||
|
npm start
|
||||||
|
# 服务运行在 http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
**数据库**:
|
||||||
|
- 本地MySQL数据库
|
||||||
|
- 数据库名:`project_manager`
|
||||||
|
- 配置文件:`.env`
|
||||||
|
|
||||||
|
### 8.2 生产环境(推荐方案)
|
||||||
|
|
||||||
|
**使用Docker Compose**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: rootpassword
|
||||||
|
MYSQL_DATABASE: project_manager
|
||||||
|
volumes:
|
||||||
|
- mysql_data:/var/lib/mysql
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
|
||||||
|
backend:
|
||||||
|
build: ./backend
|
||||||
|
ports:
|
||||||
|
- "5000:5000"
|
||||||
|
depends_on:
|
||||||
|
- mysql
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: mysql+pymysql://root:rootpassword@mysql/project_manager
|
||||||
|
SECRET_KEY: your-secret-key
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build: ./frontend
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mysql_data:
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. 后续优化建议
|
||||||
|
|
||||||
|
### 9.1 功能扩展
|
||||||
|
- 项目附件上传
|
||||||
|
- 操作日志记录
|
||||||
|
- 数据导出功能
|
||||||
|
- 报表统计功能
|
||||||
|
|
||||||
|
### 9.2 性能优化
|
||||||
|
- 数据库索引优化
|
||||||
|
- API响应缓存(如需要)
|
||||||
|
- 前端代码分割
|
||||||
|
|
||||||
|
### 9.3 用户体验
|
||||||
|
- 表单自动保存
|
||||||
|
- 批量操作功能
|
||||||
|
- 消息通知功能
|
||||||
|
|
||||||
|
## 10. 开发计划
|
||||||
|
|
||||||
|
### 10.1 第一阶段(MVP)
|
||||||
|
- 用户登录/登出
|
||||||
|
- 项目CRUD基础功能
|
||||||
|
- 简单的用户管理(仅管理员)
|
||||||
|
|
||||||
|
### 10.2 第二阶段
|
||||||
|
- 权限控制完善
|
||||||
|
- 项目列表筛选和搜索
|
||||||
|
- 表单验证优化
|
||||||
|
|
||||||
|
### 10.3 第三阶段(可选)
|
||||||
|
- 高级功能扩展
|
||||||
|
- 性能优化
|
||||||
|
- 用户体验优化
|
||||||
@@ -0,0 +1,758 @@
|
|||||||
|
# UI设计规范文档
|
||||||
|
|
||||||
|
## 1. 设计系统概述
|
||||||
|
|
||||||
|
本系统采用Ant Design风格,简洁专业,适合企业级应用。
|
||||||
|
|
||||||
|
## 2. 颜色系统
|
||||||
|
|
||||||
|
### 2.1 主色调
|
||||||
|
```css
|
||||||
|
/* 主品牌色 - 蓝色 */
|
||||||
|
--primary-color: #1890ff;
|
||||||
|
|
||||||
|
/* 功能色 */
|
||||||
|
--success-color: #52c41a; /* 成功/进行中 */
|
||||||
|
--warning-color: #fa8c16; /* 警告/暂停 */
|
||||||
|
--error-color: #ff4d4f; /* 错误/删除 */
|
||||||
|
--info-color: #1890ff; /* 信息/新建 */
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 中性色
|
||||||
|
```css
|
||||||
|
--text-primary: #333333; /* 主要文字 */
|
||||||
|
--text-secondary: #666666; /* 次要文字 */
|
||||||
|
--text-disabled: #999999; /* 禁用文字 */
|
||||||
|
--border-color: #e8e8e8; /* 边框颜色 */
|
||||||
|
--divider-color: #f0f0f0; /* 分割线颜色 */
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 背景色
|
||||||
|
```css
|
||||||
|
--layout-header-bg: #001529; /* 顶部导航背景 */
|
||||||
|
--layout-sidebar-bg: #ffffff; /* 侧边栏背景 */
|
||||||
|
--layout-content-bg: #f0f2f5; /* 内容区背景 */
|
||||||
|
--component-bg: #ffffff; /* 组件背景 */
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 状态标签颜色
|
||||||
|
|
||||||
|
| 状态 | 背景色 | 文字颜色 | 适用场景 |
|
||||||
|
|------|--------|----------|----------|
|
||||||
|
| 新建 | #e6f7ff | #1890ff | 项目初始状态 |
|
||||||
|
| 进行中 | #f6ffed | #52c41a | 项目执行中 |
|
||||||
|
| 已完成 | #f5f5f5 | #999999 | 项目完成 |
|
||||||
|
| 已暂停 | #fff7e6 | #fa8c16 | 项目暂停 |
|
||||||
|
| 已取消 | #fff1f0 | #f5222d | 项目取消 |
|
||||||
|
| 正常 | #f6ffed | #52c41a | 用户状态正常 |
|
||||||
|
| 禁用 | #f5f5f5 | #999999 | 用户状态禁用 |
|
||||||
|
| 管理员 | #fff1f0 | #f5222d | 管理员角色 |
|
||||||
|
| 市场部 | #e6f7ff | #1890ff | 市场部角色 |
|
||||||
|
| 其他部门 | #e6f7ff | #1890ff | 其他部门角色 |
|
||||||
|
|
||||||
|
## 4. 字体系统
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* 字体家族 */
|
||||||
|
--font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||||
|
|
||||||
|
/* 字体大小 */
|
||||||
|
--font-size-base: 14px;
|
||||||
|
--font-size-lg: 16px;
|
||||||
|
--font-size-sm: 12px;
|
||||||
|
--font-size-title: 18px;
|
||||||
|
--font-size-heading: 24px;
|
||||||
|
|
||||||
|
/* 字重 */
|
||||||
|
--font-weight-normal: 400;
|
||||||
|
--font-weight-medium: 500;
|
||||||
|
--font-weight-semibold: 600;
|
||||||
|
--font-weight-bold: 700;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 间距系统
|
||||||
|
|
||||||
|
```css
|
||||||
|
--spacing-xs: 4px;
|
||||||
|
--spacing-sm: 8px;
|
||||||
|
--spacing-md: 12px;
|
||||||
|
--spacing-lg: 16px;
|
||||||
|
--spacing-xl: 24px;
|
||||||
|
--spacing-xxl: 32px;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 组件样式
|
||||||
|
|
||||||
|
### 6.1 按钮
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* 主按钮 */
|
||||||
|
.btn-primary {
|
||||||
|
background-color: #1890ff;
|
||||||
|
color: #ffffff;
|
||||||
|
border: none;
|
||||||
|
padding: 6px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
background-color: #40a9ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 默认按钮 */
|
||||||
|
.btn-default {
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #666666;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
padding: 6px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-default:hover {
|
||||||
|
border-color: #1890ff;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 链接按钮 */
|
||||||
|
.btn-link {
|
||||||
|
background: none;
|
||||||
|
color: #1890ff;
|
||||||
|
border: none;
|
||||||
|
padding: 0 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-link:hover {
|
||||||
|
color: #40a9ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 危险按钮 */
|
||||||
|
.btn-danger {
|
||||||
|
background: none;
|
||||||
|
color: #ff4d4f;
|
||||||
|
border: none;
|
||||||
|
padding: 0 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger:hover {
|
||||||
|
color: #ff7875;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 输入框
|
||||||
|
|
||||||
|
```css
|
||||||
|
.form-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333333;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input:focus {
|
||||||
|
border-color: #1890ff;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input::placeholder {
|
||||||
|
color: #cccccc;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 选择框
|
||||||
|
|
||||||
|
```css
|
||||||
|
.form-select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333333;
|
||||||
|
background-color: #ffffff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-select:focus {
|
||||||
|
border-color: #1890ff;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.4 卡片
|
||||||
|
|
||||||
|
```css
|
||||||
|
.card {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-bottom: 1px solid #e8e8e8;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.5 表格
|
||||||
|
|
||||||
|
```css
|
||||||
|
.table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table thead th {
|
||||||
|
background: #fafafa;
|
||||||
|
padding: 12px 16px;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
border-bottom: 1px solid #e8e8e8;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody td {
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-bottom: 1px solid #e8e8e8;
|
||||||
|
color: #666666;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody tr:hover {
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.6 标签
|
||||||
|
|
||||||
|
```css
|
||||||
|
.tag {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-blue {
|
||||||
|
background: #e6f7ff;
|
||||||
|
border: 1px solid #91d5ff;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-green {
|
||||||
|
background: #f6ffed;
|
||||||
|
border: 1px solid #b7eb8f;
|
||||||
|
color: #52c41a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-orange {
|
||||||
|
background: #fff7e6;
|
||||||
|
border: 1px solid #ffd591;
|
||||||
|
color: #fa8c16;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-red {
|
||||||
|
background: #fff1f0;
|
||||||
|
border: 1px solid #ffa39e;
|
||||||
|
color: #f5222d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-gray {
|
||||||
|
background: #f5f5f5;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.7 头像
|
||||||
|
|
||||||
|
```css
|
||||||
|
.avatar {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
background: #1890ff;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-lg {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. 布局系统
|
||||||
|
|
||||||
|
### 7.1 整体布局
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────┐
|
||||||
|
│ Header (顶部导航) │
|
||||||
|
│ #001529 │
|
||||||
|
├──────────┬──────────────────────────────┤
|
||||||
|
│ │ │
|
||||||
|
│ Sidebar │ Content │
|
||||||
|
│ (侧边栏) │ (内容区) │
|
||||||
|
│ #ffffff │ #f0f2f5 │
|
||||||
|
│ 256px │ │
|
||||||
|
│ │ │
|
||||||
|
└──────────┴──────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 顶部导航栏
|
||||||
|
|
||||||
|
```css
|
||||||
|
.layout-header {
|
||||||
|
height: 64px;
|
||||||
|
background: #001529;
|
||||||
|
padding: 0 24px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 侧边栏
|
||||||
|
|
||||||
|
```css
|
||||||
|
.layout-sider {
|
||||||
|
width: 256px;
|
||||||
|
background: #ffffff;
|
||||||
|
border-right: 1px solid #e8e8e8;
|
||||||
|
position: fixed;
|
||||||
|
left: 64px;
|
||||||
|
top: 64px;
|
||||||
|
bottom: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 菜单项 */
|
||||||
|
.menu-item {
|
||||||
|
padding: 12px 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
color: #666666;
|
||||||
|
cursor: pointer;
|
||||||
|
border-left: 3px solid transparent;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item:hover {
|
||||||
|
background: #e6f7ff;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item.active {
|
||||||
|
background: #e6f7ff;
|
||||||
|
color: #1890ff;
|
||||||
|
border-left-color: #1890ff;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.4 内容区
|
||||||
|
|
||||||
|
```css
|
||||||
|
.layout-content {
|
||||||
|
margin-left: 256px;
|
||||||
|
margin-top: 64px;
|
||||||
|
padding: 24px;
|
||||||
|
background: #f0f2f5;
|
||||||
|
min-height: calc(100vh - 64px);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. 统计卡片
|
||||||
|
|
||||||
|
```css
|
||||||
|
.stat-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: 24px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
background: #ffffff;
|
||||||
|
padding: 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666666;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon {
|
||||||
|
float: right;
|
||||||
|
font-size: 24px;
|
||||||
|
color: #1890ff;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. 表单系统
|
||||||
|
|
||||||
|
### 9.1 表单组
|
||||||
|
|
||||||
|
```css
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #333333;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label.required::after {
|
||||||
|
content: ' *';
|
||||||
|
color: #ff4d4f;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.2 表单行(两列布局)
|
||||||
|
|
||||||
|
```css
|
||||||
|
.form-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row-3 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 10. 工具栏
|
||||||
|
|
||||||
|
```css
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-left {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 11. Modal 弹窗
|
||||||
|
|
||||||
|
```css
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.45);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
width: 600px;
|
||||||
|
max-width: 90vw;
|
||||||
|
max-height: 90vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
padding: 16px 24px;
|
||||||
|
border-bottom: 1px solid #e8e8e8;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
padding: 16px 24px;
|
||||||
|
border-top: 1px solid #e8e8e8;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 12. 响应式设计
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* 平板 */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.stat-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端 */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.layout-sider {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-content {
|
||||||
|
margin-left: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 13. 动画效果
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* 过渡动画 */
|
||||||
|
.transition-all {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 悬停效果 */
|
||||||
|
.hover-scale:hover {
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover-shadow:hover {
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 淡入淡出 */
|
||||||
|
.fade-in {
|
||||||
|
animation: fadeIn 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 14. React 组件实现参考
|
||||||
|
|
||||||
|
### 14.1 颜色配置 (config/colors.js)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
export const colors = {
|
||||||
|
primary: '#1890ff',
|
||||||
|
success: '#52c41a',
|
||||||
|
warning: '#fa8c16',
|
||||||
|
error: '#ff4d4f',
|
||||||
|
info: '#1890ff',
|
||||||
|
|
||||||
|
text: {
|
||||||
|
primary: '#333333',
|
||||||
|
secondary: '#666666',
|
||||||
|
disabled: '#999999',
|
||||||
|
},
|
||||||
|
|
||||||
|
border: {
|
||||||
|
base: '#e8e8e8',
|
||||||
|
light: '#f0f0f0',
|
||||||
|
},
|
||||||
|
|
||||||
|
bg: {
|
||||||
|
header: '#001529',
|
||||||
|
sidebar: '#ffffff',
|
||||||
|
content: '#f0f2f5',
|
||||||
|
component: '#ffffff',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 14.2 状态标签组件 (components/Tag.jsx)
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import React from 'react';
|
||||||
|
import { colors } from '../config/colors';
|
||||||
|
|
||||||
|
const tagStyles = {
|
||||||
|
new: { bg: '#e6f7ff', color: '#1890ff', border: '#91d5ff' },
|
||||||
|
inProgress: { bg: '#f6ffed', color: '#52c41a', border: '#b7eb8f' },
|
||||||
|
completed: { bg: '#f5f5f5', color: '#999999', border: '#d9d9d9' },
|
||||||
|
paused: { bg: '#fff7e6', color: '#fa8c16', border: '#ffd591' },
|
||||||
|
cancelled: { bg: '#fff1f0', color: '#f5222d', border: '#ffa39e' },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Tag = ({ children, type = 'default' }) => {
|
||||||
|
const style = tagStyles[type] || { bg: '#f5f5f5', color: '#999999', border: '#d9d9d9' };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span style={{
|
||||||
|
display: 'inline-block',
|
||||||
|
padding: '2px 8px',
|
||||||
|
borderRadius: '2px',
|
||||||
|
fontSize: '12px',
|
||||||
|
lineHeight: 1.5,
|
||||||
|
background: style.bg,
|
||||||
|
color: style.color,
|
||||||
|
border: `1px solid ${style.border}`,
|
||||||
|
}}>
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 14.3 卡片组件 (components/Card.jsx)
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export const Card = ({ title, children, extra }) => (
|
||||||
|
<div style={{
|
||||||
|
background: '#ffffff',
|
||||||
|
borderRadius: '8px',
|
||||||
|
padding: '24px',
|
||||||
|
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)',
|
||||||
|
marginBottom: '24px',
|
||||||
|
}}>
|
||||||
|
{title && (
|
||||||
|
<div style={{
|
||||||
|
fontSize: '16px',
|
||||||
|
fontWeight: 600,
|
||||||
|
color: '#333333',
|
||||||
|
marginBottom: '20px',
|
||||||
|
borderBottom: '1px solid #e8e8e8',
|
||||||
|
paddingBottom: '12px',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}>
|
||||||
|
{title}
|
||||||
|
{extra}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 15. 图标使用
|
||||||
|
|
||||||
|
推荐使用以下图标库:
|
||||||
|
- **Ant Design Icons**:`@ant-design/icons`
|
||||||
|
- **React Icons**:`react-icons`
|
||||||
|
|
||||||
|
常用图标:
|
||||||
|
- Dashboard: `DashboardOutlined`
|
||||||
|
- Project: `FileTextOutlined`
|
||||||
|
- User: `UserOutlined`
|
||||||
|
- Settings: `SettingOutlined`
|
||||||
|
- Add: `PlusOutlined`
|
||||||
|
- Edit: `EditOutlined`
|
||||||
|
- Delete: `DeleteOutlined`
|
||||||
|
- View: `EyeOutlined`
|
||||||
|
- Logout: `LogoutOutlined`
|
||||||
|
|
||||||
|
## 16. 页面布局模板
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const PageLayout = ({ children, title }) => (
|
||||||
|
<div style={{
|
||||||
|
marginLeft: '256px',
|
||||||
|
marginTop: '64px',
|
||||||
|
padding: '24px',
|
||||||
|
background: '#f0f2f5',
|
||||||
|
minHeight: 'calc(100vh - 64px)',
|
||||||
|
}}>
|
||||||
|
{title && (
|
||||||
|
<h1 style={{
|
||||||
|
fontSize: '24px',
|
||||||
|
fontWeight: 600,
|
||||||
|
color: '#333333',
|
||||||
|
marginBottom: '24px',
|
||||||
|
}}>
|
||||||
|
{title}
|
||||||
|
</h1>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 17. 注意事项
|
||||||
|
|
||||||
|
1. **颜色使用**:统一使用预定义的颜色变量,不要使用硬编码的颜色值
|
||||||
|
2. **间距统一**:使用间距系统保持一致性
|
||||||
|
3. **响应式**:移动端、平板、桌面端都要适配
|
||||||
|
4. **可访问性**:确保对比度符合WCAG标准,最小对比度4.5:1
|
||||||
|
5. **性能**:避免过度使用动画,保持流畅的用户体验
|
||||||
|
6. **浏览器兼容**:确保在主流浏览器中正常显示
|
||||||
@@ -0,0 +1,293 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>项目信息管理系统 - UI预览</title>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/antd/5.12.2/reset.min.css">
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.11.10/dayjs.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.11.10/locale/zh-cn.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/antd/5.12.2/antd.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/@ant-design/icons/5.2.6/index.min.js"></script>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||||
|
background: #f0f2f5;
|
||||||
|
}
|
||||||
|
#root {
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
.ant-layout {
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
.ant-layout-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 24px;
|
||||||
|
background: #001529;
|
||||||
|
}
|
||||||
|
.ant-layout-sider {
|
||||||
|
box-shadow: 2px 0 8px rgba(0,0,0,0.15);
|
||||||
|
}
|
||||||
|
.ant-layout-content {
|
||||||
|
margin: 24px 16px;
|
||||||
|
padding: 24px;
|
||||||
|
min-height: 280px;
|
||||||
|
}
|
||||||
|
.logo {
|
||||||
|
color: white;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.user-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.dashboard-cards {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||||
|
gap: 24px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.dashboard-card {
|
||||||
|
background: white;
|
||||||
|
padding: 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.dashboard-card h3 {
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.dashboard-card .number {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
.preview-section {
|
||||||
|
margin-bottom: 48px;
|
||||||
|
}
|
||||||
|
.preview-section h2 {
|
||||||
|
border-bottom: 2px solid #1890ff;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.mockup {
|
||||||
|
background: white;
|
||||||
|
padding: 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const { useState } = React;
|
||||||
|
const { Layout, Menu, Card, Table, Button, Form, Input, Select, DatePicker, Space, Dropdown, Avatar, Tag, Modal, message, Statistic } = antd;
|
||||||
|
const { Header, Sider, Content } = Layout;
|
||||||
|
const { ProjectOutlined, UserOutlined, DashboardOutlined, LogoutOutlined, SettingOutlined, FileTextOutlined, TeamOutlined, PlusOutlined, EditOutlined, DeleteOutlined, EyeOutlined } = icons;
|
||||||
|
dayjs.locale('zh-cn');
|
||||||
|
|
||||||
|
const { Option } = Select;
|
||||||
|
const { RangePicker } = DatePicker;
|
||||||
|
|
||||||
|
const menuItems = [
|
||||||
|
{ key: 'dashboard', icon: React.createElement(DashboardOutlined), label: '仪表盘' },
|
||||||
|
{ key: 'projects', icon: React.createElement(FileTextOutlined), label: '项目管理' },
|
||||||
|
{ key: 'users', icon: React.createElement(TeamOutlined), label: '用户管理' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const projectColumns = [
|
||||||
|
{ title: '项目编号', dataIndex: 'project_no', key: 'project_no' },
|
||||||
|
{ title: '合同编号', dataIndex: 'contract_no', key: 'contract_no' },
|
||||||
|
{ title: '项目名称', dataIndex: 'name', key: 'name' },
|
||||||
|
{ title: '预算', dataIndex: 'budget', key: 'budget', render: (val) => `¥${val.toLocaleString()}` },
|
||||||
|
{ title: '已付款', dataIndex: 'payment_amount', key: 'payment_amount', render: (val) => `¥${val.toLocaleString()}` },
|
||||||
|
{ title: '状态', dataIndex: 'status', key: 'status', render: (status) => {
|
||||||
|
const colorMap = { '新建': 'blue', '进行中': 'green', '已完成': 'default', '已暂停': 'orange', '已取消': 'red' };
|
||||||
|
return React.createElement(Tag, { color: colorMap[status] || 'default' }, status);
|
||||||
|
}},
|
||||||
|
{ title: '所属部门', dataIndex: 'department', key: 'department' },
|
||||||
|
{ title: '创建人', dataIndex: 'created_by_name', key: 'created_by_name' },
|
||||||
|
{ title: '创建时间', dataIndex: 'created_at', key: 'created_at', render: (val) => dayjs(val).format('YYYY-MM-DD') },
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
render: () => React.createElement(Space, null,
|
||||||
|
React.createElement(Button, { type: 'link', size: 'small', icon: React.createElement(EyeOutlined) }, '查看'),
|
||||||
|
React.createElement(Button, { type: 'link', size: 'small', icon: React.createElement(EditOutlined) }, '编辑'),
|
||||||
|
React.createElement(Button, { type: 'link', size: 'small', danger: true, icon: React.createElement(DeleteOutlined) }, '删除')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const userColumns = [
|
||||||
|
{ title: '用户名', dataIndex: 'username', key: 'username' },
|
||||||
|
{ title: '真实姓名', dataIndex: 'real_name', key: 'real_name' },
|
||||||
|
{ title: '部门', dataIndex: 'department', key: 'department' },
|
||||||
|
{ title: '角色', dataIndex: 'role', key: 'role', render: (role) => {
|
||||||
|
const roleMap = { 'admin': '管理员', 'market': '市场部', 'other': '其他部门' };
|
||||||
|
return React.createElement(Tag, { color: role === 'admin' ? 'red' : 'blue' }, roleMap[role] || role);
|
||||||
|
}},
|
||||||
|
{ title: '邮箱', dataIndex: 'email', key: 'email' },
|
||||||
|
{ title: '状态', dataIndex: 'is_active', key: 'is_active', render: (active) => React.createElement(Tag, { color: active ? 'green' : 'default' }, active ? '正常' : '禁用') },
|
||||||
|
{ title: '创建时间', dataIndex: 'created_at', key: 'created_at', render: (val) => dayjs(val).format('YYYY-MM-DD') },
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
render: () => React.createElement(Space, null,
|
||||||
|
React.createElement(Button, { type: 'link', size: 'small', icon: React.createElement(EditOutlined) }, '编辑'),
|
||||||
|
React.createElement(Button, { type: 'link', size: 'small', icon: React.createElement(SettingOutlined) }, '重置密码'),
|
||||||
|
React.createElement(Button, { type: 'link', size: 'small', danger: true, icon: React.createElement(DeleteOutlined) }, '删除')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const projectData = [
|
||||||
|
{ key: 1, project_no: 'PRJ2026001', contract_no: 'CT2026001', name: '某公司官网开发', budget: 50000, payment_amount: 25000, status: '进行中', department: '市场部', created_by_name: '张三', created_at: '2026-01-24' },
|
||||||
|
{ key: 2, project_no: 'PRJ2026002', contract_no: 'CT2026002', name: '电商平台开发', budget: 100000, payment_amount: 50000, status: '新建', department: '市场部', created_by_name: '李四', created_at: '2026-01-23' },
|
||||||
|
{ key: 3, project_no: 'PRJ2026003', contract_no: 'CT2026003', name: '移动APP开发', budget: 150000, payment_amount: 150000, status: '已完成', department: '技术部', created_by_name: '王五', created_at: '2026-01-20' },
|
||||||
|
{ key: 4, project_no: 'PRJ2026004', contract_no: 'CT2026004', name: '数据分析系统', budget: 80000, payment_amount: 30000, status: '进行中', department: '市场部', created_by_name: '张三', created_at: '2026-01-18' },
|
||||||
|
{ key: 5, project_no: 'PRJ2026005', contract_no: 'CT2026005', name: '企业OA系统', budget: 200000, payment_amount: 0, status: '已暂停', department: '技术部', created_by_name: '赵六', created_at: '2026-01-15' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const userData = [
|
||||||
|
{ key: 1, username: 'admin', real_name: '管理员', department: '管理部', role: 'admin', email: 'admin@example.com', is_active: true, created_at: '2026-01-10' },
|
||||||
|
{ key: 2, username: 'zhangsan', real_name: '张三', department: '市场部', role: 'market', email: 'zhangsan@example.com', is_active: true, created_at: '2026-01-15' },
|
||||||
|
{ key: 3, username: 'lisi', real_name: '李四', department: '市场部', role: 'market', email: 'lisi@example.com', is_active: true, created_at: '2026-01-16' },
|
||||||
|
{ key: 4, username: 'wangwu', real_name: '王五', department: '技术部', role: 'other', email: 'wangwu@example.com', is_active: true, created_at: '2026-01-17' },
|
||||||
|
{ key: 5, username: 'zhaoliu', real_name: '赵六', department: '技术部', role: 'other', email: 'zhaoliu@example.com', is_active: false, created_at: '2026-01-18' },
|
||||||
|
];
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [currentView, setCurrentView] = useState('dashboard');
|
||||||
|
const [selectedMenu, setSelectedMenu] = useState('dashboard');
|
||||||
|
const [userModalVisible, setUserModalVisible] = useState(false);
|
||||||
|
const [projectModalVisible, setProjectModalVisible] = useState(false);
|
||||||
|
|
||||||
|
const handleMenuClick = ({ key }) => {
|
||||||
|
setSelectedMenu(key);
|
||||||
|
setCurrentView(key);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Dashboard = () => React.createElement('div', null,
|
||||||
|
React.createElement('div', { className: 'dashboard-cards' },
|
||||||
|
React.createElement(Card, null,
|
||||||
|
React.createElement(Statistic, { title: '总项目数', value: 15, prefix: React.createElement(ProjectOutlined) })
|
||||||
|
),
|
||||||
|
React.createElement(Card, null,
|
||||||
|
React.createElement(Statistic, { title: '进行中', value: 8, valueStyle: { color: '#3f8600' }, prefix: React.createElement(FileTextOutlined) })
|
||||||
|
),
|
||||||
|
React.createElement(Card, null,
|
||||||
|
React.createElement(Statistic, { title: '已完成', value: 5, prefix: React.createElement(FileTextOutlined) })
|
||||||
|
),
|
||||||
|
React.createElement(Card, null,
|
||||||
|
React.createElement(Statistic, { title: '本月预算', value: 500000, prefix: '¥' })
|
||||||
|
)
|
||||||
|
),
|
||||||
|
React.createElement(Card, { title: '最近项目' },
|
||||||
|
React.createElement(Table, {
|
||||||
|
columns: projectColumns,
|
||||||
|
dataSource: projectData.slice(0, 3),
|
||||||
|
pagination: false
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const ProjectList = () => React.createElement('div', null,
|
||||||
|
React.createElement('div', { style: { marginBottom: 16, display: 'flex', justifyContent: 'space-between' } },
|
||||||
|
React.createElement(Space, null,
|
||||||
|
React.createElement(Input, { placeholder: '搜索项目...', style: { width: 200 } }),
|
||||||
|
React.createElement(Select, { placeholder: '状态', style: { width: 120 } },
|
||||||
|
React.createElement(Option, { value: '新建' }, '新建'),
|
||||||
|
React.createElement(Option, { value: '进行中' }, '进行中'),
|
||||||
|
React.createElement(Option, { value: '已完成' }, '已完成')
|
||||||
|
),
|
||||||
|
React.createElement(Select, { placeholder: '部门', style: { width: 120 } },
|
||||||
|
React.createElement(Option, { value: '市场部' }, '市场部'),
|
||||||
|
React.createElement(Option, { value: '技术部' }, '技术部')
|
||||||
|
)
|
||||||
|
),
|
||||||
|
React.createElement(Button, { type: 'primary', icon: React.createElement(PlusOutlined) }, '新建项目')
|
||||||
|
),
|
||||||
|
React.createElement(Card, null,
|
||||||
|
React.createElement(Table, {
|
||||||
|
columns: projectColumns,
|
||||||
|
dataSource: projectData,
|
||||||
|
pagination: { pageSize: 10 }
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const UserList = () => React.createElement('div', null,
|
||||||
|
React.createElement('div', { style: { marginBottom: 16, display: 'flex', justifyContent: 'space-between' } },
|
||||||
|
React.createElement(Space, null,
|
||||||
|
React.createElement(Input, { placeholder: '搜索用户...', style: { width: 200 } }),
|
||||||
|
React.createElement(Select, { placeholder: '角色', style: { width: 120 } },
|
||||||
|
React.createElement(Option, { value: 'admin' }, '管理员'),
|
||||||
|
React.createElement(Option, { value: 'market' }, '市场部'),
|
||||||
|
React.createElement(Option, { value: 'other' }, '其他部门')
|
||||||
|
)
|
||||||
|
),
|
||||||
|
React.createElement(Button, { type: 'primary', icon: React.createElement(PlusOutlined) }, '新建用户')
|
||||||
|
),
|
||||||
|
React.createElement(Card, null,
|
||||||
|
React.createElement(Table, {
|
||||||
|
columns: userColumns,
|
||||||
|
dataSource: userData,
|
||||||
|
pagination: { pageSize: 10 }
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const content = {
|
||||||
|
dashboard: Dashboard,
|
||||||
|
projects: ProjectList,
|
||||||
|
users: UserList
|
||||||
|
}[currentView] || Dashboard;
|
||||||
|
|
||||||
|
return React.createElement(Layout, null,
|
||||||
|
React.createElement(Header, null,
|
||||||
|
React.createElement('div', { className: 'logo' },
|
||||||
|
React.createElement(ProjectOutlined),
|
||||||
|
' 项目信息管理系统'
|
||||||
|
),
|
||||||
|
React.createElement('div', { className: 'user-info' },
|
||||||
|
React.createElement(Avatar, { icon: React.createElement(UserOutlined) }),
|
||||||
|
React.createElement('span', { style: { color: 'white' } }, '管理员'),
|
||||||
|
React.createElement(Button, { type: 'text', icon: React.createElement(LogoutOutlined), style: { color: 'white' } }, '退出')
|
||||||
|
)
|
||||||
|
),
|
||||||
|
React.createElement(Layout, null,
|
||||||
|
React.createElement(Sider, { width: 256, style: { background: '#fff' } },
|
||||||
|
React.createElement(Menu, {
|
||||||
|
mode: 'inline',
|
||||||
|
selectedKeys: [selectedMenu],
|
||||||
|
items: menuItems,
|
||||||
|
onClick: handleMenuClick
|
||||||
|
})
|
||||||
|
),
|
||||||
|
React.createElement(Content, { style: { background: '#fff' } }, content())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||||
|
root.render(React.createElement(App));
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+762
@@ -0,0 +1,762 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>项目信息管理系统 - UI设计图</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||||
|
background: #f0f2f5;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.design-container {
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.design-title {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.section-title {
|
||||||
|
font-size: 24px;
|
||||||
|
color: #1890ff;
|
||||||
|
border-bottom: 3px solid #1890ff;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
margin: 40px 0 20px 0;
|
||||||
|
}
|
||||||
|
.mockup {
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||||
|
margin-bottom: 30px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.mockup-header {
|
||||||
|
background: #001529;
|
||||||
|
color: white;
|
||||||
|
padding: 16px 24px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.logo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.logo-icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
background: #1890ff;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.user-nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
.avatar {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
background: #1890ff;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.layout-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 256px 1fr;
|
||||||
|
min-height: 600px;
|
||||||
|
}
|
||||||
|
.sidebar {
|
||||||
|
background: #fff;
|
||||||
|
border-right: 1px solid #e8e8e8;
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
.menu-item {
|
||||||
|
padding: 12px 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
color: #666;
|
||||||
|
cursor: pointer;
|
||||||
|
border-left: 3px solid transparent;
|
||||||
|
}
|
||||||
|
.menu-item:hover {
|
||||||
|
background: #e6f7ff;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
.menu-item.active {
|
||||||
|
background: #e6f7ff;
|
||||||
|
color: #1890ff;
|
||||||
|
border-left-color: #1890ff;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: 24px;
|
||||||
|
background: #f0f2f5;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 24px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.card-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.stat-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.stat-card {
|
||||||
|
background: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
||||||
|
}
|
||||||
|
.stat-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.stat-value {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
.stat-icon {
|
||||||
|
float: right;
|
||||||
|
font-size: 24px;
|
||||||
|
color: #1890ff;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background: #fafafa;
|
||||||
|
padding: 12px 16px;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
border-bottom: 1px solid #e8e8e8;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-bottom: 1px solid #e8e8e8;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.tag {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.tag-blue { background: #e6f7ff; color: #1890ff; }
|
||||||
|
.tag-green { background: #f6ffed; color: #52c41a; }
|
||||||
|
.tag-orange { background: #fff7e6; color: #fa8c16; }
|
||||||
|
.tag-red { background: #fff1f0; color: #f5222d; }
|
||||||
|
.tag-gray { background: #f5f5f5; color: #999; }
|
||||||
|
.btn {
|
||||||
|
padding: 6px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.btn-primary {
|
||||||
|
background: #1890ff;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.btn-default {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.btn-link {
|
||||||
|
background: none;
|
||||||
|
color: #1890ff;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
.btn-danger {
|
||||||
|
background: none;
|
||||||
|
color: #ff4d4f;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.toolbar-left {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.input {
|
||||||
|
padding: 6px 12px;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
.select {
|
||||||
|
padding: 6px 12px;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.form-label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.form-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.form-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
.login-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 600px;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
}
|
||||||
|
.login-box {
|
||||||
|
background: white;
|
||||||
|
padding: 40px;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 400px;
|
||||||
|
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
|
||||||
|
}
|
||||||
|
.login-title {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.login-btn {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
background: #1890ff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 16px;
|
||||||
|
margin-top: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.design-note {
|
||||||
|
background: #fffbe6;
|
||||||
|
border-left: 4px solid #faad14;
|
||||||
|
padding: 16px;
|
||||||
|
margin: 20px 0;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="design-container">
|
||||||
|
<h1 class="design-title">📐 项目信息管理系统 - UI设计图</h1>
|
||||||
|
|
||||||
|
<!-- 登录页 -->
|
||||||
|
<h2 class="section-title">1. 登录页面</h2>
|
||||||
|
<div class="mockup">
|
||||||
|
<div class="login-container">
|
||||||
|
<div class="login-box">
|
||||||
|
<div class="login-title">项目信息管理系统</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">用户名</label>
|
||||||
|
<input type="text" class="form-input" placeholder="请输入用户名">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">密码</label>
|
||||||
|
<input type="password" class="form-input" placeholder="请输入密码">
|
||||||
|
</div>
|
||||||
|
<button class="login-btn">登录</button>
|
||||||
|
<div style="text-align: center; margin-top: 20px; color: #999; font-size: 12px;">
|
||||||
|
© 2026 项目信息管理系统
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 仪表盘 -->
|
||||||
|
<h2 class="section-title">2. 仪表盘页面</h2>
|
||||||
|
<div class="mockup">
|
||||||
|
<div class="mockup-header">
|
||||||
|
<div class="logo">
|
||||||
|
<div class="logo-icon"></div>
|
||||||
|
<span>项目信息管理系统</span>
|
||||||
|
</div>
|
||||||
|
<div class="user-nav">
|
||||||
|
<span>管理员</span>
|
||||||
|
<div class="avatar">管</div>
|
||||||
|
<button class="btn btn-link" style="color: white;">退出</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layout-grid">
|
||||||
|
<div class="sidebar">
|
||||||
|
<div class="menu-item active">
|
||||||
|
<span>📊</span> 仪表盘
|
||||||
|
</div>
|
||||||
|
<div class="menu-item">
|
||||||
|
<span>📄</span> 项目管理
|
||||||
|
</div>
|
||||||
|
<div class="menu-item">
|
||||||
|
<span>👥</span> 用户管理
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div class="stat-grid">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon">📊</div>
|
||||||
|
<div class="stat-label">总项目数</div>
|
||||||
|
<div class="stat-value">15</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon">🔄</div>
|
||||||
|
<div class="stat-label">进行中</div>
|
||||||
|
<div class="stat-value" style="color: #52c41a;">8</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon">✅</div>
|
||||||
|
<div class="stat-label">已完成</div>
|
||||||
|
<div class="stat-value" style="color: #1890ff;">5</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon">💰</div>
|
||||||
|
<div class="stat-label">本月预算</div>
|
||||||
|
<div class="stat-value">¥50万</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title">最近项目</div>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>项目编号</th>
|
||||||
|
<th>项目名称</th>
|
||||||
|
<th>预算</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>创建人</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>PRJ2026001</td>
|
||||||
|
<td>某公司官网开发</td>
|
||||||
|
<td>¥50,000</td>
|
||||||
|
<td><span class="tag tag-green">进行中</span></td>
|
||||||
|
<td>张三</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>PRJ2026002</td>
|
||||||
|
<td>电商平台开发</td>
|
||||||
|
<td>¥100,000</td>
|
||||||
|
<td><span class="tag tag-blue">新建</span></td>
|
||||||
|
<td>李四</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>PRJ2026003</td>
|
||||||
|
<td>移动APP开发</td>
|
||||||
|
<td>¥150,000</td>
|
||||||
|
<td><span class="tag tag-gray">已完成</span></td>
|
||||||
|
<td>王五</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 项目管理 -->
|
||||||
|
<h2 class="section-title">3. 项目管理页面</h2>
|
||||||
|
<div class="mockup">
|
||||||
|
<div class="mockup-header">
|
||||||
|
<div class="logo">
|
||||||
|
<div class="logo-icon"></div>
|
||||||
|
<span>项目信息管理系统</span>
|
||||||
|
</div>
|
||||||
|
<div class="user-nav">
|
||||||
|
<span>管理员</span>
|
||||||
|
<div class="avatar">管</div>
|
||||||
|
<button class="btn btn-link" style="color: white;">退出</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layout-grid">
|
||||||
|
<div class="sidebar">
|
||||||
|
<div class="menu-item">
|
||||||
|
<span>📊</span> 仪表盘
|
||||||
|
</div>
|
||||||
|
<div class="menu-item active">
|
||||||
|
<span>📄</span> 项目管理
|
||||||
|
</div>
|
||||||
|
<div class="menu-item">
|
||||||
|
<span>👥</span> 用户管理
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title">项目管理</div>
|
||||||
|
<div class="toolbar">
|
||||||
|
<div class="toolbar-left">
|
||||||
|
<input type="text" class="input" placeholder="搜索项目...">
|
||||||
|
<select class="select">
|
||||||
|
<option>所有状态</option>
|
||||||
|
<option>新建</option>
|
||||||
|
<option>进行中</option>
|
||||||
|
<option>已完成</option>
|
||||||
|
</select>
|
||||||
|
<select class="select">
|
||||||
|
<option>所有部门</option>
|
||||||
|
<option>市场部</option>
|
||||||
|
<option>技术部</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary">+ 新建项目</button>
|
||||||
|
</div>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>项目编号</th>
|
||||||
|
<th>合同编号</th>
|
||||||
|
<th>项目名称</th>
|
||||||
|
<th>预算</th>
|
||||||
|
<th>已付款</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>部门</th>
|
||||||
|
<th>创建人</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>PRJ2026001</td>
|
||||||
|
<td>CT2026001</td>
|
||||||
|
<td>某公司官网开发</td>
|
||||||
|
<td>¥50,000</td>
|
||||||
|
<td>¥25,000</td>
|
||||||
|
<td><span class="tag tag-green">进行中</span></td>
|
||||||
|
<td>市场部</td>
|
||||||
|
<td>张三</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-link">查看</button>
|
||||||
|
<button class="btn btn-link">编辑</button>
|
||||||
|
<button class="btn btn-danger">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>PRJ2026002</td>
|
||||||
|
<td>CT2026002</td>
|
||||||
|
<td>电商平台开发</td>
|
||||||
|
<td>¥100,000</td>
|
||||||
|
<td>¥50,000</td>
|
||||||
|
<td><span class="tag tag-blue">新建</span></td>
|
||||||
|
<td>市场部</td>
|
||||||
|
<td>李四</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-link">查看</button>
|
||||||
|
<button class="btn btn-link">编辑</button>
|
||||||
|
<button class="btn btn-danger">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>PRJ2026003</td>
|
||||||
|
<td>CT2026003</td>
|
||||||
|
<td>移动APP开发</td>
|
||||||
|
<td>¥150,000</td>
|
||||||
|
<td>¥150,000</td>
|
||||||
|
<td><span class="tag tag-gray">已完成</span></td>
|
||||||
|
<td>技术部</td>
|
||||||
|
<td>王五</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-link">查看</button>
|
||||||
|
<button class="btn btn-link">编辑</button>
|
||||||
|
<button class="btn btn-danger">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>PRJ2026004</td>
|
||||||
|
<td>CT2026004</td>
|
||||||
|
<td>数据分析系统</td>
|
||||||
|
<td>¥80,000</td>
|
||||||
|
<td>¥30,000</td>
|
||||||
|
<td><span class="tag tag-green">进行中</span></td>
|
||||||
|
<td>市场部</td>
|
||||||
|
<td>张三</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-link">查看</button>
|
||||||
|
<button class="btn btn-link">编辑</button>
|
||||||
|
<button class="btn btn-danger">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>PRJ2026005</td>
|
||||||
|
<td>CT2026005</td>
|
||||||
|
<td>企业OA系统</td>
|
||||||
|
<td>¥200,000</td>
|
||||||
|
<td>¥0</td>
|
||||||
|
<td><span class="tag tag-orange">已暂停</span></td>
|
||||||
|
<td>技术部</td>
|
||||||
|
<td>赵六</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-link">查看</button>
|
||||||
|
<button class="btn btn-link">编辑</button>
|
||||||
|
<button class="btn btn-danger">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 用户管理 -->
|
||||||
|
<h2 class="section-title">4. 用户管理页面(仅管理员可见)</h2>
|
||||||
|
<div class="mockup">
|
||||||
|
<div class="mockup-header">
|
||||||
|
<div class="logo">
|
||||||
|
<div class="logo-icon"></div>
|
||||||
|
<span>项目信息管理系统</span>
|
||||||
|
</div>
|
||||||
|
<div class="user-nav">
|
||||||
|
<span>管理员</span>
|
||||||
|
<div class="avatar">管</div>
|
||||||
|
<button class="btn btn-link" style="color: white;">退出</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layout-grid">
|
||||||
|
<div class="sidebar">
|
||||||
|
<div class="menu-item">
|
||||||
|
<span>📊</span> 仪表盘
|
||||||
|
</div>
|
||||||
|
<div class="menu-item">
|
||||||
|
<span>📄</span> 项目管理
|
||||||
|
</div>
|
||||||
|
<div class="menu-item active">
|
||||||
|
<span>👥</span> 用户管理
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title">用户管理</div>
|
||||||
|
<div class="toolbar">
|
||||||
|
<div class="toolbar-left">
|
||||||
|
<input type="text" class="input" placeholder="搜索用户...">
|
||||||
|
<select class="select">
|
||||||
|
<option>所有角色</option>
|
||||||
|
<option>管理员</option>
|
||||||
|
<option>市场部</option>
|
||||||
|
<option>其他部门</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary">+ 新建用户</button>
|
||||||
|
</div>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>用户名</th>
|
||||||
|
<th>真实姓名</th>
|
||||||
|
<th>部门</th>
|
||||||
|
<th>角色</th>
|
||||||
|
<th>邮箱</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>创建时间</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>admin</td>
|
||||||
|
<td>管理员</td>
|
||||||
|
<td>管理部</td>
|
||||||
|
<td><span class="tag tag-red">管理员</span></td>
|
||||||
|
<td>admin@example.com</td>
|
||||||
|
<td><span class="tag tag-green">正常</span></td>
|
||||||
|
<td>2026-01-10</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-link">编辑</button>
|
||||||
|
<button class="btn btn-link">重置密码</button>
|
||||||
|
<button class="btn btn-link">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>zhangsan</td>
|
||||||
|
<td>张三</td>
|
||||||
|
<td>市场部</td>
|
||||||
|
<td><span class="tag tag-blue">市场部</span></td>
|
||||||
|
<td>zhangsan@example.com</td>
|
||||||
|
<td><span class="tag tag-green">正常</span></td>
|
||||||
|
<td>2026-01-15</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-link">编辑</button>
|
||||||
|
<button class="btn btn-link">重置密码</button>
|
||||||
|
<button class="btn btn-danger">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>lisi</td>
|
||||||
|
<td>李四</td>
|
||||||
|
<td>市场部</td>
|
||||||
|
<td><span class="tag tag-blue">市场部</span></td>
|
||||||
|
<td>lisi@example.com</td>
|
||||||
|
<td><span class="tag tag-green">正常</span></td>
|
||||||
|
<td>2026-01-16</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-link">编辑</button>
|
||||||
|
<button class="btn btn-link">重置密码</button>
|
||||||
|
<button class="btn btn-danger">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>wangwu</td>
|
||||||
|
<td>王五</td>
|
||||||
|
<td>技术部</td>
|
||||||
|
<td><span class="tag tag-blue">其他部门</span></td>
|
||||||
|
<td>wangwu@example.com</td>
|
||||||
|
<td><span class="tag tag-green">正常</span></td>
|
||||||
|
<td>2026-01-17</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-link">编辑</button>
|
||||||
|
<button class="btn btn-link">重置密码</button>
|
||||||
|
<button class="btn btn-danger">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>zhaoliu</td>
|
||||||
|
<td>赵六</td>
|
||||||
|
<td>技术部</td>
|
||||||
|
<td><span class="tag tag-blue">其他部门</span></td>
|
||||||
|
<td>zhaoliu@example.com</td>
|
||||||
|
<td><span class="tag tag-gray">禁用</span></td>
|
||||||
|
<td>2026-01-18</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-link">编辑</button>
|
||||||
|
<button class="btn btn-link">重置密码</button>
|
||||||
|
<button class="btn btn-danger">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 新建/编辑项目表单 -->
|
||||||
|
<h2 class="section-title">5. 新建/编辑项目表单</h2>
|
||||||
|
<div class="mockup">
|
||||||
|
<div class="content">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-title">新建项目</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">项目编号 *</label>
|
||||||
|
<input type="text" class="form-input" placeholder="如:PRJ2026001">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">合同编号 *</label>
|
||||||
|
<input type="text" class="form-input" placeholder="如:CT2026001">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">项目名称 *</label>
|
||||||
|
<input type="text" class="form-input" placeholder="请输入项目名称">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">预算金额 *</label>
|
||||||
|
<input type="text" class="form-input" placeholder="请输入预算金额">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">已付款金额</label>
|
||||||
|
<input type="text" class="form-input" placeholder="请输入已付款金额">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">所属部门 *</label>
|
||||||
|
<select class="form-input">
|
||||||
|
<option>市场部</option>
|
||||||
|
<option>技术部</option>
|
||||||
|
<option>财务部</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">项目状态 *</label>
|
||||||
|
<select class="form-input">
|
||||||
|
<option>新建</option>
|
||||||
|
<option>进行中</option>
|
||||||
|
<option>已完成</option>
|
||||||
|
<option>已暂停</option>
|
||||||
|
<option>已取消</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">开始日期</label>
|
||||||
|
<input type="date" class="form-input">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">结束日期</label>
|
||||||
|
<input type="date" class="form-input">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">项目描述</label>
|
||||||
|
<textarea class="form-input" rows="4" placeholder="请输入项目描述"></textarea>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; gap: 12px; justify-content: flex-end;">
|
||||||
|
<button class="btn btn-default">取消</button>
|
||||||
|
<button class="btn btn-primary">保存</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="design-note">
|
||||||
|
<strong>💡 设计说明:</strong>
|
||||||
|
<ul style="margin-top: 10px; padding-left: 20px;">
|
||||||
|
<li>整体采用Ant Design风格,简洁专业</li>
|
||||||
|
<li>深色顶部导航栏(#001529),浅色侧边栏(白色),内容区使用浅灰背景(#f0f2f5)</li>
|
||||||
|
<li>状态标签使用不同颜色区分:新建-蓝色、进行中-绿色、已完成-灰色、已暂停-橙色、已取消-红色</li>
|
||||||
|
<li>按钮样式:主操作用蓝色(#1890ff),删除操作用红色</li>
|
||||||
|
<li>表格支持搜索和筛选功能</li>
|
||||||
|
<li>所有页面共用相同的布局结构(Header + Sidebar + Content)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user