first commit

This commit is contained in:
2026-01-08 14:58:16 +08:00
commit 556e5d64aa
9575 changed files with 1621095 additions and 0 deletions
@@ -0,0 +1,207 @@
<template>
<div>
<van-uploader
v-model="fileList"
:before-read="beforeRead"
:after-read="afterRead"
:before-delete="beforeDelete"
multiple
progress
:accept="accept"
:max-count="upload_number"
title=""
description=""
tip=""
></van-uploader>
</div>
</template>
<script>
module.exports = {
name: "h5Index",
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
},
// 上传返回id或url
upload_result_type: {
type: String,
default: "url",
required: false
},
// 上传返回分类 数组或字符串逗号隔离 interval | array
upload_result_category: {
type: String,
default: "interval",
required: false
},
// 跟antdv官方一样,是否显示文件列表
show_upload_list: {
type: Boolean,
default: true,
required: false
},
// 跟antdv官方一样,接受上传的文件类型
accept: {
type: String,
default: "",
required: false
},
// 是否是完整的结果(就是文件上传返回什么,该组件返回什么,uploadResultCategory必须为array
complete_result: {
type: Boolean,
default: false,
required: false
},
// 父组件传来的参数
value: {
type: [String, Array],
required: false
}
},
data() {
return {
fileList: [
// { url: "http://localhost:8080/platform/sys/file/download?id=rn1v1efh2ag0aps59ult3ebudf", isImage: true }
]
}
},
watch: {
value: {
handler: function (val) {
if (val) {
if (Array.isArray(val)) {
this.fileList = val.map((v) => {
return {
...v,
status: null,
isImage: ["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(v.name.split(".").pop().toLowerCase())
}
})
} else if(typeof val === "string") {
this.fileList = [
{
url: val,
status: null,
isImage: true
}
]
} else {
val = JSON.parse(val)
this.fileList = val.map((v) => {
return {
...v,
url: v.url ? v.url : v.response?.data,
isImage: ["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(v.name.split(".").pop().toLowerCase()),
status: null
}
})
}
} else {
this.fileList = []
}
},
immediate: true
}
},
computed: {
action() {
return this.upload_result_type === "id" ? this.upload_return_id_api : this.upload_dynamic_return_url_api
}
},
methods: {
beforeDelete(file) {
this.fileList = this.fileList.filter((f) => f.url !== file.url)
this.$emit("update:value", this.fileList)
},
beforeRead(file) {
return true
},
afterRead(files) {
this.fileList.map((f) => {
if (f.file && f.file.name === files.file.name) {
f.status = "loading"
}
})
const uploadPromises = this.fileList
.filter((f) => f.status === "loading")
.map((f) => {
const formData = new FormData()
formData.append("file", f.file)
return this.$axios.post(this.action, formData).then((resp) => {
if (resp.code === 0) {
f.name = f.file.name
f.size = f.file.size
f.status = null
f.url = resp.data
f.response = resp
f.percentage = 100
f.isImage = true
delete f.file
delete f.content
} else {
f.status = "fail"
}
})
})
Promise.all(uploadPromises)
.then(() => {
if (this.upload_result_category === "interval") {
} else if (this.upload_result_category === "array") {
if (this.complete_result) {
this.$emit("update:value", this.fileList)
} else {
const resultArrayValue = []
this.fileList.forEach((data) => {
resultArrayValue.push(data.response.data)
})
this.$emit("update:value", resultArrayValue)
}
}
})
.catch((error) => {
console.error("所有上传请求失败:", error)
})
}
},
created() {
}
}
</script>
<style scoped>
.van-uploader__title {
padding-left: 0;
}
.van-uploader__wrapper {
margin-left: 0;
}
</style>
@@ -0,0 +1,646 @@
<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"
>
<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}</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 (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") {
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);
}
</style>