Unverified Commit 1c2a611c authored by Sendya's avatar Sendya

feat: add menuItemRender, subMenuItemRender

parent b70b15bd
import { defineComponent } from 'vue';
import { useRoute } from 'vue-router';
import { PageContainer, Route } from '../../../src';
export default defineComponent({
setup() {
const route = useRoute();
return () => (
<div>
<h1>Child Form: {route.meta.title}</h1>
<pre>{JSON.stringify(route.meta, null, 4)}</pre>
</div>
);
},
});
import { defineComponent } from 'vue';
import { useRoute } from 'vue-router';
import { i18n } from '../../index';
import { PageContainer } from '../../../src';
export default defineComponent({
setup() {
const route = useRoute();
return () => (
<div>
<h1>Child</h1>
<PageContainer title={i18n(`${route.meta.title}`)}>
<router-view />
</div>
</PageContainer>
);
},
});
import { defineComponent } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { Table, Button } from 'ant-design-vue';
import { PlusOutlined } from '@ant-design/icons-vue';
export default defineComponent({
setup() {
const router = useRouter();
const route = useRoute();
const columns = [
{ dataIndex: 'key', title: '#' },
{ dataIndex: 'title', title: '标题' },
{ dataIndex: 'content', title: '内容' },
{
dataIndex: 'action',
title: '操作',
customRender: ({ record }) => {
return (
<>
<a>编辑</a>
<a>删除</a>
</>
);
},
},
];
return () => (
<div>
<h1>Child Table: {route.meta.title}</h1>
<Button
style={{ marginBottom: '12px' }}
onClick={() => {
router.push({ path: '/form/child/page2' });
}}
>
<PlusOutlined />
新增
</Button>
<Table columns={columns} />
</div>
);
},
});
......@@ -7,7 +7,7 @@ export default defineComponent({
const route = useRoute();
return () => (
<PageContainer
title={route.meta.title}
title={route.meta?.title}
breadcrumb={{
routes: [{ path: '/', breadcrumbName: 'home' }] as Route[],
}}
......
import 'ant-design-vue/dist/antd.less';
import { createApp, defineComponent, watch, ref, watchEffect, onMounted, computed } from 'vue';
import { createRouter, createWebHashHistory, useRoute, useRouter, RouterLink } from 'vue-router';
import { Avatar, Button, Space, Select, Switch } from 'ant-design-vue';
import { UserOutlined } from '@ant-design/icons-vue';
import { Avatar, Button, Space, Select, Switch, Menu } from 'ant-design-vue';
import { UserOutlined, SmileOutlined } from '@ant-design/icons-vue';
import { default as ProLayout, FooterToolbar, WaterMark, getMenuData, Route } from '../src/';
import { globalState as state } from './state';
import './demo.less';
......@@ -14,6 +14,8 @@ import Page1 from './demo/page1';
import Welcome from './demo/welcome';
import FormPage from './demo/form';
import ChildPage from './demo/child/child-page';
import ChildTable from './demo/child/child-table';
import ChildForm from './demo/child/child-form';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const noop = () => {};
......@@ -46,6 +48,9 @@ const BasicLayout = defineComponent({
matched.shift();
state.selectedKeys = matched;
};
const updateOpenKeys = () => {
state.openKeys = route.matched.concat().map(item => item.path);
};
onMounted(() => {
// if sider collapsed, set openKeys is null.
......@@ -65,6 +70,7 @@ const BasicLayout = defineComponent({
// watch route
watchEffect(() => {
updateSelectedMenu();
updateOpenKeys();
});
});
......@@ -108,6 +114,23 @@ const BasicLayout = defineComponent({
{state.collapsed && state.layout !== 'mix' ? null : <h1>Pro Preview</h1>}
</a>
)}
menuItemRender={item => {
return (
<Menu.Item inlineIndent={24} key={item.path}>
<router-link to={{ ...item.meta, path: item.path }}>
<SmileOutlined />
<span class="custom-menu-item">{i18n(`${item.meta.title}`)}</span>
</router-link>
</Menu.Item>
);
}}
subMenuItemRender={(item, children) => {
return (
<Menu.SubMenu title={<span>{i18n(`${item.meta.title}`)}</span>} key={item.path}>
{children}
</Menu.SubMenu>
);
}}
breadcrumbRender={({ route: r, routes, paths }) =>
routes.indexOf(r) === routes.length - 1 ? (
<span>{i18n(r.breadcrumbName)}</span>
......@@ -258,15 +281,15 @@ const routes = [
children: [
{
path: '/form/child/page1',
name: 'child-page1-form',
meta: { title: 'Page1 Child' },
component: FormPage,
name: 'child-page1-table',
meta: { title: 'Page1 Child Table' },
component: ChildTable,
},
{
path: '/form/child/page2',
name: 'child-page2-form',
meta: { title: 'Page2 Child' },
component: FormPage,
name: 'child-page1-form',
meta: { title: 'Page2 Child Form' },
component: ChildForm,
},
],
},
......
......@@ -245,7 +245,7 @@ const PageContainer: FunctionalComponent<PageContainerProps> = (props, { slots }
headerDom
)}
<GridContent>{loading ? <Spin /> : content}</GridContent>
{footer && <FooterToolbar>{footer}</FooterToolbar>}
{value.hasFooterToolbar && <FooterToolbar>{footer}</FooterToolbar>}
</div>
);
};
......
......@@ -15,7 +15,7 @@ import Menu from 'ant-design-vue/es/menu';
import defaultSettings, { PureSettings } from '../defaultSettings';
import { isImg, isUrl } from '../utils';
import { MenuMode, SelectInfo, OpenEventHandler } from './typings';
import { MenuDataItem, MenuTheme, FormatMessage, WithFalse } from '../typings';
import { MenuDataItem, MenuTheme, FormatMessage, CustomRender, WithFalse } from '../typings';
import { PrivateSiderMenuProps } from './SiderMenu';
import './index.less';
......@@ -26,7 +26,15 @@ export function useRootSubmenuKeys(menus: MenuDataItem[]): ComputedRef<string[]>
}
// ts typo
export interface BaseMenuProps extends Partial<PureSettings>, PrivateSiderMenuProps {
interface CustomMenuRender {
menuItemRender: WithFalse<(item: MenuDataItem) => CustomRender>;
subMenuItemRender: WithFalse<(item: MenuDataItem, children?: CustomRender[]) => CustomRender>;
}
export interface BaseMenuProps
extends Partial<PureSettings>,
PrivateSiderMenuProps,
CustomMenuRender {
menuProps?: Record<string, any>;
prefixCls?: string;
collapsed?: boolean;
splitMenus?: boolean;
......@@ -78,6 +86,18 @@ export const baseMenuProps = {
type: Array as PropType<WithFalse<string[]>>,
default: undefined,
},
menuProps: {
type: Object as PropType<BaseMenuProps['menuProps']>,
default: () => null,
},
menuItemRender: {
type: [Function, Boolean] as PropType<BaseMenuProps['menuItemRender']>,
default: () => false,
},
subMenuItemRender: {
type: [Function, Boolean] as PropType<BaseMenuProps['subMenuItemRender']>,
default: () => false,
},
};
const IconFont = createFromIconfontCN({
......@@ -123,12 +143,19 @@ class MenuUtil {
return menusData.map(item => this.getSubMenuOrItem(item, isChildren)).filter(item => item);
};
getSubMenuOrItem = (item: MenuDataItem, isChildren: boolean) => {
getSubMenuOrItem = (item: MenuDataItem, isChildren: boolean): VNode => {
if (
Array.isArray(item.children) &&
item.children.length > 0 &&
!item?.meta?.hideInMenu &&
!item?.meta?.hideChildrenInMenu
) {
if (this.props.subMenuItemRender) {
return this.props.subMenuItemRender(
item,
this.getNavMenuItems(item.children, true),
) as VNode;
}
const { prefixCls, i18n } = this.props;
const menuTitle = (i18n && i18n(item.meta?.title)) || item.meta?.title;
const defaultTitle = item.meta?.icon ? (
......@@ -139,6 +166,7 @@ class MenuUtil {
) : (
<span class={`${prefixCls}-menu-item`}>{menuTitle}</span>
);
const MenuComponent = item.meta?.type === 'group' ? Menu.ItemGroup : Menu.SubMenu;
return (
<MenuComponent title={defaultTitle} key={item.path}>
......@@ -148,14 +176,16 @@ class MenuUtil {
}
return (
<Menu.Item
inlineIndent={24}
disabled={item.meta?.disabled}
key={item.path}
// onClick={}
>
{this.getMenuItem(item, isChildren)}
</Menu.Item>
((this.props.menuItemRender && this.props.menuItemRender(item)) as VNode) || (
<Menu.Item
inlineIndent={24}
disabled={item.meta?.disabled}
key={item.path}
// onClick={}
>
{this.getMenuItem(item, isChildren)}
</Menu.Item>
)
);
};
......@@ -165,7 +195,7 @@ class MenuUtil {
const hasUrl = isUrl(item.path);
const CustomTag: any = resolveComponent((target && 'a') || 'router-link');
const props = { to: { name: item.name } };
const attrs = hasUrl || target ? { href: item.path, target: target } : {};
const attrs = hasUrl || target ? { ...item.meta, href: item.path, target: target } : {};
const { prefixCls, i18n } = this.props;
const menuTitle = (i18n && i18n(item.meta?.title)) || item.meta?.title;
......@@ -227,6 +257,7 @@ export default defineComponent({
selectedKeys={props.selectedKeys || []}
onOpenChange={handleOpenChange}
onSelect={handleSelect}
{...props.menuProps}
>
{menuUtil.getNavMenuItems(props.menuData, false)}
</Menu>
......
......@@ -137,6 +137,8 @@ const SiderMenu: FunctionalComponent<SiderMenuProps> = (props: SiderMenuProps) =
collapsed={props.collapsed}
openKeys={context.openKeys}
selectedKeys={context.selectedKeys}
menuItemRender={props.menuItemRender}
subMenuItemRender={props.subMenuItemRender}
style={{
width: '100%',
}}
......
......@@ -7,7 +7,7 @@ import SiderMenu, { SiderMenuProps, PrivateSiderMenuProps } from './SiderMenu';
export type SiderMenuWrapperProps = SiderMenuProps & Partial<PrivateSiderMenuProps>;
const SiderMenuWrapper: FunctionalComponent<SiderMenuWrapperProps> = props => {
const SiderMenuWrapper: FunctionalComponent<SiderMenuWrapperProps> = (props, { attrs }) => {
return props.isMobile ? (
<Drawer
visible={!props.collapsed}
......@@ -22,13 +22,14 @@ const SiderMenuWrapper: FunctionalComponent<SiderMenuWrapperProps> = props => {
bodyStyle={{ height: '100vh', padding: 0, display: 'flex', flexDirection: 'row' }}
>
<SiderMenu
{...attrs}
{...props}
collapsed={props.isMobile ? false : props.collapsed}
splitMenus={false}
/>
</Drawer>
) : (
<SiderMenu {...props} />
<SiderMenu {...attrs} {...props} />
);
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment