从零实现一套低代码(保姆级教程)【运行时服务】 --- 【2】实现Button组件的事件处理

246 阅读4分钟

摘要

在上一篇中,我们实现了Button组件的动作面板,其实也就是几个按钮。 image.png

我们希望,点击动作面板里的按钮时,有一个弹窗。可以提供编辑代码的能力,比如我想点击按钮的时候,alert(123)。这段代码我希望可以写进去。所以我们需要一个JS编辑器的组件 。

这里我们直接去github上找一个现成的。用的是react-race组件:

GitHub: github.com/securingsin…

这里我们在项目中安装一下这个组件:

npm install react-ace ace-builds

有了这些准备之后,我们就可以开始实现事件系统里。

1.实现action弹窗

因为我们要实现一个JS编辑器弹窗,所以我们在modal文件夹下,新增一个editAction弹窗。 在弹窗里面,我们直接引入react-race,并且做出一定的配置(这里读者可以自行修改配置)。

import React, { useState } from 'react'
import './index.css'
import {Modal} from 'antd'
// 核心组件
import AceEditor from 'react-ace'
// 引入对应的语言
import 'ace-builds/src-noconflict/mode-javascript'
//以下import的是风格
import 'ace-builds/src-noconflict/theme-eclipse'
// 代码提示
import 'ace-builds/src-noconflict/ext-language_tools'
import 'ace-builds/webpack-resolver'

interface IEditAction {
    showAction: boolean
    actionName: string
    setShowAction: any
}

const EditAction: React.FunctionComponent<IEditAction> = (props: IEditAction) => {
    const { showAction,actionName, setShowAction } = props
    const [ actionJs, setActionJs ] = useState('')

    const onOk = () => {
        setShowAction(false)
    }

    const onCancel = () => {
        setShowAction(false)
    }

    return (
      <Modal
        open={showAction}
        onOk={onOk}
        width={600}
        onCancel={onCancel}
        closable={false}
      >
        <AceEditor
          name='editAction'
          mode='javascript'
          theme='github'
          setOptions={{
            // 基础的自动完成
            enableBasicAutocompletion: true,
            // 实时自动完成
            enableLiveAutocompletion: true,
            // 代码块
            enableSnippets: true,
            // 显示行号
            showLineNumbers: true,
            // tab键两个空格
            tabSize: 2
          }}
          onChange={setActionJs}
          value={actionJs}
        />
      </Modal>
    )
};

export default EditAction;

以上就是一个基础的编辑弹窗了,弹窗内部维护了actionJs,也就是在编辑器里面写的代码。

我们知道,是当我点击某个组件的某个事件时,这个弹窗才会出现。所以我们需要从props里面拿到对应的事件类型,比如是onClick类型或者是onChange类型。

之后,我们要给动作面板里的按钮,绑定一个点击事件,可以控制弹窗的显示:

  const getActionPanel = () => {
    const comType = selectNode?.comType || '';
    return <div className='actionPanel'>
      {
        (actionMap?.[comType as keyof typeof actionMap] || []).map((item, index) => {
          return <Button onClick={showActionModal(item)} key={index}>{item}</Button>
        })
      }
    </div>
  } 


  const showActionModal = (action: string) => {
    return () => {
      setShowAction(true);
      setActionName(action);
    }
  }

这样当我点击右侧动作面板的按钮时,就会出现对应的JS编辑弹窗了。

image.png

2.给Button组件添加事件协议

现在我们有了JS编辑弹窗之后,我们希望的是,保存之后,可以将写的代码保存下来。应该保存在什么位置呢?

其实正常来讲。这部分应该直接给后端存储,但是这里为了方便,我们就直接存在组件的协议里面。key就是事件类型,value就是事件代码。

现在我们实现一下:

    const comList = JSON.parse(JSON.stringify(Store.getState().comList))
    const selectCom = Store.getState().selectCom
    const selectNode = getComById(selectCom, comList)
    const [ actionJs, setActionJs ] = useState('')

	useEffect(() => {
        if(selectNode?.[actionName]) {
            setActionJs(selectNode?.[actionName])
        }else {
            setActionJs('')
        }
    }, [showAction])

    const onOk = () => {
        setShowAction(false);
        if(selectNode) {
            selectNode[actionName] = actionJs;
        }
        Store.dispatch({type: 'changeComList', value:comList})
    }

实现起来很简单,只需要给当前组件加一个属性之后,更新redux的值即可。

当我们写了事件之后,Button组件的协议应该是这样的:

image.png

3.Button组件处理事件

现在我们已经有了按钮的事件代码,以字符串的形式存在了协议里面。现在我们需要修改按钮组件,拿到对应的事件,在运行时触发。

import { Button as AntButton } from 'antd'
import { isRender } from '../../../../../../utils/nodeUtils';

export default function Button(props: any) {
  const { caption, danger, disabled, ghost, shape, size, type, comStyle, onClick } = props
  const IconComponent = require('@ant-design/icons')[type]

  const onclick = () => {
    if(!isRender()){
      return;
    }
    try {
      const fun = new Function(onClick);
      fun()
    } catch (error) {
      console.error(error);
    }
  }

  return (
    <div>
      <AntButton
        style={{...comStyle}}
        danger={danger}
        disabled={disabled}
        ghost={ghost}
        shape={shape}
        size={size}
        icon={type ?  <IconComponent /> : null}
        onClick={onclick}
        >
          {caption || '按钮'}
      </AntButton>
    </div>
  )
}

OK,现在我们来到运行时的页面,就可以点击按钮从而触发事件了。

image.png

博主补充

这样我们就将事件处理的过程串通了。后面读者可以再继续添加其他组件的事件,只需要按照这个流程走一遍即可。

可能读者会有疑问,组件和组件之间的交互应该如何去做呢?后面我们会继续说这个事情。

这部分代码提交在github上
github.com/TeacherXin/…
commit: fix: 第二十二节:实现Button组件的事件处理