From 91f78c7a3c325b7627f269524cdf92f006948cdf Mon Sep 17 00:00:00 2001
From: WXL (wul) <wl_5969728@163.com>
Date: 星期一, 20 十月 2025 17:37:35 +0800
Subject: [PATCH] 景宁电话接入
---
src/views/followvisit/discharge/ClickCall copy.vue | 1258 +++++++++++++++++++++++++++++
dist.zip | 0
vue.config.js | 4
src/api/AiCentre/phoneCall.js | 4
src/views/followvisit/record/detailpage/index.vue | 62
src/views/followvisit/discharge/ClickCall.vue | 622 ++++++++++---
src/components/CallCenterLs/index.vue | 614 ++++++++++++++
7 files changed, 2,371 insertions(+), 193 deletions(-)
diff --git a/dist.zip b/dist.zip
index b6e6235..829d089 100644
--- a/dist.zip
+++ b/dist.zip
Binary files differ
diff --git a/src/api/AiCentre/phoneCall.js b/src/api/AiCentre/phoneCall.js
index 84769dc..f2f5124 100644
--- a/src/api/AiCentre/phoneCall.js
+++ b/src/api/AiCentre/phoneCall.js
@@ -1,11 +1,13 @@
import request from "@/utils/request";
-
// 鍒犻櫎澶栭儴鎮h�呰〃
export function CallgetList() {
return request({
url: "/smartor/ServiceTelInfo/getList",
method: "get",
+ params: {
+ orgid: localStorage.getItem("orgid"),
+ },
});
}
// 鏌ヨ澶栭儴鎮h�呰〃
diff --git a/src/components/CallCenterLs/index.vue b/src/components/CallCenterLs/index.vue
new file mode 100644
index 0000000..2114df6
--- /dev/null
+++ b/src/components/CallCenterLs/index.vue
@@ -0,0 +1,614 @@
+<template>
+ <div class="call-center-container">
+ <!-- 涓绘帶鍒跺尯 -->
+ <div class="control-section">
+ <div class="phone-control-card">
+ <h3 class="section-title">鐢佃瘽鎺у埗</h3>
+ <div class="input-group">
+ <div class="form-field">
+ <label class="form-label">瀹㈡埛鐢佃瘽鍙风爜</label>
+ <input
+ v-model="customerPhone"
+ type="text"
+ placeholder="璇疯緭鍏ョ數璇濆彿鐮�"
+ :disabled="isCalling"
+ class="phone-input"
+ />
+ </div>
+
+ <div class="button-group">
+ <button
+ @click="handleCall"
+ :class="['call-btn', callButtonClass]"
+ :disabled="!canMakeCall"
+ >
+ <span class="btn-icon">馃摓</span>
+ {{ callButtonText }}
+ </button>
+
+ <button
+ @click="handleHangup"
+ class="hangup-btn"
+ :disabled="!canHangup"
+ >
+ <span class="btn-icon">馃摰</span>
+ 鎸傛柇
+ </button>
+ </div>
+ </div>
+ </div>
+
+ <!-- 鐘舵�佹樉绀哄尯 -->
+ <div class="status-card">
+ <h3 class="section-title">鐘舵�佺洃鎺�</h3>
+ <div class="status-grid">
+ <div class="status-item">
+ <span class="status-label">搴у腑鐘舵��:</span>
+ <span :class="['status-indicator', seatStatusClass]">
+ <span class="status-dot"></span>
+ {{ seatStatusText }}
+ </span>
+ </div>
+
+ <div class="status-item">
+ <span class="status-label">閫氳瘽鐘舵��:</span>
+ <span :class="['status-indicator', callStatusClass]">
+ <span class="status-dot"></span>
+ {{ callStatusText }}
+ </span>
+ </div>
+
+ <div class="status-item" v-if="callDuration">
+ <span class="status-label">閫氳瘽鏃堕暱:</span>
+ <span class="duration-display">
+ 鈴憋笍 {{ callDuration }}
+ </span>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- 璋冭瘯闈㈡澘 -->
+ <div class="debug-section">
+ <el-collapse accordion>
+ <el-collapse-item name="debug">
+ <template slot="title">
+ <div class="debug-header">
+ <span class="debug-title">鍛煎彨璋冭瘯鏃ュ織</span>
+ <span class="debug-subtitle">鐐瑰嚮鏌ョ湅璇︾粏閫氳瘽淇℃伅</span>
+ </div>
+ </template>
+ <div class="debug-content">
+ <WebsocketDemo
+ ref="callComponent"
+ :customer-phone="customerPhone"
+ :auto-login="true"
+ @status-change="onSeatStatusChange"
+ @call-status="onCallStatusChange"
+ @error="onCallError"
+ class="call-component"
+ />
+ </div>
+ </el-collapse-item>
+ </el-collapse>
+ </div>
+ </div>
+</template>
+
+<script>
+import WebsocketDemo from "../../views/followvisit/discharge/ClickCall.vue";
+
+export default {
+ name: "CallCenterModal",
+ components: {
+ WebsocketDemo,
+ },
+
+ props: {
+ initialPhone: {
+ type: String,
+ default: "",
+ },
+ },
+
+ data() {
+ return {
+ customerPhone: "",
+ isSeatLoggedIn: false,
+ callStatus: "idle",
+ callStartTime: null,
+ callDuration: "00:00",
+ durationTimer: null,
+ lastError: null,
+ };
+ },
+
+ computed: {
+ isCalling() {
+ return this.callStatus === "calling";
+ },
+
+ isInCall() {
+ return this.callStatus === "connected";
+ },
+
+ canMakeCall() {
+ return (
+ this.isSeatLoggedIn &&
+ this.customerPhone &&
+ this.callStatus === "idle" &&
+ !this.lastError
+ );
+ },
+
+ canHangup() {
+ return this.isCalling || this.isInCall;
+ },
+
+ callButtonClass() {
+ if (!this.canMakeCall) return "disabled";
+ return this.isCalling ? "calling" : "idle";
+ },
+
+ callButtonText() {
+ if (this.isCalling) return "鍛煎彨涓�...";
+ if (this.isInCall) return "閫氳瘽涓�";
+ return "寮�濮嬪懠鍙�";
+ },
+
+ seatStatusClass() {
+ return this.isSeatLoggedIn ? "success" : "error";
+ },
+
+ seatStatusText() {
+ return this.isSeatLoggedIn ? "宸茬鍏�" : "鏈鍏�";
+ },
+
+ callStatusClass() {
+ switch (this.callStatus) {
+ case "connected":
+ return "success";
+ case "calling":
+ return "warning";
+ default:
+ return "idle";
+ }
+ },
+
+ callStatusText() {
+ switch (this.callStatus) {
+ case "connected":
+ return "閫氳瘽涓�";
+ case "calling":
+ return "鍛煎彨涓�";
+ default:
+ return "绌洪棽";
+ }
+ },
+ },
+
+ watch: {
+ initialPhone: {
+ immediate: true,
+ handler(newVal) {
+ if (newVal) {
+ this.customerPhone = newVal;
+ }
+ },
+ },
+ },
+
+ methods: {
+ handleCall() {
+ if (!this.canMakeCall) return;
+ this.$refs.callComponent.callout(this.customerPhone);
+ this.startCallTimer();
+ },
+
+ handleHangup() {
+ this.$refs.callComponent.hangup();
+ this.stopCallTimer();
+ this.callStatus = "idle";
+ },
+
+ onSeatStatusChange(status) {
+ this.isSeatLoggedIn = status.isLoggedIn;
+ },
+
+ onCallStatusChange(status) {
+ this.callStatus = status.status;
+ if (status.status === "connected") {
+ this.startCallTimer();
+ } else if (status.status === "idle") {
+ this.stopCallTimer();
+ }
+ },
+
+ onCallError(error) {
+ this.lastError = error;
+ this.$emit("error", error);
+ },
+
+ startCallTimer() {
+ this.callStartTime = new Date();
+ this.durationTimer = setInterval(() => {
+ if (this.callStartTime) {
+ const duration = Math.floor((new Date() - this.callStartTime) / 1000);
+ const minutes = Math.floor(duration / 60)
+ .toString()
+ .padStart(2, "0");
+ const seconds = (duration % 60).toString().padStart(2, "0");
+ this.callDuration = `${minutes}:${seconds}`;
+ }
+ }, 1000);
+ },
+
+ stopCallTimer() {
+ if (this.durationTimer) {
+ clearInterval(this.durationTimer);
+ this.durationTimer = null;
+ }
+ this.callDuration = "00:00";
+ this.callStartTime = null;
+ },
+
+ handleSeatBusy() {
+ this.$refs.callComponent.afk();
+ },
+
+ handleSeatReady() {
+ this.$refs.callComponent.online();
+ },
+
+ handleHold() {
+ this.$refs.callComponent.hold();
+ },
+
+ handleResume() {
+ this.$refs.callComponent.holdresume();
+ },
+
+ // 鎻愪緵缁欑埗缁勪欢璋冪敤鐨勬柟娉�
+ setPhoneNumber(phone) {
+ this.customerPhone = phone;
+ },
+
+ autoCall(phone) {
+ this.setPhoneNumber(phone);
+ this.$nextTick(() => {
+ this.handleCall();
+ });
+ },
+ },
+
+ beforeUnmount() {
+ this.stopCallTimer();
+ },
+};
+</script>
+
+<style lang="scss" scoped>
+.call-center-container {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ padding: 0;
+ background: #f8fafc;
+}
+
+// 鎺у埗鍖哄煙鏍峰紡
+.control-section {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 16px;
+ padding: 20px;
+ padding-bottom: 0;
+
+ @media (max-width: 1024px) {
+ grid-template-columns: 1fr;
+ }
+}
+
+.section-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #1e293b;
+ margin-bottom: 16px;
+ display: flex;
+ align-items: center;
+
+ &::before {
+ content: "";
+ width: 3px;
+ height: 16px;
+ background: #3b82f6;
+ margin-right: 8px;
+ border-radius: 2px;
+ }
+}
+
+// 鍗$墖閫氱敤鏍峰紡
+.phone-control-card,
+.status-card {
+ background: white;
+ padding: 20px;
+ border-radius: 12px;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+ border: 1px solid #e2e8f0;
+}
+
+// 琛ㄥ崟鎺т欢鏍峰紡
+.form-field {
+ margin-bottom: 20px;
+}
+
+.form-label {
+ display: block;
+ font-weight: 500;
+ color: #475569;
+ margin-bottom: 8px;
+ font-size: 14px;
+}
+
+.phone-input {
+ width: 100%;
+ padding: 12px;
+ border: 2px solid #e2e8f0;
+ border-radius: 8px;
+ font-size: 14px;
+ transition: all 0.3s ease;
+
+ &:focus {
+ outline: none;
+ border-color: #3b82f6;
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
+ }
+
+ &:disabled {
+ background-color: #f8fafc;
+ color: #94a3b8;
+ cursor: not-allowed;
+ }
+}
+
+.button-group {
+ display: flex;
+ gap: 12px;
+ flex-wrap: wrap;
+}
+
+// 鎸夐挳鍩虹鏍峰紡
+.call-btn,
+.hangup-btn {
+ padding: 12px 28px; /* 澧炲姞鍐呰竟璺濓紝浣挎寜閽洿澶ф柟 */
+ border: none;
+ border-radius: 12px; /* 浣跨敤鏇村ぇ鐨勫渾瑙掞紝鍒涢�犫�滆嵂涓糕�濆舰 */
+ cursor: pointer;
+ font-size: 14px;
+ font-weight: 600; /* 瀛椾綋鍔犵矖 */
+ transition: all 0.3s ease; /* 骞虫粦杩囨浮鎵�鏈夊睘鎬� */
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8px; /* 鍥炬爣鍜屾枃瀛楃殑闂磋窛 */
+ min-width: 120px; /* 璁剧疆鏈�灏忓搴� */
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15), 0 2px 4px rgba(0, 0, 0, 0.1); /* 澶氬眰闃村奖澧炲己绔嬩綋鎰� */
+}
+
+.call-btn {
+ background: linear-gradient(135deg, #10b981, #059669); /* 缁胯壊娓愬彉 */
+ color: white;
+}
+
+.call-btn:hover:not(.disabled) {
+ transform: translateY(-2px); /* 鎮仠鏃惰交寰笂娴� */
+ box-shadow: 0 6px 16px rgba(16, 185, 129, 0.4); /* 鎮仠鏃堕槾褰辨洿鏄庢樉 */
+}
+
+.call-btn.calling {
+ background: linear-gradient(135deg, #f59e0b, #d97706); /* 鍛煎彨涓姸鎬佹敼涓烘鑹叉笎鍙� */
+ animation: pulse 1.5s infinite; /* 鍛煎彨涓坊鍔犲懠鍚歌剦鍐插姩鐢� */
+}
+
+.hangup-btn {
+ background: linear-gradient(135deg, #ef4444, #dc2626); /* 绾㈣壊娓愬彉 */
+ color: white;
+}
+
+.hangup-btn:hover:not(:disabled) {
+ transform: translateY(-2px);
+ box-shadow: 0 6px 16px rgba(239, 68, 68, 0.4);
+}
+
+/* 绂佺敤鐘舵�� */
+.call-btn.disabled,
+.hangup-btn:disabled {
+ background: #cbd5e1 !important;
+ cursor: not-allowed;
+ transform: none !important;
+ box-shadow: none !important;
+}
+
+/* 鑴夊啿鍔ㄧ敾瀹氫箟 */
+@keyframes pulse {
+ 0% { box-shadow: 0 4px 12px rgba(245, 158, 11, 0.5); }
+ 50% { box-shadow: 0 4px 20px rgba(245, 158, 11, 0.8); }
+ 100% { box-shadow: 0 4px 12px rgba(245, 158, 11, 0.5); }
+}
+
+// 鐘舵�佹樉绀烘牱寮�
+.status-grid {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+.status-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 12px;
+ background: #f8fafc;
+ border-radius: 8px;
+}
+
+.status-label {
+ font-size: 14px;
+ color: #475569;
+ font-weight: 500;
+}
+
+.status-indicator {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 13px;
+ font-weight: 500;
+ padding: 4px 12px;
+ border-radius: 20px;
+}
+
+.status-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ animation: pulse 2s infinite;
+}
+
+.status-indicator.success {
+ background: #f0fdf4;
+ color: #059669;
+
+ .status-dot {
+ background: #059669;
+ }
+}
+
+.status-indicator.error {
+ background: #fef2f2;
+ color: #dc2626;
+
+ .status-dot {
+ background: #dc2626;
+ }
+}
+
+.status-indicator.warning {
+ background: #fffbeb;
+ color: #d97706;
+
+ .status-dot {
+ background: #f59e0b;
+ }
+}
+
+.status-indicator.idle {
+ background: #f8fafc;
+ color: #64748b;
+
+ .status-dot {
+ background: #94a3b8;
+ }
+}
+
+.duration-display {
+ font-family: 'Courier New', monospace;
+ font-weight: 600;
+ color: #059669;
+ font-size: 14px;
+}
+
+// 璋冭瘯闈㈡澘鏍峰紡
+.debug-section {
+ background: white;
+ margin: 0 20px 20px;
+ padding: 20px;
+ border-radius: 12px;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+
+.debug-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ padding-right: 20px;
+}
+
+.debug-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #1e293b;
+ margin-bottom: 16px;
+ display: flex;
+ align-items: center;
+
+ &::before {
+ content: "";
+ width: 3px;
+ height: 16px;
+ background: #3b82f6;
+ margin-right: 8px;
+ border-radius: 2px;
+ }
+}
+
+.debug-subtitle {
+ font-size: 12px;
+ color: #64748b;
+}
+
+.debug-content {
+ padding: 0;
+}
+
+.call-component {
+ min-height: 200px;
+ border-top: 1px solid #e2e8f0;
+}
+
+// 鍔ㄧ敾瀹氫箟
+@keyframes pulse {
+ 0% { opacity: 1; }
+ 50% { opacity: 0.5; }
+ 100% { opacity: 1; }
+}
+
+// 鍝嶅簲寮忚璁�
+@media (max-width: 768px) {
+ .control-section {
+ padding: 16px;
+ grid-template-columns: 1fr;
+ }
+
+ .phone-control-card,
+ .status-card {
+ padding: 16px;
+ }
+
+ .button-group {
+ flex-direction: column;
+ }
+
+ .status-item {
+ flex-direction: column;
+ gap: 8px;
+ align-items: flex-start;
+ }
+
+ .debug-section {
+ margin: 0 16px 16px;
+ }
+}
+
+@media (max-width: 480px) {
+ .call-center-container {
+ gap: 12px;
+ }
+
+ .control-section {
+ padding: 12px;
+ }
+}
+</style>
diff --git a/src/views/followvisit/discharge/ClickCall copy.vue b/src/views/followvisit/discharge/ClickCall copy.vue
new file mode 100644
index 0000000..b922b0c
--- /dev/null
+++ b/src/views/followvisit/discharge/ClickCall copy.vue
@@ -0,0 +1,1258 @@
+<template>
+ <div class="websocket-demo">
+ <div>
+ <h3>Websocket鎺ュ彛娴嬭瘯DEMO</h3>
+ <div class="config-area">
+ <div class="input-group">
+ <label>CTI_WS_URL</label>
+ <input
+ type="text"
+ v-model="config.cti_ws_url"
+ placeholder="ws://40.78.0.169:6688"
+ />
+
+ <label>鍧愬腑宸ュ彿</label>
+ <input type="text" v-model="config.seatname" placeholder="8000" />
+
+ <label>鍧愬腑鍒嗘満</label>
+ <input type="text" v-model="config.seatnum" placeholder="8000" />
+
+ <label>瀵嗙爜</label>
+ <input type="text" v-model="config.password" placeholder="123456" />
+ </div>
+
+ <div class="input-group">
+ <label>澶栫嚎鍙风爜</label>
+ <input type="text" v-model="config.phone" placeholder="10086" />
+
+ <label>UUID</label>
+ <input type="text" v-model="config.uuid" />
+
+ <label>鍏朵粬鍧愬腑</label>
+ <input type="text" v-model="config.other" placeholder="8001" />
+
+ <label>鎶�鑳界粍</label>
+ <input type="text" v-model="config.group" placeholder="a3" />
+
+ <label>澶栧懠鍙傛暟id</label>
+ <input type="text" v-model="config.paramid" placeholder="3" />
+ </div>
+ </div>
+
+ <!-- 鎿嶄綔鎸夐挳鍖哄煙 -->
+ <div class="button-area">
+ <!-- 绗竴琛屾寜閽� -->
+ <div class="button-row">
+ <button @click="seatlogin">绛惧叆</button>
+ <button @click="seatlogout">绛惧嚭</button>
+ <button @click="afk">绀哄繖</button>
+ <button @click="online">绀洪棽</button>
+ <button @click="pickup">浠g瓟</button>
+ </div>
+
+ <!-- 绗簩琛屾寜閽� -->
+ <div class="button-row">
+ <button @click="hangup">鎸傛満</button>
+ <button @click="callout">澶栧懠</button>
+ <button @click="transfer">閫氳瘽杞Щ</button>
+ <button @click="transferresume">閫氳瘽杞Щ鏀跺洖</button>
+ <button @click="hold">閫氳瘽淇濇寔</button>
+ <button @click="holdresume">閫氳瘽淇濇寔鏀跺洖</button>
+ <button @click="remove">閫氳瘽寮烘媶</button>
+ <button @click="insert">閫氳瘽寮烘彃</button>
+ <button @click="monitor">鐩戝惉</button>
+ <button @click="monitor_to_talk">鐩戝惉杞�氳瘽</button>
+ <button @click="monitor_end">鐩戝惉缁撴潫</button>
+ <button @click="choosecall">閫夋嫨</button>
+ <button @click="replacecall">浠f帴</button>
+ <button @click="three">涓夋柟閫氳瘽</button>
+ </div>
+
+ <!-- 绗笁琛屾寜閽� -->
+ <div class="button-row">
+ <button @click="handoff_ready">鍜ㄨ寮�濮�</button>
+ <button @click="handoff_call">鍜ㄨ鍛煎彨</button>
+ <button @click="handoff_resume">鍜ㄨ鏀跺洖</button>
+ <button @click="handoff_transfer">鍜ㄨ杞Щ</button>
+ <button @click="handoff_three">鍜ㄨ涓夋柟</button>
+ <button @click="record_start">寮�濮嬮�氳瘽褰曢煶</button>
+ <button @click="record_stop">鍋滄閫氳瘽褰曢煶</button>
+ </div>
+
+ <!-- 绗洓琛屾寜閽� -->
+ <div class="button-row">
+ <button @click="openseatlist">鎵撳紑鍧愬腑鐘舵��</button>
+ <button @click="closeseatlist">鍏抽棴鍧愬腑鐘舵��</button>
+ <button @click="openqueues">鎵撳紑闃熷垪淇℃伅</button>
+ <button @click="closequeues">鍏抽棴闃熷垪淇℃伅</button>
+ <button @click="opencalllist">鎵撳紑閫氳瘽淇℃伅</button>
+ <button @click="closecalllist">鍏抽棴閫氳瘽淇℃伅</button>
+ <button @click="openroutelist">鎵撳紑璺敱淇℃伅</button>
+ <button @click="closeroutelist">鍏抽棴璺敱淇℃伅</button>
+ </div>
+
+ <!-- 绗簲琛屾寜閽� -->
+ <div class="button-row">
+ <button @click="seatlist">鑾峰彇鍧愬腑淇℃伅</button>
+ <button @click="queues">鑾峰彇闃熷垪淇℃伅</button>
+ <button @click="calllist">鑾峰彇閫氳瘽淇℃伅</button>
+ <button @click="routelist">鑾峰彇璺敱淇℃伅</button>
+ <button @click="batch">鑾峰彇澶栧懠鍙傛暟淇℃伅</button>
+ <button @click="batch_start">寮�濮嬪鍛间换鍔�</button>
+ <button @click="batch_stop">鍋滄澶栧懠浠诲姟</button>
+ </div>
+ </div>
+
+ <!-- 鏃ュ織鏄剧ず鍖哄煙 -->
+ <h3>鍗忚鏃ュ織鍖�<button @click="testclear">娓呴櫎</button></h3>
+ <div id="msg" class="log-area">{{ logs }}</div>
+ </div>
+ </div>
+</template>
+
+<script>
+import { CallsetState, CallgetList } from "@/api/AiCentre/index";
+
+export default {
+ name: "WebsocketDemo",
+
+ data() {
+ return {
+ config: {
+ cti_ws_url: "wss://9.208.2.190:8092/cal-api/",
+ seatname: "8000",
+ seatnum: "8000",
+ password: "123456",
+ phone: "10086",
+ uuid: "",
+ other: "8001",
+ group: "a3",
+ paramid: "3",
+ },
+ randomNum: "",
+ randomID: "",
+ logs: "",
+ ws: null,
+ isConnected: false,
+ };
+ },
+
+ mounted() {
+ this.CallgetList();
+ this.initializeWebSocket();
+ },
+
+ beforeUnmount() {
+ this.disconnectWebSocket();
+ },
+
+ methods: {
+ // 鍒濆鍖朩ebSocket杩炴帴
+ initializeWebSocket() {
+ try {
+ // 鏍规嵁褰撳墠椤甸潰鍗忚鑷姩閫夋嫨WS鍗忚
+ const isHttps = window.location.protocol === "https:";
+ this.config.cti_ws_url = isHttps
+ ? "wss://9.208.2.190:8092/cal-api/"
+ : "ws://40.78.0.169:6688";
+
+ if (typeof window.WebSocket === "undefined") {
+ this.addLog("閿欒: 娴忚鍣ㄤ笉鏀寔WebSocket");
+ return;
+ }
+
+ this.connectWebSocket();
+ } catch (error) {
+ this.addLog(`鍒濆鍖朩ebSocket閿欒: ${error.message}`);
+ // 灏濊瘯浣跨敤澶囩敤鍦板潃
+ this.config.cti_ws_url = "wss://9.208.2.190:8092/cal-api/";
+ setTimeout(() => this.connectWebSocket(), 2000);
+ }
+ },
+ // 鏌ヨ鍙敤鍒嗘満鍙�
+ async CallgetList() {
+ try {
+ const res = await CallgetList();
+ this.randomNum = res.data[0].tel;
+ this.randomID = res.data[0].id;
+ // 姝g‘璁剧疆 sipUri
+ this.config.seatname = randomNum;
+ this.config.seatnum = randomNum;
+ this.startCallsetState();
+ } catch (error) {
+ console.error("鑾峰彇搴у腑鍙峰け璐�:", error);
+ this.updateStatus("failed", "鑾峰彇搴у腑鍙峰け璐�");
+ }
+ },
+ //浣跨敤搴у腑鍙�
+ async startCallsetState() {
+ try {
+ await CallsetState({ id: this.randomID, state: 1 });
+ console.log("搴у腑鍙风姸鎬佹洿鏂颁负浣跨敤涓�");
+ } catch (error) {
+ console.error("鏇存柊搴у腑鍙风姸鎬佸け璐�:", error);
+ }
+ },
+ //閲婃斁搴у腑鍙�
+ async overCallsetState() {
+ try {
+ if (this.randomID) {
+ await CallsetState({ id: this.randomID, state: 0 });
+ console.log("搴у腑鍙风姸鎬佹洿鏂颁负鍙敤");
+ }
+ } catch (error) {
+ console.error("閲婃斁搴у腑鍙峰け璐�:", error);
+ }
+ },
+ // 杩炴帴WebSocket
+ connectWebSocket() {
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
+ this.addLog("WebSocket宸茶繛鎺�");
+ return;
+ }
+
+ try {
+ let wsUrl = this.config.cti_ws_url;
+ // 纭繚HTTPS椤甸潰浣跨敤WSS
+ if (
+ window.location.protocol === "https:" &&
+ wsUrl.startsWith("ws://")
+ ) {
+ wsUrl = wsUrl.replace("ws://", "wss://");
+ }
+
+ this.ws = new WebSocket(wsUrl);
+
+ this.ws.onopen = () => {
+ this.isConnected = true;
+ this.addLog("WebSocket杩炴帴鎴愬姛");
+ };
+
+ this.ws.onmessage = (event) => {
+ this.handleWebSocketMessage(event);
+ };
+
+ this.ws.onclose = (event) => {
+ this.isConnected = false;
+ this.addLog(`WebSocket杩炴帴鍏抽棴: ${event.code} ${event.reason}`);
+ // 鑷姩閲嶈繛
+ setTimeout(() => this.connectWebSocket(), 3000);
+ };
+
+ this.ws.onerror = (error) => {
+ this.addLog(`WebSocket閿欒: ${error.message}`);
+ // 灏濊瘯澶囩敤URL
+ if (!wsUrl.includes("9.208.2.190")) {
+ this.config.cti_ws_url = "wss://9.208.2.190:8092/cal-api/";
+ setTimeout(() => this.connectWebSocket(), 3000);
+ }
+ };
+ } catch (error) {
+ this.addLog(`杩炴帴WebSocket澶辫触: ${error.message}`);
+ // 灏濊瘯澶囩敤URL
+ this.config.cti_ws_url = "wss://9.208.2.190:8092/cal-api/";
+ setTimeout(() => this.connectWebSocket(), 3000);
+ }
+ },
+
+ // 澶勭悊WebSocket娑堟伅
+ handleWebSocketMessage(event) {
+ const reader = new FileReader();
+ reader.onloadend = (e) => {
+ const message = reader.result;
+ this.addLog(`鏀跺埌娑堟伅: ${message}`);
+
+ try {
+ const obj = JSON.parse(message);
+
+ // 澶勭悊蹇冭烦鍖�
+ if (obj.cmd === "system" && obj.action === "keepalive") {
+ this.keepalive(obj.seatname, obj.seatnum);
+ }
+
+ // 鑷姩璁剧疆UUID
+ if (obj.cmd === "control" && obj.action === "tp_callin") {
+ this.config.uuid = obj.uuid;
+ this.addLog(`鑷姩璁剧疆UUID: ${obj.uuid}`);
+ }
+ } catch (error) {
+ this.addLog(`娑堟伅瑙f瀽閿欒: ${error.message}`);
+ }
+ };
+ reader.readAsText(event.data);
+ },
+
+ // 鏂紑WebSocket杩炴帴
+ disconnectWebSocket() {
+ if (this.ws) {
+ this.ws.close();
+ this.ws = null;
+ this.isConnected = false;
+ this.addLog("WebSocket宸叉柇寮�");
+ }
+ },
+
+ // 鍙戦�乄ebSocket娑堟伅
+ sendWebSocketMessage(message) {
+ if (!this.isConnected || !this.ws) {
+ this.addLog("閿欒: WebSocket鏈繛鎺�");
+ return false;
+ }
+
+ try {
+ const messageStr =
+ typeof message === "string" ? message : JSON.stringify(message);
+ this.ws.send(messageStr);
+ this.addLog(`鍙戦�佹秷鎭�: ${messageStr}`);
+ return true;
+ } catch (error) {
+ this.addLog(`鍙戦�佹秷鎭け璐�: ${error.message}`);
+ return false;
+ }
+ },
+
+ // 楠岃瘉鍙傛暟
+ validateParams(params, requiredFields) {
+ for (const field of requiredFields) {
+ if (!params[field] || params[field].toString().trim() === "") {
+ this.addLog(`閿欒: ${field} 涓嶈兘涓虹┖`);
+ return false;
+ }
+ }
+ return true;
+ },
+
+ // ==================== WebSocket.js 鍔熻兘鏁村悎 ====================
+
+ // 绛惧叆
+ seatlogin() {
+ const { seatname, seatnum, password, cti_ws_url } = this.config;
+
+ if (
+ !this.validateParams({ seatname, seatnum }, ["seatname", "seatnum"])
+ ) {
+ return;
+ }
+
+ // 閲嶆柊杩炴帴WebSocket锛堝師js鏂囦欢涓殑閫昏緫锛�
+ this.connectWebSocket();
+ setTimeout(() => {
+ const protocol = {
+ cmd: "system",
+ action: "seatlogin",
+ seatname: seatname,
+ seatnum: seatnum,
+ password: password,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ }, 1000);
+ },
+
+ // 绛惧嚭
+ seatlogout() {
+ const { seatname, seatnum } = this.config;
+
+ if (
+ !this.validateParams({ seatname, seatnum }, ["seatname", "seatnum"])
+ ) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "system",
+ action: "seatlogout",
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ this.ws.close();
+ },
+
+ // 绀哄繖
+ afk() {
+ const { seatname, seatnum } = this.config;
+
+ if (
+ !this.validateParams({ seatname, seatnum }, ["seatname", "seatnum"])
+ ) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "system",
+ action: "afk",
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 绀洪棽
+ online() {
+ const { seatname, seatnum } = this.config;
+
+ if (
+ !this.validateParams({ seatname, seatnum }, ["seatname", "seatnum"])
+ ) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "system",
+ action: "online",
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 浠g瓟
+ pickup() {
+ const { seatname, seatnum } = this.config;
+
+ if (!this.validateParams({ seatnum }, ["seatnum"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "pickup",
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鎸傛満
+ hangup() {
+ const { seatname, seatnum } = this.config;
+
+ if (!this.validateParams({ seatnum }, ["seatnum"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "hangup",
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 澶栧懠
+ callout() {
+ const { seatname, seatnum, phone } = this.config;
+
+ if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "callout",
+ phone: phone,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 閫氳瘽杞Щ
+ transfer() {
+ const { seatname, seatnum, phone, uuid } = this.config;
+
+ if (
+ !this.validateParams({ seatnum, phone, uuid }, [
+ "seatnum",
+ "phone",
+ "uuid",
+ ])
+ ) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "transfer",
+ uuid: uuid,
+ phone: phone,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 閫氳瘽杞Щ鏀跺洖
+ transferresume() {
+ const { seatname, seatnum, phone, uuid } = this.config;
+
+ if (
+ !this.validateParams({ seatnum, phone, uuid }, [
+ "seatnum",
+ "phone",
+ "uuid",
+ ])
+ ) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "transferresume",
+ uuid: uuid,
+ phone: phone,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 閫氳瘽淇濇寔
+ hold() {
+ const { seatname, seatnum, uuid } = this.config;
+
+ if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "hold",
+ uuid: uuid,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 閫氳瘽淇濇寔鏀跺洖
+ holdresume() {
+ const { seatname, seatnum, uuid } = this.config;
+
+ if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "holdresume",
+ uuid: uuid,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 閫氳瘽寮烘媶
+ remove() {
+ const { seatname, seatnum, phone } = this.config;
+
+ if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "remove",
+ phone: phone,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 閫氳瘽寮烘彃
+ insert() {
+ const { seatname, seatnum, phone } = this.config;
+
+ if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "insert",
+ phone: phone,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鐩戝惉
+ monitor() {
+ const { seatname, seatnum, phone } = this.config;
+
+ if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "monitor",
+ phone: phone,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鐩戝惉杞�氳瘽
+ monitor_to_talk() {
+ const { seatname, seatnum, phone } = this.config;
+
+ if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "monitor_to_talk",
+ phone: phone,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鐩戝惉缁撴潫
+ monitor_end() {
+ const { seatname, seatnum, phone } = this.config;
+
+ if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "monitor_end",
+ phone: phone,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 閫夋嫨閫氳瘽
+ choosecall() {
+ const { seatname, seatnum, uuid } = this.config;
+
+ if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "choosecall",
+ uuid: uuid,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 浠f帴
+ replacecall() {
+ const { seatname, seatnum, phone } = this.config;
+
+ if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "replacecall",
+ phone: phone,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 涓夋柟閫氳瘽
+ three() {
+ const { seatname, seatnum, phone } = this.config;
+
+ if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "three",
+ phone: phone,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鍜ㄨ寮�濮�
+ handoff_ready() {
+ const { seatname, seatnum, uuid } = this.config;
+
+ if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "handoff_ready",
+ uuid: uuid,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鍜ㄨ鍛煎彨
+ handoff_call() {
+ const { seatname, seatnum, other, uuid } = this.config;
+
+ if (
+ !this.validateParams({ seatnum, other, uuid }, [
+ "seatnum",
+ "other",
+ "uuid",
+ ])
+ ) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "handoff_call",
+ uuid: uuid,
+ phone: other,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鍜ㄨ鏀跺洖
+ handoff_resume() {
+ const { seatname, seatnum, uuid } = this.config;
+
+ if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "handoff_resume",
+ uuid: uuid,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鍜ㄨ杞Щ
+ handoff_transfer() {
+ const { seatname, seatnum, other, uuid } = this.config;
+
+ if (
+ !this.validateParams({ seatnum, other, uuid }, [
+ "seatnum",
+ "other",
+ "uuid",
+ ])
+ ) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "handoff_transfer",
+ uuid: uuid,
+ phone: other,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鍜ㄨ涓夋柟
+ handoff_three() {
+ const { seatname, seatnum, uuid } = this.config;
+
+ if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "handoff_three",
+ uuid: uuid,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 寮�濮嬮�氳瘽褰曢煶
+ record_start() {
+ const { seatname, seatnum, uuid } = this.config;
+
+ if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "record_start",
+ uuid: uuid,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鍋滄閫氳瘽褰曢煶
+ record_stop() {
+ const { seatname, seatnum, uuid } = this.config;
+
+ if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "control",
+ action: "record_stop",
+ uuid: uuid,
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鎵撳紑鍧愬腑鐘舵��
+ openseatlist() {
+ const { seatname, seatnum } = this.config;
+
+ if (!this.validateParams({ seatnum }, ["seatnum"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "status",
+ action: "openseatlist",
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鍏抽棴鍧愬腑鐘舵��
+ closeseatlist() {
+ const { seatname, seatnum } = this.config;
+
+ if (!this.validateParams({ seatnum }, ["seatnum"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "status",
+ action: "closeseatlist",
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鎵撳紑闃熷垪淇℃伅
+ openqueues() {
+ const { seatname, seatnum } = this.config;
+
+ if (!this.validateParams({ seatnum }, ["seatnum"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "status",
+ action: "openqueues",
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鍏抽棴闃熷垪淇℃伅
+ closequeues() {
+ const { seatname, seatnum } = this.config;
+
+ if (!this.validateParams({ seatnum }, ["seatnum"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "status",
+ action: "closequeues",
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鎵撳紑閫氳瘽淇℃伅
+ opencalllist() {
+ const { seatname, seatnum } = this.config;
+
+ if (!this.validateParams({ seatnum }, ["seatnum"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "status",
+ action: "opencalllist",
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鍏抽棴閫氳瘽淇℃伅
+ closecalllist() {
+ const { seatname, seatnum } = this.config;
+
+ if (!this.validateParams({ seatnum }, ["seatnum"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "status",
+ action: "closecalllist",
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鎵撳紑璺敱淇℃伅
+ openroutelist() {
+ const { seatname, seatnum } = this.config;
+
+ if (!this.validateParams({ seatnum }, ["seatnum"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "status",
+ action: "openroutelist",
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鍏抽棴璺敱淇℃伅
+ closeroutelist() {
+ const { seatname, seatnum } = this.config;
+
+ if (!this.validateParams({ seatnum }, ["seatnum"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "status",
+ action: "closeroutelist",
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鑾峰彇鍧愬腑淇℃伅
+ seatlist() {
+ const { group } = this.config;
+
+ if (!this.validateParams({ group }, ["group"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "status",
+ action: "seatlist",
+ group: group,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鑾峰彇闃熷垪淇℃伅
+ queues() {
+ const protocol = {
+ cmd: "status",
+ action: "queues",
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鑾峰彇閫氳瘽淇℃伅
+ calllist() {
+ const protocol = {
+ cmd: "status",
+ action: "calllist",
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鑾峰彇璺敱淇℃伅
+ routelist() {
+ const protocol = {
+ cmd: "status",
+ action: "routelist",
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鑾峰彇澶栧懠鍙傛暟淇℃伅
+ batch() {
+ const { paramid } = this.config;
+
+ if (!this.validateParams({ paramid }, ["paramid"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "status",
+ action: "batch",
+ paramid: paramid,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 寮�濮嬪鍛间换鍔�
+ batch_start() {
+ const { seatname, seatnum } = this.config;
+
+ if (!this.validateParams({ seatnum }, ["seatnum"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "system",
+ action: "batch_start",
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 鍋滄澶栧懠浠诲姟
+ batch_stop() {
+ const { seatname, seatnum } = this.config;
+
+ if (!this.validateParams({ seatnum }, ["seatnum"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "system",
+ action: "batch_stop",
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 蹇冭烦鍖�
+ keepalive(seatname, seatnum) {
+ if (!this.validateParams({ seatnum }, ["seatnum"])) {
+ return;
+ }
+
+ const protocol = {
+ cmd: "system",
+ action: "keepalive",
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+ this.sendWebSocketMessage(protocol);
+ },
+
+ // 娓呴櫎鏃ュ織
+ testclear() {
+ this.logs = "";
+ this.addLog("鏃ュ織宸叉竻闄�");
+ },
+
+ // 娣诲姞鏃ュ織
+ addLog(message) {
+ const timestamp = new Date().toLocaleTimeString();
+ this.logs += `[${timestamp}] ${message}\n`;
+
+ // 闄愬埗鏃ュ織闀垮害锛岄槻姝㈠唴瀛樻孩鍑�
+ const logLines = this.logs.split("\n");
+ if (logLines.length > 100) {
+ this.logs = logLines.slice(-50).join("\n");
+ }
+ },
+ },
+};
+</script>
+
+<style scoped>
+.websocket-demo {
+ font-family: Arial, sans-serif;
+ padding: 20px;
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+.config-area {
+ margin-bottom: 20px;
+ padding: 15px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ background-color: #f9f9f9;
+}
+
+.input-group {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 10px;
+ margin-bottom: 10px;
+}
+
+.input-group label {
+ font-weight: bold;
+ min-width: 80px;
+}
+
+.input-group input {
+ padding: 5px 10px;
+ border: 1px solid #ccc;
+ border-radius: 3px;
+ width: 120px;
+}
+
+.button-area {
+ margin-bottom: 20px;
+}
+
+.button-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 5px;
+ margin-bottom: 10px;
+}
+
+.button-row button {
+ padding: 8px 15px;
+ border: 1px solid #ccc;
+ border-radius: 3px;
+ background-color: #f0f0f0;
+ cursor: pointer;
+ transition: background-color 0.3s;
+ font-size: 12px;
+}
+
+.button-row button:hover {
+ background-color: #e0e0e0;
+}
+
+.button-row button:active {
+ background-color: #d0d0d0;
+ transform: translateY(1px);
+}
+
+.log-area {
+ height: 300px;
+ overflow-y: auto;
+ border: 1px solid #ccc;
+ padding: 10px;
+ background-color: #f5f5f5;
+ white-space: pre-wrap;
+ font-family: "Courier New", monospace;
+ font-size: 12px;
+ line-height: 1.4;
+}
+
+h3 {
+ color: #333;
+ border-bottom: 2px solid #eee;
+ padding-bottom: 10px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+h3 button {
+ padding: 5px 10px;
+ font-size: 12px;
+ background-color: #f0f0f0;
+ border: 1px solid #ccc;
+ border-radius: 3px;
+ cursor: pointer;
+}
+
+/* 鍝嶅簲寮忚璁� */
+@media (max-width: 768px) {
+ .websocket-demo {
+ padding: 10px;
+ }
+
+ .input-group {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .input-group input {
+ width: 100%;
+ }
+
+ .button-row {
+ flex-direction: column;
+ }
+
+ .button-row button {
+ width: 100%;
+ margin-bottom: 5px;
+ }
+}
+</style>
diff --git a/src/views/followvisit/discharge/ClickCall.vue b/src/views/followvisit/discharge/ClickCall.vue
index 2896d7b..727d67e 100644
--- a/src/views/followvisit/discharge/ClickCall.vue
+++ b/src/views/followvisit/discharge/ClickCall.vue
@@ -1,21 +1,36 @@
<template>
<div class="websocket-demo">
<div>
- <h3>Websocket鎺ュ彛娴嬭瘯DEMO</h3>
+ <h3>Websocket鍛煎彨涓績鎺ュ彛</h3>
<div class="config-area">
+ <div class="status-indicator">
+ <span :class="['status-dot', connectionStatus]"></span>
+ 杩炴帴鐘舵��: {{ connectionText }}
+ <span :class="['status-dot', seatStatus]"></span>
+ 搴у腑鐘舵��: {{ seatStatusText }}
+ </div>
+
<div class="input-group">
<label>CTI_WS_URL</label>
<input
type="text"
v-model="config.cti_ws_url"
- placeholder="ws://40.78.0.169:6688"
+ placeholder="wss://your-server.com"
/>
<label>鍧愬腑宸ュ彿</label>
- <input type="text" v-model="config.seatname" placeholder="8000" />
+ <input
+ type="text"
+ v-model="config.seatname"
+ :placeholder="randomNum"
+ />
<label>鍧愬腑鍒嗘満</label>
- <input type="text" v-model="config.seatnum" placeholder="8000" />
+ <input
+ type="text"
+ v-model="config.seatnum"
+ :placeholder="randomNum"
+ />
<label>瀵嗙爜</label>
<input type="text" v-model="config.password" placeholder="123456" />
@@ -23,129 +38,136 @@
<div class="input-group">
<label>澶栫嚎鍙风爜</label>
- <input type="text" v-model="config.phone" placeholder="10086" />
-
- <label>UUID</label>
- <input type="text" v-model="config.uuid" />
-
- <label>鍏朵粬鍧愬腑</label>
- <input type="text" v-model="config.other" placeholder="8001" />
+ <input
+ type="text"
+ v-model="customerPhone"
+ placeholder="璇疯緭鍏ョ數璇濆彿鐮�"
+ />
<label>鎶�鑳界粍</label>
<input type="text" v-model="config.group" placeholder="a3" />
-
- <label>澶栧懠鍙傛暟id</label>
- <input type="text" v-model="config.paramid" placeholder="3" />
</div>
</div>
<!-- 鎿嶄綔鎸夐挳鍖哄煙 -->
<div class="button-area">
- <!-- 绗竴琛屾寜閽� -->
<div class="button-row">
- <button @click="seatlogin">绛惧叆</button>
- <button @click="seatlogout">绛惧嚭</button>
- <button @click="afk">绀哄繖</button>
- <button @click="online">绀洪棽</button>
- <button @click="pickup">浠g瓟</button>
+ <button
+ @click="handleSeatLogin"
+ :disabled="!isConnected || isSeatLoggedIn"
+ >
+ 绛惧叆
+ </button>
+ <button @click="handleSeatLogout" :disabled="!isSeatLoggedIn">
+ 绛惧嚭
+ </button>
+ <button @click="callout" :disabled="!isSeatLoggedIn">澶栧懠</button>
+ <button @click="hangup" :disabled="!isSeatLoggedIn">鎸傛満</button>
</div>
- <!-- 绗簩琛屾寜閽� -->
<div class="button-row">
- <button @click="hangup">鎸傛満</button>
- <button @click="callout">澶栧懠</button>
- <button @click="transfer">閫氳瘽杞Щ</button>
- <button @click="transferresume">閫氳瘽杞Щ鏀跺洖</button>
- <button @click="hold">閫氳瘽淇濇寔</button>
- <button @click="holdresume">閫氳瘽淇濇寔鏀跺洖</button>
- <button @click="remove">閫氳瘽寮烘媶</button>
- <button @click="insert">閫氳瘽寮烘彃</button>
- <button @click="monitor">鐩戝惉</button>
- <button @click="monitor_to_talk">鐩戝惉杞�氳瘽</button>
- <button @click="monitor_end">鐩戝惉缁撴潫</button>
- <button @click="choosecall">閫夋嫨</button>
- <button @click="replacecall">浠f帴</button>
- <button @click="three">涓夋柟閫氳瘽</button>
- </div>
-
- <!-- 绗笁琛屾寜閽� -->
- <div class="button-row">
- <button @click="handoff_ready">鍜ㄨ寮�濮�</button>
- <button @click="handoff_call">鍜ㄨ鍛煎彨</button>
- <button @click="handoff_resume">鍜ㄨ鏀跺洖</button>
- <button @click="handoff_transfer">鍜ㄨ杞Щ</button>
- <button @click="handoff_three">鍜ㄨ涓夋柟</button>
- <button @click="record_start">寮�濮嬮�氳瘽褰曢煶</button>
- <button @click="record_stop">鍋滄閫氳瘽褰曢煶</button>
- </div>
-
- <!-- 绗洓琛屾寜閽� -->
- <div class="button-row">
- <button @click="openseatlist">鎵撳紑鍧愬腑鐘舵��</button>
- <button @click="closeseatlist">鍏抽棴鍧愬腑鐘舵��</button>
- <button @click="openqueues">鎵撳紑闃熷垪淇℃伅</button>
- <button @click="closequeues">鍏抽棴闃熷垪淇℃伅</button>
- <button @click="opencalllist">鎵撳紑閫氳瘽淇℃伅</button>
- <button @click="closecalllist">鍏抽棴閫氳瘽淇℃伅</button>
- <button @click="openroutelist">鎵撳紑璺敱淇℃伅</button>
- <button @click="closeroutelist">鍏抽棴璺敱淇℃伅</button>
- </div>
-
- <!-- 绗簲琛屾寜閽� -->
- <div class="button-row">
- <button @click="seatlist">鑾峰彇鍧愬腑淇℃伅</button>
- <button @click="queues">鑾峰彇闃熷垪淇℃伅</button>
- <button @click="calllist">鑾峰彇閫氳瘽淇℃伅</button>
- <button @click="routelist">鑾峰彇璺敱淇℃伅</button>
- <button @click="batch">鑾峰彇澶栧懠鍙傛暟淇℃伅</button>
- <button @click="batch_start">寮�濮嬪鍛间换鍔�</button>
- <button @click="batch_stop">鍋滄澶栧懠浠诲姟</button>
+ <button @click="afk" :disabled="!isSeatLoggedIn">绀哄繖</button>
+ <button @click="online" :disabled="!isSeatLoggedIn">绀洪棽</button>
+ <button @click="hold" :disabled="!isSeatLoggedIn">淇濇寔</button>
+ <button @click="holdresume" :disabled="!isSeatLoggedIn">
+ 鍙栨秷淇濇寔
+ </button>
</div>
</div>
<!-- 鏃ュ織鏄剧ず鍖哄煙 -->
- <h3>鍗忚鏃ュ織鍖�<button @click="testclear">娓呴櫎</button></h3>
- <div id="msg" class="log-area">{{ logs }}</div>
+ <h3>鍗忚鏃ュ織鍖� <button @click="testclear">娓呴櫎</button></h3>
+ <div class="log-area">{{ logs }}</div>
</div>
</div>
</template>
<script>
+import { CallsetState, CallgetList } from "@/api/AiCentre/index";
+
export default {
name: "WebsocketDemo",
+ emits: ["status-change", "call-status", "error"],
+
+ props: {
+ customerPhone: {
+ type: String,
+ default: "",
+ },
+ autoLogin: {
+ type: Boolean,
+ default: true,
+ },
+ },
data() {
return {
config: {
- cti_ws_url: "wss://9.208.2.190:8092/cal-api/",
- seatname: "8000",
- seatnum: "8000",
+ cti_ws_url: "",
+ seatname: "",
+ seatnum: "",
password: "123456",
- phone: "10086",
+ phone: "",
uuid: "",
other: "8001",
group: "a3",
paramid: "3",
},
+ randomNum: "",
+ randomID: "",
logs: "",
ws: null,
isConnected: false,
+ isSeatLoggedIn: false,
+ currentCallStatus: "idle", // idle, calling, connected
+ seatResourceAcquired: false,
+ reconnectAttempts: 0,
+ maxReconnectAttempts: 5,
+ heartbeatTimer: null,
};
},
- mounted() {
+ computed: {
+ connectionStatus() {
+ return this.isConnected ? "connected" : "disconnected";
+ },
+ connectionText() {
+ return this.isConnected ? "宸茶繛鎺�" : "鏈繛鎺�";
+ },
+ seatStatus() {
+ return this.isSeatLoggedIn ? "logged-in" : "logged-out";
+ },
+ seatStatusText() {
+ return this.isSeatLoggedIn ? "宸茬鍏�" : "鏈鍏�";
+ },
+ },
+
+ watch: {
+ customerPhone(newVal) {
+ this.config.phone = newVal;
+ },
+ isSeatLoggedIn(newVal) {
+ this.$emit("status-change", {
+ isLoggedIn: newVal,
+ seatNumber: this.config.seatnum,
+ status: newVal ? "ready" : "offline",
+ });
+ },
+ },
+
+ async mounted() {
+ await this.initializeSeatResource();
this.initializeWebSocket();
},
beforeUnmount() {
- this.disconnectWebSocket();
+ this.cleanup();
},
methods: {
// 鍒濆鍖朩ebSocket杩炴帴
initializeWebSocket() {
try {
- // 鏍规嵁褰撳墠椤甸潰鍗忚鑷姩閫夋嫨WS鍗忚
const isHttps = window.location.protocol === "https:";
this.config.cti_ws_url = isHttps
? "wss://9.208.2.190:8092/cal-api/"
@@ -159,22 +181,70 @@
this.connectWebSocket();
} catch (error) {
this.addLog(`鍒濆鍖朩ebSocket閿欒: ${error.message}`);
- // 灏濊瘯浣跨敤澶囩敤鍦板潃
- this.config.cti_ws_url = "wss://9.208.2.190:8092/cal-api/";
setTimeout(() => this.connectWebSocket(), 2000);
}
},
+ // 鍒濆鍖栧骇甯彿璧勬簮
+ async initializeSeatResource() {
+ try {
+ const res = await CallgetList();
+ if (res.data && res.data.length > 0) {
+ // this.randomNum = res.data[0].tel;
+ // this.randomID = res.data[0].id;
+ this.randomNum = 8000;
+ this.randomID = 8000;
+ // 璁剧疆榛樿搴у腑鍙�
+ this.config.seatname = this.randomNum;
+ this.config.seatnum = this.randomNum;
+ // 绔嬪嵆鍗犵敤搴у腑鍙疯祫婧�
+ await this.startCallsetState();
+ this.seatResourceAcquired = true;
+ this.addLog(`搴у腑鍙疯祫婧愯幏鍙栨垚鍔�: ${this.randomNum}`);
+ }
+ } catch (error) {
+ console.error("鑾峰彇搴у腑鍙峰け璐�:", error);
+ this.addLog("閿欒: 鑾峰彇搴у腑鍙疯祫婧愬け璐�");
+ this.$emit("error", { type: "seat_acquisition_failed", error });
+ }
+ },
+ // 鍗犵敤搴у腑鍙�
+ async startCallsetState() {
+ try {
+ await CallsetState({ id: this.randomID, state: 1 });
+ this.addLog("搴у腑鍙风姸鎬佹洿鏂颁负浣跨敤涓�");
+ } catch (error) {
+ console.error("鏇存柊搴у腑鍙风姸鎬佸け璐�:", error);
+ throw error;
+ }
+ },
+
+ // 閲婃斁搴у腑鍙�
+ async releaseSeatResource() {
+ if (this.seatResourceAcquired && this.randomID) {
+ try {
+ await CallsetState({ id: this.randomID, state: 0 });
+ this.addLog("搴у腑鍙疯祫婧愬凡閲婃斁");
+ this.seatResourceAcquired = false;
+ } catch (error) {
+ console.error("閲婃斁搴у腑鍙峰け璐�:", error);
+ }
+ }
+ },
+ // 杩炴帴WebSocket
// 杩炴帴WebSocket
connectWebSocket() {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
- this.addLog("WebSocket宸茶繛鎺�");
+ return;
+ }
+
+ if (this.reconnectAttempts >= this.maxReconnectAttempts) {
+ this.addLog("閿欒: 杈惧埌鏈�澶ч噸杩炴鏁帮紝鍋滄閲嶈繛");
return;
}
try {
let wsUrl = this.config.cti_ws_url;
- // 纭繚HTTPS椤甸潰浣跨敤WSS
if (
window.location.protocol === "https:" &&
wsUrl.startsWith("ws://")
@@ -186,7 +256,14 @@
this.ws.onopen = () => {
this.isConnected = true;
+ this.reconnectAttempts = 0;
this.addLog("WebSocket杩炴帴鎴愬姛");
+ this.startHeartbeat();
+
+ // 杩炴帴鎴愬姛鍚庤嚜鍔ㄧ鍏�
+ if (this.autoLogin && this.seatResourceAcquired) {
+ setTimeout(() => this.handleSeatLogin(), 500);
+ }
};
this.ws.onmessage = (event) => {
@@ -195,54 +272,202 @@
this.ws.onclose = (event) => {
this.isConnected = false;
+ this.isSeatLoggedIn = false;
+ this.stopHeartbeat();
this.addLog(`WebSocket杩炴帴鍏抽棴: ${event.code} ${event.reason}`);
+
// 鑷姩閲嶈繛
- setTimeout(() => this.connectWebSocket(), 3000);
+ if (this.reconnectAttempts < this.maxReconnectAttempts) {
+ this.reconnectAttempts++;
+ setTimeout(() => this.connectWebSocket(), 3000);
+ }
};
this.ws.onerror = (error) => {
this.addLog(`WebSocket閿欒: ${error.message}`);
- // 灏濊瘯澶囩敤URL
- if (!wsUrl.includes("9.208.2.190")) {
- this.config.cti_ws_url = "wss://9.208.2.190:8092/cal-api/";
- setTimeout(() => this.connectWebSocket(), 3000);
- }
};
} catch (error) {
this.addLog(`杩炴帴WebSocket澶辫触: ${error.message}`);
- // 灏濊瘯澶囩敤URL
- this.config.cti_ws_url = "wss://9.208.2.190:8092/cal-api/";
setTimeout(() => this.connectWebSocket(), 3000);
}
},
// 澶勭悊WebSocket娑堟伅
handleWebSocketMessage(event) {
- const reader = new FileReader();
- reader.onloadend = (e) => {
- const message = reader.result;
- this.addLog(`鏀跺埌娑堟伅: ${message}`);
-
- try {
- const obj = JSON.parse(message);
-
- // 澶勭悊蹇冭烦鍖�
- if (obj.cmd === "system" && obj.action === "keepalive") {
- this.keepalive(obj.seatname, obj.seatnum);
- }
-
- // 鑷姩璁剧疆UUID
- if (obj.cmd === "control" && obj.action === "tp_callin") {
- this.config.uuid = obj.uuid;
- this.addLog(`鑷姩璁剧疆UUID: ${obj.uuid}`);
- }
- } catch (error) {
- this.addLog(`娑堟伅瑙f瀽閿欒: ${error.message}`);
+ try {
+ // 妫�鏌ユ暟鎹被鍨嬶細鍙兘鏄瓧绗︿覆鎴朆lob
+ if (event.data instanceof Blob) {
+ // 澶勭悊浜岃繘鍒舵暟鎹紙Blob锛�
+ const reader = new FileReader();
+ reader.onload = () => {
+ try {
+ const textData = reader.result;
+ this.addLog(`鏀跺埌Blob娑堟伅: ${textData}`);
+ this.processWebSocketData(textData);
+ } catch (error) {
+ this.addLog(`Blob鏁版嵁澶勭悊閿欒: ${error.message}`);
+ }
+ };
+ reader.readAsText(event.data);
+ } else if (typeof event.data === "string") {
+ // 鐩存帴澶勭悊鏂囨湰鏁版嵁
+ this.addLog(`鏀跺埌鏂囨湰娑堟伅: ${event.data}`);
+ this.processWebSocketData(event.data);
+ } else {
+ this.addLog(`鏈煡鏁版嵁绫诲瀷: ${typeof event.data}`);
}
+ } catch (error) {
+ this.addLog(`娑堟伅澶勭悊閿欒: ${error.message}`);
+ }
+ },
+ // 涓撻棬澶勭悊瑙f瀽鍚庣殑WebSocket鏁版嵁
+ processWebSocketData(messageText) {
+ console.log(messageText,'娑堟伅1');
+
+ try {
+ const obj = JSON.parse(messageText);
+ console.log(obj,'娑堟伅2');
+
+ // 澶勭悊蹇冭烦鍖�
+ if (obj.cmd === "system" && obj.action === "keepalive") {
+ this.keepalive(obj.seatname, obj.seatnum);
+ }
+ // 澶勭悊鎸傛柇
+ if (obj.action === "calloutend") {
+ this.hangup();
+ }
+
+ // 澶勭悊绛惧叆鍝嶅簲
+ if (obj.cmd === "system" && obj.action === "seatlogin") {
+ this.isSeatLoggedIn = true;
+ this.addLog("搴у腑绛惧叆鎴愬姛");
+ this.$emit("status-change", {
+ isLoggedIn: true,
+ seatNumber: this.config.seatnum,
+ status: "ready",
+ });
+ }
+
+ // 澶勭悊绛惧嚭鍝嶅簲
+ if (obj.cmd === "system" && obj.action === "seatlogout") {
+ this.isSeatLoggedIn = false;
+ this.addLog("搴у腑绛惧嚭鎴愬姛");
+ this.$emit("status-change", {
+ isLoggedIn: false,
+ status: "offline",
+ });
+ }
+
+ // 鑷姩璁剧疆UUID锛堟潵鐢典簨浠讹級
+ if (obj.cmd === "control" && obj.action === "tp_callin") {
+ this.config.uuid = obj.uuid;
+ this.addLog(`鑷姩璁剧疆UUID: ${obj.uuid}`);
+ this.$emit("call-status", {
+ status: "incoming",
+ uuid: obj.uuid,
+ phone: obj.phone || "鏈煡鍙风爜",
+ });
+ }
+
+ // 澶勭悊澶栧懠鍝嶅簲
+ if (obj.cmd === "control" && obj.action === "callout") {
+ this.$emit("call-status", {
+ status: obj.status || "calling",
+ uuid: obj.uuid,
+ phone: this.config.phone,
+ });
+ }
+
+ // 澶勭悊鎸傛満鍝嶅簲
+ if (obj.cmd === "control" && obj.action === "hangup") {
+ this.$emit("call-status", {
+ status: "idle",
+ uuid: obj.uuid,
+ });
+ }
+
+ // 澶勭悊閫氳瘽鐘舵�佸彉鍖�
+ if (obj.cmd === "control" && obj.status) {
+ this.handleCallStatusChange(obj);
+ }
+ } catch (error) {
+ this.addLog(`JSON瑙f瀽閿欒: ${error.message}, 鍘熷鏁版嵁: ${messageText}`);
+ }
+ },
+ // 澶勭悊鍛煎彨鐘舵�佸彉鍖�
+ handleCallStatusChange(obj) {
+ const statusMap = {
+ ringing: "鎸搩涓�",
+ connected: "閫氳瘽涓�",
+ held: "宸蹭繚鎸�",
+ ended: "閫氳瘽缁撴潫",
};
- reader.readAsText(event.data);
+
+ this.addLog(`閫氳瘽鐘舵��: ${statusMap[obj.status] || obj.status}`);
+ this.$emit("call-status", {
+ status: obj.status,
+ uuid: obj.uuid,
+ phone: obj.phone || this.config.phone,
+ });
+ },
+ // 寮�濮嬪績璺虫娴�
+ startHeartbeat() {
+ this.heartbeatTimer = setInterval(() => {
+ if (this.isConnected && this.isSeatLoggedIn) {
+ this.keepalive(this.config.seatname, this.config.seatnum);
+ }
+ }, 30000); // 30绉掑績璺�
},
+ // 鍋滄蹇冭烦妫�娴�
+ stopHeartbeat() {
+ if (this.heartbeatTimer) {
+ clearInterval(this.heartbeatTimer);
+ this.heartbeatTimer = null;
+ }
+ },
+ // 搴у腑绛惧叆
+ async handleSeatLogin() {
+ if (!this.seatResourceAcquired) {
+ this.addLog("閿欒: 鏈幏鍙栧骇甯彿璧勬簮锛屾棤娉曠鍏�");
+ return;
+ }
+
+ const { seatname, seatnum, password } = this.config;
+ if (!seatname || !seatnum) {
+ this.addLog("閿欒: 搴у腑宸ュ彿鍜屽垎鏈哄彿涓嶈兘涓虹┖");
+ return;
+ }
+
+ const protocol = {
+ cmd: "system",
+ action: "seatlogin",
+ seatname: seatname,
+ seatnum: seatnum,
+ password: password,
+ timestamp: Date.now(),
+ };
+
+ this.sendWebSocketMessage(protocol);
+ },
+ // 搴у腑绛惧嚭
+ async handleSeatLogout() {
+ const { seatname, seatnum } = this.config;
+
+ const protocol = {
+ cmd: "system",
+ action: "seatlogout",
+ seatname: seatname,
+ seatnum: seatnum,
+ timestamp: Date.now(),
+ };
+
+ if (this.sendWebSocketMessage(protocol)) {
+ this.isSeatLoggedIn = false;
+ // 寤惰繜閲婃斁璧勬簮锛岀‘淇濈鍑哄畬鎴�
+ setTimeout(() => this.releaseSeatResource(), 1000);
+ }
+ },
// 鏂紑WebSocket杩炴帴
disconnectWebSocket() {
if (this.ws) {
@@ -333,19 +558,11 @@
// 绀哄繖
afk() {
- const { seatname, seatnum } = this.config;
-
- if (
- !this.validateParams({ seatname, seatnum }, ["seatname", "seatnum"])
- ) {
- return;
- }
-
const protocol = {
cmd: "system",
action: "afk",
- seatname: seatname,
- seatnum: seatnum,
+ seatname: this.config.seatname,
+ seatnum: this.config.seatnum,
timestamp: Date.now(),
};
this.sendWebSocketMessage(protocol);
@@ -353,19 +570,11 @@
// 绀洪棽
online() {
- const { seatname, seatnum } = this.config;
-
- if (
- !this.validateParams({ seatname, seatnum }, ["seatname", "seatnum"])
- ) {
- return;
- }
-
const protocol = {
cmd: "system",
action: "online",
- seatname: seatname,
- seatnum: seatnum,
+ seatname: this.config.seatname,
+ seatnum: this.config.seatnum,
timestamp: Date.now(),
};
this.sendWebSocketMessage(protocol);
@@ -391,27 +600,28 @@
// 鎸傛満
hangup() {
- const { seatname, seatnum } = this.config;
-
- if (!this.validateParams({ seatnum }, ["seatnum"])) {
- return;
- }
-
const protocol = {
cmd: "control",
action: "hangup",
- seatname: seatname,
- seatnum: seatnum,
+ seatname: this.config.seatname,
+ seatnum: this.config.seatnum,
timestamp: Date.now(),
};
this.sendWebSocketMessage(protocol);
},
// 澶栧懠
- callout() {
- const { seatname, seatnum, phone } = this.config;
+ // 澶栧懠鎿嶄綔
+ async callout(phoneNumber = null) {
+ const phone = phoneNumber || this.customerPhone || this.config.phone;
+ if (!phone) {
+ this.addLog("閿欒: 琚彨鍙风爜涓嶈兘涓虹┖");
+ this.$emit("error", { type: "phone_number_required" });
+ return;
+ }
- if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
+ if (!this.isSeatLoggedIn) {
+ this.addLog("閿欒: 搴у腑鏈鍏ワ紝鏃犳硶澶栧懠");
return;
}
@@ -419,13 +629,23 @@
cmd: "control",
action: "callout",
phone: phone,
- seatname: seatname,
- seatnum: seatnum,
+ seatname: this.config.seatname,
+ seatnum: this.config.seatnum,
timestamp: Date.now(),
};
- this.sendWebSocketMessage(protocol);
- },
+ this.sendWebSocketMessage(protocol);
+ this.$emit("call-status", { status: "calling", phone });
+ },
+ // 娓呯悊璧勬簮
+ cleanup() {
+ this.stopHeartbeat();
+ if (this.ws) {
+ this.ws.close();
+ this.ws = null;
+ }
+ this.releaseSeatResource();
+ },
// 閫氳瘽杞Щ
transfer() {
const { seatname, seatnum, phone, uuid } = this.config;
@@ -480,18 +700,12 @@
// 閫氳瘽淇濇寔
hold() {
- const { seatname, seatnum, uuid } = this.config;
-
- if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
- return;
- }
-
const protocol = {
cmd: "control",
action: "hold",
- uuid: uuid,
- seatname: seatname,
- seatnum: seatnum,
+ uuid: this.config.uuid,
+ seatname: this.config.seatname,
+ seatnum: this.config.seatnum,
timestamp: Date.now(),
};
this.sendWebSocketMessage(protocol);
@@ -499,18 +713,12 @@
// 閫氳瘽淇濇寔鏀跺洖
holdresume() {
- const { seatname, seatnum, uuid } = this.config;
-
- if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
- return;
- }
-
const protocol = {
cmd: "control",
action: "holdresume",
- uuid: uuid,
- seatname: seatname,
- seatnum: seatnum,
+ uuid: this.config.uuid,
+ seatname: this.config.seatname,
+ seatnum: this.config.seatnum,
timestamp: Date.now(),
};
this.sendWebSocketMessage(protocol);
@@ -1059,12 +1267,7 @@
this.sendWebSocketMessage(protocol);
},
- // 蹇冭烦鍖�
keepalive(seatname, seatnum) {
- if (!this.validateParams({ seatnum }, ["seatnum"])) {
- return;
- }
-
const protocol = {
cmd: "system",
action: "keepalive",
@@ -1097,6 +1300,87 @@
</script>
<style scoped>
+.status-indicator {
+ margin-bottom: 15px;
+ padding: 10px;
+ background: #f5f5f5;
+ border-radius: 4px;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.status-dot {
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+ display: inline-block;
+}
+
+.status-dot.connected {
+ background-color: #52c41a;
+}
+
+.status-dot.disconnected {
+ background-color: #f5222d;
+}
+
+.status-dot.logged-in {
+ background-color: #1890ff;
+}
+
+.status-dot.logged-out {
+ background-color: #d9d9d9;
+}
+
+.button-row button:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+}
+
+.config-area {
+ margin-bottom: 20px;
+}
+
+.input-group {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ margin-bottom: 10px;
+}
+
+.input-group label {
+ font-weight: bold;
+ min-width: 80px;
+}
+
+.input-group input {
+ padding: 5px 10px;
+ border: 1px solid #ccc;
+ border-radius: 3px;
+ width: 120px;
+}
+
+.button-area {
+ margin-bottom: 20px;
+}
+
+.button-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 5px;
+ margin-bottom: 10px;
+}
+
+.log-area {
+ height: 200px;
+ overflow-y: auto;
+ border: 1px solid #ccc;
+ padding: 10px;
+ background: #f5f5f5;
+ white-space: pre-wrap;
+ font-family: monospace;
+}
.websocket-demo {
font-family: Arial, sans-serif;
padding: 20px;
diff --git a/src/views/followvisit/record/detailpage/index.vue b/src/views/followvisit/record/detailpage/index.vue
index c5945bb..0525fec 100644
--- a/src/views/followvisit/record/detailpage/index.vue
+++ b/src/views/followvisit/record/detailpage/index.vue
@@ -1013,6 +1013,18 @@
<el-button type="primary" @click="setupsubtask">纭鍒涘缓鏈嶅姟</el-button>
</div>
</el-dialog>
+ <div class="main-content" v-if="orgname == '鏅畞鐣叉棌鑷不鍘夸汉姘戝尰闄�'">
+ <!-- <el-button @click="CaldialogVisible = true">鎵撳紑寮规</el-button> -->
+
+ <!-- 寮规璋冪敤 -->
+ <el-dialog
+ title="鍛煎彨鍔熻兘妗�"
+ :visible.sync="CaldialogVisible"
+ width="60%"
+ >
+ <CallCenterLs ref="callCenterModal" :initial-phone="currentPhoneNumber" />
+ </el-dialog>
+ </div>
</div>
</template>
@@ -1038,10 +1050,12 @@
} from "@/api/patient/homepage";
import CallButton from "@/components/CallButton";
import MergeAndModify from "./MergeAndModify.vue";
+import CallCenterLs from "@/components/CallCenterLs";
export default {
components: {
CallButton,
MergeAndModify,
+ CallCenterLs,
},
directives: {
numericOnly: {
@@ -1125,6 +1139,7 @@
// 宸叉湁鏁版嵁...
callStatus: "idle", // idle, calling, connected, ended, failed
isEndingCall: false,
+ CaldialogVisible: false,
currentCall: null, // 褰撳墠閫氳瘽瀵硅薄
input: "浠婂ぉ韬綋杩樹笉閿�",
radio: "2",
@@ -1673,8 +1688,13 @@
this.$message.error("璇疯緭鍏ユ纭殑鎵嬫満鍙风爜");
return;
}
-
this.currentPhoneNumber = phone;
+ // 鍛煎彨鍒ゆ柇
+ if (this.orgname == "鏅畞鐣叉棌鑷不鍘夸汉姘戝尰闄�") {
+ this.CaldialogVisible = true;
+ return
+ }
+
this.callType = type;
this.callStatus = "calling";
@@ -1721,28 +1741,28 @@
}, 3000);
},
yuyingetdetail() {
- const dataToSubmit = JSON.parse(JSON.stringify(this.tableDatatop));
+ const dataToSubmit = JSON.parse(JSON.stringify(this.tableDatatop));
- dataToSubmit.forEach((item, index) => {
- // 瀵规嫹璐濈殑鏁版嵁杩涜鎿嶄綔锛屼笉褰卞搷鍘熷鐨� scriptResult 鏁扮粍
- item.scriptResult = item.scriptResult.join("&");
- item.templatequestionnum = index + 1;
- item.subId = this.id;
- item.taskid = this.taskid;
- item.asrtext = item.matchedtext;
- if (!item.id) {
- item.isoperation = 1;
- }
- item.patid = this.patid;
- item.templateid = item.templateID;
- });
+ dataToSubmit.forEach((item, index) => {
+ // 瀵规嫹璐濈殑鏁版嵁杩涜鎿嶄綔锛屼笉褰卞搷鍘熷鐨� scriptResult 鏁扮粍
+ item.scriptResult = item.scriptResult.join("&");
+ item.templatequestionnum = index + 1;
+ item.subId = this.id;
+ item.taskid = this.taskid;
+ item.asrtext = item.matchedtext;
+ if (!item.id) {
+ item.isoperation = 1;
+ }
+ item.patid = this.patid;
+ item.templateid = item.templateID;
+ });
- let obj = {
- serviceSubtaskDetailList: dataToSubmit, // 鎻愪氦澶勭悊鍚庣殑鍓湰
- param1: this.taskid,
- param2: this.patid,
- subId: this.id,
- };
+ let obj = {
+ serviceSubtaskDetailList: dataToSubmit, // 鎻愪氦澶勭悊鍚庣殑鍓湰
+ param1: this.taskid,
+ param2: this.patid,
+ subId: this.id,
+ };
addPersonVoices(obj).then((res) => {
if (res.code == 200) {
diff --git a/vue.config.js b/vue.config.js
index e6f390f..5f97768 100644
--- a/vue.config.js
+++ b/vue.config.js
@@ -37,8 +37,8 @@
[process.env.VUE_APP_BASE_API]: {
// target: `https://www.health-y.cn/lssf`,
// target: `http://192.168.100.129:8095`,
- target: `http://192.168.100.10:8096`,
- // target:`http://localhost:8095`,
+ // target: `http://192.168.100.10:8096`,
+ target:`http://localhost:8095`,
// target:`http://35z1t16164.qicp.vip`,
// target: `http://192.168.100.193:8095`,
// target: `http://192.168.101.166:8093`,
--
Gitblit v1.9.3