Template
1
0
mirror of https://github.com/un-pany/v3-admin-vite.git synced 2025-04-21 11:29:20 +08:00

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

View File

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

View File

@ -8,12 +8,14 @@ import path from "path-browserify"
interface Props {
item: RouteRecordRaw
isCollapse?: boolean
isTop?: boolean
isFirstLevel?: boolean
basePath?: string
}
const props = withDefaults(defineProps<Props>(), {
isCollapse: false,
isTop: false,
isFirstLevel: true,
basePath: ""
})
@ -58,7 +60,10 @@ const resolvePath = (routePath: string) => {
</script>
<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">
<SidebarItemLink v-if="theOnlyOneChild.meta" :to="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 Logo from "../Logo/index.vue"
import { getCssVariableValue } from "@/utils"
import { DeviceEnum } from "@/constants/app-key"
const v3SidebarMenuBgColor = getCssVariableValue("--v3-sidebar-menu-bg-color")
const v3SidebarMenuTextColor = getCssVariableValue("--v3-sidebar-menu-text-color")
@ -18,6 +19,7 @@ const appStore = useAppStore()
const permissionStore = usePermissionStore()
const settingsStore = useSettingsStore()
const { sidebar, device } = storeToRefs(appStore)
const { layoutMode, showLogo } = storeToRefs(settingsStore)
const activeMenu = computed(() => {
@ -28,12 +30,25 @@ const activeMenu = computed(() => {
return activeMenu ? activeMenu : path
})
const isCollapse = computed(() => !appStore.sidebar.opened)
const isCollapse = computed(() => !sidebar.value.opened)
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 backgroundColor = computed(() => (isLeft.value ? v3SidebarMenuBgColor : undefined))
const textColor = computed(() => (isLeft.value ? v3SidebarMenuTextColor : 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>
<template>
@ -42,13 +57,13 @@ const activeTextColor = computed(() => (isLeft.value ? v3SidebarMenuActiveTextCo
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
:collapse="isCollapse && !isTop"
:background-color="backgroundColor"
:text-color="textColor"
:active-text-color="activeTextColor"
:unique-opened="true"
:collapse-transition="false"
mode="vertical"
:mode="isTop && !isMobile ? 'horizontal' : 'vertical'"
>
<SidebarItem
v-for="route in permissionStore.routes"
@ -56,6 +71,7 @@ const activeTextColor = computed(() => (isLeft.value ? v3SidebarMenuActiveTextCo
:item="route"
:base-path="route.path"
:is-collapse="isCollapse"
:is-top="isTop"
/>
</el-menu>
</el-scrollbar>
@ -69,7 +85,7 @@ const activeTextColor = computed(() => (isLeft.value ? v3SidebarMenuActiveTextCo
position: absolute;
top: 0;
left: 0;
width: 2px;
width: v-bind(tipLineWidth);
height: 100%;
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-sub-menu__title),
:deep(.el-sub-menu .el-menu-item) {
height: var(--v3-sidebar-menu-item-height);
line-height: var(--v3-sidebar-menu-item-height);
:deep(.el-sub-menu .el-menu-item),
:deep(.el-menu--horizontal .el-menu-item) {
height: v-bind(sidebarMenuItemHeight);
line-height: v-bind(sidebarMenuItemHeight);
&.is-active,
&:hover {
background-color: var(--v3-sidebar-menu-hover-bg-color);
background-color: v-bind(sidebarMenuHoverBgColor);
}
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) {
&.is-active {
@include tip-line;

View File

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

View File

@ -16,5 +16,12 @@
.el-sub-menu__title {
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 {
background-color: lighten($theme-bg-color, 4%) !important;
}
.el-sub-menu {
&.is-active {
.el-sub-menu__title {
color: $active-font-color !important;
}
}
}
}
// Header