vue输入框 tree树展示

注释:代码仅供参考

<el-form-item label="设备" prop="deviceCodeList">
    <selectTree class="parentC" :value="'groupCode'" :data="options" v-model="ruleForm.deviceCodeList"
              :props="props" :show-checkbox="true" :filterable="true" :collapse-tags="true" :placeholder="'请选择设备'"
              :lazy="true" :default-expand-all="true" :width="287" />
</el-form-item>



import selectTree from './selectTree.vue';

<!-- 
 * @description: 通用树型下拉框
 * @fileName: treeSelect.vue 
 * @author: t 
 * @date: 2023-03-07 17:15:03
 * @Attributes: data	展示数据	array
                props	配置选项,具体配置可以参照element ui库中el-tree的配置	object
                show-checkbox	节点是否可被选择	boolean
                check-strictly	在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false	boolean
                icon-class	自定义树节点的图标	string
                load	加载子树数据的方法,仅当 lazy 属性为true 时生效	function(node, resolve)
                lazy	是否懒加载子节点,需与 load 方法结合使用	boolean
                disabled	下拉框是否禁用
                getCheckedKeys	若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点的 key 所组成的数组
                getCurrentNode	获取当前被选中节点的 data,若没有节点被选中则返回 null  
                collapse-tags	多选时是否将选中值按文字的形式展示
                select-last-node	单选时是否只能选择最后一个节点
                Scoped Slot 自定义树节点的内容,参数为 { node, data }
                show-count 若节点中存在children 则在父节点展示所属children的数量,注意但设置插槽时 show-count将失效!
                clearable	单选时是否可以清空选项
                filterable 属性即可启用搜索功
                !!!!!必传!!!!!! props-value	每个树节点用来作为唯一标识的属性,整棵树应该是唯一的 !!!!!!
!-->
<!--:clearable="$attrs['show-checkbox']==undefined?$attrs['clearable']:false"-->
<template>
    <el-select class="selectTree" :style="`width:${width}px;overflow-y: auto`" :value="valueFilter(value)"
        :placeholder="$attrs['placeholder']" :multiple="$attrs['show-checkbox']" :disabled="$attrs['disabled']"
        :filterable="$attrs['filterable']" :clearable="$attrs['clearable']" :collapse-tags="$attrs['collapse-tags']"
        @change="selectChange" @clear="selectClear" ref="mySelect" :filter-method="remoteMethod">
        <template slot="empty">
            <div class="selecTree">
                <el-tree :load="loadRegionNode" :data="treeData" :props="props" @node-click="handleNodeClick"
                    :show-checkbox="$attrs['show-checkbox']" :check-strictly="$attrs['check-strictly']"
                    :icon-class="$attrs['icon-class']" :lazy="$attrs['lazy']" :node-key="props.value"
                    :filter-node-method="filterNode" @check-change="handleCheckChange"
                    :default-expanded-keys="defaultExpandedKeys" :default-expand-all="$attrs['default-expand-all']"
                    ref="myTree">
                    <!--  highlight-current -->
                    <template slot-scope="{ node, data }">
                        <slot :node="node" :data="data">
                            <span class="slotSpan">
                                <span>
                                    {{ data[props.label] }}
                                    <b v-if="$attrs['show-count'] != undefined && data[props.children]">({{
                                        data[props.children].length }})</b>
                                </span>
                            </span>
                        </slot>
                    </template>
                </el-tree>
            </div>
        </template>
    </el-select>
</template>
<script>
import { getTreeList, getDeviceList } from '@/api/aiproductmgt/quipment';
export default {
    props: {
        width: {
            type: Number,
            default: 200,
        },
        value: {
            type: undefined,
            default: null,
        },
        data: {
            type: Array,
            default: null,
        },
        props: {
            type: Object,
            default: null,
        },
    },
    data() {
        return {
            searchVal: '',
            treeData: [],
            defaultExpandedKeys: [],
        };
    },
    created() {
        this.queryRegionGroupList()
        this.propsInit();
    },
    mounted() {
        setTimeout(this.initData, 10);
    },
    beforeUpdate() {
        this.propsInit();
        this.initData();
    },

    methods: {
        /**
     * 懒加载树节点
     * @param {Obejct} node 用户点击的树节点
     * @param {Function} resolve 异步数据后的回调
     */
        loadRegionNode(node, resolve) {
            // 层级为0时
            if (node.level === 0) {
                return resolve(this.treeData);
            } else {
                this.queryRegionGroupList(node?.data, resolve);
            }
        },

        /**
         * 查询设备分组列表
         * @param {String} loadRegionNode 异步展开的父节点的属性 首次查询时不传
         * @param {Function} resolve 懒加载时需要resolve的数据
         */
        queryRegionGroupList(loadRegionNode, resolve) {
            const data = {
                pageSize: 200000,
                pageNo: 1,
                groupCode: loadRegionNode ? loadRegionNode?.groupCode : '0',
                getGroupOnlineDeviceCount: '1',
            };
            const list = []
            if (data.groupCode == undefined || data.groupCode == '') {
                return;
            } else {
                getTreeList(data).then((res) => {
                    const groupList = res.data.groupList || [];
                    const deviceList = res.data.deviceList || [];
                    groupList.forEach((item) => {
                        item.label = item.groupName;
                        item.value = item.groupCode;
                    });
                    // deviceList.forEach((item) => {
                    //     item.label = item.deviceName;
                    //     item.value = item.deviceGroupCode;
                    //     groupName = item.deviceName;
                    //     groupCode = item.deviceGroupCode;
                    //     // item.isEquipment = true;
                    //     // item.isLeaf = true;
                    // });
                    let arr = []
                    for (let i = 0; i < deviceList.length; i++) {
                        arr.push({
                            label: deviceList[i].deviceName,
                            value: deviceList[i].deviceGroupCode,
                            groupName: deviceList[i].deviceName,
                            groupCode: deviceList[i].deviceUuid,
                            deviceGroupCode: deviceList[i].deviceGroupCode,
                        })
                    }
                    const list = groupList.concat(arr);
                    // const list = groupList;
                    resolve && resolve(list);
                    if (loadRegionNode) {
                        loadRegionNode.children = list;

                    } else {
                        // 第一次加载或者搜索加载
                        this.treeData = this.searchVal ? this.arrayToTree(list) : list;
                    }
                });
            }
        },
        /**
     * 扁平化数据转换为想要的树结构
     */
        // arrayToTree(arr) {
        //     if (!Array.isArray(arr)) {
        //         return [];
        //     }
        //     const obj = {};
        //     arr.forEach((item) => {
        //         // 做字典
        //         obj[item.regionCode] = item;
        //     });
        //     const targetArr = [];
        //     arr.forEach((item) => {
        //         const parent = obj[item.parentGroupCode];
        //         if (parent) {
        //             parent.children = parent.children || [];
        //             parent.children.push(item);
        //         } else {
        //             targetArr.push(item);
        //         }
        //     });
        //     return targetArr;
        // },
        initData() {
            if (this.$attrs['show-checkbox'] === undefined) {
                let newItem = this.recurrenceQuery(this.treeData, this.props.value, this.value);
                if (newItem.length) {
                    if (this.props.value && newItem[0][this.props.value]) {
                        this.defaultExpandedKeys = [newItem[0][this.props.value]];
                    }
                    this.$nextTick(() => {
                        this.$refs.myTree.setCurrentNode(newItem[0]);
                    });
                }
            } else {
                let newValue = JSON.parse(JSON.stringify(this.value));
                if (!(newValue instanceof Array)) {
                    newValue = [newValue];
                }
                if (newValue?.length) {
                    let checkList = newValue.map(key => {
                        if (key) {
                            let newItem = this.recurrenceQuery(this.treeData, this.props.value, key);
                            return newItem[0] || '';
                        }
                    });
                    if (checkList?.length) {
                        let defaultExpandedKeys = checkList.map(item => item?.[this.props.value || '']);
                        if (defaultExpandedKeys.length) this.defaultExpandedKeys = defaultExpandedKeys;
                        this.$nextTick(() => {
                            this.$refs.myTree.setCheckedNodes(checkList);
                        });
                    }
                }
            }
            this.$forceUpdate();
        },
        // 多选
        handleCheckChange(data, e, ev) {
            let checkList = this.$refs.myTree.getCheckedNodes();
            let setList = null;
            if (checkList.length) {
                setList = checkList.map(item => item[this.props.value]);
            }
            this.$emit('input', setList);
            // 共三个参数,依次为:传递给 data 属性的数组中该节点所对应的对象、节点本身是否被选中、节点的子树中是否有被选中的节点
            this.$emit('change', data, e, ev);
        },
        // 单选事件
        handleNodeClick(data, e) {
            if (!(this.$attrs['select-last-node'] === undefined)) {
                if (data[this.props.children] && data[this.props.children]?.length) {
                    return false;
                }
            }
            if (this.$attrs['show-checkbox'] === undefined) {
                this.$emit('input', data[this.props.value]);
                this.$refs.mySelect.blur();
            }
            this.$emit('change', data, e);
        },
        //   递归查找通用方法
        recurrenceQuery(list, key, value) {
            if (!list || !key || !value) return [];
            let queryData = [];
            list.map(item => {
                if (item[this.props.children] && item[this.props.children].length) {
                    queryData.push(...this.recurrenceQuery(item[this.props.children], key, value));
                }
                if (item[key] == value) {
                    queryData.push(item);
                }
                return item;
            });
            return queryData;
        },
        selectChange(e) {
            if (this.$attrs['show-checkbox'] !== undefined) {
                let checkList = e.map(key => {
                    let newItem = this.recurrenceQuery(this.treeData, this.props.label, key);
                    return newItem[0] || '';
                });
                this.$refs.myTree.setCheckedNodes(checkList);
                this.$emit('input', e);
            }
        },
        selectClear(flag) {
            if (this.$attrs['show-checkbox'] === undefined) {
                if (!flag) this.$emit('input', '');
                this.$refs.myTree.setCurrentKey(null);
            } else {
                if (!flag) this.$emit('input', []);
                this.$refs.myTree.setCheckedKeys([]);
            }
            this.remoteMethod('');
        },
        getCheckedNodes() {
            if (this.value !== null && this.value !== undefined && this.value !== '') {
                return this.$refs.myTree.getCheckedNodes();
            }
            return [];
        },
        getCurrentNode() {
            if (this.value !== null && this.value !== undefined && this.value !== '') {
                return this.$refs.myTree.getCurrentNode();
            }
            return null;
        },
        valueFilter(val) {
            if (this.$attrs['show-checkbox'] === undefined) {
                let res = '';
                [res] = this.recurrenceQuery(this.treeData, this.props.value, val);
                return res?.[this.props.label] || '';
            } else {
                if (!val?.length) return [];
                let res = val.map(item => {
                    let [newItem] = this.recurrenceQuery(this.treeData, this.props.value, item);
                    return newItem?.[this.props.label] || '';
                });
                if (!res?.length) return [];
                res = res.filter(item => item);
                return res;
            }
        },
        propsInit() {
            this.props.label = this.props.label || 'label';
            this.props.value = this.props.value || 'value';
            this.props.children = this.props.children || 'children';
            if (this.$attrs['select-last-node'] !== undefined && !this.props.disabled) {
                this.props.disabled = data => data?.[this.props.children]?.length;
                this.$attrs['check-strictly'] = true;
            }
        },

        remoteMethod(query) {
            this.$refs.myTree.filter(query);
        },
        filterNode(value, data) {
            if (!value) return true;
            return data[this.props.label].indexOf(value) !== -1;
        },
    },

    watch: {
        value: {
            deep: true,
            handler(val) {
                if (!val || !val?.length) {
                    this.selectClear(true);
                }
            },
        },
    },
};
</script>
<style scoped lang="scss">
.selectTree {
    ::v-deep {
        .el-select__tags {
            max-width: 250px !important;
            display: inline-block;

            span {
                display: flex !important;
            }
        }

        .el-select .el-select__tags span {
            display: flex !important;
        }
    }
}

.selecTree {
    min-height: 250px;
    overflow: auto;
    padding: 0px;

    ::v-deep {
        .el-tree {
            width: 290px;
            max-height: 250px;
        }

        .el-tree-node__content {
            width: 100%;
        }

    }
}

// .el-select {
//     width: 100%;
// }

.slotSpan {
    font-size: 14px;

    b {
        font-weight: normal;
        font-size: 12px;
        color: #999;
    }
}

.selecTree ::v-deep .el-tree-node__content {
    font-size: 14px;
}
</style>

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue el-tree组件是一个支持层级结构的组件,可以用于展示组织结构、文件目录等型结构。给每个节点后面添加输入框的需求可能是为了让用户编辑节点的名称或其他信息。实现这个需求的步骤如下: 1. 在el-tree中的node默认插槽中添加一个输入框,如下: ``` <el-tree :data="data"> <template slot="node" slot-scope="{ node, data }"> <span>{{ node.label }}</span> <el-input v-model="node.name"></el-input> </template> </el-tree> ``` 2. 在data中添加需要修改的节点的name属性,然后在用户编辑输入框中的内容时实时更新该节点的name属性,如下: ``` data() { return { data: [ { label: '第一层', children: [ { label: '第二层', children: [ { label: '第三层', name: '节点名称' } ] } ] } ] } } ``` ``` <el-tree :data="data"> <template slot="node" slot-scope="{ node, data }"> <span>{{ node.label }}</span> <el-input v-model="node.name"></el-input> </template> </el-tree> ``` 3. 在用axios发送保存请求时,将修改的节点数据传给后端,让后端修改相应节点的数据,如下: ``` methods: { saveNodeData(node) { axios.post('/api/saveNodeData', { node }).then(res => { console.log(res.data) }).catch(error => { console.log(error.response.data) }) } } ``` 通过以上几个步骤,即可实现在el-tree中给每个节点后面添加输入框并保存数据的功能。注意,这只是实现的一个思路,具体实现还需要结合项目实际情况进行调整和优化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值