说明
该文章是属于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} ]
创建匹配组件模型

//公式匹配模型 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; }
创建生成随机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(); };
编写组件
我的页面目录: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>
编写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>
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);}});};
这个时候,我们点击按钮,不会出现下级。因为递归的重要一步,并没有完成。
在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>
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);}}}});};
查看效果,如下图↓
验证条件是否完整
编写验证方法

//验证条件是否为空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);}});};
在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>
做完这些就能达到最终效果
需要源码的,关注公众号,发送【权限】获取源码
以上就是本篇文章的全部内容,感谢耐心观看
后端WebApi 预览地址:http://139.155.137.144:8880/swagger/index.html
前端vue 预览地址:http://139.155.137.144:8881
关注公众号:发送【权限】,获取源码
有兴趣的朋友,请关注我微信公众号吧(*^▽^*)。
关注我:一个全栈多端的宝藏博主,定时分享技术文章,不定时分享开源项目。关注我,带你认识不一样的程序世界