feat: 🎸 重构sider组件中
This commit is contained in:
@@ -1,27 +1,23 @@
|
||||
<template>
|
||||
<div>
|
||||
<menu-unfold-outlined
|
||||
v-if="collapsed"
|
||||
class="trigger"
|
||||
<svg
|
||||
:class="{'is-active': !collapsed}"
|
||||
class="hamburger"
|
||||
viewBox="0 0 1024 1024"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
@click="toggleCollapsed(!collapsed)"
|
||||
/>
|
||||
<menu-fold-outlined
|
||||
v-else
|
||||
class="trigger"
|
||||
@click="toggleCollapsed(!collapsed)"
|
||||
/>
|
||||
>
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
// import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons-vue'
|
||||
export default defineComponent({
|
||||
name: 'Hamburger',
|
||||
// components: {
|
||||
// MenuUnfoldOutlined,
|
||||
// MenuFoldOutlined
|
||||
// },
|
||||
props: {
|
||||
collapsed: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
@@ -43,13 +39,13 @@ export default defineComponent({
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.trigger {
|
||||
.hamburger {
|
||||
display: inline-block;
|
||||
transition: color 0.3s;
|
||||
height: @navbarHeight;
|
||||
line-height: @navbarHeight;
|
||||
cursor: pointer;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.trigger:hover {
|
||||
color: #1890ff;
|
||||
.hamburger.is-active {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -50,7 +50,7 @@ export default defineComponent({
|
||||
padding-left: 18px;
|
||||
cursor: pointer;
|
||||
height: @topSilderHeight;
|
||||
max-width: 200px;
|
||||
width: 100%;
|
||||
img {
|
||||
width: 37px;
|
||||
height: 37px;
|
||||
|
||||
34
src/components/Sider/Item.vue
Normal file
34
src/components/Sider/Item.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div>
|
||||
<i v-if="icon.includes('el-icon')" :class="[icon, 'sub-el-icon', 'anticon']" />
|
||||
<svg-icon v-else :icon-class="icon" class="anticon" />
|
||||
<slot name="title">
|
||||
<span class="anticon-item">{{ title }}</span>
|
||||
</slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Item',
|
||||
props: {
|
||||
icon: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
},
|
||||
title: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.anticon-item {
|
||||
opacity: 1;
|
||||
transition: opacity .3s cubic-bezier(.645,.045,.355,1),width .3s cubic-bezier(.645,.045,.355,1);
|
||||
}
|
||||
</style>
|
||||
27
src/components/Sider/Link.vue
Normal file
27
src/components/Sider/Link.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<a v-if="isExternal(to)" :href="to" target="_blank" rel="noopener">
|
||||
<slot />
|
||||
</a>
|
||||
<router-link v-else :to="to">
|
||||
<slot />
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { isExternal } from '@/utils/validate'
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
to: {
|
||||
type: String as PropType<string>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
isExternal
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,11 +1,94 @@
|
||||
<template>
|
||||
<div />
|
||||
<template v-if="!item.meta?.hidden">
|
||||
<template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.meta?.alwaysShow">
|
||||
<el-menu-item :index="resolvePath(onlyOneChild.path)">
|
||||
<item v-if="onlyOneChild.meta" :icon="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" :title="onlyOneChild.meta.title" />
|
||||
<template #title>
|
||||
{{ onlyOneChild.meta.title }}
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
|
||||
<el-submenu v-else :index="resolvePath(item.path)">
|
||||
<template #title>
|
||||
<item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
|
||||
</template>
|
||||
<sider-item
|
||||
v-for="child in item.children"
|
||||
:key="child.path"
|
||||
:is-nest="true"
|
||||
:item="child"
|
||||
:base-path="resolvePath(child.path)"
|
||||
/>
|
||||
</el-submenu>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { defineComponent, PropType, ref } from 'vue'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import path from 'path'
|
||||
import { isExternal } from '@/utils/validate'
|
||||
import Item from './Item.vue'
|
||||
import AppLink from './Link.vue'
|
||||
export default defineComponent({
|
||||
name: 'SilderItem'
|
||||
name: 'SiderItem',
|
||||
components: { Item, AppLink },
|
||||
props: {
|
||||
// route object
|
||||
item: {
|
||||
type: Object as PropType<object>,
|
||||
required: true
|
||||
},
|
||||
isNest: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false
|
||||
},
|
||||
basePath: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const onlyOneChild = ref<any>(null)
|
||||
|
||||
function hasOneShowingChild(children: RouteRecordRaw[] = [], parent: RouteRecordRaw): boolean {
|
||||
const showingChildren: RouteRecordRaw[] = children.filter((item: RouteRecordRaw) => {
|
||||
if (item.meta && item.meta.hidden) {
|
||||
return false
|
||||
} else {
|
||||
// Temp set(will be used if only has one showing child)
|
||||
onlyOneChild.value = item
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
// When there is only one child router, the child router is displayed by default
|
||||
if (showingChildren.length === 1) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Show parent if there are no child router to display
|
||||
if (showingChildren.length === 0) {
|
||||
onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function resolvePath(routePath: string): string {
|
||||
if (isExternal(routePath)) {
|
||||
return routePath
|
||||
}
|
||||
return path.resolve(props.basePath, routePath)
|
||||
}
|
||||
return {
|
||||
onlyOneChild,
|
||||
hasOneShowingChild,
|
||||
resolvePath
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,21 +1,27 @@
|
||||
<template>
|
||||
<div :class="{'has-logo':show_logo}">
|
||||
<el-scrollbar wrap-class="scrollbar-wrapper">
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
:collapse="collapsed"
|
||||
:unique-opened="false"
|
||||
mode="vertical"
|
||||
>
|
||||
<sider-item
|
||||
v-for="route in routers"
|
||||
:key="route.path"
|
||||
:item="route"
|
||||
:base-path="route.path"
|
||||
/>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<!-- <div :class="{'has-logo': showLogo}" class="sidebar-container"> -->
|
||||
<!-- <el-scrollbar class="menu-wrap"> -->
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
:collapse="collapsed"
|
||||
:unique-opened="false"
|
||||
:background-color="variables.menuBg"
|
||||
:text-color="variables.menuText"
|
||||
:active-text-color="variables.menuActiveText"
|
||||
mode="vertical"
|
||||
class="sidebar-container"
|
||||
:class="{'sidebar__wrap--collapsed': collapsed}"
|
||||
@select="selectMenu"
|
||||
>
|
||||
<sider-item
|
||||
v-for="route in routers"
|
||||
:key="route.path"
|
||||
:item="route"
|
||||
:base-path="route.path"
|
||||
/>
|
||||
</el-menu>
|
||||
<!-- </el-scrollbar> -->
|
||||
<!-- </div> -->
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -25,9 +31,8 @@ import { permissionStore } from '_p/index/store/modules/permission'
|
||||
import { appStore } from '_p/index/store/modules/app'
|
||||
import type { RouteRecordRaw, RouteLocationNormalizedLoaded } from 'vue-router'
|
||||
import SiderItem from './SiderItem.vue'
|
||||
// import variables from '@/styles/variables.less'
|
||||
import config from '_p/index/config'
|
||||
const { show_logo } = config
|
||||
import variables from '@/styles/variables.less'
|
||||
import { isExternal } from '@/utils/validate'
|
||||
|
||||
export default defineComponent({
|
||||
components: { SiderItem },
|
||||
@@ -45,12 +50,23 @@ export default defineComponent({
|
||||
return path
|
||||
})
|
||||
const collapsed = computed(() => appStore.collapsed)
|
||||
const showLogo = computed(() => appStore.showLogo)
|
||||
|
||||
function selectMenu(path: string) {
|
||||
if (isExternal(path)) {
|
||||
window.open(path)
|
||||
} else {
|
||||
push(path)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
routers,
|
||||
activeMenu,
|
||||
collapsed,
|
||||
show_logo
|
||||
showLogo,
|
||||
variables,
|
||||
selectMenu
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -59,12 +75,24 @@ export default defineComponent({
|
||||
<style lang="less" scoped>
|
||||
.sidebar-container {
|
||||
height: 100%;
|
||||
background: @menuBg;
|
||||
@{deep}(.svg-icon) {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
.has-logo {
|
||||
height: calc(~"100% - @{topSilderHeight}");
|
||||
}
|
||||
.menu-wrap {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
// .menu-wrap {
|
||||
// height: 100%;
|
||||
// overflow: hidden;
|
||||
// @{deep}(.el-scrollbar__wrap) {
|
||||
// overflow-x: hidden;
|
||||
// overflow-y: auto;
|
||||
// }
|
||||
// @{deep}(.el-menu) {
|
||||
// border-right: none;
|
||||
// width: 100%;
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user