From 22cc8ce22157a32bfcd4ee14d824769c6e318cec Mon Sep 17 00:00:00 2001
From: p-honggang.li <p-honggang.li@pcitc.com>
Date: 星期一, 08 九月 2025 23:30:57 +0800
Subject: [PATCH] 修改取消订单列表展示的问题

---
 src/views/tradeManage/evaluate/index.vue |  988 +++++++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 766 insertions(+), 222 deletions(-)

diff --git a/src/views/tradeManage/evaluate/index.vue b/src/views/tradeManage/evaluate/index.vue
index 0493cb9..52a0fcd 100644
--- a/src/views/tradeManage/evaluate/index.vue
+++ b/src/views/tradeManage/evaluate/index.vue
@@ -142,6 +142,8 @@
         </el-table>
       </div>
 
+      <!-- 绉婚櫎鍘熸潵鐨勮〃鏍煎簳閮ㄤ俊鎭紝鍥犱负宸茬粡绉诲埌琛ㄦ牸鏈�鍚庝竴琛� -->
+
        <!-- 浜ゆ槗鏂囦欢锛堢Щ鍔ㄥ埌璁㈠崟璇︽儏涓嬮潰锛屽悓涓�鍗$墖鍐咃級 -->
        <div class="file-section" v-if="fileList.length > 0">
          <el-table
@@ -168,103 +170,192 @@
                {{ formatFileSize(row.size) }}
              </template>
            </el-table-column>
-           <el-table-column label="鎿嶄綔" width="100">
+           <el-table-column label="鎿嶄綔" width="180">
              <template #default="{ row }">
-               <el-button
-                 type="text"
-                 size="small"
-                 class="preview-btn"
-                 @click="handlePreview(row)"
-               >
-                 棰勮
-               </el-button>
-             </template>
-           </el-table-column>
-         </el-table>
-       </div>
-
-       <!-- 浜ゆ槗淇℃伅澶囨敞锛堢Щ鍔ㄥ埌浜ゆ槗鏂囦欢涓嬮潰锛屽悓涓�鍗$墖鍐咃級 -->
-       <div class="remark-section" v-if="remarkItems.length > 0">
-         <el-table
-           :data="remarkItems"
-           border
-           class="remark-table"
-           :header-cell-style="remarkTableHeaderStyle"
-           :cell-style="remarkTableCellStyle"
-         >
-           <el-table-column min-width="200">
-             <template #header>
-               <el-icon class="header-icon"><Document /></el-icon>
-               <span>浜ゆ槗淇℃伅澶囨敞</span>
-             </template>
-             <template #default="{ row }">
-               <div class="remark-name">
-                 <span>{{ row.name }}</span>
+               <div class="file-actions">
+                 <el-button
+                   type="text"
+                   size="small"
+                   class="preview-btn"
+                   @click="handlePreview(row)"
+                   v-if="isPreviewable(row)"
+                   :disabled="!isFileUploaded(row)"
+                 >
+                   棰勮
+                 </el-button>
+                 <el-button
+                   type="text"
+                   size="small"
+                   class="download-btn"
+                   @click="handleDownload(row)"
+                   :disabled="!isFileUploaded(row)"
+                 >
+                   涓嬭浇
+                 </el-button>
                </div>
-             </template>
-           </el-table-column>
-           <el-table-column label="鎺堟潈寮�濮嬫椂闂�" width="200">
-             <template #default="{ row }">
-               <div class="remark-date">{{ row.start }}</div>
-             </template>
-           </el-table-column>
-           <el-table-column label="鎺堟潈缁撴潫鏃堕棿" width="200">
-             <template #default="{ row }">
-               <div class="remark-date">
-                 {{ row.forever ? '姘镐箙' : row.end }}
-               </div>
-             </template>
-           </el-table-column>
-           <el-table-column label="澶囨敞" min-width="300">
-             <template #default="{ row }">
-               <div class="remark-content">{{ row.remark }}</div>
              </template>
            </el-table-column>
          </el-table>
        </div>
      </el-card>
 
-     <!-- 浜ゆ槗璇勪环 -->
+     <!-- 浜ゆ槗淇℃伅澶囨敞 -->
     <el-card class="mt15" shadow="never">
-      <div class="title">浜ゆ槗璇勪环</div>
-      <el-form :model="form" label-width="120px" class="mt10" :rules="rules" ref="formRef">
-        <el-form-item label="缁煎悎璇勫垎" prop="score">
-          <el-rate v-model="form.score" />
-        </el-form-item>
-        <el-form-item label="璇勪环鍐呭" prop="content">
-          <el-input v-model="form.content" type="textarea" :autosize="{ minRows: 4 }" placeholder="璇疯緭鍏ヨ瘎浠峰唴瀹�" />
-        </el-form-item>
-      </el-form>
-      <div class="action-buttons">
-        <el-button @click="goBack">杩斿洖</el-button>
-        <el-button type="primary" @click="submit">鎻愪氦璇勪环</el-button>
-      </div>
-    </el-card>
-  </div>
-</template>
+      <div class="title">浜ゆ槗淇℃伅澶囨敞</div>
+      <el-table :data="form.items" border class="mt10 remark-table">
+        <el-table-column label="璇︽儏" prop="name" min-width="200" />
+                 <el-table-column label="鎺堟潈寮�濮嬫椂闂�" width="200">
+           <template #default="{ row, $index }">
+             <span>{{ form.items[$index].start || '-' }}</span>
+           </template>
+         </el-table-column>
+         <el-table-column label="鎺堟潈缁撴潫鏃堕棿" width="200">
+           <template #default="{ row, $index }">
+             <div class="end-time-wrapper">
+               <span v-if="form.items[$index].forever" class="forever-text">姘镐箙</span>
+               <span v-else>{{ form.items[$index].end || '-' }}</span>
+             </div>
+           </template>
+         </el-table-column>
+         <el-table-column label="澶囨敞" min-width="300">
+           <template #default="{ row, $index }">
+             <span>{{ form.items[$index].remark || '-' }}</span>
+           </template>
+         </el-table-column>
+             </el-table>
+     </el-card>
+
+     <!-- 浜ゆ槗璇勪环 -->
+     <el-card class="mt15" shadow="never">
+       <div class="title">浜ゆ槗璇勪环</div>
+       <div class="evaluation-content">
+         <div class="evaluation-form">
+                       <!-- 璇勫垎閮ㄥ垎 -->
+            <div class="rating-section">
+              <div class="rating-items">
+                <div class="rating-row">
+                  <div class="rating-item">
+                    <label class="required">缁煎悎璇勫垎:</label>
+                    <el-rate
+                      v-model="evaluationForm.overallRating"
+                      :max="5"
+                      :texts="['寰堝樊', '杈冨樊', '涓�鑸�', '杈冨ソ', '寰堝ソ']"
+                      show-text
+                      :colors="['#99A9BF', '#F7BA2A', '#FF9900']"
+                    />
+                  </div>
+                  <div class="rating-item">
+                    <label class="required">鏈嶅姟璇勫垎:</label>
+                    <el-rate
+                      v-model="evaluationForm.serviceRating"
+                      :max="5"
+                      :texts="['寰堝樊', '杈冨樊', '涓�鑸�', '杈冨ソ', '寰堝ソ']"
+                      show-text
+                      :colors="['#99A9BF', '#F7BA2A', '#FF9900']"
+                    />
+                  </div>
+                </div>
+                <div class="rating-row">
+                  <div class="rating-item">
+                    <label class="required">璐ㄩ噺璇勫垎:</label>
+                    <el-rate
+                      v-model="evaluationForm.qualityRating"
+                      :max="5"
+                      :texts="['寰堝樊', '杈冨樊', '涓�鑸�', '杈冨ソ', '寰堝ソ']"
+                      show-text
+                      :colors="['#99A9BF', '#F7BA2A', '#FF9900']"
+                    />
+                  </div>
+                  <div class="rating-item">
+                    <label class="required">閫熷害璇勫垎:</label>
+                    <el-rate
+                      v-model="evaluationForm.speedRating"
+                      :max="5"
+                      :texts="['寰堝樊', '杈冨樊', '涓�鑸�', '杈冨ソ', '寰堝ソ']"
+                      show-text
+                      :colors="['#99A9BF', '#F7BA2A', '#FF9900']"
+                    />
+                  </div>
+                </div>
+              </div>
+            </div>
+           
+                       <!-- 璇勪环鍐呭 -->
+            <div class="form-item">
+              <label class="required">璇勪环鍐呭:</label>
+              <el-input
+                v-model="evaluationForm.content"
+                type="textarea"
+                :rows="4"
+                placeholder="璇疯緭鍏ヨ瘎浠峰唴瀹�"
+                maxlength="500"
+                show-word-limit
+              />
+            </div>
+            
+            <!-- 鍖垮悕鍙戝竷閫夐」 -->
+            <div class="form-item">
+              <label></label>
+              <div class="anonymous-option">
+                <el-checkbox v-model="evaluationForm.isAnonymous">
+                  鍖垮悕鍙戝竷璇勪环
+                </el-checkbox>
+                <div class="anonymous-tip">
+                  <el-icon><InfoFilled /></el-icon>
+                  <span>閫夋嫨鍖垮悕鍙戝竷鍚庯紝鎮ㄧ殑濮撳悕灏嗕笉浼氬湪璇勪环涓樉绀�</span>
+                </div>
+              </div>
+            </div>
+         </div>
+         <div class="evaluation-actions">
+           <el-button @click="goBack">杩斿洖</el-button>
+           <el-button 
+             type="primary" 
+             @click="handleSubmitEvaluation"
+             :loading="evaluationLoading"
+           >
+             鎻愪氦璇勪环
+           </el-button>
+         </div>
+       </div>
+     </el-card>
+   </div>
+ </template>
 
 <script setup lang="ts">
 import { onMounted, reactive, ref, computed, type CSSProperties } from 'vue'
 import { useRoute, useRouter } from 'vue-router'
-import { Document, User, Goods, List } from '@element-plus/icons-vue'
-import { fetchOrderDetail, submitEvaluate } from '@/api/tradeManage'
-import { ElMessage, FormInstance } from 'element-plus'
+import { Document, User, Goods, List, InfoFilled } from '@element-plus/icons-vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import orderApi from '@/api/orderApi'
+import { useUserInfo } from '@/stores/modules/userInfo'
+import createAxios from '@/utils/axios'
+import {queryUserDetail} from "@/api/userInfo";
+import productApi from "@/api/productApi";
+import sysUserService from "@/api/sysUser";
 
 const route = useRoute()
 const router = useRouter()
+const userStore = useUserInfo()
 const detail = reactive<any>({ items: [] })
-const formRef = ref<FormInstance>()
-const form = reactive({ score: 0, content: '' })
+const form = reactive<any>({ items: [] })
 const fileList = ref<any[]>([])
-const remarkItems = ref<any[]>([])
 const orderTableWrapRef = ref<HTMLElement | null>(null)
 const labelStyle = { width: '180px', maxWidth: '180px' }
 const contentStyle = { width: 'calc(50% - 180px)' }
 
-const rules = {
-  score: [{ required: true, message: '璇疯瘎鍒�', trigger: 'change' }],
-  content: [{ required: true, message: '璇疯緭鍏ヨ瘎浠峰唴瀹�', trigger: 'blur' }],
-}
+// 璇勪环琛ㄥ崟鏁版嵁
+const evaluationForm = reactive({
+  content: '',
+  overallRating: 0,    // 缁煎悎璇勫垎
+  serviceRating: 0,    // 鏈嶅姟璇勫垎
+  qualityRating: 0,    // 璐ㄩ噺璇勫垎
+  speedRating: 0,      // 閫熷害璇勫垎
+  isAnonymous: false   // 鏄惁鍖垮悕鍙戝竷
+})
+
+// 璇勪环loading鐘舵��
+const evaluationLoading = ref(false)
 
 // 璁$畻琛ㄦ牸鏁版嵁锛屾坊鍔犳眹鎬昏
 const tableData = computed(() => {
@@ -284,126 +375,217 @@
   return [...detail.items, summaryRow]
 })
 
+// 鐘舵�佹槧灏勶紙鍚庣涓枃 -> 鍓嶇鏋氫妇锛�
+const statusServerToUi: Record<string, string> = {
+  '寰呬笂浼犳枃浠�': 'WAIT_UPLOAD',
+  '寰呮巿鏉�': 'WAIT_AUTHORIZE',
+  '寰呬氦鏄撶‘璁�': 'WAIT_CONFIRM',
+  '宸插畬鎴�': 'COMPLETED',
+  '宸茶瘎浠�': 'EVALUATED',
+}
+
+const formatDateTime = (val?: string) => (val ? val.replace('T', ' ').slice(0, 19) : '')
+
+const normalizePriceType = (val?: string): 'points' | 'currency' | 'agreement' | 'free' => {
+  if (!val) return 'currency'
+  const s = String(val)
+  if (/(绉垎|points)/i.test(s)) return 'points'
+  if (/(鍗忚|agreement)/i.test(s)) return 'agreement'
+  if (/(鍏嶈垂|free)/i.test(s)) return 'free'
+  return 'currency'
+}
+
 onMounted(async () => {
-  // 浣跨敤鍓嶇妯℃嫙鏁版嵁浠ヤ究寮�鍙� UI锛堜笉鏀瑰姩鍚庣鏈嶅姟锛�
-  const mockDetail = {
-    orderNo: '4348442557619205545',
-    resourceTypeName: '杞欢浜у搧',
-    status: 'FINISH',
-    statusName: '宸插畬鎴�',
-    applyTime: '2025-05-21 10:00:00',
-    unitName: '涓氦鏂硅繙寤鸿鏈夐檺鍏徃',
-    userName: '寮犻潤',
-    userAccount: 'L20159922',
-    userDept: '淇℃伅涓績',
-    userPhone: '13800000000',
-    productName: '涓氦鏂硅繙鏅鸿兘瀹炴祴瀹為噺绠$悊绯荤粺',
-    supplier: '涓氦鏂硅繙绉戞妧鏈夐檺鍏徃',
-    industry: '寤虹瓚宸ョ▼',
-    projectUnit: '鍦熷缓宸ョ▼',
-    productType: '杞欢搴�',
-    productDesc:
-      '闈㈠悜宸ョ▼椤圭洰鐨勮川閲忓疄娴嬪疄閲忔暟瀛楀寲绠$悊绯荤粺锛屾敮鎸佹爣鍑嗗寲閲囬泦銆佽嚜鍔ㄧ粺璁′笌杩囩▼绠℃帶銆�',
-    items: [
-      {
-        id: '1',
-        name: '浼佷笟绉佹湁SaaS鐗堣鍙�',
-        saleType: '涔版柇',
-        accountCount: 50,
-        customerTarget: '浼佷笟',
-        concurrentNodes: 50,
-        pricePoint: 50000,
-        priceCash: 0,
-        quantity: 1,
-        period: 0,
-      },
-      {
-        id: '2',
-        name: '浼佷笟绉佹湁SaaS鐗圤TA鍗囩骇鏈嶅姟',
-        saleType: 'OTA鏈嶅姟',
-        accountCount: 50,
-        customerTarget: '浼佷笟',
-        concurrentNodes: 50,
-        pricePoint: 0,
-        priceCash: 7500,
-        quantity: 1,
-        period: 1,
-      },
-      {
-        id: '3',
-        name: '浼佷笟绉佹湁SaaS鐗堢敤鎴峰閲忓寘',
-        saleType: '绉佹湁澧為噺鍖�',
-        accountCount: 100,
-        customerTarget: '浼佷笟',
-        concurrentNodes: 100,
-        pricePoint: 0,
-        priceCash: 0,
-        priceProtocol: true,
-        quantity: 1,
-        period: 1,
-      },
-      {
-        id: '4',
-        name: '涓汉鍏湁SaaS鐗堣鍙�',
-        saleType: '绉佹湁澧為噺鍖�',
-        accountCount: 50,
-        customerTarget: '涓汉',
-        concurrentNodes: 50,
-        pricePoint: 0,
-        priceCash: 0,
-        quantity: 1,
-        period: 0,
-      },
-    ],
-    pointTotal: '50,000',
-    cashTotal: '7,500',
-  }
-
-  Object.assign(detail, mockDetail)
-  
-  // 娣诲姞妯℃嫙鏂囦欢鏁版嵁鐢ㄤ簬灞曠ず
-  fileList.value = [
-    {
-      name: '绛惧瓧鐩栫珷鏂囦欢.pdf',
-      size: 2621440, // 2.5MB
-      uid: '1',
-      status: 'success'
-    },
-    {
-      name: 'API Keys.txt',
-      size: 354, // 354 Bytes
-      uid: '2',
-      status: 'success'
+  const orderId = String(route.params.id || '')
+  if (!orderId) return
+  // 鑾峰彇鐢ㄦ埛淇℃伅
+  if (!userStore.getUserId) {
+    try {
+      const res: any = await queryUserDetail()
+      if (res?.code === 200 && res.data) {
+        userStore.updateUserDetail(res.data)
+      } else {
+        ElMessage.error(res?.msg || '鏃犳硶鑾峰彇鐢ㄦ埛淇℃伅锛岃鍏堢櫥褰�')
+        return
+      }
+    } catch (e) {
+      console.error('鑾峰彇鐢ㄦ埛璇︽儏澶辫触:', e)
+      ElMessage.error('鑾峰彇鐢ㄦ埛淇℃伅澶辫触锛岃绋嶅悗閲嶈瘯')
+      return
     }
-  ]
+  }
+  try {
+    const res = (await orderApi.getOrderDetail(orderId)) as any
+    const data = res?.data || {}
 
-  // 娣诲姞妯℃嫙浜ゆ槗淇℃伅澶囨敞鏁版嵁
-  remarkItems.value = (detail.items || []).map((item: any, index: number) => ({ 
-    name: item.name,
-    start: '2025-06-01', 
-    end: index === 0 || index === 3 ? '' : '2025-06-01', 
-    forever: index === 0 || index === 3, 
-    remark: index === 3 ? '寮�閫氱鐞嗗憳璐﹀彿1涓�,璐﹀彿admin' : '寮�閫氱鐞嗗憳璐﹀彿1涓�,璐﹀彿admin,鐧诲綍绠$悊鍛樿处鍙峰彲绠$悊鏅�氱敤鎴�' 
-  }))
-  
-  // 娉ㄩ噴鎺夊師鏈夌殑API璋冪敤锛屼娇鐢ㄦā鎷熸暟鎹�
-  // const { data } = (await fetchOrderDetail({ id: route.params.id })) as any
-  // Object.assign(detail, data || {})
+    const statusName: string = data.orderStatus || ''
+    const uiStatus = statusServerToUi[statusName] || 'INFO'
+
+    // 鏍规嵁浜у搧id鑾峰彇浜у搧淇℃伅锛屾洿鏂板ご閮ㄥ睍绀�
+    try {
+      if (data.productId) {
+        const detailRes: any = await productApi.getProductById({ id: data.productId })
+        if (detailRes?.code === 200 && detailRes.data) {
+          // 鐢ㄤ骇鍝佽鎯呰ˉ鍏ㄥご淇℃伅
+          data.productName = detailRes.data.name || data.productName
+          data.providerName = detailRes.data.submissionUnit || data.providerName
+          data.industry = detailRes.data.industrialChainName || data.industry
+          data.productDesc = detailRes.data.describe || data.productDesc
+          data.projectUnit = detailRes.data.importantAreaName || data.productDesc
+          data.productType = detailRes.data.typeName || data.productDesc
+        }
+      }
+    } catch (e) {
+      // 蹇界暐浜у搧璇︽儏澶辫触锛屼笉闃诲璁㈠崟璇︽儏
+    }
+
+    // 鑾峰彇鐢ㄦ埛淇℃伅
+    try {
+      const userRes: any = await sysUserService.getUserdetail({ userId: data.userId })
+      if (userRes?.code === 200 && userRes.data) {
+        // 鐢ㄤ骇鍝佽鎯呰ˉ鍏ㄥご淇℃伅
+        data.unitName = userRes.data.unitName || data.unitName
+        data.userName = userRes.data.name || data.userName
+        data.userDept = userRes.data.departmentName || data.userDept
+        data.userPhone = userRes.data.phone || data.userPhone
+        data.userAccount = userRes.data.username || data.userAccount
+      }
+    }catch (e){
+
+    }
+
+    // 鏄犲皠璁㈠崟璇︽儏澶撮儴淇℃伅
+    const head = {
+      orderNo: data.orderId,
+      resourceTypeName: '杞欢浜у搧',
+      status: uiStatus,
+      statusName,
+      applyTime: formatDateTime(data.applyTime),
+      unitName: data.unitName || '-',
+      userName: data.userName || '-',
+      userAccount: data.userAccount || '-',
+      userDept: data.userDept || '-',
+      userPhone: data.userPhone || '-',
+      productName: data.productName || '-',
+      supplier: data.providerName || '-',
+      industry: data.industry || '-',
+      projectUnit: data.projectUnit || '-',
+      productType: data.productType || '-',
+      productDesc: data.productDesc || '-',
+    }
+
+    // 鏄庣粏椤规槧灏�
+    const items: any[] = Array.isArray(data.orderDetails)
+      ? data.orderDetails.map((d: any, idx: number) => {
+          const pt = normalizePriceType(d.priceType)
+          return {
+            id: String(d.id ?? idx + 1),
+            name: d.suiteName,
+            saleType: d.salesForm,
+            accountCount: d.accountLimit,
+            customerTarget: d.customerType,
+            concurrentNodes: d.concurrentNodes,
+            pricePoint: pt === 'points' ? Number(d.unitPrice || 0) : 0,
+            priceCash: pt === 'currency' ? Number(d.unitPrice || 0) : 0,
+            priceProtocol: pt === 'agreement',
+            quantity: Number(d.quantity || 0),
+            period: Number(d.duration || 0),
+            remarks: d.remarks || '', // 鏂板 remarks 瀛楁
+          }
+        })
+      : []
+
+    // 姹囨�伙紙绠�鍗曠浉鍔狅細鍗曚环*鏁伴噺锛�
+    const pointTotalNum = items.reduce((sum, it) => sum + Number(it.pricePoint || 0) * Number(it.quantity || 0), 0)
+    const cashTotalNum = items.reduce((sum, it) => sum + Number(it.priceCash || 0) * Number(it.quantity || 0), 0)
+
+    Object.assign(detail, head, {
+      items,
+      pointTotal: pointTotalNum.toLocaleString(),
+      cashTotal: cashTotalNum.toLocaleString(),
+    })
+
+    // 鍒濆鍖栬〃鍗曟暟鎹�
+    form.items = (detail.items || []).map((item: any, index: number) => {
+      // 璁$畻鎺堟潈缁撴潫鏃堕棿
+      let endDate = ''
+      if (item.period > 0) {
+        // 濡傛灉鏈夋湡闄愶紝璁$畻缁撴潫鏃堕棿
+        const startDate = new Date(data.applyTime || new Date())
+        const endDateObj = new Date(startDate)
+        endDateObj.setFullYear(endDateObj.getFullYear() + item.period)
+        endDate = endDateObj.toISOString().split('T')[0] // 鏍煎紡鍖栦负 YYYY-MM-DD
+      }
+      
+      return { 
+        name: item.name,
+        start: data.applyTime ? data.applyTime.split('T')[0] : '', // 浣跨敤璁㈠崟鐢宠鏃堕棿
+        end: endDate, 
+        forever: item.period === 0, // 鏈熼檺涓�0鏃惰缃负姘镐箙
+        remark: item.remarks || '' // 浣跨敤濂椾欢淇℃伅涓殑remarks瀛楁
+      }
+    })
+
+    // 鑾峰彇璁㈠崟闄勪欢鍒楄〃锛堝鏋滄湁鐨勮瘽锛�
+    if (data.attachments && Array.isArray(data.attachments)) {
+      fileList.value = data.attachments.map((file: any) => ({
+        name: file.fileName || file.originalName,
+        size: file.fileSize || 0,
+        uid: file.id,
+        status: 'success',
+        url: file.fileUrl,
+        uploaded: true
+      }))
+    } else {
+      fileList.value = []
+    }
+  } catch (error) {
+    console.error('鑾峰彇璁㈠崟璇︽儏澶辫触:', error)
+    ElMessage.error('鑾峰彇璁㈠崟璇︽儏澶辫触')
+  }
 })
 
 const goBack = () => router.back()
 const submit = async () => {
-  await formRef.value?.validate()
-  
-  // 妯℃嫙鎻愪氦鎴愬姛鍝嶅簲
-  const mockResponse = { code: 200 }
-  
-  // 娉ㄩ噴鎺夊師鏈夌殑API璋冪敤锛屼娇鐢ㄦā鎷熸暟鎹�
-  // const { code } = (await submitEvaluate({ id: route.params.id, ...form })) as any
-  
-  if (mockResponse.code === 200) {
-    ElMessage.success('鎻愪氦鎴愬姛')
-    router.back()
+  try {
+    const orderId = String(route.params.id || '')
+    const userId = userStore.getUserId ? userStore.getUserId : undefined
+    
+    if (!orderId || !userId) {
+      ElMessage.error('璁㈠崟ID鎴栫敤鎴稩D涓嶈兘涓虹┖')
+      return
+    }
+
+    // 纭鎿嶄綔
+    await ElMessageBox.confirm('纭畾瑕佺‘璁や氦鏄撳苟鎻愪氦瀹℃壒鍚楋紵', '纭鎿嶄綔', {
+      confirmButtonText: '纭畾',
+      cancelButtonText: '鍙栨秷',
+      type: 'warning'
+    })
+
+    // 璋冪敤鏇存柊璁㈠崟璇︽儏API锛屽皢鐘舵�佹洿鏂颁负"宸茶瘎浠�"
+    const updateData = {
+      orderId: orderId,
+      orderStatus: '宸茶瘎浠�', // 鏇存柊涓哄凡璇勪环鐘舵��
+      orderDetails: detail.items.map((item: any) => ({
+        id: item.id,
+        remarks: item.remarks || '' // 浣跨敤濂椾欢淇℃伅涓殑remarks瀛楁
+      }))
+    }
+
+    const res = (await orderApi.updateOrderDetail(updateData)) as any
+    
+    if (res?.code === 200) {
+      ElMessage.success('浜ゆ槗纭鎴愬姛锛屽凡鎻愪氦瀹℃壒')
+      router.back()
+    } else {
+      ElMessage.error(res?.msg || '浜ゆ槗纭澶辫触')
+    }
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error('浜ゆ槗纭澶辫触:', error)
+      ElMessage.error('浜ゆ槗纭澶辫触')
+    }
   }
 }
 
@@ -481,15 +663,6 @@
 // 鏂囦欢鍒楄〃琛ㄦ牸琛ㄤ綋鏂囧瓧澶у皬
 const fileTableCellStyle: CSSProperties = { fontSize: '12px' }
 
-// 浜ゆ槗淇℃伅澶囨敞琛ㄦ牸琛ㄥご鏂囧瓧灞呬腑锛屼絾绗竴鍒楃殑"浜ゆ槗淇℃伅澶囨敞"鏂囧瓧闈犲乏瀵归綈
-const remarkTableHeaderStyle: CSSProperties = { 
-  textAlign: 'center',
-  fontSize: '14px',
-  background: '#f3f6fb'
-}
-// 浜ゆ槗淇℃伅澶囨敞琛ㄦ牸琛ㄤ綋鏂囧瓧澶у皬
-const remarkTableCellStyle: CSSProperties = { fontSize: '12px' }
-
 // 鏂囦欢澶у皬鏍煎紡鍖�
 const formatFileSize = (size: number) => {
   if (!size || size === 0) return '0 Bytes';
@@ -499,10 +672,265 @@
   return parseFloat((size / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
 };
 
-// 鏂囦欢棰勮澶勭悊
-const handlePreview = (file: any) => {
-  ElMessage.info(`棰勮鏂囦欢锛�${file.name}`)
+// 鍒ゆ柇鏂囦欢鏄惁鍙瑙�
+const isPreviewable = (file: any) => {
+  // 棣栧厛妫�鏌IME绫诲瀷
+  const previewableTypes = [
+    'image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/bmp', 'image/webp',
+    'text/plain', 'text/html', 'text/css', 'text/javascript',
+    'application/pdf',
+    'application/vnd.openxmlformats-officedocument.wordprocessingml.document', // .docx
+    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx
+    'application/vnd.openxmlformats-officedocument.presentationml.presentation', // .pptx
+  ]
+  
+  // 濡傛灉MIME绫诲瀷鍖归厤锛岀洿鎺ヨ繑鍥瀟rue
+  if (previewableTypes.includes(file.type || '')) {
+    return true
+  }
+  
+  // 濡傛灉MIME绫诲瀷涓虹┖鎴栦笉鍖归厤锛屾牴鎹枃浠舵墿灞曞悕鍒ゆ柇
+  const fileName = file.name || ''
+  const fileExtension = fileName.toLowerCase().split('.').pop()
+  
+  const previewableExtensions = [
+    'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp',
+    'txt', 'html', 'htm', 'css', 'js',
+    'pdf',
+    'docx', 'xlsx', 'pptx'
+  ]
+  
+  return previewableExtensions.includes(fileExtension)
 }
+
+// 鍒ゆ柇鏂囦欢鏄惁宸蹭笂浼犳垚鍔�
+const isFileUploaded = (file: any) => {
+  // 鏂囦欢鏈塽rl涓旂姸鎬佷负success琛ㄧず宸蹭笂浼犳垚鍔�
+  return file.url && (file.status === 'success' || file.uploaded)
+}
+
+// 鏂囦欢棰勮
+const handlePreview = async (file: any) => {
+  if (!file.url) {
+    ElMessage.warning('鏂囦欢閾炬帴涓嶅瓨鍦�')
+    return
+  }
+  
+  // 鑾峰彇鏂囦欢鎵╁睍鍚�
+  const fileName = file.name || ''
+  const fileExtension = fileName.toLowerCase().split('.').pop()
+  
+  let previewUrl = file.url
+  
+  // 濡傛灉鏂囦欢瀛樺偍鍦∕inIO锛屼紭鍏堜娇鐢ㄩ瑙圲RL
+  if (file.url.includes('order-attachments')) {
+    try {
+      // 棣栧厛灏濊瘯鑾峰彇棰勮URL
+      const previewResponse = await createAxios({
+        url: `/admin/file/preview`,
+        method: 'GET',
+        params: {
+          fileName: file.url
+        }
+      })
+      
+      console.log('棰勮URL鍝嶅簲:', previewResponse)
+      
+      // 妫�鏌ュ搷搴旀牸寮�
+      const responseData = previewResponse as any
+      if (responseData && responseData.code === 200 && responseData.data) {
+        previewUrl = responseData.data
+        console.log('浣跨敤棰勮URL:', previewUrl)
+      } else {
+        console.log('棰勮URL鑾峰彇澶辫触锛屼娇鐢ㄤ笅杞芥柟寮�')
+        // 濡傛灉棰勮URL鑾峰彇澶辫触锛屽洖閫�鍒颁笅杞芥柟寮�
+        const response = await createAxios({
+          url: `/admin/file/download`,
+          method: 'GET',
+          responseType: 'blob',
+          params: {
+            fileName: file.url,
+            originalName: file.name
+          }
+        })
+        
+        // 鍒涘缓棰勮URL
+        const blob = new Blob([response as any])
+        previewUrl = window.URL.createObjectURL(blob)
+        console.log('浣跨敤Blob URL:', previewUrl)
+      }
+    } catch (error) {
+      console.error('鑾峰彇鏂囦欢澶辫触:', error)
+      ElMessage.error('鑾峰彇鏂囦欢澶辫触锛屾棤娉曢瑙�')
+      return
+    }
+  }
+  
+  // 鍥剧墖鏂囦欢鐩存帴鍦ㄦ柊绐楀彛鎵撳紑
+  if ((file.type && file.type.startsWith('image/')) || 
+      ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(fileExtension)) {
+    console.log('棰勮鍥剧墖鏂囦欢:', previewUrl)
+    window.open(previewUrl, '_blank')
+    return
+  }
+  
+  // PDF鏂囦欢鍦ㄦ柊绐楀彛鎵撳紑
+  if (file.type === 'application/pdf' || fileExtension === 'pdf') {
+    console.log('棰勮PDF鏂囦欢:', previewUrl)
+    window.open(previewUrl, '_blank')
+    return
+  }
+  
+  // 鏂囨湰鏂囦欢鍦ㄦ柊绐楀彛鎵撳紑
+  if ((file.type && file.type.startsWith('text/')) || 
+      ['txt', 'html', 'htm', 'css', 'js'].includes(fileExtension)) {
+    console.log('棰勮鏂囨湰鏂囦欢:', previewUrl)
+    window.open(previewUrl, '_blank')
+    return
+  }
+  
+  // Office鏂囨。鍜屽叾浠栨枃浠剁被鍨嬶紝灏濊瘯鍦ㄦ柊绐楀彛鎵撳紑
+  try {
+    console.log('棰勮鍏朵粬鏂囦欢:', previewUrl)
+    window.open(previewUrl, '_blank')
+  } catch (error) {
+    console.error('棰勮澶辫触:', error)
+    ElMessage.error('棰勮澶辫触锛岃涓嬭浇鍚庢煡鐪�')
+  }
+}
+
+// 鏂囦欢涓嬭浇
+const handleDownload = async (file: any) => {
+  if (!file.url) {
+    ElMessage.warning('鏂囦欢閾炬帴涓嶅瓨鍦�')
+    return
+  }
+  
+  console.log('寮�濮嬩笅杞芥枃浠�:', file.name, 'URL:', file.url)
+  
+  try {
+    // 濡傛灉鏂囦欢瀛樺偍鍦∕inIO锛屼娇鐢ㄥ悗绔洿鎺ヤ笅杞紸PI
+    if (file.url.includes('order-attachments')) {
+      console.log('浣跨敤MinIO涓嬭浇API')
+      
+      // 浣跨敤axios閫氳繃浠g悊璁块棶鍚庣API
+      const response = await createAxios({
+        url: `/admin/file/download`,
+        method: 'GET',
+        responseType: 'blob',
+        params: {
+          fileName: file.url,
+          originalName: file.name
+        }
+      })
+      
+      console.log('涓嬭浇鍝嶅簲:', response)
+      
+      // 鍒涘缓涓嬭浇閾炬帴
+      const blob = new Blob([response as any])
+      const downloadUrl = window.URL.createObjectURL(blob)
+      
+      console.log('鍒涘缓涓嬭浇閾炬帴:', downloadUrl)
+      
+      const link = document.createElement('a')
+      link.href = downloadUrl
+      link.download = file.name || 'download'
+      link.target = '_blank'
+      link.rel = 'noopener noreferrer'
+      
+      document.body.appendChild(link)
+      link.click()
+      document.body.removeChild(link)
+      
+      // 娓呯悊URL瀵硅薄
+      window.URL.revokeObjectURL(downloadUrl)
+      
+      ElMessage.success('鏂囦欢涓嬭浇鎴愬姛')
+    } else {
+      console.log('浣跨敤鐩存帴URL涓嬭浇')
+      // 鍏朵粬鎯呭喌鐩存帴浣跨敤鍘烾RL
+      const link = document.createElement('a')
+      link.href = file.url
+      link.download = file.name || 'download'
+      link.target = '_blank'
+      link.rel = 'noopener noreferrer'
+      
+      document.body.appendChild(link)
+      link.click()
+      document.body.removeChild(link)
+      
+      ElMessage.success('寮�濮嬩笅杞芥枃浠�')
+    }
+  } catch (error) {
+    console.error('涓嬭浇澶辫触:', error)
+    ElMessage.error('涓嬭浇澶辫触锛岃閲嶈瘯')
+  }
+}
+
+// 鎻愪氦璇勪环澶勭悊
+const handleSubmitEvaluation = async () => {
+  // 楠岃瘉璇勫垎鏄惁宸插~鍐�
+  if (!evaluationForm.overallRating || !evaluationForm.serviceRating || 
+      !evaluationForm.qualityRating || !evaluationForm.speedRating) {
+    ElMessage.warning('璇峰畬鎴愭墍鏈夎瘎鍒嗛」鐩�')
+    return
+  }
+  
+  if (!evaluationForm.content.trim()) {
+    ElMessage.warning('璇疯緭鍏ヨ瘎浠峰唴瀹�')
+    return
+  }
+
+  try {
+    evaluationLoading.value = true
+    await ElMessageBox.confirm('纭畾瑕佹彁浜よ瘎浠峰悧锛�', '纭鎿嶄綔', {
+      confirmButtonText: '纭畾',
+      cancelButtonText: '鍙栨秷',
+      type: 'warning'
+    })
+
+    const orderId = String(route.params.id || '')
+    const userId = userStore.getUserId ?  userStore.getUserId : undefined
+    const content = evaluationForm.content.trim()
+
+    if (!orderId || !userId) {
+      ElMessage.error('璁㈠崟ID鎴栫敤鎴稩D涓嶈兘涓虹┖')
+      evaluationLoading.value = false
+      return
+    }
+
+    // 璋冪敤鎻愪氦璇勪环API
+    const result = await orderApi.submitEvaluation({
+      orderId: orderId,
+      evaluationContent: content,
+      evaluatorId: userId,
+      evaluatorName: evaluationForm.isAnonymous ? '鍖垮悕鐢ㄦ埛' : (userStore.getUserDetail || '绠$悊鍛�'),
+      overallRating: evaluationForm.overallRating,
+      serviceRating: evaluationForm.serviceRating,
+      qualityRating: evaluationForm.qualityRating,
+      speedRating: evaluationForm.speedRating,
+      isAnonymous: evaluationForm.isAnonymous
+    })
+
+    if (result && result.code === 200) {
+      // 鏇存柊璁㈠崟璇勪环鐘舵�佷负宸茶瘎浠�
+      await orderApi.updateOrderStatusToIsEvaluate(orderId)
+      ElMessage.success('璇勪环鎻愪氦鎴愬姛')
+      router.back()    
+      evaluationLoading.value = false
+    } else {
+      ElMessage.error(result?.msg || '璇勪环鎻愪氦澶辫触')
+    }
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error('璇勪环鎻愪氦澶辫触:', error)
+      ElMessage.error('璇勪环鎻愪氦澶辫触')
+    }
+  } finally {
+    evaluationLoading.value = false
+  }
+}
+
 
 // 鐥囩粨涓庝慨澶嶈鏄庯細
 // 1) Element Plus 鐨� el-table 瀛愬垪 width 鐧惧垎姣旀槸鐩稿浜庤〃鏍煎鍣ㄧ殑鍍忕礌瀹藉害璁$畻锛屼絾鍙湁鍦ㄨ〃鏍煎鍣ㄦ湁鏄庣‘瀹藉害鏃舵墠鐢熸晥銆�
@@ -706,36 +1134,152 @@
   text-align: left !important;
 }
 
-/* 浜ゆ槗淇℃伅澶囨敞琛ㄦ牸鏍峰紡 */
-.remark-section {
-  margin-top: 15px;
-  .remark-table {
-    width: 100%;
-    .remark-name {
-      font-weight: 500;
+/* 鏂囦欢鎿嶄綔鎸夐挳鏍峰紡 */
+.file-actions {
+  display: flex;
+  gap: 8px;
+  align-items: center;
+  justify-content: center;
+  
+  .preview-btn {
+    color: #409eff;
+    &:hover {
+      text-decoration: underline;
     }
-    .remark-date {
-      color: #606266;
+    &:disabled {
+      color: #c0c4cc;
+      cursor: not-allowed;
     }
-    .remark-content {
-      color: #606266;
-      line-height: 1.4;
+  }
+  
+  .download-btn {
+    color: #67c23a;
+    &:hover {
+      text-decoration: underline;
+    }
+    &:disabled {
+      color: #c0c4cc;
+      cursor: not-allowed;
     }
   }
 }
 
-/* 浜ゆ槗淇℃伅澶囨敞琛ㄦ牸琛ㄥご绗竴鍒�"浜ゆ槗淇℃伅澶囨敞"鏂囧瓧闈犲乏瀵归綈 */
-.remark-table :deep(.el-table__header-wrapper thead tr th:first-child) {
-  text-align: left !important;
+/* 浜ゆ槗淇℃伅澶囨敞琛ㄦ牸鏍峰紡 */
+.remark-table {
+  width: 100%;
+  .end-time-wrapper {
+    display: flex;
+    align-items: center;
+    gap: 10px;
+    .forever-text {
+      color: #409eff;
+      font-weight: 500;
+    }
+  }
 }
 
-/* 鎿嶄綔鎸夐挳鏍峰紡 */
-.action-buttons {
-  display: flex;
-  justify-content: flex-end;
-  margin-top: 15px;
-  .el-button {
-    margin-left: 10px;
+/* 浜ゆ槗璇勪环鏍峰紡 */
+.evaluation-content {
+  .evaluation-form {
+    /* 璇勫垎閮ㄥ垎鏍峰紡 */
+    .rating-section {
+      margin-bottom: 30px;
+      
+      .rating-title {
+        font-weight: 600;
+        margin-bottom: 15px;
+        color: #303133;
+      }
+      
+             .rating-items {
+         display: flex;
+         flex-direction: column;
+         gap: 20px;
+         
+         .rating-row {
+           display: flex;
+           gap: 40px;
+           
+           .rating-item {
+             flex: 1;
+             display: flex;
+             align-items: center;
+             
+             label {
+               width: 100px;
+               line-height: 32px;
+               margin-right: 15px;
+               font-weight: 500;
+               white-space: nowrap;
+               
+               &.required::before {
+                 content: '*';
+                 color: #f56c6c;
+                 margin-right: 4px;
+               }
+             }
+             
+             .el-rate {
+               flex: 1;
+             }
+           }
+         }
+       }
+    }
+    
+    .form-item {
+      display: flex;
+      align-items: flex-start;
+      margin-bottom: 20px;
+      
+      label {
+        width: 120px;
+        line-height: 32px;
+        margin-right: 10px;
+        font-weight: 500;
+        
+        &.required::before {
+          content: '*';
+          color: #f56c6c;
+          margin-right: 4px;
+        }
+      }
+      
+             .el-textarea {
+         flex: 1;
+       }
+       
+               /* 鍖垮悕閫夐」鏍峰紡 */
+        .anonymous-option {
+          display: flex;
+          align-items: center;
+          gap: 15px;
+          
+          .anonymous-tip {
+            display: flex;
+            align-items: center;
+            gap: 6px;
+            color: #909399;
+            font-size: 12px;
+            
+            .el-icon {
+              color: #409eff;
+              font-size: 14px;
+            }
+          }
+        }
+     }
+   }
+  
+  .evaluation-actions {
+    display: flex;
+    justify-content: center;
+    gap: 15px;
+    margin-top: 30px;
+    
+    .el-button {
+      min-width: 100px;
+    }
   }
 }
 </style>

--
Gitblit v1.8.0