wip: 顶部布局模式开发中

This commit is contained in:
pany 2023-07-18 11:14:07 +08:00
parent d5afb53c25
commit 8a0b8c5b3a
8 changed files with 158 additions and 15 deletions

77
src/layouts/TopMode.vue Normal file
View File

@ -0,0 +1,77 @@
<script lang="ts" setup>
import { storeToRefs } from "pinia"
import { useSettingsStore } from "@/store/modules/settings"
import { AppMain, NavigationBar, TagsView, Logo } from "./components"
const settingsStore = useSettingsStore()
const { showTagsView, showLogo } = storeToRefs(settingsStore)
</script>
<template>
<div class="app-wrapper">
<!-- 头部导航栏和标签栏 -->
<div class="fixed-header layout-header">
<div class="content">
<Logo v-if="showLogo" :collapse="false" class="logo" />
<NavigationBar class="navigation-bar" />
</div>
<TagsView v-show="showTagsView" />
</div>
<!-- 主容器 -->
<div :class="{ hasTagsView: showTagsView }" class="main-container">
<!-- 页面主体内容 -->
<AppMain class="app-main" />
</div>
</div>
</template>
<style lang="scss" scoped>
@import "@/styles/mixins.scss";
$transition-time: 0.35s;
.app-wrapper {
@include clearfix;
width: 100%;
}
.fixed-header {
position: fixed;
top: 0;
z-index: 1002;
width: 100%;
.logo {
width: var(--v3-sidebar-width);
}
.content {
display: flex;
.navigation-bar {
flex: 1;
}
}
}
.layout-header {
box-shadow: var(--el-box-shadow-lighter);
}
.main-container {
min-height: 100%;
}
.app-main {
transition: padding-left $transition-time;
padding-top: var(--v3-navigationbar-height);
height: 100vh;
overflow: auto;
}
.hasTagsView {
.sidebar-container {
padding-top: var(--v3-header-height);
}
.app-main {
padding-top: var(--v3-header-height);
}
}
</style>

View File

@ -23,6 +23,12 @@ const bgCloor = computed(() => {
? getCssVariableValue("--v3-header-bg-color") ? getCssVariableValue("--v3-header-bg-color")
: getCssVariableValue("--v3-sidebar-menu-bg-color") : getCssVariableValue("--v3-sidebar-menu-bg-color")
}) })
const logoHeight = computed(() => {
return layoutMode.value !== "top"
? getCssVariableValue("--v3-header-height")
: getCssVariableValue("--v3-navigationbar-height")
})
</script> </script>
<template> <template>
@ -42,8 +48,8 @@ const bgCloor = computed(() => {
.layout-logo-container { .layout-logo-container {
position: relative; position: relative;
width: 100%; width: 100%;
height: var(--v3-header-height); height: v-bind(logoHeight);
line-height: var(--v3-header-height); line-height: v-bind(logoHeight);
background-color: v-bind(bgCloor); background-color: v-bind(bgCloor);
text-align: center; text-align: center;
overflow: hidden; overflow: hidden;

View File

@ -1,4 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from "vue"
import { useRouter } from "vue-router" import { useRouter } from "vue-router"
import { storeToRefs } from "pinia" import { storeToRefs } from "pinia"
import { useAppStore } from "@/store/modules/app" import { useAppStore } from "@/store/modules/app"
@ -7,22 +8,28 @@ import { useUserStore } from "@/store/modules/user"
import { UserFilled } from "@element-plus/icons-vue" import { UserFilled } from "@element-plus/icons-vue"
import Hamburger from "../Hamburger/index.vue" import Hamburger from "../Hamburger/index.vue"
import Breadcrumb from "../Breadcrumb/index.vue" import Breadcrumb from "../Breadcrumb/index.vue"
import Sidebar from "../Sidebar/index.vue"
import ThemeSwitch from "@/components/ThemeSwitch/index.vue" import ThemeSwitch from "@/components/ThemeSwitch/index.vue"
import Screenfull from "@/components/Screenfull/index.vue" import Screenfull from "@/components/Screenfull/index.vue"
import Notify from "@/components/Notify/index.vue" import Notify from "@/components/Notify/index.vue"
import { DeviceEnum } from "@/constants/app-key"
const router = useRouter() const router = useRouter()
const appStore = useAppStore() const appStore = useAppStore()
const settingsStore = useSettingsStore() const settingsStore = useSettingsStore()
const userStore = useUserStore() const userStore = useUserStore()
const { sidebar } = storeToRefs(appStore) const { sidebar, device } = storeToRefs(appStore)
const { showNotify, showThemeSwitch, showScreenfull } = storeToRefs(settingsStore) const { layoutMode, showNotify, showThemeSwitch, showScreenfull } = storeToRefs(settingsStore)
const isTop = computed(() => layoutMode.value === "top")
const isMobile = computed(() => device.value === DeviceEnum.Mobile)
/** 切换侧边栏 */ /** 切换侧边栏 */
const toggleSidebar = () => { const toggleSidebar = () => {
appStore.toggleSidebar(false) appStore.toggleSidebar(false)
} }
/** 登出 */ /** 登出 */
const logout = () => { const logout = () => {
userStore.logout() userStore.logout()
@ -32,8 +39,9 @@ const logout = () => {
<template> <template>
<div class="navigation-bar"> <div class="navigation-bar">
<Hamburger :is-active="sidebar.opened" class="hamburger" @toggle-click="toggleSidebar" /> <Hamburger v-if="!isTop || isMobile" :is-active="sidebar.opened" class="hamburger" @toggle-click="toggleSidebar" />
<Breadcrumb class="breadcrumb" /> <Breadcrumb v-if="!isTop || isMobile" class="breadcrumb" />
<Sidebar v-if="isTop && !isMobile" class="sidebar" />
<div class="right-menu"> <div class="right-menu">
<Screenfull v-if="showScreenfull" class="right-menu-item" /> <Screenfull v-if="showScreenfull" class="right-menu-item" />
<ThemeSwitch v-if="showThemeSwitch" class="right-menu-item" /> <ThemeSwitch v-if="showThemeSwitch" class="right-menu-item" />
@ -84,6 +92,11 @@ const logout = () => {
display: none; display: none;
} }
} }
.sidebar {
float: left;
width: 992px;
height: 100%;
}
.right-menu { .right-menu {
float: right; float: right;
margin-right: 10px; margin-right: 10px;

View File

@ -8,12 +8,14 @@ import path from "path-browserify"
interface Props { interface Props {
item: RouteRecordRaw item: RouteRecordRaw
isCollapse?: boolean isCollapse?: boolean
isTop?: boolean
isFirstLevel?: boolean isFirstLevel?: boolean
basePath?: string basePath?: string
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
isCollapse: false, isCollapse: false,
isTop: false,
isFirstLevel: true, isFirstLevel: true,
basePath: "" basePath: ""
}) })
@ -58,7 +60,10 @@ const resolvePath = (routePath: string) => {
</script> </script>
<template> <template>
<div v-if="!props.item.meta?.hidden" :class="{ 'simple-mode': props.isCollapse, 'first-level': props.isFirstLevel }"> <div
v-if="!props.item.meta?.hidden"
:class="{ 'simple-mode': props.isCollapse && !isTop, 'first-level': props.isFirstLevel }"
>
<template v-if="!alwaysShowRootMenu && theOnlyOneChild && !theOnlyOneChild.children"> <template v-if="!alwaysShowRootMenu && theOnlyOneChild && !theOnlyOneChild.children">
<SidebarItemLink v-if="theOnlyOneChild.meta" :to="resolvePath(theOnlyOneChild.path)"> <SidebarItemLink v-if="theOnlyOneChild.meta" :to="resolvePath(theOnlyOneChild.path)">
<el-menu-item :index="resolvePath(theOnlyOneChild.path)"> <el-menu-item :index="resolvePath(theOnlyOneChild.path)">

View File

@ -8,6 +8,7 @@ import { useSettingsStore } from "@/store/modules/settings"
import SidebarItem from "./SidebarItem.vue" import SidebarItem from "./SidebarItem.vue"
import Logo from "../Logo/index.vue" import Logo from "../Logo/index.vue"
import { getCssVariableValue } from "@/utils" import { getCssVariableValue } from "@/utils"
import { DeviceEnum } from "@/constants/app-key"
const v3SidebarMenuBgColor = getCssVariableValue("--v3-sidebar-menu-bg-color") const v3SidebarMenuBgColor = getCssVariableValue("--v3-sidebar-menu-bg-color")
const v3SidebarMenuTextColor = getCssVariableValue("--v3-sidebar-menu-text-color") const v3SidebarMenuTextColor = getCssVariableValue("--v3-sidebar-menu-text-color")
@ -18,6 +19,7 @@ const appStore = useAppStore()
const permissionStore = usePermissionStore() const permissionStore = usePermissionStore()
const settingsStore = useSettingsStore() const settingsStore = useSettingsStore()
const { sidebar, device } = storeToRefs(appStore)
const { layoutMode, showLogo } = storeToRefs(settingsStore) const { layoutMode, showLogo } = storeToRefs(settingsStore)
const activeMenu = computed(() => { const activeMenu = computed(() => {
@ -28,12 +30,25 @@ const activeMenu = computed(() => {
return activeMenu ? activeMenu : path return activeMenu ? activeMenu : path
}) })
const isCollapse = computed(() => !appStore.sidebar.opened) const isCollapse = computed(() => !sidebar.value.opened)
const isLeft = computed(() => layoutMode.value === "left") const isLeft = computed(() => layoutMode.value === "left")
const isTop = computed(() => layoutMode.value === "top")
const isMobile = computed(() => device.value === DeviceEnum.Mobile)
const isLogo = computed(() => isLeft.value && showLogo.value) const isLogo = computed(() => isLeft.value && showLogo.value)
const backgroundColor = computed(() => (isLeft.value ? v3SidebarMenuBgColor : undefined)) const backgroundColor = computed(() => (isLeft.value ? v3SidebarMenuBgColor : undefined))
const textColor = computed(() => (isLeft.value ? v3SidebarMenuTextColor : undefined)) const textColor = computed(() => (isLeft.value ? v3SidebarMenuTextColor : undefined))
const activeTextColor = computed(() => (isLeft.value ? v3SidebarMenuActiveTextColor : undefined)) const activeTextColor = computed(() => (isLeft.value ? v3SidebarMenuActiveTextColor : undefined))
const sidebarMenuItemHeight = computed(() => {
return layoutMode.value !== "top"
? getCssVariableValue("--v3-sidebar-menu-item-height")
: getCssVariableValue("--v3-navigationbar-height")
})
const sidebarMenuHoverBgColor = computed(() => {
return layoutMode.value !== "top" ? getCssVariableValue("--v3-sidebar-menu-hover-bg-color") : "transparent"
})
const tipLineWidth = computed(() => {
return layoutMode.value !== "top" ? "2px" : "0px"
})
</script> </script>
<template> <template>
@ -42,13 +57,13 @@ const activeTextColor = computed(() => (isLeft.value ? v3SidebarMenuActiveTextCo
<el-scrollbar wrap-class="scrollbar-wrapper"> <el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu <el-menu
:default-active="activeMenu" :default-active="activeMenu"
:collapse="isCollapse" :collapse="isCollapse && !isTop"
:background-color="backgroundColor" :background-color="backgroundColor"
:text-color="textColor" :text-color="textColor"
:active-text-color="activeTextColor" :active-text-color="activeTextColor"
:unique-opened="true" :unique-opened="true"
:collapse-transition="false" :collapse-transition="false"
mode="vertical" :mode="isTop && !isMobile ? 'horizontal' : 'vertical'"
> >
<SidebarItem <SidebarItem
v-for="route in permissionStore.routes" v-for="route in permissionStore.routes"
@ -56,6 +71,7 @@ const activeTextColor = computed(() => (isLeft.value ? v3SidebarMenuActiveTextCo
:item="route" :item="route"
:base-path="route.path" :base-path="route.path"
:is-collapse="isCollapse" :is-collapse="isCollapse"
:is-top="isTop"
/> />
</el-menu> </el-menu>
</el-scrollbar> </el-scrollbar>
@ -69,7 +85,7 @@ const activeTextColor = computed(() => (isLeft.value ? v3SidebarMenuActiveTextCo
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
width: 2px; width: v-bind(tipLineWidth);
height: 100%; height: 100%;
background-color: var(--v3-sidebar-menu-tip-line-bg-color); background-color: var(--v3-sidebar-menu-tip-line-bg-color);
} }
@ -107,12 +123,13 @@ const activeTextColor = computed(() => (isLeft.value ? v3SidebarMenuActiveTextCo
:deep(.el-menu-item), :deep(.el-menu-item),
:deep(.el-sub-menu__title), :deep(.el-sub-menu__title),
:deep(.el-sub-menu .el-menu-item) { :deep(.el-sub-menu .el-menu-item),
height: var(--v3-sidebar-menu-item-height); :deep(.el-menu--horizontal .el-menu-item) {
line-height: var(--v3-sidebar-menu-item-height); height: v-bind(sidebarMenuItemHeight);
line-height: v-bind(sidebarMenuItemHeight);
&.is-active, &.is-active,
&:hover { &:hover {
background-color: var(--v3-sidebar-menu-hover-bg-color); background-color: v-bind(sidebarMenuHoverBgColor);
} }
display: block; display: block;
* { * {
@ -120,6 +137,14 @@ const activeTextColor = computed(() => (isLeft.value ? v3SidebarMenuActiveTextCo
} }
} }
:deep(.el-sub-menu) {
&.is-active {
.el-sub-menu__title {
color: v-bind(activeTextColor) !important;
}
}
}
:deep(.el-menu-item) { :deep(.el-menu-item) {
&.is-active { &.is-active {
@include tip-line; @include tip-line;

View File

@ -5,6 +5,7 @@ import { useAppStore } from "@/store/modules/app"
import { useSettingsStore } from "@/store/modules/settings" import { useSettingsStore } from "@/store/modules/settings"
import useResize from "./hooks/useResize" import useResize from "./hooks/useResize"
import LeftMode from "./LeftMode.vue" import LeftMode from "./LeftMode.vue"
import TopMode from "./TopMode.vue"
import LeftTopMode from "./LeftTopMode.vue" import LeftTopMode from "./LeftTopMode.vue"
import { Settings, RightPanel } from "./components" import { Settings, RightPanel } from "./components"
import { DeviceEnum } from "@/constants/app-key" import { DeviceEnum } from "@/constants/app-key"
@ -40,6 +41,8 @@ watchEffect(() => {
<div :class="classes"> <div :class="classes">
<!-- 左侧模式 --> <!-- 左侧模式 -->
<LeftMode v-if="layoutMode === 'left' || appStore.device === DeviceEnum.Mobile" /> <LeftMode v-if="layoutMode === 'left' || appStore.device === DeviceEnum.Mobile" />
<!-- 顶部模式 -->
<TopMode v-else-if="layoutMode === 'top'" />
<!-- 混合模式 --> <!-- 混合模式 -->
<LeftTopMode v-else-if="layoutMode === 'left-top'" /> <LeftTopMode v-else-if="layoutMode === 'left-top'" />
<!-- 右侧设置面板 --> <!-- 右侧设置面板 -->

View File

@ -16,5 +16,12 @@
.el-sub-menu__title { .el-sub-menu__title {
background-color: lighten($theme-bg-color, 4%) !important; background-color: lighten($theme-bg-color, 4%) !important;
} }
.el-sub-menu {
&.is-active {
.el-sub-menu__title {
color: $active-font-color !important;
}
}
}
} }
} }

View File

@ -30,6 +30,13 @@
.el-sub-menu__title { .el-sub-menu__title {
background-color: lighten($theme-bg-color, 4%) !important; background-color: lighten($theme-bg-color, 4%) !important;
} }
.el-sub-menu {
&.is-active {
.el-sub-menu__title {
color: $active-font-color !important;
}
}
}
} }
// Header // Header