From 94c4bb6e4fa8bb713be0f9dc395c8edac8a7d6eb Mon Sep 17 00:00:00 2001 From: harrywan <445436867@qq.com> Date: Mon, 11 Apr 2022 19:40:16 +0800 Subject: [PATCH] =?UTF-8?q?*=20feat(plugin-layout):=20layout=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E8=BF=90=E8=A1=8C=E6=97=B6=E9=85=8D=E7=BD=AEmenus=20(?= =?UTF-8?q?#113)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(plugin-layout): 支持运行时配置menus * refactor(plugin-model): 去掉provide * refactor(plugin-layout): 优化runtimeConfig,重新实现运行时menus配置方式,修复多页时setup执行两次的bug * feat(plugin-layout): 菜单支持配置默认展开等 * refactor: 优化 --- docs/reference/plugin/plugins/layout.md | 53 +++++++++++++- packages/fes-plugin-layout/src/index.js | 11 +-- packages/fes-plugin-layout/src/node/helper.js | 71 +++---------------- .../src/runtime/helpers/fillMenu.js | 60 ++++++++++++++++ .../src/runtime/helpers/getRuntimeConfig.js | 22 ++++++ .../fes-plugin-layout/src/runtime/index.tpl | 45 ++++++++---- .../fes-plugin-layout/src/runtime/runtime.js | 14 +--- .../src/runtime/views/BaseLayout.vue | 27 ++++--- .../src/runtime/views/MenuIcon.vue | 36 ++++++---- .../src/runtime/views/MultiTabProvider.vue | 41 +++++------ packages/fes-plugin-model/src/index.js | 33 +++------ .../fes-plugin-model/src/runtime/core.tpl | 42 +++++------ .../src/runtime/models/initialState.js | 5 ++ .../src/runtime/models/initialState.tpl | 5 -- .../fes-plugin-model/src/runtime/runtime.tpl | 5 -- .../fes-plugin-model/src/utils/getModels.js | 6 +- .../src/plugins/generateFiles/fes/fes.tpl | 3 +- .../plugins/misc/route/template/routes.tpl | 2 +- packages/fes-template/.fes.js | 5 +- packages/fes-template/src/app.js | 17 ++++- packages/fes-template/src/pages/editor.vue | 1 + packages/fes-template/src/pages/index.vue | 6 ++ packages/fes-template/src/pages/store.vue | 1 + 23 files changed, 298 insertions(+), 213 deletions(-) create mode 100644 packages/fes-plugin-layout/src/runtime/helpers/fillMenu.js create mode 100644 packages/fes-plugin-layout/src/runtime/helpers/getRuntimeConfig.js create mode 100644 packages/fes-plugin-model/src/runtime/models/initialState.js delete mode 100644 packages/fes-plugin-model/src/runtime/models/initialState.tpl delete mode 100644 packages/fes-plugin-model/src/runtime/runtime.tpl diff --git a/docs/reference/plugin/plugins/layout.md b/docs/reference/plugin/plugins/layout.md index 238031c1..0aa320b6 100644 --- a/docs/reference/plugin/plugins/layout.md +++ b/docs/reference/plugin/plugins/layout.md @@ -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` diff --git a/packages/fes-plugin-layout/src/index.js b/packages/fes-plugin-layout/src/index.js index 14bafc45..a9417292 100644 --- a/packages/fes-plugin-layout/src/index.js +++ b/packages/fes-plugin-layout/src/index.js @@ -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')} }` }); diff --git a/packages/fes-plugin-layout/src/node/helper.js b/packages/fes-plugin-layout/src/node/helper.js index d5b83208..effedb72 100644 --- a/packages/fes-plugin-layout/src/node/helper.js +++ b/packages/fes-plugin-layout/src/node/helper.js @@ -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)); } }); diff --git a/packages/fes-plugin-layout/src/runtime/helpers/fillMenu.js b/packages/fes-plugin-layout/src/runtime/helpers/fillMenu.js new file mode 100644 index 00000000..cbf7da55 --- /dev/null +++ b/packages/fes-plugin-layout/src/runtime/helpers/fillMenu.js @@ -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; diff --git a/packages/fes-plugin-layout/src/runtime/helpers/getRuntimeConfig.js b/packages/fes-plugin-layout/src/runtime/helpers/getRuntimeConfig.js new file mode 100644 index 00000000..c44b2797 --- /dev/null +++ b/packages/fes-plugin-layout/src/runtime/helpers/getRuntimeConfig.js @@ -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; +}; diff --git a/packages/fes-plugin-layout/src/runtime/index.tpl b/packages/fes-plugin-layout/src/runtime/index.tpl index 3ffef152..683b9015 100644 --- a/packages/fes-plugin-layout/src/runtime/index.tpl +++ b/packages/fes-plugin-layout/src/runtime/index.tpl @@ -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; diff --git a/packages/fes-plugin-layout/src/runtime/runtime.js b/packages/fes-plugin-layout/src/runtime/runtime.js index c676984f..4eb3273f 100644 --- a/packages/fes-plugin-layout/src/runtime/runtime.js +++ b/packages/fes-plugin-layout/src/runtime/runtime.js @@ -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 diff --git a/packages/fes-plugin-layout/src/runtime/views/BaseLayout.vue b/packages/fes-plugin-layout/src/runtime/views/BaseLayout.vue index 83065240..f10a353b 100644 --- a/packages/fes-plugin-layout/src/runtime/views/BaseLayout.vue +++ b/packages/fes-plugin-layout/src/runtime/views/BaseLayout.vue @@ -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 diff --git a/packages/fes-plugin-layout/src/runtime/views/MenuIcon.vue b/packages/fes-plugin-layout/src/runtime/views/MenuIcon.vue index ab24e975..8efd8dd8 100644 --- a/packages/fes-plugin-layout/src/runtime/views/MenuIcon.vue +++ b/packages/fes-plugin-layout/src/runtime/views/MenuIcon.vue @@ -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 ( diff --git a/packages/fes-plugin-layout/src/runtime/views/MultiTabProvider.vue b/packages/fes-plugin-layout/src/runtime/views/MultiTabProvider.vue index a7d35863..2dff56e8 100644 --- a/packages/fes-plugin-layout/src/runtime/views/MultiTabProvider.vue +++ b/packages/fes-plugin-layout/src/runtime/views/MultiTabProvider.vue @@ -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)) { diff --git a/packages/fes-plugin-model/src/index.js b/packages/fes-plugin-model/src/index.js index 6c013f5d..d706158b 100644 --- a/packages/fes-plugin-model/src/index.js +++ b/packages/fes-plugin-model/src/index.js @@ -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}`); }; diff --git a/packages/fes-plugin-model/src/runtime/core.tpl b/packages/fes-plugin-model/src/runtime/core.tpl index 5dc0ed61..679d1e03 100644 --- a/packages/fes-plugin-model/src/runtime/core.tpl +++ b/packages/fes-plugin-model/src/runtime/core.tpl @@ -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); +}; diff --git a/packages/fes-plugin-model/src/runtime/models/initialState.js b/packages/fes-plugin-model/src/runtime/models/initialState.js new file mode 100644 index 00000000..da7c6f64 --- /dev/null +++ b/packages/fes-plugin-model/src/runtime/models/initialState.js @@ -0,0 +1,5 @@ +import { inject } from 'vue'; + +export default function initialStateModel() { + return inject('initialState'); +} diff --git a/packages/fes-plugin-model/src/runtime/models/initialState.tpl b/packages/fes-plugin-model/src/runtime/models/initialState.tpl deleted file mode 100644 index 0af1920b..00000000 --- a/packages/fes-plugin-model/src/runtime/models/initialState.tpl +++ /dev/null @@ -1,5 +0,0 @@ -import { inject, reactive } from "vue"; - -export default function initalModel() { - return reactive(inject("initialState")); -} diff --git a/packages/fes-plugin-model/src/runtime/runtime.tpl b/packages/fes-plugin-model/src/runtime/runtime.tpl deleted file mode 100644 index 41c090a7..00000000 --- a/packages/fes-plugin-model/src/runtime/runtime.tpl +++ /dev/null @@ -1,5 +0,0 @@ -import { install } from "./core"; - -export function onAppCreated({ app }) { - install(app) -} diff --git a/packages/fes-plugin-model/src/utils/getModels.js b/packages/fes-plugin-model/src/utils/getModels.js index cd33456f..d8175b93 100644 --- a/packages/fes-plugin-model/src/utils/getModels.js +++ b/packages/fes-plugin-model/src/utils/getModels.js @@ -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); diff --git a/packages/fes-preset-built-in/src/plugins/generateFiles/fes/fes.tpl b/packages/fes-preset-built-in/src/plugins/generateFiles/fes/fes.tpl index a2ecd1d9..0f7f5f5c 100644 --- a/packages/fes-preset-built-in/src/plugins/generateFiles/fes/fes.tpl +++ b/packages/fes-preset-built-in/src/plugins/generateFiles/fes/fes.tpl @@ -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', diff --git a/packages/fes-preset-built-in/src/plugins/misc/route/template/routes.tpl b/packages/fes-preset-built-in/src/plugins/misc/route/template/routes.tpl index 6d2083d4..ff615a7c 100644 --- a/packages/fes-preset-built-in/src/plugins/misc/route/template/routes.tpl +++ b/packages/fes-preset-built-in/src/plugins/misc/route/template/routes.tpl @@ -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 }}}; diff --git a/packages/fes-template/.fes.js b/packages/fes-template/.fes.js index 82c2d81d..43dfcb4b 100644 --- a/packages/fes-template/.fes.js +++ b/packages/fes-template/.fes.js @@ -72,7 +72,10 @@ export default { { name: 'pinia' } - ] + ], + menuConfig: { + defaultExpandAll: false + } }, locale: { legacy: true diff --git a/packages/fes-template/src/app.js b/packages/fes-template/src/app.js index 167601b7..c93adcad 100644 --- a/packages/fes-template/src/app.js +++ b/packages/fes-template/src/app.js @@ -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; + } +}); diff --git a/packages/fes-template/src/pages/editor.vue b/packages/fes-template/src/pages/editor.vue index 6aed5d6b..891e9311 100644 --- a/packages/fes-template/src/pages/editor.vue +++ b/packages/fes-template/src/pages/editor.vue @@ -24,6 +24,7 @@ export default { MonacoEditor }, setup() { + console.log('editor.vue'); const editorRef = ref(); const json = ref(''); const language = ref('json'); diff --git a/packages/fes-template/src/pages/index.vue b/packages/fes-template/src/pages/index.vue index c8fb7f9b..1c392885 100644 --- a/packages/fes-template/src/pages/index.vue +++ b/packages/fes-template/src/pages/index.vue @@ -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 { }; } diff --git a/packages/fes-template/src/pages/store.vue b/packages/fes-template/src/pages/store.vue index 63c9df2b..bcb1a939 100644 --- a/packages/fes-template/src/pages/store.vue +++ b/packages/fes-template/src/pages/store.vue @@ -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);