From c4938f6f4e839890b032c75c7a57333a6a9157a9 Mon Sep 17 00:00:00 2001
From: peng <peng.com>
Date: 星期四, 06 十一月 2025 17:06:10 +0800
Subject: [PATCH] 添加新闻功能
---
web/src/views/check-detail.vue | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 165 insertions(+), 13 deletions(-)
diff --git a/web/src/views/check-detail.vue b/web/src/views/check-detail.vue
index 6843618..cfa0d6e 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">
@@ -224,6 +248,12 @@
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('')
const approving = ref(false)
@@ -240,11 +270,7 @@
try {
loading.value = true
const playerId = route.params.id as string
-
- // 杩欓噷搴旇璋冪敤API鑾峰彇鏁版嵁
- // 鏆傛椂浣跨敤妯℃嫙鏁版嵁
await loadPlayerData(playerId)
-
} catch (error) {
console.error('鍔犺浇鏁版嵁澶辫触:', error)
ElMessage.error('鍔犺浇鏁版嵁澶辫触')
@@ -282,7 +308,6 @@
regionInfo {
id
name
- fullPath
}
activityName
projectName
@@ -296,6 +321,7 @@
fileExt
fileSize
mediaType
+ thumbUrl
}
}
}
@@ -307,7 +333,7 @@
const loadPlayerData = async (playerId: string) => {
try {
const data = await graphqlRequest(ACTIVITY_PLAYER_DETAIL_QUERY, { id: playerId })
- const detail = data.activityPlayerDetail
+ const detail = data?.data?.activityPlayerDetail || data?.activityPlayerDetail
if (detail) {
// 璁剧疆player鍩烘湰淇℃伅
@@ -345,9 +371,12 @@
// 鍒濆鍖栧鏍告剰瑙�
feedbackText.value = detail.feedback || ''
+ } else {
+ ElMessage.warning('鏈壘鍒板搴旂殑鍙傝禌浜哄憳璇︽儏淇℃伅')
}
} catch (error) {
console.error('鍔犺浇鏁版嵁澶辫触:', error)
+ ElMessage.error(`鍔犺浇鏁版嵁澶辫触: ${error.message || error}`)
throw error
}
}
@@ -382,13 +411,13 @@
const getGenderText = (gender: number) => {
const genderMap: Record<number, string> = {
1: '鐢�',
- 2: '濂�'
+ 0: '濂�'
}
return genderMap[gender] || '-'
}
// 鑾峰彇瀛﹀巻鏂囨湰
-const getEducationText = (education: number) => {
+const getEducationText = (education: number | string) => {
const educationMap: Record<number, string> = {
1: '楂樹腑',
2: '澶т笓',
@@ -396,7 +425,8 @@
4: '纭曞+',
5: '鍗氬+'
}
- return educationMap[education] || '-'
+ const numEducation = typeof education === 'string' ? parseInt(education) : education
+ return educationMap[numEducation] || '-'
}
// 鑾峰彇鐘舵�佹枃鏈�
@@ -425,6 +455,113 @@
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 })
+ }
+}
+
// 瀹℃牳閫氳繃
const handleApprove = async () => {
try {
@@ -437,7 +574,7 @@
approving.value = true
const result = await approveActivityPlayer(activityPlayerData.value.id, feedbackText.value)
- if (result.approveActivityPlayer) {
+ if (result.data.approveActivityPlayer) {
ElMessage.success('瀹℃牳閫氳繃鎴愬姛')
activityPlayerData.value.state = 1
activityPlayerData.value.feedback = feedbackText.value
@@ -471,7 +608,7 @@
rejecting.value = true
const result = await rejectActivityPlayer(activityPlayerData.value.id, feedbackText.value)
- if (result.rejectActivityPlayer) {
+ if (result.data.rejectActivityPlayer) {
ElMessage.success('瀹℃牳椹冲洖鎴愬姛')
activityPlayerData.value.state = 2
activityPlayerData.value.feedback = feedbackText.value
@@ -499,7 +636,7 @@
updating.value = true
const result = await updatePlayerFeedback(activityPlayerData.value.id, feedbackText.value)
- if (result.updatePlayerFeedback) {
+ if (result.data.updatePlayerFeedback) {
ElMessage.success('瀹℃牳鎰忚鏇存柊鎴愬姛')
activityPlayerData.value.feedback = feedbackText.value
} else {
@@ -771,4 +908,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