jssz-meeting-component 1.1.41 → 1.2.0

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 CHANGED
@@ -20374,6 +20374,31 @@ function normalizeRoleCodes(value) {
20374
20374
  }
20375
20375
  return text.split(",").map((item) => item.trim()).filter(Boolean);
20376
20376
  }
20377
+ function normalizeBrokerUrl(baseUrl) {
20378
+ const trimmed = toText(baseUrl).trim();
20379
+ if (!trimmed) {
20380
+ throw new Error("MQTT 初始化失败:mqttUrl 为空");
20381
+ }
20382
+ const normalizedScheme = trimmed.startsWith("tcp://") ? `mqtt://${trimmed.slice("tcp://".length)}` : trimmed;
20383
+ console.log("normalizedScheme:", normalizedScheme);
20384
+ const withoutTrailingSlash = normalizedScheme.replace(/\/+$/g, "");
20385
+ if (withoutTrailingSlash.endsWith(DEFAULT_MQTT_PATH)) {
20386
+ return withoutTrailingSlash;
20387
+ }
20388
+ return `${withoutTrailingSlash}${DEFAULT_MQTT_PATH}`;
20389
+ }
20390
+ async function getMeetingWsMqttUrl() {
20391
+ const response = await getMqttConfig();
20392
+ if (response.code !== 200 || !response.data) {
20393
+ throw new Error(response.message || "获取 MQTT 配置失败");
20394
+ }
20395
+ const data = response.data;
20396
+ const wsMqttUrl = toText(data.wsMqttUrl).trim();
20397
+ if (wsMqttUrl) {
20398
+ return wsMqttUrl;
20399
+ }
20400
+ throw new Error("获取 MQTT 配置失败:wsMqttUrl 为空");
20401
+ }
20377
20402
  function buildRequestId() {
20378
20403
  return `mqtt-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
20379
20404
  }
@@ -20482,7 +20507,9 @@ async function initializeMeetingMQTT(options = {}) {
20482
20507
  const myInfo = options.myInfo || await readMyInfo();
20483
20508
  const loginDevice = toText(myInfo.currentDevice).trim();
20484
20509
  const roleCodes = normalizeRoleCodes(myInfo.roleCodes);
20485
- const brokerUrl = "wss://testdcs.jingshuosz.cn" + DEFAULT_MQTT_PATH;
20510
+ const mqttUrl = toText(options.mqttUrl).trim() || await getMeetingWsMqttUrl();
20511
+ const brokerUrl = normalizeBrokerUrl(mqttUrl);
20512
+ console.log("brokerUrl:", brokerUrl);
20486
20513
  const responseTopic = buildResponseTopic(loginDevice);
20487
20514
  const nextRuntime = {
20488
20515
  brokerUrl,
@@ -20608,6 +20635,9 @@ const getUploadFile = (data) => {
20608
20635
  const getMyInfo = () => {
20609
20636
  return http.request("get", "/meeting/api/v2/contact/myInfo");
20610
20637
  };
20638
+ const getMqttConfig = () => {
20639
+ return http.request("get", "/meeting/config/detail");
20640
+ };
20611
20641
  const getCreateMeeting = (data) => {
20612
20642
  return http.request(
20613
20643
  "post",
@@ -40531,7 +40561,7 @@ const _sfc_main$s = /* @__PURE__ */ defineComponent({
40531
40561
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => searchKeyword.value = $event),
40532
40562
  type: "text",
40533
40563
  placeholder: "搜索用户",
40534
- class: "search-input",
40564
+ class: "js-search-input",
40535
40565
  onMousedown: _cache[1] || (_cache[1] = withModifiers(() => {
40536
40566
  }, ["stop"])),
40537
40567
  onClick: _cache[2] || (_cache[2] = withModifiers(() => {
@@ -40674,7 +40704,7 @@ const _sfc_main$s = /* @__PURE__ */ defineComponent({
40674
40704
  };
40675
40705
  }
40676
40706
  });
40677
- const JSUser = /* @__PURE__ */ _export_sfc(_sfc_main$s, [["__scopeId", "data-v-23a8a167"]]);
40707
+ const JSUser = /* @__PURE__ */ _export_sfc(_sfc_main$s, [["__scopeId", "data-v-3cb577c5"]]);
40678
40708
  var RoomModalSelectType = /* @__PURE__ */ ((RoomModalSelectType2) => {
40679
40709
  RoomModalSelectType2["microphoneClick"] = "microphoneClick";
40680
40710
  RoomModalSelectType2["cameraClick"] = "cameraClick";
@@ -41448,405 +41478,96 @@ const _sfc_main$q = /* @__PURE__ */ defineComponent({
41448
41478
  }
41449
41479
  });
41450
41480
  const JSCall = /* @__PURE__ */ _export_sfc(_sfc_main$q, [["__scopeId", "data-v-e1585009"]]);
41451
- const HANDOFF_SOURCE_TAB_ID_KEY = "JS_MEETING_MQTT_HANDOFF_SOURCE_TAB_ID";
41452
- const HANDOFF_TARGET_TAB_ID_KEY = "JS_MEETING_MQTT_HANDOFF_TARGET_TAB_ID";
41453
- const HANDOFF_ID_KEY = "JS_MEETING_MQTT_HANDOFF_ID";
41454
- const RECONNECT_SIGNAL_KEY = "JS_MEETING_MQTT_RECONNECT_SIGNAL";
41455
- const TARGET_HEARTBEAT_KEY = "JS_MEETING_MQTT_HANDOFF_HEARTBEAT";
41456
- const TARGET_TAB_STATUS_KEY = "JS_MEETING_NEW_TAB_STATUS";
41457
- const TARGET_HEARTBEAT_INTERVAL_MS = 1e3;
41458
- const RECONNECT_CHECK_DELAY_MS = 1500;
41459
- const TARGET_TAB_STATUS_CLOSED = 0;
41460
- const TARGET_TAB_STATUS_OPEN = 1;
41461
- let installed = false;
41462
- let closeSignalSent = false;
41463
- let heartbeatTimer = null;
41464
- let lastHandledReconnectSignalTs = 0;
41465
- let leaveRequestSent = false;
41466
- function safeSessionStorageGetItem(key) {
41467
- try {
41468
- return sessionStorage.getItem(key);
41469
- } catch {
41470
- return null;
41471
- }
41472
- }
41473
- function safeLocalStorageSetItem(key, value) {
41474
- try {
41475
- localStorage.setItem(key, value);
41476
- } catch {
41477
- }
41478
- }
41479
- function safeLocalStorageGetItem(key) {
41480
- try {
41481
- return localStorage.getItem(key);
41482
- } catch {
41483
- return null;
41484
- }
41485
- }
41486
- function getMeetingMqttHandoffContext() {
41487
- const sourceTabId = safeSessionStorageGetItem(HANDOFF_SOURCE_TAB_ID_KEY) || "";
41488
- const targetTabId = safeSessionStorageGetItem(HANDOFF_TARGET_TAB_ID_KEY) || "";
41489
- const handoffId = safeSessionStorageGetItem(HANDOFF_ID_KEY) || "";
41490
- if (!sourceTabId || !targetTabId || !handoffId) {
41491
- return null;
41492
- }
41493
- return {
41494
- sourceTabId,
41495
- targetTabId,
41496
- handoffId
41497
- };
41498
- }
41499
- function readTargetHeartbeat() {
41500
- const raw = safeLocalStorageGetItem(TARGET_HEARTBEAT_KEY);
41501
- if (!raw) {
41502
- return null;
41503
- }
41504
- try {
41505
- const parsed = JSON.parse(raw);
41506
- if (!(parsed == null ? void 0 : parsed.targetTabId) || !(parsed == null ? void 0 : parsed.handoffId) || !(parsed == null ? void 0 : parsed.ts)) {
41507
- return null;
41508
- }
41509
- return {
41510
- targetTabId: parsed.targetTabId,
41511
- handoffId: parsed.handoffId,
41512
- ts: Number(parsed.ts)
41513
- };
41514
- } catch {
41515
- return null;
41516
- }
41517
- }
41518
- function writeTargetHeartbeat(context) {
41519
- safeLocalStorageSetItem(
41520
- TARGET_HEARTBEAT_KEY,
41521
- JSON.stringify({
41522
- targetTabId: context.targetTabId,
41523
- handoffId: context.handoffId,
41524
- ts: Date.now()
41525
- })
41526
- );
41527
- }
41528
- function writeTargetTabStatus(status, context) {
41529
- safeLocalStorageSetItem(
41530
- TARGET_TAB_STATUS_KEY,
41531
- JSON.stringify({
41532
- sourceTabId: context.sourceTabId,
41533
- targetTabId: context.targetTabId,
41534
- handoffId: context.handoffId,
41535
- status,
41536
- ts: Date.now()
41537
- })
41538
- );
41539
- }
41540
- function sendReconnectSignal(context) {
41541
- if (closeSignalSent) {
41542
- return;
41543
- }
41544
- closeSignalSent = true;
41545
- console.warn("[MQTT_HANDOFF] 目标页关闭,通知来源页恢复 MQTT", {
41546
- sourceTabId: context.sourceTabId,
41547
- targetTabId: context.targetTabId,
41548
- handoffId: context.handoffId
41549
- });
41550
- safeLocalStorageSetItem(
41551
- RECONNECT_SIGNAL_KEY,
41552
- JSON.stringify({
41553
- ...context,
41554
- ts: Date.now()
41555
- })
41556
- );
41557
- }
41558
- function stopTargetHeartbeat() {
41559
- if (heartbeatTimer) {
41560
- clearInterval(heartbeatTimer);
41561
- heartbeatTimer = null;
41562
- }
41563
- }
41564
- function startTargetHeartbeat() {
41565
- const context = getMeetingMqttHandoffContext();
41566
- if (!context) {
41567
- return;
41568
- }
41569
- const currentTabId = getCurrentMeetingTabId();
41570
- if (currentTabId !== context.targetTabId) {
41571
- return;
41572
- }
41573
- closeSignalSent = false;
41574
- console.info("[MQTT_HANDOFF] 目标页心跳已启动", {
41575
- sourceTabId: context.sourceTabId,
41576
- targetTabId: context.targetTabId,
41577
- handoffId: context.handoffId
41578
- });
41579
- writeTargetTabStatus(TARGET_TAB_STATUS_OPEN, context);
41580
- writeTargetHeartbeat(context);
41581
- stopTargetHeartbeat();
41582
- heartbeatTimer = setInterval(() => {
41583
- writeTargetHeartbeat(context);
41584
- }, TARGET_HEARTBEAT_INTERVAL_MS);
41585
- }
41586
- function notifySourceTabForReconnect() {
41587
- const context = getMeetingMqttHandoffContext();
41588
- if (!context) {
41589
- return;
41590
- }
41591
- const currentTabId = getCurrentMeetingTabId();
41592
- if (currentTabId !== context.targetTabId) {
41593
- return;
41594
- }
41595
- sendReconnectSignal(context);
41596
- }
41597
- function shouldWarnBeforeUnload() {
41598
- const context = getMeetingMqttHandoffContext();
41599
- if (!context) {
41600
- return false;
41601
- }
41602
- const currentTabId = getCurrentMeetingTabId();
41603
- return currentTabId === context.targetTabId;
41604
- }
41605
- function buildMeetingLeavePayload() {
41606
- try {
41607
- const meetingInfo = JSON.parse(sessionStorage.getItem("JS_MEETING_INFO") || "{}");
41608
- const meetingId = String((meetingInfo == null ? void 0 : meetingInfo.meetingId) || "").trim();
41609
- const memberId = String(getUserId() || "").trim();
41610
- if (!meetingId || !memberId) {
41611
- return null;
41612
- }
41613
- return { meetingId, memberId, force: true };
41614
- } catch {
41615
- return null;
41616
- }
41617
- }
41618
- function sendMeetingLeaveOnClose() {
41619
- if (leaveRequestSent) {
41620
- return;
41621
- }
41622
- const payload = buildMeetingLeavePayload();
41623
- if (!payload) {
41624
- return;
41625
- }
41626
- leaveRequestSent = true;
41627
- const body = JSON.stringify(payload);
41628
- const token = getSdkToken();
41629
- const authHeader = token ? { Authorization: `Bearer ${token}` } : {};
41630
- const requestUrl = "/meeting/api/v2/meeting/leave";
41631
- try {
41632
- fetch(requestUrl, {
41633
- method: "POST",
41634
- headers: {
41635
- "Content-Type": "application/json",
41636
- Accept: "application/json, text/plain, */*",
41637
- "X-Requested-With": "XMLHttpRequest",
41638
- ...authHeader
41639
- },
41640
- body,
41641
- keepalive: true,
41642
- credentials: "same-origin"
41643
- }).catch(() => {
41644
- });
41645
- console.warn("[MQTT_HANDOFF] 页面关闭触发离会请求(keepalive)", payload);
41646
- } catch {
41647
- }
41648
- try {
41649
- if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
41650
- const beaconData = new Blob([body], { type: "application/json" });
41651
- const sent = navigator.sendBeacon(requestUrl, beaconData);
41652
- console.warn("[MQTT_HANDOFF] 页面关闭触发离会请求(beacon)", {
41653
- ...payload,
41654
- sent
41655
- });
41656
- }
41657
- } catch {
41658
- }
41659
- }
41660
- function shouldReconnectForSignal(signal) {
41661
- const currentTabId = getCurrentMeetingTabId();
41662
- if (!signal.sourceTabId || signal.sourceTabId !== currentTabId) {
41663
- return false;
41664
- }
41665
- const latestHeartbeat = readTargetHeartbeat();
41666
- if (!latestHeartbeat) {
41667
- return true;
41668
- }
41669
- if (latestHeartbeat.targetTabId !== signal.targetTabId) {
41670
- return true;
41671
- }
41672
- if (latestHeartbeat.handoffId !== signal.handoffId) {
41673
- return true;
41674
- }
41675
- return latestHeartbeat.ts <= signal.ts;
41676
- }
41677
- function installMeetingMqttHandoffBridge(onReconnectRequested) {
41678
- if (installed || typeof window === "undefined") {
41679
- return;
41680
- }
41681
- installed = true;
41682
- startTargetHeartbeat();
41683
- window.addEventListener("storage", (event) => {
41684
- if (event.key !== RECONNECT_SIGNAL_KEY || !event.newValue) {
41685
- return;
41686
- }
41687
- let signal = null;
41688
- try {
41689
- signal = JSON.parse(event.newValue);
41690
- } catch {
41691
- return;
41692
- }
41693
- if (!(signal == null ? void 0 : signal.sourceTabId) || !signal.targetTabId || !signal.handoffId) {
41694
- return;
41695
- }
41696
- const signalTs = Number(signal.ts);
41697
- if (!signalTs || signalTs <= lastHandledReconnectSignalTs) {
41698
- return;
41699
- }
41700
- lastHandledReconnectSignalTs = signalTs;
41701
- console.warn("[MQTT_HANDOFF] 收到 MQTT 恢复信号,等待确认目标页是否真的关闭", {
41702
- sourceTabId: signal.sourceTabId,
41703
- targetTabId: signal.targetTabId,
41704
- handoffId: signal.handoffId,
41705
- signalTs
41706
- });
41707
- window.setTimeout(() => {
41708
- if (!shouldReconnectForSignal(signal)) {
41709
- console.info("[MQTT_HANDOFF] 忽略 MQTT 恢复,本次更像是目标页刷新/重载", {
41710
- sourceTabId: signal.sourceTabId,
41711
- targetTabId: signal.targetTabId,
41712
- handoffId: signal.handoffId
41713
- });
41714
- return;
41715
- }
41716
- console.warn("[MQTT_HANDOFF] 确认目标页已关闭,准备恢复来源页 MQTT", {
41717
- sourceTabId: signal.sourceTabId,
41718
- targetTabId: signal.targetTabId,
41719
- handoffId: signal.handoffId
41720
- });
41721
- void onReconnectRequested();
41722
- }, RECONNECT_CHECK_DELAY_MS);
41723
- });
41724
- window.addEventListener("beforeunload", (event) => {
41725
- if (!shouldWarnBeforeUnload()) {
41726
- return;
41727
- }
41728
- event.preventDefault();
41729
- event.returnValue = "";
41730
- });
41731
- window.addEventListener("pagehide", (event) => {
41732
- const context = getMeetingMqttHandoffContext();
41733
- const shouldHandleTargetTabClose = !!context && getCurrentMeetingTabId() === context.targetTabId;
41734
- if (shouldHandleTargetTabClose && context) {
41735
- writeTargetTabStatus(TARGET_TAB_STATUS_CLOSED, context);
41736
- sendMeetingLeaveOnClose();
41737
- }
41738
- stopTargetHeartbeat();
41739
- if (event.persisted) {
41740
- return;
41741
- }
41742
- notifySourceTabForReconnect();
41743
- });
41744
- }
41745
- const _hoisted_1$n = { class: "js-dialog-header-title" };
41746
- const _hoisted_2$l = { class: "js-dialog-header-actions" };
41747
- const _sfc_main$p = /* @__PURE__ */ defineComponent({
41748
- __name: "meeting-header",
41749
- props: {
41750
- theme: {},
41751
- isMaximized: { type: Boolean }
41752
- },
41753
- emits: ["onChange"],
41754
- setup(__props, { emit: __emit }) {
41755
- const props = __props;
41756
- const { meState: meState2 } = storeToRefs(useMeetingStore());
41757
- const meetingInfo = computed(() => {
41758
- return JSON.parse(sessionStorage.getItem("JS_MEETING_INFO") || "{}");
41759
- });
41760
- const emit = __emit;
41761
- const closeRef = ref(null);
41762
- const showCloseConfirm = ref(false);
41763
- const themeClass = computed(() => `theme-${props.theme || "dark"}`);
41764
- const hasOpenedNewTab = ref(false);
41765
- const getNewTabFlagFromLocation = () => {
41766
- if (typeof window === "undefined") return false;
41767
- const searchParams = new URLSearchParams(window.location.search || "");
41768
- if (searchParams.get("newTab") === "true") return true;
41769
- const hash = window.location.hash || "";
41770
- const hashQueryIndex = hash.indexOf("?");
41771
- if (hashQueryIndex === -1) return false;
41772
- const hashQuery = hash.slice(hashQueryIndex + 1);
41773
- const hashParams = new URLSearchParams(hashQuery);
41774
- return hashParams.get("newTab") === "true";
41775
- };
41776
- computed(() => {
41777
- if (hasOpenedNewTab.value) return false;
41778
- return !getNewTabFlagFromLocation();
41779
- });
41780
- const handleCloseClick = async () => {
41781
- showCloseConfirm.value = !showCloseConfirm.value;
41782
- };
41783
- const handleEndMeeting = () => {
41784
- showCloseConfirm.value = false;
41785
- emit("onChange", RoomModalSelectType.endClick);
41786
- };
41787
- const handleLeaveMeeting = () => {
41788
- showCloseConfirm.value = false;
41789
- emit("onChange", RoomModalSelectType.leaveClick);
41790
- };
41791
- return (_ctx, _cache) => {
41792
- return openBlock(), createElementBlock("div", {
41793
- class: normalizeClass(["js-dialog-header", themeClass.value])
41794
- }, [
41795
- createElementVNode("div", _hoisted_1$n, toDisplayString(meetingInfo.value.meetingName), 1),
41796
- createElementVNode("div", _hoisted_2$l, [
41797
- createElementVNode("div", {
41798
- title: "最小化",
41799
- onClick: _cache[0] || (_cache[0] = ($event) => emit("onChange", unref(RoomModalSelectType).minimizeClick))
41800
- }, [
41801
- createVNode(SvgIcon, {
41802
- name: "MinimizeIcon",
41803
- size: 16
41804
- })
41805
- ]),
41806
- createElementVNode("div", {
41807
- ref_key: "closeRef",
41808
- ref: closeRef,
41809
- title: "关闭",
41810
- onClick: handleCloseClick
41811
- }, [
41812
- createVNode(SvgIcon, {
41813
- name: "CloseIcon",
41814
- size: 17
41815
- })
41816
- ], 512)
41817
- ]),
41818
- createVNode(SmartPopup, {
41819
- visible: showCloseConfirm.value,
41820
- "onUpdate:visible": _cache[2] || (_cache[2] = ($event) => showCloseConfirm.value = $event),
41821
- trigger: closeRef.value,
41822
- gap: 10
41823
- }, {
41824
- default: withCtx(() => [
41825
- createElementVNode("div", {
41826
- class: normalizeClass(["close-confirm-dropdown", themeClass.value])
41827
- }, [
41828
- unref(meState2).roleType == "1" ? (openBlock(), createElementBlock("div", {
41829
- key: 0,
41830
- class: "close-confirm-option danger",
41831
- onClick: withModifiers(handleEndMeeting, ["stop"])
41832
- }, " 结束会议 ")) : createCommentVNode("", true),
41833
- createElementVNode("div", {
41834
- class: "close-confirm-option simple",
41835
- onClick: withModifiers(handleLeaveMeeting, ["stop"])
41836
- }, " 离开会议 "),
41837
- createElementVNode("div", {
41838
- class: "close-confirm-option simple",
41839
- onClick: _cache[1] || (_cache[1] = ($event) => showCloseConfirm.value = false)
41840
- }, " 取消 ")
41841
- ], 2)
41842
- ]),
41843
- _: 1
41844
- }, 8, ["visible", "trigger"])
41845
- ], 2);
41481
+ const _hoisted_1$n = { class: "js-dialog-header-title" };
41482
+ const _hoisted_2$l = { class: "js-dialog-header-actions" };
41483
+ const _sfc_main$p = /* @__PURE__ */ defineComponent({
41484
+ __name: "meeting-header",
41485
+ props: {
41486
+ theme: {},
41487
+ isMaximized: { type: Boolean }
41488
+ },
41489
+ emits: ["onChange"],
41490
+ setup(__props, { emit: __emit }) {
41491
+ const props = __props;
41492
+ const { meState: meState2 } = storeToRefs(useMeetingStore());
41493
+ const meetingInfo = computed(() => {
41494
+ return JSON.parse(sessionStorage.getItem("JS_MEETING_INFO") || "{}");
41495
+ });
41496
+ const emit = __emit;
41497
+ const closeRef = ref(null);
41498
+ const showCloseConfirm = ref(false);
41499
+ const themeClass = computed(() => `theme-${props.theme || "dark"}`);
41500
+ ref(false);
41501
+ const handleCloseClick = async () => {
41502
+ showCloseConfirm.value = !showCloseConfirm.value;
41503
+ };
41504
+ const handleEndMeeting = () => {
41505
+ showCloseConfirm.value = false;
41506
+ emit("onChange", RoomModalSelectType.endClick);
41507
+ };
41508
+ const handleLeaveMeeting = () => {
41509
+ showCloseConfirm.value = false;
41510
+ emit("onChange", RoomModalSelectType.leaveClick);
41511
+ };
41512
+ return (_ctx, _cache) => {
41513
+ return openBlock(), createElementBlock("div", {
41514
+ class: normalizeClass(["js-dialog-header", themeClass.value])
41515
+ }, [
41516
+ createElementVNode("div", _hoisted_1$n, toDisplayString(meetingInfo.value.meetingName), 1),
41517
+ createElementVNode("div", _hoisted_2$l, [
41518
+ createElementVNode("div", {
41519
+ title: "最小化",
41520
+ onClick: _cache[0] || (_cache[0] = ($event) => emit("onChange", unref(RoomModalSelectType).minimizeClick))
41521
+ }, [
41522
+ createVNode(SvgIcon, {
41523
+ name: "MinimizeIcon",
41524
+ size: 16
41525
+ })
41526
+ ]),
41527
+ createElementVNode("div", {
41528
+ ref_key: "closeRef",
41529
+ ref: closeRef,
41530
+ title: "关闭",
41531
+ onClick: handleCloseClick
41532
+ }, [
41533
+ createVNode(SvgIcon, {
41534
+ name: "CloseIcon",
41535
+ size: 17
41536
+ })
41537
+ ], 512)
41538
+ ]),
41539
+ createVNode(SmartPopup, {
41540
+ visible: showCloseConfirm.value,
41541
+ "onUpdate:visible": _cache[2] || (_cache[2] = ($event) => showCloseConfirm.value = $event),
41542
+ trigger: closeRef.value,
41543
+ gap: 10
41544
+ }, {
41545
+ default: withCtx(() => [
41546
+ createElementVNode("div", {
41547
+ class: normalizeClass(["close-confirm-dropdown", themeClass.value])
41548
+ }, [
41549
+ unref(meState2).roleType == "1" ? (openBlock(), createElementBlock("div", {
41550
+ key: 0,
41551
+ class: "close-confirm-option danger",
41552
+ onClick: withModifiers(handleEndMeeting, ["stop"])
41553
+ }, " 结束会议 ")) : createCommentVNode("", true),
41554
+ createElementVNode("div", {
41555
+ class: "close-confirm-option simple",
41556
+ onClick: withModifiers(handleLeaveMeeting, ["stop"])
41557
+ }, " 离开会议 "),
41558
+ createElementVNode("div", {
41559
+ class: "close-confirm-option simple",
41560
+ onClick: _cache[1] || (_cache[1] = ($event) => showCloseConfirm.value = false)
41561
+ }, " 取消 ")
41562
+ ], 2)
41563
+ ]),
41564
+ _: 1
41565
+ }, 8, ["visible", "trigger"])
41566
+ ], 2);
41846
41567
  };
41847
41568
  }
41848
41569
  });
41849
- const MeetingHeader = /* @__PURE__ */ _export_sfc(_sfc_main$p, [["__scopeId", "data-v-8296cfa6"]]);
41570
+ const MeetingHeader = /* @__PURE__ */ _export_sfc(_sfc_main$p, [["__scopeId", "data-v-28fc3b9d"]]);
41850
41571
  const _hoisted_1$m = ["title"];
41851
41572
  const _hoisted_2$k = {
41852
41573
  class: "signal-bars",
@@ -42952,17 +42673,17 @@ const _sfc_main$l = /* @__PURE__ */ defineComponent({
42952
42673
  }
42953
42674
  });
42954
42675
  const MeetingFooter = /* @__PURE__ */ _export_sfc(_sfc_main$l, [["__scopeId", "data-v-480c2c76"]]);
42955
- const _hoisted_1$i = { class: "search" };
42956
- const _hoisted_2$h = { class: "custom-tabs" };
42957
- const _hoisted_3$h = { class: "member-list" };
42676
+ const _hoisted_1$i = { class: "js-search" };
42677
+ const _hoisted_2$h = { class: "js-custom-tabs" };
42678
+ const _hoisted_3$h = { class: "js-member-list" };
42958
42679
  const _hoisted_4$f = ["onMouseenter"];
42959
- const _hoisted_5$d = { class: "default-avatar" };
42960
- const _hoisted_6$c = { class: "info" };
42961
- const _hoisted_7$b = { class: "name-line" };
42962
- const _hoisted_8$a = { class: "name" };
42680
+ const _hoisted_5$d = { class: "js-default-avatar" };
42681
+ const _hoisted_6$c = { class: "js-info" };
42682
+ const _hoisted_7$b = { class: "js-name-line" };
42683
+ const _hoisted_8$a = { class: "js-name" };
42963
42684
  const _hoisted_9$8 = { key: 0 };
42964
42685
  const _hoisted_10$8 = { key: 1 };
42965
- const _hoisted_11$7 = { class: "member-actions" };
42686
+ const _hoisted_11$7 = { class: "js-member-actions" };
42966
42687
  const _hoisted_12$7 = ["onClick"];
42967
42688
  const _hoisted_13$6 = ["onClick"];
42968
42689
  const _hoisted_14$5 = ["onClick"];
@@ -42972,9 +42693,9 @@ const _hoisted_17$3 = ["onClick"];
42972
42693
  const _hoisted_18$3 = ["onClick"];
42973
42694
  const _hoisted_19$3 = {
42974
42695
  key: 0,
42975
- class: "empty-tip"
42696
+ class: "js-empty-tip"
42976
42697
  };
42977
- const _hoisted_20$3 = { class: "footer-btns" };
42698
+ const _hoisted_20$3 = { class: "js-footer-btns" };
42978
42699
  const _sfc_main$k = /* @__PURE__ */ defineComponent({
42979
42700
  __name: "ManageMembers",
42980
42701
  props: {
@@ -43127,10 +42848,10 @@ const _sfc_main$k = /* @__PURE__ */ defineComponent({
43127
42848
  }
43128
42849
  activeMoreMenuId.value = memberId;
43129
42850
  await nextTick();
43130
- const menuEl = document.getElementById(`dropdown-menu-${memberId}`);
42851
+ const menuEl = document.getElementById(`js-dropdown-menu-${memberId}`);
43131
42852
  const anchorEl = event.currentTarget;
43132
42853
  const listEl = document.querySelector(
43133
- ".manage-members .member-list"
42854
+ ".js-manage-members .js-member-list"
43134
42855
  );
43135
42856
  if (!menuEl || !anchorEl || !listEl) {
43136
42857
  moreMenuPlacement.value = "down";
@@ -43179,30 +42900,30 @@ const _sfc_main$k = /* @__PURE__ */ defineComponent({
43179
42900
  };
43180
42901
  return (_ctx, _cache) => {
43181
42902
  return openBlock(), createElementBlock("div", {
43182
- class: normalizeClass(["manage-members", themeClass.value])
42903
+ class: normalizeClass(["js-manage-members", themeClass.value])
43183
42904
  }, [
43184
42905
  createElementVNode("div", _hoisted_1$i, [
43185
42906
  withDirectives(createElementVNode("input", {
43186
42907
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => search.value = $event),
43187
42908
  type: "text",
43188
42909
  placeholder: "搜索用户",
43189
- class: "search-input"
42910
+ class: "js-search-input"
43190
42911
  }, null, 512), [
43191
42912
  [vModelText, search.value]
43192
42913
  ]),
43193
42914
  createVNode(SvgIcon, {
43194
42915
  name: "SearchIcon",
43195
- class: "search-icon",
42916
+ class: "js-search-icon",
43196
42917
  size: 16
43197
42918
  })
43198
42919
  ]),
43199
42920
  createElementVNode("div", _hoisted_2$h, [
43200
42921
  createElementVNode("div", {
43201
- class: normalizeClass(["tab-item", { active: activeTab.value === "inMeeting" }]),
42922
+ class: normalizeClass(["js-tab-item", { active: activeTab.value === "inMeeting" }]),
43202
42923
  onClick: _cache[1] || (_cache[1] = ($event) => activeTab.value = "inMeeting")
43203
42924
  }, " 会议中·" + toDisplayString(inMeetingCount.value), 3),
43204
42925
  createElementVNode("div", {
43205
- class: normalizeClass(["tab-item", { active: activeTab.value === "notJoined" }]),
42926
+ class: normalizeClass(["js-tab-item", { active: activeTab.value === "notJoined" }]),
43206
42927
  onClick: _cache[2] || (_cache[2] = ($event) => activeTab.value = "notJoined")
43207
42928
  }, " 未进入 ", 2)
43208
42929
  ]),
@@ -43210,7 +42931,7 @@ const _sfc_main$k = /* @__PURE__ */ defineComponent({
43210
42931
  (openBlock(true), createElementBlock(Fragment, null, renderList(filteredMembers.value, (member) => {
43211
42932
  return openBlock(), createElementBlock("div", {
43212
42933
  key: member.member.memberId,
43213
- class: "member-item",
42934
+ class: "js-member-item",
43214
42935
  onMouseenter: ($event) => hoveredMemberId.value = member.member.memberId,
43215
42936
  onMouseleave: handleMouseLeave
43216
42937
  }, [
@@ -43228,33 +42949,33 @@ const _sfc_main$k = /* @__PURE__ */ defineComponent({
43228
42949
  hoveredMemberId.value === member.member.memberId && member.member.memberId !== unref(userId) && isCurrentUserHost.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
43229
42950
  member.audioStatus == "1" && member.roleType !== "5" ? (openBlock(), createElementBlock("div", {
43230
42951
  key: 0,
43231
- class: "action-btn",
42952
+ class: "js-action-btn",
43232
42953
  onClick: ($event) => controlMember("1", "0", member.member.memberId)
43233
42954
  }, " 静音 ", 8, _hoisted_12$7)) : createCommentVNode("", true),
43234
42955
  member.audioStatus !== "1" && member.roleType !== "5" ? (openBlock(), createElementBlock("div", {
43235
42956
  key: 1,
43236
- class: "action-btn",
42957
+ class: "js-action-btn",
43237
42958
  onClick: ($event) => controlMember("1", "1", member.member.memberId)
43238
42959
  }, " 解除静音 ", 8, _hoisted_13$6)) : createCommentVNode("", true),
43239
42960
  member.member.memberId !== unref(userId) ? (openBlock(), createElementBlock("div", {
43240
42961
  key: 2,
43241
- class: "more-dropdown",
42962
+ class: "js-more-dropdown",
43242
42963
  onClick: withModifiers(($event) => toggleMoreMenu(member.member.memberId, $event), ["stop"])
43243
42964
  }, [
43244
42965
  _cache[4] || (_cache[4] = createElementVNode("span", null, "更多", -1)),
43245
- _cache[5] || (_cache[5] = createElementVNode("div", { class: "dropdown-arrow" }, "▼", -1)),
42966
+ _cache[5] || (_cache[5] = createElementVNode("div", { class: "js-dropdown-arrow" }, "▼", -1)),
43246
42967
  activeMoreMenuId.value === member.member.memberId ? (openBlock(), createElementBlock("div", {
43247
42968
  key: 0,
43248
- id: `dropdown-menu-${member.member.memberId}`,
43249
- class: normalizeClass(["dropdown-menu", { up: moreMenuPlacement.value === "up" }])
42969
+ id: `js-dropdown-menu-${member.member.memberId}`,
42970
+ class: normalizeClass(["js-dropdown-menu", { up: moreMenuPlacement.value === "up" }])
43250
42971
  }, [
43251
42972
  member.roleType !== "5" ? (openBlock(), createElementBlock("div", {
43252
42973
  key: 0,
43253
- class: "dropdown-item",
42974
+ class: "js-dropdown-item",
43254
42975
  onClick: ($event) => transferHost(member.member.memberId)
43255
42976
  }, " 设为主持人 ", 8, _hoisted_16$4)) : createCommentVNode("", true),
43256
42977
  createElementVNode("div", {
43257
- class: "dropdown-item",
42978
+ class: "js-dropdown-item",
43258
42979
  onClick: ($event) => kickOut(member.member.memberId)
43259
42980
  }, " 移出会议 ", 8, _hoisted_17$3)
43260
42981
  ], 10, _hoisted_15$4)) : createCommentVNode("", true)
@@ -43274,7 +42995,7 @@ const _sfc_main$k = /* @__PURE__ */ defineComponent({
43274
42995
  ], 64)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
43275
42996
  unref(meState2).roleType == "1" ? (openBlock(), createElementBlock("button", {
43276
42997
  key: 0,
43277
- class: "action-btn",
42998
+ class: "js-action-btn",
43278
42999
  onClick: ($event) => inviteMember(member)
43279
43000
  }, " 邀请 ", 8, _hoisted_18$3)) : createCommentVNode("", true)
43280
43001
  ], 64))
@@ -43286,12 +43007,12 @@ const _sfc_main$k = /* @__PURE__ */ defineComponent({
43286
43007
  createElementVNode("div", _hoisted_20$3, [
43287
43008
  activeTab.value === "inMeeting" && canTakeBackHost.value ? (openBlock(), createElementBlock("div", {
43288
43009
  key: 0,
43289
- class: "btn",
43010
+ class: "js-btn",
43290
43011
  onClick: _cache[3] || (_cache[3] = ($event) => takeBack())
43291
43012
  }, " 收回主持人 ")) : createCommentVNode("", true),
43292
43013
  activeTab.value === "inMeeting" && isCurrentUserHost.value ? (openBlock(), createElementBlock("div", {
43293
43014
  key: 1,
43294
- class: normalizeClass(["btn", { disabled: allMutedExceptMe.value }]),
43015
+ class: normalizeClass(["js-btn", { disabled: allMutedExceptMe.value }]),
43295
43016
  onClick: handleToggleAllMute
43296
43017
  }, toDisplayString(allMutedExceptMe.value ? "已全体静音" : "全体静音"), 3)) : createCommentVNode("", true)
43297
43018
  ])
@@ -43299,7 +43020,7 @@ const _sfc_main$k = /* @__PURE__ */ defineComponent({
43299
43020
  };
43300
43021
  }
43301
43022
  });
43302
- const ManageMembers = /* @__PURE__ */ _export_sfc(_sfc_main$k, [["__scopeId", "data-v-2cdcde37"]]);
43023
+ const ManageMembers = /* @__PURE__ */ _export_sfc(_sfc_main$k, [["__scopeId", "data-v-497b82f0"]]);
43303
43024
  const _hoisted_1$h = { class: "chat-container" };
43304
43025
  const _hoisted_2$g = { class: "chat-content" };
43305
43026
  const _hoisted_3$g = { class: "chat-meta" };
@@ -44102,14 +43823,14 @@ const _sfc_main$g = /* @__PURE__ */ defineComponent({
44102
43823
  }
44103
43824
  });
44104
43825
  const ScreenSharingIndicator = /* @__PURE__ */ _export_sfc(_sfc_main$g, [["__scopeId", "data-v-c5d77535"]]);
44105
- const _hoisted_1$e = { class: "video-container" };
43826
+ const _hoisted_1$e = { class: "js-video-container" };
44106
43827
  const _hoisted_2$d = {
44107
43828
  id: "my-video",
44108
43829
  class: "video-element"
44109
43830
  };
44110
43831
  const _hoisted_3$d = { class: "avatar-container" };
44111
43832
  const _hoisted_4$b = { class: "avatar small" };
44112
- const _hoisted_5$a = { class: "minimized-actions" };
43833
+ const _hoisted_5$a = { class: "js-minimized-actions" };
44113
43834
  const _hoisted_6$9 = ["title"];
44114
43835
  const _hoisted_7$8 = ["title"];
44115
43836
  const _hoisted_8$7 = ["title"];
@@ -44166,7 +43887,7 @@ const _sfc_main$f = /* @__PURE__ */ defineComponent({
44166
43887
  return openBlock(), createBlock(Teleport, { to: "body" }, [
44167
43888
  _ctx.visible ? withDirectives((openBlock(), createElementBlock("div", {
44168
43889
  key: 0,
44169
- class: normalizeClass(["minimized-meeting", themeClass.value]),
43890
+ class: normalizeClass(["js-minimized-meeting", themeClass.value]),
44170
43891
  style: normalizeStyle({ height: componentHeight.value })
44171
43892
  }, [
44172
43893
  withDirectives(createElementVNode("div", _hoisted_1$e, [
@@ -44183,7 +43904,7 @@ const _sfc_main$f = /* @__PURE__ */ defineComponent({
44183
43904
  ]),
44184
43905
  createElementVNode("div", _hoisted_5$a, [
44185
43906
  createElementVNode("div", {
44186
- class: "action-btn",
43907
+ class: "js-action-btn",
44187
43908
  title: ((_c2 = unref(meState2)) == null ? void 0 : _c2.microPhoneState) == "1" ? "静音" : "取消静音",
44188
43909
  onClick: withModifiers(toggleMic, ["stop"])
44189
43910
  }, [
@@ -44193,7 +43914,7 @@ const _sfc_main$f = /* @__PURE__ */ defineComponent({
44193
43914
  }, null, 8, ["name"])
44194
43915
  ], 8, _hoisted_6$9),
44195
43916
  createElementVNode("div", {
44196
- class: "action-btn",
43917
+ class: "js-action-btn",
44197
43918
  title: ((_e = unref(meState2)) == null ? void 0 : _e.cameraState) == "1" ? "关闭摄像头" : "打开摄像头",
44198
43919
  onClick: withModifiers(toggleCamera, ["stop"])
44199
43920
  }, [
@@ -44203,7 +43924,7 @@ const _sfc_main$f = /* @__PURE__ */ defineComponent({
44203
43924
  }, null, 8, ["name"])
44204
43925
  ], 8, _hoisted_7$8),
44205
43926
  createElementVNode("div", {
44206
- class: "action-btn",
43927
+ class: "js-action-btn",
44207
43928
  title: "返回会议",
44208
43929
  onClick: withModifiers(handleRestore, ["stop"])
44209
43930
  }, [
@@ -44213,7 +43934,7 @@ const _sfc_main$f = /* @__PURE__ */ defineComponent({
44213
43934
  })
44214
43935
  ]),
44215
43936
  createElementVNode("div", {
44216
- class: "action-btn",
43937
+ class: "js-action-btn",
44217
43938
  title: isCollapsed.value ? "展开画面小窗" : "收起画面小窗",
44218
43939
  onClick: withModifiers(handleToggleCollapse, ["stop"])
44219
43940
  }, [
@@ -44230,7 +43951,7 @@ const _sfc_main$f = /* @__PURE__ */ defineComponent({
44230
43951
  };
44231
43952
  }
44232
43953
  });
44233
- const MinimizedMeeting = /* @__PURE__ */ _export_sfc(_sfc_main$f, [["__scopeId", "data-v-5489e659"]]);
43954
+ const MinimizedMeeting = /* @__PURE__ */ _export_sfc(_sfc_main$f, [["__scopeId", "data-v-ec9b81c5"]]);
44234
43955
  const _hoisted_1$d = {
44235
43956
  key: 0,
44236
43957
  class: "toggle"
@@ -44750,7 +44471,7 @@ const _sfc_main$c = /* @__PURE__ */ defineComponent({
44750
44471
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => searchKeyword.value = $event),
44751
44472
  type: "text",
44752
44473
  placeholder: "搜索用户",
44753
- class: "search-input",
44474
+ class: "js-search-input",
44754
44475
  onInput: handleSearch
44755
44476
  }, null, 544), [
44756
44477
  [vModelText, searchKeyword.value]
@@ -44826,19 +44547,19 @@ const _sfc_main$c = /* @__PURE__ */ defineComponent({
44826
44547
  };
44827
44548
  }
44828
44549
  });
44829
- const Invite = /* @__PURE__ */ _export_sfc(_sfc_main$c, [["__scopeId", "data-v-8d4f89eb"]]);
44830
- const _hoisted_1$a = { class: "meeting-invite-modal" };
44831
- const _hoisted_2$a = { class: "meeting-invite-modal-header" };
44832
- const _hoisted_3$a = { class: "meeting-invite-modal-title" };
44550
+ const Invite = /* @__PURE__ */ _export_sfc(_sfc_main$c, [["__scopeId", "data-v-b2cd501c"]]);
44551
+ const _hoisted_1$a = { class: "js-meeting-invite-modal" };
44552
+ const _hoisted_2$a = { class: "js-meeting-invite-modal-header" };
44553
+ const _hoisted_3$a = { class: "js-meeting-invite-modal-title" };
44833
44554
  const _hoisted_4$8 = { class: "js-invite-modal-header-close" };
44834
- const _hoisted_5$7 = { class: "meeting-invite-modal-content" };
44835
- const _hoisted_6$7 = { class: "meeting-info" };
44836
- const _hoisted_7$6 = { class: "meeting-name" };
44837
- const _hoisted_8$5 = { class: "meeting-details" };
44838
- const _hoisted_9$5 = { class: "meeting-detail-item" };
44839
- const _hoisted_10$5 = { class: "value" };
44840
- const _hoisted_11$4 = { class: "meeting-detail-item" };
44841
- const _hoisted_12$4 = { class: "value" };
44555
+ const _hoisted_5$7 = { class: "js-meeting-invite-modal-content" };
44556
+ const _hoisted_6$7 = { class: "js-meeting-info" };
44557
+ const _hoisted_7$6 = { class: "js-meeting-name" };
44558
+ const _hoisted_8$5 = { class: "js-meeting-details" };
44559
+ const _hoisted_9$5 = { class: "js-meeting-detail-item" };
44560
+ const _hoisted_10$5 = { class: "js-value" };
44561
+ const _hoisted_11$4 = { class: "js-meeting-detail-item" };
44562
+ const _hoisted_12$4 = { class: "js-value" };
44842
44563
  const _sfc_main$b = /* @__PURE__ */ defineComponent({
44843
44564
  __name: "MeetingInviteModal",
44844
44565
  props: {
@@ -44907,7 +44628,7 @@ const _sfc_main$b = /* @__PURE__ */ defineComponent({
44907
44628
  return (_ctx, _cache) => {
44908
44629
  return _ctx.visible ? (openBlock(), createElementBlock("div", {
44909
44630
  key: 0,
44910
- class: normalizeClass(["meeting-invite-modal-overlay", themeClass.value])
44631
+ class: normalizeClass(["js-meeting-invite-modal-overlay", themeClass.value])
44911
44632
  }, [
44912
44633
  createElementVNode("div", _hoisted_1$a, [
44913
44634
  createElementVNode("div", _hoisted_2$a, [
@@ -44932,24 +44653,24 @@ const _sfc_main$b = /* @__PURE__ */ defineComponent({
44932
44653
  createElementVNode("div", _hoisted_7$6, toDisplayString(_ctx.meetingInfo.meetingName || "未知会议"), 1),
44933
44654
  createElementVNode("div", _hoisted_8$5, [
44934
44655
  createElementVNode("div", _hoisted_9$5, [
44935
- _cache[1] || (_cache[1] = createElementVNode("span", { class: "label" }, "邀请人:", -1)),
44656
+ _cache[1] || (_cache[1] = createElementVNode("span", { class: "js-label" }, "邀请人:", -1)),
44936
44657
  createElementVNode("span", _hoisted_10$5, toDisplayString(_ctx.meetingInfo.inviterName || "未知"), 1)
44937
44658
  ]),
44938
44659
  createElementVNode("div", _hoisted_11$4, [
44939
- _cache[2] || (_cache[2] = createElementVNode("span", { class: "label" }, "邀请时间:", -1)),
44660
+ _cache[2] || (_cache[2] = createElementVNode("span", { class: "js-label" }, "邀请时间:", -1)),
44940
44661
  createElementVNode("span", _hoisted_12$4, toDisplayString(formatTime(_ctx.meetingInfo.inviteTime)), 1)
44941
44662
  ])
44942
44663
  ])
44943
44664
  ]),
44944
- createElementVNode("div", { class: "meeting-invite-modal-actions" }, [
44665
+ createElementVNode("div", { class: "js-meeting-invite-modal-actions" }, [
44945
44666
  createElementVNode("button", {
44946
- class: "btn btn-accept",
44667
+ class: "js-btn js-btn-accept",
44947
44668
  onClick: handleAccept
44948
44669
  }, [..._cache[4] || (_cache[4] = [
44949
44670
  createElementVNode("span", null, "接受邀请", -1)
44950
44671
  ])]),
44951
44672
  createElementVNode("button", {
44952
- class: "btn btn-decline",
44673
+ class: "js-btn js-btn-decline",
44953
44674
  onClick: handleDecline
44954
44675
  }, [..._cache[5] || (_cache[5] = [
44955
44676
  createElementVNode("span", null, "拒绝邀请", -1)
@@ -44961,7 +44682,7 @@ const _sfc_main$b = /* @__PURE__ */ defineComponent({
44961
44682
  };
44962
44683
  }
44963
44684
  });
44964
- const MeetingInviteModal = /* @__PURE__ */ _export_sfc(_sfc_main$b, [["__scopeId", "data-v-891c0b93"]]);
44685
+ const MeetingInviteModal = /* @__PURE__ */ _export_sfc(_sfc_main$b, [["__scopeId", "data-v-397c4954"]]);
44965
44686
  const _hoisted_1$9 = {
44966
44687
  key: 0,
44967
44688
  class: "js-dialog-overlay"
@@ -46256,7 +45977,7 @@ const _sfc_main$7 = /* @__PURE__ */ defineComponent({
46256
45977
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => searchKeyword.value = $event),
46257
45978
  type: "text",
46258
45979
  placeholder: "搜索用户",
46259
- class: "search-input"
45980
+ class: "js-search-input"
46260
45981
  }, null, 512), [
46261
45982
  [vModelText, searchKeyword.value]
46262
45983
  ]),
@@ -46323,7 +46044,7 @@ const _sfc_main$7 = /* @__PURE__ */ defineComponent({
46323
46044
  };
46324
46045
  }
46325
46046
  });
46326
- const JSUnattendedSetting = /* @__PURE__ */ _export_sfc(_sfc_main$7, [["__scopeId", "data-v-bd4919c7"]]);
46047
+ const JSUnattendedSetting = /* @__PURE__ */ _export_sfc(_sfc_main$7, [["__scopeId", "data-v-1a8b386c"]]);
46327
46048
  const _hoisted_1$5 = { class: "notification-container" };
46328
46049
  const _hoisted_2$5 = ["onClick"];
46329
46050
  const _hoisted_3$5 = { class: "notification-content" };
@@ -46576,7 +46297,7 @@ const _sfc_main$4 = /* @__PURE__ */ defineComponent({
46576
46297
  };
46577
46298
  }
46578
46299
  });
46579
- const GlobalIncomingCallModal = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["__scopeId", "data-v-de2969ec"]]);
46300
+ const GlobalIncomingCallModal = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["__scopeId", "data-v-0803c0d7"]]);
46580
46301
  const _hoisted_1$2 = {
46581
46302
  key: 0,
46582
46303
  class: "unattended-display"
@@ -47294,11 +47015,306 @@ function scheduleJoinRoom() {
47294
47015
  }
47295
47016
  }, 1e3);
47296
47017
  }
47018
+ const HANDOFF_SOURCE_TAB_ID_KEY = "JS_MEETING_MQTT_HANDOFF_SOURCE_TAB_ID";
47019
+ const HANDOFF_TARGET_TAB_ID_KEY = "JS_MEETING_MQTT_HANDOFF_TARGET_TAB_ID";
47020
+ const HANDOFF_ID_KEY = "JS_MEETING_MQTT_HANDOFF_ID";
47021
+ const RECONNECT_SIGNAL_KEY = "JS_MEETING_MQTT_RECONNECT_SIGNAL";
47022
+ const TARGET_HEARTBEAT_KEY = "JS_MEETING_MQTT_HANDOFF_HEARTBEAT";
47023
+ const TARGET_TAB_STATUS_KEY = "JS_MEETING_NEW_TAB_STATUS";
47024
+ const TARGET_HEARTBEAT_INTERVAL_MS = 1e3;
47025
+ const RECONNECT_CHECK_DELAY_MS = 1500;
47026
+ const TARGET_TAB_STATUS_CLOSED = 0;
47027
+ const TARGET_TAB_STATUS_OPEN = 1;
47028
+ let installed = false;
47029
+ let closeSignalSent = false;
47030
+ let heartbeatTimer = null;
47031
+ let lastHandledReconnectSignalTs = 0;
47032
+ let leaveRequestSent = false;
47033
+ function safeSessionStorageGetItem(key) {
47034
+ try {
47035
+ return sessionStorage.getItem(key);
47036
+ } catch {
47037
+ return null;
47038
+ }
47039
+ }
47040
+ function safeLocalStorageSetItem(key, value) {
47041
+ try {
47042
+ localStorage.setItem(key, value);
47043
+ } catch {
47044
+ }
47045
+ }
47046
+ function safeLocalStorageGetItem(key) {
47047
+ try {
47048
+ return localStorage.getItem(key);
47049
+ } catch {
47050
+ return null;
47051
+ }
47052
+ }
47053
+ function getMeetingMqttHandoffContext() {
47054
+ const sourceTabId = safeSessionStorageGetItem(HANDOFF_SOURCE_TAB_ID_KEY) || "";
47055
+ const targetTabId = safeSessionStorageGetItem(HANDOFF_TARGET_TAB_ID_KEY) || "";
47056
+ const handoffId = safeSessionStorageGetItem(HANDOFF_ID_KEY) || "";
47057
+ if (!sourceTabId || !targetTabId || !handoffId) {
47058
+ return null;
47059
+ }
47060
+ return {
47061
+ sourceTabId,
47062
+ targetTabId,
47063
+ handoffId
47064
+ };
47065
+ }
47066
+ function readTargetHeartbeat() {
47067
+ const raw = safeLocalStorageGetItem(TARGET_HEARTBEAT_KEY);
47068
+ if (!raw) {
47069
+ return null;
47070
+ }
47071
+ try {
47072
+ const parsed = JSON.parse(raw);
47073
+ if (!(parsed == null ? void 0 : parsed.targetTabId) || !(parsed == null ? void 0 : parsed.handoffId) || !(parsed == null ? void 0 : parsed.ts)) {
47074
+ return null;
47075
+ }
47076
+ return {
47077
+ targetTabId: parsed.targetTabId,
47078
+ handoffId: parsed.handoffId,
47079
+ ts: Number(parsed.ts)
47080
+ };
47081
+ } catch {
47082
+ return null;
47083
+ }
47084
+ }
47085
+ function writeTargetHeartbeat(context) {
47086
+ safeLocalStorageSetItem(
47087
+ TARGET_HEARTBEAT_KEY,
47088
+ JSON.stringify({
47089
+ targetTabId: context.targetTabId,
47090
+ handoffId: context.handoffId,
47091
+ ts: Date.now()
47092
+ })
47093
+ );
47094
+ }
47095
+ function writeTargetTabStatus(status, context) {
47096
+ safeLocalStorageSetItem(
47097
+ TARGET_TAB_STATUS_KEY,
47098
+ JSON.stringify({
47099
+ sourceTabId: context.sourceTabId,
47100
+ targetTabId: context.targetTabId,
47101
+ handoffId: context.handoffId,
47102
+ status,
47103
+ ts: Date.now()
47104
+ })
47105
+ );
47106
+ }
47107
+ function sendReconnectSignal(context) {
47108
+ if (closeSignalSent) {
47109
+ return;
47110
+ }
47111
+ closeSignalSent = true;
47112
+ console.warn("[MQTT_HANDOFF] 目标页关闭,通知来源页恢复 MQTT", {
47113
+ sourceTabId: context.sourceTabId,
47114
+ targetTabId: context.targetTabId,
47115
+ handoffId: context.handoffId
47116
+ });
47117
+ safeLocalStorageSetItem(
47118
+ RECONNECT_SIGNAL_KEY,
47119
+ JSON.stringify({
47120
+ ...context,
47121
+ ts: Date.now()
47122
+ })
47123
+ );
47124
+ }
47125
+ function stopTargetHeartbeat() {
47126
+ if (heartbeatTimer) {
47127
+ clearInterval(heartbeatTimer);
47128
+ heartbeatTimer = null;
47129
+ }
47130
+ }
47131
+ function startTargetHeartbeat() {
47132
+ const context = getMeetingMqttHandoffContext();
47133
+ if (!context) {
47134
+ return;
47135
+ }
47136
+ const currentTabId = getCurrentMeetingTabId();
47137
+ if (currentTabId !== context.targetTabId) {
47138
+ return;
47139
+ }
47140
+ closeSignalSent = false;
47141
+ console.info("[MQTT_HANDOFF] 目标页心跳已启动", {
47142
+ sourceTabId: context.sourceTabId,
47143
+ targetTabId: context.targetTabId,
47144
+ handoffId: context.handoffId
47145
+ });
47146
+ writeTargetTabStatus(TARGET_TAB_STATUS_OPEN, context);
47147
+ writeTargetHeartbeat(context);
47148
+ stopTargetHeartbeat();
47149
+ heartbeatTimer = setInterval(() => {
47150
+ writeTargetHeartbeat(context);
47151
+ }, TARGET_HEARTBEAT_INTERVAL_MS);
47152
+ }
47153
+ function notifySourceTabForReconnect() {
47154
+ const context = getMeetingMqttHandoffContext();
47155
+ if (!context) {
47156
+ return;
47157
+ }
47158
+ const currentTabId = getCurrentMeetingTabId();
47159
+ if (currentTabId !== context.targetTabId) {
47160
+ return;
47161
+ }
47162
+ sendReconnectSignal(context);
47163
+ }
47164
+ function shouldWarnBeforeUnload() {
47165
+ const context = getMeetingMqttHandoffContext();
47166
+ if (!context) {
47167
+ return false;
47168
+ }
47169
+ const currentTabId = getCurrentMeetingTabId();
47170
+ return currentTabId === context.targetTabId;
47171
+ }
47172
+ function buildMeetingLeavePayload() {
47173
+ try {
47174
+ const meetingInfo = JSON.parse(sessionStorage.getItem("JS_MEETING_INFO") || "{}");
47175
+ const meetingId = String((meetingInfo == null ? void 0 : meetingInfo.meetingId) || "").trim();
47176
+ const memberId = String(getUserId() || "").trim();
47177
+ if (!meetingId || !memberId) {
47178
+ return null;
47179
+ }
47180
+ return { meetingId, memberId, force: true };
47181
+ } catch {
47182
+ return null;
47183
+ }
47184
+ }
47185
+ function sendMeetingLeaveOnClose() {
47186
+ if (leaveRequestSent) {
47187
+ return;
47188
+ }
47189
+ const payload = buildMeetingLeavePayload();
47190
+ if (!payload) {
47191
+ return;
47192
+ }
47193
+ leaveRequestSent = true;
47194
+ const body = JSON.stringify(payload);
47195
+ const token = getSdkToken();
47196
+ const authHeader = token ? { Authorization: `Bearer ${token}` } : {};
47197
+ const requestUrl = "/meeting/api/v2/meeting/leave";
47198
+ try {
47199
+ fetch(requestUrl, {
47200
+ method: "POST",
47201
+ headers: {
47202
+ "Content-Type": "application/json",
47203
+ Accept: "application/json, text/plain, */*",
47204
+ "X-Requested-With": "XMLHttpRequest",
47205
+ ...authHeader
47206
+ },
47207
+ body,
47208
+ keepalive: true,
47209
+ credentials: "same-origin"
47210
+ }).catch(() => {
47211
+ });
47212
+ console.warn("[MQTT_HANDOFF] 页面关闭触发离会请求(keepalive)", payload);
47213
+ } catch {
47214
+ }
47215
+ try {
47216
+ if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
47217
+ const beaconData = new Blob([body], { type: "application/json" });
47218
+ const sent = navigator.sendBeacon(requestUrl, beaconData);
47219
+ console.warn("[MQTT_HANDOFF] 页面关闭触发离会请求(beacon)", {
47220
+ ...payload,
47221
+ sent
47222
+ });
47223
+ }
47224
+ } catch {
47225
+ }
47226
+ }
47227
+ function shouldReconnectForSignal(signal) {
47228
+ const currentTabId = getCurrentMeetingTabId();
47229
+ if (!signal.sourceTabId || signal.sourceTabId !== currentTabId) {
47230
+ return false;
47231
+ }
47232
+ const latestHeartbeat = readTargetHeartbeat();
47233
+ if (!latestHeartbeat) {
47234
+ return true;
47235
+ }
47236
+ if (latestHeartbeat.targetTabId !== signal.targetTabId) {
47237
+ return true;
47238
+ }
47239
+ if (latestHeartbeat.handoffId !== signal.handoffId) {
47240
+ return true;
47241
+ }
47242
+ return latestHeartbeat.ts <= signal.ts;
47243
+ }
47244
+ function installMeetingMqttHandoffBridge(onReconnectRequested) {
47245
+ if (installed || typeof window === "undefined") {
47246
+ return;
47247
+ }
47248
+ installed = true;
47249
+ startTargetHeartbeat();
47250
+ window.addEventListener("storage", (event) => {
47251
+ if (event.key !== RECONNECT_SIGNAL_KEY || !event.newValue) {
47252
+ return;
47253
+ }
47254
+ let signal = null;
47255
+ try {
47256
+ signal = JSON.parse(event.newValue);
47257
+ } catch {
47258
+ return;
47259
+ }
47260
+ if (!(signal == null ? void 0 : signal.sourceTabId) || !signal.targetTabId || !signal.handoffId) {
47261
+ return;
47262
+ }
47263
+ const signalTs = Number(signal.ts);
47264
+ if (!signalTs || signalTs <= lastHandledReconnectSignalTs) {
47265
+ return;
47266
+ }
47267
+ lastHandledReconnectSignalTs = signalTs;
47268
+ console.warn("[MQTT_HANDOFF] 收到 MQTT 恢复信号,等待确认目标页是否真的关闭", {
47269
+ sourceTabId: signal.sourceTabId,
47270
+ targetTabId: signal.targetTabId,
47271
+ handoffId: signal.handoffId,
47272
+ signalTs
47273
+ });
47274
+ window.setTimeout(() => {
47275
+ if (!shouldReconnectForSignal(signal)) {
47276
+ console.info("[MQTT_HANDOFF] 忽略 MQTT 恢复,本次更像是目标页刷新/重载", {
47277
+ sourceTabId: signal.sourceTabId,
47278
+ targetTabId: signal.targetTabId,
47279
+ handoffId: signal.handoffId
47280
+ });
47281
+ return;
47282
+ }
47283
+ console.warn("[MQTT_HANDOFF] 确认目标页已关闭,准备恢复来源页 MQTT", {
47284
+ sourceTabId: signal.sourceTabId,
47285
+ targetTabId: signal.targetTabId,
47286
+ handoffId: signal.handoffId
47287
+ });
47288
+ void onReconnectRequested();
47289
+ }, RECONNECT_CHECK_DELAY_MS);
47290
+ });
47291
+ window.addEventListener("beforeunload", (event) => {
47292
+ if (!shouldWarnBeforeUnload()) {
47293
+ return;
47294
+ }
47295
+ event.preventDefault();
47296
+ event.returnValue = "";
47297
+ });
47298
+ window.addEventListener("pagehide", (event) => {
47299
+ const context = getMeetingMqttHandoffContext();
47300
+ const shouldHandleTargetTabClose = !!context && getCurrentMeetingTabId() === context.targetTabId;
47301
+ if (shouldHandleTargetTabClose && context) {
47302
+ writeTargetTabStatus(TARGET_TAB_STATUS_CLOSED, context);
47303
+ sendMeetingLeaveOnClose();
47304
+ }
47305
+ stopTargetHeartbeat();
47306
+ if (event.persisted) {
47307
+ return;
47308
+ }
47309
+ notifySourceTabForReconnect();
47310
+ });
47311
+ }
47297
47312
  let authToken = null;
47298
47313
  async function restoreMeetingMqttConnection() {
47299
47314
  console.warn("[MQTT_RESTORE] 收到恢复请求,开始重连 MQTT");
47300
47315
  try {
47301
- await initializeMeetingMQTT();
47316
+ const wsMqttUrl = await getMeetingWsMqttUrl();
47317
+ await initializeMeetingMQTT({ mqttUrl: wsMqttUrl });
47302
47318
  installMeetingMessageHandlers();
47303
47319
  console.info("[MQTT_RESTORE] MQTT 恢复成功,消息监听已恢复");
47304
47320
  } catch (error) {
@@ -47423,9 +47439,8 @@ const JSSZMeeting = {
47423
47439
  }
47424
47440
  }
47425
47441
  }
47426
- await initializeMeetingMQTT({
47427
- myInfo: myInfo.data
47428
- });
47442
+ const wsMqttUrl = await getMeetingWsMqttUrl();
47443
+ await initializeMeetingMQTT({ myInfo: myInfo.data, mqttUrl: wsMqttUrl });
47429
47444
  installMeetingMessageHandlers();
47430
47445
  },
47431
47446
  //宿主退出登陆时