| | |
| | | "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", |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | 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() |
| | |
| | | </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="è¿æ»¤åå "> |
| | |
| | | handleUpdate(row) { |
| | | particularpatient(row.patid).then((response) => { |
| | | this.form = response.data; |
| | | this.form.filterDrname = store.getters.name; |
| | | }); |
| | | this.amendtag = true; |
| | | this.Labelchange = true; |
| | |
| | | </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" |
| | |
| | | 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 { |
| | |
| | | this.timeout = this.$route.query.timeout; |
| | | // 鲿¢ç¨æ·å¤æ¬¡è¿ç»ç¹å»å起请æ±ï¼æä»¥è¦å
å
³é䏿¬¡çws请æ±ã |
| | | closeWebsocket(); |
| | | console.log(this.id); |
| | | const obj = { |
| | | type: "text", |
| | | userId: this.userid, |
| | |
| | | </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" |
| | |
| | | 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> |
| | |
| | | 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> |
| | |
| | | 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 |
| | |
| | | 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"; |
| | |
| | | idds: "", //åç±»id |
| | | // æ»æ¡æ° |
| | | total: 0, |
| | | flatArray: [], |
| | | deptflatArray: [], |
| | | flatArrayhospit: [], |
| | | flatArraydept: [], |
| | | |
| | | Statisticallist: [ |
| | | { |
| | |
| | | // è·åç§å®¤æ |
| | | 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 = []; |
| | |
| | | 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] |
| | | ); |