web架构师编辑器内容-创建业务组件和编辑器基本行为
编辑器主要分为三部分,左侧是组件模板库,中间是画布区域,右侧是面板设置区域。
左侧是预设各种组件模板进行添加
中间是使用交互手段来更新元素的值
右侧是使用表单的方式来更新元素的值。
大致效果:

- 左侧组件模板库
最初的模板配置:
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>
- 中间画布区
基本的数据结构
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>
- 右侧设置面板区域的渲染:
在中间画布区域进行点击的时候,通过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;}
}
难点:
相关文章:
web架构师编辑器内容-创建业务组件和编辑器基本行为
编辑器主要分为三部分,左侧是组件模板库,中间是画布区域,右侧是面板设置区域。 左侧是预设各种组件模板进行添加 中间是使用交互手段来更新元素的值 右侧是使用表单的方式来更新元素的值。 大致效果: 左侧组件模板库 最初的模板…...
力扣刷题记录(18)LeetCode:474、518、377、322
目录 474. 一和零 518. 零钱兑换 II 377. 组合总和 Ⅳ 322. 零钱兑换 总结: 474. 一和零 这道题和前面的思路一样,就是需要将背包扩展到二维。 class Solution { public:int findMaxForm(vector<string>& strs, int m, int n) {vector&l…...
MongoDB创建和查询视图(一)
目录 限制和注意事项 应用两种方式创建视图 本文整理mongodb的官方文档,介绍mongodb的视图创建和查询。 Mongodb中,允许使用两种方式来创建视图。 //使用db.createCollection()来创建视图 db.createCollection("<viewName>",{"…...
paddle 53 基于PaddleClas2.5训练自己的数据(训练|验证|推理|c++ 部署)
项目地址:https://github.com/PaddlePaddle/PaddleClas 文档地址:https://paddleclas.readthedocs.io/zh-cn/latest/tutorials/install.html paddleclas的最新项目已经不适应其官网的使用案例(训练、验证、推理命令均不适用),为此博主对其进行命令重新进行修改。同时padd…...
智能优化算法应用:基于卷积优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码
智能优化算法应用:基于卷积优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于卷积优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.卷积优化算法4.实验参数设定5.算法结果6.…...
项目中日期封装
官网:Moment.js 中文网项目中安装:npm install moment --save封装:创建一个.js文件 // 日期、时间封装 import moment from moment moment.locale("zh-cn"); const formatTime {getTime: (date) > {return moment().format(YY…...
7.仿若依后端系统业务实践
目录 概述项目实践mybatis 反向生成代码有覆盖问题解决pom.xmlbootstrap.ymlapplication.ymlmaven测试各种校验问题实践单个属性校验级联属性校验接口实体类测试结果自定义关联属性校验接口...
java:4-9键盘输入
文章目录 键盘输入.1 定义.2 步骤.3 演示 键盘输入 .1 定义 在编程中,需要接收用户输入的数据,就可以使用键盘输入语句来获取。Input.java , 需要一个 扫描器(对象), 就是 Scanner .2 步骤 导入该类的所在包package, java.util.*创建该类对象(声明变…...
制作自己的 Docker 容器
软件开发最大的麻烦事之一,就是环境配置。用户必须保证操作系统的设置,各种库和组件的安装,只有它们都正确,软件才能运行。docker从根本上解决问题,软件安装的时候,把原始环境一模一样地复制过来。 以 koa-…...
Linux的账号及权限管理
一.管理用户账号 1.1 用户账户的分类 1.1.1 用户账号的分类 超级用户:(拥有至高无上的权利) root用户是Linux操作系统中默认的超级用户账号,对本主机拥有最高的权限,系统中超级用户是唯一的。普通用户: …...
Flink 状态管理与容错机制(CheckPoint SavePoint)的关系
一、什么是状态 无状态计算的例子: 例如一个加法算子,第一次输入235那么以后我多次数据23的时候得到的结果都是5。得出的结论就是,相同的输入都会得到相同的结果,与次数无关。 有状态计算的例子: 访问量的统计&#x…...
CSS中更加高级的布局手段——定位之绝对定位
定位: - 定位指的就是将指定的元素摆放到页面的任意位置,通过定位可以任意的摆放元素 - 通过position属性来设置元素的定位 -可选值: static: [sttik] 默认值,元素没有开启定位 relative: [relətiv] 开启元素…...
SQL server 数据库练习题及答案(练习3)
一、编程题 公司部门表 department 字段名称 数据类型 约束等 字段描述 id int 主键,自增 部门ID name varchar(32) 非空,唯一 部门名称 description varchar(1024) …...
太绝了!这个食堂服务,戳中了打工人的心巴!
在当今数字化时代,科技的迅猛发展已经渗透到我们生活的方方面面,其中餐饮行业也不例外。食堂作为人们日常生活中不可或缺的一部分,其管理和运营也需要紧跟科技潮流。 智慧收银系统的引入,旨在提高食堂的效率、准确性和服务水平&am…...
围栏中心点
后端返回的数据格式是 [{height: 0,lat: 30.864277169098443,lng:114.35252972024682}{height: 1,lat: 30.864277169098443,lng:114.35252972024682}.........]我们要转换成 33.00494857612568,112.53886564762979;33.00307854503083,112.53728973842954;33.00170296814311,11…...
【go-zero】simple-admin框架 整合ent mysql批量插入 | ent批量插入mysql
一、完整流程 我们需要通过goctls快速生成一个RPC项目 【go-zero】simple-admin 开篇:进击 go-zero 二开框架 simple-admin 加速 go-zero 开发 之 rpc项目快速创建(更新中~) https://ctraplatform.blog.csdn.net/article/details/130087729 1、RPC项目 1.1、.proto synta…...
漏洞复现-泛微OA xmlrpcServlet接口任意文件读取漏洞(附漏洞检测脚本)
免责声明 文章中涉及的漏洞均已修复,敏感信息均已做打码处理,文章仅做经验分享用途,切勿当真,未授权的攻击属于非法行为!文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的…...
Flink CDC 1.0至3.0回忆录
Flink CDC 1.0至3.0回忆录 一、引言二、CDC概述三、Flink CDC 1.0:扬帆起航3.1 架构设计3.2 版本痛点 四、Flink CDC 2.0:成长突破4.1 DBlog 无锁算法4.2 FLIP-27 架构实现4.3 整体流程 五、Flink CDC 3.0:应运而生六、Flink CDC 的影响和价值…...
c语言例题7
以下程序中,主函数调用了LineMax函数,实现在N行M列的二维数组中,找出每一行上的最大值。请填空。 #define N 3 #define M 4 void LineMax(int x[N][M]) { int i,j,p; for(i0; i<N;i) { p0; for(j1; j<M;j) …...
【Linux驱动】最基本的驱动框架 | LED驱动
🐱作者:一只大喵咪1201 🐱专栏:《Linux驱动》 🔥格言:你只管努力,剩下的交给时间! 目录 🏀最基本的驱动框架⚽驱动程序框架⚽编程 🏀LED驱动⚽配置GPIO⚽编程…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...
上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式
简介 在我的 QT/C 开发工作中,合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式:工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...
从实验室到产业:IndexTTS 在六大核心场景的落地实践
一、内容创作:重构数字内容生产范式 在短视频创作领域,IndexTTS 的语音克隆技术彻底改变了配音流程。B 站 UP 主通过 5 秒参考音频即可克隆出郭老师音色,生成的 “各位吴彦祖们大家好” 语音相似度达 97%,单条视频播放量突破百万…...
CppCon 2015 学习:Reactive Stream Processing in Industrial IoT using DDS and Rx
“Reactive Stream Processing in Industrial IoT using DDS and Rx” 是指在工业物联网(IIoT)场景中,结合 DDS(Data Distribution Service) 和 Rx(Reactive Extensions) 技术,实现 …...
