wip: 顶部布局模式开发中
This commit is contained in:
parent
d5afb53c25
commit
8a0b8c5b3a
77
src/layouts/TopMode.vue
Normal file
77
src/layouts/TopMode.vue
Normal 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>
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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)">
|
||||||
|
@ -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;
|
||||||
|
@ -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'" />
|
||||||
<!-- 右侧设置面板 -->
|
<!-- 右侧设置面板 -->
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user