You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

330 lines
8.8 KiB
Vue

1 year ago
<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] 00图标模式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() {
this.grid = this.list.length;
if(this.grid==0) return;
this.$nextTick(async function() {
let p = await this.$Querey(".tm-dragGrid", this).catch(e => {})
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>