好傢伙,
在寫專案的時候,我發現自己的平臺的元件寫的實在是太難看了,於是想去gitee上偷點東西,於是我們本期的受害者出現了
gitee專案地址
https://gitee.com/jjxliu306/ng-form-elementplus-sample.git
元件庫以及引擎完全開源,非常牛逼的專案,非常牛逼的作者
專案名:ng-form-element
整體的佈局,元件樣式,編輯器模板,元件拖動時的過渡動畫,都寫的非常漂亮,(比我寫的好看多了)
於是我決定,扒一下他的內褲,學習(抄襲)一下
0.專案解析
官方文件:NG-FORM
於是我們快速定位到引擎部分
1.開始分析
我們知道,低開的引擎做的事無非是
把資料
變成檢視
我們先來找資料
資料部分
//packages/index.vue
data() {
return {
selectItem: {},
arrow: false,
i18nkey: getUUID(),
formTemplate: this.template || {
list: [
],
config: {
labelPosition: 'left',
labelWidth: 100,
size: 'mini',
outputHidden: true, // 是否輸出隱藏欄位的值 預設開啟,所有欄位都輸出
hideRequiredMark: false,
syncLabelRequired: false,
labelSuffix: '' , // 標籤字尾
customStyle: ''
}
},
}
},
props: {
template: {
type: Object,
default: () => {
return {
list: [],
config: {
labelPosition: 'top',
labelWidth: 80,
size: 'mini',
outputHidden: true, // 是否輸出隱藏欄位的值 預設開啟,所有欄位都輸出
hideRequiredMark: false,
syncLabelRequired: false,
labelSuffix: '' , // 標籤字尾
customStyle: ''
}
}
}
},
然後來找檢視部分
//packages/index.vue
<ContainerPanel
:formTemplate="formTemplate"
@handleSelectItem="handleSelectItem"
:selectItem="selectItem"
:arrow="arrow"
>
</ContainerPanel>
import ContainerPanel from './panel-container/index.vue'
-
:formTemplate
:傳模板資料的 -
@handleSelectItem
:這是一個事件處理器,當元件內的handleSelectItem
方法被觸發時,會執行傳入的回撥函式。handleSelectItem
是元件內部定義的事件處理函式名。 -
:selectItem
:這是繫結的資料屬性,用於傳遞一個"被選中的資料" -
:arrow
:暫時沒看出來幹嘛的
//packages/panel-container/index.vue
<el-form
:label-width="formTemplate.config.labelWidth + 'px'"
class="ng-form"
:label-position="formTemplate.config.labelPosition"
:hide-required-asterisk="formTemplate.config.hideRequiredMark"
:label-suffix="formTemplate.config.labelSuffix"
ref="form"
:style="formTemplate.config.customStyle"
:size="formTemplate.config.size"
>
<el-row :gutter="20" class="row">
<draggable
tag="div"
class="draggable-box"
v-bind="{
group: 'form-draggable',
ghostClass: 'moving',
animation: 180,
handle: '.drag-move'
}"
:force-fallback="true"
v-model="formTemplate.list"
@add="dragEnd($event, formTemplate.list)"
>
<transition-group tag="div" name="list" class="items-main">
<Node
:class="{'drag-move' : record.drag_ == undefined || record.drag_ }"
v-for="record in formTemplate.list"
:key="record.key"
:record="record"
:isDrag="true"
:config="formTemplate.config"
:selectItem="selectItem"
@handleSelectItem="handleSelectItem"
@handleCopy="handleCopy(record)"
@handleDetele="handleDetele(record)"
>
</Node>
</transition-group>
</draggable>
</el-row>
</el-form>
import Item from '../items/index.vue'
-
<transition-group>
是 Vue.js 的內建過渡元件,用於給列表新增過渡效果。 - el-form的作用我們後面說
//packages/form-design/items/index.vue
<template>
<ItemNode
v-if="isLayout"
:record="record"
:disabled="disabled"
:preview="preview"
:isDragPanel="isDragPanel"
:prop-prepend="propPrepend"
:selectItem="selectItem"
:style="{'display': recordVisible ? '' : 'none'}"
:models="models"
@handleSelectItem="handleSelectItem"
>
<!-- 遞迴傳遞插槽!!! -->
<template v-for="slot in Object.keys($slots)" :slot="slot">
<slot :name="slot" :record="record"/>
</template>
</ItemNode>
<el-form-item
v-else
:label="label"
:style="{'display': recordVisible ? '' : 'none'}"
:rules="recordRules"
:prop="recordProps"
:key="record.key"
:required="recordRequired"
:id="record.model"
:name="record.model"
:label-width="labelWidth"
>
<ItemNode
:record="record"
:disabled="disabled"
:preview="preview"
:isDragPanel="isDragPanel"
:selectItem="selectItem"
:prop-prepend="propPrepend"
:models="models"
@handleSelectItem="handleSelectItem"
>
<!-- 遞迴傳遞插槽!!! -->
<template v-for="slot in Object.keys($slots)" :slot="slot">
<slot :name="slot" :record="record"/>
</template>
</ItemNode>
</el-form-item>
</template>
import ItemNode from './node.vue'
1.v-if="isLayout":是否為預覽模式
//packages/form-design/items/node.vue
<template>
<component
:record="record"
:style="{
margin: record.margin && record.margin.length > 0 ? record.margin.join('px ') + 'px' : '0px',
borderRadius: (record.itemBorderRadius ? record.itemBorderRadius : 0) + 'px',
backgroundColor: record.backgroundColor ? record.backgroundColor : '',
}"
:disabled="disabled"
:preview="preview"
:isDragPanel="isDragPanel"
:selectItem="selectItem"
:prop-prepend="propPrepend"
:models.sync="models"
@handleSelectItem="handleSelectItem"
@handleFocus="handleFocus"
@handleBlur="handleBlur"
:is="customComponent">
<!-- 遞迴傳遞插槽!!! -->
<template v-for="slot in Object.keys($slots)" :slot="slot">
<slot :name="slot" :record="record"/>
</template>
</component>
</template>
ok終於到了最後一層
最終的關鍵就是這麼行程式碼
:is="customComponent"
:動態繫結元件名稱,根據customComponent
的值來渲染不同的元件。
customComponent() {
// 判斷是否自定義元件
if(this.customComponents && this.customComponents.length > 0) {
const cs = this.customComponents.filter(t=> t.type == this.record.type)
if(cs && cs.length > 0) {
return cs[0].component
}
}
const selectItemType = this.record.type
// 將陣列對映成json
if(this.items && this.items.length > 0) {
for(let i = 0 ; i < this.items.length ; i++) {
const itemList = this.items[i]
if(itemList.list && itemList.list.length > 0) {
const fs = itemList.list.filter(t=>t.type == selectItemType)
if(fs && fs.length > 0) {
return fs[0].component
}
}
}
}
return null
},
2.資料格式
在這個專案上隨便做的一個表格並匯出資料
{
"list": [
{
"type": "input",
"options": {
"defaultValue": "",
"type": "text",
"prepend": "",
"append": "",
"placeholder": "請輸入",
"maxLength": 0,
"clearable": false,
"hidden": false,
"disabled": false
},
"label": "輸入框",
"labelWidth": -1,
"width": "100%",
"span": 24,
"model": "input_17156872001522",
"key": "input_17156872001522",
"rules": [
{
"required": false,
"message": "必填項",
"trigger": [
"blur"
]
}
],
"dynamicLabel": false
},
{
"type": "radio",
"options": {
"defaultValue": "",
"placeholder": "請輸入",
"dynamic": 0,
"options": [
{
"value": "1",
"label": "選項1"
},
{
"value": "2",
"label": "選項2"
}
],
"methodType": "get",
"dynamicPostData": "",
"remoteFunc": "",
"dataPath": "",
"remoteValue": "",
"remoteLabel": "",
"dictType": "",
"disableItemScript": "",
"hidden": false,
"disabled": false,
"linkage": false,
"linkData": []
},
"label": "單選框",
"labelWidth": -1,
"width": "100%",
"span": 24,
"model": "radio_17156872321432",
"key": "radio_17156872321432",
"rules": [
{
"required": false,
"message": "必填項",
"trigger": [
"blur"
]
}
],
"dynamicLabel": false
},
{
"type": "button",
"event_": false,
"listen_": false,
"options": {
"size": "mini",
"type": "primary",
"align": "left",
"control": "",
"eventName": "",
"script": "",
"plain": false,
"circle": false,
"round": false,
"disabled": false
},
"label": "按鈕",
"labelWidth": 0,
"width": "100%",
"span": 24,
"model": "button_17156901763582",
"key": "button_17156901763582",
"dynamicLabel": false
},
{
"type": "rate",
"options": {
"max": 5,
"defaultValue": 0,
"allowHalf": false,
"hidden": false,
"disabled": false
},
"label": "評分",
"labelWidth": -1,
"width": "100%",
"span": 24,
"model": "rate_17156901773022",
"key": "rate_17156901773022",
"rules": [
{
"required": false,
"message": "必填項",
"trigger": [
"blur"
]
}
],
"dynamicLabel": false
}
],
"config": {
"labelPosition": "top",
"labelWidth": 80,
"size": "mini",
"outputHidden": true,
"hideRequiredMark": false,
"syncLabelRequired": false,
"labelSuffix": "",
"customStyle": ""
}
}
type
:表示該元件的型別,該物件的型別為 "rate",用於評分。options
:表示該元件的選項,包括:label
:表示該元件的標籤文字,值為 "評分"。labelWidth
:表示該元件的標籤寬度,值為 -1,表示使用系統預設值。width
:表示該元件的寬度,值為 "100%"。span
:表示該元件所佔的柵格數,值為 24。model
:表示該元件的 v-model 繫結值的變數名,值為 "rate\_17156901773022"。key
:表示該元件的唯一標識,值為 "rate\_17156901773022"。rules
:表示該元件的校驗規則,包括:dynamicLabel
:表示該元件的標籤是否動態顯示,值為 false。
3.總結
在翻了許許多多的低開專案後,發現,
巨大多數的低開專案要麼引擎核心閉源,要麼物料元件庫閉源
而這個專案,所有的東西都開源了,真真正正的開源,真的牛bi
非常值得自學的一個低開專案