基於Vue和Quasar的前端SPA專案實戰之拖拽表單定製(十六)
回顧
通過前一篇文章 基於Vue和Quasar的前端SPA專案實戰之動態表單(五)的介紹,實現了後設資料中動態表單設計功能,支援常見的資料型別和索引,然後實現了動態表單的crud增刪改查功能,所有的表單頁面都是預設的風格。本文主要介紹拖拽表單定製功能,通過拖拽的方式定製表單錄入和編輯頁面,滿足了個性化需求。
簡介
針對後設資料表的每個欄位,通過拖拽方式決定是否顯示或者隱藏,然後還可以配置顯示的寬度。最終以json格式儲存到後臺資料庫,執行時根據配置動態渲染錄入和編輯表單form頁面。針對不同的裝置(電腦,平板,手機)都可以單獨定製。
UI介面
頁面構建
執行時
程式碼
說明
採用開源框架vuesortable,基於vue的實現排序,支援拖拽。頁面構建分為左中右三個部分,左邊為候選欄位,中間為需要顯示的欄位,右邊可以針對每個欄位單獨設定一些屬性,比如寬度等。
資料表
建立表單tableFormBuilder,用於儲存頁面構建json資料,包括型別type、裝置device、內容body等欄位, 充分利用crudapi功能,API部分零程式碼實現。
tableFormBuilder
核心程式碼
頁面構建
<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>
通過draggable標籤實現
執行時渲染
<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>
判斷是否存在定製頁面,如果存在動態渲染,否則採用預設頁面佈局。
例子
以產品為例,配置好錄入頁面之後,執行時原來的預設錄入頁面用新的頁面代替,新的表單頁面和之前配置的表單頁面一致,功能不受影響,可以正常的錄入資料。
小結
本文主要通過拖拽方式實現表單定製功能,使用非常方便,零程式碼定製表單錄入和編輯頁面,滿足了個性化需求,整個過程無需寫程式碼。
crudapi簡介
crudapi是crud+api組合,表示增刪改查介面,是一款零程式碼可配置的產品。使用crudapi可以告別枯燥無味的增刪改查程式碼,讓您更加專注業務,節約大量成本,從而提高工作效率。crudapi的目標是讓處理資料變得更簡單,所有人都可以免費使用!無需程式設計,通過配置自動生成crud增刪改查RESTful API,提供後臺UI管理業務資料。基於主流的開源框架,擁有自主智慧財產權,支援二次開發。
demo演示
crudapi屬於產品級的零程式碼平臺,不同於自動程式碼生成器,不需要生成Controller、Service、Repository、Entity等業務程式碼,程式執行起來就可以使用,真正0程式碼,可以覆蓋基本的和業務無關的CRUD RESTful API。
官網地址:https://crudapi.cn
測試地址:https://demo.crudapi.cn/crudapi/login
附原始碼地址
GitHub地址
https://github.com/crudapi/crudapi-admin-web
Gitee地址
https://gitee.com/crudapi/crudapi-admin-web
由於網路原因,GitHub可能速度慢,改成訪問Gitee即可,程式碼同步更新。