|
|
@@ -5,7 +5,7 @@ import { TabBarLayout } from '@/layouts/tab-bar-layout'
|
|
|
import TDesignSearch from '@/components/tdesign/search'
|
|
|
import GoodsList from '@/components/goods-list'
|
|
|
import { GoodsData } from '@/components/goods-card'
|
|
|
-import { goodsClient, advertisementClient } from '@/api'
|
|
|
+import { goodsClient, advertisementClient, authClient } from '@/api'
|
|
|
import './index.css'
|
|
|
import { useAuth } from '@/utils/auth'
|
|
|
import { useCart } from '@/contexts/CartContext'
|
|
|
@@ -56,9 +56,11 @@ interface Goods {
|
|
|
}
|
|
|
|
|
|
const HomePage: React.FC = () => {
|
|
|
- const { isLoggedIn } = useAuth();
|
|
|
+ const { isLoggedIn, setUser } = useAuth();
|
|
|
const { addToCart } = useCart();
|
|
|
const [refreshing, setRefreshing] = React.useState(false);
|
|
|
+ const [showPrivacyModal, setShowPrivacyModal] = React.useState(false);
|
|
|
+ const [isLoggingIn, setIsLoggingIn] = React.useState(false);
|
|
|
|
|
|
// 广告数据查询
|
|
|
const {
|
|
|
@@ -242,8 +244,59 @@ const HomePage: React.FC = () => {
|
|
|
}
|
|
|
})
|
|
|
|
|
|
- // 如果未登录,不渲染页面内容
|
|
|
- if (!isLoggedIn) return null
|
|
|
+ // 未登录时显示隐私政策弹框
|
|
|
+ React.useEffect(() => {
|
|
|
+ if (!isLoggedIn) {
|
|
|
+ setShowPrivacyModal(true)
|
|
|
+ }
|
|
|
+ }, [isLoggedIn])
|
|
|
+
|
|
|
+ // 同意隐私政策,直接静默登录获取openid
|
|
|
+ const handleAgreePrivacy = async () => {
|
|
|
+ setIsLoggingIn(true)
|
|
|
+ try {
|
|
|
+ // 获取登录code
|
|
|
+ const loginRes = await Taro.login()
|
|
|
+ if (!loginRes.code) {
|
|
|
+ throw new Error('获取登录凭证失败')
|
|
|
+ }
|
|
|
+
|
|
|
+ // 调用后端静默登录API(不传userInfo)
|
|
|
+ const response = await authClient['mini-login'].$post({
|
|
|
+ json: {
|
|
|
+ code: loginRes.code,
|
|
|
+ tenantId: Number(process.env.TARO_APP_TENANT_ID) || 1
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ if (response.status === 200) {
|
|
|
+ const { token, user } = await response.json()
|
|
|
+
|
|
|
+ // 保存token和用户信息
|
|
|
+ Taro.setStorageSync('userInfo', user)
|
|
|
+ Taro.setStorageSync('mini_token', token)
|
|
|
+ setUser(user as any)
|
|
|
+
|
|
|
+ setShowPrivacyModal(false)
|
|
|
+ Taro.showToast({ title: '登录成功', icon: 'success' })
|
|
|
+ } else {
|
|
|
+ throw new Error('登录失败')
|
|
|
+ }
|
|
|
+ } catch (error: any) {
|
|
|
+ console.error('静默登录失败:', error)
|
|
|
+ // 静默登录失败,跳转到登录页
|
|
|
+ setShowPrivacyModal(false)
|
|
|
+ Taro.navigateTo({ url: '/pages/login/index' })
|
|
|
+ } finally {
|
|
|
+ setIsLoggingIn(false)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 拒绝隐私政策,跳转到登录页
|
|
|
+ const handleRejectPrivacy = () => {
|
|
|
+ setShowPrivacyModal(false)
|
|
|
+ Taro.navigateTo({ url: '/pages/login/index' })
|
|
|
+ }
|
|
|
|
|
|
// 跳转到商品详情
|
|
|
const handleGoodsClick = (goods: GoodsData, index: number) => {
|
|
|
@@ -325,110 +378,166 @@ const HomePage: React.FC = () => {
|
|
|
}
|
|
|
|
|
|
return (
|
|
|
- <TabBarLayout activeKey="home">
|
|
|
- <Navbar
|
|
|
- title="首页"
|
|
|
- leftIcon=""
|
|
|
- onClickLeft={() => Taro.navigateBack()}
|
|
|
- rightIcon=""
|
|
|
- onClickRight={() => {}}
|
|
|
- />
|
|
|
- <ScrollView
|
|
|
- className="home-scroll-view"
|
|
|
- scrollY
|
|
|
- refresherEnabled={true}
|
|
|
- refresherTriggered={refreshing}
|
|
|
- onRefresherRefresh={handleRefresh}
|
|
|
- onScrollToLower={handleScrollToLower} >
|
|
|
- {/* 页面头部 - 搜索栏和轮播图 */}
|
|
|
- <View className="home-page-header">
|
|
|
- {/* 搜索栏 */}
|
|
|
- <View className="search" onClick={handleSearchClick}>
|
|
|
- <TDesignSearch
|
|
|
- placeholder="搜索商品..."
|
|
|
- disabled={true}
|
|
|
- shape="round"
|
|
|
- />
|
|
|
+ <>
|
|
|
+ <TabBarLayout activeKey="home">
|
|
|
+ <Navbar
|
|
|
+ title="首页"
|
|
|
+ leftIcon=""
|
|
|
+ onClickLeft={() => Taro.navigateBack()}
|
|
|
+ rightIcon=""
|
|
|
+ onClickRight={() => {}}
|
|
|
+ />
|
|
|
+ <ScrollView
|
|
|
+ className="home-scroll-view"
|
|
|
+ scrollY
|
|
|
+ refresherEnabled={true}
|
|
|
+ refresherTriggered={refreshing}
|
|
|
+ onRefresherRefresh={handleRefresh}
|
|
|
+ onScrollToLower={handleScrollToLower} >
|
|
|
+ {/* 页面头部 - 搜索栏和轮播图 */}
|
|
|
+ <View className="home-page-header">
|
|
|
+ {/* 搜索栏 */}
|
|
|
+ <View className="search" onClick={handleSearchClick}>
|
|
|
+ <TDesignSearch
|
|
|
+ placeholder="搜索商品..."
|
|
|
+ disabled={true}
|
|
|
+ shape="round"
|
|
|
+ />
|
|
|
+ </View>
|
|
|
+
|
|
|
+ {/* 轮播图 */}
|
|
|
+ <View className="swiper-wrap">
|
|
|
+ {isAdLoading ? (
|
|
|
+ <View className="loading-container">
|
|
|
+ <Text className="loading-text">广告加载中...</Text>
|
|
|
+ </View>
|
|
|
+ ) : adError ? (
|
|
|
+ <View className="error-container">
|
|
|
+ <Text className="error-text">广告加载失败</Text>
|
|
|
+ </View>
|
|
|
+ ) : finalImgSrcs && finalImgSrcs.length > 0 ? (
|
|
|
+
|
|
|
+ <Carousel
|
|
|
+ items={finalImgSrcs.filter((item: any) => item.imageFile?.fullUrl).map((item: any) => ({
|
|
|
+ src: item.imageFile!.fullUrl,
|
|
|
+ title: item.title || '',
|
|
|
+ description: item.description || ''
|
|
|
+ }))}
|
|
|
+ height={800}
|
|
|
+ autoplay={true}
|
|
|
+ interval={3000}
|
|
|
+ circular={true}
|
|
|
+ imageMode="aspectFit"
|
|
|
+ />
|
|
|
+
|
|
|
+ )
|
|
|
+ : (
|
|
|
+ <View className="empty-container">
|
|
|
+ <Text className="empty-text">暂无广告</Text>
|
|
|
+ </View>
|
|
|
+ )}
|
|
|
+ </View>
|
|
|
</View>
|
|
|
|
|
|
- {/* 轮播图 */}
|
|
|
- <View className="swiper-wrap">
|
|
|
- {isAdLoading ? (
|
|
|
+ {/* 页面内容 - 商品列表 */}
|
|
|
+ <View className="home-page-container">
|
|
|
+ {isLoading ? (
|
|
|
<View className="loading-container">
|
|
|
- <Text className="loading-text">广告加载中...</Text>
|
|
|
+ <Text className="loading-text">加载中...</Text>
|
|
|
</View>
|
|
|
- ) : adError ? (
|
|
|
+ ) : error ? (
|
|
|
<View className="error-container">
|
|
|
- <Text className="error-text">广告加载失败</Text>
|
|
|
+ <Text className="error-text">加载失败,请重试</Text>
|
|
|
</View>
|
|
|
- ) : finalImgSrcs && finalImgSrcs.length > 0 ? (
|
|
|
-
|
|
|
- <Carousel
|
|
|
- items={finalImgSrcs.filter(item => item.imageFile?.fullUrl).map(item => ({
|
|
|
- src: item.imageFile!.fullUrl,
|
|
|
- title: item.title || '',
|
|
|
- description: (item as any).description || ''
|
|
|
- }))}
|
|
|
- height={800}
|
|
|
- autoplay={true}
|
|
|
- interval={4000}
|
|
|
- circular={true}
|
|
|
- imageMode="aspectFit"
|
|
|
- />
|
|
|
-
|
|
|
- )
|
|
|
- : (
|
|
|
+ ) : goodsList.length === 0 ? (
|
|
|
<View className="empty-container">
|
|
|
- <Text className="empty-text">暂无广告</Text>
|
|
|
+ <Text className="empty-text">暂无商品</Text>
|
|
|
</View>
|
|
|
- )}
|
|
|
- </View>
|
|
|
- </View>
|
|
|
+ ) : (
|
|
|
+ <>
|
|
|
+ <GoodsList
|
|
|
+ goodsList={goodsList}
|
|
|
+ onClick={handleGoodsClick}
|
|
|
+ onAddCart={handleAddCart}
|
|
|
+ />
|
|
|
|
|
|
- {/* 页面内容 - 商品列表 */}
|
|
|
- <View className="home-page-container">
|
|
|
- {isLoading ? (
|
|
|
- <View className="loading-container">
|
|
|
- <Text className="loading-text">加载中...</Text>
|
|
|
- </View>
|
|
|
- ) : error ? (
|
|
|
- <View className="error-container">
|
|
|
- <Text className="error-text">加载失败,请重试</Text>
|
|
|
- </View>
|
|
|
- ) : goodsList.length === 0 ? (
|
|
|
- <View className="empty-container">
|
|
|
- <Text className="empty-text">暂无商品</Text>
|
|
|
- </View>
|
|
|
- ) : (
|
|
|
- <>
|
|
|
- <GoodsList
|
|
|
- goodsList={goodsList}
|
|
|
- onClick={handleGoodsClick}
|
|
|
- onAddCart={handleAddCart}
|
|
|
- />
|
|
|
+ {/* 加载更多状态 */}
|
|
|
+ {isFetchingNextPage && (
|
|
|
+ <View className="loading-more-container">
|
|
|
+ <Text className="loading-more-text">加载更多...</Text>
|
|
|
+ </View>
|
|
|
+ )}
|
|
|
|
|
|
- {/* 加载更多状态 */}
|
|
|
- {isFetchingNextPage && (
|
|
|
- <View className="loading-more-container">
|
|
|
- <Text className="loading-more-text">加载更多...</Text>
|
|
|
+ {/* 无更多数据状态 */}
|
|
|
+ {!hasNextPage && goodsList.length > 0 && (
|
|
|
+ <View className="no-more-container">
|
|
|
+ <Text className="no-more-text">
|
|
|
+ {`已经到底啦 (共${goodsList.length}件商品)`}
|
|
|
+ </Text>
|
|
|
</View>
|
|
|
)}
|
|
|
-
|
|
|
- {/* 无更多数据状态 */}
|
|
|
- {!hasNextPage && goodsList.length > 0 && (
|
|
|
- <View className="no-more-container">
|
|
|
- <Text className="no-more-text">
|
|
|
- {`已经到底啦 (共${goodsList.length}件商品)`}
|
|
|
- </Text>
|
|
|
- </View>
|
|
|
- )}
|
|
|
</>
|
|
|
)}
|
|
|
<View className='height130'></View>
|
|
|
</View>
|
|
|
-
|
|
|
+
|
|
|
</ScrollView>
|
|
|
</TabBarLayout>
|
|
|
+
|
|
|
+ {/* 隐私政策弹框 - 模态弹框,最上层 */}
|
|
|
+ {showPrivacyModal && (
|
|
|
+ <View className="fixed inset-0 z-[9999] flex items-end justify-center" style={{backgroundColor: 'rgba(0, 0, 0, 0.5)'}}>
|
|
|
+ <View className="bg-white rounded-t-2xl w-full max-h-[70vh] flex flex-col">
|
|
|
+ {/* 标题栏 */}
|
|
|
+ <View className="flex justify-center items-center py-4 border-b border-gray-200 flex-shrink-0">
|
|
|
+ <View className="w-12 h-1 bg-gray-300 rounded-full"></View>
|
|
|
+ </View>
|
|
|
+
|
|
|
+ {/* 内容 */}
|
|
|
+ <View className="p-6 flex-1 overflow-y-auto">
|
|
|
+ {/* 温馨提示标题 */}
|
|
|
+ <View className="flex items-center justify-center mb-6">
|
|
|
+ <Text className="text-lg font-bold text-gray-900">温馨提示</Text>
|
|
|
+ </View>
|
|
|
+
|
|
|
+ {/* 隐私政策内容 */}
|
|
|
+ <View className="space-y-4 mb-8">
|
|
|
+ <Text className="text-sm text-gray-700 leading-relaxed">
|
|
|
+ 亲爱的用户,欢迎使用小程序。我们依据相关法律制定了小程序用户隐私政策,请您在使用我们的产品前仔细阅读并充分理解相关条款,以了解您的权利。
|
|
|
+ </Text>
|
|
|
+ <Text className="text-sm text-gray-700 leading-relaxed">
|
|
|
+ 感谢您的支持与关注,小程序为您提供在线下单服务等功能,在您使用相应功能时,我们会根据服务内容获取必要的个人信息,请您仔细阅读
|
|
|
+ <Text className="text-blue-500">《商家小程序隐私保护声明》</Text>
|
|
|
+ </Text>
|
|
|
+ <Text className="text-sm text-gray-700 leading-relaxed">
|
|
|
+ 如您同意此协议,请点击同意。
|
|
|
+ </Text>
|
|
|
+ </View>
|
|
|
+ </View>
|
|
|
+
|
|
|
+ {/* 按钮组 - 固定在底部 */}
|
|
|
+ <View className="p-6 pt-0 flex-shrink-0">
|
|
|
+ <View className="flex space-x-3">
|
|
|
+ <View
|
|
|
+ className="flex-1 border border-gray-300 text-gray-600 text-center py-3 rounded-lg"
|
|
|
+ onClick={handleRejectPrivacy}
|
|
|
+ >
|
|
|
+ <Text className="text-gray-600">拒绝</Text>
|
|
|
+ </View>
|
|
|
+ <View
|
|
|
+ className="flex-1 bg-green-500 text-white text-center py-3 rounded-lg"
|
|
|
+ onClick={handleAgreePrivacy}
|
|
|
+ >
|
|
|
+ <Text className="text-white font-medium">
|
|
|
+ {isLoggingIn ? '登录中...' : '同意'}
|
|
|
+ </Text>
|
|
|
+ </View>
|
|
|
+ </View>
|
|
|
+ </View>
|
|
|
+ </View>
|
|
|
+ </View>
|
|
|
+ )}
|
|
|
+ </>
|
|
|
)
|
|
|
}
|
|
|
|