mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-03-05 09:47:20 +00:00
Merge branch feat/plugin-utils-pane into release/1.0.0
Title: feat: 完成 utils 面板的基本功能 Link: https://code.aone.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/codereview/4227732
This commit is contained in:
commit
47a32edcd9
@ -2,8 +2,9 @@ import logo from '@ali/lowcode-plugin-sample-logo';
|
|||||||
import samplePreview from '@ali/lowcode-plugin-sample-preview';
|
import samplePreview from '@ali/lowcode-plugin-sample-preview';
|
||||||
import undoRedo from '@ali/lowcode-plugin-undo-redo';
|
import undoRedo from '@ali/lowcode-plugin-undo-redo';
|
||||||
import componentsPane from '@ali/lowcode-plugin-components-pane';
|
import componentsPane from '@ali/lowcode-plugin-components-pane';
|
||||||
import outline, { OutlinePane } from '@ali/lowcode-plugin-outline-pane';
|
import outline from '@ali/lowcode-plugin-outline-pane';
|
||||||
import dataSourcePane from '@ali/lowcode-plugin-datasource-pane';
|
import dataSourcePane from '@ali/lowcode-plugin-datasource-pane';
|
||||||
|
import utilsPane from '@ali/lowcode-plugin-utils-pane';
|
||||||
import zhEn from '@ali/lowcode-plugin-zh-en';
|
import zhEn from '@ali/lowcode-plugin-zh-en';
|
||||||
import eventBindDialog from '@ali/lowcode-plugin-event-bind-dialog';
|
import eventBindDialog from '@ali/lowcode-plugin-event-bind-dialog';
|
||||||
import variableBindDialog from '@ali/lowcode-plugin-variable-bind-dialog';
|
import variableBindDialog from '@ali/lowcode-plugin-variable-bind-dialog';
|
||||||
@ -18,6 +19,7 @@ export default {
|
|||||||
undoRedo,
|
undoRedo,
|
||||||
componentsPane,
|
componentsPane,
|
||||||
outline,
|
outline,
|
||||||
|
utilsPane,
|
||||||
zhEn,
|
zhEn,
|
||||||
eventBindDialog,
|
eventBindDialog,
|
||||||
variableBindDialog,
|
variableBindDialog,
|
||||||
|
|||||||
@ -83,6 +83,26 @@ export default {
|
|||||||
},
|
},
|
||||||
pluginProps: {},
|
pluginProps: {},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
pluginKey: 'utilsPane',
|
||||||
|
type: 'PanelIcon',
|
||||||
|
props: {
|
||||||
|
align: 'top',
|
||||||
|
icon: 'util',
|
||||||
|
description: '工具类',
|
||||||
|
panelProps: {
|
||||||
|
floatable: true,
|
||||||
|
height: 300,
|
||||||
|
help: undefined,
|
||||||
|
hideTitleBar: false,
|
||||||
|
maxHeight: 800,
|
||||||
|
maxWidth: 1200,
|
||||||
|
title: '工具类扩展面板',
|
||||||
|
width: 430,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pluginProps: {},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
pluginKey: 'sourceEditor',
|
pluginKey: 'sourceEditor',
|
||||||
type: 'PanelIcon',
|
type: 'PanelIcon',
|
||||||
@ -183,14 +203,14 @@ export default {
|
|||||||
'https://dev.g.alicdn.com/ali-lowcode/ali-lowcode-engine/1.0.0/react-simulator-renderer.css',
|
'https://dev.g.alicdn.com/ali-lowcode/ali-lowcode-engine/1.0.0/react-simulator-renderer.css',
|
||||||
//'https://dev.g.alicdn.com/ali-lowcode/ali-lowcode-engine/1.0.0/react-simulator-renderer.js',
|
//'https://dev.g.alicdn.com/ali-lowcode/ali-lowcode-engine/1.0.0/react-simulator-renderer.js',
|
||||||
// for debug
|
// for debug
|
||||||
'http://localhost:3333/js/react-simulator-renderer.js',
|
'http://localhost:3333/js/react-simulator-renderer.js',
|
||||||
// 'http://localhost:3333/js/react-simulator-renderer.css',
|
// 'http://localhost:3333/js/react-simulator-renderer.css',
|
||||||
];
|
];
|
||||||
editor.set('simulatorUrl', simulatorUrl);
|
editor.set('simulatorUrl', simulatorUrl);
|
||||||
editor.set('requestHandlersMap', {
|
editor.set('requestHandlersMap', {
|
||||||
mtop: createMtopHandler(),
|
mtop: createMtopHandler(),
|
||||||
fetch: createFetchHandler(),
|
fetch: createFetchHandler(),
|
||||||
jsonp: createJsonpHandler()
|
jsonp: createJsonpHandler(),
|
||||||
});
|
});
|
||||||
// editor.set('renderEnv', 'rax');
|
// editor.set('renderEnv', 'rax');
|
||||||
|
|
||||||
|
|||||||
@ -104,7 +104,9 @@ export class Project {
|
|||||||
| string,
|
| string,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
value: any,
|
value: any,
|
||||||
): void {}
|
): void {
|
||||||
|
Object.assign(this.data, { [key]: value });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分字段设置储存数据
|
* 分字段设置储存数据
|
||||||
@ -121,7 +123,9 @@ export class Project {
|
|||||||
| 'css'
|
| 'css'
|
||||||
| 'dataSource'
|
| 'dataSource'
|
||||||
| string,
|
| string,
|
||||||
): any {}
|
): any {
|
||||||
|
return Reflect.get(this.data, key);
|
||||||
|
}
|
||||||
|
|
||||||
open(doc?: string | DocumentModel | RootSchema): DocumentModel {
|
open(doc?: string | DocumentModel | RootSchema): DocumentModel {
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
@ -152,7 +156,9 @@ export class Project {
|
|||||||
if (isDocumentModel(doc)) {
|
if (isDocumentModel(doc)) {
|
||||||
return doc.open();
|
return doc.open();
|
||||||
} else if (isPageSchema(doc)) {
|
} else if (isPageSchema(doc)) {
|
||||||
const foundDoc = this.documents.find(curDoc => curDoc?.rootNode?.id && curDoc?.rootNode?.id === doc?.id);
|
const foundDoc = this.documents.find(
|
||||||
|
(curDoc) => curDoc?.rootNode?.id && curDoc?.rootNode?.id === doc?.id,
|
||||||
|
);
|
||||||
if (foundDoc) {
|
if (foundDoc) {
|
||||||
foundDoc.remove();
|
foundDoc.remove();
|
||||||
}
|
}
|
||||||
|
|||||||
1
packages/plugin-utils-pane/CHANGELOG.md
Normal file
1
packages/plugin-utils-pane/CHANGELOG.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
# Change Log
|
||||||
3
packages/plugin-utils-pane/README.md
Normal file
3
packages/plugin-utils-pane/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
## 关于 @ali/lowcode-plugin-utils-pane
|
||||||
|
|
||||||
|
这是低代码引擎的 utils 面板。
|
||||||
9
packages/plugin-utils-pane/build.json
Normal file
9
packages/plugin-utils-pane/build.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"build-plugin-component",
|
||||||
|
"build-plugin-fusion",
|
||||||
|
["build-plugin-moment-locales", {
|
||||||
|
"locales": ["zh-cn"]
|
||||||
|
}]
|
||||||
|
]
|
||||||
|
}
|
||||||
43
packages/plugin-utils-pane/package.json
Normal file
43
packages/plugin-utils-pane/package.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "@ali/lowcode-plugin-utils-pane",
|
||||||
|
"version": "1.0.8-0",
|
||||||
|
"description": "alibaba lowcode editor utils pane plugin",
|
||||||
|
"files": [
|
||||||
|
"es/",
|
||||||
|
"lib/"
|
||||||
|
],
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"module": "es/index.js",
|
||||||
|
"stylePath": "style.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "build-scripts build --skip-demo",
|
||||||
|
"test": "ava",
|
||||||
|
"test:snapshot": "ava --update-snapshots"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"lowcode",
|
||||||
|
"editor"
|
||||||
|
],
|
||||||
|
"author": "changyun.pcy",
|
||||||
|
"dependencies": {
|
||||||
|
"@alifd/next": "1.x"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@ali/lowcode-editor-core": "^1.0.8-0",
|
||||||
|
"prop-types": "^15.5.8",
|
||||||
|
"react": "^16.8.1",
|
||||||
|
"react-dom": "^16.8.1",
|
||||||
|
"react-router-dom": "^5.1.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@alib/build-scripts": "^0.1.3",
|
||||||
|
"@types/react": "^16.9.13",
|
||||||
|
"@types/react-dom": "^16.9.4",
|
||||||
|
"build-plugin-component": "^0.2.7-1",
|
||||||
|
"build-plugin-fusion": "^0.1.0",
|
||||||
|
"build-plugin-moment-locales": "^0.1.0"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"registry": "https://registry.npm.alibaba-inc.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
packages/plugin-utils-pane/src/form-components/index.ts
Normal file
1
packages/plugin-utils-pane/src/form-components/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './js-function';
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import { connect } from '@formily/react-schema-renderer';
|
||||||
|
import MonacoEditor, { EditorWillMount } from 'react-monaco-editor';
|
||||||
|
import noop from 'lodash/noop';
|
||||||
|
|
||||||
|
export interface JSFunctionProps {
|
||||||
|
className: string;
|
||||||
|
value: string;
|
||||||
|
onChange?: (val: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Arg0TypeOf<T> = T extends (arg0: infer U) => any ? U : never;
|
||||||
|
type MonacoRef = Arg0TypeOf<EditorWillMount>;
|
||||||
|
|
||||||
|
class InternalJSFunction extends PureComponent<JSFunctionProps, unknown> {
|
||||||
|
static isFieldComponent = true;
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
onChange: noop,
|
||||||
|
};
|
||||||
|
|
||||||
|
private monacoRef: MonacoRef | null = null;
|
||||||
|
|
||||||
|
private handleEditorChange = () => {
|
||||||
|
if (
|
||||||
|
this.monacoRef &&
|
||||||
|
this.monacoRef.editor &&
|
||||||
|
!this.monacoRef.editor.getModelMarkers({}).find((marker) => marker.owner === 'json') &&
|
||||||
|
this.props.onChange
|
||||||
|
) {
|
||||||
|
this.props.onChange(this.monacoRef.editor.getModels()?.[0]?.getValue());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleEditorWillMount: EditorWillMount = (monaco) => {
|
||||||
|
this.monacoRef = monaco;
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { value } = this.props;
|
||||||
|
return (
|
||||||
|
<MonacoEditor
|
||||||
|
theme="vs-dark"
|
||||||
|
width={400}
|
||||||
|
height={150}
|
||||||
|
defaultValue={value}
|
||||||
|
language="js"
|
||||||
|
onChange={this.handleEditorChange}
|
||||||
|
editorWillMount={this.handleEditorWillMount}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const JSFunction = connect()(InternalJSFunction);
|
||||||
116
packages/plugin-utils-pane/src/index.scss
Normal file
116
packages/plugin-utils-pane/src/index.scss
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
.lowcode-plugin-utils-pane {
|
||||||
|
margin: 0 8px;
|
||||||
|
> .next-tabs {
|
||||||
|
> .next-tabs-bar {
|
||||||
|
.next-tabs-nav-extra {
|
||||||
|
.next-btn {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lowcode-plugin-utils-pane-list {
|
||||||
|
margin: 8px;
|
||||||
|
.next-search {
|
||||||
|
width: 100%;
|
||||||
|
.next-search-left {
|
||||||
|
height: 28px !important;
|
||||||
|
border-top-left-radius: 3px;
|
||||||
|
border-bottom-left-radius: 3px;
|
||||||
|
.next-before {
|
||||||
|
height: 28px !important;
|
||||||
|
.next-select {
|
||||||
|
height: 28px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.next-search-input {
|
||||||
|
height: 28px !important;
|
||||||
|
input {
|
||||||
|
height: 28px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.next-input {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.next-after {
|
||||||
|
// height: 28px !important;
|
||||||
|
.next-btn {
|
||||||
|
height: 30px !important;
|
||||||
|
border-top-right-radius: 3px;
|
||||||
|
border-bottom-right-radius: 3px;
|
||||||
|
.next-icon:before {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.utils-list {
|
||||||
|
margin-top: 8px;
|
||||||
|
height: unquote('calc(100vh - 48px - 48px - 42px - 28px - 8px - 8px)');
|
||||||
|
overflow: auto;
|
||||||
|
.next-virtual-list-wrapper > div > ul > li {
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
&:first-child {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.utils-item {
|
||||||
|
margin: 8px;
|
||||||
|
.utils-item-title {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
height: 28px;
|
||||||
|
.next-btn {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.utils-item-name-wrap {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.utils-item-from {
|
||||||
|
color: rgba(0, 0, 0, 0.65);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.utils-item-import-from {
|
||||||
|
color: rgba(0, 0, 0, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
.utils-item-desc {
|
||||||
|
.next-tag {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lowcode-plugin-utils-form {
|
||||||
|
height: unquote('calc(100vh - 48px - 48px - 42px)');
|
||||||
|
overflow: auto;
|
||||||
|
.next-form-item {
|
||||||
|
.next-form-item-control {
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.utils-item-detail-func-expr {
|
||||||
|
pre {
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
86
packages/plugin-utils-pane/src/index.tsx
Normal file
86
packages/plugin-utils-pane/src/index.tsx
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import { PluginProps, UtilItem, UtilsMap } from '@ali/lowcode-types';
|
||||||
|
import get from 'lodash/get';
|
||||||
|
import { UtilsPane, UtilTypeInfo } from './pane';
|
||||||
|
|
||||||
|
import './index.scss';
|
||||||
|
import { DEFAULT_UTILS_TYPES } from './utils-types';
|
||||||
|
import { DEFAULT_UTILS } from './utils-defaults';
|
||||||
|
|
||||||
|
const PLUGIN_NAME = 'utilsPane';
|
||||||
|
|
||||||
|
export interface UtilsPaneProps extends PluginProps {
|
||||||
|
/**
|
||||||
|
* 支持的 Util 的类型
|
||||||
|
*/
|
||||||
|
utilsTypes: UtilTypeInfo[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始的 Utils (若 schema 中尚未定义 utils)
|
||||||
|
*/
|
||||||
|
initialUtils?: UtilItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
active: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class UtilsPanePlugin extends PureComponent<UtilsPaneProps, State> {
|
||||||
|
static displayName = 'UtilsPanePlugin';
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
initialUtils: DEFAULT_UTILS,
|
||||||
|
};
|
||||||
|
|
||||||
|
state = {
|
||||||
|
active: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props: UtilsPaneProps) {
|
||||||
|
super(props);
|
||||||
|
this.state.active = true;
|
||||||
|
|
||||||
|
const { editor } = this.props;
|
||||||
|
|
||||||
|
// @todo pluginName, to unsubscribe
|
||||||
|
// 第一次 active 事件不会触发监听器
|
||||||
|
editor.on('skeleton.panel-dock.active', (pluginName) => {
|
||||||
|
if (pluginName === PLUGIN_NAME) {
|
||||||
|
this.setState({ active: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.on('skeleton.panel-dock.unactive', (pluginName) => {
|
||||||
|
if (pluginName === PLUGIN_NAME) {
|
||||||
|
this.setState({ active: false });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { initialUtils = DEFAULT_UTILS, utilsTypes = DEFAULT_UTILS_TYPES, editor } = this.props;
|
||||||
|
const { active } = this.state;
|
||||||
|
|
||||||
|
if (!active) return null;
|
||||||
|
|
||||||
|
const projectSchema = editor.get('designer').project.getSchema() ?? {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<UtilsPane
|
||||||
|
initialUtils={initialUtils}
|
||||||
|
utilTypes={utilsTypes}
|
||||||
|
schema={get(projectSchema, 'utils')}
|
||||||
|
onSchemaChange={this.handleSchemaChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleSchemaChange = (utilsMap: UtilsMap) => {
|
||||||
|
const { editor } = this.props;
|
||||||
|
|
||||||
|
// @TODO 姿势是否最优?
|
||||||
|
if (editor.get('designer')) {
|
||||||
|
editor.get('designer').project.set('utils', utilsMap);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
191
packages/plugin-utils-pane/src/list.tsx
Normal file
191
packages/plugin-utils-pane/src/list.tsx
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
import { UtilItem } from '@ali/lowcode-types';
|
||||||
|
import { Balloon, Button, Search, Table, Tag, VirtualList } from '@alifd/next';
|
||||||
|
import tap from 'lodash/tap';
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
|
import type { UtilTypeInfo } from './pane';
|
||||||
|
|
||||||
|
const { Column: TableCol } = Table;
|
||||||
|
|
||||||
|
export interface UtilsListProps {
|
||||||
|
utilTypes: UtilTypeInfo[];
|
||||||
|
utilItems: UtilItem[] | null | undefined;
|
||||||
|
onEditUtil?: (utilName: string) => void;
|
||||||
|
onDuplicateUtil?: (utilName: string) => void;
|
||||||
|
onRemoveUtil?: (utilName: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
filteredType: string;
|
||||||
|
keyword: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type TableRow = {
|
||||||
|
label: string;
|
||||||
|
value: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class UtilList extends PureComponent<UtilsListProps, State> {
|
||||||
|
state = {
|
||||||
|
filteredType: '',
|
||||||
|
keyword: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleSearchFilterChange = (filteredType: any) => {
|
||||||
|
this.setState({ filteredType });
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleSearch = (keyword: any) => {
|
||||||
|
this.setState({ keyword });
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleEditUtilItem = (id: any) => {
|
||||||
|
if (this.props.onEditUtil) {
|
||||||
|
this.props.onEditUtil(id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleDuplicateUtilItem = (id: any) => {
|
||||||
|
if (this.props.onDuplicateUtil) {
|
||||||
|
this.props.onDuplicateUtil(id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleRemoveDataSource = (id: any) => {
|
||||||
|
if (this.props.onRemoveUtil) {
|
||||||
|
this.props.onRemoveUtil(id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private renderVirtualUtilsList = () => {
|
||||||
|
const { filteredType, keyword } = this.state;
|
||||||
|
const utilsMap = this.props.utilItems || [];
|
||||||
|
const { utilTypes } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
utilsMap
|
||||||
|
.filter((item) => !filteredType || item.type === filteredType)
|
||||||
|
.filter((item) => !keyword || item.name.indexOf(keyword) >= 0)
|
||||||
|
.map((item) => (
|
||||||
|
<li key={item.name}>
|
||||||
|
<div className="utils-item">
|
||||||
|
<div className="utils-item-title">
|
||||||
|
<div className="utils-item-name-wrap" title={item.name}>
|
||||||
|
<span className="utils-item-name">{item.name}</span>
|
||||||
|
|
||||||
|
{(item.type === 'npm' || item.type === 'tnpm') && (
|
||||||
|
<span className="utils-item-from">
|
||||||
|
{' '}
|
||||||
|
<span className="utils-item-import-from">源自</span>{' '}
|
||||||
|
<span className="utils-item-package-name">
|
||||||
|
"{item.content?.package}
|
||||||
|
{item.content?.main ? `/${item.content?.main}` : ''}"
|
||||||
|
</span>
|
||||||
|
<span className="utils-item-export-name">
|
||||||
|
{item.content?.exportName && item.content?.exportName !== item.name
|
||||||
|
? `中的 ${item.content?.exportName}${
|
||||||
|
item.content?.subName ? `.${item.content?.subName}` : ''
|
||||||
|
}`
|
||||||
|
: ''}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{!!utilTypes.some((t) => t.type === item.type) &&
|
||||||
|
this.renderItemDetailBalloon(item)}
|
||||||
|
{!!utilTypes.some((t) => t.type === item.type) && (
|
||||||
|
<Button size="small" onClick={this.handleEditUtilItem.bind(this, item.name)}>
|
||||||
|
编辑
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{!!utilTypes.some((t) => t.type === item.type) && (
|
||||||
|
<Button size="small" onClick={this.handleDuplicateUtilItem.bind(this, item.name)}>
|
||||||
|
复制
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button size="small" onClick={this.handleRemoveDataSource.bind(this, item.name)}>
|
||||||
|
删除
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="utils-item-desc">
|
||||||
|
<Tag size="small">{item.type}</Tag>
|
||||||
|
{(item.type === 'npm' || item.type === 'tnpm') && item.content?.destructuring && (
|
||||||
|
<Tag size="small">解构</Tag>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)) || []
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
private renderItemDetailBalloon(item: UtilItem): React.ReactNode {
|
||||||
|
return (
|
||||||
|
<Balloon
|
||||||
|
trigger={<Button size="small">详情</Button>}
|
||||||
|
align="b"
|
||||||
|
alignEdge
|
||||||
|
triggerType="hover"
|
||||||
|
style={{ width: 300 }}
|
||||||
|
>
|
||||||
|
{item.type === 'function' ? (
|
||||||
|
<div className="utils-item-detail-func-expr">
|
||||||
|
<pre>
|
||||||
|
<code>{item.content?.value || ''}</code>
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Table
|
||||||
|
dataSource={tap(
|
||||||
|
Object.entries({
|
||||||
|
type: item.type,
|
||||||
|
...item.content,
|
||||||
|
}).map<TableRow>(([key, value]) => ({
|
||||||
|
label: key,
|
||||||
|
value: `${value}`,
|
||||||
|
})),
|
||||||
|
console.log,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<TableCol title="" dataIndex="label" />
|
||||||
|
<TableCol
|
||||||
|
title=""
|
||||||
|
dataIndex="value"
|
||||||
|
cell={(val: any) => <div>{typeof val === 'string' ? `"${val}"` : `${val}`}</div>}
|
||||||
|
/>
|
||||||
|
</Table>
|
||||||
|
)}
|
||||||
|
</Balloon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { utilTypes } = this.props;
|
||||||
|
const { filteredType } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="lowcode-plugin-utils-pane-list">
|
||||||
|
<Search
|
||||||
|
hasClear
|
||||||
|
onSearch={this.handleSearch}
|
||||||
|
filterProps={{}}
|
||||||
|
defaultFilterValue={filteredType}
|
||||||
|
filter={[
|
||||||
|
{
|
||||||
|
label: '全部',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
...(utilTypes || []).map((utilType) => ({
|
||||||
|
label: utilType.label,
|
||||||
|
value: utilType.type,
|
||||||
|
})),
|
||||||
|
]}
|
||||||
|
onFilterChange={this.handleSearchFilterChange}
|
||||||
|
/>
|
||||||
|
<div className="utils-list">
|
||||||
|
<VirtualList>{this.renderVirtualUtilsList()}</VirtualList>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
packages/plugin-utils-pane/src/locale/en-US.json
Normal file
3
packages/plugin-utils-pane/src/locale/en-US.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"UtilsPane": "Utils Pane"
|
||||||
|
}
|
||||||
10
packages/plugin-utils-pane/src/locale/index.ts
Normal file
10
packages/plugin-utils-pane/src/locale/index.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { createIntl } from '@ali/lowcode-editor-core';
|
||||||
|
import enUS from './en-US.json';
|
||||||
|
import zhCN from './zh-CN.json';
|
||||||
|
|
||||||
|
const { intl, intlNode, getLocale, setLocale } = createIntl({
|
||||||
|
'en-US': enUS,
|
||||||
|
'zh-CN': zhCN,
|
||||||
|
});
|
||||||
|
|
||||||
|
export { intl, intlNode, getLocale, setLocale };
|
||||||
3
packages/plugin-utils-pane/src/locale/zh-CN.json
Normal file
3
packages/plugin-utils-pane/src/locale/zh-CN.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"UtilsPane": "工具类扩展面板"
|
||||||
|
}
|
||||||
308
packages/plugin-utils-pane/src/pane.tsx
Normal file
308
packages/plugin-utils-pane/src/pane.tsx
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
/**
|
||||||
|
* 面板,先通过 Dialog 呈现
|
||||||
|
*/
|
||||||
|
import { UtilItem, UtilsMap } from '@ali/lowcode-types';
|
||||||
|
import { Button, Dialog, MenuButton, Message, Tab } from '@alifd/next';
|
||||||
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
|
import get from 'lodash/get';
|
||||||
|
import isArray from 'lodash/isArray';
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
|
import { UtilList } from './list';
|
||||||
|
import { UtilsForm } from './utils-form';
|
||||||
|
|
||||||
|
const { Item: TabItem } = Tab;
|
||||||
|
const { Item: MenuButtonItem } = MenuButton;
|
||||||
|
|
||||||
|
enum PaneTabKey {
|
||||||
|
List = 'list',
|
||||||
|
Create = 'create',
|
||||||
|
Edit = 'edit',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UtilTypeInfo = {
|
||||||
|
type: UtilItem['type'];
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface UtilsPaneProps {
|
||||||
|
initialUtils?: UtilsMap | null;
|
||||||
|
schema?: UtilsMap | null;
|
||||||
|
utilTypes: UtilTypeInfo[];
|
||||||
|
onSchemaChange?: (schema: UtilsMap) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TabItem {
|
||||||
|
key: PaneTabKey;
|
||||||
|
title: string;
|
||||||
|
closeable: boolean;
|
||||||
|
data?: Partial<UtilItem>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
utilItems: UtilsMap;
|
||||||
|
tabItems: TabItem[];
|
||||||
|
activeTabKey: PaneTabKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UtilsPane extends PureComponent<UtilsPaneProps, State> {
|
||||||
|
state: State = {
|
||||||
|
utilItems: this.props.schema || this.props.initialUtils || [],
|
||||||
|
tabItems: [
|
||||||
|
{
|
||||||
|
key: PaneTabKey.List,
|
||||||
|
title: '工具类扩展列表',
|
||||||
|
closeable: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
activeTabKey: PaneTabKey.List,
|
||||||
|
};
|
||||||
|
|
||||||
|
private notifyItemsChanged = () => {
|
||||||
|
this.setState({}, () => {
|
||||||
|
if (this.props.onSchemaChange) {
|
||||||
|
this.props.onSchemaChange(this.state.utilItems);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleCreateItem = (newItem: UtilItem) => {
|
||||||
|
const doSaveNewItem = () => {
|
||||||
|
this.closeTab(PaneTabKey.Create);
|
||||||
|
|
||||||
|
this.setState(({ utilItems }) => ({
|
||||||
|
utilItems: [{ ...newItem }, ...utilItems.filter((x) => x.name !== newItem.name)],
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.notifyItemsChanged();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.state.utilItems.some((util) => util.name === newItem.name)) {
|
||||||
|
Dialog.confirm({
|
||||||
|
content: `工具类扩展 "${newItem.name}" 已存在,如果导入会替换已存在的扩展,是否继续?`,
|
||||||
|
onOk: () => {
|
||||||
|
doSaveNewItem();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
doSaveNewItem();
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleUpdateItem = (changedItem: UtilItem) => {
|
||||||
|
this.closeTab(PaneTabKey.Edit);
|
||||||
|
this.setState(({ utilItems }) => ({
|
||||||
|
utilItems: utilItems.map((x) => [x.name === changedItem.name ? changedItem : x][0]),
|
||||||
|
}));
|
||||||
|
this.notifyItemsChanged();
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleRemoveItem = (toBeRemovedUtilName: string) => {
|
||||||
|
const doRemove = () => {
|
||||||
|
this.setState(
|
||||||
|
({ utilItems }) => ({
|
||||||
|
utilItems: utilItems.filter((item) => item.name !== toBeRemovedUtilName),
|
||||||
|
}),
|
||||||
|
() => {
|
||||||
|
this.notifyItemsChanged();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Dialog.confirm({
|
||||||
|
content: '确定要删除吗?',
|
||||||
|
onOk: () => {
|
||||||
|
doRemove();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleDuplicateItem = (utilName: string) => {
|
||||||
|
const targetUtil = this.state.utilItems.find((item) => item.name === utilName);
|
||||||
|
if (!targetUtil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.openCreateItemTab({
|
||||||
|
...cloneDeep(targetUtil),
|
||||||
|
name: `${targetUtil.name}Copy`,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleEditItem = (utilName: string) => {
|
||||||
|
const targetUtil = this.state.utilItems.find((item) => item.name === utilName);
|
||||||
|
if (!targetUtil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.openEditDataSourceTab(cloneDeep(targetUtil));
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleTabChange = (activeTabKey: string | number) => {
|
||||||
|
if (isValidTabKey(activeTabKey)) {
|
||||||
|
this.setState({ activeTabKey });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private openCreateItemTab = (initialItem: Partial<UtilItem>) => {
|
||||||
|
const { tabItems } = this.state;
|
||||||
|
|
||||||
|
if (!tabItems.find((item) => item.key === PaneTabKey.Create)) {
|
||||||
|
this.setState(({ tabItems: latestTabItems }) => ({
|
||||||
|
tabItems: latestTabItems.concat({
|
||||||
|
key: PaneTabKey.Create,
|
||||||
|
title: '添加工具类扩展',
|
||||||
|
closeable: true,
|
||||||
|
data: {
|
||||||
|
...initialItem,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
this.setState({ activeTabKey: PaneTabKey.Create });
|
||||||
|
} else {
|
||||||
|
Message.notice('当前已经有一个添加工具类扩展的标签页了');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleCreateItemBtnClick = (dataSourceType: string) => {
|
||||||
|
this.openCreateItemTab({
|
||||||
|
type: dataSourceType as UtilItem['type'],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleCreateItemMenuBtnClick = (dataSourceType: string) => {
|
||||||
|
this.openCreateItemTab({
|
||||||
|
type: dataSourceType as UtilItem['type'],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private openEditDataSourceTab = (utilItem: UtilItem) => {
|
||||||
|
const { tabItems } = this.state;
|
||||||
|
|
||||||
|
if (!tabItems.find((item) => item.key === PaneTabKey.Edit)) {
|
||||||
|
this.setState(({ tabItems: latestTabItems }) => ({
|
||||||
|
tabItems: latestTabItems.concat({
|
||||||
|
key: PaneTabKey.Edit,
|
||||||
|
title: '修改工具类扩展',
|
||||||
|
closeable: true,
|
||||||
|
data: {
|
||||||
|
...utilItem,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ activeTabKey: PaneTabKey.Edit });
|
||||||
|
};
|
||||||
|
|
||||||
|
private closeTab = (tabKey: any) => {
|
||||||
|
this.setState(
|
||||||
|
({ tabItems }) => ({
|
||||||
|
tabItems: tabItems.filter((item) => item.key !== tabKey),
|
||||||
|
}),
|
||||||
|
() => {
|
||||||
|
this.setState(({ tabItems }) => ({
|
||||||
|
activeTabKey: get(tabItems, '[0].key'),
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
renderTabExtraContent = () => {
|
||||||
|
const { utilTypes } = this.props;
|
||||||
|
|
||||||
|
if (isArray(utilTypes)) {
|
||||||
|
if (utilTypes.length > 1) {
|
||||||
|
return [
|
||||||
|
<MenuButton label="添加" onItemClick={this.handleCreateItemMenuBtnClick}>
|
||||||
|
{utilTypes.map((type) => (
|
||||||
|
<MenuButtonItem key={type.type}>{type.label}</MenuButtonItem>
|
||||||
|
))}
|
||||||
|
</MenuButton>,
|
||||||
|
];
|
||||||
|
} else if (utilTypes.length === 1) {
|
||||||
|
return [
|
||||||
|
<Button onClick={this.handleCreateItemBtnClick.bind(this, utilTypes[0].type)}>
|
||||||
|
添加
|
||||||
|
</Button>,
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更通用的处理
|
||||||
|
private renderTabItemContent = (
|
||||||
|
tabItemKey: PaneTabKey,
|
||||||
|
data: Partial<UtilItem> | undefined | null,
|
||||||
|
) => {
|
||||||
|
const { utilItems } = this.state;
|
||||||
|
const { utilTypes = [] } = this.props;
|
||||||
|
|
||||||
|
if (tabItemKey === PaneTabKey.List) {
|
||||||
|
if (utilItems.length <= 0) {
|
||||||
|
return (
|
||||||
|
<Message type="help" title="暂无工具类扩展">
|
||||||
|
您可以点击右上角的【添加】按钮来添加一个。
|
||||||
|
</Message>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<UtilList
|
||||||
|
utilTypes={utilTypes}
|
||||||
|
utilItems={utilItems}
|
||||||
|
onEditUtil={this.handleEditItem}
|
||||||
|
onDuplicateUtil={this.handleDuplicateItem}
|
||||||
|
onRemoveUtil={this.handleRemoveItem}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else if (tabItemKey === PaneTabKey.Edit) {
|
||||||
|
return (
|
||||||
|
<UtilsForm
|
||||||
|
item={data}
|
||||||
|
onComplete={this.handleUpdateItem}
|
||||||
|
onCancel={this.closeTab.bind(this, PaneTabKey.Edit)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else if (tabItemKey === PaneTabKey.Create) {
|
||||||
|
return (
|
||||||
|
<UtilsForm
|
||||||
|
item={data}
|
||||||
|
onComplete={this.handleCreateItem}
|
||||||
|
onCancel={this.closeTab.bind(this, tabItemKey)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.warn('Unknown tab type: ', tabItemKey);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { activeTabKey, tabItems } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="lowcode-plugin-datasource-pane">
|
||||||
|
<Tab
|
||||||
|
activeKey={activeTabKey}
|
||||||
|
extra={this.renderTabExtraContent()}
|
||||||
|
onChange={this.handleTabChange}
|
||||||
|
onClose={this.closeTab}
|
||||||
|
>
|
||||||
|
{tabItems.map((item: TabItem) => (
|
||||||
|
<TabItem {...item}>{this.renderTabItemContent(item.key, item.data)}</TabItem>
|
||||||
|
))}
|
||||||
|
</Tab>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidTabKey(tabKey: unknown): tabKey is PaneTabKey {
|
||||||
|
return typeof tabKey === 'string' && (Object.values(PaneTabKey) as string[]).includes(tabKey);
|
||||||
|
}
|
||||||
31
packages/plugin-utils-pane/src/utils-defaults.tsx
Normal file
31
packages/plugin-utils-pane/src/utils-defaults.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { UtilItem } from '@ali/lowcode-types';
|
||||||
|
|
||||||
|
export const DEFAULT_UTILS: UtilItem[] = [
|
||||||
|
{
|
||||||
|
type: 'npm',
|
||||||
|
name: 'clone',
|
||||||
|
content: {
|
||||||
|
package: 'lodash',
|
||||||
|
destructuring: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'npm',
|
||||||
|
name: 'moment',
|
||||||
|
content: {
|
||||||
|
package: 'moment',
|
||||||
|
destructuring: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'function',
|
||||||
|
name: 'record',
|
||||||
|
content: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: `function(logkey, gmkey, gokey, reqMethod) {
|
||||||
|
goldlog.record('/demo.event.' + logkey, gmkey, gokey, reqMethod);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
247
packages/plugin-utils-pane/src/utils-form.tsx
Normal file
247
packages/plugin-utils-pane/src/utils-form.tsx
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
// @todo schema default
|
||||||
|
import { UtilItem } from '@ali/lowcode-types';
|
||||||
|
import { Button } from '@alifd/next';
|
||||||
|
import { FormButtonGroup, registerValidationFormats, SchemaForm, Submit } from '@formily/next';
|
||||||
|
import { ArrayTable, Input, NumberPicker, Switch } from '@formily/next-components';
|
||||||
|
import memorize from 'lodash/memoize';
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
|
import { JSFunction } from './form-components';
|
||||||
|
|
||||||
|
registerValidationFormats({
|
||||||
|
util_npm_version_format: /^\d+\.\d+\.\d+(-[a-z0-9-]+(\.[a-z0-9]+))?$/i,
|
||||||
|
util_name_js_identifier: /[a-z$_][a-z$_0-9]+/i,
|
||||||
|
});
|
||||||
|
|
||||||
|
type FlatUtilItem = {
|
||||||
|
name: string;
|
||||||
|
type: UtilItem['type'];
|
||||||
|
|
||||||
|
// NPM/TNPM util:
|
||||||
|
componentName?: string;
|
||||||
|
package?: string;
|
||||||
|
version?: string;
|
||||||
|
destructuring?: boolean;
|
||||||
|
exportName?: string;
|
||||||
|
subName?: string;
|
||||||
|
main?: string;
|
||||||
|
|
||||||
|
// function util
|
||||||
|
functionExpr?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const FORM_SCHEMA_NPM = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
type: TYPE_FIELD(),
|
||||||
|
name: NAME_FIELD(),
|
||||||
|
componentName: {
|
||||||
|
type: 'string',
|
||||||
|
title: 'componentName',
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
package: {
|
||||||
|
type: 'string',
|
||||||
|
title: '包名',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
version: {
|
||||||
|
type: 'string',
|
||||||
|
title: '版本号',
|
||||||
|
required: false,
|
||||||
|
'x-rules': {
|
||||||
|
format: 'util_npm_version_format',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
destructuring: {
|
||||||
|
type: 'boolean',
|
||||||
|
title: '需解构',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
exportName: {
|
||||||
|
type: 'string',
|
||||||
|
title: '导出名',
|
||||||
|
// hide: '{{!destructuring}}', // TODO: 这联动一直报错
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
subName: {
|
||||||
|
type: 'string',
|
||||||
|
title: '子导出名',
|
||||||
|
// hide: '{{!destructuring}}',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
main: {
|
||||||
|
type: 'string',
|
||||||
|
title: '入口文件',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const FORM_SCHEMA_FUNCTION = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
type: TYPE_FIELD(),
|
||||||
|
name: NAME_FIELD(),
|
||||||
|
functionExpr: {
|
||||||
|
type: 'string',
|
||||||
|
title: '函数定义',
|
||||||
|
required: true,
|
||||||
|
'x-component': 'JSFunction',
|
||||||
|
'x-component-props': {
|
||||||
|
defaultValue: `/**
|
||||||
|
* 这里是一个 util 函数的示例
|
||||||
|
* 在工具类扩展中,可以通过 this.xxx 来访问各种上下文 API
|
||||||
|
**/
|
||||||
|
function () {
|
||||||
|
console.log("Hello world! (Context: %o)", this);
|
||||||
|
// TODO: 完善这个 util 函数
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface UtilsFormProps {
|
||||||
|
item?: Partial<UtilItem> | null;
|
||||||
|
onComplete?: (item: UtilItem) => void;
|
||||||
|
onCancel?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过是否存在 ID 来决定读写状态
|
||||||
|
*/
|
||||||
|
export class UtilsForm extends PureComponent<UtilsFormProps, unknown> {
|
||||||
|
state = {};
|
||||||
|
|
||||||
|
private handleFormSubmit = (formData: any) => {
|
||||||
|
const utilItem = parseFlatUtilItem(formData);
|
||||||
|
|
||||||
|
if (this.props.onComplete) {
|
||||||
|
this.props.onComplete(utilItem);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleCancel = () => {
|
||||||
|
if (this.props.onCancel) {
|
||||||
|
this.props.onCancel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private getInitialValues = memorize((utilItem: Partial<UtilItem> | undefined | null) => {
|
||||||
|
return flattenUtilItem(utilItem || {});
|
||||||
|
});
|
||||||
|
|
||||||
|
private readonly formComponents = {
|
||||||
|
string: Input,
|
||||||
|
boolean: Switch,
|
||||||
|
number: NumberPicker,
|
||||||
|
ArrayTable,
|
||||||
|
Input,
|
||||||
|
NumberPicker,
|
||||||
|
Switch,
|
||||||
|
JSFunction,
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly formLabelCol = {
|
||||||
|
span: 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly formWrapperCol = {
|
||||||
|
span: 16,
|
||||||
|
};
|
||||||
|
|
||||||
|
private get schema() {
|
||||||
|
return this.props.item?.type === 'function' ? FORM_SCHEMA_FUNCTION : FORM_SCHEMA_NPM;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { item } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="lowcode-plugin-datasource-form">
|
||||||
|
<SchemaForm
|
||||||
|
onSubmit={this.handleFormSubmit}
|
||||||
|
components={this.formComponents}
|
||||||
|
labelCol={this.formLabelCol}
|
||||||
|
wrapperCol={this.formWrapperCol}
|
||||||
|
schema={this.schema}
|
||||||
|
initialValues={this.getInitialValues(item)}
|
||||||
|
>
|
||||||
|
<FormButtonGroup offset={this.formLabelCol.span}>
|
||||||
|
<Submit>保存</Submit>
|
||||||
|
<Button onClick={this.handleCancel}>取消</Button>
|
||||||
|
</FormButtonGroup>
|
||||||
|
</SchemaForm>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function TYPE_FIELD() {
|
||||||
|
return {
|
||||||
|
title: '类型',
|
||||||
|
type: 'string',
|
||||||
|
editable: false,
|
||||||
|
'x-component': 'Input',
|
||||||
|
'x-component-props': {
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function NAME_FIELD() {
|
||||||
|
return {
|
||||||
|
type: 'string',
|
||||||
|
title: '引用名',
|
||||||
|
required: true,
|
||||||
|
'x-component-props': {
|
||||||
|
placeholder: '请输入引用名(工具类扩展在引用时的名称)',
|
||||||
|
autoFocus: true,
|
||||||
|
},
|
||||||
|
'x-rules': {
|
||||||
|
format: 'util_name_js_identifier',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function flattenUtilItem(utilItem: Partial<UtilItem>): FlatUtilItem {
|
||||||
|
return {
|
||||||
|
...(utilItem.type === 'function'
|
||||||
|
? {
|
||||||
|
functionExpr: utilItem.content?.value,
|
||||||
|
}
|
||||||
|
: utilItem.content),
|
||||||
|
|
||||||
|
name: utilItem.name || '',
|
||||||
|
type: utilItem.type || 'npm',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseFlatUtilItem(flatUtil: FlatUtilItem): UtilItem {
|
||||||
|
if (flatUtil.type === 'function') {
|
||||||
|
return {
|
||||||
|
name: flatUtil.name,
|
||||||
|
type: flatUtil.type,
|
||||||
|
content: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: flatUtil.functionExpr || '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: flatUtil.name,
|
||||||
|
type: flatUtil.type,
|
||||||
|
content: {
|
||||||
|
componentName: flatUtil.componentName || flatUtil.name,
|
||||||
|
package: flatUtil.package || '',
|
||||||
|
version: flatUtil.version,
|
||||||
|
destructuring: flatUtil.destructuring ?? false,
|
||||||
|
exportName: flatUtil.exportName,
|
||||||
|
subName: flatUtil.subName,
|
||||||
|
main: flatUtil.main,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
16
packages/plugin-utils-pane/src/utils-types.tsx
Normal file
16
packages/plugin-utils-pane/src/utils-types.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { UtilTypeInfo } from './pane';
|
||||||
|
|
||||||
|
export const DEFAULT_UTILS_TYPES: UtilTypeInfo[] = [
|
||||||
|
{
|
||||||
|
type: 'npm',
|
||||||
|
label: 'NPM 包',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tnpm',
|
||||||
|
label: 'TNPM 包',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'function',
|
||||||
|
label: '自定义函数',
|
||||||
|
},
|
||||||
|
];
|
||||||
10
packages/plugin-utils-pane/tsconfig.json
Normal file
10
packages/plugin-utils-pane/tsconfig.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "lib"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./src/"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
@ -54,5 +54,5 @@
|
|||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "http://registry.npm.alibaba-inc.com"
|
"registry": "http://registry.npm.alibaba-inc.com"
|
||||||
},
|
},
|
||||||
"homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-react-renderer@1.0.20/build/index.html"
|
"homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-react-renderer@1.0.21/build/index.html"
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user