| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- 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, useRef, useState } from 'react';
- import {
- Alert,
- ImageBackground,
- ScrollView,
- StatusBar,
- StyleSheet,
- Text,
- TouchableOpacity,
- View,
- } from 'react-native';
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
- interface IndexData {
- couponCount?: number;
- inventoryCount?: number;
- magicBalance?: number;
- treasureBoxCount?: number;
- }
- export default function MineScreen() {
- const router = useRouter();
- const insets = useSafeAreaInsets();
- const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
- const [indexData, setIndexData] = useState<IndexData | null>(null);
- const [inviteShow, setInviteShow] = useState(false);
- 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 {
- const [info, magicData] = await Promise.all([
- getUserInfo(),
- getMagicIndex(),
- ]);
- setUserInfo(info);
- setIndexData(magicData);
- // 获取邀请配置
- const inviteConfig = await getParamConfig('invite_config');
- setInviteShow(inviteConfig?.state !== 0);
- // 获取积分显示配置
- const creditConfig = await getParamConfig('credit_show');
- setShowCredit(creditConfig?.state !== 0);
- // 获取备案信息
- const filingConfig = await getParamConfig('beian_icp');
- if (filingConfig) {
- setFilingInfo({ state: filingConfig.state, data: filingConfig.data });
- }
- // 获取钱包显示配置
- const walletConfig = await getParamConfig('wallet_recharge_show');
- setShowWallet(walletConfig?.state === 1);
- } catch (error) {
- console.error('获取数据失败:', error);
- }
- }, []);
- useFocusEffect(
- useCallback(() => {
- loadData();
- }, [loadData])
- );
- const handleLogin = () => {
- if (!userInfo) {
- router.push('/login' as any);
- }
- };
- const handleCopy = async (text: string) => {
- await Clipboard.setStringAsync(text);
- Alert.alert('提示', '复制成功');
- };
- const handleMenuPress = (route: string) => {
- if (!userInfo) {
- router.push('/login' as any);
- return;
- }
- if (route) {
- router.push(route as any);
- }
- };
- const handleMenuItemPress = (type: string) => {
- if (!userInfo && type !== '4_4') {
- router.push('/login' as any);
- return;
- }
- switch (type) {
- case '1_1': // 钱包
- router.push('/wallet' as any);
- break;
- case '2_0': // 全部订单
- router.push('/orders' as any);
- break;
- case '6_1': // 兑换码
- router.push('/exchange' as any);
- break;
- case '4_4': // 联系客服
- kefuRef.current?.open();
- break;
- case '4_3': // 地址
- router.push('/address' as any);
- break;
- case '4_9': // 意见反馈
- router.push('/feedback' as any);
- break;
- case '4_5': // 设置
- router.push('/setting' as any);
- break;
- default:
- break;
- }
- };
- const showNumber = (key: keyof IndexData) => {
- if (!indexData) return '-';
- // Loose check for undefined to match legacy
- if (typeof indexData[key] === 'undefined') return '-';
- return bigNumberTransform(indexData[key]!);
- };
- const bigNumberTransform = (value: number) => {
- const newValue = ['', '', ''];
- let fr = 1000;
- let num = 3;
- let text1 = '';
- let fm = 1;
- // Determine magnitude
- let tempValue = value;
- while (tempValue / fr >= 1) {
- fr *= 10;
- num += 1;
- }
- if (num <= 4) {
- // 千 (Thousand)
- newValue[0] = parseInt(String(value / 1000)) + '';
- newValue[1] = '千';
- } else if (num <= 8) {
- // 万 (Ten Thousand)
- text1 = (num - 4) / 3 > 1 ? '千万' : '万';
- fm = text1 === '万' ? 10000 : 10000000;
- if (value % fm === 0) {
- newValue[0] = parseInt(String(value / fm)) + '';
- } else {
- newValue[0] = String(Math.floor((value / fm) * 10) / 10);
- }
- newValue[1] = text1;
- } else if (num <= 16) {
- // 亿 (Hundred Million)
- text1 = (num - 8) / 3 > 1 ? '千亿' : '亿';
- text1 = (num - 8) / 4 > 1 ? '万亿' : text1;
- text1 = (num - 8) / 7 > 1 ? '千万亿' : text1;
- fm = 1;
- if (text1 === '亿') {
- fm = 100000000;
- } else if (text1 === '千亿') {
- fm = 100000000000;
- } else if (text1 === '万亿') {
- fm = 1000000000000;
- } else if (text1 === '千万亿') {
- fm = 1000000000000000;
- }
- if (value % fm === 0) {
- newValue[0] = parseInt(String(value / fm)) + '';
- } else {
- newValue[0] = String(Math.floor((value / fm) * 10) / 10);
- }
- newValue[1] = text1;
- }
- if (value < 1000) {
- newValue[0] = String(value);
- newValue[1] = '';
- }
- return newValue.join('');
- };
- return (
- <View style={styles.container}>
- <StatusBar barStyle="light-content" />
- <ImageBackground
- source={{ uri: Images.mine.kaixinMineBg }}
- style={styles.background}
- resizeMode="cover"
- >
- {/* 顶部背景 */}
- <Image
- source={{ uri: Images.mine.kaixinMineHeadBg }}
- style={[styles.headerBg, { top: 0 }]}
- contentFit="cover"
- />
- <ScrollView
- style={styles.scrollView}
- contentContainerStyle={{ paddingTop: insets.top }}
- showsVerticalScrollIndicator={false}
- >
- {/* 顶部标题 */}
- <View style={styles.header}>
- <Text style={styles.title}>个人中心</Text>
- </View>
- {/* 用户信息 */}
- <TouchableOpacity
- style={styles.userBox}
- onPress={handleLogin}
- activeOpacity={0.8}
- >
- <ImageBackground
- source={{ uri: Images.mine.avatarBorderBg }}
- style={styles.avatarBorder}
- resizeMode="contain"
- >
- <Image
- source={{ uri: userInfo?.avatar || Images.common.defaultAvatar }}
- style={styles.avatar}
- contentFit="cover"
- />
- </ImageBackground>
- <View style={styles.userInfo}>
- <View style={styles.nicknameRow}>
- <Text style={styles.nickname}>
- {userInfo?.nickname || '暂未登录!'}
- </Text>
- {userInfo && (
- <TouchableOpacity onPress={() => handleMenuPress('/profile')}>
- <Image
- source={{ uri: Images.mine.editIcon }}
- style={styles.editIcon}
- contentFit="contain"
- />
- </TouchableOpacity>
- )}
- </View>
- {userInfo ? (
- <View style={styles.idRow}>
- <TouchableOpacity
- style={styles.idItem}
- onPress={() => handleCopy(userInfo.username || userInfo.id || '')}
- >
- <Text style={styles.idText}>ID:{userInfo.username || userInfo.id}</Text>
- <Image
- source={{ uri: Images.mine.kaixinUserCopyIcon }}
- style={styles.copyIcon}
- contentFit="contain"
- />
- </TouchableOpacity>
- {(userInfo.mobile || userInfo.phone) && (
- <Text style={styles.phoneText}>手机:{userInfo.mobile || userInfo.phone}</Text>
- )}
- </View>
- ) : (
- <Text style={styles.loginTip}>点此登录账号</Text>
- )}
- </View>
- </TouchableOpacity>
- {/* 数据统计 */}
- <ImageBackground
- source={{ uri: Images.mine.kaixinUserDataBg }}
- style={styles.dataBox}
- resizeMode="stretch"
- >
- <TouchableOpacity style={styles.dataItem} onPress={() => handleMenuPress('/coupon')}>
- <Text style={styles.dataNum}>{showNumber('couponCount')}</Text>
- <Text style={styles.dataLabel}>优惠券</Text>
- </TouchableOpacity>
- <TouchableOpacity style={styles.dataItem} onPress={() => handleMenuPress('/store')}>
- <Text style={styles.dataNum}>{showNumber('inventoryCount')}</Text>
- <Text style={styles.dataLabel}>仓库</Text>
- </TouchableOpacity>
- <TouchableOpacity style={styles.dataItem} onPress={() => handleMenuPress('/magic')}>
- <Text style={styles.dataNum}>{showNumber('magicBalance')}</Text>
- <Text style={styles.dataLabel}>果实</Text>
- </TouchableOpacity>
- <TouchableOpacity style={styles.dataItem} onPress={() => handleMenuPress('/boxInBox/boxList')}>
- <Text style={styles.dataNum}>{showNumber('treasureBoxCount')}</Text>
- <Text style={styles.dataLabel}>宝箱</Text>
- </TouchableOpacity>
- </ImageBackground>
- {/* 功能入口 */}
- <ImageBackground
- source={{ uri: Images.mine.userSection1Bg }}
- style={styles.funcBox}
- resizeMode="stretch"
- >
- <View style={styles.funcList}>
- {inviteShow && userInfo && (
- <TouchableOpacity style={styles.funcItem} onPress={() => handleMenuPress('/invite')}>
- <Image source={{ uri: Images.mine.invite }} style={styles.funcIcon} contentFit="contain" />
- <Text style={styles.funcText}>邀新有礼</Text>
- </TouchableOpacity>
- )}
- {showCredit && (
- <TouchableOpacity style={styles.funcItem} onPress={() => handleMenuPress('/integral')}>
- <Image source={{ uri: Images.mine.kaixinintegral }} style={styles.funcIcon} contentFit="contain" />
- <Text style={styles.funcText}>签到领积分</Text>
- </TouchableOpacity>
- )}
- <TouchableOpacity style={styles.funcItem} onPress={() => handleMenuPress('/message')}>
- <Image source={{ uri: Images.mine.message }} style={styles.funcIcon} contentFit="contain" />
- <Text style={styles.funcText}>系统消息</Text>
- </TouchableOpacity>
- <TouchableOpacity style={styles.funcItem} onPress={() => handleMenuPress('/orders')}>
- <Image source={{ uri: Images.mine.kaixinorder }} style={styles.funcIcon} contentFit="contain" />
- <Text style={styles.funcText}>宝箱订单</Text>
- </TouchableOpacity>
- </View>
- </ImageBackground>
- {/* 订单入口 */}
- <ImageBackground
- source={{ uri: Images.welfare.roomBg }}
- style={styles.orderBox}
- resizeMode="stretch"
- >
- <View style={styles.orderList}>
- <TouchableOpacity style={styles.orderItem} onPress={() => handleMenuPress('/orders/shop?active=1')}>
- <Image source={{ uri: Images.mine.order1 }} style={styles.orderImage} contentFit="contain" />
- </TouchableOpacity>
- <TouchableOpacity style={styles.orderItem} onPress={() => handleMenuPress('/orders/shop?active=4')}>
- <Image source={{ uri: Images.mine.order2 }} style={styles.orderImage} contentFit="contain" />
- </TouchableOpacity>
- </View>
- </ImageBackground>
- {/* 菜单列表 */}
- <View style={styles.menuSection}>
- <MenuCell onItemPress={handleMenuItemPress} showWallet={showWallet} />
- </View>
- {/* 备案信息 */}
- {filingInfo && filingInfo.state !== 0 && (
- <View style={styles.filingBox}>
- <Text style={styles.filingText}>{filingInfo.data}</Text>
- </View>
- )}
- {/* 底部占位 */}
- <View style={{ height: 120 }} />
- </ScrollView>
- </ImageBackground>
- {/* 客服弹窗 */}
- <KefuPopup ref={kefuRef} />
- </View>
- );
- }
- const styles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: '#1a1a2e',
- },
- background: {
- flex: 1,
- },
- headerBg: {
- position: 'absolute',
- left: 0,
- width: '100%',
- height: 260,
- zIndex: 1,
- },
- scrollView: {
- flex: 1,
- zIndex: 2,
- },
- header: {
- alignItems: 'center',
- paddingBottom: 10,
- },
- title: {
- color: '#fff',
- fontSize: 16,
- fontWeight: 'bold',
- height: 40,
- lineHeight: 40,
- },
- userBox: {
- flexDirection: 'row',
- paddingHorizontal: 22,
- paddingVertical: 8,
- marginHorizontal: 8,
- },
- avatarBorder: {
- width: 64,
- height: 64,
- marginRight: 21,
- padding: 7.5, // 原项目 15rpx
- justifyContent: 'center',
- alignItems: 'center',
- },
- avatarBox: {
- width: '100%',
- height: '100%',
- borderRadius: 4,
- overflow: 'hidden',
- },
- avatar: {
- width: 49,
- height: 49,
- borderRadius: 2,
- },
- userInfo: {
- flex: 1,
- justifyContent: 'center',
- },
- nicknameRow: {
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'space-between',
- marginBottom: 10,
- },
- nickname: {
- color: '#fff',
- fontSize: 16,
- fontWeight: '700',
- },
- editIcon: {
- width: 67,
- height: 23,
- },
- idRow: {
- flexDirection: 'row',
- alignItems: 'center',
- },
- idItem: {
- flexDirection: 'row',
- alignItems: 'center',
- marginRight: 22,
- },
- idText: {
- color: '#fff',
- fontSize: 11,
- fontWeight: 'bold',
- },
- copyIcon: {
- width: 14,
- height: 14,
- marginLeft: 6,
- },
- phoneText: {
- color: '#fff',
- fontSize: 11,
- fontWeight: 'bold',
- },
- loginTip: {
- color: '#fff',
- fontSize: 12,
- },
- dataBox: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- width: 360,
- height: 57,
- marginHorizontal: 'auto',
- paddingHorizontal: 11,
- alignSelf: 'center',
- },
- dataItem: {
- width: '25%',
- alignItems: 'center',
- justifyContent: 'center',
- paddingTop: 10,
- },
- dataNum: {
- color: '#000',
- fontSize: 16,
- fontWeight: 'bold',
- },
- dataLabel: {
- color: '#934800',
- fontSize: 12,
- },
- funcBox: {
- height: 115, // 原项目 230rpx
- marginHorizontal: 0,
- paddingTop: 20,
- paddingHorizontal: 10,
- },
- funcList: {
- flexDirection: 'row',
- justifyContent: 'space-around',
- },
- funcItem: {
- flex: 1,
- alignItems: 'center',
- },
- funcIcon: {
- width: 59, // 原项目 118rpx
- height: 57, // 原项目 114rpx
- },
- funcText: {
- color: '#000',
- fontSize: 12, // 原项目 24rpx
- fontWeight: '400',
- marginTop: 4,
- },
- orderBox: {
- marginHorizontal: 8,
- paddingTop: 20,
- paddingHorizontal: 16,
- paddingBottom: 25,
- },
- orderList: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- },
- orderItem: {
- width: 161,
- },
- orderImage: {
- width: 161,
- height: 75,
- },
- filingBox: {
- alignItems: 'center',
- marginTop: 20,
- paddingHorizontal: 20,
- },
- filingText: {
- color: '#fff',
- fontSize: 14,
- textAlign: 'center',
- },
- menuSection: {
- backgroundColor: '#fff',
- marginHorizontal: 8,
- marginTop: 10,
- borderRadius: 8,
- overflow: 'hidden',
- },
- });
|