From c3c9eb1acdb767840a1260789507fe8dc787e161 Mon Sep 17 00:00:00 2001 From: zpc Date: Thu, 25 Dec 2025 23:43:27 +0800 Subject: [PATCH] 321 --- docs/数据库重构计划.md | 32 +++++++++++++++------ src/extension/background/background.js | 7 +++++ src/extension/content/content.js | 2 +- src/extension/newtab/index.html | 19 ++++++++++++ src/extension/popup/popup.css | 14 +++++++++ src/extension/popup/popup.html | 3 ++ src/extension/popup/popup.js | 40 ++++++++++++++++++++++++-- src/extension/shared/api.js | 22 ++++++++++++-- 8 files changed, 123 insertions(+), 16 deletions(-) diff --git a/docs/数据库重构计划.md b/docs/数据库重构计划.md index 37ba933..14aac06 100644 --- a/docs/数据库重构计划.md +++ b/docs/数据库重构计划.md @@ -107,7 +107,7 @@ --- -## 三、前端重构计划 +## 三、前端重构计划(已完成) ### 3.1 需要修改的文件清单 @@ -674,12 +674,26 @@ function renderSearchResult(bookmark) { - 标签管理入口 - [x] 构建验证通过 -### 8.3 浏览器插件重构(待开始) +### 8.3 浏览器插件重构(已完成 ✅) -- [ ] 修改 shared/api.js -- [ ] 修改 content/content.js 搜索结果渲染 -- [ ] 修改 newtab/index.html 书签显示 -- [ ] 修改 popup/popup.js 保存功能 +- [x] 修改 shared/api.js + - getBookmarks 新增 folderId 参数 + - 新增 getFolders() 方法 + - 新增 createTag() 方法 +- [x] 修改 content/content.js 搜索结果渲染 + - 适配新的 Tag 对象结构(tag.name, tag.color) +- [x] 修改 newtab/index.html 书签显示 + - 搜索结果中添加标签显示 + - 支持标签颜色 +- [x] 修改 popup/popup.js 保存功能 + - 新增文件夹选择器 + - 保存时支持 folderId +- [x] 修改 background/background.js + - 新增 getFolders 消息处理 +- [x] 修改 popup/popup.html + - 新增文件夹选择器 UI +- [x] 修改 popup/popup.css + - 新增文件夹选择器样式 --- @@ -695,8 +709,8 @@ function renderSearchResult(bookmark) { ## 十、版本信息 -- 文档版本:v2.1 +- 文档版本:v2.2 - 创建日期:2024-12-25 -- 最后更新:2024-12-25 +- 最后更新:2025-12-25 - 作者:Claude Code -- 更新说明:完成前端重构 +- 更新说明:完成浏览器插件重构 diff --git a/src/extension/background/background.js b/src/extension/background/background.js index 1676441..73715bf 100644 --- a/src/extension/background/background.js +++ b/src/extension/background/background.js @@ -159,6 +159,13 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { }) return true } + + if (request.action === 'getFolders') { + api.getFolders().then(result => { + sendResponse(result) + }) + return true + } }) console.log('Bookmark extension background script loaded') diff --git a/src/extension/content/content.js b/src/extension/content/content.js index 66b0d8a..feed5b7 100644 --- a/src/extension/content/content.js +++ b/src/extension/content/content.js @@ -192,7 +192,7 @@ function updateSearchResults(results) { ${item.tags.length > 0 ? `
- ${item.tags.slice(0, 2).map(tag => `${escapeHtml(tag)}`).join('')} + ${item.tags.slice(0, 2).map(tag => `${escapeHtml(tag.name)}`).join('')}
` : ''} diff --git a/src/extension/newtab/index.html b/src/extension/newtab/index.html index 2531957..353cd68 100644 --- a/src/extension/newtab/index.html +++ b/src/extension/newtab/index.html @@ -195,6 +195,20 @@ text-overflow: ellipsis; white-space: nowrap; } + + .result-tags { + display: flex; + gap: 4px; + margin-top: 4px; + } + + .result-tag { + font-size: 10px; + padding: 2px 6px; + border-radius: 4px; + background: #e0e7ff; + color: #4f46e5; + } @@ -292,6 +306,11 @@
${escapeHtml(b.title)}
${escapeHtml(b.url)}
+ ${b.tags && b.tags.length > 0 ? ` +
+ ${b.tags.slice(0, 3).map(tag => `${escapeHtml(tag.name)}`).join('')} +
+ ` : ''}
`).join('')} diff --git a/src/extension/popup/popup.css b/src/extension/popup/popup.css index 4c7fd4e..57b54c9 100644 --- a/src/extension/popup/popup.css +++ b/src/extension/popup/popup.css @@ -135,6 +135,20 @@ body { border-color: #667eea; } +#quick-save select { + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: 6px; + font-size: 13px; + outline: none; + background: #fff; + cursor: pointer; +} + +#quick-save select:focus { + border-color: #667eea; +} + #save-btn { padding: 8px 12px; background: #667eea; diff --git a/src/extension/popup/popup.html b/src/extension/popup/popup.html index 370373b..c10ae71 100644 --- a/src/extension/popup/popup.html +++ b/src/extension/popup/popup.html @@ -40,6 +40,9 @@ +
diff --git a/src/extension/popup/popup.js b/src/extension/popup/popup.js index 9391efb..27956c9 100644 --- a/src/extension/popup/popup.js +++ b/src/extension/popup/popup.js @@ -11,6 +11,7 @@ const deviceName = document.getElementById('device-name') const bookmarkTitle = document.getElementById('bookmark-title') const bookmarkUrl = document.getElementById('bookmark-url') const bookmarkTags = document.getElementById('bookmark-tags') +const bookmarkFolder = document.getElementById('bookmark-folder') const saveBtn = document.getElementById('save-btn') const saveResult = document.getElementById('save-result') const autoSyncCheckbox = document.getElementById('auto-sync') @@ -25,6 +26,7 @@ async function init() { await loadUserInfo() await loadCurrentTab() await loadSettings() + await loadFolders() } else { showLoginView() } @@ -76,6 +78,37 @@ async function loadSettings() { showNotificationCheckbox.checked = settings.showNotification } +// 加载文件夹列表 +async function loadFolders() { + try { + const result = await chrome.runtime.sendMessage({ action: 'getFolders' }) + if (result.success && result.data) { + // 清空现有选项(保留第一个默认选项) + while (bookmarkFolder.options.length > 1) { + bookmarkFolder.remove(1) + } + // 添加文件夹选项 + renderFolderOptions(result.data, 0) + } + } catch (e) { + console.error('Failed to load folders:', e) + } +} + +// 递归渲染文件夹选项 +function renderFolderOptions(folders, level) { + folders.forEach(folder => { + const option = document.createElement('option') + option.value = folder.id + option.textContent = ' '.repeat(level) + (level > 0 ? '└ ' : '') + folder.name + bookmarkFolder.appendChild(option) + // 递归渲染子文件夹 + if (folder.children && folder.children.length > 0) { + renderFolderOptions(folder.children, level + 1) + } + }) +} + // 保存设置 async function saveSettings() { await storage.setSettings({ @@ -130,16 +163,17 @@ saveBtn.addEventListener('click', async () => { const title = bookmarkTitle.value.trim() const url = bookmarkUrl.value.trim() const tags = bookmarkTags.value.split(',').map(t => t.trim()).filter(Boolean) - + const folderId = bookmarkFolder.value || null + if (!title || !url) { showSaveResult('请填写标题和URL', false) return } - + try { const result = await chrome.runtime.sendMessage({ action: 'createBookmark', - data: { title, url, tags } + data: { title, url, tags, folderId } }) if (result.success) { diff --git a/src/extension/shared/api.js b/src/extension/shared/api.js index a3c3a2e..09b21be 100644 --- a/src/extension/shared/api.js +++ b/src/extension/shared/api.js @@ -104,9 +104,12 @@ export const api = { }, // 获取书签列表 - async getBookmarks(tag) { - const params = tag ? `?tag=${encodeURIComponent(tag)}` : '' - return request(`/bookmarks${params}`) + async getBookmarks(tag = null, folderId = null) { + const params = new URLSearchParams() + if (tag) params.append('tag', tag) + if (folderId) params.append('folderId', folderId) + const queryString = params.toString() + return request(`/bookmarks${queryString ? '?' + queryString : ''}`) }, // 搜索书签 @@ -135,6 +138,19 @@ export const api = { // 获取标签列表 async getTags() { return request('/tags') + }, + + // 获取文件夹列表 + async getFolders() { + return request('/folders') + }, + + // 创建标签 + async createTag(name, color = null, icon = null) { + return request('/tags', { + method: 'POST', + body: JSON.stringify({ name, color, icon }) + }) } }