面包屑导航
说明:实现基础面包屑导航。
1.实现效果
2.面包屑组件
<template>
<el-breadcrumb :separator-icon="ArrowRightIcon">
<transition-group name="breadcrumb" @enter="onEnter" :css="false">
<!-- transition-group自带过渡效果 -->
<!-- <transition-group name="breadcrumb"> -->
<el-breadcrumb-item
:to="{ path: item.name }"
v-for="(item, index) in breadcrumbData"
:key="item.name"
:data-index="index"
>
<span>{{ $t(item.meta?.title) }}</span>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script setup lang="tsx">
import { ArrowRight } from '@element-plus/icons-vue'
import { TransitionGroup } from 'vue'
import { useRoute } from 'vue-router/auto'
import gsap from 'gsap'
const ArrowRightIcon = () => (
<el-icon>
<ArrowRight />
</el-icon>
)
const route = useRoute()
const breadcrumbData = ref<any>([])
// 获取面包屑数据
function getBreadcrumbData() {
breadcrumbData.value = route.matched.filter((item) => item.name && item.meta && item.meta?.title)
}
// 监听路由变化
watch(
route,
() => {
getBreadcrumbData()
},
{ immediate: true }
)
// 过渡动画库
function onEnter(el, done) {
gsap.fromTo(
el,
{
opacity: 0,
x: 30
},
{
opacity: 1,
x: 0,
delay: el.dataset.index * 0.15,
onComplete: done
}
)
}
function onLeave(el, done) {
gsap.to(el, {
opacity: 0,
height: 0,
duration: 0.5,
onComplete: done
})
}
</script>
<style scoped lang="scss">
.breadcrumb-move,
.breadcrumb-enter-active,
.breadcrumb-leave-active {
transition: all 0.5s;
}
.breadcrumb-enter-from,
.breadcrumb-leave-to {
opacity: 0;
transform: translateX(20px);
}
.breadcrumb-leave-active {
position: absolute;
}
</style>
3.布局的Header.vue中引用
<template>
<el-row class="flex items-center flex-nowrap! h-[50px]">
<!-- 折叠图标 -->
<Icon
:icon="collapseModel ? 'ep:expand' : 'ep:fold'"
@click="collapseModel = !collapseModel"
class="cursor-pointer text-2xl ml-2"
v-if="setting?.mode !== 'top'"
/>
<Breadcrumb class="ml-4" v-if="!['mix', 'top'].includes(setting?.mode || '')"></Breadcrumb>
<div class="flex-grow relative overflow-x-hidden">
<slot name="menu"></slot>
</div>
<el-row class="flex items-center flex-nowrap!">
<!-- 设置主题 -->
<ThemeSettings class="mr-3" @change="handleChange" v-bind="setting"></ThemeSettings>
<!-- 暗黑模式 -->
<DarkModeTaggle
class="mr-3"
:dark="setting?.darkMode"
@change="handleChangeDarkMode"
></DarkModeTaggle>
<!-- 国际化 -->
<ChangeLocale :locales="locales" class="mr-2" @change="changeLocale"></ChangeLocale>
<!-- 全屏 -->
<FullScreen class="mr-2"></FullScreen>
<el-divider direction="vertical"></el-divider>
<!-- 用户信息 -->
<Avatar
v-if="username || src"
v-bind="avatarProps"
@command="handleCommand"
class="mx-2"
></Avatar>
</el-row>
</el-row>
</template>
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import type { ThemeSettingProps } from '../Themes/type'
import type { HeaderProps } from './types'
import { loadLocaleMessages } from '@/modules/i18n'
import Breadcrumb from '../Themes/Breadcrumb.vue'
const props = withDefaults(defineProps<HeaderProps>(), {
collapse: false
})
// 使用v-model指令父子组件双向绑定,实现抽屉的展开和收起
const collapseModel = defineModel('collapse', {
type: Boolean,
default: false
})
// 获取头部导航栏数据,实现暗黑模式和主题设置中的数据转换
const localProps = reactive({ ...props })
// 过滤出头像数据
const avatarProps = computed(() => {
const { collapse, locales, ...restProps } = props
return restProps
})
// 回传数据
const emits = defineEmits<{
menuChange: [arg: string | number | object]
settingChange: [settings: ThemeSettingProps]
}>()
// 监听主题设置中的变化
watch(
() => localProps.setting,
() => {
emits('settingChange', localProps.setting!)
},
{ deep: true }
)
const handleCommand = (command: string | number | object) => {
emits('menuChange', command)
}
// 主题设置
const handleChange = (settings: ThemeSettingProps) => {
localProps.setting = settings
}
// 暗黑模式切换
const handleChangeDarkMode = (darkMode: boolean) => {
localProps.setting!.darkMode = darkMode
}
// 切换中英文
const changeLocale = (locale: string) => {
loadLocaleMessages(locale)
}
</script>
<style scoped></style>