--- trigger: manual --- # 文件上传功能实现指南 ## 概述 文件上传是系统中常见的功能,用于上传图片、文档等文件。本指南将详细介绍如何在系统中实现文件上传功能。 ## 技术栈 系统使用的是基于 Taro 的 React 框架,上传组件来自 `@nutui/nutui-react-taro` 库,文件上传通过 Taro 的 uploadFile API 实现。 ## 基本用法 ### 1. 导入相关组件和工具 ```tsx import { Uploader, UploaderFileItem } from "@nutui/nutui-react-taro"; import { uploadFile } from "@/utils/uploader"; ``` ### 2. 基本属性 Uploader 组件常用属性包括: - `value`: 当前已上传的文件列表(UploaderFileItem[]) - `onChange`: 文件列表变化时的回调函数 - `upload`: 自定义上传函数 - `sourceType`: 选择图片的来源(["album", "camera"]) - `maxCount`: 最大上传数量 - `multiple`: 是否支持多选 - `uploadIcon`: 自定义上传图标 - `uploadLabel`: 自定义上传标签文本 ### 3. 基本结构 ```tsx ``` ## 实现步骤 ### 1. 状态管理 定义文件列表状态: ```tsx const [fileList, setFileList] = useState([]); ``` ### 2. 实现上传工具函数 系统中使用统一的上传工具函数 [utils/uploader.ts](file:///D:/xinfaleisheng/ERPTurbo_Client/packages/app-client/src/utils/uploader.ts): ```tsx import Taro from "@tarojs/taro"; import { Toast } from "@nutui/nutui-react-taro"; export const uploadFile = async (file: File | string) => { console.log("file", file); const res = await Taro.uploadFile({ url: process.env.TARO_API_DOMAIN + `/auth/upload`, // @ts-ignore filePath: file.tempFilePath || file, name: "file", header: { saToken: Taro.getStorageSync("saToken"), "Content-Type": "multipart/form-data", }, }); if (res.errMsg == "uploadFile:ok") { const data = JSON.parse(res.data); if (data.errCode == "401") { Taro.removeStorageSync("user"); Toast.show("toast", { icon: "warn", title: "", content: "超时请重试", }); return Promise.reject(); } else { return { url: data?.data, }; } } else { Toast.show("toast", { icon: "fail", title: "", content: "上传失败", }); return Promise.reject(); } }; ``` ### 3. 处理文件变化 实现 onChange 回调函数处理文件变化: ```tsx const handleFileChange = (files: UploaderFileItem[]) => { setFileList(files); // 处理上传后的逻辑 if (files.length > 0 && files[0].status === 'success') { // 文件上传成功,可以保存URL到业务数据中 const fileUrls = files.map(file => file.url).filter(url => url) as string[]; // 保存到业务状态中 setBusinessData(fileUrls); } }; ``` ### 4. 初始化已上传文件 如果有已上传的文件需要显示,需要将其转换为 UploaderFileItem 格式: ```tsx useEffect(() => { if (existingFileUrls && existingFileUrls.length > 0) { const fileList = existingFileUrls.map((url, index) => ({ url: url, name: `file-${index}`, status: 'success' })); setFileList(fileList); } }, [existingFileUrls]); ``` ## 常见使用场景 ### 1. 单图片上传 用于头像上传等只需要一张图片的场景: ```tsx ``` ### 2. 多图片上传 用于需要上传多张图片的场景,如合同、发票等: ```tsx } uploadLabel={ 拍照上传合同 } maxCount={5} upload={uploadFile} multiple /> ``` ### 3. 不同类型文件上传 系统中常见的文件上传场景包括: 1. 空磅照片上传 2. 总磅照片上传 3. 发票照片上传 4. 合同照片上传 5. 头像上传 ## 最佳实践 ### 1. 状态管理 - 使用 useState 管理文件列表状态 - 将上传成功的文件URL保存到业务数据中 - 在组件卸载时清理相关状态 ### 2. 用户体验 - 提供清晰的上传指引说明 - 显示上传进度和状态 - 支持图片预览功能 - 合理设置最大上传数量 ### 3. 错误处理 - 在上传工具函数中统一处理上传错误 - 提供友好的错误提示信息 - 处理网络异常情况 ### 4. 性能优化 - 对大文件上传提供进度提示 - 支持断点续传(如果后端支持) - 合理设置图片压缩参数 ## 注意事项 1. 确保在组件卸载时清理相关状态和副作用 2. 注意文件大小和格式限制 3. 处理上传失败的重试机制 4. 考虑移动端网络环境的适配 5. 遵循系统统一的上传接口规范 ## 示例代码 ```tsx import { View } from "@tarojs/components"; import { Uploader, UploaderFileItem } from "@nutui/nutui-react-taro"; import { useState, useEffect } from "react"; import { uploadFile } from "@/utils/uploader"; export default function UploadExample() { // 头像上传状态 const [avatarList, setAvatarList] = useState([]); // 合同照片上传状态 const [contractImgList, setContractImgList] = useState([]); // 初始化已上传的文件 useEffect(() => { // 假设从服务器获取到已上传的文件URL const existingAvatarUrl = "https://example.com/avatar.jpg"; if (existingAvatarUrl) { setAvatarList([{ url: existingAvatarUrl, name: 'avatar', status: 'success' }]); } }, []); // 处理头像上传变化 const handleAvatarChange = (files: UploaderFileItem[]) => { setAvatarList(files); // 如果上传成功,保存URL到业务数据 if (files.length > 0 && files[0].status === 'success' && files[0].url) { // 保存头像URL到用户信息中 saveAvatarUrl(files[0].url); } }; // 处理合同照片上传变化 const handleContractImgChange = (files: UploaderFileItem[]) => { setContractImgList(files); // 保存所有成功上传的文件URL const urls = files .filter(file => file.status === 'success' && file.url) .map(file => file.url!) as string[]; // 保存到业务数据中 saveContractImgUrls(urls); }; return ( 头像上传 合同照片上传 ); } ```