通用中后台框架第一版

This commit is contained in:
BBIT-Kai
2026-04-28 16:27:16 +08:00
commit b8d25869c6
115 changed files with 15223 additions and 0 deletions
+181
View File
@@ -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
}
})