<template> <div class="myTable"> <div class="searchNav"> <el-button class="newAdd" type="primary" @click="showAddPermissionPage">新增</el-button> <span><span>角色 :</span><el-select v-model="searchInfo.role" clearable placeholder="請選擇角色"> <el-option v-for="(item, i) in roleList" :key="i" :label="item.description" :value="item.role"> </el-option> </el-select></span> <el-button type="primary" @click="search" style="float: right; position: relative;left: -20px;" >搜索</el-button> </div> <div class="tableStyle"> <el-table v-loading="loading" :data="tableData" style="width: 100%;" :row-class-name="tableRowClassName"> <el-table-column prop="role" label="角色" ></el-table-column> <el-table-column prop="description" label="描述" ></el-table-column> <el-table-column prop="createTime" label="創建時間"></el-table-column> <el-table-column prop="updateTime" label="修改時間"></el-table-column> <el-table-column fixed="right" label="操作" width="250"> <template slot-scope="scope"> <el-button type="text" size="small" @click="editRole(scope.row)">編輯</el-button> <el-button type="text" size="small" @click="confirmRemove(scope.row.id)">刪除</el-button> </template> </el-table-column> </el-table> <el-pagination @current-change="handleCurrentChange" :current-page.sync="currentPage" :page-size="searchInfo.pageSize" background layout="total, prev, pager, next" :total="total"> </el-pagination> </div> <div> <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" :before-close="handleClose"> <div class="addInfoStyle"> <div><span>角色:</span><el-input v-model="addInfo.role" placeholder="請輸入" clearable></el-input> <span>描述:</span><el-input v-model="addInfo.description" placeholder="請輸入" clearable></el-input> </div> <div><span>權限:</span> <div style="margin:0px 20px;"> <div style=" float: left; border: 1px solid #ccc;padding: 10px; width: 230px;"> <div>未有權限</div> <el-tree style="height: 330px;overflow-y: auto;" :data="unOwnPermissions" default-expand-all show-checkbox node-key="id" ref="unOwnTree" highlight-current :default-checked-keys="defaultCheckedKeys" :props="defaultProps"> </el-tree> </div> <div style=" float: left;"> <button @click="removeRight"><i class="el-icon-d-arrow-right"></i></button> <br> <button @click="removeLeft"><i class="el-icon-d-arrow-left"></i></button> </div> <div style=" float: right; border: 1px solid #ccc;padding: 10px; width: 230px;"> <div>已有權限</div> <el-tree style="height: 330px;overflow-y: auto;" :data="ownPermissions" default-expand-all show-checkbox node-key="id" ref="ownTree" highlight-current :props="defaultProps"> </el-tree> </div> </div> </div> </div> <div style="clear: both"></div> <span slot="footer" class="dialog-footer"> <el-button @click="dialogVisible = false">取 消</el-button> <el-button type="primary" @click="save">確 定</el-button> </span> </el-dialog> </div> </div> </template> <script> import {axios} from "../../config/axios"; let currentTree; // 臨時保存要移向的樹 export default { name: "white-list", data () { return { unOwnPermissions: [], // 未擁有的樹權限數據 ownPermissions:[], // 已擁有的樹權限數據 havePermissions:[], defaultCheckedKeys:[], dialogTitle:'', interval:0, // 定時任務解決 將左側選擇的移到右側 時可以出現 爲空的情況 defaultProps: { children: 'children', label: 'label' }, total:1, tableData: [], loading: false, dialogVisible: false, currentPage:1, roleList:'', // 得到角色 addInfo:{ role:'', description:'', addPermissions:[], removePermissions:[] }, searchInfo:{ role:'', pageNumber:0, pageSize:10, } } }, created: function () { this.getAllRole(); // 搜索框中使用 this.pageInfo(); }, methods:{ removeRight(){ let $unOwnTree = this.$refs.unOwnTree; let checkedNodes = $unOwnTree.getCheckedKeys(); console.log("removeRight:",checkedNodes); checkedNodes.forEach(item =>{ let node = $unOwnTree.getNode(item); let nodeData = $unOwnTree.getCheckedNodes(item); if (node !== null){ currentTree = this.$refs.ownTree; this.appendNode(node); $unOwnTree.remove(node,nodeData); } }); clearInterval(this.interval); // 當移過來的不爲空是,停止定時任務。 }, removeLeft(){ let $ownTree = this.$refs.ownTree; let checkedNodes = $ownTree.getCheckedKeys(); console.log("removeLeft:",checkedNodes); checkedNodes.forEach(item =>{ let node = $ownTree.getNode(item); let nodeData = $ownTree.getCheckedNodes(item); if (node !== null){ currentTree = this.$refs.unOwnTree; this.appendNode(node); // 移除左邊的 $ownTree.remove(node,nodeData); } }) }, // 這個編寫過程是先從左邊 向右邊添加,當各種情況都滿足時。再加上反向操作。 appendNode(node){ // 1、如果移動的是一級目錄 // ①先判斷 當前目錄 在右邊一個目錄的位置 // 先判斷是否爲一級目錄 if (node.level === 1){ // level = 1 說明是一級目錄 // 獲取節點的 id const oneMenuNodeData = node.data; this.oneMenuHandle(oneMenuNodeData); /* * 2、如果移動的是二級目 * ① 當右邊沒有一級目錄時 * 一、先要獲取一級目錄數據 * 二、查看應該插入右邊目錄位置 * ② 當右邊有一級目錄 * 一、先要獲取二級目錄的位置 */ }else if(node.level ===2){ const oneMenu = JSON.parse(JSON.stringify(node.parent.data)); // 深度拷貝 纔不會影響到 左側的樹結構 oneMenu.children = [node.data]; this.oneMenuHandle(oneMenu); /* * 3、如果移動的是權限 * ① 當右邊沒有一級目錄時 * 一、當沒有二級目錄時 * 1、創建,二級目錄,一個目錄,直接遷移 * 二、當有二級目錄時 * 判斷權限插入的位置 * ② 當右邊有一級目錄 * 一、有二級目錄。 * 判斷權限插入位置 * 二、沒有二級目錄 * 把二級目錄一起遷移過去 **/ }else { // 移動的是權限 const ownTowMenuId = node.parent.data.id; let towMenuNode = currentTree.getNode(ownTowMenuId); if (towMenuNode === null){ //當沒有二級菜單 const ownOneMenuId = node.parent.parent.data.id; let oneMenuNode = currentTree.getNode(ownOneMenuId); let removeTowMenuData = JSON.parse(JSON.stringify(node.parent.data)); // 將權限放到二級菜單中 removeTowMenuData.children = [node.data]; if (oneMenuNode === null){ // 當沒有一級菜單 let removeOneMenuData = JSON.parse(JSON.stringify(node.parent.parent.data)); removeOneMenuData.children = [removeTowMenuData]; this.oneMenuHandle(removeOneMenuData); } else{ // 當有一級菜單 this.oneMenuHandle(removeTowMenuData); } } else { //當有二級菜單 this.towMenuHandle(node.data,towMenuNode); } } }, // 當有二級目的時候 towMenuHandle(permission, towMenuNode){ // 獲取二級目錄下面所有權限 let ownPermissions = towMenuNode.data.children; let ownPermissionsArr = []; ownPermissions.forEach(permission =>{ ownPermissionsArr.push(permission.id); }); this.toInsert(ownPermissionsArr,permission); }, oneMenuHandle(oneMenuNodeData){ let ownOneMenu = currentTree.getNode(oneMenuNodeData.id); if (ownOneMenu === null) { // 說明右邊未擁有 // 獲取右邊所有的一級目。 const ownTreeDataArr = currentTree.data; let ownOneMenus = []; ownTreeDataArr.forEach(oneMenu =>{ ownOneMenus.push(oneMenu.id); }) // 獲取當前一級目錄附近的一級目錄的 id 和 插入當前id 之前還是之後。 if (ownOneMenus.length >0){ this.toInsert(ownOneMenus,oneMenuNodeData); }else { currentTree.append(oneMenuNodeData,0); } }else{ // 右邊擁有一級目錄時 // 需要右移的二級目錄 遍歷進行插入 let rightMoveTowMenu = oneMenuNodeData.children; rightMoveTowMenu.forEach(towMenu =>{ let ownTowMenu = currentTree.getNode(towMenu.id); // 獲取右邊擁有的二級目錄 if(ownTowMenu === null){ // 如果不存在 let ownTowTreeDataArr = ownOneMenu.data.children; let ownTowMenus = []; ownTowTreeDataArr.forEach(oneMenu =>{ ownTowMenus.push(oneMenu.id); }) this.toInsert(ownTowMenus,towMenu); }else{ // 如果存在 let ownPermissions = []; // 獲取原來的 二級目錄下的所有的權限 ownTowMenu.data.children.forEach(ownPermission =>{ ownPermissions.push(ownPermission.id); }); // 得到 要移動的二級目錄下的所有權限 towMenu.children.forEach(unOwnPermission =>{ this.toInsert(ownPermissions,unOwnPermission); }) } }); } }, // 獲取當前一級目錄附近的一級目錄的 id 和 插入當前id 之前還是之後。 getNearbyOneMenu(ownOneMenus,oneMenuId){ let nearbyId = -1; let isBefore = false; ownOneMenus.some(id =>{ if (oneMenuId<id){ nearbyId = id; isBefore = true; return true; } }); // 如果 nearbyId !== -1 則將 ownOneMenus 數組中最大的賦值給 nearbyId nearbyId = nearbyId !== -1?nearbyId:ownOneMenus[ownOneMenus.length-1]; return {nearbyId:nearbyId,isBefore:isBefore}; }, toInsert(arr,toMoveNode){ const nearbyOnePermission = this.getNearbyOneMenu(arr.sort(),toMoveNode.id); if (nearbyOnePermission.isBefore) { currentTree.insertBefore(toMoveNode,nearbyOnePermission.nearbyId); }else{ currentTree.insertAfter(toMoveNode,nearbyOnePermission.nearbyId); } }, search(){ this.pageInfo(); }, showAddPermissionPage(){ this.dialogTitle = "新增角色"; this.dialogVisible = true; this.ownPermissions = Object.values([]); // 清空右側的權限 this.getPermission(); // 獲取左側的權限 }, tableRowClassName({row, rowIndex}) { if (rowIndex%2 ===0) { return 'success-row'; } return ''; }, handleCurrentChange(val) { this.currentPage = val; this.searchInfo.pageNumber = val -1; this.pageInfo(); }, validate(){ let flag = true; let msg = ""; if (this.addInfo.role.length < 3){ flag = false; msg = " 角色 長度有誤 " }else if(this.addInfo.description.length < 3){ flag = false; msg = "角色描述 長度有誤 " } return {code:flag,msg:msg} }, save(){ let validateObj = this.validate(); if (validateObj.code){ if (typeof (this.addInfo.id) === "undefined"){ // 新增 this.getPermissionsId(this.$refs.ownTree.data); // 遞歸得到所有節點的 id console.log("permissions:",this.addInfo.addPermissions); axios.post('/role',this.addInfo).then(result => { this.dialogVisible = false; if (result.code !== 200){ console.log(result); } this.pageInfo(); this.clearObj(this.addInfo); // 清空新增數據 }).catch(err => { console.log(err); }); } else { // 編輯 console.log("this.$refs.ownTree.data:",this.$refs.ownTree.data); this.getPermissionsId(this.$refs.ownTree.data); // 遞歸得到所有節點的 id let diffInfo = this.diff(this.havePermissions,this.addInfo.addPermissions); // 比較兩個數組差異 this.addInfo.addPermissions = diffInfo[0]; this.addInfo.removePermissions = diffInfo[1]; console.log("diffInfo:",diffInfo); axios.put('/role',this.addInfo).then(result => { this.dialogVisible = false; if (result.code !== 200){ console.log(result); } this.pageInfo(); this.clearObj(this.addInfo); // 清空新增數據 }).catch(err => { console.log(err); }); } }else{ this.msg(validateObj.msg); } }, editRole(obj){ this.dialogTitle = "編輯角色"; this.ownPermissions = Object.values([]); // 清空右側的權限 const newObj = JSON.parse(JSON.stringify(obj)); console.log("roleId:",obj); this.addInfo = {id:newObj.id, role:newObj.role, description:newObj.description, addPermissions:[], removePermissions:[]}; this.dialogVisible = true; // 展現頁面 this.getOwnPermissions(obj.id); // 所有右側目錄和權限 this.getPermission(); this.interval = setInterval(() => { console.log("interval:======================="); this.removeRight(); }, 1000) }, getPermissionsId(data){ console.log("this.addInfo",this.addInfo); data.forEach( item =>{ this.addInfo.addPermissions.push(item.id); if( item.children.length > 0 ){ this.getPermissionsId(item.children); } }) }, remove(id){ axios.delete('/role',{data: {id:id}}).then(result => { if (result.code !== 200){ console.log(result); } this.pageInfo(); }) }, confirmRemove(obj){ this.$confirm('此操作將刪除該數據, 是否繼續?', '提示', { confirmButtonText: '確定', cancelButtonText: '取消', type: 'warning' }).then(() => { this.remove(obj); this.$message({ type: 'success', message: '刪除成功!' }); }).catch(() => { this.$message({ type: 'info', message: '已取消刪除' }); }); }, diff(arr1, arr2) { // 比較兩個數組的差異 var result = []; var addArr = []; var lessenArr = []; var arr3=arr1.concat(arr2);//將arr1和arr2合併爲arr3 for(var i = 0; i<arr3.length; i++){ if(arr1.indexOf(arr3[i])<0){ addArr.push(arr3[i]); } if(arr2.indexOf(arr3[i])<0){ lessenArr.push(arr3[i]); } } result.push(addArr); result.push(lessenArr); return result; }, handleClose(done) { // 彈出框點擊右上角的 X 隱藏彈出框 this.clearObj(this.addInfo); // 清空新增數據 done(); }, getAllRole(){ axios.get('/role-all',).then(result => { this.loading=false; if (result.code !== 200){ console.log(result); } this.roleList = result.data; }).catch(err => { console.log(err); this.loading=false; }); }, pageInfo(){ if (this.loading) return; // 防止重複提交 this.loading=true; axios.get('/role',{ params: this.searchInfo }).then(result => { this.loading=false; if (result.code !== 200){ console.log(result); } let page = result.data; this.total = page.total; this.tableData = page.list; }).catch(err => { console.log(err); this.loading=false; }); }, getPermission(){ axios.get('/permission').then(result => { if (result.code !== 200){ console.log(result); } this.unOwnPermissions = this.toTree(result.data); }); }, getOwnPermissions(roleId){ this.havePermissions = []; this.defaultCheckedKeys = []; axios.get('/permissionByRoleId',{params:{roleId:roleId}}).then(result => { if (result.code !== 200){ console.log(result); } // 保存 角色現有的所有的權限 result.data.forEach(item =>{ let permissionId = item.id; this.havePermissions.push(permissionId); if (item.type === 0){ this.defaultCheckedKeys.push(permissionId); } }); }); }, toTree(data){ let firstMenuMap = new Map(); // 以第一目錄id 爲 key,下標作爲 value let secondMenuMap = new Map(); let permissionMap = new Map(); let iMap = new Map(); let j = 0; const parent = {} ; data.forEach(item =>{ if(item.type === 0 && item.parentId ===0){ // 這個是一級菜單 parent[j] = { label: item.permission, value: item.id, id: item.id, children: [], }; firstMenuMap.set(item.id,j); j++; }else { if(item.type === 0){ // 這個是二級菜單 permissionMap.set(item.id, item.parentId); let parentIdIndex = secondMenuMap.get(item.parentId); if(typeof(parentIdIndex) === "undefined"){ parentIdIndex = 0; secondMenuMap.set(item.parentId, parentIdIndex); }else{ parentIdIndex = parentIdIndex+1; secondMenuMap.set(item.parentId, parentIdIndex); } iMap.set(item.id, parentIdIndex); const oneIndex = firstMenuMap.get(item.parentId); parent[oneIndex].children.push({ label: item.description, value: item.id, id: item.id, children: [] }); }else{ // 這是權限 const secondMenuId = permissionMap.get(item.parentId); // 單純獲取獲取二級菜單的id const firstMenuIndex = firstMenuMap.get(secondMenuId); // 通過二級菜單獲取一級菜單的下標 const indexPermissionIndex = iMap.get(item.parentId); parent[firstMenuIndex].children[indexPermissionIndex].children.push({ label: item.description, value: item.id, id: item.id, children: [] }) } } }); return Object.values(parent); } } } </script> <style lang="less" scoped> .addInfoStyle div{ line-height: 4; .el-input{ margin-right: 15px; width: 200px; } } </style>