好家伙,
在写项目的时候,我发现自己的平台的组件写的实在是太难看了,于是想去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
非常值得自学的一个低开项目
标签:vue,false,form,labelWidth,items,组件,Star,解析,type From: https://www.cnblogs.com/FatTiger4399/p/18192155