Plugins für Plugins möglich.

Man kann nun Plugins für Plugins schreiben. Dabei können die Routes vom ursprünglichen Plugin überschrieben werden.
Außerdem wird unterschieden zwischen required und optional (loadPlugins) Plugins.

routes werden nun kombiniert und die MainLinks ebenfalls.
This commit is contained in:
Tim Gröger 2020-10-13 20:17:00 +02:00
parent dddafef3a1
commit c5799967af
18 changed files with 268 additions and 156 deletions

View File

@ -13,13 +13,15 @@ interface EssentialLink {
} }
interface Plugin { interface Plugin {
router: PluginRouteConfig[]; name: string;
store?: Module<never, never>; routes: PluginRouteConfig[] | RouteConfig[];
store?: Module<never, never>[];
mainLink: PluginMainLink; mainLink: PluginMainLink;
requiredModules: string[];
} }
interface PluginMainLink extends PluginChildLink { interface PluginMainLink extends PluginChildLink {
children: PluginChildLink[] | []; children: PluginChildLink[];
} }
interface PluginChildLink { interface PluginChildLink {
@ -29,20 +31,136 @@ interface PluginChildLink {
icon: string; icon: string;
} }
const config = {
// Do not change required Modules !!
requiredModules: ['user'],
// here you can import plugins.
loadModules: ['plugin1', 'user-plugin']
};
export { PluginRouteConfig, Plugin, PluginChildLink, PluginMainLink }; export { PluginRouteConfig, Plugin, PluginChildLink, PluginMainLink };
function combineRoutes(
target: RouteConfig[],
source: RouteConfig[]
): RouteConfig[] {
// iterate first layer e.g. /main, /login etc.
source.forEach((sourceRouteConfig: RouteConfig) => {
const targetRouteConfig: RouteConfig | undefined = target.find(
(routeConfig: RouteConfig) => {
return sourceRouteConfig.path == routeConfig.path;
}
);
if (targetRouteConfig) {
sourceRouteConfig.children?.forEach(
(sourcePluginChildRouteConfig: RouteConfig) => {
const targetPluginRouteConfig:
| RouteConfig
| undefined = targetRouteConfig.children?.find(
(routeConfig: RouteConfig) => {
return sourcePluginChildRouteConfig.path == routeConfig.path;
}
);
if (targetPluginRouteConfig) {
if (targetPluginRouteConfig.children) {
targetPluginRouteConfig.children = Object.assign(
targetPluginRouteConfig.children,
sourcePluginChildRouteConfig.children
);
} else {
targetPluginRouteConfig.children =
sourcePluginChildRouteConfig.children;
}
} else {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
targetRouteConfig.children.push(sourcePluginChildRouteConfig);
}
}
);
} else {
target.push(sourceRouteConfig);
}
});
return target;
}
function combineMainLinks(
target: PluginMainLink[],
source: PluginMainLink
): PluginMainLink[] {
console.log('target', target);
console.log('source', source);
const targetPluginMainLink: PluginMainLink | undefined = target.find(
(targetPluginMainLink: PluginMainLink) => {
console.log(targetPluginMainLink.title, source.title);
return targetPluginMainLink.title == source.title;
}
);
console.log('targetPluginMainLink', targetPluginMainLink);
if (targetPluginMainLink) {
source.children.forEach((sourcePluginChildLink: PluginChildLink) => {
targetPluginMainLink.children.push(sourcePluginChildLink);
});
} else {
console.log('push source', source);
target.push(source);
}
console.log('merged', target);
return target;
}
// "async" is optional; // "async" is optional;
// more info on params: https://quasar.dev/quasar-cli/cli-documentation/boot-files#Anatomy-of-a-boot-file // more info on params: https://quasar.dev/quasar-cli/cli-documentation/boot-files#Anatomy-of-a-boot-file
export default boot(({ Vue, router }) => { export default boot(({ Vue, router, store }) => {
const plugins = require.context('src/plugins', true, /.+\/plugin.ts$/); const pluginsContext = require.context('src/plugins', true, /.+\/plugin.ts$/);
const pluginMainLinks: PluginMainLink[] = []; let pluginMainLinks: PluginMainLink[] = [];
plugins.keys().forEach((fileName: string) => { const plugins: Plugin[] = [];
pluginsContext.keys().forEach((fileName: string) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
router.addRoutes(plugins(fileName).default.router); plugins.push(pluginsContext(fileName).default);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
pluginMainLinks.push(plugins(fileName).default.mainLink);
}); });
let routes: RouteConfig[] = [];
config.requiredModules.forEach(requiredModule => {
const plugin = plugins.find(plugin => {
return plugin.name == requiredModule;
});
if (plugin) {
routes = combineRoutes(routes, plugin.routes);
if (plugin.store) {
plugin.store.forEach(store_module => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
store.registerModule(store_module);
});
}
pluginMainLinks = combineMainLinks(pluginMainLinks, plugin.mainLink);
} else {
console.exception(`Don't find required Plugin ${requiredModule}`);
}
});
config.loadModules.forEach(loadModule => {
const plugin = plugins.find(plugin => {
return plugin.name == loadModule;
});
if (plugin) {
routes = combineRoutes(routes, plugin.routes);
if (plugin.store) {
plugin.store.forEach(store_module => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
store.registerModule(store_module);
});
}
pluginMainLinks = combineMainLinks(pluginMainLinks, plugin.mainLink);
} else {
console.exception(`Don't find Plugin ${loadModule}`);
}
});
console.log(routes);
router.addRoutes(routes);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
Vue.prototype.$flaschengeist_plugins = pluginMainLinks; Vue.prototype.$flaschengeist_plugins = pluginMainLinks;
console.log(plugins); console.log(pluginMainLinks);
}); });

View File

@ -103,15 +103,6 @@ import { PluginMainLink } from 'boot/plugins';
import { defineComponent, ref, computed } from '@vue/composition-api'; import { defineComponent, ref, computed } from '@vue/composition-api';
const links = [ const links = [
{
name: 'home',
title: 'home',
icon: 'mdi-home',
children: [
{ title: 'Neues Home', link: 'newHome', icon: 'mdi-google-home' },
{ title: 'Altes Home', link: 'oldHome', icon: 'mdi-home-modern' }
]
},
{ {
name: 'about', name: 'about',
title: 'about', title: 'about',
@ -162,11 +153,14 @@ export default defineComponent({
| PluginMainLink | PluginMainLink
| undefined = ctx.root.$flaschengeist_plugins.find( | undefined = ctx.root.$flaschengeist_plugins.find(
(plugin: PluginMainLink) => { (plugin: PluginMainLink) => {
if (ctx.root.$route.matched.length > 1) {
return plugin.name == ctx.root.$route.matched[1].name; return plugin.name == ctx.root.$route.matched[1].name;
} }
}
); );
console.log(test); console.log(test);
if (typeof test == undefined) { console.log(typeof test);
if (test == undefined) {
return []; return [];
} else { } else {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment

View File

@ -47,7 +47,7 @@ export default defineComponent({
function doLogin() { function doLogin() {
console.log(username.value, password.value); console.log(username.value, password.value);
void ctx.root.$router.push({ name: 'home' }); void ctx.root.$router.push({ name: 'main' });
} }
return { username, password, doLogin, rules }; return { username, password, doLogin, rules };

View File

@ -1,24 +0,0 @@
<template>
<q-page padding class="fit row justify-center content-center items-center">
<q-card class="col-4" height="" v-if="$route.name == 'home'">
<q-card-section>
Home
</q-card-section>
<q-card-section>
{{ a }}
</q-card-section>
</q-card>
<router-view />
</q-page>
</template>
<script lang="ts">
import { defineComponent } from '@vue/composition-api';
export default defineComponent({
// name: 'PageName'
setup(_, ctx) {
const a = ctx.root.$flaschengeist_plugins;
return { a };
}
});
</script>

View File

@ -1,14 +0,0 @@
<template>
<q-card class="col-4" height="">
<q-card-section>
Neues Zuhause
</q-card-section>
</q-card>
</template>
<script lang="ts">
import { defineComponent } from '@vue/composition-api';
export default defineComponent({
// name: 'PageName'
});
</script>

View File

@ -1,14 +0,0 @@
<template>
<q-card class="col-4" height="">
<q-card-section>
Altes Zuhause
</q-card-section>
</q-card>
</template>
<script lang="ts">
import { defineComponent } from '@vue/composition-api';
export default defineComponent({
// name: 'PageName'
});
</script>

View File

@ -1,30 +0,0 @@
import { Plugin } from 'boot/plugins';
import routes from 'src/plugins/plugin-example-2/routes';
const plugin: Plugin = {
router: routes,
mainLink: {
name: 'plugin2',
title: 'Plugin2',
link: 'plugin2',
icon: 'mdi-toy-brick-plus',
children: [
{
name: 'plugin2',
title: 'Neues Plugin2',
link: 'plugin2_1',
icon: 'mdi-information-outline'
},
{
name: 'plugin2',
title: 'Altes Plugin2',
link: 'plugin2_2',
icon: 'mdi-information-variant'
}
]
}
};
const mainLink = plugin.mainLink;
export { mainLink };
export default plugin;

View File

@ -1,9 +1,7 @@
import { Plugin } from 'boot/plugins'; import { Plugin, PluginMainLink } from 'boot/plugins';
import routes from 'src/plugins/plugin-example/routes'; import routes from 'src/plugins/plugin-example/routes';
const plugin: Plugin = { const mainLink: PluginMainLink = {
router: routes,
mainLink: {
name: 'plugin1', name: 'plugin1',
title: 'Plugin1', title: 'Plugin1',
link: 'plugin1', link: 'plugin1',
@ -22,10 +20,14 @@ const plugin: Plugin = {
icon: 'mdi-information-variant' icon: 'mdi-information-variant'
} }
] ]
}
}; };
const mainLink = plugin.mainLink; const plugin: Plugin = {
routes,
mainLink,
name: mainLink.name,
requiredModules: []
};
export { mainLink }; export { mainLink };

View File

@ -0,0 +1,33 @@
import { Plugin, PluginMainLink } from 'boot/plugins';
import routes from './routes';
const mainLink: PluginMainLink = {
name: 'user-plugin',
title: 'User',
link: 'user',
icon: 'mdi-account',
children: [
{
name: 'user-plugin',
title: 'Erstes Plugin für User',
link: 'user-plugin1',
icon: 'mdi-account-plus'
},
{
name: 'user-plugin',
title: 'Zweites Plugin für User',
link: 'user-plugin2',
icon: 'mdi-account-minus'
}
]
};
const plugin: Plugin = {
routes,
mainLink,
name: mainLink.name,
requiredModules: ['user']
};
export { mainLink };
export default plugin;

View File

@ -8,21 +8,18 @@ const routes: RouteConfig[] = [
}, },
children: [ children: [
{ {
path: 'plugin2', path: 'user',
name: 'plugin2', name: 'user',
components: { component: () => import('src/plugins/user/pages/User.vue'),
default: () => import('../pages/Plugin.vue'),
'plugin-nav': () => import('../components/navigation/PluginLinks.vue')
},
children: [ children: [
{ {
path: 'plugin2_1', path: 'user-plugin',
name: 'plugin2_1', name: 'user-plugin1',
component: () => import('../pages/NewPlugin.vue') component: () => import('../pages/NewPlugin.vue')
}, },
{ {
path: 'plugin2_2', path: 'user-plugin2',
name: 'plugin2_2', name: 'user-plugin2',
component: () => import('../pages/OldPlugin.vue') component: () => import('../pages/OldPlugin.vue')
} }
] ]

View File

@ -0,0 +1,28 @@
<template>
<div>
<q-page
padding
class="fit row justify-center content-center items-center"
v-if="$route.name == mainLink.link"
>
<q-card class="col-4" height="">
<q-card-section>
{{ mainLink.title }}
</q-card-section>
</q-card>
</q-page>
<router-view />
</div>
</template>
<script lang="ts">
import { defineComponent } from '@vue/composition-api';
import { mainLink } from '../plugin';
export default defineComponent({
// name: 'PageName'
setup(_, ctx) {
const a = ctx.root.$flaschengeist_plugins;
return { a, mainLink };
}
});
</script>

View File

@ -0,0 +1,21 @@
import { Plugin, PluginMainLink } from 'boot/plugins';
import routes from './routes';
const mainLink: PluginMainLink = {
name: 'user',
title: 'User',
link: 'user',
icon: 'mdi-account',
children: []
};
const plugin: Plugin = {
routes,
mainLink,
name: mainLink.name,
requiredModules: []
};
export { mainLink };
export default plugin;

View File

@ -0,0 +1,17 @@
import { RouteConfig } from 'vue-router';
const routes: RouteConfig[] = [
{
path: '/main',
component: () => import('layouts/MainLayout.vue'),
children: [
{
path: 'user',
name: 'user',
component: () => import('../pages/User.vue')
}
]
}
];
export default routes;

View File

@ -15,25 +15,9 @@ const routes: RouteConfig[] = [
}, },
{ {
path: '/main', path: '/main',
name: 'main',
component: () => import('layouts/MainLayout.vue'), component: () => import('layouts/MainLayout.vue'),
children: [ children: [
{
name: 'home',
path: 'home',
component: () => import('pages/home/Home.vue'),
children: [
{
name: 'newHome',
path: 'newHome',
component: () => import('pages/home/NewHome.vue')
},
{
name: 'oldHome',
path: 'oldHome',
component: () => import('pages/home/OldHome.vue')
}
]
},
{ {
name: 'about', name: 'about',
path: 'about', path: 'about',