Explorar o código

bug-716:协定方新增时的病名、证型、治法、西医诊断搜索问题

张田田 hai 2 días
pai
achega
17d770d573

+ 453 - 170
src/views/business/LocalExpertTech.vue

@@ -93,11 +93,13 @@
             <el-table-column prop="westernDisease" label="西医病名" width="120" align="center"></el-table-column>
             <el-table-column prop="expert" label="专家" width="100" align="center"></el-table-column>
             <el-table-column label="操作" width="180" align="center">
-              <div class="flex-center operation" slot-scope="scope">
-                <div class="flex-center" @click="openEditDialog(scope.row)">编辑</div>
-                <div class="flex-center bg-yellow" @click="handleDelete(scope.row)">删除</div>
-                <div class="flex-center" @click="handleView(scope.row)">查看</div>
-              </div>
+              <template slot-scope="scope">
+                <div class="flex-center">
+                  <div class="find-detail find-fill" @click="openEditDialog(scope.row)">编辑</div>
+                  <div class="find-detail find-fill1" @click="handleDelete(scope.row)">删除</div>
+                  <div class="find-detail find-fill2" @click="handleView(scope.row)">查看</div>
+                </div>
+              </template>
             </el-table-column>
           </el-table>
         </div>
@@ -164,35 +166,128 @@
             <span style="opacity:0">*</span>
             <div class="name">中医病名:</div>
             <div class="input">
-              <el-select size="mini" v-model="editData.chineseDisease" placeholder="请搜索选择" clearable filterable :loading="diseaseLoading" loading-text="搜索中..." :filter-method="searchDisease" @focus="searchDisease('')" @change="onDiseaseChange">
-                <el-option v-for="(item, idx) in diseaseOptions" :key="idx" :label="item.disname" :value="String(item.disid)"></el-option>
-              </el-select>
+              <el-popover placement="bottom" width="180" trigger="focus" :close-delay="100">
+                <el-input
+                  :class="{invalid: editData.chineseDiseaseName && (!editData.disCode)}"
+                  size="mini"
+                  slot="reference"
+                  :placeholder="key1?key1:'中医病名'"
+                  v-model="key1"
+                  ref="zybm"
+                  @input="getDiseaseList(key1)"
+                  @focus="handleFocus('bm')"
+                  @keydown.enter.native="handleEnter('bm')"
+                >
+                  <div slot="suffix" class="suffix">
+                    <i class="el-icon-arrow-down" v-if="!key1"></i>
+                    <i class="el-icon-circle-close" v-else @click="clearBm"></i>
+                  </div>
+                </el-input>
+                <ul class="option-list">
+                  <li
+                    v-for="item in diseaseList"
+                    :key="item.$uid"
+                    :class="{ active: editData.chineseDiseaseName === item.$name }"
+                    @click="handleBm(item)"
+                  >{{ item.$name }}</li>
+                </ul>
+              </el-popover>
             </div>
           </div>
           <div class="form-item flex flex-col-center">
             <span style="opacity:0">*</span>
             <div class="name">证型:</div>
             <div class="input">
-              <el-select size="mini" v-model="editData.symptomid" placeholder="请搜索选择" clearable filterable :loading="symptomLoading" loading-text="搜索中..." :filter-method="searchSymptom" @focus="searchSymptom('')" @change="onSymptomChange">
-                <el-option v-for="(item, idx) in symptomOptions" :key="idx" :label="item.symname" :value="item.symid"></el-option>
-              </el-select>
+              <el-popover placement="bottom" width="180" trigger="focus" :close-delay="100">
+                <el-input
+                  :class="{invalid: editData.symptomName && !editData.symptomCode}"
+                  size="mini"
+                  slot="reference"
+                  :placeholder="key2?key2:'中医证型'"
+                  v-model="key2"
+                  ref="zhengxing"
+                  @input="getSymptomList(key2)"
+                  @focus="handleFocus('zx')"
+                  @keydown.enter.native="handleEnter('zx')"
+                >
+                  <div slot="suffix" class="suffix">
+                    <i class="el-icon-arrow-down" v-if="!key2"></i>
+                    <i class="el-icon-circle-close" v-else @click="clearZx"></i>
+                  </div>
+                </el-input>
+                <ul class="option-list">
+                  <li
+                    v-for="item in symptomList"
+                    :key="item.$uid"
+                    :class="{ active: editData.symptomName === item.$name, matched: item.isMatched }"
+                    @click="handleZx(item)"
+                  >{{ item.$name }}</li>
+                </ul>
+              </el-popover>
             </div>
           </div>
           <div class="form-item flex flex-col-center">
             <span style="opacity:0">*</span>
             <div class="name">治法:</div>
             <div class="input">
-              <el-select size="mini" v-model="editData.therapyCode" placeholder="请搜索选择" clearable filterable :loading="therapyLoading" loading-text="搜索中..." :filter-method="searchTherapy" @focus="searchTherapy('')">
-                <el-option v-for="(item, idx) in therapyOptions" :key="idx" :label="item.therapyName || item.therapy" :value="item.therapyCode"></el-option>
-              </el-select>
+              <el-popover placement="bottom" width="180" trigger="focus" :close-delay="100" v-model="popoverZF">
+                <el-input
+                  :class="{invalid: editData.therapyName && !editData.therapyCode}"
+                  size="mini"
+                  slot="reference"
+                  :placeholder="key3?key3:'中医治法'"
+                  v-model="key3"
+                  ref="zhifa"
+                  @input="getTherapyList(key3)"
+                  @focus="handleFocus('zf')"
+                  @keydown.enter.native="handleEnter('zf')"
+                >
+                  <div slot="suffix" class="suffix">
+                    <i class="el-icon-arrow-down" v-if="!key3"></i>
+                    <i class="el-icon-circle-close" v-else @click="clearZf"></i>
+                  </div>
+                </el-input>
+                <ul class="option-list">
+                  <li
+                    v-for="item in therapyList"
+                    :key="item.$uid"
+                    :class="{ active: editData.therapyName === item.$name, matched: item.isMatched }"
+                    @click="handleZf(item)"
+                  >{{ item.$name }}</li>
+                </ul>
+              </el-popover>
             </div>
           </div>
           <div class="form-item flex flex-col-center">
             <span style="opacity:0">*</span>
             <div class="name">西医诊断:</div>
             <div class="input">
-              <el-select size="mini" v-model="editData.westernDisease" placeholder="请搜索选择" clearable filterable multiple collapse-tags :loading="westernDiseaseLoading" loading-text="搜索中..." :filter-method="searchWesternDisease" @focus="searchWesternDisease('')" @change="onWesternDiseaseChange" popper-class="western-disease-select" @visible-change="onWesternDiseaseVisible">                <el-option v-for="(item, idx) in westernDiseaseOptions" :key="idx" :label="item.westname" :value="String(item.westcode)"></el-option>
-              </el-select>
+              <div class="western-disease-input-wrapper" @click="$refs.wdAutocomplete.focus()">
+                <el-tag
+                  v-for="code in editData.westernDisease"
+                  :key="code"
+                  size="mini"
+                  closable
+                  @close="removeWesternDisease(code)"
+                >{{ westernDiseaseNameMap[code] || code }}</el-tag>
+                <el-autocomplete
+                  ref="wdAutocomplete"
+                  size="mini"
+                  v-model="westernDiseaseSearch"
+                  :fetch-suggestions="queryWesternDisease"
+                  value-key="westname"
+                  :placeholder="editData.westernDisease.length ? '' : '请搜索选择'"
+                  clearable
+                  :trigger-on-focus="true"
+                  @select="onWesternDiseaseSelect"
+                >
+                  <template slot-scope="{ item }">
+                    <div :class="{ 'autocomplete-disabled': item._disabled }">
+                      {{ item.westname }}
+                    </div>
+                  </template>
+                </el-autocomplete>
+              </div>
             </div>
           </div>
           <div class="form-item flex flex-col-center" style="width: 100%">
@@ -211,7 +306,7 @@
           :isEditable.sync="editData.isEditable"
           :detailTypes.sync="editData.detailTypes"
           :statistics.sync="editData.statistics"
-          :showGuide="true"
+          :showGuide="false"
           @save="onAcupointSave"
         />
         </div>
@@ -301,6 +396,11 @@ import {
   getSymptomListMethod,
   getTherapyListMethod,
 } from "@/request/api.illness.js";
+import {
+  getDiseaseListMethod as getDiseaseListMethodApi,
+  getSymptomListMethod as getSymptomListMethodApi,
+  getTherapyListMethod as getTherapyListMethodApi,
+} from "@/request/api";
 import { getLocalSuitableTechList, deleteLocalSuitableTech, saveLocalSuitableTech, getLocalSuitableTechInfo, getMappedNondrugItemList } from "@/api/technology.js";
 import { getXDiseaseName } from "@/api/knowledge.js";
 import SuitableTechDetail from "./components/SuitableTechDetail.vue";
@@ -317,14 +417,22 @@ export default {
         westernDisease: '',
         expert: ''
       },
-      // 下拉选项
+      // 搜索区域下拉
       diseaseOptions: [],
       westernDiseaseOptions: [],
       westernDiseasePage: 1,
       westernDiseaseHasMore: true,
-      symptomOptions: [],
-      therapyOptions: [],
       projectOptions: [],
+      // 弹窗内下拉(popover 方式)
+      diseaseList: [],
+      symptomList: [],
+      therapyList: [],
+      popoverZF: false,
+      key1: "",
+      key2: "",
+      key3: "",
+      westernDiseaseSearch: "",
+      westernDiseaseNameMap: {},
       titlesList: [
         { key: "1", value: "国医大师" },
         { key: "2", value: "国家级名老中医" },
@@ -334,8 +442,6 @@ export default {
       // 下拉加载状态
       diseaseLoading: false,
       westernDiseaseLoading: false,
-      symptomLoading: false,
-      therapyLoading: false,
       projectLoading: false,
       // 表格数据
       tableData: [],
@@ -348,7 +454,11 @@ export default {
       showEditDialog: false,
       editLoading: false,
       editData: {
-        statistics: {}
+        statistics: {},
+        westernDisease: [],
+        chineseDiseaseName: "",
+        symptomName: "",
+        therapyName: "",
       },
       // 查看详情弹窗
       showViewDialog: false,
@@ -359,8 +469,47 @@ export default {
   created() {
     this.getList()
   },
+  watch: {
+    key1: {
+      handler(value, oldVal) {
+        if (this.key1 === "") {
+          this.editData.chineseDisease = ""
+          this.editData.chineseDiseaseName = ""
+          this.editData.disCode = ""
+        }
+        if (!value && oldVal) {
+          this.getSymptomList()
+          this.getTherapyList()
+        }
+      }
+    },
+    key2: {
+      handler(value, oldVal) {
+        if (this.key2 === "") {
+          this.editData.symptomName = ""
+          this.editData.symptomid = ""
+          this.editData.symptomCode = ""
+        }
+        if (!value && oldVal) {
+          this.getSymptomList()
+          this.getTherapyList()
+        }
+      }
+    },
+    key3: {
+      handler(value, oldVal) {
+        if (this.key3 === "") {
+          this.editData.therapyName = ""
+          this.editData.therapyCode = ""
+        }
+        if (!value && oldVal) {
+          this.getTherapyList()
+        }
+      }
+    },
+  },
   methods: {
-    // ========== 中医病名搜索 ==========
+    // ========== 搜索区域:中医病名 ==========
     async searchDisease(query) {
       this.diseaseLoading = true
       try {
@@ -372,71 +521,91 @@ export default {
         this.diseaseLoading = false
       }
     },
-    onDiseaseChange(disid) {
-      const selected = this.diseaseOptions.find(item => item.disid === disid)
-      if (selected) {
-        this.editData.chineseDiseaseName = selected.disname
-        this.editData.disCode = selected.disCode
-      }
-      // 联动清空证型
-      this.editData.symptomid = ''
-      this.editData.symptomName = ''
-      this.symptomOptions = []
-      // 联动清空治法
-      this.editData.therapyCode = ''
-      this.editData.therapyName = ''
-      this.therapyOptions = []
-      if (disid) {
-        this.searchSymptom('')
-      }
+    // ========== 弹窗:focus/enter ==========
+    handleFocus(type) {
+      if (type === 'bm' && this.diseaseList.length === 0) this.getDiseaseList(this.key1);
+      else if (type === 'zx' && this.symptomList.length === 0) this.getSymptomList(this.key2);
+      else if (type === 'zf' && this.therapyList.length === 0) this.getTherapyList(this.key3);
     },
-    // ========== 证型搜索 ==========
-    async searchSymptom(query) {
-      this.symptomLoading = true
-      try {
-        const params = { keyword: query || '' }
-        if (this.editData.chineseDisease) params.disid = this.editData.chineseDisease
-        if (this.editData.disCode) params.disCode = this.editData.disCode
-        const { list } = await getSymptomListMethod(1, 9999, params)
-        this.symptomOptions = list || []
-      } catch (e) {
-        console.error('搜索证型失败', e)
-      } finally {
-        this.symptomLoading = false
-      }
+    handleEnter(type) {
+      if (type === 'bm' && this.diseaseList.length) this.handleBm(this.diseaseList[0]);
+      else if (type === 'zx' && this.symptomList.length) this.handleZx(this.symptomList[0]);
+      else if (type === 'zf' && this.therapyList.length) this.handleZf(this.therapyList[0]);
     },
-    onSymptomChange(symid) {
-      const selected = this.symptomOptions.find(item => item.symid === symid)
-      if (selected) {
-        this.editData.symptomName = selected.symname
-      }
-      // 联动清空治法
-      this.editData.therapyCode = ''
-      this.editData.therapyName = ''
-      this.therapyOptions = []
-      if (symid) {
-        this.searchTherapy('')
-      }
+    // ========== 弹窗:中医病名(popover) ==========
+    async getDiseaseList(keyword = '') {
+      const { total, list } = await getDiseaseListMethodApi(1, 9999, { keyword }).catch(() => ({ total: 0, list: [] }));
+      this.diseaseList = list;
     },
-    // ========== 治法搜索 ==========
-    async searchTherapy(query) {
-      this.therapyLoading = true
-      try {
-        const params = { keyword: query || '' }
-        if (this.editData.disCode) params.disCode = this.editData.disCode
-        if (this.editData.symptomid) {
-          params.symptomCode = this.editData.symptomid
-          params.symid = this.editData.symptomid
-        }
-        const { list } = await getTherapyListMethod(1, 9999, params)
-        this.therapyOptions = list || []
-      } catch (e) {
-        console.error('搜索治法失败', e)
-      } finally {
-        this.therapyLoading = false
-      }
+    async handleBm(item) {
+      this.editData.chineseDisease = String(item.disid);
+      this.editData.chineseDiseaseName = item.$name;
+      this.editData.disCode = item.$code;
+      this.key1 = item.$name;
+      this.$refs.zybm.blur();
+      this.clearZx();
+      if (!this.key1) return;
+      this.$refs.zhengxing && this.$refs.zhengxing.focus();
+      await this.getSymptomList();
+    },
+    clearBm() {
+      this.editData.chineseDisease = "";
+      this.editData.chineseDiseaseName = "";
+      this.editData.disCode = "";
+      this.key1 = "";
+      this.clearZx();
+    },
+    // ========== 弹窗:证型(popover) ==========
+    async getSymptomList(keyword = '') {
+      const { total, list } = await getSymptomListMethodApi(1, 9999, {
+        keyword,
+        disid: this.editData.chineseDisease,
+        disCode: this.editData.disCode,
+      }).catch(() => ({ total: 0, list: [] }));
+      this.symptomList = list;
+    },
+    async handleZx(item) {
+      this.editData.symptomName = item.$name;
+      this.editData.symptomid = item.$code;
+      this.editData.symptomCode = item.$code;
+      this.key2 = item.$name;
+      this.$refs.zhengxing.blur();
+      this.clearZf();
+      this.$refs.zhifa && this.$refs.zhifa.focus();
+      await this.getTherapyList();
     },
-    // ========== 西医诊断搜索(分页) ==========
+    clearZx() {
+      this.editData.symptomName = "";
+      this.editData.symptomid = "";
+      this.editData.symptomCode = "";
+      this.key2 = "";
+      this.clearZf();
+    },
+    // ========== 弹窗:治法(popover) ==========
+    async getTherapyList(keyword = '') {
+      const { total, list } = await getTherapyListMethodApi(1, 9999, {
+        keyword,
+        disCode: this.editData.disCode,
+        symptomCode: this.editData.symptomid,
+        symid: this.editData.symptomid,
+      }).catch(() => ({ total: 0, list: [] }));
+      this.therapyList = list;
+    },
+    handleZf(item) {
+      this.editData.therapyName = item.$name;
+      this.editData.therapyCode = item.$code;
+      this.key3 = item.$name;
+      this.$nextTick(() => {
+        this.$refs.zhifa && this.$refs.zhifa.blur();
+        this.popoverZF = false;
+      });
+    },
+    clearZf() {
+      this.editData.therapyName = "";
+      this.editData.therapyCode = "";
+      this.key3 = "";
+    },
+    // ========== 西医诊断搜索(搜索区域 + 弹窗共用) ==========
     async searchWesternDisease(query) {
       this.westernDiseasePage = 1
       this.westernDiseaseHasMore = true
@@ -466,6 +635,11 @@ export default {
             }))
           this.westernDiseaseOptions = [...preserved, ...list]
           this.westernDiseaseHasMore = list.length >= 200
+          list.forEach((item) => {
+            if (item.westcode && item.westname) {
+              this.westernDiseaseNameMap[item.westcode] = item.westname
+            }
+          })
         }
       } catch (e) {
         console.error('搜索西医诊断失败', e)
@@ -473,52 +647,42 @@ export default {
         this.westernDiseaseLoading = false
       }
     },
-    async loadMoreWesternDisease() {
-      if (this.westernDiseaseLoading || !this.westernDiseaseHasMore) return
-      this.westernDiseasePage++
-      this.westernDiseaseLoading = true
-      try {
-        const query = this._wdQuery || ''
-        const pinyin = /^[A-Za-z]+$/g
-        const serchtype = query && pinyin.test(query) ? '1' : ''
-        const res = await getXDiseaseName({
-          pageid: this.westernDiseasePage,
-          pagesize: 200,
-          keyword: query,
-          serchtype,
-        })
-        if (res.code == 0) {
-          const list = res.data?.wests || []
-          this.westernDiseaseOptions = this.westernDiseaseOptions.concat(list)
-          this.westernDiseaseHasMore = list.length >= 200
-        }
-      } catch (e) {
-        console.error('加载更多西医诊断失败', e)
-      } finally {
-        this.westernDiseaseLoading = false
+    // 弹窗:西医诊断 autocomplete 回调
+    async queryWesternDisease(queryString, cb) {
+      await this.searchWesternDisease(queryString)
+      if (this.westernDiseaseOptions.length > 0) {
+        cb(this.westernDiseaseOptions)
+      } else {
+        cb([{ westname: "暂无数据", _disabled: true }])
       }
     },
-    onWesternDiseaseVisible(show) {
-      const wrap = document.querySelector(".western-disease-select .el-scrollbar__wrap")
-      if (!wrap) return
-      if (show) {
-        wrap.addEventListener("scroll", this._wdScroll = () => {
-          if (wrap.scrollHeight - wrap.scrollTop <= wrap.clientHeight + 5) {
-            this.loadMoreWesternDisease()
-          }
+    onWesternDiseaseSelect(item) {
+      if (item._disabled) {
+        this.$nextTick(() => {
+          this.westernDiseaseSearch = ""
         })
-      } else {
-        wrap.removeEventListener("scroll", this._wdScroll)
+        return
+      }
+      const code = String(item.westcode)
+      if (!this.editData.westernDisease.includes(code)) {
+        this.editData.westernDisease.push(code)
+        this.westernDiseaseNameMap[code] = item.westname
+        this.updateWesternDiseaseName()
       }
+      this.westernDiseaseSearch = ""
+      this.$nextTick(() => {
+        this.searchWesternDisease("")
+      })
     },
-    onWesternDiseaseChange(codes) {
-      const names = codes
-        .map(code => {
-          const item = this.westernDiseaseOptions.find(o => o.westcode === code)
-          return item ? item.westname : ''
-        })
+    removeWesternDisease(code) {
+      this.editData.westernDisease = this.editData.westernDisease.filter((c) => c !== code)
+      this.updateWesternDiseaseName()
+    },
+    updateWesternDiseaseName() {
+      const names = this.editData.westernDisease
+        .map((code) => this.westernDiseaseNameMap[code] || "")
         .filter(Boolean)
-      this.editData.westernDiseaseName = names.join(',')
+      this.editData.westernDiseaseName = names.join(",")
     },
     // 涵盖项目远程模糊搜索
     async searchProject(query) {
@@ -607,27 +771,33 @@ export default {
           this.editLoading = false
         }
 
-        this.diseaseOptions = detail.disId != null
-          ? [{ disid: detail.disId, disname: detail.disName, disCode: detail.disCode }]
-          : []
-        this.symptomOptions = detail.synCode != null
-          ? [{ symid: detail.synCode, symname: detail.synName, synCode: detail.synCode }]
-          : []
-        this.therapyOptions = detail.theCode != null
-          ? [{ therapyCode: detail.theCode, therapy: detail.theName, therapyName: detail.theName }]
-          : []
+        // 初始化搜索关键字,用于回显
+        this.key1 = detail.disName || ""
+        this.key2 = detail.synName || ""
+        this.key3 = detail.theName || ""
+        // 加载下拉列表数据
+        if (this.key1) this.getDiseaseList(this.key1)
+        if (this.key2) this.getSymptomList(this.key2)
+        if (this.key3) this.getTherapyList(this.key3)
         if (detail.westernCode) {
-          const codes = String(detail.westernCode).split(',')
+          const codes = String(detail.westernCode).split(",")
           const names = detail.westernDiag
-            ? String(detail.westernDiag).split(',')
+            ? String(detail.westernDiag).split(",")
             : []
           this.westernDiseaseOptions = codes.map((code, idx) => ({
             westcode: code.trim(),
-            westname: (names[idx] || '').trim() || code.trim(),
+            westname: (names[idx] || "").trim() || code.trim(),
           }))
         } else {
           this.westernDiseaseOptions = []
         }
+        // 同步到名称映射
+        this.westernDiseaseNameMap = {}
+        this.westernDiseaseOptions.forEach((item) => {
+          if (item.westcode && item.westname) {
+            this.westernDiseaseNameMap[item.westcode] = item.westname
+          }
+        })
 
         this.editData = {
           ...detail,
@@ -680,6 +850,12 @@ export default {
           acupoints: [],
           statistics: {},
         }
+        this.key1 = ""
+        this.key2 = ""
+        this.key3 = ""
+        this.diseaseList = []
+        this.symptomList = []
+        this.therapyList = []
         this.showEditDialog = true
       }
     },
@@ -689,14 +865,14 @@ export default {
     },
     async onAcupointSave(treatmentList, institutionInfo) {
       // 从下拉选项中查找选中项的名称
-      const selectedDisease = this.diseaseOptions.find(
-        o => o.disid === this.editData.chineseDisease
+      const selectedDisease = this.diseaseList.find(
+        (o) => String(o.disid) === this.editData.chineseDisease,
       )
-      const selectedSymptom = this.symptomOptions.find(
-        o => o.symid === this.editData.symptomid
+      const selectedSymptom = this.symptomList.find(
+        (o) => o.$code === this.editData.symptomid,
       )
-      const selectedTherapy = this.therapyOptions.find(
-        o => o.therapyCode === this.editData.therapyCode
+      const selectedTherapy = this.therapyList.find(
+        (o) => o.$code === this.editData.therapyCode,
       )
       const params = {
         name: this.editData.name,
@@ -710,18 +886,18 @@ export default {
         // 疾病信息
         disId: this.editData.chineseDisease || undefined,
         disName: selectedDisease
-          ? selectedDisease.disname
+          ? selectedDisease.$name
           : this.editData.chineseDiseaseName || '',
         disCode: this.editData.disCode || '',
         // 证型信息
         synName: selectedSymptom
-          ? selectedSymptom.symname
+          ? selectedSymptom.$name
           : this.editData.symptomName || '',
         synCode: this.editData.symptomid || '',
         // 治法信息
         theCode: this.editData.therapyCode || '',
         theName: selectedTherapy
-          ? selectedTherapy.therapy
+          ? selectedTherapy.$name
           : this.editData.therapyName || '',
         // 西医诊断
         westernCode: Array.isArray(this.editData.westernDisease)
@@ -905,9 +1081,19 @@ export default {
       min-width: 0;
 
       .el-input,
-      .el-select {
+      .el-select,
+      .el-autocomplete {
         width: 100%;
       }
+
+      .el-popover__reference-wrapper {
+        display: block !important;
+        width: 100%;
+
+        .el-input {
+          width: 100%;
+        }
+      }
     }
   }
 }
@@ -920,6 +1106,98 @@ export default {
   flex-direction: column;
 }
 
+.autocomplete-disabled {
+  color: #999 !important;
+  cursor: not-allowed !important;
+  pointer-events: none;
+}
+
+.el-input.invalid::v-deep {
+  input {
+    color: #ff0000;
+  }
+}
+
+.option-list {
+  margin: -12px -12px;
+  max-height: 300px;
+  overflow: auto;
+  .active {
+    color: #5386f6;
+  }
+  li {
+    padding: 8px 12px;
+    list-style: none;
+    font-size: 14px;
+    box-sizing: border-box;
+    width: 100%;
+    color: #606266;
+    font-weight: 500;
+    cursor: pointer;
+  }
+  li:hover {
+    background: #f5f7fa;
+  }
+}
+
+.suffix {
+  height: 100%;
+  padding-right: 5px;
+  display: flex;
+  align-items: center;
+  i {
+    cursor: pointer;
+    color: #c0c4cc;
+  }
+  i:hover {
+    color: #909399;
+  }
+}
+
+.western-disease-input-wrapper {
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  padding: 2px 4px;
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  min-height: 28px;
+  cursor: text;
+
+  .el-tag {
+    margin: 2px 4px 2px 0;
+    max-width: 200px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+
+  .el-autocomplete {
+    flex: 1;
+    min-width: 80px;
+  }
+
+  ::v-deep .el-input__inner {
+    border: none !important;
+    padding: 0 4px;
+    height: 24px;
+    line-height: 24px;
+    background: transparent;
+  }
+
+  ::v-deep .el-input__suffix {
+    display: flex;
+    align-items: center;
+  }
+
+  &:hover {
+    border-color: #c0c4cc;
+  }
+
+  &:focus-within {
+    border-color: #409eff;
+  }
+}
+
 .dialog-scroll-area ::v-deep .recipe-acupoint-wrapper {
   flex: 1;
   display: flex;
@@ -956,30 +1234,35 @@ export default {
   .today-table {
     height: 100%;
 
-    .operation {
-      display: flex;
-      align-items: center;
-      justify-content: space-around;
-      flex-wrap: wrap;
-
-      div {
-        width: 60px;
-        height: 24px;
-        background: #5386f6;
-        border-radius: 2px;
-        font-size: 14px;
-        font-family: PingFang SC;
-        font-weight: 400;
-        color: #ffffff;
-        cursor: pointer;
-        margin: 5px 0;
-      }
+    .find-detail {
+      width: 60px;
+      height: 24px;
+      border: 1px solid #ffae45;
+      border-radius: 2px;
+      text-align: center;
+      color: #fff;
+      font-size: 14px;
+      cursor: pointer;
+      margin: 0 2px;
+      background: #ffae45;
+    }
 
-      .bg-yellow {
-        width: 60px;
-        height: 24px;
-        background: #ffae45;
-      }
+    .find-fill {
+      background: #5386f6 !important;
+      color: #fff !important;
+      border: 1px solid #5386f6;
+    }
+
+    .find-fill1 {
+      background: #ff6245;
+      color: #fff !important;
+      border: 1px solid #ff6245;
+    }
+
+    .find-fill2 {
+      background: #68a8ff !important;
+      color: #fff !important;
+      border: 1px solid #68a8ff;
     }
   }
 }

+ 407 - 319
src/views/business/SuitableTech.vue

@@ -291,69 +291,96 @@
               <span style="opacity:0">*</span>
               <div class="name">中医病名:</div>
               <div class="input">
-                <el-autocomplete
-                  size="mini"
-                  v-model="editData.chineseDiseaseName"
-                  :fetch-suggestions="queryDisease"
-                  value-key="disname"
-                  placeholder="请搜索选择"
-                  clearable
-                  :trigger-on-focus="true"
-                  @select="onDiseaseSelect"
-                  @clear="onDiseaseClear"
-                >
-                  <template slot-scope="{ item }">
-                    <div :class="{ 'autocomplete-disabled': item._disabled }">
-                      {{ item.disname }}
+                <el-popover placement="bottom" width="180" trigger="focus" :close-delay="100">
+                  <el-input
+                    :class="{invalid: editData.chineseDiseaseName && (!editData.disCode)}"
+                    size="mini"
+                    slot="reference"
+                    :placeholder="key1?key1:'中医病名'"
+                    v-model="key1"
+                    ref="zybm"
+                    @input="getDiseaseList(key1)"
+                    @focus="handleFocus('bm')"
+                    @keydown.enter.native="handleEnter('bm')"
+                  >
+                    <div slot="suffix" class="suffix">
+                      <i class="el-icon-arrow-down" v-if="!key1"></i>
+                      <i class="el-icon-circle-close" v-else @click="clearBm"></i>
                     </div>
-                  </template>
-                </el-autocomplete>
+                  </el-input>
+                  <ul class="option-list">
+                    <li
+                      v-for="item in diseaseList"
+                      :key="item.$uid"
+                      :class="{ active: editData.chineseDiseaseName === item.$name }"
+                      @click="handleBm(item)"
+                    >{{ item.$name }}</li>
+                  </ul>
+                </el-popover>
               </div>
             </div>
              <div class="form-item flex flex-col-center form-item-sixth">
               <span style="opacity:0">*</span>
               <div class="name">证型:</div>
               <div class="input">
-                <el-autocomplete
-                  size="mini"
-                  v-model="editData.symptomName"
-                  :fetch-suggestions="querySymptom"
-                  value-key="symname"
-                  placeholder="请搜索选择"
-                  clearable
-                  :trigger-on-focus="true"
-                  @select="onSymptomSelect"
-                  @clear="onSymptomClear"
-                >
-                  <template slot-scope="{ item }">
-                    <div :class="{ 'autocomplete-disabled': item._disabled }">
-                      {{ item.symname }}
+                <el-popover placement="bottom" width="180" trigger="focus" :close-delay="100">
+                  <el-input
+                    :class="{invalid: editData.symptomName && !editData.symptomCode}"
+                    size="mini"
+                    slot="reference"
+                    :placeholder="key2?key2:'中医证型'"
+                    v-model="key2"
+                    ref="zhengxing"
+                    @input="getSymptomList(key2)"
+                    @focus="handleFocus('zx')"
+                    @keydown.enter.native="handleEnter('zx')"
+                  >
+                    <div slot="suffix" class="suffix">
+                      <i class="el-icon-arrow-down" v-if="!key2"></i>
+                      <i class="el-icon-circle-close" v-else @click="clearZx"></i>
                     </div>
-                  </template>
-                </el-autocomplete>
+                  </el-input>
+                  <ul class="option-list">
+                    <li
+                      v-for="item in symptomList"
+                      :key="item.$uid"
+                      :class="{ active: editData.symptomName === item.$name, matched: item.isMatched }"
+                      @click="handleZx(item)"
+                    >{{ item.$name }}</li>
+                  </ul>
+                </el-popover>
               </div>
             </div>
             <div class="form-item flex flex-col-center form-item-sixth">
               <span style="opacity:0">*</span>
               <div class="name">治法:</div>
               <div class="input">
-                <el-autocomplete
-                  size="mini"
-                  v-model="editData.therapyName"
-                  :fetch-suggestions="queryTherapy"
-                  value-key="therapy"
-                  placeholder="请搜索选择"
-                  clearable
-                  :trigger-on-focus="true"
-                  @select="onTherapySelect"
-                  @clear="onTherapyClear"
-                >
-                  <template slot-scope="{ item }">
-                    <div :class="{ 'autocomplete-disabled': item._disabled }">
-                      {{ item.therapy }}
+                <el-popover placement="bottom" width="180" trigger="focus" :close-delay="100" v-model="popoverZF">
+                  <el-input
+                    :class="{invalid: editData.therapyName && !editData.therapyCode}"
+                    size="mini"
+                    slot="reference"
+                    :placeholder="key3?key3:'中医治法'"
+                    v-model="key3"
+                    ref="zhifa"
+                    @input="getTherapyList(key3)"
+                    @focus="handleFocus('zf')"
+                    @keydown.enter.native="handleEnter('zf')"
+                  >
+                    <div slot="suffix" class="suffix">
+                      <i class="el-icon-arrow-down" v-if="!key3"></i>
+                      <i class="el-icon-circle-close" v-else @click="clearZf"></i>
                     </div>
-                  </template>
-                </el-autocomplete>
+                  </el-input>
+                  <ul class="option-list">
+                    <li
+                      v-for="item in therapyList"
+                      :key="item.$uid"
+                      :class="{ active: editData.therapyName === item.$name, matched: item.isMatched }"
+                      @click="handleZf(item)"
+                    >{{ item.$name }}</li>
+                  </ul>
+                </el-popover>
               </div>
             </div>
             <div class="form-item flex flex-col-center form-item-sixth">
@@ -387,11 +414,11 @@
             </div>
            
             <!-- 第二行:西医诊断、功效与适应症 -->
-            <div class="form-item flex flex-col-center form-item-half">
+            <div class="form-item flex form-item-half" style="align-items: flex-start;">
               <span style="opacity:0">*</span>
               <div class="name">西医诊断:</div>
               <div class="input">
-                <div class="western-disease-tags" v-if="editData.westernDisease.length">
+                <div class="western-disease-input-wrapper" @click="$refs.wdAutocomplete.focus()">
                   <el-tag
                     v-for="code in editData.westernDisease"
                     :key="code"
@@ -399,23 +426,24 @@
                     closable
                     @close="removeWesternDisease(code)"
                   >{{ westernDiseaseNameMap[code] || code }}</el-tag>
+                  <el-autocomplete
+                    ref="wdAutocomplete"
+                    size="mini"
+                    v-model="westernDiseaseSearch"
+                    :fetch-suggestions="queryWesternDisease"
+                    value-key="westname"
+                    :placeholder="editData.westernDisease.length ? '' : '请搜索选择'"
+                    clearable
+                    :trigger-on-focus="true"
+                    @select="onWesternDiseaseSelect"
+                  >
+                    <template slot-scope="{ item }">
+                      <div :class="{ 'autocomplete-disabled': item._disabled }">
+                        {{ item.westname }}
+                      </div>
+                    </template>
+                  </el-autocomplete>
                 </div>
-                <el-autocomplete
-                  size="mini"
-                  v-model="westernDiseaseSearch"
-                  :fetch-suggestions="queryWesternDisease"
-                  value-key="westname"
-                  placeholder="请搜索选择"
-                  clearable
-                  :trigger-on-focus="true"
-                  @select="onWesternDiseaseSelect"
-                >
-                  <template slot-scope="{ item }">
-                    <div :class="{ 'autocomplete-disabled': item._disabled }">
-                      {{ item.westname }}
-                    </div>
-                  </template>
-                </el-autocomplete>
               </div>
             </div>
             <div class="form-item flex flex-col-center form-item-half">
@@ -483,73 +511,35 @@
         <div class="dialog-back-btn" @click="showViewDialog = false">返回</div>
         <div class="dialog-scroll-area">
           <div class="dialog-form flex-wrap">
-            <div class="form-item flex flex-col-center">
+            <!-- 第一行:方名、中医病名、证型、治法、共享至、处方出处 -->
+            <div class="form-item flex flex-col-center form-item-sixth">
               <div class="name">方名:</div>
               <div class="input">
-                <el-input
-                  size="mini"
-                  v-model="viewData.name"
-                  placeholder="-"
-                  disabled
-                ></el-input>
+                <el-input size="mini" v-model="viewData.name" placeholder="-" disabled></el-input>
               </div>
             </div>
-            <div class="form-item flex flex-col-center">
+            <div class="form-item flex flex-col-center form-item-sixth">
+              <span style="opacity:0">*</span>
               <div class="name">中医病名:</div>
               <div class="input">
-                <el-input
-                  size="mini"
-                  v-model="viewData.disName"
-                  placeholder="-"
-                  disabled
-                ></el-input>
+                <el-input size="mini" v-model="viewData.disName" placeholder="-" disabled></el-input>
               </div>
             </div>
-            <div class="form-item flex flex-col-center">
+            <div class="form-item flex flex-col-center form-item-sixth">
+              <span style="opacity:0">*</span>
               <div class="name">证型:</div>
               <div class="input">
-                <el-input
-                  size="mini"
-                  v-model="viewData.synName"
-                  placeholder="-"
-                  disabled
-                ></el-input>
+                <el-input size="mini" v-model="viewData.synName" placeholder="-" disabled></el-input>
               </div>
             </div>
-            <div class="form-item flex flex-col-center">
+            <div class="form-item flex flex-col-center form-item-sixth">
+              <span style="opacity:0">*</span>
               <div class="name">治法:</div>
               <div class="input">
-                <el-input
-                  size="mini"
-                  v-model="viewData.theName"
-                  placeholder="-"
-                  disabled
-                ></el-input>
-              </div>
-            </div>
-            <div class="form-item flex flex-col-center">
-              <div class="name">西医诊断:</div>
-              <div class="input">
-                <el-input
-                  size="mini"
-                  v-model="viewData.westernDiag"
-                  placeholder="-"
-                  disabled
-                ></el-input>
-              </div>
-            </div>
-            <div class="form-item flex flex-col-center">
-              <div class="name">功效与适应症:</div>
-              <div class="input">
-                <el-input
-                  size="mini"
-                  v-model="viewData.effect"
-                  placeholder="-"
-                  disabled
-                ></el-input>
+                <el-input size="mini" v-model="viewData.theName" placeholder="-" disabled></el-input>
               </div>
             </div>
-            <div class="form-item flex flex-col-center">
+            <div class="form-item flex flex-col-center form-item-sixth">
               <div class="name">共享至:</div>
               <div class="input">
                 <el-select size="mini" v-model="viewData.showType" disabled>
@@ -559,54 +549,53 @@
                 </el-select>
               </div>
             </div>
-            <div class="form-item flex flex-col-center">
+            <div class="form-item flex flex-col-center form-item-sixth">
               <div class="name">处方出处:</div>
               <div class="input">
-                <el-input
-                  size="mini"
-                  v-model="viewData.provenance"
-                  placeholder="-"
-                  disabled
-                ></el-input>
+                <el-input size="mini" v-model="viewData.provenance" placeholder="-" disabled></el-input>
               </div>
             </div>
-            <div class="form-item flex flex-col-center">
-              <div class="name">统建处方:</div>
+
+            <!-- 第二行:西医诊断、功效与适应症 -->
+            <div class="form-item flex form-item-half" style="align-items: flex-start;">
+              <span style="opacity:0">*</span>
+              <div class="name">西医诊断:</div>
               <div class="input">
-                <el-checkbox
-                  v-model="viewData.isUnifiedChecked"
-                  true-label="1"
-                  false-label="0"
-                  disabled
-                ></el-checkbox>
-              </div>
-            </div>
-            <template v-if="viewData.purposeType === '1'">
-              <div class="form-item flex flex-col-center">
-                <div class="name">医派:</div>
-                <div class="input">
-                  <el-input
+                <div class="western-disease-input-wrapper view-mode">
+                  <el-tag
+                    v-for="(name, idx) in viewWesternDiseaseNames"
+                    :key="idx"
                     size="mini"
-                    v-model="viewData.sectCategory"
-                    placeholder="-"
-                    disabled
-                  ></el-input>
+                  >{{ name }}</el-tag>
                 </div>
               </div>
-              <div class="form-item flex flex-col-center">
-                <div class="name">科室:</div>
-                <div class="input">
-                  <el-input
-                    size="mini"
-                    v-model="viewData.sectDept"
-                    placeholder="-"
-                    disabled
-                  ></el-input>
-                </div>
+            </div>
+            <div class="form-item flex flex-col-center form-item-half">
+              <span style="opacity:0">*</span>
+              <div class="name">功效与适应症:</div>
+              <div class="input">
+                <el-input size="mini" v-model="viewData.effect" placeholder="-" disabled></el-input>
               </div>
-            </template>
+            </div>
+            <!-- 第三行:统建处方 -->
+            <div class="form-item flex flex-col-center">
+              <span style="opacity:0">*</span>
+              <el-checkbox v-model="viewData.isUnifiedChecked" class="unified-checkbox" true-label="1" false-label="0" disabled>统建处方</el-checkbox>
+            </div>
+            <div class="form-item flex flex-col-center" v-if="viewData.purposeType === '1'">
+              <div class="name">医派:</div>
+              <div class="input">
+                <el-input size="mini" v-model="viewData.sectCategory" placeholder="-" disabled></el-input>
+              </div>
+            </div>
+            <div class="form-item flex flex-col-center" v-if="viewData.purposeType === '1'">
+              <div class="name">科室:</div>
+              <div class="input">
+                <el-input size="mini" v-model="viewData.sectDept" placeholder="-" disabled></el-input>
+              </div>
+            </div>
           </div>
-          <SuitableTechDetail ref="techDetail" :detailData="viewData" />
+          <SuitableTechDetail ref="techDetail" :detailData="viewData" :showGuide="true" />
         </div>
       </div>
     </popup>
@@ -621,7 +610,7 @@ import {
   getDiseaseListMethod,
   getSymptomListMethod,
   getTherapyListMethod,
-} from "@/request/api.illness.js";
+} from "@/request/api";
 import {
   getSuitableTechList,
   getSuitableTechInfo,
@@ -646,10 +635,14 @@ export default {
         includeItem: "",
         efficacy: "",
       },
-      // 下拉选项
-      diseaseOptions: [],
-      symptomOptions: [],
-      therapyOptions: [],
+      // 中医诊断下拉
+      diseaseList: [],
+      symptomList: [],
+      therapyList: [],
+      popoverZF: false,
+      key1: "",
+      key2: "",
+      key3: "",
       westernDiseaseOptions: [],
       westernDiseaseNameMap: {},
       westernDiseaseSearch: "",
@@ -657,9 +650,6 @@ export default {
       westernDiseaseHasMore: true,
       projectOptions: [],
       // 下拉加载状态
-      diseaseLoading: false,
-      symptomLoading: false,
-      therapyLoading: false,
       westernDiseaseLoading: false,
       projectLoading: false,
       // 表格数据
@@ -713,140 +703,157 @@ export default {
       const options = this.shareOptions;
       return options.length ? options[0].value : "0";
     },
+    viewWesternDiseaseNames() {
+      if (!this.viewData.westernDiag) return [];
+      return String(this.viewData.westernDiag).split(",").map((s) => s.trim()).filter(Boolean);
+    },
   },
   created() {
     this.getList();
   },
-  methods: {
-    // 中医病名搜索 
-    async queryDisease(queryString, cb) {
-      this._prevDiseaseInput = queryString;
-      try {
-        const { list } = await getDiseaseListMethod(1, 9999, {
-          keyword: queryString || "",
-        });
-        if (list && list.length > 0) {
-          cb(list);
-        } else {
-          cb([{ disname: "暂无数据", _disabled: true }]);
+  watch: {
+    key1: {
+      handler(value, oldVal) {
+        if (this.key1 === "") {
+          this.editData.chineseDisease = "";
+          this.editData.chineseDiseaseName = "";
+          this.editData.disCode = "";
+        }
+        if (!value && oldVal) {
+          this.getSymptomList();
+          this.getTherapyList();
         }
-      } catch (e) {
-        console.error("搜索中医病名失败", e);
-        cb([{ disname: "搜索失败,请重试", _disabled: true }]);
       }
     },
-    onDiseaseSelect(item) {
-      if (item._disabled) {
-        // 恢复为点击前的输入文字
-        this.$nextTick(() => {
-          this.editData.chineseDiseaseName = this._prevDiseaseInput || "";
-        });
-        return;
+    key2: {
+      handler(value, oldVal) {
+        if (this.key2 === "") {
+          this.editData.symptomName = "";
+          this.editData.symptomid = "";
+          this.editData.symptomCode = "";
+        }
+        if (!value && oldVal) {
+          this.getSymptomList();
+          this.getTherapyList();
+        }
       }
+    },
+    key3: {
+      handler(value, oldVal) {
+        if (this.key3 === "") {
+          this.editData.therapyName = "";
+          this.editData.therapyCode = "";
+        }
+        if (!value && oldVal) {
+          this.getTherapyList();
+        }
+      }
+    },
+  },
+  methods: {
+    /**
+     * focus 事件
+     * @param {'bm'|'zx'|'zf'} type
+     */
+    handleFocus(type) {
+      if (type === 'bm' && this.diseaseList.length === 0) this.getDiseaseList(this.key1);
+      else if (type === 'zx' && this.symptomList.length === 0) this.getSymptomList(this.key2);
+      else if (type === 'zf' && this.therapyList.length === 0) this.getTherapyList(this.key3);
+    },
+    /**
+     * enter事件
+     * @param {'bm'|'zx'|'zf'} type
+     */
+    handleEnter(type) {
+      if (type === 'bm' && this.diseaseList.length) this.handleBm(this.diseaseList[0]);
+      else if (type === 'zx' && this.symptomList.length) this.handleZx(this.symptomList[0]);
+      else if (type === 'zf' && this.therapyList.length) this.handleZf(this.therapyList[0]);
+    },
+    //获取病名数据
+    async getDiseaseList(keyword = '') {
+      const {total, list} = await getDiseaseListMethod(1, 9999, {keyword}).catch(() => ({total: 0, list: []}));
+      this.diseaseList = list;
+    },
+    // 病名选中
+    async handleBm(item) {
       this.editData.chineseDisease = String(item.disid);
-      this.editData.chineseDiseaseName = item.disname;
-      this.editData.disCode = item.disCode || "";
-      // 联动加载证型
-      this.editData.symptomid = "";
-      this.editData.symptomName = "";
-      this.symptomOptions = [];
-      // 联动清空治法
-      this.editData.therapyCode = "";
-      this.editData.therapyName = "";
-      this.therapyOptions = [];
-      this.querySymptom("", () => {});
+      this.editData.chineseDiseaseName = item.$name;
+      this.editData.disCode = item.$code;
+      this.key1 = item.$name;
+      this.$refs.zybm.blur();
+
+      this.clearZx();
+
+      if (!this.key1) return;
+      this.$refs.zhengxing && this.$refs.zhengxing.focus();
+      await this.getSymptomList();
     },
-    onDiseaseClear() {
+    // 清空病名
+    clearBm() {
       this.editData.chineseDisease = "";
       this.editData.chineseDiseaseName = "";
       this.editData.disCode = "";
-      this.editData.symptomid = "";
-      this.editData.symptomName = "";
-      this.symptomOptions = [];
-      this.editData.therapyCode = "";
-      this.editData.therapyName = "";
-      this.therapyOptions = [];
+      this.key1 = "";
+      this.clearZx();
     },
-    // ========== 证型搜索 ==========
-    async querySymptom(queryString, cb) {
-      this._prevSymptomInput = queryString;
-      try {
-        const params = { keyword: queryString || "" };
-        if (this.editData.chineseDisease)
-          params.disid = this.editData.chineseDisease;
-        if (this.editData.disCode) params.disCode = this.editData.disCode;
-        const { list } = await getSymptomListMethod(1, 9999, params);
-        if (list && list.length > 0) {
-          cb(list);
-        } else {
-          cb([{ symname: "暂无数据", _disabled: true }]);
-        }
-      } catch (e) {
-        console.error("搜索证型失败", e);
-        cb([{ symname: "搜索失败,请重试", _disabled: true }]);
-      }
+    // 获取证型数据
+    async getSymptomList(keyword = '') {
+      const {total, list} = await getSymptomListMethod(1, 9999, {
+        keyword,
+        disid: this.editData.chineseDisease,
+        disCode: this.editData.disCode,
+      }).catch(() => ({total: 0, list: []}));
+      this.symptomList = list;
     },
-    onSymptomSelect(item) {
-      if (item._disabled) {
-        this.$nextTick(() => {
-          this.editData.symptomName = this._prevSymptomInput || "";
-        });
-        return;
-      }
-      this.editData.symptomid = String(item.symid);
-      this.editData.symptomName = item.symname;
-      this.editData.symptomCode = item.synCode || "";
-      // 联动清空治法并重新加载
-      this.editData.therapyCode = "";
-      this.editData.therapyName = "";
-      this.therapyOptions = [];
-      if (item.symid) {
-        this.queryTherapy("", () => {});
-      }
+    // 证型选中
+    async handleZx(item) {
+      this.editData.symptomName = item.$name;
+      this.editData.symptomid = item.$code;
+      this.editData.symptomCode = item.$code;
+      this.key2 = item.$name;
+      this.$refs.zhengxing.blur();
+
+      this.clearZf();
+      this.$refs.zhifa && this.$refs.zhifa.focus();
+      await this.getTherapyList();
     },
-    onSymptomClear() {
-      this.editData.symptomid = "";
+    // 清空证型
+    clearZx() {
       this.editData.symptomName = "";
+      this.editData.symptomid = "";
       this.editData.symptomCode = "";
-      // 联动清空治法
-      this.editData.therapyCode = "";
-      this.editData.therapyName = "";
-      this.therapyOptions = [];
+      this.key2 = "";
+      this.clearZf();
     },
-    // ========== 治法搜索 ==========
-    async queryTherapy(queryString, cb) {
-      this._prevTherapyInput = queryString;
-      try {
-        const params = { keyword: queryString || "" };
-        if (this.editData.disCode) params.disCode = this.editData.disCode;
-        if (this.editData.symptomid) {
-          params.symptomCode = this.editData.symptomid;
-          params.symid = this.editData.symptomid;
-        }
-        const { list } = await getTherapyListMethod(1, 9999, params);
-        if (list && list.length > 0) {
-          cb(list);
-        } else {
-          cb([{ therapy: "暂无数据", _disabled: true }]);
-        }
-      } catch (e) {
-        console.error("搜索治法失败", e);
-        cb([{ therapy: "搜索失败,请重试", _disabled: true }]);
-      }
+    /**
+     * 获取治法
+     * @param keyword
+     * @return {Promise<void>}
+     */
+    async getTherapyList(keyword = '') {
+      const {total, list} = await getTherapyListMethod(1, 9999, {
+        keyword,
+        disCode: this.editData.disCode,
+        symptomCode: this.editData.symptomid,
+        symid: this.editData.symptomid,
+      }).catch(() => ({total: 0, list: []}));
+      this.therapyList = list;
     },
-    onTherapySelect(item) {
-      if (item._disabled) {
-        this.$nextTick(() => {
-          this.editData.therapyName = this._prevTherapyInput || "";
-        });
-        return;
-      }
-      this.editData.therapyCode = String(item.therapyCode);
-      this.editData.therapyName = item.therapy;
+    // 治法选中
+    handleZf(item) {
+      this.editData.therapyName = item.$name;
+      this.editData.therapyCode = item.$code;
+      this.key3 = item.$name;
+      this.$nextTick(() => {
+        this.$refs.zhifa && this.$refs.zhifa.blur();
+        this.popoverZF = false;
+      });
     },
-    onTherapyClear() {
-      this.editData.therapyCode = "";
+    // 清空治法
+    clearZf() {
       this.editData.therapyName = "";
+      this.editData.therapyCode = "";
+      this.key3 = "";
     },
     // ========== 西医诊断搜索(分页) ==========
     async searchWesternDisease(query) {
@@ -1098,37 +1105,14 @@ export default {
           this.editLoading = false;
         }
 
-        // 用详情数据构造下拉选项,确保回显时下拉框能显示名称
-        this.diseaseOptions =
-          detail.disId != null
-            ? [
-                {
-                  disid: detail.disId,
-                  disname: detail.disName,
-                  disCode: detail.disCode,
-                },
-              ]
-            : [];
-        this.symptomOptions =
-          detail.synCode != null
-            ? [
-                {
-                  symid: detail.synCode,
-                  symname: detail.synName,
-                  synCode: detail.synCode,
-                },
-              ]
-            : [];
-        this.therapyOptions =
-          detail.theCode != null
-            ? [
-                {
-                  therapyCode: detail.theCode,
-                  therapy: detail.theName,
-                  therapyName: detail.theName,
-                },
-              ]
-            : [];
+        // 初始化搜索关键字,用于回显
+        this.key1 = detail.disName || "";
+        this.key2 = detail.synName || "";
+        this.key3 = detail.theName || "";
+        // 加载下拉列表数据
+        if (this.key1) this.getDiseaseList(this.key1);
+        if (this.key2) this.getSymptomList(this.key2);
+        if (this.key3) this.getTherapyList(this.key3);
         if (detail.westernCode) {
           const codes = String(detail.westernCode).split(",");
           const names = detail.westernDiag
@@ -1207,6 +1191,12 @@ export default {
           acupoints: [],
           statistics: {},
         };
+        this.key1 = "";
+        this.key2 = "";
+        this.key3 = "";
+        this.diseaseList = [];
+        this.symptomList = [];
+        this.therapyList = [];
         this.showEditDialog = true;
       }
     },
@@ -1216,14 +1206,14 @@ export default {
     },
     async onAcupointSave(treatmentList, institutionInfo) {
       // 从下拉选项中查找选中项的名称
-      const selectedDisease = this.diseaseOptions.find(
-        (o) => o.disid === this.editData.chineseDisease,
+      const selectedDisease = this.diseaseList.find(
+        (o) => String(o.disid) === this.editData.chineseDisease,
       );
-      const selectedSymptom = this.symptomOptions.find(
-        (o) => o.symid === this.editData.symptomid,
+      const selectedSymptom = this.symptomList.find(
+        (o) => o.$code === this.editData.symptomid,
       );
-      const selectedTherapy = this.therapyOptions.find(
-        (o) => o.therapyCode === this.editData.therapyCode,
+      const selectedTherapy = this.therapyList.find(
+        (o) => o.$code === this.editData.therapyCode,
       );
       const params = {
         // 必填字段
@@ -1238,19 +1228,19 @@ export default {
         // 疾病信息
         disId: this.editData.chineseDisease || undefined,
         disName: selectedDisease
-          ? selectedDisease.disname
+          ? selectedDisease.$name
           : this.editData.chineseDiseaseName || "",
         disCode: this.editData.disCode || "",
         // 证型信息
         synId: undefined,
         synName: selectedSymptom
-          ? selectedSymptom.symname
+          ? selectedSymptom.$name
           : this.editData.symptomName || "",
         synCode: this.editData.symptomid || "",
         // 治法信息
         theCode: this.editData.therapyCode || "",
         theName: selectedTherapy
-          ? selectedTherapy.therapy
+          ? selectedTherapy.$name
           : this.editData.therapyName || "",
         // 西医诊断
         westernCode: Array.isArray(this.editData.westernDisease)
@@ -1576,6 +1566,15 @@ export default {
       .el-autocomplete {
         width: 100%;
       }
+
+      .el-popover__reference-wrapper {
+        display: block !important;
+        width: 100%;
+
+        .el-input {
+          width: 100%;
+        }
+      }
     }
 
     .unified-checkbox {
@@ -1605,11 +1604,100 @@ export default {
   pointer-events: none;
 }
 
-.western-disease-tags {
-  margin-bottom: 4px;
+.el-input.invalid::v-deep {
+  input {
+    color: #ff0000;
+  }
+}
+
+.option-list {
+  margin: -12px -12px;
+  max-height: 300px;
+  overflow: auto;
+  .active {
+    color: #5386f6;
+  }
+  li {
+    padding: 8px 12px;
+    list-style: none;
+    font-size: 14px;
+    box-sizing: border-box;
+    width: 100%;
+    color: #606266;
+    font-weight: 500;
+    cursor: pointer;
+  }
+  li:hover {
+    background: #f5f7fa;
+  }
+}
+
+.suffix {
+  height: 100%;
+  padding-right: 5px;
+  display: flex;
+  align-items: center;
+  i {
+    cursor: pointer;
+    color: #c0c4cc;
+  }
+  i:hover {
+    color: #909399;
+  }
+}
+
+.western-disease-input-wrapper {
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  padding: 2px 4px;
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  min-height: 28px;
+  cursor: text;
+
   .el-tag {
-    margin-right: 4px;
-    margin-bottom: 2px;
+    margin: 2px 4px 2px 0;
+    max-width: 200px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+
+  .el-autocomplete {
+    flex: 1;
+    min-width: 80px;
+  }
+
+  ::v-deep .el-input__inner {
+    border: none !important;
+    padding: 0 4px;
+    height: 24px;
+    line-height: 24px;
+    background: transparent;
+  }
+
+  ::v-deep .el-input__suffix {
+    display: flex;
+    align-items: center;
+  }
+
+  &:hover {
+    border-color: #c0c4cc;
+  }
+
+  &:focus-within {
+    border-color: #409eff;
+  }
+
+  &.view-mode {
+    cursor: default;
+    background: #f5f7fa;
+
+    .el-tag {
+      background-color: #f0f2f5;
+      border-color: #e4e7ed;
+      color: #909399;
+    }
   }
 }
 

+ 2 - 1
src/views/business/components/AcupointTable.vue

@@ -363,8 +363,9 @@
               <div
               class="item flex-vertical-center-l"
               style="align-items: flex-start;"
+              v-if="showGuide"
             >
-              <span> 配穴指南:</span>
+              <span><span style="opacity:0">*</span> 配穴指南:</span>
               <div style="flex: 1;">
                 <el-input
                   size="mini"

+ 21 - 0
src/views/business/components/SuitableTechDetail.vue

@@ -179,6 +179,23 @@
       <div class="table-bottom">
         <div class="table-b-header">
           <div class="header-about">
+            <div
+              class="item flex-vertical-center-l"
+              style="align-items: flex-start;"
+              v-if="showGuide"
+            >
+              <span>配穴指南:</span>
+              <div style="flex: 1;">
+                <el-input
+                  size="mini"
+                  placeholder="-"
+                  :value="activeTreatment.pointMatchGuide"
+                  type="textarea"
+                  autosize
+                  disabled
+                ></el-input>
+              </div>
+            </div>
             <div
               class="item flex-vertical-center-l"
               style="align-items: flex-start;"
@@ -381,6 +398,10 @@ export default {
       type: Object,
       default: () => ({}),
     },
+    showGuide: {
+      type: Boolean,
+      default: false,
+    },
   },
   data() {
     return {