338 lines
7.1 KiB
Vue
338 lines
7.1 KiB
Vue
<template>
|
|
<!-- 图片预览模态框 -->
|
|
<a-modal
|
|
v-model:visible="previewModalVisible"
|
|
:width="1000"
|
|
:footer="false"
|
|
title="多媒体预览"
|
|
class="preview-modal"
|
|
@close="handleModalClose"
|
|
>
|
|
<div class="preview-container">
|
|
<!-- 图片预览区域 -->
|
|
<div class="image-section">
|
|
<img
|
|
:src="currentPreviewItem?.url"
|
|
:alt="currentPreviewItem?.name"
|
|
class="preview-image"
|
|
/>
|
|
</div>
|
|
|
|
<!-- 音频列表区域 -->
|
|
<div class="audio-section">
|
|
<h3 class="audio-title">关联音频 ({{ audioList.length }})</h3>
|
|
|
|
<div class="audio-list">
|
|
<div
|
|
v-for="(audio, index) in audioList"
|
|
:key="index"
|
|
class="audio-item"
|
|
:class="{ 'active': currentAudioIndex === index }"
|
|
>
|
|
<div class="audio-info">
|
|
<span class="audio-name">{{ audio.audioId || `音频 ${index + 1}` }}</span>
|
|
<span class="audio-duration">{{ formatDuration(audio.duration) }}</span>
|
|
</div>
|
|
|
|
<div class="audio-controls">
|
|
<a-button
|
|
size="mini"
|
|
@click="toggleAudio(index)"
|
|
:loading="audioLoading && currentAudioIndex === index"
|
|
>
|
|
{{ currentAudioIndex === index && isPlaying ? '暂停' : '播放' }}
|
|
</a-button>
|
|
|
|
<a-progress
|
|
v-if="currentAudioIndex === index"
|
|
:percent="playProgress"
|
|
:show-text="false"
|
|
size="small"
|
|
class="progress-bar"
|
|
/>
|
|
</div>
|
|
|
|
<!-- 隐藏的audio元素 -->
|
|
<audio
|
|
ref="audioPlayers"
|
|
:src="audio.url"
|
|
@timeupdate="handleTimeUpdate"
|
|
@ended="handleAudioEnded"
|
|
preload="metadata"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</a-modal>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed, nextTick } from 'vue'
|
|
import { Message } from '@arco-design/web-vue'
|
|
|
|
interface AudioItem {
|
|
url: string
|
|
name?: string
|
|
duration?: number // 单位秒
|
|
}
|
|
|
|
interface PreviewItem {
|
|
url: string
|
|
name: string
|
|
audios?: AudioItem[]
|
|
}
|
|
|
|
// 模态框显示状态
|
|
const previewModalVisible = ref(false)
|
|
const currentPreviewItem = ref<PreviewItem | null>(null)
|
|
|
|
// 音频相关状态
|
|
const audioList = ref<AudioItem[]>([])
|
|
const currentAudioIndex = ref(-1)
|
|
const isPlaying = ref(false)
|
|
const audioLoading = ref(false)
|
|
const playProgress = ref(0)
|
|
const audioPlayers = ref<HTMLAudioElement[]>([])
|
|
|
|
const getAudioUrl = (filePath: string): string => {
|
|
if (!filePath) return ''
|
|
if (filePath.startsWith('http')) return filePath
|
|
const baseUrl = 'http://pms.dtyx.net:9158'
|
|
return `${baseUrl}${filePath}`
|
|
}
|
|
|
|
// 打开预览模态框
|
|
const openPreview = (item: PreviewItem) => {
|
|
currentPreviewItem.value = item
|
|
audioList.value = []
|
|
if(item.audios){
|
|
for (const audio of item.audios) {
|
|
let temp={
|
|
audioId:audio.audioId,
|
|
url:getAudioUrl(audio.filePath)
|
|
}
|
|
audioList.value.push(temp)
|
|
}
|
|
}
|
|
|
|
previewModalVisible.value = true
|
|
resetAudioState()
|
|
|
|
// 预加载音频时长
|
|
nextTick(() => {
|
|
loadAudioDurations()
|
|
})
|
|
}
|
|
|
|
// 关闭模态框
|
|
const handleModalClose = () => {
|
|
stopAudio()
|
|
previewModalVisible.value = false
|
|
}
|
|
|
|
// 重置音频状态
|
|
const resetAudioState = () => {
|
|
currentAudioIndex.value = -1
|
|
isPlaying.value = false
|
|
playProgress.value = 0
|
|
}
|
|
|
|
// 加载音频时长信息
|
|
const loadAudioDurations = () => {
|
|
audioPlayers.value.forEach((player, index) => {
|
|
player.onloadedmetadata = () => {
|
|
if (!audioList.value[index].duration) {
|
|
audioList.value[index].duration = player.duration
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// 切换音频播放状态
|
|
const toggleAudio = (index: number) => {
|
|
if (currentAudioIndex.value === index) {
|
|
// 同一音频,切换播放/暂停
|
|
if (isPlaying.value) {
|
|
pauseAudio()
|
|
} else {
|
|
playAudio(index)
|
|
}
|
|
} else {
|
|
// 切换不同音频
|
|
stopAudio()
|
|
playAudio(index)
|
|
}
|
|
}
|
|
|
|
// 播放音频
|
|
const playAudio = (index: number) => {
|
|
audioLoading.value = true
|
|
currentAudioIndex.value = index
|
|
|
|
const player = audioPlayers.value[index]
|
|
player.play()
|
|
.then(() => {
|
|
isPlaying.value = true
|
|
audioLoading.value = false
|
|
})
|
|
.catch(err => {
|
|
console.error('播放失败:', err)
|
|
Message.error('音频播放失败')
|
|
audioLoading.value = false
|
|
})
|
|
}
|
|
|
|
// 暂停音频
|
|
const pauseAudio = () => {
|
|
const player = audioPlayers.value[currentAudioIndex.value]
|
|
player.pause()
|
|
isPlaying.value = false
|
|
}
|
|
|
|
// 停止音频
|
|
const stopAudio = () => {
|
|
if (currentAudioIndex.value !== -1) {
|
|
const player = audioPlayers.value[currentAudioIndex.value]
|
|
player.pause()
|
|
player.currentTime = 0
|
|
}
|
|
isPlaying.value = false
|
|
currentAudioIndex.value = -1
|
|
playProgress.value = 0
|
|
}
|
|
|
|
// 音频时间更新
|
|
const handleTimeUpdate = (e: Event) => {
|
|
const player = e.target as HTMLAudioElement
|
|
if (player.duration) {
|
|
playProgress.value = (player.currentTime / player.duration) * 100
|
|
}
|
|
}
|
|
|
|
// 音频播放结束
|
|
const handleAudioEnded = () => {
|
|
isPlaying.value = false
|
|
playProgress.value = 100
|
|
}
|
|
|
|
// 格式化时长显示
|
|
const formatDuration = (seconds?: number) => {
|
|
if (!seconds) return '--:--'
|
|
const mins = Math.floor(seconds / 60)
|
|
const secs = Math.floor(seconds % 60)
|
|
return `${mins}:${secs < 10 ? '0' : ''}${secs}`
|
|
}
|
|
|
|
// 暴露方法供父组件调用
|
|
defineExpose({
|
|
openPreview
|
|
})
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
.preview-modal {
|
|
:deep(.arco-modal-content) {
|
|
padding: 0;
|
|
}
|
|
}
|
|
|
|
.preview-container {
|
|
display: flex;
|
|
height: 70vh;
|
|
max-height: 800px;
|
|
}
|
|
|
|
.image-section {
|
|
flex: 1;
|
|
padding: 16px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background-color: #f5f5f5;
|
|
overflow: hidden;
|
|
|
|
.preview-image {
|
|
max-width: 100%;
|
|
max-height: 100%;
|
|
object-fit: contain;
|
|
}
|
|
}
|
|
|
|
.audio-section {
|
|
width: 350px;
|
|
border-left: 1px solid #e5e5e5;
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
.audio-title {
|
|
padding: 16px;
|
|
margin: 0;
|
|
font-size: 16px;
|
|
border-bottom: 1px solid #e5e5e5;
|
|
}
|
|
|
|
.audio-list {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 8px 0;
|
|
}
|
|
|
|
.audio-item {
|
|
padding: 12px 16px;
|
|
transition: background-color 0.2s;
|
|
|
|
&.active {
|
|
background-color: #f0f7ff;
|
|
}
|
|
|
|
&:hover {
|
|
background-color: #f9f9f9;
|
|
}
|
|
}
|
|
|
|
.audio-info {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-bottom: 8px;
|
|
|
|
.audio-name {
|
|
font-weight: 500;
|
|
}
|
|
|
|
.audio-duration {
|
|
color: #8c8c8c;
|
|
font-size: 12px;
|
|
}
|
|
}
|
|
|
|
.audio-controls {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
|
|
.progress-bar {
|
|
flex: 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
@media (max-width: 1200px) {
|
|
.preview-container {
|
|
flex-direction: column;
|
|
height: 80vh;
|
|
}
|
|
|
|
.image-section {
|
|
height: 60%;
|
|
}
|
|
|
|
.audio-section {
|
|
width: 100%;
|
|
height: 40%;
|
|
border-left: none;
|
|
border-top: 1px solid #e5e5e5;
|
|
}
|
|
}
|
|
</style>
|