百度Amis+React低代码实践

这篇具有很好参考价值的文章主要介绍了百度Amis+React低代码实践。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景

在项目中有集成低代码平台的想法,经过多方对比最后选择了 amis,主要是需要通过 amis 进行页面配置,导出 json 供移动端和 PC 端进行渲染,所以接下来讲一下近两周研究 amis 的新的以及一些简单经验,供大家参考.

什么是 amis

amis 是一个低代码前端框架,它使用 JSON 配置来生成页面,可以减少页面开发工作量,极大提升效率。

如何使用 amis

在 amis 官网提供了两种使用 amis 的方式分别是

  • JSSDK 可以在任意页面使用
  • React 可以在 React 项目中使用

博主是在 umi 框架下结合 React 使用 amis,所以本文主要着重介绍第二种方法

在使用时需要对 amis 进行安装,项目中也需要使用 amis-editor 进行页面配置所以需要同时安装如下两个包

{
  "amis": "^3.1.1",
  "amis-editor": "^5.4.1"
}

amis

首先介绍 amis,amis 提供了 render 方法来对 amis-editor 生成的 JSON 对象页面配置进行渲染,如下,在使用是 render 主要作用就是进行渲染

import { render as renderAmis } from "amis";

const App = () => {
  return (
    <div>
      {renderAmis({
        type: "button",
        label: "保存",
        level: "primary",
        onClick: function () {
          console.log("TEST");
        },
      })}
    </div>
  );
};

export default App;

amis-editor

amis-editor 提供了一个编译器组件 <Editor />

import { useState } from "react";
import { Editor, setSchemaTpl } from "amis-editor";
import type { SchemaObject } from "amis";
import { render as renderAmis } from "amis";
import type { Schema } from "amis/lib/types";
// 以下样式均生效
import "amis/lib/themes/default.css";
import "amis/lib/helper.css";
import "amis/sdk/iconfont.css";
import "amis-editor-core/lib/style.css";
import "amis-ui/lib/themes/antd.css";
type Props = {
  defaultPageConfig?: Schema;
  codeGenHandler?: (codeObject: Schema) => void;
  pageChangeHandler?: (codeObject: Schema) => void;
};

export function Amis(props: Props) {
  const [mobile, setMobile] = useState(false);
  const [preview, setPreview] = useState(false);
  const [defaultPageConfig] = useState<Schema>(props.defaultPageConfig); // 传入配置
  const defaultSchema: Schema | SchemaObject = defaultPageConfig || {
    type: "page",
    body: "",
    title: "标题",
    regions: ["body"],
  };
  const [schema] = useState(defaultSchema);
  let pageJsonObj: Schema = defaultSchema;
  const onChange = (value: Schema) => {
    pageJsonObj = value;
    props.pageChangeHandler && props.pageChangeHandler(value);
  };
  const onSave = () => {
    props.codeGenHandler && props.codeGenHandler(pageJsonObj);
  };
  return (
    <>
      {renderAmis({
        type: "form",
        mode: "inline",
        title: "",
        body: [
          {
            type: "switch",
            option: "预览",
            name: "preview",
            onChange: function (v: any) {
              setPreview(v);
            },
          },
          {
            type: "switch",
            option: "移动端",
            name: "mobile",
            onChange: function (v: any) {
              setMobile(v);
            },
          },
          {
            type: "button",
            label: "保存",
            level: "primary",
            onClick: function () {
              onSave();
            },
          },
          {
            type: "button",
            label: "退出",
            level: "danger",
            onClick: function () {
              // if (!window.confirm('确定退出?')) return;
              if (props.cancleGenHandler) props.cancleGenHandler();
            },
          },
        ],
      })}
      <Editor
        preview={preview}
        isMobile={mobile}
        onChange={onChange}
        value={schema as SchemaObject}
        theme={"antd"}
        onSave={onSave}
      />
    </>
  );
}

export default Amis;

在 amis 中提供了两套组件样式供我们选择,分别是 cxd(云舍)和 antd(仿 Antd),我们可以通过设置Editor组件中 theme 属性来进行主题的选择,同时需要引入对应的组件样式在以上代码中,我们对Editor组件进行了二次封装,暴露出了defaultPageConfig(进入编译器默认页面 JSON 配置)属性和codeGenHandler(代码生成保存方法),cancleGenHandler(退出页面编辑器方法),pageChangeHandler(页面改变方法)供外部使用

自定义组件

在 amis-editor 中使用的组件可以是我们的自定义组件.在编写自定义组件时特别需要主义的是它的 plugin 配置接下来以MyButton为例来进行自定义组件的介绍

首先来介绍以下组件结构

├─MyButton
  │  ├─comp.tsx # 组件本体
  │  ├─index.tsx # 整体导出
  │  ├─plugin.tsx # 右侧panel配置

在comp.tsx中主要进行组件的开发

import React from "react";
import type { Schema } from "amis/lib/types";
import { Button } from "antd";

const MyButtonRender = React.forwardRef((props: Schema, ref: any) => {
  // const props = this.props
  return (
    <Button
      {...props}
      ref={ref}
      type={props.level || "primary"}
      name={props.name}
    >
      {props.label}
    </Button>
  );
});

class MyButtonRender2 extends React.Component<any, any> {
  handleClick = (nativeEvent: React.MouseEvent<any>) => {
    const { dispatchEvent, onClick } = this.props;
    // const params = this.getResolvedEventParams();
    dispatchEvent(nativeEvent, {});
    onClick?.({});
  };

  handleMouseEnter = (e: React.MouseEvent<any>) => {
    const { dispatchEvent } = this.props;
    // const params = this.getResolvedEventParams();

    dispatchEvent(e, {});
  };

  handleMouseLeave = (e: React.MouseEvent<any>) => {
    const { dispatchEvent } = this.props;
    // const params = this.getResolvedEventParams();

    dispatchEvent(e, {});
  };

  render() {
    return (
      <MyButtonRender
        onClick={this.handleClick}
        onMouseEnter={this.handleMouseEnter}
        onMouseLeave={this.handleMouseLeave}
      >
        {this.props.label}2
      </MyButtonRender>
    );
  }
}

export default MyButtonRender2;

在上述代码中MyButtonRender简单的对Button组件进行了简单的封装,MyButtonRender2对 amis 中组件的事件进行了简单的处理并暴露出去

plugin.tsx中主要对MyButtonRender组件进行渲染器注册以及对组件的 plugin 进行配置,注册渲染器是为了将自定义组件拖入中间预览区域是可以正常的显示,这一操作与 amis 的工作原理有关(amis 的渲染过程是将 json 转成对应的 React 组件。先通过 json 的 type 找到对应的 Component,然后把其他属性作为 props 传递过去完成渲染。工作原理
)

在plugin.tsx中进行 panel 配置

import { Renderer } from "amis";
import MyButtonRender from "./comp";
import type { BaseEventContext } from "amis-editor-core";
import { BasePlugin } from "amis-editor-core";
import { getSchemaTpl } from "amis-editor-core";
import type { RendererPluginAction, RendererPluginEvent } from "amis-editor";
import { getEventControlConfig } from "amis-editor/lib/renderer/event-control/helper";

// 渲染器注册
Renderer({
  type: "my-button",
  autoVar: true,
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore
})(MyButtonRender);

export class MyButton extends BasePlugin {
  // 关联渲染器名字
  rendererName = "my-button";
  $schema = "/schemas/ActionSchema.json";
  order = -400;
  // 组件名称
  name = "MyButton";
  isBaseComponent = true;
  description =
    "用来展示一个按钮,你可以配置不同的展示样式,配置不同的点击行为。";
  docLink = "/amis/zh-CN/components/button";
  tags = ["自定义"];
  icon = "fa fa-square";
  pluginIcon = "button-plugin";
  scaffold = {
    type: "my-button",
    label: "MyButton",
    wrapperBody: true,
  };
  previewSchema: any = {
    type: "my-button",
    label: "MyButton",
    wrapperBody: true,
  };

  panelTitle = "MyButton";

  // 事件定义
  events: RendererPluginEvent[] = [
    {
      eventName: "click",
      eventLabel: "点击",
      description: "点击时触发",
      defaultShow: true,
      dataSchema: [
        {
          type: "object",
          properties: {
            nativeEvent: {
              type: "object",
              title: "鼠标事件对象",
            },
          },
        },
      ],
    },
    {
      eventName: "mouseenter",
      eventLabel: "鼠标移入",
      description: "鼠标移入时触发",
      dataSchema: [
        {
          type: "object",
          properties: {
            nativeEvent: {
              type: "object",
              title: "鼠标事件对象",
            },
          },
        },
      ],
    },
    {
      eventName: "mouseleave",
      eventLabel: "鼠标移出",
      description: "鼠标移出时触发",
      dataSchema: [
        {
          type: "object",
          properties: {
            nativeEvent: {
              type: "object",
              title: "鼠标事件对象",
            },
          },
        },
      ],
    },
  ];

  // 动作定义
  actions: RendererPluginAction[] = [];

  panelJustify = true;

  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  panelBodyCreator = (context: BaseEventContext) => {
    return getSchemaTpl("tabs", [
      {
        title: "属性",
        body: [
          getSchemaTpl("label", {
            label: "按钮名称",
          }),
          {
            type: "input-text",
            label: "字段名称",
            name: "name",
          },
          {
            type: "select",
            label: "按钮类型",
            name: "level",
            options: [
              {
                label: "默认",
                value: "primary",
              },
              {
                label: "危险",
                value: "danger",
              },
              {
                label: "警告",
                value: "warn",
              },
              {
                label: "成功",
                value: "success",
              },
              {
                label: "浅色",
                value: "default",
              },
            ],
            multiple: false,
            selectFirst: false,
          },
          {
            type: "input-text",
            label: "按钮图标",
            name: "icon",
            // 提示
            labelRemark: {
              icon: 'icon-close',
              trigger: ['hover'],
              className: 'Remark--warning',
              title: '提示',
              content: '图标请从My Iconfont库中选择 部分图标需要加icon-前缀 如close -> icon-close',
            // },
          },
        ],
      },
      {
        title: "样式",
        body: [
          getSchemaTpl("buttonLevel", {
            label: "高亮样式",
            name: "activeLevel",
            visibleOn: "data.active",
          }),

          getSchemaTpl("switch", {
            name: "block",
            label: "块状显示",
          }),

          getSchemaTpl("size", {
            label: "尺寸",
          }),
        ],
      },
      {
        title: "事件",
        className: "p-none",
        body:
          !!context.schema.actionType ||
          ["submit", "reset"].includes(context.schema.type)
            ? [
                getSchemaTpl("eventControl", {
                  name: "onEvent",
                  ...getEventControlConfig(this.manager, context),
                }),
                // getOldActionSchema(this.manager, context)
              ]
            : [
                getSchemaTpl("eventControl", {
                  name: "onEvent",
                  ...getEventControlConfig(this.manager, context),
                }),
              ],
      },
    ]);
  };
}

当点选某个组件的时候,编辑器内部会触发面板构建动作,每个插件都可以通过实现 buildEditorPanel 来插入右侧面板。通常右侧面板都是表单配置,使用 amis 配置就可以完成。所以推荐的做法是,直接在这个插件上面定义 panelBody 或者 panelBodyCreator 即可。

具体配置可以参考上述代码,其中需要注意的是getSchemaTpl这一方法,该方法通过获取预先通过setSchemaTpl设置的模板来进行渲染某些元素组件,一下部分源码可进行参考,tpl 部分全部源码可参考这里

export function getSchemaTpl(
  name: string,
  patch?: object,
  rendererSchema?: any
): any {
  const tpl = tpls[name] || {};
  let schema = null;

  if (typeof tpl === "function") {
    schema = tpl(patch, rendererSchema);
  } else {
    schema = patch
      ? {
          ...tpl,
          ...patch,
        }
      : tpl;
  }

  return schema;
}

export function setSchemaTpl(name: string, value: any) {
  tpls[name] = value;
}

index.tsx中主要进行自定义组件插件的注册以及导出

import { registerEditorPlugin } from "amis-editor";
import { MyButton } from "./plugin";

registerEditorPlugin(MyButton);

其他

在拖拽组件生成页面时,amis-editor 可选择的组件有很多,如果我们想使用自己组建的同时忽略隐藏原有组件可以通过disabledRendererPlugin来对原生组件进行隐藏

import { registerEditorPlugin, BasePlugin } from "amis-editor";
import type {
  RendererEventContext,
  SubRendererInfo,
  BasicSubRenderInfo,
} from "amis-editor";

/**
 * 用于隐藏一些不需要的Editor组件
 * 备注: 如果不知道当前Editor中有哪些预置组件,可以在这里设置一个断点,console.log 看一下 renderers。
 */

// 需要在组件面板中隐藏的组件
const disabledRenderers = [
  // 'flex',
  "crud2",
  "crud2",
  "crud2",
  // 'crud',
  // 'input-text',
  "input-email",
  "input-password",
  "input-url",
  // "button",
  "reset",
  "submit",
  "tpl",
  "grid",
  "container",
  // 'flex',
  // 'flex',
  "collapse-group",
  "panel",
  "tabs",
  // 'form',
  "service",
  "textarea",
  "input-number",
  // 'select',
  "nested-select",
  "chained-select",
  "dropdown-button",
  "checkboxes",
  "radios",
  "checkbox",
  "input-date",
  "input-date-range",
  "input-file",
  "input-image",
  "input-excel",
  "input-tree",
  "input-tag",
  "list-select",
  "button-group-select",
  "button-toolbar",
  "picker",
  "switch",
  "input-range",
  "input-rating",
  "input-city",
  "transfer",
  "tabs-transfer",
  "input-color",
  "condition-builder",
  "fieldset",
  "combo",
  "input-group",
  "input-table",
  "matrix-checkboxes",
  "input-rich-text",
  "diff-editor",
  "editor",
  "search-box",
  "input-kv",
  "input-repeat",
  "uuid",
  "location-picker",
  "input-sub-form",
  "hidden",
  "button-group",
  "nav",
  "anchor-nav",
  "tooltip-wrapper",
  "alert",
  "wizard",
  "table-view",
  "web-component",
  "audio",
  "video",
  "custom",
  "tasks",
  "each",
  "property",
  "iframe",
  "qrcode",
  "icon",
  "link",
  "list",
  "mapping",
  "avatar",
  "card",
  "card2",
  "cards",
  "table",
  "table2",
  "chart",
  "sparkline",
  "carousel",
  "image",
  "images",
  "date",
  "time",
  "datetime",
  "tag",
  "json",
  "progress",
  "status",
  "steps",
  "timeline",
  "divider",
  "code",
  "markdown",
  "collapse",
  "log",
  "input-array",
  "control",
  "input-datetime",
  "input-datetime-range",
  "formula",
  "group",
  "input-month",
  "input-month-range",
  "input-quarter",
  "input-quarter-range",
  "static",
  "input-time",
  "input-time-range",
  "tree-select",
  "input-year",
  "input-year-range",
  "breadcrumb",
  "custom",
  "hbox",
  "page",
  "pagination",
  "plain",
  "wrapper",
  "column-toggler",
];

export class ManagerEditorPlugin extends BasePlugin {
  order = 9999;
  buildSubRenderers(
    context: RendererEventContext,
    renderers: SubRendererInfo[]
  ): BasicSubRenderInfo | BasicSubRenderInfo[] | void {
    // 更新NPM自定义组件排序和分类
    // console.log(renderers);
    for (let index = 0, size = renderers.length; index < size; index++) {
      // 判断是否需要隐藏 Editor预置组件
      const pluginRendererName = renderers[index].rendererName;
      if (
        pluginRendererName &&
        disabledRenderers.indexOf(pluginRendererName) > -1
      ) {
        renderers[index].disabledRendererPlugin = true; // 更新状态
      }
    }
  }
}

registerEditorPlugin(ManagerEditorPlugin);

写在最后

一个阶段的结束伴随着另一个阶段的开始,在新的阶段中会继续学习继续进步文章来源地址https://www.toymoban.com/news/detail-498614.html

到了这里,关于百度Amis+React低代码实践的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue项目百度ueditor编辑器集成135和秀米,主题图标美化

    本文介绍vue项目里引入百度Ueditor富文本编辑器,集成135编辑器和秀米编辑器,使内容编辑更加丰富,跳转体验更加丝滑。再封装成组件,使用更加快捷。 编辑器主界面 弹框打开135编辑器 弹框打开秀米编辑器 操作说明:ueditor编辑器菜单栏集成135和秀米图标,点击分别弹框打

    2024年02月05日
    浏览(61)
  • React Native 集成到iOS原有的项目上

    集成到现有原生应用 把 React Native 组件集成到 iOS 应用中有如下几个主要步骤: 配置好 React Native 依赖和项目结构。 了解你要集成的 React Native 组件。 使用 CocoaPods,把这些组件以依赖的形式加入到项目中。 创建 js 文件,编写 React Native 组件的 js 代码。 在应用中添加一个RC

    2024年02月12日
    浏览(76)
  • vue3.0 amis 低代码框架

    amis amis 是百度开源的一个低代码前端框架,它使用 JSON 配置来生成页面,可以减少页面开发工作量,极大提升效率。 组件地址:https://aisuda.bce.baidu.com/amis 编辑器地址:https://aisuda.github.io/amis-editor Vue3 中使用 安装依赖 封装组件 AmisComponent 动态渲染组件 LowcodeEngine 路由处理 项

    2024年02月15日
    浏览(45)
  • 高效营销系统集成:百度营销的API无代码解决方案,提升电商与广告效率

    百度营销API连接:构建无代码开发的高效集成体系 在数字营销的高速发展时代,企业追求的是快速响应市场的能力以及提高用户运营的效率。百度营销API连接正是为此而生,它通过无代码开发的方式,实现了电商平台、营销系统和CRM的一站式集成。这种创新的连接方式不仅省

    2024年02月04日
    浏览(47)
  • 请简述React是什么?React的主要特点有哪些?React中有哪些主要组件?

    React是一个用于构建用户界面的JavaScript库,它由Facebook开发并开源。React的主要特点是其数据驱动和组件化的设计理念。它允许开发者将复杂的界面分解为简单的组件,并将这些组件以数据流的方式组合在一起,使得组件的状态和行为可以根据数据的变化而自动更新。React还提

    2024年02月14日
    浏览(46)
  • 前端面试:【代码质量与工程实践】单元测试、集成测试和持续集成

    在现代软件开发中,确保代码质量是至关重要的。单元测试、集成测试和持续集成是关键的工程实践,用于提高代码的可靠性和可维护性。本文将深入探讨这些概念,以及它们如何在软件开发中发挥作用。 1. 单元测试(Unit Testing): 单元测试是一种测试方法,用于验证代码

    2024年02月11日
    浏览(43)
  • 【实战】 React 与 Hook 应用:实现项目列表 —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(二)

    学习内容来源:React + React Hook + TS 最佳实践-慕课网 相对原教程,我在学习开始时(2023.03)采用的是当前最新版本: 项 版本 react react-dom ^18.2.0 react-router react-router-dom ^6.11.2 antd ^4.24.8 @commitlint/cli @commitlint/config-conventional ^17.4.4 eslint-config-prettier ^8.6.0 husky ^8.0.3 lint-staged ^13.1.2 p

    2024年02月09日
    浏览(56)
  • 【实战】 项目起航:项目初始化与配置 —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(一)

    学习内容来源:React + React Hook + TS 最佳实践-慕课网 相对原教程,我在学习开始时(2023.03)采用的是当前最新版本: 项 版本 react react-dom ^18.2.0 react-router react-router-dom ^6.11.2 antd ^4.24.8 @commitlint/cli @commitlint/config-conventional ^17.4.4 eslint-config-prettier ^8.6.0 husky ^8.0.3 lint-staged ^13.1.2 p

    2024年02月10日
    浏览(78)
  • 【实战】一、项目起航:项目初始化与配置 —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(一)

    学习内容来源:React + React Hook + TS 最佳实践-慕课网 相对原教程,我在学习开始时(2023.03)采用的是当前最新版本: 项 版本 react react-dom ^18.2.0 react-router react-router-dom ^6.11.2 antd ^4.24.8 @commitlint/cli @commitlint/config-conventional ^17.4.4 eslint-config-prettier ^8.6.0 husky ^8.0.3 lint-staged ^13.1.2 p

    2024年02月12日
    浏览(53)
  • Ueditor 百度强大富文本Springboot 项目集成使用(包含上传文件和上传图片的功能使用)简单易懂,举一反三

    首先如果大家的富文本中不考虑图片或者附件的情况下,只考虑纯文本且排版的情况下我们可以直接让前端的vue来继承UEditor就可以啦。但是要让前端将那几个上传图片和附件的哪些功能给阉割掉! 然后就是说如果考虑到了上传图片或者视频和附件那么咱们还是用的前后分离

    2024年02月15日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包