Files
zhgh_jshvc/src/main/resources/static/components/plugins/sysUpload/index.vue
T
2026-01-28 15:43:10 +08:00

652 lines
19 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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"
:before-upload="beforeUpload"
:limit="upload_number"
: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="upload_number"
:accept="fileAccept"
:multiple="true"
list-type="picture-card"
:class="{ hide: hideUpload }"
>
<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="upload_number"
: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,
default: 1,
required: false
},
// 上传文件大小限制
upload_size: {
type: Number,
default: 1024 * 1024 * 10,
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
},
upload_tips() {
const uploadNumber = this.upload_number
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 (fileSize) {
tips = tips + `单个文件大小不能超过<span style="color: red">${(fileSize / 1024 / 1024).toFixed(2)}</span>M`
}
return tips
},
hideUpload() {
// 实时根据文件列表长度和限制数量计算
return this.fileList.length >= this.upload_number;
},
},
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 (file.size > this.upload_size) {
this.$message.error("文件过大,请上传小于" + (this.upload_size / 1024 / 1024).toFixed(2) + "M的文件!")
return false
}
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") {
if (this.upload_result_type === "id") {
}
if (this.upload_result_type === "url") {
}
}
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)
)
}
}
} else {
this.$emit("update:value", fileList)
}
},
handleExceed(files, fileList) {
this.$message.warning("最多只能上传" + this.upload_number + "个文件")
},
// 上传事件
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.upload_number) {
if (originFileList.length + data.files.length > this.upload_number) {
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);
}
.hide .el-upload--picture-card {
display: none;
}
</style>