This commit is contained in:
邓智航
2026-01-18 16:51:28 +08:00
parent 0b71afea29
commit 5364edfc46
3 changed files with 1867 additions and 271 deletions

View File

@@ -294,39 +294,42 @@
<td width="60%" valign="top" class="layout-cell main-content"> <td width="60%" valign="top" class="layout-cell main-content">
<h2>库存列表 - 区域 A1</h2> <h2>库存列表 - 区域 A1</h2>
<form action="submit_filter"> <form id="searchForm" onsubmit="event.preventDefault(); handleSearch();">
<table border="0" width="100%" class="filter-table"> <table border="0" width="100%" class="filter-table">
<tr> <tr>
<td>关键词: <input type="text" name="kw" size="30" placeholder="输入 ID 或名称"></td> <td>关键词: <input type="text" id="searchInput" name="kw" size="30" placeholder="输入 ID 或名称"></td>
<td> <td>
类型: 类型:
<select name="type"> <select id="typeSelect" name="type">
<option value="all">-- 所有 --</option> <option value="all">-- 所有 --</option>
<option value="res">电阻</option> <option value="res">电阻 (Resistor)</option>
<option value="cap">电容</option> <option value="cap">电容 (Capacitor)</option>
<option value="ind">电感</option> <option value="ind">电感 (Inductor)</option>
<option value="chip">芯片</option> <option value="chip">芯片 (Chip)</option>
<option value="conn">连接器</option> <option value="conn">连接器 (Connector)</option>
<option value="other">其他 (Other)</option>
</select> </select>
</td> </td>
<td> <td>
状态: 状态:
<label><input type="checkbox" name="st1" checked> 正常</label> <label><input type="checkbox" name="st_normal" value="NORMAL" checked> 正常</label>
<label><input type="checkbox" name="st2"> 警告</label> <label><input type="checkbox" name="st_low" value="LOW" checked> 警告</label>
<label><input type="checkbox" name="st3"> 损坏</label> <label><input type="checkbox" name="st_crit" value="CRITICAL" checked> 严重</label>
</td> </td>
</tr> </tr>
<tr> <tr>
<td colspan="3" style="padding-top:10px;"> <td colspan="3" style="padding-top:10px;">
高级选项: 排序:
<label><input type="radio" name="sort" value="date"> 按日期</label> <label><input type="radio" name="sort" value="date" checked>更新日期</label>
<label><input type="radio" name="sort" value="id"> 按ID</label> <label><input type="radio" name="sort" value="id"> 按ID</label>
<label><input type="radio" name="sort" value="wt"></label> <label><input type="radio" name="sort" value="qty">库存</label>
<label><input type="radio" name="sort" value="pr"> 按优先级</label>
<br><br> <br><br>
<input type="submit" value="开始检索"> <button type="submit" class="page-btn active" style="padding:6px 20px;">
<input type="reset" value="重置条件"> <span id="search-spinner" class="spinner" style="display:none; width:12px; height:12px; border-width:2px; vertical-align:middle; margin-right:5px;"></span>
<input type="button" value="导出Excel (不可用)" disabled style="background:#eee; color:#aaa; cursor:not-allowed;"> 开始检索
</button>
<button type="button" class="page-btn" onclick="resetSearch()">重置条件</button>
<button type="button" class="page-btn" disabled style="opacity:0.6; cursor:not-allowed;">导出Excel</button>
</td> </td>
</tr> </tr>
</table> </table>
@@ -735,6 +738,82 @@
// --- 导航与通用模态框 --- // --- 导航与通用模态框 ---
// --- 搜索逻辑 ---
function handleSearch() {
const spinner = document.getElementById('search-spinner');
spinner.style.display = 'inline-block';
// 获取筛选条件
const kw = document.getElementById('searchInput').value.toLowerCase().trim();
const typeFilter = document.getElementById('typeSelect').value;
const checkedStatuses = [];
if(document.querySelector('input[name="st_normal"]').checked) checkedStatuses.push('NORMAL');
if(document.querySelector('input[name="st_low"]').checked) checkedStatuses.push('LOW');
if(document.querySelector('input[name="st_crit"]').checked) checkedStatuses.push('CRITICAL');
// 获取排序
const sortVal = document.querySelector('input[name="sort"]:checked').value;
// 模拟网络延迟
setTimeout(() => {
state.filteredData = state.data.filter(item => {
// 1. 关键词匹配 (ID, Name, Supplier, Batch)
const matchKw = !kw ||
item.id.toLowerCase().includes(kw) ||
item.name.toLowerCase().includes(kw) ||
item.supplier.toLowerCase().includes(kw) ||
item.batch.toLowerCase().includes(kw);
// 2. 类型匹配 (简单的字符串包含判断)
let matchType = true;
if (typeFilter !== 'all') {
if (typeFilter === 'res') matchType = item.name.includes('Resistor');
else if (typeFilter === 'cap') matchType = item.name.includes('Capacitor');
else if (typeFilter === 'ind') matchType = item.name.includes('Inductor');
else if (typeFilter === 'chip') matchType = item.name.includes('MCU') || item.name.includes('Regulator') || item.name.includes('Transistor') || item.name.includes('Diode');
else if (typeFilter === 'conn') matchType = item.name.includes('Connector') || item.name.includes('Header');
else if (typeFilter === 'other') matchType = !item.name.includes('Resist') && !item.name.includes('Capacit') && !item.name.includes('Induct') && !item.name.includes('Connect');
}
// 3. 状态匹配 (注意: 之前生成的 Mock Status 包含 DAMAGED/PENDING但Checkbox只有三种需兼容)
// 这里只筛选三种主要显示状态,如果用户没选也没事
let matchStatus = false;
if (checkedStatuses.length === 0) matchStatus = true; // 如果全不选,默认全显示?或者全不显示?通常全不选意味着什么都不看,或者看全部...
// 为了用户体验,全不选就当全选吧 (或者严谨点就是空)
// 这里逻辑只要item.status在checkedStatuses里就显示
// 特殊处理: CRITICAL 包含了 Mock 里的 CRITICAL 和 DAMAGED (假设)
if (checkedStatuses.includes(item.status)) matchStatus = true;
// 修正逻辑如果勾选了CRITICAL我们把DAMAGED也算进去?
if (checkedStatuses.includes('CRITICAL') && item.status === 'DAMAGED') matchStatus = true;
return matchKw && matchType && matchStatus;
});
// 排序
if (sortVal === 'id') {
state.filteredData.sort((a,b) => a.id.localeCompare(b.id));
} else if (sortVal === 'qty') {
state.filteredData.sort((a,b) => b.qty - a.qty); // Fixed: property name is qty
} else if (sortVal === 'date') {
state.filteredData.sort((a,b) => new Date(b.date) - new Date(a.date));
}
state.currentPage = 1;
renderTable();
spinner.style.display = 'none';
showToast(`检索完成,找到 ${state.filteredData.length} 条记录`, 'success');
}, 400);
}
function resetSearch() {
document.getElementById('searchInput').value = '';
document.getElementById('typeSelect').value = 'all';
document.querySelectorAll('input[type="checkbox"]').forEach(c => c.checked = true);
document.querySelector('input[value="date"]').checked = true;
handleSearch();
}
function openModal(id) { function openModal(id) {
const modal = document.getElementById(id); const modal = document.getElementById(id);
if (modal) { if (modal) {

View File

@@ -5,103 +5,263 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BSOD Help - System_Thread_Exception - TechZone Forums</title> <title>BSOD Help - System_Thread_Exception - TechZone Forums</title>
<style> <style>
/* CSS Reset */ /* === CSS Variables & Reset === */
:root {
--primary: #1877f2;
--secondary: #42b72a;
--bg-body: #f0f2f5;
--bg-card: #ffffff;
--text-main: #1c1e21;
--text-sub: #65676b;
--border: #dddfe2;
--header-height: 60px;
--danger: #dc3545;
--warning: #ffc107;
}
/* Dark Mode Support */
body.dark-mode {
--primary: #4599ff;
--secondary: #42b72a;
--bg-body: #18191a;
--bg-card: #242526;
--text-main: #e4e6eb;
--text-sub: #b0b3b8;
--border: #3e4042;
}
* { box-sizing: border-box; } * { box-sizing: border-box; }
body { font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; background-color: #e9ebee; margin: 0; color: #1c1e21; line-height: 1.5; } body {
a { text-decoration: none; color: #365899; } font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
background-color: var(--bg-body);
color: var(--text-main);
margin: 0;
line-height: 1.5;
padding-top: var(--header-height); /* Nav spacing */
transition: background 0.3s, color 0.3s;
}
a { text-decoration: none; color: var(--primary); cursor: pointer; }
a:hover { text-decoration: underline; } a:hover { text-decoration: underline; }
ul { padding-left: 20px; } ul { list-style: none; padding: 0; margin: 0; }
/* 容器布局 */ /* === Layout Components === */
.container { max-width: 1200px; margin: 0 auto; background: transparent; } .container { max-width: 1200px; margin: 0 auto; padding: 20px; }
.content-wrapper { display: flex; gap: 20px; padding: 20px; } .layout-grid { display: grid; grid-template-columns: 280px 1fr 300px; gap: 20px; }
.main-column { flex: 3; }
.side-column { flex: 1; }
/* 顶部导航 */ /* Thread View Layout Fix */
.header { background: #fff; padding: 15px 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); margin-bottom: 20px; display: flex; justify-content: space-between; align-items: center; } /* 当处于帖子详情页时,我们将隐藏左侧边栏,只保留 主内容区(1fr) 和 右侧边栏(300px) */
.header h1 { margin: 0; font-size: 24px; color: #4267b2; } .layout-grid.thread-view { grid-template-columns: 1fr 300px; }
.header p { margin: 0; font-size: 12px; color: #999; } .layout-grid.thread-view .sidebar { display: none; } /* 关键修复:隐藏左侧导航 */
.nav-links a { margin-right: 15px; font-weight: bold; color: #4b4f56; }
.search-bar input { padding: 5px; border: 1px solid #ccc; border-radius: 3px; }
/* 帖子样式 */ /* Responsive */
.post-title { background: #fff; padding: 15px; border-radius: 4px; border: 1px solid #ddd; margin-bottom: 10px; } @media (max-width: 1000px) {
.post-title h2 { margin: 0 0 10px 0; } .layout-grid { grid-template-columns: 1fr; }
.sidebar { display: none; } /* Hide sidebar on small screens for simplicty */
.layout-grid.thread-view { grid-template-columns: 1fr; } /* 移动端响应式 */
.layout-grid.thread-view .sidebar-right { display: none; } /* 移动端帖子页隐藏右侧栏 */
}
/* 楼层卡片 (原Table转换) */ /* === Fixed Header === */
.post-card { background: #fff; border: 1px solid #ddd; border-radius: 3px; margin-bottom: 15px; display: flex; flex-direction: column; } .site-header {
/* 用CSS强制改变table的默认显示方式使其像现代Div布局 */ position: fixed; top: 0; left: 0; right: 0;
.post-card table { width: 100%; border-collapse: collapse; border: none; } height: var(--header-height);
.post-card td { border: none; padding: 15px; } background: var(--bg-card);
.user-info-cell { background: #f7f7f7; border-right: 1px solid #eee !important; width: 140px; text-align: center; font-size: 12px; } border-bottom: 1px solid var(--border);
.user-info-cell img { width: 80px; height: 80px; border-radius: 50%; background: #ddd; margin-top: 10px; } box-shadow: 0 2px 4px rgba(0,0,0,0.05);
.content-cell { position: relative; } z-index: 1000;
display: flex; align-items: center; padding: 0 20px;
justify-content: space-between;
}
.logo { font-size: 1.5rem; font-weight: bold; color: var(--primary); display: flex; align-items: center; gap: 10px; }
.search-bar { position: relative; width: 400px; }
.search-bar input {
width: 100%; padding: 8px 15px; border-radius: 20px;
border: 1px solid var(--border); background: var(--bg-body);
color: var(--text-main);
}
.nav-user { display: flex; gap: 15px; align-items: center; }
.user-avatar-sm { width: 32px; height: 32px; border-radius: 50%; background: #ccc; cursor: pointer; }
/* 帖子元数据 */ /* === Cards & Panels === */
.post-meta { font-size: 12px; color: #90949c; margin-bottom: 10px; border-bottom: 1px solid #eee; padding-bottom: 5px; } .card {
background: var(--bg-card);
border-radius: 8px;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
margin-bottom: 20px;
overflow: hidden;
border: 1px solid var(--border);
}
.card-header {
padding: 12px 15px; border-bottom: 1px solid var(--border);
font-weight: 600; font-size: 0.95rem; display: flex; justify-content: space-between;
}
.card-body { padding: 15px; }
/* 附件区域 */ /* === Forum List Styles === */
fieldset { border: 1px solid #d8dfea; background: #f7f7f7; padding: 10px; margin: 10px 0; border-radius: 4px; } .forum-category-item { display: flex; justify-content: space-between; padding: 10px; border-bottom: 1px solid var(--border); cursor: pointer; }
legend { font-weight: bold; color: #4b4f56; } .forum-category-item:hover { background: rgba(0,0,0,0.02); }
.thread-item {
display: flex; gap: 15px; padding: 15px; border-bottom: 1px solid var(--border);
transition: 0.2s;
}
.thread-item:hover { background: rgba(0,0,0,0.02); transform: translateX(5px); }
.thread-stats {
display: flex; flex-direction: column; align-items: center; min-width: 60px;
color: var(--text-sub); font-size: 0.8rem;
}
.stat-count { font-size: 1.1rem; font-weight: bold; color: var(--text-main); }
.thread-main { flex: 1; }
.thread-title { font-size: 1.1rem; font-weight: 500; margin-bottom: 5px; display: block; color: var(--text-main); }
.thread-meta { font-size: 0.85rem; color: var(--text-sub); display: flex; gap: 10px; }
.tag { padding: 2px 6px; border-radius: 4px; font-size: 0.75rem; background: #eee; color: #555; }
.tag.red { background: #fee2e2; color: #b91c1c; }
.tag.blue { background: #e0f2fe; color: #0369a1; }
/* 按钮组 */ /* === Post View Styles (Logic Refactored from Tables to Flex) === */
.action-bar { background: #f6f7f9; border-top: 1px solid #e9ebee; padding: 8px 15px !important; text-align: right !important; } .post-container { display: flex; border-bottom: 1px solid var(--border); }
.action-bar button { background: transparent; border: none; color: #4b4f56; cursor: pointer; font-weight: bold; font-size: 12px; margin-left: 10px; } .post-sidebar {
.action-bar button:hover { color: #4267b2; background: #eee; border-radius: 2px; } width: 180px; background: rgba(0,0,0,0.02); border-right: 1px solid var(--border);
padding: 20px; text-align: center; flex-shrink: 0;
}
.post-avatar-lg { width: 100px; height: 100px; border-radius: 4px; background: #ddd; margin-bottom: 10px; object-fit: cover; }
.user-badge { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 0.75rem; font-weight: bold; margin: 5px 0; }
.post-content-area { flex: 1; padding: 20px; position: relative; min-height: 200px; }
.post-meta-header {
border-bottom: 1px dashed var(--border); padding-bottom: 10px; margin-bottom: 15px;
font-size: 0.85rem; color: var(--text-sub); display: flex; justify-content: space-between;
}
.post-body { font-size: 1rem; line-height: 1.7; min-height: 100px; }
.post-signature {
margin-top: 30px; border-top: 1px dashed var(--border); padding-top: 10px;
color: var(--text-sub); font-size: 0.85rem; font-style: italic;
}
.post-actions {
margin-top: 20px; display: flex; justify-content: flex-end; gap: 10px;
}
.btn-action {
background: transparent; border: 1px solid transparent; color: var(--text-sub);
padding: 5px 10px; border-radius: 4px; font-size: 0.85rem; cursor: pointer;
}
.btn-action:hover { background: rgba(0,0,0,0.05); color: var(--primary); }
/* 特殊身份标识 */ /* === Components === */
.badge-mod { color: green; font-weight: bold; } .btn { padding: 8px 16px; border-radius: 6px; border: none; font-weight: 600; cursor: pointer; transition: 0.2s; }
.badge-new { color: #888; } .btn-primary { background: var(--primary); color: white; }
.btn-primary:hover { opacity: 0.9; }
.btn-block { display: block; width: 100%; }
/* 干扰信息处理 */ /* Pagination */
.ad-banner { background: #fff3cd; color: #856404; padding: 10px; text-align: center; border: 1px solid #ffeeba; border-radius: 4px; margin-bottom: 20px; font-size: 14px; } .pagination { display: flex; gap: 5px; margin-top: 20px; justify-content: center; }
.ad-banner a { color: #856404; font-weight: bold; text-decoration: underline; } .page-num {
width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;
border: 1px solid var(--border); border-radius: 4px; background: var(--bg-card);
cursor: pointer;
}
.page-num.active { background: var(--primary); color: white; border-color: var(--primary); }
/* 恶意广告楼层处理 */ /* Modals */
.spam-post { border: 2px solid red; opacity: 0.7; position: relative; } .modal-backdrop {
.spam-post::after { content: '警告:疑似广告机器人'; position: absolute; top: 0; right: 0; background: red; color: white; padding: 2px 10px; font-size: 10px; } position: fixed; top: 0; left: 0; width: 100%; height: 100%;
.fake-download-area { opacity: 0.3; pointer-events: none; /* 让假按钮无法点击 */ filter: grayscale(100%); } background: rgba(0,0,0,0.5); z-index: 2000;
.fake-download-area::before { content: ' [系统已屏蔽广告链接] '; color: red; font-weight: bold; } display: none; justify-content: center; align-items: center;
backdrop-filter: blur(2px);
}
.modal-backdrop.show { display: flex; }
.modal-panel {
background: var(--bg-card); width: 500px; max-width: 90%;
border-radius: 12px; box-shadow: 0 20px 50px rgba(0,0,0,0.3);
overflow: hidden; animation: slideUp 0.3s ease;
}
@keyframes slideUp { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
/* 侧边栏 */ /* Toast */
.sidebar-box { background: #fff; padding: 15px; border: 1px solid #ddd; margin-bottom: 20px; border-radius: 4px; } .toast-box {
.sidebar-box h3 { margin-top: 0; font-size: 14px; border-left: 3px solid #4267b2; padding-left: 10px; } position: fixed; bottom: 30px; left: 50%; transform: translateX(-50%);
.sidebar-box ul { padding-left: 15px; font-size: 13px; } background: #333; color: white; padding: 12px 24px; border-radius: 30px;
.sidebar-box li { margin-bottom: 8px; } box-shadow: 0 5px 15px rgba(0,0,0,0.2); z-index: 3000;
display: none; font-weight: 500;
}
/* 底部 */ /* Spam Style */
.footer { text-align: center; color: #777; font-size: 12px; padding: 20px; border-top: 1px solid #ccc; margin-top: 20px; background: #fff; } .spam-mask { opacity: 0.5; filter: grayscale(1); pointer-events: none; position: relative; }
.spam-mask::after {
content: "系统已屏蔽此内容"; position: absolute; top: 50%; left: 50%;
transform: translate(-50%, -50%); color: red; font-weight: bold; border: 2px solid red;
padding: 5px 10px; transform: rotate(-10deg);
}
/* 快速回复 */ /* Editor Toolbar */
.quick-reply { background: #fff; padding: 20px; border: 1px solid #ddd; border-radius: 4px; } .editor-toolbar {
.quick-reply textarea { width: 100%; padding: 10px; border: 1px solid #ccc; margin-bottom: 10px; font-family: inherit; } border: 1px solid var(--border); border-bottom: none;
.btn-primary { background: #4267b2; color: white; border: none; padding: 5px 15px; border-radius: 2px; cursor: pointer; } background: rgba(0,0,0,0.02); padding: 5px; display: flex; gap: 5px;
}
.tool-btn {
width: 30px; height: 30px; border: 1px solid transparent; background: transparent;
border-radius: 4px; cursor: pointer; display: flex; justify-content: center; align-items: center;
}
.tool-btn:hover { background: rgba(0,0,0,0.1); }
</style> </style>
</head> </head>
<body> <body id="body">
<div class="container"> <!-- Navbar -->
<div class="header"> <header class="site-header">
<div> <div class="logo">
<h1>TechZone 硬件技术论坛</h1> <span style="font-size:2rem;">🛠️</span> TechZone
<p>全球最大的极客交流社区</p>
</div> </div>
<div class="nav-links"> <div class="search-bar">
<nav> <input type="text" id="searchInput" placeholder="搜索帖子、用户或资源..." onkeydown="if(event.key==='Enter') doSearch()">
<a href="#">首页</a>
<a href="#">硬件专区</a>
<a href="#">软件分享</a>
<a href="#">灌水区</a>
<a href="#">注册/登录</a>
</nav>
<br>
<form class="search-bar">
<input type="text" placeholder="搜索帖子...">
<button class="btn-primary">搜索</button>
</form>
</div> </div>
</div> <div class="nav-user" id="navUser">
<button class="btn btn-primary" onclick="openModal('loginModal')">注册 / 登录</button>
</div>
</header>
<!-- Main Layout -->
<div class="container layout-grid" id="mainLayout">
<!-- Left Sidebar -->
<nav class="sidebar">
<div class="card">
<div class="card-header">板块导航</div>
<div class="card-body" style="padding:0;">
<div class="forum-category-item" onclick="switchView('home')">🏠 论坛首页</div>
<div class="forum-category-item" onclick="filterCategory('hardware')">🖥️ 硬件专区</div>
<div class="forum-category-item" onclick="filterCategory('software')">💿 软件分享</div>
<div class="forum-category-item" onclick="filterCategory('water')">🌊 灌水吐槽</div>
<div class="forum-category-item" onclick="filterCategory('market')">💹 二手交易</div>
<div class="forum-category-item" onclick="filterCategory('feedback')">📢 站务公告</div>
</div>
</div>
<div class="card">
<div class="card-header">我的状态</div>
<div class="card-body">
<p style="color:#666; font-size:0.9rem;">请先登录以查看您的积分和任务。</p>
</div>
</div>
<div class="card" style="border-color: #ffeeba; background:#fffdf5;">
<div class="card-header" style="color:#856404;">📅 每日签到</div>
<div class="card-body">
<p style="font-size:0.8rem; margin-bottom:10px;">今日已签到: 12,503 人</p>
<button class="btn btn-block" style="background:#ffc107; color:#333;" onclick="showToast('请先登录!', 'warning')">立即签到 (+5 积分)</button>
</div>
</div>
</nav>
<!-- Main Content Area -->
<main class="main-content">
<div id="loading" style="text-align:center; padding:50px;">
<div style="font-size:2rem; animation: spin 1s infinite linear;">⚙️</div>
<p>正在加载社区内容...</p>
</div>
<div id="contentArea"></div>
<!-- Old content place holder to be deleted by JS or ignored -->
<div style="display:none;">
<div class="ad-banner"> <div class="ad-banner">
<p>【通告】2025新款显卡预售开启点击查看详情 >></p> <p>【通告】2025新款显卡预售开启点击查看详情 >></p>
@@ -382,30 +542,553 @@
</div> </div>
<div class="footer"> </div> <!-- /End hidden content -->
<p>Copyright © 2000-2025 TechZone Inc. All Rights Reserved.</p> </main>
<p><a href="#">联系我们</a> | <a href="#">免责声明</a> | <a href="#">广告合作</a> | <a href="#">手机版</a></p>
<p>页面执行时间 0.045秒</p> <!-- Right Sidebar -->
<aside class="sidebar-right">
<div class="card">
<div class="card-header">🔥 24小时热帖</div>
<div class="card-body" style="padding:0;">
<ul id="hotThreadsList" style="padding:0; margin:0;">
<!-- JS Generated -->
</ul>
</div>
</div>
<div class="card">
<div class="card-header">📊 社区统计</div>
<div class="card-body" style="font-size:0.85rem; line-height:2;">
<div>在线会员: <strong style="color:green">1,024</strong></div>
<div>今日发帖: <strong>5,231</strong></div>
<div>总帖子数: <strong>8.4M</strong></div>
<div>服务器延迟: <span id="pingValue">12ms</span></div>
</div>
</div>
</aside>
</div> </div>
</div> <!-- Modals -->
<div class="modal-backdrop" id="loginModal">
<div class="modal-panel">
<div class="card-header">
用户登录 <span style="cursor:pointer; float:right;" onclick="closeModal('loginModal')">&times;</span>
</div>
<div class="card-body">
<div style="margin-bottom:15px;">
<label style="display:block; margin-bottom:5px;">用户名 / 邮箱</label>
<input type="text" id="loginUser" class="form-control" style="width:100%; padding:8px;" value="TechUser_2026">
</div>
<div style="margin-bottom:20px;">
<label style="display:block; margin-bottom:5px;">密码</label>
<input type="password" class="form-control" style="width:100%; padding:8px;" value="123456">
</div>
<button class="btn btn-primary btn-block" onclick="doLogin()">登录</button>
</div>
</div>
</div>
<div class="modal-backdrop" id="newThreadModal">
<div class="modal-panel" style="width:800px;">
<div class="card-header">
发布新主题 <span style="cursor:pointer; float:right;" onclick="closeModal('newThreadModal')">&times;</span>
</div>
<div class="card-body">
<input type="text" id="newPostTitle" placeholder="请输入标题..." style="width:100%; padding:10px; font-size:1.1rem; margin-bottom:10px; border:1px solid #ddd; border-radius:4px;">
<div class="editor-toolbar">
<button class="tool-btn"><b>B</b></button>
<button class="tool-btn"><i>I</i></button>
<button class="tool-btn"><u>U</u></button>
<button class="tool-btn" onclick="insertText(' [code] ', ' [/code] ')">Code</button>
<button class="tool-btn">🔗</button>
<button class="tool-btn">📷</button>
</div>
<textarea id="newPostContent" style="width:100%; height:300px; padding:10px; border:1px solid #ddd; border-top:none; resize:vertical;" placeholder="请详细描述您的问题..."></textarea>
<div style="margin-top:10px; text-align:right;">
<button class="btn" onclick="closeModal('newThreadModal')">取消</button>
<button class="btn btn-primary" onclick="submitNewThread()">发布帖子</button>
</div>
</div>
</div>
</div>
<div class="toast-box" id="toast"></div>
<script> <script>
function likePost(btn) { // === Single Page Application Logic (SPA) ===
let text = btn.innerText;
let count = parseInt(text.match(/\d+/)[0]); const DB = {
btn.innerText = "支持 (" + (count + 1) + ")"; users: [
btn.style.color = "red"; { id: 1, name: 'System_Admin', role: 'admin', avatar: 'https://ui-avatars.com/api/?name=SA&background=0D8ABC&color=fff' },
{ id: 2, name: 'Hardware_Guru', role: 'mod', avatar: 'https://ui-avatars.com/api/?name=HG&background=ff5722&color=fff' },
{ id: 3, name: 'Newbie_User_007', role: 'user', avatar: 'https://ui-avatars.com/api/?name=NU&background=4caf50&color=fff' },
{ id: 99, name: 'Spam_Bot_X', role: 'banned', avatar: '' }
],
threads: [], // Will be generated
};
const STATE = {
currentUser: null, // null = guest
currentView: 'home',
currentCategory: 'all',
viewingThreadId: null,
notifications: [],
page: 1
};
// --- Mock Data Generator ---
function initData() {
// 预设的高质量模拟数据源
const mockSources = [
{
title: "RTX 5090 性能偷跑,功耗高达 600W",
cat: "hardware",
content: "据外媒爆料,下一代旗舰显卡 RTX 5090 的 TGP 可能高达 600W。这意味着现有的 ATX 3.0 电源可能都要退役了。<br><br>从泄露的 PCB 设计图来看,核心面积比 4090 增加了 15%,显存可能采用 GDDR7。大家怎么看我是准备换电源了。",
replies: ["600W这不得开个空调对着吹", "电费伤不起啊,还是继续用我的 1060 吧。", "AMD Yes! 只要苏妈不跟进这种功耗竞赛。", "消息保真吗?感觉像是烟雾弹。", "正好冬天到了,不仅能玩游戏还能取暖,赢麻了!"]
},
{
title: "求助Win12 更新后蓝屏 Critical_Process_Died",
cat: "software",
content: "昨晚手贱更新了 Windows 12 预览版Build 26000重启后直接蓝屏进不去系统。<br><br>错误代码Critical_Process_Died。<br>尝试过安全模式也进不去,手头没有 PE 盘。里面有重要的毕业论文,求大神支招!不要重装系统啊!",
replies: ["预览版也敢主力机更新?勇士。", "试试制作一个 Linux Live USB 进去把文件拷出来。", "可能是驱动冲突,进 RE 模式回退更新试试。", "Critical_Process_Died 一般是系统核心文件损坏,大概率要重装了。", "没有备份吗?那基本凉了。"]
},
{
title: "【装机作业】白色海景房,颜值即正义!",
cat: "hardware",
content: "心心念念的白色海景房终于装好了!<br><br>配置单:<br>- CPU: i7-14700K<br>- MB: ROG Z790吹雪<br>- RAM: 威刚 吹雪联名 D5 6400<br>- GPU: 影驰 名人堂 4080<br>- Case: 联力 O11D Evo<br><br>[图片] 效果炸裂,光污染拉满。就是理线理得我腰酸背痛。",
replies: ["富哥V50看看实力。", "这理线水平可以啊,强迫症表示很舒适。", "全白确实好看,就是容易积灰,记得常清理。", "我也准备抄作业,这一套下来得 2W 吧?", "只有我关注桌子也是白的吗?"]
},
{
title: "Python 爬虫总是被封 IP 怎么办?",
cat: "software",
content: "最近在写一个爬虫抓取某电商平台的数据,但是频率稍微高一点就 403 Forbidden。<br><br>已经加了 User-Agent 池,延时也设了随机 1-3 秒,还是不行。是不是必须得上收费代理了?有没有开源好用的代理池推荐?",
replies: ["现在的反爬都很智能了,光换 UA 没用的。", "建议买隧道代理,免费的不稳定而且慢。", "看看有没有验证码或者 Cookie 追踪,封 IP 只是最后手段。", "降低频率吧,或者用 Selenium 模拟浏览器行为。", "少爬点吧,小心律师函警告 [滑稽]"]
},
{
title: "AMD YES! Ryzen 9000 系列参数前瞻",
cat: "hardware",
content: "AMD 官方 PPT 终于放出来了Zen 5 架构 IPC 提升 15%<br><br>最关键的是,积热问题似乎得到了改善。首发价格如果能维持在 7000 系列的水平Intel 这一代又要被吊打了。大家冲不冲?",
replies: ["AMD Yes! 喊就完事了。", "首发肯定贵,等半年降价才是 真·YES。", "只要不换主板接口,我就冲。", "积热改善?我不信,除非你有实测。", "Intel 15代也要出来了如果不急可以再等等。"]
},
{
title: "机械键盘轴体推荐:红轴还是茶轴?",
cat: "hardware",
content: "想入把机械键盘办公用,偶尔打打 LOL。<br><br>之前用过青轴,太吵了被同事投诉。现在纠结红轴和茶轴,听说红轴软绵绵的?茶轴又有段落感?<br>要求:声音小,手感好,久打不累。",
replies: ["办公绝对红轴,行云流水。", "茶轴是万用轴,有段落感打字比较爽。", "这就不得不推荐金粉轴了,比红轴还轻,办公神器。", "建议买个试轴器,几十块钱,把所有轴都摸一遍。", "静电容一步到位,退烧之选。"]
},
{
title: "显示器选购指南IPS vs OLED",
cat: "hardware",
content: "现在的 OLED 显示器越来越便宜了27寸 2K 240Hz 只要 3000 多。<br><br>但是很担心烧屏问题。我是重度 Windows 用户任务栏常驻。IPS 的 Nano-IPS 面板对比度又不行。到底该怎么选?主要用途是看电影和 3A 游戏。",
replies: ["看电影绝对 OLED黑场效果无敌。", "重度办公就算了吧,半年必烧。", "现在的 OLED 都有防烧屏技术,没那么脆弱。", "Mini-LED 考虑一下?折中方案。", "只要你有钱,坏了就换,那就无脑 OLED。"]
},
{
title: "公司服务器被雷劈了,如何恢复数据?",
cat: "water",
content: "真实惨案。昨天雷雨天机房不知怎么回事遭雷击了UPS 也没抗住。<br><br>现在 RAID 卡报错,有两块盘亮红灯。虽然有异地备份,但是是一周前的... 老板脸都绿了,让我死马当活马医,求靠谱的数据恢复机构!",
replies: ["这是物理损坏了,软件没救的,找专业开盘公司吧。", "同情楼主,这锅不该你背,是机房建设的问题。", "以前我们也遇到过,花了十几万才找回部分数据。", "千万别通电了!越通电损坏越严重!", "一周前的备份不错了,要是没备份你现在已经失业了。"]
},
{
title: "小白求推荐 3000 元价位组装机",
cat: "hardware",
content: "预算只有 3000想组一台能玩《黑神话悟空》的主机。<br><br>不要求全高画质1080P 中画质流畅就行。目前看了 12400F + 6600 的组合,这套稳吗?还是说等等党终将胜利?",
replies: ["3000 玩 3A 有点勉强,建议加钱上 4060。", "12400F + 6600 性价比很高1080P 没问题。", "二手市场淘一淘3000 能收到不错的配置。", "电源别省,炸了就是全家桶。", "黑悟空优化怎么样还不知道,建议等游戏出了再买。"]
},
{
title: "Linux 下如何配置 Nginx 反向代理?",
cat: "software",
content: "我在 Ubuntu 上部署了一个 Node.js 服务,跑在 3000 端口。<br><br>现在想通过域名直接访问,配置了 Nginx 的 proxy_pass但是静态资源全都 404 了。有没有完整的 conf 模板可以参考一下?我是新手,文档看不太懂。",
replies: ["静态资源要单独配 root 或 alias。", "直接贴你的配置文件出来,不然怎么帮你改?", "建议用 Docker 部署 Nginx Proxy Manager图形化界面很方便。", "chatGPT 很擅长写配置,直接问它。", "注意 location 的匹配优先级,可能是被覆盖了。"]
}
];
for (let i = 0; i < 45; i++) {
const seed = mockSources[i % mockSources.length]; // 循环使用模板
const author = DB.users[Math.floor(Math.random() * 3)];
// 随机波动回复数和浏览量
const replyCount = Math.floor(Math.random() * 150) + (seed.replies.length * 5);
const viewCount = replyCount * (Math.floor(Math.random() * 40) + 20);
// 稍微修改标题以避免完全重复
let titleVariant = seed.title;
if (i >= mockSources.length) {
const suffixes = ["(求助)", "[讨论]", "...", "!!", "(更新)"];
titleVariant += " " + suffixes[i % suffixes.length];
}
// 将预设回复存入 customReplies 属性,以便详情页生成时调用
DB.threads.push({
id: 1000 + i,
title: titleVariant,
author: author,
date: new Date(Date.now() - Math.floor(Math.random() * 2000000000)).toISOString(),
category: seed.cat,
views: viewCount,
replies: replyCount,
isHot: replyCount > 80,
content: seed.content, // 使用真实内容
mockReplies: seed.replies // 传递特定话题的回复池
});
}
// Add fake sidebar hot data
const hotList = document.getElementById('hotThreadsList');
DB.threads.slice(0, 5).sort((a,b) => b.replies - a.replies).forEach(t => {
hotList.innerHTML += `
<li class="thread-item" style="padding:10px; border:none;" onclick="openThread(${t.id})">
<span style="color:var(--primary); font-weight:bold;">${t.replies}</span>
<a href="javascript:void(0)" style="margin-left:10px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">${t.title}</a>
</li>`;
});
} }
function replyPost() { // --- Core View Logic ---
document.querySelector('textarea').focus(); function renderApp() {
document.querySelector('textarea').value = "回复 楼主:"; const container = document.getElementById('contentArea');
container.innerHTML = '';
document.getElementById('loading').style.display = 'block';
// Simulate network delay
setTimeout(() => {
document.getElementById('loading').style.display = 'none';
if (STATE.currentView === 'home') {
renderHome(container);
} else if (STATE.currentView === 'thread') {
renderThreadDetail(container, STATE.viewingThreadId);
}
// Update UI User State
updateUserUI();
}, 300);
} }
function reportPost() { function renderHome(container) {
alert("举报成功!管理员将尽快审核。"); // Filter Logic
let data = DB.threads;
if (STATE.currentCategory !== 'all') {
data = data.filter(t => t.category === STATE.currentCategory);
}
// Toolbar
const toolbar = document.createElement('div');
toolbar.className = 'card';
toolbar.innerHTML = `
<div class="card-body" style="display:flex; justify-content:space-between; align-items:center; padding:10px 15px;">
<div>
<span style="font-weight:bold; font-size:1.1rem;">${getCategoryName(STATE.currentCategory)}</span>
<span style="color:#888; margin-left:10px; font-size:0.9rem;">共 ${data.length} 个主题</span>
</div>
<div>
<button class="btn btn-primary" onclick="openModal('newThreadModal')">✍️ 发帖</button>
<select style="padding:5px; border-radius:4px; border:1px solid #ddd; margin-left:10px;" onchange="showToast('排序功能开发中...', 'info')">
<option>最新回复</option>
<option>最新发布</option>
<option>最多浏览</option>
</select>
</div>
</div>
`;
container.appendChild(toolbar);
// List
const listCard = document.createElement('div');
listCard.className = 'card';
// Header
listCard.innerHTML = `<div class="card-header" style="color:#666; font-size:0.8rem;">
<span style="flex:1;">主题</span>
<span style="width:100px; text-align:center;">作者</span>
<span style="width:80px; text-align:center;">回复/查看</span>
<span style="width:120px; text-align:right;">最后发表</span>
</div>`;
const listBody = document.createElement('div');
data.slice(0, 15).forEach(t => {
const row = document.createElement('div');
row.className = 'thread-item';
row.onclick = () => openThread(t.id);
row.style.cursor = 'pointer';
let tagsHtml = '';
if(t.isHot) tagsHtml += `<span class="tag red">HOT</span> `;
if(t.category === 'hardware') tagsHtml += `<span class="tag blue">硬件</span> `;
row.innerHTML = `
<div class="thread-stats">
<span class="stat-count">${t.replies}</span>
</div>
<div class="thread-main">
<span class="thread-title">${tagsHtml}${t.title}</span>
<span class="thread-meta">
发布于 ${formatDate(t.date)}
${t.replies > 50 ? '🔥' : ''}
</span>
</div>
<div style="width:100px; text-align:center; font-size:0.85rem;">
<img src="${t.author.avatar}" style="width:20px; vertical-align:middle; border-radius:50%;">
${t.author.name}
</div>
<div style="width:120px; text-align:right; font-size:0.8rem; color:#888;">
${timeAgo(t.date)}
</div>
`;
listBody.appendChild(row);
});
listCard.appendChild(listBody);
// Manual Pagination
const pagination = document.createElement('div');
pagination.className = 'pagination';
pagination.innerHTML = `
<div class="page-num active">1</div>
<div class="page-num">2</div>
<div class="page-num">3</div>
<div class="page-num">...</div>
<div class="page-num">9</div>
`;
container.appendChild(listCard);
container.appendChild(pagination);
} }
function renderThreadDetail(container, threadId) {
const thread = DB.threads.find(t => t.id === threadId);
if(!thread) { container.innerHTML = "Thread Not Found"; return; }
// Breadcrumb
container.innerHTML = `
<div style="margin-bottom:15px; font-size:0.9rem;">
<a onclick="switchView('home')">首页</a> &gt; ${getCategoryName(thread.category)} &gt; 正文
</div>
<div class="card">
<div class="post-title" style="border:none; border-bottom:1px solid var(--border);">
<h1 style="font-size:1.5rem; margin:0;">${thread.title}</h1>
<div style="margin-top:10px; font-size:0.85rem; color:#666;">
<span style="color:red;">[阅读权限 10]</span>
浏览: ${thread.views} | 回复: ${thread.replies}
</div>
</div>
</div>
`;
// Generate Posts (Mock Replies)
const posts = generateMockReplies(thread);
posts.forEach((post, index) => {
const isLouZhu = index === 0;
const postEl = document.createElement('div');
postEl.className = 'card';
if(post.isSpam) postEl.classList.add('spam-mask'); // CSS logic for spam
postEl.innerHTML = `
<div class="post-container">
<div class="post-sidebar">
<img src="${post.author.avatar}" class="post-avatar-lg">
<div style="font-weight:bold; color:var(--primary);">${post.author.name}</div>
<div class="user-badge" style="background:${post.author.role === 'admin' ? '#000' : '#eee'}; color:${post.author.role === 'admin' ? '#fff' : '#333'}">
${post.author.role.toUpperCase()}
</div>
<ul style="font-size:0.8rem; text-align:left; margin-top:10px; color:#666; padding-left:10px;">
<li>UID: ${post.author.id}</li>
<li>帖子: ${Math.floor(Math.random()*1000)}</li>
<li>金币: ${Math.floor(Math.random()*5000)}</li>
</ul>
</div>
<div class="post-content-area">
<div class="post-meta-header">
<span>发表于 ${formatDate(post.date)} ${isLouZhu ? '<span class="tag red">楼主</span>' : `#${index}`}</span>
<span>只看该作者 | 倒序排列</span>
</div>
<div class="post-body">
${post.content}
${post.attachment ? renderAttachment(post.attachment) : ''}
</div>
<div class="post-signature">
${post.signature || '这个人很懒,什么都没写。'}
</div>
<div class="post-actions">
<button class="btn-action" onclick="showToast('已举报', 'success')">举报</button>
<button class="btn-action">支持(${Math.floor(Math.random()*10)})</button>
<button class="btn-action" onclick="openReply()">回复</button>
</div>
</div>
</div>
`;
container.appendChild(postEl);
});
// Quick Reply Box
const replyBox = document.createElement('div');
replyBox.className = 'card';
replyBox.innerHTML = `
<div class="card-header">快速回复</div>
<div class="card-body">
<textarea style="width:100%; height:100px; padding:10px; border:1px solid #ddd;"></textarea>
<button class="btn btn-primary" style="margin-top:10px;" onclick="showToast('回复成功!(+1 积分)', 'success')">发表回复</button>
</div>
`;
container.appendChild(replyBox);
}
// --- Utils & Helpers ---
function generateMockReplies(thread) {
// Always include the OP
const list = [{
author: thread.author,
date: thread.date,
content: thread.content, // Now uses the realistic content content from initData
signature: "System.out.println('Hello World');",
attachment: thread.category === 'software' ? { name: "Error_Log.txt", size: "256 KB" } :
thread.category === 'hardware' ? { name: "BIOS_Backup.bin", size: "16 MB" } : null
}];
// Context-aware reply pool
const genericReplies = [
"楼主好人一生平安。", "这个教程太详细了,必须收藏!",
"前排围观大神。", "我也觉得是这样,完全同意。",
"有一说一,这个性价比确实高。", "建议楼主左转图吧,那里老哥个个是人才。",
"路过帮顶。", "不明觉厉。", "火钳刘明。"
];
// Use thread-specific replies first, then generic ones
const availableReplies = (thread.mockReplies && thread.mockReplies.length > 0)
? [...thread.mockReplies, ...genericReplies]
: genericReplies;
// Generate random replies (between 3 and 10)
const count = Math.floor(Math.random() * 8) + 3;
for(let i=0; i<count; i++) {
// Pick a reply and ensure we don't repeat too often if possible
const replyText = availableReplies[i % availableReplies.length];
list.push({
author: DB.users[Math.floor(Math.random() * DB.users.length)],
date: new Date(new Date(thread.date).getTime() + (i+1)*Math.random()*3600000).toISOString(), // Time progresses
content: replyText,
signature: `Signature_User_${Math.floor(Math.random()*999)}`
});
}
return list;
}
function renderAttachment(att) {
return `
<fieldset style="border:1px solid #ddd; padding:10px; margin-top:20px; background:#f9f9f9;">
<legend style="font-weight:bold;">附件</legend>
📄 ${att.name} (${att.size}) <a href="#">[点击下载]</a>
</fieldset>
`;
}
function getCategoryName(cat) {
const map = { all: '全部主题', hardware: '硬件专区', software: '软件分享', water: '灌水吐槽', market: '二手交易', feedback: '站务公告' };
return map[cat] || '未知板块';
}
// --- Actions ---
function switchView(viewName) {
STATE.currentView = viewName;
// Reset layout if needed
document.getElementById('mainLayout').className = viewName === 'thread' ? 'container layout-grid thread-view' : 'container layout-grid';
renderApp();
}
function openThread(id) {
STATE.viewingThreadId = id;
switchView('thread');
}
function filterCategory(cat) {
STATE.currentCategory = cat;
switchView('home');
}
// Modal & Toast
function openModal(id) {
document.getElementById(id).classList.add('show');
}
function closeModal(id) {
document.getElementById(id).classList.remove('show');
}
function showToast(msg, type) {
const t = document.getElementById('toast');
t.innerText = msg;
t.style.display = 'block';
t.style.background = type === 'warning' ? '#ff9800' : (type === 'success' ? '#4caf50' : '#333');
setTimeout(() => t.style.display = 'none', 3000);
}
// Auth Simulation
function doLogin() {
const user = document.getElementById('loginUser').value;
closeModal('loginModal');
STATE.currentUser = { name: user || "User" };
showToast(`欢迎回来,${STATE.currentUser.name}`, 'success');
updateUserUI();
}
function updateUserUI() {
const nav = document.getElementById('navUser');
if (STATE.currentUser) {
nav.innerHTML = `
<div style="display:flex; align-items:center; gap:10px; cursor:pointer;">
<span style="font-weight:bold;">${STATE.currentUser.name}</span>
<div class="user-avatar-sm" style="background:#4caf50;"></div>
<button class="btn" style="padding:2px 8px; font-size:0.8rem;" onclick="location.reload()">注销</button>
</div>
`;
}
}
function formatDate(iso) {
return iso.replace('T', ' ').substring(0, 16);
}
function timeAgo(props) {
return "1小时前"; // Simplified
}
function insertText(prefix, suffix) {
const area = document.getElementById('newPostContent');
area.value += prefix + suffix;
}
function submitNewThread() {
closeModal('newThreadModal');
showToast('发布成功!(模拟)', 'success');
// Fake add
setTimeout(() => {
DB.threads.unshift({
id: 9999,
title: document.getElementById('newPostTitle').value || "无标题",
author: STATE.currentUser || DB.users[2],
replies: 0, views: 1, category: 'hardware',
date: new Date().toISOString(),
isHot: false
});
renderHome(document.getElementById('contentArea'));
}, 500);
}
// --- Init ---
window.onload = function() {
initData();
renderApp();
// Background Ping Simulation
setInterval(() => {
const ms = Math.floor(Math.random() * 50) + 10;
document.getElementById('pingValue').innerText = ms + 'ms';
document.getElementById('pingValue').style.color = ms > 100 ? 'red' : 'green';
}, 2000);
};
</script> </script>
</body> </body>

File diff suppressed because it is too large Load Diff