From ae7277fa376166ef2bd66253dd16337a35229d10 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期四, 21 五月 2026 10:11:32 +0800
Subject: [PATCH] 阳光彩印 1.web端设备巡检要求也可以上传图片
---
multiple/config.json | 2
vite.config.js | 4
src/views/equipmentManagement/inspectionManagement/index.vue | 27 ++
src/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue | 580 ++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 606 insertions(+), 7 deletions(-)
diff --git a/multiple/config.json b/multiple/config.json
index 04c80e8..5044486 100644
--- a/multiple/config.json
+++ b/multiple/config.json
@@ -20,7 +20,7 @@
"YGYS": {
"env": {
"VITE_APP_TITLE": "闃冲厜鍗板埛淇℃伅绠$悊",
- "VITE_BASE_API": "http://1.15.17.182:9022",
+ "VITE_BASE_API": "http://1.15.17.182:9023",
"VITE_JAVA_API": "http://1.15.17.182:9022"
},
"screen": "screen/login-background.png",
diff --git a/src/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue b/src/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue
new file mode 100644
index 0000000..a907b61
--- /dev/null
+++ b/src/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue
@@ -0,0 +1,580 @@
+<template>
+ <div>
+ <el-dialog
+ v-model="showUploadDialog"
+ title="涓婁紶宸℃璁板綍"
+ width="560px"
+ :before-close="closeUploadDialog"
+ >
+ <el-tabs v-model="currentUploadType">
+ <el-tab-pane label="鐢熶骇鍓�" name="before" />
+ <el-tab-pane label="鐢熶骇涓�" name="after" />
+ <el-tab-pane label="鐢熶骇鍚�" name="issue" />
+ </el-tabs>
+
+ <div class="exception-section">
+ <div class="section-title">鏄惁瀛樺湪寮傚父锛�</div>
+ <el-radio-group v-model="hasException">
+ <el-radio :value="false">姝e父</el-radio>
+ <el-radio :value="true">瀛樺湪寮傚父</el-radio>
+ </el-radio-group>
+ </div>
+
+ <div class="upload-buttons">
+ <el-upload
+ ref="uploadRef"
+ v-model:file-list="uploadFileList"
+ :action="uploadUrl"
+ :headers="uploadHeaders"
+ :show-file-list="false"
+ :accept="uploadAccept"
+ :multiple="false"
+ :before-upload="handleBeforeUpload"
+ :on-success="handleUploadSuccess"
+ :on-error="handleUploadError"
+ :on-progress="handleUploadProgress"
+ :disabled="uploading || getCurrentFiles().length >= uploadConfig.limit"
+ >
+ <el-button
+ type="primary"
+ :loading="uploading"
+ :disabled="getCurrentFiles().length >= uploadConfig.limit"
+ >
+ 閫夋嫨鍥剧墖/瑙嗛
+ </el-button>
+ </el-upload>
+ </div>
+
+ <el-progress
+ v-if="uploading"
+ :percentage="uploadProgress"
+ style="margin: 12px 0"
+ />
+
+ <div v-if="getCurrentFiles().length" class="file-list">
+ <div
+ v-for="(file, index) in getCurrentFiles()"
+ :key="file.uid || file.id || index"
+ class="file-item"
+ >
+ <div class="file-preview-container">
+ <img
+ v-if="isImageFile(file)"
+ :src="file.url || file.downloadUrl"
+ class="file-preview"
+ @click="previewAttachment(file)"
+ />
+ <div
+ v-else
+ class="video-preview"
+ @click="previewAttachment(file)"
+ >
+ 瑙嗛
+ </div>
+
+ <button class="delete-btn" @click="removeFile(index)">x</button>
+ </div>
+
+ <div class="file-name">
+ {{ file.bucketFilename || file.name || "闄勪欢" }}
+ </div>
+ <div class="file-size">
+ {{ formatFileSize(file.size || file.byteSize) }}
+ </div>
+ </div>
+ </div>
+
+ <el-empty
+ v-else
+ :description="`璇烽�夋嫨瑕佷笂浼犵殑${getUploadTypeText()}鍥剧墖鎴栬棰慲"
+ />
+
+ <div class="upload-summary">
+ 鐢熶骇鍓嶏細{{ beforeModelValue.length }} 涓� |
+ 鐢熶骇涓細{{ afterModelValue.length }} 涓� |
+ 鐢熶骇鍚庯細{{ issueModelValue.length }} 涓�
+ </div>
+
+ <template #footer>
+ <el-button type="primary" @click="submitUpload">鎻愪氦</el-button>
+ <el-button @click="closeUploadDialog">鍙栨秷</el-button>
+ </template>
+ </el-dialog>
+
+ <el-dialog
+ v-model="showVideoDialog"
+ :title="currentVideoFile?.originalFilename || '瑙嗛棰勮'"
+ width="720px"
+ >
+ <video
+ v-if="currentVideoFile"
+ :src="currentVideoFile.url || currentVideoFile.downloadUrl"
+ class="video-player"
+ controls
+ autoplay
+ />
+ </el-dialog>
+ </div>
+</template>
+
+<script setup>
+import { computed, ref } from "vue";
+import { ElLoading, ElMessage, ElMessageBox } from "element-plus";
+import { getToken } from "@/utils/auth";
+import { uploadInspectionTask } from "@/api/inspectionManagement/index.js";
+
+const emit = defineEmits(["closeDia", "success"]);
+
+const showUploadDialog = ref(false);
+const uploading = ref(false);
+const uploadProgress = ref(0);
+const uploadRef = ref(null);
+const uploadFileList = ref([]);
+
+const beforeModelValue = ref([]);
+const afterModelValue = ref([]);
+const issueModelValue = ref([]);
+const currentUploadType = ref("before");
+const hasException = ref(null);
+const currentTask = ref(null);
+
+const showVideoDialog = ref(false);
+const currentVideoFile = ref(null);
+
+const uploadConfig = {
+ action: "/file/upload",
+ limit: 10,
+ fileSize: 50,
+ fileType: ["jpg", "jpeg", "png", "gif", "webp", "mp4", "mov", "avi", "wmv"],
+};
+
+const uploadUrl = `${import.meta.env.VITE_APP_BASE_API}${uploadConfig.action}`;
+const uploadHeaders = {
+ Authorization: `Bearer ${getToken()}`,
+};
+const uploadAccept = computed(() =>
+ uploadConfig.fileType.map(item => `.${item}`).join(",")
+);
+const filePreviewBase = __BASE_API__;
+
+const cloneData = value => JSON.parse(JSON.stringify(value || {}));
+
+const normalizeFileUrl = rawUrl => {
+ if (!rawUrl || typeof rawUrl !== "string") return "";
+
+ let fileUrl = rawUrl.trim();
+ if (!fileUrl) return "";
+ if (/^https?:\/\//i.test(fileUrl)) return fileUrl;
+
+ if (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.startsWith("http")) {
+ if (!fileUrl.startsWith("/")) {
+ fileUrl = `/${fileUrl}`;
+ }
+ fileUrl = `${filePreviewBase}${fileUrl}`;
+ }
+
+ return fileUrl;
+};
+
+const mapExistingFile = (file, type) => ({
+ ...file,
+ id: file?.id,
+ tempId: file?.tempId ?? file?.tempFileId ?? file?.id,
+ tempFileId: file?.tempFileId ?? file?.id,
+ url: normalizeFileUrl(file?.url || file?.downloadUrl || file?.fileUrl || ""),
+ downloadUrl: normalizeFileUrl(
+ file?.downloadUrl || file?.url || file?.fileUrl || ""
+ ),
+ bucketFilename:
+ file?.bucketFilename || file?.originalFilename || file?.fileName || file?.name,
+ originalFilename:
+ file?.originalFilename || file?.bucketFilename || file?.fileName || file?.name,
+ size: file?.size || file?.byteSize,
+ byteSize: file?.byteSize || file?.size,
+ contentType: file?.contentType || "",
+ type,
+ uid: file?.uid || `${type}-${file?.id || file?.url || Math.random()}`,
+});
+
+const resetDialogState = () => {
+ beforeModelValue.value = [];
+ afterModelValue.value = [];
+ issueModelValue.value = [];
+ currentUploadType.value = "before";
+ hasException.value = null;
+ currentTask.value = null;
+ uploadProgress.value = 0;
+ uploading.value = false;
+ uploadFileList.value = [];
+ uploadRef.value?.clearFiles?.();
+};
+
+const openDialog = task => {
+ const rawTask = cloneData(task?.__raw || task);
+ currentTask.value = {
+ ...rawTask,
+ taskId: rawTask.taskId || rawTask.id,
+ storageBlobDTO: [],
+ };
+
+ beforeModelValue.value = Array.isArray(rawTask.commonFileListBefore)
+ ? rawTask.commonFileListBefore.map(file => mapExistingFile(file, 10))
+ : [];
+ afterModelValue.value = Array.isArray(rawTask.commonFileListAfter)
+ ? rawTask.commonFileListAfter.map(file => mapExistingFile(file, 11))
+ : [];
+ issueModelValue.value = Array.isArray(rawTask.commonFileList)
+ ? rawTask.commonFileList.map(file => mapExistingFile(file, 12))
+ : [];
+
+ currentUploadType.value = "before";
+ hasException.value =
+ typeof rawTask.hasException === "boolean" ? rawTask.hasException : null;
+ uploadFileList.value = [];
+ showUploadDialog.value = true;
+};
+
+const closeUploadDialog = () => {
+ showUploadDialog.value = false;
+ resetDialogState();
+ emit("closeDia");
+};
+
+const getCurrentFiles = () => {
+ if (currentUploadType.value === "before") return beforeModelValue.value;
+ if (currentUploadType.value === "after") return afterModelValue.value;
+ return issueModelValue.value;
+};
+
+const getUploadTypeText = () => {
+ if (currentUploadType.value === "before") return "鐢熶骇鍓�";
+ if (currentUploadType.value === "after") return "鐢熶骇涓�";
+ return "鐢熶骇鍚�";
+};
+
+const getTabType = () => {
+ if (currentUploadType.value === "before") return 10;
+ if (currentUploadType.value === "after") return 11;
+ return 12;
+};
+
+const handleBeforeUpload = file => {
+ if (getCurrentFiles().length >= uploadConfig.limit) {
+ ElMessage.warning(`鏈�澶氬彧鑳介�夋嫨${uploadConfig.limit}涓枃浠禶);
+ return false;
+ }
+
+ const ext = file.name.split(".").pop()?.toLowerCase();
+ if (!uploadConfig.fileType.includes(ext)) {
+ ElMessage.warning(`鏂囦欢鏍煎紡涓嶆敮鎸侊紝璇蜂笂浼� ${uploadConfig.fileType.join("/")} 鏍煎紡`);
+ return false;
+ }
+
+ const maxSize = uploadConfig.fileSize * 1024 * 1024;
+ if (file.size > maxSize) {
+ ElMessage.warning(`鏂囦欢澶у皬涓嶈兘瓒呰繃 ${uploadConfig.fileSize}MB`);
+ return false;
+ }
+
+ uploading.value = true;
+ uploadProgress.value = 0;
+ return true;
+};
+
+const handleUploadProgress = event => {
+ if (event?.percent) {
+ uploadProgress.value = Math.round(event.percent);
+ }
+};
+
+const handleUploadSuccess = (response, file) => {
+ uploading.value = false;
+ uploadProgress.value = 0;
+ uploadFileList.value = [];
+ uploadRef.value?.clearFiles?.();
+
+ const uploadedFile = response?.data;
+ if (response?.code !== 200 || !uploadedFile) {
+ ElMessage.error(response?.msg || "涓婁紶鍝嶅簲鏁版嵁鏍煎紡閿欒");
+ return;
+ }
+
+ const type = getTabType();
+ const objectUrl = file?.raw ? URL.createObjectURL(file.raw) : "";
+ const fileData = {
+ id: uploadedFile.id,
+ tempId: uploadedFile.tempId ?? uploadedFile.tempFileId ?? uploadedFile.id,
+ tempFileId: uploadedFile.tempFileId ?? uploadedFile.id,
+ url: normalizeFileUrl(uploadedFile.url || uploadedFile.downloadUrl || objectUrl),
+ downloadUrl: normalizeFileUrl(
+ uploadedFile.downloadUrl || uploadedFile.url || objectUrl
+ ),
+ bucketFilename:
+ uploadedFile.bucketFilename || uploadedFile.originalFilename || file.name,
+ originalFilename:
+ uploadedFile.originalFilename || uploadedFile.bucketFilename || file.name,
+ size: uploadedFile.size || uploadedFile.byteSize || file.size,
+ byteSize: uploadedFile.byteSize || uploadedFile.size || file.size,
+ createTime: uploadedFile.createTime || Date.now(),
+ contentType: uploadedFile.contentType || file.raw?.type || "",
+ type,
+ uid: `${Date.now()}-${Math.random()}`,
+ };
+
+ if (currentUploadType.value === "before") {
+ beforeModelValue.value.push(fileData);
+ } else if (currentUploadType.value === "after") {
+ afterModelValue.value.push(fileData);
+ } else {
+ issueModelValue.value.push(fileData);
+ }
+
+ ElMessage.success("涓婁紶鎴愬姛");
+};
+
+const handleUploadError = error => {
+ uploading.value = false;
+ uploadProgress.value = 0;
+ uploadFileList.value = [];
+ uploadRef.value?.clearFiles?.();
+ ElMessage.error(error?.message || "涓婁紶澶辫触");
+};
+
+const removeFile = async index => {
+ try {
+ await ElMessageBox.confirm("纭畾瑕佸垹闄よ繖涓枃浠跺悧锛�", "纭鍒犻櫎", {
+ type: "warning",
+ });
+
+ if (currentUploadType.value === "before") {
+ beforeModelValue.value.splice(index, 1);
+ } else if (currentUploadType.value === "after") {
+ afterModelValue.value.splice(index, 1);
+ } else {
+ issueModelValue.value.splice(index, 1);
+ }
+
+ ElMessage.success("鍒犻櫎鎴愬姛");
+ } catch {}
+};
+
+const buildSubmitFiles = () => {
+ const list = [
+ ...beforeModelValue.value,
+ ...afterModelValue.value,
+ ...issueModelValue.value,
+ ];
+
+ return list.map(item => ({
+ ...item,
+ url: item?.downloadUrl || item?.url || "",
+ }));
+};
+
+const submitUpload = async () => {
+ if (hasException.value === null) {
+ ElMessage.warning("璇烽�夋嫨鏄惁瀛樺湪寮傚父");
+ return;
+ }
+
+ const files = buildSubmitFiles();
+ if (!files.length) {
+ ElMessage.warning("璇峰厛涓婁紶鏂囦欢");
+ return;
+ }
+
+ const loadingInstance = ElLoading.service({
+ text: "鎻愪氦涓�...",
+ background: "rgba(0, 0, 0, 0.3)",
+ });
+
+ try {
+ const tempFileIds = files
+ .map(item => item?.tempId ?? item?.tempFileId ?? item?.id)
+ .filter(Boolean);
+
+ const payload = {
+ ...currentTask.value,
+ hasException: hasException.value,
+ storageBlobDTO: files,
+ tempFileIds,
+ commonFileListBefore: beforeModelValue.value,
+ commonFileListAfter: afterModelValue.value,
+ commonFileList: issueModelValue.value,
+ };
+
+ const result = await uploadInspectionTask(payload);
+ if (result?.code === 200 || result?.success) {
+ ElMessage.success("鎻愪氦鎴愬姛");
+ showUploadDialog.value = false;
+ resetDialogState();
+ emit("success");
+ emit("closeDia");
+ } else {
+ ElMessage.error(result?.msg || result?.message || "鎻愪氦澶辫触");
+ }
+ } catch (error) {
+ ElMessage.error(error?.message || "鎻愪氦澶辫触");
+ } finally {
+ loadingInstance.close();
+ }
+};
+
+const goToRepair = () => {
+ const taskInfo = {
+ taskId: currentTask.value?.taskId || currentTask.value?.id,
+ taskName: currentTask.value?.taskName,
+ inspectionLocation: currentTask.value?.inspectionLocation,
+ inspector: currentTask.value?.inspector,
+ uploadedFiles: {
+ before: beforeModelValue.value,
+ after: afterModelValue.value,
+ issue: issueModelValue.value,
+ },
+ };
+
+ sessionStorage.setItem("repairTaskInfo", JSON.stringify(taskInfo));
+ window.location.href = "/equipmentManagement/repair";
+};
+
+const previewAttachment = file => {
+ if (isImageFile(file)) {
+ window.open(file.url || file.downloadUrl, "_blank");
+ } else {
+ currentVideoFile.value = file;
+ showVideoDialog.value = true;
+ }
+};
+
+const isImageFile = file => {
+ if (file?.contentType?.startsWith("image/")) return true;
+ const name =
+ file?.bucketFilename || file?.originalFilename || file?.name || "";
+ const ext = name.split(".").pop()?.toLowerCase();
+ return ["jpg", "jpeg", "png", "gif", "webp"].includes(ext);
+};
+
+const formatFileSize = size => {
+ if (!size) return "";
+ if (size < 1024) return `${size}B`;
+ if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)}KB`;
+ return `${(size / 1024 / 1024).toFixed(1)}MB`;
+};
+
+defineExpose({
+ openDialog,
+});
+</script>
+
+<style scoped>
+.exception-section {
+ margin-bottom: 16px;
+ padding: 12px;
+ background: #f8f9fa;
+ border-radius: 8px;
+}
+
+.section-title {
+ font-weight: 600;
+ margin-bottom: 8px;
+}
+
+.upload-buttons {
+ margin-bottom: 12px;
+}
+
+.file-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+}
+
+.file-item {
+ width: 120px;
+ padding: 8px;
+ border: 1px solid #e5e5e5;
+ border-radius: 10px;
+ background: #fff;
+ text-align: center;
+}
+
+.file-preview-container {
+ position: relative;
+}
+
+.file-preview {
+ width: 90px;
+ height: 90px;
+ object-fit: cover;
+ border-radius: 8px;
+ border: 1px solid #eee;
+ cursor: pointer;
+}
+
+.video-preview {
+ width: 90px;
+ height: 90px;
+ margin: 0 auto;
+ border-radius: 8px;
+ background: #eef3f8;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #409eff;
+ cursor: pointer;
+}
+
+.delete-btn {
+ position: absolute;
+ top: -6px;
+ right: 6px;
+ width: 22px;
+ height: 22px;
+ border: none;
+ border-radius: 50%;
+ color: #fff;
+ background: #f56c6c;
+ cursor: pointer;
+}
+
+.file-name {
+ margin-top: 6px;
+ font-size: 12px;
+ color: #333;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.file-size {
+ font-size: 11px;
+ color: #999;
+}
+
+.upload-summary {
+ margin-top: 16px;
+ padding: 10px;
+ background: #f8f9fa;
+ border-left: 3px solid #409eff;
+ font-size: 13px;
+ color: #666;
+}
+
+.video-player {
+ width: 100%;
+ max-height: 70vh;
+ background: #000;
+}
+</style>
diff --git a/src/views/equipmentManagement/inspectionManagement/index.vue b/src/views/equipmentManagement/inspectionManagement/index.vue
index 35f82d5..d80d29a 100644
--- a/src/views/equipmentManagement/inspectionManagement/index.vue
+++ b/src/views/equipmentManagement/inspectionManagement/index.vue
@@ -76,6 +76,8 @@
<form-dia ref="formDia"
@closeDia="handleQuery"></form-dia>
<view-files ref="viewFiles"></view-files>
+ <upload-files ref="uploadFiles"
+ @closeDia="handleQuery"></upload-files>
</div>
</template>
@@ -87,6 +89,7 @@
// 缁勪欢寮曞叆
import PIMTable from "@/components/PIMTable/PIMTable.vue";
import FormDia from "@/views/equipmentManagement/inspectionManagement/components/formDia.vue";
+ import UploadFiles from "@/views/equipmentManagement/inspectionManagement/components/uploadFiles.vue";
import ViewFiles from "@/views/equipmentManagement/inspectionManagement/components/viewFiles.vue";
// 鎺ュ彛寮曞叆
@@ -100,6 +103,7 @@
const { proxy } = getCurrentInstance();
const formDia = ref();
const viewFiles = ref();
+ const uploadFiles = ref();
// 鏌ヨ鍙傛暟
const queryParams = reactive({
@@ -184,8 +188,9 @@
const operationConfig = {
label: "鎿嶄綔",
- width: 130,
+ width: operations.length > 1 ? 180 : 130,
fixed: "right",
+ align: 'center',
dataType: "action",
operation: operations
.map(op => {
@@ -194,6 +199,12 @@
return {
name: "缂栬緫",
clickFun: handleAdd,
+ color: "#409EFF",
+ };
+ case "upload":
+ return {
+ name: "涓婁紶",
+ clickFun: openUploadFiles,
color: "#409EFF",
};
case "viewFile":
@@ -226,12 +237,12 @@
];
operationsArr.value = ["edit"];
} else if (value === "task") {
- const operationColumn = getOperationColumn(["viewFile"]);
+ const operationColumn = getOperationColumn(["upload", "viewFile"]);
tableColumns.value = [
...columns.value,
...(operationColumn ? [operationColumn] : []),
];
- operationsArr.value = ["viewFile"];
+ operationsArr.value = ["upload", "viewFile"];
}
pageNum.value = 1;
pageSize.value = 10;
@@ -273,6 +284,7 @@
// 澶勭悊 inspector 瀛楁锛屽皢瀛楃涓茶浆鎹负鏁扮粍锛堥�傜敤浜庢墍鏈夋儏鍐碉級
tableData.value = rawData.map(item => {
const processedItem = { ...item };
+ processedItem.__raw = { ...item };
// 澶勭悊 inspector 瀛楁
if (processedItem.inspector) {
@@ -322,6 +334,13 @@
const viewFile = row => {
nextTick(() => {
viewFiles.value?.openDialog(row);
+ });
+ };
+
+ // 涓婁紶闄勪欢
+ const openUploadFiles = row => {
+ nextTick(() => {
+ uploadFiles.value?.openDialog(row);
});
};
@@ -390,4 +409,4 @@
color: #909399;
font-size: 14px;
}
-</style>
\ No newline at end of file
+</style>
diff --git a/vite.config.js b/vite.config.js
index af1ed35..4e392ae 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -8,11 +8,11 @@
const { VITE_APP_ENV } = env;
const baseUrl =
env.VITE_APP_ENV === "development"
- ? "http://1.15.17.182:9023"
+ ? "http://1.15.17.182:9038"
: env.VITE_BASE_API;
const javaUrl =
env.VITE_APP_ENV === "development"
- ? "http://1.15.17.182:9023"
+ ? "http://1.15.17.182:9039"
: env.VITE_JAVA_API;
return {
define:{
--
Gitblit v1.9.3