This commit is contained in:
zpc 2025-12-25 23:43:27 +08:00
parent e8816c1ffb
commit c3c9eb1acd
8 changed files with 123 additions and 16 deletions

View File

@ -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
- 更新说明:完成前端重构
- 更新说明:完成浏览器插件重构

View File

@ -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')

View File

@ -192,7 +192,7 @@ function updateSearchResults(results) {
</div>
${item.tags.length > 0 ? `
<div class="bm-search-item-tags">
${item.tags.slice(0, 2).map(tag => `<span class="bm-search-tag">${escapeHtml(tag)}</span>`).join('')}
${item.tags.slice(0, 2).map(tag => `<span class="bm-search-tag"${tag.color ? ` style="background-color: ${tag.color}"` : ''}>${escapeHtml(tag.name)}</span>`).join('')}
</div>
` : ''}
</div>

View File

@ -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;
}
</style>
</head>
<body>
@ -292,6 +306,11 @@
<div class="result-content">
<div class="result-title">${escapeHtml(b.title)}</div>
<div class="result-url">${escapeHtml(b.url)}</div>
${b.tags && b.tags.length > 0 ? `
<div class="result-tags">
${b.tags.slice(0, 3).map(tag => `<span class="result-tag"${tag.color ? ` style="background-color: ${tag.color}; color: #fff"` : ''}>${escapeHtml(tag.name)}</span>`).join('')}
</div>
` : ''}
</div>
</a>
`).join('')}

View File

@ -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;

View File

@ -40,6 +40,9 @@
<input type="text" id="bookmark-title" placeholder="标题" />
<input type="url" id="bookmark-url" placeholder="URL" />
<input type="text" id="bookmark-tags" placeholder="标签(逗号分隔)" />
<select id="bookmark-folder">
<option value="">选择文件夹(可选)</option>
</select>
<button id="save-btn">收藏当前页</button>
</div>
<div id="save-result"></div>

View File

@ -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) {

View File

@ -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 })
})
}
}