Skip to content

Commit

Permalink
feat(面包屑): 增加侧边栏面包屑导航
Browse files Browse the repository at this point in the history
  • Loading branch information
hesetiema committed May 7, 2024
1 parent 23577f2 commit 8f39df8
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 43 deletions.
118 changes: 94 additions & 24 deletions src/layout/AsideView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { useCollapseStore } from '@/stores/collapse'
interface IItemBase {
title: string
key: string
children?: IItemBase[]
index?: string
}
interface ISubMenuItem extends Partial<IItemBase> {
Expand All @@ -16,7 +18,7 @@ interface ISubMenuItem extends Partial<IItemBase> {
icon?: ComponentPublicInstance
}
interface IMenuItem extends IItemBase {
interface IMenuItem extends Omit<IItemBase, 'children'> {
icon?: ComponentPublicInstance
children?: ISubMenuItem[]
disabled?: boolean
Expand All @@ -27,7 +29,8 @@ const defaultItems: IMenuItem[] = [
{
title: 'DashBoard',
icon: rawComponent(Histogram),
key: 'index'
key: 'index',
index: '/admin'
},
{
title: '组件',
Expand All @@ -37,30 +40,35 @@ const defaultItems: IMenuItem[] = [
{
groupTitle: '列表',
children: [
{ title: '普通列表', key: 'base-table' },
{ title: '普通列表', key: 'base-table', index: '/admin/base-table' },
{
title: '高级列表',
key: 'advanced-table'
key: 'advanced-table',
index: '/admin/advanced-table'
}
]
},
{
groupTitle: '表单',
children: [
{ title: '普通表单', key: 'base-form' },
{ title: '普通表单', key: 'base-form', index: '/admin/base-form' },
{
title: '高级表单',
key: 'advanced-form'
key: 'advanced-form',
children: [
{
title: '文件上传',
key: 'file-upload',
index: '/admin/advanced-form/file-upload'
},
{
title: '图片裁剪',
key: 'img-crop',
index: '/admin/advanced-form/img-crop'
}
]
}
]
},
{
title: '文件上传',
key: 'file-upload'
},
{
title: '图片裁剪',
key: 'img-crop'
}
]
},
Expand All @@ -71,23 +79,65 @@ const defaultItems: IMenuItem[] = [
children: [
{
title: '水印',
key: 'watermark'
key: 'watermark',
index: '/admin/watermark'
},
{
title: '音乐播放器',
key: 'player'
key: 'player',
index: '/admin/player'
}
]
},
{
title: '其他功能',
icon: rawComponent(Setting),
key: 'other-func'
key: 'other-func',
index: '/admin/other-func'
}
]
const menuItems = reactive(defaultItems)
const flattenTree = (root) => {
const flattened = []
const stack = [root]
while (stack.length > 0) {
const node = stack.pop()
if (!Array.isArray(node)) {
flattened.push(node)
}
for (let i = node.length - 1; i >= 0; i--) {
if (node[i]?.index) {
stack.push(node[i])
}
if (node[i]?.children?.length) {
stack.push(node[i]?.children)
}
}
}
return flattened
}
const route = useRoute()
const activeIndex = computed(() => {
const current = route.path
const flattenMenus = flattenTree(defaultItems)
const flattenMenuPaths = flattenMenus.map((v) => v.index)
let matchedPath = current
if (!flattenMenuPaths.includes(current)) {
const matched = [...route.matched]
while (matched.length > 0) {
const node = matched.pop()
if (flattenMenuPaths.includes(node.path)) {
matchedPath = node.path
break
}
}
}
return matchedPath
})
const store = useCollapseStore()
const showCollapse = computed(() => store.status === 'collapse')
const isCollapse = computed(() => store.status === 'collapse' && store.collapse)
Expand All @@ -106,7 +156,7 @@ const toggleCollapse = () => {
height="100%"
>
<el-menu
:default-active="route.name?.toString()"
:default-active="activeIndex"
:collapse="isCollapse"
router
class="aside-menu-vertical"
Expand All @@ -130,29 +180,49 @@ const toggleCollapse = () => {
<el-menu-item-group :title="submenu?.groupTitle" v-if="submenu?.groupTitle">
<template v-if="submenu?.children?.length">
<template v-for="child in submenu.children" :key="child?.key">
<el-menu-item :index="child.key">{{ child.title }}</el-menu-item>
<template v-if="child?.children?.length">
<el-sub-menu :index="child.key">
<template #title>{{ child.title }}</template>
<template v-for="grandChild in child.children" :key="grandChild?.key">
<el-menu-item :index="grandChild.index">{{
grandChild.title
}}</el-menu-item>
</template>
</el-sub-menu>
</template>
<el-menu-item v-else :index="child?.index">{{ child.title }}</el-menu-item>
</template>
</template>
<el-menu-item :index="submenu.key" v-else>{{ submenu.title }}</el-menu-item>
<el-menu-item :index="submenu.index" v-else>{{ submenu.title }}</el-menu-item>
</el-menu-item-group>
<el-sub-menu :index="submenu?.key" v-else-if="submenu?.children?.length">
<el-sub-menu :index="submenu.key" v-else-if="submenu?.children?.length">
<template #title>{{ submenu?.title }}</template>
<template v-for="child in submenu.children" :key="child?.key">
<el-menu-item :index="child.key">{{ child.title }}</el-menu-item>
<template v-if="child?.children?.length">
<el-sub-menu :index="child.key">
<template #title>{{ child.title }}</template>
<template v-for="grandChild in child.children" :key="grandChild?.key">
<el-menu-item :index="grandChild.index">{{
grandChild.title
}}</el-menu-item>
</template>
</el-sub-menu>
</template>
<el-menu-item v-else :index="child.index">{{ child.title }}</el-menu-item>
</template>
</el-sub-menu>
<el-menu-item :index="submenu?.key" v-else>
<el-menu-item :index="submenu?.index" v-else>
<el-icon v-if="submenu?.icon">
<component :is="submenu?.icon" />
</el-icon>
<span>{{ submenu?.title }}</span>
</el-menu-item>
</template>
</el-sub-menu>
<el-menu-item :index="menu.key" :disabled="menu.disabled" v-else>
<el-menu-item :index="menu.index" :disabled="menu.disabled" v-else>
<el-icon v-if="menu?.icon">
<component :is="menu?.icon" />
</el-icon>
Expand Down
30 changes: 30 additions & 0 deletions src/layout/BreadCrumb.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script lang="ts" setup>
import { useRoute } from 'vue-router'
import { computed } from 'vue'
const route = useRoute()
const matchedParentArr = computed(() => {
const showArr = route.matched.filter((v) => !!v?.meta?.title)
return showArr
})
</script>

<template>
<el-breadcrumb separator="/" v-show="matchedParentArr.length > 1">
<template v-for="item in matchedParentArr" :key="item.path">
<el-breadcrumb-item v-if="item.meta.hideBreadCrumb">
{{ item.meta.title ?? '' }}
</el-breadcrumb-item>
<el-breadcrumb-item v-else :to="{ path: item.path }">
{{ item.meta.title ?? '' }}
</el-breadcrumb-item>
</template>
</el-breadcrumb>
</template>

<style scoped>
.el-breadcrumb {
margin-bottom: 24px;
}
</style>
4 changes: 3 additions & 1 deletion src/layout/LayOut.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
<script lang="ts" setup>
import HeaderView from './HeaderView.vue'
import AsideView from './AsideView.vue'
import BreadCrumb from './BreadCrumb.vue'
import { RouterView } from 'vue-router'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useCollapseStore } from '@/stores/collapse'
const containerRef = ref()
const store = useCollapseStore()
const showDrawer = computed(() => store.status === 'drawer')
const isDrawerOpen = computed(() => store.drawer)
const isCollapse = computed(() => store.collapse)
const handleWidthChange = (width: number) => {
Expand Down Expand Up @@ -93,6 +94,7 @@ onUnmounted(() => {
'margin-left': showDrawer ? '0px' : isCollapse ? '64px' : '164px'
}"
>
<BreadCrumb />
<RouterView></RouterView>
</el-main>
</el-container>
Expand Down
80 changes: 62 additions & 18 deletions src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,52 +43,96 @@ const routes: RouteRecordRaw[] = [
{
path: 'index',
name: 'index',
component: () => import('@/views/admin/DashBoard.vue')
component: () => import('@/views/admin/DashBoard.vue'),
meta: {
hideBreadCrumb: true,
title: 'DashBoard'
}
},
{
path: 'base-table',
name: 'base-table',
component: () => import('@/views/admin/TableBase.vue')
component: () => import('@/views/admin/TableBase.vue'),
meta: {
title: '普通列表'
}
},
{
path: 'advanced-table',
name: 'advanced-table',
component: () => import('@/views/admin/TableAdvanced.vue')
component: () => import('@/views/admin/TableAdvanced.vue'),
meta: {
title: '高级列表'
}
},
{
path: 'base-form',
name: 'base-form',
component: () => import('@/views/admin/FormBase.vue')
component: () => import('@/views/admin/FormBase.vue'),
meta: {
title: '普通表单'
},
children: [
{
path: 'text-ellipse',
name: 'text-ellipse',
component: () => import('@/views/admin/TableBase.vue'),
meta: {
title: '文本省略'
}
}
]
},
{
path: 'advanced-form',
name: 'advanced-form',
component: () => import('@/views/admin/TableBase.vue')
},
{
path: 'file-upload',
name: 'file-upload',
component: () => import('@/views/admin/TableBase.vue')
},
{
path: 'img-crop',
name: 'img-crop',
component: () => import('@/views/admin/TableBase.vue')
component: () => import('@/views/admin/FormAdvanced.vue'),
meta: {
title: '高级表单',
hideBreadCrumb: true
},
children: [
{
path: 'file-upload',
name: 'file-upload',
component: () => import('@/views/admin/TableBase.vue'),
meta: {
title: '文件上传'
}
},
{
path: 'img-crop',
name: 'img-crop',
component: () => import('@/views/admin/TableBase.vue'),
meta: {
title: '图片裁剪'
}
}
]
},
{
path: 'watermark',
name: 'watermark',
component: () => import('@/views/admin/TableBase.vue')
component: () => import('@/views/admin/TableBase.vue'),
meta: {
title: '水印'
}
},
{
path: 'player',
name: 'player',
component: () => import('@/views/admin/TableBase.vue')
component: () => import('@/views/admin/TableBase.vue'),
meta: {
title: '音乐播放器'
}
},
{
path: 'other-func',
name: 'other-func',
component: () => import('@/views/admin/TableBase.vue')
component: () => import('@/views/admin/TableBase.vue'),
meta: {
title: '其他功能'
}
}
]
}
Expand Down
Loading

0 comments on commit 8f39df8

Please sign in to comment.