feat: add user permission manage

This commit is contained in:
LittleBoy 2024-08-27 22:25:14 +08:00
parent 1411360614
commit 8590d577bd
7 changed files with 266 additions and 121 deletions

View File

@ -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",

View File

@ -160,6 +160,10 @@
"total_amount": "应付总金额"
},
"permission": {
"message": {
"empty_tips": "暂无数据,请新增",
"error_require": "请设置用户名或角色"
},
"title": {
"add": "新增",
"bill.btn.check": "对账按钮",

View File

@ -160,6 +160,10 @@
"total_amount": "應付總金額"
},
"permission": {
"message": {
"empty_tips": "暫無數據,請新增",
"error_require": "請設定使用者名稱或角色"
},
"title": {
"add": "新增",
"bill.btn.check": "對帳按鈕",

View File

@ -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}}>

View File

@ -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}`)
}

View File

@ -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
View File

@ -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;
}