index.vue 8.62 KB
<!--
 * @Date: 2022-09-19 14:11:06
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2025-07-03 14:06:14
 * @FilePath: /jgdl/src/pages/myCar/index.vue
 * @Description: 文件描述
-->
<template>
  <view class="my-car-page">
    <!-- 车辆列表 -->
    <view class="flex-1">
      <!-- 滚动列表 -->
      <scroll-view
        class="car-list"
        :style="scrollStyle"
        :scroll-y="true"
        @scrolltolower="loadMore"
        @scroll="scroll"
        :lower-threshold="50"
        :enable-flex="false"
      >
        <!-- 空状态 -->
        <view v-if="!loading && carList.length === 0" class="empty-state">
          <image src="/static/images/empty-car.png" class="empty-image" mode="aspectFit" />
          <text class="empty-text">暂无车源信息</text>
          <nut-button color="#f97316" size="small" @click="goToSell">发布车源</nut-button>
        </view>

        <!-- 车辆卡片列表 -->
        <view v-else class="space-y-4">
          <view v-for="car in carList" :key="car.id" class="car-card">
            <!-- 状态标识 -->
            <view class="status-badges">
              <view v-if="car.isAuthenticated" class="status-badge verified">
                <Check class="status-icon" />
                <text>已认证</text>
              </view>
              <view v-if="car.isOffline" class="status-badge offline">
                <Close class="status-icon" />
                <text>已下架</text>
              </view>
            </view>

            <!-- 车辆图片 -->
            <view class="car-image-container">
              <image :src="car.image" class="car-image" mode="aspectFill" />
            </view>

            <!-- 车辆信息 -->
            <view class="car-info">
              <view class="car-title">{{ car.brand }} {{ car.model }}</view>
              <view class="car-details">
                <text class="detail-item">{{ car.year }}</text>
                <text class="detail-item">{{ car.condition }}</text>
                <text class="detail-item">{{ car.mileage }}公里</text>
              </view>
              <view class="car-description">{{ car.description }}</view>
              <view class="price-section">
                <view class="current-price">¥{{ car.price }}</view>
                <view class="market-price">市场价 ¥{{ car.marketPrice }}</view>
              </view>
            </view>

            <!-- 操作按钮 -->
            <view class="action-buttons">
              <nut-button size="small" type="default" @click="editCar(car.id)">编辑</nut-button>
              <nut-button
                size="small"
                :type="car.isOffline ? 'success' : 'warning'"
                @click="toggleOffline(car)"
              >
                {{ car.isOffline ? '上架' : '下架' }}
              </nut-button>
              <nut-button
                v-if="!car.isAuthenticated"
                size="small"
                type="primary"
                @click="authCar(car.id)"
              >
                认证
              </nut-button>
            </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 && carList.length > 0" class="no-more-container py-4 text-center">
          <text class="text-gray-400 text-sm">没有更多数据了</text>
        </view>
      </scroll-view>
    </view>

    <!-- 下架确认弹窗 -->
    <nut-dialog
      v-model:visible="offlineDialogVisible"
      title="确认操作"
      :content="offlineDialogContent"
      @confirm="confirmOffline"
      @cancel="cancelOffline"
    />
  </view>
</template>

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

// 添加样式定义

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



// 页面状态
const loading = ref(false)
const hasMore = ref(true)
const currentPage = ref(1)
const pageSize = ref(10)

// 车辆列表数据
const carList = ref([])

// 下架确认弹窗
const offlineDialogVisible = ref(false)
const offlineDialogContent = ref('')
const currentOfflineCar = ref(null)



/**
 * 跳转到发布车源页面
 */
const goToSell = () => {
  Taro.navigateTo({
    url: '/pages/sell/index'
  })
}

/**
 * 编辑车源
 */
const editCar = (carId) => {
  Taro.navigateTo({
    url: `/pages/sell/index?id=${carId}&mode=edit`
  })
}

/**
 * 认证车源
 */
const authCar = (carId) => {
  Taro.navigateTo({
    url: `/pages/setAuthCar/index?id=${carId}&mode=edit`
  })
}

/**
 * 切换上下架状态
 */
const toggleOffline = (car) => {
  currentOfflineCar.value = car
  offlineDialogContent.value = car.isOffline ? '确认要上架此车源吗?' : '确认要下架此车源吗?'
  offlineDialogVisible.value = true
}

/**
 * 确认上下架操作
 */
const confirmOffline = () => {
  if (currentOfflineCar.value) {
    const car = currentOfflineCar.value
    car.isOffline = !car.isOffline

    // TODO: 调用API更新车源状态
    // updateCarStatus(car.id, { isOffline: car.isOffline })

    Taro.showToast({
      title: car.isOffline ? '已下架' : '已上架',
      icon: 'success'
    })
  }
  offlineDialogVisible.value = false
  currentOfflineCar.value = null
}

/**
 * 取消上下架操作
 */
const cancelOffline = () => {
  offlineDialogVisible.value = false
  currentOfflineCar.value = null
}

/**
 * 获取车辆列表数据
 */
const fetchCarList = async (page = 1, append = false) => {
  loading.value = true

  try {
    // 模拟API调用延迟
    await new Promise(resolve => setTimeout(resolve, 800))

    const mockData = generateMockCarData(page, pageSize.value)

    if (append) {
      carList.value.push(...mockData)
    } else {
      carList.value = mockData
    }

    // 模拟分页逻辑
    if (page >= 3) {
      hasMore.value = false
    }

    currentPage.value = page
  } catch (error) {
    console.error('获取车辆列表失败:', error)
    showToast('加载失败,请重试', 'error')
  } finally {
    loading.value = false
  }
}

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

/**
 * 加载更多数据
 */
const loadMore = () => {
  if (!hasMore.value || loading.value) return
  fetchCarList(currentPage.value + 1, true)
}

/**
 * 显示提示信息
 */
const showToast = (message, type = 'success') => {
  Taro.showToast({
    title: message,
    icon: type === 'success' ? 'success' : 'none'
  })
}

/**
 * 生成模拟车辆数据
 */
const generateMockCarData = (page = 1, size = 10) => {
  const brands = ['奔驰', '宝马', '奥迪', '大众', '丰田', '本田', '日产', '现代']
  const models = ['C级', 'E级', 'S级', '3系', '5系', '7系', 'A4', 'A6', 'A8']
  const conditions = ['准新车', '车况良好', '车况一般']
  const images = [
    'https://images.unsplash.com/photo-1549924231-f129b911e442?w=400',
    'https://images.unsplash.com/photo-1552519507-da3b142c6e3d?w=400',
    'https://images.unsplash.com/photo-1494976388531-d1058494cdd8?w=400',
    'https://images.unsplash.com/photo-1503376780353-7e6692767b70?w=400',
    'https://images.unsplash.com/photo-1525609004556-c46c7d6cf023?w=400'
  ]

  const list = []

  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 condition = conditions[Math.floor(Math.random() * conditions.length)]
    const image = images[Math.floor(Math.random() * images.length)]
    const price = Math.floor(Math.random() * 200000) + 50000
    const marketPrice = Math.floor(price * 1.2)
    const year = 2018 + Math.floor(Math.random() * 6)
    const mileage = Math.floor(Math.random() * 100000) + 10000

    list.push({
      id: `car_${index + 1}`,
      brand,
      model,
      year,
      condition,
      mileage,
      price,
      marketPrice,
      image,
      description: `${year}年${brand}${model},${condition},里程${mileage}公里`,
      isAuthenticated: Math.random() > 0.5,
      isOffline: Math.random() > 0.7,
      publishTime: new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000).toISOString()
    })
  }

  return list
}

// 页面加载时获取数据
onMounted(() => {
  fetchCarList(1, false)
})
</script>

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