vue 樹結構 的 穿梭框

<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>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章