添加视频列表
This commit is contained in:
parent
a8b6fb4b8b
commit
9f60742bcf
309
src/views/AIPatrol/videos/index.vue
Normal file
309
src/views/AIPatrol/videos/index.vue
Normal file
@ -0,0 +1,309 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Search, View, Download, Refresh, Calendar, Document } from '@element-plus/icons-vue'
|
||||
import { getVideoList } from '@/api/videos'
|
||||
|
||||
// 数据状态
|
||||
const videoList = ref([])
|
||||
const loading = ref(false)
|
||||
const searchText = ref('')
|
||||
const pagination = ref({
|
||||
current: 1,
|
||||
pageSize: 12, // 默认显示12个视频
|
||||
total: 0
|
||||
})
|
||||
|
||||
// 视频查看状态
|
||||
const videoModalVisible = ref(false)
|
||||
const selectedVideo = ref(null)
|
||||
|
||||
// 获取视频列表数据
|
||||
const fetchVideoList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await getVideoList()
|
||||
videoList.value = response.data
|
||||
} catch (error) {
|
||||
console.error('获取视频列表错误:', error)
|
||||
ElMessage.error('获取视频列表失败:' + error.message)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取视频URL
|
||||
const getVideoUrl = (video) => {
|
||||
const url = `${import.meta.env.VITE_API_BASE_URL}${video.videoUrl}`
|
||||
return url
|
||||
}
|
||||
|
||||
// 事件处理函数
|
||||
const onSearch = () => {
|
||||
pagination.value.current = 1
|
||||
fetchVideoList()
|
||||
}
|
||||
|
||||
const handleRefresh = () => {
|
||||
fetchVideoList()
|
||||
}
|
||||
|
||||
const handleSizeChange = (val) => {
|
||||
pagination.value.pageSize = val
|
||||
fetchVideoList()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
pagination.value.current = val
|
||||
fetchVideoList()
|
||||
}
|
||||
|
||||
const handleWatch = (record) => {
|
||||
selectedVideo.value = record
|
||||
videoModalVisible.value = true
|
||||
}
|
||||
|
||||
const handleDownload = (record) => {
|
||||
const link = document.createElement('a')
|
||||
link.href = getVideoUrl(record)
|
||||
link.download = record.fileName
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
ElMessage.success('下载成功')
|
||||
}
|
||||
|
||||
// 工具函数
|
||||
const formatFileSize = (bytes) => {
|
||||
if (bytes === 0) return '0 B'
|
||||
const k = 1024
|
||||
const sizes = ['B', 'KB', 'MB', 'GB']
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
||||
}
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
fetchVideoList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="video-list-container">
|
||||
<!-- 标题区域 -->
|
||||
<div class="page-header">
|
||||
<h2 class="page-title">AI巡检视频列表</h2>
|
||||
</div>
|
||||
|
||||
<!-- 搜索和过滤区域 -->
|
||||
<div class="table-operations">
|
||||
<el-space>
|
||||
<el-input
|
||||
v-model="searchText"
|
||||
placeholder="搜索视频名称"
|
||||
:prefix-icon="Search"
|
||||
style="width: 200px"
|
||||
@keyup.enter="onSearch"
|
||||
/>
|
||||
<el-button type="primary" @click="handleRefresh">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
刷新
|
||||
</el-button>
|
||||
</el-space>
|
||||
</div>
|
||||
|
||||
<!-- 视频卡片列表 -->
|
||||
<div v-loading="loading" class="video-grid">
|
||||
<div
|
||||
v-for="video in videoList"
|
||||
:key="video.id"
|
||||
class="video-card"
|
||||
>
|
||||
<!-- 视频预览 -->
|
||||
<div class="video-preview">
|
||||
<video
|
||||
:src="getVideoUrl(video)"
|
||||
controls
|
||||
preload="metadata"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 视频信息 -->
|
||||
<div class="video-info">
|
||||
<h3 class="video-title" :title="video.fileName">{{ video.fileName }}</h3>
|
||||
<div class="video-meta">
|
||||
<el-text class="video-time">
|
||||
<el-icon><Calendar /></el-icon>
|
||||
{{ video.createTime }}
|
||||
</el-text>
|
||||
<el-text class="video-size">
|
||||
<el-icon><Document /></el-icon>
|
||||
{{ formatFileSize(video.fileSize) }}
|
||||
</el-text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="video-actions">
|
||||
<el-button type="primary" @click="handleWatch(video)">
|
||||
<el-icon><View /></el-icon>
|
||||
查看
|
||||
</el-button>
|
||||
<el-button type="info" @click="handleDownload(video)">
|
||||
<el-icon><Download /></el-icon>
|
||||
下载
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
v-model:current-page="pagination.current"
|
||||
v-model:page-size="pagination.pageSize"
|
||||
:total="pagination.total"
|
||||
:page-sizes="[12, 24, 36, 48]"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
v-if="pagination.total > 0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 视频查看弹窗 -->
|
||||
<el-dialog
|
||||
v-model="videoModalVisible"
|
||||
title="视频查看"
|
||||
width="800px"
|
||||
destroy-on-close
|
||||
>
|
||||
<video
|
||||
v-if="selectedVideo"
|
||||
:src="getVideoUrl(selectedVideo)"
|
||||
style="width: 100%"
|
||||
controls
|
||||
autoplay
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.video-list-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.table-operations {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.video-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 16px;
|
||||
overflow-y: auto;
|
||||
padding-right: 8px; /* 为滚动条预留空间 */
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.video-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s;
|
||||
border: 1px solid #ebeef5;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.video-card:hover {
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.video-preview {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-top: 56.25%; /* 16:9 宽高比 */
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.video-preview video {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.video-info {
|
||||
padding: 12px 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.video-title {
|
||||
margin: 0 0 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.video-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.video-meta .el-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.video-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* 自定义滚动条样式 */
|
||||
.video-grid::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.video-grid::-webkit-scrollbar-thumb {
|
||||
background-color: #dcdfe6;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.video-grid::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user