You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用JavaScript实现Firebase同节点多标签页重复打开告警

实现Firebase节点多标签页冲突提示方案

针对你需要的「同一Firebase节点在多标签页打开时弹出提示」的需求,结合现有代码,我们可以通过Firebase状态标记+Broadcast Channel跨标签通信实现类似在线Excel的冲突提示效果,具体改造如下:

核心思路

  1. 给每个formbuilder_${caseId}节点新增active_sessions子节点,记录当前打开该节点的所有活跃标签页会话ID(带过期时间,避免标签页意外关闭后残留无效记录)。
  2. 页面加载时生成唯一会话ID,检查目标节点的活跃会话:若存在其他有效会话,立即弹出提示。
  3. 利用Broadcast Channel同步会话状态,确保多标签页实时感知冲突。
  4. 标签页关闭/刷新时,主动移除自身会话记录,同时通过Firebase规则自动清理过期会话。

修改后的完整代码

// 获取URL参数case_id
$.urlParam = function (name) {
  var results = new RegExp('[\\?&]' + name + '=([^&#]*)')
    .exec(window.location.href);
  if (results == null) {
    return 0;
  }
  return results[1] || 0;
}
let caseId = $.urlParam('case_id');
if (!caseId) {
  alert('缺少case_id参数');
  throw new Error('case_id参数缺失');
}

// 导入Firebase SDK
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.22.2/firebase-app.js";
import { getDatabase, ref, set, onValue, child, update, get, remove } from "https://www.gstatic.com/firebasejs/9.22.2/firebase-database.js";

// Firebase配置
const firebaseConfig = {
  apiKey: "AIzaSyBQlm0cLrmY19nDXyRQRm_C_51gulT0mRY",
  authDomain: "devt-23a3b.firebaseapp.com",
  projectId: "devt-23a3b",
  storageBucket: "devt-23a3b.appspot.com",
  messagingSenderId: "949860997571",
  appId: "1:949860997571:web:d398ef26b70791de75c576"
};

// 初始化Firebase
const app = initializeApp(firebaseConfig);
const database = getDatabase(app);

// 核心节点引用
const formBuilderRefT = ref(database, `formbuilder_${caseId}`);
const formFieldRef = child(formBuilderRefT, 'fields');
const activeSessionsRef = child(formBuilderRefT, 'active_sessions');

// 生成唯一会话ID(时间戳+随机数保证唯一性)
const sessionId = `session_${Date.now()}_${Math.floor(Math.random() * 10000)}`;
// 会话过期时间:5分钟(处理标签页意外关闭未主动删除的情况)
const sessionExpireTime = Date.now() + 5 * 60 * 1000;

// 创建Broadcast Channel(按caseId区分,确保同一节点的标签页通信)
const broadcastChannel = new BroadcastChannel(`formbuilder_${caseId}_channel`);

$(document).ready(function () {
  // 1. 初始化时检查活跃会话
  checkActiveSessions();

  // 2. 监听Firebase字段变化,同步到其他标签页
  onValue(formFieldRef, (snapshot) => {
    const data = snapshot.val();
    console.log("Received data:", data);
    broadcastChannel.postMessage({ type: 'data_update', data });
  });

  // 3. 监听用户输入,更新Firebase字段
  $("body").on('keyup', '.user_input', function () {
    let defaultValue = $(this).val();
    const updateObject = { [`name`]: defaultValue };
    set(formFieldRef, updateObject);
  });

  // 4. 监听Broadcast Channel消息
  broadcastChannel.onmessage = (event) => {
    const { type, data } = event.data;
    if (type === 'new_session') {
      // 已有标签页收到新会话通知,提示用户有新标签页打开
      alert(`注意:当前表单(case_id: ${caseId})已在其他标签页打开`);
    } else if (type === 'data_update') {
      // 同步其他标签页的字段更新到当前UI
      $('.user_input').val(data?.name || '');
    }
  };

  // 5. 标签页关闭/刷新时,移除当前会话
  window.addEventListener('beforeunload', () => {
    remove(child(activeSessionsRef, sessionId));
    broadcastChannel.postMessage({ type: 'session_closed', sessionId });
  });
});

// 检查当前节点的活跃会话
function checkActiveSessions() {
  get(activeSessionsRef).then((snapshot) => {
    const sessions = snapshot.val() || {};
    const validSessions = Object.keys(sessions).filter(id => {
      // 过滤过期会话
      return sessions[id].expireTime > Date.now();
    });

    if (validSessions.length > 0) {
      // 已有活跃会话,弹出提示
      alert(`当前表单(case_id: ${caseId})已在其他标签页打开,请避免重复操作`);
    }

    // 注册当前会话到Firebase
    set(child(activeSessionsRef, sessionId), {
      expireTime: sessionExpireTime,
      createdAt: Date.now()
    });

    // 通知其他标签页有新会话打开
    broadcastChannel.postMessage({ type: 'new_session', sessionId });

    // 定期刷新会话过期时间(防止长时间停留导致会话过期)
    setInterval(() => {
      update(child(activeSessionsRef, sessionId), {
        expireTime: Date.now() + 5 * 60 * 1000
      });
    }, 60 * 1000);
  }).catch((error) => {
    console.error('检查活跃会话失败:', error);
  });
}

// 检查节点是否存在(保留原有逻辑)
function checkNodeExists(ref, callback) {
  get(ref)
    .then((snapshot) => {
      callback(snapshot.exists());
    })
    .catch(() => {
      callback(false);
    });
}

关键优化点说明

  • 会话唯一性与过期机制:用时间戳+随机数生成会话ID,同时设置5分钟过期时间,配合定期刷新,避免标签页意外关闭后残留无效会话。
  • 针对性的Broadcast Channel:按caseId创建专属通信频道,避免不同节点的标签页互相干扰。
  • 双向提示:新标签页打开时会主动提示,已有标签页也会收到新会话通知,确保用户感知所有冲突场景。
  • 自动清理无效会话:除了主动移除,还可以在Firebase控制台添加实时数据库规则,自动删除过期会话:
    {
      "rules": {
        "formbuilder_$caseId": {
          "active_sessions": {
            "$sessionId": {
              ".write": true,
              ".validate": "newData.hasChildren(['expireTime', 'createdAt'])",
              ".indexOn": ["expireTime"],
              ".delete": "now > data.child('expireTime').val()"
            }
          }
        }
      }
    }
    

注意事项

  • 确保所有标签页同源,Broadcast Channel仅支持同源页面通信。
  • 若用户禁用了Broadcast Channel(极少数场景),Firebase的active_sessions节点仍能提供基础的冲突检测能力。

内容的提问来源于stack exchange,提问作者Raheel Aslam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 03:54:57