chenlu
8 小时以前 33dbc6a161554f3a897f9e9273feb4f2c1b47381
north-glass-erp/northglass-erp/src/components/sd/order/UploadPicture.vue
New file
@@ -0,0 +1,450 @@
<script setup>
import { ref, reactive, onMounted } from 'vue'
import {
  Document, UploadFilled, MagicStick, RefreshLeft,
  Download, Plus, Picture, Refresh, Delete
} from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import request from "@/utils/requestByFile"
import {useI18n} from "vue-i18n";
const { t } = useI18n()
const uploadRef = ref()
const fileList = ref([])
const converting = ref(false)
const progressPercentage = ref(0)
const progressStatus = ref('')
const progressText = ref('')
const conversionResult = ref(null)
const supportedFormats = ref(['png', 'jpg', 'jpeg', 'bmp'])
const loadingFormats = ref(false)
let result = ref(null)
let props = defineProps({
  rowIndex:null,
  orderId:null,
  state:null
})
const form = reactive({
  format: 'png'
})
onMounted(() => {
  getFileList()
})
const getFileList = async () => {
  request.post(`/orderFile/getOrderNumberFile/${props.orderId}/${props.rowIndex.orderNumber}`).then(res=>{
    if(res.data===null){
      return
    }
    result.value = res.data
    conversionResult.value = res.data.imageBase64
  })
}
const deleteFile = async () => {
  request.post(`/orderFile/deleteOrderNumberFile/${props.orderId}/${props.rowIndex.orderNumber}`).then(res=>{
    result.value = null
    conversionResult.value = null
    ElMessage.success(t("basicData.msg.deleteSuccess"))
  })
}
const downloadFile = () => {
  try {
    // 移除字符串中的方括号和空格
    const cleanString = result.value.fileData.replace(/[\[\]\s]/g, '');
    // 如果字符串为空,返回空的Uint8Array
    if (!cleanString) {
      return new Uint8Array();
    }
    // 分割字符串并转换为数字数组
    const byteStrings = cleanString.split(',');
    const bytes = byteStrings.map(byteStr => {
      // 将字符串转换为数字(处理负数)
      let value = parseInt(byteStr, 10);
      // Java字节是有符号的(-128到127),需要转换为无符号(0到255)
      if (value < 0) {
        value = 256 + value;
      }
      return value;
    });
    const uint8Array = new Uint8Array(bytes);
    const file = new File([uint8Array], `${props.orderId}-${props.rowIndex.orderNumber}.dwg`, { type:'application/x-dwg'  })
    const url = URL.createObjectURL(file);
    // 创建下载链接
    const a = document.createElement('a');
    a.href = url;
    a.download = file.name;
    a.style.display = 'none';
    // 添加到DOM并触发点击
    document.body.appendChild(a);
    a.click();
    // 清理资源
    setTimeout(() => {
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
    }, 100);
  } catch (error) {
    console.error('解析Java字节数组字符串时出错:', error);
    throw new Error('无效的Java字节数组格式');
  }
}
const loadSupportedFormats = async () => {
  try {
    if (fileList.value.length === 0) {
      ElMessage.warning(t("order.msg.pleaseUploadPicture1"))
      return
    }
    loadingFormats.value = true
    converting.value = true
    progressPercentage.value = 0
    progressStatus.value = ''
    progressText.value = t("order.msg.pleaseUploadPicture2")
    // 模拟进度更新
    const progressInterval = setInterval(() => {
      if (progressPercentage.value < 80) {
        progressPercentage.value += 10
        progressText.value = t("order.msg.pleaseUploadPicture3")+progressPercentage.value+'%'
      }
    }, 500)
    const data ={
      file:fileList.value[0].raw,
      name:fileList.value[0].raw.name
    }
    request.post(`/orderFile/updateOrderFileByOrderNumber/${props.orderId}/${props.rowIndex.orderNumber}`,data).then(res=>{
      if (res.code === '200') {
        result.value = res.data
        conversionResult.value = res.data.imageBase64
        uploadRef.value.clearFiles()
        clearInterval(progressInterval)
        progressPercentage.value = 100
        progressStatus.value = 'success'
        progressText.value = t("order.msg.pleaseUploadPicture4")
        uploadRef.value.clearFiles()
        fileList.value = []
        setTimeout(() => {
          converting.value = false
        },2000)
      }
    })
  } catch (error) {
    ElMessage.error(t("order.msg.pleaseUploadPicture5"))
  } finally {
    loadingFormats.value = false
  }
}
const fileTypeCheck = (file) => {
  const fileName = file.raw.name.toLowerCase();
  switch (true) {
    case /\.dwg$/.test(fileName):
    case /\.png$/.test(fileName):
    case /\.jpg$/.test(fileName):
      return true;
    default:
      return false;
  }
};
const handleFileChange = (file) => {
  const fileTypeCheckBoole = fileTypeCheck(file)
  if (!(fileTypeCheckBoole )) {
    //ElMessage.error('请选择DWG或DXF格式的文件')
    ElMessage.error(t("order.msg.pleaseUploadPicture6"))
    uploadRef.value.clearFiles()
    return
  }
  if (file.raw.size > 50 * 1024 * 1024) {
    ElMessage.error(t("order.msg.pleaseUploadPicture7"))
    uploadRef.value.clearFiles()
    return
  }
  fileList.value = [file]
  ElMessage.success(t("order.msg.pleaseUploadPicture8")+file.name)
}
const handleFileRemove = () => {
  conversionResult.value = null
  result.value = null
  uploadRef.value.clearFiles()
}
const handleReset = () => {
  uploadRef.value.clearFiles()
  fileList.value = []
  conversionResult.value = null
  progressPercentage.value = 0
  progressStatus.value = ''
  progressText.value = ''
  form.format = 'png'
  result.value = null
}
const formatFileSize = (bytes) => {
  if (bytes === 0) return '0 Bytes'
  const k = 1024
  const sizes = ['Bytes', 'KB', 'MB', 'GB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
</script>
<template>
  <div class="file-upload-container">
    <el-card class="upload-card" shadow="hover">
      <template #header>
        <div class="card-header">
          <span class="header-title">
            <el-icon><Document /></el-icon>
            {{$t("order.msg.pleaseUploadPicture9")}}
          </span>
        </div>
      </template>
      <!-- 文件上传区域 -->
      <el-upload
          v-if="!conversionResult"
          ref="uploadRef"
          class="upload-demo"
          drag
          action=""
          :auto-upload="false"
          :on-change="handleFileChange"
          :on-remove="handleFileRemove"
          :file-list="fileList"
          :limit="1"
          :disabled="converting"
      >
        <el-icon class="el-icon--upload"><UploadFilled /></el-icon>
        <div class="el-upload__text">
          {{$t("order.msg.pleaseUploadPicture10")}} <em>{{$t("order.msg.pleaseUploadPicture11")}}</em>
        </div>
        <template #tip>
          <div class="el-upload__tip">
            {{$t("order.msg.pleaseUploadPicture12")}}
          </div>
        </template>
      </el-upload>
      <!-- 上传选项 -->
      <div v-if="!conversionResult"  class="conversion-options">
        <div class="action-buttons">
          <el-button
              type="primary"
              :loading="converting"
              @click="loadSupportedFormats"
              :disabled="!form.format || props.state !== 1 || converting"
          >
            <template #icon>
              <el-icon><MagicStick /></el-icon>
            </template>
            {{$t("basicData.save")}}
          </el-button>
          <el-button @click="handleReset"
                     :loading="converting"
                     :disabled="props.state !== 1 || converting">
            <template #icon>
              <el-icon><RefreshLeft /></el-icon>
            </template>
            {{$t("craft.reset")}}
          </el-button>
        </div>
      </div>
      <!-- 转换进度 -->
      <div v-if="converting" class="conversion-progress">
        <el-divider content-position="left">{{$t("order.msg.pleaseUploadPicture13")}}</el-divider>
        <el-progress
            :percentage="progressPercentage"
            :status="progressStatus"
            :stroke-width="8"
        />
        <div class="progress-text">
          {{ progressText }}
        </div>
      </div>
      <!-- 转换结果 -->
      <div v-if="conversionResult" class="conversion-result">
        <el-divider content-position="left">{{$t("order.msg.pleaseUploadPicture14")}}</el-divider>
        <el-result
            icon="success"
            :sub-title='t("order.msg.pleaseUploadPicture15")'
        >
          <template #extra>
            <div class="result-content">
              <!-- 图片预览 -->
              <div class="image-preview">
                <el-image
                    :src="conversionResult"
                    :preview-src-list="[conversionResult]"
                    fit="contain"
                    style="max-height: 600px;"
                >
<!--                  <template #error>-->
<!--                    <div class="image-slot">-->
<!--                      <el-icon><Picture /></el-icon>-->
<!--                      <div>图片加载失败</div>-->
<!--                    </div>-->
<!--                  </template>-->
                </el-image>
              </div>
              <!-- 操作按钮 -->
              <div class="result-actions">
                <el-button
                    @click='downloadFile'
                    type="primary"
                ><template #icon>
                  <el-icon><Download/></el-icon>
                </template>
                  {{$t("order.msg.pleaseUploadPicture16")}}
                </el-button>
                <el-button
                    @click = 'deleteFile'
                    :disabled="props.state !== 1"
                ><template #icon>
                  <el-icon><Delete  /></el-icon>
                </template>
                  {{$t("basicData.delete")}}
                </el-button>
              </div>
            </div>
          </template>
        </el-result>
      </div>
    </el-card>
  </div>
</template>
<style scoped>
.file-upload-container {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}
.upload-card {
  min-height: 600px;
}
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.header-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 18px;
  font-weight: bold;
}
:deep(.upload-demo) {
  width: 100%;
}
:deep(.el-upload-dragger) {
  width: 100%;
  padding: 40px;
}
.conversion-options {
  margin-top: 20px;
}
.action-buttons {
  display: flex;
  gap: 12px;
  justify-content: center;
  margin-top: 20px;
}
.conversion-progress {
  margin-top: 20px;
}
.progress-text {
  text-align: center;
  margin-top: 8px;
  color: #666;
  font-size: 14px;
}
.conversion-result {
  margin-top: 20px;
}
.result-content {
  text-align: center;
}
.image-preview {
  margin: 20px 0;
  display: flex;
  justify-content: center;
}
.file-info {
  margin: 20px 0;
  max-width: 400px;
  margin-left: auto;
  margin-right: auto;
}
.result-actions {
  display: flex;
  gap: 12px;
  justify-content: center;
  margin-top: 20px;
}
.image-slot {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 200px;
  color: #909399;
}
.image-slot .el-icon {
  font-size: 48px;
  margin-bottom: 16px;
}
</style>