index.vue 8.65 KB
<!--
 * @Date: 2026-01-30
 * @Description: 产品详情页
-->
<template>
  <div class="min-h-screen bg-[#F9FAFB] pb-[calc(160rpx+env(safe-area-inset-bottom))]">
    <NavHeader title="产品详情" />

    <!-- Banner Image -->
    <div class="w-full h-[420rpx] relative">
      <img
        class="w-full h-full object-cover"
        :src="bannerImage"
        mode="aspectFill"
      />
      <div class="absolute top-[32rpx] right-[32rpx] flex items-center gap-[16rpx]">
        <!-- Hot Tag -->
        <div class="bg-red-500 text-white text-[24rpx] px-[20rpx] py-[10rpx] rounded-full shadow-sm backdrop-blur-sm bg-opacity-90">
          热卖
        </div>
      </div>
    </div>

    <!-- Product Header -->
    <div class="relative mt-[-40rpx] bg-white rounded-t-[40rpx] px-[40rpx] pt-[48rpx] pb-[40rpx] z-10">
      <div class="flex items-start justify-between mb-[24rpx]">
        <h1 class="text-[#1F2937] text-[44rpx] font-bold flex-1 mr-[24rpx] leading-[1.2]">终身寿险尊享版</h1>
        <!-- Favorite Button -->
        <div
          class="w-[72rpx] h-[72rpx] flex-shrink-0 flex items-center justify-center rounded-full bg-gray-50 active:scale-95 transition-transform"
          @tap="toggleCollect"
        >
          <IconFont
            :name="isCollected ? 'heart-fill' : 'heart'"
            size="24"
            :color="isCollected ? '#EF4444' : '#9CA3AF'"
          />
        </div>
      </div>

      <div class="flex flex-wrap gap-[16rpx]">
        <div class="px-[16rpx] py-[6rpx] bg-red-50 rounded-[8rpx]">
          <span class="text-red-600 text-[24rpx]">收益率3.5%</span>
        </div>
        <div class="px-[16rpx] py-[6rpx] bg-orange-50 rounded-[8rpx]">
          <span class="text-orange-600 text-[24rpx]">5年超值</span>
        </div>
        <div class="px-[16rpx] py-[6rpx] bg-green-50 rounded-[8rpx]">
          <span class="text-green-600 text-[24rpx]">保证收益万能</span>
        </div>
      </div>
    </div>

    <!-- Stats Grid -->
    <div class="px-[32rpx] mt-[24rpx]">
      <div class="grid grid-cols-2 gap-[24rpx]">
        <div
          v-for="(item, index) in stats"
          :key="index"
          class="bg-white rounded-[24rpx] p-[32rpx] border border-gray-100"
        >
          <div class="text-[#6B7280] text-[24rpx] mb-[12rpx]">{{ item.label }}</div>
          <div class="text-[#1F2937] text-[30rpx] font-medium">{{ item.value }}</div>
        </div>
      </div>
    </div>

    <!-- Product Features -->
    <div class="px-[32rpx] mt-[32rpx]">
      <div class="bg-white rounded-[32rpx] p-[40rpx]">
        <h2 class="text-[#1F2937] text-[32rpx] font-bold mb-[32rpx]">产品特色</h2>
        <div class="flex flex-col gap-[32rpx]">
          <div v-for="(feature, index) in features" :key="index" class="flex items-start">
            <div class="w-[48rpx] h-[48rpx] rounded-full bg-blue-50 flex items-center justify-center mr-[24rpx] flex-shrink-0">
              <IconFont name="Check" size="14" color="#2563EB" />
            </div>
            <div class="flex-1">
              <div class="text-[#1F2937] text-[28rpx] font-medium leading-[1.4]">{{ feature.title }}</div>
              <div class="text-[#6B7280] text-[24rpx] mt-[8rpx] leading-[1.4]">{{ feature.desc }}</div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- Attachments -->
    <div class="px-[32rpx] mt-[32rpx]">
      <div class="bg-white rounded-[32rpx] p-[40rpx]">
        <h2 class="text-[#1F2937] text-[32rpx] font-bold mb-[32rpx]">相关附件</h2>
        <div class="flex flex-col gap-[24rpx]">
          <div
            v-for="(file, index) in files"
            :key="index"
            class="flex flex-col p-[24rpx] bg-gray-50 rounded-[16rpx]"
          >
            <div class="flex items-center justify-between mb-[8rpx]">
              <div class="flex items-center flex-1 mr-[24rpx]">
                <image
                  :src="getDocumentIcon(file.fileName)"
                  class="w-[48rpx] h-[48rpx] mr-[24rpx]"
                  mode="aspectFit"
                />
                <div class="flex flex-col">
                  <span class="text-[#1F2937] text-[28rpx] font-medium mb-[4rpx] line-clamp-1">{{ file.name }}</span>
                  <span class="text-[#9CA3AF] text-[24rpx]">{{ file.size }}</span>
                </div>
              </div>
              <IconFont name="eye" size="20" color="#2563EB" @tap="viewFile(file)" />
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- TabBar -->
    <!-- <TabBar /> -->
  </div>
</template>

<script setup>
import { ref } from 'vue'
import NavHeader from '@/components/NavHeader.vue'
import TabBar from '@/components/TabBar.vue'
import IconFont from '@/components/IconFont.vue'
import { useFileOperation } from '@/composables/useFileOperation'
import Taro, { useLoad } from '@tarojs/taro'
import { getDocumentIcon } from '@/utils/documentIcons'

const { viewFile } = useFileOperation()

// 接收页面参数
const productId = ref(null)

useLoad((options) => {
  console.log('产品详情页参数:', options)

  if (options.id) {
    productId.value = options.id
    console.log('产品ID:', productId.value)

    // TODO: 根据 productId 获取产品详情数据
    // 这里可以调用 API 获取对应产品的数据
    fetchProductDetail(options.id)
  } else {
    console.warn('未接收到产品ID')
    Taro.showToast({
      title: '产品ID不存在',
      icon: 'none',
      duration: 2000
    })
  }
})

// 根据 ID 获取产品详情(模拟)
const fetchProductDetail = async (id) => {
  console.log('正在获取产品ID', id, '的详情...')

  // TODO: 实际调用 API
  // const res = await getProductDetailAPI({ i: id })
  // if (res.code === 1) {
  //   // 更新产品数据
  // }

  // 模拟根据不同ID显示不同产品
  const productNames = {
    '1': '家庭财富传承保障计划(分红)',
    '2': '儿童教育金储备方案(分红)'
  }

  if (productNames[id]) {
    console.log('产品名称:', productNames[id])
  }
}

// Random banner image
const bannerImage = `https://picsum.photos/seed/${Math.floor(Math.random() * 1000)}/750/420`

const stats = ref([
  { label: '投保年龄', value: '30天-70周岁' },
  { label: '保障期限', value: '终身' },
  { label: '缴费方式', value: '3/5/10年交' },
  { label: '起投金额', value: '10000元起' }
])

const features = ref([
  { title: '身故保险金', desc: '赔付100%基本保额,给家人留爱不留债' },
  { title: '全残保险金', desc: '赔付100%基本保额,生活有保障' },
  { title: '保费豁免', desc: '确诊重疾后免交剩余保费,保障继续有效' },
  { title: '保单贷款', desc: '最高可贷现金价值80%,资金周转灵活' }
])

const files = ref([
  {
    name: '产品条款.pdf',
    size: '2.3MB',
    fileName: '产品条款.pdf',
    downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/test.pdf',
    iconName: 'order',
    iconColor: '#EF4444',
    fileType: 'pdf',
    showTip: false,
    tipText: ''
  },
  {
    name: '投保须知.docx',
    size: '1.8MB',
    fileName: '投保须知.docx',
    downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/%E7%94%A8%E6%88%B7%E5%8D%8F%E8%AE%AE%E6%9C%80%E7%BB%88v3.1.docx',
    iconName: 'order',
    iconColor: '#2563EB',
    fileType: 'docx',
    showTip: true,
    tipText: 'Word 文档可能无法正常显示,建议打开后点击右上角"..."菜单选择"发送给朋友"保存到电脑查看'
  },
  {
    name: '健康告知.pptx',
    size: '3.2MB',
    fileName: '健康告知.pptx',
    downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/%E8%82%A1%E5%88%A4%E5%90%88%E5%8F%8B%E7%94%A8%E7%9F%A5%E8%AF%86%E8%AF%B4%E6%98%8E20240112110417414.pptx',
    iconName: 'order',
    iconColor: '#F59E0B',
    fileType: 'pptx',
    showTip: true,
    tipText: 'PPT 文档可能无法正常显示,建议打开后点击右上角"..."菜单选择"发送给朋友"保存到电脑查看'
  },
  {
    name: '保险责任说明.xlsx',
    size: '1.5MB',
    fileName: '保险责任说明.xlsx',
    downloadUrl: 'https://cdn.ipadbiz.cn/manulife/document/%E7%94%A8%E6%88%B7%E5%8D%8F%E8%AE%AE%E6%9C%80%E7%BB%88v3.1.docx',
    iconName: 'order',
    iconColor: '#10B981',
    fileType: 'xlsx',
    showTip: true,
    tipText: 'Excel 文档可能无法正常显示,建议打开后点击右上角"..."菜单选择"发送给朋友"保存到电脑查看'
  }
])

// 收藏状态
const isCollected = ref(false)

// 切换收藏状态
const toggleCollect = () => {
  isCollected.value = !isCollected.value
  if (isCollected.value) {
    Taro.showToast({
      title: '已收藏',
      icon: 'success'
    })
  } else {
    Taro.showToast({
      title: '已取消收藏',
      icon: 'none'
    })
  }
}
</script>