Files
xin-school-taro/src/pages/index/index.tsx
T
2026-07-06 11:32:58 +08:00

269 lines
8.9 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 { useState, useMemo } from 'react'
import Taro from '@tarojs/taro'
import { View, Text, ScrollView } from '@tarojs/components'
import { Search, Swiper, SwiperItem, Grid, GridItem, Tabs, Tab, Image as VantImage } from '@antmjs/vantui'
import ArticleCard from '@/components/ArticleCard'
import ProductCard from '@/components/ProductCard'
import { banners, navItems, articles } from '@/mock/articles'
import { products } from '@/mock/products'
import type { ArticleCategory } from '@/types/article'
import type { ProductCategory } from '@/types/product'
import type { Product } from '@/types/product'
import CustomTabBar from "@/custom-tab-bar";
import './index.less'
/** 获取状态栏和导航栏高度 */
function getNavBarHeight() {
const systemInfo = Taro.getSystemInfoSync()
const statusBarHeight = systemInfo.statusBarHeight || 20
// 微信小程序导航栏高度一般为 44px,总高度 = 状态栏 + 导航栏
const navBarHeight = 44
return {
statusBarHeight,
navBarHeight,
totalHeight: statusBarHeight + navBarHeight,
}
}
/** 统一板块标签:校园商城 + 文章分类 */
const MAIN_TABS = [
{ title: '校园商城', value: 'mall' },
{ title: '最新', value: 'latest' },
{ title: '推荐', value: 'recommend' },
{ title: '热点', value: 'hot' },
]
/** 商品分类标签 */
const PRODUCT_CATEGORIES: { title: string; value: ProductCategory | 'all' }[] = [
{ title: '全部', value: 'all' },
{ title: '数码', value: 'digital' },
{ title: '图书', value: 'book' },
{ title: '生活', value: 'life' },
{ title: '美食', value: 'food' },
{ title: '服饰', value: 'cloth' },
]
export default function Index() {
const [activeMainTab, setActiveMainTab] = useState(0)
const [activeCategory, setActiveCategory] = useState(0)
const navBarHeight = useMemo(() => getNavBarHeight(), [])
/** 当前选中的板块 */
const currentTab = MAIN_TABS[activeMainTab]?.value || 'mall'
/** 根据文章分类筛 */
const filteredArticles = useMemo(() => {
const value = MAIN_TABS[activeMainTab]?.value || 'latest'
if (value === 'mall') return articles.filter((a) => a.category === 'latest')
return articles.filter((a) => a.category === value)
}, [activeMainTab])
/** 根据当前分类筛选商品 */
const filteredProducts = useMemo(() => {
const catValue = PRODUCT_CATEGORIES[activeCategory]?.value || 'all'
if (catValue === 'all') return products
return products.filter((p) => p.category === catValue)
}, [activeCategory])
/** 搜索 */
const handleSearch = () => {
Taro.navigateTo({ url: '/pages/search/index' })
}
/** 导航项点击 */
const handleNavClick = (item: { path: string; label: string }) => {
if (item.path) {
Taro.navigateTo({ url: item.path })
} else {
Taro.showToast({ title: `${item.label}即将上线`, icon: 'none' })
}
}
/** 文章点击 */
const handleArticleClick = (id: string) => {
Taro.navigateTo({ url: `/pages/article/index?id=${id}` })
}
/** 商品点击 */
const handleProductClick = (product: Product) => {
Taro.navigateTo({ url: `/pages/product-detail/index?id=${product.id}` })
}
/** 查看全部商品 */
const handleViewAll = () => {
Taro.showToast({ title: '商城即将上线', icon: 'none' })
}
/** 商家入驻 */
const handleMerchantJoin = () => {
Taro.showToast({ title: '商家入驻即将上线', icon: 'none' })
}
return (
<View className='home-page'>
{/* ========== 自定义导航栏 (毛玻璃) ========== */}
<View
className='custom-navbar'
style={{ paddingTop: `${navBarHeight.statusBarHeight}px` }}
>
<View className='navbar-content' style={{ height: `${navBarHeight.navBarHeight}px` }}>
<View className='navbar-search'>
<Search
placeholder='搜索课程、资讯、活动...'
onFocus={handleSearch}
shape='round'
background='transparent'
/>
</View>
</View>
</View>
{/* 占位,补偿固定导航栏高度 */}
<View style={{ height: `${navBarHeight.totalHeight}px` }} />
{/* ========== 可滚动内容区 ========== */}
<ScrollView
className='page-scroll'
scrollY
enhanced
showScrollbar={false}
>
{/* ========== 卡片轮播图 ========== */}
<View className='swiper-section'>
<Swiper
className='banner-swiper'
paginationColor='rgba(255,255,255,0.5)'
height={220}
autoPlay={3000}
duration={500}
loop
>
{banners.map((banner) => (
<SwiperItem key={banner.id}>
<View className='swiper-card'>
<VantImage
className='banner-image'
src={banner.image}
fit='cover'
width='100%'
height='100%'
radius={12}
/>
<View className='banner-title'>{banner.title}</View>
</View>
</SwiperItem>
))}
</Swiper>
</View>
{/* ========== 导航组 2行 x 5列 ========== */}
<View className='nav-section'>
<Grid columnNum={5} border={false} gutter={8}>
{navItems.map((item) => (
<GridItem
key={item.id}
icon={item.icon}
text={item.label}
onClick={() => handleNavClick(item)}
/>
))}
</Grid>
</View>
{/* ========== 统一板块:校园商城 | 最新 | 推荐 | 热点 ========== */}
<View className='main-tabs-section'>
<Tabs
active={activeMainTab}
onChange={(e) => setActiveMainTab(e.detail.index)}
sticky
offsetTop={navBarHeight.totalHeight}
color='#1989fa'
titleActiveColor='#1989fa'
titleInactiveColor='#646566'
animated
swipeable
>
{MAIN_TABS.map((tab) => (
<Tab key={tab.value} title={tab.title} />
))}
</Tabs>
{/* ===== 校园商城内容 ===== */}
{currentTab === 'mall' && (
<View className='mall-content'>
{/* 商品分类标签 */}
<ScrollView
className='category-scroll'
scrollX
enhanced
showScrollbar={false}
>
<View className='category-tabs'>
{PRODUCT_CATEGORIES.map((cat, idx) => (
<View
key={cat.value}
className={`category-tab ${activeCategory === idx ? 'active' : ''}`}
onClick={() => setActiveCategory(idx)}
>
<Text className='category-text'>{cat.title}</Text>
</View>
))}
</View>
</ScrollView>
{/* 商品双列网格 */}
<View className='product-grid'>
{filteredProducts.map((product) => (
<ProductCard
key={product.id}
product={product}
onClick={handleProductClick}
/>
))}
{filteredProducts.length === 0 && (
<View className='empty-grid'>暂无商品</View>
)}
</View>
{/* 商家入驻入口 */}
<View className='merchant-join-card' onClick={handleMerchantJoin}>
<View className='join-content'>
<Text className='join-icon'>🏪</Text>
<View className='join-text-wrap'>
<Text className='join-title'>商家入驻</Text>
<Text className='join-desc'>拥有自己的店铺,开启校园创业之旅</Text>
</View>
</View>
<Text className='join-arrow'></Text>
</View>
</View>
)}
{/* ===== 文章列表内容 ===== */}
{currentTab !== 'mall' && (
<View className='article-content'>
{filteredArticles.map((article) => (
<View
key={article.id}
className='article-item'
onClick={() => handleArticleClick(article.id)}
>
<ArticleCard article={article} />
</View>
))}
{filteredArticles.length === 0 && (
<View className='empty-tip'>暂无文章</View>
)}
</View>
)}
</View>
{/* 底部安全距离 */}
<View className='bottom-safe' />
</ScrollView>
{/*<CustomTabBar activeKey='index' />*/}
</View>
)
}