赞
踩
空闲时间研究了下可视化拖拽,写了个简单的demo,完成了基本的拖拽,配置功能
<template>
<div class="page-drag">
<div class="page-left">
<ul>
<li
v-for="widget in widgetTools"
:key="widget.code"
draggable="true"
@dragstart="dragStart(widget.code)"
@dragend="dragEnd"
>
<div class="tools-item">
<span class="tools-item-text">{{ widget.label }}</span>
</div>
</li>
</ul>
</div>
<div class="page-mid">
<VueRulerTool ref="rulerTool" :content-layout="{left:0,top:0}"
:is-scale-revise="true"
:preset-line="presetLine"
>
<div id="workbench" @drop="widgetOnDragged($event)" @dragover="dragOver($event)" :style="{width: bigscreenWidth + 'px', height: bigscreenHeight + 'px'}">
<component draggable="true" @on-dragstart="dragStartItem(item.id)" v-for="(item, index) in widgets" :key="index" :index="index" @on-click="toggleActive(index)" :class="{active: currentIndex == index}" :is="item.type" :value="item.value" />
</div>
</VueRulerTool>
</div>
<div class="page-right">
<VDragSet :dragWidgetOpt="dragWidgetOpt"></VDragSet>
</div>
</div>
</template>
<script>
import VueRulerTool from 'vue-ruler-tool'
import widgetText from './component/text.vue'
import {widgetTool} from '../assets/widgetInfor/all-widget'
import VDragSet from "./dragSet.vue"
export default {
components: {VueRulerTool, widgetText, VDragSet},
data () {
return {
presetLine: [{ type: 'l', site: 10 }, { type: 'v', site: 10 }],
compType: 'widgetText',
widgetTools: widgetTool,
widgets: [],
bigscreenWidth: 1400, // 大屏设计的大小
bigscreenHeight: 1080,
dragWidgetCode: "",
dragWidgetOpt: {},
currentIndex: 0,
dragItemId: ""
}
},
methods: {
dragStartItem (id) {
this.dragItemId = id;
},
toggleActive (i) {
this.currentIndex = i;
this.getValuesByInstance();
},
dragEnd() {
this.dragWidgetCode = "";
this.dragItemId = "";
this.currentIndex = this.widgets.length - 1;
this.getValuesByInstance();
},
dragStart(widgetCode) {
this.currentIndex = -1
this.dragItemId = "";
this.dragWidgetCode = widgetCode;
this.dragWidgetOpt = this.widgetTools.find(v => v.code == widgetCode).options;
},
dragOver(evt) {
evt.preventDefault();
evt.stopPropagation();
evt.dataTransfer.dropEffect = "copy";
},
widgetOnDragged (evt) {
let eventX = evt.clientX;
let eventY = evt.clientY;
let workbenchPosition = this.getDomTopLeftById("workbench");
let widgetTopInWorkbench = eventY - workbenchPosition.top;
let widgetLeftInWorkbench = eventX - workbenchPosition.left;
if (this.dragItemId) {
let dragItemIndex = this.widgets.findIndex(v => v.id == this.dragItemId);
let dragItem = this.widgets[dragItemIndex]
dragItem.value.position.left = widgetLeftInWorkbench
dragItem.value.position.top = widgetTopInWorkbench
this.widgets.splice(dragItemIndex, 1, dragItem)
} else {
this.widgets.push({
type: this.dragWidgetCode,
id: new Date().getTime(),
value: this.getValuesByMode(this.dragWidgetOpt, widgetTopInWorkbench, widgetLeftInWorkbench),
options: []
})
}
},
getValuesByInstance () {
let currentItem = this.widgets[this.currentIndex].value;
var opts = JSON.parse(JSON.stringify(this.dragWidgetOpt))
Object.keys(opts).forEach(k => {
opts[k].forEach(v => {
if (currentItem[k] && currentItem[k][v.name]) {
v.value = currentItem[k][v.name]
}
})
})
this.dragWidgetOpt = opts;
},
getValuesByMode (opt, widgetTopInWorkbench, widgetLeftInWorkbench) { // 左侧模板获取value,设置拖拽的top,left
let value = {};
var opts = JSON.parse(JSON.stringify(opt))
Object.keys(opts).forEach(k => {
value[k] = {}
opts[k].forEach(v => {
if (k == "position") {
if (widgetTopInWorkbench && v.name == "top") {
v.value = widgetTopInWorkbench
}
if (widgetLeftInWorkbench && v.name == "left") {
v.value = widgetLeftInWorkbench
}
}
value[k][v.name] = v.value
})
})
return value;
},
getDomTopLeftById (id) {
let dom = document.getElementById(id)
let top = 0
let left = 0
if (dom != null) {
top = dom.getBoundingClientRect().top
left = dom.getBoundingClientRect().left
}
return { top: top, left: left }
},
setLine () {
let params=[
{ type: "l", site: 200 },
{ type: "v", site: 200 }
]
this.$refs.rulerTool.dragHorizontalLine(params)
}
},
watch: {
dragWidgetOpt: {
handler () {
if (this.widgets[this.currentIndex]) {
let value = this.getValuesByMode(this.dragWidgetOpt)
this.widgets[this.currentIndex].value = value
}
},
deep: true
}
}
}
</script>
<style lang="less" scoped>
.page-drag {
width: 100%;
height: 100%;
display: flex;
.page-left {
flex: 1;
ul {
margin: 0;
padding: 0;
}
li {
height: 100px;
width: 200px;
font-size: 50px;
text-align: center;
line-height: 100px;
margin: 10px;
background-color: #42b983;
color: #ffffff;
}
}
.page-mid {
width: 1400px;
overflow: hidden;
#workbench {
position: relative;
.active {
border: 1px solid #85CCFE;
}
}
}
.page-right {
width: 300px;
padding-left: 10px;
border-left: 1px solid #858585;
}
}
</style>
<template>
<div class="comp-text" @click="$emit('on-click', index)" @dragstart="$emit('on-dragstart', index)" :style="{
width: value.position.width + 'px',
height: value.position.height + 'px',
lineHeight: value.position.height + 'px',
top: value.position.top + 'px',
left: value.position.left + 'px',
fontSize: value.setup.fontSize + 'px',
letterSpacing: value.setup.letterSpacing,
background: value.setup.background,
fontWeight: value.setup.fontWeight,
textAlign: value.setup.textAlign,
color: value.setup.color,
}">{{value.setup.text}}</div>
</template>
<script>
export default {
props: {
value: Object,
index: Number
}
}
</script>
<style lang='less' scoped>
.comp-text {
position: absolute;
}
</style>
<template>
<div class="right-wrap">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane :label="labels[item]" :name="item" v-for="(item, index) in Object.keys(dragWidgetOpt)" :key="index">
<div class="row" v-for="(v, i) in dragWidgetOpt[activeName]">
<span class="label">{{v.label}}:</span>
<span class="value">
<el-input v-if="v.type == 'el-input-text'" v-model="v.value" :placeholder="v.label"></el-input>
<el-input-number v-if="v.type == 'el-input-number'" v-model="v.value" :placeholder="v.label"></el-input-number>
<el-select v-if="v.type == 'el-select'" v-model="v.value" :placeholder="v.label">
<el-option v-for="item in v.selectOptions" :key="item.value" :label="item.name" :value="item.code"></el-option>
</el-select>
<el-color-picker v-if="v.type == 'vue-color'" v-model="v.value"></el-color-picker>
<el-radio-group v-if="v.type == 'el-radio-group'" v-model="v.value">
<el-radio :label="item.code" v-for="item in v.selectOptions" :key="item.value">{{item.name}}</el-radio>
</el-radio-group>
<el-button v-if="v.type == 'el-button'">{{v.value}}</el-button>
</span>
</div>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
export default {
props: {
dragWidgetOpt: Object
},
data () {
return {
labels: {
setup: "配置",
data: "数据",
position: "坐标"
},
activeName: "setup"
}
},
}
</script>
<style lang="less" scoped>
.row {
height: 40px;
line-height: 40px;
display: flex;
margin-bottom: 10px;
.label {
width: 100px;
text-align: right;
font-size: 13px;
}
.value {
flex: 1;
text-align: left;
}
}
</style>
export const widgetText = {
code: 'widget-text',
type: 'html',
label: '文本',
icon: 'iconziyuan',
options: {
// 配置
setup: [
{
type: 'el-input-text',
label: '图层名称',
name: 'layerName',
required: false,
placeholder: '',
value: '文本框',
},
{
type: 'el-input-text',
label: '文本内容',
name: 'text',
required: false,
placeholder: '',
value: '文本框',
},
{
type: 'el-input-number',
label: '字体大小',
name: 'fontSize',
required: false,
placeholder: '',
value: '26',
},
{
type: 'vue-color',
label: '字体颜色',
name: 'color',
required: false,
placeholder: '',
value: '#FAD400',
},
{
type: 'el-input-number',
label: '字体间距',
name: 'letterSpacing',
required: false,
placeholder: '',
value: '0',
},
{
type: 'vue-color',
label: '字体背景',
name: 'background',
required: false,
placeholder: '',
value: 'rgba(115,170,229,1)',
},
{
type: 'el-select',
label: '文字粗细',
name: 'fontWeight',
required: false,
placeholder: '',
selectOptions: [
{code: 'normal', name: '正常'},
{code: 'bold', name: '粗体'},
{code: 'bolder', name: '特粗体'},
{code: 'lighter', name: '细体'}
],
value: 'normal'
},
{
type: 'el-select',
label: '对齐方式',
name: 'textAlign',
required: false,
placeholder: '',
selectOptions: [
{code: 'center', name: '居中'},
{code: 'left', name: '左对齐'},
{code: 'right', name: '右对齐'},
],
value: 'center'
},
],
// 数据
data: [
{
type: 'el-radio-group',
label: '数据类型',
name: 'dataType',
require: false,
placeholder: '',
selectValue: true,
selectOptions: [
{
code: 'staticData',
name: '静态数据',
},
{
code: 'dynamicData',
name: '动态数据',
},
],
value: 'staticData',
},
{
type: 'el-input-number',
label: '刷新时间(毫秒)',
name: 'refreshTime',
relactiveDom: 'dataType',
relactiveDomValue: 'dynamicData',
value: 5000
},
{
type: 'el-button',
label: '静态数据',
name: 'staticData',
required: false,
placeholder: '',
relactiveDom: 'dataType',
relactiveDomValue: 'staticData',
value: '文本框',
},
],
// 坐标
position: [
{
type: 'el-input-number',
label: '左边距',
name: 'left',
required: false,
placeholder: '',
value: 0,
},
{
type: 'el-input-number',
label: '上边距',
name: 'top',
required: false,
placeholder: '',
value: 0,
},
{
type: 'el-input-number',
label: '宽度',
name: 'width',
required: false,
placeholder: '该容器在1920px大屏中的宽度',
value: 100,
},
{
type: 'el-input-number',
label: '高度',
name: 'height',
required: false,
placeholder: '该容器在1080px大屏中的高度',
value: 40
},
],
}
}
import {widgetText} from "./widget-text"
export const widgetTool = [widgetText]
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。