mirror of
https://github.com/WeBankFinTech/fes.js.git
synced 2025-04-05 03:05:07 +08:00
refactor: 完善useLayout (#250)
This commit is contained in:
parent
7c00f3defd
commit
35328603c6
@ -8,16 +8,16 @@ import { withBase } from 'vitepress'
|
||||
|
||||
为了进一步降低研发成本,我们将布局利用 `fes.js` 插件的方式内置,只需通过简单的配置即可拥有布局,包括导航以及侧边栏。从而做到用户无需关心布局。
|
||||
|
||||
- 侧边栏菜单数据根据路由中的配置自动生成。
|
||||
- 布局,提供 `side`、 `top`、`mixin`、`left-right`、`top-left-right` 五种布局。
|
||||
- 主题,提供 `light`、`dark` 两种主题。
|
||||
- 默认实现对路由的 404、403 处理。
|
||||
- 搭配 [@fesjs/plugin-access](./access.html) 插件使用,可以完成对路由的权限控制。
|
||||
- 搭配 [@fesjs/plugin-locale](./locale.html) 插件使用,提供切换语言的能力。
|
||||
- 支持自定义头部或者侧边栏区域。
|
||||
- 菜单支持配置 icon。
|
||||
- 菜单标题支持国际化。
|
||||
- 可配置页面是否需要 layout。
|
||||
- 侧边栏菜单数据根据路由中的配置自动生成。
|
||||
- 布局,提供 `side`、 `top`、`mixin`、`left-right`、`top-left-right` 五种布局。
|
||||
- 主题,提供 `light`、`dark` 两种主题。
|
||||
- 默认实现对路由的 404、403 处理。
|
||||
- 搭配 [@fesjs/plugin-access](./access.html) 插件使用,可以完成对路由的权限控制。
|
||||
- 搭配 [@fesjs/plugin-locale](./locale.html) 插件使用,提供切换语言的能力。
|
||||
- 支持自定义头部或者侧边栏区域。
|
||||
- 菜单支持配置 icon。
|
||||
- 菜单标题支持国际化。
|
||||
- 可配置页面是否需要 layout。
|
||||
|
||||
## 启用方式
|
||||
|
||||
@ -182,58 +182,58 @@ export function layout(layoutConfig, { initialState }) {
|
||||
|
||||
### footer
|
||||
|
||||
- **类型**:`String`
|
||||
- **默认值**:`null`
|
||||
- **类型**:`String`
|
||||
- **默认值**:`null`
|
||||
|
||||
- **详情**:页面底部的文字。
|
||||
- **详情**:页面底部的文字。
|
||||
|
||||
### theme
|
||||
|
||||
- **类型**:`String`
|
||||
- **默认值**:`dark`
|
||||
- **类型**:`String`
|
||||
- **默认值**:`dark`
|
||||
|
||||
- **详情**:主题,可选有 `dark`、`light`
|
||||
- **详情**:主题,可选有 `dark`、`light`
|
||||
|
||||
### navigation
|
||||
|
||||
- **类型**:`String`
|
||||
- **默认值**:`side`
|
||||
- **类型**:`String`
|
||||
- **默认值**:`side`
|
||||
|
||||
- **详情**:页面布局类型,可选有 `side`、 `top`、 `mixin`
|
||||
- **详情**:页面布局类型,可选有 `side`、 `top`、 `mixin`
|
||||
|
||||
### navigationOnError
|
||||
|
||||
- **类型**:`String`、`Function`
|
||||
- **类型**:`String`、`Function`
|
||||
|
||||
- **详情**:指定 `403`、`404` 时,页面的布局类型。值同 `navigation`。也支持函数返回。
|
||||
- **详情**:指定 `403`、`404` 时,页面的布局类型。值同 `navigation`。也支持函数返回。
|
||||
|
||||
### isFixedHeader
|
||||
|
||||
- **类型**:`Boolean`
|
||||
- **默认值**:`false`
|
||||
- **类型**:`Boolean`
|
||||
- **默认值**:`false`
|
||||
|
||||
- **详情**:是否固定头部,不跟随页面滚动。
|
||||
- **详情**:是否固定头部,不跟随页面滚动。
|
||||
|
||||
### isFixedSidebar
|
||||
|
||||
- **类型**:`Boolean`
|
||||
- **默认值**:`true`
|
||||
- **类型**:`Boolean`
|
||||
- **默认值**:`true`
|
||||
|
||||
- **详情**:是否固定 sidebar,不跟随页面滚动。
|
||||
- **详情**:是否固定 sidebar,不跟随页面滚动。
|
||||
|
||||
### title
|
||||
|
||||
- **类型**:`String`
|
||||
- **默认值**:默认为 [编译时配置 title](../../../reference/config/#title)
|
||||
- **类型**:`String`
|
||||
- **默认值**:默认为 [编译时配置 title](../../../reference/config/#title)
|
||||
|
||||
- **详情**:产品名。
|
||||
- **详情**:产品名。
|
||||
|
||||
### logo
|
||||
|
||||
- **类型**:`String`
|
||||
- **默认值**:默认提供 `fes.js` 的 Logo
|
||||
- **类型**:`String`
|
||||
- **默认值**:默认提供 `fes.js` 的 Logo
|
||||
|
||||
- **详情**:Logo 的链接,例如在 public/logo.png 放了一个 logo,可以这么配置([BASE_URL 来自这里](../../../guide/env#process-env))
|
||||
- **详情**:Logo 的链接,例如在 public/logo.png 放了一个 logo,可以这么配置([BASE_URL 来自这里](../../../guide/env#process-env))
|
||||
|
||||
```js
|
||||
export const layout = {
|
||||
@ -243,29 +243,29 @@ export const layout = {
|
||||
|
||||
### multiTabs
|
||||
|
||||
- **类型**:`boolean`
|
||||
- **默认值**:`false`
|
||||
- **类型**:`boolean`
|
||||
- **默认值**:`false`
|
||||
|
||||
- **详情**:是否开启多页。
|
||||
- **详情**:是否开启多页。
|
||||
|
||||
### menus
|
||||
|
||||
- **类型**:`[] | () => Ref<[]> | () => []`
|
||||
- **默认值**:`[]`
|
||||
- **类型**:`[] | () => Ref<[]> | () => []`
|
||||
- **默认值**:`[]`
|
||||
|
||||
- **详情**:菜单配置
|
||||
- **详情**:菜单配置
|
||||
|
||||
子项具体配置如下:
|
||||
|
||||
- **name**:菜单的名称。通过匹配 `name` 和路由元信息 [meta](../../../guide/route.md#扩展路由元信息) 中的 `name`,把菜单和路由关联起来, 然后使用路由元信息补充菜单配置,比如 `title`、`path` 等。
|
||||
- **name**:菜单的名称。通过匹配 `name` 和路由元信息 [meta](../../../guide/route.md#扩展路由元信息) 中的 `name`,把菜单和路由关联起来, 然后使用路由元信息补充菜单配置,比如 `title`、`path` 等。
|
||||
|
||||
- **path**:菜单的路径,可配置第三方地址。
|
||||
- **path**:菜单的路径,可配置第三方地址。
|
||||
|
||||
- **query**:同 vue-router 的 query 参数。
|
||||
- **query**:同 vue-router 的 query 参数。
|
||||
|
||||
- **params**:同 vue-router 的 params 参数。
|
||||
- **params**:同 vue-router 的 params 参数。
|
||||
|
||||
- **match (v4.0.0+)**:额外匹配的路径,当前路由命中匹配规则时,此菜单高亮。
|
||||
- **match (v4.0.0+)**:额外匹配的路径,当前路由命中匹配规则时,此菜单高亮。
|
||||
|
||||
```
|
||||
{
|
||||
@ -274,17 +274,17 @@ export const layout = {
|
||||
}
|
||||
```
|
||||
|
||||
- **title**:菜单的标题。
|
||||
- **title**:菜单的标题。
|
||||
|
||||
- 如果同时使用[国际化插件](./locale.md),而且`title`的值以`$`开头,则使用`$`后面的内容去匹配语言设置。
|
||||
- 如果同时使用[国际化插件](./locale.md),而且`title`的值以`$`开头,则使用`$`后面的内容去匹配语言设置。
|
||||
|
||||
- title 支持配置函数,对应 Fes Design 中 Menu 组件的`label`插槽。仅在运行时配置中支持。
|
||||
- title 支持配置函数,对应 Fes Design 中 Menu 组件的`label`插槽。仅在运行时配置中支持。
|
||||
|
||||
- **icon**: 菜单的图标,只一级标题展示图标。
|
||||
- **icon**: 菜单的图标,只一级标题展示图标。
|
||||
|
||||
- 图标使用[fes-design icon](https://fes-design-4gvn317r3b6bfe17-1254145788.ap-shanghai.app.tcloudbase.com/zh/components/icon.html),编译时配置使用组件名称,我们会自动引入组件。
|
||||
- 图标使用[fes-design icon](https://fes-design-4gvn317r3b6bfe17-1254145788.ap-shanghai.app.tcloudbase.com/zh/components/icon.html),编译时配置使用组件名称,我们会自动引入组件。
|
||||
|
||||
- 图标使用本地或者远程 svg 图片。
|
||||
- 图标使用本地或者远程 svg 图片。
|
||||
|
||||
```js
|
||||
{
|
||||
@ -292,7 +292,7 @@ export const layout = {
|
||||
}
|
||||
```
|
||||
|
||||
- **children**:子菜单配置。
|
||||
- **children**:子菜单配置。
|
||||
|
||||
:::tip
|
||||
函数类型仅在运行时可用,可以实现动态变更菜单。
|
||||
@ -300,42 +300,42 @@ export const layout = {
|
||||
|
||||
### menuProps
|
||||
|
||||
- **类型**:`Object`
|
||||
- **默认值**:`{}`
|
||||
- **类型**:`Object`
|
||||
- **默认值**:`{}`
|
||||
|
||||
- **详情**:菜单的配置:
|
||||
- **详情**:菜单的配置:
|
||||
|
||||
- **defaultExpandAll**:是否默认展开全部菜单。
|
||||
- **defaultExpandAll**:是否默认展开全部菜单。
|
||||
|
||||
- **expandedKeys**:配置默认展开的菜单,需要传子项是菜单路径的数组。
|
||||
- **expandedKeys**:配置默认展开的菜单,需要传子项是菜单路径的数组。
|
||||
|
||||
- **accordion**:是否只保持一个子菜单的展开。
|
||||
- **accordion**:是否只保持一个子菜单的展开。
|
||||
|
||||
### sideWidth
|
||||
|
||||
- **类型**:`Number`
|
||||
- **默认值**:`200`
|
||||
- **类型**:`Number`
|
||||
- **默认值**:`200`
|
||||
|
||||
- **详情**:sidebar 的宽度
|
||||
- **详情**:sidebar 的宽度
|
||||
|
||||
### renderCustom
|
||||
|
||||
- **类型**: `()=> VNodes`
|
||||
- **默认值**:`null`
|
||||
- **类型**: `()=> VNodes`
|
||||
- **默认值**:`null`
|
||||
|
||||
- **详情**: 自定义区域内容,仅运行时。
|
||||
- **详情**: 自定义区域内容,仅运行时。
|
||||
|
||||
### unAccessHandler
|
||||
|
||||
- **类型**:`({ to, from, next})=> void`
|
||||
- **默认值**:`null`
|
||||
- **类型**:`({ to, from, next})=> void`
|
||||
- **默认值**:`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)
|
||||
- **详情**:仅运行时,当进入某个路由时,如果路由对应的页面不属于可见资源列表,则会暂停进入,调用 `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)
|
||||
|
||||
比如:
|
||||
|
||||
@ -357,15 +357,15 @@ export const layout = {
|
||||
|
||||
### noFoundHandler
|
||||
|
||||
- **类型**:`({ to, from, next})=> void`
|
||||
- **默认值**:`null`
|
||||
- **类型**:`({ to, from, next})=> void`
|
||||
- **默认值**:`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)
|
||||
- **详情**:仅运行时,当进入某个路由时,如果路由对应的页面不存在,则会调用 `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)
|
||||
|
||||
比如:
|
||||
|
||||
@ -383,7 +383,7 @@ export const layout = {
|
||||
|
||||
## API
|
||||
|
||||
### useTabTitle
|
||||
### useTabTitle(建议使用useLayout)
|
||||
|
||||
类型定义如下:
|
||||
|
||||
@ -404,6 +404,18 @@ titleRef.value = 'changed';
|
||||
</script>
|
||||
```
|
||||
|
||||
### useLayout
|
||||
|
||||
类型定义如下:
|
||||
|
||||
```ts
|
||||
function useLayout(options: { title?: string }): { title: Ref<string>; reloadTab: () => void; closeTab: () => void };
|
||||
```
|
||||
|
||||
- title: 更新当前页签的标题
|
||||
- reloadTab:重载当前页签
|
||||
- closeTab:关闭当前页签
|
||||
|
||||
## 4.x 升级到 5.x
|
||||
|
||||
1. 个性化 layout 配置改为使用传入 navigation
|
||||
|
@ -1,3 +1,2 @@
|
||||
export { default as Page } from './views/page.vue';
|
||||
export { useTabTitle } from './useTitle';
|
||||
export * from './useLayout';
|
||||
|
@ -1,12 +1,31 @@
|
||||
import { createSharedComposable } from '@vueuse/core';
|
||||
import { shallowReactive } from 'vue';
|
||||
import { inject, ref } from 'vue';
|
||||
|
||||
function _useLayout() {
|
||||
const state = shallowReactive({
|
||||
closeTab: () => {},
|
||||
});
|
||||
import { useRoute } from '@@/core/coreExports';
|
||||
|
||||
return state;
|
||||
export const PLUGIN_LAYOUT_TITLE_KEY = Symbol('PLUGIN_LAYOUT_TITLE_KEY');
|
||||
|
||||
export const PLUGIN_LAYOUT_KEY = Symbol('PLUGIN_LAYOUT_KEY');
|
||||
|
||||
export function useTabTitle(title) {
|
||||
const titleMap = inject(PLUGIN_LAYOUT_TITLE_KEY);
|
||||
if (!titleMap) {
|
||||
console.warn('[plugin-layout]: 未正确获取到titleMap');
|
||||
return;
|
||||
}
|
||||
const route = useRoute();
|
||||
const titleRef = ref(title);
|
||||
const path = route.path;
|
||||
|
||||
titleMap.set(path, titleRef);
|
||||
|
||||
return titleRef;
|
||||
}
|
||||
|
||||
export const useLayout = createSharedComposable(_useLayout);
|
||||
export function useLayout(options) {
|
||||
const parent = inject(PLUGIN_LAYOUT_KEY, { reloadTab: () => void 0, closeTab: () => void 0 });
|
||||
const titleRef = useTabTitle(options?.title);
|
||||
return {
|
||||
...parent,
|
||||
title: titleRef,
|
||||
};
|
||||
}
|
||||
|
@ -1,18 +0,0 @@
|
||||
import { reactive, ref } from 'vue';
|
||||
import { useRoute } from '@@/core/coreExports';
|
||||
|
||||
const cache = reactive(new Map());
|
||||
|
||||
export const getTitle = path => cache.get(path);
|
||||
|
||||
export const deleteTitle = patch => cache.delete(patch);
|
||||
|
||||
export function useTabTitle(title) {
|
||||
const route = useRoute();
|
||||
const titleRef = ref(title);
|
||||
const path = route.path;
|
||||
|
||||
cache.set(path, titleRef);
|
||||
|
||||
return titleRef;
|
||||
}
|
@ -27,13 +27,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, ref, unref } from 'vue';
|
||||
import { computed, provide, reactive, ref, unref } from 'vue';
|
||||
import { FDropdown, FTabPane, FTabs } from '@fesjs/fes-design';
|
||||
import { MoreOutlined, ReloadOutlined } from '@fesjs/fes-design/icon';
|
||||
import { plugin, useRoute, useRouter } from '@@/core/coreExports';
|
||||
import { transTitle } from '../helpers/pluginLocale';
|
||||
import { deleteTitle, getTitle } from '../useTitle';
|
||||
import { useLayout } from '../useLayout';
|
||||
import { PLUGIN_LAYOUT_KEY, PLUGIN_LAYOUT_TITLE_KEY } from '../useLayout';
|
||||
import Page from './page.vue';
|
||||
|
||||
let i = 0;
|
||||
@ -54,7 +53,14 @@ export default {
|
||||
const pageRef = ref();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const layoutState = useLayout();
|
||||
|
||||
const titleCache = reactive(new Map());
|
||||
|
||||
provide(PLUGIN_LAYOUT_TITLE_KEY, titleCache);
|
||||
|
||||
const getTitle = path => titleCache.get(path);
|
||||
|
||||
const deleteTitle = patch => titleCache.delete(patch);
|
||||
|
||||
const createPage = (_route) => {
|
||||
const computedTitle = computed(() => {
|
||||
@ -146,7 +152,6 @@ export default {
|
||||
pageRef.value.removeKeepAlive(selectedPage.name);
|
||||
deleteTitle(selectedPage.path);
|
||||
};
|
||||
layoutState.closeTab = handleCloseTab;
|
||||
|
||||
const reloadPage = (path) => {
|
||||
const selectedPage = findPage(path || unref(route.path));
|
||||
@ -179,6 +184,11 @@ export default {
|
||||
}
|
||||
};
|
||||
|
||||
provide(PLUGIN_LAYOUT_KEY, {
|
||||
closeTab: handleCloseTab,
|
||||
reloadTab: reloadPage,
|
||||
});
|
||||
|
||||
return {
|
||||
pageRef,
|
||||
route,
|
||||
|
2
packages/fes-plugin-layout/types.d.ts
vendored
2
packages/fes-plugin-layout/types.d.ts
vendored
@ -28,6 +28,8 @@ export const Page: Component;
|
||||
|
||||
export function useTabTitle(title: string | Ref<string>): void;
|
||||
|
||||
export function useLayout(options: { title?: string }): { title: Ref<string>; reloadTab: () => void; closeTab: () => void };
|
||||
|
||||
interface LayoutRuntimeConfig {
|
||||
footer?: string;
|
||||
theme?: 'dark' | 'light';
|
||||
|
@ -1,19 +1,23 @@
|
||||
<template>
|
||||
<div class="page">menuTest: {{ route.params }} <input style="border: 1px solid red" /></div>
|
||||
<div class="page">
|
||||
menuTest: {{ route.params }} <input style="border: 1px solid red">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<config>
|
||||
{
|
||||
"title": "menuTest-详情"
|
||||
}
|
||||
</config>
|
||||
|
||||
<script>
|
||||
import { useRoute, useTabTitle } from '@fesjs/fes';
|
||||
import { useLayout, useRoute } from '@fesjs/fes';
|
||||
|
||||
export default {
|
||||
components: {},
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const title = useTabTitle(`详情-${route.params?.id}`);
|
||||
const { title } = useLayout({ title: `详情-${route.params?.id}` });
|
||||
|
||||
setTimeout(() => {
|
||||
title.value = `详情-${route.params?.id}-changed`;
|
||||
|
Loading…
x
Reference in New Issue
Block a user