Vue-异步树+自定义穿梭框的实现

1、效果图

                

2、需求

       首先接口获取到已添加人员列表,然后通过异步树+穿梭框进行人员的增加和删除。使用树结构展示组织信息,点击父节点展开按钮获取该组织下的所属组织,点击树节点传递获取该组织的所属人员,将人员展示在待添加列表,人员可以左移右移,并且左移时会进行回显(只回显属于该组织下的人员),最后点击保存就将右侧已添加列表进行保存。

3、源码

<template>
    <div>
        <el-button @click="handleAdd">添加</el-button>
        <el-dialog :area="[940, 628]" title="添加推送用户" :visible.sync="isShow" :show-close="false"> 
            <div class="tree-box">
                <el-tree node-key="deptIndexCode" parent-key="parentIndexCode" :props="props"
                    :data="treeData" simple-data lazy :default-expanded-keys="expandNode" 
                    :current-node-key="currentNode" :load="setTreeData" @node-click="handleClickNode"
                ></el-tree>
            </div>
            <div class="table-box">
                <div class="left-box">
                    <h3>待添加({{ leftData.length }}/{{ leftData.length }})</h3>            
                    <el-table :data="leftData" force-scroll max-height="426" @selection-change="handleLeftSelect">
                        <el-table-column type="selection"></el-table-column>
                        <el-table-column prop="username" label="用户名"></el-table-column>
                    </el-table>
                </div>
                <div class="btn-box">
                    <el-button @click="moveRight" :disabled="!leftSelect.length!=0">
                        <i class="h-icon-angle_right_sm"></i>
                    </el-button>
                    <el-button @click="moveLeft" :disabled="!rightSelect.length!=0">
                        <i class="h-icon-angle_left_sm"></i>
                    </el-button>
                </div>
                <div class="right-box">
                    <h3>已添加({{ rightData.length }}/{{ rightData.length }})</h3>
                    <el-table :data="rightData" force-scroll max-height="426" @selection-change="handleRightSelect">
                        <el-table-column type="selection"></el-table-column>
                        <el-table-column prop="username" label="用户名"></el-table-column>
                    </el-table>
                </div>
            </div>
            <div slot="footer" class="dialog-footer">
                <el-button type="primary" :disabled="rightData.length == 0" @click="handleSubmit">保 存</el-button>
                <el-button @click="handleCancel">取 消</el-button>
            </div> 
        </el-dialog>
    </div>
</template>

<script>
    import { getDepartment, getUsers } from '@/service/importantPersonArrange'
    export default {
        name: 'LastForm',
        data() {
            return {
                list: [],           // 接口获取到的右侧数据列表
                props: {
                    label: 'name',
                    children: 'zones'
                },
                treeData: [],
                expandNode: [],
                currentNode: "",
                isShow: false,      // 当前选中树节点
                treeSelect: {},
                leftData: [],       // 待添加数据
                rightData: [],      // 已添加数据
                leftSelect: [],     // 勾选的待添加数据
                rightSelect: [],    // 勾选的已添加数据
                copyRightData: [],  // 拷贝已添加数据
            }
        },
        methods: {
            // 打开添加推送用户弹窗
            handleAdd() {
                this.isShow = true;
                // 深拷贝右侧数据,如果用户点击取消进行还原
                this.copyRightData = JSON.parse(JSON.stringify(this.rightData));
                // 这一步操作是过滤左侧数据,因为可能出现用户在弹窗页面移动数据到左侧之后直接点击取消,leftData数据未还原
                this.getPersonList();
            },

            // 获取部门
            async setTreeData(node, resolve) {
                const params = {
                    parentId: node.data.deptIndexCode || "-1",
                }
                const { data } = await getDepartment(params);
                // 初始化时默认查展开根节点,并选中根节点
                if (params.parentId == -1) {
                    this.expandNode = [data[0].deptIndexCode];
                    this.currentNode = data[0].deptIndexCode;
                    this.getPersonList(data[0]);
                }
                resolve(data);
            },

            // 点击树节点
            handleClickNode(data) {
                this.treeSelect = data;
                this.getPersonList();
            },

            // 获取部门用户
            getPersonList() {
                if (this.treeSelect.deptIndexCode) {
                    getUsers({
                        deptIndexCode: this.treeSelect.deptIndexCode,
                    }).then(res => {
                        this.leftData = res.data.map(item => {
                            // 这里将左侧的数据格式进行过滤,跟右侧接口读取到的数据格式一致
                            return {
                                indexCode: item.userIndexCode,
                                username: item.realName,
                                // 后端返回的右侧数据格式必须增加一个deptIndexCode(或其他字段)标识,标明所属的树节点indexCode
                                deptIndexCode: item.deptIndexCode
                            }
                        })
                        // 将每次点击异步树节点后获取到的用户数据过滤掉跟右侧数据重复的数据
                        this.leftData = this.leftData.filter(x =>
                            !this.rightData.some(y => x.indexCode === y.indexCode)
                        )
                    })
                }
            },

            // 待添加数据选中
            handleLeftSelect(data) {
                this.leftSelect = data;
            },

            // 已添加数据选中
            handleRightSelect(data) {
                this.rightSelect = data;
            },

            // 数据右移
            moveRight() {
                // 右侧数据添加左侧勾选的数据
                this.rightData = this.rightData.concat(this.leftSelect);
                // 左侧数据删除勾选的数据
                this.leftData = this.leftData.filter(x => this.leftSelect.every(y => x.indexCode != y.indexCode));
            },

            // 数据左移
            moveLeft() {
                // 左侧数据添加左移列表中跟当前选择树节点deptIndexCode相同的数据
                this.rightSelect.forEach(item => {
                    if (item.deptIndexCode === this.treeSelect.deptIndexCode) {
                        this.leftData.push(item);
                    }
                });
                // 右侧数据删除勾选的数据
                this.rightData = this.rightData.filter(x => this.rightSelect.every(y => x.indexCode != y.indexCode));
            },

            // 保存
            handleSubmit() {
                // 更新接口获取到的右侧数据列表,以进行保存操作
                this.list = this.rightData;
                this.isShow = false;
            },

            // 取消
            handleCancel() {
                // 点击取消按钮还原右侧数据
                this.rightData = JSON.parse(JSON.stringify(this.copyRightData));
                this.isShow = false;
            }
        },
        mounted() {
            // todo 接口获取 list 列表
            this.rightData = JSON.parse(JSON.stringify(this.list));
        }
    }
</script>

<style lang="less" scoped>
    /deep/ .el-dialog__body {
        padding: 0;
    }

    // /deep/ .el-table::before {
    //     height: 0;
    // }

    // /deep/ .el-table .el-table__body-wrapper::before {
    //     height: 0;
    // }

    /deep/ .el-dialog__body-wrapper {
        padding: 30px 40px;
    }

    .tree-box {
        height: 476px;
        border: 1px solid rgb(229, 229, 229);
        border-right: 0px;
        width: 240px;
        display: inline-block;
        vertical-align: top;
        padding: 12px 8px;
    }

    /deep/ .table-box {
        height: 476px;
        width: 620px;
        display: inline-block;
        vertical-align: top;
        .left-box,
        .right-box {
            width: 274px;
            height: 476px;
            padding: 0 16px;
            border: 1px solid #e5e5e5;
            display: inline-block;
            vertical-align: top;
            h3 {
                height: 48px;
                padding: 12px 0;
                margin: 0;
                color: #4c4c4c;
                font-size: 14px;
                font-weight: normal;
                line-height: 20px;
            }
            .el-table {
                border: 0px;
            }
            .el-table:after {
                background-color: transparent;
            }
            .el-table__empty-block {
                min-height: 389px;
            }
        }
        .btn-box {
            width: 72px;
            height: 476px;
            display: inline-block;
            position: relative;
            /deep/ .el-button {
                width: 40px;
                min-width: 40px;
                padding: 0;
                position: absolute;
                left: 50%;
                margin-left: -20px;
                top: 150px;
                &:last-of-type {
                    top: 206px;
                }
                &.is-disabled {
                    background-color: transparent;
                }
            }
        }
    }
</style>

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值