659 lines
19 KiB
Vue
659 lines
19 KiB
Vue
<template>
|
||
<div class="upload_file">
|
||
<el-upload
|
||
v-if="upload_mode === 'file'"
|
||
:file-list="fileList"
|
||
:action="action"
|
||
:on-change="handleChange"
|
||
:on-success="handleSuccess"
|
||
:on-preview="handlePreview"
|
||
:on-exceed="handleExceed"
|
||
:on-remove="handleRemove"
|
||
:before-upload="beforeUpload"
|
||
:limit="uploadLimitNumber"
|
||
:accept="fileAccept"
|
||
:multiple="true"
|
||
>
|
||
<el-button :size="upload_button_size" type="primary">
|
||
{{ upload_text }}
|
||
</el-button>
|
||
<div class="el-upload__tip" v-html="upload_tips"></div>
|
||
</el-upload>
|
||
|
||
<!-- 图片上传模式 -->
|
||
<el-upload
|
||
v-if="upload_mode === 'image'"
|
||
:file-list="fileList"
|
||
:action="action"
|
||
:on-change="handleChange"
|
||
:on-success="handleSuccess"
|
||
:on-preview="handlePreview"
|
||
:on-remove="handleRemove"
|
||
:before-upload="beforeUpload"
|
||
:on-exceed="handleExceed"
|
||
:limit="uploadLimitNumber"
|
||
:accept="fileAccept"
|
||
:multiple="true"
|
||
list-type="picture-card"
|
||
>
|
||
<i class="el-icon-plus"></i>
|
||
<!-- <div class="el-upload__tip" v-if="fileList.length < upload_number">-->
|
||
<!-- <div style="margin-top: 8px">{{ upload_text }}</div>-->
|
||
<!-- </div>-->
|
||
</el-upload>
|
||
|
||
<!-- 拖拽上传模式 -->
|
||
|
||
<template v-if="upload_mode === 'drag'">
|
||
<el-tabs class="mb10" v-model="uploadMode">
|
||
<el-tab-pane label="电脑选择文件" name="pc">
|
||
<span slot="label">
|
||
<i class="fa fa-desktop"></i>
|
||
电脑选择文件
|
||
</span>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="手机扫码上传" name="mobile">
|
||
<span slot="label">
|
||
<i class="fa fa-qrcode"></i>
|
||
手机扫码上传
|
||
</span>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
<el-upload
|
||
ref="dragUploadRef"
|
||
drag
|
||
:file-list="fileList"
|
||
:multiple="true"
|
||
:action="action"
|
||
:on-change="handleChange"
|
||
:on-success="handleSuccess"
|
||
:on-preview="handlePreview"
|
||
:on-remove="handleRemove"
|
||
:on-exceed="handleExceed"
|
||
:before-upload="beforeUpload"
|
||
:limit="uploadLimitNumber"
|
||
:accept="fileAccept"
|
||
class="drag-upload"
|
||
:class="{ 'drag-upload-disabled': uploadMode !== 'pc' }"
|
||
>
|
||
<div slot="trigger" style="height: 134px" v-if="uploadMode === 'pc'">
|
||
<i class="el-icon-upload"></i>
|
||
<div class="el-upload__text">{{ upload_text }}</div>
|
||
</div>
|
||
<div>
|
||
<div class="h5-qrcode" v-if="uploadMode === 'mobile'" v-loading="codeLoading">
|
||
<QrCode :options="{ width: 126 }" :value="qrCodeAddress" class="signature-qrcode"></QrCode>
|
||
</div>
|
||
</div>
|
||
<div class="el-upload__tip" v-html="upload_tips"></div>
|
||
</el-upload>
|
||
</template>
|
||
|
||
<!-- 插槽,用于显示额外的说明信息 -->
|
||
<slot name="explain"></slot>
|
||
</div>
|
||
</template>
|
||
|
||
<script nonce="${cspNonce!}">
|
||
module.exports = {
|
||
props: {
|
||
// 上传返回id
|
||
upload_return_id_api: {
|
||
type: String,
|
||
default: "/platform/sys/file/uploadLocalReturnId",
|
||
required: false
|
||
},
|
||
// 上传返回url
|
||
upload_dynamic_return_url_api: {
|
||
type: String,
|
||
default: "/platform/sys/file/uploadDynamicReturnUrl",
|
||
required: false
|
||
},
|
||
// 当上传接口为id的情况下,配置下载接口
|
||
upload_id_download_url: {
|
||
type: String,
|
||
default: "/platform/sys/file/download?id=",
|
||
required: false
|
||
},
|
||
// 上传样式或图片方式 file || drag || image
|
||
upload_mode: {
|
||
type: String,
|
||
default: "file",
|
||
required: false
|
||
},
|
||
// 上传数量
|
||
upload_number: {
|
||
type: [Number, String],
|
||
default: 1,
|
||
required: false
|
||
},
|
||
// 上传文件大小限制
|
||
upload_size: {
|
||
type: Number,
|
||
default: 1024 * 1024 * 10,
|
||
required: false
|
||
},
|
||
// 是否限制上传文件大小,默认保留原有大小校验逻辑。
|
||
limit_upload_size: {
|
||
type: Boolean,
|
||
default: true,
|
||
required: false
|
||
},
|
||
// 上传按钮文字
|
||
upload_text: {
|
||
type: String,
|
||
default: "点击上传文件",
|
||
required: false
|
||
},
|
||
// 上传按钮大小
|
||
upload_button_size: {
|
||
type: String,
|
||
default: "small",
|
||
required: false
|
||
},
|
||
// 上传返回id或url
|
||
upload_result_type: {
|
||
type: String,
|
||
default: "url",
|
||
required: false
|
||
},
|
||
// 上传返回分类 数组或字符串逗号隔离 interval | array
|
||
upload_result_category: {
|
||
type: String,
|
||
default: "interval",
|
||
required: false
|
||
},
|
||
// 是否显示文件列表
|
||
show_upload_list: {
|
||
type: Boolean,
|
||
default: true,
|
||
required: false
|
||
},
|
||
// 接受上传的文件类型
|
||
accept: {
|
||
type: String,
|
||
default: "",
|
||
required: false
|
||
},
|
||
// 是否是完整的结果(就是文件上传返回什么,该组件返回什么,uploadResultCategory必须为array)
|
||
complete_result: {
|
||
type: Boolean,
|
||
default: false,
|
||
required: false
|
||
},
|
||
// 父组件传来的参数
|
||
value: {
|
||
type: [String, Array],
|
||
default: undefined,
|
||
required: false
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
fileList: [],
|
||
previewVisible: false,
|
||
previewImage: "",
|
||
fileAccept: null,
|
||
dragDisabled: true,
|
||
qrCodeAddress: origin + "/platform/sys/h5ScanCodeUploadFile",
|
||
uploadMode: "pc",
|
||
codeLoading: true,
|
||
}
|
||
},
|
||
computed: {
|
||
action() {
|
||
return this.upload_result_type === "id" ? this.upload_return_id_api : this.upload_dynamic_return_url_api
|
||
},
|
||
uploadLimitNumber() {
|
||
const number = Number(this.upload_number)
|
||
return Number.isFinite(number) && number > 0 ? number : 1
|
||
},
|
||
upload_tips() {
|
||
const uploadNumber = this.uploadLimitNumber
|
||
const fileAccept = this.fileAccept
|
||
const fileSize = this.upload_size
|
||
|
||
let tips = null
|
||
if (uploadNumber) {
|
||
tips = `只能上传<span style="color: red">${uploadNumber}</span>个文件;`
|
||
}
|
||
if (fileAccept) {
|
||
tips = tips + `只能上传<span style="color: red">${fileAccept}</span>文件;`
|
||
} else {
|
||
tips = tips + '不限格式;'
|
||
}
|
||
if (this.limit_upload_size && fileSize) {
|
||
tips = tips + `单个文件大小不能超过<span style="color: red">${fileSize / 1024 / 1024}</span>M;`
|
||
}
|
||
return tips
|
||
}
|
||
},
|
||
methods: {
|
||
buildFileObject(url, id) {
|
||
return {
|
||
data: url ? url : this.upload_id_download_url + id,
|
||
name: url ? url : id,
|
||
url: url ? url : this.upload_id_download_url + id,
|
||
status: "success",
|
||
response: {
|
||
data: url ? url : id,
|
||
code: 200
|
||
}
|
||
}
|
||
},
|
||
echo(newVal) {
|
||
// 字符串隔离情况
|
||
if (this.upload_result_category === "interval") {
|
||
// id隔离
|
||
if (this.upload_result_type === "id") {
|
||
newVal.split(",").forEach((id) => {
|
||
const file = this.buildFileObject(undefined, id)
|
||
this.fileList.push(file)
|
||
this.fileList.reverse()
|
||
})
|
||
}
|
||
// url隔离
|
||
if (this.upload_result_type === "url") {
|
||
newVal.split(",").forEach((url) => {
|
||
const file = this.buildFileObject(url)
|
||
this.fileList.push(file)
|
||
this.fileList.reverse()
|
||
})
|
||
}
|
||
}
|
||
// 如果是数组的情况下
|
||
if (this.upload_result_category === "array") {
|
||
if (this.complete_result) {
|
||
// 得去掉数组里面的thumbUrl,一个base64太大,无用
|
||
// let newResult = cloneDeep(newVal)
|
||
let newResult = JSON.parse(JSON.stringify(newVal))
|
||
newResult.map((e) => {
|
||
if (e.thumbUrl) {
|
||
delete e.thumbUrl
|
||
}
|
||
if (this.upload_result_type === "id") {
|
||
e.url = this.upload_id_download_url + e.response.data
|
||
}
|
||
if (this.upload_result_type === "url") {
|
||
e.url = e.response.data
|
||
}
|
||
})
|
||
this.fileList = newResult
|
||
} else {
|
||
// id数组
|
||
if (this.upload_result_type === "id") {
|
||
if (Array.isArray(newVal)) {
|
||
newVal.forEach((id) => {
|
||
this.fileList.push(this.buildFileObject(undefined, id))
|
||
})
|
||
} else {
|
||
try {
|
||
JSON.parse(newVal).forEach((id) => {
|
||
this.fileList.push(this.buildFileObject(undefined, id))
|
||
})
|
||
} catch (e) {
|
||
console.error(e)
|
||
}
|
||
}
|
||
}
|
||
// url数组
|
||
if (this.upload_result_type === "url") {
|
||
if (Array.isArray(newVal)) {
|
||
newVal.forEach((url) => {
|
||
this.fileList.push(this.buildFileObject(url))
|
||
})
|
||
} else {
|
||
try {
|
||
JSON.parse(newVal).forEach((url) => {
|
||
this.fileList.push(this.buildFileObject(url))
|
||
})
|
||
} catch (e) {
|
||
console.error(e)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
// 这是兜底逻辑,保准只让这个image类型上传图片
|
||
beforeUpload(file) {
|
||
|
||
// 需要兼容部分业务不限制附件大小,关闭后仅跳过大小校验,不影响数量和格式控制。
|
||
if (this.limit_upload_size && file.size > this.upload_size) {
|
||
this.$message.error("文件过大,请上传小于" + (this.upload_size / 1024 / 1024).toFixed(0) + "M的文件!")
|
||
return false
|
||
}
|
||
|
||
|
||
return true
|
||
|
||
// if (!this.fileAccept || typeof this.fileAccept !== "string") {
|
||
// return true
|
||
// }
|
||
//
|
||
// // 去除前后空格并转换为数组
|
||
// const acceptTypes = this.fileAccept
|
||
// .trim()
|
||
// .split(",")
|
||
// .map((type) => type.trim().toUpperCase())
|
||
//
|
||
// let fileType
|
||
// const dotIndex = file.name.lastIndexOf(".")
|
||
// if (dotIndex !== -1) {
|
||
// fileType = file.name.substring(dotIndex).toUpperCase()
|
||
// } else {
|
||
// this.$message.error("文件必须包含有效的扩展名!")
|
||
// return false
|
||
// }
|
||
//
|
||
// const valid = acceptTypes.includes(fileType)
|
||
// if (valid) {
|
||
// return true
|
||
// }
|
||
// this.$message.error(`文件只能是 ${this.fileAccept} 格式!`)
|
||
// return false
|
||
},
|
||
|
||
// 预览图片
|
||
handlePreview(file) {
|
||
const suffix = file.name.substring(file.name.lastIndexOf(".") + 1).toLowerCase()
|
||
const fileId = file.response.data.substring(file.response.data.lastIndexOf("=") + 1)
|
||
const buildFile = {
|
||
id: fileId,
|
||
suffix,
|
||
name: file.name,
|
||
downloadPath: file.response.data
|
||
}
|
||
this.$commonUtil.previewFile(buildFile)
|
||
},
|
||
|
||
handleRemove(file, fileList) {
|
||
if (this.upload_result_category === "interval") {
|
||
const resultIntervalValue = fileList
|
||
.map((item) => item.response && item.response.data ? item.response.data : item.url || item.data)
|
||
.filter((item) => item)
|
||
.join(",")
|
||
this.$emit("update:value", resultIntervalValue)
|
||
return
|
||
}
|
||
|
||
if (this.upload_result_category === "array") {
|
||
if (this.complete_result) {
|
||
this.$emit("update:value", fileList)
|
||
} else {
|
||
if (this.upload_result_type === "id") {
|
||
this.$emit(
|
||
"update:value",
|
||
fileList.map((e) => e.response.data)
|
||
)
|
||
} else {
|
||
this.$emit(
|
||
"update:value",
|
||
fileList.map((e) => e.response.data)
|
||
)
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
handleExceed(files, fileList) {
|
||
this.$message.warning("最多只能上传" + this.uploadLimitNumber + "个文件")
|
||
},
|
||
|
||
// 上传事件
|
||
handleChange(file, fileList) {
|
||
//
|
||
// let result = []
|
||
// // const file = uploads.file
|
||
// debugger
|
||
// if (file && (file.status === 'done' || file.status === 'removed') && file.response && file.response.code === 200) {
|
||
// uploads.fileList.forEach((f) => {
|
||
// result.push(f)
|
||
// })
|
||
// }
|
||
// if (result.length > 0) {
|
||
// if (this.upload_result_category === 'interval') {
|
||
// let resultIntervalValue = ''
|
||
// result.forEach((data) => {
|
||
// resultIntervalValue =
|
||
// data.response.data + (resultIntervalValue ? ',' + resultIntervalValue : '')
|
||
// })
|
||
// this.$emit('update:value', resultIntervalValue)
|
||
// this.$emit('onChange', resultIntervalValue)
|
||
// } else if (this.upload_result_category === 'array') {
|
||
// if (this.complete_result) {
|
||
// // 得去掉数组里面的thumbUrl,一个base64太大,无用
|
||
// let newResult = JSON.parse(JSON.stringify(newResult))
|
||
// newResult.map((e) => {
|
||
// if (e.thumbUrl) {
|
||
// delete e.thumbUrl
|
||
// }
|
||
// })
|
||
// this.emit('update:value', newResult)
|
||
// this.emit('onChange', newResult)
|
||
// } else {
|
||
// const resultArrayValue = []
|
||
// result.forEach((data) => {
|
||
// resultArrayValue.push(data.response.data)
|
||
// })
|
||
// this.emit('update:value', resultArrayValue)
|
||
// this.emit('onChange', resultArrayValue)
|
||
// }
|
||
// }
|
||
// return
|
||
// }
|
||
// this.$emit('update:value', undefined)
|
||
// this.$emit('onChange', undefined)
|
||
},
|
||
|
||
handleSuccess(response, file, fileList) {
|
||
let result = []
|
||
if (response && response.code === 0 && file.status === "success") {
|
||
fileList.forEach((f) => {
|
||
if(f.response.data?.includes('webvpn')) {
|
||
const index = f.response.data.indexOf('/platform')
|
||
f.response.data = f.response.data.substring(index)
|
||
}
|
||
result.push(f)
|
||
})
|
||
} else {
|
||
this.$message.error(response.msg)
|
||
fileList = fileList.splice(file, 1)
|
||
}
|
||
if (result.length > 0) {
|
||
if (this.upload_result_category === "interval") {
|
||
const resultArrayValue = []
|
||
result.forEach((data) => {
|
||
resultArrayValue.push(data.response.data)
|
||
})
|
||
this.$emit("update:value", resultArrayValue.join(","))
|
||
} else if (this.upload_result_category === "array") {
|
||
if (this.complete_result) {
|
||
// 得去掉数组里面的thumbUrl,一个base64太大,无用
|
||
let newResult = JSON.parse(JSON.stringify(result))
|
||
newResult.map((e) => {
|
||
if (!e.url) {
|
||
e.url = e.response.data
|
||
}
|
||
if (e.thumbUrl) {
|
||
delete e.thumbUrl
|
||
}
|
||
})
|
||
this.$emit("update:value", newResult)
|
||
} else {
|
||
const resultArrayValue = []
|
||
result.forEach((data) => {
|
||
resultArrayValue.push(data.response.data)
|
||
})
|
||
this.$emit("update:value", resultArrayValue)
|
||
}
|
||
}
|
||
return
|
||
}
|
||
this.$emit("update:value", fileList)
|
||
},
|
||
|
||
// 通过DOM获取上传的文件
|
||
uploadFileList() {
|
||
if (this.fileList) {
|
||
const result = []
|
||
// 只返回这些就够用了,其他基本用不到
|
||
this.fileList.value.forEach((item) => {
|
||
const obj = {
|
||
name: item.name,
|
||
type: item.type,
|
||
size: item.size,
|
||
url: item.response.data
|
||
}
|
||
result.push(obj)
|
||
})
|
||
return result
|
||
} else {
|
||
return []
|
||
}
|
||
}
|
||
},
|
||
watch: {
|
||
value: {
|
||
handler: function (newVal) {
|
||
if (this.value && newVal) {
|
||
this.fileList = []
|
||
this.echo(newVal)
|
||
} else {
|
||
this.fileList = []
|
||
this.$emit("update:value", undefined)
|
||
}
|
||
},
|
||
immediate: true,
|
||
deep: true
|
||
},
|
||
upload_mode: {
|
||
handler: function (newVal) {
|
||
if (newVal && newVal === "image") {
|
||
if (this.accept) {
|
||
this.fileAccept = this.accept
|
||
} else {
|
||
this.fileAccept = "image/*"
|
||
}
|
||
} else {
|
||
this.fileAccept = this.accept
|
||
}
|
||
},
|
||
immediate: true,
|
||
deep: true
|
||
},
|
||
accept: {
|
||
handler: function (newVal) {
|
||
if (newVal) {
|
||
this.fileAccept = newVal
|
||
} else {
|
||
this.fileAccept = this.accept
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
mounted() {
|
||
const timestamp = new Date().getTime()
|
||
this.qrCodeAddress = origin + "/platform/sys/h5ScanCodeUploadFile?timestamp=" + timestamp
|
||
console.info("qrCodeAddress:", this.qrCodeAddress)
|
||
// 可选:轮询或 watch ready 状态
|
||
const checkReady = () => {
|
||
if (webSocketPubSub.ws && webSocketPubSub.ws.readyState === WebSocket.OPEN) {
|
||
console.info("ws.readyState is open");
|
||
this.codeLoading = false;
|
||
} else {
|
||
setTimeout(checkReady, 100);
|
||
}
|
||
};
|
||
checkReady();
|
||
webSocketPubSub.subscribe("h5-scan-code-upload-file-" + timestamp, (data) => {
|
||
console.log("ws:收到手机上传文件消息:", data)
|
||
const originFileList = this.fileList || []
|
||
console.log(data.files)
|
||
|
||
if (this.uploadLimitNumber) {
|
||
if (originFileList.length + data.files.length > this.uploadLimitNumber) {
|
||
this.$message.error("上传文件数量超出限制")
|
||
return
|
||
}
|
||
}
|
||
|
||
if (this.fileAccept) {
|
||
// 去除前后空格并转换为数组
|
||
const acceptTypes = this.fileAccept
|
||
.trim()
|
||
.split(",")
|
||
.map((type) => type.trim().toUpperCase())
|
||
|
||
const validFiles = data.files.filter((file) => {
|
||
let fileType
|
||
const dotIndex = file.name.lastIndexOf(".")
|
||
if (dotIndex !== -1) {
|
||
fileType = file.name.substring(dotIndex).toUpperCase()
|
||
}
|
||
return acceptTypes.includes(fileType)
|
||
})
|
||
|
||
if (validFiles.length < data.files.length) {
|
||
data.files = validFiles
|
||
this.$message.error(`文件只能是 ${this.fileAccept} 格式,已自动过滤掉不符合要求文件!`)
|
||
}
|
||
}
|
||
|
||
this.$message.success("获取手机文件成功")
|
||
|
||
const newFileList = originFileList.concat(data.files)
|
||
this.$emit("update:value", newFileList)
|
||
})
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.upload_file {
|
||
width: 100%;
|
||
}
|
||
|
||
.upload_file .el-upload {
|
||
/*width: 100%;*/
|
||
text-align: left;
|
||
}
|
||
|
||
.upload_file .el-upload .el-upload-dragger {
|
||
width: 100%;
|
||
line-height: 26px;
|
||
height: auto;
|
||
border: 1px solid var(--border-color-base);
|
||
}
|
||
|
||
.el-upload-dragger .el-icon-upload {
|
||
margin: 10px 0 16px;
|
||
}
|
||
|
||
.drag-upload .el-upload {
|
||
width: 100%;
|
||
}
|
||
|
||
.upload_file .drag-upload-disabled > .el-upload {
|
||
pointer-events: none;
|
||
}
|
||
|
||
.el-upload--picture-card {
|
||
width: var(--upload-width, 148px) !important;
|
||
height: var(--upload-height, 148px) !important;
|
||
/*line-height: calc(var(--upload-height, 148px) - 2px) !important;*/
|
||
line-height: unset !important;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.el-upload-list--picture-card .el-upload-list__item {
|
||
width: var(--upload-width, 148px);
|
||
height: var(--upload-height, 148px);
|
||
}
|
||
|
||
</style>
|