import cx from 'classnames' ; import React , { useRef } from 'react' ; import { uniqueId } from 'lodash-es' ; import { useControllableState } from '../../hooks' ; import { FormControlOnChangeHandler , StringOrNumber } from '../../types' ; import { RexFile } from './types' ; async function defaultEmptyRequest ( ) { return Promise . reject ( Error ( '请传入自定义 request 方法' ) ) ; } function normalizeFile ( file ) { return { id : uniqueId ( 'file_' ) , name : file . name , size : file . size , type : file . type , file : file , } ; } param list1 param list2 param key returns function mergeCollections < T > ( list1 , list2 , key ) { const map = new Map ( ) ; ( list1 || [ ] ) . forEach ( ( item ) => { map . set ( item [ key ] , item ) ; } ) ; ( list2 || [ ] ) . forEach ( ( item ) => { map . set ( item [ key ] , { ... map . get ( item [ key ] ) , ... item , } ) ; } ) ; return Array . from ( map . values ( ) ) ; } param request returns function getUpload ( request ) { return async function ( data ) { const formData = new FormData ( ) ; Object . keys ( data ) . forEach ( ( key ) => { formData . append ( key , data [ key ] ) ; } ) ; let ret ; try { ret = await request ( { data : formData , headers : { 'Content-Type' : 'multipart/form-data' , } , } ) ; } catch ( err ) { ret = { error : err , status : 'error' , } ; } if ( ! ret ) { return data ; } return { ... data , status : 'success' , url : ret . url , downloadUrl : ret ?. downloadUrl || ret . url , previewUrl : ret ?. previewUrl || ret . url , } as RexFile ; } ; } interface RequestReturn { url : string ; downloadUrl ? : string ; previewUrl ? : string ; [ key ] : string ; } interface RequestOptions { data ? : FormData ; headers ? : any ; } type RequestType = ( options : RequestOptions ) => Promise < RequestReturn > ; export interface UseFilePickerProps { /**
     * 受控值
     */ value ? : RexFile [ ] ; /**
     * 非受控默认值
     */ defaultValue ? : RexFile [ ] ; /**
     * 文件上传成功后的回调
     */ onChange ? : FormControlOnChangeHandler < RexFile [ ] > ; /**
     * 删除文件后的回调
     */ onRemove ? : FormControlOnChangeHandler < RexFile > ; /**
     * 自定义请求方法
     */ request ? : RequestType ; /**
     * 是否可以选择多个文件
     */ multiple ? : boolean ; /**
     * 可以选择的文件类型，例如 `image/*`, `video/*`
     */ accept ? : string ; /**
     * 是否禁用
     */ disabled ? : boolean ; /**
     * 自定义样式名
     */ className ? : string ; } export function useFilePicker ( props ) { const { value : valueProp , defaultValue = [ ] , onChange , onRemove , request = defaultEmptyRequest , multiple , accept , disabled , className , ... htmlProps } = props ; const inputRef = useRef < HTMLInputElement > ( ) ; const [ value , updateValue ] = useControllableState < RexFile [ ] > ( { name : 'FilePicker' , value : valueProp , defaultValue , onChange , } ) ; const isControlled = valueProp === undefined ; const [ displayValue , updateDisplayValue ] = useControllableState < RexFile [ ] > ( { name : 'FilePickerList' , value : isControlled ? undefined : value , defaultValue : isControlled ? value : undefined , } ) ; const upload = getUpload ( request as RequestType ) ; const getInputProps = ( props ? ) => { return { ... props , ref : inputRef , type : 'file' , multiple , accept , disabled , onChange : async ( e ) => { const rawFiles = ( e . target as HTMLInputElement ) . files ; const files = Array . from ( rawFiles ) . map ( normalizeFile ) ; const list = mergeCollections < RexFile > ( files , value , 'id' ) ; updateDisplayValue ( list ) ; const rets = await Promise . all ( files . map ( async ( file ) => { return upload ( file ) ; } ) ) ; const updatedList = mergeCollections < RexFile > ( list , rets , 'id' ) ; updateDisplayValue ( updatedList ) ; updateValue ( updatedList ) ; } , } ; } ; const getListProps = ( props ? ) => { return { ... props , disabled , items : displayValue || [ ] , onRemove : ( id ) => { const removed = value . find ( ( item ) => item . id === id ) ; onRemove ( removed ) ; const filtered = value . filter ( ( item ) => item . id !== id ) ; updateDisplayValue ( filtered ) ; updateValue ( filtered ) ; } , } ; } ; const getRootProps = ( props ? ) => { return { ... props , ... htmlProps , className : cx ( { 'rex-file-picker' : true , 'rex-disabled' : disabled , } , className ) , } ; } ; const getSelectorProps = ( props ? ) => { return { ... props , onClick ( ) { inputRef . current . click ( ) ; } , } ; } ; const getTriggerProps = ( ) => { return { inputRef , disabled , } ; } ; return { getRootProps , getSelectorProps , getTriggerProps , getInputProps , getListProps , } ; }