vben
authored
|
1
|
<template>
|
vben
authored
|
2
|
<div :class="[prefixCls, `${prefixCls}--${theme}`]">
|
vben
authored
|
3
|
<a-breadcrumb :routes="routes">
|
|
4
|
<template #itemRender="{ route, routes: routesMatched, paths }">
|
Vben
authored
|
5
|
<Icon :icon="getIcon(route)" v-if="getShowBreadCrumbIcon && getIcon(route)" />
|
|
6
|
<span v-if="!hasRedirect(routesMatched, route)">
|
Vben
authored
|
7
|
{{ t(route.name || route.meta.title) }}
|
vben
authored
|
8
|
</span>
|
vben
authored
|
9
|
<router-link v-else to="" @click="handleClick(route, paths, $event)">
|
Vben
authored
|
10
|
{{ t(route.name || route.meta.title) }}
|
vben
authored
|
11
12
13
14
15
16
|
</router-link>
</template>
</a-breadcrumb>
</div>
</template>
<script lang="ts">
|
vben
authored
|
17
|
import type { RouteLocationMatched } from 'vue-router';
|
|
18
|
import { useRouter } from 'vue-router';
|
Vben
authored
|
19
|
import type { Menu } from '/@/router/types';
|
vben
authored
|
20
|
|
Vben
authored
|
21
|
import { defineComponent, ref, watchEffect } from 'vue';
|
vben
authored
|
22
|
|
Vben
authored
|
23
|
import { Breadcrumb } from 'ant-design-vue';
|
vben
authored
|
24
|
import Icon from '@/components/Icon/Icon.vue';
|
vben
authored
|
25
|
|
vben
authored
|
26
27
|
import { useDesign } from '/@/hooks/web/useDesign';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
Vben
authored
|
28
29
|
import { useGo } from '/@/hooks/web/usePage';
import { useI18n } from '/@/hooks/web/useI18n';
|
vben
authored
|
30
31
|
import { propTypes } from '/@/utils/propTypes';
|
vben
authored
|
32
|
import { isString } from '/@/utils/is';
|
Vben
authored
|
33
|
import { filter } from '/@/utils/helper/treeHelper';
|
Vben
authored
|
34
|
import { getMenus } from '/@/router/menus';
|
vben
authored
|
35
|
|
Vben
authored
|
36
37
38
|
import { REDIRECT_NAME } from '/@/router/constant';
import { getAllParentPath } from '/@/router/helper/menuHelper';
|
vben
authored
|
39
40
|
export default defineComponent({
name: 'LayoutBreadcrumb',
|
vben
authored
|
41
|
components: { Icon, [Breadcrumb.name]: Breadcrumb },
|
vben
authored
|
42
|
props: {
|
vben
authored
|
43
|
theme: propTypes.oneOf(['dark', 'light']),
|
vben
authored
|
44
45
46
47
|
},
setup() {
const routes = ref<RouteLocationMatched[]>([]);
const { currentRoute } = useRouter();
|
vben
authored
|
48
49
|
const { prefixCls } = useDesign('layout-breadcrumb');
const { getShowBreadCrumbIcon } = useRootSetting();
|
Vben
authored
|
50
|
const go = useGo();
|
vben
authored
|
51
52
|
const { t } = useI18n();
|
Vben
authored
|
53
|
watchEffect(async () => {
|
vben
authored
|
54
|
if (currentRoute.value.name === REDIRECT_NAME) return;
|
Vben
authored
|
55
|
const menus = await getMenus();
|
Vben
authored
|
56
|
|
Vben
authored
|
57
58
59
60
61
62
63
|
const routeMatched = currentRoute.value.matched;
const cur = routeMatched?.[routeMatched.length - 1];
let path = currentRoute.value.path;
if (cur && cur?.meta?.currentActiveMenu) {
path = cur.meta.currentActiveMenu as string;
}
|
Vben
authored
|
64
65
66
67
|
const parent = getAllParentPath(menus, path);
const filterMenus = menus.filter((item) => item.path === parent[0]);
const matched = getMatched(filterMenus, parent) as any;
|
vben
authored
|
68
|
|
vben
authored
|
69
70
|
if (!matched || matched.length === 0) return;
|
Vben
authored
|
71
|
const breadcrumbList = filterItem(matched);
|
vben
authored
|
72
|
|
vben
authored
|
73
|
if (currentRoute.value.meta?.currentActiveMenu) {
|
Vben
authored
|
74
|
breadcrumbList.push({
|
Vben
authored
|
75
76
|
...currentRoute.value,
name: currentRoute.value.meta?.title || currentRoute.value.name,
|
Vben
authored
|
77
|
} as unknown as RouteLocationMatched);
|
vben
authored
|
78
|
}
|
Vben
authored
|
79
|
routes.value = breadcrumbList;
|
vben
authored
|
80
81
|
});
|
Vben
authored
|
82
83
84
85
|
function getMatched(menus: Menu[], parent: string[]) {
const metched: Menu[] = [];
menus.forEach((item) => {
if (parent.includes(item.path)) {
|
Vben
authored
|
86
87
88
89
|
metched.push({
...item,
name: item.meta?.title || item.name,
});
|
Vben
authored
|
90
91
92
93
94
95
96
97
|
}
if (item.children?.length) {
metched.push(...getMatched(item.children, parent));
}
});
return metched;
}
|
vben
authored
|
98
|
function filterItem(list: RouteLocationMatched[]) {
|
|
99
|
return filter(list, (item) => {
|
Vben
authored
|
100
|
const { meta, name } = item;
|
vben
authored
|
101
|
if (!meta) {
|
Vben
authored
|
102
|
return !!name;
|
vben
authored
|
103
|
}
|
|
104
105
|
const { title, hideBreadcrumb, hideMenu } = meta;
if (!title || hideBreadcrumb || hideMenu) {
|
vben
authored
|
106
107
108
|
return false;
}
return true;
|
|
109
|
}).filter((item) => !item.meta?.hideBreadcrumb);
|
vben
authored
|
110
111
112
113
|
}
function handleClick(route: RouteLocationMatched, paths: string[], e: Event) {
e?.preventDefault();
|
Vben
authored
|
114
115
116
|
const { children, redirect, meta } = route;
if (children?.length && !redirect) {
|
vben
authored
|
117
118
119
120
121
122
123
124
125
126
|
e?.stopPropagation();
return;
}
if (meta?.carryParam) {
return;
}
if (redirect && isString(redirect)) {
go(redirect);
} else {
|
Vben
authored
|
127
128
129
130
131
132
|
let goPath = '';
if (paths.length === 1) {
goPath = paths[0];
} else {
const ps = paths.slice(1);
const lastPath = ps.pop() || '';
|
Vben
authored
|
133
|
goPath = `${lastPath}`;
|
Vben
authored
|
134
135
136
|
}
goPath = /^\//.test(goPath) ? goPath : `/${goPath}`;
go(goPath);
|
vben
authored
|
137
138
139
140
|
}
}
function hasRedirect(routes: RouteLocationMatched[], route: RouteLocationMatched) {
|
|
141
|
return routes.indexOf(route) !== routes.length - 1;
|
vben
authored
|
142
143
|
}
|
Vben
authored
|
144
145
146
147
148
|
function getIcon(route) {
return route.icon || route.meta?.icon;
}
return { routes, t, prefixCls, getIcon, getShowBreadCrumbIcon, handleClick, hasRedirect };
|
vben
authored
|
149
150
151
|
},
});
</script>
|
vben
authored
|
152
153
154
155
156
157
|
<style lang="less">
@prefix-cls: ~'@{namespace}-layout-breadcrumb';
.@{prefix-cls} {
display: flex;
align-items: center;
|
vben
authored
|
158
|
padding: 0 8px;
|
vben
authored
|
159
160
161
162
163
164
165
166
167
168
169
170
171
|
.ant-breadcrumb-link {
.anticon {
margin-right: 4px;
margin-bottom: 2px;
}
}
&--light {
.ant-breadcrumb-link {
color: @breadcrumb-item-normal-color;
a {
|
vben
authored
|
172
|
color: rgb(0 0 0 / 65%);
|
vben
authored
|
173
174
175
176
177
178
179
180
181
182
183
184
185
186
|
&:hover {
color: @primary-color;
}
}
}
.ant-breadcrumb-separator {
color: @breadcrumb-item-normal-color;
}
}
&--dark {
.ant-breadcrumb-link {
|
vben
authored
|
187
|
color: rgb(255 255 255 / 60%);
|
vben
authored
|
188
189
|
a {
|
vben
authored
|
190
|
color: rgb(255 255 255 / 80%);
|
vben
authored
|
191
192
193
194
195
196
197
198
199
|
&:hover {
color: @white;
}
}
}
.ant-breadcrumb-separator,
.anticon {
|
vben
authored
|
200
|
color: rgb(255 255 255 / 80%);
|
vben
authored
|
201
202
203
204
|
}
}
}
</style>
|