Prechádzať zdrojové kódy

修复个人中心显示-头像资料钱包logo客服按钮仓库 显示样式调整

zbb 3 mesiacov pred
rodič
commit
6ce397b0ad
5 zmenil súbory, kde vykonal 376 pridanie a 121 odobranie
  1. 37 28
      app/(tabs)/mine.tsx
  2. 226 92
      app/store/index.tsx
  3. 109 0
      components/mine/KefuPopup.tsx
  4. 2 1
      constants/images.ts
  5. 2 0
      services/user.ts

+ 37 - 28
app/(tabs)/mine.tsx

@@ -1,7 +1,12 @@
+import { KefuPopup, KefuPopupRef } from '@/components/mine/KefuPopup';
+import { MenuCell } from '@/components/mine/MenuCell';
+import { Images } from '@/constants/images';
+import { getMagicIndex } from '@/services/award';
+import { getParamConfig, getUserInfo, UserInfo } from '@/services/user';
 import * as Clipboard from 'expo-clipboard';
 import { Image } from 'expo-image';
 import { useFocusEffect, useRouter } from 'expo-router';
-import React, { useCallback, useState } from 'react';
+import React, { useCallback, useRef, useState } from 'react';
 import {
     Alert,
     ImageBackground,
@@ -14,11 +19,6 @@ import {
 } from 'react-native';
 import { useSafeAreaInsets } from 'react-native-safe-area-context';
 
-import { MenuCell } from '@/components/mine/MenuCell';
-import { Images } from '@/constants/images';
-import { getMagicIndex } from '@/services/award';
-import { getParamConfig, getUserInfo, UserInfo } from '@/services/user';
-
 interface IndexData {
   couponCount?: number;
   inventoryCount?: number;
@@ -35,6 +35,7 @@ export default function MineScreen() {
   const [showCredit, setShowCredit] = useState(false);
   const [filingInfo, setFilingInfo] = useState<{ state: number; data: string } | null>(null);
   const [showWallet, setShowWallet] = useState(false);
+  const kefuRef = useRef<KefuPopupRef>(null);
 
   const loadData = useCallback(async () => {
     try {
@@ -111,7 +112,7 @@ export default function MineScreen() {
         router.push('/exchange' as any);
         break;
       case '4_4': // 联系客服
-        Alert.alert('联系客服', '客服时间:10:00 ~ 18:00');
+        kefuRef.current?.open();
         break;
       case '4_3': // 地址
         router.push('/address' as any);
@@ -227,17 +228,15 @@ export default function MineScreen() {
             activeOpacity={0.8}
           >
             <ImageBackground
-              source={{ uri: Images.common.defaultAvatar }}
-              style={styles.avatarBox}
-              resizeMode="cover"
+              source={{ uri: Images.mine.avatarBorderBg }}
+              style={styles.avatarBorder}
+              resizeMode="contain"
             >
-              {userInfo?.avatar && (
-                <Image
-                  source={{ uri: userInfo.avatar }}
-                  style={styles.avatar}
-                  contentFit="cover"
-                />
-              )}
+              <Image
+                source={{ uri: userInfo?.avatar || Images.common.defaultAvatar }}
+                style={styles.avatar}
+                contentFit="cover"
+              />
             </ImageBackground>
             <View style={styles.userInfo}>
               <View style={styles.nicknameRow}>
@@ -258,17 +257,17 @@ export default function MineScreen() {
                 <View style={styles.idRow}>
                   <TouchableOpacity
                     style={styles.idItem}
-                    onPress={() => handleCopy(userInfo.id || '')}
+                    onPress={() => handleCopy(userInfo.username || userInfo.id || '')}
                   >
-                    <Text style={styles.idText}>ID:{userInfo.id}</Text>
+                    <Text style={styles.idText}>ID:{userInfo.username || userInfo.id}</Text>
                     <Image
                       source={{ uri: Images.mine.kaixinUserCopyIcon }}
                       style={styles.copyIcon}
                       contentFit="contain"
                     />
                   </TouchableOpacity>
-                  {userInfo.phone && (
-                    <Text style={styles.phoneText}>手机:{userInfo.phone}</Text>
+                  {(userInfo.mobile || userInfo.phone) && (
+                    <Text style={styles.phoneText}>手机:{userInfo.mobile || userInfo.phone}</Text>
                   )}
                 </View>
               ) : (
@@ -363,6 +362,8 @@ export default function MineScreen() {
           <View style={{ height: 120 }} />
         </ScrollView>
       </ImageBackground>
+      {/* 客服弹窗 */}
+      <KefuPopup ref={kefuRef} />
     </View>
   );
 }
@@ -403,16 +404,24 @@ const styles = StyleSheet.create({
     paddingVertical: 8,
     marginHorizontal: 8,
   },
-  avatarBox: {
+  avatarBorder: {
     width: 64,
     height: 64,
-    borderRadius: 32,
-    overflow: 'hidden',
     marginRight: 21,
+    padding: 7.5,  // 原项目 15rpx
+    justifyContent: 'center',
+    alignItems: 'center',
   },
-  avatar: {
+  avatarBox: {
     width: '100%',
     height: '100%',
+    borderRadius: 4,
+    overflow: 'hidden',
+  },
+  avatar: {
+    width: 49,
+    height: 49,
+    borderRadius: 2,
   },
   userInfo: {
     flex: 1,
@@ -448,9 +457,9 @@ const styles = StyleSheet.create({
     fontWeight: 'bold',
   },
   copyIcon: {
-    width: 11,
-    height: 11,
-    marginLeft: 5,
+    width: 14,
+    height: 14,
+    marginLeft: 6,
   },
   phoneText: {
     color: '#fff',

+ 226 - 92
app/store/index.tsx

@@ -27,10 +27,12 @@ import {
 import CheckoutModal from './components/CheckoutModal';
 
 const LEVEL_MAP: Record<string, { title: string; color: string }> = {
-  A: { title: '超神', color: '#ffae00' },
-  B: { title: '欧皇', color: '#ff0000' },
+  A: { title: '超神', color: '#ff0000' },  // 红色
+  B: { title: '欧皇', color: '#ffae00' },  // 黄色
   C: { title: '隐藏', color: '#9745e6' },
   D: { title: '普通', color: '#666666' },
+  SUBSTITUTE: { title: '置换款', color: '#666666' },
+  OTHER: { title: '其他', color: '#666666' },
 };
 
 const LEVEL_TABS = [
@@ -39,11 +41,18 @@ const LEVEL_TABS = [
   { title: '隐藏', value: 'C' },
   { title: '欧皇', value: 'B' },
   { title: '超神', value: 'A' },
+  { title: '其他', value: 'OTHER' },
 ];
 
 const FROM_TYPE_MAP: Record<string, string> = {
   LUCK: '奖池', MALL: '商城', LUCK_ROOM: '福利房', LUCK_WHEEL: '魔天轮',
-  DOLL_MACHINE: '扭蛋', ACTIVITY: '活动', SUBSTITUTE: '置换', TRANSFER: '转赠',
+  DOLL_MACHINE: '扭蛋', ACTIVITY: '活动', SUBSTITUTE: '商品置换', TRANSFER: '商品转赠',
+  DISTRIBUTION: '分销', LUCK_PICKUP: '商品提货', LUCK_EXCHANGE: '商品兑换',
+  RECHARGE: '充值', WITHDRAW: '提现', OFFICIAL: '官方',
+  LUCK_ACTIVITY: '奖池活动', CONSUMPTION_ACTIVITY: '消费活动',
+  NEW_USER_RANK_ACTIVITY: '拉新排名活动', CONSUMPTION_RANK_ACTIVITY: '消费排行榜活动',
+  WHEEL_ACTIVITY: '大转盘活动', TA_ACTIVITY: '勇者之塔活动',
+  ISLAND_ACTIVITY: '海岛活动', REDEEM_CODE_ACTIVITY: '兑换码活动',
 };
 
 interface StoreItem {
@@ -167,7 +176,6 @@ export default function StoreScreen() {
     if (res) {
       const newList = [...list]; newList[index] = { ...item, safeFlag: 1 }; setList(newList);
       setCheckMap(prev => { const m = { ...prev }; delete m[item.id]; return m; });
-      showAlert('已锁定到保险柜');
     }
   };
 
@@ -176,7 +184,6 @@ export default function StoreScreen() {
     if (res) {
       if (mainTabIndex === 1) setList(list.filter((_, i) => i !== index));
       else { const newList = [...list]; newList[index] = { ...item, safeFlag: 0 }; setList(newList); }
-      showAlert('已从保险柜移出');
     }
   };
 
@@ -210,27 +217,30 @@ export default function StoreScreen() {
     const canSelect = mainTabIndex === 1 || item.safeFlag !== 1;
     return (
       <ImageBackground source={{ uri: Images.mine.storeItemBg }} style={styles.cell} resizeMode="stretch">
-        <TouchableOpacity style={styles.cellHeader} onPress={() => canSelect && handleChoose(item)}>
-          <View style={styles.headerLeft}>
-            {canSelect && (
-              <View style={[styles.checkBox, isChecked && styles.checkBoxChecked]}>
-                {isChecked && <Text style={styles.checkIcon}>✓</Text>}
-              </View>
-            )}
-            <Text style={[styles.levelTitle, { color: levelInfo.color }]}>{levelInfo.title}</Text>
-          </View>
-          <TouchableOpacity style={styles.lockBox} onPress={() => item.safeFlag !== 1 ? handleLock(item, index) : handleUnlock(item, index)}>
-            <Text style={styles.lockText}>{item.safeFlag !== 1 ? '锁定' : '解锁'}</Text>
-            <Image source={{ uri: item.safeFlag !== 1 ? Images.mine.lock : Images.mine.unlock }} style={styles.lockIcon} />
+        <View style={styles.cellContent}>
+          <TouchableOpacity style={styles.cellHeader} onPress={() => canSelect && handleChoose(item)}>
+            <View style={styles.headerLeft}>
+              {canSelect && (
+                <View style={[styles.checkBox, isChecked && styles.checkBoxChecked]}>
+                  {isChecked && <Text style={styles.checkIcon}>✓</Text>}
+                </View>
+              )}
+              <Text style={[styles.levelTitle, { color: levelInfo.color }]}>{levelInfo.title}</Text>
+            </View>
+            <TouchableOpacity style={styles.lockBox} onPress={() => item.safeFlag !== 1 ? handleLock(item, index) : handleUnlock(item, index)}>
+              <Text style={styles.lockText}>{item.safeFlag !== 1 ? '锁定' : '解锁'}</Text>
+              <Image source={{ uri: item.safeFlag !== 1 ? Images.mine.lock : Images.mine.unlock }} style={styles.lockIcon} />
+            </TouchableOpacity>
           </TouchableOpacity>
-        </TouchableOpacity>
-        <View style={styles.cellBody}>
-          <ImageBackground source={{ uri: Images.mine.storeGoodsImgBg }} style={styles.goodsImgBg}>
-            <Image source={{ uri: item.spu?.cover }} style={styles.goodsImg} contentFit="contain" />
-          </ImageBackground>
-          <View style={styles.goodsInfo}>
-            <Text style={styles.goodsName} numberOfLines={2}>{item.spu?.name}</Text>
-            <Text style={styles.goodsSource}>从{FROM_TYPE_MAP[item.fromRelationType] || '其他'}获得</Text>
+          <View style={styles.cellBody}>
+            <ImageBackground source={{ uri: Images.mine.storeGoodsImgBg }} style={styles.goodsImgBg}>
+              <Image source={{ uri: item.spu?.cover }} style={styles.goodsImg} contentFit="contain" />
+            </ImageBackground>
+            <View style={styles.goodsInfo}>
+              <Text style={styles.goodsName} numberOfLines={2}>{item.spu?.name}</Text>
+              <Text style={styles.goodsSource}>从{FROM_TYPE_MAP[item.fromRelationType] || '其他'}获得</Text>
+            </View>
+            <Text style={styles.arrow}>{'>'}</Text>
           </View>
         </View>
       </ImageBackground>
@@ -333,20 +343,35 @@ export default function StoreScreen() {
         </View>
         <View style={[styles.content, { paddingTop: insets.top + 50 }]}>
           <View style={styles.mainTabs}>
-            {mainTabs.map((tab, index) => (
-              <TouchableOpacity key={index} style={styles.mainTabItem} onPress={() => setMainTabIndex(index)}>
-                <Text style={[styles.mainTabText, mainTabIndex === index && styles.mainTabTextActive]}>{tab}</Text>
-                {mainTabIndex === index && <View style={styles.mainTabLine} />}
-              </TouchableOpacity>
-            ))}
+            {mainTabs.map((tab, index) => {
+              const isActive = mainTabIndex === index;
+              // Use Yellow L bg for active, Grey (Hui) for inactive
+              const bg = isActive ? Images.common.butBgL : Images.common.butBgHui;
+              return (
+                <TouchableOpacity key={index} style={styles.mainTabItem} onPress={() => setMainTabIndex(index)}>
+                  <ImageBackground source={{ uri: bg }} style={styles.mainTabBg} resizeMode="contain">
+                    <Text style={isActive ? styles.mainTabTextActive : styles.mainTabText}>{tab}</Text>
+                  </ImageBackground>
+                </TouchableOpacity>
+              );
+            })}
           </View>
           {mainTabIndex === 0 && (
             <View style={styles.levelTabs}>
-              {LEVEL_TABS.map((tab, index) => (
-                <TouchableOpacity key={index} style={[styles.levelTabItem, levelTabIndex === index && styles.levelTabItemActive]} onPress={() => setLevelTabIndex(index)}>
-                  <Text style={[styles.levelTabText, levelTabIndex === index && styles.levelTabTextActive]}>{tab.title}</Text>
-                </TouchableOpacity>
-              ))}
+              {LEVEL_TABS.map((tab, index) => {
+                const isActive = levelTabIndex === index;
+                return (
+                  <TouchableOpacity 
+                    key={index} 
+                    style={[styles.levelTabItem, isActive && styles.levelTabItemActive]} 
+                    onPress={() => setLevelTabIndex(index)}
+                  >
+                    {isActive && <View style={styles.decorTL} />}
+                    <Text style={[styles.levelTabText, isActive && styles.levelTabTextActive]}>{tab.title}</Text>
+                    {isActive && <View style={styles.decorBR} />}
+                  </TouchableOpacity>
+                );
+              })}
             </View>
           )}
           <FlatList
@@ -369,12 +394,12 @@ export default function StoreScreen() {
         </View>
         {mainTabIndex !== 2 && list.length > 0 && (
           <View style={[styles.bottomBar, { paddingBottom: insets.bottom + 10 }]}>
-            <Text style={styles.bottomInfoText}>已选 <Text style={styles.bottomInfoCount}>{selectedCount}</Text> 件商品</Text>
             <TouchableOpacity style={styles.bottomBtn} onPress={mainTabIndex === 0 ? handleTakeGoods : handleMoveOutAll}>
-              <ImageBackground source={{ uri: Images.common.loginBtn }} style={styles.bottomBtnBg} resizeMode="contain">
+              <ImageBackground source={{ uri: Images.common.butBgL }} style={styles.bottomBtnBg} resizeMode="stretch">
                 <Text style={styles.bottomBtnText}>{mainTabIndex === 0 ? '立即提货' : '移出保险柜'}</Text>
               </ImageBackground>
             </TouchableOpacity>
+            <Text style={styles.bottomInfoText}>已选 <Text style={styles.bottomInfoCount}>{selectedCount}</Text> 件商品</Text>
           </View>
         )}
         
@@ -400,72 +425,181 @@ const styles = StyleSheet.create({
   title: { color: '#fff', fontSize: 16, fontWeight: 'bold' },
   placeholder: { width: 40 },
   content: { flex: 1 },
-  mainTabs: { flexDirection: 'row', justifyContent: 'space-around', paddingVertical: 10 },
-  mainTabItem: { alignItems: 'center', paddingHorizontal: 15 },
-  mainTabText: { color: '#aaa', fontSize: 14 },
-  mainTabTextActive: { color: '#fff', fontWeight: 'bold', fontSize: 16 },
-  mainTabLine: { width: 20, height: 3, backgroundColor: '#fff', marginTop: 5, borderRadius: 2 },
-  levelTabs: { flexDirection: 'row', paddingHorizontal: 10, paddingVertical: 8 },
-  levelTabItem: { paddingHorizontal: 12, paddingVertical: 6, marginRight: 8, borderRadius: 15, backgroundColor: 'rgba(255,255,255,0.1)' },
-  levelTabItemActive: { backgroundColor: '#FC7D2E' },
-  levelTabText: { color: '#aaa', fontSize: 12 },
-  levelTabTextActive: { color: '#fff' },
-  listContent: { paddingHorizontal: 10, paddingBottom: 150 },
-  cell: { width: '100%', minHeight: 154, marginBottom: 10, padding: 15 },
-  cellHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderBottomWidth: 1, borderBottomColor: 'rgba(0,0,0,0.1)', paddingBottom: 10, marginBottom: 10 },
+  mainTabs: { 
+    flexDirection: 'row', 
+    justifyContent: 'space-between', 
+    paddingHorizontal: 12, // Reduced from 15 to match everything else
+    paddingBottom: 2
+  },
+  mainTabItem: { 
+    width: '30%', 
+    height: 44, // Slightly taller
+    justifyContent: 'center',
+    alignItems: 'center'
+  },
+  mainTabBg: {
+    width: '100%',
+    height: '100%',
+    justifyContent: 'center',
+    alignItems: 'center'
+  },
+  mainTabText: { fontSize: 15, color: '#333', fontWeight: 'bold' },
+  mainTabTextActive: { fontSize: 16, color: '#000', fontWeight: 'bold' },
+  mainTabLine: { display: 'none' },
+  
+  levelTabs: { 
+    flexDirection: 'row', 
+    alignItems: 'center',
+    paddingHorizontal: 10, // Add some padding back for the text content since container is full width
+    paddingVertical: 12,
+    borderBottomWidth: 1,
+    borderBottomColor: 'rgba(255,255,255,0.15)' 
+  },
+  levelTabItem: { 
+    marginRight: 25, 
+    alignItems: 'center',
+    justifyContent: 'center',
+    paddingHorizontal: 6,
+    paddingVertical: 2,
+    position: 'relative',
+    minWidth: 40
+  },
+  levelTabItemActive: { backgroundColor: 'transparent' },
+  levelTabText: { color: '#666', fontSize: 15, fontWeight: 'bold' },
+  levelTabTextActive: { 
+    color: '#ff6b00', 
+    fontSize: 17, 
+    fontWeight: '900',
+    textShadowColor: 'rgba(0, 0, 0, 0.3)',
+    textShadowOffset: { width: 1, height: 1 },
+    textShadowRadius: 1
+  },
+  
+  // Corner Decorations - Larger and jagged simulation
+  decorTL: {
+    position: 'absolute',
+    top: 0,
+    left: -4,
+    width: 0,
+    height: 0,
+    borderTopWidth: 8,
+    borderRightWidth: 8,
+    borderTopColor: '#ff6b00',
+    borderRightColor: 'transparent',
+  },
+  decorBR: {
+    position: 'absolute',
+    bottom: 0,
+    right: -4,
+    width: 0,
+    height: 0,
+    borderBottomWidth: 8,
+    borderLeftWidth: 8,
+    borderBottomColor: '#ff6b00',
+    borderLeftColor: 'transparent',
+  },
+  
+  levelInd: { display: 'none' },
+
+  listContent: { 
+    paddingHorizontal: 8, 
+    paddingVertical: 10,
+    paddingBottom: 150,
+  },
+  
+  cell: { 
+    marginBottom: 0,
+    width: '100%',
+    minHeight: 154, // 原项目 308rpx
+  },
+  cellContent: {
+    paddingTop: 15,
+    paddingBottom: 15,
+    paddingLeft: 18,
+    paddingRight: 18, // 原项目 36rpx
+  },
+  cellHeader: { 
+    flexDirection: 'row', 
+    justifyContent: 'space-between', 
+    alignItems: 'center',
+    marginBottom: 10,
+    paddingBottom: 10,
+    borderBottomWidth: 1,
+    borderBottomColor: 'rgba(0,0,0,0.15)',
+  },
   headerLeft: { flexDirection: 'row', alignItems: 'center' },
-  checkBox: { width: 18, height: 18, borderWidth: 2, borderColor: '#000', backgroundColor: '#fff', marginRight: 10, justifyContent: 'center', alignItems: 'center' },
+  checkBox: { width: 16, height: 16, borderWidth: 2, borderColor: '#000', marginRight: 8, justifyContent: 'center', alignItems: 'center', backgroundColor: '#fff' },
   checkBoxChecked: { backgroundColor: '#000' },
-  checkIcon: { color: '#fff', fontSize: 12 },
-  levelTitle: { fontSize: 16, fontWeight: 'bold' },
+  checkIcon: { color: '#fff', fontSize: 10, fontWeight: 'bold' },
+  levelTitle: { fontSize: 16, fontWeight: 'bold', textShadowColor: '#000', textShadowOffset: { width: 1, height: 1 }, textShadowRadius: 0 },
+  
   lockBox: { flexDirection: 'row', alignItems: 'center' },
-  lockText: { fontSize: 12, color: '#666' },
-  lockIcon: { width: 16, height: 16, marginLeft: 5 },
-  cellBody: { flexDirection: 'row' },
-  goodsImgBg: { width: 65, height: 65, justifyContent: 'center', alignItems: 'center', marginRight: 10 },
-  goodsImg: { width: 55, height: 55 },
-  goodsInfo: { flex: 1, justifyContent: 'space-between' },
-  goodsName: { fontSize: 14, color: '#333', fontWeight: 'bold' },
-  goodsSource: { fontSize: 12, color: '#999' },
-  bottomBar: { position: 'absolute', bottom: 0, left: 0, right: 0, backgroundColor: 'rgba(0,0,0,0.8)', paddingHorizontal: 15, paddingTop: 10, alignItems: 'center' },
-  bottomInfoText: { color: '#fff', fontSize: 14, marginBottom: 10 },
-  bottomInfoCount: { fontWeight: 'bold', fontSize: 18 },
-  bottomBtn: { width: 260 },
-  bottomBtnBg: { width: 260, height: 60, justifyContent: 'center', alignItems: 'center' },
+  lockText: { fontSize: 12, color: '#666', marginRight: 4 },
+  lockIcon: { width: 16, height: 16 },
+  
+  cellBody: { 
+    flexDirection: 'row', 
+    alignItems: 'center',
+  },
+  goodsImgBg: { width: 65, height: 65, justifyContent: 'center', alignItems: 'center', marginRight: 12, padding: 7 },
+  goodsImg: { width: '100%', height: '100%' },
+  goodsInfo: { flex: 1, justifyContent: 'center', paddingRight: 8 },
+  goodsName: { fontSize: 15, color: '#333', fontWeight: 'bold', marginBottom: 6 },
+  goodsDesc: { fontSize: 12, color: '#999' },
+  arrowIcon: { fontSize: 18, color: '#ccc', marginLeft: 8 },
+
+  bottomBar: { 
+    position: 'absolute', 
+    bottom: 0, 
+    left: 0, 
+    right: 0, 
+    height: 100, // Taller for Top Button / Bottom Text layout
+    paddingBottom: 20, 
+    alignItems: 'center',
+    justifyContent: 'center',
+    backgroundColor: 'transparent' // Screenshot shows transparent or gradient? 
+  },
+  bottomBtn: { width: '80%', height: 45, marginBottom: 5 },
+  bottomBtnBg: { width: '100%', height: '100%', justifyContent: 'center', alignItems: 'center' },
   bottomBtnText: { color: '#000', fontSize: 16, fontWeight: 'bold' },
+  bottomInfoText: { color: '#333', fontSize: 12 }, // Text below button
+  bottomInfoCount: { fontWeight: 'bold' },
+  
   emptyBox: { marginTop: 100, alignItems: 'center' },
   emptyText: { color: '#999', fontSize: 14 },
-  // 已提货样式
-  pickupTip: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#1FA4FF', padding: 8, marginBottom: 10, borderRadius: 4 },
-  pickupTipIcon: { fontSize: 14, marginRight: 6 },
-  pickupTipText: { flex: 1, color: '#fff', fontSize: 12 },
-  pickupCell: { width: '100%', marginBottom: 10, padding: 15 },
-  pickupTop: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderBottomWidth: 1, borderBottomColor: 'rgba(0,0,0,0.05)', paddingBottom: 10 },
-  pickupTime: { fontSize: 12, color: '#666' },
+  
+  pickupCell: { width: '100%', marginBottom: 10, padding: 12 },
+  pickupTop: { flexDirection: 'row', justifyContent: 'space-between', paddingBottom: 8, borderBottomWidth: 1, borderBottomColor: '#eee' },
+  pickupTime: { fontSize: 12, color: '#999' },
   pickupStatus: { fontSize: 12, fontWeight: 'bold' },
-  pickupTimeout: { fontSize: 11, color: '#ff6b00', marginTop: 5 },
-  pickupAddress: { flexDirection: 'row', alignItems: 'flex-start', paddingVertical: 14, borderBottomWidth: 1, borderBottomColor: 'rgba(0,0,0,0.05)' },
-  locationIcon: { fontSize: 16, marginRight: 10 },
+  pickupTimeout: { fontSize: 11, color: '#ff6b00', marginTop: 4 },
+  pickupAddress: { flexDirection: 'row', paddingVertical: 10, borderBottomWidth: 1, borderBottomColor: '#eee' },
+  locationIcon: { fontSize: 16, marginRight: 8 },
   addressInfo: { flex: 1 },
-  addressName: { fontSize: 14, color: '#333', fontWeight: 'bold' },
+  addressName: { fontSize: 14, fontWeight: 'bold', color: '#333' },
   addressDetail: { fontSize: 12, color: '#666', marginTop: 4 },
-  pickupGoodsBox: { backgroundColor: '#f8f8f8', borderRadius: 6, padding: 10, marginVertical: 10 },
+  pickupGoodsBox: { paddingVertical: 10 },
   pickupGoodsList: { flexDirection: 'row' },
-  pickupGoodsItem: { width: 79, height: 103, backgroundColor: '#fff', borderRadius: 6, marginRight: 8, alignItems: 'center', justifyContent: 'center', position: 'relative', borderWidth: 1, borderColor: '#eaeaea' },
-  pickupGoodsImg: { width: 73, height: 85 },
-  pickupGoodsCount: { position: 'absolute', top: 0, right: 0, backgroundColor: '#ff6b00', borderRadius: 2, paddingHorizontal: 4, paddingVertical: 2 },
-  pickupGoodsCountText: { color: '#fff', fontSize: 10, fontWeight: 'bold' },
-  pickupOrderRow: { flexDirection: 'row', alignItems: 'center', borderTopWidth: 1, borderTopColor: 'rgba(0,0,0,0.05)', paddingTop: 14 },
+  pickupGoodsItem: { marginRight: 10, alignItems: 'center' },
+  pickupGoodsImg: { width: 60, height: 60, borderRadius: 4 },
+  pickupGoodsCount: { position: 'absolute', right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.5)', paddingHorizontal: 4, borderRadius: 4 },
+  pickupGoodsCountText: { color: '#fff', fontSize: 10 },
+  pickupOrderRow: { flexDirection: 'row', alignItems: 'center', paddingVertical: 8 },
   pickupOrderLabel: { fontSize: 12, color: '#666' },
-  pickupOrderNo: { flex: 1, fontSize: 12, color: '#333', textAlign: 'right' },
-  copyBtn: { backgroundColor: '#1FA4FF', borderRadius: 4, paddingHorizontal: 6, paddingVertical: 4, marginLeft: 5 },
-  copyBtnText: { color: '#fff', fontSize: 12 },
-  pickupInfoRow: { flexDirection: 'row', alignItems: 'center', paddingTop: 10 },
+  pickupOrderNo: { flex: 1, fontSize: 12, color: '#333' },
+  copyBtn: { paddingHorizontal: 8, paddingVertical: 4, backgroundColor: '#f5f5f5', borderRadius: 4 },
+  copyBtnText: { fontSize: 12, color: '#666' },
+  pickupInfoRow: { flexDirection: 'row', paddingVertical: 4 },
   pickupInfoLabel: { fontSize: 12, color: '#666' },
   pickupInfoValue: { fontSize: 12, color: '#333' },
-  pickupBottom: { flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-end', paddingTop: 10 },
+  pickupBottom: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingTop: 10 },
   pickupExpressAmount: { fontSize: 12, color: '#333' },
-  priceText: { color: '#ff6b00' },
-  expressBtn: { backgroundColor: '#1FA4FF', borderRadius: 12, paddingHorizontal: 10, paddingVertical: 6, marginLeft: 15 },
-  expressBtnText: { color: '#fff', fontSize: 12 },
+  priceText: { color: '#ff6b00', fontWeight: 'bold' },
+  expressBtn: { paddingHorizontal: 12, paddingVertical: 6, backgroundColor: '#fec433', borderRadius: 4 },
+  expressBtnText: { fontSize: 12, color: '#000', fontWeight: 'bold' },
+  pickupTip: { flexDirection: 'row', alignItems: 'center', padding: 10, backgroundColor: 'rgba(255,235,200,0.8)', marginHorizontal: 8, borderRadius: 6, marginBottom: 10 },
+  pickupTipIcon: { fontSize: 14, marginRight: 6 },
+  pickupTipText: { flex: 1, fontSize: 11, color: '#ff6b00' },
+  goodsSource: { fontSize: 12, color: '#666', opacity: 0.8 },
+  arrow: { fontSize: 18, color: '#fec433', marginLeft: 8 },
 });

+ 109 - 0
components/mine/KefuPopup.tsx

@@ -0,0 +1,109 @@
+import { Image } from 'expo-image';
+import React, { forwardRef, useImperativeHandle, useState } from 'react';
+import {
+    Modal,
+    StyleSheet,
+    Text,
+    TouchableOpacity,
+    View,
+} from 'react-native';
+
+export interface KefuPopupRef {
+  open: () => void;
+  close: () => void;
+}
+
+interface KefuPopupProps {
+  onClose?: () => void;
+}
+
+export const KefuPopup = forwardRef<KefuPopupRef, KefuPopupProps>(
+  ({ onClose }, ref) => {
+    const [visible, setVisible] = useState(false);
+
+    useImperativeHandle(ref, () => ({
+      open: () => setVisible(true),
+      close: () => {
+        setVisible(false);
+        onClose?.();
+      },
+    }));
+
+    const handleClose = () => {
+      setVisible(false);
+      onClose?.();
+    };
+
+    return (
+      <Modal
+        visible={visible}
+        transparent
+        animationType="fade"
+        onRequestClose={handleClose}
+      >
+        <View style={styles.overlay}>
+          <TouchableOpacity
+            style={styles.backdrop}
+            activeOpacity={1}
+            onPress={handleClose}
+          />
+          <View style={styles.content}>
+            <Image
+              source={{ uri: 'https://cdn.acetoys.cn/kefu/qr.jpg' }}
+              style={styles.qrImage}
+              contentFit="contain"
+            />
+            <Text style={styles.tipText}>长按识别二维码添加客服</Text>
+            <TouchableOpacity style={styles.closeBtn} onPress={handleClose}>
+              <Text style={styles.closeBtnText}>关闭</Text>
+            </TouchableOpacity>
+          </View>
+        </View>
+      </Modal>
+    );
+  }
+);
+
+const styles = StyleSheet.create({
+  overlay: {
+    flex: 1,
+    justifyContent: 'center',
+    alignItems: 'center',
+  },
+  backdrop: {
+    position: 'absolute',
+    top: 0,
+    left: 0,
+    right: 0,
+    bottom: 0,
+    backgroundColor: 'rgba(0, 0, 0, 0.5)',
+  },
+  content: {
+    backgroundColor: '#fff',
+    borderRadius: 15,
+    padding: 20,
+    alignItems: 'center',
+    zIndex: 10,
+  },
+  qrImage: {
+    width: 250,
+    height: 250,
+  },
+  tipText: {
+    fontSize: 14,
+    color: '#666',
+    marginTop: 15,
+    marginBottom: 20,
+  },
+  closeBtn: {
+    backgroundColor: '#FC7D2E',
+    paddingHorizontal: 40,
+    paddingVertical: 12,
+    borderRadius: 25,
+  },
+  closeBtnText: {
+    color: '#fff',
+    fontSize: 16,
+    fontWeight: 'bold',
+  },
+});

+ 2 - 1
constants/images.ts

@@ -210,7 +210,7 @@ export const Images = {
     order2: `${CDN_BASE}/mine/order2.png`,
     dialogContentBg: `${CDN_BASE}/mine/dialogContentBg.png`,
     dialogClose: `${CDN_BASE}/mine/dialogClose.png`,
-    wallet: `${CDN_BASE}/mine/wallet.png`,
+    wallet: 'https://cdn.acetoys.cn/qimiao-supermart/mine/wallet.png', // 原项目钱包图标
     exchangeIcon: `${CDN_BASE}/mine/exchangeIcon.png`,
     customerService: `${CDN_BASE}/mine/customerService.png`,
     address: `${CDN_BASE}/mine/address.png`,
@@ -225,6 +225,7 @@ export const Images = {
     stoneImage: `${CDN_BASE}/mine/stoneImage.png`,
     magicTypeBg: `${CDN_BASE}/mine/magicTypeBg.png`,
     stoneBg: `${CDN_BASE}/mine/stoneBg.png`,
+    avatarBorderBg: `${CDN_BASE}/common/noavatar.png`,  // 头像边框背景
   },
   // 地址相关
   address: {

+ 2 - 0
services/user.ts

@@ -29,9 +29,11 @@ const apis = {
 export interface UserInfo {
   id: string;
   userId?: string;
+  username?: string;
   nickname: string;
   avatar: string;
   phone?: string;
+  mobile?: string;
   realName?: string;
   idNum?: string;
   balance?: number;