
12 changed files with 1137 additions and 193 deletions
File diff suppressed because it is too large
@ -0,0 +1,7 @@ |
|||
import Breadcrumb from './src/index.vue' |
|||
|
|||
Breadcrumb.install = function(Vue) { |
|||
Vue.component(Breadcrumb.name || 'Breadcrumb', Breadcrumb) |
|||
} |
|||
|
|||
export default Breadcrumb |
@ -0,0 +1,160 @@ |
|||
<template> |
|||
<div v-if="breadcrumbs.length > 0" class="breadcrumb-container flex-between"> |
|||
<nav> |
|||
<ol class="breadcrumb"> |
|||
<li |
|||
v-for="(item, index) in breadcrumbs" |
|||
:key="index" |
|||
class="breadcrumb-item" |
|||
:class="{ active: index === breadcrumbs.length - 1 }" |
|||
> |
|||
<template v-if="index !== breadcrumbs.length - 1"> |
|||
<router-link to="/" v-if="item.title == '首页'"> |
|||
<SvgIcon1 :iconPath="require(`../../assets/home-bread.svg`)" defaultColor="#8A9099" :size="16" activeColor="#006AFF"/> |
|||
</router-link> |
|||
<router-link v-else :to="item.path">{{ item.title }}</router-link> |
|||
<img class="separator" src="../../assets/separator.png" alt=""> |
|||
</template> |
|||
<template v-else> |
|||
<span>{{ item.title }}</span> |
|||
</template> |
|||
</li> |
|||
</ol> |
|||
</nav> |
|||
<div v-if="breadRightText" class="gap8 breadRight"> |
|||
<img class="ml-8" src="../../assets/bind_sites.svg" alt="" /> |
|||
站点简称:<a :href="breadRightText">{{ breadRightText }}</a> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script> |
|||
import SvgIcon1 from '../..//SvgIcon1'; |
|||
export default { |
|||
name: 'Breadcrumb', |
|||
components: { |
|||
SvgIcon1, |
|||
}, |
|||
computed: { |
|||
breadcrumbs() { |
|||
if (this.$route.meta.hideBreadcrumb) return []; |
|||
|
|||
const crumbs = []; |
|||
let currentRoute = this.$route; |
|||
|
|||
// 递归查找所有父级路由 |
|||
while (currentRoute) { |
|||
// 获取匹配的路由记录 |
|||
// const matchedRoute = this.$router.options.routes.find( |
|||
// r => r.name === currentRoute.name |
|||
// ); |
|||
|
|||
// 构建包含完整参数的对象 |
|||
const routeWithParams = { |
|||
path: currentRoute.path, |
|||
query: currentRoute.query, |
|||
params: currentRoute.params |
|||
}; |
|||
|
|||
crumbs.unshift({ |
|||
path: routeWithParams, |
|||
title: this.getTitle(currentRoute) |
|||
}); |
|||
|
|||
// 通过 meta.breadcrumbParent 查找父级路由 |
|||
if (currentRoute.meta.breadcrumbParent) { |
|||
currentRoute = this.$router.options.routes.find( |
|||
r => r.name === currentRoute.meta.breadcrumbParent |
|||
); |
|||
|
|||
// 如果找到了父路由,创建一个模拟的$route对象 |
|||
if (currentRoute) { |
|||
currentRoute = { |
|||
...currentRoute, |
|||
path: currentRoute.path, |
|||
query: this.$route.query, // 保留当前查询参数 |
|||
params: this.$route.params, // 保留当前路由参数 |
|||
meta: currentRoute.meta || {} |
|||
}; |
|||
} |
|||
} else { |
|||
currentRoute = null; |
|||
} |
|||
} |
|||
|
|||
return crumbs; |
|||
}, |
|||
breadRightText() { |
|||
return this.$store && this.$store.state.breadRightText |
|||
} |
|||
}, |
|||
methods: { |
|||
getTitle(route) { |
|||
return typeof route.meta.title === 'function' |
|||
? route.meta.title(route) |
|||
: route.meta.title || route.name; |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
<style scoped lang="scss"> |
|||
/* 保持之前的样式不变 */ |
|||
.breadcrumb-container { |
|||
padding: 16px 12px; |
|||
background-color: #f5f5f5; |
|||
border-radius: 4px; |
|||
.breadRight{ |
|||
a{ |
|||
text-decoration: none; |
|||
color: #006AFF; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.home-icon { |
|||
width: 16px; |
|||
height: 16px; |
|||
} |
|||
|
|||
.breadcrumb { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
height: 22px; |
|||
align-items: center; |
|||
padding: 0; |
|||
margin: 0; |
|||
list-style: none; |
|||
} |
|||
|
|||
.breadcrumb-item { |
|||
display: flex; |
|||
align-items: center; |
|||
height: 100%; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.router-link-active { |
|||
height: 100%; |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.breadcrumb-item a { |
|||
color: #626573; |
|||
text-decoration: none; |
|||
|
|||
&:hover { |
|||
color: #006AFF; |
|||
} |
|||
} |
|||
|
|||
.breadcrumb-item.active span { |
|||
color: #1E2226; |
|||
; |
|||
|
|||
} |
|||
|
|||
.separator { |
|||
width: 12px; |
|||
height: 12px; |
|||
} |
|||
</style> |
@ -0,0 +1,7 @@ |
|||
import SvgIcon1 from './src/index.vue' |
|||
|
|||
SvgIcon1.install = function(Vue) { |
|||
Vue.component(SvgIcon1.name || 'SvgIcon1', SvgIcon1) |
|||
} |
|||
|
|||
export default SvgIcon1 |
@ -0,0 +1,175 @@ |
|||
<template> |
|||
<div class="svg-icon-wrapper" :style="wrapperStyle" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave"> |
|||
<div class="svg-icon" v-html="svgContent" :style="{ |
|||
'--icon-color': (hoverEffect || isActive) ? activeColor : defaultColor, |
|||
'--icon-hover-color': activeColor |
|||
}"></div> |
|||
<!-- :style="iconStyle" --> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'SvgIcon1', |
|||
props: { |
|||
// 图标路径(必须) |
|||
iconPath: { |
|||
type: String, |
|||
required: true |
|||
}, |
|||
// 默认颜色 |
|||
defaultColor: { |
|||
type: String, |
|||
default: '#606266' |
|||
}, |
|||
// 激活颜色(传入后才允许变色) |
|||
activeColor: { |
|||
type: String, |
|||
default: null |
|||
}, |
|||
// 是否开启悬停变色 |
|||
hoverEffect: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
// 图标大小 |
|||
size: { |
|||
type: [String, Number], |
|||
default: '14px' |
|||
}, |
|||
// 是否当前激活状态 |
|||
isActive: { |
|||
type: Boolean, |
|||
default: false |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
svgContent: '', |
|||
isHovering: false, |
|||
currentColor: this.defaultColor |
|||
} |
|||
}, |
|||
computed: { |
|||
wrapperStyle() { |
|||
return { |
|||
width: typeof this.size === 'number' ? `${this.size}px` : this.size, |
|||
height: typeof this.size === 'number' ? `${this.size}px` : this.size, |
|||
display: 'inline-flex', |
|||
alignItems: 'center', |
|||
justifyContent: 'center' |
|||
} |
|||
}, |
|||
iconStyle() { |
|||
return { |
|||
width: '100%', |
|||
height: '100%', |
|||
color: this.currentColor, |
|||
transition: 'color 0.3s ease' |
|||
} |
|||
}, |
|||
shouldChangeColor() { |
|||
return this.activeColor && (this.hoverEffect || this.isActive) |
|||
} |
|||
}, |
|||
watch: { |
|||
isActive(newVal) { |
|||
console.log(newVal, 'newVal---'); |
|||
if (this.shouldChangeColor) { |
|||
this.currentColor = newVal ? this.activeColor : this.defaultColor |
|||
console.log(this.currentColor, 'this.currentColor--'); |
|||
} |
|||
}, |
|||
defaultColor(newVal) { |
|||
if (!this.isHovering && !this.isActive) { |
|||
this.currentColor = newVal |
|||
} |
|||
}, |
|||
activeColor() { |
|||
this.updateColorState() |
|||
} |
|||
}, |
|||
methods: { |
|||
async loadSvg() { |
|||
try { |
|||
const response = await fetch(this.iconPath) |
|||
this.svgContent = await response.text() |
|||
this.processSvg() |
|||
} catch (error) { |
|||
console.error('Failed to load SVG:', error) |
|||
} |
|||
}, |
|||
processSvg() { |
|||
// 确保SVG没有自带颜色,以便用CSS控制 |
|||
this.$nextTick(() => { |
|||
const svgElement = this.$el.querySelector('svg') |
|||
if (svgElement) { |
|||
// 更彻底地移除颜色属性 |
|||
svgElement.removeAttribute('fill') |
|||
svgElement.removeAttribute('style') |
|||
const paths = svgElement.querySelectorAll('path, circle, rect, polygon') |
|||
paths.forEach(el => { |
|||
el.removeAttribute('fill') |
|||
}) |
|||
svgElement.style.fill = 'currentColor' |
|||
svgElement.style.width = '100%' |
|||
svgElement.style.height = '100%' |
|||
} |
|||
}) |
|||
}, |
|||
handleMouseEnter() { |
|||
this.isHovering = true |
|||
this.updateColorState() |
|||
}, |
|||
handleMouseLeave() { |
|||
this.isHovering = false |
|||
this.updateColorState() |
|||
}, |
|||
updateColorState() { |
|||
if (this.activeColor) { |
|||
if (this.isActive) { |
|||
this.currentColor = this.activeColor |
|||
} else { |
|||
this.currentColor = this.isHovering && this.hoverEffect ? |
|||
this.activeColor : |
|||
this.defaultColor |
|||
} |
|||
} else { |
|||
this.currentColor = this.defaultColor |
|||
} |
|||
} |
|||
}, |
|||
created() { |
|||
this.loadSvg() |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.svg-icon-wrapper { |
|||
cursor: pointer; |
|||
} |
|||
|
|||
|
|||
.svg-icon { |
|||
width: 100%; |
|||
height: 100%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
color: var(--icon-color); |
|||
transition: color 0.3s; |
|||
} |
|||
|
|||
.svg-icon:hover { |
|||
color: var(--icon-hover-color); |
|||
} |
|||
|
|||
.svg-icon { |
|||
display: inline-flex; |
|||
} |
|||
|
|||
.svg-icon>>>svg { |
|||
fill: currentColor; |
|||
} |
|||
</style> |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 662 B |
@ -0,0 +1,5 @@ |
|||
module.exports = { |
|||
plugins: [ |
|||
require('autoprefixer')({ grid: true }) |
|||
] |
|||
} |
Loading…
Reference in new issue