refactor: 重构配置模块
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useMemo, useRef, useEffect } from 'react';
|
||||
import { Switch, Route, Redirect, useHistory } from 'react-router-dom';
|
||||
import { Layout, Menu, Breadcrumb, Spin } from '@arco-design/web-react';
|
||||
import React, {useState, useMemo, useRef, useEffect} from 'react';
|
||||
import {Switch, Route, Redirect, useHistory} from 'react-router-dom';
|
||||
import {Layout, Menu, Breadcrumb, Spin} from '@arco-design/web-react';
|
||||
import cs from 'classnames';
|
||||
import {
|
||||
IconMenuFold,
|
||||
@@ -12,13 +12,13 @@ import qs from 'query-string';
|
||||
import NProgress from 'nprogress';
|
||||
import Navbar from './components/NavBar';
|
||||
import Footer from './components/Footer';
|
||||
import useRoute, { IRoute } from '@/routes';
|
||||
import { isArray } from './utils/is';
|
||||
import useRoute, {IRoute} from '@/routes';
|
||||
import {isArray} from './utils/is';
|
||||
import useLocale from './utils/useLocale';
|
||||
import getUrlParams from './utils/getUrlParams';
|
||||
import lazyload from './utils/lazyload';
|
||||
import styles from './style/layout.module.less';
|
||||
import { useStore } from '@/store';
|
||||
import {useStore} from '@/store';
|
||||
|
||||
const MenuItem = Menu.Item;
|
||||
const SubMenu = Menu.SubMenu;
|
||||
@@ -29,16 +29,22 @@ const Content = Layout.Content;
|
||||
function getFlattenRoutes(routes) {
|
||||
const mod = import.meta.glob('./pages/**/[a-z[]*.tsx');
|
||||
const res = [];
|
||||
|
||||
function travel(_routes) {
|
||||
_routes.forEach((route) => {
|
||||
if (route.key && !route.children) {
|
||||
try {
|
||||
route.component = lazyload(mod[`./pages/${route.key}/index.tsx`]);
|
||||
res.push(route);
|
||||
} catch (error) {
|
||||
console.error(`cannot found /pages/${route.key}/index.tsx`)
|
||||
}
|
||||
} else if (isArray(route.children) && route.children.length) {
|
||||
travel(route.children);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
travel(routes);
|
||||
return res;
|
||||
}
|
||||
@@ -50,7 +56,7 @@ function PageLayout() {
|
||||
const currentComponent = qs.parseUrl(pathname).url.slice(1);
|
||||
const locale = useLocale();
|
||||
const store = useStore()
|
||||
const {settings,userInfo,userLoading} = store
|
||||
const {settings, userInfo, userLoading} = store
|
||||
const [routes, defaultRoute] = useRoute(userInfo?.menus);
|
||||
|
||||
const defaultSelectedKeys = [currentComponent || defaultRoute];
|
||||
@@ -92,15 +98,15 @@ function PageLayout() {
|
||||
setCollapsed((collapsed) => !collapsed);
|
||||
}
|
||||
|
||||
const paddingLeft = showMenu ? { paddingLeft: menuWidth } : {};
|
||||
const paddingTop = showNavbar ? { paddingTop: navbarHeight } : {};
|
||||
const paddingStyle = { ...paddingLeft, ...paddingTop };
|
||||
const paddingLeft = showMenu ? {paddingLeft: menuWidth} : {};
|
||||
const paddingTop = showNavbar ? {paddingTop: navbarHeight} : {};
|
||||
const paddingStyle = {...paddingLeft, ...paddingTop};
|
||||
|
||||
function renderRoutes(locale) {
|
||||
routeMap.current.clear();
|
||||
return function travel(_routes: IRoute[], level, parentNode = []) {
|
||||
return _routes.map((route) => {
|
||||
const { breadcrumb = true, ignore } = route;
|
||||
const {breadcrumb = true, ignore} = route;
|
||||
const iconDom = getIconFromKey(route.key);
|
||||
const titleDom = (
|
||||
<>
|
||||
@@ -114,7 +120,7 @@ function PageLayout() {
|
||||
);
|
||||
|
||||
const visibleChildren = (route.children || []).filter((child) => {
|
||||
const { ignore, breadcrumb = true } = child;
|
||||
const {ignore, breadcrumb = true} = child;
|
||||
if (ignore || route.ignore) {
|
||||
routeMap.current.set(
|
||||
`/${child.key}`,
|
||||
@@ -129,14 +135,14 @@ function PageLayout() {
|
||||
return '';
|
||||
}
|
||||
if (visibleChildren.length) {
|
||||
menuMap.current.set(route.key, { subMenu: true });
|
||||
menuMap.current.set(route.key, {subMenu: true});
|
||||
return (
|
||||
<SubMenu key={route.key} title={titleDom}>
|
||||
{travel(visibleChildren, level + 1, [...parentNode, route.name])}
|
||||
</SubMenu>
|
||||
);
|
||||
}
|
||||
menuMap.current.set(route.key, { menuItem: true });
|
||||
menuMap.current.set(route.key, {menuItem: true});
|
||||
return <MenuItem key={route.key}>{titleDom}</MenuItem>;
|
||||
});
|
||||
};
|
||||
@@ -176,9 +182,9 @@ function PageLayout() {
|
||||
useEffect(() => {
|
||||
const routeConfig = routeMap.current.get(pathname);
|
||||
const _breadcrumb = routeConfig || []
|
||||
setBreadCrumb([getIconFromKey(paths[0]),..._breadcrumb]);
|
||||
setBreadCrumb([getIconFromKey(paths[0]), ..._breadcrumb]);
|
||||
updateMenuStatus();
|
||||
}, [pathname,userLoading]);
|
||||
}, [pathname, userLoading]);
|
||||
|
||||
return (
|
||||
<Layout className={styles.layout}>
|
||||
@@ -187,10 +193,10 @@ function PageLayout() {
|
||||
[styles['layout-navbar-hidden']]: !showNavbar,
|
||||
})}
|
||||
>
|
||||
<Navbar show={showNavbar} />
|
||||
<Navbar show={showNavbar}/>
|
||||
</div>
|
||||
{userLoading ? (
|
||||
<Spin className={styles['spin']} />
|
||||
<Spin className={styles['spin']}/>
|
||||
) : (
|
||||
<Layout>
|
||||
{showMenu && (
|
||||
@@ -218,7 +224,7 @@ function PageLayout() {
|
||||
</Menu>
|
||||
</div>
|
||||
<div className={styles['collapse-btn']} onClick={toggleCollapse}>
|
||||
{collapsed ? <IconMenuUnfold /> : <IconMenuFold />}
|
||||
{collapsed ? <IconMenuUnfold/> : <IconMenuFold/>}
|
||||
</div>
|
||||
</Sider>
|
||||
)}
|
||||
@@ -247,7 +253,7 @@ function PageLayout() {
|
||||
);
|
||||
})}
|
||||
<Route exact path="/">
|
||||
<Redirect to={`/${defaultRoute}`} />
|
||||
<Redirect to={`/${defaultRoute}`}/>
|
||||
</Route>
|
||||
<Route
|
||||
path="*"
|
||||
@@ -256,7 +262,7 @@ function PageLayout() {
|
||||
</Switch>
|
||||
</Content>
|
||||
</div>
|
||||
{showFooter && <Footer />}
|
||||
{showFooter && <Footer/>}
|
||||
</Layout>
|
||||
</Layout>
|
||||
)}
|
||||
|
||||
61
admin/src/pages/config/constants.tsx
Normal file
61
admin/src/pages/config/constants.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
Typography,
|
||||
TableColumnProps,
|
||||
Button,
|
||||
Popconfirm,
|
||||
Space,
|
||||
} from '@arco-design/web-react';
|
||||
import {genderDict, getDictValue, tableStatusDict} from "@/enum/dict";
|
||||
import {dayjs} from "@arco-design/web-react/es/_util/dayjs";
|
||||
|
||||
const {Text} = Typography;
|
||||
|
||||
export const enum TableOptions {
|
||||
EDIT = 'edit',
|
||||
DELETE = 'delete',
|
||||
}
|
||||
|
||||
export function getColumns(callback: (type: TableOptions, record: any) => Promise<void>): TableColumnProps[] {
|
||||
return [
|
||||
{
|
||||
title: '唯一编码',
|
||||
dataIndex: 'key',
|
||||
render: (value) => <Text copyable>{value}</Text>,
|
||||
},
|
||||
{
|
||||
title: '配置值',
|
||||
dataIndex: 'value'
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark'
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createdAt',
|
||||
width: '15%',
|
||||
render: (value) => dayjs(value).format('YYYY-MM-DD HH:mm:ss'),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operations',
|
||||
align: 'center',
|
||||
width: '300px',
|
||||
render: (_, record) => (
|
||||
<Space>
|
||||
<Button size='small' onClick={() => callback(TableOptions.EDIT, record)} type="text" status="default">更新</Button>
|
||||
<Popconfirm
|
||||
focusLock
|
||||
title="删除数据"
|
||||
content="确认删除该条数据吗?"
|
||||
onOk={() => callback(TableOptions.DELETE, record)}
|
||||
>
|
||||
<Button size='small' type="text" status="danger">删除</Button>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Modal, Form, Input, Select, Message } from '@arco-design/web-react';
|
||||
import { createConfig, updateConfig } from '@/api/config';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Modal, Form, Input, Select, Message} from '@arco-design/web-react';
|
||||
import {createConfig, updateConfig} from '@/api/config';
|
||||
|
||||
interface Props {
|
||||
record: { [key: string]: any } | null,
|
||||
@@ -10,17 +10,20 @@ interface Props {
|
||||
}
|
||||
|
||||
function FormComponent(props: Props) {
|
||||
const { visible, record, handleConfirm, handleCancel } = props;
|
||||
const {visible, record, handleConfirm, handleCancel} = props;
|
||||
const [confirmLoading, setConfirmLoading] = useState(false);
|
||||
|
||||
const onOk = async () => {
|
||||
try {
|
||||
setConfirmLoading(true);
|
||||
const values = await form.validate();
|
||||
const data = {
|
||||
...values,
|
||||
};
|
||||
if (record?.key) {
|
||||
await updateConfig(values);
|
||||
await updateConfig(data);
|
||||
} else {
|
||||
await createConfig(values);
|
||||
await createConfig(data);
|
||||
}
|
||||
Message.success(`${record?.key ? '更新' : '新增'}成功!`);
|
||||
handleConfirm();
|
||||
@@ -35,36 +38,37 @@ function FormComponent(props: Props) {
|
||||
|
||||
useEffect(() => {
|
||||
record && form.setFieldsValue({
|
||||
...record
|
||||
...record,
|
||||
});
|
||||
}, [record]);
|
||||
|
||||
useEffect(() => {
|
||||
!visible && form.resetFields();
|
||||
!visible && form.resetFields()
|
||||
}, [visible]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Modal
|
||||
title={<span>{`${record?.key ? '更新' : '新增'}配置信息`}</span>}
|
||||
title={<span>{`${record?.key ? '更新' : '新增'}数据`}</span>}
|
||||
visible={visible}
|
||||
onOk={onOk}
|
||||
onCancel={handleCancel}
|
||||
confirmLoading={confirmLoading}
|
||||
>
|
||||
<Form form={form}>
|
||||
<Form.Item disabled={record?.key} label="配置id" field="key" rules={[{ required: true }]}>
|
||||
<Input placeholder="请输入配置id" />
|
||||
<Form.Item disabled={record?.key} label="唯一编码" field="key" rules={[{required: true}]}>
|
||||
<Input placeholder="请输入"/>
|
||||
</Form.Item>
|
||||
<Form.Item label="配置值" field="value" rules={[{ required: true }]}>
|
||||
<Input placeholder="请输入配置值" />
|
||||
|
||||
<Form.Item label="配置值" field="value" rules={[{required: true}]}>
|
||||
<Input placeholder="请输入"/>
|
||||
</Form.Item>
|
||||
<Form.Item label="备注" field="remark">
|
||||
<Input placeholder="请输入备注" />
|
||||
|
||||
<Form.Item label="备注" field="remark" rules={[{required: false}]}>
|
||||
<Input placeholder="请输入"/>
|
||||
</Form.Item>
|
||||
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,67 +1,28 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {
|
||||
Card,
|
||||
Table,
|
||||
Typography,
|
||||
TableColumnProps,
|
||||
PaginationProps,
|
||||
Button,
|
||||
Popconfirm,
|
||||
Divider,
|
||||
Avatar, Space, Message
|
||||
Card,
|
||||
Message,
|
||||
PaginationProps, Space,
|
||||
Table,
|
||||
TableColumnProps,
|
||||
Typography
|
||||
} from '@arco-design/web-react';
|
||||
import { useQuery } from 'react-query';
|
||||
import {useQuery} from 'react-query';
|
||||
import Form from './form';
|
||||
import { deleteConfig, getConfig, syncConfig } from '@/api/config';
|
||||
import SearchForm from "./searchForm";
|
||||
import {IconPlus, IconSync} from '@arco-design/web-react/icon';
|
||||
import {getColumns, TableOptions} from './constants';
|
||||
import {useHistory} from "react-router";
|
||||
import {getConfig, deleteConfig, syncConfig} from "@/api/config";
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
const {Title} = Typography;
|
||||
|
||||
export default function () {
|
||||
const reactQueryKey = useHistory().location.pathname
|
||||
|
||||
function ConfigManage() {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [record, setRecord] = useState(null);
|
||||
const columns: TableColumnProps[] = [
|
||||
{
|
||||
title: '配置id',
|
||||
dataIndex: 'key',
|
||||
render: (value) => <Text copyable>{value}</Text>,
|
||||
align: 'center',
|
||||
width:'25%'
|
||||
},
|
||||
{
|
||||
title: '配置值',
|
||||
dataIndex: 'value',
|
||||
render: (value) => <Text copyable>{value}</Text>,
|
||||
align: 'center',
|
||||
width:'25%'
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
align: 'center',
|
||||
width:'25%'
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operations',
|
||||
render: (_, record) => (
|
||||
<>
|
||||
<Button onClick={() => showModal(record)} type="text" status="default">更新</Button>
|
||||
<Divider type="vertical" />
|
||||
<Popconfirm
|
||||
focusLock
|
||||
title="删除数据"
|
||||
content="确认删除该条数据吗?"
|
||||
onOk={() => onDelete(record)}
|
||||
>
|
||||
<Button type="text" status="danger">删除</Button>
|
||||
</Popconfirm>
|
||||
|
||||
</>
|
||||
),
|
||||
align: 'center'
|
||||
}
|
||||
];
|
||||
|
||||
const [pagination, setPagination] = useState<PaginationProps>({
|
||||
sizeCanChange: true,
|
||||
showTotal: true,
|
||||
@@ -71,47 +32,9 @@ function ConfigManage() {
|
||||
total: 0
|
||||
});
|
||||
|
||||
const onDelete = async ({ key }) => {
|
||||
await deleteConfig(key);
|
||||
Message.success('删除成功!');
|
||||
await refetch();
|
||||
};
|
||||
|
||||
const onChangeTable = async ({ current, pageSize }) => {
|
||||
setPagination({
|
||||
...pagination,
|
||||
current,
|
||||
pageSize
|
||||
});
|
||||
};
|
||||
|
||||
const fetchData = async ({ queryKey }) => {
|
||||
const [, { page, pageSize }] = queryKey;
|
||||
const { data } = await getConfig({ page, pageSize });
|
||||
setPagination(prevState => {
|
||||
prevState.total = data.count;
|
||||
return prevState;
|
||||
});
|
||||
if (page > 1 && data.list.length === 0) {
|
||||
setPagination({
|
||||
...pagination,
|
||||
current: page - 1
|
||||
});
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
const {
|
||||
data,
|
||||
isLoading,
|
||||
isFetching,
|
||||
refetch
|
||||
} = useQuery(
|
||||
['config', { page: pagination.current, pageSize: pagination.pageSize }],
|
||||
fetchData,
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
keepPreviousData: true
|
||||
const [searchQuery, setSearchQuery] = useState<Record<string, any>>({
|
||||
page: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
});
|
||||
|
||||
const showModal = (payload?) => {
|
||||
@@ -119,6 +42,63 @@ function ConfigManage() {
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
const onDelete = async ({key}) => {
|
||||
await deleteConfig(key);
|
||||
Message.success('删除成功!');
|
||||
await refetch();
|
||||
};
|
||||
|
||||
const tableCallback = async (type: TableOptions, record: any) => {
|
||||
switch (type) {
|
||||
case TableOptions.EDIT:
|
||||
showModal(record)
|
||||
break;
|
||||
case TableOptions.DELETE:
|
||||
await onDelete(record);
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const columns: TableColumnProps[] = getColumns(tableCallback)
|
||||
|
||||
const handleSearch = async (params) => {
|
||||
setSearchQuery(prev => ({...prev, page: 1, ...params}))
|
||||
}
|
||||
|
||||
const onChangeTable = async ({current, pageSize}) => {
|
||||
setSearchQuery(prev => ({...prev, page: current, pageSize}));
|
||||
};
|
||||
|
||||
const fetchData = async ({queryKey}) => {
|
||||
const [, params] = queryKey;
|
||||
const {data} = await getConfig(params);
|
||||
setPagination(prev => ({
|
||||
...prev,
|
||||
total: data.count
|
||||
}))
|
||||
// 当前列表不存在数据回退1页
|
||||
if (params.page > 1 && data.list.length === 0) {
|
||||
setSearchQuery({
|
||||
...searchQuery,
|
||||
page: params.page - 1
|
||||
});
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
const {data, isLoading, isFetching, refetch} = useQuery(
|
||||
[reactQueryKey, {
|
||||
page: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
...searchQuery
|
||||
}],
|
||||
fetchData);
|
||||
|
||||
const sync = async () => {
|
||||
await syncConfig()
|
||||
Message.success('配置已同步');
|
||||
}
|
||||
|
||||
const handleConfirm = async () => {
|
||||
setRecord(null);
|
||||
setVisible(false);
|
||||
@@ -130,10 +110,13 @@ function ConfigManage() {
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
const sync = async () => {
|
||||
await syncConfig();
|
||||
Message.success('配置已同步!');
|
||||
};
|
||||
useEffect(() => {
|
||||
setPagination(prev => ({
|
||||
...prev,
|
||||
current: searchQuery.page,
|
||||
pageSize: pagination.pageSize,
|
||||
}))
|
||||
}, [searchQuery])
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -146,9 +129,23 @@ function ConfigManage() {
|
||||
/>
|
||||
<Title heading={6}>系统配置</Title>
|
||||
|
||||
<Space style={{ marginBottom: 12 }}>
|
||||
<Button type="primary" onClick={() => showModal()}>创建配置</Button>
|
||||
<Button type="primary" onClick={() => sync()}>同步配置至redis</Button>
|
||||
<SearchForm onSearch={handleSearch}/>
|
||||
|
||||
<Space style={{marginBottom: 12}}>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<IconPlus/>}
|
||||
onClick={() => showModal()}
|
||||
>
|
||||
新建
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<IconSync/>}
|
||||
onClick={() => sync()}
|
||||
>
|
||||
同步配置至redis
|
||||
</Button>
|
||||
</Space>
|
||||
|
||||
|
||||
@@ -164,6 +161,3 @@ function ConfigManage() {
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export default ConfigManage;
|
||||
|
||||
70
admin/src/pages/config/searchForm.tsx
Normal file
70
admin/src/pages/config/searchForm.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Form,
|
||||
Input,
|
||||
Button,
|
||||
Grid,
|
||||
Space, Divider, Select
|
||||
} from '@arco-design/web-react';
|
||||
import {IconRefresh, IconSearch} from '@arco-design/web-react/icon';
|
||||
import {statusDict} from "@/enum/dict";
|
||||
|
||||
const {Row, Col} = Grid;
|
||||
const {useForm} = Form;
|
||||
|
||||
function SearchForm(props: {
|
||||
onSearch: (values: Record<string, any>) => void;
|
||||
}) {
|
||||
const [form] = useForm();
|
||||
|
||||
const handleSubmit = () => {
|
||||
const values = form.getFieldsValue();
|
||||
props.onSearch(values);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
form.resetFields();
|
||||
props.onSearch({});
|
||||
};
|
||||
|
||||
const colSpan = 6
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form
|
||||
form={form}
|
||||
labelCol={{span: 6}}
|
||||
wrapperCol={{span: 18}}
|
||||
labelAlign="left"
|
||||
>
|
||||
<Row gutter={24}>
|
||||
<Col span={colSpan}>
|
||||
<Form.Item label="唯一编码" field="key">
|
||||
<Input placeholder="请输入唯一编码" allowClear/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={colSpan}>
|
||||
<Form.Item label="配置值" field="value">
|
||||
<Input placeholder="请输入配置值" allowClear/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={colSpan}>
|
||||
<Form.Item>
|
||||
<Space>
|
||||
<Button type="primary" icon={<IconSearch/>} onClick={handleSubmit}>
|
||||
搜索
|
||||
</Button>
|
||||
<Button icon={<IconRefresh/>} onClick={handleReset}>
|
||||
重置
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
<Divider style={{margin: '0 0 20px 0'}}/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default SearchForm;
|
||||
@@ -1,15 +0,0 @@
|
||||
.search-form-wrapper {
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--color-border-1);
|
||||
margin-bottom: 20px;
|
||||
|
||||
.right-button {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding-left: 20px;
|
||||
margin-bottom: 20px;
|
||||
border-left: 1px solid var(--color-border-2);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
60
admin/src/pages/menu/constants.tsx
Normal file
60
admin/src/pages/menu/constants.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
TableColumnProps,
|
||||
Button,
|
||||
Popconfirm,
|
||||
Space,
|
||||
} from '@arco-design/web-react';
|
||||
|
||||
export const enum TableOptions {
|
||||
EDIT = 'edit',
|
||||
DELETE = 'delete',
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function getColumns(callback: (type: TableOptions, record: any) => Promise<void>): TableColumnProps[] {
|
||||
return [
|
||||
{
|
||||
title: '菜单id',
|
||||
dataIndex: 'id',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '菜单名称',
|
||||
dataIndex: 'name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '菜单url',
|
||||
dataIndex: 'url',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sort',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operations',
|
||||
align: 'center',
|
||||
width: '300px',
|
||||
render: (_, record) => (
|
||||
<Space>
|
||||
<Button size='small' onClick={() => callback(TableOptions.EDIT, record)} type="text"
|
||||
status="default">更新</Button>
|
||||
<Popconfirm
|
||||
focusLock
|
||||
title="删除数据"
|
||||
content="确认删除该条数据吗?"
|
||||
onOk={() => callback(TableOptions.DELETE, record)}
|
||||
>
|
||||
<Button size='small' type="text" status="danger">删除</Button>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,75 +1,47 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, {useState} from 'react';
|
||||
import {
|
||||
Card,
|
||||
Table,
|
||||
Typography,
|
||||
TableColumnProps,
|
||||
Button,
|
||||
Popconfirm,
|
||||
Divider,
|
||||
Space,
|
||||
Message
|
||||
} from '@arco-design/web-react';
|
||||
import { useQuery } from 'react-query';
|
||||
import {useQuery} from 'react-query';
|
||||
import Form from './form';
|
||||
import { deleteMenu, getTreeMenu } from '@/api/menu';
|
||||
import {deleteMenu, getTreeMenu} from '@/api/menu';
|
||||
import {getColumns, TableOptions} from './constants';
|
||||
import {useHistory} from "react-router";
|
||||
|
||||
const { Title } = Typography;
|
||||
const {Title} = Typography;
|
||||
|
||||
function ConfigManage() {
|
||||
const reactQueryKey = useHistory().location.pathname;
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [record, setRecord] = useState(null);
|
||||
const columns: TableColumnProps[] = [
|
||||
{
|
||||
title: '菜单id',
|
||||
dataIndex: 'id',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '菜单名称',
|
||||
dataIndex: 'name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '菜单url',
|
||||
dataIndex: 'url',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sort',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operations',
|
||||
width:'25%',
|
||||
render: (_, record) => (
|
||||
<>
|
||||
<Button onClick={() => showModal(record)} type="text" status="default">更新</Button>
|
||||
<Divider type="vertical" />
|
||||
<Popconfirm
|
||||
focusLock
|
||||
title="删除数据"
|
||||
content="确认删除该条数据吗?"
|
||||
onOk={() => onDelete(record)}
|
||||
>
|
||||
<Button type="text" status="danger">删除</Button>
|
||||
</Popconfirm>
|
||||
</>
|
||||
),
|
||||
align: 'center'
|
||||
}
|
||||
];
|
||||
|
||||
const onDelete = async ({ id }) => {
|
||||
const onDelete = async ({id}) => {
|
||||
await deleteMenu(id);
|
||||
Message.success('删除成功!');
|
||||
await refetch();
|
||||
};
|
||||
|
||||
const tableCallback = async (type: TableOptions, record: any) => {
|
||||
switch (type) {
|
||||
case TableOptions.EDIT:
|
||||
showModal(record)
|
||||
break;
|
||||
case TableOptions.DELETE:
|
||||
await onDelete(record);
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const columns: TableColumnProps[] = getColumns(tableCallback)
|
||||
|
||||
const fetchData = async () => {
|
||||
const { data } = await getTreeMenu();
|
||||
const {data} = await getTreeMenu();
|
||||
return data
|
||||
};
|
||||
|
||||
@@ -79,7 +51,7 @@ function ConfigManage() {
|
||||
isFetching,
|
||||
refetch
|
||||
} = useQuery(
|
||||
['menu'], fetchData, { refetchOnWindowFocus: false, keepPreviousData: true });
|
||||
[reactQueryKey], fetchData);
|
||||
|
||||
const showModal = (payload?) => {
|
||||
payload && setRecord(payload);
|
||||
@@ -108,7 +80,7 @@ function ConfigManage() {
|
||||
/>
|
||||
<Title heading={6}>菜单管理</Title>
|
||||
|
||||
<Space style={{ marginBottom: 12 }}>
|
||||
<Space style={{marginBottom: 12}}>
|
||||
<Button type="primary" onClick={() => showModal()}>新建菜单</Button>
|
||||
</Space>
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
.search-form-wrapper {
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--color-border-1);
|
||||
margin-bottom: 20px;
|
||||
|
||||
.right-button {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding-left: 20px;
|
||||
margin-bottom: 20px;
|
||||
border-left: 1px solid var(--color-border-2);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
79
admin/src/pages/role/constants.tsx
Normal file
79
admin/src/pages/role/constants.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
Typography,
|
||||
TableColumnProps,
|
||||
Button,
|
||||
Popconfirm,
|
||||
Space,
|
||||
} from '@arco-design/web-react';
|
||||
import {getDictValue, tableStatusDict} from "@/enum/dict";
|
||||
import {dayjs} from "@arco-design/web-react/es/_util/dayjs";
|
||||
|
||||
const {Text} = Typography;
|
||||
|
||||
export const enum TableOptions {
|
||||
EDIT = 'edit',
|
||||
DELETE = 'delete',
|
||||
ASSIGN_PERMISSION = 'assign_permission',
|
||||
}
|
||||
|
||||
export function getColumns(callback: (type: TableOptions, record: any) => Promise<void>): TableColumnProps[] {
|
||||
return [
|
||||
{
|
||||
title: '角色id',
|
||||
dataIndex: 'id',
|
||||
render: (value) => <Text copyable>{value}</Text>,
|
||||
align: 'center',
|
||||
width: '20%',
|
||||
},
|
||||
{
|
||||
title: '角色名称',
|
||||
dataIndex: 'name',
|
||||
align: 'center',
|
||||
width: '20%',
|
||||
},
|
||||
{
|
||||
title: '唯一编码',
|
||||
dataIndex: 'code',
|
||||
render: (value) => <Text copyable>{value}</Text>,
|
||||
align: 'center',
|
||||
width: '20%',
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createdAt',
|
||||
width: '15%',
|
||||
render: (value) => dayjs(value).format('YYYY-MM-DD HH:mm:ss'),
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
width: '10%',
|
||||
render: (x) => getDictValue(tableStatusDict, x)
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operations',
|
||||
width: '20%',
|
||||
align: 'center',
|
||||
render: (_, record) => (
|
||||
<Space>
|
||||
<Button size='small' onClick={() => callback(TableOptions.EDIT, record)} type="text"
|
||||
status="default">更新</Button>
|
||||
<Button size='small' onClick={() => callback(TableOptions.ASSIGN_PERMISSION, record)} type="text"
|
||||
status="default">分配权限</Button>
|
||||
<Popconfirm
|
||||
focusLock
|
||||
title="删除数据"
|
||||
content="确认删除该条数据吗?"
|
||||
onOk={() => callback(TableOptions.DELETE, record)}
|
||||
>
|
||||
<Button size='small' type="text" status="danger">删除</Button>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
),
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Modal, Form, Input, Message, Select } from '@arco-design/web-react';
|
||||
import { createRole, updateRole } from '@/api/role';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {Modal, Form, Input, Message, Select} from '@arco-design/web-react';
|
||||
import {createRole, updateRole} from '@/api/role';
|
||||
import {statusDict} from "@/enum/dict";
|
||||
|
||||
interface Props {
|
||||
record: { [key: string]: any } | null,
|
||||
@@ -10,7 +11,7 @@ interface Props {
|
||||
}
|
||||
|
||||
function FormComponent(props: Props) {
|
||||
const { visible, record, handleConfirm, handleCancel } = props;
|
||||
const {visible, record, handleConfirm, handleCancel} = props;
|
||||
const [confirmLoading, setConfirmLoading] = useState(false);
|
||||
|
||||
const onOk = async () => {
|
||||
@@ -44,7 +45,6 @@ function FormComponent(props: Props) {
|
||||
}, [visible]);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Modal
|
||||
@@ -56,16 +56,16 @@ function FormComponent(props: Props) {
|
||||
>
|
||||
<Form form={form}>
|
||||
<Form.Item hidden field="id">
|
||||
<Input />
|
||||
<Input/>
|
||||
</Form.Item>
|
||||
<Form.Item label="角色名称" field="name" rules={[{ required: true }]}>
|
||||
<Input placeholder="请输入角色名称" />
|
||||
<Form.Item label="角色名称" field="name" rules={[{required: true}]}>
|
||||
<Input placeholder="请输入角色名称"/>
|
||||
</Form.Item>
|
||||
<Form.Item disabled={record?.id} label="角色编码" field="code" rules={[{ required: true }]}>
|
||||
<Input placeholder="请输入角色编码" />
|
||||
<Form.Item disabled={record?.id} label="角色编码" field="code" rules={[{required: true}]}>
|
||||
<Input placeholder="请输入角色编码"/>
|
||||
</Form.Item>
|
||||
<Form.Item label="状态" field="status" rules={[{ required: true }]}>
|
||||
<Select placeholder="请选择" options={[{ label: '启用', value: 1 }, { label: '禁用', value: 0 }]} />
|
||||
<Form.Item label="状态" field="status" rules={[{required: true}]} initialValue={1}>
|
||||
<Select placeholder="请选择" options={statusDict}/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Modal, Form, Input, Message, Tree, Checkbox } from '@arco-design/web-react';
|
||||
import { Modal, Form, Input, Message, Tree } from '@arco-design/web-react';
|
||||
import { getTreeMenu } from '@/api/menu';
|
||||
import { getRoleMenuIds, grantPermission } from '@/api/permission';
|
||||
|
||||
|
||||
@@ -1,86 +1,31 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {
|
||||
Card,
|
||||
Table,
|
||||
Typography,
|
||||
TableColumnProps,
|
||||
PaginationProps,
|
||||
Button,
|
||||
Popconfirm,
|
||||
Divider,
|
||||
Space,
|
||||
Message, Badge
|
||||
Card,
|
||||
Message,
|
||||
PaginationProps,
|
||||
Table,
|
||||
TableColumnProps,
|
||||
Typography
|
||||
} from '@arco-design/web-react';
|
||||
import { useQuery } from 'react-query';
|
||||
import {useHistory} from "react-router";
|
||||
import {useQuery} from 'react-query';
|
||||
import Form from './form';
|
||||
import { deleteRole, getRole } from '@/api/role';
|
||||
import GrantPerm from './grantPerm';
|
||||
import {IconPlus} from '@arco-design/web-react/icon';
|
||||
import {getColumns, TableOptions} from './constants';
|
||||
import {getRole,deleteRole} from "@/api/role";
|
||||
import GrantRolesForm from "./grantPerm";
|
||||
import SearchForm from "./searchForm";
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
function ConfigManage() {
|
||||
const {Title} = Typography;
|
||||
|
||||
function UserManage() {
|
||||
const reactQueryKey = useHistory().location.pathname
|
||||
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [visibleAssignRoles, setVisibleAssignRoles] = useState(false);
|
||||
const [record, setRecord] = useState(null);
|
||||
const [visibleGrantPerm, setVisibleGrantPerm] = useState(null);
|
||||
|
||||
const columns: TableColumnProps[] = [
|
||||
{
|
||||
title: '角色id',
|
||||
dataIndex: 'id',
|
||||
render: (value) => <Text copyable>{value}</Text>,
|
||||
align: 'center',
|
||||
width: '20%',
|
||||
},
|
||||
{
|
||||
title: '角色名称',
|
||||
dataIndex: 'name',
|
||||
align: 'center',
|
||||
width: '20%',
|
||||
},
|
||||
{
|
||||
title: '唯一编码',
|
||||
dataIndex: 'code',
|
||||
render: (value) => <Text copyable>{value}</Text>,
|
||||
align: 'center',
|
||||
width: '20%',
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
align: 'center',
|
||||
width: '20%',
|
||||
render: (x) => {
|
||||
if (x === 0) {
|
||||
return <Badge status="error" text="禁用中"></Badge>;
|
||||
}
|
||||
return <Badge status="success" text="启用中"></Badge>;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operations',
|
||||
width: '20%',
|
||||
render: (_, record) => (
|
||||
<>
|
||||
<Button onClick={() => showModal(record)} type="text" status="default">更新</Button>
|
||||
<Divider type="vertical" />
|
||||
<Button onClick={() => showGrantPerm(record)} type="text" status="default">分配权限</Button>
|
||||
<Divider type="vertical" />
|
||||
<Popconfirm
|
||||
focusLock
|
||||
title="删除数据"
|
||||
content="确认删除该条数据吗?"
|
||||
onOk={() => onDelete(record)}
|
||||
>
|
||||
<Button type="text" status="danger">删除</Button>
|
||||
</Popconfirm>
|
||||
|
||||
</>
|
||||
),
|
||||
align: 'center'
|
||||
}
|
||||
];
|
||||
|
||||
const [pagination, setPagination] = useState<PaginationProps>({
|
||||
sizeCanChange: true,
|
||||
showTotal: true,
|
||||
@@ -90,70 +35,97 @@ function ConfigManage() {
|
||||
total: 0
|
||||
});
|
||||
|
||||
const onDelete = async ({ id }) => {
|
||||
await deleteRole(id);
|
||||
Message.success('删除成功!');
|
||||
await refetch();
|
||||
};
|
||||
const [searchQuery, setSearchQuery] = useState<Record<string, any>>({
|
||||
page: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
});
|
||||
|
||||
const onChangeTable = async ({ current, pageSize }) => {
|
||||
setPagination({
|
||||
...pagination,
|
||||
current,
|
||||
pageSize
|
||||
});
|
||||
const showAssignRolesModal = (payload) => {
|
||||
setRecord(payload);
|
||||
setVisibleAssignRoles(true);
|
||||
};
|
||||
|
||||
const fetchData = async ({ queryKey }) => {
|
||||
const [, { page, pageSize }] = queryKey;
|
||||
const { data } = await getRole({ page, pageSize });
|
||||
setPagination(prevState => {
|
||||
prevState.total = data.count;
|
||||
return prevState;
|
||||
});
|
||||
if (page > 1 && data.list.length === 0) {
|
||||
setPagination({
|
||||
...pagination,
|
||||
current: page - 1
|
||||
});
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
const {
|
||||
data,
|
||||
isLoading,
|
||||
isFetching,
|
||||
refetch
|
||||
} = useQuery(
|
||||
['role', { page: pagination.current, pageSize: pagination.pageSize }],
|
||||
fetchData,
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
keepPreviousData: true
|
||||
});
|
||||
|
||||
const showModal = (payload?) => {
|
||||
payload && setRecord(payload);
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
const showGrantPerm = (payload) => {
|
||||
setRecord(payload);
|
||||
setVisibleGrantPerm(true);
|
||||
const onDelete = async ({id}) => {
|
||||
await deleteRole(id);
|
||||
Message.success('删除成功!');
|
||||
await refetch();
|
||||
};
|
||||
|
||||
const tableCallback = async (type: TableOptions, record: any) => {
|
||||
switch (type) {
|
||||
case TableOptions.EDIT:
|
||||
showModal(record)
|
||||
break;
|
||||
case TableOptions.DELETE:
|
||||
await onDelete(record);
|
||||
break
|
||||
case TableOptions.ASSIGN_PERMISSION:
|
||||
showAssignRolesModal(record);
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const columns: TableColumnProps[] = getColumns(tableCallback)
|
||||
|
||||
const handleSearch = async (params) => {
|
||||
setSearchQuery(prev => ({...prev, page: 1, ...params}))
|
||||
}
|
||||
|
||||
const onChangeTable = async ({current, pageSize}) => {
|
||||
setSearchQuery(prev => ({...prev, page: current, pageSize}));
|
||||
};
|
||||
|
||||
const fetchData = async ({queryKey}) => {
|
||||
const [, params] = queryKey;
|
||||
const {data} = await getRole(params);
|
||||
setPagination(prev => ({
|
||||
...prev,
|
||||
total: data.count
|
||||
}))
|
||||
// 当前列表不存在数据回退1页
|
||||
if (params.page > 1 && data.list.length === 0) {
|
||||
setSearchQuery({
|
||||
...searchQuery,
|
||||
page: params.page - 1
|
||||
});
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
const {data, isLoading, isFetching, refetch} = useQuery(
|
||||
[reactQueryKey, {
|
||||
page: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
...searchQuery
|
||||
}],
|
||||
fetchData);
|
||||
|
||||
const handleConfirm = async () => {
|
||||
handleCancel()
|
||||
setRecord(null);
|
||||
setVisible(false);
|
||||
setVisibleAssignRoles(false);
|
||||
await refetch();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setRecord(null);
|
||||
setVisible(false);
|
||||
setVisibleGrantPerm(false)
|
||||
setVisibleAssignRoles(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setPagination(prev => ({
|
||||
...prev,
|
||||
current: searchQuery.page,
|
||||
pageSize: pagination.pageSize,
|
||||
}))
|
||||
}, [searchQuery])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card>
|
||||
@@ -163,19 +135,24 @@ function ConfigManage() {
|
||||
visible={visible}
|
||||
record={record}
|
||||
/>
|
||||
|
||||
<GrantPerm
|
||||
<GrantRolesForm
|
||||
handleConfirm={handleConfirm}
|
||||
handleCancel={handleCancel}
|
||||
visible={visibleGrantPerm}
|
||||
visible={visibleAssignRoles}
|
||||
record={record}
|
||||
/>
|
||||
|
||||
<Title heading={6}>角色管理</Title>
|
||||
|
||||
<Space style={{ marginBottom: 12 }}>
|
||||
<Button type="primary" onClick={() => showModal()}>创建角色</Button>
|
||||
</Space>
|
||||
<SearchForm onSearch={handleSearch}/>
|
||||
|
||||
<Button
|
||||
style={{marginBottom: 12}}
|
||||
type="primary"
|
||||
icon={<IconPlus/>}
|
||||
onClick={() => showModal()}
|
||||
>
|
||||
新建
|
||||
</Button>
|
||||
|
||||
<Table
|
||||
rowKey="id"
|
||||
@@ -191,4 +168,4 @@ function ConfigManage() {
|
||||
}
|
||||
|
||||
|
||||
export default ConfigManage;
|
||||
export default UserManage;
|
||||
|
||||
75
admin/src/pages/role/searchForm.tsx
Normal file
75
admin/src/pages/role/searchForm.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Form,
|
||||
Input,
|
||||
Button,
|
||||
Grid,
|
||||
Space, Divider, Select
|
||||
} from '@arco-design/web-react';
|
||||
import {IconRefresh, IconSearch} from '@arco-design/web-react/icon';
|
||||
import {statusDict} from "@/enum/dict";
|
||||
|
||||
const {Row, Col} = Grid;
|
||||
const {useForm} = Form;
|
||||
|
||||
function SearchForm(props: {
|
||||
onSearch: (values: Record<string, any>) => void;
|
||||
}) {
|
||||
const [form] = useForm();
|
||||
|
||||
const handleSubmit = () => {
|
||||
const values = form.getFieldsValue();
|
||||
props.onSearch(values);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
form.resetFields();
|
||||
props.onSearch({});
|
||||
};
|
||||
|
||||
const colSpan = 6
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form
|
||||
form={form}
|
||||
labelCol={{span: 6}}
|
||||
wrapperCol={{span: 18}}
|
||||
labelAlign="left"
|
||||
>
|
||||
<Row gutter={24}>
|
||||
<Col span={colSpan}>
|
||||
<Form.Item label="角色名称" field="name">
|
||||
<Input placeholder="请输入" allowClear/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={colSpan}>
|
||||
<Form.Item label="唯一编码" field="code">
|
||||
<Input placeholder="请输入" allowClear/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={colSpan}>
|
||||
<Form.Item label="状态" field="status">
|
||||
<Select placeholder="请选择" options={statusDict} allowClear/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={colSpan}>
|
||||
<Form.Item>
|
||||
<Space>
|
||||
<Button type="primary" icon={<IconSearch/>} onClick={handleSubmit}>
|
||||
搜索
|
||||
</Button>
|
||||
<Button icon={<IconRefresh/>} onClick={handleReset}>
|
||||
重置
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
<Divider style={{margin: '0 0 20px 0'}}/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default SearchForm;
|
||||
@@ -1,15 +0,0 @@
|
||||
.search-form-wrapper {
|
||||
display: flex;
|
||||
border-bottom: 1px solid var(--color-border-1);
|
||||
margin-bottom: 20px;
|
||||
|
||||
.right-button {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding-left: 20px;
|
||||
margin-bottom: 20px;
|
||||
border-left: 1px solid var(--color-border-2);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,6 @@ export const enum TableOptions {
|
||||
ASSIGN_ROLE = 'assign_role',
|
||||
}
|
||||
|
||||
export const reactQueryKey = 'user'
|
||||
|
||||
export function getColumns(callback: (type: TableOptions, record: any) => Promise<void>): TableColumnProps[] {
|
||||
return [
|
||||
{
|
||||
|
||||
@@ -14,11 +14,13 @@ import GrantRolesForm from './grantRoles';
|
||||
import {deleteUser, getUsers} from '@/api/user';
|
||||
import SearchForm from "./searchForm";
|
||||
import {IconPlus} from '@arco-design/web-react/icon';
|
||||
import {getColumns, TableOptions, reactQueryKey} from './constants';
|
||||
import {getColumns, TableOptions} from './constants';
|
||||
import {useHistory} from "react-router";
|
||||
|
||||
const {Title} = Typography;
|
||||
|
||||
function UserManage() {
|
||||
const reactQueryKey = useHistory().location.pathname;
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [visibleAssignRoles, setVisibleAssignRoles] = useState(false);
|
||||
const [record, setRecord] = useState(null);
|
||||
@@ -78,7 +80,6 @@ function UserManage() {
|
||||
|
||||
const fetchData = async ({queryKey}) => {
|
||||
const [, params] = queryKey;
|
||||
console.log(params)
|
||||
const {data} = await getUsers(params);
|
||||
setPagination(prev => ({
|
||||
...prev,
|
||||
|
||||
@@ -33,8 +33,8 @@ function SearchForm(props: {
|
||||
<>
|
||||
<Form
|
||||
form={form}
|
||||
labelCol={{span: 5}}
|
||||
wrapperCol={{span: 19}}
|
||||
labelCol={{span: 6}}
|
||||
wrapperCol={{span: 18}}
|
||||
labelAlign="left"
|
||||
>
|
||||
<Row gutter={24}>
|
||||
|
||||
@@ -32,7 +32,8 @@ export class ConfigService {
|
||||
await this.prisma.sysConfig.create({ data });
|
||||
}
|
||||
|
||||
async findAll({ page, pageSize }: QueryListDto): Promise<QueryListResult> {
|
||||
async findAll(dto: QueryListDto): Promise<QueryListResult> {
|
||||
const { page, pageSize, key, value } = dto;
|
||||
const query: Prisma.SysConfigFindManyArgs = {
|
||||
select: {
|
||||
key: true,
|
||||
@@ -41,6 +42,10 @@ export class ConfigService {
|
||||
},
|
||||
skip: pageSize * (page - 1),
|
||||
take: pageSize,
|
||||
where: {
|
||||
...(key && { key: { contains: key } }),
|
||||
...(value && { value: { contains: value } }),
|
||||
},
|
||||
};
|
||||
|
||||
const [list, count] = await this.prisma.$transaction([
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
import { PaginationDto } from 'src/common/dto/pagination.dto';
|
||||
import { IsOptional } from 'class-validator';
|
||||
|
||||
export class QueryListDto extends PaginationDto {}
|
||||
export class QueryListDto extends PaginationDto {
|
||||
@IsOptional()
|
||||
key?: string;
|
||||
|
||||
@IsOptional()
|
||||
value?: string;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
import { PaginationDto } from 'src/common/dto/pagination.dto';
|
||||
import { IsInt, IsOptional } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class QueryListDto extends PaginationDto {}
|
||||
export class QueryListDto extends PaginationDto {
|
||||
@IsOptional()
|
||||
code?: string;
|
||||
|
||||
@IsOptional()
|
||||
name?: string;
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
status?: number;
|
||||
}
|
||||
|
||||
@@ -25,15 +25,15 @@ export class RoleService {
|
||||
await this.prisma.sysRole.create({ data });
|
||||
}
|
||||
|
||||
async findPage({ page, pageSize }: QueryListDto): Promise<QueryListResult> {
|
||||
async findPage(dto: QueryListDto): Promise<QueryListResult> {
|
||||
const { page, pageSize, code, status, name } = dto;
|
||||
const query: Prisma.SysRoleFindManyArgs = {
|
||||
skip: pageSize * (page - 1),
|
||||
take: pageSize,
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
code: true,
|
||||
status: true,
|
||||
where: {
|
||||
...(code && { code: { contains: code } }),
|
||||
...(!isNaN(status) && { status }),
|
||||
...(name && { name: { contains: name } }),
|
||||
},
|
||||
};
|
||||
const [list, count] = await this.prisma.$transaction([
|
||||
|
||||
Reference in New Issue
Block a user