<template>
|
<basic-container>
|
<avue-crud :option="option" :table-loading="loading" :data="data" v-model:page="page" :permission="permissionList"
|
v-model="form" ref="crud" @row-del="rowDel" @search-change="searchChange" @search-reset="searchReset"
|
@selection-change="selectionChange" @current-change="currentChange" @size-change="sizeChange"
|
@refresh-change="refreshChange" @on-load="onLoad">
|
<template #menu-left>
|
<el-button type="primary" icon="el-icon-download" plain :loading="isDownloading" @click="handleZipDownload">{{
|
isDownloading ? '压缩下载中...' : '压缩下载' }}</el-button>
|
</template>
|
<template #menu="scope">
|
<el-button type="primary" text icon="el-icon-view" v-if="permission.attach_delete"
|
@click="handleRowView(scope.row)">查看
|
</el-button>
|
<el-button type="success" text icon="el-icon-download" v-if="permission.attach_delete"
|
@click="handleRowDownload(scope.row)">下载
|
</el-button>
|
<el-button type="danger" text icon="el-icon-delete" v-if="permission.attach_delete"
|
@click="handleRowDelete(scope.row)">删除
|
</el-button>
|
</template>
|
</avue-crud>
|
<el-dialog title="媒体文件" append-to-body v-model="taskBox" width="555px">
|
<avue-form ref="form" :option="taskOption" v-model="taskForm" :upload-after="uploadAfter">
|
</avue-form>
|
</el-dialog>
|
</basic-container>
|
</template>
|
|
<script>
|
import JSZip from 'jszip'
|
import { getMedia, deleteAllFile } from '@/api/resource/media'
|
import { getAirportList } from '@/api/device/device'
|
import { mapGetters } from 'vuex'
|
import func from '@/utils/func'
|
|
export default {
|
data () {
|
const endDate = new Date()
|
const startDate = new Date()
|
startDate.setMonth(startDate.getMonth() - 1)
|
const defaultStartDate = `${startDate.getFullYear()}-${String(startDate.getMonth() + 1).padStart(2, '0')}-${String(startDate.getDate()).padStart(2, '0')}`
|
const defaultEndDate = `${endDate.getFullYear()}-${String(endDate.getMonth() + 1).padStart(2, '0')}-${String(endDate.getDate()).padStart(2, '0')}`
|
|
return {
|
form: {},
|
query: {
|
daterange: [defaultStartDate, defaultEndDate]
|
},
|
loading: true,
|
page: {
|
pageSize: 10,
|
currentPage: 1,
|
total: 0,
|
},
|
taskBox: false,
|
selectionList: [],
|
selectedWorkspaceId: null,
|
option: {
|
tip: false,
|
searchShow: true,
|
searchMenuSpan: 6,
|
border: true,
|
index: true,
|
viewBtn: true,
|
selection: true,
|
dialogClickModal: false,
|
delBtn: false,
|
|
height: 'auto',
|
calcHeight: 20,
|
column: [
|
{ label: '机场选择', prop: 'airportNickname', type: 'select', search: true, searchSpan: 5, dicData: [], props: { label: 'nickname', value: 'nickname' }, hide: true, change: (value) => {
|
this.searchChange({ ...this.query,airportNickname:value.value}, () => {});
|
} },
|
{ label: '创建日期', prop: 'daterange', type: 'daterange', search: true, searchRange: true, searchSpan: 6, format: 'YYYY-MM-DD', valueFormat: 'YYYY-MM-DD', startPlaceholder: '开始时间', endPlaceholder: '结束时间', viewDisplay: false, hide: true, searchValue: [defaultStartDate, defaultEndDate]
|
, change: (value) => {
|
this.searchChange({ ...this.query,daterange:value.value}, () => {});
|
}
|
},
|
{ label: '任务名称', prop: 'job_name' },
|
{ label: '文件名称', prop: 'file_name' },
|
{ label: '拍摄时间', prop: 'create_time' },
|
{ label: '文件路径', prop: 'object_key' },
|
{ label: '设备负载', prop: 'payload' },
|
],
|
},
|
data: [],
|
taskForm: {},
|
taskOption: {
|
submitBtn: false,
|
emptyBtn: false,
|
column: [
|
{ label: '任务上传', prop: 'taskFile', type: 'upload', drag: true, loadText: '任务上传中,请稍等', span: 24, propsHttp: { res: 'data' }, action: '/blade-resource/oss/endpoint/put-file-task' },
|
],
|
},
|
domainUrl: '',
|
isDownloading: false, // 控制按钮加载状态
|
}
|
},
|
computed: {
|
func () { return func },
|
...mapGetters(['permission']),
|
permissionList () {
|
return {
|
addBtn: false,
|
editBtn: false,
|
viewBtn: this.validData(this.permission.attach_view, false),
|
delBtn: this.validData(this.permission.attach_delete, false),
|
}
|
},
|
ids () { return this.selectionList.map(ele => ele.id).join(',') },
|
},
|
mounted () {
|
this.loadAirportList()
|
this.$refs.crud.setSearch({ daterange: this.query.daterange })
|
this.onLoad(this.page, this.query)
|
},
|
methods: {
|
loadAirportList () {
|
getAirportList().then(res => {
|
const airportData = res.data.data || []
|
const airportColumn = this.option.column.find(col => col.prop === 'airportNickname')
|
if (airportColumn) {
|
airportColumn.dicData = airportData.map(item => ({
|
nickname: item.nickname,
|
workspace_id: item.workspace_id,
|
}))
|
}
|
}).catch(error => {
|
console.error('加载机场列表时出错:', error)
|
})
|
},
|
handleUpload () {
|
this.taskBox = true
|
},
|
uploadAfter (res, done) {
|
this.taskBox = false
|
this.refreshChange()
|
done()
|
},
|
async handleRowDownload (row) {
|
const mediaUrl = this.domainUrl + row.object_key
|
try {
|
const response = await fetch(mediaUrl)
|
if (!response.ok) throw new Error('文件下载失败')
|
const blob = await response.blob()
|
const url = window.URL.createObjectURL(blob)
|
const link = document.createElement('a')
|
link.href = url
|
link.download = row.file_name || 'download'
|
document.body.appendChild(link)
|
link.click()
|
document.body.removeChild(link)
|
window.URL.revokeObjectURL(url)
|
this.$message({ type: 'success', message: '文件下载成功: ' + row.file_name })
|
} catch (error) {
|
console.error('下载文件时出错:', error)
|
this.$message({ type: 'error', message: '下载失败: ' + error.message })
|
}
|
},
|
handleRowDelete (row) {
|
this.$confirm('确定删除此文件?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
|
.then(() => deleteAllFile(row.id))
|
.then(() => {
|
this.onLoad(this.page)
|
this.$message({ type: 'success', message: '删除成功!' })
|
})
|
.catch(error => {
|
console.error('删除文件时出错:', error)
|
this.$message({ type: 'error', message: '删除失败,请稍后重试!' })
|
})
|
},
|
rowDel (row) {
|
this.$confirm('确定将选择数据及对应的文件删除?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
|
.then(() => deleteAllFile(row.id))
|
.then(() => {
|
this.onLoad(this.page)
|
this.$message({ type: 'success', message: '操作成功!' })
|
})
|
},
|
searchReset () {
|
const endDate = new Date()
|
const startDate = new Date()
|
startDate.setMonth(startDate.getMonth() - 1)
|
const defaultStartDate = `${startDate.getFullYear()}-${String(startDate.getMonth() + 1).padStart(2, '0')}-${String(startDate.getDate()).padStart(2, '0')}`
|
const defaultEndDate = `${endDate.getFullYear()}-${String(endDate.getMonth() + 1).padStart(2, '0')}-${String(endDate.getDate()).padStart(2, '0')}`
|
this.query = { daterange: [defaultStartDate, defaultEndDate] }
|
this.selectedWorkspaceId = null
|
this.$refs.crud.setSearch({ daterange: this.query.daterange })
|
this.onLoad(this.page, this.query)
|
},
|
searchChange (params, done) {
|
this.query = params
|
this.page.currentPage = 1
|
const airportColumn = this.option.column.find(col => col.prop === 'airportNickname')
|
const selectedAirport = airportColumn.dicData.find(item => item.nickname === params.airportNickname)
|
this.selectedWorkspaceId = selectedAirport ? selectedAirport.workspace_id : null
|
this.onLoad(this.page, params)
|
done()
|
},
|
selectionChange (list) {
|
this.selectionList = list
|
},
|
selectionClear () {
|
this.selectionList = []
|
this.$refs.crud.toggleSelection()
|
},
|
currentChange (currentPage) {
|
this.page.currentPage = currentPage
|
this.onLoad(this.page, this.query)
|
},
|
sizeChange (pageSize) {
|
this.page.pageSize = pageSize
|
this.onLoad(this.page, this.query)
|
},
|
refreshChange () {
|
this.onLoad(this.page, this.query)
|
},
|
onLoad (page, params = {}) {
|
this.loading = true
|
const { daterange } = params
|
let create_time = null
|
let end_time = null
|
|
if (daterange && Array.isArray(daterange) && daterange.length === 2) {
|
const startStr = daterange[0]
|
const endStr = daterange[1]
|
const startDate = new Date(startStr + ' 00:00:00').getTime()
|
const endDate = new Date(endStr + ' 23:59:59').getTime()
|
if (!isNaN(startDate) && !isNaN(endDate)) {
|
create_time = startDate
|
end_time = endDate
|
}
|
}
|
|
let values = { ...params, ...this.query }
|
if (daterange) values.daterange = null
|
|
getMedia(page.currentPage, page.pageSize, this.selectedWorkspaceId || null, create_time, end_time)
|
.then(res => {
|
const data = res.data
|
if (data && data.data && data.data.list) {
|
this.page.total = data.data.pagination.total
|
this.data = data.data.list.map(item => ({
|
job_name: item.job_name,
|
file_name: item.file_name,
|
create_time: this.formatCreateTime(item.create_time),
|
object_key: item.object_key,
|
payload: item.payload,
|
airportNickname: item.airportNickname,
|
id: item.id,
|
}))
|
this.domainUrl = data.data.list.length > 0 ? data.data.list[0].domain_url : 'http://localhost:2888/manage/resource'
|
} else {
|
this.data = []
|
}
|
this.loading = false
|
this.selectionClear()
|
this.$refs.crud.refreshTable()
|
})
|
.catch(error => {
|
this.loading = false
|
console.error('加载媒体数据时出错:', error)
|
})
|
},
|
formatCreateTime (createTime) {
|
if (typeof createTime !== 'number' || isNaN(createTime)) return ''
|
const timestamp = createTime < 10000000000 ? createTime * 1000 : createTime
|
const date = new Date(timestamp)
|
if (isNaN(date.getTime())) return ''
|
const year = date.getFullYear()
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
const day = String(date.getDate()).padStart(2, '0')
|
const hours = String(date.getHours()).padStart(2, '0')
|
const minutes = String(date.getMinutes()).padStart(2, '0')
|
const seconds = String(date.getSeconds()).padStart(2, '0')
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
},
|
handleRowView (row) {
|
const mediaUrl = this.domainUrl + row.object_key
|
const mediaType = this.getMediaType(row.object_key)
|
if (mediaType === 'image' || mediaType === 'video') {
|
window.open(mediaUrl, '_blank')
|
}
|
},
|
getMediaType (objectKey) {
|
const extension = objectKey.split('.').pop().toLowerCase()
|
if (['jpg', 'jpeg', 'png', 'gif'].includes(extension)) return 'image'
|
if (['mp4', 'avi', 'mov'].includes(extension)) return 'video'
|
return 'unknown'
|
},
|
showVideo (url) {
|
this.taskBox = true
|
this.taskForm = { videoUrl: url }
|
this.taskOption = {
|
submitBtn: false,
|
emptyBtn: false,
|
column: [
|
{ label: '视频播放', prop: 'videoUrl', type: 'video', span: 24, props: { controls: true, autoplay: false } },
|
],
|
}
|
},
|
validData (val, def) {
|
return typeof val !== 'undefined' ? val : def
|
},
|
async handleZipDownload () {
|
if (this.selectionList.length === 0) {
|
this.$message({ type: 'warning', message: '请先选择需要下载的文件' })
|
return
|
}
|
|
this.isDownloading = true
|
|
try {
|
const zip = new JSZip()
|
const files = this.selectionList.map(item => ({
|
url: this.domainUrl + item.object_key,
|
name: item.file_name
|
}))
|
|
const fetchPromises = files.map(async file => {
|
const response = await fetch(file.url, { cache: 'force-cache' })
|
if (!response.ok) throw new Error(`下载文件 ${file.name} 失败`)
|
const blob = await response.blob()
|
zip.file(file.name, blob)
|
})
|
|
await Promise.all(fetchPromises)
|
|
const content = await zip.generateAsync({
|
type: 'blob',
|
compression: 'DEFLATE',
|
compressionOptions: { level: 1 }
|
})
|
|
const url = window.URL.createObjectURL(content)
|
const link = document.createElement('a')
|
link.href = url
|
link.download = `media_files_${new Date().toISOString().slice(0, 10)}_${new Date().getTime()}.zip`
|
document.body.appendChild(link)
|
link.click()
|
document.body.removeChild(link)
|
window.URL.revokeObjectURL(url)
|
|
this.$message({ type: 'success', message: `成功下载 ${this.selectionList.length} 个文件` })
|
} catch (error) {
|
console.error('压缩下载失败:', error)
|
this.$message({ type: 'error', message: '压缩下载失败: ' + error.message })
|
} finally {
|
this.isDownloading = false
|
}
|
},
|
},
|
}
|
</script>
|
|
<style scoped lang="scss"></style>
|