e10381f2480e6838aef1bdb81bac2195875c05e2..dafbb909e478015ee062bf962bddcb20a6fed55c
6 天以前 WXL
测试完成
dafbb9 对比 | 目录
6 天以前 WXL
测试完成
665ac6 对比 | 目录
6 天以前 WXL
测试完成
9a5dd2 对比 | 目录
6 天以前 WXL
测试完成
880e03 对比 | 目录
已添加2个文件
已修改5个文件
320 ■■■■■ 文件已修改
package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/CallButton/index.vue 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/sipService.js 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/discharge/index.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/record/detailpage/index.vue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/repositoryai/templateku/configurat/measurement.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/sfstatistics/percentage/index.vue 62 ●●●●● 补丁 | 查看 | 原始文档 | 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/discharge/index.vue
@@ -839,7 +839,17 @@
            </el-form-item>
          </el-col>
        </el-row>
        <el-row >
          <el-col :span="8">
            <el-form-item label="过滤医生" width="100" prop="filterDrname">
              <el-input
                v-model="form.filterDrname"
                placeholder="请输入医生姓名"
                maxlength="30"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="过滤原因">
@@ -1818,6 +1828,7 @@
    handleUpdate(row) {
      particularpatient(row.patid).then((response) => {
        this.form = response.data;
        this.form.filterDrname = store.getters.name;
      });
      this.amendtag = true;
      this.Labelchange = true;
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 {
src/views/repositoryai/templateku/configurat/measurement.vue
@@ -155,7 +155,6 @@
      this.timeout = this.$route.query.timeout;
      // é˜²æ­¢ç”¨æˆ·å¤šæ¬¡è¿žç»­ç‚¹å‡»å‘起请求,所以要先关闭上次的ws请求。
      closeWebsocket();
      console.log(this.id);
      const obj = {
        type: "text",
        userId: this.userid,
src/views/sfstatistics/percentage/index.vue
@@ -69,7 +69,7 @@
                  </el-option>
                </el-select>
                <el-select
                style="margin-left: 10px;"
                  style="margin-left: 10px"
                  v-if="queryParams.statisticaltype == 1"
                  v-model="queryParams.leavehospitaldistrictcodes"
                  size="medium"
@@ -78,10 +78,10 @@
                  placeholder="请选择病区"
                >
                  <el-option
                    v-for="item in flatArray"
                    :key="item.deptCode"
                    v-for="item in flatArrayhospit"
                    :key="item.value"
                    :label="item.label"
                    :value="item.deptCode"
                    :value="item.value"
                  >
                  </el-option>
                </el-select>
@@ -94,10 +94,10 @@
                  placeholder="请选择科室"
                >
                  <el-option
                    v-for="item in flatArray"
                    :key="item.deptCode"
                    v-for="item in flatArraydept"
                    :key="item.value"
                    :label="item.label"
                    :value="item.deptCode"
                    :value="item.value"
                  >
                  </el-option>
                </el-select>
@@ -209,6 +209,14 @@
                align="center"
                key="leavehospitaldistrictname"
                prop="leavehospitaldistrictname"
                width="150"
                :show-overflow-tooltip="true"
              />
              <el-table-column
                label="科室"
                align="center"
                key="deptname"
                prop="deptname"
                :show-overflow-tooltip="true"
              />
              <el-table-column
@@ -387,15 +395,10 @@
import {
  toamendtag,
  addapitag,
  detailstag,
  deletetag,
  changetagcategory,
  toamendtagcategory,
  addtagcategory,
  deletetagcategory,
  listtag,
  tagclassifylist,
} from "@/api/system/label";
import store from "@/store";
import { getSfStatistics, deptTreeSelect } from "@/api/system/user";
import Treeselect from "@riophae/vue-treeselect";
@@ -445,8 +448,8 @@
      idds: "", //分类id
      // æ€»æ¡æ•°
      total: 0,
      flatArray: [],
      deptflatArray: [],
      flatArrayhospit: [],
      flatArraydept: [],
      Statisticallist: [
        {
@@ -605,13 +608,23 @@
    // èŽ·å–ç§‘å®¤æ ‘
    getDeptTree() {
      // ç§‘室列表
      deptTreeSelect().then((response) => {
        this.deptOptions = response.data;
        console.log(this.deptOptions, " this.deptOptions");
        this.flatArray = this.flattenArray(response.data);
        console.log(this.flatArray, "this.flatArray");
      });
       this.flatArraydept = store.getters.belongDepts.map((dept) => {
      return {
        label: dept.deptName,
        value: dept.deptCode,
      };
    });
    this.flatArrayhospit = store.getters.belongWards.map((dept) => {
      return {
        label: dept.districtName,
        value: dept.districtCode,
      };
    });
      // deptTreeSelect().then((response) => {
      //   this.deptOptions = response.data;
      //   console.log(this.deptOptions, " this.deptOptions");
      //   this.flatArray = this.flattenArray(response.data);
      // });
    },
    flattenArray(multiArray) {
      let result = [];
@@ -713,6 +726,11 @@
    handleQuery() {
      this.queryParams.pageNum = 1;
      console.log();
      if (this.queryParams.statisticaltype == 1) {
        this.queryParams.deptcodes = [];
      } else if (this.queryParams.statisticaltype == 2) {
        this.queryParams.leavehospitaldistrictcodes = [];
      }
      this.queryParams.startTime = this.parseTime(
        this.queryParams.dateRange[0]
      );