From b4c2cbc7825fe4732fb6333447fdada9567bcb97 Mon Sep 17 00:00:00 2001
From: 吴羡 <16976746+Xian-TT3@user.noreply.gitee.com>
Date: 星期一, 12 十月 2026 17:48:36 +0800
Subject: [PATCH] 资源需求(标准物质相关)模块调整;新增统一附件组件并接入供应商管理与客户满意度;客户满意度表单改客户卡片下拉并关联已完成委托单;预览、图表、导航栏等组件调整

---
 src/views/CNAS/resourceDemand/standardMaterailRequistion/index.vue             |   37 
 src/views/CNAS/resourceDemand/standardMaterialAccept/component/AddRecord.vue   |  457 +++++++++++-----
 src/components/Attachment/lims-attachment.vue                                  |  209 ++++++++
 src/components/echarts/echarts.vue                                             |   19 
 src/layout/components/Navbar.vue                                               |   84 +++
 src/api/cnas/resourceDemand/standardMaterial/standardMaterial.js               |   10 
 src/views/CNAS/resourceDemand/standardMaterial/component/formDia.vue           |   34 +
 src/views/CNAS/resourceDemand/standardMaterial/index.vue                       |   39 -
 src/api/business/productOrder.js                                               |    7 
 src/views/CNAS/externalService/supplierManage/index.vue                        |   14 
 src/views/CNAS/systemManagement/customerSatisfaction/components/formDialog.vue |  121 ++++
 /dev/null                                                                      |  125 ----
 src/views/CNAS/systemManagement/customerSatisfaction/index.vue                 |   25 
 src/views/CNAS/resourceDemand/standardMaterial/component/borrowDia.vue         |   67 +
 src/views/CNAS/resourceDemand/standardMaterialAccept/index.vue                 |   10 
 src/components/Preview/filePreview.vue                                         |  275 ++++++----
 16 files changed, 1,045 insertions(+), 488 deletions(-)

diff --git a/src/api/business/productOrder.js b/src/api/business/productOrder.js
index 9df59d5..21eb65c 100644
--- a/src/api/business/productOrder.js
+++ b/src/api/business/productOrder.js
@@ -146,3 +146,10 @@
     data: query
   })
 }
+// 鏌ヨ宸插畬鎴愬鎵樺崟锛堝鎴锋弧鎰忓害涓嬫媺鐢紝浠呭凡妫�楠岋級
+export function selectCompletedOrders() {
+  return request({
+    url: '/insOrder/selectCompletedOrders',
+    method: 'get'
+  })
+}
diff --git a/src/api/cnas/resourceDemand/standardMaterial/standardMaterial.js b/src/api/cnas/resourceDemand/standardMaterial/standardMaterial.js
index 0ff088d..bd9ca1f 100644
--- a/src/api/cnas/resourceDemand/standardMaterial/standardMaterial.js
+++ b/src/api/cnas/resourceDemand/standardMaterial/standardMaterial.js
@@ -55,14 +55,12 @@
   })
 }
 
-//鏍囧噯鐗╄川娓呭崟褰掕繕
-export function returnSubstance(query) {
+// 鏌ヨ鏍囧噯鐗╄川鍓╀綑鍙鐢ㄦ暟閲�
+export function getRemainingQuantity(substanceId) {
   return request({
-    url: '/feStandardSubstanceRecord/returnSubstance',
-    method: 'post',
-    data: query
+    url: `/feStandardSubstanceRecord/getRemainingQuantity/${substanceId}`,
+    method: 'get'
   })
 }
-
 
 
diff --git a/src/components/Attachment/lims-attachment.vue b/src/components/Attachment/lims-attachment.vue
new file mode 100644
index 0000000..e99027c
--- /dev/null
+++ b/src/components/Attachment/lims-attachment.vue
@@ -0,0 +1,209 @@
+<template>
+  <div>
+    <el-dialog :title="dialogTitle" v-model="visible" width="760px">
+      <div class="attachment-header">
+        <div class="el-upload__tip">{{ tip }}</div>
+        <el-upload :action="uploadAction" :headers="uploadHeader" multiple :show-file-list="false"
+          :on-success="handleUploadSuccess" :on-error="handleUploadError">
+          <el-button size="small" type="primary">涓婁紶闄勪欢</el-button>
+        </el-upload>
+      </div>
+      <el-table :data="attachmentList" border size="small" style="margin-top:12px">
+        <el-table-column label="鏂囦欢鍚嶇О" prop="fileName" min-width="280" show-overflow-tooltip />
+        <el-table-column label="涓婁紶鏃堕棿" prop="createTime" width="170" />
+        <el-table-column label="鎿嶄綔" width="180">
+          <template #default="scope">
+            <el-button link type="primary" @click="previewFile(scope.row)">棰勮</el-button>
+            <el-button link type="primary" @click="downloadFile(scope.row)">涓嬭浇</el-button>
+            <el-button link type="danger" @click="removeFile(scope.row)">鍒犻櫎</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <template #footer>
+        <el-button @click="visible = false">鍏� 闂�</el-button>
+      </template>
+    </el-dialog>
+    <el-dialog title="棰勮闄勪欢" v-model="previewVisible" fullscreen append-to-body>
+      <file-preview v-if="previewVisible" :file-url="previewUrl" :current-file="previewRow" />
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import request from '@/utils/request'
+import filePreview from '@/components/Preview/filePreview.vue'
+
+/**
+ * 閫氱敤闄勪欢绠$悊缁勪欢锛堜笂浼� / 棰勮 / 涓嬭浇 / 鍒犻櫎锛�
+ *
+ * 鐢ㄦ硶锛氶〉闈㈡搷浣滃垪鏀句笁涓叆鍙o紝鍒嗗埆璋冪敤鏈粍浠剁殑鏂规硶锛�
+ *   涓婁紶闄勪欢 -> this.$refs.xxx.open(row)                 鎵撳紑闄勪欢寮圭獥锛堝脊绐楀唴鍙笂浼狅級
+ *   涓嬭浇闄勪欢 -> this.$refs.xxx.downloadAttachments(row)  鍙湁涓�涓檮浠剁洿鎺ヤ笅杞斤紝澶氫釜鎵撳紑寮圭獥
+ *   棰勮闄勪欢 -> this.$refs.xxx.previewAttachments(row)   鍙湁涓�涓檮浠剁洿鎺ラ瑙堬紝澶氫釜鎵撳紑寮圭獥
+ *
+ * 鍚庣鎺ュ彛闇�鎸夌粺涓�绾﹀畾鎻愪緵锛堝弬鐓� /supplierManagement 鐨勯檮浠舵帴鍙o級锛�
+ *   涓婁紶锛歅OST {uploadUrl}?{idParam}=涓昏〃ID  琛ㄥ崟鍙傛暟 file
+ *   鍒楄〃锛欸ET  {selectUrl}?{idParam}=涓昏〃ID  杩斿洖 [{id, fileName, fileUrl, createTime, ...}]
+ *   鍒犻櫎锛欴ELETE {deleteUrl}?id=闄勪欢ID
+ */
+export default {
+  name: 'LimsAttachment',
+  components: {
+    filePreview
+  },
+  props: {
+    // 涓婁紶鎺ュ彛鍦板潃锛屽 /supplierManagement/uploadAttachment
+    uploadUrl: {
+      type: String,
+      required: true
+    },
+    // 鏌ヨ闄勪欢鍒楄〃鎺ュ彛鍦板潃
+    selectUrl: {
+      type: String,
+      required: true
+    },
+    // 鍒犻櫎闄勪欢鎺ュ彛鍦板潃
+    deleteUrl: {
+      type: String,
+      required: true
+    },
+    // 鍏宠仈涓昏〃ID鍦ㄦ帴鍙i噷鐨勫弬鏁板悕锛屽 supplierManagementId
+    idParam: {
+      type: String,
+      default: 'id'
+    },
+    // 鍏宠仈涓昏〃ID鍦ㄨ鏁版嵁閲岀殑瀛楁鍚嶏紝涓嶄紶鏃堕粯璁や笌 idParam 鐩稿悓
+    idField: {
+      type: String,
+      default: ''
+    },
+    // 寮圭獥鏍囬涓婅鏄剧ず鍚嶇О鐨勮瀛楁锛屽 supplierName
+    nameField: {
+      type: String,
+      default: ''
+    },
+    // 寮圭獥鏍囬鍓嶇紑
+    title: {
+      type: String,
+      default: '闄勪欢'
+    },
+    // 寮圭獥宸︿笂瑙掓彁绀烘枃瀛�
+    tip: {
+      type: String,
+      default: '鏀寔浠绘剰鏍煎紡鏂囦欢锛屽崟涓枃浠朵笉瓒呰繃200MB'
+    }
+  },
+  data() {
+    return {
+      visible: false,
+      attachmentList: [],
+      currentRow: {},
+      previewVisible: false,
+      previewRow: {},
+      previewUrl: ''
+    }
+  },
+  computed: {
+    currentId() {
+      const field = this.idField || this.idParam
+      return this.currentRow ? this.currentRow[field] : undefined
+    },
+    dialogTitle() {
+      const name = this.nameField && this.currentRow ? this.currentRow[this.nameField] : ''
+      return this.title + (name ? ' - ' + name : '')
+    },
+    uploadAction() {
+      return this.javaApi + this.uploadUrl + '?' + this.idParam + '=' + this.currentId
+    }
+  },
+  methods: {
+    // 鎵撳紑闄勪欢绠$悊寮圭獥
+    open(row) {
+      this.currentRow = row
+      this.visible = true
+      this.loadList()
+    },
+    // 棰勮闄勪欢鍏ュ彛锛氬彧鏈変竴涓檮浠剁洿鎺ラ瑙堬紝澶氫釜鎵撳紑寮圭獥
+    previewAttachments(row) {
+      this.queryList(row).then(list => {
+        if (!list.length) {
+          this.$message.warning('鏆傛棤闄勪欢')
+        } else if (list.length === 1) {
+          this.previewFile(list[0])
+        } else {
+          this.open(row)
+        }
+      })
+    },
+    // 涓嬭浇闄勪欢鍏ュ彛锛氬彧鏈変竴涓檮浠剁洿鎺ヤ笅杞斤紝澶氫釜鎵撳紑寮圭獥
+    downloadAttachments(row) {
+      this.queryList(row).then(list => {
+        if (!list.length) {
+          this.$message.warning('鏆傛棤闄勪欢')
+        } else if (list.length === 1) {
+          this.downloadFile(list[0])
+        } else {
+          this.open(row)
+        }
+      })
+    },
+    queryList(row) {
+      const field = this.idField || this.idParam
+      return request({
+        url: this.selectUrl,
+        method: 'get',
+        params: { [this.idParam]: row[field] }
+      }).then(res => (res && res.data) || [])
+    },
+    loadList() {
+      this.queryList(this.currentRow).then(list => {
+        this.attachmentList = list
+      })
+    },
+    handleUploadSuccess(res) {
+      if (res.code === 200) {
+        this.$message.success('涓婁紶鎴愬姛')
+        this.loadList()
+      } else {
+        this.$message.error(res.msg || '涓婁紶澶辫触')
+      }
+    },
+    handleUploadError() {
+      this.$message.error('涓婁紶澶辫触锛屾枃浠跺彲鑳借秴杩�200MB')
+    },
+    previewFile(row) {
+      this.previewRow = row
+      this.previewUrl = this.fullUrl(row.fileUrl)
+      this.previewVisible = true
+    },
+    downloadFile(row) {
+      this.$download.saveAs(this.fullUrl(row.fileUrl), row.fileName)
+    },
+    removeFile(row) {
+      this.$confirm('鏄惁鍒犻櫎璇ラ檮浠讹紵', '鎻愮ず', { type: 'warning' }).then(() => {
+        request({
+          url: this.deleteUrl,
+          method: 'delete',
+          params: { id: row.id }
+        }).then(() => {
+          this.$message.success('鍒犻櫎鎴愬姛')
+          this.loadList()
+        })
+      }).catch(() => {})
+    },
+    // 闈欐�佸湴鍧�鎷兼帴锛屼笌鍚庣涓婁紶缁熶竴璺敱淇濇寔涓�鑷达細鍥剧墖鍦� /img锛屽叾瀹冮兘鍦� /word
+    fullUrl(fileUrl) {
+      const base = /\.(jpg|jpeg|png|gif)$/i.test(fileUrl) ? '/img/' : '/word/'
+      return this.javaApi + base + fileUrl
+    }
+  }
+}
+</script>
+
+<style scoped>
+.attachment-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+</style>
diff --git a/src/components/Preview/filePreview.vue b/src/components/Preview/filePreview.vue
index df8445e..e8ec772 100644
--- a/src/components/Preview/filePreview.vue
+++ b/src/components/Preview/filePreview.vue
@@ -1,53 +1,67 @@
 <template>
   <div>
-    <div v-if="isImage">
-      <img :src="imgUrl" alt="Image Preview" />
+    <!-- 鍥剧墖 -->
+    <div v-if="isImage" style="text-align: center">
+      <img :src="imgUrl" alt="鍥剧墖棰勮" />
     </div>
-    <div v-if="isPdf" style="height: 80vh;">
+    <!-- 瑙嗛 -->
+    <div v-else-if="isVideo" style="text-align: center">
+      <video :src="fileUrl" controls style="max-width: 100%; max-height: 85vh"></video>
+    </div>
+    <!-- 闊抽 -->
+    <div v-else-if="isAudio" style="text-align: center; padding-top: 10vh">
+      <audio :src="fileUrl" controls style="width: 60%"></audio>
+    </div>
+    <!-- PDF锛氫紶浜� OnlyOffice 閰嶇疆璧版枃妗f湇鍔★紝鍚﹀垯鐢ㄦ祻瑙堝櫒鍐呯疆闃呰鍣� -->
+    <div v-else-if="isPdf" style="height: 80vh;">
       <onlyoffice v-if="useOnlyOffice" ref="onlyoffice" :options="option" style="width: 100%;height: 100%;" />
       <iframe v-else-if="pdfUrl" :src="pdfUrl" title="PDF 棰勮" style="width: 100%;height: 100%;border: none;"></iframe>
       <p v-else-if="pdfError">PDF 棰勮澶辫触锛�<a :href="fileUrl" style="color: var(--el-color-primary);" target="_blank">鐐规涓嬭浇鏌ョ湅</a></p>
       <p v-else>姝e湪鍔犺浇 PDF鈥�</p>
     </div>
-    <div v-if="isDoc">
-      <p v-if="!isDocShow">鏂囨。鏃犳硶鐩存帴棰勮锛岃涓嬭浇鏌ョ湅銆�</p>
-      <a :href="fileUrl" v-if="!isDocShow">涓嬭浇鏂囦欢</a>
-      <vue-office-docx v-else :src="fileUrl" style="height: 100vh;" @rendered="renderedHandler" @error="errorHandler" />
-    </div>
-    <div v-if="isXls">
-      <p v-if="!isDocShow">鏂囨。鏃犳硶鐩存帴棰勮锛岃涓嬭浇鏌ョ湅銆�</p>
-      <a :href="fileUrl" v-if="!isDocShow">涓嬭浇鏂囦欢</a>
-      <vue-office-excel v-else :src="fileUrl" :options="excelOptions" style="height: 100vh;" @rendered="renderedHandler"
+    <!-- Word -->
+    <div v-else-if="isDoc">
+      <vue-office-docx v-if="isDocShow" :key="fileUrl" :src="fileUrl" style="height: 100vh;" @rendered="renderedHandler"
         @error="errorHandler" />
+      <div v-else class="preview-fallback">
+        <p>Word 鏂囨。鏃犳硶鐩存帴棰勮锛岃涓嬭浇鏌ョ湅銆�</p>
+        <el-button type="primary" @click="downloadFile">涓嬭浇鏂囦欢</el-button>
+      </div>
     </div>
-    <div v-if="isZipOrRar">
-      <p>鍘嬬缉鏂囦欢鏃犳硶鐩存帴棰勮锛岃涓嬭浇鏌ョ湅銆�</p>
-      <a :href="fileUrl">涓嬭浇鏂囦欢</a>
+    <!-- Excel -->
+    <div v-else-if="isXls">
+      <vue-office-excel v-if="isDocShow" :key="fileUrl" :src="fileUrl" :options="excelOptions" style="height: 100vh;"
+        @rendered="renderedHandler" @error="errorHandler" />
+      <div v-else class="preview-fallback">
+        <p>Excel 鏂囨。鏃犳硶鐩存帴棰勮锛岃涓嬭浇鏌ョ湅銆�</p>
+        <el-button type="primary" @click="downloadFile">涓嬭浇鏂囦欢</el-button>
+      </div>
     </div>
-    <div v-if="isCsv">
-      <p v-if="csvList.length == 0">CSV 鏂囦欢鏃犳硶鐩存帴棰勮锛岃涓嬭浇鏌ョ湅銆�</p>
-      <a :href="fileUrl" v-if="csvList.length == 0">涓嬭浇鏂囦欢</a>
-      <el-tabs type="border-card" v-if="csvList.length > 0" tab-position="bottom">
-        <el-tab-pane :label="item.sheetName" v-for="(item, index) in csvList" :key="index">
-          <el-table :data="item.tableData" height="75vh">
-            <el-table-column :label="m.label" :prop="m.prop" v-for="(m, i) in item.column" :key="i" min-width="120px"
-              show-overflow-tooltip>
-              <template #header="scope">
-                <div>
-                  <el-tooltip :content="m.label" placement="top">
-                    <div class="oneLine">
-                      <span>{{ m.label }}</span>
-                    </div>
-                  </el-tooltip>
-                </div>
-              </template>
-            </el-table-column>
-          </el-table>
-        </el-tab-pane>
-      </el-tabs>
+    <!-- CSV 琛ㄦ牸棰勮 -->
+    <div v-else-if="isCsv">
+      <el-table v-if="csvRows.length" :data="csvRows" height="75vh">
+        <el-table-column :label="col" :prop="col" v-for="(col, i) in csvColumns" :key="i" min-width="120px"
+          show-overflow-tooltip />
+      </el-table>
+      <div v-else-if="csvError" class="preview-fallback">
+        <p>CSV 鏂囦欢瑙f瀽澶辫触锛岃涓嬭浇鏌ョ湅銆�</p>
+        <el-button type="primary" @click="downloadFile">涓嬭浇鏂囦欢</el-button>
+      </div>
+      <p v-else>姝e湪鍔犺浇鈥�</p>
     </div>
-    <div v-if="!isSupported">
-      <p>涓嶆敮鎸佺殑鏂囦欢鏍煎紡</p>
+    <!-- 鏂囨湰鏂囦欢 -->
+    <div v-else-if="isText">
+      <pre v-if="textLoaded" class="text-preview">{{ textContent }}</pre>
+      <div v-else-if="textError" class="preview-fallback">
+        <p>鏂囨湰鏂囦欢璇诲彇澶辫触锛岃涓嬭浇鏌ョ湅銆�</p>
+        <el-button type="primary" @click="downloadFile">涓嬭浇鏂囦欢</el-button>
+      </div>
+      <p v-else>姝e湪鍔犺浇鈥�</p>
+    </div>
+    <!-- 鍏滃簳锛氬帇缂╁寘銆佺▼搴忔枃浠剁瓑娴忚鍣ㄦ棤娉曠洿鎺ユ樉绀虹殑鏍煎紡锛屼繚璇佷篃鍙笅杞� -->
+    <div v-else class="preview-fallback">
+      <p>璇ョ被鍨嬫枃浠讹紙{{ fileExt }}锛夋祻瑙堝櫒鏃犳硶鍦ㄧ嚎鏄剧ず锛岃涓嬭浇鍚庢煡鐪嬨��</p>
+      <el-button type="primary" @click="downloadFile">涓嬭浇鏂囦欢</el-button>
     </div>
   </div>
 </template>
@@ -85,22 +99,29 @@
   data() {
     return {
       isDocShow: true,
-      csvList: [], //csv鏂囦欢鏁版嵁
+      csvColumns: [],
+      csvRows: [],
+      csvError: false,
+      textContent: '',
+      textLoaded: false,
+      textError: false,
       pdfUrl: '', //pdf鏈湴棰勮鍦板潃
       pdfError: false
     }
   },
   computed: {
-    // 鍘熷疄鐜版妸 isImage / isXls / isCsv 鍐欐垚浜嗐�岃鏂囦欢鍚庣紑 + 椤烘墜鏀瑰埆鐨勫搷搴斿紡鐘舵�併�嶇殑 computed锛�
-    // isImage 閲屽啓 imgUrl銆乮sXls 閲屽啓 options.xls銆乮sCsv 閲岀洿鎺ュ彂璇锋眰銆�
-    // computed 閲屽啓鐘舵�佸睘浜庡壇浣滅敤锛屽湪 Vue3 涓細鍦ㄦ覆鏌撴湡瑙﹀彂鐘舵�佸彉鏇达紝寰堝鏄撴紨鍙樻垚閫掑綊鏇存柊銆�
-    // 杩欓噷鎷嗘垚绾绠楀睘鎬э紝鍓綔鐢ㄧ粺涓�浜ょ粰涓嬮潰閭d釜 watch銆�
     isImage() {
       return /\.(jpg|jpeg|png|gif)$/i.test(this.fileUrl)
     },
     imgUrl() {
-      // 鍘� isImage 鏃犺鐪熷亣閮戒細鍐� imgUrl = fileUrl锛堝懡涓浘鐗囨椂鎶� word 鎹㈡垚 img锛�
+      // 鍥剧墖瀛樺湪 /img 闈欐�佽矾寰勪笅锛岃皟鐢ㄦ柟浼犵殑鍙兘鏄� /word 鍦板潃锛岃繖閲岀粺涓�鎹㈡垚 /img
       return this.isImage ? this.fileUrl.replaceAll('word', 'img') : ''
+    },
+    isVideo() {
+      return /\.(mp4|webm|ogv|ogg)$/i.test(this.fileUrl)
+    },
+    isAudio() {
+      return /\.(mp3|wav|m4a|aac|flac|oga)$/i.test(this.fileUrl)
     },
     isPdf() {
       return /\.pdf$/i.test(this.fileUrl)
@@ -110,6 +131,12 @@
     },
     isXls() {
       return /\.(xls|xlsx)$/i.test(this.fileUrl)
+    },
+    isCsv() {
+      return /\.csv$/i.test(this.fileUrl)
+    },
+    isText() {
+      return /\.(txt|log|json|xml|md|ini|conf|cfg|yml|yaml|sql|properties)$/i.test(this.fileUrl)
     },
     // vue-office 鐨� excel 缁勪欢鐢� xls 鏍囧織鍖哄垎 xlsx / xls
     excelOptions() {
@@ -121,20 +148,15 @@
         heightOffset: 10, //鍦ㄩ粯璁ゆ覆鏌撶殑鍒楄〃楂樺害涓婂啀鍔� Npx楂�
         beforeTransformData: workbookData => {
           return workbookData
-        }, //搴曞眰閫氳繃exceljs鑾峰彇excel鏂囦欢鍐呭锛岄�氳繃璇ラ挬瀛愬嚱鏁帮紝鍙互瀵硅幏鍙栫殑excel鏂囦欢鍐呭杩涜淇敼锛屾瘮濡傛煇涓崟鍏冩牸鐨勬暟鎹樉绀轰笉姝g‘锛屽彲浠ュ湪姝よ嚜琛屼慨鏀规瘡涓崟鍏冩牸鐨剉alue鍊笺��
+        }, //搴曞眰閫氳繃exceljs鑾峰彇excel鏂囦欢鍐呭锛岄�氳繃璇ラ挬瀛愬嚱鏁帮紝鍙互瀵硅幏鍙栧埌鐨別xcel鏂囦欢鍐呭杩涜淇敼锛屾瘮濡傛煇涓崟鍏冩牸鐨勬暟鎹樉绀轰笉姝g‘锛屽彲浠ュ湪姝よ嚜琛屼慨鏀规瘡涓崟鍏冩牸鐨剉alue鍊笺��
         transformData: workbookData => {
           return workbookData
         } //灏嗚幏鍙栧埌鐨別xcel鏁版嵁杩涜澶勭悊涔嬪悗涓旀覆鏌撳埌椤甸潰涔嬪墠锛屽彲閫氳繃transformData瀵瑰嵆灏嗘覆鏌撶殑鏁版嵁鍙婃牱寮忚繘琛屼慨鏀癸紝姝ゆ椂姣忎釜鍗曞厓鏍肩殑text鍊煎氨鏄嵆灏嗘覆鏌撳埌椤甸潰涓婄殑鍐呭
       }
     },
-    isZipOrRar() {
-      return /\.(zip|rar)$/i.test(this.fileUrl)
-    },
-    isCsv() {
-      return /\.csv$/i.test(this.fileUrl)
-    },
-    isSupported() {
-      return this.isImage || this.isPdf || this.isDoc || this.isZipOrRar || this.isCsv || this.isXls
+    fileExt() {
+      const match = this.fileUrl.match(/\.([a-zA-Z0-9]+)$/)
+      return match ? match[1].toUpperCase() : '鏈煡'
     },
     // 璋冪敤鏂逛紶浜� OnlyOffice 鐨勯厤缃墠璧版枃妗f湇鍔★紝鍚﹀垯鐢ㄦ祻瑙堝櫒鍐呯疆闃呰鍣�
     useOnlyOffice() {
@@ -144,10 +166,9 @@
   watch: {
     fileUrl: {
       handler() {
+        this.resetState()
         this.loadPdf()
-        if (this.isCsv) {
-          this.loadCSVData()
-        }
+        this.loadTextContent()
       },
       immediate: true
     }
@@ -156,6 +177,16 @@
     this.releasePdfUrl()
   },
   methods: {
+    // 鍒囨崲鏂囦欢鏃舵竻鎺変笂涓�涓枃浠剁殑棰勮鐘舵�侊紝閬垮厤娌跨敤鏃х粨鏋滐紙姣斿涓婃 Word 娓叉煋澶辫触锛岃繖娆¤繕琚崱鍦ㄤ笅杞藉厹搴曢〉锛�
+    resetState() {
+      this.isDocShow = true
+      this.csvColumns = []
+      this.csvRows = []
+      this.csvError = false
+      this.textContent = ''
+      this.textLoaded = false
+      this.textError = false
+    },
     releasePdfUrl() {
       if (this.pdfUrl) {
         URL.revokeObjectURL(this.pdfUrl)
@@ -178,51 +209,44 @@
         this.pdfError = true
       }
     },
-    renderedHandler() {
-      console.log('娓叉煋瀹屾垚')
-      this.isDocShow = true
-      this.resetStyle()
-    },
-    errorHandler() {
-      console.log('娓叉煋澶辫触')
-      this.isDocShow = false
-    },
-    async loadCSVData() {
-      // 鍘熷疄鐜颁緷璧� this.$axios / this.$api锛岃�岃繖涓や釜鍏ㄥ眬鍙橀噺鍦ㄩ」鐩噷浠庢湭娉ㄥ唽杩�
-      // 锛坢ain.js 娌℃湁鎸傝浇锛屼篃娌℃湁 api 鑱氬悎瀵硅薄锛夛紝涓�鏃﹁繘鍏� CSV 鍒嗘敮灏辨姏 TypeError銆�
-      // 鍦ㄦ嬁鍒扮湡瀹炴帴鍙d箣鍓嶅厛鏄惧紡闄嶇骇涓恒�屼笅杞芥煡鐪嬨�嶏紝涓庣敤鎴峰疄闄呯湅鍒扮殑缁撴灉涓�鑷淬��
-      if (!this.$axios || !this.$api?.insOrderPlan?.preview) {
-        console.warn('[filePreview] 鏈厤缃� $axios/$api锛孋SV 杞〃鏍间笉鍙敤锛屽凡闄嶇骇涓轰笅杞芥煡鐪�')
-        this.csvList = []
+    // CSV 瑙f瀽鎴愯〃鏍笺�佹枃鏈枃浠剁洿鎺ュ睍绀猴紱鍐呭甯歌涓� GBK 缂栫爜锛孶TF-8 瑙e嚭涔辩爜鏃舵崲 GBK 鍐嶈В涓�娆�
+    async loadTextContent() {
+      if (!this.isCsv && !this.isText) {
         return
       }
-      this.$axios
-        .post(this.$api.insOrderPlan.preview, {
-          id: this.currentFile.id
-        })
-        .then(res => {
-          let arr = res.data
-          arr = arr.map(m => {
-            const obj = {
-              sheetName: m.sheetName,
-              tableData: [],
-              column: []
-            }
-            obj.tableData = this.formatCSVToTable(m.content.replaceAll('null', ' '))
-            // .replaceAll('MIN','=MIN').replaceAll('MAX','=MAX').replaceAll('AVERAGE','=AVERAGE')
-            for (const item in obj.tableData[0]) {
-              obj.column.push({
-                label: item,
-                prop: item
-              })
-            }
-            return obj
-          })
-          this.csvList = arr
-        })
-        .catch(err => {
-          console.log(err)
-        })
+      try {
+        const { data } = await axios.get(this.fileUrl, { responseType: 'arraybuffer' })
+        let text = new TextDecoder('utf-8').decode(data)
+        // 瑙g爜缁撴灉閲屽嚭鐜� U+FFFD 鏇挎崲绗︼紝璇存槑鍘熸枃涓嶆槸 UTF-8锛屽父瑙佹槸 GBK 缂栫爜鐨勪腑鏂囨枃浠�
+        if (text.indexOf(String.fromCharCode(0xFFFD)) !== -1) {
+          try {
+            text = new TextDecoder('gbk').decode(data)
+          } catch (e) {
+            // 涓埆娴忚鍣ㄤ笉鏀寔 gbk 瑙g爜锛屼繚鐣� UTF-8 缁撴灉
+          }
+        }
+        if (this.isCsv) {
+          const rows = this.formatCSVToTable(text.replaceAll('\r\n', '\n').replaceAll('\r', '\n'))
+          if (rows.length) {
+            this.csvColumns = Object.keys(rows[0])
+            this.csvRows = rows.slice(0, 5000)
+          } else {
+            this.csvError = true
+          }
+        } else {
+          this.textContent = text.length > 1000000
+            ? text.slice(0, 1000000) + '\n\n鈥︹�︼紙鏂囦欢杩囧ぇ锛屼粎棰勮鍓嶄竴鐧句竾瀛楋紝瀹屾暣鍐呭璇蜂笅杞芥煡鐪嬶級'
+            : (text || '锛堟枃浠跺唴瀹逛负绌猴級')
+          this.textLoaded = true
+        }
+      } catch (error) {
+        console.error(error)
+        if (this.isCsv) {
+          this.csvError = true
+        } else {
+          this.textError = true
+        }
+      }
     },
     formatCSVToTable(str) {
       const result = []
@@ -239,19 +263,10 @@
             } else {
               const obj = {}
               for (let index = 0; index < arrHeader.length; index++) {
-                // 缁勮琛ㄦ牸鏁版嵁
-                const name = String(arrHeader[index])
+                const name = String(arrHeader[index]).trim()
                 if (!arrHeader[index]) continue
                 if (!obj[name]) {
-                  try {
-                    if (kv[index]) {
-                      obj[name] = String(kv[index])
-                    } else {
-                      obj[name] = ''
-                    }
-                  } catch (err) {
-                    obj[name] = ''
-                  }
+                  obj[name] = kv[index] ? String(kv[index]) : ''
                 }
               }
               result.push(obj)
@@ -260,6 +275,21 @@
         }
       }
       return result
+    },
+    downloadFile() {
+      const name = this.currentFile && this.currentFile.fileName
+        ? this.currentFile.fileName
+        : this.fileUrl.split('/').pop()
+      this.$download.saveAs(this.fileUrl, name)
+    },
+    renderedHandler() {
+      console.log('娓叉煋瀹屾垚')
+      this.isDocShow = true
+      this.resetStyle()
+    },
+    errorHandler() {
+      console.log('娓叉煋澶辫触')
+      this.isDocShow = false
     },
     resetStyle() {
       // 娉細鍘熷疄鐜版槸 style.replace(/pt/g, 'px')锛屼細鎶婃牱寮忛噷浠绘剰浣嶇疆鐨� pt 閮芥崲鎺�
@@ -282,9 +312,26 @@
   max-width: 100%;
 }
 
-.oneLine {
-  overflow: hidden;
-  white-space: nowrap;
-  text-overflow: ellipsis;
+.text-preview {
+  white-space: pre-wrap;
+  word-break: break-all;
+  max-height: 85vh;
+  overflow: auto;
+  padding: 12px;
+  margin: 0;
+  background: #f8f9fa;
+  font-family: Consolas, Monaco, monospace;
+  font-size: 13px;
+  line-height: 1.6;
+}
+
+.preview-fallback {
+  text-align: center;
+  padding-top: 15vh;
+  color: #606266;
+}
+
+.preview-fallback p {
+  margin-bottom: 16px;
 }
 </style>
diff --git a/src/components/echarts/echarts.vue b/src/components/echarts/echarts.vue
index 4b85446..6caf970 100644
--- a/src/components/echarts/echarts.vue
+++ b/src/components/echarts/echarts.vue
@@ -210,21 +210,18 @@
     },
 
     generateChart(option) {
-      // 閰嶇疆鎶樼嚎鍥惧拰鏌辩姸鍥剧殑鏍峰紡
+      // 鏍峰紡鍙姞鍦ㄥ壇鏈笂锛屼笉鑳藉洖鍐� this.series锛歴eries 鏄埗椤甸潰鐨勫搷搴斿紡鏁版嵁锛�
+      // 鍥炲啓浼氳Е鍙戞湰缁勪欢 series 鐨� deep watcher 鍐嶆 renderChart锛屾瘡娆$敓鎴愭柊瀵硅薄姘镐笉鏀舵暃锛岄〉闈㈡寰幆鍗℃
       if (option.series && option.series.length > 0) {
-        option.series.forEach((s, index) => {
+        option.series = option.series.map((s, index) => {
           if (s.type === 'line') {
-            s.itemStyle = {
-              color: this.lineColors[index] || this.lineColors[0]
-            }
-            s.lineStyle = {
-              color: this.lineColors[index] || this.lineColors[0]
-            }
+            const color = this.lineColors[index] || this.lineColors[0]
+            return { ...s, itemStyle: { color }, lineStyle: { color } }
           } else if (s.type === 'bar') {
-            s.itemStyle = {
-              color: this.barColors[index] || this.barColors[0]
-            }
+            const color = this.barColors[index] || this.barColors[0]
+            return { ...s, itemStyle: { color } }
           }
+          return s
         })
       }
       this.chartInstance.clear()
diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue
index 6db7c89..159b3f1 100644
--- a/src/layout/components/Navbar.vue
+++ b/src/layout/components/Navbar.vue
@@ -5,6 +5,28 @@
       <span class="label">LIMS瀹為獙瀹ょ鐞嗙郴缁�</span>
     </div>
     <div class="right-menu">
+      <div class="menu-search">
+        <el-select
+          v-model="selectedMenu"
+          placeholder="鎼滅储鑿滃崟"
+          filterable
+          clearable
+          :fit-input-width="false"
+          :filter-method="filterMenus"
+          @change="handleMenuSelect"
+          @clear="resetMenuFilter"
+        >
+          <template #prefix>
+            <lims-icon name="el-icon-search" />
+          </template>
+          <el-option
+            v-for="item in filteredMenus"
+            :key="item.path"
+            :label="item.breadcrumb"
+            :value="item.path"
+          />
+        </el-select>
+      </div>
       <div class="avatar-wrapper">
         <el-avatar shape="square" :size="30">{{ avatarText }}</el-avatar>
         <span class="userName">{{ userStore.nickName }}</span>
@@ -31,11 +53,61 @@
 import Hamburger from '@/components/Hamburger'
 import useAppStore from '@/store/modules/app'
 import useUserStore from '@/store/modules/user'
+import usePermissionStore from '@/store/modules/permission'
+import { getNormalPath } from '@/utils/ruoyi'
 
 const appStore = useAppStore()
 const userStore = useUserStore()
+const permissionStore = usePermissionStore()
+const router = useRouter()
 
 const avatarText = computed(() => (userStore.nickName || '').substring(0, 1))
+
+const selectedMenu = ref(undefined)
+const filteredMenus = ref([])
+
+// 渚ц竟鏍忚彍鍗曟爲鎷嶅钩鎴愬彲鎼滅储鍒楄〃锛堟爣棰� + 瀹屾暣璺緞闈㈠寘灞戯級
+const menuList = computed(() => {
+  const list = []
+  const walk = (routes, basePath, parents) => {
+    ;(routes || []).forEach(route => {
+      if (!route || route.hidden || typeof route.path !== 'string') return
+      const fullPath = getNormalPath(`${basePath}/${route.path}`)
+      const title = route.meta && route.meta.title
+      const children = Array.isArray(route.children) ? route.children : []
+      if (children.length) {
+        walk(children, fullPath, title ? parents.concat(title) : parents)
+      } else if (title && fullPath) {
+        list.push({ title, path: fullPath, breadcrumb: parents.concat(title).join(' / ') })
+      }
+    })
+  }
+  walk(permissionStore.sidebarRouters, '', [])
+  return list
+})
+
+function filterMenus(query) {
+  const q = (query || '').trim().toLowerCase()
+  filteredMenus.value = q
+    ? menuList.value.filter(item =>
+        item.title.toLowerCase().includes(q) || item.breadcrumb.toLowerCase().includes(q)
+      )
+    : menuList.value
+}
+
+function resetMenuFilter() {
+  filteredMenus.value = menuList.value
+}
+
+function handleMenuSelect(path) {
+  if (path) {
+    router.push(path).catch(() => {})
+  }
+  selectedMenu.value = undefined
+  resetMenuFilter()
+}
+
+watch(menuList, resetMenuFilter, { immediate: true })
 
 function toggleSideBar() {
   appStore.toggleSideBar()
@@ -123,6 +195,18 @@
       outline: none;
     }
 
+    .menu-search {
+      margin-right: 20px;
+
+      :deep(.el-select) {
+        width: 210px;
+      }
+
+      :deep(.el-select__wrapper) {
+        border-radius: 16px;
+      }
+    }
+
     .avatar-wrapper {
       display: flex;
       align-items: center;
diff --git a/src/views/CNAS/externalService/supplierManage/index.vue b/src/views/CNAS/externalService/supplierManage/index.vue
index 49918bf..26b9fd7 100644
--- a/src/views/CNAS/externalService/supplierManage/index.vue
+++ b/src/views/CNAS/externalService/supplierManage/index.vue
@@ -22,12 +22,16 @@
         :page="page" :tableLoading="tableLoading"></lims-table>
     </div>
     <form-dia ref="formDia" v-if="formDia" @closeDia="closeDia"></form-dia>
+    <lims-attachment ref="attachmentDialog" title="渚涘簲鍟嗛檮浠�" name-field="supplierName" id-param="supplierManagementId"
+      upload-url="/supplierManagement/uploadAttachment" select-url="/supplierManagement/selectAttachments"
+      delete-url="/supplierManagement/deleteAttachment" />
   </div>
 </template>
 
 <script>
 import FormDia from "../supplierManage/component/formDia.vue";
 import limsTable from '@/components/Table/lims-table.vue'
+import limsAttachment from '@/components/Attachment/lims-attachment.vue'
 import {
   selectQualifiedSupplierManagementPage,
   exportSupplierManagement,
@@ -39,7 +43,8 @@
   // import 寮曞叆鐨勭粍浠堕渶瑕佹敞鍏ュ埌瀵硅薄涓墠鑳戒娇鐢�
   components: {
     limsTable,
-    FormDia
+    FormDia,
+    limsAttachment
   },
   data() {
     // 杩欓噷瀛樻斁鏁版嵁
@@ -83,6 +88,13 @@
               },
             },
             {
+              name: '闄勪欢',
+              type: 'text',
+              clickFun: (row) => {
+                this.$refs.attachmentDialog.open(row);
+              },
+            },
+            {
               name: '鍒犻櫎',
               type: 'text',
               color: 'var(--el-color-danger)',
diff --git a/src/views/CNAS/resourceDemand/standardMaterailRequistion/index.vue b/src/views/CNAS/resourceDemand/standardMaterailRequistion/index.vue
index 029931f..4c76fdd 100644
--- a/src/views/CNAS/resourceDemand/standardMaterailRequistion/index.vue
+++ b/src/views/CNAS/resourceDemand/standardMaterailRequistion/index.vue
@@ -59,9 +59,24 @@
           minWidth: '100'
         },
         {
-          label: '鏁伴噺',
-          prop: 'factoryNum',
+          label: '鍘熷鏁伴噺',
+          prop: 'quantity',
           minWidth: '100'
+        },
+        {
+          label: '鍗曚綅',
+          prop: 'unit',
+          minWidth: '80'
+        },
+        {
+          label: '鏈棰嗙敤鏁伴噺',
+          prop: 'borrowQuantity',
+          minWidth: '110'
+        },
+        {
+          label: '浣欓噺',
+          prop: 'remainingQuantity',
+          minWidth: '80'
         },
         {
           label: '棰嗙敤浜�',
@@ -71,21 +86,6 @@
         {
           label: '棰嗙敤鏃ユ湡',
           prop: 'borrowDate',
-          minWidth: '100'
-        },
-        {
-          label: '褰掕繕浜�',
-          prop: 'returnIntegrity',
-          minWidth: '100'
-        },
-        {
-          label: '褰掕繕鏃ユ湡',
-          prop: 'returnDate',
-          minWidth: '100'
-        },
-        {
-          label: '褰掕繕鏃剁姸鎬�',
-          prop: 'returnIntegrity',
           minWidth: '100'
         }
       ],
@@ -97,9 +97,6 @@
         current: 1
       },
       total: 0,
-      formDia: false,
-      borrowDia: false,
-      returnDia: false,
     }
   },
   mounted() {
diff --git a/src/views/CNAS/resourceDemand/standardMaterial/component/borrowDia.vue b/src/views/CNAS/resourceDemand/standardMaterial/component/borrowDia.vue
index 5c4291d..10a0cda 100644
--- a/src/views/CNAS/resourceDemand/standardMaterial/component/borrowDia.vue
+++ b/src/views/CNAS/resourceDemand/standardMaterial/component/borrowDia.vue
@@ -3,7 +3,7 @@
     <el-dialog v-loading="diaLoading" :close-on-click-modal="false"
                :close-on-press-escape="false"
                v-model="formDia"
-               title="鏍囧噯鐗╄川鍊熺敤"
+               title="鏍囧噯鐗╄川棰嗙敤"
                width="80%" @close="closeBorrowDia">
       <el-form ref="form" :model="form" :rules="rules" label-position="right" label-width="auto">
         <el-row :gutter="20">
@@ -18,12 +18,22 @@
             </el-form-item>
           </el-col>
           <el-col :span="12">
+            <el-form-item label="鍓╀綑鍙鐢�">
+              <el-input v-model="remaining" disabled size="small"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="棰嗙敤鏁伴噺" prop="borrowQuantity">
+              <el-input-number v-model="form.borrowQuantity" :min="1" :max="remaining > 0 ? remaining : 1" size="small" style="width: 100%"></el-input-number>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
             <el-form-item label="瀹屽ソ鎬�" prop="integrity">
               <el-input v-model="form.integrity" clearable size="small"></el-input>
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="鍊熺敤浜�" prop="borrowUser">
+            <el-form-item label="棰嗙敤浜�" prop="borrowUser">
               <el-input v-model="form.borrowUser" clearable size="small"></el-input>
             </el-form-item>
           </el-col>
@@ -33,7 +43,7 @@
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="鍊熷嚭鏃ユ湡" prop="borrowDate">
+            <el-form-item label="棰嗙敤鏃ユ湡" prop="borrowDate">
               <el-date-picker
                 v-model="form.borrowDate"
                 clearable
@@ -44,18 +54,7 @@
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="绾﹀畾褰掕繕鏃ユ湡" prop="borrowReturnDate">
-              <el-date-picker
-                v-model="form.borrowReturnDate"
-                clearable
-                format="YYYY-MM-DD HH:mm:ss" placeholder="閫夋嫨鏃ユ湡"
-                size="small" style="width: 100%"
-                type="datetime" value-format="YYYY-MM-DD HH:mm:ss">
-              </el-date-picker>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="鍊熷嚭浜�" prop="lender">
+            <el-form-item label="鍙戞斁浜�" prop="lender">
               <el-input v-model="form.lender" clearable size="small"></el-input>
             </el-form-item>
           </el-col>
@@ -71,7 +70,8 @@
 
 <script>
 import {
-  borrowSubstance
+  borrowSubstance,
+  getRemainingQuantity
 } from "@/api/cnas/resourceDemand/standardMaterial/standardMaterial";
 
 export default {
@@ -91,17 +91,34 @@
         borrowUser: '',
         phone: '',
         borrowDate: '',
-        borrowReturnDate: '',
         lender: '',
-        substanceId: ''
+        substanceId: '',
+        borrowQuantity: null
       },
+      remaining: 0,
       rules: {
         integrity: [{required: true, message: '璇峰~鍐欏畬濂芥��',trigger: 'blur'}],
-        borrowUser: [{required: true, message: '璇峰~鍐欏�熺敤浜�',trigger: 'blur'}],
+        borrowUser: [{required: true, message: '璇峰~鍐欓鐢ㄤ汉',trigger: 'blur'}],
         phone: [{required: true, message: '璇峰~鍐欒仈绯绘柟寮�',trigger: 'blur'}],
-        borrowDate: [{required: true, message: '璇烽�夋嫨鍊熷嚭鏃ユ湡',trigger: 'change'}],
-        borrowReturnDate: [{required: true, message: '璇烽�夋嫨绾﹀畾褰掕繕鏃ユ湡',trigger: 'change'}],
-        lender: [{required: true, message: '璇烽�夋嫨lender',trigger: 'blur'}],
+        borrowDate: [{required: true, message: '璇烽�夋嫨棰嗙敤鏃ユ湡',trigger: 'change'}],
+        lender: [{required: true, message: '璇峰~鍐欏彂鏀句汉',trigger: 'blur'}],
+        borrowQuantity: [
+          {required: true, message: '璇峰~鍐欓鐢ㄦ暟閲�', trigger: 'blur'},
+          {
+            validator: (rule, value, callback) => {
+              if (value === null || value === undefined || value === '') {
+                callback(new Error('璇峰~鍐欓鐢ㄦ暟閲�'))
+              } else if (Number(value) <= 0) {
+                callback(new Error('棰嗙敤鏁伴噺蹇呴』澶т簬0'))
+              } else if (Number(value) > this.remaining) {
+                callback(new Error('棰嗙敤鏁伴噺涓嶈兘瓒呰繃鍓╀綑鍙鐢ㄦ暟閲�'))
+              } else {
+                callback()
+              }
+            },
+            trigger: 'blur'
+          },
+        ],
       },
     };
   },
@@ -116,6 +133,12 @@
       this.form.name = row.name
       this.form.model = row.model
       this.form.substanceId = row.id
+      this.remaining = 0
+      getRemainingQuantity(row.id).then(res => {
+        if (res.code === 200) {
+          this.remaining = res.data
+        }
+      })
     },
     // 鎻愪氦寮规鏁版嵁
     handleEdit () {
diff --git a/src/views/CNAS/resourceDemand/standardMaterial/component/formDia.vue b/src/views/CNAS/resourceDemand/standardMaterial/component/formDia.vue
index 28f547c..638623c 100644
--- a/src/views/CNAS/resourceDemand/standardMaterial/component/formDia.vue
+++ b/src/views/CNAS/resourceDemand/standardMaterial/component/formDia.vue
@@ -23,6 +23,13 @@
             </el-form-item>
           </el-col>
           <el-col :span="12">
+            <el-form-item label="渚涘簲鍟�" prop="supplier">
+              <el-select v-model="form.supplier" clearable filterable placeholder="璇烽�夋嫨" size="small" style="width: 100%">
+                <el-option v-for="item in supplierOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
             <el-form-item label="鍑哄巶缂栧彿" prop="factoryNum">
               <el-input v-model="form.factoryNum" clearable size="small"></el-input>
             </el-form-item>
@@ -32,14 +39,19 @@
               <el-input v-model="form.manageNum" clearable size="small"></el-input>
             </el-form-item>
           </el-col>
-          <el-col :span="12">
+          <el-col :span="8">
             <el-form-item label="涓嶇‘瀹氬害" prop="uncertainty">
               <el-input v-model="form.uncertainty" clearable size="small"></el-input>
             </el-form-item>
           </el-col>
-          <el-col :span="12">
+          <el-col :span="8">
             <el-form-item label="鏁伴噺" prop="quantity">
               <el-input v-model="form.quantity" clearable size="small"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="鍗曚綅" prop="unit">
+              <el-input v-model="form.unit" clearable size="small"></el-input>
             </el-form-item>
           </el-col>
           <el-col :span="12">
@@ -69,7 +81,7 @@
               <el-input v-model="form.fileNum" clearable size="small"></el-input>
             </el-form-item>
           </el-col>
-          <el-col :span="24">
+          <el-col :span="12">
             <el-form-item label="瀛樻斁浣嶇疆" prop="position">
               <el-input v-model="form.position" clearable size="small"></el-input>
             </el-form-item>
@@ -94,6 +106,7 @@
 import {
   addStandardSubstance
 } from "@/api/cnas/resourceDemand/standardMaterial/standardMaterial";
+import { selectQualifiedSupplierManagementPage } from "@/api/cnas/externalService/supplierManage/supplierManage";
 
 export default {
   name: 'formDia',
@@ -109,10 +122,12 @@
         name: '',
         model: '',
         factoryManufacturer: '',
+        supplier: '',
         factoryNum: '',
         manageNum: '',
         uncertainty: '',
         quantity: '',
+        unit: '',
         acquisitionDate: '',
         effectiveDate: '',
         fileNum: '',
@@ -123,7 +138,6 @@
       rules: {
         name: [{required: true, message: '璇峰~鍐欐爣鍑嗙墿璐ㄥ悕绉�',trigger: 'blur'}],
         model: [{required: true, message: '璇峰~鍐欒鏍煎瀷鍙�',trigger: 'blur'}],
-        factoryManufacturer: [{required: true, message: '璇峰~鍐欑敓浜у巶瀹�',trigger: 'blur'}],
         factoryNum: [{required: true, message: '璇峰~鍐欏嚭鍘傜紪鍙�',trigger: 'blur'}],
         manageNum: [{required: true, message: '璇峰~鍐欑鐞嗙紪鍙�',trigger: 'blur'}],
         uncertainty: [{required: true, message: '璇峰~鍐欎笉纭畾搴�',trigger: 'blur'}],
@@ -134,6 +148,7 @@
         position: [{required: true, message: '璇峰~鍐欏瓨鏀句綅缃�',trigger: 'blur'}],
       },
       operationType: '',
+      supplierOptions: [],
     };
   },
   mounted() {
@@ -143,12 +158,23 @@
   methods: {
     // 鎵撳紑寮规
     openDia (type, row) {
+      this.loadSuppliers()
       this.formDia = true
       this.operationType = type
       if (type !== 'add') {
         this.form = row
       }
     },
+    loadSuppliers () {
+      selectQualifiedSupplierManagementPage({ current: 1, size: 999 }).then(res => {
+        if (res.code === 200) {
+          this.supplierOptions = (res.data.records || []).map(item => ({
+            label: item.supplierName,
+            value: item.supplierName
+          }))
+        }
+      })
+    },
     // 鎻愪氦寮规鏁版嵁
     handleEdit () {
       this.$refs['form'].validate((valid) => {
diff --git a/src/views/CNAS/resourceDemand/standardMaterial/component/returnDia.vue b/src/views/CNAS/resourceDemand/standardMaterial/component/returnDia.vue
deleted file mode 100644
index 5cf9ba8..0000000
--- a/src/views/CNAS/resourceDemand/standardMaterial/component/returnDia.vue
+++ /dev/null
@@ -1,125 +0,0 @@
-<template>
-  <div>
-    <el-dialog v-loading="diaLoading" :close-on-click-modal="false"
-               :close-on-press-escape="false"
-               v-model="formDia"
-               title="鏍囧噯鐗╄川褰掕繕"
-               width="80%" @close="closeReturnDia">
-      <el-form ref="form" :model="form" :rules="rules" label-position="right" label-width="auto">
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item label="鏍囧噯鐗╄川鍚嶇О" prop="name">
-              <el-input v-model="form.name" disabled size="small"></el-input>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="鍨嬪彿" prop="model">
-              <el-input v-model="form.model" disabled size="small"></el-input>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="褰掕繕鏃ユ湡" prop="returnDate">
-              <el-date-picker
-                v-model="form.returnDate"
-                clearable
-                format="YYYY-MM-DD HH:mm:ss" placeholder="閫夋嫨鏃ユ湡"
-                size="small" style="width: 100%"
-                type="datetime" value-format="YYYY-MM-DD HH:mm:ss">
-              </el-date-picker>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="褰掕繕浜�" prop="returnedPerson">
-              <el-input v-model="form.returnedPerson" clearable size="small"></el-input>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="瀹屽ソ鎬�" prop="returnIntegrity">
-              <el-input v-model="form.returnIntegrity" clearable size="small"></el-input>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="妫�鏌ヤ汉" prop="rummager">
-              <el-input v-model="form.rummager" clearable size="small"></el-input>
-            </el-form-item>
-          </el-col>
-        </el-row>
-      </el-form>
-      <template #footer><span  class="dialog-footer">
-        <el-button @click="closeReturnDia">鍙� 娑�</el-button>
-        <el-button :loading="loading" type="primary" @click="handleEdit">鎻� 浜�</el-button>
-      </span></template>
-    </el-dialog>
-  </div>
-</template>
-
-<script>
-import {
-  returnSubstance
-} from "@/api/cnas/resourceDemand/standardMaterial/standardMaterial";
-
-export default {
-  name: 'returnDia',
-  // import 寮曞叆鐨勭粍浠堕渶瑕佹敞鍏ュ埌瀵硅薄涓墠鑳戒娇鐢�
-  components: {},
-  data() {
-    // 杩欓噷瀛樻斁鏁版嵁
-    return {
-      formDia: false,
-      diaLoading: false,
-      loading: false,
-      form: {
-        name: '',
-        model: '',
-        returnedPerson: '',
-        returnDate: '',
-        returnIntegrity: '',
-        rummager: '',
-      },
-      rules: {
-      },
-    };
-  },
-  mounted() {
-
-  },
-  // 鏂规硶闆嗗悎
-  methods: {
-    // 鎵撳紑寮规
-    openDia (row) {
-      this.formDia = true
-      this.form.name = row.name
-      this.form.model = row.model
-      this.form.substanceId = row.id
-    },
-    // 鎻愪氦寮规鏁版嵁
-    handleEdit () {
-      this.$refs['form'].validate((valid) => {
-        if (valid) {
-          this.loading = true
-          this.form.status = '1';
-          returnSubstance(this.form).then(res => {
-            this.loading = false
-            if (res.code === 200) {
-              this.$message.success('鎿嶄綔鎴愬姛')
-              this.closeReturnDia()
-            }
-          })
-          let internalReport = this.HaveJson(this.form)
-
-        } else {
-          return false;
-        }
-      });
-    },
-    closeReturnDia () {
-      this.$refs.form.resetFields();
-      this.formDia = false
-      this.$emit('closeReturnDia')
-    },
-  }
-};
-</script>
-
-<style scoped>
-</style>
diff --git a/src/views/CNAS/resourceDemand/standardMaterial/index.vue b/src/views/CNAS/resourceDemand/standardMaterial/index.vue
index 1c76435..faee58c 100644
--- a/src/views/CNAS/resourceDemand/standardMaterial/index.vue
+++ b/src/views/CNAS/resourceDemand/standardMaterial/index.vue
@@ -20,7 +20,6 @@
       <div>
         <el-button size="small" @click="exportFun">瀵� 鍑�</el-button>
         <el-button size="small" type="primary" @click="importVisible = true">瀵� 鍏�</el-button>
-        <el-button size="small" type="primary" @click="openFormDia('add')">鏂� 澧�</el-button>
       </div>
     </div>
     <div class="table">
@@ -28,8 +27,7 @@
         :page="page" :tableLoading="tableLoading"></lims-table>
     </div>
     <form-dia v-if="formDia" ref="formDia" @closeYearDia="closeYearDia"></form-dia>
-    <borrow-dia v-if="borrowDia" ref="borrowDia" @closeYearDia="closeBorrowDia"></borrow-dia>
-    <return-dia v-if="returnDia" ref="returnDia" @closeYearDia="closeReturnDia"></return-dia>
+    <borrow-dia v-if="borrowDia" ref="borrowDia" @closeBorrowDia="closeBorrowDia"></borrow-dia>
     <el-dialog title="鏍囧噯鐗╄川瀵煎叆" v-model="importVisible" width="500px" @closed="resetImport">
       <div style="margin-bottom: 12px;">
         璇蜂娇鐢ㄦ爣鍑嗙墿璐ㄥ鍏ユā鏉垮~鍐欐暟鎹紝鍗曚釜鏂囦欢涓嶈秴杩�10MB銆�
@@ -61,14 +59,12 @@
 } from '@/api/cnas/resourceDemand/standardMaterial/standardMaterial'
 import FormDia from '../standardMaterial/component/formDia.vue';
 import BorrowDia from '../standardMaterial/component/borrowDia.vue';
-import ReturnDia from '../standardMaterial/component/returnDia.vue';
 export default {
   name: 'StandardMaterial',
   components: {
     limsTable,
     FormDia,
-    BorrowDia,
-    ReturnDia
+    BorrowDia
   },
   data() {
     return {
@@ -93,6 +89,11 @@
           minWidth: '100'
         },
         {
+          label: '渚涘簲鍟�',
+          prop: 'supplier',
+          minWidth: '120'
+        },
+        {
           label: '鍑哄巶缂栧彿',
           prop: 'factoryNum',
           minWidth: '100'
@@ -111,6 +112,11 @@
           label: '鏁伴噺',
           prop: 'quantity',
           minWidth: '100'
+        },
+        {
+          label: '鍗曚綅',
+          prop: 'unit',
+          minWidth: '80'
         },
         {
           label: '璐疆鏃ユ湡',
@@ -156,18 +162,11 @@
               },
             },
             {
-              name: '鍊熺敤',
+              name: '棰嗙敤',
               type: 'text',
               clickFun: (row) => {
                 this.borrow(row);
               },
-            },
-            {
-              name: '褰掕繕',
-              type: 'text',
-              clickFun: (row) => {
-                this.return(row);
-              }
             },
             {
               name: '鍒犻櫎',
@@ -190,7 +189,6 @@
       total: 0,
       formDia: false,
       borrowDia: false,
-      returnDia: false,
       importVisible: false,
       importing: false,
       importFiles: [],
@@ -316,17 +314,6 @@
     },
     closeBorrowDia() {
       this.borrowDia = false
-      this.searchList()
-    },
-    // 褰掕繕
-    return(row) {
-      this.returnDia = true
-      this.$nextTick(() => {
-        this.$refs.returnDia.openDia(row)
-      })
-    },
-    closeReturnDia() {
-      this.returnDia = false
       this.searchList()
     },
     // 閲嶇疆鏌ヨ鏉′欢
diff --git a/src/views/CNAS/resourceDemand/standardMaterialAccept/component/AddRecord.vue b/src/views/CNAS/resourceDemand/standardMaterialAccept/component/AddRecord.vue
index b89e152..8cdb9d3 100644
--- a/src/views/CNAS/resourceDemand/standardMaterialAccept/component/AddRecord.vue
+++ b/src/views/CNAS/resourceDemand/standardMaterialAccept/component/AddRecord.vue
@@ -1,107 +1,190 @@
 <template>
   <el-dialog v-model="dialogVisible" title="鏍囧噯鐗╄川楠屾敹璁板綍" width="72%" v-loading="loading">
-    <el-form :model="acceptance" ref="acceptance" label-width="140px" size="small">
-      <el-col :span="12">
-        <el-form-item label="鍘傚浠h〃" prop="producer">
-          <el-input v-model="acceptance.producer"></el-input>
-        </el-form-item>
-      </el-col>
-      <el-col :span="12">
-        <el-form-item label="鐩稿叧闄勪欢" prop="file">
-          <div class="table-between">
-            <el-input v-model="acceptance.file" style="width: 80%;" disabled></el-input>
-            <el-upload
-              ref="upload"
-              style="float: right;"
-              :headers="uploadHeader"
-              :action="action"
-              :show-file-list="false"
-              :on-success="onSuccess"
+    <el-form ref="form" :model="form" :rules="rules" label-width="140px" size="small">
+      <div class="section-title">鏍囧噯鐗╄川淇℃伅</div>
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="鏍囧噯鐗╄川鍚嶇О" prop="substance.name">
+            <el-input v-model="form.substance.name" clearable></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="瑙勬牸鍨嬪彿" prop="substance.model">
+            <el-input v-model="form.substance.model" clearable></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="鐢熶骇鍘傚" prop="substance.factoryManufacturer">
+            <el-input v-model="form.substance.factoryManufacturer" clearable></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="渚涘簲鍟�" prop="substance.supplier">
+            <el-select v-model="form.substance.supplier" clearable filterable placeholder="璇烽�夋嫨" style="width: 100%" @change="onSupplierChange">
+              <el-option v-for="item in supplierOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="鍑哄巶缂栧彿" prop="substance.factoryNum">
+            <el-input v-model="form.substance.factoryNum" clearable></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="绠$悊缂栧彿" prop="substance.manageNum">
+            <el-input v-model="form.substance.manageNum" clearable></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="涓嶇‘瀹氬害" prop="substance.uncertainty">
+            <el-input v-model="form.substance.uncertainty" clearable></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="鏁伴噺" prop="substance.quantity">
+            <el-input v-model="form.substance.quantity" clearable></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="鍗曚綅" prop="substance.unit">
+            <el-input v-model="form.substance.unit" clearable></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="璐疆鏃ユ湡" prop="substance.acquisitionDate">
+            <el-date-picker
+              v-model="form.substance.acquisitionDate"
+              clearable
+              type="datetime"
+              format="YYYY-MM-DD HH:mm:ss"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              placeholder="閫夋嫨鏃ユ湡"
+              style="width: 100%">
+            </el-date-picker>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="鏈夋晥鏈�" prop="substance.effectiveDate">
+            <el-date-picker
+              v-model="form.substance.effectiveDate"
+              clearable
+              type="datetime"
+              format="YYYY-MM-DD HH:mm:ss"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              placeholder="閫夋嫨鏃ユ湡"
+              style="width: 100%">
+            </el-date-picker>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="妗f缂栧彿" prop="substance.fileNum">
+            <el-input v-model="form.substance.fileNum" clearable></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="瀛樻斁浣嶇疆" prop="substance.position">
+            <el-input v-model="form.substance.position" clearable></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="澶囨敞" prop="substance.remark">
+            <el-input v-model="form.substance.remark" clearable></el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <div class="section-title">楠屾敹淇℃伅</div>
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="鍒拌揣鏃ユ湡" prop="acceptance.arriveDate">
+            <el-date-picker
+              v-model="form.acceptance.arriveDate"
+              type="date"
+              placeholder="閫夋嫨鏃ユ湡"
+              style="width: 100%"
+              value-format="YYYY-MM-DD"
             >
-              <el-button type="primary">
-                闄勪欢涓婁紶
-              </el-button>
-            </el-upload>
-          </div>
-        </el-form-item>
-      </el-col>
-      <el-col :span="12">
-        <el-form-item label="鎺ユ敹浜�" prop="recipient">
-          <el-input v-model="acceptance.recipient"></el-input>
-        </el-form-item>
-      </el-col>
-      <el-col :span="12">
-        <el-form-item label="鎺ュ彈绛惧瓧" prop="signature">
-          <el-input v-model="acceptance.signature"></el-input>
-        </el-form-item>
-      </el-col>
-      <el-col :span="12">
-        <el-form-item label="楠屾敹鎯呭喌" prop="situation">
-          <el-input v-model="acceptance.situation"></el-input>
-        </el-form-item>
-      </el-col>
-      <el-col :span="12">
-        <el-form-item label="瀹夎璋冭瘯鎯呭喌" prop="installation">
-          <el-input v-model="acceptance.installation"></el-input>
-        </el-form-item>
-      </el-col>
-      <el-col :span="12">
-        <el-form-item label="娓呭崟" prop="substanceId"
-                      :rule="[{ required: true, message: '璇烽�夋嫨娓呭崟', trigger: 'change' }]">
-          <el-select v-model="acceptance.substanceId" placeholder="璇烽�夋嫨" style="width: 100%">
-            <el-option
-              v-for="item in options"
-              :key="item.value"
-              :label="item.name"
-              :value="item.id">
-            </el-option>
-          </el-select>
-        </el-form-item>
-      </el-col>
-      <el-col :span="12">
-        <el-form-item label="鍒拌揣鏃ユ湡" prop="arriveDate">
-          <el-date-picker
-            v-model="acceptance.arriveDate"
-            align="right"
-            type="date"
-            placeholder="閫夋嫨鏃ユ湡"
-            style="width: 100%"
-            value-format="YYYY-MM-DD"
-          >
-          </el-date-picker>
-        </el-form-item>
-      </el-col>
-      <el-col :span="12">
-        <el-form-item label="缁翠慨鍗曚綅" prop="maintenanceUnit">
-          <el-input v-model="acceptance.maintenanceUnit"></el-input>
-        </el-form-item>
-      </el-col>
-      <el-col :span="24">
-        <el-form-item label="澶囦欢纭">
-          <el-button type="primary" style="float: right;" @click="addSpareTable">澧炲姞琛�</el-button>
-        </el-form-item>
-      </el-col>
-      <el-col :span="24">
-        <lims-table :tableData="list" :column="spareColumns" height="300">
-          <template #name="{ row, index }">
-            <el-input v-if="row.isEdit" size="small" v-model="row.name"></el-input>
-            <span v-else>{{ row.name }}</span>
-          </template>
-          <template #number="{ row, index }">
-            <el-input v-if="row.isEdit" size="small" v-model="row.number"></el-input>
-            <span v-else>{{ row.number }}</span>
-          </template>
-          <template #action="{ row, index }">
-            <div v-if="row.isEdit">
-              <el-button link type="primary" @click="save(index)">淇濆瓨</el-button>
-              <el-button link type="primary">鍙栨秷</el-button>
+            </el-date-picker>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="鍘傚浠h〃" prop="acceptance.producer">
+            <el-input v-model="form.acceptance.producer"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="鎺ユ敹浜�" prop="acceptance.recipient">
+            <el-input v-model="form.acceptance.recipient"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="鎺ュ彈绛惧瓧" prop="acceptance.signature">
+            <el-input v-model="form.acceptance.signature"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="楠屾敹鎯呭喌" prop="acceptance.situation">
+            <el-input v-model="form.acceptance.situation"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="瀹夎璋冭瘯鎯呭喌" prop="acceptance.installation">
+            <el-input v-model="form.acceptance.installation"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="缁翠慨鍗曚綅" prop="acceptance.maintenanceUnit">
+            <el-input v-model="form.acceptance.maintenanceUnit"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="鐩稿叧闄勪欢" prop="acceptance.file">
+            <div class="table-between">
+              <el-input v-model="form.acceptance.file" style="width: 80%;" disabled></el-input>
+              <el-upload
+                ref="upload"
+                style="float: right;"
+                :headers="uploadHeader"
+                :action="action"
+                :show-file-list="false"
+                :on-success="onSuccess"
+              >
+                <el-button type="primary">
+                  闄勪欢涓婁紶
+                </el-button>
+              </el-upload>
             </div>
-            <div v-else>
-              <el-button link type="primary" @click="edit(index)">淇敼</el-button>
-            </div>
-          </template>
-        </lims-table>
-      </el-col>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <div class="section-title">澶囦欢纭</div>
+      <el-row :gutter="20">
+        <el-col :span="24" class="spare-toolbar">
+          <el-button type="primary" size="small" @click="addSpareTable">澧炲姞琛�</el-button>
+        </el-col>
+        <el-col :span="24">
+          <lims-table :tableData="list" :column="spareColumns" height="300">
+            <template #name="{ row, index }">
+              <el-input v-if="row.isEdit" size="small" v-model="row.name"></el-input>
+              <span v-else>{{ row.name }}</span>
+            </template>
+            <template #number="{ row, index }">
+              <el-input v-if="row.isEdit" size="small" v-model="row.number"></el-input>
+              <span v-else>{{ row.number }}</span>
+            </template>
+            <template #action="{ row, index }">
+              <div v-if="row.isEdit">
+                <el-button link type="primary" @click="save(index)">淇濆瓨</el-button>
+                <el-button link type="primary">鍙栨秷</el-button>
+              </div>
+              <div v-else>
+                <el-button link type="primary" @click="edit(index)">淇敼</el-button>
+              </div>
+            </template>
+          </lims-table>
+        </el-col>
+      </el-row>
     </el-form>
     <template #footer><span  class="dialog-footer">
       <el-button @click="closeDialog">鍙� 娑�</el-button>
@@ -114,9 +197,45 @@
 import limsTable from "@/components/Table/lims-table.vue";
 import {
   addAcceptance, getAcceptanceDetails,
-  getStandardSubstanceAll,
   updateAcceptance
 } from "@/api/cnas/resourceDemand/standardMaterialAccept/standardMaterialAccept";
+import { selectQualifiedSupplierManagementPage } from "@/api/cnas/externalService/supplierManage/supplierManage";
+
+function defaultSubstance() {
+  return {
+    id: undefined,
+    name: undefined,
+    model: undefined,
+    factoryManufacturer: undefined,
+    supplier: undefined,
+    factoryNum: undefined,
+    manageNum: undefined,
+    uncertainty: undefined,
+    quantity: undefined,
+    unit: undefined,
+    acquisitionDate: undefined,
+    effectiveDate: undefined,
+    fileNum: undefined,
+    position: undefined,
+    remark: undefined,
+  }
+}
+
+function defaultAcceptance() {
+  return {
+    id: undefined,
+    producer: undefined, // 鍘傚浠h〃
+    file: undefined,   // 鐩稿叧闄勪欢
+    recipient: undefined,   // 鎺ユ敹浜�
+    signature: undefined,   // 鎺ュ彈绛惧瓧
+    situation: undefined,   // 楠屾敹鎯呭喌
+    installation: undefined,    // 瀹夎璋冭瘯鎯呭喌
+    substanceId: undefined, // 娓呭崟
+    arriveDate: undefined,   // 鍒拌揣鏃ユ湡
+    maintenanceUnit: undefined,   // 缁翠慨鍗曚綅
+  }
+}
+
 export default {
   components: {
     limsTable,
@@ -126,19 +245,23 @@
       dialogVisible: false,
       loading: false,
       submitLoading: false,
-      acceptance: {
-        id: undefined,
-        producer: undefined, // 鍘傚浠h〃
-        file: undefined,   // 鐩稿叧闄勪欢
-        recipient: undefined,   // 鎺ユ敹浜�
-        signature: undefined,   // 鎺ュ彈绛惧瓧
-        situation: undefined,   // 楠屾敹鎯呭喌
-        installation: undefined,    // 瀹夎璋冭瘯鎯呭喌
-        substanceId: undefined, // 娓呭崟
-        arriveDate: undefined,   // 鍒拌揣鏃ユ湡
-        maintenanceUnit: undefined,   // 缁翠慨鍗曚綅
-        list: [],
+      form: {
+        substance: defaultSubstance(),
+        acceptance: defaultAcceptance(),
       },
+      rules: {
+        'substance.name': [{required: true, message: '璇峰~鍐欐爣鍑嗙墿璐ㄥ悕绉�', trigger: 'blur'}],
+        'substance.model': [{required: true, message: '璇峰~鍐欒鏍煎瀷鍙�', trigger: 'blur'}],
+        'substance.factoryNum': [{required: true, message: '璇峰~鍐欏嚭鍘傜紪鍙�', trigger: 'blur'}],
+        'substance.manageNum': [{required: true, message: '璇峰~鍐欑鐞嗙紪鍙�', trigger: 'blur'}],
+        'substance.uncertainty': [{required: true, message: '璇峰~鍐欎笉纭畾搴�', trigger: 'blur'}],
+        'substance.quantity': [{required: true, message: '璇峰~鍐欐暟閲�', trigger: 'blur'}],
+        'substance.acquisitionDate': [{required: true, message: '璇烽�夋嫨璐疆鏃ユ湡', trigger: 'change'}],
+        'substance.effectiveDate': [{required: true, message: '璇烽�夋嫨鏈夋晥鏈�', trigger: 'change'}],
+        'substance.fileNum': [{required: true, message: '璇峰~鍐欐。妗堢紪鍙�', trigger: 'blur'}],
+        'substance.position': [{required: true, message: '璇峰~鍐欏瓨鏀句綅缃�', trigger: 'blur'}],
+      },
+      supplierOptions: [],
       list: [],
       spareColumns: [
         {
@@ -161,46 +284,52 @@
           width: 150
         }
       ],
-      options: []
     }
-  },
-  mounted() {
-    this.getStand()
   },
   methods: {
     openDialog(id) {
-      if(id) {
+      this.loadSuppliers()
+      if (id) {
         this.getDetail(id)
       } else {
         this.clearForm()
       }
       this.dialogVisible = true
     },
+    loadSuppliers() {
+      selectQualifiedSupplierManagementPage({ current: 1, size: 999 }).then(res => {
+        if (res.code === 200) {
+          this.supplierOptions = (res.data.records || []).map(item => ({
+            label: item.supplierName,
+            value: item.supplierName
+          }))
+        }
+      })
+    },
+    onSupplierChange(val) {
+      this.form.acceptance.producer = val || ''
+    },
     getDetail(id) {
       this.loading = true
-      getAcceptanceDetails({id: id}).then(res => {
-        this.acceptance = res.data.acceptance
-        this.list = res.data.list
+      getAcceptanceDetails({ id: id }).then(res => {
+        this.form.acceptance = res.data.acceptance || defaultAcceptance()
+        this.form.substance = res.data.substance || defaultSubstance()
+        this.list = res.data.list || []
         this.loading = false
       }).catch(err => {
         this.loading = false
       })
     },
-    getStand() {
-      getStandardSubstanceAll().then(res => {
-        this.options = res.data
-      })
-    },
     addSpareTable() {
       this.list.push({
-        acceptanceId: this.acceptance.id,
+        acceptanceId: this.form.acceptance.id,
         name: '',
         number: '',
         isEdit: true
       })
     },
     onSuccess(response) {
-      this.acceptance.file = response.data
+      this.form.acceptance.file = response.data
     },
     save(index) {
       this.list[index]['isEdit'] = false
@@ -209,39 +338,41 @@
       this.list[index]['isEdit'] = true
     },
     clearForm() {
+      this.form.substance = defaultSubstance()
+      this.form.acceptance = defaultAcceptance()
       this.list = []
-      this.resetForm('acceptance')
+      this.$nextTick(() => {
+        if (this.$refs.form) {
+          this.$refs.form.clearValidate()
+        }
+      })
     },
     closeDialog() {
       this.clearForm()
-      this.resetForm('acceptance')
       this.dialogVisible = false
     },
     submit() {
-      if (!this.acceptance.substanceId) {
-        this.$message.warning('璇烽�夋嫨娓呭崟')
-        return
-      }
-      this.acceptance.list = this.HaveJson(this.list)
-      this.submitLoading = true
-      if(this.acceptance.id) {
-        updateAcceptance({acceptance: this.acceptance, list: this.list}).then(res => {
-          this.$message.success('缂栬緫鎴愬姛')
+      this.$refs.form.validate((valid) => {
+        if (!valid) {
+          return false
+        }
+        const isEdit = !!this.form.acceptance.id
+        this.submitLoading = true
+        const payload = {
+          acceptance: this.form.acceptance,
+          substance: this.form.substance,
+          list: this.HaveJson(this.list)
+        }
+        const request = isEdit ? updateAcceptance : addAcceptance
+        request(payload).then(res => {
           this.submitLoading = false
+          this.$message.success(isEdit ? '缂栬緫鎴愬姛' : '鏂板鎴愬姛')
           this.closeDialog()
           this.$emit('submit')
         }).catch((e) => {
           this.submitLoading = false
         })
-      } else {
-        addAcceptance({acceptance: this.acceptance, list: this.list}).then(res => {
-          this.$message.success('鏂板鎴愬姛')
-          this.closeDialog()
-          this.$emit('submit')
-        }).catch((e) => {
-          this.submitLoading = false
-        })
-      }
+      })
     },
   },
   computed: {
@@ -258,4 +389,34 @@
   height: 580px;
   overflow-y: auto;
 }
+
+.section-title {
+  display: flex;
+  align-items: center;
+  font-size: 14px;
+  font-weight: 600;
+  color: var(--lims-text);
+  padding-bottom: 8px;
+  border-bottom: 1px solid var(--lims-divider);
+  margin: 8px 0 16px;
+}
+
+.section-title:not(:first-child) {
+  margin-top: 24px;
+}
+
+.section-title::before {
+  content: '';
+  display: inline-block;
+  width: 3px;
+  height: 14px;
+  border-radius: 2px;
+  background: var(--el-color-primary);
+  margin-right: 8px;
+}
+
+.spare-toolbar {
+  text-align: right;
+  margin-bottom: 8px;
+}
 </style>
diff --git a/src/views/CNAS/resourceDemand/standardMaterialAccept/index.vue b/src/views/CNAS/resourceDemand/standardMaterialAccept/index.vue
index 2626247..79bddd0 100644
--- a/src/views/CNAS/resourceDemand/standardMaterialAccept/index.vue
+++ b/src/views/CNAS/resourceDemand/standardMaterialAccept/index.vue
@@ -48,7 +48,7 @@
       addRecordRef: false,
       columns: [
         {
-          label: "鍑哄満缂栧彿",
+          label: "鍑哄巶缂栧彿",
           prop: "factoryNum"
         },
         {
@@ -60,7 +60,7 @@
           prop: "factoryManufacturer"
         },
         {
-          label: "鏂囨。缂栧彿",
+          label: "妗f缂栧彿",
           prop: "fileNum"
         },
         {
@@ -85,13 +85,17 @@
         // },
         {
           label: "鎻愪氦鏃ユ湡",
-          prop: "acquisitionDate"
+          prop: "createTime"
         },
         {
           label: "鏁伴噺",
           prop: "quantity"
         },
         {
+          label: "鍗曚綅",
+          prop: "unit"
+        },
+        {
           fixed: "right",
           label: "鎿嶄綔",
           align: "center",
diff --git a/src/views/CNAS/systemManagement/customerSatisfaction/components/formDialog.vue b/src/views/CNAS/systemManagement/customerSatisfaction/components/formDialog.vue
index a0d404a..8ad497f 100644
--- a/src/views/CNAS/systemManagement/customerSatisfaction/components/formDialog.vue
+++ b/src/views/CNAS/systemManagement/customerSatisfaction/components/formDialog.vue
@@ -22,11 +22,19 @@
         </tr>
         <tr>
           <td class="td-title">
-            <p>濮撳悕锛�</p>
+            <p>瀹㈡埛锛�</p>
           </td>
           <td class="td-info">
-            <el-input v-model="form.userName" placeholder="璇疯緭鍏ュ唴瀹�" size="small">
-            </el-input>
+            <el-select v-model="form.orderId" filterable clearable size="small" style="width: 100%"
+              popper-class="customer-select-popper" placeholder="璇烽�夋嫨瀹㈡埛锛堝凡瀹屾垚濮旀墭鍗曪級" @change="onCustomerChange">
+              <el-option v-for="opt in customerOptions" :key="opt.id" :label="opt.company" :value="opt.id">
+                <div class="customer-card">
+                  <div>瀹㈡埛锛歿{ opt.company }}</div>
+                  <div class="customer-card-sub">濮旀墭鍗曠紪鍙凤細{{ opt.entrustCode || '--' }}</div>
+                  <div class="customer-card-sub">鏍峰搧鍚嶇О锛歿{ opt.sampleName || '--' }}</div>
+                </div>
+              </el-option>
+            </el-select>
           </td>
           <td class="td-title">
             <p>閮ㄩ棬锛�</p>
@@ -142,6 +150,7 @@
   addClientSatisfaction,
   updateClientSatisfaction
 } from '@/api/cnas/systemManagement/customerSatisfaction.js'
+import { selectCompletedOrders } from '@/api/business/productOrder'
 export default {
   name: 'formDialog',
   // import 寮曞叆鐨勭粍浠堕渶瑕佹敞鍏ュ埌瀵硅薄涓墠鑳戒娇鐢�
@@ -157,6 +166,7 @@
         department: '',
         contactNumber: '',
         serviceAttitude: '',
+        serviceAttitudeSuggestion: '',
         technicalCompetence: '',
         technicalCompetenceSuggestion: '',
         inspectionWork: '',
@@ -165,7 +175,12 @@
         reasonableFeesSuggestion: '',
         remark: '',
         clientSatisfactionId: '',
+        orderId: '',
+        entrustCode: '',
+        sampleName: ''
       },
+      // 瀹㈡埛涓嬫媺閫夐」锛氬彧鍖呭惈"宸蹭笅杩囧鎵樺崟涓斿凡瀹屾垚"鐨勬暟鎹�
+      customerOptions: [],
       operationType: '',
       editLoad: false,
 
@@ -173,14 +188,89 @@
   },
   // 鏂规硶闆嗗悎
   methods: {
-    openDia(type, row) {
+    async openDia(type, row) {
       this.formDia = true;
       this.operationType = type
+      await this.loadCustomerOptions()
       if (this.operationType === 'edit') {
         this.form = { ...row }
+        // 鑰佹暟鎹病鏈夊叧鑱斿鎵樺崟锛氬悎鎴愪竴涓�夐」鎶婂師鏉ョ殑鍊煎洖鏄惧嚭鏉ワ紝涓嶅己鍒堕噸閫�
+        if (row.orderId == null && row.userName) {
+          this.customerOptions.unshift({
+            id: 0,
+            company: row.userName,
+            entrustCode: row.entrustCode || '--',
+            sampleName: row.sampleName || '--'
+          })
+          this.form.orderId = 0
+        } else if (row.orderId && !this.customerOptions.some(o => o.id === row.orderId)) {
+          // 鍏宠仈鐨勫鎵樺崟宸蹭笉鏄�"宸插畬鎴�"鐘舵�侊紝鐢ㄨ褰曢噷鐨勪俊鎭厹搴曞洖鏄�
+          this.customerOptions.unshift({
+            id: row.orderId,
+            company: row.userName || '鏈煡瀹㈡埛',
+            entrustCode: row.entrustCode || '--',
+            sampleName: row.sampleName || '--'
+          })
+        }
+      } else {
+        this.resetForm()
       }
     },
+    resetForm() {
+      this.form = {
+        unitName: '',
+        fillDate: '',
+        userName: '',
+        department: '',
+        contactNumber: '',
+        serviceAttitude: '',
+        serviceAttitudeSuggestion: '',
+        technicalCompetence: '',
+        technicalCompetenceSuggestion: '',
+        inspectionWork: '',
+        inspectionWorkSuggestion: '',
+        reasonableFees: '',
+        reasonableFeesSuggestion: '',
+        remark: '',
+        clientSatisfactionId: '',
+        orderId: '',
+        entrustCode: '',
+        sampleName: ''
+      }
+    },
+    // 涓嬫媺鏁版嵁锛氬悗绔彧杩斿洖"宸叉楠�"鐨勫鎵樺崟锛坰tate=4锛�
+    loadCustomerOptions() {
+      this.customerOptions = []
+      return selectCompletedOrders().then(res => {
+        if (res.code === 200 && res.data) {
+          this.customerOptions = res.data.map(o => ({
+            id: o.id,
+            company: o.company,
+            entrustCode: o.entrustCode,
+            sampleName: o.sampleView
+          }))
+        }
+      }).catch(err => {
+        console.log('err---', err)
+      })
+    },
+    // 閫変腑瀹㈡埛鍚庯細鍥炲~瀹㈡埛鍚嶅苟鑷姩甯﹀嚭鍗曚綅鍚嶇О銆佸鎵樺崟缂栧彿銆佹牱鍝佸悕绉�
+    onCustomerChange(val) {
+      const opt = this.customerOptions.find(o => o.id === val)
+      if (!opt || opt.id === 0) {
+        return
+      }
+      this.form.userName = opt.company
+      this.form.entrustCode = opt.entrustCode
+      this.form.sampleName = opt.sampleName
+      this.form.unitName = opt.company
+    },
     handleEdit() {
+      const orderIdEmpty = this.form.orderId === '' || this.form.orderId === null || this.form.orderId === undefined
+      if (orderIdEmpty) {
+        this.$message.warning('璇烽�夋嫨瀹㈡埛')
+        return
+      }
       if (!this.form.unitName) {
         this.$message.warning('璇峰~鍐欏崟浣嶅悕绉�')
         return
@@ -190,8 +280,13 @@
         return
       }
       this.editLoad = true
+      const payload = { ...this.form }
+      // 鑰佹暟鎹悎鎴愮殑鍥炴樉閫夐」锛坕d=0锛変笉绠楃湡瀹炲叧鑱旓紝淇濆瓨鏃朵粛淇濇寔鏈叧鑱�
+      if (payload.orderId === 0) {
+        payload.orderId = null
+      }
       if (this.operationType === 'add') {
-        addClientSatisfaction(this.form).then(res => {
+        addClientSatisfaction(payload).then(res => {
           this.editLoad = false
           this.$message.success('鎻愪氦鎴愬姛')
           this.closeFormDia()
@@ -200,7 +295,7 @@
           this.editLoad = false
         })
       } else {
-        updateClientSatisfaction(this.form).then(res => {
+        updateClientSatisfaction(payload).then(res => {
           this.editLoad = false
           this.$message.success('鎻愪氦鎴愬姛')
           this.closeFormDia()
@@ -251,4 +346,18 @@
   word-wrap: break-word;
   white-space: normal;
 }
+
+.customer-card-sub {
+  color: #909399;
+  font-size: 12px;
+}
+</style>
+
+<style>
+/* 涓嬫媺鍗$墖涓夎闇�瑕佽嚜閫傚簲楂樺害锛屽脊灞� teleport 鍒� body锛岀敤鑷畾涔� popper-class 鍛戒腑 */
+.customer-select-popper .el-select-dropdown__item {
+  height: auto;
+  line-height: 1.7;
+  padding: 6px 12px;
+}
 </style>
diff --git a/src/views/CNAS/systemManagement/customerSatisfaction/index.vue b/src/views/CNAS/systemManagement/customerSatisfaction/index.vue
index 64cc58c..f891e46 100644
--- a/src/views/CNAS/systemManagement/customerSatisfaction/index.vue
+++ b/src/views/CNAS/systemManagement/customerSatisfaction/index.vue
@@ -50,6 +50,9 @@
         style="height: 90vh;overflow-y: auto;" />
     </el-dialog>
     <FormDialog v-if="formDialog" ref="formDialog" @closeFormDia="closeFormDia"></FormDialog>
+    <lims-attachment ref="attachmentDialog" title="婊℃剰搴﹂檮浠�" name-field="unitName" id-param="clientSatisfactionId"
+      upload-url="/clientSatisfaction/uploadAttachment" select-url="/clientSatisfaction/selectAttachments"
+      delete-url="/clientSatisfaction/deleteAttachment" />
   </div>
 </template>
 
@@ -57,6 +60,7 @@
 import limsTable from "@/components/Table/lims-table.vue";
 import FormDialog from './components/formDialog.vue';
 import filePreview from '@/components/Preview/filePreview.vue'
+import limsAttachment from '@/components/Attachment/lims-attachment.vue'
 import {
   pageClientSatisfaction,
   pageAnalyseFile,
@@ -67,7 +71,7 @@
 export default {
   name: 'CustomerSatisfaction',
   // import 寮曞叆鐨勭粍浠堕渶瑕佹敞鍏ュ埌瀵硅薄涓墠鑳戒娇鐢�
-  components: { filePreview, FormDialog, limsTable },
+  components: { filePreview, FormDialog, limsTable, limsAttachment },
   data() {
     // 杩欓噷瀛樻斁鏁版嵁
     return {
@@ -90,8 +94,18 @@
           minWidth: '100'
         },
         {
-          label: '濮撳悕',
+          label: '瀹㈡埛',
           prop: 'userName',
+          minWidth: '100'
+        },
+        {
+          label: '濮旀墭鍗曠紪鍙�',
+          prop: 'entrustCode',
+          minWidth: '100'
+        },
+        {
+          label: '鏍峰搧鍚嶇О',
+          prop: 'sampleName',
           minWidth: '100'
         },
         {
@@ -122,6 +136,13 @@
               },
             },
             {
+              name: '闄勪欢',
+              type: 'text',
+              clickFun: (row) => {
+                this.$refs.attachmentDialog.open(row);
+              },
+            },
+            {
               name: '鍒犻櫎',
               type: 'text',
               color: 'var(--el-color-danger)',

--
Gitblit v1.9.3