index.vue 10.9 KB
<!--
 * @Date: 2022-09-19 14:11:06
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2025-07-03 13:25:46
 * @FilePath: /jgdl/src/pages/myAuthCar/index.vue
 * @Description: 我的认证车页面
-->
<template>
    <view class="flex flex-col bg-white min-h-screen">
        <!-- Auth Car List -->
        <view class="flex-1">
            <!-- 滚动列表 -->
            <scroll-view
                class="auth-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="item in authCars"
                        :key="item.id"
                        @tap="() => onItemClick(item)"
                        style="border-bottom: 1px solid #e5e7eb"
                    >
                        <view class="flex p-4">
                            <view class="w-24 h-24 relative">
                                <image
                                    :src="item.imageUrl"
                                    :alt="item.name"
                                    mode="aspectFill"
                                    class="w-full h-full object-cover rounded-lg"
                                />
                            </view>
                            <view class="flex-1 ml-4">
                                <text class="font-medium text-base block">{{ item.name }}</text>
                                <text class="text-sm text-gray-500 mt-1 block">{{ item.details }}</text>
                                <view class="mt-2 flex justify-between items-center">
                                    <view>
                                        <text class="text-orange-500 font-bold">
                                            ¥{{ item.price.toLocaleString() }}
                                        </text>
                                        <text class="text-gray-400 text-xs line-through ml-2">
                                            ¥{{ item.originalPrice.toLocaleString() }}
                                        </text>
                                    </view>
                                    <nut-button
                                        @click.stop="handleSellClick(item.id)"
                                        size="small"
                                        type="primary"
                                        class="px-3 py-1 rounded-full text-sm"
                                    >
                                        我要卖车
                                    </nut-button>
                                </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 && authCars.length > 0" class="no-more-container py-4 text-center">
                    <text class="text-gray-400 text-sm">没有更多数据了</text>
                </view>

                <!-- Empty State -->
                <view
                    v-if="authCars.length === 0 && !loading"
                    class="flex flex-col items-center justify-center h-64"
                >
                    <text class="text-gray-500">暂无认证车辆</text>
                </view>
            </scroll-view>
        </view>

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

<script setup>
import { ref, computed, onMounted } from 'vue'
import Taro from '@tarojs/taro'
import './index.less'

// ==================== API相关 ====================
/**
 * API服务 - 为真实API预留空间
 */
const apiService = {
    /**
     * 获取我的认证车列表
     * @param {number} page - 页码
     * @param {number} pageSize - 每页数量
     * @returns {Promise} API响应
     */
    async getAuthCarsList(page = 1, pageSize = 10) {
        // TODO: 替换为真实API调用
        // return await request.get('/api/auth-cars', { page, pageSize })

        // 模拟API延迟
        await new Promise(resolve => setTimeout(resolve, 800 + Math.random() * 400))

        // 模拟API响应数据
        return {
            code: 200,
            data: {
                list: generateMockData(page, pageSize),
                total: 50, // 模拟总数
                hasMore: page < 5 // 模拟是否还有更多数据
            },
            message: 'success'
        }
    }
}

// ==================== 响应式数据 ====================
/**
 * 我的认证车列表数据
 */
const authCars = ref([])

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

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

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

// ==================== 数据处理方法 ====================
/**
 * 生成模拟数据
 * @param {number} page - 页码
 * @param {number} size - 每页数量
 * @returns {Array} 模拟数据数组
 */
const generateMockData = (page, size) => {
    const brands = ['小牛', '雅迪', '绿源', '爱玛', '台铃', '新日', '立马', '小鸟']
    const models = ['豪华版', '标准版', '运动版', '经典版', '智能版', '动力版']
    const images = [
        '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',
        'https://images.unsplash.com/photo-1591637333184-19aa84b3e01f?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
        'https://images.unsplash.com/photo-1558980664-3a031cf67ea8?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=60',
        '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'
    ]

    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 model = models[Math.floor(Math.random() * models.length)]
        const image = images[Math.floor(Math.random() * images.length)]
        const originalPrice = Math.floor(Math.random() * 3000) + 3000
        const price = Math.floor(originalPrice * (0.7 + Math.random() * 0.2)) // 7-9折
        const usageTime = Math.floor(Math.random() * 24) + 1 // 1-24个月
        const range = Math.floor(Math.random() * 100) + 60 // 60-160km续航

        data.push({
            id: `auth_${index + 100}`,
            name: `${brand} ${model}`,
            details: `续航${range}km | 使用${usageTime}个月`,
            price: price,
            originalPrice: originalPrice,
            imageUrl: image,
            brand: brand,
            authTime: new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000).toISOString() // 最近30天内认证
        })
    }
    return data
}

/**
 * 初始化加载数据
 */
const initData = async () => {
    loading.value = true
    try {
        const response = await apiService.getAuthCarsList(1, pageSize.value)
        if (response.code === 200) {
            authCars.value = response.data.list
            hasMore.value = response.data.hasMore
            currentPage.value = 1
        } else {
            showToast('加载失败,请重试', 'error')
        }
    } catch (error) {
        console.error('加载我的认证车列表失败:', error)
        showToast('网络错误,请重试', 'error')
    } finally {
        loading.value = false
    }
}

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

    loading.value = true
    try {
        const nextPage = currentPage.value + 1
        const response = await apiService.getAuthCarsList(nextPage, pageSize.value)

        if (response.code === 200) {
            authCars.value.push(...response.data.list)
            hasMore.value = response.data.hasMore
            currentPage.value = nextPage
        } else {
            showToast('加载失败,请重试', 'error')
        }
    } catch (error) {
        console.error('加载更多数据失败:', error)
        showToast('网络错误,请重试', 'error')
    } finally {
        loading.value = false
    }
}

// ==================== 事件处理方法 ====================
/**
 * 点击车辆项目
 * @param {Object} item - 车辆信息
 */
const onItemClick = (item) => {
    // TODO: 跳转到车辆详情页
    Taro.navigateTo({
        url: `/pages/productDetail/index?id=${item.id}`
    })
}

/**
 * 处理我要卖车点击事件
 * @param {string} carId - 车辆ID
 */
const handleSellClick = (carId) => {
    Taro.navigateTo({
        url: `/pages/sell/index?id=${carId}&mode=edit`
    })
}

/**
 * 滚动事件处理
 */
const scroll = (e) => {
    // 可以在这里处理滚动事件,比如记录滚动位置
}

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

// ==================== 生命周期 ====================
onMounted(() => {
    initData()
})
</script>

<script>
export default {
    name: "MyAuthCarPage",
};
</script>

<style lang="less" scoped>
// 自定义样式
.object-cover {
    object-fit: cover;
}

.line-through {
    text-decoration: line-through;
}

.auth-car-list {
    .loading-container {
        display: flex;
        justify-content: center;
        align-items: center;

        .loading-text {
            font-size: 28rpx;
            color: #999;
        }
    }

    .no-more-container {
        display: flex;
        justify-content: center;
        align-items: center;
        padding: 32rpx 0;

        text {
            font-size: 24rpx;
            color: #ccc;
        }
    }
}

// 认证标签样式
.absolute {
    position: absolute;
}

.top-1 {
    top: 4rpx;
}

.right-1 {
    right: 4rpx;
}

.bg-green-500 {
    background-color: #10b981;
}

.text-white {
    color: white;
}

.text-xs {
    font-size: 20rpx;
}

.px-1 {
    padding-left: 4rpx;
    padding-right: 4rpx;
}

.rounded {
    border-radius: 8rpx;
}

.flex {
    display: flex;
}

.items-center {
    align-items: center;
}
</style>