Files
glt-taro/src/pages/index/index.tsx
赵忠林 2a3b661478 feat(ticket): 移除礼品卡相关页面并调整路由配置
- 删除 ticket/add.config.ts 和 ticket/add.tsx 页面文件
- 删除 ticket/detail.config.ts 和 ticket/detail.tsx 页面文件
- 删除 ticket/receive.config.ts 和 ticket/receive.tsx 页面文件
- 删除 ticket/redeem.config.ts 和 ticket/redeem.tsx 页面文件
- 将 app.config.ts 中的 ticket/detail 路由改为 ticket/use
- 修改首页订单按钮跳转链接从 goodsDetail 到 ticket/use
- 修改首页商品卡片按钮跳转从 coupon/index 到 ticket/index
- 新增 ticket/use.config.ts 配置文件并设置页面标题为立即送水
2026-02-05 18:35:17 +08:00

339 lines
11 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 Header from './Header'
import Banner from './Banner'
import Taro, { useDidShow, 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, ShopGoodsParam } from '@/api/shop/shopGoods/model'
import { getMyGltUserTicketTotal } from '@/api/glt/gltUserTicket'
import './index.scss'
function Home() {
const [activeTabKey, setActiveTabKey] = useState('recommend')
const [goodsList, setGoodsList] = useState<ShopGoods[]>([])
const [ticketTotal, setTicketTotal] = useState(0)
useShareAppMessage(() => {
// 获取当前用户ID用于生成邀请链接
const userId = Taro.getStorageSync('UserId');
return {
title: '🏠 首页 🏠',
path: userId ? `/pages/index/index?inviter=${userId}&source=share&t=${Date.now()}` : `/pages/index/index`,
success: function () {
console.log('首页分享成功');
Taro.showToast({
title: '分享成功',
icon: 'success',
duration: 2000
});
},
fail: function () {
console.log('首页分享失败');
Taro.showToast({
title: '分享失败',
icon: 'none',
duration: 2000
});
}
};
});
// const reloadMore = async () => {
// setPage(page + 1)
// }
const showAuthModal = () => {
Taro.showModal({
title: '授权提示',
content: '需要获取您的用户信息',
confirmText: '去授权',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
// 用户点击确认,打开授权设置页面
openSetting();
}
}
});
};
const openSetting = () => {
// Taro.openSetting调起客户端小程序设置界面返回用户设置的操作结果。设置界面只会出现小程序已经向用户请求过的权限。
Taro.openSetting({
success: (res) => {
if (res.authSetting['scope.userInfo']) {
// 用户授权成功,可以获取用户信息
reload();
} else {
// 用户拒绝授权,提示授权失败
Taro.showToast({
title: '授权失败',
icon: 'none'
});
}
}
});
};
// const onSticky = (item: IArguments) => {
// if(item){
// setStickyStatus(!stickyStatus)
// }
// }
// 处理Tabs粘性状态变化
// const handleTabsStickyChange = (isSticky: boolean) => {}
const reload = () => {
const token = Taro.getStorageSync('access_token')
const userIdRaw = Taro.getStorageSync('UserId')
const userId = Number(userIdRaw)
const hasUserId = Number.isFinite(userId) && userId > 0
if (!token && !hasUserId) {
setTicketTotal(0)
return
}
getMyGltUserTicketTotal(hasUserId ? userId : undefined)
.then((total) => setTicketTotal(typeof total === 'number' ? total : 0))
.catch((err) => {
console.error('首页水票总数加载失败:', err)
setTicketTotal(0)
})
};
// 回到首页/首次进入时都刷新一次(避免依赖 scope.userInfo 导致不触发 reload
useDidShow(() => {
reload()
})
useEffect(() => {
// 获取站点信息
getShopInfo().then(() => {
})
// 检查是否有待处理的邀请关系 - 异步处理,不阻塞页面加载
if (hasPendingInvite()) {
console.log('检测到待处理的邀请关系')
// 延迟处理,确保用户信息已加载,并设置超时保护
setTimeout(async () => {
try {
// 设置超时保护,避免长时间等待
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('邀请关系处理超时')), 8000)
);
const invitePromise = checkAndHandleInviteRelation();
const success = await Promise.race([invitePromise, timeoutPromise]);
if (success) {
console.log('首页邀请关系处理成功')
}
} catch (error) {
console.error('首页邀请关系处理失败:', error)
// 邀请关系处理失败不应该影响页面正常显示
// 可以选择清除邀请参数,避免重复尝试
const errorMessage = error instanceof Error ? error.message : String(error)
if (errorMessage?.includes('超时')) {
console.log('邀请关系处理超时,清除邀请参数')
// 可以选择清除邀请参数或稍后重试
}
}
}, 2000)
}
// Taro.getSetting获取用户的当前设置。返回值中只会出现小程序已经向用户请求过的权限。
Taro.getSetting({
success: (res) => {
if (res.authSetting['scope.userInfo']) {
// 用户已经授权过,可以直接获取用户信息
console.log('用户已经授权过,可以直接获取用户信息')
} else {
// 用户未授权,需要弹出授权窗口
console.log('用户未授权,需要弹出授权窗口')
showAuthModal();
}
}
});
// 获取用户信息
Taro.getUserInfo({
success: (res) => {
const avatar = res.userInfo.avatarUrl;
console.log(avatar, 'avatarUrl')
}
});
}, []);
const tabs = useMemo<
Array<{ key: string; title: string; params: Partial<ShopGoodsParam> }>
>(
() => [
{ key: 'recommend', title: '推荐', params: { recommend: 1 } },
{ key: '4476', title: '桶装水', params: { categoryId: 4476 } },
{ key: '4556', title: '优惠组合', params: { categoryId: 4556 } },
{ key: '4557', title: '购机套餐', params: { categoryId: 4557 } },
{ key: '4477', title: '饮水设备', params: { categoryId: 4477 } },
],
[]
)
useEffect(() => {
const tab = tabs.find((t) => t.key === activeTabKey) || tabs[0]
if (!tab) return
pageShopGoods({ ...tab.params, status: 0 })
.then((res) => setGoodsList((res?.list || []).filter((g) => g?.status === 0)))
.catch((err) => {
console.error('首页商品列表加载失败:', err)
setGoodsList([])
})
}, [activeTabKey, tabs])
const shortcuts = useMemo<
Array<{ key: string; title: string; icon: ReactNode; onClick: () => void }>
>(
() => [
{
key: 'ticket',
title: '我的水票',
icon: <Ticket size={30} />,
onClick: () => Taro.navigateTo({ url: '/user/ticket/index' }),
},
{
key: 'order',
title: '立即送水',
icon: <Cart size={30} />,
onClick: () => Taro.navigateTo({ url: '/user/ticket/use?goodsId=10074' }),
},
{
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 />
<View className="home-page">
{/* 顶部活动主视觉:使用 Banner 组件 */}
<Banner />
{/* 电子水票 */}
<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">{ticketTotal}</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.key === activeTabKey
return (
<View
key={tab.key}
className={`home-tabs__item ${active ? 'home-tabs__item--active' : ''}`}
onClick={() => setActiveTabKey(tab.key)}
>
<Text className="home-tabs__itemText">{tab.title}</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/ticket/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>
</>
)
}
export default Home