100202Title和Input组件_编辑器-react-仿低代码平台项目

文章目录

    • 1 开发两个问卷组件
      • 1.1 Title组件
      • 1.2 Input组件
      • 1.3 画布静态展示TItle和Input
    • 2 Ajax获取问卷数据,并存储到Redux store
      • 2.1 API接口
      • 2.2 组件列表存储到Redux store统一管理
      • 2.3 重构useLoadQuestionData
    • 3 在画布显示问卷列表,点击可选中
      • 3.1 Redux获取组件列表
      • 3.2 根据Redux组件列表渲染组件
      • 3.3 点击选择组件,共享selectedId
    • 关于

1 开发两个问卷组件

1.1 Title组件

数据类型和默认值,interface.ts代码如下:

export type QuestionTitlePropsType = {text?: string;level?: 1 | 2 | 3 | 4 | 5;isCenter?: boolean;
};export const QuestionTitleDefaultProps: QuestionTitlePropsType = {text: "一行标题",level: 1,isCenter: false,
};

组件Component.tsx代码如下所示:

import { FC } from "react";
import { Typography } from "antd";
import { QuestionTitlePropsType, QuestionTitleDefaultProps } from "./interface";const { Title } = Typography;const QuestionTitle: FC<QuestionTitlePropsType> = (props: QuestionTitlePropsType,
) => {const {text = "",level = 1,isCenter = false,} = { ...QuestionTitleDefaultProps, ...props };const genFontSize = (level: number) => {if (level === 1) {return "24px";} else if (level === 2) {return "20px";} else if (level === 3) {return "16px";} else if (level === 4) {return "12px";} else {return "16px";}};return (<Titlelevel={level}style={{textAlign: isCenter ? "center" : "start",marginBottom: "0",fontSize: genFontSize(level),}}>{text}</Title>);
};export default QuestionTitle;

1.2 Input组件

数据类型和默认值,interface.ts代码如下:

export type QuestionInputPropsType = {title?: string;placeholder?: string;
};export const QuestionInputDefaultProps: QuestionInputPropsType = {title: "输入框标题",placeholder: "请输入...",
};

组件Component.tsx代码如下所示:

import { FC } from "react";
import { Typography, Input } from "antd";
import { QuestionInputPropsType, QuestionInputDefaultProps } from "./interface";const { Paragraph } = Typography;const QuestionInput: FC<QuestionInputPropsType> = (props: QuestionInputPropsType,
) => {const { text = "", placeholder = "" } = {...QuestionInputDefaultProps,...props,};return (<div><Paragraph strong>{text}</Paragraph><div><Input placeholder={placeholder}></Input></div></div>);
};export default QuestionInput;

1.3 画布静态展示TItle和Input

组件EditCanvas.tsx代码如下:

import { FC } from "react";
import styles from "./EditCanvas.module.scss";import { Spin } from "antd";import QuestionTitle from "@/components/QuestionComponents/QuestionTitle/Component";
import QuestionInput from "@/components/QuestionComponents/QuestionInput/Component";type PropsType = {loading: boolean;
};const EditCanvas: FC<PropsType> = ({ loading }) => {if (loading) {return (<div style={{ textAlign: "center", marginTop: "24px" }}><Spin /></div>);}return (<div className={styles.canvas}><div className={styles["component-wrapper"]}><div className={styles.component}><QuestionTitle /></div></div><div className={styles["component-wrapper"]}><div className={styles.component}><QuestionInput /></div></div></div>);
};export default EditCanvas;

样式EditCanvas.module.scss代码如下:

.canvas {min-height: 100%;background-color: #fff;overflow: hidden;
}.component-wrapper {margin: 12px;border: 1px solid #fff;padding: 12px;border-radius: 3px;&:hover {border-color: #d9d9d9;}
}.component {pointer-events: none; // 屏蔽鼠标行为,组件不让被点击到
}

效果如下图所示:

在这里插入图片描述

2 Ajax获取问卷数据,并存储到Redux store

2.1 API接口

获取问卷详情API接口如下:

{// 获取问卷信息url: '/api/question/:id',method: 'get',response() {return {errno: 0,data: {id: Random.id(),title: Random.ctitle(),componentList: [{fe_id: Random.id(),type: 'questionTitle',title: '标题',props: {text: '个人信息调研',level: 1,isCenter: false}},{fe_id: Random.id(),type: 'questionInput',title: '输入框',props: {text: '你的姓名',placeholder: '请输入姓名...',}},{fe_id: Random.id(),type: 'questionInput',title: '输入框',props: {text: '你的电话',placeholder: '请输入电话...',}},]},}}}

2.2 组件列表存储到Redux store统一管理

src/store/componentsReducer/index.ts代码如下:

import { createSlice, PayloadAction } from "@reduxjs/toolkit";import { ComponentPropsType } from "@/components/QuestionComponents";export type ComponentInfoType = {fe_id: string;type: string;title: string;props: ComponentPropsType;
};export type ComponentsStateType = {componentList: Array<ComponentInfoType>;
};const INIT_STATE: ComponentsStateType = {componentList: [],// 其他扩展
};export const componentsSlice = createSlice({name: "components",initialState: INIT_STATE,reducers: {// 重置所有组件resetComponents(state: ComponentsStateType,action: PayloadAction<ComponentsStateType>,) {return action.payload;},},
});export const { resetComponents } = componentsSlice.actions;
export default componentsSlice.reducer;

组件属性类型src/components/QuestionComponents/index.ts代码如下:

import { FC } from "react";import QuestionInputConf, { QuestionInputPropsType } from "./QuestionInput";
import QuestionTitleConf, { QuestionTitlePropsType } from "./QuestionTitle";// 各个组件属性类型
export type ComponentPropsType = QuestionInputPropsType &QuestionTitlePropsType;// 统一组件配置
export type ComponentConfType = {title: string;type: string;Component: FC<ComponentPropsType>;defaultProps: ComponentPropsType;
};// 全部组件配置列表
const componentConfList: ComponentConfType[] = [QuestionInputConf,QuestionTitleConf,
];// 根据组件类型获取组件
export function getComponentConfByType(type: string) {return componentConfList.find((c) => c.type === type);
}

2.3 重构useLoadQuestionData

代码如下所示:

import { useParams } from "react-router-dom";
import { useRequest } from "ahooks";
import { getQuestionApi } from "@/api/question";
import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { resetComponents } from "@/store/componentsReducer";/*** 获取带加载状态的问卷信息* @returns loading状态,问卷信息*/
function useLoadQuestionData() {const { id = "" } = useParams();const dispatch = useDispatch();// ajax 加载const { data, loading, error, run } = useRequest(async (id: string) => {if (!id) {throw new Error("没有问卷 id");}const data = await getQuestionApi(id);return data;},{manual: true,},);// 根据获取的data,设置storeuseEffect(() => {if (!data) {return;}const { componentList = [] } = data;// componentList 存入redux storedispatch(resetComponents({ componentList }));// eslint-disable-next-line react-hooks/exhaustive-deps}, [data]);// 根据id变化,加载问卷数据useEffect(() => {run(id);// eslint-disable-next-line react-hooks/exhaustive-deps}, [id]);return { loading, error };
}export default useLoadQuestionData;

3 在画布显示问卷列表,点击可选中

3.1 Redux获取组件列表

自定义hook-useGetComponentInfo.ts 代码如下所示:

import { StateType } from "@/store";
import { useSelector } from "react-redux";
import { ComponentsStateType } from "@/store/componentsReducer";function useGetComponentInfo() {const components = useSelector<StateType>((state) => state.components,) as ComponentsStateType;const { componentList = [] } = components;return { componentList };
}export default useGetComponentInfo;

3.2 根据Redux组件列表渲染组件

EditCanvas.tsx代码如下所示:

import { FC } from "react";
import styles from "./EditCanvas.module.scss";import { Spin } from "antd";// import QuestionTitle from "@/components/QuestionComponents/QuestionTitle/Component";
// import QuestionInput from "@/components/QuestionComponents/QuestionInput/Component";
import useGetComponentInfo from "@/hooks/useGetComponentInfo";
import { getComponentConfByType } from "@/components/QuestionComponents";
import { ComponentInfoType } from "@/store/componentsReducer";type PropsType = {loading: boolean;
};function genComponent(componentInfo: ComponentInfoType) {const { type, props } = componentInfo;const componentConf = getComponentConfByType(type);if (componentConf == null) {return null;}const { Component } = componentConf;return <Component {...props} />;
}const EditCanvas: FC<PropsType> = ({ loading }) => {const { componentList = [] } = useGetComponentInfo();if (loading) {return (<div style={{ textAlign: "center", marginTop: "24px" }}><Spin /></div>);}return (<div className={styles.canvas}>{componentList.map((c) => {const { fe_id } = c;return (<div key={fe_id} className={styles["component-wrapper"]}><div className={styles.component}>{genComponent(c)}</div></div>);})}{/* <div className={styles["component-wrapper"]}><div className={styles.component}><QuestionTitle /></div></div><div className={styles["component-wrapper"]}><div className={styles.component}><QuestionInput /></div></div> */}</div>);
};export default EditCanvas;

3.3 点击选择组件,共享selectedId

componentsReducer/index.ts 添加selectId及修改,代码如下:

import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { produce } from "immer";
import { ComponentPropsType } from "@/components/QuestionComponents";export type ComponentInfoType = {fe_id: string;type: string;title: string;props: ComponentPropsType;
};export type ComponentsStateType = {selectedId: string;componentList: Array<ComponentInfoType>;
};const INIT_STATE: ComponentsStateType = {selectedId: "",componentList: [],// 其他扩展
};export const componentsSlice = createSlice({name: "components",initialState: INIT_STATE,reducers: {// 重置所有组件resetComponents(state: ComponentsStateType,action: PayloadAction<ComponentsStateType>,) {return action.payload;},// 切换选中组件changeSelectedId: produce((draft: ComponentsStateType, action: PayloadAction<string>) => {draft.selectedId = action.payload;}),},
});export const { resetComponents, changeSelectedId } = componentsSlice.actions;
export default componentsSlice.reducer;

新增选中 css 样式,EditCanvas.module 新增代码如下所示:

.selected {border-color: #1890ff !important;
}

组件点击选择,点击画布空白处取消,阻止默认冒泡行为,EditCanvas.tsx代码如下:

import { FC } from "react";
import styles from "./EditCanvas.module.scss";import { Spin } from "antd";// import QuestionTitle from "@/components/QuestionComponents/QuestionTitle/Component";
// import QuestionInput from "@/components/QuestionComponents/QuestionInput/Component";
import useGetComponentInfo from "@/hooks/useGetComponentInfo";
import { getComponentConfByType } from "@/components/QuestionComponents";
import { ComponentInfoType } from "@/store/componentsReducer";
import { useDispatch } from "react-redux";
import classNames from "classnames";
import { changeSelectedId } from "@/store/componentsReducer";type PropsType = {loading: boolean;
};function genComponent(componentInfo: ComponentInfoType) {const { type, props } = componentInfo;const componentConf = getComponentConfByType(type);if (componentConf == null) {return null;}const { Component } = componentConf;return <Component {...props} />;
}const EditCanvas: FC<PropsType> = ({ loading }) => {const { componentList = [], selectedId } = useGetComponentInfo();const dispatch = useDispatch();// 获取当前选中的组件function handleClick(e: React.MouseEvent<HTMLDivElement>, fe_id: string) {e.stopPropagation();dispatch(changeSelectedId(fe_id));}if (loading) {return (<div style={{ textAlign: "center", marginTop: "24px" }}><Spin /></div>);}return (<div className={styles.canvas}>{componentList.map((c) => {const { fe_id } = c;// 拼接 class nameconst wrapperDefaultClassName = styles["component-wrapper"];const selectedClassName = styles.selected;const wrapperClassName = classNames({[wrapperDefaultClassName]: true,[selectedClassName]: fe_id === selectedId,});return (<div key={fe_id} className={wrapperClassName} onClick={(e) => handleClick(e, fe_id)}><div className={styles.component}>{genComponent(c)}</div></div>);})}{/* <div className={styles["component-wrapper"]}><div className={styles.component}><QuestionTitle /></div></div><div className={styles["component-wrapper"]}><div className={styles.component}><QuestionInput /></div></div> */}</div>);
};export default EditCanvas;

默认selectId为组件列表第一个,没有不选中,useLoadQuestionData.ts代码如下所示:

import { useParams } from "react-router-dom";
import { useRequest } from "ahooks";
import { getQuestionApi } from "@/api/question";
import { useEffect } from "react";
import { useDispatch } from "react-redux";
import { resetComponents } from "@/store/componentsReducer";/*** 获取带加载状态的问卷信息* @returns loading状态,问卷信息*/
function useLoadQuestionData() {const { id = "" } = useParams();const dispatch = useDispatch();// ajax 加载const { data, loading, error, run } = useRequest(async (id: string) => {if (!id) {throw new Error("没有问卷 id");}const data = await getQuestionApi(id);return data;},{manual: true,},);// 根据获取的data,设置storeuseEffect(() => {if (!data) {return;}const { componentList = [] } = data;// 获取默认的 selectedIdlet selectedId = "";if (componentList.length > 0) {selectedId = componentList[0].fe_id;}// componentList 存入redux storedispatch(resetComponents({ componentList, selectedId }));// eslint-disable-next-line react-hooks/exhaustive-deps}, [data]);// 根据id变化,加载问卷数据useEffect(() => {run(id);// eslint-disable-next-line react-hooks/exhaustive-deps}, [id]);return { loading, error };
}export default useLoadQuestionData;

效果如下图所示:

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

在这里插入图片描述

关于

❓QQ:806797785

⭐️仓库地址:https://gitee.com/gaogzhen

⭐️仓库地址:https://github.com/gaogzhen

[1]react官网[CP/OL].

[2]Redux官网[CP/OL].

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/93591.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/93591.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设置计划任务自动备份mysql

windows系统下1.创建mysql自动备份脚本mysqlback.bat需将此脚本存放在mysql的bin文件夹下。确保此脚本执行成功了在进行第2步做计划任务。echo off REM 定义备份目录backup_dir、备份的文件名filename set "backup_dirD:\mysqlback" set "filenamemysqlback_%da…

飞机起落架轮轴深孔中间段电解扩孔内轮廓检测 - 激光频率梳 3D 轮廓检测

摘要&#xff1a;飞机起落架轮轴深孔中间段电解扩孔内轮廓检测存在精度要求高、结构复杂等挑战。本文针对电解扩孔特殊工艺特征&#xff0c;探讨激光频率梳 3D 轮廓检测技术的应用&#xff0c;分析其检测原理、技术优势及在轮轴深孔检测中的实践&#xff0c;为电解扩孔内轮廓高…

【软考中级网络工程师】知识点之入侵防御系统:筑牢网络安全防线

目录一、入侵防御系统基础概念1.1 定义与作用1.2 与其他安全设备的关系二、入侵防御系统工作原理剖析2.1 数据包捕获与预处理2.2 深度包检测&#xff08;DPI&#xff09;技术2.3 威胁特征匹配2.4 行为分析与机器学习辅助检测2.5 威胁处理与响应机制三、入侵防御系统功能全面解析…

Python爬虫实战:研究scrapfly-scrapers库,构建电商/新闻/社交媒体数据采集系统

1. 引言 1.1 研究背景与意义 在大数据与人工智能技术深度渗透各行业的背景下,数据已成为企业决策、学术研究、产品创新的核心驱动力。互联网作为全球最大的信息载体,蕴含海量结构化与非结构化数据(如电商商品信息、新闻资讯、社交媒体动态等),其价值挖掘依赖高效的数据采…

Python爬虫反爬检测失效问题的代理池轮换与请求头伪装实战方案

Python爬虫反爬检测失效问题的代理池轮换与请求头伪装实战方案 &#x1f31f; Hello&#xff0c;我是摘星&#xff01; &#x1f308; 在彩虹般绚烂的技术栈中&#xff0c;我是那个永不停歇的色彩收集者。 &#x1f98b; 每一个优化都是我培育的花朵&#xff0c;每一个特性都是…

【原理】C#构造函数可以标记为Static吗

【从UnityURP开始探索游戏渲染】专栏-直达 实例构造函数&#xff08;Instance Constructor&#xff09;不能标记为static但C#提供了一种特殊的‌ 静态构造函数&#xff08;Static Constructor&#xff09;专门用于初始化静态成员。下面依次介绍他们&#xff1a; 1. ‌实例构造…

数据结构--树(3)

数据结构基础&#xff08;13&#xff09; 文章目录数据结构基础&#xff08;13&#xff09;--树树的存储结构树的存储方式1&#xff1a;双亲表示法&#xff08;顺序存储&#xff09;树的存储方式2&#xff1a;孩子表示法树的存储方式3&#xff1a;孩子兄弟表示法树转二叉树森林…

sys.stdin读取键盘输入【持续更新~】

背景sys.stdin主要用来读取键盘的一行或者多行输入&#xff0c;读取后表达形式为字符串。下文主要探讨sys.stdin.readline()的使用&#xff0c;sys.stdin.read()参考&#xff1a;sys.stdin.readline()是逐行读取&#xff0c;通常会配合.strip()清除首尾的换行符/空格sys.stdin.…

近阈值技术引领者:STM32U3系列的能效与安全革新

引言 当电池供电设备已深度融入生活的每一个角落&#xff0c;功耗控制与续航能力俨然成为制约技术演进的核心瓶颈。在此背景下&#xff0c;超低功耗新系列STM32U3凭借前沿的近阈值设计理念&#xff0c;为受功耗瓶颈限制的设备提供了突破性解决方案&#xff0c;也为能耗管理开启…

Vue3 中的 provide 和 inject 详解:实现跨组件通信

一、provide 和 inject 概述在 Vue3 中&#xff0c;provide 和 inject 是一对用于实现跨层级组件通信的 API&#xff0c;它们解决了 props 需要逐层传递的繁琐问题。1.1 基本概念provide (提供)&#xff1a;在祖先组件中提供数据inject (注入)&#xff1a;在任意后代组件中注入…

Kafka 零拷贝(Zero-Copy)技术详解

文章目录1. 什么是零拷贝2. Kafka 如何实现零拷贝2.1 sendfile 系统调用2.2 mmap 内存映射3. 传统拷贝 vs 零拷贝3.1 传统文件传输流程3.2 零拷贝文件传输流程4. Kafka 零拷贝的具体实现4.1 消息消费时的零拷贝4.2 日志段文件的零拷贝5. 零拷贝带来的性能优势6. 零拷贝的适用场…

Vue 中 v-for 的使用及 Vue2 与 Vue3 的区别

v-for 基本用法v-for 是 Vue 中用于循环渲染列表的指令&#xff0c;基本语法如下&#xff1a;运行<!-- Vue2 和 Vue3 通用基本语法 --> <div v-for"(item, index) in items" :key"item.id">{{ index }} - {{ item.name }} </div>Vue2 和…

本地搭建dify+deepseek智能体

今天开始搭建智能体&#xff0c;学习一下&#xff0c;也是公司转型所需。(Windows下的docker安装给我差点干破防了&#xff0c;安装了一周docker才成功。我真就要放弃的时候&#xff0c;又意外成功了/(ㄒoㄒ)/~~)0、准备阶段 配置Windows10的基本配置。 按下键盘Windows键&…

网络常识-SSE对比Websocket

SSE&#xff08;Server-Sent Events&#xff09;和Websocket都是用于实现服务器与客户端实时通信的技术&#xff0c;但它们的设计理念、通信模式和适用场景有显著区别。以下从核心差异和适用场景两方面具体说明&#xff1a; 一、核心区别维度SSE&#xff08;Server-Sent Events…

lamp架构部署wordpress

CentOS 7主机&#xff1a;lamp.example.comIP&#xff1a;192.168.100.101、关闭防火墙与selinux# 关闭防火墙systemctl stop firewalldsystemctl disable firewalld# 关闭selinuxvim /etc/selinux/config # 或vim /etc/sysconfig/selinuxSELINUXdisabled:wq# 重启reboot 2、开…

DC6v-36V转3.2V1A恒流驱动芯片WT7017

DC6v-36V转3.2V1A恒流驱动芯片WT7017WT7017是一款于连续工作模式下的降压LED恒流转换器&#xff0c;可驱动单只或多只LED,内置高精度电流检测器&#xff0c;能通过外置电阻设定输出电流,开关式1A恒流芯片。软启动、高达1MHZ开关频率,开路保护,输入范围在6V-40VDC内都能稳定可靠…

js如何循环HTMLCollection

场景 当使用document.getElementsByClassName方法获取一个包含DOM节点的集合arr时&#xff0c;正常的forEach和map操作都会报一个arr.map is not a function的错误因为这里的arr并不是标准的 数组 (Array)&#xff0c;而是一个 HTMLCollection 解决 使用document.querySelector…

Dart 逆袭之路:Flutter 4.0 如何推动移动端开发变革?

本文深入探讨 Dart 语言在 Flutter 4.0 框架下如何推动移动端开发变革。开篇回顾 Dart 诞生背景与初期困境&#xff0c;阐述其在与 Flutter 结合后崭露头角。进而详细剖析 Flutter 4.0&#xff0c;从全新渲染引擎带来的性能飞跃、丰富实用新组件简化开发&#xff0c;到手势系统…

基于MATLAB的卷积神经网络手写数字识别

一、系统架构设计 #mermaid-svg-QQU8judlmQgHc2Lh {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-QQU8judlmQgHc2Lh .error-icon{fill:#552222;}#mermaid-svg-QQU8judlmQgHc2Lh .error-text{fill:#552222;stroke:#5…

从废弃到珍宝——旧物二手回收小程序系统的价值发现之旅

在我们的生活中&#xff0c;总有一些旧物因为各种原因而被遗弃在角落&#xff0c;它们或许不再新潮&#xff0c;或许不再实用&#xff0c;但它们却承载着我们的记忆和情感。旧物二手回收小程序系统的出现&#xff0c;让这些被遗忘的旧物重新焕发了生机&#xff0c;开启了一段从…