| | |
| | | 编辑 |
| | | </u-button> |
| | | <u-button |
| | | type="success" |
| | | size="small" |
| | | class="action-btn" |
| | | @click="viewAttachments(item)" |
| | | > |
| | | 查看附件 |
| | | </u-button> |
| | | <u-button |
| | | type="warning" |
| | | size="small" |
| | | class="action-btn" |
| | |
| | | <view v-else class="no-data"> |
| | | <text>暂无设备报修数据</text> |
| | | </view> |
| | | <!-- 查看附件弹窗 --> |
| | | <view v-if="showAttachmentDialog" class="custom-modal-overlay" @click="closeAttachmentDialog"> |
| | | <view class="custom-modal-container" @click.stop> |
| | | <view class="attachment-popup-content"> |
| | | <view class="attachment-popup-header"> |
| | | <text class="attachment-popup-title">查看附件 - {{ currentViewRepair?.deviceName || '报修' }}</text> |
| | | <view class="close-btn-attachment" @click="closeAttachmentDialog"> |
| | | <u-icon name="close" size="16" color="#666"></u-icon> |
| | | </view> |
| | | </view> |
| | | <view class="attachment-popup-body"> |
| | | <view class="attachment-content"> |
| | | <view v-if="attachmentList.length > 0" class="attachment-list"> |
| | | <view |
| | | v-for="(file, index) in attachmentList" |
| | | :key="index" |
| | | class="attachment-item" |
| | | @click="previewAttachment(file)" |
| | | > |
| | | <view class="attachment-preview-container"> |
| | | <image |
| | | v-if="isImageFile(file)" |
| | | :src="formatFileUrl(file.url || file.downloadUrl)" |
| | | class="attachment-preview" |
| | | mode="aspectFill" |
| | | /> |
| | | <view v-else class="attachment-video-preview"> |
| | | <u-icon name="video" size="24" color="#409eff"></u-icon> |
| | | <text class="video-text">视频</text> |
| | | </view> |
| | | </view> |
| | | <view class="attachment-info"> |
| | | <text class="attachment-name">{{ file.originalFilename || file.bucketFilename || file.name || '附件' }}</text> |
| | | <text class="attachment-size">{{ formatFileSize(file.byteSize || file.size) }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-else class="attachment-empty"> |
| | | <text>暂无附件</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- 视频预览弹窗 --> |
| | | <view v-if="showVideoDialog" class="video-modal-overlay" @click="closeVideoPreview"> |
| | | <view class="video-modal-container" @click.stop> |
| | | <view class="video-modal-header"> |
| | | <text class="video-modal-title">{{ currentVideoFile?.originalFilename || currentVideoFile?.bucketFilename || '视频预览' }}</text> |
| | | <view class="close-btn-video" @click="closeVideoPreview"> |
| | | <u-icon name="close" size="16" color="#fff"></u-icon> |
| | | </view> |
| | | </view> |
| | | <view class="video-modal-body"> |
| | | <video |
| | | v-if="currentVideoFile" |
| | | :src="currentVideoFile.url || currentVideoFile.downloadUrl" |
| | | class="video-player" |
| | | controls |
| | | autoplay |
| | | @error="handleVideoError" |
| | | ></video> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- 浮动操作按钮 --> |
| | | <view class="fab-button" @click="addRepair"> |
| | | <up-icon name="plus" size="24" color="#ffffff"></up-icon> |
| | |
| | | import { ref, onMounted } from 'vue' |
| | | import { onShow } from '@dcloudio/uni-app' |
| | | import PageHeader from '@/components/PageHeader.vue' |
| | | import { getRepairPage, delRepair } from '@/api/equipmentManagement/repair' |
| | | import { getRepairPage, delRepair, getRepairById } from '@/api/equipmentManagement/repair' |
| | | import config from '@/config' |
| | | import useUserStore from "@/store/modules/user" |
| | | |
| | | const showToast = (message) => { |
| | |
| | | |
| | | // 设备报修数据 |
| | | const repairList = ref([]) |
| | | |
| | | // 查看附件 |
| | | const showAttachmentDialog = ref(false) |
| | | const attachmentList = ref([]) |
| | | const currentViewRepair = ref(null) |
| | | const showVideoDialog = ref(false) |
| | | const currentVideoFile = ref(null) |
| | | |
| | | const filePreviewBase = config.fileUrl || config.baseUrl || '' |
| | | |
| | | const formatFileUrl = (url) => { |
| | | if (!url) return '' |
| | | if (url.startsWith('http://') || url.startsWith('https://')) return url |
| | | const base = (filePreviewBase || '').replace(/\/$/, '') |
| | | const path = (url || '').replace(/^\/+/, '') |
| | | return path ? `${base}/${path}` : base |
| | | } |
| | | |
| | | const isImageFile = (file) => { |
| | | if (file?.contentType && file.contentType.startsWith('image/')) return true |
| | | if (file?.type === 'image') return true |
| | | const name = file?.bucketFilename || file?.originalFilename || file?.name || '' |
| | | const ext = name.split('.').pop()?.toLowerCase() |
| | | return ['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext) |
| | | } |
| | | |
| | | const formatFileSize = (size) => { |
| | | if (!size) return '' |
| | | if (size < 1024) return size + 'B' |
| | | if (size < 1024 * 1024) return (size / 1024).toFixed(1) + 'KB' |
| | | return (size / (1024 * 1024)).toFixed(1) + 'MB' |
| | | } |
| | | |
| | | const viewAttachments = async (item) => { |
| | | try { |
| | | currentViewRepair.value = item |
| | | const { code, data } = await getRepairById(item.id) |
| | | if (code === 200 && data) { |
| | | attachmentList.value = Array.isArray(data.fileList) ? data.fileList : (Array.isArray(data.commonFileList) ? data.commonFileList : []) |
| | | showAttachmentDialog.value = true |
| | | } else { |
| | | showToast('获取附件失败') |
| | | } |
| | | } catch (e) { |
| | | showToast('获取附件失败') |
| | | } |
| | | } |
| | | |
| | | const closeAttachmentDialog = () => { |
| | | showAttachmentDialog.value = false |
| | | currentViewRepair.value = null |
| | | attachmentList.value = [] |
| | | } |
| | | |
| | | const previewAttachment = (file) => { |
| | | if (isImageFile(file)) { |
| | | const urls = attachmentList.value.filter((f) => isImageFile(f)).map((f) => formatFileUrl(f.url || f.downloadUrl)) |
| | | uni.previewImage({ |
| | | urls, |
| | | current: formatFileUrl(file.url || file.downloadUrl) |
| | | }) |
| | | } else { |
| | | currentVideoFile.value = file |
| | | showVideoDialog.value = true |
| | | } |
| | | } |
| | | |
| | | const closeVideoPreview = () => { |
| | | showVideoDialog.value = false |
| | | currentVideoFile.value = null |
| | | } |
| | | |
| | | const handleVideoError = () => { |
| | | uni.showToast({ title: '视频播放失败', icon: 'none' }) |
| | | } |
| | | |
| | | // 返回上一页 |
| | | const goBack = () => { |
| | |
| | | .action-buttons { |
| | | gap: 8px; // 与公共样式中的 12px 不同 |
| | | } |
| | | |
| | | /* 查看附件弹窗 */ |
| | | .custom-modal-overlay { |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: rgba(0, 0, 0, 0.5); |
| | | z-index: 100; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 20px; |
| | | } |
| | | |
| | | .custom-modal-container { |
| | | width: 100%; |
| | | max-width: 500px; |
| | | max-height: 80vh; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .attachment-popup-content { |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | width: 100%; |
| | | min-height: 300px; |
| | | max-height: 70vh; |
| | | overflow: hidden; |
| | | display: flex; |
| | | flex-direction: column; |
| | | box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); |
| | | } |
| | | |
| | | .attachment-popup-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 15px 20px; |
| | | border-bottom: 1px solid #eee; |
| | | background: #f8f9fa; |
| | | } |
| | | |
| | | .attachment-popup-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | } |
| | | |
| | | .close-btn-attachment { |
| | | width: 28px; |
| | | height: 28px; |
| | | border-radius: 50%; |
| | | background: #f5f5f5; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .attachment-popup-body { |
| | | flex: 1; |
| | | padding: 15px 20px; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .attachment-content { |
| | | min-height: 200px; |
| | | } |
| | | |
| | | .attachment-list { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .attachment-item { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | background: #fff; |
| | | border-radius: 12px; |
| | | padding: 8px; |
| | | border: 1px solid #e9ecef; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); |
| | | width: calc(33.33% - 8px); |
| | | min-width: 100px; |
| | | } |
| | | |
| | | .attachment-preview-container { |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .attachment-preview { |
| | | width: 80px; |
| | | height: 80px; |
| | | border-radius: 8px; |
| | | object-fit: cover; |
| | | border: 2px solid #f0f0f0; |
| | | } |
| | | |
| | | .attachment-video-preview { |
| | | width: 80px; |
| | | height: 80px; |
| | | background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); |
| | | border-radius: 8px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | border: 2px solid #f0f0f0; |
| | | } |
| | | |
| | | .attachment-info { |
| | | text-align: center; |
| | | width: 100%; |
| | | } |
| | | |
| | | .attachment-name { |
| | | font-size: 12px; |
| | | color: #333; |
| | | display: block; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | max-width: 80px; |
| | | } |
| | | |
| | | .attachment-size { |
| | | font-size: 10px; |
| | | color: #999; |
| | | margin-top: 2px; |
| | | display: block; |
| | | } |
| | | |
| | | .attachment-empty { |
| | | text-align: center; |
| | | padding: 60px 20px; |
| | | color: #999; |
| | | font-size: 14px; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | border: 2px dashed #ddd; |
| | | } |
| | | |
| | | /* 视频预览弹窗 */ |
| | | .video-modal-overlay { |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: rgba(0, 0, 0, 0.8); |
| | | z-index: 10001; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 20px; |
| | | } |
| | | |
| | | .video-modal-container { |
| | | width: 90%; |
| | | max-width: 800px; |
| | | max-height: 80vh; |
| | | background: #000; |
| | | border-radius: 12px; |
| | | overflow: hidden; |
| | | box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); |
| | | } |
| | | |
| | | .video-modal-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 15px 20px; |
| | | background: rgba(0, 0, 0, 0.7); |
| | | color: #fff; |
| | | } |
| | | |
| | | .video-modal-title { |
| | | font-size: 16px; |
| | | color: #fff; |
| | | } |
| | | |
| | | .close-btn-video { |
| | | width: 28px; |
| | | height: 28px; |
| | | border-radius: 50%; |
| | | background: rgba(255, 255, 255, 0.2); |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .video-modal-body { |
| | | background: #000; |
| | | } |
| | | |
| | | .video-player { |
| | | width: 100%; |
| | | height: auto; |
| | | max-height: 60vh; |
| | | display: block; |
| | | } |
| | | |
| | | .video-text { |
| | | font-size: 12px; |
| | | color: #666; |
| | | margin-top: 4px; |
| | | } |
| | | </style> |