This commit is contained in:
houjunxiang
2025-10-09 18:19:55 +08:00
parent f2ffc65094
commit 386f1e7466
1553 changed files with 284685 additions and 32820 deletions

View File

@@ -0,0 +1,196 @@
# 1.4.2
新增
1. 新增`filterValue`属性,支持通过此关键词来搜索并筛选树结构的内容
# 1.4.1
修复
1. 修复单选 onlyRadioLeaf 时末级节点无法选中的 bug
# 1.4.0
版本调整
建议更新,但需要注意,异步数据的时候,后台需返回 leaf 字段来判断是否末项数据
1. **调整数据项格式,新增 `leaf` 字段,来判断是否为末节点**
2. **调整数据项格式,新增 `sort` 字段,来排序节点位置**
3. **注意:异步加载数据,当为末项的时候,需要服务端数据返回 `leaf` 字段**
4. 新增 `alwaysFirstLoad` ,即异步数据总会在第一次展开节点时,拉取一次后台数据,来比对是否一致
5. 拆分 `field` 属性,**注意: 1.5.0 版本后将移除 `field` 属性**
6. 新增 `labelField``field.label`,指定节点对象中某个属性为**标签**字段,默认`label`
7. 新增 `valueField``field.key`,指定节点对象中某个属性为**值**字段,默认`value`
8. 新增 `childrenField``field.children`,指定节点对象中某个属性为**子树节点**字段,默认`children`
9. 新增 `disabledField``field.disabled`,指定节点对象中某个属性为**禁用**字段,默认`disabled`
10. 新增 `appendField``field.append`,指定节点对象中某个属性为**副标签**字段,默认`append`
11. 新增 `leafField``field.label`,指定节点对象中某个属性为**末级节点**字段,默认`leaf`
12. 新增 `sortField``field.label`,指定节点对象中某个属性为**排序**字段,默认`sort`
13. 新增 `isLeafFn` ,用来自定义控制数据项的末项
14. 更多的项目示例
15. 支持单选取消选中
16. 修复节点展开时可能存在的 bug
17. 修复节点选择可能存在的 bug
18. 调整为子节点默认继承父节点禁用属性
19. `setExpandedKeys` 添加参数一为 `all` 即可支持一键展开/收起全部节点
20. 其它更多优化
# 1.3.4
优化
1. 优化图标字体命名
# 1.3.3
优化
1. 新增方法调用
> - 新增`getUncheckedKeys`,返回未选的 key
> - 新增`getUncheckedNodes`,返回未选的节点
> - 新增`getUnexpandedKeys`,返回未展开的 key
> - 新增`getUnexpandedNodes`,返回未展开的节点
2. 优化示例项目
# 1.3.2
修复
1. 修复在 APP 真机环境中的报错
# 1.3.1
修复
1. 修复方法`setExpandedKeys`没联动展开上级父子节点
# 1.3.0
优化
1. `field`新增字段 `append` 用于在标签后面显示小提示
2. 新增支持点击标签也能选中节点
3. 方法`setExpandedKeys`支持加载动态数据
4. 修复父节点禁用,则不能展开及图标展开显示
5. 修复动态加载数据时,末级节点的 `children``null` 时仍显示展开图标
# 1.2.6
新增
1. 新增支持主题换色
2. 支持单选的`onlyRadioLeaf``true`时可点父节点展开/收起
3. 优化`expandChecked`调整为不展开无子节点的节点
# 1.2.5
新增
1. 新增 `expandChecked`,控制选择时是否展开当前已选的所有下级节点
# 1.2.4
修复
1. 修复动态数据展开状态异常问题
# 1.2.3
新增
1. 新增 `checkedDisabled`,是否渲染禁用值
2. 新增 `packDisabledkey`,是否返回已禁用并选中的 key
3. 修复选择父级时,子级已禁用但仍被选中的问题
# 1.2.2
优化
1. 调整动态数据载入处理方式
2. 修复节点数据因动态数据引起的状态异常
3. 修复初始节点数据默认选中
# 1.2.1
修复
1. 修复切换`选中状态`被重复选中问题
2. 修复动态数据引起的重复选择问题
# 1.2.0
新增
1. 新增方法调用
> - 新增`setCheckedKeys`,方法设置指定 key 的节点选中状态
> - 新增`setExpandedKeys`,方法设置指定 key 的节点展开状态
2. 修复小程序重复插槽一直刷报错问题
3. 优化展开时,会展开子级所以下级节点
# 1.1.1
新增
1. 新增`data``disabled`,支持节点禁用状态
2. 新增`field``disabled`,可自定`disabled`字段值
# 1.1.0
新增
1. 新增`loadMode``loadApi`,支持展开时加载异步数据
2. 新增方法调用
> - 新增`getCheckedKeys`,方法返回已选的 key
> - 新增`getHalfCheckedKeys`,方法返回半选的 key
> - 新增`getExpandedKeys`,方法返回已展开的 key
> - 新增`getCheckedNodes`,方法返回已选的节点
> - 新增`getHalfCheckedNodes`,方法返回半选的节点
> - 新增`getExpandedNodes`,方法返回已展开的节点
3. 对代码进行重构,更易于后期拓展
4. 此次更新后,页面多个的 DaTee 组件间的数据不再关联
# 1.0.6
新增
1. 新增`checkStrictly`,多选模式下选中时是否父子不关联
# 1.0.5
修复
1. 修复多选时已选数据重复问题
# 1.0.4
修复
1. 修复 `change` 事件回调数据的问题
# 1.0.3
优化
1. 优化文档及示例说明
# 1.0.2
新增
1. 新增 `onlyRadioLeaf` ,单选时只允许选中末级
2. 优化默认展开及默认选择的展开问题
# 1.0.1
新增
1. 支持展开/收起回调事件`@expand`
# 1.0.0
初始版本 1.0.0,基于 Vue3 进行开发,支持单选、多选,兼容各大平台
1. 支持单选
2. 支持多选

1181
components/da-tree/index.vue Normal file

File diff suppressed because it is too large Load Diff

197
components/da-tree/props.ts Normal file
View File

@@ -0,0 +1,197 @@
export default {
/**
* 树的数据
*/
data: {
type: Array,
default: () => []
},
/**
* 主题色
*/
themeColor: {
type: String,
default: '#007aff'
},
/**
* 是否开启多选,默认单选
*/
showCheckbox: {
type: Boolean,
default: false
},
/**
* 默认选中的节点注意单选时为单个key多选时为key的数组
*/
defaultCheckedKeys: {
type: [Array, String, Number],
default: null
},
/**
* 是否默认展开全部
*/
defaultExpandAll: {
type: Boolean,
default: false
},
/**
* 默认展开的节点
*/
defaultExpandedKeys: {
type: Array,
default: null
},
/**
* 筛选关键词
*/
filterValue: {
type: String,
default: ''
},
/**
* 是否自动展开到选中的节点,默认不展开
*/
expandChecked: {
type: Boolean,
default: false
},
/**
* (旧)字段对应内容,默认为 {label: 'label',key: 'key', children: 'children', disabled: 'disabled', append: 'append'}
* 注意1.5.0版本后不再兼容
*/
field: {
type: Object,
default: null
},
/**
* 标签字段(新,拆分了)
*/
labelField: {
type: String,
default: 'label'
},
/**
* 值字段(新,拆分了)
*/
valueField: {
type: String,
default: 'value'
},
/**
* 下级字段(新,拆分了)
*/
childrenField: {
type: String,
default: 'children'
},
/**
* 禁用字段(新,拆分了)
*/
disabledField: {
type: String,
default: 'disabled'
},
/**
* 末级节点字段(新,拆分了)
*/
leafField: {
type: String,
default: 'leaf'
},
/**
* 副标签字段(新,拆分了)
*/
appendField: {
type: String,
default: 'append'
},
/**
* 排序字段(新,拆分了)
*/
sortField: {
type: String,
default: 'sort'
},
/**
* Api数据返回后的结果路径支持嵌套如`data.list`
*/
resultField: {
type: String,
default: ''
},
isLeafFn: {
type: Function,
default: null
},
/**
* 是否显示单选图标,默认显示
*/
showRadioIcon: {
type: Boolean,
default: true
},
/**
* 单选时只允许选中末级,默认可随意选中
*/
onlyRadioLeaf: {
type: Boolean,
default: false
},
/**
* 多选时,是否执行父子不关联的任意勾选,默认父子关联
*/
checkStrictly: {
type: Boolean,
default: false
},
/**
* 为 true 时,空的 children 数组会显示展开图标
*/
loadMode: {
type: Boolean,
default: false
},
/**
* 异步加载接口
*/
loadApi: {
type: Function,
default: null
},
/**
* 是否总在首次的时候加载一下内容,来比对是否一致
*/
alwaysFirstLoad: {
type: Boolean,
default: false
},
/**
* 是否渲染(操作)禁用值
*/
checkedDisabled: {
type: Boolean,
default: false
},
/**
* 是否返回已禁用的但已选中的key
*/
packDisabledkey: {
type: Boolean,
default: true
},
/**
* 选择框的位置,可选 left/right
*/
checkboxPlacement: {
type: String,
default: 'left'
},
/**
* 子项缩进距离默认40单位rpx
*/
indent: {
type: Number,
default: 20
}
}

View File

@@ -0,0 +1,310 @@
# da-tree
一个基于 Vue3 的 tree(树)组件,同时支持主题换色,可能是最适合你的 tree(树)组件
组件一直在更新,遇到问题可在下方讨论。
`同时更新 Vue2 版本,在此查看 ===>` **[Vue2 版](https://ext.dcloud.net.cn/plugin?id=12692)**
### 关于使用
可在右侧的`使用 HBuilderX 导入插件``下载示例项目ZIP`,方便快速上手。
可通过下方的示例及文档说明,进一步了解使用组件相关细节参数。
插件地址https://ext.dcloud.net.cn/plugin?id=12384
### 组件示例
```jsx
<template>
<view>多选</view>
<view><button @click="doCheckedTree(['2'],true)">全选</button></view>
<view><button @click="doCheckedTree(['2'],false)">取消全选</button></view>
<view><button @click="doCheckedTree(['211','222'],true)">选中指定节点</button></view>
<view><button @click="doCheckedTree(['211','222'],false)">取消选中指定节点</button></view>
<view><button @click="doExpandTree('all',true)">展开全部节点</button></view>
<view><button @click="doExpandTree('all',false)">收起全部节点</button></view>
<view><button @click="doExpandTree(['22','23'],true)">展开节点</button></view>
<view><button @click="doExpandTree(['22','23'],false)">收起节点</button></view>
<DaTree
ref="DaTreeRef"
:data="roomTreeData"
labelField="name"
valueField="id"
defaultExpandAll
showCheckbox
:defaultCheckedKeys="defaultCheckedKeysValue"
@change="handleTreeChange"
@expand="handleExpandChange"></DaTree>
<view>单选</view>
<DaTree
:data="roomTreeData"
labelField="name"
valueField="id"
defaultExpandAll
:defaultCheckedKeys="defaultCheckedKeysValue2"
@change="handleTreeChange"
@expand="handleExpandChange"></DaTree>
<view>默认展开指定节点</view>
<DaTree
:data="roomTreeData"
labelField="name"
valueField="id"
showCheckbox
:defaultExpandedKeys="defaultExpandKeysValue3"
@change="handleTreeChange"
@expand="handleExpandChange"></DaTree>
<view>异步加载数据</view>
<DaTree
:data="roomTreeData"
labelField="name"
valueField="id"
showCheckbox
loadMode
:loadApi="GetApiData"
defaultExpandAll
@change="handleTreeChange"
@expand="handleExpandChange"></DaTree>
</template>
```
```js
import { defineComponent, ref } from 'vue'
/**
* 模拟创建一个接口数据
*/
function GetApiData(currentNode) {
const { key } = currentNode
return new Promise((resolve) => {
setTimeout(() => {
// 模拟返回空数据
if (key.indexOf('-') > -1) {
return resolve(null)
// return resolve([])
}
return resolve([
{
id: `${key}-1`,
name: `行政部X${key}-1`,
},
{
id: `${key}-2`,
name: `财务部X${key}-2`,
append: '定义了末项数据',
leaf: true,
},
{
id: `${key}-3`,
name: `资源部X${key}-3`,
},
{
id: `${key}-4`,
name: `资源部X${key}-3`,
append: '被禁用,无展开图标',
disabled: true,
},
])
}, 2000)
})
}
import DaTree from '@/components/da-tree/index.vue'
export default defineComponent({
components: { DaTree },
setup() {
const DaTreeRef = ref()
// key的类型必须对应树数据key的类型
const defaultCheckedKeysValue = ref(['211', '222'])
const defaultCheckedKeysValue2 = ref('222')
const defaultExpandKeysValue3 = ref(['212', '231'])
const roomTreeData = ref([
{
id: '2',
name: '行政中心',
children: [
{
id: '21',
name: '行政部',
children: [
{
id: '211',
name: '行政一部',
children: null,
},
{
id: '212',
name: '行政二部',
children: [],
disabled: true,
},
],
},
{
id: '22',
name: '财务部',
children: [
{
id: '221',
name: '财务一部',
children: [],
disabled: true,
},
{
id: '222',
name: '财务二部',
children: [],
},
],
},
{
id: '23',
name: '人力资源部',
children: [
{
id: '231',
name: '人力一部',
children: [],
},
{
id: '232',
name: '人力二部',
append: '更多示例,请下载示例项目查看',
},
],
},
],
},
])
function doExpandTree(keys, expand) {
DaTreeRef.value?.setExpandedKeys(keys, expand)
const gek = DaTreeRef.value?.getExpandedKeys()
console.log('当前已展开的KEY ==>', gek)
}
function doCheckedTree(keys, checked) {
DaTreeRef.value?.setCheckedKeys(keys, checked)
const gek = DaTreeRef.value?.getCheckedKeys()
console.log('当前已选中的KEY ==>', gek)
}
function handleTreeChange(allSelectedKeys, currentItem) {
console.log('handleTreeChange ==>', allSelectedKeys, currentItem)
}
function handleExpandChange(expand, currentItem) {
console.log('handleExpandChange ==>', expand, currentItem)
}
return {
DaTreeRef,
roomTreeData,
defaultCheckedKeysValue,
defaultCheckedKeysValue2,
defaultExpandKeysValue3,
handleTreeChange,
handleExpandChange,
GetApiData,
doExpandTree,
doCheckedTree,
}
},
})
```
** 更多示例请下载/导入示例项目 ZIP 查看 **
### 组件参数
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| :------------------ | :------------------------------ | :--------- | :--- | :--------------------------------------------------------------------------- |
| data | `Array` | - | 是 | 树的数据 |
| themeColor | `String` | `#007aff` | 否 | 主题色,十六进制 |
| defaultCheckedKeys | `Array` \| `Number` \| `String` | - | 否 | 默认选中的节点,单选为单个 key多选为 key 的数组 |
| showCheckbox | `Boolean` | `false` | 否 | 是否开启多选,默认单选 |
| checkStrictly | `Boolean` | `false` | 否 | 多选时,是否执行父子不关联的任意勾选,默认父子关联 |
| showRadioIcon | `Boolean` | `true` | 否 | 是否显示单选图标,默认显示 |
| onlyRadioLeaf | `Boolean` | `true` | 否 | 单选时只允许选中末级,默认可随意选中 |
| defaultExpandAll | `Boolean` | `false` | 否 | 是否默认展开全部 |
| defaultExpandedKeys | `Array` | - | 否 | 默认展开的节点 |
| indent | `Number` | `40` | 否 | 子项缩进距离,单位 rpx |
| checkboxPlacement | `String` | `left` | 否 | 选择框的位置,可选 left/right |
| loadMode | `Boolean` | `false` | 否 | 为 true 时,空的 children 数组会显示展开图标 |
| loadApi | `Function` | - | 否 | 选择框的位置,可选 left/right |
| checkedDisabled | `Boolean` | `false` | 否 | 是否渲染禁用值,默认不渲染 |
| packDisabledkey | `Boolean` | `true` | 否 | 是否返回已禁用的但已选中的 key默认返回禁用已选值 |
| expandChecked | `Boolean` | `false` | 否 | 是否自动展开到选中的节点,默认不展开 |
| alwaysFirstLoad | `Boolean` | `false` | 否 | 是否总在首次的时候加载一下内容,默认不加载,否则只有展开末级节点才会加载数据 |
| isLeafFn | `Function` | - | 否 | 自定义函数返回来控制数据项的末项 |
| field | `Object` | - | 否 | 字段对应内容,格式参考下方(1.5.0 后移除,请用单独的字段匹配) |
| labelField | `String` | `label` | 否 | 指定节点对象中某个属性为标签字段,默认`label` |
| valueField | `String` | `value` | 否 | 指定节点对象中某个属性为值字段,默认`value` |
| childrenField | `String` | `children` | 否 | 指定节点对象中某个属性为子树节点字段,默认`children` |
| disabledField | `String` | `disabled` | 否 | 指定节点对象中某个属性为禁用字段,默认`disabled` |
| appendField | `String` | `append` | 否 | 指定节点对象中某个属性为副标签字段,默认`append` |
| leafField | `String` | `leaf` | 否 | 指定节点对象中某个属性为末级节点字段,默认`leaf` |
| sortField | `String` | `sort` | 否 | 指定节点对象中某个属性为排序字段,默认`sort` |
| filterValue | `String` | - | 否 | 搜索筛选的关键词,通过输入关键词筛选内容 |
**field 格式(1.5.0 后移除,请用单独的字段匹配)**
```js
{
label: 'label',
key: 'key',
children: 'children',
disabled: 'disabled',
append: 'append'
}
```
### 组件事件
| 事件名称 | 回调参数 | 说明 |
| :------- | :-------------------------------------- | :-------------- |
| change | `(allCheckedKeys, currentItem) => void` | 选中时回调 |
| expand | `(expandState, currentItem) => void` | 展开/收起时回调 |
### 组件方法
| 方法名称 | 参数 | 说明 |
| :------------------ | :--------------- | :------------------------------------------------------------------------------------------------ |
| setCheckedKeys | `(keys,checked)` | 设置指定 key 的节点选中/取消选中的状态。注: keys 单选时为 key多选时为 key 的数组 |
| setExpandedKeys | `(keys,expand)` | 设置指定 key 的节点展开/收起的状态,当 keys 为 all 时即代表展开/收起全部。注keys 为数组或 `all` |
| getCheckedKeys | - | 返回已选的 key |
| getHalfCheckedKeys | - | 返回半选的 key |
| getUncheckedKeys | - | 返回未选的 key |
| getCheckedNodes | - | 返回已选的节点 |
| getUncheckedNodes | - | 返回未选的节点 |
| getHalfCheckedNodes | - | 返回半选的节点 |
| getExpandedKeys | - | 返回已展开的 key |
| getUnexpandedKeys | - | 返回未展开的 key |
| getExpandedNodes | - | 返回已展开的节点 |
| getUnexpandedNodes | - | 返回未展开的节点 |
### 组件版本
v1.4.2
### 差异化
已通过测试
> - H5 页面
> - 微信小程序
> - 支付宝、钉钉小程序
> - 字节跳动、抖音、今日头条小程序
> - 百度小程序
> - 飞书小程序
> - QQ 小程序
> - 京东小程序
未测试
> - 快手小程序由于非企业用户暂无演示
> - 快应用、360 小程序因 Vue3 支持的原因暂无演示
### 开发组
[@CRLANG](https://crlang.com)

150
components/da-tree/utils.ts Normal file
View File

@@ -0,0 +1,150 @@
/** 未选 */
export const unCheckedStatus = 0
/** 半选 */
export const halfCheckedStatus = 1
/** 选中 */
export const isCheckedStatus = 2
/**
* 深拷贝内容
* @param originData 拷贝对象
* @author crlang(https://crlang.com)
*/
export function deepClone(originData) {
const type = Object.prototype.toString.call(originData)
let data
if (type === '[object Array]') {
data = []
for (let i = 0; i < originData.length; i++) {
data.push(deepClone(originData[i]))
}
} else if (type === '[object Object]') {
data = {}
for (const prop in originData) {
// eslint-disable-next-line no-prototype-builtins
if (originData.hasOwnProperty(prop)) { // 非继承属性
data[prop] = deepClone(originData[prop])
}
}
} else {
data = originData
}
return data
}
/**
* 获取所有指定的节点
* @param type
* @param value
* @author crlang(https://crlang.com)
*/
export function getAllNodes(list, type, value, packDisabledkey = true) {
if (!list || list.length === 0) {
return []
}
const res = []
for (let i = 0; i < list.length; i++) {
const item = list[i]
if (item[type] === value) {
if ((packDisabledkey && item.disabled) || !item.disabled) {
res.push(item)
}
}
}
return res
}
/**
* 获取所有指定的key值
* @param type
* @param value
* @author crlang(https://crlang.com)
*/
export function getAllNodeKeys(list, type, value, packDisabledkey = true) {
if (!list || list.length === 0) {
return null
}
const res = []
for (let i = 0; i < list.length; i++) {
const item = list[i]
if (item[type] === value) {
if ((packDisabledkey && item.disabled) || !item.disabled) {
res.push(item.key)
}
}
}
return res.length ? res : null
}
/**
* 错误输出
*
* @param msg
*/
export function logError(msg, ...args) {
console.error(`DaTree: ${msg}`, ...args)
}
const toString = Object.prototype.toString
export function is(val, type) {
return toString.call(val) === `[object ${type}]`
}
/**
* 是否对象(Object)
* @param val
*/
export function isObject(val) {
return val !== null && is(val, 'Object')
}
/**
* 是否数字(Number)
* @param val
*/
export function isNumber(val) {
return is(val, 'Number')
}
/**
* 是否字符串(String)
* @param val
*/
export function isString(val) {
return is(val, 'String')
}
/**
* 是否函数方法(Function)
* @param val
*/
export function isFunction(val) {
return typeof val === 'function'
}
/**
* 是否布尔(Boolean)
* @param val
*/
export function isBoolean(val) {
return is(val, 'Boolean')
}
/**
* 是否数组(Array)
* @param val
*/
export function isArray(val) {
return val && Array.isArray(val)
}