This commit is contained in:
liu
2026-08-06 16:37:04 +08:00
parent c613b520a9
commit 39b789154c
47 changed files with 3377 additions and 265 deletions
+229 -27
View File
@@ -1,34 +1,236 @@
import { View } from '@tarojs/components'
import { Button } from '@antmjs/vantui'
import { useCallback, useState } from 'react'
import Taro, { useDidShow } from '@tarojs/taro'
import { View, Text, Image, ScrollView } from '@tarojs/components'
import { Grid, GridItem } from '@antmjs/vantui'
import useAuthStore from '@/stores/auth/useAuthStore'
import useCartStore from '@/stores/cart/useCartStore'
import { getOrderSummaryApi } from '@/services/order'
import { getCategoriesApi } from '@/services/product'
import { getNoticeListApi, readNoticeApi } from '@/services/notice'
import { resolveAvatarUrl } from '@/utils/format'
import type { Category } from '@/types/product'
import type { SummaryGroup } from '@/types/order'
import type { Notice } from '@/types/notice'
import './index.less'
export default function Index() {
export default function IndexPage() {
const user = useAuthStore(s => s.user)
const token = useAuthStore(s => s.token)
const fetchCart = useCartStore(s => s.fetchCart)
/** 本月订货汇总(取当前月份分组,无则取最近一组) */
const [summary, setSummary] = useState<SummaryGroup | null>(null)
/** 顶级分类快捷入口 */
const [categories, setCategories] = useState<Category[]>([])
/** 最新通知 */
const [notices, setNotices] = useState<Notice[]>([])
const [unreadCount, setUnreadCount] = useState(0)
const loggedIn = !!token && !!user
useDidShow(() => {
if (!loggedIn) return
// 静默同步购物车角标
fetchCart().catch(() => {})
loadSummary()
loadCategories()
loadNotices()
})
/** 本月订货汇总 */
const loadSummary = useCallback(async () => {
try {
const res = await getOrderSummaryApi('month')
const groups = res.data.groups
if (!groups.length) {
setSummary(null)
return
}
const now = new Date()
const thisMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
setSummary(groups.find(g => g.period_label === thisMonth) ?? groups[groups.length - 1])
} catch {
// 错误已由 request 层 toast
}
}, [])
/** 商品分类(顶级) */
const loadCategories = useCallback(async () => {
try {
const res = await getCategoriesApi()
setCategories(res.data.filter(c => c.parent_id === 0))
} catch {
// 错误已由 request 层 toast
}
}, [])
/** 最新通知 + 未读数 */
const loadNotices = useCallback(async () => {
try {
const res = await getNoticeListApi({ page: 1, pageSize: 5 })
setNotices(res.data.data)
setUnreadCount(res.data.unread_count)
} catch {
// 错误已由 request 层 toast
}
}, [])
/** 点击分类 → 商品页(switchTab 无法传参,经本地存储传递) */
const handleCategoryTap = useCallback((id: number) => {
try {
Taro.setStorageSync('product_category_id', id)
} catch {
// noop
}
Taro.switchTab({ url: '/pages/product/index' })
}, [])
/** 点击通知 → 标记已读 */
const handleNoticeTap = useCallback(
async (notice: Notice) => {
if (notice.is_read === 1) return
try {
await readNoticeApi(notice.id)
setNotices(prev => prev.map(n => (n.id === notice.id ? { ...n, is_read: 1 } : n)))
setUnreadCount(count => Math.max(0, count - 1))
} catch {
// 错误已由 request 层 toast
}
},
[],
)
const goLogin = useCallback(() => {
Taro.navigateTo({ url: '/pages/login/index' })
}, [])
return (
<View className='index'>
<View><Button type='primary'>Hello world!</Button></View>
<View>src/styles/index.less</View>
<View className='home-page'>
{/* ========== 门店信息卡片 ========== */}
<View className='store-card'>
{!loggedIn ? (
<View className='store-card__row'>
<View className='store-card__info'>
<Text className='store-card__name'>使</Text>
<Text className='store-card__desc'></Text>
</View>
<View className='store-card__btn' onClick={goLogin}></View>
</View>
) : (
<>
<View className='store-card__row'>
{user?.avatar ? (
<Image className='store-card__avatar' src={resolveAvatarUrl(user.avatar)} mode='aspectFill' />
) : (
<View className='store-card__avatar store-card__avatar--text'>{user?.nickname?.[0] || '用'}</View>
)}
<View className='store-card__info'>
<Text className='store-card__name'>{user?.nickname}</Text>
{user?.phone ? (
<Text className='store-card__desc'>{user.phone}</Text>
) : (
<Text className='store-card__desc' onClick={goLogin}></Text>
)}
</View>
</View>
<View className='store-card__identity'>
{user?.type === 0 ? (
<View className='store-card__btn' onClick={goLogin}></View>
) : user?.store ? (
<View className='store-card__tags'>
<Text className='store-card__tag'> · {user.store.name}</Text>
{user.store.level && <Text className='store-card__tag store-card__tag--level'>{user.store.level.name}</Text>}
</View>
) : user?.supplier ? (
<View className='store-card__tags'>
<Text className='store-card__tag'> · {user.supplier.name}</Text>
</View>
) : (
<View className='store-card__btn' onClick={goLogin}></View>
)}
</View>
</>
)}
</View>
{/* ========== 本月订货汇总 ========== */}
<View className='summary-card'>
<View className='summary-card__header'>
<Text className='summary-card__title'></Text>
<Text className='summary-card__period'>{summary?.period_label ?? '—'}</Text>
</View>
<View className='summary-card__body'>
<View className='summary-card__item'>
<Text className='summary-card__value summary-card__value--strong'>{summary?.total_amount ?? '0.00'}</Text>
<Text className='summary-card__label'></Text>
</View>
<View className='summary-card__item'>
<Text className='summary-card__value'>{summary?.total_quantity ?? '0.00'}</Text>
<Text className='summary-card__label'></Text>
</View>
<View className='summary-card__item'>
<Text className='summary-card__value'>{summary?.order_count ?? 0}</Text>
<Text className='summary-card__label'></Text>
</View>
</View>
</View>
{/* ========== 快捷入口 ========== */}
<View className='section-card'>
<Grid columnNum={4} iconSize={52} border={false}>
<GridItem icon='apps-o' text='商品中心' onClick={() => Taro.switchTab({ url: '/pages/product/index' })} />
<GridItem icon='shopping-cart-o' text='购物车' onClick={() => Taro.switchTab({ url: '/pages/cart/index' })} />
<GridItem icon='orders-o' text='我的订单' onClick={() => Taro.switchTab({ url: '/pages/profile/index' })} />
<GridItem icon='balance-list-o' text='对账单' onClick={() => Taro.switchTab({ url: '/pages/profile/index' })} />
</Grid>
</View>
{/* ========== 商品分类 ========== */}
{categories.length > 0 && (
<View className='section-card'>
<View className='section-card__header'>
<Text className='section-card__title'></Text>
<Text className='section-card__more' onClick={() => Taro.switchTab({ url: '/pages/product/index' })}> </Text>
</View>
<ScrollX items={categories} onTap={handleCategoryTap} />
</View>
)}
{/* ========== 通知 ========== */}
<View className='section-card'>
<View className='section-card__header'>
<Text className='section-card__title'></Text>
{unreadCount > 0 && <Text className='section-card__more'> {unreadCount} </Text>}
</View>
{notices.length === 0 ? (
<View className='section-card__empty'>
<Text className='section-card__empty-text'></Text>
</View>
) : (
notices.map(notice => (
<View key={notice.id} className='notice-item' onClick={() => handleNoticeTap(notice)}>
{notice.is_read === 0 && <View className='notice-item__dot' />}
<View className='notice-item__content'>
<Text className='notice-item__title'>{notice.title}</Text>
<Text className='notice-item__desc' numberOfLines={1}>{notice.content}</Text>
</View>
</View>
))
)}
</View>
</View>
)
}
// export default class Index extends Component {
// componentWillMount () { }
// componentDidMount () { }
// componentWillUnmount () { }
// componentDidShow () { }
// componentDidHide () { }
// render () {
// return (
// <View className='index'>
// <View><Button type='primary'>Hello world!</Button></View>
// <View>上面的按钮的颜色已经通过全局主题重写覆盖了,参见src/style/index.less</View>
// </View>
// )
// }
// }
/** 横向滚动的分类入口 */
function ScrollX({ items, onTap }: { items: Category[]; onTap: (id: number) => void }) {
return (
<ScrollView scrollX className='category-scroll'>
{items.map(item => (
<View key={item.id} className='category-chip' onClick={() => onTap(item.id)}>
<Text className='category-chip__name'>{item.name}</Text>
</View>
))}
</ScrollView>
)
}