vue 實(shí)現(xiàn)上傳組件
1.介紹
效果如下圖
2.思路
文件上傳的兩種實(shí)現(xiàn)方式
1.From形式
<form method="post" enctype="multipart/from-data" action="api/upload" > <input type="file name="file"> <button type="submit">Submit</button> </form>
form的method屬性指定為 "post" 請求,通過HTML表單發(fā)送數(shù)據(jù)給服務(wù)器,并返回服務(wù)器的修改結(jié)果,在這種情況下Content-Type是通過在<form>元素中設(shè)置正確的enctype屬性。
form的enctype屬性規(guī)定在發(fā)送到服務(wù)器之前應(yīng)該如何對表單數(shù)據(jù)進(jìn)行編碼。
- application/x-www-form-urlencoded(默認(rèn)值):表示在發(fā)送前編碼所有字符,數(shù)據(jù)被編碼成以"&"分隔的鍵值對,同時(shí)以"="分隔鍵和值,("name=seven&age=19")。不支持二進(jìn)制數(shù)據(jù)。
- multipart/form-data:支持二進(jìn)制數(shù)據(jù)(上傳文件時(shí)必須指定)
2.JavaScript異步請求形式
我們知道 FormData 接口提供了一種表示表單數(shù)據(jù)的鍵值對 key/value 的構(gòu)造方式,并且可以輕松的將數(shù)據(jù)通過XMLHttpRequest.send()方法發(fā)送出去,本接口和此方法都相當(dāng)簡單直接。如果送出時(shí)的編碼類型被設(shè)為 "multipart/form-data",它會使用和表單一樣的格式。
var formdata = new FormData(); // 創(chuàng)建FormData對象
formdata.append("name","laotie"); // 通過append()方法添加新的屬性值
... // 更多方法請點(diǎn)下面鏈接
3.生命周期
上傳組件也有它的生命周期
beforeUpload --> uploading --> fileUploaded 或者 uploadedError
4.代碼草稿
本例中采用js異步請求的方式開發(fā)上傳組件
<input type="file" name="file" @change.prevent="handleFileChange"> // 創(chuàng)建一個(gè)file類型的input,用于觸發(fā)文件上傳,后面可以把input隱藏掉,自定義好看的樣式 // 自定義樣式的時(shí)候可以用slot區(qū)分不同上傳狀態(tài)的樣式(loading,success,defult)
const handleFileChange = (e:Event)=>{
const target = e.target as HTMLInputElement
const files = Array.from(target.files)// 注意這里取得的是一個(gè)類數(shù)組
if(files){
// 取得文件
const uploadedFile = files[0]
if(!validateFormat) return
// ...這里只是提供一種思路,具體校驗(yàn)不再講述
// 在這里做一些上傳文件前的校驗(yàn),比如文件格式,大小等,
// 不符合要求的話就不在繼續(xù)發(fā)送請求
const formData = new FormData()
formData.append(uploadedFile.name,uploadedFile)
axios.post('/upload',formData,{
headers:{
// 注意設(shè)置編碼類型
'Content-Type': 'multipart/form-data'
}
}).then(res=>{
console.log('上傳成功')
}).catch(error =>{
// 文件上傳失敗
}).finally(()=>{
// 文件上傳完成,無論成功還是失敗
// 這里可以清除一下input.value
})
}
}
5.具體實(shí)現(xiàn)
// Upload.vue
<template>
<div class="upload-container">
<div class="upload-box" @click.prevent="triggerUpload" v-bind="$attrs">
<slot name="loading" v-if="fileStatus==='loading'">
<button class="btn btn-primary">上傳中</button>
</slot>
<slot name="uploaded" v-else-if="fileStatus==='success'" :uploadedData="fileData">
<button class="btn btn-primary">上傳成功</button>
</slot>
<slot v-else name="default">
<button class="btn btn-primary">點(diǎn)擊上傳</button>
</slot>
</div>
<input type="file" class="file-input d-none" name="file" ref="uploadInput" @change="hanldeInput"/>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, PropType, watch } from 'vue'
import axios from 'axios'
type UploadStatus = 'ready' | 'loading' | 'success' | 'error'
type FunctionProps = (file:File) => boolean
export default defineComponent({
name: 'Upload',
inheritAttrs: false,
props: {
// 上傳的url
action: {
type: String,
required: true
},
// 上傳之前的校驗(yàn),是一個(gè)返回布爾值的函數(shù)
beforeUpload: {
type: Function as PropType<FunctionProps>
},
// 上傳好的數(shù)據(jù),用來判斷狀態(tài)或做初始化展示
uploadedData: {
type: Object
}
},
emits: ['file-uploaded-success', 'file-uploaded-error'],
setup(props, ctx) {
const uploadInput = ref<null | HTMLInputElement>(null)
const fileStatus = ref<UploadStatus>(props.uploadedData ? 'success' : 'ready')
const fileData = ref(props.uploadedData)
watch(() => props.uploadedData, (val) => {
if (val) {
fileStatus.value = 'success'
fileData.value = val
}
})
const triggerUpload = () => {
if (uploadInput.value) {
uploadInput.value.click()
}
}
const hanldeInput = (e:Event) => {
const target = e.target as HTMLInputElement
const files = target.files
console.log(target)
if (files) {
const uploadFile = Array.from(files)
const validateFormat = props.beforeUpload ? props.beforeUpload(uploadFile[0]) : true
if (!validateFormat) return
fileStatus.value = 'loading'
const formData = new FormData()
formData.append('file', uploadFile[0])
axios.post(props.action, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(res => {
console.log('文件上傳成功', res)
fileStatus.value = 'success'
fileData.value = res.data
ctx.emit('file-uploaded-success', res.data)
}).catch(error => {
console.log('文件上傳失敗', error)
fileStatus.value = 'error'
ctx.emit('file-uploaded-error', error)
}).finally(() => {
console.log('文件上傳完成')
if (uploadInput.value) {
uploadInput.value.value = ''
}
})
}
}
return {
uploadInput,
triggerUpload,
hanldeInput,
fileStatus,
fileData
}
}
})
</script>
使用示例:
<template>
<div class="create-post-page">
<upload
action="/upload"
:beforeUpload="beforeUpload"
:uploadedData="uploadedData"
@file-uploaded-success="hanldeUploadSuccess"
class="d-flex align-items-center justify-content-center bg-light text-secondary w-100 my-4"
>
<template #uploaded="slotProps">
<div class="uploaded-area">
<img :src="slotProps.uploadedData.data.url"/>
<h3>點(diǎn)擊重新上傳</h3>
</div>
</template>
<template #default>
<h2>點(diǎn)擊上傳頭圖</h2>
</template>
<template #loading>
<div class="d-flex">
<div class="spinner-border text-secondary" role="status">
<span class="sr-only"></span>
</div>
</div>
</template>
</upload>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, onMounted } from 'vue'
import Upload from '../components/Upload.vue'
import createMessage from '../components/createMessage'
export default defineComponent({
name: 'CreatePost',
components: { Upload },
setup() {
const uploadedData = ref() //創(chuàng)建一個(gè)響應(yīng)式數(shù)據(jù)
let imageId = ''
onMounted(() => {
....
// 這里有邏輯省略了,取到初始化數(shù)據(jù)image
if (image) {
uploadedData.value = { data: image }
}
})
// 上傳前校驗(yàn),返回布爾值
const beforeUpload = (file:File) => {
const res = beforeUploadCheck(file, {
format: ['image/jpeg', 'image/png'],
size: 1
})
const { error, passed } = res
if (error === 'format') {
createMessage('上傳圖片只能是JPG/PNG格式!', 'error')
}
if (error === 'size') {
createMessage('上傳圖片大小不能超過1MB', 'error')
}
return passed
}
// 上傳成功后拿到imageId就可以進(jìn)行后續(xù)處理,創(chuàng)建表單啥的
const hanldeUploadSuccess = (res:ResponeseProps<ImageProps>) => {
createMessage(`上傳圖片ID ${res.data._id}`, 'success')
if (res.data._id) {
imageId = res.data._id
}
}
return {
beforeUpload,
hanldeUploadSuccess,
uploadedData
}
}
})
</script>
<style>
.create-post-page{
padding:0 20px 20px;
}
.create-post-page .upload-box{
height:200px;
cursor: pointer;
overflow: hidden;
}
.create-post-page .upload-box img{
width: 100%;
height: 100%;
object-fit: cover;
}
.uploaded-area{
position: relative;
}
.uploaded-area:hover h3{
display: block;
}
.uploaded-area h3{
display: none;
position: absolute;
color: #999;
text-align: center;
width: 100%;
top:50%
}
</style>
以上就是vue 實(shí)現(xiàn)上傳組件的詳細(xì)內(nèi)容,更多關(guān)于vue 上傳組件的資料請關(guān)注腳本之家其它相關(guān)文章!
- vue-cropper組件實(shí)現(xiàn)圖片切割上傳
- vue-cropper插件實(shí)現(xiàn)圖片截取上傳組件封裝
- vue 圖片裁剪上傳組件的實(shí)現(xiàn)
- vue3.0搭配.net core實(shí)現(xiàn)文件上傳組件
- vue 使用原生組件上傳圖片的實(shí)例
- Vue + Node.js + MongoDB圖片上傳組件實(shí)現(xiàn)圖片預(yù)覽和刪除功能詳解
- vue圖片上傳組件使用詳解
- vue中使用elementUI組件手動上傳圖片功能
- Vue開發(fā)之封裝上傳文件組件與用法示例
- vue圖片上傳本地預(yù)覽組件使用詳解
- vue-cli3.0+element-ui上傳組件el-upload的使用
相關(guān)文章
Vue使用mixins實(shí)現(xiàn)壓縮圖片代碼
本篇文章主要介紹了Vue使用mixins實(shí)現(xiàn)壓縮圖片代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-03-03
vue.js 左側(cè)二級菜單顯示與隱藏切換的實(shí)例代碼
這篇文章主要介紹了vue.js 左側(cè)二級菜單顯示與隱藏切換的實(shí)例代碼,需要的朋友可以參考下2017-05-05
前端使用el-table自帶排序功能\后端排序方法實(shí)例
在Vue.js中使用Element UI庫時(shí)可以通過el-table組件來展示表格數(shù)據(jù),并支持列排序,下面這篇文章主要給大家介紹了關(guān)于前端使用el-table自帶排序功能\后端排序的相關(guān)資料,需要的朋友可以參考下2024-08-08
Vue3的ts報(bào)錯(cuò):類型"{}"上不存在屬性"xxx"的兩種徹底根治解決方法
這篇文章主要給大家介紹了關(guān)于Vue3的ts報(bào)錯(cuò):類型"{}"上不存在屬性"xxx"的兩種徹底根治解決方法,這是最近做項(xiàng)目中遇到的一個(gè)問題,這里給大家總結(jié)下解決辦法,需要的朋友可以參考下2023-08-08
Vue用Export2Excel導(dǎo)出excel,多級表頭數(shù)據(jù)方式
這篇文章主要介紹了Vue用Export2Excel導(dǎo)出excel,多級表頭數(shù)據(jù)方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04
vue3基礎(chǔ)組件開發(fā)detePicker日期選擇組件示例
這篇文章主要為大家介紹了vue3基礎(chǔ)組件開發(fā)-detePicker(日期選擇組件)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03

