「免费开源」基于Vue和Quasar的前端SPA项目crudapi零代码开发平台后台管理系统实战之之拖拽表单定制(十六)

  • A+
所属分类:Web前端
摘要

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


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

回顾

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

简介

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

UI界面

「免费开源」基于Vue和Quasar的前端SPA项目crudapi零代码开发平台后台管理系统实战之之拖拽表单定制(十六)
页面构建

「免费开源」基于Vue和Quasar的前端SPA项目crudapi零代码开发平台后台管理系统实战之之拖拽表单定制(十六)
运行时

代码

说明

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

数据表

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

「免费开源」基于Vue和Quasar的前端SPA项目crudapi零代码开发平台后台管理系统实战之之拖拽表单定制(十六)
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> 

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

例子

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

小结

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

crudapi简介

crudapi是crud+api组合,表示增删改查接口,是一款零代码可配置的产品。使用crudapi可以告别枯燥无味的增删改查代码,让您更加专注业务,节约大量成本,从而提高工作效率。crudapi的目标是让处理数据变得更简单,所有人都可以免费使用!无需编程,通过配置自动生成crud增删改查RESTful API,提供后台UI管理业务数据。基于主流的开源框架,拥有自主知识产权,支持二次开发。

demo演示

crudapi属于产品级的零代码平台,不同于自动代码生成器,不需要生成Controller、Service、Repository、Entity等业务代码,程序运行起来就可以使用,真正0代码,可以覆盖基本的和业务无关的CRUD RESTful API。

官网地址:https://crudapi.cn
测试地址:https://demo.crudapi.cn/crudapi/login

附源码地址

GitHub地址

https://github.com/crudapi/crudapi-admin-web

Gitee地址

https://gitee.com/crudapi/crudapi-admin-web

由于网络原因,GitHub可能速度慢,改成访问Gitee即可,代码同步更新。