init Project

This commit is contained in:
2025-04-09 12:10:46 +08:00
parent 505d08443c
commit 75a1447d66
207 changed files with 26387 additions and 13 deletions

8
src/pinia/index.js Normal file
View File

@@ -0,0 +1,8 @@
import { createPinia } from 'pinia'
import { useAppStore } from '@/pinia/modules/app'
import { useUserStore } from '@/pinia/modules/user'
import { useDictionaryStore } from '@/pinia/modules/dictionary'
const store = createPinia()
export { store, useAppStore, useUserStore, useDictionaryStore }

133
src/pinia/modules/app.js Normal file
View File

@@ -0,0 +1,133 @@
import { defineStore } from 'pinia'
import { ref, watchEffect, reactive } from 'vue'
import { setBodyPrimaryColor } from '@/utils/format'
import { useDark, usePreferredDark } from '@vueuse/core'
export const useAppStore = defineStore('app', () => {
const device = ref('')
const drawerSize = ref('')
const operateMinWith = ref('240')
const config = reactive({
weakness: false,
grey: false,
primaryColor: '#3b82f6',
showTabs: true,
darkMode: 'auto',
layout_side_width: 256,
layout_side_collapsed_width: 80,
layout_side_item_height: 48,
show_watermark: true,
side_mode: 'normal',
// 页面过渡动画配置
transition_type: 'slide'
})
const isDark = useDark({
selector: 'html',
attribute: 'class',
valueDark: 'dark',
valueLight: 'light'
})
const preferredDark = usePreferredDark()
const toggleTheme = (darkMode) => {
isDark.value = darkMode
}
const toggleWeakness = (e) => {
config.weakness = e
}
const toggleGrey = (e) => {
config.grey = e
}
const togglePrimaryColor = (e) => {
config.primaryColor = e
}
const toggleTabs = (e) => {
config.showTabs = e
}
const toggleDevice = (e) => {
if (e === 'mobile') {
drawerSize.value = '100%'
operateMinWith.value = '80'
} else {
drawerSize.value = '800'
operateMinWith.value = '240'
}
device.value = e
}
const toggleDarkMode = (e) => {
config.darkMode = e
}
// 监听系统主题变化
watchEffect(() => {
if (config.darkMode === 'auto') {
isDark.value = preferredDark.value
return
}
isDark.value = config.darkMode === 'dark'
})
const toggleConfigSideWidth = (e) => {
config.layout_side_width = e
}
const toggleConfigSideCollapsedWidth = (e) => {
config.layout_side_collapsed_width = e
}
const toggleConfigSideItemHeight = (e) => {
config.layout_side_item_height = e
}
const toggleConfigWatermark = (e) => {
config.show_watermark = e
}
const toggleSideMode = (e) => {
config.side_mode = e
}
const toggleTransition = (e) => {
config.transition_type = e
}
// 监听色弱模式和灰色模式
watchEffect(() => {
document.documentElement.classList.toggle('html-weakenss', config.weakness)
document.documentElement.classList.toggle('html-grey', config.grey)
})
// 监听主题色
watchEffect(() => {
setBodyPrimaryColor(config.primaryColor, isDark.value ? 'dark' : 'light')
})
return {
isDark,
device,
drawerSize,
operateMinWith,
config,
toggleTheme,
toggleDevice,
toggleWeakness,
toggleGrey,
togglePrimaryColor,
toggleTabs,
toggleDarkMode,
toggleConfigSideWidth,
toggleConfigSideCollapsedWidth,
toggleConfigSideItemHeight,
toggleConfigWatermark,
toggleSideMode,
toggleTransition
}
})

View File

@@ -0,0 +1,41 @@
import { findSysDictionary } from '@/api/sysDictionary'
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useDictionaryStore = defineStore('dictionary', () => {
const dictionaryMap = ref({})
const setDictionaryMap = (dictionaryRes) => {
dictionaryMap.value = { ...dictionaryMap.value, ...dictionaryRes }
}
const getDictionary = async (type) => {
if (dictionaryMap.value[type] && dictionaryMap.value[type].length) {
return dictionaryMap.value[type]
} else {
const res = await findSysDictionary({ type })
if (res.code === 0) {
const dictionaryRes = {}
const dict = []
res.data.resysDictionary.sysDictionaryDetails &&
res.data.resysDictionary.sysDictionaryDetails.forEach((item) => {
dict.push({
label: item.label,
value: item.value,
extend: item.extend
})
})
dictionaryRes[res.data.resysDictionary.type] = dict
setDictionaryMap(dictionaryRes)
return dictionaryMap.value[type]
}
}
}
return {
dictionaryMap,
setDictionaryMap,
getDictionary
}
})

View File

@@ -0,0 +1,31 @@
import { getSysParam } from '@/api/sysParams'
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useParamsStore = defineStore('params', () => {
const paramsMap = ref({})
const setParamsMap = (paramsRes) => {
paramsMap.value = { ...paramsMap.value, ...paramsRes }
}
const getParams = async(key) => {
if (paramsMap.value[key] && paramsMap.value[key].length) {
return paramsMap.value[key]
} else {
const res = await getSysParam({ key })
if (res.code === 0) {
const paramsRes = {}
paramsRes[key] = res.data.value
setParamsMap(paramsRes)
return paramsMap.value[key]
}
}
}
return {
paramsMap,
setParamsMap,
getParams
}
})

144
src/pinia/modules/router.js Normal file
View File

@@ -0,0 +1,144 @@
import { asyncRouterHandle } from '@/utils/asyncRouter'
import { emitter } from '@/utils/bus.js'
import { asyncMenu } from '@/api/menu'
import { defineStore } from 'pinia'
import { ref, watchEffect } from 'vue'
import pathInfo from '@/pathInfo.json'
const notLayoutRouterArr = []
const keepAliveRoutersArr = []
const nameMap = {}
const formatRouter = (routes, routeMap, parent) => {
routes &&
routes.forEach((item) => {
item.parent = parent
item.meta.btns = item.btns
item.meta.hidden = item.hidden
if (item.meta.defaultMenu === true) {
if (!parent) {
item = { ...item, path: `/${item.path}` }
notLayoutRouterArr.push(item)
}
}
routeMap[item.name] = item
if (item.children && item.children.length > 0) {
formatRouter(item.children, routeMap, item)
}
})
}
const KeepAliveFilter = (routes) => {
routes &&
routes.forEach((item) => {
// 子菜单中有 keep-alive 的,父菜单也必须 keep-alive否则无效。这里将子菜单中有 keep-alive 的父菜单也加入。
if (
(item.children && item.children.some((ch) => ch.meta.keepAlive)) ||
item.meta.keepAlive
) {
const path = item.meta.path
keepAliveRoutersArr.push(pathInfo[path])
nameMap[item.name] = pathInfo[path]
}
if (item.children && item.children.length > 0) {
KeepAliveFilter(item.children)
}
})
}
export const useRouterStore = defineStore('router', () => {
const keepAliveRouters = ref([])
const asyncRouterFlag = ref(0)
const setKeepAliveRouters = (history) => {
const keepArrTemp = []
history.forEach((item) => {
if (nameMap[item.name]) {
keepArrTemp.push(nameMap[item.name])
}
})
keepAliveRouters.value = Array.from(new Set(keepArrTemp))
}
emitter.on('setKeepAlive', setKeepAliveRouters)
const asyncRouters = ref([])
const topMenu = ref([])
const leftMenu = ref([])
const menuMap = {}
const topActive = ref('')
const setLeftMenu = (name) => {
sessionStorage.setItem('topActive', name)
topActive.value = name
leftMenu.value = []
if (menuMap[name]?.children) {
leftMenu.value = menuMap[name].children
}
return menuMap[name]?.children
}
watchEffect(() => {
let topActive = sessionStorage.getItem('topActive')
asyncRouters.value[0]?.children.forEach((item) => {
if (item.hidden) return
menuMap[item.name] = item
topMenu.value.push({ ...item, children: [] })
})
setLeftMenu(topActive)
})
const routeMap = {}
// 从后台获取动态路由
const SetAsyncRouter = async () => {
asyncRouterFlag.value++
const baseRouter = [
{
path: '/layout',
name: 'layout',
component: 'view/layout/index.vue',
meta: {
title: '底层layout'
},
children: []
}
]
const asyncRouterRes = await asyncMenu()
const asyncRouter = asyncRouterRes.data.menus
asyncRouter &&
asyncRouter.push({
path: 'reload',
name: 'Reload',
hidden: true,
meta: {
title: '',
closeTab: true
},
component: 'view/error/reload.vue'
})
formatRouter(asyncRouter, routeMap)
baseRouter[0].children = asyncRouter
if (notLayoutRouterArr.length !== 0) {
baseRouter.push(...notLayoutRouterArr)
}
asyncRouterHandle(baseRouter)
KeepAliveFilter(asyncRouter)
asyncRouters.value = baseRouter
return true
}
return {
topActive,
setLeftMenu,
topMenu,
leftMenu,
asyncRouters,
keepAliveRouters,
asyncRouterFlag,
SetAsyncRouter,
routeMap
}
})

144
src/pinia/modules/user.js Normal file
View File

@@ -0,0 +1,144 @@
import { login, getUserInfo } from '@/api/user'
import { jsonInBlacklist } from '@/api/jwt'
import router from '@/router/index'
import { ElLoading, ElMessage } from 'element-plus'
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { useRouterStore } from './router'
import { useCookies } from '@vueuse/integrations/useCookies'
import { useStorage } from '@vueuse/core'
import { useAppStore } from '@/pinia'
export const useUserStore = defineStore('user', () => {
const appStore = useAppStore()
const loadingInstance = ref(null)
const userInfo = ref({
uuid: '',
nickName: '',
headerImg: '',
authority: {}
})
const token = useStorage('token', '')
const xToken = useCookies('x-token')
const currentToken = computed(() => token.value || xToken.value || '')
const setUserInfo = (val) => {
userInfo.value = val
if (val.originSetting) {
Object.keys(appStore.config).forEach((key) => {
if (val.originSetting[key] !== undefined) {
appStore.config[key] = val.originSetting[key]
}
})
}
console.log(appStore.config)
}
const setToken = (val) => {
token.value = val
xToken.value = val
}
const NeedInit = async () => {
await ClearStorage()
await router.push({ name: 'Init', replace: true })
}
const ResetUserInfo = (value = {}) => {
userInfo.value = {
...userInfo.value,
...value
}
}
/* 获取用户信息*/
const GetUserInfo = async () => {
const res = await getUserInfo()
if (res.code === 0) {
setUserInfo(res.data.userInfo)
}
return res
}
/* 登录*/
const LoginIn = async (loginInfo) => {
try {
loadingInstance.value = ElLoading.service({
fullscreen: true,
text: '登录中,请稍候...'
})
const res = await login(loginInfo)
if (res.code !== 0) {
ElMessage.error(res.message || '登录失败')
return false
}
// 登陆成功,设置用户信息和权限相关信息
setUserInfo(res.data.user)
setToken(res.data.token)
// 初始化路由信息
const routerStore = useRouterStore()
await routerStore.SetAsyncRouter()
const asyncRouters = routerStore.asyncRouters
// 注册到路由表里
asyncRouters.forEach((asyncRouter) => {
router.addRoute(asyncRouter)
})
if (!router.hasRoute(userInfo.value.authority.defaultRouter)) {
ElMessage.error('请联系管理员进行授权')
} else {
await router.replace({ name: userInfo.value.authority.defaultRouter })
}
const isWindows = /windows/i.test(navigator.userAgent)
window.localStorage.setItem('osType', isWindows ? 'WIN' : 'MAC')
// 全部操作均结束关闭loading并返回
return true
} catch (error) {
console.error('LoginIn error:', error)
return false
} finally {
loadingInstance.value?.close()
}
}
/* 登出*/
const LoginOut = async () => {
const res = await jsonInBlacklist()
// 登出失败
if (res.code !== 0) {
return
}
await ClearStorage()
// 把路由定向到登录页无需等待直接reload
router.push({ name: 'Login', replace: true })
window.location.reload()
}
/* 清理数据 */
const ClearStorage = async () => {
token.value = ''
xToken.value = ''
sessionStorage.clear()
localStorage.removeItem('originSetting')
}
return {
userInfo,
token: currentToken,
NeedInit,
ResetUserInfo,
GetUserInfo,
LoginIn,
LoginOut,
setToken,
loadingInstance,
ClearStorage
}
})