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

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

基于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>

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

例子

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

小结

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

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即可,代码同步更新。

目录
相关文章
|
19天前
|
JavaScript 前端开发 API
Vue.js:现代前端开发的强大框架
【10月更文挑战第11天】Vue.js:现代前端开发的强大框架
62 41
|
11天前
|
JavaScript 前端开发 Docker
前端全栈之路Deno篇(二):几行代码打包后接近100M?别慌,带你掌握Deno2.0的安装到项目构建全流程、剖析构建物并了解其好处
在使用 Deno 构建项目时,生成的可执行文件体积较大,通常接近 100 MB,而 Node.js 构建的项目体积则要小得多。这是由于 Deno 包含了完整的 V8 引擎和运行时,使其能够在目标设备上独立运行,无需额外安装依赖。尽管体积较大,但 Deno 提供了更好的安全性和部署便利性。通过裁剪功能、使用压缩工具等方法,可以优化可执行文件的体积。
前端全栈之路Deno篇(二):几行代码打包后接近100M?别慌,带你掌握Deno2.0的安装到项目构建全流程、剖析构建物并了解其好处
|
11天前
|
监控 JavaScript 前端开发
前端的混合之路Meteor篇(六):发布订阅示例代码及如何将Meteor的响应数据映射到vue3的reactive系统
本文介绍了 Meteor 3.0 中的发布-订阅模型,详细讲解了如何在服务器端通过 `Meteor.publish` 发布数据,包括简单发布和自定义发布。客户端则通过 `Meteor.subscribe` 订阅数据,并使用 MiniMongo 实现实时数据同步。此外,还展示了如何在 Vue 3 中将 MiniMongo 的 `cursor` 转化为响应式数组,实现数据的自动更新。
|
21天前
|
JavaScript 前端开发 API
Vue.js:打造高效前端应用的最佳选择
【10月更文挑战第9天】Vue.js:打造高效前端应用的最佳选择
16 2
|
7天前
|
JavaScript UED
"Vue实战技巧大揭秘:一招解决路由跳转页面不回顶部难题,让你的单页面应用用户体验飙升!"
【10月更文挑战第23天】在Vue单页面应用中,点击路由跳转时,默认情况下页面不会自动滚动到顶部,这可能影响用户体验。本文通过一个新闻网站的案例,介绍了如何使用Vue-router的全局前置守卫和`scrollBehavior`方法,实现路由跳转时页面自动滚动到顶部的功能,提升用户浏览体验。
30 0
|
11天前
|
前端开发 安全 API
前端全栈之路Deno篇(三):一次性搞懂和学会用Deno 2.0 的权限系统详解和多种权限配置权限声明方式
本文深入解析了 Deno 2.0 的权限系统,涵盖主包和第三方包的权限控制机制,探讨了通过命令行参数、权限 API 和配置文件等多种权限授予方式,并提供了代码示例和运行指导,帮助开发者有效管理权限,提升应用安全性。
|
12天前
|
缓存 前端开发 JavaScript
前端架构思考:代码复用带来的隐形耦合,可能让大模型造轮子是更好的选择-从 CDN 依赖包被删导致个站打不开到数年前因11 行代码导致上千项目崩溃谈谈npm黑洞 - 统计下你的项目有多少个依赖吧!
最近,我的个人网站因免费CDN上的Vue.js包路径变更导致无法访问,引发了我对前端依赖管理的深刻反思。文章探讨了NPM依赖陷阱、开源库所有权与维护压力、NPM生态问题,并提出减少不必要的依赖、重视模块设计等建议,以提升前端项目的稳定性和可控性。通过“left_pad”事件及个人经历,强调了依赖管理的重要性和让大模型代替人造轮子的潜在收益
|
18天前
|
前端开发 JavaScript 开发工具
前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(三)
前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(三)
27 0
|
18天前
|
Web App开发 前端开发 JavaScript
前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(二)
前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(二)
38 0
|
18天前
|
Web App开发 移动开发 前端开发
前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(一)
前端代码规范和质量是确保项目可维护性、可读性和可扩展性的关键(一)
36 0