Files
school2-taro/src/pages/index/index.tsx
T
2026-08-09 11:24:20 +08:00

287 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { View, Image, Text } from '@tarojs/components'
import {Search, Swiper, SwiperItem, Grid, GridItem, Icon} from '@antmjs/vantui'
import { useState, useEffect } from 'react'
import Taro from '@tarojs/taro'
import { getHomeData } from '@/api/home'
import type { HomeLinkItem, HomeActivityItem } from '@/api/home'
import { navigateToLink } from '@/utils/navigate'
import './index.less'
// 背景图
import bgImg from '../../../static/img/bg.png'
// ==================== 本地兜底数据 ====================
// ==================== 工具函数 ====================
/** 排序 + 过滤启用的项 */
function prepareItems<T extends { status: number; sort: number }>(items: T[]): T[] {
return items
.filter((item) => item.status === 0)
.sort((a, b) => a.sort - b.sort)
}
/** 处理导航跳转 */
function handleNavigate(item: HomeLinkItem): void {
// link_type = 1:跳转分类页,携带分类 ID
if (item.link_type === 1) {
Taro.navigateTo({ url: `/pages/category/index?category_id=${item.category_id ?? ''}` })
return
}
// link_type = 0:直接跳转 link 链接
navigateToLink(item.link)
}
// ==================== 页面组件 ====================
export default function Index() {
const [searchValue, setSearchValue] = useState('')
const [carousel, setCarousel] = useState<HomeLinkItem[]>([])
const [gridNav, setGridNav] = useState<HomeLinkItem[]>([])
const [activity, setActivity] = useState<HomeActivityItem[]>([])
const [siteTitle, setSiteTitle] = useState<string>('河南省工业大学')
const [loading, setLoading] = useState(true)
// 获取首页数据
useEffect(() => {
async function fetchHomeData() {
try {
const res = await getHomeData()
if (res.success && res.data) {
// 轮播图:API 有数据则用,否则兜底
if (res.data.carousel?.length > 0) {
setCarousel(prepareItems(res.data.carousel))
}
// 宫格导航:API 有数据则用,否则兜底
if (res.data.gridNav?.length > 0) {
setGridNav(prepareItems(res.data.gridNav))
}
// 活动专区:API 有数据则用,否则兜底静态内容
if (res.data.activity?.length > 0) {
setActivity(prepareItems(res.data.activity))
}
if (res.data.site_title) {
setSiteTitle(res.data.site_title)
}
}
} catch (err) {
console.warn('获取首页数据失败,使用本地兜底数据', err)
} finally {
setLoading(false)
}
}
fetchHomeData()
}, [])
return (
<View className='index'>
{/* 背景图层 */}
<Image className='bg-image' src={bgImg} mode='widthFix' />
{/* 内容层 */}
<View className='index-content'>
<View className='site-name'>
<Icon name='location-o' style={{ marginRight: '10px' }} />
{siteTitle}
</View>
{/* 1. 顶部搜索栏 */}
<View className='search-wrapper'>
<Search
value={searchValue}
onChange={(e) => setSearchValue(e.detail)}
shape='round'
placeholder='搜索美食、餐厅...'
background='rgba(255,255,255,0.9)'
onSearch={() => {
// 搜索逻辑
}}
/>
</View>
{/* 2. 中间轮播图 */}
<View className='swiper-wrapper'>
{carousel.length > 0 ? (
<Swiper
autoPlay={3000}
loop
paginationVisible
paginationColor='#ffffff'
duration={500}
height={220}
>
{carousel.map((item) => (
<SwiperItem key={item.id}>
<Image
className='swiper-img'
src={item.image.preview_url}
mode='aspectFill'
onClick={() => handleNavigate(item)}
/>
</SwiperItem>
))}
</Swiper>
) : (
<View className='swiper-placeholder'>暂无轮播图</View>
)}
</View>
{/* 3. 宫格导航 */}
<View className='grid-wrapper'>
{gridNav.length > 0 ? (
<Grid columnNum={5} border={false} gutter={12} iconSize={48}>
{gridNav.map((item) => (
<GridItem
key={item.id}
icon={item.image.preview_url}
text={item.title}
onClick={() => handleNavigate(item)}
/>
))}
</Grid>
) : (
<View className='grid-placeholder'>暂无分类</View>
)}
</View>
{/* 4. 活动专区 */}
<View className='activity-section'>
<View className='activity-header'>
<View className='activity-title'>
<Text className='title-accent' />
活动专区
</View>
<View className='activity-more'>
更多
<View className='more-arrow'></View>
</View>
</View>
{activity.length > 0 ? (
<>
{/* 前两个:activity-card 渐变卡片 */}
<View className={`activity-cards ${activity.length === 1 ? 'single' : ''}`}>
{activity.slice(0, 2).map((item, index) => (
<View
key={item.id}
className={`activity-card ${index === 0 ? 'card-fullcut' : 'card-newbie'}`}
onClick={() => handleNavigate(item)}
>
<View className='deco-circle circle-1' />
<View className='deco-circle circle-2' />
<View className='activity-card-main'>
<View className='activity-icon'>
<Image className='activity-icon-img' src={item.image.preview_url} mode='aspectFill' />
</View>
<View className='activity-name'>{item.title}</View>
<View className='activity-desc'>{item.description}</View>
</View>
</View>
))}
</View>
{/* 其余:section-card 样式 */}
{activity.slice(2).map((item, index) => (
<View
key={item.id}
className={`section-card card-ic-${index % 4}`}
onClick={() => handleNavigate(item)}
>
<View className='card-icon'>
<Image className='card-icon-img' src={item.image.preview_url} mode='aspectFill' />
</View>
<View className='card-info'>
<View className='card-title'>{item.title}</View>
<View className='card-desc'>{item.description}</View>
</View>
<View className='card-arrow'></View>
</View>
))}
</>
) : (
/* 兜底静态内容 */
<View className='activity-cards'>
{/* 满减优惠 */}
<View
className='activity-card card-fullcut'
onClick={() => {
// TODO: 活动页面上线后配置跳转链接
}}
>
<View className='deco-circle circle-1' />
<View className='deco-circle circle-2' />
<View className='activity-card-main'>
<View className='activity-icon'>🎫</View>
<View className='activity-name'>满减优惠</View>
<View className='activity-desc'>305 · 可与券叠加</View>
</View>
</View>
{/* 新品首发 */}
<View
className='activity-card card-newbie'
onClick={() => {
// TODO: 活动页面上线后配置跳转链接
}}
>
<View className='deco-circle circle-1' />
<View className='deco-circle circle-2' />
<View className='activity-card-main'>
<View className='activity-icon'></View>
<View className='activity-name'>新品首发</View>
<View className='activity-desc'>每周三上新 · 限时尝鲜价</View>
</View>
</View>
</View>
)}
</View>
{/* 5. 底部推荐卡片 */}
<View className='bottom-section'>
<View className='section-card card-hot'>
<View className='card-icon'>🔥</View>
<View className='card-info'>
<View className='card-title'>热门推荐</View>
<View className='card-desc'>查看同学们都在点什么</View>
</View>
<View className='card-arrow'></View>
</View>
<View className='section-card card-discount'>
<View className='card-icon'>💰</View>
<View className='card-info'>
<View className='card-title'>限时优惠</View>
<View className='card-desc'>超值套餐低至9.9</View>
</View>
<View className='card-arrow'></View>
</View>
<View className='section-card card-new'>
<View className='card-icon'></View>
<View className='card-info'>
<View className='card-title'>新品上架</View>
<View className='card-desc'>尝尝校园新味道</View>
</View>
<View className='card-arrow'></View>
</View>
<View className='section-card card-order'>
<View className='card-icon'>📋</View>
<View className='card-info'>
<View className='card-title'>我的订单</View>
<View className='card-desc'>查看订单状态与历史</View>
</View>
<View className='card-arrow'></View>
</View>
</View>
{/* 底部留白 */}
<View className='bottom-spacer' />
</View>
</View>
)
}