|
|
|
|
@ -0,0 +1,409 @@
|
|
|
|
|
<template>
|
|
|
|
|
<div class="upload-file">
|
|
|
|
|
<div class="upload-tool">
|
|
|
|
|
<el-select v-if="typeOptions && typeOptions.length > 0" style="margin-right: 10px;" v-model="uploadParame.remark"
|
|
|
|
|
placeholder="请选择文件类型">
|
|
|
|
|
<el-option v-for="item in typeOptions" :key="item.value" :label="item.value + ' - ' + item.label"
|
|
|
|
|
:value="item.value"></el-option>
|
|
|
|
|
</el-select>
|
|
|
|
|
|
|
|
|
|
<el-upload
|
|
|
|
|
multiple
|
|
|
|
|
action
|
|
|
|
|
:http-request="handleUploadForm"
|
|
|
|
|
:before-upload="handleBeforeUpload"
|
|
|
|
|
:file-list="fileList"
|
|
|
|
|
:limit="limit"
|
|
|
|
|
:on-error="handleUploadError"
|
|
|
|
|
:on-exceed="handleExceed"
|
|
|
|
|
:show-file-list="imageOnly"
|
|
|
|
|
:headers="myHeaders"
|
|
|
|
|
:on-remove="handleRemove"
|
|
|
|
|
:list-type="imageOnly?'picture-card':'text'"
|
|
|
|
|
class="upload-file-uploader"
|
|
|
|
|
ref="fileUpload"
|
|
|
|
|
>
|
|
|
|
|
<template v-if="imageOnly">
|
|
|
|
|
<i class="el-icon-plus"></i>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else>
|
|
|
|
|
<!-- 上传按钮 -->
|
|
|
|
|
<el-button size="mini" type="primary"
|
|
|
|
|
:disabled="typeOptions && typeOptions.length > 0 && !uploadParame.remark">选取文件
|
|
|
|
|
</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-upload>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 上传提示 -->
|
|
|
|
|
<div class="el-upload__tip" slot="tip" v-if="showTip && !imageOnly">
|
|
|
|
|
请上传
|
|
|
|
|
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b></template>
|
|
|
|
|
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b></template>
|
|
|
|
|
的文件
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 文件列表 -->
|
|
|
|
|
<transition-group v-if="!imageOnly" class="upload-file-list el-upload-list el-upload-list--text"
|
|
|
|
|
name="el-fade-in-linear" tag="ul">
|
|
|
|
|
<li :key="file.id || file.attachFileUrl" class="el-upload-list__item" v-for="(file, index) in displayFileList">
|
|
|
|
|
<div v-if="file._isFrist">
|
|
|
|
|
<dict-tag :options="typeOptions" :value="file.remark"></dict-tag>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="ele-upload-list__item-content">
|
|
|
|
|
<el-link :underline="false" @click="handleFileDownload(file)">
|
|
|
|
|
<span class="el-icon-document"> {{ getFileName(file.oldName) }} </span>
|
|
|
|
|
</el-link>
|
|
|
|
|
<div class="ele-upload-list__item-content-action">
|
|
|
|
|
<el-link :underline="false" @click="handleDelete(index)" type="danger">删除</el-link>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</li>
|
|
|
|
|
</transition-group>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import {getToken} from "@/utils/auth";
|
|
|
|
|
import {fileFileApi} from '@/api/systemSetting';
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
name: "FileUploadVO",
|
|
|
|
|
props: {
|
|
|
|
|
// 文件类型
|
|
|
|
|
typeOptions: [Array],
|
|
|
|
|
// 值
|
|
|
|
|
value: [String, Object, Array],
|
|
|
|
|
// 数量限制
|
|
|
|
|
limit: {
|
|
|
|
|
type: Number,
|
|
|
|
|
default: 5,
|
|
|
|
|
},
|
|
|
|
|
// 大小限制(MB)
|
|
|
|
|
fileSize: {
|
|
|
|
|
type: Number,
|
|
|
|
|
default: 50,
|
|
|
|
|
},
|
|
|
|
|
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
|
|
|
|
fileType: {
|
|
|
|
|
type: Array,
|
|
|
|
|
default: function () {
|
|
|
|
|
return ["doc", "docx", "xls", "ppt", "txt", "pdf", "xlsx", "jpg", "jpeg", "png"];
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
// 是否显示提示
|
|
|
|
|
isShowTip: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: true
|
|
|
|
|
},
|
|
|
|
|
imageOnly: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: false,
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
number: 0,
|
|
|
|
|
uploadList: [],
|
|
|
|
|
baseUrl: process.env.VUE_APP_BASE_API,
|
|
|
|
|
myHeaders: {
|
|
|
|
|
'X-Token': getToken()
|
|
|
|
|
},
|
|
|
|
|
fileList: [],
|
|
|
|
|
uploadParame: {
|
|
|
|
|
remark: ''
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
watch: {
|
|
|
|
|
value: {
|
|
|
|
|
handler(val) {
|
|
|
|
|
if (val) {
|
|
|
|
|
let temp = 1;
|
|
|
|
|
const list = Array.isArray(val) ? val : [val];
|
|
|
|
|
this.fileList = list.map(function (item) {
|
|
|
|
|
// 确保对象包含 oldName 和 attachFileUrl 属性
|
|
|
|
|
item.oldName = item.oldName || '未知文件名';
|
|
|
|
|
item.attachFileUrl = item.attachFileUrl || '';
|
|
|
|
|
item.uid = item.uid || new Date().getTime() + temp++;
|
|
|
|
|
return item;
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
this.fileList = [];
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
deep: true,
|
|
|
|
|
immediate: true
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
computed: {
|
|
|
|
|
// 是否显示提示
|
|
|
|
|
showTip() {
|
|
|
|
|
return this.isShowTip && (this.fileType || this.fileSize);
|
|
|
|
|
},
|
|
|
|
|
displayFileList() {
|
|
|
|
|
if (this.typeOptions && this.typeOptions.length > 0) {
|
|
|
|
|
const list = this.fileList.sort(function (a, b) {
|
|
|
|
|
return a.remark - b.remark;
|
|
|
|
|
});
|
|
|
|
|
let tempType = null;
|
|
|
|
|
list.forEach(function (item, index) {
|
|
|
|
|
if (index === 0) {
|
|
|
|
|
item._isFrist = true;
|
|
|
|
|
tempType = item.remark;
|
|
|
|
|
} else if (item.remark !== tempType) {
|
|
|
|
|
item._isFrist = true;
|
|
|
|
|
tempType = item.remark;
|
|
|
|
|
} else {
|
|
|
|
|
item._isFrist = false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
return this.fileList;
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
// 上传前校检格式和大小
|
|
|
|
|
handleBeforeUpload(file) {
|
|
|
|
|
// 校检文件类型
|
|
|
|
|
if (this.fileType) {
|
|
|
|
|
const fileName = file.name.split('.');
|
|
|
|
|
const fileExt = fileName[fileName.length - 1];
|
|
|
|
|
const isTypeOk = this.fileType.indexOf(fileExt) >= 0;
|
|
|
|
|
if (!isTypeOk) {
|
|
|
|
|
this.$modal.msgError(`文件格式不正确, 请上传${this.fileType.join("/")}格式文件!`);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 校检文件大小
|
|
|
|
|
if (this.fileSize) {
|
|
|
|
|
const isLt = file.size / 1024 / 1024 < this.fileSize;
|
|
|
|
|
if (!isLt) {
|
|
|
|
|
this.$modal.msgError(`上传文件大小不能超过 ${this.fileSize} MB!`);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this.$modal.loading("正在上传文件,请稍候...");
|
|
|
|
|
this.number++;
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
// 文件个数超出
|
|
|
|
|
handleExceed() {
|
|
|
|
|
this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`);
|
|
|
|
|
},
|
|
|
|
|
// 上传失败
|
|
|
|
|
handleUploadError(err) {
|
|
|
|
|
this.$message.error("上传文件失败,请重试");
|
|
|
|
|
this.$modal.closeLoading()
|
|
|
|
|
},
|
|
|
|
|
// 自定义上传方法
|
|
|
|
|
handleUploadForm(param) {
|
|
|
|
|
const formData = new FormData()
|
|
|
|
|
const data = {
|
|
|
|
|
model: this.$route.path.split('/')[1],
|
|
|
|
|
pid: 10
|
|
|
|
|
}
|
|
|
|
|
// 如果有文件类型选择,添加到参数中
|
|
|
|
|
if (this.uploadParame.remark) {
|
|
|
|
|
data.remark = this.uploadParame.remark;
|
|
|
|
|
}
|
|
|
|
|
formData.append('multipart', param.file)
|
|
|
|
|
|
|
|
|
|
const loading = this.$loading({
|
|
|
|
|
lock: true,
|
|
|
|
|
text: '上传中,请稍候...',
|
|
|
|
|
spinner: 'el-icon-loading',
|
|
|
|
|
background: 'rgba(0, 0, 0, 0.7)'
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
var _this = this;
|
|
|
|
|
console.log(this.baseUrl);
|
|
|
|
|
|
|
|
|
|
fileFileApi(formData, data).then(function (res) {
|
|
|
|
|
loading.close()
|
|
|
|
|
// 直接处理返回的文件信息,不再检查code字段
|
|
|
|
|
// 从返回的响应中提取所需信息
|
|
|
|
|
const fileInfo = res || {};
|
|
|
|
|
const newFile = {
|
|
|
|
|
id: fileInfo.id || '',
|
|
|
|
|
oldName: fileInfo.fileName || param.file.name,
|
|
|
|
|
attachFileUrl: fileInfo.url || fileInfo.path || '',
|
|
|
|
|
remark: _this.uploadParame.remark || '',
|
|
|
|
|
uid: param.file.uid
|
|
|
|
|
};
|
|
|
|
|
_this.uploadList.push(newFile);
|
|
|
|
|
_this.uploadedSuccessfully();
|
|
|
|
|
}).catch(function (error) {
|
|
|
|
|
loading.close()
|
|
|
|
|
console.error('上传错误:', error)
|
|
|
|
|
_this.$message.error('上传失败,请重试')
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
// 原有的成功回调方法保留但适配新的返回格式
|
|
|
|
|
handleUploadSuccess(res, file) {
|
|
|
|
|
// 此方法可能不再需要,但保留以防万一
|
|
|
|
|
if (res && res.code === 200) {
|
|
|
|
|
const newFile = {
|
|
|
|
|
id: res.id || '',
|
|
|
|
|
oldName: file.name,
|
|
|
|
|
attachFileUrl: res.url || '',
|
|
|
|
|
remark: this.uploadParame.remark || '',
|
|
|
|
|
uid: file.uid
|
|
|
|
|
};
|
|
|
|
|
this.uploadList.push(newFile);
|
|
|
|
|
this.uploadedSuccessfully();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
handleRemove(file) {
|
|
|
|
|
this.fileList = this.fileList.filter(function (item) {
|
|
|
|
|
return item !== file;
|
|
|
|
|
});
|
|
|
|
|
this.$emit("input", this.fileList);
|
|
|
|
|
},
|
|
|
|
|
// 删除文件
|
|
|
|
|
handleDelete(index) {
|
|
|
|
|
this.fileList.splice(index, 1);
|
|
|
|
|
this.$emit("input", this.fileList);
|
|
|
|
|
},
|
|
|
|
|
// 上传结束处理
|
|
|
|
|
uploadedSuccessfully() {
|
|
|
|
|
if (this.number > 0 && this.uploadList.length === this.number) {
|
|
|
|
|
this.fileList = this.fileList.concat(this.uploadList);
|
|
|
|
|
this.uploadList = [];
|
|
|
|
|
this.number = 0;
|
|
|
|
|
this.$emit("input", this.fileList);
|
|
|
|
|
this.$modal.closeLoading();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// 获取文件名称
|
|
|
|
|
getFileName(name) {
|
|
|
|
|
if (typeof name === 'string' && name.lastIndexOf("/") > -1) {
|
|
|
|
|
return name.slice(name.lastIndexOf("/") + 1);
|
|
|
|
|
} else {
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// 获取完整的文件URL
|
|
|
|
|
getFullFileUrl(url) {
|
|
|
|
|
if (!url) return '';
|
|
|
|
|
// 判断URL是否已经是完整的HTTP链接
|
|
|
|
|
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
|
|
|
return url;
|
|
|
|
|
}
|
|
|
|
|
// 如果不是完整链接,则拼接baseUrl
|
|
|
|
|
// 避免重复添加斜杠
|
|
|
|
|
const base = this.baseUrl.endsWith('/') ? this.baseUrl.slice(0, -1) : this.baseUrl;
|
|
|
|
|
const path = url.startsWith('/') ? url : '/' + url;
|
|
|
|
|
return base + path;
|
|
|
|
|
},
|
|
|
|
|
// 对象转成指定字符串分隔
|
|
|
|
|
listToString(list, separator) {
|
|
|
|
|
let strs = "";
|
|
|
|
|
separator = separator || ",";
|
|
|
|
|
for (let i in list) {
|
|
|
|
|
strs += list[i].attachFileUrl + separator;
|
|
|
|
|
}
|
|
|
|
|
return strs !== '' ? strs.substr(0, strs.length - 1) : '';
|
|
|
|
|
},
|
|
|
|
|
// 处理文件下载
|
|
|
|
|
handleFileDownload(file) {
|
|
|
|
|
// 显示加载状态
|
|
|
|
|
this.$modal.loading("下载文件中,请稍候...");
|
|
|
|
|
|
|
|
|
|
// 获取token
|
|
|
|
|
const token = getToken();
|
|
|
|
|
if (!token) {
|
|
|
|
|
this.$modal.closeLoading();
|
|
|
|
|
this.$modal.msgError("未获取到登录凭证,请重新登录");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 创建XMLHttpRequest对象
|
|
|
|
|
const xhr = new XMLHttpRequest();
|
|
|
|
|
const fileUrl = this.getFullFileUrl(file.attachFileUrl);
|
|
|
|
|
|
|
|
|
|
xhr.open('GET', fileUrl, true);
|
|
|
|
|
// 设置响应类型为blob
|
|
|
|
|
xhr.responseType = 'blob';
|
|
|
|
|
// 设置请求头,携带token
|
|
|
|
|
xhr.setRequestHeader('X-Token', token);
|
|
|
|
|
|
|
|
|
|
// 监听请求状态变化
|
|
|
|
|
xhr.onreadystatechange = () => {
|
|
|
|
|
if (xhr.readyState === 4) {
|
|
|
|
|
this.$modal.closeLoading();
|
|
|
|
|
|
|
|
|
|
if (xhr.status === 200) {
|
|
|
|
|
// 创建下载链接
|
|
|
|
|
const url = window.URL.createObjectURL(new Blob([xhr.response]));
|
|
|
|
|
const link = document.createElement('a');
|
|
|
|
|
link.style.display = 'none';
|
|
|
|
|
link.href = url;
|
|
|
|
|
// 设置文件名
|
|
|
|
|
link.setAttribute('download', file.oldName || `file_${Date.now()}`);
|
|
|
|
|
document.body.appendChild(link);
|
|
|
|
|
link.click();
|
|
|
|
|
|
|
|
|
|
// 清理
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
document.body.removeChild(link);
|
|
|
|
|
window.URL.revokeObjectURL(url);
|
|
|
|
|
}, 100);
|
|
|
|
|
} else {
|
|
|
|
|
// 错误处理
|
|
|
|
|
let errorMsg = `文件下载失败,状态码: ${xhr.status}`;
|
|
|
|
|
try {
|
|
|
|
|
// 尝试解析错误响应
|
|
|
|
|
const errorData = JSON.parse(xhr.responseText);
|
|
|
|
|
if (errorData.message) {
|
|
|
|
|
errorMsg = errorData.message;
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
// 非JSON响应
|
|
|
|
|
}
|
|
|
|
|
this.$modal.msgError(errorMsg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 网络错误处理
|
|
|
|
|
xhr.onerror = () => {
|
|
|
|
|
this.$modal.closeLoading();
|
|
|
|
|
this.$modal.msgError("网络错误,文件下载失败");
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 发送请求
|
|
|
|
|
xhr.send();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
.upload-tool {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.upload-file-uploader {
|
|
|
|
|
margin-bottom: 5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.upload-file-list .el-upload-list__item {
|
|
|
|
|
border: 1px solid #e4e7ed;
|
|
|
|
|
line-height: 2;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.upload-file-list .ele-upload-list__item-content {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
color: inherit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ele-upload-list__item-content-action .el-link {
|
|
|
|
|
margin-right: 10px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|