feat(home): 重构首页界面并更新API配置

- 移除底部导航栏中的"基地生活"选项卡
- 切换开发环境API地址为线上测试接口
- 添加完整的首页样式定义,包括英雄区域、商品卡片、快捷入口等
- 重构首页组件结构,集成商品列表、分类标签页和交互功能
- 更新主题管理逻辑,支持多种主题模式和用户ID兼容处理
- 添加商品数据获取和展示功能,实现首页内容动态加载
This commit is contained in:
2026-01-15 10:12:49 +08:00
parent 039af32fc3
commit 0770eb1699
6 changed files with 597 additions and 58 deletions

View File

@@ -1,19 +1,17 @@
import Header from './Header';
import BestSellers from './BestSellers';
import Taro from '@tarojs/taro';
import {useShareAppMessage} from "@tarojs/taro"
import {useEffect, useState} from "react";
import {getShopInfo} from "@/api/layout";
import Menu from "./Menu";
import Banner from "./Banner";
import {checkAndHandleInviteRelation, hasPendingInvite} from "@/utils/invite";
import Header from './Header'
import Taro, { useShareAppMessage } from '@tarojs/taro'
import { View, Text, Image, ScrollView } from '@tarojs/components'
import { useEffect, useMemo, useState, type ReactNode } from 'react'
import { Cart, Coupon, Gift, Ticket } from '@nutui/icons-react-taro'
import { getShopInfo } from '@/api/layout'
import { checkAndHandleInviteRelation, hasPendingInvite } from '@/utils/invite'
import { pageShopGoods } from '@/api/shop/shopGoods'
import type { ShopGoods } from '@/api/shop/shopGoods/model'
import './index.scss'
function Home() {
// 吸顶状态
// const [stickyStatus, setStickyStatus] = useState<boolean>(false)
// Tabs粘性状态
const [_, setTabsStickyStatus] = useState<boolean>(false)
const [activeTab, setActiveTab] = useState('推荐')
const [goodsList, setGoodsList] = useState<ShopGoods[]>([])
useShareAppMessage(() => {
// 获取当前用户ID用于生成邀请链接
@@ -85,9 +83,7 @@ function Home() {
// }
// 处理Tabs粘性状态变化
const handleTabsStickyChange = (isSticky: boolean) => {
setTabsStickyStatus(isSticky)
}
// const handleTabsStickyChange = (isSticky: boolean) => {}
const reload = () => {
@@ -99,6 +95,10 @@ function Home() {
})
pageShopGoods({}).then(res => {
setGoodsList(res?.list || [])
})
// 检查是否有待处理的邀请关系 - 异步处理,不阻塞页面加载
if (hasPendingInvite()) {
console.log('检测到待处理的邀请关系')
@@ -152,16 +152,177 @@ function Home() {
});
}, []);
const tabs = useMemo(() => ['推荐', '桶装水', '优惠组合', '购机套餐', '饮水设备'], [])
const shortcuts = useMemo<
Array<{ key: string; title: string; icon: ReactNode; onClick: () => void }>
>(
() => [
{
key: 'ticket',
title: '充值水票',
icon: <Ticket size={30} />,
onClick: () => Taro.navigateTo({ url: '/user/wallet/wallet' }),
},
{
key: 'order',
title: '立即订水',
icon: <Cart size={30} />,
onClick: () => Taro.switchTab({ url: '/pages/category/index' }),
},
{
key: 'invite',
title: '邀请有礼',
icon: <Gift size={30} />,
onClick: () => Taro.navigateTo({ url: '/dealer/qrcode/index' }),
},
{
key: 'coupon',
title: '领券中心',
icon: <Coupon size={30} />,
onClick: () => Taro.navigateTo({ url: '/coupon/index' }),
},
],
[]
)
const visibleGoods = useMemo(() => {
// 先按效果图展示两列卡片,数据不够时也保持布局稳定
const list = goodsList || []
if (list.length <= 6) return list
return list.slice(0, 6)
}, [goodsList])
return (
<>
{/* Header区域 - 现在由Header组件内部处理吸顶逻辑 */}
<Header />
<div className={'flex flex-col mt-12'}>
<Menu/>
<Banner/>
<BestSellers onStickyChange={handleTabsStickyChange}/>
</div>
<View className="home-page">
{/* 顶部活动主视觉 */}
<View className="home-hero">
<View className="home-hero__bg" />
<View className="home-hero__content">
<View className="home-hero__left">
<View className="home-hero__topRow">
<View className="home-hero__brand">
<Text className="home-hero__brandText"></Text>
</View>
<View className="home-hero__tag">
<Text className="home-hero__tagText"></Text>
</View>
<View className="home-hero__date">
<Text className="home-hero__dateText">202X年X月X日 - X月X日</Text>
</View>
</View>
<View className="home-hero__headline">
<Text className="home-hero__headlineText"></Text>
<Text className="home-hero__headlineText">15L</Text>
</View>
</View>
<View className="home-hero__right">
<View className="home-hero__bottle">
<View className="home-hero__bottleCap" />
<View className="home-hero__bottleLabel">
<Text className="home-hero__bottleLabelText"></Text>
</View>
</View>
</View>
</View>
</View>
{/* 电子水票 */}
<View className="ticket-card">
<View className="ticket-card__head">
<Text className="ticket-card__title"></Text>
<Text className="ticket-card__count">
<Text className="ticket-card__countNum">0</Text>
</Text>
</View>
<View className="ticket-card__body">
<View className="shortcut-grid">
{shortcuts.map((item) => (
<View
key={item.key}
className="shortcut-grid__item"
onClick={item.onClick}
>
<View className="shortcut-grid__icon">{item.icon}</View>
<Text className="shortcut-grid__text">{item.title}</Text>
</View>
))}
</View>
</View>
</View>
{/* 分类Tabs */}
<ScrollView className="home-tabs" scrollX enableFlex>
<View className="home-tabs__inner">
{tabs.map((tab) => {
const active = tab === activeTab
return (
<View
key={tab}
className={`home-tabs__item ${active ? 'home-tabs__item--active' : ''}`}
onClick={() => setActiveTab(tab)}
>
<Text className="home-tabs__itemText">{tab}</Text>
</View>
)
})}
</View>
</ScrollView>
{/* 商品列表 */}
<View className="goods-grid">
{visibleGoods.map((item) => (
<View key={item.goodsId} className="goods-card">
<View className="goods-card__imgWrap">
<Image
className="goods-card__img"
src={item.image}
mode="aspectFill"
lazyLoad={false}
onClick={() =>
Taro.navigateTo({ url: `/shop/goodsDetail/index?id=${item.goodsId}` })
}
/>
</View>
<View className="goods-card__body">
<Text className="goods-card__title">{item.name}</Text>
<View className="goods-card__meta">
<Text className="goods-card__sold">:{item.sales || 0}</Text>
<View className="goods-card__price">
<Text className="goods-card__priceUnit"></Text>
<Text className="goods-card__priceValue">{item.price}</Text>
</View>
</View>
<View className="goods-card__actions">
<View
className="goods-card__btn goods-card__btn--ghost"
onClick={() => Taro.navigateTo({ url: '/user/coupon/index' })}
>
<Text className="goods-card__btnText"></Text>
</View>
<View
className="goods-card__btn goods-card__btn--primary"
onClick={() =>
Taro.navigateTo({ url: `/shop/goodsDetail/index?id=${item.goodsId}` })
}
>
<Text className="goods-card__btnText goods-card__btnText--primary"></Text>
</View>
</View>
</View>
</View>
))}
</View>
</View>
</>
)
}