Drag and drop form customization of front-end spa project based on Vue and Quasar (16)
review
Pass the previous articleDynamic form of front-end spa project practice based on Vue and Quasar (V)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 article mainly introduces the drag and drop form customization function, which can customize the form entry and editing pages by dragging to meet the personalized needs.
brief introduction
For each field in the metadata table, you can determine 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. When running, it dynamically renders the input and edit form form page according to the configuration. 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. 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 to store page construction JSON data, including type, device device, content body and other fields. Make full use of the 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>
Determine whether there is a customized page. If there is dynamic rendering, otherwise, the default page layout is used.
example
Take the product as an example. After the entry page is configured, the original default entry 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
In this paper, the form customization function is realized by dragging and dropping. It is very convenient to use. The form entry and editing pages are customized with zero code, which meets the personalized needs. 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 querying interfaces. It is a zero code configurable product. Using crudapi can bid farewell to the tedious addition, deletion, modification and query of code, so that you can focus more on business, save a lot of costs, and improve work efficiency. The goal of crudapi is to make it easier to process data, and everyone can use it for free! No programming is required. The crud is automatically generated through configuration. The addition, deletion, modification and query restful API provides background UI management business data. Based on the mainstream open source framework, it has independent intellectual property rights and supports secondary development.
Demo demo
Crudapi is a product level zero code platform, which is different from the automatic code generator. It does not need to generate business codes such as controller, service, repository and entity. It can be used when the program is running. The real 0 code can cover the basic business independent crud restful API.
Official website address:https://crudapi.cn
Test address:https://demo.crudapi.cn/crudapi/login
Source code address attached
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.