@@ -0,0 +1,83 @@
|
||||
<script setup lang="ts">
|
||||
import { NTreeSelect, type TreeSelectOption } from 'naive-ui'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
import type { Compartment } from '@/types/api'
|
||||
|
||||
/** 区间选择器:按 parentId 组树,与官方控制台一致分层展示;
|
||||
* 父级不在列表中的节点视为根 compartment 的直接子级,根以 value='' 表示。 */
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
value: string
|
||||
compartments: Compartment[]
|
||||
rootLabel: string
|
||||
loading?: boolean
|
||||
disabled?: boolean
|
||||
size?: 'small' | 'medium'
|
||||
}>(),
|
||||
{ size: 'small' },
|
||||
)
|
||||
const emit = defineEmits<{ 'update:value': [string] }>()
|
||||
|
||||
function groupByParent(list: Compartment[]): Map<string, Compartment[]> {
|
||||
const ids = new Set(list.map((c) => c.id))
|
||||
const map = new Map<string, Compartment[]>()
|
||||
for (const c of list) {
|
||||
const parent = ids.has(c.parentId) ? c.parentId : ''
|
||||
map.set(parent, [...(map.get(parent) ?? []), c])
|
||||
}
|
||||
for (const arr of map.values()) arr.sort((a, b) => a.name.localeCompare(b.name, 'zh-CN'))
|
||||
return map
|
||||
}
|
||||
|
||||
function toNodes(map: Map<string, Compartment[]>, parent: string): TreeSelectOption[] | undefined {
|
||||
const list = map.get(parent)
|
||||
if (!list) return undefined
|
||||
return list.map((c) => ({ label: c.name, key: c.id, children: toNodes(map, c.id) }))
|
||||
}
|
||||
|
||||
const tree = computed<TreeSelectOption[]>(() => {
|
||||
const map = groupByParent(props.compartments)
|
||||
return [{ label: props.rootLabel, key: '', children: toNodes(map, '') }]
|
||||
})
|
||||
|
||||
/** 选项异步加载,default-expand-all 不可靠,改为受控展开全部父节点 */
|
||||
const expandedKeys = ref<Array<string | number>>([])
|
||||
watch(
|
||||
tree,
|
||||
(nodes) => {
|
||||
const keys: string[] = []
|
||||
const walk = (list: TreeSelectOption[]) => {
|
||||
for (const n of list) {
|
||||
if (!n.children?.length) continue
|
||||
keys.push(String(n.key))
|
||||
walk(n.children)
|
||||
}
|
||||
}
|
||||
walk(nodes)
|
||||
expandedKeys.value = keys
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
function onUpdate(v: string | number | Array<string | number> | null) {
|
||||
emit('update:value', typeof v === 'string' ? v : '')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NTreeSelect
|
||||
class="min-w-0"
|
||||
:value="value"
|
||||
:options="tree"
|
||||
:expanded-keys="expandedKeys"
|
||||
:loading="loading"
|
||||
:disabled="disabled"
|
||||
:size="size"
|
||||
:indent="18"
|
||||
:consistent-menu-width="false"
|
||||
filterable
|
||||
@update:value="onUpdate"
|
||||
@update:expanded-keys="(keys: Array<string | number>) => (expandedKeys = keys)"
|
||||
/>
|
||||
</template>
|
||||
Reference in New Issue
Block a user