1
0
mirror of https://github.com/fumiama/paper-manager.git synced 2026-06-10 10:50:23 +08:00

add frontend/vben from vben-admin-thin

This commit is contained in:
源文雨
2023-03-10 17:18:32 +08:00
parent 30cd57ef76
commit 2a0fdeae31
469 changed files with 42028 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
import type { App } from 'vue'
import { createPinia } from 'pinia'
const store = createPinia()
export function setupStore(app: App<Element>) {
app.use(store)
}
export { store }

View File

@@ -0,0 +1,108 @@
import type {
ProjectConfig,
HeaderSetting,
MenuSetting,
TransitionSetting,
MultiTabsSetting,
} from '/#/config'
import type { BeforeMiniState } from '/#/store'
import { defineStore } from 'pinia'
import { store } from '/@/store'
import { ThemeEnum } from '/@/enums/appEnum'
import { APP_DARK_MODE_KEY_, PROJ_CFG_KEY } from '/@/enums/cacheEnum'
import { Persistent } from '/@/utils/cache/persistent'
import { darkMode } from '/@/settings/designSetting'
import { resetRouter } from '/@/router'
import { deepMerge } from '/@/utils'
interface AppState {
darkMode?: ThemeEnum
// Page loading status
pageLoading: boolean
// project config
projectConfig: ProjectConfig | null
// When the window shrinks, remember some states, and restore these states when the window is restored
beforeMiniInfo: BeforeMiniState
}
let timeId: TimeoutHandle
export const useAppStore = defineStore({
id: 'app',
state: (): AppState => ({
darkMode: undefined,
pageLoading: false,
projectConfig: Persistent.getLocal(PROJ_CFG_KEY),
beforeMiniInfo: {},
}),
getters: {
getPageLoading(): boolean {
return this.pageLoading
},
getDarkMode(): 'light' | 'dark' | string {
return this.darkMode || localStorage.getItem(APP_DARK_MODE_KEY_) || darkMode
},
getBeforeMiniInfo(): BeforeMiniState {
return this.beforeMiniInfo
},
getProjectConfig(): ProjectConfig {
return this.projectConfig || ({} as ProjectConfig)
},
getHeaderSetting(): HeaderSetting {
return this.getProjectConfig.headerSetting
},
getMenuSetting(): MenuSetting {
return this.getProjectConfig.menuSetting
},
getTransitionSetting(): TransitionSetting {
return this.getProjectConfig.transitionSetting
},
getMultiTabsSetting(): MultiTabsSetting {
return this.getProjectConfig.multiTabsSetting
},
},
actions: {
setPageLoading(loading: boolean): void {
this.pageLoading = loading
},
setDarkMode(mode: ThemeEnum): void {
this.darkMode = mode
localStorage.setItem(APP_DARK_MODE_KEY_, mode)
},
setBeforeMiniInfo(state: BeforeMiniState): void {
this.beforeMiniInfo = state
},
setProjectConfig(config: DeepPartial<ProjectConfig>): void {
this.projectConfig = deepMerge(this.projectConfig || {}, config)
Persistent.setLocal(PROJ_CFG_KEY, this.projectConfig)
},
async resetAllState() {
resetRouter()
Persistent.clearAll()
},
async setPageLoadingAction(loading: boolean): Promise<void> {
if (loading) {
clearTimeout(timeId)
// Prevent flicker
timeId = setTimeout(() => {
this.setPageLoading(loading)
}, 50)
} else {
this.setPageLoading(loading)
clearTimeout(timeId)
}
},
},
})
// Need to be used outside the setup
export function useAppStoreWithOut() {
return useAppStore(store)
}

View File

@@ -0,0 +1,55 @@
import type { LocaleSetting, LocaleType } from '/#/config'
import { defineStore } from 'pinia'
import { store } from '/@/store'
import { LOCALE_KEY } from '/@/enums/cacheEnum'
import { createLocalStorage } from '/@/utils/cache'
import { localeSetting } from '/@/settings/localeSetting'
const ls = createLocalStorage()
const lsLocaleSetting = (ls.get(LOCALE_KEY) || localeSetting) as LocaleSetting
interface LocaleState {
localInfo: LocaleSetting
}
export const useLocaleStore = defineStore({
id: 'app-locale',
state: (): LocaleState => ({
localInfo: lsLocaleSetting,
}),
getters: {
getShowPicker(): boolean {
return !!this.localInfo?.showPicker
},
getLocale(): LocaleType {
return this.localInfo?.locale ?? 'zh_CN'
},
},
actions: {
/**
* Set up multilingual information and cache
* @param info multilingual info
*/
setLocaleInfo(info: Partial<LocaleSetting>) {
this.localInfo = { ...this.localInfo, ...info }
ls.set(LOCALE_KEY, this.localInfo)
},
/**
* Initialize multilingual information and load the existing configuration from the local cache
*/
initLocale() {
this.setLocaleInfo({
...localeSetting,
...this.localInfo,
})
},
},
})
// Need to be used outside the setup
export function useLocaleStoreWithOut() {
return useLocaleStore(store)
}

View File

@@ -0,0 +1,359 @@
import type { RouteLocationNormalized, RouteLocationRaw, Router } from 'vue-router'
import { toRaw, unref } from 'vue'
import { defineStore } from 'pinia'
import { store } from '/@/store'
import { useGo, useRedo } from '/@/hooks/web/usePage'
import { Persistent } from '/@/utils/cache/persistent'
import { PageEnum } from '/@/enums/pageEnum'
import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/routes/basic'
import { getRawRoute } from '/@/utils'
import { MULTIPLE_TABS_KEY } from '/@/enums/cacheEnum'
import projectSetting from '/@/settings/projectSetting'
import { useUserStore } from '/@/store/modules/user'
export interface MultipleTabState {
cacheTabList: Set<string>
tabList: RouteLocationNormalized[]
lastDragEndIndex: number
}
function handleGotoPage(router: Router) {
const go = useGo(router)
go(unref(router.currentRoute).path, true)
}
const getToTarget = (tabItem: RouteLocationNormalized) => {
const { params, path, query } = tabItem
return {
params: params || {},
path,
query: query || {},
}
}
const cacheTab = projectSetting.multiTabsSetting.cache
export const useMultipleTabStore = defineStore({
id: 'app-multiple-tab',
state: (): MultipleTabState => ({
// Tabs that need to be cached
cacheTabList: new Set(),
// multiple tab list
tabList: cacheTab ? Persistent.getLocal(MULTIPLE_TABS_KEY) || [] : [],
// Index of the last moved tab
lastDragEndIndex: 0,
}),
getters: {
getTabList(): RouteLocationNormalized[] {
return this.tabList
},
getCachedTabList(): string[] {
return Array.from(this.cacheTabList)
},
getLastDragEndIndex(): number {
return this.lastDragEndIndex
},
},
actions: {
/**
* Update the cache according to the currently opened tabs
*/
async updateCacheTab() {
const cacheMap: Set<string> = new Set()
for (const tab of this.tabList) {
const item = getRawRoute(tab)
// Ignore the cache
const needCache = !item.meta?.ignoreKeepAlive
if (!needCache) {
continue
}
const name = item.name as string
cacheMap.add(name)
}
this.cacheTabList = cacheMap
},
/**
* Refresh tabs
*/
async refreshPage(router: Router) {
const { currentRoute } = router
const route = unref(currentRoute)
const name = route.name
const findTab = this.getCachedTabList.find((item) => item === name)
if (findTab) {
this.cacheTabList.delete(findTab)
}
const redo = useRedo(router)
await redo()
},
clearCacheTabs(): void {
this.cacheTabList = new Set()
},
resetState(): void {
this.tabList = []
this.clearCacheTabs()
},
goToPage(router: Router) {
const go = useGo(router)
const len = this.tabList.length
const { path } = unref(router.currentRoute)
let toPath: PageEnum | string = PageEnum.BASE_HOME
if (len > 0) {
const page = this.tabList[len - 1]
const p = page.fullPath || page.path
if (p) {
toPath = p
}
}
// Jump to the current page and report an error
path !== toPath && go(toPath as PageEnum, true)
},
async addTab(route: RouteLocationNormalized) {
const { path, name, fullPath, params, query, meta } = getRawRoute(route)
// 404 The page does not need to add a tab
if (
path === PageEnum.ERROR_PAGE ||
path === PageEnum.BASE_LOGIN ||
!name ||
[REDIRECT_ROUTE.name, PAGE_NOT_FOUND_ROUTE.name].includes(name as string)
) {
return
}
let updateIndex = -1
// Existing pages, do not add tabs repeatedly
const tabHasExits = this.tabList.some((tab, index) => {
updateIndex = index
return (tab.fullPath || tab.path) === (fullPath || path)
})
// If the tab already exists, perform the update operation
if (tabHasExits) {
const curTab = toRaw(this.tabList)[updateIndex]
if (!curTab) {
return
}
curTab.params = params || curTab.params
curTab.query = query || curTab.query
curTab.fullPath = fullPath || curTab.fullPath
this.tabList.splice(updateIndex, 1, curTab)
} else {
// Add tab
// 获取动态路由打开数,超过 0 即代表需要控制打开数
const dynamicLevel = meta?.dynamicLevel ?? -1
if (dynamicLevel > 0) {
// 如果动态路由层级大于 0 了,那么就要限制该路由的打开数限制了
// 首先获取到真实的路由,使用配置方式减少计算开销.
// const realName: string = path.match(/(\S*)\//)![1];
const realPath = meta?.realPath ?? ''
// 获取到已经打开的动态路由数, 判断是否大于某一个值
if (
this.tabList.filter((e) => e.meta?.realPath ?? '' === realPath).length >= dynamicLevel
) {
// 关闭第一个
const index = this.tabList.findIndex((item) => item.meta.realPath === realPath)
index !== -1 && this.tabList.splice(index, 1)
}
}
this.tabList.push(route)
}
this.updateCacheTab()
cacheTab && Persistent.setLocal(MULTIPLE_TABS_KEY, this.tabList)
},
async closeTab(tab: RouteLocationNormalized, router: Router) {
const close = (route: RouteLocationNormalized) => {
const { fullPath, meta: { affix } = {} } = route
if (affix) {
return
}
const index = this.tabList.findIndex((item) => item.fullPath === fullPath)
index !== -1 && this.tabList.splice(index, 1)
}
const { currentRoute, replace } = router
const { path } = unref(currentRoute)
if (path !== tab.path) {
// Closed is not the activation tab
close(tab)
return
}
// Closed is activated atb
let toTarget: RouteLocationRaw = {}
const index = this.tabList.findIndex((item) => item.path === path)
// If the current is the leftmost tab
if (index === 0) {
// There is only one tab, then jump to the homepage, otherwise jump to the right tab
if (this.tabList.length === 1) {
const userStore = useUserStore()
toTarget = userStore.getUserInfo.homePath || PageEnum.BASE_HOME
} else {
// Jump to the right tab
const page = this.tabList[index + 1]
toTarget = getToTarget(page)
}
} else {
// Close the current tab
const page = this.tabList[index - 1]
toTarget = getToTarget(page)
}
close(currentRoute.value)
await replace(toTarget)
},
// Close according to key
async closeTabByKey(key: string, router: Router) {
const index = this.tabList.findIndex((item) => (item.fullPath || item.path) === key)
if (index !== -1) {
await this.closeTab(this.tabList[index], router)
const { currentRoute, replace } = router
// 检查当前路由是否存在于tabList中
const isActivated = this.tabList.findIndex((item) => {
return item.fullPath === currentRoute.value.fullPath
})
// 如果当前路由不存在于TabList中尝试切换到其它路由
if (isActivated === -1) {
let pageIndex
if (index > 0) {
pageIndex = index - 1
} else if (index < this.tabList.length - 1) {
pageIndex = index + 1
} else {
pageIndex = -1
}
if (pageIndex >= 0) {
const page = this.tabList[index - 1]
const toTarget = getToTarget(page)
await replace(toTarget)
}
}
}
},
// Sort the tabs
async sortTabs(oldIndex: number, newIndex: number) {
const currentTab = this.tabList[oldIndex]
this.tabList.splice(oldIndex, 1)
this.tabList.splice(newIndex, 0, currentTab)
this.lastDragEndIndex = this.lastDragEndIndex + 1
},
// Close the tab on the right and jump
async closeLeftTabs(route: RouteLocationNormalized, router: Router) {
const index = this.tabList.findIndex((item) => item.path === route.path)
if (index > 0) {
const leftTabs = this.tabList.slice(0, index)
const pathList: string[] = []
for (const item of leftTabs) {
const affix = item?.meta?.affix ?? false
if (!affix) {
pathList.push(item.fullPath)
}
}
this.bulkCloseTabs(pathList)
}
this.updateCacheTab()
handleGotoPage(router)
},
// Close the tab on the left and jump
async closeRightTabs(route: RouteLocationNormalized, router: Router) {
const index = this.tabList.findIndex((item) => item.fullPath === route.fullPath)
if (index >= 0 && index < this.tabList.length - 1) {
const rightTabs = this.tabList.slice(index + 1, this.tabList.length)
const pathList: string[] = []
for (const item of rightTabs) {
const affix = item?.meta?.affix ?? false
if (!affix) {
pathList.push(item.fullPath)
}
}
this.bulkCloseTabs(pathList)
}
this.updateCacheTab()
handleGotoPage(router)
},
async closeAllTab(router: Router) {
this.tabList = this.tabList.filter((item) => item?.meta?.affix ?? false)
this.clearCacheTabs()
this.goToPage(router)
},
/**
* Close other tabs
*/
async closeOtherTabs(route: RouteLocationNormalized, router: Router) {
const closePathList = this.tabList.map((item) => item.fullPath)
const pathList: string[] = []
for (const path of closePathList) {
if (path !== route.fullPath) {
const closeItem = this.tabList.find((item) => item.path === path)
if (!closeItem) {
continue
}
const affix = closeItem?.meta?.affix ?? false
if (!affix) {
pathList.push(closeItem.fullPath)
}
}
}
this.bulkCloseTabs(pathList)
this.updateCacheTab()
handleGotoPage(router)
},
/**
* Close tabs in bulk
*/
async bulkCloseTabs(pathList: string[]) {
this.tabList = this.tabList.filter((item) => !pathList.includes(item.fullPath))
},
/**
* Set tab's title
*/
async setTabTitle(title: string, route: RouteLocationNormalized) {
const findTab = this.getTabList.find((item) => item === route)
if (findTab) {
findTab.meta.title = title
await this.updateCacheTab()
}
},
/**
* replace tab's path
* **/
async updateTabPath(fullPath: string, route: RouteLocationNormalized) {
const findTab = this.getTabList.find((item) => item === route)
if (findTab) {
findTab.fullPath = fullPath
findTab.path = fullPath
await this.updateCacheTab()
}
},
},
})
// Need to be used outside the setup
export function useMultipleTabWithOutStore() {
return useMultipleTabStore(store)
}

View File

@@ -0,0 +1,259 @@
import type { AppRouteRecordRaw, Menu } from '/@/router/types'
import { defineStore } from 'pinia'
import { store } from '/@/store'
import { useI18n } from '/@/hooks/web/useI18n'
import { useUserStore } from './user'
import { useAppStoreWithOut } from './app'
import { toRaw } from 'vue'
import { transformObjToRoute, flatMultiLevelRoutes } from '/@/router/helper/routeHelper'
import { transformRouteToMenu } from '/@/router/helper/menuHelper'
import projectSetting from '/@/settings/projectSetting'
import { PermissionModeEnum } from '/@/enums/appEnum'
import { asyncRoutes } from '/@/router/routes'
import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic'
import { filter } from '/@/utils/helper/treeHelper'
import { getMenuList } from '/@/api/sys/menu'
import { getPermCode } from '/@/api/sys/user'
import { useMessage } from '/@/hooks/web/useMessage'
import { PageEnum } from '/@/enums/pageEnum'
interface PermissionState {
// Permission code list
// 权限代码列表
permCodeList: string[] | number[]
// Whether the route has been dynamically added
// 路由是否动态添加
isDynamicAddedRoute: boolean
// To trigger a menu update
// 触发菜单更新
lastBuildMenuTime: number
// Backstage menu list
// 后台菜单列表
backMenuList: Menu[]
// 菜单列表
frontMenuList: Menu[]
}
export const usePermissionStore = defineStore({
id: 'app-permission',
state: (): PermissionState => ({
// 权限代码列表
permCodeList: [],
// Whether the route has been dynamically added
// 路由是否动态添加
isDynamicAddedRoute: false,
// To trigger a menu update
// 触发菜单更新
lastBuildMenuTime: 0,
// Backstage menu list
// 后台菜单列表
backMenuList: [],
// menu List
// 菜单列表
frontMenuList: [],
}),
getters: {
getPermCodeList(): string[] | number[] {
return this.permCodeList
},
getBackMenuList(): Menu[] {
return this.backMenuList
},
getFrontMenuList(): Menu[] {
return this.frontMenuList
},
getLastBuildMenuTime(): number {
return this.lastBuildMenuTime
},
getIsDynamicAddedRoute(): boolean {
return this.isDynamicAddedRoute
},
},
actions: {
setPermCodeList(codeList: string[]) {
this.permCodeList = codeList
},
setBackMenuList(list: Menu[]) {
this.backMenuList = list
list?.length > 0 && this.setLastBuildMenuTime()
},
setFrontMenuList(list: Menu[]) {
this.frontMenuList = list
},
setLastBuildMenuTime() {
this.lastBuildMenuTime = new Date().getTime()
},
setDynamicAddedRoute(added: boolean) {
this.isDynamicAddedRoute = added
},
resetState(): void {
this.isDynamicAddedRoute = false
this.permCodeList = []
this.backMenuList = []
this.lastBuildMenuTime = 0
},
async changePermissionCode() {
const codeList = await getPermCode()
this.setPermCodeList(codeList)
},
// 构建路由
async buildRoutesAction(): Promise<AppRouteRecordRaw[]> {
const { t } = useI18n()
const userStore = useUserStore()
const appStore = useAppStoreWithOut()
let routes: AppRouteRecordRaw[] = []
const roleList = toRaw(userStore.getRoleList) || []
const { permissionMode = projectSetting.permissionMode } = appStore.getProjectConfig
// 路由过滤器 在 函数filter 作为回调传入遍历使用
const routeFilter = (route: AppRouteRecordRaw) => {
const { meta } = route
// 抽出角色
const { roles } = meta || {}
if (!roles) return true
// 进行角色权限判断
return roleList.some((role) => roles.includes(role))
}
const routeRemoveIgnoreFilter = (route: AppRouteRecordRaw) => {
const { meta } = route
// ignoreRoute 为true 则路由仅用于菜单生成,不会在实际的路由表中出现
const { ignoreRoute } = meta || {}
// arr.filter 返回 true 表示该元素通过测试
return !ignoreRoute
}
/**
* @description 根据设置的首页path修正routes中的affix标记固定首页
* */
const patchHomeAffix = (routes: AppRouteRecordRaw[]) => {
if (!routes || routes.length === 0) return
let homePath: string = userStore.getUserInfo.homePath || PageEnum.BASE_HOME
function patcher(routes: AppRouteRecordRaw[], parentPath = '') {
if (parentPath) parentPath = parentPath + '/'
routes.forEach((route: AppRouteRecordRaw) => {
const { path, children, redirect } = route
const currentPath = path.startsWith('/') ? path : parentPath + path
if (currentPath === homePath) {
if (redirect) {
homePath = route.redirect! as string
} else {
route.meta = Object.assign({}, route.meta, { affix: true })
throw new Error('end')
}
}
children && children.length > 0 && patcher(children, currentPath)
})
}
try {
patcher(routes)
} catch (e) {
// 已处理完毕跳出循环
}
return
}
switch (permissionMode) {
// 角色权限
case PermissionModeEnum.ROLE:
// 对非一级路由进行过滤
routes = filter(asyncRoutes, routeFilter)
// 对一级路由根据角色权限过滤
routes = routes.filter(routeFilter)
// Convert multi-level routing to level 2 routing
// 将多级路由转换为 2 级路由
routes = flatMultiLevelRoutes(routes)
break
// 路由映射, 默认进入该case
case PermissionModeEnum.ROUTE_MAPPING:
// 对非一级路由进行过滤
routes = filter(asyncRoutes, routeFilter)
// 对一级路由再次根据角色权限过滤
routes = routes.filter(routeFilter)
// 将路由转换成菜单
const menuList = transformRouteToMenu(routes, true)
// 移除掉 ignoreRoute: true 的路由 非一级路由
routes = filter(routes, routeRemoveIgnoreFilter)
// 移除掉 ignoreRoute: true 的路由 一级路由;
routes = routes.filter(routeRemoveIgnoreFilter)
// 对菜单进行排序
menuList.sort((a, b) => {
return (a.meta?.orderNo || 0) - (b.meta?.orderNo || 0)
})
// 设置菜单列表
this.setFrontMenuList(menuList)
// Convert multi-level routing to level 2 routing
// 将多级路由转换为 2 级路由
routes = flatMultiLevelRoutes(routes)
break
// If you are sure that you do not need to do background dynamic permissions, please comment the entire judgment below
// 如果确定不需要做后台动态权限,请在下方评论整个判断
case PermissionModeEnum.BACK:
const { createMessage } = useMessage()
createMessage.loading({
content: t('sys.app.menuLoading'),
duration: 1,
})
// !Simulate to obtain permission codes from the background,
// 模拟从后台获取权限码,
// this function may only need to be executed once, and the actual project can be put at the right time by itself
// 这个功能可能只需要执行一次,实际项目可以自己放在合适的时间
let routeList: AppRouteRecordRaw[] = []
try {
await this.changePermissionCode()
routeList = (await getMenuList()) as AppRouteRecordRaw[]
} catch (error) {
console.error(error)
}
// Dynamically introduce components
// 动态引入组件
routeList = transformObjToRoute(routeList)
// Background routing to menu structure
// 后台路由到菜单结构
const backMenuList = transformRouteToMenu(routeList)
this.setBackMenuList(backMenuList)
// remove meta.ignoreRoute item
// 删除 meta.ignoreRoute 项
routeList = filter(routeList, routeRemoveIgnoreFilter)
routeList = routeList.filter(routeRemoveIgnoreFilter)
routeList = flatMultiLevelRoutes(routeList)
routes = [PAGE_NOT_FOUND_ROUTE, ...routeList]
break
}
patchHomeAffix(routes)
return routes
},
},
})
// Need to be used outside the setup
// 需要在设置之外使用
export function usePermissionStoreWithOut() {
return usePermissionStore(store)
}

View File

@@ -0,0 +1,177 @@
import type { UserInfo } from '/#/store'
import type { ErrorMessageMode } from '/#/axios'
import { defineStore } from 'pinia'
import { store } from '/@/store'
import { RoleEnum } from '/@/enums/roleEnum'
import { PageEnum } from '/@/enums/pageEnum'
import { ROLES_KEY, TOKEN_KEY, USER_INFO_KEY } from '/@/enums/cacheEnum'
import { getAuthCache, setAuthCache } from '/@/utils/auth'
import { GetUserInfoModel, LoginParams } from '/@/api/sys/model/userModel'
import { doLogout, getUserInfo, loginApi } from '/@/api/sys/user'
import { useI18n } from '/@/hooks/web/useI18n'
import { useMessage } from '/@/hooks/web/useMessage'
import { router } from '/@/router'
import { usePermissionStore } from '/@/store/modules/permission'
import { RouteRecordRaw } from 'vue-router'
import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic'
import { isArray } from '/@/utils/is'
import { h } from 'vue'
interface UserState {
userInfo: Nullable<UserInfo>
token?: string
roleList: RoleEnum[]
sessionTimeout?: boolean
lastUpdateTime: number
}
export const useUserStore = defineStore({
id: 'app-user',
state: (): UserState => ({
// user info
userInfo: null,
// token
token: undefined,
// roleList
roleList: [],
// Whether the login expired
sessionTimeout: false,
// Last fetch time
lastUpdateTime: 0,
}),
getters: {
getUserInfo(): UserInfo {
return this.userInfo || getAuthCache<UserInfo>(USER_INFO_KEY) || {}
},
getToken(): string {
return this.token || getAuthCache<string>(TOKEN_KEY)
},
getRoleList(): RoleEnum[] {
return this.roleList.length > 0 ? this.roleList : getAuthCache<RoleEnum[]>(ROLES_KEY)
},
getSessionTimeout(): boolean {
return !!this.sessionTimeout
},
getLastUpdateTime(): number {
return this.lastUpdateTime
},
},
actions: {
setToken(info: string | undefined) {
this.token = info ? info : '' // for null or undefined value
setAuthCache(TOKEN_KEY, info)
},
setRoleList(roleList: RoleEnum[]) {
this.roleList = roleList
setAuthCache(ROLES_KEY, roleList)
},
setUserInfo(info: UserInfo | null) {
this.userInfo = info
this.lastUpdateTime = new Date().getTime()
setAuthCache(USER_INFO_KEY, info)
},
setSessionTimeout(flag: boolean) {
this.sessionTimeout = flag
},
resetState() {
this.userInfo = null
this.token = ''
this.roleList = []
this.sessionTimeout = false
},
/**
* @description: login
*/
async login(
params: LoginParams & {
goHome?: boolean
mode?: ErrorMessageMode
},
): Promise<GetUserInfoModel | null> {
try {
const { goHome = true, mode, ...loginParams } = params
const data = await loginApi(loginParams, mode)
const { token } = data
// save token
this.setToken(token)
return this.afterLoginAction(goHome)
} catch (error) {
return Promise.reject(error)
}
},
async afterLoginAction(goHome?: boolean): Promise<GetUserInfoModel | null> {
if (!this.getToken) return null
// get user info
const userInfo = await this.getUserInfoAction()
const sessionTimeout = this.sessionTimeout
if (sessionTimeout) {
this.setSessionTimeout(false)
} else {
const permissionStore = usePermissionStore()
if (!permissionStore.isDynamicAddedRoute) {
const routes = await permissionStore.buildRoutesAction()
routes.forEach((route) => {
router.addRoute(route as unknown as RouteRecordRaw)
})
router.addRoute(PAGE_NOT_FOUND_ROUTE as unknown as RouteRecordRaw)
permissionStore.setDynamicAddedRoute(true)
}
goHome && (await router.replace(userInfo?.homePath || PageEnum.BASE_HOME))
}
return userInfo
},
async getUserInfoAction(): Promise<UserInfo | null> {
if (!this.getToken) return null
const 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)
return userInfo
},
/**
* @description: logout
*/
async logout(goLogin = false) {
if (this.getToken) {
try {
await doLogout()
} catch {
console.log('注销Token失败')
}
}
this.setToken(undefined)
this.setSessionTimeout(false)
this.setUserInfo(null)
goLogin && router.push(PageEnum.BASE_LOGIN)
},
/**
* @description: Confirm before logging out
*/
confirmLoginOut() {
const { createConfirm } = useMessage()
const { t } = useI18n()
createConfirm({
iconType: 'warning',
title: () => h('span', t('sys.app.Tip')),
content: () => h('span', t('sys.app.logoutMessage')),
onOk: async () => {
await this.logout(true)
},
})
},
},
})
// Need to be used outside the setup
export function useUserStoreWithOut() {
return useUserStore(store)
}