fix: 修复执行: check:cspell 命令路径参数传入没有转义导致检测路径失效。添加菜单右键功能。优化:多余监听 activePath 变化,自动滚动到激活项 watch

This commit is contained in:
lmx
2026-03-04 18:25:15 +08:00
parent 2a86404ba5
commit 173e6b08c9
9 changed files with 77 additions and 44 deletions

View File

@@ -37,7 +37,7 @@
"changeset": "pnpm exec changeset", "changeset": "pnpm exec changeset",
"check": "pnpm run check:circular && pnpm run check:dep && pnpm run check:type && pnpm check:cspell", "check": "pnpm run check:circular && pnpm run check:dep && pnpm run check:type && pnpm check:cspell",
"check:circular": "vsh check-circular", "check:circular": "vsh check-circular",
"check:cspell": "cspell lint **/*.ts **/README.md .changeset/*.md --no-progress", "check:cspell": "cspell lint \"**/*.ts\" \"**/README.md\" \".changeset/*.md\" --no-progress",
"check:dep": "vsh check-dep", "check:dep": "vsh check-dep",
"check:type": "turbo run typecheck", "check:type": "turbo run typecheck",
"clean": "node ./scripts/clean.mjs", "clean": "node ./scripts/clean.mjs",
@@ -59,7 +59,7 @@
"reinstall": "pnpm clean --del-lock && pnpm install", "reinstall": "pnpm clean --del-lock && pnpm install",
"test:unit": "vitest run --dom", "test:unit": "vitest run --dom",
"test:e2e": "turbo run test:e2e", "test:e2e": "turbo run test:e2e",
"update:deps": "npx taze -r -w", "up te:deps": "npx taze -r -w",
"version": "pnpm exec changeset version && pnpm install --no-frozen-lockfile", "version": "pnpm exec changeset version && pnpm install --no-frozen-lockfile",
"catalog": "pnpx codemod pnpm/catalog" "catalog": "pnpx codemod pnpm/catalog"
}, },

View File

@@ -1,6 +1,8 @@
import type { Component } from 'vue'; import type { Component } from 'vue';
import type { RouteRecordRaw } from 'vue-router'; import type { RouteRecordRaw } from 'vue-router';
import type { Recordable } from './helper';
/** /**
* 扩展路由原始对象 * 扩展路由原始对象
*/ */
@@ -66,6 +68,10 @@ interface MenuRecordRaw extends MenuRecordBadgeRaw {
* 菜单路径唯一可当作key * 菜单路径唯一可当作key
*/ */
path: string; path: string;
/**
* 菜单参数
*/
query?: Recordable<any>;
/** /**
* 是否显示菜单 * 是否显示菜单
* @default true * @default true

View File

@@ -306,13 +306,13 @@ const handleDragSidebar = (e: MouseEvent) => {
ref="asideRef" ref="asideRef"
:style="style" :style="style"
class="fixed left-0 top-0 h-full transition-all duration-150" class="fixed left-0 top-0 h-full transition-all duration-150"
:class="theme"
@mouseenter="handleMouseenter" @mouseenter="handleMouseenter"
@mouseleave="handleMouseleave" @mouseleave="handleMouseleave"
> >
<div <div
class="h-full" class="h-full"
:class="[ :class="[
theme,
{ {
'bg-sidebar-deep': isSidebarMixed, 'bg-sidebar-deep': isSidebarMixed,
'border-r border-border bg-sidebar': !isSidebarMixed, 'border-r border-border bg-sidebar': !isSidebarMixed,

View File

@@ -43,6 +43,10 @@
"@vben-core/shared": "workspace:*", "@vben-core/shared": "workspace:*",
"@vben-core/typings": "workspace:*", "@vben-core/typings": "workspace:*",
"@vueuse/core": "catalog:", "@vueuse/core": "catalog:",
"qs": "catalog:",
"vue": "catalog:" "vue": "catalog:"
},
"devDependencies": {
"@types/qs": "catalog:"
} }
} }

View File

@@ -6,6 +6,8 @@ import { computed, onBeforeUnmount, onMounted, reactive, useSlots } from 'vue';
import { useNamespace } from '@vben-core/composables'; import { useNamespace } from '@vben-core/composables';
import { VbenIcon, VbenTooltip } from '@vben-core/shadcn-ui'; import { VbenIcon, VbenTooltip } from '@vben-core/shadcn-ui';
import qs from 'qs';
import { MenuBadge } from '../components'; import { MenuBadge } from '../components';
import { useMenu, useMenuContext, useSubMenuContext } from '../hooks'; import { useMenu, useMenuContext, useSubMenuContext } from '../hooks';
@@ -54,6 +56,7 @@ const item: MenuItemRegistered = reactive({
active, active,
parentPaths: parentPaths.value, parentPaths: parentPaths.value,
path: props.path || '', path: props.path || '',
query: props.query,
}); });
/** /**
@@ -81,42 +84,50 @@ onBeforeUnmount(() => {
}); });
</script> </script>
<template> <template>
<li <a
:class="[ :href="
rootMenu.theme, (item.parentPaths.at(-1) ?? '') +
b(), (item?.query ? `?${qs.stringify(item?.query)}` : '')
is('active', active), "
is('disabled', disabled), @click.prevent.stop="handleClick"
is('collapse-show-title', collapseShowTitle),
]"
role="menuitem"
@click.stop="handleClick"
> >
<VbenTooltip <li
v-if="showTooltip" :class="[
:content-class="[rootMenu.theme]" rootMenu.theme,
side="right" b(),
is('active', active),
is('disabled', disabled),
is('collapse-show-title', collapseShowTitle),
]"
role="menuitem"
> >
<template #trigger> <!-- -->
<div :class="[nsMenu.be('tooltip', 'trigger')]"> <VbenTooltip
<VbenIcon :class="nsMenu.e('icon')" :icon="menuIcon" fallback /> v-if="showTooltip"
<slot></slot> :content-class="[rootMenu.theme]"
<span v-if="collapseShowTitle" :class="nsMenu.e('name')"> side="right"
<slot name="title"></slot> >
</span> <template #trigger>
</div> <div :class="[nsMenu.be('tooltip', 'trigger')]">
</template> <VbenIcon :class="nsMenu.e('icon')" :icon="menuIcon" fallback />
<slot name="title"></slot> <slot></slot>
</VbenTooltip> <span v-if="collapseShowTitle" :class="nsMenu.e('name')">
<div v-show="!showTooltip" :class="[e('content')]"> <slot name="title"></slot>
<MenuBadge </span>
v-if="rootMenu.props.mode !== 'horizontal'" </div>
class="right-2" </template>
v-bind="props" <slot name="title"></slot>
/> </VbenTooltip>
<VbenIcon :class="nsMenu.e('icon')" :icon="menuIcon" /> <div v-show="!showTooltip" :class="[e('content')]">
<slot></slot> <MenuBadge
<slot name="title"></slot> v-if="rootMenu.props.mode !== 'horizontal'"
</div> class="right-2"
</li> v-bind="props"
/>
<VbenIcon :class="nsMenu.e('icon')" :icon="menuIcon" />
<slot></slot>
<slot name="title"></slot>
</div>
</li>
</a>
</template> </template>

View File

@@ -212,15 +212,15 @@ const enableScroll = computed(
() => props.scrollToActive && props.mode === 'vertical' && !props.collapse, () => props.scrollToActive && props.mode === 'vertical' && !props.collapse,
); );
const { scrollToActiveItem } = useMenuScroll(activePath, { useMenuScroll(activePath, {
enable: enableScroll, enable: enableScroll,
delay: 320, delay: 320,
}); });
// 监听 activePath 变化,自动滚动到激活项 // 监听 activePath 变化,自动滚动到激活项
watch(activePath, () => { // watch(activePath, () => {
scrollToActiveItem(); // scrollToActiveItem();
}); // });
// 默认展开菜单 // 默认展开菜单
function initMenu() { function initMenu() {

View File

@@ -41,6 +41,7 @@ const hasChildren = computed(() => {
:badge-variants="menu.badgeVariants" :badge-variants="menu.badgeVariants"
:icon="menu.icon" :icon="menu.icon"
:path="menu.path" :path="menu.path"
:query="menu.query"
> >
<template #title> <template #title>
<span>{{ menu.name }}</span> <span>{{ menu.name }}</span>

View File

@@ -1,6 +1,10 @@
import type { Component, Ref } from 'vue'; import type { Component, Ref } from 'vue';
import type { MenuRecordBadgeRaw, ThemeModeType } from '@vben-core/typings'; import type {
MenuRecordBadgeRaw,
Recordable,
ThemeModeType,
} from '@vben-core/typings';
interface MenuProps { interface MenuProps {
/** /**
@@ -91,12 +95,17 @@ interface MenuItemProps extends MenuRecordBadgeRaw {
* @zh_CN menuitem 名称 * @zh_CN menuitem 名称
*/ */
path: string; path: string;
/**
* @zh_CN 菜单所携带的参数
*/
query?: Recordable<any>;
} }
interface MenuItemRegistered { interface MenuItemRegistered {
active: boolean; active: boolean;
parentPaths: string[]; parentPaths: string[];
path: string; path: string;
query?: Recordable<any>;
} }
interface MenuItemClicked { interface MenuItemClicked {

View File

@@ -43,6 +43,7 @@ function generateMenus(
link, link,
order, order,
title = '', title = '',
query,
} = meta; } = meta;
// 确保菜单名称不为空 // 确保菜单名称不为空
@@ -71,6 +72,7 @@ function generateMenus(
badgeVariants, badgeVariants,
icon, icon,
name, name,
query,
order, order,
parent: route.parent, parent: route.parent,
parents: route.parents, parents: route.parents,