更新记录

1.0.4(2025-07-25) 下载此版本

修改当源数据发生变化时页面显示未改变bug

1.0.3(2025-07-24) 下载此版本

修改id、pid属性无效bug

1.0.2(2025-07-24) 下载此版本

1、添加搜索功能,注意新属性的使用

2、修改懒加载示例中没有子项仍然显示加载中bug

查看更多

平台兼容性

uni-app(4.23)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
- - - -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 快应用-华为 快应用-联盟
- - - - - - - - - -

uni-app x(4.41)

Chrome Safari Android iOS 鸿蒙 微信小程序
5.0 12 -

其他

多语言 暗黑模式 宽屏模式
× ×

写在最前面

小伙伴在使用过程中遇到任何问题或发现功能不足的地方,欢迎随时留言反馈,您的宝贵意见将帮助我持续完善插件功能,在此衷心感谢您的支持与帮助!

sjx-tree-perfect

该组件为uni_modules组件,下载导入后可以全局直接使用,无需在页面中引入、声明组件

在开发中,结合数据源及实际需求,该组件提供了三种数据源结构,支持懒加载、支持单选、多选。

更重要的是支持自定义每一项的样式,主要是为了解决实际开发中不同页面树样式不一样的问题。

Props

字段 类型 默认值 可选值 功能描述 更新时间
dataType Number 0 0|1|2 数据源类型:0--树形结构;1--列表结构(所有数据);2--列表结构(懒加载) 2025-07-10
isSelf Boolean false true|false 是否使用自定义样式 2025-07-10
treeData Array [] ~ 数据源 2025-07-10
id String id ~ id字段名 2025-07-10
pid String pid ~ pid字段名 2025-07-10
name String name ~ name字段名 2025-07-10
childsField String children ~ 当数据源是树形结构时,子项字段名 2025-07-10
paddingGap Number 15 ~ 父项与子项缩进距离 2025-07-10
hasChildField String hasChild ~ 懒加载时是否有子项或者是否是叶子项 2025-07-10
childData Array [] ~ 懒加载时子项列表 2025-07-10
choiceType Number 0 0|1|2 选择类型:0--不需要;1--单选;2--多选 2025-07-10
isShowSearch Boolean false true|false 是否使用搜索框 2025-07-24
filterField String name ~ 搜索时过滤的字段名 2025-07-24
placeholderText String 请输入内容 ~ 搜索框提示内容 2025-07-24

Events

事件名 默认值 功能描述 更新时间
nodeClick ~ 列表项点击事件 2025-07-10
singleSelectChange ~ 单选事件 2025-07-10
multiSelectChange ~ 多选事件 2025-07-10

代码演示

数据源是树形结构

<template>
    <view class="content">
        <sjx-tree-perfect :treeData="treeData" name="title" @nodeClick='nodeClick'>
        </sjx-tree-perfect>
    </view>
</template>

<script>
    export default {
        data() {
            return {
                title: 'Hello',
                treeData: [{
                    id: 1,
                    pid: 0,
                    title: '测试1',
                    children: [{
                        id: 4,
                        pid: 1,
                        title: '测试1-1',
                        children: [{
                            id: 8,
                            pid: 4,
                            title: '测试1-1-1'
                        }]
                    }, {
                        id: 5,
                        pid: 1,
                        title: '测试1-2'
                    }]
                }, {
                    id: 2,
                    pid: 0,
                    title: '测试2',
                    children: [{
                        id: 6,
                        pid: 2,
                        title: '测试2-1'
                    }]
                }, {
                    id: 3,
                    pid: 0,
                    title: '测试3',
                    children: [{
                        id: 7,
                        pid: 3,
                        title: '测试3-1'
                    }]
                }]
            }
        },
        mounted() {

        },
        methods: {
            nodeClick(item) {
                console.log('当前点击项', item);
            }
        }
    }
</script>

<style lang="scss" scoped>
    .content {
        padding: 10px;
    }
</style>

数据源是列表(所有数据)

<template>
    <view class="content">
        <sjx-tree-perfect :dataType="1" :treeData="treeData" name="title" @nodeClick='nodeClick' :isShowSearch="true"
            filterField="title">
        </sjx-tree-perfect>
    </view>
</template>

<script>
    export default {
        data() {
            return {
                title: 'Hello',
                treeData: [{
                        "id": "1",
                        "pid": "0",
                        "title": "1-生产者资质"
                    }, {
                        "id": "5",
                        "pid": "1",
                        "title": "1.1-资质,生产许可证在有效期内。"
                    },
                    {
                        "id": "19",
                        "pid": "5",
                        "title": "1.1.1-资质测试。"
                    },
                    {
                        "id": "20",
                        "pid": "19",
                        "title": "1.1.1.1-资质测试1111。"
                    },
                    {
                        "id": "6",
                        "pid": "1",
                        "title": "1.2-生产的食品、食品添加剂在许可范围内。"
                    },
                    {
                        "id": "7",
                        "pid": "1",
                        "title": "1.3-实际生产的特殊食品按规定注册或备案"
                    },
                    {
                        "id": "2",
                        "pid": "0",
                        "title": "2-生产环境条件"
                    },
                    {
                        "id": "8",
                        "pid": "2",
                        "title": "2.1-厂区无扬尘、车间卫生整洁。"
                    },
                    {
                        "id": "9",
                        "pid": "2",
                        "title": "2.2-厂区、车间与有毒、有害场所"
                    }, {

                        "id": "10",
                        "pid": "2",
                        "title": "2.3-设备布局和工艺流程"
                    },
                    {
                        "id": "11",
                        "pid": "2",
                        "title": "2.4-卫生间保持清洁,未与食品生产连通。"
                    },
                    {
                        "id": "12",
                        "pid": "2",
                        "title": "2.5-满足正常使用。"
                    },
                    {
                        "id": "13",
                        "pid": "2",
                        "title": "2.6-设备设施正常运行,存放垃圾、废弃物的设备设施标识清晰"
                    },
                    {
                        "id": "14",
                        "pid": "2",
                        "title": "2.7-车间内使用的材料等分隔放置,并有相应的使用记录。"
                    },
                    {
                        "id": "3",
                        "pid": "0",
                        "title": "3-进货"
                    },
                    {
                        "id": "15",
                        "pid": "3",
                        "title": "3.1-食品原料、食品添加剂"
                    },
                    {
                        "id": "16",
                        "pid": "3",
                        "title": "3.2-证明材料真实"
                    },
                    {
                        "id": "17",
                        "pid": "3",
                        "title": "3.3-建立贮存、保管记录、领用出库和退库记录。"
                    },
                    {
                        "id": "18",
                        "pid": "3",
                        "title": "3.4-生产特殊食品使用的原料要求一致。"
                    },
                    {
                        "id": "4",
                        "pid": "0",
                        "title": "叶子项测试-资质测试。"
                    }
                ]
            }
        },
        mounted() {

        },
        methods: {
            nodeClick(item) {
                console.log('当前点击项', item);
            }
        }
    }
</script>

<style lang="scss" scoped>
    .content {
        padding: 10px;
    }
</style>

数据源是列表(懒加载)

<template>
    <view class="content">
        <sjx-tree-perfect :dataType="2" :treeData="treeData" name="title" @nodeClick='nodeClick' :childData="childData"
            hasChildField="haschild">
        </sjx-tree-perfect>
    </view>
</template>

<script>
    export default {
        data() {
            return {
                title: 'Hello',
                treeData: [{
                    id: 1,
                    pid: 0,
                    title: '测试1',
                    haschild: true
                }, {
                    id: 2,
                    pid: 0,
                    title: '测试2',
                    haschild: true
                }, {
                    id: 3,
                    pid: 0,
                    title: '测试3',
                    haschild: true
                }],
                childData: []
            }
        },
        mounted() {

        },
        methods: {
            nodeClick(item) {
                console.log('当前点击项', item);
                this.getNextData(item)
            },
            getNextData(item) {
                if (!item.isLoad) {
                    setTimeout(() => {
                        this.childData = []
                        if (item.id == 1) {
                            this.childData.push({
                                id: 4,
                                pid: 1,
                                title: '测试1-1',
                                haschild: true
                            })
                            this.childData.push({
                                id: 5,
                                pid: 1,
                                title: '测试1-2',
                                haschild: false
                            })
                        }
                        if (item.id == 2) {
                            this.childData.push({
                                id: 6,
                                pid: 2,
                                title: '测试2-1',
                                haschild: false
                            })
                        }
                        if (item.id == 3) {
                            this.childData.push({
                                id: 7,
                                pid: 3,
                                title: '测试3-1',
                                haschild: false
                            })
                        }
                        if (item.id == 4) {
                            this.childData.push({
                                id: 8,
                                pid: 4,
                                title: '测试1-1-1',
                                haschild: false
                            })
                        }
                    }, 1000)
                }

            }
        }
    }
</script>

<style lang="scss" scoped>
    .content {
        padding: 10px;
    }
</style>

默认单选

<template>
    <view class="content">
        <sjx-tree-perfect :dataType="1" :treeData="treeData" name="title" @nodeClick='nodeClick' :choiceType="1"
            @singleSelectChange='radioChanges'>
        </sjx-tree-perfect>
    </view>
</template>

<script>
    export default {
        data() {
            return {
                title: 'Hello',
                treeData: [{
                    id: 1,
                    pid: 0,
                    title: '测试1'
                }, {
                    id: 2,
                    pid: 0,
                    title: '测试2'
                }, {
                    id: 3,
                    pid: 0,
                    title: '测试3'
                }, {
                    id: 4,
                    pid: 1,
                    title: '测试1-1'
                }, {
                    id: 5,
                    pid: 1,
                    title: '测试1-2'
                }, {
                    id: 6,
                    pid: 2,
                    title: '测试2-1'
                }, {
                    id: 7,
                    pid: 3,
                    title: '测试3-1'
                }, {
                    id: 8,
                    pid: 4,
                    title: '测试1-1-1'
                }]
            }
        },
        mounted() {

        },
        methods: {
            nodeClick(item) {
                console.log('当前点击项', item);
            },
            /* 参数说明
            item:当前选中项
            */
            radioChanges(item) {
                console.log('组件单选选中的项', item);
            }
        }
    }
</script>

<style lang="scss" scoped>
    .content {
        padding: 10px;
    }
</style>

默认多选

<template>
    <view class="content">
        <sjx-tree-perfect :dataType="1" :treeData="treeData" name="title" @nodeClick='nodeClick' :choiceType="2"
            @multiSelectChange='checkBoxChange'>
        </sjx-tree-perfect>
    </view>
</template>

<script>
    export default {
        data() {
            return {
                title: 'Hello',
                treeData: [{
                    id: 1,
                    pid: 0,
                    title: '测试1'
                }, {
                    id: 2,
                    pid: 0,
                    title: '测试2'
                }, {
                    id: 3,
                    pid: 0,
                    title: '测试3'
                }, {
                    id: 4,
                    pid: 1,
                    title: '测试1-1'
                }, {
                    id: 5,
                    pid: 1,
                    title: '测试1-2'
                }, {
                    id: 6,
                    pid: 2,
                    title: '测试2-1'
                }, {
                    id: 7,
                    pid: 3,
                    title: '测试3-1'
                }, {
                    id: 8,
                    pid: 4,
                    title: '测试1-1-1'
                }]
            }
        },
        mounted() {

        },
        methods: {
            nodeClick(item) {
                console.log('当前点击项', item);
            },
            /* 参数说明 
            ids:选中项id数组
            names:选中项name数组
            items:选中项数组
            */
            checkBoxChange(ids, names, items) {
                console.log('组件多选选中的项', ids.join(','), names.join(','), items);
            }
        }
    }
</script>

<style lang="scss" scoped>
    .content {
        padding: 10px;
    }
</style>

自定义单选

主要是使用插槽slot

<template>
    <view class="container">
        <sjx-tree-perfect ref="tree" :dataType="1" :treeData="treeData" name="title" :is-self="true"
            @singleSelectChange='radioChanges'>
            <template v-slot:default="{data}">
                <view>
                    <!-- 以下内容是自定义样式,你可以根据自己实际需求书写样式,这里使用了组件默认样式 -->
                    <view class="item" v-for="(item,index) in data" :key="index"
                        :style="{paddingLeft: item.level*15+'px' }">
                        <view class="choice">
                            <radio :value="JSON.stringify(item)" @click="myRadioChange(item)" :checked="item.radio">
                            </radio>
                        </view>
                        <view class="content" @click="myItemClick(item)">
                            <view class="switch">
                                <view :class="item.isLeaf?'':(item.isExpan?'switch-on':'switch-off')"></view>
                            </view>
                            <text class="name">{{item.title}}</text>
                        </view>
                    </view>
                </view>
            </template>
        </sjx-tree-perfect>
    </view>
</template>

<script>
    export default {
        data() {
            return {
                title: 'Hello',
                treeData: [{
                    id: 1,
                    pid: 0,
                    title: '测试1'
                }, {
                    id: 2,
                    pid: 0,
                    title: '测试2'
                }, {
                    id: 3,
                    pid: 0,
                    title: '测试3'
                }, {
                    id: 4,
                    pid: 1,
                    title: '测试1-1'
                }, {
                    id: 5,
                    pid: 1,
                    title: '测试1-2'
                }, {
                    id: 6,
                    pid: 2,
                    title: '测试2-1'
                }, {
                    id: 7,
                    pid: 3,
                    title: '测试3-1'
                }, {
                    id: 8,
                    pid: 4,
                    title: '测试1-1-1'
                }]
            }
        },
        mounted() {

        },
        methods: {
            nodeClick(item) {
                console.log('当前点击项', item);
            },
            myItemClick(item) {
                this.$refs.tree.itemClick(item)
            },
            myRadioChange(item) {
                this.$refs.tree.radioChange(item)
            },
            radioChanges(item) {
                console.log('组件单选选中的项', item);
            }

        }
    }
</script>

<style lang="scss" scoped>
    .container {
        padding: 10px;
    }

    .item {
        display: flex;
        justify-content: flex-start;
        align-items: center;
        padding: 5px 0;
        border-bottom: #f6f6f6 1px solid;
        box-sizing: border-box;

        .choice {

            radio,
            checkbox {
                transform: scale(0.7);
            }
        }

        .content {
            display: flex;
            justify-content: flex-start;
            align-items: center;

            .switch {
                display: flex;
                width: 30px;
                justify-content: center;
                align-items: center;
            }

            .name {
                flex: 1;
            }

            .switch-on {
                width: 0;
                height: 0;
                border: #666 8px solid;
                border-radius: 5px;
                border-color: black transparent transparent transparent;
                transform: translateY(25%);
            }

            .switch-off {
                width: 0;
                height: 0;
                border: #666 8px solid;
                border-radius: 5px;

                border-color: transparent transparent transparent black;
                transform: translateX(25%);
            }
        }
    }
</style>

自定义多选

<template>
    <view class="container">
        <sjx-tree-perfect ref="tree" :dataType="1" :treeData="treeData" name="title" @nodeClick='nodeClick'
            :is-self="true" @multiSelectChange='checkBoxChange'>
            <template v-slot:default="{data}">
                <view>
                     <!-- 以下内容是自定义样式,你可以根据自己实际需求书写样式,这里使用了组件默认样式 -->
                    <view class="item" v-for="(item,index) in data" :key="index"
                        :style="{paddingLeft: item.level*15+'px' }">
                        <view class="choice">
                            <checkbox :value="JSON.stringify(item)" @click="myCheckChange(item)" :checked="item.check">
                            </checkbox>
                        </view>
                        <view class="content" @click="myItemClick(item)">
                            <view class="switch">
                                <view :class="item.isLeaf?'':(item.isExpan?'switch-on':'switch-off')"></view>
                            </view>
                            <!-- 以下显示内容可以根据item对象随意修改 -->
                            <text class="name">{{item.title}}</text>
                        </view>
                    </view>
                </view>
            </template>
        </sjx-tree-perfect>
    </view>
</template>

<script>
    export default {
        data() {
            return {
                title: 'Hello',
                treeData: [{
                    id: 1,
                    pid: 0,
                    title: '测试1'
                }, {
                    id: 2,
                    pid: 0,
                    title: '测试2'
                }, {
                    id: 3,
                    pid: 0,
                    title: '测试3'
                }, {
                    id: 4,
                    pid: 1,
                    title: '测试1-1'
                }, {
                    id: 5,
                    pid: 1,
                    title: '测试1-2'
                }, {
                    id: 6,
                    pid: 2,
                    title: '测试2-1'
                }, {
                    id: 7,
                    pid: 3,
                    title: '测试3-1'
                }, {
                    id: 8,
                    pid: 4,
                    title: '测试1-1-1'
                }]
            }
        },
        mounted() {

        },
        methods: {
            nodeClick(item) {
                console.log('当前点击项', item);
            },
            myItemClick(item) {
                this.$refs.tree.itemClick(item)
            },
            myCheckChange(item) {
                this.$refs.tree.checkBoxChange(item)
            },
            checkBoxChange(ids, names, items) {
                console.log('组件单选选中的项', ids.join(','), names.join(','), items);
            }
        }
    }
</script>

<style lang="scss" scoped>
    .container {
        padding: 10px;
    }

    .item {
        display: flex;
        justify-content: flex-start;
        align-items: center;
        padding: 5px 0;
        border-bottom: #f6f6f6 1px solid;
        box-sizing: border-box;

        .choice {

            radio,
            checkbox {
                transform: scale(0.7);
            }
        }

        .content {
            display: flex;
            justify-content: flex-start;
            align-items: center;

            .switch {
                display: flex;
                width: 30px;
                justify-content: center;
                align-items: center;
            }

            .name {
                flex: 1;
            }

            .switch-on {
                width: 0;
                height: 0;
                border: #666 8px solid;
                border-radius: 5px;
                border-color: black transparent transparent transparent;
                transform: translateY(25%);
            }

            .switch-off {
                width: 0;
                height: 0;
                border: #666 8px solid;
                border-radius: 5px;

                border-color: transparent transparent transparent black;
                transform: translateX(25%);
            }
        }
    }
</style>

关于我

1、如有不足或不方便的地方,请指出,我尽快完善。

2、各位小伙伴如果需要其他插件的话,请留言,我会根据实际需求及自身能力完成插件或给出建议。

谢谢大家!

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议