cnhis-design-vue 2.1.57 → 2.1.59

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 (574) hide show
  1. package/.prettierrc.js +27 -27
  2. package/CHANGELOG.md +2610 -2634
  3. package/README.md +90 -90
  4. package/commitlint.config.js +1 -1
  5. package/es/affix/index.js +8 -8
  6. package/es/age/index.js +18 -18
  7. package/es/alert/index.js +8 -8
  8. package/es/anchor/index.js +8 -8
  9. package/es/auto-complete/index.js +8 -8
  10. package/es/avatar/index.js +8 -8
  11. package/es/back-top/index.js +8 -8
  12. package/es/badge/index.js +8 -8
  13. package/es/base/index.js +8 -8
  14. package/es/big-table/index.js +345 -333
  15. package/es/big-table/style.css +1 -1
  16. package/es/breadcrumb/index.js +8 -8
  17. package/es/button/index.js +31 -31
  18. package/es/calendar/index.js +8 -8
  19. package/es/captcha/index.js +7 -7
  20. package/es/card/index.js +8 -8
  21. package/es/carousel/index.js +8 -8
  22. package/es/cascader/index.js +8 -8
  23. package/es/checkbox/index.js +17 -17
  24. package/es/col/index.js +8 -8
  25. package/es/collapse/index.js +8 -8
  26. package/es/color-picker/index.js +5 -5
  27. package/es/comment/index.js +8 -8
  28. package/es/config-provider/index.js +8 -8
  29. package/es/date-picker/index.js +30 -30
  30. package/es/descriptions/index.js +8 -8
  31. package/es/divider/index.js +8 -8
  32. package/es/drag-layout/index.js +5 -5
  33. package/es/drawer/index.js +8 -8
  34. package/es/dropdown/index.js +8 -8
  35. package/es/editor/index.js +1 -1
  36. package/es/ellipsis/index.js +1 -1
  37. package/es/empty/index.js +8 -8
  38. package/es/fabric-chart/index.js +724 -436
  39. package/es/fabric-chart/style.css +1 -1
  40. package/es/form/index.js +8 -8
  41. package/es/form-model/index.js +8 -8
  42. package/es/form-table/index.js +221 -189
  43. package/es/form-table/style.css +1 -1
  44. package/es/grid/index.js +4 -4
  45. package/es/index/index.js +2042 -1695
  46. package/es/index/style.css +1 -1
  47. package/es/input/index.js +11 -11
  48. package/es/input-number/index.js +8 -8
  49. package/es/keep-cache/index.js +9 -9
  50. package/es/layout/index.js +8 -8
  51. package/es/list/index.js +8 -8
  52. package/es/locale-provider/index.js +8 -8
  53. package/es/map/index.js +13 -13
  54. package/es/mentions/index.js +8 -8
  55. package/es/menu/index.js +8 -8
  56. package/es/message/index.js +8 -8
  57. package/es/multi-chat/index.js +116 -116
  58. package/es/multi-chat-client/index.js +110 -110
  59. package/es/multi-chat-history/index.js +6 -6
  60. package/es/multi-chat-record/index.js +27 -27
  61. package/es/multi-chat-setting/index.js +43 -43
  62. package/es/multi-chat-sip/index.js +1 -1
  63. package/es/notification/index.js +8 -8
  64. package/es/page-header/index.js +8 -8
  65. package/es/pagination/index.js +8 -8
  66. package/es/popconfirm/index.js +8 -8
  67. package/es/popover/index.js +8 -8
  68. package/es/progress/index.js +8 -8
  69. package/es/radio/index.js +17 -17
  70. package/es/rate/index.js +8 -8
  71. package/es/result/index.js +8 -8
  72. package/es/row/index.js +8 -8
  73. package/es/scale-container/index.js +28 -23
  74. package/es/scale-container/style.css +1 -1
  75. package/es/scale-view/index.js +158 -158
  76. package/es/select/index.js +44 -44
  77. package/es/select-label/index.js +50 -50
  78. package/es/select-person/index.js +20 -20
  79. package/es/shortcut-setter/index.js +10 -10
  80. package/es/skeleton/index.js +8 -8
  81. package/es/slider/index.js +8 -8
  82. package/es/space/index.js +8 -8
  83. package/es/spin/index.js +8 -8
  84. package/es/statistic/index.js +8 -8
  85. package/es/steps/index.js +8 -8
  86. package/es/switch/index.js +8 -8
  87. package/es/table-filter/index.js +253 -238
  88. package/es/table-filter/style.css +1 -1
  89. package/es/tabs/index.js +8 -8
  90. package/es/tag/index.js +9 -9
  91. package/es/time-picker/index.js +8 -8
  92. package/es/timeline/index.js +8 -8
  93. package/es/tooltip/index.js +8 -8
  94. package/es/transfer/index.js +8 -8
  95. package/es/tree/index.js +8 -8
  96. package/es/tree-select/index.js +8 -8
  97. package/es/upload/index.js +8 -8
  98. package/es/utils/clickoutside.js +7 -7
  99. package/es/utils/kty.min-1.0.0.js +5 -5
  100. package/es/utils/time-domain.js +16 -16
  101. package/es/utils/utils-map.js +32 -32
  102. package/es/utils/vexutils.js +27 -27
  103. package/es/verification-code/index.js +2 -2
  104. package/lib/cui.common.js +2172 -1825
  105. package/lib/cui.umd.js +2172 -1825
  106. package/lib/cui.umd.min.js +28 -28
  107. package/package.json +110 -110
  108. package/packages/affix/index.js +12 -12
  109. package/packages/affix/src/Affix.jsx +12 -12
  110. package/packages/age/index.js +27 -27
  111. package/packages/age/src/age/index.vue +139 -139
  112. package/packages/age/src/age-com/index.vue +209 -209
  113. package/packages/alert/index.js +12 -12
  114. package/packages/alert/src/Alert.jsx +11 -11
  115. package/packages/anchor/index.js +18 -18
  116. package/packages/anchor/src/Anchor.jsx +11 -11
  117. package/packages/anchor/src/Link.jsx +11 -11
  118. package/packages/auto-complete/index.js +12 -12
  119. package/packages/auto-complete/src/AutoComplete.jsx +12 -12
  120. package/packages/avatar/index.js +12 -12
  121. package/packages/avatar/src/Avatar.jsx +11 -11
  122. package/packages/back-top/index.js +12 -12
  123. package/packages/back-top/src/BackTop.jsx +11 -11
  124. package/packages/badge/index.js +12 -12
  125. package/packages/badge/src/Badge.jsx +11 -11
  126. package/packages/base/index.js +3 -3
  127. package/packages/big-table/index.js +16 -16
  128. package/packages/big-table/src/BigTable.vue +3150 -3144
  129. package/packages/big-table/src/Fieldset.vue +2067 -2067
  130. package/packages/big-table/src/assets/iconfont/iconfont.css +21 -21
  131. package/packages/big-table/src/assets/style/table-base.less +385 -385
  132. package/packages/big-table/src/assets/style/table-global.less +175 -175
  133. package/packages/big-table/src/components/AutoLayoutButton.vue +279 -279
  134. package/packages/big-table/src/components/NoData.vue +81 -81
  135. package/packages/big-table/src/components/TextOverTooltip.vue +120 -120
  136. package/packages/big-table/src/components/edit-form/EditForm.vue +509 -509
  137. package/packages/big-table/src/components/edit-form/edit-component/config-data/index.js +68 -68
  138. package/packages/big-table/src/components/edit-form/edit-component/edit-date-picker/edit-date-picker.vue +60 -60
  139. package/packages/big-table/src/components/edit-form/edit-component/edit-digital/edit-digital.vue +54 -54
  140. package/packages/big-table/src/components/edit-form/edit-component/edit-input/edit-input.vue +39 -39
  141. package/packages/big-table/src/components/edit-form/edit-component/edit-input-password/edit-input-password.vue +79 -79
  142. package/packages/big-table/src/components/edit-form/edit-component/edit-month-picker/edit-month-picker.vue +37 -37
  143. package/packages/big-table/src/components/edit-form/edit-component/edit-search/edit-search.vue +82 -82
  144. package/packages/big-table/src/components/edit-form/edit-component/edit-search-more/edit-search-more.vue +95 -95
  145. package/packages/big-table/src/components/edit-form/edit-component/edit-select/edit-select.vue +40 -40
  146. package/packages/big-table/src/components/edit-form/edit-component/edit-select-multiple/edit-select-multiple.vue +55 -55
  147. package/packages/big-table/src/components/edit-form/edit-component/edit-switch/edit-switch.vue +43 -43
  148. package/packages/big-table/src/components/edit-form/edit-component/edit-textarea/edit-textarea.vue +41 -41
  149. package/packages/big-table/src/components/edit-form/edit-component/edit-time-picker/edit-time-picker.vue +40 -40
  150. package/packages/big-table/src/components/edit-form/edit-component/mixins/bound-date.js +3 -3
  151. package/packages/big-table/src/components/edit-form/edit-component/mixins/dateType.js +217 -217
  152. package/packages/big-table/src/components/edit-form/edit-component/mixins/item-default.js +639 -639
  153. package/packages/big-table/src/components/edit-form/edit-component/mixins/search.js +1247 -1247
  154. package/packages/big-table/src/components/edit-form/edit-component/register-com.js +26 -26
  155. package/packages/big-table/src/components/edit-form/edit-item/form-event.js +80 -80
  156. package/packages/big-table/src/components/edit-form/edit-item/global-props.js +33 -33
  157. package/packages/big-table/src/components/edit-form/edit-item/index.js +4 -4
  158. package/packages/big-table/src/components/edit-form/edit-item/render-methods.js +28 -28
  159. package/packages/big-table/src/components/edit-form/edit-item/validate-rules.js +463 -463
  160. package/packages/big-table/src/components/edit-form/edit-mixins/form-commom.js +673 -673
  161. package/packages/big-table/src/components/edit-form/edit-mixins/index.js +3 -3
  162. package/packages/big-table/src/components/edit-form/edit-utils/index.js +112 -112
  163. package/packages/big-table/src/components/password-com.vue +58 -58
  164. package/packages/big-table/src/components/player-vod/index.vue +57 -57
  165. package/packages/big-table/src/components/player-vod/player.vue +189 -189
  166. package/packages/big-table/src/components/player-vod/video-list.vue +265 -265
  167. package/packages/big-table/src/components/player-vod/video-modal.vue +126 -126
  168. package/packages/big-table/src/utils/CustomPagination.vue +86 -86
  169. package/packages/big-table/src/utils/batchEditing.js +610 -610
  170. package/packages/big-table/src/utils/bigTableProps.js +103 -103
  171. package/packages/big-table/src/utils/format.js +557 -557
  172. package/packages/big-table/src/utils/nestTable.js +109 -109
  173. package/packages/big-table/src/utils/tableParse.js +234 -234
  174. package/packages/breadcrumb/index.js +21 -21
  175. package/packages/breadcrumb/src/Breadcrumb.jsx +11 -11
  176. package/packages/breadcrumb/src/BreadcrumbItem.jsx +11 -11
  177. package/packages/breadcrumb/src/BreadcrumbSeparator.jsx +11 -11
  178. package/packages/button/index.js +21 -21
  179. package/packages/button/src/Button.jsx +11 -11
  180. package/packages/button/src/ButtonGroup.jsx +11 -11
  181. package/packages/button/src/ButtonPrint/components/IdentityVerification.vue +181 -181
  182. package/packages/button/src/ButtonPrint/index.vue +766 -766
  183. package/packages/button/src/ButtonPrint/mixin/his-print.js +95 -95
  184. package/packages/calendar/index.js +12 -12
  185. package/packages/calendar/src/Calendar.jsx +11 -11
  186. package/packages/captcha/index.js +9 -9
  187. package/packages/captcha/src/Captcha.vue +164 -164
  188. package/packages/captcha/src/Index.vue +47 -47
  189. package/packages/captcha/src/SlideVerify.vue +285 -285
  190. package/packages/card/index.js +21 -21
  191. package/packages/card/src/Card.jsx +12 -12
  192. package/packages/card/src/CardGrid.js +7 -7
  193. package/packages/card/src/CardMeta.js +7 -7
  194. package/packages/carousel/index.js +12 -12
  195. package/packages/carousel/src/Carousel.jsx +12 -12
  196. package/packages/cascader/index.js +12 -12
  197. package/packages/cascader/src/Cascader.jsx +11 -11
  198. package/packages/checkbox/index.js +30 -30
  199. package/packages/checkbox/src/Checkbox.jsx +11 -11
  200. package/packages/checkbox/src/CheckboxImg/index.vue +141 -141
  201. package/packages/checkbox/src/Group.jsx +11 -11
  202. package/packages/col/index.js +13 -13
  203. package/packages/col/src/Col.jsx +11 -11
  204. package/packages/collapse/index.js +18 -18
  205. package/packages/collapse/src/Collapse.jsx +11 -11
  206. package/packages/collapse/src/Panel.jsx +11 -11
  207. package/packages/color-picker/index.js +10 -10
  208. package/packages/color-picker/src/color-picker.vue +191 -191
  209. package/packages/color-picker/src/style.less +109 -109
  210. package/packages/comment/index.js +12 -12
  211. package/packages/comment/src/Comment.jsx +11 -11
  212. package/packages/config-provider/index.js +12 -12
  213. package/packages/config-provider/src/ConfigProvider.jsx +11 -11
  214. package/packages/date-picker/index.js +26 -26
  215. package/packages/date-picker/src/DatePicker.jsx +12 -12
  216. package/packages/date-picker/src/MonthPicker.jsx +11 -11
  217. package/packages/date-picker/src/RangePicker.jsx +11 -11
  218. package/packages/date-picker/src/WeekPicker.jsx +11 -11
  219. package/packages/date-picker/src/utils/index.js +374 -374
  220. package/packages/descriptions/index.js +18 -18
  221. package/packages/descriptions/src/Descriptions.jsx +11 -11
  222. package/packages/descriptions/src/Item.jsx +11 -11
  223. package/packages/divider/index.js +12 -12
  224. package/packages/divider/src/Divider.jsx +11 -11
  225. package/packages/drag-layout/DragFormLeftItem.vue +173 -173
  226. package/packages/drag-layout/DragFormRightItem.vue +284 -284
  227. package/packages/drag-layout/I18n-mixins.js +10 -10
  228. package/packages/drag-layout/drag-layout.vue +778 -778
  229. package/packages/drag-layout/index.js +12 -12
  230. package/packages/drawer/index.js +12 -12
  231. package/packages/drawer/src/Drawer.jsx +11 -11
  232. package/packages/dropdown/index.js +12 -12
  233. package/packages/dropdown/src/Dropdown.jsx +11 -11
  234. package/packages/editor/index.js +9 -9
  235. package/packages/ellipsis/index.js +8 -8
  236. package/packages/ellipsis/src/Ellipsis.vue +65 -65
  237. package/packages/empty/index.js +12 -12
  238. package/packages/empty/src/Empty.jsx +11 -11
  239. package/packages/fabric-chart/index.js +9 -9
  240. package/packages/fabric-chart/src/FabricGrid.vue +67 -66
  241. package/packages/fabric-chart/src/components/DropPopup.vue +90 -90
  242. package/packages/fabric-chart/src/components/MouseRightClick.vue +168 -168
  243. package/packages/fabric-chart/src/components/TimeScaleValue.vue +114 -115
  244. package/packages/fabric-chart/src/const/defaultVaule.js +60 -59
  245. package/packages/fabric-chart/src/fabric-chart/FabricCanvas.vue +43 -9
  246. package/packages/fabric-chart/src/fabric-chart/FabricLines.vue +579 -552
  247. package/packages/fabric-chart/src/fabric-chart/FabricPolylines.vue +1142 -1126
  248. package/packages/fabric-chart/src/fabric-chart/FabricScaleValue.vue +134 -134
  249. package/packages/fabric-chart/src/fabric-chart/FabricTextGroup.vue +574 -566
  250. package/packages/fabric-chart/src/mixins/eventCommon.js +34 -1
  251. package/packages/fabric-chart/src/mixins/fabricCommon.js +95 -95
  252. package/packages/fabric-chart/src/mixins/fabricObject.js +193 -193
  253. package/packages/fabric-chart/src/mixins/type.js +5 -5
  254. package/packages/form/index.js +16 -16
  255. package/packages/form/src/Form.jsx +11 -11
  256. package/packages/form/src/Item.jsx +11 -11
  257. package/packages/form-model/index.js +14 -14
  258. package/packages/form-model/src/FormModel.jsx +11 -11
  259. package/packages/form-model/src/Item.jsx +11 -11
  260. package/packages/form-table/index.js +16 -16
  261. package/packages/form-table/src/FormTable.vue +1110 -1110
  262. package/packages/form-table/src/components/table-component/config-data/index.js +80 -80
  263. package/packages/form-table/src/components/table-component/global-props.js +22 -22
  264. package/packages/form-table/src/components/table-component/index.js +8 -8
  265. package/packages/form-table/src/components/table-component/mixins/bound-date.js +455 -455
  266. package/packages/form-table/src/components/table-component/mixins/dateType.js +217 -217
  267. package/packages/form-table/src/components/table-component/mixins/item-default.js +257 -257
  268. package/packages/form-table/src/components/table-component/mixins/search.js +1242 -1238
  269. package/packages/form-table/src/components/table-component/register-com.js +30 -30
  270. package/packages/form-table/src/components/table-component/table-age/table-age.vue +175 -175
  271. package/packages/form-table/src/components/table-component/table-date-picker/table-date-picker.vue +87 -87
  272. package/packages/form-table/src/components/table-component/table-digital/table-digital.vue +93 -93
  273. package/packages/form-table/src/components/table-component/table-input/table-input.vue +81 -81
  274. package/packages/form-table/src/components/table-component/table-input-password/table-input-password.vue +126 -126
  275. package/packages/form-table/src/components/table-component/table-month-picker/table-month-picker.vue +55 -55
  276. package/packages/form-table/src/components/table-component/table-search/table-search.vue +174 -174
  277. package/packages/form-table/src/components/table-component/table-search-more/table-search-more.vue +191 -191
  278. package/packages/form-table/src/components/table-component/table-select/table-select.vue +64 -64
  279. package/packages/form-table/src/components/table-component/table-select-multiple/table-select-multiple.vue +81 -81
  280. package/packages/form-table/src/components/table-component/table-textarea/table-textarea.vue +76 -76
  281. package/packages/form-table/src/components/table-component/table-time-picker/table-time-picker.vue +55 -55
  282. package/packages/form-table/src/components/table-component/table-tree-select/table-tree-select.vue +135 -135
  283. package/packages/form-table/src/components/table-component/text-over-tooltip/TextOverTooltip.vue +97 -97
  284. package/packages/form-table/src/components/table-item/form-event.js +81 -81
  285. package/packages/form-table/src/components/table-item/global-props.js +27 -27
  286. package/packages/form-table/src/components/table-item/index.js +4 -4
  287. package/packages/form-table/src/components/table-item/render-methods.js +28 -28
  288. package/packages/form-table/src/components/table-item/validate-rules.js +520 -520
  289. package/packages/form-table/src/components/table-mixins/form-commom.js +98 -98
  290. package/packages/form-table/src/components/table-mixins/index.js +3 -3
  291. package/packages/form-table/src/components/table-utils/index.js +112 -112
  292. package/packages/form-table/src/disabledDetail.less +46 -46
  293. package/packages/grid/index.js +10 -10
  294. package/packages/grid/src/grid.js +28 -28
  295. package/packages/icon/index.js +10 -10
  296. package/packages/icon/src/icon.js +13 -13
  297. package/packages/index.js +313 -313
  298. package/packages/input/index.js +30 -30
  299. package/packages/input/src/Group.jsx +11 -11
  300. package/packages/input/src/Input.jsx +11 -11
  301. package/packages/input/src/Password.jsx +11 -11
  302. package/packages/input/src/Search.jsx +11 -11
  303. package/packages/input/src/TextArea.jsx +11 -11
  304. package/packages/input/src/input-quick/components/quick-item.vue +284 -284
  305. package/packages/input/src/input-quick/components/quick-popover.vue +596 -596
  306. package/packages/input/src/input-quick/index.vue +137 -137
  307. package/packages/input-number/index.js +12 -12
  308. package/packages/input-number/src/InputNumber.jsx +11 -11
  309. package/packages/keep-cache/KeepCache.js +236 -236
  310. package/packages/keep-cache/index.css +2 -2
  311. package/packages/keep-cache/index.js +8 -8
  312. package/packages/layout/index.js +27 -27
  313. package/packages/layout/src/Content.jsx +11 -11
  314. package/packages/layout/src/Footer.jsx +11 -11
  315. package/packages/layout/src/Header.jsx +11 -11
  316. package/packages/layout/src/Layout.jsx +11 -11
  317. package/packages/layout/src/Sider.jsx +11 -11
  318. package/packages/list/index.js +21 -21
  319. package/packages/list/src/Item.jsx +11 -11
  320. package/packages/list/src/ItemMeta.jsx +11 -11
  321. package/packages/list/src/List.jsx +11 -11
  322. package/packages/locale-provider/index.js +12 -12
  323. package/packages/locale-provider/src/LocaleProvider.jsx +11 -11
  324. package/packages/map/index.js +9 -9
  325. package/packages/map/src/Map.vue +484 -484
  326. package/packages/map/src/popup-map.vue +53 -53
  327. package/packages/mentions/index.js +18 -18
  328. package/packages/mentions/src/Mentions.jsx +11 -11
  329. package/packages/mentions/src/Option.jsx +11 -11
  330. package/packages/menu/index.js +27 -27
  331. package/packages/menu/src/Divider.jsx +11 -11
  332. package/packages/menu/src/Item.jsx +11 -11
  333. package/packages/menu/src/ItemGroup.jsx +11 -11
  334. package/packages/menu/src/Menu.jsx +11 -11
  335. package/packages/menu/src/SubMenu.jsx +11 -11
  336. package/packages/message/index.js +8 -8
  337. package/packages/modal/index.js +10 -10
  338. package/packages/modal/src/Modal.js +7 -7
  339. package/packages/multi-chat/chat/addConference.vue +200 -200
  340. package/packages/multi-chat/chat/addMembers.vue +411 -411
  341. package/packages/multi-chat/chat/advancedFilter.vue +372 -372
  342. package/packages/multi-chat/chat/calling.vue +246 -246
  343. package/packages/multi-chat/chat/chatFooter.vue +1596 -1596
  344. package/packages/multi-chat/chat/chatHistory.vue +605 -605
  345. package/packages/multi-chat/chat/chatMain.vue +1486 -1486
  346. package/packages/multi-chat/chat/client/index.vue +149 -149
  347. package/packages/multi-chat/chat/delay.vue +177 -177
  348. package/packages/multi-chat/chat/evaluate.vue +343 -343
  349. package/packages/multi-chat/chat/messageRecord.vue +324 -324
  350. package/packages/multi-chat/chat/mixins/NoData.js +20 -20
  351. package/packages/multi-chat/chat/mixins/base.js +97 -97
  352. package/packages/multi-chat/chat/mixins/uniRTCAPI.js +80 -80
  353. package/packages/multi-chat/chat/mixins/viewerOptions.js +67 -67
  354. package/packages/multi-chat/chat/quickReply.vue +439 -439
  355. package/packages/multi-chat/chat/robot/index.vue +312 -312
  356. package/packages/multi-chat/chat/scrollList.vue +1238 -1238
  357. package/packages/multi-chat/chat/videoVoiceList.vue +348 -348
  358. package/packages/multi-chat/chat/voice.vue +431 -431
  359. package/packages/multi-chat/components/avatar.vue +113 -113
  360. package/packages/multi-chat/components/chat-tabs-header.vue +251 -251
  361. package/packages/multi-chat/components/classify-tabs.vue +185 -185
  362. package/packages/multi-chat/components/empty.vue +24 -24
  363. package/packages/multi-chat/components/modal-refuse-reason.vue +112 -112
  364. package/packages/multi-chat/components/modal-sip.vue +160 -160
  365. package/packages/multi-chat/components/modal-user-transfer.vue +98 -98
  366. package/packages/multi-chat/components/msg-describe.vue +138 -138
  367. package/packages/multi-chat/components/msg-picture.vue +68 -68
  368. package/packages/multi-chat/components/msg-prescription.vue +205 -205
  369. package/packages/multi-chat/components/read-record.vue +133 -133
  370. package/packages/multi-chat/components/read-status.vue +34 -34
  371. package/packages/multi-chat/components/user-status.vue +198 -198
  372. package/packages/multi-chat/index.js +7 -7
  373. package/packages/multi-chat/setting/authority/index.vue +156 -156
  374. package/packages/multi-chat/setting/authority/roleSetting.vue +204 -204
  375. package/packages/multi-chat/setting/baseInfo/index.vue +1316 -1316
  376. package/packages/multi-chat/setting/customerService/batchSelect.vue +403 -403
  377. package/packages/multi-chat/setting/customerService/index.vue +273 -273
  378. package/packages/multi-chat/setting/event/edit/condition.vue +128 -128
  379. package/packages/multi-chat/setting/event/edit/index.vue +437 -437
  380. package/packages/multi-chat/setting/event/edit/notice.vue +129 -129
  381. package/packages/multi-chat/setting/event/edit/strategy.vue +98 -98
  382. package/packages/multi-chat/setting/event/index.vue +249 -249
  383. package/packages/multi-chat/setting/index.vue +269 -269
  384. package/packages/multi-chat/setting/page.vue +14 -14
  385. package/packages/multi-chat/setting/sessionList/index.vue +412 -412
  386. package/packages/multi-chat/setting/sessionList/messageRecord.vue +372 -372
  387. package/packages/multi-chat/setting/userConfig/index.vue +124 -124
  388. package/packages/multi-chat/setting/worktime/index.vue +274 -274
  389. package/packages/multi-chat/store/actions.js +452 -452
  390. package/packages/multi-chat/store/getters.js +371 -371
  391. package/packages/multi-chat/store/helper.js +66 -66
  392. package/packages/multi-chat/store/index.js +50 -50
  393. package/packages/multi-chat/store/mutation.js +296 -296
  394. package/packages/multi-chat/store/state.js +117 -117
  395. package/packages/multi-chat/style/emoji.css +315 -315
  396. package/packages/multi-chat/style/message.mixin.less +38 -38
  397. package/packages/multi-chat/utils/chatSock.js +93 -93
  398. package/packages/multi-chat/utils/compressImage.js +115 -115
  399. package/packages/multi-chat/utils/emoji.json +68 -68
  400. package/packages/multi-chat/utils/index.js +259 -259
  401. package/packages/multi-chat/utils/observer-scroll.js +49 -49
  402. package/packages/multi-chat/utils/panelsetting.js +48 -48
  403. package/packages/multi-chat-client/index.js +7 -7
  404. package/packages/multi-chat-history/index.js +7 -7
  405. package/packages/multi-chat-record/index.js +7 -7
  406. package/packages/multi-chat-setting/index.js +7 -7
  407. package/packages/multi-chat-sip/index.js +6 -6
  408. package/packages/notification/index.js +8 -8
  409. package/packages/page-header/index.js +12 -12
  410. package/packages/page-header/src/PageHeader.jsx +11 -11
  411. package/packages/pagination/index.js +12 -12
  412. package/packages/pagination/src/Pagination.jsx +11 -11
  413. package/packages/popconfirm/index.js +12 -12
  414. package/packages/popconfirm/src/Popconfirm.jsx +11 -11
  415. package/packages/popover/index.js +12 -12
  416. package/packages/popover/src/Popover.jsx +11 -11
  417. package/packages/progress/index.js +12 -12
  418. package/packages/progress/src/Progress.jsx +11 -11
  419. package/packages/radio/index.js +33 -33
  420. package/packages/radio/src/Group.jsx +11 -11
  421. package/packages/radio/src/Radio.jsx +11 -11
  422. package/packages/radio/src/RadioButton.jsx +11 -11
  423. package/packages/radio/src/RadioImg/index.vue +124 -124
  424. package/packages/rate/index.js +12 -12
  425. package/packages/rate/src/Rate.jsx +11 -11
  426. package/packages/result/index.js +12 -12
  427. package/packages/result/src/Result.jsx +11 -11
  428. package/packages/row/index.js +12 -12
  429. package/packages/row/src/Row.jsx +11 -11
  430. package/packages/scale-container/index.js +8 -8
  431. package/packages/scale-container/src/ScaleContainer.vue +197 -194
  432. package/packages/scale-view/NoData.vue +81 -81
  433. package/packages/scale-view/answerParse.vue +133 -133
  434. package/packages/scale-view/customList.vue +801 -801
  435. package/packages/scale-view/data.js +80 -80
  436. package/packages/scale-view/evaluateCountdown.vue +155 -155
  437. package/packages/scale-view/evaluatePage.vue +202 -202
  438. package/packages/scale-view/formitem/data.js +3991 -3991
  439. package/packages/scale-view/formitem/index.js +6 -6
  440. package/packages/scale-view/formitem/r-address.vue +238 -238
  441. package/packages/scale-view/formitem/r-choice.vue +726 -726
  442. package/packages/scale-view/formitem/r-input.vue +92 -92
  443. package/packages/scale-view/formitem/r-prompt.vue +52 -52
  444. package/packages/scale-view/formitem/r-time.vue +285 -285
  445. package/packages/scale-view/formitem/r-upload-custom-list.vue +242 -242
  446. package/packages/scale-view/formitem/r-upload.vue +287 -287
  447. package/packages/scale-view/formitem/text-over-tooltip/TextOverTooltip.vue +98 -98
  448. package/packages/scale-view/index.js +17 -17
  449. package/packages/scale-view/mixin/NoData.js +38 -38
  450. package/packages/scale-view/mixin/evaluate.js +146 -146
  451. package/packages/scale-view/mixin/index.js +337 -337
  452. package/packages/scale-view/mixin/judgeTypes.js +267 -267
  453. package/packages/scale-view/scaleView.vue +2010 -2010
  454. package/packages/select/index.js +27 -27
  455. package/packages/select/src/CustomSelect/index.vue +130 -130
  456. package/packages/select/src/OptGroup.jsx +11 -11
  457. package/packages/select/src/Option.jsx +11 -11
  458. package/packages/select/src/Select/Select.vue +231 -231
  459. package/packages/select/src/Select/index.js +12 -12
  460. package/packages/select/src/TableSelect/index.vue +514 -514
  461. package/packages/select-label/index.js +14 -14
  462. package/packages/select-label/label-classify.vue +129 -129
  463. package/packages/select-label/labelFormContent.vue +787 -787
  464. package/packages/select-label/select-label.vue +581 -581
  465. package/packages/select-person/index.js +10 -10
  466. package/packages/select-person/search-tree.vue +373 -373
  467. package/packages/select-person/select-person.vue +1696 -1696
  468. package/packages/shortcut-setter/index.js +12 -12
  469. package/packages/shortcut-setter/src/ShortcutSetter.vue +55 -55
  470. package/packages/shortcut-setter/src/ShortcutSetterItem.vue +84 -84
  471. package/packages/shortcut-setter/src/utils/index.js +63 -63
  472. package/packages/skeleton/index.js +12 -12
  473. package/packages/skeleton/src/Skeleton.jsx +11 -11
  474. package/packages/slider/index.js +12 -12
  475. package/packages/slider/src/Slider.jsx +11 -11
  476. package/packages/space/index.js +12 -12
  477. package/packages/space/src/Space.jsx +11 -11
  478. package/packages/spin/index.js +12 -12
  479. package/packages/spin/src/Spin.jsx +11 -11
  480. package/packages/statistic/index.js +18 -18
  481. package/packages/statistic/src/Countdown.jsx +11 -11
  482. package/packages/statistic/src/Statistic.jsx +11 -11
  483. package/packages/steps/index.js +18 -18
  484. package/packages/steps/src/Step.jsx +11 -11
  485. package/packages/steps/src/Steps.jsx +11 -11
  486. package/packages/switch/index.js +12 -12
  487. package/packages/switch/src/Switch.jsx +11 -11
  488. package/packages/table-filter/index.js +27 -27
  489. package/packages/table-filter/src/base-search-com/BaseSearch.vue +2572 -2572
  490. package/packages/table-filter/src/classification/Classification-com.vue +1756 -1756
  491. package/packages/table-filter/src/classification/search-class-name.vue +266 -266
  492. package/packages/table-filter/src/classification/search-professional-model.vue +680 -680
  493. package/packages/table-filter/src/components/TextOverTooltip.vue +107 -107
  494. package/packages/table-filter/src/components/age-com/index.vue +205 -205
  495. package/packages/table-filter/src/components/button-group/ButtonGroup.vue +162 -162
  496. package/packages/table-filter/src/components/button-icon/button-icon.js +33 -33
  497. package/packages/table-filter/src/components/button-icon/getBtnIcon.js +34 -34
  498. package/packages/table-filter/src/components/c-tree-select/tree-select.vue +336 -336
  499. package/packages/table-filter/src/components/drop-button/drop-button.vue +224 -224
  500. package/packages/table-filter/src/components/drop-button/head-btn-icon.js +33 -33
  501. package/packages/table-filter/src/components/drop-view/drop-view.vue +89 -89
  502. package/packages/table-filter/src/components/multi-select/multi-select.vue +227 -227
  503. package/packages/table-filter/src/components/out-quick-search/out-quick-search.vue +340 -340
  504. package/packages/table-filter/src/components/range-age/index.vue +172 -172
  505. package/packages/table-filter/src/components/search-condition/SearchCondition.vue +1897 -1897
  506. package/packages/table-filter/src/components/search-condition/fieldTypeList.js +169 -169
  507. package/packages/table-filter/src/components/search-filter/SearchFilter.vue +278 -278
  508. package/packages/table-filter/src/components/search-modal/set-classification.vue +310 -310
  509. package/packages/table-filter/src/components/table-modal/TableModal.vue +473 -463
  510. package/packages/table-filter/src/const/dataOptions.js +43 -43
  511. package/packages/table-filter/src/const/index.js +1 -1
  512. package/packages/table-filter/src/index.vue +584 -584
  513. package/packages/table-filter/src/mixins/mixins.js +694 -694
  514. package/packages/table-filter/src/mixins/tableSearchCon.js +128 -128
  515. package/packages/table-filter/src/mixins/wordBookutils.js +102 -102
  516. package/packages/table-filter/src/quick-search/QuickSearch.vue +2125 -2125
  517. package/packages/tabs/index.js +18 -18
  518. package/packages/tabs/src/TabPane.jsx +11 -11
  519. package/packages/tabs/src/Tabs.jsx +11 -11
  520. package/packages/tag/index.js +21 -21
  521. package/packages/tag/src/CheckableTag.jsx +11 -11
  522. package/packages/tag/src/Tag.jsx +11 -11
  523. package/packages/tag/src/TagGroup.vue +621 -621
  524. package/packages/time-picker/index.js +12 -12
  525. package/packages/time-picker/src/TimePicker.jsx +11 -11
  526. package/packages/timeline/index.js +14 -14
  527. package/packages/timeline/src/Item.jsx +11 -11
  528. package/packages/timeline/src/Timeline.jsx +11 -11
  529. package/packages/tooltip/index.js +12 -12
  530. package/packages/tooltip/src/Tooltip.jsx +11 -11
  531. package/packages/transfer/index.js +12 -12
  532. package/packages/transfer/src/Transfer.jsx +11 -11
  533. package/packages/tree/index.js +18 -18
  534. package/packages/tree/src/Tree.jsx +11 -11
  535. package/packages/tree/src/TreeNode.jsx +11 -11
  536. package/packages/tree-select/index.js +18 -18
  537. package/packages/tree-select/src/TreeNode.jsx +11 -11
  538. package/packages/tree-select/src/TreeSelect.jsx +11 -11
  539. package/packages/upload/chunk-upload/chunk-upload-new.vue +1001 -1001
  540. package/packages/upload/chunk-upload/vod-chunk-upload.vue +749 -749
  541. package/packages/upload/chunk-upload/vod-upload-modal.vue +100 -100
  542. package/packages/upload/index.js +12 -12
  543. package/packages/upload/src/Upload.jsx +11 -11
  544. package/packages/verification-code/SlideVerify.vue +306 -306
  545. package/packages/verification-code/index.js +17 -17
  546. package/packages/verification-code/verification-code.vue +147 -147
  547. package/src/component/player-vod/index.vue +57 -57
  548. package/src/component/player-vod/player.vue +188 -188
  549. package/src/component/player-vod/video-list.vue +262 -262
  550. package/src/component/player-vod/video-modal.vue +128 -128
  551. package/src/component/select-options/index.vue +430 -413
  552. package/src/component/select-pages/index.vue +95 -95
  553. package/src/component/svg/index.vue +59 -59
  554. package/src/core/create.js +6 -6
  555. package/src/core/event.js +23 -23
  556. package/src/core/table-methods.js +444 -444
  557. package/src/directive/flexibleResize.js +151 -151
  558. package/src/directive/preventReClick.js +12 -12
  559. package/src/directive/scroll.js +230 -230
  560. package/src/global/variable.js +2 -2
  561. package/src/style/normalize.css +424 -424
  562. package/src/style/style.less +49 -49
  563. package/src/utils/UniRTCv2.js +626 -626
  564. package/src/utils/chatFetch.js +61 -61
  565. package/src/utils/clickoutside.js +75 -75
  566. package/src/utils/crypto.js +25 -25
  567. package/src/utils/index.js +81 -81
  568. package/src/utils/kty-sdk.js +582 -582
  569. package/src/utils/kty.min-1.0.0.js +14378 -14378
  570. package/src/utils/sip-device.js +79 -79
  571. package/src/utils/time-domain.js +193 -193
  572. package/src/utils/trtc.js +1 -1
  573. package/src/utils/utils-map.js +484 -484
  574. package/src/utils/vexutils.js +836 -836
@@ -1,1486 +1,1486 @@
1
- <template>
2
- <div class="message-wrapper">
3
- <chat-header :hideHeader="hideHeader" @openPortraitPanel="handleOpenPortraitPanel" v-if="isServer && enable"></chat-header>
4
- <a-layout-content>
5
- <slot></slot>
6
- <div v-if="showBroadcast" class="broadcast-wrap">
7
- <div v-if="broadcastCarousel" class="carousel-item" :class="{ 'has-link': assemblySetting.broadcast.PC.list[0].url }" @click="goToBroadcastLink(assemblySetting.broadcast.PC.list[0])">
8
- <a-icon type="sound" />
9
- <span class="ellips">
10
- {{ assemblySetting.broadcast.PC.list[0].content }}
11
- </span>
12
- </div>
13
- <template v-else>
14
- <a-carousel dotPosition="right" autoplay :dots="false">
15
- <div class="carousel-item" v-for="(item, i) in assemblySetting.broadcast.PC.list" :key="i" :class="{ 'has-link': item.url }" @click="goToBroadcastLink(item)">
16
- <a-icon type="sound" />
17
- <span class="ellips">{{ item.content }}</span>
18
- </div>
19
- </a-carousel>
20
- </template>
21
- </div>
22
- <div v-if="sessionHistoryList.length > 1 && !(!onChating && currentTab === 'end') && !sessionEnd" style="position: absolute; top: 0; left: 20px; z-index: 1">
23
- <a-radio-group :value="selectedSession" @change="handleSessionIdChange" buttonStyle="solid">
24
- <a-radio-button :value="item.id + '-' + item.fromId" v-for="item in sessionHistoryList" :key="item.id + item.fromId">{{ item.fromName }}</a-radio-button>
25
- </a-radio-group>
26
- </div>
27
- <div class="online-message" ref="message-wrapper" v-infinite-scroll="handleInfiniteOnLoad" @scroll="scrollEvent">
28
- <template v-for="(item, index) in msgList">
29
- <div :key="index" v-if="item">
30
- <p v-if="isNeedShowTime(item, index)" class="message-time">
31
- {{ formatDate(item.createdTime) }}
32
- </p>
33
- <div v-if="item.recallFlag" style="margin: 10px 0">
34
- <template v-if="handleRecallFlag(item)">
35
- {{ getRecallText(item, isServer) }}
36
- <a style="text-decoration: underline" @click="reEditMessage(item)" v-if="[0, 4].includes(item.content && item.content.type) && onChating && isServer">,{{ i18nText('1.9.368') }}</a>
37
- </template>
38
- <template v-else>
39
- {{ getRecallText(item) || `${item.fromName} ${i18nText('1.9.365')}` }}
40
- </template>
41
- </div>
42
- <!-- 系统消息 -->
43
- <div class="system-msg" v-else-if="isSystemMsg(item)">
44
- <a-icon class="system-icon" theme="filled" type="exclamation-circle" />
45
- <div>
46
- <span v-html="getSystemMsgContent(item)"></span>
47
- <span class="system-btn" v-for="(btn, index) in getTemplateButton(item)" @click="handleTemplateButton(btn)" :key="index">{{ btn.title }}</span>
48
- </div>
49
- </div>
50
- <div
51
- v-else-if="!item.recallFlag"
52
- class="message-item"
53
- :class="[getMessageItemClass(item), { 'read-status': enableReadRecord && item.readStatus }]"
54
- :data-rid="item.readStatus === 'N' ? item.id : ''"
55
- >
56
- <img v-if="!isServer && item.fromPortrait" class="msg-avatar" :src="item.fromPortrait" />
57
-
58
- <div v-else-if="!isServer && item.fromName" class="first-name">
59
- {{ item.fromName && item.fromName.slice(0, 2) }}
60
- </div>
61
- <img v-else-if="!isServer" class="msg-avatar" :src="getAvatar(item)" width="40" height="40" />
62
-
63
- <template v-else>
64
- <img :src="customerStaffIcon" class="msg-avatar" width="40" height="40" v-if="item.fromId === 'SYSTEM'" />
65
-
66
- <img src="../img/system_message.png" class="msg-avatar" width="40" height="40" v-else-if="item.content && item.content.source === 1" />
67
-
68
- <img v-else-if="item.portrait" class="msg-avatar" :src="item.portrait" />
69
-
70
- <div v-else class="first-name">
71
- {{ item.fromName && item.fromName.slice(0, 2) }}
72
- </div>
73
- </template>
74
- <div @contextmenu="handleContextmenu($event, item)" style="display: flex">
75
- <a-spin v-if="item.sending">
76
- <a-icon slot="indicator" type="loading" style="font-size: 24px" spin />
77
- </a-spin>
78
- <a-popconfirm @confirm="handleSendFail(item)" :okText="i18nText('1.1.1.1.3')" :cancelText="i18nText('2.7.1.14')">
79
- <a-icon type="exclamation-circle" slot="icon" />
80
- <div slot="title">
81
- <span>确认重新发送该消息?</span>
82
- </div>
83
- <a-icon v-if="item.fail" theme="filled" type="exclamation-circle" style="font-size: 16px; display: flex; align-items: center; color: red" />
84
- </a-popconfirm>
85
- <div class="content-wrap" v-if="item.content.type === 0">
86
- <span class="right-time">
87
- {{ formatDate(item.createdTime || item.sendTime, true) }}
88
- </span>
89
- <div
90
- :class="{
91
- content: true,
92
- 'has-dictionary': item.dictionaryValues && item.dictionaryValues.length,
93
- 'has-intention': item.intentionList && item.intentionList.length
94
- }"
95
- >
96
- <p v-html="getContentHtml(item)"></p>
97
- <template v-if="item.content.attachments && item.content.attachments.length > 0">
98
- <div v-for="(v, index) in item.content.attachments" :key="index" class="robot-item">
99
- <a v-if="v.type == 1" :href="v.url" target="_blank"> {{ v.desc }}(超链) </a>
100
- <a @click.prevent.stop="$emit('sendQuestion', v, item)" v-if="v.type == 2 || v.type == 3" href="javascript:void(0);">
101
- {{ v.desc }}
102
- </a>
103
- </div>
104
- </template>
105
- <div v-if="item.intentionList && item.intentionList.length" class="intention-wrap">
106
- <div v-for="(v, index) in item.intentionList" :key="index" @click.prevent.stop="$emit('sendIntention', v)">
107
- <svg-icon :icon-class="v.icon || 'moren'" style="font-size: 26px"></svg-icon>
108
- <span>{{ v.name }}</span>
109
- </div>
110
- </div>
111
- </div>
112
- <ul class="dictionary-ul" v-if="item.dictionaryValues && item.dictionaryValues.length">
113
- <li v-for="(v, i) in item.dictionaryValues" :key="i" @click.prevent.stop="$emit('sendDictionary', v, item)">
114
- {{ v }}
115
- </li>
116
- </ul>
117
- <div class="client-btn" v-if="item.autoAccess && item.autoAccess === 'false'">
118
- <svg-icon icon-class="kefu"></svg-icon>
119
- <span @click="$emit('goCustomServe')">转人工客服</span>
120
- </div>
121
- </div>
122
- <div v-else-if="item.content.type === 1" class="upload-image">
123
- <MsgPicture style="max-width: 345px" :image="item.content.content" @click="show(item)" />
124
- </div>
125
- <div class="content-wrap" v-else-if="item.content.type === 2">
126
- <span class="right-time">
127
- {{ formatDate(item.createdTime || item.sendTime, true) }}
128
- </span>
129
- <div class="content">
130
- <img style="width: 16px;margin-right:8px" src="../img/multi-video.png" />
131
- {{ item.content.content }}&nbsp;
132
- <span @click.prevent.stop="reCall(item, 'video')" class="re-call" v-if="item.content.isRefuse == 1">
133
- {{ handleReCall(item) }}
134
- </span>
135
- </div>
136
- </div>
137
- <div class="content-wrap" v-else-if="item.content.type === 5">
138
- <span class="right-time">
139
- {{ formatDate(item.createdTime || item.sendTime, true) }}
140
- </span>
141
- <div class="content">
142
- <img style="width: 16px;margin-right:8px" src="../img/multi-voice.png" />
143
- {{ item.content.content }}&nbsp;
144
- <span @click.prevent.stop="reCall(item, 'voice')" class="re-call" v-if="item.content.isRefuse == 1">
145
- {{ handleReCall(item) }}
146
- </span>
147
- </div>
148
- </div>
149
- <div class="content-wrap" v-else-if="item.content.type === 6">
150
- <span class="right-time">
151
- {{ formatDate(item.createdTime || item.sendTime, true) }}
152
- </span>
153
- <div class="voice-bg content" @click.prevent.stop="playAudio(item)">
154
- <img v-if="item.content.isPlay" src="../img/audio-play.gif" alt="" />
155
- <img v-else src="../img/audio-new.png" alt="" />
156
- <span class="voice-text" :style="AudioStyle(item)"> {{ item.content.duration || 1 }}" </span>
157
- </div>
158
- </div>
159
- <template v-else-if="item.content.type === 4">
160
- <msg-describe v-if="isDescribeMsg(item)" :item="item" />
161
- <div v-else class="content-wrap">
162
- <span class="right-time">
163
- {{ formatDate(item.createdTime || item.sendTime, true) }}
164
- </span>
165
- <MsgPrescription :data="item" :isServer="isServer" :theme="isServer ? '' : 'customer'" @handleTemplateDetail="handleTemplateDetail" @handleTemplateButton="handleTemplateButton" />
166
- </div>
167
- </template>
168
- </div>
169
- <ReadStatus v-if="enableReadRecord" :status="item.readStatus" @click="openReadRecord($event, item)" />
170
- </div>
171
- </div>
172
- </template>
173
- <ul class="menu-main" ref="contextMenu" :style="contextMenuStyle" v-if="contextMenuShow">
174
- <template v-for="(item, index) in rightClickSetting">
175
- <li :key="index" class="menu-item" @click="() => handleRightClick(contextMenuItem, item)">
176
- {{ item.rightClickName }}
177
- </li>
178
- </template>
179
- </ul>
180
- <div class="session-end" v-if="sessionEnd && !isServer">
181
- <span class="end">{{ i18nText('1.9.370') }}</span>
182
- </div>
183
- <a-spin v-if="loading" class="loading-icon" />
184
- </div>
185
- <!-- 图片预览 -->
186
- <viewer class="viewer" ref="viewer" :images="images" @inited="inited" :options="options" style="display: none">
187
- <template slot-scope="scope">
188
- <img v-for="src in scope.images" :src="src" :key="src" />
189
- </template>
190
- </viewer>
191
- <!-- 卡片详情 -->
192
- <a-modal
193
- wrapClassName="chat-footer-modal-small"
194
- :title="modalData.title || i18nText('1.1.1.10')"
195
- :visible="modalShow"
196
- :maskClosable="false"
197
- :mask="true"
198
- width="80%"
199
- destroyOnClose
200
- :okText="i18nText('1.2.1.11.6')"
201
- :footer="null"
202
- @cancel="handleClose"
203
- >
204
- <iframe id="chat-footer-modal" v-if="modalData.targetType === 'LINK_ADDRESS'" width="100%" height="100%" frameborder="0" :src="modalData.address" allow="camera;midi"></iframe>
205
- </a-modal>
206
- </a-layout-content>
207
- <audio style="display: none" controls autoplay ref="audio" preload="auto" crossOrigin="anonymous" media-player="audioPlayer" :src="curAudioUrl" @ended="audioEnd"></audio>
208
- <ReadRecord v-if="readRecordVisible" :style="readRecordStyle" :list="readRecordList" @close="closeReadRecord" />
209
- </div>
210
- </template>
211
-
212
- <script>
213
- import { Layout, Modal, Icon, Spin, Popconfirm, Carousel, Radio } from 'ant-design-vue';
214
- import SvgIcon from '@/component/svg/index.vue';
215
- import InfiniteScroll from '@/directive/scroll';
216
- import { mapGetters, mapMutations, mapActions } from '../store/helper';
217
- import fetch, { qs } from '@/utils/chatFetch';
218
- import MsgDescribe from '../components/msg-describe';
219
- import MsgPicture from '../components/msg-picture';
220
- import MsgPrescription from '../components/msg-prescription';
221
- import ReadStatus from '../components/read-status';
222
- import ReadRecord from '../components/read-record';
223
- import chatHeader from './chatHeader';
224
- import viewerOptions from './mixins/viewerOptions';
225
- import { getRecallText } from '../utils';
226
- import vexutils from '@/utils/vexutils';
227
- import ObserverScroll from '../utils/observer-scroll';
228
-
229
- const customerStaffIcon = require('../img/customer_staff.png');
230
- export default {
231
- mixins: [viewerOptions],
232
- inject: ['store', 'dispatchEvent', 'registerEvent', 'unregisterEvent', 'i18nText'],
233
- props: {
234
- hideHeader: {
235
- type: Boolean,
236
- default: false
237
- },
238
- robotAvatar: {
239
- type: String,
240
- default: ''
241
- },
242
- curChatType: {
243
- type: String
244
- },
245
- isShowPortraitPanel: {
246
- type: Boolean
247
- },
248
- activatedTime: {
249
- type: Number
250
- }
251
- },
252
- data() {
253
- return {
254
- loading: false,
255
- // 卡片详情
256
- modalShow: false,
257
- modalData: {},
258
- // 客户头像
259
- // 右键操作
260
- contextMenuStyle: {
261
- position: 'fixed',
262
- top: 0,
263
- left: 0
264
- },
265
- contextMenuShow: false,
266
- contextMenuItem: null,
267
- // 查看历史消息滚动定位
268
- scrollHeight: 0,
269
- curAudioUrl: '',
270
- lastAudioItem: null,
271
- readRecordStyle: '',
272
- readRecordVisible: false,
273
- readRecordList: []
274
- };
275
- },
276
- computed: {
277
- ...mapGetters([
278
- 'userInfo',
279
- 'currentTab',
280
- 'isAppendMsg',
281
- 'message',
282
- 'msgList',
283
- 'sessionId',
284
- 'serviceId',
285
- 'scrollTo',
286
- 'isServer',
287
- 'clientId',
288
- 'sessionEnd',
289
- 'assemblyId',
290
- 'footerMessage',
291
- 'portraitPanelParams',
292
- 'assemblySetting',
293
- 'clientParams',
294
- 'onChating',
295
- 'classify',
296
- 'collapsed',
297
- 'barStatus',
298
- 'sessionHistoryList',
299
- 'enable',
300
- 'appendList',
301
- 'queueItem',
302
- 'isRecorderVoice',
303
- 'callTimer',
304
- 'requestId'
305
- ]),
306
- orgId() {
307
- const getters = this.store.getters;
308
- return this.isServer ? this.userInfo?.sysParams?.orgId : getters.accessParams.orgId || getters.queryParams.orgId;
309
- },
310
- userId() {
311
- const getters = this.store.getters;
312
- return this.isServer ? this.userInfo?.sysParams?.userId : getters.accessParams.userId || getters.chatUserId || getters.queryParams.userId;
313
- },
314
- enableReadRecord() {
315
- return this.assemblySetting?.readStatus === 'Y';
316
- },
317
- customerStaffIcon() {
318
- return this.robotAvatar || customerStaffIcon;
319
- },
320
- selectedSession() {
321
- let sessionMap = this.sessionHistoryList.find(item => item.fromId === this.serviceId);
322
- if (sessionMap) {
323
- return sessionMap.id + '-' + sessionMap.fromId;
324
- }
325
- return '';
326
- },
327
- isListClassify() {
328
- return this.assemblySetting.isListClassify === 'Y';
329
- },
330
- showPanel() {
331
- return this.assemblySetting.portraitPanelSetting?.targetIs;
332
- },
333
- showBroadcast() {
334
- let res = (!this.isServer && this.assemblySetting?.broadcast?.PC?.isChecked == 'Y' && this.assemblySetting?.broadcast?.PC?.list?.length > 0) || false;
335
- return res;
336
- },
337
- broadcastCarousel() {
338
- return (this.assemblySetting.broadcast && this.assemblySetting.broadcast.PC.list.length == 1) || false;
339
- },
340
- rightClickSetting() {
341
- const { isServer } = this;
342
- if (!isServer) return [];
343
- const isSelf = this.contextMenuItem.fromId === this.serviceId;
344
- const setting = this.assemblySetting?.rightClickSetting || [];
345
- const doArr = ['resend', 'rollBack'];
346
- return setting.filter(item => {
347
- const { rightClickDo } = item;
348
- return isSelf || doArr.indexOf(rightClickDo) < 0;
349
- });
350
- },
351
- handleReCall() {
352
- return function(item) {
353
- if (item.content?.source === 1) {
354
- return '[点击回拨]';
355
- } else if (item.senderFlag == 1) {
356
- return `[点击${this.isServer ? '回拨' : '重拨'}]`;
357
- }
358
- if (item.senderFlag == 2) {
359
- return `[点击${this.isServer ? '重拨' : '回拨'}]`;
360
- }
361
- if (this.isServer) {
362
- let isRight = item.fromId !== 'SYSTEM' && item.fromId && item.fromId === this.serviceId;
363
- return `[点击${isRight ? '重拨' : '回拨'}]`;
364
- } else {
365
- let isRight = (item.fromId !== 'SYSTEM' && item.toId === 'SYSTEM') || item.fromId === this.clientId || (item.mode === 'bot' && !item.fromId);
366
- return `[点击${isRight ? '重拨' : '回拨'}]`;
367
- }
368
- };
369
- },
370
- handleRecallFlag() {
371
- return function(item) {
372
- return this.serviceId == item.fromId;
373
- };
374
- },
375
- AudioStyle() {
376
- return function(item) {
377
- let { duration = 1 } = item.content;
378
- if (!duration) {
379
- duration = 1;
380
- }
381
- let curWidth = (+duration / 60) * 400;
382
- return {
383
- width: curWidth < 13 ? 13 : curWidth + 'px'
384
- };
385
- };
386
- },
387
- showTimeDuration() {
388
- let { variableSetting = null } = this.assemblySetting || {};
389
- if (!variableSetting) return 3;
390
- let matchItem = variableSetting.find(v => v.name === 'CHAT_SEPARATION_TIME');
391
- if (!matchItem) return 3;
392
- if (!+matchItem.value) return 3;
393
- return +matchItem.value;
394
- },
395
- images() {
396
- return this.msgList.filter(v => !v.recallFlag && v.content.type == 1)?.map(v => v.content?.content);
397
- }
398
- },
399
- created() {
400
- this.attachEvent();
401
- this.getUserInformation();
402
- },
403
- methods: {
404
- ...mapMutations([
405
- 'setIsAppendMsg',
406
- 'setMsgList',
407
- 'setScrollTo',
408
- 'setFooterMessage',
409
- 'setIsScrollListChange',
410
- 'setListChangeItem',
411
- 'setSessionId',
412
- 'setOnChating',
413
- 'setServiceId',
414
- 'setShowAudio',
415
- 'setVideoMode',
416
- 'setVideoMembers',
417
- 'setIsRecorderVoice',
418
- 'setDoctorInfo',
419
- 'setCallTimer'
420
- ]),
421
- ...mapActions({
422
- getEarlierMsg: 'setMsgList',
423
- handleBotChat: 'handleBotChat',
424
- sendMessage: 'sendMessage'
425
- }),
426
- // 获取医生信息
427
- getUserInformation() {
428
- const url = '/user/getUserInformation';
429
- fetch.get(url).then(({ data }) => {
430
- if (data.result === 'SUCCESS') {
431
- this.setDoctorInfo(data?.map?.sysUserInformation)
432
- console.log('data?.map?.sysUserInformation', data?.map?.sysUserInformation);
433
- }
434
- });
435
- },
436
- getMessageItemClass(item) {
437
- // content.source === 1, 代表为系统消息, 系统统一左侧展示
438
- if (item.content?.source === 1) {
439
- return {
440
- left: true
441
- };
442
- }
443
-
444
- // 服务端 senderFlag 1 左 2 右
445
- // 客户端 senderFlag 1 右 2 左
446
- if (item.content?.source === 1) {
447
- return {
448
- left: true
449
- };
450
- } else if (item.senderFlag == 1) {
451
- if (this.isServer) {
452
- return {
453
- left: true
454
- };
455
- } else {
456
- return {
457
- right: true
458
- };
459
- }
460
- }
461
- if (item.senderFlag == 2) {
462
- if (this.isServer) {
463
- return {
464
- right: true
465
- };
466
- } else {
467
- return {
468
- left: true
469
- };
470
- }
471
- }
472
- if (this.isServer) {
473
- return {
474
- right: item.fromId !== 'SYSTEM' && item.fromId && item.fromId === this.serviceId,
475
- left: item.fromId === 'SYSTEM' || item.fromId === this.clientId || item.content?.source === 1 || !item.fromId
476
- };
477
- } else {
478
- let isRight, isLeft;
479
- if (this.curChatType == 'robot') {
480
- isRight = item.fromId !== 'SYSTEM';
481
- isLeft = item.fromId === 'SYSTEM';
482
- } else {
483
- isRight = (item.fromId !== 'SYSTEM' && item.toId === 'SYSTEM') || item.fromId === this.clientId || (item.mode === 'bot' && !item.fromId);
484
- isLeft = item.fromId === 'SYSTEM' || item.fromId !== this.clientId || item.content?.source === 1;
485
- }
486
- return {
487
- right: isRight,
488
- left: isLeft
489
- };
490
- }
491
- },
492
- /* 向上滚动刷新 */
493
- handleInfiniteOnLoad() {
494
- if (this.curChatType === 'robot') return;
495
- this.loading = true;
496
- this.getEarlierMsg().then(msg => {
497
- if (msg) {
498
- this.$message.info(msg);
499
- }
500
- this.loading = false;
501
- });
502
- },
503
- show(item) {
504
- const src = item.content.content;
505
- // this.images = vexutils.imgs2imgArr(src);
506
- this.$viewer.view(this.images.findIndex(v => v === src));
507
- this.$viewer.show();
508
- this.readSingleMsg(item);
509
- },
510
- /* 滚动条定位 */
511
- handleScrollTo(scrollTo) {
512
- let target = this.$refs['message-wrapper'];
513
- this.$nextTick().then(() => {
514
- if (!target) return;
515
- let top = target.scrollHeight;
516
- if (scrollTo === 'current') {
517
- top = target.scrollHeight - this.scrollHeight;
518
- }
519
- if (typeof scrollTo === 'number') {
520
- top = scrollTo;
521
- }
522
- target.scrollTo({
523
- left: 0,
524
- top,
525
- behavior: 'instant'
526
- });
527
- this.scrollHeight = 0;
528
- });
529
- this.setScrollTo('');
530
- },
531
-
532
- /**
533
- * 是否需要显示时间
534
- */
535
- isNeedShowTime(item, index) {
536
- const curMsgTime = item.createdTime || new Date().getTime();
537
- let duration = 60 * 1000 * this.showTimeDuration;
538
- let lastMsgTime;
539
- if (index == 0) {
540
- lastMsgTime = 0;
541
- } else {
542
- lastMsgTime = this.msgList[index - 1].createdTime;
543
- }
544
- if (Number(curMsgTime) - Number(lastMsgTime) > duration) {
545
- return true;
546
- } else {
547
- return false;
548
- }
549
- },
550
- formatDate(date) {
551
- return vexutils.formatDate(date, true);
552
- },
553
- getAvatar(item) {
554
- if (item.content && item.content.source === 1) {
555
- return require('../img/system_message.png');
556
- }
557
- if (item.mode === 'bot' && !item.fromId) {
558
- return require('../img/default.png');
559
- }
560
- if (this.isServer) {
561
- if (item.fromId === this.clientId || !item.fromId) {
562
- return require('../img/default.png');
563
- } else {
564
- return this.customerStaffIcon;
565
- }
566
- } else {
567
- if (this.curChatType == 'robot') {
568
- if (item.fromId === 'SYSTEM') {
569
- return this.customerStaffIcon;
570
- } else {
571
- return require('../img/default.png');
572
- }
573
- } else {
574
- if (item.fromId !== this.clientId) {
575
- return this.customerStaffIcon;
576
- } else {
577
- return require('../img/default.png');
578
- }
579
- }
580
- }
581
- },
582
- getContent(item) {
583
- let content;
584
- if (vexutils.isJSON(item.content.content)) {
585
- content = JSON.parse(item.content.content);
586
- } else {
587
- content = item.content.content;
588
- }
589
- return content;
590
- },
591
- getContentHtml(item) {
592
- return !~item.content.content.indexOf('emoji') ? this.$xss(item.content.content) : item.content.content;
593
- },
594
- getTemplateTitle(item) {
595
- return this.getContent(item)?.title;
596
- },
597
- showArrow(item) {
598
- let setting = this.getContent(item)?.customerSetting || {};
599
- if (this.isServer) {
600
- setting = this.getContent(item)?.serverSetting || {};
601
- }
602
- let { address } = setting;
603
- if (!address) return false;
604
- return true;
605
- },
606
- getTemplateTitleIcon(item) {
607
- return this.getContent(item)?.icon;
608
- },
609
- getTemplateContent(item) {
610
- let content = this.getContent(item);
611
- let array = [];
612
- try {
613
- array = content.content.split('##');
614
- } catch {
615
- console.log(content, 'error');
616
- }
617
- array = array.map(item => `<div>${item}</div>`);
618
- return array.join('');
619
- },
620
- getSystemMsgContent(item) {
621
- let content = this.getContent(item);
622
- let array = [];
623
- try {
624
- array = content.content.split('##');
625
- } catch {
626
- console.log(content, 'error');
627
- }
628
- return array.join('<br>');
629
- },
630
- getTemplateButton(item) {
631
- let content = this.getContent(item);
632
- const setting = this.isServer ? content.serverSetting : content.customerSetting;
633
- return setting?.toolbar_button || [];
634
- },
635
- // 处理模板按钮,详情等跳转
636
- handleTemplate(data, dispatch = true) {
637
- let { targetType, address, params = [], openMode } = data;
638
- dispatch &&
639
- this.dispatchEvent('click_templateCard', {
640
- ...data
641
- });
642
- if (!address) return;
643
- if (targetType === 'LINK_ADDRESS') {
644
- let urlParams = [];
645
- params.forEach(({ p_name, p_value }) => {
646
- urlParams.push(`${p_name}=${p_value}`);
647
- });
648
- if (address.includes('?')) {
649
- address += `&${urlParams.join('&')}`;
650
- } else {
651
- address += `?${urlParams.join('&')}`;
652
- }
653
- if (openMode === 'WINDOW') {
654
- window.open(address);
655
- return;
656
- }
657
- if (openMode === 'EJECT') {
658
- // 打开云his页面需要从cookie内获取最新的jsessionids
659
- if (address.includes('jsessionidsFlag=1')) {
660
- let jsessionids = vexutils.cookie.get('jsessionids')
661
- address = address + `&jsessionids=${jsessionids}`
662
- }
663
- this.modalShow = true;
664
- this.modalData = Object.assign({}, data, {
665
- address
666
- });
667
- this.$nextTick().then(() => {
668
- window.addEventListener('message', this.iframeEvent);
669
- });
670
- return;
671
- }
672
- }
673
- },
674
- iframeEvent(event) {
675
- const method = event?.data?.method;
676
- this[method]?.(event);
677
- },
678
- // 发送
679
- handleParentMessageSend({ data, source, origin }) {
680
- let params = {
681
- assemblyId: this.assemblyId,
682
- sessionId: this.sessionId,
683
- orgId: this.orgId
684
- };
685
- params = Object.assign(params, data.data);
686
- return fetch.post('/chat/access/sendToolBarData', qs.stringify(params)).then(({ data }) => {
687
- if (data.result === 'SUCCESS') {
688
- source.postMessage({ status: 0, resultMsg: data.resultMsg }, origin);
689
- } else {
690
- source.postMessage({ status: 1, resultMsg: data.resultMsg }, origin);
691
- }
692
- });
693
- },
694
- // 关闭
695
- handleClose() {
696
- this.modalShow = false;
697
- window.removeEventListener('message', this.iframeEvent);
698
- },
699
- handleTemplateDetail(item) {
700
- let content = this.getContent(item) || {};
701
- let setting = content.customerSetting || {};
702
- if (this.isServer) {
703
- setting = content.serverSetting || {};
704
- }
705
- this.readSingleMsg(item);
706
- this.handleTemplate(setting);
707
- },
708
- handleTemplateButton(btn) {
709
- this.handleTemplate(btn);
710
- },
711
- // 消息撤回
712
- handleRecall(item) {
713
- let params = {
714
- assemblyId: this.assemblyId,
715
- sessionId: this.sessionId,
716
- messageId: item.id,
717
- messageType: item.content?.type,
718
- userId: this.userInfo?.sysParams?.userId || ''
719
- };
720
-
721
- fetch.post('/chat/service/recallMessage', qs.stringify(params)).then(({ data }) => {
722
- if (data.result === 'SUCCESS') {
723
- this.dispatchEvent('msg_recallSuccess', {
724
- message: { ...item },
725
- ...params
726
- });
727
- this.$set(item, 'recallFlag', 1);
728
- // 撤回成功提示
729
- let title = this.getContent(item)?.title;
730
- let text = title ? `已撤回[${title}]` : '已撤回';
731
- this.$message.success(text);
732
-
733
- if (!this.isLastMessage(item.id)) return;
734
- this.setIsScrollListChange(true);
735
- this.setListChangeItem({
736
- opt: 1,
737
- from: this.clientId,
738
- sessionId: this.sessionId,
739
- msgCount: 0,
740
- lastContentRecallFlag: 1,
741
- lastContent: vexutils.clone(item.content, true),
742
- lastTime: new Date().getTime()
743
- });
744
- } else {
745
- this.$message.error(data.resultMsg);
746
- }
747
- });
748
- },
749
- handleJumpUrl(contextMenuItem, item) {
750
- const title = item.rightClickName;
751
- const url = item.rightClickUrl.replace(/{(.*?)}/g, (match, p) => {
752
- return eval(p);
753
- });
754
- this.dispatchEvent('open_jumpUrl', {
755
- title: title,
756
- url: url
757
- });
758
- },
759
- // 右键菜单功能
760
- handleRightClick(contextMenuItem, item) {
761
- if (item.rightClickDo === 'rollBack') {
762
- this.handleRecall(contextMenuItem);
763
- } else if (item.rightClickDo === 'jumpUrl') {
764
- this.handleJumpUrl(contextMenuItem, item);
765
- } else if (item.rightClickDo === 'resend') {
766
- this.resendMessage(contextMenuItem);
767
- }
768
- },
769
- resendMessage(item) {
770
- fetch({
771
- method: 'POST',
772
- url: '/chat/service/resendMessage',
773
- data: qs.stringify({
774
- assemblyId: this.assemblyId,
775
- sessionId: this.sessionId,
776
- messageId: item.id
777
- })
778
- });
779
- },
780
- // 重新编辑
781
- reEditMessage(item) {
782
- if (item.content?.type === 4) {
783
- let content = this.getContent(item);
784
- this.handleTemplate(content.serverSetting.reEditSetting, false);
785
- return;
786
- }
787
- this.setFooterMessage(this.footerMessage + item.content.content.slice(5, -6));
788
- },
789
- observeMsg() {
790
- if (this.enableReadRecord) {
791
- if (!this._observer) {
792
- this._observer = new ObserverScroll(this.$refs['message-wrapper'], this.readMsg);
793
- this.$on('hook:beforeDestroy', () => {
794
- this._observer.remove();
795
- this._observer = null;
796
- });
797
- }
798
- this._observer.disconnect();
799
- if (this.msgList.length) {
800
- this.$nextTick(() => {
801
- const el = this.$refs['message-wrapper'];
802
- const els = el?.querySelectorAll('.message-item.left');
803
- els && this._observer.observe(Array.from(els).filter(item => item.getAttribute('data-rid')));
804
- });
805
- }
806
- }
807
- },
808
- readMsg(ids, enableVaid = true, notify = true, post = true) {
809
- const msgList = this.msgList;
810
- const idKeys = msgList.reduce((obj, item, i) => {
811
- const id = item?.id;
812
- if (id) obj[id] = i;
813
- return obj;
814
- }, {});
815
- const data = [];
816
- const validFn = (item, type) => {
817
- /**
818
- * 针对文本、表情消息类型,只要在聊天区域内,视为已读,
819
- * 图片、语音类型消息,需要点击查看后,才视为已读,
820
- * 模板 类型消息,如果没有详情链接,只要在聊天区域内,视为已读,若有详情链接,需要点击查看后,才视为已读
821
- */
822
- const jumpType = `,6,1,`;
823
- if (jumpType.includes(type)) return false;
824
- if (type == 4 && !this.isDescribeMsg(item)) {
825
- let content = this.getContent(item) || {};
826
- let setting = (this.isServer ? content.serverSetting : content.customerSetting) || {};
827
- return !setting?.address;
828
- }
829
- return true;
830
- };
831
- const value = ids.filter(id => {
832
- const i = idKeys[id];
833
- let item = msgList[i];
834
- if (item) {
835
- const type = item.content.type;
836
- if (!enableVaid || validFn(item, type)) {
837
- item.readStatus = 'Y';
838
- data.push({ id, type });
839
- return true;
840
- }
841
- return false;
842
- }
843
- });
844
- if (!value.length) return;
845
- notify &&
846
- this.dispatchEvent('msg_readed', {
847
- data,
848
- assemblyId: this.assemblyId,
849
- sessionId: this.sessionId
850
- });
851
- post && this.postReadMessage({ ids: value.join(',') });
852
- },
853
- postReadMessage(data) {
854
- fetch.post(
855
- `/chat/${this.isServer ? 'service' : 'access'}/readMessage`,
856
- qs.stringify({
857
- assemblyId: this.assemblyId,
858
- orgId: this.orgId,
859
- userId: this.userId,
860
- sessionId: this.sessionId,
861
- ...data
862
- })
863
- );
864
- },
865
- readSingleMsg(item) {
866
- if (this.enableReadRecord && item.readStatus === 'N') {
867
- const cls = this.getMessageItemClass(item);
868
- if (!cls?.left) return;
869
- this.readMsg([item.id], false);
870
- }
871
- },
872
- closeReadRecord() {
873
- this.readRecordVisible = false;
874
- this.readRecordList = [];
875
- },
876
- openReadRecord(event, item) {
877
- const readStatus = item.readStatus;
878
- if (readStatus) {
879
- const cls = this.getMessageItemClass(item);
880
- if (!cls.right) return;
881
- const rect = event.target.getBoundingClientRect();
882
- const w = 440;
883
- const h = 340;
884
- const top = Math.max(0, Math.min(document.body.clientHeight - h, rect.top - h / 2));
885
- const left = Math.max(0, Math.min(document.body.clientWidth - w, rect.left - w));
886
- fetch({
887
- url: `/chat/${this.isServer ? 'service' : 'access'}/getMessageMember`,
888
- method: 'get',
889
- params: {
890
- assemblyId: this.assemblyId,
891
- messageId: item.id,
892
- orgId: this.isServer ? undefined : this.orgId
893
- }
894
- }).then(res => {
895
- const list = res.data?.list || [];
896
- this.readRecordStyle = `top:${top}px;left:${left}px;position:fixed;z-index:10`;
897
- this.readRecordVisible = list.length > 0;
898
- this.readRecordList = Object.freeze(list);
899
- if (item.readStatus && list.length) {
900
- const unread = list.filter(item => item.readStatus == 'N').length;
901
- const single = list.length < 2;
902
- const status = unread > 0 ? (single ? 'N' : `${unread}`) : single ? 'Y' : 'ALL';
903
- // 检查是否更新人数
904
- if (item.readStatus != status) {
905
- item.readStatus = status;
906
- }
907
- }
908
- });
909
- }
910
- },
911
- isLastMessage(id) {
912
- return this.msgList.findIndex(msg => msg.id === id) === this.msgList.length - 1;
913
- },
914
- isSystemMsg(item) {
915
- return item.content.type == 4 && this.getContent(item).styleId == '2';
916
- },
917
- isDescribeMsg(item) {
918
- return this.getContent(item).styleId == '3';
919
- },
920
- async handleOpenPortraitPanel(item) {
921
- if (!this.showPanel || item.fromId === this.serviceId || !this.isServer || !this.isShowPortraitPanel) return;
922
- const { portraitPanelParams, clientParams } = this;
923
- const panelSetting = this.assemblySetting?.portraitPanelSetting;
924
- this.dispatchEvent('open_portraitPanel', {
925
- panelSetting,
926
- clientParams,
927
- panelParams: portraitPanelParams,
928
- targetId: panelSetting?.targetId,
929
- targetType: panelSetting?.targetType
930
- });
931
- },
932
- handleContextmenu(e, item) {
933
- // 系统消息,客户消息,待接入消息,历史消息不允许撤回
934
- // 模板-描述消息禁用右键
935
- if (item.content?.source === 1 || !this.sessionId || (this.sessionId && !this.onChating) || (item.content.type === 4 && this.isDescribeMsg(item))) return;
936
- this.contextMenuItem = item;
937
- if (this.rightClickSetting.length < 1) return;
938
- e.preventDefault();
939
- let left = e.pageX + 'px';
940
- let top = e.pageY + 'px';
941
- this.contextMenuStyle = Object.assign(this.contextMenuStyle, {
942
- top,
943
- left
944
- });
945
- this.contextMenuShow = true;
946
- },
947
- scrollEvent(e) {
948
- console.log('e.target.scrollTop->', e.target.scrollTop);
949
- this._scrollTop = e.target.scrollTop;
950
- },
951
- /* 事件绑定 */
952
- attachEvent() {
953
- this.scrollEvent = vexutils.debounce(this.scrollEvent, 800);
954
- const handleClick = () => {
955
- this.contextMenuShow = false;
956
- };
957
- const handleRasize = () => {
958
- if (this.readRecordVisible) {
959
- let style = this.readRecordStyle;
960
- const top = style.match(topReg)?.[1];
961
- const topReg = /top:([\d.]+)/;
962
- const maxH = document.body.clientHeight - 360;
963
- if (top && maxH < top) {
964
- style = style.replace(topReg, `top:${maxH}`);
965
- }
966
- const leftReg = /left:([\d.]+)/;
967
- const left = style.match(leftReg)?.[1];
968
- const maxW = document.body.clientWidth - 440;
969
- if (left && maxW < left) {
970
- style = style.replace(leftReg, `left:${maxW}`);
971
- }
972
- this.readRecordStyle = style;
973
- }
974
- };
975
- window.addEventListener('resize', handleRasize);
976
- document.addEventListener('click', handleClick);
977
- this.registerEvent('openPortraitPanel', this.handleOpenPortraitPanel);
978
- const readMessage = data => {
979
- this.readMsg(data.ids.split(','), false, false, false);
980
- this.postReadMessage(data);
981
- };
982
- this.registerEvent('readMessage', readMessage);
983
- this.$on('hook:beforeDestroy', () => {
984
- window.removeEventListener('resize', handleRasize);
985
- document.removeEventListener('click', handleClick);
986
- this.unregisterEvent('openPortraitPanel', this.handleOpenPortraitPanel);
987
- this.unregisterEvent('readMessage', readMessage);
988
- });
989
- },
990
- goToBroadcastLink(item) {
991
- if (!item.url) return;
992
- window.open(item.url);
993
- },
994
- /**
995
- * 客服切换
996
- */
997
- handleSessionIdChange(e) {
998
- let [sessionId, serviceId] = e.target.value.split('-');
999
- this.setSessionId(sessionId);
1000
- this.setServiceId(serviceId);
1001
- let isOnChating = this.sessionHistoryList.find(item => item.fromId === serviceId).onChating;
1002
- this.setOnChating(!!isOnChating);
1003
- this.setMsgList([]);
1004
- this.getEarlierMsg();
1005
- },
1006
- handleSendFail(item) {
1007
- this.sendMessage(Object.assign(item.content, { failItem: item }));
1008
- },
1009
- reCall(type) {
1010
- this.setShowAudio(true);
1011
- this.setVideoMode(type === 'voice' ? 1 : 2);
1012
- this.$nextTick(() => {
1013
- this.setVideoMembers([]);
1014
- });
1015
- },
1016
- playAudio(item) {
1017
- let { content = '' } = item?.content || {};
1018
- if (!content) return;
1019
- if (this.lastAudioItem && (item.id !== this.lastAudioItem.id || item.createdTime !== this.lastAudioItem.createdTime)) {
1020
- this.$set(this.lastAudioItem.content, 'isPlay', false);
1021
- }
1022
-
1023
- this.readSingleMsg(item);
1024
- this.lastAudioItem = item;
1025
- this.$set(item.content, 'isPlay', !item.content.isPlay);
1026
- this.$nextTick(() => {
1027
- this.curAudioUrl = content;
1028
- let audioRef = this.$refs['audio'];
1029
- if (!audioRef) return;
1030
- audioRef.load();
1031
- setTimeout(() => {
1032
- if (item.content.isPlay) {
1033
- let playPromise = audioRef.play();
1034
- if (playPromise !== undefined) {
1035
- playPromise.then(() => {
1036
- audioRef.play();
1037
- });
1038
- }
1039
- } else {
1040
- audioRef.pause();
1041
- this.curAudioUrl = '';
1042
- }
1043
- });
1044
- });
1045
- },
1046
- audioEnd() {
1047
- if (!this.lastAudioItem) return;
1048
- this.$set(this.lastAudioItem.content, 'isPlay', false);
1049
- },
1050
- getRecallText(item, flag = false) {
1051
- return getRecallText(this, item, flag);
1052
- }
1053
- },
1054
- directives: {
1055
- InfiniteScroll
1056
- },
1057
- components: {
1058
- AModal: Modal,
1059
- [Layout.Content.name]: Layout.Content,
1060
- [Spin.name]: Spin,
1061
- [Icon.name]: Icon,
1062
- [Popconfirm.name]: Popconfirm,
1063
- [Radio.Button.name]: Radio.Button,
1064
- [Radio.Group.name]: Radio.Group,
1065
- [Carousel.name]: Carousel,
1066
- SvgIcon,
1067
- MsgPrescription,
1068
- ReadStatus,
1069
- ReadRecord,
1070
- MsgPicture,
1071
- MsgDescribe,
1072
- chatHeader
1073
- },
1074
- watch: {
1075
- msgList() {
1076
- this.observeMsg();
1077
- },
1078
- isAppendMsg: {
1079
- immediate: true,
1080
- handler(isAppendMsg) {
1081
- if (isAppendMsg) {
1082
- let newArr = [];
1083
- if (vexutils.isPlainObject(this.message)) {
1084
- newArr = [...this.msgList, this.message];
1085
- } else if (vexutils.isArray(this.message)) {
1086
- newArr = [...this.msgList, ...this.message];
1087
- }
1088
- this.setMsgList(newArr);
1089
- this.setIsAppendMsg(false);
1090
- this.setScrollTo('bottom');
1091
- }
1092
- }
1093
- },
1094
- scrollTo: {
1095
- immediate: true,
1096
- handler(scrollTo) {
1097
- if (scrollTo) {
1098
- this.handleScrollTo(scrollTo);
1099
- }
1100
- }
1101
- },
1102
- activatedTime: {
1103
- immediate: true,
1104
- handler(value) {
1105
- this.handleScrollTo(this._scrollTop);
1106
- }
1107
- },
1108
- /**
1109
- * 图片懒加载处理
1110
- */
1111
- appendList: {
1112
- handler() {
1113
- if (!this.scrollHeight) {
1114
- let target = this.$refs['message-wrapper'];
1115
- this.scrollHeight = target.scrollHeight;
1116
- }
1117
- if (this.appendList.length === this.msgList.length || this.appendList.length === 1) {
1118
- this.setScrollTo('bottom');
1119
- } else {
1120
- this.setScrollTo('current');
1121
- }
1122
- }
1123
- },
1124
- isRecorderVoice: {
1125
- immediate: true,
1126
- handler(val) {
1127
- if (!val) return;
1128
- this.curAudioUrl = '';
1129
- this.setIsRecorderVoice(false);
1130
- if (!this.lastAudioItem) return;
1131
- this.$set(this.lastAudioItem.content, 'isPlay', false);
1132
- }
1133
- }
1134
- }
1135
- };
1136
- </script>
1137
-
1138
- <style lang="less" scoped>
1139
- @import '../style/emoji.css';
1140
- @import '../style/message.mixin.less';
1141
- .message-wrapper {
1142
- .createMessageStyle(#ebebeb, @primary-color);
1143
- flex: 1;
1144
- height: 0;
1145
- // position: relative;
1146
- display: flex;
1147
- flex-direction: column;
1148
- overflow-x: hidden;
1149
- .broadcast-wrap {
1150
- height: 30px;
1151
- text-align: center;
1152
- font-size: 14px;
1153
- background-color: #fffbe8;
1154
- /deep/ .carousel-item {
1155
- display: inline-flex !important;
1156
- justify-content: center;
1157
- align-items: center;
1158
- width: 100%;
1159
- height: 30px;
1160
- text-align: center;
1161
- &.has-link {
1162
- color: #ed6a0c;
1163
- cursor: pointer;
1164
- }
1165
- span {
1166
- display: inline-block;
1167
- max-width: calc(100% - 30px);
1168
- margin-left: 4px;
1169
- }
1170
- }
1171
- }
1172
-
1173
- .loading-icon {
1174
- position: absolute;
1175
- top: 0;
1176
- left: 50%;
1177
- }
1178
- }
1179
- /deep/ .ant-layout-content {
1180
- flex: 1;
1181
- display: flex;
1182
- flex-direction: column;
1183
- position: relative;
1184
- }
1185
- .online-message {
1186
- flex: 1;
1187
- height: 100%;
1188
- // height: 608px;
1189
- overflow-y: auto;
1190
- overflow-x: hidden;
1191
- padding: 10px 22px;
1192
- // margin: 10px 0;
1193
- text-align: center;
1194
- &::-webkit-scrollbar {
1195
- width: 5px;
1196
- }
1197
- .message-time {
1198
- display: inline-block;
1199
- color: #666;
1200
- font-size: 14px;
1201
- border-radius: 4px;
1202
- padding: 4px;
1203
- margin: 0 auto;
1204
- margin-bottom: 16px;
1205
- }
1206
- > div {
1207
- margin-bottom: 16px;
1208
- }
1209
- .message-item {
1210
- display: flex;
1211
- /deep/ .chat-read-status-text {
1212
- display: none;
1213
- }
1214
- &.right.read-status {
1215
- padding-bottom: 24px;
1216
- position: relative;
1217
- /deep/ .chat-read-status-text {
1218
- display: block;
1219
- position: absolute;
1220
- right: 50px;
1221
- bottom: 0;
1222
- }
1223
- }
1224
- .right-time {
1225
- display: none;
1226
- position: absolute;
1227
- top: -16px;
1228
- left: 10px;
1229
- font-size: 12px;
1230
- color: #b0b1b1;
1231
- white-space: nowrap;
1232
- }
1233
-
1234
- .content-wrap {
1235
- padding: 0 10px;
1236
- text-align: left;
1237
- position: relative;
1238
- &:hover {
1239
- .right-time {
1240
- display: block;
1241
- }
1242
- }
1243
-
1244
- .robot-item {
1245
- text-align: left;
1246
- a {
1247
- text-decoration: underline;
1248
- }
1249
- }
1250
- .intention-wrap {
1251
- display: flex;
1252
- flex-wrap: wrap;
1253
- padding: 8px 0 4px 0;
1254
- background-color: #fff;
1255
- border-radius: 0 0 8px 8px;
1256
- > div {
1257
- display: inline-flex;
1258
- flex-direction: column;
1259
- align-items: center;
1260
- width: 60px;
1261
- height: 54px;
1262
- cursor: pointer;
1263
-
1264
- &:hover {
1265
- color: #5585f5;
1266
- span {
1267
- color: #5585f5;
1268
- }
1269
- }
1270
- }
1271
- span {
1272
- margin-top: 8px;
1273
- max-width: 90%;
1274
- text-overflow: ellipsis;
1275
- overflow: hidden;
1276
- white-space: nowrap;
1277
- font-size: 12px;
1278
- color: #6a6a6a;
1279
- }
1280
- }
1281
- .dictionary-ul {
1282
- padding-top: 8px;
1283
- display: flex;
1284
- flex-wrap: wrap;
1285
- li {
1286
- padding: 4px 16px;
1287
- margin-right: 16px;
1288
- font-size: 14px;
1289
- color: #5585f5;
1290
- background: #efefef;
1291
- border-radius: 16px;
1292
- cursor: pointer;
1293
- }
1294
- }
1295
- .client-btn {
1296
- display: inline-block;
1297
- padding: 4px 8px;
1298
- margin-top: 8px;
1299
- color: #fff;
1300
- font-size: 12px;
1301
- border-radius: 20px;
1302
- background: #5585f5;
1303
- cursor: pointer;
1304
- }
1305
- .re-call {
1306
- cursor: pointer;
1307
- color: #5585f5;
1308
- }
1309
- .voice-bg {
1310
- display: flex;
1311
- align-items: center;
1312
- cursor: pointer;
1313
- img {
1314
- mix-blend-mode: difference;
1315
- display: inline-block;
1316
- width: 14px;
1317
- }
1318
- .voice-text {
1319
- display: inline-block;
1320
- }
1321
- span {
1322
- margin-left: 8px;
1323
- }
1324
- }
1325
- }
1326
- .has-dictionary {
1327
- padding: 0;
1328
- text-align: left;
1329
- background: unset;
1330
- }
1331
- .content {
1332
- padding: 10px;
1333
- background: #ebebeb;
1334
- border-radius: 0px 8px 8px 8px;
1335
- max-width: 500px;
1336
- margin: 0px;
1337
- line-height: 20px;
1338
- font-size: 14px;
1339
- color: #000;
1340
- text-align: left;
1341
- word-break: break-word;
1342
- /deep/ p {
1343
- margin: 0;
1344
- }
1345
- }
1346
- .upload-image {
1347
- margin-left: 8px;
1348
- }
1349
- &.right {
1350
- flex-direction: row-reverse;
1351
- .right-time {
1352
- left: unset;
1353
- right: 10px;
1354
- }
1355
- .content-wrap {
1356
- text-align: right;
1357
- .robot-item {
1358
- text-align: right;
1359
- }
1360
- .voice-bg {
1361
- flex-direction: row-reverse;
1362
- align-self: flex-end;
1363
- img {
1364
- transform: rotateY(180deg);
1365
- }
1366
- span {
1367
- margin-left: 0;
1368
- margin-right: 8px;
1369
- text-align: right;
1370
- }
1371
- }
1372
- }
1373
- .content-wrap .content {
1374
- background-color: #e7f0ff;
1375
- border-radius: 8px 0px 8px 8px;
1376
- border: 1px solid #e7f0ff;
1377
- box-sizing: border-box;
1378
- }
1379
- .upload-image {
1380
- margin-right: 8px;
1381
- }
1382
- }
1383
- }
1384
- .bot-message {
1385
- display: block;
1386
- text-decoration: underline;
1387
- color: #5585f5;
1388
- cursor: pointer;
1389
- }
1390
- .session-end {
1391
- display: flex;
1392
- justify-content: center;
1393
- margin-top: 15px;
1394
- margin-bottom: 25px;
1395
- .end {
1396
- display: flex;
1397
- justify-content: center;
1398
- width: 30%;
1399
- height: 36px;
1400
- line-height: 36px;
1401
- background-color: #e8e8e8;
1402
- color: #939393;
1403
- font-size: 14px;
1404
- border-radius: 18px;
1405
- }
1406
- }
1407
- .menu-tip {
1408
- font-size: 20px;
1409
- align-self: flex-end;
1410
- margin-bottom: 8px;
1411
- background: #f6f6f6;
1412
- height: 25px;
1413
- line-height: 25px;
1414
- width: 30px;
1415
- border-radius: 4px;
1416
- }
1417
- .menu-item {
1418
- :hover {
1419
- background: red;
1420
- }
1421
- }
1422
- .menu-main {
1423
- width: 100px;
1424
- background-color: #101627;
1425
- color: #fff;
1426
- text-align: left;
1427
- border-radius: 6px;
1428
- padding: 10px 0;
1429
- z-index: 3000;
1430
- cursor: pointer;
1431
- .menu-item {
1432
- padding: 0 10px;
1433
- }
1434
- .menu-item:hover {
1435
- background-color: #efefef;
1436
- color: #000;
1437
- }
1438
- }
1439
- .first-name {
1440
- width: 40px;
1441
- height: 40px;
1442
- line-height: 40px;
1443
- text-align: center;
1444
- background: #5585f5;
1445
- color: #fff;
1446
- border-radius: 100%;
1447
- font-size: 15px;
1448
- }
1449
- .msg-avatar {
1450
- width: 40px;
1451
- height: 40px;
1452
- border-radius: 50%;
1453
-
1454
- object-fit: cover;
1455
- background: #fff;
1456
- }
1457
- /deep/ .ant-spin-spinning {
1458
- display: flex;
1459
- align-items: center;
1460
- }
1461
- .system-msg {
1462
- margin: 10px 0;
1463
- text-align: left;
1464
- line-height: 20px;
1465
- word-wrap: break-word;
1466
- display: flex;
1467
- justify-content: center;
1468
- .system-icon {
1469
- font-size: 18px;
1470
- color: #ffc200;
1471
- vertical-align: middle;
1472
- margin-right: 10px;
1473
- margin-top: 1px;
1474
- }
1475
- .system-btn {
1476
- color: @primary-color;
1477
- margin-left: 8px;
1478
- cursor: pointer;
1479
- text-decoration: underline;
1480
- }
1481
- }
1482
- }
1483
- /deep/ p {
1484
- margin-bottom: 0;
1485
- }
1486
- </style>
1
+ <template>
2
+ <div class="message-wrapper">
3
+ <chat-header :hideHeader="hideHeader" @openPortraitPanel="handleOpenPortraitPanel" v-if="isServer && enable"></chat-header>
4
+ <a-layout-content>
5
+ <slot></slot>
6
+ <div v-if="showBroadcast" class="broadcast-wrap">
7
+ <div v-if="broadcastCarousel" class="carousel-item" :class="{ 'has-link': assemblySetting.broadcast.PC.list[0].url }" @click="goToBroadcastLink(assemblySetting.broadcast.PC.list[0])">
8
+ <a-icon type="sound" />
9
+ <span class="ellips">
10
+ {{ assemblySetting.broadcast.PC.list[0].content }}
11
+ </span>
12
+ </div>
13
+ <template v-else>
14
+ <a-carousel dotPosition="right" autoplay :dots="false">
15
+ <div class="carousel-item" v-for="(item, i) in assemblySetting.broadcast.PC.list" :key="i" :class="{ 'has-link': item.url }" @click="goToBroadcastLink(item)">
16
+ <a-icon type="sound" />
17
+ <span class="ellips">{{ item.content }}</span>
18
+ </div>
19
+ </a-carousel>
20
+ </template>
21
+ </div>
22
+ <div v-if="sessionHistoryList.length > 1 && !(!onChating && currentTab === 'end') && !sessionEnd" style="position: absolute; top: 0; left: 20px; z-index: 1">
23
+ <a-radio-group :value="selectedSession" @change="handleSessionIdChange" buttonStyle="solid">
24
+ <a-radio-button :value="item.id + '-' + item.fromId" v-for="item in sessionHistoryList" :key="item.id + item.fromId">{{ item.fromName }}</a-radio-button>
25
+ </a-radio-group>
26
+ </div>
27
+ <div class="online-message" ref="message-wrapper" v-infinite-scroll="handleInfiniteOnLoad" @scroll="scrollEvent">
28
+ <template v-for="(item, index) in msgList">
29
+ <div :key="index" v-if="item">
30
+ <p v-if="isNeedShowTime(item, index)" class="message-time">
31
+ {{ formatDate(item.createdTime) }}
32
+ </p>
33
+ <div v-if="item.recallFlag" style="margin: 10px 0">
34
+ <template v-if="handleRecallFlag(item)">
35
+ {{ getRecallText(item, isServer) }}
36
+ <a style="text-decoration: underline" @click="reEditMessage(item)" v-if="[0, 4].includes(item.content && item.content.type) && onChating && isServer">,{{ i18nText('1.9.368') }}</a>
37
+ </template>
38
+ <template v-else>
39
+ {{ getRecallText(item) || `${item.fromName} ${i18nText('1.9.365')}` }}
40
+ </template>
41
+ </div>
42
+ <!-- 系统消息 -->
43
+ <div class="system-msg" v-else-if="isSystemMsg(item)">
44
+ <a-icon class="system-icon" theme="filled" type="exclamation-circle" />
45
+ <div>
46
+ <span v-html="getSystemMsgContent(item)"></span>
47
+ <span class="system-btn" v-for="(btn, index) in getTemplateButton(item)" @click="handleTemplateButton(btn)" :key="index">{{ btn.title }}</span>
48
+ </div>
49
+ </div>
50
+ <div
51
+ v-else-if="!item.recallFlag"
52
+ class="message-item"
53
+ :class="[getMessageItemClass(item), { 'read-status': enableReadRecord && item.readStatus }]"
54
+ :data-rid="item.readStatus === 'N' ? item.id : ''"
55
+ >
56
+ <img v-if="!isServer && item.fromPortrait" class="msg-avatar" :src="item.fromPortrait" />
57
+
58
+ <div v-else-if="!isServer && item.fromName" class="first-name">
59
+ {{ item.fromName && item.fromName.slice(0, 2) }}
60
+ </div>
61
+ <img v-else-if="!isServer" class="msg-avatar" :src="getAvatar(item)" width="40" height="40" />
62
+
63
+ <template v-else>
64
+ <img :src="customerStaffIcon" class="msg-avatar" width="40" height="40" v-if="item.fromId === 'SYSTEM'" />
65
+
66
+ <img src="../img/system_message.png" class="msg-avatar" width="40" height="40" v-else-if="item.content && item.content.source === 1" />
67
+
68
+ <img v-else-if="item.portrait" class="msg-avatar" :src="item.portrait" />
69
+
70
+ <div v-else class="first-name">
71
+ {{ item.fromName && item.fromName.slice(0, 2) }}
72
+ </div>
73
+ </template>
74
+ <div @contextmenu="handleContextmenu($event, item)" style="display: flex">
75
+ <a-spin v-if="item.sending">
76
+ <a-icon slot="indicator" type="loading" style="font-size: 24px" spin />
77
+ </a-spin>
78
+ <a-popconfirm @confirm="handleSendFail(item)" :okText="i18nText('1.1.1.1.3')" :cancelText="i18nText('2.7.1.14')">
79
+ <a-icon type="exclamation-circle" slot="icon" />
80
+ <div slot="title">
81
+ <span>确认重新发送该消息?</span>
82
+ </div>
83
+ <a-icon v-if="item.fail" theme="filled" type="exclamation-circle" style="font-size: 16px; display: flex; align-items: center; color: red" />
84
+ </a-popconfirm>
85
+ <div class="content-wrap" v-if="item.content.type === 0">
86
+ <span class="right-time">
87
+ {{ formatDate(item.createdTime || item.sendTime, true) }}
88
+ </span>
89
+ <div
90
+ :class="{
91
+ content: true,
92
+ 'has-dictionary': item.dictionaryValues && item.dictionaryValues.length,
93
+ 'has-intention': item.intentionList && item.intentionList.length
94
+ }"
95
+ >
96
+ <p v-html="getContentHtml(item)"></p>
97
+ <template v-if="item.content.attachments && item.content.attachments.length > 0">
98
+ <div v-for="(v, index) in item.content.attachments" :key="index" class="robot-item">
99
+ <a v-if="v.type == 1" :href="v.url" target="_blank"> {{ v.desc }}(超链) </a>
100
+ <a @click.prevent.stop="$emit('sendQuestion', v, item)" v-if="v.type == 2 || v.type == 3" href="javascript:void(0);">
101
+ {{ v.desc }}
102
+ </a>
103
+ </div>
104
+ </template>
105
+ <div v-if="item.intentionList && item.intentionList.length" class="intention-wrap">
106
+ <div v-for="(v, index) in item.intentionList" :key="index" @click.prevent.stop="$emit('sendIntention', v)">
107
+ <svg-icon :icon-class="v.icon || 'moren'" style="font-size: 26px"></svg-icon>
108
+ <span>{{ v.name }}</span>
109
+ </div>
110
+ </div>
111
+ </div>
112
+ <ul class="dictionary-ul" v-if="item.dictionaryValues && item.dictionaryValues.length">
113
+ <li v-for="(v, i) in item.dictionaryValues" :key="i" @click.prevent.stop="$emit('sendDictionary', v, item)">
114
+ {{ v }}
115
+ </li>
116
+ </ul>
117
+ <div class="client-btn" v-if="item.autoAccess && item.autoAccess === 'false'">
118
+ <svg-icon icon-class="kefu"></svg-icon>
119
+ <span @click="$emit('goCustomServe')">转人工客服</span>
120
+ </div>
121
+ </div>
122
+ <div v-else-if="item.content.type === 1" class="upload-image">
123
+ <MsgPicture style="max-width: 345px" :image="item.content.content" @click="show(item)" />
124
+ </div>
125
+ <div class="content-wrap" v-else-if="item.content.type === 2">
126
+ <span class="right-time">
127
+ {{ formatDate(item.createdTime || item.sendTime, true) }}
128
+ </span>
129
+ <div class="content">
130
+ <img style="width: 16px;margin-right:8px" src="../img/multi-video.png" />
131
+ {{ item.content.content }}&nbsp;
132
+ <span @click.prevent.stop="reCall(item, 'video')" class="re-call" v-if="item.content.isRefuse == 1">
133
+ {{ handleReCall(item) }}
134
+ </span>
135
+ </div>
136
+ </div>
137
+ <div class="content-wrap" v-else-if="item.content.type === 5">
138
+ <span class="right-time">
139
+ {{ formatDate(item.createdTime || item.sendTime, true) }}
140
+ </span>
141
+ <div class="content">
142
+ <img style="width: 16px;margin-right:8px" src="../img/multi-voice.png" />
143
+ {{ item.content.content }}&nbsp;
144
+ <span @click.prevent.stop="reCall(item, 'voice')" class="re-call" v-if="item.content.isRefuse == 1">
145
+ {{ handleReCall(item) }}
146
+ </span>
147
+ </div>
148
+ </div>
149
+ <div class="content-wrap" v-else-if="item.content.type === 6">
150
+ <span class="right-time">
151
+ {{ formatDate(item.createdTime || item.sendTime, true) }}
152
+ </span>
153
+ <div class="voice-bg content" @click.prevent.stop="playAudio(item)">
154
+ <img v-if="item.content.isPlay" src="../img/audio-play.gif" alt="" />
155
+ <img v-else src="../img/audio-new.png" alt="" />
156
+ <span class="voice-text" :style="AudioStyle(item)"> {{ item.content.duration || 1 }}" </span>
157
+ </div>
158
+ </div>
159
+ <template v-else-if="item.content.type === 4">
160
+ <msg-describe v-if="isDescribeMsg(item)" :item="item" />
161
+ <div v-else class="content-wrap">
162
+ <span class="right-time">
163
+ {{ formatDate(item.createdTime || item.sendTime, true) }}
164
+ </span>
165
+ <MsgPrescription :data="item" :isServer="isServer" :theme="isServer ? '' : 'customer'" @handleTemplateDetail="handleTemplateDetail" @handleTemplateButton="handleTemplateButton" />
166
+ </div>
167
+ </template>
168
+ </div>
169
+ <ReadStatus v-if="enableReadRecord" :status="item.readStatus" @click="openReadRecord($event, item)" />
170
+ </div>
171
+ </div>
172
+ </template>
173
+ <ul class="menu-main" ref="contextMenu" :style="contextMenuStyle" v-if="contextMenuShow">
174
+ <template v-for="(item, index) in rightClickSetting">
175
+ <li :key="index" class="menu-item" @click="() => handleRightClick(contextMenuItem, item)">
176
+ {{ item.rightClickName }}
177
+ </li>
178
+ </template>
179
+ </ul>
180
+ <div class="session-end" v-if="sessionEnd && !isServer">
181
+ <span class="end">{{ i18nText('1.9.370') }}</span>
182
+ </div>
183
+ <a-spin v-if="loading" class="loading-icon" />
184
+ </div>
185
+ <!-- 图片预览 -->
186
+ <viewer class="viewer" ref="viewer" :images="images" @inited="inited" :options="options" style="display: none">
187
+ <template slot-scope="scope">
188
+ <img v-for="src in scope.images" :src="src" :key="src" />
189
+ </template>
190
+ </viewer>
191
+ <!-- 卡片详情 -->
192
+ <a-modal
193
+ wrapClassName="chat-footer-modal-small"
194
+ :title="modalData.title || i18nText('1.1.1.10')"
195
+ :visible="modalShow"
196
+ :maskClosable="false"
197
+ :mask="true"
198
+ width="80%"
199
+ destroyOnClose
200
+ :okText="i18nText('1.2.1.11.6')"
201
+ :footer="null"
202
+ @cancel="handleClose"
203
+ >
204
+ <iframe id="chat-footer-modal" v-if="modalData.targetType === 'LINK_ADDRESS'" width="100%" height="100%" frameborder="0" :src="modalData.address" allow="camera;midi"></iframe>
205
+ </a-modal>
206
+ </a-layout-content>
207
+ <audio style="display: none" controls autoplay ref="audio" preload="auto" crossOrigin="anonymous" media-player="audioPlayer" :src="curAudioUrl" @ended="audioEnd"></audio>
208
+ <ReadRecord v-if="readRecordVisible" :style="readRecordStyle" :list="readRecordList" @close="closeReadRecord" />
209
+ </div>
210
+ </template>
211
+
212
+ <script>
213
+ import { Layout, Modal, Icon, Spin, Popconfirm, Carousel, Radio } from 'ant-design-vue';
214
+ import SvgIcon from '@/component/svg/index.vue';
215
+ import InfiniteScroll from '@/directive/scroll';
216
+ import { mapGetters, mapMutations, mapActions } from '../store/helper';
217
+ import fetch, { qs } from '@/utils/chatFetch';
218
+ import MsgDescribe from '../components/msg-describe';
219
+ import MsgPicture from '../components/msg-picture';
220
+ import MsgPrescription from '../components/msg-prescription';
221
+ import ReadStatus from '../components/read-status';
222
+ import ReadRecord from '../components/read-record';
223
+ import chatHeader from './chatHeader';
224
+ import viewerOptions from './mixins/viewerOptions';
225
+ import { getRecallText } from '../utils';
226
+ import vexutils from '@/utils/vexutils';
227
+ import ObserverScroll from '../utils/observer-scroll';
228
+
229
+ const customerStaffIcon = require('../img/customer_staff.png');
230
+ export default {
231
+ mixins: [viewerOptions],
232
+ inject: ['store', 'dispatchEvent', 'registerEvent', 'unregisterEvent', 'i18nText'],
233
+ props: {
234
+ hideHeader: {
235
+ type: Boolean,
236
+ default: false
237
+ },
238
+ robotAvatar: {
239
+ type: String,
240
+ default: ''
241
+ },
242
+ curChatType: {
243
+ type: String
244
+ },
245
+ isShowPortraitPanel: {
246
+ type: Boolean
247
+ },
248
+ activatedTime: {
249
+ type: Number
250
+ }
251
+ },
252
+ data() {
253
+ return {
254
+ loading: false,
255
+ // 卡片详情
256
+ modalShow: false,
257
+ modalData: {},
258
+ // 客户头像
259
+ // 右键操作
260
+ contextMenuStyle: {
261
+ position: 'fixed',
262
+ top: 0,
263
+ left: 0
264
+ },
265
+ contextMenuShow: false,
266
+ contextMenuItem: null,
267
+ // 查看历史消息滚动定位
268
+ scrollHeight: 0,
269
+ curAudioUrl: '',
270
+ lastAudioItem: null,
271
+ readRecordStyle: '',
272
+ readRecordVisible: false,
273
+ readRecordList: []
274
+ };
275
+ },
276
+ computed: {
277
+ ...mapGetters([
278
+ 'userInfo',
279
+ 'currentTab',
280
+ 'isAppendMsg',
281
+ 'message',
282
+ 'msgList',
283
+ 'sessionId',
284
+ 'serviceId',
285
+ 'scrollTo',
286
+ 'isServer',
287
+ 'clientId',
288
+ 'sessionEnd',
289
+ 'assemblyId',
290
+ 'footerMessage',
291
+ 'portraitPanelParams',
292
+ 'assemblySetting',
293
+ 'clientParams',
294
+ 'onChating',
295
+ 'classify',
296
+ 'collapsed',
297
+ 'barStatus',
298
+ 'sessionHistoryList',
299
+ 'enable',
300
+ 'appendList',
301
+ 'queueItem',
302
+ 'isRecorderVoice',
303
+ 'callTimer',
304
+ 'requestId'
305
+ ]),
306
+ orgId() {
307
+ const getters = this.store.getters;
308
+ return this.isServer ? this.userInfo?.sysParams?.orgId : getters.accessParams.orgId || getters.queryParams.orgId;
309
+ },
310
+ userId() {
311
+ const getters = this.store.getters;
312
+ return this.isServer ? this.userInfo?.sysParams?.userId : getters.accessParams.userId || getters.chatUserId || getters.queryParams.userId;
313
+ },
314
+ enableReadRecord() {
315
+ return this.assemblySetting?.readStatus === 'Y';
316
+ },
317
+ customerStaffIcon() {
318
+ return this.robotAvatar || customerStaffIcon;
319
+ },
320
+ selectedSession() {
321
+ let sessionMap = this.sessionHistoryList.find(item => item.fromId === this.serviceId);
322
+ if (sessionMap) {
323
+ return sessionMap.id + '-' + sessionMap.fromId;
324
+ }
325
+ return '';
326
+ },
327
+ isListClassify() {
328
+ return this.assemblySetting.isListClassify === 'Y';
329
+ },
330
+ showPanel() {
331
+ return this.assemblySetting.portraitPanelSetting?.targetIs;
332
+ },
333
+ showBroadcast() {
334
+ let res = (!this.isServer && this.assemblySetting?.broadcast?.PC?.isChecked == 'Y' && this.assemblySetting?.broadcast?.PC?.list?.length > 0) || false;
335
+ return res;
336
+ },
337
+ broadcastCarousel() {
338
+ return (this.assemblySetting.broadcast && this.assemblySetting.broadcast.PC.list.length == 1) || false;
339
+ },
340
+ rightClickSetting() {
341
+ const { isServer } = this;
342
+ if (!isServer) return [];
343
+ const isSelf = this.contextMenuItem.fromId === this.serviceId;
344
+ const setting = this.assemblySetting?.rightClickSetting || [];
345
+ const doArr = ['resend', 'rollBack'];
346
+ return setting.filter(item => {
347
+ const { rightClickDo } = item;
348
+ return isSelf || doArr.indexOf(rightClickDo) < 0;
349
+ });
350
+ },
351
+ handleReCall() {
352
+ return function(item) {
353
+ if (item.content?.source === 1) {
354
+ return '[点击回拨]';
355
+ } else if (item.senderFlag == 1) {
356
+ return `[点击${this.isServer ? '回拨' : '重拨'}]`;
357
+ }
358
+ if (item.senderFlag == 2) {
359
+ return `[点击${this.isServer ? '重拨' : '回拨'}]`;
360
+ }
361
+ if (this.isServer) {
362
+ let isRight = item.fromId !== 'SYSTEM' && item.fromId && item.fromId === this.serviceId;
363
+ return `[点击${isRight ? '重拨' : '回拨'}]`;
364
+ } else {
365
+ let isRight = (item.fromId !== 'SYSTEM' && item.toId === 'SYSTEM') || item.fromId === this.clientId || (item.mode === 'bot' && !item.fromId);
366
+ return `[点击${isRight ? '重拨' : '回拨'}]`;
367
+ }
368
+ };
369
+ },
370
+ handleRecallFlag() {
371
+ return function(item) {
372
+ return this.serviceId == item.fromId;
373
+ };
374
+ },
375
+ AudioStyle() {
376
+ return function(item) {
377
+ let { duration = 1 } = item.content;
378
+ if (!duration) {
379
+ duration = 1;
380
+ }
381
+ let curWidth = (+duration / 60) * 400;
382
+ return {
383
+ width: curWidth < 13 ? 13 : curWidth + 'px'
384
+ };
385
+ };
386
+ },
387
+ showTimeDuration() {
388
+ let { variableSetting = null } = this.assemblySetting || {};
389
+ if (!variableSetting) return 3;
390
+ let matchItem = variableSetting.find(v => v.name === 'CHAT_SEPARATION_TIME');
391
+ if (!matchItem) return 3;
392
+ if (!+matchItem.value) return 3;
393
+ return +matchItem.value;
394
+ },
395
+ images() {
396
+ return this.msgList.filter(v => !v.recallFlag && v.content.type == 1)?.map(v => v.content?.content);
397
+ }
398
+ },
399
+ created() {
400
+ this.attachEvent();
401
+ this.getUserInformation();
402
+ },
403
+ methods: {
404
+ ...mapMutations([
405
+ 'setIsAppendMsg',
406
+ 'setMsgList',
407
+ 'setScrollTo',
408
+ 'setFooterMessage',
409
+ 'setIsScrollListChange',
410
+ 'setListChangeItem',
411
+ 'setSessionId',
412
+ 'setOnChating',
413
+ 'setServiceId',
414
+ 'setShowAudio',
415
+ 'setVideoMode',
416
+ 'setVideoMembers',
417
+ 'setIsRecorderVoice',
418
+ 'setDoctorInfo',
419
+ 'setCallTimer'
420
+ ]),
421
+ ...mapActions({
422
+ getEarlierMsg: 'setMsgList',
423
+ handleBotChat: 'handleBotChat',
424
+ sendMessage: 'sendMessage'
425
+ }),
426
+ // 获取医生信息
427
+ getUserInformation() {
428
+ const url = '/user/getUserInformation';
429
+ fetch.get(url).then(({ data }) => {
430
+ if (data.result === 'SUCCESS') {
431
+ this.setDoctorInfo(data?.map?.sysUserInformation)
432
+ console.log('data?.map?.sysUserInformation', data?.map?.sysUserInformation);
433
+ }
434
+ });
435
+ },
436
+ getMessageItemClass(item) {
437
+ // content.source === 1, 代表为系统消息, 系统统一左侧展示
438
+ if (item.content?.source === 1) {
439
+ return {
440
+ left: true
441
+ };
442
+ }
443
+
444
+ // 服务端 senderFlag 1 左 2 右
445
+ // 客户端 senderFlag 1 右 2 左
446
+ if (item.content?.source === 1) {
447
+ return {
448
+ left: true
449
+ };
450
+ } else if (item.senderFlag == 1) {
451
+ if (this.isServer) {
452
+ return {
453
+ left: true
454
+ };
455
+ } else {
456
+ return {
457
+ right: true
458
+ };
459
+ }
460
+ }
461
+ if (item.senderFlag == 2) {
462
+ if (this.isServer) {
463
+ return {
464
+ right: true
465
+ };
466
+ } else {
467
+ return {
468
+ left: true
469
+ };
470
+ }
471
+ }
472
+ if (this.isServer) {
473
+ return {
474
+ right: item.fromId !== 'SYSTEM' && item.fromId && item.fromId === this.serviceId,
475
+ left: item.fromId === 'SYSTEM' || item.fromId === this.clientId || item.content?.source === 1 || !item.fromId
476
+ };
477
+ } else {
478
+ let isRight, isLeft;
479
+ if (this.curChatType == 'robot') {
480
+ isRight = item.fromId !== 'SYSTEM';
481
+ isLeft = item.fromId === 'SYSTEM';
482
+ } else {
483
+ isRight = (item.fromId !== 'SYSTEM' && item.toId === 'SYSTEM') || item.fromId === this.clientId || (item.mode === 'bot' && !item.fromId);
484
+ isLeft = item.fromId === 'SYSTEM' || item.fromId !== this.clientId || item.content?.source === 1;
485
+ }
486
+ return {
487
+ right: isRight,
488
+ left: isLeft
489
+ };
490
+ }
491
+ },
492
+ /* 向上滚动刷新 */
493
+ handleInfiniteOnLoad() {
494
+ if (this.curChatType === 'robot') return;
495
+ this.loading = true;
496
+ this.getEarlierMsg().then(msg => {
497
+ if (msg) {
498
+ this.$message.info(msg);
499
+ }
500
+ this.loading = false;
501
+ });
502
+ },
503
+ show(item) {
504
+ const src = item.content.content;
505
+ // this.images = vexutils.imgs2imgArr(src);
506
+ this.$viewer.view(this.images.findIndex(v => v === src));
507
+ this.$viewer.show();
508
+ this.readSingleMsg(item);
509
+ },
510
+ /* 滚动条定位 */
511
+ handleScrollTo(scrollTo) {
512
+ let target = this.$refs['message-wrapper'];
513
+ this.$nextTick().then(() => {
514
+ if (!target) return;
515
+ let top = target.scrollHeight;
516
+ if (scrollTo === 'current') {
517
+ top = target.scrollHeight - this.scrollHeight;
518
+ }
519
+ if (typeof scrollTo === 'number') {
520
+ top = scrollTo;
521
+ }
522
+ target.scrollTo({
523
+ left: 0,
524
+ top,
525
+ behavior: 'instant'
526
+ });
527
+ this.scrollHeight = 0;
528
+ });
529
+ this.setScrollTo('');
530
+ },
531
+
532
+ /**
533
+ * 是否需要显示时间
534
+ */
535
+ isNeedShowTime(item, index) {
536
+ const curMsgTime = item.createdTime || new Date().getTime();
537
+ let duration = 60 * 1000 * this.showTimeDuration;
538
+ let lastMsgTime;
539
+ if (index == 0) {
540
+ lastMsgTime = 0;
541
+ } else {
542
+ lastMsgTime = this.msgList[index - 1].createdTime;
543
+ }
544
+ if (Number(curMsgTime) - Number(lastMsgTime) > duration) {
545
+ return true;
546
+ } else {
547
+ return false;
548
+ }
549
+ },
550
+ formatDate(date) {
551
+ return vexutils.formatDate(date, true);
552
+ },
553
+ getAvatar(item) {
554
+ if (item.content && item.content.source === 1) {
555
+ return require('../img/system_message.png');
556
+ }
557
+ if (item.mode === 'bot' && !item.fromId) {
558
+ return require('../img/default.png');
559
+ }
560
+ if (this.isServer) {
561
+ if (item.fromId === this.clientId || !item.fromId) {
562
+ return require('../img/default.png');
563
+ } else {
564
+ return this.customerStaffIcon;
565
+ }
566
+ } else {
567
+ if (this.curChatType == 'robot') {
568
+ if (item.fromId === 'SYSTEM') {
569
+ return this.customerStaffIcon;
570
+ } else {
571
+ return require('../img/default.png');
572
+ }
573
+ } else {
574
+ if (item.fromId !== this.clientId) {
575
+ return this.customerStaffIcon;
576
+ } else {
577
+ return require('../img/default.png');
578
+ }
579
+ }
580
+ }
581
+ },
582
+ getContent(item) {
583
+ let content;
584
+ if (vexutils.isJSON(item.content.content)) {
585
+ content = JSON.parse(item.content.content);
586
+ } else {
587
+ content = item.content.content;
588
+ }
589
+ return content;
590
+ },
591
+ getContentHtml(item) {
592
+ return !~item.content.content.indexOf('emoji') ? this.$xss(item.content.content) : item.content.content;
593
+ },
594
+ getTemplateTitle(item) {
595
+ return this.getContent(item)?.title;
596
+ },
597
+ showArrow(item) {
598
+ let setting = this.getContent(item)?.customerSetting || {};
599
+ if (this.isServer) {
600
+ setting = this.getContent(item)?.serverSetting || {};
601
+ }
602
+ let { address } = setting;
603
+ if (!address) return false;
604
+ return true;
605
+ },
606
+ getTemplateTitleIcon(item) {
607
+ return this.getContent(item)?.icon;
608
+ },
609
+ getTemplateContent(item) {
610
+ let content = this.getContent(item);
611
+ let array = [];
612
+ try {
613
+ array = content.content.split('##');
614
+ } catch {
615
+ console.log(content, 'error');
616
+ }
617
+ array = array.map(item => `<div>${item}</div>`);
618
+ return array.join('');
619
+ },
620
+ getSystemMsgContent(item) {
621
+ let content = this.getContent(item);
622
+ let array = [];
623
+ try {
624
+ array = content.content.split('##');
625
+ } catch {
626
+ console.log(content, 'error');
627
+ }
628
+ return array.join('<br>');
629
+ },
630
+ getTemplateButton(item) {
631
+ let content = this.getContent(item);
632
+ const setting = this.isServer ? content.serverSetting : content.customerSetting;
633
+ return setting?.toolbar_button || [];
634
+ },
635
+ // 处理模板按钮,详情等跳转
636
+ handleTemplate(data, dispatch = true) {
637
+ let { targetType, address, params = [], openMode } = data;
638
+ dispatch &&
639
+ this.dispatchEvent('click_templateCard', {
640
+ ...data
641
+ });
642
+ if (!address) return;
643
+ if (targetType === 'LINK_ADDRESS') {
644
+ let urlParams = [];
645
+ params.forEach(({ p_name, p_value }) => {
646
+ urlParams.push(`${p_name}=${p_value}`);
647
+ });
648
+ if (address.includes('?')) {
649
+ address += `&${urlParams.join('&')}`;
650
+ } else {
651
+ address += `?${urlParams.join('&')}`;
652
+ }
653
+ if (openMode === 'WINDOW') {
654
+ window.open(address);
655
+ return;
656
+ }
657
+ if (openMode === 'EJECT') {
658
+ // 打开云his页面需要从cookie内获取最新的jsessionids
659
+ if (address.includes('jsessionidsFlag=1')) {
660
+ let jsessionids = vexutils.cookie.get('jsessionids')
661
+ address = address + `&jsessionids=${jsessionids}`
662
+ }
663
+ this.modalShow = true;
664
+ this.modalData = Object.assign({}, data, {
665
+ address
666
+ });
667
+ this.$nextTick().then(() => {
668
+ window.addEventListener('message', this.iframeEvent);
669
+ });
670
+ return;
671
+ }
672
+ }
673
+ },
674
+ iframeEvent(event) {
675
+ const method = event?.data?.method;
676
+ this[method]?.(event);
677
+ },
678
+ // 发送
679
+ handleParentMessageSend({ data, source, origin }) {
680
+ let params = {
681
+ assemblyId: this.assemblyId,
682
+ sessionId: this.sessionId,
683
+ orgId: this.orgId
684
+ };
685
+ params = Object.assign(params, data.data);
686
+ return fetch.post('/chat/access/sendToolBarData', qs.stringify(params)).then(({ data }) => {
687
+ if (data.result === 'SUCCESS') {
688
+ source.postMessage({ status: 0, resultMsg: data.resultMsg }, origin);
689
+ } else {
690
+ source.postMessage({ status: 1, resultMsg: data.resultMsg }, origin);
691
+ }
692
+ });
693
+ },
694
+ // 关闭
695
+ handleClose() {
696
+ this.modalShow = false;
697
+ window.removeEventListener('message', this.iframeEvent);
698
+ },
699
+ handleTemplateDetail(item) {
700
+ let content = this.getContent(item) || {};
701
+ let setting = content.customerSetting || {};
702
+ if (this.isServer) {
703
+ setting = content.serverSetting || {};
704
+ }
705
+ this.readSingleMsg(item);
706
+ this.handleTemplate(setting);
707
+ },
708
+ handleTemplateButton(btn) {
709
+ this.handleTemplate(btn);
710
+ },
711
+ // 消息撤回
712
+ handleRecall(item) {
713
+ let params = {
714
+ assemblyId: this.assemblyId,
715
+ sessionId: this.sessionId,
716
+ messageId: item.id,
717
+ messageType: item.content?.type,
718
+ userId: this.userInfo?.sysParams?.userId || ''
719
+ };
720
+
721
+ fetch.post('/chat/service/recallMessage', qs.stringify(params)).then(({ data }) => {
722
+ if (data.result === 'SUCCESS') {
723
+ this.dispatchEvent('msg_recallSuccess', {
724
+ message: { ...item },
725
+ ...params
726
+ });
727
+ this.$set(item, 'recallFlag', 1);
728
+ // 撤回成功提示
729
+ let title = this.getContent(item)?.title;
730
+ let text = title ? `已撤回[${title}]` : '已撤回';
731
+ this.$message.success(text);
732
+
733
+ if (!this.isLastMessage(item.id)) return;
734
+ this.setIsScrollListChange(true);
735
+ this.setListChangeItem({
736
+ opt: 1,
737
+ from: this.clientId,
738
+ sessionId: this.sessionId,
739
+ msgCount: 0,
740
+ lastContentRecallFlag: 1,
741
+ lastContent: vexutils.clone(item.content, true),
742
+ lastTime: new Date().getTime()
743
+ });
744
+ } else {
745
+ this.$message.error(data.resultMsg);
746
+ }
747
+ });
748
+ },
749
+ handleJumpUrl(contextMenuItem, item) {
750
+ const title = item.rightClickName;
751
+ const url = item.rightClickUrl.replace(/{(.*?)}/g, (match, p) => {
752
+ return eval(p);
753
+ });
754
+ this.dispatchEvent('open_jumpUrl', {
755
+ title: title,
756
+ url: url
757
+ });
758
+ },
759
+ // 右键菜单功能
760
+ handleRightClick(contextMenuItem, item) {
761
+ if (item.rightClickDo === 'rollBack') {
762
+ this.handleRecall(contextMenuItem);
763
+ } else if (item.rightClickDo === 'jumpUrl') {
764
+ this.handleJumpUrl(contextMenuItem, item);
765
+ } else if (item.rightClickDo === 'resend') {
766
+ this.resendMessage(contextMenuItem);
767
+ }
768
+ },
769
+ resendMessage(item) {
770
+ fetch({
771
+ method: 'POST',
772
+ url: '/chat/service/resendMessage',
773
+ data: qs.stringify({
774
+ assemblyId: this.assemblyId,
775
+ sessionId: this.sessionId,
776
+ messageId: item.id
777
+ })
778
+ });
779
+ },
780
+ // 重新编辑
781
+ reEditMessage(item) {
782
+ if (item.content?.type === 4) {
783
+ let content = this.getContent(item);
784
+ this.handleTemplate(content.serverSetting.reEditSetting, false);
785
+ return;
786
+ }
787
+ this.setFooterMessage(this.footerMessage + item.content.content.slice(5, -6));
788
+ },
789
+ observeMsg() {
790
+ if (this.enableReadRecord) {
791
+ if (!this._observer) {
792
+ this._observer = new ObserverScroll(this.$refs['message-wrapper'], this.readMsg);
793
+ this.$on('hook:beforeDestroy', () => {
794
+ this._observer.remove();
795
+ this._observer = null;
796
+ });
797
+ }
798
+ this._observer.disconnect();
799
+ if (this.msgList.length) {
800
+ this.$nextTick(() => {
801
+ const el = this.$refs['message-wrapper'];
802
+ const els = el?.querySelectorAll('.message-item.left');
803
+ els && this._observer.observe(Array.from(els).filter(item => item.getAttribute('data-rid')));
804
+ });
805
+ }
806
+ }
807
+ },
808
+ readMsg(ids, enableVaid = true, notify = true, post = true) {
809
+ const msgList = this.msgList;
810
+ const idKeys = msgList.reduce((obj, item, i) => {
811
+ const id = item?.id;
812
+ if (id) obj[id] = i;
813
+ return obj;
814
+ }, {});
815
+ const data = [];
816
+ const validFn = (item, type) => {
817
+ /**
818
+ * 针对文本、表情消息类型,只要在聊天区域内,视为已读,
819
+ * 图片、语音类型消息,需要点击查看后,才视为已读,
820
+ * 模板 类型消息,如果没有详情链接,只要在聊天区域内,视为已读,若有详情链接,需要点击查看后,才视为已读
821
+ */
822
+ const jumpType = `,6,1,`;
823
+ if (jumpType.includes(type)) return false;
824
+ if (type == 4 && !this.isDescribeMsg(item)) {
825
+ let content = this.getContent(item) || {};
826
+ let setting = (this.isServer ? content.serverSetting : content.customerSetting) || {};
827
+ return !setting?.address;
828
+ }
829
+ return true;
830
+ };
831
+ const value = ids.filter(id => {
832
+ const i = idKeys[id];
833
+ let item = msgList[i];
834
+ if (item) {
835
+ const type = item.content.type;
836
+ if (!enableVaid || validFn(item, type)) {
837
+ item.readStatus = 'Y';
838
+ data.push({ id, type });
839
+ return true;
840
+ }
841
+ return false;
842
+ }
843
+ });
844
+ if (!value.length) return;
845
+ notify &&
846
+ this.dispatchEvent('msg_readed', {
847
+ data,
848
+ assemblyId: this.assemblyId,
849
+ sessionId: this.sessionId
850
+ });
851
+ post && this.postReadMessage({ ids: value.join(',') });
852
+ },
853
+ postReadMessage(data) {
854
+ fetch.post(
855
+ `/chat/${this.isServer ? 'service' : 'access'}/readMessage`,
856
+ qs.stringify({
857
+ assemblyId: this.assemblyId,
858
+ orgId: this.orgId,
859
+ userId: this.userId,
860
+ sessionId: this.sessionId,
861
+ ...data
862
+ })
863
+ );
864
+ },
865
+ readSingleMsg(item) {
866
+ if (this.enableReadRecord && item.readStatus === 'N') {
867
+ const cls = this.getMessageItemClass(item);
868
+ if (!cls?.left) return;
869
+ this.readMsg([item.id], false);
870
+ }
871
+ },
872
+ closeReadRecord() {
873
+ this.readRecordVisible = false;
874
+ this.readRecordList = [];
875
+ },
876
+ openReadRecord(event, item) {
877
+ const readStatus = item.readStatus;
878
+ if (readStatus) {
879
+ const cls = this.getMessageItemClass(item);
880
+ if (!cls.right) return;
881
+ const rect = event.target.getBoundingClientRect();
882
+ const w = 440;
883
+ const h = 340;
884
+ const top = Math.max(0, Math.min(document.body.clientHeight - h, rect.top - h / 2));
885
+ const left = Math.max(0, Math.min(document.body.clientWidth - w, rect.left - w));
886
+ fetch({
887
+ url: `/chat/${this.isServer ? 'service' : 'access'}/getMessageMember`,
888
+ method: 'get',
889
+ params: {
890
+ assemblyId: this.assemblyId,
891
+ messageId: item.id,
892
+ orgId: this.isServer ? undefined : this.orgId
893
+ }
894
+ }).then(res => {
895
+ const list = res.data?.list || [];
896
+ this.readRecordStyle = `top:${top}px;left:${left}px;position:fixed;z-index:10`;
897
+ this.readRecordVisible = list.length > 0;
898
+ this.readRecordList = Object.freeze(list);
899
+ if (item.readStatus && list.length) {
900
+ const unread = list.filter(item => item.readStatus == 'N').length;
901
+ const single = list.length < 2;
902
+ const status = unread > 0 ? (single ? 'N' : `${unread}`) : single ? 'Y' : 'ALL';
903
+ // 检查是否更新人数
904
+ if (item.readStatus != status) {
905
+ item.readStatus = status;
906
+ }
907
+ }
908
+ });
909
+ }
910
+ },
911
+ isLastMessage(id) {
912
+ return this.msgList.findIndex(msg => msg.id === id) === this.msgList.length - 1;
913
+ },
914
+ isSystemMsg(item) {
915
+ return item.content.type == 4 && this.getContent(item).styleId == '2';
916
+ },
917
+ isDescribeMsg(item) {
918
+ return this.getContent(item).styleId == '3';
919
+ },
920
+ async handleOpenPortraitPanel(item) {
921
+ if (!this.showPanel || item.fromId === this.serviceId || !this.isServer || !this.isShowPortraitPanel) return;
922
+ const { portraitPanelParams, clientParams } = this;
923
+ const panelSetting = this.assemblySetting?.portraitPanelSetting;
924
+ this.dispatchEvent('open_portraitPanel', {
925
+ panelSetting,
926
+ clientParams,
927
+ panelParams: portraitPanelParams,
928
+ targetId: panelSetting?.targetId,
929
+ targetType: panelSetting?.targetType
930
+ });
931
+ },
932
+ handleContextmenu(e, item) {
933
+ // 系统消息,客户消息,待接入消息,历史消息不允许撤回
934
+ // 模板-描述消息禁用右键
935
+ if (item.content?.source === 1 || !this.sessionId || (this.sessionId && !this.onChating) || (item.content.type === 4 && this.isDescribeMsg(item))) return;
936
+ this.contextMenuItem = item;
937
+ if (this.rightClickSetting.length < 1) return;
938
+ e.preventDefault();
939
+ let left = e.pageX + 'px';
940
+ let top = e.pageY + 'px';
941
+ this.contextMenuStyle = Object.assign(this.contextMenuStyle, {
942
+ top,
943
+ left
944
+ });
945
+ this.contextMenuShow = true;
946
+ },
947
+ scrollEvent(e) {
948
+ console.log('e.target.scrollTop->', e.target.scrollTop);
949
+ this._scrollTop = e.target.scrollTop;
950
+ },
951
+ /* 事件绑定 */
952
+ attachEvent() {
953
+ this.scrollEvent = vexutils.debounce(this.scrollEvent, 800);
954
+ const handleClick = () => {
955
+ this.contextMenuShow = false;
956
+ };
957
+ const handleRasize = () => {
958
+ if (this.readRecordVisible) {
959
+ let style = this.readRecordStyle;
960
+ const top = style.match(topReg)?.[1];
961
+ const topReg = /top:([\d.]+)/;
962
+ const maxH = document.body.clientHeight - 360;
963
+ if (top && maxH < top) {
964
+ style = style.replace(topReg, `top:${maxH}`);
965
+ }
966
+ const leftReg = /left:([\d.]+)/;
967
+ const left = style.match(leftReg)?.[1];
968
+ const maxW = document.body.clientWidth - 440;
969
+ if (left && maxW < left) {
970
+ style = style.replace(leftReg, `left:${maxW}`);
971
+ }
972
+ this.readRecordStyle = style;
973
+ }
974
+ };
975
+ window.addEventListener('resize', handleRasize);
976
+ document.addEventListener('click', handleClick);
977
+ this.registerEvent('openPortraitPanel', this.handleOpenPortraitPanel);
978
+ const readMessage = data => {
979
+ this.readMsg(data.ids.split(','), false, false, false);
980
+ this.postReadMessage(data);
981
+ };
982
+ this.registerEvent('readMessage', readMessage);
983
+ this.$on('hook:beforeDestroy', () => {
984
+ window.removeEventListener('resize', handleRasize);
985
+ document.removeEventListener('click', handleClick);
986
+ this.unregisterEvent('openPortraitPanel', this.handleOpenPortraitPanel);
987
+ this.unregisterEvent('readMessage', readMessage);
988
+ });
989
+ },
990
+ goToBroadcastLink(item) {
991
+ if (!item.url) return;
992
+ window.open(item.url);
993
+ },
994
+ /**
995
+ * 客服切换
996
+ */
997
+ handleSessionIdChange(e) {
998
+ let [sessionId, serviceId] = e.target.value.split('-');
999
+ this.setSessionId(sessionId);
1000
+ this.setServiceId(serviceId);
1001
+ let isOnChating = this.sessionHistoryList.find(item => item.fromId === serviceId).onChating;
1002
+ this.setOnChating(!!isOnChating);
1003
+ this.setMsgList([]);
1004
+ this.getEarlierMsg();
1005
+ },
1006
+ handleSendFail(item) {
1007
+ this.sendMessage(Object.assign(item.content, { failItem: item }));
1008
+ },
1009
+ reCall(type) {
1010
+ this.setShowAudio(true);
1011
+ this.setVideoMode(type === 'voice' ? 1 : 2);
1012
+ this.$nextTick(() => {
1013
+ this.setVideoMembers([]);
1014
+ });
1015
+ },
1016
+ playAudio(item) {
1017
+ let { content = '' } = item?.content || {};
1018
+ if (!content) return;
1019
+ if (this.lastAudioItem && (item.id !== this.lastAudioItem.id || item.createdTime !== this.lastAudioItem.createdTime)) {
1020
+ this.$set(this.lastAudioItem.content, 'isPlay', false);
1021
+ }
1022
+
1023
+ this.readSingleMsg(item);
1024
+ this.lastAudioItem = item;
1025
+ this.$set(item.content, 'isPlay', !item.content.isPlay);
1026
+ this.$nextTick(() => {
1027
+ this.curAudioUrl = content;
1028
+ let audioRef = this.$refs['audio'];
1029
+ if (!audioRef) return;
1030
+ audioRef.load();
1031
+ setTimeout(() => {
1032
+ if (item.content.isPlay) {
1033
+ let playPromise = audioRef.play();
1034
+ if (playPromise !== undefined) {
1035
+ playPromise.then(() => {
1036
+ audioRef.play();
1037
+ });
1038
+ }
1039
+ } else {
1040
+ audioRef.pause();
1041
+ this.curAudioUrl = '';
1042
+ }
1043
+ });
1044
+ });
1045
+ },
1046
+ audioEnd() {
1047
+ if (!this.lastAudioItem) return;
1048
+ this.$set(this.lastAudioItem.content, 'isPlay', false);
1049
+ },
1050
+ getRecallText(item, flag = false) {
1051
+ return getRecallText(this, item, flag);
1052
+ }
1053
+ },
1054
+ directives: {
1055
+ InfiniteScroll
1056
+ },
1057
+ components: {
1058
+ AModal: Modal,
1059
+ [Layout.Content.name]: Layout.Content,
1060
+ [Spin.name]: Spin,
1061
+ [Icon.name]: Icon,
1062
+ [Popconfirm.name]: Popconfirm,
1063
+ [Radio.Button.name]: Radio.Button,
1064
+ [Radio.Group.name]: Radio.Group,
1065
+ [Carousel.name]: Carousel,
1066
+ SvgIcon,
1067
+ MsgPrescription,
1068
+ ReadStatus,
1069
+ ReadRecord,
1070
+ MsgPicture,
1071
+ MsgDescribe,
1072
+ chatHeader
1073
+ },
1074
+ watch: {
1075
+ msgList() {
1076
+ this.observeMsg();
1077
+ },
1078
+ isAppendMsg: {
1079
+ immediate: true,
1080
+ handler(isAppendMsg) {
1081
+ if (isAppendMsg) {
1082
+ let newArr = [];
1083
+ if (vexutils.isPlainObject(this.message)) {
1084
+ newArr = [...this.msgList, this.message];
1085
+ } else if (vexutils.isArray(this.message)) {
1086
+ newArr = [...this.msgList, ...this.message];
1087
+ }
1088
+ this.setMsgList(newArr);
1089
+ this.setIsAppendMsg(false);
1090
+ this.setScrollTo('bottom');
1091
+ }
1092
+ }
1093
+ },
1094
+ scrollTo: {
1095
+ immediate: true,
1096
+ handler(scrollTo) {
1097
+ if (scrollTo) {
1098
+ this.handleScrollTo(scrollTo);
1099
+ }
1100
+ }
1101
+ },
1102
+ activatedTime: {
1103
+ immediate: true,
1104
+ handler(value) {
1105
+ this.handleScrollTo(this._scrollTop);
1106
+ }
1107
+ },
1108
+ /**
1109
+ * 图片懒加载处理
1110
+ */
1111
+ appendList: {
1112
+ handler() {
1113
+ if (!this.scrollHeight) {
1114
+ let target = this.$refs['message-wrapper'];
1115
+ this.scrollHeight = target.scrollHeight;
1116
+ }
1117
+ if (this.appendList.length === this.msgList.length || this.appendList.length === 1) {
1118
+ this.setScrollTo('bottom');
1119
+ } else {
1120
+ this.setScrollTo('current');
1121
+ }
1122
+ }
1123
+ },
1124
+ isRecorderVoice: {
1125
+ immediate: true,
1126
+ handler(val) {
1127
+ if (!val) return;
1128
+ this.curAudioUrl = '';
1129
+ this.setIsRecorderVoice(false);
1130
+ if (!this.lastAudioItem) return;
1131
+ this.$set(this.lastAudioItem.content, 'isPlay', false);
1132
+ }
1133
+ }
1134
+ }
1135
+ };
1136
+ </script>
1137
+
1138
+ <style lang="less" scoped>
1139
+ @import '../style/emoji.css';
1140
+ @import '../style/message.mixin.less';
1141
+ .message-wrapper {
1142
+ .createMessageStyle(#ebebeb, @primary-color);
1143
+ flex: 1;
1144
+ height: 0;
1145
+ // position: relative;
1146
+ display: flex;
1147
+ flex-direction: column;
1148
+ overflow-x: hidden;
1149
+ .broadcast-wrap {
1150
+ height: 30px;
1151
+ text-align: center;
1152
+ font-size: 14px;
1153
+ background-color: #fffbe8;
1154
+ /deep/ .carousel-item {
1155
+ display: inline-flex !important;
1156
+ justify-content: center;
1157
+ align-items: center;
1158
+ width: 100%;
1159
+ height: 30px;
1160
+ text-align: center;
1161
+ &.has-link {
1162
+ color: #ed6a0c;
1163
+ cursor: pointer;
1164
+ }
1165
+ span {
1166
+ display: inline-block;
1167
+ max-width: calc(100% - 30px);
1168
+ margin-left: 4px;
1169
+ }
1170
+ }
1171
+ }
1172
+
1173
+ .loading-icon {
1174
+ position: absolute;
1175
+ top: 0;
1176
+ left: 50%;
1177
+ }
1178
+ }
1179
+ /deep/ .ant-layout-content {
1180
+ flex: 1;
1181
+ display: flex;
1182
+ flex-direction: column;
1183
+ position: relative;
1184
+ }
1185
+ .online-message {
1186
+ flex: 1;
1187
+ height: 100%;
1188
+ // height: 608px;
1189
+ overflow-y: auto;
1190
+ overflow-x: hidden;
1191
+ padding: 10px 22px;
1192
+ // margin: 10px 0;
1193
+ text-align: center;
1194
+ &::-webkit-scrollbar {
1195
+ width: 5px;
1196
+ }
1197
+ .message-time {
1198
+ display: inline-block;
1199
+ color: #666;
1200
+ font-size: 14px;
1201
+ border-radius: 4px;
1202
+ padding: 4px;
1203
+ margin: 0 auto;
1204
+ margin-bottom: 16px;
1205
+ }
1206
+ > div {
1207
+ margin-bottom: 16px;
1208
+ }
1209
+ .message-item {
1210
+ display: flex;
1211
+ /deep/ .chat-read-status-text {
1212
+ display: none;
1213
+ }
1214
+ &.right.read-status {
1215
+ padding-bottom: 24px;
1216
+ position: relative;
1217
+ /deep/ .chat-read-status-text {
1218
+ display: block;
1219
+ position: absolute;
1220
+ right: 50px;
1221
+ bottom: 0;
1222
+ }
1223
+ }
1224
+ .right-time {
1225
+ display: none;
1226
+ position: absolute;
1227
+ top: -16px;
1228
+ left: 10px;
1229
+ font-size: 12px;
1230
+ color: #b0b1b1;
1231
+ white-space: nowrap;
1232
+ }
1233
+
1234
+ .content-wrap {
1235
+ padding: 0 10px;
1236
+ text-align: left;
1237
+ position: relative;
1238
+ &:hover {
1239
+ .right-time {
1240
+ display: block;
1241
+ }
1242
+ }
1243
+
1244
+ .robot-item {
1245
+ text-align: left;
1246
+ a {
1247
+ text-decoration: underline;
1248
+ }
1249
+ }
1250
+ .intention-wrap {
1251
+ display: flex;
1252
+ flex-wrap: wrap;
1253
+ padding: 8px 0 4px 0;
1254
+ background-color: #fff;
1255
+ border-radius: 0 0 8px 8px;
1256
+ > div {
1257
+ display: inline-flex;
1258
+ flex-direction: column;
1259
+ align-items: center;
1260
+ width: 60px;
1261
+ height: 54px;
1262
+ cursor: pointer;
1263
+
1264
+ &:hover {
1265
+ color: #5585f5;
1266
+ span {
1267
+ color: #5585f5;
1268
+ }
1269
+ }
1270
+ }
1271
+ span {
1272
+ margin-top: 8px;
1273
+ max-width: 90%;
1274
+ text-overflow: ellipsis;
1275
+ overflow: hidden;
1276
+ white-space: nowrap;
1277
+ font-size: 12px;
1278
+ color: #6a6a6a;
1279
+ }
1280
+ }
1281
+ .dictionary-ul {
1282
+ padding-top: 8px;
1283
+ display: flex;
1284
+ flex-wrap: wrap;
1285
+ li {
1286
+ padding: 4px 16px;
1287
+ margin-right: 16px;
1288
+ font-size: 14px;
1289
+ color: #5585f5;
1290
+ background: #efefef;
1291
+ border-radius: 16px;
1292
+ cursor: pointer;
1293
+ }
1294
+ }
1295
+ .client-btn {
1296
+ display: inline-block;
1297
+ padding: 4px 8px;
1298
+ margin-top: 8px;
1299
+ color: #fff;
1300
+ font-size: 12px;
1301
+ border-radius: 20px;
1302
+ background: #5585f5;
1303
+ cursor: pointer;
1304
+ }
1305
+ .re-call {
1306
+ cursor: pointer;
1307
+ color: #5585f5;
1308
+ }
1309
+ .voice-bg {
1310
+ display: flex;
1311
+ align-items: center;
1312
+ cursor: pointer;
1313
+ img {
1314
+ mix-blend-mode: difference;
1315
+ display: inline-block;
1316
+ width: 14px;
1317
+ }
1318
+ .voice-text {
1319
+ display: inline-block;
1320
+ }
1321
+ span {
1322
+ margin-left: 8px;
1323
+ }
1324
+ }
1325
+ }
1326
+ .has-dictionary {
1327
+ padding: 0;
1328
+ text-align: left;
1329
+ background: unset;
1330
+ }
1331
+ .content {
1332
+ padding: 10px;
1333
+ background: #ebebeb;
1334
+ border-radius: 0px 8px 8px 8px;
1335
+ max-width: 500px;
1336
+ margin: 0px;
1337
+ line-height: 20px;
1338
+ font-size: 14px;
1339
+ color: #000;
1340
+ text-align: left;
1341
+ word-break: break-word;
1342
+ /deep/ p {
1343
+ margin: 0;
1344
+ }
1345
+ }
1346
+ .upload-image {
1347
+ margin-left: 8px;
1348
+ }
1349
+ &.right {
1350
+ flex-direction: row-reverse;
1351
+ .right-time {
1352
+ left: unset;
1353
+ right: 10px;
1354
+ }
1355
+ .content-wrap {
1356
+ text-align: right;
1357
+ .robot-item {
1358
+ text-align: right;
1359
+ }
1360
+ .voice-bg {
1361
+ flex-direction: row-reverse;
1362
+ align-self: flex-end;
1363
+ img {
1364
+ transform: rotateY(180deg);
1365
+ }
1366
+ span {
1367
+ margin-left: 0;
1368
+ margin-right: 8px;
1369
+ text-align: right;
1370
+ }
1371
+ }
1372
+ }
1373
+ .content-wrap .content {
1374
+ background-color: #e7f0ff;
1375
+ border-radius: 8px 0px 8px 8px;
1376
+ border: 1px solid #e7f0ff;
1377
+ box-sizing: border-box;
1378
+ }
1379
+ .upload-image {
1380
+ margin-right: 8px;
1381
+ }
1382
+ }
1383
+ }
1384
+ .bot-message {
1385
+ display: block;
1386
+ text-decoration: underline;
1387
+ color: #5585f5;
1388
+ cursor: pointer;
1389
+ }
1390
+ .session-end {
1391
+ display: flex;
1392
+ justify-content: center;
1393
+ margin-top: 15px;
1394
+ margin-bottom: 25px;
1395
+ .end {
1396
+ display: flex;
1397
+ justify-content: center;
1398
+ width: 30%;
1399
+ height: 36px;
1400
+ line-height: 36px;
1401
+ background-color: #e8e8e8;
1402
+ color: #939393;
1403
+ font-size: 14px;
1404
+ border-radius: 18px;
1405
+ }
1406
+ }
1407
+ .menu-tip {
1408
+ font-size: 20px;
1409
+ align-self: flex-end;
1410
+ margin-bottom: 8px;
1411
+ background: #f6f6f6;
1412
+ height: 25px;
1413
+ line-height: 25px;
1414
+ width: 30px;
1415
+ border-radius: 4px;
1416
+ }
1417
+ .menu-item {
1418
+ :hover {
1419
+ background: red;
1420
+ }
1421
+ }
1422
+ .menu-main {
1423
+ width: 100px;
1424
+ background-color: #101627;
1425
+ color: #fff;
1426
+ text-align: left;
1427
+ border-radius: 6px;
1428
+ padding: 10px 0;
1429
+ z-index: 3000;
1430
+ cursor: pointer;
1431
+ .menu-item {
1432
+ padding: 0 10px;
1433
+ }
1434
+ .menu-item:hover {
1435
+ background-color: #efefef;
1436
+ color: #000;
1437
+ }
1438
+ }
1439
+ .first-name {
1440
+ width: 40px;
1441
+ height: 40px;
1442
+ line-height: 40px;
1443
+ text-align: center;
1444
+ background: #5585f5;
1445
+ color: #fff;
1446
+ border-radius: 100%;
1447
+ font-size: 15px;
1448
+ }
1449
+ .msg-avatar {
1450
+ width: 40px;
1451
+ height: 40px;
1452
+ border-radius: 50%;
1453
+
1454
+ object-fit: cover;
1455
+ background: #fff;
1456
+ }
1457
+ /deep/ .ant-spin-spinning {
1458
+ display: flex;
1459
+ align-items: center;
1460
+ }
1461
+ .system-msg {
1462
+ margin: 10px 0;
1463
+ text-align: left;
1464
+ line-height: 20px;
1465
+ word-wrap: break-word;
1466
+ display: flex;
1467
+ justify-content: center;
1468
+ .system-icon {
1469
+ font-size: 18px;
1470
+ color: #ffc200;
1471
+ vertical-align: middle;
1472
+ margin-right: 10px;
1473
+ margin-top: 1px;
1474
+ }
1475
+ .system-btn {
1476
+ color: @primary-color;
1477
+ margin-left: 8px;
1478
+ cursor: pointer;
1479
+ text-decoration: underline;
1480
+ }
1481
+ }
1482
+ }
1483
+ /deep/ p {
1484
+ margin-bottom: 0;
1485
+ }
1486
+ </style>