feat: 导航混合

Former-commit-id: 5942884a80
This commit is contained in:
april 2023-08-14 18:20:51 +08:00
parent c471f3c68b
commit 52a5d064a5
6 changed files with 287 additions and 47 deletions

View File

@ -0,0 +1,41 @@
<script lang="ts" setup>
import { useRoute } from "vue-router";
import SidebarItem from "./SidebarItem.vue";
import { useSettingsStore } from "@/store/modules/settings";
import { useAppStore } from "@/store/modules/app";
import variables from "@/styles/variables.module.scss";
const settingsStore = useSettingsStore();
const appStore = useAppStore();
const currRoute = useRoute();
const layout = computed(() => settingsStore.layout);
const props = defineProps({
menuList: {
required: true,
default: () => {
return [];
},
type: Array<any>,
},
});
</script>
<template>
<el-menu
:default-active="layout === 'top' ? '-' : currRoute.path"
:collapse="!appStore.sidebar.opened"
:background-color="variables.menuBg"
:text-color="variables.menuText"
:active-text-color="variables.menuActiveText"
:unique-opened="false"
:collapse-transition="false"
:mode="layout === 'top' ? 'horizontal' : 'vertical'"
>
<sidebar-item
v-for="route in menuList"
:key="route.path"
:item="route"
:base-path="route.path"
:is-collapse="!appStore.sidebar.opened"
/>
</el-menu>
</template>

View File

@ -0,0 +1,52 @@
<script lang="ts" setup>
import { usePermissionStore } from "@/store/modules/permission";
import variables from "@/styles/variables.module.scss";
import { useAppStore } from "@/store/modules/app";
import { translateRouteTitleI18n } from "@/utils/i18n";
const appStore = useAppStore();
const tPath = ref();
const selectMenu = (index: string) => {
appStore.changeTopActive(index);
};
const permissionStore = usePermissionStore();
const topMenu = ref<any[]>([]);
onMounted(() => {
topMenu.value = permissionStore.routes.filter((el) => !el.meta?.hidden);
});
</script>
<template>
<el-scrollbar>
<el-menu
mode="horizontal"
class="el-menu-demo"
:default-active="tPath"
:background-color="variables.menuBg"
:text-color="variables.menuText"
:active-text-color="variables.menuActiveText"
@select="selectMenu"
>
<el-menu-item
v-for="route in topMenu"
:key="route.path"
:index="route.path"
>
<template #title>
<svg-icon
v-if="route.meta && route.meta.icon"
:icon-class="route.meta.icon"
/>
<span v-if="route.meta && route.meta.title">{{
translateRouteTitleI18n(route.meta.title)
}}</span>
</template>
</el-menu-item>
<!-- <sidebar-item
v-for="route in topMenu"
:key="route.path"
:item="route"
:base-path="route.path"
:is-collapse="false"
/> -->
</el-menu>
</el-scrollbar>
</template>

View File

@ -1,18 +1,15 @@
<script setup lang="ts">
import { useRoute } from "vue-router";
import SidebarItem from "./SidebarItem.vue";
import TopMenu from "./TopMenu.vue";
import LeftMenu from "./LeftMenu.vue";
import Logo from "./Logo.vue";
import { useSettingsStore } from "@/store/modules/settings";
import { usePermissionStore } from "@/store/modules/permission";
import { useAppStore } from "@/store/modules/app";
import { storeToRefs } from "pinia";
import variables from "@/styles/variables.module.scss";
const settingsStore = useSettingsStore();
const permissionStore = usePermissionStore();
const appStore = useAppStore();
const currRoute = useRoute();
const { sidebarLogo } = storeToRefs(settingsStore);
const layout = computed(() => settingsStore.layout);
const showContent = ref(true);
@ -28,41 +25,86 @@ watch(
</script>
<template>
<div :class="{ 'has-logo': sidebarLogo }" class="menu-wrap">
<div
:class="{ 'has-logo': sidebarLogo }"
class="menu-wrap"
v-if="layout !== 'mix'"
>
<logo v-if="sidebarLogo" :collapse="!appStore.sidebar.opened" />
<el-scrollbar v-if="showContent">
<el-menu
:default-active="layout === 'top' ? '-' : currRoute.path"
:collapse="!appStore.sidebar.opened"
:background-color="variables.menuBg"
:text-color="variables.menuText"
:active-text-color="variables.menuActiveText"
:unique-opened="false"
:collapse-transition="false"
:mode="layout === 'top' ? 'horizontal' : 'vertical'"
>
<sidebar-item
v-for="route in permissionStore.routes"
:key="route.path"
:item="route"
:base-path="route.path"
:is-collapse="!appStore.sidebar.opened"
/>
</el-menu>
<LeftMenu :menu-list="permissionStore.routes" />
</el-scrollbar>
<NavRight v-if="layout === 'top'" />
</div>
<template v-else>
<div :class="{ 'has-logo': sidebarLogo }" class="menu-wrap">
<div class="header">
<logo v-if="sidebarLogo" :collapse="!appStore.sidebar.opened" />
<TopMenu />
<NavRight />
</div>
</div>
</template>
</template>
<style lang="scss" scoped>
:deep(.setting-container) {
.setting-item {
color: #fff;
.svg-icon {
margin-right: 0px;
margin-right: 0;
}
&:hover {
color: var(--el-color-primary);
}
}
}
.isMix {
.menu-wrap {
width: 100% !important;
height: 50px;
background-color: $menuBg;
:deep(.header) {
display: flex;
width: 100%;
//
--el-menu-item-height: 50px;
.logo-wrap {
width: 210px;
}
.el-menu {
background-color: $menuBg;
.el-menu-item {
color: $menuText;
}
}
.el-scrollbar {
flex: 1;
min-width: 0;
height: 50px;
}
}
}
.left-menu {
display: inline-block;
width: 210px;
background-color: $menuBg;
:deep(.el-menu) {
background-color: $menuBg;
.el-menu-item {
color: $menuText;
}
}
}
}
</style>

View File

@ -1,13 +1,15 @@
<script setup lang="ts">
import Main from "./main.vue";
import { computed, watchEffect } from "vue";
import { useWindowSize } from "@vueuse/core";
import { AppMain, Navbar, Settings, TagsView } from "./components/index";
import Sidebar from "./components/Sidebar/index.vue";
import RightPanel from "@/components/RightPanel/index.vue";
import LeftMenu from "./components/Sidebar/LeftMenu.vue";
import { useAppStore } from "@/store/modules/app";
import { useSettingsStore } from "@/store/modules/settings";
import { usePermissionStore } from "@/store/modules/permission";
import { useRouter } from "vue-router";
const permissionStore = usePermissionStore();
const { width } = useWindowSize();
/**
@ -22,9 +24,31 @@ const WIDTH = 992;
const appStore = useAppStore();
const settingsStore = useSettingsStore();
const fixedHeader = computed(() => settingsStore.fixedHeader);
const showTagsView = computed(() => settingsStore.tagsView);
const showSettings = computed(() => settingsStore.showSettings);
const activeTopMenu = computed(() => {
return appStore.activeTopMenu;
});
//
const mixLeftMenu = ref<any[]>([]);
const router = useRouter();
watch(
() => activeTopMenu.value,
(newVal) => {
permissionStore.routes.forEach((item) => {
if (item.path === newVal) {
mixLeftMenu.value = item.children || [];
}
});
console.log(" mixLeftMenu.value ", mixLeftMenu.value);
// if (mixLeftMenu.value.length) {
// router.push({
// path: mixLeftMenu.value[0].path,
// });
// }
},
{
deep: true,
}
);
const layout = computed(() => settingsStore.layout);
const classObj = computed(() => ({
@ -33,6 +57,7 @@ const classObj = computed(() => ({
withoutAnimation: appStore.sidebar.withoutAnimation,
mobile: appStore.device === "mobile",
isTop: layout.value === "top",
isMix: layout.value === "mix",
}));
watchEffect(() => {
@ -66,21 +91,14 @@ function handleOutsideClick() {
></div>
<Sidebar class="sidebar-container" />
<div :class="{ hasTagsView: showTagsView }" class="main-container">
<div :class="{ 'fixed-header': fixedHeader }">
<navbar v-if="layout !== 'top'" />
<tags-view v-if="showTagsView" />
<template v-if="layout === 'mix'">
<div class="mix-wrap">
<!-- :menu-list="mixLeftMenu -->
<LeftMenu :menu-list="permissionStore.routes" />
<Main />
</div>
<!--主页面-->
<app-main />
<!-- 设置面板 -->
<RightPanel v-if="showSettings">
<settings />
</RightPanel>
</div>
</template>
<Main v-else />
</div>
</template>
@ -155,4 +173,26 @@ function handleOutsideClick() {
//
--el-menu-item-height: 50px;
}
.isMix {
:deep(.main-container) {
display: inline-block;
width: calc(100% - 210px);
margin-left: 0;
}
.mix-wrap {
display: flex;
padding-top: 50px;
.el-menu {
width: 210px;
}
.main-container {
flex: 1;
min-width: 0;
}
}
}
</style>

57
src/layout/main.vue Normal file
View File

@ -0,0 +1,57 @@
<script lang="ts" setup>
import { computed, watchEffect } from "vue";
import { useWindowSize } from "@vueuse/core";
import { AppMain, Navbar, Settings, TagsView } from "./components/index";
import RightPanel from "@/components/RightPanel/index.vue";
import { useAppStore } from "@/store/modules/app";
import { useSettingsStore } from "@/store/modules/settings";
const { width } = useWindowSize();
/**
* 响应式布局容器固定宽度
*
* 大屏>=1200px
* 中屏>=992px
* 小屏>=768px
*/
const WIDTH = 992;
const appStore = useAppStore();
const settingsStore = useSettingsStore();
const fixedHeader = computed(() => settingsStore.fixedHeader);
const showTagsView = computed(() => settingsStore.tagsView);
const showSettings = computed(() => settingsStore.showSettings);
const layout = computed(() => settingsStore.layout);
watchEffect(() => {
if (width.value < WIDTH) {
appStore.toggleDevice("mobile");
appStore.closeSideBar(true);
} else {
appStore.toggleDevice("desktop");
if (width.value >= 1200) {
//
appStore.openSideBar(true);
} else {
appStore.closeSideBar(true);
}
}
});
</script>
<template>
<div :class="{ hasTagsView: showTagsView }" class="main-container">
<div :class="{ 'fixed-header': fixedHeader }">
<navbar v-if="layout === 'left'" />
<tags-view v-if="showTagsView" />
</div>
<!--主页面-->
<app-main />
<!-- 设置面板 -->
<RightPanel v-if="showSettings">
<settings />
</RightPanel>
</div>
</template>

View File

@ -14,11 +14,12 @@ export const useAppStore = defineStore("app", () => {
const language = useStorage("language", defaultSettings.language);
const sidebarStatus = useStorage("sidebarStatus", "closed");
const sidebar = reactive({
opened: sidebarStatus.value !== "closed",
withoutAnimation: false,
});
const activeTopMenu = useStorage("activeTop", "");
/**
*
*/
@ -68,18 +69,25 @@ export const useAppStore = defineStore("app", () => {
function changeLanguage(val: string) {
language.value = val;
}
/**
*
*/
function changeTopActive(val: string) {
activeTopMenu.value = val;
}
return {
device,
sidebar,
language,
locale,
size,
activeTopMenu,
toggleDevice,
changeSize,
changeLanguage,
toggleSidebar,
closeSideBar,
openSideBar,
changeTopActive,
};
});