Cristy 发表于 2026-3-4 16:35

【油猴脚本】52pj帖子列表增强

本帖最后由 Cristy 于 2026-3-5 10:20 编辑

52pj帖子列表增强**集成设置面板**,将原有多个弹窗菜单整合到一个直观的浮动窗口中,支持所有功能的可视化配置。

便捷安装地址:
https://scriptcat.org/scripts/code/5509/52pj%E5%B8%96%E5%AD%90%E5%88%97%E8%A1%A8%E5%A2%9E%E5%BC%BA.user.js

**2026-03-05**更新
根据坛友 xinchenmetx 的启发,让AI优化了设置面板 更加易于操作, 测试了ScriptCat 和 油猴插件 均正常展示。






下面为原文
---

## 效果:




## 代码:

```javascript
// ==UserScript==
// @name         52pj撒币帖子更显眼(设置面板版)
// @match      https://www.52pojie.cn/*
// @version      2.0
// @description实时监测并修改链接样式,集成可视化设置面板(支持回帖奖励、悬赏已解决、悬赏金额动态样式)
// @AuThor       aura service
// @grant      GM_registerMenuCommand
// @grant      GM_setValue
// @grant      GM_getValue
// @license      ISC
// ==/UserScript==

(function() {
    'use strict';

    // ---------- 配置存储 ----------
    // 回帖奖励样式
    const defaultStyles = { fontWeight: 'bold', color: 'red', fontSize: '16px' };
    let styles = {
      fontWeight: GM_getValue('fontWeight', defaultStyles.fontWeight),
      color: GM_getValue('color', defaultStyles.color),
      fontSize: GM_getValue('fontSize', defaultStyles.fontSize)
    };

    // 悬赏已解决配置
    const defaultResolvedAction = 'mark';
    const defaultResolvedBgColor = '#f0f0f0';
    let resolvedAction = GM_getValue('resolvedAction', defaultResolvedAction);
    let resolvedBgColor = GM_getValue('resolvedBgColor', defaultResolvedBgColor);

    // 悬赏金额动态样式配置
    const REWARD_ENABLED_KEY = 'rewardEnabled';
    const REWARD_RULES_KEY = 'rewardRules';
    const defaultRewardRules = [
      { min: 1, max: 9, style: { color: 'green' } },
      { min: 10, max: 49, style: { color: 'orange', fontWeight: 'bold' } },
      { min: 50, max: 99, style: { color: 'red', fontWeight: 'bold', fontSize: '16px' } },
      { min: 100, max: null, style: { color: 'purple', fontWeight: 'bold', fontSize: '18px', backgroundColor: '#ffffcc' } }
    ];
    let rewardEnabled = GM_getValue(REWARD_ENABLED_KEY, false);
    let rewardRules = GM_getValue(REWARD_RULES_KEY, defaultRewardRules);

    // ---------- 辅助函数 ----------
    function getStyleForReward(amount) {
      if (!rewardEnabled) return null;
      for (const rule of rewardRules) {
            if (amount >= rule.min && (rule.max === null || amount <= rule.max)) {
                return rule.style;
            }
      }
      return null;
    }

    // ---------- 核心处理函数(同之前)----------
    function checkAndModify() {
      const rows = document.querySelectorAll('tbody tr');
      rows.forEach(row => {
            // 回帖奖励
            const ths = row.querySelectorAll('th.common, th.new');
            let hasReward = false;
            ths.forEach(th => {
                const span = th.querySelector('span');
                if (span && span.textContent.includes('回帖奖励')) hasReward = true;
            });
            if (hasReward) {
                const links = row.querySelectorAll('th.common a.s.xst, th.new a.s.xst');
                links.forEach(link => {
                  link.style.fontWeight = styles.fontWeight;
                  link.style.color = styles.color;
                  link.style.fontSize = styles.fontSize;
                });
            }

            // 悬赏已解决标记/隐藏
            const rewardIcon = row.querySelector('td.icn img');
            if (rewardIcon) {
                const resolvedLink = Array.from(row.querySelectorAll('th a')).find(a => a.textContent.trim() === '[已解决]');
                if (resolvedLink) {
                  if (resolvedAction === 'mark') row.style.backgroundColor = resolvedBgColor;
                  else if (resolvedAction === 'hide') row.style.display = 'none';
                }
            }

            // 悬赏金额动态样式
            if (rewardEnabled && rewardIcon) {
                const rewardLink = row.querySelector('th a');
                if (rewardLink) {
                  const amountSpan = rewardLink.querySelector('span.xw1');
                  if (amountSpan) {
                        const amount = parseInt(amountSpan.textContent.trim(), 10);
                        if (!isNaN(amount)) {
                            const style = getStyleForReward(amount);
                            if (style) {
                              const titleLink = row.querySelector('th.common a.s.xst, th.new a.s.xst');
                              if (titleLink) {
                                    for (const of Object.entries(style)) {
                                        titleLink.style = val;
                                    }
                              }
                            }
                        }
                  }
                }
            }
      });
    }

    // ---------- 设置面板 ----------
    let settingsPanel = null;

    function createSettingsPanel() {
      if (settingsPanel) {
            settingsPanel.style.display = settingsPanel.style.display === 'none' ? 'block' : 'none';
            return;
      }

      // 创建面板容器
      const panel = document.createElement('div');
      panel.id = 'pj-settings-panel';
      Object.assign(panel.style, {
            position: 'fixed',
            top: '60px',
            right: '20px',
            width: '400px',
            maxHeight: '80vh',
            overflowY: 'auto',
            backgroundColor: '#fff',
            border: '1px solid #ccc',
            borderRadius: '8px',
            boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
            padding: '16px',
            zIndex: '9999',
            fontFamily: 'Arial, sans-serif',
            fontSize: '14px',
            display: 'block'
      });

      // 标题 + 关闭按钮
      const header = document.createElement('div');
      Object.assign(header.style, {
            display: 'flex',
            justifyContent: 'space-between',
            alignItems: 'center',
            marginBottom: '16px',
            paddingBottom: '8px',
            borderBottom: '1px solid #eee'
      });
      header.innerHTML = '<h3 style="margin:0;">52pj 帖子增强设置</h3>';
      const closeBtn = document.createElement('button');
      closeBtn.textContent = '✕';
      Object.assign(closeBtn.style, {
            background: 'none',
            border: 'none',
            fontSize: '18px',
            cursor: 'pointer',
            padding: '0 6px'
      });
      closeBtn.onclick = () => panel.style.display = 'none';
      header.appendChild(closeBtn);
      panel.appendChild(header);

      // ---------- 回帖奖励样式 ----------
      const section1 = document.createElement('div');
      section1.innerHTML = '<h4 style="margin:12px 0 8px;">回帖奖励样式</h4>';
      const form1 = document.createElement('div');
      form1.style.display = 'grid';
      form1.style.gridTemplateColumns = '80px 1fr';
      form1.style.gap = '8px';
      form1.style.marginBottom = '8px';

      // 字体粗细
      form1.appendChild(document.createTextNode('字体粗细:'));
      const inputWeight = document.createElement('input');
      inputWeight.type = 'text';
      inputWeight.value = styles.fontWeight;
      form1.appendChild(inputWeight);

      // 颜色
      form1.appendChild(document.createTextNode('颜色:'));
      const inputColor = document.createElement('input');
      inputColor.type = 'text';
      inputColor.value = styles.color;
      form1.appendChild(inputColor);

      // 字体大小
      form1.appendChild(document.createTextNode('字体大小:'));
      const inputSize = document.createElement('input');
      inputSize.type = 'text';
      inputSize.value = styles.fontSize;
      form1.appendChild(inputSize);

      section1.appendChild(form1);
      panel.appendChild(section1);

      // ---------- 悬赏已解决处理 ----------
      const section2 = document.createElement('div');
      section2.innerHTML = '<h4 style="margin:16px 0 8px;">悬赏已解决处理</h4>';
      const form2 = document.createElement('div');
      form2.style.marginBottom = '8px';

      // 处理方式单选
      const radioNone = document.createElement('input');
      radioNone.type = 'radio';
      radioNone.name = 'resolvedAction';
      radioNone.value = 'none';
      radioNone.checked = resolvedAction === 'none';
      const labelNone = document.createTextNode('无操作 ');

      const radioMark = document.createElement('input');
      radioMark.type = 'radio';
      radioMark.name = 'resolvedAction';
      radioMark.value = 'mark';
      radioMark.checked = resolvedAction === 'mark';
      const labelMark = document.createTextNode('标记背景 ');

      const radioHide = document.createElement('input');
      radioHide.type = 'radio';
      radioHide.name = 'resolvedAction';
      radioHide.value = 'hide';
      radioHide.checked = resolvedAction === 'hide';
      const labelHide = document.createTextNode('隐藏整行 ');

      form2.appendChild(radioNone);
      form2.appendChild(labelNone);
      form2.appendChild(radioMark);
      form2.appendChild(labelMark);
      form2.appendChild(radioHide);
      form2.appendChild(labelHide);
      form2.appendChild(document.createElement('br'));

      // 背景色
      const bgColorLabel = document.createTextNode('背景色: ');
      const inputBgColor = document.createElement('input');
      inputBgColor.type = 'text';
      inputBgColor.value = resolvedBgColor;
      inputBgColor.style.width = '100px';
      form2.appendChild(bgColorLabel);
      form2.appendChild(inputBgColor);

      section2.appendChild(form2);
      panel.appendChild(section2);

      // ---------- 悬赏金额动态样式 ----------
      const section3 = document.createElement('div');
      section3.innerHTML = '<h4 style="margin:16px 0 8px;">悬赏金额动态样式</h4>';
      const form3 = document.createElement('div');
      form3.style.marginBottom = '8px';

      // 启用开关
      const enableCheck = document.createElement('input');
      enableCheck.type = 'checkbox';
      enableCheck.checked = rewardEnabled;
      const enableLabel = document.createTextNode(' 启用');
      form3.appendChild(enableCheck);
      form3.appendChild(enableLabel);
      form3.appendChild(document.createElement('br'));

      // 规则编辑(JSON文本域)
      const rulesLabel = document.createTextNode('规则 (JSON):');
      form3.appendChild(rulesLabel);
      form3.appendChild(document.createElement('br'));

      const rulesTextarea = document.createElement('textarea');
      rulesTextarea.value = JSON.stringify(rewardRules, null, 2);
      Object.assign(rulesTextarea.style, {
            width: '100%',
            height: '150px',
            fontFamily: 'monospace',
            fontSize: '12px',
            marginTop: '4px'
      });
      form3.appendChild(rulesTextarea);

      // 重置默认规则按钮
      const resetBtn = document.createElement('button');
      resetBtn.textContent = '恢复默认规则';
      resetBtn.style.marginTop = '4px';
      resetBtn.onclick = () => {
            rulesTextarea.value = JSON.stringify(defaultRewardRules, null, 2);
      };
      form3.appendChild(resetBtn);

      section3.appendChild(form3);
      panel.appendChild(section3);

      // ---------- 保存/取消按钮 ----------
      const btnGroup = document.createElement('div');
      btnGroup.style.display = 'flex';
      btnGroup.style.justifyContent = 'flex-end';
      btnGroup.style.gap = '8px';
      btnGroup.style.marginTop = '20px';

      const saveBtn = document.createElement('button');
      saveBtn.textContent = '保存';
      Object.assign(saveBtn.style, {
            padding: '6px 16px',
            backgroundColor: '#4CAF50',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer'
      });
      saveBtn.onclick = () => {
            // 回帖奖励样式
            styles.fontWeight = inputWeight.value;
            styles.color = inputColor.value;
            styles.fontSize = inputSize.value;
            GM_setValue('fontWeight', styles.fontWeight);
            GM_setValue('color', styles.color);
            GM_setValue('fontSize', styles.fontSize);

            // 悬赏已解决
            const selectedAction = document.querySelector('input:checked')?.value;
            if (selectedAction) {
                resolvedAction = selectedAction;
                GM_setValue('resolvedAction', resolvedAction);
            }
            resolvedBgColor = inputBgColor.value;
            GM_setValue('resolvedBgColor', resolvedBgColor);

            // 悬赏金额
            rewardEnabled = enableCheck.checked;
            GM_setValue(REWARD_ENABLED_KEY, rewardEnabled);
            try {
                const parsed = JSON.parse(rulesTextarea.value);
                if (Array.isArray(parsed) && parsed.every(r => typeof r.min === 'number' && r.style)) {
                  rewardRules = parsed;
                  GM_setValue(REWARD_RULES_KEY, rewardRules);
                } else {
                  alert('规则格式无效,请检查后重试');
                  return;
                }
            } catch (e) {
                alert('JSON 解析失败: ' + e.message);
                return;
            }

            alert('设置已保存!');
            panel.style.display = 'none';
            // 立即重新应用样式
            checkAndModify();
      };

      const cancelBtn = document.createElement('button');
      cancelBtn.textContent = '关闭';
      Object.assign(cancelBtn.style, {
            padding: '6px 16px',
            backgroundColor: '#f44336',
            color: 'white',
            border: 'none',
            borderRadius: '4px',
            cursor: 'pointer'
      });
      cancelBtn.onclick = () => panel.style.display = 'none';

      btnGroup.appendChild(saveBtn);
      btnGroup.appendChild(cancelBtn);
      panel.appendChild(btnGroup);

      document.body.appendChild(panel);
      settingsPanel = panel;
    }

    // ---------- 注册菜单命令 ----------
    GM_registerMenuCommand("⚙️ 打开设置面板", createSettingsPanel);

    // ---------- 启动核心功能 ----------
    const observer = new MutationObserver(checkAndModify);
    observer.observe(document.body, { childList: true, subtree: true });
    checkAndModify();
    setInterval(checkAndModify, 1000);

})();
```

---

## &#127919; 主要改进

### 1. 集成设置面板
- 通过油猴菜单命令 **⚙️ 打开设置面板** 调出浮动窗口。
- 面板固定在页面右上角,可关闭,不干扰正常浏览。
- 所有配置项集中在一个界面中,告别多个弹窗。

### 2. 可视化控件
- **回帖奖励样式**:三个输入框分别控制字体粗细、颜色、大小。
- **悬赏已解决处理**:单选按钮选择处理方式(无操作/标记背景/隐藏),并支持自定义背景色。
- **悬赏金额动态样式**:
- 复选框启用/禁用功能。
- 文本域直接编辑 JSON 规则,带语法高亮(虽无高亮,但可手动调整)。
- **恢复默认规则** 按钮一键填充默认配置。

### 3. 即时生效
- 点击 **保存** 后,所有配置通过 `GM_setValue` 持久化,并立即调用 `checkAndModify()` 刷新页面样式。
- 点击 **关闭** 仅隐藏面板,不丢失已保存的设置。

### 4. 保留原有核心功能
- 定时器 + MutationObserver 双重保障,确保动态加载内容(翻页、异步更新)也能应用样式。
- 所有处理逻辑(回帖奖励、已解决处理、金额样式)保持不变,仅将配置方式从 `prompt` 升级为面板。

---

## &#128204; 使用说明
1. 安装脚本后,访问 52pojie.cn 任意页面。
2. 点击 Tampermonkey 图标 → 选择 **⚙️ 打开设置面板**。
3. 在面板中调整各项参数,点击 **保存** 即可看到效果。
4. 如需修改,再次打开面板调整并保存。

---

## &#128295; 自定义扩展
- 您可以根据需要修改面板的样式(如宽度、颜色、位置)。
- 若希望规则编辑更友好,可替换文本域为可编辑表格,但 JSON 方式已足够灵活,且能完整保留原数据结构。


xinchenmetx 发表于 2026-3-4 17:55

本帖最后由 xinchenmetx 于 2026-3-6 01:16 编辑

用AI优化了界面
// ==UserScript==
// @name         52pj帖子列表增强
// @match      https://www.52pojie.cn/*
// @version      3.1
// @description实时监测并修改链接样式,集成可视化设置面板(支持回帖奖励、悬赏已解决、悬赏金额动态样式)
// @author       aura service
// @grant      GM_registerMenuCommand
// @grant      GM_setValue
// @grant      GM_getValue
// @license      ISC
// ==/UserScript==

(function() {
    'use strict';

    // ---------- 配置存储 ----------
    const defaultStyles = { fontWeight: 'bold', color: '#ff0000', fontSize: '16px' };
    let styles = {
      fontWeight: GM_getValue('fontWeight', defaultStyles.fontWeight),
      color: GM_getValue('color', defaultStyles.color),
      fontSize: GM_getValue('fontSize', defaultStyles.fontSize)
    };

    const defaultResolvedAction = 'mark';
    const defaultResolvedBgColor = '#f0f0f0';
    let resolvedAction = GM_getValue('resolvedAction', defaultResolvedAction);
    let resolvedBgColor = GM_getValue('resolvedBgColor', defaultResolvedBgColor);

    const REWARD_ENABLED_KEY = 'rewardEnabled';
    const REWARD_RULES_KEY = 'rewardRules';
    const defaultRewardRules = [
      { min: 1, max: 9, style: { color: '#008000' } },
      { min: 10, max: 49, style: { color: '#ffa500', fontWeight: 'bold' } },
      { min: 50, max: 99, style: { color: '#ff0000', fontWeight: 'bold', fontSize: '16px' } },
      { min: 100, max: null, style: { color: '#800080', fontWeight: 'bold', fontSize: '18px', backgroundColor: '#ffffcc' } }
    ];
    let rewardEnabled = GM_getValue(REWARD_ENABLED_KEY, false);
    let rewardRules = GM_getValue(REWARD_RULES_KEY, defaultRewardRules);

    // ---------- 辅助函数 ----------
    function getStyleForReward(amount) {
      if (!rewardEnabled) return null;
      for (const rule of rewardRules) {
            if (amount >= rule.min && (rule.max === null || amount <= rule.max)) {
                return rule.style;
            }
      }
      return null;
    }

    // ---------- 核心处理函数 ----------
    function checkAndModify() {
      const rows = document.querySelectorAll('#threadlisttableid tbody tr, tbody tr');
      rows.forEach(row => {
            // 回帖奖励
            const ths = row.querySelectorAll('th.common, th.new');
            let hasReward = false;
            ths.forEach(th => {
                const span = th.querySelector('span');
                if (span && span.textContent.includes('回帖奖励')) hasReward = true;
            });
            if (hasReward) {
                const links = row.querySelectorAll('th.common a.s.xst, th.new a.s.xst');
                links.forEach(link => {
                  link.style.fontWeight = styles.fontWeight;
                  link.style.color = styles.color;
                  link.style.fontSize = styles.fontSize;
                });
            }

            // 悬赏已解决标记/隐藏
            const rewardIcon = row.querySelector('td.icn img');
            if (rewardIcon) {
                const resolvedLink = Array.from(row.querySelectorAll('th a')).find(a => a.textContent.trim() === '[已解决]');
                if (resolvedLink) {
                  if (resolvedAction === 'mark') row.style.backgroundColor = resolvedBgColor;
                  else if (resolvedAction === 'hide') row.style.display = 'none';
                }
            }

            // 悬赏金额动态样式
            if (rewardEnabled && rewardIcon) {
                const rewardLink = row.querySelector('th a');
                if (rewardLink) {
                  const amountSpan = rewardLink.querySelector('span.xw1');
                  if (amountSpan) {
                        const amount = parseInt(amountSpan.textContent.trim(), 10);
                        if (!isNaN(amount)) {
                            const style = getStyleForReward(amount);
                            if (style) {
                              const titleLink = row.querySelector('th.common a.s.xst, th.new a.s.xst');
                              if (titleLink) {
                                    for (const of Object.entries(style)) {
                                        titleLink.style = val;
                                    }
                              }
                            }
                        }
                  }
                }
            }
      });
    }

    // ---------- 设置面板 ----------
    let settingsPanel = null;
    let overlay = null;

    function createSettingsPanel() {
      if (settingsPanel) {
            settingsPanel.style.display = settingsPanel.style.display === 'none' ? 'flex' : 'none';
            if (overlay) overlay.style.display = settingsPanel.style.display === 'none' ? 'none' : 'block';
            return;
      }

      // 创建遮罩层
      overlay = document.createElement('div');
      overlay.id = 'pj-settings-overlay';
      Object.assign(overlay.style, {
            position: 'fixed',
            top: '0',
            left: '0',
            width: '100%',
            height: '100%',
            backgroundColor: 'rgba(0,0,0,0.5)',
            zIndex: '9998',
            display: 'none'
      });
      overlay.onclick = () => {
            settingsPanel.style.display = 'none';
            overlay.style.display = 'none';
      };
      document.body.appendChild(overlay);

      // 主面板容器
      const panel = document.createElement('div');
      panel.id = 'pj-settings-panel';
      Object.assign(panel.style, {
            position: 'fixed',
            top: '50%',
            left: '50%',
            transform: 'translate(-50%, -50%)',
            width: '520px',
            maxHeight: '85vh',
            backgroundColor: '#ffffff',
            borderRadius: '12px',
            boxShadow: '0 20px 60px rgba(0,0,0,0.3)',
            zIndex: '9999',
            fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
            fontSize: '14px',
            display: 'flex',
            flexDirection: 'column',
            overflow: 'hidden'
      });

      // 标题栏
      const header = document.createElement('div');
      Object.assign(header.style, {
            display: 'flex',
            justifyContent: 'space-between',
            alignItems: 'center',
            padding: '20px 24px',
            backgroundColor: '#f8f9fa',
            borderBottom: '1px solid #e9ecef'
      });

      const title = document.createElement('h3');
      title.textContent = '⚙️ 52pj 帖子增强设置';
      Object.assign(title.style, {
            margin: '0',
            fontSize: '18px',
            fontWeight: '600',
            color: '#212529'
      });

      const closeBtn = document.createElement('button');
      closeBtn.innerHTML = '✕';
      Object.assign(closeBtn.style, {
            background: 'none',
            border: 'none',
            fontSize: '20px',
            color: '#6c757d',
            cursor: 'pointer',
            padding: '4px 8px',
            borderRadius: '4px',
            transition: 'all 0.2s'
      });
      closeBtn.onmouseover = () => closeBtn.style.backgroundColor = '#e9ecef';
      closeBtn.onmouseout = () => closeBtn.style.backgroundColor = 'transparent';
      closeBtn.onclick = () => {
            panel.style.display = 'none';
            overlay.style.display = 'none';
      };

      header.appendChild(title);
      header.appendChild(closeBtn);
      panel.appendChild(header);

      // 标签页导航
      const tabNav = document.createElement('div');
      Object.assign(tabNav.style, {
            display: 'flex',
            borderBottom: '1px solid #dee2e6',
            backgroundColor: '#fff',
            padding: '0 24px'
      });

      const tabs = [
            { id: 'reply', label: '&#128172; 回帖奖励', icon: '' },
            { id: 'resolved', label: '✅ 悬赏已解决', icon: '' },
            { id: 'reward', label: '&#128176; 悬赏金额', icon: '' }
      ];

      let activeTab = 'reply';
      const tabButtons = {};
      const tabContents = {}; // 提前定义

      function switchTab(tabId) {
            activeTab = tabId;
            // 更新按钮样式
            Object.keys(tabButtons).forEach(key => {
                const btn = tabButtons;
                if (key === tabId) {
                  btn.style.color = '#0d6efd';
                  btn.style.borderBottomColor = '#0d6efd';
                } else {
                  btn.style.color = '#6c757d';
                  btn.style.borderBottomColor = 'transparent';
                }
            });
            // 显示对应内容
            Object.keys(tabContents).forEach(key => {
                tabContents.style.display = key === tabId ? 'block' : 'none';
            });
      }

      tabs.forEach(tab => {
            const btn = document.createElement('button');
            btn.textContent = tab.label;
            Object.assign(btn.style, {
                padding: '12px 16px',
                border: 'none',
                background: 'none',
                cursor: 'pointer',
                fontSize: '14px',
                fontWeight: '500',
                color: '#6c757d',
                borderBottom: '2px solid transparent',
                transition: 'all 0.2s',
                marginRight: '8px'
            });

            btn.onclick = () => switchTab(tab.id);
            tabButtons = btn;
            tabNav.appendChild(btn);
      });

      panel.appendChild(tabNav);

      // 内容区域
      const contentArea = document.createElement('div');
      Object.assign(contentArea.style, {
            flex: '1',
            overflowY: 'auto',
            padding: '24px'
      });

      // ========== 标签页 1: 回帖奖励样式 ==========
      const replyTab = document.createElement('div');
      tabContents.reply = replyTab;

      // 预览区域
      const previewBox1 = createPreviewBox();
      const previewText1 = document.createElement('a');
      previewText1.textContent = '这是一个回帖奖励帖子的预览';
      previewText1.href = '#';
      Object.assign(previewText1.style, {
            textDecoration: 'none',
            cursor: 'default'
      });
      previewBox1.appendChild(previewText1);

      // 表单
      const form1 = document.createElement('div');
      Object.assign(form1.style, {
            display: 'flex',
            flexDirection: 'column',
            gap: '16px'
      });

      // 颜色选择
      const colorGroup = createFormGroup('文字颜色');
      const colorWrapper = document.createElement('div');
      Object.assign(colorWrapper.style, {
            display: 'flex',
            alignItems: 'center',
            gap: '8px'
      });

      const colorInput = document.createElement('input');
      colorInput.type = 'color';
      colorInput.value = rgbToHex(styles.color) || styles.color;
      Object.assign(colorInput.style, {
            width: '50px',
            height: '36px',
            border: '1px solid #ced4da',
            borderRadius: '4px',
            cursor: 'pointer'
      });

      const colorText = document.createElement('input');
      colorText.type = 'text';
      colorText.value = styles.color;
      Object.assign(colorText.style, {
            flex: '1',
            padding: '8px 12px',
            border: '1px solid #ced4da',
            borderRadius: '4px',
            fontSize: '14px'
      });

      colorInput.oninput = (e) => {
            colorText.value = e.target.value;
            updatePreview1();
      };
      colorText.oninput = (e) => {
            colorInput.value = rgbToHex(e.target.value) || e.target.value;
            updatePreview1();
      };

      colorWrapper.appendChild(colorInput);
      colorWrapper.appendChild(colorText);
      colorGroup.appendChild(colorWrapper);
      form1.appendChild(colorGroup);

      // 字体粗细
      const weightGroup = createFormGroup('字体粗细');
      const weightSelect = document.createElement('select');
      const weights = [
            { value: 'normal', label: '正常 (Normal)' },
            { value: 'bold', label: '粗体 (Bold)' },
            { value: 'bolder', label: '更粗 (Bolder)' },
            { value: 'lighter', label: '更细 (Lighter)' },
            { value: '100', label: '100 - Thin' },
            { value: '400', label: '400 - Regular' },
            { value: '700', label: '700 - Bold' },
            { value: '900', label: '900 - Black' }
      ];
      weights.forEach(w => {
            const opt = document.createElement('option');
            opt.value = w.value;
            opt.textContent = w.label;
            if (w.value === styles.fontWeight) opt.selected = true;
            weightSelect.appendChild(opt);
      });
      Object.assign(weightSelect.style, {
            width: '100%',
            padding: '8px 12px',
            border: '1px solid #ced4da',
            borderRadius: '4px',
            fontSize: '14px',
            backgroundColor: '#fff'
      });
      weightSelect.onchange = updatePreview1;
      weightGroup.appendChild(weightSelect);
      form1.appendChild(weightGroup);

      // 字体大小
      const sizeGroup = createFormGroup('字体大小');
      const sizeWrapper = document.createElement('div');
      Object.assign(sizeWrapper.style, {
            display: 'flex',
            alignItems: 'center',
            gap: '12px'
      });

      const sizeInput = document.createElement('input');
      sizeInput.type = 'range';
      sizeInput.min = '12';
      sizeInput.max = '24';
      sizeInput.value = parseInt(styles.fontSize) || 16;
      Object.assign(sizeInput.style, {
            flex: '1',
            cursor: 'pointer'
      });

      const sizeValue = document.createElement('span');
      sizeValue.textContent = styles.fontSize;
      Object.assign(sizeValue.style, {
            minWidth: '50px',
            textAlign: 'center',
            fontWeight: '600',
            color: '#495057'
      });

      sizeInput.oninput = (e) => {
            sizeValue.textContent = e.target.value + 'px';
            updatePreview1();
      };

      sizeWrapper.appendChild(sizeInput);
      sizeWrapper.appendChild(sizeValue);
      sizeGroup.appendChild(sizeWrapper);
      form1.appendChild(sizeGroup);

      replyTab.appendChild(previewBox1);
      replyTab.appendChild(form1);

      function updatePreview1() {
            previewText1.style.color = colorText.value;
            previewText1.style.fontWeight = weightSelect.value;
            previewText1.style.fontSize = sizeValue.textContent;
      }
      updatePreview1();

      // ========== 标签页 2: 悬赏已解决 ==========
      const resolvedTab = document.createElement('div');
      tabContents.resolved = resolvedTab;
      resolvedTab.style.display = 'none';

      const form2 = document.createElement('div');
      Object.assign(form2.style, {
            display: 'flex',
            flexDirection: 'column',
            gap: '20px'
      });

      // 操作方式卡片
      const actionGroup = createFormGroup('处理方式');
      const actionCards = document.createElement('div');
      Object.assign(actionCards.style, {
            display: 'grid',
            gridTemplateColumns: 'repeat(3, 1fr)',
            gap: '12px'
      });

      const actions = [
            { value: 'none', label: '无操作', desc: '保持原样', icon: '⭕' },
            { value: 'mark', label: '标记背景', desc: '改变背景色', icon: '&#127912;' },
            { value: 'hide', label: '隐藏整行', desc: '完全隐藏', icon: '&#128584;' }
      ];

      let selectedAction = resolvedAction;
      const actionCardEls = {};

      actions.forEach(action => {
            const card = document.createElement('div');
            Object.assign(card.style, {
                padding: '16px',
                border: '2px solid ' + (action.value === selectedAction ? '#0d6efd' : '#dee2e6'),
                borderRadius: '8px',
                cursor: 'pointer',
                textAlign: 'center',
                transition: 'all 0.2s',
                backgroundColor: action.value === selectedAction ? '#f8f9ff' : '#fff'
            });

            const icon = document.createElement('div');
            icon.textContent = action.icon;
            Object.assign(icon.style, {
                fontSize: '24px',
                marginBottom: '8px'
            });

            const label = document.createElement('div');
            label.textContent = action.label;
            Object.assign(label.style, {
                fontWeight: '600',
                marginBottom: '4px',
                color: '#212529'
            });

            const desc = document.createElement('div');
            desc.textContent = action.desc;
            Object.assign(desc.style, {
                fontSize: '12px',
                color: '#6c757d'
            });

            card.appendChild(icon);
            card.appendChild(label);
            card.appendChild(desc);

            card.onclick = () => {
                selectedAction = action.value;
                Object.keys(actionCardEls).forEach(key => {
                  const el = actionCardEls;
                  if (key === action.value) {
                        el.style.borderColor = '#0d6efd';
                        el.style.backgroundColor = '#f8f9ff';
                  } else {
                        el.style.borderColor = '#dee2e6';
                        el.style.backgroundColor = '#fff';
                  }
                });
                bgColorGroup.style.opacity = action.value === 'mark' ? '1' : '0.5';
                bgColorGroup.style.pointerEvents = action.value === 'mark' ? 'auto' : 'none';
            };

            actionCards.appendChild(card);
            actionCardEls = card;
      });

      actionGroup.appendChild(actionCards);
      form2.appendChild(actionGroup);

      // 背景色选择
      const bgColorGroup = createFormGroup('标记背景色');
      Object.assign(bgColorGroup.style, {
            opacity: selectedAction === 'mark' ? '1' : '0.5',
            pointerEvents: selectedAction === 'mark' ? 'auto' : 'none',
            transition: 'opacity 0.2s'
      });

      const bgColorWrapper = document.createElement('div');
      Object.assign(bgColorWrapper.style, {
            display: 'flex',
            alignItems: 'center',
            gap: '12px'
      });

      const bgColorInput = document.createElement('input');
      bgColorInput.type = 'color';
      bgColorInput.value = rgbToHex(resolvedBgColor) || resolvedBgColor;
      Object.assign(bgColorInput.style, {
            width: '60px',
            height: '40px',
            border: '1px solid #ced4da',
            borderRadius: '4px',
            cursor: 'pointer'
      });

      const bgColorPreview = document.createElement('div');
      Object.assign(bgColorPreview.style, {
            flex: '1',
            height: '40px',
            borderRadius: '4px',
            border: '1px solid #ced4da',
            backgroundColor: resolvedBgColor
      });

      bgColorInput.oninput = (e) => {
            bgColorPreview.style.backgroundColor = e.target.value;
      };

      bgColorWrapper.appendChild(bgColorInput);
      bgColorWrapper.appendChild(bgColorPreview);
      bgColorGroup.appendChild(bgColorWrapper);
      form2.appendChild(bgColorGroup);

      resolvedTab.appendChild(form2);

      // ========== 标签页 3: 悬赏金额动态样式 ==========
      const rewardTab = document.createElement('div');
      tabContents.reward = rewardTab;
      rewardTab.style.display = 'none';

      // 启用开关
      const enableGroup = document.createElement('div');
      Object.assign(enableGroup.style, {
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'space-between',
            padding: '16px',
            backgroundColor: '#f8f9fa',
            borderRadius: '8px',
            marginBottom: '20px'
      });

      const enableLabel = document.createElement('div');
      enableLabel.innerHTML = '<strong>启用悬赏金额高亮</strong><br><span style="font-size:12px;color:#6c757d;">根据悬赏金额自动设置不同样式</span>';

      const enableSwitch = createToggleSwitch(rewardEnabled);
      // 修复:使用 querySelector 获取 input 元素绑定事件
      enableSwitch.querySelector('input').addEventListener('change', (e) => {
            rulesContainer.style.opacity = e.target.checked ? '1' : '0.5';
            rulesContainer.style.pointerEvents = e.target.checked ? 'auto' : 'none';
      });

      enableGroup.appendChild(enableLabel);
      enableGroup.appendChild(enableSwitch);
      rewardTab.appendChild(enableGroup);

      // 规则容器
      const rulesContainer = document.createElement('div');
      Object.assign(rulesContainer.style, {
            opacity: rewardEnabled ? '1' : '0.5',
            pointerEvents: rewardEnabled ? 'auto' : 'none',
            transition: 'opacity 0.2s'
      });

      const rulesHeader = document.createElement('div');
      rulesHeader.innerHTML = '<strong>金额区间规则</strong><span style="float:right;font-size:12px;color:#6c757d;">优先级从上到下</span>';
      Object.assign(rulesHeader.style, {
            marginBottom: '12px',
            paddingBottom: '8px',
            borderBottom: '1px solid #dee2e6'
      });
      rulesContainer.appendChild(rulesHeader);

      const rulesList = document.createElement('div');
      Object.assign(rulesList.style, {
            display: 'flex',
            flexDirection: 'column',
            gap: '12px'
      });

      let ruleEditors = [];

      function createRuleEditor(rule, index) {
            const editor = document.createElement('div');
            Object.assign(editor.style, {
                padding: '16px',
                backgroundColor: '#f8f9fa',
                borderRadius: '8px',
                border: '1px solid #e9ecef',
                position: 'relative'
            });

            // 头部:范围
            const header = document.createElement('div');
            Object.assign(header.style, {
                display: 'flex',
                alignItems: 'center',
                gap: '8px',
                marginBottom: '12px'
            });

            const rangeLabel = document.createElement('span');
            rangeLabel.textContent = '金额范围:';

            const minInput = document.createElement('input');
            minInput.type = 'number';
            minInput.value = rule.min;
            Object.assign(minInput.style, {
                width: '70px',
                padding: '6px',
                border: '1px solid #ced4da',
                borderRadius: '4px'
            });

            const sep = document.createElement('span');
            sep.textContent = '-';

            const maxInput = document.createElement('input');
            maxInput.type = 'text';
            maxInput.value = rule.max === null ? '∞' : rule.max;
            maxInput.placeholder = '无上限';
            Object.assign(maxInput.style, {
                width: '70px',
                padding: '6px',
                border: '1px solid #ced4da',
                borderRadius: '4px'
            });

            header.appendChild(rangeLabel);
            header.appendChild(minInput);
            header.appendChild(sep);
            header.appendChild(maxInput);

            // 删除按钮
            if (index > 0) {
                const deleteBtn = document.createElement('button');
                deleteBtn.innerHTML = '&#128465;️';
                Object.assign(deleteBtn.style, {
                  marginLeft: 'auto',
                  background: '#dc3545',
                  border: 'none',
                  color: 'white',
                  padding: '4px 8px',
                  borderRadius: '4px',
                  cursor: 'pointer',
                  fontSize: '12px'
                });
                deleteBtn.onclick = () => {
                  editor.remove();
                  ruleEditors = ruleEditors.filter(e => e !== editor);
                };
                header.appendChild(deleteBtn);
            }

            editor.appendChild(header);

            // 样式设置
            const styleGrid = document.createElement('div');
            Object.assign(styleGrid.style, {
                display: 'grid',
                gridTemplateColumns: 'repeat(2, 1fr)',
                gap: '12px'
            });

            // 文字颜色
            const colorCtrl = createColorControl('文字颜色', rule.style.color || '#000000');
            styleGrid.appendChild(colorCtrl);

            // 背景颜色
            const bgCtrl = createColorControl('背景颜色', rule.style.backgroundColor || 'transparent', true);
            styleGrid.appendChild(bgCtrl);

            // 字体粗细
            const weightCtrl = createSelectControl('字体粗细',
                ['normal', 'bold', 'bolder', 'lighter', '100', '400', '700', '900'],
                rule.style.fontWeight || 'normal'
            );
            styleGrid.appendChild(weightCtrl);

            // 字体大小
            const sizeCtrl = createSelectControl('字体大小',
                ['12px', '14px', '16px', '18px', '20px', '24px'],
                rule.style.fontSize || '14px'
            );
            styleGrid.appendChild(sizeCtrl);

            editor.appendChild(styleGrid);

            // 预览
            const previewDiv = document.createElement('div');
            Object.assign(previewDiv.style, {
                marginTop: '12px',
                padding: '8px',
                backgroundColor: '#fff',
                borderRadius: '4px',
                textAlign: 'center'
            });

            const previewLink = document.createElement('a');
            previewLink.textContent = `悬赏 $${rule.min} 金币的帖子标题`;
            previewLink.href = '#';
            Object.assign(previewLink.style, {
                textDecoration: 'none',
                color: rule.style.color || '#000',
                fontWeight: rule.style.fontWeight || 'normal',
                fontSize: rule.style.fontSize || '14px',
                backgroundColor: rule.style.backgroundColor || 'transparent',
                padding: '2px 8px',
                borderRadius: '4px'
            });

            // 实时更新预览
            function updatePreview() {
                previewLink.style.color = colorCtrl.value;
                previewLink.style.backgroundColor = bgCtrl.value === 'transparent' ? 'transparent' : bgCtrl.value;
                previewLink.style.fontWeight = weightCtrl.value;
                previewLink.style.fontSize = sizeCtrl.value;
            }

            colorCtrl.addEventListener('input', updatePreview);
            colorCtrl.addEventListener('change', updatePreview);
            bgCtrl.addEventListener('input', updatePreview);
            bgCtrl.addEventListener('change', updatePreview);
            weightCtrl.addEventListener('change', updatePreview);
            sizeCtrl.addEventListener('change', updatePreview);

            previewDiv.appendChild(previewLink);
            editor.appendChild(previewDiv);

            // 存储引用以便保存时读取
            editor.getData = () => ({
                min: parseInt(minInput.value) || 0,
                max: maxInput.value === '∞' || maxInput.value === '' ? null : parseInt(maxInput.value),
                style: {
                  color: colorCtrl.value,
                  backgroundColor: bgCtrl.value === 'transparent' ? undefined : bgCtrl.value,
                  fontWeight: weightCtrl.value === 'normal' ? undefined : weightCtrl.value,
                  fontSize: sizeCtrl.value === '14px' ? undefined : sizeCtrl.value
                }
            });

            return editor;
      }

      // 初始化现有规则
      rewardRules.forEach((rule, idx) => {
            const editor = createRuleEditor(rule, idx);
            rulesList.appendChild(editor);
            ruleEditors.push(editor);
      });

      rulesContainer.appendChild(rulesList);

      // 添加规则按钮
      const addRuleBtn = document.createElement('button');
      addRuleBtn.innerHTML = '+ 添加金额区间';
      Object.assign(addRuleBtn.style, {
            marginTop: '12px',
            width: '100%',
            padding: '10px',
            backgroundColor: '#e9ecef',
            border: '2px dashed #adb5bd',
            borderRadius: '8px',
            cursor: 'pointer',
            color: '#495057',
            fontWeight: '500',
            transition: 'all 0.2s'
      });
      addRuleBtn.onmouseover = () => {
            addRuleBtn.style.backgroundColor = '#dee2e6';
            addRuleBtn.style.borderColor = '#6c757d';
      };
      addRuleBtn.onmouseout = () => {
            addRuleBtn.style.backgroundColor = '#e9ecef';
            addRuleBtn.style.borderColor = '#adb5bd';
      };
      addRuleBtn.onclick = () => {
            const newRule = { min: 0, max: null, style: { color: '#000000' } };
            const editor = createRuleEditor(newRule, ruleEditors.length);
            rulesList.appendChild(editor);
            ruleEditors.push(editor);
      };
      rulesContainer.appendChild(addRuleBtn);

      // 重置按钮
      const resetRulesBtn = document.createElement('button');
      resetRulesBtn.textContent = '恢复默认规则';
      Object.assign(resetRulesBtn.style, {
            marginTop: '8px',
            width: '100%',
            padding: '8px',
            backgroundColor: 'transparent',
            border: '1px solid #dc3545',
            color: '#dc3545',
            borderRadius: '4px',
            cursor: 'pointer',
            fontSize: '13px'
      });
      resetRulesBtn.onclick = () => {
            if (confirm('确定要恢复默认规则吗?当前自定义规则将丢失。')) {
                rulesList.innerHTML = '';
                ruleEditors = [];
                defaultRewardRules.forEach((rule, idx) => {
                  const editor = createRuleEditor(rule, idx);
                  rulesList.appendChild(editor);
                  ruleEditors.push(editor);
                });
            }
      };
      rulesContainer.appendChild(resetRulesBtn);

      rewardTab.appendChild(rulesContainer);

      // 组装内容区
      Object.values(tabContents).forEach(content => {
            contentArea.appendChild(content);
      });
      panel.appendChild(contentArea);

      // 底部按钮
      const footer = document.createElement('div');
      Object.assign(footer.style, {
            display: 'flex',
            justifyContent: 'flex-end',
            gap: '12px',
            padding: '16px 24px',
            backgroundColor: '#f8f9fa',
            borderTop: '1px solid #e9ecef'
      });

      const cancelBtn = document.createElement('button');
      cancelBtn.textContent = '取消';
      Object.assign(cancelBtn.style, {
            padding: '8px 20px',
            backgroundColor: '#6c757d',
            color: 'white',
            border: 'none',
            borderRadius: '6px',
            cursor: 'pointer',
            fontSize: '14px',
            fontWeight: '500'
      });
      cancelBtn.onclick = () => {
            panel.style.display = 'none';
            overlay.style.display = 'none';
      };

      const saveBtn = document.createElement('button');
      saveBtn.textContent = '保存设置';
      Object.assign(saveBtn.style, {
            padding: '8px 24px',
            backgroundColor: '#0d6efd',
            color: 'white',
            border: 'none',
            borderRadius: '6px',
            cursor: 'pointer',
            fontSize: '14px',
            fontWeight: '500',
            boxShadow: '0 2px 4px rgba(13,110,253,0.3)'
      });
      saveBtn.onmouseover = () => saveBtn.style.backgroundColor = '#0b5ed7';
      saveBtn.onmouseout = () => saveBtn.style.backgroundColor = '#0d6efd';

      saveBtn.onclick = () => {
            // 保存回帖奖励样式
            styles.color = colorText.value;
            styles.fontWeight = weightSelect.value;
            styles.fontSize = sizeValue.textContent;
            GM_setValue('color', styles.color);
            GM_setValue('fontWeight', styles.fontWeight);
            GM_setValue('fontSize', styles.fontSize);

            // 保存悬赏已解决
            resolvedAction = selectedAction;
            resolvedBgColor = bgColorInput.value;
            GM_setValue('resolvedAction', resolvedAction);
            GM_setValue('resolvedBgColor', resolvedBgColor);

            // 保存悬赏金额规则
            rewardEnabled = enableSwitch.querySelector('input').checked;
            GM_setValue(REWARD_ENABLED_KEY, rewardEnabled);

            const newRules = ruleEditors.map(editor => editor.getData());
            // 清理 undefined 值
            newRules.forEach(rule => {
                Object.keys(rule.style).forEach(key => {
                  if (rule.style === undefined) delete rule.style;
                });
            });
            rewardRules = newRules;
            GM_setValue(REWARD_RULES_KEY, rewardRules);

            // 显示成功提示
            showToast('设置已保存!');
            panel.style.display = 'none';
            overlay.style.display = 'none';

            checkAndModify();
      };

      footer.appendChild(cancelBtn);
      footer.appendChild(saveBtn);
      panel.appendChild(footer);

      document.body.appendChild(panel);
      settingsPanel = panel;

      // 初始化标签页
      switchTab('reply');
      overlay.style.display = 'block';

      // 辅助函数:创建表单组
      function createFormGroup(labelText) {
            const group = document.createElement('div');
            const label = document.createElement('label');
            label.textContent = labelText;
            Object.assign(label.style, {
                display: 'block',
                marginBottom: '6px',
                fontWeight: '500',
                color: '#495057',
                fontSize: '14px'
            });
            group.appendChild(label);
            return group;
      }

      // 辅助函数:创建预览框
      function createPreviewBox() {
            const box = document.createElement('div');
            Object.assign(box.style, {
                padding: '20px',
                backgroundColor: '#f8f9fa',
                borderRadius: '8px',
                marginBottom: '20px',
                textAlign: 'center',
                border: '1px dashed #dee2e6'
            });
            const label = document.createElement('div');
            label.textContent = '&#128065;️ 实时预览';
            Object.assign(label.style, {
                fontSize: '12px',
                color: '#6c757d',
                marginBottom: '8px',
                textTransform: 'uppercase',
                letterSpacing: '1px'
            });
            box.appendChild(label);
            return box;
      }

      // 辅助函数:创建颜色选择控件
      function createColorControl(label, defaultValue, allowTransparent = false) {
            const group = document.createElement('div');
            const lbl = document.createElement('div');
            lbl.textContent = label;
            Object.assign(lbl.style, {
                fontSize: '12px',
                color: '#6c757d',
                marginBottom: '4px'
            });

            const wrapper = document.createElement('div');
            Object.assign(wrapper.style, {
                display: 'flex',
                gap: '6px'
            });

            const colorInput = document.createElement('input');
            colorInput.type = 'color';
            colorInput.value = defaultValue === 'transparent' ? '#ffffff' : (rgbToHex(defaultValue) || defaultValue);
            Object.assign(colorInput.style, {
                width: '36px',
                height: '32px',
                border: '1px solid #ced4da',
                borderRadius: '4px',
                padding: '0',
                cursor: 'pointer'
            });

            const textInput = document.createElement('input');
            textInput.type = 'text';
            textInput.value = defaultValue;
            Object.assign(textInput.style, {
                flex: '1',
                padding: '6px',
                border: '1px solid #ced4da',
                borderRadius: '4px',
                fontSize: '12px'
            });

            let transparentBtn = null;
            if (allowTransparent) {
                transparentBtn = document.createElement('button');
                transparentBtn.textContent = '无';
                Object.assign(transparentBtn.style, {
                  padding: '6px 10px',
                  backgroundColor: defaultValue === 'transparent' ? '#0d6efd' : '#e9ecef',
                  color: defaultValue === 'transparent' ? 'white' : '#495057',
                  border: 'none',
                  borderRadius: '4px',
                  cursor: 'pointer',
                  fontSize: '12px'
                });
                transparentBtn.onclick = () => {
                  textInput.value = 'transparent';
                  colorInput.value = '#ffffff';
                  transparentBtn.style.backgroundColor = '#0d6efd';
                  transparentBtn.style.color = 'white';
                  // 触发自定义事件
                  textInput.dispatchEvent(new Event('input'));
                };
                wrapper.appendChild(transparentBtn);
            }

            colorInput.oninput = (e) => {
                textInput.value = e.target.value;
                if (allowTransparent && transparentBtn) {
                  transparentBtn.style.backgroundColor = '#e9ecef';
                  transparentBtn.style.color = '#495057';
                }
                textInput.dispatchEvent(new Event('input'));
            };
            textInput.oninput = (e) => {
                const hex = rgbToHex(e.target.value);
                if (hex) colorInput.value = hex;
            };

            Object.defineProperty(group, 'value', {
                get: () => textInput.value,
                set: (v) => {
                  textInput.value = v;
                  const hex = rgbToHex(v);
                  if (hex) colorInput.value = hex;
                  if (allowTransparent && transparentBtn) {
                        transparentBtn.style.backgroundColor = v === 'transparent' ? '#0d6efd' : '#e9ecef';
                        transparentBtn.style.color = v === 'transparent' ? 'white' : '#495057';
                  }
                }
            });

            // 自定义事件监听接口
            group.addEventListener = (type, fn) => {
                colorInput.addEventListener(type, fn);
                textInput.addEventListener(type, fn);
            };

            wrapper.appendChild(colorInput);
            wrapper.appendChild(textInput);
            group.appendChild(lbl);
            group.appendChild(wrapper);

            return group;
      }

      // 辅助函数:创建下拉控件
      function createSelectControl(label, options, defaultValue) {
            const group = document.createElement('div');
            const lbl = document.createElement('div');
            lbl.textContent = label;
            Object.assign(lbl.style, {
                fontSize: '12px',
                color: '#6c757d',
                marginBottom: '4px'
            });

            const select = document.createElement('select');
            options.forEach(opt => {
                const option = document.createElement('option');
                option.value = opt;
                option.textContent = opt;
                if (opt === defaultValue) option.selected = true;
                select.appendChild(option);
            });
            Object.assign(select.style, {
                width: '100%',
                padding: '6px',
                border: '1px solid #ced4da',
                borderRadius: '4px',
                fontSize: '13px'
            });

            Object.defineProperty(group, 'value', {
                get: () => select.value,
                set: (v) => select.value = v
            });

            group.addEventListener = (type, fn) => select.addEventListener(type, fn);

            group.appendChild(lbl);
            group.appendChild(select);
            return group;
      }

      // 辅助函数:创建开关
      function createToggleSwitch(checked) {
            const label = document.createElement('label');
            Object.assign(label.style, {
                position: 'relative',
                display: 'inline-block',
                width: '50px',
                height: '26px',
                cursor: 'pointer'
            });

            const input = document.createElement('input');
            input.type = 'checkbox';
            input.checked = checked;
            Object.assign(input.style, {
                opacity: '0',
                width: '0',
                height: '0'
            });

            const slider = document.createElement('span');
            Object.assign(slider.style, {
                position: 'absolute',
                cursor: 'pointer',
                top: '0',
                left: '0',
                right: '0',
                bottom: '0',
                backgroundColor: checked ? '#0d6efd' : '#ccc',
                transition: '.3s',
                borderRadius: '26px'
            });

            const knob = document.createElement('span');
            Object.assign(knob.style, {
                position: 'absolute',
                content: '""',
                height: '20px',
                width: '20px',
                left: checked ? '26px' : '3px',
                bottom: '3px',
                backgroundColor: 'white',
                transition: '.3s',
                borderRadius: '50%',
                boxShadow: '0 1px 3px rgba(0,0,0,0.3)'
            });

            input.onchange = (e) => {
                slider.style.backgroundColor = e.target.checked ? '#0d6efd' : '#ccc';
                knob.style.left = e.target.checked ? '26px' : '3px';
            };

            label.appendChild(input);
            label.appendChild(slider);
            label.appendChild(knob);

            Object.defineProperty(label, 'checked', {
                get: () => input.checked,
                set: (v) => {
                  input.checked = v;
                  slider.style.backgroundColor = v ? '#0d6efd' : '#ccc';
                  knob.style.left = v ? '26px' : '3px';
                }
            });

            return label;
      }

      // 辅助函数:RGB转Hex
      function rgbToHex(rgb) {
            if (!rgb || typeof rgb !== 'string') return null;
            if (rgb.startsWith('#')) return rgb;
            const match = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$$/);
            if (!match) return null;
            const r = parseInt(match);
            const g = parseInt(match);
            const b = parseInt(match);
            return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
      }

      // 辅助函数:显示提示
      function showToast(message) {
            // 检查是否已存在样式
            if (!document.getElementById('pj-toast-style')) {
                const style = document.createElement('style');
                style.id = 'pj-toast-style';
                style.textContent = `
                  @keyframes slideDown {
                        from { transform: translate(-50%, -100%); opacity: 0; }
                        to { transform: translate(-50%, 0); opacity: 1; }
                  }
                `;
                document.head.appendChild(style);
            }

            const toast = document.createElement('div');
            toast.textContent = message;
            Object.assign(toast.style, {
                position: 'fixed',
                top: '20px',
                left: '50%',
                transform: 'translateX(-50%)',
                backgroundColor: '#28a745',
                color: 'white',
                padding: '12px 24px',
                borderRadius: '6px',
                boxShadow: '0 4px 12px rgba(0,0,0,0.2)',
                zIndex: '10000',
                fontWeight: '500',
                animation: 'slideDown 0.3s ease'
            });

            document.body.appendChild(toast);
            setTimeout(() => {
                toast.style.opacity = '0';
                toast.style.transition = 'opacity 0.3s';
                setTimeout(() => toast.remove(), 300);
            }, 2000);
      }
    }

    // ---------- 注册菜单命令 ----------
    GM_registerMenuCommand("⚙️ 打开设置面板", createSettingsPanel);

    // ---------- 启动核心功能 ----------
    const observer = new MutationObserver(checkAndModify);
    observer.observe(document.body, { childList: true, subtree: true });
    checkAndModify();
    setInterval(checkAndModify, 1000);
})();https://picui.ogmua.cn/s1/2026/03/04/69a802d531f7c.webp

AiniWang 发表于 2026-3-4 17:25

已安装,好用,感谢分享

af8889 发表于 2026-3-4 18:10

前排支持!!谢谢有你!{:1_893:}

cgbchen 发表于 2026-3-4 18:37

这个好玩,以后找自己关心的帖子方便了。手动点赞!

zxinyun 发表于 2026-3-4 18:41

为啥我设置了都不生效呢,奇怪。

zzq90322 发表于 2026-3-4 19:38

已安装,好用,感谢分享这个好玩,以后找自己关心的帖子方便了。手动点赞!

anranleixia 发表于 2026-3-4 20:02

已收藏,感谢大佬的分享

picoyiyi 发表于 2026-3-4 20:48

能标记那些我已经回答过的帖么?

icho 发表于 2026-3-4 21:33

给楼主点赞
页: [1] 2
查看完整版本: 【油猴脚本】52pj帖子列表增强