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

前言: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);

        }

    })

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容