vue2-client 1.16.88 → 1.16.90

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. package/.claude/settings.local.json +20 -20
  2. package/.cursorrules +19 -19
  3. package/.env.apply +19 -19
  4. package/.env.gaslink +19 -19
  5. package/.env.his +19 -19
  6. package/.env.liuli +20 -20
  7. package/.env.scada +19 -19
  8. package/.eslintrc.js +90 -90
  9. package/CHANGELOG.md +830 -830
  10. package/CLAUDE.md +97 -97
  11. package/Components.md +60 -60
  12. package/docs/LowCode/lowcode.md +155 -155
  13. package/docs/LowCode/lowcodeForDeveloper.md +230 -230
  14. package/docs/index.md +30 -30
  15. package/index.js +31 -31
  16. package/jest-transform-stub.js +8 -8
  17. package/jest.setup.js +7 -7
  18. package/jsconfig.json +19 -19
  19. package/package.json +112 -112
  20. package/public/his/editor/editor.html +51 -51
  21. package/public/his/editor/mock/bind_data.html +779 -779
  22. package/public/his/editor/mock/data_table.html +40 -40
  23. package/public/his/editor/mock/sign.html +75 -75
  24. package/public/his/editor/vender/JsBarcode.all.js +3669 -3669
  25. package/public/his/editor/vender/date97/My97DatePicker.htm +65 -65
  26. package/public/his/editor/vender/date97/WdatePicker.js +677 -677
  27. package/public/his/editor/vender/date97/calendar.js +4 -4
  28. package/public/his/editor/vender/date97/lang/en.js +13 -13
  29. package/public/his/editor/vender/date97/lang/zh-cn.js +13 -13
  30. package/public/his/editor/vender/date97/lang/zh-tw.js +13 -13
  31. package/public/his/editor/vender/date97/skin/WdatePicker.css +10 -10
  32. package/public/his/editor/vender/date97/skin/default/datepicker.css +328 -328
  33. package/public/his/editor/vender/date97/skin/ext/datepicker.css +308 -308
  34. package/public/his/editor/vender/date97/skin/whyGreen/datepicker.css +255 -255
  35. package/public/his/editor/vender/diff.js +1627 -1627
  36. package/public/his/editor/vender/editor.js +1 -1
  37. package/public/his/editor/vender/fabric.js +31187 -31187
  38. package/public/his/editor/vender/jquery/jquery.base64.js +190 -190
  39. package/public/his/editor/vender/jquery/jquery.js +10872 -10872
  40. package/public/his/editor/vender/jquery/jquery.print.js +255 -255
  41. package/public/his/editor/vender/jquery/zTreeStyle/zTreeStyle.css +96 -96
  42. package/public/his/editor/vender/mui/mui.min.css +4 -4
  43. package/public/his/editor/vender/mui/mui.min.js +5 -5
  44. package/public/his/editor/vender/mui/mui.picker.min.css +6 -6
  45. package/public/his/editor/vender/mui/mui.picker.min.js +6 -6
  46. package/public/his/editor/vender/qrcode.js +7 -7
  47. package/public/his/editor/vender/requirejs/require.js +2145 -2145
  48. package/public/his/editor/vender/signature/jSignature.CompressorSVG.js +518 -518
  49. package/public/his/editor/vender/signature/jSignature.UndoButton.js +164 -164
  50. package/public/his/editor/vender/signature/jSignature.js +1486 -1486
  51. package/public/his/editor/vender/validator.js +5094 -5094
  52. package/public/his/editor/vender/weui/weui.css +5659 -5659
  53. package/public/his/editor/vender/weui/weui.min.css +4 -4
  54. package/public/his/editor/vender/weui/weui.min.js +11 -11
  55. package/src/assets/img/querySlotDemo.svg +15 -15
  56. package/src/assets/svg/badtwo.svg +1 -1
  57. package/src/assets/svg/female.svg +1 -1
  58. package/src/assets/svg/goodtwo.svg +1 -1
  59. package/src/assets/svg/male.svg +1 -1
  60. package/src/base-client/components/AI/AskAiBtn.vue +136 -136
  61. package/src/base-client/components/AI/demo.vue +31 -31
  62. package/src/base-client/components/common/AddressSearchCombobox/IcMapIcon.vue +16 -16
  63. package/src/base-client/components/common/AddressSearchCombobox/demo.vue +36 -36
  64. package/src/base-client/components/common/AddressSearchCombobox/ic_map.svg +6 -6
  65. package/src/base-client/components/common/AmapMarker/AmapPointRendering.vue +120 -120
  66. package/src/base-client/components/common/CitySelect/index.js +3 -3
  67. package/src/base-client/components/common/CitySelect/index.md +109 -109
  68. package/src/base-client/components/common/CreateQuery/CreateQuery.vue +669 -669
  69. package/src/base-client/components/common/CreateQuery/CreateQueryItem.vue +1014 -1014
  70. package/src/base-client/components/common/CreateQuery/index.js +3 -3
  71. package/src/base-client/components/common/CreateQuery/index.md +42 -42
  72. package/src/base-client/components/common/CreateSimpleFormQuery/CreateSimpleFormQuery.vue +452 -452
  73. package/src/base-client/components/common/CreateSimpleFormQuery/CreateSimpleFormQueryItem.vue +511 -511
  74. package/src/base-client/components/common/CreateSimpleFormQuery/index.js +3 -3
  75. package/src/base-client/components/common/CreateSimpleFormQuery/index.md +42 -42
  76. package/src/base-client/components/common/FormGroupEdit/index.js +3 -3
  77. package/src/base-client/components/common/FormGroupEdit/index.md +43 -43
  78. package/src/base-client/components/common/FormGroupQuery/FormGroupQuery.vue +166 -166
  79. package/src/base-client/components/common/FormGroupQuery/index.js +3 -3
  80. package/src/base-client/components/common/FormGroupQuery/index.md +43 -43
  81. package/src/base-client/components/common/HIS/HButtons/HButtons.vue +444 -444
  82. package/src/base-client/components/common/HIS/HForm/HForm.vue +361 -361
  83. package/src/base-client/components/common/HIS/HFormGroup/index.js +3 -3
  84. package/src/base-client/components/common/HIS/HTab/HTab.vue +443 -443
  85. package/src/base-client/components/common/HIS/demo.vue +61 -61
  86. package/src/base-client/components/common/JSONToTree/jsontotree.vue +271 -271
  87. package/src/base-client/components/common/LowCodeComponent/LowCodeEditorModal.vue +108 -108
  88. package/src/base-client/components/common/LowCodeComponent/LowCodeEditorPanel.vue +413 -413
  89. package/src/base-client/components/common/LowCodeComponent/LowCodePageOrganization.vue +502 -502
  90. package/src/base-client/components/common/LowCodeComponent/LowCodeRender.vue +728 -728
  91. package/src/base-client/components/common/LowCodeComponent/LowCodeRenderEnter.vue +29 -29
  92. package/src/base-client/components/common/LowCodeComponent/LowCodeUIStore.vue +219 -219
  93. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeAddPageModal.vue +117 -117
  94. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeCustomJSModal.vue +80 -80
  95. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeEventEditorModal.vue +398 -398
  96. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeLifeCycleModal.vue +65 -65
  97. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeLogicCallbackModal.vue +64 -64
  98. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeLogicParamModal.vue +73 -73
  99. package/src/base-client/components/common/LowCodeComponent/modal/lowCodeRunFunctionParamModal.vue +76 -76
  100. package/src/base-client/components/common/PersonSetting/PersonSetting.vue +208 -208
  101. package/src/base-client/components/common/PersonSetting/index.js +3 -3
  102. package/src/base-client/components/common/Recording/Recording.vue +243 -243
  103. package/src/base-client/components/common/Recording/index.js +3 -3
  104. package/src/base-client/components/common/Tree/Tree.vue +149 -149
  105. package/src/base-client/components/common/Tree/index.js +2 -2
  106. package/src/base-client/components/common/Upload/Upload.vue +334 -334
  107. package/src/base-client/components/common/Upload/index.js +3 -3
  108. package/src/base-client/components/common/XAddForm/XAddForm.vue +113 -113
  109. package/src/base-client/components/common/XAddNativeForm/index.md +146 -146
  110. package/src/base-client/components/common/XAddNativeFormOA/XAddNativeFormOA.vue +304 -304
  111. package/src/base-client/components/common/XAddNativeFormOA/index.js +3 -3
  112. package/src/base-client/components/common/XAddNativeFormOA/index.md +146 -146
  113. package/src/base-client/components/common/XAddReport/index.js +3 -3
  114. package/src/base-client/components/common/XAddReport/index.md +56 -56
  115. package/src/base-client/components/common/XBadge/XBadge.vue +94 -94
  116. package/src/base-client/components/common/XButtons/XButtonDemo.vue +28 -28
  117. package/src/base-client/components/common/XButtons/index.js +3 -3
  118. package/src/base-client/components/common/XButtons/index.md +61 -61
  119. package/src/base-client/components/common/XCalendar/XCalendar.vue +4 -4
  120. package/src/base-client/components/common/XCard/XCard.vue +64 -64
  121. package/src/base-client/components/common/XCheckList/XCheckList.vue +106 -106
  122. package/src/base-client/components/common/XCheckList/XCheckListDemo.vue +41 -41
  123. package/src/base-client/components/common/XCollapse/XCollapse.vue +833 -833
  124. package/src/base-client/components/common/XDataCard/index.js +3 -3
  125. package/src/base-client/components/common/XDataCard/index.md +1 -1
  126. package/src/base-client/components/common/XDataDrawer/XDataDrawer.vue +180 -180
  127. package/src/base-client/components/common/XDataDrawer/index.js +3 -3
  128. package/src/base-client/components/common/XDataDrawer/index.md +41 -41
  129. package/src/base-client/components/common/XDatePicker/demo.vue +153 -153
  130. package/src/base-client/components/common/XDescriptions/index.js +3 -3
  131. package/src/base-client/components/common/XDescriptions/index.md +83 -83
  132. package/src/base-client/components/common/XDetailsView/XDetailsView.vue +238 -238
  133. package/src/base-client/components/common/XDetailsView/index.js +3 -3
  134. package/src/base-client/components/common/XForm/XFormItem.vue +17 -1
  135. package/src/base-client/components/common/XForm/XStatusButton.vue +54 -54
  136. package/src/base-client/components/common/XForm/index.md +178 -178
  137. package/src/base-client/components/common/XForm/itemComponent/XClickChangeBtn/index.vue +49 -49
  138. package/src/base-client/components/common/XFormGroup/index.js +3 -3
  139. package/src/base-client/components/common/XFormGroup/index.md +38 -38
  140. package/src/base-client/components/common/XFormGroupDetails/index.js +3 -3
  141. package/src/base-client/components/common/XFormTable/demo.vue +2 -2
  142. package/src/base-client/components/common/XFormTable/index.md +92 -92
  143. package/src/base-client/components/common/XInput/XInput.vue +205 -205
  144. package/src/base-client/components/common/XLabelSelect/XLabelSelect.vue +110 -110
  145. package/src/base-client/components/common/XLabelSelect/XLabelSelectDemo.vue +35 -35
  146. package/src/base-client/components/common/XLicensePlate/XLicensePlate.vue +193 -193
  147. package/src/base-client/components/common/XLicensePlate/XLicensePlateDemo.vue +48 -48
  148. package/src/base-client/components/common/XPrint/OpenInvoice.vue +21 -21
  149. package/src/base-client/components/common/XPrint/PrintHtml.js +98 -98
  150. package/src/base-client/components/common/XPrint/css/hiPrintCss.js +359 -359
  151. package/src/base-client/components/common/XPrint/css/lodopCss.js +26 -26
  152. package/src/base-client/components/common/XPrint/css/print-lock.css +351 -351
  153. package/src/base-client/components/common/XPrint/index.vue +97 -97
  154. package/src/base-client/components/common/XReport/XReportDesign.vue +463 -463
  155. package/src/base-client/components/common/XReport/XReportJsonRender.vue +381 -381
  156. package/src/base-client/components/common/XReport/index.js +3 -3
  157. package/src/base-client/components/common/XReport/print.js +186 -186
  158. package/src/base-client/components/common/XReportDrawer/index.js +3 -3
  159. package/src/base-client/components/common/XReportGrid/index.js +3 -3
  160. package/src/base-client/components/common/XReportGrid/index.md +44 -44
  161. package/src/base-client/components/common/XReportSlot/XReportSlot.vue +110 -110
  162. package/src/base-client/components/common/XReportSlot/index.js +3 -3
  163. package/src/base-client/components/common/XReportSlot/index.md +48 -48
  164. package/src/base-client/components/common/XSimpleDescriptions/XSimpleDescriptions.vue +166 -166
  165. package/src/base-client/components/common/XSimpleDescriptions/index.js +3 -3
  166. package/src/base-client/components/common/XSimpleDescriptions/index.md +7 -7
  167. package/src/base-client/components/common/XStepView/XStepView.vue +252 -252
  168. package/src/base-client/components/common/XStepView/index.js +3 -3
  169. package/src/base-client/components/common/XStepView/index.md +31 -31
  170. package/src/base-client/components/common/XTab/XTabDemo.vue +22 -22
  171. package/src/base-client/components/common/XTab/index.js +3 -3
  172. package/src/base-client/components/common/XTable/CustomFuncCel.vue +51 -51
  173. package/src/base-client/components/common/XTable/TableCellRenderer.vue +161 -161
  174. package/src/base-client/components/common/XTable/XTableWrapper.vue +28 -9
  175. package/src/base-client/components/common/XTable/index.md +255 -255
  176. package/src/base-client/components/common/XTagGroup/index.vue +52 -52
  177. package/src/base-client/components/common/XTimeline/XTimeline.vue +477 -477
  178. package/src/base-client/components/common/XTree/XTree.vue +424 -424
  179. package/src/base-client/components/common/XTree/index.js +3 -3
  180. package/src/base-client/components/common/XTree/index.md +36 -36
  181. package/src/base-client/components/common/XTreeOne/XTreeOne.vue +113 -113
  182. package/src/base-client/components/common/XTreeOne/XTreeOnePro.vue +128 -128
  183. package/src/base-client/components/common/richTextModal/index.vue +56 -56
  184. package/src/base-client/components/common/richTextModal/richDemo.vue +48 -48
  185. package/src/base-client/components/his/XCharge/XChargeDemo.vue +145 -145
  186. package/src/base-client/components/his/XHisEditor/XHisEditor.vue +705 -705
  187. package/src/base-client/components/his/XHisEditor/index.js +3 -3
  188. package/src/base-client/components/his/XList/XList.vue +829 -829
  189. package/src/base-client/components/his/XRadio/XRadio.vue +469 -469
  190. package/src/base-client/components/his/XSimpleTable/XSimpleTable.vue +159 -159
  191. package/src/base-client/components/his/XTimeSelect/XTimeSelect.vue +383 -383
  192. package/src/base-client/components/his/XTitle/XTitle.vue +274 -274
  193. package/src/base-client/components/his/XTreeRows/XTreeRows.vue +341 -341
  194. package/src/base-client/components/his/threeTestOrders/editor.vue +113 -113
  195. package/src/base-client/components/index.js +51 -51
  196. package/src/base-client/components/layout/XTreeView/XTreeView.vue +130 -130
  197. package/src/base-client/components/layout/XTreeView/index.js +3 -3
  198. package/src/base-client/components/layout/XTreeView/index.md +46 -46
  199. package/src/base-client/components/system/DictionaryDetailsView/DictionaryDetailsView.vue +232 -232
  200. package/src/base-client/components/system/QueryParamsDetailsView/QueryParamsDetailsView.vue +281 -281
  201. package/src/base-client/plugins/Config.js +19 -19
  202. package/src/base-client/plugins/GetLoginInfoService.js +183 -183
  203. package/src/base-client/plugins/Recording.js +258 -258
  204. package/src/base-client/plugins/index.js +23 -23
  205. package/src/base-client/plugins/tabs-page-plugin.js +39 -39
  206. package/src/components/Charts/Bar.vue +62 -62
  207. package/src/components/Charts/ChartCard.vue +134 -134
  208. package/src/components/Charts/Liquid.vue +67 -67
  209. package/src/components/Charts/MiniArea.vue +39 -39
  210. package/src/components/Charts/MiniBar.vue +39 -39
  211. package/src/components/Charts/MiniProgress.vue +75 -75
  212. package/src/components/Charts/MiniSmoothArea.vue +40 -40
  213. package/src/components/Charts/Radar.vue +68 -68
  214. package/src/components/Charts/RankList.vue +77 -77
  215. package/src/components/Charts/TagCloud.vue +113 -113
  216. package/src/components/Charts/TransferBar.vue +64 -64
  217. package/src/components/Charts/Trend.vue +82 -82
  218. package/src/components/Charts/chart.less +12 -12
  219. package/src/components/Charts/smooth.area.less +13 -13
  220. package/src/components/CodeMirror/inedx.vue +118 -118
  221. package/src/components/CodeMirror/setting.js +40 -40
  222. package/src/components/NumberInfo/NumberInfo.vue +54 -54
  223. package/src/components/NumberInfo/index.js +3 -3
  224. package/src/components/NumberInfo/index.less +54 -54
  225. package/src/components/NumberInfo/index.md +43 -43
  226. package/src/components/card/ChartCard.vue +79 -79
  227. package/src/components/chart/Bar.vue +60 -60
  228. package/src/components/chart/MiniArea.vue +67 -67
  229. package/src/components/chart/MiniBar.vue +59 -59
  230. package/src/components/chart/MiniProgress.vue +57 -57
  231. package/src/components/chart/Radar.vue +80 -80
  232. package/src/components/chart/RankingList.vue +60 -60
  233. package/src/components/chart/Trend.vue +79 -79
  234. package/src/components/chart/index.less +9 -9
  235. package/src/components/checkbox/ColorCheckbox.vue +157 -157
  236. package/src/components/checkbox/ImgCheckbox.vue +117 -117
  237. package/src/components/checkbox/ImgCheckboxGroup.vue +76 -76
  238. package/src/components/checkbox/index.js +9 -9
  239. package/src/components/exception/ExceptionPage.vue +70 -70
  240. package/src/components/g2Charts/constants.js +202 -202
  241. package/src/components/g2Charts/demo.vue +808 -808
  242. package/src/components/g2Charts/designer.vue +228 -228
  243. package/src/components/g2Charts/designerBaseConfig.vue +61 -61
  244. package/src/components/g2Charts/designerDataConfig.vue +259 -259
  245. package/src/components/g2Charts/designerStyleConfig.vue +16 -16
  246. package/src/components/g2Charts/index.vue +397 -397
  247. package/src/components/index.js +36 -36
  248. package/src/components/input/IInput.vue +66 -66
  249. package/src/components/menu/SideMenu.vue +75 -75
  250. package/src/components/menu/menu.js +273 -273
  251. package/src/components/setting/Setting.vue +234 -234
  252. package/src/components/tool/AStepItem.vue +60 -60
  253. package/src/config/CreateQueryConfig.js +325 -325
  254. package/src/config/default/antd.config.js +89 -89
  255. package/src/config/default/setting.config.js +55 -55
  256. package/src/font-style/font.css +60 -60
  257. package/src/layouts/CommonLayout.vue +56 -56
  258. package/src/layouts/PageLayout.vue +151 -151
  259. package/src/layouts/SinglePageView.vue +136 -136
  260. package/src/layouts/header/AdminHeader.vue +132 -132
  261. package/src/layouts/header/HeaderNotice.vue +177 -177
  262. package/src/layouts/header/InstitutionDetail.vue +181 -181
  263. package/src/layouts/tabs/TabsHead.vue +189 -189
  264. package/src/lib.js +1 -1
  265. package/src/mock/extend/index.js +84 -84
  266. package/src/mock/goods/index.js +108 -108
  267. package/src/pages/DefaultExample/index.vue +77 -77
  268. package/src/pages/DynamicStatistics/ChartSelector.vue +331 -331
  269. package/src/pages/DynamicStatistics/DataTabs.vue +83 -83
  270. package/src/pages/DynamicStatistics/DynamicTable.vue +128 -128
  271. package/src/pages/DynamicStatistics/EvaluationArea.vue +69 -69
  272. package/src/pages/DynamicStatistics/FavoriteList.vue +50 -50
  273. package/src/pages/DynamicStatistics/QuestionHistoryAndFavorites.vue +591 -591
  274. package/src/pages/DynamicStatistics/SearchBar.vue +192 -192
  275. package/src/pages/DynamicStatistics/index.vue +282 -282
  276. package/src/pages/Example/childIndex.vue +15 -15
  277. package/src/pages/Example/index.vue +30 -30
  278. package/src/pages/NewDynamicStatistics/ChartSelector.vue +331 -331
  279. package/src/pages/NewDynamicStatistics/DataTabs.vue +122 -122
  280. package/src/pages/NewDynamicStatistics/DynamicTable.vue +128 -128
  281. package/src/pages/NewDynamicStatistics/EvaluationArea.vue +69 -69
  282. package/src/pages/NewDynamicStatistics/FavoriteList.vue +50 -50
  283. package/src/pages/NewDynamicStatistics/QuestionHistoryAndFavorites.vue +289 -289
  284. package/src/pages/NewDynamicStatistics/SearchBar.vue +193 -193
  285. package/src/pages/NewDynamicStatistics/index.vue +258 -258
  286. package/src/pages/Recording/index.vue +77 -77
  287. package/src/pages/ServiceReview/index.vue +284 -284
  288. package/src/pages/SubExample/index.vue +26 -26
  289. package/src/pages/WorkflowDetail/WorkflowPageDetail/TrimTextTail.vue +23 -23
  290. package/src/pages/XReportView/index.vue +64 -64
  291. package/src/pages/XTreeOneProExample/index.vue +67 -67
  292. package/src/pages/dashboard/workplace/WorkPlace.vue +141 -141
  293. package/src/pages/login/Login.vue +379 -379
  294. package/src/pages/login/LoginV3.vue +389 -389
  295. package/src/pages/lowCode/lowCodeEditor.vue +1219 -1219
  296. package/src/pages/lowCode/lowCodeRenderPage.vue +43 -43
  297. package/src/pages/report/ReportTable.js +124 -124
  298. package/src/pages/resourceManage/orgListManage.vue +98 -98
  299. package/src/pages/system/dictionary/index.vue +44 -44
  300. package/src/pages/system/monitor/loginInfor/index.vue +37 -37
  301. package/src/pages/system/monitor/operLog/index.vue +37 -37
  302. package/src/pages/system/settings/modifyPassword.vue +117 -117
  303. package/src/pages/system/ticket/index.vue +480 -480
  304. package/src/pages/system/ticket/submitTicketSuccess.vue +484 -484
  305. package/src/pages/userInfoDetailManage/ChangeMeterRecordQuery/index.vue +64 -64
  306. package/src/pages/userInfoDetailManage/ExceptionRecordQuery/index.vue +45 -45
  307. package/src/pages/userInfoDetailManage/InfoChangeRecordQuery/index.vue +64 -64
  308. package/src/pages/userInfoDetailManage/InstructRecordQuery/index.vue +64 -64
  309. package/src/pages/userInfoDetailManage/MeterParamRecordQuery/index.vue +64 -64
  310. package/src/pages/userInfoDetailManage/TransferRecordQuery/index.vue +66 -66
  311. package/src/pages/userInfoDetailManage/WatchCollectionRecordQuery/index.vue +64 -64
  312. package/src/plugins/EventLogPlugin.js +33 -33
  313. package/src/plugins/FindParentsData.js +17 -17
  314. package/src/services/DataModel.js +30 -30
  315. package/src/services/LodopFuncs.js +137 -137
  316. package/src/services/api/TicketDetailsViewApi.js +46 -46
  317. package/src/services/api/cas.js +79 -79
  318. package/src/services/api/entity.js +18 -18
  319. package/src/services/api/index.js +17 -17
  320. package/src/store/modules/account.js +115 -115
  321. package/src/store/modules/index.js +5 -5
  322. package/src/store/modules/lowCode.js +33 -33
  323. package/src/store/modules/setting.js +119 -119
  324. package/src/theme/default/style.less +58 -58
  325. package/src/utils/authority-utils.js +85 -85
  326. package/src/utils/errorCode.js +6 -6
  327. package/src/utils/formatter.js +74 -74
  328. package/src/utils/htmlToPDF.js +108 -108
  329. package/src/utils/htmlToPDFApi.js +5 -5
  330. package/src/utils/login.js +188 -188
  331. package/src/utils/lowcode/lowcodeComponentMixin.js +120 -120
  332. package/src/utils/lowcode/lowcodeLog.js +29 -29
  333. package/src/utils/lowcode/lowcodeUtils.js +373 -373
  334. package/src/utils/lowcode/registerComponentForEditor.js +1 -1
  335. package/src/utils/lowcode/registerComponentForRender.js +11 -11
  336. package/src/utils/map-utils.js +47 -47
  337. package/src/utils/reg.js +95 -95
  338. package/src/utils/runEvalFunction.js +14 -14
  339. package/src/utils/theme-color-replacer-extend.js +92 -92
  340. package/src/utils/util.js +329 -329
  341. package/src/utils/waterMark.js +31 -31
  342. package//350/277/201/347/247/273/346/227/245/345/277/227.md +15 -15
  343. package/src-base-client/components/common/HIS/HForm/HForm.vue +0 -347
  344. package/src-base-client/components/common/XCollapse/XCollapse.vue +0 -0
@@ -1,829 +1,829 @@
1
- <template>
2
- <!-- 列表卡片模式 listMode: card -->
3
- <div
4
- class="x-list-wrapper"
5
- :class="wrapperClassObject"
6
- v-if="listMode"
7
- ref="listRef"
8
- @scroll="handleInfiniteOnLoad">
9
- <a-list
10
- :grid="{ gutter: 16, xs: 1, sm: 2, md: 2, lg: 3, xl: 3, xxl: 4 }"
11
- :data-source="localData">
12
- <a-list-item slot="renderItem" slot-scope="item, index">
13
- <div class="card-a-col" :class="{ 'selected-active': enableSelectRow && currentSelectedIndex === index }" :style="getCardStyle(currentTitleValue(index))" @click="handleCardClick(index)">
14
- <a-row class="card-row">
15
- <a-col class="id-a-col" :span="4" v-for="(detail,idx) in item.filter(d => d.label == label)" :key="idx">
16
- {{ detail.value }}
17
- <div class="gender-icon" v-if="getHasPatient(item) && getGender(item)">
18
- <img :src="getGender(item) === '男' ? maleIcon : femaleIcon" :alt="getGender(item)" class="gender-img" />
19
- </div>
20
- </a-col>
21
- <a-col :span="20" class="id-a-col-2">
22
- <template v-for="(detail,idx) in item">
23
- <div :key="`title_${idx}`" class="title-row" v-if="detail.type == 'title'">
24
- <a-tooltip :title="detail.value" placement="topLeft">
25
- <span class="describe-list-a-col" :class="{name: detail.bold}">{{ detail.value }}</span>
26
- </a-tooltip>
27
- <div class="title-actions" v-if="getTitleOptions && getTitleOptions.length">
28
- <span class="title-select-label">{{ getDisplayTitleLabel(index) }}</span>
29
- <a-dropdown placement="bottomRight" trigger="['click']">
30
- <a class="arrow-btn" @click.prevent>
31
- <a-icon type="down" />
32
- </a>
33
- <a-menu slot="overlay" :selectedKeys="[String(currentTitleValue(index))]" @click="info => handleTitleMenuClick(info, index)">
34
- <a-menu-item v-for="opt in getTitleOptions" :key="String(opt && opt.value !== undefined ? opt.value : opt)">
35
- {{ opt && opt.label !== undefined ? opt.label : opt }}
36
- </a-menu-item>
37
- </a-menu>
38
- </a-dropdown>
39
- </div>
40
- </div>
41
- <span :key="idx" class="component-a-col" v-else-if="getHasPatient(item) && detail.type == 'component' && detail.label != label">
42
- <a-tooltip :title="detail.label" placement="topLeft">
43
- <span class="label-text">{{ `${detail.label}:` }}</span>
44
- </a-tooltip>
45
- <component
46
- :is="detail.slotType"
47
- :key="idx"
48
- :ref="`dynamicComponent_${ idx.slotType || idx}_${idx}`"
49
- :serviceName="serviceName"
50
- v-on="forwardAllEvents"
51
- :queryParamsName="detail.value"
52
- :countVisible="false"
53
- />
54
- </span>
55
- <span :key="idx" class="component-a-col" v-else-if="getHasPatient(item) && detail.type == 'date'">
56
- <a-tooltip :title="detail.label" placement="topLeft">
57
- <span class="label-text">{{ `${detail.label}:` }}</span>
58
- </a-tooltip>
59
- <a-date-picker @change="onChange" />
60
- </span>
61
- <a-tooltip :key="idx" :title="`${detail.label}:${detail.value}`" placement="topLeft" v-else-if="getHasPatient(item) && detail.label != label">
62
- <span class="describe-list-a-col" :class="{name: detail.bold}">{{ `${detail.label}:${detail.value}` }}</span>
63
- </a-tooltip>
64
- </template>
65
- <a-button
66
- v-if="getHasPatient(item) && showCardButtons"
67
- v-for="(btn, i) in buttonNames"
68
- :key="i"
69
- icon="search"
70
- class="button-a-col"
71
- @click.stop="click(item, index)">
72
- {{ btn }}
73
- </a-button>
74
- </a-col>
75
- </a-row>
76
- </div>
77
- </a-list-item>
78
- <div v-if="loading" class="demo-loading-container">
79
- <a-spin />
80
- </div>
81
- <div v-if="allLoaded">
82
- <div class="demo-infinite-list-bottom">
83
- 已经显示全部数据
84
- </div>
85
- </div>
86
- </a-list>
87
- </div>
88
-
89
- <!-- 默认标签模式 -->
90
- <div class="list-wrapper x-list-wrapper" :class="wrapperClassObject" v-else>
91
- <a-list size="large" :data-source="data" itemLayout="horizontal" class="list-container" ref="listRef">
92
- <a-list-item
93
- slot="renderItem"
94
- slot-scope="item, index"
95
- class="list-item"
96
- @click="handleClick(index)"
97
- @mouseenter="enableHoverOptions && handleMouseEnter(index)"
98
- @mouseleave="handleMouseLeave"
99
- :class="{ 'hover-active': enableHoverOptions && hoveredIndex === index, 'selected-active': enableSelectRow && currentSelectedIndex === index }">
100
- <i
101
- v-if="icon"
102
- class="icon-menu"
103
- :style="getIconStyle(item)">
104
- </i>
105
- <span
106
- class="item-text">
107
- {{ item.number }} {{ item.name }}
108
- </span>
109
-
110
- <div v-if="button" class="button-group">
111
- <a-button
112
- v-for="(name, idx) in buttonNames"
113
- :key="idx"
114
- type="link"
115
- :class="['confirm-btn', buttonMode ? 'hover-btn' : '']"
116
- @click.stop="click(index, idx)">
117
- <span :class="{ 'hover-active': enableHoverOptions && hoveredIndex === index }">{{ name }}</span>
118
- </a-button>
119
- </div>
120
-
121
- <!-- 悬浮选项框 -->
122
- <div
123
- v-show="enableHoverOptions && hoveredIndex === index"
124
- class="hover-options"
125
- @mouseenter="handleOptionsEnter"
126
- @mouseleave="handleOptionsLeave">
127
- <div class="hover-options-content">
128
- <div
129
- v-for="(Item, idx) in select_options"
130
- :key="idx"
131
- class="option-item"
132
- @click="handleOptionClick(index, Item)">
133
- {{ Item }}
134
- </div>
135
- </div>
136
- </div>
137
- </a-list-item>
138
- </a-list>
139
- </div>
140
- </template>
141
-
142
- <script>
143
-
144
- import { getConfigByName, runLogic } from '@vue2-client/services/api/common'
145
-
146
- export default {
147
- name: 'XList',
148
- components: {
149
- XReport: () => import('@vue2-client/base-client/components/common/XReport/XReport.vue'),
150
- XButtons: () => import('@vue2-client/base-client/components/common/XButtons/XButtons.vue'),
151
- XInput: () => import('@vue2-client/base-client/components/common/XInput/XInput.vue'),
152
- XRadio: () => import('@vue2-client/base-client/components/his/XRadio/XRadio.vue'),
153
- XTimeSelect: () => import('@vue2-client/base-client/components/his/XTimeSelect/XTimeSelect.vue'),
154
- XCheckbox: () => import('@vue2-client/base-client/components/his/XCheckbox/XCheckbox.vue'),
155
- XTitle: () => import('@vue2-client/base-client/components/his/XTitle/XTitle.vue'),
156
- XSelect: () => import('@vue2-client/base-client/components/his/XSelect/XSelect.vue')
157
- },
158
- props: {
159
- queryParamsName: {
160
- type: Object,
161
- default: null
162
- },
163
- fixedQueryForm: {
164
- type: Object,
165
- default: { condition: '1=1' }
166
- },
167
- enableHoverOptions: {
168
- type: Boolean,
169
- default: true
170
- },
171
- serviceName: {
172
- type: String,
173
- default: 'af-his'
174
- },
175
- // 点击是否触发选中(默认不通过点击选中,仅手动控制)
176
- selectOnClick: {
177
- type: Boolean,
178
- default: false
179
- },
180
- // 受控选中索引;不传则内部维护
181
- selectedIndex: {
182
- type: Number,
183
- default: undefined
184
- },
185
- // 卡片按钮显示控制(默认隐藏,可通过属性或配置开启)
186
- showCardButtons: {
187
- type: Boolean,
188
- default: false
189
- },
190
- // 标题右侧下拉选项与受控值
191
- titleSelectOptions: {
192
- type: Array,
193
- default: () => []
194
- },
195
- titleSelectValue: {
196
- type: [String, Number],
197
- default: undefined
198
- }
199
- },
200
- data () {
201
- return {
202
- data: [], // 数据源
203
- localData: [], // 本地数据源
204
- loading: false, // 加载中
205
- busy: false, // 繁忙状态
206
- button: false,
207
- icon: false,
208
- buttonNames: [],
209
- listMode: undefined, // 列表模式
210
- buttonMode: true,
211
- hoveredIndex: -1, // 当前悬浮的索引
212
- isOptionsHovered: false, // 悬浮选项框是否悬浮
213
- hoverTimer: null, // 悬浮选项框定时器
214
- leaveTimer: null, // 离开选项框定时器
215
- select_options: [], // 悬浮选项框
216
- logicName: '',
217
- nowPage: 0, // 当前页
218
- pageSize: 12,
219
- allLoaded: false,
220
- label: 'id',
221
- scrollTimer: null,
222
- // 内部选中索引(非受控时使用)
223
- localSelectedIndex: -1,
224
- maleIcon: require('@vue2-client/assets/svg/male.svg'),
225
- femaleIcon: require('@vue2-client/assets/svg/female.svg'),
226
- // 下拉配置
227
- internalTitleOptions: [],
228
- titleValueByIndex: {},
229
- // 选择请求的轻量防抖
230
- selectDebounceTimer: null
231
- }
232
- },
233
- inject: ['getComponentByName'],
234
-
235
- created () {
236
- this.getData(this.queryParamsName, this.fixedQueryForm)
237
- },
238
- mounted () {},
239
- computed: {
240
- // 参考 HForm 的 wrapperClassObject 规则,支持通过组件属性动态控制样式
241
- wrapperClassObject () {
242
- const a = this.$attrs || {}
243
- const classes = {}
244
- // 多个布尔型样式开关(存在且为真则生效)
245
- const booleanStyleKeys = [
246
- ''
247
- ]
248
- booleanStyleKeys.forEach(key => {
249
- const val = a[key]
250
- const truthy = val === true || val === '' || val === 'true'
251
- if (truthy) classes[`x-list-${key}`] = true
252
- })
253
- return classes
254
- },
255
- // 标题下拉:优先使用外部 props,否则使用内部配置
256
- getTitleOptions () {
257
- if (Array.isArray(this.titleSelectOptions) && this.titleSelectOptions.length) return this.titleSelectOptions
258
- return this.internalTitleOptions
259
- },
260
- currentTitleValue () {
261
- return (index) => {
262
- if (this.titleSelectValue !== undefined) return this.titleSelectValue
263
- const local = this.titleValueByIndex[index]
264
- if (local !== undefined) return local
265
- // 二次回退:若模板标题提供了 titleRightValue,则用于首屏渲染颜色与文案
266
- const row = Array.isArray(this.localData) ? this.localData[index] : null
267
- if (Array.isArray(row)) {
268
- const title = row.find(d => d && d.type === 'title')
269
- if (title && title.titleRightValue !== undefined) return title.titleRightValue
270
- }
271
- // 最终回退:使用 options 的第一个
272
- const def = this.internalTitleOptions && this.internalTitleOptions.length ? (this.internalTitleOptions[0].value !== undefined ? this.internalTitleOptions[0].value : this.internalTitleOptions[0]) : undefined
273
- return def
274
- }
275
- },
276
- // 选择控制:受控优先
277
- currentSelectedIndex () {
278
- return typeof this.selectedIndex === 'number' ? this.selectedIndex : this.localSelectedIndex
279
- },
280
- enableSelectRow () {
281
- const a = this.$attrs || {}
282
- const val = a.enableSelection
283
- return val === true || val === '' || val === 'true'
284
- },
285
- forwardAllEvents () {
286
- return {
287
- // 监听所有事件并转发给父组件
288
- '*': (eventName, ...payload) => {
289
- this.$emit(eventName, ...payload)
290
- }
291
- }
292
- }
293
- },
294
- methods: {
295
- onChange (date, dateString) {
296
- this.$emit('dateChange', date, dateString)
297
- },
298
- handleInfiniteOnLoad (event) {
299
- if (this.busy || this.allLoaded) return // 防止重复加载
300
- if (this.scrollTimer) clearTimeout(this.scrollTimer)
301
- this.scrollTimer = setTimeout(() => {
302
- const container = event.target
303
- const scrollTop = container.scrollTop
304
- const scrollHeight = container.scrollHeight
305
- const clientHeight = container.clientHeight
306
- const bottomOffset = 10
307
- if (scrollTop + clientHeight >= scrollHeight - bottomOffset) {
308
- this.busy = true
309
- this.loading = true
310
- this.nowPage = this.nowPage + this.pageSize
311
- this.fixedQueryForm.condition = `Limit ${this.nowPage}, ${this.pageSize}`
312
- runLogic(this.logicName, this.fixedQueryForm, 'af-his').then(async (res) => {
313
- this.localData = [...this.localData, ...res]
314
- if (res.length < this.pageSize) {
315
- this.allLoaded = true
316
- }
317
- }).catch(e => {
318
- this.$message.error(e.message)
319
- }).finally(() => {
320
- this.loading = false
321
- this.busy = false
322
- })
323
- }
324
- }, 100)
325
- },
326
- async getData (config, param) {
327
- const that = this
328
- getConfigByName(config, 'af-his', async (res) => {
329
- that.listMode = await res.listMode == 'card'
330
- that.logicName = await res.data
331
- that.button = await res.button // 按钮
332
- that.icon = await res.icon // 图标
333
- that.label = await res.label // 标签
334
- that.buttonNames = await res.buttonNames || []// 按钮文本
335
- that.buttonMode = await res.buttonMode || false// 按钮模式
336
- that.cardButtonsVisible = await res.cardButtonsVisible || false// 卡片按钮
337
- // 标题下拉:从配置拿 options: [{label,value,color}]
338
- if (Array.isArray(res.titleOptions)) {
339
- this.internalTitleOptions = res.titleOptions
340
- if (res.titleDefaultValue !== undefined && Array.isArray(this.localData)) {
341
- // 初始化每个卡片的默认值
342
- this.titleValueByIndex = {}
343
- }
344
- }
345
- this.enableHoverOptions = await res.enableHoverOptions || false// 悬浮选项框
346
- if (this.enableHoverOptions) {
347
- this.select_options = await res.select_options
348
- }
349
- if (that.listMode) { param.condition = `Limit ${that.nowPage}, ${that.pageSize}` }
350
- runLogic(res.data, param, 'af-his').then(result => {
351
- that.data = result
352
- if (that.nowPage === 0) { this.localData = result }
353
- // 初始化每张卡片的 title 选中值:优先读取模板 titleRightValue,否则使用配置默认或第一项
354
- if (Array.isArray(this.localData)) {
355
- this.localData.forEach((row, idx) => {
356
- if (this.titleValueByIndex[idx] !== undefined) return
357
- if (Array.isArray(row)) {
358
- const title = row.find(d => d && d.type === 'title')
359
- if (title && title.titleRightValue !== undefined) {
360
- this.$set(this.titleValueByIndex, idx, title.titleRightValue)
361
- return
362
- }
363
- }
364
- // fallback:配置默认或第一项
365
- const def = (this.internalTitleOptions && this.internalTitleOptions.length)
366
- ? (this.internalTitleOptions[0].value !== undefined ? this.internalTitleOptions[0].value : this.internalTitleOptions[0])
367
- : undefined
368
- if (def !== undefined) this.$set(this.titleValueByIndex, idx, def)
369
- })
370
- }
371
- })
372
- })
373
- },
374
- // 提取性别:从数组数据中查找 label 含“性别”的项
375
- getGender (item) {
376
- if (!Array.isArray(item)) return null
377
- const g = item.find(d => d && typeof d.label === 'string' && d.label.indexOf('性别') > -1)
378
- const val = g && (g.value || g.text)
379
- if (!val) return null
380
- if (String(val).includes('男')) return '男'
381
- if (String(val).includes('女')) return '女'
382
- return null
383
- },
384
- // 读取标题项上的 hasPatient 布尔值
385
- getHasPatient (item) {
386
- if (!Array.isArray(item)) return true
387
- const title = item.find(d => d && d.type === 'title')
388
- if (!title || typeof title.hasPatient === 'undefined') return true
389
- return !!title.hasPatient
390
- },
391
- // 标题右侧下拉选择
392
- handleTitleSelectChange (val, index) {
393
- // 重复值拦截:若值未变化则直接返回
394
- const prev = this.normalizeValue(this.currentTitleValue(index))
395
- const next = this.normalizeValue(val)
396
- if (prev === next) return
397
-
398
- this.$set(this.titleValueByIndex, index, val)
399
- this.$emit('update:titleSelectValue', val)
400
- this.$emit('titleSelectChange', { index, value: val })
401
- // 变更后仅通知后端更新,不处理返回值;支持从标题项读取 extraParams 合并(如住院号)
402
- const extra = this.getTitleExtraParams(index)
403
- if (this.selectDebounceTimer) clearTimeout(this.selectDebounceTimer)
404
- this.selectDebounceTimer = setTimeout(() => {
405
- runLogic(this.logicName, Object.assign({ type: 'select', value: val }, extra), 'af-his').catch(() => {})
406
- }, 200)
407
- },
408
- // 读取标题项中额外需要传递的参数(可选),例如 { inpatientNo: row.t_id }
409
- getTitleExtraParams (index) {
410
- const row = Array.isArray(this.localData) ? this.localData[index] : null
411
- if (!Array.isArray(row)) return {}
412
- const title = row.find(d => d && d.type === 'title') || {}
413
- if (title && title.extraParams && typeof title.extraParams === 'object') return title.extraParams
414
- return {}
415
- },
416
- handleTitleMenuClick ({ key }, index) {
417
- const v = isNaN(Number(key)) ? key : Number(key)
418
- this.handleTitleSelectChange(v, index)
419
- },
420
- // 根据当前选择项的 color 设置卡片背景色
421
- normalizeValue (v) { return String(v) },
422
- getCardStyle (val) {
423
- const target = this.normalizeValue(val)
424
- const opt = (this.getTitleOptions || []).find(o => this.normalizeValue(o && (o.value !== undefined ? o.value : o)) === target)
425
- const color = opt && (opt.color || (opt.style && opt.style.background))
426
- return color ? { backgroundColor: color } : {}
427
- },
428
- getTitleLabel (val) {
429
- const target = this.normalizeValue(val)
430
- const opt = (this.getTitleOptions || []).find(o => this.normalizeValue(o && (o.value !== undefined ? o.value : o)) === target)
431
- return opt && (opt.label !== undefined ? opt.label : opt)
432
- },
433
- getDisplayTitleLabel (index) {
434
- // 优先展示“当前选中值”的 label,未选中再回退到模板提供的 titleRightLabel
435
- const current = this.getTitleLabel(this.currentTitleValue(index))
436
- if (current) return current
437
- const title = Array.isArray(this.localData && this.localData[index])
438
- ? this.localData[index].find(d => d && d.type === 'title')
439
- : null
440
- return title && title.titleRightLabel ? title.titleRightLabel : ''
441
- },
442
- // 点击列表项
443
- handleClick (index) {
444
- if (this.enableSelectRow && this.selectOnClick) this.setSelectedIndex(index)
445
- this.$emit('listClick', this.data[index])
446
- },
447
- // 卡片模式点击卡片
448
- handleCardClick (index) {
449
- if (this.enableSelectRow && this.selectOnClick) this.setSelectedIndex(index)
450
- this.$emit('listClick', this.localData[index])
451
- },
452
- // 外部可调用:设置选中索引
453
- setSelectedIndex (index) {
454
- const next = typeof index === 'number' ? index : -1
455
- if (typeof this.selectedIndex === 'number') {
456
- // 受控:仅派发事件
457
- this.$emit('update:selectedIndex', next)
458
- } else {
459
- // 非受控:更新内部并派发事件
460
- this.localSelectedIndex = next
461
- this.$emit('update:selectedIndex', next)
462
- }
463
- },
464
- // 外部可调用:清空选中
465
- clearSelected () { this.setSelectedIndex(-1) },
466
- refreshList (param) {
467
- this.getData(this.queryParamsName, param)
468
- },
469
- click (index, buttonIndex) {
470
- this.$emit('click', { data: this.data[index], name: this.buttonNames[buttonIndex] })
471
- },
472
- // 根据对象字段匹配选中(默认按 id 字段)
473
- setSelectedById (id, field = 'id') {
474
- const list = this.listMode ? this.localData : this.data
475
- if (!Array.isArray(list)) return
476
- const index = list.findIndex(item => {
477
- if (Array.isArray(item)) {
478
- const f = item.find(d => d && d.label === field)
479
- return f && f.value === id
480
- }
481
- return item && item[field] === id
482
- })
483
- if (index >= 0) this.setSelectedIndex(index)
484
- },
485
- // 根据 label/value(卡片数组数据场景)匹配选中
486
- setSelectedByLabelValue (label, value) {
487
- const list = this.listMode ? this.localData : this.data
488
- if (!Array.isArray(list)) return
489
- const index = list.findIndex(item => Array.isArray(item) && item.some(d => d && d.label === label && d.value === value))
490
- if (index >= 0) this.setSelectedIndex(index)
491
- },
492
- // 通用:传入谓词函数决定选中项
493
- setSelectedBy (predicate) {
494
- if (typeof predicate !== 'function') return
495
- const list = this.listMode ? this.localData : this.data
496
- if (!Array.isArray(list)) return
497
- const index = list.findIndex(predicate)
498
- if (index >= 0) this.setSelectedIndex(index)
499
- },
500
- getIconStyle (item) {
501
- return item.picture
502
- ? { backgroundImage: `url(${item.picture})` }
503
- : {}
504
- },
505
- filterData (par) {
506
- runLogic(this.queryParamsName, par, 'af-his').then(res => {
507
- this.data = res.data
508
- })
509
- },
510
- // 鼠标进入列表项
511
- handleMouseEnter (index) {
512
- this.clearAllTimers()
513
- this.hoveredIndex = index
514
- this.isOptionsHovered = true
515
- },
516
- // 鼠标离开列表项
517
- handleMouseLeave () {
518
- this.clearAllTimers()
519
- this.leaveTimer = setTimeout(() => {
520
- this.isOptionsHovered = false
521
- this.hoveredIndex = -1
522
- }, 100)
523
- },
524
- // 鼠标进入悬浮选项框
525
- handleOptionsEnter () {
526
- this.clearAllTimers()
527
- this.isOptionsHovered = true
528
- },
529
- // 鼠标离开悬浮选项框
530
- handleOptionsLeave () {
531
- this.clearAllTimers()
532
- this.leaveTimer = setTimeout(() => {
533
- this.isOptionsHovered = false
534
- this.hoveredIndex = -1
535
- }, 100)
536
- },
537
- // 清除所有定时器
538
- clearAllTimers () {
539
- if (this.hoverTimer) {
540
- clearTimeout(this.hoverTimer)
541
- this.hoverTimer = null
542
- }
543
- if (this.leaveTimer) {
544
- clearTimeout(this.leaveTimer)
545
- this.leaveTimer = null
546
- }
547
- },
548
- // 选项框点击
549
- handleOptionClick (index, action) {
550
- this.$emit('optionClick', { data: this.data[index], action })
551
- }
552
- },
553
- watch: {
554
- // 同步受控值变化
555
- selectedIndex (val) {
556
- if (typeof val === 'number') {
557
- // 允许受控值影响内部显示
558
- this.localSelectedIndex = val
559
- }
560
- },
561
- fixedQueryForm: {
562
- deep: true,
563
- handler (val) {
564
- this.refreshList(val)
565
- }
566
- }
567
- },
568
- beforeDestroy () {
569
- const ref = this.$refs.listRef
570
- if (ref && ref.removeEventListener) {
571
- ref.removeEventListener('scroll', this.handleInfiniteOnLoad)
572
- }
573
- this.clearAllTimers()
574
- }
575
- }
576
- </script>
577
-
578
- <style scoped>
579
- .list-wrapper {
580
- max-height: 240px;
581
- overflow-y: auto;
582
- padding-right: 2px;
583
- }
584
-
585
- .list-container {
586
- width: 100%;
587
- }
588
-
589
- .list-item {
590
- height: 35px;
591
- border-radius: 6px;
592
- background-color: #F4F4F4;
593
- padding: 8px 15px;
594
- font-size: 16px;
595
- display: flex;
596
- align-items: center;
597
- width: 100%;
598
- border: 1px solid #D9D9D9;
599
- box-sizing: border-box;
600
- margin-bottom: 8px !important;
601
- position: relative;
602
- transition: background-color 0.3s ease;
603
- }
604
-
605
- .icon-menu {
606
- display: inline-block;
607
- width: 20px;
608
- height: 20px;
609
- background-color: #ccc;
610
- margin-right: 8px;
611
- }
612
-
613
- .item-text {
614
- flex: 1;
615
- }
616
-
617
- .confirm-btn {
618
- margin-left: auto;
619
- padding: 0 8px;
620
- }
621
-
622
- .confirm-btn.hover-btn {
623
- opacity: 0;
624
- transition: opacity 0.3s ease;
625
- }
626
-
627
- .button-group {
628
- display: flex;
629
- gap: 2px; /* 按钮之间的间距 */
630
- }
631
-
632
- .list-item:hover .confirm-btn.hover-btn {
633
- opacity: 1;
634
- }
635
-
636
- /* 自定义滚动条样式 */
637
- .list-wrapper::-webkit-scrollbar {
638
- width: 6px;
639
- }
640
-
641
- .list-wrapper::-webkit-scrollbar-thumb {
642
- background-color: #d9d9d9;
643
- border-radius: 3px;
644
- }
645
-
646
- .list-wrapper::-webkit-scrollbar-track {
647
- background-color: #f0f0f0;
648
- }
649
-
650
- .hover-active {
651
- color: white;
652
- }
653
-
654
- .list-item.hover-active {
655
- background-color: rgb(0, 87, 254) !important;
656
- color: white;
657
- border: 1px solid black;
658
- }
659
-
660
- /* 选中态样式(通过 selectRow 开启) */
661
- .selected-active { color: white; }
662
- .list-item.selected-active {
663
- background-color: #0057FE !important;
664
- color: white;
665
- border: none !important;
666
- }
667
- .list-item.selected-active .confirm-btn,
668
- .list-item.selected-active .confirm-btn span,
669
- .list-item.selected-active .ant-btn,
670
- .list-item.selected-active .ant-btn > span { color: #ffffff !important; }
671
- .card-a-col.selected-active {
672
- background-color: #0057FE !important;
673
- color: white;
674
- border: none !important;
675
- }
676
- .card-a-col.selected-active .button-a-col,
677
- .card-a-col.selected-active .button-a-col .ant-btn,
678
- .card-a-col.selected-active .button-a-col .ant-btn > span { color: #ffffff !important; }
679
-
680
- .hover-options {
681
- position: absolute;
682
- left: 0;
683
- right: 0;
684
- top: 100%;
685
- background: white;
686
- border: 1px solid #d9d9d9;
687
- border-radius: 4px;
688
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
689
- z-index: 1000;
690
- margin-top: 4px;
691
- width: 100%;
692
- box-sizing: border-box;
693
- pointer-events: auto;
694
- }
695
-
696
- .hover-options-content {
697
- padding: 4px 0;
698
- display: flex;
699
- flex-direction: column;
700
- width: 100%;
701
- }
702
-
703
- .option-item {
704
- padding: 8px 12px;
705
- cursor: pointer;
706
- transition: all 0.3s ease;
707
- color: #333;
708
- font-size: 14px;
709
- display: flex;
710
- align-items: center;
711
- }
712
-
713
- .option-item:hover {
714
- background-color: #f5f5f5;
715
- color: #1890ff;
716
- }
717
-
718
- .option-item:active {
719
- background-color: #e6f7ff;
720
- }
721
-
722
- .demo-loading-container {
723
- position: absolute;
724
- bottom: 40px;
725
- width: 100%;
726
- text-align: center;
727
- }
728
-
729
- .demo-infinite-container{
730
- overflow-x: hidden;
731
- overflow-y: auto;
732
- height: 85vh;
733
- }
734
- .card-row{
735
- height: 100%;
736
- width: 100%;
737
- border-radius: 6px;
738
- padding: 6px;
739
- }
740
- .id-a-col{
741
- font-size: 22px;
742
- font-weight: 700;
743
- display: flex;
744
- flex-direction: column;
745
- align-items: center;
746
- justify-content: center;
747
- gap: 4px;
748
- }
749
- .id-a-col-2{
750
- padding: 8px;
751
- height: 100%;
752
- overflow: hidden;
753
- display: flex;
754
- flex-direction: column;
755
- justify-content: space-between;
756
- }
757
- .describe-list-a-col{
758
- display: block;
759
- font-family: Source Han Sans;
760
- font-size: 16px;
761
- font-weight: normal;
762
- line-height: normal;
763
- overflow: hidden;
764
- text-overflow: ellipsis;
765
- white-space: nowrap;
766
- max-width: 100%;
767
- }
768
- .name{
769
- font-family: Source Han Sans;
770
- font-size: 22px;
771
- font-weight: bold;
772
- line-height: normal;
773
- }
774
- .component-a-col{
775
- display: flex;
776
- align-items: center;
777
- overflow: hidden;
778
- }
779
- .label-text{
780
- overflow: hidden;
781
- text-overflow: ellipsis;
782
- white-space: nowrap;
783
- max-width: 120px;
784
- display: inline-block;
785
- }
786
- .gender-icon{
787
- margin-top: 2px;
788
- }
789
- .gender-icon .male{ color: #2f54eb; font-size: 14px; }
790
- .gender-icon .female{ color: #eb2f96; font-size: 14px; }
791
- .gender-img{ width: 100%; height: 100%; opacity: 1; display: inline-block; }
792
- .title-row{
793
- display: flex;
794
- align-items: center;
795
- justify-content: space-between;
796
- gap: 8px;
797
- }
798
- /* 标题文本占满剩余空间,确保箭头贴右 */
799
- .title-row .describe-list-a-col{ flex: 1; }
800
- .title-actions{ display: inline-flex; align-items: center; gap: 6px; }
801
- .title-select-label{
802
- font-family: Source Han Sans;
803
- font-size: 16px;
804
- font-weight: normal;
805
- line-height: normal;
806
- color: #595959;
807
- }
808
- .title-select{ min-width: 96px; }
809
- .arrow-btn{ display: inline-flex; align-items: center; color: #8C8C8C; }
810
- .arrow-btn .anticon{ font-size: 18px; }
811
- /* 放大下拉箭头的 svg 尺寸以符合 UI(scoped 深度选择) */
812
- ::v-deep .arrow-btn .anticon svg{ width: 2em; height: 2em; }
813
- .button-a-col{
814
- position: absolute;
815
- top: 245px;
816
- left: 225px;
817
- }
818
- .card-a-col{
819
- background-color: rgba(247, 249, 252);
820
- height: 297px;
821
- width: auto;
822
- border-radius: 6px;
823
- border: 1px solid #E5E9F0;
824
- overflow: hidden;
825
- position: relative;
826
- box-sizing: border-box;
827
- margin: 10px;
828
- }
829
- </style>
1
+ <template>
2
+ <!-- 列表卡片模式 listMode: card -->
3
+ <div
4
+ class="x-list-wrapper"
5
+ :class="wrapperClassObject"
6
+ v-if="listMode"
7
+ ref="listRef"
8
+ @scroll="handleInfiniteOnLoad">
9
+ <a-list
10
+ :grid="{ gutter: 16, xs: 1, sm: 2, md: 2, lg: 3, xl: 3, xxl: 4 }"
11
+ :data-source="localData">
12
+ <a-list-item slot="renderItem" slot-scope="item, index">
13
+ <div class="card-a-col" :class="{ 'selected-active': enableSelectRow && currentSelectedIndex === index }" :style="getCardStyle(currentTitleValue(index))" @click="handleCardClick(index)">
14
+ <a-row class="card-row">
15
+ <a-col class="id-a-col" :span="4" v-for="(detail,idx) in item.filter(d => d.label == label)" :key="idx">
16
+ {{ detail.value }}
17
+ <div class="gender-icon" v-if="getHasPatient(item) && getGender(item)">
18
+ <img :src="getGender(item) === '男' ? maleIcon : femaleIcon" :alt="getGender(item)" class="gender-img" />
19
+ </div>
20
+ </a-col>
21
+ <a-col :span="20" class="id-a-col-2">
22
+ <template v-for="(detail,idx) in item">
23
+ <div :key="`title_${idx}`" class="title-row" v-if="detail.type == 'title'">
24
+ <a-tooltip :title="detail.value" placement="topLeft">
25
+ <span class="describe-list-a-col" :class="{name: detail.bold}">{{ detail.value }}</span>
26
+ </a-tooltip>
27
+ <div class="title-actions" v-if="getTitleOptions && getTitleOptions.length">
28
+ <span class="title-select-label">{{ getDisplayTitleLabel(index) }}</span>
29
+ <a-dropdown placement="bottomRight" trigger="['click']">
30
+ <a class="arrow-btn" @click.prevent>
31
+ <a-icon type="down" />
32
+ </a>
33
+ <a-menu slot="overlay" :selectedKeys="[String(currentTitleValue(index))]" @click="info => handleTitleMenuClick(info, index)">
34
+ <a-menu-item v-for="opt in getTitleOptions" :key="String(opt && opt.value !== undefined ? opt.value : opt)">
35
+ {{ opt && opt.label !== undefined ? opt.label : opt }}
36
+ </a-menu-item>
37
+ </a-menu>
38
+ </a-dropdown>
39
+ </div>
40
+ </div>
41
+ <span :key="idx" class="component-a-col" v-else-if="getHasPatient(item) && detail.type == 'component' && detail.label != label">
42
+ <a-tooltip :title="detail.label" placement="topLeft">
43
+ <span class="label-text">{{ `${detail.label}:` }}</span>
44
+ </a-tooltip>
45
+ <component
46
+ :is="detail.slotType"
47
+ :key="idx"
48
+ :ref="`dynamicComponent_${ idx.slotType || idx}_${idx}`"
49
+ :serviceName="serviceName"
50
+ v-on="forwardAllEvents"
51
+ :queryParamsName="detail.value"
52
+ :countVisible="false"
53
+ />
54
+ </span>
55
+ <span :key="idx" class="component-a-col" v-else-if="getHasPatient(item) && detail.type == 'date'">
56
+ <a-tooltip :title="detail.label" placement="topLeft">
57
+ <span class="label-text">{{ `${detail.label}:` }}</span>
58
+ </a-tooltip>
59
+ <a-date-picker @change="onChange" />
60
+ </span>
61
+ <a-tooltip :key="idx" :title="`${detail.label}:${detail.value}`" placement="topLeft" v-else-if="getHasPatient(item) && detail.label != label">
62
+ <span class="describe-list-a-col" :class="{name: detail.bold}">{{ `${detail.label}:${detail.value}` }}</span>
63
+ </a-tooltip>
64
+ </template>
65
+ <a-button
66
+ v-if="getHasPatient(item) && showCardButtons"
67
+ v-for="(btn, i) in buttonNames"
68
+ :key="i"
69
+ icon="search"
70
+ class="button-a-col"
71
+ @click.stop="click(item, index)">
72
+ {{ btn }}
73
+ </a-button>
74
+ </a-col>
75
+ </a-row>
76
+ </div>
77
+ </a-list-item>
78
+ <div v-if="loading" class="demo-loading-container">
79
+ <a-spin />
80
+ </div>
81
+ <div v-if="allLoaded">
82
+ <div class="demo-infinite-list-bottom">
83
+ 已经显示全部数据
84
+ </div>
85
+ </div>
86
+ </a-list>
87
+ </div>
88
+
89
+ <!-- 默认标签模式 -->
90
+ <div class="list-wrapper x-list-wrapper" :class="wrapperClassObject" v-else>
91
+ <a-list size="large" :data-source="data" itemLayout="horizontal" class="list-container" ref="listRef">
92
+ <a-list-item
93
+ slot="renderItem"
94
+ slot-scope="item, index"
95
+ class="list-item"
96
+ @click="handleClick(index)"
97
+ @mouseenter="enableHoverOptions && handleMouseEnter(index)"
98
+ @mouseleave="handleMouseLeave"
99
+ :class="{ 'hover-active': enableHoverOptions && hoveredIndex === index, 'selected-active': enableSelectRow && currentSelectedIndex === index }">
100
+ <i
101
+ v-if="icon"
102
+ class="icon-menu"
103
+ :style="getIconStyle(item)">
104
+ </i>
105
+ <span
106
+ class="item-text">
107
+ {{ item.number }} {{ item.name }}
108
+ </span>
109
+
110
+ <div v-if="button" class="button-group">
111
+ <a-button
112
+ v-for="(name, idx) in buttonNames"
113
+ :key="idx"
114
+ type="link"
115
+ :class="['confirm-btn', buttonMode ? 'hover-btn' : '']"
116
+ @click.stop="click(index, idx)">
117
+ <span :class="{ 'hover-active': enableHoverOptions && hoveredIndex === index }">{{ name }}</span>
118
+ </a-button>
119
+ </div>
120
+
121
+ <!-- 悬浮选项框 -->
122
+ <div
123
+ v-show="enableHoverOptions && hoveredIndex === index"
124
+ class="hover-options"
125
+ @mouseenter="handleOptionsEnter"
126
+ @mouseleave="handleOptionsLeave">
127
+ <div class="hover-options-content">
128
+ <div
129
+ v-for="(Item, idx) in select_options"
130
+ :key="idx"
131
+ class="option-item"
132
+ @click="handleOptionClick(index, Item)">
133
+ {{ Item }}
134
+ </div>
135
+ </div>
136
+ </div>
137
+ </a-list-item>
138
+ </a-list>
139
+ </div>
140
+ </template>
141
+
142
+ <script>
143
+
144
+ import { getConfigByName, runLogic } from '@vue2-client/services/api/common'
145
+
146
+ export default {
147
+ name: 'XList',
148
+ components: {
149
+ XReport: () => import('@vue2-client/base-client/components/common/XReport/XReport.vue'),
150
+ XButtons: () => import('@vue2-client/base-client/components/common/XButtons/XButtons.vue'),
151
+ XInput: () => import('@vue2-client/base-client/components/common/XInput/XInput.vue'),
152
+ XRadio: () => import('@vue2-client/base-client/components/his/XRadio/XRadio.vue'),
153
+ XTimeSelect: () => import('@vue2-client/base-client/components/his/XTimeSelect/XTimeSelect.vue'),
154
+ XCheckbox: () => import('@vue2-client/base-client/components/his/XCheckbox/XCheckbox.vue'),
155
+ XTitle: () => import('@vue2-client/base-client/components/his/XTitle/XTitle.vue'),
156
+ XSelect: () => import('@vue2-client/base-client/components/his/XSelect/XSelect.vue')
157
+ },
158
+ props: {
159
+ queryParamsName: {
160
+ type: Object,
161
+ default: null
162
+ },
163
+ fixedQueryForm: {
164
+ type: Object,
165
+ default: { condition: '1=1' }
166
+ },
167
+ enableHoverOptions: {
168
+ type: Boolean,
169
+ default: true
170
+ },
171
+ serviceName: {
172
+ type: String,
173
+ default: 'af-his'
174
+ },
175
+ // 点击是否触发选中(默认不通过点击选中,仅手动控制)
176
+ selectOnClick: {
177
+ type: Boolean,
178
+ default: false
179
+ },
180
+ // 受控选中索引;不传则内部维护
181
+ selectedIndex: {
182
+ type: Number,
183
+ default: undefined
184
+ },
185
+ // 卡片按钮显示控制(默认隐藏,可通过属性或配置开启)
186
+ showCardButtons: {
187
+ type: Boolean,
188
+ default: false
189
+ },
190
+ // 标题右侧下拉选项与受控值
191
+ titleSelectOptions: {
192
+ type: Array,
193
+ default: () => []
194
+ },
195
+ titleSelectValue: {
196
+ type: [String, Number],
197
+ default: undefined
198
+ }
199
+ },
200
+ data () {
201
+ return {
202
+ data: [], // 数据源
203
+ localData: [], // 本地数据源
204
+ loading: false, // 加载中
205
+ busy: false, // 繁忙状态
206
+ button: false,
207
+ icon: false,
208
+ buttonNames: [],
209
+ listMode: undefined, // 列表模式
210
+ buttonMode: true,
211
+ hoveredIndex: -1, // 当前悬浮的索引
212
+ isOptionsHovered: false, // 悬浮选项框是否悬浮
213
+ hoverTimer: null, // 悬浮选项框定时器
214
+ leaveTimer: null, // 离开选项框定时器
215
+ select_options: [], // 悬浮选项框
216
+ logicName: '',
217
+ nowPage: 0, // 当前页
218
+ pageSize: 12,
219
+ allLoaded: false,
220
+ label: 'id',
221
+ scrollTimer: null,
222
+ // 内部选中索引(非受控时使用)
223
+ localSelectedIndex: -1,
224
+ maleIcon: require('@vue2-client/assets/svg/male.svg'),
225
+ femaleIcon: require('@vue2-client/assets/svg/female.svg'),
226
+ // 下拉配置
227
+ internalTitleOptions: [],
228
+ titleValueByIndex: {},
229
+ // 选择请求的轻量防抖
230
+ selectDebounceTimer: null
231
+ }
232
+ },
233
+ inject: ['getComponentByName'],
234
+
235
+ created () {
236
+ this.getData(this.queryParamsName, this.fixedQueryForm)
237
+ },
238
+ mounted () {},
239
+ computed: {
240
+ // 参考 HForm 的 wrapperClassObject 规则,支持通过组件属性动态控制样式
241
+ wrapperClassObject () {
242
+ const a = this.$attrs || {}
243
+ const classes = {}
244
+ // 多个布尔型样式开关(存在且为真则生效)
245
+ const booleanStyleKeys = [
246
+ ''
247
+ ]
248
+ booleanStyleKeys.forEach(key => {
249
+ const val = a[key]
250
+ const truthy = val === true || val === '' || val === 'true'
251
+ if (truthy) classes[`x-list-${key}`] = true
252
+ })
253
+ return classes
254
+ },
255
+ // 标题下拉:优先使用外部 props,否则使用内部配置
256
+ getTitleOptions () {
257
+ if (Array.isArray(this.titleSelectOptions) && this.titleSelectOptions.length) return this.titleSelectOptions
258
+ return this.internalTitleOptions
259
+ },
260
+ currentTitleValue () {
261
+ return (index) => {
262
+ if (this.titleSelectValue !== undefined) return this.titleSelectValue
263
+ const local = this.titleValueByIndex[index]
264
+ if (local !== undefined) return local
265
+ // 二次回退:若模板标题提供了 titleRightValue,则用于首屏渲染颜色与文案
266
+ const row = Array.isArray(this.localData) ? this.localData[index] : null
267
+ if (Array.isArray(row)) {
268
+ const title = row.find(d => d && d.type === 'title')
269
+ if (title && title.titleRightValue !== undefined) return title.titleRightValue
270
+ }
271
+ // 最终回退:使用 options 的第一个
272
+ const def = this.internalTitleOptions && this.internalTitleOptions.length ? (this.internalTitleOptions[0].value !== undefined ? this.internalTitleOptions[0].value : this.internalTitleOptions[0]) : undefined
273
+ return def
274
+ }
275
+ },
276
+ // 选择控制:受控优先
277
+ currentSelectedIndex () {
278
+ return typeof this.selectedIndex === 'number' ? this.selectedIndex : this.localSelectedIndex
279
+ },
280
+ enableSelectRow () {
281
+ const a = this.$attrs || {}
282
+ const val = a.enableSelection
283
+ return val === true || val === '' || val === 'true'
284
+ },
285
+ forwardAllEvents () {
286
+ return {
287
+ // 监听所有事件并转发给父组件
288
+ '*': (eventName, ...payload) => {
289
+ this.$emit(eventName, ...payload)
290
+ }
291
+ }
292
+ }
293
+ },
294
+ methods: {
295
+ onChange (date, dateString) {
296
+ this.$emit('dateChange', date, dateString)
297
+ },
298
+ handleInfiniteOnLoad (event) {
299
+ if (this.busy || this.allLoaded) return // 防止重复加载
300
+ if (this.scrollTimer) clearTimeout(this.scrollTimer)
301
+ this.scrollTimer = setTimeout(() => {
302
+ const container = event.target
303
+ const scrollTop = container.scrollTop
304
+ const scrollHeight = container.scrollHeight
305
+ const clientHeight = container.clientHeight
306
+ const bottomOffset = 10
307
+ if (scrollTop + clientHeight >= scrollHeight - bottomOffset) {
308
+ this.busy = true
309
+ this.loading = true
310
+ this.nowPage = this.nowPage + this.pageSize
311
+ this.fixedQueryForm.condition = `Limit ${this.nowPage}, ${this.pageSize}`
312
+ runLogic(this.logicName, this.fixedQueryForm, 'af-his').then(async (res) => {
313
+ this.localData = [...this.localData, ...res]
314
+ if (res.length < this.pageSize) {
315
+ this.allLoaded = true
316
+ }
317
+ }).catch(e => {
318
+ this.$message.error(e.message)
319
+ }).finally(() => {
320
+ this.loading = false
321
+ this.busy = false
322
+ })
323
+ }
324
+ }, 100)
325
+ },
326
+ async getData (config, param) {
327
+ const that = this
328
+ getConfigByName(config, 'af-his', async (res) => {
329
+ that.listMode = await res.listMode == 'card'
330
+ that.logicName = await res.data
331
+ that.button = await res.button // 按钮
332
+ that.icon = await res.icon // 图标
333
+ that.label = await res.label // 标签
334
+ that.buttonNames = await res.buttonNames || []// 按钮文本
335
+ that.buttonMode = await res.buttonMode || false// 按钮模式
336
+ that.cardButtonsVisible = await res.cardButtonsVisible || false// 卡片按钮
337
+ // 标题下拉:从配置拿 options: [{label,value,color}]
338
+ if (Array.isArray(res.titleOptions)) {
339
+ this.internalTitleOptions = res.titleOptions
340
+ if (res.titleDefaultValue !== undefined && Array.isArray(this.localData)) {
341
+ // 初始化每个卡片的默认值
342
+ this.titleValueByIndex = {}
343
+ }
344
+ }
345
+ this.enableHoverOptions = await res.enableHoverOptions || false// 悬浮选项框
346
+ if (this.enableHoverOptions) {
347
+ this.select_options = await res.select_options
348
+ }
349
+ if (that.listMode) { param.condition = `Limit ${that.nowPage}, ${that.pageSize}` }
350
+ runLogic(res.data, param, 'af-his').then(result => {
351
+ that.data = result
352
+ if (that.nowPage === 0) { this.localData = result }
353
+ // 初始化每张卡片的 title 选中值:优先读取模板 titleRightValue,否则使用配置默认或第一项
354
+ if (Array.isArray(this.localData)) {
355
+ this.localData.forEach((row, idx) => {
356
+ if (this.titleValueByIndex[idx] !== undefined) return
357
+ if (Array.isArray(row)) {
358
+ const title = row.find(d => d && d.type === 'title')
359
+ if (title && title.titleRightValue !== undefined) {
360
+ this.$set(this.titleValueByIndex, idx, title.titleRightValue)
361
+ return
362
+ }
363
+ }
364
+ // fallback:配置默认或第一项
365
+ const def = (this.internalTitleOptions && this.internalTitleOptions.length)
366
+ ? (this.internalTitleOptions[0].value !== undefined ? this.internalTitleOptions[0].value : this.internalTitleOptions[0])
367
+ : undefined
368
+ if (def !== undefined) this.$set(this.titleValueByIndex, idx, def)
369
+ })
370
+ }
371
+ })
372
+ })
373
+ },
374
+ // 提取性别:从数组数据中查找 label 含“性别”的项
375
+ getGender (item) {
376
+ if (!Array.isArray(item)) return null
377
+ const g = item.find(d => d && typeof d.label === 'string' && d.label.indexOf('性别') > -1)
378
+ const val = g && (g.value || g.text)
379
+ if (!val) return null
380
+ if (String(val).includes('男')) return '男'
381
+ if (String(val).includes('女')) return '女'
382
+ return null
383
+ },
384
+ // 读取标题项上的 hasPatient 布尔值
385
+ getHasPatient (item) {
386
+ if (!Array.isArray(item)) return true
387
+ const title = item.find(d => d && d.type === 'title')
388
+ if (!title || typeof title.hasPatient === 'undefined') return true
389
+ return !!title.hasPatient
390
+ },
391
+ // 标题右侧下拉选择
392
+ handleTitleSelectChange (val, index) {
393
+ // 重复值拦截:若值未变化则直接返回
394
+ const prev = this.normalizeValue(this.currentTitleValue(index))
395
+ const next = this.normalizeValue(val)
396
+ if (prev === next) return
397
+
398
+ this.$set(this.titleValueByIndex, index, val)
399
+ this.$emit('update:titleSelectValue', val)
400
+ this.$emit('titleSelectChange', { index, value: val })
401
+ // 变更后仅通知后端更新,不处理返回值;支持从标题项读取 extraParams 合并(如住院号)
402
+ const extra = this.getTitleExtraParams(index)
403
+ if (this.selectDebounceTimer) clearTimeout(this.selectDebounceTimer)
404
+ this.selectDebounceTimer = setTimeout(() => {
405
+ runLogic(this.logicName, Object.assign({ type: 'select', value: val }, extra), 'af-his').catch(() => {})
406
+ }, 200)
407
+ },
408
+ // 读取标题项中额外需要传递的参数(可选),例如 { inpatientNo: row.t_id }
409
+ getTitleExtraParams (index) {
410
+ const row = Array.isArray(this.localData) ? this.localData[index] : null
411
+ if (!Array.isArray(row)) return {}
412
+ const title = row.find(d => d && d.type === 'title') || {}
413
+ if (title && title.extraParams && typeof title.extraParams === 'object') return title.extraParams
414
+ return {}
415
+ },
416
+ handleTitleMenuClick ({ key }, index) {
417
+ const v = isNaN(Number(key)) ? key : Number(key)
418
+ this.handleTitleSelectChange(v, index)
419
+ },
420
+ // 根据当前选择项的 color 设置卡片背景色
421
+ normalizeValue (v) { return String(v) },
422
+ getCardStyle (val) {
423
+ const target = this.normalizeValue(val)
424
+ const opt = (this.getTitleOptions || []).find(o => this.normalizeValue(o && (o.value !== undefined ? o.value : o)) === target)
425
+ const color = opt && (opt.color || (opt.style && opt.style.background))
426
+ return color ? { backgroundColor: color } : {}
427
+ },
428
+ getTitleLabel (val) {
429
+ const target = this.normalizeValue(val)
430
+ const opt = (this.getTitleOptions || []).find(o => this.normalizeValue(o && (o.value !== undefined ? o.value : o)) === target)
431
+ return opt && (opt.label !== undefined ? opt.label : opt)
432
+ },
433
+ getDisplayTitleLabel (index) {
434
+ // 优先展示“当前选中值”的 label,未选中再回退到模板提供的 titleRightLabel
435
+ const current = this.getTitleLabel(this.currentTitleValue(index))
436
+ if (current) return current
437
+ const title = Array.isArray(this.localData && this.localData[index])
438
+ ? this.localData[index].find(d => d && d.type === 'title')
439
+ : null
440
+ return title && title.titleRightLabel ? title.titleRightLabel : ''
441
+ },
442
+ // 点击列表项
443
+ handleClick (index) {
444
+ if (this.enableSelectRow && this.selectOnClick) this.setSelectedIndex(index)
445
+ this.$emit('listClick', this.data[index])
446
+ },
447
+ // 卡片模式点击卡片
448
+ handleCardClick (index) {
449
+ if (this.enableSelectRow && this.selectOnClick) this.setSelectedIndex(index)
450
+ this.$emit('listClick', this.localData[index])
451
+ },
452
+ // 外部可调用:设置选中索引
453
+ setSelectedIndex (index) {
454
+ const next = typeof index === 'number' ? index : -1
455
+ if (typeof this.selectedIndex === 'number') {
456
+ // 受控:仅派发事件
457
+ this.$emit('update:selectedIndex', next)
458
+ } else {
459
+ // 非受控:更新内部并派发事件
460
+ this.localSelectedIndex = next
461
+ this.$emit('update:selectedIndex', next)
462
+ }
463
+ },
464
+ // 外部可调用:清空选中
465
+ clearSelected () { this.setSelectedIndex(-1) },
466
+ refreshList (param) {
467
+ this.getData(this.queryParamsName, param)
468
+ },
469
+ click (index, buttonIndex) {
470
+ this.$emit('click', { data: this.data[index], name: this.buttonNames[buttonIndex] })
471
+ },
472
+ // 根据对象字段匹配选中(默认按 id 字段)
473
+ setSelectedById (id, field = 'id') {
474
+ const list = this.listMode ? this.localData : this.data
475
+ if (!Array.isArray(list)) return
476
+ const index = list.findIndex(item => {
477
+ if (Array.isArray(item)) {
478
+ const f = item.find(d => d && d.label === field)
479
+ return f && f.value === id
480
+ }
481
+ return item && item[field] === id
482
+ })
483
+ if (index >= 0) this.setSelectedIndex(index)
484
+ },
485
+ // 根据 label/value(卡片数组数据场景)匹配选中
486
+ setSelectedByLabelValue (label, value) {
487
+ const list = this.listMode ? this.localData : this.data
488
+ if (!Array.isArray(list)) return
489
+ const index = list.findIndex(item => Array.isArray(item) && item.some(d => d && d.label === label && d.value === value))
490
+ if (index >= 0) this.setSelectedIndex(index)
491
+ },
492
+ // 通用:传入谓词函数决定选中项
493
+ setSelectedBy (predicate) {
494
+ if (typeof predicate !== 'function') return
495
+ const list = this.listMode ? this.localData : this.data
496
+ if (!Array.isArray(list)) return
497
+ const index = list.findIndex(predicate)
498
+ if (index >= 0) this.setSelectedIndex(index)
499
+ },
500
+ getIconStyle (item) {
501
+ return item.picture
502
+ ? { backgroundImage: `url(${item.picture})` }
503
+ : {}
504
+ },
505
+ filterData (par) {
506
+ runLogic(this.queryParamsName, par, 'af-his').then(res => {
507
+ this.data = res.data
508
+ })
509
+ },
510
+ // 鼠标进入列表项
511
+ handleMouseEnter (index) {
512
+ this.clearAllTimers()
513
+ this.hoveredIndex = index
514
+ this.isOptionsHovered = true
515
+ },
516
+ // 鼠标离开列表项
517
+ handleMouseLeave () {
518
+ this.clearAllTimers()
519
+ this.leaveTimer = setTimeout(() => {
520
+ this.isOptionsHovered = false
521
+ this.hoveredIndex = -1
522
+ }, 100)
523
+ },
524
+ // 鼠标进入悬浮选项框
525
+ handleOptionsEnter () {
526
+ this.clearAllTimers()
527
+ this.isOptionsHovered = true
528
+ },
529
+ // 鼠标离开悬浮选项框
530
+ handleOptionsLeave () {
531
+ this.clearAllTimers()
532
+ this.leaveTimer = setTimeout(() => {
533
+ this.isOptionsHovered = false
534
+ this.hoveredIndex = -1
535
+ }, 100)
536
+ },
537
+ // 清除所有定时器
538
+ clearAllTimers () {
539
+ if (this.hoverTimer) {
540
+ clearTimeout(this.hoverTimer)
541
+ this.hoverTimer = null
542
+ }
543
+ if (this.leaveTimer) {
544
+ clearTimeout(this.leaveTimer)
545
+ this.leaveTimer = null
546
+ }
547
+ },
548
+ // 选项框点击
549
+ handleOptionClick (index, action) {
550
+ this.$emit('optionClick', { data: this.data[index], action })
551
+ }
552
+ },
553
+ watch: {
554
+ // 同步受控值变化
555
+ selectedIndex (val) {
556
+ if (typeof val === 'number') {
557
+ // 允许受控值影响内部显示
558
+ this.localSelectedIndex = val
559
+ }
560
+ },
561
+ fixedQueryForm: {
562
+ deep: true,
563
+ handler (val) {
564
+ this.refreshList(val)
565
+ }
566
+ }
567
+ },
568
+ beforeDestroy () {
569
+ const ref = this.$refs.listRef
570
+ if (ref && ref.removeEventListener) {
571
+ ref.removeEventListener('scroll', this.handleInfiniteOnLoad)
572
+ }
573
+ this.clearAllTimers()
574
+ }
575
+ }
576
+ </script>
577
+
578
+ <style scoped>
579
+ .list-wrapper {
580
+ max-height: 240px;
581
+ overflow-y: auto;
582
+ padding-right: 2px;
583
+ }
584
+
585
+ .list-container {
586
+ width: 100%;
587
+ }
588
+
589
+ .list-item {
590
+ height: 35px;
591
+ border-radius: 6px;
592
+ background-color: #F4F4F4;
593
+ padding: 8px 15px;
594
+ font-size: 16px;
595
+ display: flex;
596
+ align-items: center;
597
+ width: 100%;
598
+ border: 1px solid #D9D9D9;
599
+ box-sizing: border-box;
600
+ margin-bottom: 8px !important;
601
+ position: relative;
602
+ transition: background-color 0.3s ease;
603
+ }
604
+
605
+ .icon-menu {
606
+ display: inline-block;
607
+ width: 20px;
608
+ height: 20px;
609
+ background-color: #ccc;
610
+ margin-right: 8px;
611
+ }
612
+
613
+ .item-text {
614
+ flex: 1;
615
+ }
616
+
617
+ .confirm-btn {
618
+ margin-left: auto;
619
+ padding: 0 8px;
620
+ }
621
+
622
+ .confirm-btn.hover-btn {
623
+ opacity: 0;
624
+ transition: opacity 0.3s ease;
625
+ }
626
+
627
+ .button-group {
628
+ display: flex;
629
+ gap: 2px; /* 按钮之间的间距 */
630
+ }
631
+
632
+ .list-item:hover .confirm-btn.hover-btn {
633
+ opacity: 1;
634
+ }
635
+
636
+ /* 自定义滚动条样式 */
637
+ .list-wrapper::-webkit-scrollbar {
638
+ width: 6px;
639
+ }
640
+
641
+ .list-wrapper::-webkit-scrollbar-thumb {
642
+ background-color: #d9d9d9;
643
+ border-radius: 3px;
644
+ }
645
+
646
+ .list-wrapper::-webkit-scrollbar-track {
647
+ background-color: #f0f0f0;
648
+ }
649
+
650
+ .hover-active {
651
+ color: white;
652
+ }
653
+
654
+ .list-item.hover-active {
655
+ background-color: rgb(0, 87, 254) !important;
656
+ color: white;
657
+ border: 1px solid black;
658
+ }
659
+
660
+ /* 选中态样式(通过 selectRow 开启) */
661
+ .selected-active { color: white; }
662
+ .list-item.selected-active {
663
+ background-color: #0057FE !important;
664
+ color: white;
665
+ border: none !important;
666
+ }
667
+ .list-item.selected-active .confirm-btn,
668
+ .list-item.selected-active .confirm-btn span,
669
+ .list-item.selected-active .ant-btn,
670
+ .list-item.selected-active .ant-btn > span { color: #ffffff !important; }
671
+ .card-a-col.selected-active {
672
+ background-color: #0057FE !important;
673
+ color: white;
674
+ border: none !important;
675
+ }
676
+ .card-a-col.selected-active .button-a-col,
677
+ .card-a-col.selected-active .button-a-col .ant-btn,
678
+ .card-a-col.selected-active .button-a-col .ant-btn > span { color: #ffffff !important; }
679
+
680
+ .hover-options {
681
+ position: absolute;
682
+ left: 0;
683
+ right: 0;
684
+ top: 100%;
685
+ background: white;
686
+ border: 1px solid #d9d9d9;
687
+ border-radius: 4px;
688
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
689
+ z-index: 1000;
690
+ margin-top: 4px;
691
+ width: 100%;
692
+ box-sizing: border-box;
693
+ pointer-events: auto;
694
+ }
695
+
696
+ .hover-options-content {
697
+ padding: 4px 0;
698
+ display: flex;
699
+ flex-direction: column;
700
+ width: 100%;
701
+ }
702
+
703
+ .option-item {
704
+ padding: 8px 12px;
705
+ cursor: pointer;
706
+ transition: all 0.3s ease;
707
+ color: #333;
708
+ font-size: 14px;
709
+ display: flex;
710
+ align-items: center;
711
+ }
712
+
713
+ .option-item:hover {
714
+ background-color: #f5f5f5;
715
+ color: #1890ff;
716
+ }
717
+
718
+ .option-item:active {
719
+ background-color: #e6f7ff;
720
+ }
721
+
722
+ .demo-loading-container {
723
+ position: absolute;
724
+ bottom: 40px;
725
+ width: 100%;
726
+ text-align: center;
727
+ }
728
+
729
+ .demo-infinite-container{
730
+ overflow-x: hidden;
731
+ overflow-y: auto;
732
+ height: 85vh;
733
+ }
734
+ .card-row{
735
+ height: 100%;
736
+ width: 100%;
737
+ border-radius: 6px;
738
+ padding: 6px;
739
+ }
740
+ .id-a-col{
741
+ font-size: 22px;
742
+ font-weight: 700;
743
+ display: flex;
744
+ flex-direction: column;
745
+ align-items: center;
746
+ justify-content: center;
747
+ gap: 4px;
748
+ }
749
+ .id-a-col-2{
750
+ padding: 8px;
751
+ height: 100%;
752
+ overflow: hidden;
753
+ display: flex;
754
+ flex-direction: column;
755
+ justify-content: space-between;
756
+ }
757
+ .describe-list-a-col{
758
+ display: block;
759
+ font-family: Source Han Sans;
760
+ font-size: 16px;
761
+ font-weight: normal;
762
+ line-height: normal;
763
+ overflow: hidden;
764
+ text-overflow: ellipsis;
765
+ white-space: nowrap;
766
+ max-width: 100%;
767
+ }
768
+ .name{
769
+ font-family: Source Han Sans;
770
+ font-size: 22px;
771
+ font-weight: bold;
772
+ line-height: normal;
773
+ }
774
+ .component-a-col{
775
+ display: flex;
776
+ align-items: center;
777
+ overflow: hidden;
778
+ }
779
+ .label-text{
780
+ overflow: hidden;
781
+ text-overflow: ellipsis;
782
+ white-space: nowrap;
783
+ max-width: 120px;
784
+ display: inline-block;
785
+ }
786
+ .gender-icon{
787
+ margin-top: 2px;
788
+ }
789
+ .gender-icon .male{ color: #2f54eb; font-size: 14px; }
790
+ .gender-icon .female{ color: #eb2f96; font-size: 14px; }
791
+ .gender-img{ width: 100%; height: 100%; opacity: 1; display: inline-block; }
792
+ .title-row{
793
+ display: flex;
794
+ align-items: center;
795
+ justify-content: space-between;
796
+ gap: 8px;
797
+ }
798
+ /* 标题文本占满剩余空间,确保箭头贴右 */
799
+ .title-row .describe-list-a-col{ flex: 1; }
800
+ .title-actions{ display: inline-flex; align-items: center; gap: 6px; }
801
+ .title-select-label{
802
+ font-family: Source Han Sans;
803
+ font-size: 16px;
804
+ font-weight: normal;
805
+ line-height: normal;
806
+ color: #595959;
807
+ }
808
+ .title-select{ min-width: 96px; }
809
+ .arrow-btn{ display: inline-flex; align-items: center; color: #8C8C8C; }
810
+ .arrow-btn .anticon{ font-size: 18px; }
811
+ /* 放大下拉箭头的 svg 尺寸以符合 UI(scoped 深度选择) */
812
+ ::v-deep .arrow-btn .anticon svg{ width: 2em; height: 2em; }
813
+ .button-a-col{
814
+ position: absolute;
815
+ top: 245px;
816
+ left: 225px;
817
+ }
818
+ .card-a-col{
819
+ background-color: rgba(247, 249, 252);
820
+ height: 297px;
821
+ width: auto;
822
+ border-radius: 6px;
823
+ border: 1px solid #E5E9F0;
824
+ overflow: hidden;
825
+ position: relative;
826
+ box-sizing: border-box;
827
+ margin: 10px;
828
+ }
829
+ </style>