今日掘友分油猴插件

5,780 阅读1分钟

注意

使用油猴插件可能会有一些安全风险的,所以这里提供的是源码(未压缩),供大家审查。

介绍

玩转掘金,玩转掘友分。

点击今日掘友分,有更多功能啦。

image.png

TODO

  1. 稳定性持续优化(有时需要刷新页面才能显示的问题)
  2. 解决进入用户页面卡死,猜测为MutaionObserve持续触发导致

2023.9.27更新

  1. 修复:沸点弹窗无法发送自定义内容
  2. 新功能:好文推荐

2023.9.25更新

  1. 新功能:一键前往我的文章

  2. 新功能:本页面弹窗发布沸点

    image.png

  3. 新功能:一键随机评论/点赞沸点

  4. 新功能:对本页面文章一键收藏、点赞、评论

2023.9.21 更新

  1. 新功能:点击今日掘友分可弹出操作快捷栏

  2. 新功能:操作快捷栏增加一键签到、抽奖、沾喜气。

  3. 修复层级遮挡问题(header的z-index调整了)

    image.png

2023.9.20 更新

  1. 发文章:支持选择参与热门活动主题

2023.9.19 更新

  1. 任务进度条排序调整
  2. 进度条样式调整
  3. 间隔3秒自动刷新掘友分进度
  4. 使用rollup+vue重构,数据驱动开发

2023.9.18 更新

  1. 调整的进度条色彩与样式
  2. 支持深色模式
  3. 支持更多页面展示插件并做了一定兼容

最新插件代码地址

// ==UserScript==
// @name         今日掘友分
// @namespace    http://tampermonkey.net/
// @version      0.1.1
// @description  帮助玩转掘金社区,快速升级
// @author       奇幻心灵
// @match        https://juejin.cn/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=juejin.cn
// @connect      juejin.cn
// @grant        GM_xmlhttpRequest
// @require      https://cdn.jsdelivr.net/npm/vue@2.6.14
// ==/UserScript==

(function (Vue) {
  'use strict';

  

  function ___$insertStyle(css) {
      if (!css || typeof window === 'undefined') {
          return;
      }
      const style = document.createElement('style');
      style.setAttribute('type', 'text/css');
      style.innerHTML = css;
      document.head.appendChild(style);
      return css;
  }

  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

  var Vue__default = /*#__PURE__*/_interopDefaultLegacy(Vue);

  function request(method, path, data) {
    return new Promise((resolve, reject) => {
      const url = `https://api.juejin.cn/${path}`;
      const headers = {
        'Content-Type': 'application/json',
      };
      const options = {
        method,
        url,
        headers,
        responseType: 'json',
        onload: xhr => {
          // console.log(url, xhr.response);
          const { err_no, data, err_msg } = xhr.response;
          if (err_no === 0) return resolve(data);
          reject({ message: err_msg });
        },
        onerror: err => reject(err),
      };
      if (method === 'POST') {
        options.data = JSON.stringify(data);
      }
      GM_xmlhttpRequest(options);
    });
  }

  const post = (...args) => request('POST', ...args);
  const get = (...args) => request('GET', ...args);

  // 获取成长任务列表
  function getGrowthTaskList() {
    return post('growth_api/v1/user_growth/task_list', {
      growth_type: 1,
    }).then(data => {
      const { growth_tasks, today_jscore } = data;
      // 有完成次数的任务
      const limitTasks = [];
      Object.values(growth_tasks).forEach(tasks => {
        tasks.forEach(task => {
          const { limit, score } = task;
          // -1表示无限制,任务可以完成任意次数
          if (limit !== -1) {
            task.maxScore = limit * score;
            limitTasks.push(task);
          }
        });
      });
      // 排序:优先展示单任务分值较大的任务,已完成的任务往后排
      limitTasks.sort((a, b) => {
        if (b.done === b.limit && a.done !== a.limit) return -1;
        if (a.done === a.limit && b.done !== b.limit) return 1;
        if (a.score !== b.score) return b.score - a.score;
        return b.maxScore - a.maxScore;
      });
      return { today_jscore, limitTasks };
    });
  }

  // 查询文章创作话题
  function getArticleThemes() {
    return post('tag_api/v1/theme/list_by_hot', {
      limit: 1000,
      cursor: '',
      theme_type: 1,
    }).then(list => {
      // 按推荐取前两个
      return list.slice(0, 2).map(item => item.theme);
    });
  }

  // 今日状态-是否已签到
  function getIsCheckIn() {
    return get('growth_api/v2/get_today_status').then(data => data.check_in_done);
  }

  // 签到
  function checkIn() {
    return post('growth_api/v1/check_in').then(data => {
      // 增长和总数
      const { incr_point, sum_point } = data;
      return `签到成功:获得${incr_point}矿石。\n`;
    });
  }

  // 抽奖
  function lottery() {
    return post('growth_api/v1/lottery/draw').then(data => {
      const { lottery_name } = data;
      return `抽奖成功:获得${lottery_name}。\n`;
    });
  }

  // 沾喜气
  function dipLucky() {
    // 查询获奖抽奖
    return post('growth_api/v1/lottery_history/global_big', {
      page_no: 1,
      page_size: 1,
    })
      .then(({ lotteries }) => lotteries?.[0]?.history_id)
      .then(lottery_history_id =>
        post('growth_api/v1/lottery_lucky/dip_lucky', {
          lottery_history_id,
        })
      )
      .then(data => {
        // 幸运值
        const { dip_value } = data;
        return `沾喜气:获得${dip_value}幸运值。\n`;
      });
  }

  function getRandomItems(list, count = 1) {
    const items = [];
    const newList = [...list];
    for (let i = 0; i < count; i++) {
      const randomIndex = Math.floor(Math.random() * newList.length);
      items.push(...newList.splice(randomIndex, 1));
    }
    return count === 1 ? items[0] : items;
  }
  const comments = ['666', '哈哈哈哈~~~', '打卡~', '坚持打卡每一天'];

  // 评论沸点
  function publishComments() {
    // 查询5个最新沸点
    return post('recommend_api/v1/short_msg/recommend', {
      id_type: 4,
      sort_type: 300,
      cursor: '0',
      limit: 20,
    }).then(list => {
      return Promise.all([
        // 随机选5条评论
        ...getRandomItems(list, 5).map(msg =>
          post('interact_api/v1/comment/publish', {
            item_id: msg.msg_id,
            item_type: 4,
            comment_content: getRandomItems(comments),
            comment_pics: [],
            client_type: 2608,
          })
        ),
        // 随机选2条点赞
        ...getRandomItems(list, 2).map(msg =>
          post('interact_api/v1/digg/save', {
            item_id: msg.msg_id,
            item_type: 4,
            client_type: 2608,
          })
        ),
      ]);
    });
  }

  // 发布沸点
  function publishShortMsg(content) {
    return post('content_api/v1/short_msg/publish', {
      content,
      sync_to_org: false,
    });
  }

  // 收藏文章至默认收藏集
  function addArticleToDefaultCollectionset(articleId) {
    // 获取收藏集
    return post('interact_api/v2/collectionset/list', {
      limit: 10,
      cursor: '0',
      article_id: articleId,
    }).then(list => {
      const collectionIds = list.map(collection => collection.collection_id);
      const defaultIndex = list.findIndex(collection => collection.is_default);
      const defaultCollectionIds = collectionIds.splice(defaultIndex, 1);
      return post('interact_api/v2/collectionset/add_article', {
        article_id: articleId,
        select_collection_ids: defaultCollectionIds,
        unselect_collection_ids: collectionIds,
        is_collect_fast: false,
      }).then(() => list[defaultIndex]);
    });
  }

  // 文章点赞
  function upvoteArticle(articleId) {
    return post('interact_api/v1/digg/save', {
      item_id: articleId,
      item_type: 2,
      client_type: 2608,
    });
  }

  // 评论文章
  function commentArticle(articleId) {
    return post('interact_api/v1/comment/publish', {
      client_type: 2608,
      item_id: articleId,
      item_type: 2,
      comment_content: '666',
      comment_pics: [],
    });
  }

  // 好文推荐
  function getRecommendArticles() {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        method: 'GET',
        url: 'https://juejin.cn/post/7283028899042017343',
        headers: {
          'Content-Type': 'text/html; charset=utf-8',
        },
        onload: xhr => {
          // 解析内容
          const content = /<h2 data\-id="heading\-0">.*?<ol>(.*?)<\/ol>/s.exec(
            xhr.response
          )?.[1];
          let result = [];
          if (content) {
            result = content.match(/<li>(.*?)<\/li>/gs).map(li => {
              const [, url, name] = /<a href="(.*?)"[^>]+>(.*?)<\/a>/.exec(li);
              return { url, name };
            });
          }
          resolve(result);
        },
        onerror: err => reject(err),
      });
    });
  }

  const messagesQueue = [];
  let currentMessage = null;

  const nextMessage = () => {
    if (messagesQueue.length === 0) return;
    currentMessage = messagesQueue.shift();
    currentMessage.$mount();
  };

  const MessageConstructor = Vue__default['default'].extend({
    props: {
      message: String,
      stay: {
        type: Number,
        default: 3000,
      },
      resolve: Function,
    },
    render(h) {
      return h(
        'div',
        {
          staticClass: 'xx-message-box',
        },
        this.message
      );
    },
    mounted() {
      document.body.appendChild(this.$el);
      setTimeout(() => {
        this.$destroy();
        currentMessage = null;
        nextMessage();
        this.resolve();
      }, this.stay);
    },
    destroyed() {
      this.$el.remove();
    },
  });

  const message = options =>
    new Promise(resolve => {
      if (typeof options === 'string') {
        options = { message: options };
      }
      const instance = new MessageConstructor({
        propsData: { resolve, ...options },
      });
      if (currentMessage) {
        messagesQueue.push(instance);
      } else {
        currentMessage = instance;
        instance.$mount();
      }
    });

  const ConfirmBox = {
    name: 'ConfirmBox',
    props: {
      title: {
        type: String,
        default: '提示',
      },
      content: String,
    },
    render(h) {
      const content = this.$slots.default || this.content;

      return h(
        'div',
        {
          staticClass: 'xx-confirm-box',
        },
        [
          h('div', { staticClass: 'xx-confirm-box-title' }, [
            h('div', { staticClass: 'title' }, this.title),
            h(
              'a',
              { staticClass: 'close', on: { click: this.handleClose } },
              '关闭'
            ),
          ]),
          h('div', { staticClass: 'xx-confirm-box-content' }, [content]),
          h('div', { staticClass: 'xx-confirm-box-footer' }, [
            h('button', { on: { click: this.handleClose } }, '取消'),
            h('button', { on: { click: this.handleConfirm } }, '确认'),
          ]),
        ]
      );
    },
    methods: {
      handleClose(e) {
        this.$emit('close', e);
      },
      handleConfirm(e) {
        this.$emit('confirm', e);
      },
    },
  };

  const ConfirmBoxConstructor = Vue__default['default'].extend(ConfirmBox);

  const confirm = (options = {}) => {
    if (typeof options === 'string') options = { content: options };
    const { content, title, onConfirm, onClose } = options;
    let instance;
    const props = { content, title };
    const on = {};
    const close = () => instance.$destroy();
    on.close = onClose ? () => onClose(close) : close;
    on.confirm = onConfirm ? () => onConfirm(close) : close;

    if (typeof content === 'string') {
      instance = new ConfirmBoxConstructor({ propsData: props, on });
    } else {
      const CompConstructor = Vue__default['default'].extend({
        render: h => h(ConfirmBox, { props, on }, [content]),
      });
      instance = new CompConstructor();
    }

    instance.$on('hook:mounted', () => {
      document.body.appendChild(instance.$el);
    });
    instance.$on('hook:destroyed', () => {
      instance.$el.remove();
    });

    instance.$mount();
  };

  const ActionItem = {
    props: {
      disabled: {
        type: Boolean,
        default: false,
      },
      name: String,
    },
    render(h) {
      return h(
        'div',
        {
          staticClass: 'action-item',
          class: {
            disabled: this.disabled,
          },
          on: {
            click: e => this.$emit('click', e),
          },
        },
        this.name
      );
    },
  };

  var QuickOperates = {
    props: {
      isCheckIn: Boolean,
    },
    data() {
      return {
        recommendArticles: [],
      };
    },
    render(h) {
      // 签到/抽奖
      const CheckIn = h(ActionItem, {
        props: {
          name: this.isCheckIn ? '已签到/抽奖' : '签到/抽奖',
          disabled: this.isCheckIn,
        },
        on: {
          click: e => {
            if (this.isCheckIn) return;
            const p1 = checkIn().then(msg1 => {
              this.$emit('checkIn');
              return lottery().then(msg2 => msg1 + msg2);
            });
            Promise.all([p1, dipLucky()]).then(([msg1, msg2]) => {
              message(msg1 + msg2);
            });
          },
        },
      });
      // 前往我的文章
      const GoMyArticle = h(ActionItem, {
        props: {
          name: '前往我的文章',
        },
        on: {
          click: e => {
            const userId = localStorage.getItem(
              'user_first_visit_dispatch_coupon'
            );
            window.open(`/user/${userId}/posts`);
          },
        },
      });
      // 发布一条沸点
      const PublishShortMsg = h(ActionItem, {
        props: {
          name: '发布一条沸点',
        },
        on: {
          click: e => {
            const content = h(
              'textarea',
              {
                attrs: { rows: '3' },
                style: { width: '300px' },
              },
              '每日打卡,从使用今日掘友分油猴插件开始。\nhttps://juejin.cn/post/7280006996572340283'
            );
            confirm({
              title: '输入沸点内容',
              content,
              onConfirm: close => {
                const msg = content.elm.innerHTML;
                close();
                publishShortMsg(msg).then(msg => {
                  message('发布一条沸点成功').then(() => {
                    window.open(`/pin/${msg.msg_id}`);
                  });
                });
              },
            });
          },
        },
      });
      // 评论/点赞沸点
      const PublishComments = h(ActionItem, {
        props: {
          name: '评论/点赞沸点',
        },
        on: {
          click: e => {
            publishComments().then(() => {
              message('评论/点赞成功');
            });
          },
        },
      });
      // 收藏/点赞/评论文章
      const CollectArticle = h(ActionItem, {
        props: {
          name: '收藏/点赞/评论文章',
        },
        on: {
          click: e => {
            const articleId = location.pathname.split('/').pop();
            addArticleToDefaultCollectionset(articleId).then(
              defaultCollection => {
                message(
                  `文件成功加入到默认收藏集(${defaultCollection.collection_name})中`
                );
              }
            );
            upvoteArticle(articleId).then(() => {
              message('文章点赞成功!');
            });
            commentArticle(articleId).then(() => {
              message('文章评论成功!');
            });
          },
        },
      });

      return h('div', { staticClass: 'quick-operates' }, [
        h('div', { staticClass: 'action-list' }, [
          CheckIn,
          GoMyArticle,
          PublishShortMsg,
          PublishComments,
          CollectArticle,
        ]),
        h('div', { staticClass: 'recommend-articles' }, [
          h('div', { staticClass: 'recommend-articles-title' }, '好文推荐'),
          h(
            'ul',
            {},
            this.recommendArticles.map((article, i) =>
              h('li', {}, [
                h(
                  'a',
                  { attrs: { href: article.url, target: '_blank' } },
                  `${i + 1}.${article.name}`
                ),
              ])
            )
          ),
        ]),
      ]);
    },
    mounted() {
      getRecommendArticles().then(list => {
        this.recommendArticles = list;
      });
    },
  };

  const TaskDetailPopup = {
    props: {
      task: {
        type: Array,
        default() {
          return [];
        },
      },
      themes: {
        type: Array,
        default() {
          return [];
        },
      },
    },

    render(h) {
      const { icon, title, score, done, limit, task_id, web_jump_url } =
        this.task;

      const content = h('div', { staticClass: 'task-detail-popup-content' }, [
        h('div', { staticClass: 'task-icon' }, [
          h('img', { attrs: { src: icon, alt: title } }),
        ]),
        h('div', { staticClass: 'task-info' }, [
          h('span', {}, title),
          h('span', {}, `掘友分+${score},已完成${done}/${limit}`),
        ]),
      ]);
      let extra;
      // 发文
      if (task_id === 5 && this.themes.length) {
        extra = h(
          'div',
          { staticClass: 'task-detail-popup-extra' },
          this.themes.map(theme => {
            const { name, theme_id } = theme;
            return h(
              'a',
              {
                attrs: {
                  href: `${web_jump_url}&theme_id=${theme_id}`,
                  target: '_blank',
                },
                on: {
                  click(e) {
                    e.stopPropagation();
                  },
                },
              },
              `# ${name}`
            );
          })
        );
      }

      return h('div', { staticClass: 'task-detail-popup' }, [content, extra]);
    },
  };

  var ProgressBar = {
    name: 'ProgressBar',

    data() {
      return {
        // 今日已获掘友分
        today_jscore: 0,
        // 有完成次数的任务
        limitTasks: [],
        // 头部区域是否展示
        headerVisible: true,
        themes: [],
        showQuickOperates: false,
        isCheckIn: false,
      };
    },

    render(h) {
      const { themes } = this;
      return h(
        'div',
        { staticClass: 'pl-progressbar', class: { top: !this.headerVisible } },
        [
          // 进度条
          h(
            'div',
            { staticClass: 'pl-progressbar-container' },
            this.limitTasks.map(task => {
              const { done, limit, maxScore } = task;
              // 任务进度
              const taskProgressItems = [];
              for (let i = 0; i < limit; i++) {
                taskProgressItems.push(
                  h('div', {
                    staticClass: 'task-progress-item',
                    class: { 'item-done': i < done },
                  })
                );
              }
              return h(
                'div',
                {
                  staticClass: 'task-item',
                  class: {
                    'all-finished': done === limit,
                    'not-start': done === 0,
                  },
                  // 按比例占据宽度
                  style: {
                    'flex-grow': maxScore,
                  },
                  on: {
                    // 点击任务跳转去完成
                    click() {
                      window.open(task.web_jump_url);
                    },
                  },
                },
                [
                  h(
                    'div',
                    { staticClass: 'progress-item-wrapper' },
                    taskProgressItems
                  ),
                  h(TaskDetailPopup, { props: { task, themes } }),
                ]
              );
            })
          ),
          // 总分
          h('div', { staticClass: 'pl-progressbar-totalscore' }, [
            h(
              'span',
              {
                style: 'line-height: 1; cursor: pointer',
                on: {
                  click: () => {
                    // 关闭
                    if (this.showQuickOperates) {
                      this.showQuickOperates = false;
                    } else {
                      Promise.all([this.getIsCheckIn()]).finally(() => {
                        this.showQuickOperates = true;
                      });
                    }
                  },
                },
              },
              `今日掘友分 ${this.today_jscore}`
            ),
            h(QuickOperates, {
              style: { display: this.showQuickOperates ? '' : 'none' },
              props: {
                isCheckIn: this.isCheckIn,
              },
              on: {
                checkIn: () => (this.isCheckIn = true),
              },
            }),
          ]),
        ]
      );
    },

    mounted() {
      this.observeHeader();
      const loop = () => {
        this.getGrowthTaskList().finally(() => {
          setTimeout(loop, 3000);
        });
      };
      loop();
      this.getArticleThemes();
    },

    methods: {
      // 需要监听头部的展示与隐藏
      observeHeader() {
        const header = document.querySelector('header.main-header');
        const observer = new MutationObserver(mutationsList => {
          const [record] = mutationsList;
          if (record.attributeName !== 'class') return;
          this.headerVisible = header.classList.contains('visible');
        });
        observer.observe(header, {
          attributes: true,
          childList: false,
          subtree: false,
        });
      },
      getGrowthTaskList() {
        return getGrowthTaskList().then(data => {
          Object.assign(this, data);
        });
      },
      getArticleThemes() {
        return getArticleThemes().then(themes => {
          this.themes = themes;
        });
      },
      getIsCheckIn() {
        return getIsCheckIn().then(isCheckIn => {
          this.isCheckIn = isCheckIn;
        });
      },
    },
  };

  ___$insertStyle(".main-header {\n  box-shadow: none !important;\n}\n\n.list-block .list-header.sticky {\n  top: calc(5rem + 12px) !important;\n}\n.list-block .list-header.sticky.top {\n  top: 12px !important;\n}\n\n.view-nav {\n  top: calc(5rem + 12px) !important;\n}\n\n.pl-progressbar {\n  display: flex;\n  justify-content: space-between;\n  padding: 1px;\n  font-size: 12px;\n  height: 10px;\n  color: var(--juejin-brand-1-normal);\n  background: var(--juejin-navigation);\n  position: fixed;\n  top: 5rem;\n  left: 0;\n  right: 0;\n  z-index: 249;\n  transition: transform 0.2s;\n}\n.pl-progressbar.top {\n  transform: translate3d(0, -5rem, 0);\n}\n.pl-progressbar-container {\n  flex-grow: 1;\n  display: flex;\n}\n.pl-progressbar-totalscore {\n  align-self: center;\n  padding: 0 20px;\n  position: relative;\n  user-select: none;\n}\n\nbody[data-theme=dark] .pl-progressbar {\n  background: var(--juejin-navigation);\n}\n\n.task-item {\n  overflow: hidden;\n  display: flex;\n  cursor: pointer;\n}\n.task-item .progress-item-wrapper {\n  position: relative;\n  flex: 1;\n  display: flex;\n}\n.task-item .progress-item-wrapper::before {\n  content: \"\";\n  border: 5px solid var(--juejin-brand-1-normal);\n  border-left: 3px solid var(--juejin-navigation);\n  position: absolute;\n  z-index: 1;\n}\n.task-item .progress-item-wrapper::after {\n  content: \"\";\n  border: solid 5px var(--juejin-navigation);\n  border-left: 3px solid transparent;\n  position: absolute;\n  right: -5px;\n}\n.task-item .task-detail-popup {\n  position: absolute;\n  top: 12px;\n  display: none;\n  background: var(--juejin-navigation);\n  padding: 5px 10px;\n  border-radius: 5px;\n  border: solid 1px;\n  z-index: 999;\n}\n.task-item .task-detail-popup-content {\n  display: flex;\n  align-items: center;\n}\n.task-item .task-detail-popup-content .task-icon {\n  width: 40px;\n  height: 40px;\n  flex-shrink: 0;\n}\n.task-item .task-detail-popup-content .task-icon img {\n  width: 100%;\n  height: 100%;\n}\n.task-item .task-detail-popup-content .task-info {\n  margin-left: 10px;\n  display: flex;\n  flex-direction: column;\n  justify-content: space-between;\n}\n.task-item .task-detail-popup-extra {\n  display: flex;\n  flex-direction: column;\n  margin-top: 5px;\n}\n.task-item.all-finished .progress-item-wrapper::after {\n  border-left-color: var(--juejin-brand-1-normal);\n}\n.task-item.not-start .progress-item-wrapper::before {\n  border-top-color: transparent;\n  border-bottom-color: transparent;\n  border-right-color: transparent;\n}\n.task-item:hover .progress-item-wrapper {\n  opacity: 0.5;\n}\n.task-item:hover .task-detail-popup {\n  display: block;\n}\n.task-item .task-progress-item {\n  flex: 1;\n  background: #f2f3f5;\n  position: relative;\n}\n.task-item .task-progress-item.item-done {\n  background: var(--juejin-brand-1-normal);\n}\n.task-item .task-progress-item + .task-progress-item {\n  margin-left: 1px;\n}\n\n.quick-operates {\n  position: absolute;\n  background: #fff;\n  right: 0;\n  top: 20px;\n  padding: 20px;\n  border-radius: 4px;\n  box-shadow: 0 0 24px rgba(81, 87, 103, 0.16);\n}\n.quick-operates .action-list {\n  width: 160px;\n}\n.quick-operates .action-list .action-item {\n  line-height: 1.8;\n  cursor: pointer;\n}\n.quick-operates .action-list .action-item:hover {\n  background-color: #e8f3ff;\n}\n.quick-operates .action-list .action-item.disabled {\n  color: #777;\n  cursor: not-allowed;\n}\n\n.recommend-articles {\n  margin-top: 5px;\n}\n.recommend-articles-title {\n  font-weight: 700;\n  color: #777;\n  line-height: 2;\n}\n\n.xx-message-box, .xx-confirm-box {\n  position: fixed;\n  top: 80px;\n  left: 0;\n  right: 0;\n  margin: auto;\n  padding: 5px 10px;\n  width: fit-content;\n  background: #e8f3ff;\n  z-index: 2999;\n  color: var(--juejin-brand-1-normal);\n  border-radius: 5px;\n  white-space: pre-wrap;\n  line-height: 1.8;\n  border: solid 1px;\n}\n\n.xx-confirm-box-title {\n  display: flex;\n  justify-content: space-between;\n  padding-bottom: 4px;\n}\n.xx-confirm-box-title .title {\n  flex: 1;\n  font-weight: 600;\n}\n.xx-confirm-box-footer {\n  margin-top: 10px;\n  display: flex;\n}\n.xx-confirm-box-footer button {\n  flex: 1;\n}\n.xx-confirm-box-footer button:first-child {\n  background-color: #7bbdff;\n}");

  const app = new Vue__default['default']({ render: h => h(ProgressBar) }).$mount();

  function insertApp() {
    document.querySelector('.main-header-box').appendChild(app.$el);
  }

  const progressBar = app.$children[0];

  const observeCb = () => {
    insertApp();
    progressBar.observeHeader();
  };

  const observeConfig = { attributes: false, childList: false, subtree: false };

  // 请求完接口再展示dom
  progressBar.getGrowthTaskList().then(() => {
    insertApp();
    // 由于是ssr会导致el被删除
    const observer = new MutationObserver(observeCb);
    observer.observe(document.querySelector('#__nuxt'), {
      ...observeConfig,
      attributes: true,
    });
    // 单页导航
    const observer2 = new MutationObserver(observeCb);
    observer2.observe(document.querySelector('#juejin'), {
      ...observeConfig,
      childList: true,
    });
  });

}(Vue));