فهرست منبع

814文件上传改

tangjunhao 4 هفته پیش
والد
کامیت
3191b3c2a3
3فایلهای تغییر یافته به همراه111 افزوده شده و 120 حذف شده
  1. 25 28
      src/utils/request.ts
  2. 82 89
      src/views/components/fileuploads.vue
  3. 4 3
      src/views/mainContent/leftaside/agentModel.vue

+ 25 - 28
src/utils/request.ts

@@ -141,37 +141,34 @@ const request = (params, channelNo = 'service', method = 'post') => {
   })
 }
 
-// 文件上传
-const uploadFile = (params, channelNo = 'service', callback1) => {
-  let config = {
+/**
+ * 上传文件(支持 FormData)
+ * @param {FormData} formData 
+ * @param {string} channelNo 
+ * @param {Function} onProgress 上传进度回调,可选
+ */
+const uploadFile = (formData, channelNo = 'service', onProgress) => {
+  // 自动加固定字段
+  formData.append('clientToken', getToken() || 'e47b87eec69545559d1e81e56626da68')
+  formData.append('userId', getUserId() || '5f06c8bc77234f969d13e160b54c27e3')
+  formData.append('channelNo', channelNo)
+
+  return axios.post(getUrl(channelNo), formData, {
     timeout: 60000,
-    headers: { "Content-Type": "multipart/form-data" },
-    onUploadProgress: progressEvent => {
-      //属性lengthComputable主要表明总共需要完成的工作量和已经完成的工作是否可以被测量,如果lengthComputable为false,就获取不到progress.total和progress.loaded
-      if (progressEvent.lengthComputable) {
-        let loaded = progressEvent.loaded
-        let total = progressEvent.total
-        // let progress = Math.floor((loaded / total) * 100) > 1 ? Math.floor((loaded / total) * 100) : 1;
-        // console.log('上传进度 ' + progress + '%');
-        // callback1(progress)
-        
+    headers: { 'Content-Type': 'multipart/form-data' },
+    onUploadProgress: e => {
+      if (e.lengthComputable && typeof onProgress === 'function') {
+        const percent = e.total ? Math.floor((e.loaded / e.total) * 100) : 0
+        onProgress(percent)
       }
     }
-  }
-  let url = getUrl(channelNo);//完善请求url
-  params.append("clientToken", store.getters.token || getToken());
-  params.append("userId", store.getters.userId);
-  params.append("channelNo", channelNo);//配置上行报文公共包头
-  console.log(params);
-  return new Promise((resolve, reject) => {
-    axios.post(url, params, config).then(res => {
-      resolve(res)
-    }).catch(err => {
-      if (err != 'cancel') {
-        message.error(err.returnMsg)
-      }
-      reject(err)
-    })
+  })
+  .then(res => res)
+  .catch(err => {
+    if (err !== 'cancel') {
+      message.error(err.returnMsg || '上传失败')
+    }
+    return Promise.reject(err)
   })
 }
 

+ 82 - 89
src/views/components/fileuploads.vue

@@ -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;
 }

+ 4 - 3
src/views/mainContent/leftaside/agentModel.vue

@@ -43,7 +43,7 @@
               >
                 <el-input 
                   v-if="item.vo.valType === 2"
-                  v-model="item.vo.val" 
+                  v-model="item.vo.filename"
                   size="small"
                 >
                   <template #append>
@@ -142,13 +142,14 @@ watch(
 
 // 上传成功回调
 function handleUploadSuccess(fileInfo, item) {
-  item.vo.val = fileInfo.fname
+  console.log('fileInfo',fileInfo)
+  item.vo.val = fileInfo.bfid
   uploadPercent.value = 0
 }
 
 // 文件名更新回调
 function handleUpdateFileName(name, item) {
-  item.vo.val = name
+  item.vo.filename = name
   console.log('选中文件名', name, item)
 }