feat: 商品详情接口对接

This commit is contained in:
zoujing committed 2026-08-12 11:11:26 +08:00
1 parent 6c0384f60f
commit b066de2c8c
4 files changed
+139 -78

No files matched your search

@@ -1,10 +1,13 @@
<template>
<view class="flex h-full min-w-0 flex-col overflow-hidden bg-slate-100">
<scroll-view class="min-h-0 flex-1" scroll-y>
<view class="relative h-56 overflow-hidden bg-white">
<view v-if="productImages.length" class="relative h-56 overflow-hidden bg-white">
<image class="h-full w-full bg-slate-200" :src="productImages[0]" mode="aspectFill" />
<view class="absolute bottom-3 right-3 rounded-full bg-slate-950/65 px-2 py-1 text-[10px] text-white">1 / {{ productImages.length }}</view>
</view>
<view v-else class="flex h-56 items-center justify-center bg-slate-200">
<text class="text-[12px] text-slate-400">暂无商品图片</text>
</view>
<view v-if="productImages.length > 1" class="flex gap-2 overflow-hidden bg-white px-4 pb-3 pt-2">
<image
@@ -19,55 +22,29 @@
<view class="bg-white px-4 pb-4 pt-4">
<view class="flex flex-wrap gap-1.5">
<text v-for="highlight in product.highlights || []" :key="highlight" class="rounded bg-blue-50 px-1.5 py-0.5 text-[10px] font-semibold text-blue-600">{{ highlight }}</text>
<text v-if="!(product.highlights || []).length" class="text-[10px] text-slate-400">尚未配置销售标签</text>
</view>
<text class="mt-2 block text-lg font-bold leading-7 text-slate-950">{{ product.name }}</text>
<text class="mt-1 block text-xs leading-5 text-slate-500">{{ product.description || product.shortTitle || '暂无商品简介' }}</text>
<view v-if="product.ageRule" class="mt-3 rounded-xl border border-violet-100 bg-violet-50 p-3 text-[11px] leading-5 text-violet-700">
<text class="font-bold">年龄限制:{{ ageRuleText }}</text>
<text class="block">下单时仅对门票游客按使用日和身份证校验。</text>
<text v-if="product.fulfillmentLabel" class="rounded bg-blue-50 px-1.5 py-0.5 text-[10px] font-semibold text-blue-600">{{ product.fulfillmentLabel }}</text>
<text v-for="tag in product.tags || []" :key="tag" class="rounded bg-blue-50 px-1.5 py-0.5 text-[10px] font-semibold text-blue-600">{{ tag }}</text>
</view>
<text class="mt-2 block text-lg font-bold leading-7 text-slate-950">{{ product.commodityName }}</text>
<text v-if="product.subtitle" class="mt-1 block text-xs leading-5 text-slate-500">{{ product.subtitle }}</text>
<view class="mt-3 flex items-end gap-1 text-orange-600">
<text v-if="Number(product.salePrice) === 0" class="text-2xl font-bold leading-none">免费预约</text>
<text v-if="Number(product.startingPrice) === 0" class="text-2xl font-bold leading-none">免费预约</text>
<template v-else>
<text class="pb-0.5 text-xs">¥</text>
<text class="text-3xl font-bold leading-none">{{ product.salePrice }}</text>
<text class="pb-0.5 text-[10px] text-slate-400"></text>
<text class="pb-0.5 text-xs">{{ currencySymbol }}</text>
<text class="text-3xl font-bold leading-none">{{ product.startingPrice }}</text>
<text class="pb-0.5 text-[10px] text-slate-400">起</text>
</template>
</view>
</view>
<view class="mx-[12px] mt-[12px] rounded-[16px] bg-white p-[16px]">
<text class="block text-[16px] font-semibold text-slate-800">费用包含</text>
<text v-for="item in product.included || []" :key="item" class="mt-[10px] block text-[14px] leading-[20px] text-slate-600">· {{ item }}</text>
<text v-if="!(product.included || []).length" class="mt-[10px] block text-[14px] text-slate-400">以商品说明为准</text>
</view>
<view class="mx-[12px] mt-[12px] rounded-[16px] bg-white p-[16px]">
<text class="block text-[16px] font-semibold text-slate-800">费用不含</text>
<text v-for="item in product.excluded || []" :key="item" class="mt-[10px] block text-[14px] leading-[20px] text-slate-600">· {{ item }}</text>
<text v-if="!(product.excluded || []).length" class="mt-[10px] block text-[14px] text-slate-400">无</text>
</view>
<view class="mx-[12px] mt-[12px] rounded-[16px] bg-white p-[16px]">
<text class="block text-[16px] font-semibold text-slate-800">商品详情</text>
<text class="mt-[10px] block text-[14px] leading-[22px] text-slate-600">{{ product.detail || product.description || '暂无商品详情' }}</text>
</view>
<view class="mx-[12px] mt-[12px] rounded-[16px] bg-white p-[16px]">
<text class="block text-[16px] font-semibold text-slate-800">购买须知</text>
<text class="mt-[10px] block text-[14px] leading-[22px] text-slate-600">{{ product.purchaseNotice || '请以实际预约规则为准。' }}</text>
</view>
<view class="mx-[12px] mt-[12px] rounded-[16px] bg-white p-[16px]">
<text class="block text-[16px] font-semibold text-slate-800">使用说明</text>
<text class="mt-[10px] block text-[14px] leading-[22px] text-slate-600">{{ product.useRule || '请按预约日期和场次使用,具体以出票凭证及现场指引为准。' }}</text>
</view>
<view class="mx-[12px] mb-[20px] mt-[12px] rounded-[16px] bg-white p-[16px]">
<text class="block text-[16px] font-semibold text-slate-800">退款说明</text>
<text class="mt-[10px] block text-[14px] leading-[22px] text-slate-600">{{ product.refundRule || '未使用且符合商品退改规则时可申请退款,最终结果以订单审核为准。' }}</text>
<view
v-for="section in detailSections"
:key="section.sectionCode"
class="mx-[12px] mt-[12px] rounded-[16px] bg-white p-[16px]"
:class="section === detailSections[detailSections.length - 1] ? 'mb-[20px]' : ''"
>
<text class="block text-[16px] font-semibold text-slate-800">{{ section.title }}</text>
<text class="mt-[10px] block whitespace-pre-wrap text-[14px] leading-[22px] text-slate-600">{{ section.content }}</text>
</view>
</scroll-view>
@@ -75,10 +52,10 @@
<view class="flex items-center justify-between">
<view>
<text class="text-[12px] text-slate-500">优惠价</text>
<text class="ml-[4px] text-[14px] font-medium text-[#f26b32]">¥<text class="text-[25px] font-bold">{{ product.salePrice }}</text>起</text>
<text class="ml-[4px] text-[14px] font-medium text-[#f26b32]">{{ currencySymbol }}<text class="text-[25px] font-bold">{{ product.startingPrice }}</text>起</text>
</view>
<view class="rounded-full bg-[#f26b32] px-[28px] py-[11px]" @click="emit('buy', product.id)">
<text class="text-[15px] font-semibold text-white">去预订</text>
<view class="rounded-full bg-[#f26b32] px-[28px] py-[11px]" @click="emit('buy', product.commodityId)">
<text class="text-[15px] font-semibold text-white">{{ product.bookingButtonText || '去预订' }}</text>
</view>
</view>
</view>
@@ -94,11 +71,14 @@ const props = defineProps({
const emit = defineEmits(["buy"]);
const productImages = computed(() =>
(props.product.images || []).filter(Boolean)
[props.product.coverImage, ...(props.product.imageList || [])].filter(Boolean)
);
const detailSections = computed(() =>
[...(props.product.detailSections || [])].sort(
(left, right) => Number(left.sortOrder || 0) - Number(right.sortOrder || 0)
)
);
const currencySymbol = computed(() =>
props.product.currency === "CNY" ? "¥" : props.product.currency || ""
);
const ageRuleText = computed(() => {
const { min, max } = props.product.ageRule || {};
if (Number(max) >= 999) return `${min}周岁及以上`;
return `${min}—${max}周岁`;
});
</script>