通用中后台框架第一版
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
import { computed, ref } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { loginApi, logoutApi, meApi } from '@/api/auth'
|
||||
import { listDictItemsApi } from '@/api/system/dict'
|
||||
import type { CurrentUserProfile, LoginRequest, MeResponse, MenuNode } from '@/types/auth'
|
||||
import type { DictItem } from '@/types/system/dict'
|
||||
|
||||
const TOKEN_KEY = 'platform.token'
|
||||
|
||||
export interface TabItem {
|
||||
name: string
|
||||
title: string
|
||||
path: string
|
||||
closable: boolean
|
||||
}
|
||||
|
||||
function normalizeTabPath(path: string) {
|
||||
if (path === '/workbench') return '/dashboard'
|
||||
return path
|
||||
}
|
||||
|
||||
export const useAuthStore = defineStore('auth', () => {
|
||||
const token = ref<string>(localStorage.getItem(TOKEN_KEY) ?? '')
|
||||
const user = ref<CurrentUserProfile | null>(null)
|
||||
const menus = ref<MenuNode[]>([])
|
||||
const permissions = ref<string[]>([])
|
||||
const layoutCollapsed = ref(false)
|
||||
const dictCache = ref<Record<string, DictItem[]>>({})
|
||||
const tabs = ref<TabItem[]>([
|
||||
{ name: 'workbench-home', title: '工作台', path: '/dashboard', closable: false }
|
||||
])
|
||||
const tabRefreshMarks = ref<Record<string, number>>({})
|
||||
const loaded = ref(false)
|
||||
|
||||
const isAuthed = computed(() => Boolean(token.value))
|
||||
|
||||
function setToken(nextToken: string) {
|
||||
token.value = nextToken
|
||||
localStorage.setItem(TOKEN_KEY, nextToken)
|
||||
}
|
||||
|
||||
function clearAuth() {
|
||||
token.value = ''
|
||||
user.value = null
|
||||
menus.value = []
|
||||
permissions.value = []
|
||||
dictCache.value = {}
|
||||
loaded.value = false
|
||||
tabs.value = [{ name: 'workbench-home', title: '工作台', path: '/dashboard', closable: false }]
|
||||
tabRefreshMarks.value = {}
|
||||
localStorage.removeItem(TOKEN_KEY)
|
||||
}
|
||||
|
||||
async function login(payload: LoginRequest) {
|
||||
const result = await loginApi(payload)
|
||||
setToken(result.accessToken)
|
||||
await loadProfile()
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
try {
|
||||
if (token.value) {
|
||||
await logoutApi()
|
||||
}
|
||||
} finally {
|
||||
clearAuth()
|
||||
}
|
||||
}
|
||||
|
||||
async function loadProfile(force = false): Promise<MeResponse | null> {
|
||||
if (!token.value) return null
|
||||
if (loaded.value && !force) {
|
||||
return {
|
||||
user: user.value!,
|
||||
menus: menus.value,
|
||||
permissions: permissions.value
|
||||
}
|
||||
}
|
||||
const profile = await meApi()
|
||||
user.value = profile.user
|
||||
menus.value = profile.menus
|
||||
permissions.value = [...profile.permissions]
|
||||
loaded.value = true
|
||||
return profile
|
||||
}
|
||||
|
||||
function hasPermission(permission?: string | null) {
|
||||
if (!permission) return true
|
||||
return permissions.value.includes(permission)
|
||||
}
|
||||
|
||||
async function loadDictByTypeId(typeId: string, force = false) {
|
||||
const cached = dictCache.value[typeId]
|
||||
if (cached && !force) return cached
|
||||
const result = await listDictItemsApi({ typeId, page: 1, pageSize: 200 })
|
||||
dictCache.value[typeId] = result.items.filter((item) => item.status === 'ENABLED')
|
||||
return dictCache.value[typeId]
|
||||
}
|
||||
|
||||
function getDictLabel(typeId: string, value: string) {
|
||||
const item = dictCache.value[typeId]?.find((entry) => entry.value === value)
|
||||
return item?.label ?? value
|
||||
}
|
||||
|
||||
function getDictColor(typeId: string, value: string) {
|
||||
const item = dictCache.value[typeId]?.find((entry) => entry.value === value)
|
||||
return item?.color ?? undefined
|
||||
}
|
||||
|
||||
function addTab(tab: TabItem) {
|
||||
const normalizedPath = normalizeTabPath(tab.path)
|
||||
if (tabs.value.some((item) => item.path === normalizedPath)) return
|
||||
tabs.value.push({
|
||||
...tab,
|
||||
path: normalizedPath
|
||||
})
|
||||
}
|
||||
|
||||
function closeTab(path: string) {
|
||||
const targetPath = normalizeTabPath(path)
|
||||
const fixed = tabs.value.filter((item) => !item.closable)
|
||||
tabs.value = [
|
||||
...fixed,
|
||||
...tabs.value.filter((item) => item.closable && item.path !== targetPath)
|
||||
]
|
||||
delete tabRefreshMarks.value[targetPath]
|
||||
}
|
||||
|
||||
function closeOtherTabs(path: string) {
|
||||
const targetPath = normalizeTabPath(path)
|
||||
tabs.value = tabs.value.filter((item) => !item.closable || item.path === targetPath)
|
||||
Object.keys(tabRefreshMarks.value).forEach((tabPath) => {
|
||||
if (!tabs.value.some((item) => item.path === tabPath)) {
|
||||
delete tabRefreshMarks.value[tabPath]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function closeAllTabs() {
|
||||
const fixedTabs = tabs.value.filter((item) => !item.closable)
|
||||
const fixedPaths = new Set(fixedTabs.map((item) => item.path))
|
||||
Object.keys(tabRefreshMarks.value).forEach((path) => {
|
||||
if (!fixedPaths.has(path)) {
|
||||
delete tabRefreshMarks.value[path]
|
||||
}
|
||||
})
|
||||
tabs.value = fixedTabs
|
||||
}
|
||||
|
||||
function refreshTab(path: string) {
|
||||
const targetPath = normalizeTabPath(path)
|
||||
tabRefreshMarks.value[targetPath] = Date.now()
|
||||
}
|
||||
|
||||
return {
|
||||
token,
|
||||
user,
|
||||
menus,
|
||||
permissions,
|
||||
layoutCollapsed,
|
||||
dictCache,
|
||||
tabs,
|
||||
tabRefreshMarks,
|
||||
loaded,
|
||||
isAuthed,
|
||||
setToken,
|
||||
clearAuth,
|
||||
login,
|
||||
logout,
|
||||
loadProfile,
|
||||
hasPermission,
|
||||
addTab,
|
||||
closeTab,
|
||||
closeOtherTabs,
|
||||
closeAllTabs,
|
||||
refreshTab,
|
||||
loadDictByTypeId,
|
||||
getDictLabel,
|
||||
getDictColor
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user