Compare commits

12 Commits
Author SHA1 Message Date
Dawn1Ocean 2731f5560f fix welcome layout 2024-04-07 13:19:01 +08:00
Dawn1Ocean 0604a3dfbf fix welcome layout 2024-04-07 13:18:32 +08:00
Dawn1Ocean 3b52792917 fix: type ref bug in tiny.ring 2024-04-06 17:00:51 +08:00
Dawn1Ocean 29704a6197 fix: welcome page layout 2024-04-04 04:09:17 +08:00
Dawn1Ocean 29fa1341d8 feat: chart in welcome page 2024-04-04 03:12:28 +08:00
Dawn1Ocean 58719b709b change /admin/reportlist api 2024-04-03 16:38:07 +08:00
Dawn1Ocean 01234a6042 add procard in welcome 2024-04-02 19:21:12 +08:00
Dawn1Ocean 54f7fe53a5 feat: prolist in /admin/report 2024-04-02 15:53:26 +08:00
Dawn1Ocean e71abc0c0b add mock: reportlist 2024-04-02 14:59:06 +08:00
Dawn1Ocean 2cb4684130 add log component: prolist 2024-04-02 02:55:09 +08:00
Dawn1Ocean 0d1665f609 remove redundant services 2024-03-31 22:32:12 +08:00
Dawn1Ocean da62939b0d change README.md 2024-03-31 22:15:12 +08:00
29 changed files with 819 additions and 773 deletions
+40 -37
View File
@@ -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
npm start
```
1. 个人信息、职位
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
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).
1. 查询功能
-78
View File
@@ -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": {
"type": "object",
"properties": {
+3 -2
View File
@@ -14,6 +14,8 @@ export default [
{ path: '/admin', redirect: '/admin/dutyinfo' },
{ path: '/admin/dutyinfo', name: '值班信息', component: './Admin/DutyInfo' },
{ path: '/admin/dutytable', name: '值班表管理', component: './Admin/DutyTable' },
{ path: '/admin/report', name: '反馈信息', component: './Admin/Report' },
{ path: '/admin/logs', name: '日志', component: './Admin/Logs' },
],
},
{
@@ -21,11 +23,10 @@ export default [
name: '值班管理',
icon: 'team',
routes: [
{ path: '/leader', redirect: '/admin/dutyinfo' },
{ path: '/leader', redirect: '/leader/conclusion' },
{ path: '/leader/conclusion', name: '值班总结', component: './Leader/Conclusion' },
],
},
{ name: '日志', icon: 'clockCircle', path: '/logs', component: './Logs' },
{ name: '关于我们', icon: 'thunderbolt', path: '/about', component: './About' },
{ path: '/', redirect: '/welcome' },
{ path: '*', layout: false, component: './404' },
+94 -19
View File
@@ -34,7 +34,7 @@
## 主页面
### 统计数据 `GET /admin/stats`
### 统计数据 `GET /stats`
data:
@@ -42,14 +42,35 @@ data:
{
"success": true,
"data": {
"notInOreo": 114,
"total": 514
"todayPT": 114, // 今日预填写工单总数
"totalPT": 514, // 所有预填写工单
"oreo": 93, // 加入 Oreo 工单数
"report": 8, // 今日反馈数
"allSuccess": 1919, // 所有成功工单(包括 Oreo 内)
"all": 2024 // 所有工单(包括 Oreo 内)
}
}
```
### 当前值班信息 `GET /admin/duty/current`
#### 正常值班
data:
```json
{
"isNormalDuty": true,
"inDutyCnt": 3,
"otherDutyTime": "",
"offDutyReason": "",
"place": "204",
"dutyRecoverTime": ""
}
```
#### 其他值班
data:
```json
@@ -93,7 +114,6 @@ request:
}
```
#### 更新值班表 `POST /admin/sheet/update`
request:
@@ -216,27 +236,82 @@ request:
},
```
## 日志页面
## 反馈页面
### 获取日志列表 `GET /admin/loglist`
### 获取反馈列表 `GET /admin/reportlist`
request:
```json
{
"current": 1,
"pageSize": 10,
"name?": "西西弗", // 如果不筛选则不出现在 request-body 内
"phone?": "18888888888" // 如果不筛选则不出现在 request-body 内
}
```
data:
```json
{
"op": "宇航员",
"time": "2024-03-07T19:52:48.523303",
"operation": 2,
"target": 1,
"prev": {
"id": 114,
"info": "马嘉祺"
},
"curr": {
"id": 514,
"info": "何君琳"
}
},
"page": 1,
"total": 24,
"success": true,
"data": [
{
"name": "晓洋",
"phone": "13333333333",
"time": "2024-03-07T17:52:48.523303",
"text": "你们为什么修不好天选3的PD充电?"
}
// ...
]
}
```
## 日志页面
### 获取日志列表 `GET /admin/loglist`
request:
```json
{
"current": 1,
"pageSize": 10,
"op?": "晓洋", // 如果不筛选则不出现在 request-body 内
"operation": "all" | "add" | "delete" | "change", // all 之外的选项用于筛选
"target": "all" | "dutyTable" | "dutyInfo" | "conclusion" // all 之外的选项用于筛选
}
```
data:
```json
{
"page": 1,
"total": 24,
"success": true,
"data": [
{
"op": "宇航员",
"avatar": "https://...",
"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}"
+152
View File
@@ -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": "何君琳"
}
}
]
+72
View File
@@ -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充电?"
}
]
+13 -16
View File
@@ -1,5 +1,7 @@
import sheet from './sheet.json'
import sheetlist from './sheetlist.json'
import loglist from './loglist.json';
import sheet from './sheet.json';
import sheetlist from './sheetlist.json';
import reportlist from './reportlist.json';
module.exports = {
'GET /admin/stats': {
@@ -43,20 +45,15 @@ module.exports = {
},
'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: '何君琳',
},
},
page: 1,
total: 24,
data: loglist,
},
'GET /admin/reportlist': {
success: true,
page: 1,
total: 24,
data: reportlist,
},
'GET /api/currentUser': {
data: {
+1
View File
@@ -46,6 +46,7 @@
"not ie <= 10"
],
"dependencies": {
"@ant-design/charts": "^2.0.3",
"@ant-design/icons": "^4.8.1",
"@ant-design/pro-components": "^2.7.0",
"@umijs/route-utils": "^2.2.2",
+2 -1
View File
@@ -1,5 +1,5 @@
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 type { Settings as LayoutSettings } 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 defaultSettings from '../config/defaultSettings';
import { errorConfig } from './requestErrorConfig';
import { API } from './services/typings';
const isDev = process.env.NODE_ENV === 'development';
const loginPath = '/user/login';
+1 -1
View File
@@ -21,7 +21,7 @@ const PageFooter: React.FC = () => {
{
key: 'EVA Tea',
title: 'EVA Tea',
href: 'https://pro.ant.design',
href: 'https://a.zjueva.net',
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 { history, useModel } from '@umijs/max';
import { Spin } from 'antd';
+65 -4
View File
@@ -1,12 +1,73 @@
import { Todo } from '@/components';
import { PageContainer } from '@ant-design/pro-components';
import { Card } from 'antd';
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>
<Todo />
<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>
);
+107
View File
@@ -0,0 +1,107 @@
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: ['time', 'operation', 'target', 'prev', 'curr'],
search: false,
render: (_, row) => {
return (
<div>
{'于 ' +
moment(row.time).format('YYYY-M-D HH:mm:ss') +
' ' +
transInfo(row.operation, 'operation') +
'了' +
transInfo(row.target, 'target') +
':' +
row.prev.info +
' -> ' +
row.curr.info}
</div>
);
},
},
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;
+37
View File
@@ -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;
+70
View File
@@ -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[];
}>('/admin/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;
+6
View File
@@ -0,0 +1,6 @@
export type ReportItem = {
name: string;
phone: string;
time: string;
text: 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;
+2 -1
View File
@@ -1,7 +1,8 @@
import Settings from '@/../config/defaultSettings';
import oidcLogo from '@/assets/oidc-logo.svg';
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 { LoginForm, ProFormCheckbox, ProFormText } from '@ant-design/pro-components';
import { Helmet, history, useModel } from '@umijs/max';
+111 -5
View File
@@ -1,12 +1,118 @@
import { Todo } from '@/components';
import { PageContainer } from '@ant-design/pro-components';
import { Card } from 'antd';
import type { TinyRingConfig } from '@ant-design/charts';
import { Tiny } from '@ant-design/charts';
import { PageContainer, ProCard, StatisticCard } from '@ant-design/pro-components';
import { Card, Statistic } from 'antd';
import RcResizeObserver from 'rc-resize-observer';
import { useState } from 'react';
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} />;
};
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 [responsive, setResponsive] = useState(false);
return (
<PageContainer>
<Card>
<Todo />
<Card style={{ color: '#f5f5f5' }}>
<RcResizeObserver
key="resize-observer"
onResize={(offset) => {
setResponsive(offset.width < 596);
}}
>
<ProCard split={responsive ? 'horizontal' : 'vertical'}>
<ProCard colSpan="45%">
<ProCard hoverable>左侧内容</ProCard>
</ProCard>
<ProCard colSpan="20%">
<StatisticCard
hoverable
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
hoverable
chart={percentRing(
Math.round(((indexData.oreo / indexData.totalPT) * 1000) / 10),
'#00CC66',
)}
statistic={{
title: '加入 Oreo 工单数',
value: indexData.oreo,
description: <Statistic title="预填写工单总数" value={indexData.totalPT} />,
}}
chartPlacement="left"
/>
<StatisticCard
hoverable
style={{ marginTop: '3vh' }}
chart={percentRing(
Math.round((indexData.allSuccess / indexData.all) * 1000) / 10,
'#66AFF4',
)}
statistic={{
title: '维修成功次数',
value: indexData.allSuccess,
description: <Statistic title="Oreo、ETA 总工单数" value={indexData.all} />,
}}
chartPlacement="left"
/>
</StatisticCard.Group>
</ProCard>
</ProCard>
</RcResizeObserver>
</Card>
</PageContainer>
);
-94
View File
@@ -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 || {}),
},
});
}
-21
View File
@@ -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 || {}),
});
}
+34
View File
@@ -0,0 +1,34 @@
// @ts-ignore
/* eslint-disable */
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 || {}),
});
}
@@ -3,8 +3,6 @@
// API 更新时间:
// API 唯一标识:
import * as api from './api';
import * as login from './login';
export default {
api,
login,
};
-12
View File
@@ -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,
};
-153
View File
@@ -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 || {}),
});
}
-48
View File
@@ -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 || {}),
});
}
-112
View File
@@ -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;
};
}
-100
View File
@@ -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 || {}),
});
}
@@ -1,5 +1,4 @@
// @ts-ignore
/* eslint-disable */
import { LogsItem } from '@/pages/Admin/Logs/logItem';
declare namespace API {
type CurrentUser = {
@@ -34,33 +33,6 @@ declare namespace API {
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;
@@ -76,26 +48,10 @@ declare namespace API {
/** 业务上的请求是否成功 */
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;
type LogList = {
pageIndex: number;
pageCount: number;
size: number;
data: LogsItem[];
};
}