vben
2021-08-24 56a966cfbf8db5b29a42185f0f25a0e800c30dbb
提交 | 用户 | 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>