12 Commits

Author SHA1 Message Date
clay
58523423d5 Merge pull request '添加 'Dockerfile'' (#7) from clay-patch-1 into master
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/7
2023-11-01 03:00:19 +00:00
clay
c990ace677 添加 'Dockerfile' 2023-11-01 02:59:38 +00:00
clay
0ceb87b234 Merge pull request '添加 'docker.sh'' (#6) from clay-patch-1 into master
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/6
2023-11-01 02:58:36 +00:00
clay
2751816113 添加 'docker.sh' 2023-11-01 02:58:23 +00:00
clay
cd817c479e Merge pull request '添加 'deployment.yml'' (#5) from clay-patch-1 into master
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/5
2023-11-01 02:57:33 +00:00
clay
a3f4ea5af7 添加 'deployment.yml' 2023-11-01 02:57:12 +00:00
clay
9a51189ec3 Merge pull request '添加 '.dockerignore'' (#4) from clay-patch-1 into master
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/4
2023-11-01 02:56:01 +00:00
clay
dda4b377fe 添加 '.dockerignore' 2023-11-01 02:55:44 +00:00
clay
6c2ef50d74 Merge pull request '添加 'nginx.conf'' (#3) from clay-patch-1 into master
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/3
2023-11-01 02:53:46 +00:00
clay
493456c839 添加 'nginx.conf' 2023-11-01 02:53:27 +00:00
clay
d6ce344331 Merge pull request '添加 '.drone.yml'' (#2) from clay-patch-1 into master
Reviewed-on: http://git.feashow.cn/wenhua/fateverse-react/pulls/2
2023-11-01 02:51:50 +00:00
clay
06dc2b3dd1 添加 '.drone.yml' 2023-11-01 02:51:37 +00:00
23 changed files with 389 additions and 2100 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

107
.drone.yml Normal file
View File

@@ -0,0 +1,107 @@
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
- name: build
path: /app/build
commands:
- export CI=false
- mkdir -p /app/build/$DRONE_REPO_NAME
- rm -rf /app/build/$DRONE_REPO_NAME/*
- cp deployment.yml /app/build/$DRONE_REPO_NAME/
- cp Dockerfile /app/build/$DRONE_REPO_NAME/
- cp .dockerignore /app/build/$DRONE_REPO_NAME/
- cp default.conf /app/build/$DRONE_REPO_NAME/
- cp docker.sh /app/build/$DRONE_REPO_NAME/
- cp nginx.conf /app/build/$DRONE_REPO_NAME/
- npm -v
- 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
- ls /app/build/$DRONE_REPO_NAME/
- echo $NODE_MODULES_PATH
- cp -r dist /app/build/$DRONE_REPO_NAME
- name: build-docker # 制作docker镜像
image: docker # 使用官方docker镜像
volumes: # 将容器内目录挂载到宿主机
- name: build
path: /app/build
- 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命令
- cd /app/build/$DRONE_REPO_NAME/
- cat Dockerfile
- 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
# - sed -i 's/$DRONE_COMMIT/'"$DRONE_COMMIT"'/' docker.sh
# - sed -i 's/$DRONE_REPO_NAME/'"$DRONE_REPO_NAME"'/' docker.sh
# 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: build
path: /app/build # 将应用打包好的Jar和执行脚本挂载出来
- name: config
path: /app/config # 将kubectl 配置文件挂载出来
commands: # 定义在Docker容器中执行的shell命令
- cd /app/build/$DRONE_REPO_NAME/
# 将deployment中定义的变量替换为drone中的内置变量
- cat deployment.yml
# 通过kubectl指令运行deployment.yml,并指定授权文件kubectl_conf.yml
# - kubectl apply -f deployment.yml -n $DRONE_COMMIT_BRANCH --kubeconfig=/app/config/kubectl_conf.yml
- kubectl apply -f deployment.yml -n dev --kubeconfig=/app/config/hcy-text-kubectl.yml
volumes:
- name: build
host:
path: /home/build
- name: cache
host:
path: /home/npm/cache
- 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
event:
- push

View File

@@ -2,19 +2,17 @@ module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ["dist", ".eslintrc.cjs"],
parser: "@typescript-eslint/parser",
plugins: ["react-refresh"],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
"react-refresh/only-export-components": [
"warn",
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": "off",
},
};
}

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

View File

@@ -20,12 +20,11 @@ npm run dev
```sh
npm run build
```
## 目录结构
```sh
public #静态资源
src
src
|--api #接口文件夹
|--assets #图片,字体等静态资源文件
|--components #公共组件
@@ -52,4 +51,4 @@ src
|--Notfound.tsx #404页面
|--ParentView.tsx #三级菜单路由出口
|--permission.ts #生成菜单文件
```
```

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;
}
}
}

View File

@@ -14,10 +14,8 @@
"antd": "^5.7.3",
"axios": "^1.4.0",
"babel-plugin-import": "^1.13.8",
"dayjs": "^1.11.10",
"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",

View File

@@ -1,59 +1,60 @@
import { Button, Form, Input, Image, message } from "antd";
import React, { useEffect, useState } from "react";
import "./assets/styles/login.scss";
import './assets/styles/login.scss'
import { getCaptchaApi, loginApi } from "./api/login";
import { useNavigate } from "react-router-dom";
import { setToken } from "./utils/auth";
type Login = {
uuid?: string;
username?: string;
password?: string;
code?: string;
};
uuid?: string,
username?: string,
password?: string,
code?: string
}
export default function Login() {
const [messageApi, contextHolder] = message.useMessage();
const [uuid, setUuid] = useState<string>("");
const [captcha, setCaptcha] = useState<string>("");
const navigate = useNavigate();
const [messageApi, contextHolder] = message.useMessage()
const [uuid, setUuid] = useState<string>('')
const [captcha, setCaptcha] = useState<string>('')
const navigate = useNavigate()
const getCaptcha = async () => {
try {
const { code, data } = await getCaptchaApi();
const { code, data } = await getCaptchaApi()
if (code === 1000) {
setUuid(data.uuid);
setCaptcha(data.img);
setUuid(data.uuid)
setCaptcha(data.img)
}
} catch (err: any) {
messageApi.error(err);
} catch(err: any) {
messageApi.error(err)
}
};
}
const handleLogin = async (values: Login) => {
values.uuid = uuid;
values.uuid = uuid
try {
const { code, data, msg } = await loginApi(values);
const { code, data, msg } = await loginApi(values)
if (code === 1000) {
setToken(data);
messageApi.success(msg);
navigate("/");
return;
setToken(data)
messageApi.success(msg)
navigate('/')
return
}
messageApi.error(msg);
getCaptcha();
} catch (err: any) {
messageApi.error(err);
messageApi.error(msg)
getCaptcha()
} catch(err: any) {
messageApi.error(err)
}
};
}
const handleLoginFailed = (errorInfo: any) => {
console.log(errorInfo);
};
}
useEffect(() => {
getCaptcha();
}, []);
getCaptcha()
}, [])
return (
<div id="login">
{contextHolder}
@@ -68,41 +69,35 @@ export default function Login() {
<h3>FateVerse</h3>
</Form.Item>
<Form.Item<Login>
label="用户名"
name="username"
rules={[{ required: true, message: "请输入用户名" }]}
initialValue="admin"
label='用户名'
name='username'
rules={[{ required: true, message: '请输入用户名' }]}
initialValue='admin'
>
<Input placeholder="请输入用户名" />
</Form.Item>
<Form.Item<Login>
label="密码"
name="password"
rules={[{ required: true, message: "请输入密码" }]}
initialValue="123456"
label='密码'
name='password'
rules={[{ required: true, message: '请输入密码' }]}
initialValue='123456'
>
<Input.Password placeholder="请输入密码" />
</Form.Item>
<Form.Item<Login>
label="验证码"
name="code"
rules={[{ required: true, message: "请输入验证码" }]}
label='验证码'
name='code'
rules={[{ required: true, message: '请输入验证码' }]}
>
<div className="code-box">
<Input placeholder="请输入验证码" />
<Image
src={"data:image/gif;base64," + captcha}
preview={false}
onClick={getCaptcha}
/>
<Image src={'data:image/gif;base64,' + captcha} preview={false} onClick={getCaptcha} />
</div>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
</Button>
<Button type="primary" htmlType="submit"></Button>
</Form.Item>
</Form>
</div>
);
}
)
}

View File

@@ -1,23 +0,0 @@
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",
});
};

View File

@@ -1,51 +0,0 @@
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, pageSize } = 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",
});
};

View File

@@ -1,94 +0,0 @@
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,
pageSize,
} = 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

@@ -1,21 +1,21 @@
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { BrowserRouter } from "react-router-dom";
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
import { BrowserRouter } from 'react-router-dom'
// redux toolkit
import { Provider } from "react-redux";
import store, { persistor } from "./stores/index.ts";
import { PersistGate } from "redux-persist/integration/react";
ReactDOM.createRoot(document.getElementById("root")!).render(
import { Provider } from 'react-redux'
import store, { persistor } from './stores/index.ts'
import { PersistGate } from 'redux-persist/integration/react'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<PersistGate loading={null} persistor={persistor} >
<BrowserRouter>
<App />
</BrowserRouter>
</PersistGate>
</Provider>
</React.StrictMode>
);
</React.StrictMode>,
)

View File

@@ -1,13 +0,0 @@
export default function FormatData(DataStr: string) {
const dateStr = DataStr;
const date = new Date(dateStr);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
const seconds = String(date.getSeconds()).padStart(2, "0");
const formattedDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
return formattedDate;
}

View File

@@ -1,86 +1,75 @@
import { IResponse, IToken } from "@/type";
import axios, {
InternalAxiosRequestConfig,
AxiosInstance,
AxiosRequestConfig,
} from "axios";
import Cookie from "js-cookie";
import axios, { InternalAxiosRequestConfig, AxiosInstance, AxiosRequestConfig } from "axios";
import Cookie from 'js-cookie'
import { getToken, removeToken } from "./auth";
import { message } from "antd";
const service: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_BASEURL,
headers: {
"Content-Type": "application/json",
'Content-Type': 'application/json'
},
timeout: 6000,
});
timeout: 6000
})
// 请求前拦截
service.interceptors.request.use((config: InternalAxiosRequestConfig) => {
const Authorization: IToken = getToken() || "";
const Authorization: IToken = getToken() || ''
if (config && config.headers && Authorization) {
config.headers.Authorization = Authorization;
config.headers.Authorization = Authorization
}
return config;
});
return config
})
const request = async <T = any>(
config: AxiosRequestConfig
): Promise<IResponse<T>> => {
const request = async <T = any>(config: AxiosRequestConfig): Promise<IResponse<T>> => {
try {
const response = await service.request<IResponse<T>>(config);
const { code, msg } = response.data;
const response = await service.request<IResponse<T>>(config)
const { code, msg } = response.data
if (code !== 1000) {
switch (code) {
case 401:
redirectLogin();
break;
case 402:
message.error(msg);
break;
case 403:
message.error(msg);
break;
case 405:
message.error(msg);
break;
default:
message.error("系统未知错误");
case 401: redirectLogin()
break
case 402: message.error(msg)
break
case 403: message.error(msg)
break
case 405: message.error(msg)
break
default: message.error('系统未知错误')
}
return {
code: -1,
msg: "error",
data: null as any,
};
msg: 'error',
data: null as any
}
}
return response.data;
return response.data
} catch (err: any) {
switch (err) {
}
if (!window.navigator.onLine) {
errorInfoRet("请检查网络连接");
if(!window.navigator.onLine) {
errorInfoRet('请检查网络连接')
}
return {
code: -1,
msg: "error",
data: null as any,
};
msg: 'error',
data: null as any
}
}
};
}
const redirectLogin = () => {
message.warning("用户登录已过期, 即将跳转到登录页面重新登录");
removeToken();
window.location.href = "/login";
};
message.warning('用户登录已过期, 即将跳转到登录页面重新登录')
removeToken()
window.location.href = '/login'
}
const errorInfoRet = (msg: string) => {
message.error(msg);
message.error(msg)
return {
code: -1,
msg,
data: null as any,
};
};
export default request;
data: null as any
}
}
export default request

View File

@@ -1,5 +1,7 @@
import React from "react";
export default function Dept() {
return <div>dept</div>;
}
return(
<div>dept</div>
)
}

View File

@@ -1,60 +0,0 @@
#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

@@ -1,242 +0,0 @@
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

@@ -1,103 +0,0 @@
#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

@@ -1,473 +0,0 @@
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 React, { useEffect, useState } from "react";
import { SearchOutlined, RedoOutlined, PlusOutlined } from "@ant-design/icons";
export default function Post() {
const [form] = Form.useForm();
const postIdList: number[] = [];
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

@@ -1,104 +0,0 @@
#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,807 +1,7 @@
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";
import React from 'react'
export default function User() {
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>
);
}
return(
<div>user</div>
)
}