Files
oci-portal-dash/src/components/CompartmentSelect.vue
T

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>