LatestScooters.vue 5.28 KB
<template>
  <view v-show="latestScooters.length" class="latest-scooters">
    <!-- 最新上架 -->
    <view class="px-4 py-3">
      <view class="flex items-center justify-between mb-3">
        <text class="text-lg font-medium">最新上架</text>
        <view class="text-sm text-gray-500 flex items-center" @tap="onNewCarClick">
          <text>更多</text>
          <RectRight size="12" />
        </view>
      </view>
      <view class="space-y-4">
        <view v-for="scooter in latestScooters" :key="scooter.id"
          class="bg-white rounded-lg shadow-sm overflow-hidden" @tap="() => onProductClick(scooter)">
          <view class="flex min-h-32">
            <view class="w-32 relative p-2 flex flex-col">
              <image :src="scooter.front_photo" mode="aspectFill"
                class="w-full flex-1 object-cover rounded-lg" />
              <view v-if="scooter.verification_status === 5"
                class="absolute bottom-3 right-3 text-white text-xs px-1 rounded flex items-center"
                style="background-color: #EB5305;">
                <Check size="12" color="#ffffff" class="mr-0.5" />
                <text class="text-white">认证</text>
              </view>
            </view>
            <view class="flex-1 p-3 relative flex flex-col">
              <view class="absolute top-2 right-2" @tap.stop="() => toggleFavorite(scooter)">
                <Heart1 v-if="!scooter.is_favorite" size="22" :color="'#9ca3af'" />
                <HeartFill v-else size="22" :color="'#ef4444'" />
              </view>
              <text class="font-medium text-sm block">{{ scooter.brand }} {{ scooter.model }}</text>
              <text class="text-xs text-gray-600 mt-1 block" style="word-break: break-all;">
                {{ scooter.manufacture_year }}年
                <text v-if="scooter.range_km">续航{{ scooter.range_km }}km</text>
                <text v-if="scooter.max_speed_kmh"> 最高时速{{ scooter.max_speed_kmh }}km/h</text>
              </text>
              <view class="mt-auto">
                <text class="text-orange-500 font-bold" style="font-size: 1.25rem;">
                  ¥{{ scooter.price.toLocaleString() }}
                </text>
                <text class="text-xs text-gray-500 mt-1 block">{{ scooter.school_name }}</text>
              </view>
            </view>
          </view>
        </view>
      </view>
    </view>
  </view>
</template>

<script setup>
import Taro from '@tarojs/taro'
import { ref, onMounted } from 'vue'
import { RectRight, Check, Heart1, HeartFill } from '@nutui/icons-vue-taro'
import { getVehicleListAPI } from '@/api/car'
import { useFavorite } from '@/composables/useFavorite'
import { DEFAULT_COVER_IMG } from '@/utils/config'

// 最新上架数据
const latestScooters = ref([])

// 使用收藏功能composables
const { toggleFavorite } = useFavorite()

/**
 * 点击产品卡片
 * @param {Object} scooter - 电动车信息
 */
const onProductClick = (scooter) => {
  Taro.navigateTo({
    url: `/pages/productDetail/index?id=${scooter.id}`
  })
}

/**
 * 点击查看更多
 */
const onNewCarClick = () => {
  Taro.navigateTo({
    url: '/pages/newCarList/index'
  })
}

/**
 * 加载最新上架数据
 * 循环查询直到获取到数据或确认没有数据为止
 */
const loadLatestData = async () => {
  try {
    let page = 0
    let hasData = false
    let allData = []
    const limit = 5

    // 循环查询直到获取到足够数据或没有更多数据
    while (!hasData && page < 10) { // 最多查询10页防止无限循环
      const res = await getVehicleListAPI({ page, limit })

      if (res.code && res.data && res.data.list && res.data.list.length > 0) {
        // 处理图片数据
        const processedData = res.data.list.map(item => ({
          ...item,
          front_photo: item.front_photo || DEFAULT_COVER_IMG,
          // 确保价格为数字类型
          price: Number(item.price) || 0,
          market_price: Number(item.market_price) || 0,
        }))

        allData = [...allData, ...processedData]

        // 如果已经获取到足够数据,停止查询
        if (allData.length >= limit) {
          hasData = true
          latestScooters.value = allData.slice(0, limit)
        } else if (res.data.list.length < limit) {
          // 如果返回的数据少于请求的数量,说明没有更多数据了
          hasData = true
          latestScooters.value = allData
        }
      } else {
        // 如果当前页没有数据,检查是否还有下一页
        if (res.data && res.data.total !== undefined) {
          const totalPages = Math.ceil(res.data.total / limit)
          if (page >= totalPages - 1) {
            // 已经是最后一页,停止查询
            break
          }
        } else {
          // 没有总数信息,如果连续没有数据就停止
          break
        }
      }

      page++
    }

    // 如果最终没有获取到任何数据
    if (allData.length === 0) {
      latestScooters.value = []
    }
  } catch (error) {
    console.error('加载最新上架数据失败:', error)
    latestScooters.value = []
  }
}

onMounted(() => {
  loadLatestData()
})

// 暴露刷新方法供父组件调用
defineExpose({
  refresh: loadLatestData
})
</script>

<style lang="less" scoped>
.latest-scooters {
  // 组件样式可以根据需要添加
}
</style>