From fe6509e66e6852f6b5d740385bdae4fd0a11da77 Mon Sep 17 00:00:00 2001
From: 董国庆 <364620639@qq.com>
Date: 星期二, 21 十月 2025 14:55:05 +0800
Subject: [PATCH] 修改问题

---
 laboratory/src/views/reportLibrary/feasibilityStudy/components/approval/index.vue          |   22 
 laboratory/src/utils/utils.js                                                              |   17 
 laboratory/src/views/deliveryAssessment/testingAndEvaluation/components/approval/index.vue |  913 ++++++++++++-----------
 laboratory/src/views/chemistQa/productApproval/components/approval/index.vue               |   32 
 laboratory/src/views/chemistQa/productApproval/add.vue                                     |   22 
 laboratory/src/views/chemistQa/rawMaterials/components/approval/index.vue                  |   22 
 laboratory/src/views/chemistQa/pilotAndProduction/components/approval/index.vue            |   22 
 laboratory/src/views/reportLibrary/verificationRelease/components/approval/index.vue       |   32 
 laboratory/src/components/AiEditor/index.vue                                               |   94 ++
 laboratory/src/views/chemistQa/projectTesting/components/approval/index.vue                |  930 +++++++++++++-----------
 laboratory/src/views/reportLibrary/projectProposalLibrary/components/approval/index.vue    |   32 
 laboratory/src/views/reportLibrary/processDevelopment/components/approval/index.vue        |   32 
 laboratory/src/views/reportLibrary/feasibilityReport/components/approval/index.vue         |   22 
 13 files changed, 1,234 insertions(+), 958 deletions(-)

diff --git a/laboratory/src/components/AiEditor/index.vue b/laboratory/src/components/AiEditor/index.vue
index 373fafc..ba6f6ea 100644
--- a/laboratory/src/components/AiEditor/index.vue
+++ b/laboratory/src/components/AiEditor/index.vue
@@ -57,7 +57,8 @@
     value: {
       handler(newVal) {
         if (this.editor && newVal !== this.editor.getHtml()) {
-          this.editor.setContent(newVal)
+          const contentWithIp = this.addIpToContent(newVal);
+          this.editor.setContent(contentWithIp)
         }
       },
       immediate: true
@@ -78,18 +79,91 @@
     this.destroyEditor()
   },
   methods: {
+    // 去除内容中的IP地址,只保留相对路径
+    removeIpFromContent(content) {
+      if (!content || typeof content !== 'string') return content;
+      
+      // 获取当前IP地址
+      const currentIp = getAllocateIp();
+      
+      // 使用正则表达式匹配并替换所有包含当前IP的URL
+      const ipRegex = new RegExp(currentIp.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
+      return content.replace(ipRegex, '');
+    },
+    
+    // 为内容中的相对路径添加IP地址,并处理已包含IP的绝对路径
+    addIpToContent(content) {
+      if (!content || typeof content !== 'string') return content;
+      
+      // 获取当前IP地址
+      const currentIp = getAllocateIp();
+      
+      // 匹配所有src、href、poster属性
+      const pathRegex = /(src|href|poster)="([^"]+)"/g;
+      
+      return content.replace(pathRegex, (match, attr, path) => {
+        // 如果路径是相对路径(不以http开头),则添加当前IP地址
+        if (!path.startsWith('http')) {
+          // 检查路径是否已经以/images/开头,避免重复添加
+          if (path.startsWith('/images/')) {
+            // 如果currentIp已经以/images/结尾,则去掉路径开头的/
+            if (currentIp.endsWith('/images/')) {
+              return `${attr}="${currentIp}${path.substring(1)}"`;
+            } else {
+              return `${attr}="${currentIp}${path}"`;
+            }
+          } else {
+            return `${attr}="${currentIp}${path}"`;
+          }
+        }
+        
+        // 如果路径已经包含当前IP地址,直接返回
+        if (path.startsWith(currentIp)) {
+          return match;
+        }
+        
+        // 如果路径是绝对路径,提取路径部分并替换为当前IP地址
+        // 匹配形如 http://192.168.1.100:8080/xxx 的路径
+        const absolutePathRegex = /^https?:\/\/[^\/]+(\/.*)$/;
+        const matchResult = path.match(absolutePathRegex);
+        
+        if (matchResult) {
+          // 提取路径部分,替换为当前IP地址
+          let pathPart = matchResult[1];
+          
+          // 如果currentIp已经以/images/结尾,且pathPart以/images/开头,则去掉pathPart开头的/images/
+          if (currentIp.endsWith('/images/') && pathPart.startsWith('/images/')) {
+            pathPart = pathPart.substring(7); // 去掉开头的 '/images/'
+            // 如果pathPart以/开头,则去掉开头的/
+            if (pathPart.startsWith('/')) {
+              pathPart = pathPart.substring(1);
+            }
+          }
+          
+          return `${attr}="${currentIp}${pathPart}"`;
+        }
+        
+        // 其他情况(如外部链接)保持不变
+
+        return match;
+      });
+    },
+    
     initEditor() {
       const options = {
         element: this.$refs.editorRef,
         placeholder: this.placeholder,
-        content: this.value,
+        content: this.addIpToContent(this.value),
         editable: !this.readOnly,
         toolbar: this.toolbar,
         toolbarExcludeKeys: ["ai"],
         draggable: false,
         onChange: (content) => {
-          this.$emit('input', content)
-          this.$emit('change', content)
+          // 确保content是字符串类型
+          const contentStr = typeof content === 'string' ? content : String(content || '');
+          const contentWithoutIp = this.removeIpFromContent(contentStr);
+          this.$emit('input', contentWithoutIp)
+          this.$emit('change', contentWithoutIp)
         },
         onFocus: () => {
           this.$emit('focus')
@@ -205,6 +279,7 @@
 
       }
 
+      console.log('this.addIpToContent(this.value)',this.addIpToContent(this.value))
       try {
         this.editor = new AiEditor(options)
       } catch (error) {
@@ -217,14 +292,17 @@
         this.editor = null
       }
     },
-    // 获取编辑器内容
+    // 获取编辑器内容(去除IP地址,只保留相对路径)
     getContent() {
-      return this.editor ? this.editor.getHtml() : ''
+      if (!this.editor) return '';
+      const content = this.editor.getHtml();
+      return this.removeIpFromContent(content);
     },
-    // 设置编辑器内容
+    // 设置编辑器内容(为相对路径添加IP地址)
     setContent(content) {
       if (this.editor) {
-        this.editor.setContent(content)
+        const contentWithIp = this.addIpToContent(content);
+        this.editor.setContent(contentWithIp);
       }
     },
     // 清空编辑器内容
diff --git a/laboratory/src/utils/utils.js b/laboratory/src/utils/utils.js
index 5b9aacb..859f8db 100644
--- a/laboratory/src/utils/utils.js
+++ b/laboratory/src/utils/utils.js
@@ -51,11 +51,10 @@
  * @param {string} name 下载保存的文件名(可选)
  */
 export function downloadFileByUrl(url, name) {
-  console.log("22222222222222222222", url);
   if (!url) return;
   // 处理相对路径
   const fullUrl = getFullUrl(url);
-  console.log("fullUrl fullUrl", fullUrl);
+  console.log("下载文件地址", fullUrl);
   const a = document.createElement("a");
   a.href = fullUrl;
   if (name) {
@@ -79,7 +78,19 @@
   window.open(fullUrl, "_blank");
 }
 
+/**
+ * 判断文件是否为图片
+ * @param {string} url 文件URL
+ * @returns {boolean} 是否为图片文件
+ */
+export function isImageFile(url) {
+  if (!url) return false;
+  const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg'];
+  const lowerUrl = url.toLowerCase();
+  return imageExtensions.some(ext => lowerUrl.includes(ext));
+}
+
 export function getAllocateIp() {
   const userInfo = JSON.parse(sessionStorage.getItem("userInfo") || "{}");
-  return userInfo.allocateIp ? userInfo.allocateIp +'/images/' : apiConfig.showImgUrl;
+  return userInfo.allocateIp ? 'http://'+userInfo.allocateIp +':11222/images/' : apiConfig.showImgUrl;
 }
diff --git a/laboratory/src/views/chemistQa/pilotAndProduction/components/approval/index.vue b/laboratory/src/views/chemistQa/pilotAndProduction/components/approval/index.vue
index fdccba5..01997a6 100644
--- a/laboratory/src/views/chemistQa/pilotAndProduction/components/approval/index.vue
+++ b/laboratory/src/views/chemistQa/pilotAndProduction/components/approval/index.vue
@@ -108,7 +108,7 @@
 import AiEditor from "@/components/AiEditor";
 import { getDetail, audit } from "../../service";
 import apiConfig from "@/utils/baseurl";
-import { getAllocateIp } from "@/utils/utils";
+import { getAllocateIp, downloadFileByUrl, isImageFile } from "@/utils/utils";
 
 export default {
   name: "ApprovalDialog",
@@ -172,13 +172,23 @@
   },
   methods: {
     handlePreview(file) {
-      console.log("url", file);
       if (file && file.url) {
-        if (file.url && file.url.startsWith(getAllocateIp())) {
-          window.open(file.url, "_blank");
+        // 判断是否为图片文件
+        const isImage = isImageFile(file.url);
+        
+        if (isImage) {
+          // 图片文件:使用 window.open 预览
+          if (file.url && file.url.startsWith(getAllocateIp())) {
+            console.log('图片链接', file.url)
+            window.open(file.url, '_blank');
+          } else {
+            let newUrl = getAllocateIp() + file.url;
+            console.log('图片链接', newUrl)
+            window.open(newUrl, '_blank');
+          }
         } else {
-          let newUrl = getAllocateIp() + file.url;
-          window.open(newUrl, "_blank");
+          // 非图片文件:使用下载方式
+          downloadFileByUrl(file.url, file.name);
         }
       }
     },
diff --git a/laboratory/src/views/chemistQa/productApproval/add.vue b/laboratory/src/views/chemistQa/productApproval/add.vue
index c97068e..4d0a4ba 100644
--- a/laboratory/src/views/chemistQa/productApproval/add.vue
+++ b/laboratory/src/views/chemistQa/productApproval/add.vue
@@ -77,7 +77,7 @@
 import AiEditor from "@/components/AiEditor";
 import chooseProject from "@/components/chooseProject";
 import { addData, getDetail, editData } from "./service";
-import { customUploadRequest, getFullUrl, getAllocateIp } from "@/utils/utils";
+import { customUploadRequest, getFullUrl, getAllocateIp , downloadFileByUrl, isImageFile } from "@/utils/utils";
 import { mapState } from "vuex";
 import apiConfig from '@/utils/baseurl';
 
@@ -136,13 +136,23 @@
 
   methods: {
     handlePreview(file) {
-      console.log('url', file)
       if (file && file.url) {
-        if (file.url && file.url.startsWith(getAllocateIp())) {
-          window.open(file.url, '_blank');
+        // 判断是否为图片文件
+        const isImage = isImageFile(file.url);
+        
+        if (isImage) {
+          // 图片文件:使用 window.open 预览
+          if (file.url && file.url.startsWith(getAllocateIp())) {
+            console.log('图片链接', file.url)
+            window.open(file.url, '_blank');
+          } else {
+            let newUrl = getAllocateIp() + file.url;
+            console.log('图片链接', newUrl)
+            window.open(newUrl, '_blank');
+          }
         } else {
-          let newUrl = getAllocateIp() + file.url;
-          window.open(newUrl, '_blank');
+          // 非图片文件:使用下载方式
+          downloadFileByUrl(file.url, file.name);
         }
       }
     },
diff --git a/laboratory/src/views/chemistQa/productApproval/components/approval/index.vue b/laboratory/src/views/chemistQa/productApproval/components/approval/index.vue
index 88dc505..dbe3b34 100644
--- a/laboratory/src/views/chemistQa/productApproval/components/approval/index.vue
+++ b/laboratory/src/views/chemistQa/productApproval/components/approval/index.vue
@@ -112,7 +112,7 @@
 import AiEditor from '@/components/AiEditor'
 import { getDetail, audit } from '../../service'
 import apiConfig from '@/utils/baseurl';
-import { getAllocateIp } from '@/utils/utils'
+import { getAllocateIp, downloadFileByUrl, isImageFile  } from '@/utils/utils'
 
 export default {
     name: "ApprovalDialog",
@@ -176,16 +176,26 @@
     },
     methods: {
         handlePreview(file) {
-            console.log('url', file)
-            if (file && file.url) {
-                if (file.url && file.url.startsWith(getAllocateIp())) {
-                    window.open(file.url, '_blank');
-                } else {
-                    let newUrl = getAllocateIp() + file.url;
-                    window.open(newUrl, '_blank');
-                }
-            }
-        },
+      if (file && file.url) {
+        // 判断是否为图片文件
+        const isImage = isImageFile(file.url);
+        
+        if (isImage) {
+          // 图片文件:使用 window.open 预览
+          if (file.url && file.url.startsWith(getAllocateIp())) {
+            console.log('图片链接', file.url)
+            window.open(file.url, '_blank');
+          } else {
+            let newUrl = getAllocateIp() + file.url;
+            console.log('图片链接', newUrl)
+            window.open(newUrl, '_blank');
+          }
+        } else {
+          // 非图片文件:使用下载方式
+          downloadFileByUrl(file.url, file.name);
+        }
+      }
+    },
         getDetail() {
             getDetail(this.data.id).then(res => {
                 if (res) {
diff --git a/laboratory/src/views/chemistQa/projectTesting/components/approval/index.vue b/laboratory/src/views/chemistQa/projectTesting/components/approval/index.vue
index ef49b5a..b38f3d5 100644
--- a/laboratory/src/views/chemistQa/projectTesting/components/approval/index.vue
+++ b/laboratory/src/views/chemistQa/projectTesting/components/approval/index.vue
@@ -1,507 +1,557 @@
 <template>
-    <el-dialog :title="dialogTitle" :visible.sync="visible" width="90%" top="5vh" @open="open"
-        :close-on-click-modal="false" @close="handleClose">
-        <div class="approval-dialog">
-            <!-- 左侧审批内容 -->
-            <div class="approval-content">
-                <Card class="approval-content-card">
-                    <template style="position: relative">
-                        <div class="header-title" style="width: 100%;">
-                            <div class="header-title-left">
-                                <img src="@/assets/public/headercard.png" />
-                                <div>所属检测项</div>
-                            </div>
-                        </div>
-                        <Table :height="null" :total="0" :data="tableData">
-                            <template>
-                                <el-table-column prop="teamName" label="所属项目组" />
-                                <el-table-column prop="itemName" label="检测项名称" />
-                                <el-table-column prop="itemCode" label="检测项编号" />
-                                <el-table-column prop="remark" label="备注" />
-                                <el-table-column prop="createBy" label="创建人" />
-                                <el-table-column prop="createTime" label="创建时间" />
-                            </template>
-                        </Table>
-
-
-                        <el-form ref="form" :model="form" :rules="rules" inline label-position="top"
-                            style="margin-top: 18px">
-                            <el-form-item prop="name" label="报告内容">
-                                <el-select v-model="form.reportContent" style="width: 100%;" disabled
-                                    placeholder="请选择报告内容">
-                                    <el-option label="国家标准" :value="1" />
-                                    <el-option label="分析方法开发" :value="2" />
-                                    <el-option label="方法验证报告" :value="3" />
-                                    <el-option label="方法确认" :value="4" />
-                                    <el-option label="操作规程" :value="5" />
-                                    <el-option label="方法转移记录清单" :value="6" />
-                                </el-select>
-                            </el-form-item>
-
-                            <div class="header-title" style="width: 100%;">
-                                <div class="header-title-left">
-                                    <img src="@/assets/public/headercard.png" />
-                                    <div>报告正文</div>
-                                </div>
-                            </div>
-                            <el-form-item prop="name" style="margin-top: 18px">
-                                <ai-editor :value="form.reportText" :readOnly="true" style="width: 100%;"
-                                    placeholder="请输入报告编号" />
-                            </el-form-item>
-                            <div class="header-title" style="width: 100%;">
-                                <div class="header-title-left">
-                                    <img src="@/assets/public/headercard.png" />
-                                    <span>附件</span>
-                                </div>
-                            </div>
-                            <el-form-item prop="name" style="margin-top: 18px">
-                                <el-upload action="https://jsonplaceholder.typicode.com/posts/" :file-list="fileList"
-                                    :on-preview="handlePreview">
-                                    <!-- <el-button size="small" type="primary">点击上传</el-button> -->
-                                </el-upload>
-                            </el-form-item>
-
-                        </el-form>
-                    </template>
-                    <!-- <SelectMember ref="selectMember" /> -->
-                </Card>
+  <el-dialog
+    :title="dialogTitle"
+    :visible.sync="visible"
+    width="90%"
+    top="5vh"
+    @open="open"
+    :close-on-click-modal="false"
+    @close="handleClose"
+  >
+    <div class="approval-dialog">
+      <!-- 左侧审批内容 -->
+      <div class="approval-content">
+        <Card class="approval-content-card">
+          <template style="position: relative">
+            <div class="header-title" style="width: 100%">
+              <div class="header-title-left">
+                <img src="@/assets/public/headercard.png" />
+                <div>所属检测项</div>
+              </div>
             </div>
-            <!-- 右侧审批流程 -->
-            <div class="approval-flow">
-                <div class="flow-content">
-                    <approval-process :processData="form.processData" />
+            <Table :height="null" :total="0" :data="tableData">
+              <template>
+                <el-table-column prop="teamName" label="所属项目组" />
+                <el-table-column prop="itemName" label="检测项名称" />
+                <el-table-column prop="itemCode" label="检测项编号" />
+                <el-table-column prop="remark" label="备注" />
+                <el-table-column prop="createBy" label="创建人" />
+                <el-table-column prop="createTime" label="创建时间" />
+              </template>
+            </Table>
+
+            <el-form
+              ref="form"
+              :model="form"
+              :rules="rules"
+              inline
+              label-position="top"
+              style="margin-top: 18px"
+            >
+              <el-form-item prop="name" label="报告内容">
+                <el-select
+                  v-model="form.reportContent"
+                  style="width: 100%"
+                  disabled
+                  placeholder="请选择报告内容"
+                >
+                  <el-option label="国家标准" :value="1" />
+                  <el-option label="分析方法开发" :value="2" />
+                  <el-option label="方法验证报告" :value="3" />
+                  <el-option label="方法确认" :value="4" />
+                  <el-option label="操作规程" :value="5" />
+                  <el-option label="方法转移记录清单" :value="6" />
+                </el-select>
+              </el-form-item>
+
+              <div class="header-title" style="width: 100%">
+                <div class="header-title-left">
+                  <img src="@/assets/public/headercard.png" />
+                  <div>报告正文</div>
                 </div>
+              </div>
+              <el-form-item prop="name" style="margin-top: 18px">
+                <ai-editor
+                  :value="form.reportText"
+                  :readOnly="true"
+                  style="width: 100%"
+                  placeholder="请输入报告编号"
+                />
+              </el-form-item>
+              <div class="header-title" style="width: 100%">
+                <div class="header-title-left">
+                  <img src="@/assets/public/headercard.png" />
+                  <span>附件</span>
+                </div>
+              </div>
+              <el-form-item prop="name" style="margin-top: 18px">
+                <el-upload
+                  action="https://jsonplaceholder.typicode.com/posts/"
+                  :file-list="fileList"
+                  :on-preview="handlePreview"
+                >
+                  <!-- <el-button size="small" type="primary">点击上传</el-button> -->
+                </el-upload>
+              </el-form-item>
+            </el-form>
+          </template>
+          <!-- <SelectMember ref="selectMember" /> -->
+        </Card>
+      </div>
+      <!-- 右侧审批流程 -->
+      <div class="approval-flow">
+        <div class="flow-content">
+          <approval-process :processData="form.processData" />
+        </div>
+      </div>
+    </div>
+    <div class="approval-dialog-approve" v-if="type == 'approve'">
+      <el-row :span="24">
+        <el-col :span="12">
+          <div class="status">
+            <div class="status-title">审批结果</div>
+            <div class="status-content">
+              <div
+                class="resolve"
+                :class="status == '1' && 'activeStatus'"
+                @click.stop="status = 1"
+              >
+                通过
+              </div>
+              <div
+                class="reject"
+                :class="status == '2' && 'activeStatus'"
+                @click.stop="status = 2"
+              >
+                驳回
+              </div>
             </div>
-        </div>
-        <div class="approval-dialog-approve" v-if="type == 'approve'">
-            <el-row :span="24">
-                <el-col :span="12">
-                    <div class="status">
-                        <div class="status-title">审批结果</div>
-                        <div class="status-content">
-                            <div class="resolve" :class="status == '1' && 'activeStatus'" @click.stop="status = 1">
-                                通过
-                            </div>
-                            <div class="reject" :class="status == '2' && 'activeStatus'" @click.stop="status = 2">
-                                驳回
-                            </div>
-                        </div>
-                    </div>
-                </el-col>
-                <el-col :span="12">
-                    <div class="remark">
-                        <div class="remark-title">审批意见</div>
-                        <el-input type="textarea" v-model="remark" placeholder="请输入审批意见" />
-                    </div>
-                </el-col>
-            </el-row>
-
-
-        </div>
-        <div slot="footer" class="dialog-footer">
-            <el-button @click="handleClose">取 消</el-button>
-            <el-button type="primary" @click="handleApprove" v-if="type == 'approve'">确认</el-button>
-        </div>
-    </el-dialog>
+          </div>
+        </el-col>
+        <el-col :span="12">
+          <div class="remark">
+            <div class="remark-title">审批意见</div>
+            <el-input
+              type="textarea"
+              v-model="remark"
+              placeholder="请输入审批意见"
+            />
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+    <div slot="footer" class="dialog-footer">
+      <el-button @click="handleClose">取 消</el-button>
+      <el-button type="primary" @click="handleApprove" v-if="type == 'approve'"
+        >确认</el-button
+      >
+    </div>
+  </el-dialog>
 </template>
 
 <script>
-import ApprovalProcess from '@/components/approvalProcess'
-import AiEditor from '@/components/AiEditor'
-import { getDetailInfo, getDetail, detailAuditReport } from '../../service'
-import { getFullUrl, getAllocateIp } from '@/utils/utils'
-import apiConfig from '@/utils/baseurl';
+import ApprovalProcess from "@/components/approvalProcess";
+import AiEditor from "@/components/AiEditor";
+import { getDetailInfo, getDetail, detailAuditReport } from "../../service";
+import { getFullUrl, getAllocateIp, downloadFileByUrl, isImageFile } from "@/utils/utils";
+import apiConfig from "@/utils/baseurl";
 
 export default {
-    name: "ApprovalDialog",
-    components: {
-        ApprovalProcess,
-        AiEditor
+  name: "ApprovalDialog",
+  components: {
+    ApprovalProcess,
+    AiEditor,
+  },
+  props: {
+    visible: {
+      type: Boolean,
+      default: false,
     },
-    props: {
-        visible: {
-            type: Boolean,
-            default: false,
-        },
-        type: {
-            type: String,
-            default: "approve", // approve-审批,view-查看
-        },
-        data: {
-            type: Object,
-            default: () => ({}),
-        },
-        id: {
-            type: String,
-            default: "",
-        },
+    type: {
+      type: String,
+      default: "approve", // approve-审批,view-查看
     },
-    data() {
-        return {
-            form: {
-                reportText: "",
-                reportContent: "",
-                qaReportFiles: [],
-                processData: [],
-            },
-            tableData: [],
-            fileList: [],
-            radio1: 1,
-            rules: {},
-            status: "1",
-            remark: "",
-        };
+    data: {
+      type: Object,
+      default: () => ({}),
     },
-    computed: {
-        dialogTitle() {
-            return this.type == "approve" ? "审核项目检测项、检验包" : "项目检测项、检验包详情";
-        },
+    id: {
+      type: String,
+      default: "",
     },
-    watch: {
-        visible: {
-            handler(val) {
-                if (val && this.data.id) {
-                    this.getDetailInfo()
-                }
-                if (val && this.data.itemId) {
-                    this.getDetail()
-                }
-            },
-            immediate: true,
-        },
+  },
+  data() {
+    return {
+      form: {
+        reportText: "",
+        reportContent: "",
+        qaReportFiles: [],
+        processData: [],
+      },
+      tableData: [],
+      fileList: [],
+      radio1: 1,
+      rules: {},
+      status: "1",
+      remark: "",
+    };
+  },
+  computed: {
+    dialogTitle() {
+      return this.type == "approve"
+        ? "审核项目检测项、检验包"
+        : "项目检测项、检验包详情";
     },
-    methods: {
-        getFullUrl,
-        handlePreview(file) {
-            console.log('url', file)
-            if (file && file.url) {
-                if (file.url && file.url.startsWith(getAllocateIp())) {
-                    window.open(file.url, '_blank');
-                } else {
-                    let newUrl = getAllocateIp() + file.url;
-                    window.open(newUrl, '_blank');
-                }
-            }
-        },
-        open() {
-            this.status = 1;
-            this.remark = ''
-        },
-        getDetailInfo() {
-            getDetailInfo({ id: this.data.id }).then(res => {
-                if (res) {
-                    this.form = { ...res, reportContent: Number(res.reportContent), processData: [] }
-                    if (res.qaReportFileList && res.qaReportFileList.length > 0) {
-                        this.fileList = res.qaReportFileList.map(file => ({
-                            name: file.fileName,
-                            url: getFullUrl(file.fileUrl),
-                            uid: file.id
-                        }))
-                        this.form.qaReportFileList = res.fileList
-                    } else {
-                        this.fileList = []
-                        this.form.qaReportFileList = []
-                    }
-                    // 组装流程数据
-                    let processData = [];
-                    // 提交节点
-                    processData.push({
-                        type: "primary",
-                        mode: "list",
-                        fields: [
-                            { label: "提交人:", value: res.updateBy || "" },
-                            { label: "提交时间:", value: res.createTime || "" },
-                        ],
-                    });
-                    if (res.status == 2 || res.status == 3) {
-                        processData.push({
-                            type:
-                                res.status == 2
-                                    ? "primary"
-                                    : res.status == 3
-                                        ? "danger"
-                                        : "warning",
-                            mode: "list",
-                            fields: [
-                                {
-                                    label: "审核结果:",
-                                    value:
-                                        res.status == 2
-                                            ? "通过"
-                                            : res.status == 3
-                                                ? "驳回"
-                                                : "待审批",
-                                },
-                                { label: "审批意见:", value: res.auditRemark || "" },
-                                { label: "审核人:", value: res.auditPersonName || "" },
-                                { label: "审核时间:", value: res.auditTime || "" },
-                            ],
-                        });
-                    } else {
-                        processData.push({
-                            type: "warning",
-                            mode: "list",
-                            fields: [
-                                { label: "等待审核" },
-                            ],
-                        });
-                    }
-                    // 如有卡片模式,按前述结构 push
+  },
+  watch: {
+    visible: {
+      handler(val) {
+        if (val && this.data.id) {
+          this.getDetailInfo();
+        }
+        if (val && this.data.itemId) {
+          this.getDetail();
+        }
+      },
+      immediate: true,
+    },
+  },
+  methods: {
+    getFullUrl,
+    handlePreview(file) {
+      if (file && file.url) {
+        // 判断是否为图片文件
+        const isImage = isImageFile(file.url);
 
-                    this.$nextTick(() => {
-                        this.form.processData = processData;
-                    })
-                }
-            })
-        },
-        getDetail() {
-            getDetail(this.data.itemId).then(res => {
-                if (res) {
-                    this.tableData = [{ ...res, teamName: res.projectTeamVO.teamName }]
-                }
-            })
-        },
-        handleClose() {
+        if (isImage) {
+          // 图片文件:使用 window.open 预览
+          if (file.url && file.url.startsWith(getAllocateIp())) {
+            console.log("图片链接", file.url);
+            window.open(file.url, "_blank");
+          } else {
+            let newUrl = getAllocateIp() + file.url;
+            console.log("图片链接", newUrl);
+            window.open(newUrl, "_blank");
+          }
+        } else {
+          // 非图片文件:使用下载方式
+          downloadFileByUrl(file.url, file.name);
+        }
+      }
+    },
+    open() {
+      this.status = 1;
+      this.remark = "";
+    },
+    getDetailInfo() {
+      getDetailInfo({ id: this.data.id }).then((res) => {
+        if (res) {
+          this.form = {
+            ...res,
+            reportContent: Number(res.reportContent),
+            processData: [],
+          };
+          if (res.qaReportFileList && res.qaReportFileList.length > 0) {
+            this.fileList = res.qaReportFileList.map((file) => ({
+              name: file.fileName,
+              url: getFullUrl(file.fileUrl),
+              uid: file.id,
+            }));
+            this.form.qaReportFileList = res.fileList;
+          } else {
+            this.fileList = [];
+            this.form.qaReportFileList = [];
+          }
+          // 组装流程数据
+          let processData = [];
+          // 提交节点
+          processData.push({
+            type: "primary",
+            mode: "list",
+            fields: [
+              { label: "提交人:", value: res.updateBy || "" },
+              { label: "提交时间:", value: res.createTime || "" },
+            ],
+          });
+          if (res.status == 2 || res.status == 3) {
+            processData.push({
+              type:
+                res.status == 2
+                  ? "primary"
+                  : res.status == 3
+                  ? "danger"
+                  : "warning",
+              mode: "list",
+              fields: [
+                {
+                  label: "审核结果:",
+                  value:
+                    res.status == 2
+                      ? "通过"
+                      : res.status == 3
+                      ? "驳回"
+                      : "待审批",
+                },
+                { label: "审批意见:", value: res.auditRemark || "" },
+                { label: "审核人:", value: res.auditPersonName || "" },
+                { label: "审核时间:", value: res.auditTime || "" },
+              ],
+            });
+          } else {
+            processData.push({
+              type: "warning",
+              mode: "list",
+              fields: [{ label: "等待审核" }],
+            });
+          }
+          // 如有卡片模式,按前述结构 push
+
+          this.$nextTick(() => {
+            this.form.processData = processData;
+          });
+        }
+      });
+    },
+    getDetail() {
+      getDetail(this.data.itemId).then((res) => {
+        if (res) {
+          this.tableData = [{ ...res, teamName: res.projectTeamVO.teamName }];
+        }
+      });
+    },
+    handleClose() {
+      this.$emit("close");
+
+      this.form.approvalComment = "";
+    },
+    handleApprove() {
+      if (!this.remark && this.status == "2") {
+        this.$message.warning("请输入审批意见");
+        return;
+      }
+      const params = {
+        id: this.data.id,
+        auditRemark: this.remark,
+        auditStatus: this.status == "1" ? 2 : 3,
+      };
+      detailAuditReport(params)
+        .then((res) => {
+          if (res) {
+            this.$message.success("审核成功");
             this.$emit("close");
-
-            this.form.approvalComment = "";
-        },
-        handleApprove() {
-            if (!this.remark && this.status == '2') {
-                this.$message.warning("请输入审批意见");
-                return;
-            }
-            const params = {
-                id: this.data.id,
-                auditRemark: this.remark,
-                auditStatus: this.status == '1' ? 2 : 3
-            };
-            detailAuditReport(params).then(res => {
-                if (res) {
-                    this.$message.success('审核成功');
-                    this.$emit("close");
-                }
-            }).catch(err => {
-                // this.$message.error(err.message || '审核失败');
-            });
-        },
-        handleReject() {
-            if (!this.form.approvalComment) {
-                this.$message.warning("请输入审批意见");
-                return;
-            }
-            this.$emit("reject", {
-                ...this.form,
-                status: "rejected",
-            });
-        },
+          }
+        })
+        .catch((err) => {
+          // this.$message.error(err.message || '审核失败');
+        });
     },
+    handleReject() {
+      if (!this.form.approvalComment) {
+        this.$message.warning("请输入审批意见");
+        return;
+      }
+      this.$emit("reject", {
+        ...this.form,
+        status: "rejected",
+      });
+    },
+  },
 };
 </script>
 
 <style scoped lang="less">
 ::v-deep .el-dialog__header {
-    border-bottom: 1px solid #e4e7ed;
+  border-bottom: 1px solid #e4e7ed;
 }
 
 .approval-dialog {
-    display: flex;
-    // height: 40vh;
-    min-height: 60vh;
-    max-height: 80vh;
+  display: flex;
+  // height: 40vh;
+  min-height: 60vh;
+  max-height: 80vh;
 
-    .approval-content {
-        flex: 1;
-        margin-right: 20px;
-        background: #ffffff;
-        box-shadow: 0px 4px 12px 4px rgba(0, 0, 0, 0.08);
-        border-radius: 10px;
+  .approval-content {
+    flex: 1;
+    margin-right: 20px;
+    background: #ffffff;
+    box-shadow: 0px 4px 12px 4px rgba(0, 0, 0, 0.08);
+    border-radius: 10px;
+  }
+
+  .approval-flow {
+    padding: 20px 10px;
+    width: 305px;
+    // flex: 2;
+    background: #ffffff;
+    box-shadow: 0px 4px 12px 4px rgba(0, 0, 0, 0.08);
+    border-radius: 10px;
+
+    .flow-title {
+      font-size: 16px;
+      font-weight: bold;
+      margin-bottom: 20px;
+      color: #303133;
     }
 
-    .approval-flow {
-        padding: 20px 10px;
-        width: 305px;
-        // flex: 2;
-        background: #ffffff;
-        box-shadow: 0px 4px 12px 4px rgba(0, 0, 0, 0.08);
-        border-radius: 10px;
+    .flow-content {
+      height: calc(100% - 40px);
+      overflow-y: auto;
 
-        .flow-title {
-            font-size: 16px;
-            font-weight: bold;
-            margin-bottom: 20px;
-            color: #303133;
-        }
-
-        .flow-content {
-            height: calc(100% - 40px);
-            overflow-y: auto;
-
-            .el-form--inline .el-form-item {
-                margin-right: 83px;
-            }
-        }
+      .el-form--inline .el-form-item {
+        margin-right: 83px;
+      }
     }
+  }
 }
 
 .approval-content-card {
-    height: calc(100% - 10px) !important;
-    box-shadow: none !important;
+  height: calc(100% - 10px) !important;
+  box-shadow: none !important;
 }
 
 .header-title {
-    // display: flex;
+  // display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+  margin-bottom: 20px;
+  gap: 13px;
+
+  .header-title-left {
+    display: flex;
     align-items: center;
-    flex-wrap: wrap;
-    margin-bottom: 20px;
     gap: 13px;
+    margin-top: 18px;
 
-    .header-title-left {
-        display: flex;
-        align-items: center;
-        gap: 13px;
-        margin-top: 18px;
-
-        img {
-            width: 12px;
-            height: 19px;
-        }
-
-        div {
-            flex-shrink: 0;
-            font-weight: bold;
-            font-size: 18px;
-            color: #222222;
-            line-height: 27px;
-            font-family: "Source Han Sans CN Bold Bold";
-
-            &:before {
-                content: "*";
-                color: #f56c6c;
-                margin-right: 4px;
-            }
-        }
-
-        span {
-            flex-shrink: 0;
-            font-weight: bold;
-            font-size: 18px;
-            color: #222222;
-            line-height: 27px;
-            font-family: "Source Han Sans CN Bold Bold";
-        }
+    img {
+      width: 12px;
+      height: 19px;
     }
 
-    .header-title-left :first-child {
-        margin-top: 0;
+    div {
+      flex-shrink: 0;
+      font-weight: bold;
+      font-size: 18px;
+      color: #222222;
+      line-height: 27px;
+      font-family: "Source Han Sans CN Bold Bold";
+
+      &:before {
+        content: "*";
+        color: #f56c6c;
+        margin-right: 4px;
+      }
     }
+
+    span {
+      flex-shrink: 0;
+      font-weight: bold;
+      font-size: 18px;
+      color: #222222;
+      line-height: 27px;
+      font-family: "Source Han Sans CN Bold Bold";
+    }
+  }
+
+  .header-title-left :first-child {
+    margin-top: 0;
+  }
 }
 
 .header-title:first-child {
-    .header-title-left {
-        margin-top: 0;
-    }
+  .header-title-left {
+    margin-top: 0;
+  }
 }
 
 .item-title {
-    padding-left: 25px;
+  padding-left: 25px;
 
-    span {
-        flex-shrink: 0;
-        font-weight: bold;
-        font-size: 14px;
-        color: #222222;
-        line-height: 27px;
-        font-family: "Source Han Sans CN Bold Bold";
-        margin: 18px 0;
+  span {
+    flex-shrink: 0;
+    font-weight: bold;
+    font-size: 14px;
+    color: #222222;
+    line-height: 27px;
+    font-family: "Source Han Sans CN Bold Bold";
+    margin: 18px 0;
 
-        &:before {
-            content: "*";
-            color: #f56c6c;
-            margin-right: 4px;
-        }
+    &:before {
+      content: "*";
+      color: #f56c6c;
+      margin-right: 4px;
     }
+  }
 }
 
 .approval-dialog-approve {
-    padding: 18px 20px;
-    // display: flex;
-    align-content: center;
+  padding: 18px 20px;
+  // display: flex;
+  align-content: center;
 
-    .status {
-        margin-right: 40px;
-        max-width: 60%;
+  .status {
+    margin-right: 40px;
+    max-width: 60%;
+  }
+
+  //   align-items: center;
+  .status-title {
+    color: #222222;
+    font-family: "SourceHanSansCN-Medium";
+    line-height: 14px;
+    margin-bottom: 16px;
+  }
+
+  .status-content {
+    display: flex;
+    align-items: center;
+    width: 100%;
+    gap: 16px;
+    background: #ffffff;
+    border-radius: 10px;
+    border: 1px solid rgba(4, 156, 154, 0.5);
+
+    .resolve {
+      border-radius: 10px;
+      flex: 1;
+      font-size: 16px;
+      // padding: 5px 55px;
+      font-weight: 400;
+      color: #333333;
+      cursor: pointer;
+      line-height: 32px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
     }
 
-    //   align-items: center;
-    .status-title {
-        color: #222222;
-        font-family: "SourceHanSansCN-Medium";
-        line-height: 14px;
-        margin-bottom: 16px;
+    .reject {
+      flex: 1;
+      border-radius: 10px;
+      font-size: 16px;
+      line-height: 32px;
+      // padding: 5px 55px;
+      font-weight: 400;
+      color: #333333;
+      cursor: pointer;
+      display: flex;
+      align-items: center;
+      justify-content: center;
     }
 
-    .status-content {
-        display: flex;
-        align-items: center;
-        width: 100%;
-        gap: 16px;
-        background: #ffffff;
-        border-radius: 10px;
-        border: 1px solid rgba(4, 156, 154, 0.5);
-
-        .resolve {
-            border-radius: 10px;
-            flex: 1;
-            font-size: 16px;
-            // padding: 5px 55px;
-            font-weight: 400;
-            color: #333333;
-            cursor: pointer;
-            line-height: 32px;
-            display: flex;
-            align-items: center;
-            justify-content: center
-        }
-
-        .reject {
-            flex: 1;
-            border-radius: 10px;
-            font-size: 16px;
-            line-height: 32px;
-            // padding: 5px 55px;
-            font-weight: 400;
-            color: #333333;
-            cursor: pointer;
-            display: flex;
-            align-items: center;
-            justify-content: center
-        }
-
-        .activeStatus {
-            background: #ebfefd;
-            color: #049c9a;
-            box-shadow: 0px 0px 6px 0px rgba(10, 109, 108, 0.25);
-            border-radius: 10px;
-        }
+    .activeStatus {
+      background: #ebfefd;
+      color: #049c9a;
+      box-shadow: 0px 0px 6px 0px rgba(10, 109, 108, 0.25);
+      border-radius: 10px;
     }
+  }
 
-    .remark-title {
-        color: #222222;
-        font-family: "SourceHanSansCN-Medium";
-        line-height: 14px;
-        margin-bottom: 16px;
-    }
+  .remark-title {
+    color: #222222;
+    font-family: "SourceHanSansCN-Medium";
+    line-height: 14px;
+    margin-bottom: 16px;
+  }
 }
 
 .dialog-footer {
-    align-items: center;
-    display: flex;
-    justify-content: center;
-    gap: 20px;
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  gap: 20px;
 
-    button {
-        width: 150px;
-    }
+  button {
+    width: 150px;
+  }
 }
 </style>
\ No newline at end of file
diff --git a/laboratory/src/views/chemistQa/rawMaterials/components/approval/index.vue b/laboratory/src/views/chemistQa/rawMaterials/components/approval/index.vue
index ca5748f..27bad6b 100644
--- a/laboratory/src/views/chemistQa/rawMaterials/components/approval/index.vue
+++ b/laboratory/src/views/chemistQa/rawMaterials/components/approval/index.vue
@@ -109,7 +109,7 @@
 import AiEditor from "@/components/AiEditor";
 import { getDetail, audit } from "../../service";
 import apiConfig from '@/utils/baseurl';
-import { getAllocateIp } from '@/utils/utils'
+import { getAllocateIp, downloadFileByUrl, isImageFile  } from '@/utils/utils'
 
 export default {
   name: "ApprovalDialog",
@@ -173,13 +173,23 @@
   },
   methods: {
     handlePreview(file) {
-      console.log('url', file)
       if (file && file.url) {
-        if (file.url && file.url.startsWith(getAllocateIp())) {
-          window.open(file.url, '_blank');
+        // 判断是否为图片文件
+        const isImage = isImageFile(file.url);
+        
+        if (isImage) {
+          // 图片文件:使用 window.open 预览
+          if (file.url && file.url.startsWith(getAllocateIp())) {
+            console.log('图片链接', file.url)
+            window.open(file.url, '_blank');
+          } else {
+            let newUrl = getAllocateIp() + file.url;
+            console.log('图片链接', newUrl)
+            window.open(newUrl, '_blank');
+          }
         } else {
-          let newUrl = getAllocateIp() + file.url;
-          window.open(newUrl, '_blank');
+          // 非图片文件:使用下载方式
+          downloadFileByUrl(file.url, file.name);
         }
       }
     },
diff --git a/laboratory/src/views/deliveryAssessment/testingAndEvaluation/components/approval/index.vue b/laboratory/src/views/deliveryAssessment/testingAndEvaluation/components/approval/index.vue
index 2d7b534..ba8c88c 100644
--- a/laboratory/src/views/deliveryAssessment/testingAndEvaluation/components/approval/index.vue
+++ b/laboratory/src/views/deliveryAssessment/testingAndEvaluation/components/approval/index.vue
@@ -1,499 +1,546 @@
 <template>
-    <el-dialog :title="dialogTitle" :visible.sync="visible" width="80%" po :close-on-click-modal="false"
-        @close="handleClose">
-        <div class="approval-dialog">
-            <!-- 左侧审批内容 -->
-            <div class="approval-content">
-                <Card class="approval-content-card">
-                    <template style="position: relative">
-                        <div class="header-title" style="width: 100%;">
-                            <div class="header-title-left">
-                                <img src="@/assets/public/headercard.png" />
-                                <div>所属检测项</div>
-                            </div>
-                        </div>
-                        <Table :height="null" :total="0" :data="tableData">
-                            <template>
-                                <el-table-column prop="teamName" label="所属项目组" />
-                                <el-table-column prop="itemName" label="检测项名称" />
-                                <el-table-column prop="itemCode" label="检测项编号" />
-                                <el-table-column prop="remark" label="备注" />
-                                <el-table-column prop="createBy" label="创建人" />
-                                <el-table-column prop="createTime" label="创建时间" />
-                            </template>
-                        </Table>
-
-
-                        <el-form ref="form" :model="form" :rules="rules" inline label-position="top"
-                            style="margin-top: 18px">
-                            <el-form-item prop="name" label="报告内容">
-                                <el-select v-model="form.reportContent" style="width: 100%;" disabled
-                                    placeholder="请选择报告内容">
-                                    <el-option label="国家标准" :value="1" />
-                                    <el-option label="分析方法开发" :value="2" />
-                                    <el-option label="方法验证报告" :value="3" />
-                                    <el-option label="方法确认" :value="4" />
-                                    <el-option label="操作规程" :value="5" />
-                                    <el-option label="方法转移记录清单" :value="6" />
-                                </el-select>
-                            </el-form-item>
-
-                            <div class="header-title" style="width: 100%;">
-                                <div class="header-title-left">
-                                    <img src="@/assets/public/headercard.png" />
-                                    <div>报告正文</div>
-                                </div>
-                            </div>
-                            <el-form-item prop="name" style="margin-top: 18px">
-                                <ai-editor :value="form.reportText" :readOnly="true" style="width: 100%;"
-                                    placeholder="请输入报告编号" />
-                            </el-form-item>
-                            <div class="header-title" style="width: 100%;">
-                                <div class="header-title-left">
-                                    <img src="@/assets/public/headercard.png" />
-                                    <span>附件</span>
-                                </div>
-                            </div>
-                            <el-form-item prop="name" style="margin-top: 18px">
-                                <el-upload :on-preview="handlePreview"
-                                    action="https://jsonplaceholder.typicode.com/posts/" :file-list="fileList">
-                                    <!-- <el-button size="small" type="primary">点击上传</el-button> -->
-                                </el-upload>
-                            </el-form-item>
-
-                        </el-form>
-                    </template>
-                    <!-- <SelectMember ref="selectMember" /> -->
-                </Card>
+  <el-dialog
+    :title="dialogTitle"
+    :visible.sync="visible"
+    width="80%"
+    po
+    :close-on-click-modal="false"
+    @close="handleClose"
+  >
+    <div class="approval-dialog">
+      <!-- 左侧审批内容 -->
+      <div class="approval-content">
+        <Card class="approval-content-card">
+          <template style="position: relative">
+            <div class="header-title" style="width: 100%">
+              <div class="header-title-left">
+                <img src="@/assets/public/headercard.png" />
+                <div>所属检测项</div>
+              </div>
             </div>
-            <!-- 右侧审批流程 -->
-            <div class="approval-flow">
-                <div class="flow-content">
-                    <approval-process :processData="form.processData" />
+            <Table :height="null" :total="0" :data="tableData">
+              <template>
+                <el-table-column prop="teamName" label="所属项目组" />
+                <el-table-column prop="itemName" label="检测项名称" />
+                <el-table-column prop="itemCode" label="检测项编号" />
+                <el-table-column prop="remark" label="备注" />
+                <el-table-column prop="createBy" label="创建人" />
+                <el-table-column prop="createTime" label="创建时间" />
+              </template>
+            </Table>
+
+            <el-form
+              ref="form"
+              :model="form"
+              :rules="rules"
+              inline
+              label-position="top"
+              style="margin-top: 18px"
+            >
+              <el-form-item prop="name" label="报告内容">
+                <el-select
+                  v-model="form.reportContent"
+                  style="width: 100%"
+                  disabled
+                  placeholder="请选择报告内容"
+                >
+                  <el-option label="国家标准" :value="1" />
+                  <el-option label="分析方法开发" :value="2" />
+                  <el-option label="方法验证报告" :value="3" />
+                  <el-option label="方法确认" :value="4" />
+                  <el-option label="操作规程" :value="5" />
+                  <el-option label="方法转移记录清单" :value="6" />
+                </el-select>
+              </el-form-item>
+
+              <div class="header-title" style="width: 100%">
+                <div class="header-title-left">
+                  <img src="@/assets/public/headercard.png" />
+                  <div>报告正文</div>
                 </div>
+              </div>
+              <el-form-item prop="name" style="margin-top: 18px">
+                <ai-editor
+                  :value="form.reportText"
+                  :readOnly="true"
+                  style="width: 100%"
+                  placeholder="请输入报告编号"
+                />
+              </el-form-item>
+              <div class="header-title" style="width: 100%">
+                <div class="header-title-left">
+                  <img src="@/assets/public/headercard.png" />
+                  <span>附件</span>
+                </div>
+              </div>
+              <el-form-item prop="name" style="margin-top: 18px">
+                <el-upload
+                  :on-preview="handlePreview"
+                  action="https://jsonplaceholder.typicode.com/posts/"
+                  :file-list="fileList"
+                >
+                  <!-- <el-button size="small" type="primary">点击上传</el-button> -->
+                </el-upload>
+              </el-form-item>
+            </el-form>
+          </template>
+          <!-- <SelectMember ref="selectMember" /> -->
+        </Card>
+      </div>
+      <!-- 右侧审批流程 -->
+      <div class="approval-flow">
+        <div class="flow-content">
+          <approval-process :processData="form.processData" />
+        </div>
+      </div>
+    </div>
+    <div class="approval-dialog-approve" v-if="type === 'approve'">
+      <el-row :span="24">
+        <el-col :span="12">
+          <div class="status">
+            <div class="status-title">审批结果</div>
+            <div class="status-content">
+              <div
+                class="resolve"
+                :class="status == '1' && 'activeStatus'"
+                @click.stop="status = 1"
+              >
+                通过
+              </div>
+              <div
+                class="reject"
+                :class="status == '2' && 'activeStatus'"
+                @click.stop="status = 2"
+              >
+                驳回
+              </div>
             </div>
-        </div>
-        <div class="approval-dialog-approve" v-if="type === 'approve'">
-            <el-row :span="24">
-                <el-col :span="12">
-                    <div class="status">
-                        <div class="status-title">审批结果</div>
-                        <div class="status-content">
-                            <div class="resolve" :class="status == '1' && 'activeStatus'" @click.stop="status = 1">
-                                通过
-                            </div>
-                            <div class="reject" :class="status == '2' && 'activeStatus'" @click.stop="status = 2">
-                                驳回
-                            </div>
-                        </div>
-                    </div>
-                </el-col>
-                <el-col :span="12">
-                    <div class="remark">
-                        <div class="remark-title">审批意见</div>
-                        <el-input type="textarea" v-model="remark" placeholder="请输入审批意见" />
-                    </div>
-                </el-col>
-            </el-row>
-
-
-        </div>
-        <div slot="footer" class="dialog-footer">
-            <el-button @click="handleClose">取 消</el-button>
-            <el-button type="primary" @click="handleApprove" v-if="type === 'approve'">确认</el-button>
-        </div>
-    </el-dialog>
+          </div>
+        </el-col>
+        <el-col :span="12">
+          <div class="remark">
+            <div class="remark-title">审批意见</div>
+            <el-input
+              type="textarea"
+              v-model="remark"
+              placeholder="请输入审批意见"
+            />
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+    <div slot="footer" class="dialog-footer">
+      <el-button @click="handleClose">取 消</el-button>
+      <el-button type="primary" @click="handleApprove" v-if="type === 'approve'"
+        >确认</el-button
+      >
+    </div>
+  </el-dialog>
 </template>
 
 <script>
-import ApprovalProcess from '@/components/approvalProcess'
-import AiEditor from '@/components/AiEditor'
-import { getDetailInfo, getDetail, detailAuditReport } from '../../service'
-import apiConfig from '@/utils/baseurl';
-import { getAllocateIp } from '@/utils/utils'
+import ApprovalProcess from "@/components/approvalProcess";
+import AiEditor from "@/components/AiEditor";
+import { getDetailInfo, getDetail, detailAuditReport } from "../../service";
+import apiConfig from "@/utils/baseurl";
+import { getAllocateIp, downloadFileByUrl, isImageFile } from "@/utils/utils";
 export default {
-    name: "ApprovalDialog",
-    components: {
-        ApprovalProcess,
-        AiEditor
+  name: "ApprovalDialog",
+  components: {
+    ApprovalProcess,
+    AiEditor,
+  },
+  props: {
+    visible: {
+      type: Boolean,
+      default: false,
     },
-    props: {
-        visible: {
-            type: Boolean,
-            default: false,
-        },
-        type: {
-            type: String,
-            default: "approve", // approve-审批,view-查看
-        },
-        data: {
-            type: Object,
-            default: () => ({}),
-        },
-        id: {
-            type: String,
-            default: "",
-        },
+    type: {
+      type: String,
+      default: "approve", // approve-审批,view-查看
     },
-    data() {
-        return {
-            form: {
-                reportText: "",
-                reportContent: "",
-                qaReportFiles: [],
-                processData: [],
-            },
-            tableData: [],
-            fileList: [],
-            radio1: 1,
-            rules: {},
-            status: "1",
-            remark: "",
-        };
+    data: {
+      type: Object,
+      default: () => ({}),
     },
-    computed: {
-        dialogTitle() {
-            return this.type === "approve" ? "方法验证报告审批" : "方法验证报告详情";
-        },
+    id: {
+      type: String,
+      default: "",
     },
-    watch: {
-        visible: {
-            handler(val) {
-                if (val && this.data.id) {
-                    this.getDetailInfo()
-                }
-                if (val && this.data.itemId) {
-                    this.getDetail()
-                }
-            },
-            immediate: true,
-        },
+  },
+  data() {
+    return {
+      form: {
+        reportText: "",
+        reportContent: "",
+        qaReportFiles: [],
+        processData: [],
+      },
+      tableData: [],
+      fileList: [],
+      radio1: 1,
+      rules: {},
+      status: "1",
+      remark: "",
+    };
+  },
+  computed: {
+    dialogTitle() {
+      return this.type === "approve" ? "方法验证报告审批" : "方法验证报告详情";
     },
-    methods: {
-        handlePreview(file) {
-            console.log('url', file)
-            if (file && file.url) {
-                if (file.url && file.url.startsWith(getAllocateIp())) {
-                    window.open(file.url, '_blank');
-                } else {
-                    let newUrl = getAllocateIp() + file.url;
-                    window.open(newUrl, '_blank');
-                }
-            }
-        },
-        getDetailInfo() {
-            getDetailInfo({ id: this.data.id }).then(res => {
-                if (res) {
-                    this.form = { ...res, reportContent: Number(res.reportContent), processData: [] }
-                    if (res.qaReportFileList && res.qaReportFileList.length > 0) {
-                        this.fileList = res.qaReportFileList.map(file => ({
-                            name: file.fileName,
-                            url: getFullUrl(file.fileUrl),
-                            uid: file.id
-                        }))
-                        this.form.qaReportFileList = res.fileList
-                    } else {
-                        this.fileList = []
-                        this.form.qaReportFileList = []
-                    }
-                    // 组装流程数据
-                    let processData = [];
-                    // 提交节点
-                    processData.push({
-                        type: "primary",
-                        mode: "list",
-                        fields: [
-                            { label: "提交人:", value: res.updateBy || "" },
-                            { label: "提交时间:", value: res.createTime || "" },
-                        ],
-                    });
-                    if (res.status == 2 || res.status == 3) {
-                        processData.push({
-                            type:
-                                res.status === 2
-                                    ? "primary"
-                                    : res.status === 3
-                                        ? "danger"
-                                        : "warning",
-                            mode: "list",
-                            fields: [
-                                {
-                                    label: "审核结果:",
-                                    value:
-                                        res.status === 2
-                                            ? "通过"
-                                            : res.status === 3
-                                                ? "驳回"
-                                                : "待审批",
-                                },
-                                { label: "审批意见:", value: res.auditRemark || "" },
-                                { label: "审核人:", value: res.auditPersonName || "" },
-                                { label: "审核时间:", value: res.auditTime || "" },
-                            ],
-                        });
-                    } else {
-                        processData.push({
-                            type: "warning",
-                            mode: "list",
-                            fields: [
-                                { label: "等待审核" },
-                            ],
-                        });
-                    }
-                    // 如有卡片模式,按前述结构 push
+  },
+  watch: {
+    visible: {
+      handler(val) {
+        if (val && this.data.id) {
+          this.getDetailInfo();
+        }
+        if (val && this.data.itemId) {
+          this.getDetail();
+        }
+      },
+      immediate: true,
+    },
+  },
+  methods: {
+    handlePreview(file) {
+      if (file && file.url) {
+        // 判断是否为图片文件
+        const isImage = isImageFile(file.url);
 
-                    this.$nextTick(() => {
-                        this.form.processData = processData;
-                    })
-                }
-            })
-        },
-        getDetail() {
-            getDetail(this.data.itemId).then(res => {
-                if (res) {
-                    this.tableData = [{ ...res, teamName: res.projectTeamVO.teamName }]
-                }
-            })
-        },
-        handleClose() {
+        if (isImage) {
+          // 图片文件:使用 window.open 预览
+          if (file.url && file.url.startsWith(getAllocateIp())) {
+            console.log("图片链接", file.url);
+            window.open(file.url, "_blank");
+          } else {
+            let newUrl = getAllocateIp() + file.url;
+            console.log("图片链接", newUrl);
+            window.open(newUrl, "_blank");
+          }
+        } else {
+          // 非图片文件:使用下载方式
+          downloadFileByUrl(file.url, file.name);
+        }
+      }
+    },
+    getDetailInfo() {
+      getDetailInfo({ id: this.data.id }).then((res) => {
+        if (res) {
+          this.form = {
+            ...res,
+            reportContent: Number(res.reportContent),
+            processData: [],
+          };
+          if (res.qaReportFileList && res.qaReportFileList.length > 0) {
+            this.fileList = res.qaReportFileList.map((file) => ({
+              name: file.fileName,
+              url: getFullUrl(file.fileUrl),
+              uid: file.id,
+            }));
+            this.form.qaReportFileList = res.fileList;
+          } else {
+            this.fileList = [];
+            this.form.qaReportFileList = [];
+          }
+          // 组装流程数据
+          let processData = [];
+          // 提交节点
+          processData.push({
+            type: "primary",
+            mode: "list",
+            fields: [
+              { label: "提交人:", value: res.updateBy || "" },
+              { label: "提交时间:", value: res.createTime || "" },
+            ],
+          });
+          if (res.status == 2 || res.status == 3) {
+            processData.push({
+              type:
+                res.status === 2
+                  ? "primary"
+                  : res.status === 3
+                  ? "danger"
+                  : "warning",
+              mode: "list",
+              fields: [
+                {
+                  label: "审核结果:",
+                  value:
+                    res.status === 2
+                      ? "通过"
+                      : res.status === 3
+                      ? "驳回"
+                      : "待审批",
+                },
+                { label: "审批意见:", value: res.auditRemark || "" },
+                { label: "审核人:", value: res.auditPersonName || "" },
+                { label: "审核时间:", value: res.auditTime || "" },
+              ],
+            });
+          } else {
+            processData.push({
+              type: "warning",
+              mode: "list",
+              fields: [{ label: "等待审核" }],
+            });
+          }
+          // 如有卡片模式,按前述结构 push
+
+          this.$nextTick(() => {
+            this.form.processData = processData;
+          });
+        }
+      });
+    },
+    getDetail() {
+      getDetail(this.data.itemId).then((res) => {
+        if (res) {
+          this.tableData = [{ ...res, teamName: res.projectTeamVO.teamName }];
+        }
+      });
+    },
+    handleClose() {
+      this.$emit("close");
+
+      this.form.approvalComment = "";
+    },
+    handleApprove() {
+      if (!this.remark) {
+        this.$message.warning("请输入审批意见");
+        return;
+      }
+      const params = {
+        id: this.data.id,
+        auditRemark: this.remark,
+        auditStatus: this.status === "1" ? 2 : 3,
+      };
+      detailAuditReport(params)
+        .then((res) => {
+          if (res) {
+            this.$message.success("审核成功");
             this.$emit("close");
-
-            this.form.approvalComment = "";
-        },
-        handleApprove() {
-            if (!this.remark) {
-                this.$message.warning("请输入审批意见");
-                return;
-            }
-            const params = {
-                id: this.data.id,
-                auditRemark: this.remark,
-                auditStatus: this.status === '1' ? 2 : 3
-            };
-            detailAuditReport(params).then(res => {
-                if (res) {
-                    this.$message.success('审核成功');
-                    this.$emit("close");
-                }
-            }).catch(err => {
-                this.$message.error(err.message || '审核失败');
-            });
-        },
-        handleReject() {
-            if (!this.form.approvalComment) {
-                this.$message.warning("请输入审批意见");
-                return;
-            }
-            this.$emit("reject", {
-                ...this.form,
-                status: "rejected",
-            });
-        },
+          }
+        })
+        .catch((err) => {
+          this.$message.error(err.message || "审核失败");
+        });
     },
+    handleReject() {
+      if (!this.form.approvalComment) {
+        this.$message.warning("请输入审批意见");
+        return;
+      }
+      this.$emit("reject", {
+        ...this.form,
+        status: "rejected",
+      });
+    },
+  },
 };
 </script>
 
 <style scoped lang="less">
 ::v-deep .el-dialog__header {
-    border-bottom: 1px solid #e4e7ed;
+  border-bottom: 1px solid #e4e7ed;
 }
 
 .approval-dialog {
-    display: flex;
-    height: 40vh;
+  display: flex;
+  height: 40vh;
 
-    .approval-content {
-        flex: 3;
-        margin-right: 20px;
-        background: #ffffff;
-        box-shadow: 0px 4px 12px 4px rgba(0, 0, 0, 0.08);
-        border-radius: 10px;
+  .approval-content {
+    flex: 3;
+    margin-right: 20px;
+    background: #ffffff;
+    box-shadow: 0px 4px 12px 4px rgba(0, 0, 0, 0.08);
+    border-radius: 10px;
+  }
+
+  .approval-flow {
+    padding: 40px 20px;
+    // width: 405px;
+    flex: 2;
+    background: #ffffff;
+    box-shadow: 0px 4px 12px 4px rgba(0, 0, 0, 0.08);
+    border-radius: 10px;
+
+    .flow-title {
+      font-size: 16px;
+      font-weight: bold;
+      margin-bottom: 20px;
+      color: #303133;
     }
 
-    .approval-flow {
-        padding: 40px 20px;
-        // width: 405px;
-        flex: 2;
-        background: #ffffff;
-        box-shadow: 0px 4px 12px 4px rgba(0, 0, 0, 0.08);
-        border-radius: 10px;
+    .flow-content {
+      height: calc(100% - 40px);
+      overflow-y: auto;
 
-        .flow-title {
-            font-size: 16px;
-            font-weight: bold;
-            margin-bottom: 20px;
-            color: #303133;
-        }
-
-        .flow-content {
-            height: calc(100% - 40px);
-            overflow-y: auto;
-
-            .el-form--inline .el-form-item {
-                margin-right: 83px;
-            }
-        }
+      .el-form--inline .el-form-item {
+        margin-right: 83px;
+      }
     }
+  }
 }
 
 .approval-content-card {
-    height: calc(100% - 100px) !important;
-    box-shadow: none !important;
+  height: calc(100% - 100px) !important;
+  box-shadow: none !important;
 }
 
 .header-title {
-    // display: flex;
+  // display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+  margin-bottom: 20px;
+  gap: 13px;
+
+  .header-title-left {
+    display: flex;
     align-items: center;
-    flex-wrap: wrap;
-    margin-bottom: 20px;
     gap: 13px;
+    margin-top: 18px;
 
-    .header-title-left {
-        display: flex;
-        align-items: center;
-        gap: 13px;
-        margin-top: 18px;
-
-        img {
-            width: 12px;
-            height: 19px;
-        }
-
-        div {
-            flex-shrink: 0;
-            font-weight: bold;
-            font-size: 18px;
-            color: #222222;
-            line-height: 27px;
-            font-family: "Source Han Sans CN Bold Bold";
-
-            &:before {
-                content: "*";
-                color: #f56c6c;
-                margin-right: 4px;
-            }
-        }
-
-        span {
-            flex-shrink: 0;
-            font-weight: bold;
-            font-size: 18px;
-            color: #222222;
-            line-height: 27px;
-            font-family: "Source Han Sans CN Bold Bold";
-        }
+    img {
+      width: 12px;
+      height: 19px;
     }
 
-    .header-title-left :first-child {
-        margin-top: 0;
+    div {
+      flex-shrink: 0;
+      font-weight: bold;
+      font-size: 18px;
+      color: #222222;
+      line-height: 27px;
+      font-family: "Source Han Sans CN Bold Bold";
+
+      &:before {
+        content: "*";
+        color: #f56c6c;
+        margin-right: 4px;
+      }
     }
+
+    span {
+      flex-shrink: 0;
+      font-weight: bold;
+      font-size: 18px;
+      color: #222222;
+      line-height: 27px;
+      font-family: "Source Han Sans CN Bold Bold";
+    }
+  }
+
+  .header-title-left :first-child {
+    margin-top: 0;
+  }
 }
 
 .header-title:first-child {
-    .header-title-left {
-        margin-top: 0;
-    }
+  .header-title-left {
+    margin-top: 0;
+  }
 }
 
 .item-title {
-    padding-left: 25px;
+  padding-left: 25px;
 
-    span {
-        flex-shrink: 0;
-        font-weight: bold;
-        font-size: 14px;
-        color: #222222;
-        line-height: 27px;
-        font-family: "Source Han Sans CN Bold Bold";
-        margin: 18px 0;
+  span {
+    flex-shrink: 0;
+    font-weight: bold;
+    font-size: 14px;
+    color: #222222;
+    line-height: 27px;
+    font-family: "Source Han Sans CN Bold Bold";
+    margin: 18px 0;
 
-        &:before {
-            content: "*";
-            color: #f56c6c;
-            margin-right: 4px;
-        }
+    &:before {
+      content: "*";
+      color: #f56c6c;
+      margin-right: 4px;
     }
+  }
 }
 
 .approval-dialog-approve {
-    padding: 18px 20px;
-    // display: flex;
-    align-content: center;
+  padding: 18px 20px;
+  // display: flex;
+  align-content: center;
 
-    .status {
-        margin-right: 40px;
-        max-width: 60%;
+  .status {
+    margin-right: 40px;
+    max-width: 60%;
+  }
+
+  //   align-items: center;
+  .status-title {
+    color: #222222;
+    font-family: "SourceHanSansCN-Medium";
+    line-height: 14px;
+    margin-bottom: 16px;
+  }
+
+  .status-content {
+    display: flex;
+    align-items: center;
+    width: 100%;
+    gap: 16px;
+    background: #ffffff;
+    border-radius: 10px;
+    border: 1px solid rgba(4, 156, 154, 0.5);
+
+    .resolve {
+      border-radius: 10px;
+      flex: 1;
+      font-size: 16px;
+      // padding: 5px 55px;
+      font-weight: 400;
+      color: #333333;
+      cursor: pointer;
+      line-height: 32px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
     }
 
-    //   align-items: center;
-    .status-title {
-        color: #222222;
-        font-family: "SourceHanSansCN-Medium";
-        line-height: 14px;
-        margin-bottom: 16px;
+    .reject {
+      flex: 1;
+      border-radius: 10px;
+      font-size: 16px;
+      line-height: 32px;
+      // padding: 5px 55px;
+      font-weight: 400;
+      color: #333333;
+      cursor: pointer;
+      display: flex;
+      align-items: center;
+      justify-content: center;
     }
 
-    .status-content {
-        display: flex;
-        align-items: center;
-        width: 100%;
-        gap: 16px;
-        background: #ffffff;
-        border-radius: 10px;
-        border: 1px solid rgba(4, 156, 154, 0.5);
-
-        .resolve {
-            border-radius: 10px;
-            flex: 1;
-            font-size: 16px;
-            // padding: 5px 55px;
-            font-weight: 400;
-            color: #333333;
-            cursor: pointer;
-            line-height: 32px;
-            display: flex;
-            align-items: center;
-            justify-content: center
-        }
-
-        .reject {
-            flex: 1;
-            border-radius: 10px;
-            font-size: 16px;
-            line-height: 32px;
-            // padding: 5px 55px;
-            font-weight: 400;
-            color: #333333;
-            cursor: pointer;
-            display: flex;
-            align-items: center;
-            justify-content: center
-        }
-
-        .activeStatus {
-            background: #ebfefd;
-            color: #049c9a;
-            box-shadow: 0px 0px 6px 0px rgba(10, 109, 108, 0.25);
-            border-radius: 10px;
-        }
+    .activeStatus {
+      background: #ebfefd;
+      color: #049c9a;
+      box-shadow: 0px 0px 6px 0px rgba(10, 109, 108, 0.25);
+      border-radius: 10px;
     }
+  }
 
-    .remark-title {
-        color: #222222;
-        font-family: "SourceHanSansCN-Medium";
-        line-height: 14px;
-        margin-bottom: 16px;
-    }
+  .remark-title {
+    color: #222222;
+    font-family: "SourceHanSansCN-Medium";
+    line-height: 14px;
+    margin-bottom: 16px;
+  }
 }
 
 .dialog-footer {
-    align-items: center;
-    display: flex;
-    justify-content: center;
-    gap: 20px;
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  gap: 20px;
 
-    button {
-        width: 150px;
-    }
+  button {
+    width: 150px;
+  }
 }
 </style>
\ No newline at end of file
diff --git a/laboratory/src/views/reportLibrary/feasibilityReport/components/approval/index.vue b/laboratory/src/views/reportLibrary/feasibilityReport/components/approval/index.vue
index 75583ba..d61d2e7 100644
--- a/laboratory/src/views/reportLibrary/feasibilityReport/components/approval/index.vue
+++ b/laboratory/src/views/reportLibrary/feasibilityReport/components/approval/index.vue
@@ -111,7 +111,7 @@
 import { getDetail } from "../../service";
 import { customUploadRequest, getFullUrl } from "@/utils/utils";
 import apiConfig from '@/utils/baseurl';
-import { getAllocateIp } from '@/utils/utils'
+import { getAllocateIp, downloadFileByUrl, isImageFile } from '@/utils/utils'
 
 export default {
   name: "ApprovalDialog",
@@ -168,13 +168,23 @@
   methods: {
     getFullUrl,
     handlePreview(file) {
-      console.log('url', file)
       if (file && file.url) {
-        if (file.url && file.url.startsWith(getAllocateIp())) {
-          window.open(file.url, '_blank');
+        // 判断是否为图片文件
+        const isImage = isImageFile(file.url);
+        
+        if (isImage) {
+          // 图片文件:使用 window.open 预览
+          if (file.url && file.url.startsWith(getAllocateIp())) {
+            console.log('图片链接', file.url)
+            window.open(file.url, '_blank');
+          } else {
+            let newUrl = getAllocateIp() + file.url;
+            console.log('图片链接', newUrl)
+            window.open(newUrl, '_blank');
+          }
         } else {
-          let newUrl = getAllocateIp() + file.url;
-          window.open(newUrl, '_blank');
+          // 非图片文件:使用下载方式
+          downloadFileByUrl(file.url, file.name);
         }
       }
     },
diff --git a/laboratory/src/views/reportLibrary/feasibilityStudy/components/approval/index.vue b/laboratory/src/views/reportLibrary/feasibilityStudy/components/approval/index.vue
index b177b84..0ce1b8e 100644
--- a/laboratory/src/views/reportLibrary/feasibilityStudy/components/approval/index.vue
+++ b/laboratory/src/views/reportLibrary/feasibilityStudy/components/approval/index.vue
@@ -109,7 +109,7 @@
 import ApprovalProcess from "@/components/approvalProcess";
 import AiEditor from "@/components/AiEditor";
 import { getDetail } from "../../service";
-import { customUploadRequest, getFullUrl, getAllocateIp } from "@/utils/utils";
+import { customUploadRequest, getFullUrl, getAllocateIp, downloadFileByUrl, isImageFile } from "@/utils/utils";
 import apiConfig from '@/utils/baseurl';
 
 export default {
@@ -167,13 +167,23 @@
   methods: {
     getFullUrl,
     handlePreview(file) {
-      console.log('url', file)
       if (file && file.url) {
-        if (file.url && file.url.startsWith(getAllocateIp())) {
-          window.open(file.url, '_blank');
+        // 判断是否为图片文件
+        const isImage = isImageFile(file.url);
+        
+        if (isImage) {
+          // 图片文件:使用 window.open 预览
+          if (file.url && file.url.startsWith(getAllocateIp())) {
+            console.log('图片链接', file.url)
+            window.open(file.url, '_blank');
+          } else {
+            let newUrl = getAllocateIp() + file.url;
+            console.log('图片链接', newUrl)
+            window.open(newUrl, '_blank');
+          }
         } else {
-          let newUrl = getAllocateIp() + file.url;
-          window.open(newUrl, '_blank');
+          // 非图片文件:使用下载方式
+          downloadFileByUrl(file.url, file.name);
         }
       }
     },
diff --git a/laboratory/src/views/reportLibrary/processDevelopment/components/approval/index.vue b/laboratory/src/views/reportLibrary/processDevelopment/components/approval/index.vue
index dff66d0..a87e688 100644
--- a/laboratory/src/views/reportLibrary/processDevelopment/components/approval/index.vue
+++ b/laboratory/src/views/reportLibrary/processDevelopment/components/approval/index.vue
@@ -115,7 +115,7 @@
 import ApprovalProcess from '@/components/approvalProcess'
 import AiEditor from '@/components/AiEditor'
 import { getDetail } from '../../service';
-import { customUploadRequest, getFullUrl, getAllocateIp } from "@/utils/utils";
+import { customUploadRequest, getFullUrl, getAllocateIp , downloadFileByUrl, isImageFile } from "@/utils/utils";
 import apiConfig from '@/utils/baseurl';
 
 
@@ -174,16 +174,26 @@
     methods: {
         getFullUrl,
         handlePreview(file) {
-            console.log('url', file)
-            if (file && file.url) {
-                if (file.url && file.url.startsWith(getAllocateIp())) {
-                    window.open(file.url, '_blank');
-                } else {
-                    let newUrl = getAllocateIp() + file.url;
-                    window.open(newUrl, '_blank');
-                }
-            }
-        },
+      if (file && file.url) {
+        // 判断是否为图片文件
+        const isImage = isImageFile(file.url);
+        
+        if (isImage) {
+          // 图片文件:使用 window.open 预览
+          if (file.url && file.url.startsWith(getAllocateIp())) {
+            console.log('图片链接', file.url)
+            window.open(file.url, '_blank');
+          } else {
+            let newUrl = getAllocateIp() + file.url;
+            console.log('图片链接', newUrl)
+            window.open(newUrl, '_blank');
+          }
+        } else {
+          // 非图片文件:使用下载方式
+          downloadFileByUrl(file.url, file.name);
+        }
+      }
+    },
         open() {
             if (!this.obj.id) {
                 this.$message.error('缺少必要参数');
diff --git a/laboratory/src/views/reportLibrary/projectProposalLibrary/components/approval/index.vue b/laboratory/src/views/reportLibrary/projectProposalLibrary/components/approval/index.vue
index 8328e36..6c89fe5 100644
--- a/laboratory/src/views/reportLibrary/projectProposalLibrary/components/approval/index.vue
+++ b/laboratory/src/views/reportLibrary/projectProposalLibrary/components/approval/index.vue
@@ -116,7 +116,7 @@
 import ApprovalProcess from '@/components/approvalProcess'
 import AiEditor from '@/components/AiEditor'
 import { getDetail } from '../../service';
-import { customUploadRequest, getFullUrl, getAllocateIp } from "@/utils/utils";
+import { customUploadRequest, getFullUrl, getAllocateIp , downloadFileByUrl, isImageFile } from "@/utils/utils";
 import apiConfig from '@/utils/baseurl';
 
 
@@ -175,16 +175,26 @@
     methods: {
         getFullUrl,
         handlePreview(file) {
-            console.log('url', file)
-            if (file && file.url) {
-                if (file.url && file.url.startsWith(getAllocateIp())) {
-                    window.open(file.url, '_blank');
-                } else {
-                    let newUrl = getAllocateIp() + file.url;
-                    window.open(newUrl, '_blank');
-                }
-            }
-        },
+      if (file && file.url) {
+        // 判断是否为图片文件
+        const isImage = isImageFile(file.url);
+        
+        if (isImage) {
+          // 图片文件:使用 window.open 预览
+          if (file.url && file.url.startsWith(getAllocateIp())) {
+            console.log('图片链接', file.url)
+            window.open(file.url, '_blank');
+          } else {
+            let newUrl = getAllocateIp() + file.url;
+            console.log('图片链接', newUrl)
+            window.open(newUrl, '_blank');
+          }
+        } else {
+          // 非图片文件:使用下载方式
+          downloadFileByUrl(file.url, file.name);
+        }
+      }
+    },
         open() {
             if (!this.obj.id) {
                 this.$message.error('缺少必要参数');
diff --git a/laboratory/src/views/reportLibrary/verificationRelease/components/approval/index.vue b/laboratory/src/views/reportLibrary/verificationRelease/components/approval/index.vue
index 206c48e..f8011cb 100644
--- a/laboratory/src/views/reportLibrary/verificationRelease/components/approval/index.vue
+++ b/laboratory/src/views/reportLibrary/verificationRelease/components/approval/index.vue
@@ -114,7 +114,7 @@
 import ApprovalProcess from '@/components/approvalProcess'
 import AiEditor from '@/components/AiEditor'
 import { getDetail } from '../../service';
-import { customUploadRequest, getFullUrl, getAllocateIp } from "@/utils/utils";
+import { customUploadRequest, getFullUrl, getAllocateIp , downloadFileByUrl, isImageFile } from "@/utils/utils";
 import apiConfig from '@/utils/baseurl';
 
 
@@ -173,16 +173,26 @@
     methods: {
         getFullUrl,
         handlePreview(file) {
-            console.log('url', file)
-            if (file && file.url) {
-                if (file.url && file.url.startsWith(getAllocateIp())) {
-                    window.open(file.url, '_blank');
-                } else {
-                    let newUrl = getAllocateIp() + file.url;
-                    window.open(newUrl, '_blank');
-                }
-            }
-        },
+      if (file && file.url) {
+        // 判断是否为图片文件
+        const isImage = isImageFile(file.url);
+        
+        if (isImage) {
+          // 图片文件:使用 window.open 预览
+          if (file.url && file.url.startsWith(getAllocateIp())) {
+            console.log('图片链接', file.url)
+            window.open(file.url, '_blank');
+          } else {
+            let newUrl = getAllocateIp() + file.url;
+            console.log('图片链接', newUrl)
+            window.open(newUrl, '_blank');
+          }
+        } else {
+          // 非图片文件:使用下载方式
+          downloadFileByUrl(file.url, file.name);
+        }
+      }
+    },
         open() {
             if (!this.obj.id) {
                 this.$message.error('缺少必要参数');

--
Gitblit v1.7.1