amis-editor 注册自定义组件

news2024/10/6 8:28:52

建议先将amis文档从头到尾,仔细看一遍。
参考:amis - 低代码前端框架 

amis 的渲染过程是将 json 转成对应的 React 组件。先通过 json 的 type 找到对应的 Component,然后把其他属性作为 props 传递过去完成渲染。

import * as React from 'react';
import {Renderer} from 'amis-core';
@Renderer({  // amis-core/src/factory.tsx里的Renderer方法,主要作用识别json格式的type交给对应react组件来处理(现在可以识别{"type": "page", "title": "自定义组件示例"} )。
  type: 'page'
  // ... 其他信息隐藏了
})
export class PageRenderer extends React.Component {
  // ... 其他信息隐藏了
  render() {
    const { title, body,  render /*用来渲染孩子节点,如果当前是叶子节点则可以忽略。*/  } = this.props;
    return (
      <div className="page">
        <h1>{title}</h1>
        <div className="body-container">
          {render('body', body,{
	 	// 这里的信息会作为 props 传递给子组件,一般情况下都不需要这个
	   }) /*渲染孩子节点*/}
        </div>
      </div>
    );
  }
}
// 如果不支持 Decorators 语法也可以使用如下写法
export Renderer({
  type: 'page'
})(class PageRenderer extends React.Component {
  render() {
    // ...同上
  }
})




 

React注册自定义组件:

1.比如:注册一个 React 组件,当节点的 type 是 my-renderer 时,交给当前组件来完成渲染。

import * as React from 'react';
import {Renderer} from 'amis';
@Renderer({
  type: 'my-renderer',
  autoVar: true // amis 1.8 之后新增的功能,自动解析出参数里的变量
})
class CustomRenderer extends React.Component {
  render() {
    const {tip} = this.props;
    return <div>这是自定义组件:{tip}</div>;
  }
}
有了以上这段代码后,就可以这样使用了:
{
  "type": "page",
  "title": "自定义组件示例",
  "body": {
    "type": "my-renderer",
    "tip": "简单示例"
  }
}

如果这个组件还能通过 children 属性添加子节点,则需从props中获取body,  render处理(参考上面page组件)。

render(region, node, props) 方法,这个方法就是专门用来渲染子节点的。来看下参数说明:

* region 区域名称,你有可能有多个区域可以作为容器,请不要重复。

* node 子节点。

* props 可选,可以通过此对象跟子节点通信等。

属性支持变量

因为配置了 autoVar: true,使得所有组件参数将自动支持变量,在组件内拿到的将是解析后的值(ps: 1.8.0 及以上版本新增配置,之前版本需要调用 amis 里的 resolveVariableAndFilter 方法)

2.表单项FormItem的扩展(amis-core/src/renderes/Item)

以上是普通渲染器的注册方式,如果是表单项,为了更简单的扩充,请使用 FormItem 注解,而不是 Renderer。 原因是如果用 FormItem 是不用关心:label 怎么摆,表单验证器怎么实现,如何适配表单的 3 种展现方式(水平、上下和内联模式),而只用关心:有了值后如何回显,响应用户交互设置新值。

import * as React from 'react';
import {FormItem} from 'amis';
@FormItem({
  type: 'custom'
})
class MyFormItem extends React.Component {
  render() {
    const {value, onChange} = this.props;
    return (
      <div>
        <p>这个是个自定义组件</p>
        <p>当前值:{value}</p>
        <a
          className="btn btn-default"
          onClick={() => onChange(Math.round(Math.random() * 10000))}
        >
          随机修改
        </a>
      </div>
    );
  }
}

有了以上这段代码后,就可以这样使用了:

{
  "type": "page",
  "title": "自定义组件示例",
  "body": {
    "type": "form",
    "body": [
      {
        "type": "custom",
        "label": "随机值",
        "name": "random"
      }
    ]
  }
}

注意: 使用 FormItem 默认是严格模式,即只有必要的属性变化才会重新渲染,有可能满足不了你的需求,如果忽略性能问题,可以传入 strictMode: false 来关闭。

表单项开发主要关心两件事。

1.呈现当前值。如以上例子,通过 this.props.value 判定如果勾选了则显示已勾选,否则显示请勾选。

2.接收用户交互,通过 this.props.onChange 修改表单项值。如以上例子,当用户点击按钮时,切换当前选中的值。

至于其他功能如:label/description 的展示、表单验证功能、表单布局(常规、左右或者内联)等等,只要是通过 FormItem 注册进去的都无需自己实现。

需要注意,获取或者修改的是什么值跟配置中 type 并列的 name 属性有关,也就是说直接关联某个变量,自定义中直接通过 props 下发了某个指定变量的值和修改的方法。如果你想获取其他数据,或者设置其他数据可以看下以下说明:

* 获取其他数据 可以通过 this.props.data 查看,作用域中所有的数据都在这了。

* 设置其他数据 可以通过 this.props.onBulkChange, 比如: this.props.onBulkChange({a: 1, b: 2}) 等于同时设置了两个值。当做数据填充的时候,这个方法很有用。

3.其它高级定制

——自定义验证器

如果 amis 自带的验证能满足需求了,则不需要关心。组件可以有自己的验证逻辑。

@FormItem({  type: 'custom-checkbox' })
export default class CustomCheckbox extends React.Component {
  validate() {
    // 通过 this.props.value 可以知道当前值。
    return isValid ? '' : '不合法,说明不合法原因。';
  }
  // ... 其他省略了
}

上面的例子只是简单说明,另外可以做异步验证,validate 方法可以返回一个 promise。

——OptionsControl (amis-core/src/renderes/Options)

如果你的表单组件性质和 amis 的 Select、Checkboxes、List 差不多,用户配置配置 source 可通过 API 拉取选项,你可以用 OptionsControl 取代 FormItem 这个注解。

用法是一样,功能方面主要多了以下功能。

* 可以配置 options,options 支持配置 visibleOn hiddenOn 等表达式

* 可以配置 source 换成动态拉取 options 的功能,source 中有变量依赖会自动重新拉取。

* 下发了这些 props,可以更方便选项。

    * options 不管是用户配置的静态 options 还是配置 source 拉取的,下发到组件已经是最终的选项了。

    * selectedOptions 数组类型,当前用户选中的选项。

    * loading 当前选项是否在加载

    * onToggle 切换一个选项的值

    * onToggleAll 切换所有选项的值,类似于全选。

4.组件间通信

关于组件间通信,amis 中有个机制就是,把需要被引用的组件设置一个 name 值,然后其他组件就可以通过这个 name 与其通信,比如这个例子。其实内部是依赖于内部的一个 Scoped Context。你的组件希望可以被别的组件引用,你需要把自己注册进去,默认自定义的非表单类组件并没有把自己注册进去,可以参考以下代码做添加:

import * as React from 'react';
import {Renderer, ScopedContext} from 'amis';
@Renderer({  type: 'my-renderer'})
export class CustomRenderer extends React.Component {
  static contextType = ScopedContext;
  constructor() {
    const scoped = this.context;
    scoped.registerComponent(this);
  }
  componentWillUnmount() {
    const scoped = this.context;
    scoped.unRegisterComponent(this);
  }
  // 其他部分省略了。
}

把自己注册进去了,其他组件就能引用到了。同时,如果你想找别的组件,也同样是通过 scoped 这个 context,如: scoped.getComponentByName("xxxName") 这样就能拿到目标组件的实例了(前提是目标组件已经配置了 name 为 xxxName)。

5.自定义组件接入事件动作

需求场景主要是想要自定义组件的内部事件暴露出去,能够通过对事件的监听来执行所需动作,并希望自定义组件自身的动作能够被其他组件调用。接入方法是通过`props.dispatchEvent`派发自身的各种事件,使其具备更灵活的交互设计能力;

通过重写`doAction`方法实现其他组件对其专属动作的调用,需要注意的是,此处依赖内部的 `Scoped Context`来实现自身的注册

amis/src/renderers中不同的组件可重写自己的doAction方法(实现自己的组件专属动作)

   可以直接调某一组件的doAction方法:comp.doAction()触发组件特有动作。 const values = await form.doAction( { type: 'submit' }, form.props.data, true );

   也可以通过onEvent配置组件特有动作(CmptAction)去触发对应组件的特有动作

自定义的渲染器 props 会下发一个非常有用的 env 对象。这个 env 有以下功能方法:

* env.fetcher 可以用来做 ajax 请求如: this.props.env.fetcher('xxxAPi', this.props.data).then((result) => console.log(result))

* env.confirm 确认框,返回一个 promise 等待用户确认如: this.props.env.confirm('你确定要这么做?').then((confirmed) => console.log(confirmed))

* env.alert 用 Modal 实现的弹框,个人觉得更美观。

* env.notify toast 某个消息 如: this.props.env.notify("error", "出错了")

* env.jumpTo 页面跳转。

大部分组件都是直接继承 RendererProps,里面包含渲染组件所需的常用属性. 例如:export interface PageProps extends RendererProps

 amis-editor注册自定义组件

比如antd按钮组件:

方法一:这里'amis-widget'的registerAmisEditorPlugin, registerRendererByType分别注册plugin插件和renderer渲染器。

src/plugins/AntdButton.tsx:

import type {BaseEventContext, RendererPluginEvent} from 'amis-editor-core';
import {getSchemaTpl} from 'amis-editor-core';
import {getEventControlConfig} from 'amis-editor/lib/renderer/event-control/helper';
import {Button, ButtonProps} from 'antd';
import React from 'react';

export class AntdButtonPlugin {
  rendererName = 'antd-button';
  $schema = '/schemas/UnkownSchema.json';
  name = '按钮';
  description = 'Ant Design按钮预设模板';
  tags = ['Ant Design'];
  icon = 'fa fa-square';
  scaffold = {
    type: 'antd-button',
    content: 'Antd 按钮',
    block: false,
    danger: false,
    disabled: false,
    ghost: false,
    shape: 'default',
    size: 'middle',
    buttonType: 'primary'
  };
  previewSchema = {
    ...this.scaffold
  };

  panelTitle = '按钮';

  events: RendererPluginEvent[] = [
    {
      eventName: 'onClick',
      eventLabel: '按钮点击',
      description: '按钮点击时触发',
      defaultShow: true
    }
  ];

  panelBodyCreator = (context: BaseEventContext) => {
    const id = context.id;
    const manager = (window as any).store.editorManager;
    return getSchemaTpl('tabs', [
      {
        title: '基础',
        body: [
          {
            type: 'input-text',
            name: 'content',
            label: '按钮内容',
            value: 'Antd 按钮'
          },
          {
            type: 'switch',
            name: 'block',
            label: '将按钮宽度调整为其父宽度的选项',
            value: false
          },
          {
            type: 'switch',
            name: 'danger',
            label: '危险按钮',
            value: false
          },
          {
            type: 'switch',
            name: 'disabled',
            label: '禁用按钮',
            value: false
          },
          {
            type: 'switch',
            name: 'ghost',
            label: '幽灵属性',
            value: false
          },
          {
            type: 'input-text',
            name: 'href',
            label: '点击跳转的地址',
            value: undefined
          },
          {
            type: 'select',
            name: 'shape',
            label: '按钮形状',
            value: 'default',
            options: [
              {
                label: '默认',
                value: 'default'
              },
              {
                label: '圆形',
                value: 'circle'
              },
              {
                label: '圆弧',
                value: 'round'
              }
            ]
          },
          {
            type: 'select',
            name: 'size',
            label: '按钮大小',
            value: 'middle',
            options: [
              {
                label: 'large',
                value: 'large'
              },
              {
                label: 'middle',
                value: 'middle'
              },
              {
                label: 'small',
                value: 'small'
              }
            ]
          },

          {
            type: 'select',
            name: 'buttonType',
            label: '按钮类型',
            value: 'primary',
            options: [
              {
                label: '主要按钮',
                value: 'primary'
              },
              {
                label: '虚线按钮',
                value: 'dashed'
              },
              {
                label: '链接按钮',
                value: 'link'
              },
              {
                label: '文本按钮',
                value: 'text'
              },
              {
                label: '默认按钮',
                value: 'default'
              }
            ]
          }
        ]
      },
      {
        title: '事件',
        className: 'p-none',
        body: [
          getSchemaTpl('eventControl', {
            name: 'onEvent',
            ...getEventControlConfig(manager, context)
          })
        ]
      }
    ]);
  };
}

/**
  onClick={
        onClick
          ? e => new Function(`return ${onClick}`)()(e)
          : function onClick(e) {
              console.log('click');
            }
      }
 */

export function AntdButton({
  content,
  block,
  danger,
  disabled,
  ghost,
  href,
  shape,
  size,
  buttonType,
  onClick
}: ButtonProps & {buttonType: ButtonProps['type']; onClick: string}) {
  const type = buttonType;
  return (
    <Button
      danger={danger || false}
      disabled={disabled || false}
      type={type || 'primary'}
      block={block || false}
      ghost={ghost || false}
      href={href || undefined}
      shape={shape || 'default'}
      size={size || 'middle'}
    >
      {content || 'Antd 按钮'}
    </Button>
  );
}

src/plugins/index.ts中进行plugin注册:

//@ts-ignore
import {registerAmisEditorPlugin, registerRendererByType} from 'amis-widget';

// import {registerEditorPlugin} from 'amis-editor';
// import {AntdCalendarPlugin, AntdCalendar} from './AntdCalendar';
// registerEditorPlugin(AntdCalendarPlugin)

import './AntdCalendar';

import {AntdButtonPlugin, AntdButton} from './AntdButton';
import {AntdDropdownPlugin, AntdDropdown} from './AntdDropdown';
import {ProCRUDPlugin, ProCRUD} from './ProCRUD';
import {ChartPiePlugin, ChartPie} from './ChartPie';
import {ChartScatterPlugin, ChartScatter} from './ChartScatter';
import {ChartMapPlugin, ChartMap} from './ChartMap';

enum Usage {
  renderer = 'renderer',
  formitem = 'formitem',
  options = 'options'
}
enum Framework {
  react = 'react',
  vue2 = 'vue2',
  vue3 = 'vue3',
  jquery = 'jquery'
}

const plugins = [
  {
    type: 'antd-button',
    plugin: AntdButtonPlugin,
    component: AntdButton
  },
  {
    type: 'antd-dropdown',
    plugin: AntdDropdownPlugin,
    component: AntdDropdown
  },
  {
    type: 'pro-crud',
    plugin: ProCRUDPlugin,
    component: ProCRUD
  },
  {
    type: 'chart-pie',
    plugin: ChartPiePlugin,
    component: ChartPie
  },
  {
    type: 'chart-scatter',
    plugin: ChartScatterPlugin,
    component: ChartScatter
  },
  {
    type: 'chart-map',
    plugin: ChartMapPlugin,
    component: ChartMap
  },
];

export default () => {
  plugins.forEach(({type, plugin, component}) => {
    registerAmisEditorPlugin(plugin);
    registerRendererByType(component, {
      type,
      usage: Usage.renderer,
      weight: 99,
      framework: Framework.react
    });
  });
};

方法二:采用amis-editor的registerEditorPlugin注册plugin插件。 amis的@Renderer 注册renderer渲染器

src/plugins/AntdCalendar.tsx:

import {Calendar, CalendarProps} from 'antd';
import React from 'react';
import {Renderer, RendererProps} from 'amis';
import {BasePlugin, registerEditorPlugin} from 'amis-editor';

export class AntdCalendarPlugin extends BasePlugin{
  rendererName = 'antd-calendar';
  $schema = '/schemas/UnkownSchema.json';
  name = '日历';
  description = 'Ant Design日历预设模板';
  tags = ['Ant Design'];
  icon = 'fa fa-calendar';
  scaffold = {
    type: 'antd-calendar',
    fullscreen: false
  };
  previewSchema = {
    ...this.scaffold
  };

  panelTitle = '日历';

  panelControls = [
    {
      type: 'switch',
      name: 'fullscreen',
      label: '是否全屏',
      value: false
    }
  ];
}

// @Renderer({
//   type: 'antd-calendar',
//   name: 'antd-calendar',
//   autoVar: true
// })
// export class AntdCalendar extends React.Component<RendererProps> {
//   render() {
//     const {fullscreen} = this.props;
//     return <Calendar fullscreen={fullscreen || false} />;
//   }
// }

export function AntdCalendar({fullscreen}: RendererProps) {
  return <Calendar fullscreen={fullscreen || false} />;
}
Renderer({
  type: 'antd-calendar',
  name: 'antd-calendar',
  autoVar: true
})(AntdCalendar);


registerEditorPlugin(AntdCalendarPlugin);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1878570.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[数据集][目标检测]睡岗检测数据集VOC+YOLO格式3290张4类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;3316 标注数量(xml文件个数)&#xff1a;3316 标注数量(txt文件个数)&#xff1a;3316 标注…

了解C# 中的集合(包括泛型和非泛型)

跨站点脚本 (XSS) 攻击是一种严重的安全威胁&#xff0c;恶意脚本会注入其他用户查看的网页中。本文演示了如何在 ASP.NET Core MVC 中构建一个简单的博客应用程序&#xff0c;同时使用内置安全功能和最佳实践来防止 XSS 攻击。 步骤 1.创建 ASP.NET Core MVC 项目 创建新的 …

Ollama中文版部署

M1部署Ollama Ollama中文网站: Featured - 精选 - Ollama中文网 下载网址: Download Ollama on macOS 安装后运行llma3模型: ollama run llama3:8b 界面使用: GitHub - open-webui/open-webui: User-friendly WebUI for LLMs (Formerly Ollama WebUI) 部署open-webui: do…

摸鱼大数据——Spark基础——Spark环境安装——PySpark搭建

三、PySpark环境安装 PySpark: 是Python的库, 由Spark官方提供. 专供Python语言使用. 类似Pandas一样,是一个库 Spark: 是一个独立的框架, 包含PySpark的全部功能, 除此之外, Spark框架还包含了对R语言\ Java语言\ Scala语言的支持. 功能更全. 可以认为是通用Spark。 功能 P…

【教学类-64-08】20240629彩棒鱼骨图(五)AAB排列 6.5*1CM 6选2根 30种

背景需求&#xff1a; 【教学类-64-04】20240619彩棒鱼骨图&#xff08;一&#xff09;6.5*1CM 6根棒子720种-CSDN博客文章浏览阅读897次&#xff0c;点赞23次&#xff0c;收藏13次。【教学类-64-04】20240619彩棒鱼骨图&#xff08;一&#xff09;6.5*1CM 6根棒子720种https:…

Java学习 - Redis开发规范与安全

开发规范 key设计 因素说明键名可读性&#xff0c;可管理性业务名:表名:字段名键名简洁性user:{uid}:friends:message:{mid}简化为u:{uid}&#x1f1eb;&#x1f1f7;m:{mid}与embstr和raw有关键名不包含特殊字符比如空格&#xff0c;制表符等&#xff0c;最好只有字母&#…

【Lua】第三篇:基本变量类型介绍

文章目录 一. 变量类型介绍二. 基本知识三. 基本类型介绍1. 空类型&#xff08;nil&#xff09;2. 数值类型&#xff08;number&#xff09;3. 字符串类型&#xff08;string&#xff09;4. 布尔类型&#xff08;boolean&#xff09; 一. 变量类型介绍 Lua中一共有如下8中变量…

Linux:RAID磁盘阵列

目录 一、RAID&#xff08;磁盘阵列&#xff09; 1.1、概念 1.2、RAID 0&#xff08;条带化存储&#xff09; 1.3、RAID 1&#xff08;镜像存储&#xff09; 1.4、RAID 5 1.5、RAID 6 1.6、RAID 10 (先做镜像&#xff0c;再做条带) 二、创建RAID 2.1、建立RAID 0 …

利用 Docker 简化 Nacos 部署:快速搭建 Nacos 服务

利用 Docker 简化 Nacos 部署&#xff1a;快速搭建 Nacos 服务 引言 在微服务架构中&#xff0c;服务注册与发现是确保服务间通信顺畅的关键组件。Nacos&#xff08;Dynamic Naming and Configuration Service&#xff09;作为阿里巴巴开源的一个服务发现和配置管理平台&…

Spring事务及其传播机制(一)

目录 1.事务回顾 1.1什么是事务 1.2事务的四大重要特性&#xff08;ACID&#xff09; 1.3事务的操作 2.Spring中事务的实现 2.1编程式事务&#xff08;了解&#xff09; 2.2声明式事务Transactional 3.Transactional作用 3.1重新抛出异常 3.2手动回滚事务 1.事务回顾…

【Redis】SpringBoot连接Redis

1. 创建项目并配置文件 勾选NoSQL中的 Spring Data Redis。当然,把 Web 中的 SpringWeb 也勾选一下.方便写接口进行后续测试。 在 application.yml 中配置 2. 不同数据类型使用Demo 在SpringBoot中&#xff0c;为我们提供了StringRedisTemplate类&#xff0c;供我们处理一些文…

详细介绍MySQL的索引(上)

索引 索引概述 索引(index)是帮助MySQL高效获取数据的数据结构(有序)。在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用(指向数据&#xff0c;这样就可以在这些数据结构上实现高级查找算法&#xff0c;这种数据结…

VisualStudio2019受支持的.NET Core

1.VS Studio2019受支持的.NET Core&#xff1f; 适用于 Visual Studio 的 .NET SDK 下载 (microsoft.com) Visual Studio 2019 默认并不直接支持 .NET 6 及以上版本。要使用 .NET 6 或更高版本&#xff0c;你需要在 Visual Studio 2019 中采取额外步骤&#xff0c;比如安装相应…

<电力行业> - 《第9课:输电(二)》

4 输送电能流程 输送电能总共有&#xff1a;发电站→升压变压器→高压输电线→降压变压器→用电单位等五个流程。 电力工业初期&#xff0c;发电厂建在电力用户附近&#xff0c;直接向用户送电&#xff0c;所以那个时候只有发电和用电两个环节。 随着电力生产规模和负荷中心规…

QTreeView第一列自适应

通过setStretchLastSection(bool stretch)可以设置最后一列自适应,对于QTreeView,stretch默认为true。但有时候我们需要设置第一列自适应,比如文件浏览器,共有名称、大小和修改日期三列,大小和日期的宽度几乎是固定的,但名称却可长可短,此时我们希望在窗口大小变化时,第…

一元线性回归-R语言

# # 安装包 # install.packages(ggplot2) # library(ggplot2) Sys.setlocale(category LC_ALL, locale English_United States.1252) # Sys.setlocale("LC_ALL","Chinese") x <- c(18, 20, 22, 24, 26, 28, 30) y <- c(26.86, 28.35, 28.87,28.75,…

【源码+文档+调试讲解】actual self 服装店的设计与实现

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统服装销售信息管理难度大&#xff0c;容错率低&#xff0c…

封装了一个iOS滚动厨窗效果

效果图 背景 我们要实现如图的厨窗效果&#xff0c;不能通过在tableView底部添加一个背景图片的方式&#xff0c;因为这需要修改整个tableView的背景色为透明&#xff0c;影响到的范围太大&#xff0c;只能将这个效果局限在这个cell 中&#xff0c;然后通过监听tableView的滚动…

Python应用开发——30天学习Streamlit Python包进行APP的构建(12)

st.checkbox 显示复选框部件。 Function signature[source] st.checkbox(label, valueFalse, keyNone, helpNone, on_changeNone, argsNone, kwargsNone, *, disabledFalse, label_visibility"visible") Returns (bool) Whether or not the checkbox is checked. …

认识100种电路之放大电路

在电子技术的广袤世界中&#xff0c;放大电路犹如一颗璀璨的明珠&#xff0c;发挥着至关重要的作用。那么&#xff0c;为什么电路需要放大&#xff1f;放大的原理又是什么&#xff1f;实现放大又需要用到哪些元器件以及数量如何呢&#xff1f;接着往下看&#xff0c;会解开你的…