mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-27 13:55:39 +08:00
feat(breadcrumb): add breadcrumb demo #143
This commit is contained in:
@@ -6,6 +6,8 @@
|
|||||||
- 新增左侧菜单混合模式
|
- 新增左侧菜单混合模式
|
||||||
- 新增 markdown 嵌入表单内示例
|
- 新增 markdown 嵌入表单内示例
|
||||||
- 新增主框架外页面示例
|
- 新增主框架外页面示例
|
||||||
|
- `route.meta` 新增`currentActiveMenu`,`hideTab`,`hideMenu`参数 用于控制详情页面包屑级菜单显示隐藏。
|
||||||
|
- 新增面包屑导航示例
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
@@ -14,6 +16,7 @@
|
|||||||
- 修复图表库切换页面导致宽高计算错误
|
- 修复图表库切换页面导致宽高计算错误
|
||||||
- 修复多语言配置 `Locale.show`导致配置不生效
|
- 修复多语言配置 `Locale.show`导致配置不生效
|
||||||
- 修复路由类型错误
|
- 修复路由类型错误
|
||||||
|
- 修复菜单分割时权限失效问题
|
||||||
|
|
||||||
## 2.0.0-rc.14 (2020-12-15)
|
## 2.0.0-rc.14 (2020-12-15)
|
||||||
|
|
||||||
|
@@ -118,16 +118,21 @@
|
|||||||
listenerLastChangeTab((route) => {
|
listenerLastChangeTab((route) => {
|
||||||
if (route.name === REDIRECT_NAME) return;
|
if (route.name === REDIRECT_NAME) return;
|
||||||
handleMenuChange(route);
|
handleMenuChange(route);
|
||||||
}, false);
|
const currentActiveMenu = route.meta?.currentActiveMenu;
|
||||||
|
if (currentActiveMenu) {
|
||||||
|
menuState.selectedKeys = [currentActiveMenu];
|
||||||
|
setOpenKeys(currentActiveMenu);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.items,
|
() => props.items,
|
||||||
() => {
|
() => {
|
||||||
handleMenuChange();
|
handleMenuChange();
|
||||||
},
|
|
||||||
{
|
|
||||||
immediate: true,
|
|
||||||
}
|
}
|
||||||
|
// {
|
||||||
|
// immediate: true,
|
||||||
|
// }
|
||||||
);
|
);
|
||||||
|
|
||||||
async function handleMenuClick({ key, keyPath }: { key: string; keyPath: string[] }) {
|
async function handleMenuClick({ key, keyPath }: { key: string; keyPath: string[] }) {
|
||||||
@@ -149,9 +154,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const path = (route || unref(currentRoute)).path;
|
const path = (route || unref(currentRoute)).path;
|
||||||
if (props.mode !== MenuModeEnum.HORIZONTAL) {
|
setOpenKeys(path);
|
||||||
setOpenKeys(path);
|
|
||||||
}
|
|
||||||
if (props.isHorizontal && unref(getSplit)) {
|
if (props.isHorizontal && unref(getSplit)) {
|
||||||
const parentPath = await getCurrentParentPath(path);
|
const parentPath = await getCurrentParentPath(path);
|
||||||
menuState.selectedKeys = [parentPath];
|
menuState.selectedKeys = [parentPath];
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<BasicMenuItem v-if="!menuHasChildren(item)" v-bind="$props" />
|
<BasicMenuItem v-if="!menuHasChildren(item) && getShowMenu" v-bind="$props" />
|
||||||
<SubMenu v-else :class="[`${prefixCls}__level${level}`, theme]">
|
<SubMenu
|
||||||
|
v-if="menuHasChildren(item) && getShowMenu"
|
||||||
|
:class="[`${prefixCls}__level${level}`, theme]"
|
||||||
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
<MenuItemContent v-bind="$props" :item="item" />
|
<MenuItemContent v-bind="$props" :item="item" />
|
||||||
</template>
|
</template>
|
||||||
@@ -16,7 +19,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Menu as MenuType } from '/@/router/types';
|
import type { Menu as MenuType } from '/@/router/types';
|
||||||
|
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent, computed } from 'vue';
|
||||||
import { Menu } from 'ant-design-vue';
|
import { Menu } from 'ant-design-vue';
|
||||||
import { useDesign } from '/@/hooks/web/useDesign';
|
import { useDesign } from '/@/hooks/web/useDesign';
|
||||||
import { itemProps } from '../props';
|
import { itemProps } from '../props';
|
||||||
@@ -35,8 +38,12 @@
|
|||||||
// ExpandIcon: createAsyncComponent(() => import('./ExpandIcon.vue')),
|
// ExpandIcon: createAsyncComponent(() => import('./ExpandIcon.vue')),
|
||||||
},
|
},
|
||||||
props: itemProps,
|
props: itemProps,
|
||||||
setup() {
|
setup(props) {
|
||||||
const { prefixCls } = useDesign('basic-menu-item');
|
const { prefixCls } = useDesign('basic-menu-item');
|
||||||
|
|
||||||
|
const getShowMenu = computed(() => {
|
||||||
|
return !props.item.meta?.hideMenu;
|
||||||
|
});
|
||||||
function menuHasChildren(menuTreeItem: MenuType): boolean {
|
function menuHasChildren(menuTreeItem: MenuType): boolean {
|
||||||
return (
|
return (
|
||||||
Reflect.has(menuTreeItem, 'children') &&
|
Reflect.has(menuTreeItem, 'children') &&
|
||||||
@@ -47,6 +54,7 @@
|
|||||||
return {
|
return {
|
||||||
prefixCls,
|
prefixCls,
|
||||||
menuHasChildren,
|
menuHasChildren,
|
||||||
|
getShowMenu,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@@ -18,6 +18,9 @@ export function useOpenKeys(
|
|||||||
const { getCollapsed } = useMenuSetting();
|
const { getCollapsed } = useMenuSetting();
|
||||||
|
|
||||||
function setOpenKeys(path: string) {
|
function setOpenKeys(path: string) {
|
||||||
|
if (mode.value === MenuModeEnum.HORIZONTAL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const menuList = toRaw(menus.value);
|
const menuList = toRaw(menus.value);
|
||||||
if (!unref(accordion)) {
|
if (!unref(accordion)) {
|
||||||
menuState.openKeys = es6Unique([...menuState.openKeys, ...getAllParentPath(menuList, path)]);
|
menuState.openKeys = es6Unique([...menuState.openKeys, ...getAllParentPath(menuList, path)]);
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="[prefixCls, `${prefixCls}--${theme}`]">
|
<div :class="[prefixCls, `${prefixCls}--${theme}`]">
|
||||||
<a-breadcrumb :routes="routes">
|
<a-breadcrumb :routes="routes">
|
||||||
<template #itemRender="{ route, routes }">
|
<template #itemRender="{ route, routes, paths }">
|
||||||
<Icon :icon="route.meta.icon" v-if="getShowBreadCrumbIcon && route.meta.icon" />
|
<Icon :icon="route.meta.icon" v-if="getShowBreadCrumbIcon && route.meta.icon" />
|
||||||
<span v-if="routes.indexOf(route) === routes.length - 1">
|
<span v-if="!hasRedirect(routes, route)">
|
||||||
{{ t(route.meta.title) }}
|
{{ t(route.meta.title) }}
|
||||||
</span>
|
</span>
|
||||||
<router-link v-else :to="route.path">
|
<router-link v-else to="" @click="handleClick(route, paths, $event)">
|
||||||
{{ t(route.meta.title) }}
|
{{ t(route.meta.title) }}
|
||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
@@ -30,6 +30,8 @@
|
|||||||
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
||||||
|
|
||||||
import { propTypes } from '/@/utils/propTypes';
|
import { propTypes } from '/@/utils/propTypes';
|
||||||
|
import { useGo } from '/@/hooks/web/usePage';
|
||||||
|
import { isString } from '/@/utils/is';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'LayoutBreadcrumb',
|
name: 'LayoutBreadcrumb',
|
||||||
@@ -45,22 +47,12 @@
|
|||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (currentRoute.value.name === REDIRECT_NAME) {
|
if (currentRoute.value.name === REDIRECT_NAME) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
const matched = currentRoute.value?.matched;
|
const matched = currentRoute.value?.matched;
|
||||||
if (!matched || matched.length === 0) return;
|
if (!matched || matched.length === 0) return;
|
||||||
|
|
||||||
let breadcrumbList = filter(toRaw(matched), (item) => {
|
let breadcrumbList = filterItem(toRaw(matched));
|
||||||
if (!item.meta) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const { title, hideBreadcrumb } = item.meta;
|
|
||||||
if (!title || hideBreadcrumb) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
const filterBreadcrumbList = breadcrumbList.filter(
|
const filterBreadcrumbList = breadcrumbList.filter(
|
||||||
(item) => item.path !== PageEnum.BASE_HOME
|
(item) => item.path !== PageEnum.BASE_HOME
|
||||||
@@ -71,13 +63,86 @@
|
|||||||
path: PageEnum.BASE_HOME,
|
path: PageEnum.BASE_HOME,
|
||||||
meta: {
|
meta: {
|
||||||
title: t('layout.header.home'),
|
title: t('layout.header.home'),
|
||||||
|
isLink: true,
|
||||||
},
|
},
|
||||||
} as unknown) as RouteLocationMatched);
|
} as unknown) as RouteLocationMatched);
|
||||||
}
|
}
|
||||||
routes.value = filterBreadcrumbList.length === 1 ? [] : filterBreadcrumbList;
|
|
||||||
|
if (currentRoute.value.meta?.currentActiveMenu) {
|
||||||
|
filterBreadcrumbList.push((currentRoute.value as unknown) as RouteLocationMatched);
|
||||||
|
}
|
||||||
|
// routes.value = filterBreadcrumbList.length === 1 ? [] : filterBreadcrumbList;
|
||||||
|
routes.value = filterBreadcrumbList;
|
||||||
});
|
});
|
||||||
|
|
||||||
return { routes, t, prefixCls, getShowBreadCrumbIcon };
|
function filterItem(list: RouteLocationMatched[]) {
|
||||||
|
let resultList = filter(list, (item) => {
|
||||||
|
const { meta } = item;
|
||||||
|
|
||||||
|
if (!meta) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const { title, hideBreadcrumb, hideMenu } = meta;
|
||||||
|
if (!title || hideBreadcrumb || hideMenu) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}).filter((item) => !item.meta?.hideBreadcrumb || !item.meta?.hideMenu);
|
||||||
|
|
||||||
|
resultList = resultList.filter((item) => item.path !== PageEnum.BASE_HOME);
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClick(route: RouteLocationMatched, paths: string[], e: Event) {
|
||||||
|
e?.preventDefault();
|
||||||
|
const {
|
||||||
|
children,
|
||||||
|
redirect,
|
||||||
|
meta,
|
||||||
|
// components
|
||||||
|
} = route;
|
||||||
|
|
||||||
|
// const isParent =
|
||||||
|
// components?.default?.name === 'DefaultLayout' || (components?.default as any)?.parentView;
|
||||||
|
|
||||||
|
if (
|
||||||
|
children?.length &&
|
||||||
|
!redirect
|
||||||
|
// && !isParent
|
||||||
|
) {
|
||||||
|
e?.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (meta?.carryParam) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const go = useGo();
|
||||||
|
if (redirect && isString(redirect)) {
|
||||||
|
go(redirect);
|
||||||
|
} else {
|
||||||
|
const ps = paths.slice(1);
|
||||||
|
const lastPath = ps.pop() || '';
|
||||||
|
const parentPath = ps.pop() || '';
|
||||||
|
let path = `${parentPath}/${lastPath}`;
|
||||||
|
path = /^\//.test(path) ? path : `/${path}`;
|
||||||
|
go(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasRedirect(routes: RouteLocationMatched[], route: RouteLocationMatched) {
|
||||||
|
if (route?.meta?.isLink) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (routes.indexOf(route) === routes.length - 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { routes, t, prefixCls, getShowBreadCrumbIcon, handleClick, hasRedirect };
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@@ -59,7 +59,7 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf
|
|||||||
return { menuSetting: { bgColor: value } };
|
return { menuSetting: { bgColor: value } };
|
||||||
|
|
||||||
case HandlerEnum.MENU_SPLIT:
|
case HandlerEnum.MENU_SPLIT:
|
||||||
return { menuSetting: { split: value, collapsedShowTitle: true } };
|
return { menuSetting: { split: value } };
|
||||||
|
|
||||||
case HandlerEnum.MENU_CLOSE_MIX_SIDEBAR_ON_CHANGE:
|
case HandlerEnum.MENU_CLOSE_MIX_SIDEBAR_ON_CHANGE:
|
||||||
return { menuSetting: { closeMixSidebarOnChange: value } };
|
return { menuSetting: { closeMixSidebarOnChange: value } };
|
||||||
|
@@ -38,6 +38,7 @@
|
|||||||
|
|
||||||
<div :class="`${prefixCls}-menu-list`" ref="sideRef" :style="getMenuStyle">
|
<div :class="`${prefixCls}-menu-list`" ref="sideRef" :style="getMenuStyle">
|
||||||
<div
|
<div
|
||||||
|
v-show="openMenu"
|
||||||
:class="[
|
:class="[
|
||||||
`${prefixCls}-menu-list__title`,
|
`${prefixCls}-menu-list__title`,
|
||||||
{
|
{
|
||||||
|
@@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
import { Tabs } from 'ant-design-vue';
|
import { Tabs } from 'ant-design-vue';
|
||||||
import TabContent from './components/TabContent.vue';
|
import TabContent from './components/TabContent.vue';
|
||||||
|
import type { RouteLocationNormalized } from 'vue-router';
|
||||||
|
|
||||||
import { useGo } from '/@/hooks/web/usePage';
|
import { useGo } from '/@/hooks/web/usePage';
|
||||||
|
|
||||||
@@ -43,6 +44,8 @@
|
|||||||
import { listenerLastChangeTab } from '/@/logics/mitt/tabChange';
|
import { listenerLastChangeTab } from '/@/logics/mitt/tabChange';
|
||||||
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
|
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
|
||||||
|
|
||||||
|
import router from '/@/router';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'MultipleTabs',
|
name: 'MultipleTabs',
|
||||||
components: {
|
components: {
|
||||||
@@ -61,7 +64,9 @@
|
|||||||
const go = useGo();
|
const go = useGo();
|
||||||
const { getShowQuick, getShowRedo } = useMultipleTabSetting();
|
const { getShowQuick, getShowRedo } = useMultipleTabSetting();
|
||||||
|
|
||||||
const getTabsState = computed(() => tabStore.getTabsState);
|
const getTabsState = computed(() => {
|
||||||
|
return tabStore.getTabsState.filter((item) => !item.meta?.hideTab);
|
||||||
|
});
|
||||||
|
|
||||||
const unClose = computed(() => unref(getTabsState).length === 1);
|
const unClose = computed(() => unref(getTabsState).length === 1);
|
||||||
|
|
||||||
@@ -78,13 +83,24 @@
|
|||||||
const { name } = route;
|
const { name } = route;
|
||||||
if (name === REDIRECT_NAME || !route || !userStore.getTokenState) return;
|
if (name === REDIRECT_NAME || !route || !userStore.getTokenState) return;
|
||||||
|
|
||||||
const { path, fullPath } = route;
|
const { path, fullPath, meta = {} } = route;
|
||||||
const p = fullPath || path;
|
|
||||||
|
|
||||||
|
const { currentActiveMenu, hideTab } = meta;
|
||||||
|
const isHide = !hideTab ? null : currentActiveMenu;
|
||||||
|
const p = isHide || fullPath || path;
|
||||||
if (activeKeyRef.value !== p) {
|
if (activeKeyRef.value !== p) {
|
||||||
activeKeyRef.value = p;
|
activeKeyRef.value = p;
|
||||||
}
|
}
|
||||||
tabStore.addTabAction(unref(route));
|
|
||||||
|
if (isHide) {
|
||||||
|
const findParentRoute = router
|
||||||
|
.getRoutes()
|
||||||
|
.find((item) => item.path === currentActiveMenu);
|
||||||
|
findParentRoute &&
|
||||||
|
tabStore.addTabAction((findParentRoute as unknown) as RouteLocationNormalized);
|
||||||
|
} else {
|
||||||
|
tabStore.addTabAction(unref(route));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleChange(activeKey: any) {
|
function handleChange(activeKey: any) {
|
||||||
|
@@ -15,4 +15,11 @@ export default {
|
|||||||
tab: 'Tab with parameters',
|
tab: 'Tab with parameters',
|
||||||
tab1: 'Tab with parameter 1',
|
tab1: 'Tab with parameter 1',
|
||||||
tab2: 'Tab with parameter 2',
|
tab2: 'Tab with parameter 2',
|
||||||
|
|
||||||
|
breadcrumb: 'Breadcrumbs',
|
||||||
|
breadcrumbFlat: 'Flat Mode',
|
||||||
|
breadcrumbFlatDetail: 'Flat mode details',
|
||||||
|
|
||||||
|
breadcrumbChildren: 'Level mode',
|
||||||
|
breadcrumbChildrenDetail: 'Level mode detail',
|
||||||
};
|
};
|
||||||
|
@@ -15,4 +15,11 @@ export default {
|
|||||||
tab: 'Tab带参',
|
tab: 'Tab带参',
|
||||||
tab1: 'Tab带参1',
|
tab1: 'Tab带参1',
|
||||||
tab2: 'Tab带参2',
|
tab2: 'Tab带参2',
|
||||||
|
|
||||||
|
breadcrumb: '面包屑导航',
|
||||||
|
breadcrumbFlat: '平级模式',
|
||||||
|
breadcrumbFlatDetail: '平级详情',
|
||||||
|
|
||||||
|
breadcrumbChildren: '层级模式',
|
||||||
|
breadcrumbChildrenDetail: '层级详情',
|
||||||
};
|
};
|
||||||
|
@@ -16,7 +16,7 @@ function asyncImportRoute(routes: AppRouteRecordRaw[] | undefined) {
|
|||||||
const { component, name } = item;
|
const { component, name } = item;
|
||||||
const { children } = item;
|
const { children } = item;
|
||||||
if (component) {
|
if (component) {
|
||||||
item.component = dynamicImport(component);
|
item.component = dynamicImport(component as string);
|
||||||
} else if (name) {
|
} else if (name) {
|
||||||
item.component = getParentLayout(name);
|
item.component = getParentLayout(name);
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModul
|
|||||||
routeList.forEach((route) => {
|
routeList.forEach((route) => {
|
||||||
if (route.component) {
|
if (route.component) {
|
||||||
if ((route.component as string).toUpperCase() === 'LAYOUT') {
|
if ((route.component as string).toUpperCase() === 'LAYOUT') {
|
||||||
route.component = LayoutMap.get(route.component);
|
route.component = LayoutMap.get(route.component as LayoutMapKey);
|
||||||
} else {
|
} else {
|
||||||
route.children = [cloneDeep(route)];
|
route.children = [cloneDeep(route)];
|
||||||
route.component = LAYOUT;
|
route.component = LAYOUT;
|
||||||
|
@@ -71,8 +71,10 @@ export async function getShallowMenus(): Promise<Menu[]> {
|
|||||||
export async function getChildrenMenus(parentPath: string) {
|
export async function getChildrenMenus(parentPath: string) {
|
||||||
const menus = await getAsyncMenus();
|
const menus = await getAsyncMenus();
|
||||||
const parent = menus.find((item) => item.path === parentPath);
|
const parent = menus.find((item) => item.path === parentPath);
|
||||||
if (!parent) return [] as Menu[];
|
if (!parent || !parent.children) return [] as Menu[];
|
||||||
return parent.children;
|
const routes = router.getRoutes();
|
||||||
|
|
||||||
|
return !isBackMode() ? filter(parent.children, basicFilter(routes)) : parent.children;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通用过滤方法
|
// 通用过滤方法
|
||||||
|
@@ -151,9 +151,6 @@ const menu: MenuModule = {
|
|||||||
{
|
{
|
||||||
path: 'loading',
|
path: 'loading',
|
||||||
name: t('routes.demo.comp.loading'),
|
name: t('routes.demo.comp.loading'),
|
||||||
tag: {
|
|
||||||
content: 'new',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'tree',
|
path: 'tree',
|
||||||
@@ -176,6 +173,9 @@ const menu: MenuModule = {
|
|||||||
{
|
{
|
||||||
name: t('routes.demo.editor.editor'),
|
name: t('routes.demo.editor.editor'),
|
||||||
path: 'editor',
|
path: 'editor',
|
||||||
|
tag: {
|
||||||
|
content: 'new',
|
||||||
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'markdown',
|
path: 'markdown',
|
||||||
|
@@ -19,6 +19,7 @@ const menu: MenuModule = {
|
|||||||
path: 'tabs',
|
path: 'tabs',
|
||||||
name: t('routes.demo.feat.tabs'),
|
name: t('routes.demo.feat.tabs'),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: 'context-menu',
|
path: 'context-menu',
|
||||||
name: t('routes.demo.feat.contextMenu'),
|
name: t('routes.demo.feat.contextMenu'),
|
||||||
@@ -85,6 +86,27 @@ const menu: MenuModule = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: t('routes.demo.feat.breadcrumb'),
|
||||||
|
path: 'breadcrumb',
|
||||||
|
tag: {
|
||||||
|
content: 'new',
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'flat',
|
||||||
|
name: t('routes.demo.feat.breadcrumbFlat'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'flatDetail',
|
||||||
|
name: t('routes.demo.feat.breadcrumbFlatDetail'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'children',
|
||||||
|
name: t('routes.demo.feat.breadcrumbChildrenDetail'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'testTab',
|
path: 'testTab',
|
||||||
name: t('routes.demo.feat.tab'),
|
name: t('routes.demo.feat.tab'),
|
||||||
|
@@ -7,14 +7,14 @@ const menu: MenuModule = {
|
|||||||
name: t('routes.demo.iframe.frame'),
|
name: t('routes.demo.iframe.frame'),
|
||||||
path: '/frame',
|
path: '/frame',
|
||||||
children: [
|
children: [
|
||||||
{
|
|
||||||
path: 'antv',
|
|
||||||
name: t('routes.demo.iframe.antv'),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'doc',
|
path: 'doc',
|
||||||
name: t('routes.demo.iframe.doc'),
|
name: t('routes.demo.iframe.doc'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'antv',
|
||||||
|
name: t('routes.demo.iframe.antv'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'https://vvbin.cn/doc-next/',
|
path: 'https://vvbin.cn/doc-next/',
|
||||||
name: t('routes.demo.iframe.docExternal'),
|
name: t('routes.demo.iframe.docExternal'),
|
||||||
|
@@ -6,9 +6,6 @@ const menu: MenuModule = {
|
|||||||
menu: {
|
menu: {
|
||||||
name: t('routes.demo.level.level'),
|
name: t('routes.demo.level.level'),
|
||||||
path: '/level',
|
path: '/level',
|
||||||
tag: {
|
|
||||||
dot: true,
|
|
||||||
},
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'menu1',
|
path: 'menu1',
|
||||||
|
@@ -6,9 +6,7 @@ const menu: MenuModule = {
|
|||||||
menu: {
|
menu: {
|
||||||
name: t('routes.demo.page.page'),
|
name: t('routes.demo.page.page'),
|
||||||
path: '/page-demo',
|
path: '/page-demo',
|
||||||
tag: {
|
|
||||||
dot: true,
|
|
||||||
},
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'form',
|
path: 'form',
|
||||||
@@ -102,9 +100,6 @@ const menu: MenuModule = {
|
|||||||
{
|
{
|
||||||
path: 'list',
|
path: 'list',
|
||||||
name: t('routes.demo.page.list'),
|
name: t('routes.demo.page.list'),
|
||||||
tag: {
|
|
||||||
content: 'new',
|
|
||||||
},
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'basic',
|
path: 'basic',
|
||||||
|
@@ -13,6 +13,14 @@ const charts: AppRouteModule = {
|
|||||||
title: t('routes.demo.charts.charts'),
|
title: t('routes.demo.charts.charts'),
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: 'apexChart',
|
||||||
|
name: 'ApexChart',
|
||||||
|
meta: {
|
||||||
|
title: t('routes.demo.charts.apexChart'),
|
||||||
|
},
|
||||||
|
component: () => import('/@/views/demo/echarts/apex/index.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'echarts',
|
path: 'echarts',
|
||||||
name: 'Echarts',
|
name: 'Echarts',
|
||||||
@@ -20,6 +28,7 @@ const charts: AppRouteModule = {
|
|||||||
meta: {
|
meta: {
|
||||||
title: 'Echarts',
|
title: 'Echarts',
|
||||||
},
|
},
|
||||||
|
redirect: '/charts/echarts/map',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'map',
|
path: 'map',
|
||||||
@@ -47,14 +56,6 @@ const charts: AppRouteModule = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'apexChart',
|
|
||||||
name: 'ApexChart',
|
|
||||||
meta: {
|
|
||||||
title: t('routes.demo.charts.apexChart'),
|
|
||||||
},
|
|
||||||
component: () => import('/@/views/demo/echarts/apex/index.vue'),
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -29,6 +29,68 @@ const feat: AppRouteModule = {
|
|||||||
title: t('routes.demo.feat.tabs'),
|
title: t('routes.demo.feat.tabs'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'breadcrumb',
|
||||||
|
name: 'BreadcrumbDemo',
|
||||||
|
redirect: '/feat/breadcrumb/flat',
|
||||||
|
component: getParentLayout('BreadcrumbDemo'),
|
||||||
|
meta: {
|
||||||
|
title: t('routes.demo.feat.breadcrumb'),
|
||||||
|
},
|
||||||
|
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'flat',
|
||||||
|
name: 'BreadcrumbFlatDemo',
|
||||||
|
component: () => import('/@/views/demo/feat/breadcrumb/FlatList.vue'),
|
||||||
|
meta: {
|
||||||
|
title: t('routes.demo.feat.breadcrumbFlat'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'flatDetail',
|
||||||
|
name: 'BreadcrumbFlatDetailDemo',
|
||||||
|
component: () => import('/@/views/demo/feat/breadcrumb/FlatListDetail.vue'),
|
||||||
|
meta: {
|
||||||
|
title: t('routes.demo.feat.breadcrumbFlatDetail'),
|
||||||
|
hideMenu: true,
|
||||||
|
hideTab: true,
|
||||||
|
currentActiveMenu: '/feat/breadcrumb/flat',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'children',
|
||||||
|
name: 'BreadcrumbChildrenDemo',
|
||||||
|
component: getParentLayout('BreadcrumbChildrenDemo'),
|
||||||
|
redirect: '/feat/breadcrumb/children',
|
||||||
|
meta: {
|
||||||
|
title: t('routes.demo.feat.breadcrumbFlat'),
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
name: 'BreadcrumbChildren',
|
||||||
|
component: () => import('/@/views/demo/feat/breadcrumb/ChildrenList.vue'),
|
||||||
|
meta: {
|
||||||
|
title: t('routes.demo.feat.breadcrumbChildren'),
|
||||||
|
hideBreadcrumb: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'childrenDetail',
|
||||||
|
name: 'BreadcrumbChildrenDetailDemo',
|
||||||
|
component: () => import('/@/views/demo/feat/breadcrumb/ChildrenListDetail.vue'),
|
||||||
|
meta: {
|
||||||
|
currentActiveMenu: '/feat/breadcrumb/children',
|
||||||
|
title: t('routes.demo.feat.breadcrumbChildrenDetail'),
|
||||||
|
hideTab: true,
|
||||||
|
hideMenu: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: 'context-menu',
|
path: 'context-menu',
|
||||||
|
@@ -8,22 +8,13 @@ const iframe: AppRouteModule = {
|
|||||||
path: '/frame',
|
path: '/frame',
|
||||||
name: 'Frame',
|
name: 'Frame',
|
||||||
component: LAYOUT,
|
component: LAYOUT,
|
||||||
redirect: '/frame/antv',
|
redirect: '/frame/doc',
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'mdi:page-next-outline',
|
icon: 'mdi:page-next-outline',
|
||||||
title: t('routes.demo.iframe.frame'),
|
title: t('routes.demo.iframe.frame'),
|
||||||
},
|
},
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
{
|
|
||||||
path: 'antv',
|
|
||||||
name: 'Antv',
|
|
||||||
component: IFrame,
|
|
||||||
meta: {
|
|
||||||
frameSrc: 'https://2x.antdv.com/docs/vue/introduce-cn/',
|
|
||||||
title: t('routes.demo.iframe.antv'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'doc',
|
path: 'doc',
|
||||||
name: 'Doc',
|
name: 'Doc',
|
||||||
@@ -33,6 +24,15 @@ const iframe: AppRouteModule = {
|
|||||||
title: t('routes.demo.iframe.doc'),
|
title: t('routes.demo.iframe.doc'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'antv',
|
||||||
|
name: 'Antv',
|
||||||
|
component: IFrame,
|
||||||
|
meta: {
|
||||||
|
frameSrc: 'https://2x.antdv.com/docs/vue/introduce-cn/',
|
||||||
|
title: t('routes.demo.iframe.antv'),
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'https://vvbin.cn/doc-next/',
|
path: 'https://vvbin.cn/doc-next/',
|
||||||
name: 'DocExternal',
|
name: 'DocExternal',
|
||||||
|
@@ -7,7 +7,7 @@ const permission: AppRouteModule = {
|
|||||||
path: '/level',
|
path: '/level',
|
||||||
name: 'Level',
|
name: 'Level',
|
||||||
component: LAYOUT,
|
component: LAYOUT,
|
||||||
redirect: '/level/menu1/menu1-1',
|
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||||
meta: {
|
meta: {
|
||||||
icon: 'carbon:user-role',
|
icon: 'carbon:user-role',
|
||||||
title: t('routes.demo.level.level'),
|
title: t('routes.demo.level.level'),
|
||||||
@@ -21,6 +21,7 @@ const permission: AppRouteModule = {
|
|||||||
meta: {
|
meta: {
|
||||||
title: 'Menu1',
|
title: 'Menu1',
|
||||||
},
|
},
|
||||||
|
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'menu1-1',
|
path: 'menu1-1',
|
||||||
@@ -29,6 +30,7 @@ const permission: AppRouteModule = {
|
|||||||
meta: {
|
meta: {
|
||||||
title: 'Menu1-1',
|
title: 'Menu1-1',
|
||||||
},
|
},
|
||||||
|
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'menu1-1-1',
|
path: 'menu1-1-1',
|
||||||
|
@@ -30,13 +30,22 @@ export interface RouteMeta {
|
|||||||
|
|
||||||
// Used internally to mark single-level menus
|
// Used internally to mark single-level menus
|
||||||
single?: boolean;
|
single?: boolean;
|
||||||
|
|
||||||
|
// Currently active menu
|
||||||
|
currentActiveMenu?: string;
|
||||||
|
|
||||||
|
// Never show in tab
|
||||||
|
hideTab?: boolean;
|
||||||
|
|
||||||
|
// Never show in menu
|
||||||
|
hideMenu?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
|
export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
|
||||||
name: string;
|
name: string;
|
||||||
meta: RouteMeta;
|
meta: RouteMeta;
|
||||||
component?: Component;
|
component?: Component | string;
|
||||||
components?: Component;
|
components?: Component;
|
||||||
children?: AppRouteRecordRaw[];
|
children?: AppRouteRecordRaw[];
|
||||||
props?: Record<string, any>;
|
props?: Record<string, any>;
|
||||||
|
10
src/views/demo/feat/breadcrumb/ChildrenList.vue
Normal file
10
src/views/demo/feat/breadcrumb/ChildrenList.vue
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-5">
|
||||||
|
<router-link to="/feat/breadcrumb/children/childrenDetail">进入子级详情页</router-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
export default defineComponent({});
|
||||||
|
</script>
|
8
src/views/demo/feat/breadcrumb/ChildrenListDetail.vue
Normal file
8
src/views/demo/feat/breadcrumb/ChildrenListDetail.vue
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<template>
|
||||||
|
<div> 子级详情页 </div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
export default defineComponent({});
|
||||||
|
</script>
|
10
src/views/demo/feat/breadcrumb/FlatList.vue
Normal file
10
src/views/demo/feat/breadcrumb/FlatList.vue
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-5">
|
||||||
|
<router-link to="/feat/breadcrumb/flatDetail">进入平级详情页</router-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
export default defineComponent({});
|
||||||
|
</script>
|
8
src/views/demo/feat/breadcrumb/FlatListDetail.vue
Normal file
8
src/views/demo/feat/breadcrumb/FlatListDetail.vue
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<template>
|
||||||
|
<div> 平级详情页 </div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
|
export default defineComponent({});
|
||||||
|
</script>
|
Reference in New Issue
Block a user