yblunan@gmail.com hace 1 mes
padre
commit
b1de7dfa0e

+ 4 - 4
ios/LoanAssistant.xcodeproj/project.pbxproj

@@ -372,11 +372,11 @@
 				);
 				OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
 				PRODUCT_BUNDLE_IDENTIFIER = com.cdloan.assistant;
-				PRODUCT_NAME = "LoanAssistant";
+				PRODUCT_NAME = LoanAssistant;
 				SWIFT_OBJC_BRIDGING_HEADER = "LoanAssistant/LoanAssistant-Bridging-Header.h";
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1";
+				TARGETED_DEVICE_FAMILY = 1;
 				VERSIONING_SYSTEM = "apple-generic";
 			};
 			name = Debug;
@@ -403,10 +403,10 @@
 				);
 				OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
 				PRODUCT_BUNDLE_IDENTIFIER = com.cdloan.assistant;
-				PRODUCT_NAME = "LoanAssistant";
+				PRODUCT_NAME = LoanAssistant;
 				SWIFT_OBJC_BRIDGING_HEADER = "LoanAssistant/LoanAssistant-Bridging-Header.h";
 				SWIFT_VERSION = 5.0;
-				TARGETED_DEVICE_FAMILY = "1";
+				TARGETED_DEVICE_FAMILY = 1;
 				VERSIONING_SYSTEM = "apple-generic";
 			};
 			name = Release;

+ 3 - 3
ios/Podfile.lock

@@ -2681,7 +2681,7 @@ SPEC CHECKSUMS:
   EXUpdates: 1457ed285cd7c5e9d4c388ace518af55fa4b21ec
   EXUpdatesInterface: 39d05400a9226c438565dbaaa8447b5f0672ba45
   FBLazyVector: 061f518bbd81677ed8a8317e2ae60b8779495808
-  hermes-engine: e4ecc5957f671df3fa4828a33b435bd6955c25a5
+  hermes-engine: f4c4bf9a6979a9d777e0e4696e56be72362744d6
   libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7
   libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f
   libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
@@ -2697,7 +2697,7 @@ SPEC CHECKSUMS:
   React: 3e14066ac707b3e369d09e2e923d8bee7f8c33ff
   React-callinvoker: bd7959a24564feaf5e4c8c11789e64884da13482
   React-Core: f060f4e14e9301685ce63ea65fbe903bf3397e45
-  React-Core-prebuilt: e958d0ca3f8828f37f35c4cff2594b6e080fd991
+  React-Core-prebuilt: 0bd7bd67a89706f97a8d6150c409951862aecfc2
   React-CoreModules: 89a1a544297be88ca4cdff317423f9e0d0c192a4
   React-cxxreact: 81b1bfa305b1b759cc0fe18327644816216e5993
   React-debug: 234fddb309822e13b9b442ef1bdca8d2b8c3cbf2
@@ -2760,7 +2760,7 @@ SPEC CHECKSUMS:
   ReactAppDependencyProvider: 2b19d66e5ddfe8dc7afb6338a4626156cbf2bab1
   ReactCodegen: 43f0948182edee9407c7b977fb059455dfeb9361
   ReactCommon: c6e81cc1ae185fa84863f3ea1d58caac4be741d7
-  ReactNativeDependencies: e3081feec6a8266d996ca8ce3d5707360258baf8
+  ReactNativeDependencies: bcf3a9cabdc4c83653b9318342a4025f5a2effc1
   RNGestureHandler: 0ea8153746a92b3744d4eaadade647debedf646e
   RNReanimated: 86e5991396f1aa514db90d6c79d4c3e37a37bb10
   RNScreens: 02e62f995ceb94ea465864b3bf4d4767b87f0362

+ 1 - 1
src/app/(tabs)/analytics.tsx

@@ -7,7 +7,7 @@ import { useFocusEffect } from 'expo-router';
 import React, { useCallback } from 'react';
 import { Pressable, ScrollView, Text, View } from 'react-native';
 import { SafeAreaView } from 'react-native-safe-area-context';
-import { UploadComponent } from '../credit/upload';
+import { UploadComponent } from '../../components/upload';
 
 
 

+ 5 - 4
src/app/(tabs)/index.tsx

@@ -8,8 +8,8 @@ import { Ionicons } from '@expo/vector-icons';
 import { useFocusEffect } from 'expo-router';
 import { useCallback, useState } from 'react';
 import { Pressable, ScrollView, Text, View } from 'react-native';
-import { SafeAreaView } from 'react-native-safe-area-context';
-import { UploadScreen } from '../credit/upload';
+import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
+import { UploadScreen } from '../../components/upload';
 
 
 
@@ -107,6 +107,7 @@ export default function HomeScreen() {
     useCallback(() => {
       load();
     }, []));
+  const insets = useSafeAreaInsets();
 
 
   const [selectCredit, setSelectCredit] = useState(false);
@@ -258,8 +259,8 @@ export default function HomeScreen() {
         </View>
       </ScrollView>
       <View
-        className="absolute z-50 h-9 w-24 right-12 bottom-24 android:bottom-10 items-center justify-center rounded-full bg-primary"
-
+        className="absolute z-50 h-9 w-24 right-12 items-center justify-center rounded-full bg-primary"
+        style={{ bottom: 64 + insets?.bottom || 0 }}
       >
         <Text className="text-lg font-bold text-white">
           AI 助理

+ 5 - 1
src/app/_layout.tsx

@@ -69,7 +69,7 @@ export default function RootLayout() {
         getGlobalStorage().set('last_update_app', now);
 
         SplashScreen.hide();
-        if (version === application.nativeApplicationVersion) {
+        if (!version || version === application.nativeApplicationVersion) {
           return true;
         }
         const va = version.split('.');
@@ -129,6 +129,10 @@ export default function RootLayout() {
     }
 
     (async () => {
+      if (__DEV__) {
+        setIniting(false);
+        return;
+      }
       if (!await checkApp()) {
         setIniting(false);
         return;

+ 2 - 2
src/app/customer/add.tsx

@@ -18,7 +18,7 @@ import {
   View,
 } from "react-native";
 import { useSafeAreaInsets } from "react-native-safe-area-context";
-import { UploadScreen } from "../credit/upload";
+import { UploadScreen } from "../../components/upload";
 
 type CustomerForm = {
   name: string;
@@ -495,7 +495,7 @@ export default function AddCustomerScreen() {
               <View className="flex-1">
                 <Text className="text-base font-bold text-on-surface">提示:</Text>
                 <Text className="mt-1 text-sm leading-6 text-on-surface-variant">
-                  您可以先<Link href="/credit/select" replace asChild><Text className="text-primary font-bold">上传分析征信</Text></Link>,然后从已分析征信中<Link href="/credit/select" asChild><Text className="text-primary font-bold">选择</Text></Link>一条信息以填充客户资料。
+                  您可以先<Text className="text-primary font-bold" onPress={() => setSelectCredit(true)}>上传分析征信</Text>,然后从已分析征信中<Link href="/credit/select" asChild><Text className="text-primary font-bold">选择</Text></Link>一条信息以填充客户资料。
                 </Text>
               </View>
             </View>

+ 0 - 56
src/components/app-tabs.tsx

@@ -1,56 +0,0 @@
-import { Colors } from '@/constants/theme';
-import { NativeTabs } from 'expo-router/unstable-native-tabs';
-import React from 'react';
-
-
-export default function AppTabs() {
-
-  return (
-    <NativeTabs
-      labelVisibilityMode="labeled"
-      backgroundColor={Colors.background}
-      indicatorColor={Colors.backgroundElement}
-      tintColor={Colors.brand_primary}
-    >
-      <NativeTabs.Trigger name="index">
-        <NativeTabs.Trigger.Label>首页</NativeTabs.Trigger.Label>
-        <NativeTabs.Trigger.Icon
-          src={require('@/assets/images/tabIcons/home.png')}
-          renderingMode="template"
-        />
-      </NativeTabs.Trigger>
-
-      <NativeTabs.Trigger name="customer">
-        <NativeTabs.Trigger.Label>客户</NativeTabs.Trigger.Label>
-        <NativeTabs.Trigger.Icon
-          src={require('@/assets/images/tabIcons/explore.png')}
-          renderingMode="template"
-        />
-      </NativeTabs.Trigger>
-
-      <NativeTabs.Trigger name="analytics">
-        <NativeTabs.Trigger.Label>分析</NativeTabs.Trigger.Label>
-        <NativeTabs.Trigger.Icon
-          src={require('@/assets/images/tabIcons/explore.png')}
-          renderingMode="template"
-        />
-      </NativeTabs.Trigger>
-
-      <NativeTabs.Trigger name="reports">
-        <NativeTabs.Trigger.Label>报表</NativeTabs.Trigger.Label>
-        <NativeTabs.Trigger.Icon
-          src={require('@/assets/images/tabIcons/explore.png')}
-          renderingMode="template"
-        />
-      </NativeTabs.Trigger>
-
-      <NativeTabs.Trigger name="profile">
-        <NativeTabs.Trigger.Label>我的</NativeTabs.Trigger.Label>
-        <NativeTabs.Trigger.Icon
-          src={require('@/assets/images/tabIcons/home.png')}
-          renderingMode="template"
-        />
-      </NativeTabs.Trigger>
-    </NativeTabs>
-  );
-}

+ 0 - 122
src/components/app-tabs.web.tsx

@@ -1,122 +0,0 @@
-import {
-  TabList,
-  TabListProps,
-  Tabs,
-  TabSlot,
-  TabTrigger,
-  TabTriggerSlotProps,
-} from 'expo-router/ui';
-import React from 'react';
-import { Pressable, StyleSheet, View } from 'react-native';
-
-import { ThemedText } from './themed-text';
-import { ThemedView } from './themed-view';
-
-import { MaxContentWidth, Spacing } from '@/constants/theme';
-import { Image, ImageSource } from 'expo-image';
-
-import antdDefaultTheme from '@ant-design/react-native/lib/style/themes/default';
-
-
-export default function AppTabs() {
-  return (
-    <Tabs>
-      <TabSlot detachInactiveScreens style={{ height: '100%', paddingBottom: 44 }} />
-      <TabList asChild>
-        <CustomTabList>
-          <TabTrigger name="home" href="/" asChild>
-            <TabButton icon={require("@/assets/images/tabIcons/home.png")}>首页</TabButton>
-          </TabTrigger>
-          <TabTrigger name="customer" href="/customer" asChild>
-            <TabButton  icon={require("@/assets/images/tabIcons/explore.png")}>客户</TabButton>
-          </TabTrigger>
-          <TabTrigger name="analytics" href="/analytics" asChild>
-            <TabButton icon={require("@/assets/images/tabIcons/explore.png")}>统计</TabButton>
-          </TabTrigger>
-          <TabTrigger name="reports" href="/reports" asChild>
-            <TabButton icon={require("@/assets/images/tabIcons/explore.png")}>分析</TabButton>
-          </TabTrigger>
-          <TabTrigger name="profile" href="/profile" asChild>
-            <TabButton icon={require("@/assets/images/tabIcons/home.png")}>我的</TabButton>
-          </TabTrigger>
-        </CustomTabList>
-      </TabList>
-    </Tabs>
-  );
-}
-
-export function TabButton({ children, isFocused, icon, ...props }: TabTriggerSlotProps & {icon: ImageSource}) {
-  return (
-    <Pressable {...props} style={({ pressed }) => pressed && styles.pressed}>
-      <ThemedView
-        type={'backgroundElement'}
-        style={styles.tabButtonView}>
-          <Image tintColor={isFocused ? antdDefaultTheme.brand_primary : undefined} className="w-6 h-6 mb-1 self-center " source={icon} />
-        <ThemedText type="small" style={{color: isFocused ? antdDefaultTheme.brand_primary : undefined}}>
-          {children}
-        </ThemedText>
-      </ThemedView>
-    </Pressable>
-  );
-}
-
-export function CustomTabList(props: TabListProps) {
-  // const scheme = "light"
-
-
-  return (
-    <View {...props} style={styles.tabListContainer}>
-      <ThemedView type="backgroundElement" style={styles.innerContainer}>
-        
-
-        {props.children}
-
-        
-      </ThemedView>
-    </View>
-  );
-}
-
-const styles = StyleSheet.create({
-  tabListContainer: {
-    position: 'absolute',
-    width: '100%',
-    padding: Spacing.three,
-    justifyContent: 'center',
-    alignItems: 'center',
-    flexDirection: 'row',
-    bottom: 0,
-  },
-  innerContainer: {
-    paddingVertical: Spacing.two,
-    paddingHorizontal: Spacing.five,
-    borderRadius: Spacing.five,
-    flexDirection: 'row',
-    alignItems: 'center',
-    flexGrow: 1,
-    gap: Spacing.two,
-    maxWidth: MaxContentWidth,
-  },
-  brandText: {
-    marginRight: 'auto',
-  },
-  pressed: {
-    opacity: 0.7,
-  },
-  tabButtonView: {
-    paddingVertical: Spacing.one,
-    paddingHorizontal: Spacing.three,
-    borderRadius: Spacing.half,
-    display: 'flex',
-    flexDirection: "column",
-    justifyContent: "center",
-    alignContent: "center",
-  },
-  externalPressable: {
-    flexDirection: 'row',
-    justifyContent: 'center',
-    alignItems: 'center',
-    gap: Spacing.one,
-    marginLeft: Spacing.three,
-  },
-});

+ 0 - 25
src/components/external-link.tsx

@@ -1,25 +0,0 @@
-import { Href, Link } from 'expo-router';
-import { openBrowserAsync, WebBrowserPresentationStyle } from 'expo-web-browser';
-import { type ComponentProps } from 'react';
-
-type Props = Omit<ComponentProps<typeof Link>, 'href'> & { href: Href & string };
-
-export function ExternalLink({ href, ...rest }: Props) {
-  return (
-    <Link
-      target="_blank"
-      {...rest}
-      href={href}
-      onPress={async (event) => {
-        if (process.env.EXPO_OS !== 'web') {
-          // Prevent the default behavior of linking to the default browser on native.
-          event.preventDefault();
-          // Open the link in an in-app browser.
-          await openBrowserAsync(href, {
-            presentationStyle: WebBrowserPresentationStyle.AUTOMATIC,
-          });
-        }
-      }}
-    />
-  );
-}

+ 0 - 35
src/components/hint-row.tsx

@@ -1,35 +0,0 @@
-import React, { type ReactNode } from 'react';
-import { View, StyleSheet } from 'react-native';
-
-import { ThemedText } from './themed-text';
-import { ThemedView } from './themed-view';
-
-import { Spacing } from '@/constants/theme';
-
-type HintRowProps = {
-  title?: string;
-  hint?: ReactNode;
-};
-
-export function HintRow({ title = 'Try editing', hint = 'app/index.tsx' }: HintRowProps) {
-  return (
-    <View style={styles.stepRow}>
-      <ThemedText type="small">{title}</ThemedText>
-      <ThemedView type="backgroundSelected" style={styles.codeSnippet}>
-        <ThemedText themeColor="textSecondary">{hint}</ThemedText>
-      </ThemedView>
-    </View>
-  );
-}
-
-const styles = StyleSheet.create({
-  stepRow: {
-    flexDirection: 'row',
-    justifyContent: 'space-between',
-  },
-  codeSnippet: {
-    borderRadius: Spacing.two,
-    paddingVertical: Spacing.half,
-    paddingHorizontal: Spacing.two,
-  },
-});

+ 0 - 71
src/components/themed-text.tsx

@@ -1,71 +0,0 @@
-import { Platform, StyleSheet, Text, type TextProps } from 'react-native';
-
-import { Colors, Fonts, ThemeColor } from '@/constants/theme';
-
-export type ThemedTextProps = TextProps & {
-  type?: 'default' | 'title' | 'small' | 'smallBold' | 'subtitle' | 'link' | 'linkPrimary' | 'code';
-  themeColor?: ThemeColor;
-};
-
-export function ThemedText({ style, type = 'default', themeColor, ...rest }: ThemedTextProps) {
-
-  return (
-    <Text
-      style={[
-        { color: themeColor ?? Colors.text},
-        type === 'default' && styles.default,
-        type === 'title' && styles.title,
-        type === 'small' && styles.small,
-        type === 'smallBold' && styles.smallBold,
-        type === 'subtitle' && styles.subtitle,
-        type === 'link' && styles.link,
-        type === 'linkPrimary' && styles.linkPrimary,
-        type === 'code' && styles.code,
-        style,
-      ]}
-      {...rest}
-    />
-  );
-}
-
-const styles = StyleSheet.create({
-  small: {
-    fontSize: 14,
-    lineHeight: 20,
-    fontWeight: 500,
-  },
-  smallBold: {
-    fontSize: 14,
-    lineHeight: 20,
-    fontWeight: 700,
-  },
-  default: {
-    fontSize: 16,
-    lineHeight: 24,
-    fontWeight: 500,
-  },
-  title: {
-    fontSize: 48,
-    fontWeight: 600,
-    lineHeight: 52,
-  },
-  subtitle: {
-    fontSize: 32,
-    lineHeight: 44,
-    fontWeight: 600,
-  },
-  link: {
-    lineHeight: 30,
-    fontSize: 14,
-  },
-  linkPrimary: {
-    lineHeight: 30,
-    fontSize: 14,
-    color: '#3c87f7',
-  },
-  code: {
-    fontFamily: Fonts.mono,
-    fontWeight: Platform.select({ android: 700 }) ?? 500,
-    fontSize: 12,
-  },
-});

+ 0 - 14
src/components/themed-view.tsx

@@ -1,14 +0,0 @@
-import { View, type ViewProps } from 'react-native';
-
-import { Colors, ThemeColor } from '@/constants/theme';
-
-export type ThemedViewProps = ViewProps & {
-  lightColor?: string;
-  darkColor?: string;
-  type?: ThemeColor;
-};
-
-export function ThemedView({ style, lightColor, darkColor, type, ...otherProps }: ThemedViewProps) {
-
-  return <View style={[{ backgroundColor: Colors[type as keyof typeof Colors] as any ??Colors.background }, style]} {...otherProps} />;
-}

+ 0 - 25
src/components/ui/action-icon-button.tsx

@@ -1,25 +0,0 @@
-import { Ionicons } from '@expo/vector-icons';
-import React from 'react';
-import { Pressable, Text, View } from 'react-native';
-
-interface ActionIconButtonProps {
-  icon: keyof typeof Ionicons.glyphMap;
-  label: string;
-  onPress?: () => void;
-}
-
-/** 快捷操作按钮 — 圆角图标 + 底部文字 */
-export function ActionIconButton({ icon, label, onPress }: ActionIconButtonProps) {
-  return (
-    <Pressable
-      onPress={onPress}
-      className="items-center gap-2"
-      style={({ pressed }) => ({ opacity: pressed ? 0.7 : 1, transform: [{ scale: pressed ? 0.95 : 1 }] })}
-    >
-      <View className="w-14 h-14 rounded-2xl bg-surface-container-high items-center justify-center">
-        <Ionicons name={icon} size={24} color="#004ac6" />
-      </View>
-      <Text className="text-xs font-medium text-on-surface-variant">{label}</Text>
-    </Pressable>
-  );
-}

+ 0 - 63
src/components/ui/activity-card.tsx

@@ -1,63 +0,0 @@
-import { Image } from 'expo-image';
-import type { ComponentProps } from 'react';
-import React from 'react';
-import { Pressable, Text, View } from 'react-native';
-import { StatusBadge } from './status-badge';
-
-interface ActivityCardProps {
-  avatarUri?: string;
-  name: string;
-  loanType: string;
-  time: string;
-  description: string;
-  badgeText: string;
-  badgeVariant?: ComponentProps<typeof StatusBadge>['variant'];
-  onPress?: () => void;
-}
-
-/**
- * 动态卡片 — 展示客户最近操作动态
- * 采用浅底色和细边框,避免阴影带来的悬浮感。
- */
-export function ActivityCard({
-  avatarUri,
-  name,
-  loanType,
-  time,
-  description,
-  badgeText,
-  badgeVariant = 'secondary',
-  onPress,
-}: ActivityCardProps) {
-  return (
-    <Pressable
-      onPress={onPress}
-      className="bg-surface-container-lowest border border-outline-variant p-4 rounded-xl flex-row items-center gap-4"
-    >
-      {/* 头像 */}
-      <View className="w-12 h-12 rounded-full overflow-hidden bg-surface-container">
-        {avatarUri ? (
-          <Image source={{ uri: avatarUri }} className="w-full h-full" contentFit="cover" />
-        ) : (
-          <View className="w-full h-full bg-secondary-container items-center justify-center">
-            <Text className="text-on-secondary-container font-bold">{name[0]}</Text>
-          </View>
-        )}
-      </View>
-      {/* 正文 */}
-      <View className="flex-1">
-        <View className="flex-row justify-between items-start mb-1">
-          <Text className="font-bold text-on-surface" numberOfLines={1}>
-            {name} - {loanType}
-          </Text>
-          <Text className="text-[10px] text-on-surface-variant ml-2">{time}</Text>
-        </View>
-        <Text className="text-xs text-on-surface-variant" numberOfLines={1}>
-          {description}
-        </Text>
-      </View>
-      {/* 状态 */}
-      <StatusBadge text={badgeText} variant={badgeVariant} />
-    </Pressable>
-  );
-}

+ 0 - 35
src/components/ui/bento-feature.tsx

@@ -1,35 +0,0 @@
-import { Ionicons } from '@expo/vector-icons';
-import React from 'react';
-import { Text, View } from 'react-native';
-
-interface BentoFeatureProps {
-  icon: keyof typeof Ionicons.glyphMap;
-  iconBgClass?: string;
-  iconColor?: string;
-  label: string;
-  value: string;
-}
-
-/**
- * Bento 特性卡片 — 小卡片展示关键指标
- * surface-container-lowest 底色,xl 圆角
- */
-export function BentoFeature({
-  icon,
-  iconBgClass = 'bg-blue-50',
-  iconColor = '#2563eb',
-  label,
-  value,
-}: BentoFeatureProps) {
-  return (
-    <View className="flex-1 bg-surface-container-lowest p-5 rounded-2xl justify-between h-36 shadow-sm">
-      <View className={`w-10 h-10 rounded-xl items-center justify-center ${iconBgClass}`}>
-        <Ionicons name={icon} size={22} color={iconColor} />
-      </View>
-      <View className="mt-2">
-        <Text className="text-xs text-on-surface-variant">{label}</Text>
-        <Text className="font-bold text-lg text-on-surface">{value}</Text>
-      </View>
-    </View>
-  );
-}

+ 0 - 63
src/components/ui/collapsible.tsx

@@ -1,63 +0,0 @@
-import { SymbolView } from 'expo-symbols';
-import { PropsWithChildren, useState } from 'react';
-import { Pressable, StyleSheet } from 'react-native';
-import Animated, { FadeIn } from 'react-native-reanimated';
-
-import { ThemedText } from '@/components/themed-text';
-import { ThemedView } from '@/components/themed-view';
-import { Colors, Spacing } from '@/constants/theme';
-
-export function Collapsible({ children, title }: PropsWithChildren & { title: string }) {
-  const [isOpen, setIsOpen] = useState(false);
-
-  return (
-    <ThemedView>
-      <Pressable
-        style={({ pressed }) => [styles.heading, pressed && styles.pressedHeading]}
-        onPress={() => setIsOpen((value) => !value)}>
-        <ThemedView type="backgroundElement" style={styles.button}>
-          <SymbolView
-            name={{ ios: 'chevron.right', android: 'chevron_right', web: 'chevron_right' }}
-            size={14}
-            weight="bold"
-            tintColor={Colors.text}
-            style={{ transform: [{ rotate: isOpen ? '-90deg' : '90deg' }] }}
-          />
-        </ThemedView>
-
-        <ThemedText type="small">{title}</ThemedText>
-      </Pressable>
-      {isOpen && (
-        <Animated.View entering={FadeIn.duration(200)}>
-          <ThemedView type="backgroundElement" style={styles.content}>
-            {children}
-          </ThemedView>
-        </Animated.View>
-      )}
-    </ThemedView>
-  );
-}
-
-const styles = StyleSheet.create({
-  heading: {
-    flexDirection: 'row',
-    alignItems: 'center',
-    gap: Spacing.two,
-  },
-  pressedHeading: {
-    opacity: 0.7,
-  },
-  button: {
-    width: Spacing.four,
-    height: Spacing.four,
-    borderRadius: 12,
-    justifyContent: 'center',
-    alignItems: 'center',
-  },
-  content: {
-    marginTop: Spacing.three,
-    borderRadius: Spacing.three,
-    marginLeft: Spacing.four,
-    padding: Spacing.four,
-  },
-});

+ 0 - 77
src/components/ui/confirm-dialog.tsx

@@ -1,77 +0,0 @@
-import { Ionicons } from '@expo/vector-icons';
-import React from 'react';
-import { Pressable, Text, View } from 'react-native';
-
-interface ConfirmDialogProps {
-  icon?: keyof typeof Ionicons.glyphMap;
-  title: string;
-  subtitle?: string;
-  /** 详情键值对 */
-  details?: { label: string; value: string }[];
-  cancelText?: string;
-  confirmText: string;
-  onCancel?: () => void;
-  onConfirm?: () => void;
-}
-
-/**
- * 确认操作对话框 — 用于放款、转账等需二次确认的场景
- * 标题 + 详情面板 + 双按钮
- */
-export function ConfirmDialog({
-  icon = 'wallet-outline',
-  title,
-  subtitle,
-  details,
-  cancelText = '稍后再说',
-  confirmText,
-  onCancel,
-  onConfirm,
-}: ConfirmDialogProps) {
-  return (
-    <View className="bg-surface-container-lowest border border-outline-variant p-6 rounded-xl">
-      {/* 头部 */}
-      <View className="flex-row items-start gap-4 mb-6">
-        <View className="p-3 bg-tertiary-fixed rounded-xl">
-          <Ionicons name={icon} size={24} color="#943700" />
-        </View>
-        <View className="flex-1">
-          <Text className="text-lg font-bold text-on-surface">{title}</Text>
-          {subtitle && (
-            <Text className="text-on-surface-variant text-sm mt-1">{subtitle}</Text>
-          )}
-        </View>
-      </View>
-
-      {/* 详情面板 */}
-      {details && details.length > 0 && (
-        <View className="bg-surface-container-low rounded-xl p-4 gap-3 mb-6">
-          {details.map((d, i) => (
-            <View key={i} className="flex-row justify-between items-center">
-              <Text className="text-sm text-on-surface-variant">{d.label}</Text>
-              <Text className="font-bold text-base text-on-surface">{d.value}</Text>
-            </View>
-          ))}
-        </View>
-      )}
-
-      {/* 按钮组 */}
-      <View className="flex-row gap-3">
-        <Pressable
-          onPress={onCancel}
-          className="flex-1 py-4 bg-surface-container-high rounded-xl items-center"
-          style={({ pressed }) => ({ opacity: pressed ? 0.8 : 1 })}
-        >
-          <Text className="font-semibold text-on-surface">{cancelText}</Text>
-        </Pressable>
-        <Pressable
-          onPress={onConfirm}
-          className="flex-1 py-4 bg-primary rounded-xl items-center"
-          style={({ pressed }) => ({ opacity: pressed ? 0.8 : 1 })}
-        >
-          <Text className="font-semibold text-on-primary">{confirmText}</Text>
-        </Pressable>
-      </View>
-    </View>
-  );
-}

+ 0 - 50
src/components/ui/input-field.tsx

@@ -1,50 +0,0 @@
-import React, { useState } from 'react';
-import { Text, TextInput, View, type TextInputProps } from 'react-native';
-
-interface InputFieldProps extends TextInputProps {
-  label?: string;
-  /** 输入框左侧前缀文字,如 "+86" */
-  prefix?: string;
-  /** 输入框右侧操作按钮 */
-  suffix?: React.ReactNode;
-}
-
-/**
- * 填充式输入框 — 遵循 "Ethereal Interface" 设计规范:
- * surface-container-low 底色,聚焦时切换为 surface-container-lowest + primary 微光边框
- */
-export function InputField({ label, prefix, suffix, ...inputProps }: InputFieldProps) {
-  const [focused, setFocused] = useState(false);
-
-  return (
-    <View className="mb-4">
-      {label && (
-        <Text className="text-xs font-bold tracking-wider text-outline uppercase mb-2 ml-1">
-          {label}
-        </Text>
-      )}
-      <View
-        className={`flex-row items-center px-4 py-3.5 rounded-lg ${
-          focused
-            ? 'bg-surface-container-lowest border-2 border-primary/20'
-            : 'bg-surface-container-low border-2 border-transparent'
-        }`}
-      >
-        {prefix && (
-          <>
-            <Text className="text-on-surface font-semibold mr-3">{prefix}</Text>
-            <View className="w-px h-4 bg-outline-variant/30 mr-3" />
-          </>
-        )}
-        <TextInput
-          className="flex-1 text-on-surface font-medium p-0 text-base"
-          placeholderTextColor="#737686"
-          onFocus={() => setFocused(true)}
-          onBlur={() => setFocused(false)}
-          {...inputProps}
-        />
-        {suffix}
-      </View>
-    </View>
-  );
-}

+ 0 - 58
src/components/ui/list-row.tsx

@@ -1,58 +0,0 @@
-import { Ionicons } from '@expo/vector-icons';
-import type { ComponentProps } from 'react';
-import React from 'react';
-import { Pressable, Text, View } from 'react-native';
-import { StatusBadge } from './status-badge';
-
-interface ListRowProps {
-  icon: keyof typeof Ionicons.glyphMap;
-  iconBgClass?: string;
-  iconColor?: string;
-  title: string;
-  subtitle?: string;
-  /** 右侧徽章 */
-  badgeText?: string;
-  badgeVariant?: ComponentProps<typeof StatusBadge>['variant'];
-  /** 自定义右侧元素(如 switch) */
-  right?: React.ReactNode;
-  onPress?: () => void;
-}
-
-/**
- * 通用列表行 — 图标 + 标题/副标题 + 可选徽章/右侧控件
- * 遵循 "No-Divider" 规则,通过 padding 和背景变化分隔
- */
-export function ListRow({
-  icon,
-  iconBgClass = 'bg-blue-50',
-  iconColor = '#2563eb',
-  title,
-  subtitle,
-  badgeText,
-  badgeVariant = 'error',
-  right,
-  onPress,
-}: ListRowProps) {
-  return (
-    <Pressable
-      onPress={onPress}
-      className="flex-row items-center justify-between p-5 active:bg-surface-container-low"
-    >
-      <View className="flex-row items-center gap-4 flex-1">
-        <View className={`w-10 h-10 rounded-xl items-center justify-center ${iconBgClass}`}>
-          <Ionicons name={icon} size={22} color={iconColor} />
-        </View>
-        <View className="flex-1">
-          <Text className="font-semibold text-on-surface">{title}</Text>
-          {subtitle && (
-            <Text className="text-xs text-on-surface-variant mt-0.5">{subtitle}</Text>
-          )}
-        </View>
-      </View>
-      <View className="flex-row items-center gap-2">
-        {badgeText && <StatusBadge text={badgeText} variant={badgeVariant} />}
-        {right ?? <Ionicons name="chevron-forward" size={20} color="#737686" />}
-      </View>
-    </Pressable>
-  );
-}

+ 0 - 24
src/components/ui/primary-button.tsx

@@ -1,24 +0,0 @@
-import React from 'react';
-import { Pressable, Text, type ViewStyle } from 'react-native';
-
-interface PrimaryButtonProps {
-  title: string;
-  onPress?: () => void;
-  className?: string;
-  style?: ViewStyle;
-}
-
-/**
- * 主操作按钮 — 保持纯色和边界清晰,避免过强立体感。
- */
-export function PrimaryButton({ title, onPress, className = '' }: PrimaryButtonProps) {
-  return (
-    <Pressable
-      onPress={onPress}
-      className={`w-full py-4 bg-primary rounded-lg items-center justify-center ${className}`}
-      style={({ pressed }) => ({ opacity: pressed ? 0.92 : 1, transform: [{ scale: pressed ? 0.98 : 1 }] })}
-    >
-      <Text className="text-on-primary font-bold text-base">{title}</Text>
-    </Pressable>
-  );
-}

+ 0 - 49
src/components/ui/success-modal.tsx

@@ -1,49 +0,0 @@
-import { Ionicons } from '@expo/vector-icons';
-import React from 'react';
-import { Pressable, Text, View } from 'react-native';
-
-interface SuccessModalProps {
-  title: string;
-  message: string;
-  primaryAction: string;
-  secondaryAction?: string;
-  onPrimary?: () => void;
-  onSecondary?: () => void;
-}
-
-/**
- * 成功/结果弹窗卡片 — iOS 居中样式
- * 绿色圆形打钩 + 标题 + 描述 + 按钮组
- */
-export function SuccessModal({
-  title,
-  message,
-  primaryAction,
-  secondaryAction,
-  onPrimary,
-  onSecondary,
-}: SuccessModalProps) {
-  return (
-    <View className="bg-surface-container-lowest border border-outline-variant p-8 rounded-xl items-center">
-      <View className="w-16 h-16 rounded-full bg-green-50 items-center justify-center mb-4">
-        <Ionicons name="checkmark-circle" size={40} color="#22c55e" />
-      </View>
-      <Text className="text-xl font-bold text-on-surface mb-2">{title}</Text>
-      <Text className="text-on-surface-variant text-sm text-center leading-relaxed mb-6">
-        {message}
-      </Text>
-      <Pressable
-        onPress={onPrimary}
-        className="w-full py-4 bg-primary rounded-xl items-center mb-3"
-        style={({ pressed }) => ({ opacity: pressed ? 0.9 : 1 })}
-      >
-        <Text className="text-on-primary font-semibold">{primaryAction}</Text>
-      </Pressable>
-      {secondaryAction && (
-        <Pressable onPress={onSecondary} className="w-full py-4 items-center">
-          <Text className="text-primary font-medium">{secondaryAction}</Text>
-        </Pressable>
-      )}
-    </View>
-  );
-}

+ 0 - 24
src/components/ui/toggle-switch.tsx

@@ -1,24 +0,0 @@
-import React from 'react';
-import { Pressable, View } from 'react-native';
-
-interface ToggleSwitchProps {
-  value: boolean;
-  onValueChange?: (v: boolean) => void;
-}
-
-/** iOS 风格开关 — primary 色轨道 + 白色滑块 */
-export function ToggleSwitch({ value, onValueChange }: ToggleSwitchProps) {
-  return (
-    <Pressable
-      onPress={() => onValueChange?.(!value)}
-      className={`w-12 h-7 rounded-full px-1 justify-center ${
-        value ? 'bg-primary' : 'bg-surface-container-highest'
-      }`}
-    >
-      <View
-        className="w-5 h-5 bg-surface-container-lowest rounded-full shadow-sm"
-        style={{ alignSelf: value ? 'flex-end' : 'flex-start' }}
-      />
-    </Pressable>
-  );
-}

+ 0 - 0
src/app/credit/upload.tsx → src/components/upload.tsx


+ 0 - 44
src/components/web-badge.tsx

@@ -1,44 +0,0 @@
-// import { version } from 'expo/package.json';
-// import { Image } from 'expo-image';
-// import React from 'react';
-// import { useColorScheme, StyleSheet } from 'react-native';
-
-// import { ThemedText } from './themed-text';
-// import { ThemedView } from './themed-view';
-
-// import { Spacing } from '@/constants/theme';
-
-// export function WebBadge() {
-//   const scheme = useColorScheme();
-
-//   return (
-//     <ThemedView style={styles.container}>
-//       <ThemedText type="code" themeColor="textSecondary" style={styles.versionText}>
-//         v{version}
-//       </ThemedText>
-//       <Image
-//         source={
-//           scheme === 'dark'
-//             ? require('@/assets/images/expo-badge-white.png')
-//             : require('@/assets/images/expo-badge.png')
-//         }
-//         style={styles.badgeImage}
-//       />
-//     </ThemedView>
-//   );
-// }
-
-// const styles = StyleSheet.create({
-//   container: {
-//     padding: Spacing.five,
-//     alignItems: 'center',
-//     gap: Spacing.two,
-//   },
-//   versionText: {
-//     textAlign: 'center',
-//   },
-//   badgeImage: {
-//     width: 123,
-//     aspectRatio: 123 / 24,
-//   },
-// });

+ 0 - 27
src/hooks/tabview.tsx

@@ -1,27 +0,0 @@
-// hooks/useLazyScreen.ts
-
-import { useFocusEffect } from 'expo-router';
-import { useCallback, useState } from 'react';
-
-/** 懒加载:首次访问才渲染,离开后保留 */
-export function useLazyLoad() {
-    const [hasLoaded, setHasLoaded] = useState(false);
-    useFocusEffect(
-        useCallback(() => {
-            if (!hasLoaded) setHasLoaded(true);
-        }, [hasLoaded])
-    );
-    return hasLoaded;
-}
-
-/** 销毁模式:每次离开销毁,进入重新 mount */
-export function useUnmountOnBlur() {
-    const [isVisible, setIsVisible] = useState(false);
-    useFocusEffect(
-        useCallback(() => {
-            setIsVisible(true);
-            return () => setIsVisible(false);
-        }, [])
-    );
-    return isVisible;
-}

+ 0 - 1
src/hooks/use-color-scheme.ts

@@ -1 +0,0 @@
-export { useColorScheme } from 'react-native';

+ 0 - 21
src/hooks/use-color-scheme.web.ts

@@ -1,21 +0,0 @@
-import { useEffect, useState } from 'react';
-import { useColorScheme as useRNColorScheme } from 'react-native';
-
-/**
- * To support static rendering, this value needs to be re-calculated on the client side for web
- */
-export function useColorScheme() {
-  const [hasHydrated, setHasHydrated] = useState(false);
-
-  useEffect(() => {
-    setHasHydrated(true);
-  }, []);
-
-  const colorScheme = useRNColorScheme();
-
-  if (hasHydrated) {
-    return colorScheme;
-  }
-
-  return 'light';
-}

+ 0 - 6
src/utils/size.ts

@@ -1,6 +0,0 @@
-import { Dimensions } from "react-native";
-
-const Window = Dimensions.get('window');
-const Screen = Dimensions.get('screen');
-
-export { Screen, Window };

+ 0 - 8
src/utils/size.web.ts

@@ -1,8 +0,0 @@
-import { Dimensions } from "react-native";
-const win = Dimensions.get('window');
-const Screen = Dimensions.get('screen');
-const Window = {
-    ...win,
-    width: win.width > 768 ? 768 : win.width
-}
-export { Screen, Window };

BIN
unused-src-files-20260501.zip