|  |  | 
 |  |  |     <div class="sip-status" :class="sipStatusClass"> | 
 |  |  |       SIP状态: {{ sipStatus }} | 
 |  |  |     </div> | 
 |  |  |     <!-- 号码输入 --> | 
 |  |  |     <input | 
 |  |  |       v-model="phoneNumber" | 
 |  |  |       type="text" | 
 |  |  |       placeholder="输入电话号码" | 
 |  |  |       @keyup.enter="startCall" | 
 |  |  |     /> | 
 |  |  |  | 
 |  |  |     <!-- 状态显示 --> | 
 |  |  |     <div class="call-status" :class="callStatusClass"> | 
 |  |  |       {{ callStatusText }} | 
 |  |  |     </div> | 
 |  |  |  | 
 |  |  |     <!-- 呼叫按钮 --> | 
 |  |  |     <button :class="['call-btn', { calling: isCalling }]" @click="startCall"> | 
 |  |  |       {{ isCalling ? "通话中..." : "一键呼叫" }} | 
 |  |  |     <button | 
 |  |  |       :class="['call-btn', { calling: isCalling }]" | 
 |  |  |       @click="startCall" | 
 |  |  |       :disabled="isCalling || sipStatus !== '已注册'" | 
 |  |  |     > | 
 |  |  |       {{ callButtonText }} | 
 |  |  |     </button> | 
 |  |  |  | 
 |  |  |     <!-- 挂断按钮 --> | 
 |  |  | 
 |  |  |  | 
 |  |  |     <!-- 音频元素(隐藏) --> | 
 |  |  |     <audio id="remoteAudio" autoplay></audio> | 
 |  |  |  | 
 |  |  |     <!-- 状态显示 --> | 
 |  |  |     <div class="call-status"> | 
 |  |  |       {{ callStatus }} | 
 |  |  |     </div> | 
 |  |  |   </div> | 
 |  |  | </template> | 
 |  |  |  | 
 |  |  | 
 |  |  | import sipService from "@/utils/sipService"; | 
 |  |  |  | 
 |  |  | export default { | 
 |  |  |   props: { | 
 |  |  |     phoneNumber: { | 
 |  |  |       type: String, | 
 |  |  |       default: "", | 
 |  |  |     }, | 
 |  |  |   }, | 
 |  |  |   data() { | 
 |  |  |     const randomNum = Math.floor(Math.random() * 20) + 1000; // 内部定义 | 
 |  |  |     return { | 
 |  |  |       // phoneNumber: "", | 
 |  |  |       isCalling: false, | 
 |  |  |       callStatus: "准备就绪", | 
 |  |  |       callStatus: "idle", // idle, calling, connected, ended | 
 |  |  |       sipStatus: "未连接", | 
 |  |  |       sipStatusClass: "status-disconnected", | 
 |  |  |       sipConfig: { | 
 |  |  |         wsUrl: "wss://9.208.5.18:7443", | 
 |  |  |         sipUri: "1000@9.208.5.18", | 
 |  |  |         wsUrl: "wss://1192.170.66.107:7443", | 
 |  |  |         sipUri: `${randomNum}` + "@1192.170.66.107", | 
 |  |  |         password: "Smartor@2023", | 
 |  |  |         displayName: "Web 小龙", | 
 |  |  |         // realm: "9.208.5.18:8090", | 
 |  |  |       }, | 
 |  |  |     }; | 
 |  |  |   }, | 
 |  |  |   props: { | 
 |  |  |     phoneNumber: { | 
 |  |  |       type: String, | 
 |  |  |       default: '' | 
 |  |  |     } | 
 |  |  |   computed: { | 
 |  |  |     callStatusText() { | 
 |  |  |       const statusMap = { | 
 |  |  |         idle: "准备就绪", | 
 |  |  |         calling: "呼叫中...", | 
 |  |  |         connected: "通话中", | 
 |  |  |         ended: "通话结束", | 
 |  |  |       }; | 
 |  |  |       return statusMap[this.callStatus]; | 
 |  |  |     }, | 
 |  |  |     callStatusClass() { | 
 |  |  |       return `status-${this.callStatus}`; | 
 |  |  |     }, | 
 |  |  |     callButtonText() { | 
 |  |  |       return this.isCalling ? "通话中..." : "一键呼叫"; | 
 |  |  |     }, | 
 |  |  |   }, | 
 |  |  |   mounted() { | 
 |  |  |     // 测试 | 
 |  |  | 
 |  |  |     sipService.onStatusChange = (status) => { | 
 |  |  |       this.sipStatus = status.text; | 
 |  |  |       this.sipStatusClass = `status-${status.type}`; | 
 |  |  |     }; | 
 |  |  |  | 
 |  |  |       // 根据状态更新UI或执行其他操作 | 
 |  |  |       if (status.type === "registered") { | 
 |  |  |         console.log("SIP注册成功,可以开始呼叫"); | 
 |  |  |       } else if (status.type === "failed") { | 
 |  |  |         console.error("SIP注册失败"); | 
 |  |  |       } | 
 |  |  |     // 监听通话状态变化 | 
 |  |  |     sipService.onCallStatusChange = (status) => { | 
 |  |  |       this.callStatus = status.type; | 
 |  |  |       this.isCalling = status.type === "calling" || status.type === "connected"; | 
 |  |  |  | 
 |  |  |       // 通知父组件通话状态变化 | 
 |  |  |       this.$emit("call-status-change", status); | 
 |  |  |     }; | 
 |  |  |   }, | 
 |  |  |   beforeDestroy() { | 
 |  |  |     // 组件销毁时结束通话 | 
 |  |  |     this.endCall(); | 
 |  |  |   }, | 
 |  |  |   methods: { | 
 |  |  |     // 开始呼叫 | 
 |  |  |     async startCall() { | 
 |  |  |       if (!this.phoneNumber) { | 
 |  |  |         this.callStatus = "请输入电话号码"; | 
 |  |  |         this.$message.error("请输入电话号码"); | 
 |  |  |         return; | 
 |  |  |       } | 
 |  |  |       try { | 
 |  |  |         this.isCalling = true; | 
 |  |  |         this.callStatus = "呼叫中..."; | 
 |  |  |         // 调用SIP服务 | 
 |  |  |         sipService.makeCall(this.phoneNumber); | 
 |  |  |  | 
 |  |  |         this.callStatus = "通话已建立"; | 
 |  |  |       try { | 
 |  |  |         this.callStatus = "calling"; | 
 |  |  |         this.isCalling = true; | 
 |  |  |  | 
 |  |  |         await sipService.makeCall(this.phoneNumber); | 
 |  |  |       } catch (error) { | 
 |  |  |         console.error("呼叫失败:", error); | 
 |  |  |         this.callStatus = `呼叫失败: ${error.message}`; | 
 |  |  |         this.callStatus = "ended"; | 
 |  |  |         this.isCalling = false; | 
 |  |  |         this.$message.error(`呼叫失败: ${error.message}`); | 
 |  |  |       } | 
 |  |  |     }, | 
 |  |  |  | 
 |  |  |     // 结束通话 | 
 |  |  |     endCall() { | 
 |  |  |       sipService.endCall(); | 
 |  |  |       this.callStatus = "ended"; | 
 |  |  |       this.isCalling = false; | 
 |  |  |       this.callStatus = "通话已结束"; | 
 |  |  |     }, | 
 |  |  |   }, | 
 |  |  | }; | 
 |  |  | 
 |  |  |   border-radius: 4px; | 
 |  |  |   cursor: pointer; | 
 |  |  | } | 
 |  |  | .call-status { | 
 |  |  |   padding: 8px; | 
 |  |  |   margin: 10px 0; | 
 |  |  |   border-radius: 4px; | 
 |  |  |   text-align: center; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .status-idle { | 
 |  |  |   background-color: #f5f5f5; | 
 |  |  |   color: #666; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .status-calling { | 
 |  |  |   background-color: #fff8e1; | 
 |  |  |   color: #ff8f00; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .status-connected { | 
 |  |  |   background-color: #e8f5e9; | 
 |  |  |   color: #2e7d32; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .status-ended { | 
 |  |  |   background-color: #ffebee; | 
 |  |  |   color: #c62828; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | /* 原有样式保持不变 */ | 
 |  |  | .call-container { | 
 |  |  |   display: flex; | 
 |  |  |   flex-direction: column; | 
 |  |  |   gap: 10px; | 
 |  |  |   max-width: 300px; | 
 |  |  |   margin: 0 auto; | 
 |  |  |   padding: 20px; | 
 |  |  |   border: 1px solid #eee; | 
 |  |  |   border-radius: 8px; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .call-btn { | 
 |  |  |   padding: 10px; | 
 |  |  |   background-color: #4caf50; | 
 |  |  |   color: white; | 
 |  |  |   border: none; | 
 |  |  |   border-radius: 4px; | 
 |  |  |   cursor: pointer; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .call-btn:hover:not(:disabled) { | 
 |  |  |   background-color: #45a049; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .call-btn:disabled { | 
 |  |  |   background-color: #cccccc; | 
 |  |  |   cursor: not-allowed; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .call-btn.calling { | 
 |  |  |   background-color: #2196f3; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .end-call-btn { | 
 |  |  |   padding: 10px; | 
 |  |  |   background-color: #f44336; | 
 |  |  |   color: white; | 
 |  |  |   border: none; | 
 |  |  |   border-radius: 4px; | 
 |  |  |   cursor: pointer; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .end-call-btn:hover { | 
 |  |  |   background-color: #d32f2f; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .sip-status { | 
 |  |  |   padding: 8px; | 
 |  |  |   margin-bottom: 10px; | 
 |  |  |   border-radius: 4px; | 
 |  |  |   text-align: center; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .status-disconnected { | 
 |  |  |   background-color: #ffebee; | 
 |  |  |   color: #c62828; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .status-connecting { | 
 |  |  |   background-color: #fff8e1; | 
 |  |  |   color: #ff8f00; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .status-registered { | 
 |  |  |   background-color: #e8f5e9; | 
 |  |  |   color: #2e7d32; | 
 |  |  | } | 
 |  |  |  | 
 |  |  | .status-failed { | 
 |  |  |   background-color: #ffebee; | 
 |  |  |   color: #c62828; | 
 |  |  | } | 
 |  |  | .call-btn:hover { | 
 |  |  |   background-color: #45a049; | 
 |  |  | } |