基于Vue和Quasar的前端SPA項(xiàng)目實(shí)戰(zhàn)之拖拽表單定制(十六)
回顧
通過(guò)前一篇文章 基于Vue和Quasar的前端SPA項(xiàng)目實(shí)戰(zhàn)之動(dòng)態(tài)表單(五)的介紹,實(shí)現(xiàn)了元數(shù)據(jù)中動(dòng)態(tài)表單設(shè)計(jì)功能姿染,支持常見(jiàn)的數(shù)據(jù)類型和索引苛蒲,然后實(shí)現(xiàn)了動(dòng)態(tài)表單的crud增刪改查功能,所有的表單頁(yè)面都是默認(rèn)的風(fēng)格蔫劣。本文主要介紹拖拽表單定制功能诡必,通過(guò)拖拽的方式定制表單錄入和編輯頁(yè)面脊框,滿足了個(gè)性化需求涤妒。
簡(jiǎn)介
針對(duì)元數(shù)據(jù)表的每個(gè)字段单雾,通過(guò)拖拽方式?jīng)Q定是否顯示或者隱藏,然后還可以配置顯示的寬度她紫。最終以json格式保存到后臺(tái)數(shù)據(jù)庫(kù)硅堆,運(yùn)行時(shí)根據(jù)配置動(dòng)態(tài)渲染錄入和編輯表單form頁(yè)面。針對(duì)不同的設(shè)備(電腦贿讹,平板渐逃,手機(jī))都可以單獨(dú)定制。
UI界面
頁(yè)面構(gòu)建
運(yùn)行時(shí)
代碼
說(shuō)明
采用開(kāi)源框架vuesortable围详,基于vue的實(shí)現(xiàn)排序朴乖,支持拖拽。頁(yè)面構(gòu)建分為左中右三個(gè)部分助赞,左邊為候選字段买羞,中間為需要顯示的字段,右邊可以針對(duì)每個(gè)字段單獨(dú)設(shè)置一些屬性雹食,比如寬度等畜普。
數(shù)據(jù)表
創(chuàng)建表單tableFormBuilder,用于存儲(chǔ)頁(yè)面構(gòu)建json數(shù)據(jù)群叶,包括類型type吃挑、設(shè)備device、內(nèi)容body等字段, 充分利用crudapi功能街立,API部分零代碼實(shí)現(xiàn)舶衬。
tableFormBuilder
核心代碼
頁(yè)面構(gòu)建
<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>
通過(guò)draggable標(biāo)簽實(shí)現(xiàn)
運(yùn)行時(shí)渲染
<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>
判斷是否存在定制頁(yè)面,如果存在動(dòng)態(tài)渲染赎离,否則采用默認(rèn)頁(yè)面布局逛犹。
例子
以產(chǎn)品為例,配置好錄入頁(yè)面之后梁剔,運(yùn)行時(shí)原來(lái)的默認(rèn)錄入頁(yè)面用新的頁(yè)面代替虽画,新的表單頁(yè)面和之前配置的表單頁(yè)面一致,功能不受影響荣病,可以正常的錄入數(shù)據(jù)码撰。
小結(jié)
本文主要通過(guò)拖拽方式實(shí)現(xiàn)表單定制功能,使用非常方便个盆,零代碼定制表單錄入和編輯頁(yè)面脖岛,滿足了個(gè)性化需求,整個(gè)過(guò)程無(wú)需寫代碼砾省。