495 lines
12 KiB
Vue
495 lines
12 KiB
Vue
<template>
|
||
<GiPageLayout>
|
||
<!-- 页面标题 -->
|
||
<!-- <div class="page-header">
|
||
<h2 class="page-title">图像音频关联查看</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="filter-section">
|
||
<a-space size="large">
|
||
<!-- 项目选择 -->
|
||
<div class="filter-item">
|
||
<span class="filter-label">项目:</span>
|
||
<a-select
|
||
v-model="filterParams.project"
|
||
placeholder="请选择项目"
|
||
:options="projectOptions"
|
||
allow-search
|
||
allow-clear
|
||
:loading="loading.project"
|
||
style="width: 200px"
|
||
@change="handleFilterChange"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 机组选择 -->
|
||
<div class="filter-item">
|
||
<span class="filter-label">机组:</span>
|
||
<a-select
|
||
v-model="filterParams.unit"
|
||
placeholder="请先选择项目"
|
||
:options="unitOptions"
|
||
allow-search
|
||
allow-clear
|
||
:disabled="!filterParams.project"
|
||
:loading="loading.unit"
|
||
style="width: 200px"
|
||
@change="handleFilterChange"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 部件选择 -->
|
||
<div class="filter-item">
|
||
<span class="filter-label">部件:</span>
|
||
<a-select
|
||
v-model="filterParams.component"
|
||
placeholder="请先选择机组"
|
||
:options="componentOptions"
|
||
allow-search
|
||
allow-clear
|
||
:disabled="!filterParams.unit"
|
||
:loading="loading.component"
|
||
style="width: 200px"
|
||
@change="handleFilterChange"
|
||
/>
|
||
</div>
|
||
</a-space>
|
||
</div>
|
||
|
||
<!-- 已上传数据列表 -->
|
||
<div class="uploaded-files-section">
|
||
<a-table
|
||
:columns="fileColumns"
|
||
:data="imageList"
|
||
:pagination="false"
|
||
:scroll="{ x: '100%', y: 'calc(100vh - 380px)' }"
|
||
:loading="loading.image"
|
||
class="scrollable-table"
|
||
>
|
||
<!-- 文件类型 -->
|
||
<template #type="{ record }">
|
||
<a-tag :color="getFileTypeColor(record.type)" size="small">
|
||
{{ record.type }}
|
||
</a-tag>
|
||
</template>
|
||
|
||
<!-- 文件大小 -->
|
||
<template #size="{ record }">
|
||
<span>{{ record.imageTypeLabel}}</span>
|
||
</template>
|
||
|
||
<!-- 状态 -->
|
||
<template #status="{ record }">
|
||
<a-tag :color="getStatusColor(record.preTreatment)" size="small">
|
||
{{ record.preTreatment }}
|
||
</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-tabs>
|
||
</div>
|
||
|
||
<!-- 文件预览模态框 -->
|
||
<PreviewModal ref="previewModal" />
|
||
</GiPageLayout>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, reactive, computed,onMounted } from 'vue'
|
||
import { Message } from '@arco-design/web-vue'
|
||
import type { TableColumnData } from '@arco-design/web-vue'
|
||
import PreviewModal from './components/PreviewModal.vue'
|
||
|
||
import {
|
||
getProjectList,
|
||
getTurbineList,
|
||
getPartList,
|
||
getImageList,
|
||
deleteImage,
|
||
autoAnnotateImage,
|
||
generateReport,
|
||
batchUploadImages,
|
||
uploadImageToPartV2
|
||
} from '@/apis/industrial-image'
|
||
|
||
const previewModal = ref()
|
||
// 活动选项卡
|
||
const activeTab = ref('image')
|
||
|
||
// 筛选参数
|
||
const filterParams = reactive({
|
||
project: null,
|
||
unit: null,
|
||
component: null
|
||
})
|
||
|
||
// 筛选选项
|
||
const projectOptions = ref<Array<{label: string, value: string}>>([])
|
||
const unitOptions = ref<Array<{label: string, value: string}>>([])
|
||
const componentOptions = ref<Array<{label: string, value: string}>>([])
|
||
|
||
// 图像列表
|
||
const imageList = ref<Array<{
|
||
id: number
|
||
name: string
|
||
type: string
|
||
imageTypeLabel: string
|
||
shootingTime: string
|
||
preTreatment: string
|
||
url: string
|
||
}>>([])
|
||
|
||
// 加载状态
|
||
const loading = reactive({
|
||
project: false,
|
||
unit: false,
|
||
component: false,
|
||
image: false
|
||
})
|
||
|
||
// 在组件挂载时获取项目列表
|
||
onMounted(() => {
|
||
fetchProjectList()
|
||
})
|
||
|
||
// 监听项目变化,获取机组列表
|
||
if (watch) {
|
||
watch(() => filterParams.project, (newVal) => {
|
||
// 清空已选的机组和部件
|
||
filterParams.unit = null
|
||
filterParams.component = null
|
||
unitOptions.value = []
|
||
componentOptions.value = []
|
||
imageList.value = []
|
||
|
||
if (newVal) {
|
||
fetchTurbineList(newVal)
|
||
}
|
||
})
|
||
}
|
||
|
||
// 监听机组变化,获取部件列表
|
||
if (watch) {
|
||
watch(() => filterParams.unit, (newVal) => {
|
||
// 清空已选的部件
|
||
filterParams.component = null
|
||
componentOptions.value = []
|
||
imageList.value = []
|
||
|
||
if (newVal && filterParams.project) {
|
||
fetchPartList(filterParams.project, newVal)
|
||
}
|
||
})
|
||
}
|
||
|
||
// 获取项目列表
|
||
const fetchProjectList = async () => {
|
||
loading.project = true
|
||
try {
|
||
const res = await getProjectList({ page: 1, pageSize: 1000 });
|
||
projectOptions.value = res.data.map(item => ({
|
||
label: item.projectName,
|
||
value: item.projectId
|
||
}))
|
||
} catch (error) {
|
||
Message.error('获取项目列表失败')
|
||
} finally {
|
||
loading.project = false
|
||
}
|
||
}
|
||
|
||
// 获取机组列表
|
||
const fetchTurbineList = async (projectId: string) => {
|
||
loading.unit = true
|
||
try {
|
||
const res = await getTurbineList({ projectId })
|
||
unitOptions.value = res.data.map(item => ({
|
||
label: item.turbineName,
|
||
value: item.turbineId
|
||
}))
|
||
} catch (error) {
|
||
console.error('获取机组列表失败:', error)
|
||
Message.error('获取机组列表失败')
|
||
} finally {
|
||
loading.unit = false
|
||
}
|
||
}
|
||
|
||
// 获取部件列表
|
||
const fetchPartList = async (projectId: string, turbineId: string) => {
|
||
loading.component = true
|
||
try {
|
||
const res = await getPartList({ projectId, turbineId })
|
||
componentOptions.value = res.data.map(item => ({
|
||
label: item.partName, // 根据实际接口调整
|
||
value: item.partId // 根据实际接口调整
|
||
}))
|
||
} catch (error) {
|
||
console.error('获取部件列表失败:', error)
|
||
Message.error('获取部件列表失败')
|
||
} finally {
|
||
loading.component = false
|
||
}
|
||
}
|
||
|
||
// 处理筛选变化,获取图像列表
|
||
const handleFilterChange = async () => {
|
||
// if (!filterParams.project) return
|
||
|
||
loading.image = true
|
||
try {
|
||
let params = {
|
||
projectId: filterParams.project
|
||
}
|
||
if(filterParams.unit){
|
||
params = {
|
||
projectId: filterParams.project,
|
||
turbineId: filterParams.unit
|
||
}
|
||
}
|
||
if(filterParams.component){
|
||
params = {
|
||
projectId: filterParams.project,
|
||
turbineId: filterParams.unit,
|
||
partId: filterParams.component
|
||
}
|
||
}
|
||
|
||
const res = await getImageList(params)
|
||
imageList.value = res.data.map((item: any) => ({
|
||
id: item.imageId,
|
||
name: item.imageName,
|
||
type: item.imageType?item.imageType:"未指定类型",
|
||
imageTypeLabel: item.imageTypeLabel,
|
||
shootingTime: item.shootingTime,
|
||
preTreatment: item.preTreatment?"已审核":"未审核",
|
||
imagePath: item.imagePath,
|
||
audioList:item.audioList
|
||
}))
|
||
Message.success(`获取到 ${imageList.value.length} 条图像数据`)
|
||
} catch (error) {
|
||
Message.error('获取图像列表失败')
|
||
} finally {
|
||
loading.image = false
|
||
}
|
||
}
|
||
|
||
// const response = await getTurbineList({ projectId: node.id })
|
||
// 上传状态
|
||
const uploading = ref(false)
|
||
const uploadQueue = ref<any[]>([])
|
||
|
||
// 预览模态框
|
||
const previewModalVisible = ref(false)
|
||
const previewFileData = ref<any>(null)
|
||
|
||
// 已上传文件列表
|
||
const uploadedFiles = ref([])
|
||
|
||
// 文件表格列配置
|
||
const fileColumns: TableColumnData[] = [
|
||
{ title: '文件名', dataIndex: 'name', width: 250 },
|
||
{ title: '类型', dataIndex: 'type', slotName: 'type', width: 100 },
|
||
{ title: '类型描述', dataIndex: 'imageTypeLabel', slotName: 'imageTypeLabel', width: 100 },
|
||
{ title: '上传时间', dataIndex: 'shootingTime', width: 150 },
|
||
{ title: '状态', dataIndex: 'preTreatment', slotName: 'preTreatment', width: 100 },
|
||
{ title: '操作', slotName: 'action', width: 150, fixed: 'right' }
|
||
]
|
||
|
||
// 过滤后的文件列表
|
||
const filteredFiles = computed(() => {
|
||
return uploadedFiles.value.filter(file =>
|
||
(filterParams.project === null || file.project === filterParams.project) &&
|
||
(filterParams.unit === null || file.unit === filterParams.unit) &&
|
||
(filterParams.component === null || file.component === filterParams.component)
|
||
)
|
||
})
|
||
|
||
// 获取文件类型颜色
|
||
const getFileTypeColor = (type: string) => {
|
||
const colorMap: Record<string, string> = {
|
||
'TYPICAL': 'blue',
|
||
'DEFECT': 'orange',
|
||
'other': 'gray'
|
||
}
|
||
return colorMap[type] || 'gray'
|
||
}
|
||
|
||
// 获取状态颜色
|
||
const getStatusColor = (status: string) => {
|
||
console.log(status);
|
||
const colorMap: Record<string, string> = {
|
||
'已审核': 'green',
|
||
'未审核': 'red'
|
||
}
|
||
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 getImageUrl = (imagePath: string): string => {
|
||
if (!imagePath) return ''
|
||
if (imagePath.startsWith('http')) return imagePath
|
||
const baseUrl = 'http://pms.dtyx.net:9158'
|
||
return `${baseUrl}${imagePath}`
|
||
}
|
||
|
||
// 预览文件
|
||
const previewFile = (file: any) => {
|
||
/* previewFileData.value = file
|
||
previewModalVisible.value = true*/
|
||
|
||
const fileObj ={
|
||
id: file.id,
|
||
name: file.name,
|
||
url: getImageUrl(file.imagePath),
|
||
audios: file.audioList
|
||
}
|
||
previewModal.value.openPreview(fileObj)
|
||
}
|
||
|
||
// 删除文件
|
||
const deleteFile = (file: any) => {
|
||
console.log(index);
|
||
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;
|
||
}
|
||
|
||
.filter-section {
|
||
margin-bottom: 16px;
|
||
padding: 12px;
|
||
background: #fafafa;
|
||
border-radius: 4px;
|
||
|
||
.filter-item {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
|
||
.filter-label {
|
||
font-size: 14px;
|
||
color: #595959;
|
||
margin-right: 8px;
|
||
}
|
||
}
|
||
}
|
||
|
||
.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>
|