摘要
在上一篇中,我们实现了Button组件的动作面板,其实也就是几个按钮。
我们希望,点击动作面板里的按钮时,有一个弹窗。可以提供编辑代码的能力,比如我想点击按钮的时候,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编辑弹窗了。
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组件的协议应该是这样的:
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,现在我们来到运行时的页面,就可以点击按钮从而触发事件了。
博主补充
这样我们就将事件处理的过程串通了。后面读者可以再继续添加其他组件的事件,只需要按照这个流程走一遍即可。
可能读者会有疑问,组件和组件之间的交互应该如何去做呢?后面我们会继续说这个事情。
这部分代码提交在github上
github.com/TeacherXin/…
commit: fix: 第二十二节:实现Button组件的事件处理