(function (window) {
  // 私有作用域,不污染全局
  const AI_FLOAT_PLUGIN = function () {
    // 配置项
    const config = {
      // iframe 地址,外部可修改
      iframeSrc: "https://www.baidu.com",
      // 弹窗宽高
      modalWidth: 420,
      modalHeight: 520,
      // 悬浮球大小
      btnSize: 56,
      // 主题色
      primaryColor: "#1677ff"
    };

    // 拖拽状态
    const state = {
      btnDrag: { isDrag: false, offsetX: 0, offsetY: 0 },
      modalDrag: { isDrag: false, startX: 0, startY: 0, startLeft: 0, startTop: 0 }
    };

    // 容器DOM
    let floatBtn = null;
    let modalWrap = null;
    let modalHeader = null;
    let closeBtn = null;
    let iframe = null;

    // ===================== 创建样式 =====================
    function createStyle() {
      const style = document.createElement("style");
      style.id = "ai-float-plugin-style";
      style.textContent = `
        #ai-float-btn {
          position: fixed;
          right: 20px;
          bottom: 120px;
          width: ${config.btnSize}px;
          height: ${config.btnSize}px;
          border-radius: 50%;
          background: ${config.primaryColor};
          box-shadow: 0 4px 12px rgba(22, 119, 255, 0.3);
          cursor: move;
          z-index: 99999;
          display: flex;
          align-items: center;
          justify-content: center;
          color: #fff;
          font-size: 24px;
          user-select: none;
          transition: transform 0.2s;
        }
        #ai-float-btn:hover {
          transform: scale(1.08);
        }
        #ai-modal-wrap {
          position: fixed;
          width: ${config.modalWidth}px;
          height: ${config.modalHeight}px;
          background: #fff;
          border-radius: 12px;
          box-shadow: 0 8px 30px rgba(0,0,0,0.2);
          z-index: 99998;
          overflow: hidden;
          display: none;
          user-select: none;
        }
        #ai-modal-header {
          height: 40px;
          line-height: 40px;
          background: ${config.primaryColor};
          color: #fff;
          padding: 0 15px;
          display: flex;
          align-items: center;
          justify-content: space-between;
          cursor: move;
        }
        #ai-modal-close {
          font-size: 20px;
          cursor: pointer;
          padding: 0 8px;
        }
        #ai-modal-close:hover {
          color: #f5f5f5;
        }
        #ai-iframe {
          width: 100%;
          height: calc(100% - 40px);
          border: none;
        }
      `;
      document.head.appendChild(style);
    }

    // ===================== 创建DOM结构 =====================
    function createDom() {
      // 悬浮按钮
      floatBtn = document.createElement("div");
      floatBtn.id = "ai-float-btn";
      floatBtn.innerText = "AI";

      // 弹窗外层
      modalWrap = document.createElement("div");
      modalWrap.id = "ai-modal-wrap";

      // 弹窗头部
      modalHeader = document.createElement("div");
      modalHeader.id = "ai-modal-header";
      modalHeader.innerHTML = `<span>AI智能助手</span>`;

      // 关闭按钮
      closeBtn = document.createElement("span");
      closeBtn.id = "ai-modal-close";
      closeBtn.innerText = "×";
      modalHeader.appendChild(closeBtn);

      // iframe
      iframe = document.createElement("iframe");
      iframe.id = "ai-iframe";
      iframe.src = config.iframeSrc;

      // 组装
      modalWrap.appendChild(modalHeader);
      modalWrap.appendChild(iframe);

      // 挂载到body
      document.body.appendChild(floatBtn);
      document.body.appendChild(modalWrap);
    }

    // ===================== 悬浮球拖拽 =====================
    function bindBtnDrag() {
      floatBtn.addEventListener("mousedown", (e) => {
        if (e.button !== 0) return;
        state.btnDrag.isDrag = true;
        const rect = floatBtn.getBoundingClientRect();
        state.btnDrag.offsetX = e.clientX - rect.left;
        state.btnDrag.offsetY = e.clientY - rect.top;
        floatBtn.style.transition = "none";
        e.preventDefault();
      });

      document.addEventListener("mousemove", (e) => {
        if (!state.btnDrag.isDrag) return;
        const winW = window.innerWidth;
        const winH = window.innerHeight;
        const btnW = floatBtn.offsetWidth;
        const btnH = floatBtn.offsetHeight;

        let x = e.clientX - state.btnDrag.offsetX;
        let y = e.clientY - state.btnDrag.offsetY;

        x = Math.max(0, Math.min(x, winW - btnW));
        y = Math.max(0, Math.min(y, winH - btnH));

        floatBtn.style.left = x + "px";
        floatBtn.style.right = "auto";
        floatBtn.style.top = y + "px";
      });

      document.addEventListener("mouseup", () => {
        if (state.btnDrag.isDrag) {
          state.btnDrag.isDrag = false;
          floatBtn.style.transition = "transform 0.2s";
        }
      });
    }

    // ===================== 点击打开弹窗 =====================
    function bindBtnClick() {
      floatBtn.addEventListener("click", () => {
        // 拖拽不触发点击
        if (state.btnDrag.isDrag) return;
        modalWrap.style.display = "block";
        // 弹窗居中
        const winW = window.innerWidth;
        const winH = window.innerHeight;
        const mW = modalWrap.offsetWidth;
        const mH = modalWrap.offsetHeight;
        modalWrap.style.left = (winW - mW) / 2 + "px";
        modalWrap.style.top = (winH - mH) / 2 + "px";
      });
    }

    // ===================== 弹窗关闭 =====================
    function bindClose() {
      closeBtn.addEventListener("click", () => {
        modalWrap.style.display = "none";
      });

      // ESC关闭
      document.addEventListener("keydown", (e) => {
        if (e.key === "Escape") {
          modalWrap.style.display = "none";
        }
      });
    }

    // ===================== 弹窗拖拽 =====================
    function bindModalDrag() {
      modalHeader.addEventListener("mousedown", (e) => {
        if (e.button !== 0) return;
        state.modalDrag.isDrag = true;
        state.modalDrag.startX = e.clientX;
        state.modalDrag.startY = e.clientY;
        const rect = modalWrap.getBoundingClientRect();
        state.modalDrag.startLeft = rect.left;
        state.modalDrag.startTop = rect.top;
        e.preventDefault();
      });

      document.addEventListener("mousemove", (e) => {
        if (!state.modalDrag.isDrag) return;
        const winW = window.innerWidth;
        const winH = window.innerHeight;
        const mW = modalWrap.offsetWidth;
        const mH = modalWrap.offsetHeight;

        const moveX = e.clientX - state.modalDrag.startX;
        const moveY = e.clientY - state.modalDrag.startY;

        let newLeft = state.modalDrag.startLeft + moveX;
        let newTop = state.modalDrag.startTop + moveY;

        newLeft = Math.max(0, Math.min(newLeft, winW - mW));
        newTop = Math.max(0, Math.min(newTop, winH - mH));

        modalWrap.style.left = newLeft + "px";
        modalWrap.style.top = newTop + "px";
      });

      document.addEventListener("mouseup", () => {
        state.modalDrag.isDrag = false;
      });
    }

    // ===================== 初始化 =====================
    function init(options = {}) {
      // 合并外部配置
      Object.assign(config, options);
      // 防止重复创建
      if (document.getElementById("ai-float-btn")) return;

      createStyle();
      createDom();
      bindBtnDrag();
      bindBtnClick();
      bindClose();
      bindModalDrag();
    }

    // 对外暴露方法
    return {
      init,
      // 手动关闭弹窗
      closeModal() {
        if (modalWrap) modalWrap.style.display = "none";
      },
      // 手动打开弹窗
      openModal() {
        if (modalWrap) modalWrap.style.display = "block";
      },
      // 更新iframe地址
      setIframeSrc(url) {
        if (iframe) iframe.src = url;
      },
      // 销毁插件
      destroy() {
        const style = document.getElementById("ai-float-plugin-style");
        style && style.remove();
        floatBtn && floatBtn.remove();
        modalWrap && modalWrap.remove();
      }
    };
  };

  // 挂载到全局,兼容所有框架
  window.AiFloatPlugin = AI_FLOAT_PLUGIN();
})(window);

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐