fix: 修复文件上传逻辑和UI问题
- 修复文件上传暂停/恢复逻辑错误,调整播放状态与上传动作的对应关系 - 为视频上传添加半透明蒙层提升用户体验 - 移除上传管理中的冗余字段和注释代码 - 调整确认框标题的padding样式 - 添加消息重发确认功能
This commit is contained in:
parent
5bda2be585
commit
e2e0a3ea3a
@ -29,7 +29,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
<XNModal v-model:show="show" :closable="false" class="w-724px" content-style="padding:0px" @after-leave="emit('after-leave')">
|
<XNModal v-model:show="show" :closable="false" class="w-724px" content-style="padding:0px" @after-leave="emit('after-leave')">
|
||||||
<div class="flex flex-col w-full px-25px pb-49px">
|
<div class="flex flex-col w-full px-25px pb-49px">
|
||||||
<div class="text-20px text-#1F2225 w-full text-center border-b-1px border-b-solid border-b-#E9E9E9 py-25px">{{ title }}</div>
|
<div class="text-20px text-#1F2225 w-full text-center border-b-1px border-b-solid border-b-#E9E9E9 py-20px">{{ title }}</div>
|
||||||
<div class="py-60px text-center text-20px text-#1F2225">
|
<div class="py-60px text-center text-20px text-#1F2225">
|
||||||
{{ content }}
|
{{ content }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -71,9 +71,8 @@ function getFileExtension(filename) {
|
|||||||
// 切换播放状态
|
// 切换播放状态
|
||||||
const togglePlay = () => {
|
const togglePlay = () => {
|
||||||
isPlaying.value = !isPlaying.value
|
isPlaying.value = !isPlaying.value
|
||||||
|
|
||||||
if (props.extra.is_uploading && props.extra.upload_id) {
|
if (props.extra.is_uploading && props.extra.upload_id) {
|
||||||
const action = isPlaying.value ? 'resumeUpload' : 'pauseUpload'
|
const action = isPlaying.value ? 'pauseUpload' : 'resumeUpload'
|
||||||
uploadsStore[action](props.extra.upload_id)
|
uploadsStore[action](props.extra.upload_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,7 +123,7 @@ const handleDownload = () => {
|
|||||||
|
|
||||||
<!-- 上传进度圆环 - 上传状态 -->
|
<!-- 上传进度圆环 - 上传状态 -->
|
||||||
<div v-if="extra.is_uploading" class="progress-overlay">
|
<div v-if="extra.is_uploading" class="progress-overlay">
|
||||||
<div class="circle-progress-container" @click="togglePlay">
|
<div class="circle-progress-container" @click.stop="togglePlay">
|
||||||
<svg class="circle-progress" width="20" height="20" viewBox="0 0 20 20">
|
<svg class="circle-progress" width="20" height="20" viewBox="0 0 20 20">
|
||||||
<!-- 底色圆环 -->
|
<!-- 底色圆环 -->
|
||||||
<circle
|
<circle
|
||||||
@ -150,13 +149,14 @@ const handleDownload = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 暂停/播放图标 -->
|
<!-- 暂停/播放图标 -->
|
||||||
<g v-if="isPlaying" class="pause-icon">
|
|
||||||
|
<g v-if="isPlaying" class="play-icon">
|
||||||
|
<rect x="6" y="6" width="8" height="8" :fill="fileInfo.color" />
|
||||||
|
</g>
|
||||||
|
<g v-else class="pause-icon">
|
||||||
<rect x="7" y="5" width="2" height="10" :fill="fileInfo.color" />
|
<rect x="7" y="5" width="2" height="10" :fill="fileInfo.color" />
|
||||||
<rect x="11" y="5" width="2" height="10" :fill="fileInfo.color" />
|
<rect x="11" y="5" width="2" height="10" :fill="fileInfo.color" />
|
||||||
</g>
|
</g>
|
||||||
<g v-else class="play-icon">
|
|
||||||
<rect x="6" y="6" width="8" height="8" :fill="fileInfo.color" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -137,7 +137,8 @@ function resumeUpload(e) {
|
|||||||
|
|
||||||
<!-- <n-image :src="extra.cover" preview-disabled /> -->
|
<!-- <n-image :src="extra.cover" preview-disabled /> -->
|
||||||
<video :src="props.extra.url" :controls="false"></video>
|
<video :src="props.extra.url" :controls="false"></video>
|
||||||
|
<!-- 上传进度时的黑色半透明蒙层 -->
|
||||||
|
<div v-if="extra.is_uploading && !uploadFailed" class="upload-mask"></div>
|
||||||
<!-- 上传进度显示 -->
|
<!-- 上传进度显示 -->
|
||||||
<div v-if="extra.is_uploading && !uploadFailed" class="upload-progress">
|
<div v-if="extra.is_uploading && !uploadFailed" class="upload-progress">
|
||||||
<n-progress
|
<n-progress
|
||||||
@ -245,6 +246,17 @@ function resumeUpload(e) {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.upload-mask {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.45);
|
||||||
|
z-index: 1;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.upload-progress {
|
.upload-progress {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
@ -255,6 +267,7 @@ function resumeUpload(e) {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
.upload-control {
|
.upload-control {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -79,28 +79,6 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
findItemByClientId(clientUploadId: string): UploadItem | undefined {
|
findItemByClientId(clientUploadId: string): UploadItem | undefined {
|
||||||
return this.items.find((item) => item.client_upload_id === clientUploadId)
|
return this.items.find((item) => item.client_upload_id === clientUploadId)
|
||||||
},
|
},
|
||||||
|
|
||||||
// // 暂停文件上传
|
|
||||||
// pauseUpload(uploadId: string) {
|
|
||||||
// const item = this.findItem(uploadId)
|
|
||||||
// if (!item) return
|
|
||||||
|
|
||||||
// item.is_paused = true
|
|
||||||
// console.log(`暂停上传: ${uploadId}`)
|
|
||||||
// },
|
|
||||||
|
|
||||||
// 恢复文件上传
|
|
||||||
// resumeUpload(uploadId: string) {
|
|
||||||
// const item = this.findItem(uploadId)
|
|
||||||
// if (!item) return
|
|
||||||
|
|
||||||
// item.is_paused = false
|
|
||||||
// console.log(`恢复上传: ${uploadId}`)
|
|
||||||
|
|
||||||
// // 继续上传
|
|
||||||
// this.triggerUpload(uploadId)
|
|
||||||
// },
|
|
||||||
|
|
||||||
// 发送上传消息
|
// 发送上传消息
|
||||||
async sendUploadMessage(item: any) {
|
async sendUploadMessage(item: any) {
|
||||||
try {
|
try {
|
||||||
@ -119,8 +97,7 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
file: File,
|
file: File,
|
||||||
talkType: number,
|
talkType: number,
|
||||||
receiverId: number,
|
receiverId: number,
|
||||||
username: string,
|
clientUploadId: string,
|
||||||
uploadId: string,
|
|
||||||
onProgress: (percentage: number) => void,
|
onProgress: (percentage: number) => void,
|
||||||
onComplete: (data: any) => void
|
onComplete: (data: any) => void
|
||||||
) {
|
) {
|
||||||
@ -147,13 +124,11 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
talk_type: talkType,
|
talk_type: talkType,
|
||||||
receiver_id: receiverId,
|
receiver_id: receiverId,
|
||||||
upload_id: upload_id,
|
upload_id: upload_id,
|
||||||
client_upload_id: uploadId, // 客户端生成的上传ID,用于前端标识
|
client_upload_id: clientUploadId, // 客户端生成的上传ID,用于前端标识
|
||||||
uploadIndex: 0,
|
uploadIndex: 0,
|
||||||
percentage: 0,
|
percentage: 0,
|
||||||
status: 0, // 文件上传状态 0:等待上传 1:上传中 2:上传完成 3:网络异常
|
status: 0, // 文件上传状态 0:等待上传 1:上传中 2:上传完成 3:网络异常
|
||||||
files: fileChunks,
|
files: fileChunks,
|
||||||
avatar: '',
|
|
||||||
username: username,
|
|
||||||
is_paused: false,
|
is_paused: false,
|
||||||
onProgress: onProgress,
|
onProgress: onProgress,
|
||||||
onComplete: onComplete,
|
onComplete: onComplete,
|
||||||
@ -162,7 +137,7 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
this.isShow = false // 不显示上传管理抽屉
|
this.isShow = false // 不显示上传管理抽屉
|
||||||
|
|
||||||
// 开始上传分片
|
// 开始上传分片
|
||||||
this.triggerUpload(upload_id, uploadId)
|
this.triggerUpload(upload_id, clientUploadId)
|
||||||
} else {
|
} else {
|
||||||
message.error(res.message)
|
message.error(res.message)
|
||||||
onProgress(-1) // 通知上传失败
|
onProgress(-1) // 通知上传失败
|
||||||
@ -198,16 +173,16 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
|
|
||||||
// 上传当前分片
|
// 上传当前分片
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const res = await ServeFileSubareaUpload(form)
|
const res = await ServeFileSubareaUpload(form)
|
||||||
|
|
||||||
// 获取最新的项目状态,确保仍然存在且没有被暂停
|
// 获取最新的项目状态,确保仍然存在且没有被暂停
|
||||||
const updatedItem = this.findItem(uploadId)
|
const updatedItem:any = this.findItem(uploadId)
|
||||||
if (!updatedItem || updatedItem.is_paused) return
|
|
||||||
|
|
||||||
if (res.code == 200) {
|
if (res.code == 200) {
|
||||||
// 当前分片上传成功,增加索引
|
// 当前分片上传成功,增加索引
|
||||||
updatedItem.uploadIndex++
|
updatedItem.uploadIndex++
|
||||||
|
|
||||||
// 计算上传进度
|
// 计算上传进度
|
||||||
const percentage = (updatedItem.uploadIndex / updatedItem.files.length) * 100
|
const percentage = (updatedItem.uploadIndex / updatedItem.files.length) * 100
|
||||||
updatedItem.percentage = parseFloat(percentage.toFixed(1))
|
updatedItem.percentage = parseFloat(percentage.toFixed(1))
|
||||||
@ -234,8 +209,7 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
console.error(`分片上传失败,错误码: ${res.code},错误信息: ${res.message || '未知错误'}`);
|
console.error(`分片上传失败,错误码: ${res.code},错误信息: ${res.message || '未知错误'}`);
|
||||||
updatedItem.status = 3
|
updatedItem.status = 3
|
||||||
|
|
||||||
// 尝试重试当前分片
|
|
||||||
this.retryUpload(uploadId, clientUploadId, res.message || '上传失败,请重试')
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("分片上传错误:", error);
|
console.error("分片上传错误:", error);
|
||||||
@ -248,37 +222,10 @@ export const useUploadsStore = defineStore('uploads', {
|
|||||||
if (updatedItem.is_paused) return
|
if (updatedItem.is_paused) return
|
||||||
|
|
||||||
updatedItem.status = 3
|
updatedItem.status = 3
|
||||||
|
|
||||||
// 尝试重试当前分片
|
|
||||||
this.retryUpload(uploadId, clientUploadId, '网络错误,正在重试')
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 重试上传
|
|
||||||
retryUpload(uploadId: string, clientUploadId?: string, errorMessage?: string) {
|
|
||||||
const item = this.findItem(uploadId)
|
|
||||||
if (!item) return
|
|
||||||
|
|
||||||
// 如果有暂停/恢复按钮,先告知用户上传出错
|
|
||||||
if (item.onProgress) {
|
|
||||||
item.onProgress(-1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示错误提示
|
|
||||||
message.warning(errorMessage)
|
|
||||||
|
|
||||||
// 创建一个5秒后自动重试的机制
|
|
||||||
setTimeout(() => {
|
|
||||||
const currentItem = this.findItem(uploadId)
|
|
||||||
if (!currentItem) return
|
|
||||||
|
|
||||||
// 如果用户没有手动暂停,则自动重试
|
|
||||||
if (!currentItem.is_paused) {
|
|
||||||
console.log('正在重试上传分片...');
|
|
||||||
this.triggerUpload(uploadId, clientUploadId)
|
|
||||||
}
|
|
||||||
}, 5000)
|
|
||||||
},
|
|
||||||
|
|
||||||
// 完成上传
|
// 完成上传
|
||||||
async completeUpload(item: UploadItem, clientUploadId: string) {
|
async completeUpload(item: UploadItem, clientUploadId: string) {
|
||||||
|
@ -17,6 +17,7 @@ import { ExclamationCircleFilled } from '@ant-design/icons-vue'
|
|||||||
import { useUserStore } from '@/store'
|
import { useUserStore } from '@/store'
|
||||||
import RevokeMessage from '@/components/talk/message/RevokeMessage.vue'
|
import RevokeMessage from '@/components/talk/message/RevokeMessage.vue'
|
||||||
import { voiceToText } from '@/api/chat.js'
|
import { voiceToText } from '@/api/chat.js'
|
||||||
|
import {confirmBox} from '@/components/confirm-box/service.js'
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
uid: {
|
uid: {
|
||||||
type: Number,
|
type: Number,
|
||||||
@ -314,6 +315,13 @@ watch(
|
|||||||
// onMounted(() => {
|
// onMounted(() => {
|
||||||
// onLoad({ ...props, limit: 30 })
|
// onLoad({ ...props, limit: 30 })
|
||||||
// })
|
// })
|
||||||
|
const retry=()=>{
|
||||||
|
confirmBox({
|
||||||
|
content:'确定重发吗'
|
||||||
|
}).then(()=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -414,7 +422,7 @@ watch(
|
|||||||
"
|
"
|
||||||
class="mr-10px"
|
class="mr-10px"
|
||||||
>
|
>
|
||||||
<n-button text style="font-size: 20px;">
|
<n-button text style="font-size: 20px;" @click="retry">
|
||||||
<n-icon color="#CF3050">
|
<n-icon color="#CF3050">
|
||||||
<ExclamationCircleFilled />
|
<ExclamationCircleFilled />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
|
@ -116,15 +116,13 @@ const onSendVideoEvent = async ({ data }) => {
|
|||||||
msg_type: 5, // 视频消息类型
|
msg_type: 5, // 视频消息类型
|
||||||
user_id: props.uid,
|
user_id: props.uid,
|
||||||
receiver_id: props.receiver_id,
|
receiver_id: props.receiver_id,
|
||||||
nickname: '我', // 本地显示
|
|
||||||
avatar: userStore.avatar, // 本地显示可能不需要
|
|
||||||
is_revoke: 0,
|
is_revoke: 0,
|
||||||
is_mark: 0,
|
is_mark: 0,
|
||||||
is_read: 1,
|
is_read: 1,
|
||||||
content: '',
|
content: '',
|
||||||
created_at: parseTime(new Date(), '{y}-{m}-{d} {h}:{i}'),
|
created_at: parseTime(new Date(), '{y}-{m}-{d} {h}:{i}'),
|
||||||
extra: {
|
extra: {
|
||||||
url: '', // 上传完成后会更新
|
url: '',
|
||||||
size: data.size,
|
size: data.size,
|
||||||
is_uploading: true,
|
is_uploading: true,
|
||||||
upload_id: uploadId,
|
upload_id: uploadId,
|
||||||
@ -141,35 +139,12 @@ const onSendVideoEvent = async ({ data }) => {
|
|||||||
data,
|
data,
|
||||||
props.talk_type,
|
props.talk_type,
|
||||||
props.receiver_id,
|
props.receiver_id,
|
||||||
dialogueStore.talk.username,
|
|
||||||
uploadId,
|
uploadId,
|
||||||
async (percentage) => {
|
async (percentage) => {
|
||||||
dialogueStore.updateUploadProgress(uploadId, percentage)
|
dialogueStore.updateUploadProgress(uploadId, percentage)
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
dialogueStore.batchDelDialogueRecord([uploadId])
|
dialogueStore.batchDelDialogueRecord([uploadId])
|
||||||
// console.log('videoData', videoData)
|
|
||||||
// // 上传完成后的回调
|
|
||||||
|
|
||||||
// // 更新临时消息为最终消息
|
|
||||||
// dialogueStore.completeUpload(uploadId, {
|
|
||||||
// url: videoData.data.ori_url,
|
|
||||||
// cover: videoData.data.cover_url
|
|
||||||
// })
|
|
||||||
|
|
||||||
// // 上传成功后,发送正式消息给服务端
|
|
||||||
// let finalMessage = {
|
|
||||||
// type: 'video',
|
|
||||||
// url: videoData.data.ori_url,
|
|
||||||
|
|
||||||
// size: data.size
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // 发送真实消息到服务端
|
|
||||||
// onSendMessage(finalMessage, () => {
|
|
||||||
// // 上传成功且消息发送成功后,删除临时消息
|
|
||||||
// dialogueStore.batchDelDialogueRecord([uploadId])
|
|
||||||
// })
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -185,10 +160,10 @@ const onSendFileEvent = ({ data }) => {
|
|||||||
if (data.size > maxsize) {
|
if (data.size > maxsize) {
|
||||||
return window['$message'].warning('上传文件不能超过100M!')
|
return window['$message'].warning('上传文件不能超过100M!')
|
||||||
}
|
}
|
||||||
const uploadId = `file-${Date.now()}-${Math.floor(Math.random() * 1000)}`
|
const clientUploadId = `file-${Date.now()}-${Math.floor(Math.random() * 1000)}`
|
||||||
|
|
||||||
const tempMessage = {
|
const tempMessage = {
|
||||||
msg_id: uploadId,
|
msg_id: clientUploadId,
|
||||||
sequence: Date.now(),
|
sequence: Date.now(),
|
||||||
talk_type: props.talk_type,
|
talk_type: props.talk_type,
|
||||||
msg_type: 6,
|
msg_type: 6,
|
||||||
@ -204,20 +179,19 @@ const onSendFileEvent = ({ data }) => {
|
|||||||
url: '',
|
url: '',
|
||||||
size: data.size,
|
size: data.size,
|
||||||
is_uploading: true,
|
is_uploading: true,
|
||||||
upload_id: uploadId,
|
upload_id: clientUploadId,
|
||||||
percentage: 0
|
percentage: 0
|
||||||
},
|
},
|
||||||
erp_user_id: 4692,
|
|
||||||
float: 'right'
|
float: 'right'
|
||||||
}
|
}
|
||||||
dialogueStore.addDialogueRecord(tempMessage)
|
dialogueStore.addDialogueRecord(tempMessage)
|
||||||
|
|
||||||
uploadsStore.initUploadFile(data, props.talk_type, props.receiver_id, dialogueStore.talk.username,uploadId,
|
uploadsStore.initUploadFile(data, props.talk_type, props.receiver_id,clientUploadId,
|
||||||
async (percentage) => {
|
async (percentage) => {
|
||||||
dialogueStore.updateUploadProgress(uploadId, percentage)
|
dialogueStore.updateUploadProgress(clientUploadId, percentage)
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
dialogueStore.batchDelDialogueRecord([uploadId])
|
dialogueStore.batchDelDialogueRecord([clientUploadId])
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ export default defineConfig(({ mode }) => {
|
|||||||
compressPlugin(),
|
compressPlugin(),
|
||||||
UnoCSS(),
|
UnoCSS(),
|
||||||
vueDevTools({
|
vueDevTools({
|
||||||
launchEditor: 'cursor',
|
launchEditor: 'trae',
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
define: {
|
define: {
|
||||||
|
Loading…
Reference in New Issue
Block a user