mirror of
https://github.com/WeBankFinTech/fes.js.git
synced 2025-04-05 19:41:57 +08:00
* feat(plugin-layout): layout支持运行时配置menus (#113)
* feat(plugin-layout): 支持运行时配置menus * refactor(plugin-model): 去掉provide * refactor(plugin-layout): 优化runtimeConfig,重新实现运行时menus配置方式,修复多页时setup执行两次的bug * feat(plugin-layout): 菜单支持配置默认展开等 * refactor: 优化
This commit is contained in:
parent
b5184a4e7d
commit
94c4bb6e4f
@ -20,7 +20,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@fesjs/fes": "^2.0.0",
|
||||
"@fesjs/plugin-layout": "^2.0.0"
|
||||
"@fesjs/plugin-layout": "^4.0.0"
|
||||
},
|
||||
}
|
||||
```
|
||||
@ -115,7 +115,12 @@ export default {
|
||||
name: 'store'
|
||||
}, {
|
||||
name: 'simpleList'
|
||||
}]
|
||||
}],
|
||||
menuConfig: {
|
||||
defaultExpandAll: false,
|
||||
expandedKeys: [],
|
||||
accordion: false
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
@ -219,7 +224,19 @@ export default {
|
||||
```
|
||||
|
||||
- **children**:子菜单配置。
|
||||
|
||||
#### menusConfig
|
||||
- **类型**:`Object`
|
||||
|
||||
- **默认值**:`{}`
|
||||
|
||||
- **详情**:菜单的配置:
|
||||
|
||||
- **defaultExpandAll**:是否默认展开全部菜单。
|
||||
|
||||
- **expandedKeys**:配置默认展开的菜单,需要传子项是菜单路径的数组。
|
||||
|
||||
- **accordion**:是否只保持一个子菜单的展开。
|
||||
|
||||
### 运行时配置
|
||||
在 `app.js` 中配置:
|
||||
@ -231,6 +248,38 @@ export const layout = {
|
||||
|
||||
```
|
||||
|
||||
#### 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,需要传组件本身,而不是组件的名称。
|
||||
:::tip
|
||||
|
||||
|
||||
#### header
|
||||
- **类型**:`String`
|
||||
|
||||
|
@ -39,14 +39,9 @@ export default (api) => {
|
||||
...(api.config.layout || {})
|
||||
};
|
||||
|
||||
// 路由信息
|
||||
const routes = await api.getRoutes();
|
||||
// 把路由的meta合并到menu配置中
|
||||
userConfig.menus = helper.fillMenuByRoute(userConfig.menus, routes);
|
||||
const iconNames = helper.getIconNamesFromMenu(userConfig.menus);
|
||||
|
||||
const icons = helper.getIconsFromMenu(userConfig.menus);
|
||||
|
||||
const iconsString = icons.map(
|
||||
const iconsString = iconNames.map(
|
||||
iconName => `import { ${iconName} } from '@fesjs/fes-design/icon'`
|
||||
);
|
||||
api.writeTmpFile({
|
||||
@ -54,7 +49,7 @@ export default (api) => {
|
||||
content: `
|
||||
${iconsString.join(';\n')}
|
||||
export default {
|
||||
${icons.join(',\n')}
|
||||
${iconNames.join(',\n')}
|
||||
}`
|
||||
});
|
||||
|
||||
|
@ -1,61 +1,5 @@
|
||||
|
||||
const matchName = (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 = matchName(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, matchName(routeConfig, menu.name));
|
||||
}
|
||||
// menu的配置优先级高,当menu存在配置时,忽略页面的配置
|
||||
Object.keys(pageConfig).forEach((prop) => {
|
||||
if (menu[prop] === undefined || menu[prop] === null || menu[prop] === '') {
|
||||
menu[prop] = pageConfig[prop];
|
||||
}
|
||||
});
|
||||
// 处理icon
|
||||
if (menu.icon) {
|
||||
const icon = menu.icon;
|
||||
const urlReg = /^((https?|ftp|file):\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/;
|
||||
if (typeof icon === 'string' && !((urlReg.test(icon) || icon.includes('.svg')))) {
|
||||
menu.icon = {
|
||||
type: 'icon',
|
||||
name: icon
|
||||
};
|
||||
}
|
||||
}
|
||||
if (menu.children && menu.children.length > 0) {
|
||||
menu.children = fillMenuByRoute(menu.children, routeConfig, dep);
|
||||
}
|
||||
arr.push(menu);
|
||||
});
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
export function getIconsFromMenu(data) {
|
||||
export function getIconNamesFromMenu(data) {
|
||||
if (!Array.isArray(data)) {
|
||||
return [];
|
||||
}
|
||||
@ -63,12 +7,19 @@ export function getIconsFromMenu(data) {
|
||||
data.forEach((item = { path: '/' }) => {
|
||||
if (item.icon) {
|
||||
const { icon } = item;
|
||||
if (icon.type === 'icon') {
|
||||
icons.push(icon.name);
|
||||
// 处理icon
|
||||
if (icon) {
|
||||
const urlReg = /^((https?|ftp|file):\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/;
|
||||
if (
|
||||
typeof icon === 'string'
|
||||
&& !(urlReg.test(icon) || icon.includes('.svg'))
|
||||
) {
|
||||
icons.push(icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item.children) {
|
||||
icons = icons.concat(getIconsFromMenu(item.children));
|
||||
icons = icons.concat(getIconNamesFromMenu(item.children));
|
||||
}
|
||||
});
|
||||
|
||||
|
60
packages/fes-plugin-layout/src/runtime/helpers/fillMenu.js
Normal file
60
packages/fes-plugin-layout/src/runtime/helpers/fillMenu.js
Normal file
@ -0,0 +1,60 @@
|
||||
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);
|
||||
if (res.path) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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,17 +1,25 @@
|
||||
import { reactive, defineComponent } 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(){
|
||||
const userConfig = reactive({{{REPLACE_USER_CONFIG}}});
|
||||
const runtimeConfig = plugin.applyPlugins({
|
||||
key: "layout",
|
||||
type: ApplyPluginsType.modify,
|
||||
initialValue: {},
|
||||
setup() {
|
||||
const userConfig = {{{REPLACE_USER_CONFIG}}};
|
||||
const runtimeConfig = getRuntimeConfig();
|
||||
let menusRef = ref(userConfig.menus);
|
||||
// 如果运行时配置了menus,则需要处理
|
||||
if (runtimeConfig.menus && typeof runtimeConfig.menus === 'function') {
|
||||
menusRef = ref(runtimeConfig.menus(userConfig.menus));
|
||||
}
|
||||
// 把路由的meta合并到menu配置中
|
||||
const filledMenuRef = computed(() => {
|
||||
return fillMenu(menusRef.value, getRoutes());
|
||||
});
|
||||
const localeShared = plugin.getShared("locale");
|
||||
|
||||
const localeShared = plugin.getShared('locale');
|
||||
return () => {
|
||||
const slots = {
|
||||
customHeader: () => {
|
||||
@ -24,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} 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
|
||||
|
@ -20,6 +20,9 @@
|
||||
:collapsed="collapsedRef"
|
||||
mode="vertical"
|
||||
:inverted="theme === 'dark'"
|
||||
:expandedKeys="menuConfig?.expandedKeys"
|
||||
:defaultExpandAll="menuConfig?.defaultExpandAll"
|
||||
:accordion="menuConfig?.accordion"
|
||||
/>
|
||||
</f-aside>
|
||||
<f-layout
|
||||
@ -70,6 +73,9 @@
|
||||
:menus="menus"
|
||||
mode="horizontal"
|
||||
:inverted="theme === 'dark'"
|
||||
:expandedKeys="menuConfig?.expandedKeys"
|
||||
:defaultExpandAll="menuConfig?.defaultExpandAll"
|
||||
:accordion="menuConfig?.accordion"
|
||||
/>
|
||||
<div class="layout-header-custom">
|
||||
<slot name="customHeader"></slot>
|
||||
@ -124,6 +130,9 @@
|
||||
:menus="menus"
|
||||
:collapsed="collapsedRef"
|
||||
mode="vertical"
|
||||
:expandedKeys="menuConfig?.expandedKeys"
|
||||
:defaultExpandAll="menuConfig?.defaultExpandAll"
|
||||
:accordion="menuConfig?.accordion"
|
||||
/>
|
||||
</f-aside>
|
||||
<f-layout
|
||||
@ -146,13 +155,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: {
|
||||
@ -207,7 +217,10 @@ export default {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
footer: String
|
||||
footer: String,
|
||||
menuConfig: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const headerRef = ref();
|
||||
@ -221,15 +234,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
|
||||
|
@ -1,34 +1,42 @@
|
||||
<script>
|
||||
import { ref, onBeforeMount } from 'vue';
|
||||
import { ref, onBeforeMount, isVNode } from 'vue';
|
||||
// eslint-disable-next-line import/extensions
|
||||
import Icons from '../icons';
|
||||
import { validateContent } from '../helpers/svg';
|
||||
|
||||
const urlReg = /^((https?|ftp|file):\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/;
|
||||
const isUrlResource = name => urlReg.test(name) || name.includes('.svg');
|
||||
|
||||
export default {
|
||||
props: {
|
||||
icon: [String, Object]
|
||||
},
|
||||
setup(props) {
|
||||
const AIcon = ref(null);
|
||||
const AIconComponent = ref(null);
|
||||
const AText = ref(null);
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (props.icon && props.icon.type === 'icon') {
|
||||
AIcon.value = Icons[props.icon.name];
|
||||
} else {
|
||||
fetch(props.icon).then((rsp) => {
|
||||
if (rsp.ok) {
|
||||
return rsp.text().then((svgContent) => {
|
||||
AText.value = validateContent(svgContent);
|
||||
});
|
||||
}
|
||||
});
|
||||
if (typeof props.icon === 'string') {
|
||||
if (isUrlResource(props.icon)) {
|
||||
fetch(props.icon).then((rsp) => {
|
||||
if (rsp.ok) {
|
||||
return rsp.text().then((svgContent) => {
|
||||
AText.value = validateContent(svgContent);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
AIconComponent.value = Icons[props.icon];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (AIcon.value) {
|
||||
return <AIcon.value />;
|
||||
if (isVNode(props.icon)) {
|
||||
return props.icon;
|
||||
}
|
||||
if (AIconComponent.value) {
|
||||
return <AIconComponent.value />;
|
||||
}
|
||||
if (AText.value) {
|
||||
return (
|
||||
|
@ -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,9 +1,6 @@
|
||||
|
||||
import { readFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { lodash, winPath } from '@fesjs/utils';
|
||||
import { getModels } from './utils/getModels';
|
||||
import { getTmpFile } from './utils/getTmpFile';
|
||||
|
||||
const namespace = 'plugin-model';
|
||||
|
||||
@ -13,6 +10,10 @@ export default (api) => {
|
||||
utils: { Mustache }
|
||||
} = api;
|
||||
|
||||
const { lodash, winPath } = require('@fesjs/utils');
|
||||
const { getModels } = require('./utils/getModels');
|
||||
const { getTmpFile } = require('./utils/getTmpFile');
|
||||
|
||||
function getModelDir() {
|
||||
return api.config.singular ? 'model' : 'models';
|
||||
}
|
||||
@ -25,22 +26,16 @@ export default (api) => {
|
||||
const srcModelsPath = getModelsPath();
|
||||
return lodash.uniq([
|
||||
...getModels(srcModelsPath)
|
||||
// ...getModels(
|
||||
// paths.absPagesPath,
|
||||
// `**/${getModelDir()}/**/*.{js,jsx}`
|
||||
// ),
|
||||
// ...getModels(paths.absPagesPath, '**/*.model.{js,jsx}')
|
||||
]);
|
||||
}
|
||||
|
||||
const absCoreFilePath = join(namespace, 'core.js');
|
||||
const absRuntimeFilePath = join(namespace, 'runtime.js');
|
||||
const absInitlaStateFilePath = join(namespace, 'models/initialState.js');
|
||||
const absInitialStateFilePath = join(namespace, 'models/initialState.js');
|
||||
|
||||
api.register({
|
||||
key: 'addExtraModels',
|
||||
fn: () => [{
|
||||
absPath: winPath(join(paths.absTmpPath, absInitlaStateFilePath)),
|
||||
absPath: winPath(join(paths.absTmpPath, absInitialStateFilePath)),
|
||||
namespace: '@@initialState'
|
||||
}]
|
||||
});
|
||||
@ -63,16 +58,10 @@ export default (api) => {
|
||||
})
|
||||
});
|
||||
|
||||
api.writeTmpFile({
|
||||
path: absRuntimeFilePath,
|
||||
content: Mustache.render(readFileSync(join(__dirname, 'runtime/runtime.tpl'), 'utf-8'), {
|
||||
})
|
||||
});
|
||||
|
||||
api.writeTmpFile({
|
||||
path: absInitlaStateFilePath,
|
||||
content: Mustache.render(readFileSync(join(__dirname, 'runtime/models/initialState.tpl'), 'utf-8'), {
|
||||
})
|
||||
api.copyTmpFiles({
|
||||
namespace,
|
||||
path: join(__dirname, 'runtime'),
|
||||
ignore: ['.tpl']
|
||||
});
|
||||
});
|
||||
|
||||
@ -82,6 +71,4 @@ export default (api) => {
|
||||
source: absCoreFilePath
|
||||
}
|
||||
]);
|
||||
|
||||
api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`);
|
||||
};
|
||||
|
@ -1,39 +1,29 @@
|
||||
import { inject } from "vue";
|
||||
|
||||
const modelKey = Symbol("plugin-model");
|
||||
|
||||
{{{userImports}}}
|
||||
{{{extraImports}}}
|
||||
|
||||
export const models = {
|
||||
export const models = {
|
||||
{{#extraModels}}
|
||||
{{{extraModels}}},
|
||||
{{/extraModels}}
|
||||
{{#userModels}}
|
||||
{{{userModels}}},
|
||||
{{/userModels}}
|
||||
}
|
||||
const cache = new Map();
|
||||
|
||||
export const install = (app)=>{
|
||||
const useModel = (name) => {
|
||||
const model = models[name];
|
||||
if(model === undefined){
|
||||
throw new Error("[plugin-model]: useModel, name is undefined.");
|
||||
}
|
||||
if (typeof model !== "function") {
|
||||
throw new Error("[plugin-model]: useModel is not a function.");
|
||||
}
|
||||
if(!cache.has(name)){
|
||||
cache.set(name, model())
|
||||
}
|
||||
return cache.get(name)
|
||||
};
|
||||
app.provide(modelKey, useModel);
|
||||
}
|
||||
|
||||
export const useModel = (name) => {
|
||||
return inject(modelKey)(name);
|
||||
};
|
||||
|
||||
|
||||
const cache = new Map();
|
||||
|
||||
export const useModel = (name) => {
|
||||
const modelFunc = models[name];
|
||||
if (modelFunc === undefined) {
|
||||
throw new Error('[plugin-model]: useModel, name is undefined.');
|
||||
}
|
||||
if (typeof modelFunc !== 'function') {
|
||||
throw new Error('[plugin-model]: useModel is not a function.');
|
||||
}
|
||||
if (!cache.has(name)) {
|
||||
cache.set(name, modelFunc());
|
||||
}
|
||||
return cache.get(name);
|
||||
};
|
||||
|
@ -0,0 +1,5 @@
|
||||
import { inject } from 'vue';
|
||||
|
||||
export default function initialStateModel() {
|
||||
return inject('initialState');
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
import { inject, reactive } from "vue";
|
||||
|
||||
export default function initalModel() {
|
||||
return reactive(inject("initialState"));
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
import { install } from "./core";
|
||||
|
||||
export function onAppCreated({ app }) {
|
||||
install(app)
|
||||
}
|
@ -3,13 +3,13 @@ import { getValidFiles } from '.';
|
||||
|
||||
export function getModels(cwd, pattern) {
|
||||
const files = glob
|
||||
.sync(pattern || '**/*.{js,jsx}', {
|
||||
.sync(pattern || '**/*.{js,jsx,ts,tsx}', {
|
||||
cwd
|
||||
})
|
||||
.filter(
|
||||
file => !file.endsWith('.d.ts')
|
||||
&& !file.endsWith('.test.js')
|
||||
&& !file.endsWith('.test.jsx')
|
||||
&& !file.endsWith('.test.js')
|
||||
&& !file.endsWith('.test.jsx')
|
||||
);
|
||||
|
||||
return getValidFiles(files, cwd);
|
||||
|
@ -26,7 +26,8 @@ const renderClient = (opts = {}) => {
|
||||
});
|
||||
|
||||
const app = createApp(rootContainer);
|
||||
app.provide("initialState", initialState);
|
||||
// initialState是响应式的,后期可以更改
|
||||
app.provide("initialState", reactive(initialState));
|
||||
|
||||
plugin.applyPlugins({
|
||||
key: 'onAppCreated',
|
||||
|
@ -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 }}};
|
||||
|
@ -72,7 +72,10 @@ export default {
|
||||
{
|
||||
name: 'pinia'
|
||||
}
|
||||
]
|
||||
],
|
||||
menuConfig: {
|
||||
defaultExpandAll: false
|
||||
}
|
||||
},
|
||||
locale: {
|
||||
legacy: true
|
||||
|
@ -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,6 +25,16 @@ export const beforeRender = {
|
||||
}
|
||||
};
|
||||
|
||||
export const layout = {
|
||||
customHeader: <UserCenter />
|
||||
};
|
||||
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