"Free and open source" drag and drop form customization of background management system of crudapi zero code development platform of front-end SPA project based on Vue and Quasar

Posted by shivers on Fri, 05 Nov 2021 03:34:31 +0100

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

review

Through the previous article Dynamic form of front-end SPA project practice based on Vue and Quasar (5) It realizes the dynamic form design function in metadata, supports common data types and indexes, and then realizes the crud addition, deletion, modification and query function of dynamic forms. All form pages are in the default style. This paper mainly introduces the drag form customization function, which can customize the form entry and editing page by dragging to meet the personalized needs.

brief introduction

For each field in the metadata table, you can decide whether to display or hide it by dragging, and then you can configure the display width. Finally, it is saved to the background database in json format, and the form page is dynamically rendered, entered and edited according to the configuration at run time. It can be customized for different devices (computers, tablets, mobile phones).

UI interface


Page construction


Runtime

code

explain

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

data sheet

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


tableFormBuilder

Core code

Page construction

<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>remove</q-tooltip>
        </q-btn>
      </div>
    </div>
  </div>
</draggable>

Implemented by draggable tag

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>

Judge whether there is a customized page. If there is dynamic rendering, otherwise, the default page layout is adopted.

example

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

Summary

This paper mainly realizes the form customization function by dragging. It is very convenient to use. Zero code customizes the form entry and editing page to meet the personalized needs. There is no need to write code in the whole process.

Introduction to crud API

Crud API is a combination of crud+api, which means the interface for adding, deleting, modifying and querying. It is a zero code configurable product. Using crud API can bid farewell to the boring addition, deletion, modification and query code, make you more focused on business, save a lot of costs, and improve work efficiency. The crud API's goal is to make it easier to process data, and everyone can use it for free! Without programming, crud is automatically generated through configuration, and RESTful API is added, deleted, modified and queried to provide background UI management business data. Based on the mainstream open source framework, it has independent intellectual property rights and supports secondary development.

Demo demo

Crud API is a product level zero code platform, which is different from automatic code generator. It does not need to generate business codes such as Controller, Service, Repository and Entity. The program can be used when running. The real 0 code can cover the basic CRUD RESTful API irrelevant to the business.

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

Attached source 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 visit Gitee and update the code synchronously.

Topics: Vue