2021-03-05 19:10:39 +08:00
|
|
|
|
# @fesjs/plugin-layout
|
|
|
|
|
|
|
|
|
|
## 介绍
|
|
|
|
|
为了进一步降低研发成本,我们尝试将布局通过 fes 插件的方式内置,只需通过简单的配置即可拥有布局,包括导航以及侧边栏。从而做到用户无需关心布局。
|
|
|
|
|
- 侧边栏菜单数据根据路由中的配置自动生成。
|
|
|
|
|
- 布局,提供 `side`、 `top`、`mixin` 三种布局。
|
|
|
|
|
- 主题,提供 `light`、`dark` 两种主题。
|
|
|
|
|
- 默认实现对路由的 404、403 处理。
|
|
|
|
|
- 搭配 [@fesjs/plugin-access](./access.html) 插件使用,可以完成对路由的权限控制。
|
2021-03-07 18:12:27 +08:00
|
|
|
|
- 搭配 [@fesjs/plugin-locale](./locale.html) 插件使用,提供切换语言的能力。
|
2021-03-05 19:10:39 +08:00
|
|
|
|
- 支持自定义头部区域。
|
2021-03-15 20:20:32 +08:00
|
|
|
|
- 菜单支持配置icon
|
|
|
|
|
- 菜单标题支持国际化
|
2021-03-05 19:10:39 +08:00
|
|
|
|
|
|
|
|
|
- 可配置页面是否需要 layout。
|
|
|
|
|
|
2021-07-27 16:23:00 +08:00
|
|
|
|
## 启用方式
|
|
|
|
|
在 `package.json` 中引入依赖:
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"dependencies": {
|
|
|
|
|
"@fesjs/fes": "^2.0.0",
|
2022-04-11 19:40:16 +08:00
|
|
|
|
"@fesjs/plugin-layout": "^4.0.0"
|
2021-07-27 16:23:00 +08:00
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2021-03-05 19:10:39 +08:00
|
|
|
|
## 布局类型
|
2021-07-27 16:23:00 +08:00
|
|
|
|
配置参数是 `navigation`, 布局有三种类型 `side`、`mixin` 和 `top`, 默认是 `side`:
|
2021-03-11 16:19:13 +08:00
|
|
|
|
```js
|
|
|
|
|
export default {
|
|
|
|
|
layout: {
|
2021-07-27 16:23:00 +08:00
|
|
|
|
navigation: 'side'
|
2021-03-11 16:19:13 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
2021-03-05 19:10:39 +08:00
|
|
|
|
|
|
|
|
|
### side
|
2021-03-06 15:22:09 +08:00
|
|
|
|
<!-- ![side](/side.png) -->
|
|
|
|
|
<img :src="$withBase('side.png')" alt="side">
|
|
|
|
|
|
2021-03-05 19:10:39 +08:00
|
|
|
|
### top
|
2021-03-06 15:22:09 +08:00
|
|
|
|
<!-- ![top](/top.png) -->
|
|
|
|
|
<img :src="$withBase('top.png')" alt="top">
|
2021-05-19 21:07:21 +08:00
|
|
|
|
|
2021-03-05 19:10:39 +08:00
|
|
|
|
### mixin
|
2021-03-06 15:22:09 +08:00
|
|
|
|
<!-- ![mixin](/mixin.png) -->
|
|
|
|
|
<img :src="$withBase('mixin.png')" alt="mixin">
|
2021-03-05 19:10:39 +08:00
|
|
|
|
|
|
|
|
|
### 页面禁用布局
|
2021-07-27 16:23:00 +08:00
|
|
|
|
布局是默认开启的,但是可能某些页面不需要展示布局样式,比如登录页面。我们只需要在页面的`.vue`中添加如下配置:
|
2021-03-05 19:10:39 +08:00
|
|
|
|
```vue
|
2021-07-27 16:23:00 +08:00
|
|
|
|
<config lang="json">
|
2021-03-05 19:10:39 +08:00
|
|
|
|
{
|
|
|
|
|
"layout": false
|
|
|
|
|
}
|
|
|
|
|
</config>
|
|
|
|
|
```
|
2022-01-05 16:38:20 +08:00
|
|
|
|
如果只是不想展示`sidebar`,则:
|
2021-07-27 20:30:34 +08:00
|
|
|
|
```
|
2021-07-27 16:23:00 +08:00
|
|
|
|
<config lang="json">
|
|
|
|
|
{
|
|
|
|
|
"layout": {
|
2022-01-05 16:38:20 +08:00
|
|
|
|
"sidebar": false
|
2021-07-27 16:23:00 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</config>
|
|
|
|
|
```
|
|
|
|
|
`layout`的可选配置有:
|
|
|
|
|
|
2022-01-05 16:38:20 +08:00
|
|
|
|
- **sidebar**: 左侧区域,从v4.0.0开始,之前名称叫`side`
|
2021-07-27 16:23:00 +08:00
|
|
|
|
|
2022-01-10 12:48:16 +08:00
|
|
|
|
- **header**: 头部区域,从v4.0.0开始,之前名称叫`top`
|
2021-07-27 16:23:00 +08:00
|
|
|
|
|
|
|
|
|
- **logo**:logo和标题区域。
|
2021-03-05 19:10:39 +08:00
|
|
|
|
|
|
|
|
|
## 配置
|
|
|
|
|
|
2022-03-22 11:58:10 +08:00
|
|
|
|
### keep-alive
|
|
|
|
|
从 4.0.7 开始支持配置路由页面缓存:
|
|
|
|
|
```
|
|
|
|
|
<config lang="json">
|
|
|
|
|
{
|
|
|
|
|
"keep-alive": true
|
|
|
|
|
}
|
|
|
|
|
</config>
|
|
|
|
|
```
|
|
|
|
|
|
2021-03-05 19:10:39 +08:00
|
|
|
|
### 编译时配置
|
|
|
|
|
在 `.fes.js` 中配置:
|
|
|
|
|
```js
|
|
|
|
|
export default {
|
|
|
|
|
layout: {
|
2021-03-11 16:19:13 +08:00
|
|
|
|
// 标题
|
2021-03-05 19:10:39 +08:00
|
|
|
|
title: "Fes.js",
|
2021-03-11 16:19:13 +08:00
|
|
|
|
// 底部文字
|
2022-01-04 16:19:17 +08:00
|
|
|
|
footer: 'Created by MumbleFE',
|
2021-05-19 21:07:21 +08:00
|
|
|
|
// 主题light
|
|
|
|
|
theme: 'dark'
|
2021-03-11 16:19:13 +08:00
|
|
|
|
// 是否开启 tabs
|
2021-03-05 19:10:39 +08:00
|
|
|
|
multiTabs: false,
|
2021-03-11 16:19:13 +08:00
|
|
|
|
// 布局类型
|
|
|
|
|
navigation: 'side',
|
|
|
|
|
// 是否固定头部
|
|
|
|
|
fixedHeader: false,
|
|
|
|
|
// 是否固定sidebar
|
|
|
|
|
fixedSideBar: true,
|
|
|
|
|
// sidebar的宽度
|
|
|
|
|
sideWidth: 200,
|
2021-03-05 19:10:39 +08:00
|
|
|
|
menus: [{
|
|
|
|
|
name: 'index'
|
|
|
|
|
}, {
|
|
|
|
|
name: 'onepiece'
|
|
|
|
|
}, {
|
|
|
|
|
name: 'store'
|
|
|
|
|
}, {
|
|
|
|
|
name: 'simpleList'
|
2022-04-11 19:40:16 +08:00
|
|
|
|
}],
|
|
|
|
|
menuConfig: {
|
|
|
|
|
defaultExpandAll: false,
|
|
|
|
|
expandedKeys: [],
|
|
|
|
|
accordion: false
|
|
|
|
|
}
|
2021-03-05 19:10:39 +08:00
|
|
|
|
},
|
|
|
|
|
```
|
|
|
|
|
|
2021-12-16 16:25:46 +08:00
|
|
|
|
#### footer
|
2021-03-11 16:19:13 +08:00
|
|
|
|
- **类型**:`String`
|
|
|
|
|
|
|
|
|
|
- **默认值**:`null`
|
|
|
|
|
|
|
|
|
|
- **详情**:页面底部的文字。
|
|
|
|
|
|
2021-12-16 16:25:46 +08:00
|
|
|
|
#### theme
|
2021-05-19 21:07:21 +08:00
|
|
|
|
- **类型**:`String`
|
|
|
|
|
|
|
|
|
|
- **默认值**:`dark`
|
|
|
|
|
|
|
|
|
|
- **详情**:主题,可选有 `dark`、`light`
|
|
|
|
|
|
2021-12-16 16:25:46 +08:00
|
|
|
|
#### navigation
|
2021-03-11 16:19:13 +08:00
|
|
|
|
- **类型**:`String`
|
|
|
|
|
|
|
|
|
|
- **默认值**:`side`
|
|
|
|
|
|
|
|
|
|
- **详情**:页面布局类型,可选有 `side`、 `top`、 `mixin`
|
|
|
|
|
|
2021-12-16 16:25:46 +08:00
|
|
|
|
#### fixedHeader
|
2021-03-11 16:19:13 +08:00
|
|
|
|
- **类型**:`Boolean`
|
|
|
|
|
|
|
|
|
|
- **默认值**:`false`
|
|
|
|
|
|
|
|
|
|
- **详情**:是否固定头部,不跟随页面滚动。
|
|
|
|
|
|
2021-12-16 16:25:46 +08:00
|
|
|
|
#### fixedSideBar
|
2021-03-11 16:19:13 +08:00
|
|
|
|
- **类型**:`Boolean`
|
|
|
|
|
|
|
|
|
|
- **默认值**:`true`
|
|
|
|
|
|
|
|
|
|
- **详情**:是否固定sidebar,不跟随页面滚动。
|
|
|
|
|
|
2021-03-05 19:10:39 +08:00
|
|
|
|
#### title
|
|
|
|
|
- **类型**:`String`
|
|
|
|
|
|
|
|
|
|
- **默认值**:`name` in package.json
|
|
|
|
|
|
|
|
|
|
- **详情**:产品名,会显示在 Logo 旁边。
|
|
|
|
|
|
|
|
|
|
#### logo
|
|
|
|
|
- **类型**:`String`
|
|
|
|
|
|
|
|
|
|
- **默认值**:默认提供 fes.js 的 Logo
|
|
|
|
|
|
|
|
|
|
- **详情**:Logo,会显示在布局上。
|
|
|
|
|
|
|
|
|
|
#### locale
|
|
|
|
|
- **类型**:`boolean`
|
|
|
|
|
|
|
|
|
|
- **默认值**:`false`
|
|
|
|
|
|
|
|
|
|
- **详情**:是否显示语言选择框。
|
|
|
|
|
|
|
|
|
|
#### multiTabs
|
|
|
|
|
- **类型**:`boolean`
|
|
|
|
|
|
|
|
|
|
- **默认值**:`false`
|
|
|
|
|
|
|
|
|
|
- **详情**:是否开启多页。
|
|
|
|
|
|
|
|
|
|
#### menus
|
|
|
|
|
- **类型**:`Array`
|
|
|
|
|
|
|
|
|
|
- **默认值**:`[]`
|
|
|
|
|
|
|
|
|
|
- **详情**:菜单配置,子项具体配置如下:
|
|
|
|
|
|
2021-12-16 16:25:46 +08:00
|
|
|
|
- **name**:菜单的名称。通过匹配 `name` 和路由元信息 [meta](../../../guide/route.md#扩展路由元信息) 中的 `name`,把菜单和路由关联起来,然后使用路由元信息补充菜单配置,比如 `title`、`path` 等。
|
2021-03-05 19:10:39 +08:00
|
|
|
|
|
|
|
|
|
- **path**:菜单的路径,可配置第三方地址。
|
2022-01-05 16:38:20 +08:00
|
|
|
|
|
|
|
|
|
- **match**:额外匹配的路径,当前路由命中匹配规则时,此菜单高亮。 (v4.0.0+)
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
{
|
|
|
|
|
path: '/product',
|
|
|
|
|
match: ['/product/*', '/product/create']
|
|
|
|
|
}
|
|
|
|
|
```
|
2021-03-05 19:10:39 +08:00
|
|
|
|
|
2022-01-04 16:19:17 +08:00
|
|
|
|
- **title**:菜单的标题,如果同时使用[国际化插件](./locale.md),而且`title`的值以`$`开头,则使用`$`后面的内容去匹配语言设置。
|
2021-03-07 21:41:25 +08:00
|
|
|
|
|
2021-03-15 20:20:32 +08:00
|
|
|
|
- **icon**: 菜单的图标,只有一级标题展示图标。
|
2022-01-04 16:19:17 +08:00
|
|
|
|
- 图标使用[fes-design icon](https://fes-design-4gvn317r3b6bfe17-1254145788.ap-shanghai.app.tcloudbase.com/zh/components/icon.html),在这里使用组件名称。
|
2021-03-15 20:20:32 +08:00
|
|
|
|
```js
|
|
|
|
|
{
|
2022-01-04 16:19:17 +08:00
|
|
|
|
icon: "AppstoreOutlined"
|
2021-03-15 20:20:32 +08:00
|
|
|
|
}
|
|
|
|
|
```
|
2021-07-27 16:23:00 +08:00
|
|
|
|
- 图标使用本地或者远程svg图片。
|
2021-03-15 20:20:32 +08:00
|
|
|
|
```js
|
|
|
|
|
{
|
2021-07-27 16:23:00 +08:00
|
|
|
|
icon: "/wine-outline.svg"
|
2021-03-15 20:20:32 +08:00
|
|
|
|
}
|
|
|
|
|
```
|
2021-03-05 19:10:39 +08:00
|
|
|
|
|
|
|
|
|
- **children**:子菜单配置。
|
2022-04-11 19:40:16 +08:00
|
|
|
|
|
|
|
|
|
#### menusConfig
|
|
|
|
|
- **类型**:`Object`
|
|
|
|
|
|
|
|
|
|
- **默认值**:`{}`
|
|
|
|
|
|
|
|
|
|
- **详情**:菜单的配置:
|
|
|
|
|
|
|
|
|
|
- **defaultExpandAll**:是否默认展开全部菜单。
|
2021-03-05 19:10:39 +08:00
|
|
|
|
|
2022-04-11 19:40:16 +08:00
|
|
|
|
- **expandedKeys**:配置默认展开的菜单,需要传子项是菜单路径的数组。
|
|
|
|
|
|
|
|
|
|
- **accordion**:是否只保持一个子菜单的展开。
|
2021-03-05 19:10:39 +08:00
|
|
|
|
|
|
|
|
|
### 运行时配置
|
|
|
|
|
在 `app.js` 中配置:
|
|
|
|
|
```js
|
|
|
|
|
import UserCenter from '@/components/UserCenter';
|
|
|
|
|
export const layout = {
|
|
|
|
|
customHeader: <UserCenter />
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
```
|
2021-12-16 16:25:46 +08:00
|
|
|
|
|
2022-04-11 19:40:16 +08:00
|
|
|
|
#### menus
|
|
|
|
|
- **类型**:`(defaultMenus: [] )=> Ref | []`
|
|
|
|
|
|
|
|
|
|
- **详情**:运行时修改菜单,入参是默认菜单配置(.fes.js中的menu配置),需要返回一个`Ref`或者数组。
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
import { ClusterOutlined } from '@fesjs/fes-design/icon'
|
|
|
|
|
export const layout = layoutConfig => ({
|
|
|
|
|
...layoutConfig,
|
|
|
|
|
customHeader: <UserCenter />,
|
|
|
|
|
menus: (defaultMenuData) => {
|
|
|
|
|
const menusRef = ref(defaultMenuData);
|
|
|
|
|
watch(() => layoutConfig.initialState.userName, () => {
|
|
|
|
|
menusRef.value = [{
|
|
|
|
|
name: 'store',
|
|
|
|
|
icon: <ClusterOutlined />
|
|
|
|
|
}];
|
|
|
|
|
});
|
|
|
|
|
return menusRef;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
`layoutConfig.initialState` 是 `beforeRender.action`执行后创建的应用初始状态数据。
|
|
|
|
|
|
|
|
|
|
如果菜单需要根据某些状态动态改变,则返回`Ref`,否则只需要返回数组。
|
|
|
|
|
|
|
|
|
|
:::tip
|
|
|
|
|
在运行时配置菜单中的icon,需要传组件本身,而不是组件的名称。
|
2022-04-11 20:02:07 +08:00
|
|
|
|
:::
|
2022-04-11 19:40:16 +08:00
|
|
|
|
|
|
|
|
|
|
2022-01-10 12:48:16 +08:00
|
|
|
|
#### header
|
2021-12-16 16:25:46 +08:00
|
|
|
|
- **类型**:`String`
|
|
|
|
|
|
|
|
|
|
- **默认值**:`true`
|
|
|
|
|
|
2022-01-10 12:48:16 +08:00
|
|
|
|
- **详情**:是否显示 header 区域。
|
2021-12-16 16:25:46 +08:00
|
|
|
|
|
2022-01-10 12:48:16 +08:00
|
|
|
|
#### sidebar
|
2021-12-16 16:25:46 +08:00
|
|
|
|
- **类型**:`String`
|
|
|
|
|
|
|
|
|
|
- **默认值**:`true`
|
|
|
|
|
|
2022-01-10 12:48:16 +08:00
|
|
|
|
- **详情**:是否显示 sidebar 区域。
|
2021-12-16 16:25:46 +08:00
|
|
|
|
|
2022-01-10 12:48:16 +08:00
|
|
|
|
#### logo
|
2021-12-16 16:25:46 +08:00
|
|
|
|
- **类型**:`String`
|
|
|
|
|
|
|
|
|
|
- **默认值**:`true`
|
|
|
|
|
|
2022-01-10 12:48:16 +08:00
|
|
|
|
- **详情**:是否显示 logo 区域。
|
2021-12-16 16:25:46 +08:00
|
|
|
|
|
2021-03-05 19:10:39 +08:00
|
|
|
|
#### customHeader
|
|
|
|
|
- **类型**:Vue Component
|
|
|
|
|
|
|
|
|
|
- **默认值**:`null`
|
|
|
|
|
|
2021-07-27 16:23:00 +08:00
|
|
|
|
- **详情**:top的区域部分位置提供组件自定义功能。
|
2021-03-05 19:10:39 +08:00
|
|
|
|
|
|
|
|
|
#### unAccessHandler
|
|
|
|
|
- **类型**:`Function`
|
|
|
|
|
|
|
|
|
|
- **默认值**:`null`
|
|
|
|
|
|
|
|
|
|
- **详情**:
|
|
|
|
|
|
|
|
|
|
当进入某个路由时,如果路由对应的页面不属于可见资源列表,则会暂停进入,调用 `unAccessHandler` 函数。
|
|
|
|
|
- **参数**
|
|
|
|
|
- router:createRouter 创建的路由实例
|
|
|
|
|
- to: 准备进入的路由
|
|
|
|
|
- from:离开的路由
|
|
|
|
|
- next: [next函数](https://next.router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%8F%AF%E9%80%89%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-next)
|
|
|
|
|
|
|
|
|
|
比如:
|
|
|
|
|
```js
|
|
|
|
|
export const access = {
|
|
|
|
|
unAccessHandler({ to, next }) {
|
|
|
|
|
const accesssIds = accessApi.getAccess();
|
|
|
|
|
if (to.path === '/404') {
|
|
|
|
|
accessApi.setAccess(accesssIds.concat(['/404']));
|
|
|
|
|
return next('/404');
|
|
|
|
|
}
|
|
|
|
|
if (!accesssIds.includes('/403')) {
|
|
|
|
|
accessApi.setAccess(accesssIds.concat(['/403']));
|
|
|
|
|
}
|
|
|
|
|
next('/403');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### noFoundHandler
|
|
|
|
|
- **类型**:函数
|
|
|
|
|
|
|
|
|
|
- **默认值**:null
|
|
|
|
|
|
|
|
|
|
- **详情**:
|
|
|
|
|
|
|
|
|
|
当进入某个路由时,如果路由对应的页面不存在,则会调用 `noFoundHandler` 函数。
|
|
|
|
|
- **参数**
|
|
|
|
|
- router:createRouter 创建的路由实例
|
|
|
|
|
- to: 准备进入的路由
|
|
|
|
|
- from:离开的路由
|
|
|
|
|
- next: [next函数](https://next.router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%8F%AF%E9%80%89%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-next)
|
|
|
|
|
|
|
|
|
|
比如:
|
|
|
|
|
```js
|
|
|
|
|
export const access = {
|
|
|
|
|
noFoundHandler({ next }) {
|
|
|
|
|
const accesssIds = accessApi.getAccess();
|
|
|
|
|
if (!accesssIds.includes('/404')) {
|
|
|
|
|
accessApi.setAccess(accesssIds.concat(['/404']));
|
|
|
|
|
}
|
|
|
|
|
next('/404');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-07-27 16:23:00 +08:00
|
|
|
|
```
|