从零实现一套低代码(保姆级教程)【运行时服务】 --- 【1】实现运行时路由页面和动作面板

278 阅读4分钟

摘要

经过之前的十篇文章,我们已经完成了后端服务。从这一篇开始,我们着重于实现运行时的功能。回顾上一篇的内容,我们实现了一个可以存储数据的Form表单。

而这个Form表单有一个submit的事件,我们思考一下,像这种组件的事件,应该在设计态的时候触发吗?

应该是不允许的,因为我们在设计页面的时候,很容易不小心点击到按钮的点击事件等。所以我们需要将这些事件的触发,放在另一个地方。

我们再想一个问题,我们设计好的页面,应该以页面的方式呈现出来,而不应该有那些左边的组件,右边的属性面板。所以需要一个纯展示组件的页面。

这里提供一个正常的思路,我们创建一个项目叫Render,然后这个项目只负责处理接口返回的页面数据,进行展示。但是由于我们这个项目只作为教程,这里我们就不新建一个项目了,只做一个路由来表示运行时的页面。

然后我们的事件,只会在运行时的页面触发,在设计态不允许触发。

1.创建运行时路由并实现

在这里我们使用react-router作为路由管理。 安装:

npm install react-router-dom

安装好后我们创建路由页面,修改App.tsx

import Builder from './pages/builder';
import Render from './pages/render';
import { BrowserRouter, Routes ,Route } from "react-router-dom";


function App() {
  return (
      <BrowserRouter>
        <Routes>
          <Route path="/" Component={Builder} />
        </Routes>
        <Routes>
          <Route path="/render" Component={Render} />
        </Routes>
      </BrowserRouter>
  );
}

export default App;

同时在pages文件夹下,创建render文件夹之后来做render组件的实现。

我们之前在mainPart下做了组件是如何显示的。所以到render组件里面我们只需要复用即可。

import React, { useEffect } from 'react';
import { message } from 'antd';
import Store from '../../store'
import axios from 'axios';
import { ComJson } from '../builder/mainPart';
import * as components from '../builder/leftPart/component'
import { subscribeHook } from '../../store/subscribe';

interface IRenderProps {
}

const Render: React.FunctionComponent<IRenderProps> = (props) => {
    const comList = JSON.parse(JSON.stringify(Store.getState().comList))

    useEffect(() => {
        const search = window.location.search || '';
        const pageId = search.replace('?pageId=', '');
        axios.post('http://localhost:4000/page-json/findPageByID', {
          pageId
        })
        .then(res => {
          if(res.data.data) {
            Store.dispatch({type: 'changeComList', value: res.data.data.pageJson || []})
          }else{
            message.error('获取页面详情失败')
          }
        })
      }, [])

      subscribeHook()

      const getComponent = (com: any) => {
        const Com = components[com.comType as keyof typeof components];
        return <div key={com.comId}>
          <div style={com.style}>
            <Com {...com} >
              {
                com.childList && com.childList.map((item: any) => {
                  return getComponent(item)
                })
              }
            </Com>
          </div>
        </div>
      }

      return (
        <div className='mainCom'>
          {
            comList.map((com: ComJson) => {
              return getComponent(com)
            })
          }
        </div>
      )
};

export default Render;

注意,在render里面,我们不需要drag相关的事件,因为它是在设计态需要的。在运行态,我们纯展示即可。

这里记得之前在AppBuilder项目中,我们留了一个口子,就是预览页面。现在我们可以访问这个路由页面,达到预览的效果了。

image.png

2.修改Form组件的submit事件

现在,我们需要一个方法,用来判断当前页面是不是运行时页面。

const isRender = () => {
  if(window.location.pathname === '/render') {
    return true;
  }else {
    return false;
  }
}

OK,有了这个方法之后,我们去修改Form组件的提交事件,让它只在运行时生效。

  const submit = () => {
    if(!isRender()) {
      return;
    }
    // 其他代码

3.添加动作面板

对于组件来说,右侧有属性面板和样式面板。现在我们想增加一个动作面板,用来绑定动作。比如按钮组件可以绑定点击事件onClick。Input组件可以绑定onChange事件。

至于展示的方式我们只需要以按钮的方式即可。类似于下面这样:

image.png

所以现在我们回到rightPart下: src/pages/builder/rightPart/staticUtils

在staticUtils中增加一个comAction,用来表示每个组件可以绑定的事件类型。

export interface ActionMap {
    [key: string]: string[]
}

const actionMap = {
    Button: ['onClick', 'onMousedown', 'onMouseup']
}

export {
    actionMap
  }

有了这个之后,我们修改rightPart组件: src/pages/builder/rightPart/index.tsx

增加一个获取动作面板的方法:

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

再在右侧增加一个面板:

  const items: TabsProps['items'] = [
    {
      key: 'attributePanel',
      label: <div style={{fontSize:'18px',width:'100px',textAlign:'center'}}>属性</div>,
      children: getAttributePanel(),
    },
    {
      key: 'stylePanel',
      label: <div style={{fontSize:'18px',width:'100px',textAlign:'center'}}>样式</div>,
      children: getStylePanel(),
    },
    {
      key: 'actionPanel',
      label: <div style={{fontSize:'18px',width:'100px',textAlign:'center'}}>动作</div>,
      children: getActionPanel(),
    }
  ];

博主补充

这篇就写到这里吧,可能读者会奇怪,动作面板为什么是一堆按钮呢?因为如果我们想给组件添加事件,那不可避免的就要写代码。所以我们希望,点击动作面板的按钮,可以出现一个给用户写代码的弹窗。

在弹窗里写和动作相关的逻辑。这里我们留到下一篇章去实现。

这部分代码提交在github上
github.com/TeacherXin/…
commit: fix: 第二十一节:实现运行时路由页面和动作面板