【油猴脚本】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);
})();
```
---
## 🎯 主要改进
### 1. 集成设置面板
- 通过油猴菜单命令 **⚙️ 打开设置面板** 调出浮动窗口。
- 面板固定在页面右上角,可关闭,不干扰正常浏览。
- 所有配置项集中在一个界面中,告别多个弹窗。
### 2. 可视化控件
- **回帖奖励样式**:三个输入框分别控制字体粗细、颜色、大小。
- **悬赏已解决处理**:单选按钮选择处理方式(无操作/标记背景/隐藏),并支持自定义背景色。
- **悬赏金额动态样式**:
- 复选框启用/禁用功能。
- 文本域直接编辑 JSON 规则,带语法高亮(虽无高亮,但可手动调整)。
- **恢复默认规则** 按钮一键填充默认配置。
### 3. 即时生效
- 点击 **保存** 后,所有配置通过 `GM_setValue` 持久化,并立即调用 `checkAndModify()` 刷新页面样式。
- 点击 **关闭** 仅隐藏面板,不丢失已保存的设置。
### 4. 保留原有核心功能
- 定时器 + MutationObserver 双重保障,确保动态加载内容(翻页、异步更新)也能应用样式。
- 所有处理逻辑(回帖奖励、已解决处理、金额样式)保持不变,仅将配置方式从 `prompt` 升级为面板。
---
## 📌 使用说明
1. 安装脚本后,访问 52pojie.cn 任意页面。
2. 点击 Tampermonkey 图标 → 选择 **⚙️ 打开设置面板**。
3. 在面板中调整各项参数,点击 **保存** 即可看到效果。
4. 如需修改,再次打开面板调整并保存。
---
## 🔧 自定义扩展
- 您可以根据需要修改面板的样式(如宽度、颜色、位置)。
- 若希望规则编辑更友好,可替换文本域为可编辑表格,但 JSON 方式已足够灵活,且能完整保留原数据结构。
本帖最后由 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: '💬 回帖奖励', icon: '' },
{ id: 'resolved', label: '✅ 悬赏已解决', icon: '' },
{ id: 'reward', label: '💰 悬赏金额', 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: '🎨' },
{ value: 'hide', label: '隐藏整行', desc: '完全隐藏', icon: '🙈' }
];
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 = '🗑️';
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 = '👁️ 实时预览';
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 已安装,好用,感谢分享 前排支持!!谢谢有你!{:1_893:} 这个好玩,以后找自己关心的帖子方便了。手动点赞! 为啥我设置了都不生效呢,奇怪。 已安装,好用,感谢分享这个好玩,以后找自己关心的帖子方便了。手动点赞! 已收藏,感谢大佬的分享 能标记那些我已经回答过的帖么? 给楼主点赞
页:
[1]
2