mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-07-02 11:12:20 +08:00
240 lines
8.7 KiB
TypeScript
240 lines
8.7 KiB
TypeScript
/**
|
|
* This file is part of the NocoBase (R) project.
|
|
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
* Authors: NocoBase Team.
|
|
*
|
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
*/
|
|
|
|
import { BlockModel } from '@nocobase/client';
|
|
import { APIResource } from '@nocobase/flow-engine';
|
|
import { Card, Skeleton, Spin } from 'antd';
|
|
import React, { createRef } from 'react';
|
|
import { CodeEditor } from './CodeEditor';
|
|
|
|
export class LowcodeBlockModel extends BlockModel {
|
|
ref = createRef<HTMLDivElement>();
|
|
declare resource: APIResource;
|
|
|
|
render() {
|
|
const { loading, error } = this.props;
|
|
|
|
if (error) {
|
|
return (
|
|
<Card>
|
|
<div style={{ color: 'red', padding: '16px' }}>Error loading lowcode component: {error}</div>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Card id={`model-${this.uid}`} className="lowcode-block">
|
|
<Spin spinning={loading} tip="Loading lowcode component...">
|
|
<div ref={this.ref} style={{ width: '100%' }} />
|
|
</Spin>
|
|
</Card>
|
|
);
|
|
}
|
|
}
|
|
|
|
// Export CodeEditor for external use
|
|
export { CodeEditor };
|
|
|
|
LowcodeBlockModel.define({
|
|
title: 'Lowcode',
|
|
group: 'Content',
|
|
icon: 'CloudOutlined',
|
|
defaultOptions: {
|
|
use: 'LowcodeBlockModel',
|
|
stepParams: {
|
|
default: {
|
|
executionStep: {
|
|
code: `
|
|
// Welcome to the lowcode block
|
|
// Create powerful interactive components with JavaScript
|
|
element.innerHTML = \`
|
|
<div style="padding: 24px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; max-width: 600px;">
|
|
<h2 style="color: #1890ff; margin: 0 0 20px 0; font-size: 24px; font-weight: 600;">
|
|
🚀 Welcome to Lowcode Block
|
|
</h2>
|
|
|
|
<p style="color: #666; margin-bottom: 24px; font-size: 16px;">
|
|
Build interactive components with JavaScript and external libraries
|
|
</p>
|
|
|
|
<div style="background: #f8f9fa; border-radius: 8px; padding: 20px; margin-bottom: 20px;">
|
|
<h3 style="color: #333; margin: 0 0 16px 0; font-size: 18px;">✨ Key Features</h3>
|
|
<ul style="margin: 0; padding-left: 20px; color: #555;">
|
|
<li style="margin-bottom: 8px;">🎨 <strong>Custom JavaScript execution</strong> - Full programming capabilities</li>
|
|
<li style="margin-bottom: 8px;">📚 <strong>External library support</strong> - Load any npm package or CDN library</li>
|
|
<li style="margin-bottom: 8px;">🔗 <strong>NocoBase API integration</strong> - Access your data and collections</li>
|
|
<li style="margin-bottom: 8px;">💡 <strong>Async/await support</strong> - Handle asynchronous operations</li>
|
|
<li style="margin-bottom: 8px;">🎯 <strong>Direct DOM manipulation</strong> - Full control over rendering</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div style="background: #e6f7ff; border-left: 4px solid #1890ff; padding: 16px; border-radius: 4px;">
|
|
<p style="margin: 0; color: #333; font-size: 14px;">
|
|
💡 <strong>Ready to start?</strong> Replace this code with your custom JavaScript to build amazing components!
|
|
</p>
|
|
</div>
|
|
</div>
|
|
\`;
|
|
`.trim(),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
LowcodeBlockModel.registerFlow({
|
|
key: 'default',
|
|
auto: true,
|
|
steps: {
|
|
setMainResource: {
|
|
handler(ctx) {
|
|
if (ctx.model.resource) {
|
|
return;
|
|
}
|
|
ctx.model.resource = new APIResource();
|
|
ctx.model.resource.setAPIClient(ctx.globals.api);
|
|
},
|
|
},
|
|
executionStep: {
|
|
uiSchema: {
|
|
code: {
|
|
type: 'string',
|
|
title: 'Execution Code',
|
|
'x-component': 'CodeEditor',
|
|
'x-component-props': {
|
|
minHeight: '400px',
|
|
theme: 'light',
|
|
enableLinter: true,
|
|
},
|
|
},
|
|
},
|
|
defaultParams: {
|
|
code: `
|
|
// Welcome to the lowcode block
|
|
// Create powerful interactive components with JavaScript
|
|
element.innerHTML = \`
|
|
<div style="padding: 24px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; max-width: 600px;">
|
|
<h2 style="color: #1890ff; margin: 0 0 20px 0; font-size: 24px; font-weight: 600;">
|
|
🚀 Welcome to Lowcode Block
|
|
</h2>
|
|
|
|
<p style="color: #666; margin-bottom: 24px; font-size: 16px;">
|
|
Build interactive components with JavaScript and external libraries
|
|
</p>
|
|
|
|
<div style="background: #f8f9fa; border-radius: 8px; padding: 20px; margin-bottom: 20px;">
|
|
<h3 style="color: #333; margin: 0 0 16px 0; font-size: 18px;">✨ Key Features</h3>
|
|
<ul style="margin: 0; padding-left: 20px; color: #555;">
|
|
<li style="margin-bottom: 8px;">🎨 <strong>Custom JavaScript execution</strong> - Full programming capabilities</li>
|
|
<li style="margin-bottom: 8px;">📚 <strong>External library support</strong> - Load any npm package or CDN library</li>
|
|
<li style="margin-bottom: 8px;">🔗 <strong>NocoBase API integration</strong> - Access your data and collections</li>
|
|
<li style="margin-bottom: 8px;">💡 <strong>Async/await support</strong> - Handle asynchronous operations</li>
|
|
<li style="margin-bottom: 8px;">🎯 <strong>Direct DOM manipulation</strong> - Full control over rendering</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div style="background: #e6f7ff; border-left: 4px solid #1890ff; padding: 16px; border-radius: 4px;">
|
|
<p style="margin: 0; color: #333; font-size: 14px;">
|
|
💡 <strong>Ready to start?</strong> Replace this code with your custom JavaScript to build amazing components!
|
|
</p>
|
|
</div>
|
|
</div>
|
|
\`;
|
|
`.trim(),
|
|
},
|
|
settingMode: 'drawer',
|
|
title: 'Code',
|
|
async handler(ctx, params: any) {
|
|
ctx.model.setProps('loading', true);
|
|
ctx.model.setProps('error', null);
|
|
ctx.reactView.onRefReady(ctx.model.ref, async (element: HTMLElement) => {
|
|
try {
|
|
// Get requirejs from app context
|
|
const requirejs = ctx.app?.requirejs?.requirejs;
|
|
|
|
// Helper function to load CSS
|
|
const loadCSS = (url: string): Promise<void> => {
|
|
return new Promise((resolve, reject) => {
|
|
// Check if CSS is already loaded
|
|
const existingLink = document.querySelector(`link[href="${url}"]`);
|
|
if (existingLink) {
|
|
resolve();
|
|
return;
|
|
}
|
|
|
|
const link = document.createElement('link');
|
|
link.rel = 'stylesheet';
|
|
link.href = url;
|
|
link.onload = () => resolve();
|
|
link.onerror = () => reject(new Error(`Failed to load CSS: ${url}`));
|
|
document.head.appendChild(link);
|
|
});
|
|
};
|
|
|
|
// Helper function for async requirejs
|
|
const requireAsync = (modules: string | string[]): Promise<any> => {
|
|
return new Promise((resolve, reject) => {
|
|
if (!requirejs) {
|
|
reject(new Error('requirejs is not available'));
|
|
return;
|
|
}
|
|
|
|
const moduleList = Array.isArray(modules) ? modules : [modules];
|
|
requirejs(
|
|
moduleList,
|
|
(...args: any[]) => {
|
|
// If single module, return the module directly
|
|
// If multiple modules, return array
|
|
resolve(moduleList.length === 1 ? args[0] : args);
|
|
},
|
|
reject,
|
|
);
|
|
});
|
|
};
|
|
|
|
const getModelById = (uid: string) => {
|
|
return ctx.globals.flowEngine.getModel(uid);
|
|
};
|
|
|
|
const request = ctx.globals.api.request.bind(ctx.globals.api);
|
|
|
|
// Create a safe execution context for the code (as async function)
|
|
// Wrap user code in an async function
|
|
const wrappedCode = `
|
|
return (async function(element, ctx, model, resource, requirejs, requireAsync, loadCSS, getModelById, request) {
|
|
${params.code}
|
|
}).apply(this, arguments);
|
|
`;
|
|
const executionFunction = new Function(wrappedCode);
|
|
|
|
// Execute the code
|
|
await executionFunction(
|
|
element,
|
|
ctx,
|
|
ctx.model,
|
|
ctx.model.resource,
|
|
requirejs,
|
|
requireAsync,
|
|
loadCSS,
|
|
getModelById,
|
|
request,
|
|
);
|
|
|
|
ctx.model.setProps('loading', false);
|
|
} catch (error: any) {
|
|
console.error('Lowcode component execution error:', error);
|
|
ctx.model.setProps('error', error.message);
|
|
ctx.model.setProps('loading', false);
|
|
}
|
|
});
|
|
},
|
|
},
|
|
},
|
|
});
|