mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-21 19:16:37 +08:00
fix(projects): 修复vertical-mix导航模式的二级菜单显示问题
This commit is contained in:
parent
c7e6b86a5b
commit
6f286e6747
18
package.json
18
package.json
@ -23,21 +23,21 @@
|
|||||||
"chroma-js": "^2.1.2",
|
"chroma-js": "^2.1.2",
|
||||||
"dayjs": "^1.10.7",
|
"dayjs": "^1.10.7",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"naive-ui": "^2.19.2",
|
"naive-ui": "^2.19.3",
|
||||||
"pinia": "^2.0.0-rc.4",
|
"pinia": "^2.0.0-rc.4",
|
||||||
"qs": "^6.10.1",
|
"qs": "^6.10.1",
|
||||||
"vue": "^3.2.10",
|
"vue": "^3.2.10",
|
||||||
"vue-router": "^4.0.11"
|
"vue-router": "^4.0.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^13.1.0",
|
"@commitlint/cli": "^13.2.0",
|
||||||
"@commitlint/config-conventional": "^13.1.0",
|
"@commitlint/config-conventional": "^13.2.0",
|
||||||
"@iconify/json": "^1.1.406",
|
"@iconify/json": "^1.1.407",
|
||||||
"@iconify/vue": "^3.0.0",
|
"@iconify/vue": "^3.0.0",
|
||||||
"@types/chroma-js": "^2.1.3",
|
"@types/chroma-js": "^2.1.3",
|
||||||
"@types/qs": "^6.9.7",
|
"@types/qs": "^6.9.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.31.2",
|
"@typescript-eslint/eslint-plugin": "^4.32.0",
|
||||||
"@typescript-eslint/parser": "^4.31.2",
|
"@typescript-eslint/parser": "^4.32.0",
|
||||||
"@vicons/antd": "^0.11.0",
|
"@vicons/antd": "^0.11.0",
|
||||||
"@vicons/carbon": "^0.11.0",
|
"@vicons/carbon": "^0.11.0",
|
||||||
"@vicons/fa": "^0.11.0",
|
"@vicons/fa": "^0.11.0",
|
||||||
@ -70,11 +70,11 @@
|
|||||||
"unplugin-icons": "^0.11.4",
|
"unplugin-icons": "^0.11.4",
|
||||||
"unplugin-vue-components": "^0.15.4",
|
"unplugin-vue-components": "^0.15.4",
|
||||||
"vite": "^2.5.10",
|
"vite": "^2.5.10",
|
||||||
"vite-plugin-html": "^2.1.0",
|
"vite-plugin-html": "^2.1.1",
|
||||||
"vite-plugin-windicss": "^1.4.7",
|
"vite-plugin-windicss": "^1.4.8",
|
||||||
"vue-tsc": "^0.3.0",
|
"vue-tsc": "^0.3.0",
|
||||||
"vueuc": "^0.4.12",
|
"vueuc": "^0.4.12",
|
||||||
"windicss": "^3.1.7"
|
"windicss": "^3.1.8"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"commitizen": {
|
"commitizen": {
|
||||||
|
2223
pnpm-lock.yaml
2223
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -27,7 +27,6 @@ export default function useReloadContext() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
context,
|
|
||||||
useReloadProvide,
|
useReloadProvide,
|
||||||
useReloadInject
|
useReloadInject
|
||||||
};
|
};
|
||||||
|
@ -1 +1,3 @@
|
|||||||
export { setupAppContext, useReloadInject } from './app';
|
export { setupAppContext, useReloadInject } from './app';
|
||||||
|
|
||||||
|
export { useVerticalMixSiderContext } from './part';
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
import useVerticalMixSiderContext from './useVerticalMixSiderContext';
|
||||||
|
|
||||||
|
export { useVerticalMixSiderContext };
|
54
src/context/part/useVerticalMixSiderContext.ts
Normal file
54
src/context/part/useVerticalMixSiderContext.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { ref } from 'vue';
|
||||||
|
import type { Ref } from 'vue';
|
||||||
|
import { useContext, useBoolean } from '@/hooks';
|
||||||
|
|
||||||
|
interface VerticalMixSiderContext {
|
||||||
|
/** 子菜单可见性 */
|
||||||
|
childMenuVisible: Ref<boolean>;
|
||||||
|
/** 展示子菜单 */
|
||||||
|
showChildMenu(): void;
|
||||||
|
/** 隐藏子菜单 */
|
||||||
|
hideChildMenu(): void;
|
||||||
|
/** 鼠标悬浮的一级菜单对应的路由名称 */
|
||||||
|
hoverRouteName: Ref<string>;
|
||||||
|
/** 设置悬浮路由名称 */
|
||||||
|
setHoverRouteName(name: string): void;
|
||||||
|
isMouseEnterChildMenu: Ref<boolean>;
|
||||||
|
setMouseEnterChildMenu(): void;
|
||||||
|
setMouseLeaveChildMenu(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { useProvide, useInject: useVerticalMixSiderInject } = useContext<VerticalMixSiderContext>();
|
||||||
|
|
||||||
|
export default function useVerticalMixSiderContext() {
|
||||||
|
const { bool: childMenuVisible, setTrue: showChildMenu, setFalse: hideChildMenu } = useBoolean();
|
||||||
|
const {
|
||||||
|
bool: isMouseEnterChildMenu,
|
||||||
|
setTrue: setMouseEnterChildMenu,
|
||||||
|
setFalse: setMouseLeaveChildMenu
|
||||||
|
} = useBoolean();
|
||||||
|
|
||||||
|
const hoverRouteName = ref('');
|
||||||
|
function setHoverRouteName(name: string) {
|
||||||
|
hoverRouteName.value = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const context: VerticalMixSiderContext = {
|
||||||
|
childMenuVisible,
|
||||||
|
showChildMenu,
|
||||||
|
hideChildMenu,
|
||||||
|
hoverRouteName,
|
||||||
|
setHoverRouteName,
|
||||||
|
isMouseEnterChildMenu,
|
||||||
|
setMouseEnterChildMenu,
|
||||||
|
setMouseLeaveChildMenu
|
||||||
|
};
|
||||||
|
function useVerticalMixSiderProvide() {
|
||||||
|
useProvide(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
useVerticalMixSiderProvide,
|
||||||
|
useVerticalMixSiderInject
|
||||||
|
};
|
||||||
|
}
|
@ -21,10 +21,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch } from 'vue';
|
import { computed } from 'vue';
|
||||||
import type { PropType, VNodeChild } from 'vue';
|
import type { PropType, VNodeChild } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
import { useAppStore } from '@/store';
|
||||||
import { useBoolean } from '@/hooks';
|
import { useBoolean } from '@/hooks';
|
||||||
|
import { useVerticalMixSiderContext } from '@/context';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
routeName: {
|
routeName: {
|
||||||
@ -39,51 +41,50 @@ const props = defineProps({
|
|||||||
type: Function as PropType<() => VNodeChild>,
|
type: Function as PropType<() => VNodeChild>,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
isActive: {
|
activeRouteName: {
|
||||||
type: Boolean,
|
type: String,
|
||||||
default: false
|
required: true
|
||||||
},
|
},
|
||||||
isMini: {
|
isMini: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
|
||||||
hoverRoute: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update:hoverRoute']);
|
const app = useAppStore();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { useVerticalMixSiderInject } = useVerticalMixSiderContext();
|
||||||
const { bool: isHover, setTrue, setFalse } = useBoolean();
|
const { bool: isHover, setTrue, setFalse } = useBoolean();
|
||||||
|
const { bool: isMouseEnterMenu, setTrue: setMouseEnterMenu, setFalse: setMouseLeaveMenu } = useBoolean();
|
||||||
|
|
||||||
const hoverRouteName = ref(props.hoverRoute);
|
const { setHoverRouteName, showChildMenu, hideChildMenu, isMouseEnterChildMenu } = useVerticalMixSiderInject();
|
||||||
function setHoverRouteName(name: string) {
|
|
||||||
hoverRouteName.value = name;
|
const isActive = computed(() => props.routeName === props.activeRouteName);
|
||||||
}
|
|
||||||
|
|
||||||
function handleRouter() {
|
function handleRouter() {
|
||||||
router.push({ name: props.routeName });
|
router.push({ name: props.routeName });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMouseEvent(type: 'enter' | 'leave') {
|
async function setActiveHoverRouteName() {
|
||||||
if (type === 'enter') {
|
setTimeout(() => {
|
||||||
setTrue();
|
if (app.menu.fixedMix && !isMouseEnterChildMenu.value && !isMouseEnterMenu.value) {
|
||||||
setHoverRouteName(props.routeName);
|
setHoverRouteName(props.activeRouteName);
|
||||||
} else {
|
|
||||||
setFalse();
|
|
||||||
}
|
}
|
||||||
|
setMouseLeaveMenu();
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
function handleMouseEvent(type: 'enter' | 'leave') {
|
||||||
() => props.hoverRoute,
|
if (type === 'enter') {
|
||||||
newValue => {
|
setMouseEnterMenu();
|
||||||
setHoverRouteName(newValue);
|
setTrue();
|
||||||
|
setHoverRouteName(props.routeName);
|
||||||
|
showChildMenu();
|
||||||
|
} else {
|
||||||
|
setFalse();
|
||||||
|
hideChildMenu();
|
||||||
|
setActiveHoverRouteName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
|
||||||
watch(hoverRouteName, newValue => {
|
|
||||||
emit('update:hoverRoute', newValue);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
dark:bg-[#18181c]
|
dark:bg-[#18181c]
|
||||||
"
|
"
|
||||||
:style="{ width: showDrawer ? theme.menuStyle.width + 'px' : '0px' }"
|
:style="{ width: showDrawer ? theme.menuStyle.width + 'px' : '0px' }"
|
||||||
@mouseleave="handleResetHoverRoute"
|
@mouseenter="handleMouseEvent('enter')"
|
||||||
|
@mouseleave="handleMouseEvent('leave')"
|
||||||
>
|
>
|
||||||
<header class="header-height flex-y-center justify-between">
|
<header class="header-height flex-y-center justify-between">
|
||||||
<h2 class="pl-8px text-16px text-primary font-bold">{{ title }}</h2>
|
<h2 class="pl-8px text-16px text-primary font-bold">{{ title }}</h2>
|
||||||
@ -38,29 +39,39 @@ import { NScrollbar, NMenu } from 'naive-ui';
|
|||||||
import type { MenuOption } from 'naive-ui';
|
import type { MenuOption } from 'naive-ui';
|
||||||
import { useThemeStore, useAppStore } from '@/store';
|
import { useThemeStore, useAppStore } from '@/store';
|
||||||
import { useAppTitle } from '@/hooks';
|
import { useAppTitle } from '@/hooks';
|
||||||
|
import { useVerticalMixSiderContext } from '@/context';
|
||||||
import { menus } from '@/router';
|
import { menus } from '@/router';
|
||||||
import type { GlobalMenuOption } from '@/interface';
|
import type { GlobalMenuOption } from '@/interface';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
hoverRoute: {
|
activeRouteName: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
required: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['reset-hover-route']);
|
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const theme = useThemeStore();
|
const theme = useThemeStore();
|
||||||
const app = useAppStore();
|
const app = useAppStore();
|
||||||
const { toggleFixedMixMenu } = useAppStore();
|
const { toggleFixedMixMenu } = useAppStore();
|
||||||
|
const { useVerticalMixSiderInject } = useVerticalMixSiderContext();
|
||||||
const title = useAppTitle();
|
const title = useAppTitle();
|
||||||
|
|
||||||
|
const {
|
||||||
|
childMenuVisible,
|
||||||
|
hoverRouteName,
|
||||||
|
setHoverRouteName,
|
||||||
|
showChildMenu,
|
||||||
|
hideChildMenu,
|
||||||
|
setMouseEnterChildMenu,
|
||||||
|
setMouseLeaveChildMenu
|
||||||
|
} = useVerticalMixSiderInject();
|
||||||
|
|
||||||
const childMenus = computed(() => {
|
const childMenus = computed(() => {
|
||||||
const children: MenuOption[] = [];
|
const children: MenuOption[] = [];
|
||||||
menus.some(item => {
|
menus.some(item => {
|
||||||
const flag = item.routeName === props.hoverRoute && Boolean(item.children?.length);
|
const flag = item.routeName === hoverRouteName.value && Boolean(item.children?.length);
|
||||||
if (flag) {
|
if (flag) {
|
||||||
children.push(...item.children!);
|
children.push(...item.children!);
|
||||||
}
|
}
|
||||||
@ -69,17 +80,9 @@ const childMenus = computed(() => {
|
|||||||
return children;
|
return children;
|
||||||
});
|
});
|
||||||
|
|
||||||
const showDrawer = computed(() => childMenus.value.length || app.menu.fixedMix);
|
const showDrawer = computed(() => (childMenuVisible.value && childMenus.value.length) || app.menu.fixedMix);
|
||||||
|
|
||||||
const activeKey = computed(() => getActiveKey());
|
const activeKey = computed(() => route.name as string);
|
||||||
|
|
||||||
function getActiveKey() {
|
|
||||||
return route.name as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleResetHoverRoute() {
|
|
||||||
emit('reset-hover-route');
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleUpdateMenu(key: string, item: MenuOption) {
|
function handleUpdateMenu(key: string, item: MenuOption) {
|
||||||
const menuItem = item as GlobalMenuOption;
|
const menuItem = item as GlobalMenuOption;
|
||||||
@ -90,6 +93,17 @@ const headerHeight = computed(() => {
|
|||||||
const { height } = theme.headerStyle;
|
const { height } = theme.headerStyle;
|
||||||
return `${height}px`;
|
return `${height}px`;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function handleMouseEvent(type: 'enter' | 'leave') {
|
||||||
|
if (type === 'enter') {
|
||||||
|
showChildMenu();
|
||||||
|
setMouseEnterChildMenu();
|
||||||
|
} else {
|
||||||
|
hideChildMenu();
|
||||||
|
setMouseLeaveChildMenu();
|
||||||
|
setHoverRouteName(props.activeRouteName);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.drawer-shadow {
|
.drawer-shadow {
|
||||||
|
@ -10,11 +10,10 @@
|
|||||||
<mix-menu
|
<mix-menu
|
||||||
v-for="item in firstDegreeMenus"
|
v-for="item in firstDegreeMenus"
|
||||||
:key="item.routeName"
|
:key="item.routeName"
|
||||||
v-model:hover-route="hoverRoute"
|
|
||||||
:route-name="item.routeName"
|
:route-name="item.routeName"
|
||||||
:label="item.label"
|
:label="item.label"
|
||||||
:icon="item.icon"
|
:icon="item.icon"
|
||||||
:is-active="activeParentRouteName === item.routeName"
|
:active-route-name="activeParentRouteName"
|
||||||
:is-mini="app.menu.collapsed"
|
:is-mini="app.menu.collapsed"
|
||||||
/>
|
/>
|
||||||
</n-scrollbar>
|
</n-scrollbar>
|
||||||
@ -25,17 +24,18 @@
|
|||||||
class="relative h-full transition-width duration-300 ease-in-out"
|
class="relative h-full transition-width duration-300 ease-in-out"
|
||||||
:style="{ width: app.menu.fixedMix ? theme.menuStyle.width + 'px' : '0px' }"
|
:style="{ width: app.menu.fixedMix ? theme.menuStyle.width + 'px' : '0px' }"
|
||||||
>
|
>
|
||||||
<mix-menu-drawer :hover-route="hoverRoute" @reset-hover-route="resetHoverRoute" />
|
<mix-menu-drawer :active-route-name="activeParentRouteName" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue';
|
import { computed } from 'vue';
|
||||||
import type { VNodeChild } from 'vue';
|
import type { VNodeChild } from 'vue';
|
||||||
import { NScrollbar } from 'naive-ui';
|
import { NScrollbar } from 'naive-ui';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useAppStore, useThemeStore } from '@/store';
|
import { useAppStore, useThemeStore } from '@/store';
|
||||||
|
import { useVerticalMixSiderContext } from '@/context';
|
||||||
import { menus } from '@/router';
|
import { menus } from '@/router';
|
||||||
import { MixMenu, MixMenuCollapse, MixMenuDrawer } from './components';
|
import { MixMenu, MixMenuCollapse, MixMenuDrawer } from './components';
|
||||||
import { GlobalLogo } from '../../../common';
|
import { GlobalLogo } from '../../../common';
|
||||||
@ -43,6 +43,7 @@ import { GlobalLogo } from '../../../common';
|
|||||||
const theme = useThemeStore();
|
const theme = useThemeStore();
|
||||||
const app = useAppStore();
|
const app = useAppStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const { useVerticalMixSiderProvide } = useVerticalMixSiderContext();
|
||||||
|
|
||||||
const mixMenuWidth = computed(() => `${theme.menuStyle.mixWidth}px`);
|
const mixMenuWidth = computed(() => `${theme.menuStyle.mixWidth}px`);
|
||||||
const mixMenuCollapsedWidth = computed(() => `${theme.menuStyle.mixCollapsedWidth}px`);
|
const mixMenuCollapsedWidth = computed(() => `${theme.menuStyle.mixCollapsedWidth}px`);
|
||||||
@ -68,10 +69,7 @@ const activeParentRouteName = computed(() => {
|
|||||||
return name;
|
return name;
|
||||||
});
|
});
|
||||||
|
|
||||||
const hoverRoute = ref('');
|
useVerticalMixSiderProvide();
|
||||||
function resetHoverRoute() {
|
|
||||||
hoverRoute.value = '';
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.mix-menu-width {
|
.mix-menu-width {
|
||||||
|
@ -43,7 +43,7 @@ const notification = useNotification();
|
|||||||
|
|
||||||
const formRef = ref<(HTMLElement & FormInst) | null>(null);
|
const formRef = ref<(HTMLElement & FormInst) | null>(null);
|
||||||
const model = reactive({
|
const model = reactive({
|
||||||
phone: '15170283876',
|
phone: '15100000000',
|
||||||
pwd: '123456'
|
pwd: '123456'
|
||||||
});
|
});
|
||||||
const rules = {
|
const rules = {
|
||||||
|
Loading…
Reference in New Issue
Block a user