// ==UserScript==
// @name 嘉立创社区布局优化
// @namespace http://tampermonkey.net/
// @version 1.7.0
// @description 优化嘉立创社区(jlc-bbs.com)布局:隐藏右侧低价值模块、内容区扩展、合并操作行、删除问题状态、紧凑间距、官方筛选、今日筛选、筛选数量标注
// @author You
// @match https://www.jlc-bbs.com/*
// @icon https://www.jlc-bbs.com/ssr/img/logo.970c992.svg
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
// ========== 配置项 ==========
const CONFIG = {
// 右侧边栏各个模块的可见性(默认全部隐藏)
sidebarBlocks: {
'社区数据': false, // 今日帖子/互动量/在线人数等统计
'活动日历': false, // 活动日历
'推荐话题': false, // 推荐话题标签
'打赏记录': false, // 打赏记录列表
'涨分锦囊': false, // 积分任务
'广告横幅': false, // 积分抽奖等广告图
'页脚链接': false, // 备案信息等
},
// 是否隐藏整个右侧边栏
hideRightSidebar: true,
// 主内容区最大宽度(null 表示不限,随屏幕自适应)
contentMaxWidth: null,
// 是否显示切换按钮(允许临时恢复右侧边栏)
showToggleButton: true,
};
// 从存储加载用户配置
const savedConfig = GM_getValue('layoutConfig', null);
if (savedConfig) {
try {
const parsed = JSON.parse(savedConfig);
if (parsed.sidebarBlocks) {
CONFIG.sidebarBlocks = { ...CONFIG.sidebarBlocks, ...parsed.sidebarBlocks };
}
if (parsed.hideRightSidebar !== undefined) {
CONFIG.hideRightSidebar = parsed.hideRightSidebar;
}
if (parsed.contentMaxWidth !== undefined) {
CONFIG.contentMaxWidth = parsed.contentMaxWidth;
}
} catch (e) { /* 忽略解析错误,使用默认配置 */ }
}
// 保存配置到存储
function saveConfig() {
GM_setValue('layoutConfig', JSON.stringify({
sidebarBlocks: CONFIG.sidebarBlocks,
hideRightSidebar: CONFIG.hideRightSidebar,
contentMaxWidth: CONFIG.contentMaxWidth,
}));
}
// ========== 样式注入 ==========
function applyStyles() {
// 移除旧的样式
const oldStyle = document.getElementById('jlc-layout-optimize-style');
if (oldStyle) oldStyle.remove();
let css = '';
if (CONFIG.hideRightSidebar) {
// 隐藏右侧边栏容器
css += `
/* 隐藏右侧 sticky-wrap(第2个) */
.content.flex.relative > .sticky-wrap:nth-child(3) {
display: none !important;
}
/* 移除主内容区最大宽度限制,让它填充 */
.content.flex.relative > .flex-1 {
max-width: none !important;
}
`;
} else {
// 按模块逐个控制可见性
const rightSidebar = document.querySelector('.position-sticky.w-\\[310px\\]');
if (rightSidebar) {
const blocks = rightSidebar.children;
for (let i = 0; i < blocks.length; i++) {
const block = blocks[i];
const titleEl = block.querySelector('h3, h4, [class*="title"]');
const title = titleEl ? titleEl.textContent.trim() : '';
const preview = block.textContent.substring(0, 50).trim();
// 匹配模块名称
let matched = false;
for (const [name, visible] of Object.entries(CONFIG.sidebarBlocks)) {
if (preview.includes(name) && !visible) {
block.style.display = 'none';
matched = true;
break;
}
}
if (!matched) {
block.style.display = '';
}
}
}
}
// 如果设置了自定义最大宽度
if (CONFIG.contentMaxWidth) {
css += `
.content.flex.relative > .flex-1 {
max-width: ${CONFIG.contentMaxWidth}px !important;
}
`;
}
// 通用优化:发帖区域也扩大
css += `
/* 发帖框宽度自适应 */
.content.flex.relative > .flex-1 .mt-\\[20px\\] {
max-width: 100% !important;
}
/* ===== 搜索框加宽 + 颜色增强 ===== */
.search-input.el-input {
width: 500px !important;
}
.search-input .el-input__inner {
background-color: #fff !important;
border: 1.5px solid #c0c4cc !important;
border-radius: 20px !important;
height: 36px !important;
line-height: 36px !important;
padding-left: 36px !important;
font-size: 14px !important;
color: #333 !important;
}
.search-input .el-input__inner::placeholder {
color: #909399 !important;
}
.search-input .el-input__inner:focus {
border-color: #1890ff !important;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.15) !important;
}
.search-input .el-input__prefix {
left: 12px !important;
}
/* ===== 紧凑间距:20px → 8px ===== */
/* 主内容区内边距 */
.flex-1.bg-\\[\\#f2f3f5\\] {
padding-left: 8px !important;
padding-right: 8px !important;
}
/* 发帖框顶部间距 */
.flex-1.bg-\\[\\#f2f3f5\\] > .mt-\\[20px\\] {
margin-top: 8px !important;
}
/* 卡片内边距 上下 */
.borderb, .borderb.px-0.relative {
padding-top: 8px !important;
padding-bottom: 8px !important;
}
/* 卡片内容区信息行间距 */
.borderb .mr-\\[20px\\] {
margin-right: 8px !important;
}
/* 左侧/右侧导航 pt-20 → 8px */
.sticky-wrap.pt-20, .sticky-wrap.pt-\\[20px\\] {
padding-top: 8px !important;
}
/* 发帖编辑区内边距 */
.px-\\[20px\\].pt-\\[20px\\] {
padding-left: 8px !important;
padding-right: 8px !important;
padding-top: 8px !important;
}
/* ===== 合并操作行后的样式 ===== */
/* info-part-2 容器:允许换行,对齐 */
.info-part-2.flex.items-center {
flex-wrap: wrap !important;
gap: 0 4px !important;
}
/* 合并进来的 menu 容器 */
.info-part-2 .menu {
display: inline-flex !important;
align-items: center !important;
}
.info-part-2 .menu .flex.items-center {
display: inline-flex !important;
align-items: center !important;
}
/* 合并后的按钮缩小间距和字号 */
.info-part-2 .menu .el-tooltip,
.info-part-2 .menu .share-btn,
.info-part-2 .menu .flag-btn {
margin-right: 12px !important;
font-size: 12px !important;
color: #899099 !important;
display: inline-flex !important;
align-items: center !important;
gap: 3px !important;
}
.info-part-2 .menu .el-tooltip img,
.info-part-2 .menu .share-btn img,
.info-part-2 .menu .flag-btn img {
width: 14px !important;
height: 14px !important;
}
.info-part-2 .menu .el-tooltip span,
.info-part-2 .menu .share-btn span,
.info-part-2 .menu .flag-btn span {
font-size: 12px !important;
}
/* 分隔符 */
.jlc-separator {
color: #d0d5da;
margin: 0 6px;
font-size: 12px;
}
/* ===== 筛选按钮选中样式 ===== */
.jlc-official-filter.question-type-active,
.jlc-today-filter.question-type-active {
color: #1890ff !important;
font-weight: 600 !important;
}
/* 合并进来的标签(如"嘉立创EDA") */
.info-part-2 > .text-\\[\\#899099\\] {
margin-left: 4px !important;
font-size: 12px !important;
}
/* 切换按钮样式 */
#jlc-sidebar-toggle {
position: fixed;
right: 16px;
bottom: 80px;
z-index: 9999;
width: 40px;
height: 40px;
border-radius: 50%;
background: #fff;
border: 1px solid #e0e0e0;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
transition: all 0.2s ease;
color: #666;
line-height: 1;
}
#jlc-sidebar-toggle:hover {
background: #f5f5f5;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
color: #333;
}
#jlc-sidebar-toggle.sidebar-visible {
background: #1890ff;
color: #fff;
border-color: #1890ff;
}
#jlc-sidebar-toggle.sidebar-visible:hover {
background: #40a9ff;
}
/* 设置面板样式 */
#jlc-settings-panel {
position: fixed;
right: 16px;
bottom: 130px;
z-index: 9999;
background: #fff;
border-radius: 12px;
box-shadow: 0 4px 24px rgba(0,0,0,0.18);
padding: 20px;
width: 280px;
font-size: 13px;
display: none;
}
#jlc-settings-panel.visible {
display: block;
}
#jlc-settings-panel h4 {
margin: 0 0 12px 0;
font-size: 15px;
font-weight: 600;
color: #333;
border-bottom: 1px solid #eee;
padding-bottom: 8px;
}
.jlc-setting-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 6px 0;
color: #555;
}
.jlc-setting-row label {
cursor: pointer;
user-select: none;
}
.jlc-setting-row input[type="checkbox"] {
width: 16px;
height: 16px;
cursor: pointer;
accent-color: #1890ff;
}
.jlc-setting-divider {
height: 1px;
background: #eee;
margin: 10px 0;
}
.jlc-setting-btn {
display: inline-block;
padding: 4px 12px;
border-radius: 4px;
border: 1px solid #d9d9d9;
background: #fff;
cursor: pointer;
font-size: 12px;
color: #555;
margin-right: 6px;
margin-top: 8px;
transition: all 0.15s;
}
.jlc-setting-btn:hover {
border-color: #1890ff;
color: #1890ff;
}
`;
// 注入样式
const styleEl = document.createElement('style');
styleEl.id = 'jlc-layout-optimize-style';
styleEl.textContent = css;
document.head.appendChild(styleEl);
}
// ========== UI 控件 ==========
// 创建切换按钮
function createToggleButton() {
if (!CONFIG.showToggleButton) return;
if (document.getElementById('jlc-sidebar-toggle')) return;
const btn = document.createElement('button');
btn.id = 'jlc-sidebar-toggle';
btn.title = CONFIG.hideRightSidebar ? '显示右侧边栏' : '隐藏右侧边栏';
btn.innerHTML = CONFIG.hideRightSidebar ? '📱' : '📐';
btn.addEventListener('click', () => {
CONFIG.hideRightSidebar = !CONFIG.hideRightSidebar;
btn.innerHTML = CONFIG.hideRightSidebar ? '📱' : '📐';
btn.title = CONFIG.hideRightSidebar ? '显示右侧边栏' : '隐藏右侧边栏';
applyStyles();
saveConfig();
});
document.body.appendChild(btn);
}
// 创建设置面板
function createSettingsPanel() {
if (document.getElementById('jlc-settings-panel')) return;
const panel = document.createElement('div');
panel.id = 'jlc-settings-panel';
let html = '
🛠️ 布局优化设置
';
// 右侧边栏各模块开关(仅在右侧边栏可见时生效)
for (const [name, visible] of Object.entries(CONFIG.sidebarBlocks)) {
html += `
`;
}
html += '';
html += '';
html += '';
panel.innerHTML = html;
// 绑定事件
panel.querySelectorAll('input[type="checkbox"]').forEach(cb => {
cb.addEventListener('change', (e) => {
const name = e.target.dataset.blockName;
CONFIG.sidebarBlocks[name] = e.target.checked;
applyStyles();
saveConfig();
});
});
panel.querySelector('#jlc-settings-restore').addEventListener('click', () => {
CONFIG.sidebarBlocks = {
'社区数据': false,
'活动日历': false,
'推荐话题': false,
'打赏记录': false,
'涨分锦囊': false,
'广告横幅': false,
'页脚链接': false,
};
CONFIG.hideRightSidebar = true;
saveConfig();
applyStyles();
panel.querySelectorAll('input[type="checkbox"]').forEach(cb => {
cb.checked = false;
});
const toggleBtn = document.getElementById('jlc-sidebar-toggle');
if (toggleBtn) {
toggleBtn.innerHTML = '📱';
toggleBtn.title = '显示右侧边栏';
}
});
panel.querySelector('#jlc-settings-close').addEventListener('click', () => {
panel.classList.remove('visible');
});
document.body.appendChild(panel);
// 长按切换按钮打开设置面板
const toggleBtn = document.getElementById('jlc-sidebar-toggle');
if (toggleBtn) {
let longPressTimer = null;
toggleBtn.addEventListener('mousedown', (e) => {
longPressTimer = setTimeout(() => {
panel.classList.toggle('visible');
}, 500);
});
toggleBtn.addEventListener('mouseup', () => {
clearTimeout(longPressTimer);
});
toggleBtn.addEventListener('mouseleave', () => {
clearTimeout(longPressTimer);
});
}
}
// ========== 卡片结构优化:合并操作行 + 删除问题状态 ==========
// 标记已处理的卡片,避免重复处理
const processedCards = new WeakSet();
function optimizeCards() {
// 找到所有帖子卡片
const cards = document.querySelectorAll('.borderb, .borderb.px-0.relative');
for (const card of cards) {
if (processedCards.has(card)) continue;
processedCards.add(card);
// --- 1. 删除问题状态块 (.question-status) ---
const qStatus = card.querySelector('.question-status');
if (qStatus) {
const qItem = qStatus.closest('.question-item');
if (qItem) {
// 把 question-item 里的内容区(标题+正文)提升出来,替代 question-item
const contentCol = qItem.querySelector('.flex.flex-col.justify-center');
if (contentCol) {
const parent = qItem.parentElement;
// 将内容区的子节点插入到 question-item 前面
while (contentCol.firstChild) {
parent.insertBefore(contentCol.firstChild, qItem);
}
}
qItem.remove();
}
}
// --- 2. 合并操作行到信息行 ---
// 找到交互按钮行:.flex.justify-between.items-center.mt-[16px]
const actionBar = card.querySelector('.flex.justify-between.items-center.mt-\\[16px\\]');
if (!actionBar) continue;
// 找到信息行:.info-part-2.flex.items-center
const infoPart2 = card.querySelector('.info-part-2.flex.items-center');
if (!infoPart2) continue;
// 提取操作行中的元素
const menu = actionBar.querySelector('.menu');
const tagEl = actionBar.querySelector('.text-\\[\\#899099\\]');
if (menu) {
// 创建一个分隔符
const separator = document.createElement('span');
separator.textContent = ' | ';
separator.className = 'jlc-separator';
infoPart2.appendChild(separator);
// 移动 menu 到 info-part-2
// 缩小间距,调整样式
menu.style.display = 'inline-flex';
menu.style.alignItems = 'center';
menu.querySelectorAll('.mr-\\[30px\\]').forEach(el => {
el.classList.remove('mr-[30px]');
el.classList.add('mr-[16px]');
// 缩小图标
el.style.fontSize = '12px';
});
infoPart2.appendChild(menu);
}
if (tagEl) {
// 标签也移动到 info-part-2
infoPart2.appendChild(tagEl);
}
// 移除原来的操作行
actionBar.remove();
}
}
// ========== 官方筛选功能 ==========
// 筛选状态: 'all' | 'official' | 'unofficial'
let officialFilter = 'all';
let todayFilter = 'all'; // 'all' | 'today' | 'not-today'
const injectedFilterBars = new WeakSet();
function injectOfficialFilter() {
// 找到所有 tab pane(含当前激活和未激活的,SPA 切换时需要处理新出现的)
const tabPanes = document.querySelectorAll('.el-tab-pane');
for (const pane of tabPanes) {
const questionType = pane.querySelector(':scope > .question-type');
if (!questionType || injectedFilterBars.has(questionType)) continue;
injectedFilterBars.add(questionType);
// 找到"综合"按钮所在的容器(即 .question-type-item 的直接父级 div)
const firstTabItem = questionType.querySelector('.question-type-item');
if (!firstTabItem) continue;
const tabContainer = firstTabItem.parentElement;
if (!tabContainer) continue;
if (tabContainer.querySelector('.jlc-official-filter')) continue;
// 用和"综合"/"最新发布"完全相同的 class 创建按钮
const btnOfficial = document.createElement('span');
btnOfficial.className = 'mr-[10px] cursor-pointer question-type-item jlc-official-filter';
btnOfficial.dataset.filter = 'official';
btnOfficial.textContent = '只看官方';
const btnUnofficial = document.createElement('span');
btnUnofficial.className = 'mr-[10px] cursor-pointer question-type-item jlc-official-filter';
btnUnofficial.dataset.filter = 'unofficial';
btnUnofficial.textContent = '不看官方';
tabContainer.appendChild(btnOfficial);
tabContainer.appendChild(btnUnofficial);
// 今日筛选按钮
const btnToday = document.createElement('span');
btnToday.className = 'mr-[10px] cursor-pointer question-type-item jlc-today-filter';
btnToday.dataset.filter = 'today';
btnToday.textContent = '只看今日';
const btnNotToday = document.createElement('span');
btnNotToday.className = 'mr-[10px] cursor-pointer question-type-item jlc-today-filter';
btnNotToday.dataset.filter = 'not-today';
btnNotToday.textContent = '不看今日';
tabContainer.appendChild(btnToday);
tabContainer.appendChild(btnNotToday);
// 点击事件
btnOfficial.addEventListener('click', () => {
officialFilter = officialFilter === 'official' ? 'all' : 'official';
updateFilterUI(questionType);
applyOfficialFilter(pane);
});
btnUnofficial.addEventListener('click', () => {
officialFilter = officialFilter === 'unofficial' ? 'all' : 'unofficial';
updateFilterUI(questionType);
applyOfficialFilter(pane);
});
btnToday.addEventListener('click', () => {
todayFilter = todayFilter === 'today' ? 'all' : 'today';
updateAllTodayFilterUI();
applyOfficialFilter(pane);
});
btnNotToday.addEventListener('click', () => {
todayFilter = todayFilter === 'not-today' ? 'all' : 'not-today';
updateAllTodayFilterUI();
applyOfficialFilter(pane);
});
}
// 更新所有已注入按钮的激活状态
updateAllFilterUI();
}
function updateFilterUI(questionType) {
updateAllFilterUI();
}
function updateAllFilterUI() {
document.querySelectorAll('.jlc-official-filter').forEach(btn => {
const f = btn.dataset.filter;
if (f === officialFilter) {
btn.classList.add('question-type-active');
} else {
btn.classList.remove('question-type-active');
}
});
}
function updateAllTodayFilterUI() {
document.querySelectorAll('.jlc-today-filter').forEach(btn => {
const f = btn.dataset.filter;
if (f === todayFilter) {
btn.classList.add('question-type-active');
} else {
btn.classList.remove('question-type-active');
}
});
}
// 更新筛选按钮上的数量标注
function updateFilterCounts() {
const now = new Date();
const todayStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
document.querySelectorAll('.el-tab-pane').forEach(pane => {
const cardList = pane.querySelector('.bg-\\[\\#f2f3f5\\]');
if (!cardList) return;
const cards = cardList.querySelectorAll('.borderb');
let officialCount = 0;
let unofficialCount = 0;
let todayCount = 0;
let notTodayCount = 0;
cards.forEach(card => {
const isOfficial = !!card.querySelector('.guan');
if (isOfficial) officialCount++;
else unofficialCount++;
const timeSpans = card.querySelectorAll('span[title]');
const timeSpan = [...timeSpans].find(s => /^\d{4}-\d{2}-\d{2}/.test(s.title));
const isToday = timeSpan && timeSpan.title.startsWith(todayStr);
if (isToday) todayCount++;
else notTodayCount++;
});
pane.querySelectorAll('.jlc-official-filter').forEach(btn => {
const f = btn.dataset.filter;
const baseText = f === 'official' ? '只看官方' : '不看官方';
const count = f === 'official' ? officialCount : unofficialCount;
const newText = `${baseText}(${count}条)`;
if (btn.textContent !== newText) btn.textContent = newText;
});
pane.querySelectorAll('.jlc-today-filter').forEach(btn => {
const f = btn.dataset.filter;
const baseText = f === 'today' ? '只看今日' : '不看今日';
const count = f === 'today' ? todayCount : notTodayCount;
const newText = `${baseText}(${count}条)`;
if (btn.textContent !== newText) btn.textContent = newText;
});
});
}
function applyOfficialFilter(pane) {
const cardList = pane.querySelector('.bg-\\[\\#f2f3f5\\]');
if (!cardList) return;
// 获取今天的日期字符串 (YYYY-MM-DD)
const now = new Date();
const todayStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
cardList.querySelectorAll('.borderb').forEach(card => {
let visible = true;
// 官方筛选
const isOfficial = !!card.querySelector('.guan');
if (officialFilter === 'official') visible = visible && isOfficial;
else if (officialFilter === 'unofficial') visible = visible && !isOfficial;
// 今日筛选:查找 span[title] 中格式为 YYYY-MM-DD HH:MM:SS 的时间
if (todayFilter !== 'all') {
const timeSpans = card.querySelectorAll('span[title]');
const timeSpan = [...timeSpans].find(s => /^\d{4}-\d{2}-\d{2}/.test(s.title));
const isToday = timeSpan && timeSpan.title.startsWith(todayStr);
if (todayFilter === 'today') visible = visible && isToday;
else if (todayFilter === 'not-today') visible = visible && !isToday;
}
card.style.display = visible ? '' : 'none';
});
}
// 对所有 tab pane 重新应用筛选(无限滚动加载新卡片后调用)
function reapplyAllFilters() {
if (officialFilter === 'all' && todayFilter === 'all') return;
document.querySelectorAll('.el-tab-pane').forEach(pane => {
applyOfficialFilter(pane);
});
updateAllTodayFilterUI();
}
// ========== 初始化 ==========
function init() {
applyStyles();
createToggleButton();
createSettingsPanel();
optimizeCards();
injectOfficialFilter();
updateFilterCounts();
// 监听 DOM 变化,处理 SPA 路由切换和无限滚动加载新卡片
const observer = new MutationObserver(() => {
applyStyles();
createToggleButton();
createSettingsPanel();
optimizeCards();
injectOfficialFilter();
// 无限滚动加载新卡片后,重新应用官方筛选
reapplyAllFilters();
updateFilterCounts();
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
console.log('[嘉立创社区布局优化] 已加载 - 右侧边栏已隐藏,内容区已扩展,操作行已合并');
}
// 等待页面加载完成
if (document.readyState === 'complete') {
init();
} else {
window.addEventListener('load', init);
}
})();