从零实现一套低代码(保姆级教程) --- 【7】实现Icon组件

2023-12-27 14:10:34

摘要

在上一篇中,我们在项目中引入了redux。所以改动比较大,也是最复杂的一个章节。那如果你已经对上一节掌握了,那后面也不会有更难的内容了。

因为我们已经把一个整体的架子搭好了,后续只会在这个架子上缝缝补补。来到本系列的第七节内容,如果你是第一次看到这一篇文章, 建议先看一下第一节内容:
从零实现一套低代码(保姆级教程) — 【1】初始化项目,实现左侧组件列表

看到标题,我想读者可能不会理解,为什么实现一个Icon组件,要单独放一个章节呢?
如果你使用过antD,应该知道,对于antD组件,我们虽然用的是Icon组件,但其实是引入Icon下的不同图标组件。

那对于用户来讲,他并不知道,StepBackwardOutlined代表的是左箭头组件,所以在我们的低代码中,要支持用户以界面的形式,去选择图标。

在这里插入图片描述

之前我们实现了Icon组件,虽然只是一个文本,现在,我们来开始实现

在这里插入图片描述

1.实现Icon组件的属性默认配置

那我们怎么知道Icon组件都需要配置什么属性呢?来到AntD的官网:

https://ant-design.antgroup.com/components/icon-cn

在API中看它都可以配置什么属性:
在这里插入图片描述

似乎也没有什么属性,我们就在comAttribute文件夹下,新增一个iconAttribute用来管理Icon组件的属性列表。

XinBuilder2\src\pages\builder\rightPart\staticUtils\comAttribute\buttonAttribute.ts

import { ComAttribute } from "../attributeMap"

const iconAttribute: ComAttribute[] = [
  {
    label: '图标旋转角度',
    value: 'rotate',
    type: 'number'
  },
  {
    label: '是否有旋转动画',
    value: 'spin',
    type: 'switch'
  }
]

export {
  iconAttribute
}

XinBuilder2\src\pages\builder\rightPart\staticUtils\attributeMap.ts

import { iconAttribute } from './comAttribute/iconAttribute'


const attributeMap: AttributeMap = {
  Button: buttonAttribute,
  Input: inputAttribute,
  Icon: iconAttribute
}

在配置的数组里你会发现,我又新增了一个type为number,也就是说当type是number的时候,我应该通过数字框来配置属性,现在我们到InputComponent组件下修改一下:

  const getComponent = () => {
      // 其他代码
      // 返回数字框
      case 'number': {
        return <Input type="number" value={selectNode[value] || ''} style={{width:'120px'}} defaultValue={defaultValue} onChange = {onChange}/>
      }
    }
  }

2.实现Icon组件

OK,针对于属性列表,我们已经完成了。现在我们来到组件里面,对属性进行兼容:
这里我先使用一个房子的Icon,来进行展示:

import { HomeOutlined } from '@ant-design/icons';

export default function Icon(props: any) {
  const { rotate, spin } = props;
  return (
    <div>
      <HomeOutlined rotate={rotate} spin={spin}/>
    </div>
  )
}

OK,现在页面效果就是这样的:

在这里插入图片描述
那我肯定希望,图标的类型是用户自定义的,那我们的组件,就不能使用HomeOutlined作为固定的图标

而是希望,我能从props里面,拿到一个type,值为HomeOutlined,我再根据这个HomeOutlined去渲染对应的图标

我们先不管怎么从props里面拿,先假设props里面有一个type,值就是图标类型,我们的组件应该怎么写?

我们可以通过require直接拿到所有的Icon组件,然后根据type返回对应的组件即可。

export default function Icon(props: any) {
  const { rotate, spin, type } = props;
  // 根据type来返回对应的Icon
  const IconComponent = require('@ant-design/icons')[type || 'HomeOutlined']
  return (
    <div>
      <IconComponent rotate={rotate} spin={spin}/>
    </div>
  )
}

3.实现弹窗类型属性

OK,现在我们的组件已经写好了,只需要解决怎么将type传递给props了。我们要怎么做呢?
当然我们可以用一个输入框,让用户输入Icon的类型,但是用户需要去AntD中取查,图标对应的类型。

所以我们更希望做一个弹窗,让用户自己选择,类似于这样:

在这里插入图片描述

所以,在这里我们还要再加一个属性类型。

回到iconAttribute.ts文件下,我们新增一个弹窗类型:
XinBuilder2\src\pages\builder\rightPart\staticUtils\comAttribute\iconAttribute.ts

import { ComAttribute } from "../attributeMap"

const iconAttribute: ComAttribute[] = [
  {
    label: '图标旋转角度',
    value: 'rotate',
    type: 'number'
  },
  {
    label: '是否有旋转动画',
    value: 'spin',
    type: 'switch'
  },
  // 新增弹窗类型, modalType是弹窗的类型(确定是哪个弹窗)
  {
    label: '选择图标',
    value: 'type',
    type: 'modal',
    modalType: 'IconSelect'
  }
]

export {
  iconAttribute
}

因为我们后面肯定会有其他组件,需要其他的弹窗,所以我们新建一个文件夹来统一管理,就在pages下新建一个文件夹,用来管理所有的弹窗。

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/f05fdec3af2342579d709cc3e0cf9a6b.png

在IconSelect中,我们实现选择选择图标的弹窗。我们就先写一个弹窗,一会在实现。
XinBuilder2\src\pages\modal\IconSelect\index.tsx

import { Modal } from 'antd'

export default function IconSelect() {
  return (
    <div>
      <Modal>
        12345
      </Modal>
    </div>
  )
}

XinBuilder2\src\pages\modal\index.ts

import IconSelect from "./IconSelect";

export default {
  IconSelect
}

那我应该在哪里引入呢,一定是在InputComponent里面,对type === modal的类型,进行引入。

export default function InputComponent(props: any) {

  const { onChange, type, defaultValue, options, selectNode, value, modalType,label } = props
  // 获取组件的弹窗
  const ModalComponent = require('../../../modal')[modalType || 'IconSelect'];
  
  const showModal = () => {
    
  }

  const getComponent = () => {
    switch (type) {
	  // 对于modal类型返回一个Button
      case 'modal': {
        return <Button onClick={showModal} style={{width:'120px'}}>{label}</Button>
      }
    }
  }

  return (
    <div>
      {getComponent()}
      <ModalComponent />
    </div>
  )
}

在showModal方法中,我们只需要将这个弹窗进行展示即可:

  const [openModal, setOpenModal] = useState(false)
  
  const showModal = () => {
    setOpenModal(true)
  }
  
    return (
    <div>
      {getComponent()}
      <ModalComponent  openModal={openModal} setOpenModal=setOpenModal{}/>
    </div>
  )

4.实现选择图标弹窗

OK,现在我们来实现一下选择图标的弹窗,我们先将弹窗的基本样子写出来:

import { Modal } from 'antd'

export default function IconSelect(props: any) {
  const { openModal, setOpenModal } = props;

  const handleOk = () => {
    setOpenModal(false)
  }

  const handleCancel = () => {
    setOpenModal(false)
  }
  return (
    <div>
      <Modal open={openModal} onOk={handleOk} onCancel={handleCancel}>
        12345
      </Modal>
    </div>
  )
}

这样当你点击选择图标后,就可以看到弹窗弹出来了:

在这里插入图片描述

现在我们回到antD的官网上,我们先通过脚本将所有的图标爬下来。一个很简单的脚本,只需要到控制台去执行:

let arr = []
for(let i=0; i<document.getElementsByClassName('ant-badge').length; i++) {
    arr.push(document.getElementsByClassName('ant-badge')[i].innerHTML)
}

在这里插入图片描述

然后把这个arr复制下来,粘贴到IconSelect下的一个json里面。(这里如果大家懒得去弄,就直接在我的github上复制即可,github在最下面)

在这里插入图片描述

到我们的弹窗里面,给他遍历一下:

import { Modal } from 'antd'
// 引入所有的组件类型
import IconList from './iconMap.json'

export default function IconSelect(props: any) {
  return (
    <div>
      <Modal closable={false} open={openModal} onOk={handleOk} onCancel={handleCancel}>
        <div className='iconList'>
          {
            IconList.map(item => {
              const Icon =  require('@ant-design/icons')[item];
              return <div className='iconItem' key={item}>
                <Icon />
              </div>
            })
          }
        </div>
      </Modal>
    </div>
  )
}

现在我们的弹窗就展示了所有的Icon了:
在这里插入图片描述
现在我们来修改一下样式。

.iconList {
  display: flex;
  flex-wrap: wrap;
  height: 400px;
  overflow: auto;
}

.iconItem {
  width:60px;
  height: 60px;
  font-size: 18px;
  display: inline-block;
  text-align: center;
  line-height: 60px;
}

.iconItem:hover {
  background-color: rgb(235, 232, 232);
}

现在我们的弹窗就展示没问题了:
在这里插入图片描述

5.实现弹窗和组件之间的交互

OK,现在我们要实现第一个逻辑,点击图标的选中功能:
我们只需要记录一下,当前选中的图标类型,然后给选中的图标加一个背景样式就行了。

  const [selectIcon, setSelectIcon] = useState('')
    return (
    <div>
      <Modal closable={false} open={openModal} onOk={handleOk} onCancel={handleCancel}>
        <div className='iconList'>
          {
            IconList.map(item => {
              const Icon =  require('@ant-design/icons')[item];
              // 点击选中节点
              return <div onClick={() => {setSelectIcon(item)}} className={selectIcon === item ? 'activeIcon':'iconItem'} key={item}>
                <Icon />
              </div>
            })
          }
        </div>
      </Modal>
    </div>
  )

选中节点的CSS样式

.activeIcon {
  width:60px;
  height: 60px;
  font-size: 18px;
  display: inline-block;
  text-align: center;
  line-height: 60px;
  background-color: rgb(235, 232, 232);
}

最后当我们点击确定的时候,我们要更新对应的组件。怎么更新之前已经说过了,只需要从Store中拿到当前选中的节点,然后更新它的属性,在通过Store.dispatch方法更新Store。

import Store from '../../../store/index'

  const { openModal, setOpenModal } = props;
  const comList = JSON.parse(JSON.stringify(Store.getState().comList))
  const selectCom = Store.getState().selectCom
  const selectNode = comList.find((item: any) => item.comId === selectCom)
  const [selectIcon, setSelectIcon] = useState('')
  
  useEffect(() => {
    setSelectIcon(selectNode.type)
  },[openModal])

  const handleOk = () => {
    selectNode.type = selectIcon;
    Store.dispatch({type: 'changeComList', value:comList})
    setOpenModal(false)
    setSelectIcon('')
  }

  const handleCancel = () => {
    setOpenModal(false)
    setSelectIcon('')
  }

到此,我们就可以更改组件的图标了:

在这里插入图片描述

博主补充

虽然这一篇只是实现了Icon组件,但其实内容也不少:
首先,我们增加了两个类型的属性,一种是number,一种是弹窗。

number很好理解,弹窗的话需要自己处理相关的属性更新。
同时,如果读者想要自己增加一个组件,过程也是一模一样的。后面我也只会挑一些特殊的组件来进行实现,一些普通的可能就只提交在github上了,就不会单独去写一篇文章了。

本章内容会提交在github上:
https://github.com/TeacherXin/XinBuilder2
commit: 第七节:实现Icon组件

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

额外提交

如果你已经实现了上面的内容,你可以看一下antD中的Button组件,它有一个属性是icon,也就是按钮图标。你可不可以用上面的弹窗,给按钮实现这一个属性的配置呢?

这一部分我就不会额外开一个章节,会在github上有一个提交记录:
https://github.com/TeacherXin/XinBuilder2
commit: 第七节:实现Button组件的icon属性

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