gongchunyi
2 天以前 4c5d743a929942f784a8b1899b8a65f55722f8c1
feat: 设备台账上传图片
已修改5个文件
227 ■■■■■ 文件已修改
src/api/equipmentManagement/ledger.js 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/FileUpload/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/ImageUpload/index.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/ledger/Form.vue 190 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/equipmentManagement/ledger/Modal.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/equipmentManagement/ledger.js
@@ -42,3 +42,29 @@
    method: "get",
  });
};
export const getLedgerFileList = (deviceLedgerId) => {
  return request({
    url: `/device/ledger/file/${deviceLedgerId}`,
    method: "get",
  });
};
export const uploadLedgerFile = (formData) => {
  return request({
    url: "/device/ledger/uploadFile",
    method: "post",
    data: formData,
    headers: {
      "Content-Type": "multipart/form-data",
      repeatSubmit: false,
    },
  });
};
export const deleteLedgerFile = (fileId) => {
  return request({
    url: `/device/ledger/file/${fileId}`,
    method: "delete",
  });
};
src/components/FileUpload/index.vue
@@ -20,7 +20,7 @@
    <!-- 文件列表 -->
    <transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
      <li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
        <el-link :href="`${baseUrl}${file.url}`" :underline="false" target="_blank">
        <el-link :href="`${baseUrl}${baseUrl.endsWith('/') || file.url.startsWith('/') ? '' : '/'}${file.url}`" :underline="false" target="_blank">
          <span class="el-icon-document"> {{ getFileName(file.name) }} </span>
        </el-link>
        <div class="ele-upload-list__item-content-action">
src/components/ImageUpload/index.vue
@@ -113,7 +113,8 @@
      fileList.value = list.map((item) => {
        if (typeof item === "string") {
          if (item.indexOf(baseUrl) === -1 && !isExternal(item)) {
            item = { name: baseUrl + item, url: baseUrl + item };
            let separator = baseUrl.endsWith('/') || item.startsWith('/') ? '' : '/';
            item = { name: baseUrl + separator + item, url: baseUrl + separator + item };
          } else {
            item = { name: item, url: item };
          }
src/views/equipmentManagement/ledger/Form.vue
@@ -180,14 +180,41 @@
                    />
                </el-form-item>
            </el-col>
      <el-col :span="24">
        <el-form-item label="设备图片" prop="deviceImage">
          <div v-if="displayedExistingProblems.length" class="problem-existing-wrap">
            <div v-for="img in displayedExistingProblems" :key="img.id" class="problem-thumb">
              <el-image :src="img.url" fit="cover" class="problem-thumb-img" @click="previewProblem(img.url)" />
              <el-icon class="problem-thumb-remove" @click.stop="markProblemRemove(img.id)"><Close /></el-icon>
            </div>
          </div>
          <el-upload
            class="repair-attachment-upload"
            v-model:file-list="attachmentFileList"
            drag
            multiple
            :auto-upload="false"
            :limit="9"
            accept="image/png,image/jpeg,image/jpg"
            :before-upload="beforeUpload"
          >
            <el-icon class="repair-upload-icon"><UploadFilled /></el-icon>
            <div class="el-upload__text">将文件拖到此处,或 <em>点击选择文件</em></div>
            <template #tip>
              <div class="el-upload__tip">
                支持 png / jpg / jpeg,单张不超过 50MB,最多 9 张
              </div>
            </template>
          </el-upload>
        </el-form-item>
      </el-col>
    </el-row>
  </el-form>
</template>
<script setup>
import useFormData from "@/hooks/useFormData";
// import useUserStore from "@/store/modules/user";
import { getLedgerById } from "@/api/equipmentManagement/ledger";
import { getLedgerById, getLedgerFileList, uploadLedgerFile, deleteLedgerFile } from "@/api/equipmentManagement/ledger";
import { processList } from "@/api/productionManagement/productionProcess";
import dayjs from "dayjs";
import {
@@ -195,7 +222,9 @@
  calculateTaxExclusiveTotalPrice,
} from "@/utils/summarizeTable";
import { ElMessage } from "element-plus";
import { ref, onMounted } from "vue";
import { ref, onMounted, getCurrentInstance } from "vue";
import { UploadFilled, Close } from "@element-plus/icons-vue";
import { getToken } from "@/utils/auth";
defineOptions({
  name: "设备台账表单",
@@ -256,6 +285,20 @@
    planRuntimeTime: dayjs().format("YYYY-MM-DD"), // 录入日期
});
const attachmentFileList = ref([]);
const existingProblemImages = ref([]);
const pendingRemoveProblemIds = ref([]);
const displayedExistingProblems = computed(() =>
  existingProblemImages.value.filter((img) => !pendingRemoveProblemIds.value.includes(img.id))
);
const markProblemRemove = (fileId) => {
  if (!pendingRemoveProblemIds.value.includes(fileId)) {
    pendingRemoveProblemIds.value.push(fileId);
  }
};
const loadForm = async (id) => {
    if (id) {
        operationType.value = 'edit'
@@ -289,6 +332,23 @@
      form.planRuntimeTime = dayjs(data.planRuntimeTime).format('YYYY-MM-DD');
    } else {
      form.planRuntimeTime = undefined;
    }
  }
  existingProblemImages.value = [];
  pendingRemoveProblemIds.value = [];
  attachmentFileList.value = [];
  if (id) {
    try {
      const res = await getLedgerFileList(id);
      const list = Array.isArray(res?.data) ? res.data : [];
      existingProblemImages.value = list.map((item) => ({
        id: item.id,
        name: item.name,
        url: getFileAccessUrl(item),
      }));
    } catch {
      existingProblemImages.value = [];
    }
  }
};
@@ -359,6 +419,77 @@
  getProcessOptions();
});
const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload");
const headers = ref({ Authorization: "Bearer " + getToken() });
const ATTACH_MAX_MB = 50;
const beforeUpload = (rawFile) => {
  const okType = ["image/jpeg", "image/jpg", "image/png"].includes(rawFile.type);
  if (!okType) {
    ElMessage.error("只能上传 png / jpg / jpeg 图片");
    return false;
  }
  const maxBytes = ATTACH_MAX_MB * 1024 * 1024;
  if (rawFile.size > maxBytes) {
    ElMessage.error(`单个文件不能超过 ${ATTACH_MAX_MB}MB`);
    return false;
  }
  return true;
};
const submitFiles = async (ledgerId) => {
  for (const fileId of pendingRemoveProblemIds.value) {
    await deleteLedgerFile(fileId);
  }
  pendingRemoveProblemIds.value = [];
  const rows = attachmentFileList.value || [];
  const files = rows.map((f) => f.raw).filter(Boolean);
  if (!files.length || ledgerId == null) return;
  for (const file of files) {
    const fd = new FormData();
    fd.append("file", file);
    fd.append("deviceLedgerId", String(ledgerId));
    // 16 is the FileNameType for EQUIPMENT_LEDGER that we added
    fd.append("fileType", "16");
    const res = await uploadLedgerFile(fd);
    if (res.code !== 200) {
      throw new Error(res.msg || "附件上传失败");
    }
  }
  attachmentFileList.value = [];
};
const getFileAccessUrl = (file = {}) => {
  let raw = file?.link || file?.url || "";
  if (String(raw).startsWith("http")) return raw;
  const javaApi = getCurrentInstance()?.proxy?.javaApi || import.meta.env.VITE_APP_BASE_API || "/dev-api";
  let fileUrl = raw;
  if (fileUrl.indexOf("\\") > -1) {
    const lowerPath = fileUrl.toLowerCase();
    const uploadPathIndex = lowerPath.indexOf("uploadpath");
    if (uploadPathIndex > -1) {
      fileUrl = fileUrl.substring(uploadPathIndex).replace(/\\/g, "/");
    } else {
      fileUrl = fileUrl.replace(/\\/g, "/");
    }
  }
  fileUrl = fileUrl.replace(/^\/?uploadPath/, "/profile");
  if (fileUrl.startsWith("upload/")) {
    fileUrl = "/profile/" + fileUrl;
  }
  if (!fileUrl.startsWith("http")) {
    if (!fileUrl.startsWith("/")) fileUrl = "/" + fileUrl;
    fileUrl = javaApi + fileUrl;
  }
  return fileUrl;
};
const previewProblem = (url) => {
  window.open(url, "_blank");
};
defineExpose({
  form,
  loadForm,
@@ -366,5 +497,58 @@
  clearValidate,
  resetFormAndValidate,
    formRef,
  submitFiles,
});
</script>
<style lang="scss" scoped>
.repair-attachment-upload {
  width: 100%;
  :deep(.el-upload) {
    width: 100%;
  }
  :deep(.el-upload-dragger) {
    width: 100%;
    padding: 24px 16px;
  }
}
.repair-upload-icon {
  font-size: 48px;
  color: var(--el-color-primary);
  margin-bottom: 8px;
}
.problem-existing-wrap {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 12px;
}
.problem-thumb {
  position: relative;
  width: 88px;
  height: 88px;
  border-radius: 6px;
  overflow: hidden;
  border: 1px solid var(--el-border-color);
}
.problem-thumb-img {
  width: 100%;
  height: 100%;
  cursor: pointer;
}
.problem-thumb-remove {
  position: absolute;
  top: 2px;
  right: 2px;
  font-size: 16px;
  color: #fff;
  background: rgba(0, 0, 0, 0.45);
  border-radius: 50%;
  padding: 2px;
  cursor: pointer;
}
</style>
src/views/equipmentManagement/ledger/Modal.vue
@@ -37,10 +37,12 @@
const sendForm = () => {
    proxy.$refs.formRef.$refs.formRef.validate(async valid => {
        if (valid) {
            const {code} = id.value
            const res = id.value
                ? await editLedger({id: id.value, ...formRef.value.form})
                : await addLedger(formRef.value.form);
            if (code == 200) {
            if (res.code == 200) {
                const ledgerId = id.value || res.data;
                await proxy.$refs.formRef.submitFiles(ledgerId);
                emits("success");
                ElMessage({message: "操作成功", type: "success"});
                close();