| | |
| | | |
| | | </select> |
| | | </view> |
| | | <view class="info-block" style="margin-top: 10px;"> |
| | | <view class="info-block" style="margin-top: 10px;"> |
| | | <view class="info-label">备注:</view> |
| | | <input type="text" id="lotNo1" v-model="formData.lotNo1" |
| | | placeholder="请输入备注信息" |
| | | style="color: red; font-weight: bold;"/> |
| | | </view> |
| | | <view class="info-block" style="margin-top: 10px;"> |
| | | <view class="info-label">破坏实验数量:</view> |
| | | <view class="info-value highlight">{{formData.phsy}}</view> |
| | | </view> |
| | | |
| | | |
| | | |
| | | |
| | | <!-- 操作按钮区 --> |
| | | <view class="action-buttons" v-if="this.current"> |
| | | <button class="secondary-btn" @click="handleEmergencyRelease">紧急放行</button> |
| | | <button class="secondary-btn" @click="handleWithdraw">撤回</button> |
| | | <button class="secondary-btn" @click="getInspectionItems">获取检验项目</button> |
| | | </view> |
| | | |
| | | |
| | | |
| | |
| | | </table> |
| | | </view> |
| | | |
| | | <!-- 操作按钮区 --> |
| | | <view class="action-buttons"> |
| | | <button class="secondary-btn" @click="uploadImages">上传/查看图片</button> |
| | | <button class="secondary-btn" @click="fetchDrawingNumber">调取PLM图纸</button> |
| | | <button class="secondary-btn" @click="addDefectDescription" v-if="this.current">添加不良描述</button> |
| | | <button class="primary-btn" @click="submitInspection" v-if="this.current">检验提交</button> |
| | | <!-- 页面内容区域 --> |
| | | <view class="content-wrapper"> |
| | | <!-- 为底部按钮留出空间 --> |
| | | </view> |
| | | |
| | | <!-- 固定在底部的操作按钮区 --> |
| | | <view class="fixed-action-buttons"> |
| | | <button class="action-btn" @click="handleEmergencyRelease" v-if="this.current">紧急放行</button> |
| | | <button class="action-btn" @click="handleWithdraw" v-if="this.current">撤回</button> |
| | | <button class="action-btn" @click="getInspectionItems" v-if="this.current">获取检验项目</button> |
| | | <button class="action-btn" @click="addDestruction" v-if="this.current">破坏实验</button> |
| | | <button class="action-btn" @click="uploadImages">上传/查看图片</button> |
| | | <button class="action-btn" @click="fetchDrawingNumber">调取PLM图纸</button> |
| | | <button class="action-btn" @click="viewAttachmentInfo">查看附件信息</button> |
| | | <button class="action-btn" @click="addDefectDescription" v-if="this.current">添加不良描述</button> |
| | | <button class="action-btn primary" @click="submitInspection" v-if="this.current">检验提交</button> |
| | | </view> |
| | | <view v-if="remarksPopup" class="overlay"> |
| | | <view class="popup"> |
| | |
| | | </view> |
| | | </view> |
| | | |
| | | <view v-if="destructionPopup" class="overlay"> |
| | | <view class="popup"> |
| | | <h3>破坏实验数量</h3> |
| | | <form> |
| | | <view class="form-group"> |
| | | <label class="form-label">破坏实验数量:</label> |
| | | <input class="form-input" type="text" v-model="PHSY" placeholder="留空表示清除数量" /> |
| | | </view> |
| | | </form> |
| | | <div v-if="!isInteger" class="error-message">请输入整数值或留空</div> |
| | | <button class="updateBut" @click="editDestruction">修改</button> |
| | | <button @click="clearDestruction">清除</button> |
| | | <button @click="destructionPopup = !destructionPopup">取消</button> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="barcode"> |
| | | <u-modal :show="drawingShow" title="图纸明细" @confirm="drawingConfirm" @cancel="drawingCancel" |
| | | showCancelButton> |
| | | <uni-table border stripe emptyText="暂无更多数据" style="margin-left: 5px;margin-right: 5px;height: 500px;"> |
| | | showCancelButton :z-index="1000"> |
| | | <uni-table border stripe emptyText="暂无更多数据" style="margin-left: 5px;margin-right: 5px;height: 400px;max-height: 60vh;overflow-y: auto;"> |
| | | <uni-tr> |
| | | <uni-th align="center">相关文档</uni-th> |
| | | <uni-th align="center" width="90">有无关联PDF文件</uni-th> |
| | |
| | | </uni-tr> |
| | | </uni-table> |
| | | </u-modal> |
| | | </view> |
| | | <!-- 附件详情弹窗 --> |
| | | <view v-if="showAttachmentDetail" class="overlay"> |
| | | <view class="popup attachment-detail-popup"> |
| | | <h3 class="attachment-popup-title">附件详情</h3> |
| | | <div class="attachment-popup-divider"></div> |
| | | <div v-if="selectedAttachment" class="attachment-detail-content"> |
| | | <div class="attachment-detail-row"><span class="attachment-label">ID:</span><span>{{ Math.trunc(selectedAttachment.id) }}</span></div> |
| | | <div class="attachment-detail-row"><span class="attachment-label">附件名:</span><span>{{ selectedAttachment.fattach }}</span></div> |
| | | <div class="attachment-detail-row"><span class="attachment-label">类型:</span><span>{{ selectedAttachment.ftype }}</span></div> |
| | | <div class="attachment-detail-row"><span class="attachment-label">版本:</span><span>{{ selectedAttachment.fversion }}</span></div> |
| | | <div class="attachment-detail-row"><span class="attachment-label">受控日期:</span><span>{{ selectedAttachment.fdate }}</span></div> |
| | | <div class="attachment-detail-row"><span class="attachment-label">上传人:</span><span>{{ selectedAttachment.createBy }}</span></div> |
| | | <div class="attachment-detail-row"><span class="attachment-label">上传时间:</span><span>{{ selectedAttachment.createDate }}</span></div> |
| | | <div class="attachment-actions-detail"> |
| | | <button class="attachment-action-btn preview-btn" |
| | | @click="previewFtpFile(selectedAttachment)" |
| | | v-if="isPreviewable(selectedAttachment.fattach)"> |
| | | 🔍 在线预览 |
| | | </button> |
| | | <button class="attachment-action-btn download-btn" |
| | | @click="downloadAttachment(selectedAttachment)"> |
| | | 📥 下载文件 |
| | | </button> |
| | | </div> |
| | | </div> |
| | | <div v-else class="attachment-detail-empty">暂无附件信息</div> |
| | | <button class="attachment-popup-close" @click="closeAttachmentDetail">返回附件列表</button> |
| | | </view> |
| | | </view> |
| | | <!-- 附件列表弹窗 --> |
| | | <view v-if="showAttachmentPopup" class="overlay"> |
| | | <view class="popup" style="width: 60vw; max-width: 500px;"> |
| | | <h3>附件列表</h3> |
| | | <div v-if="attachmentsLoading">加载中...</div> |
| | | <div v-else-if="attachments.length === 0">暂无附件</div> |
| | | <ul class="attachment-list" v-else> |
| | | <li v-for="item in attachments" :key="item.id"> |
| | | <div class="attachment-info"> |
| | | <span class="attachment-name" @click="showAttachmentDetailDialog(item)"> |
| | | {{ item.fattach }} |
| | | </span> |
| | | <div class="attachment-meta"> |
| | | <span class="attachment-type">{{ item.ftype || '未知类型' }}</span> |
| | | </div> |
| | | </div> |
| | | <div class="attachment-actions"> |
| | | <button class="secondary-btn" @click="showAttachmentDetailDialog(item)">详情</button> |
| | | <button class="secondary-btn preview-btn" @click="previewFtpFile(item)" |
| | | v-if="isPreviewable(item.fattach)">预览</button> |
| | | <button class="secondary-btn" @click="downloadAttachment(item)">下载</button> |
| | | </div> |
| | | </li> |
| | | </ul> |
| | | <button class="attachment-popup-close" @click="closeAttachmentPopup">关闭</button> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 文件预览弹窗 --> |
| | | <view v-if="showFilePreviewPopup" class="overlay"> |
| | | <view class="popup file-preview-popup"> |
| | | <h3 class="file-preview-title">{{ previewTitle }}</h3> |
| | | <div class="file-preview-divider"></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="widthFix" style="width: 100%; max-height: 400px;"></image> |
| | | </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> |
| | | |
| | |
| | | permissionAccount: '', |
| | | hasEmergencyPermission: false, |
| | | showPopup: false, |
| | | PHSY:'', |
| | | EMERGENCY: '0', |
| | | |
| | | }, |
| | | tableData: [], |
| | |
| | | fileName:'', |
| | | originalRemarks: '', |
| | | hasEmergencyPermission: false, |
| | | destructionPopup: false, |
| | | PHSY: '', |
| | | isInteger: true, |
| | | attachments: [], |
| | | showAttachmentPopup: false, |
| | | attachmentsLoading: false, |
| | | selectedAttachment: null, |
| | | showAttachmentDetail: false, |
| | | showFilePreviewPopup: false, |
| | | previewContent: '', |
| | | previewTitle: '', |
| | | previewItemNo: '', |
| | | previewType: '', // 'text', 'image', 'excel', 'unsupported' |
| | | |
| | | } |
| | | }, |
| | |
| | | } |
| | | if (this.$loginInfo.account === 'PL017') {this.hasEmergencyPermission = true;} |
| | | |
| | | // 测试附件服务器连接 |
| | | |
| | | |
| | | |
| | | }, |
| | | methods: { |
| | | getStatusText(status) { |
| | |
| | | data: { |
| | | itemNo: this.formData.itemId, |
| | | quantity: this.formData.fcovertQty, |
| | | releaseNo: this.formData.releaseNo |
| | | releaseNo: this.formData.releaseNo, |
| | | PHSY: this.formData.PHSY |
| | | } |
| | | }).then(res => { |
| | | if (res.status == 0) { |
| | |
| | | staffNo: 'HMCS', |
| | | // staffNo: this.$loginInfo.account, |
| | | iqcStatus: this.PSTYPE, |
| | | department: this.DEPARTMENT |
| | | department: this.DEPARTMENT, |
| | | EMERGENCY: this.formData.emergencyStatus ? "1" : "0" // 确保紧急放行状态正确传递 |
| | | }; |
| | | } |
| | | else{ |
| | |
| | | // staffNo: 'HMCS', |
| | | staffNo: this.$loginInfo.account, |
| | | iqcStatus: this.PSTYPE, |
| | | department: this.DEPARTMENT |
| | | department: this.DEPARTMENT, |
| | | EMERGENCY: this.formData.emergencyStatus ? "1" : "0" // 确保紧急放行状态正确传递 |
| | | }; |
| | | } |
| | | |
| | | console.log(this.QcIssueResultDetailes) |
| | | console.log("提交的紧急放行状态:", this.formData.emergencyStatus ? "1" : "0"); |
| | | console.log("完整的提交数据:", this.QcIssueResultDetailes); |
| | | |
| | | const url = this.$store.state.serverInfo.serverAPI+`/QcIssueResult/GetProcessNo`; |
| | | // 发送 POST 请求 |
| | | if (this.PSTYPE === '特采/让步使用') {// 发送 POST 请求 |
| | | uni.request({ |
| | | url: url, // 请求地址 |
| | | method: 'POST', // 请求方法 |
| | |
| | | |
| | | // 如果有页面跳转,需要用定时器延迟 |
| | | setTimeout(() => { |
| | | uni.navigateTo({ |
| | | // 设置一个标记,告诉List页面需要刷新 |
| | | getApp().globalData = getApp().globalData || {}; |
| | | getApp().globalData.needRefreshList = true; |
| | | uni.redirectTo({ |
| | | url: 'List' |
| | | }); |
| | | }, 2000); // 保持与 duration 相同的时长 |
| | |
| | | }); |
| | | } |
| | | }); |
| | | |
| | | } |
| | | uni.showToast({ |
| | | title: res.message.toString(), |
| | | icon: 'success', |
| | |
| | | }) |
| | | // 如果有页面跳转,需要用定时器延迟 |
| | | setTimeout(() => { |
| | | uni.navigateTo({ |
| | | url: 'List' |
| | | }); |
| | | // 设置一个标记,告诉List页面需要刷新 |
| | | getApp().globalData = getApp().globalData || {}; |
| | | getApp().globalData.needRefreshList = true; |
| | | uni.navigateBack(); |
| | | }, 2000); // 保持与 duration 相同的时长 |
| | | |
| | | } else { |
| | | uni.showModal({ |
| | | title: "提示", |
| | |
| | | if (data) { |
| | | this.formData = { |
| | | ...data, |
| | | emergencyStatus: data.emergency |
| | | emergencyStatus: data.emergency === 1 || data.emergency === "1" || data.emergency === true |
| | | }; |
| | | |
| | | |
| | | // 确保PHSY字段被正确设置 |
| | | this.formData.PHSY = data.PHSY; |
| | | |
| | | console.log("加载的紧急放行状态:", this.formData.emergencyStatus); |
| | | |
| | | this.$post({ |
| | | url: "/LLJ/getJYItem", |
| | | data: { |
| | |
| | | }); |
| | | }, |
| | | editRemarks() { |
| | | if (this.remarks) { |
| | | //saveRemarksGid |
| | | this.$post({ |
| | | url: "/LLJ/saveRemarksGid", |
| | | data: { |
| | | gid: this.formData.id, |
| | | remarks: this.remarks, |
| | | releaseNo: this.formData.releaseNo, |
| | | } |
| | | }).then(res => { |
| | | if (res.data.tbBillList > 0) { |
| | | this.formData.remarks = this.remarks; |
| | | this.remarksPopup = !this.remarksPopup; |
| | | this.$showMessage("保存成功"); |
| | | setTimeout(() => { |
| | | let pages = getCurrentPages(); |
| | | let beforePage = pages[pages.length - 2]; |
| | | uni.navigateBack({ |
| | | delta: 1, //返回的页面数,如果为1表示返回上一页 |
| | | success: (event) => { |
| | | beforePage.$vm.reload() |
| | | } |
| | | }); |
| | | }, 2000); |
| | | } |
| | | }) |
| | | } else { |
| | | this.$post({ |
| | | url: "/LLJ/saveRemarksGid", |
| | | data: { |
| | | gid: this.formData.id, |
| | | remarks: '', |
| | | releaseNo: this.formData.releaseNo, |
| | | } |
| | | }).then(res => { |
| | | if (res.data.tbBillList > 0) { |
| | | this.formData.remarks = this.remarks; |
| | | this.remarksPopup = !this.remarksPopup; |
| | | this.$showMessage("保存成功"); |
| | | setTimeout(() => { |
| | | let pages = getCurrentPages(); |
| | | let beforePage = pages[pages.length - 2]; |
| | | uni.navigateBack({ |
| | | delta: 1, //返回的页面数,如果为1表示返回上一页 |
| | | success: (event) => { |
| | | beforePage.$vm.reload() |
| | | } |
| | | }); |
| | | }, 2000); |
| | | } |
| | | }) |
| | | } |
| | | // 保存不良描述 |
| | | this.$post({ |
| | | url: "/LLJ/saveRemarksGid", |
| | | data: { |
| | | gid: this.formData.id, |
| | | remarks: this.remarks || '', |
| | | releaseNo: this.formData.releaseNo |
| | | // 不再包含PHSY字段 |
| | | } |
| | | }).then(res => { |
| | | if (res.data.tbBillList > 0) { |
| | | this.formData.remarks = this.remarks; |
| | | this.remarksPopup = !this.remarksPopup; |
| | | this.$showMessage("保存成功"); |
| | | setTimeout(() => { |
| | | this.init(); |
| | | }, 2000); |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | drawingConfirm() { |
| | |
| | | this.formData.emergencyStatus = true; |
| | | this.originalLotNo1 = lotNo1; |
| | | this.formData.lotNo1 = lotNo1; |
| | | // 更新紧急放行状态,确保提交时能正确传递 |
| | | this.formData.EMERGENCY = "1"; |
| | | |
| | | uni.showToast({ |
| | | title: message || '紧急放行成功', |
| | | icon: 'success', |
| | |
| | | |
| | | if (result === "0") { |
| | | this.formData.emergencyStatus = false; |
| | | // 更新紧急放行状态,确保提交时能正确传递 |
| | | this.formData.EMERGENCY = "0"; |
| | | |
| | | uni.showToast({ |
| | | title: message || '撤回成功', |
| | | icon: 'success', |
| | |
| | | }); |
| | | }, |
| | | editRemarks() { |
| | | if (this.remarks) { |
| | | // 只保存修改的部分 |
| | | const modifiedRemarks = this.remarks !== this.originalRemarks ? this.remarks : ''; |
| | | |
| | | this.$post({ |
| | | url: "/LLJ/saveRemarksGid", |
| | | data: { |
| | | gid: this.formData.id, |
| | | remarks: modifiedRemarks, |
| | | releaseNo: this.formData.releaseNo, |
| | | } |
| | | }).then(res => { |
| | | if (res.data.tbBillList > 0) { |
| | | this.formData.remarks = this.remarks; |
| | | this.originalRemarks = this.remarks; // 更新原始备注值 |
| | | this.remarksPopup = !this.remarksPopup; |
| | | this.$showMessage("保存成功"); |
| | | setTimeout(() => { |
| | | let pages = getCurrentPages(); |
| | | let beforePage = pages[pages.length - 2]; |
| | | uni.navigateBack({ |
| | | delta: 1, //返回的页面数,如果为1表示返回上一页 |
| | | success: (event) => { |
| | | beforePage.$vm.reload() |
| | | } |
| | | }); |
| | | }, 2000); |
| | | } |
| | | }) |
| | | } else { |
| | | this.$post({ |
| | | url: "/LLJ/saveRemarksGid", |
| | | data: { |
| | | gid: this.formData.id, |
| | | remarks: '', |
| | | releaseNo: this.formData.releaseNo, |
| | | } |
| | | }).then(res => { |
| | | if (res.data.tbBillList > 0) { |
| | | this.formData.remarks = this.remarks; |
| | | this.originalRemarks = this.remarks; // 更新原始备注值 |
| | | this.remarksPopup = !this.remarksPopup; |
| | | this.$showMessage("保存成功"); |
| | | setTimeout(() => { |
| | | let pages = getCurrentPages(); |
| | | let beforePage = pages[pages.length - 2]; |
| | | uni.navigateBack({ |
| | | delta: 1, //返回的页面数,如果为1表示返回上一页 |
| | | success: (event) => { |
| | | beforePage.$vm.reload() |
| | | } |
| | | }); |
| | | }, 2000); |
| | | } |
| | | }) |
| | | } |
| | | }, |
| | | // 保存不良描述 |
| | | this.$post({ |
| | | url: "/LLJ/saveRemarksGid", |
| | | data: { |
| | | gid: this.formData.id, |
| | | remarks: this.remarks || '', |
| | | releaseNo: this.formData.releaseNo |
| | | // 不再包含PHSY字段 |
| | | } |
| | | }).then(res => { |
| | | if (res.data.tbBillList > 0) { |
| | | this.formData.remarks = this.remarks; |
| | | this.remarksPopup = !this.remarksPopup; |
| | | this.$showMessage("保存成功"); |
| | | setTimeout(() => { |
| | | this.init(); |
| | | }, 2000); |
| | | } |
| | | }) |
| | | }, |
| | | addDestruction() { |
| | | // 添加破坏实验的逻辑 |
| | | this.destructionPopup = !this.destructionPopup; |
| | | this.PHSY = this.formData.PHSY || ''; |
| | | this.isInteger = true; |
| | | }, |
| | | editDestruction() { |
| | | // 验证输入是否为整数 |
| | | if(this.PHSY === '') { |
| | | this.isInteger = true; |
| | | // 如果为空,传递空字符串,后端会处理为null |
| | | } else { |
| | | const isInteger = /^-?\d+$/.test(this.PHSY); |
| | | this.isInteger = isInteger; |
| | | if (!isInteger) { |
| | | return; |
| | | } |
| | | } |
| | | |
| | | // 保存破坏实验数量 |
| | | this.$post({ |
| | | url: "/LLJ/saveRemarksGid", |
| | | data: { |
| | | gid: this.formData.id, |
| | | releaseNo: this.formData.releaseNo, |
| | | PHSY: this.PHSY // 可以是空字符串或有效整数 |
| | | // 不再包含remarks字段 |
| | | } |
| | | }).then(res => { |
| | | if (res.data.tbBillList > 0) { |
| | | // 如果输入为空,设置为null以便不显示 |
| | | this.formData.PHSY = this.PHSY === '' ? null : this.PHSY; |
| | | this.destructionPopup = false; |
| | | this.$showMessage("保存成功"); |
| | | setTimeout(() => { |
| | | this.init(); |
| | | }, 2000); |
| | | } |
| | | }) |
| | | }, |
| | | clearDestruction() { |
| | | // 清除破坏实验数量 |
| | | this.PHSY = ''; |
| | | this.$post({ |
| | | url: "/LLJ/saveRemarksGid", |
| | | data: { |
| | | gid: this.formData.id, |
| | | releaseNo: this.formData.releaseNo, |
| | | PHSY: '' // 空字符串,后端会处理为null |
| | | } |
| | | }).then(res => { |
| | | if (res.data.tbBillList > 0) { |
| | | this.formData.PHSY = null; // 确保前端也为null,不显示 |
| | | this.destructionPopup = false; |
| | | this.$showMessage("清除成功"); |
| | | setTimeout(() => { |
| | | this.init(); |
| | | }, 2000); |
| | | } |
| | | }) |
| | | }, |
| | | viewAttachmentInfo() { |
| | | this.showAttachmentPopup = true; // 先弹窗 |
| | | this.attachmentsLoading = true; |
| | | this.attachments = []; |
| | | this.$post({ |
| | | url: "/LLJ/getAttachments", |
| | | data: { releaseNo: this.formData.releaseNo } |
| | | }).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; |
| | | }, |
| | | getAttachmentUrl(item) { |
| | | // 去除所有空白字符(包括中英文空格、制表符等) |
| | | let fileName = item.fattach.replace(/[\s\u3000\r\n]+/g, '').trim(); |
| | | // 统一使用FTP下载接口,包含FTP服务器地址 |
| | | return this.$store.state.serverInfo.serverAPI + "/LLJ/DownloadFtpFile?itemNo=" + encodeURIComponent(item.itemNo) + "&fileName=" + encodeURIComponent(fileName) + "&ftpServer=" + encodeURIComponent(this.$store.state.serverInfo.ftpServer); |
| | | }, |
| | | showAttachmentDetailDialog(item) { |
| | | console.log('查看详情', item); |
| | | this.selectedAttachment = item; |
| | | this.showAttachmentPopup = false; |
| | | this.showAttachmentDetail = true; |
| | | console.log('showAttachmentDetail:', this.showAttachmentDetail); |
| | | }, |
| | | 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', // PDF文件 |
| | | 'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', // 图片文件 |
| | | 'txt', 'log', 'md', // 文本文件 |
| | | 'doc', 'docx', // Word文档 |
| | | 'xls', 'xlsx', // Excel表格 |
| | | 'ppt', 'pptx', // PowerPoint演示文稿 |
| | | 'csv' // CSV文件 |
| | | ].includes(ext); |
| | | }, |
| | | // 处理附件下载错误 |
| | | handleAttachmentError(item) { |
| | | uni.showModal({ |
| | | title: '下载失败', |
| | | content: `无法下载附件:${item.fattach}\n请检查网络连接或联系管理员`, |
| | | showCancel: false |
| | | }); |
| | | }, |
| | | // 预览FTP文件 |
| | | 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 + "/LLJ/PreviewFtpFile?itemNo=" + encodeURIComponent(item.itemNo) + "&fileName=" + encodeURIComponent(fileName) + "&ftpServer=" + encodeURIComponent(this.$store.state.serverInfo.ftpServer); |
| | | |
| | | // 根据文件类型进行不同的预览处理 |
| | | 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); |
| | | } |
| | | }, |
| | | |
| | | // 预览PDF文件 |
| | | previewPdfFile(url, fileName) { |
| | | // 先下载PDF文件,转为base64后预览 |
| | | uni.request({ |
| | | url: url, |
| | | method: 'GET', |
| | | responseType: 'arraybuffer', |
| | | success: (res) => { |
| | | if (res.statusCode === 200) { |
| | | const base64Data = uni.arrayBufferToBase64(res.data); |
| | | // 存储到全局变量 |
| | | getApp().globalData.tempPDF = base64Data; |
| | | uni.navigateTo({ |
| | | url: `/pages/fileView/pdfView` |
| | | }); |
| | | } else { |
| | | this.handlePreviewError(res.statusCode, fileName); |
| | | } |
| | | }, |
| | | fail: (error) => { |
| | | this.handlePreviewError(0, fileName, error.errMsg); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // 预览图片文件 |
| | | previewImageFile(url, fileName) { |
| | | // #ifdef APP-PLUS |
| | | // APP环境:先下载到本地再预览,避免网络图片加载问题 |
| | | uni.showLoading({ title: '加载图片...' }); |
| | | uni.downloadFile({ |
| | | url: url, |
| | | success: (res) => { |
| | | uni.hideLoading(); |
| | | if (res.statusCode === 200) { |
| | | // 使用本地临时路径 |
| | | uni.navigateTo({ |
| | | url: `/pages/fileView/imageView?url=${encodeURIComponent(res.tempFilePath)}` |
| | | }); |
| | | } else { |
| | | this.handlePreviewError(res.statusCode, fileName); |
| | | } |
| | | }, |
| | | fail: (error) => { |
| | | uni.hideLoading(); |
| | | this.handlePreviewError(0, fileName, error.errMsg); |
| | | } |
| | | }); |
| | | // #endif |
| | | |
| | | // #ifdef H5 || MP |
| | | // H5和小程序:直接使用网络URL |
| | | uni.navigateTo({ |
| | | url: `/pages/fileView/imageView?url=${encodeURIComponent(url)}` |
| | | }); |
| | | // #endif |
| | | }, |
| | | |
| | | // 预览文本文件 |
| | | previewTextFile(url, fileName) { |
| | | // 文本文件直接显示在弹窗中 |
| | | uni.showLoading({ title: '加载文件内容...' }); |
| | | uni.request({ |
| | | url: url, |
| | | method: 'GET', |
| | | success: (res) => { |
| | | uni.hideLoading(); |
| | | if (res.statusCode === 200) { |
| | | const fileType = this.getFileType(fileName); |
| | | |
| | | if (fileType === 'text') { |
| | | // 文本文件:显示内容 |
| | | this.showFilePreview(res.data, fileName); |
| | | } else if (fileType === 'image') { |
| | | // 图片文件:显示图片URL |
| | | this.showFilePreview(url, fileName); |
| | | } else { |
| | | // 其他文件类型:显示提示信息 |
| | | this.showFilePreview('', fileName); |
| | | } |
| | | } else { |
| | | this.handlePreviewError(res.statusCode, fileName); |
| | | } |
| | | }, |
| | | fail: (error) => { |
| | | uni.hideLoading(); |
| | | this.handlePreviewError(0, fileName, error.errMsg); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // 检测文件类型 |
| | | getFileType(fileName) { |
| | | const fileExt = fileName.split('.').pop().toLowerCase(); |
| | | |
| | | if (['txt', 'log', 'md', 'csv', 'json', 'xml'].includes(fileExt)) { |
| | | return 'text'; |
| | | } else if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(fileExt)) { |
| | | return 'image'; |
| | | } else if (['xls', 'xlsx', 'doc', 'docx', 'ppt', 'pptx'].includes(fileExt)) { |
| | | return 'excel'; |
| | | } else { |
| | | return 'unsupported'; |
| | | } |
| | | }, |
| | | |
| | | // 显示文件预览弹窗 |
| | | showFilePreview(content, fileName) { |
| | | this.previewContent = content; |
| | | this.previewTitle = fileName; |
| | | this.previewItemNo = this.selectedAttachment?.itemNo || ''; |
| | | this.previewType = this.getFileType(fileName); |
| | | this.showFilePreviewPopup = true; |
| | | }, |
| | | |
| | | // 关闭文件预览弹窗 |
| | | closeFilePreview() { |
| | | this.showFilePreviewPopup = false; |
| | | this.previewContent = ''; |
| | | this.previewTitle = ''; |
| | | this.previewItemNo = ''; |
| | | this.previewType = ''; |
| | | }, |
| | | |
| | | // 下载预览文件 |
| | | downloadPreviewFile() { |
| | | const item = { fattach: this.previewTitle, itemNo: this.previewItemNo }; |
| | | this.downloadAttachment(item); |
| | | this.closeFilePreview(); |
| | | }, |
| | | |
| | | // 预览Office文件 |
| | | previewOfficeFile(url, fileName) { |
| | | // 先检查Excel文件,使用专门的Excel预览页面 |
| | | const fileExt = fileName.split('.').pop().toLowerCase(); |
| | | if (['xls', 'xlsx'].includes(fileExt)) { |
| | | // Excel文件预览 |
| | | uni.request({ |
| | | url: url, |
| | | method: 'GET', |
| | | responseType: 'arraybuffer', |
| | | success: (res) => { |
| | | if (res.statusCode === 200) { |
| | | const base64Data = uni.arrayBufferToBase64(res.data); |
| | | // 存储 Base64 数据到本地存储 |
| | | uni.setStorageSync('excelBase64Data', base64Data); |
| | | uni.navigateTo({ |
| | | url: `/pages/fileView/excelView` |
| | | }); |
| | | } else { |
| | | this.handlePreviewError(res.statusCode, fileName); |
| | | } |
| | | }, |
| | | fail: (error) => { |
| | | this.handlePreviewError(0, fileName, error.errMsg); |
| | | } |
| | | }); |
| | | } else if (['doc', 'docx'].includes(fileExt)) { |
| | | // Word文件,尝试使用Word预览页面或者微软在线预览 |
| | | try { |
| | | const officePreviewUrl = `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(url)}`; |
| | | // 如果有webView页面,使用webView预览 |
| | | this.previewGenericFile(officePreviewUrl, fileName); |
| | | } catch (error) { |
| | | this.handlePreviewError(0, fileName, '不支持此Office文件类型的预览'); |
| | | } |
| | | } else { |
| | | // 其他Office文件,使用微软在线预览服务 |
| | | const officePreviewUrl = `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(url)}`; |
| | | this.previewGenericFile(officePreviewUrl, fileName); |
| | | } |
| | | }, |
| | | |
| | | // 通用文件预览 |
| | | previewGenericFile(url, fileName) { |
| | | // 由于没有通用的webView页面,显示提示并提供下载 |
| | | uni.showModal({ |
| | | title: '文件预览', |
| | | content: `文件 "${fileName}" 需要下载后查看,是否立即下载?`, |
| | | showCancel: true, |
| | | confirmText: '下载', |
| | | cancelText: '取消', |
| | | success: (res) => { |
| | | if (res.confirm) { |
| | | const item = { fattach: fileName, itemNo: this.selectedAttachment.itemNo }; |
| | | this.downloadAttachment(item); |
| | | } |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // 处理预览错误 |
| | | handlePreviewError(statusCode, fileName, errorMsg = '') { |
| | | let message = ''; |
| | | if (statusCode === 404) { |
| | | message = `文件 ${fileName} 在FTP服务器上不存在`; |
| | | } else if (statusCode === 0) { |
| | | message = `预览失败:${errorMsg}`; |
| | | } else { |
| | | message = `预览失败,状态码:${statusCode}`; |
| | | } |
| | | |
| | | uni.showModal({ |
| | | title: '预览失败', |
| | | content: message, |
| | | showCancel: true, |
| | | confirmText: '下载', |
| | | cancelText: '取消', |
| | | success: (res) => { |
| | | if (res.confirm) { |
| | | // 用户选择下载文件 |
| | | const item = { fattach: fileName, itemNo: this.selectedAttachment.itemNo }; |
| | | this.downloadAttachment(item); |
| | | } |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | downloadAttachment(item) { |
| | | // 去除所有空格、全角空格、回车、换行 |
| | | const fileName = item.fattach.replace(/[\s\u3000\r\n]+/g, '').trim(); |
| | | // 使用配置的服务器地址和FTP服务器地址 |
| | | const url = this.$store.state.serverInfo.serverAPI + "/LLJ/DownloadFtpFile?itemNo=" + encodeURIComponent(item.itemNo) + "&fileName=" + encodeURIComponent(fileName) + "&ftpServer=" + encodeURIComponent(this.$store.state.serverInfo.ftpServer); |
| | | |
| | | // 检查运行环境 |
| | | // #ifdef H5 |
| | | // H5环境:使用浏览器下载 |
| | | this.downloadFileInBrowser(url, fileName); |
| | | // #endif |
| | | |
| | | // #ifdef APP-PLUS |
| | | // APP环境:使用uni.downloadFile |
| | | this.downloadFileInApp(url, fileName); |
| | | // #endif |
| | | |
| | | // #ifdef MP |
| | | // 小程序环境:使用uni.downloadFile |
| | | this.downloadFileInApp(url, fileName); |
| | | // #endif |
| | | }, |
| | | |
| | | // 在浏览器中下载文件 |
| | | downloadFileInBrowser(url, fileName) { |
| | | uni.showLoading({ title: '正在准备下载...' }); |
| | | |
| | | // 方法1:创建隐藏的a标签下载 |
| | | try { |
| | | const link = document.createElement('a'); |
| | | link.href = url; |
| | | link.download = fileName; |
| | | link.style.display = 'none'; |
| | | document.body.appendChild(link); |
| | | link.click(); |
| | | document.body.removeChild(link); |
| | | |
| | | uni.hideLoading(); |
| | | uni.showToast({ |
| | | title: '下载已开始', |
| | | icon: 'success', |
| | | duration: 2000 |
| | | }); |
| | | } catch (error) { |
| | | console.log('a标签下载失败,尝试window.open方式:', error); |
| | | // 方法2:使用window.open |
| | | try { |
| | | window.open(url, '_blank'); |
| | | uni.hideLoading(); |
| | | uni.showToast({ |
| | | title: '下载已开始', |
| | | icon: 'success', |
| | | duration: 2000 |
| | | }); |
| | | } catch (error2) { |
| | | console.log('window.open下载失败,尝试fetch方式:', error2); |
| | | // 方法3:使用fetch下载 |
| | | this.downloadFileWithFetch(url, fileName); |
| | | } |
| | | } |
| | | }, |
| | | |
| | | // 使用fetch下载文件 |
| | | downloadFileWithFetch(url, fileName) { |
| | | fetch(url) |
| | | .then(response => { |
| | | if (!response.ok) { |
| | | throw new Error(`HTTP error! status: ${response.status}`); |
| | | } |
| | | return response.blob(); |
| | | }) |
| | | .then(blob => { |
| | | // 创建blob URL |
| | | const blobUrl = window.URL.createObjectURL(blob); |
| | | |
| | | // 创建下载链接 |
| | | const link = document.createElement('a'); |
| | | link.href = blobUrl; |
| | | link.download = fileName; |
| | | link.style.display = 'none'; |
| | | document.body.appendChild(link); |
| | | link.click(); |
| | | document.body.removeChild(link); |
| | | |
| | | // 释放blob URL |
| | | window.URL.revokeObjectURL(blobUrl); |
| | | |
| | | uni.hideLoading(); |
| | | uni.showToast({ |
| | | title: '下载成功', |
| | | icon: 'success', |
| | | duration: 2000 |
| | | }); |
| | | }) |
| | | .catch(error => { |
| | | console.error('Fetch下载失败:', error); |
| | | uni.hideLoading(); |
| | | if (error.message.includes('404')) { |
| | | uni.showModal({ |
| | | title: '文件不存在', |
| | | content: `该附件在FTP服务器上不存在`, |
| | | showCancel: false |
| | | }); |
| | | } else { |
| | | uni.showModal({ |
| | | title: '下载失败', |
| | | content: `下载失败: ${error.message}`, |
| | | showCancel: false |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // 在APP中下载文件 |
| | | downloadFileInApp(url, fileName) { |
| | | // #ifdef APP-PLUS |
| | | uni.showLoading({ title: '从FTP服务器下载中...' }); |
| | | |
| | | // Android 获取存储路径 |
| | | const saveDir = plus.os.name === 'Android' ? plus.io.convertLocalFileSystemURL('_downloads/') : plus.io.convertLocalFileSystemURL('_documents/'); |
| | | const filePath = `${saveDir}${fileName}`; |
| | | |
| | | const downloadTask = uni.downloadFile({ |
| | | url: url, |
| | | filePath: filePath, // 指定保存路径 |
| | | success: (res) => { |
| | | uni.hideLoading(); |
| | | if (res.statusCode === 200) { |
| | | const fileInfo = { |
| | | name: fileName, |
| | | path: res.filePath || filePath, |
| | | tempPath: res.tempFilePath |
| | | }; |
| | | |
| | | uni.showModal({ |
| | | title: '下载成功', |
| | | content: `文件已保存到:${fileInfo.path}`, |
| | | showCancel: true, |
| | | confirmText: '打开文件', |
| | | cancelText: '确定', |
| | | success: (modalRes) => { |
| | | if (modalRes.confirm) { |
| | | // 用户选择打开文件 |
| | | this.openFileInApp(fileInfo); |
| | | } |
| | | } |
| | | }); |
| | | } else if (res.statusCode === 404) { |
| | | uni.showModal({ |
| | | title: '文件不存在', |
| | | content: `该附件在FTP服务器上不存在`, |
| | | showCancel: false |
| | | }); |
| | | } else { |
| | | uni.showModal({ |
| | | title: '下载失败', |
| | | content: `状态码:${res.statusCode}`, |
| | | showCancel: false |
| | | }); |
| | | } |
| | | }, |
| | | fail: (error) => { |
| | | uni.hideLoading(); |
| | | console.error('下载失败:', error); |
| | | uni.showModal({ |
| | | title: '下载失败', |
| | | content: `网络错误:${error.errMsg}`, |
| | | showCancel: false |
| | | }); |
| | | } |
| | | }); |
| | | |
| | | // 监听下载进度 |
| | | downloadTask.onProgressUpdate((res) => { |
| | | const progress = Math.round(res.progress); |
| | | uni.showLoading({ |
| | | title: `下载中 ${progress}%`, |
| | | mask: true |
| | | }); |
| | | }); |
| | | // #endif |
| | | |
| | | // #ifdef MP |
| | | // 小程序环境的简化实现 |
| | | uni.showLoading({ title: '下载中...' }); |
| | | uni.downloadFile({ |
| | | url: url, |
| | | success: (res) => { |
| | | uni.hideLoading(); |
| | | if (res.statusCode === 200) { |
| | | uni.showToast({ title: '下载完成', icon: 'success' }); |
| | | } |
| | | }, |
| | | fail: (error) => { |
| | | uni.hideLoading(); |
| | | uni.showModal({ title: '下载失败', content: error.errMsg, showCancel: false }); |
| | | } |
| | | }); |
| | | // #endif |
| | | }, |
| | | |
| | | // APP中打开文件 |
| | | openFileInApp(fileInfo) { |
| | | // #ifdef APP-PLUS |
| | | if (typeof plus !== 'undefined') { |
| | | const filePath = fileInfo.path || fileInfo.tempPath; |
| | | |
| | | // 尝试打开文件 |
| | | plus.runtime.openFile(filePath, {}, (error) => { |
| | | console.error('打开文件失败:', error); |
| | | uni.showModal({ |
| | | title: '无法打开', |
| | | content: '系统中没有找到能打开此文件的应用程序', |
| | | showCancel: false |
| | | }); |
| | | }); |
| | | } |
| | | // #endif |
| | | }, |
| | | |
| | | } |
| | | } |
| | | </script> |
| | |
| | | font-family: 'Microsoft YaHei', 'Segoe UI', sans-serif; |
| | | max-width: 1000px; |
| | | margin: 0 auto; |
| | | padding: 20px; |
| | | padding: 20px 20px 160px 20px; /* 底部增加padding为固定按钮留空间 */ |
| | | background-color: #fff; |
| | | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); |
| | | min-height: 100vh; |
| | | position: relative; |
| | | } |
| | | |
| | | /* 头部样式 */ |
| | |
| | | background-color: #f1f5f9; |
| | | } |
| | | |
| | | /* 按钮样式 */ |
| | | /* 固定底部按钮样式 */ |
| | | .fixed-action-buttons { |
| | | position: fixed; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | background-color: #fff; |
| | | box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1); |
| | | padding: 10px 15px 20px 15px; |
| | | z-index: 100; |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 8px; |
| | | max-height: 150px; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .action-btn { |
| | | background-color: #ecf0f1; |
| | | color: #34495e; |
| | | padding: 12px 15px; |
| | | border: none; |
| | | border-radius: 6px; |
| | | cursor: pointer; |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | transition: all 0.3s ease; |
| | | text-align: center; |
| | | min-height: 44px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .action-btn:hover { |
| | | background-color: #d5dbdb; |
| | | transform: translateY(-1px); |
| | | } |
| | | |
| | | .action-btn.primary { |
| | | background-color: #3498db; |
| | | color: #fff; |
| | | } |
| | | |
| | | .action-btn.primary:hover { |
| | | background-color: #2980b9; |
| | | } |
| | | |
| | | /* 内容包装器,为底部按钮留出空间 */ |
| | | .content-wrapper { |
| | | height: 20px; /* 额外的空白区域 */ |
| | | } |
| | | |
| | | /* 原有按钮样式保持兼容 */ |
| | | .action-buttons { |
| | | display: flex; |
| | | gap: 10px; |
| | |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | z-index: 10; |
| | | z-index: 1000; /* 提高层级,确保在固定按钮上方 */ |
| | | } |
| | | |
| | | .popup { |
| | | background-color: #fff; |
| | | padding: 20px; |
| | | border: 1px solid #ccc; |
| | | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); |
| | | width: 68vw; |
| | | /* 设置宽度为视口宽度的80% */ |
| | | height: 25vh; |
| | | /* 设置高度为视口高度的80% */ |
| | | /* 弹窗整体美化 */ |
| | | .popup, .attachment-detail-popup { |
| | | background: #fff; |
| | | border-radius: 16px; |
| | | box-shadow: 0 8px 32px rgba(60,60,60,0.18); |
| | | padding: 28px 28px 18px 28px; |
| | | border: none; |
| | | position: relative; |
| | | min-width: 260px; |
| | | z-index: 1001; /* 确保弹窗内容在最上层 */ |
| | | max-height: 80vh; /* 限制最大高度,避免被底部按钮遮挡 */ |
| | | overflow-y: auto; /* 内容过多时可滚动 */ |
| | | } |
| | | |
| | | .form-group { |
| | | .attachment-popup-title { |
| | | font-size: 22px; |
| | | font-weight: 700; |
| | | color: #222; |
| | | margin-bottom: 8px; |
| | | letter-spacing: 1px; |
| | | text-align: center; |
| | | } |
| | | .attachment-popup-divider { |
| | | height: 1px; |
| | | background: linear-gradient(90deg,#e0e7ef 0%,#f5f7fa 100%); |
| | | margin-bottom: 18px; |
| | | } |
| | | .attachment-detail-content { |
| | | margin-bottom: 18px; |
| | | } |
| | | .attachment-detail-row { |
| | | display: flex; |
| | | align-items: center; |
| | | border-bottom: 1px solid #c9c9c9; |
| | | margin-bottom: 8px; |
| | | font-size: 15px; |
| | | } |
| | | |
| | | .updateBut { |
| | | background-color: #3498db; |
| | | .attachment-label { |
| | | min-width: 80px; |
| | | color: #1976d2; |
| | | font-weight: 500; |
| | | margin-right: 8px; |
| | | } |
| | | .attachment-preview-area { |
| | | margin: 18px 0 8px 0; |
| | | border-radius: 10px; |
| | | background: #f8fafc; |
| | | padding: 10px; |
| | | box-shadow: 0 2px 8px rgba(60,60,60,0.06); |
| | | } |
| | | .attachment-download-area { |
| | | margin: 18px 0 8px 0; |
| | | text-align: center; |
| | | } |
| | | .attachment-download-link { |
| | | display: inline-block; |
| | | padding: 7px 18px; |
| | | background: linear-gradient(90deg,#4f8cff 0%,#1976d2 100%); |
| | | color: #fff; |
| | | border-radius: 6px; |
| | | font-weight: 500; |
| | | text-decoration: none; |
| | | transition: background 0.2s; |
| | | box-shadow: 0 2px 8px rgba(60,60,60,0.08); |
| | | } |
| | | .attachment-download-link:hover { |
| | | background: linear-gradient(90deg,#1976d2 0%,#4f8cff 100%); |
| | | } |
| | | .attachment-detail-empty { |
| | | color: #888; |
| | | text-align: center; |
| | | margin: 30px 0; |
| | | font-size: 16px; |
| | | } |
| | | .attachment-popup-close { |
| | | margin-top: 18px; |
| | | width: 100%; |
| | | background: linear-gradient(90deg,#e0e0e0 0%,#f5f7fa 100%); |
| | | color: #444; |
| | | border-radius: 8px; |
| | | font-size: 16px; |
| | | padding: 10px 0; |
| | | border: none; |
| | | font-weight: 600; |
| | | letter-spacing: 1px; |
| | | transition: background 0.2s, color 0.2s; |
| | | box-shadow: 0 2px 8px rgba(60,60,60,0.06); |
| | | } |
| | | .attachment-popup-close:hover { |
| | | background: linear-gradient(90deg,#bdbdbd 0%,#e0e0e0 100%); |
| | | color: #1976d2; |
| | | } |
| | | |
| | | /* 附件详情页面的操作按钮 */ |
| | | .attachment-actions-detail { |
| | | margin: 20px 0; |
| | | display: flex; |
| | | gap: 12px; |
| | | justify-content: center; |
| | | flex-wrap: wrap; |
| | | } |
| | | .attachment-action-btn { |
| | | padding: 10px 20px; |
| | | border: none; |
| | | border-radius: 8px; |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | cursor: pointer; |
| | | transition: all 0.3s ease; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | min-width: 120px; |
| | | box-shadow: 0 2px 4px rgba(0,0,0,0.1); |
| | | } |
| | | .attachment-action-btn.preview-btn { |
| | | background: linear-gradient(135deg, #4CAF50, #45a049); |
| | | color: white; |
| | | } |
| | | |
| | | /* 响应式设计 */ |
| | | @media (max-width: 500px) { |
| | | |
| | | .info-row, |
| | | .info-block { |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | } |
| | | |
| | | .doc-links { |
| | | margin-left: 0; |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | .action-buttons { |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .inspection-table table { |
| | | display: block; |
| | | overflow-x: auto; |
| | | } |
| | | |
| | | .click-wd { |
| | | color: #056cba; |
| | | font-size: 1.25rem; |
| | | text-decoration: underline; |
| | | } |
| | | |
| | | .attachment-action-btn.preview-btn:hover { |
| | | background: linear-gradient(135deg, #45a049, #3d8b40); |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 4px 8px rgba(0,0,0,0.15); |
| | | } |
| | | .attachment-action-btn.download-btn { |
| | | background: linear-gradient(135deg, #2196F3, #1976D2); |
| | | color: white; |
| | | } |
| | | .attachment-action-btn.download-btn:hover { |
| | | background: linear-gradient(135deg, #1976D2, #1565C0); |
| | | transform: translateY(-1px); |
| | | box-shadow: 0 4px 8px rgba(0,0,0,0.15); |
| | | } |
| | | |
| | | /* 文件预览弹窗样式 */ |
| | | .file-preview-popup { |
| | | width: 80vw; |
| | | max-width: 600px; |
| | | max-height: 70vh; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | /* APP环境适配 */ |
| | | /* #ifdef APP-PLUS */ |
| | | .file-preview-popup { |
| | | width: 85vw; |
| | | max-height: 75vh; |
| | | } |
| | | .file-preview-content { |
| | | max-height: 350px; |
| | | } |
| | | /* #endif */ |
| | | .file-preview-title { |
| | | font-size: 18px; |
| | | font-weight: 700; |
| | | color: #222; |
| | | margin-bottom: 8px; |
| | | text-align: center; |
| | | word-break: break-all; |
| | | } |
| | | .file-preview-divider { |
| | | height: 1px; |
| | | background: linear-gradient(90deg,#e0e7ef 0%,#f5f7fa 100%); |
| | | margin-bottom: 16px; |
| | | } |
| | | .file-preview-content { |
| | | flex: 1; |
| | | max-height: 400px; |
| | | overflow-y: auto; |
| | | background: #f8fafc; |
| | | border-radius: 8px; |
| | | padding: 16px; |
| | | margin-bottom: 16px; |
| | | border: 1px solid #e2e8f0; |
| | | } |
| | | .file-preview-content 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; |
| | | justify-content: center; |
| | | align-items: center; |
| | | min-height: 200px; |
| | | } |
| | | |
| | | /* 不支持文件类型的提示样式 */ |
| | | .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; |
| | | } |
| | | .file-preview-btn.download-btn { |
| | | background: linear-gradient(135deg, #2196F3, #1976D2); |
| | | color: white; |
| | | } |
| | | .file-preview-btn.download-btn:hover { |
| | | background: linear-gradient(135deg, #1976D2, #1565C0); |
| | | transform: translateY(-1px); |
| | | } |
| | | .file-preview-btn.close-btn { |
| | | background: linear-gradient(135deg, #e0e0e0, #bdbdbd); |
| | | color: #444; |
| | | } |
| | | .file-preview-btn.close-btn:hover { |
| | | background: linear-gradient(135deg, #bdbdbd, #9e9e9e); |
| | | transform: translateY(-1px); |
| | | } |
| | | /* 列表弹窗美化(保留原有) */ |
| | | .attachment-list { |
| | | padding: 0; |
| | | margin: 0; |
| | | list-style: none; |
| | | max-height: 300px; |
| | | overflow-y: auto; |
| | | } |
| | | .attachment-list li { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | padding: 12px 0; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | .attachment-info { |
| | | flex: 1; |
| | | margin-right: 10px; |
| | | } |
| | | .attachment-name { |
| | | color: #3498db; |
| | | cursor: pointer; |
| | | font-weight: 500; |
| | | transition: color 0.2s; |
| | | display: block; |
| | | margin-bottom: 4px; |
| | | } |
| | | .attachment-name:hover { |
| | | color: #217dbb; |
| | | text-decoration: underline; |
| | | } |
| | | .attachment-meta { |
| | | font-size: 12px; |
| | | } |
| | | .attachment-type { |
| | | color: #7f8c8d; |
| | | font-style: italic; |
| | | } |
| | | .attachment-actions { |
| | | display: flex; |
| | | gap: 8px; |
| | | flex-shrink: 0; |
| | | } |
| | | .attachment-list .secondary-btn { |
| | | padding: 4px 10px; |
| | | font-size: 13px; |
| | | border-radius: 3px; |
| | | background: #f5f7fa; |
| | | color: #333; |
| | | border: 1px solid #dbe2ea; |
| | | transition: background 0.2s, color 0.2s; |
| | | } |
| | | .attachment-list .secondary-btn:hover { |
| | | background: #e6f0fa; |
| | | color: #1976d2; |
| | | } |
| | | .preview-btn { |
| | | background: #e8f5e8 !important; |
| | | color: #2e7d2e !important; |
| | | border-color: #a5d6a5 !important; |
| | | } |
| | | .preview-btn:hover { |
| | | background: #d4eecc !important; |
| | | color: #1e5f1e !important; |
| | | } |
| | | .attachment-popup-close { |
| | | margin-top: 18px; |
| | | width: 100%; |
| | | background: #e0e0e0; |
| | | color: #444; |
| | | border-radius: 4px; |
| | | font-size: 15px; |
| | | padding: 8px 0; |
| | | border: none; |
| | | transition: background 0.2s; |
| | | } |
| | | .attachment-popup-close:hover { |
| | | background: #bdbdbd; |
| | | } |
| | | </style> |