过程
登录成功后,去路由守卫那获取用户名,如果有则放行,没则请求用户信息以及权限菜单和字典表等信息,存入浏览器缓存中,在需要的下拉框或者表格中使用,每次后端新增字典,前端需要在utils中的字典工具类中DICT_TYPE里加上对应的常量
代码
仅供参考
先路由守卫配置
//路由鉴权:鉴权,项目当中路由能不能被的权限的设置(某一个路由什么条件下可以访问、什么条件下不可以访问)
import router from '@/router'
import setting from './setting'
import useDictStore from '@/store/modules/dict'
//@ts-ignore
import nprogress from 'nprogress'
//引入进度条样式
import 'nprogress/nprogress.css'
nprogress.configure({ showSpinner: false })
//获取用户相关的小仓库内部token数据,去判断用户是否登录成功
import useUserStore from './store/modules/user'
import pinia from './store'
const userStore = useUserStore(pinia)
//全局守卫:项目当中任意路由切换都会触发的钩子
//全局前置守卫
router.beforeEach(async (to: any, from: any, next: any) => {document.title = `${setting.title} - ${to.meta.title}`//to:你将要访问那个路由//from:你从来个路由而来//next:路由的放行函数nprogress.start()//获取token,去判断用户登录、还是未登录const token = userStore.tokenconsole.log('🚀 ~ router.beforeEach ~ token:', token)//获取用户名字const username = userStore.username//用户登录判断if (token) {//登录成功,访问login,不能访问,指向首页if (to.path == '/login') {next({ path: '/' })} else {//登录成功访问其余六个路由(登录排除)//有用户信息if (username) {//放行next()} else {//如果没有用户信息,在守卫这里发请求获取到了用户信息再放行try {//获取用户信息await userStore.userInfo()// 获取所有字典const dictStore = useDictStore()if (!dictStore.getIsSetDict) {await dictStore.setDictMap()}//放行//万一:刷新的时候是异步路由,有可能获取到用户信息、异步路由还没有加载完毕,出现空白的效果next({ ...to })} catch (error) {//token过期:获取不到用户信息了//用户手动修改本地存储token//退出登录->用户相关的数据清空await userStore.userLogout()next({ path: '/login', query: { redirect: to.path } })}}}} else {//用户未登录判断if (to.path == '/login') {next()} else {next({ path: '/login', query: { redirect: to.path } })}}
})
//全局后置守卫
router.afterEach((to: any, from: any) => {nprogress.done()
})//第一个问题:任意路由切换实现进度条业务 ---nprogress
//第二个问题:路由鉴权(路由组件访问权限的设置)
//全部路由组件:登录|404|任意路由|首页|数据大屏|权限管理(三个子路由)|商品管理(四个子路由)//用户未登录:可以访问login,其余六个路由不能访问(指向login)
//用户登录成功:不可以访问login[指向首页],其余的路由可以访问
字典的小仓库
import { defineStore } from 'pinia'import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
const { wsCache } = useCache('sessionStorage')
import { listSimpleDictDatas } from '@/api/dict/data'const useDictStore = defineStore('dict', {state: () => ({dictMap: new Map(),isSetDict: false,}),getters: {getDictMap() {const dictMap = wsCache.get(CACHE_KEY.DICT_CACHE)console.log('🚀 ~ getDictMap ~ dictMap:', dictMap)if (dictMap) {this.dictMap = dictMap}return this.dictMap},getIsSetDict() {return this.isSetDict},},actions: {async setDictMap() {const dictMap = wsCache.get(CACHE_KEY.DICT_CACHE)if (dictMap) {this.dictMap = dictMapthis.isSetDict = true} else {const res = await listSimpleDictDatas()console.log('🚀 ~ setDictMap ~ res:字典回显', res)// 设置数据const dictDataMap = new Map()res.data.forEach((dictData) => {// 获得 dictType 层级const enumValueObj = dictDataMap[dictData.dictType]if (!enumValueObj) {dictDataMap[dictData.dictType] = []}// 处理 dictValue 层级dictDataMap[dictData.dictType].push({value: dictData.value,label: dictData.label,colorType: dictData.colorType,cssClass: dictData.cssClass,})})this.dictMap = dictDataMapthis.isSetDict = truewsCache.set(CACHE_KEY.DICT_CACHE, dictDataMap, { exp: 60 }) // 60 秒 过期}},getDictByType(type) {console.log(' getDictByType(type)')if (!this.isSetDict) {this.setDictMap()}return this.dictMap[type]},async resetDict() {wsCache.delete(CACHE_KEY.DICT_CACHE)const res = await listSimpleDictDatas()// 设置数据const dictDataMap = new Map()res.forEach((dictData) => {// 获得 dictType 层级const enumValueObj = dictDataMap[dictData.dictType]if (!enumValueObj) {dictDataMap[dictData.dictType] = []}// 处理 dictValue 层级dictDataMap[dictData.dictType].push({value: dictData.value,label: dictData.label,colorType: dictData.colorType,cssClass: dictData.cssClass,})})this.dictMap = dictDataMapthis.isSetDict = truewsCache.set(CACHE_KEY.DICT_CACHE, dictDataMap, { exp: 60 }) // 60 秒 过期},},
})export default useDictStore
utils里的字典工具类
/*** 数据字典工具类*/
import useDictStore from '@/store/modules/dict'const dictStore = useDictStore()export const getDictOptions = (dictType) => {return dictStore.getDictByType(dictType) || []
}export const getIntDictOptions = (dictType) => {// 获得通用的 DictDataType 列表const dictOptions = getDictOptions(dictType)// 转换成 number 类型的 NumberDictDataType 类型// why 需要特殊转换:避免 IDEA 在 v-for="dict in getIntDictOptions(...)" 时,el-option 的 key 会告警const dictOption = []dictOptions.forEach((dict) => {dictOption.push({...dict,value: parseInt(dict.value + ''),})})return dictOption
}export const getStrDictOptions = (dictType) => {const dictOption = []const dictOptions = getDictOptions(dictType)dictOptions.forEach((dict) => {dictOption.push({...dict,value: dict.value + '',})})return dictOption
}export const getBoolDictOptions = (dictType) => {const dictOption = []const dictOptions = getDictOptions(dictType)dictOptions.forEach((dict) => {dictOption.push({...dict,value: dict.value + '' === 'true',})})return dictOption
}/*** 获取指定字典类型的指定值对应的字典对象* @param dictType 字典类型* @param value 字典值* @return DictDataType 字典对象*/
export const getDictObj = (dictType, value) => {const dictOptions = getDictOptions(dictType)for (const dict of dictOptions) {if (dict.value === value + '') {return dict}}
}/*** 获得字典数据的文本展示** @param dictType 字典类型* @param value 字典数据的值* @return 字典名称*/
export const getDictLabel = (dictType, value) => {const dictOptions = getDictOptions(dictType)const dictLabel = ref('')dictOptions.forEach((dict) => {if (dict.value === value + '') {dictLabel.value = dict.label}})return dictLabel.value
}export function getDictData(dictType, value) {// 获取 dictType 对应的数据字典数组const dictDatas = getDictDatas(dictType)console.log('🚀 ~ getDictData ~ dictDatas:', dictDatas)if (!dictDatas || dictDatas.length === 0) {return ''}// 获取 value 对应的展示名value = value + '' // 强制转换成字符串,因为 DictData 小类数值,是字符串for (const dictData of dictDatas) {if (dictData.value === value) {return dictData}}return undefined
}export function getDictDataLabel(dictType, value) {const dict = getDictData(dictType, value)return dict ? dict.label : ''
}/*** 获取 dictType 对应的数据字典数组** @param dictType 数据类型* @returns {*|Array} 数据字典数组*/
export function getDictDatas(dictType) {console.log('xxxxx', dictStore.getDictMap[dictType])return dictStore.getDictMap[dictType] || []
}/*** 获取 dictType 对应的数据字典数组** @param dictType 数据类型* @param values 数组、单个元素* @returns {*|Array} 数据字典数组*/
export function getDictDatas2(dictType, values) {if (values === undefined) {return []}// 如果是单个元素,则转换成数组if (!Array.isArray(values)) {values = [this.value]}// 获得字典数据const results = []for (const value of values) {const dict = getDictData(dictType, value)if (dict) {results.push(dict)}}return results
}export const DICT_TYPE = {// ========== SYSTEM 模块 ==========SYSTEM_USER_SEX: 'system_user_sex',SYSTEM_MENU_TYPE: 'system_menu_type',SYSTEM_ROLE_TYPE: 'system_role_type',SYSTEM_DATA_SCOPE: 'system_data_scope',SYSTEM_NOTICE_TYPE: 'system_notice_type',SYSTEM_OPERATE_TYPE: 'system_operate_type',SYSTEM_LOGIN_TYPE: 'system_login_type',SYSTEM_LOGIN_RESULT: 'system_login_result',SYSTEM_SMS_CHANNEL_CODE: 'system_sms_channel_code',SYSTEM_SMS_TEMPLATE_TYPE: 'system_sms_template_type',SYSTEM_SMS_SEND_STATUS: 'system_sms_send_status',SYSTEM_SMS_RECEIVE_STATUS: 'system_sms_receive_status',SYSTEM_ERROR_CODE_TYPE: 'system_error_code_type',SYSTEM_OAUTH2_GRANT_TYPE: 'system_oauth2_grant_type',SYSTEM_MAIL_SEND_STATUS: 'system_mail_send_status',SYSTEM_NOTIFY_TEMPLATE_TYPE: 'system_notify_template_type',// ========== 通用 模块 ==========COMMON_STATUS: 'common_status',
}
搜索框使用
<el-form-item :label="$t('systemManager.userManager.status')" prop="status"><el-select v-model="searchData.status" placeholder="请选择租户状态" clearable style="width: 240px"><el-option v-for="dict in getDictDatas(DICT_TYPE.COMMON_STATUS)" :key="dict.value" :label="dict.label" :value="dict.value" /></el-select></el-form-item>import { getDictDatas } from '@/utils/dict'
import { DICT_TYPE } from '@/utils/dict'
表格中使用
<el-table-column prop="enabled" align="center" width="80" :label="$t('page.status')"><template #default="scope"><dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /></template></el-table-column>
组件
<template><span><template v-for="(dict, index) in getDictDatas2(props.type, props.value)"><!-- 默认样式 --><spanv-if="dict.colorType === 'default' || dict.colorType === '' || dict.colorType === undefined":key="dict.value":index="index":type="dict.colorType":class="dict.cssClass">{{ dict.label }}</span><!-- Tag 样式 --><el-tag v-else :disable-transitions="true" :key="dict.dictType" :index="index" :type="dict.colorType" :class="dict.cssClass">{{ dict.label }}</el-tag></template></span>
</template><script setup>
import { getDictDatas2 } from '@/utils/dict'const props = defineProps({type: String,value: [Number, String, Boolean, Array],
})// onMounted(() => {
// console.log('自定义属性', props.type, props.value)
// })
</script>
<style scoped>
.el-tag + .el-tag {margin-left: 10px;
}
.testAA {color: darkmagenta;
}
</style>
数据
// 表格数据
function createdDict() {return [{ dictType: 'common_status', value: '1', label: '启用', colorType: 'primary', cssClass: '' },{ dictType: 'common_status', value: '0', label: '停用', colorType: 'warning', cssClass: '' },{ dictType: 'system_operate_type', value: '0', label: '其它', colorType: 'info', cssClass: '' },{ dictType: 'system_operate_type', value: '1', label: '查询', colorType: 'info', cssClass: '' },{ dictType: 'system_operate_type', value: '2', label: '新增', colorType: 'primary', cssClass: '' },{ dictType: 'system_operate_type', value: '3', label: '修改', colorType: 'warning', cssClass: '' },{ dictType: 'system_operate_type', value: '4', label: '删除', colorType: 'danger', cssClass: '' },{ dictType: 'system_operate_type', value: '5', label: '导出', colorType: 'success', cssClass: '' },{ dictType: 'system_operate_type', value: '6', label: '导入', colorType: 'success', cssClass: '' },]
}