# 挂载点

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

```javascript
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,
  });
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://shenjunhong.gitbook.io/blog/chang-jian-zong-jie/tencent/tapd/bian-ji-qi-gua-zai-dian-shi-xian.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
