过程
登录成功后,去路由守卫那获取用户名,如果有则放行,没则请求用户信息以及权限菜单和字典表等信息,存入浏览器缓存中,在需要的下拉框或者表格中使用,每次后端新增字典,前端需要在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.token
console.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 = dictMap
this.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 = dictDataMap
this.isSetDict = true
wsCache.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 = dictDataMap
this.isSetDict = true
wsCache.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)">
<!-- 默认样式 -->
<span
v-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: '' },
]
}