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

摘要

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

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

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

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

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

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

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

ant-design.antgroup.com/components/...

在API中看它都可以配置什么属性:

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

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

javascript 复制代码
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

javascript 复制代码
import { iconAttribute } from './comAttribute/iconAttribute'


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

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

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

2.实现Icon组件

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

javascript 复制代码
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返回对应的组件即可。

javascript 复制代码
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

javascript 复制代码
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下新建一个文件夹,用来管理所有的弹窗。

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

javascript 复制代码
import { Modal } from 'antd'

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

XinBuilder2\src\pages\modal\index.ts

javascript 复制代码
import IconSelect from "./IconSelect";

export default {
  IconSelect
}

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

javascript 复制代码
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方法中,我们只需要将这个弹窗进行展示即可:

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

4.实现选择图标弹窗

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

javascript 复制代码
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的官网上,我们先通过脚本将所有的图标爬下来。一个很简单的脚本,只需要到控制台去执行:

javascript 复制代码
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在最下面)

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

javascript 复制代码
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了: 现在我们来修改一下样式。

css 复制代码
.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,现在我们要实现第一个逻辑,点击图标的选中功能: 我们只需要记录一下,当前选中的图标类型,然后给选中的图标加一个背景样式就行了。

javascript 复制代码
  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样式

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。

javascript 复制代码
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上:
github.com/TeacherXin/...
commit: 第七节:实现Icon组件

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

额外提交

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

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

相关推荐
谢尔登5 小时前
【React】事件机制
前端·javascript·react.js
小程xy11 小时前
react 知识点汇总(非常全面)
前端·javascript·react.js
无知的小菜鸡11 小时前
路由:ReactRouter
react.js
zqx_71 天前
随记 前端框架React的初步认识
前端·react.js·前端框架
TonyH20022 天前
webpack 4 的 30 个步骤构建 react 开发环境
前端·css·react.js·webpack·postcss·打包
布列瑟农的星空2 天前
低代码平台实践——代码编辑器
低代码
掘金泥石流2 天前
React v19 的 React Complier 是如何优化 React 组件的,看 AI 是如何回答的
javascript·人工智能·react.js
lucifer3112 天前
深入解析 React 组件封装 —— 从业务需求到性能优化
前端·react.js
colorknight2 天前
1.2.3 HuggingFists安装说明-MacOS安装
人工智能·低代码·macos·huggingface·数据科学·ai agent