123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- <template>
- <div class="content">
- <el-upload
- v-loading="isUploading"
- :disabled="componentError"
- class="upload-demo"
- drag
- :headers="headers"
- :file-list="viewFileList"
- :name="name"
- :show-file-list="showFileList"
- :list-type="showFileListType"
- :multiple="multiple"
- :action="uploadUrl"
- :limit="limit"
- :on-success="handleUploadSuccess"
- :before-upload="handleBeforeUpload"
- :on-error="handleUploadError"
- :on-exceed="handleExceed"
- :before-remove="handleBeforeRemove"
- :on-remove="handleRemove"
- :on-preview="handlePreviewOpen"
- >
- <i class="el-icon-upload"></i>
- <div
- class="el-upload__text"
- v-if="!componentError"
- >将文件拖到此处,或<em>点击上传</em></div>
- <div
- class="error-text"
- v-else
- > 组件配置错误,请查看控制台
- </div>
- <div
- class="el-upload__tip"
- slot="tip"
- >
- 文件大小不超过{{ limitSize }}m,文件类型为{{ types.toString() }}
- </div>
- </el-upload>
- <!-- 预览 -->
- <el-dialog
- title="预览"
- :visible.sync="previewObj.show"
- width="60%"
- :before-close="handlePreviewClose"
- >
- <div class="preview-content">
- <template v-if="previewObj.file && previewObj.file.type.includes('image')">
- <el-image class="preview-item" :src="previewObj.file.url" />
- </template>
- <template v-if="previewObj.file && previewObj.file.type.includes('video')">
- <video class="preview-item" controls :src="previewObj.file.url" />
- </template>
- </div>
- </el-dialog>
- </div>
- </template>
- <script>
- import mime from 'mime'
- let fullLoading = null
- const baseURL = process.env.VUE_APP_DOMAIN_PREFIX
- export default {
- name: "Upload",
- props: {
- headers:{
- type:Object,
- default:()=>({})
- },
- /** 上传时候表单的KEY */
- name: {
- type: String,
- default: () => "file"
- },
- /** 限制上传数量 */
- limit: {
- type: Number,
- default: () => 5
- },
- /** 限制的那张大小 单位M */
- limitSize: {
- type: Number,
- default: () => 5
- },
- /** 是否多选 */
- multiple: {
- type: Boolean,
- default: () => true
- },
- /** 是否展示文件列表 */
- showFileList: {
- type: Boolean,
- default: () => true
- },
- /** 文件展示方式 text/picture/picture-card */
- showFileListType:{
- type:String,
- default:()=>'text'
- },
- /** 允许上传的文件尾缀 string[] */
- types: {
- type: Array,
- default: () => (['jpg', 'png', 'gif'])
- },
- /** 默认的文件列表 string[] */
- defaultFileList: {
- type: Array,
- default: () => ([])
- },
- /** 上传成功后端返回的字段名称 */
- responseFileName: {
- type: String,
- default: () => 'url'
- },
- /** 是否需要全屏loading */
- needFullScreenLoading:{
- type:Boolean,
- default:()=>true
- }
- },
- data() {
- return {
- uploadUrl: `${ baseURL }/file/upload`,
- // 真实文件列表
- fileList: [],
- // 默认展示的list,解决多上传只回调success一次的问题
- viewFileList:[],
- // 组件是否部署错误
- componentError: false,
- // 是否正在上传
- isUploading:false,
- // 预览对象
- previewObj:{
- show:false,
- file:null
- }
- }
- },
- watch: {
- defaultFileList: {
- handler() {
- // 判断类型
- const flag = Object.prototype.toString.call(this.defaultFileList) === '[object Array]'
- && this.defaultFileList.length > 0 &&
- Object.prototype.toString.call(this.defaultFileList[0]) !== '[object String]'
- if (flag) {
- this.componentError = true
- throw new Error('defaultFileList格式错误,应为string[]格式')
- }else{
- this.componentError = false
- }
- this.viewFileList = this.defaultFileList.map(defaultFilePath => ({name: defaultFilePath, url: defaultFilePath}))
- this.viewFileList.forEach(item=>{
- this.fileList.push(item)
- })
- },
- deep: true,
- immediate: true
- },
- fileList:{
- handler(){
- this.handleNotifyFather()
- },
- deep:true,
- immediate:false
- }
- },
- methods: {
- /**
- * 检查type是否符合types的mime
- * @param type 文件后缀
- * @param types 可用文件后缀集合
- */
- handleCheckFileMime(type, types) {
- const typeMimes = types.map(item => mime.getType(item))
- return typeMimes.includes(type)
- },
- handleCheckFileSize(fileSize, limitSize) {
- const limitByteSize = limitSize * 1024 * 1024
- return limitByteSize > fileSize
- },
- /**
- * 上传之前的钩子
- * @param file
- * @return {undefined}
- */
- handleBeforeUpload(file) {
- // 检查mime
- const fileType = file.type || mime.getType(file.name.slice(file.name.lastIndexOf('.') + 1))
- const checkFileMime = this.handleCheckFileMime(fileType, this.types)
- const checkFileSize = this.handleCheckFileSize(file.size, this.limitSize);
- !checkFileSize ? file.isJumpRemove = true : undefined
- !checkFileSize ? this.$notify.warning(`文件大小不得超出${ this.limitSize }m`) : undefined
- !checkFileMime ? file.isJumpRemove = true : undefined
- !checkFileMime ? this.$notify.warning(`文件类型不在合法列表 ${ this.types }`) : undefined
- if(checkFileSize && checkFileMime){
- // 开启loading
- this.isUploading = true
- if(this.needFullScreenLoading){
- fullLoading = this.$loading({
- background:`rgba(255,255,255,0.5)`,
- text:'上传中',
- fullscreen:true
- })
- }
- }
- return checkFileSize && checkFileMime
- },
- /**
- * 上传成功钩子
- * @param response
- * @param file
- * @param fileList
- */
- handleUploadSuccess(response, file, fileList) {
- this.isUploading = false
- if(this.needFullScreenLoading){
- fullLoading?.close()
- }
- const successObj = {
- url: response.data[this.responseFileName],
- name: file.name
- }
- file.url = response.data[this.responseFileName]
- this.fileList.push(successObj)
- },
- /**
- * 上传失败的钩子
- * @param err
- * @param file
- * @param fileList
- */
- handleUploadError(err, file, fileList) {
- },
- /**
- * 超出数量的钩子
- * @param files
- * @param fileList
- */
- handleExceed(files, fileList) {
- this.$notify.warning(`文件总数大于可上传数量 ${ this.limit }`)
- },
- /**
- * 文件即将移除的钩子
- * @param file
- * @param fileList
- */
- async handleBeforeRemove(file, fileList) {
- // 如果是超出文件大小调用,放行
- if (file?.raw?.isJumpRemove) {
- return true
- }
- return await this.$confirm('此操作将会删除已上传的文件, 是否继续?', '提示', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning'
- })
- },
- /**
- * 移除文件的钩子
- */
- handleRemove(file, fileList) {
- if (file?.raw?.isJumpRemove) {
- return
- }
- this.fileList.splice(this.fileList.findIndex(fileItem => file?.response?.data[this.responseFileName] === fileItem.url || file.url === fileItem.url), 1)
- },
- /**
- * 通知父组件
- */
- handleNotifyFather(){
- this.$emit('change',this.fileList)
- },
- /**
- * 预览
- * 图片视频直接预览,其他下载
- * @param file
- */
- handlePreviewOpen(file){
- if(!file.type){
- file.type = mime.getType(file?.url?.slice(file?.url?.lastIndexOf('.')+1)) || mime.getType(file?.name?.slice(file?.name.lastIndexOf('.')+1)) || undefined
- }
- if(file.type.includes('image') || file.type.includes('video')){
- this.previewObj.file = file
- this.previewObj.show = true
- }else{
- this.$confirm('需要下载才能预览此文件, 是否继续?', '提示', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning'
- }).then(() => {
- let htmlAnchorElement = document.createElement('a');
- htmlAnchorElement.download = file?.url.slice(file?.url.lastIndexOf('/')+1)
- htmlAnchorElement.target='_bank'
- htmlAnchorElement.href = file?.url
- htmlAnchorElement.click()
- htmlAnchorElement = null
- }).catch(() => {
- });
- }
- },
- handlePreviewClose(){
- this.previewObj.file = null
- this.previewObj.show = false
- }
- }
- }
- </script>
- <style
- lang="scss"
- scoped
- >
- ::v-deep .el-upload {
- width: 100% !important;
- .el-upload-dragger {
- width: 100% !important;
- }
- }
- .error-text {
- font-size: 18px;
- font-weight: bolder;
- color: red;
- animation: error-animation 2.5s ease-in-out infinite;
- }
- @keyframes error-animation {
- 0%, 100% {
- font-size: 18px;
- color: red;
- }
- 25%, 75% {
- font-size: 16px;
- color: #b9b1b1;
- }
- 50% {
- font-size: 18px;
- color: #500000;
- }
- }
- .preview-content{
- display: flex;
- align-items: center;
- justify-content: center;
- .preview-item{
- min-width: 800px;
- }
- }
- </style>
|