(一)JS部分
import { computed } from "vue";
interface BreadcrumbItem {
label: string;
path?: string;
disabled?: boolean;
[key: string]: any;
}
interface EllipsisItem {
type: "ellipsis";
children: BreadcrumbItem[];
}
type DisplayItem = BreadcrumbItem | EllipsisItem;
const props = withDefaults(
defineProps<{
items: BreadcrumbItem[];
ellipsisIndex?: number;
afterCount?: number;
itemMaxWidth?: number | string;
separator?: string;
}>(),
{
items: () => [],
ellipsisIndex: 1,
afterCount: 2,
itemMaxWidth: 160,
separator: "/",
}
);
const emit = defineEmits<{
(e: "click", item: BreadcrumbItem): void;
}>();
const itemStyle = computed(() => {
const maxWidth = typeof props.itemMaxWidth === "number" ? `${props.itemMaxWidth}px` : props.itemMaxWidth;
return {
maxWidth,
};
});
function isEllipsisItem(item: DisplayItem): item is EllipsisItem {
return (item as EllipsisItem).type === "ellipsis";
}
function isCurrent(item: BreadcrumbItem) {
return props.items[props.items.length - 1] === item;
}
function handleClick(item: BreadcrumbItem) {
if (item.disabled || isCurrent(item)) return;
emit("click", item);
}
function handleCommand(item: BreadcrumbItem) {
handleClick(item);
}
function getItemKey(item: BreadcrumbItem, index: number) {
return item.path || item.label || index;
}
function getRenderKey(item: DisplayItem, index: number) {
if (isEllipsisItem(item)) return `ellipsis-${index}`;
return getItemKey(item, index);
}
const displayItems = computed<DisplayItem[]>(() => {
const list = props.items || [];
const total = list.length;
if (!total) return [];
const safeEllipsisIndex = Math.max(0, props.ellipsisIndex);
const safeAfterCount = Math.max(1, props.afterCount);
const frontCount = Math.min(safeEllipsisIndex, total);
const tailCount = Math.min(safeAfterCount, total);
const hiddenStart = frontCount;
const hiddenEnd = total - tailCount;
if (hiddenStart >= hiddenEnd) {
return list;
}
const hiddenList = list.slice(hiddenStart, hiddenEnd);
// 折叠区只有一个,直接全展示
if (hiddenList.length <= 1) {
return list;
}
const frontList = list.slice(0, hiddenStart);
const tailList = list.slice(hiddenEnd);
return [
...frontList,
{
type: "ellipsis",
children: hiddenList,
},
...tailList,
];
});
(2) html 和 css 部分
<template>
<el-breadcrumb class="smart-breadcrumb" :separator="separator">
<template v-for="(item, index) in displayItems" :key="getRenderKey(item, index)">
<el-breadcrumb-item v-if="isEllipsisItem(item)">
<el-dropdown trigger="hover" placement="bottom-start" @command="handleCommand">
<span class="smart-breadcrumb__link smart-breadcrumb__ellipsis">...</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="(hiddenItem, hiddenIndex) in item.children"
:key="getItemKey(hiddenItem, hiddenIndex)"
:command="hiddenItem"
:disabled="isCurrent(hiddenItem) || hiddenItem.disabled"
>
{{ hiddenItem.label }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-breadcrumb-item>
<!-- 普通项 -->
<el-breadcrumb-item v-else>
<span
class="smart-breadcrumb__label"
:class="{
'smart-breadcrumb__link': !isCurrent(item) && !item.disabled,
'smart-breadcrumb__current': isCurrent(item),
'smart-breadcrumb__disabled': item.disabled,
}"
:style="itemStyle"
:title="item.label"
@click="handleClick(item)"
>
{{ item.label }}
</span>
</el-breadcrumb-item>
</template>
</el-breadcrumb>
</template>
<style scoped lang="scss">
.smart-breadcrumb {
min-width: 0;
overflow: hidden;
white-space: nowrap;
:deep(.el-breadcrumb__inner) {
display: inline-flex;
align-items: center;
min-width: 0;
max-width: 100%;
font-weight: 400;
}
:deep(.el-breadcrumb__separator) {
margin: 0 8px;
color: #c0c4cc;
}
}
.smart-breadcrumb__label,
.smart-breadcrumb__ellipsis {
display: inline-block;
min-width: 0;
vertical-align: middle;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.smart-breadcrumb__label {
color: #606266;
transition: color 0.2s;
}
.smart-breadcrumb__link {
cursor: pointer;
color: #606266;
}
.smart-breadcrumb__link:hover {
color: #409eff;
}
.smart-breadcrumb__current {
color: #303133;
font-weight: 600;
cursor: default;
}
.smart-breadcrumb__disabled {
color: #c0c4cc;
cursor: not-allowed;
}
.smart-breadcrumb__ellipsis {
max-width: none !important;
}
</style>