添加视频列表

This commit is contained in:
wzclm 2025-03-15 23:13:19 +08:00
parent a8b6fb4b8b
commit 9f60742bcf

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