当前位置: 首页 > news >正文

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.lryc.cn/news/267593.html

相关文章:

  • 力扣刷题记录(18)LeetCode:474、518、377、322
  • MongoDB创建和查询视图(一)
  • paddle 53 基于PaddleClas2.5训练自己的数据(训练|验证|推理|c++ 部署)
  • 智能优化算法应用:基于卷积优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码
  • 项目中日期封装
  • 7.仿若依后端系统业务实践
  • java:4-9键盘输入
  • 制作自己的 Docker 容器
  • Linux的账号及权限管理
  • Flink 状态管理与容错机制(CheckPoint SavePoint)的关系
  • CSS中更加高级的布局手段——定位之绝对定位
  • SQL server 数据库练习题及答案(练习3)
  • 太绝了!这个食堂服务,戳中了打工人的心巴!
  • 围栏中心点
  • 【go-zero】simple-admin框架 整合ent mysql批量插入 | ent批量插入mysql
  • 漏洞复现-泛微OA xmlrpcServlet接口任意文件读取漏洞(附漏洞检测脚本)
  • Flink CDC 1.0至3.0回忆录
  • c语言例题7
  • 【Linux驱动】最基本的驱动框架 | LED驱动
  • 前端---表单提交
  • [C#]Parallel使用
  • docker container 指定gpu设备
  • 时间Date
  • 前端---css 选择器
  • 【MybatisPlus快速入门】(2)SpringBoot整合MybatisPlus 之 标准数据层开发 代码示例
  • 如何将自建的ElasticSearch注册成一个服务
  • 360勒索病毒:了解最新变种.360,以及如何保护您的数据
  • vue使用ElementUI搭建精美页面入门
  • 【C->Cpp】深度解析#由C迈向Cpp(2)
  • WPS中如何根据身份证号生成出生日期并排序