在http文件中写预请求,通常用于非hooks的页面请求
// 预请求api 常用于非useHook的页面请求
const preRequest = async (
request: AxiosRequestConfigs,
config: preRequestReturnProp
): Promise<AnyIfEmpty> => {
return new Promise((reslove, reject) => {
preload(request, () => api(requestInit(request, config)))
.then((res: AnyIfEmpty) => {
config.onSuccess && config.onSuccess(res)
reslove(res)
})
.catch((err: AnyIfEmpty) => {
config?.onError && config.onError(err)
reject(err)
})
})
}
export { preRequest }
在api文件中写传入接口
const upLoad = (url: string) => {
return {
url: url + Math.random(),
method: 'post',
type: 'form',
}
}
在data.d.ts文件定义父组件需要传的值
interface UpLoadProps {
setReturn?: object // 返回值的时候增加的除了url其他的东西
urlTitle?: string // 父组件传过来的图片链接名称
uploadAddress?: string // 上传的地址
imageList?: ImageUploadItem[] // 默认图片集合 type node 时候必传
imageSize?: number // 图片大小 宽高
getUploadImg: (arr: ImageUploadItem[]) => void // 返回的数组集合
additionParams?: object // 上传所需额外参数
[key: string]: unknown
customButton?: ReactDOM // 自定义上传按钮
upLoadType?: 'avatar' | 'list' // 头像上传 / 列表上传 默认列表
defaultImg?: string // 默认图 只有头像上传的时候有默认图
uploadTips?: string //上传中的提示语
}
export { UpLoadProps }
在index.tsx中实现
import { upLoad } from '@/api'
import { preRequest } from '@/http'
import { useSafeState, useUpdateEffect } from 'ahooks'
import { ImageUploader, Image, Toast } from 'antd-mobile'
import { mapValues } from 'lodash-es'
import React, { useEffect, useRef } from 'react'
import { createRoot } from 'react-dom/client'
import { UpLoadProps } from './data'
import { ImageUploaderList } from './styled'
import { ReactComponent as UpLoadDetele } from '@svg/upLoadDetele.svg'
import { ReactComponent as UploadAdd } from '@svg/uploadAdd.svg'
const ImageUpload: React.FC<UpLoadProps> = (props) => {
const {
setReturn,
urlTitle='url',
uploadAddress='/api/plugin/sider/customer/uploadFileByPhotos/customerHeadImage',
imageList,
imageSize=106,
getUploadImg,
additionParams,
customButton,
upLoadType= 'list',
defaultImg='',
uploadTips='上传中',
deletable
} = props
// 将父组件传过来的url链接的字段名称改为url
const fileUrlName = (list: { [key: string]: string; url: string }[]) => {
list.map((item: { url: string; [key: string]: string }) => {
item.url = item[urlTitle] || item.url
})
return list
}
// 头像图片,默认图片为父组件传过来的默认图片
const [avatarImg, setAvatarImg] = useSafeState<string>(defaultImg)
// 默认刚进来的父组件传过来的图片
useUpdateEffect(() => {
if (defaultImg) {
setAvatarImg(defaultImg)
}
}, [defaultImg])
// 获取上传列表图片中的dom元素对象
const deteleIcon = useRef<HTMLDivElement>(null)
// 替换或删除图片
const replaceIcon = () => {
const iconList: AnyIfEmpty = deteleIcon.current?.getElementsByClassName(
'adm-image-uploader-cell-delete'
)
mapValues(iconList, (item) => {
if (!item.getElementsByClassName('deleteIcon')[0]) {
createRoot(item).render(<UpLoadDetele className="deleteIcon" />)
}
})
}
// 当变化之后 重新赋值icon
useEffect(() => {
replaceIcon()
}, [imageList, deletable])
// 获取上传头像的dom元素对象
const upLoadImg = useRef<HTMLDivElement>(null)
// 上传的回调
const handlerUpload = async (file: File) => {
type !== 'list' &&
Toast.show({
icon: 'loading',
content: uploadTips, // 上传提示
duration: 5000, // 等待5秒
})
// 如果是上传列表
const Lists: File = file
const index = Lists.name.lastIndexOf('.')
const fileName =
'img' + '.' + Lists.name.substring(index + 1, Lists.name.length)
const newFile = new window.File([Lists], fileName)
const res = await preRequest(upLoad(action), {
defaultParams: {
file: newFile,
...data,
},
onError: () => {
setTimeout(() => {
replactIcon()
})
},
})
Toast.clear()
// 如果是头像上传直接返回string url
if (type !== 'list') {
getUploadImg([{ url: res.fileUrl }])
setAvatarImg(res.fileUrl)
}
return Promise.resolve({
url: res.fileUrl || '',
...(setReturn || {}),
})
}
// 获取当前dom对象并调用此对象的点击事件
const handlerCustom = () => {
const addpicDom = document.getElementsByClassName('adm-image-uploader-input')[0] as HTMLElement
addpicDom.click()
}
return (
<>
{type === 'list' ? (
<div ref={deteleIcon}>
<ImageList
size={imageSize}
{...props}
value={fileUrlName(imageList || [])}
onChange={(val) => {
val.map((item: AnyIfEmpty) => {
item[urlName] = item.url
})
getUploadImg(val)
}}
upload={handlerUpload}
>
<div className="add">
<UploadAdd className="addIcon" />
</div>
</ImageList>
</div>
) : (
<div ref={upLoadImg} onClick={() => handlerCustom()}>
<Image className="avatar" src={avatarImg} fit="fill" />
<ImageUploader
style={{ display: 'none' }}
preview={false}
{...props}
upload={handlerUpload}
>
{customButton}
</ImageUploader>
</div>
)}
</>
)
}
样式显示
import { ImageUploader } from 'antd-mobile'
import styled from 'styled-components'
const ImageList = styled(ImageUploader)<{
size: number
}>`
&& {
--cell-size: ${(p) => p.size + 'px'};
.adm-space {
--gap-vertical: 0;
--gap-horizontal: 4px;
}
.adm-space-item {
margin-bottom: 4px;
&:nth-child(3n) {
margin-right: 0;
}
}
.adm-image-uploader-cell {
border-radius: 4px;
}
.add {
width: ${(p) => p.size + 'px'};
height: ${(p) => p.size + 'px'};
cursor: pointer;
background: #f2f2f2;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
.addIcon {
width: 28px;
height: 28px;
}
}
.adm-image-uploader-cell-delete {
position: absolute;
top: 2px;
right: 2px;
width: 20px;
height: 20px;
background-color: #fff;
border-radius: 50%;
font-size: 0;
cursor: pointer;
.deleteIcon {
width: 20px;
height: 20px;
}
}
}
`
export { ImageList }
1、调用此组件传图片列表
const [imgList, setImgList] = useState<AnyIfEmpty>([]) // 图片列表传接口
const [buttonState, { setTrue, setFalse }] = useBoolean(true) //保存按钮状态 根据场景需要
<ImageUpload
additionParams={{
groupId: paramsId.groupId,
userId: paramsId.userId,
}}
maxCount={9} // 最多上传几张图片,超出数量会自动隐藏上传按钮
urlTitle="mediaUrl"
setReturn={{
mediaType: 1,
}}
imageList={
imgList.length > 0 ? imgList : ''
}
getUploadImg={(val) => {
setImgList(val)
setFalse()
}}
/>
2、调用此组件传头像
<ImageUpload
upLoadType={'avatar'}
uploadTips="头像上传中"
defaultImg={data.Image}
getUploadImg={(val) =>
run({ ImageUrl: val[0].url })
}
additionParams={{ groupId: paramsId.groupId, userId: paramsId.userId }}
/>