批量打開 reddit 的 r/udemyfreeebies 連結(Vue Composition 簡化)

使用 Vue 3 Composition API,合併啟動選項,移除 IIFE,狀態同步

// ==UserScript==
// @name         批量打開 reddit 的 r/udemyfreeebies 連結(Vue Composition 簡化)
// @namespace    http://tampermonkey.net/
// @version      0.13
// @description  使用 Vue 3 Composition API,合併啟動選項,移除 IIFE,狀態同步
// @license      GPL-3.0
// @match        https://www.reddit.com/r/udemyfreeebies/*
// @grant        GM_registerMenuCommand
// @author       twozwu
// ==/UserScript==

let links = [];
let currentIndex = 0;

function loadVue() {
  const vueScript = document.createElement('script');
  vueScript.src = 'https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js';
  vueScript.onload = initApp;
  document.head.appendChild(vueScript);
}

function initApp() {
  const appContainer = document.createElement('div');
  appContainer.id = 'vue-bulk-opener';
  document.body.appendChild(appContainer);

  document.getElementById('vue-bulk-opener').innerHTML = `
    <div id="app" style="
        position: fixed;
        top: 80px;
        right: 20px;
        background: #fff;
        border: 2px solid #ccc;
        border-radius: 12px;
        padding: 15px;
        z-index: 9999;
        font-family: sans-serif;
        width: 260px;
        box-shadow: 0 0 10px rgba(0,0,0,0.2);
    ">
      <h3 style="margin-top: 0;">🔗 批量開啟設定</h3>
      <div>
        <label>每批數量:
          <input type="number" v-model.number="perClick" min="1" style="width: 4rem;">
        </label>
      </div>
      <div style="margin-top: 6px;">
        <label>從第
          <input type="number" v-model.number="indexKey" style="width: auto;">
          個開始
        </label>
        <small style="display:block;color:#666;">(預設為上次進度)</small>
      </div>
      <div style="margin-top:10px;">
        <button @click="runOpener">🚀 開始</button>
        <button @click="closePanel" style="float: right;">❌</button>
      </div>

      <div v-if="controlVisible" style="margin-top:15px;">
        <p>{{ statusText }}</p>
        <button @click="continueBatch">▶️ 繼續</button>
        <button @click="stopBatch">🛑 停止</button>
      </div>
    </div>
  `;

  const { createApp, ref, watch } = Vue;

  createApp({
    setup() {
      const perClick = ref(parseInt(localStorage.getItem('defaultPerClick')) || 15);
      const indexKey = ref(parseInt(localStorage.getItem('indexKey')) || 0);
      const controlVisible = ref(false);
      const statusText = ref('');

      // 同步 currentIndex <-> indexKey 雙向同步
      watch(indexKey, val => {
        currentIndex = val;
      });

      const closePanel = () => {
        document.getElementById('vue-bulk-opener')?.remove();
      };

      const updateStatus = () => {
        if (currentIndex >= links.length) {
          alert("✅ 所有連結已打開完畢!");
          controlVisible.value = false;
          indexKey.value = 0;
          localStorage.removeItem('indexKey');
        } else {
          statusText.value = `已打開 ${currentIndex} / ${links.length},繼續下一批?`;
        }
      };

      const gatherLinks = () => {
        links = [];
        document.querySelectorAll('.text-neutral-content ul li a').forEach(anchor => {
          if (anchor.href) links.push(anchor.href);
        });
        if (links.length === 0) {
          alert("⚠️ 找不到連結!");
        }
      };

      const openBatch = () => {
        const end = Math.min(currentIndex + perClick.value, links.length);
        for (let i = currentIndex; i < end; i++) {
          const win = window.open(links[i], "_blank", "noopener,noreferrer");
          if (win) {
            win.blur();
            window.focus();
          }
        }
        currentIndex = end;
        indexKey.value = end; // 雙向更新
        localStorage.setItem('indexKey', end.toString());
        updateStatus();
      };

      const runOpener = () => {
        localStorage.setItem('defaultPerClick', perClick.value.toString());
        currentIndex = indexKey.value || 0;

        gatherLinks();

        controlVisible.value = true;
        openBatch();
      };

      const continueBatch = () => openBatch();

      const stopBatch = () => {
        alert("⏹️ 已停止,進度已保存");
      };

      return {
        perClick,
        indexKey,
        controlVisible,
        statusText,
        runOpener,
        continueBatch,
        stopBatch,
        closePanel,
      };
    }
  }).mount('#app');
}

GM_registerMenuCommand("📂 開啟連結設定面板", loadVue);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址