| | |
| | | import request from "@/utils/request"; |
| | | |
| | | |
| | | // å é¤å¤é¨æ£è
表 |
| | | export function CallgetList() { |
| | | return request({ |
| | | url: "/smartor/ServiceTelInfo/getList", |
| | | method: "get", |
| | | params: { |
| | | orgid: localStorage.getItem("orgid"), |
| | | }, |
| | | }); |
| | | } |
| | | // æ¥è¯¢å¤é¨æ£è
表 |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <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">代ç</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">代æ¥</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: { |
| | | // åå§åWebSocketè¿æ¥ |
| | | 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(`åå§åWebSocketé误: ${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; |
| | | // æ£ç¡®è®¾ç½® 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(`æ¶æ¯è§£æé误: ${error.message}`); |
| | | } |
| | | }; |
| | | reader.readAsText(event.data); |
| | | }, |
| | | |
| | | // æå¼WebSocketè¿æ¥ |
| | | disconnectWebSocket() { |
| | | if (this.ws) { |
| | | this.ws.close(); |
| | | this.ws = null; |
| | | this.isConnected = false; |
| | | this.addLog("WebSocketå·²æå¼"); |
| | | } |
| | | }, |
| | | |
| | | // åéWebSocketæ¶æ¯ |
| | | 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); |
| | | }, |
| | | |
| | | // 代ç |
| | | 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); |
| | | }, |
| | | |
| | | // ä»£æ¥ |
| | | 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> |
| | |
| | | <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" /> |
| | |
| | | |
| | | <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">代ç</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">代æ¥</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: { |
| | | // åå§åWebSocketè¿æ¥ |
| | | initializeWebSocket() { |
| | | try { |
| | | // æ ¹æ®å½å页é¢åè®®èªå¨éæ©WSåè®® |
| | | const isHttps = window.location.protocol === "https:"; |
| | | this.config.cti_ws_url = isHttps |
| | | ? "wss://9.208.2.190:8092/cal-api/" |
| | |
| | | this.connectWebSocket(); |
| | | } catch (error) { |
| | | this.addLog(`åå§åWebSocketé误: ${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://") |
| | |
| | | |
| | | 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) => { |
| | |
| | | |
| | | 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(`æ¶æ¯è§£æé误: ${error.message}`); |
| | | try { |
| | | // æ£æ¥æ°æ®ç±»åï¼å¯è½æ¯å符串æBlob |
| | | 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}`); |
| | | } |
| | | }, |
| | | // ä¸é¨å¤çè§£æåç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è§£æé误: ${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) { |
| | |
| | | |
| | | // ç¤ºå¿ |
| | | 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); |
| | |
| | | |
| | | // ç¤ºé² |
| | | 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); |
| | |
| | | |
| | | // ææº |
| | | 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; |
| | | } |
| | | |
| | |
| | | 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; |
| | |
| | | |
| | | // éè¯ä¿æ |
| | | 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); |
| | |
| | | |
| | | // éè¯ä¿ææ¶å |
| | | 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); |
| | |
| | | this.sendWebSocketMessage(protocol); |
| | | }, |
| | | |
| | | // å¿è·³å
|
| | | keepalive(seatname, seatnum) { |
| | | if (!this.validateParams({ seatnum }, ["seatnum"])) { |
| | | return; |
| | | } |
| | | |
| | | const protocol = { |
| | | cmd: "system", |
| | | action: "keepalive", |
| | |
| | | </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; |
| | |
| | | <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> |
| | | |
| | |
| | | } 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: { |
| | |
| | | // å·²ææ°æ®... |
| | | callStatus: "idle", // idle, calling, connected, ended, failed |
| | | isEndingCall: false, |
| | | CaldialogVisible: false, |
| | | currentCall: null, // å½åéè¯å¯¹è±¡ |
| | | input: "ä»å¤©èº«ä½è¿ä¸é", |
| | | radio: "2", |
| | |
| | | this.$message.error("请è¾å
¥æ£ç¡®çææºå·ç "); |
| | | return; |
| | | } |
| | | |
| | | this.currentPhoneNumber = phone; |
| | | // å¼å«å¤æ |
| | | if (this.orgname == "æ¯å®ç²æèªæ²»å¿äººæ°å»é¢") { |
| | | this.CaldialogVisible = true; |
| | | return |
| | | } |
| | | |
| | | this.callType = type; |
| | | this.callStatus = "calling"; |
| | | |
| | |
| | | }, 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) { |
| | |
| | | [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`, |