使用 useId 优化菜单逻辑

This commit is contained in:
Hooray
2025-06-07 10:20:49 +08:00
parent ab9719727c
commit 34f29061f8
3 changed files with 20 additions and 7 deletions
+15 -4
View File
@@ -20,6 +20,16 @@ const props = withDefaults(
},
)
// 用于缓存对象到 ID 的映射
const idMap = new WeakMap<object, string>()
// 获取对象的唯一 ID,如果已存在则返回缓存的 ID,否则生成新的
function getUseId(obj: object): string {
if (!idMap.has(obj)) {
idMap.set(obj, useId())
}
return idMap.get(obj)!
}
const activeIndex = ref<MenuInjection['activeIndex']>(props.value)
const items = ref<MenuInjection['items']>({})
const subMenus = ref<MenuInjection['subMenus']>({})
@@ -32,7 +42,7 @@ const isMenuPopup = computed<MenuInjection['isMenuPopup']>(() => {
// 解析传入的 menu 数据,并保存到 items 和 subMenus 对象中
function initItems(menu: MenuProps['menu'], parentPaths: string[] = []) {
menu.forEach((item) => {
const index = item.path ?? JSON.stringify(item)
const index = item.path ?? getUseId(item)
if (item.children) {
const indexPath = [...parentPaths, index]
subMenus.value[index] = {
@@ -144,6 +154,7 @@ watch(() => props.collapse, (value) => {
provide(rootMenuInjectionKey, reactive({
props,
getUseId,
items,
subMenus,
activeIndex,
@@ -164,10 +175,10 @@ provide(rootMenuInjectionKey, reactive({
'py-1': props.mode === 'vertical',
})"
>
<template v-for="item in menu" :key="item.path ?? JSON.stringify(item)">
<template v-for="item in menu" :key="item.path ?? getUseId(item)">
<template v-if="item.meta?.menu !== false">
<SubMenu v-if="item.children?.length" :menu="item" :unique-key="[item.path ?? JSON.stringify(item)]" />
<Item v-else :item="item" :unique-key="[item.path ?? JSON.stringify(item)]" @click="handleMenuItemClick(item.path ?? JSON.stringify(item))" />
<SubMenu v-if="item.children?.length" :menu="item" :unique-key="[item.path ?? getUseId(item)]" />
<Item v-else :item="item" :unique-key="[item.path ?? getUseId(item)]" @click="handleMenuItemClick(item.path ?? getUseId(item))" />
</template>
</template>
</div>
+4 -3
View File
@@ -16,11 +16,12 @@ const props = withDefaults(
},
)
const index = props.menu.path ?? JSON.stringify(props.menu)
const itemRef = useTemplateRef('itemRef')
const subMenuRef = useTemplateRef('subMenuRef')
const rootMenu = inject(rootMenuInjectionKey)!
const index = props.menu.path ?? rootMenu.getUseId(props.menu)
const opened = computed(() => {
return rootMenu.openedMenus.includes(props.uniqueKey.at(-1)!)
})
@@ -201,8 +202,8 @@ function handleMouseleave() {
'py-1': rootMenu.isMenuPopup,
})"
>
<template v-for="item in menu.children" :key="item.path ?? JSON.stringify(item)">
<SubMenu v-if="item.meta?.menu !== false" :unique-key="[...uniqueKey, item.path ?? JSON.stringify(item)]" :menu="item" :level="level + 1" />
<template v-for="item in menu.children" :key="item.path ?? rootMenu.getUseId(item)">
<SubMenu v-if="item.meta?.menu !== false" :unique-key="[...uniqueKey, item.path ?? rootMenu.getUseId(item)]" :menu="item" :level="level + 1" />
</template>
</FaScrollArea>
</Transition>
+1
View File
@@ -18,6 +18,7 @@ export interface MenuProps {
export interface MenuInjection {
props: MenuProps
getUseId: (obj: object) => string
items: Record<string, MenuItem>
subMenus: Record<string, MenuItem>
activeIndex: MenuProps['value']