From 92f2177d06cc67b2055bac0331a85780a086df0b Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期日, 20 九月 2026 19:51:47 +0800
Subject: [PATCH] feat(order): 优化订单管理功能并增加设备绑定支持 - 修改生产日期字段支持清除和显示默认值 - 在标准库中增加设备绑定功能,支持模板与设备关联 - 新增价格字段用于检验项目定价管理 - 增强模板绑定界面,支持多选和设备分配操作

---
 src/views/standard/model/index.vue                                       |   12 +++-
 .env.development                                                         |    4 
 src/api/standard/standardLibrary.js                                      |    9 +++
 src/views/standard/standardLibrary/index.vue                             |   59 +++++++++++++++----
 vue.config.js                                                            |    4 
 src/views/business/productOrder/components/addOrder.vue                  |   16 ++++-
 src/main.js                                                              |    8 +-
 src/views/business/productOrder/components/printDialog.vue               |    4 
 src/views/business/productOrder/components/addView.vue                   |    2 
 src/components/Onlyoffice/onlyoffice.vue                                 |   40 ++++++++++++-
 src/views/business/costStatistics/components/viewInfoDia.vue             |    9 +--
 src/views/business/materialOrderComponents/materialOrder/printDialog.vue |    4 
 12 files changed, 129 insertions(+), 42 deletions(-)

diff --git a/.env.development b/.env.development
index 8215534..2a82886 100644
--- a/.env.development
+++ b/.env.development
@@ -7,8 +7,8 @@
 # LIMS瀹為獙瀹ょ鐞嗙郴缁�/寮�鍙戠幆澧�
 VUE_APP_BASE_API = '/dev-api'
 
-# 鏈湴鐜ip+绔彛
-VUE_APP_BASE_URL = 'http://127.0.0.1:8001/lims'
+# 鏂囦欢璁块棶涓庢櫘閫氭帴鍙g粺涓�璧版湰鍦板紑鍙戜唬鐞�
+VUE_APP_BASE_URL = '/dev-api'
 
 # OnlyOffice 鏂囨。鏈嶅姟鍦板潃锛堥瑙� pdf 鐢紝娌℃湁 http:// 鍓嶇紑浼氬綋鎴愮浉瀵硅矾寰勶級
 VUE_APP_ONLYOFFICE_API = 'http://192.168.21.53:9001'
diff --git a/src/api/standard/standardLibrary.js b/src/api/standard/standardLibrary.js
index 73d1965..a380534 100644
--- a/src/api/standard/standardLibrary.js
+++ b/src/api/standard/standardLibrary.js
@@ -140,6 +140,15 @@
   return request({ url: "/standardTree/saveTemplateBindings", method: "post", data });
 }
 
+// 鏌ヨ鍙粦瀹氬埌鍘熷璁板綍妯℃澘鐨勮澶�
+export function searchDevices(query) {
+  return request({
+    url: "/deviceScope/search",
+    method: "get",
+    params: query,
+  });
+}
+
 // 鑾峰彇鏍囧噯鏂规硶鏋氫妇
 export function selectStandardMethods(query) {
   return request({
diff --git a/src/components/Onlyoffice/onlyoffice.vue b/src/components/Onlyoffice/onlyoffice.vue
index 1c1e261..cfafc9f 100644
--- a/src/components/Onlyoffice/onlyoffice.vue
+++ b/src/components/Onlyoffice/onlyoffice.vue
@@ -1,6 +1,15 @@
 <!--onlyoffice 缂栬緫鍣�-->
 <template>
-  <div id="vabOnlyOffice"></div>
+  <div class="document-preview">
+    <iframe
+      v-if="isPdf"
+      :key="option.url"
+      :src="option.url"
+      class="native-pdf-preview"
+      title="PDF棰勮"
+    ></iframe>
+    <div v-else id="vabOnlyOffice"></div>
+  </div>
 </template>
 
 <script>
@@ -55,6 +64,11 @@
       }
     };
   },
+  computed: {
+    isPdf() {
+      return this.isPdfFile(this.option);
+    }
+  },
   created() {
     if(this.options){
       const option = this.options
@@ -106,10 +120,12 @@
     },
     async setEditor(option) {
       this.destroyEditor();
-      // 娌℃湁鏂囨。鍦板潃灏变笉鍒濆鍖栫紪杈戝櫒锛岄伩鍏嶈闂皻鏈姞杞界殑 DocsAPI
-      if (!option || !option.url) {
+      // PDF 浣跨敤娴忚鍣ㄥ師鐢熼瑙堬紱鍏朵粬鏂囨。鎵嶅垵濮嬪寲 OnlyOffice銆�
+      if (!option || !option.url || this.isPdfFile(option)) {
         return;
       }
+      // 绛夊緟 v-else 涓殑 OnlyOffice 瀹瑰櫒瀹屾垚娓叉煋銆�
+      await this.$nextTick();
       this.doctype = this.getFileType(option.fileType);
       let config = {
         document: {
@@ -162,6 +178,14 @@
         return;
       }
       this.docEditor = new window.DocsAPI.DocEditor("vabOnlyOffice", config);
+    },
+    isPdfFile(option) {
+      if (!option) {
+        return false;
+      }
+      const fileType = String(option.fileType || "").toLowerCase();
+      const url = String(option.url || "").split(/[?#]/)[0].toLowerCase();
+      return fileType === "pdf" || url.endsWith(".pdf");
     },
     getFileType(fileType) {
       let docType = "";
@@ -227,6 +251,16 @@
 </script>
 
 <style scoped>
+.document-preview,
+.native-pdf-preview,
+#vabOnlyOffice {
+  width: 100%;
+  height: 100%;
+}
+.native-pdf-preview {
+  display: block;
+  border: 0;
+}
 html,
 body {
   height: 100%;
diff --git a/src/main.js b/src/main.js
index 255d118..8cc126f 100644
--- a/src/main.js
+++ b/src/main.js
@@ -65,10 +65,10 @@
 Vue.prototype.HaveJson = (val) => {
   return JSON.parse(JSON.stringify(val));
 };
-// Vue.prototype.javaApi = process.env.NODE_ENV === "production"?"http://192.168.21.53:8001/lims":"http://127.0.0.1:8001/lims";
-// Vue.prototype.javaApi = process.env.VUE_APP_BASE_URL && process.env.NODE_ENV==='production'?process.env.VUE_APP_BASE_URL : "http://127.0.0.1:8001/lims";
-Vue.prototype.javaApi =
-  process.env.VUE_APP_BASE_URL || process.env.VUE_APP_BASE_API;
+const apiBase = process.env.VUE_APP_BASE_URL || process.env.VUE_APP_BASE_API;
+Vue.prototype.javaApi = /^https?:\/\//i.test(apiBase)
+  ? apiBase.replace(/\/$/, "")
+  : window.location.origin + "/" + apiBase.replace(/^\/+|\/+$/g, "");
 Vue.prototype.checkPermi = checkPermi;
 Vue.prototype.uploadHeader = {
   Authorization: "Bearer " + getToken(),
diff --git a/src/views/business/costStatistics/components/viewInfoDia.vue b/src/views/business/costStatistics/components/viewInfoDia.vue
index 19d5876..6ea5798 100644
--- a/src/views/business/costStatistics/components/viewInfoDia.vue
+++ b/src/views/business/costStatistics/components/viewInfoDia.vue
@@ -20,12 +20,9 @@
     return {
       tableData: [],
       column: [
-        { label: '濮旀墭缂栧彿', prop: 'entrustCode', width: 160 },
-        {label: '妫�楠岄」鍒嗙被', prop: 'inspectionItemClass', width: 160},
-        {label: '妫�楠岄」', prop: 'inspectionItem', width: 160},
-        {label: '妫�楠屽瓙椤�', prop: 'inspectionItemSubclass', width: 160},
-        { label: '鏀惰垂鏍囧噯(鍏�/娆�)', prop: 'rates' },
-        { label: '鍒嗙粍绯绘暟', prop: 'manHourGroup' },
+        { label: '妫�楠岄」鐩�', prop: 'number', width: 160 },
+        { label: '妯℃澘鍚嶇О', prop: 'name', width: 200 },
+        { label: '浠锋牸(鍏�)', prop: 'price' },
       ],
       viewInfoDia: false,
       tableLoading: false,
diff --git a/src/views/business/materialOrderComponents/materialOrder/printDialog.vue b/src/views/business/materialOrderComponents/materialOrder/printDialog.vue
index fe99554..2d39bd1 100644
--- a/src/views/business/materialOrderComponents/materialOrder/printDialog.vue
+++ b/src/views/business/materialOrderComponents/materialOrder/printDialog.vue
@@ -30,7 +30,7 @@
                     <span class="info">{{ item.partDesc }}</span>
                   </div>
                   <div class="item">
-                    <span class="full-title">濮旀墭鏃ユ湡</span>:
+                    <span class="full-title">閫佹牱鏃ユ湡</span>:
                     <span class="info">{{ item.sendTime }}</span>
                   </div>
                   <div class="item">
@@ -93,7 +93,7 @@
                 <span class="info4">{{ item.partDesc }}</span>
               </div>
               <div class="item">
-                <span class="full-title2">濮旀墭鏃ユ湡</span>:
+                <span class="full-title2">閫佹牱鏃ユ湡</span>:
                 <span class="info">{{ item.sendTime }}</span>
               </div>
               <div class="item">
diff --git a/src/views/business/productOrder/components/addOrder.vue b/src/views/business/productOrder/components/addOrder.vue
index f70fb79..7376ef4 100644
--- a/src/views/business/productOrder/components/addOrder.vue
+++ b/src/views/business/productOrder/components/addOrder.vue
@@ -126,7 +126,7 @@
             <el-col :span="6">
               <el-form-item label="鐢熶骇鏃ユ湡">
                 <el-date-picker v-model="addObj.productionDate" type="date" value-format="yyyy-MM-dd"
-                                :disabled="active>1" size="small"/>
+                                :disabled="active>1" clearable placeholder="涓嶉�夋嫨鏃舵姤鍛婃樉绀�/" size="small"/>
               </el-form-item>
             </el-col>
             <el-col :span="6">
@@ -853,6 +853,8 @@
         quarterItemId: '',
         sampleView: '', // 鏍峰搧鍚嶇О锛堟姤鍛婂睍绀哄瓧娈碉級
         sampleViewEn: '', // 鏍峰搧鍚嶇О鑻辨枃锛堟姤鍛婂睍绀哄瓧娈碉級
+        productionDate: null,
+        sampleDate: null,
       },
       addObjRules: { // 琛ㄥ崟鏍¢獙瑙勫垯
         orderType: [
@@ -1567,9 +1569,15 @@
     },
     saveData(sampleList) {
       this.saveLoad = true
+      const insOrder = {
+        ...this.addObj,
+        productionDate: this.addObj.productionDate === '/' || !this.addObj.productionDate
+          ? null : this.addObj.productionDate,
+        typeSource: -1,
+      }
       if (this.tabIndex == 4 && this.active == 2) {
         // 閫�鍥炲悗鎻愪氦
-        updateInsOrder({insOrder: this.addObj, sampleProduct: sampleList}).then(res => {
+        updateInsOrder({insOrder, sampleProduct: sampleList}).then(res => {
           this.saveLoad = false
           this.$message.success('宸叉彁浜�')
           this.bsm3Dia = false;
@@ -1579,7 +1587,7 @@
         })
       } else {
         // 甯歌鎻愪氦
-        const newJuOrder = {...this.addObj, typeSource: -1}
+        const newJuOrder = {...insOrder}
         delete newJuOrder.temperature
         delete newJuOrder.humidity
         const payload = this.addObj.standardTreeId
@@ -1589,7 +1597,7 @@
             sampleCode: this.sampleCode,
             templateList: this.boundTemplateList.map((item, index) => ({templateId: item.templateId, sort: index}))
           }
-          : {insOrder: {...this.addObj, typeSource: -1}, sampleList: sampleList}
+          : {insOrder, sampleList: sampleList}
         addInsOrder(payload).then(res => {
           this.saveLoad = false
           this.$message.success('宸叉彁浜�')
diff --git a/src/views/business/productOrder/components/addView.vue b/src/views/business/productOrder/components/addView.vue
index 1503f96..cd295a3 100644
--- a/src/views/business/productOrder/components/addView.vue
+++ b/src/views/business/productOrder/components/addView.vue
@@ -57,7 +57,7 @@
           <el-descriptions-item label="鏍峰搧缂栧彿">{{ sampleCode || '-' }}</el-descriptions-item>
           <el-descriptions-item label="鏍峰搧澶栬">{{ addObj.sampleStatus || '-' }}</el-descriptions-item>
           <el-descriptions-item label="閫佹牱浜�">{{ addObj.prepareUser || '-' }}</el-descriptions-item>
-          <el-descriptions-item label="鐢熶骇鏃ユ湡">{{ addObj.productionDate || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="鐢熶骇鏃ユ湡">{{ addObj.productionDate || '/' }}</el-descriptions-item>
           <el-descriptions-item label="鐢熶骇鎵瑰彿">{{ addObj.productionBatch || '-' }}</el-descriptions-item>
           <el-descriptions-item label="閫佹牱鏃ユ湡">{{ addObj.sampleDate || '-' }}</el-descriptions-item>
           <el-descriptions-item label="鑱旂郴鏂瑰紡">{{ addObj.phone || '-' }}</el-descriptions-item>
diff --git a/src/views/business/productOrder/components/printDialog.vue b/src/views/business/productOrder/components/printDialog.vue
index 77a921f..a85c32e 100644
--- a/src/views/business/productOrder/components/printDialog.vue
+++ b/src/views/business/productOrder/components/printDialog.vue
@@ -30,7 +30,7 @@
                     <span class="info">{{ item.sampleModel }}</span>
                   </div>
                   <div class="item">
-                    <span class="full-title">濮旀墭鏃ユ湡</span>:
+                    <span class="full-title">閫佹牱鏃ユ湡</span>:
                     <span class="info">{{ item.sendTime }}</span>
                   </div>
                   <div class="item">
@@ -91,7 +91,7 @@
                 <span class="info4">{{ item.sampleModel }}</span>
               </div>
               <div class="item">
-                <span class="full-title2">濮旀墭鏃ユ湡</span>:
+                <span class="full-title2">閫佹牱鏃ユ湡</span>:
                 <span class="info">{{ item.sendTime }}</span>
               </div>
               <div class="item">
diff --git a/src/views/standard/model/index.vue b/src/views/standard/model/index.vue
index 6d3ce8f..e179b7a 100644
--- a/src/views/standard/model/index.vue
+++ b/src/views/standard/model/index.vue
@@ -27,7 +27,7 @@
     <el-dialog :before-close="closeCopyTem" :close-on-click-modal="false" :close-on-press-escape="false"
       :visible.sync="isShowCopyTem" :title="title" width="35%" :modal-append-to-body="false">
       <el-form ref="copyForm" :model="copyForm" :rules="copyFormRules" label-position="right" label-width="80px">
-        <el-form-item label="妯$増缂栧彿" prop="number">
+        <el-form-item label="妫�楠岄」鐩�" prop="number">
           <el-input v-model="copyForm.number" clearable size="small"></el-input>
         </el-form-item>
         <el-form-item label="妯$増鍚嶇О" prop="name">
@@ -35,6 +35,9 @@
         </el-form-item>
         <el-form-item label="澶囨敞" prop="remark">
           <el-input v-model="copyForm.remark" clearable size="small"></el-input>
+        </el-form-item>
+        <el-form-item label="浠锋牸(鍏�)" prop="price">
+          <el-input v-model="copyForm.price" clearable size="small"></el-input>
         </el-form-item>
       </el-form>
       <span slot="footer" class="dialog-footer">
@@ -79,6 +82,7 @@
         number: "",
         name: "",
         remark: "",
+        price: "",
         id: "",
       },
       copyFormRules: {
@@ -89,9 +93,10 @@
       },
       tableData: [],
       column: [
-        { label: "妯℃澘缂栧彿", prop: "number" },
+        { label: "妫�楠岄」鐩�", prop: "number" },
         { label: "妯℃澘鍚嶇О", prop: "name" },
         { label: "澶囨敞", prop: "remark" },
+        { label: "浠锋牸(鍏�)", prop: "price" },
         { label: "鍒涘缓鐢ㄦ埛", prop: "createUserName" },
         { label: "鍒涘缓鏃堕棿", prop: "createTime", width: "160" },
         { label: "鏇存柊鐢ㄦ埛", prop: "updateUserName" },
@@ -216,6 +221,7 @@
             name: this.copyForm.name,
             number: this.copyForm.number,
             remark: this.copyForm.remark,
+            price: this.copyForm.price === "" || this.copyForm.price == null ? null : this.copyForm.price,
           };
           switch (this.title) {
             case "鏂板":
@@ -225,7 +231,7 @@
                   this.isShowCopyTem = false;
                   this.submitCopyInfoLoading = false;
                   this.$message.success("鏂板鎴愬姛");
-                  this.refreshTable("page");
+                  this.getList();
                 })
                 .catch((err) => {
                   console.log("copyTemplate----", err);
diff --git a/src/views/standard/standardLibrary/index.vue b/src/views/standard/standardLibrary/index.vue
index a800e11..464bb37 100644
--- a/src/views/standard/standardLibrary/index.vue
+++ b/src/views/standard/standardLibrary/index.vue
@@ -48,9 +48,21 @@
             <p v-if="!templateBindings.length" class="binding-tip">璇蜂负褰撳墠鏍囧噯缁戝畾鍘熷璁板綍妯℃澘銆�</p>
             <el-table :data="templateBindings" border size="small">
               <el-table-column type="index" label="椤哄簭" width="70" /><el-table-column label="妯℃澘缂栧彿" prop="number" width="180" /><el-table-column label="妯℃澘鍚嶇О" prop="name" /><el-table-column label="妫�娴嬩緷鎹紙妯℃澘澶囨敞锛�" prop="remark" />
+              <el-table-column label="璁惧缁戝畾" min-width="360">
+                <template slot-scope="scope">
+                  <el-select v-model="scope.row.deviceIds"
+                             :disabled="!checkPermi(['standard:standardLibrary:upStandardProduct'])"
+                             multiple filterable clearable size="small"
+                             placeholder="璇烽�夋嫨璇ユā鏉跨殑榛樿璁惧" style="width:100%">
+                    <el-option v-for="device in deviceOptions" :key="device.id"
+                               :label="device.label" :value="device.id" />
+                  </el-select>
+                </template>
+              </el-table-column>
               <el-table-column v-if="checkPermi(['standard:standardLibrary:upStandardProduct'])" label="鎿嶄綔" width="150"><template slot-scope="scope"><el-button type="text" :disabled="scope.$index === 0" @click="moveBinding(scope.$index, -1)">涓婄Щ</el-button><el-button type="text" :disabled="scope.$index === templateBindings.length - 1" @click="moveBinding(scope.$index, 1)">涓嬬Щ</el-button><el-button type="text" @click="removeBinding(scope.$index)">绉婚櫎</el-button></template></el-table-column>
             </el-table>
-            <el-select v-if="checkPermi(['standard:standardLibrary:upStandardProduct'])" v-model="templateToBind" filterable placeholder="閫夋嫨鍘熷璁板綍妯℃澘" size="small" style="width:320px;margin-top:16px" @change="addBinding"><el-option v-for="template in templateList.filter(item => !templateBindings.some(binding => binding.templateId === item.id))" :key="template.id" :label="template.number ? template.number + ' - ' + template.name : template.name" :value="template.id" /></el-select>
+            <el-select v-if="checkPermi(['standard:standardLibrary:upStandardProduct'])" v-model="templateToBind" multiple filterable placeholder="閫夋嫨鍘熷璁板綍妯℃澘" size="small" style="width:320px;margin-top:16px"><el-option v-for="template in templateList.filter(item => !templateBindings.some(binding => binding.templateId === item.id))" :key="template.id" :label="template.number ? template.number + ' - ' + template.name : template.name" :value="template.id" /></el-select>
+            <el-button v-if="checkPermi(['standard:standardLibrary:upStandardProduct'])" type="primary" size="small" style="margin-top:16px;margin-left:8px" @click="confirmAddBindings">纭</el-button>
           </el-card>
           <el-empty v-else description="璇烽�夋嫨浜岀骇鏍囧噯锛岀淮鎶ゅ叾鍘熷璁板綍妯℃澘" />
           <div v-if="false">
@@ -322,6 +334,7 @@
   getStandardTemplate,
   selectTemplateBindings,
   saveTemplateBindings,
+  searchDevices,
 } from "@/api/standard/standardLibrary";
 import bindSupplierDensityDialogAsk from "./components/bindSupplierDensityDialogAsk.vue";
 import BatchCopy from "./components/BatchCopy.vue";
@@ -393,7 +406,8 @@
       sectionList: [],
       templateList: [],
       templateBindings: [],
-      templateToBind: null,
+      templateToBind: [],
+      deviceOptions: [],
       total: 0,
       currentPage: 1,
       standardId: 0,
@@ -419,6 +433,7 @@
     this.selectEnumByCategoryForSonLaboratory();
     this.selectEnumByCategoryForsampleType();
     this.getStandardTemplate();
+    this.loadDeviceOptions();
     this.selectStandardMethodsSec();
     this.token = {
       token: sessionStorage.getItem("token"),
@@ -977,18 +992,32 @@
         this.templateList = res.data;
       });
     },
-    loadTemplateBindings(standardTreeId) {
-      selectTemplateBindings({ standardTreeId }).then((res) => {
-        this.templateBindings = res.data || [];
-        this.templateToBind = null;
+    loadDeviceOptions() {
+      searchDevices({ status: 0 }).then((res) => {
+        this.deviceOptions = (res.data || []).map((device) => ({
+          id: device.id,
+          label: `${device.deviceName}--${device.managementNumber}`,
+        }));
       });
     },
-    addBinding(templateId) {
-      const template = this.templateList.find((item) => item.id === templateId);
-      if (template && !this.templateBindings.some((item) => item.templateId === templateId)) {
-        this.templateBindings.push({ ...template, templateId, sort: this.templateBindings.length });
-      }
-      this.templateToBind = null;
+    loadTemplateBindings(standardTreeId) {
+      selectTemplateBindings({ standardTreeId }).then((res) => {
+        this.templateBindings = (res.data || []).map((item) => ({
+          ...item,
+          deviceIds: Array.isArray(item.deviceIds) ? [...item.deviceIds] : [],
+        }));
+        this.templateToBind = [];
+      });
+    },
+    confirmAddBindings() {
+      const selectedIds = Array.isArray(this.templateToBind) ? this.templateToBind : [];
+      selectedIds.forEach((templateId) => {
+        const template = this.templateList.find((item) => item.id === templateId);
+        if (template && !this.templateBindings.some((item) => item.templateId === templateId)) {
+          this.templateBindings.push({ ...template, templateId, sort: this.templateBindings.length, deviceIds: [] });
+        }
+      });
+      this.templateToBind = [];
     },
     removeBinding(index) {
       this.templateBindings.splice(index, 1);
@@ -1000,7 +1029,11 @@
     },
     saveBindings() {
       saveTemplateBindings({ standardTreeId: this.selected.id,
-        templates: this.templateBindings.map((item, index) => ({ templateId: item.templateId, sort: index }))
+        templates: this.templateBindings.map((item, index) => ({
+          templateId: item.templateId,
+          sort: index,
+          deviceIds: Array.isArray(item.deviceIds) ? [...item.deviceIds] : [],
+        }))
       }).then(() => {
         this.$message.success("妯℃澘缁戝畾宸蹭繚瀛�");
         this.loadTemplateBindings(this.selected.id);
diff --git a/vue.config.js b/vue.config.js
index e11b2a9..3c39cb2 100644
--- a/vue.config.js
+++ b/vue.config.js
@@ -36,8 +36,8 @@
     proxy: {
       // detail: https://cli.vuejs.org/config/#devserver-proxy
       [process.env.VUE_APP_BASE_API]: {
-        // target: `http://36.213.90.123:9015/lims`,
-        target: `http://192.168.21.53:9001/lims`,
+        // 鏈湴寮�鍙戠粺涓�璁块棶鏈満鍚庣锛屾姤鍛婃枃浠惰鍙栨湰鏈� wordUrl 鐩綍銆�
+        target: `http://127.0.0.1:9509/lims`,
         changeOrigin: true,
         pathRewrite: {
           ["^" + process.env.VUE_APP_BASE_API]: "",

--
Gitblit v1.9.3