From afeeed281e60466b576fbe74d339634cc5d07b82 Mon Sep 17 00:00:00 2001 From: Codex Assistant <codex@example.com> Date: 星期三, 08 十月 2025 08:56:42 +0800 Subject: [PATCH] 修复评审功能和用户认证问题 --- web/src/views/check-detail.vue | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 153 insertions(+), 1 deletions(-) diff --git a/web/src/views/check-detail.vue b/web/src/views/check-detail.vue index 139047a..d60d400 100644 --- a/web/src/views/check-detail.vue +++ b/web/src/views/check-detail.vue @@ -115,6 +115,13 @@ <el-button type="primary" size="small" + @click="previewAttachment(attachment)" + > + 棰勮 + </el-button> + <el-button + type="primary" + size="small" @click="downloadAttachment(attachment)" > 涓嬭浇 @@ -130,7 +137,24 @@ <div class="card-header"> <span>瀹℃牳绠$悊</span> </div> - </template> + <!-- 闄勪欢棰勮瀵硅瘽妗� --> + <el-dialog v-model="previewVisible" title="鏂囦欢棰勮" width="80%" center> + <div class="preview-content"> + <!-- 鍥剧墖棰勮 --> + <img v-if="previewType === 'image' && previewUrl" :src="previewUrl" style="max-width: 100%; max-height: 70vh; object-fit: contain;" /> + <!-- 瑙嗛棰勮 --> + <video v-else-if="previewType === 'video' && previewUrl" :src="previewUrl" controls style="width: 100%; max-height: 70vh;"></video> + <!-- PDF 棰勮 --> + <iframe v-else-if="previewType === 'pdf' && previewUrl" :src="previewUrl" style="width: 100%; height: 70vh; border: none;"></iframe> + <!-- DOCX 棰勮 --> + <div v-else-if="previewType === 'docx'" ref="docxContainer" class="docx-preview"></div> + <!-- 鍏跺畠涓嶆敮鎸� --> + <div v-else class="preview-error"> + <el-empty description="鏃犳硶棰勮姝ゆ枃浠剁被鍨嬶紝璇蜂笅杞芥煡鐪�" /> + </div> + </div> + </el-dialog> +</template> <div class="review-section"> <div class="review-status"> @@ -223,6 +247,12 @@ const playerData = ref<any>(null) const activityPlayerData = ref<any>(null) const attachments = ref<any[]>([]) + +// 棰勮鐩稿叧 +const previewVisible = ref(false) +const previewUrl = ref('') +const previewType = ref<'' | 'image' | 'video' | 'pdf' | 'docx' | 'unknown'>('') +const docxContainer = ref<HTMLElement | null>(null) // 瀹℃牳鐩稿叧鏁版嵁 const feedbackText = ref('') @@ -422,6 +452,113 @@ const downloadAttachment = (attachment: any) => { // TODO: 瀹炵幇闄勪欢涓嬭浇鍔熻兘 window.open(attachment.url, '_blank') +} + +/** + * 棰勮闄勪欢锛氭寜鎵╁睍鍚嶅垎鍙� 鍥剧墖/瑙嗛/PDF/DOCX + */ +const previewAttachment = async (attachment: any) => { + const name: string = attachment.originalName || attachment.name || '' + const url: string = attachment.url + const ext = (name.split('.').pop() || '').toLowerCase() + + previewVisible.value = true + previewUrl.value = '' + previewType.value = '' + + const imageExts = ['jpg','jpeg','png','gif','bmp','webp'] + const videoExts = ['mp4','webm','ogg','avi','mov','wmv','flv','mkv'] + + if (imageExts.includes(ext)) { + previewType.value = 'image' + previewUrl.value = url + return + } + if (videoExts.includes(ext)) { + previewType.value = 'video' + previewUrl.value = url + return + } + if (ext === 'pdf') { + previewType.value = 'pdf' + previewUrl.value = url + return + } + if (ext === 'docx') { + previewType.value = 'docx' + try { + await renderDocx(url) + } catch (e: any) { + console.error('DOCX 棰勮澶辫触:', e) + ElMessage.warning('DOCX 棰勮澶辫触锛屽缓璁笅杞芥煡鐪�') + previewType.value = 'unknown' + } + return + } + if (ext === 'doc') { + ElMessage.info('鏆備笉鏀寔 .doc 棰勮锛岃涓嬭浇鏌ョ湅') + previewType.value = 'unknown' + return + } + + ElMessage.warning('姝ゆ枃浠剁被鍨嬩笉鏀寔棰勮锛岃涓嬭浇鏌ョ湅') + previewType.value = 'unknown' +} + +/** + * 鍔ㄦ�佸姞杞� docx-preview 骞舵覆鏌� DOCX + */ +const renderDocx = async (url: string) => { + // 鍔ㄦ�佸姞杞� docx-preview + const ensureScript = () => new Promise((resolve, reject) => { + if ((window as any).docx && (window as any).docx.renderAsync) return resolve(true) + const existed = document.querySelector('script[data-docx-preview]') + if (existed) { + existed.addEventListener('load', () => resolve(true)) + existed.addEventListener('error', reject) + return + } + const s = document.createElement('script') + s.src = 'https://unpkg.com/docx-preview/dist/docx-preview.min.js' + s.async = true + s.setAttribute('data-docx-preview', '1') + s.onload = () => resolve(true) + s.onerror = reject + document.head.appendChild(s) + }) + + // 瑙勮寖鍖� URL锛堟敮鎸佺浉瀵硅矾寰勶級 + const buildUrl = (u: string) => { + if (!u) return u + if (u.startsWith('http://') || u.startsWith('https://')) return u + if (u.startsWith('/')) return location.origin + u + return u + } + + // 鎼哄甫閴存潈澶磋闂檮浠讹紙寰堝鏂囦欢鏈嶅姟涓嶈蛋 cookie锛岃�屾槸 JWT Header锛� + const { getToken } = await import('@/utils/auth') + const token = getToken ? getToken() : '' + const requestUrl = buildUrl(url) + + let res: Response + try { + res = await fetch(requestUrl, { + credentials: 'include', + headers: token ? { Authorization: `Bearer ${token}` } : undefined, + mode: 'cors' + } as RequestInit) + } catch (e) { + throw new Error('鑾峰彇 DOCX 澶辫触: 缃戠粶涓嶅彲杈炬垨琚嫤鎴�') + } + if (!res.ok) throw new Error('鑾峰彇 DOCX 澶辫触: ' + res.status) + + const blob = await res.blob() + + await ensureScript() + if (docxContainer.value) { + docxContainer.value.innerHTML = '' + await (window as any).docx.renderAsync(blob, docxContainer.value, null, { inWrapper: true }) + } } // 瀹℃牳閫氳繃 @@ -770,4 +907,19 @@ grid-template-columns: 1fr; } } + .preview-content { + text-align: center; + } + + .preview-error { + padding: 40px 0; + } + + .docx-preview { + text-align: left; + max-height: 70vh; + overflow: auto; + background: #fff; + padding: 12px; + } </style> \ No newline at end of file -- Gitblit v1.8.0