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