From d5fb2cd48d89012aa173b79f510c63d44663ee79 Mon Sep 17 00:00:00 2001 From: FatttSnake Date: Wed, 29 Nov 2023 17:59:41 +0800 Subject: [PATCH] Add change password to user management page --- src/assets/svg/eye.svg | 1 + src/components/home/Footer.tsx | 2 +- src/global.d.ts | 36 +++-- src/pages/HomeFramework.tsx | 2 +- src/pages/system/User.tsx | 274 +++++++++++++++++++++++++-------- src/services/system.tsx | 29 ++-- src/utils/common.tsx | 8 + 7 files changed, 260 insertions(+), 92 deletions(-) create mode 100644 src/assets/svg/eye.svg diff --git a/src/assets/svg/eye.svg b/src/assets/svg/eye.svg new file mode 100644 index 0000000..e642fde --- /dev/null +++ b/src/assets/svg/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/home/Footer.tsx b/src/components/home/Footer.tsx index bf11c34..3dee754 100644 --- a/src/components/home/Footer.tsx +++ b/src/components/home/Footer.tsx @@ -8,7 +8,7 @@ const Footer: React.FC = () => { return ( <> - +
diff --git a/src/global.d.ts b/src/global.d.ts index 587ba46..94527a2 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -95,21 +95,6 @@ interface UserWithRoleInfoVo { groups: GroupVo[] } -interface UserAddEditParam { - id?: string - username: string - password?: string - locking?: boolean - expiration?: string - credentialsExpiration?: string - enable?: boolean - nickname?: string - avatar?: string - email?: string - roleIds: number[] - groupIds: number[] -} - interface UserInfoVo { id: string userId: string @@ -190,6 +175,27 @@ interface TableParam { filters?: Record } +interface UserAddEditParam { + id?: string + username: string + password?: string + locking?: boolean + expiration?: string + credentialsExpiration?: string + enable?: boolean + nickname?: string + avatar?: string + email?: string + roleIds: number[] + groupIds: number[] +} + +interface UserChangePasswordParam { + id: string + password: string + credentialsExpiration?: string +} + interface SysLogGetParam extends PageParam { searchRequestUrl?: string searchRegex?: boolean diff --git a/src/pages/HomeFramework.tsx b/src/pages/HomeFramework.tsx index 1809bb0..e174f00 100644 --- a/src/pages/HomeFramework.tsx +++ b/src/pages/HomeFramework.tsx @@ -80,7 +80,7 @@ const HomeFramework: React.FC = () => { ref={hideScrollbarRef} isPreventVerticalScroll={preventScroll} isShowHorizontalScrollbar={true} - minWidth={'900px'} + minWidth={900} >
diff --git a/src/pages/system/User.tsx b/src/pages/system/User.tsx index 89f686d..6c61387 100644 --- a/src/pages/system/User.tsx +++ b/src/pages/system/User.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useEffect, useState } from 'react' import Icon from '@ant-design/icons' import dayjs from 'dayjs' import { @@ -12,11 +12,12 @@ import { DATABASE_SELECT_SUCCESS, DATABASE_UPDATE_SUCCESS } from '@/constants/common.constants' -import { utcToLocalTime, isPastTime, localTimeToUtc, dayjsToUtc } from '@/utils/common' +import { utcToLocalTime, isPastTime, localTimeToUtc, dayjsToUtc, getNowUtc } from '@/utils/common' import { r_sys_group_get_list, r_sys_role_get_list, r_sys_user_add, + r_sys_user_change_password, r_sys_user_delete, r_sys_user_delete_list, r_sys_user_get, @@ -28,13 +29,30 @@ import HideScrollbar from '@/components/common/HideScrollbar' import FlexBox from '@/components/common/FlexBox' import Card from '@/components/common/Card' +interface ChangePasswordFields extends UserChangePasswordParam { + passwordConfirm: string + needChangePassword: boolean +} + const User: React.FC = () => { const [modal, contextHolder] = AntdModal.useModal() + + const [isDrawerOpen, setIsDrawerOpen] = useState(false) + const [isDrawerEdit, setIsDrawerEdit] = useState(false) + const [isDrawerSubmittable, setIsDrawerSubmittable] = useState(false) + const [isDrawerSubmitting, setIsDrawerSubmitting] = useState(false) + const [roleData, setRoleData] = useState([]) + const [groupData, setGroupData] = useState([]) + const [isLoadingRole, setIsLoadingRole] = useState(false) + const [isLoadingGroup, setIsLoadingGroup] = useState(false) + const [avatar, setAvatar] = useState('') + const [form] = AntdForm.useForm() const formValues = AntdForm.useWatch([], form) const [newFormValues, setNewFormValues] = useState() - const [userData, setUserData] = useState([]) - const [isLoading, setIsLoading] = useState(false) + + const [changePasswordForm] = AntdForm.useForm() + const [tableParams, setTableParams] = useState({ pagination: { current: 1, @@ -42,25 +60,19 @@ const User: React.FC = () => { position: ['bottomCenter'] } }) + const [tableSelectedItem, setTableSelectedItem] = useState([]) + const [userData, setUserData] = useState([]) + const [isLoadingUserData, setIsLoadingUserData] = useState(false) + const [searchValue, setSearchValue] = 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 [groupData, setGroupData] = useState([]) - const [isLoadingRole, setIsLoadingRole] = useState(false) - const [isLoadingGroup, setIsLoadingGroup] = useState(false) - const [isSubmitting, setIsSubmitting] = useState(false) - const [tableSelectedItem, setTableSelectedItem] = useState([]) - const [avatar, setAvatar] = useState('') const dataColumns: _ColumnsType = [ { dataIndex: 'username', title: '用户', - render: (value, record) => `${value}(${record.id})`, + render: (value, record) => {value}, width: '0' }, { @@ -68,7 +80,13 @@ const User: React.FC = () => { title: '头像', render: (value) => ( } + src={ + }} + src={`data:image/png;base64,${value}`} + alt={'avatar'} + /> + } style={{ background: COLOR_BACKGROUND }} /> ), @@ -127,25 +145,35 @@ const User: React.FC = () => { }, { title: '状态', - render: (_, record) => - !record.locking && - (!record.expiration || !isPastTime(record.expiration)) && - (!record.credentialsExpiration || !isPastTime(record.credentialsExpiration)) && - record.enable ? ( - 正常 - ) : ( - <> - {record.locking ? 锁定 : undefined} - {record.expiration && isPastTime(record.expiration) ? ( - 过期 - ) : undefined} - {record.credentialsExpiration && - isPastTime(record.credentialsExpiration) ? ( - 改密 - ) : undefined} - {!record.enable ? 禁用 : undefined} - - ), + render: (_, record) => ( + +

创建:{utcToLocalTime(record.createTime)}

+

修改:{utcToLocalTime(record.updateTime)}

+ + } + > + {!record.locking && + (!record.expiration || !isPastTime(record.expiration)) && + (!record.credentialsExpiration || !isPastTime(record.credentialsExpiration)) && + record.enable ? ( + 正常 + ) : ( + <> + {record.locking ? 锁定 : undefined} + {record.expiration && isPastTime(record.expiration) ? ( + 过期 + ) : undefined} + {record.credentialsExpiration && + isPastTime(record.credentialsExpiration) ? ( + 改密 + ) : undefined} + {!record.enable ? 禁用 : undefined} + + )} +
+ ), align: 'center' }, { @@ -155,6 +183,12 @@ const User: React.FC = () => { render: (_, record) => ( <> + + 修改密码 + { .then( (confirmed) => { if (confirmed) { - setIsLoading(true) + setIsLoadingUserData(true) void r_sys_user_delete_list(tableSelectedItem) .then((res) => { @@ -246,7 +280,7 @@ const User: React.FC = () => { } }) .finally(() => { - setIsLoading(false) + setIsLoadingUserData(false) }) } }, @@ -254,6 +288,115 @@ const User: React.FC = () => { ) } + const handleOnChangePasswordBtnClick = (value: UserWithRoleInfoVo) => { + return () => { + changePasswordForm.setFieldValue('id', value.id) + changePasswordForm.setFieldValue('password', undefined) + changePasswordForm.setFieldValue('passwordConfirm', undefined) + changePasswordForm.setFieldValue( + 'needChangePassword', + value.credentialsExpiration && isPastTime(value.credentialsExpiration) + ) + void modal.confirm({ + icon: <>, + title: ( + <> + + 修改用户 {value.username} 的密码 + + ), + content: ( + + + + + + + + ({ + validator(_, value) { + if (!value || getFieldValue('password') === value) { + return Promise.resolve() + } + return Promise.reject(new Error('两次密码输入不一致')) + } + }) + ]} + > + + + + + + + ), + onOk: () => + changePasswordForm + .validateFields() + .then( + () => { + return new Promise((resolve, reject) => { + void r_sys_user_change_password({ + id: changePasswordForm.getFieldValue('id') as string, + password: changePasswordForm.getFieldValue( + 'password' + ) as string, + credentialsExpiration: (changePasswordForm.getFieldValue( + 'needChangePassword' + ) as boolean) + ? getNowUtc() + : undefined + }).then((res) => { + const response = res.data + if (response.success) { + resolve(true) + } else { + reject(response.msg) + } + }) + }) + }, + () => { + return new Promise((_, reject) => { + reject('输入有误') + }) + } + ) + .then(() => { + getUser() + }) + }) + } + } + const handleOnEditBtnClick = (value: UserWithRoleInfoVo) => { return () => { setIsDrawerEdit(true) @@ -291,7 +434,7 @@ const User: React.FC = () => { .then( (confirmed) => { if (confirmed) { - setIsLoading(true) + setIsLoadingUserData(true) void r_sys_user_delete(value.id) .then((res) => { @@ -306,7 +449,7 @@ const User: React.FC = () => { } }) .finally(() => { - setIsLoading(false) + setIsLoadingUserData(false) }) } }, @@ -323,11 +466,11 @@ const User: React.FC = () => { (option?.label ?? '').toLowerCase().includes(input.toLowerCase()) const handleOnSubmit = () => { - if (isSubmitting) { + if (isDrawerSubmitting) { return } - setIsSubmitting(true) + setIsDrawerSubmitting(true) if (isDrawerEdit) { void r_sys_user_update({ @@ -355,7 +498,7 @@ const User: React.FC = () => { } }) .finally(() => { - setIsSubmitting(false) + setIsDrawerSubmitting(false) }) } else { void r_sys_user_add({ @@ -384,7 +527,7 @@ const User: React.FC = () => { } }) .finally(() => { - setIsSubmitting(false) + setIsDrawerSubmitting(false) }) } } @@ -429,7 +572,7 @@ const User: React.FC = () => { } const getUser = () => { - if (isLoading) { + if (isLoadingUserData) { return } @@ -438,7 +581,7 @@ const User: React.FC = () => { return } - setIsLoading(true) + setIsLoadingUserData(true) void r_sys_user_get() .then((res) => { @@ -460,7 +603,7 @@ const User: React.FC = () => { } }) .finally(() => { - setIsLoading(false) + setIsLoadingUserData(false) }) } @@ -526,10 +669,10 @@ const User: React.FC = () => { useEffect(() => { form.validateFields({ validateOnly: true }).then( () => { - setSubmittable(true) + setIsDrawerSubmittable(true) }, () => { - setSubmittable(false) + setIsDrawerSubmittable(false) } ) @@ -564,8 +707,19 @@ const User: React.FC = () => { ]) const addAndEditForm = ( - -
+ +
{ /> } size={100} - style={{ background: COLOR_BACKGROUND }} + style={{ background: COLOR_BACKGROUND, cursor: 'pointer' }} onClick={getAvatar} /> @@ -646,7 +800,7 @@ const User: React.FC = () => { valuePropName={'checked'} name={'locking'} label={'锁定'} - rules={[{ required: true, type: 'boolean' }]} + rules={[{ type: 'boolean' }]} > @@ -684,7 +838,7 @@ const User: React.FC = () => { valuePropName={'checked'} name={'enable'} label={'启用'} - rules={[{ required: true, type: 'boolean' }]} + rules={[{ type: 'boolean' }]} > @@ -756,7 +910,7 @@ const User: React.FC = () => { columns={dataColumns} rowKey={(record) => record.id} pagination={tableParams.pagination} - loading={isLoading} + loading={isLoadingUserData} onChange={handleOnTableChange} rowSelection={{ type: 'checkbox', @@ -769,17 +923,17 @@ const User: React.FC = () => { const drawerToolbar = ( - + - + 取消 提交 @@ -806,8 +960,8 @@ const User: React.FC = () => { width={'36vw'} onClose={handleOnDrawerClose} open={isDrawerOpen} - closable={!isSubmitting} - maskClosable={!isSubmitting} + closable={!isDrawerSubmitting} + maskClosable={!isDrawerSubmitting} extra={drawerToolbar} > {addAndEditForm} diff --git a/src/services/system.tsx b/src/services/system.tsx index 90f1b5b..15efd36 100644 --- a/src/services/system.tsx +++ b/src/services/system.tsx @@ -15,10 +15,12 @@ export const r_sys_user_info = () => request.get(URL_SYS_US export const r_sys_user_get = () => request.get>(URL_SYS_USER) -export const r_sys_user_add = (param: UserAddEditParam) => request.post(URL_SYS_USER, { ...param }) +export const r_sys_user_add = (param: UserAddEditParam) => request.post(URL_SYS_USER, param) -export const r_sys_user_update = (param: UserAddEditParam) => - request.put(URL_SYS_USER, { ...param }) +export const r_sys_user_update = (param: UserAddEditParam) => request.put(URL_SYS_USER, param) + +export const r_sys_user_change_password = (param: UserChangePasswordParam) => + request.patch(URL_SYS_USER, param) export const r_sys_user_delete = (id: string) => request.delete(`${URL_SYS_USER}/${id}`) @@ -27,39 +29,36 @@ export const r_sys_user_delete_list = (ids: React.Key[]) => request.delete(URL_S export const r_sys_power_get_list = () => request.get(URL_SYS_POWER_LIST) export const r_sys_role_get = (param: RoleGetParam) => - request.get>(URL_SYS_ROLE, { ...param }) + request.get>(URL_SYS_ROLE, param) export const r_sys_role_get_list = () => request.get(URL_SYS_ROLE_LIST) export const r_sys_role_change_status = (param: RoleChangeStatusParam) => - request.patch(URL_SYS_ROLE, { ...param }) + request.patch(URL_SYS_ROLE, param) -export const r_sys_role_add = (param: RoleAddEditParam) => request.post(URL_SYS_ROLE, { ...param }) +export const r_sys_role_add = (param: RoleAddEditParam) => request.post(URL_SYS_ROLE, param) -export const r_sys_role_update = (param: RoleAddEditParam) => - request.put(URL_SYS_ROLE, { ...param }) +export const r_sys_role_update = (param: RoleAddEditParam) => request.put(URL_SYS_ROLE, param) export const r_sys_role_delete = (id: string) => request.delete(`${URL_SYS_ROLE}/${id}`) export const r_sys_role_delete_list = (ids: React.Key[]) => request.delete(URL_SYS_ROLE, { ids }) export const r_sys_group_get = (param: GroupGetParam) => - request.get>(URL_SYS_GROUP, { ...param }) + request.get>(URL_SYS_GROUP, param) export const r_sys_group_get_list = () => request.get(URL_SYS_GROUP_LIST) export const r_sys_group_change_status = (param: GroupChangeStatusParam) => - request.patch(URL_SYS_GROUP, { ...param }) + request.patch(URL_SYS_GROUP, param) -export const r_sys_group_add = (param: GroupAddEditParam) => - request.post(URL_SYS_GROUP, { ...param }) +export const r_sys_group_add = (param: GroupAddEditParam) => request.post(URL_SYS_GROUP, param) -export const r_sys_group_update = (param: GroupAddEditParam) => - request.put(URL_SYS_GROUP, { ...param }) +export const r_sys_group_update = (param: GroupAddEditParam) => request.put(URL_SYS_GROUP, param) export const r_sys_group_delete = (id: string) => request.delete(`${URL_SYS_GROUP}/${id}`) export const r_sys_group_delete_list = (ids: React.Key[]) => request.delete(URL_SYS_GROUP, { ids }) export const r_sys_log_get = (param: SysLogGetParam) => - request.get>(URL_SYS_LOG, { ...param }) + request.get>(URL_SYS_LOG, param) diff --git a/src/utils/common.tsx b/src/utils/common.tsx index 2136aa5..c58a74a 100644 --- a/src/utils/common.tsx +++ b/src/utils/common.tsx @@ -145,6 +145,14 @@ export const getRedirectUrl = (path: string, redirectUrl: string): string => { return `${path}?redirect=${encodeURIComponent(redirectUrl)}` } +export const getNowLocalTime = (format: string = 'yyyy-MM-DD HH:mm:ssZ') => { + return moment().local().format(format) +} + +export const getNowUtc = () => { + return moment().toISOString() +} + export const utcToLocalTime = (utcTime: string, format: string = 'yyyy-MM-DD HH:mm:ssZ') => { return moment.utc(utcTime).local().format(format) }