完成原始数据回流后查看界面开发和功能开发,支持对所选图片的关联音频进行播放|修复典型图片设置不生效的问题
This commit is contained in:
parent
7728cfd8aa
commit
0108dd6068
|
@ -70,6 +70,6 @@ declare global {
|
|||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||
import('vue')
|
||||
}
|
||||
|
|
|
@ -183,12 +183,11 @@ const handleSave = async () => {
|
|||
console.log("requestData:",requestData);
|
||||
|
||||
// 调用接口更新数据
|
||||
const response = await axios.put(
|
||||
const response = await axios.post(
|
||||
`http://pms.dtyx.net:9158/image/setting-info/${editingData.value.partId}`,
|
||||
requestData
|
||||
)
|
||||
|
||||
if (response.data && response.data.code === 0) {
|
||||
if (response.data && response.data.code === 200 && response.data.success) {
|
||||
Message.success('图片信息保存成功')
|
||||
emit('save', editingData.value)
|
||||
emit('update:previewModalVisible', false)
|
||||
|
@ -240,4 +239,4 @@ const handleSave = async () => {
|
|||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,335 @@
|
|||
<template>
|
||||
<!-- 图片预览模态框 -->
|
||||
<a-modal
|
||||
v-model:visible="previewModalVisible"
|
||||
:width="1000"
|
||||
:footer="false"
|
||||
title="多媒体预览"
|
||||
class="preview-modal"
|
||||
@close="handleModalClose"
|
||||
>
|
||||
<div class="preview-container">
|
||||
<!-- 图片预览区域 -->
|
||||
<div class="image-section">
|
||||
<img
|
||||
:src="currentPreviewItem?.url"
|
||||
:alt="currentPreviewItem?.name"
|
||||
class="preview-image"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 音频列表区域 -->
|
||||
<div class="audio-section">
|
||||
<h3 class="audio-title">关联音频 ({{ audioList.length }})</h3>
|
||||
|
||||
<div class="audio-list">
|
||||
<div
|
||||
v-for="(audio, index) in audioList"
|
||||
:key="index"
|
||||
class="audio-item"
|
||||
:class="{ 'active': currentAudioIndex === index }"
|
||||
>
|
||||
<div class="audio-info">
|
||||
<span class="audio-name">{{ audio.audioId || `音频 ${index + 1}` }}</span>
|
||||
<span class="audio-duration">{{ formatDuration(audio.duration) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="audio-controls">
|
||||
<a-button
|
||||
size="mini"
|
||||
@click="toggleAudio(index)"
|
||||
:loading="audioLoading && currentAudioIndex === index"
|
||||
>
|
||||
{{ currentAudioIndex === index && isPlaying ? '暂停' : '播放' }}
|
||||
</a-button>
|
||||
|
||||
<a-progress
|
||||
v-if="currentAudioIndex === index"
|
||||
:percent="playProgress"
|
||||
:show-text="false"
|
||||
size="small"
|
||||
class="progress-bar"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 隐藏的audio元素 -->
|
||||
<audio
|
||||
ref="audioPlayers"
|
||||
:src="audio.url"
|
||||
@timeupdate="handleTimeUpdate"
|
||||
@ended="handleAudioEnded"
|
||||
preload="metadata"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, nextTick } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
|
||||
interface AudioItem {
|
||||
url: string
|
||||
name?: string
|
||||
duration?: number // 单位秒
|
||||
}
|
||||
|
||||
interface PreviewItem {
|
||||
url: string
|
||||
name: string
|
||||
audios?: AudioItem[]
|
||||
}
|
||||
|
||||
// 模态框显示状态
|
||||
const previewModalVisible = ref(false)
|
||||
const currentPreviewItem = ref<PreviewItem | null>(null)
|
||||
|
||||
// 音频相关状态
|
||||
const audioList = ref<AudioItem[]>([])
|
||||
const currentAudioIndex = ref(-1)
|
||||
const isPlaying = ref(false)
|
||||
const audioLoading = ref(false)
|
||||
const playProgress = ref(0)
|
||||
const audioPlayers = ref<HTMLAudioElement[]>([])
|
||||
|
||||
const getAudioUrl = (filePath: string): string => {
|
||||
if (!filePath) return ''
|
||||
if (filePath.startsWith('http')) return filePath
|
||||
const baseUrl = 'http://pms.dtyx.net:9158'
|
||||
return `${baseUrl}${filePath}`
|
||||
}
|
||||
|
||||
// 打开预览模态框
|
||||
const openPreview = (item: PreviewItem) => {
|
||||
currentPreviewItem.value = item
|
||||
audioList.value = []
|
||||
for (const audio of item.audios) {
|
||||
let temp={
|
||||
audioId:audio.audioId,
|
||||
url:getAudioUrl(audio.filePath)
|
||||
}
|
||||
audioList.value.push(temp)
|
||||
}
|
||||
|
||||
previewModalVisible.value = true
|
||||
resetAudioState()
|
||||
|
||||
// 预加载音频时长
|
||||
nextTick(() => {
|
||||
loadAudioDurations()
|
||||
})
|
||||
}
|
||||
|
||||
// 关闭模态框
|
||||
const handleModalClose = () => {
|
||||
stopAudio()
|
||||
previewModalVisible.value = false
|
||||
}
|
||||
|
||||
// 重置音频状态
|
||||
const resetAudioState = () => {
|
||||
currentAudioIndex.value = -1
|
||||
isPlaying.value = false
|
||||
playProgress.value = 0
|
||||
}
|
||||
|
||||
// 加载音频时长信息
|
||||
const loadAudioDurations = () => {
|
||||
audioPlayers.value.forEach((player, index) => {
|
||||
player.onloadedmetadata = () => {
|
||||
if (!audioList.value[index].duration) {
|
||||
audioList.value[index].duration = player.duration
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 切换音频播放状态
|
||||
const toggleAudio = (index: number) => {
|
||||
if (currentAudioIndex.value === index) {
|
||||
// 同一音频,切换播放/暂停
|
||||
if (isPlaying.value) {
|
||||
pauseAudio()
|
||||
} else {
|
||||
playAudio(index)
|
||||
}
|
||||
} else {
|
||||
// 切换不同音频
|
||||
stopAudio()
|
||||
playAudio(index)
|
||||
}
|
||||
}
|
||||
|
||||
// 播放音频
|
||||
const playAudio = (index: number) => {
|
||||
audioLoading.value = true
|
||||
currentAudioIndex.value = index
|
||||
|
||||
const player = audioPlayers.value[index]
|
||||
player.play()
|
||||
.then(() => {
|
||||
isPlaying.value = true
|
||||
audioLoading.value = false
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('播放失败:', err)
|
||||
Message.error('音频播放失败')
|
||||
audioLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 暂停音频
|
||||
const pauseAudio = () => {
|
||||
const player = audioPlayers.value[currentAudioIndex.value]
|
||||
player.pause()
|
||||
isPlaying.value = false
|
||||
}
|
||||
|
||||
// 停止音频
|
||||
const stopAudio = () => {
|
||||
if (currentAudioIndex.value !== -1) {
|
||||
const player = audioPlayers.value[currentAudioIndex.value]
|
||||
player.pause()
|
||||
player.currentTime = 0
|
||||
}
|
||||
isPlaying.value = false
|
||||
currentAudioIndex.value = -1
|
||||
playProgress.value = 0
|
||||
}
|
||||
|
||||
// 音频时间更新
|
||||
const handleTimeUpdate = (e: Event) => {
|
||||
const player = e.target as HTMLAudioElement
|
||||
if (player.duration) {
|
||||
playProgress.value = (player.currentTime / player.duration) * 100
|
||||
}
|
||||
}
|
||||
|
||||
// 音频播放结束
|
||||
const handleAudioEnded = () => {
|
||||
isPlaying.value = false
|
||||
playProgress.value = 100
|
||||
}
|
||||
|
||||
// 格式化时长显示
|
||||
const formatDuration = (seconds?: number) => {
|
||||
if (!seconds) return '--:--'
|
||||
const mins = Math.floor(seconds / 60)
|
||||
const secs = Math.floor(seconds % 60)
|
||||
return `${mins}:${secs < 10 ? '0' : ''}${secs}`
|
||||
}
|
||||
|
||||
// 暴露方法供父组件调用
|
||||
defineExpose({
|
||||
openPreview
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.preview-modal {
|
||||
:deep(.arco-modal-content) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
display: flex;
|
||||
height: 70vh;
|
||||
max-height: 800px;
|
||||
}
|
||||
|
||||
.image-section {
|
||||
flex: 1;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #f5f5f5;
|
||||
overflow: hidden;
|
||||
|
||||
.preview-image {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.audio-section {
|
||||
width: 350px;
|
||||
border-left: 1px solid #e5e5e5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.audio-title {
|
||||
padding: 16px;
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.audio-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.audio-item {
|
||||
padding: 12px 16px;
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&.active {
|
||||
background-color: #f0f7ff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
|
||||
.audio-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.audio-name {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.audio-duration {
|
||||
color: #8c8c8c;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.audio-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
.progress-bar {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.preview-container {
|
||||
flex-direction: column;
|
||||
height: 80vh;
|
||||
}
|
||||
|
||||
.image-section {
|
||||
height: 60%;
|
||||
}
|
||||
|
||||
.audio-section {
|
||||
width: 100%;
|
||||
height: 40%;
|
||||
border-left: none;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,151 +1,290 @@
|
|||
<template>
|
||||
<GiPageLayout>
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-header">
|
||||
<h2 class="page-title">数据入库 - 1号机组叶片A型检查</h2>
|
||||
</div>
|
||||
<!-- 页面标题 -->
|
||||
<!-- <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="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="tabs-section">
|
||||
<a-tabs v-model:active-key="activeTab" type="line">
|
||||
<a-tab-pane key="image" tab="列表" title="列表">
|
||||
<div class="tab-content">
|
||||
|
||||
<!-- 操作按钮区域 -->
|
||||
<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="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="uploaded-files-section">
|
||||
<h3 class="section-title">已上传数据</h3>
|
||||
<a-table
|
||||
<!-- 机组选择 -->
|
||||
<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="uploadedFiles"
|
||||
:data="imageList"
|
||||
:pagination="false"
|
||||
:scroll="{ x: '100%' }"
|
||||
>
|
||||
<!-- 文件类型 -->
|
||||
<template #type="{ record }">
|
||||
<a-tag :color="getFileTypeColor(record.type)" size="small">
|
||||
{{ record.type }}
|
||||
</a-tag>
|
||||
</template>
|
||||
: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>{{ formatFileSize(record.size) }}</span>
|
||||
</template>
|
||||
<!-- 文件大小 -->
|
||||
<template #size="{ record }">
|
||||
<span>{{ record.imageTypeLabel}}</span>
|
||||
</template>
|
||||
|
||||
<!-- 状态 -->
|
||||
<template #status="{ record }">
|
||||
<a-tag :color="getStatusColor(record.status)" size="small">
|
||||
{{ record.status }}
|
||||
</a-tag>
|
||||
</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>
|
||||
<!-- 操作 -->
|
||||
<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>
|
||||
</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>
|
||||
</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>
|
||||
<PreviewModal ref="previewModal" />
|
||||
</GiPageLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import { ref, reactive, computed,onMounted } 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'
|
||||
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.unit) return
|
||||
|
||||
loading.image = true
|
||||
try {
|
||||
let params = {
|
||||
turbineId: filterParams.unit
|
||||
}
|
||||
if(filterParams.component){
|
||||
params = {
|
||||
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[]>([])
|
||||
|
@ -155,65 +294,32 @@ 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 uploadedFiles = ref([])
|
||||
|
||||
// 文件表格列配置
|
||||
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: '类型描述', 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 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 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> = {
|
||||
'image': 'blue',
|
||||
'video': 'green',
|
||||
'audio': 'orange',
|
||||
'document': 'purple',
|
||||
'TYPICAL': 'blue',
|
||||
'DEFECT': 'orange',
|
||||
'other': 'gray'
|
||||
}
|
||||
return colorMap[type] || 'gray'
|
||||
|
@ -221,11 +327,10 @@ const getFileTypeColor = (type: string) => {
|
|||
|
||||
// 获取状态颜色
|
||||
const getStatusColor = (status: string) => {
|
||||
console.log(status);
|
||||
const colorMap: Record<string, string> = {
|
||||
'成功': 'green',
|
||||
'失败': 'red',
|
||||
'上传中': 'blue',
|
||||
'等待': 'orange'
|
||||
'已审核': 'green',
|
||||
'未审核': 'red'
|
||||
}
|
||||
return colorMap[status] || 'gray'
|
||||
}
|
||||
|
@ -239,107 +344,30 @@ const formatFileSize = (bytes: number) => {
|
|||
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 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
|
||||
/* 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)
|
||||
|
@ -357,7 +385,7 @@ const deleteFile = (file: any) => {
|
|||
|
||||
.page-header {
|
||||
margin-bottom: 16px;
|
||||
|
||||
|
||||
.page-title {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
|
@ -377,65 +405,22 @@ const deleteFile = (file: any) => {
|
|||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.upload-section {
|
||||
.filter-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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
padding: 12px;
|
||||
background: #fafafa;
|
||||
border-radius: 4px;
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-bottom: 24px;
|
||||
.filter-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
.filter-label {
|
||||
font-size: 14px;
|
||||
color: #595959;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.uploaded-files-section {
|
||||
|
@ -449,16 +434,16 @@ const deleteFile = (file: any) => {
|
|||
|
||||
.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;
|
||||
|
@ -499,4 +484,4 @@ const deleteFile = (file: any) => {
|
|||
:deep(.arco-upload-drag:hover) {
|
||||
border-color: #1890ff;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue