@vtj/web
Version:
VTJ 是一款基于 Vue3 + Typescript 的低代码页面可视化设计器。内置低代码引擎、渲染器和代码生成器,面向前端开发者,开箱即用。 无缝嵌入本地开发工程,不改变前端开发流程和编码习惯。
190 lines (175 loc) • 4.62 kB
text/typescript
import type { App } from 'vue';
import type { Router, RouteRecordRaw } from 'vue-router';
import { kebabCase } from '@vtj/utils';
import { autoUpdate } from '@vtj/ui';
import type { ProjectSchema, PageFile } from '@vtj/core';
import {
createProvider,
LocalService,
NodeEnv,
ContextMode,
notify,
loading,
createAdapter,
createServiceRequest
} from '../renderer';
import { createModules } from '../modules';
type VtjModules = ReturnType<typeof createModules>;
export interface SetupElementAdminOptions {
id: string;
app: App;
ready?: () => void;
}
export interface setupElementAdminRoutesOptions {
id: string;
router: Router;
routes: any[];
layout: any;
}
function toElIcon(icon?: string) {
return icon ? `vi-ep:${kebabCase(icon)}` : undefined;
}
async function getComponent(path: string, modules: VtjModules) {
// const rawPath = `.vtj/vue/${id}.vue`;
const rawModule = modules[path];
if (rawModule) {
return ((await rawModule()) as any)?.default;
}
}
function createPageRoute(page: PageFile, modules: VtjModules) {
const { id, title, icon, hidden, cache = false, meta = {} } = page;
return {
path: `page/${id}`,
name: `Page_${id}`,
component: () => getComponent(`.vtj/vue/${id}.vue`, modules),
meta: {
title,
hidden,
icon: toElIcon(icon),
noCache: !cache,
...meta
}
};
}
function createNoMaskRoute(page: PageFile, modules: VtjModules) {
const { id, title, icon, cache = false, meta = {} } = page;
return {
path: `/page/${id}`,
component: () => getComponent(`.vtj/vue/${id}.vue`, modules),
meta: {
title,
icon: toElIcon(icon),
hidden: true,
noCache: !cache,
...meta
}
};
}
function childrenToRoutes(
children: PageFile[] = [],
noMask: any[],
modules: VtjModules
): RouteRecordRaw[] {
return children.map((child) => {
const { id, dir, mask = true, title, icon, hidden } = child;
if (dir) {
return {
path: '',
name: `Dir_${id}`,
redirect: `/page/${id}`,
meta: {
hidden,
title,
alwaysShow: true,
icon: toElIcon(icon)
},
children: childrenToRoutes(child.children, noMask, modules)
};
}
if (!mask) {
noMask.push(createNoMaskRoute(child, modules));
}
return createPageRoute(child, modules);
});
}
function pagesToRoutes(pages: PageFile[], layout: any, modules: VtjModules) {
const noMask: any[] = [];
const items: any[] = [];
for (const page of pages) {
const { id, dir, mask = true, title, icon, hidden, children = [] } = page;
if (!dir && !mask) {
noMask.push(createNoMaskRoute(page, modules));
}
if (children.length) {
items.push({
path: '/',
name: `Root_${id}`,
component: layout,
meta: {
hidden,
title,
alwaysShow: true,
icon: toElIcon(icon)
},
children: childrenToRoutes(children, noMask, modules)
});
} else {
items.push({
path: '/',
name: `Root_${id}`,
component: layout,
meta: {
hidden,
title,
alwaysShow: !!dir,
icon: toElIcon(icon)
},
children: dir ? [] : [createPageRoute(page, modules)]
});
}
}
return [...noMask, ...items];
}
export function setupElementAdmin(options: SetupElementAdminOptions) {
const { id, app, ready } = options;
const adapter = createAdapter({ loading, notify });
const service = new LocalService(createServiceRequest(notify));
const modules = createModules();
const { provider, onReady } = createProvider({
nodeEnv: process.env.NODE_ENV as NodeEnv,
mode: ContextMode.Raw,
modules,
adapter,
service,
dependencies: {
Vue: () => import('vue'),
VueRouter: () => import('vue-router')
},
project: {
id
}
});
onReady(async () => {
app.use(provider);
ready && ready();
});
if (process.env.NODE_ENV === 'production') {
autoUpdate();
}
}
export async function setupElementAdminRoutes(
options: setupElementAdminRoutesOptions
) {
const modules = createModules();
const { id, layout, routes, router } = options;
const loader = modules[`.vtj/projects/${id}.json`];
if (loader) {
const project = ((await loader()) as any).default as ProjectSchema;
const pages = project.pages || [];
const pageRoutes = pagesToRoutes(pages, layout, modules);
routes.unshift(...pageRoutes);
routes.forEach((item) => {
router.addRoute(item);
});
}
}