mirror of
https://github.com/un-pany/v3-admin-vite.git
synced 2025-04-20 19:09:21 +08:00
feat: 新增混合布局模式
This commit is contained in:
parent
c3ad3c0ce1
commit
442ae06c47
@ -1,13 +1,15 @@
|
||||
import { getConfigLayout } from "@/utils/cache/local-storage"
|
||||
|
||||
/** 布局配置 */
|
||||
/** 项目配置 */
|
||||
export interface LayoutSettings {
|
||||
/** 是否显示 Settings Panel */
|
||||
showSettings: boolean
|
||||
/** 布局模式 */
|
||||
layoutMode: "left" | "top" | "left-top"
|
||||
/** 是否显示标签栏 */
|
||||
showTagsView: boolean
|
||||
/** 是否显示侧边栏 Logo */
|
||||
showSidebarLogo: boolean
|
||||
/** 是否显示 Logo */
|
||||
showLogo: boolean
|
||||
/** 是否固定 Header */
|
||||
fixedHeader: boolean
|
||||
/** 是否显示消息通知 */
|
||||
@ -25,10 +27,11 @@ export interface LayoutSettings {
|
||||
}
|
||||
|
||||
export const layoutSettings: LayoutSettings = getConfigLayout() ?? {
|
||||
layoutMode: "left",
|
||||
showSettings: true,
|
||||
showTagsView: true,
|
||||
fixedHeader: true,
|
||||
showSidebarLogo: true,
|
||||
showLogo: true,
|
||||
showNotify: true,
|
||||
showThemeSwitch: true,
|
||||
showScreenfull: true,
|
||||
|
165
src/layout/LeftMode.vue
Normal file
165
src/layout/LeftMode.vue
Normal file
@ -0,0 +1,165 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue"
|
||||
import { storeToRefs } from "pinia"
|
||||
import { useAppStore } from "@/store/modules/app"
|
||||
import { useSettingsStore } from "@/store/modules/settings"
|
||||
import { AppMain, NavigationBar, Sidebar, TagsView } from "./components"
|
||||
import { DeviceEnum } from "@/constants/app-key"
|
||||
|
||||
const appStore = useAppStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
|
||||
const { showTagsView, fixedHeader } = storeToRefs(settingsStore)
|
||||
|
||||
/** 定义计算属性 layoutClasses,用于控制布局的类名 */
|
||||
const layoutClasses = computed(() => {
|
||||
return {
|
||||
hideSidebar: !appStore.sidebar.opened,
|
||||
openSidebar: appStore.sidebar.opened,
|
||||
withoutAnimation: appStore.sidebar.withoutAnimation,
|
||||
mobile: appStore.device === DeviceEnum.Mobile
|
||||
}
|
||||
})
|
||||
|
||||
/** 用于处理点击 mobile 端侧边栏遮罩层的事件 */
|
||||
const handleClickOutside = () => {
|
||||
appStore.closeSidebar(false)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="layoutClasses" class="app-wrapper">
|
||||
<!-- mobile 端侧边栏遮罩层 -->
|
||||
<div v-if="layoutClasses.mobile && layoutClasses.openSidebar" class="drawer-bg" @click="handleClickOutside" />
|
||||
<!-- 左侧边栏 -->
|
||||
<Sidebar class="sidebar-container" />
|
||||
<!-- 主容器 -->
|
||||
<div :class="{ hasTagsView: showTagsView }" class="main-container">
|
||||
<!-- 头部导航栏和标签栏 -->
|
||||
<div :class="{ 'fixed-header': fixedHeader }" class="layout-header">
|
||||
<NavigationBar />
|
||||
<TagsView v-show="showTagsView" />
|
||||
</div>
|
||||
<!-- 页面主体内容 -->
|
||||
<AppMain class="app-main" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/styles/mixins.scss";
|
||||
$transition-time: 0.35s;
|
||||
|
||||
.app-wrapper {
|
||||
@include clearfix;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.drawer-bg {
|
||||
background-color: #000;
|
||||
opacity: 0.3;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.sidebar-container {
|
||||
transition: width $transition-time;
|
||||
width: var(--v3-sidebar-width) !important;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1001;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
min-height: 100%;
|
||||
transition: margin-left $transition-time;
|
||||
margin-left: var(--v3-sidebar-width);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.fixed-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
width: calc(100% - var(--v3-sidebar-width));
|
||||
transition: width $transition-time;
|
||||
}
|
||||
|
||||
.layout-header {
|
||||
box-shadow: var(--el-box-shadow-lighter);
|
||||
}
|
||||
|
||||
.app-main {
|
||||
min-height: calc(100vh - var(--v3-navigationbar-height));
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fixed-header + .app-main {
|
||||
padding-top: var(--v3-navigationbar-height);
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.hasTagsView {
|
||||
.app-main {
|
||||
min-height: calc(100vh - var(--v3-header-height));
|
||||
}
|
||||
.fixed-header + .app-main {
|
||||
padding-top: var(--v3-header-height);
|
||||
}
|
||||
}
|
||||
|
||||
.hideSidebar {
|
||||
.sidebar-container {
|
||||
width: var(--v3-sidebar-hide-width) !important;
|
||||
}
|
||||
.main-container {
|
||||
margin-left: var(--v3-sidebar-hide-width);
|
||||
}
|
||||
.fixed-header {
|
||||
width: calc(100% - var(--v3-sidebar-hide-width));
|
||||
}
|
||||
}
|
||||
|
||||
// 适配 mobile 端
|
||||
.mobile {
|
||||
.sidebar-container {
|
||||
transition: transform $transition-time;
|
||||
width: var(--v3-sidebar-width) !important;
|
||||
}
|
||||
.main-container {
|
||||
margin-left: 0px;
|
||||
}
|
||||
.fixed-header {
|
||||
width: 100%;
|
||||
}
|
||||
&.openSidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
&.hideSidebar {
|
||||
.sidebar-container {
|
||||
pointer-events: none;
|
||||
transition-duration: 0.3s;
|
||||
transform: translate3d(calc(0px - var(--v3-sidebar-width)), 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.withoutAnimation {
|
||||
.sidebar-container,
|
||||
.main-container {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
</style>
|
108
src/layout/LeftTopMode.vue
Normal file
108
src/layout/LeftTopMode.vue
Normal file
@ -0,0 +1,108 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue"
|
||||
import { storeToRefs } from "pinia"
|
||||
import { useAppStore } from "@/store/modules/app"
|
||||
import { useSettingsStore } from "@/store/modules/settings"
|
||||
import { AppMain, NavigationBar, Sidebar, TagsView, Logo } from "./components"
|
||||
|
||||
const appStore = useAppStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
|
||||
const { showTagsView, showLogo } = storeToRefs(settingsStore)
|
||||
|
||||
/** 定义计算属性 layoutClasses,用于控制布局的类名 */
|
||||
const layoutClasses = computed(() => {
|
||||
return {
|
||||
hideSidebar: !appStore.sidebar.opened
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="layoutClasses" class="app-wrapper">
|
||||
<!-- 头部导航栏和标签栏 -->
|
||||
<div class="fixed-header layout-header">
|
||||
<Logo v-if="showLogo" :collapse="false" class="logo" />
|
||||
<div class="content">
|
||||
<NavigationBar />
|
||||
<TagsView v-show="showTagsView" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 主容器 -->
|
||||
<div :class="{ hasTagsView: showTagsView }" class="main-container">
|
||||
<!-- 左侧边栏 -->
|
||||
<Sidebar class="sidebar-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%;
|
||||
display: flex;
|
||||
.logo {
|
||||
width: var(--v3-sidebar-width);
|
||||
}
|
||||
.content {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.layout-header {
|
||||
box-shadow: var(--el-box-shadow-lighter);
|
||||
}
|
||||
|
||||
.main-container {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.sidebar-container {
|
||||
transition: width $transition-time;
|
||||
width: var(--v3-sidebar-width) !important;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
z-index: 1001;
|
||||
overflow: hidden;
|
||||
padding-top: var(--v3-navigationbar-height);
|
||||
}
|
||||
|
||||
.app-main {
|
||||
transition: padding-left $transition-time;
|
||||
padding-top: var(--v3-navigationbar-height);
|
||||
padding-left: var(--v3-sidebar-width);
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.hideSidebar {
|
||||
.sidebar-container {
|
||||
width: var(--v3-sidebar-hide-width) !important;
|
||||
}
|
||||
.app-main {
|
||||
padding-left: var(--v3-sidebar-hide-width);
|
||||
}
|
||||
}
|
||||
|
||||
.hasTagsView {
|
||||
.sidebar-container {
|
||||
padding-top: var(--v3-header-height);
|
||||
}
|
||||
.app-main {
|
||||
padding-top: var(--v3-header-height);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -30,10 +30,7 @@ const key = computed(() => {
|
||||
@import "@/styles/mixins.scss";
|
||||
|
||||
.app-main {
|
||||
min-height: calc(100vh - var(--v3-navigationbar-height));
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-color: var(--v3-body-bg-color);
|
||||
}
|
||||
|
||||
@ -42,19 +39,4 @@ const key = computed(() => {
|
||||
overflow: auto;
|
||||
@include scrollbar;
|
||||
}
|
||||
|
||||
.fixed-header + .app-main {
|
||||
padding-top: var(--v3-navigationbar-height);
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.hasTagsView {
|
||||
.app-main {
|
||||
min-height: calc(100vh - var(--v3-header-height));
|
||||
}
|
||||
.fixed-header + .app-main {
|
||||
padding-top: var(--v3-header-height);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
70
src/layout/components/Logo/index.vue
Normal file
70
src/layout/components/Logo/index.vue
Normal file
@ -0,0 +1,70 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue"
|
||||
import { storeToRefs } from "pinia"
|
||||
import { useSettingsStore } from "@/store/modules/settings"
|
||||
import { getCssVariableValue } from "@/utils"
|
||||
import logo from "@/assets/layout/logo.png?url"
|
||||
import logoText1 from "@/assets/layout/logo-text-1.png?url"
|
||||
import logoText2 from "@/assets/layout/logo-text-2.png?url"
|
||||
|
||||
interface Props {
|
||||
collapse?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
collapse: true
|
||||
})
|
||||
|
||||
const settingsStore = useSettingsStore()
|
||||
const { layoutMode } = storeToRefs(settingsStore)
|
||||
|
||||
const bgCloor = computed(() => {
|
||||
return layoutMode.value !== "left"
|
||||
? getCssVariableValue("--v3-header-bg-color")
|
||||
: getCssVariableValue("--v3-sidebar-menu-bg-color")
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="layout-logo-container" :class="{ collapse: props.collapse }">
|
||||
<transition name="layout-logo-fade">
|
||||
<router-link v-if="props.collapse" key="collapse" to="/">
|
||||
<img :src="logo" class="layout-logo" />
|
||||
</router-link>
|
||||
<router-link v-else key="expand" to="/">
|
||||
<img :src="layoutMode !== 'left' ? logoText2 : logoText1" class="layout-logo-text" />
|
||||
</router-link>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.layout-logo-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: var(--v3-header-height);
|
||||
line-height: var(--v3-header-height);
|
||||
background-color: v-bind(bgCloor);
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
.layout-logo {
|
||||
display: none;
|
||||
}
|
||||
.layout-logo-text {
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.collapse {
|
||||
.layout-logo {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
.layout-logo-text {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -5,8 +5,8 @@ import { useAppStore } from "@/store/modules/app"
|
||||
import { useSettingsStore } from "@/store/modules/settings"
|
||||
import { useUserStore } from "@/store/modules/user"
|
||||
import { UserFilled } from "@element-plus/icons-vue"
|
||||
import Breadcrumb from "../Breadcrumb/index.vue"
|
||||
import Hamburger from "../Hamburger/index.vue"
|
||||
import Breadcrumb from "../Breadcrumb/index.vue"
|
||||
import ThemeSwitch from "@/components/ThemeSwitch/index.vue"
|
||||
import Screenfull from "@/components/Screenfull/index.vue"
|
||||
import Notify from "@/components/Notify/index.vue"
|
||||
@ -68,7 +68,7 @@ const logout = () => {
|
||||
.navigation-bar {
|
||||
height: var(--v3-navigationbar-height);
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
background: var(--v3-header-bg-color);
|
||||
.hamburger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -1,4 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { watchEffect } from "vue"
|
||||
import { storeToRefs } from "pinia"
|
||||
import { useSettingsStore } from "@/store/modules/settings"
|
||||
import { removeConfigLayout } from "@/utils/cache/local-storage"
|
||||
@ -8,8 +9,9 @@ const settingsStore = useSettingsStore()
|
||||
|
||||
/** 使用 storeToRefs 将提取的属性保持其响应性 */
|
||||
const {
|
||||
layoutMode,
|
||||
showTagsView,
|
||||
showSidebarLogo,
|
||||
showLogo,
|
||||
fixedHeader,
|
||||
showNotify,
|
||||
showThemeSwitch,
|
||||
@ -22,7 +24,7 @@ const {
|
||||
/** 定义 switch 设置项 */
|
||||
const switchSettings = {
|
||||
显示标签栏: showTagsView,
|
||||
"显示侧边栏 Logo": showSidebarLogo,
|
||||
"显示 Logo": showLogo,
|
||||
"固定 Header": fixedHeader,
|
||||
显示消息通知: showNotify,
|
||||
显示切换主题按钮: showThemeSwitch,
|
||||
@ -32,6 +34,11 @@ const switchSettings = {
|
||||
显示色弱模式: showColorWeakness
|
||||
}
|
||||
|
||||
/** 非左侧模式时,Header 都是 fixed 布局 */
|
||||
watchEffect(() => {
|
||||
layoutMode.value !== "left" && (fixedHeader.value = true)
|
||||
})
|
||||
|
||||
/** 重置配置 */
|
||||
const reset = () => {
|
||||
removeConfigLayout()
|
||||
@ -41,10 +48,16 @@ const reset = () => {
|
||||
|
||||
<template>
|
||||
<div class="setting-container">
|
||||
<h4>系统布局配置</h4>
|
||||
<h4>布局配置</h4>
|
||||
<el-radio-group v-model="layoutMode">
|
||||
<el-radio label="left">左侧模式</el-radio>
|
||||
<el-radio label="top">顶部模式(开发中)</el-radio>
|
||||
<el-radio label="left-top">混合模式</el-radio>
|
||||
</el-radio-group>
|
||||
<h4>功能配置</h4>
|
||||
<div class="setting-item" v-for="(settingValue, settingName, index) in switchSettings" :key="index">
|
||||
<span class="setting-name">{{ settingName }}</span>
|
||||
<el-switch v-model="settingValue.value" />
|
||||
<el-switch v-model="settingValue.value" :disabled="layoutMode !== 'left' && settingName === '固定 Header'" />
|
||||
</div>
|
||||
<el-button type="danger" :icon="Refresh" @click="reset">重 置</el-button>
|
||||
</div>
|
||||
@ -57,7 +70,8 @@ const reset = () => {
|
||||
padding: 20px;
|
||||
.setting-item {
|
||||
font-size: 14px;
|
||||
padding: 6px 0;
|
||||
color: var(--el-text-color-regular);
|
||||
padding: 5px 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
@ -1,53 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
interface Props {
|
||||
collapse?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
collapse: true
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sidebar-logo-container" :class="{ collapse: props.collapse }">
|
||||
<transition name="sidebar-logo-fade">
|
||||
<router-link v-if="props.collapse" key="collapse" to="/">
|
||||
<img src="@/assets/layout/logo.png" class="sidebar-logo" />
|
||||
</router-link>
|
||||
<router-link v-else key="expand" to="/">
|
||||
<img src="@/assets/layout/logo-text-1.png" class="sidebar-logo-text" />
|
||||
</router-link>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sidebar-logo-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: var(--v3-header-height);
|
||||
line-height: var(--v3-header-height);
|
||||
background-color: var(--v3-sidebarlogo-bg-color);
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
.sidebar-logo {
|
||||
display: none;
|
||||
}
|
||||
.sidebar-logo-text {
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.collapse {
|
||||
.sidebar-logo {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
.sidebar-logo-text {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -6,7 +6,7 @@ import { useAppStore } from "@/store/modules/app"
|
||||
import { usePermissionStore } from "@/store/modules/permission"
|
||||
import { useSettingsStore } from "@/store/modules/settings"
|
||||
import SidebarItem from "./SidebarItem.vue"
|
||||
import SidebarLogo from "./SidebarLogo.vue"
|
||||
import Logo from "../Logo/index.vue"
|
||||
import { getCssVariableValue } from "@/utils"
|
||||
|
||||
const v3SidebarMenuBgColor = getCssVariableValue("--v3-sidebar-menu-bg-color")
|
||||
@ -18,7 +18,7 @@ const appStore = useAppStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
|
||||
const { showSidebarLogo } = storeToRefs(settingsStore)
|
||||
const { layoutMode, showLogo } = storeToRefs(settingsStore)
|
||||
|
||||
const activeMenu = computed(() => {
|
||||
const {
|
||||
@ -29,11 +29,12 @@ const activeMenu = computed(() => {
|
||||
})
|
||||
|
||||
const isCollapse = computed(() => !appStore.sidebar.opened)
|
||||
const isLogo = computed(() => layoutMode.value === "left" && showLogo.value)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="{ 'has-logo': showSidebarLogo }">
|
||||
<SidebarLogo v-if="showSidebarLogo" :collapse="isCollapse" />
|
||||
<div :class="{ 'has-logo': isLogo }">
|
||||
<Logo v-if="isLogo" :collapse="isCollapse" />
|
||||
<el-scrollbar wrap-class="scrollbar-wrapper">
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
|
@ -199,9 +199,8 @@ onMounted(() => {
|
||||
.tags-view-container {
|
||||
height: var(--v3-tagsview-height);
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #d8dce5;
|
||||
box-shadow: 0 1px 3px 0 #00000010, 0 0 3px 0 #00000010;
|
||||
background-color: var(--v3-header-bg-color);
|
||||
box-shadow: 0 0 3px 0 #00000010;
|
||||
.tags-view-wrapper {
|
||||
.tags-view-item {
|
||||
display: inline-block;
|
||||
|
@ -4,3 +4,4 @@ export { default as Settings } from "./Settings/index.vue"
|
||||
export { default as Sidebar } from "./Sidebar/index.vue"
|
||||
export { default as TagsView } from "./TagsView/index.vue"
|
||||
export { default as RightPanel } from "./RightPanel/index.vue"
|
||||
export { default as Logo } from "./Logo/index.vue"
|
||||
|
@ -1,71 +1,55 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue"
|
||||
import { computed, watchEffect } from "vue"
|
||||
import { storeToRefs } from "pinia"
|
||||
import { useAppStore } from "@/store/modules/app"
|
||||
import { useSettingsStore } from "@/store/modules/settings"
|
||||
import { AppMain, NavigationBar, Settings, Sidebar, TagsView, RightPanel } from "./components"
|
||||
import useResize from "./hooks/useResize"
|
||||
import LeftMode from "./LeftMode.vue"
|
||||
import LeftTopMode from "./LeftTopMode.vue"
|
||||
import { Settings, RightPanel } from "./components"
|
||||
import { DeviceEnum } from "@/constants/app-key"
|
||||
|
||||
const appStore = useAppStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
|
||||
const { showGreyMode, showColorWeakness, showSettings, showTagsView, fixedHeader } = storeToRefs(settingsStore)
|
||||
import { getCssVariableValue, setCssVariableValue } from "@/utils"
|
||||
|
||||
/** Layout 布局响应式 */
|
||||
useResize()
|
||||
|
||||
/** 定义计算属性 layoutClasses,用于控制布局的类名 */
|
||||
const layoutClasses = computed(() => {
|
||||
const appStore = useAppStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
|
||||
const { showSettings, layoutMode, showTagsView, showGreyMode, showColorWeakness } = storeToRefs(settingsStore)
|
||||
|
||||
const classes = computed(() => {
|
||||
return {
|
||||
hideSidebar: !appStore.sidebar.opened,
|
||||
openSidebar: appStore.sidebar.opened,
|
||||
withoutAnimation: appStore.sidebar.withoutAnimation,
|
||||
mobile: appStore.device === DeviceEnum.Mobile,
|
||||
showGreyMode: showGreyMode.value,
|
||||
showColorWeakness: showColorWeakness.value
|
||||
}
|
||||
})
|
||||
|
||||
/** 用于处理点击 mobile 端侧边栏遮罩层的事件 */
|
||||
const handleClickOutside = () => {
|
||||
appStore.closeSidebar(false)
|
||||
}
|
||||
//#region 隐藏标签栏时删除其高度,是为了让 Logo 组件高度和 Header 区域高度始终一致
|
||||
const cssVariableName = "--v3-tagsview-height"
|
||||
const v3TagsviewHeight = getCssVariableValue(cssVariableName)
|
||||
watchEffect(() => {
|
||||
showTagsView.value
|
||||
? setCssVariableValue(cssVariableName, v3TagsviewHeight)
|
||||
: setCssVariableValue(cssVariableName, "0px")
|
||||
})
|
||||
//#endregion
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="layoutClasses" class="app-wrapper">
|
||||
<!-- mobile 端侧边栏遮罩层 -->
|
||||
<div v-if="layoutClasses.mobile && layoutClasses.openSidebar" class="drawer-bg" @click="handleClickOutside" />
|
||||
<!-- 左侧边栏 -->
|
||||
<Sidebar class="sidebar-container" />
|
||||
<!-- 主容器 -->
|
||||
<div :class="{ hasTagsView: showTagsView }" class="main-container">
|
||||
<!-- 头部导航栏和标签栏 -->
|
||||
<div :class="{ 'fixed-header': fixedHeader }">
|
||||
<NavigationBar />
|
||||
<TagsView v-show="showTagsView" />
|
||||
</div>
|
||||
<!-- 页面主体内容 -->
|
||||
<AppMain />
|
||||
<!-- 右侧设置面板 -->
|
||||
<RightPanel v-if="showSettings">
|
||||
<Settings />
|
||||
</RightPanel>
|
||||
</div>
|
||||
<div :class="classes">
|
||||
<!-- 左侧模式 -->
|
||||
<LeftMode v-if="layoutMode === 'left' || appStore.device === DeviceEnum.Mobile" />
|
||||
<!-- 混合模式 -->
|
||||
<LeftTopMode v-else-if="layoutMode === 'left-top'" />
|
||||
<!-- 右侧设置面板 -->
|
||||
<RightPanel v-if="showSettings">
|
||||
<Settings />
|
||||
</RightPanel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/styles/mixins.scss";
|
||||
$transition-time: 0.35s;
|
||||
|
||||
.app-wrapper {
|
||||
@include clearfix;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.showGreyMode {
|
||||
filter: grayscale(1);
|
||||
}
|
||||
@ -73,88 +57,4 @@ $transition-time: 0.35s;
|
||||
.showColorWeakness {
|
||||
filter: invert(0.8);
|
||||
}
|
||||
|
||||
.drawer-bg {
|
||||
background-color: #000;
|
||||
opacity: 0.3;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
min-height: 100%;
|
||||
transition: margin-left $transition-time;
|
||||
margin-left: var(--v3-sidebar-width);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sidebar-container {
|
||||
transition: width $transition-time;
|
||||
width: var(--v3-sidebar-width) !important;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
font-size: 0px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1001;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fixed-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
width: calc(100% - var(--v3-sidebar-width));
|
||||
transition: width $transition-time;
|
||||
}
|
||||
|
||||
.hideSidebar {
|
||||
.main-container {
|
||||
margin-left: var(--v3-sidebar-hide-width);
|
||||
}
|
||||
.sidebar-container {
|
||||
width: var(--v3-sidebar-hide-width) !important;
|
||||
}
|
||||
.fixed-header {
|
||||
width: calc(100% - var(--v3-sidebar-hide-width));
|
||||
}
|
||||
}
|
||||
|
||||
// 适配 mobile 端
|
||||
.mobile {
|
||||
.main-container {
|
||||
margin-left: 0px;
|
||||
}
|
||||
.sidebar-container {
|
||||
transition: transform $transition-time;
|
||||
width: var(--v3-sidebar-width) !important;
|
||||
}
|
||||
&.openSidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
&.hideSidebar {
|
||||
.sidebar-container {
|
||||
pointer-events: none;
|
||||
transition-duration: 0.3s;
|
||||
transform: translate3d(calc(0px - var(--v3-sidebar-width)), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.fixed-header {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.withoutAnimation {
|
||||
.main-container,
|
||||
.sidebar-container {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,13 +1,21 @@
|
||||
/** Layout 相关 */
|
||||
|
||||
.app-wrapper {
|
||||
#app {
|
||||
color: $font-color;
|
||||
// 右侧设置面板
|
||||
.handle-button {
|
||||
background-color: lighten($theme-bg-color, 20%) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.app-wrapper {
|
||||
// Logo
|
||||
.layout-logo-container {
|
||||
background-color: lighten($theme-bg-color, 2%) !important;
|
||||
}
|
||||
|
||||
// 侧边栏
|
||||
.sidebar-container {
|
||||
.sidebar-logo-container {
|
||||
background-color: lighten($theme-bg-color, 2%) !important;
|
||||
}
|
||||
.el-menu {
|
||||
background-color: lighten($theme-bg-color, 4%) !important;
|
||||
.el-menu-item {
|
||||
@ -24,6 +32,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Header
|
||||
.layout-header {
|
||||
border-bottom: 1px solid lighten($theme-bg-color, 10%) !important;
|
||||
}
|
||||
|
||||
// 顶部导航栏
|
||||
.navigation-bar {
|
||||
background-color: $theme-bg-color;
|
||||
@ -37,7 +50,6 @@
|
||||
// TagsView
|
||||
.tags-view-container {
|
||||
background-color: $theme-bg-color !important;
|
||||
border-bottom: 1px solid lighten($theme-bg-color, 10%) !important;
|
||||
.tags-view-item {
|
||||
background-color: $theme-bg-color !important;
|
||||
color: $font-color !important;
|
||||
@ -58,9 +70,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 右侧设置面板
|
||||
.handle-button {
|
||||
background-color: lighten($theme-bg-color, 20%) !important;
|
||||
}
|
||||
}
|
||||
|
@ -14,12 +14,12 @@
|
||||
transform: translateX(30px);
|
||||
}
|
||||
|
||||
// sidebar-logo-fade
|
||||
.sidebar-logo-fade-enter-active,
|
||||
.sidebar-logo-fade-leave-active {
|
||||
// layout-logo-fade
|
||||
.layout-logo-fade-enter-active,
|
||||
.layout-logo-fade-leave-active {
|
||||
transition: opacity 1.5s;
|
||||
}
|
||||
.sidebar-logo-fade-enter-from,
|
||||
.sidebar-logo-fade-leave-to {
|
||||
.layout-logo-fade-enter-from,
|
||||
.layout-logo-fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
--v3-body-bg-color: #f2f3f5;
|
||||
/** Header 区域 = NavigationBar 组件 + TagsView 组件 */
|
||||
--v3-header-height: calc(var(--v3-navigationbar-height) + var(--v3-tagsview-height));
|
||||
--v3-header-bg-color: #ffffff;
|
||||
/** NavigationBar 组件 */
|
||||
--v3-navigationbar-height: 50px;
|
||||
/** Sidebar 组件 */
|
||||
@ -15,8 +16,6 @@
|
||||
--v3-sidebar-menu-hover-bg-color: #ffffff10;
|
||||
--v3-sidebar-menu-text-color: #c0c4cc;
|
||||
--v3-sidebar-menu-active-text-color: #ffffff;
|
||||
/** SidebarLogo 组件 */
|
||||
--v3-sidebarlogo-bg-color: #001428;
|
||||
/** TagsView 组件 */
|
||||
--v3-tagsview-height: 34px;
|
||||
--v3-tagsview-tag-text-color: #495060;
|
||||
|
Loading…
x
Reference in New Issue
Block a user