38 Commits
leei ... dev

Author SHA1 Message Date
wenhua
37fc934233 Merge pull request 'dev_wenhua_plus' (#29) from dev_wenhua_plus into dev
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/29
2023-12-11 07:28:36 +00:00
wenhua
aebbaadfac feat: 新增权限按钮 2023-12-11 15:26:18 +08:00
wenhua
6929f6535c Merge pull request 'dev' (#28) from dev into dev_wenhua_plus
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/28
2023-11-30 03:17:53 +00:00
clay
e2f2b1518c 更新 '.drone.yml' 2023-11-28 03:25:19 +00:00
wenhua
e0d42f8913 Merge pull request '修改base 为 /' (#27) from dev_wenhua_plus into dev
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/27
2023-11-20 14:10:53 +00:00
wenhua
4ed199fc89 修改base 为 / 2023-11-20 22:10:13 +08:00
wenhua
1b68171ea0 Merge pull request '更改登录懒加载' (#26) from dev_wenhua_plus into dev
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/26
2023-11-20 14:01:41 +00:00
wenhua
ae23186c56 更改登录懒加载 2023-11-20 22:01:05 +08:00
wenhua
f87666b1de Merge pull request '唐简:提交新分支' (#24) from tangjian_new into dev
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/24
2023-11-20 13:25:22 +00:00
Jim__TT
f6889ac3df 唐简:提交新分支 2023-11-20 21:24:43 +08:00
wenhua
c1ae6a9e31 Merge pull request '修改api路径问题,以及部分没用变量注释(有一个暂时不敢动)' (#23) from leeiNew into dev
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/23
2023-11-20 12:54:56 +00:00
Lhk0001
95b5604801 修改api路径问题,以及部分没用变量注释(有一个暂时不敢动) 2023-11-20 20:52:43 +08:00
wenhua
9a6bd457b0 Merge pull request 'leeiNew' (#22) from leeiNew into dev
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/22
2023-11-20 12:00:28 +00:00
wenhua
3995318ff8 修复问题文件 2023-11-18 14:50:05 +08:00
Lhk0001
add8a9524d 雷的新建分支 2023-11-18 14:37:29 +08:00
clay
2b2a5c65aa ci: 测试ci 2023-11-11 17:25:38 +08:00
clay
d6b9d4baca ci: 测试ci 2023-11-11 17:25:30 +08:00
clay
3c4f562e17 ci: 测试ci 2023-11-11 17:18:44 +08:00
wenhua
b59c94fbd3 Merge pull request '11' (#17) from dev_wenhua_plus into dev
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/17
2023-11-11 09:17:05 +00:00
wenhua
4315e42521 11 2023-11-11 17:16:03 +08:00
clay
ae8e8cd9c0 ci: 测试ci 2023-11-11 17:13:56 +08:00
clay
f86a49d1e8 Merge pull request 'ci : 测试ci运行' (#14) from clay into dev
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/14
2023-11-11 08:56:05 +00:00
wenhua
ca4bc80156 Merge pull request 'feat: 新增菜单图标, 搜索组件' (#13) from dev_wenhua_plus into dev
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/13
2023-11-10 06:32:29 +00:00
wenhua
ff24a5cec9 feat: 新增菜单图标, 搜索组件 2023-11-10 14:31:25 +08:00
clay
7dbb60d774 ci : 测试ci运行 2023-11-01 22:43:52 +08:00
clay
2eeaf31cf4 Merge pull request 'ci : 测试ci运行' (#10) from clay into dev
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/10
2023-11-01 14:35:36 +00:00
clay
20c98c6ed2 ci : 测试ci运行 2023-11-01 22:34:55 +08:00
clay
0e828bd15f Merge pull request 'ci : 测试ci运行' (#9) from clay into dev
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/9
2023-11-01 14:34:18 +00:00
clay
460a5bd060 ci : 测试ci运行 2023-11-01 22:33:49 +08:00
clay
a1d8629dbe Merge pull request 'ci : 测试ci运行' (#8) from clay into dev
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/8
2023-11-01 14:32:40 +00:00
clay
e489bfa89c ci : 测试ci运行 2023-11-01 22:31:49 +08:00
wenhua
23cebffc12 温华:暂时修复打包失败问题 2023-11-01 11:30:19 +08:00
wenhua
1e768b147f 温华: 添加authstore并对数据做终端缓存 2023-10-16 14:29:50 +08:00
wenhua
4bf63e1306 温华: 添加404路由到常驻路由表 2023-10-16 14:28:58 +08:00
wenhua
72392e9571 温华: 精简Layout文件 2023-10-16 14:28:05 +08:00
wenhua
28047d7061 温华: 修改按钮权限组件为无权限移除节点 2023-10-16 14:27:35 +08:00
wenhua
60788d7090 温华: 迁移路由请求和用户信息请求到路由拦截处 2023-10-16 14:26:37 +08:00
wenhua
730b12c2e4 温华: 登录用户信息api 2023-10-16 14:25:15 +08:00
93 changed files with 9877 additions and 1338 deletions

41
.dockerignore Normal file
View File

@@ -0,0 +1,41 @@
### Java template
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### Maven template
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
.mvn/wrapper/maven-wrapper.jar
### Example user template template
### Example user template
# IntelliJ project files
.idea
*.iml
out
gen
!build
!default.conf
!nginx.conf

76
.drone.yml Normal file
View File

@@ -0,0 +1,76 @@
kind: pipeline
type: docker
name: fateverse-react
steps:
- name: build-package
image: registry.cn-chengdu.aliyuncs.com/claywang/node:16.20.0
volumes:
- name: cache
path: /drone/src/node_modules
commands:
- export CI=false
- mkdir -p ./node_modules
- export NODE_MODULES_PATH=`pwd`/node_modules
- npm config set registry https://registry.npm.taobao.org
- set NODE_OPTIONS=--openssl-legacy-provider
- npm install
- npm run build
- name: build-docker # 制作docker镜像
image: docker # 使用官方docker镜像
volumes: # 将容器内目录挂载到宿主机
- name: docker
path: /var/run/docker.sock # 挂载宿主机的docker
- name: config
path: /config
environment: # 获取到密文的docker用户名和密码
DOCKER_USERNAME:
from_secret: docker_username
DOCKER_PASSWORD:
from_secret: docker_password
REGISTRY:
from_secret: registry
REGISTRY_NAMESPACE:
from_secret: registry_namespace
commands: # 定义在Docker容器中执行的shell命令
- sed -i 's/$REGISTRY/'"$REGISTRY"'/' deployment.yml
- sed -i 's/$REGISTRY_NAMESPACE/'"$REGISTRY_NAMESPACE"'/' deployment.yml
- sed -i 's/$DRONE_REPO_NAME/'"$DRONE_REPO_NAME"'/' deployment.yml
- sed -i 's/$DRONE_COMMIT/'"$DRONE_COMMIT"'/' deployment.yml
# docker登录,不能在脚本中登录,并且不能使用docker login -u -p
- echo $DOCKER_PASSWORD | docker login $REGISTRY --username $DOCKER_USERNAME --password-stdin
- chmod +x docker.sh
- cat docker.sh
- sh docker.sh
# 执行完脚本删除本次制作的docker镜像,避免多次后当前runner空间不足
- docker rmi -f $(docker images | grep $DRONE_REPO_NAME | awk '{print $3}')
- name: drone-rancher # rancher运行
image: registry.cn-chengdu.aliyuncs.com/claywang/kubectl #阿里云的kubectl镜像,里面包含kubectl命令行工具
volumes: # 将容器内目录挂载到宿主机
- name: config
path: /app/config # 将kubectl 配置文件挂载出来
commands: # 定义在Docker容器中执行的shell命令
# 通过kubectl指令运行deployment.yml,并指定授权文件kubectl_conf.yml
- kubectl apply -f deployment.yml -n dev --kubeconfig=/app/config/hcy-text-kubectl.yml
volumes:
- name: cache
host:
path: /home/npm/fateverse-react
- name: config # k8s对接的配置文件
host:
path: /home/kubect
- name: maven-cache # maven的缓存文件
host:
path: /home/data/maven/cache
- name: docker # 宿主机中的docker
host:
path: /var/run/docker.sock
trigger:
branch:
# - prod
- dev
event:
- push

9
Dockerfile Normal file
View File

@@ -0,0 +1,9 @@
FROM nginx
RUN rm -rf /etc/nginx/nginx.conf
COPY nginx.conf /etc/nginx/
#RUN useradd -b /home/clay -m -s /bin/bash clay
#RUN chmod a+xr -R /home/clay && chown clay:clay -R /home/clay
#USER clay
COPY ./dist /home/clay
WORKDIR /home/clay
EXPOSE 80

49
deployment.yml Normal file
View File

@@ -0,0 +1,49 @@
apiVersion: v1
kind: Service
metadata:
name: $DRONE_REPO_NAME
spec:
type: ClusterIP
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: $DRONE_REPO_NAME
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: $DRONE_REPO_NAME
spec:
replicas: 1
selector:
matchLabels:
app: $DRONE_REPO_NAME
template:
metadata:
labels:
app: $DRONE_REPO_NAME
spec:
imagePullSecrets:
- name: harbor
containers:
- image: $REGISTRY/$REGISTRY_NAMESPACE/$DRONE_REPO_NAME:$DRONE_COMMIT
name: $DRONE_REPO_NAME
imagePullPolicy: Always
env:
- name: TIME_ZONE
value: Asia/Shanghai
- name: REF_NAME
value: dev
resources:
requests:
memory: 0.1Gi
cpu: 0.1
limits:
memory: 2Gi
cpu: 2
ports:
- containerPort: 8080
name: app-port

15
docker.sh Normal file
View File

@@ -0,0 +1,15 @@
#!/bin/sh
# 定义应用组名
group_name='clay'
# 定义应用名称
app_name=$DRONE_REPO_NAME
# 定义应用版本
app_version=$DRONE_COMMIT
echo ${app_version}
# 打包编译docker镜像
echo '----build image start----'
docker build -t ${group_name}/${app_name} .
echo '----build image success----'
docker tag ${group_name}/${app_name} $REGISTRY/$REGISTRY_NAMESPACE/${app_name}:${app_version}
docker push $REGISTRY/$REGISTRY_NAMESPACE/${app_name}:${app_version}
echo 'push success'

50
nginx.conf Normal file
View File

@@ -0,0 +1,50 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Allow-Methods' '*';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
listen [::]:80;
location /api {
proxy_pass http://gateway.dev.svc.cluster.local:8080;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header Host $host;
rewrite "^/api/(.*)$" /$1 break;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
}
location / {
root /home/clay;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
}

6099
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@
"babel-plugin-import": "^1.13.8",
"echarts": "^5.4.3",
"js-cookie": "^3.0.5",
"moment": "^2.29.4",
"nprogress": "^0.2.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -23,10 +24,12 @@
"react-router-dom": "^6.15.0",
"redux-persist": "^6.0.0",
"sass": "^1.64.2",
"vite-plugin-imp": "^2.4.0"
"vite-plugin-imp": "^2.4.0",
"vite-plugin-svg-icons": "^2.0.1"
},
"devDependencies": {
"@types/js-cookie": "^3.0.3",
"@types/node": "^20.9.0",
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.0.0",

View File

@@ -5,7 +5,7 @@ import ParentView from './ParentView'
import Loading from './Loading'
import currentRouter from './router'
import RouteInterception from './router/RouteInterception'
import { useDispatch, useSelector } from 'react-redux'
import { useSelector } from 'react-redux'
import { Suspense, lazy, useEffect, useState } from 'react'
const Views = import.meta.glob('./view/**/*.tsx')
const transferReactNode = (comp: string) => {

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import { useEffect, useState } from "react";
import NProgress from './mynprogress'
import { Spin } from "antd";

View File

@@ -1,9 +1,11 @@
import { Button, Form, Input, Image, message } from "antd";
import React, { useEffect, useState } from "react";
import { useEffect, useState } from "react";
import './assets/styles/login.scss'
import { getCaptchaApi, loginApi } from "./api/login";
import { useNavigate } from "react-router-dom";
import { setToken } from "./utils/auth";
import { useDispatch } from "react-redux";
import { addAuthInfo, getAuthInfo } from "./stores/auth";
type Login = {
uuid?: string,
@@ -16,8 +18,14 @@ export default function Login() {
const [messageApi, contextHolder] = message.useMessage()
const [uuid, setUuid] = useState<string>('')
const [captcha, setCaptcha] = useState<string>('')
const dispatch = useDispatch()
const navigate = useNavigate()
const requestAuthInfo = async () => {
const { payload } = await dispatch(getAuthInfo() as any)
dispatch(addAuthInfo(payload))
}
const getCaptcha = async () => {
try {
const { code, data } = await getCaptchaApi()
@@ -37,6 +45,7 @@ export default function Login() {
if (code === 1000) {
setToken(data)
messageApi.success(msg)
requestAuthInfo()
navigate('/')
return
}

View File

@@ -1,4 +1,4 @@
import React from 'react'
export default function Notfound() {
return <div>404</div>

View File

@@ -1,4 +1,4 @@
import React from 'react'
import { Outlet } from 'react-router-dom'
export default function ParentView() {

8
src/api/auth.ts Normal file
View File

@@ -0,0 +1,8 @@
import request from "../utils/request";
export const getAuthInfoApi = () => {
return request({
url: '/auth/info',
method: 'get'
})
}

59
src/api/system/dept.ts Normal file
View File

@@ -0,0 +1,59 @@
import request from "@/utils/request";
interface queryParamsType {
deptName?: string;
state?: number;
}
interface addParamsType {
deptName: string;
leader:string;
leaderId:number;
parentId?:number;
ancestors?:string
delFlag?:string
deptId?:number
email?:string
phone?:string
state?:string
}
export const getDatasAPI = (params: queryParamsType) => {
console.log(params);
return request({
url: "/admin/dept",
method: "get",
params: params,
});
};
export const editDataAPI = (params: addParamsType) => {
console.log(params);
params.leaderId=0
return request({
url: "/admin/dept",
method: "put",
data:params
});
};
export const delDataAPI=(deptId:number)=>{
console.log(deptId);
return request({
url:`admin/dept/${deptId}`,
method:'delete'
})
}
export const addDataAPI=(dept:addParamsType)=>{
console.log(dept);
dept.leaderId=0
return request({
url:`admin/dept`,
method:'post',
data:dept
})
}
export const getDataAPI=(deptId:number)=>{
return request({
url:`/admin/dept/${deptId}`,
method:'get'
})
}

23
src/api/system/notice.ts Normal file
View File

@@ -0,0 +1,23 @@
import request from "../../utils/request";
export const getNoticeListApi = (params: any) => {
return request({
url: "/notice/notify",
method: "get",
params,
});
};
export const getNoticeInfoApi = (id: number) => {
return request({
url: "/notice/notify/" + id,
method: "get",
});
};
export const deleteNoticeInfoApi = (id: number) => {
return request({
url: "/notice/notify/" + id,
method: "delete",
});
};

51
src/api/system/post.ts Normal file
View File

@@ -0,0 +1,51 @@
import request from "../../utils/request";
interface postListParams {
postCode?: string;
postName?: string;
state?: string;
pageNum?: string;
pageSize?: string;
}
export const getPostListApi = (data: postListParams) => {
const { postCode, postName, state, pageNum } = data;
const params = data
? {
postCode,
postName,
state,
pageNum,
pageSize: 500,
}
: undefined;
return request({
url: "/admin/post",
method: "get",
params,
});
};
export const addPostApi = (data: any) => {
return request({
url: "/admin/post",
method: "post",
data,
});
};
export const editPostApi = (data: any) => {
return request({
url: "/admin/post",
method: "put",
data,
});
};
export const deletePostApi = (postId: any) => {
return request({
url: "/admin/post/" + postId,
method: "delete",
});
};

67
src/api/system/publish.ts Normal file
View File

@@ -0,0 +1,67 @@
import request from "@/utils/request";
interface paramsType {
cluster?: string;
contentType?: string;
noticeTitle?: string;
noticeType?: string;
publishId?: number;
sendType?: string;
pageNum?: number;
pageSize?: number;
}
interface addParamsType {
action?: string;
cluster?: string;
contentType?: string;
noticeContent?: string;
noticeTitle?: string;
noticeType?: string;
remark?: string;
sendType?: string;
senderIds?: number[];
}
export const getDataListAPI = (params: paramsType) => {
return request({
url: "/notice/notice",
method: "get",
params: params,
});
};
export const getUserListAPI = () => {
return request({
url: "/admin/user",
method: "get",
});
};
export const getRolesListAPI = () => {
return request({
url: "/admin/role",
method: "get",
});
};
export const getDeptsListAPI = () => {
return request({
url: "/admin/dept",
method: "get",
});
};
export const addDataAPI = (addParams: addParamsType) => {
return request({
url: `/notice/notice`,
method: "post",
data: addParams,
});
};
export const deleteDataAPI = (noticeId: number) => {
return request({
url: `/notice/notice/${noticeId}`,
method: "delete",
});
};
export const getDataAPI = (noticeId: number) => {
return request({
url: `/notice/notice/${noticeId}`,
method: "get",
});
};

75
src/api/system/role.ts Normal file
View File

@@ -0,0 +1,75 @@
import request from "@/utils/request";
interface paramsType {
createTime?: string;
dataScope?: string;
deptIds?: any[];
menuIds?: any[];
roleId?: number;
roleKey?: string;
roleName?: string;
state?: string;
endTime?: string;
startTime?: string;
pageNum?: number;
pageSize?: number;
}
export const getRolesDataAPI = (params: paramsType) => {
const { endTime, roleKey, roleName, startTime, state, pageNum, pageSize } =
params;
// console.log('API',params);
return request({
url: "/admin/role",
method: "get",
params: {
endTime,
roleKey,
roleName,
startTime,
state,
pageNum,
pageSize,
},
});
};
export const delRoleAPI = (roleId: paramsType) => {
// console.log(roleId);
return request({
url: `/admin/role/${roleId}`,
method: "delete",
});
};
export const getMenuLiseAPI = () => {
return request({
url: "/admin/menu",
method: "get",
});
};
export const addRoleAPI = (data: any) => {
// console.log('addAPI',data);
return request({
url: `/admin/role`,
method: "post",
data,
});
};
export const getRoleAPI = (params: paramsType) => {
const { roleId } = params;
return request({
url: `/admin/role/${roleId}`,
method: "get",
});
};
export const setRoleDataAPI = (data: any) => {
// data.menuIds=[]
// const {roleId}=params
console.log("修改", data);
return request({
url: `/admin/role`,
method: "put",
data: data,
});
};

86
src/api/system/users.ts Normal file
View File

@@ -0,0 +1,86 @@
import request from "../../utils/request";
interface userListParams {
deptId?: string;
userName?: string;
phoneNumber?: string;
state?: string;
startTime?: string;
endTime?: string;
pageNum?: number;
pageSize?: number;
}
export const getDeptTreeApi = () => {
return request({
url: "/admin/dept",
method: "get",
});
};
export const getUserInfoApi = (userId: string) => {
return request({
url: "/admin/user/info/" + userId,
method: "get",
});
};
export const getRoleListApi = () => {
return request({
url: "/admin/role",
method: "get",
});
};
export const getPostListApi = () => {
return request({
url: "/admin/post",
method: "get",
});
};
export const getUserListApi = (data: userListParams) => {
const { deptId, userName, phoneNumber, state, startTime, endTime, pageNum } =
data;
const params = data
? {
deptId,
userName,
phoneNumber,
state,
startTime,
endTime,
pageNum,
pageSize: 500,
}
: undefined;
return request({
url: "/admin/user",
method: "get",
params,
});
};
export const addUserApi = (data: any) => {
return request({
url: "/admin/user",
method: "post",
data,
});
};
export const editUserApi = (data: any) => {
return request({
url: "/admin/user",
method: "put",
data,
});
};
export const deleteUserApi = (userId: any) => {
return request({
url: "/admin/user/" + userId,
method: "delete",
});
};

View File

@@ -3,4 +3,5 @@
height: calc(100vh - 64px);
padding: 20px;
box-sizing: border-box;
overflow: auto;
}

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689263020512" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2369" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M704 128 704 192 832 192 832 320 896 320 896 192 896 128Z" p-id="2370"></path><path d="M832 832 704 832 704 896 896 896 896 832 896 704 832 704Z" p-id="2371"></path><path d="M192 704 128 704 128 832 128 896 320 896 320 832 192 832Z" p-id="2372"></path><path d="M192 192 320 192 320 128 128 128 128 192 128 320 192 320Z" p-id="2373"></path><path d="M256 319.808l0 384.384C256 739.008 284.544 768 319.808 768l384.384 0C739.008 768 768 739.456 768 704.192L768 319.808C768 284.992 739.456 256 704.192 256L319.808 256C284.992 256 256 284.544 256 319.808zM320 320l384 0 0 384L320 704 320 320z" p-id="2374"></path></svg>

After

Width:  |  Height:  |  Size: 944 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M258.133333 128A106.666667 106.666667 0 0 0 362.666667 213.333333h298.666666a106.666667 106.666667 0 0 0 104.533334-85.333333h87.424A128 128 0 0 1 981.333333 255.744v597.845333A127.872 127.872 0 0 1 853.290667 981.333333H170.666667A128 128 0 0 1 42.666667 853.589333V255.744A127.872 127.872 0 0 1 170.709333 128H258.133333zM298.666667 384a42.666667 42.666667 0 1 0 0 85.333333h256a42.666667 42.666667 0 0 0 0-85.333333H298.666667z m0 170.666667a42.666667 42.666667 0 0 0 0 85.333333h426.666666a42.666667 42.666667 0 0 0 0-85.333333H298.666667z m0 170.666666a42.666667 42.666667 0 0 0 0 85.333334h256a42.666667 42.666667 0 0 0 0-85.333334H298.666667zM362.666667 42.666667h298.666666a64 64 0 0 1 0 128h-298.666666a64 64 0 0 1 0-128z" /></svg>

After

Width:  |  Height:  |  Size: 1020 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M186.4 478.4c-44-0.8-80 33.6-81.6 77.6v128H63.2C30.4 683.2 3.2 708.8 2.4 742.4v170.4c0.8 32.8 28 58.4 60.8 57.6h165.6c32.8 0.8 60-24.8 60.8-58.4V742.4c-0.8-32.8-28-59.2-60.8-58.4h-54.4V545.6h302.4v139.2h-48c-32.8-0.8-60 24.8-61.6 58.4v170.4c0.8 32.8 28 58.4 61.6 57.6h166.4c32.8 0.8 60-24.8 60.8-58.4V742.4c-0.8-32.8-28-59.2-60.8-58.4h-48V545.6h302.4v139.2h-54.4c-32.8-0.8-60 24.8-60.8 58.4v170.4c0.8 32.8 28 58.4 60.8 57.6h166.4c32.8 0.8 60-24.8 60.8-58.4V742.4c-0.8-32.8-28-59.2-61.6-58.4h-41.6v-128c-0.8-44-37.6-78.4-81.6-77.6H186.4z m20.8 272.8c7.2 0 12.8 5.6 12.8 12v128.8c0 7.2-5.6 12-12.8 12H84.8c-7.2 0-12.8-5.6-12.8-12V763.2c0-7.2 5.6-12 12.8-12h122.4z m366.4 0c7.2 0 12.8 5.6 12.8 12v128.8c0 7.2-5.6 12-12.8 12H450.4c-7.2 0-12.8-5.6-12.8-12V763.2c0-7.2 5.6-12 12.8-12h123.2z m378.4 140.8c0 7.2-5.6 12-12.8 12h-122.4c-7.2 0-12.8-5.6-12.8-12V763.2c0-7.2 5.6-12 12.8-12h122.4c7.2 0 12.8 5.6 12.8 12v128.8zM671.2 251.2c-4.8-6.4-4.8-15.2 0-21.6l37.6-45.6-4-10.4-4.8-10.4c-3.2-8-6.4-15.2-9.6-20-4.8-10.4-11.2-20-19.2-28l-7.2-7.2-57.6 8c-8.8 0.8-16.8-4.8-20-12.8L568 50.4l-11.2-1.6c-28.8-6.4-57.6-6.4-86.4 0l-9.6 1.6-20 54.4c-3.2 7.2-10.4 11.2-18.4 11.2h-1.6l-56.8-9.6-8 8c-15.2 18.4-27.2 39.2-35.2 61.6l-4 9.6 37.6 45.6c4.8 5.6 4.8 14.4 0 20.8L315.2 296l4 9.6c3.2 12.8 8.8 24.8 15.2 35.2 4.8 9.6 11.2 18.4 18.4 26.4l7.2 7.2 59.2-9.6c8-0.8 15.2 3.2 18.4 11.2l19.2 54.4 11.2 1.6c28.8 6.4 59.2 6.4 88 0l9.6-2.4 20-56c3.2-7.2 10.4-12 18.4-11.2l57.6 9.6 8-7.2c15.2-17.6 26.4-37.6 34.4-60l4-9.6-36.8-44z m-157.6 71.2c-45.6 0-82.4-36.8-82.4-82.4 0-45.6 36.8-82.4 82.4-82.4 45.6 0 82.4 36.8 82.4 82.4 0 45.6-36.8 81.6-82.4 82.4z" /></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689758652423" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2526" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M245.333333 341.333333c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h320c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32h-320zM480 490.666667h-234.666667c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h234.666667c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32zM394.666667 704c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32h-149.333334c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h149.333334zM974.933333 650.666667L883.2 490.666667c-8.533333-12.8-21.333333-21.333333-36.266667-21.333334H661.333333c-14.933333 0-29.866667 8.533333-36.266666 21.333334L533.333333 650.666667c-8.533333 12.8-8.533333 29.866667 0 42.666666l91.733334 160c8.533333 12.8 21.333333 21.333333 36.266666 21.333334h185.6c14.933333 0 29.866667-8.533333 36.266667-21.333334l91.733333-160c8.533333-12.8 8.533333-29.866667 0-42.666666zM834.133333 810.666667h-160l-81.066666-138.666667 81.066666-138.666667h160l81.066667 138.666667-81.066667 138.666667z" p-id="2527"></path><path d="M791.466667 625.066667c-2.133333-4.266667-6.4-6.4-10.666667-6.4h-46.933333c-4.266667 0-8.533333 2.133333-10.666667 6.4l-23.466667 40.533333c-2.133333 4.266667-2.133333 8.533333 0 12.8l23.466667 40.533333c2.133333 4.266667 6.4 6.4 10.666667 6.4h46.933333c4.266667 0 8.533333-2.133333 10.666667-6.4l23.466666-40.533333c2.133333-4.266667 2.133333-8.533333 0-12.8l-23.466666-40.533333zM480 810.666667H106.666667V213.333333h597.333333v160c0 17.066667 14.933333 32 32 32s32-14.933333 32-32V192c0-23.466667-19.2-42.666667-42.666667-42.666667H85.333333c-23.466667 0-42.666667 19.2-42.666666 42.666667v640c0 23.466667 19.2 42.666667 42.666666 42.666667h394.666667c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32z" p-id="2528"></path></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M758.969877 1017.428352H58.739777a35.205133 35.205133 0 0 1-35.205134-35.205134V154.902586a35.205133 35.205133 0 0 1 35.205134-35.205133h700.2301a35.205133 35.205133 0 0 1 35.205134 35.205133v177.609898a35.205133 35.205133 0 0 1-70.410267 0V190.10772H93.94491v756.910365h629.819834V805.376099a35.205133 35.205133 0 0 1 70.410267 0V982.223218a35.205133 35.205133 0 0 1-35.205134 35.205134zM503.321935 70.410267v45.766673h-188.934216V70.410267h188.934216m0-70.410267h-188.934216a70.410267 70.410267 0 0 0-70.410266 70.410267v45.766673a70.410267 70.410267 0 0 0 70.410266 70.410266h188.934216a70.410267 70.410267 0 0 0 70.410266-70.410266V70.410267a70.410267 70.410267 0 0 0-70.410266-70.410267zM402.400553 428.329122h-199.495756a35.205133 35.205133 0 1 1 0-70.410267h199.495756a35.205133 35.205133 0 0 1 0 70.410267zM369.542428 603.181283h-166.637631a35.205133 35.205133 0 0 1 0-70.410266h166.637631a35.205133 35.205133 0 0 1 0 70.410266zM627.713406 778.033445h-424.808609a35.205133 35.205133 0 0 1 0-70.410266h424.808609a35.205133 35.205133 0 0 1 0 70.410266zM893.488692 1023.999977a107.187896 107.187896 0 0 1-96.415125-60.200778l-82.473893-169.137196h-3.344487a251.787113 251.787113 0 0 1-225.993486-141.219524 249.275814 249.275814 0 0 1-21.017464-157.00316 253.383079 253.383079 0 0 1 74.482327-136.04437 35.205133 35.205133 0 0 1 55.788401 10.197754L663.094564 511.272416l52.479119-25.582397L646.947143 345.010306a35.205133 35.205133 0 0 1 26.33344-50.237725 253.559105 253.559105 0 0 1 153.09539 25.12473 249.240609 249.240609 0 0 1 110.731879 113.278384 251.810583 251.810583 0 0 1-30.006509 267.641158l82.509098 169.184136a107.070545 107.070545 0 0 1-96.121749 153.998988zM735.933985 722.855266a35.205133 35.205133 0 0 1 31.625945 19.785285l92.800731 190.354156a36.672014 36.672014 0 0 0 65.88054-32.130552l-92.706851-190.36589a35.205133 35.205133 0 0 1 6.571625-40.145587 181.200821 181.200821 0 0 0 33.761722-206.325551 180.097727 180.097727 0 0 0-139.377122-100.31116l59.848726 122.7603a35.205133 35.205133 0 0 1-16.217831 47.080998l-115.766213 56.433829a35.205133 35.205133 0 0 1-47.092734-16.217832l-59.848726-122.795505a181.118676 181.118676 0 0 0 176.471598 272.11221 36.378638 36.378638 0 0 1 4.04859-0.234701z" /></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

1
src/assets/svg/chart.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1690337061706" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2298" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M888 792H200V168c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v688c0 4.4 3.6 8 8 8h752c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z" p-id="2299"></path><path d="M288 712h56c4.4 0 8-3.6 8-8V560c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v144c0 4.4 3.6 8 8 8zM440 712h56c4.4 0 8-3.6 8-8V384c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v320c0 4.4 3.6 8 8 8zM592 712h56c4.4 0 8-3.6 8-8V462c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v242c0 4.4 3.6 8 8 8zM744 712h56c4.4 0 8-3.6 8-8V304c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v400c0 4.4 3.6 8 8 8z" p-id="2300"></path></svg>

After

Width:  |  Height:  |  Size: 858 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689752871415" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2396" width="48" height="48" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M896 64H128c-35.296 0-64 28.704-64 64v768c0 35.296 28.704 64 64 64h592a32 32 0 1 0 0-64H128V128h768v592a32 32 0 1 0 64 0V128c0-35.296-28.704-64-64-64z" p-id="2397"></path><path d="M791.744 746.496A206.752 206.752 0 0 0 832 624c0-114.688-93.312-208-208-208S416 509.312 416 624s93.312 208 208 208a206.752 206.752 0 0 0 122.496-40.256l110.88 110.88a31.904 31.904 0 0 0 45.248 0 31.968 31.968 0 0 0 0-45.248l-110.88-110.88zM480 624c0-79.392 64.608-144 144-144s144 64.608 144 144-64.608 144-144 144-144-64.608-144-144zM800 264a32 32 0 0 0-32-32H256a32 32 0 0 0 0 64h512a32 32 0 0 0 32-32zM256 422.656a32 32 0 0 0 0 64h96a32 32 0 0 0 0-64H256z" p-id="2398"></path></svg>

After

Width:  |  Height:  |  Size: 995 B

1
src/assets/svg/clear.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689262997739" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1907" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M987.428571 268.190476h-950.857142a36.571429 36.571429 0 0 1 0-73.142857H243.809524V73.142857a73.142857 73.142857 0 0 1 73.142857-73.142857h390.095238a73.142857 73.142857 0 0 1 73.142857 73.142857v121.904762h207.238095a36.571429 36.571429 0 0 1 0 73.142857zM707.047619 121.904762a48.761905 48.761905 0 0 0-48.761905-48.761905H365.714286a48.761905 48.761905 0 0 0-48.761905 48.761905v73.142857h390.095238V121.904762zM341.333333 816.761905v-390.095238a36.571429 36.571429 0 0 1 73.142857 0v390.095238a36.571429 36.571429 0 0 1-73.142857 0z m268.190477 0v-390.095238a36.571429 36.571429 0 0 1 73.142857 0v390.095238a36.571429 36.571429 0 0 1-73.142857 0zM158.47619 316.952381a36.254476 36.254476 0 0 1 34.133334 24.380952H195.047619v560.761905a48.761905 48.761905 0 0 0 48.761905 48.761905h536.380952a48.761905 48.761905 0 0 0 48.761905-48.761905V341.333333h2.438095a36.059429 36.059429 0 0 1 68.266667 0h2.438095v609.52381a73.142857 73.142857 0 0 1-73.142857 73.142857H195.047619a73.142857 73.142857 0 0 1-73.142857-73.142857V341.333333h2.438095a36.254476 36.254476 0 0 1 34.133333-24.380952z" fill="#333333" p-id="1908"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

1
src/assets/svg/copy.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689263581410" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2531" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M746.932 698.108" fill="#A9B7B7" p-id="2532"></path><path d="M925.731 288.698c-1.261-1.18-3.607-3.272-6.902-6.343-5.486-5.112-11.615-10.758-18.236-16.891-18.921-17.526-38.003-35.028-56.046-51.397-2.038-1.848-2.038-1.835-4.077-3.682-24.075-21.795-44.156-39.556-58.996-52.076-8.682-7.325-15.517-12.807-20.539-16.426-3.333-2.402-6.043-4.13-8.715-5.396-3.365-1.595-6.48-2.566-10.905-2.483C729.478 134.227 720 143.77 720 155.734l0 42.475 0 42.475 0 84.95L720 347l21.205 0L890 347l0 595L358.689 942C323.429 942 295 913.132 295 877.922L295 177l361.205 0c11.736 0 21.25-9.771 21.25-21.5s-9.514-21.5-21.25-21.5l-382.5 0L252 134l0 21.734L252 813l-52.421 0C166.646 813 140 786.928 140 754.678L140 72l566.286 0C739.29 72 766 98.154 766 130.404L766 134l40 0 0-3.596C806 76.596 761.271 33 706.286 33L119.958 33 100 33l0 19.506 0 702.172C100 808.463 144.642 852 199.579 852L252 852l0 25.922C252 936.612 299.979 984 358.689 984l552.515 0L932 984l0-21.237L932 325.635 932 304l0.433 0C932.432 299 930.196 292.878 925.731 288.698zM762 304l0-63.315L762 198.21l0-0.273c14 11.479 30.3 26.369 49.711 43.942 2.022 1.832 2.136 1.832 4.157 3.665 17.923 16.259 36.957 33.492 55.779 50.926 2.878 2.666 5.713 5.531 8.391 7.531L762 304.001z" fill="#272636" p-id="2533"></path><path d="M816.936 436 407.295 436c-10.996 0-19.91 8.727-19.91 19.5 0 10.77 8.914 19.5 19.91 19.5l409.641 0c11 0 19.914-8.73 19.914-19.5C836.85 444.727 827.936 436 816.936 436z" fill="#272636" p-id="2534"></path><path d="M816.936 553 407.295 553c-10.996 0-19.91 8.727-19.91 19.5 0 10.774 8.914 19.5 19.91 19.5l409.641 0c11 0 19.914-8.726 19.914-19.5C836.85 561.727 827.936 553 816.936 553z" fill="#272636" p-id="2535"></path><path d="M816.936 689 407.295 689c-10.996 0-19.91 8.729-19.91 19.503 0 10.769 8.914 19.497 19.91 19.497l409.641 0c11 0 19.914-8.729 19.914-19.497C836.85 697.729 827.936 689 816.936 689z" fill="#272636" p-id="2536"></path></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M768.7 719H255.3c-43.2 0-78.3-35.1-78.3-78.3V267.3c0-43.2 35.1-78.3 78.3-78.3h513.5c43.2 0 78.3 35.1 78.3 78.3v373.5c-0.1 43.1-35.2 78.2-78.4 78.2zM255.3 239c-15.6 0-28.3 12.7-28.3 28.3v373.5c0 15.6 12.7 28.3 28.3 28.3h513.5c15.6 0 28.3-12.7 28.3-28.3V267.3c0-15.6-12.7-28.3-28.3-28.3H255.3zM847 785H177c-13.8 0-25 11.2-25 25s11.2 25 25 25h670c13.8 0 25-11.2 25-25s-11.2-25-25-25zM701.8 564.5H532.2c-13.8 0-25 11.2-25 25s11.2 25 25 25h169.6c13.8 0 25-11.2 25-25s-11.2-25-25-25zM455.2 300.4c-6.4 0-12.8 2.4-17.7 7.3L303.2 442.1c-9.8 9.8-9.8 25.6 0 35.4l134.4 134.4c4.9 4.9 11.3 7.3 17.7 7.3s12.8-2.4 17.7-7.3c9.8-9.8 9.8-25.6 0-35.4L356.2 459.8l116.7-116.7c9.8-9.8 9.8-25.6 0-35.4-4.9-4.8-11.3-7.3-17.7-7.3z" /></svg>

After

Width:  |  Height:  |  Size: 996 B

1
src/assets/svg/data.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689756990510" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1866" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M887.808 137.92c21.312 21.312 32.256 47.68 32.832 79.168v593.28c-0.576 31.488-11.52 57.856-32.832 79.168s-47.68 32.256-79.168 32.832h-593.28c-31.488-0.576-57.92-11.52-79.168-32.832-21.312-21.312-32.256-47.68-32.832-79.168v-593.28c0.576-31.488 11.52-57.856 32.832-79.168s47.68-32.256 79.168-32.832h593.28c31.488 0.64 57.856 11.584 79.168 32.832zM383.36 564.48c0-14.592-4.224-26.56-12.672-35.904-8.448-9.344-21.12-14.016-38.08-14.016-16.896 0-30.208 4.672-39.808 14.016s-12.992 21.312-10.048 35.904v128.64c0 14.016 4.8 25.792 14.464 35.456s21.568 14.464 35.904 14.464c14.272 0 26.112-4.8 35.456-14.464 9.344-9.6 14.272-21.44 14.848-35.456V564.48z m173.248-230.08c0-14.016-4.8-25.792-14.464-35.456s-21.568-14.464-35.904-14.464c-14.272 0-26.112 4.8-35.456 14.464-9.344 9.6-14.272 21.44-14.848 35.456v358.72c0.576 14.016 5.568 25.792 14.848 35.456 9.344 9.6 21.12 14.464 35.456 14.464 14.272 0 26.24-4.8 35.904-14.464s14.464-21.44 14.464-35.456V334.4zM736 508.48c-0.576-14.592-6.4-26.56-17.472-35.904s-23.936-14.016-38.528-14.016c-14.592 0-26.24 4.672-35.008 14.016s-11.968 21.312-9.6 35.904v184.64c0 14.016 4.8 25.792 14.464 35.456s21.568 14.464 35.904 14.464c14.272 0 26.112-4.8 35.456-14.464 9.344-9.6 14.272-21.44 14.848-35.456V508.48z" p-id="1867"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689753422872" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5415" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M489.6 727.2h421.1c26.9 0 49.3 22.4 49.3 53.8v80.6c0 26.9-22.4 53.8-49.3 53.8H489.6c-26.9 0-49.3-22.4-49.3-53.8v-9H158.1c-17.9 0-31.4-13.4-31.4-31.4v-524h-13.4c-26.9 0-49.3-26.9-49.3-53.8v-80.6c0-31.4 22.4-53.8 49.3-53.8h85.1c26.9 0 49.3 22.4 49.3 53.8v80.6c0 26.9-22.4 53.8-49.3 53.8H185v188.2h250.9v-9c0-26.9 22.4-53.8 49.3-53.8h421.1c26.9 0 49.3 22.4 49.3 53.8V557c0 26.9-22.4 53.8-49.3 53.8H485.1c-26.9 0-49.3-22.4-49.3-53.8v-9H185v246.4h250.9V781c-0.1-26.9 22.3-53.8 53.7-53.8z m421.1-430h-551c-31.4 0-53.8-26.9-53.8-53.8v-80.6c0-31.4 22.4-53.8 53.8-53.8h551c26.9 0 49.3 22.4 49.3 53.8v80.6c0 26.9-22.4 53.8-49.3 53.8z" p-id="5416" fill="#ffffff"></path></svg>

After

Width:  |  Height:  |  Size: 996 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M525.158 921.6a283.351 283.351 0 0 0 55.296 51.2H145.06a93.02 93.02 0 0 1-93.86-92.16V143.36a93.02 93.02 0 0 1 93.86-92.16h682.675a93.025 93.025 0 0 1 93.865 92.16v381.798a281.795 281.795 0 0 0-51.2-33.638V324.27h-768v555.52a42.24 42.24 0 0 0 42.66 41.81h380.098zM870.4 273.065v-128.87a42.24 42.24 0 0 0-42.665-41.795H145.06a42.24 42.24 0 0 0-42.66 41.795v128.87h768z m-358.4-51.2a34.135 34.135 0 1 1 34.13-34.135A34.13 34.13 0 0 1 512 221.865z m119.465 0A34.135 34.135 0 1 1 665.6 187.73a34.13 34.13 0 0 1-34.135 34.135z m119.47 0a34.135 34.135 0 1 1 34.13-34.135 34.13 34.13 0 0 1-34.13 34.135zM449.418 441.283L302.592 569.088a30.136 30.136 0 0 1-41.938 0l-59.684-51.779a28.001 28.001 0 0 1-3.236-38.83v-1.618a25.912 25.912 0 0 1 37.12-3.246l46.777 40.448 135.516-116.475h1.618a27.648 27.648 0 0 1 37.12 4.864c8.043 12.934 6.436 30.72-6.467 38.83z m-3.65 224.558L298.952 793.656a30.136 30.136 0 0 1-41.928 0l-59.694-51.768a28.001 28.001 0 0 1-3.251-38.83v-1.618a25.928 25.928 0 0 1 37.125-3.246l46.781 40.474 135.522-116.501h1.607a27.617 27.617 0 0 1 37.12 4.864c8.054 12.933 6.441 30.72-6.466 38.825zM742.4 972.8a230.4 230.4 0 1 1 230.4-230.4 230.4 230.4 0 0 1-230.4 230.4z m0-51.2a179.2 179.2 0 1 0-179.2-179.2 179.2 179.2 0 0 0 179.2 179.2z m-8.704-170.21l93.952-1.3a25.242 25.242 0 1 1 0.886 50.473l-119.742 1.654a27.832 27.832 0 0 1-26.112-27.515V656.983a25.513 25.513 0 1 1 51.026 0v94.413z m0 0" /></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

1
src/assets/svg/fabu.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1690372430043" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2365" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M682.47 101c72.01 0 130.041 59.663 131.194 133.157l0.018 2.231v467.066c0 16.519-13.383 29.91-29.891 29.91-16.273 0-29.509-13.012-29.883-29.204l-0.009-0.706V236.388c0-41.574-31.608-74.895-70.255-75.557l-1.174-0.01H202.212c-38.787 0-70.784 32.905-71.42 74.31l-0.01 1.257v601.224c0 41.574 31.609 74.895 70.256 75.557l1.174 0.01h642.917c21.34 0 38.686-17.111 39.082-38.37l0.007-0.743V199.292c0-16.52 13.382-29.91 29.89-29.91 16.273 0 29.509 13.01 29.892 29.204v675.48c0 54.093-43.385 98.047-97.236 98.92l-1.635 0.014H202.212c-72.01 0-130.041-59.663-131.195-133.157L71 837.612V236.388c0-73.768 57.311-134.17 129.034-135.37l2.178-0.018H682.47z m-78.027 616.612c12.7 0 22.994 10.301 22.994 23.008 0 12.492-9.949 22.658-22.35 23l-0.644 0.008H289.436c-12.699 0-22.993-10.301-22.993-23.008 0-12.491 9.948-22.658 22.35-22.999l0.643-0.009h315.007z m0-138.047c12.7 0 22.994 10.3 22.994 23.008 0 12.491-9.949 22.658-22.35 22.999l-0.644 0.008H289.436c-12.699 0-22.993-10.3-22.993-23.007 0-12.492 9.948-22.658 22.35-23l0.643-0.008h315.007z m0-138.048c12.7 0 22.994 10.301 22.994 23.008 0 12.492-9.949 22.658-22.35 23l-0.644 0.008H289.436c-12.699 0-22.993-10.301-22.993-23.008 0-12.491 9.948-22.658 22.35-22.999l0.643-0.009h315.007z m-75.876-132.181c12.7 0 22.994 10.3 22.994 23.008 0 12.491-9.949 22.658-22.35 22.999l-0.644 0.009H366.343c-12.699 0-22.993-10.301-22.993-23.008 0-12.492 9.948-22.658 22.35-23l0.643-0.008h162.224z" fill="#333333" p-id="2366"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

1
src/assets/svg/fit.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689263007214" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2215" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M597.333333 405.376c0-0.128 0.085333-0.256 0.085333-0.384C597.418667 404.821333 597.333333 404.650667 597.333333 404.522667L597.333333 192.768C597.333333 180.522667 606.933333 170.538667 618.666667 170.666667c11.733333-0.042667 21.376 9.813333 21.376 22.144L640.042667 384l191.189333 0c12.16 0 22.186667 9.6 22.101333 21.333333 0 11.733333-9.813333 21.333333-22.186667 21.333333l-211.754667 0c-0.128 0-0.256-0.085333-0.384-0.085333S618.837333 426.666667 618.709333 426.666667c-4.821333 0-9.045333-1.962667-12.629333-4.736-0.853333-0.64-1.749333-0.981333-2.432-1.706667-0.213333-0.213333-0.298667-0.469333-0.512-0.725333C599.68 415.744 597.333333 410.837333 597.333333 405.376zM362.666667 170.666667C350.933333 170.624 341.290667 180.48 341.290667 192.810667L341.290667 384 150.101333 384C137.898667 384 127.914667 393.6 128 405.333333 128 417.066667 137.813333 426.666667 150.186667 426.666667l211.754667 0c0.128 0 0.256-0.085333 0.384-0.085333S362.496 426.666667 362.624 426.666667c4.821333 0 9.045333-1.962667 12.629333-4.736 0.853333-0.64 1.749333-0.981333 2.432-1.706667 0.213333-0.213333 0.298667-0.469333 0.512-0.725333C381.653333 415.744 384 410.837333 384 405.376c0-0.128-0.085333-0.256-0.085333-0.384C383.914667 404.821333 384 404.650667 384 404.522667L384 192.768C384 180.522667 374.4 170.538667 362.666667 170.666667zM831.146667 640l-211.754667 0c-0.128 0-0.256 0.085333-0.384 0.085333S618.837333 640 618.709333 640c-4.821333 0-9.088 2.005333-12.672 4.778667-0.810667 0.597333-1.706667 0.938667-2.389333 1.664-0.213333 0.213333-0.298667 0.469333-0.512 0.725333C599.68 650.922667 597.333333 655.829333 597.333333 661.290667c0 0.128 0.085333 0.256 0.085333 0.384 0 0.170667-0.085333 0.341333-0.085333 0.469333l0 211.797333c0 12.202667 9.6 22.229333 21.333333 22.101333 11.733333 0.042667 21.376-9.813333 21.376-22.144L640.042667 682.666667l191.189333 0c12.16 0 22.186667-9.6 22.101333-21.333333C853.333333 649.6 843.52 640 831.146667 640zM378.154667 647.168c-0.213333-0.256-0.298667-0.512-0.512-0.725333-0.682667-0.725333-1.578667-1.066667-2.389333-1.664C371.712 642.005333 367.445333 640 362.624 640c-0.128 0-0.213333 0.085333-0.341333 0.085333S362.026667 640 361.898667 640L150.186667 640C137.813333 640 128 649.6 128 661.333333 127.914667 673.066667 137.898667 682.666667 150.101333 682.666667l191.189333 0 0 191.189333c0 12.288 9.642667 22.186667 21.376 22.144C374.4 896.128 384 886.144 384 873.898667l0-211.797333c0-0.128-0.085333-0.298667-0.085333-0.469333C383.914667 661.546667 384 661.418667 384 661.290667 384 655.829333 381.653333 650.922667 378.154667 647.168zM490.666667 469.333333c-35.328 0-64 28.672-64 64s28.672 64 64 64 64-28.672 64-64S525.994667 469.333333 490.666667 469.333333z" fill="#020202" p-id="2216"></path></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M136.3 410.2h70v150.2h-70zM206.3 851.3V733.8h-70v117.5c0 40.7 31.5 73.9 70.2 73.9h206.1v-70H207.5c-0.5-0.6-1.2-1.9-1.2-3.9zM206.3 139c0-2 0.7-3.3 1.1-3.9h619.8c0.5 0.6 1.1 1.8 1.1 3.9v151.1h70V139c0-40.7-31.5-73.9-70.2-73.9H206.5c-38.7 0-70.2 33.1-70.2 73.9v97.9h70V139zM886.8 677.2c-20-20-43-35.9-68-47.2 19.2-22.2 30.9-51.2 30.9-82.8 0-69.9-56.8-126.7-126.7-126.7s-126.7 56.8-126.7 126.7c0 31.6 11.7 60.6 30.9 82.8-25 11.4-48 27.2-68 47.2-43.8 43.8-67.9 101.9-67.9 163.8 0 6.1 0.2 12.2 0.7 18.2l69.8-5.5c-0.3-4.2-0.5-8.5-0.5-12.7 0-89.1 72.5-161.6 161.6-161.6S884.6 751.9 884.6 841c0 4.3-0.2 8.6-0.5 12.7l69.8 5.5c0.5-6 0.7-12.1 0.7-18.2 0-61.9-24.1-120-67.8-163.8zM723 490.5c31.3 0 56.7 25.4 56.7 56.7s-25.4 56.7-56.7 56.7-56.7-25.4-56.7-56.7 25.4-56.7 56.7-56.7zM93.9 288.5h154.8v70H93.9zM93.9 612.1h154.8v70H93.9z" /></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M564.906667 534.613333a216.746667 216.746667 0 1 1 216.746666-216.746666 217.173333 217.173333 0 0 1-216.746666 216.746666z m0-366.506666a149.76 149.76 0 1 0 149.76 149.76 150.186667 150.186667 0 0 0-149.76-149.76z" /><path fill="#ffffff" d="M216.32 896a33.706667 33.706667 0 0 1-33.28-33.706667c0-185.6 157.866667-392.96 384-392.96a33.706667 33.706667 0 0 1 0 66.986667c-181.76 0-317.866667 170.666667-317.866667 325.973333a33.706667 33.706667 0 0 1-32.853333 33.706667zM316.586667 488.96c-56.32 0-125.013333-72.106667-125.013334-156.586667A157.013333 157.013333 0 0 1 256 207.36 33.28 33.28 0 0 1 301.226667 213.333333a32.853333 32.853333 0 0 1-6.826667 47.36 87.893333 87.893333 0 0 0-35.84 71.68 97.28 97.28 0 0 0 58.026667 89.6 33.706667 33.706667 0 0 1 33.28 33.706667 33.28 33.28 0 0 1-33.28 33.28z" /><path fill="#ffffff" d="M115.2 725.333333a33.28 33.28 0 0 1-33.28-33.28c0-128 102.4-271.786667 234.666667-271.786666a33.706667 33.706667 0 0 1 0 66.986666c-90.453333 0-167.68 112.213333-167.68 204.8a33.706667 33.706667 0 0 1-33.706667 33.28zM810.666667 779.093333a128 128 0 1 1 130.133333-128 128 128 0 0 1-130.133333 128z m0-192.853333a63.146667 63.146667 0 1 0 63.146666 63.146667A63.146667 63.146667 0 0 0 810.666667 586.24z" /><path fill="#ffffff" d="M773.973333 724.906667m32.426667 0l4.266667 0q32.426667 0 32.426666 32.426666l0 145.493334q0 32.426667-32.426666 32.426666l-4.266667 0q-32.426667 0-32.426667-32.426666l0-145.493334q0-32.426667 32.426667-32.426666Z" /><path fill="#ffffff" d="M912.64 809.813333m0 34.56l0 0q0 34.56-34.56 34.56l-69.973333 0q-34.56 0-34.56-34.56l0 0q0-34.56 34.56-34.56l69.973333 0q34.56 0 34.56 34.56Z" /></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M591.644444 574.577778l-85.333333 85.333333-136.533333-136.533333 79.644444-79.644445-39.822222-39.822222-79.644444 79.644445-79.644445-79.644445-96.711111 96.711111a261.688889 261.688889 0 0 0-22.755556 347.022222L0 984.177778l39.822222 39.822222L170.666667 893.155556c45.511111 39.822222 102.4 56.888889 159.288889 56.888888 68.266667 0 136.533333-28.444444 187.733333-79.644444l96.711111-96.711111-73.955556-73.955556 85.333334-85.333333-34.133334-39.822222z m-113.777777 250.311111c-39.822222 39.822222-91.022222 62.577778-142.222223 62.577778-51.2 0-102.4-17.066667-136.533333-56.888889l-5.688889-5.688889c-73.955556-73.955556-73.955556-199.111111 5.688889-278.755556l51.2-51.2 278.755556 278.755556-51.2 51.2zM978.488889 91.022222l-39.822222-39.822222L813.511111 170.666667c-45.511111-39.822222-102.4-56.888889-159.288889-56.888889-68.266667 0-136.533333 28.444444-187.733333 79.644444L369.777778 284.444444l369.777778 369.777778 91.022222-91.022222a261.688889 261.688889 0 0 0 22.755555-347.022222l125.155556-125.155556z m-193.422222 426.666667l-45.511111 45.511111L460.8 284.444444l45.511111-45.511111c39.822222-39.822222 91.022222-62.577778 142.222222-62.577777 51.2 0 102.4 17.066667 136.533334 56.888888l5.688889 5.688889c79.644444 73.955556 73.955556 199.111111-5.688889 278.755556z" /></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M790.528 464h2.944a232 232 0 1 0-2.944 0zM793.472 64a168 168 0 0 1 0 336h-2.944A168 168 0 1 1 793.472 64zM425.6 560H38.4A38.432 38.432 0 0 0 0 598.4v387.2A38.464 38.464 0 0 0 38.4 1024h387.2a38.464 38.464 0 0 0 38.4-38.4V598.4a38.432 38.432 0 0 0-38.4-38.4zM400 960H64V624h336zM985.6 560H598.4a38.432 38.432 0 0 0-38.4 38.4v387.2a38.464 38.464 0 0 0 38.4 38.4h387.2a38.464 38.464 0 0 0 38.4-38.4V598.4a38.432 38.432 0 0 0-38.4-38.4zM960 960H624V624H960zM425.6 0H38.4A38.432 38.432 0 0 0 0 38.4v387.2a38.464 38.464 0 0 0 38.4 38.4h387.2a38.464 38.464 0 0 0 38.4-38.4V38.4A38.432 38.432 0 0 0 425.6 0z m-25.6 400H64V64h336z" /></svg>

After

Width:  |  Height:  |  Size: 912 B

1
src/assets/svg/paste.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689263685428" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3855" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M426.666667 896H170.666667a128 128 0 0 1-128-128V213.333333a128 128 0 0 1 128-128h85.333333a42.666667 42.666667 0 0 1 42.666667 42.666667v42.666667h170.666666V128a42.666667 42.666667 0 0 1 42.666667-42.666667h85.333333a128 128 0 0 1 128 128v170.666667a42.666667 42.666667 0 0 1-85.333333 0V213.333333a42.666667 42.666667 0 0 0-42.666667-42.666666h-42.666666v42.666666a42.666667 42.666667 0 0 1-42.666667 42.666667H256a42.666667 42.666667 0 0 1-42.666667-42.666667V170.666667H170.666667a42.666667 42.666667 0 0 0-42.666667 42.666666v554.666667a42.666667 42.666667 0 0 0 42.666667 42.666667h256a42.666667 42.666667 0 0 1 0 85.333333z" p-id="3856"></path><path d="M512 256H256a42.666667 42.666667 0 0 1-42.666667-42.666667V42.666667a42.666667 42.666667 0 0 1 42.666667-42.666667h256a42.666667 42.666667 0 0 1 42.666667 42.666667v170.666666a42.666667 42.666667 0 0 1-42.666667 42.666667zM298.666667 170.666667h170.666666V85.333333H298.666667zM853.333333 1024h-341.333333a128 128 0 0 1-128-128V469.333333a128 128 0 0 1 128-128h341.333333a128 128 0 0 1 128 128v426.666667a128 128 0 0 1-128 128z m-341.333333-597.333333a42.666667 42.666667 0 0 0-42.666667 42.666666v426.666667a42.666667 42.666667 0 0 0 42.666667 42.666667h341.333333a42.666667 42.666667 0 0 0 42.666667-42.666667V469.333333a42.666667 42.666667 0 0 0-42.666667-42.666666z" p-id="3857"></path><path d="M768 597.333333h-170.666667a42.666667 42.666667 0 0 1 0-85.333333h170.666667a42.666667 42.666667 0 0 1 0 85.333333zM768 725.333333h-170.666667a42.666667 42.666667 0 0 1 0-85.333333h170.666667a42.666667 42.666667 0 0 1 0 85.333333zM768 853.333333h-170.666667a42.666667 42.666667 0 0 1 0-85.333333h170.666667a42.666667 42.666667 0 0 1 0 85.333333z" p-id="3858"></path></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M448 128l128 96h384v704H64V128h384z m-21.312 64H128v672h768v-576H554.688l-128-96zM512 448a64 64 0 0 1 32.064 119.424L544 704h-64V567.424A64 64 0 0 1 512 448z" /></svg>

After

Width:  |  Height:  |  Size: 448 B

1
src/assets/svg/redo.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689263778560" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1632" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M765.013333 509.013333C487.253333 235.392 67.968 391.466667 65.792 697.941333l7.765333 2.645334c176.853333-218.112 417.024-291.84 605.866667-106.453334 1.706667 1.706667 1.792 4.608 0.085333 6.314667L561.92 718.08a4.266667 4.266667 0 0 0 2.986667 7.296h326.826666a4.266667 4.266667 0 0 0 4.266667-4.266667V394.24a4.224 4.224 0 0 0-7.253333-2.986667l-117.76 117.76a4.096 4.096 0 0 1-5.888 0z" fill="#8a8a8a" p-id="1633"></path></svg>

After

Width:  |  Height:  |  Size: 763 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689263027132" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2527" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M0 585.142857h73.142857v73.142857H0v-73.142857z m0 365.714286h73.142857v-73.142857H0v73.142857z m0-146.285714h73.142857v-73.142858H0v73.142858z m0-292.571429h73.142857v-73.142857H0v73.142857zM0 73.142857h73.142857V0H0v73.142857z m0 292.571429h73.142857v-73.142857H0v73.142857z m0-146.285715h73.142857v-73.142857H0v73.142857z m877.714286 438.857143h73.142857v-73.142857h-73.142857v73.142857z m73.142857-219.428571h-73.142857v73.142857h73.142857v-73.142857zM804.571429 0h-73.142858v73.142857h73.142858V0z m146.285714 0h-73.142857v73.142857h73.142857V0z m0 146.285714h-73.142857v73.142857h73.142857v-73.142857z m0 146.285715h-73.142857v73.142857h73.142857v-73.142857zM292.571429 73.142857h73.142857V0h-73.142857v73.142857z m585.142857 950.857143h73.142857v-73.142857h-73.142857v73.142857zM585.142857 73.142857h73.142857V0h-73.142857v73.142857z m146.285714 804.571429v73.142857h146.285715v-73.142857H731.428571z m219.428572-146.285715h-73.142857v146.285715h73.142857V731.428571z m0 146.285715v73.142857h73.142857v-73.142857h-73.142857z m-658.285714 73.142857h73.142857v-73.142857h-73.142857v73.142857z m-146.285715 0h73.142857v-73.142857h-73.142857v73.142857z m0-877.714286h73.142857V0h-73.142857v73.142857z m438.857143 877.714286h73.142857v-73.142857h-73.142857v73.142857zM438.857143 73.142857h73.142857V0h-73.142857v73.142857z m0 877.714286h73.142857v-73.142857h-73.142857v73.142857z" p-id="2528"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M65.6 674.88v49.28c0 34.24 27.52 62.08 62.08 62.4h769.92c34.24 0 62.08-27.52 62.4-62.08v-49.6H65.6zM706.56 882.24h-52.48l-25.28-123.84c-3.2-14.72-16-25.28-30.72-25.28h-170.88c-14.72 0-27.84 10.56-30.72 25.28l-25.92 123.84h-51.2c-17.28 0-31.36 14.08-31.36 31.36s14.08 31.36 31.36 31.36h387.2c17.28-0.64 31.36-14.08 31.36-31.36s-14.08-31.36-31.36-31.36z m-271.36-0.64l17.92-86.4h119.68l17.92 86.4H435.2zM897.92 160H128c-34.24 0-62.08 27.52-62.4 62.08v407.36H960V222.4C960 187.84 932.48 160 897.92 160z m-489.6 399.36c-0.32 7.68-10.56 14.08-23.04 14.08h-22.72c-12.48 0-22.72-6.4-22.72-14.08v-68.48l68.48-31.68v100.16z m102.08 0c0 7.68-10.88 14.08-24.64 14.08h-24.64c-13.44 0-24.64-6.4-24.64-14.08v-142.4l23.04-9.92 50.56 22.08v130.24z m69.76 14.08h-20.8c-11.52 0-20.8-6.4-20.8-14.08V470.4l0.64 0.32c2.56 0.64 5.12 0.96 7.68 0.96 2.88-0.32 5.76-0.64 8-1.6 0.96-0.32 1.6-0.64 2.56-0.96 1.28-0.64 2.56-0.96 3.52-1.6l39.68-27.2 0.32 119.04c0 7.68-9.28 14.08-20.8 14.08z m115.2-16.32c0 7.68-10.56 13.76-23.36 13.76h-23.36c-13.12 0-23.36-6.08-23.36-13.76v-169.6l70.4-41.28v210.88z m-40.96-267.2l-118.08 101.76-85.76-72.64-77.44 68.48-42.88-31.68 119.36-103.68 84.8 70.08 78.4-70.08L580.16 224h115.2l0.32 96.64-41.28-30.72z" /></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689758652423" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2526" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M245.333333 341.333333c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h320c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32h-320zM480 490.666667h-234.666667c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h234.666667c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32zM394.666667 704c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32h-149.333334c-17.066667 0-32 14.933333-32 32s14.933333 32 32 32h149.333334zM974.933333 650.666667L883.2 490.666667c-8.533333-12.8-21.333333-21.333333-36.266667-21.333334H661.333333c-14.933333 0-29.866667 8.533333-36.266666 21.333334L533.333333 650.666667c-8.533333 12.8-8.533333 29.866667 0 42.666666l91.733334 160c8.533333 12.8 21.333333 21.333333 36.266666 21.333334h185.6c14.933333 0 29.866667-8.533333 36.266667-21.333334l91.733333-160c8.533333-12.8 8.533333-29.866667 0-42.666666zM834.133333 810.666667h-160l-81.066666-138.666667 81.066666-138.666667h160l81.066667 138.666667-81.066667 138.666667z" p-id="2527" fill="#ffffff"></path><path d="M791.466667 625.066667c-2.133333-4.266667-6.4-6.4-10.666667-6.4h-46.933333c-4.266667 0-8.533333 2.133333-10.666667 6.4l-23.466667 40.533333c-2.133333 4.266667-2.133333 8.533333 0 12.8l23.466667 40.533333c2.133333 4.266667 6.4 6.4 10.666667 6.4h46.933333c4.266667 0 8.533333-2.133333 10.666667-6.4l23.466666-40.533333c2.133333-4.266667 2.133333-8.533333 0-12.8l-23.466666-40.533333zM480 810.666667H106.666667V213.333333h597.333333v160c0 17.066667 14.933333 32 32 32s32-14.933333 32-32V192c0-23.466667-19.2-42.666667-42.666667-42.666667H85.333333c-23.466667 0-42.666667 19.2-42.666666 42.666667v640c0 23.466667 19.2 42.666667 42.666666 42.666667h394.666667c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32z" p-id="2528" fill="#ffffff"></path></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

1
src/assets/svg/sql.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1691830095931" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4048" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M776.533333 755.456H598.903467c-14.421333 0-26.112 12.475733-26.112 27.869867v168.618666h28.433066L776.533333 760.439467v-4.983467zM776.533333 699.733333v-88.183466c0-15.189333 11.4688-27.477333 25.6-27.477334s25.6 12.288 25.6 27.477334v160.136533c0 7.2192-2.645333 14.1312-7.338666 19.285333L630.2208 998.7072a24.746667 24.746667 0 0 1-18.261333 8.226133H228.693333C163.669333 1006.933333 110.933333 950.306133 110.933333 880.452267V143.530667C110.933333 73.693867 163.669333 17.066667 228.693333 17.066667h481.28C774.997333 17.066667 827.733333 73.693867 827.733333 143.547733v48.930134c0 15.189333-11.4688 27.477333-25.6 27.477333s-25.6-12.288-25.6-27.477333V143.530667c0-39.4752-29.7984-71.492267-66.56-71.492267H228.693333c-36.7616 0-66.56 32.017067-66.56 71.509333v736.887467c0 39.4752 29.7984 71.492267 66.56 71.492267H520.533333v-168.618667C520.533333 737.160533 555.6224 699.733333 598.903467 699.733333H776.533333zM367.7696 280.576c27.272533 0 47.530667 5.512533 60.416 16.896 13.687467 11.997867 19.1488 30.8224 16.093867 56.149333h-37.666134c-0.238933-14.2848-4.215467-24.6784-11.3664-30.839466-7.0656-6.485333-19.456-9.4208-36.317866-9.4208-14.609067 0-26.0608 1.9456-34.747734 6.178133-10.752 4.864-17.015467 12.970667-19.336533 24.0128-2.048 9.745067 1.4336 17.8688 11.246933 23.722667 4.317867 2.577067 16.605867 7.458133 36.608 14.267733 29.44 9.728 47.9232 17.527467 55.927467 22.7328 17.578667 12.014933 24.1664 28.5696 19.6608 50.005333-4.369067 20.770133-15.9744 37.3248-34.730667 49.322667-18.688 11.690667-43.042133 17.8688-72.584533 17.8688-28.5696 0-49.8176-5.5296-63.402667-16.554667-16.605867-13.653333-22.801067-35.072-18.210133-64.6144h37.666133c-1.092267 17.527467 2.389333 30.208 10.888534 37.666134 7.7312 6.485333 20.9408 10.069333 40.106666 10.069333 16.878933 0 31.112533-2.935467 42.018134-8.448 10.973867-5.8368 17.749333-13.312 19.797333-23.04 2.594133-12.356267-2.833067-22.084267-15.616-29.2352-4.061867-2.269867-17.902933-7.458133-41.915733-15.2576-26.658133-9.079467-43.144533-15.581867-49.152-19.473067-15.581867-10.717867-21.077333-26.299733-16.776534-46.762666 4.3008-20.445867 16.162133-36.676267 36.164267-48.366934 18.602667-11.3664 40.226133-16.878933 65.2288-16.878933z m249.122133 0c35.3792 0 60.910933 11.3664 76.509867 34.4064 14.8992 21.76 18.568533 50.653867 11.042133 86.357333-7.304533 34.747733-22.664533 62.976-45.346133 84.411734 6.109867 11.042133 12.424533 22.7328 18.261333 35.072l-29.184 21.418666a712.5504 712.5504 0 0 0-18.1248-35.703466c-18.944 9.728-40.482133 14.933333-63.863466 14.933333-35.703467 0-61.166933-11.690667-76.509867-34.423467-14.848-22.0672-18.244267-50.6368-10.8544-85.7088 7.441067-35.3792 22.869333-63.9488 47.0016-86.016 25.0368-23.381333 55.3472-34.747733 91.067733-34.747733z m-7.048533 33.450667c-24.0128 0-44.2368 8.106667-60.962133 24.337066-15.940267 15.581867-26.4704 36.352-32.085334 62.976-5.5296 26.299733-3.7376 47.086933 5.649067 62.6688 9.642667 15.906133 26.760533 24.029867 50.7904 24.029867a90.794667 90.794667 0 0 0 41.2672-9.4208c-8.704-14.2848-17.066667-26.948267-25.378133-38.314667l26.5728-21.418666a704.085333 704.085333 0 0 1 25.838933 37.649066c11.451733-14.2848 19.899733-32.785067 24.610133-55.1936 5.7344-27.255467 3.754667-48.6912-5.632-64.273066-9.710933-15.581867-26.624-23.04-50.670933-23.04z m171.434667-28.910934h37.666133l-41.949867 199.338667h125.320534l-6.826667 32.477867h-162.986667l48.776534-231.816534z" fill="#ffffff" p-id="4049"></path></svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

1
src/assets/svg/table.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M960 64H64v224h1v164h-1v64h1v160h-1v64h1v219.7h275v0.3h64v-0.3h216v0.3h64v-0.3h275V288h1V64zM620 452H404V288h216v164z m0 224H404V516h216v160z m64-160h211v160H684V516zM129 288h211v164H129V288z m0 228h211v160H129V516z m0 379.7V740h211v155.7H129z m275 0V740h216v155.7H404z m280 0V740h211v155.7H684zM895 452H684V288h211v164z" /></svg>

After

Width:  |  Height:  |  Size: 611 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M928 768a160 160 0 1 1-192-156.8V544h-448v67.2a160 160 0 1 1-64 0V480h256v-64h-64a128 128 0 0 1-128-128v-64a128 128 0 0 1 128-128h192a128 128 0 0 1 128 128v64a128 128 0 0 1-128 128h-64v64h256v131.2a160 160 0 0 1 128 156.8zM256 672A96 96 0 1 0 352 768 96 96 0 0 0 256 672z m352-320a64 64 0 0 0 64-64v-64a64 64 0 0 0-64-64h-192a64 64 0 0 0-64 64v64a64 64 0 0 0 64 64h192z m160 320a96 96 0 1 0 96 96 96 96 0 0 0-96-96z" /></svg>

After

Width:  |  Height:  |  Size: 706 B

1
src/assets/svg/undo.svg Normal file
View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689262813730" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1753" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M533.333333 341.333333c-113.066667 0-215.466667 42.24-294.4 110.933334L85.333333 298.666667v384h384l-154.453333-154.453334c59.306667-49.493333 134.826667-80.213333 218.453333-80.213333 151.04 0 279.466667 98.56 324.266667 234.666667l101.12-33.28C899.413333 470.613333 731.733333 341.333333 533.333333 341.333333z" fill="#8a8a8a" p-id="1754"></path></svg>

After

Width:  |  Height:  |  Size: 685 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M792.14 905.5H244.66a39.46 39.46 0 0 1-39.41-39.41V294.16a39.46 39.46 0 0 1 39.41-39.41h114.75V308H258.46v544.28h519.87V308H675.56v-53.25h116.58a39.46 39.46 0 0 1 39.41 39.41v571.93a39.46 39.46 0 0 1-39.41 39.41zM476.56 735.49L355.58 608l38.6-36.63 83.57 88.05 165.62-163.98 37.44 37.82-204.25 202.23zM679.71 367.17H357.25V198.54h322.46zM410.47 314h216v-62.2h-216z" /></svg>

After

Width:  |  Height:  |  Size: 654 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689757664262" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2575" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M800.529 853.171c16.521 0 29.914 13.393 29.914 29.915 0 16.52-13.393 29.914-29.914 29.914H163.77c-16.521 0-29.914-13.393-29.914-29.914 0-16.522 13.393-29.915 29.914-29.915h636.759zM784.464 80C863.184 80 927 143.875 927 222.669v375.08c0 78.793-63.816 142.668-142.536 142.668H177.536C98.816 740.417 35 676.542 35 597.75v-375.08C35 143.875 98.816 80 177.536 80h606.928z m-90.181 152.253l-0.39 0.416-160.43 174.128-110.091-83.862-0.393-0.294c-12.863-9.494-29.72-7.109-40.246 5.695l-0.317 0.391-159.56 199.719-0.356 0.453c-11.52 14.898-10.787 37.753 1.659 51.612l0.38 0.417 0.39 0.414c12.824 13.384 32.497 12.531 44.425-1.927l0.36-0.443L410.9 402.25l108.507 82.656 0.37 0.279c12.157 8.986 27.996 7.408 38.657-3.854l0.322-0.344 178.78-194.045 0.383-0.423c12.53-14.048 13.084-37.228 1.153-52.065-11.932-14.837-31.876-15.768-44.79-2.2z" fill="#ffffff" p-id="2576"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M569.6 750.933333l-19.2-68.266666H42.666667v106.666666c0 46.933333 38.4 85.333333 85.333333 85.333334h448l-4.266667-4.266667c-32-34.133333-34.133333-85.333333-2.133333-119.466667zM266.666667 810.666667h-106.666667c-17.066667 0-32-14.933333-32-32S142.933333 746.666667 160 746.666667h106.666667c17.066667 0 32 14.933333 32 32s-14.933333 32-32 32zM573.866667 565.333333l138.666666-138.666666H42.666667v192h503.466666c4.266667-19.2 12.8-38.4 27.733334-53.333334zM266.666667 554.666667h-106.666667c-17.066667 0-32-14.933333-32-32S142.933333 490.666667 160 490.666667h106.666667c17.066667 0 32 14.933333 32 32s-14.933333 32-32 32zM789.333333 256c0-46.933333-38.4-85.333333-85.333333-85.333333H128c-46.933333 0-85.333333 38.4-85.333333 85.333333v106.666667h746.666666v-106.666667z m-522.666666 42.666667h-106.666667c-17.066667 0-32-14.933333-32-32S142.933333 234.666667 160 234.666667h106.666667c17.066667 0 32 14.933333 32 32s-14.933333 32-32 32z m298.666666 0c-17.066667 0-32-14.933333-32-32s14.933333-32 32-32 32 14.933333 32 32-14.933333 32-32 32z m106.666667 0c-17.066667 0-32-14.933333-32-32s14.933333-32 32-32 32 14.933333 32 32-14.933333 32-32 32zM964.266667 629.333333l-121.6-121.6c-17.066667-17.066667-42.666667-17.066667-59.733334 0l-29.866666 29.866667-29.866667-29.866667-102.4 102.4c-10.666667 10.666667-14.933333 27.733333-10.666667 42.666667l34.133334 117.333333-25.6 25.6c-8.533333 8.533333-8.533333 21.333333 0 29.866667l29.866666 29.866667c8.533333 8.533333 21.333333 8.533333 29.866667 0l25.6-25.6 117.333333 34.133333c14.933333 4.266667 32 0 42.666667-10.666667l102.4-102.4-29.866667-29.866666 29.866667-29.866667c14.933333-19.2 14.933333-46.933333-2.133333-61.866667z m-136.533334-14.933333l-29.866666-29.866667 29.866666-29.866666 29.866667 29.866666-29.866667 29.866667z m61.866667 59.733333l-29.866667-29.866666 29.866667-29.866667 29.866667 29.866667-29.866667 29.866666z" /></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689757603554" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2420" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M844.7 124.4H489c-13.8 0-25 11.2-25 25s11.2 25 25 25h355.7c9 0 16.3 7.3 16.3 16.3v329.8c0 9-7.3 16.3-16.3 16.3H489c-13.8 0-25 11.2-25 25s11.2 25 25 25h355.7c36.6 0 66.3-29.8 66.3-66.3V190.8c0-36.6-29.7-66.4-66.3-66.4z" p-id="2421"></path><path d="M648 515.4c1.9 0 3.8-0.2 5.7-0.7 9.1-2.1 16.2-9.1 18.5-18.1l24.2-93.6 40.2 38.1c6.6 6.3 16.2 8.5 24.9 5.7s15.2-10.1 16.9-19.1l36.1-190c2.6-13.6-6.3-26.6-19.9-29.2-13.6-2.6-26.6 6.3-29.2 19.9l-27.5 145-37.7-35.8c-6.4-6.1-15.6-8.3-24.1-5.9-8.5 2.4-15.1 9.2-17.3 17.8l-22.9 88.4-13.3-15.5c-9-10.5-24.8-11.7-35.3-2.6-10.5 9-11.7 24.8-2.6 35.3l44.5 51.7c4.7 5.4 11.6 8.6 18.8 8.6zM282.4 337.4c60.2 0 109.2-49 109.2-109.2S342.6 119 282.4 119s-109.2 49-109.2 109.2 49 109.2 109.2 109.2z m0-168.4c32.6 0 59.2 26.5 59.2 59.2s-26.5 59.2-59.2 59.2-59.2-26.5-59.2-59.2 26.6-59.2 59.2-59.2zM551.6 413.7c0-33.2-27-60.2-60.2-60.2H167c-14.8 0-28.6 5.9-38.9 16.5-10.3 10.6-15.6 24.6-15.1 39.4l6.9 197c0.6 17.1 9.4 33.1 23.6 42.8l29.4 20c1 0.7 1.6 1.8 1.7 3l14.2 165.6c1.6 18.4 9.9 35.4 23.5 47.8 13.6 12.5 31.2 19.3 49.7 19.3h40.5c37.8 0 69.2-28.4 73.1-66L410 503.7c0.2-1.6 1.3-3 2.8-3.5l96-29c25.6-7.6 42.8-30.8 42.8-57.5z m-57.3 9.8l-96 29c-20.9 6.3-35.9 24.5-38.1 46.2L325.8 834c-1.2 12-11.3 21.1-23.3 21.1H262c-12.3 0-22.3-9.2-23.4-21.5L224.4 668c-1.4-16.2-9.9-30.8-23.4-40l-29.4-20c-1.1-0.7-1.7-1.9-1.8-3.2l-6.9-197c0-1.4 0.6-2.4 1.1-2.9 0.5-0.6 1.5-1.2 2.9-1.2h324.4c5.6 0 10.2 4.6 10.2 10.2 0.1 4.3-2.8 8.3-7.2 9.6z" p-id="2422"></path></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M519.868669 583.569892A291.784946 291.784946 0 1 1 811.653615 291.784946 292.115269 292.115269 0 0 1 519.868669 583.569892z m0-528.516129A236.731183 236.731183 0 1 0 756.599852 291.784946 236.951398 236.951398 0 0 0 519.868669 55.053763zM35.395551 1024A27.526882 27.526882 0 0 1 7.868669 996.473118C7.868669 738.491183 237.55297 528.516129 519.868669 528.516129A546.463656 546.463656 0 0 1 745.589099 576.082581a27.526882 27.526882 0 1 1-22.021505 50.209032A492.84129 492.84129 0 0 0 519.868669 583.569892C267.942648 583.569892 62.922433 768.770753 62.922433 996.473118A27.526882 27.526882 0 0 1 35.395551 1024zM571.839422 854.874839a26.866237 26.866237 0 0 1-8.808602-1.431398 27.526882 27.526882 0 0 1-17.396989-34.793979 247.852043 247.852043 0 0 1 468.947957-2.972903 27.526882 27.526882 0 1 1-51.970753 18.167742 192.688172 192.688172 0 0 0-364.676129 2.312258 27.636989 27.636989 0 0 1-26.095484 18.71828zM652.548239 916.094624a27.196559 27.196559 0 0 1-11.891613-2.202151 27.636989 27.636989 0 0 1-12.88258-36.775914 170.446452 170.446452 0 0 1 152.719139-96.013763A168.574624 168.574624 0 0 1 932.771895 875.685161a27.526882 27.526882 0 1 1-49.43828 24.223656 113.851183 113.851183 0 0 0-102.950537-64.302795 115.062366 115.062366 0 0 0-103.060645 64.853333 27.747097 27.747097 0 0 1-24.774194 15.635269zM780.162863 962.670108m-52.190968 0a52.190968 52.190968 0 1 0 104.381936 0 52.190968 52.190968 0 1 0-104.381936 0Z" /></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M192 384h-64v512a64 64 0 0 0 64 64h512v-64H192zM544 288h-96v320h96c88.22 0 160-71.78 160-160s-71.78-160-160-160z" /><path fill="#ffffff" d="M832 64H320a64.19 64.19 0 0 0-64 64v640a64.19 64.19 0 0 0 64 64h512a64.19 64.19 0 0 0 64-64V128a64.19 64.19 0 0 0-64-64zM702.39 606.39A222.53 222.53 0 0 1 544 672H384V224h160a224 224 0 0 1 158.39 382.39z" /></svg>

After

Width:  |  Height:  |  Size: 635 B

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689263495822" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1180" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M463.238095 146.285714c175.055238 0 316.952381 141.897143 316.952381 316.952381 0 77.775238-28.038095 149.040762-74.532571 204.214857l188.440381 194.535619-55.247238 57.002667-191.536762-197.705143A315.489524 315.489524 0 0 1 463.238095 780.190476c-175.055238 0-316.952381-141.897143-316.952381-316.952381S288.182857 146.285714 463.238095 146.285714z m0 73.142857C328.582095 219.428571 219.428571 328.582095 219.428571 463.238095s109.153524 243.809524 243.809524 243.809524a242.883048 242.883048 0 0 0 161.889524-61.513143l25.551238-26.355809A242.834286 242.834286 0 0 0 707.047619 463.238095c0-134.656-109.153524-243.809524-243.809524-243.809524z m36.571429 97.52381v109.714286H609.52381v73.142857h-109.714286V609.52381h-73.142857v-109.738667L316.952381 499.809524v-73.142857l109.714286-0.024381V316.952381h73.142857z" p-id="1181"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1689263030318" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2681" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M463.238095 146.285714c175.055238 0 316.952381 141.897143 316.952381 316.952381 0 77.775238-28.038095 149.040762-74.532571 204.214857l188.440381 194.535619-55.247238 57.002667-191.536762-197.705143A315.489524 315.489524 0 0 1 463.238095 780.190476c-175.055238 0-316.952381-141.897143-316.952381-316.952381S288.182857 146.285714 463.238095 146.285714z m0 73.142857C328.582095 219.428571 219.428571 328.582095 219.428571 463.238095s109.153524 243.809524 243.809524 243.809524a242.883048 242.883048 0 0 0 161.889524-61.513143l25.551238-26.355809A242.834286 242.834286 0 0 0 707.047619 463.238095c0-134.656-109.153524-243.809524-243.809524-243.809524z m152.210286 219.428572v73.142857H316.952381v-73.142857h298.496z" p-id="2682"></path></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="32px" height="30.12px" viewBox="0 0 1088 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M960 832H128c-70.4 0-128-57.6-128-128V128C0 57.6 57.6 0 128 0h832c70.4 0 128 57.6 128 128v576c0 70.4-57.6 128-128 128zM128 64c-38.4 0-64 25.6-64 64v576c0 38.4 25.6 64 64 64h832c38.4 0 64-25.6 64-64V128c0-38.4-25.6-64-64-64H128z" /><path fill="#ffffff" d="M160 672s-6.4 0 0 0c-25.6-6.4-32-19.2-32-38.4 0-6.4 6.4-19.2 121.6-224 6.4-6.4 12.8-12.8 25.6-19.2 12.8 0 19.2 6.4 25.6 12.8l89.6 134.4 160-332.8c6.4-12.8 12.8-19.2 32-19.2 12.8 0 25.6 6.4 25.6 19.2L768 550.4l96-147.2c6.4-12.8 32-19.2 44.8-12.8 12.8 6.4 19.2 32 12.8 44.8l-128 198.4c-6.4 12.8-19.2 19.2-32 12.8-12.8 0-19.2-6.4-25.6-19.2L576 288l-153.6 320c-6.4 12.8-12.8 19.2-25.6 19.2s-25.6-6.4-32-12.8L268.8 473.6C224 550.4 185.6 627.2 179.2 640c6.4 25.6-6.4 32-19.2 32zM544 1024c-19.2 0-32-12.8-32-32v-192c0-19.2 12.8-32 32-32s32 12.8 32 32v192c0 19.2-12.8 32-32 32z" /><path fill="#ffffff" d="M736 1024h-384c-19.2 0-32-12.8-32-32s12.8-32 32-32h384c19.2 0 32 12.8 32 32s-12.8 32-32 32z" /></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,4 +1,5 @@
import React, { useState } from "react";
import { useState } from "react";
import { useSelector } from "react-redux";
/**
* @author {温华}
@@ -7,11 +8,12 @@ import React, { useState } from "react";
* @param {permission: string[]}
*/
export default function Authenticationbtn({ permission }: any) {
export default function Authenticationbtn({ permission, children }: any) {
// 还需从缓存中获取用户权限数组
const authInfo = useSelector((store: any) => store.auth)
const [visible, setVisible] = useState(true)
const allPermission: string = "*:*:*"
const authPermissions: Array<string> = []
const authPermissions: Array<string> = authInfo.permissions
if(permission && permission instanceof Array && permission.length > 0) {
const permissiosFlag = permission
const hasPermission = authPermissions.some((perm: string)=>{
@@ -20,5 +22,6 @@ export default function Authenticationbtn({ permission }: any) {
setVisible(hasPermission)
}
return <div style={{display: visible ? 'block' : 'none'}}></div>
if(visible) return children
return null
}

View File

@@ -1,5 +1,5 @@
import React from "react";
// import React from "react";
export default function ConfirmModel() {
// export default function ConfirmModel() {
}
// }

View File

@@ -0,0 +1,143 @@
import { Fragment, useState } from "react";
import {Button, DatePicker, Form, Input, Select} from 'antd'
import { useForm } from "antd/es/form/Form";
import { formatDate } from "@/utils/tool";
const { RangePicker } = DatePicker
interface IItemConfig {
name: string,
type: string,
key: string,
options?: Array<any>,
placeholder?: string,
picker?: any,
showTime?: boolean,
showHour?: boolean,
showMinute?: boolean,
showSecond?: boolean,
showNow?: boolean
}
export interface ISearchformConfig {
config: {
name: string,
formItem: Array<IItemConfig>
},
submit: (val: object)=>void
}
interface ISubmitVal {
[x: string]: any
}
export default function Fvsearchform(props: ISearchformConfig) {
const [val, setVal] = useState<object>()
const [form] = useForm()
const {config, submit} = props
const handleSubmit = (value: ISubmitVal) => {
if(Object.keys(value).length) {
config.formItem.forEach(item => {
for (const key in value) {
if ( item.type == 'date' && item.key == key ) {
value[key] && (value[key] = formatDate(value[key].$d))
}
if(item.type == 'rangedate' && item.key == key) {
value[key] && (value[key] = value[key]?.map((date: any)=>date = formatDate(date.$d)))
}
}
})
}
setVal({...val, ...value})
submit(value)
}
const handleReset = () => {
form.resetFields()
}
return(
<Fragment>
<Form
name={config?.name}
form={form}
onFinish={handleSubmit}
layout="inline"
>
{
config?.formItem.map((item:IItemConfig) => {
if(item) {
if(item.type === 'input') {
return(
<Form.Item
key={item.key}
label={item.name}
name={item.key}
>
<Input placeholder={item?.placeholder || '请输入'} allowClear></Input>
</Form.Item>
)
}
if(item.type === 'select') {
return(
<Form.Item
key={item.key}
label={item.name}
name={item.key}
>
<Select
options={item?.options}
placeholder={item?.placeholder || '请选择'}
allowClear
/>
</Form.Item>
)
}
if(item.type === 'date') {
return(
<Form.Item
key={item.key}
label={item.name}
name={item.key}
>
<DatePicker
picker={item.picker}
showTime={item?.showTime}
showNow={item?.showNow}
format="YYYY-MM-DD HH:mm:ss"
allowClear
/>
</Form.Item>
)
}
if(item.type === 'rangedate') {
return(
<Form.Item
key={item.key}
label={item.name}
name={item.key}
>
<RangePicker
showTime={item?.showTime}
showHour={item?.showHour}
showMinute={item?.showMinute}
showSecond={item?.showSecond}
showNow={item?.showNow}
picker={item.picker}
format="YYYY-MM-DD HH:mm:ss"
allowClear
/>
</Form.Item>
)
}
}
})
}
<Form.Item>
<Button htmlType="submit" type="primary" ></Button>
</Form.Item>
<Form.Item>
<Button onClick={handleReset} htmlType="submit"></Button>
</Form.Item>
</Form>
</Fragment>
)
}

View File

@@ -1,5 +1,5 @@
import { useState, useEffect } from "react";
// import { useState, useEffect } from "react";
export default function useDemo() {
// export default function useDemo() {
}
// }

View File

@@ -1,5 +1,4 @@
import React from "react";
import '../../assets/styles/appmain.scss'
import '@/assets/styles/appmain.scss'
import { Outlet } from "react-router-dom";
export default function AppMain() {

View File

@@ -1,4 +1,4 @@
import React, { useEffect } from "react";
import { useEffect } from "react";
import { Layout } from 'antd'
import { useDispatch, useSelector } from 'react-redux'
import SideBar from "./sidebar";

View File

@@ -3,7 +3,7 @@ import {
MenuFoldOutlined,
MenuUnfoldOutlined,
} from '@ant-design/icons';
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { setCollapsed } from '../../stores/sidebar';

View File

@@ -0,0 +1,19 @@
import { useMemo } from "react"
interface IMenuIconProps {
prefix?: string
name: string
color?: string,
size?: number | string
}
export default function Menuicon(props: IMenuIconProps) {
const { prefix = 'icon', name, color, size = 16 } = props
const symbolId = useMemo(() => `#${prefix}-${name}`, [prefix, name])
return (
<span className="anticon">
<svg aria-hidden="true" width={size} height={size} fill={color} >
<use href={symbolId} fill={color} />
</svg>
</span>
)
}

View File

@@ -1,6 +1,6 @@
import { Menu } from 'antd'
import React, {Fragment, useEffect, useState} from 'react'
import { useDispatch, useSelector } from 'react-redux';
import {Fragment, useEffect, useState} from 'react'
import { useSelector } from 'react-redux';
import { generateMenu } from '../../permission';
import { useLocation, useNavigate } from 'react-router-dom';
@@ -8,7 +8,7 @@ export default function SideBar() {
const navigate = useNavigate()
const location = useLocation()
const routes: [] = useSelector((store: any)=>store.permission)
const dispatch = useDispatch()
// const dispatch = useDispatch()
const [menuItem, setMenuItem] = useState<Array<any>>([{
key: '/',
label: '首页',
@@ -24,7 +24,7 @@ export default function SideBar() {
const goPage = (val: any) => {
console.log(val);
navigate(val.key)
navigate(val.key, { replace: true })
}
useEffect(()=>{
getMenu()
@@ -36,6 +36,7 @@ export default function SideBar() {
theme="dark"
mode="inline"
defaultSelectedKeys={[location.pathname]}
selectedKeys={[location.pathname]}
items={menuItem}
onClick={goPage}
/>

View File

@@ -3,6 +3,9 @@ import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import { BrowserRouter } from 'react-router-dom'
import { ConfigProvider } from 'antd'
import zhCn from 'antd/locale/zh_CN'
import 'virtual:svg-icons-register'
// redux toolkit
import { Provider } from 'react-redux'
@@ -13,7 +16,9 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor} >
<BrowserRouter>
<ConfigProvider locale={zhCn}>
<App />
</ConfigProvider>
</BrowserRouter>
</PersistGate>
</Provider>

View File

@@ -1,3 +1,4 @@
import Menuicon from "./layout/sidebar/Menuicon"
import { IRouter } from "./stores/permission"
// 生成菜单
@@ -11,7 +12,7 @@ export const generateMenu = (routes: IRouter): Array<any> => {
if(item.meta) {
item.title = item.meta?.title
item.label = item.meta?.title
item.icon = item.meta?.icon
item.icon = <Svgicon name={item.meta?.icon} />
}
if(item.children && item.children?.length) {
item.children = generateChildMenu(item.children)
@@ -29,7 +30,7 @@ const generateChildMenu = (routes: any): Array<any> => {
if(item.meta) {
item.title = item.meta?.title
item.label = item.meta?.title
item.icon = item.meta?.icon
item.icon = <Svgicon name={item.meta?.icon} />
}
if(item.children && item.children?.length) {
generateChildMenu(item.children)
@@ -37,3 +38,7 @@ const generateChildMenu = (routes: any): Array<any> => {
return true
})
}
function Svgicon({name}:any) {
return <Menuicon name={name} />
}

View File

@@ -1,5 +1,4 @@
import { Navigate } from "react-router-dom"
import Notfound from "../Notfound"
import { getToken } from "../utils/auth"
const Views = import.meta.glob('../view/**/*.tsx')
@@ -19,11 +18,11 @@ export default function RouteInterception ({children, filePath=""}: any) {
if (hasMapFile(filePath)) {
return children
} else {
return <Notfound />
return <Navigate to='/404' />
}
}
return children
} else {
return <Navigate to='/login' />
return <Navigate to='/login' replace={true} />
}
}

View File

@@ -1,13 +1,12 @@
import { getToken } from '../utils/auth'
import LayOut from '../layout/index'
import Home from '../view/home'
import { Suspense, lazy } from 'react'
import Loading from '../Loading'
import RouteInterception from './RouteInterception'
interface Meta {
title: string,
redirect?: boolean
}
import Notfound from '../Notfound'
// interface Meta {
// title: string,
// redirect?: boolean
// }
type route = {
path: string,
@@ -21,6 +20,10 @@ const lazyLoad = (viewName: string) => {
const LoginView = lazy(()=>import('../Login'))
return <LoginView />
}
if(viewName === 'home') {
const Home = lazy(()=>import('../view/home'))
return <Home />
}
const View = lazy(()=>import(`../view/${viewName}.tsx`))
return <View/>
}
@@ -29,17 +32,21 @@ const lazyLoad = (viewName: string) => {
const currentRouter: Array<route> = [
{
path: '/',
element: <RouteInterception><Suspense fallback={<Loading />}><LayOut /></Suspense></RouteInterception>,
element: <RouteInterception><LayOut /></RouteInterception>,
children: [
{
path: "", //登录后默认跳转路径
element : <Home/>
element : <Suspense fallback={<Loading />}>{lazyLoad('home')}</Suspense>
},
{
path: "*",
element: <Notfound />
}
]
},
{
path: '/login',
element: lazyLoad('login')
element: <Suspense fallback={<Loading />}>{lazyLoad('login')}</Suspense>
}
]

53
src/stores/auth.ts Normal file
View File

@@ -0,0 +1,53 @@
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import { getAuthInfoApi } from '../api/auth'
export interface IAuthInfo {
permissions: Array<string>,
roles: Array<string>
user: object
}
const initAuthInfo = new Object() as IAuthInfo
export const getAuthInfo = createAsyncThunk('/auth/info',
async () => {
try {
const {code, data} = await getAuthInfoApi()
if(code === 1000) {
return data
}
} catch (err) {
return Promise.reject(err)
}
}
)
export const authInfoSlice = createSlice({
name: 'authinfo',
initialState: initAuthInfo,
reducers: {
addAuthInfo(state, {payload}) {
state = {...payload}
return state
}
},
extraReducers(builder) {
builder
.addCase(getAuthInfo.pending, (state) => {
console.log(state);
console.log('正在请求登录用户信息')
})
.addCase(getAuthInfo.fulfilled, (state, {payload}) => {
console.log(state);
return payload
})
.addCase(getAuthInfo.rejected, (state, err) => {
console.log(state);
console.log(err, 'rejected')
})
}
})
export const { addAuthInfo } = authInfoSlice.actions
export default authInfoSlice.reducer

View File

@@ -1,10 +1,10 @@
import { createSlice } from '@reduxjs/toolkit'
type CacheKey = string
// type CacheKey = string
type CacheVal = any
// type CacheVal = any
const initCache = new Map<CacheKey, CacheVal>([])
// const initCache = new Map<CacheKey, CacheVal>([])
// 创建一个slice
export const cacheSlice = createSlice({

View File

@@ -4,6 +4,7 @@ import storage from "redux-persist/lib/storage";
import cacheSlice from "./cache";
import sidebarSlice from "./sidebar";
import permissionSlice from "./permission";
import authInfoSlice from "./auth"
const persistConfig = {
key: 'root',
@@ -14,7 +15,8 @@ const persistConfig = {
const reducers = combineReducers({
cache: cacheSlice,
sidebar: sidebarSlice,
permission: permissionSlice
permission: permissionSlice,
auth: authInfoSlice
})
// configureStore创建一个redux数据

View File

@@ -46,13 +46,16 @@ export const permissionSlice = createSlice({
extraReducers(builder) {
builder
.addCase(getAsyncRouters.pending, (state) => {
console.log(state);
console.log('正在请求路由');
})
.addCase(getAsyncRouters.fulfilled, (state, { payload }) => {
console.log(state);
// 请求成功后处理路由
return payload
})
.addCase(getAsyncRouters.rejected, (state, err) => {
console.log(state);
console.log(err,'rejected');
})

View File

@@ -6,13 +6,13 @@ export interface IResponse<T=any> {
}
// 定义请求方式枚举
enum EMethod {
GET = 'get',
POST = 'post',
PUT = 'put',
DELETE = 'delete',
PATCH = 'patch'
}
// enum EMethod {
// GET = 'get',
// POST = 'post',
// PUT = 'put',
// DELETE = 'delete',
// PATCH = 'patch'
// }
// 用户token类型定义
export type IToken = string

View File

@@ -1,6 +1,5 @@
import { IResponse, IToken } from "@/type";
import axios, { InternalAxiosRequestConfig, AxiosInstance, AxiosRequestConfig } from "axios";
import Cookie from 'js-cookie'
import { getToken, removeToken } from "./auth";
import { message } from "antd";

5
src/utils/tool.ts Normal file
View File

@@ -0,0 +1,5 @@
import moment from 'moment'
export function formatDate(date: Date, format='YYYY-MM-DD HH:mm:ss'): string {
return moment(date).format(format)
}

View File

@@ -1,7 +1,10 @@
import React from 'react'
import Authenticationbtn from "@/components/Authenticationbtn";
import { Button } from "antd";
export default function Home() {
return(
<div>home</div>
<Authenticationbtn permission="['system:list']">
<Button></Button>
</Authenticationbtn>
)
}

View File

@@ -0,0 +1,48 @@
.deptBox{
background: white;
padding: 20px;
.tablebox{
table{
thead{
th::before{
}
tr{
.ant-table-cell{
background: white;
color:#909399
}
}
}
tbody{
tr{
.ant-table-cell{
background: white;
color:#606266
}
}
}
}
.ant-table-cell{
button:focus
{
outline: none;
}
button:hover
{
border: 0px rgb(250, 250, 250);
}
}
}
.newAdd{
display: flex;
margin-top: 15px;
button{
margin-left: 15px;
}
}
}

View File

@@ -1,7 +1,575 @@
import React from "react";
export default function Dept() {
return(
<div>dept</div>
)
import { useState,useEffect } from "react";
import {
Button,
Input,
Select,
message,
Table,
Tag,
Modal,
Form,
InputNumber,
Radio,
ConfigProvider,
TreeSelect,
Row,
Col,
// Icon
} from "antd";
import type { ColumnsType } from "antd/es/table";
import {
SyncOutlined,
SearchOutlined,
ExclamationCircleFilled,
SwapOutlined,
} from "@ant-design/icons";
import zh_CN from "antd/es/locale/zh_CN";
import "dayjs/locale/zh-cn";
// import 'moment/locale/zh-cn';
import {
getDatasAPI,
delDataAPI,
addDataAPI,
editDataAPI,
getDataAPI,
} from "../../../api/system/dept";
import "./index.scss";
// import { useForm } from "antd/es/form/Form";
interface searchValueType {
deptName: string | undefined;
state: string | undefined;
}
//表格单项数据类型
interface TableDataType {
children: number[];
createTime: string;
deptId: number;
deptName: string;
orderNum: string;
state: string;
}
let thisDept = {};
let treeData: any = [];
let isTreeAnyId = false;
export default function Dept() {
const { confirm } = Modal;
//提示组件
const [messageApi, contextHolder] = message.useMessage();
const [isAllExpend, setIsAllExpend] = useState(true);
//搜索框内所有内容
let [searchValue, setSearchValue] = useState<searchValueType>({
deptName: undefined,
state: undefined,
});
// const is
//树列表数据
// const [isTreeAnyId,setIsTreeAnyId]=useState(false)
const [treeAnyId, setTreeAnyId] = useState<number[]>([]);
const [expandedRowKeys, setExpandedRowKeys] = useState<number[]>([]);
//Modal
const [isModalOpen, setIsModalOpen] = useState(false);
const [modalTitle, setModalTitle] = useState('');
//表单区域
const [isDisbaled, setIsDisbaled] = useState<boolean>(false);
//serach
const [formSearch] = Form.useForm();
const [formModal] = Form.useForm();
//当前页面页数
//表格内数据
let [tableData, setTableData] = useState<TableDataType[]>([]);
//表格格式
const columns: ColumnsType<TableDataType> = [
{
title: "部门名称",
dataIndex: "deptName",
key: "deptName",
},
{
title: "排序",
dataIndex: "orderNum",
key: "orderNum",
},
{
title: "状态",
key: "state",
dataIndex: "state",
render: (text) => (
<>
{text === "0" ? (
<Tag color="success"></Tag>
) : (
<Tag color="error"></Tag>
)}
</>
),
},
{
title: "创建时间",
key: "createTime",
dataIndex: "createTime",
},
{
title: "操作",
key: "",
dataIndex: "",
render: (text) => (
<>
<Button type="link" onClick={() => handleNewAdd(text.deptId)}>
{"新增"}
</Button>
{/* <Button type='link' danger onClick={() => handleClickDel(text)}>{'删除'}</Button> */}
<Button type="link" onClick={() => handleEdit(text)}>
{"修改"}
</Button>
{text.parentId !== 0 ? (
<Button type="link" onClick={() => handleDelete(text)}>
{"删除"}
</Button>
) : null}
</>
),
},
];
//请求表格内容参数
let [queryTableDataParams, setQueryTableDataParams] = useState<any>({
deptName: undefined,
state: undefined,
});
const handleDelete = (dept: any) => {
confirm({
title: "系统提示",
icon: <ExclamationCircleFilled />,
content: `确定删除部门名称为${dept.deptName}的数据吗`,
okText: "确定",
okType: "danger",
cancelText: "取消",
onOk() {
(async () => {
try {
let { code } = await delDataAPI(dept.deptId);
if (code === 1000) {
console.log("del", dept.deptId);
messageApi.open({
type: "success",
content: "操作成功",
});
getDatas({});
}
} catch (error) {
console.log(error);
}
})();
},
onCancel() {
console.log("Cancel");
},
});
};
const handleSerach = () => {
searchValue = formSearch.getFieldsValue();
setSearchValue(searchValue)
console.log(searchValue);
setQueryTableDataParams({ ...searchValue });
};
const handleReset = () => {
formSearch.resetFields();
searchValue = formSearch.getFieldsValue();
setSearchValue(searchValue)
setQueryTableDataParams({ ...searchValue });
};
const handleEdit = async (dept: any) => {
if (dept.parentId === 0) {
setIsDisbaled(true);
formModal.setFieldsValue({ ...thisDept, parentId: dept.deptName });
} else {
setIsDisbaled(false);
// formModal.setFieldsValue({...thisDept});
}
await getData(dept.deptId);
if (dept.parentId === 0) {
setIsDisbaled(true);
formModal.setFieldsValue({ ...thisDept, parentId: dept.deptName });
} else {
setIsDisbaled(false);
formModal.setFieldsValue({ ...thisDept });
}
console.log(thisDept);
setModalTitle("修改部门");
setIsModalOpen(true);
};
const handleNewAdd = (deptId?: string) => {
setModalTitle("新增部门");
setIsModalOpen(true);
if (deptId) {
formModal.setFieldsValue({
parentId: deptId,
deptName: '',
orderNum: 0,
leader: '',
phone: '',
email: '',
state: "0",
});
} else
formModal.setFieldsValue({
parentId: undefined,
deptName: undefined,
orderNum: 0,
leader: undefined,
phone: undefined,
email: undefined,
state: "0",
});
};
const handleModalOk = () => {
formModal
.validateFields()
.then((values) => {
console.log("add", values);
if (modalTitle == "新增部门") {
console.log(values);
postAdd(values);
} else if (modalTitle == "修改部门") {
editData(values);
}
})
.catch((info) => {
console.log("Validate Failed:", info);
});
};
//遍历菜单树获取所有节点ID
const getTree = function (tree: any) {
tree.title = tree.deptName;
tree.value = tree.deptId;
if (!isTreeAnyId) {
treeAnyId.push(tree.deptId);
setTreeAnyId([...treeAnyId]);
}
if (tree.children) {
for (let child of tree.children) {
getTree(child);
}
}
setExpandedRowKeys([...treeAnyId])
};
const handleModalCanle = () => {
setIsModalOpen(false);
};
//获取详细数据
const getData = async (deptId: number) => {
try {
const { code, data } = await getDataAPI(deptId);
if (code === 1000) {
thisDept = data;
// console.log(thisDept);
}
} catch (error) {}
};
//修改数据
const editData = async (dept: any) => {
try {
const { code } = await editDataAPI({ ...thisDept, ...dept });
if (code === 1000) {
messageApi.open({
type: "success",
content: "操作成功",
});
setIsModalOpen(false);
getDatas(queryTableDataParams);
} else {
messageApi.open({
type: "error",
content: "操作失败",
});
}
} catch (error) {}
};
//获取部门数据
const getDatas = async (queryTableDataParams: any) => {
console.log("参数:", queryTableDataParams);
try {
const { code, data } = await getDatasAPI(queryTableDataParams);
console.log(data);
if (code === 1000) {
console.log(isTreeAnyId);
for (let child of data) {
getTree(child);
}
console.log(treeAnyId);
setExpandedRowKeys([...treeAnyId]);
// console.log(treeAnyId);
console.log(data);
setTableData(data);
treeData = data;
console.log(treeData);
// setTotal(data.total);
}
} catch (err: any) {
console.log("555", err);
}
isTreeAnyId = true;
};
//新增
const postAdd = async (dept: any) => {
try {
let { code } = await addDataAPI(dept);
console.log(code);
if (code === 1000) {
console.log("success");
messageApi.open({
type: "success",
content: "操作成功",
});
formModal.setFieldsValue({});
getDatas({ ...queryTableDataParams });
setIsModalOpen(false);
} else {
messageApi.open({
type: "error",
content: "部门名称已存在",
});
}
} catch (error) {}
};
useEffect(() => {
queryTableDataParams = {
...queryTableDataParams,
};
getDatas(queryTableDataParams);
return ()=>{
setIsAllExpend(true),
isTreeAnyId=false
}
}, [queryTableDataParams]);
return (
<ConfigProvider locale={zh_CN}>
<div className="deptBox">
<Form
layout="inline"
initialValues={undefined}
form={formSearch}
onFinish={handleSerach}
style={{ fontWeight: "700" }}
>
<Form.Item
label="部门名称"
name="deptName"
style={{ width: 300 + "px", marginTop: "10px" }}
>
<Input placeholder="请输入部门名称" allowClear />
</Form.Item>
<Form.Item
label="状态"
name="state"
wrapperCol={{ span: 22 }}
style={{ marginTop: "10px" }}
>
<Select
style={{ width: 300 + "px",textAlign:"left" }}
allowClear
showSearch
placeholder="请选择部门状态"
optionFilterProp="lable"
options={[
{
value: 0,
label: "正常",
},
{
value: 1,
label: "停用",
},
]}
/>
</Form.Item>
<Form.Item style={{ marginTop: "10px" }}>
<Button
type="primary"
htmlType="submit"
style={{ marginLeft: 20 + "px" }}
onClick={() => handleSerach()}
>
<SearchOutlined />
</Button>
</Form.Item>
<Form.Item style={{ marginTop: "10px" }}>
<Button onClick={() => handleReset()}>
<SyncOutlined />
</Button>
</Form.Item>
</Form>
{/* 新增 */}
<div className="newAdd">
<Button type="primary" onClick={() => handleNewAdd()}>
+
</Button>
<Button
type="primary"
style={{ backgroundColor: "rgb(144,147,153)" }}
onClick={() => {
setIsAllExpend(!isAllExpend);
if (isAllExpend) {
setExpandedRowKeys([]);
} else setExpandedRowKeys(treeAnyId);
}}
>
<SwapOutlined rotate={90} />
{isAllExpend ? "全部收起" : "全部展开"}
</Button>
</div>
{/* 弹出 */}
<Modal
open={isModalOpen}
title={modalTitle}
okText="确定"
cancelText="取消"
onCancel={handleModalCanle}
onOk={handleModalOk}
width={"700px"}
>
<Form
form={formModal}
// layout="inline"
name="form_in_modal"
initialValues={{ state: "0" }}
labelAlign="right"
// labelCol={{span:12}}
wrapperCol={{ span: 16 }}
>
<Row>
<Col span={24}>
<Form.Item
name="parentId"
label="上级部门"
rules={[{ required: true, message: "请选择上级部门" }]}
// style={{ width: 100 + "%" }}
>
<TreeSelect
disabled={isDisbaled}
// showSearch
style={{ width: "559px" }}
// value={value}
dropdownStyle={{ maxHeight: 400, overflow: "auto" }}
placeholder="请选择上级部门"
allowClear
// treeDefaultExpandAll
// onChange={onChange}
treeData={treeData}
//
/>
</Form.Item>
</Col>
</Row>
<Row>
<Col span={12}>
<Form.Item
name="deptName"
label="部门名称"
rules={[{ required: true, message: "请输入部门名称" }]}
// style={{ width: 45 + "%", marginTop: "20px" }}
>
<Input placeholder="请输入部门名称" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
required
name="orderNum"
label="显示顺序"
rules={[{ required: true, message: "请输入部门显示顺序" }]}
// style={{ width: 40 + "%", marginTop: "20px" }}
>
<InputNumber min={0} controls={true} />
</Form.Item>
</Col>
</Row>
<Row style={{translate:'25px'}}>
<Col span={12}>
<Form.Item
name="leader"
label="负责人"
// style={{ width: 40 + "%", marginTop: "20px", marginLeft: "10px" }}
>
<Input placeholder="请输入负责人" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="phone"
label="联系电话"
// style={{ width: 40 + "%", marginTop: "20px" }}
>
<Input placeholder="请输入联系电话" />
</Form.Item>
</Col>
</Row>
<Row style={{translate:'38px'}}>
<Col span={12}>
<Form.Item
name="email"
label="邮箱"
// style={{ width: 40 + "%", marginTop: "20px", marginLeft: "10px" }}
>
<Input placeholder="请输入邮箱" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="state"
label="状态"
className="collection-create-form_last-form-item"
// style={{ width: 40 + "%", marginTop: "20px", marginLeft: "10px" }}
>
<Radio.Group>
<Radio defaultChecked={true} value="0">
</Radio>
<Radio value="1"></Radio>
</Radio.Group>
</Form.Item>
</Col>
</Row>
</Form>
</Modal>
{/* 表格 */}
<div className="tablebox">
{contextHolder}
<Table
expandedRowKeys={expandedRowKeys}
onExpand={(expanded, record) => {
// console.log(expanded, record);
if (expanded) {
setExpandedRowKeys([record.deptId, ...expandedRowKeys]);
} else {
const newEx = expandedRowKeys.filter((element) => {
return element != record.deptId;
});
// console.log(expandedRowKeys);
setExpandedRowKeys(newEx);
}
console.log(expandedRowKeys);
}}
columns={columns}
rowKey={"deptId"}
dataSource={tableData}
pagination={false}
/>
</div>
</div>
</ConfigProvider>
);
}

View File

@@ -0,0 +1,60 @@
#content {
display: flex;
border-top: 1px solid black;
.dept-content {
flex: 20%;
padding: 35px 12px 0 0;
height: calc(100vh - 85px);
}
.left-dept {
flex: 20%;
padding: 35px 12px 0 0;
height: calc(100vh - 85px);
border: 1px solid red;
border-top: 1px solid black;
}
.right {
flex: 80%;
height: 190px;
display: flex;
flex-direction: column;
.search-content {
display: flex;
width: 500px;
justify-content: space-around;
padding: 35px 12px 0 0;
margin-left: 20px;
}
}
.table-content {
margin: 12px 0 0 12px;
.ant-table-thead > tr > th {
color: #909399;
text-align: center;
}
.ant-table-tbody > tr > td {
text-align: center;
}
}
.add-content {
display: flex;
margin-left: 12px;
margin-top: 20px;
button:focus,
button:focus-visible {
outline: none;
}
}
.user-mid-button {
display: flex;
margin-left: 12px;
margin-top: 20px;
button:focus,
button:focus-visible {
outline: none;
}
}
}

View File

@@ -0,0 +1,242 @@
import "./index.scss";
import { Select, Button, Table, Space, Tag, message, Modal } from "antd";
import {
EditOutlined,
DeleteOutlined,
ExclamationCircleFilled,
} from "@ant-design/icons";
import {
getNoticeListApi,
deleteNoticeInfoApi,
} from "../../../../api/system/notice";
import { useEffect, useState } from "react";
import { SearchOutlined, RedoOutlined } from "@ant-design/icons";
export default function Inform() {
const [noticeInfo, setNoticeInfo] = useState<string>("");
const [isNoticeOpen, setIsNoticeOpen] = useState<boolean>(false);
const [listData, setListData] = useState([]);
const [messageApi, contextHolder] = message.useMessage();
const [queryParams, setQueryParams] = useState<QueryParamsType>({
state: undefined,
pageSize: undefined,
pageNum: undefined,
});
const columns = [
{
title: "序号",
dataIndex: "noticeId",
key: "noticeId",
render: (text: string) => <>{text ? text : "null"}</>,
},
{
title: "公告标题",
dataIndex: "noticeTitle",
key: "noticeTitle",
render: (text: string) => <>{text ? text : "null"}</>,
},
{
title: "阅读状态",
key: "state",
dataIndex: "state",
render: (text: string) => (
<>
{text === "1" ? (
<Tag color="warning"></Tag>
) : (
<Tag color="error"></Tag>
)}
</>
),
},
{
title: "操作",
key: "operate",
dataIndex: "operate",
render: (_: string, record: object) => (
<div style={{ color: "#58aaff" }}>
<Space>
<EditOutlined />
<a
href="#"
onClick={() => handleInfo(record)}
style={{ color: " #58aaff" }}
>
</a>
<DeleteOutlined />
<a
href="#"
onClick={() => handleDel(record)}
style={{ color: " #58aaff" }}
>
</a>
</Space>
</div>
),
},
];
interface QueryParamsType {
state?: string;
pageSize?: number;
pageNum?: number;
}
const getList = async (newQueryParams: object) => {
try {
const { code, data } = await getNoticeListApi(newQueryParams);
if (code === 1000) {
setListData(data.rows);
}
} catch (err) {
console.error(err);
}
};
const handleReset = async () => {
setQueryParams({});
getList({});
};
const handleSearch = () => {
getList(queryParams);
};
const handleInfo = (record: any) => {
setIsNoticeOpen(true);
setNoticeInfo(record.noticeTitle);
};
const handleInfoOk = () => {
setIsNoticeOpen(false);
};
const handleInfoCancel = () => {
setIsNoticeOpen(false);
};
const handleDel = async (record: any) => {
Modal.confirm({
title: `确定删除Id为-${record.noticeId}-, 名称为-${record.noticeTitle}-的岗位吗?`,
icon: <ExclamationCircleFilled />,
async onOk() {
const { code } = await deleteNoticeInfoApi(record.noticeId);
try {
if (code === 1000) {
messageApi.open({
type: "success",
content: "删除成功",
});
getList({});
} else {
messageApi.open({
type: "error",
content: "删除失败",
});
}
} catch (err) {
console.log(err);
}
},
onCancel() {
messageApi.open({
type: "warning",
content: "取消成功",
});
},
});
};
const handlePageChange = (page: number) => {
setQueryParams({
...queryParams,
pageNum: page,
});
};
useEffect(() => {
getList(queryParams);
}, []);
return (
<div id="content">
<>
<div className="right">
<div className="search-content">
<>
<Space>
<div></div>
<Select
style={{ width: 220 }}
placeholder="请选择状态"
allowClear
options={[
{ value: "1", label: "已读" },
{ value: "0", label: "未读" },
]}
value={queryParams.state}
onChange={(value: any) =>
setQueryParams({
...queryParams,
state: value,
})
}
/>
</Space>
<Space>
<div className="search-button">
<Button type="primary" onClick={handleSearch}>
<SearchOutlined />
</Button>
</div>
</Space>
<Space>
<div className="reset-button" onClick={handleReset}>
<Button>
<RedoOutlined />
</Button>
</div>
</Space>
</>
</div>
<Modal
title="通知弹框"
open={isNoticeOpen}
onOk={handleInfoOk}
onCancel={handleInfoCancel}
footer={null}
>
<h1>{noticeInfo}</h1>
</Modal>
<div className="table-content">
<>
{contextHolder}
<Table
columns={columns}
dataSource={listData}
className="article-table"
pagination={{
onChange: handlePageChange,
locale: {
jump_to: "跳至",
page: "页",
},
showQuickJumper: true,
showTotal: (total, range) =>
`${range[0]}-${range[1]} 条,共 ${total}`,
}}
/>
</>
</div>
</div>
</>
</div>
);
}

View File

@@ -0,0 +1,61 @@
.Box{
width: 100%;
height: 100%;
background-color: white;
padding: 15px;
.tablebox{
table{
thead{
th::before{
}
tr{
.ant-table-cell{
background: white;
color:#909399
}
}
}
tbody{
tr{
.ant-table-cell{
background: white;
color:#606266
}
}
}
}
.ant-table-cell{
button:focus
{
outline: none;
}
button:hover
{
border: 0px rgb(250, 250, 250);
}
}
}
.newadd{
width: 60px;
height: 40px;
margin-top: 15px;
}
}
.myTree{
margin-top: 10px;
border: 1px solid rgb(173, 131, 131);
width: 250px;
}
.checkBox{
margin-top: 5px;
width: 250px;
display: flex;
label{
height: 20px;
margin-right: 10px;
}
}

View File

@@ -0,0 +1,790 @@
import { useState, useEffect } from "react";
import {
Button,
Input,
Select,
message,
Table,
Tag,
Modal,
Pagination,
Form,
Radio,
ConfigProvider,
Row,
Col,
TreeSelect,
} from "antd";
import type { ColumnsType } from "antd/es/table";
import {
SyncOutlined,
SearchOutlined,
ExclamationCircleFilled,
} from "@ant-design/icons";
import zh_CN from "antd/es/locale/zh_CN";
import "dayjs/locale/zh-cn";
import "./index.scss";
import {
getDataListAPI,
getUserListAPI,
getRolesListAPI,
getDeptsListAPI,
addDataAPI,
deleteDataAPI,
getDataAPI,
} from "../../../../api/system/publish";
interface searchValueType {
dateTime: string[] | undefined;
roleName: string | undefined;
roleKey: string | undefined;
state: string | undefined;
startTime: string | undefined;
endTime: string | undefined;
}
//表格单项数据类型
interface TableDataType {
createTime: string;
dataScope: string;
deptIds: any[];
menuIds: any[];
roleId: number;
roleKey: string;
roleName: string;
roleSort: string;
state: string;
updateTime: string;
key?: number;
total?: number;
}
//当前选择的角色信息
interface queryParamsType {
cluster?: string;
contentType?: string;
noticeTitle?: string;
noticeType?: string;
publishId?: number;
sendType?: string;
pageNum?: number;
pageSize?: number;
}
let thisrole: any = {};
export default function Inform() {
const { confirm } = Modal;
//提示组件
const [messageApi, contextHolder] = message.useMessage();
//搜索框内所有内容
let [searchValue, setSearchValue] = useState<searchValueType>({
dateTime: undefined,
roleName: undefined,
roleKey: undefined,
startTime: undefined,
state: undefined,
endTime: undefined,
});
const [thisNotice, setThisNotice] = useState<any>({});
//Modal
const [isModalOpen, setIsModalOpen] = useState(false);
const [isDetailModalOpen, setIsDetailModalOpen] = useState(false);
const [modalTitle, setModalTitle] = useState("");
//表单区域
const [addForm] = Form.useForm();
const [detailForm] = Form.useForm();
//serach
const [formSearch] = Form.useForm();
//当前页面页数
let [count, setCount] = useState(1);
//表格内数据
let [tableData, setTableData] = useState<TableDataType[]>([]);
//发送对象数据
const [senderIds, setSenderIds] = useState<any>(undefined);
//表格数据总条数
let [total, setTotal] = useState<number>(0);
//表格格式
const columns: ColumnsType<TableDataType> = [
{
title: "序号",
dataIndex: "key",
key: "key",
},
{
title: "公告标题",
dataIndex: "noticeTitle",
key: "noticeTitle",
},
{
title: "公告类型",
dataIndex: "noticeType",
key: "noticeType",
render: (text) => {
switch (text) {
case "1":
return <Tag color="processing"></Tag>;
case "2":
return <Tag color="processing"></Tag>;
}
},
},
{
title: "内容类型",
key: "contentType",
dataIndex: "contentType",
render: (text) => {
switch (text) {
case "text":
return <Tag color=""></Tag>;
case "html":
return <Tag color="success"></Tag>;
}
},
},
{
title: "发送类型",
key: "sendType",
dataIndex: "sendType",
render: (text) => {
switch (text) {
case "user":
return <></>;
case "role":
return <></>;
case "dept":
return <></>;
case "all":
return <></>;
}
},
},
{
title: "创建时间",
key: "createTime",
dataIndex: "createTime",
},
{
title: "操作",
key: "",
dataIndex: "",
render: (text) => (
<>
<Button type="link" onClick={() => handleDetail(text)}>
{"详情"}
</Button>
<Button type="link" danger onClick={() => handleDelete(text)}>
{"删除"}
</Button>
</>
),
},
];
//请求表格内容参数
let [queryTableDataParams, setQueryTableDataParams] = useState<any>({
cluster: undefined,
contentType: undefined,
noticeTitle: undefined,
noticeType: undefined,
publishId: undefined,
sendType: undefined,
pageSize: undefined,
pageNum: undefined,
});
const onSendTypeChange = (value: string) => {
console.log(value);
if (value === "user") {
getUserList();
} else if (value === "role") {
getRolesList();
} else if (value === "dept") {
getDeptsList();
}
};
// let thisrole: any = {roleId:'s'};
const handleDetail = (text: any) => {
getData(text.noticeId);
setIsDetailModalOpen(true);
};
const handleDelete = (text: any) => {
console.log(text);
confirm({
title: "系统提示",
icon: <ExclamationCircleFilled />,
content: `确定删除部门名称为${text.noticeTitle}的数据吗`,
okText: "确定",
okType: "danger",
cancelText: "取消",
onOk() {
(async () => {
try {
let { code } = await deleteDataAPI(text.noticeId);
if (code === 1000) {
messageApi.open({
type: "success",
content: "操作成功",
});
getDataList({});
}
} catch (error) {
console.log(error);
}
})();
},
onCancel() {
console.log("Cancel");
},
});
};
const handleSerach = () => {
searchValue = formSearch.getFieldsValue();
setSearchValue(searchValue);
count = 1;
setQueryTableDataParams({ ...searchValue });
};
const handleReset = () => {
formSearch.resetFields();
searchValue = formSearch.getFieldsValue();
setSearchValue(searchValue);
setQueryTableDataParams({ ...searchValue });
};
//Modal区域
const handleModalOk = () => {
addForm
.validateFields()
.then((values) => {
// form.resetFields();
console.log("add", values);
if (modalTitle == "新增公告") {
postAdd({ ...values, cluster: "notice", action: "SEND" });
}
})
.catch((info) => {
console.log("Validate Failed:", info);
});
console.log("modal thisrole", thisrole);
};
const handleModalCanle = () => {
setIsModalOpen(false);
};
const handleDetailCanle = () => {
setIsDetailModalOpen(false);
};
//新增
const handleNewAdd = () => {
setModalTitle("新增公告");
addForm.setFieldsValue({
noticeTitle: undefined,
noticeType: "1",
sendType: undefined,
senderIds: undefined,
contentType: "html",
});
setIsModalOpen(true);
};
const filterOption = (
input: string,
option?: { label: string; value: string }
) => (option?.label ?? "").toLowerCase().includes(input.toLowerCase());
//表格函数
//从服务器获取数据
//页码改变
const pageChange = (page: number, pageSize: number) => {
console.log(page, pageSize);
queryTableDataParams = { ...queryTableDataParams, pageNum: page, pageSize };
// setQueryParams({...queryParams,pageNum:page,pageSize})
console.log(queryTableDataParams);
getDataList(queryTableDataParams);
setCount(page);
};
//新增请求
const postAdd = async (value: any) => {
try {
let { code } = await addDataAPI(value);
if (code === 1000) {
console.log("success");
messageApi.open({
type: "success",
content: "操作成功",
});
addForm.setFieldsValue({
noticeTitle: undefined,
noticeType: "1",
sendType: undefined,
senderIds: undefined,
contentType: "html",
});
getDataList({ ...queryTableDataParams, pageNum: count });
setIsModalOpen(false);
} else {
messageApi.open({
type: "error",
content: "角色名称已存在",
});
}
} catch (error) {}
};
const getData = async (noticeId: number) => {
try {
const { data, code } = await getDataAPI(noticeId);
if (code === 1000) {
setThisNotice({ ...data });
}
} catch (error) {
console.log(error);
}
};
const getTree = function (tree: any) {
tree.title = tree.deptName;
tree.value = tree.deptId;
if (tree.children) {
for (let child of tree.children) {
getTree(child);
}
}
};
const getUserList = async () => {
try {
const { code, data } = await getUserListAPI();
if (code === 1000) {
let userIds: any = [];
console.log(data.rows);
data.rows.forEach((item: any) => {
userIds.push({ value: item.userId, label: item.userName });
});
setSenderIds([...userIds]);
}
} catch (error) {
console.log(error);
}
};
const getRolesList = async () => {
try {
const { code, data } = await getRolesListAPI();
if (code === 1000) {
let RolesIds: any = [];
console.log(data.rows);
data.rows.forEach((item: any) => {
RolesIds.push({ value: item.roleId, label: item.roleName });
});
setSenderIds([...RolesIds]);
}
} catch (error) {
console.log(error);
}
};
const getDeptsList = async () => {
try {
const { code, data } = await getDeptsListAPI();
if (code === 1000) {
for (let child of data) {
getTree(child);
}
console.log(data);
setSenderIds([...data]);
// setTotal(data.total);
}
} catch (err: any) {
console.log("555", err);
}
};
const getDataList = async (queryTableDataParams: queryParamsType) => {
console.log("参数:", queryTableDataParams);
try {
const { code, data } = await getDataListAPI(queryTableDataParams);
if (code === 1000) {
const sortData = data.rows.sort(
(a: TableDataType, b: TableDataType) =>
parseInt(a.roleSort) - parseInt(b.roleSort)
);
sortData.forEach((element: TableDataType, i: number) => {
element.key = i + 1;
});
setTableData(sortData);
setTotal(data.total);
}
} catch (err: any) {
console.log("555", err);
}
};
useEffect(() => {
// setQueryParams({...queryParams,...valueSch})
queryTableDataParams = {
...queryTableDataParams,
// ...searchValue,
pageNum: count,
};
getDataList(queryTableDataParams);
// console.log(queryParams);
}, [queryTableDataParams]);
return (
<ConfigProvider locale={zh_CN}>
<div className="Box">
<Form
layout="inline"
initialValues={undefined}
form={formSearch}
onFinish={handleSerach}
style={{ fontWeight: "700" }}
>
<Form.Item
label="公告标题"
name="noticeTitle"
style={{ width: 350 + "px", marginTop: "10px" }}
>
<Input placeholder="请输入公告标题" allowClear />
</Form.Item>
<Form.Item
label="公告类型"
name="noticeType"
style={{ width: 350 + "px", marginTop: "10px" }}
>
<Select
style={{ textAlign: "left" }}
allowClear
showSearch
placeholder="请选择公告类型"
optionFilterProp="lable"
filterOption={filterOption}
options={[
{
value: "1",
label: "通知",
},
{
value: "2",
label: "公告",
},
]}
/>
</Form.Item>
<Form.Item
label="内容类型"
name="contentType"
style={{ width: 350 + "px", marginTop: "10px" }}
>
<Select
style={{ textAlign: "left" }}
allowClear
showSearch
placeholder="请选择内容类型"
optionFilterProp="lable"
filterOption={filterOption}
options={[
{
value: "text",
label: "文本",
},
{
value: "html",
label: "富文本",
},
]}
/>
</Form.Item>
<Form.Item
label="发送类型"
name="sendType"
style={{ width: 350 + "px", marginTop: "10px" }}
>
<Select
style={{ textAlign: "left" }}
allowClear
showSearch
placeholder="请选择发送类型"
optionFilterProp="lable"
filterOption={filterOption}
options={[
{
value: "user",
label: "用户",
},
{
value: "role",
label: "角色",
},
{
value: "dept",
label: "部门",
},
{
value: "all",
label: "全发",
},
]}
/>
</Form.Item>
<Form.Item style={{ marginTop: "10px" }}>
<Button
type="primary"
htmlType="submit"
style={{ marginLeft: 20 + "px" }}
onClick={() => handleSerach()}
>
<SearchOutlined />
</Button>
</Form.Item>
<Form.Item style={{ marginTop: "10px" }}>
<Button onClick={() => handleReset()}>
<SyncOutlined />
</Button>
</Form.Item>
</Form>
{/* 新增 */}
<div className="newadd">
<Button type="primary" onClick={handleNewAdd}>
+
</Button>
</div>
{/* 弹出 */}
<Modal
open={isModalOpen}
title={modalTitle}
width={800}
okText="确定"
cancelText="取消"
onCancel={handleModalCanle}
onOk={handleModalOk}
>
<Form
form={addForm}
layout="horizontal"
name="form_in_modal"
initialValues={{ noticeType: "1", contentType: "1" }}
labelAlign="right"
wrapperCol={{ span: 16 }}
>
<Row>
<Col span={12}>
<Form.Item
name="noticeTitle"
label="公告标题"
rules={[{ required: true, message: "请输入公告标题" }]}
>
<Input placeholder="请输入公告标题" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="noticeType"
label="公告类型"
rules={[{ required: true }]}
>
<Radio.Group>
<Radio defaultChecked={true} value="1">
</Radio>
<Radio value="2"></Radio>
</Radio.Group>
</Form.Item>
</Col>
</Row>
<Row>
<Col span={12}>
<Form.Item
required
name="sendType"
label="发送类型"
rules={[{ required: true, message: "选择发送类型" }]}
>
<Select
style={{ textAlign: "left" }}
allowClear
showSearch
placeholder="请选择发送类型"
optionFilterProp="lable"
filterOption={filterOption}
onChange={(value) => onSendTypeChange(value)}
options={[
{
value: "user",
label: "用户",
},
{
value: "role",
label: "角色",
},
{
value: "dept",
label: "部门",
},
{
value: "all",
label: "全发",
},
]}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="senderIds"
label="发送对象"
rules={[{ required: true, message: "选择发送对象" }]}
>
{senderIds === undefined ? (
<Select
mode="multiple"
style={{ textAlign: "left" }}
allowClear
showSearch
placeholder="请选择发送对象"
optionFilterProp="lable"
filterOption={filterOption}
/>
) : senderIds[0].children === undefined ? (
<Select
mode="multiple"
style={{ textAlign: "left" }}
allowClear
showSearch
placeholder="请选择发送对象"
optionFilterProp="lable"
filterOption={filterOption}
options={senderIds}
/>
) : (
<TreeSelect
multiple
style={{ textAlign: "left" }}
dropdownStyle={{ maxHeight: 400, overflow: "auto" }}
placeholder="请选择发送对象"
allowClear
treeData={senderIds}
//
/>
)}
</Form.Item>
</Col>
</Row>
<Form.Item
name="contentType"
label="内容类型"
rules={[{ required: true, message: "选择发送类型" }]}
>
<Radio.Group>
<Radio defaultChecked={true} value="html">
</Radio>
<Radio value="text"></Radio>
</Radio.Group>
</Form.Item>
<Form.Item
name="menuIds"
label="菜单权限"
className="collection-create-form_last-form-item"
></Form.Item>
</Form>
</Modal>
<Modal
open={isDetailModalOpen}
title={"公告详情"}
width={1200}
footer={null}
onCancel={handleDetailCanle}
>
<Form
form={detailForm}
layout="horizontal"
name="form_in_modal"
initialValues={{}}
labelAlign="right"
wrapperCol={{ span: 16 }}
>
<Row>
<Col span={8}>
<Form.Item
name="noticeTitle"
label="公告标题"
// initialValue={'ssss'}
>
<span> {thisNotice.noticeTitle}</span>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
name="sendType"
label="发送类型"
// initialValue={'ssss'}
>
<span>
{" "}
{thisNotice.sendType === "user"
? "用户"
: thisNotice.sendType === "role"
? "角色"
: thisNotice.sendType === "dept"
? "部门"
: "全发"}
</span>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="sendIds" label="发送对象">
<span></span>
</Form.Item>
</Col>
</Row>
<Row>
<Col span={8}>
<Form.Item name="noticeType" label="公告类型">
<span>
{thisNotice.noticeType === "1" ? (
<Tag color="processing"></Tag>
) : (
<Tag color="processing"></Tag>
)}
</span>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item name="contentType" label="内容类型">
<span>
{thisNotice.contentType === "html" ? (
<Tag color="success"></Tag>
) : (
<Tag></Tag>
)}
</span>
</Form.Item>
</Col>
</Row>
<Row>
<Form.Item name="noticeContent" label="公告内容">
<span
dangerouslySetInnerHTML={{ __html: thisNotice.noticeContent }}
></span>
</Form.Item>
</Row>
</Form>
</Modal>
{/* 表格 */}
<div className="tablebox">
{contextHolder}
<Table columns={columns} dataSource={tableData} pagination={false} />
<Pagination
style={{
float: "right",
marginTop: "10px",
}}
total={total}
current={count}
showSizeChanger
showQuickJumper
onChange={(page, pageSize) => pageChange(page, pageSize)}
showTotal={(total) => `${total}`}
/>
</div>
</div>
</ConfigProvider>
);
}

View File

@@ -0,0 +1,103 @@
#content {
display: flex;
border-top: 1px solid black;
.dept-content {
flex: 20%;
padding: 35px 12px 0 0;
height: calc(100vh - 85px);
}
.left-dept {
flex: 20%;
padding: 35px 12px 0 0;
height: calc(100vh - 85px);
border: 1px solid red;
border-top: 1px solid black;
}
.right {
flex: 80%;
height: 190px;
display: flex;
flex-direction: column;
.search-content {
display: flex;
flex-direction: column;
padding: 35px 12px 0 0;
.content-top {
display: flex;
.input-item {
display: flex;
align-items: center;
margin-right: 20px;
width: 270px;
div {
width: 120px;
font-weight: bold;
}
}
:nth-child(3) {
div {
margin-right: -30px;
}
}
}
.content-bottom {
display: flex;
margin-left: 12px;
margin-top: 20px;
div {
margin-right: 10px;
}
.time-selector {
display: flex;
align-items: center;
div {
width: 120px;
font-weight: bold;
}
.time-picker {
width: 400px;
text-align: center;
margin-left: -30px;
margin-right: 25px;
}
}
button:focus,
button:focus-visible {
outline: none;
}
}
}
.table-content {
margin: 12px 0 0 12px;
.ant-table-thead > tr > th {
color: #909399;
text-align: center;
}
.ant-table-tbody > tr > td {
text-align: center;
}
}
.add-content {
display: flex;
margin-left: 12px;
margin-top: 20px;
button:focus,
button:focus-visible {
outline: none;
}
}
.user-mid-button {
display: flex;
margin-left: 12px;
margin-top: 20px;
button:focus,
button:focus-visible {
outline: none;
}
}
}
}

View File

@@ -0,0 +1,472 @@
import "./index.scss";
import {
Input,
Select,
Button,
Table,
Space,
Tag,
message,
Modal,
Form,
Row,
Col,
Radio,
InputNumber,
} from "antd";
import {
EditOutlined,
DeleteOutlined,
ExclamationCircleFilled,
} from "@ant-design/icons";
import {
getPostListApi,
addPostApi,
editPostApi,
deletePostApi,
} from "../../../api/system/post";
import { useEffect, useState } from "react";
import { SearchOutlined, RedoOutlined, PlusOutlined } from "@ant-design/icons";
export default function Post() {
const [form] = Form.useForm();
const [echoData, setEchoData] = useState<QueryParamsType>({});
const [listData, setListData] = useState([]);
const [radioValue, setRadioValue] = useState(1);
const [messageApi, contextHolder] = message.useMessage();
const [modalTitle, setModalTitle] = useState<string>("");
const [buttonType, setButtonType] = useState<string>("");
const [isShowModal, setIsShowModal] = useState<boolean>(false);
const [queryParams, setQueryParams] = useState<QueryParamsType>({
postName: undefined,
postCode: undefined,
state: undefined,
postSort: undefined,
pageSize: undefined,
pageNum: undefined,
});
const columns = [
{
title: "序号",
dataIndex: "postId",
key: "Id",
render: (text: string) => <>{text ? text : "null"}</>,
},
{
title: "岗位名称",
dataIndex: "postName",
key: "postName",
render: (text: string) => <>{text ? text : "null"}</>,
},
{
title: "岗位编码",
dataIndex: "postCode",
key: "postCode",
render: (text: string) => <>{text ? text : "null"}</>,
},
{
title: "排序",
key: "postSort",
dataIndex: "postSort",
render: (text: string) => <>{text ? text : "--"}</>,
},
{
title: "状态",
key: "state",
dataIndex: "state",
render: (text: string) => (
<>
{text === "0" ? (
<Tag color="processing"></Tag>
) : (
<Tag color="processing"></Tag>
)}
</>
),
},
{
title: "创建时间",
key: "createTime",
dataIndex: "createTime",
render: (text: string) => <>{text ? text : "null"}</>,
},
{
title: "操作",
key: "operate",
dataIndex: "operate",
render: (_: string, record: object) => (
<div style={{ color: "#58aaff" }}>
<Space>
<EditOutlined />
<a
href="#"
onClick={() => handleEdit(record)}
style={{ color: " #58aaff" }}
>
</a>
<DeleteOutlined />
<a
href="#"
onClick={() => handleDel(record)}
style={{ color: " #58aaff" }}
>
</a>
</Space>
</div>
),
},
];
interface QueryParamsType {
postName?: string;
postCode?: string;
state?: string;
postSort?: string;
postId?: string;
pageSize?: number;
pageNum?: number;
}
const getPostList = async (newQueryParams: object) => {
try {
const { code, data } = await getPostListApi(newQueryParams);
if (code === 1000) {
setListData(data.rows);
}
} catch (err) {
console.error(err);
}
};
const handleAdd = () => {
setModalTitle("新增用户");
setButtonType("ADD");
setIsShowModal(true);
};
const handleEdit = (data: any) => {
setModalTitle("修改用户");
setButtonType("EDIT");
setIsShowModal(true);
console.log(data);
setEchoData({
postId: data.postId,
});
handleEcho(data);
};
const handleConfirm = () => {
form.validateFields().then((values) => {
console.log(values);
console.log(buttonType);
if (buttonType === "ADD") {
handleConfirmAdd(values);
} else if (buttonType === "EDIT") {
handleConfirmEdit(values);
} else {
messageApi.open({
type: "error",
content: "系统异常,请刷新后重试",
});
}
});
};
const handleConfirmAdd = async (data: object) => {
console.log(data);
const { code } = await addPostApi(data);
try {
if (code === 1000) {
messageApi.open({
type: "success",
content: "创建成功",
});
setQueryParams({});
handleClear();
} else {
messageApi.open({
type: "error",
content: "岗位名称已经存在",
});
}
} catch (err) {
console.log(err);
}
};
const handleConfirmEdit = async (data: object) => {
const editData = {
...data,
postId: echoData.postId,
};
console.log(editData);
const { code } = await editPostApi(editData);
try {
if (code === 1000) {
messageApi.open({
type: "success",
content: "修改成功",
});
setQueryParams({});
handleClear();
} else {
messageApi.open({
type: "error",
content: "修改失败",
});
}
} catch (err) {
console.log(err);
}
};
const handleReset = async () => {
setQueryParams({});
getPostList({});
};
const handleSearch = () => {
getPostList(queryParams);
};
const handleDel = async (record: any) => {
Modal.confirm({
title: `确定删除Id为-${record.postId}-, 岗位名称为-${record.postName}-的岗位吗?`,
icon: <ExclamationCircleFilled />,
async onOk() {
const { code } = await deletePostApi(record.postId);
try {
if (code === 1000) {
messageApi.open({
type: "success",
content: "删除成功",
});
setQueryParams({});
} else {
messageApi.open({
type: "error",
content: "删除失败",
});
}
} catch (err) {
console.log(err);
}
},
onCancel() {
messageApi.open({
type: "warning",
content: "取消成功",
});
},
});
};
const handleSearchValues = (e: any) => {
const { name, value } = e.target;
setQueryParams({ ...queryParams, [name]: value });
};
const handlePageChange = (page: number) => {
setQueryParams({
...queryParams,
pageNum: page,
});
};
const handleEcho = (data: any) => {
const { postName, postCode, state, postSort } = data;
form.setFieldsValue({
postName,
postCode,
state,
postSort,
});
};
const handleCancel = () => {
handleClear();
};
const handleClear = () => {
form.resetFields();
setIsShowModal(false);
};
const onRadio = (e: any) => {
setRadioValue(e.target.value);
};
useEffect(() => {
getPostList(queryParams);
}, []);
return (
<div id="content">
<>
<div className="right">
<div className="search-content">
<>
<div className="content-top">
<div className="input-item">
<div></div>
<Input
placeholder="请输入岗位名称"
name="postName"
allowClear
value={queryParams.postName}
onChange={handleSearchValues}
/>
</div>
<div className="input-item">
<div></div>
<Input
placeholder="请输入岗位编码"
name="postCode"
allowClear
value={queryParams.postCode}
onChange={handleSearchValues}
/>
</div>
<div className="input-item">
<div></div>
<Select
style={{ width: 120 }}
placeholder="用户状态"
allowClear
options={[
{ value: "1", label: "正常" },
{ value: "0", label: "停用" },
]}
value={queryParams.state}
onChange={(value: any) =>
setQueryParams({
...queryParams,
state: value,
})
}
/>
</div>
</div>
<div className="content-bottom">
<div className="search-button">
<Button type="primary" onClick={handleSearch}>
<SearchOutlined />
</Button>
</div>
<div className="reset-button" onClick={handleReset}>
<Button>
<RedoOutlined />
</Button>
</div>
</div>
</>
</div>
<div className="add-content">
<>
<Button type="primary" onClick={handleAdd}>
<PlusOutlined />
</Button>
</>
</div>
<div className="table-content">
<>
{contextHolder}
<Table
columns={columns}
dataSource={listData}
className="article-table"
pagination={{
onChange: handlePageChange,
locale: {
jump_to: "跳至",
page: "页",
},
showQuickJumper: true,
showTotal: (total, range) =>
`${range[0]}-${range[1]} 条,共 ${total}`,
}}
/>
</>
</div>
<>
{contextHolder}
<Modal
title={modalTitle}
width={600}
okText="确认"
cancelText="取消"
open={isShowModal}
onOk={handleConfirm}
onCancel={handleCancel}
>
<Form
form={form}
name="basic"
initialValues={{ remember: true }}
onFinish={handleConfirm}
labelCol={{ span: 4 }}
autoComplete="off"
className="modal-form"
>
<Row>
<Col span={24}>
<Form.Item
label="岗位名称"
name="postName"
rules={[{ required: true, message: "请输入岗位名称" }]}
>
<Input defaultValue={""} placeholder="请输入岗位名称" />
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
label="岗位编码"
name="postCode"
rules={[{ required: true, message: "请输入岗位编码" }]}
>
<Input defaultValue={""} placeholder="请输入岗位编码" />
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
label="显示顺序"
name="postSort"
rules={[{ required: false }]}
>
<InputNumber />
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
label="状态"
name="state"
rules={[
{
required: false,
},
]}
>
<Radio.Group onChange={onRadio} value={radioValue}>
<Radio value="1"></Radio>
<Radio value="0"></Radio>
</Radio.Group>
</Form.Item>
</Col>
</Row>
</Form>
</Modal>
</>
</div>
</>
</div>
);
}

View File

@@ -0,0 +1,103 @@
.roleBox{
width: 100%;
height: 100%;
background-color: white;
padding: 15px;
// display: flex;
// flex-wrap: wrap;
// .SearchBox{
// display: flex;
// flex-wrap: wrap;
// .ipt{
// margin-top: 10px;
// margin-right: 10px;
// height: 32px;
// line-height: 32px;
// display: flex;
// span{
// font-weight: 700;
// // line-height: 32px;
// padding-right: 12px;
// // color: #606266;
// }
// .item,input{
// flex: 1;
// font-weight: 400;
// // width: 300px;
// margin-right: 40px;
// .ant-space-item{
// // margin-left: 500px;
// width: 500px;
// }
// }
// }
// .btn{
// button{
// margin-right: 15px;
// }
// margin-top: 10px;
// }
// }
// .btn{
// button{
// margin-right: 15px;
// }
// margin-top: 10px;
// }
.tablebox{
table{
thead{
th::before{
}
tr{
.ant-table-cell{
background: white;
color:#909399
}
}
}
tbody{
tr{
.ant-table-cell{
background: white;
color:#606266
}
}
}
}
.ant-table-cell{
button:focus
{
outline: none;
}
button:hover
{
border: 0px rgb(250, 250, 250);
}
}
}
.newadd{
width: 60px;
height: 40px;
margin-top: 15px;
}
}
.myTree{
margin-top: 10px;
border: 1px solid rgb(173, 131, 131);
width: 250px;
}
.checkBox{
margin-top: 5px;
width: 250px;
display: flex;
label{
height: 20px;
margin-right: 10px;
}
}

View File

@@ -0,0 +1,715 @@
import { useState, useEffect } from "react";
import {
Button,
Input,
Select,
DatePicker,
Space,
message,
Table,
Tag,
Modal,
Pagination,
Form,
Checkbox,
InputNumber,
Radio,
Tree,
ConfigProvider,
} from "antd";
import type { CheckboxChangeEvent } from "antd/es/checkbox";
import type { DatePickerProps, RangePickerProps } from "antd/es/date-picker";
import type { ColumnsType } from "antd/es/table";
import {
SyncOutlined,
SearchOutlined,
ExclamationCircleFilled,
} from "@ant-design/icons";
import zh_CN from "antd/es/locale/zh_CN";
import dataLocale from "antd/es/date-picker/locale/zh_CN";
import "dayjs/locale/zh-cn";
import "./index.scss";
import {
getRolesDataAPI,
delRoleAPI,
getRoleAPI,
addRoleAPI,
setRoleDataAPI,
getMenuLiseAPI,
} from "../../../api/system/role";
// import { useForm } from "antd/es/form/Form";
interface searchValueType {
dateTime: string[] | undefined;
roleName: string | undefined;
roleKey: string | undefined;
state: string | undefined;
startTime: string | undefined;
endTime: string | undefined;
}
//表格单项数据类型
interface TableDataType {
createTime: string;
dataScope: string;
deptIds: any[];
menuIds: any[];
roleId: number;
roleKey: string;
roleName: string;
roleSort: string;
state: string;
updateTime: string;
key?: number;
total?: number;
}
//当前选择的角色信息
const fieldNames = {
title: "menuName",
key: "menuId",
children: "children",
};
let thisrole: any = {};
let treeIds: number[] = [];
export default function Role() {
const { confirm } = Modal;
const { RangePicker } = DatePicker;
//提示组件
const [messageApi, contextHolder] = message.useMessage();
//搜索框内所有内容
let [searchValue, setSearchValue] = useState<searchValueType>({
dateTime: undefined,
roleName: undefined,
roleKey: undefined,
startTime: undefined,
state: undefined,
endTime: undefined,
});
//树列表数据
const [menuTreeData, setMenuTreeData] = useState([]);
//父子联动
const [checkStrictly, setCheckStrictly] = useState(false);
//树所有节点IDs
//树是否自动展开
const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
const [checkedKeys, setCheckedKeys] = useState<React.Key[]>([]);
const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]);
//Modal
const [isModalOpen, setIsModalOpen] = useState(false);
const [modalTitle, setModalTitle] = useState("");
//表单区域
const [form] = Form.useForm();
//serach
const [formSearch] = Form.useForm();
//当前页面页数
let [count, setCount] = useState(1);
//表格内数据
let [tableData, setTableData] = useState<TableDataType[]>([]);
//表格数据总条数
let [total, setTotal] = useState<number>(0);
//表格格式
const columns: ColumnsType<TableDataType> = [
{
title: "序号",
dataIndex: "key",
key: "key",
},
{
title: "角色名称",
dataIndex: "roleName",
key: "roleName",
},
{
title: "角色权限",
dataIndex: "roleKey",
key: "roleKey",
},
{
title: "数据范围",
key: "dataScope",
dataIndex: "dataScope",
render: (text) => {
switch (text) {
case "1":
return <></>;
case "2":
return <></>;
case "3":
return <></>;
default:
return <></>;
}
},
},
{
title: "显示顺序",
key: "roleSort",
dataIndex: "roleSort",
},
{
title: "状态",
key: "state",
dataIndex: "state",
render: (text) => (
<>
{text === "0" ? (
<Tag color="success"></Tag>
) : (
<Tag color="error"></Tag>
)}
</>
),
},
{
title: "创建时间",
key: "createTime",
dataIndex: "createTime",
},
{
title: "操作",
key: "",
dataIndex: "",
render: (text) => (
<>
<Button type="link" onClick={() => handleEdit(text)}>
{"修改"}
</Button>
{/* <Button type='link' danger onClick={() => handleClickDel(text)}>{'删除'}</Button> */}
<Button type="link" danger onClick={() => handleDelete(text)}>
{"删除"}
</Button>
<Button type="link">{"分配用户"}</Button>
</>
),
},
];
//请求表格内容参数
let [queryTableDataParams, setQueryTableDataParams] = useState<any>({
roleKey: undefined,
roleName: undefined,
state: undefined,
endTime: undefined,
startTime: undefined,
pageNum: undefined,
pageSize: undefined,
});
const [allChecked, setAllChecked] = useState(false);
const onTreeExpand = (expandedKeysValue: React.Key[]) => {
// console.log("onExpand", expandedKeysValue);
setExpandedKeys(expandedKeysValue);
};
const onCheckExpand = (e: CheckboxChangeEvent) => {
console.log(`checked = ${e.target.checked}`);
console.log(treeIds);
if (e.target.checked) setExpandedKeys(treeIds);
else setExpandedKeys([]);
console.log(expandedKeys);
};
const onCheckAll = (e: CheckboxChangeEvent) => {
console.log(`checked = ${e.target.checked}`);
setAllChecked(!allChecked);
if (!allChecked) setCheckedKeys(treeIds);
else setCheckedKeys([]);
};
const onCheckFaSon = (e: CheckboxChangeEvent) => {
console.log(`checked = ${e.target.checked}`);
setCheckStrictly(!e.target.checked);
};
const onTreeCheck = (checkedKeysValue: any) => {
// console.log("onCheck", checkedKeysValue.checked);
console.log("onCheck", checkedKeysValue);
console.log("treeIds", treeIds);
if (checkedKeysValue.length != treeIds.length) {
setAllChecked(false);
} else {
setAllChecked(true);
}
if (checkStrictly === false) {
form.setFieldsValue({ menuIds: checkedKeysValue });
setCheckedKeys(checkedKeysValue);
} else {
form.setFieldsValue({ menuIds: checkedKeysValue.checked });
setCheckedKeys(checkedKeysValue.checked);
}
};
const onTreeSelect = (selectedKeysValue: React.Key[], info: any) => {
console.log("onSelect", info);
// setExpandedKeys([...expandedKeys,...selectedKeysValue])
setSelectedKeys(selectedKeysValue);
};
//当前角色的数据
// let thisrole: any = {roleId:'s'};
const handleEdit = async (role: any) => {
await getRoleData(role);
setModalTitle("修改角色");
// setCheckedKeys(role)
setIsModalOpen(true);
};
const handleDelete = (role: any) => {
console.log(role);
confirm({
title: "系统提示",
icon: <ExclamationCircleFilled />,
content: `确定删除角色名称为${role.roleName}的数据吗`,
okText: "确定",
okType: "danger",
cancelText: "取消",
onOk() {
handleClickDel(role);
},
onCancel() {
console.log("Cancel");
},
});
};
const handleSerach = () => {
searchValue = formSearch.getFieldsValue();
if (typeof searchValue.dateTime !== "undefined") {
searchValue.startTime = (searchValue.dateTime as string[])[0];
searchValue.endTime = (searchValue.dateTime as string[])[1];
}
setSearchValue(searchValue);
count = 1;
setQueryTableDataParams({ ...searchValue });
};
const handleReset = () => {
formSearch.resetFields();
searchValue = formSearch.getFieldsValue();
setSearchValue(searchValue);
setQueryTableDataParams({ ...searchValue });
// setSearchValue({ ...searchValue });
// setCount(1)
};
//Modal区域
const handleModalOk = () => {
form
.validateFields()
.then((values) => {
// form.resetFields();
console.log("add", values);
if (modalTitle == "新增角色") {
postAdd({ ...values, menuIds: checkedKeys });
} else if (modalTitle == "修改角色") {
console.log(thisrole);
console.log(checkedKeys);
console.log("now", { ...thisrole, ...values, menuIds: checkedKeys });
// console.log({ ...thisrole, ...values,menuIds:setCheckedKeys });
thisrole = { ...thisrole, ...values, menuIds: checkedKeys };
editRoleData({ ...thisrole });
}
})
.catch((info) => {
console.log("Validate Failed:", info);
});
console.log("modal thisrole", thisrole);
};
const handleModalCanle = () => {
setIsModalOpen(false);
};
//新增
const handleNewAdd = () => {
setModalTitle("新增角色");
form.setFieldsValue({
roleName: "",
roleKey: "",
state: "0",
dataScope: "1",
roleSort: "0",
});
setCheckedKeys([]);
setIsModalOpen(true);
};
const onDateChange = (
valueDate: DatePickerProps["value"] | RangePickerProps["value"],
dateString: [string, string] | string
) => {
// setSelectedDate(valueDate)
console.log(valueDate);
searchValue.startTime = dateString[0];
searchValue.endTime = dateString[1];
formSearch.setFieldsValue({ dateTime: [dateString[0], dateString[1]] });
setSearchValue(searchValue);
// setSearchValue({ ...searchValue })
};
const filterOption = (
input: string,
option?: { label: string; value: string }
) => (option?.label ?? "").toLowerCase().includes(input.toLowerCase());
//表格函数
//从服务器获取数据
//页码改变
const pageChange = (page: number, pageSize: number) => {
console.log(page, pageSize);
queryTableDataParams = { ...queryTableDataParams, pageNum: page, pageSize };
// setQueryParams({...queryParams,pageNum:page,pageSize})
console.log(queryTableDataParams);
getRolesData(queryTableDataParams);
setCount(page);
};
//删除单项
const handleClickDel = (role: TableDataType) => {
console.log(role);
let { roleId } = role;
del(roleId);
};
//遍历菜单树获取所有节点ID
const getTreeIds = function (tree: any) {
// console.log('Ids:',tree);
tree.forEach((item: any) => {
// console.log(item);
treeIds.push(item.menuId);
if (item.children != null) getTreeIds(item.children);
});
};
//获取单个角色详细信息
const getRoleData = async (role: any) => {
// const {roleId,roleKey,roleName,roleSort,dataScope,state,meunIds}=role
// thisrole=role
try {
let { code, data } = await getRoleAPI(role);
if (code === 1000) {
console.log("role:", data);
thisrole = data;
console.log("thisrole", thisrole);
form.setFieldsValue(thisrole);
setCheckedKeys(data.menuIds);
}
} catch (error) {
console.log(error);
}
// setOpen(true);
};
//获取菜单权限树
const getMenuList = async () => {
try {
let menul = await getMenuLiseAPI();
console.log("tree", menul.data);
if (treeIds.length == 0) getTreeIds(menul.data);
console.log(treeIds);
setMenuTreeData(menul.data);
// treeData = menul.data
// setMenulist(menul.data)
} catch (error) {
console.log(error);
}
};
//新增请求
const postAdd = async (value: any) => {
try {
let { code } = await addRoleAPI(value);
console.log(code);
if (code === 1000) {
console.log("success");
messageApi.open({
type: "success",
content: "操作成功",
});
form.setFieldsValue({
roleName: "",
roleKey: "",
state: "0",
dataScope: "1",
});
getRolesData({ ...queryTableDataParams, pageNum: count });
setIsModalOpen(false);
} else {
messageApi.open({
type: "error",
content: "角色名称已存在",
});
}
} catch (error) {}
};
const getRolesData = async (queryTableDataParams: any) => {
console.log("参数:", queryTableDataParams);
try {
const { code, data } = await getRolesDataAPI(queryTableDataParams);
if (code === 1000) {
const sortData = data.rows.sort(
(a: TableDataType, b: TableDataType) =>
parseInt(a.roleSort) - parseInt(b.roleSort)
);
sortData.forEach((element: TableDataType, i: number) => {
element.key = i + 1;
});
setTableData(sortData);
setTotal(data.total);
}
} catch (err: any) {
console.log("555", err);
}
};
//服务器删除角色
const del = async (roleId: number) => {
try {
let { code } = await delRoleAPI({ roleId });
if (code === 1000) {
console.log("del", roleId);
messageApi.open({
type: "success",
content: "操作成功",
});
getRolesData({ pageNum: count });
}
} catch (error) {
console.log(error);
}
};
//修改角色信息
const editRoleData = async (role: any) => {
console.log(role);
try {
let { code, data } = await setRoleDataAPI(thisrole);
// let a = await setRoleDataAPI(role);
if (code === 1000) {
thisrole = { ...thisrole, ...data };
getRolesData({ ...queryTableDataParams, pageNum: count });
messageApi.open({
type: "success",
content: "操作成功",
});
setIsModalOpen(false);
form.setFieldsValue({
roleName: "",
roleKey: "",
state: "0",
dataScope: "1",
});
} else {
messageApi.open({
type: "error",
content: "角色名称已存在!",
});
}
} catch (error) {
messageApi.open({
type: "error",
content: "系统未知错误",
});
console.log(error);
}
};
useEffect(() => {
// setQueryParams({...queryParams,...valueSch})
queryTableDataParams = {
...queryTableDataParams,
// ...searchValue,
pageNum: count,
};
getRolesData(queryTableDataParams);
// console.log(queryParams);
}, [queryTableDataParams]);
useEffect(() => {
getMenuList();
}, []);
return (
<ConfigProvider locale={zh_CN}>
<div className="roleBox">
<Form
layout="inline"
initialValues={undefined}
form={formSearch}
onFinish={handleSerach}
style={{ fontWeight: "700" }}
>
<Form.Item
label="角色名称"
name="roleName"
style={{ width: 300 + "px", marginTop: "10px" }}
>
<Input placeholder="请输入角色名称" allowClear />
</Form.Item>
<Form.Item
label="权限字符"
name="roleKey"
style={{ width: 300 + "px", marginTop: "10px" }}
>
<Input allowClear placeholder="请输入权限字符" />
</Form.Item>
<Form.Item
label="状态:"
name="state"
wrapperCol={{ span: 22 }}
style={{ marginTop: "10px" }}
>
<Select
style={{ width: 300 + "px", textAlign: "left" }}
allowClear
showSearch
placeholder="请选择角色状态"
optionFilterProp="lable"
// onChange={onStateChange}
// onSearch={onSearch}
filterOption={filterOption}
// value={searchValue.state}
options={[
{
value: "0",
label: "正常",
},
{
value: "1",
label: "停用",
},
]}
/>
</Form.Item>
<Form.Item
label="登陆时间"
name="dateTime"
style={{ marginTop: "10px" }}
>
<Space direction="vertical" style={{ width: 400 + "px" }}>
<RangePicker
locale={dataLocale}
showTime
format="YYYY-MM-DD HH:mm:ss"
onChange={onDateChange}
/>
</Space>
</Form.Item>
<Form.Item style={{ marginTop: "10px" }}>
<Button
type="primary"
htmlType="submit"
style={{ marginLeft: 20 + "px" }}
onClick={() => handleSerach()}
>
<SearchOutlined />
</Button>
</Form.Item>
<Form.Item style={{ marginTop: "10px" }}>
<Button onClick={() => handleReset()}>
<SyncOutlined />
</Button>
</Form.Item>
</Form>
{/* 新增 */}
<div className="newadd">
<Button type="primary" onClick={handleNewAdd}>
+
</Button>
</div>
{/* 弹出 */}
<Modal
open={isModalOpen}
title={modalTitle}
okText="确定"
cancelText="取消"
onCancel={handleModalCanle}
onOk={handleModalOk}
>
<Form
form={form}
layout="horizontal"
name="form_in_modal"
initialValues={{ dataScope: "1", state: "0" }}
>
<Form.Item
name="roleName"
label="角色名称"
rules={[{ required: true, message: "请输入角色名称" }]}
>
<Input placeholder="请输入角色名称" />
</Form.Item>
<Form.Item
name="roleKey"
label="角色权限"
rules={[{ required: true, message: "请输入角色权限" }]}
>
<Input placeholder="请输入角色权限" />
</Form.Item>
<Form.Item
required
name="roleSort"
label="显示顺序"
rules={[{ required: true, message: "请输入角色显示顺序" }]}
>
<InputNumber min={0} max={10} controls={true} />
</Form.Item>
<Form.Item name="dataScope" label="数据范围">
<Radio.Group>
<Radio value="1" defaultChecked={true}>
</Radio>
<Radio value="2"></Radio>
<Radio value="3"></Radio>
<Radio value="4"></Radio>
</Radio.Group>
</Form.Item>
<Form.Item
name="state"
label="状态"
className="collection-create-form_last-form-item"
>
<Radio.Group>
<Radio defaultChecked={true} value="0">
</Radio>
<Radio value="1"></Radio>
</Radio.Group>
</Form.Item>
<Form.Item
name="menuIds"
label="菜单权限"
className="collection-create-form_last-form-item"
>
{/* <input type="text" /> */}
<div className="checkBox">
<Checkbox onChange={onCheckExpand}></Checkbox>
<Checkbox onChange={onCheckAll} checked={allChecked}>
</Checkbox>
<Checkbox onChange={onCheckFaSon}></Checkbox>
</div>
<div
className="myTree"
style={{ maxHeight: "250px", overflowY: "auto" }}
>
<Tree
fieldNames={fieldNames}
checkable
onExpand={onTreeExpand}
expandedKeys={expandedKeys}
autoExpandParent={true}
onCheck={onTreeCheck}
checkedKeys={checkedKeys}
checkStrictly={checkStrictly}
onSelect={onTreeSelect}
selectedKeys={selectedKeys}
treeData={menuTreeData}
/>
</div>
</Form.Item>
</Form>
</Modal>
{/* 表格 */}
<div className="tablebox">
{contextHolder}
<Table columns={columns} dataSource={tableData} pagination={false} />
<Pagination
style={{
float: "right",
marginTop: "10px",
}}
total={total}
current={count}
showSizeChanger
showQuickJumper
onChange={(page, pageSize) => pageChange(page, pageSize)}
showTotal={(total) => `${total}`}
/>
</div>
</div>
</ConfigProvider>
);
}

View File

@@ -0,0 +1,104 @@
#content {
display: flex;
border-top: 1px solid black;
.dept-content {
flex: 20%;
padding: 35px 12px 0 0;
height: calc(100vh - 85px);
}
.left-dept {
flex: 20%;
padding: 35px 12px 0 0;
height: calc(100vh - 85px);
border: 1px solid red;
border-top: 1px solid black;
}
.right-user {
flex: 80%;
height: 190px;
display: flex;
flex-direction: column;
.search-content {
display: flex;
flex-direction: column;
padding: 35px 12px 0 0;
.content-top {
display: flex;
.input-item {
display: flex;
align-items: center;
margin-right: 20px;
width: 270px;
div {
width: 120px;
font-weight: bold;
}
}
:nth-child(3) {
div {
margin-right: -30px;
}
}
}
.content-bottom {
display: flex;
margin-left: -20px;
margin-top: 20px;
div {
margin-right: 10px;
}
.time-selector {
display: flex;
align-items: center;
div {
width: 120px;
font-weight: bold;
}
.time-picker {
width: 400px;
text-align: center;
margin-left: -30px;
margin-right: 25px;
}
}
button:focus,
button:focus-visible {
outline: none;
}
}
}
.table-content {
margin: 12px 0 0 12px;
.ant-table-thead > tr > th {
color: #909399;
text-align: center;
}
.ant-table-tbody > tr > td {
text-align: center;
}
}
.add-content {
display: flex;
margin-left: 12px;
margin-top: 20px;
button:focus,
button:focus-visible {
outline: none;
}
}
.user-mid-button {
display: flex;
margin-left: 12px;
margin-top: 20px;
button:focus,
button:focus-visible {
outline: none;
}
}
}
}

View File

@@ -1,7 +1,807 @@
import React from 'react'
import "./index.scss";
import {
Input,
Tree,
Select,
DatePicker,
Button,
Table,
Space,
Tag,
message,
Modal,
Form,
Row,
Col,
TreeSelect,
Radio,
} from "antd";
import {
EditOutlined,
DeleteOutlined,
ExclamationCircleFilled,
} from "@ant-design/icons";
import {
getUserListApi,
deleteUserApi,
getDeptTreeApi,
addUserApi,
getRoleListApi,
getUserInfoApi,
editUserApi,
} from "../../../api/system/users";
import dataLocale from "antd/es/date-picker/locale/zh_CN";
import React, { useEffect, useState, useMemo } from "react";
import { SearchOutlined, PlusOutlined } from "@ant-design/icons";
export default function User() {
return(
<div>user</div>
)
const postIdList: number[] = [];
const [form] = Form.useForm();
const [searchForm] = Form.useForm();
const [echoId, setEchoId] = useState<any>();
const [myTreeData, setMyTreeData] = useState<[]>([]);
const [listData, setListData] = useState([]);
const [radioValue, setRadioValue] = useState(1);
const [searchTreeValue, setSearchTreeValue] = useState("");
const [roleList, setRoleList] = useState<any[]>([]);
const [messageApi, contextHolder] = message.useMessage();
const [modalTitle, setModalTitle] = useState<string>("");
const [buttonType, setButtonType] = useState<string>("");
const [isShowModal, setIsShowModal] = useState<boolean>(false);
const [autoExpandParent, setAutoExpandParent] = useState(true);
const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([
100, 102, 101, 104,
]);
const [treeValue, setTreeValue] = useState<string>();
const fieldNames = {
label: "deptName",
title: "deptName",
value: "deptId",
key: "deptName",
};
const [queryParams, setQueryParams] = useState<QueryParamsType>({
deptId: undefined,
userName: undefined,
phoneNumber: undefined,
state: undefined,
startTime: undefined,
endTime: undefined,
pageNum: undefined,
pageSize: undefined,
});
const columns = [
{
title: "序号",
dataIndex: "userId",
key: "Id",
render: (text: string) => <>{text ? text : "null"}</>,
},
{
title: "用户名称",
dataIndex: "userName",
key: "userName",
render: (text: string) => <>{text ? text : "null"}</>,
},
{
title: "用户昵称",
dataIndex: "nickName",
key: "nikeName",
render: (text: string) => <>{text ? text : "null"}</>,
},
{
title: "部门",
key: "deptId",
dataIndex: "deptName",
render: (text: string) => <>{text ? text : "--"}</>,
},
{
title: "手机号码",
key: "phoneNumber",
dataIndex: "phoneNumber",
render: (text: string) => <>{text ? text : "--"}</>,
},
{
title: "状态",
key: "state",
dataIndex: "state",
render: (text: string) => (
<>
{text === "0" ? (
<Tag color="processing"></Tag>
) : (
<Tag color="processing"></Tag>
)}
</>
),
},
{
title: "创建时间",
key: "createTime",
dataIndex: "createTime",
render: (text: string) => <>{text ? text : "null"}</>,
},
{
title: "操作",
key: "operate",
dataIndex: "operate",
render: (_: string, record: object) => (
<div style={{ color: "#58aaff" }}>
<Space>
<EditOutlined />
<a
href="#"
onClick={() => handleEdit(record)}
style={{ color: " #58aaff" }}
>
</a>
<DeleteOutlined />
<a
href="#"
onClick={() => handleDel(record)}
style={{ color: " #58aaff" }}
>
</a>
</Space>
</div>
),
},
];
interface AddUserType {
email: string;
nickName: string;
password: string;
phoneNumber: string;
postIds: number[];
roleIds: number[];
sex: string;
state: string;
userName: string;
}
interface QueryParamsType {
deptId?: number;
userName?: string;
phoneNumber?: string;
state?: string;
startTime?: string | any;
endTime?: string | any;
pageNum?: number;
pageSize?: number;
}
let addUserParams: AddUserType = {
email: "",
nickName: "",
password: "",
phoneNumber: "",
postIds: [],
roleIds: [],
sex: "",
state: "",
userName: "",
};
const getDeptTreeData = async () => {
try {
const { code, data } = await getDeptTreeApi();
if (code === 1000) {
setMyTreeData(data);
handleLoopPostList(data);
setExpandedKeys(postIdList);
}
} catch (error) {
console.error(error);
}
};
const getUserList = async (newQueryParams: object) => {
try {
const { code, data } = await getUserListApi(newQueryParams);
if (code === 1000) {
setListData(data.rows);
}
} catch (err) {
console.error(err);
}
};
const getUserInfo = async () => {
try {
const { code, data } = await getUserInfoApi(echoId);
if (code === 1000) {
handleEcho(data);
}
} catch (error) {
console.error(error);
}
};
const getRoleList = async () => {
try {
const { code, data } = await getRoleListApi();
if (code === 1000) {
setRoleList(data.rows);
}
} catch (err) {
console.log(err);
}
};
const handleAdd = () => {
setModalTitle("新增用户");
setButtonType("ADD");
setIsShowModal(true);
};
const handleEdit = (echoData: any) => {
setModalTitle("修改用户");
setButtonType("EDIT");
setIsShowModal(true);
setEchoId(echoData.userId);
};
const handleConfirm = () => {
form.validateFields().then((values) => {
Object.keys(values).forEach(
(key) => values[key] === undefined && delete values[key]
);
addUserParams = {
...addUserParams,
...values,
userId: echoId,
};
if (buttonType === "ADD") {
handleAddUser();
} else if (buttonType === "EDIT") {
handleEditUser();
} else {
messageApi.open({
type: "error",
content: "系统异常,请刷新后重试",
});
}
});
};
const handleAddUser = async () => {
const { code } = await addUserApi(addUserParams);
try {
if (code === 1000) {
messageApi.open({
type: "success",
content: "创建成功",
});
setQueryParams({});
handleClear();
} else {
messageApi.open({
type: "error",
content: "用户名已经存在",
});
}
} catch (err) {
console.log(err);
}
};
const handleEditUser = async () => {
const { code } = await editUserApi(addUserParams);
try {
if (code === 1000) {
messageApi.open({
type: "success",
content: "修改成功",
});
setQueryParams({});
handleClear();
} else {
messageApi.open({
type: "error",
content: "修改失败",
});
}
} catch (err) {
console.log(err);
}
};
const handleReset = async () => {
searchForm.resetFields();
setQueryParams({});
getUserList({});
};
const handleSearch = () => {
getUserList(queryParams);
};
const handleDel = async (record: any) => {
Modal.confirm({
title: `确定删除Id为-${record.userId}-, 用户名为-${record.userName}-的用户吗?`,
icon: <ExclamationCircleFilled />,
async onOk() {
const { code } = await deleteUserApi(record.userId);
try {
if (code === 1000) {
messageApi.open({
type: "success",
content: "删除成功",
});
setQueryParams({});
} else {
messageApi.open({
type: "error",
content: "删除失败",
});
}
} catch (err) {
console.log(err);
}
},
onCancel() {
messageApi.open({
type: "warning",
content: "取消成功",
});
},
});
};
const handleTimePikerValue = (_: any, dates: string[]) => {
setQueryParams({
...queryParams,
startTime: dates[0],
endTime: dates[1],
});
searchForm.setFieldsValue({ dataTime: [dates[0], dates[1]] });
};
const handleSearchValues = (e: any) => {
const { name, value } = e.target;
setQueryParams({ ...queryParams, [name]: value });
};
const handlePageChange = (page: number) => {
setQueryParams({
...queryParams,
pageNum: page,
});
};
const handleTreeSelect = (deptId: any) => {
console.log(queryParams);
getUserList({
...queryParams,
deptId: deptId[0],
});
};
const handleEcho = (data: any) => {
const { userName, nickName, sex, state, phoneNumber, deptId, email } =
data.user;
const { postIds, roleIds } = data;
form.setFieldsValue({
userName,
nickName,
state,
phoneNumber,
sex,
deptId,
email,
postIds,
roleIds,
});
};
const handleCancel = () => {
handleClear();
};
const handleClear = () => {
form.resetFields();
setIsShowModal(false);
};
const handleExpandKeys = (key: string, data: object[]) => {
const filterRes = new RegExp(key, "i");
data.forEach((item: any) => {
if (filterRes.test(item?.title?.props?.children)) {
postIdList.push(item.key);
}
item.children ? handleExpandKeys(key, item.children) : "";
});
};
const handleLoopPostList = (loopData: object[]) => {
loopData.forEach((item: any) => {
if (item?.deptId) {
const flag = postIdList.some((someItem) => {
someItem === item.deptId;
});
if (!flag) {
postIdList.push(item.deptId);
}
}
item.children ? handleLoopPostList(item.children) : "";
});
};
const onRadio = (e: any) => {
setRadioValue(e.target.value);
};
const onTreeSelect = (newValue: string) => {
setTreeValue(newValue);
};
const onTreeSearch = (e?: any) => {
const { value } = e.target ? e.target : null;
setSearchTreeValue(value);
handleExpandKeys(value, treeData);
setExpandedKeys(postIdList);
setAutoExpandParent(true);
};
const onExpand = (newExpandedKeys: React.Key[]) => {
console.log(newExpandedKeys);
setExpandedKeys(newExpandedKeys);
setAutoExpandParent(false);
};
const treeData = useMemo(() => {
const loop = (data: any[]): any[] =>
data.map((item) => {
const strTitle = item.deptName as string;
const index = strTitle.indexOf(searchTreeValue);
const beforeStr = strTitle.substring(0, index);
const afterStr = strTitle.slice(index + searchTreeValue.length);
const title =
index > -1 ? (
<span>
{beforeStr}
<span className="site-tree-search-value">{searchTreeValue}</span>
{afterStr}
</span>
) : (
<span>{strTitle}</span>
);
if (item.children) {
return { title, key: item.deptId, children: loop(item.children) };
}
return {
title,
key: item.deptId,
};
});
return loop(myTreeData);
}, [myTreeData]);
useEffect(() => {
getDeptTreeData();
getRoleList();
getUserInfo();
}, [echoId]);
useEffect(() => {
getUserList({});
}, []);
return (
<div id="content">
<div className="dept-content">
<Input
allowClear
style={{ marginBottom: 8 }}
prefix={<SearchOutlined />}
placeholder="请输入部门名称"
onChange={onTreeSearch}
/>
<Tree
onExpand={onExpand}
defaultExpandAll={true}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
treeData={treeData}
onSelect={handleTreeSelect}
/>
</div>
<>
<div className="right-user">
<Form
layout="inline"
form={searchForm}
onFinish={handleSearch}
style={{ fontWeight: "700", marginLeft: 12, marginTop: 23 }}
>
<Form.Item
label="用户名称"
name="userName"
style={{ width: 300 + "px", marginTop: "10px" }}
>
<Input
placeholder="请输入用户名称"
name="userName"
allowClear
value={queryParams.userName}
onChange={handleSearchValues}
/>
</Form.Item>
<Form.Item
label="手机号码"
name="phoneNumber"
style={{ width: 300 + "px", marginTop: "10px" }}
>
<Input
placeholder="请输入手机号码"
name="phoneNumber"
allowClear
value={queryParams.phoneNumber}
onChange={handleSearchValues}
/>
</Form.Item>
<Form.Item label="状态" name="state" style={{ marginTop: "10px" }}>
<Select
style={{ width: 250 }}
placeholder="用户状态"
allowClear
options={[
{ value: "1", label: "正常" },
{ value: "0", label: "停用" },
]}
value={queryParams.state}
onChange={(value: any) =>
setQueryParams({
...queryParams,
state: value,
})
}
/>
</Form.Item>
<Form.Item
label="创建时间"
name="dateTime"
style={{ marginTop: "10px" }}
>
<DatePicker.RangePicker
locale={dataLocale}
showTime
format="YYYY-MM-DD HH:mm:ss"
onChange={handleTimePikerValue}
/>
</Form.Item>
<Form.Item style={{ marginTop: "10px" }}>
<Button
type="primary"
htmlType="submit"
style={{ marginLeft: 20 + "px" }}
>
</Button>
</Form.Item>
<Form.Item style={{ marginTop: "10px" }}>
<Button onClick={handleReset}></Button>
</Form.Item>
</Form>
<div className="add-content">
<>
<Button type="primary" onClick={handleAdd}>
<PlusOutlined />
</Button>
</>
</div>
<div className="table-content">
<>
{contextHolder}
<Table
columns={columns}
dataSource={listData}
className="article-table"
pagination={{
onChange: handlePageChange,
locale: {
jump_to: "跳至",
page: "页",
},
showQuickJumper: true,
showTotal: (total, range) =>
`${range[0]}-${range[1]} 条,共 ${total}`,
}}
/>
</>
</div>
<>
{contextHolder}
<Modal
title={modalTitle}
width={700}
okText="确认"
cancelText="取消"
open={isShowModal}
onOk={handleConfirm}
onCancel={handleCancel}
>
<Form
form={form}
name="basic"
initialValues={{ remember: true }}
labelCol={{ span: 6 }}
onFinish={handleConfirm}
autoComplete="off"
className="modal-form"
>
<Row>
<Col span={12}>
<Form.Item
label="用户名称"
name="userName"
rules={[{ required: true, message: "请输入用户名称" }]}
>
<Input defaultValue={""} placeholder="请输入用户名称" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="用户昵称"
name="nickName"
rules={[{ required: true, message: "请输入用户昵称" }]}
>
<Input defaultValue={""} placeholder="请输入用户昵称" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="用户性别"
name="sex"
rules={[{ required: false }]}
>
<Select
placeholder="请选择用户性别"
options={[
{ value: "0", label: "男" },
{ value: "1", label: "女" },
{ value: "2", label: "未知" },
]}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="用户密码"
name="password"
rules={[{ required: true, message: "请输入用户密码" }]}
>
<Input.Password
defaultValue={""}
placeholder="请输入密码"
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="归属部门"
name="deptId"
rules={[{ required: false }]}
>
<TreeSelect
showSearch
style={{ width: "100%" }}
value={treeValue}
dropdownStyle={{ maxHeight: 400, overflow: "auto" }}
placeholder="请选择"
allowClear
fieldNames={fieldNames}
treeDefaultExpandAll
onChange={onTreeSelect}
treeData={myTreeData}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="手机号码"
name="phoneNumber"
rules={[{ required: false }]}
>
<Input placeholder="请输入手机号码" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="邮箱"
name="email"
rules={[{ required: false }]}
>
<Input defaultValue={""} placeholder="请输入邮箱" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="状态"
name="state"
rules={[
{
required: false,
message: "Please input your password!",
},
]}
>
<Radio.Group onChange={onRadio} value={radioValue}>
<Radio value="1"></Radio>
<Radio value="0"></Radio>
</Radio.Group>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="岗位"
name="postIds"
rules={[
{
required: false,
message: "Please input your password!",
},
]}
>
<Select
mode="multiple"
defaultValue={[]}
options={[
{ value: "董事长", label: "董事长" },
{ value: "项目经理", label: "项目经理" },
{ value: "人力资源", label: "人力资源" },
{ value: "普通员工", label: "普通员工" },
]}
placeholder="请选择岗位"
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="角色"
name="roleIds"
rules={[
{
required: false,
message: "Please input your password!",
},
]}
>
<Select mode="multiple" placeholder="请选择角色">
{roleList.map((item) => (
<Select.Option key={item.roleId} value={item.roleId}>
{item.roleName}
</Select.Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
label="备注"
labelCol={{ span: 3 }}
rules={[
{
required: false,
message: "Please input your password!",
},
]}
>
<Input.TextArea placeholder="请输入内容" />
</Form.Item>
</Col>
</Row>
</Form>
</Modal>
</>
</div>
</>
</div>
);
}

View File

@@ -1,15 +1,21 @@
import { defineConfig } from 'vite'
import path from 'path'
import react from '@vitejs/plugin-react'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
// https://vitejs.dev/config/
export default defineConfig({
base: './',
base: '/',
plugins: [
react(),
createSvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(),'src/assets/svg')],
symbolId: 'icon-[dir]-[name]'
})
],
resolve: {
alias: {
'@': 'src/'
'@': path.resolve(__dirname, 'src')
}
},
build: {