uniapp目录树
- 父组件调用目录树组件
html
<DaTreeVue2
:data="treeData"
:field="{
label: 'name',
key: 'id',
}"
@change="handleTreeChange"
:defaultCheckedKeys="addressDefaultCheckedKey"
:defaultExpandedKeys="addressDefaultCheckedKey"
:loadApi="getTreeData"
ref="addressRef"
loadMode
/>
- 父组件方法
javascript
handleTreeChange(allSelectedKeys, currentItem, parentKeys) {
this.addressDefaultCheckedKey = parentKeys;
}
// 获取树
async getTreeData(code) {
let result = {};
await this.$u.api.jobPost
.getseekTreeData({
parentCode: code,
})
.then((res) => {
result = res.map((it) => {
if (it.isParent) {
it.children = [];
return it;
} else {
return it;
}
});
});
return result;
}
- 子组件
javascript
<template>
<view class="da-tree" :style="{'--theme-color': themeColor}">
<scroll-view class="da-tree-scroll" :scroll-y="true" :scroll-x="false">
<view class="da-tree-item" :class="{'is-show': item.show}"
:style="{paddingLeft: item.level * indent + 'rpx'}" v-for="item in datalist" :key="item.key">
<view v-if="item.showArrow" class="da-tree-item__icon" @click="handleExpandedChange(item)">
<view :class="['da-tree-item__icon--arr','is-loading']" v-if="loadLoading && item.loading" />
<view :class="['da-tree-item__icon--arr','is-expand', {'is-right':!item.expand}]" v-else />
</view>
<view v-else class="da-tree-item__icon" />
<view class="da-tree-item__checkbox"
:class="[`da-tree-item__checkbox--${checkboxPlacement}`,{'is--disabled': item.disabled}]"
v-if="showCheckbox" @click="handleCheckChange(item)">
<view class="da-tree-item__checkbox--icon da-tree-checkbox-checked"
v-if="item.checkedStatus === isCheckedStatus" />
<view class="da-tree-item__checkbox--icon da-tree-checkbox-indeterminate"
v-else-if="item.checkedStatus === halfCheckedStatus" />
<view class="da-tree-item__checkbox--icon da-tree-checkbox-outline" v-else />
</view>
<view class="da-tree-item__checkbox"
:class="[`da-tree-item__checkbox--${checkboxPlacement}`,{'is--disabled': item.disabled}]"
v-if="!showCheckbox && showRadioIcon" @click="handleRadioChange(item)">
<view class="da-tree-item__checkbox--icon da-tree-radio-checked"
v-if="item.checkedStatus === isCheckedStatus" />
<view class="da-tree-item__checkbox--icon da-tree-radio-indeterminate"
v-else-if="item.checkedStatus === halfCheckedStatus" />
<view class="da-tree-item__checkbox--icon da-tree-radio-outline" v-else />
</view>
<view class="da-tree-item__label" :class="'da-tree-item__label--'+item.checkedStatus"
@click="handleLabelClick(item)">{{ item.label }} <text class="da-tree-item__label--append"
v-if="item.append">{{ item.append }}</text></view>
</view>
</scroll-view>
</view>
</template>
<script>
import {
unCheckedStatus,
halfCheckedStatus,
isCheckedStatus,
deepClone,
getAllNodeKeys,
getAllNodes
} from './utils'
export default {
name: 'DaTree',
props: {
/**
* 树的数据
*/
data: {
type: Array,
default: () => [],
},
/**
* 主题色
*/
themeColor: {
type: String,
default: '#007aff',
},
/**
* 默认选中的节点,注意单选时为单个key,多选时为key的数组
*/
defaultCheckedKeys: {
type: [Array, String, Number],
default: null,
},
/**
* 选择框的位置,可选 left/right
*/
checkboxPlacement: {
type: String,
default: 'left',
},
/**
* 是否默认展开全部
*/
defaultExpandAll: {
type: Boolean,
default: false,
},
/**
* 默认展开的节点
*/
defaultExpandedKeys: {
type: Array,
default: null,
},
/**
* 子项缩进距离,默认40,单位rpx
*/
indent: {
type: Number,
default: 40,
},
/**
* 字段对应内容,默认为 {label: 'label',key: 'key', children: 'children', disabled: 'disabled', append: 'append'}
*/
field: {
type: Object,
default: null,
},
/**
* 是否开启多选,默认单选
*/
showCheckbox: {
type: Boolean,
default: false,
},
/**
* 是否显示单选图标,默认显示
*/
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,
},
/**
* 是否渲染禁用值
*/
checkedDisabled: {
type: Boolean,
default: false,
},
/**
* 是否返回已禁用的但已选中的key
*/
packDisabledkey: {
type: Boolean,
default: true,
},
/**
* 选择时是否展开当前已选的所有下级节点,默认不展开
*/
expandChecked: {
type: Boolean,
default: false,
},
},
data() {
return {
unCheckedStatus,
halfCheckedStatus,
isCheckedStatus,
/** 原始的树数据 */
dataRef: [],
/** 处理后的一维树项数据 */
datalist: [],
/** 处理后的以key为键值的树项数据 */
datamap: {},
/** 默认的展开数据 */
expandedKeys: [],
/** 默认的已选数据 */
checkedKeys: null,
/** 加载状态 */
loadLoading: false,
}
},
watch: {
defaultExpandedKeys: {
// deep: true,
immediate: true,
handler: function(v) {
if (v?.length) {
this.expandedKeys = v
} else {
this.expandedKeys = []
}
// if (v) this.checkInitData(this.datalist)
},
},
defaultCheckedKeys: {
// deep: true,
immediate: true,
handler: function(v) {
if (this.showCheckbox) {
if (v?.length) {
this.checkedKeys = v
} else {
this.checkedKeys = []
}
} else {
if (v || v === 0) {
this.checkedKeys = v
} else {
this.checkedKeys = null
}
}
// this.checkInitData(this.datalist)
},
},
data: {
deep: true,
immediate: true,
handler: function(v) {
this.dataRef = deepClone(v)
setTimeout(() => {
this.initData()
}, 36)
},
},
},
methods: {
/**
* 初始化数据结构
*/
initData() {
const data = deepClone(this.dataRef)
this.datalist = []
this.datamap = {}
// clean tree
this.handleTreeData(data)
// flat tree
this.datalist = this.checkInitData(this.datalist)
// console.log('init datalist', this.datalist)
// console.log('init datamap', this.datamap)
},
/**
* 转换为节点数据
* @param data
* @param parent
* @param level
*/
handleTreeData(data = [], parent = null, level = 0, insertIndex = -1, secondIndex) {
return data.reduce((prev, cur, index) => {
const key = cur[this.field?.key || 'key']
const children = cur[this.field?.children || 'children'] || null
const newItem = this.createNewItem(cur, index, parent, level)
this.datamap[key] = newItem
if (insertIndex > -1) {
this.datalist.splice(insertIndex + 1, 0, newItem)
parent.children.push(newItem)
if (newItem.parentKeys?.length) {
newItem.parentKeys.forEach(k => {
this.datamap[k].childrenKeys = [...this.datamap[k].childrenKeys,
newItem
.key
]
})
}
} else {
this.datalist.push(newItem)
}
const hasChildren = children && children.length > 0
if (hasChildren) {
const childrenData = this.handleTreeData(children, newItem, level + 1)
newItem.children = childrenData
const childrenKeys = childrenData.reduce((p, k) => {
const keys = k.childrenKeys
p.push(...keys, k.key)
return p
}, [])
newItem.childrenKeys = childrenKeys
}
newItem.positionCode = cur.positionCode
// newItem.parentCodes = cur.parentCodes.split(',')
prev.push(newItem)
return prev
}, [])
},
/**
* 创建节点
* @param item
* @param index
* @param parent
* @param level
*/
createNewItem(item, index, parent, level) {
const key = item[this.field?.key || 'key']
const label = item[this.field?.label || 'label']
const children = item[this.field?.children || 'children'] || null
const append = item[this.field?.append || 'append'] || null
let disabled = item[this.field?.disabled || 'disabled'] || false
const hasChildren = children && children.length > 0
const hasEmptyChildren = children && children.length === 0
let showArrow = true
let isLeaf = !hasChildren
let expand = this.defaultExpandAll
if (this.loadMode && hasEmptyChildren) {
isLeaf = false
expand = false
showArrow = true
}
if (disabled) {
showArrow = false
}
if (hasChildren) {
showArrow = true
} else {
if (this.loadMode && hasEmptyChildren && !disabled) {
showArrow = true
} else {
showArrow = false
}
}
if (!isLeaf && !this.showCheckbox && this.onlyRadioLeaf) {
disabled = true
showArrow = true
}
const parentKey = parent ? parent.key : null
const show = this.defaultExpandAll ? true : level === 0
const newItem = {
key,
parentKey,
label,
append,
isLeaf,
showArrow,
level,
expand,
show,
disabled,
loading: false,
indexs: [index],
checkedStatus: unCheckedStatus,
parentKeys: [],
childrenKeys: [],
children: [],
originItem: item,
}
if (parent) {
newItem.parentKeys = [parent.key, ...parent.parentKeys]
newItem.indexs = [...parent.indexs, index]
}
return newItem
},
/**
* 处理初始化内容
* @param list
*/
checkInitData(list) {
let checkedKeyList = null
let expandedKeyList = []
if (this.showCheckbox) {
checkedKeyList = [...new Set(this.checkedKeys || [])]
} else {
checkedKeyList = this.checkedKeys || null
}
this.handleCheckState(list, checkedKeyList)
// 处理初始展开
expandedKeyList = [...new Set(this.expandedKeys || [])]
if (!this.defaultExpandAll) {
this.handleExpandState(list, expandedKeyList, true)
}
return list
},
/**
* 处理选中
* @param list
* @param checkedKeyList
*/
handleCheckState(list, checkedKeyList, checked = true) {
if (this.showCheckbox) {
for (let i = 0; i < list.length; i++) {
const item = list[i]
if (checkedKeyList?.includes(item.key)) {
if (this.checkedDisabled || !item.disabled) {
this.handleExpandParentNode(item, checked)
this.checkTheChecked(item, checked)
}
}
}
} else {
// 单选
for (let i = 0; i < list.length; i++) {
const item = list[i]
if (item.key === checkedKeyList && !item.disabled) {
if (this.checkedDisabled || !item.disabled) {
this.checkTheRadio(item, checked)
break
}
}
}
}
},
/**
* 校验多选节点
* @param item
* @param checked
*/
checkTheChecked(item, checked = true) {
const {
childrenKeys,
parentKeys,
disabled = false
} = item
if (!this.checkedDisabled && disabled) return
// 当前
item.checkedStatus = checked ? isCheckedStatus : unCheckedStatus
if (!this.checkStrictly) {
// 子类
childrenKeys.forEach(k => {
const childrenItem = this.datamap[k]
childrenItem.checkedStatus = (!this.checkedDisabled && childrenItem.disabled) ?
childrenItem.checkedStatus : item.checkedStatus
})
// 父类
parentKeys.forEach(k => {
const parentItem = this.datamap[k]
parentItem.checkedStatus = this.getParentCheckedStatus(parentItem)
})
}
},
/**
* 校验单选节点
* @param item
*/
checkTheRadio(item, checked) {
// console.log('item',item);
// console.log('checked',checked);
const {
parentKeys,
isLeaf,
disabled = false
} = item
if (!this.checkedDisabled && disabled) return
// 限制末节点选中,但当前非末节点
if (this.onlyRadioLeaf && !isLeaf) {
console.error(`DaTree: 限制了末节点选中,当前[${item.label}]非末节点`)
return
}
if (this.datalist?.length) {
for (let i = 0; i < this.datalist.length; i++) {
const k = this.datalist[i]
k.checkedStatus = unCheckedStatus
}
}
parentKeys.forEach(k => {
const parentItem = this.datamap[k]
parentItem.checkedStatus = this.getParentCheckedStatus(parentItem, checked)
})
// 当前
if (checked === true) {
item.checkedStatus = isCheckedStatus
}
if (checked === false) {
item.checkedStatus = unCheckedStatus
}
// item.checkedStatus = unCheckedStatus
},
/**
* 处理父节点展开
* @param item
* @param expand
*/
handleExpandParentNode(item, expand = true) {
if (!expand) return
if (item?.parentKeys?.length) {
item.parentKeys.forEach(pk => {
if (!this.datamap[pk].expand) {
this.datamap[pk].expand = true
}
})
}
},
/**
* 处理节点展开
* @param list
* @param expandedKeyList
* @param expand
*/
handleExpandState(list, expandedKeyList, expand = true) {
for (let i = 0; i < list.length; i++) {
const item = list[i]
if (expand === true) {
// 处理展开
if (expandedKeyList?.includes(item.key)) {
item.expand = true
this.handleExpandParentNode(item, true)
}
} else {
// 处理收起
if (expandedKeyList?.includes(item.key)) {
item.expand = false
if (item?.childrenKeys?.length) {
item.childrenKeys.forEach(ck => {
this.datamap[ck].expand = false
this.datamap[ck].show = false
})
}
}
}
}
for (let i = 0; i < list.length; i++) {
const item = list[i]
if (item.level > 0) {
const parentItem = this.datamap[item.parentKey]
if (parentItem) {
if (parentItem.expand && parentItem.show) {
item.show = true
}
}
}
}
},
/**
* 点击选框
* @param item
*/
handleCheckChange(item) {
const {
childrenKeys,
parentKeys,
checkedStatus,
isLeaf,
originItem = null,
disabled = false
} = item
if (!this.showCheckbox) return
if (disabled) return
// 当前
item.checkedStatus = checkedStatus === isCheckedStatus ? unCheckedStatus : isCheckedStatus
// 子类
if (!this.checkStrictly) {
if (this.expandChecked) {
item.show = true
item.expand = childrenKeys?.length > 0 || isLeaf
}
childrenKeys.forEach(k => {
const childrenItem = this.datamap[k]
childrenItem.checkedStatus = childrenItem.disabled ? childrenItem.checkedStatus : item
.checkedStatus
if (this.expandChecked) {
childrenItem.show = true
childrenItem.expand = childrenItem?.childrenKeys?.length > 0 || childrenItem.isLeaf
}
})
} else {
if (this.expandChecked) {
console.error(`DaTree: 多选时,当 checkStrictly 为 true 时,不支持选择自动展开子节点属性(expandChecked)`)
}
}
// 父类
if (!this.checkStrictly) {
parentKeys.forEach(k => {
const parentItem = this.datamap[k]
parentItem.checkedStatus = this.getParentCheckedStatus(parentItem)
})
}
const hasCheckedKeys = []
for (let i = 0; i < this.datalist.length; i++) {
const k = this.datalist[i]
if (k.checkedStatus === isCheckedStatus) {
if ((this.packDisabledkey && k.disabled) || !k.disabled) {
hasCheckedKeys.push(k.key)
}
}
}
this.checkedKeys = [...hasCheckedKeys]
this.$emit('change', hasCheckedKeys, originItem)
},
/**
* 点击单选
* @param item
*/
handleRadioChange(item) {
const {
parentKeys,
checkedStatus,
key,
originItem = null,
disabled = false
} = item
if (this.showCheckbox) return
if (this.onlyRadioLeaf) {
this.handleExpandedChange(item)
}
if (disabled) return
// 重置所有选择
if (this.datalist?.length) {
for (let i = 0; i < this.datalist.length; i++) {
const k = this.datalist[i]
k.checkedStatus = unCheckedStatus
}
}
parentKeys.forEach(k => {
const parentItem = this.datamap[k]
parentItem.checkedStatus = this.getParentCheckedStatus(parentItem)
})
// 当前
item.checkedStatus = checkedStatus === isCheckedStatus ? unCheckedStatus : isCheckedStatus
this.checkedKeys = key
this.$emit('change', key, originItem, parentKeys)
},
/**
* 点击标签
*/
handleLabelClick(item) {
if (this.showCheckbox) {
this.handleCheckChange(item)
} else {
this.handleRadioChange(item)
}
},
/**
* 点击展开收起
* @param item
*/
async handleExpandedChange(item) {
const {
expand,
originItem = null,
loading = false
} = item
if (this.loadLoading && loading) return
this.checkExpandedChange(item)
// 异步
item.expand = !expand
let currentItem = null
if (!this.showCheckbox && this.onlyRadioLeaf && this.loadMode) {
console.error(`DaTree: 单选时,当 onlyRadioLeaf 为 true 时不支持动态数据`)
} else {
currentItem = await this.loadExpandNode(item)
}
this.$emit('expand', item, !expand, currentItem || originItem || null)
},
/**
* 检查展开状态
* @param item
*/
checkExpandedChange(item) {
const {
expand,
childrenKeys,
children = null
} = item
if (expand) {
if (childrenKeys?.length) {
childrenKeys.forEach(k => {
if (this.datamap[k]) {
this.datamap[k].show = false
this.datamap[k].expand = false
}
})
}
} else {
if (children?.length) {
const childrenKeys = children.map(k => k.key)
childrenKeys.forEach(k => {
if (this.datamap[k]) {
this.datamap[k].show = true
}
})
}
}
},
/**
* 加载异步数据
* @param item
*/
async loadExpandNode(item) {
const {
expand,
key,
children = null
} = item
if (expand && this.loadMode && (!children || children.length === 0)) {
if (typeof this.loadApi === 'function') {
this.expandedKeys.push(key)
this.loadLoading = true
item.loading = true
const currentNode = deepClone(item)
const apiRes = await this.loadApi(currentNode.positionCode)
item.originItem = apiRes || null
if (apiRes?.length) {
const insertIndex = this.datalist.findIndex(k => k.key === item.key)
this.handleTreeData(apiRes, item, item.level + 1, insertIndex)
this.datalist = this.checkInitData(this.datalist)
} else {
// 加载后无数据就移除展开图标
item.expand = false
item.isLeaf = true
item.showArrow = false
}
this.loadLoading = false
item.loading = false
}
} else {
const eki = this.expandedKeys.findIndex(k => k === key)
if (eki >= 0) {
this.expandedKeys.splice(eki, 1)
}
}
return item
},
/**
* 获取父类的选中状态
* @param item
*/
getParentCheckedStatus(item, checked = true) {
console.log(item);
console.log(checked);
if (!item) {
return unCheckedStatus
}
if (!this.checkedDisabled && item.disabled) {
return item.checkedStatus || unCheckedStatus
}
// 单选时,父类永远为半选
// checked为false unCheckedStatus取消选择
if (!this.showCheckbox && checked == true) {
// console.log('半半');
return halfCheckedStatus
}
if (!this.showCheckbox && checked === false) {
// console.log('add');
return unCheckedStatus
}
const {
children
} = item
// 子类全选中
const childrenCheckedAll = children.every(k => k.checkedStatus === isCheckedStatus)
if (childrenCheckedAll) {
return isCheckedStatus
}
// 子类全不选中
const childrenUncheckedAll = children.every(k => k.checkedStatus === unCheckedStatus)
if (childrenUncheckedAll) {
return unCheckedStatus
}
return halfCheckedStatus
},
/**
* 返回已选的 key
*/
getCheckedKeys() {
return getAllNodeKeys(this.datalist, 'checkedStatus', isCheckedStatus, this.packDisabledkey)
},
/**
* 根据key设置已选
* @param keys 多选时为key的数组,单选时为key
* @param checked 多选时为key的数组,单选时为key
*/
setCheckedKeys(keys, checked = true) {
// console.log('--==========================',keys);
// console.log('--==========================',checked);
if (!Array.isArray(keys) && this.showCheckbox) {
console.error('DaTree: setCheckedKeys 第一个参数非数组,传入的是:', keys)
return
}
const list = this.datalist
if (checked === false) {
// console.log('11111111111111111111111111111111111111111111111111111111111111111111111');
let newCheckedKeys
if (this.showCheckbox) {
newCheckedKeys = []
for (let i = 0; i < this.checkedKeys.length; i++) {
const ck = this.checkedKeys[i]
if (!keys.includes(ck)) {
newCheckedKeys.push(ck)
}
}
newCheckedKeys = [...new Set(newCheckedKeys)]
} else {
// 单选时,必须至少勾选一个,所以单选不支持取消选中。
// newCheckedKeys = null
newCheckedKeys = keys
}
this.checkedKeys = newCheckedKeys
// console.log(this.checkedKeys);
console.log(list);
this.handleCheckState(list, keys, false)
} else {
this.handleCheckState(list, keys, true)
if (this.showCheckbox) {
this.checkedKeys = [...new Set([...(this.checkedKeys || []), ...(keys || [])])]
this.handleExpandState(list, keys, true)
} else {
// 单选时如果为数组则拿第一个
if (Array.isArray(keys)) {
keys = keys[0]
}
this.checkedKeys = keys || null
this.handleExpandState(list, [keys], true)
}
}
},
/**
* 返回半选的 key
*/
getHalfCheckedKeys() {
return getAllNodeKeys(this.datalist, 'checkedStatus', halfCheckedStatus, this.packDisabledkey)
},
/**
* 返回已展开的 key
*/
getExpandedKeys() {
return getAllNodeKeys(this.datalist, 'expand', true)
},
/**
* 根据key展开/收起
* @param keys key的数组
* @param expand true为展开/false为收起
*/
setExpandedKeys(keys, expand = true) {
if (!Array.isArray(keys)) {
console.error('DaTree: setExpandedKeys 第一个参数非数组,传入的是:', keys)
return
}
const list = this.datalist
if (expand === false) {
const newExpandedKeys = []
for (let i = 0; i < this.expandedKeys.length; i++) {
const ek = this.expandedKeys[i]
if (!keys.includes(ek)) {
newExpandedKeys.push(ek)
}
}
this.expandedKeys = [...new Set(newExpandedKeys)]
this.handleExpandState(list, keys, false)
} else {
this.datalist.forEach(k => {
if (keys.includes(k.key)) {
if (k.parentKeys?.length) {
k.parentKeys.forEach(pk => {
const parentItem = this.datamap[pk]
parentItem.expand = true
parentItem.show = true
if (parentItem.children?.length) {
const pkcs = parentItem.children.map(k => k.key)
pkcs.forEach(pkc => {
this.datamap[pkc].show = true
})
}
})
}
k.show = true
this.handleExpandedChange(k)
}
})
}
},
/**
* 返回已选的节点
*/
getCheckedNodes() {
return getAllNodes(this.datalist, 'checkedStatus', isCheckedStatus, this.packDisabledkey)
},
/**
* 返回半选的节点
*/
getHalfCheckedNodes() {
return getAllNodes(this.datalist, 'checkedStatus', halfCheckedStatus, this.packDisabledkey)
},
/**
* 返回已展开的节点
*/
getExpandedNodes() {
return getAllNodes(this.datalist, 'expand', true)
},
},
}
</script>
<style lang="scss" scoped>
@font-face {
font-family: 'iconfont';
/* Project id */
src: url('data:application/octet-stream;base64,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') format('truetype');
}
.da-tree {
width: 100%;
height: 100%;
&-scroll {
width: 100%;
height: 100%;
}
&-item {
display: flex;
align-items: center;
height: 0;
padding: 0;
overflow: hidden;
font-size: 28rpx;
line-height: 1;
visibility: hidden;
opacity: 0;
transition: opacity 0.2s linear;
&.is-show {
height: auto;
padding: 12rpx 24rpx;
visibility: visible;
opacity: 1;
}
&__icon {
display: flex;
align-items: center;
justify-content: center;
width: 40rpx;
height: 40rpx;
overflow: hidden;
&--arr {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 32rpx;
height: 32rpx;
&::after {
position: relative;
z-index: 1;
overflow: hidden;
/* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
font-family: 'iconfont' !important;
font-size: 32rpx;
font-style: normal;
color: #999;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
&.is-expand {
&::after {
content: '\e604';
}
}
&.is-right {
transform: rotate(-90deg);
}
&.is-loading {
animation: IconLoading 1s linear 0s infinite;
&::after {
content: '\e7f1';
}
}
}
}
&__checkbox {
width: 40rpx;
height: 40rpx;
overflow: hidden;
&--left {
order: 0;
}
&--right {
order: 1;
}
&--icon {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 40rpx;
height: 40rpx;
&::after {
position: relative;
top: 0;
left: 0;
z-index: 1;
overflow: hidden;
/* stylelint-disable-next-line font-family-no-missing-generic-family-keyword */
font-family: 'iconfont' !important;
font-size: 32rpx;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
&.da-tree-checkbox-outline::after {
color: #bbb;
content: '\ead5';
}
&.da-tree-checkbox-checked::after {
color: var(--theme-color, #007aff);
content: '\ead4';
}
&.da-tree-checkbox-indeterminate::after {
color: var(--theme-color, #007aff);
content: '\ebce';
}
&.da-tree-radio-outline::after {
color: #bbb;
content: '\ecc5';
}
&.da-tree-radio-checked::after {
color: var(--theme-color, #007aff);
content: '\ecc4';
}
&.da-tree-radio-indeterminate::after {
color: var(--theme-color, #007aff);
content: '\ea4f';
}
}
&.is--disabled {
cursor: not-allowed;
opacity: 0.35;
}
}
&__label {
flex: 1;
margin-left: 4rpx;
color: #555;
&--2 {
color: var(--theme-color, #007aff);
}
&--append {
font-size: 60%;
opacity: 0.6;
}
}
}
}
@keyframes IconLoading {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
- utils.js文件
javascript
// @ts-nocheck
/** 未选 */
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 []
}
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
}