Commit a104e5cc authored by Sendya's avatar Sendya

fix: breadcrumbRender and props breadcrumb #10

parent 1187b501
English | [简体中文](./README.zh-CN.md) English | [简体中文](./README.zh-CN.md)
<h1 align="center">Ant Design Pro Layout</h1> <h1 align="center">Ant Design Pro Layout</h1>
## Usage ## Usage
```bash ```bash
...@@ -106,8 +107,12 @@ export default { ...@@ -106,8 +107,12 @@ export default {
</script> </script>
``` ```
## API ## API
### ProLayout ### ProLayout
| Property | Description | Type | Default Value | | Property | Description | Type | Default Value |
...@@ -127,15 +132,30 @@ export default { ...@@ -127,15 +132,30 @@ export default {
| rightContentRender | header right content render method | (props: HeaderViewProps) => VNode | - | | rightContentRender | header right content render method | (props: HeaderViewProps) => VNode | - |
| collapsedButtonRender | custom collapsed button method | (collapsed: boolean) => VNode | - | | collapsedButtonRender | custom collapsed button method | (collapsed: boolean) => VNode | - |
| footerRender | custom footer render method | (props: BasicLayoutProps) => VNode | - | | footerRender | custom footer render method | (props: BasicLayoutProps) => VNode | - |
| breadcrumbRender | custom breadcrumb render method | ({ route, params, routes, paths, h }) => VNode[] | - |
| i18nRender | i18n | Function (key: string) => string | - | | i18nRender | i18n | Function (key: string) => string | - |
| handleMediaQuery | media matchs callback | (querys: []) => void | - | | handleMediaQuery | media matchs callback | (querys: []) => void | - |
| mediaQuery | media matchs | Array | - | | mediaQuery | media matchs | Array | - |
### PageHeaderWrapper
| Property | Description | Type | Default Value |
| --- | --- | --- | --- |
| content | Content area | VNode \| v-slot | - |
| extra | Extra content area, on the right side of content | VNode \| v-slot | - |
| extraContent | Extra content area, on the right side of content | VNode \| v-slot | - |
| tabList | Tabs title list | `Array<{key: string, tab: sting}>` | - |
| tab-change | Switch panel callback | (key) => void | - |
| tab-active-key | The currently highlighted tab item | string | - |
### SettingDrawer ### SettingDrawer
#### {settings}
| Property | Description | Type | Default Value | | Property | Description | Type | Default Value |
| ---- | ---- | ---- | ---- | | ---- | ---- | ---- | ---- |
| theme | Theme | `dark` `light` `realDark` | `light` | | theme | Theme | `dark` `light` `realDark` | `light` |
......
...@@ -102,8 +102,12 @@ export default { ...@@ -102,8 +102,12 @@ export default {
</script> </script>
``` ```
## API ## API
### ProLayout ### ProLayout
| Property | Description | Type | Default Value | | Property | Description | Type | Default Value |
...@@ -123,13 +127,30 @@ export default { ...@@ -123,13 +127,30 @@ export default {
| rightContentRender | 自定义头右部的 render 方法 | (props: HeaderViewProps) => VNode | - | | rightContentRender | 自定义头右部的 render 方法 | (props: HeaderViewProps) => VNode | - |
| collapsedButtonRender | 自定义 侧栏收缩按钮 的方法 | (collapsed: boolean) => VNode | - | | collapsedButtonRender | 自定义 侧栏收缩按钮 的方法 | (collapsed: boolean) => VNode | - |
| footerRender | 自定义 底部区域内容 | (props: BasicLayoutProps) => VNode | - | | footerRender | 自定义 底部区域内容 | (props: BasicLayoutProps) => VNode | - |
| breadcrumbRender | 自定义面包屑渲染方法 | ({ route, params, routes, paths, h }) => VNode[] | - |
| i18nRender | 本地化渲染函数 (this.$t) | Function (key: string) => string | - | | i18nRender | 本地化渲染函数 (this.$t) | Function (key: string) => string | - |
| handleMediaQuery | 媒体查询回调 | (querys: []) => void | - | | handleMediaQuery | 媒体查询回调 | (querys: []) => void | - |
| mediaQuery | ProLayout 当前的媒体查询 | Array | - | | mediaQuery | ProLayout 当前的媒体查询 | Array | - |
### PageHeaderWrapper
| Property | Description | Type | Default Value |
| --- | --- | --- | --- |
| content | 内容区 | VNode \| v-slot | - |
| extra | 扩展区域 | VNode \| v-slot | - |
| extraContent | 扩展内容区 | VNode \| v-slot | - |
| tabList | Tabs 导航 | `Array<{key: string, tab: sting}>` | - |
| tab-change | Tab 改变事件 | (key) => void | - |
| tab-active-key | 当前 Tab 选中项 | string | - |
### SettingDrawer ### SettingDrawer
#### {settings}
| Property | Description | Type | Default Value | | Property | Description | Type | Default Value |
| ---- | ---- | ---- | ---- | | ---- | ---- | ---- | ---- |
| theme | 主题 | `dark` `light` `realDark` | `light` | | theme | 主题 | `dark` `light` `realDark` | `light` |
......
...@@ -62,7 +62,7 @@ const asyncRouterMap = [ ...@@ -62,7 +62,7 @@ const asyncRouterMap = [
icon: 'smile', icon: 'smile',
title: 'menu.form.basicform' title: 'menu.form.basicform'
}, },
component: () => import(/* webpackChunkName: "about" */ '../views/BlockPage') component: () => import(/* webpackChunkName: "about" */ '../views/form/basic-form')
}, },
{ {
path: '/form/step-form', path: '/form/step-form',
...@@ -72,7 +72,7 @@ const asyncRouterMap = [ ...@@ -72,7 +72,7 @@ const asyncRouterMap = [
icon: 'smile', icon: 'smile',
title: 'menu.form.stepform' title: 'menu.form.stepform'
}, },
component: () => import(/* webpackChunkName: "about" */ '../views/BlockPage') component: () => import(/* webpackChunkName: "about" */ '../views/form/step-form')
}, },
{ {
path: '/form/advanced-form', path: '/form/advanced-form',
...@@ -82,7 +82,7 @@ const asyncRouterMap = [ ...@@ -82,7 +82,7 @@ const asyncRouterMap = [
icon: 'smile', icon: 'smile',
title: 'menu.form.advancedform' title: 'menu.form.advancedform'
}, },
component: () => import(/* webpackChunkName: "about" */ '../views/BlockPage') component: () => import(/* webpackChunkName: "about" */ '../views/form/advanced-form')
} }
] ]
}, },
......
export {
default as SettingOutline
} from '@ant-design/icons/lib/outline/SettingOutline'
export {
default as GithubOutline
} from '@ant-design/icons/lib/outline/GithubOutline'
export {
default as CopyrightOutline
} from '@ant-design/icons/lib/outline/CopyrightOutline'
/* MultiTab begin */
export {
default as CloseOutline
} from '@ant-design/icons/lib/outline/CloseOutline'
export {
default as ReloadOutline
} from '@ant-design/icons/lib/outline/ReloadOutline'
export {
default as DownOutline
} from '@ant-design/icons/lib/outline/DownOutline'
export {
default as AlignLeftOutline
} from '@ant-design/icons/lib/outline/AlignLeftOutline'
/* MultiTab end */
/* Layout begin */ /* Layout begin */
export { export {
default as LeftOutline default as LeftOutline
...@@ -72,4 +47,34 @@ export { ...@@ -72,4 +47,34 @@ export {
export { export {
default as NotificationOutline default as NotificationOutline
} from '@ant-design/icons/lib/outline/NotificationOutline' } from '@ant-design/icons/lib/outline/NotificationOutline'
export {
default as SettingOutline
} from '@ant-design/icons/lib/outline/SettingOutline'
export {
default as GithubOutline
} from '@ant-design/icons/lib/outline/GithubOutline'
export {
default as CopyrightOutline
} from '@ant-design/icons/lib/outline/CopyrightOutline'
/* Layout end */ /* Layout end */
/* Feedback begin */
export {
default as QuestionCircleOutline
} from '@ant-design/icons/lib/outline/QuestionCircleOutline'
/* Feedback end */
/* MultiTab begin */
export {
default as CloseOutline
} from '@ant-design/icons/lib/outline/CloseOutline'
export {
default as ReloadOutline
} from '@ant-design/icons/lib/outline/ReloadOutline'
export {
default as DownOutline
} from '@ant-design/icons/lib/outline/DownOutline'
export {
default as AlignLeftOutline
} from '@ant-design/icons/lib/outline/AlignLeftOutline'
/* MultiTab end */
...@@ -105,6 +105,7 @@ export default { ...@@ -105,6 +105,7 @@ export default {
theme: 'dark', theme: 'dark',
// 主色调 // 主色调
primaryColor: '#1890ff', primaryColor: '#1890ff',
colorWeak: false,
hideHintAlert: false, hideHintAlert: false,
hideCopyButton: false hideCopyButton: false
...@@ -132,7 +133,6 @@ export default { ...@@ -132,7 +133,6 @@ export default {
const menus = asyncRouterMap.find(item => item.path === '/').children const menus = asyncRouterMap.find(item => item.path === '/').children
const handleSettingChange = ({ type, value, ...args }) => { const handleSettingChange = ({ type, value, ...args }) => {
console.log('type', type, 'value', value, 'args:', args)
this.settings[type] = value this.settings[type] = value
if (type === 'contentWidth') { if (type === 'contentWidth') {
...@@ -148,6 +148,15 @@ export default { ...@@ -148,6 +148,15 @@ export default {
} }
} }
// 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 = { const cdProps = {
props: { props: {
...this.settings, ...this.settings,
...@@ -162,6 +171,7 @@ export default { ...@@ -162,6 +171,7 @@ export default {
footerRender, footerRender,
i18nRender, i18nRender,
menuHeaderRender, menuHeaderRender,
breadcrumbRender,
// logo: LogoSvg, // logo: LogoSvg,
title: 'Ant Design Pro' title: 'Ant Design Pro'
......
...@@ -15,13 +15,13 @@ const messages = { ...@@ -15,13 +15,13 @@ const messages = {
} }
const i18n = new VueI18n({ const i18n = new VueI18n({
silentTranslationWarn: true,
locale: defaultLang, locale: defaultLang,
fallbackLocale: defaultLang, fallbackLocale: defaultLang,
messages messages
}) })
const loadedLanguages = [defaultLang] const loadedLanguages = [defaultLang]
// eslint-disable-next-line
function setI18nLanguage (lang) { function setI18nLanguage (lang) {
i18n.locale = lang i18n.locale = lang
......
import Vue from 'vue' import Vue from 'vue'
import VueRouter from 'vue-router' import VueRouter from 'vue-router'
import { asyncRouterMap } from '../config/router.config' import { asyncRouterMap } from '@/config/router.config'
// hack router push/replace callback // hack router push/replace callback
['push', 'replace'].map(key => { ['push', 'replace'].map(key => {
...@@ -20,6 +20,7 @@ Vue.use(VueRouter) ...@@ -20,6 +20,7 @@ Vue.use(VueRouter)
const routes = asyncRouterMap const routes = asyncRouterMap
const router = new VueRouter({ const router = new VueRouter({
mode: 'history',
routes routes
}) })
......
<template>
<page-header-wrapper
:tab-list="tabList"
:tab-active-key="tabActiveKey"
:tab-change="(key) => {
this.tabActiveKey = key
console.log('PageHeader::tabChange', key)
}"
@back="() => {
console.log('PageHeader::@back')
}"
:back="() => {
// 自定义 back,不会覆盖 onBack 事件
console.log('PageHeader::.back')
}"
>
<template v-slot:content>
<span>{{ $t('pages.form.basicform.content') }}</span>
</template>
<template v-slot:extraContent>
<div><a-button>{{ $t('pages.form.basicform.headers.btn1') }}</a-button></div>
</template>
<div>
<strong>Advanced Form</strong>
</div>
</page-header-wrapper>
</template>
<script>
export default {
name: 'AdvancedForm',
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>
<template>
<page-header-wrapper
:tab-list="tabList"
:tab-active-key="tabActiveKey"
:tab-change="(key) => {
this.tabActiveKey = key
console.log('PageHeader::tabChange', key)
}"
@back="() => {
console.log('PageHeader::@back')
}"
:back="() => {
// 自定义 back,不会覆盖 onBack 事件
console.log('PageHeader::.back')
}"
>
<template v-slot:content>
<span>{{ $t('pages.form.basicform.content') }}</span>
</template>
<template v-slot:extraContent>
<div><a-button>{{ $t('pages.form.basicform.headers.btn1') }}</a-button></div>
</template>
<div>
<strong>Basic Form</strong>
</div>
</page-header-wrapper>
</template>
<script>
export default {
name: 'BasicForm',
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>
<template>
<page-header-wrapper
:tab-list="tabList"
:tab-active-key="tabActiveKey"
:tab-change="(key) => {
this.tabActiveKey = key
console.log('PageHeader::tabChange', key)
}"
@back="() => {
console.log('PageHeader::@back')
}"
:back="() => {
// 自定义 back,不会覆盖 onBack 事件
console.log('PageHeader::.back')
}"
:breadcrumb="customBreadcrumb"
>
<template v-slot:content>
<span>{{ $t('pages.form.basicform.content') }}</span>
</template>
<template v-slot:extraContent>
<div><a-button>{{ $t('pages.form.basicform.headers.btn1') }}</a-button></div>
</template>
<div>
<strong>Step Form</strong>
</div>
</page-header-wrapper>
</template>
<script>
export default {
name: 'StepForm',
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'
}
},
computed: {
customBreadcrumb () {
return {
props: {
routes: this.$route.matched.concat().map(route => {
return {
path: route.path,
breadcrumbName: this.$t(route.meta.title)
}
}),
itemRender: ({ 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>
)
}
}
}
}
},
methods: {
handleTabChange (key) {
this.tabActiveKey = key
console.log('PageHeader::tabChange', key)
}
}
}
</script>
<style scoped>
</style>
...@@ -93,8 +93,8 @@ const defaultConfig = { ...@@ -93,8 +93,8 @@ const defaultConfig = {
} }
}, },
devServer: { devServer: {
// development server port 8000 // default development server port 8000
port: 8000 port: 9001
// If you want to turn on the proxy, please remove the mockjs /src/main.jsL11 // If you want to turn on the proxy, please remove the mockjs /src/main.jsL11
// proxy: { // proxy: {
// '/api': { // '/api': {
......
import './BasicLayout.less' import './BasicLayout.less'
import PropTypes from 'ant-design-vue/es/_util/vue-types'
import { Layout } from 'ant-design-vue' import { Layout } from 'ant-design-vue'
import { ContainerQuery } from 'vue-container-query' import { ContainerQuery } from 'vue-container-query'
import { SiderMenuWrapper, GlobalFooter } from './components' import { SiderMenuWrapper, GlobalFooter } from './components'
...@@ -9,33 +11,17 @@ import HeaderView, { HeaderViewProps } from './Header' ...@@ -9,33 +11,17 @@ import HeaderView, { HeaderViewProps } from './Header'
import WrapContent from './WrapContent' import WrapContent from './WrapContent'
import ConfigProvider from './components/ConfigProvider' import ConfigProvider from './components/ConfigProvider'
const noop = () => {}
export const BasicLayoutProps = { export const BasicLayoutProps = {
...SiderMenuProps, ...SiderMenuProps,
...HeaderViewProps, ...HeaderViewProps,
locale: { locale: PropTypes.string.def('en-US'),
type: String, breadcrumbRender: PropTypes.func,
default: 'en-US' disableMobile: PropTypes.bool.def(false),
}, mediaQuery: PropTypes.object.def({}),
breadcrumbRender: { handleMediaQuery: PropTypes.func,
type: Function, footerRender: PropTypes.func,
default: () => undefined
},
disableMobile: {
type: Boolean,
default: false
},
mediaQuery: {
type: Object,
default: () => {}
},
handleMediaQuery: {
type: Function,
default: () => undefined
},
footerRender: {
type: Function,
default: () => undefined
}
} }
const MediaQueryEnum = { const MediaQueryEnum = {
...@@ -107,6 +93,8 @@ const BasicLayout = { ...@@ -107,6 +93,8 @@ const BasicLayout = {
const rightContentRender = getComponentFromProp(content, 'rightContentRender') const rightContentRender = getComponentFromProp(content, 'rightContentRender')
const collapsedButtonRender = getComponentFromProp(content, 'collapsedButtonRender') const collapsedButtonRender = getComponentFromProp(content, 'collapsedButtonRender')
const menuHeaderRender = getComponentFromProp(content, 'menuHeaderRender') const menuHeaderRender = getComponentFromProp(content, 'menuHeaderRender')
const breadcrumbRender = getComponentFromProp(content, 'breadcrumbRender')
const isTopMenu = layout === 'topmenu' const isTopMenu = layout === 'topmenu'
const hasSiderMenu = !isTopMenu const hasSiderMenu = !isTopMenu
// If it is a fix menu, calculate padding // If it is a fix menu, calculate padding
...@@ -118,11 +106,12 @@ const BasicLayout = { ...@@ -118,11 +106,12 @@ const BasicLayout = {
footerRender, footerRender,
menuHeaderRender, menuHeaderRender,
rightContentRender, rightContentRender,
collapsedButtonRender collapsedButtonRender,
breadcrumbRender
} }
return ( return (
<ConfigProvider i18nRender={i18nRender} contentWidth={contentWidth}> <ConfigProvider i18nRender={i18nRender} contentWidth={contentWidth} breadcrumbRender={breadcrumbRender}>
<ContainerQuery query={MediaQueryEnum} onChange={handleMediaQuery}> <ContainerQuery query={MediaQueryEnum} onChange={handleMediaQuery}>
<Layout class={{ <Layout class={{
'ant-pro-basicLayout': true, 'ant-pro-basicLayout': true,
......
...@@ -5,12 +5,14 @@ const ConfigProvider = { ...@@ -5,12 +5,14 @@ const ConfigProvider = {
props: { props: {
i18nRender: PropTypes.any, i18nRender: PropTypes.any,
contentWidth: PropTypes.bool, contentWidth: PropTypes.bool,
breadcrumbRender: PropTypes.func,
}, },
provide () { provide () {
const _self = this const _self = this
return { return {
locale: _self.$props.i18nRender, locale: _self.$props.i18nRender,
contentWidth: _self.$props.contentWidth contentWidth: _self.$props.contentWidth,
breadcrumbRender: _self.$props.breadcrumbRender,
} }
}, },
render () { render () {
......
...@@ -23,7 +23,7 @@ const PageHeaderWrapperProps = { ...@@ -23,7 +23,7 @@ const PageHeaderWrapperProps = {
content: PropTypes.any, content: PropTypes.any,
extraContent: PropTypes.any, extraContent: PropTypes.any,
pageHeaderRender: PropTypes.func, pageHeaderRender: PropTypes.func,
breadcrumb: PropTypes.oneOf([PropTypes.object, PropTypes.bool]).def(true), breadcrumb: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]).def(true),
back: PropTypes.func, back: PropTypes.func,
// 包装 pro-layout 才能使用 // 包装 pro-layout 才能使用
...@@ -131,14 +131,15 @@ const defaultPageHeaderRender = (h, props, pageMeta, i18nRender) => { ...@@ -131,14 +131,15 @@ const defaultPageHeaderRender = (h, props, pageMeta, i18nRender) => {
const PageHeaderWrapper = { const PageHeaderWrapper = {
name: 'PageHeaderWrapper', name: 'PageHeaderWrapper',
props: PageHeaderWrapperProps, props: PageHeaderWrapperProps,
inject: ['locale', 'contentWidth'], inject: ['locale', 'contentWidth', 'breadcrumbRender'],
render(h) { render(h) {
const { $route } = this
const children = this.$slots.default const children = this.$slots.default
const content = getComponentFromProp(this, 'content') const content = getComponentFromProp(this, 'content')
const extra = getComponentFromProp(this, 'extra') const extra = getComponentFromProp(this, 'extra')
const extraContent = getComponentFromProp(this, 'extraContent') const extraContent = getComponentFromProp(this, 'extraContent')
const pageMeta = useContext(this.$props.route || this.$route) const pageMeta = useContext(this.$props.route || $route)
const i18n = this.$props.i18nRender || this.locale || defaultI18nRender const i18n = this.$props.i18nRender || this.locale || defaultI18nRender
const contentWidth = this.$props.contentWidth || this.contentWidth || false const contentWidth = this.$props.contentWidth || this.contentWidth || false
// 当未设置 back props 或未监听 @back,不显示 back // 当未设置 back props 或未监听 @back,不显示 back
...@@ -155,26 +156,31 @@ const PageHeaderWrapper = { ...@@ -155,26 +156,31 @@ const PageHeaderWrapper = {
onTabChange && onTabChange(key) onTabChange && onTabChange(key)
} }
const propsBreadcrumb = this.$props.breadcrumb
let breadcrumb = {} let breadcrumb = {}
if (propsBreadcrumb === undefined) { const propsBreadcrumb = this.$props.breadcrumb
const routes = this.$route.matched.concat().map(route => { if (propsBreadcrumb === true) {
const routes = $route.matched.concat().map(route => {
return { return {
path: route.path, path: route.path,
breadcrumbName: i18n(route.meta.title), breadcrumbName: i18n(route.meta.title),
} }
}) })
// TODO:: warn -> abs path
const itemRender = ({ route, params, routes, paths, h }) => { const defaultItemRender = ({ route, params, routes, paths, h }) => {
return routes.indexOf(route) === routes.length - 1 && ( return routes.indexOf(route) === routes.length - 1 && (
<span>{route.breadcrumbName}</span> <span>{route.breadcrumbName}</span>
) || ( ) || (
<router-link to={{ path: route.path }}>{route.breadcrumbName}</router-link> <router-link to={{ path: route.path || '/', params }}>{route.breadcrumbName}</router-link>
) )
} }
// If custom breadcrumb render undefined
// use default breadcrumb..
const itemRender = this.breadcrumbRender || defaultItemRender
breadcrumb = { props: { routes, itemRender } } breadcrumb = { props: { routes, itemRender } }
} else { } else {
breadcrumb = propsBreadcrumb breadcrumb = propsBreadcrumb || null
} }
const props = { const 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