从零实现一套低代码(保姆级教程) --- 【4】实现右侧属性面板

2023-12-22 17:16:45

摘要

继画布区的实现之后,来到本系列的第四篇文章,如果你没有看过之前的文章,可以建议先看一下第一篇文章,里面介绍了要实现的项目,是否是你要学习的内容,再决定是否要学习这一些列的文章。
从零实现一套低代码(保姆级教程) — 【1】初始化项目,实现左侧组件列表

在上一篇中,我们实现了画布区的渲染,同时也支持了组件在画布区的随意拖拽布局。
所以我们实现出的低代码项目,在画布区的布局方式是自由布局。

同时在上一篇的结尾,也把Input组件的实现补充了(没有在文章体现,在GitHub上的提交记录可以看到)。

目前我们的项目是长这个样子的。

在这里插入图片描述
那如果我们只能拖拽组件,并不能对组件进行修改,那这个低代码就很无用了,所以这一篇。我们主要来实现右侧属性面板。

能够通过可视化的配置,来修改组件的属性。比如我想通过一个开关,来控制按钮的显示与隐藏,通过一个输入框,来控制按钮的文本。

OK,垃圾话就不说太多了,我们开始实现这一部分内容。

在这里插入图片描述

1.实现右侧面板结构

现在请读者打开rightPart下的index.tsx文件;

对于右侧的属性面板,我们可以参考左侧的面板,样式都一样,只不过定位后,属性面板的right值应该为0。

同时我们要思考一个问题,在项目的后期,我们可能不止可以修改组件的属性,也可以修改组件的样式,或者其他配置。所以我们可以在右侧的面板上,实现一个多页签。当然现在,我们只实现属性的面板。

import './index.css'
import { Tabs } from 'antd';
import type { TabsProps } from 'antd';

export default function RightCom() {

  const getAttributePanel = () => {
    return <div></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: 'Content of Tab Pane 2',
    }
  ];

  const onChange = () => {
    
  }

  return (
    <div className='rightCom'>
      <Tabs defaultActiveKey="1" items={items} onChange={onChange} />
    </div>
  )
}

.rightCom {
  width: 20%;
  position: absolute;
  right: 0;
  top: 10%;
  background-color: white;
  height: 90%;
}

我们实现出来的效果就是:

在这里插入图片描述

写到现在,是不是已经有点低代码的样子了,已经像回事了是吧。不要开心太早,后面还有很多的内容需要写。。。。 ̄□ ̄||

2.实现组件的点击选中

那我们想一下,对于右侧的属性面板。我们在什么时机去展示呢

在网站的例子上,大家可以尝试一下,我在第一版实现的时候,是通过右键组件,然后选择设置属性,就会展示属性面板。

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
XinBuilder 点击跳转

但是我觉得这种方式不是很友好,很多第一次打开的人不知道。所以在这个版本我准备换一种实现思路,点击组件的时候直接触发属性面板的显示。

OK,那我们现在就要回到mainPart中了,我的点击事件写在哪里呢?记得之前拖拽的事件写在哪了吗,我们就写在那里!!!!但是为了结构比较清晰,还是在外面包一层div用来转本展示选中的样式。

第二个问题是,在画布区我们怎么知道选中的是哪个组件呢?我们可以将组件的border设置为蓝色,这样我们就可以知道选中的是哪一个组件了!

这个时候,为了保证组件的唯一性,你就会发现,我们必须要给组件一个ID了,我们用时间戳来给组件一个comId。
interface ComJson {
  comType: string,
  // 组件的唯一ID
  comId: string,
  style?: any
}

// 当前选中节点的comId
const [selectId, setSelectId] = useState<string>('')

const onDrop = (e: any) => {
// 其他代码
   if(window.nowCom === 'renderCom' && dragCom && dragCom.style) {
	// 其他代码
   }else{
  // 其他代码
     comList.push({
       comType: window.nowCom,
       style,
       // 通过时间戳生成comId
      let comId = `comId_${Date.now()}`
      comList.push({
        comType: window.nowCom,
        style,
        comId
      })
      setSelectId(comId)
     })
   }
   setComList([...comList])
}
  const selectCom = (com: ComJson) => {
    return () => {
      // 点击事件设置选中节点的ID
      setSelectId(com.comId)
    }
  }
  
  return (
    <div onDrop={onDrop} onDragOver={onDragOver} onDragEnter={onDragEnter} className='mainCom'>
      {
        comList.map(com => {
          const Com = components[com.comType as keyof typeof components];
          // 这里可以把key加上了
          return <div key={com.comId} onClick={selectCom(com)} draggable onDragStart={onDragStart(com)}>
          	// 触发点击事件,新加的div
            <div className={com.comId === selectId ? 'selectCom' : ''} style={com.style}>
              <Com />
            </div>
          </div>
        })
      }
    </div>
  )
.selectCom{
  border: 0.5px solid #1677ff;
}


这里一定要注意的是,我把Com的style提到了外层div上,因为这个style里面包含的内容主要是和位置相关的,我们没必要将其挂载在组件上。一定要修改一下,不然你的项目会有问题的,一拖拽就把其他的元素带起来

现在我们就实现了画布区节点选中的状态。

在这里插入图片描述

3.渲染右侧属性面板

当我们在画布区选中某个节点的时候,要怎么通知右侧属性面板展示对应组件的属性配置呢?
这就涉及到跨组件传递数据的内容了。

其实从左侧的组件到画布区这一个过程,我就提到过,后面我们会用redux来进行状态管理,而全量的组件,正适合在redux中进行管理。但是和redux相关的内容,我会专门用一章节去实现,所以现在,我们依旧先挂载在window上。

  const selectCom = (com: ComJson) => {
    return () => {
      setSelectId(com.comId);
      // 挂载在window上,后面会使用redux进行替换
      window.renderCom = com;
      window.comList = comList;
      window.setComList = setComList
    }
  }

我们先将当前组件,以及触发画布区重绘的方法挂载在window上。

现在我们回到rightPart下。我们在右侧面板先只展示一个输入框,那这个输入框用来干什么呢?用来更改按钮的文字!!!!!

  const getAttributePanel = () => {
    return <div>
      <div className='attributeItem'>
        <label>按钮文字:</label>
        <div className='attributeItemValue'>
          <Input onChange={changeComAttribute} />
        </div>
      </div>
    </div>
  }

  const changeComAttribute = () => {

  }
.attributeItem{
  width: 200px;
  height: 60px;
  display: flex;
  justify-content: space-between;
}

OK。现在我们在onchange方法里面,更改当前组件的按钮文字。这里我们起一个caption名称

  const changeComAttribute = (e:any) => {
    window.renderCom.caption = e.target.value;
    window.setComList([...window.comList])
  }

更改后,我们要在mainPart中,将这个属性传递给组件Com。
在mainPart下的index.tsx中:

    <div onDrop={onDrop} onDragOver={onDragOver} onDragEnter={onDragEnter} className='mainCom'>
      {
        comList.map(com => {
          const Com = components[com.comType as keyof typeof components];
          return <div key={com.comId} onClick={selectCom(com)} draggable onDragStart={onDragStart(com)}>
            <div className={com.comId === selectId ? 'selectCom' : ''} style={com.style}>
              // 直接通过结构,将属性传给Com
              <Com {...com}/>
            </div>
          </div>
        })
      }
    </div>

最后我们来到组件:

import { Button as AntButton } from 'antd'

export default function Button(props: any) {
  const {caption} = props
  return (
    <div>
      <AntButton>{caption || '按钮'}</AntButton>
    </div>
  )
}

拿到caption后,去渲染按钮的文字。

这里要注意的是,因为刚刚我把style属性提到了外层的div上,所以在组件这里,就不要再接受style了!!!!!!!!!!!!!!!!

OK,到这里,右侧的属性面板,就可以更改按钮的文字了。

在这里插入图片描述

本章内容会提交在github上:
https://github.com/TeacherXin/XinBuilder2
commit: 第四节:实现右侧属性面板

如果可以的话,可以给博主的GitHub点亮一颗小星星(?▽?)

博主补充

本篇文章主要是为了帮助大家串通属性面板和组件之间的机制,主要还是清楚右侧改变的属性是如何映射到组件上的。

而在下一篇,我会主要实现右侧属性面板。比如,对于按钮来说,需要什么样的属性配置,对于Input框来说,需要什么样的属性配置。应该怎么去写代码,都会在下一章节继续完善。

文章来源:https://blog.csdn.net/weixin_46726346/article/details/135151929
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。