# react-lowcode-editor **Repository Path**: huangshaomo/react-lowcode-editor ## Basic Information - **Project Name**: react-lowcode-editor - **Description**: react 低代码编辑器 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2024-10-12 - **Last Updated**: 2025-03-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 低代码的核心原理是围绕 JSON 进行操作,通过 JSON 来描述页面,并将 JSON 与组件建立映射,来渲染页面 ### 核心实现 1. 通过 JSON 描述页面 ```tsx export const useComponentStore = defineStore((set, get) => ({ components: [ { id: "1", name: "Page", desc: "页面", props: {}, children: [], }, ], })); ``` 2. 将 JSON 与我们编写的组件建立映射 ```tsx import Button from "../materials/Button" import Container from "../materials/Container" import Page from "../materials/Page" export const useComponentConfigStore = defineStore((set, get) => ({ componentConfigs: { // 这里是组件名与组件的映射关系 Button: { name: "Button", desc: "按钮", defaultProps: { type: "primary", text: "button" }, component: Button, // 这里是需要映射的组件 setter: [...], stylesSetter: [...] }, } })) ``` 3. 声明一个渲染函数,用户遍历 components,取出对应的映射组件和默认配置来渲染页面 ```tsx // 深度遍历 components, 根据组件名渲染对应的组件 function renderComponents(components: Component[]): React.ReactNode { return components.map((component) => { const config = componentConfigs[component.name]; if (!config?.component) return null; return React.createElement( config.component, { key: component.id, id: component.id, name: component.name, styles: component.styles, ...config.defaultProps, ...component.props, }, renderComponents(component.children || []) ); }); } ``` ### zustand + immer 实现全局不可变数据仓库 ```shell npm install zustand immer ``` ### allotment 实现可调整的布局 ```shell npm install react-alotment ``` 实现步骤 1. 在 main.tsx 引入组件 ```jsx import { HTML5Backend } from "react-dnd-html5-backend"; import { DndProvider } from "react-dnd"; ; ``` ![alt text](image-1.png) 2. 在要拖拽的组件上添加 useDrag, 在放置的组件上添加 useDrop ### 拖拽实现 ```shell npm install react-dbd react-dnd-html5-backend ``` ### 4. 画布区 hover 组件时,出现时高亮效果 ![alt text](image-2.png) 实现思路: 1. 给每个渲染组件都加上唯一的 id 标识 2. 给画布区添加 mouseover 事件 3. 根据移动时鼠标坐标位置,找到最近带有 id 的元素 4. 获取这个元素的位置信息,就是高亮框的位置信息 ### 5. 画布区 Click 组件时,出现操作栏 ![alt text](image-3.png) 实现思路: 1. 给画布区添加 click 事件 2. 根据点击时鼠标坐标位置,找到最近带有 id 的元素 3. 由于点击 id 除了用于展示操作栏,还需要显示对应组件属性配置面板,因此把 id 存储到全局中 难点: 1. 由于遮罩层的层级是固定的,高于组件,因此一旦遮罩层出现,就无法点击组件,因此需要动态调整遮罩层的层级 > 解决方法则是使用 pointerEvents: 'none', 让遮罩层不拦截鼠标事件,但给操作栏添加 pointerEvents: 'auto',让操作栏可以拦截鼠标事件 ### 6. 画布区 Click 组件时, 出现组件属性配置面板 ![alt text](image-4.png) 实现思路: 1. 基础属性来自 components, 用于展示组件的基础信息(ID, name, desc) 2. 配置属性来自 setter, 用于渲染可交互的表单组件,并与 defaultProps 里的属性建立绑定关系 ```tsx componentConfigs: { Button: { name: "Button", desc: "按钮", defaultProps: { type: "primary", text: "button" }, component: Button, // 用于渲染表单组件 setter: [ { name: 'type', // 值对应 defaultProps 中的 type 键 label: '按钮类型', type: 'select', options: [ {label: '主按钮', value: 'primary'}, {label: '次按钮', value: 'default'}, ], }, { name: 'text', // 值对应 defaultProps 中的 text 键 label: '文本', type: 'input', } ] }, } ``` 3. 建立组件配置面板与 antd 组件的映射关系 ```tsx function renderFormElement(setter: ComponentSetter) { const { type, options } = setter; switch (type) { case "input": return ; case "number": return ; case "select": return