13 changed files with 500 additions and 0 deletions
-
4.prettierrc
-
BINsrc/assets/images/avatar.png
-
BINsrc/assets/images/logo.png
-
209src/assets/styles/sidebar.scss
-
0src/layout/components/AppMain.vue
-
124src/layout/components/Settings/index.vue
-
7src/layout/components/Sidebar/FixiOSBug.js
-
29src/layout/components/Sidebar/Item.vue
-
0src/layout/components/Sidebar/Link.vue
-
82src/layout/components/Sidebar/Logo.vue
-
0src/layout/components/Sidebar/SidebarItem.vue
-
45src/layout/components/Sidebar/index.vue
-
0src/layout/components/index.js
@ -0,0 +1,4 @@ |
|||
{ |
|||
"singleQuote": true, |
|||
"semi": false |
|||
} |
After Width: 120 | Height: 120 | Size: 1.8 KiB |
After Width: 128 | Height: 128 | Size: 8.1 KiB |
@ -0,0 +1,209 @@ |
|||
#app { |
|||
|
|||
.main-container { |
|||
min-height: 100%; |
|||
transition: margin-left .28s; |
|||
margin-left: $sideBarWidth; |
|||
position: relative; |
|||
} |
|||
|
|||
.sidebar-container { |
|||
transition: width 0.28s; |
|||
width: $sideBarWidth !important; |
|||
background-color: $menuBg; |
|||
height: 100%; |
|||
position: fixed; |
|||
font-size: 0; |
|||
top: 0; |
|||
bottom: 0; |
|||
left: 0; |
|||
z-index: 1001; |
|||
overflow: hidden; |
|||
|
|||
// reset element-ui css |
|||
.horizontal-collapse-transition { |
|||
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; |
|||
} |
|||
|
|||
.scrollbar-wrapper { |
|||
overflow-x: hidden !important; |
|||
} |
|||
|
|||
.el-scrollbar__bar.is-vertical { |
|||
right: 0; |
|||
} |
|||
|
|||
.el-scrollbar { |
|||
height: 100%; |
|||
} |
|||
|
|||
&.has-logo { |
|||
.el-scrollbar { |
|||
height: calc(100% - 50px); |
|||
} |
|||
} |
|||
|
|||
.is-horizontal { |
|||
display: none; |
|||
} |
|||
|
|||
a { |
|||
display: inline-block; |
|||
width: 100%; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.svg-icon { |
|||
margin-right: 16px; |
|||
} |
|||
|
|||
.el-menu { |
|||
border: none; |
|||
height: 100%; |
|||
width: 100% !important; |
|||
} |
|||
|
|||
// menu hover |
|||
.submenu-title-noDropdown, |
|||
.el-submenu__title { |
|||
&:hover { |
|||
background-color: $menuHover !important; |
|||
} |
|||
} |
|||
|
|||
.is-active>.el-submenu__title { |
|||
color: $subMenuActiveText !important; |
|||
} |
|||
|
|||
& .nest-menu .el-submenu>.el-submenu__title, |
|||
& .el-submenu .el-menu-item { |
|||
min-width: $sideBarWidth !important; |
|||
background-color: $subMenuBg !important; |
|||
|
|||
&:hover { |
|||
background-color: $subMenuHover !important; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.hideSidebar { |
|||
.sidebar-container { |
|||
width: 54px !important; |
|||
} |
|||
|
|||
.main-container { |
|||
margin-left: 54px; |
|||
} |
|||
|
|||
.submenu-title-noDropdown { |
|||
padding: 0 !important; |
|||
position: relative; |
|||
|
|||
.el-tooltip { |
|||
padding: 0 !important; |
|||
|
|||
.svg-icon { |
|||
margin-left: 20px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.el-submenu { |
|||
overflow: hidden; |
|||
|
|||
&>.el-submenu__title { |
|||
padding: 0 !important; |
|||
|
|||
.svg-icon { |
|||
margin-left: 20px; |
|||
} |
|||
|
|||
.el-submenu__icon-arrow { |
|||
display: none; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.el-menu--collapse { |
|||
.el-submenu { |
|||
&>.el-submenu__title { |
|||
&>span { |
|||
height: 0; |
|||
width: 0; |
|||
overflow: hidden; |
|||
visibility: hidden; |
|||
display: inline-block; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.el-menu--collapse .el-menu .el-submenu { |
|||
min-width: $sideBarWidth !important; |
|||
} |
|||
|
|||
// mobile responsive |
|||
.mobile { |
|||
.main-container { |
|||
margin-left: 0; |
|||
} |
|||
|
|||
.sidebar-container { |
|||
transition: transform .28s; |
|||
width: $sideBarWidth !important; |
|||
} |
|||
|
|||
&.hideSidebar { |
|||
.sidebar-container { |
|||
pointer-events: none; |
|||
transition-duration: 0.3s; |
|||
transform: translate3d(-$sideBarWidth, 0, 0); |
|||
} |
|||
} |
|||
} |
|||
|
|||
.withoutAnimation { |
|||
|
|||
.main-container, |
|||
.sidebar-container { |
|||
transition: none; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// when menu collapsed |
|||
.el-menu--vertical { |
|||
&>.el-menu { |
|||
.svg-icon { |
|||
margin-right: 16px; |
|||
} |
|||
} |
|||
|
|||
.nest-menu .el-submenu>.el-submenu__title, |
|||
.el-menu-item { |
|||
&:hover { |
|||
// you can use $subMenuHover |
|||
background-color: $menuHover !important; |
|||
} |
|||
} |
|||
|
|||
// the scroll bar appears when the subMenu is too long |
|||
>.el-menu--popup { |
|||
max-height: 100vh; |
|||
overflow-y: auto; |
|||
|
|||
&::-webkit-scrollbar-track-piece { |
|||
background: #d3dce6; |
|||
} |
|||
|
|||
&::-webkit-scrollbar { |
|||
width: 6px; |
|||
} |
|||
|
|||
&::-webkit-scrollbar-thumb { |
|||
background: #99a9bf; |
|||
border-radius: 20px; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,124 @@ |
|||
<template> |
|||
<div class="drawer-container"> |
|||
<div> |
|||
<h3 class="drawer-title">系统布局设置</h3> |
|||
|
|||
<div class="drawer-item"> |
|||
<span>主题颜色</span> |
|||
<theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" /> |
|||
</div> |
|||
|
|||
<div class="drawer-item"> |
|||
<span>显示标签</span> |
|||
<el-switch v-model="tagsView" class="drawer-switch" /> |
|||
</div> |
|||
|
|||
<div class="drawer-item"> |
|||
<span>固定头部</span> |
|||
<el-switch v-model="fixedHeader" class="drawer-switch" /> |
|||
</div> |
|||
|
|||
<div class="drawer-item"> |
|||
<span>显示LOGO</span> |
|||
<el-switch v-model="sidebarLogo" class="drawer-switch" /> |
|||
</div> |
|||
|
|||
<div class="drawer-item"> |
|||
<span>菜单UniqueOpened</span> |
|||
<el-switch v-model="uniqueOpened" class="drawer-switch" /> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import ThemePicker from '@/components/ThemePicker' |
|||
|
|||
export default { |
|||
components: { ThemePicker }, |
|||
data() { |
|||
return {} |
|||
}, |
|||
computed: { |
|||
fixedHeader: { |
|||
get() { |
|||
return this.$store.state.settings.fixedHeader |
|||
}, |
|||
set(val) { |
|||
this.$store.dispatch('settings/changeSetting', { |
|||
key: 'fixedHeader', |
|||
value: val |
|||
}) |
|||
} |
|||
}, |
|||
tagsView: { |
|||
get() { |
|||
return this.$store.state.settings.tagsView |
|||
}, |
|||
set(val) { |
|||
this.$store.dispatch('settings/changeSetting', { |
|||
key: 'tagsView', |
|||
value: val |
|||
}) |
|||
} |
|||
}, |
|||
sidebarLogo: { |
|||
get() { |
|||
return this.$store.state.settings.sidebarLogo |
|||
}, |
|||
set(val) { |
|||
this.$store.dispatch('settings/changeSetting', { |
|||
key: 'sidebarLogo', |
|||
value: val |
|||
}) |
|||
} |
|||
}, |
|||
uniqueOpened: { |
|||
get() { |
|||
return this.$store.state.settings.uniqueOpened |
|||
}, |
|||
set(val) { |
|||
this.$store.dispatch('settings/changeSetting', { |
|||
key: 'uniqueOpened', |
|||
value: val |
|||
}) |
|||
} |
|||
} |
|||
}, |
|||
methods: { |
|||
themeChange(val) { |
|||
this.$store.dispatch('settings/changeSetting', { |
|||
key: 'theme', |
|||
value: val |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.drawer-container { |
|||
padding: 24px; |
|||
font-size: 14px; |
|||
line-height: 1.5; |
|||
word-wrap: break-word; |
|||
|
|||
.drawer-title { |
|||
margin-bottom: 12px; |
|||
color: rgba(0, 0, 0, 0.85); |
|||
font-size: 14px; |
|||
line-height: 22px; |
|||
} |
|||
|
|||
.drawer-item { |
|||
color: rgba(0, 0, 0, 0.65); |
|||
font-size: 14px; |
|||
padding: 12px 0; |
|||
} |
|||
|
|||
.drawer-switch { |
|||
float: right; |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,7 @@ |
|||
// export default{
|
|||
// computed:{
|
|||
// device(){
|
|||
// return this.$store.state.app
|
|||
// }
|
|||
// }
|
|||
// }
|
@ -0,0 +1,29 @@ |
|||
<script> |
|||
export default { |
|||
name: 'MenuItem', |
|||
functional: true, |
|||
props: { |
|||
icon: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
title: { |
|||
type: String, |
|||
default: '' |
|||
} |
|||
}, |
|||
render(h, context) { |
|||
const { icon, title } = context.props |
|||
const vnodes = [] |
|||
|
|||
if (icon) { |
|||
vnodes.push(<svg-icon icon-class={icon}/>) |
|||
} |
|||
|
|||
if (title) { |
|||
vnodes.push(<span slot='title'>{(title)}</span>) |
|||
} |
|||
return vnodes |
|||
} |
|||
} |
|||
</script> |
@ -0,0 +1,82 @@ |
|||
<template> |
|||
<div class="sidebar-logo-container" :class="{'collapse':collapse}"> |
|||
<transition name="sidebarLogoFade"> |
|||
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/"> |
|||
<img v-if="logo" :src="logo" class="sidebar-logo"> |
|||
<h1 v-else class="sidebar-title">{{ title }} </h1> |
|||
</router-link> |
|||
<router-link v-else key="expand" class="sidebar-logo-link" to="/"> |
|||
<img v-if="logo" :src="logo" class="sidebar-logo"> |
|||
<h1 class="sidebar-title">{{ title }} </h1> |
|||
</router-link> |
|||
</transition> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import Logo from '@/assets/images/logo.png' |
|||
export default { |
|||
name: 'SidebarLogo', |
|||
props: { |
|||
collapse: { |
|||
type: Boolean, |
|||
required: true |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
title: '阅行集成后台管理', |
|||
logo: Logo |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.sidebarLogoFade-enter-active { |
|||
transition: opacity 1.5s; |
|||
} |
|||
|
|||
.sidebarLogoFade-enter, |
|||
.sidebarLogoFade-leave-to { |
|||
opacity: 0; |
|||
} |
|||
|
|||
.sidebar-logo-container { |
|||
position: relative; |
|||
width: 100%; |
|||
height: 50px; |
|||
line-height: 50px; |
|||
text-align: center; |
|||
overflow: hidden; |
|||
|
|||
& .sidebar-logo-link { |
|||
height: 100%; |
|||
width: 100%; |
|||
|
|||
& .sidebar-logo { |
|||
width: 32px; |
|||
height: 32px; |
|||
vertical-align: middle; |
|||
margin-right: 6px; |
|||
} |
|||
|
|||
& .sidebar-title { |
|||
display: inline-block; |
|||
margin: 0; |
|||
color: #fff; |
|||
font-weight: 600; |
|||
line-height: 50px; |
|||
font-size: 14px; |
|||
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif; |
|||
vertical-align: middle; |
|||
} |
|||
} |
|||
|
|||
&.collapse { |
|||
.sidebar-logo { |
|||
margin-right: 0px; |
|||
} |
|||
} |
|||
} |
|||
</style> |
@ -0,0 +1,45 @@ |
|||
<template> |
|||
<div :class="{'has-logo':showLogo}"> |
|||
<logo v-if="showLogo" :collapse="isCollapse" /> |
|||
<el-scrollbar wrap-class="scrollbar-wrapper"> |
|||
<el-menu :default-active="activeMenu" :collapse="isCollapse" :background-color="variables.menuBg" :text-color="variables.menuText" :unique-opened="$store.state.settings.uniqueOpened" :active-text-color="variables.menuActiveText" :collapse-transition="false" mode="vertical"> |
|||
<sidebar-item v-for="route in sidebarRouters" :key="route.path" :item="route" :base-path="route.path" /> |
|||
</el-menu> |
|||
</el-scrollbar> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { mapGetters } from 'vuex' |
|||
import Logo from './Logo' |
|||
import SidebarItem from './SidebarItem' |
|||
import variables from '@/assets/styles/variables.scss' |
|||
|
|||
export default { |
|||
components: { SidebarItem, Logo }, |
|||
computed: { |
|||
...mapGetters([ |
|||
'sidebarRouters', |
|||
'sidebar' |
|||
]), |
|||
activeMenu() { |
|||
const route = this.$route |
|||
const { meta, path } = route |
|||
|
|||
if (meta.activeMenu) { |
|||
return meta.activeMenu |
|||
} |
|||
return path |
|||
}, |
|||
showLogo() { |
|||
return this.$store.state.settings.sidebarLogo |
|||
}, |
|||
variables() { |
|||
return variables |
|||
}, |
|||
isCollapse() { |
|||
return !this.sidebar.opened |
|||
} |
|||
} |
|||
} |
|||
</script> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue