|
@@ -120,12 +120,13 @@ 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 { ElMessage, ElMessageBox, FormInstance } from "element-plus";
|
|
|
import { Phone, PhoneFilled } from "@element-plus/icons-vue";
|
|
|
import { useThemeConfig } from "@/stores/themeConfig";
|
|
|
import { storeToRefs } from "pinia";
|
|
|
import { getListenExtensionXt } from "api/seats";
|
|
|
import mittBus from "@/utils/mitt";
|
|
|
+import { useTimeoutFn, useWebSocket } from "@vueuse/core";
|
|
|
|
|
|
const storesThemeConfig = useThemeConfig();
|
|
|
const { themeConfig } = storeToRefs(storesThemeConfig);
|
|
@@ -136,14 +137,281 @@ const props = defineProps({
|
|
|
},
|
|
|
});
|
|
|
const now = useNow(); // 获取当前时间
|
|
|
-const state = reactive({
|
|
|
+const state = reactive<any>({
|
|
|
dutyDialogVisible: false,
|
|
|
- dutyForm: {
|
|
|
+ dutyForm: <any>{
|
|
|
currentTel: null,
|
|
|
},
|
|
|
loading: false,
|
|
|
telsList: [],
|
|
|
});
|
|
|
+
|
|
|
+const m_strUserNo = ref(""); // 分机号码
|
|
|
+const m_strUserName = ref(""); // 用户名称
|
|
|
+const m_strJobNum = ref("1"); // 坐席工号
|
|
|
+const m_strSkillId = ref("99"); // 技能组
|
|
|
+const m_strLevel = ref("1"); // 优先级别
|
|
|
+const m_strGroup = ref("1"); // 分组ID
|
|
|
+const m_strCompanyId = ref(""); // 企业编码
|
|
|
+const m_bLogin = ref(false); // 登录状态
|
|
|
+const m_strTelState = ref("0"); // 当前状态
|
|
|
+const m_strIsMonitor = ref("1"); // 是否监控分机 1-是监控分机
|
|
|
+const m_IsHold = ref(false); // 是否保持
|
|
|
+
|
|
|
+const globalState = useGlobalState(); // 全局变量
|
|
|
+// 发送消息
|
|
|
+const e_TelSendMsg = (strObj: Object) => {
|
|
|
+ // 客户端当前时间
|
|
|
+ const strMsg = JSON.stringify(strObj);
|
|
|
+ console.log(`${getNowDateTime()} 发送消息:`, strMsg, wsRef.value.status);
|
|
|
+ if (wsRef.value.ws?.readyState === 1) {
|
|
|
+ // 已经链接并且可以通讯,则发放文本消息
|
|
|
+ wsRef.value.send(strMsg);
|
|
|
+ } else {
|
|
|
+ ElMessage.error("请先签入");
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// ws实例对象
|
|
|
+const wsRef = ref();
|
|
|
+const initWs = () => {
|
|
|
+ wsRef.value = useWebSocket(themeConfig.value.callCenterSocketUrl, {
|
|
|
+ heartbeat: {
|
|
|
+ message: JSON.stringify({
|
|
|
+ Action: "ReqHealthCheck",
|
|
|
+ Param: { Extension: m_strUserNo.value },
|
|
|
+ }),
|
|
|
+ interval: 5000,
|
|
|
+ pongTimeout: 5000,
|
|
|
+ },
|
|
|
+ autoReconnect: {
|
|
|
+ delay: 2000,
|
|
|
+ }, // 自动重连
|
|
|
+ immediate: true, // 是否立即链接
|
|
|
+ onMessage: e_TelMsgReceive, // 消息接收
|
|
|
+ onError: e_websocketError, // 错误
|
|
|
+ onDisconnected: e_websocketClose, // 断开
|
|
|
+ onConnected: e_websocketOpen, // 链接成功
|
|
|
+ });
|
|
|
+};
|
|
|
+// 消息接收
|
|
|
+const e_TelMsgReceive = (ws: any, restMsg: any) => {
|
|
|
+ console.log(`${getNowDateTime()} 接收消息:${restMsg.data}`);
|
|
|
+ if (restMsg.data) {
|
|
|
+ const data = eval("(" + restMsg.data + ")");
|
|
|
+ if (data) {
|
|
|
+ // 方法
|
|
|
+ const strAction = data.Action;
|
|
|
+ switch (strAction) {
|
|
|
+ // 登录返回值
|
|
|
+ case "ResAgentLogin":
|
|
|
+ retSignIn(data);
|
|
|
+ break;
|
|
|
+ // 示闲
|
|
|
+ case "ResAgentIdle":
|
|
|
+ break;
|
|
|
+ // 示忙
|
|
|
+ case "ResAgentBusy":
|
|
|
+ break;
|
|
|
+ // 外呼状态
|
|
|
+ case "ResMakeCall":
|
|
|
+ break;
|
|
|
+ // 保持
|
|
|
+ case "ResHoldCall":
|
|
|
+ break;
|
|
|
+ // 取消保持
|
|
|
+ case "ResRetrieve":
|
|
|
+ break;
|
|
|
+ // 咨询内线
|
|
|
+ case "ResConsultInline":
|
|
|
+ break;
|
|
|
+ // 咨询外线
|
|
|
+ case "ResConsultOutline":
|
|
|
+ break;
|
|
|
+ // 咨询群组
|
|
|
+ case "ResConsultSkillGroup":
|
|
|
+ break;
|
|
|
+ // 咨询转移
|
|
|
+ case "ResTransfer":
|
|
|
+ break;
|
|
|
+ // 三方会议
|
|
|
+ case "ResConference":
|
|
|
+ break;
|
|
|
+ // 三方会议
|
|
|
+ case "ResMonConf":
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ // 事件
|
|
|
+ const strEvent = data.Event;
|
|
|
+ switch (strEvent) {
|
|
|
+ // 签出事件
|
|
|
+ case "EvtLogout":
|
|
|
+ retSignOut();
|
|
|
+ break;
|
|
|
+ // 呼入振铃事件
|
|
|
+ case "EvtCallAlerting":
|
|
|
+ break;
|
|
|
+ // 应答事件
|
|
|
+ case "EvtCallAnswer":
|
|
|
+ break;
|
|
|
+ // 挂机事件
|
|
|
+ case "EvtHangup":
|
|
|
+ break;
|
|
|
+ // 状态
|
|
|
+ case "EvtSeatState":
|
|
|
+ evtSeatState(data);
|
|
|
+ break;
|
|
|
+ // 队列等待
|
|
|
+ case "EvtAcdInfo":
|
|
|
+ break;
|
|
|
+ // 语音识别结果通知事件
|
|
|
+ case "EvtRecognize":
|
|
|
+ break;
|
|
|
+
|
|
|
+ // 转三方接通状态
|
|
|
+ case "EvtDispatchState":
|
|
|
+ break;
|
|
|
+
|
|
|
+ // 签出事件
|
|
|
+ case "ResStopMonitor":
|
|
|
+ retSignOut();
|
|
|
+ break;
|
|
|
+
|
|
|
+ case "ResError": // 异常
|
|
|
+ // 异常处理
|
|
|
+ retResError(data);
|
|
|
+ break;
|
|
|
+ // 呼出接通事件
|
|
|
+ case "EvtOutCalling":
|
|
|
+ break;
|
|
|
+ // 签出事件
|
|
|
+ case "EvtQuit":
|
|
|
+ retSignOut();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+const evtSeatState = (data: any) => {
|
|
|
+ if (m_strIsMonitor.value == "1") {
|
|
|
+ // 推送分机信息
|
|
|
+ const pushExt = data.Param.Extension || "";
|
|
|
+ if (pushExt !== m_strUserNo.value) {
|
|
|
+ // 如果分机实时推送的分机信息和当前登录分机不一致,则表明当前登录分机为监控分机,开启了状态监控功能
|
|
|
+ // 1.不属于当前分机的状态,则不改变当前分机电话条状态
|
|
|
+ // 2.调整其他分机大屏监控状态
|
|
|
+ const pushState = GetTelState(data.Param.State);
|
|
|
+ if (pushState) {
|
|
|
+ // 修改后台数据
|
|
|
+ console.log(pushExt, pushState);
|
|
|
+ // SetMonitorState(pushExt, pushState);
|
|
|
+ }
|
|
|
+ mittBus.emit("monitorInfoTel", data.Param);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 状态 0:闲 1:忙 2:会议 3:登出 4:呼入 5:呼出 6:咨询 7:其他 8:通话
|
|
|
+ const strState = data.Param.State;
|
|
|
+ switch (strState) {
|
|
|
+ // 空闲
|
|
|
+ case "0":
|
|
|
+ break;
|
|
|
+ // 示忙
|
|
|
+ case "1":
|
|
|
+ break;
|
|
|
+ case "2":
|
|
|
+ break;
|
|
|
+ // 登出
|
|
|
+ case "3":
|
|
|
+ // 附加签出方法(用户分机分离调用业务系统签出);
|
|
|
+ break;
|
|
|
+ // 通话振铃
|
|
|
+ case "4":
|
|
|
+ break;
|
|
|
+ // 通话振铃
|
|
|
+ case "5":
|
|
|
+ break;
|
|
|
+ // 咨询
|
|
|
+ case "6":
|
|
|
+ break;
|
|
|
+ // 其他
|
|
|
+ case "7":
|
|
|
+ break;
|
|
|
+ // 接通
|
|
|
+ case "8":
|
|
|
+ break;
|
|
|
+ case "9": // 工单整理
|
|
|
+ break;
|
|
|
+ }
|
|
|
+};
|
|
|
+// 获取分机状态
|
|
|
+const GetTelState = (strState: any) => {
|
|
|
+ let strResult = "";
|
|
|
+ switch (strState) {
|
|
|
+ // 空闲
|
|
|
+ case "0":
|
|
|
+ strResult = "200";
|
|
|
+ break;
|
|
|
+ // 示忙
|
|
|
+ case "1":
|
|
|
+ strResult = "201";
|
|
|
+ break;
|
|
|
+ case "2":
|
|
|
+ break;
|
|
|
+ // 登出
|
|
|
+ case "3":
|
|
|
+ strResult = "0";
|
|
|
+ break;
|
|
|
+ // 通话振铃
|
|
|
+ case "4":
|
|
|
+ strResult = "302"; // 默认呼入振铃
|
|
|
+ break;
|
|
|
+ // 通话振铃
|
|
|
+ case "5":
|
|
|
+ strResult = "302"; // 默认呼出振铃
|
|
|
+ break;
|
|
|
+ // 咨询
|
|
|
+ case "6":
|
|
|
+ break;
|
|
|
+ // 其他
|
|
|
+ case "7":
|
|
|
+ break;
|
|
|
+ // 接通
|
|
|
+ case "8":
|
|
|
+ strResult = "301";
|
|
|
+ break;
|
|
|
+ case "9": // 工单整理
|
|
|
+ strResult = "900";
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return strResult;
|
|
|
+};
|
|
|
+// ws链接开启成功
|
|
|
+const e_websocketOpen = () => {
|
|
|
+ /* if (userAlreadyLogin.value) {
|
|
|
+ // 检查到用户已经登录需要先签出 再签入
|
|
|
+ sendSignOut();
|
|
|
+ useTimeoutFn(() => {
|
|
|
+ sendSignIn();
|
|
|
+ }, 500);
|
|
|
+
|
|
|
+ // {"Action" : "ReqStateMonitor", "Param": { "MonitorId":""}}
|
|
|
+ } else {
|
|
|
+ if (m_strUserNo.value && m_strSkillId.value) sendSignIn();
|
|
|
+ }*/
|
|
|
+ sendSignIn(); // 签入
|
|
|
+};
|
|
|
+// 链接关闭
|
|
|
+const e_websocketClose = () => {
|
|
|
+ globalState.callCenterIsSignIn = false; // 签出状态
|
|
|
+ console.log(`${getNowDateTime()} 呼叫中心链接关闭`);
|
|
|
+};
|
|
|
+// 链接错误
|
|
|
+const e_websocketError = () => {
|
|
|
+ globalState.callCenterWs = null;
|
|
|
+ globalState.callCenterIsSignIn = false; // 签出状态
|
|
|
+ console.log(`${getNowDateTime()} 呼叫中心链接错误`);
|
|
|
+};
|
|
|
// 签入
|
|
|
const signIn = () => {
|
|
|
if (!globalState.callCenterIsSignIn) {
|
|
@@ -158,18 +426,68 @@ 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);
|
|
|
+ m_strUserNo.value = state.dutyForm.currentTel;
|
|
|
+ m_strJobNum.value = state.dutyForm.currentTel; // 默认取用户工号
|
|
|
+ initWs();
|
|
|
state.dutyDialogVisible = false;
|
|
|
});
|
|
|
};
|
|
|
+/*
|
|
|
+* 登录
|
|
|
+* ReqAgentLogin - 登录方法名
|
|
|
+* JobNum - 工号
|
|
|
+* Name - 姓名
|
|
|
+* Extension - 分机号
|
|
|
+* SkillId - 技能组
|
|
|
+* Level - 为要设置的级别,分为9个级别,从高到低分别为0-8;同一技能组中级别越高的坐席优先被分配
|
|
|
+* Role - 角色,保留,不做设置
|
|
|
+* GroupName - 技能组名称
|
|
|
+* OrgId - 组织ID
|
|
|
+* 返回内容:
|
|
|
+{“Action”:”ResAgentLogin”,”Param”:{“Result”:}}
|
|
|
+Result: 3:分机错误 7:已登录 0:登录成功
|
|
|
+ */
|
|
|
+const sendSignIn = () => {
|
|
|
+ globalState.callCenterWs = wsRef.value;
|
|
|
+ const sendObj = {
|
|
|
+ Action: "ReqAgentLogin",
|
|
|
+ Param: {
|
|
|
+ JobNum: m_strJobNum.value,
|
|
|
+ Name: m_strUserName.value,
|
|
|
+ Extension: m_strUserNo.value,
|
|
|
+ SkillId: m_strSkillId.value,
|
|
|
+ Level: m_strLevel.value,
|
|
|
+ Role: "",
|
|
|
+ GroupName: m_strGroup.value,
|
|
|
+ OrgId: m_strCompanyId.value,
|
|
|
+ },
|
|
|
+ };
|
|
|
+ // 发送请求
|
|
|
+ e_TelSendMsg(sendObj);
|
|
|
+ console.log(`${getNowDateTime()} 呼叫中心发起签入`);
|
|
|
+};
|
|
|
+// 签入消息回调
|
|
|
+const retSignIn = (data: any) => {
|
|
|
+ if (data.Param.Result === "0") {
|
|
|
+ // 登录成功
|
|
|
+ m_bLogin.value = true;
|
|
|
+
|
|
|
+ // 登录成功
|
|
|
+ globalState.callCenterIsSignIn = true; // 签入状态
|
|
|
+
|
|
|
+ console.log(`${getNowDateTime()} 呼叫中心签入成功回调`);
|
|
|
+ } else if (data.Param.Result === "3") {
|
|
|
+ // 分机错误
|
|
|
+ ElMessage.error("分机错误");
|
|
|
+ wsRef.value.close();
|
|
|
+ // 分机错误
|
|
|
+ } else if (data.Param.Result === "7") {
|
|
|
+ // 已经处于登录状态
|
|
|
+ // 先签出再签入
|
|
|
+ wsRef.value.close();
|
|
|
+ ElMessage.error("当前分机已经签入");
|
|
|
+ }
|
|
|
+};
|
|
|
// 签出
|
|
|
const signOut = () => {
|
|
|
ElMessageBox.confirm(`签出后无法监听和插话,确定签出?`, "提示", {
|
|
@@ -181,90 +499,48 @@ const signOut = () => {
|
|
|
autofocus: false,
|
|
|
})
|
|
|
.then(async () => {
|
|
|
- callCenterLogout();
|
|
|
+ sendSignOut();
|
|
|
})
|
|
|
.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,
|
|
|
+/*
|
|
|
+ * 签出
|
|
|
+ * ReqAgentLogout - 签出方法名称
|
|
|
+ * Extension - 分机号码
|
|
|
+ */
|
|
|
+const sendSignOut = () => {
|
|
|
+ const objMsg = {
|
|
|
+ Action: "ReqAgentLogout",
|
|
|
+ Param: {
|
|
|
+ Extension: m_strUserNo.value,
|
|
|
},
|
|
|
- });
|
|
|
- console.log(`${getNowDateTime()}:开始链接呼叫中心`);
|
|
|
+ };
|
|
|
+ // 发送请求
|
|
|
+ e_TelSendMsg(objMsg);
|
|
|
};
|
|
|
-// 呼叫中心链接成功
|
|
|
-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) => {
|
|
|
+/*
|
|
|
+ * 签出事件
|
|
|
+ */
|
|
|
+const retSignOut = () => {
|
|
|
+ // 登出成功
|
|
|
+ m_strTelState.value = "0";
|
|
|
+ m_bLogin.value = false;
|
|
|
globalState.callCenterIsSignIn = false; // 签出状态
|
|
|
- console.log(`${getNowDateTime()}:呼叫中心链接错误`, e);
|
|
|
+ globalState.callCenterWs = null;
|
|
|
+ wsRef.value.close();
|
|
|
+ console.log(`${getNowDateTime()} 呼叫中心签出回调`);
|
|
|
};
|
|
|
-// 呼叫中心消息
|
|
|
-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;
|
|
|
- }
|
|
|
+/**
|
|
|
+ * 异常处理
|
|
|
+ * @param {any} data
|
|
|
+ */
|
|
|
+const retResError = (data: any) => {
|
|
|
+ if (data.Param.Result == "99") {
|
|
|
+ // 掉线
|
|
|
+
|
|
|
+ globalState.callCenterWs = null;
|
|
|
+ ElMessage.error("连接已断开");
|
|
|
+ // 自动签入
|
|
|
}
|
|
|
};
|
|
|
// 空闲数量
|
|
@@ -336,11 +612,9 @@ onMounted(() => {
|
|
|
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);
|
|
|
}
|
|
|
});
|