侧边栏适应移动端(真香)

master
toesbieya 6 years ago
parent 522a3ea6c1
commit 76cc6772d9
  1. 1
      vue/src/assets/styles/index.scss
  2. 8
      vue/src/assets/styles/sidebar.scss
  3. 1
      vue/src/layout/components/Navbar/components/Breadcrumb.vue
  4. 53
      vue/src/layout/components/Navbar/components/Hamburger.vue
  5. 91
      vue/src/layout/components/Navbar/components/SettingDrawer.vue
  6. 12
      vue/src/layout/components/Navbar/index.vue
  7. 29
      vue/src/layout/components/Sidebar/index.vue
  8. 35
      vue/src/layout/index.vue
  9. 25
      vue/src/layout/mixin/ResizeHandler.js
  10. 1
      vue/src/store/modules/app.js
  11. 34
      vue/src/store/modules/setting.js

@ -2,7 +2,6 @@
@import "~element-ui/packages/theme-chalk/src/index";
@import "transition";
@import "element-ui";
@import "sidebar";
@import "nprogress";
body {

@ -9,6 +9,14 @@ $iconSize: 17px;
overflow: hidden;
flex-shrink: 0;
transition: all .3s;
top: 0;
bottom: 0;
z-index: 10;
//移动端时为fixed
&.mobile {
position: fixed;
}
//折叠设置
&.collapse-sidebar {

@ -37,7 +37,6 @@
.app-breadcrumb.el-breadcrumb {
display: inline-block;
line-height: 50px;
margin-left: 15px;
.no-redirect {
color: $--color-gray;

@ -0,0 +1,53 @@
<script type="text/jsx">
export default {
name: 'Hamburger',
functional: true,
props: {
isActive: Boolean
},
render(h, context) {
const {isActive} = context.props
return (
<div class="hamburger-container" {...context.data}>
<svg
class={{'hamburger': true, 'is-active': isActive}}
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
width="64"
height="64"
>
<path
d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"/>
</svg>
</div>
)
}
}
</script>
<style lang="scss">
.hamburger-container {
padding: 0 15px;
line-height: 46px;
height: 100%;
float: left;
cursor: pointer;
transition: background .3s;
-webkit-tap-highlight-color: transparent;
&:hover {
background: rgba(0, 0, 0, .025)
}
.hamburger {
display: inline-block;
vertical-align: middle;
width: 20px;
height: 20px;
&.is-active {
transform: rotate(180deg);
}
}
}
</style>

@ -39,83 +39,30 @@
</template>
<script>
const states = ['showLogo', 'showBreadcrumb', 'sidebarUniqueOpen', 'sidebarCollapse',
'sidebarAutoHidden', 'sidebarShowParent', 'headerAutoHidden', 'showBackToTop']
function createStateComputed() {
const obj = {}
states.forEach(key => {
obj[key] = {
get() {
return this.$store.state.setting[key]
},
set(v) {
this.$store.commit(`setting/${key}`, v)
}
}
})
return obj
}
export default {
name: "SettingDrawer",
props: {
value: Boolean
},
computed: {
showLogo: {
get() {
return this.$store.state.setting.showLogo
},
set(v) {
this.$store.commit('setting/showLogo', v)
}
},
showBreadcrumb: {
get() {
return this.$store.state.setting.showBreadcrumb
},
set(v) {
this.$store.commit('setting/showBreadcrumb', v)
}
},
sidebarUniqueOpen: {
get() {
return this.$store.state.setting.sidebarUniqueOpen
},
set(v) {
this.$store.commit('setting/sidebarUniqueOpen', v)
}
},
sidebarCollapse: {
get() {
return this.$store.state.setting.sidebarCollapse
},
set(v) {
this.$store.commit('setting/sidebarCollapse', v)
if (v && this.sidebarAutoHidden) {
this.$store.commit('setting/sidebarAutoHidden', false)
}
}
},
sidebarShowParent: {
get() {
return this.$store.state.setting.sidebarShowParent
},
set(v) {
this.$store.commit('setting/sidebarShowParent', v)
}
},
sidebarAutoHidden: {
get() {
return this.$store.state.setting.sidebarAutoHidden
},
set(v) {
this.$store.commit('setting/sidebarAutoHidden', v)
if (v && this.sidebarCollapse) {
this.$store.commit('setting/sidebarCollapse', false)
}
}
},
headerAutoHidden: {
get() {
return this.$store.state.setting.headerAutoHidden
},
set(v) {
this.$store.commit('setting/headerAutoHidden', v)
}
},
showBackToTop: {
get() {
return this.$store.state.setting.showBackToTop
},
set(v) {
this.$store.commit('setting/showBackToTop', v)
}
},
}
computed: createStateComputed()
}
</script>

@ -1,5 +1,7 @@
<template>
<nav class="navbar">
<hamburger :is-active="!sidebarCollapse" @click="clickHamburger"/>
<breadcrumb v-if="showBreadcrumb"/>
<div class="right-menu">
@ -41,11 +43,13 @@
</el-dropdown-menu>
</el-dropdown>
</div>
<setting-drawer v-model="settingDrawer"/>
</nav>
</template>
<script>
import Hamburger from './components/Hamburger'
import Breadcrumb from './components/Breadcrumb'
import Search from './components/HeaderSearch'
import Fullscreen from "./components/Fullscreen"
@ -53,12 +57,12 @@
import {mapState} from 'vuex'
import GuideMixin from '@/mixins/guide'
import {auth} from "@/utils/auth"
import {elAlert, elConfirm} from "@/utils/message"
import {elConfirm} from "@/utils/message"
export default {
name: 'navbar',
mixins: [GuideMixin.navbar],
components: {Breadcrumb, Search, Fullscreen, SettingDrawer},
components: {Hamburger, Breadcrumb, Search, Fullscreen, SettingDrawer},
data() {
return {
fullscreen: false,
@ -72,6 +76,7 @@
prepare_logout: state => state.prepare_logout
}),
...mapState('setting', {
sidebarCollapse: state => state.sidebarCollapse,
showBreadcrumb: state => state.showBreadcrumb
}),
showSystemMonitor() {
@ -82,6 +87,9 @@
}
},
methods: {
clickHamburger() {
this.$store.commit('setting/sidebarCollapse', !this.sidebarCollapse)
},
logout() {
if (this.prepare_logout) return
elConfirm('确认退出?')

@ -1,17 +1,16 @@
<template>
<aside
:class="{'collapse-sidebar':sidebarCollapse,'hide-sidebar':hideSidebar}"
class="sidebar-container"
:class="sidebarClass"
@mouseenter="mouseOutside=false"
@mouseleave="mouseOutside=true"
>
<logo v-if="showLogo" :collapse="sidebarCollapse"/>
<logo v-if="showLogo" :collapse="collapse"/>
<el-scrollbar>
<el-menu
ref="menu"
:active-text-color="variables.primary"
:background-color="variables.menuBg"
:collapse="sidebarCollapse"
:collapse="collapse"
:collapse-transition="false"
:default-active="$route.path"
:text-color="variables.menuText"
@ -47,18 +46,34 @@
}
},
computed: {
...mapState('app', {
device: state => state.device
}),
...mapState('resource', {
routes: state => state.sidebarMenus
}),
...mapState('setting', {
showLogo: state => state.showLogo,
sidebarUniqueOpen: state => state.sidebarUniqueOpen,
sidebarCollapse: state => state.sidebarCollapse,
sidebarUniqueOpen: state => state.sidebarUniqueOpen,
sidebarShowParent: state => state.sidebarShowParent,
sidebarAutoHidden: state => state.sidebarAutoHidden
}),
//pc
collapse() {
return this.sidebarCollapse && this.device === 'pc'
},
hideSidebar() {
return this.sidebarAutoHidden && this.mouseOutside
|| this.sidebarCollapse && this.device === 'mobile'
},
sidebarClass() {
return {
'sidebar-container': true,
'mobile': this.device === 'mobile',
'collapse-sidebar': this.collapse,
'hide-sidebar': this.hideSidebar
}
}
},
watch: {
@ -89,3 +104,7 @@
}
}
</script>
<style lang="scss">
@import "~@/assets/styles/sidebar";
</style>

@ -5,6 +5,12 @@
<v-header/>
<v-main/>
</section>
<!--移动端侧边栏展开时的遮罩-->
<div
v-show="showSidebarMask"
class="drawer-bg"
@click.stop.prevent="$store.commit('setting/sidebarCollapse',true)"
/>
</section>
</template>
@ -13,15 +19,21 @@
import VMain from './components/Main'
import VHeader from './components/Header'
import VSidebar from './components/Sidebar'
import ResizeHandler from './mixin/ResizeHandler'
import {isEmpty} from "@/utils"
export default {
name: 'Layout',
mixins: [ResizeHandler],
components: {VMain, VSidebar, VHeader},
computed: {
...mapState('app', {
device: state => state.device,
hasHeader: state => state.hasHeader
}),
...mapState('setting', {
sidebarCollapse: state => state.sidebarCollapse
}),
...mapState('socket', {
online: state => state.online
}),
@ -30,6 +42,9 @@
}),
showOfflineTip() {
return !this.online && this.isLogin
},
showSidebarMask() {
return !this.sidebarCollapse && this.device === 'mobile'
}
},
watch: {
@ -46,11 +61,21 @@
height: 100%;
width: 100%;
flex-direction: row;
}
.main-container {
overflow: hidden;
position: relative;
flex-direction: column;
.drawer-bg {
background: #000;
opacity: 0.3;
width: 100%;
top: 0;
height: 100%;
position: absolute;
z-index: 9;
}
.main-container {
overflow: hidden;
position: relative;
flex-direction: column;
}
}
</style>

@ -0,0 +1,25 @@
const {body} = document
const WIDTH = 768
export default {
methods: {
$_isMobile() {
const rect = body.getBoundingClientRect()
return rect.width - 1 < WIDTH
},
$_resizeHandler() {
if (!document.hidden) {
const isMobile = this.$_isMobile()
this.$store.commit('app/device', isMobile ? 'mobile' : 'pc')
isMobile && this.$store.commit('setting/sidebarCollapse', true)
}
}
},
mounted() {
window.addEventListener('resize', this.$_resizeHandler)
this.$_resizeHandler()
},
beforeDestroy() {
window.removeEventListener('resize', this.$_resizeHandler)
}
}

@ -4,6 +4,7 @@ import {createMutations} from "@/utils"
const localSettings = getLocalPersonalSettings()
const state = {
//区分pc和移动端(mobile)
device: 'pc',
//登陆页背景动画
loginPageBackgroundAnimation: 'firework',

@ -7,6 +7,16 @@ import {getLocalPersonalSettings, setLocalPersonalSettings} from "@/utils/localS
const localSettings = getLocalPersonalSettings()
function createMutations(state) {
return Object.keys(state).reduce((mutations, key) => {
mutations[key] = (ref, data) => {
ref[key] = data
setLocalPersonalSettings(ref)
}
return mutations
}, {})
}
const state = {
//是否显示logo
showLogo: true,
@ -31,16 +41,22 @@ Object.keys(state).forEach(key => {
if (!isEmpty(localSettings[key])) state[key] = localSettings[key]
})
const mutations = createMutations(state)
function createMutations(state) {
return Object.keys(state).reduce((mutations, key) => {
mutations[key] = (ref, data) => {
ref[key] = data
setLocalPersonalSettings(ref)
const mutations = {
...createMutations(state),
sidebarCollapse(state, v) {
if (v && state.sidebarAutoHidden) {
state.sidebarAutoHidden = false
}
return mutations
}, {})
state.sidebarCollapse = v
setLocalPersonalSettings(state)
},
sidebarAutoHidden(state, v) {
if (v && state.sidebarCollapse) {
state.sidebarCollapse = false
}
state.sidebarAutoHidden = v
setLocalPersonalSettings(state)
}
}
export default {

Loading…
Cancel
Save