React + 项目(从基础到实战) -- 第11期

news2025/2/22 10:49:30

目标

问卷编辑器的开发

设计UI - 拆分布局

水平垂直居中

在这里插入图片描述

画布 y方向滚动

在这里插入图片描述

自定义问卷组件

在这里插入图片描述

后端 返回组件数据

 //获取单个问卷信息

    {

        url: '/api/question/:id',

        method: 'get',

        response: () => {

            return {

                errno: 0,

                data: {

                    id: Random.id(),

                    title: Random.ctitle(),

                    componentList:[

                        //Title

                        {

                            id:Random.id(),

                            type:'questionTitle', //组件类型不能重复,前后端统一

                            title:"标题",

                            props:{

                                text:"问卷标题",

                                level:1,

                                isCenter:false

                            }

                        },

                        //Input

                        {

                            id:Random.id(),

                            type:'questionInput',

                            title:"输入框",

                            props:{

                                title:"输入框",

                                placeholder:"请输入内容",

                            }

                        },

                          //Input2

                          {

                            id:Random.id(),

                            type:'questionInput',

                            title:"输入框2",

                            props:{

                                title:"输入框2",

                                placeholder:"请输入内容2",

                            }

                        }

  
  

                    ]

                }

            }

        }

    },

前端 redux 存储后端返回组件数据

切片

import { createSlice , PayloadAction } from "@reduxjs/toolkit";

import { ComponentPropsType } from "../../components/QuestionComponents";

  
  

//单个组件的信息

export type ComponentInfoType={

    fe_id : string,//为什么下划线

    type : string,

    title: string,

    props:ComponentPropsType

}

  
  

//redux存放组件列表

//1. 定义数据结构

export type ComponentsStateType={

    componentList:Array<ComponentInfoType>,

}

  

//2. 初始化

const INIT_STATE:ComponentsStateType={

    componentList:[],

    //其他扩展

  

}

 export const componentsSlice = createSlice({

     name:"components",

     initialState:INIT_STATE,

     reducers:{

         //重置所有组件

         //看不懂啊老铁!!!!

         resetComponentList:(state: ComponentsStateType , action: PayloadAction<ComponentsStateType>)=>{

             return action.payload

         }

     }

  

 })

  

//导出所有的actions

  

export const {resetComponentList} = componentsSlice.actions

  

export default componentsSlice.reducer

store

import { configureStore } from '@reduxjs/toolkit'

import userReducer, { UserStateType } from './userReducer'

import componentsReducer , {ComponentsStateType}from './componentsReducer'

  
  

export type StateType={

  user : UserStateType,

  components : ComponentsStateType

}

  

export default configureStore({

  reducer: {

    //分模块注册

    user: userReducer, // 存储user数据

    components : componentsReducer// 存储问卷组件列表的数据

    // 存储问卷组件列表的数据

  
  

    // 存储问卷信息数据

  }

})

发请求时存储数据

import { useEffect , useState } from "react";

  

import { useParams } from "react-router-dom";

import { useRequest } from "ahooks";

  

import {useDispatch} from 'react-redux'

import {resetComponentList} from '../store/componentsReducer'

  

//导入发起请求的函数

  

import { getQuestinService } from "../services/question";

  

function useLoadQuestionData() {

    const dispatch = useDispatch()

    const {id = ''} =useParams()

  

   const {data , loading , error , run} = useRequest(

    async (id : string) => {

        if(!id) throw new Error('不存在问卷id')

        const data = await getQuestinService(id)

        return data

    },

    {

        manual: true,

    }

   )

    //根据获取的data 设置redux store

    useEffect(() => {

        if(!data) return

        const {title ='' , componentList = []} = data

  

        //获取到的componentList 存储到 Redux store中

        dispatch(resetComponentList({

            componentList

        }))

  

    },[data])

  
  

    //问卷改变时, 重新加载问卷数据

    useEffect(() => {

        run(id)

    },[id])

  
  

    return {

        loading,

        error,

    }

  
  

}

  
  

export default useLoadQuestionData;

在这里插入图片描述

页面画布区域显示组件列表

自定义hook获取数据

import { useSelector } from "react-redux";

import { StateType } from "../store";

import { ComponentsStateType } from "../store/componentsReducer";

  
  

function useGetComponentInfo() {

    //使用useSelector获取store中的数据

    const componens= useSelector<StateType>(state => state.components) as ComponentsStateType

  

    //结构出空数组

    const {componentList = []} = componens

    return {

        componentList

    }

  

}

  

export default useGetComponentInfo;

重构canvas页面

import { FC } from 'react';

import styles from './EditCanvas.module.scss';

//静态展示



import useGetComponentInfo from '../../../hooks/useGetComponentInfo';

import { ComponentInfoType } from '../../../store/componentsReducer';

import { getComponentConfByType } from '../../../components/QuestionComponents';

type PropsType={

  loading : boolean

}

  
  

const EditCanvas: FC<PropsType> = ({loading}) => {

  const {componentList } = useGetComponentInfo();

  

  if(loading){

    return <div>loading</div>

  }

  

  //根据传入的组件 ,

  function getComponent(componetInfo : ComponentInfoType)

  {

    const {type , props} = componetInfo

    //根据组件类型找到对应的组件配置

    const componentConf= getComponentConfByType(type)

    if(!componentConf) return null

   const {Component} = componentConf

  

    return <Component {...props} />

  }

  

  return (

    <div className={styles.canvas}>

  

      {componentList.map(c => {

        const {id} = c

        return (

          <div key={id} className={styles['component-warpper']}>

          <div className={styles.component}>

            {getComponent(c)}

          </div>

        </div>

        )

      })}

      

    </div>

  );

};

  

export default EditCanvas;

点击组件选中效果

添加selectedId,点击时,修改当前选中组件id

import { createSlice , PayloadAction } from "@reduxjs/toolkit";

import { ComponentPropsType } from "../../components/QuestionComponents";

import {produce} from "immer";

  

//单个组件的信息

export type ComponentInfoType={

    // fe_id : string,//为什么是fe_id

    id: string

    type : string,

    title: string,

    props:ComponentPropsType

}

  
  

//redux存放组件列表

//1. 定义数据结构

export type ComponentsStateType={

    componentList:Array<ComponentInfoType>,

    selectedId:string

}

  

//2. 初始化

const INIT_STATE:ComponentsStateType={

    selectedId:'',

    componentList:[],

    //其他扩展

  

}

 export const componentsSlice = createSlice({

     name:"components",

     initialState:INIT_STATE,

     reducers:{

         //1. 重置所有组件

         //看不懂啊老铁!!!!

         resetComponentList:(state: ComponentsStateType , action: PayloadAction<ComponentsStateType>)=>{

             return action.payload

         },

  

         //2.修改选中的组件id

         //使用immer , 改进state不可变数据的写法

         changeSelctedId:produce((state: ComponentsStateType , action: PayloadAction<string>)=>{

             state.selectedId=action.payload

         })

  

     }

  

 })

  

//导出所有的actions

  

export const {resetComponentList} = componentsSlice.actions

  

export default componentsSlice.reducer

页面注册点击事件

 //点击选中组件

 function handleClick(id: string) {

   dispatch(changeSelctedId(id))

 }

点击后改变样式

classsNames css样式的拼接

import classNames from 'classnames'; // 这个是实现css样式的拼接


  {componentList.map(c => {

        const {id} = c

  

        //拼接classname

        const defaultClassName=styles['component-warpper']

        const selectedClassName=styles.selected

        const wrapperClassName = classNames({

          [defaultClassName]: true,

          [selectedClassName]: id === selectedId

        })

  
  

        return (

          <div onClick={()=>{handleClick(id)}} key={id} className={wrapperClassName}>

          <div className={styles.component}>

            {getComponent(c)}

          </div>

        </div>

        )

      })}

在这里插入图片描述

点击空白处,取消选中效果

注意这里阻止冒泡的操作

在这里插入图片描述

默认初始加载时选择第一个组件

在这里插入图片描述

组件库

组件分组

left 布局搭建
选取 组件库中的 tabs组件
又提取出组件 componentlib

在这里插入图片描述

显示到组件库

在这里插入图片描述

点击,添加组件到画布

画布信息需要更新
画布信息存在Redux中

处理redux

在这里插入图片描述

页面中使用

在这里插入图片描述

组件属性面板

组件属性的配置

每个组件的属性不一样,单独配置

import React, {FC, useEffect} from "react";

import { QuestionInputPropsType } from "./interface";

//引入组件库

import {Form ,Input} from "antd";

  
  

const PropComponent:FC<QuestionInputPropsType> = (props:QuestionInputPropsType) => {

    const {title , placeholder} = props;

  

    return (

        <div>

            <Form

              layout="vertical"

              initialValues={{ title ,placeholder }}

              >

                 <Form.Item label="标题" name="title" rules={[{ required: true, message: '请输入标题' }]}>

                  <Input />

                 </Form.Item>

                 <Form.Item label="Placeholder" name="placeholder">

                  <Input/>

                 </Form.Item>

  

            </Form>

        </div>

    )

}

  

export default PropComponent;

画布中点击不同的组件时,监听变化,即时显示在右侧属性栏

在这里插入图片描述

组件配置中引入属性配置

在这里插入图片描述

属性面板显示组件属性

根据selectedID面板显示组件属性

在这里插入图片描述

在这里插入图片描述

onchange改变组件属性时,同步到Redux Store

改变组件属性时,统一交给上层的componentProops管理

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在redux store 中增加修改属性的方法

在这里插入图片描述

头部编辑栏

定义组件
页面中引入

import React , {FC} from "react";

import styles from "./EditHeader.module.scss";

import {

    Button,

    Space,

    Typography,

}from 'antd';

import { LeftOutlined } from "@ant-design/icons";

import { useNavigate } from "react-router-dom";

  

const {Title} = Typography;

  

const EditHeader:FC = ()=>{

    const nav = useNavigate()

    return (

        <div className={styles['header-wrapper']}>

          <div className={styles.header}>

            <div className={styles.left}>

                <Space>

                    <Button type="link" icon={<LeftOutlined></LeftOutlined>} onClick={()=>nav(-1)}>返回</Button>

                    <Title>问卷标题</Title>

                </Space>

            </div>

            <div className={styles.main}></div>

            <div className={styles.right}>

                <Space>

                    <Button>保存</Button>

                    <Button>发布</Button>

                </Space>

            </div>

          </div>

        </div>

    )

  

}

  

export default EditHeader;

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

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

相关文章

纯血鸿蒙APP实战开发——页面间共享组件实例的案例

介绍 本示例提供组件实例在页面间共享的解决方案&#xff1a;通过Stack容器&#xff0c;下层放地图组件&#xff0c;上层放Navigation组件来管理页面&#xff0c;页面可以共享下层的地图组件&#xff0c;页面中需要显示地图的区域设置为透明&#xff0c;并参考触摸交互控制&am…

Linux网络-部署YUM仓库及NFS共享服务

目录 一.YUM仓库服务 1.YUM概述 1.1.YUM&#xff08;Yellow dog Updater Modified&#xff09; 2.准备安装源 2.1.软件仓库的提供方式 2.2.RPM软件包的来源 2.3.构建CentOS 7 软件仓库 2.4.在软件仓库中加入非官方RPM包组 3.一键安装软件包的工具&#xff1a; 好处&a…

ubuntu中的docker记录(3)——如何安装nvidia-docker以更好地支持GPU加速计算应用程序的运行

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、nvidia-docker2的安装1. 安装docker2. 安装nvidia-docker2(1) 添加密钥(2) 更新软件列表(3) 安装nvidia-docker2(4) 测试nvidia-docker2 二、可能的报错及解…

大模型争霸的下一站:不仅是超越GPT-4,更是寻求模型之间的平衡应用

文 | 智能相对论 作者 | 沈浪 知名科学杂志《Nature》发表了一篇关于大模型规模参数大小争议的文章《In Al, is bigger always better?》——AI大模型&#xff0c;越大越好吗&#xff1f;随着大模型应用走向实践&#xff0c;这一问题不可避免地成为了当前AI行业发展的焦点与…

【网络原理】IP协议详解

一.与IP协议相关的基本概念 IP协议&#xff0c;即网际互连协议&#xff08;Internet Protocol&#xff09;&#xff0c;是TCP/IP体系中的核心网络层协议。 网络层IP协议解决的问题 数据传输的过程中,不是直接进行的传输,而是经过层层的封装和分用的过程才能到达对端. IP协议主…

怎么通过网页查看iPhone手机的备忘录内容?

在这个数字化的时代&#xff0c;iPhone已成为我们日常生活中不可或缺的一部分。我特别喜欢用iPhone的备忘录功能&#xff0c;随时随地记录生活的点点滴滴&#xff0c;工作中的待办事项。然而&#xff0c;有时候&#xff0c;当我需要在电脑上查看或编辑这些备忘录时&#xff0c;…

最原理的一集——Mathtype公式编号设置(Mathtype7.8+Word)

版本 Mathtype7.8Office2019 Word 读完本文你将会 随心所欲&#xff0c;想怎么给公式编号就怎么给公式编号&#xff0c;想从(X.1)开始&#xff0c;就从(X.1)开始大概了解Mathtype公式设置原理给作者点赞 如果你想自己跟着文章做的话 请不要在自己的论文里边直接操作&#…

超详细——集成学习——Adaboost——笔记

资料参考 1.【集成学习】boosting与bagging_哔哩哔哩_bilibili 集成学习——boosting与bagging 强学习器&#xff1a;效果好&#xff0c;模型复杂 弱学习器&#xff1a;效果不是很好&#xff0c;模型简单 优点 集成学习通过将多个学习器进行结合&#xff0c;常可获得比单一…

批量剪辑利器:视频随机分割,创意无限,高效剪辑视频

在视频制作和编辑的过程中&#xff0c;剪辑是一项至关重要的技术。对于需要处理大量视频素材的用户来说&#xff0c;批量剪辑不仅能提高工作效率&#xff0c;还能为视频内容带来创意和多样性。随着技术的发展&#xff0c;一些高效的剪辑工具逐渐崭露头角&#xff0c;其中视频随…

产品推荐 | 基于 Virtex UltraScale+ XCVU3P的FACE-VPXSSD-3PA 存储板

01 产品概述 FACE&#xff08;FPGA Algorithm aCceleration Engine&#xff09;FPGA算法加速开发引擎是基于FPGA可编程器件构建的一系列算法加速开发引擎平台。FACE-VPXSSD-3PA存储平台是FACE系列中的一员。该平台板载2组2GB 64bit DDR4、2路QSFP28光接口、4个NVME SSD M.2接口…

Linux:进程信号(一)信号的产生

目录 一、信号是什么&#xff1f; 二、Linux信号 三、信号处理方式 四、信号的产生 1、 通过终端按键产生信号 2、调用系统函数向进程发信号 3、 硬件异常产生信号 一、信号是什么&#xff1f; 在生活中&#xff0c;有许多信号&#xff0c;比如红绿灯&#xff0c;下课铃声…

达梦主从数据库实例恢复

测试环境&#xff1a;实时主备数据库 1、在节点1向测试表aaa插入数据 如图可见&#xff0c;会话139695153554808向aaa表插入了10000行数据。事务id460520。 2、提交前在另一个窗口kill掉dmserver进程。 3、查看节点2的数据库日志 上图可见&#xff0c;系统执行alter database…

Java基础教程 - 4 流程控制

更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 4 流程控制 4.1 分支结构…

在企业中软件产品测试报告可以运用的场景

在企业应用场景中&#xff0c;测试报告的应用场景十分广泛且重要。以下是几个主要的应用场景&#xff1a; 产品质量评估与保证&#xff1a;测试报告是企业评估软件或产品质量的重要依据。通过测试报告&#xff0c;企业可以了解产品在不同场景下的性能表现、安全性、稳定性以及…

esp32-cam 1. 出厂固件编译与测试

0. 环境 - ubuntu18 - esp32-cam - usb转ttl ch340 硬件连接 esp32-camch340板子U0RTXDU0TRXDGNDGND5V5V 1. 安装依赖 sudo apt-get install vim sudo apt install git sudo apt-get install git wget flex bison gperf python python-pip python-setuptools python-serial p…

Redis的数据类型及使用场景

redis命令大全官网: Commands | Docs (redis.io) 基本介绍 redis起初主要就是为了解决性能问题的&#xff0c;那么redis为什么快? 基于内存操作的&#xff0c;所以操作不需要跟磁盘进行交互&#xff0c;单次的执行会很快 命令执行是单线程 因为基于内存操作 单次执行时间反…

Vue开发者工具Vue.js devtools Vue开发者工具安装步骤前端开发工具免费附带教程

下载地址&#xff1a; 链接: https://pan.baidu.com/s/1JaGvhS4NoD8lL07n2ScE9A 密码: 9rfs 安装步骤&#xff1a; 以谷歌浏览器为例 第一步&#xff1a;打开Chrome的拓展程序 如图 第二步&#xff1a; 将下载好的拓展程序拖入即可&#xff0c;如下图 第三步&#xff1a;…

Python数据清洗与可视化实践:国际旅游收入数据分析

文章目录 概要整体流程名词解释NumPyPandasMatplotlibre 技术细节数据清洗可视化 小结 概要 在本篇博客中&#xff0c;我们将通过一个实际的案例&#xff0c;演示如何使用Python进行数据清洗和可视化&#xff0c;以分析国际旅游收入数据。我们将使用Python中的Pandas库来进行数…

OpenHarmony实战开发-应用侧调用前端页面函数

应用侧可以通过runJavaScript()方法调用前端页面的JavaScript相关函数。 在下面的示例中&#xff0c;点击应用侧的“runJavaScript”按钮时&#xff0c;来触发前端页面的htmlTest()方法。 前端页面代码。 <!-- index.html --> <!DOCTYPE html> <html> <…

接口自动化框架篇:Pytest + Allure报告企业定制化实现!

接口自动化框架是现代软件开发中的重要组成部分&#xff0c;能够帮助开发团队提高测试效率和质量。本文将介绍如何使用Pytest作为测试框架&#xff0c;并结合Allure报告进行企业定制化实现。 目标规划 在开始编写接口自动化测试框架之前&#xff0c;我们需要先进行目标规划。…