287 lines
10 KiB
TypeScript
287 lines
10 KiB
TypeScript
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'>满30减5 · 可与券叠加</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>
|
||
)
|
||
}
|