lv пре 1 месец
родитељ
комит
05c95ffac8
4 измењених фајлова са 47 додато и 20 уклоњено
  1. 2 2
      src/app/(tabs)/index.tsx
  2. 27 6
      src/app/credit/upload.tsx
  3. 5 0
      src/app/customer/add.tsx
  4. 13 12
      src/utils/api.ts

+ 2 - 2
src/app/(tabs)/index.tsx

@@ -91,7 +91,7 @@ function getGreeting() {
 
 export default function HomeScreen() {
   const { data: summary } = useSWC<{
-    pendding: number;
+    pending: number;
     matched: number;
     completed: number;
   }>('home/summary', async () => {
@@ -139,7 +139,7 @@ export default function HomeScreen() {
           <View className="flex-row gap-3">
             <View className="flex-1 rounded-2xl border border-outline-variant bg-surface-container-lowest px-4 py-5">
               <Text className="mb-2 text-base text-on-surface-variant">已分析</Text>
-              <Text className="text-4xl font-bold text-on-surface">{summary ? summary.pendding : '...'}</Text>
+              <Text className="text-4xl font-bold text-on-surface">{summary ? summary.pending : '...'}</Text>
               <View className="mt-4 h-1.5 rounded-full bg-surface-container">
                 <View
                   className="h-full rounded-full bg-secondary"

+ 27 - 6
src/app/credit/upload.tsx

@@ -8,26 +8,46 @@ import { Platform, Pressable, Text, View } from "react-native";
 import api from "@/utils/api";
 import { openSystemSettings } from "@/utils/os";
 import { DocumentPickerAsset, getDocumentAsync } from 'expo-document-picker';
+import { File } from 'expo-file-system';
 import { ImagePickerAsset, launchCameraAsync, launchImageLibraryAsync, requestCameraPermissionsAsync, requestMediaLibraryPermissionsAsync } from 'expo-image-picker';
 import { Link } from "expo-router";
-import { useCallback, useState } from "react";
+import { useCallback, useEffect, useState } from "react";
+
+
 export default function UploadScreen({ visible, onClose }: { visible: boolean, onClose: () => void }) {
 
 
+    useEffect(() => {
+        if (!visible) {
+            setState(0);
+        }
+    }, [visible]);
     const [state, setState] = useState(0);
     const upload = useCallback(async (assets: ImagePickerAsset[] | DocumentPickerAsset[]) => {
         setState(1);
-        const list = assets.map((item) => api.uploadFile('common/upload', item.uri));
+        let att: { url: string, fullurl: string, attid: any } = null!;
+        try {
+            const file = new File(assets[0].uri);
+            // file.type = item.mimeType || 'application/octet-stream';
+            const formData = new FormData();
+            formData.append('file', file);
+            att = await api.uploadFile<{ url: string, fullurl: string, attid: any }>('common/upload', { body: formData });
+
+        } catch (err) {
+            setState(0);
+            console.warn(err);
+            Toast.fail('上传失败,请重试');
+            return;
+        }
         try {
-            const results = await Promise.all(list);
-            await api.post('/credit/a', {
-                fid: results,
+            await api.post('/credit/create', {
+                att: JSON.stringify(att),
             });
             setState(2);
             // eslint-disable-next-line @typescript-eslint/no-unused-vars
         } catch (err) {
             setState(0);
-            Toast.fail('有文件上传失败,请重试');
+            Toast.fail('添加分析到队列失败,请重试');
         }
     }, []);
 
@@ -183,6 +203,7 @@ export default function UploadScreen({ visible, onClose }: { visible: boolean, o
                     </>
                 )
             }
+            <View className="h-8" />
             <Pressable hitSlop={8} className="absolute top-0 right-4" onPress={() => { onClose(); }}>
                 <Icon name="close" size={32} />
             </Pressable>

+ 5 - 0
src/app/customer/add.tsx

@@ -16,6 +16,7 @@ import {
   View,
 } from "react-native";
 import { useSafeAreaInsets } from "react-native-safe-area-context";
+import UploadScreen from "../credit/upload";
 
 type CustomerForm = {
   name: string;
@@ -194,6 +195,7 @@ function PickerField({
 }) {
   const hasValue = Boolean(value);
 
+  const [selectCredit, setSelectCredit] = useState(false);
   return (
     <View className="mb-4">
       <FieldLabel label={label} required={required} />
@@ -361,6 +363,8 @@ export default function AddCustomerScreen() {
       ? "已根据身份证自动识别,可继续手动调整"
       : "填写身份证后可自动识别,也可以手动选择";
 
+
+  const [selectCredit, setSelectCredit] = useState(false);
   return (
     <View className="flex-1 bg-surface">
       <Stack.Screen options={{ title: "添加客户" }} />
@@ -590,6 +594,7 @@ export default function AddCustomerScreen() {
           </UIButton>
         </ScrollView>
       </KeyboardAvoidingView>
+      <UploadScreen visible={selectCredit} onClose={() => setSelectCredit(false)} />
     </View>
   );
 }

+ 13 - 12
src/utils/api.ts

@@ -1,10 +1,9 @@
 import axios, { AxiosRequestConfig } from 'axios';
 import Constants from 'expo-constants';
+import { fetch, FetchRequestInit } from 'expo/fetch';
 import { Platform } from 'react-native';
 import { type AccessToken } from './auth';
 
-import * as fs from 'expo-file-system/legacy';
-
 const { api: apiConfig, jsVersion } = require('@/config.json') as AppConfig;
 let accessToken: AccessToken | undefined | null = null;
 
@@ -67,7 +66,7 @@ export class ApiError extends HttpError {
 
 interface ApiResponse<T> {
     code?: number;
-    message?: string;
+    msg?: string;
     data?: T;
 }
 
@@ -154,6 +153,7 @@ if (__DEV__) {
 type Options = Omit<AxiosRequestConfig, 'headers'> & { headers?: Record<string, any> };
 async function request<T>(url: string, method: 'get' | 'post' | 'put' | 'delete', data?: any, config?: Options): Promise<T> {
     const headers: Record<string, string> = {
+        'Content-Type': 'application/json',
         ...config?.headers
     };
 
@@ -176,7 +176,7 @@ async function request<T>(url: string, method: 'get' | 'post' | 'put' | 'delete'
 
 
         if (`${res?.code}` !== '1') {
-            throw new ApiError(res?.message || response.statusText, res?.code || 0, res?.data);
+            throw new ApiError(res?.msg || response.statusText, res?.code || 0, res?.data);
         }
         return res?.data as T;
     } catch (error) {
@@ -211,26 +211,27 @@ const rawRequest = async <T>(req: AxiosRequestConfig) => {
     return res;
 }
 
-async function uploadFile<T>(url: string, fileUri: string, config?: Options): Promise<T> {
+async function uploadFile<T>(url: string, config?: FetchRequestInit): Promise<T> {
     const headers: Record<string, any> = {
-        'Content-Type': 'multipart/form-data',
+        // 'Content-Type': 'multipart/form-data',
         ...config?.headers,
     };
     const token = getAccessToken();
     if (token?.token) {
         headers['Authorization'] = `Bearer ${token.token}`;
     }
-    const result = await fs.uploadAsync(`${apiConfig.url}${url}`, fileUri, {
-        fieldName: 'file',
-        httpMethod: 'POST',
+    const result = await fetch(`${apiConfig.url}${url}`, {
+        method: 'POST',
+        ... config,
         headers
     });
+    console.log('上传文件响应状态:', result);
     if (result.status !== 200) {
-        throw new HttpError(result.body, result.status);
+        throw new HttpError((await result.bytes()).toString(), result.status);
     }
-    const res = JSON.parse(result.body) as ApiResponse<T>;
+    const res = await result.json() as ApiResponse<T>;
     if (`${res?.code}` !== '1') {
-        throw new ApiError(res?.message || 'upload error', res?.code || 0, res?.data);
+        throw new ApiError(res?.msg || 'upload error', res?.code || 0, res?.data);
     }
     return res.data as T;
 }