ソースを参照

用户端小程序优化

skyline 3 週間 前
コミット
a8acaf8f87

+ 18 - 17
haha-mp/src/App.vue

@@ -2,12 +2,13 @@
 import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
 import { API_CONFIG } from './utils/config';
 import { handleUnauthorized, clearAuth } from './utils/auth';
+import { logger } from './utils/logger';
 
 /**
  * 应用启动
  */
 onLaunch((options: any) => {
-  console.log("App Launch, options:", JSON.stringify(options));
+  logger.log("App Launch, options:", JSON.stringify(options));
   checkLoginStatus(options);
   checkBindingCode(options);
 });
@@ -22,10 +23,10 @@ const checkBindingCode = (options: any) => {
   // 检查 scene 场景值(小程序码场景)
   if (options.scene) {
     const scene = decodeURIComponent(options.scene);
-    console.log('[App] 检测到场景值:', scene);
+    logger.log('[App] 检测到场景值:', scene);
     // 24位大写字母数字组合为绑定码格式
     if (/^[A-Z0-9]{24}$/.test(scene)) {
-      console.log('[App] 场景值为补货员绑定码');
+      logger.log('[App] 场景值为补货员绑定码');
       uni.navigateTo({
         url: '/pages/replenish/bind?code=' + scene
       });
@@ -35,7 +36,7 @@ const checkBindingCode = (options: any) => {
 
   // 检查 query 参数中的绑定码
   if (options.query && options.query.bindingCode) {
-    console.log('[App] 检测到补货员绑定码:', options.query.bindingCode);
+    logger.log('[App] 检测到补货员绑定码:', options.query.bindingCode);
     uni.navigateTo({
       url: '/pages/replenish/bind?code=' + encodeURIComponent(options.query.bindingCode)
     });
@@ -46,19 +47,19 @@ const checkBindingCode = (options: any) => {
  * 应用显示
  */
 onShow(() => {
-  console.log("App Show");
+  logger.log("App Show");
 });
 
 /**
  * 应用隐藏
  */
 onHide(() => {
-  console.log("App Hide");
+  logger.log("App Hide");
 
   // 当应用隐藏时,清理购物页面的轮询状态
   const pollingActive = uni.getStorageSync('shoppingPollingActive');
   if (pollingActive === 'true') {
-    console.log('应用隐藏,清理购物页面轮询状态');
+    logger.log('应用隐藏,清理购物页面轮询状态');
     uni.removeStorageSync('shoppingPollingActive');
   }
 });
@@ -102,22 +103,22 @@ const verifyToken = (): Promise<boolean> => {
             resolve(true);
           } else if (data && data.code === 401) {
             // token无效或已过期
-            console.log('[App] token无效:', data.message);
+            logger.log('[App] token无效:', data.message);
             resolve(false);
           } else {
             // 其他业务错误,保守起见仍视为token有效
-            console.warn('[App] 验证token返回异常状态:', data?.code, data?.message);
+            logger.warn('[App] 验证token返回异常状态:', data?.code, data?.message);
             resolve(true);
           }
         } else {
           // 网络或服务异常,不阻断用户使用,保守视为token有效
-          console.warn('[App] 验证token网络异常, statusCode:', res.statusCode);
+          logger.warn('[App] 验证token网络异常, statusCode:', res.statusCode);
           resolve(true);
         }
       },
       fail: (err: any) => {
         // 网络请求失败,保守视为token有效,不阻断用户使用
-        console.warn('[App] 验证token请求失败:', err.errMsg);
+        logger.warn('[App] 验证token请求失败:', err.errMsg);
         resolve(true);
       }
     });
@@ -139,33 +140,33 @@ const checkLoginStatus = async (launchOptions?: any) => {
     currentPath = currentPage ? currentPage.route : '';
   }
 
-  console.log('[App] 检查登录状态 - token:', token ? '已存在' : '不存在', ', 目标页面:', currentPath);
+  logger.log('[App] 检查登录状态 - token:', token ? '已存在' : '不存在', ', 目标页面:', currentPath);
 
   const isWhiteListed = LOGIN_WHITE_LIST.some(path => currentPath.indexOf(path) !== -1);
 
   // 没有token
   if (!token) {
     if (!isWhiteListed) {
-      console.log('[App] 未登录,跳转到登录页');
+      logger.log('[App] 未登录,跳转到登录页');
       uni.reLaunch({ url: '/pages/login/login' });
     }
     return;
   }
 
   // 有token,向后端验证是否有效
-  console.log('[App] token存在,验证token有效性...');
+  logger.log('[App] token存在,验证token有效性...');
   const isValid = await verifyToken();
 
   if (!isValid && !isWhiteListed) {
     // token无效且不在白名单 -> 触发全局未授权处理
-    console.log('[App] token无效,触发全局未授权处理');
+    logger.log('[App] token无效,触发全局未授权处理');
     handleUnauthorized();
   } else if (!isValid && isWhiteListed) {
     // 白名单页面只清除数据不跳转(不触发全局未授权处理的重定向)
-    console.log('[App] token无效,清除登录数据(当前在白名单页面)');
+    logger.log('[App] token无效,清除登录数据(当前在白名单页面)');
     clearAuth();
   } else {
-    console.log('[App] token有效');
+    logger.log('[App] token有效');
   }
 };
 </script>

+ 4 - 3
haha-mp/src/api/device.ts

@@ -3,6 +3,7 @@
  */
 
 import { post, get } from '../utils/request';
+import { API_PATHS } from '../utils/config';
 
 /**
  * 扫码开门请求参数
@@ -37,7 +38,7 @@ export interface DeviceStatusResponse {
  * @param deviceId 设备ID(从二维码中解析)
  */
 export const scanDoor = (deviceId: string): Promise<ScanDoorResponse> => {
-  return post<ScanDoorResponse>('/device/scan-open', {
+  return post<ScanDoorResponse>(API_PATHS.scanDoor, {
     deviceId
   });
 };
@@ -47,7 +48,7 @@ export const scanDoor = (deviceId: string): Promise<ScanDoorResponse> => {
  * @param deviceId 设备ID
  */
 export const checkDeviceStatus = (deviceId: string): Promise<DeviceStatusResponse> => {
-  return post<DeviceStatusResponse>('/device/status', {
+  return post<DeviceStatusResponse>(API_PATHS.checkDeviceStatus, {
     deviceId
   });
 };
@@ -107,5 +108,5 @@ export interface DeviceProductsResponse {
  * @param deviceId 设备ID
  */
 export const getDeviceProducts = (deviceId: string): Promise<DeviceProductsResponse> => {
-  return get<DeviceProductsResponse>(`/device/products/${deviceId}`, undefined, { skipAuth: true });
+  return get<DeviceProductsResponse>(API_PATHS.getDeviceProducts(deviceId), undefined, { skipAuth: true });
 };

+ 4 - 3
haha-mp/src/api/order.ts

@@ -3,6 +3,7 @@
  */
 
 import { post } from '../utils/request';
+import { API_PATHS } from '../utils/config';
 
 /**
  * 订单商品信息
@@ -75,7 +76,7 @@ export interface CancelOrderRequest {
  * @param params 查询参数
  */
 export const getOrderList = (params?: OrderListRequest): Promise<OrderInfo[]> => {
-  return post<OrderInfo[]>('/order/list', params || {});
+  return post<OrderInfo[]>(API_PATHS.getOrders, params || {});
 };
 
 /**
@@ -83,7 +84,7 @@ export const getOrderList = (params?: OrderListRequest): Promise<OrderInfo[]> =>
  * @param params 查询参数(必须包含orderId、orderNo或outTradeNo之一)
  */
 export const getOrderDetail = (params: OrderDetailRequest): Promise<OrderInfo> => {
-  return post<OrderInfo>('/order/detail', params);
+  return post<OrderInfo>(API_PATHS.getOrderDetail, params);
 };
 
 /**
@@ -91,5 +92,5 @@ export const getOrderDetail = (params: OrderDetailRequest): Promise<OrderInfo> =
  * @param orderId 订单ID
  */
 export const cancelOrder = (orderId: string): Promise<void> => {
-  return post<void>('/order/cancel', { orderId });
+  return post<void>(API_PATHS.cancelOrder, { orderId });
 };

+ 7 - 0
haha-mp/src/api/payscore.ts

@@ -59,3 +59,10 @@ export const enablePayscore = (): Promise<EnablePayscoreResponse> => {
 export const createPayscoreOrder = (params: CreatePayscoreOrderRequest): Promise<CreatePayscoreOrderResponse> => {
   return post<CreatePayscoreOrderResponse>('/payscore/create', params);
 };
+
+/**
+ * 轮询确认开通状态
+ */
+export const confirmEnablePayscore = (outOrderNo: string): Promise<{ enabled: boolean }> => {
+  return post<{ enabled: boolean }>('/payscore/confirm-enable', { outOrderNo });
+};

+ 4 - 3
haha-mp/src/api/user.ts

@@ -1,4 +1,5 @@
 import { post } from '../utils/request';
+import { API_PATHS } from '../utils/config';
 
 /**
  * 用户相关接口
@@ -30,19 +31,19 @@ export interface UserInfo {
  * phoneCode: 手机号获取凭证(用于获取手机号)
  */
 export const loginByMiniappPhone = (params: { code: string; phoneCode: string }): Promise<LoginResult> => {
-  return post<LoginResult>('/login/miniapp-phone', params);
+  return post<LoginResult>(API_PATHS.loginMiniappPhone, params);
 };
 
 /**
  * 获取用户信息
  */
 export const getUserInfo = (): Promise<UserInfo> => {
-  return post<UserInfo>('/login/user-info');
+  return post<UserInfo>(API_PATHS.getUserInfo);
 };
 
 /**
  * 退出登录
  */
 export const logout = (): Promise<void> => {
-  return post<void>('/login/logout');
+  return post<void>(API_PATHS.logout);
 };

+ 1 - 33
haha-mp/src/pages/couponCenter/couponCenter.vue

@@ -114,6 +114,7 @@ import { ref, reactive, onMounted } from 'vue';
 import { getAvailableCoupons, receiveCoupon } from '../../api/coupon';
 import type { CouponTemplateInfo } from '../../api/coupon';
 import { checkAuth } from '../../utils/auth';
+import { getCouponTypeLabel, getScopeLabel, formatTime } from '../../utils/coupon';
 
 // 扩展接口,添加前端需要的字段
 interface CouponTemplateInfoExtended extends CouponTemplateInfo {
@@ -135,39 +136,6 @@ const formatDiscountValue = (item: CouponTemplateInfoExtended): string => {
   return item.discountValue ? `${item.discountValue}` : '0';
 };
 
-/**
- * 获取优惠券类型标签
- */
-const getCouponTypeLabel = (type: number): string => {
-  const map: Record<number, string> = {
-    1: '满减',
-    2: '折扣',
-    3: '抵扣',
-    4: '兑换'
-  };
-  return map[type] || '优惠';
-};
-
-/**
- * 获取适用范围标签
- */
-const getScopeLabel = (scope: number): string => {
-  const map: Record<number, string> = {
-    1: '全场通用',
-    2: '指定品类',
-    3: '指定商品'
-  };
-  return map[scope] || '';
-};
-
-/**
- * 格式化时间
- */
-const formatTime = (timeStr: string): string => {
-  if (!timeStr) return '';
-  return timeStr.substring(0, 10);
-};
-
 /**
  * 判断是否可领取
  */

+ 1 - 34
haha-mp/src/pages/coupons/coupons.vue

@@ -102,6 +102,7 @@ import { ref, computed, onMounted } from 'vue';
 import { getMyCoupons } from '../../api/coupon';
 import type { UserCouponInfo, PageResult } from '../../api/coupon';
 import { checkAuth } from '../../utils/auth';
+import { getCouponTypeLabel, getScopeLabel, formatTime } from '../../utils/coupon';
 
 const tabs = [
   { label: '未使用', value: 0 },
@@ -187,40 +188,6 @@ const formatValue = (item: UserCouponInfo): string => {
   return val ? `${val}` : '--';
 };
 
-/**
- * 获取优惠券类型标签
- */
-const getCouponTypeLabel = (type: number): string => {
-  const map: Record<number, string> = {
-    1: '满减',
-    2: '折扣',
-    3: '抵扣',
-    4: '兑换'
-  };
-  return map[type] || '优惠';
-};
-
-/**
- * 获取适用范围标签
- */
-const getScopeLabel = (scope: number): string => {
-  const map: Record<number, string> = {
-    1: '全场通用',
-    2: '指定品类',
-    3: '指定商品'
-  };
-  return map[scope] || '';
-};
-
-/**
- * 格式化时间
- */
-const formatTime = (timeStr: string): string => {
-  if (!timeStr) return '';
-  // 取日期部分 yyyy-MM-dd
-  return timeStr.substring(0, 10);
-};
-
 /**
  * 跳转领券中心
  */

+ 12 - 8
haha-mp/src/pages/index/index.vue

@@ -47,7 +47,7 @@
       </view>
       <view class="info-item" @tap="callService">
         <text class="info-icon">📞</text>
-        <text class="info-text">客服: 400-0755-315</text>
+        <text class="info-text">客服: {{ customerServicePhone }}</text>
       </view>
     </view>
   </view>
@@ -60,16 +60,20 @@ import { scanDoor } from '../../api/device'
 import { checkPayscoreEnabled } from '../../api/payscore'
 import { getCouponCount } from '../../api/coupon'
 import { isLoggedIn, getToken } from '../../utils/auth'
+import { logger } from '../../utils/logger'
+import { CUSTOMER_SERVICE_PHONE } from '../../utils/config'
 
 // 页面显示时检查 token
 onShow(() => {
   const token = getToken()
-  console.log('[首页 onShow] token 状态:', token ? '存在' : '不存在')
+  logger.log('[首页 onShow] token 状态:', token ? '存在' : '不存在')
 })
 
+const customerServicePhone = CUSTOMER_SERVICE_PHONE
+
 // 处理支付分开通成功
 const onPayscoreEnabled = () => {
-  console.log('[首页] 用户已开通支付分')
+  logger.log('[首页] 用户已开通支付分')
   // 可以自动触发扫码
   setTimeout(() => {
     scanCode()
@@ -119,7 +123,7 @@ const scanCode = async () => {
   // 调用摄像头扫码
   uni.scanCode({
     success: async function (res) {
-      console.log('扫码结果:', res.result);
+      logger.log('扫码结果:', res.result);
 
       // 从扫码结果中解析设备ID
       // 二维码格式: https://hh.hahabianli.com/B142977?_wxpmm0=6009000C0000
@@ -133,7 +137,7 @@ const scanCode = async () => {
 
         if (match && match[1]) {
           deviceId = match[1];
-          console.log('提取到设备ID:', deviceId);
+          logger.log('提取到设备ID:', deviceId);
         } else {
           // 如果不是URL格式,尝试解析JSON格式
           try {
@@ -174,12 +178,12 @@ const scanCode = async () => {
           }
         },
         fail: () => {
-          console.log('用户取消选择');
+          logger.log('用户取消选择');
         }
       });
     },
     fail: function (err) {
-      console.log('扫码取消:', err);
+      logger.log('扫码取消:', err);
       uni.showToast({
         title: '扫码取消',
         icon: 'none'
@@ -250,7 +254,7 @@ const callService = () => {
   uni.makePhoneCall({
     phoneNumber: '4000755315',
     success: () => {
-      console.log('[首页] 拨号成功')
+      logger.log('[首页] 拨号成功')
     },
     fail: (err) => {
       console.error('[首页] 拨号失败:', err)

+ 13 - 11
haha-mp/src/pages/login/login.vue

@@ -99,7 +99,9 @@
 import { ref } from 'vue';
 import { onLoad } from '@dcloudio/uni-app';
 import { loginByMiniappPhone } from '@/api/user';
+import type { LoginResult } from '@/api/user';
 import { setToken, setUserInfo } from '@/utils/auth';
+import { logger } from '@/utils/logger';
 
 const isLoading = ref(false);
 
@@ -114,7 +116,7 @@ onLoad((options: any) => {
 });
 
 const onGetPhoneNumber = async (e: any) => {
-  console.log('[登录] 获取手机号事件:', e.detail);
+  logger.log('[登录] 获取手机号事件:', e.detail);
   
   if (e.detail.errMsg !== 'getPhoneNumber:ok') {
     console.error('[登录] 获取手机号失败:', e.detail.errMsg);
@@ -130,12 +132,12 @@ const onGetPhoneNumber = async (e: any) => {
   
   try {
     // 获取登录code
-    console.log('[登录] 开始获取login code...');
+    logger.log('[登录] 开始获取login code...');
     const loginRes = await new Promise<any>((resolve, reject) => {
       uni.login({
         provider: 'weixin',
         success: (res) => {
-          console.log('[登录] login成功:', res);
+          logger.log('[登录] login成功:', res);
           resolve(res);
         },
         fail: (err) => {
@@ -145,15 +147,15 @@ const onGetPhoneNumber = async (e: any) => {
       });
     });
     
-    console.log('[登录] loginRes:', loginRes);
-    console.log('[登录] phoneCode:', e.detail.code);
+    logger.log('[登录] loginRes:', loginRes);
+    logger.log('[登录] phoneCode:', e.detail.code);
     
     const { code, encryptedData, iv } = e.detail;
     const res = await loginByMiniappPhone({ 
       code: loginRes.code,
       phoneCode: code
     });
-    console.log('[登录] 登录成功:', res);
+    logger.log('[登录] 登录成功:', res);
     handleLoginSuccess(res);
   } catch (error: any) {
     console.error('[登录] 微信手机号登录失败', error);
@@ -169,16 +171,16 @@ const onGetPhoneNumber = async (e: any) => {
   }
 };
 
-const handleLoginSuccess = (res: any) => {
-  console.log('[登录成功] 收到的响应数据:', res);
-  console.log('[登录成功] token:', res.token);
-  console.log('[登录成功] userInfo:', res.userInfo);
+const handleLoginSuccess = (res: LoginResult) => {
+  logger.log('[登录成功] 收到的响应数据:', res);
+  logger.log('[登录成功] token:', res.token);
+  logger.log('[登录成功] userInfo:', res.userInfo);
 
   setToken(res.token);
   setUserInfo(res.userInfo);
 
   const savedToken = uni.getStorageSync('accessToken');
-  console.log('[登录成功] 保存后验证 - token:', savedToken ? '已保存' : '保存失败');
+  logger.log('[登录成功] 保存后验证 - token:', savedToken ? '已保存' : '保存失败');
 
   uni.vibrateShort({ type: 'medium' });
   

+ 8 - 9
haha-mp/src/pages/my/my.vue

@@ -100,7 +100,7 @@
 
     <!-- 版本信息 -->
     <view class="version-info">
-      v1.62.11
+      {{ appVersion }}
     </view>
   </view>
 </template>
@@ -111,14 +111,13 @@ import { onShow } from '@dcloudio/uni-app';
 import { logout as logoutApi } from '../../api/user';
 import { getCouponCount } from '../../api/coupon';
 import { clearAuth, getUserInfo as getStoredUserInfo } from '../../utils/auth';
+import { logger } from '../../utils/logger';
+import { APP_VERSION } from '../../utils/config';
+import type { UserInfo } from '../../api/user';
 
-const userInfo = ref<any>(null);
+const userInfo = ref<UserInfo | null>(null);
 const availableCouponCount = ref(0);
-
-// 显示用户名(已隐藏,仅显示手机号)
-const displayName = computed(() => {
-  return '';
-});
+const appVersion = APP_VERSION;
 
 // 显示手机号(脱敏)
 const displayPhone = computed(() => {
@@ -149,7 +148,7 @@ onMounted(async () => {
     const result = await getCouponCount();
     availableCouponCount.value = result.availableCount || 0;
   } catch (e) {
-    console.log('获取优惠券数量失败', e);
+    logger.log('获取优惠券数量失败', e);
   }
 });
 
@@ -241,7 +240,7 @@ const handleLogout = () => {
         try {
           await logoutApi();
         } catch (e) {
-          console.log('退出登录接口调用失败', e);
+          logger.log('退出登录接口调用失败', e);
         } finally {
           clearAuth();
           uni.reLaunch({

+ 2 - 42
haha-mp/src/pages/orderDetail/orderDetail.vue

@@ -56,11 +56,8 @@
           </view>
         </view>
 
-        <!-- 操作按钮 -->
         <view class="card-footer-actions">
           <button v-if="canRefund(order)" class="action-btn-outline" @click="applyRefund">申请退款</button>
-<!--          <button class="action-btn-primary" @click="printOrder">打印订单</button>-->
-<!--          <button class="action-btn-primary" @click="exportImage">导出图片</button>-->
         </view>
       </view>
 
@@ -135,23 +132,12 @@ import { ref, onMounted } from 'vue';
 import { getOrderDetail } from '../../api/order';
 import type { OrderInfo } from '../../api/order';
 import { checkAuth } from '../../utils/auth';
+import { getStatusText, canRefund } from '../../utils/order';
 
 const order = ref<OrderInfo | null>(null);
 const loading = ref(false);
 const orderId = ref<string | null>(null);
 
-/**
- * 获取订单状态文本
- */
-const getStatusText = (status: number) => {
-  const statusMap: any = {
-    0: '待支付',
-    1: '已完成',
-    2: '已取消'
-  };
-  return statusMap[status] || '未知';
-};
-
 /**
  * 获取状态样式
  */
@@ -162,32 +148,6 @@ const getStatusClass = (status: number) => {
   return '';
 };
 
-/**
- * 判断是否可以退款
- * 只有已完成的订单可以退款,且订单完成时间不能超过7天
- */
-const canRefund = (order: OrderInfo) => {
-  // 首先检查订单状态是否为已完成
-  if (order.status !== 1) {
-    return false;
-  }
-  
-  // 检查订单完成时间是否超过7天
-  if (order.payTime) {
-    const payTime = new Date(order.payTime);
-    const now = new Date();
-    const diffTime = now.getTime() - payTime.getTime();
-    const diffDays = diffTime / (1000 * 60 * 60 * 24);
-    
-    // 如果超过7天,不允许退款
-    if (diffDays > 7) {
-      return false;
-    }
-  }
-  
-  return true;
-};
-
 /**
  * 判断订单是否超过退款期限(7天)
  */
@@ -350,7 +310,7 @@ const copyText = (text: string) => {
  * 查看视频
  */
 const viewVideo = (url: string) => {
-  // TODO: 实现视频播放功能
+  // TODO: [video] 实现视频播放功能(需后端提供视频URL)
   uni.showToast({
     title: '视频播放功能开发中',
     icon: 'none'

+ 1 - 73
haha-mp/src/pages/orders/orders.vue

@@ -60,22 +60,11 @@ import { ref, onMounted } from 'vue';
 import { getOrderList } from '../../api/order';
 import type { OrderInfo } from '../../api/order';
 import { checkAuth } from '../../utils/auth';
+import { getStatusText, canRefund, isRefundExpired, getRefundExpireText } from '../../utils/order';
 
 const orders = ref<OrderInfo[]>([]);
 const loading = ref(false);
 
-/**
- * 获取订单状态文本
- */
-const getStatusText = (status: number) => {
-  const statusMap: any = {
-    0: '待支付',
-    1: '已完成',
-    2: '已取消'
-  };
-  return statusMap[status] || '未知';
-};
-
 /**
  * 计算订单商品总数量
  */
@@ -84,67 +73,6 @@ const getQuantity = (order: OrderInfo) => {
   return order.products.reduce((acc: number, p: any) => acc + p.quantity, 0);
 };
 
-/**
- * 判断是否可以申请退款
- * 只有已完成的订单可以退款,且订单完成时间不能超过7天
- */
-const canRefund = (order: OrderInfo) => {
-  // 首先检查订单状态是否为已完成
-  if (order.status !== 1) {
-    return false;
-  }
-  
-  // 检查订单完成时间是否超过7天
-  if (order.payTime) {
-    const payTime = new Date(order.payTime);
-    const now = new Date();
-    const diffTime = now.getTime() - payTime.getTime();
-    const diffDays = diffTime / (1000 * 60 * 60 * 24);
-    
-    // 如果超过7天,不允许退款
-    if (diffDays > 7) {
-      return false;
-    }
-  }
-  
-  return true;
-};
-
-/**
- * 判断订单是否超过退款期限(7天)
- */
-const isRefundExpired = (order: OrderInfo) => {
-  if (order.status !== 1 || !order.payTime) {
-    return false;
-  }
-  
-  const payTime = new Date(order.payTime);
-  const now = new Date();
-  const diffTime = now.getTime() - payTime.getTime();
-  const diffDays = diffTime / (1000 * 60 * 60 * 24);
-  
-  return diffDays > 7;
-};
-
-/**
- * 获取退款期限提示文本
- */
-const getRefundExpireText = (order: OrderInfo) => {
-  if (!order.payTime) return '';
-  
-  const payTime = new Date(order.payTime);
-  const now = new Date();
-  const diffTime = now.getTime() - payTime.getTime();
-  const diffDays = diffTime / (1000 * 60 * 60 * 24);
-  
-  if (diffDays > 7) {
-    return '已超过退款期限';
-  } else {
-    const remainingDays = Math.ceil(7 - diffDays);
-    return `剩余 ${remainingDays} 天可申请退款`;
-  }
-};
-
 /**
  * 加载订单列表
  */

+ 11 - 15
haha-mp/src/pages/payscore/enable.vue

@@ -129,7 +129,7 @@
       
       <view class="service-section">
         <text class="service-text">如有疑问,请联系客服</text>
-        <text class="service-link" @click="contactService">400-123-4567</text>
+        <text class="service-link" @click="contactService">{{ customerServicePhone }}</text>
       </view>
     </view>
   </view>
@@ -138,11 +138,15 @@
 <script setup lang="ts">
 import { ref } from 'vue'
 // @ts-ignore
-import { enablePayscore, checkPayscoreEnabled } from '@/api/payscore'
+import { enablePayscore, checkPayscoreEnabled, confirmEnablePayscore } from '@/api/payscore'
+
+import { logger } from '@/utils/logger';
+import { CUSTOMER_SERVICE_PHONE } from '@/utils/config';
 
 declare const wx: any
 
 const isEnabling = ref(false)
+const customerServicePhone = CUSTOMER_SERVICE_PHONE
 
 // 返回上一页
 const goBack = () => {
@@ -188,7 +192,7 @@ const handleEnablePayscore = async () => {
         out_order_no: outOrderNo
       },
       success: (res: any) => {
-        console.log('调起支付分成功:', res)
+        logger.log('调起支付分成功:', res)
         // 用户已完成授权操作,轮询确认开通状态
         pollEnableStatus(outOrderNo)
       },
@@ -234,18 +238,10 @@ const pollEnableStatus = (outOrderNo: string) => {
     }
     
     try {
-      // 调用确认开通接口
-      const response = await fetch('/api/payscore/confirm-enable', {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json'
-        },
-        body: JSON.stringify({ outOrderNo })
-      })
-      
-      const data = await response.json()
+      // 调用确认开通接口(通过统一请求封装,自动携带 token)
+      const data = await confirmEnablePayscore(outOrderNo)
       
-      if (data.code === 'SUCCESS' && data.data?.enabled) {
+      if (data?.enabled) {
         clearInterval(timer)
         
         // 开通成功
@@ -278,7 +274,7 @@ const pollEnableStatus = (outOrderNo: string) => {
 // 联系客服
 const contactService = () => {
   uni.makePhoneCall({
-    phoneNumber: '400-123-4567'
+    phoneNumber: CUSTOMER_SERVICE_PHONE
   })
 }
 

+ 3 - 2
haha-mp/src/pages/products/products.vue

@@ -113,6 +113,7 @@ import { onLoad } from '@dcloudio/uni-app';
 import { getDeviceProducts, scanDoor } from '@/api/device';
 import { checkPayscoreEnabled } from '@/api/payscore';
 import { isLoggedIn } from '@/utils/auth';
+import { logger } from '@/utils/logger';
 import type { FloorConfig } from '@/api/device';
 
 // 页面参数
@@ -159,7 +160,7 @@ onLoad((options: any) => {
     // 链接格式: https://dev-haha.kuaiyuman.cn/B150534
     try {
       const url = decodeURIComponent(options.q);
-      console.log('扫码链接:', url);
+      logger.log('扫码链接:', url);
       // 从 URL 路径中提取设备ID
       const match = url.match(/\/([A-Za-z0-9]+)(?:\?|$)/);
       if (match && match[1]) {
@@ -171,7 +172,7 @@ onLoad((options: any) => {
   }
 
   if (id) {
-    console.log('设备ID:', id);
+    logger.log('设备ID:', id);
     deviceId.value = id;
     loadProducts();
   } else {

+ 1 - 1
haha-mp/src/pages/refund/refund.vue

@@ -280,7 +280,7 @@ const increaseQuantity = (index: number) => {
  * 显示商品详情
  */
 const showProductDetail = () => {
-  // TODO: 跳转到商品详情页或弹出商品详情弹窗
+  // TODO: [sku-detail] 跳转到商品详情页或弹出商品详情弹窗
   uni.showToast({
     title: '商品详情功能开发中',
     icon: 'none'

+ 8 - 7
haha-mp/src/pages/replenish/bind.vue

@@ -91,6 +91,7 @@
 import { ref, computed, onMounted } from 'vue';
 import { onLoad } from '@dcloudio/uni-app';
 import { bindWechat, loginByWechat } from '@/api/replenish';
+import { logger } from '@/utils/logger';
 
 const bindingCode = ref('');
 const bindLoading = ref(false);
@@ -108,7 +109,7 @@ const displayCode = computed(() => {
 });
 
 onLoad((options: any) => {
-  console.log('[补货员绑定] onLoad options:', options);
+  logger.log('[补货员绑定] onLoad options:', options);
 
   // 从 scene 场景值或 query 参数获取绑定码
   if (options.scene) {
@@ -116,7 +117,7 @@ onLoad((options: any) => {
     if (/^[A-Z0-9]{24}$/.test(scene)) {
       bindingCode.value = scene;
       step.value = 1;
-      console.log('[补货员绑定] 从场景值获取绑定码:', scene);
+      logger.log('[补货员绑定] 从场景值获取绑定码:', scene);
       return;
     }
   }
@@ -124,14 +125,14 @@ onLoad((options: any) => {
   if (options.code) {
     bindingCode.value = options.code;
     step.value = 1;
-    console.log('[补货员绑定] 从参数获取绑定码:', options.code);
+    logger.log('[补货员绑定] 从参数获取绑定码:', options.code);
     return;
   }
 
   if (options.bindingCode) {
     bindingCode.value = options.bindingCode;
     step.value = 1;
-    console.log('[补货员绑定] 从参数获取绑定码:', options.bindingCode);
+    logger.log('[补货员绑定] 从参数获取绑定码:', options.bindingCode);
     return;
   }
 
@@ -154,12 +155,12 @@ const handleBind = async () => {
 
   try {
     // 1. 微信授权获取code
-    console.log('[补货员绑定] 开始微信授权...');
+    logger.log('[补货员绑定] 开始微信授权...');
     const loginRes = await new Promise<any>((resolve, reject) => {
       uni.login({
         provider: 'weixin',
         success: (res) => {
-          console.log('[补货员绑定] 微信授权成功:', res);
+          logger.log('[补货员绑定] 微信授权成功:', res);
           resolve(res);
         },
         fail: (err) => {
@@ -176,7 +177,7 @@ const handleBind = async () => {
     }
 
     // 2. 调用绑定接口
-    console.log('[补货员绑定] 开始调用绑定接口...');
+    logger.log('[补货员绑定] 开始调用绑定接口...');
     const response = await bindWechat({
       bindingCode: bindingCode.value.trim().toUpperCase(),
       code: loginRes.code

+ 6 - 3
haha-mp/src/pages/shopping/shopping.vue

@@ -101,10 +101,13 @@ import { ref, onMounted, onUnmounted, onActivated, onDeactivated } from 'vue';
 import { pollDeviceStatus, pollRecognizeResult, pollOrderInfo } from '../../api/status';
 import type { RecognizeResultResponse } from '../../api/status';
 
+import { logger } from '../../utils/logger';
+import type { OrderProduct } from '../../api/order';
+
 const doorStatus = ref<'opened' | 'closing' | 'closed' | 'error'>('opened');
 const countdown = ref(60);
 const returnCountdown = ref(5); // 返回首页倒计时
-const purchasedProducts = ref<any[]>([]);
+const purchasedProducts = ref<OrderProduct[]>([]);
 const totalPrice = ref(0);
 const errorMessage = ref('');
 const currentDeviceId = ref('');
@@ -214,7 +217,7 @@ const startStatusPolling = async () => {
       return;
     }
     
-    console.log('设备状态更新:', status);
+    logger.log('设备状态更新:', status);
     
     // 检查门是否关闭
     if (status.doorStatus === 'close') {
@@ -222,7 +225,7 @@ const startStatusPolling = async () => {
       doorStatus.value = 'closing';
       currentActivityId.value = status.activityId;
       
-      console.log('门已关,购物完成,活动ID:', status.activityId);
+      logger.log('门已关,购物完成,活动ID:', status.activityId);
       
       // 停止选购倒计时
       if (countdownTimer) {

+ 6 - 4
haha-mp/src/utils/auth.ts

@@ -3,12 +3,14 @@
  * 提供登录状态检查、token管理等功能
  */
 
+import { logger } from './logger';
+
 /**
  * 获取本地存储的token
  */
 export const getToken = (): string => {
   const token = uni.getStorageSync('accessToken');
-  console.log('[Auth] 获取token:', token ? '存在(长度:' + token.length + ')' : '不存在');
+  logger.log('[Auth] 获取token:', token ? '存在(长度:' + token.length + ')' : '不存在');
   return token || '';
 };
 
@@ -16,12 +18,12 @@ export const getToken = (): string => {
  * 保存token到本地存储
  */
 export const setToken = (token: string): void => {
-  console.log('[Auth] 保存token:', token ? '有效(长度:' + token.length + ')' : '无效');
+  logger.log('[Auth] 保存token:', token ? '有效(长度:' + token.length + ')' : '无效');
   uni.setStorageSync('accessToken', token);
 
   // 验证保存是否成功
   const saved = uni.getStorageSync('accessToken');
-  console.log('[Auth] 保存后验证:', saved === token ? '成功' : '失败');
+  logger.log('[Auth] 保存后验证:', saved === token ? '成功' : '失败');
 };
 
 /**
@@ -102,7 +104,7 @@ export const checkAuth = (redirectUrl?: string): boolean => {
  * 任何接口遇到401时都应调用此函数
  */
 export const handleUnauthorized = (): void => {
-  console.log('[Auth] token无效或已过期,清除登录信息并跳转登录页');
+  logger.log('[Auth] token无效或已过期,清除登录信息并跳转登录页');
   clearAuth();
   uni.reLaunch({
     url: '/pages/login/login'

+ 37 - 0
haha-mp/src/utils/coupon.ts

@@ -0,0 +1,37 @@
+/**
+ * 优惠券相关工具函数
+ * 抽离 coupons.vue 和 couponCenter.vue 中的重复逻辑
+ */
+
+/**
+ * 获取优惠券类型标签
+ */
+export const getCouponTypeLabel = (type: number): string => {
+  const map: Record<number, string> = {
+    1: '满减',
+    2: '折扣',
+    3: '抵扣',
+    4: '兑换'
+  };
+  return map[type] || '优惠';
+};
+
+/**
+ * 获取适用范围标签
+ */
+export const getScopeLabel = (scope: number): string => {
+  const map: Record<number, string> = {
+    1: '全场通用',
+    2: '指定品类',
+    3: '指定商品'
+  };
+  return map[scope] || '';
+};
+
+/**
+ * 格式化时间(取日期部分 yyyy-MM-dd)
+ */
+export const formatTime = (timeStr: string): string => {
+  if (!timeStr) return '';
+  return timeStr.substring(0, 10);
+};

+ 27 - 0
haha-mp/src/utils/logger.ts

@@ -0,0 +1,27 @@
+/**
+ * 日志工具
+ * 开发环境下输出 console.log,生产环境自动关闭
+ * console.error / console.warn 始终输出(用于异常监控)
+ */
+
+// @ts-ignore
+const isDev = import.meta.env.DEV || false;
+
+export const logger = {
+  log: (message: string, ...args: any[]) => {
+    if (isDev) {
+      console.log(message, ...args);
+    }
+  },
+
+  warn: (message: string, ...args: any[]) => {
+    if (isDev) {
+      console.warn(message, ...args);
+    }
+  },
+
+  error: (message: string, ...args: any[]) => {
+    // 错误日志始终输出,用于运维排查
+    console.error(message, ...args);
+  }
+};

+ 76 - 0
haha-mp/src/utils/order.ts

@@ -0,0 +1,76 @@
+/**
+ * 订单相关工具函数
+ * 抽离 orders.vue 和 orderDetail.vue 中的重复逻辑
+ */
+
+import type { OrderInfo } from '../api/order';
+
+/**
+ * 获取订单状态文本
+ */
+export const getStatusText = (status: number): string => {
+  const statusMap: Record<number, string> = {
+    0: '待支付',
+    1: '已完成',
+    2: '已取消'
+  };
+  return statusMap[status] || '未知';
+};
+
+/**
+ * 判断订单是否可以申请退款
+ * 只有已完成的订单可以退款,且订单完成时间不能超过7天
+ */
+export const canRefund = (order: OrderInfo): boolean => {
+  if (order.status !== 1) {
+    return false;
+  }
+  
+  if (order.payTime) {
+    const payTime = new Date(order.payTime);
+    const now = new Date();
+    const diffTime = now.getTime() - payTime.getTime();
+    const diffDays = diffTime / (1000 * 60 * 60 * 24);
+    
+    if (diffDays > 7) {
+      return false;
+    }
+  }
+  
+  return true;
+};
+
+/**
+ * 判断订单是否超过退款期限(7天)
+ */
+export const isRefundExpired = (order: OrderInfo): boolean => {
+  if (order.status !== 1 || !order.payTime) {
+    return false;
+  }
+  
+  const payTime = new Date(order.payTime);
+  const now = new Date();
+  const diffTime = now.getTime() - payTime.getTime();
+  const diffDays = diffTime / (1000 * 60 * 60 * 24);
+  
+  return diffDays > 7;
+};
+
+/**
+ * 获取退款期限提示文本
+ */
+export const getRefundExpireText = (order: OrderInfo): string => {
+  if (!order.payTime) return '';
+  
+  const payTime = new Date(order.payTime);
+  const now = new Date();
+  const diffTime = now.getTime() - payTime.getTime();
+  const diffDays = diffTime / (1000 * 60 * 60 * 24);
+  
+  if (diffDays > 7) {
+    return '已超过退款期限(7天)';
+  } else {
+    const remainingDays = Math.ceil(7 - diffDays);
+    return `剩余 ${remainingDays} 天可申请退款`;
+  }
+};