mdm-client 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/App.vue +72 -67
- package/src/assets/image/common/layout-active16.png +0 -0
- package/src/assets/image/common/layout-active25.png +0 -0
- package/src/assets/image/common/layout-active3.png +0 -0
- package/src/assets/image/common/layout-active9.png +0 -0
- package/src/assets/image/common/layout16.png +0 -0
- package/src/assets/image/common/layout25.png +0 -0
- package/src/assets/image/common/layout3.png +0 -0
- package/src/assets/image/common/layout9.png +0 -0
- package/src/assets/image/common/mirror.png +0 -0
- package/src/assets/image/common/rotate_icon1.png +0 -0
- package/src/assets/image/common/rotate_icon2.png +0 -0
- package/src/assets/image/common/rotate_icon3.png +0 -0
- package/src/assets/image/common/rotate_icon4.png +0 -0
- package/src/assets/style/base.scss +5 -0
- package/src/components/LiveMulti/LiveMulti.vue +27 -6
- package/src/components/LiveMultipleMeeting/LiveMultipleMeeting.vue +1163 -99
- package/src/components/LiveMultipleMeeting/style/index.scss +145 -14
- package/src/components/LivePoint/LivePoint.vue +71 -208
- package/src/components/LivePointMeeting/LivePointMeeting.vue +223 -13
- package/src/components/LivePointMeeting/style/index.scss +35 -0
- package/src/components/MeetingReadyDialog/MeetingReadyDialog.vue +96 -14
- package/src/components/MiniumVideoDialog/MiniumVideoDialog.vue +185 -50
- package/src/components/other/addressBook.vue +137 -20
- package/src/components/other/appointDialog.vue +1 -1
- package/src/components/other/customLayout.vue +368 -202
- package/src/components/other/layoutSwitch.vue +253 -37
- package/src/components/other/leadershipFocus.vue +422 -0
- package/src/components/other/leaveOptionDialog.vue +1 -1
- package/src/components/other/moreOptionDialog.vue +17 -1
- package/src/components/other/screenShareBoard.vue +2 -2
- package/src/components/other/selectDialog.vue +1 -1
- package/src/components/other/selectSpecialDialog.vue +1 -1
- package/src/utils/api.js +19 -0
- package/src/utils/livekit/live-client-esm.js +1 -1
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
></div>
|
|
20
20
|
<div class="identity">{{ curSpeaker.name }}</div>
|
|
21
21
|
</div>
|
|
22
|
+
<!-- 当当前讲话者未开启摄像头时,显示占位板(与 liveMultipleMeeting 的 board 样式一致) -->
|
|
23
|
+
<div v-if="!curSpeakerCameraStatus" class="board">
|
|
24
|
+
<div class="board-icon"></div>
|
|
25
|
+
</div>
|
|
22
26
|
</div>
|
|
23
27
|
<div class="speaking-wrapper" v-show="!isExpand">
|
|
24
28
|
<div :class="['cur-speaking-mic', curSpeakerMicrophoneStatus ? 'mic-on' : 'mic-off']"></div>
|
|
@@ -34,8 +38,10 @@
|
|
|
34
38
|
>
|
|
35
39
|
<div class="minium-video-dialog-footer-btn mic-on" v-if="localMicrophoneStatus" @click="changeLocalMicrophoneStatus"></div>
|
|
36
40
|
<div class="minium-video-dialog-footer-btn mic-off" v-else @click="changeLocalMicrophoneStatus"></div>
|
|
37
|
-
<
|
|
38
|
-
|
|
41
|
+
<template v-if="roomMode === 'auto'">
|
|
42
|
+
<div class="minium-video-dialog-footer-btn camera-on" v-if="localCameraStatus" @click="changeLocalCameraStatus"></div>
|
|
43
|
+
<div class="minium-video-dialog-footer-btn camera-off" v-else @click="changeLocalCameraStatus"></div>
|
|
44
|
+
</template>
|
|
39
45
|
<div class="minium-video-dialog-footer-btn reset" @click="reset"></div>
|
|
40
46
|
<div :class="['minium-video-dialog-footer-btn', isExpand ? 'slide' : 'expand']" @click="changeExpand"></div>
|
|
41
47
|
</div>
|
|
@@ -59,6 +65,8 @@ export default {
|
|
|
59
65
|
isExpand: true,
|
|
60
66
|
curSpeakers: [],
|
|
61
67
|
curSpeaker: null,
|
|
68
|
+
// 统一维护的 liveClient 实例,避免在各处重复通过 window 访问
|
|
69
|
+
liveClient: null,
|
|
62
70
|
localCameraStatus: false,
|
|
63
71
|
localMicrophoneStatus: false,
|
|
64
72
|
curSpeakerMicrophoneStatus: false,
|
|
@@ -66,6 +74,8 @@ export default {
|
|
|
66
74
|
curSpeakerTrack: null,
|
|
67
75
|
showFooter: false,
|
|
68
76
|
footerTimer: null,
|
|
77
|
+
// room模式,auto-音视频,audio-仅音频
|
|
78
|
+
roomMode: 'auto',
|
|
69
79
|
};
|
|
70
80
|
},
|
|
71
81
|
computed: {
|
|
@@ -84,7 +94,10 @@ export default {
|
|
|
84
94
|
},
|
|
85
95
|
beforeDestroy() {
|
|
86
96
|
this.clearFooterTimer();
|
|
97
|
+
this.detachCurrentTrack();
|
|
87
98
|
this.dispatchLiveClientEvent();
|
|
99
|
+
// 组件销毁时清理引用
|
|
100
|
+
this.liveClient = null;
|
|
88
101
|
},
|
|
89
102
|
methods: {
|
|
90
103
|
changeExpand() {
|
|
@@ -131,19 +144,21 @@ export default {
|
|
|
131
144
|
|
|
132
145
|
// 初始化liveClient
|
|
133
146
|
initLiveClient() {
|
|
134
|
-
|
|
135
|
-
if(!liveClient) {
|
|
147
|
+
this.liveClient = window['liveClient'] || null;
|
|
148
|
+
if(!this.liveClient) {
|
|
136
149
|
return;
|
|
137
150
|
}
|
|
151
|
+
this.roomMode = this.liveClient?.roomMode || 'auto';
|
|
138
152
|
this.curSpeaker = {
|
|
139
|
-
identity: liveClient.room.localParticipant.identity,
|
|
140
|
-
name: liveClient.room.localParticipant.name,
|
|
153
|
+
identity: this.liveClient.room.localParticipant.identity,
|
|
154
|
+
name: this.liveClient.room.localParticipant.name,
|
|
141
155
|
};
|
|
142
|
-
this.localCameraStatus = liveClient.room.localParticipant.isCameraEnabled;
|
|
143
|
-
this.localMicrophoneStatus = liveClient.room.localParticipant.isMicrophoneEnabled;
|
|
144
|
-
this.curSpeakerMicrophoneStatus = liveClient.room.localParticipant.isMicrophoneEnabled;
|
|
145
|
-
this.curSpeakerCameraStatus = liveClient.room.localParticipant.isCameraEnabled;
|
|
146
|
-
|
|
156
|
+
this.localCameraStatus = this.liveClient.room.localParticipant.isCameraEnabled;
|
|
157
|
+
this.localMicrophoneStatus = this.liveClient.room.localParticipant.isMicrophoneEnabled;
|
|
158
|
+
this.curSpeakerMicrophoneStatus = this.liveClient.room.localParticipant.isMicrophoneEnabled;
|
|
159
|
+
this.curSpeakerCameraStatus = this.liveClient.room.localParticipant.isCameraEnabled;
|
|
160
|
+
// 仅在视频会议模式下记录并绑定视频轨道
|
|
161
|
+
this.curSpeakerTrack = this.roomMode === 'auto' ? this.trackData.videoTrack : null;
|
|
147
162
|
// 初始化视频轨道绑定
|
|
148
163
|
this.initVideoAttach();
|
|
149
164
|
// 监听并处理liveclient事件
|
|
@@ -194,22 +209,27 @@ export default {
|
|
|
194
209
|
},
|
|
195
210
|
|
|
196
211
|
initVideoAttach() {
|
|
197
|
-
if(this.
|
|
198
|
-
|
|
199
|
-
videoElm
|
|
212
|
+
if (this.roomMode !== 'auto') return
|
|
213
|
+
if (this.curSpeakerTrack) {
|
|
214
|
+
const videoElm = document.getElementById('video-contain')
|
|
215
|
+
if (videoElm) {
|
|
216
|
+
// 先解绑,确保不会重复附加
|
|
217
|
+
try { this.curSpeakerTrack.detach(videoElm) } catch (e) {}
|
|
218
|
+
this.curSpeakerTrack.attach(videoElm)
|
|
219
|
+
}
|
|
200
220
|
}
|
|
201
221
|
},
|
|
202
222
|
|
|
203
223
|
initLiveClientEvent() {
|
|
204
|
-
|
|
205
|
-
if(!liveClient) {
|
|
224
|
+
if(!this.liveClient) {
|
|
206
225
|
return;
|
|
207
226
|
}
|
|
208
|
-
liveClient.on("localCameraChange", this.handleLocalCameraChange);
|
|
209
|
-
liveClient.on("localMicrophoneChange", this.handleLocalMicrophoneChange);
|
|
210
|
-
liveClient.on("activeSpeakerChange", this.handleActiveSpeakerChange);
|
|
211
|
-
liveClient.on("videoCallRender", this.handleVideoCallRender);
|
|
212
|
-
liveClient.on("
|
|
227
|
+
this.liveClient.on("localCameraChange", this.handleLocalCameraChange);
|
|
228
|
+
this.liveClient.on("localMicrophoneChange", this.handleLocalMicrophoneChange);
|
|
229
|
+
this.liveClient.on("activeSpeakerChange", this.handleActiveSpeakerChange);
|
|
230
|
+
this.liveClient.on("videoCallRender", this.handleVideoCallRender);
|
|
231
|
+
this.liveClient.on("audioCallRender", this.handleAudioCallRender);
|
|
232
|
+
this.liveClient.on("roomDisconnected", this.handleRoomDisconnected);
|
|
213
233
|
},
|
|
214
234
|
|
|
215
235
|
handleLocalCameraChange(e) {
|
|
@@ -221,19 +241,35 @@ export default {
|
|
|
221
241
|
},
|
|
222
242
|
|
|
223
243
|
handleActiveSpeakerChange(e) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
244
|
+
this.curSpeakers = Array.isArray(e) ? e : []
|
|
245
|
+
|
|
246
|
+
if(this.curSpeakers.length > 0) {
|
|
247
|
+
// 找到音量最大的讲话者
|
|
248
|
+
let maxAudioLevel = -1
|
|
249
|
+
let winner = null
|
|
250
|
+
this.curSpeakers.forEach(speaker => {
|
|
251
|
+
const level = typeof speaker.audioLevel === 'number' ? speaker.audioLevel : 0
|
|
252
|
+
if (level > maxAudioLevel) {
|
|
253
|
+
maxAudioLevel = level
|
|
254
|
+
winner = speaker
|
|
255
|
+
}
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
if (!winner) return
|
|
259
|
+
|
|
260
|
+
// 仅当 identity 变化时才切换,以减少抖动
|
|
261
|
+
if (winner.identity !== this.curSpeaker?.identity) {
|
|
262
|
+
// 解绑旧轨道,等待 videoCallRender 到来后再绑定新轨道
|
|
263
|
+
// detachCurrentTrack()
|
|
264
|
+
this.curSpeaker = {
|
|
265
|
+
identity: winner.identity,
|
|
266
|
+
name: winner.name || winner.identity,
|
|
267
|
+
isLocal: !!winner.isLocal,
|
|
231
268
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
269
|
+
// 在未收到渲染事件前,暂时认为摄像头状态未知/关闭,用以显示占位
|
|
270
|
+
// curSpeakerCameraStatus.value = false
|
|
271
|
+
// curSpeakerMicrophoneStatus.value = false
|
|
235
272
|
}
|
|
236
|
-
this.curSpeakers = e;
|
|
237
273
|
}
|
|
238
274
|
},
|
|
239
275
|
|
|
@@ -242,15 +278,61 @@ export default {
|
|
|
242
278
|
this.localCameraStatus = e.isCameraEnabled;
|
|
243
279
|
this.localMicrophoneStatus = e.isMicrophoneEnabled;
|
|
244
280
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
this.
|
|
248
|
-
|
|
249
|
-
|
|
281
|
+
// 利用 videoCallRender 的 remove 标记判断离会
|
|
282
|
+
if (e?.remove === true && e.identity === this.curSpeaker?.identity) {
|
|
283
|
+
this.switchToLocalAsCurrentSpeaker()
|
|
284
|
+
return
|
|
285
|
+
}
|
|
286
|
+
if (e.identity === this.curSpeaker?.identity) {
|
|
287
|
+
// 更新当前讲话者音视频状态
|
|
288
|
+
this.curSpeakerCameraStatus = this.roomMode === 'auto' ? !!e.isCameraEnabled : false
|
|
289
|
+
this.curSpeakerMicrophoneStatus = !!e.isMicrophoneEnabled
|
|
290
|
+
|
|
291
|
+
const videoElm = document.getElementById('video-contain')
|
|
292
|
+
|
|
293
|
+
if (this.roomMode === 'auto' && e.videoTrack && videoElm) {
|
|
294
|
+
// 使用 sid 作为更稳定的身份标识;sid 发布后会保持不变
|
|
295
|
+
const oldTrack = this.curSpeakerTrack
|
|
296
|
+
const newTrack = e.videoTrack
|
|
297
|
+
if (oldTrack) {
|
|
298
|
+
const sameBySid = oldTrack.sid && newTrack?.sid && oldTrack.sid === newTrack.sid
|
|
299
|
+
// 当 sid 不同或对象引用不同(无 sid 时),说明发生实际轨道切换,需要解绑旧轨道
|
|
300
|
+
if (!sameBySid) {
|
|
301
|
+
try { oldTrack.detach(videoElm) } catch (err) {}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
this.curSpeakerTrack = newTrack
|
|
305
|
+
// 重新绑定(同轨道重复 attach 时内部已处理,不影响稳定性)
|
|
306
|
+
try { this.curSpeakerTrack.attach(videoElm) } catch (err) {}
|
|
307
|
+
} else {
|
|
308
|
+
// 摄像头关闭或无轨道,解绑任何已绑定轨道
|
|
309
|
+
if (this.curSpeakerTrack && videoElm) {
|
|
310
|
+
try { this.curSpeakerTrack.detach(videoElm) } catch (err) {}
|
|
311
|
+
}
|
|
312
|
+
this.curSpeakerTrack = null
|
|
250
313
|
}
|
|
251
314
|
}
|
|
252
315
|
},
|
|
253
316
|
|
|
317
|
+
// 音频会议渲染事件:不进行任何视频轨道的绑定,仅更新状态并显示占位
|
|
318
|
+
handleAudioCallRender(e) {
|
|
319
|
+
if (e?.remove === true && e.identity === this.curSpeaker?.identity) {
|
|
320
|
+
this.switchToLocalAsCurrentSpeaker()
|
|
321
|
+
return
|
|
322
|
+
}
|
|
323
|
+
if (e.local) {
|
|
324
|
+
this.localMicrophoneStatus = !!e.isMicrophoneEnabled
|
|
325
|
+
}
|
|
326
|
+
if (e.identity === this.curSpeaker?.identity) {
|
|
327
|
+
this.curSpeakerMicrophoneStatus = !!e.isMicrophoneEnabled
|
|
328
|
+
// 音频模式下不显示摄像头画面
|
|
329
|
+
this.curSpeakerCameraStatus = false
|
|
330
|
+
// 确保解绑任何已绑定的视频轨道
|
|
331
|
+
this.detachCurrentTrack()
|
|
332
|
+
this.curSpeakerTrack = null
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
|
|
254
336
|
handleRoomDisconnected() {
|
|
255
337
|
this.$emit('miniVideoDialogClose');
|
|
256
338
|
},
|
|
@@ -260,27 +342,25 @@ export default {
|
|
|
260
342
|
},
|
|
261
343
|
|
|
262
344
|
dispatchLiveClientEvent() {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
liveClient.off("
|
|
266
|
-
liveClient.off("
|
|
267
|
-
liveClient.off("
|
|
268
|
-
liveClient.off("
|
|
269
|
-
liveClient.off("roomDisconnected", this.handleRoomDisconnected);
|
|
345
|
+
if(this.liveClient) {
|
|
346
|
+
this.liveClient.off("localCameraChange", this.handleLocalCameraChange);
|
|
347
|
+
this.liveClient.off("localMicrophoneChange", this.handleLocalMicrophoneChange);
|
|
348
|
+
this.liveClient.off("activeSpeakerChange", this.handleActiveSpeakerChange);
|
|
349
|
+
this.liveClient.off("videoCallRender", this.handleVideoCallRender);
|
|
350
|
+
this.liveClient.off("audioCallRender", this.handleAudioCallRender)
|
|
351
|
+
this.liveClient.off("roomDisconnected", this.handleRoomDisconnected);
|
|
270
352
|
}
|
|
271
353
|
},
|
|
272
354
|
|
|
273
355
|
async changeLocalMicrophoneStatus() {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
await liveClient.changeMicrophoneStatus();
|
|
356
|
+
if(this.liveClient) {
|
|
357
|
+
await this.liveClient.changeMicrophoneStatus();
|
|
277
358
|
}
|
|
278
359
|
},
|
|
279
360
|
|
|
280
361
|
async changeLocalCameraStatus() {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
await liveClient.changeCameraStatus();
|
|
362
|
+
if(this.liveClient) {
|
|
363
|
+
await this.liveClient.changeCameraStatus();
|
|
284
364
|
}
|
|
285
365
|
},
|
|
286
366
|
|
|
@@ -290,6 +370,42 @@ export default {
|
|
|
290
370
|
this.footerTimer = null;
|
|
291
371
|
}
|
|
292
372
|
},
|
|
373
|
+
|
|
374
|
+
// 工具:解绑当前视频轨道
|
|
375
|
+
detachCurrentTrack() {
|
|
376
|
+
try {
|
|
377
|
+
const videoElm = document.getElementById('video-contain')
|
|
378
|
+
if (this.curSpeakerTrack && videoElm) {
|
|
379
|
+
this.curSpeakerTrack.detach(videoElm)
|
|
380
|
+
}
|
|
381
|
+
} catch (e) {}
|
|
382
|
+
},
|
|
383
|
+
// 工具:切换当前讲话者为本地参会者
|
|
384
|
+
switchToLocalAsCurrentSpeaker() {
|
|
385
|
+
try {
|
|
386
|
+
const lp = this.liveClient?.room?.localParticipant
|
|
387
|
+
this.detachCurrentTrack()
|
|
388
|
+
if (lp) {
|
|
389
|
+
this.curSpeaker = {
|
|
390
|
+
identity: lp.identity,
|
|
391
|
+
name: lp.name,
|
|
392
|
+
isLocal: true,
|
|
393
|
+
}
|
|
394
|
+
this.curSpeakerCameraStatus = this.roomMode === 'auto' ? !!lp.isCameraEnabled : false
|
|
395
|
+
this.curSpeakerMicrophoneStatus = !!lp.isMicrophoneEnabled
|
|
396
|
+
// 立即尝试绑定已知的本地轨道(如存在),后续仍会由 videoCallRender 纠正
|
|
397
|
+
if (this.roomMode === 'auto') {
|
|
398
|
+
const track = this.trackData?.videoTrack
|
|
399
|
+
if (track && this.curSpeakerCameraStatus) {
|
|
400
|
+
this.curSpeakerTrack = track
|
|
401
|
+
this.initVideoAttach()
|
|
402
|
+
}
|
|
403
|
+
} else {
|
|
404
|
+
this.curSpeakerTrack = null
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
} catch (e) {}
|
|
408
|
+
}
|
|
293
409
|
},
|
|
294
410
|
}
|
|
295
411
|
</script>
|
|
@@ -318,6 +434,25 @@ export default {
|
|
|
318
434
|
border-radius: 4px;
|
|
319
435
|
object-fit: contain;
|
|
320
436
|
}
|
|
437
|
+
// 与 liveMultipleMeeting 保持一致的缺省板样式
|
|
438
|
+
.board {
|
|
439
|
+
position: absolute;
|
|
440
|
+
width: 100%;
|
|
441
|
+
height: 100%;
|
|
442
|
+
left: 0;
|
|
443
|
+
top: 0;
|
|
444
|
+
z-index: 30;
|
|
445
|
+
display: flex;
|
|
446
|
+
align-items: center;
|
|
447
|
+
justify-content: center;
|
|
448
|
+
background: var(--meeting-board-bg) no-repeat center / 100% 100%;
|
|
449
|
+
.board-icon {
|
|
450
|
+
width: 20%;
|
|
451
|
+
height: auto;
|
|
452
|
+
aspect-ratio: 1/1;
|
|
453
|
+
background: url("../../assets/image/common/default_avatar.png") no-repeat center / 100% 100%;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
321
456
|
.describe {
|
|
322
457
|
position: absolute;
|
|
323
458
|
bottom: 6px;
|
|
@@ -47,6 +47,31 @@
|
|
|
47
47
|
</template>
|
|
48
48
|
</vue-virtual-tree>
|
|
49
49
|
|
|
50
|
+
<!-- 会议终端树 -->
|
|
51
|
+
<vue-virtual-tree
|
|
52
|
+
v-show="activeTab === '会议终端'"
|
|
53
|
+
ref="terminalTreeRef"
|
|
54
|
+
:data="terminalTreeData"
|
|
55
|
+
:props="{ children: 'children', label: 'label' }"
|
|
56
|
+
node-key="id"
|
|
57
|
+
show-checkbox
|
|
58
|
+
:height="dynamicTreeHeight"
|
|
59
|
+
:item-height="36"
|
|
60
|
+
@check="handleNodeCheck"
|
|
61
|
+
:filter-node-method="filterNode"
|
|
62
|
+
>
|
|
63
|
+
<template #default="{ node, data }">
|
|
64
|
+
<span class="custom-tree-node">
|
|
65
|
+
<span class="custom-tree-node-label" :title="node.label" v-if="data.isDept">{{
|
|
66
|
+
node.label
|
|
67
|
+
}}</span>
|
|
68
|
+
<span class="custom-tree-node-user" v-else>
|
|
69
|
+
<span :class="['custom-tree-node-icon']"></span>
|
|
70
|
+
<span class="custom-tree-node-label" :title="node.label">{{ node.label }}</span>
|
|
71
|
+
</span>
|
|
72
|
+
</span>
|
|
73
|
+
</template>
|
|
74
|
+
</vue-virtual-tree>
|
|
50
75
|
<!-- 应用树 -->
|
|
51
76
|
<vue-virtual-tree
|
|
52
77
|
v-show="activeTab === '人员'"
|
|
@@ -192,6 +217,15 @@
|
|
|
192
217
|
<div class="label">
|
|
193
218
|
<span :class="['custom-item-icon', getInviteItemIconClass(item)]"></span>
|
|
194
219
|
<span>{{ item.label }}</span>
|
|
220
|
+
<!-- 会议终端:小输入框编辑会议ID,Enter/失焦提交 -->
|
|
221
|
+
<el-input
|
|
222
|
+
v-if="item.source === '会议终端' && item.callMethod == 1"
|
|
223
|
+
v-model="item._callMeetingID"
|
|
224
|
+
class="terminal-id-input"
|
|
225
|
+
placeholder="请输入会议ID"
|
|
226
|
+
@keyup.enter="confirmTerminalMeetingId(item)"
|
|
227
|
+
@blur="confirmTerminalMeetingId(item)"
|
|
228
|
+
/>
|
|
195
229
|
<span :class="getChosenItemPlatformClass(item)"></span>
|
|
196
230
|
</div>
|
|
197
231
|
<div class="close" @click="removeInviteItem(item)"></div>
|
|
@@ -234,7 +268,7 @@
|
|
|
234
268
|
|
|
235
269
|
<script>
|
|
236
270
|
import { debounce } from "lodash-es";
|
|
237
|
-
import { mittBus, ShowMessage, deleteCommonGroupAndUser, getAddressOrgTree, getAddressAppTree, getCommonOrgTree, queryEquipmentAddressBook, queryMonitorAddressBook, getVolteAddressBook } from "../../utils/index";
|
|
271
|
+
import { mittBus, ShowMessage, deleteCommonGroupAndUser, getAddressOrgTree, getAddressAppTree, getCommonOrgTree, queryEquipmentAddressBook, queryMonitorAddressBook, getVolteAddressBook, getMeetingRoomAddressBook, updateConfTerminal } from "../../utils/index";
|
|
238
272
|
import VueVirtualTree from "@fit2cloud-ui/vue-virtual-tree";
|
|
239
273
|
import CustomGroupDialog from "./customGroupDialog.vue";
|
|
240
274
|
import EditGroupDialog from "./editGroupDialog.vue";
|
|
@@ -263,6 +297,12 @@ export default {
|
|
|
263
297
|
type: String,
|
|
264
298
|
default: "",
|
|
265
299
|
},
|
|
300
|
+
tabList: {
|
|
301
|
+
type: Array,
|
|
302
|
+
default: () => {
|
|
303
|
+
return ["组织架构", "人员", "会议终端", "volte", "设备", "监控", "常用分组"];
|
|
304
|
+
},
|
|
305
|
+
}
|
|
266
306
|
},
|
|
267
307
|
data() {
|
|
268
308
|
return {
|
|
@@ -270,6 +310,7 @@ export default {
|
|
|
270
310
|
|
|
271
311
|
// 树数据
|
|
272
312
|
treeData: [],
|
|
313
|
+
terminalTreeData: [],
|
|
273
314
|
appTreeData: [],
|
|
274
315
|
volteTreeData: [],
|
|
275
316
|
groupTreeData: [],
|
|
@@ -278,7 +319,6 @@ export default {
|
|
|
278
319
|
|
|
279
320
|
// 标签页相关
|
|
280
321
|
activeTab: "组织架构",
|
|
281
|
-
tabList: ["组织架构", "人员", "volte", "设备", "监控", "常用分组"],
|
|
282
322
|
|
|
283
323
|
// 搜索相关
|
|
284
324
|
filterText: "",
|
|
@@ -344,17 +384,17 @@ export default {
|
|
|
344
384
|
|
|
345
385
|
// 监听对话框显示状态
|
|
346
386
|
isaddressShow: {
|
|
347
|
-
|
|
387
|
+
handler(newVal) {
|
|
348
388
|
if (newVal) {
|
|
349
389
|
// 初始化选中状态
|
|
350
390
|
this.isSyncing = true;
|
|
351
391
|
this.chosenList = [...this.tempInviteList];
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
392
|
+
this.$nextTick(() => {
|
|
393
|
+
this.syncTreeSelectionFromChosenList();
|
|
394
|
+
// 更新树高度
|
|
395
|
+
this.updateTreeHeight();
|
|
396
|
+
this.isSyncing = false;
|
|
397
|
+
});
|
|
358
398
|
}
|
|
359
399
|
}
|
|
360
400
|
}
|
|
@@ -377,7 +417,9 @@ export default {
|
|
|
377
417
|
this.$refs.equipmentTreeRef.filter(val);
|
|
378
418
|
} else if (this.activeTab === "监控" && this.$refs.monitorTreeRef) {
|
|
379
419
|
this.$refs.monitorTreeRef.filter(val);
|
|
380
|
-
}
|
|
420
|
+
} else if (this.activeTab === "会议终端" && this.$refs.terminalTreeRef) {
|
|
421
|
+
this.$refs.terminalTreeRef.filter(val);
|
|
422
|
+
}
|
|
381
423
|
}, 300);
|
|
382
424
|
|
|
383
425
|
// 监听mitt事件
|
|
@@ -460,7 +502,20 @@ export default {
|
|
|
460
502
|
this.monitorTreeData = res.data.data;
|
|
461
503
|
}
|
|
462
504
|
},
|
|
463
|
-
|
|
505
|
+
// 获取会议终端树数据
|
|
506
|
+
async getTerminalTreeData() {
|
|
507
|
+
try {
|
|
508
|
+
const res = await getMeetingRoomAddressBook({}, { baseUrl: this.baseUrl, token: this.token });
|
|
509
|
+
if (res.data?.code == 200) {
|
|
510
|
+
const data = res.data?.data || [];
|
|
511
|
+
this.terminalTreeData = data.length > 0 ? data : [];
|
|
512
|
+
} else {
|
|
513
|
+
this.terminalTreeData = [];
|
|
514
|
+
}
|
|
515
|
+
} catch (e) {
|
|
516
|
+
this.terminalTreeData = [];
|
|
517
|
+
}
|
|
518
|
+
},
|
|
464
519
|
// 获取所有树数据
|
|
465
520
|
async getAllTreeData() {
|
|
466
521
|
await this.getOrgTreeData();
|
|
@@ -469,6 +524,7 @@ export default {
|
|
|
469
524
|
await this.getGroupTreeData();
|
|
470
525
|
await this.getEquipmentTreeData();
|
|
471
526
|
await this.getMonitorTreeData();
|
|
527
|
+
await this.getTerminalTreeData();
|
|
472
528
|
},
|
|
473
529
|
|
|
474
530
|
// 过滤树节点的方法 - 虚拟树使用filter-node-method
|
|
@@ -496,16 +552,20 @@ export default {
|
|
|
496
552
|
// 修改判断条件,只有当isDept为false时,才是叶子节点
|
|
497
553
|
// isDept为true的节点(即使是空数组)都视为非叶子节点
|
|
498
554
|
if (!node.isDept) {
|
|
499
|
-
leafNodes.push(
|
|
500
|
-
...node,
|
|
501
|
-
source: this.activeTab, // 记录节点来源于哪个树
|
|
502
|
-
});
|
|
555
|
+
leafNodes.push(this.prepareChosenUser(node, this.activeTab));
|
|
503
556
|
}
|
|
504
557
|
});
|
|
505
558
|
|
|
506
559
|
return leafNodes;
|
|
507
560
|
},
|
|
508
|
-
|
|
561
|
+
// 构建已选用户对象:当来源为“会议终端”时,初始化 _callMeetingID,避免输入框为空
|
|
562
|
+
prepareChosenUser(raw, source) {
|
|
563
|
+
const user = { ...raw, source };
|
|
564
|
+
if (source === '会议终端') {
|
|
565
|
+
user._callMeetingID = raw?.callMeetingID ?? '';
|
|
566
|
+
}
|
|
567
|
+
return user;
|
|
568
|
+
},
|
|
509
569
|
// 更新chosenList,保留其他树的选中状态
|
|
510
570
|
updateChosenListWithLeafNodes(newLeafNodes) {
|
|
511
571
|
// 从chosenList中过滤掉当前激活树的节点
|
|
@@ -565,6 +625,8 @@ export default {
|
|
|
565
625
|
this.$refs.equipmentTreeRef.setCheckedKeys([]);
|
|
566
626
|
} else if (this.activeTab === "监控" && this.$refs.monitorTreeRef) {
|
|
567
627
|
this.$refs.monitorTreeRef.setCheckedKeys([]);
|
|
628
|
+
} else if (this.activeTab === "会议终端" && this.$refs.terminalTreeRef) {
|
|
629
|
+
this.$refs.terminalTreeRef.setCheckedKeys([]);
|
|
568
630
|
}
|
|
569
631
|
|
|
570
632
|
// 然后根据chosenList设置选中状态
|
|
@@ -594,6 +656,10 @@ export default {
|
|
|
594
656
|
selectedIds.forEach((id) => {
|
|
595
657
|
this.$refs.monitorTreeRef.setChecked(id, true, false);
|
|
596
658
|
});
|
|
659
|
+
} else if (this.activeTab === "会议终端" && this.$refs.terminalTreeRef) {
|
|
660
|
+
selectedIds.forEach((id) => {
|
|
661
|
+
this.$refs.terminalTreeRef.setChecked(id, true, false);
|
|
662
|
+
});
|
|
597
663
|
}
|
|
598
664
|
},
|
|
599
665
|
|
|
@@ -666,11 +732,11 @@ export default {
|
|
|
666
732
|
this.showMessage.message("error", "请先选择要添加的人员");
|
|
667
733
|
return;
|
|
668
734
|
}
|
|
669
|
-
if(this.tempInviteList.some((item) => item.source === '设备' || item.source === '监控')) {
|
|
735
|
+
if(this.tempInviteList.some((item) => item.source === '设备' || item.source === '监控' || item.source === '会议终端')) {
|
|
670
736
|
this.showMessage.message("info", "设备和监控人员暂不支持添加分组, 将不会被添加到常用分组");
|
|
671
737
|
}
|
|
672
738
|
// 确保数据格式正确
|
|
673
|
-
const formattedList = this.tempInviteList.filter((item) => item.source !== '设备' && item.source !== '监控').map((item) => {
|
|
739
|
+
const formattedList = this.tempInviteList.filter((item) => item.source !== '设备' && item.source !== '监控' && item.source !== '常用分组' && item.source !== '会议终端').map((item) => {
|
|
674
740
|
return {
|
|
675
741
|
id: item.id,
|
|
676
742
|
label: item.label,
|
|
@@ -737,12 +803,33 @@ export default {
|
|
|
737
803
|
if( this.$refs.volteTreeRef) {
|
|
738
804
|
this.$refs.volteTreeRef.setChecked(item.id, false);
|
|
739
805
|
}
|
|
806
|
+
if (this.$refs.terminalTreeRef) {
|
|
807
|
+
this.$refs.terminalTreeRef.setChecked(item.id, false);
|
|
808
|
+
}
|
|
740
809
|
this.$nextTick(() => {
|
|
741
810
|
this.isSyncing = false;
|
|
742
811
|
});
|
|
743
812
|
}
|
|
744
813
|
},
|
|
745
814
|
|
|
815
|
+
// 更新会议终端会议ID(Enter/失焦)
|
|
816
|
+
async confirmTerminalMeetingId(user) {
|
|
817
|
+
const newId = (user._callMeetingID || '').toString().trim()
|
|
818
|
+
const oldId = (user.callMeetingID || '').toString().trim()
|
|
819
|
+
if (newId === oldId) return
|
|
820
|
+
try {
|
|
821
|
+
const res = await updateConfTerminal({ id: user.id, callMeetingID: newId }, { baseUrl: props.baseUrl, token: props.token })
|
|
822
|
+
if (res.data?.code == 200) {
|
|
823
|
+
user.callMeetingID = newId
|
|
824
|
+
this.showMessage.message('success', '会议ID已更新')
|
|
825
|
+
} else {
|
|
826
|
+
this.showMessage.message('error', res.data?.msg || '更新会议ID失败')
|
|
827
|
+
}
|
|
828
|
+
} catch (e) {
|
|
829
|
+
this.showMessage.message('error', e.message || '网络异常,更新会议ID失败')
|
|
830
|
+
}
|
|
831
|
+
},
|
|
832
|
+
|
|
746
833
|
cancel() {
|
|
747
834
|
this.tempInviteList = [];
|
|
748
835
|
this.isaddressShow = false;
|
|
@@ -750,7 +837,7 @@ export default {
|
|
|
750
837
|
|
|
751
838
|
confirm() {
|
|
752
839
|
// 转换数据格式以保持向后兼容
|
|
753
|
-
const convertedList = this.tempInviteList.filter((item) => item.source !== '设备' && item.source !== '监控').map((item) => {
|
|
840
|
+
const convertedList = this.tempInviteList.filter((item) => item.source !== '设备' && item.source !== '监控' && item.source !== '会议终端').map((item) => {
|
|
754
841
|
return {
|
|
755
842
|
...item,
|
|
756
843
|
};
|
|
@@ -760,11 +847,17 @@ export default {
|
|
|
760
847
|
...item,
|
|
761
848
|
};
|
|
762
849
|
});
|
|
763
|
-
|
|
850
|
+
const convertedTerminalList = this.tempInviteList.filter(item => item.source === '会议终端')
|
|
851
|
+
.map(item => {
|
|
852
|
+
return {
|
|
853
|
+
...item,
|
|
854
|
+
}
|
|
855
|
+
})
|
|
764
856
|
this.$emit("inviteListUpdate", convertedList);
|
|
765
857
|
if (this.isInner) {
|
|
766
858
|
this.$emit("appendInvitePeople", convertedList);
|
|
767
859
|
this.$emit("appendInviteDevice", convertedDeviceList);
|
|
860
|
+
this.$emit("appendInviteTerminal", convertedTerminalList);
|
|
768
861
|
}
|
|
769
862
|
this.isaddressShow = false;
|
|
770
863
|
},
|
|
@@ -1134,6 +1227,30 @@ export default {
|
|
|
1134
1227
|
font-size: 16px;
|
|
1135
1228
|
color: var(--theme-font-color);
|
|
1136
1229
|
|
|
1230
|
+
:deep(.el-input) {
|
|
1231
|
+
margin-left: 8px;
|
|
1232
|
+
width: 100px;
|
|
1233
|
+
line-height: 28px;
|
|
1234
|
+
height: 28px;
|
|
1235
|
+
border: none;
|
|
1236
|
+
.el-input__inner {
|
|
1237
|
+
box-shadow: none;
|
|
1238
|
+
background-color: transparent;
|
|
1239
|
+
border-radius: 0px;
|
|
1240
|
+
border: none;
|
|
1241
|
+
border-bottom: 1px solid var(--input-border-color);
|
|
1242
|
+
padding: 0 6px;
|
|
1243
|
+
color: var(--theme-font-color);
|
|
1244
|
+
font-size: 14px;
|
|
1245
|
+
font-weight: 400;
|
|
1246
|
+
line-height: 28px;
|
|
1247
|
+
&::placeholder {
|
|
1248
|
+
color: var(--input-placeholder-color);
|
|
1249
|
+
font-family: var(--main-font);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1137
1254
|
.custom-item-icon {
|
|
1138
1255
|
width: 20px;
|
|
1139
1256
|
height: 20px;
|