fix(projects): 修复vertical-mix导航模式的二级菜单显示问题

This commit is contained in:
Soybean 2021-09-29 08:49:28 +08:00
parent c7e6b86a5b
commit 6f286e6747
10 changed files with 1443 additions and 979 deletions

View File

@ -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": {

File diff suppressed because it is too large Load Diff

View File

@ -27,7 +27,6 @@ export default function useReloadContext() {
} }
return { return {
context,
useReloadProvide, useReloadProvide,
useReloadInject useReloadInject
}; };

View File

@ -1 +1,3 @@
export { setupAppContext, useReloadInject } from './app'; export { setupAppContext, useReloadInject } from './app';
export { useVerticalMixSiderContext } from './part';

View File

@ -0,0 +1,3 @@
import useVerticalMixSiderContext from './useVerticalMixSiderContext';
export { useVerticalMixSiderContext };

View 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
};
}

View File

@ -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>

View File

@ -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 {

View File

@ -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 {

View File

@ -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 = {