<script setup lang="ts">
import {
  appMerchantDishDetailGet,
  appCollectCollectPost,
  type MerchantDishVo,
  appMerchantCartAddCartPost,
  type MerchantCartVo,
  appMerchantCartListByMerchantIdPost,
  appMerchantDetailMerchantIdGet,
  getDishListByCategoryId,
  appMerchantDishNearbyListPost,
  type MerchantVo
} from "@/service";
import { debounce } from 'throttle-debounce'
const { t } = useI18n();
import Config from "@/config";
import {useConfigStore, useUserStore} from "@/store";
import DishesSkeleton from "./components/dishes-skeleton.vue";
import {CollectionType} from "@/constant/enums";
import { formatDeliveryScheduleDays } from "@/utils/deliverySchedule";
import { parseMerchantCartPayload } from "@/utils/utils";
const configStore = useConfigStore();
const userStore = useUserStore();

// 响应式数据
const loading = ref(true); // 加载状态
const selectedSauce = ref([]); // 默认选择第一个

/** 是否售罄：不调用 Number()，按原文/类型判断 */
function isSoldOutStock(stockLike: unknown) {
  if (stockLike == null || stockLike === "") return false;
  if (typeof stockLike === "number") return stockLike <= 0;
  const s = String(stockLike).trim();
  if (s === "") return false;
  if (s.startsWith("-") && !/^-0+\.?0*$/.test(s)) return true;
  if (s === "0" || /^0+\.?0*$/.test(s)) return true;
  return false;
}

function isSideDishRequired(item: { isRequired?: unknown }) {
  return String(item.isRequired) === "1";
}

/** 是否有「非零」展示意义的价格/数值（字符串形态，不用 Number） */
function isNonZeroScalar(val: unknown) {
  if (val == null || val === "") return false;
  const s = String(val).trim();
  if (s === "") return false;
  if (s === "0" || /^0+\.?0*$/.test(s)) return false;
  return true;
}

function showSoldOutToast() {
  uni.showToast({
    title: t('common.prompt.soldOut'),
    icon: 'none'
  })
}

const selectSauce = (item, id: unknown) => {
  if (!item.selectedIds) {
    item.selectedIds = [];
  }
  if (item.selectedIds.includes(id)) {
    item.selectedIds = item.selectedIds.filter((selectedId) => selectedId !== id);
  } else {
    item.selectedIds = [id];
  }
};

function handleSubmit() {
  const dish = dishDetailData.value;
  if (!dish?.id) {
    return;
  }
  if (isSoldOutStock(dish.stock)) {
    showSoldOutToast();
    return;
  }

  if (!storeId.value) {
    uni.showToast({
      title: t("common.prompt.request-failed-please-try-again-later"),
      icon: "none",
    });
    return;
  }

  // 无配菜时 backend 仍需要数组；原先未做空值保护会抛错导致整个提交被 catch 掉
  const sideList = dish.merchantSideDishVoList ?? [];

  for (const item of sideList) {
    if (isSideDishRequired(item)) {
      if (!item.selectedIds || item.selectedIds.length === 0) {
        uni.showToast({
          title: `${t("common.placeholder.pleaseSelect")} ${item.sideDishName}`,
          icon: "none",
        });
        return;
      }
    }
  }

  const merchantCartSideDishBoList = sideList
    .filter(
      (item) =>
        isSideDishRequired(item) ||
        (item.selectedIds && item.selectedIds.length > 0)
    )
    .map((item) => ({
      sideDishId: item.id,
      sideDishItemId: item.selectedIds![0],
    }));

  appMerchantCartAddCartPost({
    body: {
      merchantId: storeId.value,
      dishId: dish.id,
      count: 1,
      merchantCartSideDishBoList,
    } as any,
  }).then(() => {
    uni.showToast({
      title: t("toast.addCartSuccess"),
      icon: "none",
    });
    getCartInfo().then(() => loadRecommendDishes(true));
  });
}

const addCart = debounce(600, handleSubmit, {
  atBegin: true,
});

function onAddCartClick() {
  if (isSoldOut.value) {
    showSoldOutToast();
    return;
  }
  if (!userStore.isLogin) {
    uni.showToast({
      title: t("common.pleaseLogin"),
      icon: "none",
    });
    setTimeout(() => {
      uni.navigateTo({ url: Config.loginPath });
    }, 400);
    return;
  }
  if (hasSideDishes.value) {
    openSpecPopup();
  } else {
    addCart();
  }
}

onPageScroll((e) => {
  uni.$emit("page-scroll", e);
});

const dishImageList = computed(() => {
  const raw = dishDetailData.value?.dishImage;
  if (!raw) return [];
  return String(raw)
    .split(",")
    .map((s) => s.trim())
    .filter(Boolean);
});

function pad2(n: string | number) {
  const s = String(n);
  return s.length >= 2 ? s : `0${s}`;
}

function formatSalesCount(n?: unknown) {
  if (n == null || n === "") return t("pages-store.store.dishDetail.dash");
  return String(n);
}

function specFromWords(index: number) {
  const w = dishDetailData.value?.words;
  if (!w) return t("pages-store.store.dishDetail.dash");
  const parts = String(w)
    .split(/[,，]/)
    .map((s) => s.trim())
    .filter(Boolean);
  return parts[index] ?? t("pages-store.store.dishDetail.dash");
}

const specUnitDisplay = computed(() => {
  const weight = dishDetailData.value?.weight;
  if (weight != null && weight !== "") {
    return `${weight}${t("pages-store.store.dishDetail.lbUnit")}`;
  }
  return t("pages-store.store.dishDetail.dash");
});

function resolveCategoryNameByMenuId(menuId: unknown) {
  if (menuId == null || menuId === "") return "";
  const categories = (storeDetail.value as any)?.merchantCategoryIds;
  if (!Array.isArray(categories) || categories.length === 0) return "";
  const targetId = String(menuId);
  const hit = categories.find(
    (item: any) => item != null && String(item.id) === targetId
  );
  return hit?.menuName ? String(hit.menuName).trim() : "";
}

const specCategoryDisplay = computed(() => {
  const dish = dishDetailData.value as any;
  const categoryFromMenuId = resolveCategoryNameByMenuId(dish?.menuId);
  const category =
    categoryFromMenuId ||
    dish?.recipeCategoryName ||
    dish?.categoryName ||
    dish?.dishCategoryName ||
    dish?.recipeCategory ||
    "";
  const text = String(category).trim();
  return text || t("pages-store.store.dishDetail.dash");
});

function goStorePage() {
  if (!storeId.value) return;
  uni.navigateTo({
    url: `/pages-store/pages/store/index?id=${storeId.value}`,
  });
}

function showHotBadge() {
  return isNonZeroScalar(dishDetailData.value?.salesCount);
}

function navigateBack() {
  uni.navigateBack({
    delta: 1,
  });
}

const storeId = ref('')
onLoad((options: any) => {
  loading.value = true;
  if(options.id&&options.id!='undefined') {
    getDishDetail(options.id)
  }
  if(options.storeId&&options.storeId!='undefined') {
    storeId.value = options.storeId
    getCartInfo()
    getStoreDetail()
  }
})

onShow(() => {
  if (userStore.isLogin && storeId.value) {
    getCartInfo().then(() => loadRecommendDishes(true));
  }
})

// 获取菜品详情
const dishDetailData = ref<MerchantDishVo>(null);
function getDishDetail(id: string) {
  appMerchantDishDetailGet({
    params: {
      dishId: id
    }
  }).then((res: any)=> {
    console.log('获取菜品详情', res)
    dishDetailData.value = res.data;
  }).finally(()=> {
    loading.value = false;
  })
}

// 收藏菜品
function handleDishCollectionClick() {
  debouncedEmit(dishDetailData.value.isCollect, dishDetailData.value.id, CollectionType.DISH, ()=> {
    dishDetailData.value.isCollect = !dishDetailData.value.isCollect
  })
}
// 防抖处理函数
const debouncedEmit = debounce(1300, (isCollected: boolean, id: string, type: CollectionType, callback: ()=> void) => {
  // 收藏接口
  appCollectCollectPost({
    body: {
      targetId: id,
      targetType: type
    }
  }).then(res=> {
    callback()
  })
}, {
  atBegin: true, // 立即触发
})

function handleShare() {
  uni.shareWithSystem({
    summary: '',
    href: `${Config.shareLink}pages-store/pages/store/dishes?id=${dishDetailData.value.id}&storeId=${storeId.value}`,
    success(){
      // 分享完成，请注意此时不一定是成功分享
    },
    fail(){
      // 分享失败
    }
  })
}

const isSoldOut = computed(() => isSoldOutStock(dishDetailData.value?.stock))

const specPopupVisible = ref(false)

const hasSideDishes = computed(() => {
  const list = dishDetailData.value?.merchantSideDishVoList ?? [];
  return list.some(item => item.merchantSideDishItemVoList && item.merchantSideDishItemVoList.length > 0);
})

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

const memberPriceLabelText = computed(() =>
  t("pages-store.store.dishDetail.memberPriceLabel", {
    name: appDisplayName.value,
  })
);

function formatPriceText(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}`
    .replace(/\.00$/, "")
    .replace(/(\.\d)0$/, "$1");
}

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

function centStringToPriceText(centsLike: string) {
  const cents = centsLike.replace(/^0+(?=\d)/, "") || "0";
  if (cents === "0") return "0";
  const padded = cents.length >= 3 ? cents : cents.padStart(3, "0");
  const intPart = padded.slice(0, -2).replace(/^0+(?=\d)/, "") || "0";
  const decPart = padded.slice(-2);
  return `${intPart}.${decPart}`
    .replace(/\.00$/, "")
    .replace(/(\.\d)0$/, "$1");
}

const specPopupDisplayPrice = computed(() => {
  const dish = dishDetailData.value as any;
  const sideList = dish?.merchantSideDishVoList ?? [];
  for (const side of sideList) {
    const selectedId = side?.selectedIds?.[0];
    if (selectedId == null) continue;
    const selectedItem = (side?.merchantSideDishItemVoList ?? []).find(
      (opt: any) => String(opt?.id) === String(selectedId)
    );
    const selectedPriceCent = priceToCentString(selectedItem?.price);
    if (selectedPriceCent !== "0") {
      return centStringToPriceText(selectedPriceCent);
    }
  }

  const memberCent = priceToCentString(dish?.memberPrice);
  const baseCent =
    userStore.isMember && memberCent !== "0"
      ? memberCent
      : priceToCentString(dish?.discountPrice);
  return centStringToPriceText(baseCent);
});

const detailDisplayPrice = computed(() => {
  const dish = dishDetailData.value as any;
  // const firstSpecPrice = dish?.merchantSideDishVoList?.[0]?.merchantSideDishItemVoList?.[0]?.actualSalePrice;
  // if (firstSpecPrice != null && String(firstSpecPrice) !== "") {
  //   return firstSpecPrice;
  // }
  return dish?.actualSalePrice;
});

function openSpecPopup() {
  const sideList = dishDetailData.value?.merchantSideDishVoList ?? [];
  for (const item of sideList) {
    if (
      (!item.selectedIds || item.selectedIds.length === 0) &&
      item.merchantSideDishItemVoList &&
      item.merchantSideDishItemVoList.length > 0
    ) {
      item.selectedIds = [item.merchantSideDishItemVoList[0].id];
    }
  }
  specPopupVisible.value = true;
}

function onSpecConfirm() {
  specPopupVisible.value = false;
  addCart();
}

const cartDataList = ref<MerchantCartVo[]>([])
function getCartInfo() {
  if (!userStore.isLogin || !storeId.value) {
    cartDataList.value = []
    return Promise.resolve()
  }
  return appMerchantCartListByMerchantIdPost({
    params: {
      merchantId: storeId.value,
    }
  }).then((res: any)=> {
    console.log('购物车列表', res)
    cartDataList.value = parseMerchantCartPayload(res?.data).items as MerchantCartVo[]
  })
}

const recommendDishes = ref<MerchantDishVo[]>([]);
const addingRecommendId = ref<string | number | null>(null);

/** 推荐区分页，每页条数（接口分页） */
const RECOMMEND_PAGE_SIZE = 12;
/** 首屏自动预拉：至少凑满条数（仍不足则继续翻页直到无更多或达上限） */
const MIN_RECOMMEND_ITEMS = 6;
/** 首屏自动翻页最多请求页数，防止异常死循环 */
const MAX_AUTO_FETCH_PAGES = 5;
/** 下一次要请求的页码（与 pages-store/store/index 菜品列表分页一致） */
const recommendPageNum = ref(1);
const recommendHasMore = ref(false);
const recommendLoadingMore = ref(false);

/** 兼容接口返回 rows 在 data 内或顶层 */
function extractDishPageRows(res: unknown): MerchantDishVo[] {
  const r = res as Record<string, any> | null | undefined;
  if (!r) return [];
  if (Array.isArray(r.rows)) return r.rows as MerchantDishVo[];
  const d = r.data;
  if (Array.isArray(d)) return d as MerchantDishVo[];
  if (d && Array.isArray(d.rows)) return d.rows as MerchantDishVo[];
  return [];
}

function filterRecommendRows(
  rows: MerchantDishVo[],
  did: string | number,
  cartDishIds: Set<string>
) {
  return rows.filter(
    (r: MerchantDishVo) =>
      r && String(r.id) !== String(did) && !cartDishIds.has(String(r.id))
  );
}

/** 列表项去重：跨店时同一 dishId 可能出现在不同店，用 id+merchantId */
function recommendRowKey(r: MerchantDishVo) {
  return `${String(r.id)}_${String(r.merchantId ?? "")}`;
}

/**
 * 按菜谱分类跨店推荐（与 pages-store/pages/list 一致）；无分类 id 时回退附近菜品
 */
async function fetchRecommendPage(pageToFetch: number): Promise<MerchantDishVo[]> {
  const dish = dishDetailData.value as any;
  const rid =
    dish?.recipeCategoryId != null && String(dish.recipeCategoryId).trim() !== ""
      ? String(dish.recipeCategoryId)
      : "";
  if (rid) {
    const res: any = await getDishListByCategoryId({
      params: {
        pageNum: pageToFetch,
        pageSize: RECOMMEND_PAGE_SIZE,
        recipeCategoryId: rid,
      },
      body: {
        pageNum: pageToFetch,
        pageSize: RECOMMEND_PAGE_SIZE,
        lat: userStore.userLocation.latitude || undefined,
        lng: userStore.userLocation.longitude || undefined,
        recipeCategoryId: rid,
        merchantCategoryIds: [],
        merchantLabelIds: [],
      },
    });
    return extractDishPageRows(res);
  }
  const res: any = await appMerchantDishNearbyListPost({
    params: {
      pageNum: pageToFetch,
      pageSize: RECOMMEND_PAGE_SIZE,
    },
    body: {
      lat: userStore.userLocation.latitude,
      lng: userStore.userLocation.longitude,
    } as any,
  });
  return extractDishPageRows(res);
}

/**
 * @param reset 为 true 时从第一页重新拉取（切换菜品、购物车变化等）
 */
async function loadRecommendDishes(reset = true) {
  const did = dishDetailData.value?.id;
  if (!did) {
    recommendDishes.value = [];
    recommendHasMore.value = false;
    recommendPageNum.value = 1;
    return;
  }
  if (reset) {
    recommendPageNum.value = 1;
    recommendDishes.value = [];
    recommendHasMore.value = false;
  }
  const cartDishIds = new Set<string>();
  cartDataList.value.forEach((it: MerchantCartVo) => {
    if (it.dishId != null) cartDishIds.add(String(it.dishId));
  });
  try {
    const existing = new Set(recommendDishes.value.map((d) => recommendRowKey(d)));
    const toAdd: MerchantDishVo[] = [];
    let pageToFetch = reset ? 1 : recommendPageNum.value;
    let lastGotFullPage = false;
    // reset：自动连续请求第 2、3…页，直到凑够 MIN、或本页非满页、或达到页数上限
    // !reset：仅请求一页（用户点击「加载更多」）
    while (true) {
      const pageRows = await fetchRecommendPage(pageToFetch);
      lastGotFullPage = pageRows.length >= RECOMMEND_PAGE_SIZE;
      const filtered = filterRecommendRows(pageRows, did, cartDishIds);
      for (const r of filtered) {
        if (!r?.id || existing.has(recommendRowKey(r))) continue;
        existing.add(recommendRowKey(r));
        toAdd.push(r);
      }
      if (!reset) {
        break;
      }
      const reachedPageCap = pageToFetch >= MAX_AUTO_FETCH_PAGES;
      if (
        !lastGotFullPage ||
        toAdd.length >= MIN_RECOMMEND_ITEMS ||
        reachedPageCap
      ) {
        break;
      }
      pageToFetch += 1;
    }
    if (toAdd.length > 0) {
      if (reset) {
        recommendDishes.value = toAdd;
      } else {
        recommendDishes.value = [...recommendDishes.value, ...toAdd];
      }
    } else if (reset) {
      recommendDishes.value = [];
    }
    recommendHasMore.value = lastGotFullPage;
    recommendPageNum.value = lastGotFullPage ? pageToFetch + 1 : pageToFetch;
  } catch (e) {
    console.error(e);
    if (reset) recommendDishes.value = [];
    recommendHasMore.value = false;
  }
}

async function loadMoreRecommendDishes() {
  if (!recommendHasMore.value || recommendLoadingMore.value) return;
  recommendLoadingMore.value = true;
  try {
    await loadRecommendDishes(false);
  } finally {
    recommendLoadingMore.value = false;
  }
}

/** 页面滚动触底：与「加载更多」按钮同逻辑，避免只能点按钮才能翻页 */
onReachBottom(() => {
  if (!recommendHasMore.value || recommendLoadingMore.value) return;
  loadMoreRecommendDishes();
});

watch(
  () =>
    [
      dishDetailData.value?.id,
      (dishDetailData.value as any)?.recipeCategoryId,
      storeId.value,
      cartDataList.value.length,
    ] as const,
  () => {
    loadRecommendDishes(true);
  },
  { immediate: true }
);

function openDish(item: MerchantDishVo) {
  if (!item?.id) return;
  const sid = item.merchantId ?? storeId.value;
  if (!sid) return;
  uni.navigateTo({
    url: `/pages-store/pages/store/dishes?id=${item.id}&storeId=${sid}`,
  });
}

function recommendAddKey(dish: MerchantDishVo) {
  return `${String(dish.id)}_${String(dish.merchantId ?? "")}`;
}

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

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

function navigateToCart() {
  uni.navigateTo({
    url: "/pages-user/pages/cart/index",
  });
}

// 获取商家详情信息
const storeDetail = ref<MerchantVo>({})
const deliveryScheduleDaysText = computed(() => {
  const days = formatDeliveryScheduleDays(
    storeDetail.value?.deliveryScheduleTimes,
    (key) => t(key)
  );
  return days ? t("pages-store.store.deliveryScheduleDays", { days }) : "";
});
function getStoreDetail() {
  appMerchantDetailMerchantIdGet({
    params: {
      merchantId: storeId.value,
    }
  }).then((res: any) => {
    console.log('商家详情', res)
    storeDetail.value = res.data as MerchantVo
  }).catch((error) => {
    console.error('获取商家详情失败:', error);
  })
}
</script>

<template>
  <view class="dish-page-root">
  <view
      class="animate-in fade-in animate-ease-out animate-duration-300"
      v-show="loading"
  >
    <!-- 骨架屏 -->
    <DishesSkeleton />
  </view>
  <view
      class="animate-in fade-in animate-ease-in animate-duration-300"
      v-show="!loading"
  >
    <!-- 页面内容 -->
    <view class="dish-detail">
      <!-- 顶栏：返回、标题、购物车 -->
      <view class="detail-nav">
        <status-bar />
        <view class="detail-nav__row">
          <view class="nav-icon-btn" @click="navigateBack">
            <image
              src="@img/chef/1327.png"
              mode="aspectFit"
              class="nav-icon-btn__img"
            />
          </view>
          <view class="detail-nav__titles">
            <text class="detail-nav__title ellipsis">{{ dishDetailData?.dishName || "" }}</text>
            <text class="detail-nav__sub ellipsis">{{
              dishDetailData?.dishDescription || ""
            }}</text>
          </view>
          <view class="nav-icon-btn nav-icon-btn--cart" @click="navigateToCart">
            <view class="i-carbon:shopping-cart text-36rpx text-#14181b"></view>
            <view
              v-if="userStore.isLogin && cartDataList.length > 0"
              class="dish-nav-cart-badge"
              >{{ cartDataList.length > 99 ? "99+" : cartDataList.length }}</view
            >
          </view>
        </view>
      </view>

      <!-- 轮播图 -->
      <view class="dish-swiper-box">
        <wd-swiper
          custom-class="dish-swiper-inner"
          height="520rpx"
          :list="dishImageList"
          :autoplay="dishImageList.length > 1"
          :loop="dishImageList.length > 1"
          image-mode="aspectFill"
          :indicator="true"
        >
          <template #indicator="{ current, total }">
            <view v-if="total > 0" class="dish-swiper-ind">
              <text class="dish-swiper-ind__text"
                >{{ pad2(current + 1) }} / {{ pad2(total) }}</text
              >
              <view class="dish-swiper-ind__track">
                <view
                  class="dish-swiper-ind__fill"
                  :style="{
                    width: `${Math.min(100, ((current + 1) / total) * 100)}%`,
                  }"
                />
              </view>
            </view>
          </template>
        </wd-swiper>
        <view class="swiper-float-actions">
          <view class="swiper-fab" @click="handleShare">
            <image
              src="@img-store/1336.png"
              mode="aspectFit"
              class="swiper-fab__icon"
            />
          </view>
          <view class="swiper-fab" @click.stop="handleDishCollectionClick">
            <image
              v-if="!dishDetailData?.isCollect"
              src="@img-store/1334.png"
              mode="aspectFit"
              class="swiper-fab__icon"
            />
            <image
              v-else
              src="@img-store/1337.png"
              mode="aspectFit"
              class="swiper-fab__icon"
            />
          </view>
        </view>
      </view>

      <!-- 菜品信息 -->
      <view class="dish-info">
        <view class="price-block">
          <view class="price-block__row">
            <view class="price-current" v-if="dishDetailData?.originalPrice != null && dishDetailData?.originalPrice !== ''">
              <text class="price-current__sym">$</text>
              <text class="price-current__num">{{
                dishDetailData?.originalPrice
              }}</text>
            </view>
            <text
              v-if="
                dishDetailData?.discountPrice != null &&
                dishDetailData?.discountPrice !== ''
              "
              class="price-original"
              >${{ dishDetailData?.discountPrice }}</text
            >
          </view>
          <view
            v-if="isNonZeroScalar(dishDetailData?.memberPrice)"
            class="price-member"
          >
            <text class="price-member__icon">◆</text>
            <text class="price-member__txt"
              >{{ memberPriceLabelText }}:
              ${{ dishDetailData?.memberPrice }}</text
            >
          </view>
        </view>

        <view v-if="showHotBadge()" class="hot-pill">
          <text class="hot-pill__fire">🔥</text>
          <text class="hot-pill__txt">{{
            t("pages-store.store.dishDetail.hotSalesBadge")
          }}</text>
          <text class="hot-pill__arr">›</text>
        </view>

        <view class="meta-tags">
          <view
            v-if="
              dishDetailData?.stock !== undefined &&
              dishDetailData?.stock !== null
            "
            class="meta-tag"
          >
            {{ t("pages-store.store.dishDetail.stockLabel") }} :
            {{ dishDetailData?.stock }}
          </view>
          <view class="meta-tag">
            {{ t("pages-store.store.dishDetail.weeklySales") }} :
            {{ formatSalesCount(dishDetailData?.salesCount) }}
          </view>
        </view>

        <view
          v-if="storeDetail?.merchantName"
          class="store-pill"
          @click="goStorePage"
        >
          <image
            src="/static/images/store1.svg"
            mode="aspectFit"
            class="store-pill__icon"
          />
          <text class="store-pill__name">{{ storeDetail.merchantName }}</text>
          <text class="store-pill__arr">›</text>
        </view>
        <view
          v-if="deliveryScheduleDaysText"
          class="delivery-schedule-hint"
        >{{ deliveryScheduleDaysText }}</view>

        <!-- 产品详情 -->
        <view class="section-title">{{
          t("pages-store.store.dishDetail.productDetails")
        }}</view>
        <text class="section-product-name">{{ dishDetailData?.dishName }}</text>
        <view class="spec-grid">
          <!-- <view class="spec-cell">
            <text class="spec-cell__val">{{ specFromWords(0) }}</text>
            <text class="spec-cell__lbl">{{
              t("pages-store.store.dishDetail.origin")
            }}</text>
          </view> -->
          <view class="spec-cell">
            <text class="spec-cell__val">{{ specUnitDisplay }}</text>
            <text class="spec-cell__lbl">{{
              t("pages-store.store.dishDetail.unitQty")
            }}</text>
          </view>
          <view class="spec-cell">
            <text class="spec-cell__val ellipsis">{{
              specCategoryDisplay
            }}</text>
            <text class="spec-cell__lbl">{{
              t("pages-store.store.dishDetail.category")
            }}</text>
          </view>
          <!-- <view class="spec-cell">
            <text class="spec-cell__val">{{ specFromWords(1) }}</text>
            <text class="spec-cell__lbl">{{
              t("pages-store.store.dishDetail.allergen")
            }}</text>
          </view> -->
        </view>

        <!-- 产品简介 -->
        <view
          v-if="dishDetailData?.dishDescription"
          class="section-title section-title--spaced"
          >{{ t("pages-store.store.dishDetail.productIntro") }}</view
        >
        <text
          v-if="dishDetailData?.dishDescription"
          class="intro-body"
          >{{ dishDetailData.dishDescription }}</text
        >
      </view>

      <view class="divider"></view>

      <!-- 为您推荐（底部区域，与购物车同款样式与加购；支持分页加载更多） -->
      <view
        v-if="recommendDishes.length > 0 || recommendHasMore"
        class="cart-recommend"
      >
        <view class="cart-recommend-pill">{{
          t("pages-user.cart.forYou")
        }}</view>
        <view v-if="recommendDishes.length > 0" class="cart-recommend-grid">
          <view
            v-for="(dish, ri) in recommendDishes"
            :key="`${dish.id}-${dish.merchantId ?? ''}-${ri}`"
            class="cart-rec-card"
            @click="openDish(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 === recommendAddKey(dish),
                }"
                @click.stop="addRecommendToCart(dish)"
                >+</view
              >
            </view>
          </view>
        </view>
        <view
          v-if="recommendHasMore"
          class="cart-recommend-more"
          :class="{ 'cart-recommend-more--disabled': recommendLoadingMore }"
          @click="loadMoreRecommendDishes"
        >
          <text class="cart-recommend-more__text">{{
            recommendLoadingMore
              ? t("common.loading")
              : t("pages-store.store.dishDetail.loadMoreRecommend")
          }}</text>
        </view>
      </view>

      <view class="bottom-spacer-fab" :style="[configStore.iosSafeBottomPlaceholder]" />

      <view class="fab-add-cart-wrap">
        <view
          class="fab-add-cart"
          :class="{ 'fab-add-cart--disabled': isSoldOut }"
          @click="onAddCartClick"
        >
          <text class="fab-add-cart__text">{{
            t("pages-store.store.addToCart")
          }}</text>
        </view>
      </view>
    </view>

    <!-- 规格选择弹窗 -->
    <wd-popup
      v-model="specPopupVisible"
      position="bottom"
      custom-style="border-radius: 24rpx 24rpx 0 0;"
      @close="specPopupVisible = false"
    >
      <view class="spec-popup">
        <!-- 头部：菜品信息 -->
        <view class="spec-popup__header">
          <image
            class="spec-popup__img"
            :src="dishImageList[0] || ''"
            mode="aspectFill"
          />
          <view class="spec-popup__info">
            <text class="spec-popup__name ellipsis">{{ dishDetailData?.dishName }}</text>
            <view class="spec-popup__price">
              <text class="spec-popup__sym">$</text>
              <text class="spec-popup__num">{{ specPopupDisplayPrice }}</text>
            </view>
          </view>
          <view class="spec-popup__close" @click="specPopupVisible = false">
            <text class="spec-popup__close-icon">×</text>
          </view>
        </view>

        <!-- 规格选项列表 -->
        <scroll-view scroll-y class="spec-popup__body">
          <template
            v-for="item in dishDetailData?.merchantSideDishVoList"
            :key="item.id"
          >
            <view
              v-if="item.merchantSideDishItemVoList && item.merchantSideDishItemVoList.length > 0"
              class="spec-popup__group"
            >
              <view class="spec-popup__group-header">
                <text class="spec-popup__group-title">{{ item.sideDishName }}</text>
                <!-- <view
                  v-if="isSideDishRequired(item)"
                  class="spec-popup__required-tag"
                  :class="{ 'spec-popup__required-tag--done': item.selectedIds && item.selectedIds.length > 0 }"
                >
                  <image
                    v-if="item.selectedIds && item.selectedIds.length > 0"
                    src="@img-store/1338.png"
                    mode="aspectFill"
                    class="w-24rpx h-24rpx shrink-0 mr-4rpx"
                  />
                  <text class="text-24rpx">{{ t('pages-store.store.required') }}</text>
                </view> -->
              </view>
              <view class="spec-popup__options">
                <view
                  v-for="sauce in item.merchantSideDishItemVoList"
                  :key="sauce.id"
                  class="spec-popup__option"
                  :class="{ 'spec-popup__option--active': item.selectedIds && item.selectedIds.includes(sauce.id) }"
                  @click="selectSauce(item, sauce.id)"
                >
                  <text class="spec-popup__option-name">{{ sauce.name }}</text>
                </view>
              </view>
            </view>
          </template>
        </scroll-view>

        <!-- 底部确认按钮 -->
        <view class="spec-popup__footer">
          <view class="spec-popup__confirm-btn" @click="onSpecConfirm">
            <text class="spec-popup__confirm-text">{{ t('pages-store.store.addToCart') }}</text>
          </view>
        </view>
        <view :style="[configStore.iosSafeBottomPlaceholder]" />
      </view>
    </wd-popup>

  </view>
  </view>
</template>
<style lang="scss" scoped>
.dish-page-root {
  width: 100%;
  min-height: 100vh;
}

.dish-detail {
  width: 100%;
  min-height: 100vh;
  background-color: #ffffff;
  padding-bottom: constant(safe-area-inset-bottom);
  padding-bottom: env(safe-area-inset-bottom);
}

.detail-nav {
  position: sticky;
  top: 0;
  z-index: 20;
  background: #fff;
}

.detail-nav__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12rpx 24rpx 16rpx;
  min-height: 88rpx;
  box-sizing: border-box;
}

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

.nav-icon-btn__img {
  width: 36rpx;
  height: 36rpx;
}

.nav-icon-btn--cart {
  position: relative;
}

.dish-nav-cart-badge {
  position: absolute;
  top: 2rpx;
  right: 2rpx;
  min-width: 28rpx;
  height: 28rpx;
  padding: 0 6rpx;
  font-size: 18rpx;
  line-height: 28rpx;
  font-weight: 600;
  color: #fff;
  text-align: center;
  background: #e23636;
  border-radius: 999rpx;
}

.detail-nav__titles {
  flex: 1;
  min-width: 0;
  padding: 0 20rpx;
  text-align: center;
}

.detail-nav__title {
  display: block;
  font-size: 30rpx;
  font-weight: 600;
  color: #000;
  line-height: 1.3;
}

.detail-nav__sub {
  display: block;
  margin-top: 6rpx;
  font-size: 22rpx;
  color: #888;
  line-height: 1.2;
}

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

.dish-swiper-box {
  position: relative;
  margin: 0 30rpx;
  margin-top: 8rpx;
}

:deep(.dish-swiper-inner.wd-swiper .wd-swiper__track) {
  border-radius: 24rpx !important;
  overflow: hidden;
}

:deep(.dish-swiper-inner .wd-swiper__item) {
  padding: 0 !important;
}

.dish-swiper-ind {
  position: absolute;
  left: 24rpx;
  bottom: 28rpx;
  z-index: 3;
  max-width: 70%;
}

.dish-swiper-ind__text {
  font-size: 24rpx;
  color: #fff;
  font-weight: 500;
  text-shadow: 0 1rpx 4rpx rgba(0, 0, 0, 0.35);
}

.dish-swiper-ind__track {
  margin-top: 10rpx;
  height: 4rpx;
  border-radius: 4rpx;
  background: rgba(255, 255, 255, 0.45);
  overflow: hidden;
}

.dish-swiper-ind__fill {
  height: 100%;
  border-radius: 4rpx;
  background: #fff;
  transition: width 0.25s ease;
}

.swiper-float-actions {
  position: absolute;
  right: 20rpx;
  bottom: 100rpx;
  z-index: 5;
  display: flex;
  flex-direction: column;
  gap: 20rpx;
}

.swiper-fab {
  width: 72rpx;
  height: 72rpx;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.88);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
}

.swiper-fab__icon {
  width: 40rpx;
  height: 40rpx;
}

.dish-info {
  padding: 28rpx 30rpx 24rpx;
  position: relative;
}

.price-block {
  margin-bottom: 16rpx;
}

.price-block__row {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 12rpx 16rpx;
}

.price-current {
  display: flex;
  align-items: baseline;
  color: #e02020;
  font-weight: 600;
}

.price-current__sym {
  font-size: 32rpx;
  margin-right: 4rpx;
}

.price-current__num {
  font-size: 48rpx;
  line-height: 1;
}

.price-original {
  font-size: 26rpx;
  color: #888;
  text-decoration: line-through;
}

.price-member {
  margin-top: 12rpx;
  display: flex;
  align-items: center;
  gap: 8rpx;
  font-size: 26rpx;
  color: #b8860b;
}

.price-member__icon {
  font-size: 22rpx;
}

.hot-pill {
  display: inline-flex;
  align-items: center;
  padding: 8rpx 18rpx;
  border-radius: 999rpx;
  background: rgba(224, 32, 32, 0.1);
  margin-bottom: 16rpx;
}

.hot-pill__fire {
  font-size: 24rpx;
  margin-right: 6rpx;
}

.hot-pill__txt {
  font-size: 24rpx;
  color: #e02020;
  font-weight: 500;
}

.hot-pill__arr {
  font-size: 28rpx;
  color: #e02020;
  margin-left: 4rpx;
  line-height: 1;
}

.meta-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 12rpx;
  margin-bottom: 16rpx;
  position: absolute;
  top: 28rpx;
  right: 30rpx;
}

.meta-tag {
  padding: 10rpx 20rpx;
  border-radius: 999rpx;
  background: #f2f2f2;
  font-size: 24rpx;
  color: #666;
}

.store-pill {
  display: flex;
  align-items: center;
  padding: 16rpx 20rpx;
  border-radius: 16rpx;
  background: #e6f0ff;
  margin-bottom: 28rpx;
}

.store-pill__icon {
  width: 36rpx;
  height: 36rpx;
  margin-right: 12rpx;
  flex-shrink: 0;
}

.store-pill__name {
  flex: 1;
  min-width: 0;
  font-size: 26rpx;
  color: #333;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.delivery-schedule-hint {
  margin-top: 16rpx;
  font-size: 24rpx;
  line-height: 32rpx;
  color: #00a76d;
}

.store-pill__arr {
  font-size: 32rpx;
  color: #5a8fd8;
  margin-left: 8rpx;
}

.section-title {
  font-size: 32rpx;
  font-weight: 600;
  color: #000;
  margin-bottom: 12rpx;
}

.section-title--spaced {
  margin-top: 32rpx;
}

.section-product-name {
  display: block;
  font-size: 26rpx;
  color: #888;
  margin-bottom: 16rpx;
  line-height: 1.4;
}

.spec-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0;
  border: 1rpx solid #e8e8e8;
  border-radius: 16rpx;
  overflow: hidden;
  background: #fafafa;
}

.spec-cell {
  padding: 20rpx 8rpx;
  text-align: center;
  border-right: 1rpx solid #e8e8e8;
  box-sizing: border-box;
}

.spec-cell:nth-child(4n) {
  border-right: none;
}

.spec-cell__val {
  display: block;
  font-size: 24rpx;
  color: #000;
  font-weight: 500;
  margin-bottom: 8rpx;
  line-height: 1.25;
}

.spec-cell__lbl {
  font-size: 20rpx;
  color: #888;
}

.intro-body {
  display: block;
  font-size: 28rpx;
  color: #333;
  line-height: 1.55;
}

.divider {
  height: 10rpx;
  background: #f6f6f6;
  margin: 0 0 8rpx;
}

/* 与 pages-user/pages/cart 为您推荐一致 */
.cart-recommend {
  margin-top: 8rpx;
  padding: 0 30rpx 32rpx;
}

.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;
}

.cart-recommend-more {
  margin-top: 8rpx;
  padding: 20rpx 0 8rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

.cart-recommend-more__text {
  font-size: 28rpx;
  font-weight: 500;
  color: #14181b;
  padding: 16rpx 48rpx;
  border-radius: 999rpx;
  border: 1rpx solid #ddd;
  background: #fff;
}

.cart-recommend-more--disabled .cart-recommend-more__text {
  opacity: 0.5;
}

.bottom-spacer-fab {
  height: 240rpx;
  padding-bottom: constant(safe-area-inset-bottom);
  padding-bottom: env(safe-area-inset-bottom);
}

/* 悬浮「加入购物车」：居中胶囊 + 毛玻璃 */
.fab-add-cart-wrap {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 50;
  display: flex;
  justify-content: center;
  pointer-events: none;
  padding-bottom: calc(20rpx + constant(safe-area-inset-bottom));
  padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
}

.fab-add-cart {
  pointer-events: auto;
  min-width: 62%;
  max-width: calc(100% - 96rpx);
  padding: 26rpx 56rpx;
  border-radius: 999rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.45);
  border: 1rpx solid rgba(255, 255, 255, 0.65);
  box-shadow:
    0 8rpx 32rpx rgba(0, 0, 0, 0.1),
    inset 0 1rpx 0 rgba(255, 255, 255, 0.75);
  backdrop-filter: blur(24px);
  -webkit-backdrop-filter: blur(24px);
}

.fab-add-cart--disabled {
  opacity: 0.5;
}

.fab-add-cart__text {
  font-size: 30rpx;
  font-weight: 600;
  color: #111;
  letter-spacing: 0.02em;
}

/* 规格选择弹窗 */
.spec-popup {
  background: #fff;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  overflow-x: hidden;
  width: 100%;
  box-sizing: border-box;
}

.spec-popup__header {
  display: flex;
  align-items: center;
  padding: 30rpx;
  border-bottom: 1rpx solid #f0f0f0;
  box-sizing: border-box;
}

.spec-popup__img {
  width: 140rpx;
  height: 140rpx;
  border-radius: 16rpx;
  flex-shrink: 0;
  background: #f5f5f5;
}

.spec-popup__info {
  flex: 1;
  min-width: 0;
  margin-left: 24rpx;
  margin-right: 16rpx;
}

.spec-popup__name {
  display: block;
  font-size: 30rpx;
  font-weight: 600;
  color: #333;
  line-height: 1.4;
  margin-bottom: 12rpx;
}

.spec-popup__price {
  display: flex;
  align-items: baseline;
  color: #e02020;
  font-weight: 600;
}

.spec-popup__sym {
  font-size: 26rpx;
  margin-right: 4rpx;
}

.spec-popup__num {
  font-size: 40rpx;
  line-height: 1;
}

.spec-popup__close {
  width: 56rpx;
  height: 56rpx;
  border-radius: 50%;
  background: #f2f2f2;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.spec-popup__close-icon {
  font-size: 36rpx;
  color: #999;
  line-height: 1;
}

.spec-popup__body {
  flex: 1;
  max-height: 50vh;
  padding: 0 30rpx;
  overflow-x: hidden;
  box-sizing: border-box;
}

.spec-popup__group {
  padding: 28rpx 0;
  border-bottom: 1rpx solid #f5f5f5;
}

.spec-popup__group:last-child {
  border-bottom: none;
}

.spec-popup__group-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 20rpx;
}

.spec-popup__group-title {
  font-size: 30rpx;
  font-weight: 600;
  color: #333;
}

.spec-popup__required-tag {
  display: inline-flex;
  align-items: center;
  padding: 6rpx 16rpx;
  border-radius: 8rpx;
  background: #f2f2f2;
  color: #666;
}

.spec-popup__required-tag--done {
  background: #000;
  color: #fff;
  font-weight: 500;
}

.spec-popup__options {
  display: flex;
  flex-wrap: wrap;
  gap: 16rpx;
  overflow: hidden;
}

.spec-popup__option {
  display: flex;
  align-items: center;
  gap: 8rpx;
  padding: 16rpx 28rpx;
  border-radius: 20rpx;
  background: #fff;
  border: 2rpx solid #000;
  color: #333;
  font-weight: 500;
  transition: all 0.2s;
  max-width: 100%;
  box-sizing: border-box;
}

.spec-popup__option--active {
  background: #000;
  color: #fff;
  border-color: #000;
}

.spec-popup__option-name {
  font-size: 26rpx;
  color: inherit;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.spec-popup__option-price {
  font-size: 24rpx;
  color: inherit;
  flex-shrink: 0;
  white-space: nowrap;
  opacity: 0.7;
}

.spec-popup__footer {
  padding: 20rpx 30rpx;
}

.spec-popup__confirm-btn {
  width: 100%;
  height: 96rpx;
  border-radius: 48rpx;
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
}

.spec-popup__confirm-text {
  font-size: 32rpx;
  font-weight: 600;
  color: #fff;
  letter-spacing: 0.02em;
}

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