当前位置: 首页 > 科技观察

低代码平台的属性面板应该如何设计?

时间:2023-03-18 19:18:43 科技观察

先简单回顾一下整个平台的设计:这里是我平时自己维护的一个低代码平台,技术栈是Vue。后续的分享也是基于平台的一些具体实现细节,与市面上大部分可视化构建系统基本类似。左边对应组件区,中间是画布区,右边是属性区。大致的操作流程是将左侧的组件拖到中间的画布上,选中组件,右侧的属性面板会显示该组件关联的属性。编辑右侧的属性,画布中对应的组件样式会同步更新。页面拼接完成后,可以通过预览按钮预览页面。如果预览正确,您可以通过发布按钮发布活动。当然还有撤销、重做等操作。今天我们要讨论的是在画布中选中指定的组件,右侧的属性面板会显示该组件关联的表单。如果修改右侧的表单,画布中组件的样式会同步更新。先看下编辑器的全局数据结构:状态。components.push(组件);},setActive(state,id){state.currentElement=id;},updateComponent(state,{id,key,value,isProps}){constupdatedComponent=state.components.find((component)=>component.id===(id||state.currentElement));if(updatedComponent){if(isProps){updatedComponent.props[key]=value;}else{updatedComponent[key]=value;}}},},getters:{getCurrentElement:(state)=>{returnstate.components.find((component)=>component.id===state.currentElement);},}}editor存放的是components(所有组件数据)和currentElement(当前选中的组件信息)。当点击左侧的业务组件时,会触发业务组件的click事件,然后会触发addComponentToEditor,将组件添加到编辑器存储的组件中。我们在这里添加一个普通的文本组件,然后看一下它的初始属性:{actionType:"",backgroundColor:"",borderColor:"#000",borderRadius:"0",borderStyle:"none",borderWidth:"0",boxShadow:"000#000000",颜色:"#000000",fontFamily:"",fontSize:"14px",fontStyle:"normal",fontWeight:"normal",height:"36px",left:“97.5px”,lineHeight:“1”,不透明度:1,paddingBottom:“0px”,paddingLeft:“0px”,paddingRight:“0px”,paddingTop:“0px”,位置:“absolute”,right:“0”,tag:"p",text:"textcontent",textAlign:"center",textDecoration:"none",top:"232px",url:"",width:"125px"}在画布中选中时当使用文本组件时,会触发setActive更新currentElement。(当前正在操作的组件可以通过getCurrentElement获取)。这时候我们应该如何添加属性和表单的基本对应关系呢?这也是本文的主题:低代码平台的属性面板如何设计?1、属性面板应该包含哪些内容?我们的ChobaLego平台中有很多业务组件,每一个丰富的交互页面都是由这些业务组件组成的,每个组件又包含一些通用的属性和组件特有的属性,反映了当前组件的各种状态,非常复杂.对于单个组件,属性面板应该是语义化的。无论您是开发人员还是非开发人员,都可以通过属性面板的操作区直观地了解一个组件的属性有哪些以及如何使用和编辑。那么属性面板应该包含什么呢?标签:属性名称。这样可以明确的告诉具体属性的作用,比如元素的宽高、边框、背景色等。description:属性的描述信息。对于一些特殊的属性,您可能无法在第一时间通过标签直观地识别属性的含义,可以添加描述信息进行阐述。内容:属性渲染器。用户可以在此基础上修改属性。最常见的有textarea,input,select等。error:属性校验信息。当用户输入非法或类型不匹配时,可以给出适当的错误信息。通过上面的描述,我们会发现其实这也是我们常用的形式。2.属性与组件的映射关系其实在以上四块内容中,内容渲染器应该是最复杂的。最重要的是使用合适的渲染器来渲染相应的属性。但是有些场景有些属性可以被多个renderer渲染,比如fontsize-fontSize,input-number和slider都可以使用。那么如何为这种场景选择最合适的渲染器呢?其实我觉得这种情况完全可以取决于开发者和用户的综合意愿,没有绝对的对错。对应以上组件的props信息,我们可以对这些属性进行分类。分类标准是什么?我觉得应该把属性和js中的数据类型进行映射,然后根据具体的分类选择合适的renderer。我们知道,在JavaScript中,一共有七种数据类型,字符串(String)、数字(Number)、布尔(Boolean)、空(Null)、未定义(Undefined)、符号(Symbol)和对象(Object)。对象类型包括:数组(Array)、函数(Function),以及两种特殊对象:正则(RegExp)和日期(Date)。空(Null)、未定义(Undefined)、符号和正则(RegExp)在渲染器中基本不用。我们先看看String、Number、Boolean和Date可能的渲染方式:String(字符串)渲染器类型组件inputtextareaNumber(数字)渲染器类型组件input-numbersliderBoolean(布尔)渲染器类型组件switchDate(日期)渲染器类型组件date除了这些类型之外,还有对象(Object)、数组(Array)、函数(Function)。对象和数组是比较复杂的类型,但是我们可以将它们抽象成多级(可以理解为嵌套)的基本数据类型:渲染器类型组件数组一般像数组一样以下拉框的形式显示。至于函数(Function),可以是预定义的形式:renderer类型的组件函数就在这里,不难想象我们需要维护一个属性和一个表单组件的对应关系。属性对应上面的key,比如borderColor、text、width、fontFamily等,那components呢?组件其实就是属性的具体表现。比如width可以使用数字输入框,text可以使用普通的输入框,但是对于一些比较复杂的Features,我们自己去实现这些组件就显得捉襟见肘了。这时候我们可以考虑结合已有的组件库(这里我使用的是AntDesignVue)。那么这样一来,属性prop与组件base的对应关系就是:constmapPropsToComponents={text:{component:"a-input",},width:{component:"a-input-number",},borderWidth:{component:"a-slider",},//...}但这只是满足了一般的基础组件设计,像一些独特的属性或者基础组件无法满足的情况,我们需要对其进行扩展:rendering上面提到的上传组件和选色组件需要单独实现。3.属性分类仅仅有属性和组件之间的对应关系是不够的。每个组件都会对应大量的表单属性,因此需要根据它们的功能对其进行分类。基本属性是每个组件特有的一些属性。除了基本属性外,其余都是所有组件的通用属性。属性分类虽然是一个比较简单的实现,但是可以给用户带来很大的好处,可以清楚的知道每个属性的变化对组件的不同影响。4.更新表格,更新数据到属性。有了以上准备工作,最重要的一步就是选择组件,属性面板显示组件关联的表单属性,修改属性,组件数据会同步更新。根据我以往的经验:在设计一个表单组件的时候,有两件事是必须的:表单的初始值(defaultvalue),以及初始显示的改变表单属性的事件(default是change)。对于不同的表单,初始值参数的处理与属性改变后的处理不同:对于高度、宽度等数值类型,传入表单时应为number(24)类型,属性后为changed,事件参数类型为string(24px)字体是否为粗体、斜体、下划线,传入表单时为boolean(true/false)类型,属性改变后,事件参数应该是string(bold/normal)类型的,所以给每个属性传入表单,事件发生变化后,必须额外增加一个转换函数来处理值:initialValueConverteventChangeValueConvert并且在给属性赋值的时候,不是所有的表单控件接收值,比如checkbox被选中,这种单独抽象一个属性valueProp可以用来控制它。其次,和上面说的父子级渲染一样,除了component之外,还额外增加了一个subComponent。上述配置完成后,属性与组件的对应关系为:)=>(v?parseInt(v):""),eventChangeValueConvert:(e)=>(e?`${e}px`:""),text:"width",},textAlign:{component:"a-radio-group",subComponent:"a-radio-button",eventName:"change",valueProp:"value",eventChangeValueConvert:(e)=>e.target.value,text:"alignment",选项:[{value:"left",text:"left"},{value:"center",text:"middle"},{value:"right",text:"right"},],},//...}我们的数据始终保持自上而下的顺序,这意味着表单更新最终将反映回整个商店。这时,我们在相应的组件中发出一个事件(变化)。当变化发生时,我们可以知道哪个元素的哪个属性以及新值是什么。我们利用这些信息来更新值,让store更新完成后,更新元素的props,完成整个数据流。5.参考链接https://mp.weixin.qq.com/s/u2AkeXiL0pi4799ccjR_Tg