<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>