Vue3面包屑(Breadcrumb)

简介: 该Breadcrumb组件允许自定义设置多个属性,包括路由数组、面包屑类名和样式、文本最大显示宽度、分隔符及样式、以及目标URL的打开方式。通过这些配置项,可以轻松实现不同样式的面包屑导航。组件支持点击跳转,并且能够处理带查询参数的路径。在线预览展示了其丰富的定制功能。可通过引入并在页面中使用该组件来快速构建导航结构。

可自定义设置以下属性:

  • router 的路由数组(routes),类型:Array<{name: string, path?: string, query?: { [propName: string]: any } }>,默认 []

  • 设置面包屑类名(breadcrumbClass),类型:string,默认 undefined

  • 设置面包屑样式(breadcrumbStyle),类型:CSSProperties,默认 {}

  • 设置文本最大显示宽度,超出后显示省略号(maxWidth),类型:string | number,单位 px,默认 '100%'

  • 自定义分隔符(separator),类型:string | slot,默认 undefined,默认使用 > 分隔

  • 设置分隔符样式(separatorStyle),类型:CSSProperties,默认 {}

  • 如何打开目标URL,当前窗口或新窗口(target),类型:'_self' | '_blank',默认 '_self'

效果如下图:在线预览

①创建面包屑组件Breadcrumb.vue:

<script setup lang="ts">
import { computed } from 'vue'
import type { CSSProperties } from 'vue'
interface Query {
  [propName: string]: any // 添加一个字符串索引签名,用于包含带有任意数量的其他属性
}
interface Route {
  name: string // 路由名称
  path?: string // 路由地址
  query?: Query // 路由查询参数
}
interface Props {
  routes?: Route[] // router 路由数组
  breadcrumbClass?: string // 设置面包屑类名
  breadcrumbStyle?: CSSProperties // 设置面包屑样式
  maxWidth?: string | number // 设置文本最大显示宽度,超出后显示省略号,单位 px
  separator?: string // 自定义分隔符,默认为 > string | slot
  separatorStyle?: CSSProperties // 设置分隔符样式
  target?: '_self' | '_blank' // 如何打开目标 URL,当前窗口或新窗口
}
const props = withDefaults(defineProps<Props>(), {
  routes: () => [],
  breadcrumbClass: undefined,
  breadcrumbStyle: () => ({}),
  maxWidth: '100%',
  separator: undefined,
  separatorStyle: () => ({}),
  target: '_self'
})
const len = computed(() => {
  return props.routes.length
})
function getUrl(route: Route) {
  var targetUrl = route.path
  if (route.query && JSON.stringify(route.query) !== '{}') {
    const query = route.query
    Object.keys(query).forEach((param, index) => {
      if (index === 0) {
        targetUrl = targetUrl + '?' + param + '=' + query[param]
      } else {
        targetUrl = targetUrl + '&' + param + '=' + query[param]
      }
    })
  }
  return targetUrl
}
</script>
<template>
  <div class="m-breadcrumb" :class="breadcrumbClass" :style="breadcrumbStyle">
    <div class="m-breadcrumb-item" v-for="(route, index) in routes" :key="index">
      <a
        v-if="route.path"
        class="breadcrumb-link link-hover"
        :class="{ 'link-active': index === len - 1 }"
        :style="`max-width: ${maxWidth}px;`"
        :href="getUrl(route)"
        :target="target"
        :title="route.name"
      >
        {
  { route.name }}
      </a>
      <span
        v-else
        class="breadcrumb-link"
        :class="{ 'link-active': index === len - 1 }"
        :style="`max-width: ${maxWidth}px;`"
        :title="route.name"
      >
        {
  { route.name }}
      </span>
      <span v-if="index < len - 1" class="breadcrumb-separator" :style="separatorStyle">
        <slot name="separator" :index="index">
          <span v-if="separator">{
  { separator }}</span>
          <svg v-else class="icon-arrow" viewBox="64 64 896 896" data-icon="right" aria-hidden="true" focusable="false">
            <path
              d="M765.7 486.8L314.9 134.7A7.97 7.97 0 0 0 302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 0 0 0-50.4z"
            ></path>
          </svg>
        </slot>
      </span>
    </div>
  </div>
</template>
<style lang="less" scoped>
.m-breadcrumb {
  font-size: 14px;
  color: rgba(0, 0, 0, 0.45);
  line-height: 1.5714285714285714;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  .m-breadcrumb-item {
    display: inline-flex;
    align-items: center;
    .breadcrumb-link {
      display: inline-block;
      color: rgba(0, 0, 0, 0.45);
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;
      padding: 0 4px;
      border-radius: 4px;
      text-decoration: none;
      cursor: text;
      transition:
        color 0.2s,
        background-color 0.2s;
    }
    .link-hover {
      cursor: pointer;
      &:hover {
        background-color: rgba(0, 0, 0, 0.06);
        color: rgba(0, 0, 0, 0.88);
      }
    }
    .link-active {
      color: rgba(0, 0, 0, 0.88);
    }
    .breadcrumb-separator {
      display: inline-flex;
      align-items: center;
      margin: 0 4px;
      color: rgba(0, 0, 0, 0.45);
      :deep(svg) {
        margin-inline: -2px;
      }
      .icon-arrow {
        display: inline-block;
        width: 1em;
        height: 1em;
        fill: rgba(0, 0, 0, 0.45);
      }
    }
  }
}
</style>

②在要使用的页面引入:

<script setup lang="ts">
import Breadcrumb from './Breadcrumb.vue'
import { ref } from 'vue'
const routes = ref([
  {
    name: '一级路由',
    path: '/first'
  },
  {
    name: '二级路由',
    path: '/second',
    query: { id: 1, tab: 2 }
  },
  {
    name: '三级路由'
  }
])
const longNameRoutes = ref([
  {
    name: '一级路由'
  },
  {
    name: '二级路由',
    path: '/second',
    query: { id: 1, tab: 2 }
  },
  {
    name: '我是一个名字超长的三级路由'
  }
])
</script>
<template>
  <div>
    <h1>{
  { $route.name }} {
  { $route.meta.title }}</h1>
    <h2 class="mt30 mb10">基本使用</h2>
    <Breadcrumb :routes="routes" />
    <h2 class="mt30 mb10">自定义分隔符</h2>
    <Flex vertical>
      <Breadcrumb :routes="routes" separator="/" />
      <Breadcrumb :routes="routes">
        <template #separator>
          <svg
            class="svg-icon"
            focusable="false"
            data-icon="arrow-right"
            width="1em"
            height="1em"
            fill="currentColor"
            aria-hidden="true"
            viewBox="64 64 896 896"
          >
            <path
              d="M869 487.8L491.2 159.9c-2.9-2.5-6.6-3.9-10.5-3.9h-88.5c-7.4 0-10.8 9.2-5.2 14l350.2 304H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h585.1L386.9 854c-5.6 4.9-2.2 14 5.2 14h91.5c1.9 0 3.8-.7 5.2-2L869 536.2a32.07 32.07 0 000-48.4z"
            ></path>
          </svg>
        </template>
      </Breadcrumb>
      <Breadcrumb :routes="routes">
        <template #separator>
          <svg
            class="svg-icon"
            focusable="false"
            data-icon="double-right"
            width="1em"
            height="1em"
            fill="currentColor"
            aria-hidden="true"
            viewBox="64 64 896 896"
          >
            <path
              d="M533.2 492.3L277.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H188c-6.7 0-10.4 7.7-6.3 12.9L447.1 512 181.7 851.1A7.98 7.98 0 00188 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5zm304 0L581.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H492c-6.7 0-10.4 7.7-6.3 12.9L751.1 512 485.7 851.1A7.98 7.98 0 00492 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5z"
            ></path>
          </svg>
        </template>
      </Breadcrumb>
    </Flex>
    <h2 class="mt30 mb10">自定义样式</h2>
    <Flex vertical>
      <Breadcrumb
        :routes="longNameRoutes"
        breadcrumb-class="custom-breadcrumb-class"
        :max-width="210" />
      <Breadcrumb
        :routes="longNameRoutes"
        :breadcrumb-style="{ fontSize: '20px', height: '40px' }"
        :separator-style="{ fontSize: '18px' }"
        :max-width="210" />
      <Breadcrumb
        :routes="longNameRoutes"
        :breadcrumb-style="{ fontSize: '20px', height: '40px' }"
        :separator-style="{ fontSize: '18px' }"
        :max-width="210">
        <template #separator>
          <svg
            class="svg-icon"
            focusable="false"
            data-icon="arrow-right"
            width="1em"
            height="1em"
            fill="currentColor"
            aria-hidden="true"
            viewBox="64 64 896 896"
          >
            <path
              d="M869 487.8L491.2 159.9c-2.9-2.5-6.6-3.9-10.5-3.9h-88.5c-7.4 0-10.8 9.2-5.2 14l350.2 304H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h585.1L386.9 854c-5.6 4.9-2.2 14 5.2 14h91.5c1.9 0 3.8-.7 5.2-2L869 536.2a32.07 32.07 0 000-48.4z"
            ></path>
          </svg>
        </template>
      </Breadcrumb>
      <Breadcrumb
        :routes="longNameRoutes"
        :breadcrumb-style="{ fontSize: '20px', height: '40px' }"
        :separator-style="{ fontSize: '18px' }"
        :max-width="210">
        <template #separator="{ index }">
          <svg
            v-if="index === 0"
            class="svg-icon"
            focusable="false"
            data-icon="arrow-right"
            width="1em"
            height="1em"
            fill="currentColor"
            aria-hidden="true"
            viewBox="64 64 896 896"
          >
            <path
              d="M869 487.8L491.2 159.9c-2.9-2.5-6.6-3.9-10.5-3.9h-88.5c-7.4 0-10.8 9.2-5.2 14l350.2 304H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h585.1L386.9 854c-5.6 4.9-2.2 14 5.2 14h91.5c1.9 0 3.8-.7 5.2-2L869 536.2a32.07 32.07 0 000-48.4z"
            ></path>
          </svg>
          <svg
            v-if="index === 1"
            class="svg-icon"
            focusable="false"
            data-icon="double-right"
            width="1em"
            height="1em"
            fill="currentColor"
            aria-hidden="true"
            viewBox="64 64 896 896"
          >
            <path
              d="M533.2 492.3L277.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H188c-6.7 0-10.4 7.7-6.3 12.9L447.1 512 181.7 851.1A7.98 7.98 0 00188 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5zm304 0L581.9 166.1c-3-3.9-7.7-6.1-12.6-6.1H492c-6.7 0-10.4 7.7-6.3 12.9L751.1 512 485.7 851.1A7.98 7.98 0 00492 864h77.3c4.9 0 9.6-2.3 12.6-6.1l255.3-326.1c9.1-11.7 9.1-27.9 0-39.5z"
            ></path>
          </svg>
        </template>
      </Breadcrumb>
    </Flex>
    <h2 class="mt30 mb10">新页面打开目标链接</h2>
    <Breadcrumb :routes="routes" target="_blank" />
  </div>
</template>
<style lang="less" scoped>
.svg-icon {
  fill: rgba(0, 0, 0, 0.45);
}
.custom-breadcrumb-class {
  font-size: 20px;
  color: #1677ff;
  height: 40px;
  :deep(.m-breadcrumb-item) {
    .breadcrumb-link {
      color: rgba(22, 119, 255, 0.72);
      padding: 0 8px;
      border-radius: 8px;
    }
    .link-hover {
      &:hover {
        color: #fff;
        background: #4096ff;
      }
    }
    .link-active {
      color: #1677ff;
    }
    .breadcrumb-separator {
      .icon-arrow {
        fill: rgba(22, 119, 255, 0.72);
      }
    }
  }
}
</style>
相关文章
|
4天前
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
106 64
|
4天前
|
JavaScript 前端开发 API
Vue 3 中 v-model 与 Vue 2 中 v-model 的区别是什么?
总的来说,Vue 3 中的 `v-model` 在灵活性、与组合式 API 的结合、对自定义组件的支持等方面都有了明显的提升和改进,使其更适应现代前端开发的需求和趋势。但需要注意的是,在迁移过程中可能需要对一些代码进行调整和适配。
|
29天前
|
存储 JavaScript 前端开发
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
【10月更文挑战第21天】 vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
vue3的脚手架模板你真的了解吗?里面有很多值得我们学习的地方!
|
2月前
|
API
vue3知识点:provide 与 inject
vue3知识点:provide 与 inject
34 4
vue3知识点:provide 与 inject
|
2月前
|
API
vue3知识点:readonly 与 shallowReadonly
vue3知识点:readonly 与 shallowReadonly
25 1
vue3知识点:readonly 与 shallowReadonly
|
26天前
|
JavaScript 前端开发 开发者
Vue 3中的Proxy
【10月更文挑战第23天】Vue 3中的`Proxy`为响应式系统带来了更强大、更灵活的功能,解决了Vue 2中响应式系统的一些局限性,同时在性能方面也有一定的提升,为开发者提供了更好的开发体验和性能保障。
52 7
|
27天前
|
前端开发 数据库
芋道框架审批流如何实现(Cloud+Vue3)
芋道框架审批流如何实现(Cloud+Vue3)
46 3
|
26天前
|
JavaScript 数据管理 Java
在 Vue 3 中使用 Proxy 实现数据双向绑定的性能如何?
【10月更文挑战第23天】Vue 3中使用Proxy实现数据双向绑定在多个方面都带来了性能的提升,从更高效的响应式追踪、更好的初始化性能、对数组操作的优化到更优的内存管理等,使得Vue 3在处理复杂的应用场景和大量数据时能够更加高效和稳定地运行。
41 1
|
26天前
|
JavaScript 开发者
在 Vue 3 中使用 Proxy 实现数据的双向绑定
【10月更文挑战第23天】Vue 3利用 `Proxy` 实现了数据的双向绑定,无论是使用内置的指令如 `v-model`,还是通过自定义事件或自定义指令,都能够方便地实现数据与视图之间的双向交互,满足不同场景下的开发需求。
46 1
|
29天前
|
前端开发 JavaScript
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef