美文网首页crudapi-admin-web
免费开源」基于Vue和Quasar的前端SPA项目crudapi

免费开源」基于Vue和Quasar的前端SPA项目crudapi

作者: crudapi | 来源:发表于2022-07-18 09:52 被阅读0次

    基于Vue和Quasar的前端SPA项目实战之拖拽表单定制(十六)

    回顾

    通过前一篇文章 基于Vue和Quasar的前端SPA项目实战之动态表单(五)的介绍,实现了元数据中动态表单设计功能,支持常见的数据类型和索引,然后实现了动态表单的crud增删改查功能,所有的表单页面都是默认的风格。本文主要介绍拖拽表单定制功能,通过拖拽的方式定制表单录入和编辑页面,满足了个性化需求。

    简介

    针对元数据表的每个字段,通过拖拽方式决定是否显示或者隐藏,然后还可以配置显示的宽度。最终以json格式保存到后台数据库,运行时根据配置动态渲染录入和编辑表单form页面。针对不同的设备(电脑,平板,手机)都可以单独定制。

    UI界面

    formbuilder
    页面构建 runtime
    运行时

    代码

    说明

    采用开源框架vuesortable,基于vue的实现排序,支持拖拽。页面构建分为左中右三个部分,左边为候选字段,中间为需要显示的字段,右边可以针对每个字段单独设置一些属性,比如宽度等。

    数据表

    创建表单tableFormBuilder,用于存储页面构建json数据,包括类型type、设备device、内容body等字段, 充分利用crudapi功能,API部分零代码实现。

    tableFormBuilder
    tableFormBuilder

    核心代码

    页面构建

    <draggable
      class="dragArea list-group row"
      :list="selectedList"
      group="people"
      @change="log"
    >
      <div class="list-group-item q-pa-md" 
        v-for="formElement in selectedList"
        :key="formElement.columnId"
        :class="formElement | classFormat(currentElement)"
        @click="selectForEdit(formElement)"
      > 
        <div>
          <div 
            v-bind:class="{ 'required': !formElement.column.nullable}">
            {{formElement.column.caption}}:
          </div>
          <q-input v-if="isStringType(formElement)"
            readonly
            :placeholder="formElement.column.description"
            :type="formElement.isPwd ? 'password' : 'text'"
            v-model="formElement.column.value" >
            <template v-slot:append v-if="!formElement.isText" >
              <q-icon
                :name="formElement.isPwd ? 'visibility_off' : 'visibility'"
                class="cursor-pointer"
                @click="formElement.isPwd = !formElement.isPwd"
              />
            </template>
          </q-input>
    
          <q-editor readonly v-else-if="isTextType(formElement)"
            v-model="textValue"
            :placeholder="formElement.column.description" >
          </q-editor>
    
          <q-input v-else-if="isDateTimeType(formElement)" readonly>
            <template v-slot:prepend>
              <q-icon name="event" class="cursor-pointer">
                <q-popup-proxy ref="qDateProxy" transition-show="scale" transition-hide="scale">
                  <q-date
                  mask="YYYY-MM-DD HH:mm:ss"
                  @input="hideRefPopProxyAction('qDateProxy')" />
                </q-popup-proxy>
              </q-icon>
            </template>
    
            <template v-slot:append>
              <q-icon name="access_time" class="cursor-pointer">
                <q-popup-proxy ref="qTimeProxy" transition-show="scale" transition-hide="scale">
                  <q-time mask="YYYY-MM-DD HH:mm:ss"
                  format24h with-seconds
                  @input="hideRefPopProxyAction('qTimeProxy')" />
                </q-popup-proxy>
              </q-icon>
            </template>
          </q-input>
    
          <q-input v-else-if="isDateType(formElement)" readonly>
            <template v-slot:append>
              <q-icon name="event" class="cursor-pointer">
                <q-popup-proxy ref="qDateProxy" transition-show="scale" transition-hide="scale">
                  <q-date
                  mask="YYYY-MM-DD"
                  @input="hideRefPopProxyAction('qDateProxy')" />
                </q-popup-proxy>
              </q-icon>
            </template>
          </q-input>
    
          <q-input v-else-if="isTimeType(formElement)" readonly>
            <template v-slot:append>
              <q-icon name="access_time" class="cursor-pointer">
                <q-popup-proxy ref="qTimeProxy" transition-show="scale" transition-hide="scale">
                  <q-time  mask="HH:mm:ss"
                  format24h with-seconds
                  @input="hideRefPopProxyAction('qTimeProxy')" />
                </q-popup-proxy>
              </q-icon>
            </template>
          </q-input>
    
          <q-toggle v-else-if="isBoolType(formElement)" readonly
            v-model="formElement.column.value">
          </q-toggle>
    
          <q-input readonly
            v-else-if="isNumberType(formElement)"
            :placeholder="formElement.column.description"
            type="number"
            v-model="formElement.column.value" >
          </q-input>
    
          <CFile v-else-if="isAttachmentType(formElement)"
            v-model="formElement.column.value" >
          </CFile>
    
          <q-input v-else
            readonly
            :placeholder="formElement.column.description"
            :type="formElement.isPwd ? 'password' : 'text'"
            v-model="formElement.column.value" >
            <template v-slot:append v-if="!formElement.isText" >
              <q-icon
                :name="formElement.isPwd ? 'visibility_off' : 'visibility'"
                class="cursor-pointer"
                @click="formElement.isPwd = !formElement.isPwd"
              />
            </template>
          </q-input>
        </div>
        <div class="row reverse editable-element-action-buttons">
          <div class="justify-end q-pt-xs">
            <q-btn 
              @click="deleteElement(formElement)"
              v-if="isSelectedForEdit(formElement)" 
              class="editable-element-button" 
              color="red" 
              icon="delete" 
              round unelevated  size="xs">
              <q-tooltip>移除</q-tooltip>
            </q-btn>
          </div>
        </div>
      </div>
    </draggable>
    

    通过draggable标签实现

    运行时渲染

    <div v-if="selectedList.length > 0" class="row">
      <div class="list-group-item q-pa-md" 
        v-for="formElement in selectedList"
        :key="formElement.columnId"
        :class="formElement | classFormat">
        <div>
          <div 
            v-bind:class="{ 'required': !formElement.column.nullable}">
            {{formElement.column.caption}}:
          </div>
    
          <div class="row items-baseline content-center"
            style="border-bottom: 1px solid rgba(0,0,0,0.12)" 
            v-if="formElement.column.relationTableName">
            <div class="col-11">
              <span>{{ formElement.column.value | relationDataFormat(formElement.column) }}</span>
            </div>
            <div class="col-1">
              <q-btn round dense flat icon="zoom_in" 
              @click="openDialogClickAction(formElement.column)" />
            </div>
          </div>
    
          <q-input v-else-if="isStringType(formElement.column.dataType)"
            v-model="formElement.column.value"
            :placeholder="formElement.column.description"
            :type="formElement.isPwd ? 'password' : 'text'" >
            <template v-slot:append v-if="!formElement.isText" >
              <q-icon
                :name="formElement.isPwd ? 'visibility_off' : 'visibility'"
                class="cursor-pointer"
                @click="formElement.isPwd = !formElement.isPwd"
              />
            </template>
          </q-input>
    
          <q-editor  v-else-if="isTextType(formElement.column.dataType)"
            v-model="formElement.column.value"
            :placeholder="formElement.column.description" >
          </q-editor>
    
          <q-input v-else-if="isDateTimeType(formElement.column.dataType)"
            v-model="formElement.column.value" >
            <template v-slot:prepend>
              <q-icon name="event" class="cursor-pointer">
                <q-popup-proxy ref="qDateProxy" transition-show="scale" transition-hide="scale">
                  <q-date v-model="formElement.column.value"
                  mask="YYYY-MM-DD HH:mm:ss"
                  @input="hideRefPopProxyAction('qDateProxy')" />
                </q-popup-proxy>
              </q-icon>
            </template>
    
            <template v-slot:append>
              <q-icon name="access_time" class="cursor-pointer">
                <q-popup-proxy ref="qTimeProxy" transition-show="scale" transition-hide="scale">
                  <q-time  v-model="formElement.column.value"
                  mask="YYYY-MM-DD HH:mm:ss"
                  format24h with-seconds
                  @input="hideRefPopProxyAction('qTimeProxy')" />
                </q-popup-proxy>
              </q-icon>
            </template>
          </q-input>
    
           <q-input v-else-if="isDateType(formElement.column.dataType)" 
            v-model="formElement.column.value">
            <template v-slot:append>
              <q-icon name="event" class="cursor-pointer">
                <q-popup-proxy ref="qDateProxy" transition-show="scale" transition-hide="scale">
                  <q-date  v-model="formElement.column.value"
                  mask="YYYY-MM-DD"
                  @input="hideRefPopProxyAction('qDateProxy')" />
                </q-popup-proxy>
              </q-icon>
            </template>
          </q-input>
    
          <q-input v-else-if="isTimeType(formElement.column.dataType)"
           v-model="formElement.column.value" >
            <template v-slot:append>
              <q-icon name="access_time" class="cursor-pointer">
                <q-popup-proxy ref="qTimeProxy" transition-show="scale" transition-hide="scale">
                  <q-time   v-model="formElement.column.value" 
                  mask="HH:mm:ss"
                  format24h with-seconds
                  @input="hideRefPopProxyAction('qTimeProxy')" />
                </q-popup-proxy>
              </q-icon>
            </template>
          </q-input>
    
          <q-toggle v-else-if="isBoolType(formElement.column.dataType)"
           v-model="formElement.column.value" >
          </q-toggle>
    
          <q-input 
            v-else-if="isNumberType(formElement.column.dataType)"
            v-model="formElement.column.value"
            :placeholder="formElement.column.description"
            type="number">
          </q-input>
    
          <CFile v-else-if="isAttachmentType(formElement.column.dataType)"
           v-model="formElement.column.value"
           @input="(data)=>{
            formElement.column.value = data.url;
           }"></CFile>
    
          <q-input v-else
            v-model="formElement.column.value"
            :placeholder="formElement.column.description"
            :type="formElement.isPwd ? 'password' : 'text'" >
            <template v-slot:append v-if="!formElement.isText" >
              <q-icon
                :name="formElement.isPwd ? 'visibility_off' : 'visibility'"
                class="cursor-pointer"
                @click="formElement.isPwd = !formElement.isPwd"
              />
            </template>
          </q-input>
        </div>
      </div>
    </div>
    

    判断是否存在定制页面,如果存在动态渲染,否则采用默认页面布局。

    例子

    以产品为例,配置好录入页面之后,运行时原来的默认录入页面用新的页面代替,新的表单页面和之前配置的表单页面一致,功能不受影响,可以正常的录入数据。

    小结

    本文主要通过拖拽方式实现表单定制功能,使用非常方便,零代码定制表单录入和编辑页面,满足了个性化需求,整个过程无需写代码。

    相关文章

      网友评论

        本文标题:免费开源」基于Vue和Quasar的前端SPA项目crudapi

        本文链接:https://www.haomeiwen.com/subject/reirirtx.html