mirror of
https://github.com/WeBankFinTech/fes.js.git
synced 2025-06-30 02:35:08 +08:00
refactor(plugin-layout): 优化runtimeConfig,重新实现运行时menus配置方式,修复多页时setup执行两次的bug
This commit is contained in:
parent
b73c1f9027
commit
9b03236654
@ -32,9 +32,6 @@ export default (api) => {
|
||||
|
||||
const HAS_LOCALE = api.hasPlugins(['@fesjs/plugin-locale']);
|
||||
|
||||
// 路由信息
|
||||
const routes = await api.getRoutes();
|
||||
|
||||
// .fes配置
|
||||
const userConfig = {
|
||||
title: name,
|
||||
@ -42,11 +39,7 @@ export default (api) => {
|
||||
...(api.config.layout || {})
|
||||
};
|
||||
|
||||
// 把路由的meta合并到menu配置中
|
||||
const menus = helper.fillMenuByRoute(userConfig.menus, routes);
|
||||
delete userConfig.menus;
|
||||
|
||||
const iconNames = helper.getIconNamesFromMenu(menus);
|
||||
const iconNames = helper.getIconNamesFromMenu(userConfig.menus);
|
||||
|
||||
const iconsString = iconNames.map(
|
||||
iconName => `import { ${iconName} } from '@fesjs/fes-design/icon'`
|
||||
@ -66,7 +59,6 @@ export default (api) => {
|
||||
readFileSync(join(__dirname, 'runtime/index.tpl'), 'utf-8'),
|
||||
{
|
||||
REPLACE_USER_CONFIG: JSON.stringify(userConfig),
|
||||
MENUS: JSON.stringify(menus),
|
||||
HAS_LOCALE
|
||||
}
|
||||
)
|
||||
|
@ -1,58 +1,3 @@
|
||||
const getMetaByName = (config, name) => {
|
||||
let res = {};
|
||||
if (Array.isArray(config)) {
|
||||
for (let i = 0; i < config.length; i++) {
|
||||
const item = config[i];
|
||||
if (item.meta && item.meta.name === name) {
|
||||
res = item.meta;
|
||||
res.path = item.path;
|
||||
break;
|
||||
}
|
||||
if (item.children && item.children.length > 0) {
|
||||
res = getMetaByName(item.children, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
export const fillMenuByRoute = (menuConfig, routeConfig, dep = 0) => {
|
||||
dep += 1;
|
||||
if (dep > 3) {
|
||||
console.warn('[plugin-layout]: 菜单层级最好不要超出三层!');
|
||||
}
|
||||
const arr = [];
|
||||
if (Array.isArray(menuConfig) && Array.isArray(routeConfig)) {
|
||||
menuConfig.forEach((menu) => {
|
||||
const pageConfig = {};
|
||||
if (menu.name) {
|
||||
Object.assign(
|
||||
pageConfig,
|
||||
getMetaByName(routeConfig, menu.name)
|
||||
);
|
||||
}
|
||||
// menu的配置优先级高,当menu存在配置时,忽略页面的配置
|
||||
Object.keys(pageConfig).forEach((prop) => {
|
||||
if (
|
||||
menu[prop] === undefined
|
||||
|| menu[prop] === null
|
||||
|| menu[prop] === ''
|
||||
) {
|
||||
menu[prop] = pageConfig[prop];
|
||||
}
|
||||
});
|
||||
if (menu.children && menu.children.length > 0) {
|
||||
menu.children = fillMenuByRoute(
|
||||
menu.children,
|
||||
routeConfig,
|
||||
dep
|
||||
);
|
||||
}
|
||||
arr.push(menu);
|
||||
});
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
export function getIconNamesFromMenu(data) {
|
||||
if (!Array.isArray(data)) {
|
||||
|
57
packages/fes-plugin-layout/src/runtime/helpers/fillMenu.js
Normal file
57
packages/fes-plugin-layout/src/runtime/helpers/fillMenu.js
Normal file
@ -0,0 +1,57 @@
|
||||
const getMetaByName = (config, name) => {
|
||||
let res = {};
|
||||
if (Array.isArray(config)) {
|
||||
for (let i = 0; i < config.length; i++) {
|
||||
const item = config[i];
|
||||
if (item.meta && item.meta.name === name) {
|
||||
res = item.meta;
|
||||
res.path = item.path;
|
||||
break;
|
||||
}
|
||||
if (item.children && item.children.length > 0) {
|
||||
res = getMetaByName(item.children, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
const fillMenuByRoute = (menuConfig, routeConfig, dep = 0) => {
|
||||
dep += 1;
|
||||
if (dep > 3) {
|
||||
console.warn('[plugin-layout]: 菜单层级最好不要超出三层!');
|
||||
}
|
||||
const arr = [];
|
||||
if (Array.isArray(menuConfig) && Array.isArray(routeConfig)) {
|
||||
menuConfig.forEach((menu) => {
|
||||
const pageConfig = {};
|
||||
if (menu.name) {
|
||||
Object.assign(
|
||||
pageConfig,
|
||||
getMetaByName(routeConfig, menu.name)
|
||||
);
|
||||
}
|
||||
// menu的配置优先级高,当menu存在配置时,忽略页面的配置
|
||||
Object.keys(pageConfig).forEach((prop) => {
|
||||
if (
|
||||
menu[prop] === undefined
|
||||
|| menu[prop] === null
|
||||
|| menu[prop] === ''
|
||||
) {
|
||||
menu[prop] = pageConfig[prop];
|
||||
}
|
||||
});
|
||||
if (menu.children && menu.children.length > 0) {
|
||||
menu.children = fillMenuByRoute(
|
||||
menu.children,
|
||||
routeConfig,
|
||||
dep
|
||||
);
|
||||
}
|
||||
arr.push(menu);
|
||||
});
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
export default fillMenuByRoute;
|
@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
import { plugin, ApplyPluginsType } from '@@/core/coreExports';
|
||||
import { inject } from 'vue';
|
||||
|
||||
let runtimeConfig;
|
||||
|
||||
export default () => {
|
||||
if (!runtimeConfig) {
|
||||
runtimeConfig = plugin.applyPlugins({
|
||||
key: 'layout',
|
||||
type: ApplyPluginsType.modify,
|
||||
initialValue: {
|
||||
initialState: inject('initialState'),
|
||||
sidebar: true,
|
||||
header: true,
|
||||
logo: true
|
||||
}
|
||||
});
|
||||
}
|
||||
return runtimeConfig;
|
||||
};
|
@ -1,49 +1,25 @@
|
||||
import { ref, defineComponent, inject } from "vue";
|
||||
import { plugin, ApplyPluginsType } from "@@/core/coreExports";
|
||||
import BaseLayout from "./views/BaseLayout.vue";
|
||||
import { ref, defineComponent, computed } from 'vue';
|
||||
import { plugin, ApplyPluginsType, getRoutes } from '@@/core/coreExports';
|
||||
import BaseLayout from './views/BaseLayout.vue';
|
||||
import getRuntimeConfig from './helpers/getRuntimeConfig';
|
||||
import fillMenu from './helpers/fillMenu';
|
||||
|
||||
const Layout = defineComponent({
|
||||
name: 'Layout',
|
||||
setup(){
|
||||
setup() {
|
||||
const userConfig = {{{REPLACE_USER_CONFIG}}};
|
||||
const menusRef = ref({{{MENUS}}})
|
||||
const runtimeConfig = plugin.applyPlugins({
|
||||
key: "layout",
|
||||
type: ApplyPluginsType.modify,
|
||||
initialValue: {
|
||||
initialState: inject('initialState')
|
||||
},
|
||||
});
|
||||
const runtimeConfig = getRuntimeConfig();
|
||||
let menusRef = ref(userConfig.menus);
|
||||
// 如果运行时配置了menus,则需要处理
|
||||
if (
|
||||
runtimeConfig.menus &&
|
||||
typeof runtimeConfig.menus.request === 'function'
|
||||
) {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const newMenusData = await runtimeConfig.menus.request(
|
||||
menusRef.value
|
||||
);
|
||||
return newMenusData;
|
||||
} catch (e) {
|
||||
console.error('[plugin-layout]: runtime config menus.request run error:', e);
|
||||
}
|
||||
return menusRef.value;
|
||||
};
|
||||
if (runtimeConfig.menus.watch) {
|
||||
watch(
|
||||
() => runtimeConfig.menus.watch,
|
||||
async () => {
|
||||
const newMenusData = await fetchData();
|
||||
menusRef.value = newMenusData;
|
||||
}
|
||||
);
|
||||
}
|
||||
fetchData().then((newMenusData) => {
|
||||
menusRef.value = newMenusData;
|
||||
});
|
||||
if (runtimeConfig.menus && typeof runtimeConfig.menus === 'function') {
|
||||
menusRef = ref(runtimeConfig.menus(userConfig.menus));
|
||||
}
|
||||
const localeShared = plugin.getShared("locale");
|
||||
// 把路由的meta合并到menu配置中
|
||||
const filledMenuRef = computed(() => {
|
||||
return fillMenu(menusRef.value, getRoutes());
|
||||
});
|
||||
|
||||
const localeShared = plugin.getShared('locale');
|
||||
return () => {
|
||||
const slots = {
|
||||
customHeader: () => {
|
||||
@ -56,14 +32,23 @@ const Layout = defineComponent({
|
||||
},
|
||||
locale: () => {
|
||||
if (localeShared) {
|
||||
return <localeShared.SelectLang></localeShared.SelectLang>;
|
||||
return (
|
||||
<localeShared.SelectLang></localeShared.SelectLang>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}
|
||||
};
|
||||
return <BaseLayout locale={ localeShared ? true : false } {...userConfig} menus={menusRef.value} v-slots={slots}></BaseLayout>;
|
||||
return (
|
||||
<BaseLayout
|
||||
locale={localeShared ? true : false}
|
||||
{...userConfig}
|
||||
menus={filledMenuRef.value}
|
||||
v-slots={slots}
|
||||
></BaseLayout>
|
||||
);
|
||||
};
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
export default Layout;
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { plugin, ApplyPluginsType } from '@@/core/coreExports';
|
||||
// eslint-disable-next-line import/extensions
|
||||
import { access as accessApi } from '../plugin-access/core';
|
||||
import Exception404 from './views/404';
|
||||
import Exception403 from './views/403';
|
||||
import getRuntimeConfig from './helpers/getRuntimeConfig';
|
||||
|
||||
if (!accessApi) {
|
||||
throw new Error(
|
||||
@ -30,11 +30,7 @@ export const access = memo => ({
|
||||
unAccessHandler({
|
||||
router, to, from, next
|
||||
}) {
|
||||
const runtimeConfig = plugin.applyPlugins({
|
||||
key: 'layout',
|
||||
type: ApplyPluginsType.modify,
|
||||
initialValue: {}
|
||||
});
|
||||
const runtimeConfig = getRuntimeConfig();
|
||||
if (runtimeConfig.unAccessHandler && typeof runtimeConfig.unAccessHandler === 'function') {
|
||||
return runtimeConfig.unAccessHandler({
|
||||
router, to, from, next
|
||||
@ -50,11 +46,7 @@ export const access = memo => ({
|
||||
noFoundHandler({
|
||||
router, to, from, next
|
||||
}) {
|
||||
const runtimeConfig = plugin.applyPlugins({
|
||||
key: 'layout',
|
||||
type: ApplyPluginsType.modify,
|
||||
initialValue: {}
|
||||
});
|
||||
const runtimeConfig = getRuntimeConfig();
|
||||
if (runtimeConfig.noFoundHandler && typeof runtimeConfig.noFoundHandler === 'function') {
|
||||
return runtimeConfig.noFoundHandler({
|
||||
router, to, from, next
|
||||
|
@ -146,13 +146,14 @@
|
||||
|
||||
<script>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useRoute, plugin, ApplyPluginsType } from '@@/core/coreExports';
|
||||
import { useRoute } from '@@/core/coreExports';
|
||||
import {
|
||||
FLayout, FAside, FMain, FFooter, FHeader
|
||||
} from '@fesjs/fes-design';
|
||||
import Menu from './Menu';
|
||||
import MultiTabProvider from './MultiTabProvider';
|
||||
import defaultLogo from '../assets/logo.png';
|
||||
import getRuntimeConfig from '../helpers/getRuntimeConfig';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -221,15 +222,7 @@ export default {
|
||||
|
||||
const collapsedRef = ref(false);
|
||||
const route = useRoute();
|
||||
const runtimeConfig = plugin.applyPlugins({
|
||||
key: 'layout',
|
||||
type: ApplyPluginsType.modify,
|
||||
initialValue: {
|
||||
sidebar: true,
|
||||
header: true,
|
||||
logo: true
|
||||
}
|
||||
});
|
||||
const runtimeConfig = getRuntimeConfig();
|
||||
const routeLayout = computed(() => {
|
||||
let config;
|
||||
// meta 中 layout 默认为 true
|
||||
|
@ -14,7 +14,6 @@ export default {
|
||||
setup(props) {
|
||||
const AIconComponent = ref(null);
|
||||
const AText = ref(null);
|
||||
const AVNode = ref(null);
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (typeof props.icon === 'string') {
|
||||
@ -29,16 +28,12 @@ export default {
|
||||
} else {
|
||||
AIconComponent.value = Icons[props.icon];
|
||||
}
|
||||
} else if (isVNode(props.icon)) {
|
||||
AVNode.value = props.icon;
|
||||
} else {
|
||||
console.log(props.icon);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (AVNode.value) {
|
||||
return AVNode.value;
|
||||
if (isVNode(props.icon)) {
|
||||
return props.icon;
|
||||
}
|
||||
if (AIconComponent.value) {
|
||||
return <AIconComponent.value />;
|
||||
|
@ -38,17 +38,12 @@
|
||||
</template>
|
||||
<router-view v-else v-slot="{ Component, route }">
|
||||
<keep-alive :include="keepAlivePages">
|
||||
<component
|
||||
:is="getComponent(Component, route)"
|
||||
:key="getPageKey(route)"
|
||||
/>
|
||||
<component :is="getComponent(Component, route)" />
|
||||
</keep-alive>
|
||||
</router-view>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
computed, onMounted, unref, ref
|
||||
} from 'vue';
|
||||
import { computed, unref, ref } from 'vue';
|
||||
import { FTabs, FTabPane, FDropdown } from '@fesjs/fes-design';
|
||||
import { ReloadOutlined, MoreOutlined } from '@fesjs/fes-design/icon';
|
||||
import { useRouter, useRoute } from '@@/core/coreExports';
|
||||
@ -68,20 +63,6 @@ export default {
|
||||
multiTabs: Boolean
|
||||
},
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const pageList = ref([]);
|
||||
const actions = [
|
||||
{
|
||||
value: 'closeOtherPage',
|
||||
label: '关闭其他'
|
||||
},
|
||||
{
|
||||
value: 'reloadPage',
|
||||
label: '刷新当前页'
|
||||
}
|
||||
];
|
||||
|
||||
const createPage = (_route) => {
|
||||
const title = _route.meta.title;
|
||||
return {
|
||||
@ -93,11 +74,21 @@ export default {
|
||||
};
|
||||
};
|
||||
|
||||
const findPage = path => pageList.value.find(item => unref(item.path) === unref(path));
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const pageList = ref([createPage(route)]);
|
||||
const actions = [
|
||||
{
|
||||
value: 'closeOtherPage',
|
||||
label: '关闭其他'
|
||||
},
|
||||
{
|
||||
value: 'reloadPage',
|
||||
label: '刷新当前页'
|
||||
}
|
||||
];
|
||||
|
||||
onMounted(() => {
|
||||
pageList.value = [createPage(route)];
|
||||
});
|
||||
const findPage = path => pageList.value.find(item => unref(item.path) === unref(path));
|
||||
|
||||
router.beforeEach((to) => {
|
||||
if (!findPage(to.path)) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { createRouter as createVueRouter, {{{ CREATE_HISTORY }}}, ApplyPluginsType } from '{{{ runtimePath }}}';
|
||||
import { plugin } from '@@/core/coreExports';
|
||||
import { plugin } from '@@/core/plugin';
|
||||
|
||||
export function getRoutes() {
|
||||
const routes = {{{ routes }}};
|
||||
|
@ -4,6 +4,7 @@ import { access as accessApi, pinia } from '@fesjs/fes';
|
||||
import PageLoading from '@/components/PageLoading';
|
||||
import UserCenter from '@/components/UserCenter';
|
||||
import { useStore } from '@/store/main';
|
||||
import { ref } from 'vue';
|
||||
|
||||
export const beforeRender = {
|
||||
loading: <PageLoading />,
|
||||
@ -24,17 +25,16 @@ export const beforeRender = {
|
||||
}
|
||||
};
|
||||
|
||||
export const layout = (initialValue) => {
|
||||
console.log('layout runtime');
|
||||
return {
|
||||
...initialValue,
|
||||
customHeader: <UserCenter />,
|
||||
menus: {
|
||||
request: async (defaultMenuData) => {
|
||||
console.log(defaultMenuData);
|
||||
console.log(initialValue.initialState);
|
||||
return defaultMenuData;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
export const layout = initialValue => ({
|
||||
...initialValue,
|
||||
customHeader: <UserCenter />,
|
||||
menus: (defaultMenuData) => {
|
||||
const menusRef = ref(defaultMenuData);
|
||||
// watch(() => initialValue.initialState.userName, () => {
|
||||
// menusRef.value = [{
|
||||
// name: 'store'
|
||||
// }];
|
||||
// });
|
||||
return menusRef;
|
||||
}
|
||||
});
|
||||
|
@ -24,6 +24,7 @@ export default {
|
||||
MonacoEditor
|
||||
},
|
||||
setup() {
|
||||
console.log('editor.vue');
|
||||
const editorRef = ref();
|
||||
const json = ref('');
|
||||
const language = ref('json');
|
||||
|
@ -7,12 +7,18 @@
|
||||
|
||||
<script>
|
||||
import { FButton } from '@fesjs/fes-design';
|
||||
import { useModel } from '@fesjs/fes';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FButton
|
||||
},
|
||||
setup() {
|
||||
const initialState = useModel('@@initialState');
|
||||
setTimeout(() => {
|
||||
initialState.userName = '1';
|
||||
}, 1000);
|
||||
console.log('index.vue');
|
||||
return {
|
||||
};
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import { MUTATION_TYPES, GETTER_TYPES, ACTION_TYPES } from '@fesjs/fes';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
console.log('store.vue');
|
||||
const store = useStore();
|
||||
console.log('store==>', store);
|
||||
const disabled = ref(false);
|
||||
|
Loading…
x
Reference in New Issue
Block a user