20 Commits
Author SHA1 Message Date
Dawn1Ocean 166c8b6319 add log api & mock 2024-03-27 13:36:37 +08:00
Dawn1Ocean fbaedaad0e add prolist in logs page 2024-03-26 20:36:27 +08:00
Dawn_Ocean 31a782daef add pro component 2024-03-25 20:07:40 +08:00
Dawn_Ocean 31e48dafc8 add antd pro 2024-03-25 20:04:18 +08:00
Dawn_Ocean 31188c0bb0 change icons & logo 2024-03-25 19:33:25 +08:00
Dawn_Ocean 57c0fed976 add logs page 2024-03-25 19:07:34 +08:00
Dawn_Ocean 56174936d3 change footer 2024-03-25 18:58:12 +08:00
Dawn_Ocean 24817aea50 add page & subpage structure 2024-03-25 16:11:01 +08:00
Dawn1Ocean 7316208b21 add mock and doc 2024-03-23 00:39:06 +08:00
Dawn1Ocean fb8b0ddc6e change README.md 2024-03-19 21:17:01 +08:00
fracher 36bc6653e2 add button text 2024-03-14 21:19:13 +08:00
fracher 3187c7be28 add read excel file 2024-03-14 20:25:45 +08:00
fracher 7ee06b9901 refactor routes 2024-03-14 19:46:11 +08:00
fracher 787b52c096 add Todo 2024-03-14 19:14:53 +08:00
fracher 3619ce3d1e update README.md 2024-03-14 18:58:49 +08:00
fracher c24a82d177 improve define menu 2024-03-14 02:33:39 +08:00
fracher ec711daa0c clear code 2024-03-14 02:23:36 +08:00
fracher d91ece0c54 improve footer 2024-03-14 02:20:23 +08:00
fracher 1937fa4217 fix breadcrumb 2024-03-14 02:09:11 +08:00
fracher 50e3f4a8f0 App layout 2024-03-14 01:44:38 +08:00
21 changed files with 2533 additions and 318 deletions
+43 -58
View File
@@ -1,75 +1,60 @@
# EVA Notify # Notify Admin
## 简介 ## 目标
使用微信小程序实现的值班信息查看、维修进度交流系统。 1. 微信小程序的信息管理页面
2. 人资部后台(长期)
## 功能 ## 功能
### 值班情况 ### 主席团管理页面
1. 能看到现在维修状况:有几台正在维修,有几个人值班(空闲状态) 1. 值班表导入、更新
2. 能看到自己机器的维修情况:维修进度、评论等 1. 值班表渲染
1. 使用手机号确定id 2. 值班表在线更改、上传后端
3. 值班表导出
2. 成员权限管理
1. 从 Auth 获取成员职位、部门
2. 主席团成员:可更改所有信息
3. 当班所有成员:可更改值班人数,可加入 Oreo,可更改状态
4. 所有成员:可认领
3. 特殊维修时间(如学园维修)、值班信息更新
4. 日志
1. 记录每一次操作、操作者
5. 自动排班功能(Enhancement)
### 互动 ### 值班组长管理页面
1. 提醒:当修好之后会向你发送信息,提醒来取 ps. 是微信小程序中小组管理页面的pc端替代
2. 线上答疑:协会成员登录账号,看到机主提出的问题可以回答(暂定由值班组长管理)
1. 公开答疑仅回复能不能修,更复杂的问题推荐其来204
2. 当自己的机器正在维修时,可以与维修人员交流进度和问题等
3. (暂定)答疑功能使用预填写工单、工单的评论实现
### 信息展示 1. 值班组长上传本班相关信息
2. 值班组长上传值班总结(Enhancement)
1. 展示协会值班时间(现在是否在值班),若不在值班显示预计值班时间、不值班的原因等
2. 展示维修流程,包括注意事项等
1. 戴尔、Surface等不拆
2. 数据备份提醒
3. 不收礼物
4. 不要在204饮食
3. (备选)其他语言本地化
### 面向协会成员
1. 权限管理
2. 值班组长签到情况、值班总结等
3. 显示自己什么时候值班
4. 提醒事项
1. 值班后收拾东西、交接
2. 螺丝记得拧上
3. 某些东西不要放在维修篮里
### 对接
1. 与EVA统一身份认证对接
1. 协会成员登录入口
2. 个人信息同步
2. 与Oreo对接
1. 维修工单列表查看
2. 将预填写工单加入Oreo
3. 防恶意行为
1. 工单填写间隔限制为【1】分钟
2. 一个人一天最多能创建【10】个预填写工单
3. 黑名单机制
## UI ## UI
### 主页 ### 仪表盘
1. 信息显示 1. 个人信息、职位
2. (协会成员)相关信息 2. 新系统中(未加入Oreo/预填写工单总数)统计
3. 提醒事项
4. 如何联系到协会
### 维修 ### 主席团管理页面
1. (面向机主)预填写工单 #### 值班表管理
2. 维修进度提示
3. (面向协会成员)维修列表
### 我的 1. 值班表渲染
2. 值班表在线更改、上传后端
1. 特殊时间值班表不上传
3. 值班表导出
1. 更改手机号等个人信息(手机号可以填写多个) #### 特殊值班时间
2. (备选)修改用户名、头像
3. 设置真名(可帮助预填写工单) 1. (switch) 是否启用平常值班时间
2. 若不在平常值班时间,选择录入不值班原因/特殊值班时间
### 值班组长管理页面(暂空)
1. (Enhancement) 值班总结上传
### 日志(所有人可见)
1. 查询功能
+242
View File
@@ -0,0 +1,242 @@
# 接口文档
## 基本格式
### 成功响应格式
```json
{
"success": true,
"data": {
"foo": "bar"
}
}
```
**NOTE:** 若无特殊说明,`POST`请求的响应`data`字段为空(`{}`),可以不用管。
### 失败响应格式
```json
{
"success": false,
"err": "some error"
}
```
### `GET`请求默认格式(若无特殊说明)
```json
{
"token": "token_test"
}
```
## 主页面
### 统计数据 `GET /admin/stats`
data:
```json
{
"success": true,
"data": {
"notInOreo": 114,
"total": 514
}
}
```
### 当前值班信息 `GET /admin/duty/current`
data:
```json
{
"isInDuty": true,
"currentDuty": "2",
"inDutyCnt": 3,
"otherDutyTime": "",
"offDutyReason": "",
"place": "204",
"dutyRecoverTime": ""
}
```
## 主席团页面
### 值班表管理
#### 更新值班表 `POST /admin/sheet/create`
request:
```json
{
"name": "2024 春学期值班表",
"sheet": [
{
"weekday": 1,
"shift": 1,
"workers": ["马嘉祺", "丁程鑫", "宋亚轩"],
"leader": "蔡徐坤"
},
{
"weekday": 1,
"shift": 2,
"workers": ["王俊凯", "易烊千玺", "王源"],
"leader": "丁真珍珠"
}
// ...
]
}
```
#### 更新值班表 `POST /admin/sheet/update`
request:
```json
{
"id": 114514,
"name": "2024 春学期值班表",
"sheet": [
{
"weekday": 1,
"shift": 1,
"workers": ["马嘉祺", "丁程鑫", "宋亚轩"],
"leader": "蔡徐坤"
},
{
"weekday": 1,
"shift": 2,
"workers": ["王俊凯", "易烊千玺", "王源"],
"leader": "丁真珍珠"
}
// ...
]
}
```
#### 获取当前值班表 `GET /admin/sheet/current`
data:
```json
{
"name": "2024 春学期值班表",
"id": 114514,
"createdTime": "2024-03-07T19:52:48.523303",
"sheet": [
{
"weekday": 1,
"shift": 1,
"workers": ["马嘉祺", "丁程鑫", "宋亚轩"],
"leader": "蔡徐坤"
},
{
"weekday": 1,
"shift": 2,
"workers": ["王俊凯", "易烊千玺", "王源"],
"leader": "丁真珍珠"
}
// ...
]
}
```
#### 获取所有值班表 `GET /admin/sheet/list`
data:
```json
{
"current": 114514,
"list": [
{
"name": "2024 春学期值班表",
"id": 114514,
"createdTime": "2024-03-07T19:52:48.523303",
"sheet": [
{
"weekday": 1,
"shift": 1,
"workers": ["马嘉祺", "丁程鑫", "宋亚轩"],
"leader": "蔡徐坤"
},
{
"weekday": 1,
"shift": 2,
"workers": ["王俊凯", "易烊千玺", "王源"],
"leader": "丁真珍珠"
}
// ...
]
}
// ...
]
}
```
### 特殊值班
#### 获取当前值班情况 `GET /admin/duty/current`
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/loglist`
data:
```json
{
"op": "宇航员",
"time": "2024-03-07T19:52:48.523303",
"operation": 2,
"target": 1,
"prev": {
"id": 114,
"info": "马嘉祺"
},
"curr": {
"id": 514,
"info": "何君琳"
}
},
```
前端显示的信息:"{op} 于 {time} {operation} 了 {target}:{prev.info} -> {curr.info}"
+63
View File
@@ -0,0 +1,63 @@
import sheetlist from './sheetlist.json';
import sheet from './sheet.json';
const api = {
'GET /admin/stats': {
success: true,
data: {
notInOreo: 114,
total: 514,
},
},
'POST /admin/sheet/update': {
success: true,
data: {},
},
'POST /admin/sheet/create': {
success: true,
data: {},
},
'GET /admin/sheet/current': {
success: true,
data: sheet,
},
'GET /admin/sheet/list': {
success: true,
data: sheetlist,
},
'GET /admin/duty/current': {
success: true,
data: {
isInDuty: true,
currentDuty: '2',
inDutyCnt: 3,
otherDutyTime: '',
offDutyReason: '',
place: '204',
dutyRecoverTime: '',
},
},
'POST /admin/duty/update': {
success: true,
data: {},
},
'GET /admin/loglist': {
success: true,
data: {
op: '宇航员',
time: '2024-03-07T19:52:48.523303',
operation: 2,
target: 1,
prev: {
id: 114,
info: '马嘉祺',
},
curr: {
id: 514,
info: '何君琳',
},
},
},
};
export default api;
+127
View File
@@ -0,0 +1,127 @@
{
"name": "2024 春学期值班表",
"createdTime": "2024-03-07T19:52:48.523303",
"id": 114,
"sheet": [
{
"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"
}
]
}
+259
View File
@@ -0,0 +1,259 @@
{
"current": 514,
"list": [
{
"name": "2024 春学期值班表",
"createdTime": "2024-03-07T19:52:48.523303",
"id": 114,
"sheet": [
{
"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 秋学期值班表",
"createdTime": "2024-03-07T11:45:14.523303",
"id": 514,
"sheet": [
{
"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"
}
]
}
]
}
+1463 -202
View File
File diff suppressed because it is too large Load Diff
+4 -3
View File
@@ -6,16 +6,18 @@
"name": "EVA Tech" "name": "EVA Tech"
}, },
"dependencies": { "dependencies": {
"@ant-design/icons": "^5.3.1",
"@ant-design/pro-components": "^2.6.52",
"@types/node": "^16.18.88", "@types/node": "^16.18.88",
"@types/react": "^18.2.65", "@types/react": "^18.2.65",
"@types/react-dom": "^18.2.21", "@types/react-dom": "^18.2.21",
"evergreen-ui": "^7.1.9", "antd": "^5.15.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-router-dom": "^6.22.3", "react-router-dom": "^6.22.3",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"web-vitals": "^2.1.4" "xlsx": "^0.18.5"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",
@@ -39,7 +41,6 @@
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.1.3",
"husky": "^9.0.11", "husky": "^9.0.11",
"lint-staged": "^15.2.2", "lint-staged": "^15.2.2",
"sass": "^1.71.1",
"stylelint": "^16.2.1" "stylelint": "^16.2.1"
}, },
"lint-staged": { "lint-staged": {
+3 -22
View File
@@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <link rel="icon" href="%PUBLIC_URL%/logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta <meta
@@ -15,29 +15,10 @@
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
--> -->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!-- <title>EVA Tea</title>
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head> </head>
<body> <body style="margin: 0;">
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div> <div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body> </body>
</html> </html>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

+145 -11
View File
@@ -1,13 +1,147 @@
import { Component, ReactNode } from 'react'; import { FC } from 'react';
import { Route, Routes } from 'react-router-dom'; import { useLocation, useNavigate } from 'react-router-dom';
import {
TeamOutlined,
HomeOutlined,
SmileOutlined,
CrownOutlined,
ClockCircleOutlined,
ThunderboltOutlined,
} from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Breadcrumb, Layout, Menu, theme } from 'antd';
import { AppRouter, routes, routeName } from './router';
import PageFooter from './components/PageFooter';
export default class App extends Component { const { Content, Sider } = Layout;
render(): ReactNode {
return ( type MenuItem = Required<MenuProps>['items'][number];
<Routes>
<Route path='/' element={<p>Main</p>} /> type KeyRouterMap = Map<React.Key, string>;
<Route path='/hello' element={<p>Hello</p>} />
</Routes> const keyRouterMap: KeyRouterMap = new Map();
);
} function getItem(
label: React.ReactNode,
key: React.Key,
icon?: React.ReactNode,
children?: MenuItem[],
): MenuItem {
return {
key,
icon,
children,
label,
} as MenuItem;
} }
function defMenu(
label: React.ReactNode,
key: React.Key,
icon: React.ReactNode,
path: string,
): MenuItem {
keyRouterMap.set(key, path);
return getItem(label, key, icon);
}
function defRootMenu(
label: React.ReactNode,
key: React.Key,
icon: React.ReactNode,
children: MenuItem[],
): MenuItem {
return getItem(label, key, icon, children);
}
function defSubMenu(
label: React.ReactNode,
key: React.Key,
path: string,
): MenuItem {
keyRouterMap.set(key, path);
return getItem(label, key);
}
const items: MenuItem[] = [
defMenu('主页', 'mainpage', <SmileOutlined />, routes.main),
defRootMenu('主席团管理', 'admin', <CrownOutlined />, [
defSubMenu('排班表管理', 'dutytable', routes.admin.dutyTable),
defSubMenu('值班信息管理', 'dutyinfo', routes.admin.dutyInfo),
]),
defRootMenu('值班组长', 'leader', <TeamOutlined />, [
defSubMenu('值班总结', 'conclusion', routes.duty.conclusion),
]),
defMenu('日志', 'logspage', <ClockCircleOutlined />, routes.logs),
defMenu('关于我们', 'aboutpage', <ThunderboltOutlined />, routes.about),
];
interface BreadcrumbItem {
href: string;
title: JSX.Element;
}
const AppLayout: FC<{ router: JSX.Element }> = (props) => {
const { colorBgContainer, borderRadiusLG } = theme.getDesignToken();
const navigate = useNavigate();
const location = useLocation();
const pathArray = location.pathname.split('/');
const currentRouteItems: BreadcrumbItem[] = [
{
href: '/',
title: <HomeOutlined />,
},
];
for (var i = 1; i < pathArray.length; i++) {
currentRouteItems.push({
href: '',
title: <span>{routeName.get(pathArray[i])}</span>,
});
}
return (
<Layout style={{ minHeight: '100vh' }}>
<Sider>
<div className='demo-logo-vertical' />
<Menu
theme='dark'
defaultSelectedKeys={['1']}
mode='inline'
items={items}
onClick={(info: { key: React.Key }) => {
console.log('path', keyRouterMap.get(info.key));
navigate(keyRouterMap.get(info.key) || '/');
}}
/>
</Sider>
<Layout>
<Content style={{ margin: '0 16px' }}>
<Breadcrumb style={{ margin: '16px 0' }} items={currentRouteItems} />
<div
style={{
padding: 24,
minHeight: 360,
background: colorBgContainer,
borderRadius: borderRadiusLG,
}}
>
<div>{props.router}</div>
</div>
</Content>
<PageFooter />
</Layout>
</Layout>
);
};
const App: FC = () => {
return (
<div className='app'>
<AppLayout router={<AppRouter />} />
</div>
);
};
export default App;
+24
View File
@@ -0,0 +1,24 @@
import { Layout } from 'antd';
const { Footer } = Layout;
const PageFooter: React.FC = () => {
const startYear = 2024;
const curYear = new Date().getFullYear();
let yearStr = '';
if (startYear === curYear) {
yearStr = curYear.toString();
} else {
yearStr = startYear.toString() + ' - ' + curYear.toString();
}
return (
<div>
<Footer style={{ textAlign: 'center', color: '#808080' }}>
EVA Tea <br /> <br />
&copy; {yearStr} EVA Tech
</Footer>
</div>
);
};
export default PageFooter;
+14
View File
@@ -0,0 +1,14 @@
import { Typography } from 'antd';
const { Title, Text } = Typography;
const Todo: React.FC = () => {
return (
<div>
<Title level={1}>施工中...</Title>
<Text type={'secondary'}>{'// TODO'}</Text>
</div>
);
};
export default Todo;
-6
View File
@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import App from './App'; import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom'; import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot( const root = ReactDOM.createRoot(
@@ -14,8 +13,3 @@ root.render(
</BrowserRouter> </BrowserRouter>
</React.StrictMode>, </React.StrictMode>,
); );
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals(console.log);
-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

+24
View File
@@ -0,0 +1,24 @@
import { Button, Upload } from 'antd';
import { UploadOutlined } from '@ant-design/icons';
import { FC } from 'react';
import { readExcelFile } from '../../utils/excel';
const DutyTablePage: FC = () => {
return (
<Upload
name='file'
action=''
beforeUpload={(file) => {
let data = readExcelFile(file, 0);
data
.then((d) => console.log('read data', d))
.catch((e) => console.log('got error', e));
return false;
}}
>
<Button icon={<UploadOutlined />}>上传排班表文件</Button>
</Upload>
);
};
export default DutyTablePage;
+61
View File
@@ -0,0 +1,61 @@
import { FC } from 'react';
import { EllipsisOutlined } from '@ant-design/icons';
import { ProList } from '@ant-design/pro-components';
import { Progress, Tag } from 'antd';
const data = [
'语雀的天空',
'Ant Design',
'蚂蚁金服体验科技',
'TechUI',
'TechUI 2.0',
'Bigfish',
'Umi',
].map((item) => ({
title: item,
subTitle: <Tag color='#5BD8A6'>语雀专栏</Tag>,
actions: [<EllipsisOutlined />],
avatar:
'https://gw.alipayobjects.com/zos/antfincdn/UCSiy1j6jx/xingzhuang.svg',
content: (
<div
style={{
flex: 1,
display: 'flex',
justifyContent: 'flex-end',
}}
>
<div
style={{
width: 200,
}}
>
<div>发布中</div>
<Progress percent={80} />
</div>
</div>
),
}));
const LogsPage: FC = () => {
return (
<ProList<any>
pagination={{
defaultPageSize: 5,
showSizeChanger: true,
}}
metas={{
title: {},
subTitle: {},
type: {},
avatar: {},
content: {},
actions: {},
}}
headerTitle='翻页'
dataSource={data}
/>
);
};
export default LogsPage;
-15
View File
@@ -1,15 +0,0 @@
import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;
+42
View File
@@ -0,0 +1,42 @@
import { Routes, Route } from 'react-router-dom';
import Todo from './components/Todo';
import DutyTablePage from './pages/duty/DutyTable';
import LogsPage from './pages/logs/Logs';
export const routes = {
main: '/',
about: '/about',
admin: {
dutyTable: '/admin/dutytable',
userControl: '/admin/usercontrol',
dutyInfo: '/admin/dutyinfo',
},
duty: {
conclusion: '/duty/conclusion',
},
logs: '/logs',
};
export const routeName = new Map<string, string>([
['admin', '主席团管理'],
['about', '关于我们'],
['duty', '值班组长'],
['dutytable', '排班表'],
['dutyinfo', '值班信息'],
['conclusion', '值班总结'],
['logs', '日志'],
]);
export const AppRouter: React.FC = () => {
return (
<Routes>
<Route path={routes.main} element={<Todo />} />
<Route path={routes.admin.dutyTable} element={<DutyTablePage />} />
<Route path={routes.admin.userControl} element={<Todo />} />
<Route path={routes.admin.dutyInfo} element={<Todo />} />
<Route path={routes.duty.conclusion} element={<Todo />} />
<Route path={routes.about} element={<Todo />} />
<Route path={routes.logs} element={<LogsPage />} />
</Routes>
);
};
View File
+10
View File
@@ -0,0 +1,10 @@
import { read as xlsxRead, utils as xlsxUtils } from 'xlsx';
import { readFile } from './readfile';
export async function readExcelFile(file: File, sheetIndex: number) {
let data = await readFile(file);
let workbook = xlsxRead(data, { type: 'binary' });
let worksheet = workbook.Sheets[workbook.SheetNames[sheetIndex]];
data = xlsxUtils.sheet_to_json(worksheet);
return data;
}
+9
View File
@@ -0,0 +1,9 @@
export function readFile(file: File) {
return new Promise((resolve) => {
let reader = new FileReader();
reader.readAsBinaryString(file);
reader.onload = (ev) => {
resolve(ev.target?.result);
};
});
}