xwt
2025-10-30 4a9c0f7ddb5eae77b1f833bd2223e33fe6bb2918
pages/QC/LLJ/detail.vue
@@ -75,11 +75,19 @@
                    <text class="spec-text">{{ formData.fspecRequ }}</text>
                </view>
            </view>
            <!-- 规格要求 -->
            <!-- 检验描述 -->
            <view class="section">
                <view class="section-header">检验描述</view>
                <view class="section-body">
                    <text class="spec-text">{{ formData.fcheckItemDesc }}</text>
                    <!-- 查看附件信息按钮 -->
                    <view class="attachment-btn-container">
                        <button class="btn attachment-btn" @tap="viewAttachmentInfo">
                            <uni-icons type="folder" size="16" color="#fff"></uni-icons>
                            查看附件信息
                        </button>
                    </view>
                </view>
            </view>
            <!-- 检验结果 -->
@@ -139,6 +147,7 @@
                                 <uni-icons type="upload" size="16" color="#fff"></uni-icons>
                                 上传/查看图片
                             </button>
                             <button v-if="this.current" class="btn upload-btn" @tap="upRemarks">
                                 <uni-icons type="compose" size="16" color="#fff"></uni-icons>
                                 不良描述
@@ -184,6 +193,7 @@
                                 <uni-icons type="upload" size="16" color="#fff"></uni-icons>
                                 上传/查看图片
                             </button>
                             <button v-if="this.current" class="btn upload-btn" @tap="upRemarks">
                                 <uni-icons type="compose" size="16" color="#fff"></uni-icons>
                                 不良描述
@@ -322,7 +332,169 @@
                    </form>
                </view>
            </view>
            <!-- 附件列表弹窗 -->
            <view v-if="showAttachmentPopup" class="overlay">
                <view class="popup attachment-list-popup">
                    <div class="attachment-popup-header">
                        <h3 class="attachment-popup-title">附件列表</h3>
                        <button class="attachment-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 class="empty-hint">该物料暂未上传任何附件</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="meta-type">{{ item.ftype || '未知类型' }}</span>
                                            <span v-if="item.fversion" class="meta-version">v{{ item.fversion }}</span>
                                            <span v-if="item.fdate" class="meta-date">{{ formatDate(item.fdate) }}</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 class="attachment-popup-title">附件详情</h3>
                        <button class="attachment-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 v-else class="attachment-detail-empty">
                            <div class="empty-icon">❌</div>
                            <div class="empty-text">暂无附件信息</div>
                        </div>
                    </div>
                </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="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>
    </view>
</template>
@@ -357,6 +529,17 @@
                showMeom:false,
                meom: '',
                isFocus: false, // 新增,控制输入框聚焦
                // ===== 附件相关数据 =====
                showAttachmentPopup: false,
                showAttachmentDetail: false,
                showFilePreviewPopup: false,
                attachments: [],
                attachmentsLoading: false,
                selectedAttachment: null,
                previewTitle: '',
                previewContent: '',
                previewType: '',
                previewFileUrl: ''
            }
        },
        computed: {
@@ -664,6 +847,280 @@
                    url: 'ImageItem?id=' + this.formData.id
                });
            },
            // ===== 附件相关方法 =====
            viewAttachmentInfo() {
                this.showAttachmentPopup = true;
                this.attachmentsLoading = true;
                this.attachments = [];
                // fversion 的值是当前检验项目名称
                // 使用当前激活标签的 fcheckItem:tabs[currentTab].fcheckItem
                const currentTabData = this.tabs[this.currentTab];
                const fversion = currentTabData && currentTabData.fcheckItem ? currentTabData.fcheckItem : '';
                console.log("========================================");
                console.log("调用附件接口:");
                console.log("检验单号 releaseNo:", this.releaseNo);
                console.log("当前标签索引 currentTab:", this.currentTab);
                console.log("当前标签数据:", currentTabData);
                console.log("检验项目名称 fcheckItem:", currentTabData ? currentTabData.fcheckItem : '');
                console.log("版本号 fversion (检验项目名称):", fversion);
                console.log("========================================");
                // 先通过 releaseNo 获取物料编码
                this.$post({
                    url: "/LLJ/getPage",
                    data: {
                        PageIndex: 1,
                        Limit: 1,
                        selectedIndex: 5,  // 5表示按检验单号搜索
                        SearchValue: this.releaseNo,
                        createUser: this.$loginInfo.account,
                        UserIndex: this.$loginInfo.userIndex || "0"
                    }
                }).then(res => {
                    let tbBillListElement = res.data.tbBillList[0];
                    if (tbBillListElement && tbBillListElement.itemNo) {
                        const itemNo = tbBillListElement.itemNo;
                        console.log("获取到的物料编码:", itemNo);
                        console.log("最终调用 getAttachments 参数:", { itemNo, fversion, fromPage: 'Detail' });
                        // 调用附件接口
                        return this.$post({
                            url: "/LLJ/getAttachments",
                            data: {
                                itemNo: itemNo,
                                fversion: fversion,  // 使用检验项目ID作为版本号
                                fromPage: 'Detail'
                            }
                        });
                    } else {
                        throw new Error("未找到物料编码信息");
                    }
                }).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" });
                    }
                }).catch(err => {
                    this.attachmentsLoading = false;
                    console.error("获取附件失败:", err);
                    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': '📊',
                    'zip': '📦', 'rar': '📦', '7z': '📦',
                    'dwg': '🏗️', 'dxf': '🏗️'
                };
                return iconMap[ext] || '📄';
            },
            // 获取文件类型CSS类
            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',
                    'zip': 'file-archive', 'rar': 'file-archive', '7z': 'file-archive',
                    'dwg': 'file-cad', 'dxf': 'file-cad'
                };
                return classMap[ext] || 'file-unknown';
            },
            // 格式化日期
            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 (e) {
                    return dateString;
                }
            },
            downloadAttachment(item) {
                const fileName = item.fattach.replace(/[\s\u3000\r\n]+/g, '').trim();
                const downloadUrl = this.$store.state.serverInfo.serverAPI + "/LLJ/DownloadFtpFile?itemNo=" +
                    encodeURIComponent(item.itemNo) + "&fileName=" + encodeURIComponent(fileName) +
                    "&ftpServer=" + encodeURIComponent(this.$store.state.serverInfo.ftpServer) +
                    "&fversion=" + encodeURIComponent(item.fversion || '');
                uni.downloadFile({
                    url: downloadUrl,
                    success: (res) => {
                        if (res.statusCode === 200) {
                            uni.showToast({ title: '下载成功', icon: 'success' });
                        } else {
                            uni.showToast({ title: '下载失败', icon: 'none' });
                        }
                    },
                    fail: (err) => {
                        console.error('下载失败:', err);
                        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 + "/LLJ/PreviewFtpFile?itemNo=" +
                    encodeURIComponent(item.itemNo) + "&fileName=" + encodeURIComponent(fileName) +
                    "&ftpServer=" + encodeURIComponent(this.$store.state.serverInfo.ftpServer) +
                    "&fversion=" + encodeURIComponent(item.fversion || '');
                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 {
                    this.previewOfficeFile(previewUrl, fileName);
                }
            },
            previewPdfFile(url, fileName) {
                this.previewTitle = fileName;
                this.previewContent = url;
                this.previewType = 'pdf';
                this.previewFileUrl = url;
                this.showFilePreviewPopup = true;
                this.showAttachmentDetail = false;
            },
            previewImageFile(url, fileName) {
                this.previewTitle = fileName;
                this.previewContent = url;
                this.previewType = 'image';
                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) => {
                        this.previewContent = res.data;
                    },
                    fail: (err) => {
                        this.previewContent = '预览失败,请下载后查看';
                    }
                });
            },
            previewOfficeFile(url, fileName) {
                this.previewTitle = fileName;
                this.previewContent = '';
                this.previewType = 'excel';
                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
                    });
                }
            },
            upRemarks() {
                this.remarksPopup = true;
            },
@@ -1311,4 +1768,434 @@
        font-weight: bold;
        font-size: 12px;
    }
    /* 附件按钮样式 */
    .attachment-btn-container {
        margin-top: 16px;
        display: flex;
        justify-content: flex-start;
    }
    .attachment-btn {
        background-color: #17a2b8;
        color: #fff;
        padding: 10px 20px;
        margin: 0;
        display: flex;
        align-items: center;
        gap: 8px;
        border-radius: 4px;
        font-size: 14px;
        transition: all 0.2s;
    }
    .attachment-btn:hover {
        background-color: #138496;
        transform: translateY(-1px);
        box-shadow: 0 2px 8px rgba(23, 162, 184, 0.3);
    }
    /* 附件相关样式 */
    .attachment-list-popup {
        width: 80vw;
        max-width: 800px;
        max-height: 85vh;
    }
    .attachment-detail-popup {
        width: 70vw;
        max-width: 600px;
    }
    .attachment-popup-header {
        padding: 16px;
        border-bottom: 1px solid #eee;
        display: flex;
        justify-content: space-between;
        align-items: center;
        background-color: white;
    }
    .attachment-popup-title {
        font-size: 16px;
        font-weight: 600;
        color: #2c3e50;
        margin: 0;
    }
    .attachment-close-btn {
        padding: 8px 16px;
        border: 1px solid #ddd;
        border-radius: 4px;
        background-color: white;
        font-size: 14px;
        transition: all 0.2s;
        color: #2c3e50;
    }
    .attachment-close-btn:hover {
        background-color: #f8f9fa;
    }
    .attachment-popup-content {
        padding: 16px;
        max-height: 60vh;
        overflow-y: auto;
    }
    /* 加载状态 */
    .attachment-loading {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        padding: 40px 20px;
        gap: 16px;
    }
    .loading-spinner {
        width: 32px;
        height: 32px;
        border: 3px solid #f3f3f3;
        border-top: 3px solid #3498db;
        border-radius: 50%;
        animation: spin 1s linear infinite;
    }
    @keyframes spin {
        0% { transform: rotate(0deg); }
        100% { transform: rotate(360deg); }
    }
    .loading-text {
        font-size: 14px;
        color: #7f8c8d;
    }
    /* 空状态 */
    .attachment-empty {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        padding: 40px 20px;
        gap: 12px;
        text-align: center;
    }
    .empty-icon {
        font-size: 36px;
        opacity: 0.6;
    }
    .empty-text {
        font-size: 16px;
        color: #7f8c8d;
        font-weight: 500;
    }
    .empty-hint {
        font-size: 14px;
        color: #95a5a6;
    }
    /* 附件列表布局 */
    .attachment-list {
        display: flex;
        flex-direction: column;
        gap: 16px;
    }
    .attachment-item {
        background-color: white;
        border-radius: 8px;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
        overflow: hidden;
        transition: all 0.3s;
        border: 1px solid #eee;
    }
    .attachment-item:hover {
        box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
    }
    .attachment-info {
        padding: 16px;
        border-bottom: 1px solid #eee;
        display: flex;
        align-items: center;
        gap: 16px;
    }
    .file-type-badge {
        width: 40px;
        height: 40px;
        border-radius: 8px;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 20px;
        background: #f8f9fa;
        border: 2px solid #e9ecef;
        flex-shrink: 0;
    }
    .file-type-badge.large {
        width: 56px;
        height: 56px;
        font-size: 28px;
    }
    .file-type-badge.file-pdf { background: #ffe6e6; border-color: #ffcccc; }
    .file-type-badge.file-image { background: #e6f3ff; border-color: #cce7ff; }
    .file-type-badge.file-text { background: #e6ffe6; border-color: #ccffcc; }
    .file-type-badge.file-word { background: #e6f0ff; border-color: #cce0ff; }
    .file-type-badge.file-excel { background: #e6ffe6; border-color: #ccffcc; }
    .file-type-badge.file-powerpoint { background: #fff0e6; border-color: #ffe0cc; }
    .file-type-badge.file-archive { background: #f0e6ff; border-color: #e0ccff; }
    .file-type-badge.file-cad { background: #e6fff0; border-color: #ccffe0; }
    .file-type-badge.file-unknown { background: #f5f5f5; border-color: #e0e0e0; }
    .attachment-details {
        flex: 1;
        min-width: 0;
    }
    .attachment-name {
        font-size: 16px;
        font-weight: 600;
        color: #2c3e50;
        cursor: pointer;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        margin-bottom: 8px;
        transition: color 0.2s;
    }
    .attachment-name:hover {
        color: #3498db;
    }
    .attachment-meta {
        display: flex;
        gap: 16px;
        font-size: 12px;
        color: #95a5a6;
    }
    .meta-type {
        background-color: #ecf0f1;
        padding: 2px 6px;
        border-radius: 10px;
        color: #7f8c8d;
    }
    .meta-version {
        background-color: #e8f5e8;
        padding: 2px 6px;
        border-radius: 10px;
        color: #2e7d32;
    }
    .meta-date {
        background-color: #fff3e0;
        padding: 2px 6px;
        border-radius: 10px;
        color: #f57c00;
    }
    .attachment-actions {
        padding: 12px 16px;
        border-top: 1px solid #eee;
        display: flex;
        gap: 8px;
        background-color: #f8f9fa;
    }
    /* 按钮样式 */
    .btn-secondary {
        padding: 8px 16px;
        border: 1px solid #ddd;
        border-radius: 4px;
        background-color: white;
        font-size: 14px;
        transition: all 0.2s;
        color: #2c3e50;
        flex: 1;
    }
    .btn-secondary:hover {
        background-color: #f8f9fa;
    }
    .btn-primary {
        padding: 8px 16px;
        border: 1px solid #3498db;
        border-radius: 4px;
        background-color: #3498db;
        color: white;
        font-size: 14px;
        transition: all 0.2s;
        flex: 1;
    }
    .btn-primary:hover {
        background-color: #2980b9;
    }
    .btn-success {
        padding: 8px 16px;
        border: 1px solid #2ecc71;
        border-radius: 4px;
        background-color: #2ecc71;
        color: white;
        font-size: 14px;
        transition: all 0.2s;
        flex: 1;
    }
    .btn-success:hover {
        background-color: #27ae60;
    }
    /* 附件详情样式 */
    .attachment-detail-header {
        display: flex;
        align-items: center;
        gap: 20px;
        margin-bottom: 20px;
        padding-bottom: 16px;
        border-bottom: 1px solid #eee;
    }
    .attachment-detail-title {
        font-size: 18px;
        font-weight: 600;
        color: #2c3e50;
        flex: 1;
        word-break: break-all;
    }
    .attachment-detail-info {
        margin-bottom: 20px;
    }
    .info-row {
        display: flex;
        margin-bottom: 12px;
        gap: 16px;
    }
    .info-content {
        font-size: 14px;
        color: #2c3e50;
        line-height: 1.5;
    }
    .attachment-detail-actions {
        padding: 12px 16px;
        border-top: 1px solid #eee;
        display: flex;
        gap: 8px;
        background-color: #f8f9fa;
    }
    /* 文件预览弹窗样式 */
    .file-preview-popup {
        width: 80vw;
        max-width: 800px;
        max-height: 80vh;
    }
    .file-preview-title {
        padding: 20px;
        margin: 0;
        font-size: 16px;
        font-weight: 600;
        color: #2c3e50;
        border-bottom: 1px solid #eee;
        word-break: break-all;
    }
    .file-preview-content {
        padding: 20px;
        max-height: 60vh;
        overflow-y: auto;
    }
    .file-preview-content pre {
        white-space: pre-wrap;
        word-wrap: break-word;
        font-family: 'Courier New', monospace;
        font-size: 12px;
        line-height: 1.4;
        background: #f8f9fa;
        padding: 15px;
        border-radius: 4px;
        border: 1px solid #e9ecef;
    }
    .image-preview-container {
        text-align: center;
    }
    .image-zoom-hint {
        margin-top: 10px;
        font-size: 12px;
        color: #7f8c8d;
    }
    .unsupported-preview {
        text-align: center;
        padding: 40px 20px;
    }
    .unsupported-icon {
        font-size: 48px;
        margin-bottom: 16px;
    }
    .unsupported-text {
        font-size: 16px;
        color: #7f8c8d;
        margin-bottom: 8px;
    }
    .unsupported-hint {
        font-size: 14px;
        color: #95a5a6;
    }
    .file-preview-actions {
        padding: 15px 20px;
        border-top: 1px solid #eee;
        display: flex;
        gap: 10px;
        justify-content: center;
    }
    .file-preview-btn {
        padding: 8px 16px;
        border-radius: 4px;
        border: none;
        font-size: 14px;
        cursor: pointer;
        transition: all 0.2s;
    }
    .file-preview-btn.download-btn {
        background: #2ecc71;
        color: white;
    }
    .file-preview-btn.close-btn {
        background: #95a5a6;
        color: white;
    }
    .file-preview-btn:hover {
        transform: translateY(-1px);
        box-shadow: 0 2px 8px rgba(0,0,0,0.15);
    }
</style>