index.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import { useRouter } from 'expo-router';
  2. import React, { useCallback, useEffect, useState } from 'react';
  3. import {
  4. ActivityIndicator,
  5. ImageBackground,
  6. RefreshControl,
  7. ScrollView,
  8. StatusBar,
  9. StyleSheet,
  10. Text,
  11. View,
  12. } from 'react-native';
  13. import { useSafeAreaInsets } from 'react-native-safe-area-context';
  14. import { Banner } from '@/components/home/Banner';
  15. import { GoodsList } from '@/components/home/GoodsList';
  16. import { IPFilter } from '@/components/home/IPFilter';
  17. import { QuickEntry } from '@/components/home/QuickEntry';
  18. import { SearchBar } from '@/components/home/SearchBar';
  19. import { Images } from '@/constants/images';
  20. // API 服务
  21. import { getIPList, IPItem } from '@/services/award';
  22. import { BannerItem, getPageConfig, TabItem } from '@/services/base';
  23. import { getGoodsList, GoodsItem, GoodsListParams } from '@/services/mall';
  24. export default function HomeScreen() {
  25. const insets = useSafeAreaInsets();
  26. const router = useRouter();
  27. const [refreshing, setRefreshing] = useState(false);
  28. const [loading, setLoading] = useState(true);
  29. // 数据状态
  30. const [goods, setGoods] = useState<GoodsItem[]>([]);
  31. const [banners, setBanners] = useState<BannerItem[]>([]);
  32. const [tabs, setTabs] = useState<TabItem[]>([]);
  33. const [ipList, setIpList] = useState<IPItem[]>([]);
  34. const [ipIndex, setIpIndex] = useState(0);
  35. // 搜索参数
  36. const [searchParams, setSearchParams] = useState<GoodsListParams>({
  37. current: 1,
  38. size: 15,
  39. keyword: '',
  40. worksId: '',
  41. });
  42. // 加载商品列表
  43. const loadGoods = useCallback(async (params: GoodsListParams, append = false) => {
  44. try {
  45. const data = await getGoodsList(params);
  46. if (append) {
  47. setGoods((prev) => [...prev, ...data]);
  48. } else {
  49. setGoods(data);
  50. }
  51. } catch (error) {
  52. console.error('加载商品失败:', error);
  53. }
  54. }, []);
  55. // 加载 IP 列表
  56. const loadIPList = useCallback(async () => {
  57. try {
  58. const data = await getIPList();
  59. const allIP: IPItem = { id: '', name: '所有IP' };
  60. setIpList([allIP, ...data.filter((item) => item !== null)]);
  61. } catch (error) {
  62. console.error('加载IP列表失败:', error);
  63. }
  64. }, []);
  65. // 加载页面配置
  66. const loadPageConfig = useCallback(async () => {
  67. try {
  68. const bannerConfig = await getPageConfig('index_banner');
  69. if (bannerConfig?.components?.[0]?.elements) {
  70. setBanners(bannerConfig.components[0].elements);
  71. }
  72. const iconConfig = await getPageConfig('index_icon');
  73. if (iconConfig?.components?.[0]?.elements) {
  74. setTabs(iconConfig.components[0].elements);
  75. }
  76. } catch (error) {
  77. console.error('加载页面配置失败:', error);
  78. }
  79. }, []);
  80. // 初始化数据
  81. const initData = useCallback(async () => {
  82. setLoading(true);
  83. await Promise.all([loadGoods(searchParams), loadIPList(), loadPageConfig()]);
  84. setLoading(false);
  85. }, []);
  86. useEffect(() => {
  87. initData();
  88. }, []);
  89. // 下拉刷新
  90. const onRefresh = async () => {
  91. setRefreshing(true);
  92. const newParams = { ...searchParams, current: 1 };
  93. setSearchParams(newParams);
  94. await Promise.all([loadGoods(newParams), loadIPList(), loadPageConfig()]);
  95. setRefreshing(false);
  96. };
  97. // 搜索
  98. const handleSearch = async (keyword: string) => {
  99. const newParams = { ...searchParams, keyword, current: 1 };
  100. setSearchParams(newParams);
  101. await loadGoods(newParams);
  102. };
  103. // Banner 点击
  104. const handleBannerPress = (item: BannerItem) => {
  105. console.log('Banner pressed:', item);
  106. };
  107. // 功能入口点击
  108. const handleQuickEntryPress = (item: TabItem) => {
  109. console.log('Quick entry pressed:', item);
  110. };
  111. // IP 筛选
  112. const handleIPSelect = async (item: IPItem, index: number) => {
  113. setIpIndex(index);
  114. const newParams = { ...searchParams, worksId: item.id, current: 1 };
  115. setSearchParams(newParams);
  116. await loadGoods(newParams);
  117. };
  118. // 商品点击
  119. const handleGoodsPress = (item: GoodsItem) => {
  120. router.push(`/product/${item.id}` as any);
  121. };
  122. if (loading) {
  123. return (
  124. <View style={styles.loadingContainer}>
  125. <ActivityIndicator size="large" color="#fff" />
  126. <Text style={styles.loadingText}>加载中...</Text>
  127. </View>
  128. );
  129. }
  130. return (
  131. <View style={styles.container}>
  132. <StatusBar barStyle="light-content" />
  133. <ImageBackground
  134. source={{ uri: Images.common.indexBg }}
  135. style={styles.background}
  136. resizeMode="cover"
  137. >
  138. <ScrollView
  139. style={styles.scrollView}
  140. contentContainerStyle={{ paddingTop: insets.top + 10 }}
  141. showsVerticalScrollIndicator={false}
  142. refreshControl={
  143. <RefreshControl refreshing={refreshing} onRefresh={onRefresh} tintColor="#fff" />
  144. }
  145. >
  146. {/* 搜索栏 */}
  147. <SearchBar onSearch={handleSearch} />
  148. {/* Banner 轮播 */}
  149. {banners.length > 0 && <Banner data={banners} onPress={handleBannerPress} />}
  150. {/* 功能入口 */}
  151. <View style={styles.section}>
  152. {tabs.length > 0 && <QuickEntry data={tabs} onPress={handleQuickEntryPress} />}
  153. {/* IP 分类筛选 */}
  154. {ipList.length > 0 && (
  155. <IPFilter data={ipList} activeIndex={ipIndex} onSelect={handleIPSelect} />
  156. )}
  157. </View>
  158. {/* 商品列表 */}
  159. <GoodsList data={goods} onItemPress={handleGoodsPress} />
  160. </ScrollView>
  161. </ImageBackground>
  162. </View>
  163. );
  164. }
  165. const styles = StyleSheet.create({
  166. container: {
  167. flex: 1,
  168. backgroundColor: '#000',
  169. },
  170. background: {
  171. flex: 1,
  172. },
  173. scrollView: {
  174. flex: 1,
  175. },
  176. section: {
  177. paddingHorizontal: 10,
  178. },
  179. loadingContainer: {
  180. flex: 1,
  181. backgroundColor: '#1a1a2e',
  182. justifyContent: 'center',
  183. alignItems: 'center',
  184. },
  185. loadingText: {
  186. color: '#fff',
  187. marginTop: 10,
  188. fontSize: 14,
  189. },
  190. });