mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2025-11-11 02:57:09 +08:00
first commit
This commit is contained in:
13
src/stores/index.ts
Normal file
13
src/stores/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { createPinia } from 'pinia'
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||
|
||||
export * from './modules/app'
|
||||
export * from './modules/route'
|
||||
export * from './modules/tabs'
|
||||
export * from './modules/dict'
|
||||
export * from './modules/user'
|
||||
|
||||
const pinia = createPinia()
|
||||
pinia.use(piniaPluginPersistedstate)
|
||||
|
||||
export default pinia
|
||||
68
src/stores/modules/app.ts
Normal file
68
src/stores/modules/app.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, reactive, toRefs } from 'vue'
|
||||
import { generate, getRgbStr } from '@arco-design/color'
|
||||
import defaultSettings from '@/config/setting.json'
|
||||
|
||||
const storeSetup = () => {
|
||||
// App配置
|
||||
const settingConfig = reactive({ ...defaultSettings }) as App.SettingConfig
|
||||
|
||||
// 页面切换动画类名
|
||||
const transitionName = computed(() => (settingConfig.animate ? settingConfig.animateMode : ''))
|
||||
|
||||
// 深色菜单主题色变量
|
||||
const themeCSSVar = computed<Record<string, string>>(() => {
|
||||
const obj: Record<string, string> = {}
|
||||
const list = generate(settingConfig.themeColor, { list: true, dark: true })
|
||||
list.forEach((color: string, index: number) => {
|
||||
obj[`--primary-${index + 1}`] = getRgbStr(color)
|
||||
})
|
||||
return obj
|
||||
})
|
||||
|
||||
// 切换主题 暗黑模式|简白模式
|
||||
const toggleTheme = (dark: boolean) => {
|
||||
if (dark) {
|
||||
settingConfig.theme = 'dark'
|
||||
document.body.setAttribute('arco-theme', 'dark')
|
||||
} else {
|
||||
settingConfig.theme = 'light'
|
||||
document.body.removeAttribute('arco-theme')
|
||||
}
|
||||
setThemeColor(settingConfig.themeColor)
|
||||
}
|
||||
|
||||
// 设置主题色
|
||||
const setThemeColor = (color: string) => {
|
||||
if (!color) return
|
||||
settingConfig.themeColor = color
|
||||
const list = generate(settingConfig.themeColor, { list: true, dark: settingConfig.theme === 'dark' })
|
||||
list.forEach((color: string, index: number) => {
|
||||
const rgbStr = getRgbStr(color)
|
||||
document.body.style.setProperty(`--primary-${index + 1}`, rgbStr)
|
||||
})
|
||||
}
|
||||
|
||||
// 初始化主题
|
||||
const initTheme = () => {
|
||||
if (!settingConfig.themeColor) return
|
||||
setThemeColor(settingConfig.themeColor)
|
||||
}
|
||||
|
||||
// 设置左侧菜单折叠状态
|
||||
const setMenuCollapse = (collapsed: boolean) => {
|
||||
settingConfig.menuCollapse = collapsed
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(settingConfig),
|
||||
transitionName,
|
||||
themeCSSVar,
|
||||
toggleTheme,
|
||||
setThemeColor,
|
||||
initTheme,
|
||||
setMenuCollapse
|
||||
}
|
||||
}
|
||||
|
||||
export const useAppStore = defineStore('app', storeSetup, { persist: true })
|
||||
57
src/stores/modules/dict.ts
Normal file
57
src/stores/modules/dict.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import type { DictState, LabelValueState } from '@/types/global'
|
||||
|
||||
const storeSetup = () => {
|
||||
const dictData = ref<DictState[]>([])
|
||||
|
||||
// 设置字典
|
||||
const setDict = (_code: string, items: Array<LabelValueState>) => {
|
||||
if (_code !== null && _code !== '') {
|
||||
dictData.value.push({
|
||||
code: _code,
|
||||
items
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 获取字典
|
||||
const getDict = (_code: string) => {
|
||||
if (_code == null || _code === '') {
|
||||
return null
|
||||
}
|
||||
for (let i = 0; i < dictData.value.length; i += 1) {
|
||||
if (dictData.value[i].code === _code) {
|
||||
return dictData.value[i].items
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// 删除字典
|
||||
const deleteDict = (_code: string) => {
|
||||
let bln = false
|
||||
try {
|
||||
for (let i = 0; i < dictData.value.length; i += 1) {
|
||||
if (dictData.value[i].code === _code) {
|
||||
dictData.value.splice(i, 1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
bln = false
|
||||
}
|
||||
return bln
|
||||
}
|
||||
// 清空字典
|
||||
const cleanDict = () => {
|
||||
dictData.value = []
|
||||
}
|
||||
return {
|
||||
setDict,
|
||||
getDict,
|
||||
deleteDict,
|
||||
cleanDict
|
||||
}
|
||||
}
|
||||
|
||||
export const useDictStore = defineStore('dict', storeSetup, { persist: true })
|
||||
100
src/stores/modules/route.ts
Normal file
100
src/stores/modules/route.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { ref, toRaw } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import { constantRoutes } from '@/router'
|
||||
import Layout from '@/layout/index.vue'
|
||||
import ParentView from '@/components/ParentView/index.vue'
|
||||
import { getUserRoute, type RouteItem } from '@/apis'
|
||||
import { mapTree } from 'xe-utils'
|
||||
import { transformPathToName } from '@/utils'
|
||||
|
||||
// 匹配views里面所有的.vue文件
|
||||
const modules = import.meta.glob('@/views/**/*.vue')
|
||||
|
||||
/** 加载模块 */
|
||||
export const loadView = (view: string) => {
|
||||
let res
|
||||
for (const path in modules) {
|
||||
const dir = path.split('views/')[1].split('.vue')[0]
|
||||
if (dir === view) {
|
||||
res = () => modules[path]()
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/** 将component由字符串转成真正的模块 */
|
||||
const transformComponentView = (component: string) => {
|
||||
if (component === 'Layout') {
|
||||
return Layout as never
|
||||
} else if (component === 'ParentView') {
|
||||
return ParentView as never
|
||||
} else {
|
||||
return loadView(component) as never
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 前端来做排序、格式化
|
||||
* @params {menus} 后端返回的路由数据,已经根据当前用户角色过滤掉了没权限的路由
|
||||
* 1. 对后端返回的路由数据进行排序,格式化
|
||||
* 2. 同时将component由字符串转成真正的模块
|
||||
*/
|
||||
const formatAsyncRoutes = (menus: RouteItem[]) => {
|
||||
if (!menus.length) return []
|
||||
menus.sort((a, b) => (a?.sort ?? 0) - (b?.sort ?? 0)) // 排序
|
||||
const routes = mapTree(menus, (item) => {
|
||||
if (item.children && item.children.length) {
|
||||
item.children.sort((a, b) => (a?.sort ?? 0) - (b?.sort ?? 0)) // 排序
|
||||
}
|
||||
return {
|
||||
path: item.path,
|
||||
name: item.name ?? transformPathToName(item.path),
|
||||
component: transformComponentView(item.component),
|
||||
redirect: item.redirect,
|
||||
meta: {
|
||||
title: item.title,
|
||||
hidden: item.isHidden,
|
||||
keepAlive: item.isCache,
|
||||
alwaysShow: item.type == 1,
|
||||
svgIcon: item.icon.startsWith('svg-') ? item.icon : '',
|
||||
icon: item.icon.startsWith('icon-') ? item.icon : ''
|
||||
}
|
||||
}
|
||||
})
|
||||
return routes as RouteRecordRaw[]
|
||||
}
|
||||
|
||||
const storeSetup = () => {
|
||||
// 所有路由(常驻路由 + 动态路由)
|
||||
const routes = ref<RouteRecordRaw[]>([])
|
||||
// 动态路由(异步路由)
|
||||
const asyncRoutes = ref<RouteRecordRaw[]>([])
|
||||
|
||||
// 合并路由
|
||||
const setRoutes = (data: RouteRecordRaw[]) => {
|
||||
routes.value = constantRoutes.concat(data)
|
||||
asyncRoutes.value = data
|
||||
console.log('路由', toRaw(routes.value))
|
||||
}
|
||||
|
||||
// 生成路由
|
||||
const generateRoutes = (): Promise<RouteRecordRaw[]> => {
|
||||
return new Promise((resolve) => {
|
||||
// 向后端请求路由数据 这个接口已经根据用户角色过滤了没权限的路由(后端根据用户角色过滤路由显得比较安全些)
|
||||
getUserRoute().then((res) => {
|
||||
const asyncRoutes = formatAsyncRoutes(res.data)
|
||||
setRoutes(asyncRoutes)
|
||||
resolve(asyncRoutes)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
routes,
|
||||
asyncRoutes,
|
||||
generateRoutes
|
||||
}
|
||||
}
|
||||
|
||||
export const useRouteStore = defineStore('route', storeSetup, { persist: true })
|
||||
115
src/stores/modules/tabs.ts
Normal file
115
src/stores/modules/tabs.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import router from '@/router'
|
||||
import type { RouteRecordRaw, RouteRecordName } from 'vue-router'
|
||||
import { useRouteStore } from '@/stores'
|
||||
import _XEUtils_ from 'xe-utils'
|
||||
|
||||
const storeSetup = () => {
|
||||
const tagList = ref<RouteRecordRaw[]>([]) // 保存页签tab的数组
|
||||
const cacheList = ref<RouteRecordName[]>([]) // keep-alive缓存的数组, 元素是组件名
|
||||
|
||||
// 添加一个页签, 如果当前路由已经打开, 则不再重复添加
|
||||
const addTagItem = (item: RouteRecordRaw) => {
|
||||
if (tagList.value.some((i) => i.path === item.path)) return
|
||||
if (item.meta?.showInTabs ?? true) {
|
||||
tagList.value.push(item)
|
||||
}
|
||||
}
|
||||
|
||||
// 删除一个页签
|
||||
const deleteTagItem = (path: string) => {
|
||||
const index = tagList.value.findIndex((item) => item.path === path && !item.meta?.affix)
|
||||
if (index >= 0) {
|
||||
const isActive = router.currentRoute.value.path === tagList.value[index]['path']
|
||||
tagList.value.splice(index, 1)
|
||||
if (isActive) {
|
||||
router.push({ path: tagList.value[tagList.value.length - 1]['path'] })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 清空页签
|
||||
const clearTagList = () => {
|
||||
const routeStore = useRouteStore()
|
||||
const arr: RouteRecordRaw[] = []
|
||||
_XEUtils_.eachTree(routeStore.routes, (item) => {
|
||||
if (item.meta?.affix ?? false) {
|
||||
arr.push(item)
|
||||
}
|
||||
})
|
||||
tagList.value = arr
|
||||
}
|
||||
|
||||
// 添加缓存页
|
||||
const addCacheItem = (item: RouteRecordRaw) => {
|
||||
if (item.name) {
|
||||
if (cacheList.value.includes(item.name)) return
|
||||
if (item.meta?.keepAlive) {
|
||||
cacheList.value.push(item.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除一个缓存页
|
||||
const deleteCacheItem = (name: RouteRecordName) => {
|
||||
const index = cacheList.value.findIndex((item) => item === name)
|
||||
if (index >= 0) {
|
||||
cacheList.value.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// 清空缓存页
|
||||
const clearCacheList = () => {
|
||||
cacheList.value = []
|
||||
}
|
||||
|
||||
// 关闭当前
|
||||
const closeCurrent = (path: string) => {
|
||||
deleteTagItem(path)
|
||||
const item = tagList.value.find((i) => i.path === path)
|
||||
if (item?.name) {
|
||||
deleteCacheItem(item.name)
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭其他
|
||||
const closeOther = (path: string) => {
|
||||
const arr = tagList.value.filter((i) => i.path !== path)
|
||||
arr.forEach((item) => {
|
||||
deleteTagItem(item.path)
|
||||
if (item?.name) {
|
||||
deleteCacheItem(item.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 关闭全部
|
||||
const closeAll = () => {
|
||||
clearTagList()
|
||||
router.push({ path: '/' })
|
||||
}
|
||||
|
||||
// 重置
|
||||
const reset = () => {
|
||||
clearTagList()
|
||||
clearCacheList()
|
||||
}
|
||||
|
||||
return {
|
||||
tagList,
|
||||
cacheList,
|
||||
addTagItem,
|
||||
deleteTagItem,
|
||||
clearTagList,
|
||||
addCacheItem,
|
||||
deleteCacheItem,
|
||||
clearCacheList,
|
||||
closeCurrent,
|
||||
closeOther,
|
||||
closeAll,
|
||||
reset
|
||||
}
|
||||
}
|
||||
|
||||
export const useTabsStore = defineStore('tabs', storeSetup, { persist: false })
|
||||
83
src/stores/modules/user.ts
Normal file
83
src/stores/modules/user.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import { resetRouter } from '@/router'
|
||||
import { accountLogin as accountLoginApi, logout as logoutApi, getUserInfo as getUserInfoApi } from '@/apis'
|
||||
import type { UserInfo } from '@/apis'
|
||||
import { setToken, clearToken, getToken } from '@/utils/auth'
|
||||
import { resetHasRouteFlag } from '@/router/permission'
|
||||
import getAvatar from '@/utils/avatar'
|
||||
|
||||
const storeSetup = () => {
|
||||
const userInfo = reactive<Pick<UserInfo, 'nickname' | 'avatar'>>({
|
||||
nickname: '',
|
||||
avatar: ''
|
||||
})
|
||||
const name = computed(() => userInfo.nickname)
|
||||
const avatar = computed(() => userInfo.avatar)
|
||||
|
||||
const token = ref(getToken() || '')
|
||||
const roles = ref<string[]>([]) // 当前用户角色
|
||||
const permissions = ref<string[]>([]) // 当前角色权限标识集合
|
||||
|
||||
// 重置token
|
||||
const resetToken = () => {
|
||||
token.value = ''
|
||||
clearToken()
|
||||
resetHasRouteFlag()
|
||||
}
|
||||
|
||||
// 登录
|
||||
const accountLogin = async (params: any) => {
|
||||
const res = await accountLoginApi(params)
|
||||
setToken(res.data.token)
|
||||
token.value = res.data.token
|
||||
}
|
||||
|
||||
// 退出登录
|
||||
const logout = async () => {
|
||||
try {
|
||||
await logoutApi()
|
||||
await logoutCallBack()
|
||||
return true
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 退出登录回调
|
||||
const logoutCallBack = async () => {
|
||||
roles.value = []
|
||||
permissions.value = []
|
||||
resetToken()
|
||||
resetRouter()
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
const getInfo = async () => {
|
||||
const res = await getUserInfoApi()
|
||||
userInfo.nickname = res.data.nickname
|
||||
userInfo.avatar = getAvatar(res.data.avatar, res.data.gender)
|
||||
if (res.data.roles && res.data.roles.length) {
|
||||
roles.value = res.data.roles
|
||||
permissions.value = res.data.permissions
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
userInfo,
|
||||
name,
|
||||
avatar,
|
||||
token,
|
||||
roles,
|
||||
permissions,
|
||||
accountLogin,
|
||||
logout,
|
||||
logoutCallBack,
|
||||
getInfo,
|
||||
resetToken
|
||||
}
|
||||
}
|
||||
|
||||
export const useUserStore = defineStore('user', storeSetup, {
|
||||
persist: { paths: ['token', 'roles', 'permissions'], storage: localStorage }
|
||||
})
|
||||
Reference in New Issue
Block a user