WXL
2025-07-03 880e036a0d85cd542034794c1dac2a4e433c5553
测试完成
已添加2个文件
已修改2个文件
244 ■■■■■ 文件已修改
package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/CallButton/index.vue 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/sipService.js 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/record/detailpage/index.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json
@@ -60,6 +60,7 @@
    "js-beautify": "1.13.0",
    "js-cookie": "3.0.1",
    "jsencrypt": "^3.3.2",
    "jssip": "^3.10.1",
    "lemon-imui": "^1.7.7",
    "moment": "^2.30.1",
    "nprogress": "0.2.0",
src/components/CallButton/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,148 @@
<template>
  <div class="call-container">
    <!-- å·ç è¾“å…¥ -->
    <input
      v-model="phoneNumber"
      type="text"
      placeholder="输入电话号码"
      @keyup.enter="startCall"
    >
    <!-- å‘¼å«æŒ‰é’® -->
    <button
      :class="['call-btn', { 'calling': isCalling }]"
      @click="startCall"
    >
      {{ isCalling ? '通话中...' : '一键呼叫' }}
    </button>
    <!-- æŒ‚断按钮 -->
    <button
      v-if="isCalling"
      class="end-call-btn"
      @click="endCall"
    >
      æŒ‚æ–­
    </button>
    <!-- éŸ³é¢‘元素(隐藏) -->
    <audio id="remoteAudio" autoplay></audio>
    <!-- çŠ¶æ€æ˜¾ç¤º -->
    <div class="call-status">
      {{ callStatus }}
    </div>
  </div>
</template>
<script>
import sipService from '@/utils/sipService'
export default {
  data() {
    return {
      phoneNumber: '',
      isCalling: false,
      callStatus: '准备就绪',
      sipConfig: {
        wsUrl: 'wss://192.168.100.6:7443',
        sipUri: '1000@192.168.100.6',
        password: 'Smartor@2023',
        displayName: 'Web å°é¾™',
        realm: '192.168.100.6:8090'
      }
    }
  },
  mounted() {
    // åˆå§‹åŒ–SIP连接
    sipService.init(this.sipConfig)
  },
  beforeDestroy() {
    // ç»„件销毁时结束通话
    this.endCall()
  },
  methods: {
    // å¼€å§‹å‘¼å«
    async startCall() {
      if (!this.phoneNumber) {
        this.callStatus = '请输入电话号码'
        return
      }
      try {
        this.isCalling = true
        this.callStatus = '呼叫中...'
        // è°ƒç”¨SIP服务
        sipService.makeCall(this.phoneNumber)
        this.callStatus = '通话已建立'
      } catch (error) {
        console.error('呼叫失败:', error)
        this.callStatus = `呼叫失败: ${error.message}`
        this.isCalling = false
      }
    },
    // ç»“束通话
    endCall() {
      sipService.endCall()
      this.isCalling = false
      this.callStatus = '通话已结束'
    }
  }
}
</script>
<style scoped>
.call-container {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: 300px;
  margin: 0 auto;
  padding: 20px;
  border: 1px solid #eee;
  border-radius: 8px;
}
input {
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 4px;
}
.call-btn {
  padding: 10px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
.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;
}
</style>
src/utils/sipService.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
import JsSIP from 'jssip'
class SipService {
  constructor() {
    this.ua = null
    this.currentSession = null
  }
  // åˆå§‹åŒ–SIP客户端
  init(config) {
    this.ua = new JsSIP.UA({
      sockets: [new JsSIP.WebSocketInterface(config.wsUrl)],
      uri: config.sipUri,
      password: config.password,
      display_name: config.displayName,
      realm: config.realm,
      ha1: config.ha1,
      register: true
    })
    this.ua.start()
    // æ³¨å†Œäº‹ä»¶ç›‘听
    this.ua.on('registered', () => {
      console.log('SIP注册成功')
    })
    this.ua.on('registrationFailed', (e) => {
      console.error('SIP注册失败:', e)
    })
    // ç›‘听来电
    this.ua.on('newRTCSession', (data) => {
      this.handleIncomingCall(data.session)
    })
  }
  // ä¸€é”®æ‹¨å·
  makeCall(targetNumber) {
    if (!this.ua) {
      console.error('SIP客户端未初始化')
      return
    }
    const options = {
      eventHandlers: {
        progress: (e) => console.log('呼叫中...'),
        failed: (e) => console.error('呼叫失败:', e),
        ended: (e) => console.log('通话结束'),
        confirmed: (e) => console.log('通话已接通')
      },
      mediaConstraints: { audio: true, video: false },
      rtcOfferConstraints: { offerToReceiveAudio: 1 }
    }
    this.currentSession = this.ua.call(`sip:${targetNumber}`, options)
    this.setupAudio(this.currentSession)
  }
  // æŒ‚断当前通话
  endCall() {
    if (this.currentSession) {
      this.currentSession.terminate()
      this.currentSession = null
    }
  }
  // å¤„理音频流
  setupAudio(session) {
    session.connection.addEventListener('addstream', (e) => {
      const audioElement = document.getElementById('remoteAudio')
      if (audioElement) {
        audioElement.srcObject = e.stream
      }
    })
  }
  // å¤„理来电
  handleIncomingCall(session) {
    if (session.direction === 'incoming') {
      console.log('来电:', session.remote_identity.uri.toString())
      // è¿™é‡Œå¯ä»¥è§¦å‘UI通知
    }
  }
}
export default new SipService()
src/views/followvisit/record/detailpage/index.vue
@@ -354,6 +354,10 @@
      </div>
    </div>
    <div>
      <h2>一键呼叫功能</h2>
      <CallButton/>
    </div>
    <div>
      <el-tabs v-model="activeName" type="border-card">
        <el-tab-pane name="wj">
          <span class="mulsz" slot="label"
@@ -744,7 +748,11 @@
  alterpatient,
  listcontactinformation,
} from "@/api/patient/homepage";
import CallButton from "@/components/CallButton";
export default {
  components: {
    CallButton,
  },
  dicts: ["sys_normal_disable", "sys_user_sex", "sys_yujing", "sys_suggest"],
  data() {
    return {