SuitableTech.vue 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782
  1. <template>
  2. <div class="suitable-tech">
  3. <!-- 搜索项 -->
  4. <div class="screening">
  5. <div class="screening-title flex-vertical-center-l">
  6. <img src="~@/assets/filters.png" alt />
  7. </div>
  8. <div style="flex:1">
  9. <div class="screening-form flex-vertical-center-l flex-wrap">
  10. <div class="screening-item flex-vertical-center-l">
  11. <span>方名:</span>
  12. <div class="input">
  13. <el-input
  14. size="mini"
  15. v-model="searchData.name"
  16. placeholder="请输入"
  17. clearable
  18. ></el-input>
  19. </div>
  20. </div>
  21. <div class="screening-item flex-vertical-center-l">
  22. <span>共享至:</span>
  23. <div class="input">
  24. <el-radio-group v-model="searchData.shareType" size="mini">
  25. <el-radio label="0">个人</el-radio>
  26. <el-radio label="1">科室</el-radio>
  27. <el-radio label="2">机构</el-radio>
  28. </el-radio-group>
  29. </div>
  30. </div>
  31. <div class="screening-item flex-vertical-center-l">
  32. <span>{{ userNameLabel }}:</span>
  33. <div class="input">
  34. <el-input
  35. size="mini"
  36. v-model="searchData.userName"
  37. placeholder="请输入"
  38. clearable
  39. ></el-input>
  40. </div>
  41. </div>
  42. <div class="screening-item flex-vertical-center-l">
  43. <span>是否统建处方:</span>
  44. <div class="input">
  45. <el-select
  46. size="mini"
  47. v-model="searchData.isUnified"
  48. placeholder="请选择"
  49. >
  50. <el-option label="是" value="1"></el-option>
  51. <el-option label="否" value="0"></el-option>
  52. </el-select>
  53. </div>
  54. </div>
  55. </div>
  56. <div class="screening-form flex-vertical-center-l flex-wrap">
  57. <div class="screening-item flex-vertical-center-l" v-if="searchData.isUnified === '1'">
  58. <span>医派:</span>
  59. <div class="input">
  60. <el-input
  61. size="mini"
  62. v-model="searchData.medicalSchool"
  63. placeholder="请输入"
  64. clearable
  65. ></el-input>
  66. </div>
  67. </div>
  68. <div class="screening-item flex-vertical-center-l" v-if="searchData.isUnified === '1'">
  69. <span>科室:</span>
  70. <div class="input">
  71. <el-input
  72. size="mini"
  73. v-model="searchData.department"
  74. placeholder="请输入"
  75. clearable
  76. ></el-input>
  77. </div>
  78. </div>
  79. <div class="screening-item flex-vertical-center-l">
  80. <span>涵盖项目:</span>
  81. <div class="input">
  82. <el-select
  83. size="mini"
  84. v-model="searchData.includeItem"
  85. placeholder="请搜索选择"
  86. clearable
  87. filterable
  88. :loading="projectLoading"
  89. loading-text="搜索中..."
  90. :filter-method="searchProject"
  91. @focus="searchProject('')"
  92. >
  93. <el-option
  94. v-for="(item, idx) in projectOptions"
  95. :key="idx"
  96. :label="item.itemName"
  97. :value="item.pid"
  98. ></el-option>
  99. </el-select>
  100. </div>
  101. </div>
  102. <div class="screening-item flex-vertical-center-l">
  103. <span>功效与适应症:</span>
  104. <div class="input">
  105. <el-input
  106. size="mini"
  107. v-model="searchData.efficacy"
  108. placeholder="请输入"
  109. clearable
  110. ></el-input>
  111. </div>
  112. </div>
  113. <el-button type="primary" size="mini" @click="search()">搜索</el-button>
  114. <el-button type="warning" size="mini" @click="clearFilter()">清空</el-button>
  115. <el-button type="primary" size="mini" @click="openEditDialog()">新增</el-button>
  116. </div>
  117. </div>
  118. </div>
  119. <!-- 列表表格数据 -->
  120. <div class="table">
  121. <div class="today-table">
  122. <div class="table-container">
  123. <el-table
  124. :data="tableData"
  125. stripe
  126. style="width: 100%"
  127. border
  128. height="100%"
  129. row-key="pid"
  130. :expand-row-keys="expandRows"
  131. @expand-change="handleExpandChange"
  132. v-loading="tableLoading"
  133. >
  134. <el-table-column type="expand">
  135. <template slot-scope="props">
  136. <div class="expand-table">
  137. <el-table
  138. :data="props.row.subList"
  139. border
  140. size="mini"
  141. style="width: 100%"
  142. >
  143. <el-table-column
  144. prop="index"
  145. label="序号"
  146. width="60"
  147. align="center"
  148. ></el-table-column>
  149. <el-table-column
  150. prop="treatItemName"
  151. label="项目名称"
  152. align="center"
  153. ></el-table-column>
  154. <el-table-column
  155. label="分类"
  156. width="120"
  157. align="center"
  158. >
  159. <template slot-scope="scope">
  160. {{ formatTreatClass(scope.row.treatClass) }}
  161. </template>
  162. </el-table-column>
  163. <el-table-column label="穴位/部位/耳穴/经络" align="center">
  164. <template slot-scope="scope">
  165. {{ formatDetailList(scope.row) }}
  166. </template>
  167. </el-table-column>
  168. <el-table-column
  169. prop="isUpdate"
  170. label="是否可修改"
  171. width="100"
  172. align="center"
  173. >
  174. <template slot-scope="scope">
  175. {{ scope.row.isUpdate === "1" ? "是" : "否" }}
  176. </template>
  177. </el-table-column>
  178. </el-table>
  179. </div>
  180. </template>
  181. </el-table-column>
  182. <el-table-column
  183. prop="index"
  184. label="序号"
  185. width="60"
  186. align="center"
  187. ></el-table-column>
  188. <el-table-column
  189. prop="name"
  190. label="方名"
  191. min-width="120"
  192. align="center"
  193. ></el-table-column>
  194. <el-table-column
  195. prop="effect"
  196. label="功效与适应症"
  197. min-width="180"
  198. align="center"
  199. show-overflow-tooltip
  200. ></el-table-column>
  201. <el-table-column
  202. prop="provenance"
  203. label="处方出处"
  204. width="100"
  205. align="center"
  206. ></el-table-column>
  207. <el-table-column
  208. prop="showType"
  209. label="共享状态"
  210. width="100"
  211. align="center"
  212. >
  213. <template slot-scope="scope">
  214. <span>{{
  215. scope.row.showType === "0"
  216. ? "个人"
  217. : scope.row.showType === "1"
  218. ? "科室"
  219. : scope.row.showType === "2"
  220. ? "机构"
  221. : "-"
  222. }}</span>
  223. </template>
  224. </el-table-column>
  225. <el-table-column
  226. prop="purposeType"
  227. label="是否统建处方"
  228. width="110"
  229. align="center"
  230. >
  231. <template slot-scope="scope">
  232. {{ scope.row.purposeType === "1" ? "是" : "否" }}
  233. </template>
  234. </el-table-column>
  235. <el-table-column
  236. prop="createUserName"
  237. label="创建者"
  238. width="100"
  239. align="center"
  240. ></el-table-column>
  241. <el-table-column label="操作" width="180" align="center">
  242. <template slot-scope="scope">
  243. <div class="flex-center">
  244. <div class="find-detail find-fill" :class="{ 'is-disabled': scope.row.createUser != currentUserId }" @click="scope.row.createUser == currentUserId && openEditDialog(scope.row)">编辑</div>
  245. <div class="find-detail find-fill1" :class="{ 'is-disabled': scope.row.createUser != currentUserId }" @click="scope.row.createUser == currentUserId && handleDelete(scope.row)">删除</div>
  246. <div class="find-detail find-fill2" @click="handleView(scope.row)">查看</div>
  247. </div>
  248. </template>
  249. </el-table-column>
  250. </el-table>
  251. </div>
  252. <div class="flex-vertical-center-r today-page">
  253. <el-pagination
  254. background
  255. layout=" prev, pager, next, jumper, total"
  256. :total="total"
  257. :page-size="limit"
  258. @current-change="sizeC($event)"
  259. ></el-pagination>
  260. </div>
  261. </div>
  262. </div>
  263. <!-- 新增/编辑弹窗 -->
  264. <popup
  265. distanceTop="5vh"
  266. fullscreen
  267. :showBtns="false"
  268. :showDialog.sync="showEditDialog"
  269. :loading="editLoading"
  270. :title="editData.pid ? '编辑适宜技术协定方' : '新增适宜技术协定方'"
  271. >
  272. <div slot="body" class="dialog-body-wrapper">
  273. <div class="dialog-back-btn" @click="handleEditBack">返回</div>
  274. <div class="dialog-scroll-area">
  275. <div class="dialog-form flex-wrap">
  276. <!-- 第一行:方名、中医病名、共享至、处方出处、证型、治法 -->
  277. <div class="form-item flex flex-col-center form-item-sixth">
  278. <div class="name"><span style="color: red">*</span> 方名:</div>
  279. <div class="input">
  280. <el-input
  281. size="mini"
  282. v-model="editData.name"
  283. placeholder="请输入"
  284. ></el-input>
  285. </div>
  286. </div>
  287. <div class="form-item flex flex-col-center form-item-sixth">
  288. <span style="opacity:0">*</span>
  289. <div class="name">中医病名:</div>
  290. <div class="input">
  291. <el-popover placement="bottom" width="180" trigger="focus" :close-delay="100">
  292. <el-input
  293. :class="{invalid: editData.chineseDiseaseName && (!editData.disCode)}"
  294. size="mini"
  295. slot="reference"
  296. :placeholder="key1?key1:'中医病名'"
  297. v-model="key1"
  298. ref="zybm"
  299. @input="getDiseaseList(key1)"
  300. @focus="handleFocus('bm')"
  301. @keydown.enter.native="handleEnter('bm')"
  302. >
  303. <div slot="suffix" class="suffix">
  304. <i class="el-icon-arrow-down" v-if="!key1"></i>
  305. <i class="el-icon-circle-close" v-else @click="clearBm"></i>
  306. </div>
  307. </el-input>
  308. <ul class="option-list">
  309. <li
  310. v-for="item in diseaseList"
  311. :key="item.$uid"
  312. :class="{ active: editData.chineseDiseaseName === item.$name }"
  313. @click="handleBm(item)"
  314. >{{ item.$name }}</li>
  315. </ul>
  316. </el-popover>
  317. </div>
  318. </div>
  319. <div class="form-item flex flex-col-center form-item-sixth">
  320. <span style="opacity:0">*</span>
  321. <div class="name">证型:</div>
  322. <div class="input">
  323. <el-popover placement="bottom" width="180" trigger="focus" :close-delay="100">
  324. <el-input
  325. :class="{invalid: editData.symptomName && !editData.symptomCode}"
  326. size="mini"
  327. slot="reference"
  328. :placeholder="key2?key2:'中医证型'"
  329. v-model="key2"
  330. ref="zhengxing"
  331. @input="getSymptomList(key2)"
  332. @focus="handleFocus('zx')"
  333. @keydown.enter.native="handleEnter('zx')"
  334. >
  335. <div slot="suffix" class="suffix">
  336. <i class="el-icon-arrow-down" v-if="!key2"></i>
  337. <i class="el-icon-circle-close" v-else @click="clearZx"></i>
  338. </div>
  339. </el-input>
  340. <ul class="option-list">
  341. <li
  342. v-for="item in symptomList"
  343. :key="item.$uid"
  344. :class="{ active: editData.symptomName === item.$name, matched: item.isMatched }"
  345. @click="handleZx(item)"
  346. >{{ item.$name }}</li>
  347. </ul>
  348. </el-popover>
  349. </div>
  350. </div>
  351. <div class="form-item flex flex-col-center form-item-sixth">
  352. <span style="opacity:0">*</span>
  353. <div class="name">治法:</div>
  354. <div class="input">
  355. <el-popover placement="bottom" width="180" trigger="focus" :close-delay="100" v-model="popoverZF">
  356. <el-input
  357. :class="{invalid: editData.therapyName && !editData.therapyCode}"
  358. size="mini"
  359. slot="reference"
  360. :placeholder="key3?key3:'中医治法'"
  361. v-model="key3"
  362. ref="zhifa"
  363. @input="getTherapyList(key3)"
  364. @focus="handleFocus('zf')"
  365. @keydown.enter.native="handleEnter('zf')"
  366. >
  367. <div slot="suffix" class="suffix">
  368. <i class="el-icon-arrow-down" v-if="!key3"></i>
  369. <i class="el-icon-circle-close" v-else @click="clearZf"></i>
  370. </div>
  371. </el-input>
  372. <ul class="option-list">
  373. <li
  374. v-for="item in therapyList"
  375. :key="item.$uid"
  376. :class="{ active: editData.therapyName === item.$name, matched: item.isMatched }"
  377. @click="handleZf(item)"
  378. >{{ item.$name }}</li>
  379. </ul>
  380. </el-popover>
  381. </div>
  382. </div>
  383. <div class="form-item flex flex-col-center form-item-sixth">
  384. <span style="opacity:0">*</span>
  385. <div class="name">共享至:</div>
  386. <div class="input">
  387. <el-select
  388. size="mini"
  389. v-model="editData.showType"
  390. placeholder="请选择"
  391. >
  392. <el-option
  393. v-for="opt in shareOptions"
  394. :key="opt.value"
  395. :label="opt.label"
  396. :value="opt.value"
  397. ></el-option>
  398. </el-select>
  399. </div>
  400. </div>
  401. <div class="form-item flex flex-col-center form-item-sixth">
  402. <span style="opacity:0">*</span>
  403. <div class="name">处方出处:</div>
  404. <div class="input">
  405. <el-input
  406. size="mini"
  407. v-model="editData.source"
  408. placeholder="请输入"
  409. ></el-input>
  410. </div>
  411. </div>
  412. <!-- 第二行:西医诊断、功效与适应症 -->
  413. <div class="form-item flex form-item-half" style="align-items: flex-start;">
  414. <span style="opacity:0">*</span>
  415. <div class="name">西医诊断:</div>
  416. <div class="input">
  417. <div class="western-disease-input-wrapper" @click="$refs.wdAutocomplete.focus()">
  418. <el-tag
  419. v-for="code in editData.westernDisease"
  420. :key="code"
  421. size="mini"
  422. closable
  423. @close="removeWesternDisease(code)"
  424. >{{ westernDiseaseNameMap[code] || code }}</el-tag>
  425. <el-autocomplete
  426. ref="wdAutocomplete"
  427. size="mini"
  428. v-model="westernDiseaseSearch"
  429. :fetch-suggestions="queryWesternDisease"
  430. value-key="westname"
  431. :placeholder="editData.westernDisease.length ? '' : '请搜索选择'"
  432. clearable
  433. :trigger-on-focus="true"
  434. @select="onWesternDiseaseSelect"
  435. >
  436. <template slot-scope="{ item }">
  437. <div :class="{ 'autocomplete-disabled': item._disabled }">
  438. {{ item.westname }}
  439. </div>
  440. </template>
  441. </el-autocomplete>
  442. </div>
  443. </div>
  444. </div>
  445. <div class="form-item flex flex-col-center form-item-half">
  446. <span style="opacity:0">*</span>
  447. <div class="name">功效与适应症:</div>
  448. <div class="input">
  449. <el-input
  450. size="mini"
  451. v-model="editData.efficacy"
  452. placeholder="请输入"
  453. ></el-input>
  454. </div>
  455. </div>
  456. <!-- 第三行:统建处方 -->
  457. <div class="form-item flex flex-col-center">
  458. <span style="opacity:0">*</span>
  459. <el-checkbox v-model="editData.isUnified" class="unified-checkbox">统建处方</el-checkbox>
  460. </div>
  461. <div class="form-item flex flex-col-center" v-if="editData.isUnified">
  462. <div class="name"><span style="color: red">*</span> 医派:</div>
  463. <div class="input">
  464. <el-input
  465. size="mini"
  466. v-model="editData.medicalSchool"
  467. placeholder="请输入"
  468. ></el-input>
  469. </div>
  470. </div>
  471. <div class="form-item flex flex-col-center" v-if="editData.isUnified">
  472. <div class="name"><span style="color: red">*</span> 科室:</div>
  473. <div class="input">
  474. <el-input
  475. size="mini"
  476. v-model="editData.department"
  477. placeholder="请输入"
  478. ></el-input>
  479. </div>
  480. </div>
  481. </div>
  482. <!-- 配穴表格 -->
  483. <AcupointTable
  484. ref="acupointTable"
  485. v-model="editData.acupoints"
  486. :isEditable.sync="editData.isEditable"
  487. :detailTypes.sync="editData.detailTypes"
  488. :statistics.sync="editData.statistics"
  489. :showGuide="true"
  490. @save="onAcupointSave"
  491. />
  492. </div>
  493. </div>
  494. </popup>
  495. <!-- 查看详情弹窗 -->
  496. <popup
  497. distanceTop="5vh"
  498. fullscreen
  499. :showBtns="false"
  500. :showDialog.sync="showViewDialog"
  501. :loading="viewLoading"
  502. title="查看适宜技术协定方"
  503. >
  504. <div slot="body" class="dialog-body-wrapper">
  505. <div class="dialog-back-btn" @click="showViewDialog = false">返回</div>
  506. <div class="dialog-scroll-area">
  507. <div class="dialog-form flex-wrap">
  508. <!-- 第一行:方名、中医病名、证型、治法、共享至、处方出处 -->
  509. <div class="form-item flex flex-col-center form-item-sixth">
  510. <div class="name">方名:</div>
  511. <div class="input">
  512. <el-input size="mini" v-model="viewData.name" placeholder="-" disabled></el-input>
  513. </div>
  514. </div>
  515. <div class="form-item flex flex-col-center form-item-sixth">
  516. <span style="opacity:0">*</span>
  517. <div class="name">中医病名:</div>
  518. <div class="input">
  519. <el-input size="mini" v-model="viewData.disName" placeholder="-" disabled></el-input>
  520. </div>
  521. </div>
  522. <div class="form-item flex flex-col-center form-item-sixth">
  523. <span style="opacity:0">*</span>
  524. <div class="name">证型:</div>
  525. <div class="input">
  526. <el-input size="mini" v-model="viewData.synName" placeholder="-" disabled></el-input>
  527. </div>
  528. </div>
  529. <div class="form-item flex flex-col-center form-item-sixth">
  530. <span style="opacity:0">*</span>
  531. <div class="name">治法:</div>
  532. <div class="input">
  533. <el-input size="mini" v-model="viewData.theName" placeholder="-" disabled></el-input>
  534. </div>
  535. </div>
  536. <div class="form-item flex flex-col-center form-item-sixth">
  537. <div class="name">共享至:</div>
  538. <div class="input">
  539. <el-select size="mini" v-model="viewData.showType" disabled>
  540. <el-option label="个人" value="0"></el-option>
  541. <el-option label="科室" value="1"></el-option>
  542. <el-option label="机构" value="2"></el-option>
  543. </el-select>
  544. </div>
  545. </div>
  546. <div class="form-item flex flex-col-center form-item-sixth">
  547. <div class="name">处方出处:</div>
  548. <div class="input">
  549. <el-input size="mini" v-model="viewData.provenance" placeholder="-" disabled></el-input>
  550. </div>
  551. </div>
  552. <!-- 第二行:西医诊断、功效与适应症 -->
  553. <div class="form-item flex form-item-half" style="align-items: flex-start;">
  554. <span style="opacity:0">*</span>
  555. <div class="name">西医诊断:</div>
  556. <div class="input">
  557. <div class="western-disease-input-wrapper view-mode">
  558. <el-tag
  559. v-for="(name, idx) in viewWesternDiseaseNames"
  560. :key="idx"
  561. size="mini"
  562. >{{ name }}</el-tag>
  563. </div>
  564. </div>
  565. </div>
  566. <div class="form-item flex flex-col-center form-item-half">
  567. <span style="opacity:0">*</span>
  568. <div class="name">功效与适应症:</div>
  569. <div class="input">
  570. <el-input size="mini" v-model="viewData.effect" placeholder="-" disabled></el-input>
  571. </div>
  572. </div>
  573. <!-- 第三行:统建处方 -->
  574. <div class="form-item flex flex-col-center">
  575. <span style="opacity:0">*</span>
  576. <el-checkbox v-model="viewData.isUnifiedChecked" class="unified-checkbox" true-label="1" false-label="0" disabled>统建处方</el-checkbox>
  577. </div>
  578. <div class="form-item flex flex-col-center" v-if="viewData.purposeType === '1'">
  579. <div class="name">医派:</div>
  580. <div class="input">
  581. <el-input size="mini" v-model="viewData.sectCategory" placeholder="-" disabled></el-input>
  582. </div>
  583. </div>
  584. <div class="form-item flex flex-col-center" v-if="viewData.purposeType === '1'">
  585. <div class="name">科室:</div>
  586. <div class="input">
  587. <el-input size="mini" v-model="viewData.sectDept" placeholder="-" disabled></el-input>
  588. </div>
  589. </div>
  590. </div>
  591. <SuitableTechDetail ref="techDetail" :detailData="viewData" :showGuide="true" />
  592. </div>
  593. </div>
  594. </popup>
  595. </div>
  596. </template>
  597. <script>
  598. import popup from "@/components/Propup.vue";
  599. import AcupointTable from "./components/AcupointTable.vue";
  600. import SuitableTechDetail from "./components/SuitableTechDetail.vue";
  601. import {
  602. getDiseaseListMethod,
  603. getSymptomListMethod,
  604. getTherapyListMethod,
  605. } from "@/request/api";
  606. import {
  607. getSuitableTechList,
  608. getSuitableTechInfo,
  609. saveSuitableTech,
  610. deleteSuitableTech,
  611. getMappedNondrugItemList,
  612. } from "@/api/technology.js";
  613. import { getXDiseaseName } from "@/api/knowledge.js";
  614. export default {
  615. name: "SuitableTech",
  616. components: { popup, AcupointTable, SuitableTechDetail },
  617. data() {
  618. return {
  619. searchData: {
  620. name: "",
  621. shareType: "0",
  622. userName: "",
  623. isUnified: "",
  624. medicalSchool: "",
  625. department: "",
  626. includeItem: "",
  627. efficacy: "",
  628. },
  629. // 中医诊断下拉
  630. diseaseList: [],
  631. symptomList: [],
  632. therapyList: [],
  633. popoverZF: false,
  634. key1: "",
  635. key2: "",
  636. key3: "",
  637. westernDiseaseOptions: [],
  638. westernDiseaseNameMap: {},
  639. westernDiseaseSearch: "",
  640. westernDiseasePage: 1,
  641. westernDiseaseHasMore: true,
  642. projectOptions: [],
  643. // 下拉加载状态
  644. westernDiseaseLoading: false,
  645. projectLoading: false,
  646. // 表格数据
  647. tableData: [],
  648. tableLoading: false,
  649. expandRows: [],
  650. page: 1,
  651. limit: 10,
  652. total: 0,
  653. // 弹窗相关
  654. showEditDialog: false,
  655. editLoading: false,
  656. editData: {
  657. statistics: {},
  658. westernDisease: [],
  659. chineseDiseaseName: "",
  660. symptomName: "",
  661. therapyName: "",
  662. },
  663. // 查看详情弹窗
  664. showViewDialog: false,
  665. viewLoading: false,
  666. viewData: {},
  667. };
  668. },
  669. computed: {
  670. userNameLabel() {
  671. const map = {
  672. "0": "姓名",
  673. "1": "科室名称",
  674. "2": "机构名称",
  675. };
  676. return map[this.searchData.shareType] || "姓名";
  677. },
  678. currentUserId() {
  679. const userInfo = this.$store.state.user.userInfo || {};
  680. return userInfo.pid;
  681. },
  682. shareOptions() {
  683. const userInfo = this.$store.state.user.userInfo || {};
  684. const perm = String(userInfo.prescriptionSharePerm || "");
  685. const permList = perm.split(/[,,]/).map(v => v.trim());
  686. const has = (v) => permList.includes(String(v));
  687. const options = [];
  688. if (has(0)) options.push({ label: "个人", value: "0" });
  689. if (has(1) || has(2)) options.push({ label: "科室", value: "1" });
  690. if (has(3)) options.push({ label: "医疗机构", value: "2" });
  691. return options;
  692. },
  693. defaultShareType() {
  694. const options = this.shareOptions;
  695. return options.length ? options[0].value : "0";
  696. },
  697. viewWesternDiseaseNames() {
  698. if (!this.viewData.westernDiag) return [];
  699. return String(this.viewData.westernDiag).split(",").map((s) => s.trim()).filter(Boolean);
  700. },
  701. },
  702. created() {
  703. this.getList();
  704. },
  705. watch: {
  706. key1: {
  707. handler(value, oldVal) {
  708. if (this.key1 === "") {
  709. this.editData.chineseDisease = "";
  710. this.editData.chineseDiseaseName = "";
  711. this.editData.disCode = "";
  712. }
  713. if (!value && oldVal) {
  714. this.getSymptomList();
  715. this.getTherapyList();
  716. }
  717. }
  718. },
  719. key2: {
  720. handler(value, oldVal) {
  721. if (this.key2 === "") {
  722. this.editData.symptomName = "";
  723. this.editData.symptomid = "";
  724. this.editData.symptomCode = "";
  725. }
  726. if (!value && oldVal) {
  727. this.getSymptomList();
  728. this.getTherapyList();
  729. }
  730. }
  731. },
  732. key3: {
  733. handler(value, oldVal) {
  734. if (this.key3 === "") {
  735. this.editData.therapyName = "";
  736. this.editData.therapyCode = "";
  737. }
  738. if (!value && oldVal) {
  739. this.getTherapyList();
  740. }
  741. }
  742. },
  743. },
  744. methods: {
  745. /**
  746. * focus 事件
  747. * @param {'bm'|'zx'|'zf'} type
  748. */
  749. handleFocus(type) {
  750. if (type === 'bm' && this.diseaseList.length === 0) this.getDiseaseList(this.key1);
  751. else if (type === 'zx' && this.symptomList.length === 0) this.getSymptomList(this.key2);
  752. else if (type === 'zf' && this.therapyList.length === 0) this.getTherapyList(this.key3);
  753. },
  754. /**
  755. * enter事件
  756. * @param {'bm'|'zx'|'zf'} type
  757. */
  758. handleEnter(type) {
  759. if (type === 'bm' && this.diseaseList.length) this.handleBm(this.diseaseList[0]);
  760. else if (type === 'zx' && this.symptomList.length) this.handleZx(this.symptomList[0]);
  761. else if (type === 'zf' && this.therapyList.length) this.handleZf(this.therapyList[0]);
  762. },
  763. //获取病名数据
  764. async getDiseaseList(keyword = '') {
  765. const {total, list} = await getDiseaseListMethod(1, 9999, {keyword}).catch(() => ({total: 0, list: []}));
  766. this.diseaseList = list;
  767. },
  768. // 病名选中
  769. async handleBm(item) {
  770. this.editData.chineseDisease = String(item.disid);
  771. this.editData.chineseDiseaseName = item.$name;
  772. this.editData.disCode = item.$code;
  773. this.key1 = item.$name;
  774. this.$refs.zybm.blur();
  775. this.clearZx();
  776. if (!this.key1) return;
  777. this.$refs.zhengxing && this.$refs.zhengxing.focus();
  778. await this.getSymptomList();
  779. },
  780. // 清空病名
  781. clearBm() {
  782. this.editData.chineseDisease = "";
  783. this.editData.chineseDiseaseName = "";
  784. this.editData.disCode = "";
  785. this.key1 = "";
  786. this.clearZx();
  787. },
  788. // 获取证型数据
  789. async getSymptomList(keyword = '') {
  790. const {total, list} = await getSymptomListMethod(1, 9999, {
  791. keyword,
  792. disid: this.editData.chineseDisease,
  793. disCode: this.editData.disCode,
  794. }).catch(() => ({total: 0, list: []}));
  795. this.symptomList = list;
  796. },
  797. // 证型选中
  798. async handleZx(item) {
  799. this.editData.symptomName = item.$name;
  800. this.editData.symptomid = item.$code;
  801. this.editData.symptomCode = item.$code;
  802. this.key2 = item.$name;
  803. this.$refs.zhengxing.blur();
  804. this.clearZf();
  805. this.$refs.zhifa && this.$refs.zhifa.focus();
  806. await this.getTherapyList();
  807. },
  808. // 清空证型
  809. clearZx() {
  810. this.editData.symptomName = "";
  811. this.editData.symptomid = "";
  812. this.editData.symptomCode = "";
  813. this.key2 = "";
  814. this.clearZf();
  815. },
  816. /**
  817. * 获取治法
  818. * @param keyword
  819. * @return {Promise<void>}
  820. */
  821. async getTherapyList(keyword = '') {
  822. const {total, list} = await getTherapyListMethod(1, 9999, {
  823. keyword,
  824. disCode: this.editData.disCode,
  825. symptomCode: this.editData.symptomid,
  826. symid: this.editData.symptomid,
  827. }).catch(() => ({total: 0, list: []}));
  828. this.therapyList = list;
  829. },
  830. // 治法选中
  831. handleZf(item) {
  832. this.editData.therapyName = item.$name;
  833. this.editData.therapyCode = item.$code;
  834. this.key3 = item.$name;
  835. this.$nextTick(() => {
  836. this.$refs.zhifa && this.$refs.zhifa.blur();
  837. this.popoverZF = false;
  838. });
  839. },
  840. // 清空治法
  841. clearZf() {
  842. this.editData.therapyName = "";
  843. this.editData.therapyCode = "";
  844. this.key3 = "";
  845. },
  846. // ========== 西医诊断搜索(分页) ==========
  847. async searchWesternDisease(query) {
  848. this.westernDiseasePage = 1;
  849. this.westernDiseaseHasMore = true;
  850. this._wdQuery = query || "";
  851. this.westernDiseaseLoading = true;
  852. try {
  853. const pinyin = /^[A-Za-z]+$/g;
  854. const serchtype = query && pinyin.test(query) ? "1" : "";
  855. const res = await getXDiseaseName({
  856. pageid: 1,
  857. pagesize: 200,
  858. keyword: query || "",
  859. serchtype,
  860. });
  861. if (res.code == 0) {
  862. const list = res.data?.wests || [];
  863. // 保留已选中但不在搜索结果中的选项
  864. const selectedCodes = Array.isArray(this.editData.westernDisease)
  865. ? this.editData.westernDisease
  866. : [];
  867. const resultCodes = new Set(list.map((i) => i.westcode));
  868. const preserved = selectedCodes
  869. .filter((code) => !resultCodes.has(code))
  870. .map((code) => ({
  871. westcode: code,
  872. westname: this.westernDiseaseNameMap[code] || code,
  873. }));
  874. this.westernDiseaseOptions = [...preserved, ...list];
  875. this.westernDiseaseHasMore = list.length >= 200;
  876. list.forEach((item) => {
  877. if (item.westcode && item.westname) {
  878. this.westernDiseaseNameMap[item.westcode] = item.westname;
  879. }
  880. });
  881. }
  882. } catch (e) {
  883. console.error("搜索西医诊断失败", e);
  884. } finally {
  885. this.westernDiseaseLoading = false;
  886. }
  887. },
  888. async loadMoreWesternDisease() {
  889. if (this.westernDiseaseLoading || !this.westernDiseaseHasMore) return;
  890. this.westernDiseasePage++;
  891. this.westernDiseaseLoading = true;
  892. try {
  893. const query = this._wdQuery || "";
  894. const pinyin = /^[A-Za-z]+$/g;
  895. const serchtype = query && pinyin.test(query) ? "1" : "";
  896. const res = await getXDiseaseName({
  897. pageid: this.westernDiseasePage,
  898. pagesize: 200,
  899. keyword: query,
  900. serchtype,
  901. });
  902. if (res.code == 0) {
  903. const list = res.data?.wests || [];
  904. this.westernDiseaseOptions = this.westernDiseaseOptions.concat(list);
  905. this.westernDiseaseHasMore = list.length >= 200;
  906. list.forEach((item) => {
  907. if (item.westcode && item.westname) {
  908. this.westernDiseaseNameMap[item.westcode] = item.westname;
  909. }
  910. });
  911. }
  912. } catch (e) {
  913. console.error("加载更多西医诊断失败", e);
  914. } finally {
  915. this.westernDiseaseLoading = false;
  916. }
  917. },
  918. onWesternDiseaseVisible(show) {
  919. if (!show) {
  920. const wrap = document.querySelector(
  921. ".western-disease-select .el-scrollbar__wrap",
  922. );
  923. if (wrap && this._wdScroll) {
  924. wrap.removeEventListener("scroll", this._wdScroll);
  925. }
  926. return;
  927. }
  928. // 首次打开且无数据时加载初始列表
  929. if (this.westernDiseaseOptions.length === 0) {
  930. this.searchWesternDisease("");
  931. }
  932. this.$nextTick(() => {
  933. const wrap = document.querySelector(
  934. ".western-disease-select .el-scrollbar__wrap",
  935. );
  936. if (wrap) {
  937. wrap.addEventListener(
  938. "scroll",
  939. (this._wdScroll = () => {
  940. if (wrap.scrollHeight - wrap.scrollTop <= wrap.clientHeight + 5) {
  941. this.loadMoreWesternDisease();
  942. }
  943. }),
  944. );
  945. }
  946. });
  947. },
  948. async queryWesternDisease(queryString, cb) {
  949. await this.searchWesternDisease(queryString);
  950. if (this.westernDiseaseOptions.length > 0) {
  951. cb(this.westernDiseaseOptions);
  952. } else {
  953. cb([{ westname: "暂无数据", _disabled: true }]);
  954. }
  955. },
  956. onWesternDiseaseSelect(item) {
  957. if (item._disabled) {
  958. this.$nextTick(() => {
  959. this.westernDiseaseSearch = "";
  960. });
  961. return;
  962. }
  963. const code = String(item.westcode);
  964. if (!this.editData.westernDisease.includes(code)) {
  965. this.editData.westernDisease.push(code);
  966. this.westernDiseaseNameMap[code] = item.westname;
  967. this.updateWesternDiseaseName();
  968. }
  969. this.westernDiseaseSearch = "";
  970. this.$nextTick(() => {
  971. this.searchWesternDisease("");
  972. });
  973. },
  974. removeWesternDisease(code) {
  975. this.editData.westernDisease = this.editData.westernDisease.filter((c) => c !== code);
  976. this.updateWesternDiseaseName();
  977. },
  978. updateWesternDiseaseName() {
  979. const names = this.editData.westernDisease
  980. .map((code) => this.westernDiseaseNameMap[code] || "")
  981. .filter(Boolean);
  982. this.editData.westernDiseaseName = names.join(",");
  983. },
  984. // ========== 涵盖项目模糊搜索 ==========
  985. async searchProject(query) {
  986. this.projectLoading = true;
  987. try {
  988. const res = await getMappedNondrugItemList({
  989. pageNum: 1,
  990. pageSize: 100,
  991. itemName: query || undefined,
  992. });
  993. if (res.ResultCode === 0) {
  994. this.projectOptions = res.Data?.Items || res.Data || [];
  995. }
  996. } catch (e) {
  997. console.error("搜索涵盖项目失败", e);
  998. } finally {
  999. this.projectLoading = false;
  1000. }
  1001. },
  1002. search() {
  1003. this.page = 1;
  1004. this.getList();
  1005. },
  1006. clearFilter() {
  1007. this.searchData = {
  1008. name: "",
  1009. shareType: "0",
  1010. userName: "",
  1011. isUnified: "",
  1012. medicalSchool: "",
  1013. department: "",
  1014. includeItem: "",
  1015. efficacy: "",
  1016. };
  1017. this.getList();
  1018. },
  1019. sizeC(e) {
  1020. this.page = e;
  1021. this.getList();
  1022. },
  1023. handleExpandChange(row, expandedRows) {
  1024. this.expandRows = expandedRows.map((item) => item.pid);
  1025. },
  1026. formatTreatClass(val) {
  1027. const map = {
  1028. 1: "艾灸疗法",
  1029. 2: "非艾·灸法",
  1030. 3: "拔罐疗法",
  1031. 4: "针刺疗法",
  1032. 5: "敷熨熏浴",
  1033. 6: "推拿疗法",
  1034. 7: "耳穴疗法",
  1035. 8: "刮痧疗法",
  1036. 9: "物理疗法",
  1037. 10: "中医肛肠",
  1038. 11: "中医微创",
  1039. 12: "其他疗法",
  1040. 194: "中医骨伤",
  1041. };
  1042. return map[val] || val || "-";
  1043. },
  1044. formatDetailList(row) {
  1045. const parts = [];
  1046. const extractNames = (arr) => {
  1047. if (!Array.isArray(arr) || !arr.length) return "";
  1048. return arr
  1049. .map((item) => item.name || "")
  1050. .filter(Boolean)
  1051. .join("、");
  1052. };
  1053. const pointNames = extractNames(row.detailPoint);
  1054. if (pointNames) parts.push("穴位: " + pointNames);
  1055. const meridianNames = extractNames(row.detailMeridian);
  1056. if (meridianNames) parts.push("经络: " + meridianNames);
  1057. const earNames = extractNames(row.detailEarPoint);
  1058. if (earNames) parts.push("耳穴: " + earNames);
  1059. const bodyNames = extractNames(row.detailBodyPart);
  1060. if (bodyNames) parts.push("部位: " + bodyNames);
  1061. const otherNames = extractNames(row.detailOther);
  1062. if (otherNames) parts.push("其他: " + otherNames);
  1063. return parts.join(";") || "-";
  1064. },
  1065. handleEditBack() {
  1066. if (!this.editData.pid) {
  1067. this.$confirm("协定方未保存,返回则会清空当前编辑内容", "提示", {
  1068. confirmButtonText: "确定",
  1069. cancelButtonText: "取消",
  1070. type: "warning",
  1071. })
  1072. .then(() => {
  1073. this.showEditDialog = false;
  1074. })
  1075. .catch(() => {});
  1076. } else {
  1077. this.showEditDialog = false;
  1078. }
  1079. },
  1080. async openEditDialog(row) {
  1081. if (row) {
  1082. // 调用详情接口获取完整数据
  1083. this.editLoading = true;
  1084. let detail = row;
  1085. try {
  1086. const res = await getSuitableTechInfo(row.pid);
  1087. if (res.ResultCode === 0) {
  1088. detail = res.Data || row;
  1089. }
  1090. } catch (e) {
  1091. console.error("获取详情失败", e);
  1092. } finally {
  1093. this.editLoading = false;
  1094. }
  1095. // 初始化搜索关键字,用于回显
  1096. this.key1 = detail.disName || "";
  1097. this.key2 = detail.synName || "";
  1098. this.key3 = detail.theName || "";
  1099. // 加载下拉列表数据
  1100. if (this.key1) this.getDiseaseList(this.key1);
  1101. if (this.key2) this.getSymptomList(this.key2);
  1102. if (this.key3) this.getTherapyList(this.key3);
  1103. if (detail.westernCode) {
  1104. const codes = String(detail.westernCode).split(",");
  1105. const names = detail.westernDiag
  1106. ? String(detail.westernDiag).split(",")
  1107. : [];
  1108. this.westernDiseaseOptions = codes.map((code, idx) => ({
  1109. westcode: code.trim(),
  1110. westname: (names[idx] || "").trim() || code.trim(),
  1111. }));
  1112. } else {
  1113. this.westernDiseaseOptions = [];
  1114. }
  1115. // 同步到名称映射
  1116. this.westernDiseaseNameMap = {};
  1117. this.westernDiseaseOptions.forEach((item) => {
  1118. if (item.westcode && item.westname) {
  1119. this.westernDiseaseNameMap[item.westcode] = item.westname;
  1120. }
  1121. });
  1122. this.editData = {
  1123. ...detail,
  1124. showType: String(detail.showType ?? "0"),
  1125. efficacy: detail.effect || "",
  1126. source: detail.provenance || "",
  1127. isUnified: String(detail.purposeType) === "1",
  1128. medicalSchool: detail.sectCategory || "",
  1129. department: detail.sectDept || "",
  1130. chineseDisease: detail.disId != null ? String(detail.disId) : "",
  1131. chineseDiseaseName: detail.disName || "",
  1132. disCode: detail.disCode || "",
  1133. symptomid: detail.synCode != null ? String(detail.synCode) : "",
  1134. symptomName: detail.synName || "",
  1135. symptomCode: detail.synCode || "",
  1136. westernDisease: detail.westernCode
  1137. ? String(detail.westernCode)
  1138. .split(",")
  1139. .map((s) => s.trim())
  1140. : [],
  1141. westernDiseaseName: detail.westernDiag || "",
  1142. therapyCode: detail.theCode != null ? String(detail.theCode) : "",
  1143. therapyName: detail.theName || "",
  1144. acupoints: detail.acupoints || [],
  1145. detailTypes: detail.detailTypes || ["穴位"],
  1146. statistics: detail.statistics || {},
  1147. };
  1148. this.showEditDialog = true;
  1149. // 弹窗显示后回填 treatmentList 到 AcupointTable
  1150. this.$nextTick(() => {
  1151. if (this.$refs.acupointTable && detail.treatmentList) {
  1152. this.$refs.acupointTable.loadFromServerData(detail.treatmentList);
  1153. }
  1154. });
  1155. } else {
  1156. this.editData = {
  1157. name: "",
  1158. chineseDisease: "",
  1159. chineseDiseaseName: "",
  1160. disCode: "",
  1161. symptomid: "",
  1162. symptomName: "",
  1163. therapyCode: "",
  1164. therapyName: "",
  1165. westernDisease: [],
  1166. westernDiseaseName: "",
  1167. efficacy: "",
  1168. showType: this.defaultShareType,
  1169. source: "",
  1170. isUnified: false,
  1171. medicalSchool: "",
  1172. department: "",
  1173. isEditable: "1",
  1174. detailTypes: [""],
  1175. acupoints: [],
  1176. statistics: {},
  1177. };
  1178. this.key1 = "";
  1179. this.key2 = "";
  1180. this.key3 = "";
  1181. this.diseaseList = [];
  1182. this.symptomList = [];
  1183. this.therapyList = [];
  1184. this.showEditDialog = true;
  1185. }
  1186. },
  1187. async submitEditData() {
  1188. // 通过 AcupointTable 的 handleSave 触发保存流程
  1189. this.$refs.acupointTable.handleSave();
  1190. },
  1191. async onAcupointSave(treatmentList, institutionInfo) {
  1192. // 从下拉选项中查找选中项的名称
  1193. const selectedDisease = this.diseaseList.find(
  1194. (o) => String(o.disid) === this.editData.chineseDisease,
  1195. );
  1196. const selectedSymptom = this.symptomList.find(
  1197. (o) => o.$code === this.editData.symptomid,
  1198. );
  1199. const selectedTherapy = this.therapyList.find(
  1200. (o) => o.$code === this.editData.therapyCode,
  1201. );
  1202. const params = {
  1203. // 必填字段
  1204. name: this.editData.name,
  1205. showType: this.editData.showType || "0",
  1206. // 基本信息
  1207. effect: this.editData.efficacy || "",
  1208. provenance: this.editData.source || "",
  1209. purposeType: this.editData.isUnified ? "1" : "0",
  1210. sectCategory: this.editData.medicalSchool || "",
  1211. sectDept: this.editData.department || "",
  1212. // 疾病信息
  1213. disId: this.editData.chineseDisease || undefined,
  1214. disName: selectedDisease
  1215. ? selectedDisease.$name
  1216. : this.editData.chineseDiseaseName || "",
  1217. disCode: this.editData.disCode || "",
  1218. // 证型信息
  1219. synId: undefined,
  1220. synName: selectedSymptom
  1221. ? selectedSymptom.$name
  1222. : this.editData.symptomName || "",
  1223. synCode: this.editData.symptomid || "",
  1224. // 治法信息
  1225. theCode: this.editData.therapyCode || "",
  1226. theName: selectedTherapy
  1227. ? selectedTherapy.$name
  1228. : this.editData.therapyName || "",
  1229. // 西医诊断
  1230. westernCode: Array.isArray(this.editData.westernDisease)
  1231. ? this.editData.westernDisease.join(",")
  1232. : this.editData.westernDisease || "",
  1233. westernDiag: this.editData.westernDiseaseName || "",
  1234. // 总金额
  1235. totalAmount: parseFloat(this.editData.statistics?.totalPrice) || 0,
  1236. // 机构信息:来自 getNondrugItemDetail 接口(新增时),或 editData 回显(编辑时)
  1237. ygtid: institutionInfo?.ygtid || this.editData.ygtid || "",
  1238. departmentId:
  1239. institutionInfo?.departmentId || this.editData.departmentId || "",
  1240. stitutionsId:
  1241. institutionInfo?.stitutionsId || this.editData.stitutionsId || "",
  1242. stitutionsName:
  1243. institutionInfo?.stitutionsName || this.editData.stitutionsName || "",
  1244. // 治疗明细项列表
  1245. treatmentList,
  1246. };
  1247. // 编辑时传递 pid
  1248. if (this.editData.pid) {
  1249. params.pid = this.editData.pid;
  1250. }
  1251. try {
  1252. const res = await saveSuitableTech(params);
  1253. if (res.ResultCode === 0) {
  1254. this.$message.success(this.editData.pid ? "编辑成功" : "新增成功");
  1255. this.showEditDialog = false;
  1256. this.getList();
  1257. }
  1258. // 后端返回错误时,request.js 响应拦截器已弹出错误消息并 reject,
  1259. // 这里不需要再重复弹窗;网络错误同样由拦截器处理
  1260. } catch (e) {
  1261. console.error("保存失败", e);
  1262. } finally {
  1263. if (this.$refs.acupointTable) {
  1264. this.$refs.acupointTable.saveDone();
  1265. }
  1266. }
  1267. },
  1268. handleDelete(row) {
  1269. this.$confirm("确认删除该条记录?", "提示", {
  1270. confirmButtonText: "确定",
  1271. cancelButtonText: "取消",
  1272. type: "warning",
  1273. })
  1274. .then(async () => {
  1275. try {
  1276. const res = await deleteSuitableTech({ pid: row.pid });
  1277. if (res.ResultCode === 0) {
  1278. this.$message.success("删除成功");
  1279. this.getList();
  1280. } else {
  1281. this.$message.error(res.ResultInfo || "删除失败");
  1282. }
  1283. } catch (e) {
  1284. console.error("删除失败", e);
  1285. this.$message.error("删除失败");
  1286. }
  1287. })
  1288. .catch(() => {});
  1289. },
  1290. // 查看详情
  1291. async handleView(row) {
  1292. this.viewLoading = true;
  1293. this.showViewDialog = true;
  1294. this.viewData = {};
  1295. try {
  1296. const res = await getSuitableTechInfo(row.pid);
  1297. if (res.ResultCode === 0) {
  1298. const data = res.Data || {};
  1299. this.viewData = {
  1300. ...data,
  1301. showType: String(data.showType ?? "0"),
  1302. isUnifiedChecked: data.purposeType || "0",
  1303. };
  1304. }
  1305. } catch (e) {
  1306. console.error("获取详情失败", e);
  1307. } finally {
  1308. this.viewLoading = false;
  1309. }
  1310. },
  1311. async getList() {
  1312. this.tableLoading = true;
  1313. try {
  1314. const params = {
  1315. pageNum: this.page,
  1316. pageSize: this.limit,
  1317. name: this.searchData.name || undefined,
  1318. effect: this.searchData.efficacy || undefined,
  1319. showType: this.searchData.shareType || undefined,
  1320. sectCategory: this.searchData.medicalSchool || undefined,
  1321. sectDept: this.searchData.department || undefined,
  1322. purposeType: this.searchData.isUnified || undefined,
  1323. coverageContent: this.searchData.includeItem || undefined,
  1324. rangtypeName: this.searchData.userName || undefined,
  1325. };
  1326. const res = await getSuitableTechList(params);
  1327. if (res.ResultCode === 0) {
  1328. const list = res.Data?.Items || [];
  1329. this.total = res.Data?.totalRecordCount || 0;
  1330. this.tableData = list.map((item, index) => ({
  1331. ...item,
  1332. index: (this.page - 1) * this.limit + index + 1,
  1333. subList: (item.treatmentList || []).map((sub, sIdx) => ({
  1334. ...sub,
  1335. index: sIdx + 1,
  1336. })),
  1337. }));
  1338. }
  1339. } catch (e) {
  1340. console.error("获取列表失败", e);
  1341. } finally {
  1342. this.tableLoading = false;
  1343. }
  1344. },
  1345. },
  1346. };
  1347. </script>
  1348. <style lang="scss" scoped>
  1349. @import "../../style/common.scss";
  1350. @import "../../style/base.scss";
  1351. .suitable-tech {
  1352. width: 100%;
  1353. height: 100%;
  1354. }
  1355. .suitable-tech ::v-deep .screening-form {
  1356. .screening-item {
  1357. width: 20%;
  1358. flex: none;
  1359. .el-input,
  1360. .el-select {
  1361. width: 100%;
  1362. }
  1363. }
  1364. }
  1365. .suitable-tech ::v-deep .popup-container {
  1366. display: flex;
  1367. flex-direction: column;
  1368. overflow: hidden !important;
  1369. height: calc(100vh - 60px) !important;
  1370. }
  1371. .dialog-body-wrapper {
  1372. display: flex;
  1373. flex-direction: column;
  1374. height: 100%;
  1375. }
  1376. .dialog-back-btn {
  1377. display: flex;
  1378. align-items: center;
  1379. justify-content: center;
  1380. font-size: 14px;
  1381. color: #333;
  1382. background: #fff;
  1383. border: 1px solid #ccc;
  1384. border-radius: 4px;
  1385. padding: 4px 16px;
  1386. cursor: pointer;
  1387. margin-bottom: 10px;
  1388. width: fit-content;
  1389. &:hover {
  1390. opacity: 0.8;
  1391. }
  1392. }
  1393. .dialog-content-box {
  1394. flex: 1;
  1395. min-height: 0;
  1396. display: flex;
  1397. flex-direction: column;
  1398. border: 1px solid #ddd;
  1399. border-radius: 4px;
  1400. padding: 10px;
  1401. overflow: hidden;
  1402. }
  1403. .screening-btn-group {
  1404. display: flex;
  1405. align-items: center;
  1406. margin: 5px 0;
  1407. }
  1408. .share-badge {
  1409. display: inline-block;
  1410. min-width: 20px;
  1411. height: 20px;
  1412. line-height: 20px;
  1413. padding: 0 6px;
  1414. background: #ffae45;
  1415. color: #fff;
  1416. border-radius: 10px;
  1417. font-size: 12px;
  1418. text-align: center;
  1419. }
  1420. .expand-table {
  1421. padding: 10px 20px;
  1422. }
  1423. .table {
  1424. padding: 10px 10px;
  1425. background: #ffffff;
  1426. border-radius: 5px;
  1427. margin-top: 10px;
  1428. height: 70vh;
  1429. .table-container {
  1430. height: 90%;
  1431. }
  1432. .today-table {
  1433. height: 100%;
  1434. .find-detail {
  1435. width: 60px;
  1436. height: 24px;
  1437. border: 1px solid #ffae45;
  1438. border-radius: 2px;
  1439. text-align: center;
  1440. color: #fff;
  1441. font-size: 14px;
  1442. cursor: pointer;
  1443. margin: 0 2px;
  1444. background: #ffae45;
  1445. }
  1446. .find-fill {
  1447. background: #5386f6 !important;
  1448. color: #fff !important;
  1449. border: 1px solid #5386f6;
  1450. }
  1451. .find-fill1 {
  1452. background: #ff6245;
  1453. color: #fff !important;
  1454. border: 1px solid #ff6245;
  1455. }
  1456. .find-fill2 {
  1457. background: #68a8ff !important;
  1458. color: #fff !important;
  1459. border: 1px solid #68a8ff;
  1460. }
  1461. .find-detail.is-disabled {
  1462. opacity: 0.5;
  1463. cursor: not-allowed;
  1464. pointer-events: none;
  1465. }
  1466. }
  1467. }
  1468. .form {
  1469. width: 50%;
  1470. .form-item {
  1471. margin-bottom: 10px;
  1472. span {
  1473. color: red;
  1474. }
  1475. .name {
  1476. width: 120px;
  1477. }
  1478. .input {
  1479. flex: 1;
  1480. }
  1481. }
  1482. }
  1483. .dialog-form {
  1484. display: flex;
  1485. flex-wrap: wrap;
  1486. flex-shrink: 0;
  1487. .form-item {
  1488. width: 25%;
  1489. box-sizing: border-box;
  1490. padding: 0 10px;
  1491. margin-bottom: 10px;
  1492. &.form-item-half {
  1493. width: 50%;
  1494. }
  1495. &.form-item-sixth {
  1496. width: 16.5%;
  1497. .name {
  1498. width: 60px;
  1499. min-width: 60px;
  1500. }
  1501. }
  1502. .name {
  1503. width: 110px;
  1504. min-width: 110px;
  1505. font-size: 14px;
  1506. white-space: nowrap;
  1507. text-align: right;
  1508. margin-right: 5px;
  1509. }
  1510. .input {
  1511. flex: 1;
  1512. min-width: 0;
  1513. .el-input,
  1514. .el-select,
  1515. .el-autocomplete {
  1516. width: 100%;
  1517. }
  1518. .el-popover__reference-wrapper {
  1519. display: block !important;
  1520. width: 100%;
  1521. .el-input {
  1522. width: 100%;
  1523. }
  1524. }
  1525. }
  1526. .unified-checkbox {
  1527. margin-left: 115px;
  1528. }
  1529. }
  1530. }
  1531. .dialog-scroll-area {
  1532. flex: 1;
  1533. min-height: 0;
  1534. overflow: hidden;
  1535. display: flex;
  1536. flex-direction: column;
  1537. }
  1538. .dialog-scroll-area ::v-deep .recipe-acupoint-wrapper {
  1539. flex: 1;
  1540. display: flex;
  1541. flex-direction: column;
  1542. min-height: 0;
  1543. }
  1544. .autocomplete-disabled {
  1545. color: #999 !important;
  1546. cursor: not-allowed !important;
  1547. pointer-events: none;
  1548. }
  1549. .el-input.invalid::v-deep {
  1550. input {
  1551. color: #ff0000;
  1552. }
  1553. }
  1554. .option-list {
  1555. margin: -12px -12px;
  1556. max-height: 300px;
  1557. overflow: auto;
  1558. .active {
  1559. color: #5386f6;
  1560. }
  1561. li {
  1562. padding: 8px 12px;
  1563. list-style: none;
  1564. font-size: 14px;
  1565. box-sizing: border-box;
  1566. width: 100%;
  1567. color: #606266;
  1568. font-weight: 500;
  1569. cursor: pointer;
  1570. }
  1571. li:hover {
  1572. background: #f5f7fa;
  1573. }
  1574. }
  1575. .suffix {
  1576. height: 100%;
  1577. padding-right: 5px;
  1578. display: flex;
  1579. align-items: center;
  1580. i {
  1581. cursor: pointer;
  1582. color: #c0c4cc;
  1583. }
  1584. i:hover {
  1585. color: #909399;
  1586. }
  1587. }
  1588. .western-disease-input-wrapper {
  1589. border: 1px solid #dcdfe6;
  1590. border-radius: 4px;
  1591. padding: 2px 4px;
  1592. display: flex;
  1593. flex-wrap: wrap;
  1594. align-items: center;
  1595. min-height: 28px;
  1596. cursor: text;
  1597. .el-tag {
  1598. margin: 2px 4px 2px 0;
  1599. max-width: 200px;
  1600. overflow: hidden;
  1601. text-overflow: ellipsis;
  1602. }
  1603. .el-autocomplete {
  1604. flex: 1;
  1605. min-width: 80px;
  1606. }
  1607. ::v-deep .el-input__inner {
  1608. border: none !important;
  1609. padding: 0 4px;
  1610. height: 24px;
  1611. line-height: 24px;
  1612. background: transparent;
  1613. }
  1614. ::v-deep .el-input__suffix {
  1615. display: flex;
  1616. align-items: center;
  1617. }
  1618. &:hover {
  1619. border-color: #c0c4cc;
  1620. }
  1621. &:focus-within {
  1622. border-color: #409eff;
  1623. }
  1624. &.view-mode {
  1625. cursor: default;
  1626. background: #f5f7fa;
  1627. .el-tag {
  1628. background-color: #f0f2f5;
  1629. border-color: #e4e7ed;
  1630. color: #909399;
  1631. }
  1632. }
  1633. }
  1634. .dialog-scroll-area ::v-deep .recipe-acupoint {
  1635. flex: 1;
  1636. min-height: 0;
  1637. }
  1638. </style>
  1639. <style lang="scss" scoped>
  1640. @media screen and (min-width: 1681px) and (max-width: 1920px) {
  1641. .table {
  1642. height: 81vh;
  1643. .table-container {
  1644. height: 94%;
  1645. }
  1646. }
  1647. .today-table::v-deep .el-table td {
  1648. padding: 18px 0;
  1649. }
  1650. .today-table::v-deep .el-table th {
  1651. padding: 18px 0;
  1652. }
  1653. }
  1654. @media screen and (min-width: 1601px) and (max-width: 1680px) {
  1655. .table {
  1656. height: 80vh;
  1657. .table-container {
  1658. height: 94%;
  1659. }
  1660. }
  1661. .today-table::v-deep .el-table td {
  1662. padding: 18px 0;
  1663. }
  1664. .today-table::v-deep .el-table th {
  1665. padding: 18px 0;
  1666. }
  1667. }
  1668. @media screen and (min-width: 1361px) and (max-width: 1600px) {
  1669. .table {
  1670. height: 72vh;
  1671. .table-container {
  1672. height: 90%;
  1673. }
  1674. }
  1675. .today-table::v-deep .el-table td {
  1676. padding: 5px 0;
  1677. }
  1678. .today-table::v-deep .el-table th {
  1679. padding: 5px 0;
  1680. }
  1681. }
  1682. @media screen and (min-width: 1281px) and (max-width: 1360px) {
  1683. .table {
  1684. height: 72vh;
  1685. .table-container {
  1686. height: 90%;
  1687. }
  1688. }
  1689. .today-table::v-deep .el-table td {
  1690. padding: 5px 0;
  1691. }
  1692. .today-table::v-deep .el-table th {
  1693. padding: 5px 0;
  1694. }
  1695. }
  1696. </style>