Add login and logout notification
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
$origin-color: white;
|
||||
$main-color: #4E47BB;
|
||||
$production-color: #4E47BB;
|
||||
$main-color: $production-color;
|
||||
$secondary-color: #BAB8E5;
|
||||
$error-color: #ff4d4f;
|
||||
$error-secondary-color: #ff7875;
|
||||
$blue-color: #1677ff;
|
||||
$active-color: #EBECFFD;
|
||||
$error-color: #FF4D4F;
|
||||
$error-secondary-color: #FF7875;
|
||||
$blue-color: #1677FF;
|
||||
$active-color: #EBECFD;
|
||||
$background-color: #F5F5F5;
|
||||
$font-main-color: #4D4D4D;
|
||||
$font-secondary-color: #9E9E9E;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import React from 'react'
|
||||
import Icon from '@ant-design/icons'
|
||||
import { getLoginStatus, logout } from '@/utils/auth'
|
||||
import { getLoginStatus, getUsername, logout } from '@/utils/auth'
|
||||
import { getRedirectUrl } from '@/utils/common'
|
||||
import { notification } from 'antd'
|
||||
import { COLOR_ERROR } from '@/constants/common.constants.ts'
|
||||
|
||||
const SidebarFooter: React.FC = () => {
|
||||
const matches = useMatches()
|
||||
@@ -9,6 +11,8 @@ const SidebarFooter: React.FC = () => {
|
||||
const location = useLocation()
|
||||
const navigate = useNavigate()
|
||||
const [exiting, setExiting] = useState(false)
|
||||
const [username, setUsername] = useState('')
|
||||
|
||||
const handleClickAvatar = () => {
|
||||
if (getLoginStatus()) {
|
||||
navigate('/user')
|
||||
@@ -24,12 +28,26 @@ const SidebarFooter: React.FC = () => {
|
||||
|
||||
setExiting(true)
|
||||
void logout().finally(() => {
|
||||
notification.info({
|
||||
message: '已退出登录',
|
||||
icon: <Icon component={IconFatwebExit} style={{ color: COLOR_ERROR }} />
|
||||
})
|
||||
setTimeout(() => {
|
||||
window.location.reload()
|
||||
}, 1500)
|
||||
})
|
||||
}
|
||||
|
||||
const loginStatus = getLoginStatus()
|
||||
|
||||
useEffect(() => {
|
||||
if (getLoginStatus()) {
|
||||
void getUsername().then((username) => {
|
||||
setUsername(username)
|
||||
})
|
||||
}
|
||||
}, [loginStatus])
|
||||
|
||||
return (
|
||||
<div className={'footer'}>
|
||||
<span className={'icon-user'} onClick={handleClickAvatar}>
|
||||
@@ -42,7 +60,7 @@ const SidebarFooter: React.FC = () => {
|
||||
</NavLink>
|
||||
</span>
|
||||
<span hidden={!getLoginStatus()} className={'text'}>
|
||||
已登录
|
||||
{username}
|
||||
</span>
|
||||
<div
|
||||
hidden={!getLoginStatus()}
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
export const PRODUCTION_NAME = 'FatWeb'
|
||||
export const STORAGE_TOKEN_KEY = 'JWT_TOKEN'
|
||||
export const STORAGE_USER_INFO_KEY = 'USER_INFO'
|
||||
export const COLOR_PRODUCTION = '#00D4FF'
|
||||
export const COLOR_ORIGIN = 'white'
|
||||
export const COLOR_PRODUCTION = '#4E47BB'
|
||||
export const COLOR_MAIN = COLOR_PRODUCTION
|
||||
export const COLOR_SECONDARY = '#BAB8E5'
|
||||
export const COLOR_ERROR = '#FF4D4F'
|
||||
export const COLOR_ERROR_SECONDARY = '#FF7875'
|
||||
export const COLOR_BLUE = '#1677FF'
|
||||
export const COLOR_ACTIVE = '#EBECFD'
|
||||
export const COLOR_BACKGROUND = '#F5F5F5'
|
||||
export const COLOR_TOP = 'rgba(234,46,13,0.85)'
|
||||
export const COLOR_FONT_MAIN = '#4D4D4D'
|
||||
export const COLOR_FONT_SECONDARY = '#9E9E9E'
|
||||
export const COLOR_FOCUS = '#DDDDDD'
|
||||
export const SIZE_ICON_XS = '16px'
|
||||
export const SIZE_ICON_SM = '20px'
|
||||
export const SIZE_ICON_MD = '24px'
|
||||
@@ -33,3 +41,12 @@ export const SYSTEM_REQUEST_ILLEGAL = 10040
|
||||
export const SYSTEM_ARGUMENT_NOT_VALID = 10041
|
||||
export const SYSTEM_ERROR = 10050
|
||||
export const SYSTEM_TIMEOUT = 10051
|
||||
|
||||
export const DATABASE_SELECT_SUCCESS = 20000
|
||||
export const DATABASE_SELECT_FAILED = 20005
|
||||
export const DATABASE_INSERT_SUCCESS = 20010
|
||||
export const DATABASE_INSERT_FAILED = 20015
|
||||
export const DATABASE_UPDATE_SUCCESS = 20020
|
||||
export const DATABASE_UPDATE_FILED = 20025
|
||||
export const DATABASE_DELETE_SUCCESS = 20030
|
||||
export const DATABASE_DELETE_FILED = 20035
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export const URL_API_LOGIN = '/login'
|
||||
export const URL_API_TOKEN = '/token'
|
||||
export const URL_API_LOGOUT = '/logout'
|
||||
export const URL_API_USER_INFO = '/system/user/info'
|
||||
|
||||
41
src/global.d.ts
vendored
41
src/global.d.ts
vendored
@@ -47,14 +47,49 @@ type Captcha = {
|
||||
base64Src: string
|
||||
}
|
||||
|
||||
type Token = {
|
||||
type TokenVo = {
|
||||
token: string
|
||||
}
|
||||
|
||||
type User = {
|
||||
id: number
|
||||
type UserVo = {
|
||||
id: string
|
||||
username: string
|
||||
locking: boolean
|
||||
expiration: Date
|
||||
credentialsExpiration: Date
|
||||
enable: number
|
||||
lastLoginTime: Date
|
||||
lastLoginIp: string
|
||||
createTime: Date
|
||||
updateTime: Date
|
||||
menus: MenuVo[]
|
||||
elements: ElementVo[]
|
||||
operations: OperationVo[]
|
||||
}
|
||||
|
||||
type MenuVo = {
|
||||
id: number
|
||||
name: string
|
||||
url: string
|
||||
powerId: number
|
||||
parentId: number
|
||||
}
|
||||
|
||||
type ElementVo = {
|
||||
id: number
|
||||
name: string
|
||||
powerId: number
|
||||
parentId: number
|
||||
menuId: number
|
||||
}
|
||||
|
||||
type OperationVo = {
|
||||
id: number
|
||||
name: string
|
||||
code: string
|
||||
powerId: number
|
||||
parentId: number
|
||||
elementId: number
|
||||
}
|
||||
|
||||
type LoginForm = {
|
||||
|
||||
@@ -7,7 +7,9 @@ import {
|
||||
} from '@/constants/common.constants'
|
||||
import '@/assets/css/pages/login.scss'
|
||||
import { setToken } from '@/utils/common'
|
||||
import { login } from '@/utils/auth'
|
||||
import { getUserInfo, login } from '@/utils/auth'
|
||||
import { notification } from 'antd'
|
||||
import moment from 'moment'
|
||||
|
||||
const Login: React.FC = () => {
|
||||
const [messageApi, contextHolder] = message.useMessage()
|
||||
@@ -31,6 +33,27 @@ const Login: React.FC = () => {
|
||||
} else {
|
||||
navigate('/')
|
||||
}
|
||||
void getUserInfo().then((user) => {
|
||||
notification.success({
|
||||
message: '欢迎回来',
|
||||
description: (
|
||||
<>
|
||||
<span>
|
||||
你好 <strong>{user.username}</strong>
|
||||
</span>
|
||||
<br />
|
||||
<span>
|
||||
上次登录:
|
||||
{moment(user.lastLoginTime).format(
|
||||
'yyyy-MM-DD HH:mm:ssZ'
|
||||
)}
|
||||
【{user.lastLoginIp}】
|
||||
</span>
|
||||
</>
|
||||
),
|
||||
placement: 'topRight'
|
||||
})
|
||||
})
|
||||
}, 1500)
|
||||
break
|
||||
case SYSTEM_USERNAME_NOT_FOUND:
|
||||
|
||||
@@ -44,7 +44,7 @@ service.interceptors.request.use(
|
||||
.get(import.meta.env.VITE_API_TOKEN_URL, {
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
})
|
||||
.then((value: AxiosResponse<_Response<Token>>) => {
|
||||
.then((value: AxiosResponse<_Response<TokenVo>>) => {
|
||||
const response = value.data
|
||||
if (response.code === SYSTEM_TOKEN_RENEW_SUCCESS) {
|
||||
setToken(response.data?.token ?? '')
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { getCaptcha, getLocalStorage, removeToken, setLocalStorage } from './common'
|
||||
import { SYSTEM_OK, STORAGE_TOKEN_KEY, STORAGE_USER_INFO_KEY } from '@/constants/common.constants'
|
||||
import {
|
||||
STORAGE_TOKEN_KEY,
|
||||
STORAGE_USER_INFO_KEY,
|
||||
DATABASE_SELECT_SUCCESS
|
||||
} from '@/constants/common.constants'
|
||||
import request from '@/services'
|
||||
import { URL_API_LOGIN, URL_API_LOGOUT } from '@/constants/urls.constants'
|
||||
import { URL_API_LOGIN, URL_API_LOGOUT, URL_API_USER_INFO } from '@/constants/urls.constants'
|
||||
|
||||
let captcha: Captcha
|
||||
|
||||
export const login = async (username: string, password: string) => {
|
||||
return await request.post<Token>(URL_API_LOGIN, {
|
||||
return await request.post<TokenVo>(URL_API_LOGIN, {
|
||||
username,
|
||||
password
|
||||
})
|
||||
@@ -22,27 +26,27 @@ export const getLoginStatus = () => {
|
||||
return getLocalStorage(STORAGE_TOKEN_KEY) !== null
|
||||
}
|
||||
|
||||
export const getUser = async (): Promise<User> => {
|
||||
export const getUserInfo = async (): Promise<UserVo> => {
|
||||
if (getLocalStorage(STORAGE_USER_INFO_KEY) !== null) {
|
||||
return new Promise((resolve) => {
|
||||
resolve(JSON.parse(getLocalStorage(STORAGE_USER_INFO_KEY) as string) as User)
|
||||
resolve(JSON.parse(getLocalStorage(STORAGE_USER_INFO_KEY) as string) as UserVo)
|
||||
})
|
||||
}
|
||||
return requestUser()
|
||||
return requestUserInfo()
|
||||
}
|
||||
|
||||
export const requestUser = async () => {
|
||||
let user: User | null
|
||||
export const requestUserInfo = async () => {
|
||||
let user: UserVo | null
|
||||
|
||||
await request.get<User>('/user/info').then((value) => {
|
||||
await request.get<UserVo>(URL_API_USER_INFO).then((value) => {
|
||||
const response = value.data
|
||||
if (response.code === SYSTEM_OK) {
|
||||
if (response.code === DATABASE_SELECT_SUCCESS) {
|
||||
user = response.data
|
||||
setLocalStorage(STORAGE_USER_INFO_KEY, JSON.stringify(user))
|
||||
}
|
||||
})
|
||||
|
||||
return new Promise<User>((resolve, reject) => {
|
||||
return new Promise<UserVo>((resolve, reject) => {
|
||||
if (user) {
|
||||
resolve(user)
|
||||
}
|
||||
@@ -51,7 +55,7 @@ export const requestUser = async () => {
|
||||
}
|
||||
|
||||
export const getUsername = async () => {
|
||||
const user = await getUser()
|
||||
const user = await getUserInfo()
|
||||
|
||||
return user.username
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user