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

lucky0-0

背景

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

大纲

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

封装一个组件树弹框组件
// 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,点击按钮显示一个消息提示。

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

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

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;

事件数据结构

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

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

代码如下

// 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;

效果展示

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

实现思路

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


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


{
  id: 1,
  name: 'Button',
  props: {
    // 点击事件显示消息
    onClick: () => {
      message.success('点击了按钮');
    }
  }
}

代码实现

渲染时处理事件

效果展示


组件联动

前言

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

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

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

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

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

实现思路

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

实战

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

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

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

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

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

效果展示

封装按钮

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获取到组件实例,然后调用配置的方法。

效果展示

最后

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

阅读量:559

点赞量:0

收藏量:0