84 lines
2.4 KiB
Vue
84 lines
2.4 KiB
Vue
<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>
|