feat: init

This commit is contained in:
ygx
2026-03-05 23:45:39 +08:00
commit 8fab91c5c7
214 changed files with 33682 additions and 0 deletions

9
src/store/index.ts Normal file
View File

@@ -0,0 +1,9 @@
import { createPinia } from 'pinia'
import useAppStore from './modules/app'
import useTabBarStore from './modules/tab-bar'
import useUserStore from './modules/user'
const pinia = createPinia()
export { useAppStore, useTabBarStore, useUserStore }
export default pinia

View File

@@ -0,0 +1,77 @@
import { defineStore } from 'pinia'
import { Notification } from '@arco-design/web-vue'
import type { NotificationReturn } from '@arco-design/web-vue/es/notification/interface'
import type { RouteRecordNormalized } from 'vue-router'
import defaultSettings from '@/config/settings.json'
import { getMenuList } from '@/api/user'
import { AppState } from './types'
const useAppStore = defineStore('app', {
state: (): AppState => ({ ...defaultSettings }),
getters: {
appCurrentSetting(state: AppState): AppState {
return { ...state }
},
appDevice(state: AppState) {
return state.device
},
appAsyncMenus(state: AppState): RouteRecordNormalized[] {
return state.serverMenu as unknown as RouteRecordNormalized[]
},
},
actions: {
// Update app settings
updateSettings(partial: Partial<AppState>) {
// @ts-ignore-next-line
this.$patch(partial)
},
// Change theme color
toggleTheme(dark: boolean) {
if (dark) {
this.theme = 'dark'
document.body.setAttribute('arco-theme', 'dark')
} else {
this.theme = 'light'
document.body.removeAttribute('arco-theme')
}
},
toggleDevice(device: string) {
this.device = device
},
toggleMenu(value: boolean) {
this.hideMenu = value
},
async fetchServerMenuConfig() {
let notifyInstance: NotificationReturn | null = null
try {
notifyInstance = Notification.info({
id: 'menuNotice', // Keep the instance id the same
content: 'loading',
closable: true,
})
const { data } = await getMenuList()
this.serverMenu = data
notifyInstance = Notification.success({
id: 'menuNotice',
content: 'success',
closable: true,
})
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
notifyInstance = Notification.error({
id: 'menuNotice',
content: 'error',
closable: true,
})
}
},
clearServerMenu() {
this.serverMenu = []
},
},
})
export default useAppStore

View File

@@ -0,0 +1,20 @@
import type { RouteRecordNormalized } from 'vue-router'
export interface AppState {
theme: string
colorWeak: boolean
navbar: boolean
menu: boolean
topMenu: boolean
hideMenu: boolean
menuCollapse: boolean
footer: boolean
themeColor: string
menuWidth: number
globalSettings: boolean
device: string
tabBar: boolean
menuFromServer: boolean
serverMenu: RouteRecordNormalized[]
[key: string]: unknown
}

View File

@@ -0,0 +1,70 @@
import type { RouteLocationNormalized } from 'vue-router'
import { defineStore } from 'pinia'
import { DEFAULT_ROUTE, DEFAULT_ROUTE_NAME, REDIRECT_ROUTE_NAME } from '@/router/constants'
import { isString } from '@/utils/is'
import { TabBarState, TagProps } from './types'
const formatTag = (route: RouteLocationNormalized): TagProps => {
const { name, meta, fullPath, query } = route
return {
title: meta.locale || '',
name: String(name),
fullPath,
query,
ignoreCache: meta.ignoreCache,
}
}
const BAN_LIST = [REDIRECT_ROUTE_NAME]
const useAppStore = defineStore('tabBar', {
state: (): TabBarState => ({
cacheTabList: new Set([DEFAULT_ROUTE_NAME]),
tagList: [DEFAULT_ROUTE],
}),
getters: {
getTabList(): TagProps[] {
return this.tagList
},
getCacheList(): string[] {
return Array.from(this.cacheTabList)
},
},
actions: {
updateTabList(route: RouteLocationNormalized) {
if (BAN_LIST.includes(route.name as string)) return
this.tagList.push(formatTag(route))
if (!route.meta.ignoreCache) {
this.cacheTabList.add(route.name as string)
}
},
deleteTag(idx: number, tag: TagProps) {
this.tagList.splice(idx, 1)
this.cacheTabList.delete(tag.name)
},
addCache(name: string) {
if (isString(name) && name !== '') this.cacheTabList.add(name)
},
deleteCache(tag: TagProps) {
this.cacheTabList.delete(tag.name)
},
freshTabList(tags: TagProps[]) {
this.tagList = tags
this.cacheTabList.clear()
// 要先判断ignoreCache
this.tagList
.filter((el) => !el.ignoreCache)
.map((el) => el.name)
.forEach((x) => this.cacheTabList.add(x))
},
resetTabList() {
this.tagList = [DEFAULT_ROUTE]
this.cacheTabList.clear()
this.cacheTabList.add(DEFAULT_ROUTE_NAME)
},
},
})
export default useAppStore

View File

@@ -0,0 +1,12 @@
export interface TagProps {
title: string
name: string
fullPath: string
query?: any
ignoreCache?: boolean
}
export interface TabBarState {
tagList: TagProps[]
cacheTabList: Set<string>
}

View File

@@ -0,0 +1,86 @@
import { LoginData, getUserInfo, login as userLogin, logout as userLogout } from '@/api/user'
import { clearToken, setToken } from '@/utils/auth'
import { removeRouteListener } from '@/utils/route-listener'
import { defineStore } from 'pinia'
import useAppStore from '../app'
import { UserState } from './types'
const useUserStore = defineStore('user', {
state: (): UserState => ({
name: undefined,
avatar: undefined,
job: undefined,
organization: undefined,
location: undefined,
email: undefined,
introduction: undefined,
personalWebsite: undefined,
jobName: undefined,
organizationName: undefined,
locationName: undefined,
phone: undefined,
registrationDate: undefined,
accountId: undefined,
certification: undefined,
role: '',
}),
getters: {
userInfo(state: UserState): UserState {
return { ...state }
},
},
actions: {
switchRoles() {
return new Promise((resolve) => {
this.role = this.role === 'user' ? 'admin' : 'user'
resolve(this.role)
})
},
// Set user's information
setInfo(partial: Partial<UserState>) {
this.$patch(partial)
},
// Reset user's information
resetInfo() {
this.$reset()
},
// Get user's information
async info() {
const res = await getUserInfo()
this.setInfo(res.data)
},
// Login
async login(loginForm: LoginData) {
try {
const res = await userLogin(loginForm)
setToken(res.data.token)
} catch (err) {
clearToken()
throw err
}
},
logoutCallBack() {
const appStore = useAppStore()
this.resetInfo()
clearToken()
removeRouteListener()
appStore.clearServerMenu()
},
// Logout
async logout() {
try {
await userLogout()
} finally {
this.logoutCallBack()
}
},
},
})
export default useUserStore

View File

@@ -0,0 +1,19 @@
export type RoleType = '' | '*' | 'admin' | 'user'
export interface UserState {
name?: string
avatar?: string
job?: string
organization?: string
location?: string
email?: string
introduction?: string
personalWebsite?: string
jobName?: string
organizationName?: string
locationName?: string
phone?: string
registrationDate?: string
accountId?: string
certification?: number
role: RoleType
}