更新记录
1.0.1(2025-07-10) 下载此版本
修改默认样式内容不显示bug
1.0.0(2025-07-10) 下载此版本
1、树形结构插件,支持三种数据源:树形结构、列表结构(所有数据)、列表结构(懒加载)
2、支持单选、多选
3、支持自定义列表样式
4、支持自定义父项与子项缩进
平台兼容性
uni-app
Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
---|---|---|---|---|---|---|---|---|
√ | √ | √ | √ | √ | - | - | - | - |
微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 快应用-华为 | 快应用-联盟 |
---|---|---|---|---|---|---|---|---|---|---|
√ | - | - | - | - | - | - | - | - | - | - |
uni-app x
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 |
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'>
</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);
}
}
}
</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、各位小伙伴如果需要其他插件的话,请留言,我会根据实际需求及自身能力完成插件或给出建议。
谢谢大家!