From 09726a51bffe4f019a57d5c4af93c5b63bd91524 Mon Sep 17 00:00:00 2001 From: Jimmy Liow Date: Thu, 27 Jun 2024 10:02:02 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E7=8A=B6=E6=80=81=E5=92=8C=E5=90=AF=E7=94=A8=E5=92=8C?= =?UTF-8?q?=E7=A6=81=E7=94=A8=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/zh/account.json | 3 +++ src/stores/Account.js | 12 +++++------ src/views/account/Management.jsx | 35 +++++++++++++++++++++++--------- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/public/locales/zh/account.json b/public/locales/zh/account.json index 35c0dc0..3849f07 100644 --- a/public/locales/zh/account.json +++ b/public/locales/zh/account.json @@ -12,7 +12,10 @@ "createdOn": "创建时间", "action": "操作", "action.edit": "编辑", + "action.enable": "启用", "action.disable": "禁用", + "action.enable.title": "确定启用该账号吗?", + "action.disable.title": "确定禁用该账号吗?", "action.resetPassword": "重置密码", "accountList": "管理账号", diff --git a/src/stores/Account.js b/src/stores/Account.js index 01010d8..ee3daca 100644 --- a/src/stores/Account.js +++ b/src/stores/Account.js @@ -70,16 +70,15 @@ const useAccountStore = create((set, get) => ({ accountList: [], - disableAccount: async (userId) => { + toggleAccountStatus: async (userId, status) => { + + const statusValue = status ? 'enable' : 'disable' const formData = new FormData() formData.append('lmi_sn', userId) - // enable | disable - formData.append('account_status', 'disable') - - const result = await postAccountStatus(formData) + formData.append('account_status', statusValue) - console.info(result) + return postAccountStatus(formData) }, resetAccountPassword: async (userId, password) => { @@ -147,6 +146,7 @@ const useAccountStore = create((set, get) => ({ lastLogin: r.wu_lastlogindate, travelAgencyName: r.travel_agency_name, travelAgencyId: r.travel_agency_id, + disabled: r.wu_limitsign, // 数据库支持逗号分隔多角色(5,6,7),目前界面只需单个。 roleId: parseInt(r.roles), role: r.roles_name, diff --git a/src/views/account/Management.jsx b/src/views/account/Management.jsx index 676ed9a..5496997 100644 --- a/src/views/account/Management.jsx +++ b/src/views/account/Management.jsx @@ -3,7 +3,7 @@ import useAccountStore, { fetchRoleList, fetchTravelAgencyByName } from '@/store import useFormStore from '@/stores/Form' import { isEmpty } from '@/utils/commons' import { ExclamationCircleFilled } from '@ant-design/icons' -import { App, Button, Col, Form, Input, Modal, Row, Select, Space, Table, Typography } from 'antd' +import { App, Button, Col, Form, Input, Modal, Row, Select, Space, Table, Typography, Switch } from 'antd' import dayjs from 'dayjs' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -53,10 +53,12 @@ function Management() { ) } - function actionRender(text, account) { + function actionRender(_, account) { return ( - + { + showDisableConfirm(account, checked) + }} /> ) @@ -69,9 +71,9 @@ function Management() { const [currentTravelAgency, setCurrentTravelAgency] = useState(null) const [accountForm] = Form.useForm() - const [searchAccountByCriteria, accountList, disableAccount, saveOrUpdateAccount, resetAccountPassword] = + const [searchAccountByCriteria, accountList, toggleAccountStatus, saveOrUpdateAccount, resetAccountPassword] = useAccountStore((state) => - [state.searchAccountByCriteria, state.accountList, state.disableAccount, state.saveOrUpdateAccount, state.resetAccountPassword]) + [state.searchAccountByCriteria, state.accountList, state.toggleAccountStatus, state.saveOrUpdateAccount, state.resetAccountPassword]) const formValues = useFormStore(state => state.formValues) const { notification, modal } = App.useApp() @@ -116,7 +118,6 @@ function Management() { } const onAccountFinish = (values) => { - console.log(values) saveOrUpdateAccount(values) .then(() => { handelAccountSearch() @@ -156,13 +157,27 @@ function Management() { setCurrentTravelAgency(newValue) } - const showDisableConfirm = (account) => { + const showDisableConfirm = (account, status) => { + + const confirmTitle = status ? t('account:action.enable.title') : t('account:action.disable.title') + modal.confirm({ - title: 'Do you want to disable this account?', + title: confirmTitle, icon: , - content: `Username: ${account.username}, Realname: ${account.realname}`, + content: t('account:username') + ': ' + account.username + ', ' + t('account:realname') + ': ' + account.realname, onOk() { - disableAccount(account.userId) + toggleAccountStatus(account.userId, status) + .then(() => { + handelAccountSearch() + }) + .catch(ex => { + notification.error({ + message: 'Notification', + description: ex.message, + placement: 'top', + duration: 4, + }) + }) }, onCancel() { }, From e3a28ebf252b309f2c27c1c3597845d7dde0b8f9 Mon Sep 17 00:00:00 2001 From: Jimmy Liow Date: Thu, 27 Jun 2024 13:49:25 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E6=9D=83=E9=99=90=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=B8=8D=E5=90=8C=E8=A7=92=E8=89=B2=E4=B8=8D=E5=90=8C=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E9=A6=96=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/RBAC 权限.sql | 10 ++++++++++ src/stores/Auth.js | 15 +++++++++++++++ src/views/Login.jsx | 7 +++---- src/views/account/Management.jsx | 6 +++--- src/views/account/RoleList.jsx | 1 + 5 files changed, 32 insertions(+), 7 deletions(-) diff --git a/doc/RBAC 权限.sql b/doc/RBAC 权限.sql index b463af5..3118103 100644 --- a/doc/RBAC 权限.sql +++ b/doc/RBAC 权限.sql @@ -74,6 +74,16 @@ VALUES ('审核价格', '/products/offer/audit', 'products') INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category]) VALUES ('录入价格', '/products/offer/put', 'products') +-- 默认页面 +INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category]) +VALUES ('最新计划', 'route=/reservation/newest', 'page') +INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category]) +VALUES ('机票订票', 'route=/airticket', 'page') +INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category]) +VALUES ('产品管理(客服)', 'route=/products', 'page') +INSERT INTO [dbo].[auth_resource] ([res_name] ,[res_pattern], [res_category]) +VALUES ('产品管理(供应商)', 'route=/products?from', 'page') + INSERT INTO [dbo].[auth_permission] ([role_id] ,[res_id]) VALUES (1, 1) INSERT INTO [dbo].[auth_permission] ([role_id] ,[res_id]) diff --git a/src/stores/Auth.js b/src/stores/Auth.js index 8374151..41b8899 100644 --- a/src/stores/Auth.js +++ b/src/stores/Auth.js @@ -60,6 +60,8 @@ const useAuthStore = create(obervseLifecycle((set, get) => ({ loginStatus: 0, + defaltRoute: '', + permissionList: [], isPermitted: (perm) => { @@ -98,13 +100,26 @@ const useAuthStore = create(obervseLifecycle((set, get) => ({ setStorage(KEY_USER_ID, userDetail.LMI_SN) setStorage(KEY_TRAVEL_AGENCY_ID, userDetail.LMI_VEI_SN) appendRequestParams('token', loginToken) + appendRequestParams('lmi_sn', userDetail.LMI_SN) // loadPageSpy(`${json.Result.VName}-${json.Result.LoginName}`) startTokenInterval() }, loadUserPermission: async(userId) => { + let deaultPage = '/' const permissionResult = await fetchPermissionListByUserId(userId) + const pageList = permissionResult.filter(p => { + return p.res_category === 'page' + }) + if (pageList.length > 0) { + const resPattern = pageList[0].res_pattern + const splitResult = resPattern.split('=') + if (splitResult.length > 1) + deaultPage = splitResult[1] + } + set(() => ({ + defaultRoute: deaultPage, permissionList: permissionResult.map(p => p.res_pattern) })) }, diff --git a/src/views/Login.jsx b/src/views/Login.jsx index f9c3008..a994d2d 100644 --- a/src/views/Login.jsx +++ b/src/views/Login.jsx @@ -6,9 +6,8 @@ import useAuthStore from '@/stores/Auth' import useNoticeStore from '@/stores/Notice' function Login() { - const [validateUserPassword, loginStatus] = - useAuthStore((state) => [state.validateUserPassword, state.loginStatus]) - const getBulletinUnReadCount = useNoticeStore((state) => state.getBulletinUnReadCount) + const [validateUserPassword, loginStatus, defaultRoute] = + useAuthStore((state) => [state.validateUserPassword, state.loginStatus, state.defaultRoute]) const { t, i18n } = useTranslation() const { notification } = App.useApp() @@ -17,7 +16,7 @@ function Login() { useEffect (() => { if (loginStatus === 302) { - navigate('/') + navigate(defaultRoute) } }, [loginStatus]) diff --git a/src/views/account/Management.jsx b/src/views/account/Management.jsx index 5496997..9fd988c 100644 --- a/src/views/account/Management.jsx +++ b/src/views/account/Management.jsx @@ -185,7 +185,7 @@ function Management() { } const showResetPasswordConfirm = (account) => { - const randomPassword = account.username + (Math.floor(Math.random() * 900) + 100) + const randomPassword = account.username + '@' + (Math.floor(Math.random() * 900) + 100) modal.confirm({ title: 'Do you want to reset password?', icon: , @@ -194,8 +194,8 @@ function Management() { resetAccountPassword(account.userId, randomPassword) .then(() => { notification.info({ - message: '新密码:' + randomPassword, - description: `请复制密码给 [${account.realname}]`, + message: `请复制新密码给 [${account.realname}]`, + description: '新密码:' + randomPassword, placement: 'top', duration: 60, }) diff --git a/src/views/account/RoleList.jsx b/src/views/account/RoleList.jsx index 71909f1..463ba60 100644 --- a/src/views/account/RoleList.jsx +++ b/src/views/account/RoleList.jsx @@ -69,6 +69,7 @@ function RoleList() { ['domestic', '国内供应商'], ['air-ticket', '机票供应商'], ['products', '产品价格'], + ['page', '默认页面'], ]); const permissionTree = [] From ce60237b3da40cc3e602dbc515880b2f14189faa Mon Sep 17 00:00:00 2001 From: Jimmy Liow Date: Thu, 27 Jun 2024 15:10:58 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E7=BB=9F=E4=B8=80=E7=99=BB?= =?UTF-8?q?=E9=99=86=E5=92=8C=E5=88=B7=E6=96=B0=E9=A1=B5=E9=9D=A2=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/价格管理平台.bmpr | Bin 276480 -> 278528 bytes src/main.jsx | 8 +-- src/stores/Auth.js | 89 +++++++++++++++---------------- src/utils/lifecycle.js | 4 +- src/views/App.jsx | 6 +-- src/views/Login.jsx | 6 +-- src/views/account/Management.jsx | 2 +- 7 files changed, 55 insertions(+), 60 deletions(-) diff --git a/doc/价格管理平台.bmpr b/doc/价格管理平台.bmpr index eb1a8d6ac17c14b1cafbeb9b654b686874de0f72..de2695d7d545f1645b1e5f1eacb453cdb73240fa 100644 GIT binary patch delta 15938 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 E0F~~jWdHyG delta 437 zcmW-cODL>i7=Yhf%`D6`q!Y0~7OtJ?c|N;{7e2?|HxX^7Ve%Y z26~|nVs>^yKXgDl#J_I|i;|GGT*jha%O&C0cQp+u>+7@rxH)093?!Y?Knq5TkAdbJ z+;bySxT@=aXpxI_=Tu;PCI6)PCtR4t1C?6-WX!2l`QB8i^zEobd~d0g`QBCunw2LB zMMW%5+vX55c8t8SZEP8iv1#Ot1LM%RG_H(m!#nThTQ2Y%1##aU8K;nVA>& zeS#`9F-#9s;D0oPS1R-V?6t}ynLU+dv^PBZ{E4p@ZOn7{;?_qg?S@;pPXBK3@pOSb K66F$q4156+&U7*W diff --git a/src/main.jsx b/src/main.jsx index 054b935..7946e02 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -39,16 +39,12 @@ import { PERM_ACCOUNT_MANAGEMENT, PERM_ROLE_NEW, PERM_OVERSEA, PERM_AIR_TICKET } import './i18n'; -const { loginToken, userId } = usingStorage() const initAppliction = async () => { - if (isNotEmpty(loginToken)) { - appendRequestParams('token', loginToken) - appendRequestParams('lmi_sn', userId) - } + const { loginToken, userId } = usingStorage() - if (isNotEmpty(userId)) { + if (isNotEmpty(userId) && isNotEmpty(loginToken)) { await fireAuth() } } diff --git a/src/stores/Auth.js b/src/stores/Auth.js index 41b8899..a2b9a42 100644 --- a/src/stores/Auth.js +++ b/src/stores/Auth.js @@ -3,14 +3,12 @@ import { appendRequestParams, fetchJSON, postForm } from '@/utils/request' import { HT_HOST } from "@/config" import { loadPageSpy } from '@/pageSpy' import { usingStorage } from '@/hooks/usingStorage' -import { devtools } from 'zustand/middleware' import { obervseLifecycle } from '@/utils/lifecycle' const KEY_LOGIN_TOKEN = 'G-STR:LOGIN_TOKEN' const KEY_TRAVEL_AGENCY_ID = 'G-INT:TRAVEL_AGENCY_ID' const KEY_USER_ID = 'G-INT:USER_ID' -const KEY_USER_DETAIL = 'G-JSON:USER_DETAIL' const WILDCARD_TOKEN = '*' @@ -47,62 +45,33 @@ async function fetchLastRequet() { const useAuthStore = create(obervseLifecycle((set, get) => ({ - onAuth: () => { + onAuth: async () => { const { startTokenInterval, loadUserPermission } = get() - const { userId } = usingStorage() - loadUserPermission(userId) - startTokenInterval() - }, - - tokenInterval: null, + const { userId, loginToken } = usingStorage() - tokenTimeout: false, - - loginStatus: 0, - - defaltRoute: '', - - permissionList: [], - - isPermitted: (perm) => { - const { permissionList } = get() - // 测试权限使用: - // if (perm === '/account/management') return false - // if (perm === '/account/role/new') return false - // return true - // 以上是 Hardcode 判断 - // 以下是权限列表从数据库读取后使用的方法 - return permissionList.some((value) => { - if (value.indexOf(WILDCARD_TOKEN) == 0) { - return true - } - if (value === perm) { - return true - } - return false - }) + appendRequestParams('token', loginToken) + appendRequestParams('lmi_sn', userId) + await loadUserPermission(userId) + startTokenInterval() }, - validateUserPassword: async (usr, pwd) => { - const { startTokenInterval, loadUserPermission } = get() + authenticate: async (usr, pwd) => { + const { onAuth } = get() const { setStorage } = usingStorage() const { token: loginToken } = await fetchLoginToken(usr, pwd) const userDetail = await fetchUserDetail(loginToken) - await loadUserPermission(userDetail.LMI_SN) + + setStorage(KEY_LOGIN_TOKEN, loginToken) + setStorage(KEY_USER_ID, userDetail.LMI_SN) + setStorage(KEY_TRAVEL_AGENCY_ID, userDetail.LMI_VEI_SN) + + await onAuth() set(() => ({ tokenTimeout: false, loginStatus: 302 })) - - setStorage(KEY_LOGIN_TOKEN, loginToken) - setStorage(KEY_USER_ID, userDetail.LMI_SN) - setStorage(KEY_TRAVEL_AGENCY_ID, userDetail.LMI_VEI_SN) - appendRequestParams('token', loginToken) - appendRequestParams('lmi_sn', userDetail.LMI_SN) - // loadPageSpy(`${json.Result.VName}-${json.Result.LoginName}`) - startTokenInterval() }, loadUserPermission: async(userId) => { @@ -130,6 +99,7 @@ const useAuthStore = create(obervseLifecycle((set, get) => ({ clearStorage() clearInterval(tokenInterval) set(() => ({ + defaultRoute: '/', loginStatus: 0, tokenInterval: null, tokenTimeout: true @@ -184,6 +154,35 @@ const useAuthStore = create(obervseLifecycle((set, get) => ({ }); }, + isPermitted: (perm) => { + const { permissionList } = get() + // 测试权限使用: + // if (perm === '/account/management') return false + // if (perm === '/account/role/new') return false + // return true + // 以上是 Hardcode 判断 + // 以下是权限列表从数据库读取后使用的方法 + return permissionList.some((value) => { + if (value.indexOf(WILDCARD_TOKEN) == 0) { + return true + } + if (value === perm) { + return true + } + return false + }) + }, + + tokenInterval: null, + + tokenTimeout: false, + + loginStatus: 0, + + defaltRoute: '', + + permissionList: [], + }))) export default useAuthStore \ No newline at end of file diff --git a/src/utils/lifecycle.js b/src/utils/lifecycle.js index a2de514..1e71d68 100644 --- a/src/utils/lifecycle.js +++ b/src/utils/lifecycle.js @@ -15,8 +15,8 @@ export const fireInit = async () => { }) } -export const fireAuth = (obj) => { - authListener.forEach(fn => fn(obj)) +export const fireAuth = async (obj) => { + authListener.forEach(async (fn) => await fn(obj)) } // Zustand 中间件,用于订阅前端应用的生命周期,实验阶段 diff --git a/src/views/App.jsx b/src/views/App.jsx index 516c02d..22b385d 100644 --- a/src/views/App.jsx +++ b/src/views/App.jsx @@ -29,8 +29,8 @@ function App() { const [password, setPassword] = useState('') const [userDetail, setUserDetail] = useState({}) - const [validateUserPassword, tokenTimeout, isPermitted] = useAuthStore( - (state) => [state.validateUserPassword, state.tokenTimeout, state.isPermitted]) + const [authenticate, tokenTimeout, isPermitted] = useAuthStore( + (state) => [state.authenticate, state.tokenTimeout, state.isPermitted]) const { loginToken } = usingStorage() @@ -61,7 +61,7 @@ function App() { }, [href]) const onSubmit = () => { - validateUserPassword(userDetail?.username, password) + authenticate(userDetail?.username, password) .catch(ex => { console.error(ex) alert(t('Validation.LoginFailed')) diff --git a/src/views/Login.jsx b/src/views/Login.jsx index a994d2d..1ce2cc3 100644 --- a/src/views/Login.jsx +++ b/src/views/Login.jsx @@ -6,8 +6,8 @@ import useAuthStore from '@/stores/Auth' import useNoticeStore from '@/stores/Notice' function Login() { - const [validateUserPassword, loginStatus, defaultRoute] = - useAuthStore((state) => [state.validateUserPassword, state.loginStatus, state.defaultRoute]) + const [authenticate, loginStatus, defaultRoute] = + useAuthStore((state) => [state.authenticate, state.loginStatus, state.defaultRoute]) const { t, i18n } = useTranslation() const { notification } = App.useApp() @@ -21,7 +21,7 @@ function Login() { }, [loginStatus]) const onFinish = (values) => { - validateUserPassword(values.username, values.password) + authenticate(values.username, values.password) .catch(ex => { console.error(ex) notification.error({ diff --git a/src/views/account/Management.jsx b/src/views/account/Management.jsx index 9fd988c..92357b1 100644 --- a/src/views/account/Management.jsx +++ b/src/views/account/Management.jsx @@ -41,7 +41,7 @@ function Management() { render: (text) => (isEmpty(text) ? '' : dayjs(text).format('YYYY-MM-DD HH:mm:ss')) }, { - title: t('account:action.edit'), + title: t('account:action'), dataIndex: 'account:action', render: actionRender }, From 9304c4735d0f9cdb34152f66adfa3415d8c2e856 Mon Sep 17 00:00:00 2001 From: Jimmy Liow Date: Fri, 28 Jun 2024 09:06:35 +0800 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E4=BD=BF=E7=94=A8=E7=A9=BA=E5=AF=B9=E8=B1=A1=EF=BC=9B?= =?UTF-8?q?=E9=87=8D=E5=91=BD=E5=90=8D=20lifecycleware?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.jsx | 5 ++--- src/stores/Account.js | 16 ++++++++++++++-- src/stores/Auth.js | 5 +++-- src/utils/lifecycle.js | 14 +++++++------- src/views/account/Management.jsx | 12 +++++++++--- src/views/account/RoleList.jsx | 8 ++++---- 6 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/main.jsx b/src/main.jsx index 7946e02..3733210 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -32,8 +32,7 @@ import AirticketPlan from "@/views/airticket/Plan"; import { ThemeContext } from '@/stores/ThemeContext' import { usingStorage } from '@/hooks/usingStorage' import { isNotEmpty } from '@/utils/commons' -import { appendRequestParams } from '@/utils/request' -import { fireAuth } from "./utils/lifecycle" +import { notifyAuth } from "./utils/lifecycle" import { PERM_ACCOUNT_MANAGEMENT, PERM_ROLE_NEW, PERM_OVERSEA, PERM_AIR_TICKET } from '@/config' @@ -45,7 +44,7 @@ const initAppliction = async () => { const { loginToken, userId } = usingStorage() if (isNotEmpty(userId) && isNotEmpty(loginToken)) { - await fireAuth() + await notifyAuth() } } diff --git a/src/stores/Account.js b/src/stores/Account.js index ee3daca..a57b6db 100644 --- a/src/stores/Account.js +++ b/src/stores/Account.js @@ -90,7 +90,7 @@ const useAccountStore = create((set, get) => ({ return postAccountPassword(formData) }, - newRole: () => { + newEmptyRole: () => { return { role_id: null, role_name: '', @@ -98,6 +98,19 @@ const useAccountStore = create((set, get) => ({ } }, + newEmptyAccount: () => { + return { + accountId: null, + userId: null, + lmi2_sn: null, + username: '', + realname: '', + email: '', + travelAgencyId: null, + roleId: '' + } + }, + saveOrUpdateRole: async (formValues) => { const formData = new FormData() formData.append('role_id', formValues.role_id) @@ -116,7 +129,6 @@ const useAccountStore = create((set, get) => ({ formData.append('user_name', formValues.username) formData.append('real_name', formValues.realname) formData.append('email', formValues.email) - formData.append('travel_agency_id', formValues.travelAgencyId) formData.append('roles', formValues.roleId) diff --git a/src/stores/Auth.js b/src/stores/Auth.js index a2b9a42..56e7a80 100644 --- a/src/stores/Auth.js +++ b/src/stores/Auth.js @@ -4,7 +4,7 @@ import { HT_HOST } from "@/config" import { loadPageSpy } from '@/pageSpy' import { usingStorage } from '@/hooks/usingStorage' -import { obervseLifecycle } from '@/utils/lifecycle' +import { lifecycleware } from '@/utils/lifecycle' const KEY_LOGIN_TOKEN = 'G-STR:LOGIN_TOKEN' const KEY_TRAVEL_AGENCY_ID = 'G-INT:TRAVEL_AGENCY_ID' @@ -43,7 +43,7 @@ async function fetchLastRequet() { return errcode !== 0 ? {} : result } -const useAuthStore = create(obervseLifecycle((set, get) => ({ +const useAuthStore = create(lifecycleware((set, get) => ({ onAuth: async () => { const { startTokenInterval, loadUserPermission } = get() @@ -136,6 +136,7 @@ const useAuthStore = create(obervseLifecycle((set, get) => ({ })) }, + // 迁移到 Account.js changeUserPassword: (password, newPassword) => { const { userId } = usingStorage() const formData = new FormData(); diff --git a/src/utils/lifecycle.js b/src/utils/lifecycle.js index 1e71d68..0d3deb1 100644 --- a/src/utils/lifecycle.js +++ b/src/utils/lifecycle.js @@ -1,28 +1,28 @@ const initListener = [] const authListener = [] -export const onInit = (fn) => { +export const addInitLinstener = (fn) => { initListener.push(fn) } -export const onAuth = (fn) => { +export const addAuthLinstener = (fn) => { authListener.push(fn) } -export const fireInit = async () => { +export const notifyInit = async () => { initListener.forEach(async (fn) => { await fn() }) } -export const fireAuth = async (obj) => { +export const notifyAuth = async (obj) => { authListener.forEach(async (fn) => await fn(obj)) } // Zustand 中间件,用于订阅前端应用的生命周期,实验阶段 -export const obervseLifecycle = (fn) => (set, get, store) => { +export const lifecycleware = (fn) => (set, get, store) => { - onInit(() => { + addInitLinstener(() => { if (store.getState().hasOwnProperty('onInit')) { store.getState().onInit() } else { @@ -30,7 +30,7 @@ export const obervseLifecycle = (fn) => (set, get, store) => { } }) - onAuth(() => { + addAuthLinstener(() => { if (store.getState().hasOwnProperty('onAuth')) { store.getState().onAuth() } else { diff --git a/src/views/account/Management.jsx b/src/views/account/Management.jsx index 92357b1..94d1a30 100644 --- a/src/views/account/Management.jsx +++ b/src/views/account/Management.jsx @@ -71,9 +71,9 @@ function Management() { const [currentTravelAgency, setCurrentTravelAgency] = useState(null) const [accountForm] = Form.useForm() - const [searchAccountByCriteria, accountList, toggleAccountStatus, saveOrUpdateAccount, resetAccountPassword] = + const [searchAccountByCriteria, accountList, toggleAccountStatus, saveOrUpdateAccount, resetAccountPassword, newEmptyAccount] = useAccountStore((state) => - [state.searchAccountByCriteria, state.accountList, state.toggleAccountStatus, state.saveOrUpdateAccount, state.resetAccountPassword]) + [state.searchAccountByCriteria, state.accountList, state.toggleAccountStatus, state.saveOrUpdateAccount, state.resetAccountPassword, state.newEmptyAccount]) const formValues = useFormStore(state => state.formValues) const { notification, modal } = App.useApp() @@ -117,6 +117,12 @@ function Management() { setAccountModalOpen(true) } + const onNewAccount = () => { + const emptyAccount = newEmptyAccount() + accountForm.setFieldsValue(emptyAccount) + setAccountModalOpen(true) + } + const onAccountFinish = (values) => { saveOrUpdateAccount(values) .then(() => { @@ -331,7 +337,7 @@ function Management() { - + diff --git a/src/views/account/RoleList.jsx b/src/views/account/RoleList.jsx index 463ba60..b4f9f92 100644 --- a/src/views/account/RoleList.jsx +++ b/src/views/account/RoleList.jsx @@ -108,11 +108,11 @@ function RoleList() { const [roleAllList, setRoleAllList] = useState([]) const [roleForm] = Form.useForm() - const [saveOrUpdateRole, newRole] = + const [saveOrUpdateRole, newEmptyRole] = useAccountStore((state) => - [state.saveOrUpdateRole, state.newRole]) + [state.saveOrUpdateRole, state.newEmptyRole]) - const { notification, modal } = App.useApp() + const { notification } = App.useApp() const onRoleSeleted = (role) => { fetchPermissionListByRoleId({ role_id: role.role_id }) @@ -124,7 +124,7 @@ function RoleList() { } const onNewRole = () => { - const role = newRole() + const role = newEmptyRole() roleForm.setFieldsValue(role) setRoleModalOpen(true) }