什么!你还不会写Vue组件,编写《功能级权限》匹配公式组件

news/2025/3/19 14:50:11/文章来源:https://www.cnblogs.com/cyzf/p/18780587

说明

    该文章是属于OverallAuth2.0系列文章,每周更新一篇该系列文章(从0到1完成系统开发)。

    该系统文章,我会尽量说的非常详细,做到不管新手、老手都能看懂。

    说明:OverallAuth2.0 是一个简单、易懂、功能强大的权限+可视化流程管理系统。

友情提醒:本篇文章是属于系列文章,看该文章前,建议先看之前文章,可以更好理解项目结构。

qq群:801913255,进群有什么不懂的尽管问,群主都会耐心解答。

有兴趣的朋友,请关注我吧(*^▽^*)。

关注我,学不会你来打我

前言

该篇文章是实现【功能级权限】的开篇文章,其主要实现内容如下图↓

该图为功能级权限匹配插件

创建模型和数据源

在实现组件前,先要使用TS把模型和数据源创建好

我的文档目录如:Src->model->match->index.ts    依托于开源项目OverallAuth2.0统一权限分发中心的系统架构

创建匹配条件的关系

//组条件
export const matchingGroup = [{label: '',value: 'And',disabled: false},{label: '',value: 'Or',disabled: false}
]
View Code

创建匹配组件模型

//公式匹配模型
export interface matchingData {id: string;// 父级idpid: string;//匹配组(and,or) matchGroup: string;//层级
    level: number;//匹配条件
    matchingWhere: matchingWhereData[];//子集
    children: matchingData[];
}//匹配条件模型
export interface matchingWhereData {//主键id: string;//字段key(选中的字段)fieldKey: string;//等式符号key(选中的符号)matchEquationKey: string;//匹配数据key(选中的匹配值)matchDataKey: string;
}
View Code

创建生成随机id的方法

/* 生成随机不重复id */
export const randamId = function () {let n = 1;let arr = [];for (let i = 0; i < n; i++) {arr[i] = parseInt((Math.random() * 10000000000).toString());}for (let i = 0; i < n; i++) {for (let j = i + 1; j < n; j++) {if (arr[i] === arr[j]) {randamId();return false;}}}return ("Item-" + arr).toString();
};
View Code

编写组件

我的页面目录:Src->views->match->index.vue      Src->views->match->match.vue

编写match.vue页面代码

<template><div class="plandiv"><div v-for="item in data" :key="item.id" class="forDiv"><div class="groupDiv"><div class="groupBackColor"><div style="width: 20%"><el-selectv-model="item.matchGroup"placeholder="请选择"style="float: left;margin-right: 10px;margin-left: 10px;min-width: 100px;"><el-optionv-for="group in matchingGroup":key="group.value":label="group.label":value="group.value"/></el-select></div><div style="width: 80%"><div class="buttonStyle">这里放操作按钮</div></div></div></div></div></div>
</template>
<script  lang="ts" >
import {matchingData,matchingGroup,matchingWhere,matchingEquation,positionList,
} from "@/model/match";
import { defineComponent, PropType } from "vue";export default defineComponent({name: "xc-match",props: {data: {type: Object as PropType<matchingData[]>,required: true,},},setup() {return {matchingGroup,matchingWhere,matchingEquation,positionList,};},components: {},
});
</script><style  scoped>
/* 最外层样式 */
.plandiv {background-color: white;height: auto;
}
/* 循环层样式 */
.forDiv {overflow-y: auto;
}
/* 分组样式 */
.groupDiv {border: 1px solid #919aa3;width: auto;height: auto;margin-top: 5px;margin-right: 20px;margin-bottom: 10px;margin-left: 20px;
}
/* 组条件背景色 */
.groupBackColor {background-color: #919aa3;height: 50px;line-height: 50px;display: flex;width: 100%;justify-content: center;align-items: center;
}/* 按钮样式 */
.buttonStyle {text-align: left;margin-left: 20px;
}
</style>
View Code

编写index.vue页面代码

<template><match :data="pageList"></match>
</template>
<script lang="ts" setup>
import { matchingData, randamId } from "@/model/match";
import { ref } from "vue";
import match from "../match/match.vue";
const pageList = ref<matchingData[]>([{id: "Group-1",pid: "0",matchGroup: "And",level: 1,matchingWhere: [{id: randamId().toString(),fieldKey: "",matchEquationKey: "",matchDataKey: "",},],children: [],},
]);
</script>
View Code

index.vue页面中,我们添加了一条分组的默认值。查看下效果

添加分组按钮

在class='buttonStyle' div中添加如下代码

 

<el-button icon="CirclePlus" plain @click="addGroup(item)">新增分组</el-button><el-button icon="Plus" plain @click="addItem(item)">新增条件</el-button><el-buttonv-if="item.level !== 1"type="danger"icon="Delete"@click="deleteGroup(item)">删除分组</el-button>

 

添加按钮事件

添加前,我们必须先安装一个插件:npm install number-precision

在setup(props)中添加如下代码,并retrun事件

//最多组const maxGroup = ref<number>(5);//最多层级const maxLevel = ref<number>(3);//最多条件const maxWhere = ref<number>(10);// 添加组事件const addGroup = function (item: matchingData) {//获取当前组的长度var listGroupLength = item.children.length;//添加前验证最多添加多少层级if (item.level >= maxLevel.value) {ElMessage({message: "最多添加" + maxLevel.value + "",type: "warning",});return;}//添加前验证能添加多少组if (listGroupLength >= maxGroup.value) {ElMessage({message: "每层下最多添加" + maxGroup.value + "个组",type: "warning",});return;}//当前组必须要有条件才能添加下级组if (item.matchingWhere.length == 0) {ElMessage({message: "当前组下无任何条件,不能添加分组!",type: "warning",});return;}//组织要添加节点的数据var groupId = item.id + "-" + (listGroupLength + 1);var groupPid = item.id;var groupLevel = item.level + 1;//找到对应的下标const index = props.data.findIndex((s) => {if (s.id === item.id) {return true;}});//精确插入当前节点及插入位置var indexLength = listGroupLength + 1;item.children.splice(plus(...[index, indexLength]), 0, {id: groupId,pid: groupPid,matchGroup: "Or",level: groupLevel,matchingWhere: [],children: [],});};// 删除组const deleteGroup = function (item: matchingData) {GetGroupSpliceIndex(item.id, props.data);};//递归删除组const GetGroupSpliceIndex = (id: string, list: matchingData[]) => {//找到删除数据下标const index = list.findIndex((p: { id: string }) => {if (p.id === id) {return true;}});if (index === -1) GetGroupSpliceIndex(id, list[0].children);list.forEach((f: { id: string }) => {if (f.id == id) {list.splice(index, 1);}});};
View Code

这个时候,我们点击按钮,不会出现下级。因为递归的重要一步,并没有完成。

在match.vue 页面中找到有class="groupDiv" 的div,在div中的末尾添加如下代码

   <xc-matchv-if="item.children && item.children.length":data="item.children"/>

以上代码是实现递归的关键,位置一定要准。

说明一点xc-match一定要和页面导出的名称一样。

看效果图

添加条件及条件按钮

添加条件项

在match.vue页面xc-match元素前,添加如下代码

 <divclass="itemBackColor"v-for="whereItem in item.matchingWhere":key="whereItem.id"><!-- 匹配条件 --><el-selectv-model="whereItem.fieldKey"placeholder="请选择匹配条件"style="width: 240px"><el-optionv-for="where in matchingWhere":key="where.value":label="where.label":value="where.value"/></el-select><!-- 匹配等式 --><el-selectv-model="whereItem.matchEquationKey"placeholder="请选择等式"style="width: 240px"><el-optionv-for="equation in matchingEquation":key="equation.value":label="equation.label":value="equation.value"/></el-select><!-- 匹配值 --><el-input-numberv-model="whereItem.matchDataKey":step="1"min="1"max="200"step-strictlystyle="width: 240px"v-if="whereItem.fieldKey === 'Age'"/><el-selectv-else-if="whereItem.fieldKey === 'Position'"v-model="whereItem.matchDataKey"placeholder="请选择职位"style="width: 240px"><el-optionv-for="position in positionList":key="position.value":label="position.label":value="position.value"/></el-select><el-date-pickerv-else-if="whereItem.fieldKey === 'CreateTime'"v-model="whereItem.matchDataKey"type="date"style="width: 240px"placeholder="请选择时间"/><el-inputv-elsev-model="whereItem.matchDataKey"style="width: 240px"placeholder="请输入"clearable/><el-buttontype="danger"icon="Delete"plainsize="small"style="margin-left: 10px"@click="deleteItem(whereItem, item)">删除条件</el-button><!-- 当前项id:{{ whereItem.id }} --></div>
View Code

css如下

/* 项背景色 */
.itemBackColor {height: 46px;display: -webkit-box;margin-left: 20px;margin-right: 20px;display: flex;align-items: center;
}
.itemBackColor > *:not(:first-child) {margin-left: 10px;
}

添加条件按钮事件

 //添加项事件const addItem = function (item: matchingData) {if (item.matchingWhere.length > maxWhere.value) {ElMessage({message: "每层下最多添加" + maxWhere.value + "组条件",type: "warning",});return;}item.matchingWhere.push({id: randamId().toString(),fieldKey: "",matchEquationKey: "",matchDataKey: "",});};// 删除项const deleteItem = function (item: matchingWhereData, data: matchingData) {GetItemSpliceIndex(item.id, data);};//递归删除项const GetItemSpliceIndex = (id: string, list: any) => {//找到删除数据下标const index = list.matchingWhere.findIndex((p: { id: string }) => {if (p.id === id) {return true;}});if (index === -1) GetItemSpliceIndex(id, list.children);list.matchingWhere.forEach((f: { id: string }) => {if (f.id == id) {//删除当前项list.matchingWhere.splice(index, 1);if (list.matchingWhere.length == 0) {var parentGroup = props.data.filter((s) => s.id == list.pid);//当前组下无任何项并且层级不等于1,删除当前组if (parentGroup.length == 0 && list.level !== 1) {GetGroupSpliceIndex(list.id, props.data);}}}});};
View Code

查看效果,如下图↓

验证条件是否完整

编写验证方法

 //验证条件是否为空const VerifyWhereEmpty = function () {const isTrueArray = ref<boolean[]>([]);VerifyFunction(props.data, isTrueArray.value);const trueArray = isTrueArray.value?.filter((f) => f === true);if (trueArray.length === 0) {ElMessage({message: "成功",type: "warning",});} else {ElMessage({message: "匹配条件未填写完整",type: "warning",});}};//递归验证const VerifyFunction = function (list: matchingData[],isTrueArray: boolean[]) {list.forEach((element) => {element.matchingWhere.forEach((w) => {if (w.matchEquationKey.length == 0 ||w.matchDataKey.length == 0 ||w.fieldKey.length == 0) {isTrueArray.push(true);return;}});if (element.children.length > 0) {VerifyFunction(element.children, isTrueArray);}});};
View Code

在index.vue 页面调用

<template><div><el-button type="success" icon="Check" @click="submitForm">保存</el-button><match :data="pageList" ref="childRef"></match></div>
</template>
<script lang="ts" setup>
import { matchingData, randamId } from "@/model/match";
import { ref } from "vue";
import match from "../match/match.vue";
//样式
const emit = defineEmits(["validate"]);
const pageList = ref<matchingData[]>([{id: "Group-1",pid: "0",matchGroup: "And",level: 1,matchingWhere: [{id: randamId().toString(),fieldKey: "",matchEquationKey: "",matchDataKey: "",},],children: [],},
]);
//保存
const childRef = ref();
const submitForm = function () {if (childRef.value != null) {childRef.value.VerifyWhereEmpty();}
};
</script>
View Code

做完这些就能达到最终效果

 

需要源码的,关注公众号,发送【权限】获取源码

以上就是本篇文章的全部内容,感谢耐心观看

后端WebApi 预览地址:http://139.155.137.144:8880/swagger/index.html

前端vue 预览地址:http://139.155.137.144:8881

关注公众号:发送【权限】,获取源码

有兴趣的朋友,请关注我微信公众号吧(*^▽^*)。

关注我:一个全栈多端的宝藏博主,定时分享技术文章,不定时分享开源项目。关注我,带你认识不一样的程序世界

 

 

 

 

 

 

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

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

相关文章

基于策略的强化学习

基于策略的强化学习 1. 策略函数近似(Policy Function Approximation) 策略函数 π(a∣s)策略函数是一个概率密度函数,它根据当前状态 s 输出在该状态下采取每个可能动作 a 的概率。 在有限的状态和动作空间中,可以直接学习这个函数。但在连续动作空间或状态空间非常大时,…

软考数据库工程师

软考数据库工程师 重点 事务调度 1. 串行调度 多个事务依次(顺序)串行执行,且只有当一个事务的所有操作都执行完成才执行另一个事务的所有操作 2. 可串行化调度:并发调度 多个事务并发执行是正确的。当且仅当其并发结果与某一次序串行地执行的结果相同 并发问题 1. 丢失修改…

为Gazebo中的iris无人机添加realsense D435i相机

本机环境和实现目标 本机的系统位ubuntu20.04,已安装有ros noetic和PX4_Firmware,能够在Gazebo的环境中进行无人机的仿真。因为后续需要用实验室搭载有realsense D435i相机的无人机进行集群跟踪与避障的实验,准备为Gazebo中的iris无人机搭载上D435i相机先进行仿真验证。 注意…

信创产业2025白皮书:国产替代率突破40%的7大征兆

开篇:一场静默的产业革命正在发生 2025年的春天,当全球科技巨头还在为量子计算和元宇宙布局时,中国信创产业已悄然完成蜕变。工信部最新数据显示,国产基础软硬件在重点行业渗透率突破40%大关,这场以"自主可控"为底色的技术革命,正通过七大显著征兆宣告着中国IT…

aaa1

Uniapp、uniappx笔记 App平台 云端打包 Uniappx原生SDK android studio原生工程配置https://doc.dcloud.net.cn/uni-app-x/native/use/android.html 配置uts插件 教程 https://doc.dcloud.net.cn/uni-app-x/native/use/androiduts.html uts插件资源位于unpackage/resource/app-…

wpa_supplicant/hostapd --- 控制接口库

官网: https://w1.fi/wpa_supplicant/只需要源码的这两个文件:

go-gRPC微服务调用

协议介绍 RPC协议RPC(远程过程调用协议),通过网络从远程计算机上请求服务,而不需要了解底层网络技术的协议。RPC假定某些协议的存在,例如TCP/UDP等,为通信程序之间携带信息数据。在OSI网络七层模型中,RPC跨越了传输层和应用层,RPC使得开发包括网络分布式多程序在内的应用…

突破性技术:制药冻干机实现EtherCAT与Profinet网关模块无缝监控集成

案例分享:冻干机 EtherCAT 转 Profinet 实现温湿度监控 在现代医药生产过程中,冻干机作为关键设备,对温湿度的控制与监测尤为重要。某医药企业在其冻干机系统升级中,面临一个典型的通讯挑战:主控制系统采用 EtherCAT 协议,而现场的温湿度监控系统及其他过程控制设备则基于…

java-JNDI(二)-高版本绕过

JNDI 高版本的绕过 为了防止 JNDI 攻击,Oracle 对 JNDI 的远程类加载漏洞(如 LDAP 或 RMI 协议的远程代码执行(RCE))进行了限制 com.sun.jndi.rmi.object.trustURLCodebase=false com.sun.jndi.cosnaming.object.trustURLCodebase=false com.sun.jndi.ldap.object.trustUR…

tile

dsfTechnorati Tags: gjhgsdhttp://dfdfdfddfdfdfdsfdfdf