feat: add user permission manage
This commit is contained in:
parent
1411360614
commit
8590d577bd
@ -160,6 +160,10 @@
|
||||
"total_amount": "Total Amount HKD"
|
||||
},
|
||||
"permission": {
|
||||
"message": {
|
||||
"empty_tips": "No data, please create new",
|
||||
"error_require": "Please set your username or role"
|
||||
},
|
||||
"title": {
|
||||
"add": "New",
|
||||
"bill.btn.check": "Reconciliation button",
|
||||
|
@ -160,6 +160,10 @@
|
||||
"total_amount": "应付总金额"
|
||||
},
|
||||
"permission": {
|
||||
"message": {
|
||||
"empty_tips": "暂无数据,请新增",
|
||||
"error_require": "请设置用户名或角色"
|
||||
},
|
||||
"title": {
|
||||
"add": "新增",
|
||||
"bill.btn.check": "对账按钮",
|
||||
|
@ -160,6 +160,10 @@
|
||||
"total_amount": "應付總金額"
|
||||
},
|
||||
"permission": {
|
||||
"message": {
|
||||
"empty_tips": "暫無數據,請新增",
|
||||
"error_require": "請設定使用者名稱或角色"
|
||||
},
|
||||
"title": {
|
||||
"add": "新增",
|
||||
"bill.btn.check": "對帳按鈕",
|
||||
|
@ -1,29 +1,124 @@
|
||||
import {useSetState} from "ahooks";
|
||||
import {Button, Checkbox, Popconfirm, Select, Space} from "@douyinfe/semi-ui";
|
||||
import {Button, Checkbox, Empty, Popconfirm, Select, Space, Toast} from "@douyinfe/semi-ui";
|
||||
import {useTranslation} from "react-i18next";
|
||||
import {useMemo} from "react";
|
||||
import {useEffect, useMemo} from "react";
|
||||
|
||||
import './permission.less';
|
||||
|
||||
import {Card} from "@/components/card";
|
||||
import {useRemoteUserList} from "@/hooks/useRemoteUserList.ts";
|
||||
import {getUserPermissionList, removeUserPermission} from "@/service/api/user.ts";
|
||||
|
||||
|
||||
const PermissionList = [
|
||||
'manual',
|
||||
'bill.query',
|
||||
'bill.check',
|
||||
'bill.pay',
|
||||
'bill.btn.confirm',
|
||||
'bill.btn.check',
|
||||
'permission'
|
||||
]
|
||||
// const PermissionList = [
|
||||
// 'manual',
|
||||
// 'bill.query',
|
||||
// 'bill.check',
|
||||
// 'bill.pay',
|
||||
// 'bill.btn.confirm',
|
||||
// 'bill.btn.check',
|
||||
// 'permission'
|
||||
// ]
|
||||
|
||||
type UserPermissionType = {
|
||||
id: number;
|
||||
username: string;
|
||||
role: string;
|
||||
permissions: string[];
|
||||
|
||||
const UserPermissionItem = ({it, onChange, usernameOptionList}: {
|
||||
it: UserPermission;
|
||||
onChange: (action:'remove'|'saved'|'modify',value:UserPermission) => void;
|
||||
usernameOptionList: OptionValue[]
|
||||
}) => {
|
||||
const {t} = useTranslation()
|
||||
const onValueChange = (value: {
|
||||
[key:string]:string|boolean
|
||||
})=>{
|
||||
onChange('modify',{
|
||||
...it,
|
||||
...value
|
||||
})
|
||||
}
|
||||
const onSave = ()=>{
|
||||
console.log(it)
|
||||
if(!it.role || !it.username){
|
||||
Toast.warning(t('permission.message.error_require'))
|
||||
return;
|
||||
}
|
||||
}
|
||||
// const [values,setValues] = use
|
||||
return (<div className="table-row">
|
||||
<div className="item item-username item-type">
|
||||
<div className="form-item">
|
||||
{it.id > 0 ? <span>{it.username}</span> : <Select
|
||||
style={{width: '100%'}}
|
||||
filter
|
||||
optionList={usernameOptionList}
|
||||
placeholder={t('base.please_select')}
|
||||
defaultValue={it.username}
|
||||
onChange={(value) => onValueChange({username: String(value)})}
|
||||
/>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="item item-role item-type">
|
||||
<div className="form-item">
|
||||
<Select
|
||||
style={{width: '100%'}}
|
||||
optionList={[
|
||||
{label: 'ROOT', value: 'root'},
|
||||
{label: 'RO', value: 'ro'},
|
||||
{label: 'FO', value: 'fo'},
|
||||
]}
|
||||
defaultValue={it.role}
|
||||
onChange={(value) => onValueChange({role: String(value)})}
|
||||
placeholder={t('base.please_select')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`item item-type item-type-manual`}>
|
||||
<Checkbox
|
||||
defaultChecked={it.manual_payment}
|
||||
onChange={e => onValueChange({manual_payment: !!e.target.checked})}/>
|
||||
</div>
|
||||
<div className={`item item-type item-type-bill-query`}>
|
||||
<Checkbox
|
||||
defaultChecked={it.bill_page}
|
||||
onChange={e => onValueChange({bill_page: !!e.target.checked})}/>
|
||||
</div>
|
||||
<div className={`item item-type item-type-bill-check`}>
|
||||
<Checkbox
|
||||
defaultChecked={it.apply_page}
|
||||
onChange={e => onValueChange({apply_page: !!e.target.checked})}/>
|
||||
</div>
|
||||
<div className={`item item-type item-type-bill-pay`}>
|
||||
<Checkbox
|
||||
defaultChecked={it.complete_button}
|
||||
onChange={e => onValueChange({complete_button: !!e.target.checked})}/>
|
||||
</div>
|
||||
<div className={`item item-type item-type-bill-btn-confirm`}>
|
||||
<Checkbox
|
||||
defaultChecked={it.confirm_button}
|
||||
onChange={e => onValueChange({confirm_button: !!e.target.checked})}/>
|
||||
</div>
|
||||
<div className={`item item-type item-type-bill-btn-check`}>
|
||||
<Checkbox
|
||||
defaultChecked={it.apply_button}
|
||||
onChange={e => onValueChange({apply_button: !!e.target.checked})}/>
|
||||
</div>
|
||||
<div className={`item item-type item-type-bill-permission`}>
|
||||
<Checkbox
|
||||
defaultChecked={it.permission_edit}
|
||||
onChange={e => onValueChange({permission_edit: !!e.target.checked})}/>
|
||||
</div>
|
||||
<div className="item item-operation text-center">
|
||||
<Space>
|
||||
<Popconfirm
|
||||
title={t('base.warning')} onConfirm={() => onChange('remove',it)}
|
||||
position={'topRight'}
|
||||
content={`${t('base.confirm_delete')}?`}
|
||||
>
|
||||
<Button theme={'solid'} type="danger" size={'small'}>{t('base.delete')}</Button>
|
||||
</Popconfirm>
|
||||
<Button size={'small'} theme={'solid'} onClick={onSave}>{t('base.save')}</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</div>)
|
||||
}
|
||||
|
||||
const Permission = () => {
|
||||
@ -31,44 +126,83 @@ const Permission = () => {
|
||||
const usernameList = useRemoteUserList();
|
||||
|
||||
const [state, setState] = useSetState<{
|
||||
list: UserPermissionType[];
|
||||
list: UserPermission[];
|
||||
loading?: boolean;
|
||||
}>({
|
||||
list: []
|
||||
})
|
||||
const usernameOptionList = useMemo(() => (usernameList.map(name => ({label: name, value: name}))), [usernameList])
|
||||
|
||||
// load user permission list
|
||||
const loadUserPermissionList = () => {
|
||||
setState({loading: true})
|
||||
getUserPermissionList().then(list => {
|
||||
setState({loading: false, list})
|
||||
})
|
||||
}
|
||||
useEffect(loadUserPermissionList, [])
|
||||
|
||||
// remove a user permission
|
||||
const removeItem = (index: number, id: number) => {
|
||||
const removeFromList = ()=>{
|
||||
const newList = [...state.list];
|
||||
newList.splice(index, 1)
|
||||
setState({list: newList})
|
||||
}
|
||||
if (id > 0) { // 数据库数据 需要调用接口
|
||||
|
||||
removeUserPermission(id).then(removeFromList)
|
||||
return;
|
||||
}
|
||||
const newList = [...state.list];
|
||||
newList.splice(index, 1)
|
||||
setState({list: newList})
|
||||
removeFromList();
|
||||
}
|
||||
|
||||
const onUsernameChange = (index: number, name: string, role: string) => {
|
||||
const newList = [...state.list];
|
||||
newList[index].username = name;
|
||||
newList[index].role = role;
|
||||
setState({list: newList})
|
||||
}
|
||||
const onPermissionChange = (_index: number, name: string, checked?: boolean) => {
|
||||
const newList = [...state.list];
|
||||
const index = newList[_index].permissions.indexOf(name)
|
||||
if (index == -1) { // 没有找到
|
||||
if (checked) { // 已经选中
|
||||
newList[index].permissions.push(name)
|
||||
}
|
||||
} else {
|
||||
if (!checked) { // 已经选中
|
||||
newList[index].permissions.splice(index, 1)
|
||||
}
|
||||
}
|
||||
setState({list: newList})
|
||||
}
|
||||
// const onUsernameChange = (index: number, name: string, role: string) => {
|
||||
// const newList = [...state.list];
|
||||
// newList[index].username = name;
|
||||
// newList[index].role = role;
|
||||
// setState({list: newList})
|
||||
// }
|
||||
// const onPermissionChange = (_index: number, name: string, checked?: boolean) => {
|
||||
// const newList = [...state.list];
|
||||
// const index = newList[_index].permissions.indexOf(name)
|
||||
// if (index == -1) { // 没有找到
|
||||
// if (checked) { // 已经选中
|
||||
// newList[index].permissions.push(name)
|
||||
// }
|
||||
// } else {
|
||||
// if (!checked) { // 已经选中
|
||||
// newList[index].permissions.splice(index, 1)
|
||||
// }
|
||||
// }
|
||||
// setState({list: newList})
|
||||
// }
|
||||
|
||||
const addNewRecord = () => {
|
||||
setState({list: [...state.list, {username: '', role: '', permissions: [], id: 0}]})
|
||||
setState({
|
||||
list: [...state.list, {
|
||||
id: 0,
|
||||
username: '',
|
||||
role: '',
|
||||
manual_payment: false,
|
||||
bill_page: false,
|
||||
apply_page: false,
|
||||
complete_button: false,
|
||||
confirm_button: false,
|
||||
apply_button: false,
|
||||
permission_edit: false
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
const handleChange = (action:'remove'|'saved'|'modify',value:UserPermission,index:number) =>{
|
||||
if(action == 'remove') removeItem(index,value.id)
|
||||
else if(action == 'modify'){
|
||||
const newList = [...state.list];
|
||||
newList[index] = {
|
||||
...value
|
||||
}
|
||||
setState({list: newList})
|
||||
}
|
||||
}
|
||||
|
||||
return (<Card style={{marginBottom: 20}}>
|
||||
@ -77,59 +211,27 @@ const Permission = () => {
|
||||
<div className="header table-row">
|
||||
<div className="item item-username item-type">{t('permission.title.username')}</div>
|
||||
<div className="item item-role item-type">{t('permission.title.role')}</div>
|
||||
{PermissionList.map((key, idx) => (
|
||||
<div key={idx}
|
||||
className={`item item-type item-type-${key.replace(/\./g, '-')}`}>{t(`permission.title.${key}`)}</div>
|
||||
))}
|
||||
<div className={`item item-type item-type-manual`}>{t(`permission.title.manual`)}</div>
|
||||
<div className={`item item-type item-type-bill-query`}>{t(`permission.title.bill.query`)}</div>
|
||||
<div className={`item item-type item-type-bill-check`}>{t(`permission.title.bill.check`)}</div>
|
||||
<div className={`item item-type item-type-bill-pay`}>{t(`permission.title.bill.pay`)}</div>
|
||||
<div
|
||||
className={`item item-type item-type-bill-btn-confirm`}>{t(`permission.title.bill.btn.confirm`)}</div>
|
||||
<div
|
||||
className={`item item-type item-type-bill-btn-check`}>{t(`permission.title.bill.btn.check`)}</div>
|
||||
<div className={`item item-type item-type-bill-permission`}>{t(`permission.title.permission`)}</div>
|
||||
<div className="item item-operation">{t('bill.title_operate')}</div>
|
||||
</div>
|
||||
{state.list.map((it, index) => (<div className="table-row" key={index}>
|
||||
<div className="item item-username item-type">
|
||||
<div className="form-item">
|
||||
<Select
|
||||
style={{width: '100%'}}
|
||||
filter
|
||||
optionList={usernameOptionList}
|
||||
placeholder={t('base.please_select')}
|
||||
onChange={(name) => onUsernameChange(index, String(name), it.role)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="item item-role item-type">
|
||||
<div className="form-item">
|
||||
<Select
|
||||
style={{width: '100%'}}
|
||||
optionList={[
|
||||
{label: 'ROOT', value: 'root'},
|
||||
{label: 'RO', value: 'ro'},
|
||||
{label: 'FO', value: 'fo'},
|
||||
]}
|
||||
onChange={(role) => onUsernameChange(index, it.username, String(role))}
|
||||
placeholder={t('base.please_select')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{PermissionList.map((key, idx) => (
|
||||
<div key={idx} className={`item item-type item-type-${key.replace(/\./g, '-')}`}>
|
||||
<Checkbox
|
||||
defaultChecked={it.permissions.includes(key)}
|
||||
onChange={e => onPermissionChange(index, key, e.target.checked)}/>
|
||||
</div>
|
||||
))}
|
||||
<div className="item item-operation text-center">
|
||||
<Space>
|
||||
<Popconfirm
|
||||
title={t('base.warning')} onConfirm={() => removeItem(index, it.id)}
|
||||
position={'topRight'}
|
||||
content={`${t('base.confirm_delete')}?`}
|
||||
>
|
||||
<Button theme={'solid'} type="danger" size={'small'}>{t('base.delete')}</Button>
|
||||
</Popconfirm>
|
||||
<Button size={'small'} theme={'solid'}>{t('base.save')}</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</div>))}
|
||||
{state.list.map((it, index) => (<UserPermissionItem
|
||||
key={index} it={it} onChange={(action,value) => handleChange(action,value,index)}
|
||||
usernameOptionList={usernameOptionList}
|
||||
/>))}
|
||||
{state.list.length == 0 && <div style={{backgroundColor:'#fafafa'}}>
|
||||
<Empty
|
||||
description={t('permission.message.empty_tips')}
|
||||
style={{paddingBottom:20}}
|
||||
/>
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
<Space style={{marginTop: 20}}>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {get,post} from "@/service/request.ts";
|
||||
import {get, post, remove} from "@/service/request.ts";
|
||||
|
||||
export function getUserInfo() {
|
||||
return get<UserProfile>('/userinfo')
|
||||
@ -18,4 +18,18 @@ export function getPermissionList(){
|
||||
}
|
||||
export function savePermissionList(roles:PermissionUserList[]){
|
||||
return post('/roles',{roles})
|
||||
}
|
||||
|
||||
export function getUserPermissionList(){
|
||||
return get<UserPermission[]>('/permissions')
|
||||
}
|
||||
|
||||
export function createUserPermission(){
|
||||
return get<UserPermission[]>('/permissions')
|
||||
}
|
||||
export function updateUserPermission(){
|
||||
return get<UserPermission[]>('/permissions')
|
||||
}
|
||||
export function removeUserPermission(id:number){
|
||||
return remove(`/permissions/${id}`)
|
||||
}
|
@ -72,6 +72,10 @@ export function put<T>(url: string, data: AllType = {}) {
|
||||
return request<T>(url, 'put', data)
|
||||
}
|
||||
|
||||
export function remove<T>(url: string, data: AllType = {}) {
|
||||
return request<T>(url, 'delete', data)
|
||||
}
|
||||
|
||||
type UploadFileModel = {
|
||||
field_name?: string;
|
||||
origin_file: File
|
||||
|
67
src/types/auth.d.ts
vendored
67
src/types/auth.d.ts
vendored
@ -1,40 +1,53 @@
|
||||
declare type UserRole = 'root' | 'ro' | 'fo' | 'staff'
|
||||
|
||||
declare type UserProfile = {
|
||||
id: string | number;
|
||||
token: string;
|
||||
email: string;
|
||||
username: string;
|
||||
department: string;
|
||||
exp: number;
|
||||
expiration_time: string;
|
||||
iat: number;
|
||||
iss: string;
|
||||
nbf: number;
|
||||
type: string;
|
||||
roles: UserRole[];
|
||||
role: UserRole;
|
||||
origin_role?: UserRole;
|
||||
id: string | number;
|
||||
token: string;
|
||||
email: string;
|
||||
username: string;
|
||||
department: string;
|
||||
exp: number;
|
||||
expiration_time: string;
|
||||
iat: number;
|
||||
iss: string;
|
||||
nbf: number;
|
||||
type: string;
|
||||
roles: UserRole[];
|
||||
role: UserRole;
|
||||
origin_role?: UserRole;
|
||||
}
|
||||
|
||||
declare interface AuthProps {
|
||||
isLoggedIn: boolean;
|
||||
isInitialized?: boolean;
|
||||
user?: UserProfile | null;
|
||||
token?: string | null;
|
||||
isLoggedIn: boolean;
|
||||
isInitialized?: boolean;
|
||||
user?: UserProfile | null;
|
||||
token?: string | null;
|
||||
}
|
||||
|
||||
declare type AuthContextType = {
|
||||
isLoggedIn: boolean;
|
||||
isInitialized?: boolean;
|
||||
user?: UserProfile | null | undefined;
|
||||
logout: () => Promise<void>;
|
||||
mockLogin: () => Promise<void>;
|
||||
login: (code:string,state:string) => Promise<void>;
|
||||
updateUser: (user:Partial<UserProfile>) => Promise<void>;
|
||||
isLoggedIn: boolean;
|
||||
isInitialized?: boolean;
|
||||
user?: UserProfile | null | undefined;
|
||||
logout: () => Promise<void>;
|
||||
mockLogin: () => Promise<void>;
|
||||
login: (code: string, state: string) => Promise<void>;
|
||||
updateUser: (user: Partial<UserProfile>) => Promise<void>;
|
||||
};
|
||||
|
||||
declare type PermissionUserList = {
|
||||
role_name:string;
|
||||
username_list: string[];
|
||||
role_name: string;
|
||||
username_list: string[];
|
||||
}
|
||||
|
||||
declare type UserPermission = {
|
||||
id: number;
|
||||
username: string;
|
||||
role: string;
|
||||
manual_payment: boolean;
|
||||
bill_page: boolean;
|
||||
apply_page: boolean;
|
||||
complete_button: boolean;
|
||||
confirm_button: boolean;
|
||||
apply_button: boolean;
|
||||
permission_edit: boolean;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user