Compare commits
32
Commits
dev
..
1caeba1225
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1caeba1225 | ||
|
|
44ce17d43d | ||
|
|
31fca48feb | ||
|
|
8f062b0e8c | ||
|
|
1ff36f366d | ||
|
|
edcd5dcbb7 | ||
|
|
dd8c7e0b92 | ||
|
|
56452da055 | ||
|
|
5fa85f7b77 | ||
|
|
a39cef6312 | ||
|
|
9fc19a3ebd | ||
|
|
311d67a1b1 | ||
|
|
9bf2ef67be | ||
|
|
19a1c407f2 | ||
|
|
de5150b385 | ||
|
|
f5c6f75779 | ||
|
|
0ea7eff03d | ||
|
|
a6d435a22f | ||
|
|
136acf2731 | ||
|
|
d6a42f6087 | ||
|
|
2731f5560f | ||
|
|
0604a3dfbf | ||
|
|
3b52792917 | ||
|
|
29704a6197 | ||
|
|
29fa1341d8 | ||
|
|
58719b709b | ||
|
|
01234a6042 | ||
|
|
54f7fe53a5 | ||
|
|
e71abc0c0b | ||
|
|
2cb4684130 | ||
|
|
0d1665f609 | ||
|
|
da62939b0d |
@@ -1,57 +1,60 @@
|
|||||||
# Ant Design Pro
|
# Notify Admin
|
||||||
|
|
||||||
This project is initialized with [Ant Design Pro](https://pro.ant.design). Follow is the quick guide for how to use.
|
## 目标
|
||||||
|
|
||||||
## Environment Prepare
|
1. 微信小程序的信息管理页面
|
||||||
|
2. 人资部后台(长期)
|
||||||
|
|
||||||
Install `node_modules`:
|
## 功能
|
||||||
|
|
||||||
```bash
|
### 主席团管理页面
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
or
|
1. 值班表导入、更新
|
||||||
|
1. 值班表渲染
|
||||||
|
2. 值班表在线更改、上传后端
|
||||||
|
3. 值班表导出
|
||||||
|
2. 成员权限管理
|
||||||
|
1. 从 Auth 获取成员职位、部门
|
||||||
|
2. 主席团成员:可更改所有信息
|
||||||
|
3. 当班所有成员:可更改值班人数,可加入 Oreo,可更改状态
|
||||||
|
4. 所有成员:可认领
|
||||||
|
3. 特殊维修时间(如学园维修)、值班信息更新
|
||||||
|
4. 日志
|
||||||
|
1. 记录每一次操作、操作者
|
||||||
|
5. 自动排班功能(Enhancement)
|
||||||
|
|
||||||
```bash
|
### 值班组长管理页面
|
||||||
yarn
|
|
||||||
```
|
|
||||||
|
|
||||||
## Provided Scripts
|
ps. 是微信小程序中小组管理页面的pc端替代
|
||||||
|
|
||||||
Ant Design Pro provides some useful script to help you quick start and build with web project, code style check and test.
|
1. 值班组长上传本班相关信息
|
||||||
|
2. 值班组长上传值班总结(Enhancement)
|
||||||
|
|
||||||
Scripts provided in `package.json`. It's safe to modify or add additional script:
|
## UI
|
||||||
|
|
||||||
### Start project
|
### 仪表盘
|
||||||
|
|
||||||
```bash
|
1. 个人信息、职位
|
||||||
npm start
|
2. 新系统中(未加入Oreo/预填写工单总数)统计
|
||||||
```
|
|
||||||
|
|
||||||
### Build project
|
### 主席团管理页面
|
||||||
|
|
||||||
```bash
|
#### 值班表管理
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Check code style
|
1. 值班表渲染
|
||||||
|
2. 值班表在线更改、上传后端
|
||||||
|
1. 特殊时间值班表不上传
|
||||||
|
3. 值班表导出
|
||||||
|
|
||||||
```bash
|
#### 特殊值班时间
|
||||||
npm run lint
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also use script to auto fix some lint error:
|
1. (switch) 是否启用平常值班时间
|
||||||
|
2. 若不在平常值班时间,选择录入不值班原因/特殊值班时间
|
||||||
|
|
||||||
```bash
|
### 值班组长管理页面(暂空)
|
||||||
npm run lint:fix
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test code
|
1. (Enhancement) 值班总结上传
|
||||||
|
|
||||||
```bash
|
### 日志(所有人可见)
|
||||||
npm test
|
|
||||||
```
|
|
||||||
|
|
||||||
## More
|
1. 查询功能
|
||||||
|
|
||||||
You can view full document on our [official website](https://pro.ant.design). And welcome any feedback in our [github](https://github.com/ant-design/ant-design-pro).
|
|
||||||
|
|||||||
@@ -233,84 +233,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"RuleListItem": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"key": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"disabled": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"href": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"avatar": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"owner": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"desc": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"callNo": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"updatedAt": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "datetime"
|
|
||||||
},
|
|
||||||
"createdAt": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "datetime"
|
|
||||||
},
|
|
||||||
"progress": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"RuleList": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"data": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/components/schemas/RuleListItem"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"total": {
|
|
||||||
"type": "integer",
|
|
||||||
"description": "列表的内容总数",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"success": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"FakeCaptcha": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"code": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int32"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"LoginParams": {
|
"LoginParams": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|||||||
+6
-3
@@ -14,18 +14,21 @@ export default [
|
|||||||
{ path: '/admin', redirect: '/admin/dutyinfo' },
|
{ path: '/admin', redirect: '/admin/dutyinfo' },
|
||||||
{ path: '/admin/dutyinfo', name: '值班信息', component: './Admin/DutyInfo' },
|
{ path: '/admin/dutyinfo', name: '值班信息', component: './Admin/DutyInfo' },
|
||||||
{ path: '/admin/dutytable', name: '值班表管理', component: './Admin/DutyTable' },
|
{ path: '/admin/dutytable', name: '值班表管理', component: './Admin/DutyTable' },
|
||||||
|
{ path: '/admin/logs', name: '日志', component: './Admin/Logs' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/leader',
|
path: '/leader',
|
||||||
name: '值班管理',
|
name: '值班管理',
|
||||||
icon: 'team',
|
icon: 'team',
|
||||||
|
access: 'canLeader',
|
||||||
routes: [
|
routes: [
|
||||||
{ path: '/leader', redirect: '/admin/dutyinfo' },
|
{ path: '/leader', redirect: '/leader/conclusion' },
|
||||||
{ path: '/leader/conclusion', name: '值班总结', component: './Leader/Conclusion' },
|
{ path: '/leader/conclusion', name: '值班总结管理', component: './Leader/Conclusion' },
|
||||||
|
{ path: '/leader/report', name: '反馈信息', component: './Leader/Report' },
|
||||||
|
{ path: '/leader/askleave', name: '请假管理', component: './Leader/AskLeave' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ name: '日志', icon: 'clockCircle', path: '/logs', component: './Logs' },
|
|
||||||
{ name: '关于我们', icon: 'thunderbolt', path: '/about', component: './About' },
|
{ name: '关于我们', icon: 'thunderbolt', path: '/about', component: './About' },
|
||||||
{ path: '/', redirect: '/welcome' },
|
{ path: '/', redirect: '/welcome' },
|
||||||
{ path: '*', layout: false, component: './404' },
|
{ path: '*', layout: false, component: './404' },
|
||||||
|
|||||||
+298
-48
@@ -24,7 +24,79 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### `GET`请求默认格式(若无特殊说明)
|
### `GET` 请求默认格式(若无特殊说明)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"token": "token_test"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 登陆
|
||||||
|
|
||||||
|
### 账号密码登陆 `POST /login`
|
||||||
|
|
||||||
|
request:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"stuid": "3220114514",
|
||||||
|
"passwd": "__passwd_test__"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
data:
|
||||||
|
|
||||||
|
.role
|
||||||
|
|
||||||
|
-1 => 未定
|
||||||
|
|
||||||
|
0 => 干事
|
||||||
|
|
||||||
|
1 => 部长
|
||||||
|
|
||||||
|
2 => 会长
|
||||||
|
|
||||||
|
3 => 副会长
|
||||||
|
|
||||||
|
4 => 技术指导
|
||||||
|
|
||||||
|
5 => 已退休
|
||||||
|
|
||||||
|
6 => 管理员
|
||||||
|
|
||||||
|
7 => 副部长
|
||||||
|
|
||||||
|
8 => 总监
|
||||||
|
|
||||||
|
.department
|
||||||
|
|
||||||
|
-1 => 未定
|
||||||
|
|
||||||
|
0 => 电器部
|
||||||
|
|
||||||
|
1 => 电脑部
|
||||||
|
|
||||||
|
2 => 人资部
|
||||||
|
|
||||||
|
3 => 文宣部
|
||||||
|
|
||||||
|
4 => 财外部
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"token": "token_test",
|
||||||
|
"name": "亦可",
|
||||||
|
"avatar": "https://....png",
|
||||||
|
"role": 6,
|
||||||
|
"department": 1,
|
||||||
|
"access": "admin" // "admin" => 主席团成员 "leader" => 值班组长 "member" => 普通成员
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 登出 `POST /logout`
|
||||||
|
|
||||||
|
request:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -34,7 +106,7 @@
|
|||||||
|
|
||||||
## 主页面
|
## 主页面
|
||||||
|
|
||||||
### 统计数据 `GET /admin/stats`
|
### 统计数据 `GET /stats`
|
||||||
|
|
||||||
data:
|
data:
|
||||||
|
|
||||||
@@ -42,29 +114,114 @@ data:
|
|||||||
{
|
{
|
||||||
"success": true,
|
"success": true,
|
||||||
"data": {
|
"data": {
|
||||||
"notInOreo": 114,
|
"todayPT": 114, // 今日预填写工单总数
|
||||||
"total": 514
|
"totalPT": 514, // 所有预填写工单
|
||||||
|
"oreo": 93, // 加入 Oreo 工单数
|
||||||
|
"report": 8, // 今日反馈数
|
||||||
|
"allSuccess": 1919, // 所有成功工单(包括 Oreo 内)
|
||||||
|
"all": 2024 // 所有工单(包括 Oreo 内)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 当前值班信息 `GET /admin/duty/current`
|
## 主席团页面
|
||||||
|
|
||||||
|
### 值班信息管理
|
||||||
|
|
||||||
|
#### 当前值班信息 `GET /admin/duty/current`
|
||||||
|
|
||||||
|
##### 正常值班
|
||||||
|
|
||||||
data:
|
data:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"isInDuty": true,
|
"isNormalDuty": true, // 判断是否是正常值班时间
|
||||||
"currentDuty": "2",
|
"currentDuty": "1", // 判断是否正在值班 / 值哪一班 "1" | "2" | "3" => 正在值班,"off" => 暂停值班, "0" => 值班下班
|
||||||
"inDutyCnt": 3,
|
"inDutyCnt": 3,
|
||||||
"otherDutyTime": "",
|
"otherDutyTime": [], // 正常值班时为空
|
||||||
"offDutyReason": "",
|
"offDutyReason": "",
|
||||||
"place": "204",
|
"place": "204",
|
||||||
"dutyRecoverTime": ""
|
"dutyRecoverTime": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 主席团页面
|
##### 其他值班
|
||||||
|
|
||||||
|
data:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"isNormalDuty": false, // 判断是否是正常值班时间
|
||||||
|
"currentDuty": "others", // 判断是否正在值班 / 值哪一班 "others" => 正在值班,"off" => 暂停值班, "0" => 值班下班
|
||||||
|
"inDutyCnt": 3,
|
||||||
|
"otherDutyTime": [
|
||||||
|
{
|
||||||
|
"name": "第一班",
|
||||||
|
"place": "蓝田",
|
||||||
|
"range": ["2024-03-07T19:52:48.523303", "2024-03-07T19:52:48.523303"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "第二班",
|
||||||
|
"place": "蓝田",
|
||||||
|
"range": ["2024-03-07T19:52:48.523303", "2024-03-07T19:52:48.523303"]
|
||||||
|
},
|
||||||
|
...
|
||||||
|
],
|
||||||
|
"offDutyReason": "",
|
||||||
|
"place": "204",
|
||||||
|
"dutyRecoverTime": ""
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 更新当前值班情况 `POST /admin/duty/update`
|
||||||
|
|
||||||
|
无关项不返回,后端手动校验
|
||||||
|
|
||||||
|
新增字段:dutyStatus "normal" => 正常值班 "others" => 特殊值班 "pause" => 暂停值班
|
||||||
|
|
||||||
|
request:
|
||||||
|
|
||||||
|
##### 正常值班
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dutyStatus": "normal",
|
||||||
|
"inDutyCnt": 3,
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 特殊值班
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dutyStatus": "others",
|
||||||
|
"inDutyCnt": 3,
|
||||||
|
"otherDutyTime": [
|
||||||
|
{
|
||||||
|
"name": "第一班",
|
||||||
|
"place": "蓝田",
|
||||||
|
"range": ["2024-03-07T19:52:48.523303", "2024-03-07T19:52:48.523303"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "第二班",
|
||||||
|
"place": "蓝田",
|
||||||
|
"range": ["2024-03-07T19:52:48.523303", "2024-03-07T19:52:48.523303"]
|
||||||
|
},
|
||||||
|
...
|
||||||
|
],
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
##### 暂停值班
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dutyStatus": "pause",
|
||||||
|
"offDutyReason": "期中考试周",
|
||||||
|
"dutyRecoverTime": "2024-03-07T19:52:48.523303",
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
### 值班表管理
|
### 值班表管理
|
||||||
|
|
||||||
@@ -93,7 +250,6 @@ request:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
#### 更新值班表 `POST /admin/sheet/update`
|
#### 更新值班表 `POST /admin/sheet/update`
|
||||||
|
|
||||||
request:
|
request:
|
||||||
@@ -180,63 +336,157 @@ data:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 特殊值班
|
### 反馈管理
|
||||||
|
|
||||||
#### 获取当前值班情况 `GET /admin/duty/current`
|
#### 获取反馈列表 `GET /admin/reportlist`
|
||||||
|
|
||||||
|
request:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"current": 1,
|
||||||
|
"pageSize": 10,
|
||||||
|
"name?": "西西弗", // 如果不筛选则不出现在 request-body 内
|
||||||
|
"phone?": "18888888888" // 如果不筛选则不出现在 request-body 内
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
data:
|
data:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"isInDuty": true,
|
"page": 1,
|
||||||
"currentDuty": "2",
|
"total": 24,
|
||||||
"inDutyCnt": 3,
|
"success": true,
|
||||||
"otherDutyTime": "",
|
"data": [
|
||||||
"offDutyReason": "",
|
{
|
||||||
"place": "",
|
"name": "晓洋",
|
||||||
"dutyRecoverTime": "",
|
"phone": "13333333333",
|
||||||
},
|
"time": "2024-03-07T17:52:48.523303",
|
||||||
|
"text": "你们为什么修不好天选3的PD充电?"
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
]
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 更新当前值班情况 `POST /admin/duty/update`
|
### 日志页面
|
||||||
|
|
||||||
无关项返回空字符串,后端手动校验
|
#### 获取日志列表 `GET /admin/loglist`
|
||||||
|
|
||||||
request:
|
request:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"isInDuty": true,
|
"current": 1,
|
||||||
"currentDuty": "2",
|
"pageSize": 10,
|
||||||
"inDutyCnt": 3,
|
"op?": "晓洋", // 如果不筛选则不出现在 request-body 内
|
||||||
"otherDutyTime": "",
|
"operation": "all" | "add" | "delete" | "change", // all 之外的选项用于筛选
|
||||||
"offDutyReason": "",
|
"target": "all" | "dutyTable" | "dutyInfo" | "conclusion" // all 之外的选项用于筛选
|
||||||
"place": "204",
|
}
|
||||||
"dutyRecoverTime": "",
|
|
||||||
},
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 日志页面
|
|
||||||
|
|
||||||
### 获取日志列表 `GET /admin/loglist`
|
|
||||||
|
|
||||||
data:
|
data:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"op": "宇航员",
|
"page": 1,
|
||||||
"time": "2024-03-07T19:52:48.523303",
|
"total": 24,
|
||||||
"operation": 2,
|
"success": true,
|
||||||
"target": 1,
|
"data": [
|
||||||
"prev": {
|
{
|
||||||
"id": 114,
|
"op": "宇航员",
|
||||||
"info": "马嘉祺"
|
"avatar": "https://...",
|
||||||
},
|
"time": "2024-03-07T19:52:48.523303",
|
||||||
"curr": {
|
"operation": 2,
|
||||||
"id": 514,
|
"target": 1,
|
||||||
"info": "何君琳"
|
"prev": {
|
||||||
}
|
"id": 114,
|
||||||
},
|
"info": "马嘉祺"
|
||||||
|
},
|
||||||
|
"curr": {
|
||||||
|
"id": 514,
|
||||||
|
"info": "何君琳"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
]
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
前端显示的信息:"{op} 于 {time} {operation} 了 {target}:{prev.info} -> {curr.info}"
|
前端显示的信息:"{op} 于 {time} {operation} 了 {target}:{prev.info} -> {curr.info}"
|
||||||
|
|
||||||
|
### 值班总结管理页面
|
||||||
|
|
||||||
|
#### 获取值班总结列表 `GET /admin/conclusionlist`
|
||||||
|
|
||||||
|
request:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"current": 1,
|
||||||
|
"pageSize": 10,
|
||||||
|
"op?": "晓洋", // 如果不筛选则不出现在 request-body 内
|
||||||
|
"week": "all" | "mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun", // all 之外的选项用于筛选
|
||||||
|
"shift": "all" | "first" | "second" | "third" // all 之外的选项用于筛选
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
data:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"page": 1,
|
||||||
|
"total": 24,
|
||||||
|
"success": true,
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://...",
|
||||||
|
"time": "2024-03-07T19:52:48.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "3",
|
||||||
|
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 请假管理页面
|
||||||
|
|
||||||
|
#### 获取请假列表 `GET /leader/askleavelist`
|
||||||
|
|
||||||
|
request:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"current": 1,
|
||||||
|
"pageSize": 10,
|
||||||
|
"member?": "晓洋", // 如果不筛选则不出现在 request-body 内
|
||||||
|
"week": "all" | "mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun", // all 之外的选项用于筛选
|
||||||
|
"shift": "all" | "first" | "second" | "third" // all 之外的选项用于筛选
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
data:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"page": 1,
|
||||||
|
"total": 24,
|
||||||
|
"success": true,
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"member": "宇航员",
|
||||||
|
"avatar": "https://...",
|
||||||
|
"time": "2024-03-07T19:52:48.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "3",
|
||||||
|
"reason": "家庭原因",
|
||||||
|
"substitute": "晓洋" // 未找人代班则为空
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"member": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:48.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "3",
|
||||||
|
"reason": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班",
|
||||||
|
"substitute": "晓洋"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"member": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:49.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "1",
|
||||||
|
"reason": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班",
|
||||||
|
"substitute": "晓洋"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"member": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:50.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "2",
|
||||||
|
"reason": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班",
|
||||||
|
"substitute": "晓洋"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"member": "晓洋",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:51.523303",
|
||||||
|
"week": "7",
|
||||||
|
"shift": "3",
|
||||||
|
"reason": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班",
|
||||||
|
"substitute": "晓洋"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"member": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:52.523303",
|
||||||
|
"week": "7",
|
||||||
|
"shift": "1",
|
||||||
|
"reason": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班",
|
||||||
|
"substitute": "晓洋"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"member": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:53.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "3",
|
||||||
|
"reason": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班",
|
||||||
|
"substitute": "晓洋"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"member": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:54.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "3",
|
||||||
|
"reason": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班",
|
||||||
|
"substitute": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"member": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:55.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "3",
|
||||||
|
"reason": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班",
|
||||||
|
"substitute": "晓洋"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"member": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:56.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "3",
|
||||||
|
"reason": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班",
|
||||||
|
"substitute": "晓洋"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"member": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:57.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "3",
|
||||||
|
"reason": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班",
|
||||||
|
"substitute": "晓洋"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:48.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "3",
|
||||||
|
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:49.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "1",
|
||||||
|
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:50.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "2",
|
||||||
|
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "晓洋",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:51.523303",
|
||||||
|
"week": "7",
|
||||||
|
"shift": "3",
|
||||||
|
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:52.523303",
|
||||||
|
"week": "7",
|
||||||
|
"shift": "1",
|
||||||
|
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:53.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "3",
|
||||||
|
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:54.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "3",
|
||||||
|
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:55.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "3",
|
||||||
|
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:56.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "3",
|
||||||
|
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:57.523303",
|
||||||
|
"week": "2",
|
||||||
|
"shift": "3",
|
||||||
|
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:48.523303",
|
||||||
|
"operation": 2,
|
||||||
|
"target": 1,
|
||||||
|
"prev": {
|
||||||
|
"id": 114,
|
||||||
|
"info": "马嘉祺"
|
||||||
|
},
|
||||||
|
"curr": {
|
||||||
|
"id": 514,
|
||||||
|
"info": "何君琳"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:49.523303",
|
||||||
|
"operation": 2,
|
||||||
|
"target": 1,
|
||||||
|
"prev": {
|
||||||
|
"id": 114,
|
||||||
|
"info": "马嘉祺"
|
||||||
|
},
|
||||||
|
"curr": {
|
||||||
|
"id": 514,
|
||||||
|
"info": "何君琳"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:50.523303",
|
||||||
|
"operation": 2,
|
||||||
|
"target": 1,
|
||||||
|
"prev": {
|
||||||
|
"id": 114,
|
||||||
|
"info": "马嘉祺"
|
||||||
|
},
|
||||||
|
"curr": {
|
||||||
|
"id": 514,
|
||||||
|
"info": "何君琳"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "晓洋",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:51.523303",
|
||||||
|
"operation": 2,
|
||||||
|
"target": 1,
|
||||||
|
"prev": {
|
||||||
|
"id": 114,
|
||||||
|
"info": "马嘉祺"
|
||||||
|
},
|
||||||
|
"curr": {
|
||||||
|
"id": 514,
|
||||||
|
"info": "何君琳"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:52.523303",
|
||||||
|
"operation": 2,
|
||||||
|
"target": 1,
|
||||||
|
"prev": {
|
||||||
|
"id": 114,
|
||||||
|
"info": "马嘉祺"
|
||||||
|
},
|
||||||
|
"curr": {
|
||||||
|
"id": 514,
|
||||||
|
"info": "何君琳"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:53.523303",
|
||||||
|
"operation": 2,
|
||||||
|
"target": 1,
|
||||||
|
"prev": {
|
||||||
|
"id": 114,
|
||||||
|
"info": "马嘉祺"
|
||||||
|
},
|
||||||
|
"curr": {
|
||||||
|
"id": 514,
|
||||||
|
"info": "何君琳"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:54.523303",
|
||||||
|
"operation": 2,
|
||||||
|
"target": 1,
|
||||||
|
"prev": {
|
||||||
|
"id": 114,
|
||||||
|
"info": "马嘉祺"
|
||||||
|
},
|
||||||
|
"curr": {
|
||||||
|
"id": 514,
|
||||||
|
"info": "何君琳"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:55.523303",
|
||||||
|
"operation": 2,
|
||||||
|
"target": 1,
|
||||||
|
"prev": {
|
||||||
|
"id": 114,
|
||||||
|
"info": "马嘉祺"
|
||||||
|
},
|
||||||
|
"curr": {
|
||||||
|
"id": 514,
|
||||||
|
"info": "何君琳"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:56.523303",
|
||||||
|
"operation": 2,
|
||||||
|
"target": 1,
|
||||||
|
"prev": {
|
||||||
|
"id": 114,
|
||||||
|
"info": "马嘉祺"
|
||||||
|
},
|
||||||
|
"curr": {
|
||||||
|
"id": 514,
|
||||||
|
"info": "何君琳"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "宇航员",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"time": "2024-03-07T19:52:57.523303",
|
||||||
|
"operation": 2,
|
||||||
|
"target": 1,
|
||||||
|
"prev": {
|
||||||
|
"id": 114,
|
||||||
|
"info": "马嘉祺"
|
||||||
|
},
|
||||||
|
"curr": {
|
||||||
|
"id": 514,
|
||||||
|
"info": "何君琳"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "晓洋",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"phone": "13333333333",
|
||||||
|
"time": "2024-03-07T17:52:48.523303",
|
||||||
|
"text": "你们为什么修不好天选3的PD充电?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "晓洋1",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"phone": "13333333334",
|
||||||
|
"time": "2024-03-07T17:53:48.523303",
|
||||||
|
"text": "你们为什么修不好天选4的PD充电?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "晓洋2",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"phone": "13333333335",
|
||||||
|
"time": "2024-03-07T17:54:48.523303",
|
||||||
|
"text": "你们为什么修不好天选5的PD充电?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "晓洋3",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"phone": "13333333336",
|
||||||
|
"time": "2024-03-07T17:55:48.523303",
|
||||||
|
"text": "你们为什么修不好天选6的PD充电?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "晓洋4",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"phone": "13333333337",
|
||||||
|
"time": "2024-03-07T17:56:48.523303",
|
||||||
|
"text": "你们为什么修不好天选7的PD充电?我要气死了我要气死了我要气死了我要气死了我要气死了我要气死了我要气死了我要气死了我要气死了我要气死了我要气死了我要气死了我要气死了我要气死了我要气死了我要气死了我要气死了我要气死了我要气死了我要气死了我要气死了我要气死了"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "晓洋5",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"phone": "13333333338",
|
||||||
|
"time": "2024-03-07T17:57:48.523303",
|
||||||
|
"text": "你们为什么修不好天选8的PD充电?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "晓洋6",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"phone": "13333333339",
|
||||||
|
"time": "2024-03-07T17:58:48.523303",
|
||||||
|
"text": "你们为什么修不好天选9的PD充电?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "晓洋7",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"phone": "13333333330",
|
||||||
|
"time": "2024-03-07T17:59:48.523303",
|
||||||
|
"text": "你们为什么修不好天选0的PD充电?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "晓洋8",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"phone": "13333333331",
|
||||||
|
"time": "2024-03-07T18:52:48.523303",
|
||||||
|
"text": "你们为什么修不好天选1的PD充电?"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "晓洋9",
|
||||||
|
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
|
||||||
|
"phone": "13333333332",
|
||||||
|
"time": "2024-03-07T19:52:48.523303",
|
||||||
|
"text": "你们为什么修不好天选2的PD充电?"
|
||||||
|
}
|
||||||
|
]
|
||||||
+73
-43
@@ -1,12 +1,20 @@
|
|||||||
import sheet from './sheet.json'
|
import askleavelist from './askleavelist.json';
|
||||||
import sheetlist from './sheetlist.json'
|
import conclusionlist from './conclusionlist.json';
|
||||||
|
import loglist from './loglist.json';
|
||||||
|
import reportlist from './reportlist.json';
|
||||||
|
import sheet from './sheet.json';
|
||||||
|
import sheetlist from './sheetlist.json';
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
'GET /admin/stats': {
|
'GET /stats': {
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
notInOreo: 114,
|
todayPT: 114, // 今日预填写工单总数
|
||||||
total: 514,
|
totalPT: 514, // 所有预填写工单
|
||||||
|
oreo: 93, // 加入 Oreo 工单数
|
||||||
|
report: 8, // 今日反馈数
|
||||||
|
allSuccess: 1919, // 所有成功工单(包括 Oreo 内)
|
||||||
|
all: 2024, // 所有工单(包括 Oreo 内)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'POST /admin/sheet/update': {
|
'POST /admin/sheet/update': {
|
||||||
@@ -28,13 +36,31 @@ module.exports = {
|
|||||||
'GET /admin/duty/current': {
|
'GET /admin/duty/current': {
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
isInDuty: true,
|
isNormalDuty: true,
|
||||||
currentDuty: '2',
|
currentDuty: 'off',
|
||||||
inDutyCnt: 3,
|
inDutyCnt: 3,
|
||||||
otherDutyTime: '',
|
otherDutyTime: [
|
||||||
offDutyReason: '',
|
{
|
||||||
place: '204',
|
id: 1,
|
||||||
dutyRecoverTime: '',
|
title: '第一班',
|
||||||
|
place: 'fsaf',
|
||||||
|
range: ['2024-03-07T19:52:48.523303', '2024-03-07T19:52:48.523303'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: '第二班',
|
||||||
|
place: 'fsdhhv',
|
||||||
|
range: ['2024-03-07T19:52:48.523303', '2024-03-07T19:52:48.523303'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: '第三班',
|
||||||
|
place: 'fdshq',
|
||||||
|
range: ['2024-03-07T19:52:48.523303', '2024-03-07T19:52:48.523303'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
offDutyReason: '期中考试周',
|
||||||
|
dutyRecoverTime: '2024-03-07T19:52:48.523303',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'POST /admin/duty/update': {
|
'POST /admin/duty/update': {
|
||||||
@@ -42,48 +68,52 @@ module.exports = {
|
|||||||
data: {},
|
data: {},
|
||||||
},
|
},
|
||||||
'GET /admin/loglist': {
|
'GET /admin/loglist': {
|
||||||
|
success: true,
|
||||||
|
page: 1,
|
||||||
|
total: 24,
|
||||||
|
data: loglist,
|
||||||
|
},
|
||||||
|
'GET /leader/reportlist': {
|
||||||
|
success: true,
|
||||||
|
page: 1,
|
||||||
|
total: 24,
|
||||||
|
data: reportlist,
|
||||||
|
},
|
||||||
|
'GET /leader/conclusionlist': {
|
||||||
|
success: true,
|
||||||
|
page: 1,
|
||||||
|
total: 24,
|
||||||
|
data: conclusionlist,
|
||||||
|
},
|
||||||
|
'GET /leader/askleavelist': {
|
||||||
|
success: true,
|
||||||
|
page: 1,
|
||||||
|
total: 24,
|
||||||
|
data: askleavelist,
|
||||||
|
},
|
||||||
|
'POST /login': {
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
op: '宇航员',
|
token: 'token_test',
|
||||||
time: '2024-03-07T19:52:48.523303',
|
name: '亦可',
|
||||||
operation: 2,
|
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
|
||||||
target: 1,
|
role: 6,
|
||||||
prev: {
|
department: 1,
|
||||||
id: 114,
|
access: 'admin',
|
||||||
info: '马嘉祺',
|
|
||||||
},
|
|
||||||
curr: {
|
|
||||||
id: 514,
|
|
||||||
info: '何君琳',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
'POST /logout': {
|
||||||
|
success: true,
|
||||||
|
data: {},
|
||||||
|
},
|
||||||
'GET /api/currentUser': {
|
'GET /api/currentUser': {
|
||||||
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
name: 'Serati Ma',
|
name: 'Serati Ma',
|
||||||
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
|
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
|
||||||
userid: '00000001',
|
userid: '00000001',
|
||||||
email: 'antdesign@alipay.com',
|
|
||||||
signature: '海纳百川,有容乃大',
|
|
||||||
title: '交互专家',
|
|
||||||
group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',
|
group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',
|
||||||
tags: [
|
access: 'admin',
|
||||||
{ key: '0', label: '很有想法的' },
|
|
||||||
{ key: '1', label: '专注设计' },
|
|
||||||
{ key: '2', label: '辣~' },
|
|
||||||
{ key: '3', label: '大长腿' },
|
|
||||||
{ key: '4', label: '川妹子' },
|
|
||||||
{ key: '5', label: '海纳百川' },
|
|
||||||
],
|
|
||||||
notifyCount: 12,
|
|
||||||
unreadCount: 11,
|
|
||||||
country: 'China',
|
|
||||||
geographic: {
|
|
||||||
province: { label: '浙江省', key: '330000' },
|
|
||||||
city: { label: '杭州市', key: '330100' },
|
|
||||||
},
|
|
||||||
address: '西湖区工专路 77 号',
|
|
||||||
phone: '0752-268888888',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'POST /api/login/outLogin': { data: {}, success: true },
|
'POST /api/login/outLogin': { data: {}, success: true },
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
export default {
|
|
||||||
'/api/auth_routes': {
|
|
||||||
'/form/advanced-form': { authority: ['admin', 'user'] },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -42,76 +42,11 @@ export default {
|
|||||||
name: 'Serati Ma',
|
name: 'Serati Ma',
|
||||||
avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',
|
avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',
|
||||||
userid: '00000001',
|
userid: '00000001',
|
||||||
email: 'antdesign@alipay.com',
|
|
||||||
signature: '海纳百川,有容乃大',
|
|
||||||
title: '交互专家',
|
|
||||||
group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',
|
group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',
|
||||||
tags: [
|
|
||||||
{
|
|
||||||
key: '0',
|
|
||||||
label: '很有想法的',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '1',
|
|
||||||
label: '专注设计',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '2',
|
|
||||||
label: '辣~',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '3',
|
|
||||||
label: '大长腿',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '4',
|
|
||||||
label: '川妹子',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '5',
|
|
||||||
label: '海纳百川',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
notifyCount: 12,
|
|
||||||
unreadCount: 11,
|
|
||||||
country: 'China',
|
|
||||||
access: getAccess(),
|
access: getAccess(),
|
||||||
geographic: {
|
|
||||||
province: {
|
|
||||||
label: '浙江省',
|
|
||||||
key: '330000',
|
|
||||||
},
|
|
||||||
city: {
|
|
||||||
label: '杭州市',
|
|
||||||
key: '330100',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
address: '西湖区工专路 77 号',
|
|
||||||
phone: '0752-268888888',
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// GET POST 可省略
|
|
||||||
'GET /api/users': [
|
|
||||||
{
|
|
||||||
key: '1',
|
|
||||||
name: 'John Brown',
|
|
||||||
age: 32,
|
|
||||||
address: 'New York No. 1 Lake Park',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '2',
|
|
||||||
name: 'Jim Green',
|
|
||||||
age: 42,
|
|
||||||
address: 'London No. 1 Lake Park',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '3',
|
|
||||||
name: 'Joe Black',
|
|
||||||
age: 32,
|
|
||||||
address: 'Sidney No. 1 Lake Park',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'POST /api/login/account': async (req: Request, res: Response) => {
|
'POST /api/login/account': async (req: Request, res: Response) => {
|
||||||
const { password, username, type } = req.body;
|
const { password, username, type } = req.body;
|
||||||
await waitTime(2000);
|
await waitTime(2000);
|
||||||
@@ -154,9 +89,6 @@ export default {
|
|||||||
access = '';
|
access = '';
|
||||||
res.send({ data: {}, success: true });
|
res.send({ data: {}, success: true });
|
||||||
},
|
},
|
||||||
'POST /api/register': (req: Request, res: Response) => {
|
|
||||||
res.send({ status: 'ok', currentAuthority: 'user', success: true });
|
|
||||||
},
|
|
||||||
'GET /api/500': (req: Request, res: Response) => {
|
'GET /api/500': (req: Request, res: Response) => {
|
||||||
res.status(500).send({
|
res.status(500).send({
|
||||||
timestamp: 1513932555104,
|
timestamp: 1513932555104,
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
export default () => {
|
||||||
|
const [user, setUser] = useState({});
|
||||||
|
const [token, setToken] = useState('');
|
||||||
|
|
||||||
|
const setUserData = useCallback(
|
||||||
|
(name: string, avatar: string, group: number, title: number, access: string) => {
|
||||||
|
setUser({
|
||||||
|
name: name,
|
||||||
|
avatar: avatar,
|
||||||
|
group: group,
|
||||||
|
title: title,
|
||||||
|
access: access,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const setTokenStr = useCallback((token: string) => {
|
||||||
|
setToken(token);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { user, setUserData, token, setTokenStr };
|
||||||
|
};
|
||||||
@@ -46,6 +46,7 @@
|
|||||||
"not ie <= 10"
|
"not ie <= 10"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ant-design/charts": "^2.0.3",
|
||||||
"@ant-design/icons": "^4.8.1",
|
"@ant-design/icons": "^4.8.1",
|
||||||
"@ant-design/pro-components": "^2.7.0",
|
"@ant-design/pro-components": "^2.7.0",
|
||||||
"@umijs/route-utils": "^2.2.2",
|
"@umijs/route-utils": "^2.2.2",
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { API } from './services/typings';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see https://umijs.org/docs/max/access#access
|
* @see https://umijs.org/docs/max/access#access
|
||||||
* */
|
* */
|
||||||
@@ -5,5 +7,6 @@ export default function access(initialState: { currentUser?: API.CurrentUser } |
|
|||||||
const { currentUser } = initialState ?? {};
|
const { currentUser } = initialState ?? {};
|
||||||
return {
|
return {
|
||||||
canAdmin: currentUser && currentUser.access === 'admin',
|
canAdmin: currentUser && currentUser.access === 'admin',
|
||||||
|
canLeader: currentUser && (currentUser.access === 'leader' || currentUser.access === 'admin'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -1,5 +1,5 @@
|
|||||||
import { AvatarDropdown, AvatarName, PageFooter } from '@/components';
|
import { AvatarDropdown, AvatarName, PageFooter } from '@/components';
|
||||||
import { currentUser as queryCurrentUser } from '@/services/ant-design-pro/api';
|
import { currentUser as queryCurrentUser } from '@/services/api';
|
||||||
import { LinkOutlined } from '@ant-design/icons';
|
import { LinkOutlined } from '@ant-design/icons';
|
||||||
import type { Settings as LayoutSettings } from '@ant-design/pro-components';
|
import type { Settings as LayoutSettings } from '@ant-design/pro-components';
|
||||||
import { SettingDrawer } from '@ant-design/pro-components';
|
import { SettingDrawer } from '@ant-design/pro-components';
|
||||||
@@ -7,6 +7,7 @@ import type { RunTimeLayoutConfig } from '@umijs/max';
|
|||||||
import { Link, history } from '@umijs/max';
|
import { Link, history } from '@umijs/max';
|
||||||
import defaultSettings from '../config/defaultSettings';
|
import defaultSettings from '../config/defaultSettings';
|
||||||
import { errorConfig } from './requestErrorConfig';
|
import { errorConfig } from './requestErrorConfig';
|
||||||
|
import { API } from './services/typings';
|
||||||
const isDev = process.env.NODE_ENV === 'development';
|
const isDev = process.env.NODE_ENV === 'development';
|
||||||
const loginPath = '/user/login';
|
const loginPath = '/user/login';
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { Flex, Image, Typography } from 'antd';
|
||||||
|
|
||||||
|
const { Text, Link } = Typography;
|
||||||
|
|
||||||
|
const Brief: React.FC<{
|
||||||
|
avatar: string;
|
||||||
|
name: string;
|
||||||
|
nickname: string;
|
||||||
|
profile: string;
|
||||||
|
link?: string;
|
||||||
|
linktext?: string;
|
||||||
|
isSideStaff?: boolean;
|
||||||
|
}> = ({ avatar, name, nickname, profile, link, linktext, isSideStaff }) => {
|
||||||
|
return (
|
||||||
|
<div style={{ marginTop: '5vh', marginBottom: '5vh' }}>
|
||||||
|
<Flex gap="middle" align="center" justify="center" vertical={!isSideStaff}>
|
||||||
|
<Image width={isSideStaff ? 75 : 200} src={avatar} preview={false} />
|
||||||
|
<Flex gap="small" align="center" justify="center" vertical>
|
||||||
|
<Text style={{ fontSize: isSideStaff ? '18px' : '24px' }}>{name}</Text>
|
||||||
|
<Flex gap="small" align="center" justify="center" vertical={false}>
|
||||||
|
<Text type={'secondary'}>{nickname}</Text>
|
||||||
|
<Link href={link} target="_blank">
|
||||||
|
{linktext}
|
||||||
|
</Link>
|
||||||
|
</Flex>
|
||||||
|
<Text type={'secondary'}>{profile}</Text>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Brief;
|
||||||
@@ -21,7 +21,7 @@ const PageFooter: React.FC = () => {
|
|||||||
{
|
{
|
||||||
key: 'EVA Tea',
|
key: 'EVA Tea',
|
||||||
title: 'EVA Tea',
|
title: 'EVA Tea',
|
||||||
href: 'https://pro.ant.design',
|
href: 'https://a.zjueva.net',
|
||||||
blankTarget: true,
|
blankTarget: true,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { outLogin } from '@/services/ant-design-pro/api';
|
import { outLogin } from '@/services/api';
|
||||||
import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
|
import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
|
||||||
import { history, useModel } from '@umijs/max';
|
import { history, useModel } from '@umijs/max';
|
||||||
import { Spin } from 'antd';
|
import { Spin } from 'antd';
|
||||||
|
|||||||
@@ -1,14 +1,82 @@
|
|||||||
import { Todo } from '@/components';
|
import Brief from '@/components/Brief';
|
||||||
import { PageContainer } from '@ant-design/pro-components';
|
import { PageContainer, ProCard } from '@ant-design/pro-components';
|
||||||
import { Card } from 'antd';
|
import { Card } from 'antd';
|
||||||
|
import RcResizeObserver from 'rc-resize-observer';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import staffs, { Staff } from './staff';
|
||||||
|
|
||||||
const Logs: React.FC = () => {
|
const Logs: React.FC = () => {
|
||||||
|
const [responsive, setResponsive] = useState(false);
|
||||||
|
const sideMembers = staffs.sideStaffList.length;
|
||||||
|
|
||||||
|
const pushSideStaff = (sideStaff: JSX.Element[][], tempMembers: Staff[]) => {
|
||||||
|
sideStaff.push(
|
||||||
|
tempMembers.map((staff: Staff, idx) => (
|
||||||
|
<ProCard key={idx} style={{ whiteSpace: 'pre-line' }}>
|
||||||
|
<Brief
|
||||||
|
avatar={staff.avatar}
|
||||||
|
name={staff.name}
|
||||||
|
nickname={staff.nickname}
|
||||||
|
profile={staff.profile}
|
||||||
|
link={staff.link}
|
||||||
|
linktext={staff.linktext}
|
||||||
|
isSideStaff
|
||||||
|
/>
|
||||||
|
</ProCard>
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let sideStaff: JSX.Element[][] = [],
|
||||||
|
tempMembers: Staff[] = [], i = 0, j;
|
||||||
|
|
||||||
|
/// 当个数模 3 余 1 时,先放置 2 行,每行 2 个;
|
||||||
|
/// 当个数模 3 余 2 时,先放置 1 行,每行 2 个;
|
||||||
|
/// 其余按每行 3 个放置
|
||||||
|
for (j = 0; j < Math.round(sideMembers / 3); j++) {
|
||||||
|
tempMembers.push(staffs.sideStaffList[i++]);
|
||||||
|
tempMembers.push(staffs.sideStaffList[i++]);
|
||||||
|
if (j >= (3 - sideMembers % 3) % 3) {
|
||||||
|
tempMembers.push(staffs.sideStaffList[i++]);
|
||||||
|
}
|
||||||
|
pushSideStaff(sideStaff, tempMembers);
|
||||||
|
tempMembers = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const staffRenderer = (
|
||||||
|
<div>
|
||||||
|
<RcResizeObserver
|
||||||
|
key="resize-observer"
|
||||||
|
onResize={(offset) => {
|
||||||
|
setResponsive(offset.width < 596);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ProCard.Group direction={responsive ? 'column' : 'row'}>
|
||||||
|
{staffs.staffList.map((staff: Staff, idx) => (
|
||||||
|
<ProCard key={idx} style={{ whiteSpace: 'pre-line' }}>
|
||||||
|
<Brief
|
||||||
|
avatar={staff.avatar}
|
||||||
|
name={staff.name}
|
||||||
|
nickname={staff.nickname}
|
||||||
|
profile={staff.profile}
|
||||||
|
link={staff.link}
|
||||||
|
linktext={staff.linktext}
|
||||||
|
/>
|
||||||
|
</ProCard>
|
||||||
|
))}
|
||||||
|
</ProCard.Group>
|
||||||
|
{sideStaff.map((staffLine, idx) => (
|
||||||
|
<ProCard.Group direction={responsive ? 'column' : 'row'} key={idx}>
|
||||||
|
{staffLine}
|
||||||
|
</ProCard.Group>
|
||||||
|
))}
|
||||||
|
</RcResizeObserver>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<Card>
|
<Card>{staffRenderer}</Card>
|
||||||
<Todo />
|
|
||||||
</Card>
|
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default Logs;
|
export default Logs;
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
export interface Staff {
|
||||||
|
avatar: string;
|
||||||
|
name: string;
|
||||||
|
nickname: string;
|
||||||
|
profile: string;
|
||||||
|
link?: string;
|
||||||
|
linktext?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const staffList: Staff[] = [
|
||||||
|
{
|
||||||
|
avatar: 'https://blog.dawn-ocean.top/assets/img/avatar.jpg',
|
||||||
|
name: '马国洋',
|
||||||
|
nickname: 'Dawn_Ocean',
|
||||||
|
profile: 'Eta & Tea 总设计\n摸鱼与开发叠加态',
|
||||||
|
link: 'https://blog.dawn-ocean.top',
|
||||||
|
linktext: '我的博客',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: 'https://static.fracher21.top/head.jpg',
|
||||||
|
name: '杨鸿奕',
|
||||||
|
nickname: 'Fracher',
|
||||||
|
profile: 'Ate 总设计, 项目合作贡献者\nReact 真是太有意思了',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const sideStaffList: Staff[] = [
|
||||||
|
{
|
||||||
|
avatar: 'https://wiki.zjueva.net/images/avatars/eva_wiki_27_l.png',
|
||||||
|
name: '亦可',
|
||||||
|
nickname: 'Echo',
|
||||||
|
profile: 'Eta 系统总运维',
|
||||||
|
link: 'https://wiki.zjueva.net/%E7%94%A8%E6%88%B7:%E4%BA%A6%E5%8F%AF',
|
||||||
|
linktext: '我的主页',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
|
||||||
|
name: '泰斯特',
|
||||||
|
nickname: 'Tester',
|
||||||
|
profile: 'Eta 系统总测试',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export default { staffList, sideStaffList };
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { Todo } from '@/components';
|
|
||||||
import { PageContainer } from '@ant-design/pro-components';
|
|
||||||
import { Card } from 'antd';
|
|
||||||
|
|
||||||
const Logs: React.FC = () => {
|
|
||||||
return (
|
|
||||||
<PageContainer>
|
|
||||||
<Card>
|
|
||||||
<Todo />
|
|
||||||
</Card>
|
|
||||||
</PageContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default Logs;
|
|
||||||
@@ -0,0 +1,218 @@
|
|||||||
|
import { getDutyInfo } from '@/services/api';
|
||||||
|
import {
|
||||||
|
EditableProTable,
|
||||||
|
PageContainer,
|
||||||
|
ProColumns,
|
||||||
|
ProForm,
|
||||||
|
ProFormDateTimePicker,
|
||||||
|
ProFormDependency,
|
||||||
|
ProFormDigit,
|
||||||
|
ProFormRadio,
|
||||||
|
ProFormText,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { Card, message } from 'antd';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import request from 'umi-request';
|
||||||
|
import { Info } from './info';
|
||||||
|
|
||||||
|
type Shift = {
|
||||||
|
id: React.Key;
|
||||||
|
title: string;
|
||||||
|
place: string;
|
||||||
|
range: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns: ProColumns<Shift>[] = [
|
||||||
|
{
|
||||||
|
title: '值班名称',
|
||||||
|
dataIndex: 'title',
|
||||||
|
width: '15%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '值班地点',
|
||||||
|
dataIndex: 'place',
|
||||||
|
width: '15%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '值班时间',
|
||||||
|
dataIndex: 'range',
|
||||||
|
valueType: 'dateTimeRange',
|
||||||
|
width: '40%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
valueType: 'option',
|
||||||
|
width: '10%',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const DutyInfo: React.FC = () => {
|
||||||
|
const [dutyInfo, setDutyInfo] = useState<Info>();
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [messageApi, contextHolder] = message.useMessage();
|
||||||
|
|
||||||
|
const [editableKeys, setEditableRowKeys] = useState<React.Key[]>(() => []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getDutyInfo()
|
||||||
|
.then((res) => {
|
||||||
|
const data = res.data;
|
||||||
|
console.log(data);
|
||||||
|
setDutyInfo({
|
||||||
|
dutyStatus:
|
||||||
|
data.currentDuty === 'off' ? 'pause' : data.isNormalDuty ? 'others' : 'normal',
|
||||||
|
inDutyCnt: data.inDutyCnt,
|
||||||
|
otherDutyTime: data.otherDutyTime,
|
||||||
|
offDutyReason: data.offDutyReason,
|
||||||
|
dutyRecoverTime: data.dutyRecoverTime,
|
||||||
|
});
|
||||||
|
setEditableRowKeys(data.otherDutyTime.map((item) => item.id));
|
||||||
|
setLoading(false);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
messageApi.open({
|
||||||
|
type: 'error',
|
||||||
|
content: '获取失败!' + err,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageContainer>
|
||||||
|
{contextHolder}
|
||||||
|
<Card>
|
||||||
|
{!loading && (
|
||||||
|
<ProForm<Info>
|
||||||
|
submitter={{
|
||||||
|
resetButtonProps: { style: { display: 'none' } },
|
||||||
|
submitButtonProps: {
|
||||||
|
style: { width: '31%' },
|
||||||
|
},
|
||||||
|
render: (_, doms) => {
|
||||||
|
return <div style={{ textAlign: 'center' }}>{doms}</div>;
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onFinish={async (values) => {
|
||||||
|
setLoading(true);
|
||||||
|
request('/admin/duty/update', {
|
||||||
|
method: 'POST',
|
||||||
|
data: values,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
setLoading(false);
|
||||||
|
message.success('提交成功');
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
setLoading(false);
|
||||||
|
message.error('提交失败:' + err);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
initialValues={dutyInfo}
|
||||||
|
>
|
||||||
|
<div style={{ justifyContent: 'center', display: 'flex' }}>
|
||||||
|
<ProFormRadio.Group
|
||||||
|
name="dutyStatus"
|
||||||
|
label="值班状态"
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
label: '正常值班',
|
||||||
|
value: 'normal',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '特殊值班',
|
||||||
|
value: 'others',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '值班暂停',
|
||||||
|
value: 'pause',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{ justifyContent: 'center', display: 'flex' }}>
|
||||||
|
<ProFormDependency name={['dutyStatus']}>
|
||||||
|
{({ dutyStatus }) => {
|
||||||
|
if (dutyStatus === 'pause') {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<ProFormText
|
||||||
|
width="md"
|
||||||
|
name="offDutyReason"
|
||||||
|
label="值班暂停原因"
|
||||||
|
placeholder="请输入暂停值班的原因"
|
||||||
|
rules={[{ required: true }]}
|
||||||
|
/>
|
||||||
|
<ProFormDateTimePicker
|
||||||
|
name="dutyRecoverTime"
|
||||||
|
width="md"
|
||||||
|
label="值班恢复时间"
|
||||||
|
rules={[{ required: true }]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<ProFormDigit
|
||||||
|
label="当前值班人数"
|
||||||
|
name="inDutyCnt"
|
||||||
|
placeholder="请输入值班人数"
|
||||||
|
width="md"
|
||||||
|
min={0}
|
||||||
|
max={10}
|
||||||
|
rules={[{ required: true }]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</ProFormDependency>
|
||||||
|
</div>
|
||||||
|
<ProFormDependency name={['dutyStatus']}>
|
||||||
|
{({ dutyStatus }) => {
|
||||||
|
if (dutyStatus === 'others') {
|
||||||
|
return (
|
||||||
|
<div style={{ justifyContent: 'center', display: 'flex' }}>
|
||||||
|
<ProForm.Item
|
||||||
|
label="其他值班班次"
|
||||||
|
name="dataSource"
|
||||||
|
initialValue={dutyInfo?.otherDutyTime}
|
||||||
|
trigger="onValuesChange"
|
||||||
|
style={{ width: '80%' }}
|
||||||
|
rules={[{ required: true }]}
|
||||||
|
>
|
||||||
|
<EditableProTable<Shift>
|
||||||
|
rowKey="id"
|
||||||
|
toolBarRender={false}
|
||||||
|
columns={columns}
|
||||||
|
recordCreatorProps={{
|
||||||
|
newRecordType: 'dataSource',
|
||||||
|
position: 'bottom',
|
||||||
|
creatorButtonText: '新建一个值班班次',
|
||||||
|
style: { width: '100%', margin: 'auto', marginTop: '2%' },
|
||||||
|
record: () => ({
|
||||||
|
id: Date.now(),
|
||||||
|
title: '',
|
||||||
|
place: '',
|
||||||
|
range: [],
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
editable={{
|
||||||
|
type: 'multiple',
|
||||||
|
editableKeys,
|
||||||
|
onChange: setEditableRowKeys,
|
||||||
|
actionRender: (row, _, dom) => {
|
||||||
|
return [dom.delete];
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ProForm.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
</ProFormDependency>
|
||||||
|
</ProForm>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default DutyInfo;
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
export type Shift = {
|
||||||
|
id: React.Key;
|
||||||
|
title: string;
|
||||||
|
place: string;
|
||||||
|
range: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ServerInfo = {
|
||||||
|
isNormalDuty: boolean;
|
||||||
|
inDutyCnt: number;
|
||||||
|
currentDuty: string;
|
||||||
|
otherDutyTime: Shift[];
|
||||||
|
offDutyReason: string;
|
||||||
|
dutyRecoverTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Info = {
|
||||||
|
dutyStatus: string;
|
||||||
|
inDutyCnt: number;
|
||||||
|
otherDutyTime: Shift[];
|
||||||
|
offDutyReason: string;
|
||||||
|
dutyRecoverTime: string;
|
||||||
|
};
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import { PageContainer, ProList } from '@ant-design/pro-components';
|
||||||
|
import { Card } from 'antd';
|
||||||
|
import moment from 'moment';
|
||||||
|
import request from 'umi-request';
|
||||||
|
import transInfo, { LogsItem } from './logItem';
|
||||||
|
|
||||||
|
const Logs: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<PageContainer>
|
||||||
|
<Card>
|
||||||
|
<ProList<LogsItem>
|
||||||
|
search={{
|
||||||
|
filterType: 'light',
|
||||||
|
}}
|
||||||
|
rowKey="op"
|
||||||
|
headerTitle="日志列表"
|
||||||
|
request={async (params = {} as Record<string, any>) =>
|
||||||
|
request<{
|
||||||
|
data: LogsItem[];
|
||||||
|
}>('/admin/loglist', {
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pagination={{
|
||||||
|
pageSize: 10,
|
||||||
|
}}
|
||||||
|
metas={{
|
||||||
|
title: {
|
||||||
|
dataIndex: 'op',
|
||||||
|
title: '用户',
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
dataIndex: 'avatar',
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
dataIndex: ['operation', 'target', 'prev', 'curr'],
|
||||||
|
search: false,
|
||||||
|
render: (_, row) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{transInfo(row.operation, 'operation') +
|
||||||
|
'了' +
|
||||||
|
transInfo(row.target, 'target') +
|
||||||
|
':' +
|
||||||
|
row.prev.info +
|
||||||
|
' -> ' +
|
||||||
|
row.curr.info}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
dataIndex: 'time',
|
||||||
|
render: (_: React.ReactNode, row: LogsItem) => {
|
||||||
|
return (
|
||||||
|
<div style={{ marginLeft: '3vh' }}>
|
||||||
|
{moment(row.time).format('YYYY-M-D HH:mm:ss')}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
render: () => [
|
||||||
|
<a href="" target="_blank" rel="noopener noreferrer" key="view">
|
||||||
|
查看
|
||||||
|
</a>,
|
||||||
|
],
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
operation: {
|
||||||
|
// 自己扩展的字段,主要用于筛选,不在列表中显示
|
||||||
|
title: '操作',
|
||||||
|
valueType: 'select',
|
||||||
|
valueEnum: {
|
||||||
|
all: { text: '全部', status: 'Default' },
|
||||||
|
add: {
|
||||||
|
text: '增加',
|
||||||
|
status: '0',
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
text: '删除',
|
||||||
|
status: '1',
|
||||||
|
},
|
||||||
|
change: {
|
||||||
|
text: '更改',
|
||||||
|
status: '2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
target: {
|
||||||
|
// 自己扩展的字段,主要用于筛选,不在列表中显示
|
||||||
|
title: '操作对象',
|
||||||
|
valueType: 'select',
|
||||||
|
valueEnum: {
|
||||||
|
all: { text: '全部', status: 'Default' },
|
||||||
|
dutyTable: {
|
||||||
|
text: '值班表',
|
||||||
|
},
|
||||||
|
dutyInfo: {
|
||||||
|
text: '值班信息',
|
||||||
|
},
|
||||||
|
conclusion: {
|
||||||
|
text: '值班总结',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default Logs;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
const operationMap: { [key: number]: string } = {
|
||||||
|
0: '增加',
|
||||||
|
1: '删除',
|
||||||
|
2: '更改',
|
||||||
|
};
|
||||||
|
|
||||||
|
const targetMap: { [key: number]: string } = {
|
||||||
|
0: '值班表',
|
||||||
|
1: '值班信息',
|
||||||
|
2: '值班总结',
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LogsItem = {
|
||||||
|
op: string;
|
||||||
|
time: string;
|
||||||
|
operation: number;
|
||||||
|
target: number;
|
||||||
|
prev: {
|
||||||
|
id: number;
|
||||||
|
info: string;
|
||||||
|
};
|
||||||
|
curr: {
|
||||||
|
id: number;
|
||||||
|
info: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const transInfo = (num: number, type: string) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'operation':
|
||||||
|
return operationMap[num];
|
||||||
|
case 'target':
|
||||||
|
return targetMap[num];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default transInfo;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export type AskLeaveItem = {
|
||||||
|
member: string;
|
||||||
|
time: string;
|
||||||
|
week: string;
|
||||||
|
shift: string;
|
||||||
|
reason: string;
|
||||||
|
substitute: string;
|
||||||
|
};
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
import { PageContainer, ProList } from '@ant-design/pro-components';
|
||||||
|
import { Card, Space, Tag } from 'antd';
|
||||||
|
import moment from 'moment';
|
||||||
|
import request from 'umi-request';
|
||||||
|
import { AskLeaveItem } from './askLeaveItem';
|
||||||
|
import transShift from '@/utils/common';
|
||||||
|
|
||||||
|
const Logs: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<PageContainer>
|
||||||
|
<Card>
|
||||||
|
<ProList<AskLeaveItem>
|
||||||
|
search={{
|
||||||
|
filterType: 'light',
|
||||||
|
}}
|
||||||
|
rowKey="op"
|
||||||
|
headerTitle="请假列表"
|
||||||
|
request={async (params = {} as Record<string, any>) =>
|
||||||
|
request<{
|
||||||
|
data: AskLeaveItem[];
|
||||||
|
}>('/leader/askleavelist', {
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pagination={{
|
||||||
|
pageSize: 10,
|
||||||
|
}}
|
||||||
|
metas={{
|
||||||
|
title: {
|
||||||
|
dataIndex: 'member',
|
||||||
|
title: '用户',
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
dataIndex: 'avatar',
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
subTitle: {
|
||||||
|
dataIndex: ['week', 'shift', 'substitute'],
|
||||||
|
render: (_, row) => {
|
||||||
|
return (
|
||||||
|
<Space size={0}>
|
||||||
|
<Tag color="blue" key={row.member}>
|
||||||
|
{transShift(row.week, row.shift)}
|
||||||
|
</Tag>
|
||||||
|
<Tag color={row.substitute ? 'green' : 'red'} key={row.member}>
|
||||||
|
{row.substitute ? '代班:' + row.substitute : '未找人代班'}
|
||||||
|
</Tag>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
title: '值班时间',
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
dataIndex: 'reason',
|
||||||
|
search: false,
|
||||||
|
render: (_, row) => {
|
||||||
|
return <div>{row.reason}</div>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
dataIndex: 'time',
|
||||||
|
render: (_: React.ReactNode, row: AskLeaveItem) => {
|
||||||
|
return (
|
||||||
|
<div style={{ marginLeft: '3vh' }}>
|
||||||
|
{moment(row.time).format('YYYY-M-D HH:mm:ss')}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
week: {
|
||||||
|
// 自己扩展的字段,主要用于筛选,不在列表中显示
|
||||||
|
title: '星期',
|
||||||
|
valueType: 'select',
|
||||||
|
valueEnum: {
|
||||||
|
all: { text: '全部', status: 'Default' },
|
||||||
|
mon: {
|
||||||
|
text: '周一',
|
||||||
|
},
|
||||||
|
tue: {
|
||||||
|
text: '周二',
|
||||||
|
},
|
||||||
|
wed: {
|
||||||
|
text: '周三',
|
||||||
|
},
|
||||||
|
thu: {
|
||||||
|
text: '周四',
|
||||||
|
},
|
||||||
|
fri: {
|
||||||
|
text: '周五',
|
||||||
|
},
|
||||||
|
sat: {
|
||||||
|
text: '周六',
|
||||||
|
},
|
||||||
|
sun: {
|
||||||
|
text: '周日',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
shift: {
|
||||||
|
// 自己扩展的字段,主要用于筛选,不在列表中显示
|
||||||
|
title: '值班',
|
||||||
|
valueType: 'select',
|
||||||
|
valueEnum: {
|
||||||
|
all: { text: '全部', status: 'Default' },
|
||||||
|
first: {
|
||||||
|
text: '第一班',
|
||||||
|
},
|
||||||
|
second: {
|
||||||
|
text: '第二班',
|
||||||
|
},
|
||||||
|
third: {
|
||||||
|
text: '第三班',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default Logs;
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { Todo } from '@/components';
|
|
||||||
import { PageContainer } from '@ant-design/pro-components';
|
|
||||||
import { Card } from 'antd';
|
|
||||||
|
|
||||||
const Logs: React.FC = () => {
|
|
||||||
return (
|
|
||||||
<PageContainer>
|
|
||||||
<Card>
|
|
||||||
<Todo />
|
|
||||||
</Card>
|
|
||||||
</PageContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default Logs;
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export type ConclusionItem = {
|
||||||
|
op: string;
|
||||||
|
time: string;
|
||||||
|
week: string;
|
||||||
|
shift: string;
|
||||||
|
detail: string;
|
||||||
|
};
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
import { PageContainer, ProList } from '@ant-design/pro-components';
|
||||||
|
import { Card, Space, Tag } from 'antd';
|
||||||
|
import moment from 'moment';
|
||||||
|
import request from 'umi-request';
|
||||||
|
import { ConclusionItem } from './conclusionItem';
|
||||||
|
import transShift from '@/utils/common';
|
||||||
|
|
||||||
|
const Logs: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<PageContainer>
|
||||||
|
<Card>
|
||||||
|
<ProList<ConclusionItem>
|
||||||
|
search={{
|
||||||
|
filterType: 'light',
|
||||||
|
}}
|
||||||
|
rowKey="op"
|
||||||
|
headerTitle="值班总结列表"
|
||||||
|
request={async (params = {} as Record<string, any>) =>
|
||||||
|
request<{
|
||||||
|
data: ConclusionItem[];
|
||||||
|
}>('/leader/conclusionlist', {
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pagination={{
|
||||||
|
pageSize: 10,
|
||||||
|
}}
|
||||||
|
metas={{
|
||||||
|
title: {
|
||||||
|
dataIndex: 'op',
|
||||||
|
title: '用户',
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
dataIndex: 'avatar',
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
subTitle: {
|
||||||
|
dataIndex: ['week', 'shift'],
|
||||||
|
render: (_, row) => {
|
||||||
|
return (
|
||||||
|
<Space size={0}>
|
||||||
|
<Tag color="blue" key={row.op}>
|
||||||
|
{transShift(row.week, row.shift)}
|
||||||
|
</Tag>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
title: '值班时间',
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
dataIndex: 'detail',
|
||||||
|
search: false,
|
||||||
|
render: (_, row) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{row.detail}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
dataIndex: 'time',
|
||||||
|
render: (_: React.ReactNode, row: ConclusionItem) => {
|
||||||
|
return (
|
||||||
|
<div style={{ marginLeft: '3vh' }}>
|
||||||
|
{moment(row.time).format('YYYY-M-D HH:mm:ss')}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
render: () => [
|
||||||
|
<a href="" target="_blank" rel="noopener noreferrer" key="view">
|
||||||
|
查看
|
||||||
|
</a>,
|
||||||
|
],
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
week: {
|
||||||
|
// 自己扩展的字段,主要用于筛选,不在列表中显示
|
||||||
|
title: '星期',
|
||||||
|
valueType: 'select',
|
||||||
|
valueEnum: {
|
||||||
|
all: { text: '全部', status: 'Default' },
|
||||||
|
mon: {
|
||||||
|
text: '周一',
|
||||||
|
},
|
||||||
|
tue: {
|
||||||
|
text: '周二',
|
||||||
|
},
|
||||||
|
wed: {
|
||||||
|
text: '周三',
|
||||||
|
},
|
||||||
|
thu: {
|
||||||
|
text: '周四',
|
||||||
|
},
|
||||||
|
fri: {
|
||||||
|
text: '周五',
|
||||||
|
},
|
||||||
|
sat: {
|
||||||
|
text: '周六',
|
||||||
|
},
|
||||||
|
sun: {
|
||||||
|
text: '周日',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
shift: {
|
||||||
|
// 自己扩展的字段,主要用于筛选,不在列表中显示
|
||||||
|
title: '值班',
|
||||||
|
valueType: 'select',
|
||||||
|
valueEnum: {
|
||||||
|
all: { text: '全部', status: 'Default' },
|
||||||
|
first: {
|
||||||
|
text: '第一班',
|
||||||
|
},
|
||||||
|
second: {
|
||||||
|
text: '第二班',
|
||||||
|
},
|
||||||
|
third: {
|
||||||
|
text: '第三班',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default Logs;
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import { PageContainer, ProList } from '@ant-design/pro-components';
|
||||||
|
import { Card, Space, Tag } from 'antd';
|
||||||
|
import moment from 'moment';
|
||||||
|
import request from 'umi-request';
|
||||||
|
import { ReportItem } from './reportItem';
|
||||||
|
|
||||||
|
const Logs: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<PageContainer>
|
||||||
|
<Card>
|
||||||
|
<ProList<ReportItem>
|
||||||
|
search={{
|
||||||
|
filterType: 'light',
|
||||||
|
}}
|
||||||
|
rowKey="name"
|
||||||
|
headerTitle="反馈列表"
|
||||||
|
request={async (params = {} as Record<string, any>) =>
|
||||||
|
request<{
|
||||||
|
data: ReportItem[];
|
||||||
|
}>('/leader/reportlist', {
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pagination={{
|
||||||
|
pageSize: 10,
|
||||||
|
}}
|
||||||
|
metas={{
|
||||||
|
title: {
|
||||||
|
dataIndex: 'name',
|
||||||
|
title: '用户',
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
dataIndex: 'avatar',
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
subTitle: {
|
||||||
|
dataIndex: 'phone',
|
||||||
|
render: (_, row) => {
|
||||||
|
return (
|
||||||
|
<Space size={0}>
|
||||||
|
<Tag color="blue" key={row.name}>
|
||||||
|
{row.phone}
|
||||||
|
</Tag>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
title: '联系方式',
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
dataIndex: 'text',
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
dataIndex: 'time',
|
||||||
|
render: (_: React.ReactNode, row: ReportItem) => {
|
||||||
|
return (
|
||||||
|
<div style={{ marginLeft: '3vh' }}>
|
||||||
|
{moment(row.time).format('YYYY-M-D HH:mm:ss')}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default Logs;
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export type ReportItem = {
|
||||||
|
name: string;
|
||||||
|
phone: string;
|
||||||
|
time: string;
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { Todo } from '@/components';
|
|
||||||
import { PageContainer } from '@ant-design/pro-components';
|
|
||||||
import { Card } from 'antd';
|
|
||||||
|
|
||||||
const Logs: React.FC = () => {
|
|
||||||
return (
|
|
||||||
<PageContainer>
|
|
||||||
<Card>
|
|
||||||
<Todo />
|
|
||||||
</Card>
|
|
||||||
</PageContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
export default Logs;
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,8 @@
|
|||||||
import Settings from '@/../config/defaultSettings';
|
import Settings from '@/../config/defaultSettings';
|
||||||
import oidcLogo from '@/assets/oidc-logo.svg';
|
import oidcLogo from '@/assets/oidc-logo.svg';
|
||||||
import { PageFooter } from '@/components';
|
import { PageFooter } from '@/components';
|
||||||
import { login } from '@/services/ant-design-pro/api';
|
import { login } from '@/services/api';
|
||||||
|
import { API } from '@/services/typings';
|
||||||
import { LockOutlined, UserOutlined } from '@ant-design/icons';
|
import { LockOutlined, UserOutlined } from '@ant-design/icons';
|
||||||
import { LoginForm, ProFormCheckbox, ProFormText } from '@ant-design/pro-components';
|
import { LoginForm, ProFormCheckbox, ProFormText } from '@ant-design/pro-components';
|
||||||
import { Helmet, history, useModel } from '@umijs/max';
|
import { Helmet, history, useModel } from '@umijs/max';
|
||||||
@@ -207,7 +208,6 @@ const Login: React.FC = () => {
|
|||||||
</ProFormCheckbox>
|
</ProFormCheckbox>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ marginBottom: '2vh' }}>
|
<div style={{ marginBottom: '2vh' }}>
|
||||||
{' '}
|
|
||||||
<Button
|
<Button
|
||||||
type="default"
|
type="default"
|
||||||
block
|
block
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
import { TestBrowser } from '@@/testBrowser';
|
|
||||||
import { act, fireEvent, render } from '@testing-library/react';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
import { startMock } from '@@/requestRecordMock';
|
|
||||||
|
|
||||||
const waitTime = (time: number = 100) => {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
resolve(true);
|
|
||||||
}, time);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let server: {
|
|
||||||
close: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('Login Page', () => {
|
|
||||||
beforeAll(async () => {
|
|
||||||
server = await startMock({
|
|
||||||
port: 8000,
|
|
||||||
scene: 'login',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
server?.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show login form', async () => {
|
|
||||||
const historyRef = React.createRef<any>();
|
|
||||||
const rootContainer = render(
|
|
||||||
<TestBrowser
|
|
||||||
historyRef={historyRef}
|
|
||||||
location={{
|
|
||||||
pathname: '/user/login',
|
|
||||||
}}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
await rootContainer.findAllByText('Ant Design');
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
historyRef.current?.push('/user/login');
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(rootContainer.baseElement?.querySelector('.ant-pro-form-login-desc')?.textContent).toBe(
|
|
||||||
'Ant Design is the most influential web design specification in Xihu district',
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(rootContainer.asFragment()).toMatchSnapshot();
|
|
||||||
|
|
||||||
rootContainer.unmount();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should login success', async () => {
|
|
||||||
const historyRef = React.createRef<any>();
|
|
||||||
const rootContainer = render(
|
|
||||||
<TestBrowser
|
|
||||||
historyRef={historyRef}
|
|
||||||
location={{
|
|
||||||
pathname: '/user/login',
|
|
||||||
}}
|
|
||||||
/>,
|
|
||||||
);
|
|
||||||
|
|
||||||
await rootContainer.findAllByText('Ant Design');
|
|
||||||
|
|
||||||
const userNameInput = await rootContainer.findByPlaceholderText('Username: admin or user');
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
fireEvent.change(userNameInput, { target: { value: 'admin' } });
|
|
||||||
});
|
|
||||||
|
|
||||||
const passwordInput = await rootContainer.findByPlaceholderText('Password: ant.design');
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
fireEvent.change(passwordInput, { target: { value: 'ant.design' } });
|
|
||||||
});
|
|
||||||
|
|
||||||
await (await rootContainer.findByText('Login')).click();
|
|
||||||
|
|
||||||
// 等待接口返回结果
|
|
||||||
await waitTime(5000);
|
|
||||||
|
|
||||||
await rootContainer.findAllByText('Ant Design Pro');
|
|
||||||
|
|
||||||
expect(rootContainer.asFragment()).toMatchSnapshot();
|
|
||||||
|
|
||||||
await waitTime(2000);
|
|
||||||
|
|
||||||
rootContainer.unmount();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
+124
-7
@@ -1,14 +1,131 @@
|
|||||||
import { Todo } from '@/components';
|
import { getStat } from '@/services/api';
|
||||||
import { PageContainer } from '@ant-design/pro-components';
|
import type { TinyRingConfig } from '@ant-design/charts';
|
||||||
import { Card } from 'antd';
|
import { Tiny } from '@ant-design/charts';
|
||||||
|
import { PageContainer, ProCard, StatisticCard } from '@ant-design/pro-components';
|
||||||
|
import { Card, Statistic, message } from 'antd';
|
||||||
|
import RcResizeObserver from 'rc-resize-observer';
|
||||||
|
import { useLayoutEffect, useState } from 'react';
|
||||||
|
import { Stat } from './stat';
|
||||||
|
|
||||||
|
const percentRing = (percent: number, frontColor: string): React.JSX.Element => {
|
||||||
|
const TinyRing = Tiny.Ring as (props: TinyRingConfig) => JSX.Element;
|
||||||
|
const config: TinyRingConfig = {
|
||||||
|
percent: percent / 100,
|
||||||
|
width: 120,
|
||||||
|
height: 120,
|
||||||
|
color: ['#E8EFF5', frontColor],
|
||||||
|
annotations: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
style: {
|
||||||
|
text: `${percent}%`,
|
||||||
|
x: '50%',
|
||||||
|
y: '50%',
|
||||||
|
textAlign: 'center',
|
||||||
|
fontSize: 16,
|
||||||
|
fontStyle: 'bold',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
return <TinyRing {...config} />;
|
||||||
|
};
|
||||||
|
|
||||||
const Logs: React.FC = () => {
|
const Logs: React.FC = () => {
|
||||||
|
const [responsive, setResponsive] = useState(false);
|
||||||
|
const [messageApi, contextHolder] = message.useMessage();
|
||||||
|
const [stat, setStat] = useState<Stat>({
|
||||||
|
todayPT: 0,
|
||||||
|
totalPT: 0,
|
||||||
|
oreo: 0,
|
||||||
|
report: 0,
|
||||||
|
allSuccess: 0,
|
||||||
|
all: 0,
|
||||||
|
});
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
getStat()
|
||||||
|
.then((res) => {
|
||||||
|
setStat(res.data);
|
||||||
|
setLoading(false);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
messageApi.open({
|
||||||
|
type: 'error',
|
||||||
|
content: '获取失败!' + err,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<Card>
|
{contextHolder}
|
||||||
<Todo />
|
{!loading && (
|
||||||
</Card>
|
<Card style={{ color: '#f5f5f5' }}>
|
||||||
|
<RcResizeObserver
|
||||||
|
key="resize-observer"
|
||||||
|
onResize={(offset) => {
|
||||||
|
setResponsive(offset.width < 596);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ProCard split={responsive ? 'horizontal' : 'vertical'}>
|
||||||
|
<ProCard colSpan="Auto">
|
||||||
|
<ProCard hoverable>左侧内容</ProCard>
|
||||||
|
</ProCard>
|
||||||
|
<ProCard colSpan="170px">
|
||||||
|
<StatisticCard
|
||||||
|
hoverable
|
||||||
|
style={{ marginTop: '0vh' }}
|
||||||
|
statistic={{
|
||||||
|
title: '今日预填写工单数',
|
||||||
|
value: stat.todayPT,
|
||||||
|
description: (
|
||||||
|
<Statistic
|
||||||
|
style={{ marginTop: '15vh', marginBottom: '8vh' }}
|
||||||
|
title="今日反馈数"
|
||||||
|
value={stat.report}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ProCard>
|
||||||
|
<ProCard colSpan="360px">
|
||||||
|
<StatisticCard.Group direction={responsive ? 'row' : 'column'}>
|
||||||
|
<StatisticCard
|
||||||
|
hoverable
|
||||||
|
chart={percentRing(
|
||||||
|
Math.round(((stat.oreo / stat.totalPT) * 1000) / 10),
|
||||||
|
'#00CC66',
|
||||||
|
)}
|
||||||
|
statistic={{
|
||||||
|
title: '加入 Oreo 工单数',
|
||||||
|
value: stat.oreo,
|
||||||
|
description: <Statistic title="预填写工单总数" value={stat.totalPT} />,
|
||||||
|
}}
|
||||||
|
chartPlacement="left"
|
||||||
|
/>
|
||||||
|
<StatisticCard
|
||||||
|
hoverable
|
||||||
|
style={{ marginTop: '3vh' }}
|
||||||
|
chart={percentRing(
|
||||||
|
Math.round((stat.allSuccess / stat.all) * 1000) / 10,
|
||||||
|
'#66AFF4',
|
||||||
|
)}
|
||||||
|
statistic={{
|
||||||
|
title: '维修成功次数',
|
||||||
|
value: stat.allSuccess,
|
||||||
|
description: <Statistic title="Oreo、ETA 总工单数" value={stat.all} />,
|
||||||
|
}}
|
||||||
|
chartPlacement="left"
|
||||||
|
/>
|
||||||
|
</StatisticCard.Group>
|
||||||
|
</ProCard>
|
||||||
|
</ProCard>
|
||||||
|
</RcResizeObserver>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default Logs;
|
export default Logs;
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export type Stat = {
|
||||||
|
todayPT: number;
|
||||||
|
totalPT: number;
|
||||||
|
oreo: number;
|
||||||
|
report: number;
|
||||||
|
allSuccess: number;
|
||||||
|
all: number;
|
||||||
|
};
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
// @ts-ignore
|
|
||||||
/* eslint-disable */
|
|
||||||
import { request } from '@umijs/max';
|
|
||||||
|
|
||||||
/** 获取当前的用户 GET /api/currentUser */
|
|
||||||
export async function currentUser(options?: { [key: string]: any }) {
|
|
||||||
return request<{
|
|
||||||
data: API.CurrentUser;
|
|
||||||
}>('/api/currentUser', {
|
|
||||||
method: 'GET',
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 退出登录接口 POST /api/login/outLogin */
|
|
||||||
export async function outLogin(options?: { [key: string]: any }) {
|
|
||||||
return request<Record<string, any>>('/api/login/outLogin', {
|
|
||||||
method: 'POST',
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 登录接口 POST /api/login/account */
|
|
||||||
export async function login(body: API.LoginParams, options?: { [key: string]: any }) {
|
|
||||||
return request<API.LoginResult>('/api/login/account', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
data: body,
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 此处后端没有提供注释 GET /api/notices */
|
|
||||||
export async function getNotices(options?: { [key: string]: any }) {
|
|
||||||
return request<API.NoticeIconList>('/api/notices', {
|
|
||||||
method: 'GET',
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 获取规则列表 GET /api/rule */
|
|
||||||
export async function rule(
|
|
||||||
params: {
|
|
||||||
// query
|
|
||||||
/** 当前的页码 */
|
|
||||||
current?: number;
|
|
||||||
/** 页面的容量 */
|
|
||||||
pageSize?: number;
|
|
||||||
},
|
|
||||||
options?: { [key: string]: any },
|
|
||||||
) {
|
|
||||||
return request<API.RuleList>('/api/rule', {
|
|
||||||
method: 'GET',
|
|
||||||
params: {
|
|
||||||
...params,
|
|
||||||
},
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 更新规则 PUT /api/rule */
|
|
||||||
export async function updateRule(options?: { [key: string]: any }) {
|
|
||||||
return request<API.RuleListItem>('/api/rule', {
|
|
||||||
method: 'POST',
|
|
||||||
data: {
|
|
||||||
method: 'update',
|
|
||||||
...(options || {}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 新建规则 POST /api/rule */
|
|
||||||
export async function addRule(options?: { [key: string]: any }) {
|
|
||||||
return request<API.RuleListItem>('/api/rule', {
|
|
||||||
method: 'POST',
|
|
||||||
data: {
|
|
||||||
method: 'post',
|
|
||||||
...(options || {}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 删除规则 DELETE /api/rule */
|
|
||||||
export async function removeRule(options?: { [key: string]: any }) {
|
|
||||||
return request<Record<string, any>>('/api/rule', {
|
|
||||||
method: 'POST',
|
|
||||||
data: {
|
|
||||||
method: 'delete',
|
|
||||||
...(options || {}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
// @ts-ignore
|
|
||||||
/* eslint-disable */
|
|
||||||
import { request } from '@umijs/max';
|
|
||||||
|
|
||||||
/** 发送验证码 POST /api/login/captcha */
|
|
||||||
export async function getFakeCaptcha(
|
|
||||||
params: {
|
|
||||||
// query
|
|
||||||
/** 手机号 */
|
|
||||||
phone?: string;
|
|
||||||
},
|
|
||||||
options?: { [key: string]: any },
|
|
||||||
) {
|
|
||||||
return request<API.FakeCaptcha>('/api/login/captcha', {
|
|
||||||
method: 'GET',
|
|
||||||
params: {
|
|
||||||
...params,
|
|
||||||
},
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
-101
@@ -1,101 +0,0 @@
|
|||||||
// @ts-ignore
|
|
||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
declare namespace API {
|
|
||||||
type CurrentUser = {
|
|
||||||
name?: string;
|
|
||||||
avatar?: string;
|
|
||||||
userid?: string;
|
|
||||||
email?: string;
|
|
||||||
signature?: string;
|
|
||||||
title?: string;
|
|
||||||
group?: string;
|
|
||||||
tags?: { key?: string; label?: string }[];
|
|
||||||
notifyCount?: number;
|
|
||||||
unreadCount?: number;
|
|
||||||
country?: string;
|
|
||||||
access?: string;
|
|
||||||
geographic?: {
|
|
||||||
province?: { label?: string; key?: string };
|
|
||||||
city?: { label?: string; key?: string };
|
|
||||||
};
|
|
||||||
address?: string;
|
|
||||||
phone?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type LoginResult = {
|
|
||||||
status?: string;
|
|
||||||
type?: string;
|
|
||||||
currentAuthority?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type PageParams = {
|
|
||||||
current?: number;
|
|
||||||
pageSize?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type RuleListItem = {
|
|
||||||
key?: number;
|
|
||||||
disabled?: boolean;
|
|
||||||
href?: string;
|
|
||||||
avatar?: string;
|
|
||||||
name?: string;
|
|
||||||
owner?: string;
|
|
||||||
desc?: string;
|
|
||||||
callNo?: number;
|
|
||||||
status?: number;
|
|
||||||
updatedAt?: string;
|
|
||||||
createdAt?: string;
|
|
||||||
progress?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type RuleList = {
|
|
||||||
data?: RuleListItem[];
|
|
||||||
/** 列表的内容总数 */
|
|
||||||
total?: number;
|
|
||||||
success?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type FakeCaptcha = {
|
|
||||||
code?: number;
|
|
||||||
status?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type LoginParams = {
|
|
||||||
username?: string;
|
|
||||||
password?: string;
|
|
||||||
autoLogin?: boolean;
|
|
||||||
type?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type ErrorResponse = {
|
|
||||||
/** 业务约定的错误码 */
|
|
||||||
errorCode: string;
|
|
||||||
/** 业务上的错误信息 */
|
|
||||||
errorMessage?: string;
|
|
||||||
/** 业务上的请求是否成功 */
|
|
||||||
success?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type NoticeIconList = {
|
|
||||||
data?: NoticeIconItem[];
|
|
||||||
/** 列表的内容总数 */
|
|
||||||
total?: number;
|
|
||||||
success?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type NoticeIconItemType = 'notification' | 'message' | 'event';
|
|
||||||
|
|
||||||
type NoticeIconItem = {
|
|
||||||
id?: string;
|
|
||||||
extra?: string;
|
|
||||||
key?: string;
|
|
||||||
read?: boolean;
|
|
||||||
avatar?: string;
|
|
||||||
title?: string;
|
|
||||||
status?: string;
|
|
||||||
datetime?: string;
|
|
||||||
description?: string;
|
|
||||||
type?: NoticeIconItemType;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
/* eslint-disable */
|
||||||
|
import { ServerInfo } from '@/pages/Admin/DutyInfo/info';
|
||||||
|
import { Stat } from '@/pages/Welcome/stat';
|
||||||
|
import { request } from '@umijs/max';
|
||||||
|
import { API } from './typings';
|
||||||
|
|
||||||
|
/** 获取当前的用户 GET /api/currentUser */
|
||||||
|
export async function currentUser(options?: { [key: string]: any }) {
|
||||||
|
return request<{
|
||||||
|
data: API.CurrentUser;
|
||||||
|
}>('/api/currentUser', {
|
||||||
|
method: 'GET',
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 退出登录接口 POST /api/login/outLogin */
|
||||||
|
export async function outLogin(options?: { [key: string]: any }) {
|
||||||
|
return request<Record<string, any>>('/api/login/outLogin', {
|
||||||
|
method: 'POST',
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 登录接口 POST /api/login/account */
|
||||||
|
export async function login(body: API.LoginParams, options?: { [key: string]: any }) {
|
||||||
|
return request<API.LoginResult>('/api/login/account', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
data: body,
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取首页信息 GET /stats */
|
||||||
|
export async function getStat(options?: { [key: string]: any }) {
|
||||||
|
return request<{
|
||||||
|
data: Stat;
|
||||||
|
}>('/stats', {
|
||||||
|
method: 'GET',
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取当前值班信息 GET /admin/duty/current */
|
||||||
|
export async function getDutyInfo(options?: { [key: string]: any }) {
|
||||||
|
return request<{
|
||||||
|
data: ServerInfo;
|
||||||
|
}>('/admin/duty/current', {
|
||||||
|
method: 'GET',
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -3,8 +3,6 @@
|
|||||||
// API 更新时间:
|
// API 更新时间:
|
||||||
// API 唯一标识:
|
// API 唯一标识:
|
||||||
import * as api from './api';
|
import * as api from './api';
|
||||||
import * as login from './login';
|
|
||||||
export default {
|
export default {
|
||||||
api,
|
api,
|
||||||
login,
|
|
||||||
};
|
};
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
// @ts-ignore
|
|
||||||
/* eslint-disable */
|
|
||||||
// API 更新时间:
|
|
||||||
// API 唯一标识:
|
|
||||||
import * as pet from './pet';
|
|
||||||
import * as store from './store';
|
|
||||||
import * as user from './user';
|
|
||||||
export default {
|
|
||||||
pet,
|
|
||||||
store,
|
|
||||||
user,
|
|
||||||
};
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
// @ts-ignore
|
|
||||||
/* eslint-disable */
|
|
||||||
import { request } from '@umijs/max';
|
|
||||||
|
|
||||||
/** Update an existing pet PUT /pet */
|
|
||||||
export async function updatePet(body: API.Pet, options?: { [key: string]: any }) {
|
|
||||||
return request<any>('/pet', {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
data: body,
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Add a new pet to the store POST /pet */
|
|
||||||
export async function addPet(body: API.Pet, options?: { [key: string]: any }) {
|
|
||||||
return request<any>('/pet', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
data: body,
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Find pet by ID Returns a single pet GET /pet/${param0} */
|
|
||||||
export async function getPetById(
|
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|
||||||
params: API.getPetByIdParams,
|
|
||||||
options?: { [key: string]: any },
|
|
||||||
) {
|
|
||||||
const { petId: param0, ...queryParams } = params;
|
|
||||||
return request<API.Pet>(`/pet/${param0}`, {
|
|
||||||
method: 'GET',
|
|
||||||
params: { ...queryParams },
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Updates a pet in the store with form data POST /pet/${param0} */
|
|
||||||
export async function updatePetWithForm(
|
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|
||||||
params: API.updatePetWithFormParams,
|
|
||||||
body: { name?: string; status?: string },
|
|
||||||
options?: { [key: string]: any },
|
|
||||||
) {
|
|
||||||
const { petId: param0, ...queryParams } = params;
|
|
||||||
const formData = new FormData();
|
|
||||||
|
|
||||||
Object.keys(body).forEach((ele) => {
|
|
||||||
const item = (body as any)[ele];
|
|
||||||
|
|
||||||
if (item !== undefined && item !== null) {
|
|
||||||
formData.append(
|
|
||||||
ele,
|
|
||||||
typeof item === 'object' && !(item instanceof File) ? JSON.stringify(item) : item,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return request<any>(`/pet/${param0}`, {
|
|
||||||
method: 'POST',
|
|
||||||
params: { ...queryParams },
|
|
||||||
data: formData,
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Deletes a pet DELETE /pet/${param0} */
|
|
||||||
export async function deletePet(
|
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|
||||||
params: API.deletePetParams & {
|
|
||||||
// header
|
|
||||||
api_key?: string;
|
|
||||||
},
|
|
||||||
options?: { [key: string]: any },
|
|
||||||
) {
|
|
||||||
const { petId: param0, ...queryParams } = params;
|
|
||||||
return request<any>(`/pet/${param0}`, {
|
|
||||||
method: 'DELETE',
|
|
||||||
headers: {},
|
|
||||||
params: { ...queryParams },
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** uploads an image POST /pet/${param0}/uploadImage */
|
|
||||||
export async function uploadFile(
|
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|
||||||
params: API.uploadFileParams,
|
|
||||||
body: { additionalMetadata?: string; file?: string },
|
|
||||||
file?: File,
|
|
||||||
options?: { [key: string]: any },
|
|
||||||
) {
|
|
||||||
const { petId: param0, ...queryParams } = params;
|
|
||||||
const formData = new FormData();
|
|
||||||
|
|
||||||
if (file) {
|
|
||||||
formData.append('file', file);
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.keys(body).forEach((ele) => {
|
|
||||||
const item = (body as any)[ele];
|
|
||||||
|
|
||||||
if (item !== undefined && item !== null) {
|
|
||||||
formData.append(
|
|
||||||
ele,
|
|
||||||
typeof item === 'object' && !(item instanceof File) ? JSON.stringify(item) : item,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return request<API.ApiResponse>(`/pet/${param0}/uploadImage`, {
|
|
||||||
method: 'POST',
|
|
||||||
params: { ...queryParams },
|
|
||||||
data: formData,
|
|
||||||
requestType: 'form',
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Finds Pets by status Multiple status values can be provided with comma separated strings GET /pet/findByStatus */
|
|
||||||
export async function findPetsByStatus(
|
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|
||||||
params: API.findPetsByStatusParams,
|
|
||||||
options?: { [key: string]: any },
|
|
||||||
) {
|
|
||||||
return request<API.Pet[]>('/pet/findByStatus', {
|
|
||||||
method: 'GET',
|
|
||||||
params: {
|
|
||||||
...params,
|
|
||||||
},
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Finds Pets by tags Muliple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. GET /pet/findByTags */
|
|
||||||
export async function findPetsByTags(
|
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|
||||||
params: API.findPetsByTagsParams,
|
|
||||||
options?: { [key: string]: any },
|
|
||||||
) {
|
|
||||||
return request<API.Pet[]>('/pet/findByTags', {
|
|
||||||
method: 'GET',
|
|
||||||
params: {
|
|
||||||
...params,
|
|
||||||
},
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
// @ts-ignore
|
|
||||||
/* eslint-disable */
|
|
||||||
import { request } from '@umijs/max';
|
|
||||||
|
|
||||||
/** Returns pet inventories by status Returns a map of status codes to quantities GET /store/inventory */
|
|
||||||
export async function getInventory(options?: { [key: string]: any }) {
|
|
||||||
return request<Record<string, any>>('/store/inventory', {
|
|
||||||
method: 'GET',
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Place an order for a pet POST /store/order */
|
|
||||||
export async function placeOrder(body: API.Order, options?: { [key: string]: any }) {
|
|
||||||
return request<API.Order>('/store/order', {
|
|
||||||
method: 'POST',
|
|
||||||
data: body,
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Find purchase order by ID For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions GET /store/order/${param0} */
|
|
||||||
export async function getOrderById(
|
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|
||||||
params: API.getOrderByIdParams,
|
|
||||||
options?: { [key: string]: any },
|
|
||||||
) {
|
|
||||||
const { orderId: param0, ...queryParams } = params;
|
|
||||||
return request<API.Order>(`/store/order/${param0}`, {
|
|
||||||
method: 'GET',
|
|
||||||
params: { ...queryParams },
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Delete purchase order by ID For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors DELETE /store/order/${param0} */
|
|
||||||
export async function deleteOrder(
|
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|
||||||
params: API.deleteOrderParams,
|
|
||||||
options?: { [key: string]: any },
|
|
||||||
) {
|
|
||||||
const { orderId: param0, ...queryParams } = params;
|
|
||||||
return request<any>(`/store/order/${param0}`, {
|
|
||||||
method: 'DELETE',
|
|
||||||
params: { ...queryParams },
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Vendored
-112
@@ -1,112 +0,0 @@
|
|||||||
declare namespace API {
|
|
||||||
type ApiResponse = {
|
|
||||||
code?: number;
|
|
||||||
type?: string;
|
|
||||||
message?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Category = {
|
|
||||||
id?: number;
|
|
||||||
name?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type deleteOrderParams = {
|
|
||||||
/** ID of the order that needs to be deleted */
|
|
||||||
orderId: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type deletePetParams = {
|
|
||||||
api_key?: string;
|
|
||||||
/** Pet id to delete */
|
|
||||||
petId: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type deleteUserParams = {
|
|
||||||
/** The name that needs to be deleted */
|
|
||||||
username: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type findPetsByStatusParams = {
|
|
||||||
/** Status values that need to be considered for filter */
|
|
||||||
status: ('available' | 'pending' | 'sold')[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type findPetsByTagsParams = {
|
|
||||||
/** Tags to filter by */
|
|
||||||
tags: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type getOrderByIdParams = {
|
|
||||||
/** ID of pet that needs to be fetched */
|
|
||||||
orderId: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type getPetByIdParams = {
|
|
||||||
/** ID of pet to return */
|
|
||||||
petId: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type getUserByNameParams = {
|
|
||||||
/** The name that needs to be fetched. Use user1 for testing. */
|
|
||||||
username: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type loginUserParams = {
|
|
||||||
/** The user name for login */
|
|
||||||
username: string;
|
|
||||||
/** The password for login in clear text */
|
|
||||||
password: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Order = {
|
|
||||||
id?: number;
|
|
||||||
petId?: number;
|
|
||||||
quantity?: number;
|
|
||||||
shipDate?: string;
|
|
||||||
/** Order Status */
|
|
||||||
status?: 'placed' | 'approved' | 'delivered';
|
|
||||||
complete?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Pet = {
|
|
||||||
id?: number;
|
|
||||||
category?: Category;
|
|
||||||
name: string;
|
|
||||||
photoUrls: string[];
|
|
||||||
tags?: Tag[];
|
|
||||||
/** pet status in the store */
|
|
||||||
status?: 'available' | 'pending' | 'sold';
|
|
||||||
};
|
|
||||||
|
|
||||||
type Tag = {
|
|
||||||
id?: number;
|
|
||||||
name?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type updatePetWithFormParams = {
|
|
||||||
/** ID of pet that needs to be updated */
|
|
||||||
petId: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type updateUserParams = {
|
|
||||||
/** name that need to be updated */
|
|
||||||
username: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type uploadFileParams = {
|
|
||||||
/** ID of pet to update */
|
|
||||||
petId: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type User = {
|
|
||||||
id?: number;
|
|
||||||
username?: string;
|
|
||||||
firstName?: string;
|
|
||||||
lastName?: string;
|
|
||||||
email?: string;
|
|
||||||
password?: string;
|
|
||||||
phone?: string;
|
|
||||||
/** User Status */
|
|
||||||
userStatus?: number;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
// @ts-ignore
|
|
||||||
/* eslint-disable */
|
|
||||||
import { request } from '@umijs/max';
|
|
||||||
|
|
||||||
/** Create user This can only be done by the logged in user. POST /user */
|
|
||||||
export async function createUser(body: API.User, options?: { [key: string]: any }) {
|
|
||||||
return request<any>('/user', {
|
|
||||||
method: 'POST',
|
|
||||||
data: body,
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get user by user name GET /user/${param0} */
|
|
||||||
export async function getUserByName(
|
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|
||||||
params: API.getUserByNameParams,
|
|
||||||
options?: { [key: string]: any },
|
|
||||||
) {
|
|
||||||
const { username: param0, ...queryParams } = params;
|
|
||||||
return request<API.User>(`/user/${param0}`, {
|
|
||||||
method: 'GET',
|
|
||||||
params: { ...queryParams },
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Updated user This can only be done by the logged in user. PUT /user/${param0} */
|
|
||||||
export async function updateUser(
|
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|
||||||
params: API.updateUserParams,
|
|
||||||
body: API.User,
|
|
||||||
options?: { [key: string]: any },
|
|
||||||
) {
|
|
||||||
const { username: param0, ...queryParams } = params;
|
|
||||||
return request<any>(`/user/${param0}`, {
|
|
||||||
method: 'PUT',
|
|
||||||
params: { ...queryParams },
|
|
||||||
data: body,
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Delete user This can only be done by the logged in user. DELETE /user/${param0} */
|
|
||||||
export async function deleteUser(
|
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|
||||||
params: API.deleteUserParams,
|
|
||||||
options?: { [key: string]: any },
|
|
||||||
) {
|
|
||||||
const { username: param0, ...queryParams } = params;
|
|
||||||
return request<any>(`/user/${param0}`, {
|
|
||||||
method: 'DELETE',
|
|
||||||
params: { ...queryParams },
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Creates list of users with given input array POST /user/createWithArray */
|
|
||||||
export async function createUsersWithArrayInput(
|
|
||||||
body: API.User[],
|
|
||||||
options?: { [key: string]: any },
|
|
||||||
) {
|
|
||||||
return request<any>('/user/createWithArray', {
|
|
||||||
method: 'POST',
|
|
||||||
data: body,
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Creates list of users with given input array POST /user/createWithList */
|
|
||||||
export async function createUsersWithListInput(body: API.User[], options?: { [key: string]: any }) {
|
|
||||||
return request<any>('/user/createWithList', {
|
|
||||||
method: 'POST',
|
|
||||||
data: body,
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Logs user into the system GET /user/login */
|
|
||||||
export async function loginUser(
|
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
|
||||||
params: API.loginUserParams,
|
|
||||||
options?: { [key: string]: any },
|
|
||||||
) {
|
|
||||||
return request<string>('/user/login', {
|
|
||||||
method: 'GET',
|
|
||||||
params: {
|
|
||||||
...params,
|
|
||||||
},
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Logs out current logged in user session GET /user/logout */
|
|
||||||
export async function logoutUser(options?: { [key: string]: any }) {
|
|
||||||
return request<any>('/user/logout', {
|
|
||||||
method: 'GET',
|
|
||||||
...(options || {}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Vendored
+31
@@ -0,0 +1,31 @@
|
|||||||
|
import { LogsItem } from '@/pages/Admin/Logs/logItem';
|
||||||
|
|
||||||
|
declare namespace API {
|
||||||
|
type CurrentUser = {
|
||||||
|
name?: string;
|
||||||
|
avatar?: string;
|
||||||
|
userid?: string;
|
||||||
|
group?: string;
|
||||||
|
access?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type LoginResult = {
|
||||||
|
status?: string;
|
||||||
|
type?: string;
|
||||||
|
currentAuthority?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type LoginParams = {
|
||||||
|
username?: string;
|
||||||
|
password?: string;
|
||||||
|
autoLogin?: boolean;
|
||||||
|
type?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type LogList = {
|
||||||
|
pageIndex: number;
|
||||||
|
pageCount: number;
|
||||||
|
size: number;
|
||||||
|
data: LogsItem[];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
const weekMap: { [key: string]: string } = {
|
||||||
|
'1': '周一',
|
||||||
|
'2': '周二',
|
||||||
|
'3': '周三',
|
||||||
|
'4': '周四',
|
||||||
|
'5': '周五',
|
||||||
|
'6': '周六',
|
||||||
|
'7': '周日',
|
||||||
|
};
|
||||||
|
|
||||||
|
const shiftMap: { [key: string]: string } = {
|
||||||
|
'1': '第一班',
|
||||||
|
'2': '第二班',
|
||||||
|
'3': '第三班',
|
||||||
|
};
|
||||||
|
|
||||||
|
const transShift = (week: string, shift: string) => {
|
||||||
|
if (week === '7' && shift === '3') {
|
||||||
|
return '其他值班时间';
|
||||||
|
} else {
|
||||||
|
return weekMap[week] + shiftMap[shift];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default transShift;
|
||||||
Reference in New Issue
Block a user