Compare commits
2 Commits
7c84803b36
...
1e0c687a9b
Author | SHA1 | Date | |
---|---|---|---|
1e0c687a9b | |||
2939228cfa |
22
src/components/permission/index.tsx
Normal file
22
src/components/permission/index.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import React from "react";
|
||||||
|
import useAuth from "@/hooks/useAuth.ts";
|
||||||
|
|
||||||
|
type PermissionKeys =
|
||||||
|
'apply_button'
|
||||||
|
| 'apply_page'
|
||||||
|
| 'bill_page'
|
||||||
|
| 'complete_button'
|
||||||
|
| 'confirm_button'
|
||||||
|
| 'manual_payment'
|
||||||
|
| 'permission_edit';
|
||||||
|
|
||||||
|
type PermissionCheckProps = {
|
||||||
|
permission: PermissionKeys ;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
export const PermissionCheck: React.FC<PermissionCheckProps> = ({permission, children}) => {
|
||||||
|
const {user} = useAuth()
|
||||||
|
return <>
|
||||||
|
{user && user.permissions && user.permissions[permission] ? children : null}
|
||||||
|
</>
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import {useEffect, useState} from "react";
|
import {useRequest} from "ahooks";
|
||||||
|
|
||||||
function getRemoteUserNameList() {
|
function getRemoteUserNameList() {
|
||||||
return new Promise<string[][]>((resolve, reject) => {
|
return new Promise<string[][]>((resolve, reject) => {
|
||||||
@ -23,13 +23,18 @@ function getRemoteUserNameList() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useRemoteUserList() {
|
export function useRemoteUserList() {
|
||||||
|
const loadUserList = async () => {
|
||||||
|
const list = await getRemoteUserNameList();
|
||||||
|
return list.flat();
|
||||||
|
}
|
||||||
|
const {data:usernameList} = useRequest(loadUserList)
|
||||||
|
// const [userList, setUserList] = useState<string[]>([])
|
||||||
|
//const [usernameList, setUserList] = useState<string[]>([])
|
||||||
|
|
||||||
const [usernameList, setUserList] = useState<string[]>([])
|
// useEffect(()=>{
|
||||||
|
// getRemoteUserNameList().then(data=>{
|
||||||
useEffect(()=>{
|
// setUserList(data.flat())
|
||||||
getRemoteUserNameList().then(data=>{
|
// })
|
||||||
setUserList(data.flat())
|
// },[])
|
||||||
})
|
|
||||||
},[])
|
|
||||||
return usernameList
|
return usernameList
|
||||||
}
|
}
|
@ -21,6 +21,8 @@
|
|||||||
"query_bill": "Failed to query bill:",
|
"query_bill": "Failed to query bill:",
|
||||||
"remove": "Remove",
|
"remove": "Remove",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
|
"save_failed": "Save failed",
|
||||||
|
"save_success": "Saved successfully",
|
||||||
"select_excel_file": "Select File",
|
"select_excel_file": "Select File",
|
||||||
"select_upload_file": "Select File",
|
"select_upload_file": "Select File",
|
||||||
"student_number": "Student Number",
|
"student_number": "Student Number",
|
||||||
@ -162,7 +164,8 @@
|
|||||||
"permission": {
|
"permission": {
|
||||||
"message": {
|
"message": {
|
||||||
"empty_tips": "No data, please create new",
|
"empty_tips": "No data, please create new",
|
||||||
"error_require": "Please set your username or role"
|
"error_require": "Please set your username or role",
|
||||||
|
"username_exist": "Username already exists"
|
||||||
},
|
},
|
||||||
"title": {
|
"title": {
|
||||||
"add": "New",
|
"add": "New",
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
"query_bill": "查询账单失败:",
|
"query_bill": "查询账单失败:",
|
||||||
"remove": "删除",
|
"remove": "删除",
|
||||||
"save": "保存",
|
"save": "保存",
|
||||||
|
"save_failed": "保存失败",
|
||||||
|
"save_success": "保存成功",
|
||||||
"select_excel_file": "选择文件",
|
"select_excel_file": "选择文件",
|
||||||
"select_upload_file": "选择文件",
|
"select_upload_file": "选择文件",
|
||||||
"student_number": "学号",
|
"student_number": "学号",
|
||||||
@ -162,7 +164,8 @@
|
|||||||
"permission": {
|
"permission": {
|
||||||
"message": {
|
"message": {
|
||||||
"empty_tips": "暂无数据,请新增",
|
"empty_tips": "暂无数据,请新增",
|
||||||
"error_require": "请设置用户名或角色"
|
"error_require": "请设置用户名或角色",
|
||||||
|
"username_exist": "用户名已经存在了"
|
||||||
},
|
},
|
||||||
"title": {
|
"title": {
|
||||||
"add": "新增",
|
"add": "新增",
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
"query_bill": "查詢帳單失敗:",
|
"query_bill": "查詢帳單失敗:",
|
||||||
"remove": "刪除",
|
"remove": "刪除",
|
||||||
"save": "儲存",
|
"save": "儲存",
|
||||||
|
"save_failed": "保存失敗",
|
||||||
|
"save_success": "保存成功",
|
||||||
"select_excel_file": "選擇文件",
|
"select_excel_file": "選擇文件",
|
||||||
"select_upload_file": "選擇文件",
|
"select_upload_file": "選擇文件",
|
||||||
"student_number": "學號",
|
"student_number": "學號",
|
||||||
@ -162,7 +164,8 @@
|
|||||||
"permission": {
|
"permission": {
|
||||||
"message": {
|
"message": {
|
||||||
"empty_tips": "暫無數據,請新增",
|
"empty_tips": "暫無數據,請新增",
|
||||||
"error_require": "請設定使用者名稱或角色"
|
"error_require": "請設定使用者名稱或角色",
|
||||||
|
"username_exist": "使用者名稱已經存在了"
|
||||||
},
|
},
|
||||||
"title": {
|
"title": {
|
||||||
"add": "新增",
|
"add": "新增",
|
||||||
|
@ -3,11 +3,16 @@ import {Button, Checkbox, Empty, Popconfirm, Select, Space, Toast} from "@douyin
|
|||||||
import {useTranslation} from "react-i18next";
|
import {useTranslation} from "react-i18next";
|
||||||
import {useEffect, useMemo} from "react";
|
import {useEffect, useMemo} from "react";
|
||||||
|
|
||||||
import './permission.less';
|
|
||||||
|
|
||||||
import {Card} from "@/components/card";
|
import {Card} from "@/components/card";
|
||||||
|
import './permission.less';
|
||||||
import {useRemoteUserList} from "@/hooks/useRemoteUserList.ts";
|
import {useRemoteUserList} from "@/hooks/useRemoteUserList.ts";
|
||||||
import {getUserPermissionList, removeUserPermission} from "@/service/api/user.ts";
|
import {
|
||||||
|
createUserPermission,
|
||||||
|
getUserPermissionList,
|
||||||
|
removeUserPermission,
|
||||||
|
updateUserPermission
|
||||||
|
} from "@/service/api/user.ts";
|
||||||
|
import useAuth from "@/hooks/useAuth.ts";
|
||||||
|
|
||||||
|
|
||||||
// const PermissionList = [
|
// const PermissionList = [
|
||||||
@ -20,28 +25,49 @@ import {getUserPermissionList, removeUserPermission} from "@/service/api/user.ts
|
|||||||
// 'permission'
|
// 'permission'
|
||||||
// ]
|
// ]
|
||||||
|
|
||||||
|
type UserPermissionItemProps = {
|
||||||
const UserPermissionItem = ({it, onChange, usernameOptionList}: {
|
|
||||||
it: UserPermission;
|
it: UserPermission;
|
||||||
onChange: (action:'remove'|'saved'|'modify',value:UserPermission) => void;
|
onChange: (action: 'remove' | 'saved' | 'modify', value: UserPermission) => Promise<void>;
|
||||||
usernameOptionList: OptionValue[]
|
usernameOptionList: OptionValue[]
|
||||||
}) => {
|
roleOptionList: OptionValue[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const UserPermissionItem = ({it, onChange, usernameOptionList,roleOptionList}: UserPermissionItemProps) => {
|
||||||
const {t} = useTranslation()
|
const {t} = useTranslation()
|
||||||
|
const {user} = useAuth()
|
||||||
|
const [state,setState] = useSetState({
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
const onValueChange = (value: {
|
const onValueChange = (value: {
|
||||||
[key:string]:string|boolean
|
[key: string]: string | boolean
|
||||||
})=>{
|
}) => {
|
||||||
onChange('modify',{
|
onChange('modify', {
|
||||||
...it,
|
...it,
|
||||||
...value
|
...value
|
||||||
})
|
}).then();
|
||||||
}
|
}
|
||||||
const onSave = ()=>{
|
const onRemove = () => {
|
||||||
|
if(it.id > 0){
|
||||||
|
setState({loading: true})
|
||||||
|
onChange('remove', it).finally(()=>{
|
||||||
|
setState({loading: false})
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onChange('remove', it).then();
|
||||||
|
}
|
||||||
|
const onSave = () => {
|
||||||
console.log(it)
|
console.log(it)
|
||||||
if(!it.role || !it.username){
|
if (!it.role || !it.username) {
|
||||||
Toast.warning(t('permission.message.error_require'))
|
Toast.warning(t('permission.message.error_require'))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setState({loading: true})
|
||||||
|
onChange('saved', it).finally(()=>{
|
||||||
|
setState({loading: false})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// const [values,setValues] = use
|
// const [values,setValues] = use
|
||||||
return (<div className="table-row">
|
return (<div className="table-row">
|
||||||
<div className="item item-username item-type">
|
<div className="item item-username item-type">
|
||||||
@ -60,11 +86,7 @@ const UserPermissionItem = ({it, onChange, usernameOptionList}: {
|
|||||||
<div className="form-item">
|
<div className="form-item">
|
||||||
<Select
|
<Select
|
||||||
style={{width: '100%'}}
|
style={{width: '100%'}}
|
||||||
optionList={[
|
optionList={roleOptionList}
|
||||||
{label: 'ROOT', value: 'root'},
|
|
||||||
{label: 'RO', value: 'ro'},
|
|
||||||
{label: 'FO', value: 'fo'},
|
|
||||||
]}
|
|
||||||
defaultValue={it.role}
|
defaultValue={it.role}
|
||||||
onChange={(value) => onValueChange({role: String(value)})}
|
onChange={(value) => onValueChange({role: String(value)})}
|
||||||
placeholder={t('base.please_select')}
|
placeholder={t('base.please_select')}
|
||||||
@ -104,18 +126,20 @@ const UserPermissionItem = ({it, onChange, usernameOptionList}: {
|
|||||||
<div className={`item item-type item-type-bill-permission`}>
|
<div className={`item item-type item-type-bill-permission`}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
defaultChecked={it.permission_edit}
|
defaultChecked={it.permission_edit}
|
||||||
|
disabled={user?.username == it.username}
|
||||||
onChange={e => onValueChange({permission_edit: !!e.target.checked})}/>
|
onChange={e => onValueChange({permission_edit: !!e.target.checked})}/>
|
||||||
</div>
|
</div>
|
||||||
<div className="item item-operation text-center">
|
<div className="item item-operation text-center">
|
||||||
<Space>
|
<Space>
|
||||||
<Popconfirm
|
{user?.username != it.username && <Popconfirm
|
||||||
title={t('base.warning')} onConfirm={() => onChange('remove',it)}
|
disabled={state.loading}
|
||||||
|
title={t('base.warning')} onConfirm={onRemove}
|
||||||
position={'topRight'}
|
position={'topRight'}
|
||||||
content={`${t('base.confirm_delete')}?`}
|
content={`${t('base.confirm_delete')}?`}
|
||||||
>
|
>
|
||||||
<Button theme={'solid'} type="danger" size={'small'}>{t('base.delete')}</Button>
|
<Button theme={'solid'} type="danger" size={'small'}>{t('base.delete')}</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>}
|
||||||
<Button size={'small'} theme={'solid'} onClick={onSave}>{t('base.save')}</Button>
|
<Button loading={state.loading} size={'small'} theme={'solid'} onClick={onSave}>{t('base.save')}</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
</div>)
|
</div>)
|
||||||
@ -124,19 +148,47 @@ const UserPermissionItem = ({it, onChange, usernameOptionList}: {
|
|||||||
const Permission = () => {
|
const Permission = () => {
|
||||||
const {t} = useTranslation()
|
const {t} = useTranslation()
|
||||||
const usernameList = useRemoteUserList();
|
const usernameList = useRemoteUserList();
|
||||||
|
const {user} = useAuth();
|
||||||
|
|
||||||
const [state, setState] = useSetState<{
|
const [state, setState] = useSetState<{
|
||||||
list: UserPermission[];
|
list: UserPermission[];
|
||||||
|
allList: UserPermission[];
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
}>({
|
}>({
|
||||||
list: []
|
list: [],
|
||||||
|
allList:[]
|
||||||
})
|
})
|
||||||
const usernameOptionList = useMemo(() => (usernameList.map(name => ({label: name, value: name}))), [usernameList])
|
|
||||||
|
const usernameOptionList = useMemo(() => (usernameList?usernameList.map(name => ({label: name, value: name})):[]), [usernameList])
|
||||||
|
const roleOptionList = useMemo(() => {
|
||||||
|
const list = [
|
||||||
|
{label: 'ROOT', value: 'root'},
|
||||||
|
{label: 'RO', value: 'ro'},
|
||||||
|
{label: 'FO', value: 'fo'},
|
||||||
|
];
|
||||||
|
const userRole = user?.permissions?.role ?? 'staff';
|
||||||
|
if(userRole == 'root') return list;
|
||||||
|
return list//.filter(it => (it.value == userRole));
|
||||||
|
}, [])
|
||||||
|
|
||||||
// load user permission list
|
// load user permission list
|
||||||
const loadUserPermissionList = () => {
|
const loadUserPermissionList = () => {
|
||||||
setState({loading: true})
|
setState({loading: true})
|
||||||
getUserPermissionList().then(list => {
|
getUserPermissionList().then(list => {
|
||||||
|
setState({allList:[...list]})
|
||||||
|
const userRole = user?.permissions?.role ?? 'staff';
|
||||||
|
list.forEach(it=>{
|
||||||
|
it.permission_edit = !!it.permission_edit
|
||||||
|
it.bill_page = !!it.bill_page
|
||||||
|
it.apply_page = !!it.apply_page
|
||||||
|
it.complete_button = !!it.complete_button
|
||||||
|
it.confirm_button = !!it.confirm_button
|
||||||
|
it.apply_button = !!it.apply_button
|
||||||
|
it.manual_payment = !!it.manual_payment
|
||||||
|
})
|
||||||
|
if(userRole != 'root') {
|
||||||
|
list = list.filter(it => (it.role == userRole));
|
||||||
|
}
|
||||||
setState({loading: false, list})
|
setState({loading: false, list})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -144,7 +196,7 @@ const Permission = () => {
|
|||||||
|
|
||||||
// remove a user permission
|
// remove a user permission
|
||||||
const removeItem = (index: number, id: number) => {
|
const removeItem = (index: number, id: number) => {
|
||||||
const removeFromList = ()=>{
|
const removeFromList = () => {
|
||||||
const newList = [...state.list];
|
const newList = [...state.list];
|
||||||
newList.splice(index, 1)
|
newList.splice(index, 1)
|
||||||
setState({list: newList})
|
setState({list: newList})
|
||||||
@ -155,28 +207,6 @@ const Permission = () => {
|
|||||||
}
|
}
|
||||||
removeFromList();
|
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 addNewRecord = () => {
|
const addNewRecord = () => {
|
||||||
setState({
|
setState({
|
||||||
list: [...state.list, {
|
list: [...state.list, {
|
||||||
@ -194,14 +224,34 @@ const Permission = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChange = (action:'remove'|'saved'|'modify',value:UserPermission,index:number) =>{
|
const handleChange = async (action: 'remove' | 'saved' | 'modify', value: UserPermission, index: number) => {
|
||||||
if(action == 'remove') removeItem(index,value.id)
|
if (action == 'remove') removeItem(index, value.id)
|
||||||
else if(action == 'modify'){
|
else if (action == 'modify') {
|
||||||
const newList = [...state.list];
|
const newList = [...state.list];
|
||||||
newList[index] = {
|
newList[index] = {
|
||||||
...value
|
...value
|
||||||
}
|
}
|
||||||
setState({list: newList})
|
setState({list: newList})
|
||||||
|
} else if (action == 'saved') {
|
||||||
|
// 判断是否存在同名用户
|
||||||
|
const exist = state.list.filter(it=>it.id > 0).find(it => it.username == value.username && it.id != value.id)
|
||||||
|
if (exist) {
|
||||||
|
Toast.error(t('permission.message.username_exist'))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const process = value.id > 0 ? updateUserPermission : createUserPermission;
|
||||||
|
try {
|
||||||
|
await process(value).then((newValue) => {
|
||||||
|
Toast.success(t('base.save_success'))
|
||||||
|
const newList = [...state.list];
|
||||||
|
newList[index] = {
|
||||||
|
...newValue
|
||||||
|
}
|
||||||
|
setState({list: newList})
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
Toast.error(t('base.save_failed') + `(${(e as Error).message})`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,20 +273,24 @@ const Permission = () => {
|
|||||||
<div className="item item-operation">{t('bill.title_operate')}</div>
|
<div className="item item-operation">{t('bill.title_operate')}</div>
|
||||||
</div>
|
</div>
|
||||||
{state.list.map((it, index) => (<UserPermissionItem
|
{state.list.map((it, index) => (<UserPermissionItem
|
||||||
key={index} it={it} onChange={(action,value) => handleChange(action,value,index)}
|
key={index} it={it}
|
||||||
|
onChange={async (action, value) => {
|
||||||
|
await handleChange(action, value, index)
|
||||||
|
}}
|
||||||
usernameOptionList={usernameOptionList}
|
usernameOptionList={usernameOptionList}
|
||||||
|
roleOptionList={roleOptionList}
|
||||||
/>))}
|
/>))}
|
||||||
{state.list.length == 0 && <div style={{backgroundColor:'#fafafa'}}>
|
{state.list.length == 0 && <div style={{backgroundColor: '#fafafa'}}>
|
||||||
<Empty
|
<Empty
|
||||||
description={t('permission.message.empty_tips')}
|
description={t('permission.message.empty_tips')}
|
||||||
style={{paddingBottom:20}}
|
style={{paddingBottom: 20}}
|
||||||
/>
|
/>
|
||||||
</div>}
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Space style={{marginTop: 20}}>
|
{!state.loading && <Space style={{marginTop: 20}}>
|
||||||
<Button style={{width: 100}} onClick={addNewRecord} theme={'solid'}>{t('permission.title.add')}</Button>
|
<Button style={{width: 100}} onClick={addNewRecord} theme={'solid'}>{t('permission.title.add')}</Button>
|
||||||
</Space>
|
</Space>}
|
||||||
</Card>)
|
</Card>)
|
||||||
}
|
}
|
||||||
export default Permission
|
export default Permission
|
@ -118,6 +118,7 @@ export const ImportBillModal: React.FC<BillPaidModalProps> = (props) => {
|
|||||||
<div className="import-record-wrapper">
|
<div className="import-record-wrapper">
|
||||||
<div className="table-list">
|
<div className="table-list">
|
||||||
<table>
|
<table>
|
||||||
|
<tbody>
|
||||||
{records.map((record, index) => {
|
{records.map((record, index) => {
|
||||||
return <tr key={index}>
|
return <tr key={index}>
|
||||||
{record.map((item, index) => {
|
{record.map((item, index) => {
|
||||||
@ -127,6 +128,7 @@ export const ImportBillModal: React.FC<BillPaidModalProps> = (props) => {
|
|||||||
})}
|
})}
|
||||||
</tr>
|
</tr>
|
||||||
})}
|
})}
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,6 +16,7 @@ import {BillTypeConfirmModal} from "@/pages/bill/components/bill_type_confirm.ts
|
|||||||
import {BillTypeConfirmBatch} from "@/pages/bill/components/bill_type_confirm_batch.tsx";
|
import {BillTypeConfirmBatch} from "@/pages/bill/components/bill_type_confirm_batch.tsx";
|
||||||
import {AddBillModal} from "@/pages/bill/components/add_bill_modal.tsx";
|
import {AddBillModal} from "@/pages/bill/components/add_bill_modal.tsx";
|
||||||
import {ImportBillModal} from "@/pages/bill/components/import_bill_modal.tsx";
|
import {ImportBillModal} from "@/pages/bill/components/import_bill_modal.tsx";
|
||||||
|
import {PermissionCheck} from "@/components/permission";
|
||||||
|
|
||||||
|
|
||||||
const DownloadButton = ({bill, text}: { bill: BillModel; text: string }) => {
|
const DownloadButton = ({bill, text}: { bill: BillModel; text: string }) => {
|
||||||
@ -29,6 +30,7 @@ const DownloadButton = ({bill, text}: { bill: BillModel; text: string }) => {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
const BillQuery = () => {
|
const BillQuery = () => {
|
||||||
|
|
||||||
// const {createPDF,downloadPDF} = useDownloadReceiptPDF()
|
// const {createPDF,downloadPDF} = useDownloadReceiptPDF()
|
||||||
const [state, setState] = useSetState<{
|
const [state, setState] = useSetState<{
|
||||||
updateBill?: BillModel;
|
updateBill?: BillModel;
|
||||||
@ -88,10 +90,13 @@ const BillQuery = () => {
|
|||||||
// }
|
// }
|
||||||
const operation = (bill: BillModel) => {
|
const operation = (bill: BillModel) => {
|
||||||
return (<div className={'table-operation-render'}>
|
return (<div className={'table-operation-render'}>
|
||||||
|
<PermissionCheck permission={'complete_button'}>
|
||||||
{bill.status != BillStatus.PAID &&
|
{bill.status != BillStatus.PAID &&
|
||||||
<Button onClick={() => setState({updateBill: bill})} size={'small'} theme={'solid'}
|
<Button onClick={() => setState({updateBill: bill})} size={'small'} theme={'solid'}
|
||||||
type={'danger'}>{t('bill.paid')}</Button>
|
type={'danger'}>{t('bill.paid')}</Button>
|
||||||
}
|
}
|
||||||
|
</PermissionCheck>
|
||||||
|
|
||||||
{bill.status == BillStatus.PENDING && <>
|
{bill.status == BillStatus.PENDING && <>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={'Notice'} onConfirm={() => onCancelBill(bill)} position={'topRight'}
|
title={'Notice'} onConfirm={() => onCancelBill(bill)} position={'topRight'}
|
||||||
@ -109,7 +114,11 @@ const BillQuery = () => {
|
|||||||
<DownloadButton bill={bill} text={t('bill.download_receipt')}/>
|
<DownloadButton bill={bill} text={t('bill.download_receipt')}/>
|
||||||
{/*<Button*/}
|
{/*<Button*/}
|
||||||
{/* onClick={() => showBillPDF(bill)} size={'small'}>download pdf</Button>*/}
|
{/* onClick={() => showBillPDF(bill)} size={'small'}>download pdf</Button>*/}
|
||||||
{bill.confirm_status == 'UNCONFIRMED' ? <Button
|
|
||||||
|
<PermissionCheck permission={'confirm_button'}>
|
||||||
|
{
|
||||||
|
bill.confirm_status == 'UNCONFIRMED'
|
||||||
|
? <Button
|
||||||
onClick={() => setState({confirmBill: bill})}
|
onClick={() => setState({confirmBill: bill})}
|
||||||
size={'small'} theme={'solid'}
|
size={'small'} theme={'solid'}
|
||||||
type={'primary'}>{t('bill.confirm_bill_type')}</Button>
|
type={'primary'}>{t('bill.confirm_bill_type')}</Button>
|
||||||
@ -118,7 +127,9 @@ const BillQuery = () => {
|
|||||||
content={`${t('base.confirm_next_operation')}?`}
|
content={`${t('base.confirm_next_operation')}?`}
|
||||||
>
|
>
|
||||||
<Button size={'small'} theme={'solid'} type={'danger'}>{t('bill.btn_cancel_confirm')}</Button>
|
<Button size={'small'} theme={'solid'} type={'danger'}>{t('bill.btn_cancel_confirm')}</Button>
|
||||||
</Popconfirm>}
|
</Popconfirm>
|
||||||
|
}
|
||||||
|
</PermissionCheck>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
</div>)
|
</div>)
|
||||||
|
@ -7,8 +7,10 @@ import SearchForm from "@/components/bill/search-form.tsx";
|
|||||||
import {BillList} from "@/components/bill/list.tsx";
|
import {BillList} from "@/components/bill/list.tsx";
|
||||||
import {billList, BillQueryParams, confirmBills} from "@/service/api/bill.ts";
|
import {billList, BillQueryParams, confirmBills} from "@/service/api/bill.ts";
|
||||||
import {BizError} from "@/service/types.ts";
|
import {BizError} from "@/service/types.ts";
|
||||||
|
import useAuth from "@/hooks/useAuth.ts";
|
||||||
|
|
||||||
const BillReconciliation = () => {
|
const BillReconciliation = () => {
|
||||||
|
const {user} = useAuth()
|
||||||
const {t} = useTranslation()
|
const {t} = useTranslation()
|
||||||
const [queryParams, setBillQueryParams] = useState<BillQueryParams>({
|
const [queryParams, setBillQueryParams] = useState<BillQueryParams>({
|
||||||
apply_status: 'UNCHECKED'
|
apply_status: 'UNCHECKED'
|
||||||
@ -108,7 +110,9 @@ const BillReconciliation = () => {
|
|||||||
/>
|
/>
|
||||||
<BillList
|
<BillList
|
||||||
source={data} type={'reconciliation'}
|
source={data} type={'reconciliation'}
|
||||||
operationRender={queryParams.apply_status == 'CHECKED' ? undefined : operation}
|
operationRender={queryParams.apply_status == 'CHECKED' ? undefined : (
|
||||||
|
user && user.permissions?.apply_button ? operation : undefined
|
||||||
|
)}
|
||||||
beforeTotalAmount={<div>{queryParams.apply_status != 'CHECKED' && (
|
beforeTotalAmount={<div>{queryParams.apply_status != 'CHECKED' && (
|
||||||
(selectKeys.length == 0) ? <Button theme={'solid'} disabled style={{marginRight: 10}}>
|
(selectKeys.length == 0) ? <Button theme={'solid'} disabled style={{marginRight: 10}}>
|
||||||
{t('bill.confirm_batch')}
|
{t('bill.confirm_batch')}
|
||||||
|
@ -12,7 +12,7 @@ import {IconExit, IconUser} from "@douyinfe/semi-icons";
|
|||||||
import styled from "@emotion/styled";
|
import styled from "@emotion/styled";
|
||||||
import useConfig from "@/hooks/useConfig.ts";
|
import useConfig from "@/hooks/useConfig.ts";
|
||||||
import {IconRoles} from "@/components/icons";
|
import {IconRoles} from "@/components/icons";
|
||||||
import {setCurrentRole} from "@/contexts/auth";
|
// import {setCurrentRole} from "@/contexts/auth";
|
||||||
|
|
||||||
const {Header, Content, Sider} = Layout;
|
const {Header, Content, Sider} = Layout;
|
||||||
|
|
||||||
@ -42,15 +42,10 @@ export const HeaderUserAvatar = () => {
|
|||||||
<Avatar color="orange" size="default"><IconUser /></Avatar>
|
<Avatar color="orange" size="default"><IconUser /></Avatar>
|
||||||
<div>
|
<div>
|
||||||
<Typography.Title heading={6}>{user?.username}</Typography.Title>
|
<Typography.Title heading={6}>{user?.username}</Typography.Title>
|
||||||
<div style={{maxWidth:250,overflow:'hidden',whiteSpace:'nowrap',textOverflow:'ellipsis'}}>
|
<div title={user?.department} style={{maxWidth:150,overflow:'hidden',whiteSpace:'nowrap',textOverflow:'ellipsis'}}>
|
||||||
<Typography.Text
|
<Typography.Text
|
||||||
type="quaternary"
|
type="quaternary"
|
||||||
size={'small'}>Department:{user?.department?.toUpperCase() || "N/A"}</Typography.Text>
|
size={'small'}>{user?.department?.toUpperCase() || "N/A"}</Typography.Text>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Typography.Text
|
|
||||||
type="quaternary"
|
|
||||||
size={'small'}>Role:{user?.role?.toUpperCase()}</Typography.Text>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Space>
|
</Space>
|
||||||
@ -66,36 +61,22 @@ export const HeaderUserAvatar = () => {
|
|||||||
<Avatar color="orange" size="small"><IconUser /></Avatar>
|
<Avatar color="orange" size="small"><IconUser /></Avatar>
|
||||||
</Dropdown>)
|
</Dropdown>)
|
||||||
}
|
}
|
||||||
const RoleList: UserRole[] = ['root', 'ro', 'fo','staff']
|
// const RoleList: UserRole[] = ['root', 'ro', 'fo','staff']
|
||||||
const RoleSwitcher = ()=>{
|
const RoleSwitcher = ()=>{
|
||||||
const {user, updateUser} = useAuth()
|
const {user} = useAuth()
|
||||||
// update user role
|
// update user role
|
||||||
const changeRole = (role:UserRole)=>{
|
// const changeRole = (role:UserRole)=>{
|
||||||
updateUser({role}).then(()=>{
|
// updateUser({role}).then(()=>{
|
||||||
setCurrentRole(role)
|
// setCurrentRole(role)
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
return (<>
|
return (<>
|
||||||
{user?.origin_role == 'root' && (<Dropdown
|
|
||||||
clickToHide
|
|
||||||
render={
|
|
||||||
<Dropdown.Menu>
|
|
||||||
{RoleList.map((key) => (
|
|
||||||
<Dropdown.Item
|
|
||||||
active={user?.role == key} key={key}
|
|
||||||
onClick={() => changeRole(key)}
|
|
||||||
><span>{key.toUpperCase()}</span></Dropdown.Item>
|
|
||||||
))}
|
|
||||||
</Dropdown.Menu>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Button theme="borderless">
|
<Button theme="borderless">
|
||||||
<Space style={{transform:'translateY(3px)'}}>
|
<Space style={{transform:'translateY(3px)'}}>
|
||||||
<IconRoles size={20} color={'white'} />
|
<IconRoles size={20} color={'white'} />
|
||||||
<span style={{color:'white'}}>{user?.role?.toUpperCase()}</span>
|
<span style={{color:'white'}}>{user?.role?.toUpperCase()}</span>
|
||||||
</Space>
|
</Space>
|
||||||
</Button>
|
</Button>
|
||||||
</Dropdown>) }
|
|
||||||
</>)
|
</>)
|
||||||
}
|
}
|
||||||
export const CommonHeader: React.FC<CommonHeaderProps> = ({children, title, rightExtra}) => {
|
export const CommonHeader: React.FC<CommonHeaderProps> = ({children, title, rightExtra}) => {
|
||||||
|
@ -10,24 +10,25 @@ export const AllDashboardMenu = [
|
|||||||
key: 'manual',
|
key: 'manual',
|
||||||
icon: <IconQRCode/>,
|
icon: <IconQRCode/>,
|
||||||
path: '/dashboard/manual',
|
path: '/dashboard/manual',
|
||||||
|
permission: 'manual_payment',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'bill',
|
key: 'bill',
|
||||||
icon: <IconQuery/>,
|
icon: <IconQuery/>,
|
||||||
path: '/dashboard/bill',
|
path: '/dashboard/bill',
|
||||||
role: ['root', 'ro', 'fo']
|
permission: 'bill_page',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'check',
|
key: 'check',
|
||||||
icon: <IconReconciliation/>,
|
icon: <IconReconciliation/>,
|
||||||
path: '/dashboard/reconciliation',
|
path: '/dashboard/reconciliation',
|
||||||
role: ['root', 'fo']
|
permission: 'apply_page',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'permission',
|
key: 'permission',
|
||||||
icon: <IconPermission/>,
|
icon: <IconPermission/>,
|
||||||
path: '/dashboard/permission',
|
path: '/dashboard/permission',
|
||||||
role: ['root']
|
permission: 'permission_edit',
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -36,10 +37,11 @@ export function DashboardNavigation() {
|
|||||||
|
|
||||||
const {user} = useAuth();
|
const {user} = useAuth();
|
||||||
const navItems = useMemo(() => {
|
const navItems = useMemo(() => {
|
||||||
if (!user) return [];
|
if (!user || !user.permissions) return [];
|
||||||
|
|
||||||
return AllDashboardMenu.filter(it => {
|
return AllDashboardMenu.filter(it => {
|
||||||
return !it.role || it.role.includes(user.role)
|
if(user.permissions && user.permissions[it.permission]) return true;
|
||||||
|
return false
|
||||||
});
|
});
|
||||||
}, [user])
|
}, [user])
|
||||||
return (<div className={'dashboard-menu-container'}>
|
return (<div className={'dashboard-menu-container'}>
|
||||||
|
@ -63,7 +63,7 @@ export function addBillRecord(bill: CreateBillRecordModel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function uploadBillingRecordFile(file: File, payment_channel: string, check_student = 'true') {
|
export function uploadBillingRecordFile(file: File, payment_channel: string, check_student = 'true') {
|
||||||
return uploadFile('/bill/import', file, {payment_channel, check_student})
|
return uploadFile('/bills/import', file, {payment_channel, check_student})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAsiaPayData(id: number) {
|
export function getAsiaPayData(id: number) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {get, post, remove} from "@/service/request.ts";
|
import {get, post, put, remove} from "@/service/request.ts";
|
||||||
|
|
||||||
export function getUserInfo() {
|
export function getUserInfo() {
|
||||||
return get<UserProfile>('/userinfo')
|
return get<UserProfile>('/userinfo')
|
||||||
@ -24,11 +24,11 @@ export function getUserPermissionList(){
|
|||||||
return get<UserPermission[]>('/permissions')
|
return get<UserPermission[]>('/permissions')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createUserPermission(){
|
export function createUserPermission(data:UserPermission){
|
||||||
return get<UserPermission[]>('/permissions')
|
return post<UserPermission>('/permission',data)
|
||||||
}
|
}
|
||||||
export function updateUserPermission(){
|
export function updateUserPermission(data:UserPermission){
|
||||||
return get<UserPermission[]>('/permissions')
|
return put<UserPermission>(`/permissions/${data.id}`,data)
|
||||||
}
|
}
|
||||||
export function removeUserPermission(id:number){
|
export function removeUserPermission(id:number){
|
||||||
return remove(`/permissions/${id}`)
|
return remove(`/permissions/${id}`)
|
||||||
|
@ -95,7 +95,7 @@ export function uploadFile<T>(url: string, file: UploadFileModel | File, data: A
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return request<T>(url, 'post', data, returnOrigin, {
|
return request<T>(url, 'post', formData, returnOrigin, {
|
||||||
'Content-Type': 'multipart/form-data'
|
'Content-Type': 'multipart/form-data'
|
||||||
})
|
})
|
||||||
// return new Promise<T>((resolve, reject) => {
|
// return new Promise<T>((resolve, reject) => {
|
||||||
|
27
src/types/auth.d.ts
vendored
27
src/types/auth.d.ts
vendored
@ -1,5 +1,18 @@
|
|||||||
declare type UserRole = 'root' | 'ro' | 'fo' | 'staff'
|
declare type UserRole = 'root' | 'ro' | 'fo' | 'staff'
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
declare type UserProfile = {
|
declare type UserProfile = {
|
||||||
id: string | number;
|
id: string | number;
|
||||||
token: string;
|
token: string;
|
||||||
@ -14,6 +27,7 @@ declare type UserProfile = {
|
|||||||
type: string;
|
type: string;
|
||||||
roles: UserRole[];
|
roles: UserRole[];
|
||||||
role: UserRole;
|
role: UserRole;
|
||||||
|
permissions:UserPermission | null;
|
||||||
origin_role?: UserRole;
|
origin_role?: UserRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,16 +52,3 @@ declare type PermissionUserList = {
|
|||||||
role_name: string;
|
role_name: string;
|
||||||
username_list: 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