chat-pc/src/components/talk/message/VideoMessage.vue
Phoenix e2e0a3ea3a fix: 修复文件上传逻辑和UI问题
- 修复文件上传暂停/恢复逻辑错误,调整播放状态与上传动作的对应关系
- 为视频上传添加半透明蒙层提升用户体验
- 移除上传管理中的冗余字段和注释代码
- 调整确认框标题的padding样式
- 添加消息重发确认功能
2025-05-26 16:43:11 +08:00

311 lines
7.0 KiB
Vue

<script lang="ts" setup>
import 'xgplayer/dist/index.min.css'
import { ref, nextTick, watch } from 'vue'
import { NImage, NModal, NCard, NProgress, NPopconfirm } from 'naive-ui'
import { Play, Close, Pause, Right, Attention } from '@icon-park/vue-next'
import { getImageInfo } from '@/utils/functions'
import {PauseOutline} from '@vicons/ionicons5'
import Player from 'xgplayer'
import { ITalkRecordExtraVideo, ITalkRecord } from '@/types/chat'
import { useUploadsStore } from '@/store'
// @ts-ignore
const message = window.$message
const uploadsStore = useUploadsStore()
const props = defineProps<{
extra: ITalkRecordExtraVideo
data: ITalkRecord
maxWidth?: Boolean
}>()
// const img = (src: string, width = 200) => {
// const info: any = getImageInfo(src)
// if (info.width == 0 || info.height == 0) {
// return {}
// }
// if (info.height > 300) {
// return {
// height: '300px'
// }
// }
// if (info.width < width) {
// return {
// width: `${info.width}px`,
// height: `${info.height}px`
// }
// }
// return {
// width: width + 'px',
// height: info.height / (info.width / width) + 'px'
// }
// }
const open = ref(false)
const isPaused = ref(false)
const uploadFailed = ref(false)
// 查找上传项并检查状态
const updatePauseStatus = () => {
if (props.extra.is_uploading && props.extra.upload_id) {
// 使用新的查找方法
const item = uploadsStore.findItemByClientId(props.extra.upload_id)
if (item && item.is_paused !== undefined) {
isPaused.value = item.is_paused
}
}
}
// 初始化时检查状态
updatePauseStatus()
// // 监听关键道具变化
// watch(() => props.extra.percentage, (newVal: number | undefined) => {
// // 确保进度更新时 UI 也实时更新
// // 检测上传失败状态 (-1表示上传失败)
// if (newVal === -1) {
// uploadFailed.value = true
// // 显示上传失败提示
// message.error('视频发送失败,请点击红色感叹号重试')
// } else if (newVal !== undefined && newVal > 0) {
// uploadFailed.value = false
// }
// }, { immediate: true })
async function onPlay() {
// 如果视频正在上传,不执行播放操作
if (props.extra.is_uploading) {
return
}
open.value = true
await nextTick()
new Player({
id: 'im-xgplayer',
url: props.extra.url,
fluid: true,
autoplay: true,
lang: 'zh-cn'
})
}
// 暂停上传
function pauseUpload(e) {
e.stopPropagation()
if (props.extra.is_uploading && props.extra.upload_id) {
uploadsStore.pauseUpload(props.extra.upload_id)
isPaused.value = true
}
}
// 继续上传
function resumeUpload(e) {
console.log('resumeUpload')
e.stopPropagation()
if (props.extra.is_uploading && props.extra.upload_id) {
uploadsStore.resumeUpload(props.extra.upload_id)
isPaused.value = false
}
}
// 重新上传视频
// function retryUpload(e) {
// e.stopPropagation()
// if (props.extra.upload_id) {
// // 重置失败状态
// uploadFailed.value = false
// // 恢复上传
// uploadsStore.resumeUpload(props.extra.upload_id)
// message.success('正在重新上传视频...')
// }
// }
</script>
<template>
<section
class="im-message-video"
:class="{ left: data.float === 'left' }"
@click="onPlay"
>
<!-- <n-image :src="extra.cover" preview-disabled /> -->
<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">
<n-progress
type="circle"
:percentage="Math.round(extra.percentage || 0)"
:show-indicator="false"
:stroke-width="6"
color="#fff"
rail-color="#E3E3E3"
/>
<!-- 暂停/继续按钮移到圆圈内部 -->
<div class="upload-control" @click.stop>
<n-icon
v-if="!isPaused"
class="control-btn"
:component="PauseOutline"
size="20"
@click="pauseUpload"
/>
<div v-else class="w-15px h-15px bg-#fff rounded-4px" @click="resumeUpload" >
</div>
<!-- <n-icon
v-else
class="control-btn"
:component="Right"
size="20"
@click="resumeUpload"
/> -->
</div>
</div>
<!-- 播放按钮,仅在视频不是上传状态且未失败时显示 -->
<div v-if="!extra.is_uploading && !uploadFailed" class="btn-video">
<n-icon :component="Play" size="40" />
</div>
<n-modal v-model:show="open">
<n-card
style="width: 800px; min-height: 300px; background-color: #ffffff; position: relative"
role="dialog"
aria-modal="true"
>
<div id="im-xgplayer"></div>
<div class="im-xgplayer-close" @click="open = false">
<n-icon :component="Close" size="18" />
</div>
</n-card>
</n-modal>
</section>
</template>
<style lang="less" scoped>
.im-message-video {
overflow: hidden;
padding: 5px;
border-radius: 5px;
background: var(--im-message-left-bg-color);
min-width: 30px;
min-height: 30px;
display: inline-flex;
position: relative;
height:149px;
width: 225px;
&.left {
background: var(--im-message-right-bg-color);
}
video {
width: 100%;
height: 100%;
border-radius: 5px;
object-fit: cover;
background-color: #333; /* 添加背景色,避免默认显示为灰色 */
}
.btn-video {
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
position: absolute;
cursor: pointer;
color: #ffffff;
}
&:hover {
.btn-video {
color: #03a9f4;
}
}
}
.im-xgplayer-close {
position: absolute;
height: 35px;
width: 35px;
background-color: #f5f5f5;
right: -45px;
top: -45px;
cursor: pointer;
border-radius: 50%;
color: #000;
display: flex;
align-items: 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 {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
z-index: 2;
.upload-control {
position: absolute;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
.control-btn {
color: white;
z-index: 2;
}
}
}
/* 上传失败样式 */
.upload-failed {
position: absolute;
left: 10px;
bottom: 10px;
z-index: 2;
.failed-icon {
width: 30px;
height: 30px;
background-color: rgba(0, 0, 0, 0.7);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
&:hover {
background-color: rgba(0, 0, 0, 0.9);
}
}
}
</style>