Commit 3d511d12 authored by Sendya's avatar Sendya

feat: add slot menuHeaderRender

parent f016cf10
......@@ -29,7 +29,6 @@ export default {
```vue
<template>
<pro-layout
title="Ant Design Pro"
:menus="menus"
:collapsed="collapsed"
:theme="theme"
......@@ -40,8 +39,13 @@ export default {
:isMobile="isMobile"
:handleMediaQuery="handleMediaQuery"
:handleCollapse="handleCollapse"
:logo="logoRender"
>
<template v-slot:menuHeaderRender>
<div>
<img src="../assets/logo.svg" />
<h1>Pro Layout</h1>
</div>
</template>
<template v-slot:rightContentRender>
<div :class="['ant-pro-global-header-index-right', layout === 'topmenu' && `ant-pro-global-header-index-${theme}`]">
rightContentRender
......@@ -61,9 +65,6 @@ export default {
import ProLayout, { SettingDrawer } from '@ant-design-vue/pro-layout'
import { asyncRouterMap } from '../config/router.config'
// import svg file
import LogoSvg from '../assets/logo.svg?inline'
export default {
name: 'BasicLayout',
data () {
......@@ -73,7 +74,7 @@ export default {
autoHideHeader: false,
query: {},
layout: 'sidemenu',
contentWidth: true,
contentWidth: false,
theme: 'dark',
isMobile: false
}
......@@ -95,9 +96,6 @@ export default {
},
handleCollapse (collapsed) {
this.collapsed = collapsed
},
logoRender () {
return <LogoSvg />
}
},
components: {
......@@ -127,7 +125,7 @@ export default {
| collapsed | control menu's collapse and expansion | boolean | true |
| isMobile | is mobile | boolean | false |
| handleCollapse | folding collapse event of menu | (collapsed: boolean) => void | - |
| menuHeaderRender | render logo and title | VNode \| (logo,title)=>VNode | - |
| menuHeaderRender | render logo and title | v-slot \| VNode \| (logo,title)=>VNode \| false | - |
| headerRender | custom header render method | (props: BasicLayoutProps) => VNode | - |
| rightContentRender | header right content render method | (props: HeaderViewProps) => VNode | - |
| collapsedButtonRender | custom collapsed button method | (collapsed: boolean) => VNode | - |
......
......@@ -27,7 +27,6 @@ export default {
```vue
<template>
<pro-layout
title="Ant Design Pro"
:menus="menus"
:collapsed="collapsed"
:theme="theme"
......@@ -38,8 +37,13 @@ export default {
:isMobile="isMobile"
:handleMediaQuery="handleMediaQuery"
:handleCollapse="handleCollapse"
:logo="logoRender"
>
<template v-slot:menuHeaderRender>
<div>
<img src="../assets/logo.svg" />
<h1>Pro Layout</h1>
</div>
</template>
<template v-slot:rightContentRender>
<div :class="['ant-pro-global-header-index-right', layout === 'topmenu' && `ant-pro-global-header-index-${theme}`]">
rightContentRender
......@@ -57,8 +61,6 @@ export default {
import ProLayout, { SettingDrawer } from '@ant-design-vue/pro-layout'
import { asyncRouterMap } from '../config/router.config'
import LogoSvg from '../assets/logo.svg?inline'
export default {
name: 'BasicLayout',
data () {
......@@ -68,7 +70,7 @@ export default {
autoHideHeader: false,
query: {},
layout: 'sidemenu',
contentWidth: true,
contentWidth: false,
theme: 'dark',
isMobile: false
}
......@@ -90,9 +92,6 @@ export default {
},
handleCollapse (collapsed) {
this.collapsed = collapsed
},
logoRender () {
return <LogoSvg />
}
},
components: {
......@@ -115,7 +114,7 @@ export default {
| title | layout 的 左上角 的 title | VNode \| String | `'Ant Design Pro'` |
| logo | layout 的 左上角 logo 的 url | VNode \| render | - |
| loading`*` | layout 的加载态 | boolean | - |
| menuHeaderRender | 渲染 logo 和 title | VNode \| (logo,title)=>VNode | - |
| menuHeaderRender | 渲染 logo 和 title | v-slot \| VNode \| (logo,title)=>VNode \| false | - |
| layout | layout 的菜单模式, sidemenu: 右侧导航, topmenu: 顶部导航 | 'sidemenu' \| 'topmenu' | `'sidemenu'` |
| contentWidth | layout 的内容模式,Fluid:定宽 1200px,Fixed:自适应 | true \| false | `false` |
| theme | 导航的主题 | 'light' \| 'dark' | `'dark'` |
......
// eslint-disable-next-line
import BasicLayout from '../layouts/BasicLayout'
import BasicLayout from '../layouts/BasicLayout.vue'
const RouteView = {
name: 'RouteView',
......@@ -40,7 +40,7 @@ const asyncRouterMap = [
icon: 'smile',
title: 'menu.dashboard.workplace'
},
component: () => import(/* webpackChunkName: "dashboard" */ '../views/TestPage')
component: () => import(/* webpackChunkName: "dashboard" */ '../views/dashboard/workplace')
}
]
},
......@@ -92,9 +92,9 @@ const asyncRouterMap = [
meta: {
keepAlive: true,
title: 'menu.nav1',
icon: 'video-camera'
icon: 'smile'
},
component: () => import(/* webpackChunkName: "about" */ '../views/TestPage2')
component: () => import(/* webpackChunkName: "about" */ '../views/TestPage1')
},
{
path: '/page2',
......@@ -102,7 +102,7 @@ const asyncRouterMap = [
meta: {
keepAlive: true,
title: 'menu.nav2',
icon: 'video-camera'
icon: 'smile'
},
component: () => import(/* webpackChunkName: "about" */ '../views/TestPage2')
},
......@@ -112,9 +112,9 @@ const asyncRouterMap = [
meta: {
keepAlive: true,
title: 'menu.nav3',
icon: 'video-camera'
icon: 'smile'
},
component: () => import(/* webpackChunkName: "about" */ '../views/TestPage2')
component: () => import(/* webpackChunkName: "about" */ '../views/TestPage3')
}
]
}
......
import './BasicLayout.less'
import { Avatar, Dropdown, Menu, Icon, Modal } from 'ant-design-vue'
import { asyncRouterMap } from '../config/router.config.js'
import { i18nRender } from '../locales'
import ProLayout, { GlobalFooter, SettingDrawer } from '@ant-design-vue/pro-layout'
import SelectLang from '../components/SelectLang'
import LogoSvg from '../assets/logo.svg?inline'
// import defaultSettings from '@config/defaultSettings'
const Account = {
name: 'Account',
render () {
const accountMenu = (
<Menu class="drop-down menu">
<Menu.Item key="info">
<Icon type={'user'} />个人信息
</Menu.Item>
<Menu.Item key="settings">
<Icon type={'setting'} />个人设置
</Menu.Item>
<Menu.Divider />
<Menu.Item key="logout" onClick={() => {
Modal.confirm({
title: i18nRender('layouts.usermenu.dialog.title'),
content: i18nRender('layouts.usermenu.dialog.content'),
onOk: () => {
return new Promise((resolve, reject) => {
setTimeout(Math.random() > 0.5 ? resolve : reject, 1500)
}).catch(() => console.log('Oops errors!'))
},
onCancel () {}
})
}}>
<Icon type={'logout'} />退出登录
</Menu.Item>
</Menu>
)
return (
<Dropdown overlay={accountMenu} placement="bottomRight">
<span class={'account'}>
<Avatar size="small" src="https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png" class="antd-pro-global-header-index-avatar" />
<span>Serati Ma</span>
</span>
</Dropdown>
)
}
}
// render logo and title
const menuHeaderRender = (h, logo, title) => {
return (
<div>
<LogoSvg />
<h1>Ant Design Pro</h1>
</div>
)
}
const rightContentRender = (h, props) => {
const cls = {
'ant-pro-global-header-index-right': true,
'ant-pro-global-header-topmenu': props.isTop,
[`ant-pro-global-header-index-${props.theme}`]: true
}
return (
<div class={cls}>
<Account class={'ant-pro-global-header-index-action'} />
<SelectLang class={'ant-pro-global-header-index-action'} />
</div>
)
}
const footerRender = (h, props) => {
return (
<GlobalFooter class={'footer custom-render'}>
<template slot="links">
<a href="https://www.github.com/vueComponent/" target="_self">Github</a>
<a href="https://www.github.com/sendya/" target="_self">@Sendya</a>
</template>
<template slot="copyright">
<a href="https://github.com/vueComponent">vueComponent</a>
</template>
</GlobalFooter>
)
}
export default {
name: 'BasicLayout',
data () {
return {
// 侧栏收起状态
collapsed: false,
// 媒体查询
query: {},
settings: {
// 布局类型
layout: 'sidemenu', // 'sidemenu', 'topmenu'
// 定宽: true / 流式: false
contentWidth: false,
fixedHeader: false,
fixSiderbar: false,
// 主题 'dark' | 'light'
theme: 'dark',
// 主色调
primaryColor: '#1890ff',
colorWeak: false,
hideHintAlert: false,
hideCopyButton: false
},
// 是否手机模式
isMobile: false
}
},
render (h) {
const handleMediaQuery = (val) => {
this.query = val
if (this.isMobile && !val['screen-xs']) {
this.isMobile = false
return
}
if (!this.isMobile && val['screen-xs']) {
this.isMobile = true
this.collapsed = false
}
}
const handleCollapse = (val) => {
this.collapsed = val
}
const menus = asyncRouterMap.find(item => item.path === '/').children
const handleSettingChange = ({ type, value, ...args }) => {
this.settings[type] = value
if (type === 'contentWidth') {
this.settings.contentWidth = value === 'Fixed'
}
if (type === 'layout') {
if (value === 'sidemenu') {
this.settings.contentWidth = false
} else {
this.settings.fixSiderbar = false
this.settings.contentWidth = true
}
}
}
// eslint-disable-next-line no-unused-vars
const breadcrumbRender = ({ route, params, routes, paths, h }) => {
return routes.indexOf(route) === routes.length - 1 && (
<span>{route.breadcrumbName}</span>
) || (
<router-link to={{ path: route.path || '/' }}>{route.breadcrumbName}</router-link>
)
}
const cdProps = {
props: {
...this.settings,
menus,
collapsed: this.collapsed,
mediaQuery: this.query,
handleMediaQuery,
handleCollapse,
isMobile: this.isMobile,
// custom render
rightContentRender,
footerRender,
// i18nRender: false,
i18nRender,
menuHeaderRender,
breadcrumbRender,
logo: LogoSvg,
title: 'Ant Design Pro'
}
}
return (
<ProLayout {...cdProps}>
<SettingDrawer
settings={this.settings}
onChange={handleSettingChange}
/>
<router-view />
</ProLayout>
)
}
}
<template>
<pro-layout
title="Ant Design Pro"
:menus="menus"
:collapsed="collapsed"
:theme="theme"
......@@ -11,9 +10,14 @@
:isMobile="isMobile"
:handleMediaQuery="handleMediaQuery"
:handleCollapse="handleCollapse"
:logo="logoRender"
:i18nRender="i18nRender"
>
<template v-slot:menuHeaderRender>
<div>
<img src="../assets/logo.svg" />
<h1>Pro Layout</h1>
</div>
</template>
<template v-slot:rightContentRender>
<div :class="['ant-pro-global-header-index-right', layout === 'topmenu' && `ant-pro-global-header-index-${theme}`]">
rightContentRender
......@@ -47,7 +51,7 @@ export default {
// 布局类型
layout: 'sidemenu', // 'sidemenu', 'topmenu'
// 定宽: true / 流式: false
contentWidth: true,
contentWidth: false,
// 主题 'dark' | 'light'
theme: 'dark',
// 是否手机模式
......
......@@ -13,7 +13,7 @@ export default {
menu: {
home: '首页',
dashboard: {
default: 'Dashboard',
default: '仪表盘',
analysis: '分析页',
workplace: '工作台'
},
......@@ -29,6 +29,11 @@ export default {
},
pages: {
dashboard: {
analysis: {
content: '分析页描述'
}
},
form: {
basicform: {
headers: {
......
<template>
<document-title title="我是PAGE1">
<div class="page-test-wrapper">
<h1>Test Page keepAlive: {{ $route.meta.keepAlive }}</h1>
<div class="box">
<div style="margin: 1em 0">
<a-input placeholder="home"/>
</div>
<a-button @click="handleClick">Click Me!</a-button><a-divider type="vertical" />
<a-button @click="handleRefresh">Refresh Current</a-button><a-divider type="vertical" />
<a-button @click="handleGetAllCache">Get All Cache</a-button><a-divider type="vertical" />
<a-button @click="handleChangeLang('zh-CN')">Lang zh-CN</a-button><a-divider type="vertical" />
<a-button @click="handleChangeLang('en-US')">Lang en-US</a-button>
<a-divider />
</div>
</div>
</document-title>
</template>
<script>
import { loadLanguageAsync } from '@/locales'
export default {
methods: {
handleClick (e) {
this.$router.push({ path: '/page2' })
},
handleClearPage1 () {
},
handleGetAllCache () {
this.$tab.caches((data) => {
})
},
handleRefresh () {
// this.$tab.refresh('/page2')
this.$tab.refresh()
},
handleChangeLang (lang) {
loadLanguageAsync(lang)
}
}
}
</script>
<style lang="less" scoped>
.page-test-wrapper {
width: 100%;
max-width: 400px;
margin: 0 auto;
}
</style>
<template>
<page-header-wrapper>
<div class="page-test-wrapper">
<h1>Test Page1 keepAlive: {{ $route.meta.keepAlive }}</h1>
<div class="box">
<div style="margin: 1em 0">
<a-input v-model="newVal" placeholder="..."/>
</div>
<a-button @click="handleClick">Click Me!</a-button>
<a-divider />
<p>{{ newVal }}</p>
</div>
</div>
</page-header-wrapper>
</template>
<script>
export default {
data () {
return {
newVal: ''
}
},
methods: {
handleClick (e) {
this.$router.push({ path: '/dashboard/analysis' })
}
}
}
</script>
<style lang="less" scoped>
.page-test-wrapper {
width: 100%;
max-width: 400px;
margin: 0 auto;
}
</style>
......@@ -15,8 +15,6 @@
</template>
<script>
import ProLayout, { PageHeaderWrapper } from '@ant-design-vue/pro-layout'
export default {
data () {
return {
......@@ -27,10 +25,6 @@ export default {
handleClick (e) {
this.$router.push({ path: '/dashboard/analysis' })
}
},
components: {
ProLayout,
PageHeaderWrapper
}
}
</script>
......
<template>
<page-header-wrapper>
<div class="page-test-wrapper">
<h1>Test Page3 keepAlive: {{ $route.meta.keepAlive }}</h1>
<div class="box">
<div style="margin: 1em 0">
<a-input v-model="newVal" placeholder="..."/>
</div>
<a-button @click="handleClick">Click Me!</a-button>
<a-divider />
<p>{{ newVal }}</p>
</div>
</div>
</page-header-wrapper>
</template>
<script>
export default {
data () {
return {
newVal: ''
}
},
methods: {
handleClick (e) {
this.$router.push({ path: '/dashboard/analysis' })
}
}
}
</script>
<style lang="less" scoped>
.page-test-wrapper {
width: 100%;
max-width: 400px;
margin: 0 auto;
}
</style>
<template>
<div>
Workplace Page
</div>
</template>
<script>
export default {
name: 'Analysis',
data () {
return {
console: window.console,
tabList: [
{ tab: 'pages.form.basicform.tabs.tab1', key: 'tab1' },
{ tab: 'pages.form.basicform.tabs.tab2', key: 'tab2' },
{ tab: 'pages.form.basicform.tabs.tab3', key: 'tab3' }
],
tabActiveKey: 'tab1'
}
},
methods: {
handleTabChange (key) {
this.tabActiveKey = key
console.log('PageHeader::tabChange', key)
}
}
}
</script>
<style scoped>
</style>
......@@ -6,9 +6,6 @@
this.tabActiveKey = key
console.log('PageHeader::tabChange', key)
}"
@back="(e) => {
console.log('PageHeader::back', e)
}"
:breadcrumb="customBreadcrumb"
>
<template v-slot:content>
......
......@@ -92,6 +92,7 @@ const BasicLayout = {
const collapsedButtonRender = getComponentFromProp(content, 'collapsedButtonRender')
const menuHeaderRender = getComponentFromProp(content, 'menuHeaderRender')
const breadcrumbRender = getComponentFromProp(content, 'breadcrumbRender')
const headerContentRender = getComponentFromProp(content, 'headerContentRender')
const isTopMenu = layout === 'topmenu'
const hasSiderMenu = !isTopMenu
......@@ -105,7 +106,8 @@ const BasicLayout = {
menuHeaderRender,
rightContentRender,
collapsedButtonRender,
breadcrumbRender
breadcrumbRender,
headerContentRender
}
return (
......
......@@ -14,6 +14,7 @@ export const GlobalHeaderProps = {
logo: PropTypes.any,
menuRender: PropTypes.any,
collapsedButtonRender: PropTypes.any,
headerContentRender: PropTypes.any,
rightContentRender: PropTypes.any,
}
......@@ -25,7 +26,7 @@ const GlobalHeader = {
name: 'GlobalHeader',
props: GlobalHeaderProps,
render (h) {
const { isMobile, logo, rightContentRender } = this.$props
const { isMobile, logo, rightContentRender, headerContentRender } = this.$props
const toggle = () => {
const { collapsed, handleCollapse } = this.$props
if (handleCollapse) handleCollapse(!collapsed)
......@@ -57,6 +58,9 @@ const GlobalHeader = {
</a>
)}
{renderCollapsedButton()}
<div class={`${headerCls}-content`}>
{isFun(headerContentRender) && headerContentRender(h, this.$props) || headerContentRender}
</div>
{isFun(rightContentRender) && rightContentRender(h, this.$props) || rightContentRender}
</div>
)
......
......@@ -2,6 +2,7 @@ import './index.less'
import PropTypes from 'ant-design-vue/es/_util/vue-types'
import { Layout } from 'ant-design-vue'
import { isFun } from '../../utils/util'
import BaseMenu from '../RouteMenu'
const { Sider } = Layout
......@@ -21,7 +22,8 @@ export const SiderMenuProps = {
fixSiderbar: PropTypes.bool,
logo: PropTypes.any,
title: PropTypes.string.def(''),
menuHeaderRender: PropTypes.func,
// render function or vnode
menuHeaderRender: PropTypes.oneOfType([PropTypes.func, PropTypes.array, PropTypes.object, PropTypes.bool]),
}
export const defaultRenderLogo = (h, logo) => {
......@@ -48,7 +50,9 @@ export const defaultRenderLogoAntTitle = (h, props) => {
const titleDom = <h1>{title}</h1>
if (menuHeaderRender) {
return menuHeaderRender(h, logoDom, props.collapsed ? null : titleDom, props)
return isFun(menuHeaderRender)
&& menuHeaderRender(h, logoDom, props.collapsed ? null : titleDom, props)
|| menuHeaderRender
}
return (
<span>
......
......@@ -14,11 +14,11 @@
line-height: @nav-header-height;
background: @layout-sider-background;
svg, h1 {
svg, img, h1 {
display: inline-block;
}
svg {
svg, img {
height: 32px;
width: 32px;
vertical-align: middle;
......
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