这是公司的一个业务接口返回的数据格式
这是一个用户反馈信息的列表,这里用到的主要数据是files_img这个数组,这里面放了反馈的图片数据列表,还需要根据这个图片id去请求另一个接口(暂称为B)拿到返回的base64图片数据.
至于为什么不是一个正常的url地址,种种原因吧,如果是正常的url地址,也就不会有这篇文章了
如果正常按逻辑去把数据放入el-table你会发现在表格中的所有图片数据没有拿到B接口的返回结果之前,什么都不显示,白屏.也就是请求B接口阻塞了表格中其他数据的渲染.这显然不行.
解决方案
1.先让图片的加载是异步的,不阻塞表格中其他数据的显示
2.控制请求B接口的并发数量(本例是3个)
3.在请求B接口的过程中加上loading,优化体验
<el-table-column label="图片" min-width="150">
<template #default="scope">
<div v-if="imageLoadingMap[scope.row.uuid]" class="image-loading">
<el-icon class="is-loading">
<Loading />
</el-icon>
</div>
<div v-else-if="imageMap[scope.row.uuid]" style="display: flex; flex-wrap: wrap;">
<el-image v-for="(img, index) in imageMap[scope.row.uuid]" :key="index" :preview-teleported="true"
style="width: 30px; height: 30px; margin-right: 5px;" :src="img.url"
:preview-src-list="imageMap[scope.row.uuid].map(i => i.url)" :fit="'cover'" />
</div>
<span v-else>-- --</span>
</template>
</el-table-column>
// B接口的请求结果,key是这一行的uuid,value是一个对象,记录这对应请求会来的base64数据
const imageMap = ref<Record<string, { url: string }[]>>({});
// 用来控制请求B接口过程中的loading显示
const imageLoadingMap = ref<Record<string, boolean>>({});
onMounted(() => {
getFbList();
});
const getFbList = async () => {
const res = (await getFeedbackApi(state)) as any; // getFeedbackApi拿到的结果如文章开始展示的图片
if (res) {
feedbackList.value = res.data;
total.value = res.total;
res.data.forEach((item: any) => {
useFormatImage(item);
});
}
};
// 修改图片处理函数
const useFormatImage = async (row: any) => {
if (!row.files_img || !row.files_img.length) return;
imageLoadingMap.value[row.uuid] = true; // 先加载loading
try {
// 通过map遍历files_img将对B接口的请求存在一个数组里
const tasks = row.files_img.map((img: string) => async () => {
const res = await getBase64File(img) as string; //getBase64File 接口 B
return { url: res };
});
const fileList = await concurrentRequest(tasks, 3); // 控制并发数
imageMap.value[row.uuid] = fileList; // 将base64数据存在对应的行中
} catch (error) {
console.error('Error loading images:', error);
} finally {
imageLoadingMap.value[row.uuid] = false; // 关闭loading
}
};
// 并发控制函数
const concurrentRequest = async (tasks: any[], maxConcurrent = 3) => {
const results = [];
const executing = new Set();
for (const task of tasks) {
const promise = Promise.resolve().then(() => task());
results.push(promise);
executing.add(promise);
const clean = () => executing.delete(promise);
promise.then(clean).catch(clean);
if (executing.size >= maxConcurrent) {
await Promise.race(executing);
}
}
return Promise.all(results);
};
注意到:在useFormatImage函数中我使用的是map,其实一开始使用的是forEach,但后来查资料才知道forEach是不支持异步循环的