uploadfileComponent
// 上傳佐證組件代碼
import Taro, { Component } from '@tarojs/taro'
import { connect } from '@tarojs/redux'
import { View, Input, Image, Text } from '@tarojs/components'
import { AtIcon, AtProgress } from 'taro-ui'
import _ from 'lodash'
import PropTypes from 'prop-types'
import { getUploadProofUrl } from '../../utils/utils'
import './index.scss'
@connect(({ loading }) => ({
loading: loading.effects['global/uploadProof']
}))
export default class GtUploadFile extends Component {
static propTypes = {
existingFiles: PropTypes.array,//已經(jīng)存在的文件
uploadFileType: PropTypes.string,//上傳文件類型控制,通過uploadFileType控制多個(gè)類型以逗號(hào)隔開,示例:uploadFileType:"image/gif,image/jp2"
maxCount: PropTypes.number,//最大上傳圖片數(shù)
multiple: PropTypes.bool,//是否支持多選
onUploadFile: PropTypes.func, // 點(diǎn)擊上傳按鈕時(shí)觸發(fā)的事件
onConfirom: PropTypes.func, // 點(diǎn)擊刪除按鈕時(shí)觸發(fā)的事件
onRemoveImage: PropTypes.func, // 點(diǎn)擊刪除按鈕時(shí)觸發(fā)的事件
tempFile: PropTypes.bool,//是文件為true 圖片為false
recTypeId: PropTypes.number,
recId: PropTypes.number,
subRecTypeId: PropTypes.number,
subRecId: PropTypes.number,
}
static defaultProps = {
existingFiles: [],
uploadFileType: 'image/*',
maxCount: 1, //最大上傳圖片數(shù)
multiple: true, //是否支持多選
onUploadFile: null, //獲取上傳的
onConfirom: null, //獲取刪除的
onRemoveImage: null, //刪除上傳的
tempFile: false,
recTypeId: 0,
recId: 0,
subRecTypeId: 0,
subRecId: 0,
}
constructor(props) {
super(props)
this.state = {
uploadTypeIsImage: true,//上傳文件類型是否是上傳圖片
viewFilesData: [],//圖片文件,在view上面顯示
showFileProgress: false,//是否顯示文件上傳進(jìn)度條
fileProgress: 0,//文件上傳進(jìn)度條進(jìn)度
hiddenUploadBtn: false,//隱藏上傳按鈕
isRemovFile: false
}
}
componentWillMount() {
const { uploadFileType } = this.props
let uploadType = _.startsWith('image/*', uploadFileType, 0)
if (uploadType) {
this.setState({
uploadTypeIsImage: true
})
} else {
this.setState({
uploadTypeIsImage: false
})
}
}
componentWillReceiveProps(nextProps) {
//如果值是異步獲取
const { existingFiles } = nextProps
const { viewFilesData, isRemovFile } = this.state
if (!viewFilesData.length && !isRemovFile) {
this.setState({
viewFilesData: existingFiles,
hiddenUploadBtn: true//隱藏上傳按鈕
})
} else {
return
}
}
//確定
handConfirm() {
this.props.onConfirom()
}
//獲取服務(wù)器和本地用戶的信息
getIncidentalInfo() {
const rootUrl = getUploadProofUrl('/file/upload') // 服務(wù)器地址
const token = Taro.getStorageSync('token') // 圖片上傳需要單獨(dú)寫入token
const locale = Taro.getStorageSync('locale') // 圖片上傳需要單獨(dú)寫入token
const userLoginName = Taro.getStorageSync('userLoginName') // 圖片上傳需要單獨(dú)寫入token
return {
rootUrl, token, locale, userLoginName
}
}
//觸發(fā)選擇文件的事件
handChooesFile() {
if (process.env.TARO_ENV === 'weapp') {
//微信環(huán)境下觸發(fā)微信小程序原生API的事件
const { uploadTypeIsImage } = this.state
if (uploadTypeIsImage) {
this.weappHandUpFiles()
} else {
this.uploadMessageFile()
}
} else if (process.env.TARO_ENV === 'h5') {
//h5環(huán)境下直接觸發(fā)input框的點(diǎn)擊事件
let uploadInput = document.getElementById('uploadInput')
uploadInput.click();
//點(diǎn)擊以后會(huì)觸發(fā)input框上面的onChange事件
}
}
/** -----h5端的處理----- */
//input框的onChange事件
handSelectFiles(e) {
let files = e.target.files
this.handUploadFilesFun(files)
}
//文件循環(huán)遍歷處理
handUploadFilesFun(files) {
let that = this
const { uploadTypeIsImage } = this.state
let tipsText = uploadTypeIsImage ? '張圖片' : '個(gè)文件'
let fileNamesData = []
let imagesSrcData = [] //轉(zhuǎn)化為blob格式在瀏覽器上顯示緩存的圖片
let uploadfilemaxsize = 10 * 1024 * 1024 //大小的上限
let uploadData = [] //確定按鈕時(shí)獲取的值
if (!files.length) {
return
} else {
for (let index = 0; index < files.length; index++) {
let filesItem = files[index]
if (filesItem.size > uploadfilemaxsize) {
let uploadfilemsg = '上傳文件大小超過系統(tǒng)規(guī)定上限(10M),請重新選擇圖片'
Taro.showToast({ title: uploadfilemsg, icon: 'success', duration: 2000 })
return
} else {
this.uploadFileItem(filesItem, index, (data) => {
if (!data) {
return
} else {
if (data.success) {
if (uploadTypeIsImage) {
//上傳圖片就添加URL
let filesSrcItem = URL.createObjectURL(filesItem);
imagesSrcData.push(filesSrcItem)
} else {
//上傳文件就添加filename
let fileName = filesItem.name
fileNamesData.push(fileName)
}
let showViewFilesData = uploadTypeIsImage ? imagesSrcData : fileNamesData
that.setState({
viewFilesData: showViewFilesData
});
Taro.showToast({ title: `第${index + 1}${tipsText}上傳成功`, icon: 'success', duration: 2000 })
uploadData.push(data.data[0])
if (index === files.length - 1) {
that.props.onUploadFile(uploadData)
return
}
} else {
Taro.showToast({ title: `第${index + 1}${tipsText}上傳失敗`, icon: 'error', duration: 2000 })
that.setState({
hiddenUploadBtn: false
})
console.log(data, 'error')
return
}
}
})
}
}
}
}
//單個(gè)文件(圖片)上傳服務(wù)器
uploadFileItem(filesItem, index, callback) {
let that = this
const { rootUrl, token, locale, userLoginName } = this.getIncidentalInfo()
const { maxCount, tempFile, recTypeId, recId, subRecTypeId, subRecId } = this.props
if (index > maxCount) {
return
} else {
let formData = new FormData();
let xhr = new XMLHttpRequest();
xhr.open('POST', rootUrl, true); // 第三個(gè)參數(shù)為async?,異步/同步
formData.append(filesItem.name, filesItem);
//把請求相關(guān)參數(shù)放入formData中
formData.append('tempFile', tempFile);
formData.append('recTypeId', recTypeId);
formData.append('recId', recId);
formData.append('subRecTypeId', subRecTypeId);
formData.append('subRecId', subRecId);
xhr.setRequestHeader("userToken", token);
xhr.setRequestHeader("userLoginName", userLoginName);
xhr.setRequestHeader("userLanguage", locale);
that.setState({
showFileProgress: true
})
//監(jiān)聽請求的進(jìn)度并在回調(diào)中傳入進(jìn)度參數(shù)
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
let progress = Math.round((e.loaded / e.total) * 100);
that.setState({
fileProgress: progress
})
if (progress === 100) {
setTimeout(function () {
that.setState({
hiddenUploadBtn: true,
showFileProgress: false
})
}, 30)
}
}
}, false); // 第三個(gè)參數(shù)為useCapture?,是否使用事件捕獲/冒泡
//監(jiān)聽readyState的變化,完成時(shí)回調(diào)后端返回的response
xhr.addEventListener('readystatechange', (e) => {
let response = e.currentTarget.response ? JSON.parse(e.currentTarget.response) : null;
if (e.currentTarget.readyState === 4) {
callback(response);
xhr.upload.removeEventListener('progress', (event) => {
if (event.lengthComputable) {
let progress = Math.round((event.loaded / event.total) * 100);
that.setState({
fileProgress: progress
})
if (progress === 100) {
that.setState({
showFileProgress: false
})
}
}
}, false)
} else {
console.log('upload loading ... ')
}
}, false);
xhr.send(formData);
}
}
/** -----weapp端的處理---- */
//圖片
weappHandUpFiles() {
let that = this
const { maxCount, tempFile, recTypeId, recId, subRecTypeId, subRecId } = this.props
const { rootUrl, token, locale, userLoginName } = this.getIncidentalInfo()
let imagesSrcData = [] //在瀏覽器上顯示緩存的圖片
let imagesUploadData = [] //確定按鈕時(shí)獲取的值
Taro.chooseImage({
count: maxCount,
sizeType: ['original', 'compressed'], // 可以指定是原圖還是壓縮圖,默認(rèn)二者都有
sourceType: ['album', 'camera'], // 可以指定來源是相冊還是相機(jī),默認(rèn)二者都有
success: (res) => {
// 返回選定照片的本地文件路徑列表,tempFilePath可以作為img標(biāo)簽的src屬性顯示圖片
let tempFilePaths = res.tempFilePaths
for (let i = 0; i < tempFilePaths.length; i++) {
if (i > maxCount) {
return
} else {
let tempFileItem = tempFilePaths[i]
imagesSrcData.push(tempFileItem)
const uploadTask = Taro.uploadFile({
url: rootUrl, //里面填寫你的上傳圖片服務(wù)器API接口的路徑
filePath: tempFileItem,//要上傳文件資源的路徑 String類型
name: 'gantIbom',//按個(gè)人情況修改,文件對應(yīng)的 key,開發(fā)者在服務(wù)器端通過這個(gè) key 可以獲取到文件二進(jìn)制內(nèi)容,(后臺(tái)接口規(guī)定的關(guān)于圖片的請求參數(shù))
header: {
"Content-Type": "multipart/form-data",//記得設(shè)置
"userToken": token,
"userLoginName": userLoginName,
"userLanguage": locale,
},
formData: {
//和服務(wù)器約定的token, 一般也可以放在header中
'tempFile': tempFile,
'recTypeId': recTypeId,
'recId': recId,
'subRecTypeId': subRecTypeId,
'subRecId': subRecId,
},
success: (data) => {
let result = JSON.parse(data.data)
if (result.success) {
Taro.showToast({ title: `第${i + 1}張圖片上傳成功`, icon: 'success', duration: 2000 })
imagesUploadData.push(result.data)
if (i === tempFilePaths.length - 1) {
this.props.onUploadFile(imagesUploadData)
}
} else {
Taro.showToast({ title: `第${i + 1}張圖片上傳失敗`, icon: 'error', duration: 2000 })
return
}
},
fail: (err) => {
console.log(err)
}
})
uploadTask.progress((progress) => {
this.setState({
fileProgress: progress.progress
})
if (progress.progress === 100) {
that.setState({
hiddenUploadBtn: true
})
setTimeout(function () {
that.setState({
showFileProgress: false
})
}, 600)
}
})
}
}
this.setState({
viewFilesData: imagesSrcData
});
},
fail: (err) => {
console.log(err)
}
})
}
//文件
uploadMessageFile() {
let that = this
const { tempFile, recTypeId, recId, subRecTypeId, subRecId } = this.props
const { rootUrl, token, locale, userLoginName } = this.getIncidentalInfo()
let fileNamesData = []
let fileUploadData = []
Taro.chooseMessageFile({
count: 1,
type: 'file',
success: (res) => {
// 返回選定的本地文件路徑列表
let tempFiles = res.tempFiles
for (let i = 0; i < tempFiles.length; i++) {
if (!tempFiles.length) {
return
} else {
let tempFileItem = tempFiles[i]
let fileName = tempFileItem.name
// let fileItemName = fileName.substring(0,fileName.lastIndexOf("."))
fileNamesData.push(fileName)
this.setState({
showFileProgress: true
})
const uploadTask = Taro.uploadFile({
url: rootUrl, //里面填寫你的上傳圖片服務(wù)器API接口的路徑
filePath: tempFileItem.path,//要上傳文件資源的路徑 String類型
name: 'gantIbom',//按個(gè)人情況修改,文件對應(yīng)的 key,開發(fā)者在服務(wù)器端通過這個(gè) key 可以獲取到文件二進(jìn)制內(nèi)容,(后臺(tái)接口規(guī)定的關(guān)于圖片的請求參數(shù))
header: {
"Content-Type": "multipart/form-data",//記得設(shè)置
"userToken": token,
"userLoginName": userLoginName,
"userLanguage": locale,
},
formData: {
//和服務(wù)器約定的token, 一般也可以放在header中
'tempFile': tempFile,
'recTypeId': recTypeId,
'recId': recId,
'subRecTypeId': subRecTypeId,
'subRecId': subRecId,
},
success: (data) => {
let result = JSON.parse(data.data)
if (result.success) {
Taro.showToast({ title: `第${i + 1}個(gè)文件上傳成功`, icon: 'success', duration: 2000 })
fileUploadData.push(result.data)
if (i === tempFiles.length) {
this.props.onUploadFile(fileUploadData)
}
} else {
Taro.showToast({ title: `第${i + 1}個(gè)文件上傳失敗`, icon: 'error', duration: 2000 })
return
}
},
fail: (err) => {
console.log(err)
}
})
uploadTask.progress((progress) => {
this.setState({
fileProgress: progress.progress
})
if (progress.progress === 100) {
setTimeout(function () {
that.setState({
showFileProgress: false,
hiddenUploadBtn: true
})
}, 30)
}
})
}
}
this.setState({
viewFilesData: fileNamesData
});
},
fail: (err) => {
console.log(`文件上傳失敗,查看錯(cuò)誤信息:${err}`)
}
})
}
//文件上傳緩存以后確定選中的文件上傳服務(wù)器
handConfirmFile() {
this.props.onConfirom()
}
//刪除文件
handDeleteFile() {
this.setState({
hiddenUploadBtn: false,
isRemovFile: true,
viewFilesData: []
}, () => {
const { viewFilesData } = this.state
this.props.onRemoveImage(viewFilesData)
});
}
render() {
const { uploadTypeIsImage, fileProgress, viewFilesData, showFileProgress, hiddenUploadBtn } = this.state
console.log(hiddenUploadBtn, 'hiddenUploadBtn')
const { uploadFileType } = this.props
let showUploadFileView = null
let uploadIcon = uploadTypeIsImage ? 'image' : 'upload'
let uplpadIconSize = uploadTypeIsImage ? '40' : '18'
let fileProgressClass = showFileProgress ? 'progress show-progress' : 'progress hidden-progress'
showUploadFileView = (
<View className='uploadfile-content'>
{
viewFilesData.map(fileVal => {
return uploadTypeIsImage ? (
<View className='img-item-warp'>
<View className='img-warp'>
<Image className='img-item' src={fileVal} />
</View>
<View className='conforim-img' onClick={this.handConfirmFile.bind(this)}>確認(rèn)</View>
<View className='icon-btn-warp'>
<AtIcon className='icon-btn' onClick={this.handDeleteFile.bind(this)} value='close-circle' size='24' color='#6e6e6e' ></AtIcon>
</View>
</View>
) : (
<View className='file-item-warp'>
<AtIcon className='icon-btn' value='file-generic' size='20' color='#336633'></AtIcon>
<Text className='uploadfile-name'>{fileVal}</Text>
<AtIcon className='close-icon-btn' onClick={this.handDeleteFile.bind(this)} value='close-circle' size='12' color='#6e6e6e'></AtIcon>
</View>
)
})
}
</View>
)
return (
<View className='uploadfile-components'>
{showUploadFileView}
<View className={hiddenUploadBtn ? 'btn-warp btn-warp-hidden' : 'btn-warp btn-warp-show'}>
<View className={uploadTypeIsImage ? 'upload-img-btn' : 'upload-file-btn'} onClick={this.handChooesFile.bind(this)}>
<AtIcon className='upload-icon' value={uploadIcon} size={uplpadIconSize} color='#fff'></AtIcon>
</View>
</View>
<View className={fileProgressClass}>
<AtProgress percent={fileProgress} />
</View>
<Input className='uploadfile-ipt' type='file' name='file' id='uploadInput' accept={uploadFileType} onChange={this.handSelectFiles.bind(this)} />
</View>
)
}
}
.uploadfile-components{
width:100%;
// height: 100vh;
position: relative;
padding: 20px;
.progress{
width: 94%;
height: 42px;
position: absolute;
left: 3%;
top:21px;
z-index: 88;
}
.show-progress{
display: block;
}
.hidden-progress{
display: none;
}
.uploadfile-content{
.img-item-warp{
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
.img-warp{
display: flex;
flex-direction: column;
justify-content: center;
overflow: hidden;
width: 100vw;
height: 100vw;
.img-item{
width: 100%;
height: auto;
min-height: 100vw;
}
}
.conforim-img{
width:100%;
height: 68px;
line-height: 68px;
font-size: 22px;
text-align: center;
background-color: #336633;
color: #fff;
border-radius: 8px;
margin-top: 20px;
}
.icon-btn-warp{
width: 42px;
height: 42px;
line-height: 42px;
position: absolute;
right: 8px;
top: 8px;
.icon-btn{
width: 42px;
height: 42px;
line-height: 42px;
}
}
}
.file-item-warp{
width: 100%;
height: 42px;
line-height: 42px;
display: flex;
flex-direction: row;
justify-content: flex-start;
.icon-btn{
height: 42px;
line-height: 42px;
}
.uploadfile-name{
height: 42px;
line-height: 42px;
font-size: 20px;
color: #336633;
margin-left: 22px;
margin-right: 130px;
}
.close-icon-btn{
flex: 1;
width: 42px;
height: 42px;
line-height: 42px;
text-align: right;
margin-right: 20px;
}
}
}
.btn-warp{
display: flex;
flex-direction: row;
justify-content: flex-start;
flex-wrap: wrap;
&.btn-warp-hidden{
display: none;
}
&.btn-warp-show{
display: block;
}
.upload-img-btn{
width: 220px;
height: 220px;
line-height: 220px;
text-align: center;
position: relative;
background-color: #336633;
border-radius: 14px;
overflow: hidden;
.upload-icon{
width: 220px;
height: 220px;
display: block;
line-height: 220px;
font-size: 48px;
color: #fff;
text-align: center;
position: absolute;
left: 0;
top: 0;
}
}
.upload-file-btn{
width: 120px;
height: 52px;
line-height: 38px;
text-align: center;
position: relative;
background-color: #336633;
border-radius: 14px;
overflow: hidden;
.upload-icon{
display: block;
width: 120px;
height: 52px;
line-height: 52px;
font-size: 22px;
color: #fff;
text-align: center;
position: absolute;
left: 0;
top: 0;
}
}
}
.uploadfile-ipt{
position: absolute;
top: -1220px;
left: 0;
}
}
組件的使用
import Taro, { Component } from '@tarojs/taro'
import { View } from '@tarojs/components'
import { PageView, GtUploadFile } from '@components'
import Avatar from '../../../static/images/default_avatar.png'
export default class UploadFileExample extends Component {
state = {
tempFile:true,
recTypeId:0,
recId:0,
subRecTypeId:0,
subRecId:0,
currentFiles:['零件庫名稱.xsl']
}
onGetUpload = (files) => {
console.log(files, `父組件獲得的value值`)
}
//刪除的時(shí)圖片ID設(shè)置
onRemoveImage = (data) => {
console.log(data)
}
//確定按鈕點(diǎn)擊
onConfirom = (data) => {
console.log(data)
}
render() {
//tempFile ,recTypeId , recId ,subRecTypeId , subRecId業(yè)務(wù)模塊區(qū)分所需的參數(shù) 組件里面默認(rèn)的是用來上傳頭像的
const { tempFile ,recTypeId , recId ,subRecTypeId , subRecId , currentFiles } = this.state
console.log(currentFiles,'currentFiles')
return (
<PageView navTitle='UploadImage組件使用示例'>
<View style='padding:10px 0;'>
<View style='padding:10px'>上傳文件</View>
<GtUploadFile
tempFile={tempFile}
recTypeId={recTypeId}
recId={recId}
subRecTypeId={subRecTypeId}
subRecId={subRecId}
uploadFileType='*'
onUploadFile={this.onGetUpload} //上傳按鈕的回調(diào)函數(shù)
onConfirom={this.onConfirom} //確定按鈕的回調(diào)函數(shù)
onRemoveImage={this.onRemoveImage} //刪除按鈕的回調(diào)函數(shù)
existingFiles={currentFiles} //默認(rèn)的文件反顯
></GtUploadFile>
</View>
</PageView>
)
}
}