前言
💡 痛点: 企业重复开发相似的 CRUD 页面?业务人员需求排队等开发?定制化需求变更频繁导致维护成本爆炸?传统开发模式无法快速响应市场变化?
🎯 解决方案: 基于可视化拖拽引擎 + 插件化架构 + 自定义组件体系,构建企业级低代码平台,让业务人员能自助搭建应用,开发者专注平台能力建设。
#mermaid-svg-C1ZZis6RuWKKh5us{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-C1ZZis6RuWKKh5us .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-C1ZZis6RuWKKh5us .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-C1ZZis6RuWKKh5us .error-icon{fill:#552222;}#mermaid-svg-C1ZZis6RuWKKh5us .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-C1ZZis6RuWKKh5us .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-C1ZZis6RuWKKh5us .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-C1ZZis6RuWKKh5us .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-C1ZZis6RuWKKh5us .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-C1ZZis6RuWKKh5us .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-C1ZZis6RuWKKh5us .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-C1ZZis6RuWKKh5us .marker{fill:#333333;stroke:#333333;}#mermaid-svg-C1ZZis6RuWKKh5us .marker.cross{stroke:#333333;}#mermaid-svg-C1ZZis6RuWKKh5us svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-C1ZZis6RuWKKh5us p{margin:0;}#mermaid-svg-C1ZZis6RuWKKh5us .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-C1ZZis6RuWKKh5us .cluster-label text{fill:#333;}#mermaid-svg-C1ZZis6RuWKKh5us .cluster-label span{color:#333;}#mermaid-svg-C1ZZis6RuWKKh5us .cluster-label span p{background-color:transparent;}#mermaid-svg-C1ZZis6RuWKKh5us .label text,#mermaid-svg-C1ZZis6RuWKKh5us span{fill:#333;color:#333;}#mermaid-svg-C1ZZis6RuWKKh5us .node rect,#mermaid-svg-C1ZZis6RuWKKh5us .node circle,#mermaid-svg-C1ZZis6RuWKKh5us .node ellipse,#mermaid-svg-C1ZZis6RuWKKh5us .node polygon,#mermaid-svg-C1ZZis6RuWKKh5us .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-C1ZZis6RuWKKh5us .rough-node .label text,#mermaid-svg-C1ZZis6RuWKKh5us .node .label text,#mermaid-svg-C1ZZis6RuWKKh5us .image-shape .label,#mermaid-svg-C1ZZis6RuWKKh5us .icon-shape .label{text-anchor:middle;}#mermaid-svg-C1ZZis6RuWKKh5us .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-C1ZZis6RuWKKh5us .rough-node .label,#mermaid-svg-C1ZZis6RuWKKh5us .node .label,#mermaid-svg-C1ZZis6RuWKKh5us .image-shape .label,#mermaid-svg-C1ZZis6RuWKKh5us .icon-shape .label{text-align:center;}#mermaid-svg-C1ZZis6RuWKKh5us .node.clickable{cursor:pointer;}#mermaid-svg-C1ZZis6RuWKKh5us .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-C1ZZis6RuWKKh5us .arrowheadPath{fill:#333333;}#mermaid-svg-C1ZZis6RuWKKh5us .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-C1ZZis6RuWKKh5us .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-C1ZZis6RuWKKh5us .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-C1ZZis6RuWKKh5us .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-C1ZZis6RuWKKh5us .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-C1ZZis6RuWKKh5us .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-C1ZZis6RuWKKh5us .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-C1ZZis6RuWKKh5us .cluster text{fill:#333;}#mermaid-svg-C1ZZis6RuWKKh5us .cluster span{color:#333;}#mermaid-svg-C1ZZis6RuWKKh5us div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-C1ZZis6RuWKKh5us .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-C1ZZis6RuWKKh5us rect.text{fill:none;stroke-width:0;}#mermaid-svg-C1ZZis6RuWKKh5us .icon-shape,#mermaid-svg-C1ZZis6RuWKKh5us .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-C1ZZis6RuWKKh5us .icon-shape p,#mermaid-svg-C1ZZis6RuWKKh5us .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-C1ZZis6RuWKKh5us .icon-shape .label rect,#mermaid-svg-C1ZZis6RuWKKh5us .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-C1ZZis6RuWKKh5us .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-C1ZZis6RuWKKh5us .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-C1ZZis6RuWKKh5us :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 部署层
扩展层
后端服务层
前端引擎层
可视化编辑器
拖拽画布/属性面板
组件库
基础组件/业务组件
Schema 引擎
JSON 配置解析
API 网关
统一接口管理
流程引擎
BPMN 工作流
数据模型
动态 Schema
插件系统
自定义组件/钩子
组件市场
共享生态
多环境部署
开发/测试/生产
版本管理
Git 集成/回滚
一、可视化拖拽引擎核心架构
1.1 Schema 驱动的设计思想
typescript
// schema 核心类型定义
interface ComponentSchema {
id: string; // 组件唯一 ID
type: string; // 组件类型 button/input/table
props: Record<string, any>; // 组件属性
style: React.CSSProperties; // 内联样式
events: EventConfig[]; // 事件配置
children?: ComponentSchema[]; // 子组件(容器类)
}
interface EventConfig {
trigger: 'click' | 'change' | 'submit';
action: 'api' | 'navigation' | 'script';
config: Record<string, any>;
}
interface PageSchema {
version: string;
pageId: string;
name: string;
components: ComponentSchema[];
globalSettings: {
apiBaseUrl: string;
authConfig: AuthConfig;
theme: ThemeConfig;
};
}
// Schema 示例:一个完整的表单页面
const formPageSchema: PageSchema = {
version: '1.0.0',
pageId: 'user-create-form',
name: '用户创建表单',
components: [
{
id: 'root-container',
type: 'container',
props: { layout: 'vertical', gap: 16 },
style: { padding: '24px' },
events: [],
children: [
{
id: 'username-input',
type: 'input',
props: {
label: '用户名',
placeholder: '请输入用户名',
required: true,
validator: 'username',
},
style: {},
events: [
{
trigger: 'change',
action: 'api',
config: {
url: '/api/validate/username',
method: 'POST',
debounce: 500,
},
},
],
},
{
id: 'submit-btn',
type: 'button',
props: { text: '提交', type: 'primary' },
style: {},
events: [
{
trigger: 'click',
action: 'api',
config: {
url: '/api/users',
method: 'POST',
onSuccess: 'navigate:/users/list',
},
},
],
},
],
},
],
globalSettings: {
apiBaseUrl: 'https://api.example.com',
authConfig: { type: 'jwt', tokenKey: 'Authorization' },
theme: { primaryColor: '#1890ff' },
},
};
1.2 拖拽引擎实现
typescript
// drag-engine.ts - 拖拽引擎核心
import { ref, reactive, computed } from 'vue'; // 或 React 版本
export class DragEngine {
// 画布状态
private canvasState = reactive({
components: [] as ComponentSchema[],
selectedId: null as string | null,
dragging: null as DragItem | null,
});
// 拖拽开始
onDragStart(event: DragEvent, componentType: string, defaultProps: Record<string, any>) {
const dragItem: DragItem = {
type: 'new',
componentType,
defaultProps,
offsetX: event.offsetX,
offsetY: event.offsetY,
};
event.dataTransfer?.setData('application/json', JSON.stringify(dragItem));
this.canvasState.dragging = dragItem;
}
// 拖拽进入画布
onDragOver(event: DragEvent) {
event.preventDefault();
event.dataTransfer!.dropEffect = 'copy';
// 计算预览位置
this.updateDropIndicator(event.clientX, event.clientY);
}
// 放置组件
onDrop(event: DragEvent, parentId?: string) {
event.preventDefault();
const dragData = event.dataTransfer?.getData('application/json');
if (!dragData) return;
const dragItem: DragItem = JSON.parse(dragData);
const newComponent = this.createComponent(dragItem, parentId);
// 计算放置位置
const position = this.calculateDropPosition(event.clientX, event.clientY, parentId);
// 插入组件树
this.insertComponent(newComponent, position);
// 选中新组件
this.selectComponent(newComponent.id);
this.canvasState.dragging = null;
}
// 创建组件实例
private createComponent(item: DragItem, parentId?: string): ComponentSchema {
const id = `comp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
return {
id,
type: item.componentType,
props: { ...item.defaultProps },
style: {},
events: [],
children: isContainer(item.componentType) ? [] : undefined,
};
}
// 移动组件
moveComponent(componentId: string, newParentId: string, index: number) {
const component = this.findComponent(componentId);
if (!component) return;
// 从原位置移除
this.removeComponent(componentId);
// 插入新位置
this.insertComponent(component, { parentId: newParentId, index });
}
// 更新组件属性
updateComponentProps(componentId: string, props: Record<string, any>) {
const component = this.findComponent(componentId);
if (component) {
component.props = { ...component.props, ...props };
this.emitChange();
}
}
// 撤销/重做
private history: PageSchema[] = [];
private historyIndex = -1;
undo() {
if (this.historyIndex > 0) {
this.historyIndex--;
this.restoreState(this.history[this.historyIndex]);
}
}
redo() {
if (this.historyIndex < this.history.length - 1) {
this.historyIndex++;
this.restoreState(this.history[this.historyIndex]);
}
}
private emitChange() {
// 保存历史
this.history = this.history.slice(0, this.historyIndex + 1);
this.history.push(this.exportSchema());
this.historyIndex++;
// 触发变更事件
this.onChange?.(this.exportSchema());
}
}
1.3 画布渲染器
tsx
// canvas-renderer.tsx - Schema 到 React 组件的渲染器
import React from 'react';
interface CanvasRendererProps {
schema: PageSchema;
previewMode: boolean;
onComponentClick?: (componentId: string) => void;
}
export const CanvasRenderer: React.FC<CanvasRendererProps> = ({
schema,
previewMode,
onComponentClick,
}) => {
// 递归渲染组件树
const renderComponent = (component: ComponentSchema): React.ReactNode => {
const Component = componentRegistry.get(component.type);
if (!Component) {
return <div key={component.id}>Unknown component: {component.type}</div>;
}
// 处理子组件
const children = component.children?.map(renderComponent);
// 绑定事件
const eventHandlers = bindEvents(component.events, previewMode);
return (
<ComponentWrapper
key={component.id}
componentId={component.id}
selected={!previewMode && schema.selectedComponentId === component.id}
onClick={() => !previewMode && onComponentClick?.(component.id)}
>
<Component
{...component.props}
style={component.style}
{...eventHandlers}
>
{children}
</Component>
</ComponentWrapper>
);
};
return <div className="canvas-container">{schema.components.map(renderComponent)}</div>;
};
// 事件绑定
function bindEvents(events: EventConfig[], previewMode: boolean) {
if (!previewMode || events.length === 0) return {};
const handlers: Record<string, () => void> = {};
for (const event of events) {
if (event.trigger === 'click') {
handlers.onClick = () => executeAction(event.action, event.config);
} else if (event.trigger === 'change') {
handlers.onChange = (value: any) => executeAction(event.action, event.config, value);
}
}
return handlers;
}
// 执行动作
async function executeAction(
action: string,
config: Record<string, any>,
payload?: any
) {
switch (action) {
case 'api':
await apiClient.request({
url: config.url,
method: config.method,
data: payload,
});
break;
case 'navigation':
router.push(config.url);
break;
case 'script':
// 执行自定义脚本(沙箱环境)
await executeSandboxedScript(config.code, payload);
break;
}
}
二、插件化架构设计
2.1 插件系统核心接口
typescript
// plugin-system.ts
export interface LowCodePlugin {
name: string;
version: string;
author?: string;
description?: string;
// 生命周期钩子
init?(context: PluginContext): void | Promise<void>;
destroy?(): void | Promise<void>;
// 扩展点
registerComponents?(): ComponentDefinition[];
registerActions?(): ActionDefinition[];
registerTemplates?(): TemplateDefinition[];
registerThemes?(): ThemeDefinition[];
// 事件钩子
onBeforeSave?(schema: PageSchema): PageSchema | Promise<PageSchema>;
onAfterLoad?(schema: PageSchema): void;
onComponentAdd?(component: ComponentSchema): void;
onComponentUpdate?(component: ComponentSchema, changes: Partial<ComponentSchema>): void;
}
export class PluginManager {
private plugins: Map<string, LowCodePlugin> = new Map();
private context: PluginContext;
constructor() {
this.context = {
registerComponent: this.registerComponent.bind(this),
registerAction: this.registerAction.bind(this),
emit: this.emit.bind(this),
on: this.on.bind(this),
};
}
// 注册插件
async register(plugin: LowCodePlugin): Promise<void> {
if (this.plugins.has(plugin.name)) {
throw new Error(`Plugin ${plugin.name} already registered`);
}
// 初始化插件
await plugin.init?.(this.context);
// 注册扩展
if (plugin.registerComponents) {
const components = plugin.registerComponents();
components.forEach((comp) => componentRegistry.register(comp));
}
if (plugin.registerActions) {
const actions = plugin.registerActions();
actions.forEach((action) => actionRegistry.register(action));
}
this.plugins.set(plugin.name, plugin);
}
// 卸载插件
async unregister(pluginName: string): Promise<void> {
const plugin = this.plugins.get(pluginName);
if (!plugin) return;
await plugin.destroy?.();
this.plugins.delete(pluginName);
}
// 触发钩子
async triggerHook(hookName: string, ...args: any[]): Promise<any> {
for (const plugin of this.plugins.values()) {
const hook = (plugin as any)[hookName];
if (typeof hook === 'function') {
await hook(...args);
}
}
}
}
2.2 自定义组件插件示例
typescript
// plugins/advanced-chart-plugin.ts
import { LowCodePlugin } from '../plugin-system';
export class AdvancedChartPlugin implements LowCodePlugin {
name = 'advanced-chart';
version = '1.0.0';
author = 'LowCode Team';
description = '高级图表组件(折线图/柱状图/饼图)';
registerComponents() {
return [
{
type: 'line-chart',
name: '折线图',
icon: 'line-chart',
category: 'charts',
defaultProps: {
title: '折线图',
dataSource: { type: 'static', data: [] },
xField: 'date',
yField: 'value',
color: '#1890ff',
showLegend: true,
},
propSchema: {
title: { type: 'string', label: '图表标题' },
dataSource: { type: 'dataSource', label: '数据源' },
xField: { type: 'string', label: 'X轴字段' },
yField: { type: 'string', label: 'Y轴字段' },
color: { type: 'color', label: '线条颜色' },
showLegend: { type: 'boolean', label: '显示图例' },
},
events: ['click', 'dataZoom'],
render: LineChartComponent,
},
{
type: 'bar-chart',
name: '柱状图',
icon: 'bar-chart',
category: 'charts',
defaultProps: {
title: '柱状图',
dataSource: { type: 'static', data: [] },
xField: 'category',
yField: 'value',
color: '#52c41a',
},
propSchema: {
title: { type: 'string', label: '图表标题' },
dataSource: { type: 'dataSource', label: '数据源' },
xField: { type: 'string', label: 'X轴字段' },
yField: { type: 'string', label: 'Y轴字段' },
color: { type: 'color', label: '柱状颜色' },
},
events: ['click', 'dataZoom'],
render: BarChartComponent,
},
];
}
registerActions() {
return [
{
name: 'fetch-chart-data',
description: '获取图表数据',
configSchema: {
apiUrl: { type: 'string', required: true },
refreshInterval: { type: 'number', default: 0 },
},
execute: async (config: any) => {
const data = await fetch(config.apiUrl).then((r) => r.json());
return { success: true, data };
},
},
];
}
async init(context) {
console.log('Advanced Chart Plugin initialized');
// 可以在这里注册全局样式、初始化第三方库等
}
}
2.3 插件市场与动态加载
typescript
// plugin-market.ts
export class PluginMarket {
private registryUrl = 'https://plugins.lowcode.com';
// 从市场安装插件
async install(pluginId: string): Promise<void> {
// 1. 从市场获取插件元数据
const metadata = await this.fetchPluginMetadata(pluginId);
// 2. 检查兼容性
if (!this.checkCompatibility(metadata)) {
throw new Error(`Plugin ${pluginId} is not compatible with current version`);
}
// 3. 下载插件包
const pluginUrl = `${this.registryUrl}/plugins/${pluginId}/download`;
const pluginCode = await this.downloadPlugin(pluginUrl);
// 4. 验证插件签名(安全)
if (!this.verifySignature(pluginCode, metadata.signature)) {
throw new Error('Plugin signature verification failed');
}
// 5. 沙箱执行插件代码
const plugin = await this.loadPluginInSandbox(pluginCode);
// 6. 注册到插件管理器
await pluginManager.register(plugin);
// 7. 保存到本地存储
await this.savePlugin(pluginId, pluginCode, metadata);
}
// 沙箱加载插件
private async loadPluginInSandbox(code: string): Promise<LowCodePlugin> {
const sandbox = {
console,
fetch: this.sandboxedFetch.bind(this),
localStorage: this.sandboxedStorage,
// 禁止访问敏感 API
// window, document 等仅提供受限访问
};
const fn = new Function('sandbox', `with (sandbox) { ${code} }`);
const pluginFactory = fn(sandbox);
return pluginFactory();
}
// 获取已安装插件列表
async getInstalledPlugins(): Promise<InstalledPlugin[]> {
const plugins = localStorage.getItem('installed_plugins');
return plugins ? JSON.parse(plugins) : [];
}
}
三、数据模型与动态表单
3.1 动态数据模型
typescript
// data-model.ts
export interface DataModel {
id: string;
name: string;
displayName: string;
fields: DataField[];
relations?: ModelRelation[];
indexes?: DataIndex[];
}
export interface DataField {
name: string;
displayName: string;
type: 'string' | 'number' | 'boolean' | 'date' | 'datetime' | 'json' | 'file' | 'relation';
required: boolean;
unique?: boolean;
defaultValue?: any;
validation?: ValidationRule[];
ui?: {
widget: 'input' | 'textarea' | 'select' | 'radio' | 'checkbox' | 'date-picker' | 'upload';
options?: { label: string; value: any }[];
placeholder?: string;
};
}
// 动态表单生成
export function generateFormFromModel(model: DataModel): ComponentSchema {
const formItems = model.fields.map((field) => {
const formItem: ComponentSchema = {
id: `field_${field.name}`,
type: getWidgetType(field),
props: {
label: field.displayName,
field: field.name,
required: field.required,
placeholder: field.ui?.placeholder,
...(field.ui?.options ? { options: field.ui.options } : {}),
},
style: {},
events: [],
};
return formItem;
});
return {
version: '1.0.0',
pageId: `form_${model.id}`,
name: `${model.displayName}表单`,
components: [
{
id: `form_${model.id}`,
type: 'form',
props: {
model: model.id,
fields: model.fields.map((f) => f.name),
},
style: {},
events: [
{
trigger: 'submit',
action: 'api',
config: {
url: `/api/data/${model.id}`,
method: 'POST',
},
},
],
children: formItems,
},
],
globalSettings: {
apiBaseUrl: '',
authConfig: { type: 'jwt' },
theme: {},
},
};
}
function getWidgetType(field: DataField): string {
const widgetMap: Record<string, string> = {
input: 'input',
textarea: 'textarea',
select: 'select',
radio: 'radio-group',
checkbox: 'checkbox-group',
'date-picker': 'date-picker',
upload: 'upload',
};
return widgetMap[field.ui?.widget || 'input'] || 'input';
}
3.2 动态列表/表格生成
typescript
// data-table-generator.ts
export function generateTableFromModel(model: DataModel): ComponentSchema {
const columns = model.fields.map((field) => ({
title: field.displayName,
dataIndex: field.name,
key: field.name,
render: getColumnRender(field),
}));
return {
version: '1.0.0',
pageId: `table_${model.id}`,
name: `${model.displayName}列表`,
components: [
{
id: `table_${model.id}`,
type: 'table',
props: {
columns,
dataSource: {
type: 'api',
url: `/api/data/${model.id}`,
method: 'GET',
params: {},
},
pagination: true,
pageSize: 20,
rowKey: 'id',
actions: [
{ label: '编辑', action: 'navigate', url: `/edit/:id` },
{ label: '删除', action: 'api', url: `/api/data/${model.id}/:id`, method: 'DELETE' },
],
},
style: {},
events: [],
},
],
globalSettings: {
apiBaseUrl: '',
authConfig: { type: 'jwt' },
theme: {},
},
};
}
四、流程引擎集成
4.1 BPMN 流程设计器
typescript
// bpmn-designer.ts
export interface WorkflowDefinition {
id: string;
name: string;
description?: string;
bpmnXml: string; // BPMN 2.0 XML
nodes: WorkflowNode[];
transitions: WorkflowTransition[];
}
export interface WorkflowNode {
id: string;
type: 'start' | 'end' | 'userTask' | 'serviceTask' | 'exclusiveGateway' | 'inclusiveGateway';
name: string;
position: { x: number; y: number };
config: {
assignee?: string; // 用户任务审批人
serviceUrl?: string; // 服务任务 API
condition?: string; // 网关条件表达式
formId?: string; // 关联表单
};
}
export class WorkflowEngine {
// 启动流程实例
async startProcess(definitionId: string, formData: Record<string, any>): Promise<string> {
const instanceId = `inst_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
await db.workflowInstances.create({
id: instanceId,
definitionId,
status: 'running',
formData,
startTime: new Date(),
currentNodes: [this.getStartNode(definitionId)],
});
// 执行第一个节点
await this.executeNode(instanceId, this.getStartNode(definitionId));
return instanceId;
}
// 执行节点
private async executeNode(instanceId: string, nodeId: string): Promise<void> {
const node = this.getNode(nodeId);
const instance = await this.getInstance(instanceId);
switch (node.type) {
case 'start':
// 启动节点,直接到下一个
await this.moveToNextNodes(instanceId, nodeId);
break;
case 'userTask':
// 创建待办任务
await this.createTask(instanceId, node);
break;
case 'serviceTask':
// 调用外部服务
const result = await this.callService(node.config.serviceUrl, instance.formData);
instance.variables = { ...instance.variables, ...result };
await this.moveToNextNodes(instanceId, nodeId);
break;
case 'exclusiveGateway':
// 排他网关,计算条件
const condition = this.evaluateCondition(node.config.condition, instance);
const nextNode = this.getNextNodeByCondition(nodeId, condition);
await this.moveToNextNodes(instanceId, nextNode);
break;
}
}
// 完成用户任务
async completeTask(taskId: string, formData: Record<string, any>): Promise<void> {
const task = await db.tasks.findOne(taskId);
const instance = await this.getInstance(task.instanceId);
// 更新实例数据
instance.formData = { ...instance.formData, ...formData };
await db.workflowInstances.update(instance.id, instance);
// 移动到下一个节点
await this.moveToNextNodes(instance.id, task.nodeId);
}
}
4.2 可视化流程设计器
tsx
// workflow-designer.tsx
import React, { useState } from 'react';
import ReactFlow, {
Node,
Edge,
addEdge,
Connection,
useNodesState,
useEdgesState,
} from 'reactflow';
const nodeTypes = {
start: StartNode,
end: EndNode,
userTask: UserTaskNode,
serviceTask: ServiceTaskNode,
gateway: GatewayNode,
};
export const WorkflowDesigner: React.FC<{ definitionId: string }> = ({ definitionId }) => {
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const onConnect = (connection: Connection) => {
setEdges((eds) => addEdge({ ...connection, type: 'smoothstep' }, eds));
};
const addNode = (type: string, position: { x: number; y: number }) => {
const newNode: Node = {
id: `node_${Date.now()}`,
type,
position,
data: { label: `${type} node` },
};
setNodes((nds) => [...nds, newNode]);
};
const saveWorkflow = async () => {
const definition: WorkflowDefinition = {
id: definitionId,
name: 'New Workflow',
nodes: nodes.map((n) => ({
id: n.id,
type: n.type as any,
name: n.data.label,
position: n.position,
config: n.data.config || {},
})),
transitions: edges.map((e) => ({
id: e.id,
source: e.source,
target: e.target,
condition: e.data?.condition,
})),
bpmnXml: generateBPMN(nodes, edges),
};
await apiClient.post('/api/workflows', definition);
};
return (
<div className="workflow-designer">
<div className="toolbar">
<button onClick={() => addNode('start', { x: 100, y: 100 })}>开始节点</button>
<button onClick={() => addNode('userTask', { x: 300, y: 100 })}>用户任务</button>
<button onClick={() => addNode('serviceTask', { x: 500, y: 100 })}>服务任务</button>
<button onClick={() => addNode('gateway', { x: 400, y: 250 })}>网关</button>
<button onClick={() => addNode('end', { x: 700, y: 100 })}>结束节点</button>
<button onClick={saveWorkflow}>保存流程</button>
</div>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
nodeTypes={nodeTypes}
fitView
/>
</div>
);
};
五、企业级功能
5.1 权限控制
typescript
// permission-system.ts
export interface PermissionPolicy {
role: string;
permissions: Permission[];
}
export interface Permission {
resource: string; // 资源类型:page/component/data/api
action: 'create' | 'read' | 'update' | 'delete' | '*';
conditions?: PermissionCondition[];
}
export class PermissionManager {
// 检查权限
async checkPermission(
user: User,
resource: string,
action: string,
context?: Record<string, any>
): Promise<boolean> {
const policies = await this.getUserPolicies(user);
for (const policy of policies) {
for (const perm of policy.permissions) {
if (this.matchPermission(perm, resource, action, context)) {
// 检查条件
if (perm.conditions) {
const conditionResult = await this.evaluateConditions(
perm.conditions,
user,
context
);
if (!conditionResult) continue;
}
return true;
}
}
}
return false;
}
// 数据行级权限
async applyRowLevelSecurity(
query: DataQuery,
user: User
): Promise<DataQuery> {
const rlsPolicies = await this.getRowLevelPolicies(user);
for (const policy of rlsPolicies) {
query.where = {
AND: [query.where, policy.condition],
};
}
return query;
}
}
5.2 版本管理
typescript
// version-control.ts
export class VersionManager {
// 保存版本
async saveVersion(
pageId: string,
schema: PageSchema,
comment: string
): Promise<Version> {
const version: Version = {
id: `ver_${Date.now()}`,
pageId,
schema: JSON.parse(JSON.stringify(schema)), // 深拷贝
comment,
author: currentUser.id,
createdAt: new Date(),
versionNumber: await this.getNextVersionNumber(pageId),
};
await db.versions.create(version);
return version;
}
// 版本对比
async compareVersions(versionId1: string, versionId2: string): Promise<DiffResult> {
const v1 = await db.versions.findById(versionId1);
const v2 = await db.versions.findById(versionId2);
return {
added: jsonDiff.getAdded(v1.schema, v2.schema),
removed: jsonDiff.getRemoved(v1.schema, v2.schema),
changed: jsonDiff.getChanged(v1.schema, v2.schema),
};
}
// 回滚到指定版本
async rollback(versionId: string): Promise<void> {
const version = await db.versions.findById(versionId);
const page = await db.pages.findById(version.pageId);
// 保存当前版本为历史版本
await this.saveVersion(page.id, page.schema, `Rollback to ${versionId}`);
// 恢复版本
page.schema = version.schema;
await db.pages.update(page.id, page);
}
// Git 集成(高级功能)
async commitToGit(pageId: string, message: string): Promise<void> {
const page = await db.pages.findById(pageId);
const repoPath = `/repos/${page.appId}`;
// 初始化 Git 仓库(如果不存在)
if (!fs.existsSync(path.join(repoPath, '.git'))) {
await git.init(repoPath);
}
// 写入文件
const filePath = path.join(repoPath, `${page.name}.json`);
fs.writeFileSync(filePath, JSON.stringify(page.schema, null, 2));
// 提交
await git.add(repoPath, filePath);
await git.commit(repoPath, message);
}
}
六、性能优化
6.1 Schema 懒加载与分片
typescript
// schema-optimization.ts
export class SchemaOptimizer {
// 大页面分片加载
async splitSchema(schema: PageSchema): Promise<SchemaChunk[]> {
const chunks: SchemaChunk[] = [];
const components = schema.components;
// 按容器组件分片
const containers = components.filter((c) => c.children);
for (const container of containers) {
chunks.push({
id: `chunk_${container.id}`,
components: [container, ...container.children!],
priority: this.calculatePriority(container),
});
}
// 非容器组件作为独立分片
const standalone = components.filter((c) => !c.children);
if (standalone.length > 0) {
chunks.push({
id: 'chunk_standalone',
components: standalone,
priority: 0,
});
}
return chunks.sort((a, b) => a.priority - b.priority);
}
// 懒加载非关键组件
async lazyLoadComponents(pageId: string, visibleArea: DOMRect): Promise<void> {
const schema = await this.getSchema(pageId);
const allComponents = schema.components;
for (const component of allComponents) {
const element = document.getElementById(component.id);
if (element) {
const rect = element.getBoundingClientRect();
if (this.isInViewport(rect, visibleArea)) {
await this.loadComponentAssets(component);
}
}
}
}
}
6.2 渲染性能优化
typescript
// render-optimization.tsx
import React, { memo, useMemo, useCallback } from 'react';
// 组件记忆化
export const MemoizedComponent = memo(({ schema, ...props }: ComponentProps) => {
return <BaseComponent schema={schema} {...props} />;
}, (prevProps, nextProps) => {
// 自定义比较函数
return (
prevProps.schema.id === nextProps.schema.id &&
prevProps.schema.version === nextProps.schema.version &&
prevProps.previewMode === nextProps.previewMode
);
});
// 虚拟滚动(大列表)
export function VirtualList({ items, itemHeight, containerHeight }: VirtualListProps) {
const [scrollTop, setScrollTop] = useState(0);
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(
startIndex + Math.ceil(containerHeight / itemHeight) + 1,
items.length
);
const visibleItems = useMemo(() => {
return items.slice(startIndex, endIndex).map((item, index) => ({
...item,
index: startIndex + index,
style: {
position: 'absolute' as const,
top: (startIndex + index) * itemHeight,
height: itemHeight,
},
}));
}, [items, startIndex, endIndex]);
return (
<div
style={{ height: containerHeight, overflow: 'auto' }}
onScroll={(e) => setScrollTop(e.currentTarget.scrollTop)}
>
<div style={{ height: items.length * itemHeight, position: 'relative' }}>
{visibleItems.map((item) => (
<div key={item.id} style={item.style}>
{item.content}
</div>
))}
</div>
</div>
);
}
七、Checklist 总结
□ 前端引擎
□ Schema 驱动架构(PageSchema/ComponentSchema)
□ 拖拽引擎(DragEngine - 拖拽/放置/移动)
□ 画布渲染器(递归渲染/事件绑定)
□ 属性面板(动态表单生成)
□ 撤销/重做(History 管理)
□ 插件系统
□ 插件接口定义(LowCodePlugin)
□ 插件管理器(注册/卸载/生命周期)
□ 扩展点(组件/动作/模板/主题)
□ 事件钩子(onBeforeSave/onAfterLoad)
□ 插件市场(安装/签名验证/沙箱执行)
□ 数据模型
□ 动态数据模型定义(DataModel/DataField)
□ 动态表单生成(generateFormFromModel)
□ 动态表格生成(generateTableFromModel)
□ 数据校验(ValidationRule)
□ 关联关系(ModelRelation)
□ 流程引擎
□ BPMN 流程设计器(ReactFlow)
□ 流程定义(WorkflowDefinition)
□ 流程引擎(启动/执行/完成任务)
□ 节点类型(开始/结束/用户任务/服务任务/网关)
□ 条件网关(条件表达式计算)
□ 企业级功能
□ 权限控制(RBAC + 行级权限)
□ 版本管理(保存/对比/回滚)
□ Git 集成(提交/分支/合并)
□ 多环境部署(开发/测试/生产)
□ 审计日志(操作记录)
□ 性能优化
□ Schema 分片加载
□ 懒加载非关键组件
□ 组件记忆化(React.memo)
□ 虚拟滚动(大列表)
□ 代码分割(Dynamic Import)
总结
一句话总结: 低代码平台 = Schema 驱动引擎 + 插件化架构 + 可视化设计器,通过拖拽生成 JSON Schema,再由渲染器动态渲染成应用,让业务人员自助搭建,开发者专注平台能力。
核心技术栈:
| 模块 | 技术选型 | 关键点 |
|---|---|---|
| 前端框架 | React/Vue 3 + TypeScript | 组件化/类型安全 |
| 拖拽引擎 | dnd-kit/react-dnd | 拖拽状态管理 |
| 流程设计器 | ReactFlow | 节点/边/自定义样式 |
| Schema 存储 | JSON + IndexedDB/PostgreSQL | 版本管理/离线支持 |
| 插件系统 | 微前端/qiankun | 隔离/动态加载 |
| 后端 | Node.js/Go + PostgreSQL | 高并发/事务支持 |
下一步推荐:
- AI 辅助低代码(自然语言生成页面)
- WebAssembly 高性能组件渲染
- 跨端低代码(Web + 小程序 + App)