feat 登录和token对接

This commit is contained in:
xxm
2022-10-12 23:34:27 +08:00
parent 014a6dcf5a
commit 8112891db2
10 changed files with 112 additions and 132 deletions

View File

@@ -1,9 +1,19 @@
/** /**
* @description: Login interface parameters * 账密登录参数
*/ */
export interface LoginParams { export interface LoginParams {
username: string // 账号/手机号/邮箱
account: string
// 密码
password: string password: string
// 终端
client: string
// 登录方式
loginType: string
// 验证码key
captchaKey: string
// 验证码
captcha: string
} }
export interface RoleInfo { export interface RoleInfo {
@@ -12,27 +22,15 @@ export interface RoleInfo {
} }
/** /**
* @description: Login interface return value * 登录后用户信息
*/
export interface LoginResultModel {
userId: string | number
token: string
role: RoleInfo
}
/**
* @description: Get user information return value
*/ */
export interface GetUserInfoModel { export interface GetUserInfoModel {
roles: RoleInfo[]
// 用户id // 用户id
userId: string | number userId: number
// 用户 // 名
name: string
// 账号
username: string username: string
// 真实名字
realName: string
// 头像 // 头像
avatar: string avatar: string
// 介绍
desc?: string
} }

View File

@@ -1,46 +1,51 @@
import { defHttp } from '/@/utils/http/axios' import { defHttp } from '/@/utils/http/axios'
import { LoginParams, LoginResultModel, GetUserInfoModel } from './model/userModel' import { LoginParams, GetUserInfoModel } from './model/userModel'
import { ErrorMessageMode, Result } from '/#/axios' import { ErrorMessageMode, Result } from '/#/axios'
enum Api { enum Api {
Login = '/login',
Logout = '/logout', Logout = '/logout',
GetUserInfo = '/getUserInfo',
GetPermCode = '/getPermCode', GetPermCode = '/getPermCode',
TestRetry = '/testRetry', TestRetry = '/testRetry',
} }
/** /**
* @description: user login api * 登录接口 返回token
*/ */
export function loginApi(params: LoginParams, mode: ErrorMessageMode = 'modal') { export function loginApi(params: LoginParams) {
return defHttp.post<Result<LoginResultModel>>( return defHttp.post<Result<string>>({
{ url: '/token/login',
url: Api.Login, params,
params, })
}, }
{ /**
errorMessageMode: mode, * 登录后获取用户信息
}, */
) export function getUserInfo() {
return defHttp.get<Result<GetUserInfoModel>>({ url: '/user/getLoginAfterUserInfo' })
} }
/** /**
* @description: getUserInfo * 获取用户菜单和资源权限
*/ */
export function getUserInfo() { export function getPermissions(clientCode: string) {
return defHttp.get<Result<GetUserInfoModel>>({ url: Api.GetUserInfo }, { errorMessageMode: 'none' }) return defHttp.get<Result<GetUserInfoModel>>({ url: '/role/menu/getPermissions', params: { clientCode } })
} }
export function getPermCode() { export function getPermCode() {
return defHttp.get<string[]>({ url: Api.GetPermCode }) return defHttp.get<string[]>({ url: Api.GetPermCode })
} }
/**
* 退出
*/
export function doLogout() { export function doLogout() {
return defHttp.get({ url: Api.Logout }) return defHttp.post({ url: '/token/logout' })
} }
/**
* 测试重试
*/
export function testRetry() { export function testRetry() {
return defHttp.get( return defHttp.get(
{ url: Api.TestRetry }, { url: Api.TestRetry },

View File

@@ -4,17 +4,17 @@
<img :class="`${prefixCls}__header`" :src="getUserInfo.avatar" /> <img :class="`${prefixCls}__header`" :src="getUserInfo.avatar" />
<span :class="`${prefixCls}__info hidden md:block`"> <span :class="`${prefixCls}__info hidden md:block`">
<span :class="`${prefixCls}__name `" class="truncate"> <span :class="`${prefixCls}__name `" class="truncate">
{{ getUserInfo.realName }} {{ getUserInfo.name }}
</span> </span>
</span> </span>
</span> </span>
<template #overlay> <template #overlay>
<Menu @click="handleMenuClick"> <Menu @click="handleMenuClick">
<MenuItem key="doc" :text="t('layout.header.dropdownItemDoc')" icon="ion:document-text-outline" v-if="getShowDoc" /> <!-- <MenuItem key="doc" text="文档" icon="ion:document-text-outline" v-if="getShowDoc" />-->
<MenuDivider v-if="getShowDoc" /> <!-- <MenuDivider v-if="getShowDoc" />-->
<MenuItem v-if="getUseLockPage" key="lock" :text="t('layout.header.tooltipLock')" icon="ion:lock-closed-outline" /> <MenuItem v-if="getUseLockPage" key="lock" text="锁定屏幕" icon="ion:lock-closed-outline" />
<MenuItem key="logout" :text="t('layout.header.dropdownItemLoginOut')" icon="ion:power-outline" /> <MenuItem key="logout" text="退出系统" icon="ion:power-outline" />
</Menu> </Menu>
</template> </template>
</Dropdown> </Dropdown>
@@ -49,7 +49,7 @@
Dropdown, Dropdown,
Menu, Menu,
MenuItem: createAsyncComponent(() => import('./DropMenuItem.vue')), MenuItem: createAsyncComponent(() => import('./DropMenuItem.vue')),
MenuDivider: Menu.Divider, // MenuDivider: Menu.Divider,
LockAction: createAsyncComponent(() => import('../lock/LockModal.vue')), LockAction: createAsyncComponent(() => import('../lock/LockModal.vue')),
}, },
props: { props: {
@@ -57,15 +57,14 @@
}, },
setup() { setup() {
const { prefixCls } = useDesign('header-user-dropdown') const { prefixCls } = useDesign('header-user-dropdown')
const { t } = useI18n()
const { getShowDoc, getUseLockPage } = useHeaderSetting() const { getShowDoc, getUseLockPage } = useHeaderSetting()
const userStore = useUserStore() const userStore = useUserStore()
// 用户信息
const getUserInfo = computed(() => { const getUserInfo = computed(() => {
const { realName = '', avatar, desc } = userStore.getUserInfo || {} const { name = '', avatar } = userStore.getUserInfo || {}
return { realName, avatar: avatar || headerImg, desc } return { name, avatar: headerImg }
}) })
const [register, { openModal }] = useModal() const [register, { openModal }] = useModal()
function handleLock() { function handleLock() {
@@ -98,10 +97,9 @@
return { return {
prefixCls, prefixCls,
t,
getUserInfo, getUserInfo,
handleMenuClick, handleMenuClick,
getShowDoc, // getShowDoc,
register, register,
getUseLockPage, getUseLockPage,
} }

View File

@@ -57,6 +57,7 @@ export const useUserStore = defineStore({
}, },
}, },
actions: { actions: {
// token信心
setToken(info: string | undefined) { setToken(info: string | undefined) {
this.token = info ? info : '' // for null or undefined value this.token = info ? info : '' // for null or undefined value
setAuthCache(TOKEN_KEY, info) setAuthCache(TOKEN_KEY, info)
@@ -80,36 +81,33 @@ export const useUserStore = defineStore({
this.sessionTimeout = false this.sessionTimeout = false
}, },
/** /**
* @description: login * 登录方法
*/ */
async login( async login(params: LoginParams) {
params: LoginParams & {
goHome?: boolean
mode?: ErrorMessageMode
},
): Promise<GetUserInfoModel | null> {
try { try {
const { goHome = true, mode, ...loginParams } = params const { data: token } = await loginApi(params)
const data = await loginApi(loginParams, mode) // 保存token
const { token } = data.data
// save token
this.setToken(token) this.setToken(token)
return this.afterLoginAction(goHome) await this.afterLoginAction(true)
return token
} catch (error) { } catch (error) {
return Promise.reject(error) return Promise.reject(error)
} }
}, },
async afterLoginAction(goHome?: boolean): Promise<GetUserInfoModel | null> { /**
* 登录后操作
*/
async afterLoginAction(goHome?: boolean) {
if (!this.getToken) return null if (!this.getToken) return null
// get user info // 获取用户信息
const userInfo = await this.getUserInfoAction() await this.getUserInfoAction()
const sessionTimeout = this.sessionTimeout const sessionTimeout = this.sessionTimeout
// 超时
if (sessionTimeout) { if (sessionTimeout) {
this.setSessionTimeout(false) this.setSessionTimeout(false)
} else { } else {
const permissionStore = usePermissionStore() const permissionStore = usePermissionStore()
console.log(permissionStore)
if (!permissionStore.isDynamicAddedRoute) { if (!permissionStore.isDynamicAddedRoute) {
const routes = await permissionStore.buildRoutesAction() const routes = await permissionStore.buildRoutesAction()
routes.forEach((route) => { routes.forEach((route) => {
@@ -118,21 +116,13 @@ export const useUserStore = defineStore({
router.addRoute(PAGE_NOT_FOUND_ROUTE as unknown as RouteRecordRaw) router.addRoute(PAGE_NOT_FOUND_ROUTE as unknown as RouteRecordRaw)
permissionStore.setDynamicAddedRoute(true) permissionStore.setDynamicAddedRoute(true)
} }
goHome && (await router.replace(userInfo?.homePath || PageEnum.BASE_HOME)) goHome && (await router.replace(PageEnum.BASE_HOME))
} }
return userInfo
}, },
async getUserInfoAction(): Promise<UserInfo | null> { // 获取并存储用户信息
async getUserInfoAction() {
if (!this.getToken) return null if (!this.getToken) return null
const { data: userInfo } = await getUserInfo() const { data: userInfo } = await getUserInfo()
const { roles = [] } = userInfo
if (isArray(roles)) {
const roleList = roles.map((item) => item.value) as RoleEnum[]
this.setRoleList(roleList)
} else {
userInfo.roles = []
this.setRoleList([])
}
this.setUserInfo(userInfo) this.setUserInfo(userInfo)
return userInfo return userInfo
}, },

View File

@@ -135,8 +135,8 @@ const transform: AxiosTransform = {
// 请求之前处理config // 请求之前处理config
const token = getToken() const token = getToken()
if (token && (config as Recordable)?.requestOptions?.withToken !== false) { if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
// jwt token // 添加 token 到请求头
;(config as Recordable).headers.Authorization = options.authenticationScheme ? `${options.authenticationScheme} ${token}` : token ;(config as Recordable).headers.AccessToken = token
} }
return config return config
}, },

View File

@@ -10,13 +10,13 @@
<a-spin :spinning="confirmLoading"> <a-spin :spinning="confirmLoading">
<a-form class="small-from-item" ref="formRef" :label-col="labelCol" :wrapper-col="wrapperCol"> <a-form class="small-from-item" ref="formRef" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-item label="主键" :hidden="true"> <a-form-item label="主键" :hidden="true">
<a-input v-model:value="form.id" :disabled="showable" /> <Input v-model:value="form.id" :disabled="showable" />
</a-form-item> </a-form-item>
<a-form-item label="编码" v-bind="validateInfos.code" name="code"> <a-form-item label="编码" v-bind="validateInfos.code" name="code">
<a-input v-model:value="form.code" :disabled="showable" @blur="validate('code')" placeholder="请输入编码" /> <Input v-model:value="form.code" :disabled="showable" @blur="validate('code')" placeholder="请输入编码" />
</a-form-item> </a-form-item>
<a-form-item label="名称" v-bind="validateInfos.name" name="name"> <a-form-item label="名称" v-bind="validateInfos.name" name="name">
<a-input v-model:value="form.name" :disabled="showable" @blur="validate('name')" placeholder="请输入名称" /> <Input v-model:value="form.name" :disabled="showable" @blur="validate('name')" placeholder="请输入名称" />
</a-form-item> </a-form-item>
<a-form-item label="启用状态" v-bind="validateInfos.enable" name="enable"> <a-form-item label="启用状态" v-bind="validateInfos.enable" name="enable">
<a-switch checked-children="" un-checked-children="" v-model:checked="form.enable" :disabled="showable || form.system" /> <a-switch checked-children="" un-checked-children="" v-model:checked="form.enable" :disabled="showable || form.system" />
@@ -59,9 +59,8 @@
import { nextTick, reactive, ref } from 'vue' import { nextTick, reactive, ref } from 'vue'
import useFormEdit from '/@/hooks/bootx/useFormEdit' import useFormEdit from '/@/hooks/bootx/useFormEdit'
import { add, Client, existsByCode, existsByCodeNotId, get, update } from './Client.api' import { add, Client, existsByCode, existsByCodeNotId, get, update } from './Client.api'
import { Rule, useForm } from "ant-design-vue/lib/form"; import { FormInstance, Rule, useForm } from 'ant-design-vue/lib/form'
import { FormEditType } from '/@/enums/formTypeEnum' import { FormEditType } from '/@/enums/formTypeEnum'
import { FormInstance } from 'ant-design-vue/es'
import { findAll, LoginType } from '/@/views/modules/system/loginType/LoginType.api' import { findAll, LoginType } from '/@/views/modules/system/loginType/LoginType.api'
const { const {

View File

@@ -10,13 +10,13 @@
<a-spin :spinning="confirmLoading"> <a-spin :spinning="confirmLoading">
<a-form class="small-from-item" ref="formRef" :model="form" :rules="rules" :label-col="labelCol" :wrapper-col="wrapperCol"> <a-form class="small-from-item" ref="formRef" :model="form" :rules="rules" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-item label="主键" name="id" :hidden="true"> <a-form-item label="主键" name="id" :hidden="true">
<a-input v-model:value="form.id" :disabled="showable" /> <Input v-model:value="form.id" :disabled="showable" />
</a-form-item> </a-form-item>
<a-form-item label="编码" name="code"> <a-form-item label="编码" name="code">
<a-input v-model:value="form.code" :disabled="showable" placeholder="请输入登录方式编码" /> <Input v-model:value="form.code" :disabled="showable" placeholder="请输入登录方式编码" />
</a-form-item> </a-form-item>
<a-form-item label="名称" name="name"> <a-form-item label="名称" name="name">
<a-input v-model:value="form.name" :disabled="showable" placeholder="请输入登录方式名称" /> <Input v-model:value="form.name" :disabled="showable" placeholder="请输入登录方式名称" />
</a-form-item> </a-form-item>
<a-form-item label="类型" name="type"> <a-form-item label="类型" name="type">
<a-radio-group v-model:value="form.type" button-style="solid"> <a-radio-group v-model:value="form.type" button-style="solid">
@@ -35,7 +35,7 @@
<a-switch checked-children="" un-checked-children="" v-model:checked="form.captcha" :disabled="showable" /> <a-switch checked-children="" un-checked-children="" v-model:checked="form.captcha" :disabled="showable" />
</a-form-item> </a-form-item>
<a-form-item label="超时时间(分钟)" name="timeout"> <a-form-item label="超时时间(分钟)" name="timeout">
<a-input-number <Input-number
v-model:value="form.timeout" v-model:value="form.timeout"
:disabled="showable" :disabled="showable"
:precision="0" :precision="0"
@@ -45,7 +45,7 @@
/> />
</a-form-item> </a-form-item>
<a-form-item label="密码可错误次数" name="pwdErrNum" v-show="form.type === PASSWORD"> <a-form-item label="密码可错误次数" name="pwdErrNum" v-show="form.type === PASSWORD">
<a-input-number <Input-number
v-model:value="form.pwdErrNum" v-model:value="form.pwdErrNum"
:disabled="showable" :disabled="showable"
:min="-1" :min="-1"
@@ -73,8 +73,7 @@
import { nextTick, reactive, ref } from 'vue' import { nextTick, reactive, ref } from 'vue'
import { add, LoginType, get, existsByCode, existsByCodeNotId, update, PASSWORD, OPEN_ID } from './LoginType.api' import { add, LoginType, get, existsByCode, existsByCodeNotId, update, PASSWORD, OPEN_ID } from './LoginType.api'
import { FormEditType } from '/@/enums/formTypeEnum' import { FormEditType } from '/@/enums/formTypeEnum'
import { FormInstance } from 'ant-design-vue/es' import { FormInstance, Rule } from 'ant-design-vue/lib/form'
import { Rule } from 'ant-design-vue/lib/form'
const { const {
initFormModel, initFormModel,
@@ -99,7 +98,7 @@
enable: true, enable: true,
description: '', description: '',
} as LoginType) } as LoginType)
const formRef = ref<FormInstance>()
// 校验状态 // 校验状态
const rules = reactive({ const rules = reactive({
code: [ code: [
@@ -118,7 +117,6 @@
const res = formEditType.value === FormEditType.Edit ? await existsByCodeNotId(code, id) : await existsByCode(code) const res = formEditType.value === FormEditType.Edit ? await existsByCodeNotId(code, id) : await existsByCode(code)
return res.data ? Promise.reject('该编码已存在!') : Promise.resolve() return res.data ? Promise.reject('该编码已存在!') : Promise.resolve()
} }
const formRef = ref<FormInstance>()
// 事件 // 事件
const emits = defineEmits(['ok']) const emits = defineEmits(['ok'])

View File

@@ -2,7 +2,7 @@
<div :class="prefixCls" class="relative w-full h-full px-4"> <div :class="prefixCls" class="relative w-full h-full px-4">
<div class="flex items-center absolute right-4 top-4"> <div class="flex items-center absolute right-4 top-4">
<AppDarkModeToggle class="enter-x mr-2" v-if="!sessionTimeout" /> <AppDarkModeToggle class="enter-x mr-2" v-if="!sessionTimeout" />
<AppLocalePicker class="text-white enter-x xl:text-gray-600" :show-text="false" v-if="!sessionTimeout && showLocale" /> <!-- <AppLocalePicker class="text-white enter-x xl:text-gray-600" :show-text="false" v-if="!sessionTimeout && showLocale" />-->
</div> </div>
<span class="-enter-x xl:hidden"> <span class="-enter-x xl:hidden">
@@ -16,10 +16,10 @@
<div class="my-auto"> <div class="my-auto">
<img :alt="title" src="../../../assets/svg/login-box-bg.svg" class="w-1/2 -mt-16 -enter-x" /> <img :alt="title" src="../../../assets/svg/login-box-bg.svg" class="w-1/2 -mt-16 -enter-x" />
<div class="mt-10 font-medium text-white -enter-x"> <div class="mt-10 font-medium text-white -enter-x">
<span class="inline-block mt-4 text-3xl"> {{ t('sys.login.signInTitle') }}</span> <span class="inline-block mt-4 text-3xl"> 开箱即用的中后台管理系统</span>
</div> </div>
<div class="mt-5 font-normal text-white dark:text-gray-500 -enter-x"> <div class="mt-5 font-normal text-white dark:text-gray-500 -enter-x">
{{ t('sys.login.signInDesc') }} 输入您的个人详细信息开始使用
</div> </div>
</div> </div>
</div> </div>
@@ -61,9 +61,9 @@
const globSetting = useGlobSetting() const globSetting = useGlobSetting()
const { prefixCls } = useDesign('login') const { prefixCls } = useDesign('login')
const { t } = useI18n() // const { t } = useI18n()
const localeStore = useLocaleStore() const localeStore = useLocaleStore()
const showLocale = localeStore.getShowPicker // const showLocale = localeStore.getShowPicker
const title = computed(() => globSetting?.title ?? '') const title = computed(() => globSetting?.title ?? '')
</script> </script>
<style lang="less"> <style lang="less">

View File

@@ -2,58 +2,46 @@
<LoginFormTitle v-show="getShow" class="enter-x" /> <LoginFormTitle v-show="getShow" class="enter-x" />
<Form class="p-4 enter-x" :model="formData" :rules="getFormRules" ref="formRef" v-show="getShow" @keypress.enter="handleLogin"> <Form class="p-4 enter-x" :model="formData" :rules="getFormRules" ref="formRef" v-show="getShow" @keypress.enter="handleLogin">
<FormItem name="account" class="enter-x"> <FormItem name="account" class="enter-x">
<Input size="large" v-model:value="formData.account" :placeholder="t('sys.login.userName')" class="fix-auto-fill" /> <Input size="large" v-model:value="formData.account" placeholder="账号" class="fix-auto-fill" />
</FormItem> </FormItem>
<FormItem name="password" class="enter-x"> <FormItem name="password" class="enter-x">
<InputPassword size="large" visibilityToggle v-model:value="formData.password" :placeholder="t('sys.login.password')" /> <InputPassword size="large" visibilityToggle v-model:value="formData.password" placeholder="密码" />
</FormItem> </FormItem>
<ARow class="enter-x"> <ARow class="enter-x">
<ACol :span="12"> <ACol :span="12">
<FormItem> <FormItem>
<!-- No logic, you need to deal with it yourself --> <!-- No logic, you need to deal with it yourself -->
<Checkbox v-model:checked="rememberMe" size="small"> <Checkbox v-model:checked="rememberMe" size="small"> 记住我 </Checkbox>
{{ t('sys.login.rememberMe') }}
</Checkbox>
</FormItem> </FormItem>
</ACol> </ACol>
<ACol :span="12"> <ACol :span="12">
<FormItem :style="{ 'text-align': 'right' }"> <FormItem :style="{ 'text-align': 'right' }">
<!-- No logic, you need to deal with it yourself --> <!-- 没有逻辑你需要自己处理 -->
<Button type="link" size="small" @click="setLoginState(LoginStateEnum.RESET_PASSWORD)"> <Button type="link" size="small" @click="setLoginState(LoginStateEnum.RESET_PASSWORD)"> 忘记密码? </Button>
{{ t('sys.login.forgetPassword') }}
</Button>
</FormItem> </FormItem>
</ACol> </ACol>
</ARow> </ARow>
<FormItem class="enter-x"> <FormItem class="enter-x">
<Button type="primary" size="large" block @click="handleLogin" :loading="loading"> <Button type="primary" size="large" block @click="handleLogin" :loading="loading"> 登录 </Button>
{{ t('sys.login.loginButton') }}
</Button>
<!-- <Button size="large" class="mt-4 enter-x" block @click="handleRegister"> <!-- <Button size="large" class="mt-4 enter-x" block @click="handleRegister">
{{ t('sys.login.registerButton') }} {{ t('sys.login.registerButton') }}
</Button> --> </Button> -->
</FormItem> </FormItem>
<ARow class="enter-x"> <ARow class="enter-x">
<ACol :md="8" :xs="24"> <ACol :md="8" :xs="24">
<Button block @click="setLoginState(LoginStateEnum.MOBILE)"> <Button block @click="setLoginState(LoginStateEnum.MOBILE)"> 手机登录 </Button>
{{ t('sys.login.mobileSignInFormTitle') }}
</Button>
</ACol> </ACol>
<ACol :md="8" :xs="24" class="!my-2 !md:my-0 xs:mx-0 md:mx-2"> <ACol :md="8" :xs="24" class="!my-2 !md:my-0 xs:mx-0 md:mx-2">
<Button block @click="setLoginState(LoginStateEnum.QR_CODE)"> <Button block @click="setLoginState(LoginStateEnum.QR_CODE)"> 二维码登录 </Button>
{{ t('sys.login.qrSignInFormTitle') }}
</Button>
</ACol> </ACol>
<ACol :md="6" :xs="24"> <ACol :md="6" :xs="24">
<Button block @click="setLoginState(LoginStateEnum.REGISTER)"> <Button block @click="setLoginState(LoginStateEnum.REGISTER)"> 注册 </Button>
{{ t('sys.login.registerButton') }}
</Button>
</ACol> </ACol>
</ARow> </ARow>
<Divider class="enter-x">{{ t('sys.login.otherSignIn') }}</Divider> <Divider class="enter-x">其他登录方式</Divider>
<div class="flex justify-evenly enter-x" :class="`${prefixCls}-sign-in-way`"> <div class="flex justify-evenly enter-x" :class="`${prefixCls}-sign-in-way`">
<GithubFilled /> <GithubFilled />
@@ -93,10 +81,10 @@
const formRef = ref() const formRef = ref()
const loading = ref(false) const loading = ref(false)
const rememberMe = ref(false) const rememberMe = ref(true)
const formData = reactive({ const formData = reactive({
account: 'vben', account: 'xxm1995',
password: '123456', password: '123456',
}) })
@@ -111,15 +99,18 @@
if (!data) return if (!data) return
try { try {
loading.value = true loading.value = true
const userInfo = await userStore.login({ const token = await userStore.login({
client: 'admin',
loginType: 'password',
password: data.password, password: data.password,
username: data.account, account: data.account,
mode: 'none', //不要默认的错误提示 captchaKey: data.captchaKey,
captcha: data.captcha,
}) })
if (userInfo) { if (token) {
notification.success({ notification.success({
message: t('sys.login.loginSuccessTitle'), message: t('sys.login.loginSuccessTitle'),
description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realName}`, description: `${t('sys.login.loginSuccessDesc')}`,
duration: 3, duration: 3,
}) })
} }

11
types/store.d.ts vendored
View File

@@ -31,13 +31,14 @@ export interface ErrorLogInfo {
} }
export interface UserInfo { export interface UserInfo {
userId: string | number // 用户id
userId: number
// 名称
name: string
// 账号
username: string username: string
realName: string // 头像
avatar: string avatar: string
desc?: string
homePath?: string
roles: RoleInfo[]
} }
export interface BeforeMiniState { export interface BeforeMiniState {