Vue上传图片预览组件
2023-09-11 14:19:54 时间
父组件:
<template>
<div>
<h4>基于Vue.2X的html5上传图片组件</h4>
<div style="width: 502px;">
<uploader :src="'/api/imgsupload'"></uploader>
</div>
</div>
</template>
<script>
import uploader from './uploader.vue'
export default {
data() {
return {}
},
components: {
uploader
}
}
</script>
子组件:
<template>
<div class="vue-uploader">
<div class="file-list">
<section v-for="(file, index) of files" class="file-item draggable-item">
<img :src="file.src" alt="" ondragstart="return false;">
<p class="file-name">{{file.name}}</p>
<span class="file-remove" @click="remove(index)">+</span>
</section>
<section v-if="status == 'ready'" class="file-item">
<div @click="add" class="add">
<span>+</span>
</div>
</section>
</div>
<section v-if="files.length != 0" class="upload-func">
<div class="progress-bar">
<section v-if="uploading" :width="(percent * 100) + '%'">{{(percent * 100) + '%'}}</section>
</div>
<div class="operation-box">
<button v-if="status == 'ready'" @click="submit">上传</button>
<button v-if="status == 'finished'" @click="finished">完成</button>
</div>
</section>
<input type="file" accept="image/*" @change="fileChanged" ref="file" multiple="multiple">
</div>
</template>
<script>
export default {
props: {
src: {
type: String,
required: true
}
},
data() {
return {
status: 'ready',
files: [],
uploading: false,
percent: 0
}
},
methods: {
add() {
this.$refs.file.click()//调用file的click事件
},
submit() {
if (this.files.length === 0) {
console.warn('no file!');
return
}
//当点击上传按钮时,将会遍历所有选中的文件,并添加到自定义的FormData中
const formData = new FormData()
this.files.forEach((item) => {
formData.append(item.name, item.file)
})
const xhr = new XMLHttpRequest()
xhr.upload.addEventListener('progress', this.uploadProgress, false)
xhr.open('POST', this.src, true)
this.uploading = true
xhr.send(formData)
xhr.onload = () => {
this.uploading = false
if (xhr.status === 200 || xhr.status === 304) {
this.status = 'finished'
console.log('upload success!')
} else {
console.log(`error:error code ${xhr.status}`)
}
}
},
finished() {
this.files = []
this.status = 'ready'
},
remove(index) {
this.files.splice(index, 1)
},
fileChanged() {
const list = this.$refs.file.files
for (let i = 0; i < list.length; i++) {
if (!this.isContain(list[i])) {
const item = {
name: list[i].name,
size: list[i].size,
file: list[i]
}
this.html5Reader(list[i], item)
this.files.push(item)
}
}
this.$refs.file.value = ''
},
// 将图片文件转成BASE64格式
html5Reader(file, item){
const reader = new FileReader()
reader.onload = (e) => {
this.$set(item, 'src', e.target.result)
}
reader.readAsDataURL(file)
},
isContain(file) {
return this.files.find((item) => item.name === file.name && item.size === file.size)
},
uploadProgress(evt) {
const component = this
if (evt.lengthComputable) {
const percentComplete = Math.round((evt.loaded * 100) / evt.total)
component.percent = percentComplete / 100
} else {
console.warn('upload progress unable to compute')
}
}
}
}
</script>
<style>
.vue-uploader {
border: 1px solid #e5e5e5;
}
.vue-uploader .file-list {
padding: 10px 0px;
}
.vue-uploader .file-list:after {
content: '';
display: block;
clear: both;
visibility: hidden;
line-height: 0;
height: 0;
font-size: 0;
}
.vue-uploader .file-list .file-item {
float: left;
position: relative;
width: 100px;
text-align: center;
}
.vue-uploader .file-list .file-item img{
width: 80px;
height: 80px;
border: 1px solid #ececec;
}
.vue-uploader .file-list .file-item .file-remove {
position: absolute;
right: 12px;
display: none;
top: 4px;
width: 14px;
height: 14px;
color: white;
cursor: pointer;
line-height: 12px;
border-radius: 100%;
transform: rotate(45deg);
background: rgba(0, 0, 0, 0.5);
}
.vue-uploader .file-list .file-item:hover .file-remove {
display: inline;
}
.vue-uploader .file-list .file-item .file-name {
margin: 0;
height: 40px;
word-break: break-all;
font-size: 14px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.vue-uploader .add {
width: 80px;
height: 80px;
margin-left: 10px;
float: left;
text-align: center;
line-height: 80px;
border: 1px dashed #ececec;
font-size: 30px;
cursor: pointer;
}
.vue-uploader .upload-func {
display: flex;
padding: 10px;
margin: 0px;
background: #f8f8f8;
border-top: 1px solid #ececec;
}
.vue-uploader .upload-func .progress-bar {
flex-grow: 1;
}
.vue-uploader .upload-func .progress-bar section {
margin-top: 5px;
background: #00b4aa;
border-radius: 3px;
text-align: center;
color: #fff;
font-size: 12px;
transition: all .5s ease;
}
.vue-uploader .upload-func .operation-box {
flex-grow: 0;
padding-left: 10px;
}
.vue-uploader .upload-func .operation-box button {
padding: 4px 12px;
color: #fff;
background: #007ACC;
border: none;
border-radius: 2px;
cursor: pointer;
}
.vue-uploader > input[type="file"] {
display: none;
}
</style>
效果:
相关文章
- import Vue from 'vue';
- (尚031)Vue_案例_自定义事件(组件间通信第2种方式:vue自定义事件)
- (尚018-第二章2.1)Vue使用vue-cli创建模板项目
- 【Vue】import和export
- vue@2.6.14小目标列表
- 【css/vue】Vue组件中对body操作样式的一种解决方案
- vue+iview tables多个分页实现
- 一文带你理解vue创建一个后台管理系统流程(Vue+Element)
- vue原生input上传文件
- 详解ESLint在Vue中的使用小结
- Vue 动态粒子特效(vue-particles)
- Vue移动端报错[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive.
- 【Vue】vue组件和vue插件的创建和使用(底部栏组件、Toast 和 Notify通知插件)
- vue文档摘录九:Vue Router
- springboot+vue在线答疑系统(源码+说明文档)
- 解决vue-cli引入sass,报错:this.getResolve is not a function问题
- vscode快速生成vue代码---创建Vue代码模板
- Vue 之 插件 轮播组件 vue-awesome-swiper 的简单使用整理
- Web 前端 之 Vue vue cli 环境的搭建简单整理(简单的一些注意事项)
- SSM项目/SpringBoot项目/SpringBoot+Vue前后端分离项目 图片上传并查看功能实现汇总
- Vue学习第27天——路由vue-router的详解及案例练习
- Vue学习第13天——vue中使用自定义插件
- 【VUE】vue配置Gzip压缩
- 手写Vue (1) 对象劫持
- antd design vue 设置 v-decorator 的初始值
- 浅谈vue中插件的使用方法Vue.use(xxx),原理及实现
- Vue基本的内置指令