chenhj
2026-04-24 4db10a5cb999af821310a9823f3fec99a1c46319
文件上传修改
已修改5个文件
224 ■■■■ 文件已修改
src/components/AttachmentPreview/image/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/AttachmentUpload/image/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue 61 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/deliveryLedger/index.vue 151 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/AttachmentPreview/image/index.vue
@@ -1,6 +1,6 @@
<script setup>
const props = defineProps({
  list: {
  fileList: {
    type: Array,
    default: () => [],
  },
@@ -15,7 +15,7 @@
})
const normalizedList = computed(() => {
  return (props.list || [])
  return (props.fileList || [])
    .filter((item) => item && item.previewURL)
    .map((item, index) => ({
      id: item.id ?? index,
src/components/AttachmentUpload/image/index.vue
@@ -88,7 +88,7 @@
})
const uploadTip = computed(() => {
  return `支持 ${props.fileType.join('/')},单张不超过 ${props.fileSize}MB`
  return `支持 ${props.fileType.join('/')},单张不超过 ${props.fileSize}MB,最多上传 ${props.limit} 张图片`
})
function getItemUid(item, index) {
src/main.js
@@ -43,11 +43,11 @@
// 富文本组件
import Editor from "@/components/Editor";
// 文件上传组件
import FileUpload from "@/components/FileUpload";
import FileUpload from "@/components/AttachmentUpload/file";
// 图片上传组件
import ImageUpload from "@/components/ImageUpload";
import ImageUpload from "@/components/AttachmentUpload/image";
// 图片预览组件
import ImagePreview from "@/components/ImagePreview";
import ImagePreview from "@/components/AttachmentPreview/image";
// 字典标签组件
import DictTag from "@/components/DictTag";
// 表格组件
src/views/equipmentManagement/inspectionManagement/components/viewFiles.vue
@@ -134,40 +134,6 @@
const currentMediaIndex = ref(0);
const mediaList = ref([]); // 存储当前要查看的媒体列表(含图片和视频对象)
const mediaType = ref('image'); // image | video
const javaApi = proxy.javaApi;
// 处理 URL:将 Windows 路径转换为可访问的 URL
function processFileUrl(fileUrl) {
  if (!fileUrl) return '';
  // 如果 URL 是 Windows 路径格式(包含反斜杠),需要转换
  if (fileUrl && fileUrl.indexOf('\\') > -1) {
    // 查找 uploads 关键字的位置,从那里开始提取相对路径
    const uploadsIndex = fileUrl.toLowerCase().indexOf('uploads');
    if (uploadsIndex > -1) {
      // 从 uploads 开始提取路径,并将反斜杠替换为正斜杠
      const relativePath = fileUrl.substring(uploadsIndex).replace(/\\/g, '/');
      fileUrl = '/' + relativePath;
    } else {
      // 如果没有找到 uploads,提取最后一个目录和文件名
      const parts = fileUrl.split('\\');
      const fileName = parts[parts.length - 1];
      fileUrl = '/uploads/' + fileName;
    }
  }
  // 确保所有非 http 开头的 URL 都拼接 baseUrl
  if (fileUrl && !fileUrl.startsWith('http')) {
    // 确保路径以 / 开头
    if (!fileUrl.startsWith('/')) {
      fileUrl = '/' + fileUrl;
    }
    // 拼接 baseUrl
    fileUrl = javaApi + fileUrl;
  }
  return fileUrl;
}
// 处理每一类数据:分离图片和视频
function processItems(items) {
@@ -180,24 +146,18 @@
  }
  
  items.forEach(item => {
    if (!item || !item.url) return;
    if (!item || !item.previewURL || !item.contentType) return;
    
    // 处理文件 URL
    const fileUrl = processFileUrl(item.url);
    const fileUrl = item.previewURL;
    const contentType = String(item.contentType).toLowerCase();
    
    // 根据文件扩展名判断是图片还是视频
    const urlLower = fileUrl.toLowerCase();
    if (urlLower.match(/\.(jpg|jpeg|png|gif|bmp|webp)$/)) {
    // 根据 contentType 判断是图片还是视频
    if (contentType.startsWith('image/')) {
      images.push(fileUrl);
    } else if (urlLower.match(/\.(mp4|avi|mov|wmv|flv|mkv|webm)$/)) {
    } else if (contentType.startsWith('video/')) {
      videos.push(fileUrl);
    } else if (item.contentType) {
      // 如果有 contentType,使用 contentType 判断
      if (item.contentType.startsWith('image/')) {
        images.push(fileUrl);
      } else if (item.contentType.startsWith('video/')) {
        videos.push(fileUrl);
      }
    }
  });
  
@@ -207,10 +167,9 @@
// 打开弹窗并加载数据
const openDialog = async (row) => {
  // 使用正确的字段名:commonFileListBefore, commonFileListAfter
  // productionIssues 可能不存在,使用空数组
  const { images: beforeImgs, videos: beforeVids } = processItems(row.commonFileListBefore || []);
  const { images: afterImgs, videos: afterVids } = processItems(row.commonFileListAfter || []);
  const { images: issueImgs, videos: issueVids } = processItems(row.productionIssues || []);
  const { images: beforeImgs, videos: beforeVids } = processItems(row.commonFileListBeforeVO || []);
  const { images: afterImgs, videos: afterVids } = processItems(row.commonFileListAfterVO || []);
  const { images: issueImgs, videos: issueVids } = processItems(row.commonFileListVO || []);
  
  beforeProductionImgs.value = beforeImgs;
  beforeProductionVideos.value = beforeVids;
src/views/salesManagement/deliveryLedger/index.vue
@@ -3,15 +3,18 @@
    <div class="search_form">
      <el-form :model="searchForm" :inline="true">
        <el-form-item label="销售订单号:">
          <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search" style="width: 200px"
          <el-input v-model="searchForm.salesContractNo" placeholder="请输入" clearable prefix-icon="Search"
                    style="width: 200px"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="车牌号:">
          <el-input v-model="searchForm.shippingCarNumber" placeholder="请输入" clearable prefix-icon="Search" style="width: 200px"
          <el-input v-model="searchForm.shippingCarNumber" placeholder="请输入" clearable prefix-icon="Search"
                    style="width: 200px"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item label="快递单号:">
          <el-input v-model="searchForm.expressNumber" placeholder="请输入" clearable prefix-icon="Search" style="width: 200px"
          <el-input v-model="searchForm.expressNumber" placeholder="请输入" clearable prefix-icon="Search"
                    style="width: 200px"
            @change="handleQuery" />
        </el-form-item>
        <el-form-item>
@@ -53,25 +56,29 @@
              link 
              type="primary"
              :disabled="!isApproved(scope.row.status)"
              @click="openForm('edit', scope.row)">补充发货信息</el-button>
                @click="openForm('edit', scope.row)">补充发货信息
            </el-button>
            <el-button
              link
              type="primary"
                            style="color: #67C23A"
              @click="openDetail(scope.row)"
            >详情</el-button>
            >详情
            </el-button>
            <el-button 
              link 
              type="danger"
              :disabled="isApproving(scope.row.status)"
              @click="handleDeleteSingle(scope.row)">删除</el-button>
                @click="handleDeleteSingle(scope.row)">删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
        :page="page.current" :limit="page.size" @pagination="paginationChange" />
    </div>
    <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增发货台账' : '编辑发货台账'" width="40%"
    <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '新增发货台账' : '编辑发货台账'"
               width="40%"
      @close="closeDia">
      <el-form :model="form" label-width="120px" label-position="top" :rules="rules" ref="formRef">
        <el-row :gutter="30">
@@ -138,29 +145,7 @@
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="发货图片:">
              <el-upload
                v-model:file-list="deliveryFileList"
                :action="upload.url"
                multiple
                ref="deliveryFileUpload"
                auto-upload
                :headers="upload.headers"
                :data="{ type: 9 }"
                :before-upload="handleDeliveryBeforeUpload"
                :on-error="handleDeliveryUploadError"
                :on-success="handleDeliveryUploadSuccess"
                :on-remove="handleDeliveryRemove"
                list-type="picture-card"
                :limit="9"
                accept="image/png,image/jpeg,image/jpg"
              >
                <el-icon class="avatar-uploader-icon"><Plus /></el-icon>
                <template #tip>
                  <div class="el-upload__tip">
                    支持 jpg、jpeg、png 格式,最多上传 9 张,单张大小不超过 10MB
                  </div>
                </template>
              </el-upload>
              <ImageUpload v-model:file-list="deliveryFileList" :limit="9"/>
            </el-form-item>
          </el-col>
        </el-row>
@@ -189,19 +174,7 @@
          <el-descriptions-item label="快递公司">{{ detailRow.expressCompany || '--' }}</el-descriptions-item>
          <el-descriptions-item label="快递单号" :span="2">{{ detailRow.expressNumber || '--' }}</el-descriptions-item>
        </el-descriptions>
        <div class="detail-images" v-if="detailImages.length">
          <div class="detail-images-title">发货图片</div>
          <el-image
            v-for="img in detailImages"
            :key="img.url"
            :src="img.url"
            :preview-src-list="detailImages.map(i => i.url)"
            fit="cover"
            class="detail-image"
          />
        </div>
        <div v-else class="detail-images-empty">暂无发货图片</div>
        <ImagePreview :file-list="detailRow.storageBlobVOs || []" />
      </div>
      <template #footer>
        <div class="dialog-footer">
@@ -216,15 +189,14 @@
import pagination from "@/components/PIMTable/Pagination.vue";
import { onMounted, ref, reactive, toRefs, getCurrentInstance } from "vue";
import { ElMessageBox } from "element-plus";
import { Plus } from "@element-plus/icons-vue";
import { getToken } from "@/utils/auth";
import { getCurrentDate } from "@/utils/index.js";
import {
    deliveryLedgerListPage,
    addOrUpdateDeliveryLedger,
    delDeliveryLedger, deductStock,
} from "@/api/salesManagement/deliveryLedger.js";
import { delLedgerFile } from "@/api/salesManagement/salesLedger.js";
import ImageUpload from "@/components/AttachmentUpload/image/index.vue";
import ImagePreview from "@/components/AttachmentPreview/image/index.vue";
 
const { proxy } = getCurrentInstance();
@@ -238,40 +210,9 @@
});
const total = ref(0);
const deliveryFileList = ref([]);
const javaApi = proxy.javaApi;
// 详情弹框
const detailDialogVisible = ref(false);
const detailRow = ref(null);
const detailImages = ref([]);
const normalizeFileUrl = (rawUrl = '') => {
  let fileUrl = rawUrl || '';
  // Windows 路径转 URL
  if (fileUrl && fileUrl.indexOf('\\') > -1) {
    const uploadsIndex = fileUrl.toLowerCase().indexOf('uploads');
    if (uploadsIndex > -1) {
      const relativePath = fileUrl.substring(uploadsIndex).replace(/\\/g, '/');
      fileUrl = '/' + relativePath;
    } else {
      const parts = fileUrl.split('\\');
      const fileName = parts[parts.length - 1];
      fileUrl = '/uploads/' + fileName;
    }
  }
  if (fileUrl && !fileUrl.startsWith('http')) {
    if (!fileUrl.startsWith('/')) fileUrl = '/' + fileUrl;
    fileUrl = javaApi + fileUrl;
  }
  return fileUrl;
};
// 上传配置
const upload = reactive({
  // 上传的地址
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
  // 设置上传的请求头部
  headers: { Authorization: "Bearer " + getToken() },
});
// 用户信息表单弹框数据
const operationType = ref("");
@@ -311,7 +252,6 @@
});
const { form, rules } = toRefs(data);
const { searchForm } = toRefs(data);
 
// 查询列表
@@ -361,7 +301,6 @@
  }
  
  operationType.value = type;
  const baseUrl = import.meta.env.VITE_APP_BASE_API;
  
  if (type === 'edit' && row) {
    form.value = {
@@ -374,41 +313,7 @@
      expressCompany: row.expressCompany ?? "",
      expressNumber: row.expressNumber ?? "",
    };
    // 如果有图片,将 commonFileList 转换为文件列表格式
    if (row.commonFileList && Array.isArray(row.commonFileList) && row.commonFileList.length > 0) {
      deliveryFileList.value = row.commonFileList.map((file, index) => {
        const fileUrl = normalizeFileUrl(file.url || '');
        return {
          uid: file.id || Date.now() + index,
          name: file.name || `image_${index + 1}.jpg`,
          url: fileUrl,
          status: 'success',
          response: {
            code: 200,
            data: {
              tempId: file.id,
              url: fileUrl
            }
          },
          tempId: file.id // 保存文件ID,用于提交时使用
        };
      });
    } else {
      deliveryFileList.value = [];
    }
  } else {
    form.value = {
      id: null,
      salesContractNo: "",
      customerName: "",
      type: "货车",
      shippingDate: getCurrentDate(),
      shippingCarNumber: "",
      expressCompany: "",
      expressNumber: "",
    };
    deliveryFileList.value = [];
    deliveryFileList.value = row.storageBlobVOS || [];
  }
  
  dialogFormVisible.value = true;
@@ -418,25 +323,17 @@
const openDetail = (row) => {
  detailRow.value = row || null;
  const list = Array.isArray(row?.commonFileList) ? row.commonFileList : [];
  detailImages.value = list
    .map((f) => ({ url: normalizeFileUrl(f?.url || '') }))
    .filter((i) => !!i.url);
  detailDialogVisible.value = true;
};
const closeDetail = () => {
  detailDialogVisible.value = false;
  detailRow.value = null;
  detailImages.value = [];
};
// 提交表单
const submitForm = () => {
  proxy.$refs["formRef"].validate((valid) => {
    if (valid) {
      let tempFileIds = [];
      if (deliveryFileList.value !== null && deliveryFileList.value.length > 0) {
        tempFileIds = deliveryFileList.value.map((item) => item.tempId);
      }
      const payload = {
        id: form.value.id,
        type: form.value.type,
@@ -444,7 +341,7 @@
        shippingCarNumber: form.value.type === "货车" ? form.value.shippingCarNumber : "",
        expressCompany: form.value.type === "快递" ? form.value.expressCompany : "",
        expressNumber: form.value.type === "快递" ? form.value.expressNumber : "",
        tempFileIds: tempFileIds,
        storageBlobDTOs: deliveryFileList.value || [],
      };
            deductStock(payload).then((res) => {
        proxy.$modal.msgSuccess("操作成功");
@@ -563,11 +460,13 @@
  proxy.$modal.loading("正在上传图片,请稍候...");
  return true;
}
// 发货图片上传失败
function handleDeliveryUploadError(err) {
  proxy.$modal.msgError("上传图片失败");
  proxy.$modal.closeLoading();
}
// 发货图片上传成功回调
function handleDeliveryUploadSuccess(res, file, uploadFiles) {
  proxy.$modal.closeLoading();
@@ -579,6 +478,7 @@
    proxy.$refs.deliveryFileUpload.handleRemove(file);
  }
}
// 移除发货图片
function handleDeliveryRemove(file) {
  console.log('file--', file)
@@ -727,17 +627,21 @@
    display: none;
  }
}
.detail-wrapper {
  padding: 8px 0;
}
.detail-images {
  margin-top: 16px;
}
.detail-images-title {
  font-weight: 600;
  margin-bottom: 10px;
  color: #303133;
}
.detail-image {
  width: 120px;
  height: 120px;
@@ -745,6 +649,7 @@
  margin-bottom: 10px;
  border-radius: 6px;
}
.detail-images-empty {
  margin-top: 16px;
  color: #909399;