低代码事件绑定和组件联动——低代码知识点详解(二)

demo体验地址:dbfu.github.io/lowcode-dem...

背景

上篇文章实现了低代码基础功能,这一篇来实现低代码高级一点的东西,事件绑定组件联动,有了这两个东西,低代码做出来的东西就不再是静态页面,可以拥有逻辑。

建议没看过上一篇的先看一下

《保姆级》低代码知识点详解(一)

大纲

下面我们实现一下查看组件大纲树功能,使用antdTree组件。

封装一个组件树弹框组件

tsx 复制代码
// src/editor/layouts/header/component-tree.tsx
import { Modal, Tree } from 'antd';
import { useComponets } from '../../stores/components';

interface ComponentTreeProps {
  open: boolean,
  onCancel: () => void,
}

const ComponentTree = ({ open, onCancel }: ComponentTreeProps) => {

  const { components, setCurComponentId } = useComponets();

  // 选择组件后,高亮当前组件,并关闭弹框
  function componentSelect([selectedKey]: any[]) {
    setCurComponentId(selectedKey);
    onCancel && onCancel();
  }

  return (
    <Modal
      open={open}
      title="组件树"
      onCancel={onCancel}
      destroyOnClose
      footer={null}
    >
      <Tree
        fieldNames={{ title: 'name', key: 'id' }}
        treeData={components as any}
        showLine
        defaultExpandAll
        onSelect={componentSelect}
      />
    </Modal>
  )
}

export default ComponentTree;

在头上工具栏加一个查看大纲按钮。

选择一个后

事件

前言

前端很多组件都会有事件,组件之间联动基本都是由事件发起的。

下面实现一个简单demo,点击按钮显示一个消息提示。

改造属性配置文件,增加事件配置

为了不让配置文件代码变得很多,把属性配置和事件配置拆成两个组件。

tsx 复制代码
import { Segmented } from 'antd';
import type { SegmentedValue } from 'antd/es/segmented';
import { useState } from 'react';
import { useComponets } from '../../stores/components';
import ComponentAttr from './attr';
import ComponentEvent from './event';

const Setting: React.FC = () => {

  const { curComponentId, curComponent } = useComponets();

  const [key, setKey] = useState<SegmentedValue>('属性');

  if (!curComponentId || !curComponent) return null;

  return (
    <div>
      <Segmented value={key} onChange={setKey} block options={['属性', '事件']} />
      <div className='pt-[20px]'>
        {
          key === '属性' && (
            <ComponentAttr />
          )
        }
        {
          key === '事件' && (
            <ComponentEvent />
          )
        }
      </div>
    </div>
  )
}

export default Setting;

事件数据结构

json 复制代码
{
  id: 1,
  name: 'Button',
  props: {
    // 点击事件绑定显示消息动作
    onClick: {
      // 动作类型
      type: 'ShowMessage',
      // 动作配置
      config: {
        // 消息类型
        type: 'success',  
        // 消息文本
        text: '点击了按钮',
      }
    }
  }
}

根据上面数据结构实现事件动作配置功能

代码如下

tsx 复制代码
// src/editor/layouts/setting/event.tsx
import { Collapse, Input, Select } from 'antd';
import { ItemType } from '../../item-type';
import { useComponets } from '../../stores/components';

const componentEventMap = {
  [ItemType.Button]: [{
    name: 'onClick',
    label: '点击事件',
  }],
}

const ComponentEvent = () => {

  const { curComponent, curComponentId, updateComponentProps } = useComponets();

  // 事件类型改变
  function typeChange(eventName: string, value: string) {
    if (!curComponentId) return;
    updateComponentProps(curComponentId, { [eventName]: { type: value, } })
  }

  // 消息类型改变
  function messageTypeChange(eventName: string, value: string) {
    if (!curComponentId) return;
    updateComponentProps(curComponentId, {
      [eventName]: {
        ...curComponent?.props?.[eventName],
        config: {
          ...curComponent?.props?.[eventName]?.config,
          type: value,
        },
      }
    })
  }

  // 消息文本改变
  function messageTextChange(eventName: string, value: string) {
    if (!curComponentId) return;
    updateComponentProps(curComponentId, {
      [eventName]: {
        ...curComponent?.props?.[eventName],
        config: {
          ...curComponent?.props?.[eventName]?.config,
          text: value,
        },
      },
    })
  }

  if (!curComponent) return null;

  return (
    <div className='px-[12px]'>
      {(componentEventMap[curComponent.name] || []).map(setting => {
        return (
          <Collapse key={setting.name} defaultActiveKey={setting.name}>
            <Collapse.Panel header={setting.label} key={setting.name}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <div>动作:</div>
                <div>
                  <Select
                    style={{ width: 160 }}
                    options={[
                      { label: '显示提示', value: 'showMessage' },
                    ]}
                    onChange={(value) => { typeChange(setting.name, value) }}
                    value={curComponent?.props?.[setting.name]?.type}
                  />
                </div>
              </div>
              {
                curComponent?.props?.[setting.name]?.type === 'showMessage' && (
                  <div className='flex flex-col gap-[12px] mt-[12px]'>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                      <div>类型:</div>
                      <div>
                        <Select
                          className='w-[160px]'
                          options={[
                            { label: '成功', value: 'success' },
                            { label: '失败', value: 'error' },
                          ]}
                          onChange={(value) => { messageTypeChange(setting.name, value) }}
                          value={curComponent?.props?.[setting.name]?.config?.type}
                        />
                      </div>
                    </div>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                      <div>文本:</div>
                      <div>
                        <Input
                          className='w-[160px]'
                          onChange={(e) => { messageTextChange(setting.name, e.target.value) }}
                          value={curComponent?.props?.[setting.name]?.config?.text}
                        />
                      </div>
                    </div>
                  </div>
                )
              }
            </Collapse.Panel>
          </Collapse>
        )
      })}
    </div>
  )
}


export default ComponentEvent;

效果展示

预览时实现点击按钮显示消息

实现思路

预览时把上面数据结构改造成下面这样就行了

json 复制代码
{
  id: 1,
  name: 'Button',
  props: {
    // 点击事件绑定显示消息动作
    onClick: {
      // 动作类型
      type: 'ShowMessage',
      // 动作配置
      config: {
        // 消息类型
        type: 'success',  
        // 消息文本
        text: '点击了按钮',
      }
    }
  }
}
json 复制代码
{
  id: 1,
  name: 'Button',
  props: {
    // 点击事件显示消息
    onClick: () => {
      message.success('点击了按钮');
    }
  }
}

代码实现

渲染时处理事件

效果展示

组件联动

前言

下面我们来实现一个简单的组件联动功能。

假设页面中有三个按钮,第一个是普通按钮,第二个按钮让第一个按钮loading,第三个按钮可以让第一个按钮结束loading。

想实现上面demo,有两种方式:

  1. 第一个按钮loading属性绑定一个变量,第二第三个按钮通过点击事件控制这个变量。
  2. 按钮暴露出一个开始loading和结束loading的方法,第二第三个按钮通过点击事件调用第一个按钮暴露出来的方法。

两个方案我们后面都会实现,这里先实现第二种方案。

实现思路

封装一个自己的按钮组件,组件内部通过react的useImperativeHandleapi把想要暴露出去的方法暴露出去,然后在渲染组件的地方通过ref获取到组件实例,这时候我们就能调用组件里面暴露出来的方法了。

实战

给事件添加组件方法动作类型

事件添加组件方法动作类型,当事件那里选的动作类型时组件方法的时候,动态显示组件树下拉框,选择一个组件后,动态显示这个组件暴露出来的方法下拉框,然后选择一个方法。

事件动作类型下拉框中天际一个组件方法类型

配置组件类型暴露出哪些方法

选择组件方法后,动态渲染组件树下拉框和组件方法下拉框

效果展示

封装按钮

tsx 复制代码
import { Button as AntdButton } from 'antd';
import { forwardRef, useImperativeHandle, useState } from 'react';

const Button = (props: any, ref: any) => {

  const [loading, setLoading] = useState(false);

  // 暴露方法,父组件可以使用ref获取组件里暴露出去的方法
  useImperativeHandle(ref, () => {
    return {
      startLoading: () => {
        setLoading(true);
      },
      endLoading: () => {
        setLoading(false);
      },
    }
  }, []);


  return (
    <AntdButton loading={loading} {...props}>{props.children}</AntdButton>
  )
}

export default forwardRef(Button);

改造渲染方法

先定义一个map,存放组件id和组件实例的映射。

动态渲染组件时,注入ref属性,拿到组件实例。

处理事件的地方添加处理组件方法的方法,通过组件id获取到组件实例,然后调用配置的方法。

效果展示

最后

这一篇我们简单的实现了大纲、事件绑定、组件之间联动功能,虽说简单,但是万变不离其宗,后续可以在这基础上去拓展一些其他动作,让其可以实现更复杂的页面和逻辑。

下一篇来实现一下组件属性绑定变量,通过事件修改变量和通过事件动态执行在线脚本,敬请期待。

demo体验地址:dbfu.github.io/lowcode-dem...

demo仓库地址:github.com/dbfu/lowcod...

相关推荐
恋猫de小郭1 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅8 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅10 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊10 小时前
jwt介绍
前端