123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- <template>
- <div class="title_wrap">
- <!-- <el-button
- round
- class="title_wrap_btn"
- @click="signIn"
- v-if="!globalState.callCenterIsSignIn"
- :icon="Phone"
- >签入
- </el-button>
- <el-button
- round
- class="title_wrap_btn"
- @click="signOut"
- v-else
- :icon="PhoneFilled"
- >签出</el-button
- >-->
- <div class="title">
- <span class="title-text">{{ title }}</span>
- </div>
- <div class="timers">
- <span class="timers-text">
- {{ formatDate(now, "YYYY年mm月dd日 HH:MM:SS WWW") }}</span
- >
- </div>
- <div class="guang"></div>
- <div class="left_icons">
- <el-badge :value="readyCount" :max="99" class="left_icons_item">
- <img src="@/assets/img/seats/ready.png" alt="" />空闲
- </el-badge>
- <el-badge :value="restCount" :max="99" class="left_icons_item">
- <img src="@/assets/img/seats/unready.png" alt="" />小休
- </el-badge>
- <el-badge :value="meetingCount" :max="99" class="left_icons_item">
- <img src="@/assets/img/seats/threeWay.png" alt="" />三方会议
- </el-badge>
- <el-badge :value="logoutCount" :max="99" class="left_icons_item">
- <img src="@/assets/img/seats/logout.png" alt="" />签出
- </el-badge>
- <el-badge :value="callInCount" :max="99" class="left_icons_item">
- <img src="@/assets/img/seats/callIn.png" alt="" />呼入
- </el-badge>
- </div>
- <div class="right_icons">
- <el-badge :value="callOutCount" :max="99" class="right_icons_item">
- <img src="@/assets/img/seats/callOut.png" alt="" />呼出
- </el-badge>
- <el-badge :value="consultCount" :max="99" class="right_icons_item">
- <img src="@/assets/img/seats/ring.png" alt="" />咨询
- </el-badge>
- <el-badge :value="otherCount" :max="99" class="right_icons_item">
- <img src="@/assets/img/seats/other.png" alt="" />其他
- </el-badge>
- <el-badge :value="callCount" :max="99" class="right_icons_item">
- <img src="@/assets/img/seats/busy.png" alt="" />通话
- </el-badge>
- <el-badge :value="cawCount" :max="99" class="right_icons_item">
- <img src="@/assets/img/seats/acw.png" alt="" />整理
- </el-badge>
- <el-badge :value="logoffCount" :max="99" class="right_icons_item">
- <img src="@/assets/img/seats/logoff.png" alt="" />注销
- </el-badge>
- </div>
- <!-- 签入弹窗 -->
- <el-dialog
- v-model="state.dutyDialogVisible"
- draggable
- title="签入"
- width="500px"
- :show-close="false"
- append-to-body
- >
- <el-form :model="state.dutyForm" label-width="80px" ref="dutyFormRef">
- <el-form-item
- label="分机"
- prop="currentTel"
- :rules="[
- {
- required: true,
- message: '请选择需要签入的分机',
- trigger: 'change',
- },
- ]"
- >
- <el-select-v2
- v-model="state.dutyForm.currentTel"
- :options="state.telsList"
- placeholder="选择要签入的分机"
- filterable
- class="w-full"
- :height="500"
- :props="{
- label: 'telNo',
- value: 'telNo',
- }"
- />
- </el-form-item>
- </el-form>
- <template #footer>
- <span class="dialog-footer">
- <el-button
- @click="state.dutyDialogVisible = false"
- :loading="state.loading"
- >取 消</el-button
- >
- <el-button @click="clickOnDuty(dutyFormRef)" :loading="state.loading"
- >确 定</el-button
- >
- </span>
- </template>
- </el-dialog>
- </div>
- </template>
- <script setup lang="ts">
- import { computed, onMounted, onUnmounted, reactive, ref, watch } from "vue";
- import signalR from "@/utils/signalR";
- import { useNow, useTitle } from "@vueuse/core";
- import { formatDate } from "@/utils/formatTime";
- import { olaFn } from "@/utils/olaFn";
- import { getNowDateTime } from "@/utils/constants";
- import { callCenterLogout, useGlobalState } from "@/utils/callCenter";
- import { ElMessageBox, FormInstance } from "element-plus";
- import { Phone, PhoneFilled } from "@element-plus/icons-vue";
- import { useThemeConfig } from "@/stores/themeConfig";
- import { storeToRefs } from "pinia";
- import { getListenExtension } from "api/seats";
- import mittBus from "@/utils/mitt";
- const storesThemeConfig = useThemeConfig();
- const { themeConfig } = storeToRefs(storesThemeConfig);
- const props = defineProps({
- data: {
- type: Array,
- default: () => [],
- },
- });
- const now = useNow(); // 获取当前时间
- const state = reactive({
- dutyDialogVisible: false,
- dutyForm: {
- currentTel: null,
- },
- loading: false,
- telsList: [],
- });
- // 签入
- const signIn = () => {
- if (!globalState.callCenterIsSignIn) {
- state.dutyDialogVisible = true;
- } else {
- globalState.callCenterIsSignIn = false;
- }
- };
- const dutyFormRef = ref();
- // 确认签入
- const clickOnDuty = (formEl: FormInstance | undefined) => {
- if (!formEl) return;
- formEl.validate((valid: boolean) => {
- if (!valid) return;
- const currentPhone: any = state.telsList.find(
- (item: any) => item.telNo === state.dutyForm.currentTel
- );
- globalState.currentTel = {
- telNo: currentPhone.telNo,
- telGroup: currentPhone.queue,
- jobNum: currentPhone.TelNo,
- };
- websocket_connect(currentPhone.telNo, currentPhone.telPwd);
- state.dutyDialogVisible = false;
- });
- };
- // 签出
- const signOut = () => {
- ElMessageBox.confirm(`签出后无法监听和插话,确定签出?`, "提示", {
- confirmButtonText: "确认",
- cancelButtonText: "取消",
- type: "warning",
- draggable: true,
- cancelButtonClass: "default-button",
- autofocus: false,
- })
- .then(async () => {
- callCenterLogout();
- })
- .catch(() => {});
- };
- const globalState = useGlobalState();
- const olaRef = ref();
- const websocket_connect = (telNo: string, telPwd: string) => {
- olaRef.value = olaFn(themeConfig.value.callCenterSocketUrl, {
- username: telNo,
- password: telPwd,
- onConnected: onConnected, // 连接成功
- onDisconnected: onDisconnected, // 断开链接
- onMessage: onMessage, // 接收消息
- onError: onError, // 错误
- autoReconnect: {
- delay: 2000,
- // retries: 10,
- }, // 自动重连
- heartbeat: {
- message: JSON.stringify({ cmd: "ping" }),
- interval: 5000,
- // pongTimeout: 1000,
- },
- });
- console.log(`${getNowDateTime()}:开始链接呼叫中心`);
- };
- // 呼叫中心链接成功
- const onConnected = () => {
- olaRef.value.logout(globalState.currentTel.telNo); //连接之后,先登出一次,防止其他地方已经登陆
- let array_ola_queue = []; // 队列
- // 普通模式
- let array = globalState.currentTel.telGroup.split(",");
- for (let i = 0; i < array.length; i++) {
- array_ola_queue[i] = array[i];
- }
- olaRef.value.login(array_ola_queue, globalState.currentTel.telNo, {
- type: "onhook",
- });
- globalState.callCenterWs = olaRef.value;
- console.log(`${getNowDateTime()}:呼叫中心链接成功`);
- };
- // 呼叫中心链接关闭
- const onDisconnected = (event: any) => {
- globalState.callCenterWs = null;
- console.log(`${getNowDateTime()}:呼叫中心断开链接`, event);
- };
- // 呼叫中心链接错误
- const onError = (ws: any, e: any) => {
- globalState.callCenterIsSignIn = false; // 签出状态
- console.log(`${getNowDateTime()}:呼叫中心链接错误`, e);
- };
- // 呼叫中心消息
- const onMessage = async (event: any) => {
- const data = JSON.parse(event);
- // console.log(`${getNowDateTime()}:接收呼叫中心消息`, event);
- if (data.event_type == "agent_state") {
- switch (data.state) {
- case "login": // 签入消息回调
- globalState.callCenterIsSignIn = true; // 签入状态
- setTimeout(() => {
- // 设置示闲状态
- olaRef.value.go_ready();
- }, 300);
- console.log(
- `${getNowDateTime()}:接收消息:呼叫中心:已签入,当前分机:${
- globalState.currentTel.telNo
- }`
- );
- break;
- case "logout": // 签出消息回调
- globalState.callCenterIsSignIn = false; // 签出状态
- console.log(`${getNowDateTime()}:接收消息:呼叫中心:已签出`);
- break;
- case "acw": // 话后整理回到
- // 调用示闲
- olaRef.value.go_ready(); // 示闲
- console.log(`${getNowDateTime()}:接收消息:呼叫中心:已示闲`);
- break;
- case "unready": // 示忙消息回调
- // 调用示闲
- olaRef.value.go_ready(); // 示闲
- console.log(`${getNowDateTime()}:接收消息:呼叫中心:已示闲`);
- break;
- }
- }
- };
- // 空闲数量
- const readyCount = computed(() => {
- return seatsList.value.filter((item: any) => item.State === "0").length;
- });
- // 小休数量、
- const restCount = computed(() => {
- return seatsList.value.filter((item: any) => item.State === "1").length;
- });
- // 三方会议数量
- const meetingCount = computed(() => {
- return seatsList.value.filter((item: any) => item.State === "2").length;
- });
- // 签出数量
- const logoutCount = computed(() => {
- return seatsList.value.filter((item: any) => item.State === "3").length;
- });
- // 呼入数量
- const callInCount = computed(() => {
- return seatsList.value.filter((item: any) => item.State === "4").length;
- });
- // 呼出数量
- const callOutCount = computed(() => {
- return seatsList.value.filter((item: any) => item.State === "5").length;
- });
- // 咨询数量
- const consultCount = computed(() => {
- return seatsList.value.filter((item: any) => item.State === "6").length;
- });
- // 其他数量
- const otherCount = computed(() => {
- return seatsList.value.filter((item: any) => item.State === "7").length;
- });
- // 通话数量
- const callCount = computed(() => {
- return seatsList.value.filter((item: any) => item.State === "8").length;
- });
- // 通话数量
- const cawCount = computed(() => {
- return seatsList.value.filter((item: any) => item.State === "9").length;
- });
- // 注销数量
- const logoffCount = computed(() => {
- return seatsList.value.filter((item: any) => item.State === "10").length;
- });
- const seatsList = ref<any>([]);
- watch(
- () => props.data,
- (newData: any) => {
- seatsList.value = newData;
- },
- { immediate: true }
- );
- // 获取可以监听的分机列表
- const getSeatsList = async () => {
- try {
- const { result } = await getListenExtension();
- state.telsList = result;
- } catch (e) {
- console.log(e);
- }
- };
- const title = ref("12345坐席监控中心");
- onMounted(() => {
- getSeatsList();
- // 接收消息
- mittBus.on("monitorInfoTel", (data: any) => {
- const item = seatsList.value.find(
- (item: any) => item.Extension === data.Extension
- );
- item.loading = true;
- if (item) {
- setTimeout(() => {
- item.State = data.State;
- item.loading = false;
- }, 500);
- }
- });
- title.value = `${themeConfig.value.cityName}12345坐席监控中心`;
- useTitle(title.value);
- });
- onUnmounted(() => {
- signalR.SR.off("SeatState");
- });
- </script>
- <style scoped lang="scss">
- :deep(.el-badge__content) {
- border: none;
- top: -3px;
- right: calc(-3px + var(--el-badge-size) / 2);
- }
- .title_wrap {
- height: 140px;
- position: relative;
- &_btn {
- position: absolute;
- left: 40px;
- top: 12px;
- z-index: 2;
- border-color: var(--el-color-white);
- }
- &::after {
- content: "";
- position: absolute;
- bottom: 13px;
- background-image: radial-gradient(circle, #1b86d1 50%, #176bb5 50%);
- width: 100%;
- height: 2px;
- }
- .guang {
- position: absolute;
- bottom: -13px;
- background-image: url("../../assets/img/seats/guang.png");
- background-position: 80px center;
- width: 100%;
- height: 56px;
- }
- .timers {
- position: absolute;
- right: 16px;
- top: 20px;
- display: flex;
- align-items: center;
- color: var(--el-color-white);
- font-size: var(--el-font-size-extra-large);
- }
- }
- .title {
- position: relative;
- text-align: center;
- background-size: cover;
- color: transparent;
- height: 140px;
- line-height: 90px;
- &-text {
- font-size: 38px;
- font-weight: 900;
- letter-spacing: 6px;
- background: linear-gradient(
- 92deg,
- #0072ff 0%,
- #00eaff 48.8525390625%,
- #01aaff 100%
- );
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- }
- }
- .left_icons {
- position: absolute;
- left: 40px;
- bottom: 25px;
- display: flex;
- align-items: center;
- .left_icons_item {
- display: flex;
- align-items: center;
- margin-right: 35px;
- &:last-child {
- margin-right: 10px;
- }
- img {
- margin-right: 5px;
- }
- }
- }
- .right_icons {
- position: absolute;
- right: 16px;
- bottom: 25px;
- display: flex;
- align-items: center;
- .right_icons_item {
- display: flex;
- align-items: center;
- margin-right: 35px;
- &:last-child {
- margin-right: 10px;
- }
- img {
- margin-right: 5px;
- }
- }
- }
- </style>
|