之前写过一个文件上传组件,该组件可以实现图片/文档/视频/其他格式的上传,并且可以选择文件后回显到表单中。
下面的代码是公司大佬写的,我先搬过来并分析一下:
SelectFileDome.vue
<template>
// vue中要求必须要有一个最外层的标签包裹,而且只有一个
<div>
//这是个按钮,可以用于显示数据的按钮,点击事件后面有
<el-button @click="showData">显示数据</el-button>
//el-row是element-ui中的一行标签
//type="flex"即代表是flex布局,justify="center"即代表横向居中
<el-row type="flex" class="raw-bg" justify="center">
<el-col :span="6"><h2>文件上传demo</h2></el-col>
</el-row>
<el-row type="flex" class="raw-bg" justify="center">
<el-col :span="6">
//此处为一个上传文件的弹窗,传入的属性:allow支持的扩展名,max最大支持的文件大小,单位为b
<upload-file-dialog allow="png" :max="100000000"/>
</el-col>
</el-row>
<el-row type="flex" class="raw-bg" justify="center">
<el-col :span="16">
//此处的el-divider,我在element-ui中没有查到,但是这个是一个分割线组件,content-position="left",代表文字在左侧,右侧部分用分割线充满
<el-divider content-position="left">整个传入</el-divider>
</el-col>
</el-row>
<el-row type="flex" class="raw-bg" justify="center">
<el-col :span="12">
//此处用到了一个子组件selectFiles,这个子组件中传递的属性有:
//v-model="filePaths",这个是自己写的一个v-model,支持v-model的组件有input select text textarea等,如果要自己写的话,需要满足两个要求,一个是要有value值,一个是要监听input或者update更新的方法。
//files是用于接收选择的文件数组的,
//limit是显示几个选择文件的输入框,默认为1个。
//allow是需要选择的文件扩展名
//up-title是展示出来的标题,可以是文字,也可以是+加号
//max是允许上传文件的大小最大值
//label是显示的标签名称
<select-files v-model="filePaths" :files="files2" :limit="4" :type="1" allow="jpg,png,gif,jpeg" up-title="+" :max="1054000" label="选择图片">
</el-col>
</el-row>
<el-row type="flex" class="row-bg" justify="center">
//此处的el-divider,我在element-ui中没有查到,但是这个是一个分割线组件,content-position="left",代表文字在左侧,右侧部分用分割线充满
<el-col :span="16"><el-divider content-position="left">传入一个空数组</el-divider></el-col>
</el-row>
<el-row type="flex" class="row-bg" justify="center">
<el-col :span="12">
//此处用到了一个子组件selectFiles,这个子组件中传递的属性有:v-model="filePaths",这个是自己写的一个v-model,支持v-model的组件有input select text textarea等,如果要自己写的话,需要满足两个要求,一个是要有value值,一个是要监听input或者update更新的方法。
//files是用于接收选择的文件数组的,
//limit是显示几个选择文件的输入框,默认为1个。
//allow是需要选择的文件扩展名
//up-title是展示出来的标题,可以是文字,也可以是+加号
//max是允许上传文件的大小最大值
//label是显示的标签名称
<select-files :files="[]" :limit="4" :type="1" allow="jpg,png,gif,jpeg" up-title="+" :max="1054000" label="选择图片 " />
</el-col>
</el-row>
<el-row type="flex" class="row-bg" justify="center">
//此处的el-divider,我在element-ui中没有查到,但是这个是一个分割线组件,content-position="left",代表文字在左侧,右侧部分用分割线充满
<el-col :span="16"><el-divider content-position="left">上传一个图像</el-divider></el-col>
</el-row>
<el-row type="flex" class="row-bg" justify="center">
<el-col :span="12">
//此处用到了一个子组件selectFiles,这个子组件中传递的属性有:v-model="filePaths",这个是自己写的一个v-model,支持v-model的组件有input select text textarea等,如果要自己写的话,需要满足两个要求,一个是要有value值,一个是要监听input或者update更新的方法。
//files是用于接收选择的文件数组的,
//limit是显示几个选择文件的输入框,默认为1个。
//allow是需要选择的文件扩展名
//up-title是展示出来的标题,可以是文字,也可以是+加号
//max是允许上传文件的大小最大值
//label是显示的标签名称
<select-files :key="filePaths" v-model="filePaths" :file-urls="fileUrls" :files="files2" :limit="4" :type="1" allow="jpg,png,gif,jpeg" up-title="+" :max="1054000" label="选择图片 " />
</el-col>
</el-row>
<el-row type="flex" class="row-bg" justify="center">
<el-col :span="16"><el-divider content-position="left">指定上传文件类型</el-divider></el-col>
</el-row>
<el-row type="flex" class="row-bg" justify="center">
<el-col :span="12"><select-files img-style="width:400px;height:600px;line-height:600px" /></el-col>
</el-row>
<el-row type="flex" class="row-bg" justify="center">
<el-col :span="16"><el-divider content-position="left">上传多个图像</el-divider></el-col>
</el-row>
<el-row type="flex" class="row-bg" justify="center">
<el-col :span="6">
<select-files :files="files" :limit="4" :type="1" allow="jpg,png,gif,jpeg" up-title="+" :max="1054000" />
</el-col>
</el-row>
<el-row type="flex" class="row-bg" justify="center">
<el-col :span="16"><el-divider content-position="left">上传一个文件</el-divider></el-col>
</el-row>
<el-row type="flex" class="row-bg" justify="center">
<el-col :span="6"><select-files /></el-col>
</el-row>
<el-row type="flex" class="row-bg" justify="center">
<el-col :span="16"><el-divider content-position="left">上传多个文件</el-divider></el-col>
</el-row>
<el-row type="flex" class="row-bg" justify="center">
<el-col :span="6"><select-files /></el-col>
</el-row>
<el-row type="flex" class="row-bg" justify="center">
<el-col :span="6"><h2>文件列表demo</h2></el-col>
</el-row>
<el-row type="flex" class="row-bg" justify="center">
<el-col :span="18">
//select-file-list是选择文件列表的组件,
//allow:是显示需要上传的文件格式也就是扩展名
//max:是显示需要上传的文件的最大值限制
//select-file-type:是显示需要上传的文件的类型,此处的类型是前后端约定好的,1图片2文档3视频4文件5其他,图片的格式有很多种,png jpg gif jpeg等,此时如果需要再加以限制,则需要通过allow进行限制。
<select-file-list allow="jpg" :max="1024*1024" />
</el-col>
</el-row>
<el-row type="flex" class="row-bg" justify="center">
<el-col :span="18">
//select-file-list是选择文件列表的组件,
//allow:是显示需要上传的文件格式也就是扩展名
//max:是显示需要上传的文件的最大值限制
//select-file-type:是显示需要上传的文件的类型,此处的类型是前后端约定好的,1图片2文档3视频4文件5其他,图片的格式有很多种,png jpg gif jpeg等,此时如果需要再加以限制,则需要通过allow进行限制。
<select-file-list :select-file-type="1" />
</el-col>
</el-row>
</div>
</template>
<script>
//以下代码为引入三个子组件,具体路径根据实际情况来
import selectFiles from '../../components/SelectFiles'
import SelectFileList from '../../components/SelectFiles/SelectFileList'
import UploadFileDialog from '../../components/SelectFiles/UploadFileDialog'
export default{
name:'SelectFileDemo',
//引入组件后,需要注册到当前页面中
components:{UploadFileDialog ,SelectFileList ,selectFiles },
data(){
return{
filePaths:'ssss,ssss',
files2:[],
files:[
{
url:'xxx',
filePath:'xxx',
suffix:'jpg'
}
]
}
},
methods:{
showData() {
console.log('-filePaths---------------------------------')
console.log(this.filePaths)
console.log('-files---------------------------------')
console.log(this.files)
console.log('-files2---------------------------------')
console.log(this.files2)
}
}
}
</script>
selectFiles.vue
//调用方法说明:filePaths和value在解析成数组的时候,是按同样的索引解析的,所以两个必须同时有且要求一致
//注意:files必须要放置一个空数组,必须要有,可以来用接收文件数据
<template>
//vue文件在template中需要有个最外层的div进行包裹,而且需要只有一个
<div>
<div class="source">
//这些el开头的类名都是从element-ui中拷贝出来的,具体方法后续研究并说明
<div class="el-upload-list el-upload-list--picture-card">
//v-for循环的时候,为了保证key值的唯一性,有时需要进行拼接才可以
//tabindex
//imgStyle
<span v-for="(item,index) in this.fileList" :key="index + item.filePath" tabindex="0" class="el-upload-list__item is-ready" :style="'border: none;' + imgStyle">
//el-image是element-ui中的图片组件,这个组件的参数如下:
//v=if是否显示
//style样式
//src图片的展示路径
//fit图片的展示方式
<el-image
v-if="item.filePath"
:style="imgStyle"
:src="$store.getters.getConfigByKey('SYS_FILE_SERVER_PATH') + item.filePath"
fit="fill"
>
//如果图片显示不出来,则会显示这个slot="error"错误的信息
//type="flex"代表是flex布局,而且元素之间的间距为10
//justify="center"代表的是flex中元素水平沿主轴方向居中展示
//gutter代表的是flex中元素之间的间距为10
<el-row slot="error" type="flex" justify="center" class="not-image-show" :gutter="10" style="background: #efefef">
<el-col :span="8">
//字符串的toUpperCase()方法是实现字符串的大写展示
<div class="text-suffix">{{ item.suffix.toUpperCase() }}</div></el-col>
<el-col :span="1">|</el-col>
<el-col :span="10" tag="div" class="text-filename">{{ item.fileName ? item.fileName : item.filePath }}</el-col>
</el-row>
</el-image>
<div v-else tabindex="0" class="el-upload el-upload--picture-card" :style="'line-height: 300px;' + imgStyle" @click="show(index)">
<i v-if="upTitle === '+'" class="el-icon-plus" />
<span v-else style="font-size: 18px">{{ upTitle }}</span>
</div>
//如果图片存在的话,则可以进行图片的查看与删除操作
<span v-if="item.filePath" class="el-upload-list__item-actions">
<span class="el-upload-list__item-preview" @click="showFile(item)"><i class="el-icon-zoom-in" /></span>
<span class="el-upload-list__item-delete" @click="deleteFile(index)"><i class="el-icon-delete" /></span>
</span>
</span>
</div>
</div>
//element-ui中的弹出层操作
//visible.sync是否展示弹出层
//modal是否需要遮罩层,如果为true,则会展示出遮罩层
//append-to-body Dialog 自身是否插入至 body 元素上。嵌套的 Dialog 必须指定该属性并赋值为 true
<el-dialog :visible.sync="showDialog" :modal="true" width="1000px" :append-to-body="true">
<select-file-list :type="type" :max="max" :allow="allow" @success="success" />
</el-dialog>
</div>
</template>
<script>
//引入选择文件列表的组件
import SelectFileList from './SelectFileList'
//定义一个空的对象,里面有三个值,分别是 文件类型 文件名称 文件扩展名
const emptyFileObj = {
suffix: '',
fileName: '',
fileType: ''
}
export default {
//引入的组件需要进行注册
components: { SelectFileList },
//父组件可以传递的属性
props: {
/* 允许选择的文件类型:从字典中获取key:1:图片;2:文档;3:音乐;4:视频*/
type: {
type: Number,
default: null
},
/* 允许上传的文件后缀:如:jpg,png,gif*/
allow: {
type: String
// default: 'jpg,png,gif,jpeg'
},
/* 最大上传文件容量*/
max: {
type: Number
// default: 2097152
},
/* 最多选择多少个文件*/
limit: {
type: Number,
default: 1
},
/* 回传或回显的文件数组*/
files: {
type: Array,
default: null
},
/* 文件路径集合,多个路径使用【,】隔开*/
filePaths: {
type: String
// require:true
},
/* filePaths 和 value 在解析成数组的时候,是按同样的索引解析的,所以两者必须同时有且要求一致*/
value: {
type: String,
defaule: ''
},
imgStyle: {
type: String,
default: 'width:200px;height:300px'
},
upTitle: {
type: String,
default: '选择图片'
},
fileKey: {
type: String
}
},
data() {
return {
showDialog: false,
currentIndex: -1,
fileList: [],
filePath: ''
}
},
watch: {
fileKey() {
//截取数组的第一项
this.fileList.splice(0)
this.resetFiles()
this.$forceUpdate()
}
},
created() {
this.fileList = this.files === null ? [] : this.files
this.resetFiles()
},
methods: {
success(fileData) {
this.showDialog = false
this.fileList[this.currentIndex] = fileData
this.updateFilePaths()
},
show(index) {
this.currentIndex = index
this.showDialog = true
},
showFile(fileData) {
window.open(this.$store.getters.getConfigByKey('SYS_FILE_SERVER_PATH') + fileData.filePath)
},
deleteFile(index) {
for (var k in this.fileList[index]) {
this.fileList[index][k] = ''
}
this.updateFilePaths()
this.$forceUpdate()
},
updateFilePaths() {
let paths = ''
for (let i = 0; i < this.fileList.length; i++) {
const file = this.fileList[i]
if (file.filePath) {
paths += file.filePath
if (i !== this.fileList.length - 1) {
paths += ','
}
}
}
this.$emit('input', paths)
},
resetFiles() {
this.filePath = this.value
if (this.fileList.length < 1 && this.filePath) {
const paths = this.filePath.split(',')
for (let i = 0; i < paths.length; i++) {
const path = paths[i]
this.fileList.push({
filePath: path,
suffix: path.substring(path.lastIndexOf('.') + 1, path.length)
})
}
}
if (this.fileList.length < this.limit) {
for (let i = this.fileList.length; i < this.limit; i++) {
this.fileList.push(emptyFileObj)
}
}
}
}
}
</script>
<style>
.text-filename{
word-break: break-all;
word-wrap: break-word;
}
</style>
SelectFileList
<template>
<el-container style="min-width: 1000px">
<el-header>
<el-row type="flex" justify="between" :gutter="10" >
<el-col :span="10">
<el-input v-model="searchParams.fileName" class="width-full" placeholder="请输入文件名称" />
</el-col>
<el-col :span="8">
<el-select v-model="searchParams.fileType" class="width-full" clearable value-key="id" :name="searchParams.fileType" placeholder="请选择文件类型" :disabled="type !== null ">
<el-option
v-for="item in fileTypeDict"
:key="item.id"
:label="item.value"
:value="item.id"
/>
</el-select>
</el-col>
<el-col :span="6" >
<el-button type="primary" icon="el-icon-search" plain @click="searchFileList(0)">搜索</el-button>
<upload-file-dialog :allow="allow" :max="max" @success="searchFileList(0)" />
</el-col>
</el-row>
</el-header>
<el-main class="main-center">
<div v-if="fileList.length === 0">
<el-row type="flex" class="row-bg no-data-row" justify="center" >
<el-col :span="12" class="no-data-text" >这里空空如也,啥也没有哦!
<upload-file-dialog :allow="allow" :max="max" @success="searchFileList(0)" />
</el-col>
</el-row>
</div>
<div v-else>
<single-file-show v-for="fileData in fileList" :key="fileData.id" :file-data="fileData" @doSelect="doSelect(fileData)" />
</div>
</el-main>
<el-footer>
<el-pagination layout="total, prev, pager, next" :total="pageObj.total" @current-change="handleCurrentChange" /></el-footer>
</el-container>
</template>
<script>
import { searchFileList } from '@/api/file'
import SingleFileShow from './SingleFileShow'
import UploadFileDialog from './UploadFileDialog'
export default {
name: 'SelectFileList',
components: { UploadFileDialog, SingleFileShow },
props: {
/* 允许选择的文件类型:从字典中获取key:1:图片;2:文档;3:音乐;4:视频*/
type: {
type: Number,
default: null
},
/* 允许上传的文件后缀:如:jpg,png,gif*/
allow: {
type: String
// default: 'jpg,png,gif,jpeg'
},
max: {
type: Number
// default: 2097152
}
},
data() {
return {
searchParams: {
fileName: '',
fileType: this.type,
page: 0,
size: 9,
sort: 'createTime,desc'
},
fileList: [],
pageObj: {
size: 0,
total: 0,
count: 0
}
}
},
computed: {
fileTypeDict() {
// console.log(this.$store.getters.getFileDicts('SYS_FILE_TYPE_DICT_ITEM'))
return this.$store.getters.getFileDicts('SYS_FILE_TYPE_DICT_ITEM')
}
},
created() {
this.searchFileList()
},
methods: {
searchFileList(page) {
if (page || page === 0) {
this.searchParams.page = page
}
searchFileList(this.searchParams).then(res => {
this.fileList = res.content
this.pageObj.total = res.totalElements
})
},
handleCurrentChange(current) {
this.searchParams.page = current - 1
this.searchFileList()
},
doSelect(fileData) {
const file = {}
for (var k in fileData) {
file[k] = fileData[k]
}
this.$emit('success', file)
}
}
}
</script>
<style scoped>
.no-data-row{
height: 300px;
align-items: center;
}
.no-data-text{
text-align: center;
font-size: 15px
}
.main-center{
min-height: 600px;
}
</style>
UploadFileDialog
<template>
<span>
<el-tooltip class="item" effect="dark" placement="bottom-end">
<div slot="content" style="font-size: 15px">
仅允许上传后缀为【 <el-link type="primary">{{ allow }}</el-link>】格式的文件 <br><br>
文件大小不允许超过【<el-link type="primary">{{ parseInt(maxFileSize / 1024) }}KB</el-link>】
/ 【<el-link type="primary">{{ parseInt(maxFileSize / 1024 / 1024) }} M</el-link>】
</div>
<el-button type="success" plain icon="el-icon-upload" @click="show = !show">上传文件</el-button>
</el-tooltip>
<el-dialog :visible.sync="show" :append-to-body="true" @close="closeDialog">
<el-upload
class="upload-demo"
:action="action"
:on-preview="handlePreview"
:headers="headers"
:list-type="listType"
with-credentials
show-file-list
:accept="allowFileSuffix"
auto-upload
:on-success="onSuccess"
:before-upload="beforeUpload"
:limit="limit"
:on-exceed="onExceed"
multiple
>
<el-button type="primary" icon="el-icon-upload">{{ btnTitle }}</el-button>
<div slot="tip" class="el-upload__tip">
<p>仅允许上传后缀为【 <el-link type="success">{{ allow }}</el-link>】格式的文件</p>
<p>文件大小不允许超过【<el-link type="success">{{ parseInt(maxFileSize / 1024) }}KB</el-link>】
/ 【<el-link type="success">{{ parseInt(maxFileSize / 1024 / 1024) }} M</el-link>】</p>
</div>
</el-upload>
<el-row type="flex" class="row-bg" justify="center">
<el-col :span="6">
<el-button v-show="flag" class="backbtn" type="success" size="small" @click="confirm()">确定</el-button>
</el-col>
</el-row>
</el-dialog>
</span>
</template>
<script>
import { getToken } from '@/utils/auth'
import store from '@/store'
export default {
name: 'UploadFileDialog',
props: {
/* 允许上传的文件后缀:如:jpg,png,gif*/
allow: {
type: String,
default: store.getters.sysConfig['FILES_ALLOWED_TO_UPLOAD']
// default: 'jpg,png,gif,jpeg'
},
// 单位B
max: {
type: Number
// default: 2097152
},
fileType: {
type: Number
},
listType: {
type: String,
default: 'picture'
},
title: {
type: String,
default: '上传文件'
},
btnTitle: {
type: String,
default: '上传文件'
},
/* 最我允许上传文件个数*/
limit: {
type: Number,
default: 5
}
},
data() {
return {
show: false,
headers: {
'Authorization': getToken()
},
flag: false
}
},
computed: {
allowFileSuffix() {
return '.' + this.allow.replace(/,/g, ',.')
},
maxFileSize() {
if (this.max == null) {
return this.$store.getters.sysConfig['FILES_ALLOWED_MAX_SIZE']
}
return this.max
},
action() {
return this.$store.getters.baseUploadFile
}
},
created() {
},
methods: {
onSuccess(result, file, fileList) {
if (result.status === 200) {
this.flag = true
file.url = result.data.url
}
this.$notify({
title: result.message
})
},
closeDialog() {
if (this.flag === true) {
this.$emit('success')
}
},
confirm() {
this.show = false
},
beforeUpload(file) {
// 判断文件大小是否符合规范
if (this.maxFileSize < file.size) {
this.$notify({
title: '文件大小超过最限制,最大上传【' + this.maxFileSize / 1024 + 'KB】当前文件大小为【' + file.size / 1024 + 'KB】'
})
return false
}
return true
},
onExceed(files, fileList) {
this.$notify({
title: '最多只能上传' + this.limit + '个文件'
})
}
}
}
</script>
<style scoped>
.el-row {
margin-top: 20px;
}
</style>