index.vue 14 KB
<!--
 * @Date: 2022-09-19 14:11:06
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2025-07-04 16:26:47
 * @FilePath: /jgdl/src/pages/recommendCarList/index.vue
 * @Description: 精品推荐页面
-->
<template>
    <view>
        <view class="flex flex-col bg-white min-h-screen">
            <!-- 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="搜索商品名称、品牌、型号" @blur="onBlurSearch" shape="round" background="transparent" input-background="#ffffff">
                                <template #leftin>
                                    <Search2 />
                                </template>
                            </nut-searchbar>
                        </nut-col>
                    </nut-row>
                </view>

                <!-- Filter options -->
                <nut-menu>
                    <nut-menu-item v-model="selectedBrand" :options="brandOptions" @change="onBrandChange" />
                    <nut-menu-item v-model="selectedYear" :options="yearOptions" @change="onYearChange" />
                    <nut-menu-item v-model="selectedSchool" :options="schoolOptions" @change="onSchoolChange" />
                </nut-menu>
            </nut-sticky>

            <!-- 精品推荐车辆列表 -->
            <view class="flex-1 p-4">
                <!-- 滚动列表 -->
                <scroll-view
                    class="recommend-car-list"
                    :style="scrollStyle"
                    :scroll-y="true"
                    @scrolltolower="loadMore"
                    @scroll="scroll"
                    :lower-threshold="50"
                    :enable-flex="false"
                >
                    <view class="space-y-4">
                        <view v-for="car in recommendCars" :key="car.id"
                            class="bg-white rounded-lg shadow-sm overflow-hidden mb-3"
                            @tap="() => onCarClick(car)"
                        >
                            <view class="flex">
                                <view class="w-32 h-24 relative p-2">
                                    <image :src="car.imageUrl" :alt="car.name" mode="aspectFill"
                                        class="w-full h-full object-cover rounded-lg" />
                                    <view v-if="car.isNew"
                                        class="absolute bottom-3 right-3 bg-red-500 text-white text-xs px-1 rounded flex items-center">
                                        <text class="text-white">新</text>
                                    </view>
                                </view>
                                <view class="flex-1 p-3 relative">
                                    <view class="absolute top-3 right-4" @tap.stop="() => toggleFavorite(car.id)">
                                        <Heart1 v-if="!favoriteIds.includes(car.id)" size="22" color="#9ca3af" />
                                        <HeartFill v-else size="22" color="#ef4444" />
                                    </view>
                                    <text class="font-medium text-sm block">{{ car.name }}</text>
                                    <text class="text-xs text-gray-600 mt-1 block">
                                        {{ car.year }} ·
                                        <text v-if="car.batteryHealth">电池健康度{{ car.batteryHealth }}%</text>
                                        <text v-if="car.mileage"> 行驶{{ car.mileage }}公里</text>
                                    </text>
                                    <view class="mt-2">
                                        <text class="text-orange-500 font-bold" style="font-size: 1.2rem;">
                                            ¥{{ car.price.toLocaleString() }}
                                        </text>
                                        <text class="text-xs text-gray-500 mt-1 block">{{ car.school }}</text>
                                    </view>
                                    <!-- 推荐理由 -->
                                    <view class="mt-1">
                                        <text class="text-xs text-green-600">{{ car.recommendReason }}</text>
                                    </view>
                                </view>
                            </view>
                        </view>
                    </view>

                    <!-- Loading indicator -->
                    <view v-if="loading" class="loading-container py-4 text-center">
                        <text class="loading-text text-gray-500">加载中...</text>
                    </view>

                    <!-- 没有更多数据 -->
                    <view v-if="!hasMore && recommendCars.length > 0" class="no-more-container py-4 text-center">
                        <text class="text-gray-400 text-sm">没有更多数据了</text>
                    </view>
                </scroll-view>
            </view>
        </view>

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

        <!-- 成功提示 -->
        <nut-toast
            v-model:visible="toastVisible"
            :msg="toastMessage"
            :type="toastType"
        />
    </view>
</template>

<script setup>
import Taro  from '@tarojs/taro'
import { ref, computed, onMounted } from 'vue'
import { Search2, Heart1, HeartFill } from '@nutui/icons-vue-taro'
import './index.less'

// 响应式数据
const searchValue = ref('')
const onBlurSearch = () => {
    console.warn(searchValue.value)
}
const favoriteIds = ref(['2', '4', '6'])

// Filter states - 使用NutUI Menu组件
const selectedBrand = ref('全部品牌')
const selectedYear = ref('出厂年份')
const selectedSchool = ref('所在学校')

// Menu选项数据
const brandOptions = ref([
    { text: '全部品牌', value: '全部品牌' },
    { text: '雅迪', value: '雅迪' },
    { text: '台铃', value: '台铃' },
    { text: '小鸟', value: '小鸟' },
    { text: '新日', value: '新日' },
    { text: '爱玛', value: '爱玛' },
    { text: '小牛', value: '小牛' }
])

const yearOptions = ref([
    { text: '出厂年份', value: '出厂年份' },
    { text: '2024年', value: '2024年' },
    { text: '2023年', value: '2023年' },
    { text: '2022年', value: '2022年' },
    { text: '2021年', value: '2021年' },
    { text: '2020年', value: '2020年' }
])

const schoolOptions = ref([
    { text: '所在学校', value: '所在学校' },
    { text: '上海理工大学', value: '上海理工大学' },
    { text: '上海复旦大学', value: '上海复旦大学' },
    { text: '上海同济大学', value: '上海同济大学' },
    { text: '上海交通大学', value: '上海交通大学' }
])

// 精品推荐车辆数据
const recommendCars = ref([
    {
        id: 1,
        name: '小牛NGT 电动车',
        year: '2024年',
        batteryHealth: 100,
        mileage: 0,
        price: 5200,
        school: '上海理工大学',
        imageUrl: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=300&fit=crop',
        recommendReason: '品质优选',
        isNew: true,
        brand: '小牛'
    },
    {
        id: 2,
        name: '雅迪 DE3 电动车',
        year: '2024年',
        batteryHealth: 98,
        mileage: 200,
        price: 4800,
        school: '上海大学',
        imageUrl: 'https://images.unsplash.com/photo-1571068316344-75bc76f77890?w=400&h=300&fit=crop',
        recommendReason: '热门推荐',
        isNew: true,
        brand: '雅迪'
    },
    {
        id: 3,
        name: '爱玛 A600 电动车',
        year: '2024年',
        batteryHealth: 95,
        mileage: 500,
        price: 3800,
        school: '华东理工大学',
        imageUrl: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=300&fit=crop',
        recommendReason: '性价比之选',
        isNew: true,
        brand: '爱玛'
    },
    {
        id: 4,
        name: '台铃 TDR-2024 电动车',
        year: '2024年',
        batteryHealth: 92,
        mileage: 800,
        price: 4200,
        school: '上海交通大学',
        imageUrl: 'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=300&fit=crop',
        recommendReason: '口碑好评',
        isNew: true,
        brand: '台铃'
    }
])

// 加载状态
const loading = ref(false)
const hasMore = ref(true)
const currentPage = ref(1)
const pageSize = ref(4)

// Toast提示
const toastVisible = ref(false)
const toastMessage = ref('')
const toastType = ref('success')

// 滚动样式 - 考虑navbar、filter和TabBar的高度
const scrollStyle = computed(() => {
    return {
        height: 'calc(100vh - 380rpx)' // 减去header、filter和TabBar的高度
    }
})

/**
 * 切换收藏状态
 * @param {string} carId - 车辆ID
 */
const toggleFavorite = (carId) => {
    const index = favoriteIds.value.indexOf(carId)
    if (index > -1) {
        favoriteIds.value.splice(index, 1)
        Taro.showToast({
            title: '取消收藏',
            icon: 'none',
            duration: 2000
        })
    } else {
        favoriteIds.value.push(carId)
        Taro.showToast({
            title: '收藏成功',
            icon: 'success',
            duration: 2000
        })
    }
}

/**
 * 点击车辆卡片
 * @param {Object} car - 车辆信息
 */
const onCarClick = (car) => {
    // TODO: 跳转到车辆详情页
    showToast(`查看${car.name}详情`, 'success')
}

// Menu组件事件处理方法
/**
 * 品牌选择变化事件
 * @param {string} value - 选中的品牌值
 */
const onBrandChange = (value) => {
    selectedBrand.value = value
    // 这里可以添加过滤逻辑
    filterCars()
}

/**
 * 年份选择变化事件
 * @param {string} value - 选中的年份值
 */
const onYearChange = (value) => {
    selectedYear.value = value
    // 这里可以添加过滤逻辑
    filterCars()
}

/**
 * 学校选择变化事件
 * @param {string} value - 选中的学校值
 */
const onSchoolChange = (value) => {
    selectedSchool.value = value
    // 这里可以添加过滤逻辑
    filterCars()
}

/**
 * 过滤车辆数据
 */
const filterCars = () => {
    // TODO: 实现过滤逻辑
    showToast('筛选条件已更新', 'success')
}

/**
 * 生成模拟车辆数据
 * @param {number} page - 页码
 * @param {number} size - 每页数量
 * @returns {Array} 车辆数据数组
 */
const generateMockData = (page, size) => {
    const brands = ['雅迪', '台铃', '小鸟', '新日', '爱玛', '小牛', '绿源', '立马']
    const schools = ['上海理工大学', '上海复旦大学', '上海同济大学', '上海交通大学', '华东师范大学', '上海大学']
    const years = ['2024年', '2023年', '2022年', '2021年', '2020年']
    const images = [
        'https://images.unsplash.com/photo-1567922045116-2a00fae2ed03?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
        'https://images.unsplash.com/photo-1573981368236-719bbb6f70f7?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
        'https://images.unsplash.com/photo-1583568671741-c70dafa8e8e7?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
        'https://images.unsplash.com/photo-1595941069915-4ebc5197c14a?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 recommendReasons = ['品质优选', '热门推荐', '性价比之选', '口碑好评', '精品车源', '优质推荐']

    const data = []
    for (let i = 0; i < size; i++) {
        const index = (page - 1) * size + i
        const brand = brands[Math.floor(Math.random() * brands.length)]
        const school = schools[Math.floor(Math.random() * schools.length)]
        const year = years[Math.floor(Math.random() * years.length)]
        const image = images[Math.floor(Math.random() * images.length)]
        const recommendReason = recommendReasons[Math.floor(Math.random() * recommendReasons.length)]

        data.push({
            id: `recommend_${index + 100}`,
            name: `${brand} ${['豪华版', '标准版', '运动版', '经典版'][Math.floor(Math.random() * 4)]}`,
            year: year,
            school: school,
            price: Math.floor(Math.random() * 3000) + 3000, // 精品推荐价格相对较高
            imageUrl: image,
            batteryHealth: Math.floor(Math.random() * 10) + 90, // 精品推荐电池健康度较高
            mileage: Math.floor(Math.random() * 1000), // 精品推荐里程较少
            brand: brand,
            recommendReason: recommendReason,
            isNew: Math.random() > 0.3 // 70%概率显示推荐标签
        })
    }
    return data
}

/**
 * 加载更多数据
 */
const loadMore = () => {
    if (loading.value || !hasMore.value) return

    loading.value = true

    // 模拟网络请求延迟
    setTimeout(() => {
        // 模拟最多加载5页数据
        if (currentPage.value >= 5) {
            hasMore.value = false
            loading.value = false
            return
        }

        currentPage.value++
        const recommendData = generateMockData(currentPage.value, pageSize.value)
        recommendCars.value.push(...recommendData)
        loading.value = false
    }, 1000 + Math.random() * 1000)
}

/**
 * 滚动事件处理
 */
const scroll = (e) => {
    // 可以在这里处理滚动事件
}

/**
 * 显示提示信息
 */
const showToast = (message, type = 'success') => {
    toastMessage.value = message
    toastType.value = type
    toastVisible.value = true
}

// 初始化
onMounted(() => {
    // 可以在这里加载初始数据
})
</script>

<script>
export default {
    name: 'RecommendCarListPage'
}
</script>