refactor: 重构配置模块

This commit is contained in:
2024-10-26 17:37:09 +08:00
parent 0283e018de
commit 5eb78c35c2
22 changed files with 1110 additions and 833 deletions

View File

@@ -1,24 +1,24 @@
import React, { useState, useMemo, useRef, useEffect } from 'react'; import React, {useState, useMemo, useRef, useEffect} from 'react';
import { Switch, Route, Redirect, useHistory } from 'react-router-dom'; import {Switch, Route, Redirect, useHistory} from 'react-router-dom';
import { Layout, Menu, Breadcrumb, Spin } from '@arco-design/web-react'; import {Layout, Menu, Breadcrumb, Spin} from '@arco-design/web-react';
import cs from 'classnames'; import cs from 'classnames';
import { import {
IconMenuFold, IconMenuFold,
IconMenuUnfold, IconMenuUnfold,
IconSettings, IconSettings,
IconList, IconDashboard, IconList, IconDashboard,
} from '@arco-design/web-react/icon'; } from '@arco-design/web-react/icon';
import qs from 'query-string'; import qs from 'query-string';
import NProgress from 'nprogress'; import NProgress from 'nprogress';
import Navbar from './components/NavBar'; import Navbar from './components/NavBar';
import Footer from './components/Footer'; import Footer from './components/Footer';
import useRoute, { IRoute } from '@/routes'; import useRoute, {IRoute} from '@/routes';
import { isArray } from './utils/is'; import {isArray} from './utils/is';
import useLocale from './utils/useLocale'; import useLocale from './utils/useLocale';
import getUrlParams from './utils/getUrlParams'; import getUrlParams from './utils/getUrlParams';
import lazyload from './utils/lazyload'; import lazyload from './utils/lazyload';
import styles from './style/layout.module.less'; import styles from './style/layout.module.less';
import { useStore } from '@/store'; import {useStore} from '@/store';
const MenuItem = Menu.Item; const MenuItem = Menu.Item;
const SubMenu = Menu.SubMenu; const SubMenu = Menu.SubMenu;
@@ -27,241 +27,247 @@ const Sider = Layout.Sider;
const Content = Layout.Content; const Content = Layout.Content;
function getFlattenRoutes(routes) { function getFlattenRoutes(routes) {
const mod = import.meta.glob('./pages/**/[a-z[]*.tsx'); const mod = import.meta.glob('./pages/**/[a-z[]*.tsx');
const res = []; const res = [];
function travel(_routes) {
_routes.forEach((route) => { function travel(_routes) {
if (route.key && !route.children) { _routes.forEach((route) => {
route.component = lazyload(mod[`./pages/${route.key}/index.tsx`]); if (route.key && !route.children) {
res.push(route); try {
} else if (isArray(route.children) && route.children.length) { route.component = lazyload(mod[`./pages/${route.key}/index.tsx`]);
travel(route.children); res.push(route);
} } catch (error) {
}); console.error(`cannot found /pages/${route.key}/index.tsx`)
} }
travel(routes); } else if (isArray(route.children) && route.children.length) {
return res; travel(route.children);
}
});
}
travel(routes);
return res;
} }
function PageLayout() { function PageLayout() {
const urlParams = getUrlParams(); const urlParams = getUrlParams();
const history = useHistory(); const history = useHistory();
const pathname = history.location.pathname; const pathname = history.location.pathname;
const currentComponent = qs.parseUrl(pathname).url.slice(1); const currentComponent = qs.parseUrl(pathname).url.slice(1);
const locale = useLocale(); const locale = useLocale();
const store = useStore() const store = useStore()
const {settings,userInfo,userLoading} = store const {settings, userInfo, userLoading} = store
const [routes, defaultRoute] = useRoute(userInfo?.menus); const [routes, defaultRoute] = useRoute(userInfo?.menus);
const defaultSelectedKeys = [currentComponent || defaultRoute]; const defaultSelectedKeys = [currentComponent || defaultRoute];
const paths = (currentComponent || defaultRoute).split('/'); const paths = (currentComponent || defaultRoute).split('/');
const defaultOpenKeys = paths.slice(0, paths.length - 1); const defaultOpenKeys = paths.slice(0, paths.length - 1);
const [breadcrumb, setBreadCrumb] = useState([]); const [breadcrumb, setBreadCrumb] = useState([]);
const [collapsed, setCollapsed] = useState<boolean>(false); const [collapsed, setCollapsed] = useState<boolean>(false);
const [selectedKeys, setSelectedKeys] = const [selectedKeys, setSelectedKeys] =
useState<string[]>(defaultSelectedKeys); useState<string[]>(defaultSelectedKeys);
const [openKeys, setOpenKeys] = useState<string[]>(defaultOpenKeys); const [openKeys, setOpenKeys] = useState<string[]>(defaultOpenKeys);
const routeMap = useRef<Map<string, React.ReactNode[]>>(new Map()); const routeMap = useRef<Map<string, React.ReactNode[]>>(new Map());
const menuMap = useRef< const menuMap = useRef<
Map<string, { menuItem?: boolean; subMenu?: boolean }> Map<string, { menuItem?: boolean; subMenu?: boolean }>
>(new Map()); >(new Map());
const navbarHeight = 60; const navbarHeight = 60;
const menuWidth = collapsed ? 48 : settings.menuWidth; const menuWidth = collapsed ? 48 : settings.menuWidth;
const showNavbar = settings.navbar && urlParams.navbar !== false; const showNavbar = settings.navbar && urlParams.navbar !== false;
const showMenu = settings.menu && urlParams.menu !== false; const showMenu = settings.menu && urlParams.menu !== false;
const showFooter = settings.footer && urlParams.footer !== false; const showFooter = settings.footer && urlParams.footer !== false;
const flattenRoutes = useMemo(() => getFlattenRoutes(routes) || [], [routes]); const flattenRoutes = useMemo(() => getFlattenRoutes(routes) || [], [routes]);
function onClickMenuItem(key) { function onClickMenuItem(key) {
const currentRoute = flattenRoutes.find((r) => r.key === key); const currentRoute = flattenRoutes.find((r) => r.key === key);
const component = currentRoute.component; const component = currentRoute.component;
const preload = component.preload(); const preload = component.preload();
NProgress.start(); NProgress.start();
preload.then(() => { preload.then(() => {
history.push(currentRoute.path ? currentRoute.path : `/${key}`); history.push(currentRoute.path ? currentRoute.path : `/${key}`);
NProgress.done(); NProgress.done();
}); });
} }
function toggleCollapse() { function toggleCollapse() {
setCollapsed((collapsed) => !collapsed); setCollapsed((collapsed) => !collapsed);
} }
const paddingLeft = showMenu ? { paddingLeft: menuWidth } : {}; const paddingLeft = showMenu ? {paddingLeft: menuWidth} : {};
const paddingTop = showNavbar ? { paddingTop: navbarHeight } : {}; const paddingTop = showNavbar ? {paddingTop: navbarHeight} : {};
const paddingStyle = { ...paddingLeft, ...paddingTop }; const paddingStyle = {...paddingLeft, ...paddingTop};
function renderRoutes(locale) { function renderRoutes(locale) {
routeMap.current.clear(); routeMap.current.clear();
return function travel(_routes: IRoute[], level, parentNode = []) { return function travel(_routes: IRoute[], level, parentNode = []) {
return _routes.map((route) => { return _routes.map((route) => {
const { breadcrumb = true, ignore } = route; const {breadcrumb = true, ignore} = route;
const iconDom = getIconFromKey(route.key); const iconDom = getIconFromKey(route.key);
const titleDom = ( const titleDom = (
<> <>
{iconDom} {locale[route.name] || route.name} {iconDom} {locale[route.name] || route.name}
</> </>
); );
routeMap.current.set( routeMap.current.set(
`/${route.key}`, `/${route.key}`,
breadcrumb ? [...parentNode, route.name] : [] breadcrumb ? [...parentNode, route.name] : []
); );
const visibleChildren = (route.children || []).filter((child) => { const visibleChildren = (route.children || []).filter((child) => {
const { ignore, breadcrumb = true } = child; const {ignore, breadcrumb = true} = child;
if (ignore || route.ignore) { if (ignore || route.ignore) {
routeMap.current.set( routeMap.current.set(
`/${child.key}`, `/${child.key}`,
breadcrumb ? [...parentNode, route.name, child.name] : [] breadcrumb ? [...parentNode, route.name, child.name] : []
); );
} }
return !ignore; return !ignore;
}); });
if (ignore) { if (ignore) {
return ''; return '';
} }
if (visibleChildren.length) { if (visibleChildren.length) {
menuMap.current.set(route.key, { subMenu: true }); menuMap.current.set(route.key, {subMenu: true});
return ( return (
<SubMenu key={route.key} title={titleDom}> <SubMenu key={route.key} title={titleDom}>
{travel(visibleChildren, level + 1, [...parentNode, route.name])} {travel(visibleChildren, level + 1, [...parentNode, route.name])}
</SubMenu> </SubMenu>
); );
} }
menuMap.current.set(route.key, { menuItem: true }); menuMap.current.set(route.key, {menuItem: true});
return <MenuItem key={route.key}>{titleDom}</MenuItem>; return <MenuItem key={route.key}>{titleDom}</MenuItem>;
}); });
}; };
} }
function updateMenuStatus() { function updateMenuStatus() {
const pathKeys = pathname.split('/'); const pathKeys = pathname.split('/');
const newSelectedKeys: string[] = []; const newSelectedKeys: string[] = [];
const newOpenKeys: string[] = [...openKeys]; const newOpenKeys: string[] = [...openKeys];
while (pathKeys.length > 0) { while (pathKeys.length > 0) {
const currentRouteKey = pathKeys.join('/'); const currentRouteKey = pathKeys.join('/');
const menuKey = currentRouteKey.replace(/^\//, ''); const menuKey = currentRouteKey.replace(/^\//, '');
const menuType = menuMap.current.get(menuKey); const menuType = menuMap.current.get(menuKey);
if (menuType && menuType.menuItem) { if (menuType && menuType.menuItem) {
newSelectedKeys.push(menuKey); newSelectedKeys.push(menuKey);
} }
if (menuType && menuType.subMenu && !openKeys.includes(menuKey)) { if (menuType && menuType.subMenu && !openKeys.includes(menuKey)) {
newOpenKeys.push(menuKey); newOpenKeys.push(menuKey);
} }
pathKeys.pop(); pathKeys.pop();
} }
setSelectedKeys(newSelectedKeys); setSelectedKeys(newSelectedKeys);
setOpenKeys(newOpenKeys); setOpenKeys(newOpenKeys);
} }
function getIconFromKey(key) { function getIconFromKey(key) {
switch (key) { switch (key) {
case 'role': case 'role':
return <IconDashboard className={styles.icon}/>; return <IconDashboard className={styles.icon}/>;
case 'user': case 'user':
return <IconSettings className={styles.icon}/>; return <IconSettings className={styles.icon}/>;
default: default:
return <IconList className={styles.icon}/>; return <IconList className={styles.icon}/>;
} }
} }
useEffect(() => { useEffect(() => {
const routeConfig = routeMap.current.get(pathname); const routeConfig = routeMap.current.get(pathname);
const _breadcrumb = routeConfig || [] const _breadcrumb = routeConfig || []
setBreadCrumb([getIconFromKey(paths[0]),..._breadcrumb]); setBreadCrumb([getIconFromKey(paths[0]), ..._breadcrumb]);
updateMenuStatus(); updateMenuStatus();
}, [pathname,userLoading]); }, [pathname, userLoading]);
return ( return (
<Layout className={styles.layout}> <Layout className={styles.layout}>
<div <div
className={cs(styles['layout-navbar'], { className={cs(styles['layout-navbar'], {
[styles['layout-navbar-hidden']]: !showNavbar, [styles['layout-navbar-hidden']]: !showNavbar,
})} })}
> >
<Navbar show={showNavbar} /> <Navbar show={showNavbar}/>
</div> </div>
{userLoading ? ( {userLoading ? (
<Spin className={styles['spin']} /> <Spin className={styles['spin']}/>
) : ( ) : (
<Layout> <Layout>
{showMenu && ( {showMenu && (
<Sider <Sider
className={styles['layout-sider']} className={styles['layout-sider']}
width={menuWidth} width={menuWidth}
collapsed={collapsed} collapsed={collapsed}
onCollapse={setCollapsed} onCollapse={setCollapsed}
trigger={null} trigger={null}
collapsible collapsible
breakpoint="xl" breakpoint="xl"
style={paddingTop} style={paddingTop}
> >
<div className={styles['menu-wrapper']}> <div className={styles['menu-wrapper']}>
<Menu <Menu
collapse={collapsed} collapse={collapsed}
onClickMenuItem={onClickMenuItem} onClickMenuItem={onClickMenuItem}
selectedKeys={selectedKeys} selectedKeys={selectedKeys}
openKeys={openKeys} openKeys={openKeys}
onClickSubMenu={(_, openKeys) => { onClickSubMenu={(_, openKeys) => {
setOpenKeys(openKeys); setOpenKeys(openKeys);
}} }}
> >
{renderRoutes(locale)(routes, 1)} {renderRoutes(locale)(routes, 1)}
</Menu> </Menu>
</div> </div>
<div className={styles['collapse-btn']} onClick={toggleCollapse}> <div className={styles['collapse-btn']} onClick={toggleCollapse}>
{collapsed ? <IconMenuUnfold /> : <IconMenuFold />} {collapsed ? <IconMenuUnfold/> : <IconMenuFold/>}
</div> </div>
</Sider> </Sider>
)} )}
<Layout className={styles['layout-content']} style={paddingStyle}> <Layout className={styles['layout-content']} style={paddingStyle}>
<div className={styles['layout-content-wrapper']}> <div className={styles['layout-content-wrapper']}>
{!!breadcrumb.length && ( {!!breadcrumb.length && (
<div className={styles['layout-breadcrumb']}> <div className={styles['layout-breadcrumb']}>
<Breadcrumb> <Breadcrumb>
{breadcrumb.map((node, index) => ( {breadcrumb.map((node, index) => (
<Breadcrumb.Item key={index}> <Breadcrumb.Item key={index}>
{typeof node === 'string' ? locale[node] || node : node} {typeof node === 'string' ? locale[node] || node : node}
</Breadcrumb.Item> </Breadcrumb.Item>
))} ))}
</Breadcrumb> </Breadcrumb>
</div> </div>
)} )}
<Content> <Content>
<Switch> <Switch>
{flattenRoutes.map((route, index) => { {flattenRoutes.map((route, index) => {
return ( return (
<Route <Route
key={index} key={index}
path={`/${route.key}`} path={`/${route.key}`}
component={route.component} component={route.component}
/> />
); );
})} })}
<Route exact path="/"> <Route exact path="/">
<Redirect to={`/${defaultRoute}`} /> <Redirect to={`/${defaultRoute}`}/>
</Route> </Route>
<Route <Route
path="*" path="*"
component={lazyload(() => import('./pages/exception/403'))} component={lazyload(() => import('./pages/exception/403'))}
/> />
</Switch> </Switch>
</Content> </Content>
</div> </div>
{showFooter && <Footer />} {showFooter && <Footer/>}
</Layout> </Layout>
</Layout> </Layout>
)} )}
</Layout> </Layout>
); );
} }
export default PageLayout; export default PageLayout;

View 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>
)
}
]
}

View File

@@ -1,71 +1,75 @@
import React, { useEffect, useState } from 'react'; import React, {useEffect, useState} from 'react';
import { Modal, Form, Input, Select, Message } from '@arco-design/web-react'; import {Modal, Form, Input, Select, Message} from '@arco-design/web-react';
import { createConfig, updateConfig } from '@/api/config'; import {createConfig, updateConfig} from '@/api/config';
interface Props { interface Props {
record: { [key: string]: any } | null, record: { [key: string]: any } | null,
visible: boolean, visible: boolean,
handleConfirm: () => void, handleConfirm: () => void,
handleCancel: () => void handleCancel: () => void
} }
function FormComponent(props: Props) { function FormComponent(props: Props) {
const { visible, record, handleConfirm, handleCancel } = props; const {visible, record, handleConfirm, handleCancel} = props;
const [confirmLoading, setConfirmLoading] = useState(false); const [confirmLoading, setConfirmLoading] = useState(false);
const onOk = async () => { const onOk = async () => {
try { try {
setConfirmLoading(true); setConfirmLoading(true);
const values = await form.validate(); const values = await form.validate();
if (record?.key) { const data = {
await updateConfig(values); ...values,
} else { };
await createConfig(values); if (record?.key) {
} await updateConfig(data);
Message.success(`${record?.key ? '更新' : '新增'}成功!`); } else {
handleConfirm(); await createConfig(data);
} catch (e) { }
console.log(e); Message.success(`${record?.key ? '更新' : '新增'}成功!`);
} finally { handleConfirm();
setConfirmLoading(false); } catch (e) {
} console.log(e);
}; } finally {
setConfirmLoading(false);
}
};
const [form] = Form.useForm(); const [form] = Form.useForm();
useEffect(() => { useEffect(() => {
record && form.setFieldsValue({ record && form.setFieldsValue({
...record ...record,
}); });
}, [record]); }, [record]);
useEffect(() => { useEffect(() => {
!visible && form.resetFields(); !visible && form.resetFields()
}, [visible]); }, [visible]);
return ( return (
<div> <Modal
<Modal title={<span>{`${record?.key ? '更新' : '新增'}数据`}</span>}
title={<span>{`${record?.key ? '更新' : '新增'}配置信息`}</span>} visible={visible}
visible={visible} onOk={onOk}
onOk={onOk} onCancel={handleCancel}
onCancel={handleCancel} confirmLoading={confirmLoading}
confirmLoading={confirmLoading} >
> <Form form={form}>
<Form form={form}> <Form.Item disabled={record?.key} label="唯一编码" field="key" rules={[{required: true}]}>
<Form.Item disabled={record?.key} label="配置id" field="key" rules={[{ required: true }]}> <Input placeholder="请输入"/>
<Input placeholder="请输入配置id" /> </Form.Item>
</Form.Item>
<Form.Item label="配置值" field="value" rules={[{ required: true }]}> <Form.Item label="配置值" field="value" rules={[{required: true}]}>
<Input placeholder="请输入配置值" /> <Input placeholder="请输入"/>
</Form.Item> </Form.Item>
<Form.Item label="备注" field="remark">
<Input placeholder="请输入备注" /> <Form.Item label="备注" field="remark" rules={[{required: false}]}>
</Form.Item> <Input placeholder="请输入"/>
</Form> </Form.Item>
</Modal>
</div> </Form>
); </Modal>
);
} }
export default FormComponent; export default FormComponent;

View File

@@ -1,169 +1,163 @@
import React, { useState } from 'react'; import React, {useEffect, useState} from 'react';
import { import {
Card, Button,
Table, Card,
Typography, Message,
TableColumnProps, PaginationProps, Space,
PaginationProps, Table,
Button, TableColumnProps,
Popconfirm, Typography
Divider,
Avatar, Space, Message
} from '@arco-design/web-react'; } from '@arco-design/web-react';
import { useQuery } from 'react-query'; import {useQuery} from 'react-query';
import Form from './form'; 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;
function ConfigManage() { export default function () {
const [visible, setVisible] = useState(false); const reactQueryKey = useHistory().location.pathname
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>
</> const [visible, setVisible] = useState(false);
), const [record, setRecord] = useState(null);
align: 'center' const [pagination, setPagination] = useState<PaginationProps>({
} sizeCanChange: true,
]; showTotal: true,
pageSize: 10,
current: 1,
pageSizeChangeResetCurrent: true,
total: 0
});
const [pagination, setPagination] = useState<PaginationProps>({ const [searchQuery, setSearchQuery] = useState<Record<string, any>>({
sizeCanChange: true, page: pagination.current,
showTotal: true, pageSize: pagination.pageSize,
pageSize: 10, });
current: 1,
pageSizeChangeResetCurrent: true,
total: 0
});
const onDelete = async ({ key }) => { const showModal = (payload?) => {
await deleteConfig(key); payload && setRecord(payload);
Message.success('删除成功!'); setVisible(true);
await refetch(); };
};
const onChangeTable = async ({ current, pageSize }) => { const onDelete = async ({key}) => {
setPagination({ await deleteConfig(key);
...pagination, Message.success('删除成功!');
current, await refetch();
pageSize };
});
};
const fetchData = async ({ queryKey }) => { const tableCallback = async (type: TableOptions, record: any) => {
const [, { page, pageSize }] = queryKey; switch (type) {
const { data } = await getConfig({ page, pageSize }); case TableOptions.EDIT:
setPagination(prevState => { showModal(record)
prevState.total = data.count; break;
return prevState; case TableOptions.DELETE:
}); await onDelete(record);
if (page > 1 && data.list.length === 0) { break
setPagination({ }
...pagination, }
current: page - 1
});
}
return data;
};
const { const columns: TableColumnProps[] = getColumns(tableCallback)
data,
isLoading,
isFetching,
refetch
} = useQuery(
['config', { page: pagination.current, pageSize: pagination.pageSize }],
fetchData,
{
refetchOnWindowFocus: false,
keepPreviousData: true
});
const showModal = (payload?) => { const handleSearch = async (params) => {
payload && setRecord(payload); setSearchQuery(prev => ({...prev, page: 1, ...params}))
setVisible(true); }
};
const handleConfirm = async () => { const onChangeTable = async ({current, pageSize}) => {
setRecord(null); setSearchQuery(prev => ({...prev, page: current, pageSize}));
setVisible(false); };
await refetch();
};
const handleCancel = () => { const fetchData = async ({queryKey}) => {
setRecord(null); const [, params] = queryKey;
setVisible(false); 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 sync = async () => { const {data, isLoading, isFetching, refetch} = useQuery(
await syncConfig(); [reactQueryKey, {
Message.success('配置已同步!'); page: pagination.current,
}; pageSize: pagination.pageSize,
...searchQuery
}],
fetchData);
return ( const sync = async () => {
<> await syncConfig()
<Card> Message.success('配置已同步');
<Form }
handleConfirm={handleConfirm}
handleCancel={handleCancel}
visible={visible}
record={record}
/>
<Title heading={6}></Title>
<Space style={{ marginBottom: 12 }}> const handleConfirm = async () => {
<Button type="primary" onClick={() => showModal()}></Button> setRecord(null);
<Button type="primary" onClick={() => sync()}>redis</Button> setVisible(false);
</Space> await refetch();
};
const handleCancel = () => {
setRecord(null);
setVisible(false);
};
useEffect(() => {
setPagination(prev => ({
...prev,
current: searchQuery.page,
pageSize: pagination.pageSize,
}))
}, [searchQuery])
return (
<>
<Card>
<Form
handleConfirm={handleConfirm}
handleCancel={handleCancel}
visible={visible}
record={record}
/>
<Title heading={6}></Title>
<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>
<Table <Table
rowKey="key" rowKey="key"
loading={isLoading || isFetching} loading={isLoading || isFetching}
columns={columns} columns={columns}
onChange={onChangeTable} onChange={onChangeTable}
pagination={pagination} pagination={pagination}
data={data?.list} data={data?.list}
/> />
</Card> </Card>
</> </>
); );
} }
export default ConfigManage;

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

View File

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

View 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>
)
}
]
}

View File

@@ -1,127 +1,99 @@
import React, { useState } from 'react'; import React, {useState} from 'react';
import { import {
Card, Card,
Table, Table,
Typography, Typography,
TableColumnProps, TableColumnProps,
Button, Button,
Popconfirm, Space,
Divider, Message
Space,
Message
} from '@arco-design/web-react'; } from '@arco-design/web-react';
import { useQuery } from 'react-query'; import {useQuery} from 'react-query';
import Form from './form'; 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() { function ConfigManage() {
const [visible, setVisible] = useState(false); const reactQueryKey = useHistory().location.pathname;
const [record, setRecord] = useState(null); const [visible, setVisible] = useState(false);
const columns: TableColumnProps[] = [ const [record, setRecord] = useState(null);
{
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); await deleteMenu(id);
Message.success('删除成功!'); Message.success('删除成功!');
await refetch(); await refetch();
}; };
const fetchData = async () => { const tableCallback = async (type: TableOptions, record: any) => {
const { data } = await getTreeMenu(); switch (type) {
return data case TableOptions.EDIT:
}; showModal(record)
break;
case TableOptions.DELETE:
await onDelete(record);
break
}
}
const { const columns: TableColumnProps[] = getColumns(tableCallback)
data,
isLoading,
isFetching,
refetch
} = useQuery(
['menu'], fetchData, { refetchOnWindowFocus: false, keepPreviousData: true });
const showModal = (payload?) => { const fetchData = async () => {
payload && setRecord(payload); const {data} = await getTreeMenu();
setVisible(true); return data
}; };
const handleConfirm = async () => { const {
setRecord(null); data,
setVisible(false); isLoading,
await refetch(); isFetching,
}; refetch
} = useQuery(
[reactQueryKey], fetchData);
const handleCancel = () => { const showModal = (payload?) => {
setRecord(null); payload && setRecord(payload);
setVisible(false); setVisible(true);
}; };
return ( const handleConfirm = async () => {
<> setRecord(null);
<Card> setVisible(false);
<Form await refetch();
handleConfirm={handleConfirm} };
handleCancel={handleCancel}
visible={visible}
record={record}
/>
<Title heading={6}></Title>
<Space style={{ marginBottom: 12 }}> const handleCancel = () => {
<Button type="primary" onClick={() => showModal()}></Button> setRecord(null);
</Space> setVisible(false);
};
<Table return (
rowKey="id" <>
loading={isLoading || isFetching} <Card>
columns={columns} <Form
pagination={false} handleConfirm={handleConfirm}
data={data} handleCancel={handleCancel}
/> visible={visible}
</Card> record={record}
</> />
); <Title heading={6}></Title>
<Space style={{marginBottom: 12}}>
<Button type="primary" onClick={() => showModal()}></Button>
</Space>
<Table
rowKey="id"
loading={isLoading || isFetching}
columns={columns}
pagination={false}
data={data}
/>
</Card>
</>
);
} }

View File

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

View 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>
),
}
]
}

View File

@@ -1,76 +1,76 @@
import React, { useEffect, useState } from 'react'; import React, {useEffect, useState} from 'react';
import { Modal, Form, Input, Message, Select } from '@arco-design/web-react'; import {Modal, Form, Input, Message, Select} from '@arco-design/web-react';
import { createRole, updateRole } from '@/api/role'; import {createRole, updateRole} from '@/api/role';
import {statusDict} from "@/enum/dict";
interface Props { interface Props {
record: { [key: string]: any } | null, record: { [key: string]: any } | null,
visible: boolean, visible: boolean,
handleConfirm: () => void, handleConfirm: () => void,
handleCancel: () => void handleCancel: () => void
} }
function FormComponent(props: Props) { function FormComponent(props: Props) {
const { visible, record, handleConfirm, handleCancel } = props; const {visible, record, handleConfirm, handleCancel} = props;
const [confirmLoading, setConfirmLoading] = useState(false); const [confirmLoading, setConfirmLoading] = useState(false);
const onOk = async () => { const onOk = async () => {
try { try {
setConfirmLoading(true); setConfirmLoading(true);
const values = await form.validate(); const values = await form.validate();
if (record?.id) { if (record?.id) {
await updateRole(values); await updateRole(values);
} else { } else {
await createRole(values); await createRole(values);
} }
Message.success(`${record?.id ? '更新' : '新增'}成功!`); Message.success(`${record?.id ? '更新' : '新增'}成功!`);
handleConfirm(); handleConfirm();
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} finally { } finally {
setConfirmLoading(false); setConfirmLoading(false);
} }
}; };
const [form] = Form.useForm(); const [form] = Form.useForm();
useEffect(() => { useEffect(() => {
record && form.setFieldsValue({ record && form.setFieldsValue({
...record ...record
}); });
}, [record]); }, [record]);
useEffect(() => { useEffect(() => {
!visible && form.resetFields(); !visible && form.resetFields();
}, [visible]); }, [visible]);
return (
return ( <div>
<div> <Modal
<Modal title={<span>{`${record?.id ? '更新' : '新增'}角色信息`}</span>}
title={<span>{`${record?.id ? '更新' : '新增'}角色信息`}</span>} visible={visible}
visible={visible} onOk={onOk}
onOk={onOk} onCancel={handleCancel}
onCancel={handleCancel} confirmLoading={confirmLoading}
confirmLoading={confirmLoading} >
> <Form form={form}>
<Form form={form}> <Form.Item hidden field="id">
<Form.Item hidden field="id"> <Input/>
<Input /> </Form.Item>
</Form.Item> <Form.Item label="角色名称" field="name" rules={[{required: true}]}>
<Form.Item label="角色名称" field="name" rules={[{ required: true }]}> <Input placeholder="请输入角色名称"/>
<Input placeholder="请输入角色名称" /> </Form.Item>
</Form.Item> <Form.Item disabled={record?.id} label="角色编码" field="code" rules={[{required: true}]}>
<Form.Item disabled={record?.id} label="角色编码" field="code" rules={[{ required: true }]}> <Input placeholder="请输入角色编码"/>
<Input placeholder="请输入角色编码" /> </Form.Item>
</Form.Item> <Form.Item label="状态" field="status" rules={[{required: true}]} initialValue={1}>
<Form.Item label="状态" field="status" rules={[{ required: true }]}> <Select placeholder="请选择" options={statusDict}/>
<Select placeholder="请选择" options={[{ label: '启用', value: 1 }, { label: '禁用', value: 0 }]} /> </Form.Item>
</Form.Item> </Form>
</Form> </Modal>
</Modal> </div>
</div> );
);
} }
export default FormComponent; export default FormComponent;

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react'; 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 { getTreeMenu } from '@/api/menu';
import { getRoleMenuIds, grantPermission } from '@/api/permission'; import { getRoleMenuIds, grantPermission } from '@/api/permission';

View File

@@ -1,194 +1,171 @@
import React, { useState } from 'react'; import React, {useEffect, useState} from 'react';
import { import {
Card, Button,
Table, Card,
Typography, Message,
TableColumnProps, PaginationProps,
PaginationProps, Table,
Button, TableColumnProps,
Popconfirm, Typography
Divider,
Space,
Message, Badge
} from '@arco-design/web-react'; } 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 Form from './form';
import { deleteRole, getRole } from '@/api/role'; import {IconPlus} from '@arco-design/web-react/icon';
import GrantPerm from './grantPerm'; 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;
const [visible, setVisible] = useState(false);
const [record, setRecord] = useState(null);
const [visibleGrantPerm, setVisibleGrantPerm] = useState(null);
const columns: TableColumnProps[] = [ function UserManage() {
{ const reactQueryKey = useHistory().location.pathname
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>
</> const [visible, setVisible] = useState(false);
), const [visibleAssignRoles, setVisibleAssignRoles] = useState(false);
align: 'center' const [record, setRecord] = useState(null);
} const [pagination, setPagination] = useState<PaginationProps>({
]; sizeCanChange: true,
showTotal: true,
pageSize: 10,
current: 1,
pageSizeChangeResetCurrent: true,
total: 0
});
const [pagination, setPagination] = useState<PaginationProps>({ const [searchQuery, setSearchQuery] = useState<Record<string, any>>({
sizeCanChange: true, page: pagination.current,
showTotal: true, pageSize: pagination.pageSize,
pageSize: 10, });
current: 1,
pageSizeChangeResetCurrent: true,
total: 0
});
const onDelete = async ({ id }) => { const showAssignRolesModal = (payload) => {
await deleteRole(id); setRecord(payload);
Message.success('删除成功!'); setVisibleAssignRoles(true);
await refetch(); };
};
const onChangeTable = async ({ current, pageSize }) => { const showModal = (payload?) => {
setPagination({ payload && setRecord(payload);
...pagination, setVisible(true);
current, };
pageSize
});
};
const fetchData = async ({ queryKey }) => { const onDelete = async ({id}) => {
const [, { page, pageSize }] = queryKey; await deleteRole(id);
const { data } = await getRole({ page, pageSize }); Message.success('删除成功!');
setPagination(prevState => { await refetch();
prevState.total = data.count; };
return prevState;
});
if (page > 1 && data.list.length === 0) {
setPagination({
...pagination,
current: page - 1
});
}
return data;
};
const { const tableCallback = async (type: TableOptions, record: any) => {
data, switch (type) {
isLoading, case TableOptions.EDIT:
isFetching, showModal(record)
refetch break;
} = useQuery( case TableOptions.DELETE:
['role', { page: pagination.current, pageSize: pagination.pageSize }], await onDelete(record);
fetchData, break
{ case TableOptions.ASSIGN_PERMISSION:
refetchOnWindowFocus: false, showAssignRolesModal(record);
keepPreviousData: true break
}); }
}
const showModal = (payload?) => { const columns: TableColumnProps[] = getColumns(tableCallback)
payload && setRecord(payload);
setVisible(true);
};
const showGrantPerm = (payload) => { const handleSearch = async (params) => {
setRecord(payload); setSearchQuery(prev => ({...prev, page: 1, ...params}))
setVisibleGrantPerm(true); }
};
const handleConfirm = async () => { const onChangeTable = async ({current, pageSize}) => {
handleCancel() setSearchQuery(prev => ({...prev, page: current, pageSize}));
await refetch(); };
};
const handleCancel = () => { const fetchData = async ({queryKey}) => {
setRecord(null); const [, params] = queryKey;
setVisible(false); const {data} = await getRole(params);
setVisibleGrantPerm(false) setPagination(prev => ({
}; ...prev,
total: data.count
}))
// 当前列表不存在数据回退1页
if (params.page > 1 && data.list.length === 0) {
setSearchQuery({
...searchQuery,
page: params.page - 1
});
}
return data;
};
return ( const {data, isLoading, isFetching, refetch} = useQuery(
<> [reactQueryKey, {
<Card> page: pagination.current,
<Form pageSize: pagination.pageSize,
handleConfirm={handleConfirm} ...searchQuery
handleCancel={handleCancel} }],
visible={visible} fetchData);
record={record}
/>
<GrantPerm const handleConfirm = async () => {
handleConfirm={handleConfirm} setRecord(null);
handleCancel={handleCancel} setVisible(false);
visible={visibleGrantPerm} setVisibleAssignRoles(false);
record={record} await refetch();
/> };
<Title heading={6}></Title> const handleCancel = () => {
setRecord(null);
setVisible(false);
setVisibleAssignRoles(false);
};
<Space style={{ marginBottom: 12 }}> useEffect(() => {
<Button type="primary" onClick={() => showModal()}></Button> setPagination(prev => ({
</Space> ...prev,
current: searchQuery.page,
pageSize: pagination.pageSize,
}))
}, [searchQuery])
<Table return (
rowKey="id" <>
loading={isLoading || isFetching} <Card>
columns={columns} <Form
onChange={onChangeTable} handleConfirm={handleConfirm}
pagination={pagination} handleCancel={handleCancel}
data={data?.list} visible={visible}
/> record={record}
</Card> />
</> <GrantRolesForm
); handleConfirm={handleConfirm}
handleCancel={handleCancel}
visible={visibleAssignRoles}
record={record}
/>
<Title heading={6}></Title>
<SearchForm onSearch={handleSearch}/>
<Button
style={{marginBottom: 12}}
type="primary"
icon={<IconPlus/>}
onClick={() => showModal()}
>
</Button>
<Table
rowKey="id"
loading={isLoading || isFetching}
columns={columns}
onChange={onChangeTable}
pagination={pagination}
data={data?.list}
/>
</Card>
</>
);
} }
export default ConfigManage; export default UserManage;

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

View File

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

View File

@@ -19,8 +19,6 @@ export const enum TableOptions {
ASSIGN_ROLE = 'assign_role', ASSIGN_ROLE = 'assign_role',
} }
export const reactQueryKey = 'user'
export function getColumns(callback: (type: TableOptions, record: any) => Promise<void>): TableColumnProps[] { export function getColumns(callback: (type: TableOptions, record: any) => Promise<void>): TableColumnProps[] {
return [ return [
{ {

View File

@@ -14,11 +14,13 @@ import GrantRolesForm from './grantRoles';
import {deleteUser, getUsers} from '@/api/user'; import {deleteUser, getUsers} from '@/api/user';
import SearchForm from "./searchForm"; import SearchForm from "./searchForm";
import {IconPlus} from '@arco-design/web-react/icon'; 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; const {Title} = Typography;
function UserManage() { function UserManage() {
const reactQueryKey = useHistory().location.pathname;
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [visibleAssignRoles, setVisibleAssignRoles] = useState(false); const [visibleAssignRoles, setVisibleAssignRoles] = useState(false);
const [record, setRecord] = useState(null); const [record, setRecord] = useState(null);
@@ -78,7 +80,6 @@ function UserManage() {
const fetchData = async ({queryKey}) => { const fetchData = async ({queryKey}) => {
const [, params] = queryKey; const [, params] = queryKey;
console.log(params)
const {data} = await getUsers(params); const {data} = await getUsers(params);
setPagination(prev => ({ setPagination(prev => ({
...prev, ...prev,

View File

@@ -33,8 +33,8 @@ function SearchForm(props: {
<> <>
<Form <Form
form={form} form={form}
labelCol={{span: 5}} labelCol={{span: 6}}
wrapperCol={{span: 19}} wrapperCol={{span: 18}}
labelAlign="left" labelAlign="left"
> >
<Row gutter={24}> <Row gutter={24}>

View File

@@ -32,7 +32,8 @@ export class ConfigService {
await this.prisma.sysConfig.create({ data }); 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 = { const query: Prisma.SysConfigFindManyArgs = {
select: { select: {
key: true, key: true,
@@ -41,6 +42,10 @@ export class ConfigService {
}, },
skip: pageSize * (page - 1), skip: pageSize * (page - 1),
take: pageSize, take: pageSize,
where: {
...(key && { key: { contains: key } }),
...(value && { value: { contains: value } }),
},
}; };
const [list, count] = await this.prisma.$transaction([ const [list, count] = await this.prisma.$transaction([

View File

@@ -1,3 +1,10 @@
import { PaginationDto } from 'src/common/dto/pagination.dto'; 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;
}

View File

@@ -1,3 +1,16 @@
import { PaginationDto } from 'src/common/dto/pagination.dto'; 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;
}

View File

@@ -25,15 +25,15 @@ export class RoleService {
await this.prisma.sysRole.create({ data }); 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 = { const query: Prisma.SysRoleFindManyArgs = {
skip: pageSize * (page - 1), skip: pageSize * (page - 1),
take: pageSize, take: pageSize,
select: { where: {
id: true, ...(code && { code: { contains: code } }),
name: true, ...(!isNaN(status) && { status }),
code: true, ...(name && { name: { contains: name } }),
status: true,
}, },
}; };
const [list, count] = await this.prisma.$transaction([ const [list, count] = await this.prisma.$transaction([