Browse Source

add 订单添加图片UI

tea 3 months ago
parent
commit
91097e499f

+ 472 - 87
kxmall-admin-ui/src/views/order/storeOrder/index.vue

@@ -1,16 +1,16 @@
 <template>
   <div class="app-container">
     <el-tabs v-model="queryParams.orderStatus" @tab-click="onOrderStatusChange">
-      <el-tab-pane v-for="item in status" :key="item.name" :label="item.label" :name="item.name"/>
+      <el-tab-pane v-for="item in status" :key="item.name" :label="item.label" :name="item.name" />
     </el-tabs>
     <el-form ref="queryForm" :model="queryParams" size="small" :inline="true" label-width="68px">
       <el-form-item prop="storageId">
         <el-select v-model="queryParams.storageId" placeholder="请选择前置仓" clearable>
-          <el-option v-for="item in storages" :key="item.id" :label="item.name" :value="item.id"/>
+          <el-option v-for="item in storages" :key="item.id" :label="item.name" :value="item.id" />
         </el-select>
       </el-form-item>
       <el-form-item prop="orderId">
-        <el-input v-model="queryParams.orderId" clearable placeholder="输入订单号" @keyup.enter.native="toQuery"/>
+        <el-input v-model="queryParams.orderId" clearable placeholder="输入订单号" @keyup.enter.native="toQuery" />
       </el-form-item>
       <el-form-item prop="createTime">
         <el-date-picker
@@ -39,12 +39,13 @@
         width="200"
         prop="orderId"
         align="center"
-        label="订单号">
+        label="订单号"
+      >
         <template slot-scope="scope">
           <el-tooltip content="点击复制订单号" placement="top">
             <span ref="orderId" @click="copyOrderId(scope.row.orderId)">
               {{ scope.row.orderId }}
-             <span v-if="isToday(scope.row.createTime)" style="color: red">今</span>
+              <span v-if="isToday(scope.row.createTime)" style="color: red">今</span>
             </span>+
           </el-tooltip>
         </template>
@@ -103,8 +104,8 @@
       >
         <template slot-scope="{row}">
           <el-tag :type="row.shippingType==1 ? 'error' : 'success'">{{
-              row.shippingType == 1 ? '配送' : '自提'
-            }}
+            row.shippingType == 1 ? '配送' : '自提'
+          }}
           </el-tag>
         </template>
       </el-table-column>
@@ -244,7 +245,7 @@
             @command="handleDeliveryCommand(row, $event)"
           >
             <el-button type="primary" size="mini">
-              配送方式<i class="el-icon-arrow-down el-icon--right"></i>
+              配送方式<i class="el-icon-arrow-down el-icon--right" />
             </el-button>
             <el-dropdown-menu slot="dropdown">
               <el-dropdown-item command="system">系统配送</el-dropdown-item>
@@ -271,7 +272,7 @@
       @pagination="getList"
     />
     <!-- 添加或修改订单对话框 -->
-    <el-dialog title="订单详情" :visible.sync="open" width="1050px" append-to-body class="order-detail-dialog">
+    <el-dialog title="订单详情" :visible.sync="open" width="1050px" append-to-body class="order-detail-dialog" :custom-class	="order-detail-dialog">
       <div class="detail-container">
         <!-- 基本信息部分 -->
         <div class="detail-section">
@@ -323,8 +324,8 @@
           <div class="detail-item">
             <span class="item-label">备注:</span>
             <span class="item-value" :class="{'empty-remark': !form.remark}">
-          {{ form.remark || '无备注' }}
-        </span>
+              {{ form.remark || '无备注' }}
+            </span>
           </div>
         </div>
 
@@ -353,7 +354,6 @@
           </el-row>
         </div>
 
-
         <!-- 骑手信息部分 -->
         <div v-if="form.riderOrder" class="detail-section">
           <h3 class="section-title">骑手信息</h3>
@@ -408,8 +408,8 @@
               <div class="detail-item">
                 <span class="item-label">支付金额:</span>
                 <span class="item-value highlight">
-              {{ form.payIntegral ? '积分' + form.payIntegral : '¥' + form.payPrice }}
-            </span>
+                  {{ form.payIntegral ? '积分' + form.payIntegral : '¥' + form.payPrice }}
+                </span>
               </div>
             </el-col>
             <el-col :span="8">
@@ -522,14 +522,38 @@
           <div class="summary-row">
             <span class="summary-label">优惠:</span>
             <span class="summary-value highlight">
-          ¥{{ Number(form.originalTotalPrice) - Number(form.payPrice) + Number(form.freightPrice) }}
-        </span>
+              ¥{{ Number(form.originalTotalPrice) - Number(form.payPrice) + Number(form.freightPrice) }}
+            </span>
           </div>
           <div class="summary-row total-row">
             <span class="summary-label">实付金额:</span>
             <span class="summary-value highlight">
-          {{ form.payIntegral ? '积分' + form.payIntegral : '¥' + form.payPrice }}
-        </span>
+              {{ form.payIntegral ? '积分' + form.payIntegral : '¥' + form.payPrice }}
+            </span>
+          </div>
+        </div>
+
+        <!-- 订单照片部分 -->
+        <div v-if="form.photos && form.photos.length > 0" class="detail-section">
+          <h3 class="section-title">订单照片</h3>
+          <div class="photo-gallery">
+            <!-- 按照片类型分组展示 -->
+            <div v-for="(groupData, groupType) in groupedPhotos" :key="groupType" class="photo-group">
+              <h4 class="photo-group-title">{{ getPhotoTypeLabel(groupType) + ' - ' + groupData[0].uploadTime }}</h4>
+              <div class="photo-grid">
+                <div
+                  v-for="(photo) in groupData"
+                  :key="photo.id"
+                  class="photo-item"
+                  @click="previewPhoto(photo.imageUrl, groupData)"
+                >
+                  <img :src="photo.imageUrl" alt="订单照片" class="photo-thumbnail">
+                  <div class="photo-overlay">
+                    <i class="el-icon-zoom-in" />
+                  </div>
+                </div>
+              </div>
+            </div>
           </div>
         </div>
       </div>
@@ -577,20 +601,81 @@
     >
       <el-form>
         <el-form-item label="核销码">
-          <el-input v-model="verifyCode" placeholder="请输入核销码"></el-input>
+          <el-input v-model="verifyCode" placeholder="请输入核销码" />
         </el-form-item>
       </el-form>
       <span slot="footer" class="dialog-footer">
-    <el-button @click="verifyDialogVisible = false">取消</el-button>
-    <el-button type="primary" @click="confirmCompletePickup">确认</el-button>
-  </span>
+        <el-button @click="verifyDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="confirmCompletePickup">确认</el-button>
+      </span>
+    </el-dialog>
+
+    <!-- 照片预览弹窗 -->
+    <el-dialog
+      :visible.sync="photoPreviewVisible"
+      :show-close="false"
+      :modal="true"
+      width="80%"
+      class="photo-preview-dialog"
+      @close="closePhotoPreview"
+    >
+      <div class="photo-preview-container">
+        <div class="photo-preview-header">
+          <span class="preview-title">{{ currentPhotoGroup.title }}</span>
+          <span class="preview-counter">{{ currentPhotoIndex + 1 }} / {{ currentPhotoGroup.photos.length }}</span>
+          <el-button
+            type="text"
+            class="close-btn"
+            icon="el-icon-close"
+            @click="closePhotoPreview"
+          />
+        </div>
+        <div class="photo-preview-content">
+          <div class="photo-navigation">
+            <el-button
+              v-if="currentPhotoIndex > 0"
+              type="text"
+              class="nav-btn prev-btn"
+              icon="el-icon-arrow-left"
+              @click="prevPhoto"
+            />
+            <div class="main-photo">
+              <img
+                :src="currentPhoto.imageUrl"
+                :alt="currentPhoto.imageUrl"
+                class="preview-image"
+                @load="onImageLoad"
+                @error="onImageError"
+              >
+            </div>
+            <el-button
+              v-if="currentPhotoIndex < currentPhotoGroup.photos.length - 1"
+              type="text"
+              class="nav-btn next-btn"
+              icon="el-icon-arrow-right"
+              @click="nextPhoto"
+            />
+          </div>
+          <div v-if="currentPhotoGroup.photos.length > 1" class="photo-thumbnails">
+            <div
+              v-for="(photo, index) in currentPhotoGroup.photos"
+              :key="photo.id"
+              class="thumbnail-item"
+              :class="{ active: index === currentPhotoIndex }"
+              @click="setCurrentPhoto(index)"
+            >
+              <img :src="photo.imageUrl" :alt="photo.imageUrl" class="thumbnail-image">
+            </div>
+          </div>
+        </div>
+      </div>
     </el-dialog>
   </div>
 </template>
 
 <script>
-import Clipboard from 'clipboard';
-import {multiply} from '@/utils/math'
+import Clipboard from 'clipboard'
+import { multiply } from '@/utils/math'
 import {
   listStoreOrder /** delStoreOrder, addStoreOrder, updateStoreOrder*/,
   getStoreOrder,
@@ -603,8 +688,8 @@ import {
   thirdPartyDistribution,
   cancelOrder
 } from '@/api/order/storeOrder'
-import {listAllStorage} from '@/api/storage/storage'
-import {getRiderByStorageId} from '@/api/rider/rider'
+import { listAllStorage } from '@/api/storage/storage'
+import { getRiderByStorageId } from '@/api/rider/rider'
 
 export default {
   name: 'StoreOrder',
@@ -616,6 +701,13 @@ export default {
       verifyDialogVisible: false,
       verifyCode: '',
       currentPickupOrderId: null,
+      // 照片预览相关
+      photoPreviewVisible: false,
+      currentPhotoIndex: 0,
+      currentPhotoGroup: {
+        title: '',
+        photos: []
+      },
       // 按钮loading
       buttonLoading: false,
       // 遮罩层
@@ -651,17 +743,17 @@ export default {
       form: {},
       // 表单校验
       status: [
-        {label: '待配货', name: '14'},
-        {label: '配货中', name: '16'},
-        {label: '待配送', name: '20'},
-        {label: '配送中', name: '30'},
-        {label: '已完成', name: '40,50'},
-        {label: '配送异常', name: '32'},
-        {label: '超时订单', name: '34'},
-        {label: '待支付', name: '10'},
-        {label: '已退款', name: '70'},
-        {label: '已取消', name: '80'},
-        {label: '全部订单', name: 'all'}
+        { label: '待配货', name: '14' },
+        { label: '配货中', name: '16' },
+        { label: '待配送', name: '20' },
+        { label: '配送中', name: '30' },
+        { label: '已完成', name: '40,50' },
+        { label: '配送异常', name: '32' },
+        { label: '超时订单', name: '34' },
+        { label: '待支付', name: '10' },
+        { label: '已退款', name: '70' },
+        { label: '已取消', name: '80' },
+        { label: '全部订单', name: 'all' }
       ],
       storages: []
     }
@@ -671,6 +763,32 @@ export default {
     //   const { modes, currentMode } = this.dialog
     //   return modes[currentMode]?.title || ''
     // }
+
+    // 按照片类型分组
+    groupedPhotos() {
+      if (!this.form.photos || this.form.photos.length === 0) {
+        return {}
+      }
+
+      const groups = {}
+      this.form.photos.forEach(photo => {
+        const type = photo.screenshotType || 0
+        if (!groups[type]) {
+          groups[type] = []
+        }
+        groups[type].push(photo)
+      })
+
+      return groups
+    },
+
+    // 当前预览的照片
+    currentPhoto() {
+      if (this.currentPhotoGroup.photos && this.currentPhotoGroup.photos.length > 0) {
+        return this.currentPhotoGroup.photos[this.currentPhotoIndex] || {}
+      }
+      return {}
+    }
   },
   created() {
     this.getList()
@@ -679,7 +797,7 @@ export default {
   methods: {
     multiply,
     listAllStorage() {
-      listAllStorage().then(({data}) => {
+      listAllStorage().then(({ data }) => {
         this.storages = data
         this.storages.forEach(storage => {
           this.storageMap[storage.id] = storage.name
@@ -687,24 +805,24 @@ export default {
       })
     },
     isToday(dateString) {
-      if (!dateString) return false;
-      const orderDate = new Date(dateString);
-      const today = new Date();
+      if (!dateString) return false
+      const orderDate = new Date(dateString)
+      const today = new Date()
 
       return (
         orderDate.getFullYear() === today.getFullYear() &&
         orderDate.getMonth() === today.getMonth() &&
         orderDate.getDate() === today.getDate()
-      );
+      )
     },
     getRiderStatusTagType(status) {
       const typeMap = {
-        0: 'warning',   // 待取货 - 黄色
-        1: 'primary',   // 配送中 - 蓝色
-        2: 'danger',    // 超时 - 红色
-        3: 'danger',    // 配送异常 - 红色
-        4: 'success',   // 已完成 - 绿色
-        5: 'info'       // 被更改分配 - 灰色
+        0: 'warning', // 待取货 - 黄色
+        1: 'primary', // 配送中 - 蓝色
+        2: 'danger', // 超时 - 红色
+        3: 'danger', // 配送异常 - 红色
+        4: 'success', // 已完成 - 绿色
+        5: 'info' // 被更改分配 - 灰色
       }
       return typeMap[status] || ''
     },
@@ -720,9 +838,9 @@ export default {
       return statusMap[status] || '未知状态'
     },
     showVerifyDialog(orderId) {
-      this.currentPickupOrderId = orderId;
-      this.verifyDialogVisible = true;
-      this.verifyCode = '';
+      this.currentPickupOrderId = orderId
+      this.verifyDialogVisible = true
+      this.verifyCode = ''
     },
     // In methods section
     handleThirdPartyDistribution(row) {
@@ -758,20 +876,20 @@ export default {
     handleDeliveryCommand(row, command) {
       switch (command) {
         case 'system':
-          this.allot(row.storeId, row.orderId, row.postId);
-          break;
+          this.allot(row.storeId, row.orderId, row.postId)
+          break
         case 'thirdParty':
-          this.handleOperation(row.id, 'thirdPartyDistribution');
-          break;
+          this.handleOperation(row.id, 'thirdPartyDistribution')
+          break
         case 'merchant':
-          this.handleOperation(row.id, 'merchantDistribution');
-          break;
+          this.handleOperation(row.id, 'merchantDistribution')
+          break
       }
     },
     async confirmCompletePickup() {
       if (!this.verifyCode) {
-        this.$message.warning('请输入核销码');
-        return;
+        this.$message.warning('请输入核销码')
+        return
       }
 
       try {
@@ -780,11 +898,11 @@ export default {
         await completePickup({
           id: this.currentPickupOrderId,
           verifyCode: this.verifyCode
-        });
+        })
 
-        this.$message.success('核销成功');
-        this.verifyDialogVisible = false;
-        this.handleQuery();
+        this.$message.success('核销成功')
+        this.verifyDialogVisible = false
+        this.handleQuery()
       } finally {
         this.loading = false
       }
@@ -792,26 +910,26 @@ export default {
     copyOrderId(orderId) {
       const clipboard = new Clipboard(this.$refs.orderId, {
         text: () => orderId
-      });
+      })
 
       clipboard.on('success', () => {
-        this.$message.success('订单号已复制');
-        clipboard.destroy(); // 销毁 clipboard 实例
-      });
+        this.$message.success('订单号已复制')
+        clipboard.destroy() // 销毁 clipboard 实例
+      })
 
       clipboard.on('error', () => {
-        this.$message.error('复制失败');
-        clipboard.destroy(); // 销毁 clipboard 实例
-      });
+        this.$message.error('复制失败')
+        clipboard.destroy() // 销毁 clipboard 实例
+      })
 
       // 手动触发点击事件
-      this.$refs.orderId.click();
+      this.$refs.orderId.click()
     },
     /** 查询订单列表 */
     getList() {
       this.loading = true
       this.storeOrderList = []
-      const params = {...this.queryParams}
+      const params = { ...this.queryParams }
       if (params.createTime) {
         params.startTime = params.createTime[0]
         params.endTime = params.createTime[1]
@@ -843,7 +961,7 @@ export default {
       this.handleQuery()
     },
     viewDetail(id) {
-      getStoreOrder(id).then(({data}) => {
+      getStoreOrder(id).then(({ data }) => {
         this.form = data
         this.open = true
       })
@@ -882,18 +1000,18 @@ export default {
     },
     getStatusTagType(status) {
       const typeMap = {
-        14: 'warning',  // 待配货 - 黄色
-        16: 'warning',  // 配货中 - 黄色
-        20: 'primary',  // 待配送 - 蓝色
-        30: 'primary',  // 配送中 - 蓝色
-        40: 'success',  // 已完成 - 绿色
-        50: 'success',  // 已完成 - 绿色
-        32: 'danger',   // 配送异常 - 红色
-        34: 'danger',   // 超时订单 - 红色
-        10: 'info',     // 待支付 - 灰色
-        70: 'danger',   // 已退款 - 红色
-        80: 'info',     // 已取消 - 灰色
-        90: 'info'      // 已取消(系统) - 灰色
+        14: 'warning', // 待配货 - 黄色
+        16: 'warning', // 配货中 - 黄色
+        20: 'primary', // 待配送 - 蓝色
+        30: 'primary', // 配送中 - 蓝色
+        40: 'success', // 已完成 - 绿色
+        50: 'success', // 已完成 - 绿色
+        32: 'danger', // 配送异常 - 红色
+        34: 'danger', // 超时订单 - 红色
+        10: 'info', // 待支付 - 灰色
+        70: 'danger', // 已退款 - 红色
+        80: 'info', // 已取消 - 灰色
+        90: 'info' // 已取消(系统) - 灰色
       }
       return typeMap[status] || ''
     },
@@ -933,7 +1051,7 @@ export default {
         thirdPartyDistribution,
         cancelOrder
       }
-      operations[operation]({id}).then(() => {
+      operations[operation]({ id }).then(() => {
         this.$message({
           message: '操作成功',
           type: 'success'
@@ -942,7 +1060,7 @@ export default {
       })
     },
     check(val) {
-      const loadingInstance = this.$loading({text: '正在分配中...'})
+      const loadingInstance = this.$loading({ text: '正在分配中...' })
       distributeOrder({
         orderNo: this.orderId,
         riderId: val
@@ -957,12 +1075,77 @@ export default {
       }).finally(() => {
         loadingInstance.close()
       })
+    },
+
+    // 获取照片类型标签
+    getPhotoTypeLabel(type) {
+      const typeMap = {
+        0: '其他',
+        1: '创建订单',
+        2: '服务前',
+        3: '服务中',
+        4: '服务完成',
+        5: '问题反馈'
+      }
+      return typeMap[type] || '其他'
+    },
+
+    // 预览照片
+    previewPhoto(currentUrl, photoGroup) {
+      const currentIndex = photoGroup.findIndex(photo => photo.imageUrl === currentUrl)
+
+      this.currentPhotoGroup = {
+        title: this.getPhotoTypeLabel(photoGroup[0].screenshotType),
+        photos: photoGroup
+      }
+      this.currentPhotoIndex = Math.max(0, currentIndex)
+      this.photoPreviewVisible = true
+    },
+
+    // 关闭照片预览
+    closePhotoPreview() {
+      this.photoPreviewVisible = false
+      this.currentPhotoIndex = 0
+      this.currentPhotoGroup = {
+        title: '',
+        photos: []
+      }
+    },
+
+    // 上一张照片
+    prevPhoto() {
+      if (this.currentPhotoIndex > 0) {
+        this.currentPhotoIndex--
+      }
+    },
+
+    // 下一张照片
+    nextPhoto() {
+      if (this.currentPhotoIndex < this.currentPhotoGroup.photos.length - 1) {
+        this.currentPhotoIndex++
+      }
+    },
+
+    // 设置当前照片
+    setCurrentPhoto(index) {
+      this.currentPhotoIndex = index
+    },
+
+    // 图片加载完成
+    onImageLoad() {
+      // 图片加载完成的处理
+    },
+
+    // 图片加载失败
+    onImageError() {
+      this.$message.error('图片加载失败')
     }
   }
 }
 </script>
 <style lang="scss" scoped>
 .order-detail-dialog {
+
   .detail-container {
     padding: 0 10px;
 
@@ -1058,4 +1241,206 @@ export default {
     font-weight: bold;
   }
 }
+
+/* 照片相关样式 */
+.photo-gallery {
+  .photo-group {
+    margin-bottom: 20px;
+
+    .photo-group-title {
+      margin: 0 0 12px 0;
+      font-size: 14px;
+      color: #606266;
+      font-weight: 500;
+      border-left: 3px solid #409eff;
+      padding-left: 8px;
+    }
+
+    .photo-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
+      gap: 15px;
+
+      .photo-item {
+        position: relative;
+        width: 120px;
+        height: 120px;
+        border-radius: 8px;
+        overflow: hidden;
+        cursor: pointer;
+        border: 2px solid #e5e5e5;
+        transition: all 0.3s ease;
+
+        &:hover {
+          border-color: #409eff;
+          transform: scale(1.05);
+
+          .photo-overlay {
+            opacity: 1;
+          }
+        }
+
+        .photo-thumbnail {
+          width: 100%;
+          height: 100%;
+          object-fit: cover;
+          display: block;
+        }
+
+        .photo-overlay {
+          position: absolute;
+          top: 0;
+          left: 0;
+          right: 0;
+          bottom: 0;
+          background-color: rgba(0, 0, 0, 0.5);
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          opacity: 0;
+          transition: opacity 0.3s ease;
+
+          i {
+            font-size: 24px;
+            color: white;
+          }
+        }
+      }
+    }
+  }
+}
+
+/* 照片预览弹窗样式 */
+.photo-preview-dialog {
+  ::v-deep .el-dialog {
+    margin-top: 5vh !important;
+    background-color: #000;
+    border-radius: 8px;
+  }
+
+  ::v-deep .el-dialog__body {
+    padding: 0;
+  }
+
+  .photo-preview-container {
+    height: 80vh;
+    display: flex;
+    flex-direction: column;
+
+    .photo-preview-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 15px 20px;
+      background-color: rgba(255, 255, 255, 0.1);
+      border-bottom: 1px solid rgba(255, 255, 255, 0.2);
+
+      .preview-title {
+        color: #fff;
+        font-size: 16px;
+        font-weight: 500;
+      }
+
+      .preview-counter {
+        color: #ccc;
+        font-size: 14px;
+      }
+
+      .close-btn {
+        color: #fff !important;
+        font-size: 20px;
+
+        &:hover {
+          color: #f56c6c !important;
+        }
+      }
+    }
+
+    .photo-preview-content {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+
+      .photo-navigation {
+        flex: 1;
+        display: flex;
+        align-items: center;
+        position: relative;
+
+        .nav-btn {
+          position: absolute;
+          top: 50%;
+          transform: translateY(-50%);
+          z-index: 10;
+          width: 50px;
+          height: 50px;
+          background-color: rgba(0, 0, 0, 0.5);
+          border-radius: 50%;
+          color: #fff !important;
+          font-size: 20px;
+
+          &:hover {
+            background-color: rgba(0, 0, 0, 0.8);
+          }
+
+          &.prev-btn {
+            left: 20px;
+          }
+
+          &.next-btn {
+            right: 20px;
+          }
+        }
+
+        .main-photo {
+          flex: 1;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          padding: 0 80px;
+
+          .preview-image {
+            max-width: 100%;
+            max-height: 60vh;
+            object-fit: contain;
+            border-radius: 4px;
+          }
+        }
+      }
+
+      .photo-thumbnails {
+        display: flex;
+        justify-content: center;
+        gap: 10px;
+        padding: 20px;
+        background-color: rgba(255, 255, 255, 0.05);
+        overflow-x: auto;
+
+        .thumbnail-item {
+          width: 60px;
+          height: 60px;
+          border-radius: 4px;
+          overflow: hidden;
+          cursor: pointer;
+          border: 2px solid transparent;
+          transition: all 0.3s ease;
+
+          &.active {
+            border-color: #409eff;
+          }
+
+          &:hover {
+            border-color: #66b1ff;
+          }
+
+          .thumbnail-image {
+            width: 100%;
+            height: 100%;
+            object-fit: cover;
+          }
+        }
+      }
+    }
+  }
+}
 </style>

+ 0 - 10
kxmall-app-ui/pages.json

@@ -227,16 +227,6 @@
       "style": {
         "navigationBarTitleText": "订单详情"
       }
-    }, {
-      "path": "pages/order/complete",
-      "style": {
-        "navigationBarTitleText": "订单完成"
-      }
-    }, {
-      "path": "pages/order/upload",
-      "style": {
-        "navigationBarTitleText": "上传图片"
-      }
     }, {
       "path": "pages/order/refund",
       "style": {

+ 1 - 0
kxmall-app-ui/pages/order/create.vue

@@ -615,6 +615,7 @@ export default {
       that.orderReqeust.shippingType = this.shippingType
       that.orderReqeust.channel = uni.getSystemInfoSync().platform
       that.orderReqeust.payType = that.payType
+      that.orderReqeust.urgentFee = that.orderReqeust.urgentFee || 0
       if (that.payType === 'BALANCE') {
         that.$api.request('post', 'order/app/takeOrder',
             JSON.stringify(that.orderReqeust)

+ 6 - 7
kxmall-rider-ui/main.js

@@ -5,9 +5,6 @@ import * as config from './config'
 
 const baseUrl = config.def().baseUrl
 Vue.config.productionTip = false
-Vue.prototype.$api = {
-  uploadImgByPath,
-};
 
 Vue.prototype.request = function(method, endpoint, token, data = {}) {
   let netWork = ''
@@ -98,9 +95,7 @@ Vue.prototype.dateFtt = function(fmt, date) {
   return fmt;
 }
 
-const uploadImgByPath = (filePath, successCallback, failCallback = undefined) => {
-  userInfo = uni.getStorageSync('userInfo');
-  var accessToken = userInfo ? userInfo.accessToken : '';
+const uploadImgByPath = (filePath, token, successCallback, failCallback = undefined) => {
   let baseUrl = config.def().baseUrl
 
   uni.getImageInfo({
@@ -116,7 +111,7 @@ const uploadImgByPath = (filePath, successCallback, failCallback = undefined) =>
         file: image,
         filePath: image.path,
         header: {
-          Authorization: 'Bearer ' + accessToken,
+          Authorization: 'Bearer ' + token,
         },
         name: 'file',
         success: res => {
@@ -150,6 +145,10 @@ const uploadImgByPath = (filePath, successCallback, failCallback = undefined) =>
   })
 }
 
+Vue.prototype.$api = {
+  uploadImgByPath
+};
+
 App.mpType = 'app'
 
 const app = new Vue({

+ 747 - 446
kxmall-rider-ui/pages/order/order.vue

@@ -1,454 +1,755 @@
 <template>
-	<view class="app">
-		<view class="header">
-			<view class="header_inner">
-				<image src="../../static/order/clock.png"></image>
-				<text class="txt1">配送剩余时间<text class="txt2">{{preTime}}</text></text>
-			</view>
-			<view class="tip">你需要在{{sdTime=='aN:aN'?'':sdTime}}前送达</view>
-		</view>
-		<view class="goods">
-			<view class="goods_tit">商品信息</view>
-			<view class="goods_details">
-				<view class="detials_l">
-					<scroll-view scroll-x="true">
-						<image mode="aspectFit" v-for="(item,index) in details.riderItemVoList" :key="index" :src="JSON.parse(item.url)[0].url" class="details_img"></image>
-					</scroll-view>
-				</view>
-				<view class="detials_r">
-					<view class="detials_r_inner">
-						<text style="margin-left: 20rpx;">共{{count}}件</text>
-						<image @click="open()" src="../../static/order/dian.png"></image>
-					</view>
-				</view>
-			</view>
-		</view>
-		<view class="count">
-			<view class="count_item">
-				<text class="txt3">配送费</text>
-				<text class="txt4">{{details.freightPrice}}元</text>
-			</view>
-			<view class="count_item nobor">
-				<text class="txt3">商品费用</text>
-				<text class="txt4 txt5">¥{{details.totalPrice}}</text>
-			</view>
-			<view class="count_item nobor">
-				<text class="txt3">合计</text>
-				<text class="txt4 txt5">¥{{ (parseFloat(details.totalPrice) + parseFloat(details.freightPrice)).toFixed(2)}}</text>
-			</view>
-		</view>
-		<view class="count">
-			<view class="count_item">
-				<text class="txt3">配送信息</text>
-				<text class="txt4"></text>
-			</view>
-			<view class="count_item">
-				<text class="txt3 txt6">收货人</text>
-				<text class="txt4 txt5 txt7">{{details.consignee}}</text>
-			</view>
-			<view class="count_item">
-				<text class="txt3 txt6">联系电话</text>
-				<text class="txt4 txt5 txt7">{{details.phone}}</text>
-			</view>
-			<view class="count_item nobor">
-				<text class="txt3 txt6">收货地址</text>
-				<text class="txt4 txt5 txt7">{{details.province}}{{details.city}}{{details.county}}{{details.address}}</text>
-			</view>
-		</view>
-		<view class="count">
-			<view class="count_item">
-				<text class="txt3">订单信息</text>
-				<text class="txt4"></text>
-			</view>
-			<view class="count_item">
-				<text class="txt3 txt6">订单编号</text>
-				<text class="txt4 txt5 txt7">{{details.orderNo}}<text style="padding-left: 20rpx;color: #3662DD;" @click="copy(details.orderNo)">复制</text> </text>
-			</view>
-			<view class="count_item">
-				<text class="txt3 txt6">备注信息</text>
-				<text class="txt4 txt5 txt7">{{details.mono || ''}}</text>
-			</view>
-			<view class="count_item">
-				<text class="txt3 txt6">支付方式</text>
-				<text class="txt4 txt5 txt7">{{details.payChannel}}</text>
-			</view>
-			<view class="count_item">
-				<text class="txt3 txt6">下单时间</text>
-				<text class="txt4 txt5 txt7">{{orderCreateTime}}</text>
-			</view>
-			<view class="count_item nobor">
-				<text class="txt3 txt6">支付时间</text>
-				<text class="txt4 txt5 txt7">{{payTime}}</text>
-			</view>
-		</view>
-		<uni-popup ref="popup" type="bottom">
-			<view class="popup_mask">
-				<view class="popup_mask_tit">
-					<text class="pop_t1">配送清单</text>
-					<text class="pop_t2">共{{count}}件</text>
-					<image @click="closePop()" src="../../static/order/mask_close.png"></image>
-				</view>
-				<view class="pop_item" v-for="(item,index) in details.riderItemVoList" :key="'dt'+index">
-					<view class="pop_item_l">
-						<view class="item_l_l">
-							<image mode="aspectFit" class="pil_img" :src="JSON.parse(item.url)[0].url"></image>
-						</view>
-						<view class="item_l_r">
-							<view>{{item.spuName}}</view>
-							<view style="padding-top: 10rpx;">x{{item.amount}}</view>
-						</view>
-						<!-- <image class="pil_img" src="../../static/login/logo.png"></image>
-						<text>上海小白菜\n<text>x2</text> </text> -->
-					</view>
-					<view class="pop_item_r">
-						¥{{item.unitPrice*item.amount}}
-					</view>
-				</view>
-			</view>
-		</uni-popup>
-	</view>
+  <view class="app">
+    <view class="header">
+      <view class="header_inner">
+        <image src="../../static/order/clock.png"></image>
+        <text class="txt1">配送剩余时间
+          <text class="txt2">{{ preTime }}</text>
+        </text>
+      </view>
+      <view class="tip">你需要在{{ sdTime === 'aN:aN' ? '' : sdTime }}前送达</view>
+    </view>
+    <view class="goods">
+      <view class="goods_tit">商品信息</view>
+      <view class="goods_details">
+        <view class="detials_l">
+          <scroll-view scroll-x="true">
+            <image mode="aspectFit" v-for="(item,index) in details.riderItemVoList" :key="index"
+                   :src="JSON.parse(item.url)[0].url" class="details_img"></image>
+          </scroll-view>
+        </view>
+        <view class="detials_r">
+          <view class="detials_r_inner">
+            <text style="margin-left: 20rpx;">共{{ count }}件</text>
+            <image @click="open()" src="../../static/order/dian.png"></image>
+          </view>
+        </view>
+      </view>
+    </view>
+    <view class="count">
+      <view class="count_item">
+        <text class="txt3">配送费</text>
+        <text class="txt4">{{ details.freightPrice }}元</text>
+      </view>
+      <view class="count_item nobor">
+        <text class="txt3">商品费用</text>
+        <text class="txt4 txt5">¥{{ details.totalPrice }}</text>
+      </view>
+      <view class="count_item nobor">
+        <text class="txt3">合计</text>
+        <text class="txt4 txt5">¥{{ (parseFloat(details.totalPrice) + parseFloat(details.freightPrice)).toFixed(2) }}
+        </text>
+      </view>
+    </view>
+    <view class="count">
+      <view class="count_item">
+        <text class="txt3">配送信息</text>
+        <text class="txt4"></text>
+      </view>
+      <view class="count_item">
+        <text class="txt3 txt6">收货人</text>
+        <text class="txt4 txt5 txt7">{{ details.consignee }}</text>
+      </view>
+      <view class="count_item">
+        <text class="txt3 txt6">联系电话</text>
+        <text class="txt4 txt5 txt7">{{ details.phone }}</text>
+      </view>
+      <view class="count_item nobor">
+        <text class="txt3 txt6">收货地址</text>
+        <text class="txt4 txt5 txt7">{{ details.province }}{{ details.city }}{{ details.county }}{{ details.address }}
+        </text>
+      </view>
+    </view>
+    <view class="count">
+      <view class="count_item">
+        <text class="txt3">订单信息</text>
+        <text class="txt4"></text>
+      </view>
+      <view class="count_item">
+        <text class="txt3 txt6">订单编号</text>
+        <text class="txt4 txt5 txt7">{{ details.orderNo }}
+          <text style="padding-left: 20rpx;color: #3662DD;" @click="copy(details.orderNo)">复制</text>
+        </text>
+      </view>
+      <view class="count_item">
+        <text class="txt3 txt6">备注信息</text>
+        <text class="txt4 txt5 txt7">{{ details.mono || '' }}</text>
+      </view>
+      <view class="count_item">
+        <text class="txt3 txt6">支付方式</text>
+        <text class="txt4 txt5 txt7">{{ details.payChannel }}</text>
+      </view>
+      <view class="count_item">
+        <text class="txt3 txt6">下单时间</text>
+        <text class="txt4 txt5 txt7">{{ orderCreateTime }}</text>
+      </view>
+      <view class="count_item nobor">
+        <text class="txt3 txt6">支付时间</text>
+        <text class="txt4 txt5 txt7">{{ payTime }}</text>
+      </view>
+    </view>
+    
+    <view class="count" >
+      <view class="count_item">
+        <text class="txt3">用户上传照片</text>
+      </view>
+      <view class="count_item nobor photo-upload-section">
+        <view class="photo-upload-container">
+          <view class="photo-list">
+            <!-- 已上传的照片预览 -->
+            <view
+                v-for="(photo, index) in userOrderPhotos"
+                :key="index"
+                class="photo-item"
+            >
+              <image :src="photo" class="photo-preview" mode="aspectFill" @click="previewPhoto(index)"></image>
+            </view>
+          </view>
+        </view>
+      </view>
+      
+      <view class="count_item nobor"></view>
+    </view>
+    
+    <!-- 照片上传区域 - 仅在订单状态为4时显示 -->
+    <view class="count" >
+      <view class="count_item">
+        <text class="txt3">订单照片</text>
+        <view class="photo-mode-btn" @click="togglePhotoMode" v-if="details.status === 4">
+          <button class="mini-btn" @click="addScreenshot" :type="isEditMode ? 'primary' : 'default'" size="mini">
+            {{ isEditMode ? '上传' : '编辑' }}
+          </button>
+        </view>
+      </view>
+      <view class="count_item nobor photo-upload-section">
+        <view class="photo-upload-container">
+          <view class="photo-list">
+            <!-- 已上传的照片预览 -->
+            <view
+                v-for="(photo, index) in orderPhotos"
+                :key="index"
+                class="photo-item"
+            >
+              <image :src="photo" class="photo-preview" mode="aspectFill" @click="previewPhoto(index)"></image>
+              <!-- 删除按钮仅在编辑模式下显示 -->
+              <view v-if="isEditMode" class="photo-delete" @click="deletePhoto(index)">
+                <text class="photo-delete-icon">×</text>
+              </view>
+            </view>
+            
+            <!-- 添加照片按钮仅在编辑模式下显示 -->
+            <view
+                v-if="isEditMode && orderPhotos.length < 6"
+                class="photo-add-btn"
+                @click="choosePhoto"
+            >
+              <text class="photo-add-icon">+</text>
+              <text class="photo-add-text">添加照片</text>
+            </view>
+          </view>
+          <view class="photo-tips" v-if="isEditMode">最多可上传6张照片</view>
+        </view>
+      </view>
+    </view>
+    <uni-popup ref="popup" type="bottom">
+      <view class="popup_mask">
+        <view class="popup_mask_tit">
+          <text class="pop_t1">配送清单</text>
+          <text class="pop_t2">共{{ count }}件</text>
+          <image @click="closePop()" src="../../static/order/mask_close.png"></image>
+        </view>
+        <view class="pop_item" v-for="(item,index) in details.riderItemVoList" :key="'dt'+index">
+          <view class="pop_item_l">
+            <view class="item_l_l">
+              <image mode="aspectFit" class="pil_img" :src="JSON.parse(item.url)[0].url"></image>
+            </view>
+            <view class="item_l_r">
+              <view>{{ item.spuName }}</view>
+              <view style="padding-top: 10rpx;">x{{ item.amount }}</view>
+            </view>
+            <!-- <image class="pil_img" src="../../static/login/logo.png"></image>
+            <text>上海小白菜\n<text>x2</text> </text> -->
+          </view>
+          <view class="pop_item_r">
+            ¥{{ item.unitPrice * item.amount }}
+          </view>
+        </view>
+      </view>
+    </uni-popup>
+  </view>
 </template>
 
 <script>
-	import uniPopup from "@/components/uni-popup/uni-popup.vue";
-	import {
-		mapState,
-		mapMutations
-	} from 'vuex';
-	export default {
-		components: {
-			uniPopup
-		},
-		computed: {
-			...mapState(['token', 'userInfo'])
-		},
-		data() {
-			return {
-				goods: [1, 2, 3, 4, 5],
-				id: '',
-				details: '',
-				preTime: '',
-				count:0,
-				sdTime:'',
-				orderCreateTime:'',
-				payTime:'',
-				interval:'',
-				flag:false,
-			}
-		},
-		onLoad(e) {
-			this.getDetails(e.id)
-		},
-		onHide() {
-			clearInterval(this.interval)
-		},
-		methods: {
-			closePop(){
-				this.$refs.popup.close()
-			},
-			open() {
-				this.$refs.popup.open()
-			},
-			copy(msg){
-			  uni.setClipboardData({
-			      data: msg,
-			      success: function () {
-			          uni.showToast({
-			          	title:'复制成功',
-						icon:'none',
-						duration:1500
-			          })
-			      }
-			  });
-			},
-			getDetails(id) {
-				let that = this
-				let user=uni.getStorageSync("userInfo")
-				let params = {
-					riderOrderId: id
-				}
-				that.request("get","rider/task/detail",that.token,params).then(res => {
-					if (res.code == 200) {
-						let num=0
-						for(let i=0;i<res.data.riderItemVoList.length;i++){
-							res.data.riderItemVoList[i].amount=parseInt(res.data.riderItemVoList[i].amount)
-							num=num+parseInt(res.data.riderItemVoList[i].amount)
-						}
-						that.details = res.data
-						// console.log(that.details)
-						that.count=num
-						that.orderCreateTime=that.dateFtt("yyyy-MM-dd hh:mm:ss", new Date(that.details.orderCreateTime))
-						that.payTime=that.dateFtt("yyyy-MM-dd hh:mm:ss", new Date(that.details.payTime))
-						that.sdTime=that.dateFtt("hh:mm", new Date(that.details.predictTime))
-						// console.log(that.dateFtt("yyyy-MM-dd hh:mm:ss", new Date(that.details.predictTime)))
-						that.preTime = new Date(that.details.predictTime).getTime()>=new Date().getTime()?that.dateFtt("hh:mm:ss", new Date(that.details.predictTime)):0
-					    if(new Date(that.details.predictTime).getTime()>=new Date().getTime()){
-							that.preTime=true
-						}
-					}
-				})
-
-				that.interval = setInterval(function() {
-					// console.log(new Date(that.details.predictTime).getTime()>=new Date().getTime())
-					if (new Date(that.details.predictTime).getTime()>new Date().getTime()) {
-
-						let d = new Date(that.details.predictTime).getTime() - new Date().getTime()
-						// that.preTime = that.dateFtt("hh:mm:ss", d)
-						//计算出相差天数
-						var days=Math.floor(d/(24*3600*1000))
-
-						//计算出小时数
-
-						var leave1=d%(24*3600*1000)    //计算天数后剩余的毫秒数
-						var hours=Math.floor(leave1/(3600*1000))
-						//计算相差分钟数
-						var leave2=leave1%(3600*1000)        //计算小时数后剩余的毫秒数
-						var minutes=Math.floor(leave2/(60*1000))
-
-
-						//计算相差秒数
-						var leave3=leave2%(60*1000)      //计算分钟数后剩余的毫秒数
-						var seconds=Math.round(leave3/1000)
-						 that.preTime=days>0?(days+":"+hours+":"+minutes+":"+seconds):(hours>0?(hours+":"+minutes+":"+seconds):(minutes+":"+seconds))
-						//alert(" 相差 "+days+"天 "+hours+"小时 "+minutes+" 分钟"+seconds+" 秒")
-					} else {
-						that.preTime=0
-						//console.log(that.flag)
-						if(that.flag==true){
-						let nav_index=2
-							uni.redirectTo({
-								url:'../task/task?nav_index='+nav_index
-							})
-						}
-
-						clearInterval(that.interval)
-					}
-				}, 1000)
-			}
-		}
-	}
+import uniPopup from "@/components/uni-popup/uni-popup.vue";
+import {
+  mapState,
+  mapMutations
+} from 'vuex';
+
+export default {
+  components: {
+    uniPopup
+  },
+  computed: {
+    ...mapState(['token', 'userInfo'])
+  },
+  data() {
+    return {
+      goods: [1, 2, 3, 4, 5],
+      id: '',
+      details: '',
+      preTime: '',
+      count: 0,
+      sdTime: '',
+      orderCreateTime: '',
+      payTime: '',
+      interval: '',
+      flag: false,
+      orderPhotos: [], // 订单照片数组
+      isEditMode: false, // 照片编辑模式标志
+      userOrderPhotos: [],
+    }
+  },
+  onLoad(e) {
+    this.getDetails(e.id)
+  },
+  onHide() {
+    clearInterval(this.interval)
+  },
+  methods: {
+    closePop() {
+      this.$refs.popup.close()
+    },
+    open() {
+      this.$refs.popup.open()
+    },
+    copy(msg) {
+      uni.setClipboardData({
+        data: msg,
+        success: function () {
+          uni.showToast({
+            title: '复制成功',
+            icon: 'none',
+            duration: 1500
+          })
+        }
+      });
+    },
+    // 切换照片编辑模式
+    togglePhotoMode() {
+      this.isEditMode = !this.isEditMode
+    },
+    // 选择照片
+    choosePhoto() {
+      const remainingCount = 6 - this.orderPhotos.length
+      uni.chooseImage({
+        count: remainingCount,
+        sizeType: ['compressed'],
+        sourceType: ['album', 'camera'],
+        success: (res) => {
+          const tempFilePaths = res.tempFilePaths
+          // 依次上传选中的照片
+          tempFilePaths.forEach(filePath => {
+            this.uploadPhoto(filePath)
+          })
+        },
+        fail: (err) => {
+          if (err.errMsg !== 'chooseImage:fail cancel') {
+            console.error('选择照片失败:', err)
+            uni.showToast({
+              title: '选择照片失败',
+              icon: 'none'
+            })
+          }
+        }
+      })
+    },
+    
+    // 上传照片
+    uploadPhoto(filePath) {
+      const that = this
+      that.$api.uploadImgByPath(filePath, that.token, (url) => {
+        that.orderPhotos.push(url)
+        uni.showToast({
+          title: '上传成功',
+          icon: 'success'
+        })
+      })
+    },
+    
+    // 预览照片
+    previewPhoto(index) {
+      uni.previewImage({
+        urls: this.orderPhotos,
+        current: index
+      })
+    },
+    
+    // 删除照片
+    deletePhoto(index) {
+      const that = this
+      uni.showModal({
+        title: '确认删除',
+        content: '确定要删除这张照片吗?',
+        success: (res) => {
+          if (res.confirm) {
+            that.orderPhotos.splice(index, 1)
+          }
+        }
+      })
+    },
+    addScreenshot() {
+      if (this.orderPhotos.length === 0) {
+        return
+      }
+      const req = {
+        orderId: this.details.orderId,
+        screenshotType: 4,
+        imageUrls: this.orderPhotos,
+        userType: 2,
+        uploadBy: this.userInfo.id
+      }
+      this.request('post', 'order/screenshot/add', this.token, req)
+          .then(res => {
+            if (res.code === 200) {
+              uni.showToast({
+                title: '添加成功',
+                icon: 'success'
+              })
+            } else {
+              uni.showToast({
+                title: res.msg,
+                icon: 'error'
+              })
+            }
+          })
+          .catch(err => {
+            uni.showToast({
+              title: '添加失败',
+              icon: 'error'
+            })
+          })
+          .catc
+    },
+    getDetails(id) {
+      let that = this
+      let params = {
+        riderOrderId: id
+      }
+      that.request("get", "rider/task/detail", that.token, params).then(res => {
+        if (res.code === 200) {
+          let num = 0
+          for (let i = 0; i < res.data.riderItemVoList.length; i++) {
+            res.data.riderItemVoList[i].amount = parseInt(res.data.riderItemVoList[i].amount)
+            num = num + parseInt(res.data.riderItemVoList[i].amount)
+          }
+          that.details = res.data
+          if (res.data.imageUrls) {
+            that.orderPhotos.push(...res.data.imageUrls)
+          }
+          if (res.data.userImageUrls) {
+            that.userOrderPhotos.push(...res.data.userImageUrls)
+          }
+          // console.log(that.details)
+          that.count = num
+          that.orderCreateTime = that.dateFtt("yyyy-MM-dd hh:mm:ss", new Date(that.details.orderCreateTime))
+          that.payTime = that.dateFtt("yyyy-MM-dd hh:mm:ss", new Date(that.details.payTime))
+          that.sdTime = that.dateFtt("hh:mm", new Date(that.details.predictTime))
+          // console.log(that.dateFtt("yyyy-MM-dd hh:mm:ss", new Date(that.details.predictTime)))
+          that.preTime = new Date(that.details.predictTime).getTime() >= new Date().getTime() ? that.dateFtt("hh:mm:ss", new Date(that.details.predictTime)) : 0
+          if (new Date(that.details.predictTime).getTime() >= new Date().getTime()) {
+            that.preTime = true
+          }
+        }
+      })
+      
+      that.interval = setInterval(function () {
+        // console.log(new Date(that.details.predictTime).getTime()>=new Date().getTime())
+        if (new Date(that.details.predictTime).getTime() > new Date().getTime()) {
+          
+          let d = new Date(that.details.predictTime).getTime() - new Date().getTime()
+          // that.preTime = that.dateFtt("hh:mm:ss", d)
+          //计算出相差天数
+          var days = Math.floor(d / (24 * 3600 * 1000))
+          
+          //计算出小时数
+          
+          var leave1 = d % (24 * 3600 * 1000)    //计算天数后剩余的毫秒数
+          var hours = Math.floor(leave1 / (3600 * 1000))
+          //计算相差分钟数
+          var leave2 = leave1 % (3600 * 1000)        //计算小时数后剩余的毫秒数
+          var minutes = Math.floor(leave2 / (60 * 1000))
+          
+          
+          //计算相差秒数
+          var leave3 = leave2 % (60 * 1000)      //计算分钟数后剩余的毫秒数
+          var seconds = Math.round(leave3 / 1000)
+          that.preTime = days > 0 ? (days + ":" + hours + ":" + minutes + ":" + seconds) : (hours > 0 ? (hours + ":" + minutes + ":" + seconds) : (minutes + ":" + seconds))
+          //alert(" 相差 "+days+"天 "+hours+"小时 "+minutes+" 分钟"+seconds+" 秒")
+        } else {
+          that.preTime = 0
+          //console.log(that.flag)
+          if (that.flag == true) {
+            let nav_index = 2
+            uni.redirectTo({
+              url: '../task/task?nav_index=' + nav_index
+            })
+          }
+          
+          clearInterval(that.interval)
+        }
+      }, 1000)
+    }
+  }
+}
 </script>
 
 <style scoped>
-	scroll-view {
-		white-space: nowrap;
-		height: 110rpx;
-		width: 100%;
-		margin-top: 42rpx;
-		box-sizing: border-box;
-	}
-    .popup_mask{
-		width: 690rpx;
-		padding: 0 30rpx;
-		height: 500rpx;
-		background-color: #FFFFFF;
-		border-top-left-radius: 24rpx;
-		border-top-right-radius: 24rpx;
-		padding-bottom: 6rpx;
-	}
-	.popup_mask_tit{
-		height: 104rpx;
-		line-height: 104rpx;
-		position: relative;
-	}
-	.pop_t1{
-		font-size: 32rpx;
-		color: #333333;
-		font-weight: bold;
-	}
-	.pop_t2{
-		font-size: 28rpx;
-		color: #1B1C33;
-		margin-left: 40rpx;
-	}
-	.popup_mask_tit image{
-		width: 30rpx;
-		height: 30rpx;
-		position: absolute;
-		right: 20rpx;
-		top: 38rpx;
-	}
-	.pop_item{
-		height: 184rpx;
-		border-bottom: 2rpx solid #F1F1F1;
-		display: flex;
-		flex-direction: row;
-	}
-	.pop_item_l{
-		width: 520rpx;
-		height: 184rpx;
-		display: flex;
-		flex-direction: row;
-	}
-	.item_l_l{
-		width: 168rpx;
-		height: 148rpx;
-		padding-top: 36rpx;
-	}
-	.item_l_r{
-		width: 452rpx;
-		height: 148rpx;
-		padding-top: 36rpx;
-	}
-	.pop_item_r{
-		width: 150rpx;
-		height: 148rpx;
-		padding-top: 36rpx;
-		font-size: 32rpx;
-		color: #333333;
-		font-weight: bold;
-		text-align: right;
-	}
-	.pil_img{
-		width: 118rpx;
-		height: 110rpx;
-	}
-	.item_l_r view{
-		font-size: 32rpx;
-		color: #333333;
-	}
-	.test {
-		width: 220upx;
-		height: 220upx;
-		display: inline-block;
-		background-color: red;
-	}
-
-	.app {
-		background-color: #F6F6F6;
-	}
-
-	.header {
-		text-align: center;
-		background-color: #FFFFFF;
-	}
-
-	.header_inner {
-		display: inline-block;
-		padding-top: 90rpx;
-
-	}
-
-	.header_inner image {
-		width: 48rpx;
-		height: 48rpx;
-		position: relative;
-		top: 8rpx;
-	}
-
-	.txt1 {
-		font-size: 40rpx;
-		color: #000000;
-		font-weight: bold;
-		margin-left: 24rpx;
-	}
-
-	.txt2 {
-		color: #3662DD;
-		padding-left: 20rpx;
-	}
-
-	.tip {
-		text-align: center;
-		font-size: 32rpx;
-		color: #717171;
-		padding-top: 24rpx;
-		padding-bottom: 70rpx;
-	}
-
-	.goods {
-		height: 282rpx;
-		margin-top: 20rpx;
-		background-color: #FFFFFF;
-		padding: 0 30rpx;
-	}
-
-	.goods_tit {
-		height: 86rpx;
-		line-height: 86rpx;
-		border-bottom: 2rpx solid #F1F1F1;
-		font-size: 32rpx;
-		color: #333333;
-		font-weight: bold;
-	}
-
-	.goods_details {
-		height: 194rpx;
-		display: flex;
-		flex-direction: row;
-	}
-
-	.detials_l {
-		width: 496rpx;
-		height: 194rpx;
-	}
-
-	.detials_r {
-		width: 174rpx;
-		height: 194rpx;
-	}
-
-	.details_img {
-		display: inline-block;
-		width: 118rpx;
-		height: 110rpx;
-		margin-right: 36rpx;
-	}
-
-	.detials_r_inner {
-		height: 110rpx;
-		margin-top: 42rpx;
-		line-height: 110rpx;
-		font-size: 28rpx;
-		color: #1B1C33;
-		position: relative;
-	}
-
-	.detials_r_inner image {
-		width: 44rpx;
-		height: 10rpx;
-		position: absolute;
-		right: 0;
-		top: 48rpx;
-	}
-
-	.count {
-		margin-top: 20rpx;
-		padding: 0 30rpx;
-		background-color: #FFFFFF;
-	}
-
-	.count_item {
-		height: 99rpx;
-		line-height: 99rpx;
-		border-bottom: 2rpx solid #F1F1F1;
-		font-size: 28rpx;
-
-	}
-
-	.txt3,
-	.txt5 {
-		font-weight: bold;
-		color: #333333;
-	}
-
-	.txt4 {
-
-		color: #666666;
-		float: right;
-	}
-
-	.nobor {
-		border: none;
-	}
-
-	.txt6 {
-		color: #999999;
-		font-weight: normal;
-	}
-
-	.txt7 {
-		font-weight: normal;
-		color: #333333;
-	}
+scroll-view {
+  white-space: nowrap;
+  height: 110rpx;
+  width: 100%;
+  margin-top: 42rpx;
+  box-sizing: border-box;
+}
+
+.popup_mask {
+  width: 690rpx;
+  padding: 0 30rpx;
+  height: 500rpx;
+  background-color: #FFFFFF;
+  border-top-left-radius: 24rpx;
+  border-top-right-radius: 24rpx;
+  padding-bottom: 6rpx;
+}
+
+.popup_mask_tit {
+  height: 104rpx;
+  line-height: 104rpx;
+  position: relative;
+}
+
+.pop_t1 {
+  font-size: 32rpx;
+  color: #333333;
+  font-weight: bold;
+}
+
+.pop_t2 {
+  font-size: 28rpx;
+  color: #1B1C33;
+  margin-left: 40rpx;
+}
+
+.popup_mask_tit image {
+  width: 30rpx;
+  height: 30rpx;
+  position: absolute;
+  right: 20rpx;
+  top: 38rpx;
+}
+
+.pop_item {
+  height: 184rpx;
+  border-bottom: 2rpx solid #F1F1F1;
+  display: flex;
+  flex-direction: row;
+}
+
+.pop_item_l {
+  width: 520rpx;
+  height: 184rpx;
+  display: flex;
+  flex-direction: row;
+}
+
+.item_l_l {
+  width: 168rpx;
+  height: 148rpx;
+  padding-top: 36rpx;
+}
+
+.item_l_r {
+  width: 452rpx;
+  height: 148rpx;
+  padding-top: 36rpx;
+}
+
+.pop_item_r {
+  width: 150rpx;
+  height: 148rpx;
+  padding-top: 36rpx;
+  font-size: 32rpx;
+  color: #333333;
+  font-weight: bold;
+  text-align: right;
+}
+
+.pil_img {
+  width: 118rpx;
+  height: 110rpx;
+}
+
+.item_l_r view {
+  font-size: 32rpx;
+  color: #333333;
+}
+
+.test {
+  width: 220upx;
+  height: 220upx;
+  display: inline-block;
+  background-color: red;
+}
+
+.app {
+  background-color: #F6F6F6;
+}
+
+.header {
+  text-align: center;
+  background-color: #FFFFFF;
+}
+
+.header_inner {
+  display: inline-block;
+  padding-top: 90rpx;
+  
+}
+
+.header_inner image {
+  width: 48rpx;
+  height: 48rpx;
+  position: relative;
+  top: 8rpx;
+}
+
+.txt1 {
+  font-size: 40rpx;
+  color: #000000;
+  font-weight: bold;
+  margin-left: 24rpx;
+}
+
+.txt2 {
+  color: #3662DD;
+  padding-left: 20rpx;
+}
+
+.tip {
+  text-align: center;
+  font-size: 32rpx;
+  color: #717171;
+  padding-top: 24rpx;
+  padding-bottom: 70rpx;
+}
+
+.goods {
+  height: 282rpx;
+  margin-top: 20rpx;
+  background-color: #FFFFFF;
+  padding: 0 30rpx;
+}
+
+.goods_tit {
+  height: 86rpx;
+  line-height: 86rpx;
+  border-bottom: 2rpx solid #F1F1F1;
+  font-size: 32rpx;
+  color: #333333;
+  font-weight: bold;
+}
+
+.goods_details {
+  height: 194rpx;
+  display: flex;
+  flex-direction: row;
+}
+
+.detials_l {
+  width: 496rpx;
+  height: 194rpx;
+}
+
+.detials_r {
+  width: 174rpx;
+  height: 194rpx;
+}
+
+.details_img {
+  display: inline-block;
+  width: 118rpx;
+  height: 110rpx;
+  margin-right: 36rpx;
+}
+
+.detials_r_inner {
+  height: 110rpx;
+  margin-top: 42rpx;
+  line-height: 110rpx;
+  font-size: 28rpx;
+  color: #1B1C33;
+  position: relative;
+}
+
+.detials_r_inner image {
+  width: 44rpx;
+  height: 10rpx;
+  position: absolute;
+  right: 0;
+  top: 48rpx;
+}
+
+.count {
+  margin-top: 20rpx;
+  padding: 0 30rpx;
+  background-color: #FFFFFF;
+}
+
+.count_item {
+  height: 99rpx;
+  line-height: 99rpx;
+  border-bottom: 2rpx solid #F1F1F1;
+  font-size: 28rpx;
+  
+}
+
+.txt3,
+.txt5 {
+  font-weight: bold;
+  color: #333333;
+}
+
+.txt4 {
+  
+  color: #666666;
+  float: right;
+}
+
+.nobor {
+  border: none;
+}
+
+.txt6 {
+  color: #999999;
+  font-weight: normal;
+}
+
+.txt7 {
+  font-weight: normal;
+  color: #333333;
+  word-wrap: break-word;
+  word-break: break-all;
+  white-space: normal;
+}
+
+/* 照片上传相关样式 */
+.photo-upload-section {
+  flex-direction: row !important; /* 改为横向布局 */
+  align-items: flex-start !important;
+  height: auto !important; /* 覆盖count_item的固定高度 */
+  line-height: normal !important; /* 覆盖count_item的固定行高 */
+  padding: 30rpx 0 !important; /* 添加上下内边距 */
+  display: flex !important;
+}
+
+.distribution-text {
+  width: 200upx;
+  float: left;
+  color: #333333;
+  display: flex; /* 使用 flex 布局 */
+  align-items: center; /* 垂直居中 */
+}
+
+.photo-upload-container {
+  flex: 1; /* 占据剩余空间 */
+  margin-top: 20rpx;
+  margin-left: 20rpx; /* 与文案保持间距 */
+  min-height: 200rpx; /* 确保至少有200rpx高度 */
+}
+
+.photo-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 20rpx;
+  align-content: flex-start;
+  width: 100%;
+}
+
+.photo-item {
+  position: relative;
+  width: 200rpx; /* 固定正方形宽度 */
+  height: 200rpx; /* 固定正方形高度 */
+  margin-bottom: 20rpx; /* 行间距 */
+}
+
+.photo-preview {
+  width: 100%;
+  height: 100%;
+  border-radius: 8rpx;
+  border: 2rpx solid #e5e5e5;
+}
+
+.photo-delete {
+  position: absolute;
+  top: -10rpx;
+  right: -10rpx;
+  width: 40rpx;
+  height: 40rpx;
+  background-color: #ff4757;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.photo-delete-icon {
+  color: #fff;
+  font-size: 24rpx;
+  font-weight: bold;
+}
+
+.photo-add-btn {
+  width: 200rpx; /* 与photo-item保持一致 */
+  height: 200rpx; /* 与photo-item保持一致 */
+  margin-bottom: 20rpx; /* 与photo-item保持一致的行间距 */
+  border: 2rpx dashed #ccc;
+  border-radius: 8rpx;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  background-color: #fafafa;
+}
+
+.photo-add-icon {
+  font-size: 32rpx; /* 调小图标尺寸 */
+  color: #999;
+  margin-bottom: 4rpx; /* 减少间距 */
+}
+
+.photo-add-text {
+  font-size: 20rpx; /* 调小文字尺寸 */
+  color: #999;
+}
+
+.photo-tips {
+  margin-top: 20rpx;
+  font-size: 24rpx;
+  color: #999;
+}
+
+/* 照片模式切换按钮样式 */
+.photo-mode-btn {
+  float: right;
+  margin-top: 20rpx;
+  margin-right: 10rpx;
+}
+
+.btn-text {
+  color: #FFFFFF;
+  font-size: 24rpx;
+  font-weight: 500;
+}
+
 </style>

+ 16 - 1
kxmall-rider-ui/pages/review/review-list.vue

@@ -49,7 +49,7 @@
 				<!-- 评价内容 -->
 				<view class="review-content">
 					<!-- 评分星级 -->
-					<view class="rating-section">
+					<view class="rating-section flex-rating-section">
 						<view class="stars">
 							<view class="star" 
 								  v-for="i in 5" 
@@ -59,6 +59,8 @@
 							</view>
 						</view>
 						<text class="rating-text">{{getRatingText(item.score)}}</text>
+            
+            <view class="product-name"> {{item.productName}} </view>
 					</view>
 
 					<!-- 评价文字 -->
@@ -508,4 +510,17 @@
 			}
 		}
 	}
+  
+  .flex-rating-section {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    
+    .product-name {
+      margin-left: auto;
+      font-size: 26rpx;
+      
+    }
+  }
+  
 </style>