浏览代码

项目制作

zxz 3 月之前
父节点
当前提交
5a28f427e3
共有 7 个文件被更改,包括 1498 次插入80 次删除
  1. 12 5
      app/(tabs)/welfare.tsx
  2. 21 17
      app/_layout.tsx
  3. 28 0
      app/test.tsx
  4. 167 49
      app/weal/wish.tsx
  5. 1 1
      components/home/QuickEntry.tsx
  6. 1264 6
      package-lock.json
  7. 5 2
      package.json

+ 12 - 5
app/(tabs)/welfare.tsx

@@ -61,10 +61,14 @@ export default function WelfareScreen() {
         </View>
 
         {/* 标题区域 */}
-        <View style={styles.headSection}>
+        <ImageBackground
+          source={{ uri: Images.welfare.wealTitle }}
+          style={styles.headSection}
+          resizeMode="cover"
+        >
           <Text style={styles.headTitle}>限时福利活动</Text>
           <Text style={styles.headText}>限时活动,不定时开放</Text>
-        </View>
+        </ImageBackground>
 
         {/* 房间列表 */}
         {showSection && (
@@ -79,7 +83,7 @@ export default function WelfareScreen() {
               activeOpacity={0.8}
             >
               <Image
-                source={{ uri: Images.welfare.kaixinRoom1 }}
+                source={{ uri: Images.welfare.indexItem1  }}
                 style={styles.roomImage}
                 contentFit="contain"
               />
@@ -91,7 +95,7 @@ export default function WelfareScreen() {
               activeOpacity={0.8}
             >
               <Image
-                source={{ uri: Images.welfare.kaixinRoom2 }}
+                source={{ uri: Images.welfare.indexItem2  }}
                 style={styles.roomImage}
                 contentFit="contain"
               />
@@ -103,7 +107,7 @@ export default function WelfareScreen() {
               activeOpacity={0.8}
             >
               <Image
-                source={{ uri: Images.welfare.kaixinRoom3 }}
+                source={{ uri: Images.welfare.indexItem3  }}
                 style={styles.roomImage}
                 contentFit="contain"
               />
@@ -159,6 +163,9 @@ const styles = StyleSheet.create({
     fontSize: 15,
     color: '#E85801',
     marginTop: 5,
+    textShadowColor: '#fff',
+    textShadowOffset: { width: 1, height: 2 },
+    textShadowRadius: 1,
   },
   scrollView: {
     flex: 1,

+ 21 - 17
app/_layout.tsx

@@ -5,6 +5,7 @@ import 'react-native-reanimated';
 
 import { AuthProvider } from '@/contexts/AuthContext';
 import { useColorScheme } from '@/hooks/use-color-scheme';
+import { PaperProvider } from 'react-native-paper';
 
 export const unstable_settings = {
   anchor: '(tabs)',
@@ -14,22 +15,25 @@ export default function RootLayout() {
   const colorScheme = useColorScheme();
 
   return (
-    <AuthProvider>
-      <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
-        <Stack>
-          <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
-          <Stack.Screen name="login" options={{ headerShown: false }} />
-          <Stack.Screen name="product/[id]" options={{ headerShown: false }} />
-          <Stack.Screen name="address" options={{ headerShown: false }} />
-          <Stack.Screen name="orders" options={{ headerShown: false }} />
-          <Stack.Screen name="award-detail" options={{ headerShown: false }} />
-          <Stack.Screen name="award-detail-yfs" options={{ headerShown: false }} />
-          <Stack.Screen name="boxInBox" options={{ headerShown: false }} />
-          <Stack.Screen name="weal" options={{ headerShown: false }} />
-          <Stack.Screen name="modal" options={{ presentation: 'modal', title: 'Modal' }} />
-        </Stack>
-        <StatusBar style="auto" />
-      </ThemeProvider>
-    </AuthProvider>
+    <PaperProvider>
+      <AuthProvider>
+        <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
+          <Stack>
+            <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
+            <Stack.Screen name="login" options={{ headerShown: false }} />
+            <Stack.Screen name="product/[id]" options={{ headerShown: false }} />
+            <Stack.Screen name="address" options={{ headerShown: false }} />
+            <Stack.Screen name="orders" options={{ headerShown: false }} />
+            <Stack.Screen name="award-detail" options={{ headerShown: false }} />
+            <Stack.Screen name="award-detail-yfs" options={{ headerShown: false }} />
+            <Stack.Screen name="boxInBox" options={{ headerShown: false }} />
+            <Stack.Screen name="weal" options={{ headerShown: false }} />
+            <Stack.Screen name="test" options={{ headerShown: false }} />
+            <Stack.Screen name="modal" options={{ presentation: 'modal', title: 'Modal' }} />
+          </Stack>
+          <StatusBar style="auto" />
+        </ThemeProvider>
+      </AuthProvider>
+    </PaperProvider>
   );
 }

+ 28 - 0
app/test.tsx

@@ -0,0 +1,28 @@
+import React from 'react';
+import { StyleSheet, Text, View } from 'react-native';
+import { Button } from 'react-native-paper';
+
+export default function TestScreen() {
+  return (
+    <View style={styles.container}>
+      <Text style={styles.text}>React!</Text>
+      <Button mode="contained" onPress={() => console.log('Pressed')}>
+        Click me
+      </Button>
+    </View>
+  );
+}
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    justifyContent: 'center',
+    alignItems: 'center',
+    backgroundColor: '#f0f0f0',
+  },
+  text: {
+    fontSize: 32,
+    fontWeight: 'bold',
+    color: '#333',
+  },
+});

+ 167 - 49
app/weal/wish.tsx

@@ -2,14 +2,14 @@ import { Image } from 'expo-image';
 import { useRouter } from 'expo-router';
 import React, { useCallback, useEffect, useState } from 'react';
 import {
-    Dimensions,
-    ImageBackground,
-    ScrollView,
-    StatusBar,
-    StyleSheet,
-    Text,
-    TouchableOpacity,
-    View,
+  Dimensions,
+  ImageBackground,
+  ScrollView,
+  StatusBar,
+  StyleSheet,
+  Text,
+  TouchableOpacity,
+  View,
 } from 'react-native';
 import { useSafeAreaInsets } from 'react-native-safe-area-context';
 
@@ -27,8 +27,10 @@ const wishImages = {
   addLiBg: `${CDN_BASE}/welfare/addLiBg.png`,
   addClose: `${CDN_BASE}/welfare/addClose.png`,
   addSectionBg: `${CDN_BASE}/welfare/toys/addSectionBg.png`,
+  add: `${CDN_BASE}/welfare/add.png`,
   left: `${CDN_BASE}/box/detail/left.png`,
   right: `${CDN_BASE}/box/detail/right.png`,
+  progressBar: `${CDN_BASE}/welfare/toys/wishProgressBar.png`,
 };
 
 interface WishItem {
@@ -78,54 +80,70 @@ export default function WishScreen() {
   const currentItem = tableData[active];
   const remaining = currentItem ? currentItem.quantity - currentItem.completeQuantity : 0;
 
+  const removeGoods = (item: GoodsItem) => {
+    setGoodsList(goodsList.filter((g) => g.id !== item.id));
+  };
+
   return (
     <View style={styles.container}>
       <StatusBar barStyle="light-content" />
       <ImageBackground source={{ uri: wishImages.bg }} style={styles.background} resizeMode="cover">
-        {/* 部导航 */}
+        {/* 部导航 */}
         <View style={[styles.header, { paddingTop: insets.top }]}>
           <TouchableOpacity style={styles.backBtn} onPress={() => router.back()}>
             <Text style={styles.backText}>←</Text>
           </TouchableOpacity>
-          <Text style={styles.title}>艾斯祈福</Text>
+          <Text style={styles.title}>祈愿</Text>
           <View style={styles.placeholder} />
         </View>
 
+        {/* 规则按钮 */}
+        <TouchableOpacity style={[styles.ruleBtn, { top: insets.top + 160 }]}>
+          <Image source={{ uri: wishImages.rule }} style={styles.ruleBtnImg} contentFit="contain" />
+        </TouchableOpacity>
+
         <ScrollView style={styles.scrollView} showsVerticalScrollIndicator={false}>
           <View style={{ height: insets.top + 50 }} />
 
-          {/* 标题 */}
+          {/* 标题图片 */}
           <View style={styles.titleBox}>
             <Image source={{ uri: wishImages.title }} style={styles.titleImg} contentFit="contain" />
           </View>
 
-          {/* 规则按钮 */}
-          <TouchableOpacity style={styles.ruleBtn}>
-            <Image source={{ uri: wishImages.rule }} style={styles.ruleBtnImg} contentFit="contain" />
-          </TouchableOpacity>
-
-          {/* 商品轮播 */}
+          {/* 卡片轮播区域 */}
           <View style={styles.swiperBox}>
+            {/* 左箭头 */}
             {active > 0 && (
               <TouchableOpacity style={styles.prevBtn} onPress={handlePrev}>
                 <Image source={{ uri: wishImages.left }} style={styles.arrowImg} contentFit="contain" />
               </TouchableOpacity>
             )}
 
+            {/* 卡片 */}
             <View style={styles.cardBox}>
-              {currentItem && (
+              {currentItem ? (
                 <View style={styles.card}>
                   <View style={styles.imgBox}>
                     <Image source={{ uri: currentItem.spu?.cover }} style={styles.spuImage} contentFit="contain" />
+                    {/* 仅剩标签 */}
                     <View style={styles.remainingBox}>
                       <Text style={styles.remainingText}>仅剩:{remaining}</Text>
                     </View>
                   </View>
-                  <Text style={styles.cardName} numberOfLines={1}>{currentItem.name}</Text>
+                  <Text style={styles.cardName} numberOfLines={1}>
+                    {currentItem.name}
+                  </Text>
+                </View>
+              ) : (
+                <View style={styles.card}>
+                  <View style={styles.imgBox}>
+                    <Text style={styles.emptyText}>暂无祈愿商品</Text>
+                  </View>
                 </View>
               )}
             </View>
 
+            {/* 右箭头 */}
             {active < tableData.length - 1 && (
               <TouchableOpacity style={styles.nextBtn} onPress={handleNext}>
                 <Image source={{ uri: wishImages.right }} style={styles.arrowImg} contentFit="contain" />
@@ -133,41 +151,49 @@ export default function WishScreen() {
             )}
           </View>
 
-          {/* 进度条 */}
+          {/* 指示点 */}
+          {tableData.length > 1 && (
+            <View style={styles.dotsBox}>
+              {tableData.map((_, i) => (
+                <View key={i} style={[styles.dot, i === active && styles.dotActive]} />
+              ))}
+            </View>
+          )}
+
+          {/* 进度条区域 */}
           <View style={styles.progressSection}>
             <Text style={styles.progressLabel}>进度:</Text>
             <View style={styles.progressBar}>
               <View style={[styles.progressFill, { width: `${progress}%` }]} />
             </View>
-            <Text style={styles.progressText}>{progress}%</Text>
+            <Text style={styles.progressText}>{progress > 0 ? progress.toFixed(1) : progress}%</Text>
           </View>
 
           {/* 材料添加区域 */}
           <ImageBackground source={{ uri: wishImages.addSectionBg }} style={styles.addSection} resizeMode="stretch">
             <Text style={styles.addTitle}>材料添加</Text>
             <View style={styles.addMain}>
+              {/* 添加按钮 */}
               <TouchableOpacity style={styles.addBtn}>
                 <ImageBackground source={{ uri: wishImages.addBg }} style={styles.addBtnBg} resizeMode="contain">
-                  <Image source={{ uri: `${CDN_BASE}/welfare/add.png` }} style={styles.addIcon} contentFit="contain" />
+                  <Image source={{ uri: wishImages.add }} style={styles.addIcon} contentFit="contain" />
                   <Text style={styles.addBtnText}>添加</Text>
                 </ImageBackground>
               </TouchableOpacity>
+
+              {/* 已添加的材料列表 */}
               <View style={styles.addCenter}>
-                <ScrollView horizontal showsHorizontalScrollIndicator={false}>
+                <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.addScrollContent}>
                   {goodsList.map((item, index) => (
                     <View key={item.id || index} style={styles.addItem}>
                       <ImageBackground source={{ uri: wishImages.addLiBg }} style={styles.addItemBg} resizeMode="contain">
                         <Image source={{ uri: item.spu?.cover }} style={styles.addItemImg} contentFit="cover" />
                       </ImageBackground>
-                      <TouchableOpacity style={styles.closeBtn}>
+                      <TouchableOpacity style={styles.closeBtn} onPress={() => removeGoods(item)}>
                         <Image source={{ uri: wishImages.addClose }} style={styles.closeIcon} contentFit="contain" />
                       </TouchableOpacity>
                     </View>
                   ))}
-                  {/* 空位占位 */}
-                  {Array.from({ length: Math.max(0, 6 - goodsList.length) }).map((_, i) => (
-                    <View key={`empty-${i}`} style={styles.emptySlot} />
-                  ))}
                 </ScrollView>
               </View>
             </View>
@@ -176,12 +202,11 @@ export default function WishScreen() {
           {/* 底部按钮 */}
           <TouchableOpacity style={styles.bottomBtn}>
             <ImageBackground source={{ uri: Images.common.butBgHui }} style={styles.bottomBtnBg} resizeMode="contain">
-              <Text style={styles.bottomBtnText}>还差一点点,去收集</Text>
+              <Text style={styles.bottomBtnText}>{progress >= 100 ? '点亮心愿' : '等待参与'}</Text>
             </ImageBackground>
           </TouchableOpacity>
 
           <Text style={styles.bottomTip}>*本活动最终解释权归本平台所有</Text>
-
           <View style={{ height: 100 }} />
         </ScrollView>
       </ImageBackground>
@@ -210,17 +235,28 @@ const styles = StyleSheet.create({
   placeholder: { width: 40 },
   scrollView: { flex: 1 },
 
+  // 规则按钮
+  ruleBtn: { position: 'absolute', left: 0, zIndex: 99 },
+  ruleBtnImg: { width: 39, height: 20 },
+
+  // 标题
   titleBox: { alignItems: 'center', marginBottom: 10 },
   titleImg: { width: 288, height: 86 },
 
-  ruleBtn: { position: 'absolute', left: 0, top: 180, zIndex: 10 },
-  ruleBtnImg: { width: 39, height: 20 },
-
-  swiperBox: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', paddingHorizontal: 20 },
+  // 轮播区域
+  swiperBox: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    justifyContent: 'center',
+    paddingHorizontal: 20,
+    minHeight: 350,
+    position: 'relative',
+  },
   prevBtn: { position: 'absolute', left: 15, zIndex: 10 },
   nextBtn: { position: 'absolute', right: 15, zIndex: 10 },
   arrowImg: { width: 51, height: 49 },
 
+  // 卡片
   cardBox: { alignItems: 'center' },
   card: { alignItems: 'center' },
   imgBox: {
@@ -233,7 +269,8 @@ const styles = StyleSheet.create({
     alignItems: 'center',
     position: 'relative',
   },
-  spuImage: { width: 200, height: 275 },
+  spuImage: { width: 200, height: 260 },
+  emptyText: { color: '#999', fontSize: 14 },
   remainingBox: {
     position: 'absolute',
     bottom: '15%',
@@ -244,32 +281,113 @@ const styles = StyleSheet.create({
     paddingVertical: 5,
   },
   remainingText: { fontSize: 13, fontWeight: 'bold', color: '#000' },
-  cardName: { color: '#D0D0D0', fontSize: 12, marginTop: 13, textAlign: 'center', maxWidth: 200 },
+  cardName: {
+    color: '#D0D0D0',
+    fontSize: 12,
+    marginTop: 13,
+    textAlign: 'center',
+    maxWidth: 200,
+  },
 
-  progressSection: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', marginTop: 5, paddingHorizontal: 20 },
+  // 指示点
+  dotsBox: { flexDirection: 'row', justifyContent: 'center', marginTop: 10 },
+  dot: { width: 8, height: 8, borderRadius: 4, backgroundColor: '#666', marginHorizontal: 3 },
+  dotActive: { backgroundColor: '#fff' },
+
+  // 进度条
+  progressSection: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    justifyContent: 'center',
+    marginTop: 5,
+    paddingHorizontal: 20,
+  },
   progressLabel: { color: '#fff', fontSize: 12, width: 40 },
-  progressBar: { flex: 1, height: 14, backgroundColor: '#FFEABE', borderWidth: 2, borderColor: '#000', marginHorizontal: 5 },
-  progressFill: { height: '100%', backgroundColor: '#FFAD00', borderRightWidth: 2, borderRightColor: '#000' },
+  progressBar: {
+    flex: 1,
+    height: 14,
+    backgroundColor: '#FFEABE',
+    borderWidth: 2,
+    borderColor: '#000',
+    marginHorizontal: 5,
+    position: 'relative',
+  },
+  progressFill: {
+    height: '100%',
+    backgroundColor: '#FFAD00',
+    borderRightWidth: 2,
+    borderRightColor: '#000',
+  },
   progressText: { color: '#fff', fontSize: 12, width: 40, textAlign: 'right' },
 
-  addSection: { width: SCREEN_WIDTH - 20, height: 124, marginHorizontal: 10, marginTop: 10, paddingTop: 10 },
-  addTitle: { color: '#fff', fontSize: 14, textAlign: 'center', fontWeight: '400', textShadowColor: '#000', textShadowOffset: { width: 1, height: 1 }, textShadowRadius: 1, marginBottom: 7 },
-  addMain: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 25 },
+  // 材料添加区域
+  addSection: {
+    width: SCREEN_WIDTH - 20,
+    height: 124,
+    marginHorizontal: 10,
+    marginTop: 10,
+    paddingTop: 10,
+  },
+  addTitle: {
+    color: '#fff',
+    fontSize: 14,
+    textAlign: 'center',
+    fontWeight: '400',
+    textShadowColor: '#000',
+    textShadowOffset: { width: 1, height: 1 },
+    textShadowRadius: 1,
+    marginBottom: 7,
+  },
+  addMain: {
+    flexDirection: 'row',
+    alignItems: 'center',
+    paddingHorizontal: 25,
+  },
   addBtn: { marginRight: 10 },
-  addBtnBg: { width: 42, height: 42, justifyContent: 'center', alignItems: 'center', paddingTop: 5 },
+  addBtnBg: {
+    width: 42,
+    height: 42,
+    justifyContent: 'center',
+    alignItems: 'center',
+    paddingTop: 5,
+  },
   addIcon: { width: 16, height: 16 },
   addBtnText: { fontSize: 12, color: '#000', fontWeight: '500' },
-  addCenter: { flex: 1, height: 60, backgroundColor: '#FFFBEA', paddingLeft: 20, paddingTop: 7 },
+  addCenter: {
+    flex: 1,
+    height: 60,
+    backgroundColor: '#FFFBEA',
+    paddingTop: 7,
+    paddingLeft: 20,
+  },
+  addScrollContent: { paddingRight: 10 },
   addItem: { position: 'relative', marginRight: 10 },
   addItemBg: { width: 42, height: 42, justifyContent: 'center', alignItems: 'center' },
   addItemImg: { width: '80%', height: '80%' },
   closeBtn: { position: 'absolute', right: 0, top: 0, width: 13, height: 13 },
   closeIcon: { width: '100%', height: '100%' },
-  emptySlot: { width: 42, height: 42, backgroundColor: '#f0f0f0', borderWidth: 1, borderColor: '#ddd', marginRight: 10 },
 
+  // 底部按钮
   bottomBtn: { alignItems: 'center', marginTop: -10 },
-  bottomBtnBg: { width: 160, height: 60, justifyContent: 'center', alignItems: 'center', paddingTop: 4 },
-  bottomBtnText: { color: '#fff', fontSize: 15, fontWeight: 'bold', textShadowColor: '#000', textShadowOffset: { width: 1, height: 1 }, textShadowRadius: 1 },
-
-  bottomTip: { color: 'rgba(255,255,255,0.67)', fontSize: 10, textAlign: 'center', marginTop: 5 },
+  bottomBtnBg: {
+    width: 160,
+    height: 60,
+    justifyContent: 'center',
+    alignItems: 'center',
+    paddingTop: 4,
+  },
+  bottomBtnText: {
+    color: '#fff',
+    fontSize: 15,
+    fontWeight: 'bold',
+    textShadowColor: '#000',
+    textShadowOffset: { width: 1, height: 1 },
+    textShadowRadius: 1,
+  },
+  bottomTip: {
+    color: 'rgba(255,255,255,0.67)',
+    fontSize: 10,
+    textAlign: 'center',
+    marginTop: 5,
+  },
 });

+ 1 - 1
components/home/QuickEntry.tsx

@@ -43,7 +43,7 @@ const styles = StyleSheet.create({
     width: '100%',
     flexDirection: 'row',
     flexWrap: 'wrap',
-    justifyContent: 'flex-start',
+    justifyContent: 'space-around',
   },
   item: {
     width: ITEM_WIDTH,

文件差异内容过多而无法显示
+ 1264 - 6
package-lock.json


+ 5 - 2
package.json

@@ -28,21 +28,24 @@
     "expo-symbols": "~1.0.8",
     "expo-system-ui": "~6.0.9",
     "expo-web-browser": "~15.0.10",
+    "native-base": "^3.4.28",
     "react": "19.1.0",
     "react-dom": "19.1.0",
     "react-native": "0.81.5",
     "react-native-gesture-handler": "~2.28.0",
+    "react-native-paper": "^5.14.5",
     "react-native-reanimated": "~4.1.1",
     "react-native-safe-area-context": "~5.6.0",
     "react-native-screens": "~4.16.0",
+    "react-native-svg": "^13.4.0",
     "react-native-web": "~0.21.0",
     "react-native-worklets": "0.5.1"
   },
   "devDependencies": {
     "@types/react": "~19.1.0",
-    "typescript": "~5.9.2",
     "eslint": "^9.25.0",
-    "eslint-config-expo": "~10.0.0"
+    "eslint-config-expo": "~10.0.0",
+    "typescript": "~5.9.2"
   },
   "private": true
 }

部分文件因为文件数量过多而无法显示