web架构师编辑器内容-创建业务组件和编辑器基本行为

编辑器主要分为三部分,左侧是组件模板库,中间是画布区域,右侧是面板设置区域。
左侧是预设各种组件模板进行添加
中间是使用交互手段来更新元素的值
右侧是使用表单的方式来更新元素的值。
大致效果:
在这里插入图片描述

  1. 左侧组件模板库
    最初的模板配置:
export const defaultTextTemplates = [{text: '大标题',fontSize: '30px',fontWeight: 'bold',tag: 'h2'},{text: '楷体副标题',fontSize: '20px',fontWeight: 'bold',fontFamily: '"KaiTi","STKaiti"',tag: 'h2'},{text: '正文内容',tag: 'p'},{text: '宋体正文内容',tag: 'p',fontFamily: '"SimSun","STSong"'},{text: 'Arial style',tag: 'p',fontFamily: '"Arial", sans-serif'},{text: 'Comic Sans',tag: 'p',fontFamily: '"Comic Sans MS"'},{text: 'Courier New',tag: 'p',fontFamily: '"Courier New", monospace'},{text: 'Times New Roman',tag: 'p',fontFamily: '"Times New Roman", serif'},{text: '链接内容',color: '#1890ff',textDecoration: 'underline',tag: 'p'},{text: '按钮内容',color: '#ffffff',backgroundColor: '#1890ff',borderWidth: '1px',borderColor: '#1890ff',borderStyle: 'solid',borderRadius: '2px',paddingLeft: '10px',paddingRight: '10px',paddingTop: '5px',paddingBottom: '5px',width: '100px',tag: 'button',textAlign: 'center'}
]

在component-list组件中循环渲染这个模板
compnent-list组件:

<divclass="component-item"v-for="(item, index) in props.list"@click="onItemClick(item)":key="index"
><LText v-bind="item"></LText>
</div>// LText组件
<component class="l-text-component" :is="props.tag" :style="styleProps" @click="handleClick">{{ props.text }}
</component>
  1. 中间画布区
    基本的数据结构
export interface ComponentData {props: { [key: string]: any }id: stringname: string
}

在左侧模板区域点击的时候,会emit一个onItemCreated事件:

const onItemCreated = (props: ComponentData) => {store.commit('addComponent', props)
}

store里面的addComponent方法:

addComponent(state, props) {const newComponent: ComponentData =id: uuidv4(),name: 'l-text',props}state.components.push(newComponent)
},

渲染中间画布区域:

<div v-for="component in components" :key="component.id"><EditWrapper v-if="!component.isHidden":id="component.id"@set-active="setActive":active="component.id === (currentElement && currentElement.id)" :props="component.props"><component :is="canvasComponentList[component.name as 'l-text' | 'l-image' | 'l-shape']" v-bind="component.props" :isEditing="true"/></EditWrapper></div>

editWrapper组件就是为了隔离两个组件,方便后续的一些拖拽,拉伸,吸附的一些效果。

<template>
<div class="edit-wrapper" @click="itemClick"@dblclick="itemEdit"ref="editWrapper":class="{active: active}" :style="styleProps":data-component-id="id"
> <!-- 元素的扩大 --><div class="move-wrapper" ref="moveWrapper" @mousedown="startMove"><slot></slot></div><div class='resizers'><div class='resizer top-left' @mousedown="startResize($event, 'top-left')"></div><div class='resizer top-right'  @mousedown="startResize($event, 'top-right')"></div><div class='resizer bottom-left' @mousedown="startResize($event, 'bottom-left')"></div><div class='resizer bottom-right' @mousedown="startResize($event, 'bottom-right')"></div></div>
</div>
</template>
  1. 右侧设置面板区域的渲染:
    在中间画布区域进行点击的时候,通过setActive事件,我们可以拿到当前的元素,
// store中的setActive
setActive(state, currentId: string) {state.currentElement = currentId;
},

然后就可以通过props-table组件进行渲染了:

<PropsTable v-if="currentElement && currentElement.props" :props="currentElement.props"@change="handleChange"
></PropsTable>

props-table比较麻烦我们来一一讲解,首先来看一下props-talbe的template部分:

<template><div class="props-table"><divv-for="(value, key) in finalProps":key="key":class="{ 'no-text': !value.text }"class="prop-item":id="`item-${key}`"><span class="label" v-if="value.text">{{ value.text }}</span><div :class="`prop-component component-${value.component}`"><component:is="value.component":[value.valueProp]="value.value"v-bind="value.extraProps"v-on="value.events"><template v-if="value.options"><component:is="value.subComponent"v-for="(option, k) in value.options":key="k":value="option.value"><render-vnode :vNode="option.text"></render-vnode></component></template></component></div></div></div>
</template>

我们最终渲染的是finalProps这个数据,finalProps数据的生成:

// 属性转化成表单的映射表 key:属性  value:使用的组件
export const mapPropsToForms: PropsToForms = {// 比如: text 属性,使用 a-input 这个组件去编辑text: {text: '文本',component: 'a-input',afterTransform: (e: any) => e.target.value,},fontSize: {text: '字号',component: 'a-input-number',// 为了适配类型,进行一定的转换initalTransform: (v: string) => parseInt(v),afterTransform: (e: number) => e ? `${e}px` : '',},lineHeight: {text: '行高',component: 'a-slider',extraProps: {min: 0,max: 3,step: 0.1},initalTransform: (v: string) => parseFloat(v)},textAlign: {component: 'a-radio-group',subComponent: 'a-radio-button',text: '对齐',options: [{value: 'left',text: '左'},{value: 'center',text: '中'},{value: 'right',text: '右'}],afterTransform: (e: any) => e.target.value},fontFamily: {component: 'a-select',subComponent: 'a-select-option',text: '字体',options: [{value: '',text: '无'},...fontFamilyOptions],afterTransform: (e: any) => e},color: {component: 'color-pick',text: '字体颜色',afterTransform: (e: any) => e}
}
const finalProps = computed(() => {// reduce是使用loadsh里面的return reduce(props.props,(result, value, key) => {const newKey = key as keyof AllComponentProps;const item = mapPropsToForms[newKey];if (item) {// v-model默认绑定的值,是value,可以自定义// v-model双向数据绑定的事件,默认是change事件,也可以自定义// initalTransform编辑前的value转换,为了适配类型,进行一定的转换// afterTransform 处理上双向数据绑定后的值。const {valueProp = 'value',eventName = 'change',initalTransform,afterTransform,} = item;const newItem: FormProps = {...item,value: initalTransform ? initalTransform(value) : value,valueProp,eventName,events: {[eventName]: (e: any) => {context.emit('change', {key,value: afterTransform ? afterTransform(e) : e,});},},};result[newKey] = newItem;}return result;},{} as { [key: string]: FormProps });
});

我们传递的props值是这样的:
在这里插入图片描述
最终转换成出来的值是这样的
在这里插入图片描述
在这里插入图片描述
当组件内的change事件改变后,组件内部会触发

context.emit('change', { key, value: afterTransform ? afterTransform(e) : e,});

在父组件中接收change事件来改变stroe中的compoents的值

const handleChange = (e) => {console.log('event', e);store.commit('updateComponent', e)
}

在store中改变components属性

updateComponent(state, { id, key, value, isProps}) {const updatedComponent = state.components.find((component) => component.id === (id || state.currentElement)) as anyif(updatedComponent) {updatedComponent.props[key as keyof TextComponentProps] = value;}
}

难点:

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

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

相关文章

基于JSP+Servlet+Mysql的调查管理系统

基于JSPServletMysql的调查管理系统 一、系统介绍二、功能展示1.项目内容2.项目骨架3.数据库3.登录4.注册3.首页5.系统管理 四、其它1.其他系统实现五.获取源码 一、系统介绍 项目名称&#xff1a;基于JSPServlet的调查管理系统 项目架构&#xff1a;B/S架构 开发语言&#…

在Next.js和React中搭建Cesium项目

在Next.js和React中搭建Cesium项目&#xff0c;需要确保Cesium能够与服务端渲染(SSR)兼容&#xff0c;因为Next.js默认是SSR的。Cesium是一个基于WebGL的地理信息可视化库&#xff0c;通常用于在网页中展示三维地球或地图。下面是一个基本的步骤&#xff0c;用于在Next.js项目中…

.raw 是一个 Anndata 包中的对象,用于存储原始的单细胞数据。scanpy种如何查看 .raw 对象的内容,

1查看 .raw 对象的内容&#xff0c;可以使用以下方法&#xff1a; .raw 是一个 Anndata 包中的对象&#xff0c;用于存储原始的单细胞数据。 使用 .X 属性查看原始数据矩阵&#xff1a;.raw.X 这将返回一个 Numpy 数组&#xff0c;其中包含原始数据的数值。 使用 .var_names 属…

nodejs微信小程序+python+PHP兴趣趣班预约管理系统设计与实现-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

ARM作业1

汇编实现三个灯闪烁 汇编代码&#xff1a; .text .global _start _start: 设置GPIOE,GPIOF时钟使能LDR R0,0X50000A28 LDR R1,[R0] ORR R1,R1,#(0x3<<4) STR R1,[R0] 设置PE10,PF10,PE8为输出 LED1LDR R0,0X50006000LDR R1,[R0]ORR R1,R1,#(0X1<<20)BIC R1…

力扣每日一题day38[106. 从中序与后序遍历序列构造二叉树]

给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1a;inorder [9,3,15,20,7], postorder [9,15,7,20,3] 输出&#xff1a;[…

华为鸿蒙(HarmonyOS):连接一切,智慧无限

华为鸿蒙是一款全场景、分布式操作系统&#xff0c;旨在构建一个真正统一的硬件生态系统。该操作系统于2019年8月首次发布&#xff0c;并被设计为可以应用于各种设备&#xff0c;包括智能手机、智能手表、智能电视、车载系统等多种智能设备。 推荐一套最新版的鸿蒙4.0开发教程 …

从零开发短视频电商 在AWS上SageMaker部署模型自定义日志输入和输出示例

从零开发短视频电商 在AWS上SageMaker部署模型自定义日志输入和输出示例 怎么部署自定义模型请看&#xff1a;从零开发短视频电商 在AWS上用SageMaker部署自定义模型 都是huaggingface上的模型或者fine-tune后的。 为了适配jumpstart上部署的模型的http输入输出&#xff0c;我…

Java设计模式之单例模式以及如何防止通过反射破坏单例模式

单例模式 单例模式使用场景 ​ 什么是单例模式&#xff1f;保障一个类只能有一个对象&#xff08;实例&#xff09;的代码开发模式就叫单例模式 ​ 什么时候使用&#xff1f; 工具类&#xff01;&#xff08;一种做法&#xff0c;所有的方法都是static&#xff0c;还有一种单…

使用 Elasticsearch 检测抄袭 (一)

作者&#xff1a;Priscilla Parodi 抄袭可以是直接的&#xff0c;涉及复制部分或全部内容&#xff0c;也可以是释义的&#xff0c;即通过更改一些单词或短语来重新表述作者的作品。 灵感和释义之间是有区别的。 即使你得出类似的结论&#xff0c;也可以阅读内容&#xff0c;获得…

Chrome浏览器http自动跳https问题

现象&#xff1a; Chrome浏览器访问http页面时有时会自动跳转https&#xff0c;导致一些问题。比如&#xff1a; 开发阶段访问dev环境网址跳https&#xff0c;后端还是http&#xff0c;导致接口跨域。 复现&#xff1a; 先访问http网址&#xff0c;再改成https访问&#xf…

Springboot+vue的装饰工程管理系统(有报告),Javaee项目,springboot vue前后端分离项目

演示视频&#xff1a; Springbootvue的装饰工程管理系统&#xff08;有报告&#xff09;&#xff0c;Javaee项目&#xff0c;springboot vue前后端分离项目 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的前后端分离的装饰工程管理系统&#xff0c;采用M&#xff08…

vue3开发一个todo List

创建新的 Vue 3 项目&#xff1a; 按装vue3的 工具 npm install -g vue/cli创建一个新的 Vue 3 项目&#xff1a; vue create vue3-todolist进入项目目录&#xff1a; cd vue3-todolist代码&#xff1a; 在项目的 src/components 目录下&#xff0c;创建一个新的文件 Todo…

洛谷 NOIP2016 普及组 回文日期

这道题目本来是不难想思路的。。。。。。 然而我第一次做的时候改了蛮久才把代码完全改对&#xff0c;主要感觉还是不够细心&#xff0c;敲的时候也没注意见检查一些小错误&#xff0c;那么接下来不说废话&#xff0c;请看题干&#xff1a; 接下来请看输入输出的样例以及数据范…

听GPT 讲Rust源代码--src/tools(23)

File: rust/src/tools/clippy/rustc_tools_util/src/lib.rs 在Rust源代码中&#xff0c;rust/src/tools/clippy/rustc_tools_util/src/lib.rs文件的作用是为Clippy提供了一些实用工具和辅助函数。 该文件中定义了VersionInfo结构体&#xff0c;它有三个字段&#xff0c;分别为m…

Web组态可视化编辑器-by组态

演示地址&#xff1a; http://www.by-lot.com http://www.byzt.net web组态可视化编辑器&#xff1a;引领未来可视化编辑的新潮流 随着网络的普及和快速发展&#xff0c;web组态可视化编辑器应运而生&#xff0c;为人们在网络世界中创建和编辑内容提供了更加便捷的操作方式。这…

【Spring实战】配置多数据源

文章目录 1. 配置数据源信息2. 创建第一个数据源3. 创建第二个数据源4. 创建启动类及查询方法5. 启动服务6. 创建表及做数据7. 查询验证8. 详细代码总结 通过上一节的介绍&#xff0c;我们已经知道了如何使用 Spring 进行数据源的配置以及应用。在一些复杂的应用中&#xff0c;…

CVE-2023-49898 Apache incubator-streampark 远程命令执行漏洞

项目介绍 Apache Flink 和 Apache Spark 被广泛用作下一代大数据流计算引擎。基于大量优秀经验结合最佳实践&#xff0c;我们将任务部署和运行时参数提取到配置文件中。这样&#xff0c;带有开箱即用连接器的易于使用的 RuntimeContext 将带来更轻松、更高效的任务开发体验。它…

【clickhouse】在CentOS中离线安装clickhouse

一、下载地址 通过以下链接进行rpm安装包的下载 https://packages.clickhouse.com/rpm/stable/ 根据需求下载对应版本 注意&#xff1a;ClickHouse 20.8.2.3版本新增加了 MaterializeMySQL 的 database 引擎&#xff0c;该 database 能映射到 MySQL 中的某个 database&#…

NativePHP:使用 PHP 构建桌面应用程序

PHP 在我心中占据着特殊的位置。它是我的第一份工作&#xff0c;我记得我在家里花了无数个小时做一些小项目。我非常想用 PHP 创建桌面应用程序&#xff0c;但我从来没有做到过。 现在&#xff0c;感谢 NativePHP&#xff0c;我可以了。 NativePHP 追随 Slack、Discord 和 Tre…