index.vue 10.1 KB
<!--
 * @Date: 2025-06-28 10:33:00
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2025-07-15 15:45:21
 * @FilePath: /jgdl/src/pages/index/index.vue
 * @Description: 捡个电驴首页
-->
<template>
  <view class="flex flex-col bg-orange-50 min-h-screen" style="background-color: #f6f6f6;">
    <!-- Header -->
    <nut-sticky>
      <view class="bg-orange-400 p-4 pt-4 pb-4">
        <nut-row type="flex" justify="center" align="center">
          <nut-col span="6">
            <view class="text-xl font-bold text-white">捡个电驴</view>
          </nut-col>
          <nut-col span="18">
            <!-- Search Bar -->
            <nut-searchbar v-model="searchValue" placeholder="搜索更多商品" :disabled="true" @click-input="onSearchHandle"
              shape="round" background="transparent" input-background="#ffffff">
              <template #leftin>
                <Search2 />
              </template>
            </nut-searchbar>
          </nut-col>
        </nut-row>
      </view>
    </nut-sticky>

    <!-- Banner -->
    <view class="px-4 pt-4" style="background: linear-gradient( 180deg, #fb923c 0%, rgba(255,203,53,0) 61%);">
      <nut-swiper :init-page="0" :pagination-visible="true" pagination-color="#ffffff" auto-play="3000"
        class="rounded-lg overflow-hidden" height="160">
        <nut-swiper-item v-for="(image, index) in bannerImages" :key="index">
          <image :src="image" mode="aspectFill" class="w-full h-40 object-cover" />
        </nut-swiper-item>
      </nut-swiper>
    </view>

    <!-- Category Icons -->
    <view class="px-4 mt-2">
      <view class="flex justify-around py-4">
        <view class="flex flex-col items-center" @tap="onNewCarClick">
          <view class="w-12 h-12 rounded-full bg-orange-100 flex items-center justify-center"
            style="background: linear-gradient( 135deg, rgba(255,161,53,0.15) 0%, rgba(235,83,5,0.25) 100%); box-shadow: 0px 2px 8px 0px rgba(235,83,5,0.15);">
            <Clock size="20" color="#f97316" />
          </view>
          <text class="text-xs mt-1 text-gray-700">最新上架</text>
        </view>
        <view class="flex flex-col items-center" @tap="onGoodCarClick">
          <view class="w-12 h-12 rounded-full bg-orange-100 flex items-center justify-center"
            style="background: linear-gradient( 135deg, rgba(255,161,53,0.15) 0%, rgba(235,83,5,0.25) 100%); box-shadow: 0px 2px 8px 0px rgba(235,83,5,0.15);">
            <Star size="20" color="#f97316" />
          </view>
          <text class="text-xs mt-1 text-gray-700">特价好车</text>
        </view>
        <view class="flex flex-col items-center" @tap="onCertifiedClick">
          <view class="w-12 h-12 rounded-full bg-orange-100 flex items-center justify-center"
            style="background: linear-gradient( 135deg, rgba(255,161,53,0.15) 0%, rgba(235,83,5,0.25) 100%); box-shadow: 0px 2px 8px 0px rgba(235,83,5,0.15);">
            <Shop size="20" color="#f97316" />
          </view>
          <text class="text-xs mt-1 text-gray-700">认证车源</text>
        </view>
      </view>
    </view>

    <!-- Featured Recommendations -->
    <FeaturedRecommendations />

    <!-- Latest Listings -->
    <view class="px-4 mt-6 mb-20">
      <view class="flex justify-between items-center mb-2">
        <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="flex flex-col">
        <view v-for="scooter in latestScooters" :key="scooter.id"
          class="bg-white rounded-lg shadow-sm overflow-hidden mb-3" @tap="() => onProductClick(scooter)">
          <view class="flex min-h-32">
            <view class="w-32 relative p-2 flex flex-col">
              <image :src="scooter.front_photo" :alt="scooter.name" 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>

    <!-- 自定义TabBar -->
    <TabBar />

    <!-- 搜索弹窗 -->
    <SearchPopup v-model:visible="showSearchPopup" />
  </view>
</template>

<script setup>
import Taro, { useShareAppMessage, useDidShow, useReady } from '@tarojs/taro'
import '@tarojs/taro/html5.css' //和 nutui组件居然有冲突?
import { ref, onMounted } from 'vue'
import { Clock, Star, RectRight, Check, Search2, Shop, Heart1, HeartFill } from '@nutui/icons-vue-taro'
import TabBar from '@/components/TabBar.vue'
import SearchPopup from '@/components/SearchPopup.vue'
import FeaturedRecommendations from '@/components/FeaturedRecommendations.vue'
import "./index.less";
// 导入接口
import { getRecommendVehicleAPI, getVehicleListAPI } from '@/api/car';
import { useFavorite } from '@/composables/useFavorite'
import { DEFAULT_COVER_IMG } from '@/utils/config'
// 响应式数据
const searchValue = ref('')
// favoriteIds 已移除,现在使用基于对象属性的收藏模式
const showSearchPopup = ref(false)

const onSearchHandle = () => {
  // 显示搜索弹窗
  showSearchPopup.value = true
}

// Banner图片
const bannerImages = ref([])

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

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

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

/**
 * 点击认证车源
 */
const onCertifiedClick = () => {
  Taro.navigateTo({
    url: '/pages/authCar/index'
  })
}

/**
 * 点击特价好车
 */
const onGoodCarClick = () => {
  Taro.navigateTo({
    url: '/pages/goodCarList/index'
  })
}

const onNewCarClick = () => {
  Taro.navigateTo({
    url: '/pages/newCarList/index'
  })
}

// 生命周期钩子
useDidShow(() => {
  console.warn('index onShow')
})

useReady(async () => {
  // 版本更新检查
  if (!wx.canIUse("getUpdateManager")) {
    wx.showModal({
      title: "提示",
      content: "当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试",
      showCancel: false,
    });
    return;
  }

  // https://developers.weixin.qq.com/miniprogram/dev/api/base/update/UpdateManager.html
  const updateManager = wx.getUpdateManager();

  updateManager.onCheckForUpdate((res) => {
    // 请求完新版本信息的回调
    if (res.hasUpdate) {
      updateManager.onUpdateReady(function () {
        Taro.showModal({
          title: "更新提示",
          content: "新版本已经准备好,是否重启应用?",
          success: function (res) {
            if (res.confirm) {
              // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
              updateManager.applyUpdate();
            }
          },
        });
      });

      updateManager.onUpdateFailed(function () {
        // 新版本下载失败
        Taro.showModal({
          title: "更新提示",
          content: "新版本已上线,请删除当前小程序,重新搜索打开",
        });
      });
    }
  });
})

onMounted(async () => {
  // 获取首页轮播
  const res1 = await getRecommendVehicleAPI({ section: 1 })
  if (res1.code) {
    bannerImages.value = res1.data.list.map(item => item.front_photo);
  }
  // 获取最新上架
  const res3 = await getVehicleListAPI({ page: 0, limit: 5 })
  if (res3.code) {
    latestScooters.value = res3.data.list
    // 处理图片数据
    const processedData = res3.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,
    }))
    latestScooters.value = processedData
  }
})

// 分享功能
wx.showShareMenu({
  withShareTicket: true,
  menus: ['shareAppMessage', 'shareTimeline']
});

// 分享功能
useShareAppMessage(() => {
  // 设置菜单中的转发按钮触发转发事件时的转发内容
  var shareObj = {
    title: '捡个电驴-分享给好友',    // 默认是小程序的名称(可以写slogan等)
    path: `pages/index/index`,    // 默认是当前页面,必须是以'/'开头的完整路径
    imageUrl: '',   //自定义图片路径,可以是本地文件路径、代码包文件路径或者网络图片路径,支持PNG及JPG,不传入 imageUrl 则使用默认截图。显示图片长宽比是 5:4
  }
  // 来自页面内的按钮的转发
  // if (options.from == 'button') {
  //   var eData = options.target.dataset;
  //   // 此处可以修改 shareObj 中的内容
  //   shareObj.path = '/pages/goods/goods?goodId=' + eData.id;
  // }
  // 返回shareObj
  return shareObj;
})
</script>