From 326c80862290fd68250fb429f35aaea41991f758 Mon Sep 17 00:00:00 2001 From: FatttSnake Date: Wed, 15 Nov 2023 18:08:47 +0800 Subject: [PATCH] Finish group page --- src/constants/urls.constants.ts | 5 +- src/global.d.ts | 26 ++ src/pages/system/Group.tsx | 633 +++++++++++++++++++++++++++++++- src/pages/system/Log.tsx | 4 +- src/pages/system/Role.tsx | 28 +- src/services/system.tsx | 50 ++- 6 files changed, 717 insertions(+), 29 deletions(-) diff --git a/src/constants/urls.constants.ts b/src/constants/urls.constants.ts index 0283a42..a3bab52 100644 --- a/src/constants/urls.constants.ts +++ b/src/constants/urls.constants.ts @@ -5,4 +5,7 @@ export const URL_API_SYS_LOG = '/system/log' export const URL_API_SYS_USER_INFO = '/system/user/info' export const URL_API_SYS_USER = '/system/user' export const URL_API_SYS_ROLE = '/system/role' -export const URL_API_SYS_POWER = '/system/power' +export const URL_API_SYS_POWER_LIST = '/system/power/list' +export const URL_API_SYS_ROLE_LIST = '/system/role/list' +export const URL_API_SYS_GROUP = '/system/group' +export const URL_API_SYS_GROUP_LIST = '/system/group/list' diff --git a/src/global.d.ts b/src/global.d.ts index 0ec2d7b..02562f2 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -233,3 +233,29 @@ interface PowerSetVo { elementList: ElementVo[] operationList: OperationVo[] } + +interface GroupGetParam extends PageParam { + searchName?: string + searchRegex?: boolean +} + +interface GroupWithRoleGetVo { + id: string + name: string + enable: string + createTime: string + updateTime: string + roles: RoleVo[] +} + +interface GroupAddEditParam { + id?: string + name: string + roleIds: number[] + enable: boolean +} + +interface GroupChangeStatusParam { + id: string + enable: boolean +} diff --git a/src/pages/system/Group.tsx b/src/pages/system/Group.tsx index e7a7aa9..2b37869 100644 --- a/src/pages/system/Group.tsx +++ b/src/pages/system/Group.tsx @@ -1,7 +1,636 @@ -import React from 'react' +import React, { useEffect, useState } from 'react' +import FitFullScreen from '@/components/common/FitFullScreen.tsx' +import HideScrollbar from '@/components/common/HideScrollbar.tsx' +import FlexBox from '@/components/common/FlexBox.tsx' +import Icon from '@ant-design/icons' +import Card from '@/components/common/Card.tsx' +import { + COLOR_ERROR_SECONDARY, + COLOR_FONT_SECONDARY, + COLOR_PRODUCTION, + DATABASE_DELETE_SUCCESS, + DATABASE_DUPLICATE_KEY, + DATABASE_INSERT_SUCCESS, + DATABASE_SELECT_SUCCESS, + DATABASE_UPDATE_SUCCESS +} from '@/constants/common.constants.ts' +import { getLocalTime } from '@/utils/common.tsx' +import { + r_sys_group_add, + r_sys_group_change_status, + r_sys_group_delete, + r_sys_group_delete_list, + r_sys_group_get, + r_sys_group_update, + r_sys_role_get_list +} from '@/services/system.tsx' const Group: React.FC = () => { - return <> + const [modal, contextHolder] = AntdModal.useModal() + const [form] = AntdForm.useForm() + const formValues = AntdForm.useWatch([], form) + const [newFormValues, setNewFormValues] = useState() + const [groupData, setGroupData] = useState([]) + const [isLoading, setIsLoading] = useState(false) + const [tableParams, setTableParams] = useState({ + pagination: { + current: 1, + pageSize: 20, + position: ['bottomCenter'] + } + }) + const [searchName, setSearchName] = useState('') + const [isUseRegex, setIsUseRegex] = useState(false) + const [isRegexLegal, setIsRegexLegal] = useState(true) + const [isDrawerOpen, setIsDrawerOpen] = useState(false) + const [isDrawerEdit, setIsDrawerEdit] = useState(false) + const [submittable, setSubmittable] = useState(false) + const [roleData, setRoleData] = useState([]) + const [isLoadingRole, setIsLoadingRole] = useState(false) + const [isSubmitting, setIsSubmitting] = useState(false) + const [tableSelectedItem, setTableSelectedItem] = useState([]) + + const dataColumns: _ColumnsType = [ + { + title: '名称', + dataIndex: 'name', + width: '15%' + }, + { + title: '角色', + dataIndex: 'roles', + render: (value: RoleVo[]) => + value.length ? ( + value.map((role) => ( + + {role.name} + + )) + ) : ( + + ) + }, + { + title: '创建时间', + dataIndex: 'createTime', + width: '10%', + align: 'center', + render: (value: string) => getLocalTime(value) + }, + { + title: '修改时间', + dataIndex: 'updateTime', + width: '10%', + align: 'center', + render: (value: string) => getLocalTime(value) + }, + { + title: '状态', + dataIndex: 'enable', + width: '5%', + align: 'center', + render: (value) => + value ? 启用 : 禁用 + }, + { + title: '操作', + dataIndex: 'enable', + width: '15em', + align: 'center', + render: (value, record) => ( + <> + + {value ? ( + + 禁用 + + ) : ( + + 启用 + + )} + + 编辑 + + + 删除 + + + + ) + } + ] + + const handleOnTableChange = ( + pagination: _TablePaginationConfig, + filters: Record, + sorter: _SorterResult | _SorterResult[] + ) => { + if (Array.isArray(sorter)) { + setTableParams({ + pagination, + filters, + sortField: sorter.map((value) => value.field).join(',') + }) + } else { + setTableParams({ + pagination, + filters, + sortField: sorter.field, + sortOrder: sorter.order + }) + } + + if (pagination.pageSize !== tableParams.pagination?.pageSize) { + setGroupData([]) + } + } + + const handleOnTableSelectChange = (selectedRowKeys: React.Key[]) => { + setTableSelectedItem(selectedRowKeys) + } + + const handleOnAddBtnClick = () => { + setIsDrawerEdit(false) + setIsDrawerOpen(true) + form.setFieldValue('id', undefined) + form.setFieldValue('name', newFormValues?.name) + form.setFieldValue('roleIds', newFormValues?.roleIds) + form.setFieldValue('enable', newFormValues?.enable ?? true) + } + + const handleOnListDeleteBtnClick = () => { + modal + .confirm({ + title: '确定删除', + content: `确定删除选中的 ${tableSelectedItem.length} 个用户组吗?` + }) + .then( + (confirmed) => { + if (confirmed) { + setIsLoading(true) + + void r_sys_group_delete_list(tableSelectedItem) + .then((res) => { + const data = res.data + + if (data.code === DATABASE_DELETE_SUCCESS) { + void message.success('删除成功') + setTimeout(() => { + getGroup() + }) + } else { + void message.error('删除失败,请稍后重试') + } + }) + .finally(() => { + setIsLoading(false) + }) + } + }, + () => {} + ) + } + + const handleOnEditBtnClick = (value: GroupWithRoleGetVo) => { + return () => { + setIsDrawerEdit(true) + setIsDrawerOpen(true) + form.setFieldValue('id', value.id) + form.setFieldValue('name', value.name) + form.setFieldValue( + 'roleIds', + value.roles.map((role) => role.id) + ) + form.setFieldValue('enable', value.enable) + void form.validateFields() + } + } + + const handleOnDeleteBtnClick = (value: GroupWithRoleGetVo) => { + return () => { + modal + .confirm({ + title: '确定删除', + content: `确定删除角色 ${value.name} 吗?` + }) + .then( + (confirmed) => { + if (confirmed) { + setIsLoading(true) + + void r_sys_group_delete(value.id) + .then((res) => { + const data = res.data + if (data.code === DATABASE_DELETE_SUCCESS) { + void message.success('删除成功') + setTimeout(() => { + getGroup() + }) + } else { + void message.error('删除失败,请稍后重试') + } + }) + .finally(() => { + setIsLoading(false) + }) + } + }, + () => {} + ) + } + } + + const handleOnDrawerClose = () => { + setIsDrawerOpen(false) + } + + const handleOnSubmit = () => { + if (isSubmitting) { + return + } + + setIsSubmitting(true) + + if (isDrawerEdit) { + void r_sys_group_update(formValues) + .then((res) => { + const data = res.data + switch (data.code) { + case DATABASE_UPDATE_SUCCESS: + setIsDrawerOpen(false) + void message.success('更新成功') + getGroup() + break + case DATABASE_DUPLICATE_KEY: + void message.error('已存在相同名称的角色') + break + default: + void message.error('更新失败,请稍后重试') + } + }) + .finally(() => { + setIsSubmitting(false) + }) + } else { + void r_sys_group_add(formValues) + .then((res) => { + const data = res.data + switch (data.code) { + case DATABASE_INSERT_SUCCESS: + setIsDrawerOpen(false) + void message.success('添加成功') + setNewFormValues(undefined) + getGroup() + break + case DATABASE_DUPLICATE_KEY: + void message.error('已存在相同名称的角色') + break + default: + void message.error('添加失败,请稍后重试') + } + }) + .finally(() => { + setIsSubmitting(false) + }) + } + } + + const handleOnSearchNameChange = (e: React.ChangeEvent) => { + setSearchName(e.target.value) + + if (isUseRegex) { + try { + RegExp(e.target.value) + setIsRegexLegal(!(e.target.value.includes('{}') || e.target.value.includes('[]'))) + } catch (e) { + setIsRegexLegal(false) + } + } else { + setIsRegexLegal(true) + } + } + + const handleOnSearchNameKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + getGroup() + } + } + + const handleOnUseRegexChange = (e: _CheckboxChangeEvent) => { + setIsUseRegex(e.target.checked) + if (e.target.checked) { + try { + RegExp(searchName) + setIsRegexLegal(!(searchName.includes('{}') || searchName.includes('[]'))) + } catch (e) { + setIsRegexLegal(false) + } + } else { + setIsRegexLegal(true) + } + } + + const handleOnQueryBtnClick = () => { + getGroup() + } + + const handleOnChangStatusBtnClick = (id: string, newStatus: boolean) => { + return () => { + if (isLoading) { + return + } + + setIsLoading(true) + void r_sys_group_change_status({ id, enable: newStatus }) + .then((res) => { + const data = res.data + if (data.code === DATABASE_UPDATE_SUCCESS) { + void message.success('更新成功') + setTimeout(() => { + getGroup() + }) + } else { + void message.error('更新失败,请稍后重试') + } + }) + .finally(() => { + setIsLoading(false) + }) + } + } + + const getGroup = () => { + if (isLoading) { + return + } + + if (!isRegexLegal) { + void message.error('非法正则表达式') + return + } + + setIsLoading(true) + + void r_sys_group_get({ + currentPage: tableParams.pagination?.current, + pageSize: tableParams.pagination?.pageSize, + sortField: + tableParams.sortField && tableParams.sortOrder + ? (tableParams.sortField as string) + : undefined, + sortOrder: + tableParams.sortField && tableParams.sortOrder ? tableParams.sortOrder : undefined, + searchName: searchName.trim().length ? searchName : undefined, + searchRegex: isUseRegex ? isUseRegex : undefined, + ...tableParams.filters + }) + .then((res) => { + const data = res.data + if (data.code === DATABASE_SELECT_SUCCESS) { + const records = data.data?.records + + records && setGroupData(records) + data.data && + setTableParams({ + ...tableParams, + pagination: { + ...tableParams.pagination, + total: data.data.total + } + }) + } else { + void message.error('获取失败,请稍后重试') + } + }) + .finally(() => { + setIsLoading(false) + }) + } + + const getRoleData = () => { + if (isLoadingRole) { + return + } + + setIsLoadingRole(true) + + void r_sys_role_get_list() + .then((res) => { + const data = res.data + + if (data.code === DATABASE_SELECT_SUCCESS) { + data.data && setRoleData(data.data) + } else { + void message.error('获取角色列表失败,请稍后重试') + } + }) + .finally(() => { + setIsLoadingRole(false) + }) + } + + useEffect(() => { + form.validateFields({ validateOnly: true }).then( + () => { + setSubmittable(true) + }, + () => { + setSubmittable(false) + } + ) + + if (!isDrawerEdit && formValues) { + setNewFormValues({ + name: formValues.name, + roleIds: formValues.roleIds, + enable: formValues.enable + }) + } + }, [formValues]) + + useEffect(() => { + getRoleData() + }, []) + + useEffect(() => { + getGroup() + }, [ + JSON.stringify(tableParams.filters), + JSON.stringify(tableParams.sortField), + JSON.stringify(tableParams.sortOrder), + JSON.stringify(tableParams.pagination?.pageSize), + JSON.stringify(tableParams.pagination?.current) + ]) + + const toolbar = ( + + + + + + + + + + 名称 + + } + suffix={ + <> + {!isRegexLegal ? ( + 非法表达式 + ) : undefined} + + .* + + + } + allowClear + value={searchName} + onChange={handleOnSearchNameChange} + onKeyDown={handleOnSearchNameKeyDown} + status={isRegexLegal ? undefined : 'error'} + /> + + + + 查询 + + + + ) + + const table = ( + + record.id} + pagination={tableParams.pagination} + loading={isLoading} + onChange={handleOnTableChange} + rowSelection={{ + type: 'checkbox', + onChange: handleOnTableSelectChange + }} + /> + + ) + + const drawerToolbar = ( + + + + + + + + 取消 + + + 提交 + + + ) + + const addAndEditForm = ( + + + + + + + ({ + value: value.id, + label: `${value.name}${!value.enable ? '(已禁用)' : ''}` + }))} + /> + + + + + + ) + + return ( + <> + + + + {toolbar} + {table} + + + + + {addAndEditForm} + + {contextHolder} + + ) } export default Group diff --git a/src/pages/system/Log.tsx b/src/pages/system/Log.tsx index 2bc9cf4..69eff9f 100644 --- a/src/pages/system/Log.tsx +++ b/src/pages/system/Log.tsx @@ -1,7 +1,7 @@ import React from 'react' import FitFullScreen from '@/components/common/FitFullScreen' import Card from '@/components/common/Card' -import { r_sysLog_get } from '@/services/system' +import { r_sys_log_get } from '@/services/system' import { COLOR_ERROR_SECONDARY, COLOR_FONT_SECONDARY, @@ -215,7 +215,7 @@ const Log: React.FC = () => { setLoading(true) - void r_sysLog_get({ + void r_sys_log_get({ currentPage: tableParams.pagination?.current, pageSize: tableParams.pagination?.pageSize, sortField: diff --git a/src/pages/system/Role.tsx b/src/pages/system/Role.tsx index 7271be0..f1451b8 100644 --- a/src/pages/system/Role.tsx +++ b/src/pages/system/Role.tsx @@ -4,13 +4,13 @@ import HideScrollbar from '@/components/common/HideScrollbar' import FlexBox from '@/components/common/FlexBox' import Card from '@/components/common/Card' import { - r_role_add, - r_role_changStatus, - r_power_get, - r_role_get, - r_role_update, - r_role_delete, - r_role_delete_list + r_sys_role_add, + r_sys_role_change_status, + r_sys_power_get_list, + r_sys_role_get, + r_sys_role_update, + r_sys_role_delete, + r_sys_role_delete_list } from '@/services/system' import { COLOR_ERROR_SECONDARY, @@ -174,7 +174,7 @@ const Role: React.FC = () => { if (confirmed) { setIsLoading(true) - void r_role_delete_list(tableSelectedItem) + void r_sys_role_delete_list(tableSelectedItem) .then((res) => { const data = res.data @@ -223,7 +223,7 @@ const Role: React.FC = () => { if (confirmed) { setIsLoading(true) - void r_role_delete(value.id) + void r_sys_role_delete(value.id) .then((res) => { const data = res.data if (data.code === DATABASE_DELETE_SUCCESS) { @@ -257,7 +257,7 @@ const Role: React.FC = () => { setIsSubmitting(true) if (isDrawerEdit) { - void r_role_update(formValues) + void r_sys_role_update(formValues) .then((res) => { const data = res.data switch (data.code) { @@ -277,7 +277,7 @@ const Role: React.FC = () => { setIsSubmitting(false) }) } else { - void r_role_add(formValues) + void r_sys_role_add(formValues) .then((res) => { const data = res.data switch (data.code) { @@ -346,7 +346,7 @@ const Role: React.FC = () => { } setIsLoading(true) - void r_role_changStatus({ id, enable: newStatus }) + void r_sys_role_change_status({ id, enable: newStatus }) .then((res) => { const data = res.data if (data.code === DATABASE_UPDATE_SUCCESS) { @@ -376,7 +376,7 @@ const Role: React.FC = () => { setIsLoading(true) - void r_role_get({ + void r_sys_role_get({ currentPage: tableParams.pagination?.current, pageSize: tableParams.pagination?.pageSize, sortField: @@ -430,7 +430,7 @@ const Role: React.FC = () => { setIsLoadingPower(true) - void r_power_get() + void r_sys_power_get_list() .then((res) => { const data = res.data diff --git a/src/services/system.tsx b/src/services/system.tsx index b385b3e..16e662b 100644 --- a/src/services/system.tsx +++ b/src/services/system.tsx @@ -1,23 +1,53 @@ import React from 'react' import request from '@/services/index' -import { URL_API_SYS_LOG, URL_API_SYS_POWER, URL_API_SYS_ROLE } from '@/constants/urls.constants' +import { + URL_API_SYS_LOG, + URL_API_SYS_POWER_LIST, + URL_API_SYS_ROLE, + URL_API_SYS_ROLE_LIST, + URL_API_SYS_GROUP, + URL_API_SYS_GROUP_LIST +} from '@/constants/urls.constants' -export const r_sysLog_get = (param: SysLogGetParam) => +export const r_sys_log_get = (param: SysLogGetParam) => request.get>(URL_API_SYS_LOG, { ...param }) -export const r_role_get = (param: RoleGetParam) => +export const r_sys_power_get_list = () => request.get(URL_API_SYS_POWER_LIST) + +export const r_sys_role_get = (param: RoleGetParam) => request.get>(URL_API_SYS_ROLE, { ...param }) -export const r_role_changStatus = (param: RoleChangeStatusParam) => +export const r_sys_role_get_list = () => request.get(URL_API_SYS_ROLE_LIST) + +export const r_sys_role_change_status = (param: RoleChangeStatusParam) => request.patch(URL_API_SYS_ROLE, { ...param }) -export const r_power_get = () => request.get(URL_API_SYS_POWER) +export const r_sys_role_add = (param: RoleAddEditParam) => + request.post(URL_API_SYS_ROLE, { ...param }) -export const r_role_add = (param: RoleAddEditParam) => request.post(URL_API_SYS_ROLE, { ...param }) - -export const r_role_update = (param: RoleAddEditParam) => +export const r_sys_role_update = (param: RoleAddEditParam) => request.put(URL_API_SYS_ROLE, { ...param }) -export const r_role_delete = (id: string) => request.delete(`${URL_API_SYS_ROLE}/${id}`) +export const r_sys_role_delete = (id: string) => request.delete(`${URL_API_SYS_ROLE}/${id}`) -export const r_role_delete_list = (ids: React.Key[]) => request.delete(URL_API_SYS_ROLE, { ids }) +export const r_sys_role_delete_list = (ids: React.Key[]) => + request.delete(URL_API_SYS_ROLE, { ids }) + +export const r_sys_group_get = (param: GroupGetParam) => + request.get>(URL_API_SYS_GROUP, { ...param }) + +export const r_sys_group_get_list = () => request.get(URL_API_SYS_GROUP_LIST) + +export const r_sys_group_change_status = (param: GroupChangeStatusParam) => + request.patch(URL_API_SYS_GROUP, { ...param }) + +export const r_sys_group_add = (param: GroupAddEditParam) => + request.post(URL_API_SYS_GROUP, { ...param }) + +export const r_sys_group_update = (param: GroupAddEditParam) => + request.put(URL_API_SYS_GROUP, { ...param }) + +export const r_sys_group_delete = (id: string) => request.delete(`${URL_API_SYS_GROUP}/${id}`) + +export const r_sys_group_delete_list = (ids: React.Key[]) => + request.delete(URL_API_SYS_GROUP, { ids })