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/doc/价格管理平台.bmpr b/doc/价格管理平台.bmpr
index eb1a8d6..de2695d 100644
Binary files a/doc/价格管理平台.bmpr and b/doc/价格管理平台.bmpr differ
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/main.jsx b/src/main.jsx
index 02f2244..5d1c8cd 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 ProductsManage from '@/views/products/Manage';
import ProductsDetail from '@/views/products/Detail';
@@ -42,19 +41,13 @@ 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)) {
- appendRequestParams('lmi_sn', userId)
- await fireAuth()
+ if (isNotEmpty(userId) && isNotEmpty(loginToken)) {
+ await notifyAuth()
}
}
diff --git a/src/stores/Account.js b/src/stores/Account.js
index 01010d8..a57b6db 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) => {
@@ -91,7 +90,7 @@ const useAccountStore = create((set, get) => ({
return postAccountPassword(formData)
},
- newRole: () => {
+ newEmptyRole: () => {
return {
role_id: null,
role_name: '',
@@ -99,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)
@@ -117,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)
@@ -147,6 +158,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/stores/Auth.js b/src/stores/Auth.js
index 344e147..4160fe3 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'
+import { lifecycleware } 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 = '*'
@@ -45,67 +43,52 @@ async function fetchLastRequet() {
return errcode !== 0 ? {} : result
}
-const useAuthStore = create(obervseLifecycle((set, get) => ({
+const useAuthStore = create(lifecycleware((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,
-
- 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) => {
+ 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)
}))
},
@@ -116,6 +99,7 @@ const useAuthStore = create(obervseLifecycle((set, get) => ({
clearStorage()
clearInterval(tokenInterval)
set(() => ({
+ defaultRoute: '/',
loginStatus: 0,
tokenInterval: null,
tokenTimeout: true
@@ -152,6 +136,7 @@ const useAuthStore = create(obervseLifecycle((set, get) => ({
}))
},
+ // 迁移到 Account.js
changeUserPassword: (password, newPassword) => {
const { userId } = usingStorage()
const formData = new FormData();
@@ -170,6 +155,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
diff --git a/src/utils/lifecycle.js b/src/utils/lifecycle.js
index a2de514..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 = (obj) => {
- authListener.forEach(fn => fn(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/App.jsx b/src/views/App.jsx
index 3541d9b..c3b227d 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 f9c3008..1ce2cc3 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 [authenticate, loginStatus, defaultRoute] =
+ useAuthStore((state) => [state.authenticate, state.loginStatus, state.defaultRoute])
const { t, i18n } = useTranslation()
const { notification } = App.useApp()
@@ -17,12 +16,12 @@ function Login() {
useEffect (() => {
if (loginStatus === 302) {
- navigate('/')
+ navigate(defaultRoute)
}
}, [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 d3e3d2d..d541fe5 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'
@@ -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
},
@@ -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, newEmptyAccount] =
useAccountStore((state) =>
- [state.searchAccountByCriteria, state.accountList, state.disableAccount, 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()
@@ -115,8 +117,13 @@ function Management() {
setAccountModalOpen(true)
}
+ const onNewAccount = () => {
+ const emptyAccount = newEmptyAccount()
+ accountForm.setFieldsValue(emptyAccount)
+ setAccountModalOpen(true)
+ }
+
const onAccountFinish = (values) => {
- console.log(values)
saveOrUpdateAccount(values)
.then(() => {
handelAccountSearch()
@@ -156,13 +163,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() {
},
@@ -170,7 +191,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: ,
@@ -179,8 +200,8 @@ function Management() {
resetAccountPassword(account.userId, randomPassword)
.then(() => {
notification.info({
- message: '新密码:' + randomPassword,
- description: `请复制密码给 [${account.realname}]`,
+ message: `请复制新密码给 [${account.realname}]`,
+ description: '新密码:' + randomPassword,
placement: 'top',
duration: 60,
})
@@ -317,7 +338,7 @@ function Management() {
-
+
diff --git a/src/views/account/RoleList.jsx b/src/views/account/RoleList.jsx
index 71909f1..b4f9f92 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 = []
@@ -107,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 })
@@ -123,7 +124,7 @@ function RoleList() {
}
const onNewRole = () => {
- const role = newRole()
+ const role = newEmptyRole()
roleForm.setFieldsValue(role)
setRoleModalOpen(true)
}