telControl.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. <!--
  2. * @Author: zc
  3. * @Description: 电话控件
  4. * @version:
  5. * @Date: 2022-11-08 14:16:48
  6. * @LastEditors: Please set LastEditors
  7. * @LastEditTime: 2022-11-18 16:04:53
  8. -->
  9. <template>
  10. <div class="phoneControls">
  11. <div class="infos">
  12. <div><span>分机号:</span>{{ telStatus.telNo }}</div>
  13. <div class="bottom_line"><span>状态:</span><b class="qianru_status">{{ telStatus.isOnDuty ? '签入' : '签出' }}</b>
  14. </div>
  15. </div>
  16. <div class="btns">
  17. <!-- 签入签出 -->
  18. <div class="item" :class="active.includes('qianchu') ? 'active' : ''" @click="onControlClick('qianchu')" v-if="telStatus.isOnDuty">
  19. <img :src="getImageUrl('phoneControls/qianchu_blue.png')" alt="">
  20. <span>签出</span>
  21. </div>
  22. <div class="item" @click="onControlClick('qianru')" v-else>
  23. <img :src="getImageUrl('phoneControls/qinaru_grey.png')" alt="">
  24. <!-- <img:src="getImageUrl('phoneControls/qianru_grey.png')" alt=""/> -->
  25. <span>签入</span>
  26. </div>
  27. <!-- 挂断 -->
  28. <el-popover :width="130*hangupList.length" v-model:visible="showHangupList" :show-arrow="false" trigger="hover" popper-class="hangup-popover" v-if="hangupList.length > 1">
  29. <template #reference>
  30. <div class="item" :class="active.includes('guaduan') ? 'active' : ''"
  31. @click="onControlClick('guaduan')">
  32. <img v-if="active.includes('guaduan')" :src="getImageUrl('phoneControls/guaduan_white.png')"
  33. alt="">
  34. <img v-else
  35. :src="telStatus.isOnDuty ? getImageUrl('phoneControls/guaduan_blue.png') : getImageUrl('phoneControls/guaduan_grey.png')"
  36. alt="">
  37. <span>挂断({{ hangupList.length }})</span>
  38. </div>
  39. </template>
  40. <div class="hangup-container">
  41. <div class="hangup-item" v-for="(item, index) in hangupList" :key="index">
  42. <p class="hangup-item-name">{{ item.name }}</p>
  43. <p class="hangup-item-phoneNumber">{{ item.tel }}</p>
  44. <button class="hangup-item-button" @click="hanup(item.tel)">挂断</button>
  45. </div>
  46. </div>
  47. </el-popover>
  48. <div class="item" :class="active.includes('guaduan') ? 'active' : ''" @click="onControlClick('guaduan')"
  49. v-else>
  50. <img v-if="active.includes('guaduan')" :src="getImageUrl('phoneControls/guaduan_white.png')" alt="">
  51. <img v-else
  52. :src="telStatus.isOnDuty ? getImageUrl('phoneControls/guaduan_blue.png') : getImageUrl('phoneControls/guaduan_grey.png')"
  53. alt="">
  54. <span>挂断</span>
  55. </div>
  56. <!-- 小休和结束休息 -->
  57. <div class="item"
  58. :class="active.includes('xiaoxiu') ? 'active' : '', userInfos.authBtnList.includes('999104') ? '' : 'disabled'"
  59. @click="onControlClick('xiaoxiuEnd')" v-if="telStatus.isResting">
  60. <img v-if="active.includes('xiaoxiu')" :src="getImageUrl('phoneControls/xiaoxiu_white.png')" alt="">
  61. <img v-else
  62. :src="telStatus.isOnDuty ? getImageUrl('phoneControls/xiaoxiu_blue.png') : getImageUrl('phoneControls/xiaoxiu_grey.png')"
  63. alt="">
  64. <span>结束小休</span>
  65. </div>
  66. <div class="item"
  67. :class="active.includes('xiaoxiu') ? 'active' : '', userInfos.authBtnList.includes('999103') ? '' : 'disabled'"
  68. @click="telStatus.isOnDuty ? onControlClick('xiaoxiu') : ''" v-else>
  69. <img v-if="active.includes('xiaoxiu')" :src="getImageUrl('phoneControls/xiaoxiu_white.png')" alt="">
  70. <img v-else
  71. :src="telStatus.isOnDuty ? getImageUrl('phoneControls/xiaoxiu_blue.png') : getImageUrl('phoneControls/xiaoxiu_grey.png')"
  72. alt="">
  73. <span>小休</span>
  74. </div>
  75. <!-- 保持和取消保持 -->
  76. <el-popover :width="130*holdList.length" v-model:visible="showHoldList" :show-arrow="false" trigger="hover" popper-class="hangup-popover" v-if="hangupList.length > 1">
  77. <template #reference>
  78. <div class="item" :class="active.includes('baochi') ? 'active' : ''"
  79. @click="onControlClick('baochi')">
  80. <img v-if="active.includes('baochi')" :src="getImageUrl('phoneControls/baochi_white.png')"
  81. alt="">
  82. <img v-else
  83. :src="telStatus.isOnDuty ? getImageUrl('phoneControls/baochi_blue.png') : getImageUrl('phoneControls/baochi_grey.png')"
  84. alt="">
  85. <span>保持({{ holdList.length }})</span>
  86. </div>
  87. </template>
  88. <div class="hangup-container">
  89. <div class="hangup-item" v-for="(item, index) in holdList" :key="index">
  90. <p class="hangup-item-name">{{ item.name }}</p>
  91. <p class="hangup-item-phoneNumber">{{ item.tel }}</p>
  92. <button class="hangup-item-button" @click="hold(item.hold)">{{item.hold?'保持':'取消保持'}}</button>
  93. </div>
  94. </div>
  95. </el-popover>
  96. <div class="item" :class="active.includes('baochi') ? 'active' : ''" @click="onControlClick('baochi')" v-else>
  97. <img v-if="active.includes('baochi')" :src="getImageUrl('phoneControls/baochi_white.png')" alt="">
  98. <img v-else
  99. :src="telStatus.isOnDuty ? getImageUrl('phoneControls/baochi_blue.png') : getImageUrl('phoneControls/baochi_grey.png')"
  100. alt="">
  101. <span>保持</span>
  102. </div>
  103. <div class="item" :class="active.includes('jingyin') ? 'active' : ''" @click="onControlClick('jingyin')">
  104. <img v-if="active.includes('jingyin')" :src="getImageUrl('phoneControls/jingyin_white.png')" alt="">
  105. <img v-else
  106. :src="telStatus.isOnDuty ? getImageUrl('phoneControls/jingyin_blue.png') : getImageUrl('phoneControls/jingyin_grey.png')"
  107. alt="">
  108. <span>静音</span>
  109. </div>
  110. <div class="item" :class="active.includes('zhuanjie') ? 'active' : ''" @click="onControlClick('zhuanjie')">
  111. <img v-if="active.includes('zhuanjie')" :src="getImageUrl('phoneControls/zhuanjie_white.png')" alt="">
  112. <img v-else
  113. :src="telStatus.isOnDuty ? getImageUrl('phoneControls/zhuanjie_blue.png') : getImageUrl('phoneControls/zhuanjie_grey.png')"
  114. alt="">
  115. <span>转接</span>
  116. </div>
  117. <div class="item" :class="active.includes('sanfang') ? 'active' : ''" @click="onControlClick('sanfang')">
  118. <img v-if="active.includes('sanfang')" :src="getImageUrl('phoneControls/sanfang_white.png')" alt="">
  119. <img v-else
  120. :src="telStatus.isOnDuty ? getImageUrl('phoneControls/sanfang_blue.png') : getImageUrl('phoneControls/sanfang_grey.png')"
  121. alt="">
  122. <span>三方会议</span>
  123. </div>
  124. <div class="item" :class="active.includes('waihu') ? 'active' : ''" @click="onControlClick('waihu')">
  125. <img v-if="active.includes('waihu')" :src="getImageUrl('phoneControls/waihu_white.png')" alt="">
  126. <img v-else
  127. :src="telStatus.isOnDuty ? getImageUrl('phoneControls/waihu_blue.png') : getImageUrl('phoneControls/waihu_grey.png')"
  128. alt="">
  129. <span>外呼</span>
  130. </div>
  131. <div class="item" :class="active.includes('hujiaozhuanyi') ? 'active' : ''"
  132. @click="onControlClick('zhuanyi')">
  133. <img v-if="active.includes('hujiaozhuanyi')" :src="getImageUrl('phoneControls/hujiaozhuanyi_white.png')"
  134. alt="">
  135. <img v-else
  136. :src="telStatus.isOnDuty ? getImageUrl('phoneControls/hujiaozhuanyi_blue.png') : getImageUrl('phoneControls/hujiaozhuanyi_grey.png')"
  137. alt="">
  138. <span>呼叫转移</span>
  139. </div>
  140. <div class="item" :class="active.includes('pingjia') ? 'active' : ''" @click="onControlClick('pingjia')">
  141. <img v-if="active.includes('pingjia')" :src="getImageUrl('phoneControls/pingjia_white.png')" alt="">
  142. <img v-else
  143. :src="telStatus.isOnDuty ? getImageUrl('phoneControls/pingjia_blue.png') : getImageUrl('phoneControls/pingjia_grey.png')"
  144. alt="">
  145. <span>评价</span>
  146. </div>
  147. </div>
  148. </div>
  149. <!-- 占位标签 -->
  150. <div class="seizeSeat-box"></div>
  151. <!-- 功能 -->
  152. <!-- 签入 -->
  153. <el-dialog v-model="showDutyDialog" draggable title="签入" width="400px">
  154. <el-form :model="dutyForm" label-width="80px" ref="dutyFormRef">
  155. <el-form-item label="分机" prop="telNo"
  156. :rules="[{ required: true, message: '请选择需要签入的分机', trigger: 'change' }]">
  157. <el-select v-model="dutyForm.telNo" placeholder="请选择需要签入的分机" class="w100">
  158. <el-option v-for="item in telsList" :key="item.id" :label="item.no" :value="item.no" />
  159. </el-select>
  160. </el-form-item>
  161. </el-form>
  162. <template #footer>
  163. <span class="dialog-footer">
  164. <el-button @click="showDutyDialog = false">取 消</el-button>
  165. <el-button type="primary" @click="clickOnDuty" :loading="loading">确 定</el-button>
  166. </span>
  167. </template>
  168. </el-dialog>
  169. </template>
  170. <script setup lang="ts" name="telControl">
  171. import { reactive, toRefs, ref, nextTick } from "vue";
  172. import { ElMessageBox, ElNotification } from 'element-plus';
  173. import { storeToRefs } from 'pinia';
  174. import { useUserInfo } from '/@/stores/userInfo';
  175. import { useTelStatus } from '/@/stores/telStatus';
  176. import { getImageUrl } from "/@/utils/tools";
  177. import { getTelsList } from '/@/api/login/user';
  178. import { onDuty, offDuty, telRest, telUnrest } from '/@/api/login/user';
  179. // import signalR from '/@/utils/signalR';
  180. const state = reactive({
  181. active: <any>[], // 当前选中
  182. showDutyDialog: false, //签入选分机弹窗
  183. dutyForm: {
  184. telNo: '' //分机号
  185. },
  186. telsList: <any>[],// 分机列表
  187. loading: false,
  188. showHangupList:false, //是否展示挂断列表
  189. hangupList: [
  190. {
  191. name: '市民',
  192. tel: 13412341234
  193. },
  194. {
  195. name: '坐席',
  196. tel: 123456
  197. },
  198. ], //挂断列表
  199. holdList: [
  200. {
  201. name: '市民',
  202. tel: 13412341234,
  203. hold:true
  204. },
  205. {
  206. name: '坐席',
  207. tel: 123456,
  208. hold:false
  209. },{
  210. name: '坐席',
  211. tel: 123456,
  212. hold:false
  213. },{
  214. name: '坐席',
  215. tel: 123456,
  216. hold:false
  217. },{
  218. name: '坐席',
  219. tel: 123456,
  220. hold:false
  221. },{
  222. name: '坐席',
  223. tel: 123456,
  224. hold:false
  225. },
  226. ], // 保持列表
  227. showHoldList:false, // 是否展示保持列表
  228. })
  229. const { active, showDutyDialog, dutyForm, loading, telsList,showHangupList, hangupList, holdList,showHoldList } = toRefs(state);
  230. const useTelStatusStore = useTelStatus();
  231. const { telStatus } = storeToRefs(useTelStatusStore);
  232. const storesUserInfo = useUserInfo();
  233. const { userInfos } = storeToRefs(storesUserInfo);
  234. nextTick(() => {
  235. useTelStatusStore.getTelStatusAction(); // 查询当前用户签入状态
  236. });
  237. const getTelsLists = () => {// 查询所有分机
  238. getTelsList().then((res: any) => {
  239. state.telsList = res?.result ?? [];
  240. })
  241. }
  242. // 点击事件
  243. const onControlClick = (val: string) => {
  244. switch (val) {
  245. case 'qianru': //签入
  246. if (userInfos.value.authBtnList.includes('999101')) { // 签入权限
  247. onDutyFn();
  248. } else {
  249. console.warn('您没有签入权限')
  250. }
  251. break;
  252. case 'qianchu': //签出
  253. if (userInfos.value.authBtnList.includes('999102')) { // 签出权限
  254. offDutyFn();
  255. } else {
  256. console.warn('您没有签出权限')
  257. }
  258. break;
  259. case 'guaduan': //挂断
  260. hangupFn();
  261. break;
  262. case 'xiaoxiu': //小休
  263. if (userInfos.value.authBtnList.includes('999103')) { // 小休权限
  264. xiaoxiuFn();
  265. } else {
  266. console.warn('您没有小休权限')
  267. }
  268. break;
  269. case 'xiaoxiuEnd': //结束小休
  270. if (userInfos.value.authBtnList.includes('999104')) { // 结束小休权限
  271. xiaoxiuEndFn();
  272. } else {
  273. console.warn('您没有结束小休权限')
  274. }
  275. break;
  276. default:
  277. break;
  278. }
  279. }
  280. const dutyFormRef = ref();
  281. const onDutyFn = () => { //签入
  282. getTelsLists();
  283. // 重置表单
  284. if (dutyFormRef.value) {
  285. dutyFormRef.value.resetFields();
  286. }
  287. state.showDutyDialog = true;
  288. }
  289. const clickOnDuty = () => {// 确认签入
  290. dutyFormRef.value.validate((valid: boolean) => {
  291. if (valid) {
  292. state.showDutyDialog = true;
  293. onDuty(state.dutyForm.telNo).then(() => {
  294. // 开启 signalr 链接
  295. // if (Session.get('token')) {
  296. // signalR.start();
  297. // signalR.SR.on("Ring", (data:any) =>{// 接收消息
  298. // console.log(data)
  299. // })
  300. // }
  301. ElNotification({
  302. title: '成功',
  303. message: '签入成功',
  304. type: 'success',
  305. })
  306. state.showDutyDialog = false;
  307. state.loading = false;
  308. useTelStatusStore.getTelStatusAction(); // 查询当前用户签入状态
  309. }).catch(() => {
  310. state.showDutyDialog = false;
  311. state.loading = false;
  312. })
  313. } else {
  314. return false;
  315. }
  316. });
  317. }
  318. // 签出
  319. const offDutyFn = () => {
  320. ElMessageBox.confirm(`确定要签出,是否继续?`, '提示', {
  321. confirmButtonText: '确认',
  322. cancelButtonText: '取消',
  323. type: 'warning',
  324. }).then(() => {
  325. offDuty().then(() => {
  326. ElNotification({
  327. title: '成功',
  328. message: '签出成功',
  329. type: 'success',
  330. })
  331. useTelStatusStore.getTelStatusAction(); // 查询当前用户签入状态
  332. })
  333. }).catch(() => { });
  334. }
  335. // 挂断
  336. const hangupFn = () => {
  337. }
  338. // 挂断其中一个
  339. const hanup = (tel: any) => {
  340. state.showHangupList = false;
  341. }
  342. // 小休
  343. const xiaoxiuFn = () => {
  344. ElMessageBox.confirm(`确定要开始小休,是否继续?`, '提示', {
  345. confirmButtonText: '确认',
  346. cancelButtonText: '取消',
  347. type: 'warning',
  348. }).then(() => {
  349. telRest().then(() => {
  350. ElNotification({
  351. title: '成功',
  352. message: '小休开始',
  353. type: 'success',
  354. })
  355. useTelStatusStore.getTelStatusAction(); // 查询当前用户签入状态
  356. })
  357. }).catch(() => { });
  358. }
  359. const holdFn = ()=>{ // 单个保持
  360. }
  361. const hold = ()=>{ //保持一个
  362. state.showHoldList = false;
  363. }
  364. // 小休结束
  365. const xiaoxiuEndFn = () => {
  366. ElMessageBox.confirm(`确定要结束小休,是否继续?`, '提示', {
  367. confirmButtonText: '确认',
  368. cancelButtonText: '取消',
  369. type: 'warning',
  370. }).then(() => {
  371. telUnrest().then(() => {
  372. ElNotification({
  373. title: '成功',
  374. message: '小休结束',
  375. type: 'success',
  376. })
  377. useTelStatusStore.getTelStatusAction(); // 查询当前用户签入状态
  378. })
  379. }).catch(() => { });
  380. }
  381. </script>
  382. <style scoped lang="scss">
  383. .seizeSeat-box {
  384. display: none;
  385. }
  386. .phoneControls {
  387. display: flex;
  388. flex: 1;
  389. background: #FFFFFF;
  390. box-shadow: 0px 1px 8px 0px rgba(0, 15, 49, 0.1);
  391. border-bottom-left-radius: 90px;
  392. border-bottom-right-radius: 90px;
  393. padding: 0 52px;
  394. color: #333;
  395. height: 100%;
  396. .infos {
  397. margin: 20px 0;
  398. text-align: left;
  399. width: 120px;
  400. .bottom_line {
  401. padding-top: 5px;
  402. }
  403. .qianru_status {
  404. color: var(--el-color-primary);
  405. font-weight: normal;
  406. }
  407. span {
  408. display: inline-block;
  409. width: 60px;
  410. text-align: right;
  411. }
  412. }
  413. // 按钮列表
  414. .btns {
  415. display: flex;
  416. width: calc(100% - 120px);
  417. justify-content: space-between;
  418. .item {
  419. text-align: center;
  420. cursor: pointer;
  421. width: 100%;
  422. height: 100%;
  423. &:hover {
  424. color: #fff;
  425. background-image: url('../../../assets/images/phoneControls/active.png');
  426. background-repeat: no-repeat;
  427. background-size: 100% 100%;
  428. // height: calc(100% + 20px);
  429. }
  430. img {
  431. display: block;
  432. margin: 0 auto;
  433. padding-top: 15px;
  434. }
  435. span {
  436. margin-top: 5px;
  437. display: inline-block;
  438. }
  439. &.disabled {
  440. cursor: not-allowed;
  441. }
  442. }
  443. .active {
  444. color: #fff;
  445. background-image: url('../../../assets/images/phoneControls/active.png');
  446. background-repeat: no-repeat;
  447. background-size: 100% 100%;
  448. height: calc(100% + 20px);
  449. background-color: var(--hotline-bg-main-color);
  450. img {
  451. display: block;
  452. margin: 0 auto;
  453. padding-top: 30px;
  454. }
  455. span {
  456. margin-top: 5px;
  457. display: inline-block;
  458. }
  459. &:hover {
  460. transition: inherit;
  461. color: var(--hotline-color-white) !important;
  462. background-color: var(--hotline-bg-main-color) !important;
  463. }
  464. }
  465. }
  466. }
  467. </style>
  468. <style lang="scss">
  469. .el-popover.hangup-popover {
  470. background: var(--el-color-primary);
  471. border-radius:16px;
  472. opacity: .9;
  473. box-shadow: 0px 1px 8px 0px rgba(0,15,49,0.1);
  474. border: 1px solid #667AED;
  475. padding:15px;
  476. .hangup-container{
  477. display: flex;
  478. flex-wrap: nowrap;
  479. color: #fff;
  480. font-size: var(--el-font-size-small);
  481. .hangup-item {
  482. text-align: center;
  483. width: 130px;
  484. border-right: 1px solid #aaa;
  485. &:last-child {
  486. border-right: 0;
  487. }
  488. // &-name {
  489. // // padding-top: 10px;
  490. // }
  491. &-phoneNumber {
  492. padding-top: 4px;
  493. }
  494. &-button {
  495. background: linear-gradient(0deg, #E3E3E3, #FFFFFF);
  496. border-radius: 8px;
  497. color:var(--el-color-primary);
  498. outline: none;
  499. padding:5px 15px;
  500. border: none;
  501. margin-top: 6px;
  502. font-weight: bold;
  503. cursor: pointer;
  504. }
  505. }
  506. }
  507. }
  508. </style>