Explorar el Código

运营端小程序订单模块调试

skyline hace 1 mes
padre
commit
ee71541e95

+ 5 - 1
haha-admin-mp/src/api/order.ts

@@ -26,7 +26,11 @@ export interface OrderListResponse {
  * 获取订单列表
  */
 export async function getOrderList(params: OrderQueryParams = {}): Promise<OrderListResponse> {
-  return get('/order/list', params);
+  const query = { ...params };
+  if (query.status === -1) {
+    delete (query as any).status;
+  }
+  return get('/order/list', query);
 }
 
 /**

+ 134 - 30
haha-admin-mp/src/pages/orders/detail.vue

@@ -8,10 +8,12 @@
       <view class="status-section">
         <view class="status-icon" :class="getStatusClass(order.status)">
           <view class="icon-check" v-if="order.status === 2"></view>
-          <view class="icon-clock" v-else-if="order.status === 4"></view>
+          <view class="icon-close" v-else-if="order.status === 0 || order.status === 3"></view>
+          <view class="icon-clock" v-else-if="order.status === 1"></view>
           <view class="icon-box" v-else></view>
         </view>
-        <text class="status-text">{{ getStatusText(order.status) }}</text>
+        <text class="status-text">{{ order.statusText || getStatusText(order.status) }}</text>
+        <text class="pay-status" v-if="order.payStatusLabel">{{ order.payStatusLabel }}</text>
       </view>
       
       <!-- 门店信息 -->
@@ -19,11 +21,11 @@
         <text class="section-title">门店信息</text>
         <view class="info-item">
           <text class="label">门店名称</text>
-          <text class="value">{{ order.shopName }}</text>
+          <text class="value">{{ order.shopName || '-' }}</text>
         </view>
         <view class="info-item">
-          <text class="label">设备名称</text>
-          <text class="value">{{ order.deviceName }}</text>
+          <text class="label">设备编号</text>
+          <text class="value mono">{{ order.deviceId || '-' }}</text>
         </view>
       </view>
       
@@ -31,16 +33,21 @@
       <view class="info-section">
         <text class="section-title">商品信息</text>
         <view class="product-list">
-          <view class="product-item" v-for="item in order.items" :key="item.productId">
+          <view class="product-item" v-for="item in order.products" :key="item.productId">
+            <image 
+              class="product-pic" 
+              :src="item.pic || '/static/images/default-product.png'" 
+              mode="aspectFill"
+            />
             <view class="product-info">
               <text class="product-name">{{ item.productName }}</text>
               <text class="product-price">¥{{ formatMoney(item.price) }}</text>
             </view>
             <view class="product-count">
-              <text>x{{ item.quantity }}</text>
+              <text>x{{ item.productNum }}</text>
             </view>
             <view class="product-amount">
-              <text>¥{{ formatMoney(item.amount) }}</text>
+              <text>¥{{ formatMoney(item.money) }}</text>
             </view>
           </view>
         </view>
@@ -53,17 +60,27 @@
           <text class="label">订单编号</text>
           <text class="value mono">{{ order.orderNo }}</text>
         </view>
+        <view class="info-item" v-if="order.outTradeNo">
+          <text class="label">外部交易号</text>
+          <text class="value mono">{{ order.outTradeNo }}</text>
+        </view>
         <view class="info-item">
           <text class="label">创建时间</text>
-          <text class="value">{{ order.createdAt }}</text>
+          <text class="value">{{ order.createTime }}</text>
         </view>
-        <view class="info-item" v-if="order.paidAt">
+        <view class="info-item" v-if="order.payTime">
           <text class="label">支付时间</text>
-          <text class="value">{{ order.paidAt }}</text>
+          <text class="value">{{ order.payTime }}</text>
         </view>
         <view class="info-item">
           <text class="label">支付方式</text>
-          <text class="value">{{ order.paymentMethod }}</text>
+          <text class="value">{{ order.payType || '-' }}</text>
+        </view>
+        <view class="info-item" v-if="order.userTagLabel">
+          <text class="label">用户标签</text>
+          <view class="tag" :class="getUserTagClass(order.userTag)">
+            <text>{{ order.userTagLabel }}</text>
+          </view>
         </view>
       </view>
       
@@ -74,48 +91,57 @@
           <text class="label">商品总额</text>
           <text class="value">¥{{ formatMoney(order.totalAmount) }}</text>
         </view>
+        <view class="info-item" v-if="order.discountAmount && order.discountAmount > 0">
+          <text class="label">优惠金额</text>
+          <text class="value discount">-¥{{ formatMoney(order.discountAmount) }}</text>
+        </view>
         <view class="info-item highlight">
           <text class="label">实付金额</text>
-          <text class="value">¥{{ formatMoney(order.payAmount) }}</text>
+          <text class="value">¥{{ formatMoney(order.paidAmount) }}</text>
         </view>
       </view>
     </view>
     
     <!-- 底部操作 -->
-    <view class="bottom-actions" v-if="order && order.status === 4">
-      <button class="refund-btn" @click="handleRefund">同意退款</button>
+    <view class="bottom-actions" v-if="order && canRefund(order)">
+      <button class="refund-btn" @click="handleRefund">申请退款</button>
     </view>
   </view>
 </template>
 
 <script setup lang="ts">
 import { ref, onMounted } from 'vue';
+import { onLoad } from '@dcloudio/uni-app';
 import NavBar from '@/components/NavBar.vue';
 import { getOrderDetail, handleRefund as refundOrder } from '@/api/order';
-import { OrderStatusText, OrderStatusColor } from '@/utils/constants';
-import { formatMoney as formatMoneyUtil, showToast, navigateBack } from '@/utils/common';
+import { OrderStatusText } from '@/utils/constants';
+import { formatMoney as formatMoneyUtil, showToast, navigateBack, showConfirm } from '@/utils/common';
 
 const order = ref<any>(null);
 const formatMoney = formatMoneyUtil;
 
 const getStatusText = (status: number) => OrderStatusText[status] || '未知';
-const getStatusColor = (status: number) => OrderStatusColor[status] || '#94a3b8';
 
 const getStatusClass = (status: number) => {
   if (status === 2) return 'success';
-  if (status === 4) return 'warning';
-  if (status === 1) return 'processing';
+  if (status === 0 || status === 3) return 'danger';
+  if (status === 1) return 'warning';
   return 'pending';
 };
 
-const loadDetail = async () => {
-  const pages = getCurrentPages();
-  const currentPage = pages[pages.length - 1];
-  const id = (currentPage as any).options?.id;
-  
+const getUserTagClass = (tag: string) => {
+  if (tag === 'new') return 'tag-new';
+  if (tag === 'frequent') return 'tag-frequent';
+  return 'tag-regular';
+};
+
+const canRefund = (order: any) => {
+  return order.payStatus === 'PAID' && order.status === 2;
+};
+
+const loadDetail = async (id?: string) => {
   if (!id) {
     showToast('订单ID不存在');
-    navigateBack();
     return;
   }
   
@@ -123,26 +149,34 @@ const loadDetail = async () => {
     order.value = await getOrderDetail(Number(id));
   } catch (error) {
     showToast('加载订单详情失败');
-    navigateBack();
   }
 };
 
 const handleRefund = async () => {
   if (!order.value) return;
   
+  const confirmed = await showConfirm('确认对该订单进行退款操作?');
+  if (!confirmed) return;
+  
   try {
-    await refundOrder(order.value.id, '商家同意退款');
+    await refundOrder(order.value.id, '商家申请退款');
     showToast('退款处理成功', 'success');
     setTimeout(() => {
-      navigateBack();
+      const pages = getCurrentPages();
+      const currentPage = pages[pages.length - 1] as any;
+      loadDetail(currentPage?.options?.id);
     }, 1000);
   } catch (error) {
     showToast('退款处理失败');
   }
 };
 
+onLoad((options: any) => {
+  loadDetail(options?.id);
+});
+
 onMounted(() => {
-  loadDetail();
+  // onLoad 中已处理加载
 });
 </script>
 
@@ -218,6 +252,35 @@ onMounted(() => {
       }
     }
     
+    &.danger {
+      background: #fef2f2;
+      
+      .icon-close {
+        width: 32rpx;
+        height: 32rpx;
+        position: relative;
+        
+        &::before,
+        &::after {
+          content: '';
+          position: absolute;
+          top: 50%;
+          left: 50%;
+          width: 24rpx;
+          height: 4rpx;
+          background: #ef4444;
+        }
+        
+        &::before {
+          transform: translate(-50%, -50%) rotate(45deg);
+        }
+        
+        &::after {
+          transform: translate(-50%, -50%) rotate(-45deg);
+        }
+      }
+    }
+    
     &.processing {
       background: #f0f9ff;
       
@@ -249,6 +312,12 @@ onMounted(() => {
     align-items: center;
     justify-content: center;
   }
+  
+  .pay-status {
+    font-size: 24rpx;
+    color: #64748b;
+    margin-top: 8rpx;
+  }
 }
 
 .detail-card {
@@ -294,6 +363,28 @@ onMounted(() => {
       }
     }
     
+    .tag {
+      padding: 4rpx 16rpx;
+      border-radius: 8rpx;
+      font-size: 24rpx;
+      font-weight: 500;
+      
+      &.tag-new {
+        background: #fef2f2;
+        color: #ef4444;
+      }
+      
+      &.tag-frequent {
+        background: #ecfdf5;
+        color: #10b981;
+      }
+      
+      &.tag-regular {
+        background: #f0f9ff;
+        color: #0ea5e9;
+      }
+    }
+    
     .product-list {
       .product-item {
         display: flex;
@@ -305,6 +396,15 @@ onMounted(() => {
           border-bottom: none;
         }
         
+        .product-pic {
+          width: 100rpx;
+          height: 100rpx;
+          border-radius: 12rpx;
+          margin-right: 16rpx;
+          background: #f1f5f9;
+          flex-shrink: 0;
+        }
+        
         .product-info {
           flex: 1;
           
@@ -340,6 +440,10 @@ onMounted(() => {
   }
   
   .amount-section {
+    .discount {
+      color: #10b981;
+    }
+    
     .info-item:last-child {
       background: #fff7ed;
       margin: 16rpx -32rpx -24rpx;

+ 15 - 10
haha-admin-mp/src/pages/orders/list.vue

@@ -13,6 +13,13 @@
         >
           <text>全部</text>
         </view>
+        <view 
+          class="filter-tab" 
+          :class="{ active: currentStatus === 1 }"
+          @click="changeStatus(1)"
+        >
+          <text>待支付</text>
+        </view>
         <view 
           class="filter-tab" 
           :class="{ active: currentStatus === 2 }"
@@ -22,10 +29,10 @@
         </view>
         <view 
           class="filter-tab" 
-          :class="{ active: currentStatus === 4 }"
-          @click="changeStatus(4)"
+          :class="{ active: currentStatus === 0 }"
+          @click="changeStatus(0)"
         >
-          <text>退款中</text>
+          <text>已取消</text>
         </view>
       </view>
       <view class="search-btn" @click="showSearch = true">
@@ -60,11 +67,11 @@
             </view>
             <view class="card-right">
               <text class="amount">¥{{ formatMoney(order.totalAmount) }}</text>
-              <text class="time-text">{{ order.createdAt }}</text>
+              <text class="time-text">{{ order.createTime }}</text>
             </view>
           </view>
           
-          <view class="card-action" v-if="order.status === 4">
+          <view class="card-action" v-if="order.payStatus === 'REFUND'">
             <view class="action-btn" @click.stop="handleRefund(order)">
               <text>处理退款</text>
             </view>
@@ -139,12 +146,10 @@ const getStatusText = (status: number) => OrderStatusText[status] || '未知';
 
 const getStatusClass = (status: number) => {
   const classMap: Record<number, string> = {
-    [OrderStatus.PENDING]: 'pending',
-    [OrderStatus.PAID]: 'processing',
-    [OrderStatus.COMPLETED]: 'completed',
     [OrderStatus.CANCELLED]: 'cancelled',
-    [OrderStatus.REFUNDING]: 'refunding',
-    [OrderStatus.REFUNDED]: 'completed'
+    [OrderStatus.PENDING_PAYMENT]: 'pending',
+    [OrderStatus.COMPLETED]: 'completed',
+    [OrderStatus.CLOSED]: 'cancelled'
   };
   return classMap[status] || 'pending';
 };

+ 10 - 16
haha-admin-mp/src/utils/constants.ts

@@ -6,30 +6,24 @@
 // ==================== 订单状态 ====================
 
 export enum OrderStatus {
-  PENDING = 0,      // 待支付
-  PAID = 1,         // 已支付
-  COMPLETED = 2,    // 已完成
-  CANCELLED = 3,    // 已取消
-  REFUNDING = 4,    // 退款中
-  REFUNDED = 5      // 已退款
+  CANCELLED = 0,      // 已取消
+  PENDING_PAYMENT = 1,// 待支付
+  COMPLETED = 2,      // 已完成
+  CLOSED = 3          // 已关闭
 }
 
 export const OrderStatusText: Record<number, string> = {
-  [OrderStatus.PENDING]: '待支付',
-  [OrderStatus.PAID]: '已支付',
-  [OrderStatus.COMPLETED]: '已完成',
   [OrderStatus.CANCELLED]: '已取消',
-  [OrderStatus.REFUNDING]: '退款中',
-  [OrderStatus.REFUNDED]: '已退款'
+  [OrderStatus.PENDING_PAYMENT]: '待支付',
+  [OrderStatus.COMPLETED]: '已完成',
+  [OrderStatus.CLOSED]: '已关闭'
 };
 
 export const OrderStatusColor: Record<number, string> = {
-  [OrderStatus.PENDING]: '#faad14',
-  [OrderStatus.PAID]: '#1890ff',
-  [OrderStatus.COMPLETED]: '#52c41a',
   [OrderStatus.CANCELLED]: '#999999',
-  [OrderStatus.REFUNDING]: '#ff4d4f',
-  [OrderStatus.REFUNDED]: '#999999'
+  [OrderStatus.PENDING_PAYMENT]: '#faad14',
+  [OrderStatus.COMPLETED]: '#52c41a',
+  [OrderStatus.CLOSED]: '#666666'
 };
 
 // ==================== 设备状态 ====================

+ 3 - 0
haha-common/src/main/java/com/haha/common/vo/OrderVO.java

@@ -39,6 +39,9 @@ public class OrderVO {
     private BigDecimal confidence;
     private List<OrderItemVO> products;
 
+    /** 门店名称 */
+    private String shopName;
+
     /** 用户消费标签:new-新用户, regular-老用户, frequent-常客 */
     private String userTag;
 

+ 5 - 0
haha-service/src/main/java/com/haha/service/impl/OrderServiceImpl.java

@@ -137,6 +137,9 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
             return null;
         }
 
+        // 填充关联标签信息(门店名称、设备名称等)
+        fillOrderLabels(List.of(order));
+
         OrderVO vo = new OrderVO();
         vo.setId(order.getId());
         vo.setOrderNo(order.getOrderNo());
@@ -155,6 +158,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
         vo.setPayTime(order.getPayTime());
         vo.setVideoUrl(order.getVideoUrl());
         vo.setConfidence(order.getConfidence());
+        vo.setShopName(order.getShopName());
 
         List<OrderItemVO> products = orderGoodsService.getVOByOrderId(id);
         vo.setProducts(products);
@@ -459,6 +463,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
                 try {
                     Device device = deviceMapper.selectByDeviceId(order.getDeviceId());
                     if (device != null) {
+                        order.setDeviceName(device.getName());
                         order.setShopId(device.getShopId());
                         if (device.getShopId() != null) {
                             Shop shop = shopMapper.selectById(device.getShopId());