电商网站建设参考文献,网站建设服务合同交印花税吗,公司管理系统名称大全,网站建设需求信息编辑器主要分为三部分#xff0c;左侧是组件模板库#xff0c;中间是画布区域#xff0c;右侧是面板设置区域。 左侧是预设各种组件模板进行添加 中间是使用交互手段来更新元素的值 右侧是使用表单的方式来更新元素的值。 大致效果#xff1a;
左侧组件模板库 最初的模板…编辑器主要分为三部分左侧是组件模板库中间是画布区域右侧是面板设置区域。 左侧是预设各种组件模板进行添加 中间是使用交互手段来更新元素的值 右侧是使用表单的方式来更新元素的值。 大致效果
左侧组件模板库 最初的模板配置
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组件
divclasscomponent-itemv-for(item, index) in props.listclickonItemClick(item):keyindex
LText v-binditem/LText
/div// LText组件
component classl-text-component :isprops.tag :stylestyleProps clickhandleClick{{ 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-forcomponent in components :keycomponent.idEditWrapper v-if!component.isHidden:idcomponent.idset-activesetActive:activecomponent.id (currentElement currentElement.id) :propscomponent.propscomponent :iscanvasComponentList[component.name as l-text | l-image | l-shape] v-bindcomponent.props :isEditingtrue//EditWrapper/diveditWrapper组件就是为了隔离两个组件方便后续的一些拖拽拉伸吸附的一些效果。
template
div classedit-wrapper clickitemClickdblclickitemEditrefeditWrapper:class{active: active} :stylestyleProps:data-component-idid!-- 元素的扩大 --div classmove-wrapper refmoveWrapper mousedownstartMoveslot/slot/divdiv classresizersdiv classresizer top-left mousedownstartResize($event, top-left)/divdiv classresizer top-right mousedownstartResize($event, top-right)/divdiv classresizer bottom-left mousedownstartResize($event, bottom-left)/divdiv classresizer bottom-right mousedownstartResize($event, bottom-right)/div/div
/div
/template右侧设置面板区域的渲染 在中间画布区域进行点击的时候通过setActive事件我们可以拿到当前的元素
// store中的setActive
setActive(state, currentId: string) {state.currentElement currentId;
},然后就可以通过props-table组件进行渲染了
PropsTable v-ifcurrentElement currentElement.props :propscurrentElement.propschangehandleChange
/PropsTableprops-table比较麻烦我们来一一讲解首先来看一下props-talbe的template部分
templatediv classprops-tabledivv-for(value, key) in finalProps:keykey:class{ no-text: !value.text }classprop-item:iditem-${key}span classlabel v-ifvalue.text{{ value.text }}/spandiv :classprop-component component-${value.component}component:isvalue.component:[value.valueProp]value.valuev-bindvalue.extraPropsv-onvalue.eventstemplate v-ifvalue.optionscomponent:isvalue.subComponentv-for(option, k) in value.options:keyk:valueoption.valuerender-vnode :vNodeoption.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;}
}难点