Skip to content

Commit 1ae7fef

Browse files
authored
Create snippet-manager.js
1 parent c6085c0 commit 1ae7fef

1 file changed

Lines changed: 320 additions & 0 deletions

File tree

js/snippet-manager.js

Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
/**
2+
* Code Snippet Manager - 代码片段管理器
3+
* 作者: wangweihanNB
4+
* 功能: 保存、管理、搜索代码片段
5+
*/
6+
7+
(function() {
8+
'use strict';
9+
10+
// 数据存储
11+
let snippets = [];
12+
let currentFilter = 'all';
13+
let searchKeyword = '';
14+
15+
// DOM 元素
16+
const snippetName = document.getElementById('snippetName');
17+
const snippetCategory = document.getElementById('snippetCategory');
18+
const snippetCode = document.getElementById('snippetCode');
19+
const snippetTags = document.getElementById('snippetTags');
20+
const saveBtn = document.getElementById('saveBtn');
21+
const searchInput = document.getElementById('searchInput');
22+
const filterBtns = document.querySelectorAll('.filter-btn');
23+
const snippetsList = document.getElementById('snippetsList');
24+
const statsCount = document.getElementById('statsCount');
25+
const exportBtn = document.getElementById('exportBtn');
26+
const importBtn = document.getElementById('importBtn');
27+
const importFile = document.getElementById('importFile');
28+
const clearAllBtn = document.getElementById('clearAllBtn');
29+
const copyToast = document.getElementById('copyToast');
30+
31+
// 加载数据
32+
function loadSnippets() {
33+
const saved = localStorage.getItem('code_snippets');
34+
if (saved) {
35+
snippets = JSON.parse(saved);
36+
} else {
37+
// 添加示例片段
38+
snippets = [
39+
{
40+
id: Date.now(),
41+
name: 'fetch GET 请求',
42+
category: 'JavaScript',
43+
code: `fetch('https://api.example.com/data')
44+
.then(response => response.json())
45+
.then(data => console.log(data))
46+
.catch(error => console.error('Error:', error));`,
47+
tags: ['api', 'fetch', '请求'],
48+
createdAt: new Date().toISOString()
49+
},
50+
{
51+
id: Date.now() + 1,
52+
name: '数组去重',
53+
category: 'JavaScript',
54+
code: `const uniqueArray = [...new Set(array)];`,
55+
tags: ['数组', '去重', 'ES6'],
56+
createdAt: new Date().toISOString()
57+
}
58+
];
59+
saveSnippets();
60+
}
61+
renderSnippets();
62+
}
63+
64+
// 保存数据
65+
function saveSnippets() {
66+
localStorage.setItem('code_snippets', JSON.stringify(snippets));
67+
}
68+
69+
// 添加片段
70+
function addSnippet() {
71+
const name = snippetName.value.trim();
72+
const category = snippetCategory.value;
73+
const code = snippetCode.value.trim();
74+
const tagsInput = snippetTags.value.trim();
75+
76+
if (!name) {
77+
alert('请输入片段名称');
78+
return;
79+
}
80+
if (!code) {
81+
alert('请输入代码内容');
82+
return;
83+
}
84+
85+
const tags = tagsInput ? tagsInput.split(',').map(t => t.trim()) : [];
86+
87+
const newSnippet = {
88+
id: Date.now(),
89+
name: name,
90+
category: category,
91+
code: code,
92+
tags: tags,
93+
createdAt: new Date().toISOString()
94+
};
95+
96+
snippets.unshift(newSnippet);
97+
saveSnippets();
98+
99+
// 清空表单
100+
snippetName.value = '';
101+
snippetCode.value = '';
102+
snippetTags.value = '';
103+
104+
renderSnippets();
105+
showToast('✅ 片段已保存');
106+
}
107+
108+
// 删除片段
109+
function deleteSnippet(id) {
110+
if (confirm('确定要删除这个片段吗?')) {
111+
snippets = snippets.filter(s => s.id !== id);
112+
saveSnippets();
113+
renderSnippets();
114+
showToast('🗑️ 已删除');
115+
}
116+
}
117+
118+
// 复制代码
119+
async function copyCode(code) {
120+
try {
121+
await navigator.clipboard.writeText(code);
122+
showToast('📋 已复制到剪贴板');
123+
} catch (err) {
124+
// 降级方案
125+
const textarea = document.createElement('textarea');
126+
textarea.value = code;
127+
document.body.appendChild(textarea);
128+
textarea.select();
129+
document.execCommand('copy');
130+
document.body.removeChild(textarea);
131+
showToast('📋 已复制到剪贴板');
132+
}
133+
}
134+
135+
// 显示提示
136+
function showToast(message) {
137+
copyToast.textContent = message;
138+
copyToast.classList.add('show');
139+
setTimeout(() => {
140+
copyToast.classList.remove('show');
141+
}, 2000);
142+
}
143+
144+
// 过滤片段
145+
function getFilteredSnippets() {
146+
let filtered = snippets;
147+
148+
// 按分类过滤
149+
if (currentFilter !== 'all') {
150+
filtered = filtered.filter(s => s.category === currentFilter);
151+
}
152+
153+
// 按搜索关键词过滤
154+
if (searchKeyword) {
155+
const keyword = searchKeyword.toLowerCase();
156+
filtered = filtered.filter(s =>
157+
s.name.toLowerCase().includes(keyword) ||
158+
s.code.toLowerCase().includes(keyword) ||
159+
s.tags.some(tag => tag.toLowerCase().includes(keyword))
160+
);
161+
}
162+
163+
return filtered;
164+
}
165+
166+
// 渲染片段列表
167+
function renderSnippets() {
168+
const filtered = getFilteredSnippets();
169+
170+
if (filtered.length === 0) {
171+
snippetsList.innerHTML = '<div class="empty-state">✨ 暂无代码片段<br>点击上方添加你的第一个代码片段</div>';
172+
statsCount.textContent = '0';
173+
return;
174+
}
175+
176+
let html = '';
177+
filtered.forEach(snippet => {
178+
// 高亮搜索关键词
179+
let displayName = snippet.name;
180+
let displayCode = snippet.code;
181+
182+
if (searchKeyword) {
183+
const regex = new RegExp(`(${searchKeyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
184+
displayName = snippet.name.replace(regex, '<mark style="background:#f9e2af; color:#1e1e2e;">$1</mark>');
185+
}
186+
187+
html += `
188+
<div class="snippet-card" data-id="${snippet.id}">
189+
<div class="snippet-header">
190+
<span class="snippet-name">${displayName}</span>
191+
<span class="snippet-category">${snippet.category}</span>
192+
</div>
193+
<div class="snippet-tags">
194+
${snippet.tags.map(tag => `<span class="snippet-tag">#${escapeHtml(tag)}</span>`).join('')}
195+
</div>
196+
<pre class="snippet-code">${escapeHtml(snippet.code)}</pre>
197+
<div class="snippet-actions">
198+
<button class="copy-btn" data-code="${escapeHtml(snippet.code)}">📋 复制代码</button>
199+
<button class="delete-btn" data-id="${snippet.id}">🗑️ 删除</button>
200+
</div>
201+
</div>
202+
`;
203+
});
204+
205+
snippetsList.innerHTML = html;
206+
statsCount.textContent = filtered.length;
207+
208+
// 绑定事件
209+
document.querySelectorAll('.copy-btn').forEach(btn => {
210+
btn.addEventListener('click', (e) => {
211+
e.stopPropagation();
212+
const code = btn.getAttribute('data-code');
213+
copyCode(code);
214+
});
215+
});
216+
217+
document.querySelectorAll('.delete-btn').forEach(btn => {
218+
btn.addEventListener('click', (e) => {
219+
e.stopPropagation();
220+
const id = parseInt(btn.getAttribute('data-id'));
221+
deleteSnippet(id);
222+
});
223+
});
224+
}
225+
226+
// 导出数据
227+
function exportData() {
228+
const dataStr = JSON.stringify(snippets, null, 2);
229+
const blob = new Blob([dataStr], { type: 'application/json' });
230+
const url = URL.createObjectURL(blob);
231+
const a = document.createElement('a');
232+
a.href = url;
233+
a.download = `code-snippets-${new Date().toISOString().slice(0, 19)}.json`;
234+
a.click();
235+
URL.revokeObjectURL(url);
236+
showToast('📎 已导出');
237+
}
238+
239+
// 导入数据
240+
function importData(file) {
241+
const reader = new FileReader();
242+
reader.onload = (e) => {
243+
try {
244+
const imported = JSON.parse(e.target.result);
245+
if (Array.isArray(imported)) {
246+
snippets = [...imported, ...snippets];
247+
saveSnippets();
248+
renderSnippets();
249+
showToast(`📂 已导入 ${imported.length} 个片段`);
250+
} else {
251+
alert('文件格式错误');
252+
}
253+
} catch (err) {
254+
alert('解析失败,请确保是有效的 JSON 文件');
255+
}
256+
};
257+
reader.readAsText(file);
258+
}
259+
260+
// 清空全部
261+
function clearAll() {
262+
if (confirm('⚠️ 确定要删除所有代码片段吗?此操作不可撤销!')) {
263+
snippets = [];
264+
saveSnippets();
265+
renderSnippets();
266+
showToast('🗑️ 已清空全部');
267+
}
268+
}
269+
270+
// 事件绑定
271+
function bindEvents() {
272+
saveBtn.addEventListener('click', addSnippet);
273+
274+
searchInput.addEventListener('input', (e) => {
275+
searchKeyword = e.target.value;
276+
renderSnippets();
277+
});
278+
279+
filterBtns.forEach(btn => {
280+
btn.addEventListener('click', () => {
281+
filterBtns.forEach(b => b.classList.remove('active'));
282+
btn.classList.add('active');
283+
currentFilter = btn.getAttribute('data-category');
284+
renderSnippets();
285+
});
286+
});
287+
288+
exportBtn.addEventListener('click', exportData);
289+
290+
importBtn.addEventListener('click', () => {
291+
importFile.click();
292+
});
293+
294+
importFile.addEventListener('change', (e) => {
295+
if (e.target.files.length > 0) {
296+
importData(e.target.files[0]);
297+
importFile.value = '';
298+
}
299+
});
300+
301+
clearAllBtn.addEventListener('click', clearAll);
302+
}
303+
304+
// HTML 转义
305+
function escapeHtml(text) {
306+
const div = document.createElement('div');
307+
div.textContent = text;
308+
return div.innerHTML;
309+
}
310+
311+
// 初始化
312+
function init() {
313+
loadSnippets();
314+
bindEvents();
315+
console.log('✅ Code Snippet Manager 已启动');
316+
console.log('💡 作者: wangweihanNB');
317+
}
318+
319+
init();
320+
})();

0 commit comments

Comments
 (0)