|
@@ -1,43 +1,33 @@
|
|
|
<template>
|
|
|
<div class="webuploader-container">
|
|
|
- <el-upload
|
|
|
- :action="uploadUrl"
|
|
|
- :before-upload="handleBeforeUpload"
|
|
|
- :on-success="handleSuccess"
|
|
|
- :on-error="handleError"
|
|
|
- :on-progress="handleProgress"
|
|
|
- :show-file-list="false"
|
|
|
- :headers="uploadHeaders"
|
|
|
- :data="uploadData"
|
|
|
- :accept="props.accept"
|
|
|
- :auto-upload="true"
|
|
|
- class="upload-box"
|
|
|
- :disabled="props.disabled"
|
|
|
+ <!-- 上传按钮 -->
|
|
|
+ <div
|
|
|
+ class="btntext"
|
|
|
+ :style="{ width: '24px', cursor: props.disabled ? 'not-allowed' : 'pointer', opacity: props.disabled ? 0.5 : 1 }"
|
|
|
+ @click="selectFile"
|
|
|
>
|
|
|
- <!-- 自定义上传按钮 -->
|
|
|
- <div
|
|
|
- class="btntext upname"
|
|
|
- :style="{ width: '24px', cursor: props.disabled ? 'not-allowed' : 'pointer', opacity: props.disabled ? 0.5 : 1 }"
|
|
|
- >
|
|
|
- <img :src="props.imgSrc" alt="upload icon" class="custom-icon" />
|
|
|
- </div>
|
|
|
- </el-upload>
|
|
|
+ <img :src="props.imgSrc" alt="upload icon" class="custom-icon" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 隐藏文件选择框 -->
|
|
|
+ <input
|
|
|
+ ref="fileInput"
|
|
|
+ type="file"
|
|
|
+ :accept="props.accept"
|
|
|
+ style="display: none"
|
|
|
+ @change="handleFileChange"
|
|
|
+ />
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
+import { ref } from 'vue'
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
import SparkMD5 from 'spark-md5'
|
|
|
-import { request } from '@/utils/request'
|
|
|
+import { uploadFile } from '@/utils/request'
|
|
|
|
|
|
const props = defineProps({
|
|
|
- projectId: Number,
|
|
|
- solverType: String,
|
|
|
accept: String,
|
|
|
- upId: String,
|
|
|
- name: String,
|
|
|
- namelist: Array,
|
|
|
- gfname: String,
|
|
|
imgSrc: String,
|
|
|
disabled: { type: Boolean, default: false }
|
|
|
})
|
|
@@ -48,85 +38,86 @@ const emit = defineEmits([
|
|
|
'upload-status',
|
|
|
])
|
|
|
|
|
|
-let fileMd5 = ref('')
|
|
|
+const fileInput = ref(null)
|
|
|
let fileName = ref('')
|
|
|
-let uuid = ref(generateUUID())
|
|
|
-
|
|
|
-const uploadUrl = import.meta.env.VITE_BASE_URL + '/TransServlet'
|
|
|
+let uuid = ref('')
|
|
|
|
|
|
-const uploadHeaders = {
|
|
|
- Content: 'multipart',
|
|
|
- Type: 'form-data',
|
|
|
+// 点击按钮触发文件选择
|
|
|
+function selectFile() {
|
|
|
+ if (props.disabled) return
|
|
|
+ fileInput.value.click()
|
|
|
}
|
|
|
|
|
|
-const uploadData = computed(() => ({
|
|
|
- transCode: 'B00028',
|
|
|
- channelNo: 'service',
|
|
|
- clientToken: 'e47b87eec69545559d1e81e56626da68',
|
|
|
- userId: '5f06c8bc77234f969d13e160b54c27e3',
|
|
|
- fileName: fileName.value,
|
|
|
- bfid: uuid.value,
|
|
|
-}))
|
|
|
-
|
|
|
-function generateUUID() {
|
|
|
- return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
|
- const r = (Math.random() * 16) | 0
|
|
|
- const v = c === 'x' ? r : (r & 0x3) | 0x8
|
|
|
- return v.toString(16)
|
|
|
- })
|
|
|
-}
|
|
|
+// 文件选择后触发
|
|
|
+async function handleFileChange(event) {
|
|
|
+ const file = event.target.files[0]
|
|
|
+ if (!file) return
|
|
|
|
|
|
-async function handleBeforeUpload(rawFile) {
|
|
|
- if (props.disabled) return false
|
|
|
- fileName.value = rawFile.name
|
|
|
+ fileName.value = file.name
|
|
|
emit('update-fileName', fileName.value)
|
|
|
+ emit('upload-status', '上传中')
|
|
|
|
|
|
- const ext = fileName.value.split('.').pop().toLowerCase()
|
|
|
- const acceptList = props.accept
|
|
|
- ? props.accept.split(',').map(e => e.trim().replace(/^\./, '').toLowerCase())
|
|
|
- : []
|
|
|
-
|
|
|
- if (acceptList.length > 0 && !acceptList.includes(ext)) {
|
|
|
- ElMessage.error(`只支持 ${props.accept} 格式文件!`)
|
|
|
- return false
|
|
|
- }
|
|
|
-
|
|
|
- fileMd5.value = await calculateMD5(rawFile)
|
|
|
uuid.value = generateUUID()
|
|
|
- emit('upload-status', '上传中')
|
|
|
- return true
|
|
|
-}
|
|
|
|
|
|
-function handleSuccess(response, file) {
|
|
|
- if (response.returnCode !== '000000000') {
|
|
|
- ElMessage.error(response.returnMsg || '上传失败')
|
|
|
+ try {
|
|
|
+ await uploadInChunks(file)
|
|
|
+ await mergeChunks()
|
|
|
+ emit('upload-success', { bfid: uuid.value, fname: fileName.value })
|
|
|
+ emit('upload-status', '上传成功')
|
|
|
+ } catch (err) {
|
|
|
+ ElMessage.error(err.message || '上传失败')
|
|
|
emit('upload-status', '上传失败')
|
|
|
- return
|
|
|
+ emit('update-percentage', 0)
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- emit('upload-success', {
|
|
|
- bfid: response.bfid,
|
|
|
- fname: file.name,
|
|
|
- })
|
|
|
- emit('upload-status', '上传成功')
|
|
|
-
|
|
|
- uuid.value = generateUUID()
|
|
|
+// 分片上传
|
|
|
+async function uploadInChunks(file) {
|
|
|
+ const chunkSize = 2 * 1024 * 1024 // 2MB
|
|
|
+ const totalChunks = Math.ceil(file.size / chunkSize)
|
|
|
+
|
|
|
+ for (let i = 0; i < totalChunks; i++) {
|
|
|
+ const start = i * chunkSize
|
|
|
+ const end = Math.min(file.size, start + chunkSize)
|
|
|
+ const chunkFile = file.slice(start, end)
|
|
|
+
|
|
|
+ const formData = new FormData()
|
|
|
+ formData.append('bfid', uuid.value)
|
|
|
+ formData.append('chunk', i + 1)
|
|
|
+ formData.append('chunks', totalChunks)
|
|
|
+ formData.append('file', chunkFile)
|
|
|
+ formData.append('fileName', fileName.value)
|
|
|
+ formData.append('transCode', 'B00028') // 分片接口固定字段
|
|
|
+
|
|
|
+ await uploadFile(formData, 'service', (progress) => {
|
|
|
+ const chunkPercent = (1 / totalChunks) * progress
|
|
|
+ const percent = Math.min(Math.floor((i / totalChunks) * 100 + chunkPercent), 100)
|
|
|
+ emit('update-percentage', percent)
|
|
|
+ })
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-function handleError() {
|
|
|
- emit('upload-status', '上传失败')
|
|
|
- emit('update-fileName', '')
|
|
|
- uuid.value = generateUUID()
|
|
|
+// 合并接口
|
|
|
+async function mergeChunks() {
|
|
|
+ const formData = new FormData()
|
|
|
+ formData.append('bfid', uuid.value)
|
|
|
+ formData.append('transCode', 'B00029') // 合并接口固定字段
|
|
|
+ await uploadFile(formData, 'service')
|
|
|
}
|
|
|
|
|
|
-function handleProgress(event, file, fileList) {
|
|
|
- const percentage = Math.floor(event.percent)
|
|
|
- emit('update-percentage', percentage)
|
|
|
+// 生成 UUID
|
|
|
+function generateUUID() {
|
|
|
+ return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
|
+ const r = (Math.random() * 16) | 0
|
|
|
+ const v = c === 'x' ? r : (r & 0x3) | 0x8
|
|
|
+ return v.toString(16)
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
+// 计算文件 MD5(可选,不影响上传)
|
|
|
function calculateMD5(file) {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
- const chunkSize = 2097152 // 2MB
|
|
|
+ const chunkSize = 2 * 1024 * 1024
|
|
|
const chunks = Math.ceil(file.size / chunkSize)
|
|
|
let currentChunk = 0
|
|
|
const spark = new SparkMD5.ArrayBuffer()
|
|
@@ -148,7 +139,7 @@ function calculateMD5(file) {
|
|
|
|
|
|
function loadNext() {
|
|
|
const start = currentChunk * chunkSize
|
|
|
- const end = Math.min(start + chunkSize, file.size)
|
|
|
+ const end = Math.min(file.size, start + chunkSize)
|
|
|
fileReader.readAsArrayBuffer(file.slice(start, end))
|
|
|
}
|
|
|
|
|
@@ -161,7 +152,9 @@ function calculateMD5(file) {
|
|
|
.webuploader-container {
|
|
|
position: relative;
|
|
|
}
|
|
|
-.upload-box {
|
|
|
+.btntext {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
}
|