vue动态表单简单设计JSX版本

template

正常vue开发中我们常常使用的template模版的形式,这也是vue的一大特色,模版语法是vue内部定制的一套规则,通过对模版的解析生成真实的code。其中还有vnode,Diff等等优化处理。总的看来说vue的模版语法给了我们一个下限,对vue的开发进行了兜底,同时也有了各种限制和学习成本,好在不高。

但是template的写法毕竟存在很大的局限性质,无法给我们提供自由的开发过程。

JSX

JSX的写法非常接近原生的写法,可以用js来编写html结构,提供了高灵活性。JSX的语法使得开发者能够以更接近自然语言的方式描述用户界面,‌减少了编写和维护代码的复杂性,‌同时也使得UI的设计和实现更加直观。

在Vue3很好的接入JSX之后,在Vue中也可以很舒服的使用JSX语法,相比较react在写法上基本相同,只是会存在细微的差距,一次上手就爱上了JSX的舒适性。

低代码

既然都有了通用渲染模版,那为啥还需要通过数据去配置页面结构呢,在此基础上加上物料区,和操作平台,不就成了低代码平台了嘛。(第三篇文章水低代码平台实现的过程)

具体实现

动态渲染的核心就是用数据驱动页面,首先就得需要定制数据的协议。 常见的协议比如钉钉的宜搭通过对JSON数据的定义,去实现一套通用的渲染方法。

针对业务需要的,主要的渲染逻辑

 function externalSkeleton(item, index) {
      if (item instanceof Array) {
        return item.map((e, i) => externalSkeleton(e, i));
      }
      if (item.type == 'title') {
        let childrenDom = []
        if (item.select && Array.isArray(item.children) && item.children.length > 0) {
          childrenDom = externalSkeleton(item.children,index)
        }
        if(childrenDom.length > 0) {
          return (
            <ElRow>
              <ElCol>
                <div class="secondaryTitle-title">
                  <span class="title"> {item.label} </span>
                </div>
              </ElCol>
              {childrenDom}
            </ElRow>
          );
        }
        return null // 没有标题不展示
      } else {
        let childrenDom = []
        if (item.select) {
          if(item.children && item.children.length > 0) {
            childrenDom = externalSkeleton(item.children,index)
            return [generateDom(item, index),...childrenDom]
          }
          return generateDom(item, index)
        }
        return null
      }
    }
    
    
    const generateDom = (item, index) => {
      return (
        <ElCol span={item.span || 12} key={item.id || UUID}>
          <ElFormItem
            prop={ElFormItem.${item.id}.itemValue}
            label='item.label'
            rules={[{ required: true, message: '请输入' + item.label, trigger: 'blur' }]} 
          >
            {item.tips && (
              <>
                <span style={ item.style || { minWidth: '50px' }} className='required'>{item.unit? `${item.label} (${unit[item.unit]})` : item.label}</span>
                <ElTooltip
                  class="box-item"
                  effect="dark"
                  content={item.tips}
                  placement="top">
                  <span style={ item.style || { marginLeft: '10px' }} class="question-icon">?</span>
                </ElTooltip>
              </>
            )}
            {!item.tips && (<span style={{ minWidth: '50px' }} className='required'>{item.unit? `${item.label} (${unit[item.unit]})` : item.label}</span>)}
            <div style={{ width: '360px' }} class="upInput">
              {typeMapping(item, index)}
            </div>
          </ElFormItem>
        </ElCol>
      )
    }

对于页面的渲染首先需要考虑的是深度优先遍历,得从最深处的子级开始渲染,子集的渲染结果来判断上层是否需要进行相应的处理。

对渲染数据和提交数据是两套树形结构,内部根据协议定制相关的属性,比如说简单的。

    id: val.id,
    itemText : val.attrName,
    itemValue: itemValueVal || a.itemValue,
    translate: translate || a.itemDescript,
    files: a.files || a.annexList,
    itemDescript: a.itemDescript,
    optionType: val.optionType,
    isFile: val.isFile,
    parentName: val.parentName,
    projectTypeName: val.projectTypeName,
    unit: val.unit

最好是根据成熟的低代码方案定制,虽然只是动态渲染,但是渲染器原理差不多,只是中间的环境多少和复杂程度

对于事件的绑定,可以在配置渲染JSON的时候使用字符串的形式传入,使用eval()方法来运行,需要考虑的是安全性的问题。

typeMapping中根据JSON提供的数据来动态的选择当前需要渲染的组件,比如

 const typeMapping = (item, index) => {
      let returnElement;
      switch (item.type) {
        case 'input':
          if (inputValue.value[item.id]) {
            returnElement = item.isFile == 1 ? (
              <AssociatedUploadInput
                singleInput={true}
                vModel={inputValue.value[item.id].itemValue}
                onInputChange={associatedInputChange}
                isNumber={item.unit}
                onFileList={(e)=>{fileListChang(e,item.id)}}
                fileList={inputValue.value[item.id].files}
              />
            ) : inputDom(item,index);
          }
          break;
        case 'radio':
        if (inputValue.value[item.id]) {
            returnElement = item.isFile == 1 ? (
              <AssociatedUploadInput
                inputType='radio'
                singleInput={true}
                vModel={inputValue.value[item.id].itemValue}
                onChange={radioGroupChange}
                onInputChange={associatedInputChange}
                onFileList={(e) => { fileListChang(e, item.id) }}
                fileList={inputValue.value[item.id].files}
                onRadioValueTranslate={(e) => { radioValueTranslate(e, item.id) }}
              />
            ) : renderRadioGroup(item, index);
          }
          break;
           // 其他类型的处理
        default:
          if (inputValue.value[item.id]) {
            returnElement = item.isFile == 1 ? (
              <AssociatedUploadInput
                singleInput={true}
                vModel={inputValue.value[item.id].itemValue}
                onInputChange={associatedInputChange}
                onFileList={(e) => { fileListChang(e, item.id) }}
                fileList={inputValue.value[item.id].files}
              />
            ) : inputDom(item, index);
          }
          break;
      }
      return returnElement;
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值