若依前后端分离版学习笔记(十八)——页面权限,页签缓存以及图标,字典,参数的利用

news/2025/10/26 15:50:32/文章来源:https://www.cnblogs.com/tlnshuju/p/19166923

若依前后端分离版学习笔记(十八)——页面权限,页签缓存以及图标,字典,参数的利用

2025-10-26 15:43  tlnshuju  阅读(0)  评论(0)    收藏  举报

一、页面权限

封装指令权限,能简单快速实现按钮级别的权限判断

1.1 hasRole

角色权限处理

/**
* v-hasRole 角色权限处理
* Copyright (c) 2019 ruoyi
*/
import useUserStore from '@/store/modules/user'
export default {
mounted(el, binding, vnode) {
const { value } = binding
const super_admin = "admin"
// 从useUserStore中获取当前用户的角色列表
const roles = useUserStore().roles
// 如果传入的指令值存在,value为非空数组
if (value && value instanceof Array && value.length > 0) {
const roleFlag = value
// 验证用户是否具有指定角色之一,或者是否为超级管理员 admin
const hasRole = roles.some(role => {
return super_admin === role || roleFlag.includes(role)
})
// 如果不具有相应角色,则从DOM中移除该元素
if (!hasRole) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`请设置角色权限标签值`)
}
}
}

使用示例

<el-button v-hasRole="['admin', 'editor']">编辑</el-button>

1.2 hasPermi

权限标识处理

import useUserStore from '@/store/modules/user'
export default {
mounted(el, binding, vnode) {
const { value } = binding
const all_permission = "*:*:*"
// 从useUserStore获取权限标识列表
const permissions = useUserStore().permissions
// 检查传入的指令值是否为非空数组
if (value && value instanceof Array && value.length > 0) {
const permissionFlag = value
// 判断用户权限是否有全部权限或具有指定权限
const hasPermissions = permissions.some(permission => {
return all_permission === permission || permissionFlag.includes(permission)
})
// 如果不具有指定权限,则移除DOM元素
if (!hasPermissions) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`请设置操作权限标签值`)
}
}
}

使用示例

<el-button v-hasPermi="['system:user:add', 'system:user:edit']">新增用户</el-button>

1.3 使用示例

示例代码

<!-- 父组件 --><template><!-- hasRole 使用示例 --><div style="margin: 20px; padding: 20px; border: 1px solid #ccc;"><h3>角色权限控制示例 (v-hasRole)</h3><el-button v-hasRole="['admin']" type="primary">仅管理员可见</el-button>// hasRole.js中设置了admin所有可见,所以这里v-hasRole="['common']" 管理员也是有权限的<el-button v-hasRole="['common']" type="success">编辑者和管理员可见</el-button></div><!-- hasPermi 使用示例 --><div style="margin: 20px; padding: 20px; border: 1px solid #ccc;"><h3>操作权限控制示例 (v-hasPermi)</h3><el-button v-hasPermi="['system:user:add']" type="primary">新增用户</el-button><el-button v-hasPermi="['system:user:edit']" type="success">编辑用户</el-button><el-button v-hasPermi="['system:user:remove']" type="danger">删除用户</el-button><el-button v-hasPermi="['system:user:add', 'system:user:edit']" type="warning">新增或编辑用户</el-button></div><!-- 函数式权限检查示例 --><div style="margin: 20px; padding: 20px; border: 1px solid #ccc;"><h3>函数式权限检查</h3><div v-if="checkRole(['admin'])"><p>这是通过函数检查显示的内容 - 仅管理员可见</p></div><div v-if="checkPermi(['system:config:edit'])"><p>这是通过函数检查显示的内容 - 编辑者和管理员可见</p></div></div></template><script setup>import { checkPermi, checkRole } from '@/utils/permission'</script>

admin账号查看
在这里插入图片描述

ry(普通角色)账号查看

这里注意要给普通角色添加新增的目录权限

在这里插入图片描述

v-if 是 Vue 的条件渲染指令,它根据表达式的真假值来决定是否渲染某个元素或组件;当 v-if 的值为 true 时,元素会被渲染到 DOM 中;
当 v-if 的值为 false 时,元素不会被渲染到 DOM 中(完全不存在于 DOM 树中)

二、页签缓存

页签缓存是一个重要的用户体验优化功能。在ruoyi中,它允许用户在切换不同页面时保持页面状态,避免重复加载和数据丢失。在ruoyi中,页签缓存主要通过Vue的组件实现

<template><section class="app-main"><router-view v-slot="{ Component, route }"><transition name="fade-transform" mode="out-in"><keep-alive :include="tagsViewStore.cachedViews"><component v-if="!route.meta.link" :is="Component" :key="route.path"/></keep-alive></transition></router-view></section></template>
  • keep-alive是Vue内置组件,用于缓存动态组件
  • :include="tagsViewStore.cachedViews"指定了需要缓存的组件列表
  • 只有在cachedViews数组中的组件才会被缓存

使用keep-alive进行组件缓存时,必须确保路由配置和组件的name属性保持一致,否则会导致缓存失效或者出现其他问题。在Vue中,keep-alive组件通过include属性来决定哪些组件需要被缓存。默认情况下,keep-alive会优先匹配组件的name属性。如果路由配置中的name与组件定义的name不一致,就会导致缓存机制失效。

在系统管理-菜单管理中可以配置菜单页签是否缓存,默认为缓存。
在这里插入图片描述

缓存的具体管理在src→store→modules→tagsViews.js中具体实现

addCachedView(view) {
// 如果该页面已在缓存列表中,则不再重复添加
if (this.cachedViews.includes(views.name)) return
// 如果该页面的路由配置中meta.noCache不为true,则将路由的name添加到缓存列表
if (!view.meta.noCache) {
this.cachedViews.push(view.name)
}
}

实际使用场景
1.用户首次访问/system/user页面,系统会加载用户管理组件
2.由于该路由未设置meta.noCache=true,所以该页面组件会被添加到cachedViews列表中
3.当用户切换到其他页面(如角色管理)再切换回来时,由于组件已在cachedViews列表中,keep-alive会直接从缓存中恢复组件状态,而不会重新创建组件
4.这样用户在用户管理页面中的操作状态(如表格的分页、筛选条件等)会被保留

三、使用图标

3.1 图标

icon图标放在src→assets→icons下

3.2 全局SvgIcon组件

src→components→SvgIcon→index.vue

<template><svg :class="svgClass" aria-hidden="true"><use :xlink:href="iconName" :fill="color" /></svg></template><script>export default defineComponent({// 定义从父组件接收的值props: {iconClass: {type: String,required: true},className: {type: String,default: ''},color: {type: String,default: ''},},setup(props) {// 要在模板中使用的响应式数据和方法return {iconName: computed(() => `#icon-${props.iconClass}`),svgClass: computed(() => {if (props.className) {return `svg-icon ${props.className}`}return 'svg-icon'})}}})</script>// 定义组件样式<style scope lang="scss">.sub-el-icon,.nav-icon {display: inline-block;font-size: 15px;margin-right: 12px;position: relative;}.svg-icon {width: 1em;height: 1em;position: relative;fill: currentColor;vertical-align: -2px;}</style>

使用方式

<!-- icon-class 为 icon 的名字; class-name 为 icon 自定义 class-->
<!-- 基本用法 --><svg-icon icon-class="user" /><!-- 带颜色 --><svg-icon icon-class="edit" color="#409EFF" /><!-- 带额外CSS--><svg-icon icon-class="dashboard" class-name="custom-class" />

实际示例

<script setup></script><template><div class="app-container">msg</div><svg-icon icon-class="user" color = "#409EFF" class-name="user-class"/><svg-icon icon-class="password" class-name="password-class"/></template><style scoped lang="scss">.password-class{font-size: 200px;fill: red;}</style>

显示效果
在这里插入图片描述

3.3 新增图标

提示
如果是从iconfont下载的图标,记得使用如 Sketch 等工具规范一下图标的大小问题,不然可能会造成项目中的图标大小尺寸不统一的问题。 项目中使用的图标都是 128*128 大小规格的。

将新图标放到src→assets→icons下,并使用即可,如:

四、使用字典

数据字典系统是一个重要的功能模块,用于管理系统中的各种枚举数据,如状态、性别、类型等。它通过前后端配合,实现了数据字典的统一管理和使用。
流程:
1.初始化:通过 useDict 函数加载所需字典数据
2.缓存:将获取的字典数据存储在 dict.js 存储模块中,避免重复请求
3.显示:通过 DictTag 组件根据字典值显示对应标签
4.更新:当字典数据发生变化时,可以刷新缓存

4.1 核心组件和实现

4.1.1 DictTag组件

src→components→DictTag→index.vue是项目中用于显示字典标签的核心组件,它可以根据字典值显示对应的标签文本,并支持不同的显示样式。

<template><div>// 遍历所有字典选项<template v-for="(item, index) in options">// 通过 values.includes(item.value) 判断当前值是否匹配字典项<template v-if="values.includes(item.value)"><span// 根据是否有自定义样式类和类型,决定使用普通 span 标签还是 el-tag 组件显示v-if="(item.elTagType == 'default' || item.elTagType == '') && (item.elTagClass == '' || item.elTagClass == null)":key="item.value":index="index":class="item.elTagClass">{{ item.label + " " }}</span><el-tagv-else:disable-transitions="true":key="item.value + ''":index="index":type="item.elTagType":class="item.elTagClass">{{ item.label + " " }}</el-tag></template></template></div></template><script setup>// 记录未匹配的项const unmatchArray = ref([])// 接收属性const props = defineProps({// 数据options: {type: Array,default: null,},// 当前的值value: [Number, String, Array],// 当未找到匹配的数据时,显示valueshowValue: {type: Boolean,default: true,},// 分隔符,默认为逗号separator: {type: String,default: ",",}})// 计算属性 values 用于处理传入的值,将其统一转换为字符串数组const values = computed(() => {if (props.value === null || typeof props.value === 'undefined' || props.value === '') return []return Array.isArray(props.value) ? props.value.map(item => '' + item) : String(props.value).split(props.separator)})const unmatch = computed(() => {unmatchArray.value = []// 没有value不显示if (props.value === null || typeof props.value === 'undefined' || props.value === '' || !Array.isArray(props.options) || props.options.length === 0) return false// 传入值为数组let unmatch = false // 添加一个标志来判断是否有未匹配项values.value.forEach(item => {if (!props.options.some(v => v.value === item)) {unmatchArray.value.push(item)unmatch = true // 如果有未匹配项,将标志设置为true}})return unmatch // 返回标志的值})function handleArray(array) {if (array.length === 0) return ""return array.reduce((pre, cur) => {return pre + " " + cur})}</script><style scoped>.el-tag + .el-tag {margin-left: 10px;}</style>

4.1.2 字典获取工具

src→utils→dict.js 用于获取字段数据的工具函数

import useDictStore from '@/store/modules/dict'
import { getDicts } from '@/api/system/dict/data'
/**
* 获取字典数据
*/
// 接收多个字典类型参数
export function useDict(...args) {
const res = ref({})
return (() => {
// 循环处理每个参数
args.forEach((dictType, index) => {
res.value[dictType] = []
// 先尝试从本地获取
const dicts = useDictStore().getDict(dictType)
if (dicts) {
res.value[dictType] = dicts
} else {
// 本地没有则调用api获取
getDicts(dictType).then(resp => {
// 将获取到的数据转换为统一格式(包含 label、value、elTagType、elTagClass)
res.value[dictType] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass }))
useDictStore().setDict(dictType, res.value[dictType])
})
}
})
return toRefs(res.value)
})()
}

字典api接口
src→api→system→dict→data.js

// 根据字典类型查询字典数据信息
export function getDicts(dictType) {
return request({
url: '/system/dict/data/type/' + dictType,
method: 'get'
})
}

4.1.3 字典存储

src→store→modules→dict.js 存储模块管理字典数据,避免重复请求

const useDictStore = defineStore(
'dict',
{
state: () => ({
dict: new Array()
}),
actions: {
// 获取字典
getDict(_key) {
if (_key == null && _key == "") {
return null
}
try {
for (let i = 0; i < this.dict.length; i++) {
if (this.dict[i].key == _key) {
return this.dict[i].value
}
}
} catch (e) {
return null
}
},
// 设置字典
setDict(_key, value) {
if (_key !== null && _key !== "") {
this.dict.push({
key: _key,
value: value
})
}
},
// 删除字典
removeDict(_key) {
var bln = false
try {
for (let i = 0; i < this.dict.length; i++) {
if (this.dict[i].key == _key) {
this.dict.splice(i, 1)
return true
}
}
} catch (e) {
bln = false
}
return bln
},
// 清空字典
cleanDict() {
this.dict = new Array()
},
// 初始字典
initDict() {
}
}
})
export default useDictStore

4.2 实际使用示例

在用户管理模块中的使用
在src/views/system/user/index.vue中

// 获取两个字典 sys_normal_disable: 通用状态字典(正常/停用),sys_user_sex: 用户性别字典
const { sys_normal_disable, sys_user_sex } = proxy.useDict("sys_normal_disable", "sys_user_sex")

模板中使用

<!-- 状态选择 --><el-select v-model="queryParams.status" placeholder="用户状态" clearable style="width: 240px"><el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" /></el-select><!-- 性别选择 --><el-select v-model="form.sex" placeholder="请选择"><el-option v-for="dict in sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value"></el-option></el-select><!-- 状态显示 --><el-table-column label="状态" align="center" key="status" v-if="columns[5].visible"><template #default="scope"><el-switchv-model="scope.row.status"active-value="0"inactive-value="1"@change="handleStatusChange(scope.row)"></el-switch></template></el-table-column>

在字典数据管理中的使用
在 src/views/system/dict/data.vue 中:

<el-table-column label="状态" align="center" prop="status"><template #default="scope">// 这里使用了DicTag组件显示状态,通过:options传入字典选项,通过:value传入当前值<dict-tag :options="sys_normal_disable" :value="scope.row.status" /></template></el-table-column>

字典更新机制
在 src/views/system/dict/data.vue中

/** 提交按钮 */
function submitForm() {
proxy.$refs["dataRef"].validate(valid => {
if (valid) {
if (form.value.dictCode != undefined) {
updateData(form.value).then(response => {
// 更新字典后清除缓存
useDictStore().removeDict(queryParams.value.dictType)
proxy.$modal.msgSuccess("修改成功")
open.value = false
getList()
})
} else {
addData(form.value).then(response => {
// 新增字典后清除缓存
useDictStore().removeDict(queryParams.value.dictType)
proxy.$modal.msgSuccess("新增成功")
open.value = false
getList()
})
}
}
})
}

点击刷新缓存按钮,调用api /system/dict/data 刷新缓存

五、使用参数

参数设置是提供开发人员、实施人员的动态系统配置参数,不需要去频繁修改后台配置文件,也无需重启服务器即可生效。

5.1 参数修改流程

1.查看参数列表
用户在参数配置页面查看所有系统参数,包括参数名称、建名、键值等信息。
在这里插入图片描述
2.修改参数
2.1 点击修改按钮
2.2 修改参数键值
2.3 确定提交修改
3.刷新缓存

5.2 实现

api接口 src/api/system/config.js

  • listConfig:查询参数列表
  • getConfig:查询单个参数详情
  • addConfig:新增参数
  • updateConfig:修改参数
  • delConfig:删除参数
  • refreshCache:刷新参数缓存
    src/views/system/config/index.vue 主要修改逻辑如下
/** 提交按钮 */
function submitForm() {
proxy.$refs["configRef"].validate(valid => {
if (valid) {
if (form.value.configId != undefined) {
updateConfig(form.value).then(response => {
proxy.$modal.msgSuccess("修改成功")
open.value = false
getList()
})
} else {
addConfig(form.value).then(response => {
proxy.$modal.msgSuccess("新增成功")
open.value = false
getList()
})
}
}
})
}

当用户修改参数并提交时,会调用updateConfig API接口将更改保存到数据库。
调用refreshCache接口来清除后端缓存,确保修改的参数能够立即生效:

/** 刷新缓存按钮操作 */
function handleRefreshCache() {
refreshCache().then(() => {
proxy.$modal.msgSuccess("刷新缓存成功")
})
}
5.3 参数使用
在用户管理页面 /src/views/system/user/index.vue 中可以看到
onMounted(() => {
getDeptTree()
getList()
proxy.getConfigKey("sys.user.initPassword").then(response => {
initPassword.value = response.msg
})
})

在组件挂载时执行,通过调用getConfigKey(“sys.user.initPassword”)获取系统配置的默认密码参数值,并将其存储在initPassword.value中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/946824.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Cloud Gateway网关路由配置 - AlanLee

Spring Cloud Gateway 配置使用 lb:// 协议时,需依赖以下组件:核心依赖 ‌Spring Cloud Gateway 依赖‌ 需添加 spring-cloud-starter-gateway 依赖,用于启用网关功能。 ‌<dependency><groupId>org.sp…

重构学习认知:从听讲、践行到教学的启示

读完 Scalers 关于大学生上课听讲的论述,以及娄老师围绕 “做中学” 与教练式教学的文章,不难发现这些内容共同指向了一套更科学的学习逻辑,打破了传统认知中对 “学习” 的单一理解,让人对如何高效获取知识、内化…

【c++】红黑树的部分构建

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

域登录态分享(类sso)

遇到一个问题,在同一主域下的多个子域之间共享登录状态的需求。例如:main.example.com 主站 learn.example.com 学习中心希望在任意子域登录后,其他子域也能自动识别登录状态,包括登出同步。Cookie 跨子域共享 浏览…

ssh原理

ssh原理 密码认证原理服务器生成一对公私钥,认证流程客户端发送请求给服务器服务器生成了一对公私钥,发送公钥给客户端客户端使用这个公钥进行加密,发送给服务端服务端通过私钥进行解密,认证成功后,就能连接上去了…

实现一个简易版本的IOC

简单的IOC其实就是一个map,key是对象名字,value是对象的实例,Spring容器初始化的时候会将配置文件或注解信息转换成BeanDefinition对象存储在集合中,然后遍历集合通过反射实例化Bean,实例化后的Bean会放入到名为si…

Alibaba Cloud Linux 4 安装docker后,修复docker的方法

Alibaba Cloud Linux 4 安装docker后,修复docker的方法#!/bin/bashset -e echo "🔧 Docker 服务崩溃修复..." # 停止所有 Docker 相关服务echo "🛑 停止 Docker 服务..."sudo systemctl stop …

MPK(Mirage Persistent Kernel)源码笔记(2)--- 多层结构化图模型

MPK(Mirage Persistent Kernel)源码笔记(2)--- 多层结构化图模型 目录MPK(Mirage Persistent Kernel)源码笔记(2)--- 多层结构化图模型0x00 概要0x01 机制1.1 当前问题1.2 解决方案1.2.1 μGraphs:多层次计算…

day000 ML串讲

numpy模块numpy模块的主要作用:numpy表示一维或者多维的数组(容器),主要是用来存储和运算数值型数据的。常用属性:shape:返回数组的形状 ndim:返回数组的维度 size:返回数组元素的个数 dtype:返回数组元素的数…

我的学习方式破局思考 ——读《认真听讲》、《做中学》与《做教练》有感

坦白说,在点击那三篇链接之前,我以为这又是一次“形式大于内容”的任务。但读完的那一刻,我感到脸上发烫,心中却豁然开朗。这三篇文章,像三位来自不同角度的导师,共同对我陈旧的学习观念进行了一次彻底的“手术”…

cmd运行python文件

C:\Users\Air>python F:\blogs\鱼书\man.py Initialized Hello David! Good-bye David!

Unity协程除了实现功能还可以增加可读性

协程作为异步、延时等待。还可以作为Update的解耦已读方案。 以一个UI淡进淡出为例。 不是用协程时,Lerp的更新都包含在Update方法中:1 1 using UnityEngine;2 2 using UnityEngine.UI;3 3 4 4 namespace UI.Mai…

2025年TPU厂家权威推荐榜:专业TPU加纤、TPU改性生产技术实力与市场口碑深度解析

2025年TPU厂家权威推荐榜:专业TPU加纤、TPU改性生产技术实力与市场口碑深度解析 随着全球高分子材料行业的快速发展,热塑性聚氨酯弹性体(TPU)作为一种性能优异的高分子材料,在汽车制造、电子电器、医疗器械、运动…

Nginx程序结构及核心配置

Nginx程序结构及核心配置1. 程序结构 $ sudo rpm -ql nginx /etc/logrotate.d/nginx # 配置日志切割策略 /etc/nginx # nginx 主程序存放路径 /etc/nginx/conf.d # nginx 配置文件目录 /etc/nginx/conf.d/default.conf…

事倍功半是蠢蛋57 typora相对路径图片上传到github

3️⃣ Typora 中设置图片自动复制(推荐) 打开 Typora → 文件(File) → 偏好设置(Preferences) → 图像(Images): 插入图片时:选择 “复制图片到指定路径” 指定路径:./images 插入时使用:相对路径 以后你在 Typ…

Nginx部署星益小游戏平台(静态页面)

Nginx部署星益小游戏平台(静态页面)1. 星益小游戏平台部署 源码下载:https://c1026.dmpdmp.com/e01306e2fdcd92aac114784572ec6e20/68a424b6/2019/08/17/f1aac2083a4d2e76196e57e68aed173f.zip?fn=%E6%98%9F%E7%9B…

hadoop应用遇到的问题

当遇到如下问题,大致是安装包有问题,需要重新下载安装包,但是直接用命令行下载需要十个小时,这边在widows上下载好在传送到虚拟机 接着按照以下步骤 结合你当前hdfs命令功能受限的问题,以下是彻底修复Hadoop环境的…

企业级Nginx安装部署

企业级Nginx安装部署1. Nginx 安装方式编译安装:使用源码,根据企业业务需求,需要什么功能,编译时添加什么模块,但是安装过程较为繁琐 自动化安装:配置好安装源,直接通过自动化工具安装即可 二进制安装:解压即用…

2025 年 10 月门窗十大品牌综合实力权威推荐榜单,聚焦产能、专利与环保的实力品牌深度解析

2025 年 10 月门窗十大品牌综合实力权威推荐榜单由中国建筑金属结构协会、全国工商联家具装饰业商会联合发布。本次榜单以 “产能保障 + 专利技术 + 环保标准” 为核心评估体系,突破传统单一性能评选局限:产能维度考…

以“听”为基,以“做”为翼

原文:Scalers:大学生上课为什么一定要认真听讲? https://www.scalerstalk.com/816-attention 读《大学生上课为什么一定要认真听讲?》,我对课堂学习的重要性有了更为深刻的体悟。 课堂是知识传递的核心阵地,老师…