|
@@ -278,6 +278,13 @@ import ccPhoneBarSocket from '@/assets/callCenterPhoneBarSdk/ccPhoneBarSocket.js
|
|
|
import { EventList, VideoLevels, AgentStatusEnum } from '@/assets/callCenterPhoneBarSdk/constants.js';
|
|
import { EventList, VideoLevels, AgentStatusEnum } from '@/assets/callCenterPhoneBarSdk/constants.js';
|
|
|
import { myCallUser, getToolbarBasicParam } from '@/api/aiSipCall/aiSipCallUser.js';
|
|
import { myCallUser, getToolbarBasicParam } from '@/api/aiSipCall/aiSipCallUser.js';
|
|
|
import { syncByUuid } from '@/api/aiSipCall/aiSipCallOutboundCdr.js';
|
|
import { syncByUuid } from '@/api/aiSipCall/aiSipCallOutboundCdr.js';
|
|
|
|
|
+import {
|
|
|
|
|
+ beginCCPhoneBarInit,
|
|
|
|
|
+ finishCCPhoneBarInitFailed,
|
|
|
|
|
+ finishCCPhoneBarInitSuccess,
|
|
|
|
|
+ getConnectedSharedCCPhoneBar,
|
|
|
|
|
+ incrementSharedCCPhoneBarRef
|
|
|
|
|
+} from '@/utils/ccPhoneBarShared';
|
|
|
|
|
|
|
|
// IPCC 和 JsSIP 默认配置已从 softPhone.js 统一导入,此处仅作别名引用
|
|
// IPCC 和 JsSIP 默认配置已从 softPhone.js 统一导入,此处仅作别名引用
|
|
|
const IPCC_CONFIG = IPCC_DEFAULTS;
|
|
const IPCC_CONFIG = IPCC_DEFAULTS;
|
|
@@ -420,6 +427,8 @@ export default {
|
|
|
// ccPhoneBar 自动重连相关
|
|
// ccPhoneBar 自动重连相关
|
|
|
_ccReconnectTimer: null,
|
|
_ccReconnectTimer: null,
|
|
|
_ccReconnectAttempts: 0,
|
|
_ccReconnectAttempts: 0,
|
|
|
|
|
+ _ccLoginConflict: false,
|
|
|
|
|
+ _initCCPromise: null,
|
|
|
_isDestroying: false,
|
|
_isDestroying: false,
|
|
|
_ccEventsBoundFor: null,
|
|
_ccEventsBoundFor: null,
|
|
|
|
|
|
|
@@ -975,16 +984,49 @@ export default {
|
|
|
|
|
|
|
|
// ==================== 呼叫中心集成 ====================
|
|
// ==================== 呼叫中心集成 ====================
|
|
|
async initCCAndStart() {
|
|
async initCCAndStart() {
|
|
|
- this._isDestroying = false;
|
|
|
|
|
- this.ccSocketConnected = false;
|
|
|
|
|
- this.ccSocketFailed = false;
|
|
|
|
|
- this.isCallingReady = false;
|
|
|
|
|
|
|
+ if (this._ccLoginConflict) {
|
|
|
|
|
+ this.showStatus('账号已在其他窗口登录,请关闭多余软电话后点击重新连接', 'error');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this._initCCPromise) {
|
|
|
|
|
+ return this._initCCPromise;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const shared = getConnectedSharedCCPhoneBar();
|
|
|
|
|
+ if (shared) {
|
|
|
|
|
+ this._isDestroying = false;
|
|
|
|
|
+ if (this.ccPhoneBar !== shared) {
|
|
|
|
|
+ this.ccPhoneBar = shared;
|
|
|
|
|
+ this._isSharedCCPhoneBar = true;
|
|
|
|
|
+ incrementSharedCCPhoneBarRef();
|
|
|
|
|
+ }
|
|
|
|
|
+ this.ccSocketConnected = true;
|
|
|
|
|
+ this.ccSocketFailed = false;
|
|
|
|
|
+ this._bindCCEvents();
|
|
|
|
|
+ if (!this.phone || !this.isRegistered) {
|
|
|
|
|
+ await this.startPhone();
|
|
|
|
|
+ }
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this._initCCPromise = (async () => {
|
|
|
|
|
+ this._isDestroying = false;
|
|
|
|
|
+ this.ccSocketFailed = false;
|
|
|
|
|
+ this.isCallingReady = false;
|
|
|
|
|
+ try {
|
|
|
|
|
+ await this.ensureCCSocketConnect();
|
|
|
|
|
+ await this.startPhone();
|
|
|
|
|
+ } catch (err) {
|
|
|
|
|
+ this.ccSocketFailed = true;
|
|
|
|
|
+ this.showStatus(`初始化失败: ${err.message}`, 'error');
|
|
|
|
|
+ throw err;
|
|
|
|
|
+ }
|
|
|
|
|
+ })();
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
- await this.ensureCCSocketConnect();
|
|
|
|
|
- await this.startPhone();
|
|
|
|
|
- } catch (err) {
|
|
|
|
|
- this.ccSocketFailed = true;
|
|
|
|
|
- this.showStatus(`初始化失败: ${err.message}`, 'error');
|
|
|
|
|
|
|
+ await this._initCCPromise;
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this._initCCPromise = null;
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
ensureCCSocketConnect() {
|
|
ensureCCSocketConnect() {
|
|
@@ -999,23 +1041,23 @@ export default {
|
|
|
},
|
|
},
|
|
|
async _doConnectCCSocket() {
|
|
async _doConnectCCSocket() {
|
|
|
try {
|
|
try {
|
|
|
- // 优先复用已连接的共享 ccPhoneBar 实例,避免同一分机号重复登录导致 status:201 冲突
|
|
|
|
|
- const shared = window.__sharedCCPhoneBar;
|
|
|
|
|
- if (shared && typeof shared.getIsConnected === 'function' && shared.getIsConnected()) {
|
|
|
|
|
|
|
+ const shared = getConnectedSharedCCPhoneBar();
|
|
|
|
|
+ if (shared) {
|
|
|
console.log('[FloatingSoftPhone] 检测到已连接的共享 ccPhoneBar,直接复用');
|
|
console.log('[FloatingSoftPhone] 检测到已连接的共享 ccPhoneBar,直接复用');
|
|
|
this.ccPhoneBar = shared;
|
|
this.ccPhoneBar = shared;
|
|
|
this._isSharedCCPhoneBar = true;
|
|
this._isSharedCCPhoneBar = true;
|
|
|
this.ccSocketConnected = true;
|
|
this.ccSocketConnected = true;
|
|
|
this.ccSocketFailed = false;
|
|
this.ccSocketFailed = false;
|
|
|
- this.isCallingReady = true; // 复用时 IPCC 已连接且坐席已置忙,直接就绪
|
|
|
|
|
- // 增加引用计数,防止创建者在其他组件仍使用时断开连接
|
|
|
|
|
- window.__sharedCCPhoneBarRefCount = (window.__sharedCCPhoneBarRefCount || 0) + 1;
|
|
|
|
|
|
|
+ this.isCallingReady = true;
|
|
|
|
|
+ incrementSharedCCPhoneBarRef();
|
|
|
this._bindCCEvents();
|
|
this._bindCCEvents();
|
|
|
if (this.ccConnectingResolve) this.ccConnectingResolve();
|
|
if (this.ccConnectingResolve) this.ccConnectingResolve();
|
|
|
this.ccConnectingPromise = null;
|
|
this.ccConnectingPromise = null;
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ beginCCPhoneBarInit();
|
|
|
|
|
+
|
|
|
const extRes = await myCallUser();
|
|
const extRes = await myCallUser();
|
|
|
if (extRes.code !== 200 || !extRes.data || !extRes.data.extNum) {
|
|
if (extRes.code !== 200 || !extRes.data || !extRes.data.extNum) {
|
|
|
throw new Error('未查询到分机号信息');
|
|
throw new Error('未查询到分机号信息');
|
|
@@ -1065,6 +1107,7 @@ export default {
|
|
|
if (originalResolve) originalResolve();
|
|
if (originalResolve) originalResolve();
|
|
|
};
|
|
};
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
|
|
+ finishCCPhoneBarInitFailed(err);
|
|
|
if (this.ccConnectingReject) this.ccConnectingReject(err);
|
|
if (this.ccConnectingReject) this.ccConnectingReject(err);
|
|
|
this.ccConnectingPromise = null;
|
|
this.ccConnectingPromise = null;
|
|
|
throw err;
|
|
throw err;
|
|
@@ -1074,26 +1117,35 @@ export default {
|
|
|
if (this._ccEventsBoundFor === this.ccPhoneBar) return;
|
|
if (this._ccEventsBoundFor === this.ccPhoneBar) return;
|
|
|
this._ccEventsBoundFor = this.ccPhoneBar;
|
|
this._ccEventsBoundFor = this.ccPhoneBar;
|
|
|
this.ccPhoneBar.on(EventList.WS_CONNECTED, () => {
|
|
this.ccPhoneBar.on(EventList.WS_CONNECTED, () => {
|
|
|
- // 注册为共享实例,供其他组件复用
|
|
|
|
|
|
|
+ finishCCPhoneBarInitSuccess(this.ccPhoneBar);
|
|
|
window.__sharedCCPhoneBar = this.ccPhoneBar;
|
|
window.__sharedCCPhoneBar = this.ccPhoneBar;
|
|
|
- window.__sharedCCPhoneBarRefCount = 1; // 创建者初始引用计数为1
|
|
|
|
|
|
|
+ window.__sharedCCPhoneBarRefCount = 1;
|
|
|
this.ccSocketConnected = true;
|
|
this.ccSocketConnected = true;
|
|
|
this.ccSocketFailed = false;
|
|
this.ccSocketFailed = false;
|
|
|
- // 重置重连计数
|
|
|
|
|
|
|
+ this._ccLoginConflict = false;
|
|
|
this._ccReconnectAttempts = 0;
|
|
this._ccReconnectAttempts = 0;
|
|
|
if (this.ccConnectingResolve) this.ccConnectingResolve();
|
|
if (this.ccConnectingResolve) this.ccConnectingResolve();
|
|
|
this.ccConnectingPromise = null;
|
|
this.ccConnectingPromise = null;
|
|
|
- // 通知其他组件共享实例已更新(如 aiSipCallManualOutbound)
|
|
|
|
|
this.$root.$emit('cc-phonebar-reconnected', this.ccPhoneBar);
|
|
this.$root.$emit('cc-phonebar-reconnected', this.ccPhoneBar);
|
|
|
console.log('[FloatingSoftPhone] ccPhoneBar 连接成功,已注册为共享实例');
|
|
console.log('[FloatingSoftPhone] ccPhoneBar 连接成功,已注册为共享实例');
|
|
|
});
|
|
});
|
|
|
|
|
+ this.ccPhoneBar.on(EventList.USER_LOGIN_ON_OTHER_DEVICE, (msg) => {
|
|
|
|
|
+ console.warn('[FloatingSoftPhone] 检测到重复登录 status:201', msg);
|
|
|
|
|
+ this._ccLoginConflict = true;
|
|
|
|
|
+ this._ccReconnectAttempts = 999;
|
|
|
|
|
+ if (this._ccReconnectTimer) {
|
|
|
|
|
+ clearTimeout(this._ccReconnectTimer);
|
|
|
|
|
+ this._ccReconnectTimer = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ finishCCPhoneBarInitFailed(new Error('user_logined_on_other_device'));
|
|
|
|
|
+ this.showStatus('软电话已在其他窗口登录,请关闭多余页面后重新连接', 'error');
|
|
|
|
|
+ });
|
|
|
this.ccPhoneBar.on(EventList.WS_DISCONNECTED, () => {
|
|
this.ccPhoneBar.on(EventList.WS_DISCONNECTED, () => {
|
|
|
console.log('[FloatingSoftPhone] ccPhoneBar WebSocket 断开');
|
|
console.log('[FloatingSoftPhone] ccPhoneBar WebSocket 断开');
|
|
|
this.ccSocketConnected = false;
|
|
this.ccSocketConnected = false;
|
|
|
this.isCallingReady = false;
|
|
this.isCallingReady = false;
|
|
|
if (this.phone && this.isRegistered) this.showStatus('连接断开', 'warn');
|
|
if (this.phone && this.isRegistered) this.showStatus('连接断开', 'warn');
|
|
|
- // 非主动销毁时自动重连
|
|
|
|
|
- if (!this._isDestroying) {
|
|
|
|
|
|
|
+ if (!this._isDestroying && !this._ccLoginConflict) {
|
|
|
this._scheduleCCReconnect();
|
|
this._scheduleCCReconnect();
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
@@ -1195,6 +1247,7 @@ export default {
|
|
|
* 使用指数退避策略,最多重试 5 次,避免无限重连消耗资源
|
|
* 使用指数退避策略,最多重试 5 次,避免无限重连消耗资源
|
|
|
*/
|
|
*/
|
|
|
_scheduleCCReconnect() {
|
|
_scheduleCCReconnect() {
|
|
|
|
|
+ if (this._ccLoginConflict) return;
|
|
|
if (this._ccReconnectTimer) clearTimeout(this._ccReconnectTimer);
|
|
if (this._ccReconnectTimer) clearTimeout(this._ccReconnectTimer);
|
|
|
const maxAttempts = 5;
|
|
const maxAttempts = 5;
|
|
|
if (this._ccReconnectAttempts >= maxAttempts) {
|
|
if (this._ccReconnectAttempts >= maxAttempts) {
|
|
@@ -1214,8 +1267,8 @@ export default {
|
|
|
window.__sharedCCPhoneBar = null;
|
|
window.__sharedCCPhoneBar = null;
|
|
|
window.__sharedCCPhoneBarRefCount = 0;
|
|
window.__sharedCCPhoneBarRefCount = 0;
|
|
|
}
|
|
}
|
|
|
- // 清理旧的 ccPhoneBar 引用
|
|
|
|
|
if (this.ccPhoneBar) {
|
|
if (this.ccPhoneBar) {
|
|
|
|
|
+ try { this.ccPhoneBar.disconnect(); } catch (e) { /* ignore */ }
|
|
|
this.ccPhoneBar = null;
|
|
this.ccPhoneBar = null;
|
|
|
}
|
|
}
|
|
|
this.ccConnectingPromise = null;
|
|
this.ccConnectingPromise = null;
|
|
@@ -1663,6 +1716,7 @@ export default {
|
|
|
},
|
|
},
|
|
|
async resetReconnectState() {
|
|
async resetReconnectState() {
|
|
|
this.showStatus('正在重置...', 'info');
|
|
this.showStatus('正在重置...', 'info');
|
|
|
|
|
+ this._ccLoginConflict = false;
|
|
|
// 清除自动重连定时器和计数
|
|
// 清除自动重连定时器和计数
|
|
|
if (this._ccReconnectTimer) { clearTimeout(this._ccReconnectTimer); this._ccReconnectTimer = null; }
|
|
if (this._ccReconnectTimer) { clearTimeout(this._ccReconnectTimer); this._ccReconnectTimer = null; }
|
|
|
this._ccReconnectAttempts = 0;
|
|
this._ccReconnectAttempts = 0;
|