<template> <view class="tm-dragGrid relative fulled" :style="{height:totalH+'px'}"> <view class="absolute " :class="[ani&&!disabled?'aniOn':'',nowMove_index==index||endDrage?'':'tranAni']" :style="{ width:itemWidth+'px', height:h+'px', left:item.left+'px', top:item.top+'px', zIndex:nowMove_index==index?5:0 }" v-for="(item,index) in listData" :key="index" :id="'tm-dragGrid-' + index" @touchstart="m_start($event,index)" @mousedown="m_start($event,index)" @touchmove.stop.prevent="m_move($event,index)" @mousemove.stop.prevent="m_move($event,index)" @touchend="m_end($event,index)" @mouseup="m_end($event,index)" @longpress="startEdit" > <slot name="default" :item="item.data"> <view v-if="model==0" class="flex-center flex-col" style="width:70%"> <view style="width:100%"><tm-badges @click.stop="delitem(item)" v-if="!disabled" :offset="[0,-5]" icon="icon-times"></tm-badges></view> <tm-icons :size="45" :name="item.data.icon" :color="item.data.color"></tm-icons> <text class="text-size-s pt-10">{{item.data.text}}</text> </view> <view v-if="model==1" style="width:100%;height:100%"> <tm-badges @click.stop="delitem(item)" v-if="!disabled" :offset="[-5,-5]" icon="icon-times"></tm-badges> <view :class="['bg-gradient-'+item.data.color+'-accent']" class=" flex-center round-4 text-size-s" style="width: 90%;height:100rpx"> {{item.data.text}} </view> </view> </slot> </view> </view> </template> <script> /** * 宫格拖动排序 * @property {Number} col = [] 4,一行排列几个 * @property {Number} width = [] 0,组件宽度,可以不设置默认取父组件宽度 * @property {Number} itemHeight = [] 120,项目的高度 * @property {Boolean} ani = [] true,是否开启拖动显示内容抖动动画 * @property {Boolean} disabled = [] false,是否开启拖动 * @property {Boolean} longTap = [] true,是否允许长按启动编辑模式。 * @property {Number} model = [0|1] 0,0图标模式,1方块样式。 * @property {Array} list = [] [],排序的列表,只要是数组就可,至于内容是什么格式无所谓。 * @property {Function} change 拖放排序时触发,返回更改后的列表数据。 * @property {Function} remove 删除一个项目时触发。 */ import tmIcons from '@/tm-vuetify/components/tm-icons/tm-icons.vue'; import tmBadges from '@/tm-vuetify/components/tm-badges/tm-badges.vue'; export default { name:"tm-dragGrid", components:{tmBadges,tmIcons}, props:{ //几列,一行排几个。 col:{ type:Number, default:4 }, //组件宽度,可以不设置默认取父组件宽度 width:{ type:Number, default:0 }, //项目的高度 itemHeight:{ type:Number, default:120 }, //是否开启拖动显示内容抖动动画 ani:{ type:Boolean, default:true }, disabled: { type: String | Boolean, default: false }, list:{ type:Array, default:()=>{} }, //是否开户长按开始编辑。 longTap:{ type:Boolean, default:true }, model:{ type:Number, default:0, } }, data() { return { w:0, h:0, row:0, totalH:0, listData:[], itemWidth:0, endDrage: false, Drage__id: '', //正在被拖动的id; nowMove_index: null, //现在正在移动的索引 x:0, y:0, prarentTop:0, prarentLeft:0, grid:0, isDrage:false, }; }, computed:{ }, created() { this.grid = this.list.length; }, mounted() { this.inits(); }, watch:{ list:{ deep:true, handler(){ this.inits(); } } }, methods: { startEdit(){ if(this.longTap==false) return; this.$emit("update:disabled",false) }, delitem(item){ this.list.splice(item.index,1) this.$emit('remove',item) this.$emit('change', this.listData); }, m_start(event,index){ event.preventDefault() event.stopPropagation() if (this.disabled) return; this.nowMove_index = index; this.endDrage = false; this.isDrage = true; if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) { var touch = event.changedTouches[0]; this.y = touch.pageY - event.currentTarget.offsetTop - this.prarentTop this.x = touch.pageX - event.currentTarget.offsetLeft - this.prarentLeft } else { this.y = event.pageY - event.currentTarget.offsetTop - this.prarentTop this.x = event.pageX - event.currentTarget.offsetLeft - this.prarentLeft } // #ifdef MP uni.vibrateShort({}) // #endif }, m_move(event,index){ let t = this if (this.disabled) return; event.preventDefault() event.stopPropagation() if(!this.isDrage) return; if (t.endDrage==true) return; //当前元素的top位置。 let chy = 0; let chx = 0; if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) { var touch = event.changedTouches[0]; chy = touch.pageY - t.y - this.prarentTop chx = touch.pageX - t.x - this.prarentLeft } else { chy = event.pageY - t.y - this.prarentTop chx = event.pageX - t.x - this.prarentLeft } t.listData.splice(index, 1, { ...t.listData[index], top: chy, left: chx, }) t.nowMove_index = index; const currenit_index = index; const currentSort = t.listData[currenit_index].sort; const currenit_id = t.listData[currenit_index].__id; // 计算当前移动的index. let moveIndex = Math.round(chx / t.itemWidth) + Math.round(chy / t.h)*t.col; moveIndex = moveIndex < 0 ? 0 : moveIndex; moveIndex = moveIndex > t.listData.length - 1 ? t.listData.length - 1 : moveIndex; moveIndex = Math.abs(moveIndex) index = moveIndex; let elList = [...t.listData] for (let i = 0; i < elList.length; i++) { if (currentSort < moveIndex) { if (elList[i].sort > currentSort && elList[i].sort <= moveIndex) { elList[i].sort -= 1; }; } else if (currentSort > moveIndex) { if (elList[i].sort < currentSort && elList[i].sort >= moveIndex) { elList[i].sort += 1; }; } }; elList[currenit_index].sort = moveIndex; elList = elList.map(el => { if (el.__id != currenit_id) { el.left = el.sort % t.col * t.itemWidth; el.top = parseInt(el.sort / t.col) * t.h; } return el; }) t.listData = elList; }, m_end(event,index){ if (this.disabled) return; let t = this; event.preventDefault() event.stopPropagation() this.isDrage = false; this.endDrage = true; if (this.nowMove_index == null) return; let elList = [...t.listData] elList = this.setTL(elList); elList.sort((a,b)=>a.sort-b.sort) t.listData = [...elList] this.nowMove_index = null; this.moveChange(); }, moveChange(e, index) { if (this.disabled) return; //change后修改的数据 。 const elList = [...this.listData] elList.sort((a,b)=>a.sort-b.sort-b) this.$emit('change',elList); }, inits() { if(this.grid==0) return; this.$nextTick(async function() { let p = await this.$Querey(".tm-dragGrid", this).catch(e => {}) this.grid = this.list.length; this.listData = []; //组件的宽度 this.w = uni.upx2px(this.width) || p[0].width || 300; this.prarentTop = p[0].top; this.prarentLeft = p[0].left; //项目的高度。 this.h = uni.upx2px(this.itemHeight) //项目的宽度 this.itemWidth = this.w / this.col //行数。 this.row = Math.ceil(this.grid / this.col); //总高度 this.totalH = this.h * this.row //构造一个list宫格列表出来。 for(let i=0;i<this.grid;i++){ this.listData.push({ "data":this.list[i], "__id":uni.$tm.guid(), "top":0, "left":0, "sort":i, "index":i }) } let list = this.setTL([...this.listData]); this.listData = [...list]; }) }, //计算位置。 setTL(el){ for(let i = 0;i < el.length;i++){ el[i].left = el[i].sort % this.col * this.itemWidth; el[i].top = parseInt(el[i].sort / this.col) * this.h; } return el; } }, } </script> <style lang="scss" scoped> .aniOn{ animation:doudong 0.5s linear infinite; background:linear-gradient(); background: -webkit-linear-gradient(); } .tranAni{ transition:all 0.15s } @keyframes doudong { 0%{ transform: rotate(-2deg) translateX(2rpx) translateY(2rpx); } 25%{ transform: rotate(0deg) translateX(-2rpx) translateY(-2rpx); } 50%{ transform: rotate(0deg) translateX(0rpx) translateY(-2rpx); } 75%{ transform: rotate(0deg) translateX(0rpx) translateY(2rpx); } 100%{ transform: rotate(-2deg) translateX(2rpx) translateY(2rpx); } } </style>