| | |
| | | <!-- 操作按钮 --> |
| | | <view class="action-buttons"> |
| | | <button class="action-btn warning" @click="saveRemarks">添加不合格描述</button> |
| | | <button class="action-btn primary" @click="viewAttachmentInfo">查看检验项目</button> |
| | | </view> |
| | | |
| | | <!-- 修改检验结果弹出框 --> |
| | |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 附件列表弹窗 --> |
| | | <view v-if="showAttachmentPopup" class="overlay"> |
| | | <view class="popup attachment-list-popup"> |
| | | <div class="attachment-popup-header"> |
| | | <h3>附件列表</h3> |
| | | <button class="close-btn" @click="closeAttachmentPopup">×</button> |
| | | </div> |
| | | |
| | | <div class="attachment-popup-content"> |
| | | <div v-if="attachmentsLoading" class="attachment-loading"> |
| | | <div class="loading-spinner"></div> |
| | | <span class="loading-text">正在加载附件...</span> |
| | | </div> |
| | | |
| | | <div v-else-if="attachments.length === 0" class="attachment-empty"> |
| | | <div class="empty-icon">📁</div> |
| | | <div class="empty-text">暂无附件</div> |
| | | </div> |
| | | |
| | | <div v-else class="attachment-list"> |
| | | <div v-for="item in attachments" :key="item.id" class="attachment-item"> |
| | | <div class="attachment-info"> |
| | | <div class="file-type-badge" :class="getFileTypeClass(item.fattach)"> |
| | | {{ getFileTypeIcon(item.fattach) }} |
| | | </div> |
| | | <div class="attachment-details"> |
| | | <div class="attachment-name" @click="showAttachmentDetailDialog(item)"> |
| | | {{ item.fattach }} |
| | | </div> |
| | | <div class="attachment-meta"> |
| | | <span class="attachment-type">{{ item.ftype || '未知类型' }}</span> |
| | | <span v-if="item.fversion" class="attachment-version">{{ item.fversion }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="attachment-actions"> |
| | | <button class="btn-secondary" @click="showAttachmentDetailDialog(item)">详情</button> |
| | | <button v-if="isPreviewable(item.fattach)" |
| | | class="btn-primary" |
| | | @click="previewFtpFile(item)">预览</button> |
| | | <button class="btn-success" @click="downloadAttachment(item)">下载</button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 附件详情弹窗 --> |
| | | <view v-if="showAttachmentDetail" class="overlay"> |
| | | <view class="popup attachment-detail-popup"> |
| | | <div class="attachment-popup-header"> |
| | | <h3>附件详情</h3> |
| | | <button class="close-btn" @click="closeAttachmentDetail">×</button> |
| | | </div> |
| | | |
| | | <div class="attachment-popup-content"> |
| | | <div v-if="selectedAttachment" class="attachment-detail-content"> |
| | | <div class="attachment-detail-header"> |
| | | <div class="file-type-badge large" :class="getFileTypeClass(selectedAttachment.fattach)"> |
| | | {{ getFileTypeIcon(selectedAttachment.fattach) }} |
| | | </div> |
| | | <div class="attachment-detail-title"> |
| | | {{ selectedAttachment.fattach }} |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="attachment-detail-info"> |
| | | <div class="info-row"> |
| | | <div class="info-item"> |
| | | <text class="info-label">ID</text> |
| | | <text class="info-content">{{ Math.trunc(selectedAttachment.id) }}</text> |
| | | </div> |
| | | <div class="info-item"> |
| | | <text class="info-label">类型</text> |
| | | <text class="info-content">{{ selectedAttachment.ftype || '未知类型' }}</text> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="info-row" v-if="selectedAttachment.fversion"> |
| | | <div class="info-item"> |
| | | <text class="info-label">版本</text> |
| | | <text class="info-content">{{ selectedAttachment.fversion }}</text> |
| | | </div> |
| | | <div class="info-item" v-if="selectedAttachment.fdate"> |
| | | <text class="info-label">受控日期</text> |
| | | <text class="info-content">{{ formatDate(selectedAttachment.fdate) }}</text> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="info-row" v-if="selectedAttachment.createBy"> |
| | | <div class="info-item"> |
| | | <text class="info-label">上传人</text> |
| | | <text class="info-content">{{ selectedAttachment.createBy }}</text> |
| | | </div> |
| | | <div class="info-item" v-if="selectedAttachment.createDate"> |
| | | <text class="info-label">上传时间</text> |
| | | <text class="info-content">{{ formatDate(selectedAttachment.createDate) }}</text> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="attachment-detail-actions"> |
| | | <button v-if="isPreviewable(selectedAttachment.fattach)" |
| | | class="btn-primary" |
| | | @click="previewFtpFile(selectedAttachment)">预览</button> |
| | | <button class="btn-success" @click="downloadAttachment(selectedAttachment)">下载</button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 文件预览弹窗 --> |
| | | <view v-if="showFilePreviewPopup" class="overlay"> |
| | | <view class="popup file-preview-popup"> |
| | | <div class="attachment-popup-header"> |
| | | <h3>{{ previewTitle }}</h3> |
| | | <button class="close-btn" @click="closeFilePreview">×</button> |
| | | </div> |
| | | |
| | | <div class="file-preview-content"> |
| | | <!-- 文本内容预览 --> |
| | | <pre v-if="previewType === 'text'">{{ previewContent }}</pre> |
| | | |
| | | <!-- 图片内容预览 --> |
| | | <view v-else-if="previewType === 'image'" class="image-preview-container"> |
| | | <image :src="previewContent" mode="aspectFit" class="preview-image-clickable" |
| | | @click="previewImageInPopup" style="width: 100%; max-height: 400px; cursor: pointer;" /> |
| | | <div class="image-zoom-hint">点击图片可放大查看</div> |
| | | </view> |
| | | |
| | | <!-- Excel 等 Office 文件提示 --> |
| | | <view v-else-if="previewType === 'excel'" class="unsupported-preview"> |
| | | <view class="unsupported-icon">📊</view> |
| | | <view class="unsupported-text">Excel 文件暂不支持在线预览</view> |
| | | <view class="unsupported-hint">请点击下载按钮获取完整文件</view> |
| | | </view> |
| | | |
| | | <!-- 不支持的文件类型 --> |
| | | <view v-else class="unsupported-preview"> |
| | | <view class="unsupported-icon">📄</view> |
| | | <view class="unsupported-text">此文件格式暂不支持预览</view> |
| | | <view class="unsupported-hint">请点击下载按钮获取完整文件</view> |
| | | </view> |
| | | </div> |
| | | |
| | | <div class="file-preview-actions"> |
| | | <button v-if="previewType !== 'text'" class="file-preview-btn download-btn" |
| | | @click="downloadPreviewFile">📥 下载文件</button> |
| | | <button class="file-preview-btn close-btn" @click="closeFilePreview">关闭</button> |
| | | </div> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | |
| | | base64Image: "", |
| | | remarks: "", |
| | | remarksPopup: false, |
| | | fcheckResu: null |
| | | fcheckResu: null, |
| | | // 附件相关数据 |
| | | showAttachmentPopup: false, |
| | | showAttachmentDetail: false, |
| | | showFilePreviewPopup: false, |
| | | attachments: [], |
| | | attachmentsLoading: false, |
| | | selectedAttachment: null, |
| | | previewTitle: '', |
| | | previewContent: '', |
| | | previewType: '', // 'text', 'image', 'excel', 'unsupported' |
| | | previewFileUrl: '' |
| | | } |
| | | }, |
| | | methods: { |
| | |
| | | this.$showMessage("保存成功"); |
| | | } |
| | | }) |
| | | } |
| | | }, |
| | | |
| | | // 附件相关方法 |
| | | viewAttachmentInfo() { |
| | | this.showAttachmentPopup = true; |
| | | this.attachmentsLoading = true; |
| | | this.attachments = []; |
| | | |
| | | // 调试日志 |
| | | console.log('viewAttachmentInfo 被调用'); |
| | | console.log('formData.projName:', this.formData.projName); |
| | | console.log('formData.itemNo:', this.formData.itemNo); |
| | | console.log('formData:', this.formData); |
| | | |
| | | this.$post({ |
| | | url: "/RKJ/getAttachments", |
| | | data: { |
| | | itemNo: this.formData.itemNo, |
| | | projName: this.formData.projName, // 传递项目名称 |
| | | fromPage: 'Detail' // 标识来自Detail页面,需要过滤 |
| | | } |
| | | }).then(res => { |
| | | this.attachmentsLoading = false; |
| | | if (res.status === 0) { |
| | | this.attachments = res.data.tbBillList; |
| | | // 为每个附件设置默认可用状态 |
| | | this.attachments.forEach((item, index) => { |
| | | this.$set(item, 'ftpAvailable', true); |
| | | this.$set(item, 'checking', false); |
| | | }); |
| | | } else if (res.status === 1 && res.message === "该检验单未上传附件信息!") { |
| | | uni.showToast({ |
| | | title: res.message, |
| | | icon: "none" |
| | | }); |
| | | } else { |
| | | uni.showToast({ |
| | | title: "获取附件失败", |
| | | icon: "none" |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | closeAttachmentPopup() { |
| | | this.showAttachmentPopup = false; |
| | | }, |
| | | |
| | | showAttachmentDetailDialog(item) { |
| | | this.selectedAttachment = item; |
| | | this.showAttachmentPopup = false; |
| | | this.showAttachmentDetail = true; |
| | | }, |
| | | |
| | | closeAttachmentDetail() { |
| | | this.showAttachmentDetail = false; |
| | | this.selectedAttachment = null; |
| | | this.showAttachmentPopup = true; |
| | | }, |
| | | |
| | | isPreviewable(filename) { |
| | | if (!filename) return false; |
| | | const ext = filename.trim().split('.').pop().toLowerCase(); |
| | | return [ |
| | | 'pdf', |
| | | 'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', |
| | | 'txt', 'log', 'md', |
| | | 'doc', 'docx', |
| | | 'xls', 'xlsx', |
| | | 'ppt', 'pptx', |
| | | 'csv' |
| | | ].includes(ext); |
| | | }, |
| | | |
| | | getFileTypeIcon(filename) { |
| | | if (!filename) return '📄'; |
| | | const ext = filename.trim().split('.').pop().toLowerCase(); |
| | | const iconMap = { |
| | | 'pdf': '📕', |
| | | 'jpg': '🖼️', 'jpeg': '🖼️', 'png': '🖼️', 'gif': '🖼️', 'bmp': '🖼️', 'webp': '🖼️', |
| | | 'txt': '📝', 'log': '📝', 'md': '📝', |
| | | 'doc': '📘', 'docx': '📘', |
| | | 'xls': '📊', 'xlsx': '📊', |
| | | 'ppt': '📙', 'pptx': '📙', |
| | | 'csv': '📊', |
| | | 'default': '📄' |
| | | }; |
| | | return iconMap[ext] || iconMap.default; |
| | | }, |
| | | |
| | | getFileTypeClass(filename) { |
| | | if (!filename) return 'file-unknown'; |
| | | const ext = filename.trim().split('.').pop().toLowerCase(); |
| | | const classMap = { |
| | | 'pdf': 'file-pdf', |
| | | 'jpg': 'file-image', 'jpeg': 'file-image', 'png': 'file-image', 'gif': 'file-image', 'bmp': 'file-image', 'webp': 'file-image', |
| | | 'txt': 'file-text', 'log': 'file-text', 'md': 'file-text', |
| | | 'doc': 'file-word', 'docx': 'file-word', |
| | | 'xls': 'file-excel', 'xlsx': 'file-excel', |
| | | 'ppt': 'file-powerpoint', 'pptx': 'file-powerpoint', |
| | | 'csv': 'file-excel', |
| | | 'default': 'file-unknown' |
| | | }; |
| | | return classMap[ext] || classMap.default; |
| | | }, |
| | | |
| | | formatDate(dateString) { |
| | | if (!dateString) return ''; |
| | | try { |
| | | const date = new Date(dateString); |
| | | return date.toLocaleDateString('zh-CN', { |
| | | year: 'numeric', |
| | | month: '2-digit', |
| | | day: '2-digit', |
| | | hour: '2-digit', |
| | | minute: '2-digit' |
| | | }); |
| | | } catch (error) { |
| | | return dateString; |
| | | } |
| | | }, |
| | | |
| | | downloadAttachment(item) { |
| | | const fileName = item.fattach.replace(/[\s\u3000\r\n]+/g, '').trim(); |
| | | const downloadUrl = this.$store.state.serverInfo.serverAPI + "/RKJ/DownloadFtpFile?itemNo=" + |
| | | encodeURIComponent(item.itemNo) + "&fileName=" + encodeURIComponent(fileName) + |
| | | "&ftpServer=" + encodeURIComponent(this.$store.state.serverInfo.ftpServer) + |
| | | "&projName=" + encodeURIComponent(this.formData.projName || ''); |
| | | |
| | | uni.downloadFile({ |
| | | url: downloadUrl, |
| | | success: (res) => { |
| | | if (res.statusCode === 200) { |
| | | uni.showToast({ |
| | | title: '下载成功', |
| | | icon: 'success' |
| | | }); |
| | | } else { |
| | | uni.showToast({ |
| | | title: '下载失败', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }, |
| | | fail: (error) => { |
| | | console.error('下载失败:', error); |
| | | uni.showToast({ |
| | | title: '下载失败', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | previewFtpFile(item) { |
| | | const fileName = item.fattach.replace(/[\s\u3000\r\n]+/g, '').trim(); |
| | | const fileExt = fileName.split('.').pop().toLowerCase(); |
| | | |
| | | if (!this.isPreviewable(fileName)) { |
| | | uni.showModal({ |
| | | title: '不支持预览', |
| | | content: '该文件类型不支持在线预览,请下载后查看', |
| | | showCancel: false |
| | | }); |
| | | return; |
| | | } |
| | | |
| | | const previewUrl = this.$store.state.serverInfo.serverAPI + "/RKJ/PreviewFtpFile?itemNo=" + |
| | | encodeURIComponent(item.itemNo) + "&fileName=" + encodeURIComponent(fileName) + |
| | | "&ftpServer=" + encodeURIComponent(this.$store.state.serverInfo.ftpServer) + |
| | | "&projName=" + encodeURIComponent(this.formData.projName || ''); |
| | | |
| | | if (['pdf'].includes(fileExt)) { |
| | | this.previewPdfFile(previewUrl, fileName); |
| | | } else if (['jpg', 'jpeg', 'png', 'gif', 'bmp'].includes(fileExt)) { |
| | | this.previewImageFile(previewUrl, fileName); |
| | | } else if (['txt'].includes(fileExt)) { |
| | | this.previewTextFile(previewUrl, fileName); |
| | | } else if (['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'].includes(fileExt)) { |
| | | this.previewOfficeFile(previewUrl, fileName); |
| | | } else { |
| | | this.previewGenericFile(previewUrl, fileName); |
| | | } |
| | | }, |
| | | |
| | | previewPdfFile(url, fileName) { |
| | | this.previewTitle = fileName; |
| | | this.previewType = 'pdf'; |
| | | this.previewFileUrl = url; |
| | | this.showFilePreviewPopup = true; |
| | | this.showAttachmentDetail = false; |
| | | }, |
| | | |
| | | previewImageFile(url, fileName) { |
| | | this.previewTitle = fileName; |
| | | this.previewType = 'image'; |
| | | this.previewContent = url; |
| | | this.previewFileUrl = url; |
| | | this.showFilePreviewPopup = true; |
| | | this.showAttachmentDetail = false; |
| | | }, |
| | | |
| | | previewTextFile(url, fileName) { |
| | | this.previewTitle = fileName; |
| | | this.previewType = 'text'; |
| | | this.previewFileUrl = url; |
| | | this.showFilePreviewPopup = true; |
| | | this.showAttachmentDetail = false; |
| | | |
| | | uni.request({ |
| | | url: url, |
| | | method: 'GET', |
| | | success: (res) => { |
| | | if (res.statusCode === 200) { |
| | | this.previewContent = res.data; |
| | | } else { |
| | | this.previewContent = '无法加载文件内容'; |
| | | } |
| | | }, |
| | | fail: (error) => { |
| | | this.previewContent = '加载文件失败'; |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | previewOfficeFile(url, fileName) { |
| | | this.previewTitle = fileName; |
| | | this.previewType = 'excel'; |
| | | this.previewFileUrl = url; |
| | | this.showFilePreviewPopup = true; |
| | | this.showAttachmentDetail = false; |
| | | }, |
| | | |
| | | previewGenericFile(url, fileName) { |
| | | this.previewTitle = fileName; |
| | | this.previewType = 'unsupported'; |
| | | this.previewFileUrl = url; |
| | | this.showFilePreviewPopup = true; |
| | | this.showAttachmentDetail = false; |
| | | }, |
| | | |
| | | closeFilePreview() { |
| | | this.showFilePreviewPopup = false; |
| | | this.showAttachmentDetail = true; |
| | | }, |
| | | |
| | | downloadPreviewFile() { |
| | | if (this.previewFileUrl && this.selectedAttachment) { |
| | | this.downloadAttachment(this.selectedAttachment); |
| | | } |
| | | }, |
| | | |
| | | previewImageInPopup() { |
| | | // 在新窗口中打开图片进行放大查看 |
| | | if (this.previewContent) { |
| | | uni.previewImage({ |
| | | urls: [this.previewContent], |
| | | current: this.previewContent |
| | | }); |
| | | } |
| | | } |
| | | }, |
| | |
| | | color: $danger-color; |
| | | } |
| | | |
| | | /* 附件相关样式 */ |
| | | .attachment-list-popup, |
| | | .attachment-detail-popup, |
| | | .file-preview-popup { |
| | | width: 90vw; |
| | | max-width: 600px; |
| | | max-height: 80vh; |
| | | } |
| | | |
| | | .attachment-popup-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 20px; |
| | | border-bottom: 1px solid #eee; |
| | | |
| | | h3 { |
| | | margin: 0; |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #2c3e50; |
| | | } |
| | | |
| | | .close-btn { |
| | | background: none; |
| | | border: none; |
| | | font-size: 24px; |
| | | color: #999; |
| | | cursor: pointer; |
| | | padding: 0; |
| | | width: 30px; |
| | | height: 30px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | |
| | | &:hover { |
| | | color: #333; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .attachment-popup-content { |
| | | padding: 20px; |
| | | max-height: 60vh; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .attachment-loading { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | padding: 40px 20px; |
| | | |
| | | .loading-spinner { |
| | | width: 40px; |
| | | height: 40px; |
| | | border: 4px solid #f3f3f3; |
| | | border-top: 4px solid #3498db; |
| | | border-radius: 50%; |
| | | animation: spin 1s linear infinite; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .loading-text { |
| | | color: #666; |
| | | font-size: 14px; |
| | | } |
| | | } |
| | | |
| | | @keyframes spin { |
| | | 0% { transform: rotate(0deg); } |
| | | 100% { transform: rotate(360deg); } |
| | | } |
| | | |
| | | .attachment-empty { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | padding: 40px 20px; |
| | | |
| | | .empty-icon { |
| | | font-size: 48px; |
| | | margin-bottom: 16px; |
| | | opacity: 0.5; |
| | | } |
| | | |
| | | .empty-text { |
| | | color: #999; |
| | | font-size: 16px; |
| | | } |
| | | } |
| | | |
| | | .attachment-list { |
| | | .attachment-item { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | padding: 16px; |
| | | border: 1px solid #eee; |
| | | border-radius: 8px; |
| | | margin-bottom: 12px; |
| | | background: #fafafa; |
| | | transition: all 0.2s; |
| | | |
| | | &:hover { |
| | | background: #f0f0f0; |
| | | border-color: #ddd; |
| | | } |
| | | } |
| | | |
| | | .attachment-info { |
| | | display: flex; |
| | | align-items: center; |
| | | flex: 1; |
| | | margin-right: 16px; |
| | | } |
| | | |
| | | .file-type-badge { |
| | | width: 40px; |
| | | height: 40px; |
| | | border-radius: 8px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | font-size: 20px; |
| | | margin-right: 12px; |
| | | flex-shrink: 0; |
| | | |
| | | &.file-pdf { background: #ffebee; color: #d32f2f; } |
| | | &.file-image { background: #e8f5e8; color: #2e7d32; } |
| | | &.file-text { background: #fff3e0; color: #f57c00; } |
| | | &.file-word { background: #e3f2fd; color: #1976d2; } |
| | | &.file-excel { background: #e8f5e8; color: #388e3c; } |
| | | &.file-powerpoint { background: #fce4ec; color: #c2185b; } |
| | | &.file-unknown { background: #f5f5f5; color: #757575; } |
| | | } |
| | | |
| | | .attachment-details { |
| | | flex: 1; |
| | | min-width: 0; |
| | | } |
| | | |
| | | .attachment-name { |
| | | font-weight: 500; |
| | | color: #333; |
| | | margin-bottom: 4px; |
| | | cursor: pointer; |
| | | word-break: break-all; |
| | | |
| | | &:hover { |
| | | color: #1976d2; |
| | | text-decoration: underline; |
| | | } |
| | | } |
| | | |
| | | .attachment-meta { |
| | | display: flex; |
| | | gap: 12px; |
| | | font-size: 12px; |
| | | color: #666; |
| | | } |
| | | |
| | | .attachment-actions { |
| | | display: flex; |
| | | gap: 8px; |
| | | flex-shrink: 0; |
| | | } |
| | | } |
| | | |
| | | .attachment-detail-content { |
| | | .attachment-detail-header { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 24px; |
| | | padding-bottom: 16px; |
| | | border-bottom: 1px solid #eee; |
| | | } |
| | | |
| | | .file-type-badge.large { |
| | | width: 60px; |
| | | height: 60px; |
| | | font-size: 28px; |
| | | margin-right: 16px; |
| | | } |
| | | |
| | | .attachment-detail-title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | .attachment-detail-info { |
| | | margin-bottom: 24px; |
| | | } |
| | | |
| | | .info-row { |
| | | display: flex; |
| | | margin-bottom: 12px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | .info-item { |
| | | flex: 1; |
| | | margin-right: 16px; |
| | | |
| | | &:last-child { |
| | | margin-right: 0; |
| | | } |
| | | } |
| | | |
| | | .info-label { |
| | | display: block; |
| | | font-size: 12px; |
| | | color: #666; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .info-content { |
| | | font-size: 14px; |
| | | color: #333; |
| | | font-weight: 500; |
| | | } |
| | | |
| | | .attachment-detail-actions { |
| | | display: flex; |
| | | gap: 12px; |
| | | justify-content: center; |
| | | } |
| | | } |
| | | |
| | | .file-preview-content { |
| | | max-height: 400px; |
| | | overflow-y: auto; |
| | | background: #f8f9fa; |
| | | border-radius: 8px; |
| | | padding: 16px; |
| | | margin-bottom: 16px; |
| | | |
| | | pre { |
| | | font-family: 'Consolas', 'Monaco', 'Courier New', monospace; |
| | | font-size: 12px; |
| | | line-height: 1.5; |
| | | color: #2d3748; |
| | | white-space: pre-wrap; |
| | | word-wrap: break-word; |
| | | margin: 0; |
| | | } |
| | | } |
| | | |
| | | .image-preview-container { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | |
| | | .preview-image-clickable { |
| | | border-radius: 8px; |
| | | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| | | cursor: pointer; |
| | | transition: transform 0.2s; |
| | | |
| | | &:hover { |
| | | transform: scale(1.02); |
| | | } |
| | | } |
| | | |
| | | .image-zoom-hint { |
| | | margin-top: 8px; |
| | | font-size: 12px; |
| | | color: #666; |
| | | font-style: italic; |
| | | } |
| | | } |
| | | |
| | | .unsupported-preview { |
| | | text-align: center; |
| | | padding: 40px 20px; |
| | | color: #666; |
| | | |
| | | .unsupported-icon { |
| | | font-size: 48px; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .unsupported-text { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #333; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .unsupported-hint { |
| | | font-size: 14px; |
| | | color: #999; |
| | | line-height: 1.4; |
| | | } |
| | | } |
| | | |
| | | .file-preview-actions { |
| | | display: flex; |
| | | gap: 12px; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .file-preview-btn { |
| | | padding: 8px 20px; |
| | | border: none; |
| | | border-radius: 6px; |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | cursor: pointer; |
| | | transition: all 0.3s ease; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | min-width: 120px; |
| | | |
| | | &.download-btn { |
| | | background: linear-gradient(135deg, #2196F3, #1976D2); |
| | | color: white; |
| | | |
| | | &:hover { |
| | | background: linear-gradient(135deg, #1976D2, #1565C0); |
| | | transform: translateY(-1px); |
| | | } |
| | | } |
| | | |
| | | &.close-btn { |
| | | background: linear-gradient(135deg, #e0e0e0, #bdbdbd); |
| | | color: #444; |
| | | |
| | | &:hover { |
| | | background: linear-gradient(135deg, #bdbdbd, #9e9e9e); |
| | | transform: translateY(-1px); |
| | | } |
| | | } |
| | | } |
| | | |
| | | .btn-secondary { |
| | | background: #f5f7fa; |
| | | color: #333; |
| | | border: 1px solid #dbe2ea; |
| | | padding: 6px 12px; |
| | | border-radius: 4px; |
| | | font-size: 12px; |
| | | cursor: pointer; |
| | | transition: all 0.2s; |
| | | |
| | | &:hover { |
| | | background: #e6f0fa; |
| | | color: #1976d2; |
| | | } |
| | | } |
| | | |
| | | .btn-primary { |
| | | background: #e8f5e8; |
| | | color: #2e7d2e; |
| | | border: 1px solid #a5d6a5; |
| | | padding: 6px 12px; |
| | | border-radius: 4px; |
| | | font-size: 12px; |
| | | cursor: pointer; |
| | | transition: all 0.2s; |
| | | |
| | | &:hover { |
| | | background: #d4eecc; |
| | | color: #1e5f1e; |
| | | } |
| | | } |
| | | |
| | | .btn-success { |
| | | background: #e3f2fd; |
| | | color: #1976d2; |
| | | border: 1px solid #90caf9; |
| | | padding: 6px 12px; |
| | | border-radius: 4px; |
| | | font-size: 12px; |
| | | cursor: pointer; |
| | | transition: all 0.2s; |
| | | |
| | | &:hover { |
| | | background: #bbdefb; |
| | | color: #1565c0; |
| | | } |
| | | } |
| | | |
| | | /* 响应式设计 */ |
| | | @media (max-width: 768px) { |
| | | .info-grid { |
| | |
| | | padding: 8px 4px; |
| | | } |
| | | } |
| | | |
| | | .attachment-list-popup, |
| | | .attachment-detail-popup, |
| | | .file-preview-popup { |
| | | width: 95vw; |
| | | max-height: 90vh; |
| | | } |
| | | |
| | | .attachment-item { |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | |
| | | .attachment-info { |
| | | margin-right: 0; |
| | | margin-bottom: 12px; |
| | | width: 100%; |
| | | } |
| | | |
| | | .attachment-actions { |
| | | width: 100%; |
| | | justify-content: flex-end; |
| | | } |
| | | } |
| | | |
| | | .attachment-detail-actions { |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .file-preview-actions { |
| | | flex-direction: column; |
| | | } |
| | | } |
| | | </style> |