什么!你還不會寫Vue組件,編寫《數據級權限》匹配公式組件
說明
該文章是屬于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; // 父級id pid: 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-select v-model="item.matchGroup" placeholder="請選擇" style=" float: left; margin-right: 10px; margin-left: 10px; min-width: 100px; " > <el-option v-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-button v-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-match v-if="item.children && item.children.length" :data="item.children" />
以上代碼是實現遞歸的關鍵,位置一定要準。
說明一點xc-match一定要和頁面導出的名稱一樣。
看效果圖

添加條件及條件按鈕
添加條件項
在match.vue頁面xc-match元素前,添加如下代碼
<div class="itemBackColor" v-for="whereItem in item.matchingWhere" :key="whereItem.id" > <!-- 匹配條件 --> <el-select v-model="whereItem.fieldKey" placeholder="請選擇匹配條件" style="width: 240px" > <el-option v-for="where in matchingWhere" :key="where.value" :label="where.label" :value="where.value" /> </el-select> <!-- 匹配等式 --> <el-select v-model="whereItem.matchEquationKey" placeholder="請選擇等式" style="width: 240px" > <el-option v-for="equation in matchingEquation" :key="equation.value" :label="equation.label" :value="equation.value" /> </el-select> <!-- 匹配值 --> <el-input-number v-model="whereItem.matchDataKey" :step="1" min="1" max="200" step-strictly style="width: 240px" v-if="whereItem.fieldKey === 'Age'" /> <el-select v-else-if="whereItem.fieldKey === 'Position'" v-model="whereItem.matchDataKey" placeholder="請選擇職位" style="width: 240px" > <el-option v-for="position in positionList" :key="position.value" :label="position.label" :value="position.value" /> </el-select> <el-date-picker v-else-if="whereItem.fieldKey === 'CreateTime'" v-model="whereItem.matchDataKey" type="date" style="width: 240px" placeholder="請選擇時間" /> <el-input v-else v-model="whereItem.matchDataKey" style="width: 240px" placeholder="請輸入" clearable /> <el-button type="danger" icon="Delete" plain size="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
關注公眾號:發送【權限】,獲取源碼
有興趣的朋友,請關注我微信公眾號吧(*^▽^*)。

關注我:一個全棧多端的寶藏博主,定時分享技術文章,不定時分享開源項目。關注我,帶你認識不一樣的程序世界

浙公網安備 33010602011771號