index.vue 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. <script setup>
  2. import { storeToRefs } from 'pinia';
  3. import useVoiceStore from "@/store/modules/voice";
  4. import { ElMessageBox } from 'element-plus';
  5. import useUserStore from '@/store/modules/user';
  6. import ResetPwdDialog from '@/components/ResetPwdDialog'
  7. import { workbenchApi } from '@/api/voice/workbench';
  8. import TelCallBoard from './TelCallBoard.vue'
  9. import NoticePopover from './NoticePopover.vue'
  10. const voiceStore = useVoiceStore();
  11. const { proxy } = getCurrentInstance();
  12. const { callAnswered, systemState } = storeToRefs(voiceStore);
  13. const userStore = useUserStore();
  14. const dialogVisible = ref(false);
  15. const SYSTEM_STAT_ENUM = [
  16. {label: '置闲', state: true, icon: 'online-icon'},
  17. {label: '置忙', state: false, icon: 'offline-icon'}
  18. ];
  19. // 修改在线状态
  20. const handlePopoverItem = async ({ state, label }) => {
  21. /**
  22. * HSCTIERRORCODE :
  23. * 100001: SDK 状态不可用,CTIStatus 的状态为 Terminated
  24. * 100002: 获取坐席媒体权限失败
  25. *
  26. * AGENTSTATUS :
  27. * 1 置忙 2 置闲
  28. * */
  29. /**
  30. * 疑问:中途切换 获取坐席状态
  31. * */
  32. await voiceStore.getAgentStatus();
  33. // console.log( "坐席状态", voiceStore.AGENTSTATUS );
  34. // console.log( "系统状态", voiceStore.HSCTIERRORCODE );
  35. if ( voiceStore.AGENTSTATUS == 0 && ![100001, 100002].includes(Number(voiceStore.HSCTIERRORCODE))) {
  36. workbenchApi.getSeatsByUser({ userId: userStore.id }).then(async({ data }) => {
  37. if (!data) return;
  38. await useVoice.HS_CTI_INSTANCE(data.outId);
  39. setTimeout(async() => {
  40. await voiceStore.getAgentStatus();
  41. // console.log( "坐席状态 下", voiceStore.AGENTSTATUS );
  42. // console.log( "系统状态 下", voiceStore.HSCTIERRORCODE );
  43. // if ( !([100001, 100002].includes( Number(voiceStore.HSCTIERRORCODE)) ) ) {
  44. if ( voiceStore.AGENTSTATUS != 0 ) {
  45. state ? await voiceStore.setIdle() : await voiceStore.setBusy();
  46. proxy.$modal[state ? 'msgSuccess' : 'msgWarning']('当前坐席状态: ' + label);
  47. } else {
  48. voiceStore.systemState = false;
  49. }
  50. }, 1000);
  51. })
  52. } else {
  53. /**
  54. * 坐席状态是 2 系统状态是100002
  55. * */
  56. if (([100001, 100002].includes(Number(voiceStore.HSCTIERRORCODE)))) {
  57. return ElMessageBox.alert(
  58. `
  59. <p>您的系统出现异常,可能导致无法正常接听电话(${voiceStore.HSCTIERRORCODE})</p>
  60. <p>请检查麦克风权限是否开启。</p>
  61. `,
  62. '提醒',
  63. {
  64. dangerouslyUseHTMLString: true,
  65. }
  66. )
  67. }
  68. state ? await voiceStore.setIdle() : await voiceStore.setBusy();
  69. proxy.$modal[state ? 'msgSuccess' : 'msgWarning']('当前坐席状态: ' + label);
  70. }
  71. }
  72. //
  73. const handleCommand = (command) => {
  74. switch (command) {
  75. case "logout":
  76. logout();
  77. break;
  78. case 'user':
  79. userCenter();
  80. break;
  81. default:
  82. break;
  83. }
  84. }
  85. const userCenter = () => {
  86. dialogVisible.value = true;
  87. }
  88. const logout = () => {
  89. ElMessageBox.confirm('确定退出系统吗?', '提示', {
  90. confirmButtonText: '确定',
  91. cancelButtonText: '取消',
  92. type: 'warning'
  93. }).then(async () => {
  94. /**
  95. * HSCTIERRORCODE :
  96. * 100001: SDK 状态不可用,CTIStatus 的状态为 Terminated
  97. * 100002: 获取坐席媒体权限失败
  98. *
  99. * AGENTSTATUS :
  100. * 0 未登录 1 置忙 2 置闲
  101. * */
  102. // voiceStore.getAgentStatus();
  103. // console.log( "退出 坐席状态", voiceStore.AGENTSTATUS );
  104. // console.log( "退出 系统状态", voiceStore.HSCTIERRORCODE );
  105. // && voiceStore.AGENTSTATUS != 0 && !([100001, 100002].includes(Number(voiceStore.HSCTIERRORCODE)))
  106. if ( voiceStore.isAuthPane ) {
  107. sessionStorage.removeItem('VOICE_STATUS')
  108. await voiceStore.unInit();
  109. }
  110. userStore.logOut().then(() => {
  111. location.href = '/index';
  112. })
  113. }).catch(() => { });
  114. }
  115. onMounted( () => {
  116. })
  117. </script>
  118. <template>
  119. <header class="header-container">
  120. <div class="navbar-left flex items-center space-x-[8px]">
  121. <img src="@/assets/images/logo/logo.png" alt="" class="w-[28px] h-[28px]">
  122. <img src="@/assets/images/logo/logo-text.svg" alt="" class="w-[150px] h-[30px]">
  123. </div>
  124. <div class="navbar-right flex items-center space-x-[12px]">
  125. <el-dropdown trigger="click" popper-class="custom-dropdown" size="small" :disabled="callAnswered" class="forbid" v-if="voiceStore.isAuthPane">
  126. <div :class="['system-state-wrapper', {forbid: callAnswered}]">
  127. <div :class="['system-state-btn', {forbid: callAnswered}]">
  128. <p class="flex items-center space-x-[4px]">
  129. <span :class="['icon', systemState ? 'online-icon' : 'offline-icon']"></span>
  130. <span>{{ systemState ? '置闲' : '置忙' }}</span>
  131. </p>
  132. <el-icon size="10"><ArrowDown /></el-icon>
  133. </div>
  134. </div>
  135. <template #dropdown>
  136. <el-dropdown-menu>
  137. <el-dropdown-item v-for="item in SYSTEM_STAT_ENUM" :key="item.label" @click="handlePopoverItem(item)">
  138. <div class="system-state-btn">
  139. <p class="flex items-center space-x-[4px]">
  140. <span :class="['icon', item.icon]"></span>
  141. <span>{{ item.label }}</span>
  142. </p>
  143. </div>
  144. </el-dropdown-item>
  145. </el-dropdown-menu>
  146. </template>
  147. </el-dropdown>
  148. <TelCallBoard v-if="voiceStore.isAuthPane"/>
  149. <NoticePopover></NoticePopover>
  150. <div class="avatar-wrapper flex items-center space-x-[4px]">
  151. <div class="avatar-img">
  152. <img :src="userStore.avatar" alt="" class="img">
  153. </div>
  154. <el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click" size="small" :disabled="callAnswered">
  155. <div class="h-[36px] flex items-center space-x-[4px]">
  156. <div class="user-name">{{ userStore.nickName }}</div>
  157. <el-icon><caret-bottom /></el-icon>
  158. </div>
  159. <template #dropdown>
  160. <el-dropdown-menu>
  161. <el-dropdown-item command="user">
  162. <span class="text-[#1D2129] text-[14px] py-[4px]">个人中心</span>
  163. </el-dropdown-item>
  164. <el-dropdown-item command="logout">
  165. <span class="text-[#1D2129] text-[14px] py-[4px]">退出登录</span>
  166. </el-dropdown-item>
  167. </el-dropdown-menu>
  168. </template>
  169. </el-dropdown>
  170. </div>
  171. </div>
  172. </header>
  173. <ResetPwdDialog v-model="dialogVisible"></ResetPwdDialog>
  174. </template>
  175. <style lang="scss">
  176. .custom-dropdown {
  177. width: 86px;
  178. .el-dropdown-menu__item {
  179. padding: 8px !important;
  180. }
  181. }
  182. .forbid:hover {
  183. cursor: not-allowed !important;
  184. }
  185. </style>
  186. <style lang="scss" scoped>
  187. .header-container {
  188. display: flex;
  189. align-items: center;
  190. justify-content: space-between;
  191. height: 92px;
  192. padding: 34px 20px 22px 20px;
  193. .navbar-left, .navbar-right {
  194. height: 100%;
  195. }
  196. .avatar-wrapper {
  197. font-size: 14px;
  198. color: #1D2129;
  199. font-weight: bold;
  200. .avatar-img {
  201. flex-shrink: 0;
  202. width: 36px;
  203. height: 36px;
  204. border-radius: 50%;
  205. background: #fff;
  206. overflow: hidden;
  207. .img {
  208. width: 100%;
  209. height: 100%;
  210. border-radius: 100%;
  211. }
  212. }
  213. .user-name {
  214. max-width: 100px;
  215. overflow:hidden;
  216. white-space: nowrap;
  217. text-overflow: ellipsis;
  218. -o-text-overflow:ellipsis;
  219. }
  220. }
  221. }
  222. .system-state-wrapper {
  223. width: 86px;
  224. height: 36px;
  225. padding: 9px 8px;
  226. border-radius: 8px;
  227. overflow: hidden;
  228. background: #fff;
  229. }
  230. .system-state-btn {
  231. display: flex;
  232. align-items: center;
  233. justify-content: space-between;
  234. line-height: 18px;
  235. text-align: center;
  236. font-size: 14px;
  237. color: #1D2129;
  238. cursor: pointer;
  239. .icon {
  240. display: block;
  241. width: 18px;
  242. height: 18px;
  243. }
  244. .online-icon {
  245. background: url('@/assets/images/header/icon-phone-online.svg') center center no-repeat;
  246. }
  247. .offline-icon {
  248. background: url('@/assets/images/header/icon-phone-offline.svg') center center no-repeat;
  249. }
  250. }
  251. </style>