From d3fd4b0ab851bab89c30c199e27245f7f45f1c0e Mon Sep 17 00:00:00 2001
From: wlzboy <66905212@qq.com>
Date: 星期六, 08 十一月 2025 08:01:12 +0800
Subject: [PATCH] feat:实现了微信上传图片

---
 app/pages/task/detail.vue |  639 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 603 insertions(+), 36 deletions(-)

diff --git a/app/pages/task/detail.vue b/app/pages/task/detail.vue
index f58d624..3932e66 100644
--- a/app/pages/task/detail.vue
+++ b/app/pages/task/detail.vue
@@ -16,12 +16,12 @@
         </view>
         <view class="info-item">
           <view class="label">浠诲姟绫诲瀷</view>
-          <view class="value">{{ getTaskTypeText(taskDetail.taskType) }}</view>
+          <view class="value">{{ displayTaskType }}</view>
         </view>
         <view class="info-item">
           <view class="label">浠诲姟鐘舵��</view>
-          <view class="value status" :class="taskDetail.taskStatus === 'PENDING' ? 'pending' : taskDetail.taskStatus === 'DEPARTING' ? 'in_progress' : taskDetail.taskStatus === 'ARRIVED' ? 'in_progress' : taskDetail.taskStatus === 'RETURNING' ? 'in_progress' : taskDetail.taskStatus === 'IN_PROGRESS' ? 'in_progress' : taskDetail.taskStatus === 'COMPLETED' ? 'completed' : taskDetail.taskStatus === 'CANCELLED' ? 'cancelled' : ''">
-            {{ getStatusText(taskDetail.taskStatus) }}
+          <view class="value status" :class="statusClass">
+            {{ displayTaskStatus }}
           </view>
         </view>
         <view class="info-item">
@@ -38,25 +38,25 @@
         <view class="section-title">鏃堕棿淇℃伅</view>
         <view class="info-item">
           <view class="label">璁″垝寮�濮嬫椂闂�</view>
-          <view class="value">{{ formatDateTime(taskDetail.plannedStartTime) }}</view>
+          <view class="value">{{ displayPlannedStartTime }}</view>
         </view>
         <view class="info-item">
           <view class="label">璁″垝缁撴潫鏃堕棿</view>
-          <view class="value">{{ formatDateTime(taskDetail.plannedEndTime) }}</view>
+          <view class="value">{{ displayPlannedEndTime }}</view>
         </view>
         <view class="info-item" v-if="taskDetail.actualStartTime">
           <view class="label">瀹為檯寮�濮嬫椂闂�</view>
-          <view class="value">{{ formatDateTime(taskDetail.actualStartTime) }}</view>
+          <view class="value">{{ displayActualStartTime }}</view>
         </view>
         <view class="info-item" v-if="taskDetail.actualEndTime">
           <view class="label">瀹為檯缁撴潫鏃堕棿</view>
-          <view class="value">{{ formatDateTime(taskDetail.actualEndTime) }}</view>
+          <view class="value">{{ displayActualEndTime }}</view>
         </view>
       </view>
       
       <view class="detail-section">
         <view class="section-title">浣嶇疆淇℃伅</view>
-        <!-- 鎬ユ晳杞繍浠诲姟锛氭樉绀鸿浆鍑�/杞叆鍖婚櫌鍦板潃 -->
+        <!-- 杞繍浠诲姟锛氭樉绀鸿浆鍑�/杞叆鍖婚櫌鍦板潃 -->
         <template v-if="taskDetail.taskType === 'EMERGENCY_TRANSFER' && taskDetail.emergencyInfo">
           <view class="info-item" v-if="taskDetail.emergencyInfo.hospitalOutAddress">
             <view class="label">杞嚭鍖婚櫌</view>
@@ -106,16 +106,20 @@
         <view class="description">{{ taskDetail.remark }}</view>
       </view>
       
-      <!-- 鎬ユ晳杞繍浠诲姟鐗规湁淇℃伅 -->
+      <!-- 杞繍浠诲姟鐗规湁淇℃伅 -->
       <view class="detail-section" v-if="taskDetail.taskType === 'EMERGENCY_TRANSFER' && taskDetail.emergencyInfo">
         <view class="section-title">鎮h�呬俊鎭�</view>
-        <view class="info-item" v-if="taskDetail.emergencyInfo.patientName">
-          <view class="label">鎮h�呭鍚�</view>
-          <view class="value">{{ taskDetail.emergencyInfo.patientName }}</view>
+        <view class="info-item">
+          <view class="label">鑱旂郴浜�</view>
+          <view class="value">{{ taskDetail.emergencyInfo.patientContact || '鏈缃�' }}</view>
         </view>
-        <view class="info-item" v-if="taskDetail.emergencyInfo.patientPhone">
+        <view class="info-item">
+          <view class="label">鎮h�呭鍚�</view>
+          <view class="value">{{ taskDetail.emergencyInfo.patientName || '鏈缃�' }}</view>
+        </view>
+        <view class="info-item">
           <view class="label">鑱旂郴鐢佃瘽</view>
-          <view class="value">{{ taskDetail.emergencyInfo.patientPhone }}</view>
+          <view class="value">{{ taskDetail.emergencyInfo.patientPhone || '鏈缃�' }}</view>
         </view>
         <view class="info-item" v-if="taskDetail.emergencyInfo.patientGender">
           <view class="label">鎬у埆</view>
@@ -135,7 +139,7 @@
         </view>
       </view>
       
-      <!-- 鎬ユ晳杞繍 - 杞嚭鍖婚櫌淇℃伅 -->
+      <!-- 杞繍 - 杞嚭鍖婚櫌淇℃伅 -->
       <view class="detail-section" v-if="taskDetail.taskType === 'EMERGENCY_TRANSFER' && taskDetail.emergencyInfo">
         <view class="section-title">杞嚭鍖婚櫌淇℃伅</view>
         <view class="info-item" v-if="taskDetail.emergencyInfo.hospitalOutName">
@@ -156,7 +160,7 @@
         </view>
       </view>
       
-      <!-- 鎬ユ晳杞繍 - 杞叆鍖婚櫌淇℃伅 -->
+      <!-- 杞繍 - 杞叆鍖婚櫌淇℃伅 -->
       <view class="detail-section" v-if="taskDetail.taskType === 'EMERGENCY_TRANSFER' && taskDetail.emergencyInfo">
         <view class="section-title">杞叆鍖婚櫌淇℃伅</view>
         <view class="info-item" v-if="taskDetail.emergencyInfo.hospitalInName">
@@ -177,7 +181,7 @@
         </view>
       </view>
       
-      <!-- 鎬ユ晳杞繍 - 璐圭敤淇℃伅 -->
+      <!-- 杞繍 - 璐圭敤淇℃伅 -->
       <view class="detail-section" v-if="taskDetail.taskType === 'EMERGENCY_TRANSFER' && taskDetail.emergencyInfo">
         <view class="section-title">璐圭敤淇℃伅</view>
         <view class="info-item" v-if="taskDetail.emergencyInfo.transferDistance">
@@ -187,6 +191,35 @@
         <view class="info-item" v-if="taskDetail.emergencyInfo.transferPrice">
           <view class="label">杞繍璐圭敤</view>
           <view class="value">锟{ taskDetail.emergencyInfo.transferPrice }}</view>
+        </view>
+      </view>
+      
+      <!-- 闄勪欢淇℃伅 -->
+      <view class="detail-section">
+        <view class="section-title">
+          浠诲姟闄勪欢
+          <button class="upload-btn" @click="showUploadDialog">涓婁紶闄勪欢</button>
+        </view>
+        <view v-if="attachmentList && attachmentList.length > 0">
+          <view class="attachment-item" v-for="(item, index) in attachmentList" :key="item.attachmentId">
+            <view class="attachment-info">
+              <view class="attachment-category">
+                <text class="category-tag">{{ getCategoryName(item.attachmentCategory) }}</text>
+              </view>
+              <view class="attachment-name">{{ item.fileName }}</view>
+              <view class="attachment-meta">
+                <text class="upload-time">{{ formatTime(item.uploadTime) }}</text>
+                <text class="file-size">{{ formatFileSize(item.fileSize) }}</text>
+              </view>
+            </view>
+            <view class="attachment-actions">
+              <button class="action-btn view-btn" @click="viewAttachment(item)">鏌ョ湅</button>
+              <button class="action-btn delete-btn" @click="deleteAttachment(item.attachmentId, index)">鍒犻櫎</button>
+            </view>
+          </view>
+        </view>
+        <view v-else class="no-attachment">
+          <text>鏆傛棤闄勪欢</text>
         </view>
       </view>
       
@@ -310,22 +343,133 @@
       
       <!-- 宸插畬鎴�/宸插彇娑�: 涓嶆樉绀烘寜閽� -->
     </view>
+    
+    <!-- 闄勪欢涓婁紶瀵硅瘽妗� -->
+    <uni-popup ref="uploadPopup" type="bottom">
+      <view class="upload-dialog">
+        <view class="dialog-header">
+          <text class="dialog-title">涓婁紶闄勪欢</text>
+          <uni-icons type="closeempty" size="24" @click="closeUploadDialog"></uni-icons>
+        </view>
+        <view class="dialog-content">
+          <view class="form-item">
+            <view class="form-label">闄勪欢鍒嗙被</view>
+            <picker @change="onCategoryChange" :value="selectedCategoryIndex" :range="categoryList" range-key="label">
+              <view class="picker-value">
+                {{ categoryList[selectedCategoryIndex].label }}
+                <uni-icons type="arrowdown" size="16"></uni-icons>
+              </view>
+            </picker>
+          </view>
+          <view class="form-item">
+            <view class="form-label">閫夋嫨鍥剧墖</view>
+            <button class="choose-image-btn" @click="chooseImage">
+              <uni-icons type="image" size="20"></uni-icons>
+              <text>鐐瑰嚮閫夋嫨</text>
+            </button>
+          </view>
+          <view class="preview-area" v-if="tempImagePath">
+            <image :src="tempImagePath" mode="aspectFit" class="preview-image"></image>
+          </view>
+        </view>
+        <view class="dialog-footer">
+          <button class="cancel-btn" @click="closeUploadDialog">鍙栨秷</button>
+          <button class="confirm-btn" @click="confirmUpload" :disabled="!tempImagePath">纭畾涓婁紶</button>
+        </view>
+      </view>
+    </uni-popup>
   </view>
 </template>
 
 <script>
   import { getTask, changeTaskStatus } from '@/api/task'
+  import { getAttachmentList, uploadAttachmentFromWechat, deleteAttachment, getWechatAccessToken } from '@/api/task'
+  import { formatDateTime } from '@/utils/common'
   
   export default {
     data() {
       return {
         taskDetail: null,
-        taskId: null
+        taskId: null,
+        attachmentList: [],
+        categoryList: [
+          { label: '鐭ユ儏鍚屾剰涔�', value: '1' },
+          { label: '鐥呬汉璧勬枡', value: '2' },
+          { label: '鎿嶄綔璁板綍', value: '3' },
+          { label: '鍑鸿溅鍓�', value: '4' },
+          { label: '鍑鸿溅鍚�', value: '5' },
+          { label: '绯诲畨鍏ㄥ甫', value: '6' }
+        ],
+        selectedCategoryIndex: 0,
+        tempImagePath: null,
+        isWechatMiniProgram: false // 鏄惁鏄井淇″皬绋嬪簭鐜
+      }
+    },
+    computed: {
+      // 鏄剧ず浠诲姟绫诲瀷
+      displayTaskType() {
+        if (!this.taskDetail || !this.taskDetail.taskType) {
+          return '鏈缃�'
+        }
+        return this.getTaskTypeText(this.taskDetail.taskType)
+      },
+      // 鏄剧ず浠诲姟鐘舵��
+      displayTaskStatus() {
+        if (!this.taskDetail || !this.taskDetail.taskStatus) {
+          return '鏈缃�'
+        }
+        return this.getStatusText(this.taskDetail.taskStatus)
+      },
+      // 鐘舵�佹牱寮忕被
+      statusClass() {
+        if (!this.taskDetail || !this.taskDetail.taskStatus) {
+          return ''
+        }
+        const status = this.taskDetail.taskStatus
+        if (status === 'PENDING') return 'pending'
+        if (['DEPARTING', 'ARRIVED', 'RETURNING', 'IN_PROGRESS'].includes(status)) return 'in_progress'
+        if (status === 'COMPLETED') return 'completed'
+        if (status === 'CANCELLED') return 'cancelled'
+        return ''
+      },
+      // 鏄剧ず璁″垝寮�濮嬫椂闂�
+      displayPlannedStartTime() {
+        if (!this.taskDetail || !this.taskDetail.plannedStartTime) {
+          return '鏈缃�'
+        }
+        return formatDateTime(this.taskDetail.plannedStartTime, 'YYYY-MM-DD HH:mm')
+      },
+      // 鏄剧ず璁″垝缁撴潫鏃堕棿
+      displayPlannedEndTime() {
+        if (!this.taskDetail || !this.taskDetail.plannedEndTime) {
+          return '鏈缃�'
+        }
+        return formatDateTime(this.taskDetail.plannedEndTime, 'YYYY-MM-DD HH:mm')
+      },
+      // 鏄剧ず瀹為檯寮�濮嬫椂闂�
+      displayActualStartTime() {
+        if (!this.taskDetail || !this.taskDetail.actualStartTime) {
+          return '鏈缃�'
+        }
+        return formatDateTime(this.taskDetail.actualStartTime, 'YYYY-MM-DD HH:mm')
+      },
+      // 鏄剧ず瀹為檯缁撴潫鏃堕棿
+      displayActualEndTime() {
+        if (!this.taskDetail || !this.taskDetail.actualEndTime) {
+          return '鏈缃�'
+        }
+        return formatDateTime(this.taskDetail.actualEndTime, 'YYYY-MM-DD HH:mm')
       }
     },
     onLoad(options) {
       this.taskId = options.id
       this.loadTaskDetail()
+      this.loadAttachmentList()
+      
+      // 妫�娴嬫槸鍚︽槸寰俊灏忕▼搴忕幆澧�
+      // #ifdef MP-WEIXIN
+      this.isWechatMiniProgram = true
+      // #endif
     },
     methods: {
       // 鍔犺浇浠诲姟璇︽儏
@@ -338,9 +482,11 @@
         getTask(this.taskId).then(response => {
           this.taskDetail = response.data || response
           // 璋冭瘯锛氭墦鍗拌繑鍥炵殑鏁版嵁
-          console.log('浠诲姟璇︽儏鏁版嵁:', this.taskDetail)
-          console.log('鍑哄彂鍦板潃:', this.taskDetail.departureAddress)
-          console.log('鐩殑鍦板潃:', this.taskDetail.destinationAddress)
+          // console.log('浠诲姟璇︽儏瀹屾暣鏁版嵁:', JSON.stringify(this.taskDetail, null, 2))
+          // console.log('浠诲姟绫诲瀷瀛楁鍊�:', this.taskDetail.taskType)
+          // console.log('浠诲姟鐘舵�佸瓧娈靛��:', this.taskDetail.taskStatus)
+          // console.log('鍑哄彂鍦板潃:', this.taskDetail.departureAddress)
+          // console.log('鐩殑鍦板潃:', this.taskDetail.destinationAddress)
         }).catch(error => {
           console.error('鍔犺浇浠诲姟璇︽儏澶辫触:', error)
           this.$modal.showToast('鍔犺浇浠诲姟璇︽儏澶辫触')
@@ -374,7 +520,7 @@
       
       // 鑾峰彇璺濈淇℃伅锛氭牴鎹换鍔$被鍨嬭繑鍥炰笉鍚屽瓧娈�
       getDistanceInfo(task) {
-        // 鎬ユ晳杞繍锛氫紭鍏堜娇鐢╰ransferDistance
+        // 杞繍锛氫紭鍏堜娇鐢╰ransferDistance
         if (task.taskType === 'EMERGENCY_TRANSFER' && task.emergencyInfo && task.emergencyInfo.transferDistance) {
           return task.emergencyInfo.transferDistance
         }
@@ -389,19 +535,6 @@
       // 杩斿洖涓婁竴椤�
       goBack() {
         uni.navigateBack()
-      },
-      
-      // 鏍煎紡鍖栨棩鏈熸椂闂�
-      formatDateTime(dateTime) {
-        if (!dateTime) return '鏈缃�'
-        const date = new Date(dateTime)
-        return date.toLocaleString('zh-CN', {
-          year: 'numeric',
-          month: '2-digit',
-          day: '2-digit',
-          hour: '2-digit',
-          minute: '2-digit'
-        })
       },
       
       // 鑾峰彇鐘舵�佹枃鏈�
@@ -424,7 +557,7 @@
           'MAINTENANCE': '缁翠慨淇濆吇',
           'FUEL': '鍔犳补',
           'OTHER': '鍏朵粬',
-          'EMERGENCY_TRANSFER': '鎬ユ晳杞繍',
+          'EMERGENCY_TRANSFER': '杞繍浠诲姟',
           'WELFARE': '绂忕杞�'
         }
         return typeMap[type] || '鏈煡绫诲瀷'
@@ -542,6 +675,254 @@
             })
           }
         })
+      },
+      
+      // 鍔犺浇闄勪欢鍒楄〃
+      loadAttachmentList() {
+        if (!this.taskId) {
+          return
+        }
+        
+        getAttachmentList(this.taskId).then(response => {
+          this.attachmentList = response.data || response || []
+        }).catch(error => {
+          console.error('鍔犺浇闄勪欢鍒楄〃澶辫触:', error)
+        })
+      },
+      
+      // 鏄剧ず涓婁紶瀵硅瘽妗�
+      showUploadDialog() {
+        this.selectedCategoryIndex = 0
+        this.tempImagePath = null
+        this.$refs.uploadPopup.open()
+      },
+      
+      // 鍏抽棴涓婁紶瀵硅瘽妗�
+      closeUploadDialog() {
+        this.$refs.uploadPopup.close()
+      },
+      
+      // 鍒嗙被閫夋嫨鍙樺寲
+      onCategoryChange(e) {
+        this.selectedCategoryIndex = e.detail.value
+      },
+      
+      // 閫夋嫨鍥剧墖
+      chooseImage() {
+        const that = this
+        uni.chooseImage({
+          count: 1,
+          sizeType: ['compressed'],
+          sourceType: ['album', 'camera'],
+          success: function(res) {
+            that.tempImagePath = res.tempFilePaths[0]
+          },
+          fail: function(err) {
+            console.error('閫夋嫨鍥剧墖澶辫触:', err)
+            that.$modal.showToast('閫夋嫨鍥剧墖澶辫触')
+          }
+        })
+      },
+      
+      // 纭涓婁紶
+      confirmUpload() {
+        if (!this.tempImagePath) {
+          this.$modal.showToast('璇峰厛閫夋嫨鍥剧墖')
+          return
+        }
+        
+        const that = this
+        const category = this.categoryList[this.selectedCategoryIndex].value
+        
+        // 寰俊灏忕▼搴忕幆澧冿細鍏堣幏鍙朅ccessToken锛屽啀涓婁紶鍒板井淇℃湇鍔″櫒锛屾渶鍚庢彁浜ediaId鍒板悗绔�
+        // #ifdef MP-WEIXIN
+        if (this.isWechatMiniProgram) {
+          uni.showLoading({
+            title: '涓婁紶涓�...'
+          })
+          
+          // 绗竴姝ワ細浠庡悗绔幏鍙朅ccessToken
+          getWechatAccessToken().then(tokenResponse => {
+            // 鎺ュ彛杩斿洖鏍煎紡锛歿"msg":"token鍊�","code":200}
+            console.log('鑾峰彇AccessToken鎴愬姛:', tokenResponse)
+            const accessToken = tokenResponse.msg || tokenResponse.data || tokenResponse
+            if (!accessToken) {
+              uni.hideLoading()
+              that.$modal.showToast('鑾峰彇AccessToken澶辫触')
+              console.error('鑾峰彇AccessToken澶辫触锛屽搷搴旀暟鎹�:', tokenResponse)
+              return
+            }
+            
+            console.log('鑾峰彇鍒癆ccessToken:', accessToken)
+            
+            // 绗簩姝ワ細涓婁紶鍒板井淇℃湇鍔″櫒
+            const uploadUrl = `https://api.weixin.qq.com/cgi-bin/media/upload?access_token=${accessToken}&type=image`
+            
+            uni.uploadFile({
+              url: uploadUrl,
+              filePath: that.tempImagePath,
+              name: 'media',
+              success: function(res) {
+                console.log('寰俊涓婁紶鍝嶅簲:', res)
+                try {
+                  const data = JSON.parse(res.data)
+                  if (data.media_id) {
+                    // 绗笁姝ワ細鎻愪氦mediaId鍒板悗绔�
+                    uploadAttachmentFromWechat(that.taskId, data.media_id, category).then(response => {
+                      uni.hideLoading()
+                      that.$modal.showToast('涓婁紶鎴愬姛')
+                      that.closeUploadDialog()
+                      that.loadAttachmentList()
+                    }).catch(error => {
+                      uni.hideLoading()
+                      console.error('鎻愪氦mediaId澶辫触:', error)
+                      that.$modal.showToast('涓婁紶澶辫触锛�' + (error.msg || '璇烽噸璇�'))
+                    })
+                  } else {
+                    uni.hideLoading()
+                    const errMsg = data.errmsg || '鏈煡閿欒'
+                    console.error('寰俊杩斿洖閿欒:', data)
+                    that.$modal.showToast('寰俊涓婁紶澶辫触锛�' + errMsg)
+                  }
+                } catch (e) {
+                  uni.hideLoading()
+                  console.error('瑙f瀽寰俊鍝嶅簲澶辫触:', e, res.data)
+                  that.$modal.showToast('涓婁紶澶辫触锛氬搷搴旇В鏋愰敊璇�')
+                }
+              },
+              fail: function(err) {
+                uni.hideLoading()
+                console.error('涓婁紶鍒板井淇″け璐�:', err)
+                that.$modal.showToast('涓婁紶澶辫触锛�' + (err.errMsg || '璇锋鏌ョ綉缁�'))
+              }
+            })
+          }).catch(error => {
+            uni.hideLoading()
+            console.error('鑾峰彇AccessToken澶辫触:', error)
+            that.$modal.showToast('鑾峰彇AccessToken澶辫触')
+          })
+          return
+        }
+        // #endif
+        
+        // 闈炲井淇″皬绋嬪簭鐜锛氱洿鎺ヤ笂浼犲埌鍚庣鏈嶅姟鍣�
+        uni.showLoading({
+          title: '涓婁紶涓�...'
+        })
+        
+        uni.uploadFile({
+          url: that.$baseUrl + '/task/attachment/upload/' + that.taskId,
+          filePath: that.tempImagePath,
+          name: 'file',
+          formData: {
+            'category': category
+          },
+          header: {
+            'Authorization': 'Bearer ' + uni.getStorageSync('token')
+          },
+          success: function(uploadRes) {
+            uni.hideLoading()
+            
+            if (uploadRes.statusCode === 200) {
+              const result = JSON.parse(uploadRes.data)
+              if (result.code === 200) {
+                that.$modal.showToast('涓婁紶鎴愬姛')
+                that.closeUploadDialog()
+                that.loadAttachmentList()
+              } else {
+                that.$modal.showToast(result.msg || '涓婁紶澶辫触')
+              }
+            } else {
+              that.$modal.showToast('涓婁紶澶辫触')
+            }
+          },
+          fail: function(err) {
+            uni.hideLoading()
+            console.error('涓婁紶澶辫触:', err)
+            that.$modal.showToast('涓婁紶澶辫触')
+          }
+        })
+      },
+      
+      // 鏌ョ湅闄勪欢
+      viewAttachment(item) {
+        // 濡傛灉鏄浘鐗囷紝浣跨敤鍥剧墖棰勮
+        const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp']
+        const fileExt = item.fileName.split('.').pop().toLowerCase()
+        
+        if (imageTypes.includes(fileExt)) {
+          // 鏋勫缓鍥剧墖璁块棶鍦板潃
+          // 濡傛灉鏄痜ilePath鏄畬鏁磋矾寰勶紝闇�瑕侀�氳繃涓嬭浇鎺ュ彛璁块棶
+          const imageUrl = this.$baseUrl + '/task/attachment/download/' + item.attachmentId
+          
+          // 寰俊灏忕▼搴忎腑棰勮鍥剧墖
+          // #ifdef MP-WEIXIN
+          // 寰俊灏忕▼搴忛渶瑕佸厛涓嬭浇鍒版湰鍦板啀棰勮
+          uni.showLoading({ title: '鍔犺浇涓�...' })
+          uni.downloadFile({
+            url: imageUrl,
+            success: function(res) {
+              uni.hideLoading()
+              if (res.statusCode === 200) {
+                uni.previewImage({
+                  urls: [res.tempFilePath],
+                  current: res.tempFilePath
+                })
+              } else {
+                uni.showToast({ title: '鍔犺浇鍥剧墖澶辫触', icon: 'none' })
+              }
+            },
+            fail: function() {
+              uni.hideLoading()
+              uni.showToast({ title: '涓嬭浇澶辫触', icon: 'none' })
+            }
+          })
+          // #endif
+          
+          // 闈炲井淇″皬绋嬪簭鐜锛岀洿鎺ラ瑙�
+          // #ifndef MP-WEIXIN
+          uni.previewImage({
+            urls: [imageUrl],
+            current: imageUrl
+          })
+          // #endif
+        } else {
+          this.$modal.showToast('浠呮敮鎸侀瑙堝浘鐗�')
+        }
+      },
+      
+      // 鍒犻櫎闄勪欢
+      deleteAttachment(attachmentId, index) {
+        const that = this
+        this.$modal.confirm('纭畾瑕佸垹闄よ闄勪欢鍚楋紵').then(() => {
+          deleteAttachment(attachmentId).then(response => {
+            that.$modal.showToast('鍒犻櫎鎴愬姛')
+            that.attachmentList.splice(index, 1)
+          }).catch(error => {
+            console.error('鍒犻櫎闄勪欢澶辫触:', error)
+            that.$modal.showToast('鍒犻櫎澶辫触')
+          })
+        }).catch(() => {})
+      },
+      
+      // 鑾峰彇鍒嗙被鍚嶇О
+      getCategoryName(category) {
+        const item = this.categoryList.find(c => c.value === category)
+        return item ? item.label : '鏈垎绫�'
+      },
+      
+      // 鏍煎紡鍖栨椂闂�
+      formatTime(time) {
+        if (!time) return ''
+        return formatDateTime(time, 'YYYY-MM-DD HH:mm')
+      },
+      
+      // 鏍煎紡鍖栨枃浠跺ぇ灏�
+      formatFileSize(size) {
+        if (!size) return '0B'
+        if (size < 1024) return size + 'B'
+        if (size < 1024 * 1024) return (size / 1024).toFixed(2) + 'KB'
+        return (size / 1024 / 1024).toFixed(2) + 'MB'
       }
     }
   }
@@ -597,6 +978,18 @@
         color: #333;
         border-bottom: 1rpx solid #f0f0f0;
         padding-bottom: 10rpx;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        
+        .upload-btn {
+          font-size: 24rpx;
+          padding: 8rpx 20rpx;
+          background-color: #007AFF;
+          color: white;
+          border-radius: 8rpx;
+          border: none;
+        }
       }
       
       .info-item {
@@ -644,6 +1037,84 @@
         background-color: #f9f9f9;
         padding: 20rpx;
         border-radius: 10rpx;
+      }
+      
+      .no-attachment {
+        text-align: center;
+        padding: 40rpx 0;
+        color: #999;
+        font-size: 28rpx;
+      }
+      
+      .attachment-item {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 20rpx;
+        margin-bottom: 15rpx;
+        background-color: #f9f9f9;
+        border-radius: 10rpx;
+        
+        &:last-child {
+          margin-bottom: 0;
+        }
+        
+        .attachment-info {
+          flex: 1;
+          margin-right: 20rpx;
+          
+          .attachment-category {
+            margin-bottom: 8rpx;
+            
+            .category-tag {
+              display: inline-block;
+              padding: 4rpx 12rpx;
+              background-color: #007AFF;
+              color: white;
+              font-size: 22rpx;
+              border-radius: 4rpx;
+            }
+          }
+          
+          .attachment-name {
+            font-size: 28rpx;
+            color: #333;
+            margin-bottom: 8rpx;
+            word-break: break-all;
+          }
+          
+          .attachment-meta {
+            font-size: 24rpx;
+            color: #999;
+            
+            .upload-time {
+              margin-right: 20rpx;
+            }
+          }
+        }
+        
+        .attachment-actions {
+          display: flex;
+          flex-direction: column;
+          gap: 10rpx;
+          
+          .action-btn {
+            padding: 8rpx 20rpx;
+            font-size: 24rpx;
+            border-radius: 6rpx;
+            border: none;
+            
+            &.view-btn {
+              background-color: #007AFF;
+              color: white;
+            }
+            
+            &.delete-btn {
+              background-color: #ff3b30;
+              color: white;
+            }
+          }
+        }
       }
     }
     
@@ -699,5 +1170,101 @@
         }
       }
     }
+    
+    .upload-dialog {
+      background-color: white;
+      border-radius: 20rpx 20rpx 0 0;
+      padding: 30rpx;
+      
+      .dialog-header {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 30rpx;
+        
+        .dialog-title {
+          font-size: 32rpx;
+          font-weight: bold;
+          color: #333;
+        }
+      }
+      
+      .dialog-content {
+        .form-item {
+          margin-bottom: 30rpx;
+          
+          .form-label {
+            font-size: 28rpx;
+            color: #333;
+            margin-bottom: 15rpx;
+          }
+          
+          .picker-value {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 20rpx;
+            background-color: #f5f5f5;
+            border-radius: 10rpx;
+            font-size: 28rpx;
+            color: #333;
+          }
+          
+          .choose-image-btn {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            padding: 30rpx;
+            background-color: #f5f5f5;
+            border-radius: 10rpx;
+            border: 2rpx dashed #ccc;
+            color: #666;
+            font-size: 28rpx;
+            
+            text {
+              margin-left: 10rpx;
+            }
+          }
+        }
+        
+        .preview-area {
+          margin-top: 20rpx;
+          
+          .preview-image {
+            width: 100%;
+            height: 400rpx;
+            border-radius: 10rpx;
+          }
+        }
+      }
+      
+      .dialog-footer {
+        display: flex;
+        gap: 20rpx;
+        margin-top: 30rpx;
+        
+        button {
+          flex: 1;
+          height: 80rpx;
+          border-radius: 10rpx;
+          font-size: 30rpx;
+          border: none;
+        }
+        
+        .cancel-btn {
+          background-color: #f5f5f5;
+          color: #666;
+        }
+        
+        .confirm-btn {
+          background-color: #007AFF;
+          color: white;
+          
+          &:disabled {
+            background-color: #ccc;
+          }
+        }
+      }
+    }
   }
 </style>
\ No newline at end of file

--
Gitblit v1.9.1