头图

Drag and drop form customization of front-end SPA project based on Vue and Quasar (16)

review

Through the previous article Vue and Quasar-based front-end SPA project actual combat dynamic form (5) , realizes the dynamic form design function in the metadata, supports common data types and indexes, and then realizes the crud of the dynamic form Add, delete, modify and check function, all form pages are in default style. This article mainly introduces the drag-and-drop form customization function. The form entry and editing pages are customized by drag-and-drop method to meet individual needs.

Introduction

For each field in the metadata table, drag and drop to decide whether to show or hide, and then you can also configure the width of the display. Finally, it is saved to the back-end database in json format, and the input and edit form pages are dynamically rendered during runtime according to the configuration. It can be individually customized for different devices (computers, tablets, mobile phones).

UI interface

formbuilder
Page build

runtime
Runtime

Code

illustrate

Using the open source framework vuesortable, sorting based on vue, supports drag and drop. The page construction is divided into three parts: the left, the middle and the right. The left is the candidate field, the middle is the field that needs to be displayed, and the right can set some attributes for each field separately, such as width.

data sheet

Create a form tableFormBuilder, used to store page construction json data, including fields such as type, device, content body, etc., make full use of the crudapi function, and implement the API part with zero code.

tableFormBuilder
tableFormBuilder

Core code

Page build
<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>

Implemented through draggable tags

Runtime rendering
<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>

Determine whether there is a custom page, if there is dynamic rendering, otherwise use the default page layout.

example

Take the product as an example. After the entry page is configured, the original default entry page at runtime is replaced with a new page. The new form page is the same as the previously configured form page. The function is not affected and data can be entered normally.

summary

This article mainly implements the form customization function by dragging and dropping, which is very convenient to use. The zero-code custom form entry and editing page meets the individual needs, and the whole process does not need to write code.

Introduction to crudapi

Crudapi is a combination of crud+api, which means adding, deleting, modifying and checking interface. It is a zero-code configurable product. Using crudapi can say goodbye to boring additions, deletions, and code changes, allowing you to focus more on your business, save a lot of costs, and improve work efficiency. The goal of crudapi is to make processing data easier, and everyone can use it for free! No programming is required. The RESTful API is automatically generated through configuration to add, delete, modify, and check crud, and provide back-end UI to manage business data. Based on the mainstream open source framework, with independent intellectual property rights, it supports secondary development.

demo

Crudapi is a product-level zero-code platform, which is different from automatic code generators. It does not need to generate business codes such as Controller, Service, Repository, Entity, etc., and can be used when the program is running. The real zero code can cover basic CRUD that has nothing to do with business. RESTful API.

Official website address: https://crudapi.cn
Test address: https://demo.crudapi.cn/crudapi/login

Attached source code address

GitHub address

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

Gitee address

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

Due to network reasons, GitHub may be slow, just change to visit Gitee, and the code will be updated synchronously.


crudapi
38 声望4 粉丝

crudapi是crud+api组合,表示增删改查接口,是一款零代码可配置的产品。使用crudapi可以告别枯燥无味的增删改查代码,让您更加专注业务,节约大量成本,从而提高工作效率。crudapi的目标是让处理数据变得更简单!