欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 艺术 > 【实战】从零开始打造一个低代码平台——9、组件编辑(中)

【实战】从零开始打造一个低代码平台——9、组件编辑(中)

2024/10/23 23:25:40 来源:https://blog.csdn.net/weixin_37760107/article/details/143076743  浏览:    关键词:【实战】从零开始打造一个低代码平台——9、组件编辑(中)

文章目录

  • 前言
  • 1. 更新组件
  • 2. AttrInput
  • 3. 重构属性面板
  • 4. 重构组件
  • 5. 重构画布
  • 6. 组装
  • 总结


前言

此前我们已经支持了拖放组件,但它们仍然无法被更改。通过全局状态管理,现在我们可以实现组件的编辑功能。在本章中,我们将在右侧面板中显示组件的属性,并允许用户更改组件的位置、外观等。

1. 更新组件

在上一章中,我们实现了一个接口addWidget,用于向WidgetStore中添加一个组件。下一步,我们将实现另一个用于更新组件的接口。

复制
import { create } from 'zustand';
import { immer } from 'zustand/middleware/immer';
import { Scr } from '../types/widget';
import { createScr } from '../utils/widget';
import { Widget } from '../types/widget';export interface WidgetState {scrs: Scr[];currScrId?: string;currWidgetId?: string;
}export interface WidgetActions {init: () => void;addScr: (scr: Scr) => void;removeScr: (scrId: string) => void;setCurrScrId: (scrId: string) => void;setCurrWidgetId: (wId: string) => void;addWidget: (widget: Widget) => void;updateWidget: (data: Partial<Widget>) => void;
}export const useWidgetStore = create(immer<WidgetState & WidgetActions>((set) => ({scrs: [],init: () => {set((state) => {state.scrs = [createScr()];state.currScrId = state.scrs[0].id;});},addScr: (scr: Scr) => {set((state) => {state.scrs = [...state.scrs, scr];});},removeScr: (scrId: string) => {set((state) => {if (state.scrs.length > 1) {state.scrs = state.scrs.filter((s) => s.id!== scrId);}if (state.scrs.length > 0) {state.currScrId = state.scrs[0].id;}});},setCurrScrId: (scrId: string) => {set((state) => {state.currScrId = scrId;});},setCurrWidgetId(wId) {set((state) => {state.currWidgetId = wId;});},addWidget: (widget: Widget) => {set((state) => {const scr = state.scrs.find((s) => s.id === state.currScrId);if (scr) {scr.children = [...scr.children, widget];}});},updateWidget: (data: Partial<Widget>) => {set((state) => {const scr = state.scrs.find((s) => s.id === state.currScrId);if (scr) {const widget = scr.children.find((w) => w.id === state.currWidgetId);if (widget) {Object.assign(widget, data);}}});},}))
)

2. AttrInput

我们将定义一个用于通用输入的 React 组件,并将其用于诸如name、text等属性。

import { useWidgetStore } from "../../stores/widget.store";interface AttrInputProps {className?: string;label?: string;name: string;value: string;
}export const AttrInput: React.FC<AttrInputProps> = ({className,label,name,value,
}) => {const updateWidget = useWidgetStore((state) => state.updateWidget);return (<div className={`flex items-center space-x-2 ${className?? ""}`}><label>{label}</label><inputtype="text"value={value}onChange={(e) => {updateWidget({[name]: e.target.value,});}}/></div>);
};

3. 重构属性面板

我们还没有在属性面板上显示任何内容。现在,我们可以使用AttrInput来显示name、text等属性。

import { Widget } from '../types/widget';
import { AttrInput } from './attrsetting/attrinput';interface AttrsProps {className?: string;widget?: Widget;
}export const Attrs: React.FC<AttrsProps> = ({ className, widget }) => {if (!widget) {return null;}const {left,top,width,height,type,name,} = widget;return (<div className={`w-1/5 min-w-[240px] p-2 ${className?? ""}`}><AttrInput label="Name" name="name" value={name} /></div>);
};

4. 重构组件

之前,在我们拖放组件后,我们无法点击并选择任何一个组件(没有任何反应)。我们将重构Button和Container组件以实现选择功能,即当点击一个组件时,通过setCurrWidgetId更改currWidgetId。希望你还记得上一章中的这两个组件。

import { WidgetProps } from ".";
import { useWidgetStore } from "../../stores/widget.store";
import { Button } from "../../types/widget";interface ButtonWidgetProps extends Button {className?: string;
}export const ButtonWidget: React.FC<WidgetProps> = (props) => {const { className, id, left, top, width, height, text } =props as ButtonWidgetProps;const setCurrWidgetId = useWidgetStore((state) => state.setCurrWidgetId);return (<divclassName={`absolute left-3 top-3 w-12 bg-gray-700 rounded-md flex flex-col justify-center items-center p-2 space-y-2 ${className?? ""}`}style={{ left, top, width, height }}onClick={() => {setCurrWidgetId(id);}}>{text}</div>);
};

在ButtonWidget中,当用户点击它时,我们设置currWidgetId。

import { WidgetProps } from ".";
import { useWidgetStore } from "../../stores/widget.store";export const ContainerWidget: React.FC<WidgetProps> = ({className,id,left,top,width,height,
}) => {const setCurrWidgetId = useWidgetStore((state) => state.setCurrWidgetId);return (<divclassName={`absolute left-3 top-3 w-12 bg-gray-700 rounded-md flex flex-col justify-center items-center p-2 space-y-2 ${className?? ""}`}style={{left,top,width,height,}}onClick={() => {setCurrWidgetId(id);}}>Container</div>);
};

在ContainerWidget中,当用户点击它时,我们设置currWidgetId。
咦?这和ButtonWidget的代码完全一样。我们需要为每个组件都编写相同的代码吗?这听起来不太对。别担心,我们以后会进行优化。

5. 重构画布

由于我们需要将一个Widget传递给Attrs,所以我们也需要对Canvas进行一些小的重构。

import { useCurrScr } from '../hooks/useCurrScr';
import { useWidgetStore } from '../stores/widget.store';
import { Attrs } from "./attrs";
import { Canvas } from "./canvas";
import { WidgetBar } from "./widgetbar";interface ContentProps {className?: string;
}export const Content: React.FC<ContentProps> = ({ className }) => {const currWidgetId = useWidgetStore((state) => state.currWidgetId);const { currScr } = useCurrScr();const currWidget = currScr?.children.find((w) => w.id === currWidgetId);return (<div className={`relative h-full ${className?? ""}`}><WidgetBar /><div className="flex h-full"><div className="flex-1 flex justify-center items-center"><Canvas className="w-[360pt] h-[240pt] bg-white" /></div><div className="w-[2px]  h-full bg-gray-950" /><Attrs widget={currWidget} /></div></div>);
};

在Canvas中,我们获取当前组件并将其传递给Attrs。

6. 组装

完成了所有步骤,让我们看看它是如何工作的。
运行应用程序后,你可以看到当你点击一个按钮组件时,右侧面板将显示name属性并允许你进行编辑。当你点击一个容器时,名称将被更改。如果你切换回来选择按钮,更改后的名称将保留。

在这里插入图片描述
在这里插入图片描述


总结

我们迈出了实现组件编辑功能的第一步。这对我们来说是一小步,但对应用程序来说是一大步。从现在开始,我们的应用程序已经支持数据编辑。你可以根据业务需求继续扩展属性。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com