美文网首页程序员
vue-antd的tree组件的异步加载

vue-antd的tree组件的异步加载

作者: 阿鱼鱼_shelly | 来源:发表于2022-10-19 19:14 被阅读0次

    前言:tree组件自定义slot,数据项需要手动添加scopedSlots。

    一、绘制的页面如下:

    二、组件代码

    /**

     *

     * @param {*} replaceFields 树结构字段替换 object

     * @param {*} defaultExpandParent 默认展开父节点 boolean

     * @param {*} default-expand-all 默认展开所有节点 boolean

     * @param {*} tree-data 树的数据 array

     * @param {*} defaultExpandedKeys 展开默认节点 array

     * @param {*} menuList 菜单 array

     * @param {*} treeType 树的类型 主题:theme 目录:catalogue

     * @param {*} interfaceType 接口参数类型

     * @param {*} expand 隐藏菜单 function

     * @param {*} rightClick 右击事件 function

     * @param {*} select 选中事件 function

     * @param {*} load-data 异步加载事件 function

     */

    <template>

        <div class="tree outside-wrap-container">

            <a-tree

                v-if="treeData.length"

                id="TreeMenu"

                ref="tree"

                key="id"

                :replaceFields="defaultFields"

                :defaultExpandParent="true"

                :default-expand-all="treeExpandAll"

                :tree-data="treeData"

                :defaultExpandedKeys="defaultExpandedKeys"

                :menuList ='menuList'

                :loadedKeys="loadedKeys"

                @expand="hiddenMenu"

                @rightClick="rightClick"

                @select="handleNodeSelect"

                :load-data="onLoadData"

                v-model="checkedKeys"

            >

                <template slot="custom" slot-scope="node">

                    <div

                        class="custom-tree-node"

                        :class="clickBlueDag == node.eventKey ? 'click-blue' : ''"

                        @click="handleNodeClick(node)"

                    >

                        <a-icon

                            :type="iconType(node[replaceFields.type], node)"

                            :class="

                                clickBlueDag == node.eventKey ? 'click-blue' : ''

                            "

                        />

                        <!-- 标签名 -->

                        <div class="node-ellipsis node">

                            <a-tooltip

                                placement="bottom"

                                :title="node[replaceFields.name]"

                            >

                                <span

                                    :key="node.eventKey + 'name'"

                                    class="node"

                                    :class="

                                        clickBlueDag == node.eventKey ? 'click-blue' : ''

                                    "

                                >

                                    {{ node[replaceFields.name]}}

                                </span>

                            </a-tooltip>

                        </div>

                        <!-- 气泡提示 -->

                        <a-tooltip placement="bottom" v-if="node.tooltip">

                            <template slot="title">

                                <span>{{ node.tooltip }}</span>

                            </template>

                            <a-icon type="question-circle" />

                        </a-tooltip>

                        <!-- 操作 -->

                        <ul

                            :id="'Menu' + node.eventKey"

                            v-show="showRightActions && showMenu == node.eventKey"

                            class="menu"

                        >

                            <li

                                v-show="transJs(item.condition, node)"

                                @click="menuEvent(node,item.type)"

                                v-for="item in menuList"

                                :key="item.name"

                            >

                                {{ item.name }}

                            </li>

                        </ul>

                    </div>

                </template>

            </a-tree>

        </div>

    </template>

    <script>

    import Utils from '@/common/utils'

    import COMMONAPI from '@/api/api_common.js'

    export default {

        props: {

            menuList: {

                type: Array,

                required: false,

                default: () => {

                    return []

                }

            },

            defaultExpandedKeys: {

                type: Array,

                required: false,

                default: () => {

                    return ['-1']

                }

            },

            treeExpandAll: {

                type: Boolean,

                required: false,

                default: false

            },

            showRightActions: {

                // 是否需要右击操作

                type: Boolean,

                required: true

            },

            treeType: {

                // 目录树或主题住

                type: String,

                required: false,

                default: 'theme'

            },

            interfaceType: {

                // 接口参数

                type: String,

                required: false,

                default: 'DOMAIN'

            },

            defaultFields: {

                // 自定义绑定属性

                type: Object,

                required: false,

                default: () => {

                    return {

                        title: 'title',

                        key: 'key',

                        name: 'name',

                        type: 'type' // 节点层级

                    }

                }

            }

        },

        computed: {

            replaceFields() {

                return {

                    title: this.defaultFields.title ? this.defaultFields.title : 'title',

                    key: this.defaultFields.key ? this.defaultFields.key : 'key',

                    name: this.defaultFields.name ? this.defaultFields.name : 'name',

                    type: this.defaultFields.type ? this.defaultFields.type : 'type'

                }

            }

        },

        data() {

            return {

                loadedKeys: [],

                checkedKeys: [],

                showMenu: 0, // 展示菜单

                showRename: 0, // 展示重命名

                inputRename: '', // 重命名输入框

                clickBlueDag: null, // 判断点击字体变色

                parent: '', // 父节点

                treeDataSlots: [],

                treeManage: null,

                treeData: []

            }

        },

        created() {

            this.loadedKeys =[]

            let node = {

                    id: '-1'

            }

            this.getLoadData(node, (res) => {

                this.setTreeData(res)

            })

        },

        methods: {

            onLoadData(node) {

                return new Promise(resolve => {

                    if (node.dataRef.children && node.dataRef.children.length) {

                        resolve();

                        return;

                    }

                    let nodeInfo = {

                        id: node.dataRef.id

                    }

                    this.getLoadData(nodeInfo, (res) => {

                        this.treeData = [...res];

                        resolve();

                    })

                });

            },

            getLoadData(node, callback) {

                // 获取tree列表

                let params ={

                    parentId: node.id,

                    type: this.interfaceType

                }

                COMMONAPI.themeLists(params)

                    .then(res => {

                        let children = res.map(t => {

                                return {

                                    ...t,

                                    name: t.parentId==='-1'&&this.treeType==='theme'?'主题':t.name,

                                    key: t.id,

                                    scopedSlots: {

                                        title: 'custom'

                                    }

                                }

                        })

                        if(node.id==='-1'){

                            // 根目录初始化

                            this.setTreeData(children)

                        }else{

                            // 异步加载

                            const data = [...this.treeData];

                            Utils.loopTree(data, node.id, item => { // 目标节点更改

                                item.children = children || []

                                item.isLeaf = !item.children || item.children.length == 0

                            }, 'id');

                            this.setTreeData(this.treeData)

                            callback(data);

                        }

                    })

                    .catch(() => {})

            },

            iconType(type, node) {

                if (node.layer > -1) {

                    return 'folder-open'

                }

                if (this.treeManage && this.treeManage.iconType) {

                    return this.treeManage.iconType(type)

                }

                return type === 1 ? 'folder-open' : 'file-text'

            },

            resetTree() {

                this.clickBlueDag = null

            },

            setTreeData(treeData) {

                this.treeData = [...treeData]

                this.$forceUpdate()

            },

            transJs(conditon, node) {

                // js语句执行

                if (conditon && this.showRightActions) {

                    let reg = new RegExp(`node.${this.replaceFields.type}`, 'g')

                    let str = conditon.replace(reg, node[this.replaceFields.type])

                    let Fn = Function // 一个变量指向Function,防止有些前端编译工具报错,替代eval方法

                    return new Fn('return ' + str)()

                }

            },

            hiddenMenu(keys, expands) {

                if (keys) {

                    if (keys.length > 1) {

                        this.$emit(

                            'treeNodeExpands',

                            expands.node.$vnode.data.props

                        )

                    }

                }

                // 去除弹框

                this.showMenu = 0

            },

            handleNodeSelect(code) {

                // 选择节点

                this.$emit('treeNodeSelect', code)

                this.hiddenMenu()

            },

            handleNodeClick(node) {

                // 左击树节点

                this.clickBlueDag = node.eventKey

                this.$emit('treeNodeClick', node)

                this.hiddenMenu()

            },

            rightClick({ event, node }) {

                // 右击树节点

                this.parent = node.$parent // 父节点

                this.showMenu = node.eventKey

                // 计算菜单个数

                let cal_menu_height = () => {

                    let height = (Number(this.menuList.length) + 1) * 20 + 2 * 5

                    return height

                }

                // 判定是否需要菜单上移

                let need_menu_move_up = function() {

                    let need = false

                    let dy = window.innerHeight

                    if (dy < menu_height) {

                        need = true

                    }

                    return need

                }

                // 需要移动的高度

                let move_height = 0

                let menu_num = Number(this.menuList.length)

                // 没有菜单

                if (menu_num == 0) {

                    this.showMenu = 0

                    return

                }

                let menu_height = cal_menu_height()

                if (need_menu_move_up()) {

                    move_height = menu_height

                }

                // 计算x轴偏移

                document.getElementById('Menu' + node.eventKey).style.left =

                    event.clientX + 30 + 'px'

                // 计算y轴偏移

                document.getElementById('Menu' + node.eventKey).style.top =

                    event.clientY - move_height + 'px'

            },

            menuEvent(node, type){

                if(type==='add'){

                    this.newTask(node)

                }else if(type==='edit'){

                    this.editTask(node)

                }else if(type==='delete'){

                    this.deleteTask(node)

                }else if(type==='release'){

                    this.releaseTask(node)

                }

            },

            newTask(node) {

                // 新建任务

                this.$emit('treeNewTask', node)

                node.expanded = true

                this.newTaskData = node

            },

            editTask(node) {

                // 编辑任务

                this.$emit('treeEditTask', node)

                node.expanded = true

            },

            deleteTask(node) {

                // 删除任务

                this.$emit('treeDeleteTask', node)

                node.expanded = true

            },

            releaseTask(node) {

                //   发布任务

                this.$emit('treeReleaseTask', node)

                node.expanded = true

                this.newTaskData = node

            },

            editDataSet(node) {

                // 修改

                this.$emit('treeEditData', node)

                node.expanded = true

                this.newTaskData = node

                // 计算菜单个数

                let cal_menu_height = () => {

                    let height = (Number(this.menuList.length) + 1) * 20 + 2 * 5

                    return height

                }

                let menu_height = cal_menu_height()

                let move_height = menu_height

                // 计算x轴偏移

                document.getElementById('Menu' + node.eventKey).style.left =

                    event.clientX + 30 + 'px'

                // 计算y轴偏移

                document.getElementById('Menu' + node.eventKey).style.top =

                    event.clientY - move_height + 'px'

            }

        }

    }

    </script>

    <style lang="scss" scoped>

    .icon {

        width: 1em;

        height: 1em;

        vertical-align: -0.15em;

        fill: currentColor;

        overflow: hidden;

    }

    .tree {

        height: 100%;

        padding: 20px;

        overflow: auto;

        .click-blue {

            //点击字体颜色

            background-color: #f0f4ff;

            border-radius: 2px;

            color: $primaryColor;

        }

        /deep/.ant-tree-node-content-wrapper.ant-tree-node-selected {

            background-color: inherit !important;

        }

    }

    .custom-tree-node {

        width: 100%;

        padding: 0 5px;

        .node-ellipsis {

            margin-left: 10px;

            max-width: 120px;

            vertical-align: middle;

            overflow: hidden;

            text-overflow: ellipsis;

            white-space: nowrap;

            display: inline-block;

        }

        .menu {

            display: inline-block;

            position: fixed;

            border-radius: 3px;

            list-style-type: none;

            z-index: 99;

            box-shadow: 0 0 5px 1px #00000038;

            padding: 0;

            li {

                padding: 6px 0;

                width: 88px;

                padding: 0 16px;

                font-size: 12px;

                height: 32px;

                line-height: 32px;

                color: #666666;

                background-color: #fff;

            }

            li:hover {

                background: #f4f6f7;

                color: #333333;

            }

        }

    }

    .custom-tree-node:hover {

        background-color: #f0f4ff;

        border-radius: 2px;

    }

    /deep/ .ant-tree li span.ant-tree-switcher {

        color: #d9d9d9;

    }

    /deep/.ant-tree li .ant-tree-node-content-wrapper {

        min-width: 100%;

        // padding: 0 5px;

    }

    /deep/ .ant-tree li .ant-tree-node-content-wrapper:hover {

        background-color: inherit;

    }

    /deep/.ant-tree li span.ant-tree-switcher,

    .ant-tree li span.ant-tree-iconEle {

        width: 5px;

    }

    </style>

    三、树循环的js函数

    /**

     * 树结构

     * @param {Array} data 树的结构

     * @param {String} key 当前节点id

     * @param {String} callback  回调函数

     * @param {String} defaultKey 默认节点

     * @returns Array

     */

    function loopTree(data, key, callback, defaultKey) { // 循环树节点

        data.forEach((item, index, arr) => {

            if (item[defaultKey] === key) {

                return callback(item, index, arr);

            }

            if (item.children) {

                return this.loopTree(item.children, key, callback, defaultKey);

            }

        })

    }

    相关文章

      网友评论

        本文标题:vue-antd的tree组件的异步加载

        本文链接:https://www.haomeiwen.com/subject/bpguzrtx.html