likes
comments
collection
share

低代码在线拖拽公共组件的鬼点子,快来看看吧

作者站长头像
站长
· 阅读数 9

如果你在项目中用过阿里低代码引擎,或尝试在项目中使用,那么你或许就会有这样的想法:能不能像前端框架中封装的公共组件一样,拖拽配置出来一个组件公用?而不是每次需要新的公共组件的时候,都要按照官方文档,去重新新建一个项目,按照文档说明去配置去开发,最后再打包引入到原工程。

基于上面的场景,笔者通过一个鬼点子实现了该功能,当前实现的方案不支持出码模块。

演示地址:www.zzusp.asia (备) http://124.222.119.198/

代码仓库:github.com/gitmyname/o…

注:阅读本篇文章需要对低代码引擎有一定的了解,如:渲染、appHelper、低代码组件开发

先说下整体思路

  1. 在原项目中实现渲染模块的逻辑,传入页面的code可以渲染对应页面(其实就是根据code获取对应的schema,然后动态渲染)
  2. 将渲染模块作为一个渲染方法的返回值,通过appHelper传入引擎中
  3. 新建项目,开发一个用于接收动态渲染的低代码组件(渲染组件),并配置到引擎的组件库中
  4. 拖拽渲染组件,并在引擎源码面板中,将渲染方法渲染组件绑定即可

1. 渲染模块代码

<Renderer page={page} />
展开查看代码
import React, {useState, useEffect} from 'react';
import {Loading} from '@alifd/next';
import mergeWith from 'lodash/mergeWith';
import isArray from 'lodash/isArray';
import {buildComponents, assetBundle, AssetLevel, AssetLoader} from '@alilc/lowcode-utils';
import ReactRenderer from '@alilc/lowcode-react-renderer';
import {injectComponents} from '@alilc/lowcode-plugin-inject';
import appHelper from '../../appHelper';
import {
  getProjectSchemaFromDb,
  getPackagesFromAssets
} from '../../services/schemaService';

const Renderer = (props) => {

  const {page} = props;

  const [data, setData] = useState({});

  useEffect(() => {
    setData({});
    init();
  }, [page])

  async function init() {
    console.log('Renderer Page ' + page);

    const projectSchema = await getProjectSchemaFromDb(page);
    const packages = await getPackagesFromAssets();
    setData({});
    const {
      componentsMap: componentsMapArray,
      componentsTree,
      i18n,
      dataSource: projectDataSource,
    } = projectSchema;
    const componentsMap: any = {};
    componentsMapArray.forEach((component: any) => {
      componentsMap[component.componentName] = component;
    });
    const pageSchema = componentsTree[0];

    const libraryMap = {};
    const libraryAsset = [];
    packages.forEach(({package: _package, library, urls, renderUrls}) => {
      libraryMap[_package] = library;
      if (renderUrls) {
        libraryAsset.push(renderUrls);
      } else if (urls) {
        libraryAsset.push(urls);
      }
    });

    const vendors = [assetBundle(libraryAsset, AssetLevel.Library)];

    // TODO asset may cause pollution
    const assetLoader = new AssetLoader();
    await assetLoader.load(libraryAsset);
    const components = await injectComponents(buildComponents(libraryMap, componentsMap));

    setData({
      schema: pageSchema,
      components,
      i18n,
      projectDataSource,
    });
  }

  const {schema, components, i18n = {}, projectDataSource = {}} = data as any;

  if (!schema) {
    return <Loading fullScreen tip={<span style={{color: '#5584ff'}}>Loading...</span>}>
      <div className="lowcode-plugin-sample-preview" style={{ minHeight : '90vh' }}></div>
    </Loading>;
  }

  function customizer(objValue: [], srcValue: []) {
    if (isArray(objValue)) {
      return objValue.concat(srcValue || []);
    }
  }

  return (
    <div className="lowcode-plugin-sample-preview" style={{ minHeight : '90vh' }}>
      {!schema ? <Loading style={{ width : '100%', height : '90vh' }}/> : <ReactRenderer
        className="lowcode-plugin-sample-preview-content"
        style={{ height : '100%' }}
        schema={{
          ...schema,
          dataSource: mergeWith(schema.dataSource, projectDataSource, customizer),
        }}
        components={components}
        messages={i18n}
        appHelper={appHelper}
      />
      }
    </div>
  );

};

export default Renderer;

2. 通过appHelper将渲染传入引擎

appHelper.utils.renderer = (page) => {
  return <Renderer page={page} />
};

3. 实现低代码组件-渲染组件

# 新建一个低代码组件项目并启动
npm init @alilc/element your-element-name
cd your-element-name
npm install
npm start

渲染组件核心代码index.tsx

import * as React from 'react';

interface ComponentProps {
  name: string,
  element: any,
  onRender: Function|undefined
}

export default class CustomRendererComponent extends React.Component<ComponentProps> {

  constructor(props) {
    super(props);
    this.state = {
      name: props.name,
      element: props.element,
      onRender: props.onRender
    };
    this.ref = React.createRef();
  }

  componentDidMount() {
    if (this.state.onRender !== undefined) {
      this.state.onRender(this.ref);
    }
  }

  render() {
    return (
      <div className="CustomRendererComponent">
        <div ref={this.ref}>
          {this.state.element || 'no element'}
        </div>
      </div>
    )
  }

}

引入到原项目

打包新建的低代码组件项目,通过本地拷贝也好,发布到npm仓库再install也好,将低代码组件的资源引入到原项目中。这部分阅读下官方文档,这里就不详细说明了。

然后修改原项目中的assets.json,将低代码组件配置到引擎中,现在在组件库面板

{
  "packages": [
    ...
  ],
  "components": [
    ...
    {
      "exportName": "PluginCustomRendererMeta",
      "npm": {
        "package": "plugin-custom-renderer",
        "version": "0.1.0"
      },
      "url": "./lowcode/meta.js",
      "urls": {
        "default": "./lowcode/meta.js"
      },
      "advancedUrls": {
        "default": [
          "./lowcode/meta.js"
        ]
      }
    }
  ],
  "sort": {
    "groupList": [
      "精选组件",
      "原子组件",
      "低代码组件"
    ],
    "categoryList": [
      "基础元素",
      "布局容器类",
      "表格类",
      "表单详情类",
      "帮助类",
      "对话框类",
      "业务类",
      "通用",
      "导航",
      "引导",
      "信息输入",
      "信息展示",
      "信息反馈",
      "常用"
    ]
  },
  "groupList": [
    "精选组件",
    "原子组件",
    "低代码组件"
  ],
  "ignoreComponents": {}
}

启动后就可以在组件面板中看到我们新建的低代码渲染组件了,如下图:

低代码在线拖拽公共组件的鬼点子,快来看看吧

4. 配置低代码渲染组件

按照下图的三步配置后,就可以渲染其他已经配置好的页面

(图中的dashboard是配置好的首页的code) 低代码在线拖拽公共组件的鬼点子,快来看看吧

结语

  1. 该方案还不支持出码模块,根据官方文档重新开发出码模块是否能够达到预期效果未知
  2. 还未实现传参,但有可行方案,如:通过appHelper
  3. 该方案只是出于兴趣,如要使用到实际项目中,还望慎重