jssz-meeting-component 1.2.1 → 1.2.2
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/dist/index.esm.js +1242 -1073
- package/dist/index.umd.js +1 -1
- package/dist/src/components/meeting.vue.d.ts.map +1 -1
- package/dist/src/internal/meeting-header.vue.d.ts.map +1 -1
- package/dist/src/utils/eventBus.d.ts +1 -0
- package/dist/src/utils/eventBus.d.ts.map +1 -1
- package/dist/src/utils/meeting-message-handlers.d.ts.map +1 -1
- package/dist/src/utils/srtc.d.ts.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -20339,7 +20339,7 @@ const MEETING_BROADCAST_TOPIC = "/meeting";
|
|
|
20339
20339
|
const MEETING_SERVER_TOPIC = "/meeting/server";
|
|
20340
20340
|
const DEFAULT_MQTT_PATH = "/mqtt";
|
|
20341
20341
|
const DEFAULT_REQUEST_TIMEOUT_MS = 15e3;
|
|
20342
|
-
const MQTT_CLIENT_INSTANCE_STORAGE_KEY = "JS_MQTT_CLIENT_INSTANCE_ID";
|
|
20342
|
+
const MQTT_CLIENT_INSTANCE_STORAGE_KEY$1 = "JS_MQTT_CLIENT_INSTANCE_ID";
|
|
20343
20343
|
let runtimeContext = null;
|
|
20344
20344
|
let offBroadcastMessage = null;
|
|
20345
20345
|
let offResponseMessage = null;
|
|
@@ -20422,12 +20422,12 @@ function buildResponseTopic(currentDevice) {
|
|
|
20422
20422
|
}
|
|
20423
20423
|
function getBrowserMqttInstanceId() {
|
|
20424
20424
|
try {
|
|
20425
|
-
const cached = sessionStorage.getItem(MQTT_CLIENT_INSTANCE_STORAGE_KEY);
|
|
20425
|
+
const cached = sessionStorage.getItem(MQTT_CLIENT_INSTANCE_STORAGE_KEY$1);
|
|
20426
20426
|
if (cached) {
|
|
20427
20427
|
return cached;
|
|
20428
20428
|
}
|
|
20429
20429
|
const nextId = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID().replace(/-/g, "").slice(0, 12) : Math.random().toString(36).slice(2, 14);
|
|
20430
|
-
sessionStorage.setItem(MQTT_CLIENT_INSTANCE_STORAGE_KEY, nextId);
|
|
20430
|
+
sessionStorage.setItem(MQTT_CLIENT_INSTANCE_STORAGE_KEY$1, nextId);
|
|
20431
20431
|
return nextId;
|
|
20432
20432
|
} catch {
|
|
20433
20433
|
return Math.random().toString(36).slice(2, 14);
|
|
@@ -36514,6 +36514,12 @@ function getCurrentMeetingTabId() {
|
|
|
36514
36514
|
}
|
|
36515
36515
|
return tabId;
|
|
36516
36516
|
}
|
|
36517
|
+
function setMeetingTabIdForWindow(targetWindow, tabId) {
|
|
36518
|
+
targetWindow.sessionStorage.setItem(TAB_ID_KEY, tabId);
|
|
36519
|
+
}
|
|
36520
|
+
function createMeetingTabId() {
|
|
36521
|
+
return createTabId();
|
|
36522
|
+
}
|
|
36517
36523
|
function getActiveMeetingOwner() {
|
|
36518
36524
|
try {
|
|
36519
36525
|
const raw = localStorage.getItem(ACTIVE_MEETING_OWNER_KEY);
|
|
@@ -37895,8 +37901,45 @@ async function joinRoom() {
|
|
|
37895
37901
|
const meetingInfo = JSON.parse(
|
|
37896
37902
|
sessionStorage.getItem("JS_MEETING_INFO") || "{}"
|
|
37897
37903
|
);
|
|
37898
|
-
|
|
37899
|
-
|
|
37904
|
+
try {
|
|
37905
|
+
await srtc.join(meetingInfo.meetingToken);
|
|
37906
|
+
await afterJoin();
|
|
37907
|
+
} catch (error) {
|
|
37908
|
+
await handleJoinRoomFailure(meetingInfo, error);
|
|
37909
|
+
throw error;
|
|
37910
|
+
}
|
|
37911
|
+
}
|
|
37912
|
+
async function handleJoinRoomFailure(meetingInfo, error) {
|
|
37913
|
+
console.error("加入 SRTC 频道失败", error);
|
|
37914
|
+
stopNetworkQualityMonitor();
|
|
37915
|
+
await cleanupSessionMediaState();
|
|
37916
|
+
if (meetingInfo == null ? void 0 : meetingInfo.meetingId) {
|
|
37917
|
+
try {
|
|
37918
|
+
await getMeetingLeave({
|
|
37919
|
+
meetingId: meetingInfo.meetingId,
|
|
37920
|
+
memberId: getUserId()
|
|
37921
|
+
});
|
|
37922
|
+
} catch (leaveError) {
|
|
37923
|
+
console.error("SDK 入频道失败后的离会补偿失败", leaveError);
|
|
37924
|
+
}
|
|
37925
|
+
}
|
|
37926
|
+
meetingStore$1.clearMembers();
|
|
37927
|
+
try {
|
|
37928
|
+
sessionStorage.removeItem(SELF_MEDIA_PREFS_KEY);
|
|
37929
|
+
} catch (_2) {
|
|
37930
|
+
}
|
|
37931
|
+
clearActiveMeetingOwnership(
|
|
37932
|
+
meetingInfo == null ? void 0 : meetingInfo.meetingId,
|
|
37933
|
+
getCurrentMeetingTabId()
|
|
37934
|
+
);
|
|
37935
|
+
sessionStorage.removeItem("JS_MEETING_INFO");
|
|
37936
|
+
sessionStorage.removeItem("joinTime");
|
|
37937
|
+
joinTime.value = 0;
|
|
37938
|
+
setCallStatus("end");
|
|
37939
|
+
closeMeeting();
|
|
37940
|
+
closeCall();
|
|
37941
|
+
logoutRoomAction();
|
|
37942
|
+
showNotification("加入音视频频道失败,已自动退出,请稍后重试", "error", 6e3);
|
|
37900
37943
|
}
|
|
37901
37944
|
async function afterJoin() {
|
|
37902
37945
|
const _joinTime = sessionStorage.getItem("joinTime");
|
|
@@ -38761,8 +38804,13 @@ function handleMeetingEvent(event, data) {
|
|
|
38761
38804
|
emitter.emit("injuryReport", data);
|
|
38762
38805
|
break;
|
|
38763
38806
|
case "2020":
|
|
38807
|
+
console.log("2020", data);
|
|
38764
38808
|
emitter.emit("onlineMember", data);
|
|
38765
38809
|
break;
|
|
38810
|
+
case "2099":
|
|
38811
|
+
console.log("2099", data);
|
|
38812
|
+
emitter.emit("meetingEnd", data);
|
|
38813
|
+
break;
|
|
38766
38814
|
}
|
|
38767
38815
|
}
|
|
38768
38816
|
function installMeetingMessageHandlers() {
|
|
@@ -41481,584 +41529,499 @@ const _sfc_main$q = /* @__PURE__ */ defineComponent({
|
|
|
41481
41529
|
}
|
|
41482
41530
|
});
|
|
41483
41531
|
const JSCall = /* @__PURE__ */ _export_sfc(_sfc_main$q, [["__scopeId", "data-v-e1585009"]]);
|
|
41484
|
-
const
|
|
41485
|
-
const
|
|
41486
|
-
const
|
|
41487
|
-
|
|
41488
|
-
|
|
41489
|
-
|
|
41490
|
-
|
|
41491
|
-
|
|
41492
|
-
|
|
41493
|
-
|
|
41494
|
-
|
|
41495
|
-
|
|
41496
|
-
|
|
41497
|
-
|
|
41498
|
-
|
|
41499
|
-
|
|
41500
|
-
|
|
41501
|
-
|
|
41502
|
-
const themeClass = computed(() => `theme-${props.theme || "dark"}`);
|
|
41503
|
-
ref(false);
|
|
41504
|
-
const handleCloseClick = async () => {
|
|
41505
|
-
showCloseConfirm.value = !showCloseConfirm.value;
|
|
41506
|
-
};
|
|
41507
|
-
const handleEndMeeting = () => {
|
|
41508
|
-
showCloseConfirm.value = false;
|
|
41509
|
-
emit("onChange", RoomModalSelectType.endClick);
|
|
41510
|
-
};
|
|
41511
|
-
const handleLeaveMeeting = () => {
|
|
41512
|
-
showCloseConfirm.value = false;
|
|
41513
|
-
emit("onChange", RoomModalSelectType.leaveClick);
|
|
41514
|
-
};
|
|
41515
|
-
return (_ctx, _cache) => {
|
|
41516
|
-
return openBlock(), createElementBlock("div", {
|
|
41517
|
-
class: normalizeClass(["js-dialog-header", themeClass.value])
|
|
41518
|
-
}, [
|
|
41519
|
-
createElementVNode("div", _hoisted_1$n, toDisplayString(meetingInfo.value.meetingName), 1),
|
|
41520
|
-
createElementVNode("div", _hoisted_2$l, [
|
|
41521
|
-
createElementVNode("div", {
|
|
41522
|
-
title: "最小化",
|
|
41523
|
-
onClick: _cache[0] || (_cache[0] = ($event) => emit("onChange", unref(RoomModalSelectType).minimizeClick))
|
|
41524
|
-
}, [
|
|
41525
|
-
createVNode(SvgIcon, {
|
|
41526
|
-
name: "MinimizeIcon",
|
|
41527
|
-
size: 16
|
|
41528
|
-
})
|
|
41529
|
-
]),
|
|
41530
|
-
createElementVNode("div", {
|
|
41531
|
-
ref_key: "closeRef",
|
|
41532
|
-
ref: closeRef,
|
|
41533
|
-
title: "关闭",
|
|
41534
|
-
onClick: handleCloseClick
|
|
41535
|
-
}, [
|
|
41536
|
-
createVNode(SvgIcon, {
|
|
41537
|
-
name: "CloseIcon",
|
|
41538
|
-
size: 17
|
|
41539
|
-
})
|
|
41540
|
-
], 512)
|
|
41541
|
-
]),
|
|
41542
|
-
createVNode(SmartPopup, {
|
|
41543
|
-
visible: showCloseConfirm.value,
|
|
41544
|
-
"onUpdate:visible": _cache[2] || (_cache[2] = ($event) => showCloseConfirm.value = $event),
|
|
41545
|
-
trigger: closeRef.value,
|
|
41546
|
-
gap: 10
|
|
41547
|
-
}, {
|
|
41548
|
-
default: withCtx(() => [
|
|
41549
|
-
createElementVNode("div", {
|
|
41550
|
-
class: normalizeClass(["close-confirm-dropdown", themeClass.value])
|
|
41551
|
-
}, [
|
|
41552
|
-
unref(meState2).roleType == "1" ? (openBlock(), createElementBlock("div", {
|
|
41553
|
-
key: 0,
|
|
41554
|
-
class: "close-confirm-option danger",
|
|
41555
|
-
onClick: withModifiers(handleEndMeeting, ["stop"])
|
|
41556
|
-
}, " 结束会议 ")) : createCommentVNode("", true),
|
|
41557
|
-
createElementVNode("div", {
|
|
41558
|
-
class: "close-confirm-option simple",
|
|
41559
|
-
onClick: withModifiers(handleLeaveMeeting, ["stop"])
|
|
41560
|
-
}, " 离开会议 "),
|
|
41561
|
-
createElementVNode("div", {
|
|
41562
|
-
class: "close-confirm-option simple",
|
|
41563
|
-
onClick: _cache[1] || (_cache[1] = ($event) => showCloseConfirm.value = false)
|
|
41564
|
-
}, " 取消 ")
|
|
41565
|
-
], 2)
|
|
41566
|
-
]),
|
|
41567
|
-
_: 1
|
|
41568
|
-
}, 8, ["visible", "trigger"])
|
|
41569
|
-
], 2);
|
|
41570
|
-
};
|
|
41532
|
+
const HANDOFF_SOURCE_TAB_ID_KEY = "JS_MEETING_MQTT_HANDOFF_SOURCE_TAB_ID";
|
|
41533
|
+
const HANDOFF_TARGET_TAB_ID_KEY = "JS_MEETING_MQTT_HANDOFF_TARGET_TAB_ID";
|
|
41534
|
+
const HANDOFF_ID_KEY = "JS_MEETING_MQTT_HANDOFF_ID";
|
|
41535
|
+
const RECONNECT_SIGNAL_KEY = "JS_MEETING_MQTT_RECONNECT_SIGNAL";
|
|
41536
|
+
const TARGET_HEARTBEAT_KEY = "JS_MEETING_MQTT_HANDOFF_HEARTBEAT";
|
|
41537
|
+
const TARGET_TAB_STATUS_KEY = "JS_MEETING_NEW_TAB_STATUS";
|
|
41538
|
+
const TARGET_HEARTBEAT_INTERVAL_MS = 1e3;
|
|
41539
|
+
const RECONNECT_CHECK_DELAY_MS = 1500;
|
|
41540
|
+
const TARGET_TAB_STATUS_CLOSED = 0;
|
|
41541
|
+
const TARGET_TAB_STATUS_OPEN = 1;
|
|
41542
|
+
let installed = false;
|
|
41543
|
+
let closeSignalSent = false;
|
|
41544
|
+
let heartbeatTimer = null;
|
|
41545
|
+
let lastHandledReconnectSignalTs = 0;
|
|
41546
|
+
let leaveRequestSent = false;
|
|
41547
|
+
function createRandomId() {
|
|
41548
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
41549
|
+
return crypto.randomUUID().replace(/-/g, "").slice(0, 12);
|
|
41571
41550
|
}
|
|
41572
|
-
|
|
41573
|
-
|
|
41574
|
-
|
|
41575
|
-
|
|
41576
|
-
|
|
41577
|
-
|
|
41578
|
-
|
|
41579
|
-
|
|
41580
|
-
|
|
41581
|
-
|
|
41582
|
-
|
|
41583
|
-
|
|
41584
|
-
|
|
41585
|
-
|
|
41586
|
-
|
|
41587
|
-
|
|
41588
|
-
|
|
41589
|
-
|
|
41590
|
-
}
|
|
41591
|
-
|
|
41592
|
-
|
|
41593
|
-
|
|
41594
|
-
|
|
41595
|
-
const
|
|
41596
|
-
const
|
|
41597
|
-
const
|
|
41598
|
-
|
|
41599
|
-
|
|
41600
|
-
|
|
41601
|
-
|
|
41602
|
-
|
|
41603
|
-
|
|
41604
|
-
|
|
41605
|
-
|
|
41606
|
-
|
|
41607
|
-
|
|
41608
|
-
|
|
41609
|
-
|
|
41610
|
-
|
|
41611
|
-
|
|
41612
|
-
|
|
41613
|
-
const
|
|
41614
|
-
|
|
41615
|
-
|
|
41616
|
-
const _hoisted_31$1 = { class: "popup-item-value" };
|
|
41617
|
-
const _hoisted_32$1 = {
|
|
41618
|
-
key: 1,
|
|
41619
|
-
class: "popup-empty"
|
|
41620
|
-
};
|
|
41621
|
-
const _sfc_main$o = /* @__PURE__ */ defineComponent({
|
|
41622
|
-
__name: "NetworkQualitySignal",
|
|
41623
|
-
props: {
|
|
41624
|
-
theme: { default: "dark" },
|
|
41625
|
-
size: { default: "default" },
|
|
41626
|
-
showText: { type: Boolean, default: false }
|
|
41627
|
-
},
|
|
41628
|
-
setup(__props) {
|
|
41629
|
-
const props = __props;
|
|
41630
|
-
const triggerRef = ref(null);
|
|
41631
|
-
const showPopup = ref(false);
|
|
41632
|
-
const advancedOpen = ref(false);
|
|
41633
|
-
const collapsedSections = ref({});
|
|
41634
|
-
const advancedDetails = ref();
|
|
41635
|
-
let detailTimer = null;
|
|
41636
|
-
const themeClass = computed(() => `theme-${props.theme}`);
|
|
41637
|
-
function formatValue(value, suffix = "", digits = 0) {
|
|
41638
|
-
if (typeof value !== "number" || Number.isNaN(value)) return "--";
|
|
41639
|
-
return `${value.toFixed(digits)}${suffix}`;
|
|
41640
|
-
}
|
|
41641
|
-
function formatMetricNumber(value, suffix = "", digits = 0) {
|
|
41642
|
-
if (typeof value !== "number" || Number.isNaN(value)) return "--";
|
|
41643
|
-
return `${value.toFixed(digits)}${suffix}`;
|
|
41644
|
-
}
|
|
41645
|
-
function formatBytes(value) {
|
|
41646
|
-
if (typeof value !== "number" || Number.isNaN(value)) return "--";
|
|
41647
|
-
if (value >= 1024 * 1024) return `${(value / (1024 * 1024)).toFixed(2)} MB`;
|
|
41648
|
-
if (value >= 1024) return `${(value / 1024).toFixed(1)} KB`;
|
|
41649
|
-
return `${value.toFixed(0)} B`;
|
|
41650
|
-
}
|
|
41651
|
-
function formatDimension(width, height) {
|
|
41652
|
-
if (!width || !height) return "--";
|
|
41653
|
-
return `${width} x ${height}`;
|
|
41654
|
-
}
|
|
41655
|
-
function formatLossRate(value) {
|
|
41656
|
-
if (typeof value !== "number" || Number.isNaN(value)) return "--";
|
|
41657
|
-
return `${value.toFixed(1)}%`;
|
|
41658
|
-
}
|
|
41659
|
-
function formatMos(value) {
|
|
41660
|
-
if (typeof value !== "number" || value <= 0 || Number.isNaN(value)) return "--";
|
|
41661
|
-
return value.toFixed(1);
|
|
41551
|
+
return Math.random().toString(36).slice(2, 14);
|
|
41552
|
+
}
|
|
41553
|
+
function safeSessionStorageGetItem(key) {
|
|
41554
|
+
try {
|
|
41555
|
+
return sessionStorage.getItem(key);
|
|
41556
|
+
} catch {
|
|
41557
|
+
return null;
|
|
41558
|
+
}
|
|
41559
|
+
}
|
|
41560
|
+
function safeLocalStorageSetItem(key, value) {
|
|
41561
|
+
try {
|
|
41562
|
+
localStorage.setItem(key, value);
|
|
41563
|
+
} catch {
|
|
41564
|
+
}
|
|
41565
|
+
}
|
|
41566
|
+
function safeLocalStorageGetItem(key) {
|
|
41567
|
+
try {
|
|
41568
|
+
return localStorage.getItem(key);
|
|
41569
|
+
} catch {
|
|
41570
|
+
return null;
|
|
41571
|
+
}
|
|
41572
|
+
}
|
|
41573
|
+
function getMeetingMqttHandoffContext() {
|
|
41574
|
+
const sourceTabId = safeSessionStorageGetItem(HANDOFF_SOURCE_TAB_ID_KEY) || "";
|
|
41575
|
+
const targetTabId = safeSessionStorageGetItem(HANDOFF_TARGET_TAB_ID_KEY) || "";
|
|
41576
|
+
const handoffId = safeSessionStorageGetItem(HANDOFF_ID_KEY) || "";
|
|
41577
|
+
if (!sourceTabId || !targetTabId || !handoffId) {
|
|
41578
|
+
return null;
|
|
41579
|
+
}
|
|
41580
|
+
return {
|
|
41581
|
+
sourceTabId,
|
|
41582
|
+
targetTabId,
|
|
41583
|
+
handoffId
|
|
41584
|
+
};
|
|
41585
|
+
}
|
|
41586
|
+
function readTargetHeartbeat() {
|
|
41587
|
+
const raw = safeLocalStorageGetItem(TARGET_HEARTBEAT_KEY);
|
|
41588
|
+
if (!raw) {
|
|
41589
|
+
return null;
|
|
41590
|
+
}
|
|
41591
|
+
try {
|
|
41592
|
+
const parsed = JSON.parse(raw);
|
|
41593
|
+
if (!(parsed == null ? void 0 : parsed.targetTabId) || !(parsed == null ? void 0 : parsed.handoffId) || !(parsed == null ? void 0 : parsed.ts)) {
|
|
41594
|
+
return null;
|
|
41662
41595
|
}
|
|
41663
|
-
|
|
41664
|
-
|
|
41665
|
-
|
|
41666
|
-
|
|
41667
|
-
|
|
41668
|
-
|
|
41669
|
-
|
|
41670
|
-
|
|
41671
|
-
|
|
41672
|
-
|
|
41673
|
-
|
|
41674
|
-
|
|
41675
|
-
|
|
41676
|
-
|
|
41677
|
-
|
|
41678
|
-
|
|
41679
|
-
|
|
41680
|
-
|
|
41681
|
-
|
|
41682
|
-
|
|
41683
|
-
|
|
41684
|
-
|
|
41685
|
-
|
|
41686
|
-
|
|
41687
|
-
|
|
41688
|
-
|
|
41689
|
-
|
|
41690
|
-
|
|
41691
|
-
}
|
|
41692
|
-
|
|
41693
|
-
|
|
41694
|
-
|
|
41695
|
-
|
|
41696
|
-
|
|
41596
|
+
return {
|
|
41597
|
+
targetTabId: parsed.targetTabId,
|
|
41598
|
+
handoffId: parsed.handoffId,
|
|
41599
|
+
ts: Number(parsed.ts)
|
|
41600
|
+
};
|
|
41601
|
+
} catch {
|
|
41602
|
+
return null;
|
|
41603
|
+
}
|
|
41604
|
+
}
|
|
41605
|
+
function writeTargetHeartbeat(context) {
|
|
41606
|
+
safeLocalStorageSetItem(
|
|
41607
|
+
TARGET_HEARTBEAT_KEY,
|
|
41608
|
+
JSON.stringify({
|
|
41609
|
+
targetTabId: context.targetTabId,
|
|
41610
|
+
handoffId: context.handoffId,
|
|
41611
|
+
ts: Date.now()
|
|
41612
|
+
})
|
|
41613
|
+
);
|
|
41614
|
+
}
|
|
41615
|
+
function writeTargetTabStatus(status, context) {
|
|
41616
|
+
safeLocalStorageSetItem(
|
|
41617
|
+
TARGET_TAB_STATUS_KEY,
|
|
41618
|
+
JSON.stringify({
|
|
41619
|
+
sourceTabId: context.sourceTabId,
|
|
41620
|
+
targetTabId: context.targetTabId,
|
|
41621
|
+
handoffId: context.handoffId,
|
|
41622
|
+
status,
|
|
41623
|
+
ts: Date.now()
|
|
41624
|
+
})
|
|
41625
|
+
);
|
|
41626
|
+
}
|
|
41627
|
+
function sendReconnectSignal(context) {
|
|
41628
|
+
if (closeSignalSent) {
|
|
41629
|
+
return;
|
|
41630
|
+
}
|
|
41631
|
+
closeSignalSent = true;
|
|
41632
|
+
console.warn("[MQTT_HANDOFF] 目标页关闭,通知来源页恢复 MQTT", {
|
|
41633
|
+
sourceTabId: context.sourceTabId,
|
|
41634
|
+
targetTabId: context.targetTabId,
|
|
41635
|
+
handoffId: context.handoffId
|
|
41636
|
+
});
|
|
41637
|
+
safeLocalStorageSetItem(
|
|
41638
|
+
RECONNECT_SIGNAL_KEY,
|
|
41639
|
+
JSON.stringify({
|
|
41640
|
+
...context,
|
|
41641
|
+
ts: Date.now()
|
|
41642
|
+
})
|
|
41643
|
+
);
|
|
41644
|
+
}
|
|
41645
|
+
function stopTargetHeartbeat() {
|
|
41646
|
+
if (heartbeatTimer) {
|
|
41647
|
+
clearInterval(heartbeatTimer);
|
|
41648
|
+
heartbeatTimer = null;
|
|
41649
|
+
}
|
|
41650
|
+
}
|
|
41651
|
+
function startTargetHeartbeat() {
|
|
41652
|
+
const context = getMeetingMqttHandoffContext();
|
|
41653
|
+
if (!context) {
|
|
41654
|
+
return;
|
|
41655
|
+
}
|
|
41656
|
+
const currentTabId = getCurrentMeetingTabId();
|
|
41657
|
+
if (currentTabId !== context.targetTabId) {
|
|
41658
|
+
return;
|
|
41659
|
+
}
|
|
41660
|
+
closeSignalSent = false;
|
|
41661
|
+
console.info("[MQTT_HANDOFF] 目标页心跳已启动", {
|
|
41662
|
+
sourceTabId: context.sourceTabId,
|
|
41663
|
+
targetTabId: context.targetTabId,
|
|
41664
|
+
handoffId: context.handoffId
|
|
41665
|
+
});
|
|
41666
|
+
writeTargetTabStatus(TARGET_TAB_STATUS_OPEN, context);
|
|
41667
|
+
writeTargetHeartbeat(context);
|
|
41668
|
+
stopTargetHeartbeat();
|
|
41669
|
+
heartbeatTimer = setInterval(() => {
|
|
41670
|
+
writeTargetHeartbeat(context);
|
|
41671
|
+
}, TARGET_HEARTBEAT_INTERVAL_MS);
|
|
41672
|
+
}
|
|
41673
|
+
function notifySourceTabForReconnect() {
|
|
41674
|
+
const context = getMeetingMqttHandoffContext();
|
|
41675
|
+
if (!context) {
|
|
41676
|
+
return;
|
|
41677
|
+
}
|
|
41678
|
+
const currentTabId = getCurrentMeetingTabId();
|
|
41679
|
+
if (currentTabId !== context.targetTabId) {
|
|
41680
|
+
return;
|
|
41681
|
+
}
|
|
41682
|
+
sendReconnectSignal(context);
|
|
41683
|
+
}
|
|
41684
|
+
function shouldWarnBeforeUnload() {
|
|
41685
|
+
const context = getMeetingMqttHandoffContext();
|
|
41686
|
+
if (!context) {
|
|
41687
|
+
return false;
|
|
41688
|
+
}
|
|
41689
|
+
const currentTabId = getCurrentMeetingTabId();
|
|
41690
|
+
return currentTabId === context.targetTabId;
|
|
41691
|
+
}
|
|
41692
|
+
function buildMeetingLeavePayload() {
|
|
41693
|
+
try {
|
|
41694
|
+
const meetingInfo = JSON.parse(sessionStorage.getItem("JS_MEETING_INFO") || "{}");
|
|
41695
|
+
const meetingId = String((meetingInfo == null ? void 0 : meetingInfo.meetingId) || "").trim();
|
|
41696
|
+
const memberId = String(getUserId() || "").trim();
|
|
41697
|
+
if (!meetingId || !memberId) {
|
|
41698
|
+
return null;
|
|
41697
41699
|
}
|
|
41698
|
-
|
|
41699
|
-
|
|
41700
|
+
return { meetingId, memberId, force: true };
|
|
41701
|
+
} catch {
|
|
41702
|
+
return null;
|
|
41703
|
+
}
|
|
41704
|
+
}
|
|
41705
|
+
function sendMeetingLeaveOnClose() {
|
|
41706
|
+
if (leaveRequestSent) {
|
|
41707
|
+
return;
|
|
41708
|
+
}
|
|
41709
|
+
const payload = buildMeetingLeavePayload();
|
|
41710
|
+
if (!payload) {
|
|
41711
|
+
return;
|
|
41712
|
+
}
|
|
41713
|
+
leaveRequestSent = true;
|
|
41714
|
+
const body = JSON.stringify(payload);
|
|
41715
|
+
const token = getSdkToken();
|
|
41716
|
+
const authHeader = token ? { Authorization: `Bearer ${token}` } : {};
|
|
41717
|
+
const requestUrl = "/meeting/api/v2/meeting/leave";
|
|
41718
|
+
try {
|
|
41719
|
+
fetch(requestUrl, {
|
|
41720
|
+
method: "POST",
|
|
41721
|
+
headers: {
|
|
41722
|
+
"Content-Type": "application/json",
|
|
41723
|
+
Accept: "application/json, text/plain, */*",
|
|
41724
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
41725
|
+
...authHeader
|
|
41726
|
+
},
|
|
41727
|
+
body,
|
|
41728
|
+
keepalive: true,
|
|
41729
|
+
credentials: "same-origin"
|
|
41730
|
+
}).catch(() => {
|
|
41731
|
+
});
|
|
41732
|
+
console.warn("[MQTT_HANDOFF] 页面关闭触发离会请求(keepalive)", payload);
|
|
41733
|
+
} catch {
|
|
41734
|
+
}
|
|
41735
|
+
try {
|
|
41736
|
+
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
41737
|
+
const beaconData = new Blob([body], { type: "application/json" });
|
|
41738
|
+
const sent = navigator.sendBeacon(requestUrl, beaconData);
|
|
41739
|
+
console.warn("[MQTT_HANDOFF] 页面关闭触发离会请求(beacon)", {
|
|
41740
|
+
...payload,
|
|
41741
|
+
sent
|
|
41742
|
+
});
|
|
41700
41743
|
}
|
|
41701
|
-
|
|
41702
|
-
|
|
41744
|
+
} catch {
|
|
41745
|
+
}
|
|
41746
|
+
}
|
|
41747
|
+
function shouldReconnectForSignal(signal) {
|
|
41748
|
+
const currentTabId = getCurrentMeetingTabId();
|
|
41749
|
+
if (!signal.sourceTabId || signal.sourceTabId !== currentTabId) {
|
|
41750
|
+
return false;
|
|
41751
|
+
}
|
|
41752
|
+
const latestHeartbeat = readTargetHeartbeat();
|
|
41753
|
+
if (!latestHeartbeat) {
|
|
41754
|
+
return true;
|
|
41755
|
+
}
|
|
41756
|
+
if (latestHeartbeat.targetTabId !== signal.targetTabId) {
|
|
41757
|
+
return true;
|
|
41758
|
+
}
|
|
41759
|
+
if (latestHeartbeat.handoffId !== signal.handoffId) {
|
|
41760
|
+
return true;
|
|
41761
|
+
}
|
|
41762
|
+
return latestHeartbeat.ts <= signal.ts;
|
|
41763
|
+
}
|
|
41764
|
+
function createMeetingMqttHandoffId() {
|
|
41765
|
+
return `mqtt_handoff_${Date.now()}_${createRandomId()}`;
|
|
41766
|
+
}
|
|
41767
|
+
function setMeetingMqttHandoffForWindow(targetWindow, context) {
|
|
41768
|
+
targetWindow.sessionStorage.setItem(
|
|
41769
|
+
HANDOFF_SOURCE_TAB_ID_KEY,
|
|
41770
|
+
context.sourceTabId
|
|
41771
|
+
);
|
|
41772
|
+
targetWindow.sessionStorage.setItem(
|
|
41773
|
+
HANDOFF_TARGET_TAB_ID_KEY,
|
|
41774
|
+
context.targetTabId
|
|
41775
|
+
);
|
|
41776
|
+
targetWindow.sessionStorage.setItem(HANDOFF_ID_KEY, context.handoffId);
|
|
41777
|
+
}
|
|
41778
|
+
function installMeetingMqttHandoffBridge(onReconnectRequested) {
|
|
41779
|
+
if (installed || typeof window === "undefined") {
|
|
41780
|
+
return;
|
|
41781
|
+
}
|
|
41782
|
+
installed = true;
|
|
41783
|
+
startTargetHeartbeat();
|
|
41784
|
+
window.addEventListener("storage", (event) => {
|
|
41785
|
+
if (event.key !== RECONNECT_SIGNAL_KEY || !event.newValue) {
|
|
41786
|
+
return;
|
|
41703
41787
|
}
|
|
41704
|
-
|
|
41705
|
-
|
|
41788
|
+
let signal = null;
|
|
41789
|
+
try {
|
|
41790
|
+
signal = JSON.parse(event.newValue);
|
|
41791
|
+
} catch {
|
|
41792
|
+
return;
|
|
41706
41793
|
}
|
|
41707
|
-
|
|
41708
|
-
|
|
41709
|
-
detailSections.value.map((section) => [section.key, false])
|
|
41710
|
-
);
|
|
41794
|
+
if (!(signal == null ? void 0 : signal.sourceTabId) || !signal.targetTabId || !signal.handoffId) {
|
|
41795
|
+
return;
|
|
41711
41796
|
}
|
|
41712
|
-
|
|
41713
|
-
|
|
41714
|
-
|
|
41715
|
-
);
|
|
41797
|
+
const signalTs = Number(signal.ts);
|
|
41798
|
+
if (!signalTs || signalTs <= lastHandledReconnectSignalTs) {
|
|
41799
|
+
return;
|
|
41716
41800
|
}
|
|
41717
|
-
|
|
41718
|
-
|
|
41719
|
-
|
|
41801
|
+
lastHandledReconnectSignalTs = signalTs;
|
|
41802
|
+
console.warn("[MQTT_HANDOFF] 收到 MQTT 恢复信号,等待确认目标页是否真的关闭", {
|
|
41803
|
+
sourceTabId: signal.sourceTabId,
|
|
41804
|
+
targetTabId: signal.targetTabId,
|
|
41805
|
+
handoffId: signal.handoffId,
|
|
41806
|
+
signalTs
|
|
41807
|
+
});
|
|
41808
|
+
window.setTimeout(() => {
|
|
41809
|
+
if (!shouldReconnectForSignal(signal)) {
|
|
41810
|
+
console.info("[MQTT_HANDOFF] 忽略 MQTT 恢复,本次更像是目标页刷新/重载", {
|
|
41811
|
+
sourceTabId: signal.sourceTabId,
|
|
41812
|
+
targetTabId: signal.targetTabId,
|
|
41813
|
+
handoffId: signal.handoffId
|
|
41814
|
+
});
|
|
41720
41815
|
return;
|
|
41721
41816
|
}
|
|
41722
|
-
|
|
41723
|
-
|
|
41724
|
-
|
|
41725
|
-
|
|
41726
|
-
|
|
41727
|
-
|
|
41728
|
-
|
|
41729
|
-
|
|
41730
|
-
|
|
41731
|
-
|
|
41732
|
-
|
|
41733
|
-
collapsedSections.value = nextState;
|
|
41734
|
-
}
|
|
41735
|
-
const activeBars = computed(() => {
|
|
41736
|
-
if (networkQualityState.isOffline || networkQualityState.isStale) {
|
|
41737
|
-
return 0;
|
|
41738
|
-
}
|
|
41739
|
-
return getNetworkSignalBars(networkQualityState.level);
|
|
41740
|
-
});
|
|
41741
|
-
const allSectionsExpanded = computed(() => {
|
|
41742
|
-
if (!detailSections.value.length) return true;
|
|
41743
|
-
return detailSections.value.every((section) => !isSectionCollapsed(section.key));
|
|
41744
|
-
});
|
|
41745
|
-
const reasonText = computed(() => {
|
|
41746
|
-
if (!networkQualityState.reasons.length) return "";
|
|
41747
|
-
return `可能原因:${networkQualityState.reasons.join("、")}`;
|
|
41748
|
-
});
|
|
41749
|
-
const qualityItems = computed(() => [
|
|
41750
|
-
{ label: "总体质量", value: networkQualityState.label },
|
|
41751
|
-
{ label: "上行质量", value: networkQualityState.uplink },
|
|
41752
|
-
{ label: "下行质量", value: networkQualityState.downlink },
|
|
41753
|
-
{ label: "MOS", value: formatMos(networkQualityState.mos) },
|
|
41754
|
-
{ label: "最近更新", value: formatUpdatedAt(networkQualityState.updatedAt) },
|
|
41755
|
-
{
|
|
41756
|
-
label: "数据状态",
|
|
41757
|
-
value: networkQualityState.isStale ? "已过期" : "实时"
|
|
41758
|
-
}
|
|
41759
|
-
]);
|
|
41760
|
-
const overviewItems = computed(() => {
|
|
41761
|
-
const stats = networkQualityState.stats;
|
|
41762
|
-
return [
|
|
41763
|
-
{ label: "上行 RTT", value: formatValue(stats == null ? void 0 : stats.rtt_up, " ms") },
|
|
41764
|
-
{ label: "下行 RTT", value: formatValue(stats == null ? void 0 : stats.rtt_down, " ms") },
|
|
41765
|
-
{ label: "上行码率", value: formatValue(stats == null ? void 0 : stats.bitrate_up, " kb/s") },
|
|
41766
|
-
{ label: "下行码率", value: formatValue(stats == null ? void 0 : stats.bitrate_down, " kb/s") },
|
|
41767
|
-
{ label: "上行丢包率", value: formatLossRate(stats == null ? void 0 : stats.lossrate_up) },
|
|
41768
|
-
{ label: "下行丢包率", value: formatLossRate(stats == null ? void 0 : stats.lossrate_down) },
|
|
41769
|
-
{
|
|
41770
|
-
label: "可用上行带宽",
|
|
41771
|
-
value: formatValue(stats == null ? void 0 : stats.available_outgoing_bitrate, " kb/s")
|
|
41772
|
-
},
|
|
41773
|
-
{
|
|
41774
|
-
label: "可用下行带宽",
|
|
41775
|
-
value: formatValue(stats == null ? void 0 : stats.available_incoming_bitrate, " kb/s")
|
|
41776
|
-
}
|
|
41777
|
-
];
|
|
41778
|
-
});
|
|
41779
|
-
function createAudioSendCard(item, index) {
|
|
41780
|
-
const stats = (item == null ? void 0 : item.stats) || {};
|
|
41781
|
-
return {
|
|
41782
|
-
title: `本地音频 ${index + 1}`,
|
|
41783
|
-
subtitle: (item == null ? void 0 : item.desc) || (stats == null ? void 0 : stats.streamId) || "发送轨道",
|
|
41784
|
-
items: [
|
|
41785
|
-
{ label: "实时码率", value: formatMetricNumber(item == null ? void 0 : item.bitrate, " kb/s") },
|
|
41786
|
-
{ label: "音频电平", value: formatMetricNumber(item == null ? void 0 : item.db, " dBFS", 1) },
|
|
41787
|
-
{ label: "已发包数", value: formatMetricNumber(stats == null ? void 0 : stats.packetsSent) },
|
|
41788
|
-
{ label: "已发字节", value: formatBytes(stats == null ? void 0 : stats.bytesSent) },
|
|
41789
|
-
{ label: "远端丢包", value: formatMetricNumber(stats == null ? void 0 : stats.packetsLost) },
|
|
41790
|
-
{ label: "往返时延", value: formatMetricNumber(stats == null ? void 0 : stats.roundTripTime, " ms") },
|
|
41791
|
-
{ label: "远端抖动", value: formatMetricNumber(stats == null ? void 0 : stats.jitter, " ms") },
|
|
41792
|
-
{ label: "采样时间", value: formatUpdatedAt(stats == null ? void 0 : stats.timestamp) }
|
|
41793
|
-
]
|
|
41794
|
-
};
|
|
41795
|
-
}
|
|
41796
|
-
function createVideoSendCard(item, index) {
|
|
41797
|
-
const stats = (item == null ? void 0 : item.stats) || {};
|
|
41798
|
-
return {
|
|
41799
|
-
title: `本地视频 ${index + 1}`,
|
|
41800
|
-
subtitle: (item == null ? void 0 : item.desc) || (stats == null ? void 0 : stats.rid) || (stats == null ? void 0 : stats.streamId) || "发送轨道",
|
|
41801
|
-
items: [
|
|
41802
|
-
{ label: "实时码率", value: formatMetricNumber(item == null ? void 0 : item.bitrate, " kb/s") },
|
|
41803
|
-
{ label: "分辨率", value: formatDimension((item == null ? void 0 : item.width) || (stats == null ? void 0 : stats.frameWidth), (item == null ? void 0 : item.height) || (stats == null ? void 0 : stats.frameHeight)) },
|
|
41804
|
-
{ label: "帧率", value: formatMetricNumber((item == null ? void 0 : item.fps) || (stats == null ? void 0 : stats.framesPerSecond), " fps") },
|
|
41805
|
-
{ label: "已发帧数", value: formatMetricNumber(stats == null ? void 0 : stats.framesSent) },
|
|
41806
|
-
{ label: "目标码率", value: formatMetricNumber(stats == null ? void 0 : stats.targetBitrate, " bps") },
|
|
41807
|
-
{ label: "FIR/PLI/NACK", value: `${formatMetricNumber(stats == null ? void 0 : stats.firCount)}/${formatMetricNumber(stats == null ? void 0 : stats.pliCount)}/${formatMetricNumber(stats == null ? void 0 : stats.nackCount)}` },
|
|
41808
|
-
{ label: "重传包数", value: formatMetricNumber(stats == null ? void 0 : stats.retransmittedPacketsSent) },
|
|
41809
|
-
{ label: "受限原因", value: formatReason(stats == null ? void 0 : stats.qualityLimitationReason) },
|
|
41810
|
-
{ label: "分辨率变化", value: formatMetricNumber(stats == null ? void 0 : stats.qualityLimitationResolutionChanges) },
|
|
41811
|
-
{ label: "往返时延", value: formatMetricNumber(stats == null ? void 0 : stats.roundTripTime, " ms") },
|
|
41812
|
-
{ label: "采样时间", value: formatUpdatedAt(stats == null ? void 0 : stats.timestamp) }
|
|
41813
|
-
]
|
|
41814
|
-
};
|
|
41817
|
+
console.warn("[MQTT_HANDOFF] 确认目标页已关闭,准备恢复来源页 MQTT", {
|
|
41818
|
+
sourceTabId: signal.sourceTabId,
|
|
41819
|
+
targetTabId: signal.targetTabId,
|
|
41820
|
+
handoffId: signal.handoffId
|
|
41821
|
+
});
|
|
41822
|
+
void onReconnectRequested();
|
|
41823
|
+
}, RECONNECT_CHECK_DELAY_MS);
|
|
41824
|
+
});
|
|
41825
|
+
window.addEventListener("beforeunload", (event) => {
|
|
41826
|
+
if (!shouldWarnBeforeUnload()) {
|
|
41827
|
+
return;
|
|
41815
41828
|
}
|
|
41816
|
-
|
|
41817
|
-
|
|
41818
|
-
|
|
41819
|
-
|
|
41820
|
-
|
|
41821
|
-
|
|
41822
|
-
|
|
41823
|
-
|
|
41824
|
-
|
|
41825
|
-
{ label: "已收字节", value: formatBytes(stats == null ? void 0 : stats.bytesReceived) },
|
|
41826
|
-
{ label: "丢包数", value: formatMetricNumber(stats == null ? void 0 : stats.packetsLost) },
|
|
41827
|
-
{ label: "网络抖动", value: formatMetricNumber(stats == null ? void 0 : stats.jitter, " ms") },
|
|
41828
|
-
{ label: "缓冲均值", value: formatMetricNumber(stats == null ? void 0 : stats.jitterBufferAvgDelay, " ms") },
|
|
41829
|
-
{ label: "隐藏事件", value: formatMetricNumber(stats == null ? void 0 : stats.concealmentEvents) },
|
|
41830
|
-
{ label: "静音隐藏", value: formatMetricNumber(stats == null ? void 0 : stats.silentConcealmentEvents) },
|
|
41831
|
-
{ label: "采样时间", value: formatUpdatedAt(stats == null ? void 0 : stats.timestamp) }
|
|
41832
|
-
]
|
|
41833
|
-
};
|
|
41829
|
+
event.preventDefault();
|
|
41830
|
+
event.returnValue = "";
|
|
41831
|
+
});
|
|
41832
|
+
window.addEventListener("pagehide", (event) => {
|
|
41833
|
+
const context = getMeetingMqttHandoffContext();
|
|
41834
|
+
const shouldHandleTargetTabClose = !!context && getCurrentMeetingTabId() === context.targetTabId;
|
|
41835
|
+
if (shouldHandleTargetTabClose && context) {
|
|
41836
|
+
writeTargetTabStatus(TARGET_TAB_STATUS_CLOSED, context);
|
|
41837
|
+
sendMeetingLeaveOnClose();
|
|
41834
41838
|
}
|
|
41835
|
-
|
|
41836
|
-
|
|
41837
|
-
|
|
41838
|
-
return {
|
|
41839
|
-
title: `远端视频 ${index + 1}`,
|
|
41840
|
-
subtitle: memberName || (item == null ? void 0 : item.uid) || (item == null ? void 0 : item.desc) || (stats == null ? void 0 : stats.mimeType) || "接收轨道",
|
|
41841
|
-
items: [
|
|
41842
|
-
{ label: "实时码率", value: formatMetricNumber(item == null ? void 0 : item.bitrate, " kb/s") },
|
|
41843
|
-
{ label: "分辨率", value: formatDimension((item == null ? void 0 : item.width) || (stats == null ? void 0 : stats.frameWidth), (item == null ? void 0 : item.height) || (stats == null ? void 0 : stats.frameHeight)) },
|
|
41844
|
-
{ label: "帧率", value: formatMetricNumber((item == null ? void 0 : item.fps) || (stats == null ? void 0 : stats.framesPerSecond), " fps") },
|
|
41845
|
-
{ label: "已收包数", value: formatMetricNumber(stats == null ? void 0 : stats.packetsReceived) },
|
|
41846
|
-
{ label: "已收字节", value: formatBytes(stats == null ? void 0 : stats.bytesReceived) },
|
|
41847
|
-
{ label: "丢包数", value: formatMetricNumber(stats == null ? void 0 : stats.packetsLost) },
|
|
41848
|
-
{ label: "接收/解码帧", value: `${formatMetricNumber(stats == null ? void 0 : stats.framesReceived)}/${formatMetricNumber(stats == null ? void 0 : stats.framesDecoded)}` },
|
|
41849
|
-
{ label: "丢帧数", value: formatMetricNumber(stats == null ? void 0 : stats.framesDropped) },
|
|
41850
|
-
{ label: "FIR/PLI/NACK", value: `${formatMetricNumber(stats == null ? void 0 : stats.firCount)}/${formatMetricNumber(stats == null ? void 0 : stats.pliCount)}/${formatMetricNumber(stats == null ? void 0 : stats.nackCount)}` },
|
|
41851
|
-
{ label: "缓冲均值", value: formatMetricNumber(stats == null ? void 0 : stats.jitterBufferAvgDelay, " ms") },
|
|
41852
|
-
{ label: "解码器", value: formatReason(stats == null ? void 0 : stats.decoderImplementation) },
|
|
41853
|
-
{ label: "MIME", value: formatReason(stats == null ? void 0 : stats.mimeType) },
|
|
41854
|
-
{ label: "采样时间", value: formatUpdatedAt(stats == null ? void 0 : stats.timestamp) }
|
|
41855
|
-
]
|
|
41856
|
-
};
|
|
41839
|
+
stopTargetHeartbeat();
|
|
41840
|
+
if (event.persisted) {
|
|
41841
|
+
return;
|
|
41857
41842
|
}
|
|
41858
|
-
|
|
41859
|
-
|
|
41860
|
-
|
|
41861
|
-
|
|
41862
|
-
|
|
41863
|
-
|
|
41864
|
-
|
|
41865
|
-
|
|
41866
|
-
|
|
41867
|
-
|
|
41868
|
-
|
|
41869
|
-
|
|
41870
|
-
|
|
41871
|
-
|
|
41872
|
-
|
|
41873
|
-
|
|
41874
|
-
|
|
41875
|
-
|
|
41876
|
-
{
|
|
41877
|
-
key: "remote-video",
|
|
41878
|
-
title: "远端视频接收",
|
|
41879
|
-
cards: ((details == null ? void 0 : details.remote_videos) || []).map(createVideoRecvCard)
|
|
41880
|
-
}
|
|
41881
|
-
];
|
|
41843
|
+
notifySourceTabForReconnect();
|
|
41844
|
+
});
|
|
41845
|
+
}
|
|
41846
|
+
const _hoisted_1$n = { class: "js-dialog-header-title" };
|
|
41847
|
+
const _hoisted_2$l = { class: "js-dialog-header-actions" };
|
|
41848
|
+
const MQTT_CLIENT_INSTANCE_STORAGE_KEY = "JS_MQTT_CLIENT_INSTANCE_ID";
|
|
41849
|
+
const _sfc_main$p = /* @__PURE__ */ defineComponent({
|
|
41850
|
+
__name: "meeting-header",
|
|
41851
|
+
props: {
|
|
41852
|
+
theme: {},
|
|
41853
|
+
isMaximized: { type: Boolean }
|
|
41854
|
+
},
|
|
41855
|
+
emits: ["onChange"],
|
|
41856
|
+
setup(__props, { emit: __emit }) {
|
|
41857
|
+
const props = __props;
|
|
41858
|
+
const { meState: meState2 } = storeToRefs(useMeetingStore());
|
|
41859
|
+
const meetingInfo = computed(() => {
|
|
41860
|
+
return JSON.parse(sessionStorage.getItem("JS_MEETING_INFO") || "{}");
|
|
41882
41861
|
});
|
|
41883
|
-
|
|
41884
|
-
|
|
41885
|
-
|
|
41886
|
-
|
|
41887
|
-
|
|
41888
|
-
|
|
41862
|
+
const emit = __emit;
|
|
41863
|
+
const closeRef = ref(null);
|
|
41864
|
+
const showCloseConfirm = ref(false);
|
|
41865
|
+
const SESSION_STORAGE_KEYS_TO_CLONE = [
|
|
41866
|
+
"JS_MEETING_INFO",
|
|
41867
|
+
"JS_SDK_TOKEN",
|
|
41868
|
+
"JS_SDK_TOKEN_EXPIRY",
|
|
41869
|
+
"JS_USER_ID",
|
|
41870
|
+
"JS_USER_NAME",
|
|
41871
|
+
"JS_SELF_MEDIA_PREFS",
|
|
41872
|
+
"JS_SERVER_TIMESTAMP",
|
|
41873
|
+
"joinTime",
|
|
41874
|
+
"JS_INCOMING_CALL_INFO",
|
|
41875
|
+
"isSosActive"
|
|
41876
|
+
];
|
|
41877
|
+
const themeClass = computed(() => `theme-${props.theme || "dark"}`);
|
|
41878
|
+
const hasOpenedNewTab = ref(false);
|
|
41879
|
+
const getNewTabFlagFromLocation = () => {
|
|
41880
|
+
if (typeof window === "undefined") return false;
|
|
41881
|
+
const searchParams = new URLSearchParams(window.location.search || "");
|
|
41882
|
+
if (searchParams.get("newTab") === "true") return true;
|
|
41883
|
+
const hash = window.location.hash || "";
|
|
41884
|
+
const hashQueryIndex = hash.indexOf("?");
|
|
41885
|
+
if (hashQueryIndex === -1) return false;
|
|
41886
|
+
const hashQuery = hash.slice(hashQueryIndex + 1);
|
|
41887
|
+
const hashParams = new URLSearchParams(hashQuery);
|
|
41888
|
+
return hashParams.get("newTab") === "true";
|
|
41889
|
+
};
|
|
41890
|
+
const showOpenNewTabButton = computed(() => {
|
|
41891
|
+
if (hasOpenedNewTab.value) return false;
|
|
41892
|
+
return !getNewTabFlagFromLocation();
|
|
41893
|
+
});
|
|
41894
|
+
const createMqttInstanceId = () => {
|
|
41895
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
41896
|
+
return crypto.randomUUID().replace(/-/g, "").slice(0, 12);
|
|
41897
|
+
}
|
|
41898
|
+
return Math.random().toString(36).slice(2, 14);
|
|
41899
|
+
};
|
|
41900
|
+
const copySessionToNewTab = (targetWindow) => {
|
|
41901
|
+
SESSION_STORAGE_KEYS_TO_CLONE.forEach((key) => {
|
|
41902
|
+
const value = sessionStorage.getItem(key);
|
|
41903
|
+
if (value === null) {
|
|
41904
|
+
targetWindow.sessionStorage.removeItem(key);
|
|
41905
|
+
return;
|
|
41889
41906
|
}
|
|
41907
|
+
targetWindow.sessionStorage.setItem(key, value);
|
|
41908
|
+
});
|
|
41909
|
+
};
|
|
41910
|
+
const handleOpenNewTab = async () => {
|
|
41911
|
+
var _a25;
|
|
41912
|
+
hasOpenedNewTab.value = true;
|
|
41913
|
+
const currentMeetingId = (_a25 = meetingInfo.value) == null ? void 0 : _a25.meetingId;
|
|
41914
|
+
const newTab = window.open("", "_blank");
|
|
41915
|
+
if (!newTab) {
|
|
41916
|
+
hasOpenedNewTab.value = false;
|
|
41917
|
+
showNotification("打开新标签页失败,请允许弹窗权限。", "warning", 5e3);
|
|
41890
41918
|
return;
|
|
41891
41919
|
}
|
|
41892
|
-
|
|
41893
|
-
|
|
41894
|
-
|
|
41895
|
-
|
|
41896
|
-
|
|
41897
|
-
|
|
41898
|
-
|
|
41899
|
-
|
|
41900
|
-
|
|
41901
|
-
|
|
41902
|
-
|
|
41903
|
-
|
|
41904
|
-
|
|
41920
|
+
try {
|
|
41921
|
+
const sourceTabId = getCurrentMeetingTabId();
|
|
41922
|
+
const nextTabId = createMeetingTabId();
|
|
41923
|
+
const handoffId = createMeetingMqttHandoffId();
|
|
41924
|
+
copySessionToNewTab(newTab);
|
|
41925
|
+
setMeetingMqttHandoffForWindow(newTab, {
|
|
41926
|
+
sourceTabId,
|
|
41927
|
+
targetTabId: nextTabId,
|
|
41928
|
+
handoffId
|
|
41929
|
+
});
|
|
41930
|
+
newTab.sessionStorage.setItem(
|
|
41931
|
+
MQTT_CLIENT_INSTANCE_STORAGE_KEY,
|
|
41932
|
+
createMqttInstanceId()
|
|
41933
|
+
);
|
|
41934
|
+
setMeetingTabIdForWindow(newTab, nextTabId);
|
|
41935
|
+
if (currentMeetingId) {
|
|
41936
|
+
claimActiveMeetingOwnership(currentMeetingId, nextTabId);
|
|
41937
|
+
}
|
|
41938
|
+
let url = window.location.href;
|
|
41939
|
+
if (url.includes("home") || url.includes("surveillance")) {
|
|
41940
|
+
url = url.replace("home", "command");
|
|
41941
|
+
url = url.replace("surveillance", "command");
|
|
41942
|
+
}
|
|
41943
|
+
newTab.location.replace(url + "?newTab=true");
|
|
41944
|
+
resetMeetingMQTT();
|
|
41945
|
+
await logoutRoomAction();
|
|
41946
|
+
closeMeeting();
|
|
41947
|
+
} catch (error) {
|
|
41948
|
+
hasOpenedNewTab.value = false;
|
|
41949
|
+
console.error("Failed to open meeting in a new tab:", error);
|
|
41950
|
+
showNotification("New tab initialized with a fallback page open.", "warning", 5e3);
|
|
41905
41951
|
}
|
|
41906
|
-
}
|
|
41907
|
-
|
|
41908
|
-
|
|
41909
|
-
|
|
41910
|
-
|
|
41911
|
-
|
|
41912
|
-
|
|
41913
|
-
|
|
41914
|
-
)
|
|
41915
|
-
|
|
41916
|
-
|
|
41917
|
-
}
|
|
41952
|
+
};
|
|
41953
|
+
const handleCloseClick = async () => {
|
|
41954
|
+
showCloseConfirm.value = !showCloseConfirm.value;
|
|
41955
|
+
};
|
|
41956
|
+
const handleEndMeeting = () => {
|
|
41957
|
+
showCloseConfirm.value = false;
|
|
41958
|
+
emit("onChange", RoomModalSelectType.endClick);
|
|
41959
|
+
};
|
|
41960
|
+
const handleLeaveMeeting = () => {
|
|
41961
|
+
showCloseConfirm.value = false;
|
|
41962
|
+
emit("onChange", RoomModalSelectType.leaveClick);
|
|
41963
|
+
};
|
|
41918
41964
|
return (_ctx, _cache) => {
|
|
41919
41965
|
return openBlock(), createElementBlock("div", {
|
|
41920
|
-
class: normalizeClass(["
|
|
41966
|
+
class: normalizeClass(["js-dialog-header", themeClass.value])
|
|
41921
41967
|
}, [
|
|
41922
|
-
createElementVNode("
|
|
41923
|
-
|
|
41924
|
-
|
|
41925
|
-
|
|
41926
|
-
|
|
41927
|
-
|
|
41928
|
-
|
|
41929
|
-
|
|
41930
|
-
|
|
41931
|
-
|
|
41932
|
-
|
|
41933
|
-
|
|
41934
|
-
|
|
41935
|
-
|
|
41936
|
-
(
|
|
41937
|
-
|
|
41938
|
-
|
|
41939
|
-
|
|
41940
|
-
|
|
41941
|
-
})
|
|
41968
|
+
createElementVNode("div", _hoisted_1$n, toDisplayString(meetingInfo.value.meetingName), 1),
|
|
41969
|
+
createElementVNode("div", _hoisted_2$l, [
|
|
41970
|
+
showOpenNewTabButton.value ? (openBlock(), createElementBlock("div", {
|
|
41971
|
+
key: 0,
|
|
41972
|
+
title: "新打开标签页",
|
|
41973
|
+
onClick: handleOpenNewTab
|
|
41974
|
+
}, [
|
|
41975
|
+
createVNode(SvgIcon, {
|
|
41976
|
+
name: "OpenNewTabIcon",
|
|
41977
|
+
size: 16
|
|
41978
|
+
})
|
|
41979
|
+
])) : createCommentVNode("", true),
|
|
41980
|
+
createElementVNode("div", {
|
|
41981
|
+
title: "最小化",
|
|
41982
|
+
onClick: _cache[0] || (_cache[0] = ($event) => emit("onChange", unref(RoomModalSelectType).minimizeClick))
|
|
41983
|
+
}, [
|
|
41984
|
+
createVNode(SvgIcon, {
|
|
41985
|
+
name: "MinimizeIcon",
|
|
41986
|
+
size: 16
|
|
41987
|
+
})
|
|
41942
41988
|
]),
|
|
41943
|
-
|
|
41944
|
-
|
|
41989
|
+
createElementVNode("div", {
|
|
41990
|
+
ref_key: "closeRef",
|
|
41991
|
+
ref: closeRef,
|
|
41992
|
+
title: "关闭",
|
|
41993
|
+
onClick: handleCloseClick
|
|
41994
|
+
}, [
|
|
41995
|
+
createVNode(SvgIcon, {
|
|
41996
|
+
name: "CloseIcon",
|
|
41997
|
+
size: 17
|
|
41998
|
+
})
|
|
41999
|
+
], 512)
|
|
42000
|
+
]),
|
|
41945
42001
|
createVNode(SmartPopup, {
|
|
41946
|
-
visible:
|
|
41947
|
-
"onUpdate:visible": _cache[
|
|
41948
|
-
trigger:
|
|
41949
|
-
gap:
|
|
42002
|
+
visible: showCloseConfirm.value,
|
|
42003
|
+
"onUpdate:visible": _cache[2] || (_cache[2] = ($event) => showCloseConfirm.value = $event),
|
|
42004
|
+
trigger: closeRef.value,
|
|
42005
|
+
gap: 10
|
|
41950
42006
|
}, {
|
|
41951
42007
|
default: withCtx(() => [
|
|
41952
|
-
createElementVNode("div",
|
|
41953
|
-
|
|
41954
|
-
|
|
41955
|
-
|
|
41956
|
-
createElementVNode("div", _hoisted_5$f, [
|
|
41957
|
-
_cache[4] || (_cache[4] = createElementVNode("div", { class: "popup-title" }, "网络详情", -1)),
|
|
41958
|
-
createElementVNode("div", _hoisted_6$e, [
|
|
41959
|
-
createElementVNode("button", {
|
|
41960
|
-
type: "button",
|
|
41961
|
-
class: "advanced-toggle",
|
|
41962
|
-
onClick: _cache[1] || (_cache[1] = withModifiers(($event) => advancedOpen.value = !advancedOpen.value, ["stop"]))
|
|
41963
|
-
}, toDisplayString(advancedOpen.value ? "关闭高级模式" : "打开高级模式"), 1),
|
|
41964
|
-
createElementVNode("div", {
|
|
41965
|
-
class: normalizeClass(["popup-status", `is-${unref(networkQualityState).level}`])
|
|
41966
|
-
}, toDisplayString(unref(networkQualityState).label), 3)
|
|
41967
|
-
])
|
|
41968
|
-
]),
|
|
41969
|
-
createElementVNode("div", _hoisted_7$d, toDisplayString(unref(networkQualityState).summary), 1),
|
|
41970
|
-
reasonText.value ? (openBlock(), createElementBlock("div", _hoisted_8$c, toDisplayString(reasonText.value), 1)) : createCommentVNode("", true),
|
|
41971
|
-
createElementVNode("div", _hoisted_9$a, [
|
|
41972
|
-
_cache[5] || (_cache[5] = createElementVNode("div", { class: "popup-section-title" }, "质量概览", -1)),
|
|
41973
|
-
createElementVNode("div", _hoisted_10$a, [
|
|
41974
|
-
(openBlock(true), createElementBlock(Fragment, null, renderList(qualityItems.value, (item) => {
|
|
41975
|
-
return openBlock(), createElementBlock("div", {
|
|
41976
|
-
key: item.label,
|
|
41977
|
-
class: "popup-item"
|
|
41978
|
-
}, [
|
|
41979
|
-
createElementVNode("div", _hoisted_11$9, toDisplayString(item.label), 1),
|
|
41980
|
-
createElementVNode("div", _hoisted_12$9, toDisplayString(item.value), 1)
|
|
41981
|
-
]);
|
|
41982
|
-
}), 128))
|
|
41983
|
-
])
|
|
41984
|
-
]),
|
|
41985
|
-
createElementVNode("div", _hoisted_13$7, [
|
|
41986
|
-
_cache[6] || (_cache[6] = createElementVNode("div", { class: "popup-section-title" }, "网络指标", -1)),
|
|
41987
|
-
createElementVNode("div", _hoisted_14$6, [
|
|
41988
|
-
(openBlock(true), createElementBlock(Fragment, null, renderList(overviewItems.value, (item) => {
|
|
41989
|
-
return openBlock(), createElementBlock("div", {
|
|
41990
|
-
key: item.label,
|
|
41991
|
-
class: "popup-item"
|
|
41992
|
-
}, [
|
|
41993
|
-
createElementVNode("div", _hoisted_15$5, toDisplayString(item.label), 1),
|
|
41994
|
-
createElementVNode("div", _hoisted_16$5, toDisplayString(item.value), 1)
|
|
41995
|
-
]);
|
|
41996
|
-
}), 128))
|
|
41997
|
-
])
|
|
41998
|
-
])
|
|
41999
|
-
], 2),
|
|
42000
|
-
advancedOpen.value ? (openBlock(), createElementBlock("div", {
|
|
42008
|
+
createElementVNode("div", {
|
|
42009
|
+
class: normalizeClass(["close-confirm-dropdown", themeClass.value])
|
|
42010
|
+
}, [
|
|
42011
|
+
unref(meState2).roleType == "1" ? (openBlock(), createElementBlock("div", {
|
|
42001
42012
|
key: 0,
|
|
42002
|
-
class:
|
|
42003
|
-
onClick:
|
|
42004
|
-
|
|
42005
|
-
|
|
42006
|
-
|
|
42007
|
-
|
|
42008
|
-
|
|
42009
|
-
|
|
42010
|
-
|
|
42011
|
-
|
|
42012
|
-
|
|
42013
|
-
|
|
42014
|
-
])
|
|
42015
|
-
]),
|
|
42016
|
-
createElementVNode("div", _hoisted_19$4, [
|
|
42017
|
-
(openBlock(true), createElementBlock(Fragment, null, renderList(detailSections.value, (section) => {
|
|
42018
|
-
return openBlock(), createElementBlock("div", {
|
|
42019
|
-
key: section.key,
|
|
42020
|
-
class: "popup-section"
|
|
42021
|
-
}, [
|
|
42022
|
-
createElementVNode("button", {
|
|
42023
|
-
type: "button",
|
|
42024
|
-
class: normalizeClass(["detail-section-toggle", { collapsed: isSectionCollapsed(section.key) }]),
|
|
42025
|
-
onClick: ($event) => toggleSection(section.key)
|
|
42026
|
-
}, [
|
|
42027
|
-
createElementVNode("span", _hoisted_21$2, [
|
|
42028
|
-
createElementVNode("span", _hoisted_22$2, toDisplayString(section.title), 1),
|
|
42029
|
-
createElementVNode("span", _hoisted_23$2, "(" + toDisplayString(section.cards.length) + ")", 1)
|
|
42030
|
-
]),
|
|
42031
|
-
createElementVNode("span", _hoisted_24$2, toDisplayString(isSectionCollapsed(section.key) ? "展开" : "收起"), 1)
|
|
42032
|
-
], 10, _hoisted_20$4),
|
|
42033
|
-
!isSectionCollapsed(section.key) && section.cards.length ? (openBlock(), createElementBlock("div", _hoisted_25$2, [
|
|
42034
|
-
(openBlock(true), createElementBlock(Fragment, null, renderList(section.cards, (card) => {
|
|
42035
|
-
return openBlock(), createElementBlock("div", {
|
|
42036
|
-
key: card.title,
|
|
42037
|
-
class: "metric-card"
|
|
42038
|
-
}, [
|
|
42039
|
-
createElementVNode("div", _hoisted_26$2, [
|
|
42040
|
-
createElementVNode("div", _hoisted_27$2, toDisplayString(card.title), 1),
|
|
42041
|
-
createElementVNode("div", _hoisted_28$2, toDisplayString(card.subtitle), 1)
|
|
42042
|
-
]),
|
|
42043
|
-
createElementVNode("div", _hoisted_29$2, [
|
|
42044
|
-
(openBlock(true), createElementBlock(Fragment, null, renderList(card.items, (item) => {
|
|
42045
|
-
return openBlock(), createElementBlock("div", {
|
|
42046
|
-
key: `${card.title}-${item.label}`,
|
|
42047
|
-
class: "metric-card-item"
|
|
42048
|
-
}, [
|
|
42049
|
-
createElementVNode("div", _hoisted_30$2, toDisplayString(item.label), 1),
|
|
42050
|
-
createElementVNode("div", _hoisted_31$1, toDisplayString(item.value), 1)
|
|
42051
|
-
]);
|
|
42052
|
-
}), 128))
|
|
42053
|
-
])
|
|
42054
|
-
]);
|
|
42055
|
-
}), 128))
|
|
42056
|
-
])) : !isSectionCollapsed(section.key) ? (openBlock(), createElementBlock("div", _hoisted_32$1, "暂无数据")) : createCommentVNode("", true)
|
|
42057
|
-
]);
|
|
42058
|
-
}), 128))
|
|
42059
|
-
])
|
|
42060
|
-
], 2)) : createCommentVNode("", true)
|
|
42061
|
-
])
|
|
42013
|
+
class: "close-confirm-option danger",
|
|
42014
|
+
onClick: withModifiers(handleEndMeeting, ["stop"])
|
|
42015
|
+
}, " 结束会议 ")) : createCommentVNode("", true),
|
|
42016
|
+
createElementVNode("div", {
|
|
42017
|
+
class: "close-confirm-option simple",
|
|
42018
|
+
onClick: withModifiers(handleLeaveMeeting, ["stop"])
|
|
42019
|
+
}, " 离开会议 "),
|
|
42020
|
+
createElementVNode("div", {
|
|
42021
|
+
class: "close-confirm-option simple",
|
|
42022
|
+
onClick: _cache[1] || (_cache[1] = ($event) => showCloseConfirm.value = false)
|
|
42023
|
+
}, " 取消 ")
|
|
42024
|
+
], 2)
|
|
42062
42025
|
]),
|
|
42063
42026
|
_: 1
|
|
42064
42027
|
}, 8, ["visible", "trigger"])
|
|
@@ -42066,194 +42029,690 @@ const _sfc_main$o = /* @__PURE__ */ defineComponent({
|
|
|
42066
42029
|
};
|
|
42067
42030
|
}
|
|
42068
42031
|
});
|
|
42069
|
-
const
|
|
42070
|
-
const _hoisted_1$
|
|
42071
|
-
const
|
|
42072
|
-
|
|
42032
|
+
const MeetingHeader = /* @__PURE__ */ _export_sfc(_sfc_main$p, [["__scopeId", "data-v-2299cd2f"]]);
|
|
42033
|
+
const _hoisted_1$m = ["title"];
|
|
42034
|
+
const _hoisted_2$k = {
|
|
42035
|
+
class: "signal-bars",
|
|
42036
|
+
"aria-hidden": "true"
|
|
42037
|
+
};
|
|
42038
|
+
const _hoisted_3$k = {
|
|
42039
|
+
key: 0,
|
|
42040
|
+
class: "signal-text"
|
|
42041
|
+
};
|
|
42042
|
+
const _hoisted_4$i = { class: "network-quality-popup-shell" };
|
|
42043
|
+
const _hoisted_5$f = { class: "popup-header" };
|
|
42044
|
+
const _hoisted_6$e = { class: "popup-header-actions" };
|
|
42045
|
+
const _hoisted_7$d = { class: "popup-summary" };
|
|
42046
|
+
const _hoisted_8$c = {
|
|
42047
|
+
key: 0,
|
|
42048
|
+
class: "popup-reasons"
|
|
42049
|
+
};
|
|
42050
|
+
const _hoisted_9$a = { class: "popup-section" };
|
|
42051
|
+
const _hoisted_10$a = { class: "popup-grid" };
|
|
42052
|
+
const _hoisted_11$9 = { class: "popup-item-label" };
|
|
42053
|
+
const _hoisted_12$9 = { class: "popup-item-value" };
|
|
42054
|
+
const _hoisted_13$7 = { class: "popup-section" };
|
|
42055
|
+
const _hoisted_14$6 = { class: "popup-grid" };
|
|
42056
|
+
const _hoisted_15$5 = { class: "popup-item-label" };
|
|
42057
|
+
const _hoisted_16$5 = { class: "popup-item-value" };
|
|
42058
|
+
const _hoisted_17$4 = { class: "popup-header" };
|
|
42059
|
+
const _hoisted_18$4 = { class: "popup-header-actions" };
|
|
42060
|
+
const _hoisted_19$4 = { class: "popup-section-container" };
|
|
42061
|
+
const _hoisted_20$4 = ["onClick"];
|
|
42062
|
+
const _hoisted_21$2 = { class: "detail-section-main" };
|
|
42063
|
+
const _hoisted_22$2 = { class: "popup-section-title" };
|
|
42064
|
+
const _hoisted_23$2 = { class: "popup-section-count" };
|
|
42065
|
+
const _hoisted_24$2 = { class: "detail-section-icon" };
|
|
42066
|
+
const _hoisted_25$2 = {
|
|
42067
|
+
key: 0,
|
|
42068
|
+
class: "metric-card-list"
|
|
42069
|
+
};
|
|
42070
|
+
const _hoisted_26$2 = { class: "metric-card-header" };
|
|
42071
|
+
const _hoisted_27$2 = { class: "metric-card-title" };
|
|
42072
|
+
const _hoisted_28$2 = { class: "metric-card-subtitle" };
|
|
42073
|
+
const _hoisted_29$2 = { class: "metric-card-grid" };
|
|
42074
|
+
const _hoisted_30$2 = { class: "popup-item-label" };
|
|
42075
|
+
const _hoisted_31$1 = { class: "popup-item-value" };
|
|
42076
|
+
const _hoisted_32$1 = {
|
|
42077
|
+
key: 1,
|
|
42078
|
+
class: "popup-empty"
|
|
42079
|
+
};
|
|
42080
|
+
const _sfc_main$o = /* @__PURE__ */ defineComponent({
|
|
42081
|
+
__name: "NetworkQualitySignal",
|
|
42073
42082
|
props: {
|
|
42074
|
-
theme: { default: "dark" }
|
|
42083
|
+
theme: { default: "dark" },
|
|
42084
|
+
size: { default: "default" },
|
|
42085
|
+
showText: { type: Boolean, default: false }
|
|
42075
42086
|
},
|
|
42076
42087
|
setup(__props) {
|
|
42077
42088
|
const props = __props;
|
|
42089
|
+
const triggerRef = ref(null);
|
|
42090
|
+
const showPopup = ref(false);
|
|
42091
|
+
const advancedOpen = ref(false);
|
|
42092
|
+
const collapsedSections = ref({});
|
|
42093
|
+
const advancedDetails = ref();
|
|
42094
|
+
let detailTimer = null;
|
|
42078
42095
|
const themeClass = computed(() => `theme-${props.theme}`);
|
|
42079
|
-
|
|
42080
|
-
if (
|
|
42081
|
-
|
|
42082
|
-
|
|
42083
|
-
|
|
42084
|
-
|
|
42085
|
-
|
|
42086
|
-
|
|
42087
|
-
|
|
42088
|
-
|
|
42089
|
-
|
|
42090
|
-
|
|
42091
|
-
|
|
42092
|
-
|
|
42093
|
-
|
|
42094
|
-
|
|
42095
|
-
|
|
42096
|
-
|
|
42097
|
-
|
|
42098
|
-
|
|
42099
|
-
|
|
42100
|
-
|
|
42101
|
-
|
|
42102
|
-
|
|
42103
|
-
|
|
42104
|
-
|
|
42105
|
-
|
|
42106
|
-
|
|
42107
|
-
const
|
|
42108
|
-
const
|
|
42109
|
-
|
|
42110
|
-
|
|
42111
|
-
}
|
|
42112
|
-
|
|
42113
|
-
|
|
42114
|
-
|
|
42115
|
-
|
|
42116
|
-
|
|
42117
|
-
const _hoisted_12$8 = ["disabled", "onClick"];
|
|
42118
|
-
const _sfc_main$m = /* @__PURE__ */ defineComponent({
|
|
42119
|
-
__name: "meeting-content",
|
|
42120
|
-
props: {
|
|
42121
|
-
layout: { default: "grid-9" },
|
|
42122
|
-
isFullscreen: { type: Boolean },
|
|
42123
|
-
joinTime: { default: 0 },
|
|
42124
|
-
theme: { default: "dark" }
|
|
42125
|
-
},
|
|
42126
|
-
emits: ["layout-change", "toggle-fullscreen"],
|
|
42127
|
-
setup(__props, { emit: __emit }) {
|
|
42128
|
-
const speakingUserName = ref("");
|
|
42129
|
-
emitter.on("speakingUserName", (name2) => {
|
|
42130
|
-
speakingUserName.value = name2;
|
|
42131
|
-
});
|
|
42132
|
-
const props = __props;
|
|
42133
|
-
const userId = getUserId();
|
|
42134
|
-
const joinTimeObj = ref("");
|
|
42135
|
-
const speakerId = ref(null);
|
|
42136
|
-
const lastGridLayout = ref("grid-9");
|
|
42137
|
-
const getUserName2 = (user) => {
|
|
42138
|
-
var _a25, _b25, _c2;
|
|
42139
|
-
return ((_a25 = user == null ? void 0 : user.member) == null ? void 0 : _a25.memberId) == userId ? `(我)${(_b25 = user == null ? void 0 : user.member) == null ? void 0 : _b25.name}` : ((_c2 = user == null ? void 0 : user.member) == null ? void 0 : _c2.name) || "未知用户";
|
|
42140
|
-
};
|
|
42141
|
-
const layoutClass = computed(() => `layout-${props.layout}`);
|
|
42142
|
-
const allJoinedMembers = computed(() => {
|
|
42143
|
-
return members.value.filter((user) => (user == null ? void 0 : user.joinStaus) == "1");
|
|
42144
|
-
});
|
|
42145
|
-
const speakerUser = computed(() => {
|
|
42146
|
-
if (!speakerId.value) return allJoinedMembers.value[0];
|
|
42147
|
-
return allJoinedMembers.value.find(
|
|
42148
|
-
(user) => {
|
|
42149
|
-
var _a25;
|
|
42150
|
-
return ((_a25 = user == null ? void 0 : user.member) == null ? void 0 : _a25.memberId) === speakerId.value;
|
|
42151
|
-
}
|
|
42152
|
-
) || allJoinedMembers.value[0];
|
|
42153
|
-
});
|
|
42154
|
-
const preferredRemoteVideoFocusId = computed(() => {
|
|
42155
|
-
var _a25, _b25;
|
|
42156
|
-
if (props.layout !== "speaker") return null;
|
|
42157
|
-
const focusedUid = (_b25 = (_a25 = speakerUser.value) == null ? void 0 : _a25.member) == null ? void 0 : _b25.memberId;
|
|
42158
|
-
return focusedUid && focusedUid !== userId ? focusedUid : null;
|
|
42159
|
-
});
|
|
42160
|
-
const filteredMembers = computed(() => {
|
|
42161
|
-
return allJoinedMembers.value;
|
|
42162
|
-
});
|
|
42163
|
-
const setSpeaker = (user) => {
|
|
42096
|
+
function formatValue(value, suffix = "", digits = 0) {
|
|
42097
|
+
if (typeof value !== "number" || Number.isNaN(value)) return "--";
|
|
42098
|
+
return `${value.toFixed(digits)}${suffix}`;
|
|
42099
|
+
}
|
|
42100
|
+
function formatMetricNumber(value, suffix = "", digits = 0) {
|
|
42101
|
+
if (typeof value !== "number" || Number.isNaN(value)) return "--";
|
|
42102
|
+
return `${value.toFixed(digits)}${suffix}`;
|
|
42103
|
+
}
|
|
42104
|
+
function formatBytes(value) {
|
|
42105
|
+
if (typeof value !== "number" || Number.isNaN(value)) return "--";
|
|
42106
|
+
if (value >= 1024 * 1024) return `${(value / (1024 * 1024)).toFixed(2)} MB`;
|
|
42107
|
+
if (value >= 1024) return `${(value / 1024).toFixed(1)} KB`;
|
|
42108
|
+
return `${value.toFixed(0)} B`;
|
|
42109
|
+
}
|
|
42110
|
+
function formatDimension(width, height) {
|
|
42111
|
+
if (!width || !height) return "--";
|
|
42112
|
+
return `${width} x ${height}`;
|
|
42113
|
+
}
|
|
42114
|
+
function formatLossRate(value) {
|
|
42115
|
+
if (typeof value !== "number" || Number.isNaN(value)) return "--";
|
|
42116
|
+
return `${value.toFixed(1)}%`;
|
|
42117
|
+
}
|
|
42118
|
+
function formatMos(value) {
|
|
42119
|
+
if (typeof value !== "number" || value <= 0 || Number.isNaN(value)) return "--";
|
|
42120
|
+
return value.toFixed(1);
|
|
42121
|
+
}
|
|
42122
|
+
function formatUpdatedAt(timestamp) {
|
|
42123
|
+
if (!timestamp) return "--";
|
|
42124
|
+
const date = new Date(timestamp);
|
|
42125
|
+
const hh = String(date.getHours()).padStart(2, "0");
|
|
42126
|
+
const mm = String(date.getMinutes()).padStart(2, "0");
|
|
42127
|
+
const ss = String(date.getSeconds()).padStart(2, "0");
|
|
42128
|
+
return `${hh}:${mm}:${ss}`;
|
|
42129
|
+
}
|
|
42130
|
+
function formatReason(value) {
|
|
42131
|
+
return value || "--";
|
|
42132
|
+
}
|
|
42133
|
+
function getMemberNameById(memberId) {
|
|
42164
42134
|
var _a25;
|
|
42165
|
-
|
|
42166
|
-
|
|
42167
|
-
|
|
42135
|
+
if (!memberId) return "";
|
|
42136
|
+
const matched = members.value.find(
|
|
42137
|
+
(member) => {
|
|
42138
|
+
var _a26;
|
|
42139
|
+
return ((_a26 = member == null ? void 0 : member.member) == null ? void 0 : _a26.memberId) === String(memberId);
|
|
42140
|
+
}
|
|
42141
|
+
);
|
|
42142
|
+
return ((_a25 = matched == null ? void 0 : matched.member) == null ? void 0 : _a25.name) || "";
|
|
42143
|
+
}
|
|
42144
|
+
function startDetailRefresh() {
|
|
42145
|
+
stopDetailRefresh();
|
|
42146
|
+
refreshAdvancedDetails();
|
|
42147
|
+
detailTimer = window.setInterval(() => {
|
|
42148
|
+
refreshAdvancedDetails();
|
|
42149
|
+
}, 2e3);
|
|
42150
|
+
}
|
|
42151
|
+
function stopDetailRefresh() {
|
|
42152
|
+
if (detailTimer !== null) {
|
|
42153
|
+
window.clearInterval(detailTimer);
|
|
42154
|
+
detailTimer = null;
|
|
42168
42155
|
}
|
|
42169
|
-
}
|
|
42170
|
-
|
|
42171
|
-
|
|
42172
|
-
|
|
42156
|
+
}
|
|
42157
|
+
function refreshAdvancedDetails() {
|
|
42158
|
+
advancedDetails.value = readNetworkQualityMetricDetails();
|
|
42159
|
+
}
|
|
42160
|
+
function isSectionCollapsed(key) {
|
|
42161
|
+
return collapsedSections.value[key] ?? false;
|
|
42162
|
+
}
|
|
42163
|
+
function toggleSection(key) {
|
|
42164
|
+
collapsedSections.value[key] = !isSectionCollapsed(key);
|
|
42165
|
+
}
|
|
42166
|
+
function expandAllSections() {
|
|
42167
|
+
collapsedSections.value = Object.fromEntries(
|
|
42168
|
+
detailSections.value.map((section) => [section.key, false])
|
|
42169
|
+
);
|
|
42170
|
+
}
|
|
42171
|
+
function collapseAllSections() {
|
|
42172
|
+
collapsedSections.value = Object.fromEntries(
|
|
42173
|
+
detailSections.value.map((section) => [section.key, true])
|
|
42174
|
+
);
|
|
42175
|
+
}
|
|
42176
|
+
function toggleAllSections() {
|
|
42177
|
+
if (allSectionsExpanded.value) {
|
|
42178
|
+
collapseAllSections();
|
|
42179
|
+
return;
|
|
42173
42180
|
}
|
|
42174
|
-
|
|
42175
|
-
|
|
42176
|
-
|
|
42177
|
-
const
|
|
42178
|
-
|
|
42179
|
-
|
|
42180
|
-
|
|
42181
|
-
|
|
42182
|
-
emit("toggle-fullscreen");
|
|
42183
|
-
};
|
|
42184
|
-
const emit = __emit;
|
|
42185
|
-
let timer = null;
|
|
42186
|
-
watch(
|
|
42187
|
-
() => props.layout,
|
|
42188
|
-
(layout) => {
|
|
42189
|
-
if (layout !== "speaker") {
|
|
42190
|
-
lastGridLayout.value = layout;
|
|
42191
|
-
}
|
|
42192
|
-
},
|
|
42193
|
-
{ immediate: true }
|
|
42194
|
-
);
|
|
42195
|
-
watch(
|
|
42196
|
-
() => props.joinTime,
|
|
42197
|
-
() => {
|
|
42198
|
-
if (timer) {
|
|
42199
|
-
clearInterval(timer);
|
|
42200
|
-
timer = null;
|
|
42201
|
-
}
|
|
42202
|
-
if (props.joinTime) {
|
|
42203
|
-
timer = setInterval(() => {
|
|
42204
|
-
const now = +/* @__PURE__ */ new Date();
|
|
42205
|
-
joinTimeObj.value = formatDuringObject((now - props.joinTime) / 1e3);
|
|
42206
|
-
}, 1e3);
|
|
42181
|
+
expandAllSections();
|
|
42182
|
+
}
|
|
42183
|
+
function syncSectionCollapse(preserveExisting = true) {
|
|
42184
|
+
const nextState = {};
|
|
42185
|
+
for (const section of detailSections.value) {
|
|
42186
|
+
if (preserveExisting && section.key in collapsedSections.value) {
|
|
42187
|
+
nextState[section.key] = collapsedSections.value[section.key];
|
|
42188
|
+
continue;
|
|
42207
42189
|
}
|
|
42208
|
-
|
|
42209
|
-
{
|
|
42210
|
-
immediate: true
|
|
42190
|
+
nextState[section.key] = false;
|
|
42211
42191
|
}
|
|
42212
|
-
|
|
42213
|
-
|
|
42214
|
-
|
|
42215
|
-
|
|
42216
|
-
|
|
42217
|
-
}, 100);
|
|
42218
|
-
});
|
|
42219
|
-
watch(
|
|
42220
|
-
() => members.value.length,
|
|
42221
|
-
async (len, prevLen) => {
|
|
42222
|
-
if (!len || len === prevLen) return;
|
|
42223
|
-
await nextTick();
|
|
42224
|
-
setTimeout(() => mountTracks(), 0);
|
|
42225
|
-
},
|
|
42226
|
-
{ flush: "post" }
|
|
42227
|
-
);
|
|
42228
|
-
watch(
|
|
42229
|
-
preferredRemoteVideoFocusId,
|
|
42230
|
-
(focusedUid) => {
|
|
42231
|
-
void setPreferredRemoteVideoFocus(focusedUid);
|
|
42232
|
-
},
|
|
42233
|
-
{ immediate: true }
|
|
42234
|
-
);
|
|
42235
|
-
onUnmounted(() => {
|
|
42236
|
-
void setPreferredRemoteVideoFocus(null);
|
|
42237
|
-
if (timer) {
|
|
42238
|
-
clearInterval(timer);
|
|
42239
|
-
timer = null;
|
|
42192
|
+
collapsedSections.value = nextState;
|
|
42193
|
+
}
|
|
42194
|
+
const activeBars = computed(() => {
|
|
42195
|
+
if (networkQualityState.isOffline || networkQualityState.isStale) {
|
|
42196
|
+
return 0;
|
|
42240
42197
|
}
|
|
42198
|
+
return getNetworkSignalBars(networkQualityState.level);
|
|
42241
42199
|
});
|
|
42242
|
-
|
|
42243
|
-
|
|
42244
|
-
|
|
42245
|
-
|
|
42246
|
-
|
|
42247
|
-
|
|
42248
|
-
|
|
42249
|
-
|
|
42250
|
-
|
|
42251
|
-
|
|
42252
|
-
|
|
42253
|
-
|
|
42254
|
-
|
|
42255
|
-
|
|
42256
|
-
|
|
42200
|
+
const allSectionsExpanded = computed(() => {
|
|
42201
|
+
if (!detailSections.value.length) return true;
|
|
42202
|
+
return detailSections.value.every((section) => !isSectionCollapsed(section.key));
|
|
42203
|
+
});
|
|
42204
|
+
const reasonText = computed(() => {
|
|
42205
|
+
if (!networkQualityState.reasons.length) return "";
|
|
42206
|
+
return `可能原因:${networkQualityState.reasons.join("、")}`;
|
|
42207
|
+
});
|
|
42208
|
+
const qualityItems = computed(() => [
|
|
42209
|
+
{ label: "总体质量", value: networkQualityState.label },
|
|
42210
|
+
{ label: "上行质量", value: networkQualityState.uplink },
|
|
42211
|
+
{ label: "下行质量", value: networkQualityState.downlink },
|
|
42212
|
+
{ label: "MOS", value: formatMos(networkQualityState.mos) },
|
|
42213
|
+
{ label: "最近更新", value: formatUpdatedAt(networkQualityState.updatedAt) },
|
|
42214
|
+
{
|
|
42215
|
+
label: "数据状态",
|
|
42216
|
+
value: networkQualityState.isStale ? "已过期" : "实时"
|
|
42217
|
+
}
|
|
42218
|
+
]);
|
|
42219
|
+
const overviewItems = computed(() => {
|
|
42220
|
+
const stats = networkQualityState.stats;
|
|
42221
|
+
return [
|
|
42222
|
+
{ label: "上行 RTT", value: formatValue(stats == null ? void 0 : stats.rtt_up, " ms") },
|
|
42223
|
+
{ label: "下行 RTT", value: formatValue(stats == null ? void 0 : stats.rtt_down, " ms") },
|
|
42224
|
+
{ label: "上行码率", value: formatValue(stats == null ? void 0 : stats.bitrate_up, " kb/s") },
|
|
42225
|
+
{ label: "下行码率", value: formatValue(stats == null ? void 0 : stats.bitrate_down, " kb/s") },
|
|
42226
|
+
{ label: "上行丢包率", value: formatLossRate(stats == null ? void 0 : stats.lossrate_up) },
|
|
42227
|
+
{ label: "下行丢包率", value: formatLossRate(stats == null ? void 0 : stats.lossrate_down) },
|
|
42228
|
+
{
|
|
42229
|
+
label: "可用上行带宽",
|
|
42230
|
+
value: formatValue(stats == null ? void 0 : stats.available_outgoing_bitrate, " kb/s")
|
|
42231
|
+
},
|
|
42232
|
+
{
|
|
42233
|
+
label: "可用下行带宽",
|
|
42234
|
+
value: formatValue(stats == null ? void 0 : stats.available_incoming_bitrate, " kb/s")
|
|
42235
|
+
}
|
|
42236
|
+
];
|
|
42237
|
+
});
|
|
42238
|
+
function createAudioSendCard(item, index) {
|
|
42239
|
+
const stats = (item == null ? void 0 : item.stats) || {};
|
|
42240
|
+
return {
|
|
42241
|
+
title: `本地音频 ${index + 1}`,
|
|
42242
|
+
subtitle: (item == null ? void 0 : item.desc) || (stats == null ? void 0 : stats.streamId) || "发送轨道",
|
|
42243
|
+
items: [
|
|
42244
|
+
{ label: "实时码率", value: formatMetricNumber(item == null ? void 0 : item.bitrate, " kb/s") },
|
|
42245
|
+
{ label: "音频电平", value: formatMetricNumber(item == null ? void 0 : item.db, " dBFS", 1) },
|
|
42246
|
+
{ label: "已发包数", value: formatMetricNumber(stats == null ? void 0 : stats.packetsSent) },
|
|
42247
|
+
{ label: "已发字节", value: formatBytes(stats == null ? void 0 : stats.bytesSent) },
|
|
42248
|
+
{ label: "远端丢包", value: formatMetricNumber(stats == null ? void 0 : stats.packetsLost) },
|
|
42249
|
+
{ label: "往返时延", value: formatMetricNumber(stats == null ? void 0 : stats.roundTripTime, " ms") },
|
|
42250
|
+
{ label: "远端抖动", value: formatMetricNumber(stats == null ? void 0 : stats.jitter, " ms") },
|
|
42251
|
+
{ label: "采样时间", value: formatUpdatedAt(stats == null ? void 0 : stats.timestamp) }
|
|
42252
|
+
]
|
|
42253
|
+
};
|
|
42254
|
+
}
|
|
42255
|
+
function createVideoSendCard(item, index) {
|
|
42256
|
+
const stats = (item == null ? void 0 : item.stats) || {};
|
|
42257
|
+
return {
|
|
42258
|
+
title: `本地视频 ${index + 1}`,
|
|
42259
|
+
subtitle: (item == null ? void 0 : item.desc) || (stats == null ? void 0 : stats.rid) || (stats == null ? void 0 : stats.streamId) || "发送轨道",
|
|
42260
|
+
items: [
|
|
42261
|
+
{ label: "实时码率", value: formatMetricNumber(item == null ? void 0 : item.bitrate, " kb/s") },
|
|
42262
|
+
{ label: "分辨率", value: formatDimension((item == null ? void 0 : item.width) || (stats == null ? void 0 : stats.frameWidth), (item == null ? void 0 : item.height) || (stats == null ? void 0 : stats.frameHeight)) },
|
|
42263
|
+
{ label: "帧率", value: formatMetricNumber((item == null ? void 0 : item.fps) || (stats == null ? void 0 : stats.framesPerSecond), " fps") },
|
|
42264
|
+
{ label: "已发帧数", value: formatMetricNumber(stats == null ? void 0 : stats.framesSent) },
|
|
42265
|
+
{ label: "目标码率", value: formatMetricNumber(stats == null ? void 0 : stats.targetBitrate, " bps") },
|
|
42266
|
+
{ label: "FIR/PLI/NACK", value: `${formatMetricNumber(stats == null ? void 0 : stats.firCount)}/${formatMetricNumber(stats == null ? void 0 : stats.pliCount)}/${formatMetricNumber(stats == null ? void 0 : stats.nackCount)}` },
|
|
42267
|
+
{ label: "重传包数", value: formatMetricNumber(stats == null ? void 0 : stats.retransmittedPacketsSent) },
|
|
42268
|
+
{ label: "受限原因", value: formatReason(stats == null ? void 0 : stats.qualityLimitationReason) },
|
|
42269
|
+
{ label: "分辨率变化", value: formatMetricNumber(stats == null ? void 0 : stats.qualityLimitationResolutionChanges) },
|
|
42270
|
+
{ label: "往返时延", value: formatMetricNumber(stats == null ? void 0 : stats.roundTripTime, " ms") },
|
|
42271
|
+
{ label: "采样时间", value: formatUpdatedAt(stats == null ? void 0 : stats.timestamp) }
|
|
42272
|
+
]
|
|
42273
|
+
};
|
|
42274
|
+
}
|
|
42275
|
+
function createAudioRecvCard(item, index) {
|
|
42276
|
+
const stats = (item == null ? void 0 : item.stats) || {};
|
|
42277
|
+
return {
|
|
42278
|
+
title: `远端音频 ${index + 1}`,
|
|
42279
|
+
subtitle: (item == null ? void 0 : item.uid) || (item == null ? void 0 : item.desc) || (stats == null ? void 0 : stats.streamId) || "接收轨道",
|
|
42280
|
+
items: [
|
|
42281
|
+
{ label: "实时码率", value: formatMetricNumber(item == null ? void 0 : item.bitrate, " kb/s") },
|
|
42282
|
+
{ label: "音频电平", value: formatMetricNumber(item == null ? void 0 : item.db, " dBFS", 1) },
|
|
42283
|
+
{ label: "已收包数", value: formatMetricNumber(stats == null ? void 0 : stats.packetsReceived) },
|
|
42284
|
+
{ label: "已收字节", value: formatBytes(stats == null ? void 0 : stats.bytesReceived) },
|
|
42285
|
+
{ label: "丢包数", value: formatMetricNumber(stats == null ? void 0 : stats.packetsLost) },
|
|
42286
|
+
{ label: "网络抖动", value: formatMetricNumber(stats == null ? void 0 : stats.jitter, " ms") },
|
|
42287
|
+
{ label: "缓冲均值", value: formatMetricNumber(stats == null ? void 0 : stats.jitterBufferAvgDelay, " ms") },
|
|
42288
|
+
{ label: "隐藏事件", value: formatMetricNumber(stats == null ? void 0 : stats.concealmentEvents) },
|
|
42289
|
+
{ label: "静音隐藏", value: formatMetricNumber(stats == null ? void 0 : stats.silentConcealmentEvents) },
|
|
42290
|
+
{ label: "采样时间", value: formatUpdatedAt(stats == null ? void 0 : stats.timestamp) }
|
|
42291
|
+
]
|
|
42292
|
+
};
|
|
42293
|
+
}
|
|
42294
|
+
function createVideoRecvCard(item, index) {
|
|
42295
|
+
const stats = (item == null ? void 0 : item.stats) || {};
|
|
42296
|
+
const memberName = getMemberNameById(item == null ? void 0 : item.uid);
|
|
42297
|
+
return {
|
|
42298
|
+
title: `远端视频 ${index + 1}`,
|
|
42299
|
+
subtitle: memberName || (item == null ? void 0 : item.uid) || (item == null ? void 0 : item.desc) || (stats == null ? void 0 : stats.mimeType) || "接收轨道",
|
|
42300
|
+
items: [
|
|
42301
|
+
{ label: "实时码率", value: formatMetricNumber(item == null ? void 0 : item.bitrate, " kb/s") },
|
|
42302
|
+
{ label: "分辨率", value: formatDimension((item == null ? void 0 : item.width) || (stats == null ? void 0 : stats.frameWidth), (item == null ? void 0 : item.height) || (stats == null ? void 0 : stats.frameHeight)) },
|
|
42303
|
+
{ label: "帧率", value: formatMetricNumber((item == null ? void 0 : item.fps) || (stats == null ? void 0 : stats.framesPerSecond), " fps") },
|
|
42304
|
+
{ label: "已收包数", value: formatMetricNumber(stats == null ? void 0 : stats.packetsReceived) },
|
|
42305
|
+
{ label: "已收字节", value: formatBytes(stats == null ? void 0 : stats.bytesReceived) },
|
|
42306
|
+
{ label: "丢包数", value: formatMetricNumber(stats == null ? void 0 : stats.packetsLost) },
|
|
42307
|
+
{ label: "接收/解码帧", value: `${formatMetricNumber(stats == null ? void 0 : stats.framesReceived)}/${formatMetricNumber(stats == null ? void 0 : stats.framesDecoded)}` },
|
|
42308
|
+
{ label: "丢帧数", value: formatMetricNumber(stats == null ? void 0 : stats.framesDropped) },
|
|
42309
|
+
{ label: "FIR/PLI/NACK", value: `${formatMetricNumber(stats == null ? void 0 : stats.firCount)}/${formatMetricNumber(stats == null ? void 0 : stats.pliCount)}/${formatMetricNumber(stats == null ? void 0 : stats.nackCount)}` },
|
|
42310
|
+
{ label: "缓冲均值", value: formatMetricNumber(stats == null ? void 0 : stats.jitterBufferAvgDelay, " ms") },
|
|
42311
|
+
{ label: "解码器", value: formatReason(stats == null ? void 0 : stats.decoderImplementation) },
|
|
42312
|
+
{ label: "MIME", value: formatReason(stats == null ? void 0 : stats.mimeType) },
|
|
42313
|
+
{ label: "采样时间", value: formatUpdatedAt(stats == null ? void 0 : stats.timestamp) }
|
|
42314
|
+
]
|
|
42315
|
+
};
|
|
42316
|
+
}
|
|
42317
|
+
const detailSections = computed(() => {
|
|
42318
|
+
const details = advancedDetails.value;
|
|
42319
|
+
return [
|
|
42320
|
+
{
|
|
42321
|
+
key: "local-audio",
|
|
42322
|
+
title: "本地音频发送",
|
|
42323
|
+
cards: ((details == null ? void 0 : details.local_audios) || []).map(createAudioSendCard)
|
|
42324
|
+
},
|
|
42325
|
+
{
|
|
42326
|
+
key: "local-video",
|
|
42327
|
+
title: "本地视频发送",
|
|
42328
|
+
cards: ((details == null ? void 0 : details.local_videos) || []).map(createVideoSendCard)
|
|
42329
|
+
},
|
|
42330
|
+
{
|
|
42331
|
+
key: "remote-audio",
|
|
42332
|
+
title: "远端音频接收",
|
|
42333
|
+
cards: ((details == null ? void 0 : details.remote_audios) || []).map(createAudioRecvCard)
|
|
42334
|
+
},
|
|
42335
|
+
{
|
|
42336
|
+
key: "remote-video",
|
|
42337
|
+
title: "远端视频接收",
|
|
42338
|
+
cards: ((details == null ? void 0 : details.remote_videos) || []).map(createVideoRecvCard)
|
|
42339
|
+
}
|
|
42340
|
+
];
|
|
42341
|
+
});
|
|
42342
|
+
watch(showPopup, (visible) => {
|
|
42343
|
+
if (visible) {
|
|
42344
|
+
refreshNetworkQualitySnapshot(false);
|
|
42345
|
+
if (advancedOpen.value) {
|
|
42346
|
+
syncSectionCollapse();
|
|
42347
|
+
startDetailRefresh();
|
|
42348
|
+
}
|
|
42349
|
+
return;
|
|
42350
|
+
}
|
|
42351
|
+
stopDetailRefresh();
|
|
42352
|
+
advancedOpen.value = false;
|
|
42353
|
+
collapsedSections.value = {};
|
|
42354
|
+
advancedDetails.value = void 0;
|
|
42355
|
+
});
|
|
42356
|
+
watch(advancedOpen, (open2) => {
|
|
42357
|
+
if (!showPopup.value) return;
|
|
42358
|
+
if (open2) {
|
|
42359
|
+
syncSectionCollapse(false);
|
|
42360
|
+
startDetailRefresh();
|
|
42361
|
+
} else {
|
|
42362
|
+
stopDetailRefresh();
|
|
42363
|
+
advancedDetails.value = void 0;
|
|
42364
|
+
}
|
|
42365
|
+
});
|
|
42366
|
+
watch(
|
|
42367
|
+
detailSections,
|
|
42368
|
+
() => {
|
|
42369
|
+
if (!advancedOpen.value) return;
|
|
42370
|
+
syncSectionCollapse();
|
|
42371
|
+
},
|
|
42372
|
+
{ deep: true }
|
|
42373
|
+
);
|
|
42374
|
+
onUnmounted(() => {
|
|
42375
|
+
stopDetailRefresh();
|
|
42376
|
+
});
|
|
42377
|
+
return (_ctx, _cache) => {
|
|
42378
|
+
return openBlock(), createElementBlock("div", {
|
|
42379
|
+
class: normalizeClass(["network-quality-signal", [`size-${_ctx.size}`]])
|
|
42380
|
+
}, [
|
|
42381
|
+
createElementVNode("button", {
|
|
42382
|
+
ref_key: "triggerRef",
|
|
42383
|
+
ref: triggerRef,
|
|
42384
|
+
type: "button",
|
|
42385
|
+
class: normalizeClass(["network-quality-trigger", [
|
|
42386
|
+
`is-${unref(networkQualityState).level}`,
|
|
42387
|
+
{
|
|
42388
|
+
"is-alert": unref(networkQualityState).isOffline || unref(networkQualityState).isStale
|
|
42389
|
+
}
|
|
42390
|
+
]]),
|
|
42391
|
+
title: unref(networkQualityState).summary,
|
|
42392
|
+
onClick: _cache[0] || (_cache[0] = ($event) => showPopup.value = !showPopup.value)
|
|
42393
|
+
}, [
|
|
42394
|
+
createElementVNode("span", _hoisted_2$k, [
|
|
42395
|
+
(openBlock(), createElementBlock(Fragment, null, renderList(4, (index) => {
|
|
42396
|
+
return createElementVNode("span", {
|
|
42397
|
+
key: index,
|
|
42398
|
+
class: normalizeClass(["signal-bar", { active: index <= activeBars.value }])
|
|
42399
|
+
}, null, 2);
|
|
42400
|
+
}), 64))
|
|
42401
|
+
]),
|
|
42402
|
+
_ctx.showText ? (openBlock(), createElementBlock("span", _hoisted_3$k, toDisplayString(unref(networkQualityState).label), 1)) : createCommentVNode("", true)
|
|
42403
|
+
], 10, _hoisted_1$m),
|
|
42404
|
+
createVNode(SmartPopup, {
|
|
42405
|
+
visible: showPopup.value,
|
|
42406
|
+
"onUpdate:visible": _cache[3] || (_cache[3] = ($event) => showPopup.value = $event),
|
|
42407
|
+
trigger: triggerRef.value,
|
|
42408
|
+
gap: 12
|
|
42409
|
+
}, {
|
|
42410
|
+
default: withCtx(() => [
|
|
42411
|
+
createElementVNode("div", _hoisted_4$i, [
|
|
42412
|
+
createElementVNode("div", {
|
|
42413
|
+
class: normalizeClass(["network-quality-popup network-quality-main", themeClass.value])
|
|
42414
|
+
}, [
|
|
42415
|
+
createElementVNode("div", _hoisted_5$f, [
|
|
42416
|
+
_cache[4] || (_cache[4] = createElementVNode("div", { class: "popup-title" }, "网络详情", -1)),
|
|
42417
|
+
createElementVNode("div", _hoisted_6$e, [
|
|
42418
|
+
createElementVNode("button", {
|
|
42419
|
+
type: "button",
|
|
42420
|
+
class: "advanced-toggle",
|
|
42421
|
+
onClick: _cache[1] || (_cache[1] = withModifiers(($event) => advancedOpen.value = !advancedOpen.value, ["stop"]))
|
|
42422
|
+
}, toDisplayString(advancedOpen.value ? "关闭高级模式" : "打开高级模式"), 1),
|
|
42423
|
+
createElementVNode("div", {
|
|
42424
|
+
class: normalizeClass(["popup-status", `is-${unref(networkQualityState).level}`])
|
|
42425
|
+
}, toDisplayString(unref(networkQualityState).label), 3)
|
|
42426
|
+
])
|
|
42427
|
+
]),
|
|
42428
|
+
createElementVNode("div", _hoisted_7$d, toDisplayString(unref(networkQualityState).summary), 1),
|
|
42429
|
+
reasonText.value ? (openBlock(), createElementBlock("div", _hoisted_8$c, toDisplayString(reasonText.value), 1)) : createCommentVNode("", true),
|
|
42430
|
+
createElementVNode("div", _hoisted_9$a, [
|
|
42431
|
+
_cache[5] || (_cache[5] = createElementVNode("div", { class: "popup-section-title" }, "质量概览", -1)),
|
|
42432
|
+
createElementVNode("div", _hoisted_10$a, [
|
|
42433
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(qualityItems.value, (item) => {
|
|
42434
|
+
return openBlock(), createElementBlock("div", {
|
|
42435
|
+
key: item.label,
|
|
42436
|
+
class: "popup-item"
|
|
42437
|
+
}, [
|
|
42438
|
+
createElementVNode("div", _hoisted_11$9, toDisplayString(item.label), 1),
|
|
42439
|
+
createElementVNode("div", _hoisted_12$9, toDisplayString(item.value), 1)
|
|
42440
|
+
]);
|
|
42441
|
+
}), 128))
|
|
42442
|
+
])
|
|
42443
|
+
]),
|
|
42444
|
+
createElementVNode("div", _hoisted_13$7, [
|
|
42445
|
+
_cache[6] || (_cache[6] = createElementVNode("div", { class: "popup-section-title" }, "网络指标", -1)),
|
|
42446
|
+
createElementVNode("div", _hoisted_14$6, [
|
|
42447
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(overviewItems.value, (item) => {
|
|
42448
|
+
return openBlock(), createElementBlock("div", {
|
|
42449
|
+
key: item.label,
|
|
42450
|
+
class: "popup-item"
|
|
42451
|
+
}, [
|
|
42452
|
+
createElementVNode("div", _hoisted_15$5, toDisplayString(item.label), 1),
|
|
42453
|
+
createElementVNode("div", _hoisted_16$5, toDisplayString(item.value), 1)
|
|
42454
|
+
]);
|
|
42455
|
+
}), 128))
|
|
42456
|
+
])
|
|
42457
|
+
])
|
|
42458
|
+
], 2),
|
|
42459
|
+
advancedOpen.value ? (openBlock(), createElementBlock("div", {
|
|
42460
|
+
key: 0,
|
|
42461
|
+
class: normalizeClass(["network-quality-popup network-quality-advanced", themeClass.value]),
|
|
42462
|
+
onClick: _cache[2] || (_cache[2] = withModifiers(() => {
|
|
42463
|
+
}, ["stop"]))
|
|
42464
|
+
}, [
|
|
42465
|
+
createElementVNode("div", _hoisted_17$4, [
|
|
42466
|
+
_cache[7] || (_cache[7] = createElementVNode("div", { class: "popup-title" }, "高级模式", -1)),
|
|
42467
|
+
createElementVNode("div", _hoisted_18$4, [
|
|
42468
|
+
createElementVNode("button", {
|
|
42469
|
+
type: "button",
|
|
42470
|
+
class: "advanced-mini-btn",
|
|
42471
|
+
onClick: toggleAllSections
|
|
42472
|
+
}, toDisplayString(allSectionsExpanded.value ? "全部收起" : "全部展开"), 1)
|
|
42473
|
+
])
|
|
42474
|
+
]),
|
|
42475
|
+
createElementVNode("div", _hoisted_19$4, [
|
|
42476
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(detailSections.value, (section) => {
|
|
42477
|
+
return openBlock(), createElementBlock("div", {
|
|
42478
|
+
key: section.key,
|
|
42479
|
+
class: "popup-section"
|
|
42480
|
+
}, [
|
|
42481
|
+
createElementVNode("button", {
|
|
42482
|
+
type: "button",
|
|
42483
|
+
class: normalizeClass(["detail-section-toggle", { collapsed: isSectionCollapsed(section.key) }]),
|
|
42484
|
+
onClick: ($event) => toggleSection(section.key)
|
|
42485
|
+
}, [
|
|
42486
|
+
createElementVNode("span", _hoisted_21$2, [
|
|
42487
|
+
createElementVNode("span", _hoisted_22$2, toDisplayString(section.title), 1),
|
|
42488
|
+
createElementVNode("span", _hoisted_23$2, "(" + toDisplayString(section.cards.length) + ")", 1)
|
|
42489
|
+
]),
|
|
42490
|
+
createElementVNode("span", _hoisted_24$2, toDisplayString(isSectionCollapsed(section.key) ? "展开" : "收起"), 1)
|
|
42491
|
+
], 10, _hoisted_20$4),
|
|
42492
|
+
!isSectionCollapsed(section.key) && section.cards.length ? (openBlock(), createElementBlock("div", _hoisted_25$2, [
|
|
42493
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(section.cards, (card) => {
|
|
42494
|
+
return openBlock(), createElementBlock("div", {
|
|
42495
|
+
key: card.title,
|
|
42496
|
+
class: "metric-card"
|
|
42497
|
+
}, [
|
|
42498
|
+
createElementVNode("div", _hoisted_26$2, [
|
|
42499
|
+
createElementVNode("div", _hoisted_27$2, toDisplayString(card.title), 1),
|
|
42500
|
+
createElementVNode("div", _hoisted_28$2, toDisplayString(card.subtitle), 1)
|
|
42501
|
+
]),
|
|
42502
|
+
createElementVNode("div", _hoisted_29$2, [
|
|
42503
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(card.items, (item) => {
|
|
42504
|
+
return openBlock(), createElementBlock("div", {
|
|
42505
|
+
key: `${card.title}-${item.label}`,
|
|
42506
|
+
class: "metric-card-item"
|
|
42507
|
+
}, [
|
|
42508
|
+
createElementVNode("div", _hoisted_30$2, toDisplayString(item.label), 1),
|
|
42509
|
+
createElementVNode("div", _hoisted_31$1, toDisplayString(item.value), 1)
|
|
42510
|
+
]);
|
|
42511
|
+
}), 128))
|
|
42512
|
+
])
|
|
42513
|
+
]);
|
|
42514
|
+
}), 128))
|
|
42515
|
+
])) : !isSectionCollapsed(section.key) ? (openBlock(), createElementBlock("div", _hoisted_32$1, "暂无数据")) : createCommentVNode("", true)
|
|
42516
|
+
]);
|
|
42517
|
+
}), 128))
|
|
42518
|
+
])
|
|
42519
|
+
], 2)) : createCommentVNode("", true)
|
|
42520
|
+
])
|
|
42521
|
+
]),
|
|
42522
|
+
_: 1
|
|
42523
|
+
}, 8, ["visible", "trigger"])
|
|
42524
|
+
], 2);
|
|
42525
|
+
};
|
|
42526
|
+
}
|
|
42527
|
+
});
|
|
42528
|
+
const NetworkQualitySignal = /* @__PURE__ */ _export_sfc(_sfc_main$o, [["__scopeId", "data-v-3378006f"]]);
|
|
42529
|
+
const _hoisted_1$l = { class: "network-copy" };
|
|
42530
|
+
const _sfc_main$n = /* @__PURE__ */ defineComponent({
|
|
42531
|
+
__name: "netWorkStatus",
|
|
42532
|
+
props: {
|
|
42533
|
+
theme: { default: "dark" }
|
|
42534
|
+
},
|
|
42535
|
+
setup(__props) {
|
|
42536
|
+
const props = __props;
|
|
42537
|
+
const themeClass = computed(() => `theme-${props.theme}`);
|
|
42538
|
+
const displayLabel = computed(() => {
|
|
42539
|
+
if (networkQualityState.isStale) {
|
|
42540
|
+
return "网络检测中";
|
|
42541
|
+
}
|
|
42542
|
+
return networkQualityState.label;
|
|
42543
|
+
});
|
|
42544
|
+
return (_ctx, _cache) => {
|
|
42545
|
+
return openBlock(), createElementBlock("div", {
|
|
42546
|
+
class: normalizeClass(["network-situation", themeClass.value])
|
|
42547
|
+
}, [
|
|
42548
|
+
createVNode(NetworkQualitySignal, {
|
|
42549
|
+
theme: _ctx.theme,
|
|
42550
|
+
size: "compact"
|
|
42551
|
+
}, null, 8, ["theme"]),
|
|
42552
|
+
createElementVNode("div", _hoisted_1$l, [
|
|
42553
|
+
createElementVNode("span", {
|
|
42554
|
+
class: normalizeClass(["network-label", `is-${unref(networkQualityState).level}`])
|
|
42555
|
+
}, toDisplayString(displayLabel.value), 3)
|
|
42556
|
+
])
|
|
42557
|
+
], 2);
|
|
42558
|
+
};
|
|
42559
|
+
}
|
|
42560
|
+
});
|
|
42561
|
+
const NetWorkStatus = /* @__PURE__ */ _export_sfc(_sfc_main$n, [["__scopeId", "data-v-565d8abd"]]);
|
|
42562
|
+
const _hoisted_1$k = { class: "js-dialog-content-inner" };
|
|
42563
|
+
const _hoisted_2$j = { class: "js-dialog-content-inner-left" };
|
|
42564
|
+
const _hoisted_3$j = { class: "js-dialog-content-inner-right" };
|
|
42565
|
+
const _hoisted_4$h = { class: "grid-layout" };
|
|
42566
|
+
const _hoisted_5$e = ["onClick"];
|
|
42567
|
+
const _hoisted_6$d = {
|
|
42568
|
+
key: 0,
|
|
42569
|
+
class: "host-tag"
|
|
42570
|
+
};
|
|
42571
|
+
const _hoisted_7$c = ["id"];
|
|
42572
|
+
const _hoisted_8$b = { class: "avatar-container" };
|
|
42573
|
+
const _hoisted_9$9 = { class: "video-loading-overlay" };
|
|
42574
|
+
const _hoisted_10$9 = { class: "video-loading-text" };
|
|
42575
|
+
const _hoisted_11$8 = { class: "video-loading-overlay failed" };
|
|
42576
|
+
const _hoisted_12$8 = ["disabled", "onClick"];
|
|
42577
|
+
const _sfc_main$m = /* @__PURE__ */ defineComponent({
|
|
42578
|
+
__name: "meeting-content",
|
|
42579
|
+
props: {
|
|
42580
|
+
layout: { default: "grid-9" },
|
|
42581
|
+
isFullscreen: { type: Boolean },
|
|
42582
|
+
joinTime: { default: 0 },
|
|
42583
|
+
theme: { default: "dark" }
|
|
42584
|
+
},
|
|
42585
|
+
emits: ["layout-change", "toggle-fullscreen"],
|
|
42586
|
+
setup(__props, { emit: __emit }) {
|
|
42587
|
+
const speakingUserName = ref("");
|
|
42588
|
+
emitter.on("speakingUserName", (name2) => {
|
|
42589
|
+
speakingUserName.value = name2;
|
|
42590
|
+
});
|
|
42591
|
+
const props = __props;
|
|
42592
|
+
const userId = getUserId();
|
|
42593
|
+
const joinTimeObj = ref("");
|
|
42594
|
+
const speakerId = ref(null);
|
|
42595
|
+
const lastGridLayout = ref("grid-9");
|
|
42596
|
+
const getUserName2 = (user) => {
|
|
42597
|
+
var _a25, _b25, _c2;
|
|
42598
|
+
return ((_a25 = user == null ? void 0 : user.member) == null ? void 0 : _a25.memberId) == userId ? `(我)${(_b25 = user == null ? void 0 : user.member) == null ? void 0 : _b25.name}` : ((_c2 = user == null ? void 0 : user.member) == null ? void 0 : _c2.name) || "未知用户";
|
|
42599
|
+
};
|
|
42600
|
+
const layoutClass = computed(() => `layout-${props.layout}`);
|
|
42601
|
+
const allJoinedMembers = computed(() => {
|
|
42602
|
+
return members.value.filter((user) => (user == null ? void 0 : user.joinStaus) == "1");
|
|
42603
|
+
});
|
|
42604
|
+
const speakerUser = computed(() => {
|
|
42605
|
+
if (!speakerId.value) return allJoinedMembers.value[0];
|
|
42606
|
+
return allJoinedMembers.value.find(
|
|
42607
|
+
(user) => {
|
|
42608
|
+
var _a25;
|
|
42609
|
+
return ((_a25 = user == null ? void 0 : user.member) == null ? void 0 : _a25.memberId) === speakerId.value;
|
|
42610
|
+
}
|
|
42611
|
+
) || allJoinedMembers.value[0];
|
|
42612
|
+
});
|
|
42613
|
+
const preferredRemoteVideoFocusId = computed(() => {
|
|
42614
|
+
var _a25, _b25;
|
|
42615
|
+
if (props.layout !== "speaker") return null;
|
|
42616
|
+
const focusedUid = (_b25 = (_a25 = speakerUser.value) == null ? void 0 : _a25.member) == null ? void 0 : _b25.memberId;
|
|
42617
|
+
return focusedUid && focusedUid !== userId ? focusedUid : null;
|
|
42618
|
+
});
|
|
42619
|
+
const filteredMembers = computed(() => {
|
|
42620
|
+
return allJoinedMembers.value;
|
|
42621
|
+
});
|
|
42622
|
+
const setSpeaker = (user) => {
|
|
42623
|
+
var _a25;
|
|
42624
|
+
speakerId.value = (_a25 = user == null ? void 0 : user.member) == null ? void 0 : _a25.memberId;
|
|
42625
|
+
if (props.layout !== "speaker") {
|
|
42626
|
+
emit("layout-change", "speaker");
|
|
42627
|
+
}
|
|
42628
|
+
};
|
|
42629
|
+
const clearSpeaker = () => {
|
|
42630
|
+
if (props.layout === "speaker") {
|
|
42631
|
+
emit("layout-change", lastGridLayout.value);
|
|
42632
|
+
}
|
|
42633
|
+
};
|
|
42634
|
+
const handleRetryVideo = async (user) => {
|
|
42635
|
+
var _a25;
|
|
42636
|
+
const memberId = (_a25 = user == null ? void 0 : user.member) == null ? void 0 : _a25.memberId;
|
|
42637
|
+
if (!memberId || memberId === userId || (user == null ? void 0 : user.videoRetrying)) return;
|
|
42638
|
+
await retryRemoteVideoSubscription(memberId);
|
|
42639
|
+
};
|
|
42640
|
+
const handleFullscreenClick = () => {
|
|
42641
|
+
emit("toggle-fullscreen");
|
|
42642
|
+
};
|
|
42643
|
+
const emit = __emit;
|
|
42644
|
+
let timer = null;
|
|
42645
|
+
watch(
|
|
42646
|
+
() => props.layout,
|
|
42647
|
+
(layout) => {
|
|
42648
|
+
if (layout !== "speaker") {
|
|
42649
|
+
lastGridLayout.value = layout;
|
|
42650
|
+
}
|
|
42651
|
+
},
|
|
42652
|
+
{ immediate: true }
|
|
42653
|
+
);
|
|
42654
|
+
watch(
|
|
42655
|
+
() => props.joinTime,
|
|
42656
|
+
() => {
|
|
42657
|
+
if (timer) {
|
|
42658
|
+
clearInterval(timer);
|
|
42659
|
+
timer = null;
|
|
42660
|
+
}
|
|
42661
|
+
if (props.joinTime) {
|
|
42662
|
+
timer = setInterval(() => {
|
|
42663
|
+
const now = +/* @__PURE__ */ new Date();
|
|
42664
|
+
joinTimeObj.value = formatDuringObject((now - props.joinTime) / 1e3);
|
|
42665
|
+
}, 1e3);
|
|
42666
|
+
}
|
|
42667
|
+
},
|
|
42668
|
+
{
|
|
42669
|
+
immediate: true
|
|
42670
|
+
}
|
|
42671
|
+
);
|
|
42672
|
+
onMounted(async () => {
|
|
42673
|
+
await nextTick();
|
|
42674
|
+
setTimeout(() => {
|
|
42675
|
+
mountTracks();
|
|
42676
|
+
}, 100);
|
|
42677
|
+
});
|
|
42678
|
+
watch(
|
|
42679
|
+
() => members.value.length,
|
|
42680
|
+
async (len, prevLen) => {
|
|
42681
|
+
if (!len || len === prevLen) return;
|
|
42682
|
+
await nextTick();
|
|
42683
|
+
setTimeout(() => mountTracks(), 0);
|
|
42684
|
+
},
|
|
42685
|
+
{ flush: "post" }
|
|
42686
|
+
);
|
|
42687
|
+
watch(
|
|
42688
|
+
preferredRemoteVideoFocusId,
|
|
42689
|
+
(focusedUid) => {
|
|
42690
|
+
void setPreferredRemoteVideoFocus(focusedUid);
|
|
42691
|
+
},
|
|
42692
|
+
{ immediate: true }
|
|
42693
|
+
);
|
|
42694
|
+
onUnmounted(() => {
|
|
42695
|
+
void setPreferredRemoteVideoFocus(null);
|
|
42696
|
+
if (timer) {
|
|
42697
|
+
clearInterval(timer);
|
|
42698
|
+
timer = null;
|
|
42699
|
+
}
|
|
42700
|
+
});
|
|
42701
|
+
return (_ctx, _cache) => {
|
|
42702
|
+
return openBlock(), createElementBlock(Fragment, null, [
|
|
42703
|
+
createElementVNode("div", _hoisted_1$k, [
|
|
42704
|
+
createElementVNode("div", _hoisted_2$j, [
|
|
42705
|
+
createVNode(NetWorkStatus, { theme: _ctx.theme }, null, 8, ["theme"]),
|
|
42706
|
+
createElementVNode("span", null, "正在讲话:" + toDisplayString(speakingUserName.value || "无"), 1)
|
|
42707
|
+
]),
|
|
42708
|
+
createElementVNode("div", _hoisted_3$j, [
|
|
42709
|
+
createElementVNode("span", null, toDisplayString(joinTimeObj.value), 1),
|
|
42710
|
+
createVNode(SvgIcon, {
|
|
42711
|
+
name: _ctx.isFullscreen ? "NoFullscreenIcon" : "FullscreenIcon",
|
|
42712
|
+
class: "icon-inactive",
|
|
42713
|
+
onClick: handleFullscreenClick
|
|
42714
|
+
}, null, 8, ["name"])
|
|
42715
|
+
])
|
|
42257
42716
|
]),
|
|
42258
42717
|
createElementVNode("div", {
|
|
42259
42718
|
class: normalizeClass(["js-dialog-content", [layoutClass.value, { "is-fullscreen-layout": _ctx.isFullscreen }]])
|
|
@@ -44832,23 +45291,26 @@ const _sfc_main$a = /* @__PURE__ */ defineComponent({
|
|
|
44832
45291
|
await nextTick();
|
|
44833
45292
|
mountTracks();
|
|
44834
45293
|
};
|
|
44835
|
-
const
|
|
44836
|
-
|
|
44837
|
-
if (
|
|
44838
|
-
|
|
44839
|
-
|
|
44840
|
-
)
|
|
44841
|
-
|
|
44842
|
-
|
|
44843
|
-
|
|
44844
|
-
|
|
44845
|
-
|
|
44846
|
-
|
|
44847
|
-
|
|
44848
|
-
document.exitFullscreen()
|
|
44849
|
-
|
|
44850
|
-
});
|
|
45294
|
+
const fullscreenOperationBusy = ref(false);
|
|
45295
|
+
const handleFullscreen = async () => {
|
|
45296
|
+
if (fullscreenOperationBusy.value) return;
|
|
45297
|
+
fullscreenOperationBusy.value = true;
|
|
45298
|
+
try {
|
|
45299
|
+
if (!document.fullscreenElement) {
|
|
45300
|
+
const fullscreenRoot = document.querySelector(
|
|
45301
|
+
".js-dialog-overlay"
|
|
45302
|
+
);
|
|
45303
|
+
if (!fullscreenRoot) return;
|
|
45304
|
+
await fullscreenRoot.requestFullscreen();
|
|
45305
|
+
isFullscreen.value = true;
|
|
45306
|
+
} else {
|
|
45307
|
+
await document.exitFullscreen();
|
|
45308
|
+
isFullscreen.value = false;
|
|
44851
45309
|
}
|
|
45310
|
+
} catch {
|
|
45311
|
+
isFullscreen.value = !!document.fullscreenElement;
|
|
45312
|
+
} finally {
|
|
45313
|
+
fullscreenOperationBusy.value = false;
|
|
44852
45314
|
}
|
|
44853
45315
|
};
|
|
44854
45316
|
const handleInvite = (arr) => {
|
|
@@ -44934,6 +45396,7 @@ const _sfc_main$a = /* @__PURE__ */ defineComponent({
|
|
|
44934
45396
|
});
|
|
44935
45397
|
};
|
|
44936
45398
|
const leaveMeeting = () => {
|
|
45399
|
+
console.log(111111111111111);
|
|
44937
45400
|
getLoading().show();
|
|
44938
45401
|
const meetingInfo = JSON.parse(
|
|
44939
45402
|
sessionStorage.getItem("JS_MEETING_INFO") || "{}"
|
|
@@ -45077,7 +45540,7 @@ const _sfc_main$a = /* @__PURE__ */ defineComponent({
|
|
|
45077
45540
|
};
|
|
45078
45541
|
}
|
|
45079
45542
|
});
|
|
45080
|
-
const JSMeeting = /* @__PURE__ */ _export_sfc(_sfc_main$a, [["__scopeId", "data-v-
|
|
45543
|
+
const JSMeeting = /* @__PURE__ */ _export_sfc(_sfc_main$a, [["__scopeId", "data-v-9fde748a"]]);
|
|
45081
45544
|
const _hoisted_1$8 = { class: "modal" };
|
|
45082
45545
|
const _hoisted_2$8 = { class: "modal-header" };
|
|
45083
45546
|
const _hoisted_3$8 = { class: "tab-header" };
|
|
@@ -46984,333 +47447,39 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
46984
47447
|
style: { "color": "#2f86ff" }
|
|
46985
47448
|
})
|
|
46986
47449
|
]),
|
|
46987
|
-
createElementVNode("div", _hoisted_30, [
|
|
46988
|
-
withDirectives(createElementVNode("input", {
|
|
46989
|
-
"onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => microphoneVolume.value = $event),
|
|
46990
|
-
type: "range",
|
|
46991
|
-
min: "0",
|
|
46992
|
-
max: "100",
|
|
46993
|
-
class: "volume-slider",
|
|
46994
|
-
style: normalizeStyle(`background: linear-gradient(to right, #2f86ff ${microphoneVolume.value}%, rgba(255, 255, 255, 0.08) ${microphoneVolume.value}%)`)
|
|
46995
|
-
}, null, 4), [
|
|
46996
|
-
[vModelText, microphoneVolume.value]
|
|
46997
|
-
])
|
|
46998
|
-
])
|
|
46999
|
-
])
|
|
47000
|
-
])
|
|
47001
|
-
]))
|
|
47002
|
-
])
|
|
47003
|
-
])
|
|
47004
|
-
])), [
|
|
47005
|
-
[_directive_draggable, { handle: ".header" }]
|
|
47006
|
-
])
|
|
47007
|
-
], 2)) : createCommentVNode("", true);
|
|
47008
|
-
};
|
|
47009
|
-
}
|
|
47010
|
-
});
|
|
47011
|
-
const JSSettings = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-27234687"]]);
|
|
47012
|
-
function scheduleJoinRoom() {
|
|
47013
|
-
setTimeout(() => {
|
|
47014
|
-
try {
|
|
47015
|
-
joinRoom();
|
|
47016
|
-
} catch (error) {
|
|
47017
|
-
console.error("恢复后无法加入通话/会议", error);
|
|
47018
|
-
}
|
|
47019
|
-
}, 1e3);
|
|
47020
|
-
}
|
|
47021
|
-
const HANDOFF_SOURCE_TAB_ID_KEY = "JS_MEETING_MQTT_HANDOFF_SOURCE_TAB_ID";
|
|
47022
|
-
const HANDOFF_TARGET_TAB_ID_KEY = "JS_MEETING_MQTT_HANDOFF_TARGET_TAB_ID";
|
|
47023
|
-
const HANDOFF_ID_KEY = "JS_MEETING_MQTT_HANDOFF_ID";
|
|
47024
|
-
const RECONNECT_SIGNAL_KEY = "JS_MEETING_MQTT_RECONNECT_SIGNAL";
|
|
47025
|
-
const TARGET_HEARTBEAT_KEY = "JS_MEETING_MQTT_HANDOFF_HEARTBEAT";
|
|
47026
|
-
const TARGET_TAB_STATUS_KEY = "JS_MEETING_NEW_TAB_STATUS";
|
|
47027
|
-
const TARGET_HEARTBEAT_INTERVAL_MS = 1e3;
|
|
47028
|
-
const RECONNECT_CHECK_DELAY_MS = 1500;
|
|
47029
|
-
const TARGET_TAB_STATUS_CLOSED = 0;
|
|
47030
|
-
const TARGET_TAB_STATUS_OPEN = 1;
|
|
47031
|
-
let installed = false;
|
|
47032
|
-
let closeSignalSent = false;
|
|
47033
|
-
let heartbeatTimer = null;
|
|
47034
|
-
let lastHandledReconnectSignalTs = 0;
|
|
47035
|
-
let leaveRequestSent = false;
|
|
47036
|
-
function safeSessionStorageGetItem(key) {
|
|
47037
|
-
try {
|
|
47038
|
-
return sessionStorage.getItem(key);
|
|
47039
|
-
} catch {
|
|
47040
|
-
return null;
|
|
47041
|
-
}
|
|
47042
|
-
}
|
|
47043
|
-
function safeLocalStorageSetItem(key, value) {
|
|
47044
|
-
try {
|
|
47045
|
-
localStorage.setItem(key, value);
|
|
47046
|
-
} catch {
|
|
47047
|
-
}
|
|
47048
|
-
}
|
|
47049
|
-
function safeLocalStorageGetItem(key) {
|
|
47050
|
-
try {
|
|
47051
|
-
return localStorage.getItem(key);
|
|
47052
|
-
} catch {
|
|
47053
|
-
return null;
|
|
47054
|
-
}
|
|
47055
|
-
}
|
|
47056
|
-
function getMeetingMqttHandoffContext() {
|
|
47057
|
-
const sourceTabId = safeSessionStorageGetItem(HANDOFF_SOURCE_TAB_ID_KEY) || "";
|
|
47058
|
-
const targetTabId = safeSessionStorageGetItem(HANDOFF_TARGET_TAB_ID_KEY) || "";
|
|
47059
|
-
const handoffId = safeSessionStorageGetItem(HANDOFF_ID_KEY) || "";
|
|
47060
|
-
if (!sourceTabId || !targetTabId || !handoffId) {
|
|
47061
|
-
return null;
|
|
47062
|
-
}
|
|
47063
|
-
return {
|
|
47064
|
-
sourceTabId,
|
|
47065
|
-
targetTabId,
|
|
47066
|
-
handoffId
|
|
47067
|
-
};
|
|
47068
|
-
}
|
|
47069
|
-
function readTargetHeartbeat() {
|
|
47070
|
-
const raw = safeLocalStorageGetItem(TARGET_HEARTBEAT_KEY);
|
|
47071
|
-
if (!raw) {
|
|
47072
|
-
return null;
|
|
47073
|
-
}
|
|
47074
|
-
try {
|
|
47075
|
-
const parsed = JSON.parse(raw);
|
|
47076
|
-
if (!(parsed == null ? void 0 : parsed.targetTabId) || !(parsed == null ? void 0 : parsed.handoffId) || !(parsed == null ? void 0 : parsed.ts)) {
|
|
47077
|
-
return null;
|
|
47078
|
-
}
|
|
47079
|
-
return {
|
|
47080
|
-
targetTabId: parsed.targetTabId,
|
|
47081
|
-
handoffId: parsed.handoffId,
|
|
47082
|
-
ts: Number(parsed.ts)
|
|
47450
|
+
createElementVNode("div", _hoisted_30, [
|
|
47451
|
+
withDirectives(createElementVNode("input", {
|
|
47452
|
+
"onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => microphoneVolume.value = $event),
|
|
47453
|
+
type: "range",
|
|
47454
|
+
min: "0",
|
|
47455
|
+
max: "100",
|
|
47456
|
+
class: "volume-slider",
|
|
47457
|
+
style: normalizeStyle(`background: linear-gradient(to right, #2f86ff ${microphoneVolume.value}%, rgba(255, 255, 255, 0.08) ${microphoneVolume.value}%)`)
|
|
47458
|
+
}, null, 4), [
|
|
47459
|
+
[vModelText, microphoneVolume.value]
|
|
47460
|
+
])
|
|
47461
|
+
])
|
|
47462
|
+
])
|
|
47463
|
+
])
|
|
47464
|
+
]))
|
|
47465
|
+
])
|
|
47466
|
+
])
|
|
47467
|
+
])), [
|
|
47468
|
+
[_directive_draggable, { handle: ".header" }]
|
|
47469
|
+
])
|
|
47470
|
+
], 2)) : createCommentVNode("", true);
|
|
47083
47471
|
};
|
|
47084
|
-
} catch {
|
|
47085
|
-
return null;
|
|
47086
|
-
}
|
|
47087
|
-
}
|
|
47088
|
-
function writeTargetHeartbeat(context) {
|
|
47089
|
-
safeLocalStorageSetItem(
|
|
47090
|
-
TARGET_HEARTBEAT_KEY,
|
|
47091
|
-
JSON.stringify({
|
|
47092
|
-
targetTabId: context.targetTabId,
|
|
47093
|
-
handoffId: context.handoffId,
|
|
47094
|
-
ts: Date.now()
|
|
47095
|
-
})
|
|
47096
|
-
);
|
|
47097
|
-
}
|
|
47098
|
-
function writeTargetTabStatus(status, context) {
|
|
47099
|
-
safeLocalStorageSetItem(
|
|
47100
|
-
TARGET_TAB_STATUS_KEY,
|
|
47101
|
-
JSON.stringify({
|
|
47102
|
-
sourceTabId: context.sourceTabId,
|
|
47103
|
-
targetTabId: context.targetTabId,
|
|
47104
|
-
handoffId: context.handoffId,
|
|
47105
|
-
status,
|
|
47106
|
-
ts: Date.now()
|
|
47107
|
-
})
|
|
47108
|
-
);
|
|
47109
|
-
}
|
|
47110
|
-
function sendReconnectSignal(context) {
|
|
47111
|
-
if (closeSignalSent) {
|
|
47112
|
-
return;
|
|
47113
|
-
}
|
|
47114
|
-
closeSignalSent = true;
|
|
47115
|
-
console.warn("[MQTT_HANDOFF] 目标页关闭,通知来源页恢复 MQTT", {
|
|
47116
|
-
sourceTabId: context.sourceTabId,
|
|
47117
|
-
targetTabId: context.targetTabId,
|
|
47118
|
-
handoffId: context.handoffId
|
|
47119
|
-
});
|
|
47120
|
-
safeLocalStorageSetItem(
|
|
47121
|
-
RECONNECT_SIGNAL_KEY,
|
|
47122
|
-
JSON.stringify({
|
|
47123
|
-
...context,
|
|
47124
|
-
ts: Date.now()
|
|
47125
|
-
})
|
|
47126
|
-
);
|
|
47127
|
-
}
|
|
47128
|
-
function stopTargetHeartbeat() {
|
|
47129
|
-
if (heartbeatTimer) {
|
|
47130
|
-
clearInterval(heartbeatTimer);
|
|
47131
|
-
heartbeatTimer = null;
|
|
47132
|
-
}
|
|
47133
|
-
}
|
|
47134
|
-
function startTargetHeartbeat() {
|
|
47135
|
-
const context = getMeetingMqttHandoffContext();
|
|
47136
|
-
if (!context) {
|
|
47137
|
-
return;
|
|
47138
|
-
}
|
|
47139
|
-
const currentTabId = getCurrentMeetingTabId();
|
|
47140
|
-
if (currentTabId !== context.targetTabId) {
|
|
47141
|
-
return;
|
|
47142
|
-
}
|
|
47143
|
-
closeSignalSent = false;
|
|
47144
|
-
console.info("[MQTT_HANDOFF] 目标页心跳已启动", {
|
|
47145
|
-
sourceTabId: context.sourceTabId,
|
|
47146
|
-
targetTabId: context.targetTabId,
|
|
47147
|
-
handoffId: context.handoffId
|
|
47148
|
-
});
|
|
47149
|
-
writeTargetTabStatus(TARGET_TAB_STATUS_OPEN, context);
|
|
47150
|
-
writeTargetHeartbeat(context);
|
|
47151
|
-
stopTargetHeartbeat();
|
|
47152
|
-
heartbeatTimer = setInterval(() => {
|
|
47153
|
-
writeTargetHeartbeat(context);
|
|
47154
|
-
}, TARGET_HEARTBEAT_INTERVAL_MS);
|
|
47155
|
-
}
|
|
47156
|
-
function notifySourceTabForReconnect() {
|
|
47157
|
-
const context = getMeetingMqttHandoffContext();
|
|
47158
|
-
if (!context) {
|
|
47159
|
-
return;
|
|
47160
|
-
}
|
|
47161
|
-
const currentTabId = getCurrentMeetingTabId();
|
|
47162
|
-
if (currentTabId !== context.targetTabId) {
|
|
47163
|
-
return;
|
|
47164
|
-
}
|
|
47165
|
-
sendReconnectSignal(context);
|
|
47166
|
-
}
|
|
47167
|
-
function shouldWarnBeforeUnload() {
|
|
47168
|
-
const context = getMeetingMqttHandoffContext();
|
|
47169
|
-
if (!context) {
|
|
47170
|
-
return false;
|
|
47171
|
-
}
|
|
47172
|
-
const currentTabId = getCurrentMeetingTabId();
|
|
47173
|
-
return currentTabId === context.targetTabId;
|
|
47174
|
-
}
|
|
47175
|
-
function buildMeetingLeavePayload() {
|
|
47176
|
-
try {
|
|
47177
|
-
const meetingInfo = JSON.parse(sessionStorage.getItem("JS_MEETING_INFO") || "{}");
|
|
47178
|
-
const meetingId = String((meetingInfo == null ? void 0 : meetingInfo.meetingId) || "").trim();
|
|
47179
|
-
const memberId = String(getUserId() || "").trim();
|
|
47180
|
-
if (!meetingId || !memberId) {
|
|
47181
|
-
return null;
|
|
47182
|
-
}
|
|
47183
|
-
return { meetingId, memberId, force: true };
|
|
47184
|
-
} catch {
|
|
47185
|
-
return null;
|
|
47186
|
-
}
|
|
47187
|
-
}
|
|
47188
|
-
function sendMeetingLeaveOnClose() {
|
|
47189
|
-
if (leaveRequestSent) {
|
|
47190
|
-
return;
|
|
47191
47472
|
}
|
|
47192
|
-
|
|
47193
|
-
|
|
47194
|
-
|
|
47195
|
-
|
|
47196
|
-
leaveRequestSent = true;
|
|
47197
|
-
const body = JSON.stringify(payload);
|
|
47198
|
-
const token = getSdkToken();
|
|
47199
|
-
const authHeader = token ? { Authorization: `Bearer ${token}` } : {};
|
|
47200
|
-
const requestUrl = "/meeting/api/v2/meeting/leave";
|
|
47201
|
-
try {
|
|
47202
|
-
fetch(requestUrl, {
|
|
47203
|
-
method: "POST",
|
|
47204
|
-
headers: {
|
|
47205
|
-
"Content-Type": "application/json",
|
|
47206
|
-
Accept: "application/json, text/plain, */*",
|
|
47207
|
-
"X-Requested-With": "XMLHttpRequest",
|
|
47208
|
-
...authHeader
|
|
47209
|
-
},
|
|
47210
|
-
body,
|
|
47211
|
-
keepalive: true,
|
|
47212
|
-
credentials: "same-origin"
|
|
47213
|
-
}).catch(() => {
|
|
47214
|
-
});
|
|
47215
|
-
console.warn("[MQTT_HANDOFF] 页面关闭触发离会请求(keepalive)", payload);
|
|
47216
|
-
} catch {
|
|
47217
|
-
}
|
|
47218
|
-
try {
|
|
47219
|
-
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
47220
|
-
const beaconData = new Blob([body], { type: "application/json" });
|
|
47221
|
-
const sent = navigator.sendBeacon(requestUrl, beaconData);
|
|
47222
|
-
console.warn("[MQTT_HANDOFF] 页面关闭触发离会请求(beacon)", {
|
|
47223
|
-
...payload,
|
|
47224
|
-
sent
|
|
47225
|
-
});
|
|
47226
|
-
}
|
|
47227
|
-
} catch {
|
|
47228
|
-
}
|
|
47229
|
-
}
|
|
47230
|
-
function shouldReconnectForSignal(signal) {
|
|
47231
|
-
const currentTabId = getCurrentMeetingTabId();
|
|
47232
|
-
if (!signal.sourceTabId || signal.sourceTabId !== currentTabId) {
|
|
47233
|
-
return false;
|
|
47234
|
-
}
|
|
47235
|
-
const latestHeartbeat = readTargetHeartbeat();
|
|
47236
|
-
if (!latestHeartbeat) {
|
|
47237
|
-
return true;
|
|
47238
|
-
}
|
|
47239
|
-
if (latestHeartbeat.targetTabId !== signal.targetTabId) {
|
|
47240
|
-
return true;
|
|
47241
|
-
}
|
|
47242
|
-
if (latestHeartbeat.handoffId !== signal.handoffId) {
|
|
47243
|
-
return true;
|
|
47244
|
-
}
|
|
47245
|
-
return latestHeartbeat.ts <= signal.ts;
|
|
47246
|
-
}
|
|
47247
|
-
function installMeetingMqttHandoffBridge(onReconnectRequested) {
|
|
47248
|
-
if (installed || typeof window === "undefined") {
|
|
47249
|
-
return;
|
|
47250
|
-
}
|
|
47251
|
-
installed = true;
|
|
47252
|
-
startTargetHeartbeat();
|
|
47253
|
-
window.addEventListener("storage", (event) => {
|
|
47254
|
-
if (event.key !== RECONNECT_SIGNAL_KEY || !event.newValue) {
|
|
47255
|
-
return;
|
|
47256
|
-
}
|
|
47257
|
-
let signal = null;
|
|
47473
|
+
});
|
|
47474
|
+
const JSSettings = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-27234687"]]);
|
|
47475
|
+
function scheduleJoinRoom() {
|
|
47476
|
+
setTimeout(() => {
|
|
47258
47477
|
try {
|
|
47259
|
-
|
|
47260
|
-
} catch {
|
|
47261
|
-
|
|
47262
|
-
}
|
|
47263
|
-
if (!(signal == null ? void 0 : signal.sourceTabId) || !signal.targetTabId || !signal.handoffId) {
|
|
47264
|
-
return;
|
|
47265
|
-
}
|
|
47266
|
-
const signalTs = Number(signal.ts);
|
|
47267
|
-
if (!signalTs || signalTs <= lastHandledReconnectSignalTs) {
|
|
47268
|
-
return;
|
|
47269
|
-
}
|
|
47270
|
-
lastHandledReconnectSignalTs = signalTs;
|
|
47271
|
-
console.warn("[MQTT_HANDOFF] 收到 MQTT 恢复信号,等待确认目标页是否真的关闭", {
|
|
47272
|
-
sourceTabId: signal.sourceTabId,
|
|
47273
|
-
targetTabId: signal.targetTabId,
|
|
47274
|
-
handoffId: signal.handoffId,
|
|
47275
|
-
signalTs
|
|
47276
|
-
});
|
|
47277
|
-
window.setTimeout(() => {
|
|
47278
|
-
if (!shouldReconnectForSignal(signal)) {
|
|
47279
|
-
console.info("[MQTT_HANDOFF] 忽略 MQTT 恢复,本次更像是目标页刷新/重载", {
|
|
47280
|
-
sourceTabId: signal.sourceTabId,
|
|
47281
|
-
targetTabId: signal.targetTabId,
|
|
47282
|
-
handoffId: signal.handoffId
|
|
47283
|
-
});
|
|
47284
|
-
return;
|
|
47285
|
-
}
|
|
47286
|
-
console.warn("[MQTT_HANDOFF] 确认目标页已关闭,准备恢复来源页 MQTT", {
|
|
47287
|
-
sourceTabId: signal.sourceTabId,
|
|
47288
|
-
targetTabId: signal.targetTabId,
|
|
47289
|
-
handoffId: signal.handoffId
|
|
47290
|
-
});
|
|
47291
|
-
void onReconnectRequested();
|
|
47292
|
-
}, RECONNECT_CHECK_DELAY_MS);
|
|
47293
|
-
});
|
|
47294
|
-
window.addEventListener("beforeunload", (event) => {
|
|
47295
|
-
if (!shouldWarnBeforeUnload()) {
|
|
47296
|
-
return;
|
|
47297
|
-
}
|
|
47298
|
-
event.preventDefault();
|
|
47299
|
-
event.returnValue = "";
|
|
47300
|
-
});
|
|
47301
|
-
window.addEventListener("pagehide", (event) => {
|
|
47302
|
-
const context = getMeetingMqttHandoffContext();
|
|
47303
|
-
const shouldHandleTargetTabClose = !!context && getCurrentMeetingTabId() === context.targetTabId;
|
|
47304
|
-
if (shouldHandleTargetTabClose && context) {
|
|
47305
|
-
writeTargetTabStatus(TARGET_TAB_STATUS_CLOSED, context);
|
|
47306
|
-
sendMeetingLeaveOnClose();
|
|
47307
|
-
}
|
|
47308
|
-
stopTargetHeartbeat();
|
|
47309
|
-
if (event.persisted) {
|
|
47310
|
-
return;
|
|
47478
|
+
joinRoom();
|
|
47479
|
+
} catch (error) {
|
|
47480
|
+
console.error("恢复后无法加入通话/会议", error);
|
|
47311
47481
|
}
|
|
47312
|
-
|
|
47313
|
-
});
|
|
47482
|
+
}, 1e3);
|
|
47314
47483
|
}
|
|
47315
47484
|
let authToken = null;
|
|
47316
47485
|
async function restoreMeetingMqttConnection() {
|