index.vue 13.3 KB
<!--
 * @Date: 2025-06-28 10:33:00
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2025-07-01 21:50:54
 * @FilePath: /jgdl/src/pages/index/index.vue
 * @Description: 捡个电驴首页
-->
<template>
  <view class="flex flex-col bg-orange-50 min-h-screen">
    <!-- Header -->
    <view class="bg-orange-400 p-4 pt-8 pb-6">
      <view class="text-2xl font-bold text-white mb-3">捡个电驴</view>
      <!-- Search Bar -->
      <nut-searchbar v-model="searchValue" placeholder="搜索品牌型号" shape="round" background="transparent"
        input-background="#ffffff">
        <template #leftin>
          <Search2 />
        </template>
      </nut-searchbar>
    </view>

    <!-- Banner -->
    <view class="px-4 mt-4">
      <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">
          <view class="w-12 h-12 rounded-full bg-orange-100 flex items-center justify-center">
            <Clock size="20" color="#f97316" />
          </view>
          <text class="text-xs mt-1 text-gray-700">最新上架</text>
        </view>
        <view class="flex flex-col items-center">
          <view class="w-12 h-12 rounded-full bg-orange-100 flex items-center justify-center">
            <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">
            <Shop size="20" color="#f97316" />
          </view>
          <text class="text-xs mt-1 text-gray-700">认证车源</text>
        </view>
      </view>
    </view>

    <!-- Featured Recommendations -->
    <view class="px-4 mt-4">
      <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">
          <text>更多</text>
          <Right size="16" />
        </view>
      </view>
      <view class="grid grid-cols-2 gap-3">
        <view v-for="scooter in featuredScooters" :key="scooter.id"
          class="bg-white rounded-lg shadow-sm overflow-hidden" @tap="() => onProductClick(scooter)">
          <view class="relative p-2">
            <image :src="scooter.imageUrl" :alt="scooter.name" mode="aspectFill"
              class="w-full h-36 object-cover rounded-lg" />
            <view class="absolute top-4 right-4 p-1" @tap.stop="() => toggleFavorite(scooter.id)">
              <Addfollow v-if="!favoriteIds.includes(scooter.id)" size="20" :color="'#ffffff'"/>
              <HeartFill v-else size="20" :color="'#ef4444'" />
            </view>
            <view v-if="scooter.isVerified"
              class="absolute bottom-4 right-4 bg-orange-500 text-white text-xs px-1.5 py-0.5 rounded flex items-center">
              <Check size="12" color="#ffffff" class="mr-0.5" />
              <text class="text-white">认证</text>
            </view>
          </view>
          <view class="p-2">
            <text class="font-medium text-sm block">{{ scooter.name }}</text>
            <text class="text-xs text-gray-500 block">
              {{ scooter.year }} ·
              <text v-if="scooter.batteryHealth">电池健康度{{ scooter.batteryHealth }}%</text>
              <text v-if="scooter.mileage"> 行驶{{ scooter.mileage }}公里</text>
            </text>
            <view class="mt-1">
              <text class="text-orange-500 font-bold">
                ¥{{ scooter.price.toLocaleString() }}
              </text>
              <text v-if="scooter.isVerified" class="ml-2 text-xs px-1 py-0.5 bg-orange-100 text-orange-500 rounded">
                低于市场价10%
              </text>
              <text class="text-xs text-gray-500 mt-1 block">{{ scooter.school }}</text>
            </view>
          </view>
        </view>
      </view>
    </view>

    <!-- 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">
          <text>更多</text>
          <Right size="16" />
        </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">
            <view class="w-32 h-24 relative p-2">
              <image :src="scooter.imageUrl" :alt="scooter.name" mode="aspectFill"
                class="w-full h-full object-cover rounded-lg" />
              <view v-if="scooter.isVerified"
                class="absolute bottom-3 right-3 bg-orange-500 text-white text-xs px-1 rounded flex items-center">
                <Check size="12" color="#ffffff" class="mr-0.5" />
                <text class="text-white">认证</text>
              </view>
            </view>
            <view class="flex-1 p-3 relative">
              <view class="absolute top-2 right-2" @tap.stop="() => toggleFavorite(scooter.id)">
                <Addfollow v-if="!favoriteIds.includes(scooter.id)" size="16" :color="'#ffffff'"/>
                <HeartFill v-else size="16" :color="'#ef4444'" />
              </view>
              <text class="font-medium text-sm block">{{ scooter.name }}</text>
              <text class="text-xs text-gray-600 mt-1 block">
                {{ scooter.year }} ·
                <text v-if="scooter.batteryHealth">电池健康度{{ scooter.batteryHealth }}%</text>
                <text v-if="scooter.mileage"> 行驶{{ scooter.mileage }}公里</text>
              </text>
              <view class="mt-2">
                <text class="text-orange-500 font-bold">
                  ¥{{ scooter.price.toLocaleString() }}
                </text>
                <text class="text-xs text-gray-500 mt-1 block">{{ scooter.school }}</text>
              </view>
            </view>
          </view>
        </view>
      </view>
    </view>
    
    <!-- 自定义TabBar -->
    <TabBar />
  </view>
</template>

<script setup>
import Taro from '@tarojs/taro'
// import '@tarojs/taro/html.css' 和 nutui组件居然有冲突?
import { ref, onMounted } from 'vue'
import { useDidShow, useReady } from '@tarojs/taro'
import { Clock, Star, Right, Addfollow, HeartFill, Check, Search2, Shop } from '@nutui/icons-vue-taro'
import TabBar from '@/components/TabBar.vue'
import "./index.less";

// 响应式数据
const searchValue = ref('')
const favoriteIds = ref([])

// Banner图片
const bannerImages = ref([
  'https://images.unsplash.com/photo-1558981806-ec527fa84c39?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
  'https://images.unsplash.com/photo-1558981285-6f0c94958bb6?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
  'https://images.unsplash.com/photo-1558981403-c5f9899a28bc?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60'
])

// 精品推荐数据
const featuredScooters = ref([
  {
    id: '1',
    name: '小龟电动车',
    year: '2023',
    school: '上海理工大学',
    price: 3880,
    imageUrl: 'https://images.unsplash.com/photo-1558981285-6f0c94958bb6?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
    isVerified: true
  },
  {
    id: '2',
    name: '立马电动车',
    year: '2022',
    school: '上海复旦大学',
    price: 2999,
    imageUrl: 'https://images.unsplash.com/photo-1558981403-c5f9899a28bc?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
    isVerified: true
  },
  {
    id: '3',
    name: '雅迪电动车',
    year: '2023',
    school: '上海理工大学',
    price: 4299,
    imageUrl: 'https://images.unsplash.com/photo-1591637333184-19aa84b3e01f?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60'
  },
  {
    id: '4',
    name: '爱玛电动车',
    year: '2022',
    school: '上海复旦大学',
    price: 3599,
    imageUrl: 'https://images.unsplash.com/photo-1558980664-3a031cf67ea8?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
    isVerified: true
  }
])

// 最新上架数据
const latestScooters = ref([
  {
    id: '5',
    name: '雅迪 豪华版',
    year: '2023',
    school: '上海理工大学',
    price: 3200,
    imageUrl: 'https://images.unsplash.com/photo-1558981403-c5f9899a28bc?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
    isVerified: true,
    batteryHealth: 98,
    mileage: 1200
  },
  {
    id: '6',
    name: '台铃 战速',
    year: '2022',
    school: '上海理工大学',
    price: 3800,
    imageUrl: 'https://images.unsplash.com/photo-1558981285-6f0c94958bb6?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
    batteryHealth: 92,
    mileage: 2500
  },
  {
    id: '7',
    name: '小鸟电车',
    year: '2023',
    school: '上海复旦大学',
    price: 3100,
    imageUrl: 'https://images.unsplash.com/photo-1558980664-3a031cf67ea8?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
    batteryHealth: 92,
    mileage: 2000
  },
  {
    id: '8',
    name: '新日电动车',
    year: '2024',
    school: '上海同济大学',
    price: 6700,
    imageUrl: 'https://images.unsplash.com/photo-1595941069915-4ebc5197c14a?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
    isVerified: true,
    batteryHealth: 96,
    mileage: 500
  }
])

/**
 * 切换收藏状态
 * @param {string} scooterId - 电动车ID
 */
const toggleFavorite = (scooterId) => {
  const index = favoriteIds.value.indexOf(scooterId)
  if (index > -1) {
    favoriteIds.value.splice(index, 1)
  } else {
    favoriteIds.value.push(scooterId)
  }
}

/**
 * 点击产品卡片
 * @param {Object} scooter - 电动车信息
 */
const onProductClick = (scooter) => {
  Taro.showToast({
    title: `查看${scooter.name}`,
    icon: 'none'
  })
}

/**
 * 点击认证车源
 */
const onCertifiedClick = () => {
  Taro.showToast({
    title: '查看认证车源',
    icon: 'none'
  })
}

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

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

  // https://developers.weixin.qq.com/miniprogram/dev/api/base/update/UpdateManager.html
  const updateManager = Taro.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(() => {
  console.warn('index mounted')
})

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

<script>
import { getCurrentPageParam } from "@/utils/weapp";

export default {
  name: "indexPage",
  onHide() {
    console.warn('index onHide')
  },
  onShareAppMessage() {
    let params = getCurrentPageParam();
    // 设置菜单中的转发按钮触发转发事件时的转发内容
    var shareObj = {
      title: "xxx",    // 默认是小程序的名称(可以写slogan等)
      path: `pages/detail/index?id=${params.id}&start_date=${params.start_date}&end_date=${params.end_date}&room_type=${params.room_type}`,    // 默认是当前页面,必须是以'/'开头的完整路径
      imageUrl: '',   //自定义图片路径,可以是本地文件路径、代码包文件路径或者网络图片路径,支持PNG及JPG,不传入 imageUrl 则使用默认截图。显示图片长宽比是 5:4
      success: function (res) {
        // 转发成功之后的回调
        if (res.errMsg == 'shareAppMessage:ok') {
          //
        }
      },
      fail: function () {
        // 转发失败之后的回调
        if (res.errMsg == 'shareAppMessage:fail cancel') {
          // 用户取消转发
        } else if (res.errMsg == 'shareAppMessage:fail') {
          // 转发失败,其中 detail message 为详细失败信息
        }
      },
      complete: function () {
        // 转发结束之后的回调(转发成不成功都会执行)
      }
    }
    // 来自页面内的按钮的转发
    // if (options.from == 'button') {
    //   var eData = options.target.dataset;
    //   // 此处可以修改 shareObj 中的内容
    //   shareObj.path = '/pages/goods/goods?goodId=' + eData.id;
    // }
    // 返回shareObj
    return shareObj;
  }
};
</script>