vue拖拽实现app或小程序装修

一、最终效果图

参考引用作者:作者:李白不吃茶v
原作者源代码git地址:大神的源代码

这里是引用

二、需要安装的依赖

安装:vuedraggable
语法:npm install vuedraggable

三、先了解一下拖拽

  • 下面是HTML5的拖拽事件
    dragstart: 开始拖元素触发
    dragenter:元素拖进可drop元素(绑定drop事件的元素)时触发
    dragover:当元素拖动到drop元素上时触发
    drop:当元素放下到drop元素触发
    dragleave:当元素离开drop元素时触发
    drag:每次元素被拖动时会触发
    dragend:放开拖动元素时触发
    完成一次拖放的事件过程是:dragstart -> dragenter->dragover -> drop->dragend
    注意:想要在DOM元素上使用拖拽事件,需要在标签上添加draggable=“true”,否则是无效的。

四、实现左侧拖拽至中间视图区域

  • 效果


    组件可自己定义

draggable.vue的代码:

<template><!-- section标签定义文档中的节(section、区段),比如章节、页眉、页脚或文档中的其他部分 --><section class="decoration-edit"><!-- 页面的左侧内容 --><section class="l"><ul @dragstart="dragStart" @dragend="dragEnd"><li v-for="(val, key, index) in typeList" draggable :key="index + 1" :data-type="key"><span :class="val.icon"></span><p>{{ val.name }}</p></li></ul></section><!-- 页面的中间内容 --><section class="c"><!-- header(导航栏) 不可拖拽 --><div class="top-nav" @click="selectType(0)"><img src="../../assets/images/topNavBlack.png" alt="" /><span class="tit">{{ info.title }}</span></div><div class="view-content" @drop="drog" @dragover="dragOver" :style="{ backgroundColor: info.backgroundColor }"><Draggable v-model="view" draggable=".item"><template v-for="(item, index) in view"><div v-if="index > 0" :data-index="index" :key="index" class="item" @click="selectType(index)"><!-- waiting 可拖拽配置区域 --><template v-if="item.status && item.status === 2"><div class="wait">{{ item.type }}</div></template><template v-else><component :is="typeList[item.type]['com']" :data="item" :className="className[item.tabType]"></component></template><i @click="deleteItem($event, index)" class="el-icon-error"></i></div></template></Draggable></div></section><!-- 页面的右侧内容 --><section class="r"></section></section>
</template><script>
// 导入draggable组件
import Draggable from 'vuedraggable'
// 导入商品视图组件
import Product from '@/components/draggView/Product.vue'
// 导入图片视图组件
import Images from '@/components/draggView/Images.vue'
// 导入轮播图组件
import Banner from '@/components/draggView/Banner.vue'
export default {// 注册组件components: {Draggable,Product,Images,Banner},data() {return {// 定义左侧循环展示的内容以及拖拽后中间区域所显示的组件// 为什么是对象不是数组,应为循环的时候,对象的键值有用处typeList: {banner: {name: '轮播图',icon: 'el-icon-picture',com: Banner},product: {name: '商品',icon: 'el-icon-s-goods',com: Product},images: {name: '图片',icon: 'el-icon-picture',com: Images}},view: [{type: 'info',title: '页面标题'}], // 中间视图所存下的数组title: '页面标题', // 中间导航栏上的标题type: '', // 进行拖拽的类型index: null, // 当前组件的索引isPush: false, // 是否已添加组件props: {}, // 传值isRight: false,className: {1: 'one',2: 'two',3: 'three'}}},computed: {info() {return this.view[0]}},methods: {// 切换视图组件selectType(index) {this.isRight = falsethis.props = this.view[index]this.$nextTick(() => (this.isRight = true))},// 删除已经拖拽进入的组件deleteItem(e, index) {e.preventDefault()e.stopPropagation()this.view.splice(index, 1)this.isRight = falsethis.props = {}},// 拖拽类型dragStart(e) {console.log(e)console.log(e.target.dataset.type)this.type = e.target.dataset.typeconsole.log(this.type)console.log(this.index)},// 结束拖拽dragEnd(e) {console.log(e)console.log(this.index)this.$delete(this.view[this.index], 'status')this.isPush = falsethis.type = null},// 当元素放下到drop元素触发drog(e) {console.log(e)console.log(this.type)if (!this.type) {// 内容拖拽return}e.preventDefault()e.stopPropagation()this.dragEnd()},// 当元素拖动到drop元素上时触发dragOver(e) {console.log(e)if (!this.type) {// 内容拖拽return}e.preventDefault()e.stopPropagation()const className = e.target.classNameconst name = className !== 'view-content' ? 'item' : 'view-content'console.log(className)const defaultData = {type: this.type, // 组件类型status: 2, // 默认状态data: [], // 数据options: {} // 选项操作}if (name === 'view-content') {if (!this.isPush) {this.index = this.view.lengthconsole.log(this.view.length)this.isPush = truethis.view.push(defaultData)console.log(this.view)}} else if (name === 'item') {const target = e.targetvar [y, h, curIndex] = [e.offsetY, target.offsetHeight, target.dataset.index]const direction = y < h / 2if (!this.isPush) {// push to top or bottomif (direction) {if (curIndex === 0) {this.view.unshift(defaultData)} else {this.view.splice(curIndex, 0, defaultData)}} else {curIndex = +curIndex + 1this.view.splice(curIndex, 0, defaultData)}} else {// Movingif (direction) {var i = curIndex === 0 ? 0 : curIndex - 1var result = this.view[i].status === 2} else {i = +curIndex + 1result = this.view.length > i && this.view[i].status === 2}if (result) returnconst temp = this.view.splice(this.index, 1)this.view.splice(curIndex, 0, temp[0])}this.index = curIndexthis.isPush = true}}}
}
</script><style lang="scss" scoped>
// 页面
.decoration-edit {display: flex;align-items: center;justify-content: space-between;padding: 10px 0;background: #f7f8f9;height: calc(100vh - 200px);// 此处的height注意不能写成 height: calc(100vh-200px);注意中间的空格position: relative;
}
// 左侧拖拽区域
.l,
.r {width: 340px;height: 100%;padding: 15px 0;background: #fff;
}
.l {ul {margin: 0;padding: 0;li {width: 80px;height: 80px;display: flex;flex-direction: column;cursor: default;justify-content: center;align-items: center;list-style: none;font-size: 14px;color: #666;float: left;margin: 0 10px;border-radius: 6px;transition: all 0.3s;cursor: pointer;// & 表示在嵌套层次中回溯一层 此处:&:hover => li:hover&:hover {background: #efefef;}span {display: block;font-size: 40px;margin-bottom: 8px;color: #999;}p {display: block;margin: 0;font-size: 12px;}}}
}
.c {width: auto;// 子元素(包括content+padding+border+margin) 撑满这个父级元素的content区域// 子元素有 margin、border、padding时,会减去子元素content区域相对应的width值// 父元素的content = 子元素(content +padding + border + margin)max-width: 400px;position: relative;.top-nav {position: absolute;top: 0;background: #fff;z-index: 999;transition: all 0.3s;&* {// 表示top-nav下的所有元素pointer-events: none;// 表示能阻止点击、状态变化和鼠标指针变化}&:hover {transform: scale(0.95);// 默认为1 缩小为 0.95border-radius: 10px;overflow: hidden;box-shadow: 0 0 10px #afafaf;}.tit {position: absolute;left: 50%;bottom: 10px;transform: translateX(-50%);}img {max-width: 100%;image-rendering: -moz-crisp-edges;image-rendering: -o-crisp-edges;image-rendering: -webkit-optimize-contrast;image-rendering: crisp-edges;-ms-interpolation-mode: nearest-neighbor;}}.view-content {width: 400px;height: 700px;background: #f5f5f5;overflow-y: auto;overflow-x: hidden;padding-top: 72px;box-shadow: 0 2px 6px #ccc;&::-webkit-scrollbar {width: 6px;}&::-webkit-scrollbar-thumb {background: #dbdbdb;}&::-webkit-scrollbar-track {background: #f6f6f6;}.item {transition: all 0.3s;background: #fff;&:hover {transform: scale(0.95);border-radius: 10px;box-shadow: 0 0 10px #afafaf;.el-icon-error {display: block;}}div {pointer-events: none;}.wait {background: #deedff;height: 35px;text-align: center;line-height: 35px;font-size: 12px;color: #666;}.el-icon-error {position: absolute;right: -10px;top: -6px;color: red;font-size: 25px;cursor: pointer;display: none;z-index: 9999;}}}
}
</style>
总结:先定义三个组件,Product、Banner、Images,导入进来,根据拖拽的类型进行判断,动态渲染组件。

五、三中间视图组件(商品、图片、轮播,后续可以自己添加其他)

components目录下定义draggView文件,该目录底下存放中间区域组件。Banner.vue、Images.vue、Product.vue三个视图组件。
Product.vue

<template><div class="product" :class="className"><!-- 商品有数据情况下 --><template v-if="data.data && data.data.length > 0"><div class="product-item" v-for="(item, index) in data.data" :key="index"><div class="image"><img :src="item.productImg" alt="" /></div><div class="info"><p class="name">{{ item.productName }}</p><p class="num">{{ options.volumeStr ? item.volumeStr + '已购买' : '' }} {{ line }}{{ options.goodRatio ? item.goodRatio + '99%' : '' }}</p><p class="price"><span>¥{{ item.productPrice }}</span><span v-if="options.origonalPrice">¥{{ item.originalPrice }}</span></p></div></div></template><!-- 商品静态未选择商品情况下 --><template v-else><div class="product-item product-default" v-for="index in 3" :key="index"><div class="image"><img src="https://img.quanminyanxuan.com/other/21188f7a1e9340759c113aa569f96699.jpg?x-oss-process=image/resize,h_600,m_lfit" alt="" /></div><div class="info"><p class="name">这是商品名称</p><p class="num">12124 已经购买 | 99%</p><p class="price"><span>¥9.99</span><span>¥9.99</span></p></div></div></template></div>
</template><script>
export default {name: 'Product',props: ['data', 'className'],computed: {options() {return this.data.options},line() {return this.options.volumeStr && this.options.goodRatio ? ' | ' : ''}}
}
</script><style lang="scss" scoped>
.product {display: flex;flex-wrap: wrap;padding: 4px 8px;box-sizing: border-box;* {box-sizing: border-box;}&.one .product-item {width: 100%;padding: 10px;display: flex;border-bottom: 1px dashed #eee;.image {width: 100px;border-radius: 5px;overflow: hidden;margin-right: 10px;}.info {padding: 0 5px;display: flex;flex-direction: space-between;flex: 1;.price {font-size: 20px;margin: 0;}.num {margin: 12px 0 0;}}}&.three .product-item {width: 33.33%;.info .price {font-size: 16px;}&.product-default:nth-of-type(3) {display: block;}}.product-item {width: 50%;padding: 5px;&.product-default:nth-of-type(3) {display: none;}.image {font-size: 0;img {max-width: 100%;}}.info {padding: 10px 5px 0;.name {font-size: 14px;margin: 0;color: #333;text-overflow: ellipsis;word-break: break-all;display: -webkit-box;-webkit-line-clamp: 2;-webkit-box-orient: vertical;overflow: hidden;height: 38px;line-height: 18px;}.num {font-size: 12px;color: #d23000;font-weight: 600;}.price {font-weight: 600;margin: 12px 0 0;font-size: 18px;span:nth-of-type(1) {color: red;}span:nth-of-type(2) {color: #b5b5b5;font-weight: 400;font-size: 12px;margin-left: 4px;text-decoration: line-through;}}}}
}
</style>


Images和Banner的后续补充进来

六、右侧拖拽编辑表单内容

在components目录下新建draggEdit目录,该目录下新建index.vue、Product.vue、Images.vue和Info.vue目录。
index.vue 表示 右侧表单编辑父级组件。
Product.vue 表示商品编辑子组件。
Images.vue 表示图片和轮播的子组件。
Info.vue 便是页面头部编辑的子组件。
index.vue

<template><section><div class="tab-content"><h2>{{ type && list[type]['tit'] }}</h2><div class="tab" v-if="type != 'info'"><span v-for="(val, key, index) in tabType" :key="index" @click="tab(key)" :class="{ active: val }"><i class="el-icon-s-data">{{ key }}</i></span></div></div><component :is="type && list[type]['com']" :data="data" @changeTab="tab"> </component></section>
</template><script>
import Product from '@/components/draggEdit/Product.vue'
export default {name: 'EditForm',components: {Product},props: {data: {type: Object,default: () => {}}},data() {return {type: '',list: {info: {tit: '页面信息',com: 'Info'},images: {tit: '图片',com: 'Images'},banner: {tit: '轮播图',com: 'Images'},product: {tit: '商品',com: 'Product'}},tabType: {1: true,2: false,3: false}}},mounted() {console.log(this.data)console.log(this.data.type)this.type = this.data.typeif (this.data.tabType) {// this.tab(this.data.tabType)}},methods: {tab(key) {for (const i in this.tabType) {if (key === i) {this.tabType[key] = truethis.$set(this.data, 'tabType', key)} else {this.tabType[i] = false}}}}
}
</script><style lang="scss" scoped>
section {height: 100%;overflow: hidden;display: flex;flex-wrap: nowrap;flex-direction: column;
}
.tab-content {margin: 0 15px;h2 {font-size: 16px;color: #333;}.tab {display: flex;justify-content: space-around;border: 1px solid #ddd;border-radius: 6px;span {width: 33.33%;text-align: center;font-size: 14px;color: #666;display: block;height: 36px;line-height: 36px;cursor: pointer;&.active {color: #fff;background: #409eff;border-radius: 2px;}&:nth-of-type(2) {border-left: 1px solid #ddd;border-right: 1px solid #ddd;}}}
}
</style>

Product.vue

<template><div class="product-content"><p class="tit">商品列表<span>(可拖动排序)</span></p><el-button class="add-btn" type="primary" @click="toggleSearchPopup"><i class="el-icon-plus"></i>添加商品</el-button><template v-if="list.data && list.data.length > 0"><vuedraggable v-model="list.data" tag="ul" draggable="li" v-if="list.data && list.data.length > 0" class="list"><li class="item" v-for="(item, index) in list.data" :key="index"><img :src="item.productImg" alt="" /><i class="el-icon-error" @click="deleteItem(index)"></i></li></vuedraggable></template><div class="options"><el-form label-width="80px"><template v-for="(key, val, index) in options"><el-form-item :label="key" :key="index" v-if="loadingOption"><el-switch v-model="list['options'][val]" :name="val" @change="optionsChange(val, $event)"></el-switch></el-form-item></template></el-form></div><el-dialog title="添加商品" :visible.sync="show" @close="close"><el-form label-width="100px"><el-form-item label="选择商品"><el-select v-model="selectProduct" filterable remote reserve-keyword placeholder="请输入商品名称" :remote-method="searchProductList" @change="addProduct" :loading="loading"><el-option v-for="item in productList" :key="item.productId" :label="item.productName" :value-key="item.productName" :value="item"></el-option></el-select></el-form-item><el-form-item><el-button type="primary" @click="confirm">确定</el-button></el-form-item></el-form></el-dialog></div>
</template><script>
import vuedraggable from 'vuedraggable'
export default {name: 'Product',components: {vuedraggable},props: {data: {type: Object,default: () => {}}},data() {return {list: {},productList: [],loading: false,show: false,selectItem: null,selectProduct: '',options: {originalPrice: '划线价',goodRatio: '好评率',volumeStr: '销售量'},loadingOption: false}},mounted() {this.list = this.dataif (!this.data.tabType) {this.$emit('changeTab', 2)}// 默认开启所有选项for (const key in this.options) {if (this.data.options[key] === undefined) {this.$set(this.list.options, key, true)}this.loadingOption = true}},methods: {optionsChange(key, result) {this.$set(this.list.options, key, result)},deleteItem(index) {this.list.data.splice(index, 1)},// 搜索商品searchProductList(productName) {this.productList = productList},confirm() {this.list.data.push(this.selectItem)this.close()},toggleSearchPopup() {this.show = true},close() {this.show = falsethis.selectItem = nullthis.selectProduct = ''},addProduct(data) {this.selectItem = data}}
}
// 模拟产品列表
var productList = [{productId: 3601,productName: '驼大大新疆正宗骆驼奶粉初乳骆驼乳粉蓝罐礼盒装120g*4罐',productImg: 'https://img.quanminyanxuan.com/excel/f6860885547648d9996474bbf21fdca9.jpg',productPrice: 299,originalPrice: 598,volumeStr: '741',goodRatio: 98},{productId: 3268,productName: '百合28件套新骨质瓷餐具',productImg: 'https://img.quanminyanxuan.com/excel/185e7365f65543f2b4ebc67036d6a78f.jpg',productPrice: 370,originalPrice: 1388,volumeStr: '400',goodRatio: 99},{productId: 3343,productName: '和商臻品槐花蜜250克/瓶',productImg: 'https://img.quanminyanxuan.com/excel/4626c8c628d04935b0262d04991416b2.jpg',productPrice: 34.5,originalPrice: 72,volumeStr: '258',goodRatio: 98},{productId: 3330,productName: '鲍参翅肚浓羹350g袋装',productImg: 'https://img.quanminyanxuan.com/excel/58a0c968dc7d42c3ac21e09d1862aa6f.jpg',productPrice: 75,originalPrice: 128,volumeStr: '258',goodRatio: 98}
]
</script><style lang="scss" scoped>
.product-content {.tit {text-align: center;font-size: 12px;color: #666;margin: 18px 0;padding-bottom: 10px;border-bottom: 1px dashed #ddd;}.add-btn {width: calc(100% - 30px);height: 34px;line-height: 34px;padding: 0;font-size: 12px;margin-left: 15px;margin-top: 5px;}.list {display: flex;flex-wrap: wrap;padding: 12px;margin: 0;.item {width: 70px;height: 70px;border-radius: 6px;margin: 4px;position: relative;transition: all 0.3s;list-style: none;img {width: 100%;height: 100%;border-radius: 4px;}i {position: absolute;top: -6px;right: -6px;cursor: pointer;opacity: 0;transition: all 0.3s;color: red;}&::before {content: '';height: 100%;width: 100%;position: absolute;top: 0;right: 0;background: rgba(0, 0, 0, 0.4);border-radius: 4px;opacity: 0;transition: all 0.3s;}&:hover {cursor: grab;&::before,i {opacity: 1;}}}}.options {padding: 15px;border-radius: 6px;.el-form {background: #f7f8f9;overflow: hidden;padding: 10px 0;.el-form-item {margin: 0;label {font-size: 12px;}}}}
}
</style>

Info.vue

<template><div class="info-content"><el-form label-width="80px"><el-form-item label="页面标题"><el-input v-model="list.title"></el-input></el-form-item><el-form-item label="页面备注"><el-input type="textarea" :rows="4" v-model="list.remarks"></el-input></el-form-item><el-form-item label="页面背景"><el-color-picker v-model="list.backgroundColor" show-alpha></el-color-picker></el-form-item></el-form></div>
</template><script>
export default {name: 'Info',props: ['data', 'className'],data() {return {list: {}}},mounted() {this.list = this.data}
}
</script><style lang="scss" scoped></style>

至此,实现效果如下图:

vue拖拽实现app或小程序装修界面相关推荐

  1. vue拖拽组件(app移动端)

    vue拖拽组件 <template><div id="webId"><!-- 1.1 如果碰到滑动问题,请检查这里是否属于同一点. -->< ...

  2. 移动APP和小程序的低代码开发平台有哪些

    市场上的小程序低代码开发平台很多,包括:有赞云.uni-app.云程.知晓云.意派Coolsite360.jeecg-uniapp.unimall小程序.微盟云.微尘.牛刀云.应用公园.叮当.即速应用 ...

  3. vue 拖拽产生连线_dragUI

    dragUI 文档目录 用于UNI可拖拽可视化编程 在线演示 demo地址 一个简单创建hello world 界面的视频,github不会放视频,放在bilibli了 dragUI 演示视频 效果图 ...

  4. vue 拖拽元素到任意位置

    vue 拖拽元素到任意位置 使用vue-drag-it-dude组件 npm install vue-drag-it-dude --save 参考地址:https://github.com/xzqyu ...

  5. APP与小程序—信息收集

    APP与小程序-信息收集 1. 简介 1.1. APK介绍 1.2. APP渗透测试介绍 2. 搭建测试环境 2.1. Fiddler 2.1.1. 下载Fiddler 2.1.2. 安装完成页面 2 ...

  6. 现有小程序平台有哪些?如何让自己的App运行小程序?

    随着小程序越来越火热,越来越多的公司开始布局小程序,一些主流公司小程序平台应用已经非常成熟,但主流公司小程序只能在自己的生态内运行,并不输出其小程序技术能力至其他的产品.如果想要自己的App运行小程序 ...

  7. uni-app插入ucharts(echarts)图表,支持H5,APP,小程序

    uni-app插入ucharts(echarts)图表,支持H5,APP,小程序 这是uni-app里通用的图表方法,从ucharts官网上整理的 链接: https://pan.baidu.com/ ...

  8. 实现uniapp的app和小程序开发中能使用axios进行跨域网络请求,并支持携带cookie

    实现uniapp的app和小程序开发中能使用axios进行跨域网络请求,并支持携带cookie 1-使用npm install axios;命令安装axios 2-新建一个文件夹再建一个.js后缀文件 ...

  9. APP和小程序有什么区别?

    很多人都会困惑于APP和小程序之间的选择,首先我们需要了解两者的区别. APP和小程序有什么区别? APP和小程序安装使用的区别:APP需要通过应用市场下载.安装,通过桌面图标启动,小程序直接在微信中 ...

最新文章

  1. 如何访问固定的内存位置?
  2. Word中插入参考文献及其引用并能够自动更新的方法
  3. OpenCV开放神经网络交换ONNX混合的实例(附完整代码)
  4. (3)LoraWAN:链路控制、SF BW CR
  5. win2003配置apache2.2下,php页面出现乱码的解决方法
  6. ubuntu安装oracle数据库乱码问题解决方案(超级简单)
  7. GridControl详解(三)列数据的格式设置
  8. numpy 转存为matlab_Numpy学习打卡task01
  9. C++笔记——指针数组/数组指针
  10. 数字后端——可制造性设计
  11. 五轴编程_沙井万丰数控数控编程五轴编程那个软件好用
  12. 如何使用视频转换器将kux格式转换成mp4格式
  13. matlab半导体器件仿真,半导体软件 - 仿真模拟半导体器件的物理场
  14. 数据分析-淘宝用户行为分析
  15. php干货网,php高手干货【必看】
  16. win7桌面背景_解决WIN7桌面背景无法更改的问题
  17. ZOJ:1003 Crashing Balloon(DFS)
  18. 简单照片墙制作html5
  19. 基于Echarts实现可视化数据大屏大数据可视化
  20. 浙江大学计算机学院博士论文格式,浙江大学外语学院英文版博士学位论文格式.pdf...

热门文章

  1. Python入门教程(一)Python简介
  2. HD2500显卡驱动linux,英特尔HD 2500 4000集显最新驱动
  3. Windows序列号大全
  4. 7805稳压器保护电路分析
  5. [2022AAAI]Knowledge Distillation for Object Detection via Rank Mimicking and ... 论文笔记
  6. 汇川H3U标准程序,程序有本体脉冲控制的三轴定位,有总线控制的汇川伺服定位,轴点动,回零,相对定位绝对定位
  7. h5支付宝转账 免签
  8. 达索系统入手XFlow开发商 强化3DEXPERIENCE平台的仿真能力
  9. 网页游戏服务器代理一键端,3D网页游戏《霸刀》服务端最新一键端
  10. 破解非完美信息场景应用,微软公布专业十段麻将AI技术细节