From 741805d8daa2d2baa0b6b75bc1724488baf9c6bc Mon Sep 17 00:00:00 2001
From: WXL (wul) <wl_5969728@163.com>
Date: 星期一, 15 六月 2026 14:55:10 +0800
Subject: [PATCH] 测试完成

---
 src/components/CallButton/index.vue |  551 ++++++++++++++++++++++++++++++++++++------------------
 1 files changed, 369 insertions(+), 182 deletions(-)

diff --git a/src/components/CallButton/index.vue b/src/components/CallButton/index.vue
index bc17eea..d95fb2f 100644
--- a/src/components/CallButton/index.vue
+++ b/src/components/CallButton/index.vue
@@ -2,6 +2,9 @@
   <div class="call-container">
     <div class="sip-status" :class="sipStatusClass">
       SIP鐘舵��: {{ sipStatus }}
+      <span v-if="reconnectCount > 0" class="reconnect-info">
+        (閲嶈繛: {{ reconnectCount }}娆�)
+      </span>
     </div>
 
     <!-- 鐘舵�佹樉绀� -->
@@ -11,11 +14,29 @@
 
     <!-- 鍛煎彨鎸夐挳 -->
     <button
-      :class="['call-btn', { calling: isCalling }]"
+      :class="[
+        'call-btn',
+        {
+          calling: isCalling,
+          registering: isRegistering,
+          reconnecting: isReconnecting,
+        },
+      ]"
       @click="startCall"
-      :disabled="isCalling || sipStatus !== '宸叉敞鍐�'"
+      :disabled="isButtonDisabled"
     >
+      <i v-if="isRegistering || isReconnecting" class="el-icon-loading"></i>
       {{ callButtonText }}
+    </button>
+
+    <!-- 鎵嬪姩閲嶈繛鎸夐挳 -->
+    <button
+      v-if="showManualReconnect"
+      class="reconnect-btn"
+      @click="manualReconnect"
+      :disabled="isRegistering"
+    >
+      鎵嬪姩閲嶈繛
     </button>
 
     <!-- 鎸傛柇鎸夐挳 -->
@@ -38,20 +59,28 @@
     },
   },
   data() {
-    const randomNum = Math.floor(Math.random() * 20) + 1000; // 瀹氫箟闅忔満鍒嗘満鍙�
+    const randomNum = Math.floor(Math.random() * 20) + 1000;
     return {
       isCalling: false,
+      isRegistering: true,
+      isReconnecting: false, // 娣诲姞閲嶈繛涓姸鎬�
       randomNum: randomNum,
       randomID: null,
-      callStatus: "idle", // idle, calling, connected, ended
+      orgname: localStorage.getItem("orgname"),
+      callStatus: "idle",
       sipStatus: "鏈繛鎺�",
       sipStatusClass: "status-disconnected",
+      reconnectCount: 0, // 閲嶈繛娆℃暟
+      lastActivityTime: null, // 鏈�鍚庢椿鍔ㄦ椂闂�
+      heartbeatTimer: null, // 蹇冭烦瀹氭椂鍣�
+      reconnectTimer: null, // 閲嶈繛瀹氭椂鍣�
+      maxReconnectAttempts: 5, // 鏈�澶ч噸杩炲皾璇曟鏁�
+      reconnectDelay: 5000, // 閲嶈繛寤惰繜(ms)
       sipConfig: {
-        wsUrl: "wss://192.168.10.124:7443",
+        wsUrl: "",
         sipUri: "",
         password: "Smartor@2023",
         displayName: "Web 灏忛緳",
-        // realm: "9.208.5.18:8090",
       },
     };
   },
@@ -67,48 +96,246 @@
     },
     countdownText() {
       if (this.sipStatus !== "宸叉敞鍐�") return "";
-
       const { canCall, reason } = sipService.canMakeCall();
       if (!canCall && reason.includes("绛夊緟")) {
         return reason;
       }
       return "";
     },
+    isButtonDisabled() {
+      return (
+        this.isCalling ||
+        this.sipStatus !== "宸叉敞鍐�" ||
+        this.isRegistering ||
+        this.isReconnecting
+      );
+    },
+    callButtonText() {
+      if (this.isRegistering) return "娉ㄥ唽涓�...";
+      if (this.isReconnecting) return "閲嶈繛涓�...";
+      return this.isCalling ? "閫氳瘽涓�..." : "涓�閿懠鍙�";
+    },
     callStatusClass() {
       return `status-${this.callStatus}`;
     },
-    callButtonText() {
-      return this.isCalling ? "閫氳瘽涓�..." : "涓�閿懠鍙�";
+    showManualReconnect() {
+      return (
+        !this.isCalling &&
+        !this.isRegistering &&
+        this.sipStatus !== "宸叉敞鍐�" &&
+        this.sipStatus !== "杩炴帴涓�"
+      );
     },
   },
   created() {
-    // CallgetList();
+    if (
+      this.orgname == "绗竴浜烘皯鍖婚櫌婀栨花闄㈠尯" ||
+      this.orgname == "绗竴浜烘皯鍖婚櫌鍚村北闄㈠尯"
+    ) {
+      this.sipConfig.password = "heskj@1234";
+    } else {
+      this.sipConfig.password = "Smartor@2023";
+    }
   },
 
   async mounted() {
+    const orgName = localStorage.getItem("orgname");
+    if (orgName == "鏅畞鐣叉棌鑷不鍘夸汉姘戝尰闄�") {
+      return;
+    }
     await this.CallgetList();
-    sipService.init(this.sipConfig);
-    // 璁剧疆鐘舵�佸洖璋�
-    sipService.onStatusChange = (status) => {
-      this.sipStatus = status.text;
-      this.sipStatusClass = `status-${status.type}`;
-
-      // 澶勭悊娉ㄥ唽澶辫触鍜屾柇寮�杩炴帴鎯呭喌
-      if (status.type === "failed" || status.type === "disconnected") {
-        this.overCallsetState(); // 閲婃斁鍒嗘満鍙�
-      }
-    };
-
-    // 鐩戝惉閫氳瘽鐘舵�佸彉鍖�
-    sipService.onCallStatusChange = (status) => {
-      this.callStatus = status.type;
-      this.isCalling = status.type === "calling" || status.type === "connected";
-
-      // 閫氱煡鐖剁粍浠堕�氳瘽鐘舵�佸彉鍖�
-      this.$emit("call-status-change", status);
-    };
+    this.isRegistering = true;
+    this.initSipService();
+    this.setupHeartbeat();
   },
   methods: {
+    async initSipService() {
+      try {
+        // 鍒濆鍖杝ipService
+        sipService.init(this.sipConfig);
+
+        // 璁剧疆鐘舵�佸洖璋�
+        sipService.onStatusChange = (status) => {
+          this.sipStatus = status.text;
+          this.sipStatusClass = `status-${status.type}`;
+
+          // 澶勭悊鍚勭鐘舵��
+          if (status.type === "registered") {
+            this.handleRegistered();
+          } else if (status.type === "failed" || status.type === "disconnected") {
+            this.handleDisconnected();
+          } else if (status.type === "connecting") {
+            this.handleConnecting();
+          }
+        };
+
+        // 鐩戝惉閫氳瘽鐘舵�佸彉鍖�
+        sipService.onCallStatusChange = (status) => {
+          this.callStatus = status.type;
+          this.isCalling = status.type === "calling" || status.type === "connected";
+          this.updateLastActivityTime(); // 閫氳瘽鐘舵�佸彉鍖栨椂鏇存柊娲诲姩鏃堕棿
+
+          this.$emit("call-status-change", status);
+        };
+
+        // 璁剧疆瓒呮椂澶勭悊
+        this.setupRegistrationTimeout();
+
+      } catch (error) {
+        console.error("SIP鏈嶅姟鍒濆鍖栧け璐�:", error);
+        this.handleDisconnected();
+      }
+    },
+
+    handleRegistered() {
+      console.log("SIP娉ㄥ唽鎴愬姛");
+      this.isRegistering = false;
+      this.isReconnecting = false;
+      this.reconnectCount = 0; // 閲嶇疆閲嶈繛璁℃暟
+      this.updateLastActivityTime();
+      this.startCallsetState();
+
+      // 娓呴櫎閲嶈繛瀹氭椂鍣�
+      if (this.reconnectTimer) {
+        clearTimeout(this.reconnectTimer);
+        this.reconnectTimer = null;
+      }
+    },
+
+    handleDisconnected() {
+      console.log("SIP杩炴帴鏂紑");
+      this.isRegistering = false;
+      this.overCallsetState();
+
+      // 濡傛灉涓嶆槸閫氳瘽涓柇寮�锛屽皾璇曢噸杩�
+      if (!this.isCalling) {
+        this.scheduleReconnect();
+      }
+    },
+
+    handleConnecting() {
+      this.isReconnecting = true;
+    },
+
+    setupRegistrationTimeout() {
+      setTimeout(() => {
+        if (this.isRegistering && this.sipStatus !== "宸叉敞鍐�") {
+          this.isRegistering = false;
+          this.$message.warning("SIP娉ㄥ唽瓒呮椂锛屾鍦ㄥ皾璇曢噸杩�...");
+          this.scheduleReconnect();
+        }
+      }, 10000);
+    },
+
+    // 璁剧疆蹇冭烦妫�娴�
+    setupHeartbeat() {
+      this.heartbeatTimer = setInterval(() => {
+        this.checkConnection();
+      }, 60000); // 姣�30绉掓鏌ヤ竴娆¤繛鎺�
+    },
+
+    // 妫�鏌ヨ繛鎺ョ姸鎬�
+    async checkConnection() {
+      // 濡傛灉姝e湪娉ㄥ唽銆侀噸杩炴垨閫氳瘽涓紝涓嶆鏌�
+      if (this.isRegistering || this.isReconnecting || this.isCalling) {
+        return;
+      }
+
+      // 妫�鏌IP杩炴帴鐘舵��
+      if (sipService && sipService.ua) {
+        const isConnected = sipService.ua.isConnected();
+        const isRegistered = sipService.ua.isRegistered();
+
+        if (!isConnected || !isRegistered) {
+          console.log("蹇冭烦妫�娴�: 杩炴帴寮傚父锛屽皾璇曢噸杩�");
+          await this.reconnectSip();
+        } else {
+          console.log("蹇冭烦妫�娴�: 杩炴帴姝e父");
+          this.updateLastActivityTime();
+        }
+      } else {
+        console.log("蹇冭烦妫�娴�: UA涓嶅瓨鍦紝灏濊瘯閲嶆柊鍒濆鍖�");
+        await this.reconnectSip();
+      }
+    },
+
+    // 璁″垝閲嶈繛
+    scheduleReconnect() {
+      if (this.reconnectTimer || this.isReconnecting) {
+        return;
+      }
+
+      if (this.reconnectCount >= this.maxReconnectAttempts) {
+        console.log("杈惧埌鏈�澶ч噸杩炴鏁帮紝鍋滄閲嶈繛");
+        this.$message.error("SIP杩炴帴澶辫触锛岃鍒锋柊椤甸潰閲嶈瘯");
+        return;
+      }
+
+      this.reconnectCount++;
+      const delay = Math.min(this.reconnectDelay * Math.pow(1.5, this.reconnectCount - 1), 30000);
+
+      console.log(`璁″垝鍦�${delay}ms鍚庨噸杩烇紝绗�${this.reconnectCount}娆″皾璇昤);
+
+      this.reconnectTimer = setTimeout(() => {
+        this.reconnectSip();
+      }, delay);
+    },
+
+    // 閲嶆柊杩炴帴SIP
+    async reconnectSip() {
+      if (this.isReconnecting || this.isRegistering) {
+        return;
+      }
+
+      console.log("寮�濮嬮噸杩濻IP鏈嶅姟...");
+      this.isReconnecting = true;
+
+      try {
+        // 娓呯悊鐜版湁杩炴帴
+        this.cleanupSipConnection();
+
+        // 绛夊緟涓�娈垫椂闂�
+        await new Promise(resolve => setTimeout(resolve, 1000));
+
+        // 閲嶆柊鍒濆鍖�
+        await this.CallgetList(); // 閲嶆柊鑾峰彇鍒嗘満鍙�
+        this.initSipService();
+
+      } catch (error) {
+        console.error("閲嶈繛澶辫触:", error);
+        this.isReconnecting = false;
+        this.scheduleReconnect(); // 澶辫触鍚庣户缁噸璇�
+      } finally {
+        if (this.reconnectTimer) {
+          clearTimeout(this.reconnectTimer);
+          this.reconnectTimer = null;
+        }
+      }
+    },
+
+    // 娓呯悊SIP杩炴帴
+    cleanupSipConnection() {
+      if (sipService && sipService.ua) {
+        try {
+          sipService.ua.stop();
+          sipService.ua.unregister();
+        } catch (e) {
+          console.warn("娓呯悊SIP杩炴帴鏃跺嚭閿�:", e);
+        }
+      }
+    },
+
+    // 鎵嬪姩閲嶈繛
+    async manualReconnect() {
+      this.reconnectCount = 0; // 閲嶇疆閲嶈繛璁℃暟
+      await this.reconnectSip();
+    },
+
+    // 鏇存柊鏈�鍚庢椿鍔ㄦ椂闂�
+    updateLastActivityTime() {
+      this.lastActivityTime = Date.now();
+    },
+
     async startCall() {
       if (!this.phoneNumber) {
         this.$message.error("璇疯緭鍏ョ數璇濆彿鐮�");
@@ -119,53 +346,60 @@
         // 鍏堟鏌ユ槸鍚﹀彲浠ュ懠鍙�
         const { canCall, reason } = sipService.canMakeCall();
         if (!canCall) {
-          const { canCall, reason } = sipService.canMakeCall();
-          //this.$message.warning(reason);
-          //return;
+          // 鍙��: 鍙互鏄剧ず鎻愮ず
         }
+
         this.callStatus = "calling";
         this.isCalling = true;
         console.log("寮�濮嬪懠鍙細", sipService);
 
         await sipService.makeCall(this.phoneNumber);
       } catch (error) {
-        let registrationTime = Date.now(); // 璁板綍娉ㄩ攢鎴愬姛鏃堕棿
-        console.log(registrationTime, "鍛煎彨澶辫触鏃堕棿");
         console.error("鍛煎彨澶辫触1:", error);
-        // this.callStatus = "ended";
-        // this.isCalling = false;
-        //this.$message.error(`鍛煎彨澶辫触: ${error.message}`);
+
         try {
-          // 鍏堟鏌ユ槸鍚﹀彲浠ュ懠鍙�
+          // 灏濊瘯鍔�0鍐嶆鍛煎彨
           const { canCall, reason } = sipService.canMakeCall();
           if (!canCall) {
-            const { canCall, reason } = sipService.canMakeCall();
+            // 鍙�夊鐞�
           }
           this.callStatus = "calling";
           this.isCalling = true;
-          console.log("寮�濮嬪懠鍙細", sipService);
+          console.log("灏濊瘯鍔�0鍐嶆鍛煎彨锛�", sipService);
 
           await sipService.makeCall("0" + this.phoneNumber);
         } catch (error) {
           this.callStatus = "ended";
           this.isCalling = false;
+          this.$message.error("鍛煎彨澶辫触锛岃妫�鏌ョ綉缁滄垨鍙风爜");
         }
       }
     },
+
     // 鏌ヨ鍙敤鍒嗘満鍙�
     async CallgetList() {
       try {
         const res = await CallgetList();
         this.randomNum = res.data[0].tel;
         this.randomID = res.data[0].id;
-        // 姝g‘璁剧疆 sipUri
-        this.sipConfig.sipUri = `${this.randomNum}@192.168.10.124`;
-        this.startCallsetState();
+
+        const orgName = localStorage.getItem("orgname");
+        if (orgName == "涓芥按甯備腑鍖婚櫌") {
+          this.sipConfig.sipUri = `${this.randomNum}@192.168.10.124`;
+        } else if (orgName == "榫欐硥甯備汉姘戝尰闄�") {
+          this.sipConfig.sipUri = `${this.randomNum}@10.10.0.220`;
+        } else if (
+          orgName == "绗竴浜烘皯鍖婚櫌婀栨花闄㈠尯" ||
+          orgName == "绗竴浜烘皯鍖婚櫌鍚村北闄㈠尯"
+        ) {
+          this.sipConfig.sipUri = `${this.randomNum}@192.169.129.198`;
+        }
       } catch (error) {
         console.error("鑾峰彇鍒嗘満鍙峰け璐�:", error);
-        this.updateStatus("failed", "鑾峰彇鍒嗘満鍙峰け璐�");
+        throw error; // 鎶涘嚭閿欒浠ヤ究涓婂眰澶勭悊
       }
     },
+
     async startCallsetState() {
       try {
         await CallsetState({ id: this.randomID, state: 1 });
@@ -185,12 +419,25 @@
         console.error("閲婃斁鍒嗘満鍙峰け璐�:", error);
       }
     },
+
     endCall() {
       sipService.endCall();
       this.callStatus = "ended";
       this.isCalling = false;
     },
+
     cleanupResources() {
+      // 娓呴櫎鎵�鏈夊畾鏃跺櫒
+      if (this.heartbeatTimer) {
+        clearInterval(this.heartbeatTimer);
+        this.heartbeatTimer = null;
+      }
+
+      if (this.reconnectTimer) {
+        clearTimeout(this.reconnectTimer);
+        this.reconnectTimer = null;
+      }
+
       // 缁撴潫閫氳瘽
       if (this.isCalling) {
         sipService.endCall();
@@ -200,19 +447,17 @@
       this.overCallsetState();
 
       // 鏂紑 SIP 杩炴帴
-      if (sipService.ua) {
-        sipService.ua.stop();
-      }
+      this.cleanupSipConnection();
     },
   },
   beforeUnmount() {
-    // 缁勪欢閿�姣佹椂纭繚閲婃斁璧勬簮
     this.cleanupResources();
   },
 };
 </script>
 
 <style scoped>
+/* 淇濇寔鍘熸湁鏍峰紡涓嶅彉锛屽彧娣诲姞鏂版牱寮� */
 .call-container {
   display: flex;
   flex-direction: column;
@@ -224,25 +469,93 @@
   border-radius: 8px;
 }
 
-input {
-  padding: 8px;
-  border: 1px solid #ccc;
-  border-radius: 4px;
+.reconnect-info {
+  font-size: 12px;
+  color: #666;
+  margin-left: 5px;
 }
 
-.call-btn {
+.reconnect-btn {
+  padding: 8px 12px;
+  background-color: #ff9800;
+  color: white;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 12px;
+}
+
+.reconnect-btn:hover:not(:disabled) {
+  background-color: #f57c00;
+}
+
+.reconnect-btn:disabled {
+  background-color: #cccccc;
+  cursor: not-allowed;
+}
+
+.call-btn.reconnecting {
+  background-color: #ff9800;
+}
+
+.call-btn:hover:not(:disabled) {
+  background-color: #45a049;
+}
+
+.call-btn:disabled {
+  background-color: #cccccc;
+  cursor: not-allowed;
+}
+
+.call-btn.calling {
+  background-color: #2196f3;
+}
+
+.call-btn.registering,
+.call-btn.reconnecting {
+  position: relative;
+}
+
+.end-call-btn {
   padding: 10px;
-  background-color: #4caf50;
+  background-color: #f44336;
   color: white;
   border: none;
   border-radius: 4px;
   cursor: pointer;
 }
+
+.end-call-btn:hover {
+  background-color: #d32f2f;
+}
+
+/* 鐘舵�佹牱寮忎繚鎸佷笉鍙� */
+.sip-status,
 .call-status {
   padding: 8px;
-  margin: 10px 0;
+  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;
 }
 
 .status-idle {
@@ -261,132 +574,6 @@
 }
 
 .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;
-}
-
-.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;
-}
-
-.call-status {
-  margin-top: 10px;
-  font-size: 14px;
-  color: #666;
-}
-.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;
 }

--
Gitblit v1.9.3