502 lines
13 KiB
Vue
502 lines
13 KiB
Vue
<template>
|
||
<GiPageLayout>
|
||
<!-- 页面标题 -->
|
||
<div class="page-header">
|
||
<h2 class="page-title">数据入库 - 1号机组叶片A型检查</h2>
|
||
</div>
|
||
|
||
<!-- 选项卡区域 -->
|
||
<div class="tabs-section">
|
||
<a-tabs v-model:active-key="activeTab" type="line">
|
||
<a-tab-pane key="image" tab="图片" title="图片">
|
||
<div class="tab-content">
|
||
<!-- 文件上传区域 -->
|
||
<div class="upload-section">
|
||
<a-upload
|
||
:custom-request="customUpload"
|
||
:show-file-list="false"
|
||
multiple
|
||
:accept="getAcceptType()"
|
||
@change="handleFileChange"
|
||
drag
|
||
class="upload-dragger"
|
||
>
|
||
<div class="upload-content">
|
||
<div class="upload-icon">
|
||
<icon-upload :size="48" />
|
||
</div>
|
||
<div class="upload-text">
|
||
<p class="primary-text">将文件拖拽到此处,或<span class="link-text">点击上传</span></p>
|
||
<p class="secondary-text">支持上传任意(不超过100MB)格式文件</p>
|
||
</div>
|
||
</div>
|
||
</a-upload>
|
||
</div>
|
||
|
||
<!-- 操作按钮区域 -->
|
||
<div class="action-buttons">
|
||
<a-button
|
||
type="primary"
|
||
@click="startUpload"
|
||
:loading="uploading"
|
||
:disabled="uploadQueue.length === 0"
|
||
>
|
||
开始上传
|
||
</a-button>
|
||
<a-button @click="clearFiles">清空文件</a-button>
|
||
<a-button @click="batchImport">批量导入...</a-button>
|
||
</div>
|
||
|
||
<!-- 已上传数据列表 -->
|
||
<div class="uploaded-files-section">
|
||
<h3 class="section-title">已上传数据</h3>
|
||
<a-table
|
||
:columns="fileColumns"
|
||
:data="uploadedFiles"
|
||
:pagination="false"
|
||
:scroll="{ x: '100%' }"
|
||
>
|
||
<!-- 文件类型 -->
|
||
<template #type="{ record }">
|
||
<a-tag :color="getFileTypeColor(record.type)" size="small">
|
||
{{ record.type }}
|
||
</a-tag>
|
||
</template>
|
||
|
||
<!-- 文件大小 -->
|
||
<template #size="{ record }">
|
||
<span>{{ formatFileSize(record.size) }}</span>
|
||
</template>
|
||
|
||
<!-- 状态 -->
|
||
<template #status="{ record }">
|
||
<a-tag :color="getStatusColor(record.status)" size="small">
|
||
{{ record.status }}
|
||
</a-tag>
|
||
</template>
|
||
|
||
<!-- 操作 -->
|
||
<template #action="{ record }">
|
||
<a-space>
|
||
<a-button size="small" @click="previewFile(record)">预览</a-button>
|
||
<a-button size="small" status="danger" @click="deleteFile(record)">删除</a-button>
|
||
</a-space>
|
||
</template>
|
||
</a-table>
|
||
</div>
|
||
</div>
|
||
</a-tab-pane>
|
||
|
||
<a-tab-pane key="video" tab="视频" title="视频">
|
||
<div class="tab-content">
|
||
<a-empty description="视频上传功能开发中..." />
|
||
</div>
|
||
</a-tab-pane>
|
||
|
||
<a-tab-pane key="audio" tab="语音" title="语音">
|
||
<div class="tab-content">
|
||
<a-empty description="语音上传功能开发中..." />
|
||
</div>
|
||
</a-tab-pane>
|
||
|
||
<a-tab-pane key="document" tab="文档" title="文档">
|
||
<div class="tab-content">
|
||
<a-empty description="文档上传功能开发中..." />
|
||
</div>
|
||
</a-tab-pane>
|
||
|
||
<a-tab-pane key="other" tab="其他" title="其他">
|
||
<div class="tab-content">
|
||
<a-empty description="其他文件上传功能开发中..." />
|
||
</div>
|
||
</a-tab-pane>
|
||
</a-tabs>
|
||
</div>
|
||
|
||
<!-- 文件预览模态框 -->
|
||
<a-modal
|
||
v-model:visible="previewModalVisible"
|
||
title="文件预览"
|
||
:width="800"
|
||
:footer="false"
|
||
>
|
||
<div class="preview-container">
|
||
<div v-if="previewFileData && previewFileData.type === 'image'" class="image-preview">
|
||
<img :src="previewFileData.url" :alt="previewFileData.name" style="max-width: 100%; height: auto;" />
|
||
</div>
|
||
<div v-else-if="previewFileData && previewFileData.type === 'video'" class="video-preview">
|
||
<video :src="previewFileData.url" controls style="max-width: 100%; height: auto;" />
|
||
</div>
|
||
<div v-else class="file-info">
|
||
<p>文件名:{{ previewFileData?.name }}</p>
|
||
<p>文件类型:{{ previewFileData?.type }}</p>
|
||
<p>文件大小:{{ formatFileSize(previewFileData?.size) }}</p>
|
||
</div>
|
||
</div>
|
||
</a-modal>
|
||
</GiPageLayout>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, reactive, computed } from 'vue'
|
||
import { Message } from '@arco-design/web-vue'
|
||
import type { TableColumnData } from '@arco-design/web-vue'
|
||
import { IconUpload } from '@arco-design/web-vue/es/icon'
|
||
|
||
// 活动选项卡
|
||
const activeTab = ref('image')
|
||
|
||
// 上传状态
|
||
const uploading = ref(false)
|
||
const uploadQueue = ref<any[]>([])
|
||
|
||
// 预览模态框
|
||
const previewModalVisible = ref(false)
|
||
const previewFileData = ref<any>(null)
|
||
|
||
// 已上传文件列表
|
||
const uploadedFiles = ref([
|
||
{
|
||
id: 1,
|
||
name: 'IMG_20231105_1430.jpg',
|
||
type: 'image',
|
||
size: 3355443, // 3.2MB
|
||
uploadTime: '2023-11-05 14:32',
|
||
status: '成功',
|
||
url: '/api/files/IMG_20231105_1430.jpg'
|
||
},
|
||
{
|
||
id: 2,
|
||
name: 'VID_20231106_0915.mp4',
|
||
type: 'video',
|
||
size: 47185920, // 45.6MB
|
||
uploadTime: '2023-11-06 09:18',
|
||
status: '成功',
|
||
url: '/api/files/VID_20231106_0915.mp4'
|
||
},
|
||
{
|
||
id: 3,
|
||
name: 'IMG_20231107_1645.jpg',
|
||
type: 'image',
|
||
size: 2936013, // 2.8MB
|
||
uploadTime: '2023-11-07 16:48',
|
||
status: '成功',
|
||
url: '/api/files/IMG_20231107_1645.jpg'
|
||
}
|
||
])
|
||
|
||
// 文件表格列配置
|
||
const fileColumns: TableColumnData[] = [
|
||
{ title: '文件名', dataIndex: 'name', width: 250 },
|
||
{ title: '类型', dataIndex: 'type', slotName: 'type', width: 100 },
|
||
{ title: '大小', dataIndex: 'size', slotName: 'size', width: 100 },
|
||
{ title: '上传时间', dataIndex: 'uploadTime', width: 150 },
|
||
{ title: '状态', dataIndex: 'status', slotName: 'status', width: 100 },
|
||
{ title: '操作', slotName: 'action', width: 150, fixed: 'right' }
|
||
]
|
||
|
||
// 根据选项卡获取接受的文件类型
|
||
const getAcceptType = () => {
|
||
const typeMap: Record<string, string> = {
|
||
'image': 'image/*',
|
||
'video': 'video/*',
|
||
'audio': 'audio/*',
|
||
'document': '.pdf,.doc,.docx,.txt,.xls,.xlsx,.ppt,.pptx',
|
||
'other': '*'
|
||
}
|
||
return typeMap[activeTab.value] || '*'
|
||
}
|
||
|
||
// 获取文件类型颜色
|
||
const getFileTypeColor = (type: string) => {
|
||
const colorMap: Record<string, string> = {
|
||
'image': 'blue',
|
||
'video': 'green',
|
||
'audio': 'orange',
|
||
'document': 'purple',
|
||
'other': 'gray'
|
||
}
|
||
return colorMap[type] || 'gray'
|
||
}
|
||
|
||
// 获取状态颜色
|
||
const getStatusColor = (status: string) => {
|
||
const colorMap: Record<string, string> = {
|
||
'成功': 'green',
|
||
'失败': 'red',
|
||
'上传中': 'blue',
|
||
'等待': 'orange'
|
||
}
|
||
return colorMap[status] || 'gray'
|
||
}
|
||
|
||
// 格式化文件大小
|
||
const formatFileSize = (bytes: number) => {
|
||
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]
|
||
}
|
||
|
||
// 自定义上传请求
|
||
const customUpload = (option: any) => {
|
||
const { file } = option
|
||
|
||
// 检查文件大小
|
||
if (file.size > 100 * 1024 * 1024) {
|
||
Message.error('文件大小不能超过100MB')
|
||
return
|
||
}
|
||
|
||
// 添加到上传队列
|
||
uploadQueue.value.push({
|
||
file,
|
||
name: file.name,
|
||
size: file.size,
|
||
type: getFileType(file.name),
|
||
status: '等待'
|
||
})
|
||
|
||
Message.success(`文件 ${file.name} 已添加到上传队列`)
|
||
}
|
||
|
||
// 获取文件类型
|
||
const getFileType = (fileName: string) => {
|
||
const extension = fileName.split('.').pop()?.toLowerCase()
|
||
if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(extension || '')) {
|
||
return 'image'
|
||
} else if (['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv'].includes(extension || '')) {
|
||
return 'video'
|
||
} else if (['mp3', 'wav', 'ogg', 'flac', 'aac'].includes(extension || '')) {
|
||
return 'audio'
|
||
} else if (['pdf', 'doc', 'docx', 'txt', 'xls', 'xlsx', 'ppt', 'pptx'].includes(extension || '')) {
|
||
return 'document'
|
||
} else {
|
||
return 'other'
|
||
}
|
||
}
|
||
|
||
// 处理文件变化
|
||
const handleFileChange = (fileList: any[]) => {
|
||
// 文件选择处理逻辑
|
||
}
|
||
|
||
// 开始上传
|
||
const startUpload = async () => {
|
||
if (uploadQueue.value.length === 0) {
|
||
Message.warning('请先选择要上传的文件')
|
||
return
|
||
}
|
||
|
||
uploading.value = true
|
||
|
||
try {
|
||
// 模拟上传过程
|
||
for (let i = 0; i < uploadQueue.value.length; i++) {
|
||
const queueItem = uploadQueue.value[i]
|
||
queueItem.status = '上传中'
|
||
|
||
// 模拟上传延迟
|
||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||
|
||
// 添加到已上传列表
|
||
uploadedFiles.value.push({
|
||
id: Date.now() + i,
|
||
name: queueItem.name,
|
||
type: queueItem.type,
|
||
size: queueItem.size,
|
||
uploadTime: new Date().toLocaleString(),
|
||
status: '成功',
|
||
url: `/api/files/${queueItem.name}`
|
||
})
|
||
}
|
||
|
||
uploadQueue.value = []
|
||
Message.success('所有文件上传完成')
|
||
} catch (error) {
|
||
Message.error('上传失败,请重试')
|
||
} finally {
|
||
uploading.value = false
|
||
}
|
||
}
|
||
|
||
// 清空文件
|
||
const clearFiles = () => {
|
||
uploadQueue.value = []
|
||
Message.success('已清空文件队列')
|
||
}
|
||
|
||
// 批量导入
|
||
const batchImport = () => {
|
||
Message.info('批量导入功能开发中...')
|
||
}
|
||
|
||
// 预览文件
|
||
const previewFile = (file: any) => {
|
||
previewFileData.value = file
|
||
previewModalVisible.value = true
|
||
}
|
||
|
||
// 删除文件
|
||
const deleteFile = (file: any) => {
|
||
const index = uploadedFiles.value.findIndex(f => f.id === file.id)
|
||
if (index > -1) {
|
||
uploadedFiles.value.splice(index, 1)
|
||
Message.success('文件已删除')
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="less">
|
||
.data-storage-container {
|
||
padding: 16px;
|
||
background: #f5f5f5;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.page-header {
|
||
margin-bottom: 16px;
|
||
|
||
.page-title {
|
||
font-size: 20px;
|
||
font-weight: 500;
|
||
color: #262626;
|
||
margin: 0;
|
||
}
|
||
}
|
||
|
||
.tabs-section {
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
padding: 16px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.tab-content {
|
||
margin-top: 16px;
|
||
}
|
||
|
||
.upload-section {
|
||
margin-bottom: 16px;
|
||
|
||
.upload-dragger {
|
||
:deep(.arco-upload-drag) {
|
||
border: 2px dashed #d9d9d9;
|
||
border-radius: 8px;
|
||
background: #fafafa;
|
||
padding: 40px;
|
||
text-align: center;
|
||
transition: all 0.3s ease;
|
||
|
||
&:hover {
|
||
border-color: #1890ff;
|
||
background: #f0f7ff;
|
||
}
|
||
}
|
||
}
|
||
|
||
.upload-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 16px;
|
||
|
||
.upload-icon {
|
||
color: #bfbfbf;
|
||
font-size: 48px;
|
||
}
|
||
|
||
.upload-text {
|
||
.primary-text {
|
||
font-size: 16px;
|
||
color: #595959;
|
||
margin: 0 0 8px 0;
|
||
|
||
.link-text {
|
||
color: #1890ff;
|
||
cursor: pointer;
|
||
|
||
&:hover {
|
||
text-decoration: underline;
|
||
}
|
||
}
|
||
}
|
||
|
||
.secondary-text {
|
||
font-size: 14px;
|
||
color: #8c8c8c;
|
||
margin: 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.action-buttons {
|
||
display: flex;
|
||
gap: 12px;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.uploaded-files-section {
|
||
.section-title {
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
color: #262626;
|
||
margin: 0 0 16px 0;
|
||
}
|
||
}
|
||
|
||
.preview-container {
|
||
text-align: center;
|
||
|
||
.image-preview,
|
||
.video-preview {
|
||
max-height: 500px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.file-info {
|
||
text-align: left;
|
||
|
||
p {
|
||
margin: 8px 0;
|
||
font-size: 14px;
|
||
color: #595959;
|
||
}
|
||
}
|
||
}
|
||
|
||
:deep(.arco-tabs-nav) {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
:deep(.arco-tabs-tab) {
|
||
font-size: 14px;
|
||
padding: 8px 16px;
|
||
}
|
||
|
||
:deep(.arco-table-th) {
|
||
background-color: #fafafa;
|
||
color: #8c8c8c;
|
||
font-weight: 500;
|
||
}
|
||
|
||
:deep(.arco-table-td) {
|
||
padding: 12px 16px;
|
||
}
|
||
|
||
:deep(.arco-tag) {
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
:deep(.arco-btn-size-small) {
|
||
padding: 2px 8px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
:deep(.arco-upload-drag:hover) {
|
||
border-color: #1890ff;
|
||
}
|
||
</style> |