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
Page build
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
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.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。