拖拽删除
背景
自营上传图片,但是需要排序和删除功能,所以用到了h5的拖拽
源元素: 即被拖拽的元素。
目标元素: 即合法的可释放元素。
每个事件的事件主体都是两者之一。
拖拽事件
触发顺序及次数
被拖拽元素,事件触发顺序是 dragstart->drag->dragend;对于目标元素,事件触发的顺序是 dragenter->dragover->drop/dropleave。
其中drag和dragover会分别在源元素和目标元素反复触发。整个流程一定是dragstart第一个触发,dragend最后一个触发。
这里还有一个注意的点,如果某个元素同时设置了dragover和drop的监听,那么必须阻止dragover的默认行为,否则drop将不会被触发。
- 站在源元素和目标元素的角度来看,就是dragstart 可以获取到当前的信息,源dragenter可以获取到目标元素,dragend,此时拖拽结束,可以做碰撞检测的逻辑
移动位置
api使用
这里我利用 dragstart 记录源数据,和索引。利用dragenter记录目标元素的数据,和索引,等拖拽结束,利用dragend做位置移动逻辑。
排序逻辑
- 利用splice, 先删除源数据,然后再目标元素之后,添加新元素。实现拖拽排序
比如将a 和b交换位置
- 获取a和b的索引
- 先将a删除
- 在b之后,再添加a
let oldData = 'a';
let newData = 'b';
let arr = ['d', 'a', 'c', 'b', 'e'];
let indexA = arr.indexOf(oldData); // 0
let indexB = arr.indexOf(newData); // 1
arr.splice(indexA, 1);
arr.splice(indexB, 0, oldData);
console.log('arr', arr); //[ 'd', 'c', 'b', 'a', 'e' ]
代码逻辑
<template><div class="mg-top-wrap"><!-- @dragover="dragover($event)" --><divclass="drag-sort-box"><divclass="drag-sort-item"v-for="(item, index) in images":key="item":draggable="true"@dragstart="dragstart(item, index)"@dragenter="dragenter(item, $event)"@dragend="dragend(item, $event)"@dragover="dragover($event)"@mouseover="mouseover(index)"@mouseout="mouseout()"><p:class="index === indexDel ? 'moxsind' : ''"@click="delPicHandler()"></p><img :src="item.image_url" /></div></div><wd-button class="" @click="chooseImage()" :loading="loading">上传图片<input@change="uploadImg($event)"type="file"style="display: none"id="upload"/></wd-button><div>记得去图文详情保存哦~</div></div>
</template><script setup>
import { onMounted, reactive, ref, watch } from 'vue';
import axios from '../../../utils/ajax';
import { WdMessage } from '@inagora/wd-view';
const props = defineProps({shopImage: Array,
});
const images = ref([]);const emit = defineEmits(['change']);watch(() => props.shopImage,(val) => {console.log('数据是否发生变化', val);if (val.length) {images.value = props.shopImage;}}
);const itemClass = ref('');
// images.value = images.value.map((v, i) => (v = v + '?index=' + i)); //不重复key
console.log('sssss', images.value);
let oldData = null;
let newData = null;const dragstart = (value, index) => {oldData = value;console.log('开始的值', images.value);
};
const dragenter = (value, e) => {newData = value;e.preventDefault();
};const dragover = (e) => {e.preventDefault();
};
const indexDel = ref('');
const mouseover = (index) => {indexDel.value = index;console.log('index', index);
};
const mouseout = () => {indexDel.value = '';
};
const delPicHandler = () => {images.value.splice(indexDel.value, 1);// emit('change', images.value);
};const dragend = () => {if (oldData !== newData) {let oldIndex = images.value.indexOf(oldData);let newIndex = images.value.indexOf(newData);let newItems = [...images.value];newItems.splice(oldIndex, 1);newItems.splice(newIndex, 0, oldData);images.value = [...newItems];console.log('结束的值', images.value);emit('change', images.value);}
};
const loading = ref(false);
const chooseImage = () => {document.getElementById('upload').click();
};
const uploadImg = (e) => {let file = e.target.files[0];let formdata = new FormData();formdata.append('file', file);uploadImage(formdata);
};
const uploadImage = (formdata) => {loading.value = true;axios.post('/Upload/uploadImage', formdata).then((res) => {if (!res.code) {loading.value = false;console.log('res.data', res.data);WdMessage({message: '上传成功',type: 'success',});images.value.push({ image: res.data.key, image_url: res.data.url });console.log('images.value', images.value);emit('change', images.value);// 刷新列表} else {loading.value = false;WdMessage({message: res.msg,type: 'error',});}});
};
</script><style lang="scss" scoped>
p {margin: 0;padding: 0;
}
.drag-sort-box {height: 330px;overflow: scroll;width: 100%;display: flex;flex-wrap: wrap;
}
.drag-sort-box .drag-sort-item {width: 200px;margin: 10px;cursor: pointer;transition: all 0.3s;// background: #ccc;position: relative;
}.drag-sort-box .drag-sort-item img {width: 100%;transition: all 0.3s;position: relative;
}
.drag-sort-box .drag-sort-item .active {position: absolute;top: 0;left: 0;align-items: center;justify-content: center;background: url(https://jira.inagora.org/secure/projectavatar?pid=11206&avatarId=10326)no-repeat center center;width: 30px;height: 30px;
}.moxsind {width: 100%;height: 100%;position: absolute;top: -10px;left: -10px;z-index: 10;background: url(https://s5.52ritao.cn/s/70/_620652.png) no-repeat;background-size: 18px 18px;width: 18px;height: 18px;
}
.mg-top-wrap {margin-top: 10px;
}
</style>