Files
ruoyi-vue/src/layout/index.vue
2026-04-07 17:29:30 +08:00

187 lines
4.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div :class="classObj" class="app-wrapper" :style="{'--current-color': theme}">
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
<sidebar v-if="!sidebar.hide && device !== 'mobile'" class="sidebar-container"/>
<div :class="{hasTagsView:needTagsView,sidebarHide:sidebar.hide, 'mobile-layout': device === 'mobile'}" class="main-container">
<div :class="{'fixed-header':fixedHeader}">
<navbar @setLayout="setLayout"/>
<tags-view v-if="needTagsView && device !== 'mobile'"/>
</div>
<app-main/>
<settings ref="settingRef"/>
</div>
<!-- 移动端底部导航key 随接口路由更新确保菜单与跳转使用最新路由 -->
<mobile-bottom-nav
v-if="device === 'mobile'"
:key="mobileNavKey"
:items="mobileNavItems"
/>
</div>
</template>
<script>
import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components'
import MobileBottomNav from '@/components/MobileBottomNav'
import ResizeMixin from './mixin/ResizeHandler'
import { mapState, mapGetters } from 'vuex'
import variables from '@/assets/styles/variables.scss'
export default {
name: 'Layout',
components: {
AppMain,
Navbar,
Settings,
Sidebar,
TagsView,
MobileBottomNav
},
mixins: [ResizeMixin],
computed: {
...mapState({
theme: state => state.settings.theme,
sideTheme: state => state.settings.sideTheme,
sidebar: state => state.app.sidebar,
device: state => state.app.device,
needTagsView: state => state.settings.tagsView,
fixedHeader: state => state.settings.fixedHeader
}),
...mapGetters(['sidebarRouters']),
/** 接口路由更新后变化,使底部导航重新渲染并拉取最新菜单,避免点击跳错页 */
mobileNavKey() {
const routes = this.sidebarRouters || []
const len = routes.length
const firstPath = (len && routes[0]) ? (routes[0].path || '') : ''
return `${len}-${firstPath}`
},
mobileNavItems() {
return [
{ path: '/sloworder/index', label: '慢单', icon: 'el-icon-tickets' },
{ path: '/jd-instruction/index', label: '中控', icon: 'el-icon-s-operation' },
{ path: '/mobile/fadan', label: '发单', icon: 'el-icon-edit-outline' },
{ path: '/mobile/xianyu-publish', label: '发品', icon: 'el-icon-goods' },
{ path: '/mobile/zhongcao', label: '种草', icon: 'el-icon-chat-dot-round' }
]
},
classObj() {
return {
hideSidebar: false, // 侧边栏始终展开
openSidebar: true, // 侧边栏始终展开
withoutAnimation: this.sidebar.withoutAnimation,
mobile: this.device === 'mobile'
}
},
variables() {
return variables
}
},
methods: {
handleClickOutside() {
this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
},
setLayout() {
this.$refs.settingRef.openSetting()
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/mixin.scss";
@import "~@/assets/styles/variables.scss";
.app-wrapper {
@include clearfix;
position: relative;
height: 100%;
width: 100%;
&.mobile.openSidebar {
position: fixed;
top: 0;
}
@media (max-width: 768px) {
&.mobile {
height: auto;
min-height: 100vh;
position: relative;
&.openSidebar {
position: relative;
}
}
}
}
.drawer-bg {
background: #000;
opacity: 0.3;
width: 100%;
top: 0;
height: 100%;
position: absolute;
z-index: 999;
}
.fixed-header {
position: fixed;
top: 0;
right: 0;
z-index: 9;
width: calc(100% - #{$base-sidebar-width});
transition: width 0.28s;
}
.hideSidebar .fixed-header {
width: calc(100% - 54px);
}
.sidebarHide .fixed-header {
width: 100%;
}
.mobile .fixed-header {
width: 100%;
}
// 移动端优化
@media (max-width: 768px) {
.app-wrapper {
&.mobile {
.sidebar-container {
display: none; // 移动端完全隐藏侧边栏,使用底部导航
}
}
.drawer-bg {
display: none; // 移动端不需要遮罩
}
.main-container {
margin-left: 0 !important;
width: 100%;
height: auto !important;
min-height: 100vh;
overflow: visible;
&.mobile-layout {
padding-bottom: 60px; // 为底部导航预留空间
}
}
.fixed-header {
width: 100% !important;
left: 0;
}
// 移动端隐藏标签页
.hasTagsView {
.fixed-header + .app-main {
padding-top: 48px !important;
}
}
}
}
</style>