index.html 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069
  1. <link rel="stylesheet" href="//at.alicdn.com/t/c/font_2385137_b8qygb2jne.css?v={$site.version|htmlentities}">
  2. <link rel="stylesheet" href="__CDN__/assets/addons/shopro/css/index.css?v={$site.version|htmlentities}">
  3. __DARK__
  4. <style>
  5. #sa-chat {
  6. position: absolute;
  7. right: 20px;
  8. bottom: 20px;
  9. z-index: 1000;
  10. }
  11. #sa-chat .chat-button {
  12. width: 40px;
  13. height: 40px;
  14. background: var(--sa-background-assist);
  15. box-shadow: 0px 1px 16px #e4e4e4;
  16. border-radius: 50%;
  17. display: flex;
  18. align-items: center;
  19. justify-content: center;
  20. cursor: pointer;
  21. }
  22. #sa-chat .chat-button:hover {
  23. background: var(--sa-table-header-bg);
  24. }
  25. #sa-chat .chat-icon {
  26. font-size: 20px;
  27. color: var(--el-color-primary);
  28. }
  29. .sa-shopro-drawer {
  30. top: 50px !important;
  31. width: 380px !important;
  32. height: calc(100% - 50px) !important;
  33. }
  34. .sa-shopro-drawer .el-drawer__body {
  35. padding: 0;
  36. }
  37. .sa-shopro-drawer .chat-container {
  38. height: 100%;
  39. flex-direction: column;
  40. }
  41. .sa-shopro-drawer .chat-header {
  42. height: 50px;
  43. background: var(--t-btn-hover);
  44. padding: 0 12px;
  45. color: var(--sa-background-assist);
  46. position: relative;
  47. }
  48. .sa-shopro-drawer .chat-header .circle-close-filled {
  49. font-size: 16px;
  50. cursor: pointer;
  51. }
  52. .sa-shopro-drawer .chat-header .circle-close-filled:hover {
  53. color: var(--el-color-primary);
  54. }
  55. .sa-shopro-drawer .drawer-tabs {
  56. width: fit-content;
  57. flex: unset;
  58. height: 36px;
  59. padding: 0 2px;
  60. border-radius: 8px;
  61. background: var(--t-bg-focus);
  62. display: flex;
  63. align-items: center;
  64. justify-content: center;
  65. position: relative;
  66. }
  67. .sa-shopro-drawer .drawer-tabs .bg {
  68. position: absolute;
  69. top: 2px;
  70. left: 2px;
  71. width: 118px;
  72. height: 32px;
  73. background: var(--sa-background-assist);
  74. border-radius: 6px;
  75. transition: all 0.2s;
  76. }
  77. .sa-shopro-drawer .drawer-tabs-item {
  78. width: 118px;
  79. height: 32px;
  80. display: flex;
  81. align-items: center;
  82. justify-content: center;
  83. position: relative;
  84. z-index: 1;
  85. padding: 0 7px;
  86. font-size: 14px;
  87. color: var(--el-color-primary);
  88. cursor: pointer;
  89. }
  90. .sa-shopro-drawer .chat-main {
  91. --el-main-padding: 0;
  92. }
  93. .sa-shopro-drawer .notification-item-more {
  94. width: 100%;
  95. height: 40px;
  96. line-height: 40px;
  97. text-align: center;
  98. font-size: 12px;
  99. color: var(--sa-subfont);
  100. }
  101. .sa-shopro-drawer .empty-icon {
  102. width: 150px;
  103. height: 150px;
  104. }
  105. .sa-shopro-drawer .empty-description {
  106. width: 200px;
  107. font-size: 16px;
  108. color: var(--sa-subfont);
  109. }
  110. .sa-chat-drawer .avatar {
  111. position: relative;
  112. width: 36px;
  113. height: 36px;
  114. }
  115. .sa-chat-drawer .avatar .avatar-bage {
  116. position: absolute;
  117. right: 0;
  118. bottom: 2px;
  119. height: 10px;
  120. width: 10px;
  121. padding: 0;
  122. background: #fff;
  123. border-radius: 50%;
  124. }
  125. .sa-chat-drawer .avatar .avatar-bage img {
  126. width: 100%;
  127. }
  128. .status-popover {
  129. width: 104px !important;
  130. min-width: 104px !important;
  131. }
  132. .status-popover img {
  133. width: 12px;
  134. height: 12px;
  135. }
  136. .customer-service {
  137. width: 122px;
  138. color: var(--sa-background-assist);
  139. }
  140. .customer-service .customer-service-name {
  141. height: 16px;
  142. line-height: 16px;
  143. font-size: 14px;
  144. font-weight: 400;
  145. margin-bottom: 4px;
  146. }
  147. .customer-service .customer-service-room-name {
  148. height: 14px;
  149. line-height: 14px;
  150. font-weight: 400;
  151. font-size: 12px;
  152. }
  153. .customer-service .customer-service-icon {
  154. font-size: 12px;
  155. margin-left: 5px;
  156. @media only screen and (max-width: 768px) {
  157. font-size: 16px;
  158. }
  159. }
  160. .sa-chat-drawer .chat-header .drawer-tabs .bg {
  161. width: 50px;
  162. }
  163. .sa-chat-drawer .chat-header .drawer-tabs .drawer-tabs-item {
  164. width: 50px;
  165. font-size: 12px;
  166. }
  167. .sa-chat-drawer .chat-main {
  168. display: flex;
  169. overflow: hidden;
  170. }
  171. .sa-chat-drawer .chat-main .content-left {
  172. flex-shrink: 0;
  173. height: 100%;
  174. border-right: 1px solid var(--sa-space);
  175. }
  176. .sa-chat-drawer .chat-session-item {
  177. width: 60px;
  178. transition: all 0.3s linear;
  179. padding: 10px 0;
  180. }
  181. .sa-chat-drawer .chat-session-item:hover {
  182. background-color: var(--t-bg-active);
  183. transition: all 0.3s linear;
  184. }
  185. .sa-chat-drawer .chat-session-item:hover .chat-session-icon {
  186. transform: scale(1);
  187. margin-left: 0;
  188. transition: all 0.2s linear;
  189. }
  190. .sa-chat-drawer .chat-session-item:hover .chat-session-icon:active {
  191. transform: translate(1px, 1px);
  192. }
  193. .sa-chat-drawer .chat-session-item:focus {
  194. background-color: var(--t-bg-active);
  195. }
  196. .sa-chat-drawer .chat-session-item.is-active {
  197. background-color: var(--t-bg-active);
  198. transition: all 0.3s linear;
  199. }
  200. .sa-chat-drawer .chat-session-item .chat-session-icon {
  201. height: 12px;
  202. font-size: 12px;
  203. color: var(--el-color-primary);
  204. margin-right: 4px;
  205. margin-left: -16px;
  206. transform: scale(0) translate(0, 0);
  207. transition: all 0.2s linear;
  208. }
  209. .sa-chat-drawer .chat-session-item .chat-session-icon .chat-session-circle-close-filled {
  210. display: none;
  211. }
  212. .sa-chat-drawer .chat-session-item .chat-session-icon:hover .chat-session-close {
  213. display: none;
  214. }
  215. .sa-chat-drawer .chat-session-item .chat-session-icon:hover .chat-session-circle-close-filled {
  216. display: block;
  217. }
  218. .sa-chat-drawer .chat-main .content-right {
  219. width: 100%;
  220. height: 100%;
  221. display: flex;
  222. flex-direction: column;
  223. }
  224. .sa-chat-drawer .current-customer {
  225. width: 100%;
  226. height: 40px;
  227. background-color: var(--sa-card-background);
  228. border-bottom: 1px solid var(--sa-space);
  229. padding: 0 10px;
  230. }
  231. .sa-chat-drawer .current-customer .el-input__inner {
  232. border: none;
  233. height: 38px;
  234. width: 140px;
  235. color: #8c8c8c;
  236. }
  237. .sa-chat-drawer .current-customer .current-customer-name {
  238. font-size: 14px;
  239. color: var(--sa-font);
  240. }
  241. .sa-chat-drawer .current-customer .current-customer-transfer {
  242. flex-shrink: 0;
  243. width: 120px;
  244. }
  245. .sa-chat-drawer .current-customer .customer-from {
  246. font-size: 14px;
  247. color: #999999;
  248. }
  249. .sa-chat-drawer .current-customer .access-btn {
  250. color: var(--el-color-primary);
  251. font-size: 12px;
  252. padding: 10px 20px;
  253. }
  254. .sa-chat-drawer .chat-message {
  255. padding: 0 16px;
  256. }
  257. .sa-chat-drawer .message-item {
  258. margin-bottom: 20px;
  259. }
  260. .sa-chat-drawer .message-date {
  261. color: var(--sa-subfont);
  262. font-size: 12px;
  263. }
  264. .sa-chat-drawer .message-item .message-item-wrap {
  265. max-width: 80%;
  266. padding: 12px;
  267. font-size: 14px;
  268. color: var(--sa-font);
  269. white-space: normal;
  270. word-break: break-all;
  271. word-wrap: break-word;
  272. }
  273. .sa-chat-drawer .message-item.sa-row-right .message-item-wrap {
  274. background: var(--t-bg-active);
  275. border-radius: 8px 2px 8px 8px;
  276. color: var(--sa-font);
  277. }
  278. .sa-chat-drawer .message-item.sa-row-left .message-item-wrap {
  279. background: var(--sa-table-header-bg);
  280. border-radius: 2px 8px 8px 8px;
  281. color: var(--sa-font);
  282. }
  283. .sa-chat-drawer .message-item .message-emoji {
  284. width: 24px !important;
  285. height: 24px;
  286. margin-right: 4px;
  287. }
  288. .sa-chat-drawer .message-item .message-emoji:last-of-type {
  289. margin-right: 0;
  290. }
  291. .sa-chat-drawer .message-item img {
  292. width: 100% !important;
  293. }
  294. .sa-chat-drawer .message-item .goods-item .el-image {
  295. flex-shrink: 0;
  296. width: 40px;
  297. height: 40px;
  298. }
  299. .sa-chat-drawer .message-item .order-sn {
  300. font-size: 12px;
  301. color: var(--el-color-primary);
  302. }
  303. .sa-chat-drawer .message-item .order-goods {
  304. padding: 8px;
  305. background: var(--sa-background-assist);
  306. border-radius: 4px;
  307. }
  308. .sa-chat-drawer .message-item .order-goods .el-image {
  309. flex-shrink: 0;
  310. width: 40px;
  311. height: 40px;
  312. }
  313. .sa-chat-drawer .message-item .order-goods .order-goods-price,
  314. .sa-chat-drawer .message-item .order-goods .order-goods-total {
  315. font-size: 12px;
  316. color: var(--sa-subfont);
  317. }
  318. .sa-chat-drawer .chat-footer {
  319. --el-footer-padding: 0;
  320. --el-footer-height: 140px;
  321. border-top: 1px solid var(--sa-space);
  322. }
  323. .sa-chat-drawer .chat-footer .chat-footer-input {
  324. width: 100%;
  325. height: 84px;
  326. overflow-y: auto;
  327. padding: 10px;
  328. font-size: 14px;
  329. line-height: 20px;
  330. color: var(--sa-font);
  331. border: none;
  332. outline: none;
  333. margin-bottom: 10px;
  334. }
  335. .sa-chat-drawer .chat-footer .chat-footer-bottom {
  336. padding: 0 12px;
  337. }
  338. .sa-chat-drawer .chat-footer .chat-footer-bottom .iconfont {
  339. font-size: 18px;
  340. }
  341. .emoji-popover {
  342. padding: 12px 0 0 12px !important;
  343. }
  344. .emoji-popover .emoji-icon {
  345. width: 20px;
  346. height: 20px;
  347. margin-right: 6px;
  348. margin-bottom: 6px;
  349. }
  350. .emoji-popover .emoji-icon img {
  351. width: 100%;
  352. }
  353. .common-words-popover {
  354. padding: 0 !important;
  355. }
  356. .common-words-popover .common-words-title {
  357. font-size: 14px;
  358. color: #8c8c8c;
  359. height: 40px;
  360. line-height: 40px;
  361. padding-left: 17px;
  362. border-bottom: 1px solid var(--sa-border);
  363. }
  364. .common-words-popover .common-words-item {
  365. color: var(--sa-font);
  366. font-size: 14px;
  367. height: 40px;
  368. line-height: 40px;
  369. border-bottom: 1px solid var(--sa-border);
  370. padding-left: 20px;
  371. }
  372. .common-words-popover .common-words-item:hover {
  373. background: rgba(128, 106, 246, 0.08);
  374. }
  375. .sa-chat-drawer .connect-status {
  376. display: flex;
  377. flex-direction: column;
  378. align-items: center;
  379. justify-content: center;
  380. font-size: 14px;
  381. color: var(--sa-subtitle);
  382. }
  383. .sa-chat-drawer .connect-status .chat-reconnect-failed {
  384. width: 150px;
  385. height: 150px;
  386. margin-bottom: 30px;
  387. }
  388. .sa-chat-drawer .connect-status .connect-status-dot {
  389. font-family: simsun;
  390. display: inline-block;
  391. width: 1.5em;
  392. vertical-align: bottom;
  393. overflow: hidden;
  394. animation: dot 3s infinite step-start;
  395. }
  396. @-webkit-keyframes dot {
  397. 0% {
  398. width: 0;
  399. margin-right: 1.5em;
  400. }
  401. 33% {
  402. width: 0.5em;
  403. margin-right: 1em;
  404. }
  405. 66% {
  406. width: 1em;
  407. margin-right: 0.5em;
  408. }
  409. 100% {
  410. width: 1.5em;
  411. margin-right: 0;
  412. }
  413. }
  414. .sa-chat-drawer .connect-status .reconnect-button {
  415. color: var(--el-color-primary);
  416. cursor: pointer;
  417. }
  418. .sa-notification-drawer .chat-header .circle-close-filled {
  419. position: absolute;
  420. top: 50%;
  421. right: 12px;
  422. margin-top: -8px;
  423. font-size: 16px;
  424. }
  425. .sa-notification-drawer .notification-item {
  426. width: 100%;
  427. padding: 16px;
  428. border-bottom: 1px solid var(--sa-space);
  429. }
  430. .sa-notification-drawer .notification-item .notification-item-time {
  431. font-family: PingFang SC;
  432. font-size: 12px;
  433. color: var(--sa-subfont);
  434. }
  435. .sa-notification-drawer .notification-item .notification-item-top {
  436. display: flex;
  437. width: inherit;
  438. overflow: hidden;
  439. }
  440. .sa-notification-drawer .notification-item .text {
  441. font-size: 14px;
  442. color: var(--sa-font);
  443. overflow: hidden;
  444. text-overflow: ellipsis;
  445. text-align: justify;
  446. position: relative;
  447. line-height: 1.5;
  448. max-height: 3em;
  449. transition: 0.3s max-height;
  450. white-space: normal;
  451. word-break: break-all;
  452. }
  453. .sa-notification-drawer .notification-item .text::before {
  454. content: '';
  455. height: calc(100% - 21px);
  456. float: right;
  457. }
  458. .sa-notification-drawer .notification-item .text::after {
  459. content: '';
  460. width: 999vw;
  461. height: 999vw;
  462. position: absolute;
  463. box-shadow: inset calc(100px - 999vw) calc(21px - 999vw) 0 0 var(--el-bg-color);
  464. margin-left: -100px;
  465. }
  466. .sa-notification-drawer .notification-item .notification-item-button {
  467. position: relative;
  468. float: right;
  469. clear: both;
  470. margin-left: 20px;
  471. line-height: 1.5;
  472. padding: 0 8px;
  473. font-size: 12px;
  474. color: var(--el-color-primary);
  475. cursor: pointer;
  476. }
  477. .sa-notification-drawer .notification-item .notification-item-button::after {
  478. content: '展开';
  479. }
  480. .sa-notification-drawer .notification-item .exp {
  481. display: none;
  482. }
  483. .sa-notification-drawer .notification-item .exp:checked+.text {
  484. max-height: none;
  485. }
  486. .sa-notification-drawer .notification-item .exp:checked+.text::after {
  487. visibility: hidden;
  488. }
  489. .sa-notification-drawer .notification-item .exp:checked+.text .notification-item-button::before {
  490. visibility: hidden;
  491. }
  492. .sa-notification-drawer .notification-item .exp:checked+.text .notification-item-button::after {
  493. content: '收起';
  494. }
  495. .sa-notification-drawer .notification-item .notification-item-button::before {
  496. content: '...';
  497. position: absolute;
  498. left: -5px;
  499. color: var(--sa-font);
  500. transform: translateX(-100%);
  501. }
  502. .sa-notification-drawer .notification-item.is-read .text,
  503. .sa-notification-drawer .notification-item.is-read .notification-item-time,
  504. .sa-notification-drawer .notification-item.is-read .notification-item-button::before {
  505. color: var(--sa-place);
  506. }
  507. .sa-notification-drawer .chat-footer {
  508. --el-footer-height: 40px;
  509. border-top: 1px solid var(--sa-space);
  510. }
  511. </style>
  512. <script type="text/x-template" id="saChatTemplate">
  513. <div id="sa-chat">
  514. <div class="chat-button mb-2" @click="onShowNotification">
  515. <el-badge is-dot :hidden="!isNotificationUnreadNum">
  516. <i class="iconfont iconNotification1 chat-icon"></i>
  517. </el-badge>
  518. </div>
  519. <div class="chat-button" @click="onShowChat">
  520. <el-badge is-dot :hidden="chat.state.connection.status != 'connect' || !isChatUnreadNum">
  521. <i v-if="chat.state.connection.status === 'reconnect_failed'" class="iconfont iconChat1 chat-icon"></i>
  522. <i v-else class="iconfont iconChat chat-icon"></i>
  523. </el-badge>
  524. </div>
  525. <el-drawer v-model="showChat" direction="rtl" custom-class="sa-chat-drawer sa-shopro-drawer"
  526. modal-class="sa-chat-drawer-overlay" :show-close="false" :with-header="false">
  527. <el-container class="chat-container">
  528. <!-- 连接成功 -->
  529. <template v-if="chat.state.connection.status == 'connect'">
  530. <el-header class="chat-header sa-flex sa-row-between">
  531. <!-- 客服 -->
  532. <div class="sa-flex">
  533. <el-popover v-if="chat.state.currentCustomerService.name" placement="bottom-start" trigger="click"
  534. popper-class="status-popover" :show-arrow="false">
  535. <div class="sa-flex sa-row-center cursor-pointer"
  536. v-for="(value,key) in customerServiceStatus"
  537. @click="onChangeCustomerServiceStatus(key)">
  538. <img :src="`/assets/addons/shopro/img/chat/status-${key}.png`" />
  539. <span class="ml-2">{{value}}</span>
  540. </div>
  541. <template #reference>
  542. <div class="avatar mr-2 cursor-pointer">
  543. <el-avatar :size="36" :src="Fast.api.cdnurl(chat.state.currentCustomerService.avatar)">
  544. <img src="/assets/addons/shopro/img/default-avatar.png" />
  545. </el-avatar>
  546. <div class="avatar-bage sa-flex sa-row-center">
  547. <img
  548. :src="`/assets/addons/shopro/img/chat/status-${chat.state.currentCustomerService.status}.png`" />
  549. </div>
  550. </div>
  551. </template>
  552. </el-popover>
  553. <div v-if="chat.state.customerServiceIdentityList.length==0" class="customer-service sa-flex">
  554. <div class="sa-flex-col">
  555. <div class="customer-service-name sa-table-line-1">
  556. {{ chat.state.currentCustomerService.name }}
  557. </div>
  558. <div class="customer-service-room-name sa-table-line-1">{{
  559. chat.state.currentCustomerService.room_name }}</div>
  560. </div>
  561. </div>
  562. <el-dropdown v-if="chat.state.customerServiceIdentityList.length > 0" trigger="click">
  563. <div class="customer-service sa-flex">
  564. <div class="sa-flex-col cursor-pointer">
  565. <div class="customer-service-name sa-table-line-1">
  566. {{ chat.state.currentCustomerService.name }}
  567. </div>
  568. <div class="customer-service-room-name sa-table-line-1">{{
  569. chat.state.currentCustomerService.room_name }}
  570. </div>
  571. </div>
  572. <el-icon class="customer-service-icon">
  573. <arrow-down />
  574. </el-icon>
  575. </div>
  576. <template #dropdown>
  577. <el-dropdown-menu>
  578. <el-dropdown-item v-for="item in chat.state.customerServiceIdentityList"
  579. :key="item.value" @click="onChangeCustomerServiceIdentity(item.value)">{{
  580. item.label }}
  581. </el-dropdown-item>
  582. </el-dropdown-menu>
  583. </template>
  584. </el-dropdown>
  585. </div>
  586. <!-- 聊天类型 -->
  587. <div class="sa-flex">
  588. <div class="drawer-tabs sa-flex sa-col-center">
  589. <div class="bg" :style="{ left: sessionTypeList[chat.state.sessionType]?.left }">
  590. </div>
  591. <el-badge v-for="(value,key) in sessionTypeList" :is-dot="key=='waiting'"
  592. :hidden="!chat.state.customerWaitingList.length">
  593. <div class="drawer-tabs-item" @click="onChangeSessionType(key)">
  594. {{value.label}}
  595. </div>
  596. </el-badge>
  597. </div>
  598. <el-icon class="circle-close-filled ml-2" @click="showChat=false">
  599. <circle-close-filled />
  600. </el-icon>
  601. </div>
  602. </el-header>
  603. <el-main class="chat-main">
  604. <div class="chat-content sa-flex sa-flex-1">
  605. <!-- 会话列表 -->
  606. <div class="content-left">
  607. <el-scrollbar class="chat-session" :min-size="10">
  608. <div class="sa-flex-col sa-row-center sa-col-center">
  609. <div class="chat-session-item sa-flex sa-col-center sa-row-center" :class="{
  610. 'is-active': currentSessionTypeIndexs[chat.state.sessionType] == index,
  611. }" v-for="(item, index) in chat.state.sessionList"
  612. @click="onChangeCurrentSessionTypeIndex(index)">
  613. <template v-if="chat.state.sessionType == 'ing'">
  614. <el-popconfirm width="fit-content" title="确定结束当前会话吗?"
  615. @confirm="onDeleteSession(item.session_id, index, chat.state.sessionType)">
  616. <template #reference>
  617. <div class="chat-session-icon cursor-pointer">
  618. <el-icon class="chat-session-close">
  619. <close />
  620. </el-icon>
  621. <el-icon class="chat-session-circle-close-filled">
  622. <circle-close-filled />
  623. </el-icon>
  624. </div>
  625. </template>
  626. </el-popconfirm>
  627. </template>
  628. <template v-if="chat.state.sessionType == 'history'">
  629. <el-popover popper-class="sa-popper"
  630. v-model:visible="historyDeletePopover.flag[index]" placement="bottom"
  631. trigger="click">
  632. <div class="sa-flex">
  633. <el-icon class="sa-color--warning mr-1">
  634. <question-filled />
  635. </el-icon>确定删除此顾客吗?
  636. </div>
  637. <el-checkbox v-model="historyDeletePopover.is_del_record"
  638. :true-label="1" :false-label="0" label="删除聊天记录"></el-checkbox>
  639. <div class="sa-flex sa-row-right">
  640. <el-button size="small" link
  641. @click="onCancelHistoryDeletePopover(index)">取消</el-button>
  642. <el-button type="primary" size="small"
  643. @click="onConfirmHistoryDeletePopover(item.session_id, index, chat.state.sessionType)">
  644. 确定</el-button>
  645. </div>
  646. <template #reference>
  647. <div class="chat-session-icon cursor-pointer">
  648. <el-icon class="chat-session-close">
  649. <close />
  650. </el-icon>
  651. <el-icon class="chat-session-circle-close-filled">
  652. <circle-close-filled />
  653. </el-icon>
  654. </div>
  655. </template>
  656. </el-popover>
  657. </template>
  658. <el-tooltip :content="item.nickname" placement="right" effect="dark">
  659. <el-badge class="avatar cursor-pointer" type="danger"
  660. :value="item.unread_num" :max="99" :hidden="!item.unread_num">
  661. <el-avatar :size="36" :src="Fast.api.cdnurl(item.avatar)">
  662. <img src="/assets/addons/shopro/img/default-avatar.png" />
  663. </el-avatar>
  664. <div class="avatar-bage sa-flex sa-row-center">
  665. <img
  666. :src="`/assets/addons/shopro/img/chat/status-${item.status?'online':'disconnect'}.png`" />
  667. </div>
  668. </el-badge>
  669. </el-tooltip>
  670. </div>
  671. </div>
  672. </el-scrollbar>
  673. </div>
  674. <div class="content-right">
  675. <!-- 当前顾客 -->
  676. <div v-if="chat.state.currentCustomer"
  677. class="current-customer sa-flex sa-col-center sa-p-l-17">
  678. <div v-if="chat.state.currentCustomer?.session_id"
  679. class="sa-flex sa-flex-1 sa-row-between">
  680. <div class="current-customer-name sa-flex sa-flex-1" @click="toDetail">
  681. {{ chat.state.currentCustomer?.nickname || '' }}
  682. </div>
  683. <div v-if="chat.state.sessionType == 'ing' && avaliableCustomerServicesList.length"
  684. class="current-customer-transfer sa-flex">
  685. <el-dropdown trigger="click" @command="onTransferCommand">
  686. <div class="el-dropdown-link sa-flex">
  687. <div class="sa-line-1">
  688. {{
  689. avaliableCustomerServicesList.find((item) => item.value ==
  690. transferCustomer)
  691. ?.label || '选择转接的客服'
  692. }}
  693. </div>
  694. <el-icon class="el-icon--right">
  695. <arrow-down />
  696. </el-icon>
  697. </div>
  698. <template #dropdown>
  699. <el-dropdown-menu>
  700. <el-dropdown-item v-for="item in avaliableCustomerServicesList"
  701. :key="item.value" :command="item.value"
  702. :disabled="item.disabled">
  703. {{ item.label }}
  704. </el-dropdown-item>
  705. </el-dropdown-menu>
  706. </template>
  707. </el-dropdown>
  708. <el-button v-if="transferCustomer" class="ml-1" type="primary" link size="small"
  709. @click="onTransferCustomer">
  710. 转接
  711. </el-button>
  712. </div>
  713. <el-button v-if="chat.state.sessionType == 'waiting'" type="primary" link
  714. @click="onAccessCustomer">
  715. 立即接入
  716. </el-button>
  717. </div>
  718. </div>
  719. <el-alert v-if="!chat.state.currentCustomer" type="warning" center>
  720. <template #title>暂无顾客会话</template>
  721. </el-alert>
  722. <el-scrollbar class="chat-message" ref="chatScrollRef">
  723. <template v-if="chat.state.sessionList.length && chat.state.chatList.length">
  724. <!-- 加载状态 -->
  725. <div class="notification-item-more">
  726. <el-button
  727. v-if="chat.state.historyPagination.page < chat.state.historyPagination.lastPage"
  728. type="info" link size="small" @click="onLoadMore">
  729. {{loadingMap[chat.state.historyPagination.loadStatus].title}}
  730. </el-button>
  731. <div v-else>
  732. {{ loadingMap[chat.state.historyPagination.loadStatus].title}}
  733. <i class="ml-1"
  734. :class="loadingMap[chat.state.historyPagination.loadStatus].icon"></i>
  735. </div>
  736. </div>
  737. <template v-for="(item,index) in chat.state.chatList">
  738. <div class="sa-flex sa-row-center">
  739. <div v-if="item.sender_identify == 'system'" class="message-system mb-4">
  740. {{ item.content.text }}
  741. </div>
  742. <div v-if="item.sender_identify != 'system' && showTime(item, index)"
  743. class="message-date mb-4">
  744. {{ formatTime(item.createtime) }}
  745. </div>
  746. </div>
  747. <div class="message-item sa-flex" :class="[
  748. item.sender_identify == 'customer_service'
  749. ? 'sa-row-right'
  750. : item.sender_identify == 'customer'
  751. ? 'sa-row-left'
  752. : '',
  753. ]">
  754. <div class="message-item-wrap">
  755. <!-- 文本 -->
  756. <template v-if="item.message_type=='text'">
  757. <div v-html="replaceEmoji(item.message)"></div>
  758. </template>
  759. <!-- 图片 -->
  760. <template v-if="item.message_type=='image'">
  761. <img :src="Fast.api.cdnurl(item.message)" />
  762. </template>
  763. <!-- 商品 -->
  764. <template v-if="item.message_type=='goods'">
  765. <div class="goods-item sa-flex cursor-pointer"
  766. @click="onOpenGoodsDetail(item.message.id)">
  767. <el-image class="mr-2"
  768. :src="Fast.api.cdnurl(item.message.image)">
  769. </el-image>
  770. <div>
  771. <div class="sa-table-line-1">{{item.message.title}}
  772. </div>
  773. <div class="sa-color--danger">
  774. ¥{{item.message.price.join(',')}}</div>
  775. </div>
  776. </div>
  777. </template>
  778. <!-- 订单 -->
  779. <template v-if="item.message_type=='order'">
  780. <div class="order-item cursor-pointer"
  781. @click="onOpenOrderDetail(item.message.id)">
  782. <div class="order-sn mb-2">{{item.message.order_sn}}</div>
  783. <div class="order-goods sa-flex">
  784. <el-image class="mr-2"
  785. :src="Fast.api.cdnurl(item.message.items[0]?.goods_image)">
  786. </el-image>
  787. <div>
  788. <div class="sa-table-line-1">
  789. {{item.message.items[0]?.goods_title}}
  790. </div>
  791. <div class="sa-flex sa-row-between">
  792. <div class="order-goods-price">
  793. 共{{item.message.items.length}}件商品
  794. </div>
  795. <div class="order-goods-total">合计
  796. ¥{{item.message.pay_fee}}</div>
  797. </div>
  798. </div>
  799. </div>
  800. </div>
  801. </template>
  802. </div>
  803. </div>
  804. </template>
  805. </template>
  806. <!-- 置空页 -->
  807. <el-empty v-if="!chat.state.chatList.length">
  808. <template #image>
  809. <img class="empty-icon" src="/assets/addons/shopro/img/chat/chat-empty.png" />
  810. </template>
  811. <template #description>
  812. <div class="empty-description">暂时没有会话哦!</div>
  813. </template>
  814. </el-empty>
  815. </el-scrollbar>
  816. </div>
  817. </div>
  818. </el-main>
  819. <el-footer v-if="chat.state.currentCustomer" class="chat-footer">
  820. <div class="chat-footer-input" ref="messageInputRef" contenteditable="true" @input="getMessageInput"
  821. placeholder="请输入内容,按Enter直接发送消息" @keydown="onKeyDown" @dragover.prevent></div>
  822. <div class="chat-footer-bottom sa-flex sa-row-between">
  823. <!-- 会话工具栏 -->
  824. <div class="sa-flex">
  825. <!-- 表情 -->
  826. <el-popover placement="top-start" :width="260" trigger="click" popper-class="emoji-popover">
  827. <div class="sa-flex sa-flex-wrap">
  828. <div class="sa-flex sa-row-center sa-col-center emoji-icon"
  829. @click="onSelectToolbar('emoji',item)" @mousedown.prevent
  830. v-for="(item, index) in emojiList" :key="index">
  831. <img :src="`/assets/addons/shopro/img/chat/emoji/${item.file}`" />
  832. </div>
  833. </div>
  834. <template #reference>
  835. <el-button link>
  836. <i class="iconfont iconbiaoqing"></i>
  837. </el-button>
  838. </template>
  839. </el-popover>
  840. <!-- 常用语 -->
  841. <el-popover placement="top-start" :width="200" trigger="click"
  842. popper-class="common-words-popover">
  843. <div class="common-words-title">常用语</div>
  844. <div class="common-words-item sa-line-1" v-for="item in chat.state.commonWords"
  845. :key="item.id" @click="onSelectToolbar('text',item.content)">
  846. {{ item.name }}
  847. </div>
  848. <template #reference>
  849. <el-button link>
  850. <i class="iconfont iconchangyongyu-011"></i>
  851. </el-button>
  852. </template>
  853. </el-popover>
  854. <!-- 商品 -->
  855. <el-button link @click="onSelectToolbar('goods')">
  856. <i class="iconfont iconshangpin"></i>
  857. </el-button>
  858. <!-- 文件 -->
  859. <el-button link @click="onSelectToolbar('image')">
  860. <i class="iconfont iconwenjian-01"></i>
  861. </el-button>
  862. </div>
  863. <el-button class="send-btn" type="primary"
  864. :disabled="!messageInput || !chat.state.currentCustomer" @click="onSendMessage">发送
  865. </el-button>
  866. </div>
  867. </el-footer>
  868. </template>
  869. <!-- 未连接 -->
  870. <el-main v-if="chat.state.connection.status != 'connect'" class="connect-status">
  871. <img class="chat-reconnect-failed" src="/assets/addons/shopro/img/chat/chat-reconnect-failed.png" />
  872. <div v-if="chat.state.connection.status === 'error'">请检查您的网络连接</div>
  873. <div v-if="chat.state.connection.status === 'connecting'">
  874. 正在连接Websocket服务
  875. <span class="connect-status-dot">...</span>
  876. </div>
  877. <div v-if="chat.state.connection.status === 'reconnecting'">
  878. ({{ chat.state.connection.attempts }}/{{
  879. chat.state.connection.reconnectionAttempts
  880. }})正在重新连接Websocket服务
  881. <span class="connect-status-dot">...</span>
  882. </div>
  883. <template v-if="chat.state.connection.status === 'reconnect_error'">
  884. <div>
  885. ({{ chat.state.connection.attempts }}/{{ chat.state.connection.reconnectionAttempts }})
  886. WebSocket服务连接失败
  887. </div>
  888. <div class="mt-1">
  889. {{ chat.state.connection.delay }} 秒后尝试重新连接
  890. <span class="connect-status-dot">...</span>
  891. </div>
  892. </template>
  893. <template v-if="chat.state.connection.status === 'reconnect_failed'">
  894. <div>Websocket服务连接失败,请检查您的环境后点击 </div>
  895. <div class="reconnect-button mt-1" @click="onReconnect">重新连接</div>
  896. </template>
  897. </el-main>
  898. </el-container>
  899. </el-drawer>
  900. <el-drawer v-model="showNotification" direction="rtl" custom-class="sa-notification-drawer sa-shopro-drawer"
  901. :show-close="false" :with-header="false">
  902. <el-container class="chat-container">
  903. <el-header class="chat-header sa-flex sa-row-center">
  904. <div class="sa-flex">
  905. <div class="drawer-tabs sa-flex sa-col-center">
  906. <div class="bg" :style="{left:chat.state.notificationType=='shop'?'120px':''}">
  907. </div>
  908. <div class="drawer-tabs-item" v-for="item in chat.state.notificationTypeList"
  909. @click="onChangeNotificationType(item.value)">
  910. <el-badge is-dot :hidden="!item.unread_num">
  911. {{ item.label }}
  912. </el-badge>
  913. </div>
  914. </div>
  915. <el-icon class="circle-close-filled ml-2" @click="showNotification=false">
  916. <circle-close-filled />
  917. </el-icon>
  918. </div>
  919. </el-header>
  920. <el-main class="chat-main">
  921. <el-scrollbar height="100%">
  922. <template v-if="chat.state.notificationList.length">
  923. <div class="notification-item" :class="item.read_time ? 'is-read' : ''"
  924. v-for="(item, index) in chat.state.notificationList" @click="onReadNotification(item.id, index)">
  925. <div class="notification-item-top">
  926. <input :id="`exp-${index}`" class="exp" type="checkbox" @click.stop />
  927. <div class="text">
  928. <label class="notification-item-button" :for="`exp-${index}`" @click.stop></label>
  929. 【{{ item.data.message_title }}】{{ item.data.message_text }}
  930. </div>
  931. </div>
  932. <span class="notification-item-time">{{ item.createtime }}</span>
  933. </div>
  934. <!-- 加载状态 -->
  935. <div class="notification-item-more">
  936. <el-button v-if="pagination.page < pagination.lastPage" type="info" link size="small"
  937. @click="onLoadMoreNotification">
  938. {{loadingMap[pagination.loadStatus].title}}
  939. </el-button>
  940. <div v-else>
  941. {{ loadingMap[pagination.loadStatus].title}}
  942. <i class="ml-1" :class="loadingMap[pagination.loadStatus].icon"></i>
  943. </div>
  944. </div>
  945. </template>
  946. <!-- 置空页 -->
  947. <el-empty v-if="!chat.state.notificationList.length">
  948. <template #image>
  949. <img class="empty-icon" src="/assets/addons/shopro/img/chat/notification-empty.png" />
  950. </template>
  951. <template #description>
  952. <div class="empty-description"> 您的工作效率很高哦, 现在还没有新的待办消息! </div>
  953. </template>
  954. </el-empty>
  955. </el-scrollbar>
  956. </el-main>
  957. <el-footer class="chat-footer sa-flex sa-row-center">
  958. <el-button link size="small" @click="onClearNotification">清空 已读消息</el-button>
  959. </el-footer>
  960. </el-container>
  961. </el-drawer>
  962. </div>
  963. </script>