- 增加自定义文件列表UI。
- 支持自定义手动上传,并且支持中断过上传文件。
- 自定义进度条动画效果。
直接上ui界面效果吧。
文件列表展示
删除背景按钮
进度条加载效果,且在上传过程中支持手动中断
下面是代码
<template>
<Upload
drag
multiple
type="drag"
ref="uploadRef"
class="upload_file"
:file-list="fileList"
:limit="uploadFileInfo.limit"
:action="uploadFileInfo.actionUrl"
:format="uploadFileInfo.format"
:headers="uploadFileInfo.headers"
:max-size="uploadFileInfo.maxSize"
:on-format-error="formatError"
:on-success="handleSuccess"
:on-progress="handleprogress"
:before-remove="beforeRemove"
:before-upload="beforeUpload"
:on-exceed="handleExceed"
:with-credentials="true"
list-type="picture"
>
<div
class="file-item"
slot="file"
slot-scope="{ file }"
@click="operationBtn(file)"
>
<img class="file_icon" :src="showFlieIcon(file)" alt="" srcset="" />
<div class="file-info">
<div class="file-name">{{ file.name }}</div>
<div class="file-size" style="font-size: 13px; font-weight: 500">
{{ getFileIfno(file) }}
</div>
</div>
<!-- 进度条样式 -->
<div class="file-percentage" v-if="file.percentage !== 100">
<div
class="progress-width"
:style="{
width: file.percentage < 20 ? '20%' : file.percentage + '%',
}"
>
<span style="color: #fff">{{ Math.round(file.percentage) }} %</span>
</div>
</div>
<div class="opertion-btn">
<Icon class="delet-btn" type="md-trash" size="22" color="#fff" />
</div>
</div>
<div style="padding: 20px 0">
<div>
<Icon type="ios-folder" size="22" color="#3399ff" />
<span>点击或者将文件拖拽到这里</span>
<a href="" onclick="event.preventDefault(); return false;">上传文件</a>
</div>
<ul class="tips_info">
<!-- <li>单个文件大小限制<=10MB</li> -->
<li>一个知识库最多允许100个文件 ,一次最多上传20个文件</li>
<li>
支持文本、表格、图片、音频、视频等格式
<span style="color: #2d8cf0" @click.stop="fileTypeExplain"
>文件上传说明</span
>
</li>
</ul>
</div>
<FileTypeModal v-model="fileTypeModalState" />
</Upload>
</template>
<script>
import { convertFileSize } from '../../../utils/tool';
import * as SVG from '../images/exprotImg';
import service from '@/axios/request';
// console.log(service, 'service');
import { EventBus } from '@/views/KnowledgeBase/utils/eventBus';
import FileTypeModal from './file-type-modal.vue';
export default {
name: 'uploadFile',
components: {
FileTypeModal,
},
props: {
allowRemove: {
type: Function,
default: () => {
return () => {};
},
},
// uploadFileInfo: {
// type: Object,
// default: () => {
// return {};
// },
// },
},
data() {
return {
SVG,
// 上传文件的信息
fileTypeModalState: false,
uploadFileInfo: {
actionUrl: `${service.defaults.baseURL}/proxy/dataset/upload_file`, //上传地址信息
headers: {
'Server-App-Id': 'ios-lma-svc',
'User-Account': 'ddh.du',
'User-Roles': 'flow_admin',
}, //请求头信息
disabled: false, //是否禁用
data: {}, //上传时附带的额外参数
name: '',
maxSize: 1024 * 20, //文件上传大小
format: [
'docx',
'doc',
'pdf',
'txt',
'md',
'csv',
'xlsx',
'png',
'jpg',
'jpeg',
'm4a',
'mp3',
'mp4',
'mov',
], //文件接收的类型
limit: 20, //最大允许上传个数
},
// 文件列表
fileList: [],
// 判断上传文件的个数
isPevFileList: [],
flieFlag: null,
deletFiles: [], //删除的文件操作远程删除的时候需要用到
handleExceedFlag: null, //文件超出提示标记
isOverFlag: [], //是否超出规定文件数量5个
};
},
methods: {
// 设置文件显示图标
showFlieIcon(file) {
if (!file) return;
console.log(file, 'file');
const { name } = file.raw || {};
const FileType = name?.split('.')[name?.split('.').length - 1] ?? '';
// 如果是图片类型转换为base64图片展示
const imgFlieList = ['png', 'jpg', 'jpeg'];
if (imgFlieList.includes(FileType)) {
return URL.createObjectURL(file.raw);
} else {
return SVG[FileType + 'Icon']
? SVG[FileType + 'Icon']
: SVG.defaultIcon;
}
},
// 文件上传说明
fileTypeExplain() {
this.fileTypeModalState = true;
},
// 显示文件大小单位
getFileIfno(file) {
if (!file.raw) return '';
const { size, name } = file.raw;
const FileType = name?.split('.')[name?.split('.').length - 1] ?? '';
return `${FileType} · ${convertFileSize(size)}`;
},
// 删除文件
async operationBtn(file) {
const fileItem = this.$refs.uploadRef.uploadFiles.find(
(e) => e.uid === file.uid,
);
if (fileItem) {
await this.$refs.uploadRef.abort(file);
}
const index = this.$refs.uploadRef.uploadFiles.findIndex(
(e) => e.uid === file.uid,
);
this.$refs.uploadRef.uploadFiles.splice(index, 1);
},
// 终止上传
abortUpload() {
this.$refs.uploadRef.abort(); // 调用 abort 方法中止上传
},
// 文件超出提示钩子
handleExceed(files, fileList) {
if (this.handleExceedFlag) {
return;
}
this.handleExceedFlag = setTimeout(() => {
this.$Message.error('最多每次上传20个文件 !');
this.handleExceedFlag = null;
}, 300);
},
// 上传时的钩子
beforeUpload(file) {
const { size, name } = file;
// console.log(file, 'file');
const { format } = this.uploadFileInfo;
const type = name?.split('.')[name?.split('.').length - 1] ?? '';
if (!format.includes(type)) {
this.$Notice.warning({
title: '文件错误提示',
desc: `${name}文件类型不符合要求, 请重新上传 !`,
duration: 6,
});
return false;
}
// if (size > 1024 * 1024 * 10) {
// this.$Message.error(`${name}-->超过文件规定大小 !`);
// return false;
// }
// if (this.isOverFlag.length > 5) {
// this.$Message.error(`一次只能上传5个文件 !`);
// return;
// }
return;
},
// 错误文件类型校验提示
formatError(file, fileList) {
this.$Message.error(`${file.name}-->文件类型错误 !`);
},
// 文件上传成功信息以及过滤掉后端报错的文件信息
handleSuccess(res, file, fileList) {
// if (res.ret === 0 && res.data) {
// const { file_id } = res.data;
// file.file_id = file_id;
// } else {
// fileList.splice(fileList.indexOf(file), 1);
// this.$Message.error(`${res.msg} !`);
// }
// const fileIdList = fileList.map((mapItem) => mapItem.file_id);
// this.isOverFlag = fileIdList;
// this.$emit('on-change-file', fileIdList);
},
// 文件上传时的钩子
handleprogress(event, file, fileList) {
// console.log(this.$refs.upload, 'this.$refs.upload');
},
// 移除文件时移除掉要删除掉文件列表
async beforeRemove(file, fileList) {
const { file_id, name } = file;
this.$emit('on-delet-file', { file_id, name });
return new Promise((resolve, reject) => {
EventBus.$on('remove-file-item', (state) => {
// console.log(state, 'state状态');
if (state) {
resolve();
} else {
reject();
}
});
}).then(() => {
const fileIdList = fileList
?.filter((filterItem) => filterItem.file_id !== file.file_id)
?.map((mapItem) => mapItem.file_id);
this.isOverFlag = fileIdList;
this.$emit('on-change-file', fileIdList);
});
},
},
mounted() {
// 判断本地环境添加请求头信息
if (process.env.NODE_ENV === 'development') {
this.uploadFileInfo.headers['User-Account'] = 'ddh.du';
this.uploadFileInfo.headers['User-Roles'] = 'flow_admin';
this.uploadFileInfo.headers['Server-App-Id'] = 'ios-lma-svc';
}
console.log(this.uploadFileInfo.headers,'this.uploadFileInfo.headers')
},
beforeDestroy() {
// 组件销毁前终止上传
this.abortUpload();
this.$off('remove-file-item');
},
};
</script>
<style lang="less" scoped>
.upload_file {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.tips_info {
line-height: 20px;
font-size: 14px;
color: #ccc;
}
.file-item {
position: relative;
padding: 10px;
display: flex;
gap: 8px;
align-items: center;
&:hover {
.opertion-btn {
display: flex;
}
}
.file_icon {
width: 40px;
height: 40px;
border-radius: 4px;
}
.file-info {
font-size: 12px;
.file-name {
max-width: 120px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
// 进度条样式
.file-percentage {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
border-radius: 8px;
.progress-width {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 0;
background: rgba(0, 0, 0, 0.5);
}
}
// 删除按钮
.opertion-btn {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: none;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, 0.3);
border-radius: 8px;
z-index: 999;
.delet-btn {
cursor: pointer;
transition: all 0.3s;
&:hover {
opacity: 0.8;
}
}
}
// width: calc(50% - 5px);
}
::v-deep .el-upload {
width: 100%;
// border: 1px dashed #ccc;
border-radius: 4px;
transition: all 0.3s;
.el-upload-dragger {
height: 120px;
width: 100%;
}
&:hover {
border-color: #57a3f3;
}
}
// 文件列表信息
::v-deep .el-upload-list {
width: 100%;
// display: flex;
// flex-direction: column;
padding-right: 10px;
max-height: 200px;
overflow-y: auto;
.ivu-upload-list-file {
margin-bottom: 10px;
border: 1px solid rgba(236, 238, 243, 1);
border-radius: 9px;
}
}
::v-deep .el-upload-list--picture {
margin-top: 10px;
display: flex;
flex-wrap: wrap;
gap: 10px;
background: rgb(248, 249, 252);
border-width: 1px;
border-radius: 8px;
padding: 12px !important;
.el-upload-list__item {
height: auto;
padding: 0;
margin: 0;
display: flex;
width: calc(50% - 5px);
border: none;
}
}
}
</style>
<style lang="less">
.ivu-notice-notice {
border-radius: 12px;
.ivu-notice-desc {
line-height: 23px;
white-space: 2px;
word-wrap: break-word;
word-break: break-all;
overflow: hidden;
white-space: wrap;
}
}
</style>