refactor: Sidebar Component

This commit is contained in:
pany 2022-08-25 16:26:28 +08:00
parent d77f7fafa9
commit d926c6c2b5
5 changed files with 77 additions and 119 deletions

View File

@ -1,9 +1,9 @@
<script lang="ts" setup>
import { computed, PropType } from "vue"
import { RouteRecordRaw } from "vue-router"
import type { RouteRecordRaw } from "vue-router"
import SidebarItemLink from "./SidebarItemLink.vue"
import { isExternal } from "@/utils/validate"
import path from "path-browserify"
import SidebarItemLink from "./SidebarItemLink.vue"
const props = defineProps({
item: {
@ -12,7 +12,7 @@ const props = defineProps({
},
isCollapse: {
type: Boolean,
required: false
default: false
},
isFirstLevel: {
type: Boolean,
@ -20,13 +20,14 @@ const props = defineProps({
},
basePath: {
type: String,
required: true
default: ""
}
})
const alwaysShowRootMenu = computed(() => {
return !!(props.item.meta && props.item.meta.alwaysShow)
return props.item.meta && props.item.meta.alwaysShow
})
const showingChildNumber = computed(() => {
if (props.item.children) {
const showingChildren = props.item.children.filter((item) => {
@ -36,6 +37,7 @@ const showingChildNumber = computed(() => {
}
return 0
})
const theOnlyOneChild = computed(() => {
if (showingChildNumber.value > 1) {
return null
@ -64,7 +66,7 @@ const resolvePath = (routePath: string) => {
</script>
<template>
<div v-if="!item.meta || !item.meta.hidden" :class="{ 'simple-mode': isCollapse, 'first-level': isFirstLevel }">
<div v-if="!props.item.meta?.hidden" :class="{ 'simple-mode': props.isCollapse, '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)">
@ -75,17 +77,17 @@ const resolvePath = (routePath: string) => {
</el-menu-item>
</SidebarItemLink>
</template>
<el-sub-menu v-else :index="resolvePath(item.path)" popper-append-to-body>
<el-sub-menu v-else :index="resolvePath(props.item.path)" popper-append-to-body>
<template #title>
<svg-icon v-if="item.meta && item.meta.icon" :name="item.meta.icon" />
<span v-if="item.meta && item.meta.title">{{ item.meta.title }}</span>
<svg-icon v-if="props.item.meta && props.item.meta.icon" :name="props.item.meta.icon" />
<span v-if="props.item.meta && props.item.meta.title">{{ props.item.meta.title }}</span>
</template>
<template v-if="item.children">
<template v-if="props.item.children">
<sidebar-item
v-for="child in item.children"
v-for="child in props.item.children"
:key="child.path"
:item="child"
:is-collapse="isCollapse"
:is-collapse="props.isCollapse"
:is-first-level="false"
:base-path="resolvePath(child.path)"
/>

View File

@ -1,5 +1,4 @@
<script lang="ts" setup>
import { useRouter } from "vue-router"
import { isExternal } from "@/utils/validate"
const props = defineProps({
@ -8,21 +7,13 @@ const props = defineProps({
required: true
}
})
const router = useRouter()
const push = () => {
router.push(props.to).catch((err) => {
console.warn(err)
})
}
</script>
<template>
<a v-if="isExternal(to)" :href="to" target="_blank" rel="noopener">
<a v-if="isExternal(props.to)" :href="props.to" target="_blank" rel="noopener">
<slot />
</a>
<div v-else @click="push">
<router-link v-else :to="props.to">
<slot />
</div>
</router-link>
</template>

View File

@ -9,11 +9,11 @@ const props = defineProps({
<template>
<div class="sidebar-logo-container" :class="{ collapse: props.collapse }">
<transition name="sidebarLogoFade">
<router-link v-if="props.collapse" key="collapse" class="sidebar-logo-link" to="/">
<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" class="sidebar-logo-link" to="/">
<router-link v-else key="expand" to="/">
<img src="@/assets/layout/logo-text-1.png" class="sidebar-logo-text" />
</router-link>
</transition>
@ -21,16 +21,6 @@ const props = defineProps({
</template>
<style lang="scss" scoped>
.sidebarLogoFade-enter-active,
.sidebarLogoFade-leave-active {
transition: opacity 1.5s;
}
.sidebarLogoFade-enter-from,
.sidebarLogoFade-leave-to {
opacity: 0;
}
.sidebar-logo-container {
position: relative;
width: 100%;
@ -42,25 +32,21 @@ const props = defineProps({
.sidebar-logo {
display: none;
}
& .sidebar-logo-link {
height: 100%;
width: 100%;
& .sidebar-logo-text {
.sidebar-logo-text {
height: 100%;
vertical-align: middle;
}
}
&.collapse {
}
.collapse {
.sidebar-logo {
width: 32px;
height: 32px;
vertical-align: middle;
margin-right: 0;
display: inline-block;
}
.sidebar-logo-text {
display: none;
}
}
}
</style>

View File

@ -1,6 +1,7 @@
<script lang="ts" setup>
import { computed } from "vue"
import { useRoute } from "vue-router"
import { storeToRefs } from "pinia"
import { useAppStore } from "@/store/modules/app"
import { usePermissionStore } from "@/store/modules/permission"
import { useSettingsStore } from "@/store/modules/settings"
@ -17,47 +18,40 @@ const appStore = useAppStore()
const permissionStore = usePermissionStore()
const settingsStore = useSettingsStore()
const sidebar = computed(() => {
return appStore.sidebar
})
const routes = computed(() => {
return permissionStore.routes
})
const showLogo = computed(() => {
return settingsStore.showSidebarLogo
})
const { showSidebarLogo } = storeToRefs(settingsStore)
const activeMenu = computed(() => {
const { meta, path } = route
if (meta !== null || meta !== undefined) {
if (meta.activeMenu) {
if (meta?.activeMenu) {
return meta.activeMenu
}
}
return path
})
const isCollapse = computed(() => {
return !sidebar.value.opened
return !appStore.sidebar.opened
})
</script>
<template>
<div :class="{ 'has-logo': showLogo }">
<SidebarLogo v-if="showLogo" :collapse="isCollapse" />
<div :class="{ 'has-logo': showSidebarLogo }">
<SidebarLogo v-if="showSidebarLogo" :collapse="isCollapse" />
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:collapse="isCollapse"
:unique-opened="true"
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="v3SidebarMenuBgColor"
:text-color="v3SidebarMenuTextColor"
:active-text-color="v3SidebarMenuActiveTextColor"
:unique-opened="true"
:collapse-transition="false"
mode="vertical"
>
<SidebarItem
v-for="routeItem in routes"
:key="routeItem.path"
:item="routeItem"
:base-path="routeItem.path"
v-for="route in permissionStore.routes"
:key="route.path"
:item="route"
:base-path="route.path"
:is-collapse="isCollapse"
/>
</el-menu>
@ -65,29 +59,6 @@ const isCollapse = computed(() => {
</div>
</template>
<style lang="scss">
.sidebar-container {
// Element-Plus CSS, scoped sidebar-container
.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__view {
height: 100%;
}
.el-scrollbar__bar {
&.is-vertical {
right: 0;
}
&.is-horizontal {
display: none;
}
}
}
</style>
<style lang="scss" scoped>
@mixin tip-line {
&::before {
@ -101,16 +72,30 @@ const isCollapse = computed(() => {
}
}
.el-scrollbar {
height: 100%;
}
.has-logo {
.el-scrollbar {
height: calc(100% - var(--v3-header-height));
}
}
.el-scrollbar {
height: 100%;
::v-deep(.scrollbar-wrapper) {
//
overflow-x: hidden !important;
.el-scrollbar__view {
height: 100%;
}
}
//
::v-deep(.el-scrollbar__bar) {
&.is-horizontal {
//
display: none;
}
}
}
.el-menu {
border: none;
height: 100%;

View File

@ -1,27 +1,14 @@
// See https://staging-cn.vuejs.org/guide/built-ins/transition.html for detail
// fade
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.28s;
}
.fade-enter,
.fade-leave-active {
opacity: 0;
}
// fade-transform
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all 0.5s;
}
.fade-transform-enter {
opacity: 0;
transform: translateX(-30px);
}
.fade-transform-leave-to {
opacity: 0;
transform: translateX(30px);
@ -32,17 +19,24 @@
.breadcrumb-leave-active {
transition: all 0.5s;
}
.breadcrumb-enter,
.breadcrumb-leave-active {
opacity: 0;
transform: translateX(20px);
}
.breadcrumb-move {
transition: all 0.5s;
}
.breadcrumb-leave-active {
position: absolute;
}
// sidebar-logo-fade
.sidebar-logo-fade-enter-active,
.sidebar-logo-fade-leave-active {
transition: opacity 1.5s;
}
.sidebar-logo-fade-enter-from,
.sidebar-logo-fade-leave-to {
opacity: 0;
}