<template>
|
<TableLayout :permissions="['business:collectionMedia:query', 'business:collectionStation:query']">
|
<div ref="QueryFormRef" slot="search-form">
|
<el-form ref="searchForm" :model="searchForm" label-width="100px" inline>
|
<el-form-item label="采集站" prop="stationId">
|
<el-select v-model="searchForm.stationId" placeholder="全部" clearable filterable @change="onStationChange">
|
<el-option v-for="item in stationOptions" :key="item.id" :label="item.name" :value="item.id" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="下载状态" prop="downloadStatus">
|
<el-select v-model="searchForm.downloadStatus" placeholder="请选择" clearable>
|
<el-option label="待下载" :value="0" />
|
<el-option label="已下载" :value="1" />
|
<el-option label="失败" :value="2" />
|
<el-option label="下载中" :value="3" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="类型" prop="mediaType">
|
<el-select v-model="searchForm.mediaType" placeholder="请选择" clearable>
|
<el-option label="视频" :value="0" />
|
<el-option label="图片" :value="1" />
|
<el-option label="音频" :value="2" />
|
</el-select>
|
</el-form-item>
|
<section>
|
<el-button type="primary" @click="search">搜索</el-button>
|
<el-button @click="reset">重置</el-button>
|
</section>
|
</el-form>
|
</div>
|
|
<template v-slot:table-wrap>
|
<ul class="toolbar">
|
<li>
|
<el-button v-permissions="['business:collectionMedia:sync', 'business:collectionStation:sync']" @click="handleSyncMedia">同步索引</el-button>
|
</li>
|
<li>
|
<el-button type="primary" v-permissions="['business:collectionMedia:download', 'business:collectionStation:sync']" @click="handleBatchDownload">批量下载</el-button>
|
</li>
|
</ul>
|
<el-table :height="tableHeightNew" v-loading="isWorking.search" :data="tableData.list" stripe>
|
<el-table-column label="序号" width="55"><template slot-scope="scope">{{ scope.$index + 1 }}</template></el-table-column>
|
<el-table-column prop="fileName" label="文件名" min-width="180" show-overflow-tooltip />
|
<el-table-column prop="mediaType" label="类型" width="80">
|
<template slot-scope="{row}">
|
<span>{{ mediaTypeLabel(row) }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column prop="fileSize" label="大小(字节)" min-width="100" />
|
<el-table-column prop="startTime" label="开始时间" min-width="160" />
|
<el-table-column prop="endTime" label="结束时间" min-width="160" />
|
<el-table-column prop="downloadStatus" label="下载状态" width="90">
|
<template slot-scope="{row}">
|
<span v-if="row.downloadStatus === 1">已下载</span>
|
<span v-else-if="row.downloadStatus === 2">失败</span>
|
<span v-else-if="row.downloadStatus === 3">下载中</span>
|
<span v-else>待下载</span>
|
</template>
|
</el-table-column>
|
<el-table-column prop="filePathLocal" label="本地路径" min-width="160" show-overflow-tooltip />
|
<el-table-column label="操作" width="220" fixed="right">
|
<template slot-scope="{row}">
|
<el-button type="text" v-if="canPreview(row)" @click="handlePreview(row)">预览</el-button>
|
<el-button type="text" v-if="canDownloadToStation(row)" v-permissions="['business:collectionMedia:download', 'business:collectionStation:sync']"
|
@click="handleDownload(row)">下载</el-button>
|
<el-button type="text" v-if="row.downloadStatus === 1" @click="handleSaveLocal(row)">下载到本地</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
<pagination @size-change="handleSizeChange" @current-change="handlePageChange" :pagination="tableData.pagination" />
|
</template>
|
|
<el-dialog :title="previewTitle" :visible.sync="previewVisible" width="860px" append-to-body @close="closePreview">
|
<div v-loading="previewLoading" class="preview-wrap">
|
<video v-if="previewMode === 'video' && previewSrc" :key="previewSrc" :src="previewSrc" controls
|
preload="metadata" playsinline class="preview-video" @error="onPreviewMediaError" />
|
<audio v-else-if="previewMode === 'audio' && previewSrc" :key="previewSrc" :src="previewSrc" controls class="preview-audio"
|
@error="onPreviewMediaError" />
|
<el-image v-else-if="previewMode === 'image' && previewSrc" :src="previewSrc" fit="contain" class="preview-image"
|
:preview-src-list="[previewSrc]" />
|
<pre v-else-if="previewMode === 'text'" class="preview-text">{{ previewText }}</pre>
|
<div v-else-if="!previewLoading" class="preview-empty">暂不支持该类型预览</div>
|
</div>
|
</el-dialog>
|
</TableLayout>
|
</template>
|
|
<script>
|
import BaseTable from '@/components/base/BaseTable'
|
import TableLayout from '@/layouts/TableLayout'
|
import Pagination from '@/components/common/Pagination'
|
import { syncMedia, downloadMedia, batchDownloadMedia, list as fetchStationList } from '@/api/business/collectionStation'
|
import { fetchPreviewText, fetchPreviewBlob, fetchMediaFile, ensureMp4Blob, buildPreviewUrl } from '@/api/business/collectionMedia'
|
|
export default {
|
name: 'CollectionMedia',
|
extends: BaseTable,
|
components: { TableLayout, Pagination },
|
data () {
|
return {
|
stationOptions: [],
|
searchForm: {
|
stationId: null,
|
downloadStatus: null,
|
mediaType: null
|
},
|
previewVisible: false,
|
previewLoading: false,
|
previewTitle: '媒体预览',
|
previewMode: '',
|
previewSrc: '',
|
previewText: '',
|
previewRow: null,
|
previewUseDirectUrl: false,
|
previewBlobUrl: '',
|
downloadPollTimer: null
|
}
|
},
|
created () {
|
if (this.$route.query.stationId) {
|
this.searchForm.stationId = parseInt(this.$route.query.stationId)
|
}
|
this.config({
|
module: '采集站媒体',
|
api: '/business/collectionMedia',
|
'field.id': 'id',
|
'field.main': 'id'
|
})
|
this.loadStations()
|
this.search()
|
},
|
beforeDestroy () {
|
this.revokePreviewUrl()
|
this.stopDownloadPoll()
|
},
|
methods: {
|
loadStations () {
|
fetchStationList({ status: 1 }).then(res => {
|
this.stationOptions = res || []
|
})
|
},
|
onStationChange () {
|
this.search()
|
},
|
mediaTypeLabel (row) {
|
const mode = this.resolvePreviewMode(row)
|
if (mode === 'image') return '图片'
|
if (mode === 'audio') return '音频'
|
if (mode === 'text') return '文本'
|
if (row.mediaType === 1) return '图片'
|
if (row.mediaType === 2) return '音频'
|
return '视频'
|
},
|
canPreview (row) {
|
return row.downloadStatus === 1 && (row.fileUrlFull || row.filePathLocal)
|
},
|
canDownloadToStation (row) {
|
return row.downloadStatus !== 1 && row.downloadStatus !== 3
|
},
|
resolvePreviewMode (row) {
|
const name = (row.fileName || '').toLowerCase()
|
if (/\.(jpg|jpeg|png|gif|bmp|webp)$/.test(name) || row.mediaType === 1) {
|
return 'image'
|
}
|
if (/\.(mp3|wav|aac|m4a)$/.test(name) || row.mediaType === 2) {
|
return 'audio'
|
}
|
if (/\.(txt|log)$/.test(name)) {
|
return 'text'
|
}
|
if (/\.(mp4|mov|avi|mkv|webm|flv|m4v)$/.test(name) || row.mediaType === 0) {
|
return 'video'
|
}
|
return 'video'
|
},
|
handleSyncMedia () {
|
syncMedia({ stationId: this.searchForm.stationId }).then(res => {
|
this.$message.success(res || '同步完成')
|
this.search()
|
})
|
},
|
handleBatchDownload () {
|
batchDownloadMedia({ stationId: this.searchForm.stationId, limit: 10 }).then(res => {
|
this.$message.success(res || '已提交下载任务')
|
this.search()
|
this.startDownloadPoll()
|
})
|
},
|
handleDownload (row) {
|
downloadMedia(row.id).then(res => {
|
this.$message.success(res || '已提交下载任务')
|
this.search()
|
this.startDownloadPoll()
|
})
|
},
|
handleSaveLocal (row) {
|
fetchMediaFile(row.id).then(response => {
|
this.download(response)
|
}).catch(err => {
|
this.$message.error(err.message || '下载到本地失败')
|
})
|
},
|
startDownloadPoll () {
|
this.stopDownloadPoll()
|
let count = 0
|
this.downloadPollTimer = setInterval(() => {
|
count++
|
if (count > 40) {
|
this.stopDownloadPoll()
|
return
|
}
|
this.api.fetchList({
|
page: this.tableData.pagination.pageIndex,
|
capacity: this.tableData.pagination.pageSize,
|
model: this.searchForm,
|
sorts: this.tableData.sorts
|
}).then(data => {
|
this.tableData.list = data.records
|
this.tableData.pagination.total = data.total
|
if (!(data.records || []).some(item => item.downloadStatus === 3)) {
|
this.stopDownloadPoll()
|
}
|
}).catch(() => {})
|
}, 3000)
|
},
|
stopDownloadPoll () {
|
if (this.downloadPollTimer) {
|
clearInterval(this.downloadPollTimer)
|
this.downloadPollTimer = null
|
}
|
},
|
handlePreview (row) {
|
if (row.downloadStatus !== 1) {
|
this.$message.warning('请先下载文件后再预览')
|
return
|
}
|
this.previewTitle = row.fileName || '媒体预览'
|
this.previewVisible = true
|
this.previewLoading = true
|
this.previewMode = this.resolvePreviewMode(row)
|
this.previewRow = row
|
this.previewSrc = ''
|
this.previewText = ''
|
this.previewUseDirectUrl = false
|
this.revokePreviewUrl()
|
|
if (this.previewMode === 'text') {
|
fetchPreviewText(row.id).then(text => {
|
this.previewText = text || ''
|
this.previewLoading = false
|
}).catch(err => {
|
this.previewLoading = false
|
this.$message.error(err.message || '文本预览失败')
|
})
|
return
|
}
|
|
if (this.previewMode === 'video') {
|
this.loadVideoPreview(row)
|
return
|
}
|
if (this.previewMode === 'audio') {
|
this.previewUseDirectUrl = !!row.fileUrlFull
|
this.previewSrc = row.fileUrlFull || buildPreviewUrl(row.id)
|
this.previewLoading = false
|
return
|
}
|
|
if (row.fileUrlFull) {
|
this.previewSrc = row.fileUrlFull
|
this.previewLoading = false
|
return
|
}
|
|
this.previewLoading = false
|
this.$message.warning('暂无可预览地址')
|
},
|
loadVideoPreview (row) {
|
this.revokePreviewUrl()
|
fetchPreviewBlob(row.id)
|
.then(blob => ensureMp4Blob(blob))
|
.then(blob => {
|
this.previewBlobUrl = URL.createObjectURL(blob)
|
this.previewSrc = this.previewBlobUrl
|
this.previewUseDirectUrl = false
|
this.previewLoading = false
|
})
|
.catch(err => {
|
if (row.fileUrlFull) {
|
this.previewUseDirectUrl = true
|
this.previewSrc = row.fileUrlFull
|
this.previewLoading = false
|
return
|
}
|
this.previewLoading = false
|
this.$message.error(err.message || '视频预览失败,请重新下载该文件')
|
})
|
},
|
onPreviewMediaError () {
|
if (!this.previewRow) {
|
this.$message.error('媒体加载失败')
|
return
|
}
|
if (this.previewMode === 'video') {
|
if (this.previewUseDirectUrl) {
|
this.$message.error('视频无法播放,请重新下载该文件(需采集站转 MP4)')
|
return
|
}
|
if (this.previewRow.fileUrlFull) {
|
this.previewUseDirectUrl = true
|
this.revokePreviewUrl()
|
this.previewSrc = this.previewRow.fileUrlFull
|
return
|
}
|
}
|
if (this.previewUseDirectUrl && this.previewMode === 'audio') {
|
this.previewUseDirectUrl = false
|
this.previewSrc = buildPreviewUrl(this.previewRow.id)
|
return
|
}
|
this.$message.error('视频加载失败,请重新下载该文件或联系管理员检查采集站转码配置')
|
},
|
closePreview () {
|
this.revokePreviewUrl()
|
this.previewSrc = ''
|
this.previewText = ''
|
this.previewMode = ''
|
this.previewRow = null
|
this.previewUseDirectUrl = false
|
},
|
revokePreviewUrl () {
|
if (this.previewBlobUrl) {
|
URL.revokeObjectURL(this.previewBlobUrl)
|
this.previewBlobUrl = ''
|
}
|
}
|
}
|
}
|
</script>
|
|
<style scoped>
|
.preview-wrap {
|
min-height: 200px;
|
}
|
.preview-video {
|
width: 100%;
|
max-height: 520px;
|
background: #000;
|
}
|
.preview-audio {
|
width: 100%;
|
}
|
.preview-image {
|
width: 100%;
|
max-height: 520px;
|
}
|
.preview-text {
|
max-height: 520px;
|
overflow: auto;
|
white-space: pre-wrap;
|
word-break: break-all;
|
margin: 0;
|
padding: 12px;
|
background: #f5f7fa;
|
border-radius: 4px;
|
}
|
.preview-empty {
|
text-align: center;
|
color: #909399;
|
padding: 40px 0;
|
}
|
</style>
|