Compare commits

20 Commits
Author SHA1 Message Date
Dawn1Ocean d74cbcb229 feat: add edit dutytable page 2024-09-19 13:59:56 +08:00
Dawn1Ocean f023e4b64a feat: dutytable page 2024-09-18 13:58:08 +08:00
Dawn1Ocean 5158fd19d9 chore: add avatar in About 2024-09-18 10:35:04 +08:00
Dawn1Ocean 1caeba1225 Merge branch 'mgy' of https://git.zjueva.net/Dawn_Ocean/EVA-Tea into mgy 2024-05-09 14:56:56 +08:00
Dawn1Ocean 44ce17d43d change doc & mock 2024-05-09 14:54:21 +08:00
Dawn1Ocean 31fca48feb feat: required check in dutyinfo; change service in dutyinfo 2024-05-08 19:01:58 +08:00
Dawn1Ocean 8f062b0e8c add dutyinfo page 2024-05-07 19:49:48 +08:00
Dawn1Ocean 1ff36f366d move week&shift process to src/utils/common 2024-05-07 12:21:51 +08:00
Dawn1Ocean edcd5dcbb7 add askleave page 2024-05-07 12:03:00 +08:00
Dawn1Ocean dd8c7e0b92 move report & conclusion from admin to leader 2024-05-07 11:26:14 +08:00
Dawn1Ocean 56452da055 add sideStaff display in about page 2024-05-07 11:14:28 +08:00
Dawn1Ocean 5fa85f7b77 fix: unstopping request bug in useEffect & useLayoutEffect 2024-04-21 23:02:18 +08:00
Dawn1Ocean a39cef6312 fix: change dutyinfo api 2024-04-21 22:59:19 +08:00
Dawn_Ocean 9fc19a3ebd refactor: request in DutyInfo & Welcome 2024-04-20 21:57:50 +08:00
Dawn1Ocean 311d67a1b1 feat: dutyinfo service 2024-04-19 19:45:58 +08:00
Dawn1Ocean 9bf2ef67be fix: api.md & mock 2024-04-19 14:24:04 +08:00
Dawn1Ocean 19a1c407f2 feat: about page & component 2024-04-18 16:20:01 +08:00
Dawn1Ocean de5150b385 refactor: remove redundant files 2024-04-17 19:43:45 +08:00
Dawn1Ocean f5c6f75779 feat: stats service in index page 2024-04-16 20:47:54 +08:00
Dawn1Ocean 0ea7eff03d temp 2024-04-16 19:30:44 +08:00
37 changed files with 1651 additions and 23881 deletions
+16 -4
View File
@@ -13,9 +13,11 @@ export default [
routes: [ routes: [
{ 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/conclusion', name: '值班总结管理', component: './Admin/Conclusion' }, path: '/admin/dutytable',
{ path: '/admin/report', name: '反馈信息', component: './Admin/Report' }, name: '值班表管理',
component: './Admin/DutyTable',
},
{ path: '/admin/logs', name: '日志', component: './Admin/Logs' }, { path: '/admin/logs', name: '日志', component: './Admin/Logs' },
], ],
}, },
@@ -23,11 +25,21 @@ export default [
path: '/leader', path: '/leader',
name: '值班管理', name: '值班管理',
icon: 'team', icon: 'team',
access: 'canLeader',
routes: [ routes: [
{ path: '/leader', redirect: '/leader/conclusion' }, { 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' },
], ],
}, },
{
path: '/admin/dutytable/edit',
component: './Admin/DutyTable/EditTable',
menu: {
hideInMenu: true,
},
},
{ 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' },
+214 -76
View File
@@ -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
{ {
@@ -52,41 +124,105 @@ data:
} }
``` ```
### 当前值班信息 `GET /admin/duty/current`
#### 正常值班
data:
```json
{
"isNormalDuty": true,
"inDutyCnt": 3,
"otherDutyTime": "",
"offDutyReason": "",
"place": "204",
"dutyRecoverTime": ""
}
```
#### 其他值班
data:
```json
{
"isInDuty": true,
"currentDuty": "2",
"inDutyCnt": 3,
"otherDutyTime": "",
"offDutyReason": "",
"place": "204",
"dutyRecoverTime": ""
}
```
## 主席团页面 ## 主席团页面
### 值班信息管理
#### 当前值班信息 `GET /admin/duty/current`
##### 正常值班
data:
```json
{
"isNormalDuty": true, // 判断是否是正常值班时间
"currentDuty": "1", // 判断是否正在值班 / 值哪一班 "1" | "2" | "3" => 正在值班,"off" => 暂停值班, "0" => 值班下班
"inDutyCnt": 3,
"otherDutyTime": [], // 正常值班时为空
"offDutyReason": "",
"place": "204",
"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",
},
```
### 值班表管理 ### 值班表管理
#### 更新值班表 `POST /admin/sheet/create` #### 更新值班表 `POST /admin/sheet/create`
@@ -200,45 +336,9 @@ data:
} }
``` ```
### 特殊值班 ### 反馈管理
#### 获取当前值班情况 `GET /admin/duty/current` #### 获取反馈列表 `GET /admin/reportlist`
data:
```json
{
"isInDuty": true,
"currentDuty": "2",
"inDutyCnt": 3,
"otherDutyTime": "",
"offDutyReason": "",
"place": "",
"dutyRecoverTime": "",
},
```
#### 更新当前值班情况 `POST /admin/duty/update`
无关项返回空字符串,后端手动校验
request:
```json
{
"isInDuty": true,
"currentDuty": "2",
"inDutyCnt": 3,
"otherDutyTime": "",
"offDutyReason": "",
"place": "204",
"dutyRecoverTime": "",
},
```
## 反馈页面
### 获取反馈列表 `GET /admin/reportlist`
request: request:
@@ -270,9 +370,9 @@ data:
} }
``` ```
## 日志页面 ### 日志页面
### 获取日志列表 `GET /admin/loglist` #### 获取日志列表 `GET /admin/loglist`
request: request:
@@ -316,9 +416,9 @@ data:
前端显示的信息:"{op} 于 {time} {operation} 了 {target}:{prev.info} -> {curr.info}" 前端显示的信息:"{op} 于 {time} {operation} 了 {target}:{prev.info} -> {curr.info}"
## 值班总结管理页面 ### 值班总结管理页面
### 获取值班总结列表 `GET /admin/conclusionlist` #### 获取值班总结列表 `GET /admin/conclusionlist`
request: request:
@@ -352,3 +452,41 @@ data:
] ]
} }
``` ```
### 请假管理页面
#### 获取请假列表 `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": "晓洋" // 未找人代班则为空
}
// ...
]
}
```
+92
View File
@@ -0,0 +1,92 @@
[
{
"member": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:48.523303",
"week": "2",
"shift": "3",
"reason": "英语六级口试",
"substitute": "晓洋"
},
{
"member": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:49.523303",
"week": "2",
"shift": "1",
"reason": "英语六级口试",
"substitute": "晓洋"
},
{
"member": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:50.523303",
"week": "2",
"shift": "2",
"reason": "英语六级口试",
"substitute": "晓洋"
},
{
"member": "晓洋",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:51.523303",
"week": "7",
"shift": "3",
"reason": "英语六级口试",
"substitute": "晓洋"
},
{
"member": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:52.523303",
"week": "7",
"shift": "1",
"reason": "英语六级口试",
"substitute": "晓洋"
},
{
"member": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:53.523303",
"week": "2",
"shift": "3",
"reason": "英语六级口试",
"substitute": "晓洋"
},
{
"member": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:54.523303",
"week": "2",
"shift": "3",
"reason": "英语六级口试",
"substitute": ""
},
{
"member": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:55.523303",
"week": "2",
"shift": "3",
"reason": "英语六级口试",
"substitute": "晓洋"
},
{
"member": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:56.523303",
"week": "2",
"shift": "3",
"reason": "英语六级口试",
"substitute": "晓洋"
},
{
"member": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:57.523303",
"week": "2",
"shift": "3",
"reason": "英语六级口试",
"substitute": "晓洋"
}
]
+81 -82
View File
@@ -1,83 +1,82 @@
[ [
{ {
"op": "宇航员", "op": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png", "avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:48.523303", "time": "2024-03-07T19:52:48.523303",
"week": "2", "week": "2",
"shift": "3", "shift": "3",
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班" "detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
}, },
{ {
"op": "宇航员", "op": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png", "avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:49.523303", "time": "2024-03-07T19:52:49.523303",
"week": "2", "week": "2",
"shift": "1", "shift": "1",
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班" "detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
}, },
{ {
"op": "宇航员", "op": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png", "avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:50.523303", "time": "2024-03-07T19:52:50.523303",
"week": "2", "week": "2",
"shift": "2", "shift": "2",
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班" "detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
}, },
{ {
"op": "晓洋", "op": "晓洋",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png", "avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:51.523303", "time": "2024-03-07T19:52:51.523303",
"week": "7", "week": "7",
"shift": "3", "shift": "3",
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班" "detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
}, },
{ {
"op": "宇航员", "op": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png", "avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:52.523303", "time": "2024-03-07T19:52:52.523303",
"week": "7", "week": "7",
"shift": "1", "shift": "1",
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班" "detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
}, },
{ {
"op": "宇航员", "op": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png", "avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:53.523303", "time": "2024-03-07T19:52:53.523303",
"week": "2", "week": "2",
"shift": "3", "shift": "3",
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班" "detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
}, },
{ {
"op": "宇航员", "op": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png", "avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:54.523303", "time": "2024-03-07T19:52:54.523303",
"week": "2", "week": "2",
"shift": "3", "shift": "3",
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班" "detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
}, },
{ {
"op": "宇航员", "op": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png", "avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:55.523303", "time": "2024-03-07T19:52:55.523303",
"week": "2", "week": "2",
"shift": "3", "shift": "3",
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班" "detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
}, },
{ {
"op": "宇航员", "op": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png", "avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:56.523303", "time": "2024-03-07T19:52:56.523303",
"week": "2", "week": "2",
"shift": "3", "shift": "3",
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班" "detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
}, },
{ {
"op": "宇航员", "op": "宇航员",
"avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png", "avatar": "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
"time": "2024-03-07T19:52:57.523303", "time": "2024-03-07T19:52:57.523303",
"week": "2", "week": "2",
"shift": "3", "shift": "3",
"detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班" "detail": "收到电脑 n 台,维修成功 m 台,已取回 x 台,有 y 台正在维修交接给下一班"
} }
] ]
+59 -33
View File
@@ -1,15 +1,20 @@
import askleavelist from './askleavelist.json';
import conclusionlist from './conclusionlist.json';
import loglist from './loglist.json'; import loglist from './loglist.json';
import reportlist from './reportlist.json';
import sheet from './sheet.json'; import sheet from './sheet.json';
import sheetlist from './sheetlist.json'; import sheetlist from './sheetlist.json';
import reportlist from './reportlist.json';
import conclusionlist from './conclusionlist.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': {
@@ -31,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': {
@@ -50,44 +73,47 @@ module.exports = {
total: 24, total: 24,
data: loglist, data: loglist,
}, },
'GET /admin/reportlist': { 'GET /leader/reportlist': {
success: true, success: true,
page: 1, page: 1,
total: 24, total: 24,
data: reportlist, data: reportlist,
}, },
'GET /admin/conclusionlist': { 'GET /leader/conclusionlist': {
success: true, success: true,
page: 1, page: 1,
total: 24, total: 24,
data: conclusionlist, data: conclusionlist,
}, },
'GET /leader/askleavelist': {
success: true,
page: 1,
total: 24,
data: askleavelist,
},
'POST /login': {
success: true,
data: {
token: 'token_test',
name: '亦可',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
role: 6,
department: 1,
access: 'admin',
},
},
'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 },
-5
View File
@@ -1,5 +0,0 @@
export default {
'/api/auth_routes': {
'/form/advanced-form': { authority: ['admin', 'user'] },
},
};
+252 -254
View File
@@ -1,260 +1,258 @@
{ [
"current": 514, {
"list": [ "name": "2024 春学期值班表",
"createdTime": "2024-03-07T19:52:48.523303",
"id": 114,
"isSelect": true,
"sheet": [
{ {
"name": "2024 春学期值班表", "weekday": 1,
"createdTime": "2024-03-07T19:52:48.523303", "shift": 1,
"id": 114, "workers": ["123423", "31424", "31242"],
"sheet": [ "leader": "3902487"
{
"weekday": 1,
"shift": 1,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 1,
"shift": 2,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 1,
"shift": 3,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 2,
"shift": 1,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 2,
"shift": 2,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 2,
"shift": 3,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 3,
"shift": 1,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 3,
"shift": 2,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 3,
"shift": 3,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 4,
"shift": 1,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 4,
"shift": 2,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 4,
"shift": 3,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 5,
"shift": 1,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 5,
"shift": 2,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 5,
"shift": 3,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 6,
"shift": 1,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 6,
"shift": 2,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 6,
"shift": 3,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 7,
"shift": 1,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 7,
"shift": 2,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
}
]
}, },
{ {
"name": "2024 秋学期值班表", "weekday": 1,
"createdTime": "2024-03-07T11:45:14.523303", "shift": 2,
"id": 514, "workers": ["123423", "31424", "31242"],
"sheet": [ "leader": "3902487"
{ },
"weekday": 1, {
"shift": 1, "weekday": 1,
"workers": ["123423", "31424", "31242"], "shift": 3,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ },
"weekday": 1, {
"shift": 2, "weekday": 2,
"workers": ["123423", "31424", "31242"], "shift": 1,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ },
"weekday": 1, {
"shift": 3, "weekday": 2,
"workers": ["123423", "31424", "31242"], "shift": 2,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ },
"weekday": 2, {
"shift": 1, "weekday": 2,
"workers": ["123423", "31424", "31242"], "shift": 3,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ },
"weekday": 2, {
"shift": 2, "weekday": 3,
"workers": ["123423", "31424", "31242"], "shift": 1,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ },
"weekday": 2, {
"shift": 3, "weekday": 3,
"workers": ["123423", "31424", "31242"], "shift": 2,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ },
"weekday": 3, {
"shift": 1, "weekday": 3,
"workers": ["123423", "31424", "31242"], "shift": 3,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ },
"weekday": 3, {
"shift": 2, "weekday": 4,
"workers": ["123423", "31424", "31242"], "shift": 1,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ },
"weekday": 3, {
"shift": 3, "weekday": 4,
"workers": ["123423", "31424", "31242"], "shift": 2,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ },
"weekday": 4, {
"shift": 1, "weekday": 4,
"workers": ["123423", "31424", "31242"], "shift": 3,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ },
"weekday": 4, {
"shift": 2, "weekday": 5,
"workers": ["123423", "31424", "31242"], "shift": 1,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ },
"weekday": 4, {
"shift": 3, "weekday": 5,
"workers": ["123423", "31424", "31242"], "shift": 2,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ },
"weekday": 5, {
"shift": 1, "weekday": 5,
"workers": ["123423", "31424", "31242"], "shift": 3,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ },
"weekday": 5, {
"shift": 2, "weekday": 6,
"workers": ["123423", "31424", "31242"], "shift": 1,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ },
"weekday": 5, {
"shift": 3, "weekday": 6,
"workers": ["123423", "31424", "31242"], "shift": 2,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ },
"weekday": 6, {
"shift": 1, "weekday": 6,
"workers": ["123423", "31424", "31242"], "shift": 3,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ },
"weekday": 6, {
"shift": 2, "weekday": 7,
"workers": ["123423", "31424", "31242"], "shift": 1,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ },
"weekday": 6, {
"shift": 3, "weekday": 7,
"workers": ["123423", "31424", "31242"], "shift": 2,
"leader": "3902487" "workers": ["123423", "31424", "31242"],
}, "leader": "3902487"
{ }
"weekday": 7, ]
"shift": 1, },
"workers": ["123423", "31424", "31242"], {
"leader": "3902487" "name": "2024 秋学期值班表",
}, "createdTime": "2024-03-07T11:45:14.523303",
{ "id": 514,
"weekday": 7, "isSelect": false,
"shift": 2, "sheet": [
"workers": ["123423", "31424", "31242"], {
"leader": "3902487" "weekday": 1,
} "shift": 1,
] "workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 1,
"shift": 2,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 1,
"shift": 3,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 2,
"shift": 1,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 2,
"shift": 2,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 2,
"shift": 3,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 3,
"shift": 1,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 3,
"shift": 2,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 3,
"shift": 3,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 4,
"shift": 1,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 4,
"shift": 2,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 4,
"shift": 3,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 5,
"shift": 1,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 5,
"shift": 2,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 5,
"shift": 3,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 6,
"shift": 1,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 6,
"shift": 2,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 6,
"shift": 3,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 7,
"shift": 1,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
},
{
"weekday": 7,
"shift": 2,
"workers": ["123423", "31424", "31242"],
"leader": "3902487"
} }
] ]
} }
]
-68
View File
@@ -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,
+24
View File
@@ -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 };
};
-21929
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -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'),
}; };
} }
+33
View File
@@ -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;
+74 -6
View File
@@ -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;
+44
View File
@@ -0,0 +1,44 @@
export interface Staff {
avatar: string;
name: string;
nickname: string;
profile: string;
link?: string;
linktext?: string;
}
const staffList: Staff[] = [
{
avatar: 'https://wiki.zjueva.net/images/1/10/%E5%A4%B4%E5%83%8F.jpg',
name: '马国洋',
nickname: 'Dawn_Ocean',
profile: 'Eta & Tea 总设计\n摸鱼与开发叠加态',
link: 'https://blog.deans.fun',
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 };
-75
View File
@@ -1,75 +0,0 @@
import { PageContainer, ProForm, ProFormRadio, ProFormText } from '@ant-design/pro-components';
import { Card, Col, Row, Space, message } from 'antd';
const waitTime = (time: number = 100) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, time);
});
};
const Logs: React.FC = () => {
return (
<PageContainer>
<Card>
<ProForm<{
name: string;
company?: string;
useMode?: string;
}>
submitter={{
render: (props, doms) => {
return (
<Row>
<Col span={14} offset={4}>
<Space>{doms}</Space>
</Col>
</Row>);
},
}}
onFinish={async (values) => {
await waitTime(2000);
console.log(values);
message.success('提交成功');
}}
params={{}}
request={async () => {
await waitTime(100);
return {
name: '蚂蚁设计有限公司',
useMode: 'chapter',
};
}}
>
<ProFormRadio.Group
style={{
margin: 16,
}}
/>
<ProFormText
width="md"
name="name"
label="签约客户名称"
tooltip="最长为 24 位"
placeholder="请输入名称"
/>
<ProFormText
width="md"
name="company"
label="我方公司名称"
placeholder="请输入名称"
/>
<ProFormText
name={['contract', 'name']}
width="md"
label="合同名称"
placeholder="请输入名称"
/>
</ProForm>
</Card>
</PageContainer>
);
};
export default Logs;
+218
View File
@@ -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;
+23
View File
@@ -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;
};
-14
View File
@@ -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,105 @@
import { Todo } from '@/components';
import { PageContainer } from '@ant-design/pro-components';
import { Card, Modal } from 'antd';
import React, { useState } from 'react';
const EditTable: React.FC = () => {
const [isSaveOpen, setIsSaveOpen] = useState(false);
const [isSetOpen, setIsSetOpen] = useState(false);
// const showSave = () => {
// setIsSaveOpen(true);
// };
const handleSaveOk = () => {
setIsSaveOpen(false);
};
const handleSaveCancel = () => {
setIsSaveOpen(false);
};
// const showSet = () => {
// setIsSetOpen(true);
// };
const handleSetOk = () => {
setIsSetOpen(false);
};
const handleSetCancel = () => {
setIsSetOpen(false);
};
// const columns: ProColumns<DataSourceType>[] = [
// {
// title: '活动名称',
// dataIndex: 'title',
// width: '30%',
// formItemProps: {
// rules: [
// {
// required: true,
// whitespace: true,
// message: '此项是必填项',
// },
// {
// message: '必须包含数字',
// pattern: /[0-9]/,
// },
// {
// max: 16,
// whitespace: true,
// message: '最长为 16 位',
// },
// {
// min: 6,
// whitespace: true,
// message: '最小为 6 位',
// },
// ],
// },
// },
// {
// title: '状态',
// key: 'state',
// dataIndex: 'state',
// valueType: 'select',
// valueEnum: {
// all: { text: '全部', status: 'Default' },
// open: {
// text: '未解决',
// status: 'Error',
// },
// closed: {
// text: '已解决',
// status: 'Success',
// },
// },
// },
// {
// title: '描述',
// dataIndex: 'decs',
// },
// ];
return (
<PageContainer>
<Modal title="确认保存?" open={isSaveOpen} onOk={handleSaveOk} onCancel={handleSaveCancel}>
<p>保存后将回到值班表页面!</p>
</Modal>
<Modal
title="确认设为当前值班表?"
open={isSetOpen}
onOk={handleSetOk}
onCancel={handleSetCancel}
>
<p>同一时间只能启用一份值班表!</p>
</Modal>
<Card>
<Todo />
</Card>
</PageContainer>
);
};
export default EditTable;
@@ -0,0 +1,10 @@
export type TableItem = {
id: React.Key;
mon?: string;
tue?: string;
wed?: string;
thu?: string;
fri?: string;
sat?: string;
sun?: string;
};
@@ -0,0 +1,6 @@
export type DutyTableItem = {
name: string;
isSelect: boolean;
time: string;
id: number;
};
+133
View File
@@ -0,0 +1,133 @@
import { PageContainer, ProList } from '@ant-design/pro-components';
import { history } from '@umijs/max';
import { Button, Card, Modal, Space, Tag } from 'antd';
import moment from 'moment';
import React, { useState } from 'react';
import request from 'umi-request';
import { DutyTableItem } from './dutyTableItem';
const DutyTable: React.FC = () => {
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
const [isSetOpen, setIsSetOpen] = useState(false);
const showDelete = () => {
setIsDeleteOpen(true);
};
const handleDeleteOk = () => {
setIsDeleteOpen(false);
};
const handleDeleteCancel = () => {
setIsDeleteOpen(false);
};
const showSet = () => {
setIsSetOpen(true);
};
const handleSetOk = () => {
setIsSetOpen(false);
};
const handleSetCancel = () => {
setIsSetOpen(false);
};
const newDutyTable = () => {
history.push('/admin/dutytable/edit');
};
return (
<PageContainer>
<Modal
title="确认删除?"
open={isDeleteOpen}
onOk={handleDeleteOk}
onCancel={handleDeleteCancel}
>
<p>删除后将无法恢复!</p>
</Modal>
<Modal
title="确认设为当前值班表?"
open={isSetOpen}
onOk={handleSetOk}
onCancel={handleSetCancel}
>
<p>同一时间只能启用一份值班表!</p>
</Modal>
<Card>
<ProList<DutyTableItem>
search={{
filterType: 'light',
}}
rowKey="op"
headerTitle="值班表列表"
request={async (params = {} as Record<string, any>) =>
request<{
data: DutyTableItem[];
}>('/admin/sheet/list', {
params,
})
}
pagination={{
pageSize: 10,
}}
metas={{
title: {
dataIndex: 'name',
title: '值班表',
},
subTitle: {
dataIndex: ['isSelect'],
render: (_, row) => {
return row.isSelect ? (
<Space size={0}>
<Tag color="blue">当前</Tag>
</Space>
) : (
<div></div>
);
},
title: '值班时间',
search: false,
},
extra: {
dataIndex: 'time',
render: (_: React.ReactNode, row: DutyTableItem) => {
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>,
<a onClick={showSet} key="view">
设为当前值班表
</a>,
<a onClick={showDelete} key="view">
删除
</a>,
],
search: false,
},
}}
toolbar={{
actions: [
<Button type="primary" key="primary" onClick={newDutyTable}>
新建值班表
</Button>,
],
}}
/>
</Card>
</PageContainer>
);
};
export default DutyTable;
@@ -0,0 +1,8 @@
export type AskLeaveItem = {
member: string;
time: string;
week: string;
shift: string;
reason: string;
substitute: string;
};
+124
View File
@@ -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;
-14
View File
@@ -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;
};
@@ -2,7 +2,8 @@ import { PageContainer, ProList } from '@ant-design/pro-components';
import { Card, Space, Tag } from 'antd'; import { Card, Space, Tag } from 'antd';
import moment from 'moment'; import moment from 'moment';
import request from 'umi-request'; import request from 'umi-request';
import transConclusion, { ConclusionItem } from './conclusionItem'; import { ConclusionItem } from './conclusionItem';
import transShift from '@/utils/common';
const Logs: React.FC = () => { const Logs: React.FC = () => {
return ( return (
@@ -17,7 +18,7 @@ const Logs: React.FC = () => {
request={async (params = {} as Record<string, any>) => request={async (params = {} as Record<string, any>) =>
request<{ request<{
data: ConclusionItem[]; data: ConclusionItem[];
}>('/admin/conclusionlist', { }>('/leader/conclusionlist', {
params, params,
}) })
} }
@@ -39,7 +40,7 @@ const Logs: React.FC = () => {
return ( return (
<Space size={0}> <Space size={0}>
<Tag color="blue" key={row.op}> <Tag color="blue" key={row.op}>
{transConclusion(row.week, row.shift)} {transShift(row.week, row.shift)}
</Tag> </Tag>
</Space> </Space>
); );
@@ -17,7 +17,7 @@ const Logs: React.FC = () => {
request={async (params = {} as Record<string, any>) => request={async (params = {} as Record<string, any>) =>
request<{ request<{
data: ReportItem[]; data: ReportItem[];
}>('/admin/reportlist', { }>('/leader/reportlist', {
params, params,
}) })
} }
File diff suppressed because it is too large Load Diff
-1
View File
@@ -208,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
-96
View File
@@ -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();
});
});
+87 -76
View File
@@ -1,9 +1,11 @@
import { getStat } from '@/services/api';
import type { TinyRingConfig } from '@ant-design/charts'; import type { TinyRingConfig } from '@ant-design/charts';
import { Tiny } from '@ant-design/charts'; import { Tiny } from '@ant-design/charts';
import { PageContainer, ProCard, StatisticCard } from '@ant-design/pro-components'; import { PageContainer, ProCard, StatisticCard } from '@ant-design/pro-components';
import { Card, Statistic } from 'antd'; import { Card, Statistic, message } from 'antd';
import RcResizeObserver from 'rc-resize-observer'; import RcResizeObserver from 'rc-resize-observer';
import { useState } from 'react'; import { useLayoutEffect, useState } from 'react';
import { Stat } from './stat';
const percentRing = (percent: number, frontColor: string): React.JSX.Element => { const percentRing = (percent: number, frontColor: string): React.JSX.Element => {
const TinyRing = Tiny.Ring as (props: TinyRingConfig) => JSX.Element; const TinyRing = Tiny.Ring as (props: TinyRingConfig) => JSX.Element;
@@ -30,90 +32,99 @@ const percentRing = (percent: number, frontColor: string): React.JSX.Element =>
return <TinyRing {...config} />; return <TinyRing {...config} />;
}; };
interface IndexData {
todayPT: number;
report: number;
oreo: number;
totalPT: number;
allSuccess: number;
all: number;
}
const indexData: IndexData = {
todayPT: 14,
report: 8,
oreo: 93,
totalPT: 1145,
allSuccess: 1818,
all: 1919,
};
const Logs: React.FC = () => { const Logs: React.FC = () => {
const [responsive, setResponsive] = useState(false); 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 style={{ color: '#f5f5f5' }}> {contextHolder}
<RcResizeObserver {!loading && (
key="resize-observer" <Card style={{ color: '#f5f5f5' }}>
onResize={(offset) => { <RcResizeObserver
setResponsive(offset.width < 596); key="resize-observer"
}} onResize={(offset) => {
> setResponsive(offset.width < 596);
<ProCard split={responsive ? 'horizontal' : 'vertical'}> }}
<ProCard colSpan="45%"> >
<ProCard hoverable>左侧内容</ProCard> <ProCard split={responsive ? 'horizontal' : 'vertical'}>
</ProCard> <ProCard colSpan="Auto">
<ProCard colSpan="20%"> <ProCard hoverable>左侧内容</ProCard>
<StatisticCard </ProCard>
hoverable <ProCard colSpan="170px">
style={{ marginTop: '0vh' }}
statistic={{
title: '今日预填写工单数',
value: indexData.todayPT,
description: (
<Statistic
style={{ marginTop: '15vh', marginBottom: '8vh' }}
title="今日反馈数"
value={indexData.report}
/>
),
}}
/>
</ProCard>
<ProCard>
<StatisticCard.Group direction={responsive ? 'row' : 'column'}>
<StatisticCard <StatisticCard
hoverable hoverable
chart={percentRing( style={{ marginTop: '0vh' }}
Math.round(((indexData.oreo / indexData.totalPT) * 1000) / 10),
'#00CC66',
)}
statistic={{ statistic={{
title: '加入 Oreo 工单数', title: '今日预填写工单数',
value: indexData.oreo, value: stat.todayPT,
description: <Statistic title="预填写工单总数" value={indexData.totalPT} />, description: (
<Statistic
style={{ marginTop: '15vh', marginBottom: '8vh' }}
title="今日反馈数"
value={stat.report}
/>
),
}} }}
chartPlacement="left"
/> />
<StatisticCard </ProCard>
hoverable <ProCard colSpan="360px">
style={{ marginTop: '3vh' }} <StatisticCard.Group direction={responsive ? 'row' : 'column'}>
chart={percentRing( <StatisticCard
Math.round((indexData.allSuccess / indexData.all) * 1000) / 10, hoverable
'#66AFF4', chart={percentRing(
)} Math.round(((stat.oreo / stat.totalPT) * 1000) / 10),
statistic={{ '#00CC66',
title: '维修成功次数', )}
value: indexData.allSuccess, statistic={{
description: <Statistic title="Oreo、ETA 总工单数" value={indexData.all} />, title: '加入 Oreo 工单数',
}} value: stat.oreo,
chartPlacement="left" description: <Statistic title="预填写工单总数" value={stat.totalPT} />,
/> }}
</StatisticCard.Group> 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> </ProCard>
</ProCard> </RcResizeObserver>
</RcResizeObserver> </Card>
</Card> )}
</PageContainer> </PageContainer>
); );
}; };
+8
View File
@@ -0,0 +1,8 @@
export type Stat = {
todayPT: number;
totalPT: number;
oreo: number;
report: number;
allSuccess: number;
all: number;
};
+22
View File
@@ -1,5 +1,7 @@
// @ts-ignore // @ts-ignore
/* eslint-disable */ /* eslint-disable */
import { ServerInfo } from '@/pages/Admin/DutyInfo/info';
import { Stat } from '@/pages/Welcome/stat';
import { request } from '@umijs/max'; import { request } from '@umijs/max';
import { API } from './typings'; import { API } from './typings';
@@ -32,3 +34,23 @@ export async function login(body: API.LoginParams, options?: { [key: string]: an
...(options || {}), ...(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 || {}),
});
}
-26
View File
@@ -5,21 +5,8 @@ declare namespace API {
name?: string; name?: string;
avatar?: string; avatar?: string;
userid?: string; userid?: string;
email?: string;
signature?: string;
title?: string;
group?: string; group?: string;
tags?: { key?: string; label?: string }[];
notifyCount?: number;
unreadCount?: number;
country?: string;
access?: string; access?: string;
geographic?: {
province?: { label?: string; key?: string };
city?: { label?: string; key?: string };
};
address?: string;
phone?: string;
}; };
type LoginResult = { type LoginResult = {
@@ -28,11 +15,6 @@ declare namespace API {
currentAuthority?: string; currentAuthority?: string;
}; };
type PageParams = {
current?: number;
pageSize?: number;
};
type LoginParams = { type LoginParams = {
username?: string; username?: string;
password?: string; password?: string;
@@ -40,14 +22,6 @@ declare namespace API {
type?: string; type?: string;
}; };
type ErrorResponse = {
/** 业务约定的错误码 */
errorCode: string;
/** 业务上的错误信息 */
errorMessage?: string;
/** 业务上的请求是否成功 */
success?: boolean;
};
type LogList = { type LogList = {
pageIndex: number; pageIndex: number;
pageCount: number; pageCount: number;
@@ -1,11 +1,3 @@
export type ConclusionItem = {
op: string;
time: string;
week: string;
shift: string;
detail: string;
};
const weekMap: { [key: string]: string } = { const weekMap: { [key: string]: string } = {
'1': '周一', '1': '周一',
'2': '周二', '2': '周二',
@@ -22,11 +14,12 @@ const shiftMap: { [key: string]: string } = {
'3': '第三班', '3': '第三班',
}; };
const transConclusion = (week: string, shift: string) => { const transShift = (week: string, shift: string) => {
if (week === '7' && shift === '3') { if (week === '7' && shift === '3') {
return '其他值班时间'; return '其他值班时间';
} else { } else {
return weekMap[week] + shiftMap[shift]; return weekMap[week] + shiftMap[shift];
} }
}; };
export default transConclusion;
export default transShift;