From c4a9cad1c50e89365e2a58b50e259af642ed3b8c Mon Sep 17 00:00:00 2001
From: Codex Assistant <codex@example.com>
Date: 星期二, 07 十月 2025 16:12:20 +0800
Subject: [PATCH] feat(review): 调整评审详情展示顺序与样式,描述支持多行,项目信息列宽40/60 fix(auth): 登录页与首页循环跳转保护;api.ts 在登录页不再重定向;401分支在登录页不跳转 fix(router): /login 放行策略优化,避免死循环;评审列表跳转到 /project-review/:id/detail fix(frontend): 补齐 utils/appConfig.ts,避免启动白屏 fix(review): 详情页提交评分缺少stageId时回退使用项目详情的stageId feat(backend): ActivityPlayerDetailResponse.playerInfo 补充 avatarUrl/avatar,服务组装时填充用户头像 chore(dev): 启动脚本注入本地JWT密钥,重启前后端

---
 wx/pages/profile/employee-review-detail.js |  118 +++++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 106 insertions(+), 12 deletions(-)

diff --git a/wx/pages/profile/employee-review-detail.js b/wx/pages/profile/employee-review-detail.js
index 0b6dadc..1e8e9e7 100644
--- a/wx/pages/profile/employee-review-detail.js
+++ b/wx/pages/profile/employee-review-detail.js
@@ -20,6 +20,7 @@
         fullUrl
         fullThumbUrl
         fileExt
+        fileSize
         mediaType
       }
     }
@@ -43,8 +44,12 @@
     loading: false,
     submitting: false,
     activityPlayerId: null,
-    detail: null,
-    feedback: ''
+    detail: {
+      playerInfo: {},
+      submissionFiles: []
+    },
+    feedback: '',
+    mediaList: []
   },
 
   onLoad(options) {
@@ -76,16 +81,17 @@
         return
       }
 
+      const mediaList = (detail.submissionFiles || []).map(item => this.transformMediaFile(item))
+      const playerInfo = detail.playerInfo || {}
+
       this.setData({
         detail: {
           ...detail,
+          playerInfo,
           stateText: this.getStateText(detail.state),
-          submissionFiles: (detail.submissionFiles || []).map(file => ({
-            id: file.id,
-            name: file.name,
-            url: file.fullUrl || file.fullThumbUrl || ''
-          }))
+          submissionFiles: mediaList
         },
+        mediaList,
         feedback: detail.feedback || ''
       })
     } catch (error) {
@@ -93,6 +99,32 @@
       wx.showToast({ title: '鍔犺浇澶辫触', icon: 'none' })
     } finally {
       this.setData({ loading: false })
+    }
+  },
+
+  transformMediaFile(file = {}) {
+    const url = file.fullUrl || file.fullThumbUrl || file.url || ''
+    const thumbUrl = file.fullThumbUrl || file.fullUrl || url
+    const ext = (file.fileExt || '').toLowerCase()
+    let mediaType = 'file'
+
+    if (file.mediaType === 1 || ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'heic'].includes(ext)) {
+      mediaType = 'image'
+    } else if (file.mediaType === 2 || ['mp4', 'mov', 'avi', 'wmv', 'mkv', 'webm', 'flv'].includes(ext)) {
+      mediaType = 'video'
+    } else if (ext === 'pdf') {
+      mediaType = 'pdf'
+    } else if (['doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'wps', 'txt', 'rtf'].includes(ext)) {
+      mediaType = 'word'
+    }
+
+    return {
+      id: file.id,
+      name: file.name || '璧勬枡鏂囦欢',
+      url,
+      thumbUrl,
+      mediaType,
+      size: Number(file.fileSize) || 0
     }
   },
 
@@ -119,9 +151,10 @@
     this.setData({ submitting: true })
 
     try {
+      const trimmedFeedback = (this.data.feedback || '').trim()
       const variables = {
         id: Number(this.data.activityPlayerId),
-        feedback: this.data.feedback ? this.data.feedback.trim() : null
+        feedback: trimmedFeedback ? trimmedFeedback : null
       }
       const result = await graphqlRequest(mutation, variables)
       const success = result && (action === 'APPROVE' ? result.approveActivityPlayer : result.rejectActivityPlayer)
@@ -143,13 +176,74 @@
     }
   },
 
-  previewFile(e) {
-    const url = e.currentTarget.dataset.url
-    if (url) {
-      wx.navigateTo({ url: `/pages/webview/webview?url=${encodeURIComponent(url)}` })
+  onMediaTap(e) {
+    const index = Number(e.currentTarget.dataset.index)
+    const mediaList = this.data.mediaList || []
+    const media = mediaList[index]
+    if (!media) return
+
+    if (media.mediaType === 'image') {
+      const images = mediaList.filter(item => item.mediaType === 'image').map(item => item.url)
+      wx.previewImage({
+        current: media.url,
+        urls: images
+      })
+    } else if (media.mediaType === 'video') {
+      wx.navigateTo({
+        url: `/pages/video/video?url=${encodeURIComponent(media.url)}&title=${encodeURIComponent(media.name)}`
+      })
+    } else {
+      this.openDocumentMedia(media)
     }
   },
 
+  async openDocumentMedia(media) {
+    try {
+      wx.showLoading({ title: '鎵撳紑涓�...' })
+      const downloadRes = await new Promise((resolve, reject) => {
+        wx.downloadFile({
+          url: media.url,
+          success: resolve,
+          fail: reject
+        })
+      })
+
+      if (downloadRes.statusCode !== 200) {
+        throw new Error('鏂囦欢涓嬭浇澶辫触')
+      }
+
+      await new Promise((resolve, reject) => {
+        wx.openDocument({
+          filePath: downloadRes.tempFilePath,
+          showMenu: true,
+          success: resolve,
+          fail: reject
+        })
+      })
+    } catch (error) {
+      console.error('鎵撳紑鏂囦欢澶辫触:', error)
+      wx.showToast({
+        title: '鏃犳硶鎵撳紑鏂囦欢',
+        icon: 'error'
+      })
+    } finally {
+      wx.hideLoading()
+    }
+  },
+
+  getFileSizeText(size) {
+    if (!size || size <= 0) {
+      return '鏈煡澶у皬'
+    }
+    if (size < 1024) {
+      return `${size}B`
+    }
+    if (size < 1024 * 1024) {
+      return `${(size / 1024).toFixed(1)}KB`
+    }
+    return `${(size / (1024 * 1024)).toFixed(1)}MB`
+  },
+
   getStateText(state) {
     switch (state) {
       case 0:

--
Gitblit v1.8.0