Complete main UI #37

Merged
FatttSnake merged 192 commits from FatttSnake into dev 2024-02-23 16:31:17 +08:00
9 changed files with 211 additions and 123 deletions
Showing only changes of commit 406c362f84 - Show all commits

View File

@@ -0,0 +1,16 @@
import React from 'react'
import { hasPermission } from '@/util/auth.tsx'
interface PermissionProps extends React.PropsWithChildren {
operationCode?: string
}
const Permission: React.FC<PermissionProps> = (props) => {
if (!props.operationCode || hasPermission(props.operationCode)) {
return props.children
}
return <></>
}
export default Permission

View File

@@ -28,13 +28,15 @@ const Login: React.FC = () => {
setToken(data?.token ?? '') setToken(data?.token ?? '')
void messageApi.success('登录成功') void messageApi.success('登录成功')
setTimeout(() => { setTimeout(() => {
void getUserInfo().then((user) => {
refreshRouter()
if (searchParams.has('redirect')) { if (searchParams.has('redirect')) {
navigate(searchParams.get('redirect') ?? '/') navigate(searchParams.get('redirect') ?? '/')
} else { } else {
navigate('/') navigate('/')
} }
void getUserInfo().then((user) => {
refreshRouter()
notification.success({ notification.success({
message: '欢迎回来', message: '欢迎回来',
description: ( description: (

View File

@@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import '@/assets/css/pages/tools-framework.scss' import '@/assets/css/pages/tools-framework.scss'
import user from '@/router/user' import user from '@/router/user'
import { hasPathPermission } from '@/util/route' import { hasPathPermission } from '@/util/auth'
import FitFullScreen from '@/components/common/FitFullScreen' import FitFullScreen from '@/components/common/FitFullScreen'
import Sidebar from '@/components/common/sidebar' import Sidebar from '@/components/common/sidebar'
import SidebarItemList from '@/components/common/sidebar/SidebarItemList' import SidebarItemList from '@/components/common/sidebar/SidebarItemList'

View File

@@ -11,6 +11,7 @@ import {
DATABASE_UPDATE_SUCCESS DATABASE_UPDATE_SUCCESS
} from '@/constants/common.constants' } from '@/constants/common.constants'
import { useUpdatedEffect } from '@/util/hooks' import { useUpdatedEffect } from '@/util/hooks'
import { hasPermission } from '@/util/auth'
import { utcToLocalTime } from '@/util/datetime' import { utcToLocalTime } from '@/util/datetime'
import { import {
r_sys_group_add, r_sys_group_add,
@@ -21,6 +22,7 @@ import {
r_sys_group_update, r_sys_group_update,
r_sys_role_get_list r_sys_role_get_list
} from '@/services/system' } from '@/services/system'
import Permission from '@/components/common/Permission'
import FitFullScreen from '@/components/common/FitFullScreen' import FitFullScreen from '@/components/common/FitFullScreen'
import HideScrollbar from '@/components/common/HideScrollbar' import HideScrollbar from '@/components/common/HideScrollbar'
import FlexBox from '@/components/common/FlexBox' import FlexBox from '@/components/common/FlexBox'
@@ -105,6 +107,7 @@ const Group: React.FC = () => {
render: (value, record) => ( render: (value, record) => (
<> <>
<AntdSpace size={'middle'}> <AntdSpace size={'middle'}>
<Permission operationCode={'system:group:modify:status'}>
{value ? ( {value ? (
<a <a
style={{ color: COLOR_PRODUCTION }} style={{ color: COLOR_PRODUCTION }}
@@ -120,18 +123,23 @@ const Group: React.FC = () => {
</a> </a>
)} )}
</Permission>
<Permission operationCode={'system:group:modify:one'}>
<a <a
style={{ color: COLOR_PRODUCTION }} style={{ color: COLOR_PRODUCTION }}
onClick={handleOnEditBtnClick(record)} onClick={handleOnEditBtnClick(record)}
> >
</a> </a>
</Permission>
<Permission operationCode={'system:group:delete:one'}>
<a <a
style={{ color: COLOR_PRODUCTION }} style={{ color: COLOR_PRODUCTION }}
onClick={handleOnDeleteBtnClick(record)} onClick={handleOnDeleteBtnClick(record)}
> >
</a> </a>
</Permission>
</AntdSpace> </AntdSpace>
</> </>
) )
@@ -488,6 +496,7 @@ const Group: React.FC = () => {
const toolbar = ( const toolbar = (
<FlexBox direction={'horizontal'} gap={10}> <FlexBox direction={'horizontal'} gap={10}>
<Permission operationCode={'system:group:add:one'}>
<Card style={{ overflow: 'inherit', flex: '0 0 auto' }}> <Card style={{ overflow: 'inherit', flex: '0 0 auto' }}>
<AntdButton <AntdButton
type={'primary'} type={'primary'}
@@ -497,6 +506,7 @@ const Group: React.FC = () => {
<Icon component={IconFatwebPlus} style={{ fontSize: '1.2em' }} /> <Icon component={IconFatwebPlus} style={{ fontSize: '1.2em' }} />
</AntdButton> </AntdButton>
</Card> </Card>
</Permission>
<Card <Card
hidden={tableSelectedItem.length === 0} hidden={tableSelectedItem.length === 0}
style={{ overflow: 'inherit', flex: '0 0 auto' }} style={{ overflow: 'inherit', flex: '0 0 auto' }}
@@ -551,10 +561,14 @@ const Group: React.FC = () => {
pagination={tableParams.pagination} pagination={tableParams.pagination}
loading={isLoading} loading={isLoading}
onChange={handleOnTableChange} onChange={handleOnTableChange}
rowSelection={{ rowSelection={
hasPermission('system:group:delete:multiple')
? {
type: 'checkbox', type: 'checkbox',
onChange: handleOnTableSelectChange onChange: handleOnTableSelectChange
}} }
: undefined
}
/> />
</Card> </Card>
) )

View File

@@ -12,7 +12,7 @@ import {
} from '@/constants/common.constants' } from '@/constants/common.constants'
import { useUpdatedEffect } from '@/util/hooks' import { useUpdatedEffect } from '@/util/hooks'
import { utcToLocalTime } from '@/util/datetime' import { utcToLocalTime } from '@/util/datetime'
import { powerListToPowerTree } from '@/util/auth.tsx' import { hasPermission, powerListToPowerTree } from '@/util/auth'
import { import {
r_sys_role_add, r_sys_role_add,
r_sys_role_change_status, r_sys_role_change_status,
@@ -22,6 +22,7 @@ import {
r_sys_role_delete, r_sys_role_delete,
r_sys_role_delete_list r_sys_role_delete_list
} from '@/services/system' } from '@/services/system'
import Permission from '@/components/common/Permission'
import FitFullScreen from '@/components/common/FitFullScreen' import FitFullScreen from '@/components/common/FitFullScreen'
import HideScrollbar from '@/components/common/HideScrollbar' import HideScrollbar from '@/components/common/HideScrollbar'
import FlexBox from '@/components/common/FlexBox' import FlexBox from '@/components/common/FlexBox'
@@ -98,6 +99,7 @@ const Role: React.FC = () => {
render: (value, record) => ( render: (value, record) => (
<> <>
<AntdSpace size={'middle'}> <AntdSpace size={'middle'}>
<Permission operationCode={'system:role:modify:status'}>
{value ? ( {value ? (
<a <a
style={{ color: COLOR_PRODUCTION }} style={{ color: COLOR_PRODUCTION }}
@@ -113,18 +115,23 @@ const Role: React.FC = () => {
</a> </a>
)} )}
</Permission>
<Permission operationCode={'system:role:modify:one'}>
<a <a
style={{ color: COLOR_PRODUCTION }} style={{ color: COLOR_PRODUCTION }}
onClick={handleOnEditBtnClick(record)} onClick={handleOnEditBtnClick(record)}
> >
</a> </a>
</Permission>
<Permission operationCode={'system:role:delete:one'}>
<a <a
style={{ color: COLOR_PRODUCTION }} style={{ color: COLOR_PRODUCTION }}
onClick={handleOnDeleteBtnClick(record)} onClick={handleOnDeleteBtnClick(record)}
> >
</a> </a>
</Permission>
</AntdSpace> </AntdSpace>
</> </>
) )
@@ -498,6 +505,7 @@ const Role: React.FC = () => {
const toolbar = ( const toolbar = (
<FlexBox direction={'horizontal'} gap={10}> <FlexBox direction={'horizontal'} gap={10}>
<Permission operationCode={'system:role:add:one'}>
<Card style={{ overflow: 'inherit', flex: '0 0 auto' }}> <Card style={{ overflow: 'inherit', flex: '0 0 auto' }}>
<AntdButton <AntdButton
type={'primary'} type={'primary'}
@@ -507,6 +515,7 @@ const Role: React.FC = () => {
<Icon component={IconFatwebPlus} style={{ fontSize: '1.2em' }} /> <Icon component={IconFatwebPlus} style={{ fontSize: '1.2em' }} />
</AntdButton> </AntdButton>
</Card> </Card>
</Permission>
<Card <Card
hidden={tableSelectedItem.length === 0} hidden={tableSelectedItem.length === 0}
style={{ overflow: 'inherit', flex: '0 0 auto' }} style={{ overflow: 'inherit', flex: '0 0 auto' }}
@@ -561,10 +570,14 @@ const Role: React.FC = () => {
pagination={tableParams.pagination} pagination={tableParams.pagination}
loading={isLoading} loading={isLoading}
onChange={handleOnTableChange} onChange={handleOnTableChange}
rowSelection={{ rowSelection={
hasPermission('system:role:delete:multiple')
? {
type: 'checkbox', type: 'checkbox',
onChange: handleOnTableSelectChange onChange: handleOnTableSelectChange
}} }
: undefined
}
/> />
</Card> </Card>
) )

View File

@@ -12,6 +12,7 @@ import {
DATABASE_UPDATE_SUCCESS DATABASE_UPDATE_SUCCESS
} from '@/constants/common.constants' } from '@/constants/common.constants'
import { useUpdatedEffect } from '@/util/hooks' import { useUpdatedEffect } from '@/util/hooks'
import { hasPermission } from '@/util/auth'
import { utcToLocalTime, isPastTime, localTimeToUtc, dayjsToUtc, getNowUtc } from '@/util/datetime' import { utcToLocalTime, isPastTime, localTimeToUtc, dayjsToUtc, getNowUtc } from '@/util/datetime'
import { import {
r_sys_group_get_list, r_sys_group_get_list,
@@ -23,6 +24,7 @@ import {
r_sys_user_get, r_sys_user_get,
r_sys_user_update r_sys_user_update
} from '@/services/system' } from '@/services/system'
import Permission from '@/components/common/Permission'
import { r_api_avatar_random_base64 } from '@/services/api/avatar' import { r_api_avatar_random_base64 } from '@/services/api/avatar'
import FitFullScreen from '@/components/common/FitFullScreen' import FitFullScreen from '@/components/common/FitFullScreen'
import HideScrollbar from '@/components/common/HideScrollbar' import HideScrollbar from '@/components/common/HideScrollbar'
@@ -188,18 +190,23 @@ const User: React.FC = () => {
render: (_, record) => ( render: (_, record) => (
<> <>
<AntdSpace size={'middle'}> <AntdSpace size={'middle'}>
<Permission operationCode={'system:user:modify:password'}>
<a <a
style={{ color: COLOR_PRODUCTION }} style={{ color: COLOR_PRODUCTION }}
onClick={handleOnChangePasswordBtnClick(record)} onClick={handleOnChangePasswordBtnClick(record)}
> >
</a> </a>
</Permission>
<Permission operationCode={'system:user:modify:one'}>
<a <a
style={{ color: COLOR_PRODUCTION }} style={{ color: COLOR_PRODUCTION }}
onClick={handleOnEditBtnClick(record)} onClick={handleOnEditBtnClick(record)}
> >
</a> </a>
</Permission>
<Permission operationCode={'system:user:delete:one'}>
{record.id !== '0' ? ( {record.id !== '0' ? (
<a <a
style={{ color: COLOR_PRODUCTION }} style={{ color: COLOR_PRODUCTION }}
@@ -208,6 +215,7 @@ const User: React.FC = () => {
</a> </a>
) : undefined} ) : undefined}
</Permission>
</AntdSpace> </AntdSpace>
</> </>
) )
@@ -393,6 +401,7 @@ const User: React.FC = () => {
}).then((res) => { }).then((res) => {
const response = res.data const response = res.data
if (response.success) { if (response.success) {
void message.success('修改密码成功')
resolve(true) resolve(true)
} else { } else {
reject(response.msg) reject(response.msg)
@@ -884,6 +893,7 @@ const User: React.FC = () => {
const toolbar = ( const toolbar = (
<FlexBox direction={'horizontal'} gap={10}> <FlexBox direction={'horizontal'} gap={10}>
<Permission operationCode={'system:user:add:one'}>
<Card style={{ overflow: 'inherit', flex: '0 0 auto' }}> <Card style={{ overflow: 'inherit', flex: '0 0 auto' }}>
<AntdButton <AntdButton
type={'primary'} type={'primary'}
@@ -893,6 +903,7 @@ const User: React.FC = () => {
<Icon component={IconFatwebPlus} style={{ fontSize: '1.2em' }} /> <Icon component={IconFatwebPlus} style={{ fontSize: '1.2em' }} />
</AntdButton> </AntdButton>
</Card> </Card>
</Permission>
<Card <Card
hidden={tableSelectedItem.length === 0} hidden={tableSelectedItem.length === 0}
style={{ overflow: 'inherit', flex: '0 0 auto' }} style={{ overflow: 'inherit', flex: '0 0 auto' }}
@@ -951,11 +962,15 @@ const User: React.FC = () => {
pagination={tableParams.pagination} pagination={tableParams.pagination}
loading={isLoadingUserData} loading={isLoadingUserData}
onChange={handleOnTableChange} onChange={handleOnTableChange}
rowSelection={{ rowSelection={
hasPermission('system:user:delete:multiple')
? {
type: 'checkbox', type: 'checkbox',
onChange: handleOnTableSelectChange, onChange: handleOnTableSelectChange,
getCheckboxProps: (record) => ({ disabled: record.id === '0' }) getCheckboxProps: (record) => ({ disabled: record.id === '0' })
}} }
: undefined
}
/> />
</Card> </Card>
) )

View File

@@ -7,6 +7,7 @@ import {
PERMISSION_TOKEN_RENEW_SUCCESS, PERMISSION_TOKEN_RENEW_SUCCESS,
PERMISSION_UNAUTHORIZED PERMISSION_UNAUTHORIZED
} from '@/constants/common.constants' } from '@/constants/common.constants'
import { getRedirectUrl } from '@/util/route'
import { getToken, setToken, removeToken } from '@/util/auth' import { getToken, setToken, removeToken } from '@/util/auth'
const service: AxiosInstance = axios.create({ const service: AxiosInstance = axios.create({
@@ -84,7 +85,9 @@ service.interceptors.response.use(
</> </>
) )
setTimeout(() => { setTimeout(() => {
location.reload() location.replace(
getRedirectUrl('/login', `${location.pathname}${location.search}`)
)
}, 1500) }, 1500)
throw response?.data throw response?.data
case PERMISSION_ACCESS_DENIED: case PERMISSION_ACCESS_DENIED:

View File

@@ -121,6 +121,12 @@ export const getUsername = async () => {
return user.username return user.username
} }
export const getUserId = async () => {
const user = await getUserInfo()
return user.id
}
export const getCaptchaSrc = () => { export const getCaptchaSrc = () => {
captcha = getCaptcha(300, 150, 4) captcha = getCaptcha(300, 150, 4)
return captcha.base64Src return captcha.base64Src
@@ -227,3 +233,41 @@ const parentToTree = (data: _DataNode[]): _DataNode[] => {
return parents return parents
} }
export const getPermissionPath = (): string[] => {
const s = getLocalStorage(STORAGE_USER_INFO_KEY)
if (s === null) {
return []
}
const user = JSON.parse(s) as UserWithPowerInfoVo
const paths: string[] = []
user.menus.forEach((menu) => {
paths.push(menu.url)
})
return paths
}
export const hasPathPermission = (path: string) => {
return getPermissionPath().indexOf(path) !== -1
}
export const getPermission = (): string[] => {
const s = getLocalStorage(STORAGE_USER_INFO_KEY)
if (s === null) {
return []
}
const user = JSON.parse(s) as UserWithPowerInfoVo
const operationCodes: string[] = []
user.operations.forEach((operation) => {
operationCodes.push(operation.code)
})
return operationCodes
}
export const hasPermission = (operationCode: string) => {
return getPermission().indexOf(operationCode) !== -1
}

View File

@@ -1,5 +1,5 @@
import { getLocalStorage } from '@/util/browser.tsx' import { RouteObject } from 'react-router'
import { STORAGE_USER_INFO_KEY } from '@/constants/common.constants.ts' import { hasPathPermission } from '@/util/auth'
export const getRedirectUrl = (path: string, redirectUrl: string): string => { export const getRedirectUrl = (path: string, redirectUrl: string): string => {
return `${path}?redirect=${encodeURIComponent(redirectUrl)}` return `${path}?redirect=${encodeURIComponent(redirectUrl)}`
@@ -15,25 +15,6 @@ export const getFullTitle = (data: _DataNode, preTitle?: string) => {
return data return data
} }
export const getPermissionPath = (): string[] => {
const s = getLocalStorage(STORAGE_USER_INFO_KEY)
if (s === null) {
return []
}
const user = JSON.parse(s) as UserWithPowerInfoVo
const paths: string[] = []
user.menus.forEach((menu) => {
paths.push(menu.url)
})
return paths
}
export const hasPathPermission = (path: string) => {
return getPermissionPath().indexOf(path) !== -1
}
export const getAuthRoute = ( export const getAuthRoute = (
route: RouteJsonObject[], route: RouteJsonObject[],
parentPermission: boolean = false parentPermission: boolean = false