el-tree树组件远程搜索后继续懒加载

需求:可以搜索出符合关键字的全部节点,搜索出的节点可以继续懒加载。
实现方式:后端把符合关键字的节点组装成树结构返回给前端,前端使用packages/tree/src/model/node.js中Node类的doCreateChildren方法手动渲染树结构,并设置节点的expanded和loaded属性。

效果展示

懒加载初始化
请添加图片描述

逐级加载
请添加图片描述

按关键字搜索
请添加图片描述
请添加图片描述

搜索到的节点可以继续展开
请添加图片描述

完整代码
html原文件

代码片
关键代码是134行的renderNodes方法
代码片中只保留了北京市、天津市、河北省的数据,完整的省市县数据在附件中

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <!-- import CSS -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <title>el-tree dynamic load</title>
</head>
<body>
<div id="app">
    <el-input
            v-model="filterText"
            style="width: 20%;"
            clearable
            placeholder="输入关键字搜索"
            @keyup.enter.native="search">
    </el-input>
    <el-button icon="el-icon-search" @click="search"></el-button>
    <el-tree
            :key="treeKey"
            :props="props"
            lazy
            :load="loadNode">
    </el-tree>
</div>
</body>
<!-- import JavaScript -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script>
    new Vue({
        el: '#app',
        data() {
            return {
                // 我发现必须指定props,否则不能识别 isLeaf
                props: {
                    label: 'label',
                    children: 'children',
                    isLeaf: 'isLeaf'
                },
                treeKey: new Date().getTime(),
                filterText: null,
                // 代码片中只保留了北京市、天津市、河北省的数据,完整数据在附件中
                treeData:[{'label':'北京市','children':[{'label':'市辖区','children':[{'label':'东城区','id':3},{'label':'西城区','id':4},{'label':'朝阳区','id':5},{'label':'丰台区','id':6},{'label':'石景山区','id':7},{'label':'海淀区','id':8},{'label':'门头沟区','id':9},{'label':'房山区','id':10},{'label':'通州区','id':11},{'label':'顺义区','id':12},{'label':'昌平区','id':13},{'label':'大兴区','id':14},{'label':'怀柔区','id':15},{'label':'平谷区','id':16},{'label':'密云区','id':17},{'label':'延庆区','id':18}],'id':2}],'id':1},{'label':'天津市','children':[{'label':'市辖区','children':[{'label':'和平区','id':21},{'label':'河东区','id':22},{'label':'河西区','id':23},{'label':'南开区','id':24},{'label':'河北区','id':25},{'label':'红桥区','id':26},{'label':'东丽区','id':27},{'label':'西青区','id':28},{'label':'津南区','id':29},{'label':'北辰区','id':30},{'label':'武清区','id':31},{'label':'宝坻区','id':32},{'label':'滨海新区','id':33},{'label':'宁河区','id':34},{'label':'静海区','id':35},{'label':'蓟州区','id':36}],'id':20}],'id':19},{'label':'河北省','children':[{'label':'石家庄市','children':[{'label':'市辖区','id':39},{'label':'长安区','id':40},{'label':'桥西区','id':41},{'label':'新华区','id':42},{'label':'井陉矿区','id':43},{'label':'裕华区','id':44},{'label':'藁城区','id':45},{'label':'鹿泉区','id':46},{'label':'栾城区','id':47},{'label':'井陉县','id':48},{'label':'正定县','id':49},{'label':'行唐县','id':50},{'label':'灵寿县','id':51},{'label':'高邑县','id':52},{'label':'深泽县','id':53},{'label':'赞皇县','id':54},{'label':'无极县','id':55},{'label':'平山县','id':56},{'label':'元氏县','id':57},{'label':'赵县','id':58},{'label':'晋州市','id':59},{'label':'新乐市','id':60}],'id':38},{'label':'唐山市','children':[{'label':'市辖区','id':62},{'label':'路南区','id':63},{'label':'路北区','id':64},{'label':'古冶区','id':65},{'label':'开平区','id':66},{'label':'丰南区','id':67},{'label':'丰润区','id':68},{'label':'曹妃甸区','id':69},{'label':'滦县','id':70},{'label':'滦南县','id':71},{'label':'乐亭县','id':72},{'label':'迁西县','id':73},{'label':'玉田县','id':74},{'label':'遵化市','id':75},{'label':'迁安市','id':76}],'id':61},{'label':'秦皇岛市','children':[{'label':'市辖区','id':78},{'label':'海港区','id':79},{'label':'山海关区','id':80},{'label':'北戴河区','id':81},{'label':'抚宁区','id':82},{'label':'青龙满族自治县','id':83},{'label':'昌黎县','id':84},{'label':'卢龙县','id':85}],'id':77},{'label':'邯郸市','children':[{'label':'市辖区','id':87},{'label':'邯山区','id':88},{'label':'丛台区','id':89},{'label':'复兴区','id':90},{'label':'峰峰矿区','id':91},{'label':'邯郸县','id':92},{'label':'临漳县','id':93},{'label':'成安县','id':94},{'label':'大名县','id':95},{'label':'涉县','id':96},{'label':'磁县','id':97},{'label':'肥乡县','id':98},{'label':'永年县','id':99},{'label':'邱县','id':100},{'label':'鸡泽县','id':101},{'label':'广平县','id':102},{'label':'馆陶县','id':103},{'label':'魏县','id':104},{'label':'曲周县','id':105},{'label':'武安市','id':106}],'id':86},{'label':'邢台市','children':[{'label':'市辖区','id':108},{'label':'桥东区','id':109},{'label':'桥西区','id':110},{'label':'邢台县','id':111},{'label':'临城县','id':112},{'label':'内丘县','id':113},{'label':'柏乡县','id':114},{'label':'隆尧县','id':115},{'label':'任县','id':116},{'label':'南和县','id':117},{'label':'宁晋县','id':118},{'label':'巨鹿县','id':119},{'label':'新河县','id':120},{'label':'广宗县','id':121},{'label':'平乡县','id':122},{'label':'威县','id':123},{'label':'清河县','id':124},{'label':'临西县','id':125},{'label':'南宫市','id':126},{'label':'沙河市','id':127}],'id':107},{'label':'保定市','children':[{'label':'市辖区','id':129},{'label':'竞秀区','id':130},{'label':'莲池区','id':131},{'label':'满城区','id':132},{'label':'清苑区','id':133},{'label':'徐水区','id':134},{'label':'涞水县','id':135},{'label':'阜平县','id':136},{'label':'定兴县','id':137},{'label':'唐县','id':138},{'label':'高阳县','id':139},{'label':'容城县','id':140},{'label':'涞源县','id':141},{'label':'望都县','id':142},{'label':'安新县','id':143},{'label':'易县','id':144},{'label':'曲阳县','id':145},{'label':'蠡县','id':146},{'label':'顺平县','id':147},{'label':'博野县','id':148},{'label':'雄县','id':149},{'label':'涿州市','id':150},{'label':'安国市','id':151},{'label':'高碑店市','id':152}],'id':128},{'label':'张家口市','children':[{'label':'市辖区','id':154},{'label':'桥东区','id':155},{'label':'桥西区','id':156},{'label':'宣化区','id':157},{'label':'下花园区','id':158},{'label':'万全区','id':159},{'label':'崇礼区','id':160},{'label':'张北县','id':161},{'label':'康保县','id':162},{'label':'沽源县','id':163},{'label':'尚义县','id':164},{'label':'蔚县','id':165},{'label':'阳原县','id':166},{'label':'怀安县','id':167},{'label':'怀来县','id':168},{'label':'涿鹿县','id':169},{'label':'赤城县','id':170}],'id':153},{'label':'承德市','children':[{'label':'市辖区','id':172},{'label':'双桥区','id':173},{'label':'双滦区','id':174},{'label':'鹰手营子矿区','id':175},{'label':'承德县','id':176},{'label':'兴隆县','id':177},{'label':'平泉县','id':178},{'label':'滦平县','id':179},{'label':'隆化县','id':180},{'label':'丰宁满族自治县','id':181},{'label':'宽城满族自治县','id':182},{'label':'围场满族蒙古族自治县','id':183}],'id':171},{'label':'沧州市','children':[{'label':'市辖区','id':185},{'label':'新华区','id':186},{'label':'运河区','id':187},{'label':'沧县','id':188},{'label':'青县','id':189},{'label':'东光县','id':190},{'label':'海兴县','id':191},{'label':'盐山县','id':192},{'label':'肃宁县','id':193},{'label':'南皮县','id':194},{'label':'吴桥县','id':195},{'label':'献县','id':196},{'label':'孟村回族自治县','id':197},{'label':'泊头市','id':198},{'label':'任丘市','id':199},{'label':'黄骅市','id':200},{'label':'河间市','id':201}],'id':184},{'label':'廊坊市','children':[{'label':'市辖区','id':203},{'label':'安次区','id':204},{'label':'广阳区','id':205},{'label':'固安县','id':206},{'label':'永清县','id':207},{'label':'香河县','id':208},{'label':'大城县','id':209},{'label':'文安县','id':210},{'label':'大厂回族自治县','id':211},{'label':'霸州市','id':212},{'label':'三河市','id':213}],'id':202},{'label':'衡水市','children':[{'label':'市辖区','id':215},{'label':'桃城区','id':216},{'label':'冀州区','id':217},{'label':'枣强县','id':218},{'label':'武邑县','id':219},{'label':'武强县','id':220},{'label':'饶阳县','id':221},{'label':'安平县','id':222},{'label':'故城县','id':223},{'label':'景县','id':224},{'label':'阜城县','id':225},{'label':'深州市','id':226}],'id':214},{'label':'省直辖县级行政区划','children':[{'label':'定州市','id':228},{'label':'辛集市','id':229}],'id':227}],'id':37}],
                treeMap: new Map()
            }
        },
        watch: {
            filterText(val) {
                if (val === '') {
                    this.search()
                }
            }
        },
        created() {
            this.init(this.treeData)
        },
        methods: {
            init(array) {
                for (const node of array) {
                    this.treeMap.set(node.id, node)
                    if (node.children && node.children.length > 0) {
                        node.isLeaf = false
                        this.init(node.children)
                    } else {
                        node.isLeaf = true
                    }
                }
            },
            search() {
                // 刷新key,重新加载组件
                this.treeKey = new Date().getTime()
            },
            loadNode(node, resolve) {
                if (node.level > 0) {

                    // 加载子节点
                    resolve(this.treeMap.get(node.data.id).children)

                } else {
                    if (this.filterText) {

                        new Promise((resolve) => {
                            // 根据关键字搜索,一定要用Promise,先重新加载组件,再渲染数据
                            const searchResult = this.findNodeTree(this.treeData)
                            resolve(searchResult)
                        }).then((data) => {
                            this.renderNodes(node, data)
                        })

                    } else {

                        // 展示全国省份
                        const totalCity = this.treeData.map((n) => {
                            return this.treeMap.get(n.id)
                        })
                        resolve(totalCity)

                    }
                }
            },
            findNodeTree(array) {
                const searchResult = []
                for (let node of array) {
                    // 复制一份node,不影响源数据
                    node = {
                        id: node.id,
                        label: node.label,
                        children: node.children,
                        isLeaf: node.isLeaf
                    }

                    // 先查找符合关键字的子节点
                    let newChildren = null
                    if (!node.isLeaf) {
                        newChildren = this.findNodeTree(node.children)
                        if (newChildren) {
                            // 如果有符合关键字的子节点,就把符合关键字的子节点作为children
                            node.children = newChildren
                            searchResult.push(node)
                        }
                    }

                    // 子节点都不符合,但当前节点符合关键字,就把当前节点纳入搜索结果中
                    if (!newChildren && node.label.indexOf(this.filterText) >= 0) {
                        node.children = null
                        searchResult.push(node)
                    }
                }

                return searchResult.length > 0 ? searchResult : null
            },
            renderNodes(parent, array) {
                // 手动渲染节点,把array渲染成parent的子节点
                if (array && array.length > 0) {
                    parent.doCreateChildren(array)
                    parent.expanded = true
                    parent.loaded = true
                    for (let i = 0; i < parent.childNodes.length; i++) {
                        this.renderNodes(parent.childNodes[i], array[i].children)
                    }
                }
            }
        }
    })
</script>
</html>


  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
对于el-tree展开节点后是另一个el-tree的情况,你可以通过使用el-tree组件的自定义模板来实现。在展开节点时,你可以将子节点绑定到另一个el-tree组件的数据源中。 首先,确保你已经在项目中引入了el-tree组件。然后,在你的Vue组件中,可以这样定义el-tree组件: ```html <template> <div> <el-tree :data="treeData" :node-key="nodeKey" :props="defaultProps" @node-click="handleNodeClick"></el-tree> </div> </template> <script> export default { data() { return { treeData: [ { label: '节点1', children: [ { label: '子节点1', children: [ { label: '子节点1.1', }, { label: '子节点1.2', }, ], }, ], }, { label: '节点2', children: [ { label: '子节点2', children: [ { label: '子节点2.1', }, { label: '子节点2.2', }, ], }, ], }, ], nodeKey: 'label', defaultProps: { children: 'children', label: 'label', }, }; }, methods: { handleNodeClick(data) { // 在这里处理节点点击事件 // 获取点击的节点数据data,然后将其作为新的数据源赋值给另一个el-tree组件 this.treeData = data.children; }, }, }; </script> ``` 在上面的示例中,我们使用了el-tree组件的data属性绑定了的数据源。当点击节点时,通过handleNodeClick方法获取到该节点的子节点数据,然后将其赋值给treeData,这样就实现了展开节点后显示另一个el-tree的效果。 注意,你可以根据实际需求修改数据结构和事件处理逻辑。这只是一个简单的示例,希望能对你有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值