1、图示 可拖动排序
2、主代码块1
<van-uploader
multiple
v-model="fileList"
:after-read="afterRead"
:preview-image="false"
accept="audio/*"
upload-icon="plus"
upload-text="选择音频"
>
<van-button icon="plus" type="primary" size="small">
选择音频
</van-button>
3、主代码块2
<van-cell-group inset>
<van-cell title="上传专辑列表(拖动排序)">
<template #right-icon>
<van-button
plain
size="small"
type="primary"
hairline
@click="sortByName"
>
按名称排序
</van-button>
<van-button
plain
size="small"
type="primary"
hairline
@click="sortBySize"
>
按大小排序
</van-button>
</template>
</van-cell>
<draggable
:list="fileList"
ghost-class="ghost"
:force-fallback="true"
:group="{ name: 'list', pull: 'clone' }"
item-key="id"
>
<template #item="{ element, index }">
<div class="item move">
<label class="move">
<van-cell>
<template #title>
<span class="custom-title">
{{ index + sort + 1 }}
<van-icon name="music-o" style="padding: 2px" />
<span>{{ element.file.name }}</span>
</span>
</template>
<template #right-icon>
<div
style="
display: flex;
justify-content: center;
align-items: center;
padding: 3px;
"
>
<van-icon
v-show="element.file.showDelete"
name="delete"
color="red"
@click="deleteFile(element.file, index)"
/>
<van-loading size="14" v-show="element.showLoading" />
<van-icon
name="success"
style="color: #07c160"
v-show="element.showSuccess"
/>
<van-icon
name="cross"
style="color: red"
v-show="element.showError"
/>
</div>
</template>
</van-cell>
</label>
</div>
</template>
</draggable>
<van-cell>
<template #title>
<van-button
type="success"
v-show="showOk"
size="small"
block
plain
round
link
:to="{
path: 'episodes',
query: { albumId: albumId },
}"
>完成(点击查看专辑列表)</van-button
>
<van-button
plain
style="float: right"
v-if="showUpload"
@click="clickPromise"
type="success"
size="small"
block
round
>
<van-icon name="back-top" />点击上传
<span style="color: red; font-size: 10px">
(共{{ fileList.length }}个文件)
</span>
</van-button>
</template>
</van-cell>
</van-cell-group>
4、完整代码 html部分
<template>
<div>
<div>
<div style="margin-top: 10px; margin-bottom: 10px">
<van-cell-group inset>
<van-cell>
<template #title>
<van-uploader
multiple
v-model="fileList"
:after-read="afterRead"
:preview-image="false"
accept="audio/*"
upload-icon="plus"
upload-text="选择音频"
>
<van-button icon="plus" type="primary" size="small">
选择音频
</van-button>
</van-uploader>
<!-- 自定义开始集数: -->
<!-- <van-stepper v-model="sort" theme="round" button-size="22" /> -->
</template>
<template #right-icon> </template>
</van-cell>
</van-cell-group>
</div>
<div>
<van-cell-group inset>
<van-cell title="上传专辑列表(拖动排序)">
<template #right-icon>
<van-button
plain
size="small"
type="primary"
hairline
@click="sortByName"
>
按名称排序
</van-button>
<van-button
plain
size="small"
type="primary"
hairline
@click="sortBySize"
>
按大小排序
</van-button>
</template>
</van-cell>
<draggable
:list="fileList"
ghost-class="ghost"
:force-fallback="true"
:group="{ name: 'list', pull: 'clone' }"
item-key="id"
>
<template #item="{ element, index }">
<div class="item move">
<label class="move">
<van-cell>
<template #title>
<span class="custom-title">
{{ index + sort + 1 }}
<van-icon name="music-o" style="padding: 2px" />
<span>{{ element.file.name }}</span>
</span>
</template>
<template #right-icon>
<div
style="
display: flex;
justify-content: center;
align-items: center;
padding: 3px;
"
>
<van-icon
v-show="element.file.showDelete"
name="delete"
color="red"
@click="deleteFile(element.file, index)"
/>
<van-loading size="14" v-show="element.showLoading" />
<van-icon
name="success"
style="color: #07c160"
v-show="element.showSuccess"
/>
<van-icon
name="cross"
style="color: red"
v-show="element.showError"
/>
</div>
</template>
</van-cell>
</label>
</div>
</template>
</draggable>
<van-cell>
<template #title>
<van-button
type="success"
v-show="showOk"
size="small"
block
plain
round
link
:to="{
path: 'episodes',
query: { albumId: albumId },
}"
>完成(点击查看专辑列表)</van-button
>
<van-button
plain
style="float: right"
v-if="showUpload"
@click="clickPromise"
type="success"
size="small"
block
round
>
<van-icon name="back-top" />点击上传
<span style="color: red; font-size: 10px">
(共{{ fileList.length }}个文件)
</span>
</van-button>
</template>
</van-cell>
</van-cell-group>
</div>
</div>
<div style="margin: 20px">
<span style="font-size: 12px; color: grey">
*注:<br />1、上传连续音频<br />2、点击选择音频<br />3、可以拖拽排序或自行选择排序方式<br />4、点击上传音频
</span>
</div>
</div>
<van-overlay :show="showOverlay">
<div class="wrapper" @click.stop>
<div class="block" />
</div>
<van-loading
vertical
style="
display: flex;
justify-content: center;
height: 100vh;
width: 100wh;
"
>
<template #icon>
<van-icon name="star-o" size="30" />
</template>
正在上传,请稍等...
</van-loading>
</van-overlay>
</template>
5、完整代码 js部分
<script setup>
import Draggable from "vuedraggable";
import { ref } from "vue";
import { uploadAlbum } from "../../api/index";
import "vant/lib/uploader/style";
import { useRoute } from "vue-router";
import router from "../../router";
components: {
Draggable;
}
const route = useRoute();
const onClickLeft = () => history.back();
const toManageHome = () =>
router.push({
path: "/manage",
});
const fileList = ref([]);
const albumId = route.query.albumId;
const albumName = route.query.albumName;
const sort = parseInt(route.query.sort);
const showOverlay = ref(false);
const showUpload = ref(false);
const showOk = ref(false);
function handleFileChange(e) {
fileList.value = [...fileList.value, ...e.target.fileList];
e.target.value = ""; // 清空input的值,以便可以重复选择文件
}
// 根据文件名排序
function sortByName() {
fileList.value.sort((a, b) => a.file.name.localeCompare(b.file.name));
}
// 根据大小排序
function sortBySize() {
fileList.value.sort((a, b) => a.file.size - b.file.size);
}
// 删除
const deleteFile = (file, index) => {
fileList.value.splice(index, 1);
};
// 选完文件加几个按钮
const afterRead = (file) => {
showUpload.value = true;
// 如果是多文件,file会是数组
const files = Array.isArray(file) ? file : [file];
console.log("文件:", files);
// 将文件添加到FormData
files.forEach((item, index) => {
item.file.showLoading = false;
item.file.showSuccess = false;
item.file.showError = false;
item.file.showDelete = true;
console.log("item.file", item.file);
// formData.append("sorts", index + sort + 1);
});
};
const tasks = ref([]);
// 异步任务
const clickPromise = async () => {
console.log("进入", fileList.value);
//tasks
fileList.value.forEach((ele, index) => {
tasks.value.push(
() =>
new Promise(async (resolve) => {
fileList.value[index].showLoading = true;
fileList.value[index].file.showDelete = false;
let formDataPro = new FormData();
formDataPro.append("file[]", ele.file);
formDataPro.append("albumId", albumId);
console.log("执行");
try {
const response = await uploadAlbum(formDataPro);
console.log("返回值", response);
if (response.data.code == 0) {
showSuccessToast({message:response.data.data,className: 'particulars-detail-popup'});
// showOverlay.value = false;
fileList.value[index].showLoading = false;
fileList.value[index].showSuccess = true;
} else {
showFailToast({message:response.data.msg,className: 'particulars-detail-popup'});
fileList.value[index].showLoading = false;
fileList.value[index].showError = true;
// showOverlay.value = false;
}
} catch (e) {
showFailToast({message:response.data.msg,className: 'particulars-detail-popup'});
fileList.value[index].showLoading = false;
fileList.value[index].showError = true;
}
resolve();
})
);
});
executeSequentialTasks();
};
// 异步执行
const executeSequentialTasks = async () => {
showUpload.value = false;
for (const task of tasks.value) {
await task();
}
showOk.value = true;
};
</script>
参考文献:vant4官方文档 axios官方文档