-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhexin.js
More file actions
213 lines (189 loc) · 6.72 KB
/
Copy pathhexin.js
File metadata and controls
213 lines (189 loc) · 6.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
/**
* hexin.js
* Bento 核心控制器与用户交互流程
* 驱动状态流转:首页看板 -> 分类详情 -> 实时拼音过滤 -> iOS 浅/深主题切换 -> 快捷键响应
*/
// 全局应用状态
const AppState = {
currentTab: 'home', // 默认首页:'home' (Bento 综合看板)
currentSearch: '', // 搜索关键字
currentCategory: 'all' // 当前选中的二级分类
};
// 缓存关键 DOM 节点
let DOM = {};
function initDOMRefs() {
DOM = {
tabBtns: document.querySelectorAll('.nav-pill-btn'),
searchInput: document.getElementById('searchInput'),
clearBtn: document.getElementById('clearSearch'),
catContainer: document.getElementById('categoryContainer'),
contentArea: document.getElementById('contentArea'),
themeToggle: document.getElementById('themeToggle')
};
}
/**
* iOS 浅色 / 深色主题管理
*/
function initTheme() {
// 默认启用夜间深色模式,升级 key 为 bento-theme-v2 避免旧访客本地存储的 light 缓存干扰
const savedTheme = localStorage.getItem('bento-theme-v2') || 'dark';
applyTheme(savedTheme);
if (DOM.themeToggle) {
DOM.themeToggle.addEventListener('click', () => {
const current = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light';
const next = current === 'dark' ? 'light' : 'dark';
applyTheme(next);
localStorage.setItem('bento-theme-v2', next);
});
}
}
function applyTheme(theme) {
if (theme === 'dark') {
document.documentElement.setAttribute('data-theme', 'dark');
if (DOM.themeToggle) DOM.themeToggle.textContent = '☀️';
} else {
document.documentElement.removeAttribute('data-theme');
if (DOM.themeToggle) DOM.themeToggle.textContent = '🌙';
}
}
/**
* 切换顶部导航 Tab
*/
window.switchNavTab = function(targetTab) {
if (!targetTab) return;
AppState.currentTab = targetTab;
AppState.currentCategory = 'all';
// 更新导航按钮 active 样式
document.querySelectorAll('.nav-pill-btn').forEach(btn => {
if (btn.dataset.tab === targetTab) {
btn.classList.add('active');
} else {
btn.classList.remove('active');
}
});
renderCurrentView();
};
/**
* 快捷点击标签雷达进行检索
*/
window.triggerQuickTagSearch = function(tag) {
if (!tag || !DOM.searchInput) return;
DOM.searchInput.value = tag;
AppState.currentSearch = tag;
DOM.clearBtn.style.display = 'flex';
DOM.searchInput.focus();
updateContent();
};
/**
* 更新主内容区域:结合分类过滤与拼音/文本搜索
*/
function updateContent() {
// 1. 搜索状态处理(在任意视图下,若有搜索词则进行即时检索)
if (AppState.currentSearch) {
if (DOM.catContainer) DOM.catContainer.innerHTML = '';
if (AppState.currentTab === 'home') {
const sArticles = filterDataByKeyword(DataStore.articles, AppState.currentSearch);
const sTools = filterDataByKeyword(DataStore.tools, AppState.currentSearch);
const sPolicies = filterDataByKeyword(DataStore.policies, AppState.currentSearch);
renderGlobalSearch(sArticles, sTools, sPolicies);
return;
} else {
let activeData = DataStore[AppState.currentTab] || [];
activeData = filterDataByKeyword(activeData, AppState.currentSearch);
activeData = filterDataByCategory(activeData, AppState.currentCategory);
renderByActiveTab(activeData);
return;
}
}
// 2. 无搜索词时的常规渲染
if (AppState.currentTab === 'home') {
if (DOM.catContainer) DOM.catContainer.innerHTML = '';
renderBentoDashboard(DataStore.articles, DataStore.tools, DataStore.policies);
} else {
let activeData = DataStore[AppState.currentTab] || [];
activeData = filterDataByCategory(activeData, AppState.currentCategory);
renderByActiveTab(activeData);
}
}
/**
* 根据当前活跃 Tab 调用对应渲染器
*/
function renderByActiveTab(data) {
if (AppState.currentTab === 'articles') {
renderArticles(data);
} else if (AppState.currentTab === 'tools') {
renderTools(data);
} else if (AppState.currentTab === 'policies') {
renderPolicies(data);
}
}
/**
* 重新布置当前界面的二级分类选择器与主内容
*/
function renderCurrentView() {
if (AppState.currentTab !== 'home') {
const catSet = DataStore.categories[AppState.currentTab];
renderCategories(catSet);
document.querySelectorAll('.cat-filter-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
document.querySelectorAll('.cat-filter-btn').forEach(b => b.classList.remove('active'));
e.target.classList.add('active');
AppState.currentCategory = e.target.dataset.cat;
updateContent();
});
});
} else {
if (DOM.catContainer) DOM.catContainer.innerHTML = '';
}
updateContent();
}
/**
* 绑定所有 DOM 交互与全局快捷键事件
*/
function bindEvents() {
// 1. 顶部 Tab 切换
DOM.tabBtns.forEach(btn => {
btn.addEventListener('click', (e) => {
const targetTab = e.currentTarget.dataset.tab;
switchNavTab(targetTab);
});
});
// 2. 搜索框输入监听 (支持即时响应)
DOM.searchInput.addEventListener('input', (e) => {
AppState.currentSearch = e.target.value.trim();
DOM.clearBtn.style.display = AppState.currentSearch ? 'flex' : 'none';
updateContent();
});
// 3. 清空搜索按钮
DOM.clearBtn.addEventListener('click', () => {
AppState.currentSearch = '';
DOM.searchInput.value = '';
DOM.clearBtn.style.display = 'none';
DOM.searchInput.focus();
updateContent();
});
// 4. 全局快捷键:Ctrl + K 或 "/" 聚焦搜索框
window.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {
e.preventDefault();
DOM.searchInput.focus();
DOM.searchInput.select();
} else if (e.key === '/' && document.activeElement !== DOM.searchInput) {
e.preventDefault();
DOM.searchInput.focus();
} else if (e.key === 'Escape' && document.activeElement === DOM.searchInput) {
DOM.searchInput.blur();
}
});
}
/**
* 应用总入口
*/
async function startApp() {
initDOMRefs();
initTheme();
await initData();
bindEvents();
renderCurrentView();
}
window.addEventListener('DOMContentLoaded', startApp);