挂载点

// 难点 怎么能够同步更新版本 如果我们自己拉下来之后

import { get, pick } from "lodash-es";
import api from "@/api";
import { MessageBox } from "element-ui";
class OpenAppEventBridge {
  constructor() {
    this.messageHandler = this.messageHandler.bind(this);

    if (window.addEventListener) {
      window.addEventListener("message", this.messageHandler);
    } else {
      window.attachEvent("onmessage", this.messageHandler);
    }

    this.store = {};
  }

  messageHandler(e) {
    const { data = {}, origin, source } = e;

    const { code } = data;
    // console.debug(data);

    if (!code) {
      return;
    }

    this.broadcast(data);
  }

  findProxy({ entranceId, appID }) {
    return Object.values(this.store).find(
      (proxy) =>
        proxy && proxy.entranceId === entranceId && proxy.appID === appID
    );
  }

  broadcast(data) {
    const { code, event, params, id } = data;
    Object.entries(this.store).forEach(([key, proxy]) => {
      if (key === code) {
        proxy.emit(event, { params, code, id }, this);
      }
    });
  }

  /**
   * 主要目的就是为了绑定相应的iframe对象
   */
  registerApp(params) {
    const proxy = new Proxy(params);

    this.store[proxy.code] = proxy;

    return proxy;
  }

  updateAppCode(preCode, newCode) {
    const appProxy = this.store[preCode];
    appProxy.code = newCode;
    this.store[newCode] = appProxy;
    this.store[preCode] = null;
  }

  destroyApp(code) {
    this.store[code] = null;
  }
}

class Proxy {
  constructor({
    code,
    appID,
    entranceParams,
    iframe,
    vm,
    workspaceId,
    entityId,
    entityType,
    entranceId,
  }) {
    this.code = code;
    this.iframe = iframe;
    this.appID = appID;
    this.workspaceId = workspaceId;
    this.entranceParams = entranceParams;
    this.vm = vm;
    this.entityId = entityId;
    this.entityType = entityType;
    this.entranceId = entranceId;

    this.queue = [];
  }

  on(event, callback) {
    this.queue.push({ event, callback });
    return this;
  }

  emit(event, { params, code, id }, controller) {
    this.queue.forEach(async (item) => {
      if (event === item.event) {
        const result = await item.callback.call(
          this.vm,
          {
            params,
            proxy: this,
            code,
            id,
          },
          controller
        );

        this.send({
          params: result,
          id,
        });
      }
    });
  }

  send({ event = "", params, id }) {
    console.log(event, params, id);
    const message = {
      code: this.code,
      event,
      params,
      id,
    };
    const { iframe } = this;
    iframe.contentWindow.postMessage(message, "*");
  }
}

export default function (Vue) {
  const controller = new OpenAppEventBridge();

  // eslint-disable-next-line no-param-reassign
  Vue.prototype.$registerApp = function (params) {
    const vm = this;

    // register open app proxy and listen common event for vue components
    const proxy = controller
      .registerApp({
        ...params,
        vm,
      })
      .on("showFlash", message)
      .on("generateCode", refreshToken)
      .on("getEntranceData", getEntranceData)
      .on("openObjURL", openObjURL)
      .on("openBkURL", openBkURLPage)
      .on("showConfirm", showConfirm)
      .on("handlerInvoke", handlerInvoke)
      .on("getUserListByKeyWords", getUserListByKeyWords)
      .on("getWorkspaceMemberList", getWorkspaceMemberList)
      .on("getAllRelateMember", getAllRelateMember)
      .on("syncPageEvent", syncPageEvent)
      .on("showDialog", showDialog)
      .on("closeConfirm", closeConfirmCallBack);

    // remove the proxy when vue components destroyed
    vm.$on("hook:beforeDestroy", () => {
      controller.destroyApp(proxy.code);
    });

    return proxy;
  };
  // Vue.prototype.$listenOpenApp = function () {};
}

/* event handle begin */

function message({ params }) {
  const { msg } = params;
  const message = msg.trim();
  message && this.$message(msg);
}

function closeConfirmCallBack({ params }) {
  const msg = get(params, "params.msg", "").trim();
  message && this.$message(msg);
}

/**
 * @todo 历史原因已经不能够将这个code作为通信凭证的问题解决
 * @description 刷新code;
 */
async function refreshToken({ proxy }, controller) {
  const vm = this;
  const { currentWorkspaceId } = vm;

  const refreshCode = await api.open.generateCode({
    workspaceId: currentWorkspaceId,
    appID: proxy.appID,
  });

  // 历史代码的兼容,如果用的是JS-sdk不需要
  proxy.send({
    event: "syncCode",
    params: { code: refreshCode },
  });

  controller.updateAppCode(proxy.code, refreshCode);
  return refreshCode;
}

/**
 * @description 同一个应用的不同挂载点通信
 */
function syncPageEvent({ params, proxy }, controller) {
  const { entranceId, data } = params;

  const targetProxy = controller.findProxy({
    appID: proxy.appID,
    entranceId,
  });

  if (targetProxy) {
    targetProxy.send({
      event: "syncPageEvent",
      params: {
        data,
      },
    });
  }
}

/**
 * @description 获取挂在点信息
 */
function getEntranceData({ proxy }) {
  return pick(
    proxy,
    "entityId",
    "entityType",
    "workspaceId",
    "appId",
    "code",
    "entranceId"
  );
}

/**
 * @description 打开3大对象页面
 */
function openObjURL({ params, proxy }) {
  const { entityType, entityId } = params;
  const { workspaceId } = proxy;

  let url = "";

  // @TODO: 待确认跳转的链接地址
  switch (entityType) {
    case "story":
      url = `/${workspaceId}/prong/stories/view/${entityId}`;
      break;
    case "bug":
      url = `/${workspaceId}/bugtrace/bugs/view?bug_id=${entityId}`;
      break;
    case "task":
      url = `/${workspaceId}/prong/tasks/view/${entityId}`;
      break;
  }

  url && window.open(url, "_blank");
}

/**
 * @description 打开 app_for_project 挂载点 跟 应用设置页面
 **/
function openBkURLPage({ params: options, proxy: data }) {
  const entityId = options.entityId || "app_for_project";
  const url = `/${this.workspaceId + entityId + data.appId}#${options.url}`;
  if (url) window.open(url, "_blank");
}

async function showConfirm({ params, proxy }) {
  return await MessageBox.confirm(params.content, "", {
    dangerouslyUseHTMLString: proxy.code === "code_review", // 老旧逻辑的白名单
  }).then(() => {
    proxy.send({
      event: "confirmYes",
      params: { data: params.data },
    });
  });
}

/**
 * Api 错误的事件,不公开也不推荐添加 ;
 */
async function getUserListByKeyWords({ params, proxy }) {
  const result = await api.open.getUsers({
    workspaceId: params.workspace_id,
    keyword: params.key_word,
  });

  const response = {
    data: result.data.data,
    keyword: params.key_word,
  };

  proxy.send({
    event: "userChooserFetchFinish",
    params: response,
  });

  return response;
}

async function handlerInvoke({ params, proxy }) {
  const { workspaceId, appID } = proxy;
  const { handler } = params;

  const res = await api.open.handleInvoke({
    workspaceId,
    appID,
    handler,
    params: params.params,
  });

  const invokeData = get(res, "data.data");

  return invokeData ? invokeData : res;
}

async function getWorkspaceMemberList() {
  return {};
}

async function getAllRelateMember({ proxy }) {
  proxy.send({ event: "getAllRelateMemberFinish", params: [] });
  return {};
}

function showDialog({ params, proxy }) {
  this.$openAppDialog({
    name: params.title,
    url: params.url,
    entranceId: "open-app-dialog",
    height: params.height,
    appID: proxy.appID,
  });
}

最后更新于

这有帮助吗?