三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

el-tree 二次封装 含搜索

el-tree 二次封装 含搜索

<template>
<div class="smart-tree-radio">
<el-input
v-model="keyword"
clearable
placeholder="请输入关键字搜索"
class="smart-tree-radio__search"
/>

<el-tree
ref="treeRef"
:data="filterTreeData"
:props="treeProps"
node-key="value"
default-expand-all
:expand-on-click-node="false"
>
<template #default="{ data }">
<el-radio
class="smart-tree-radio__radio"
:model-value="innerValue"
:value="data.value"
:disabled="data.disabled"
@change="handleRadioChange(data)"
>
{{ data.label }}
</el-radio>
</template>
</el-tree>

<div class="smart-tree-radio__footer">
<el-button size="small" @click="handleClear">清空</el-button>
</div>
</div>
</template>

<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import type { ElTree } from 'element-plus'

interface TreeItem {
label: string
value: string | number
disabled?: boolean
children?: TreeItem[]
}

const props = withDefaults(
defineProps<{
modelValue?: string | number | null
data: TreeItem[]
}>(),
{
modelValue: null,
data: () => []
}
)

const emit = defineEmits<{
'update:modelValue': [value: string | number | null]
change: [node: TreeItem | null]
}>()

const treeRef = ref<InstanceType<typeof ElTree>>()

const keyword = ref('')
const innerValue = ref<string | number | null>(props.modelValue)

const treeProps = {
label: 'label',
children: 'children'
}

/**
* 搜索核心:
* 命中当前节点,保留当前节点;
* 命中子节点,也保留父级路径。
*/
const filterTreeData = computed(() => {
const key = keyword.value.trim()

if (!key) return props.data

const loop = (list: TreeItem[]): TreeItem[] => {
return list.reduce<TreeItem[]>((result, item) => {
const children = item.children ? loop(item.children) : []
const selfMatched = item.label.includes(key)

if (selfMatched || children.length) {
result.push({
...item,
children
})
}

return result
}, [])
}

return loop(props.data)
})

/**
* 外部传 value 后,支持反显。
*/
watch(
() => props.modelValue,
value => {
innerValue.value = value ?? null
},
{
immediate: true
}
)

/**
* el-radio 单选:
* 只改变当前节点 value;
* 不影响父级,不影响子级。
*/
const handleRadioChange = (data: TreeItem) => {
if (data.disabled) return

innerValue.value = data.value

emit('update:modelValue', data.value)
emit('change', data)
}

/**
* 清空:
* 清空当前选中 value。
*/
const handleClear = () => {
innerValue.value = null

emit('update:modelValue', null)
emit('change', null)
}
</script>

<style scoped lang="scss">
.smart-tree-radio {
width: 100%;

&__search {
margin-bottom: 8px;
}

&__radio {
width: 100%;
height: 26px;
display: flex;
align-items: center;
}

&__footer {
margin-top: 8px;
text-align: right;
}
}
</style>

← 返回列表