Parcourir la source

完善购物车

sunshibo il y a 1 mois
Parent
commit
9492cdeb03
3 fichiers modifiés avec 69 ajouts et 14 suppressions
  1. 33 3
      pages/orderFood/index.vue
  2. 13 7
      pages/orderInfo/orderDetail.vue
  3. 23 4
      pages/placeOrder/index.vue

+ 33 - 3
pages/orderFood/index.vue

@@ -36,8 +36,8 @@
     <!-- 领取优惠券弹窗 -->
     <CouponModal v-model:open="couponModalOpen" :coupon-list="couponList" @close="handleCouponClose" />
 
-    <!-- 购物车弹窗:按接口格式展示 items(cuisineName/cuisineImage/quantity/unitPrice/subtotalAmount/remark) -->
-    <CartModal v-model:open="cartModalOpen" :cart-list="displayCartList"
+    <!-- 购物车弹窗:按接口格式展示 items(含 tags 从 foodList 补全) -->
+    <CartModal v-model:open="cartModalOpen" :cart-list="displayCartListWithTags"
       @increase="handleIncrease" @decrease="handleDecrease" @clear="handleCartClear"
       @order-click="handleOrderClick" @close="handleCartClose" />
 
@@ -133,6 +133,36 @@ const displayCartList = computed(() => {
   return base;
 });
 
+// 将 tags 统一为 [{ text, type }] 格式(与 FoodCard 一致)
+function normalizeTags(raw) {
+  if (raw == null) return [];
+  let arr = [];
+  if (Array.isArray(raw)) arr = raw;
+  else if (typeof raw === 'string') {
+    const t = raw.trim();
+    if (t.startsWith('[')) { try { arr = JSON.parse(t); if (!Array.isArray(arr)) arr = []; } catch { arr = t ? [t] : []; } }
+    else arr = t ? t.split(/[,,、\s]+/).map(s => s.trim()).filter(Boolean) : [];
+  } else if (raw && typeof raw === 'object') arr = Array.isArray(raw.list) ? raw.list : Array.isArray(raw.items) ? raw.items : [];
+  return arr.map((item) => {
+    if (item == null) return { text: '', type: '' };
+    if (typeof item === 'string') return { text: item, type: '' };
+    if (typeof item === 'number') return { text: String(item), type: '' };
+    return { text: item.text ?? item.tagName ?? item.name ?? item.label ?? item.title ?? '', type: item.type ?? item.tagType ?? '' };
+  }).filter((t) => t.text !== '' && t.text != null);
+}
+
+// 购物车展示列表(含标签):从 foodList 补全 cart 项缺失的 tags
+const displayCartListWithTags = computed(() => {
+  return displayCartList.value.map((item) => {
+    const tags = item?.tags ?? item?.tagList ?? item?.tagNames;
+    if (tags != null && (Array.isArray(tags) ? tags.length > 0 : true)) return item;
+    const food = findFoodByCartItem(item);
+    const fromFood = food?.tags ?? food?.tagList ?? food?.tagNames ?? food?.labels ?? food?.tag;
+    const normalized = normalizeTags(fromFood);
+    return normalized.length > 0 ? { ...item, tags: normalized } : item;
+  });
+});
+
 // 行小计:接口项用 subtotalAmount,否则 数量×单价(与 BottomActionBar/CartModal 一致)
 const getItemLinePrice = (item) => {
   if (item?.subtotalAmount != null) return Number(item.subtotalAmount);
@@ -607,7 +637,7 @@ const handleOrderClick = () => {
   const totalAmount = displayTotalAmount.value;
   const dishTotal = Math.max(0, Number(totalAmount) - Number(utensilFee));
   const cartPayload = {
-    list: displayCartList.value,
+    list: displayCartListWithTags.value,
     totalAmount,
     dishTotal,
     totalQuantity: displayTotalQuantity.value,

+ 13 - 7
pages/orderInfo/orderDetail.vue

@@ -14,8 +14,8 @@
       <view class="card-content">
         <view class="info-food">
           <view v-for="(item, index) in displayFoodList" :key="item.id || index" class="food-item">
-            <!-- 菜品图片 -->
-            <image :src="getFileUrl(item.image)" mode="aspectFill" class="food-item__image"></image>
+            <!-- 菜品图片:餐具用本地图 -->
+            <image :src="getItemImage(item)" mode="aspectFill" class="food-item__image"></image>
 
             <!-- 菜品信息 -->
             <view class="food-item__info">
@@ -139,10 +139,16 @@ const priceDetail = ref({
 // 菜品列表(接口订单明细)
 const foodList = ref([]);
 
-// 菜品详情展示:排除餐具(id/cuisineId === -1)
-const displayFoodList = computed(() => {
-  return (foodList.value ?? []).filter((it) => Number(it?.id ?? it?.cuisineId) !== -1);
-});
+// 菜品详情展示:包含所有项(含餐具 id/cuisineId === -1)
+const displayFoodList = computed(() => foodList.value ?? []);
+
+// 菜品图片:餐具(id/cuisineId=-1)用本地图
+function getItemImage(item) {
+  if (Number(item?.id ?? item?.cuisineId) === -1) return '/static/utensilFee.png';
+  const raw = item?.image ?? item?.cuisineImage ?? item?.imageUrl ?? '';
+  if (typeof raw === 'string' && (raw.startsWith('http') || raw.startsWith('//'))) return raw;
+  return getFileUrl(raw || 'img/icon/shop.png');
+}
 
 // 金额保留两位小数
 const formatPrice = (price) => {
@@ -185,7 +191,7 @@ function applyOrderData(data) {
   const dishTotal = raw?.totalAmount ?? raw?.dishTotal ?? raw?.orderAmount ?? raw?.foodAmount ?? 0;
   const tablewareFee = raw?.tablewareFee ?? raw?.tablewareAmount ?? 0;
   const couponDiscount = raw?.couponAmount ?? raw?.couponDiscount ?? 0;
-  const total = raw?.totalAmount ?? raw?.totalPrice ?? raw?.payAmount ?? 0;
+  const total = raw?.payAmount ?? raw?.totalAmount ?? raw?.totalPrice ?? 0;
   priceDetail.value = {
     dishTotal: formatPrice(dishTotal),
     tablewareFee: formatPrice(tablewareFee),

+ 23 - 4
pages/placeOrder/index.vue

@@ -48,8 +48,8 @@
             <view class="food-item__info">
               <view class="food-item__name">{{ item.name || item.cuisineName }}</view>
               <view class="food-item__desc" v-if="item.tags && item.tags.length > 0">
-                <text v-for="(tag, tagIndex) in item.tags" :key="tagIndex" class="food-item__tag">
-                  {{ tag.text }}<text v-if="tagIndex < item.tags.length - 1">,</text>
+                <text v-for="(tag, tagIndex) in item.tags" :key="tagIndex" class="food-item__tag"
+                  :class="tag.type">{{ tag.text }}<text v-if="tagIndex < item.tags.length - 1">,</text>
                 </text>
               </view>
             </view>
@@ -290,10 +290,29 @@ const fetchCouponsOnEnter = async () => {
   }
 };
 
-// 将存储的购物车项转为确认页展示格式(兼容 cuisineName/cuisineImage/unitPrice 等)
+// 将 tags 统一为 [{ text, type }] 格式
+function normalizeTags(raw) {
+  if (raw == null) return [];
+  let arr = [];
+  if (Array.isArray(raw)) arr = raw;
+  else if (typeof raw === 'string') {
+    const t = raw.trim();
+    if (t.startsWith('[')) { try { arr = JSON.parse(t); if (!Array.isArray(arr)) arr = []; } catch { arr = t ? [t] : []; } }
+    else arr = t ? t.split(/[,,、\s]+/).map(s => s.trim()).filter(Boolean) : [];
+  } else if (raw && typeof raw === 'object') arr = Array.isArray(raw.list) ? raw.list : Array.isArray(raw.items) ? raw.items : [];
+  return arr.map((it) => {
+    if (it == null) return { text: '', type: '' };
+    if (typeof it === 'string') return { text: it, type: '' };
+    if (typeof it === 'number') return { text: String(it), type: '' };
+    return { text: it.text ?? it.tagName ?? it.name ?? it.label ?? it.title ?? '', type: it.type ?? it.tagType ?? '' };
+  }).filter((t) => t.text !== '' && t.text != null);
+}
+
+// 将存储的购物车项转为确认页展示格式(兼容 cuisineName/cuisineImage/unitPrice/tags 等)
 function normalizeCartItem(item) {
   const image = item?.cuisineImage ?? item?.image ?? item?.imageUrl ?? '';
   const url = typeof image === 'string' ? image.split(',')[0].trim() : image;
+  const rawTags = item?.tags ?? item?.tagList ?? item?.tagNames ?? item?.labels ?? item?.tag;
   return {
     id: item?.id ?? item?.cuisineId,
     name: item?.name ?? item?.cuisineName,
@@ -302,7 +321,7 @@ function normalizeCartItem(item) {
     image: url,
     cuisineImage: item?.cuisineImage,
     quantity: Number(item?.quantity) || 0,
-    tags: item?.tags ?? [],
+    tags: normalizeTags(rawTags),
     subtotalAmount: item?.subtotalAmount,
     remark: item?.remark
   };