| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- import React from 'react'
- import { View, Text, ScrollView } from '@tarojs/components'
- import { useInfiniteQuery, useQuery } from '@tanstack/react-query'
- import { TabBarLayout } from '@/layouts/tab-bar-layout'
- import TDesignSearch from '@/components/tdesign/search'
- import GoodsList from '@/components/goods-list'
- import { GoodsData } from '@/components/goods-card'
- import { goodsClient, advertisementClient } from '@/api'
- import { InferResponseType } from 'hono'
- import './index.css'
- import { useAuth } from '@/utils/auth'
- import { useCart } from '@/contexts/CartContext'
- import { Navbar } from '@/components/ui/navbar'
- import { Carousel } from '@/components/ui/carousel'
- import Taro from '@tarojs/taro'
- type GoodsResponse = InferResponseType<typeof goodsClient.$get, 200>
- type Goods = GoodsResponse['data'][0]
- type AdvertisementResponse = InferResponseType<typeof advertisementClient.$get, 200>
- type Advertisement = AdvertisementResponse['data'][0]
- const HomePage: React.FC = () => {
- const { isLoggedIn } = useAuth();
- const { addToCart } = useCart();
- if( !isLoggedIn ) return null;
-
- // 广告数据查询
- const {
- data: advertisementData,
- isLoading: isAdLoading,
- error: adError
- } = useQuery({
- queryKey: ['home-advertisements'],
- queryFn: async () => {
- const response = await advertisementClient.$get({
- query: {
- filters: JSON.stringify({ status: 1, typeId: 1 }), // 过滤启用的首页轮播广告
- sortBy: 'sort', // 按sort字段排序
- sortOrder: 'ASC'
- }
- })
- if (response.status !== 200) {
- throw new Error('获取广告数据失败')
- }
- return response.json()
- },
- staleTime: 5 * 60 * 1000, // 5分钟缓存
- })
- const {
- data,
- isLoading,
- isFetchingNextPage,
- fetchNextPage,
- hasNextPage,
- error
- } = useInfiniteQuery({
- queryKey: ['home-goods-infinite'],
- queryFn: async ({ pageParam = 1 }) => {
- const response = await goodsClient.$get({
- query: {
- page: pageParam,
- pageSize: 10,
- filters: JSON.stringify({ state: 1 }) // 只显示可用的商品
- }
- })
- if (response.status !== 200) {
- throw new Error('获取商品失败')
- }
- return response.json()
- },
- getNextPageParam: (lastPage) => {
- const { pagination } = lastPage
- const totalPages = Math.ceil(pagination.total / pagination.pageSize)
- return pagination.current < totalPages ? pagination.current + 1 : undefined
- },
- staleTime: 5 * 60 * 1000,
- initialPageParam: 1,
- })
- // 合并所有分页数据
- const allGoods = data?.pages.flatMap(page => page.data) || []
- // 数据转换:将API返回的商品数据转换为GoodsData接口格式
- const convertToGoodsData = (goods: Goods): GoodsData => {
- return {
- id: goods?.id?.toString() || '', // 将number类型的id转换为string
- name: goods?.name || '',
- cover_image: goods?.imageFile?.fullUrl || '',
- price: goods?.price || 0,
- originPrice: goods?.originPrice || 0,
- tags: (goods?.salesNum || 0) > 100 ? ['热销'] : ['新品']
- }
- }
- // 转换后的商品列表
- const goodsList = allGoods.map(convertToGoodsData)
- // 广告数据转换:提取图片URL并过滤掉没有图片的广告
- const finalImgSrcs = advertisementData?.data || []
- // 错误处理
- if (adError) {
- console.error('广告数据获取失败:', adError)
- }
- // 触底加载更多
- const handleScrollToLower = () => {
- if (hasNextPage && !isFetchingNextPage) {
- fetchNextPage()
- }
- }
- // // 商品点击
- // const handleGoodsClick = (goods: GoodsData, index: number) => {
- // console.log('点击商品:', goods, index)
- // }
- // 跳转到商品详情
- const handleGoodsClick = (goods: Goods) => {
- Taro.navigateTo({
- url: `/pages/goods-detail/index?id=${goods.id}`
- })
- }
- // 添加购物车
- const handleAddCart = (goods: GoodsData, index: number) => {
- // 找到对应的原始商品数据
- const originalGoods = allGoods.find(g => g.id.toString() === goods.id)
- if (originalGoods) {
- addToCart({
- id: originalGoods.id,
- name: originalGoods.name,
- price: originalGoods.price,
- image: originalGoods.imageFile?.fullUrl || '',
- stock: originalGoods.stock,
- quantity: 1
- })
- Taro.showToast({
- title: '已添加到购物车',
- icon: 'success'
- })
- }
- }
- // 商品图片点击
- const handleThumbClick = (goods: GoodsData, index: number) => {
- console.log('点击商品图片:', goods, index)
- }
- // 搜索框点击
- const handleSearchClick = () => {
- Taro.navigateTo({
- url: '/pages/search/index'
- })
- }
- return (
- <TabBarLayout activeKey="home">
- <Navbar
- title="首页"
- leftIcon=""
- onClickLeft={() => Taro.navigateBack()}
- rightIcon=""
- onClickRight={() => {}}
- />
- <ScrollView
- className="home-scroll-view"
- scrollY
- onScrollToLower={handleScrollToLower}
- >
- {/* 页面头部 - 搜索栏和轮播图 */}
- <View className="home-page-header">
- {/* 搜索栏 */}
- <View className="search" onClick={handleSearchClick}>
- <TDesignSearch
- placeholder="搜索商品..."
- disabled={true}
- shape="round"
- />
- </View>
- {/* 轮播图 */}
- <View className="swiper-wrap">
- {isAdLoading ? (
- <View className="loading-container">
- <Text className="loading-text">广告加载中...</Text>
- </View>
- ) : adError ? (
- <View className="error-container">
- <Text className="error-text">广告加载失败</Text>
- </View>
- ) : finalImgSrcs && finalImgSrcs.length > 0 ? (
- <Carousel
- items={finalImgSrcs.filter(item => item.imageFile?.fullUrl).map(item => ({
- src: item.imageFile!.fullUrl,
- title: item.title || '',
- description: item.description || ''
- }))}
- height={800}
- autoplay={true}
- interval={4000}
- circular={true}
- imageMode="aspectFill"
- />
- ) : (
- <View className="empty-container">
- <Text className="empty-text">暂无广告</Text>
- </View>
- )}
- </View>
- </View>
- {/* 页面内容 - 商品列表 */}
- <View className="home-page-container">
- {isLoading ? (
- <View className="loading-container">
- <Text className="loading-text">加载中...</Text>
- </View>
- ) : error ? (
- <View className="error-container">
- <Text className="error-text">加载失败,请重试</Text>
- </View>
- ) : goodsList.length === 0 ? (
- <View className="empty-container">
- <Text className="empty-text">暂无商品</Text>
- </View>
- ) : (
- <>
- <GoodsList
- goodsList={goodsList}
- onClick={handleGoodsClick}
- onAddCart={handleAddCart}
- onThumbClick={handleThumbClick}
- />
- {/* 加载更多状态 */}
- {isFetchingNextPage && (
- <View className="loading-more-container">
- <Text className="loading-more-text">加载更多...</Text>
- </View>
- )}
- {/* 无更多数据状态 */}
- {!hasNextPage && goodsList.length > 0 && (
- <View className="no-more-container">
- <Text className="no-more-text">已经到底啦</Text>
- </View>
- )}
- </>
- )}
- </View>
- </ScrollView>
- </TabBarLayout>
- )
- }
- export default HomePage
|