效果预览

git地址:https://gitee.com/javanx/draglayout

效果解析

1、flex布局成3栏

(1)、左侧定义好固定组件

(2)、中间拖放后的预览效果

(3)、右侧是,点击中间的组件后,可以编辑对应属性

2、利用html5的draggable属性,完成拖动效果

3、vue完成拖放后数据的驱动

4、表单双向绑定,改变组件属性

5、最终数据结构

初始化项目

vue create hello-world

1、安装element-ui

npm i element-ui --save

2、main.js引入

import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI);

3、flex布局
删除掉默认的些许代码

<div class="home"> <div class="flex"> <div class="components-group">组件区</div> <div class="preview">预览区</div> <div class="component-attr">属性编辑区</div> </div> </div>
/** 布局样式 */ .home{ height: 100vh; padding: 20px 0; } .flex{ display: flex; justify-content: space-between; } .components-group{ width: 300px; height: 800px; background: #fff; border: 1px solid red; } .preview{ width: 320px; background: #fff; box-shadow: 0px 2px 20px 0px rgb(208 215 222 / 46%); border-radius: 12px; border: 1px solid #edf0f5; padding: 10px; } .component-attr{ width: 300px; height: 800px; background: #fff; border: 1px solid red; }

这就出来了我们整个的页面结构框架
效果预览

默认组件

咱们是数据驱动的,所有先定义好数据吧。定义好默认组件,并在左侧渲染出来。

componentsGroup: [{ type: 1, label: '文本', components: [{ id: 1, widgetKey: 'widget_text_single', label: '单行文本', type: '单行文本', required: true, hint: '请输入', value: '' }] ... }, { type: 10, label: '数值', components: [{ id: 11, widgetKey: 'widget_number', label: '数字', type: '数字', unit: '', // 单位 required: true, hint: '请输入', value: '' }] ... }]
<div class="components-group"> <div class="components-group-item" v-for="(item, key) in componentsGroup" :key="key"> <div class="type">{{item.label}}</div> <div> <div class="components-item" v-for="component in item.components" :key="component.id" :data-type="item.type" :data-id="component.id" draggable="true">{{component.label}}</div> </div> </div> </div>

样式部分大家自行到git上面看,这里就不一一列出

拖拽

mounted(){ this.initDrag() }, methods: { initDrag(){ let vm = this let offsetY = 0 var eleDustbin = $(".preview")[0], eleDrags = $(".components-item"), lDrags = eleDrags.length, eleRemind = $(".dragremind")[0], eleDrag = null; for (var i=0; i<lDrags; i+=1) { eleDrags[i].onselectstart = function() { return false; }; eleDrags[i].ondragstart = function(ev) { ev.dataTransfer.effectAllowed = "move"; ev.dataTransfer.setData("text", ev.target.innerHTML); ev.dataTransfer.setDragImage(ev.target, 0, 0); eleDrag = ev.target; return true; }; eleDrags[i].ondragend = function(ev) { ev.dataTransfer.clearData("text"); eleDrag = null; return false }; } eleDustbin.ondragover = function(ev) { ev.preventDefault(); return true; }; eleDustbin.ondragenter = function(ev) { offsetY = ev.offsetY; $('.droptarget').css({ border: 'none' }); if (ev.target.className.indexOf('droptarget')>-1) { ev.target.style.border = "1px solid red"; } return true; }; eleDustbin.ondrop = function(ev) { $('.droptarget').css({ border: 'none' }); let id = $(eleDrag).data('id'); let type = $(eleDrag).data('type'); if(!id) return false let component = '' if(vm.tabbarActive==1){ vm.componentsGroup.find(item=>{ if(item.type==type){ let components = item.components component = components.find(citem=>citem.id==id) } }) } else { component = vm.componentsGroupGroup.find(item=>item.type==type) } component.widgetNo = moment().valueOf() let components = vm.formData.components var dragitem = ev.target.closest('.form-components-item'); // console.log(dragitem) if(dragitem){ let index = $(dragitem).data('index') let ditem = $(dragitem).data('item') if(ditem.type=='明细'){ if(component.type=='明细'){ Message({ type: 'warning', message: '明细组件中不可以再添加明细组件' }) return } if(!ditem.components){ ditem.components = [] } // component.pno = ditem.widgetNo if(ev.offsetY+5>offsetY){ ditem.components.splice(index, 0, component) }else{ ditem.components.splice(index+1, 0, component) } } else { if(ev.offsetY+5>offsetY){ components.splice(index, 0, component) }else{ components.splice(index+1, 0, component) } } } else { components.push(component) } // components.push(component) components = JSON.parse(JSON.stringify(components)) console.log('components', components) Vue.set(vm.formData, 'components', components) setTimeout(()=>{ vm.initSort() }, 500) return false; }; } }

这里用到了jquery,感觉还是jquery香啊。

有了数据就简单了,变量数据,根据widgetKey,判断不同的组件渲染不同的element-ui组件即可。详情见git代码。

上方代码还有一个明细控件组,意思是,明细里面可以包含子控件。