<script setup lang="ts">
import { ref, computed, watch, nextTick } from 'vue';
import { useI18n } from 'vue-i18n';
import {
  appMerchantCartDeleteByMerchantIdPost,
  appMerchantCartListMerchantPost,
  appMerchantCartDeleteCartPost,
  appMerchantCartCalculateSavingsPost,
  appMerchantCartListByMerchantIdPost,
  appMerchantCartAddCartByIdPost,
  appMerchantCartAddCartPost,
  appMerchantDishDishIdGet,
  appMerchantDetailMerchantIdGet,
} from '@/service';
import {
  buildReservationTimeUrl,
  buildDayAppointmentSlot,
  findNearestDeliveryScheduleDate,
  saveCheckoutMerchantAppointments,
  type MerchantAppointmentSlot,
} from '@/utils/deliverySchedule';
import dayjs from "dayjs";
import "dayjs/locale/zh-cn";
import Config from '@/config';
import { parseMerchantCartPayload } from '@/utils/utils';
import { useUserStore } from '@/store';
import useEventEmit from '@/hooks/useEventEmit';
import { EventEnum } from '@/constant/enums';
import { onShow } from '@dcloudio/uni-app';

dayjs.locale("zh-cn");

const { t } = useI18n();
import RemoveCart from './components/remove-cart.vue';
import RemoveStore from './components/remove-store.vue';
import MultiStoreNotice from "./components/multi-store-notice.vue";
import CartSkeleton from './components/cart-skeleton.vue';
const userStore = useUserStore();

const removeCartRef = ref<InstanceType<typeof RemoveCart>>();
const removeItemRef = ref<InstanceType<typeof RemoveStore>>();
const isMultiStoreNoticeOpen = ref(false);
interface CartItem {
  id: string;
  dishId: string;
  merchantId: string;
  count: number;
  merchantDishVo: any;
  sideDishList: any[];
  checked: boolean;
  discountPrice: number;
  originalPrice: number;
  memberPrice: number;
}

interface CartMerchant {
  id: string;
  merchantName: string;
  logo: string;
  merchantAddress: string;
  cartTotalPrice: number;
  merchantCartVoList: CartItem[];
  allChecked: boolean;
  businessHours?: string;
  deliveryScheduleTimes?: string;
}

const dataList = ref<CartMerchant[]>([]);
const allChecked = ref(false);

const selectedCartItems = computed(() => {
  const items: CartItem[] = [];
  dataList.value.forEach((merchant) => {
    merchant.merchantCartVoList.forEach((item) => {
      if (item.checked) {
        items.push(item);
      }
    });
  });
  return items;
});

const selectedCount = computed(() => selectedCartItems.value.length);
const appDisplayName = computed(() => {
  const name = String((Config as any)?.appName ?? "").trim();
  return name || "CHEFLINK";
});

function normalizePriceString(val: unknown) {
  const s = String(val ?? "").trim();
  if (!/^\d+(\.\d+)?$/.test(s)) return "0";
  const [intRaw, decRaw = ""] = s.split(".");
  const intPart = intRaw.replace(/^0+(?=\d)/, "") || "0";
  const decPart = decRaw.slice(0, 2).padEnd(2, "0");
  return `${intPart}.${decPart}`;
}

function priceToCentString(val: unknown) {
  const normalized = normalizePriceString(val);
  const [intPart, decPart = "00"] = normalized.split(".");
  return `${intPart}${decPart}`.replace(/^0+(?=\d)/, "") || "0";
}

function formatPriceForView(val: unknown) {
  const normalized = normalizePriceString(val);
  return normalized.replace(/\.00$/, "").replace(/(\.\d)0$/, "$1");
}

function getCartLineDisplayPrice(item: CartItem) {
  // const memberCent = priceToCentString(item?.memberPrice);
  // console.log('memberCent', memberCent);
  if (userStore.userInfo.userMembershipVo!==null) {
    return item?.memberPrice;
  }
  return item?.discountPrice;
}

function showCartLineOriginalPrice(item: CartItem) {
  const originalCent = priceToCentString(item?.originalPrice);
  const currentCent = userStore.userInfo.userMembershipVo!==null && priceToCentString(item?.memberPrice) !== "0"
    ? priceToCentString(item?.memberPrice)
    : priceToCentString(item?.discountPrice);
  return originalCent !== "0" && originalCent !== currentCent;
}

const priceData = ref<
  Record<string, any> & {
    totalPayPrice?: string | number;
    savings?: string | number;
    totalQuantity?: number;
  }
>({
  totalPayPrice: 0,
  savings: 0,
});

const calculatingPrice = ref(false);

/** 已选商品件数（线条目数，与接口 totalQuantity 可能不同） */
const selectedItemQty = computed(() =>
  selectedCartItems.value.reduce((n, it) => n + (Number(it.count) || 0), 0)
);

/** 已选商品商品金额小计（不含运费等），用于与设计图「n件商品总共」对齐 */
const selectedGoodsSubtotal = computed(() => {
  let total = 0;
  selectedCartItems.value.forEach((item) => {
    const base = Number(item.merchantDishVo?.discountPrice) || 0;
    let side = 0;
    item.sideDishList?.forEach((d: any) => {
      side += Number(d?.merchantSideDishItemVo?.price) || 0;
    });
    const line = base + side;
    total += line * (Number(item.count) || 0);
  });
  return total.toFixed(2);
});

const deliveryUnifiedText = computed(() =>
  t("pages-user.cart.deliveryUnified", { name: appDisplayName.value })
);

const itemsTotalWithPriceText = computed(() =>
  t("pages-user.cart.itemsTotalWithPrice", {
    count: priceData.value.totalQuantity ?? selectedItemQty.value,
    amount: selectedGoodsSubtotal.value,
  })
);

const merchantAppointmentMap = ref<Record<string, MerchantAppointmentSlot>>({});
const merchantAppointmentDisplay = ref<Record<string, string>>({});
const selectingMerchantId = ref<string | null>(null);

function ensureDefaultMerchantAppointment(merchant: CartMerchant) {
  const key = String(merchant.id);
  if (merchantAppointmentMap.value[key]?.startTime) return;

  const nearest = findNearestDeliveryScheduleDate(
    merchant.deliveryScheduleTimes
  );
  if (!nearest) return;

  const slot = buildDayAppointmentSlot(nearest);
  merchantAppointmentMap.value[key] = slot;
  merchantAppointmentDisplay.value[key] = dayjs(nearest).format("ddd, MM/DD");
}

function getMerchantScheduleLabel(merchantId: string | number) {
  const key = String(merchantId);
  if (merchantAppointmentDisplay.value[key]) {
    return merchantAppointmentDisplay.value[key];
  }
  const ap = merchantAppointmentMap.value[key];
  if (ap?.startTime) {
    const d = dayjs(Number(ap.startTime));
    if (d.isValid()) return d.format("ddd, MM/DD");
  }
  return t("pages-store.checkout.chooseTime");
}

useEventEmit(EventEnum.CHOOSE_APPOINTMENT_TIME, (data: any) => {
  if (!data?.startTime || !data?.endTime) return;
  const merchantId = String(
    data.merchantId || selectingMerchantId.value || ""
  );
  if (!merchantId) return;
  merchantAppointmentMap.value[merchantId] = {
    date: data.date,
    timeSlot: data.timeSlot,
    startTime: data.startTime,
    endTime: data.endTime,
  };
  merchantAppointmentDisplay.value[merchantId] = dayjs(data.date).format(
    "ddd, MM/DD"
  );
  selectingMerchantId.value = null;
});

watch(
  () => selectedCartItems.value.map((item) => item.id),
  (newIds) => {
    if (newIds.length > 0) {
      calculatePrice();
    } else {
      priceData.value = {
        totalPayPrice: 0,
        savings: 0,
      };
      calculatingPrice.value = false;
    }
  },
  { deep: true }
);

const calculatePrice = () => {
  calculatingPrice.value = true;
  const cartIds = selectedCartItems.value.map((item) => item.id);
  appMerchantCartCalculateSavingsPost({
    body: cartIds as any,
  })
    .then((res) => {
      priceData.value = (res as any).data ?? {};
    })
    .catch((err) => {
      console.error("价格统计失败", err);
    })
    .finally(() => {
      calculatingPrice.value = false;
    });
};

const toggleAllCheck = () => {
  dataList.value.forEach((merchant) => {
    merchant.allChecked = allChecked.value;
    merchant.merchantCartVoList.forEach((item) => {
      item.checked = allChecked.value;
    });
  });
};

const toggleMerchantCheck = (merchant: CartMerchant) => {
  merchant.merchantCartVoList.forEach((item) => {
    item.checked = merchant.allChecked;
  });
  updateAllCheckedState();
};

const toggleItemCheck = (merchant: CartMerchant, item: CartItem) => {
  merchant.allChecked = merchant.merchantCartVoList.every((i) => i.checked);
  updateAllCheckedState();
};

const updateAllCheckedState = () => {
  const allItems = dataList.value.flatMap((m) => m.merchantCartVoList);
  allChecked.value =
    allItems.length > 0 && allItems.every((item) => item.checked);
};

const viewStore = (storeId: string) => {
  uni.navigateTo({
    url: `/pages-store/pages/store/index?id=${storeId}`,
  });
};

const removeStoreId = ref("");
const deleteStore = (item: any) => {
  removeCartRef.value?.onOpen();
  removeStoreId.value = item.id;
};
function confirmRemove() {
  appMerchantCartDeleteByMerchantIdPost({
    params: {
      merchantId: removeStoreId.value as any,
    },
  }).then(() => {
    getCartList();
    uni.showToast({
      icon: "none",
      title: t("toast.deleteSuccess"),
    });
  });
}

const continueCheckoutWithValidation = () => {
  if (selectedCount.value === 0) {
    uni.showToast({
      icon: "none",
      title: t("pages-user.cart.selectItemsHint"),
    });
    return;
  }

  if (calculatingPrice.value) {
    uni.showToast({
      icon: "none",
      title: t("pages-user.cart.calculating"),
    });
    return;
  }

  goCheckoutNow();
};

const batchCheckout = () => {
  continueCheckoutWithValidation();
};

const goCheckoutNow = () => {
  const selectedMerchants = dataList.value.filter((m) =>
    m.merchantCartVoList.some((item) => item.checked)
  );
  for (const m of selectedMerchants) {
    const ap = merchantAppointmentMap.value[String(m.id)];
    if (!ap?.startTime || !ap?.endTime) {
      uni.showToast({
        title: t("pages-store.checkout.chooseTimeForStore", {
          name: m.merchantName || "",
        }),
        icon: "none",
      });
      return;
    }
  }
  saveCheckoutMerchantAppointments(merchantAppointmentMap.value);
  const cartIds = selectedCartItems.value.map((item) => item.id);
  uni.navigateTo({
    url: `/pages-store/pages/order/checkout?cartIds=${cartIds.join(",")}&type=batch`,
  });
};

const confirmMultiStoreNotice = () => {
  continueCheckoutWithValidation();
};

const handleMultiStoreNoticeClose = () => {
  isMultiStoreNoticeOpen.value = false;
};

const goToHome = () => {
  uni.switchTab({
    url: "/pages/home/index",
  });
};

const loading = ref(true);

/* ---------- 数量调整（与单店购物车一致） ---------- */
const itemCountCache = new Map<string | number, number>();
const pendingUpdateTargets = new Map<string | number, number>();
const pendingUpdateTimers = new Map<string | number, ReturnType<typeof setTimeout>>();
const updateDelay = 100;

function syncItemCountCaches() {
  itemCountCache.clear();
  dataList.value.forEach((m) => {
    m.merchantCartVoList?.forEach((item: CartItem) => {
      if (item?.id != null) {
        itemCountCache.set(item.id, item.count ?? 0);
      }
    });
  });
}

function clearPendingTimers() {
  pendingUpdateTimers.forEach((timer) => clearTimeout(timer));
  pendingUpdateTimers.clear();
  pendingUpdateTargets.clear();
}

onBeforeUnmount(() => {
  clearPendingTimers();
});

function addCartCount(item: CartItem, count: number) {
  if (!item?.id || count <= 0) return;
  appMerchantCartAddCartByIdPost({
    body: {
      id: item.id,
      dishId: item.dishId,
      merchantId: item.merchantId,
      count,
    } as any,
  })
    .then(() => getCartList())
    .catch(() => getCartList());
}

function deleteCartCount(item: CartItem, count: number) {
  if (!item?.id || count <= 0) return;
  appMerchantCartDeleteCartPost({
    body: {
      id: item.id,
      count,
    } as any,
  })
    .then(() => getCartList())
    .catch(() => getCartList());
}

function scheduleCartUpdate(item: CartItem, targetValue: number) {
  if (!item?.id) return;
  const key = item.id;
  pendingUpdateTargets.set(key, targetValue);
  const existTimer = pendingUpdateTimers.get(key);
  if (existTimer) clearTimeout(existTimer);
  const timer = setTimeout(() => {
    pendingUpdateTimers.delete(key);
    const latestTarget = pendingUpdateTargets.get(key);
    pendingUpdateTargets.delete(key);
    if (typeof latestTarget !== "number") return;
    const prev = itemCountCache.get(key) ?? 0;
    const diff = latestTarget - prev;
    if (diff === 0) return;
    const absDiff = Math.abs(diff);
    if (diff > 0) {
      addCartCount(item, absDiff);
    } else {
      deleteCartCount(item, absDiff);
    }
    itemCountCache.set(key, latestTarget);
  }, updateDelay);
  pendingUpdateTimers.set(key, timer);
}

/** wd-input-number change 为 { value }，兼容 detail.value */
function normalizeInputNumberValue(payload: unknown): number {
  const raw =
    typeof payload === "number"
      ? payload
      : (payload as any)?.value ?? (payload as any)?.detail?.value ?? payload;
  const n = Number(raw);
  return Number.isFinite(n) ? n : 0;
}

const delItemData = ref<CartItem | null>(null);

function handleQuantityChange(payload: any, item: CartItem) {
  if (!item) return;
  const nextValue = normalizeInputNumberValue(payload);
  const key = item.id;
  const prev = itemCountCache.get(key) ?? item.count ?? 0;
  if (nextValue === prev) return;
  if (nextValue < 0) {
    item.count = prev;
    return;
  }
  if (prev === 1 && nextValue === 0) {
    delItemData.value = item;
    removeItemRef.value?.onOpen(item.merchantDishVo?.dishName || "");
    return;
  }
  scheduleCartUpdate(item, nextValue);
}

function handleRemoveItem() {
  if (!delItemData.value) return;
  deleteCartCount(delItemData.value, 1);
  delItemData.value = null;
}

function handleRemoveItemPopupClose() {
  if (!delItemData.value) return;
  const item = delItemData.value;
  const key = item.id;
  const fallback = itemCountCache.get(key) ?? 1;
  item.count = fallback || 1;
  delItemData.value = null;
}

async function onMerchantScheduleTap(merchant: CartMerchant) {
  if (!userStore.isLogin) {
    uni.showToast({ title: t("common.pleaseLogin"), icon: "none" });
    setTimeout(() => uni.navigateTo({ url: Config.loginPath }), 400);
    return;
  }
  selectingMerchantId.value = String(merchant.id);
  let businessHours = merchant.businessHours;
  let schedule = merchant.deliveryScheduleTimes;
  if (!businessHours || !schedule) {
    try {
      const detail: any = await appMerchantDetailMerchantIdGet({
        params: { merchantId: merchant.id as any },
      });
      businessHours = detail.data?.businessHours ?? businessHours;
      schedule = detail.data?.deliveryScheduleTimes ?? schedule;
      merchant.businessHours = businessHours;
      merchant.deliveryScheduleTimes = schedule;
    } catch {
      /* ignore */
    }
  }
  uni.navigateTo({
    url: buildReservationTimeUrl({
      merchantId: merchant.id,
      deliveryScheduleTimes: schedule,
      businessHours,
    }),
  });
}

const addingRecommendId = ref<string | number | null>(null);

async function addRecommendToCart(dish: any) {
  if (!userStore.isLogin) {
    uni.showToast({ title: t("common.pleaseLogin"), icon: "none" });
    setTimeout(() => uni.navigateTo({ url: Config.loginPath }), 400);
    return;
  }
  const merchantId = dataList.value[0]?.id;
  if (!dish?.id || !merchantId) return;
  if (addingRecommendId.value != null) return;

  addingRecommendId.value = dish.id;
  try {
    await appMerchantCartAddCartPost({
      body: {
        merchantId,
        dishId: dish.id,
        count: 1,
        merchantCartSideDishBoList: [],
      } as any,
    });
    uni.showToast({ title: t("toast.addCartSuccess"), icon: "none" });
    await getCartList();
    loadRecommendDishes();
  } finally {
    addingRecommendId.value = null;
  }
}

/* ---------- 推荐（首店） ---------- */
const recommendDishes = ref<any[]>([]);

async function loadRecommendDishes() {
  if (!dataList.value.length) {
    recommendDishes.value = [];
    return;
  }
  const m = dataList.value[0];
  if (!m?.id) return;
  try {
    const res: any = await appMerchantDishDishIdGet({
      body: {
        merchantId: m.id,
        pageNum: 1,
        pageSize: 8,
      } as any,
    });
    const rows = res?.data?.rows ?? [];
    const cartDishIds = new Set<string>();
    dataList.value.forEach((mer) => {
      mer.merchantCartVoList?.forEach((it: CartItem) => {
        if (it.dishId != null) cartDishIds.add(String(it.dishId));
      });
    });
    recommendDishes.value = rows
      .filter((r: any) => r && !cartDishIds.has(String(r.id)))
      .slice(0, 6);
  } catch {
    recommendDishes.value = [];
  }
}

watch(
  dataList,
  () => {
    loadRecommendDishes();
  },
  { deep: true }
);

function openRecommendDish(dish: any) {
  if (!dish?.id || !dataList.value[0]?.id) return;
  uni.navigateTo({
    url: `/pages-store/pages/store/dishes?id=${dish.id}&storeId=${dataList.value[0].id}`,
  });
}

/** 接口若返回运费/服务费（扩展字段）则展示 */
function feeLine(
  origKey: string,
  curKey: string
): { show: boolean; orig?: any; cur?: any } {
  const d = priceData.value as any;
  const orig = d[origKey];
  const cur = d[curKey];
  if (cur == null && orig == null) return { show: false };
  return { show: true, orig, cur };
}

// onMounted(() => {
//   loading.value = true;
//   getCartList();
// });

onShow(() => {
  if (userStore.isLogin) {
    userStore.getAppointmentTime();
    // 从结算页返回时强制刷新购物车，避免已结算商品残留
    loading.value = true;
    getCartList({ showMultiStoreNotice: true });
  } else {
    dataList.value = [];
    loading.value = false;
  }
});

async function getCartList(options?: { showMultiStoreNotice?: boolean }) {
  try {
    const res: any = await appMerchantCartListMerchantPost({});
    const merchants = res.data;
    if (!merchants || !merchants.length) {
      dataList.value = [];
      return;
    }

    const merchantPromises = merchants.map(async (merchant: any) => {
      try {
        const cartRes: any = await appMerchantCartListByMerchantIdPost({
          params: {
            merchantId: merchant.id,
          },
        });
        const payload = parseMerchantCartPayload(cartRes?.data);
        return {
          ...merchant,
          deliveryScheduleTimes:
            payload.deliveryScheduleTimes ?? merchant.deliveryScheduleTimes,
          allChecked: false,
          merchantCartVoList: payload.items.map((item: any) => ({
            ...item,
            checked: false,
          })),
        };
      } catch (error) {
        console.error(`获取店铺 ${merchant.id} 商品失败`, error);
        return {
          ...merchant,
          allChecked: false,
          merchantCartVoList: [],
        };
      }
    });

    dataList.value = await Promise.all(merchantPromises);
    dataList.value.forEach((m) => ensureDefaultMerchantAppointment(m));
    syncItemCountCaches();
    if (options?.showMultiStoreNotice && dataList.value.length > 1) {
      nextTick(() => {
        isMultiStoreNoticeOpen.value = true;
      });
    }
  } catch (error) {
    console.error("获取购物车列表失败", error);
    dataList.value = [];
  } finally {
    loading.value = false;
  }
}
</script>

<template>
  <view class="cart-root">
  <z-paging bg-color="#F5F5F5">
    <template #top>
      <navbar
        :title="t('pages-user.cart.title')"
        circle-back
        custom-class="!bg-white"
      />
      <view
        v-if="!loading && dataList.length > 0"
        class="cart-select-all bg-white px-30rpx py-24rpx flex items-center border-b border-[#F0F0F0]"
      >
        <wd-checkbox v-model="allChecked" shape="circle" @change="toggleAllCheck">
          <text class="text-28rpx text-[#333]">{{ t("pages-user.cart.selectAll") }}</text>
        </wd-checkbox>
      </view>
    </template>

    <view
      class="animate-in fade-in animate-ease-out animate-duration-300"
      v-show="loading"
    >
      <cart-skeleton />
    </view>
    <view
      class="animate-in fade-in animate-ease-in animate-duration-300 cart-page-body"
      v-show="!loading"
    >
      <view class="px-24rpx pt-24rpx pb-280rpx">
        <template v-if="dataList.length > 0">
          <!-- 配送与价格摘要（整页一次，对齐设计稿主卡片顶部） -->
          <view class="cart-store-card cart-summary-only mb-24rpx overflow-hidden">
            <view class="cart-delivery-block">
              <view class="flex items-start">
                <view
                  class="i-carbon:shopping-cart text-40rpx text-#14181b mt-4rpx shrink-0 opacity-80"
                />
                <view class="ml-16rpx flex-1 min-w-0">
                  <text class="cart-delivery-title">{{
                    t("pages-user.cart.localDelivery")
                  }}</text>
                  <text class="cart-delivery-sub">{{
                    deliveryUnifiedText
                  }}</text>
                </view>
              </view>

              <view class="cart-delivery-row mt-28rpx">
                <view class="cart-price-summary cart-price-summary--full">
                  <text class="cart-summary-line1">{{
                    itemsTotalWithPriceText
                  }}</text>
                  <view
                    v-if="
                      feeLine('originalDeliveryFee', 'deliveryFee').show
                    "
                    class="cart-fee-line"
                  >
                    <text>{{ t("pages-user.cart.deliveryFeeLine") }}</text>
                    <text
                      v-if="priceData.originalDeliveryFee != null"
                      class="cart-fee-strike"
                      >${{ priceData.originalDeliveryFee }}</text
                    >
                    <text class="cart-fee-cur"
                      >${{ priceData.deliveryFee }}</text
                    >
                  </view>
                  <view
                    v-if="
                      feeLine('originalServiceFee', 'serviceFee').show
                    "
                    class="cart-fee-line"
                  >
                    <text>{{ t("pages-user.cart.serviceFeeLine") }}</text>
                    <text
                      v-if="priceData.originalServiceFee != null"
                      class="cart-fee-strike"
                      >${{ priceData.originalServiceFee }}</text
                    >
                    <text
                      v-if="priceData.serviceFeeFree"
                      class="cart-fee-free"
                      >{{ t("pages-user.member.free") }}</text
                    >
                    <text
                      v-else
                      class="cart-fee-cur"
                      >${{ priceData.serviceFee }}</text
                    >
                  </view>
                </view>
              </view>
            </view>
          </view>

          <view
            v-for="merchant in dataList"
            :key="merchant.id"
            class="cart-store-card mb-24rpx"
          >
            <!-- 店铺标题行 -->
            <view class="cart-store-head">
              <view class="flex items-center flex-1 min-w-0" @click="viewStore(merchant.id)">
                <wd-checkbox
                  v-model="merchant.allChecked"
                  shape="circle"
                  @change="toggleMerchantCheck(merchant)"
                  @click.stop
                />
                <view
                  class="w-52rpx h-52rpx rounded-full overflow-hidden bg-[#E8E8E8] ml-16rpx shrink-0"
                >
                  <image
                    :src="merchant.logo"
                    class="w-full h-full"
                    mode="aspectFill"
                  />
                </view>
                <text class="cart-store-name ml-16rpx ellipsis">{{
                  merchant.merchantName
                }}</text>
              </view>
              <view
                class="cart-icon-btn"
                @click.stop="deleteStore(merchant)"
              >
                <image src="@img/chef/1278.png" class="w-64rpx h-64rpx" />
              </view>
            </view>

            <view class="cart-merchant-schedule-row">
              <view
                class="cart-date-pill"
                @click="onMerchantScheduleTap(merchant)"
              >
                <text>{{ getMerchantScheduleLabel(merchant.id) }}</text>
                <text class="cart-date-pill-arr">›</text>
              </view>
            </view>

            <!-- 商品行 -->
            <view
              v-for="(item, itemIndex) in merchant.merchantCartVoList"
              :key="item.id"
              class="cart-line"
              :class="
                itemIndex < merchant.merchantCartVoList.length - 1
                  ? 'cart-line--border'
                  : ''
              "
            >
              <view class="mt-8rpx shrink-0" @click.stop>
                <wd-checkbox
                  v-model="item.checked"
                  shape="circle"
                  @change="toggleItemCheck(merchant, item)"
                />
              </view>
              <image
                :src="item.merchantDishVo?.dishImage?.split(',')[0]"
                mode="aspectFill"
                class="cart-line-img shrink-0"
              />
              <view class="cart-line-main flex-1 min-w-0">
                <text class="cart-line-title line-clamp-2">{{
                  item.merchantDishVo?.dishName
                }}</text>
                <view
                  v-if="item.sideDishList?.length > 0"
                  class="cart-line-side flex flex-wrap"
                >
                  <view
                    v-for="(dish, dIdx) in item.sideDishList"
                    :key="dIdx"
                    class="flex flex-wrap items-center"
                  >
                    <text class="mr-6rpx">{{
                      dish?.merchantSideDishItemVo?.name || ""
                    }}</text>
                    <!-- <text
                      v-if="dish?.merchantSideDishItemVo?.price"
                      class="text-[#00A76D] mr-10rpx"
                      >${{ dish?.merchantSideDishItemVo?.price }}</text
                    > -->
                  </view>
                </view>
                <view class="cart-line-price-row">
                  <view class="flex items-baseline flex-wrap">
                    <text class="cart-line-price"
                      >${{ getCartLineDisplayPrice(item) }}</text
                    >
                    <text
                      v-if="showCartLineOriginalPrice(item)"
                      class="cart-line-price-old"
                      >${{ item?.originalPrice }}</text
                    >
                  </view>
                  <wd-input-number
                    v-model="item.count"
                    long-press
                    :min="0"
                    :step="1"
                    :input-width="72"
                    button-size="52"
                    custom-class="cart-qty-input"
                    @change="(e) => handleQuantityChange(e, item)"
                  />
                </view>
              </view>
            </view>
          </view>

          <!-- 为您推荐 -->
          <view v-if="recommendDishes.length > 0" class="cart-recommend">
            <view class="cart-recommend-pill">{{
              t("pages-user.cart.forYou")
            }}</view>
            <view class="cart-recommend-grid">
              <view
                v-for="dish in recommendDishes"
                :key="dish.id"
                class="cart-rec-card"
                @click="openRecommendDish(dish)"
              >
                <image
                  class="cart-rec-img"
                  :src="dish.dishImage?.split(',')[0] || dish.dishImage"
                  mode="aspectFill"
                />
                <text class="cart-rec-name line-clamp-2">{{
                  dish.dishName
                }}</text>
                <view class="cart-rec-bottom">
                  <text class="cart-rec-price"
                    >${{ dish.discountPrice }}</text
                  >
                  <view
                    class="cart-rec-plus"
                    :class="{
                      'cart-rec-plus--busy': addingRecommendId === dish.id,
                    }"
                    @click.stop="addRecommendToCart(dish)"
                    >+</view
                  >
                </view>
              </view>
            </view>
          </view>
        </template>

        <template v-else>
          <view class="flex flex-col items-center justify-center py-100rpx">
            <image src="@img/chef/100.png" class="w-318rpx h-318rpx" />
            <text class="text-32rpx text-[#7D7D7D]">{{ t("pages-user.cart.emptyTitle") }}</text>
            <view
              class="mt-60rpx w-400rpx h-88rpx rounded-16rpx bg-[#14181B] flex items-center justify-center"
              @click="goToHome"
            >
              <text class="text-30rpx text-white">{{ t("pages-user.cart.emptyAction") }}</text>
            </view>
          </view>
        </template>
      </view>
    </view>
  </z-paging>

  <view
    v-if="dataList.length > 0 && !isMultiStoreNoticeOpen"
    class="cart-footer-wrap"
  >
    <view class="cart-footer-float">
      <view class="cart-footer-inner">
        <view class="cart-footer-sub">
          <text class="cart-footer-sub-label">{{
            t("pages-user.cart.subtotalLine")
          }}</text>
          <text class="cart-footer-sub-colon">: </text>
          <text class="cart-footer-sub-num"
            >${{ priceData.totalPayPrice ?? "0.00" }}</text
          >
        </view>
        <view
          class="cart-footer-btn"
          :class="
            selectedCount > 0 && !calculatingPrice
              ? 'cart-footer-btn--on'
              : 'cart-footer-btn--off'
          "
          @click="batchCheckout"
        >
          <text
            class="cart-footer-btn-text"
            :class="
              selectedCount > 0 && !calculatingPrice
                ? 'text-white'
                : 'text-[#999]'
            "
          >
            <template v-if="calculatingPrice">{{
              t("pages-user.cart.calculating")
            }}</template>
            <template v-else>{{ t("common.goSettle") }}</template>
          </text>
        </view>
      </view>
    </view>
  </view>

  <remove-cart @confirm="confirmRemove" ref="removeCartRef" />
  <remove-store
    ref="removeItemRef"
    @confirm="handleRemoveItem"
    @close="handleRemoveItemPopupClose"
  />
  <multi-store-notice
    v-model="isMultiStoreNoticeOpen"
    @confirm="confirmMultiStoreNotice"
    @close="handleMultiStoreNoticeClose"
  ></multi-store-notice>
  </view>
</template>

<style scoped lang="scss">
.cart-root {
  min-height: 100%;
}

.cart-page-body {
  min-height: 40vh;
}

.cart-store-card {
  background: #fff;
  border-radius: 24rpx;
  overflow: hidden;
  box-shadow: 0 8rpx 40rpx rgba(0, 0, 0, 0.06);
}

.cart-summary-only .cart-delivery-block {
  border-bottom: none;
  padding-bottom: 8rpx;
}

.cart-delivery-block {
  padding: 28rpx 28rpx 24rpx;
  border-bottom: 1rpx solid #f0f0f0;
}

.cart-delivery-title {
  display: block;
  font-size: 30rpx;
  font-weight: 600;
  color: #111;
}

.cart-delivery-sub {
  display: block;
  margin-top: 8rpx;
  font-size: 24rpx;
  color: #888;
  line-height: 1.4;
}

.cart-delivery-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20rpx;
}

.cart-date-pill {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  padding: 18rpx 28rpx;
  border-radius: 999rpx;
  background: #111;
  color: #fff;
  font-size: 26rpx;
  font-weight: 500;
}

.cart-date-pill-arr {
  margin-left: 8rpx;
  font-size: 28rpx;
  opacity: 0.9;
}

.cart-merchant-schedule-row {
  padding: 0 24rpx 20rpx;
}

.cart-price-summary {
  flex: 1;
  min-width: 0;
  align-content: center;
  text-align: right;
}

.cart-price-summary--full {
  width: 100%;
}

.cart-summary-line1 {
  display: block;
  font-size: 24rpx;
  color: #333;
  font-weight: 500;
  line-height: 1.4;
}

.cart-fee-line {
  margin-top: 8rpx;
  font-size: 22rpx;
  color: #666;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 8rpx;
}

.cart-fee-strike {
  text-decoration: line-through;
  color: #aaa;
}

.cart-fee-cur {
  color: #111;
  font-weight: 500;
}

.cart-fee-free {
  padding: 2rpx 12rpx;
  border-radius: 8rpx;
  background: rgba(245, 200, 80, 0.25);
  color: #b8860b;
  font-size: 22rpx;
  font-weight: 600;
}

.cart-store-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 24rpx 24rpx 12rpx;
}

.cart-store-name {
  font-size: 28rpx;
  font-weight: 600;
  color: #333;
}

.cart-icon-btn {
  width: 64rpx;
  height: 64rpx;
  border-radius: 50%;
  background: #f5f5f5;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.cart-line {
  display: flex;
  align-items: flex-start;
  padding: 24rpx 24rpx 28rpx;
  gap: 16rpx;
}

.cart-line--border {
  border-bottom: 1rpx solid #f2f2f2;
}

.cart-line-img {
  width: 136rpx;
  height: 136rpx;
  border-radius: 16rpx;
  background: #f5f5f5;
}

.cart-line-title {
  font-size: 28rpx;
  font-weight: 500;
  color: #333;
  line-height: 1.35;
}

.cart-line-side {
  margin-top: 10rpx;
  font-size: 22rpx;
  color: #7d7d7d;
  line-height: 1.35;
}

.cart-line-price-row {
  margin-top: 16rpx;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16rpx;
}

.cart-line-price {
  font-size: 30rpx;
  font-weight: 600;
  color: #111;
}

.cart-line-price-old {
  margin-left: 12rpx;
  font-size: 24rpx;
  color: #999;
  text-decoration: line-through;
}

:deep(.cart-qty-input.wd-input-number) {
  background: #f2f2f2;
  border-radius: 12rpx;
  padding: 4rpx 8rpx;
}

.cart-recommend {
  margin-top: 8rpx;
  padding-bottom: 16rpx;
}

.cart-recommend-pill {
  display: inline-block;
  padding: 12rpx 28rpx;
  border-radius: 999rpx;
  background: #eee;
  font-size: 26rpx;
  color: #333;
  font-weight: 500;
  margin-bottom: 24rpx;
}

.cart-recommend-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 20rpx;
}

.cart-rec-card {
  background: #fff;
  border-radius: 20rpx;
  overflow: hidden;
  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
}

.cart-rec-img {
  width: 100%;
  height: 220rpx;
  background: #f5f5f5;
}

.cart-rec-name {
  display: block;
  padding: 12rpx 16rpx 0;
  font-size: 26rpx;
  color: #333;
  min-height: 72rpx;
}

.cart-rec-bottom {
  padding: 12rpx 16rpx 16rpx;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.cart-rec-price {
  font-size: 28rpx;
  font-weight: 600;
  color: #e02020;
}

.cart-rec-plus {
  width: 48rpx;
  height: 48rpx;
  border-radius: 50%;
  background: #f2f2f2;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 32rpx;
  font-weight: 500;
  color: #333;
}

.cart-rec-plus--busy {
  opacity: 0.45;
  pointer-events: none;
}

/* 底部结算：居中悬浮胶囊，外轮廓圆角 68rpx（不占满屏宽） */
.cart-footer-wrap {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 998;
  display: flex;
  flex-direction: column;
  align-items: center;
  pointer-events: none;
  padding: 0 32rpx;
  padding-bottom: calc(20rpx + constant(safe-area-inset-bottom));
  padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
}

.cart-footer-float {
  pointer-events: auto;
  width: 100%;
  max-width: 686rpx;
  border-radius: 68rpx;
  padding: 20rpx 28rpx;
  background: rgba(255, 255, 255, 0.72);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.08);
}

.cart-footer-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24rpx;
}

.cart-footer-sub {
  flex: 1;
  min-width: 0;
  font-size: 28rpx;
  color: #111;
}

.cart-footer-sub-label {
  font-weight: 500;
}

.cart-footer-sub-num {
  font-size: 32rpx;
  font-weight: 700;
}

.cart-footer-btn {
  flex-shrink: 0;
  min-width: 220rpx;
  height: 88rpx;
  padding: 0 40rpx;
  border-radius: 999rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

.cart-footer-btn--on {
  background: #14181b;
}

.cart-footer-btn--off {
  background: #e8e8e8;
}

.cart-footer-btn-text {
  font-size: 30rpx;
  font-weight: 600;
}

.ellipsis {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

</style>
<style>
page {
  background-color: #f5f5f5;
}
</style>
