diff --git a/README.md b/README.md index f11442c..ec12e79 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ - 🔄 **OAuth2 助手** - 内置 OAuth2 授权流程,快速获取 Refresh Token - 💾 **邮件缓存** - 智能缓存邮件列表,切换邮箱/文件夹即时展示,刷新页面清空缓存(⭐ 2026-01-29 新增) - 🔓 **信任邮件模式** - 支持查看原始邮件内容,可选择绕过安全过滤(⭐ 2026-01-29 新增) +- 🏷️ **标签管理** - 支持给邮箱账号打标签,支持批量打标签/去标签,支持按标签筛选(⭐ 2026-01-31 新增) #### Token 刷新管理 - 🔁 **全量刷新** - 一键刷新所有账号 Token,实时进度展示,防止 90 天过期(⭐ 2026-01-23 新增) @@ -95,6 +96,9 @@ Web 应用采用四栏式布局设计: ### 登录速率限制保护 ![登录失败强制间隔](img/登录失败强制间隔.png) +### 标签管理功能(⭐ 2026-01-31 新增) +![标签管理](img/标签管理.png) + ### OAuth2 授权流程 #### 步骤 1:应用注册 @@ -438,7 +442,38 @@ user@outlook.com----password123----24d9a0ed-8787-4584-883c-2fd79308940a----0.AXE - **删除分组** - 点击分组旁的「删除」按钮(默认分组不可删除) - **导出分组** - 点击分组旁的「导出」按钮,导出该分组下的所有邮箱账号 -### 6. 系统设置 +### 6. 标签管理(⭐ 2026-01-31 新增) + +标签功能允许您为邮箱账号添加自定义标签,方便分类和筛选。 + +#### 创建和管理标签 +1. 点击邮箱面板标题栏的「🏷️」按钮,打开标签管理弹窗 +2. 输入标签名称,选择标签颜色,点击「创建」 +3. 已有标签会显示在列表中,可点击右侧的「删除」按钮移除 + +#### 给邮箱打标签 +1. 在邮箱列表中,勾选一个或多个邮箱账号(每个邮箱左侧有复选框) +2. 底部会出现批量操作栏,显示「已选 N 项」 +3. 点击「打标签」按钮,选择要添加的标签,确认即可 +4. 标签会以彩色徽章形式显示在邮箱备注下方 + +#### 去除邮箱标签 +1. 勾选已打标签的邮箱账号 +2. 点击底部操作栏的「去标签」按钮 +3. 选择要移除的标签,确认即可 + +#### 按标签筛选邮箱 +1. 在排序按钮下方,有一行标签筛选区(仅在有标签时显示) +2. 勾选一个或多个标签复选框 +3. 邮箱列表会自动过滤,只显示包含所选标签的账号 +4. 取消所有勾选可恢复显示全部账号 + +#### 搜索标签 +- 在全局搜索框中输入标签名称,可搜索到带有该标签的邮箱账号 + +![标签管理](img/标签管理.png) + +### 7. 系统设置 点击导航栏的「⚙️ 设置」按钮,可以修改以下配置: @@ -454,7 +489,7 @@ user@outlook.com----password123----24d9a0ed-8787-4584-883c-2fd79308940a----0.AXE 设置会保存在数据库中,重启应用后仍然有效。 -### 7. Token 刷新管理(⭐ 2026-01-23 新增,2026-01-25 增强) +### 8. Token 刷新管理(⭐ 2026-01-23 新增,2026-01-25 增强) 为了防止 Refresh Token 因 90 天不使用而过期,系统提供了强大的 Token 刷新管理功能: diff --git a/img/标签管理.png b/img/标签管理.png new file mode 100644 index 0000000..7d0e41d Binary files /dev/null and b/img/标签管理.png differ diff --git a/templates/index.html b/templates/index.html index a697cc0..444d463 100644 --- a/templates/index.html +++ b/templates/index.html @@ -391,12 +391,11 @@ .account-remark { font-size: 12px; color: #666; - padding-left: 20px; + padding-left: 0; margin-top: 4px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 300px; + white-space: pre-wrap; + word-break: break-all; + line-height: 1.4; } .account-email { @@ -1351,13 +1350,14 @@ style="padding: 4px 10px; font-size: 12px; background-color: #ffffff; border: 1px solid #e5e5e5; border-radius: 4px; color: #666; cursor: pointer; transition: all 0.15s;"> 📧 邮箱名 -
- + + +
📁
@@ -2686,12 +2686,16 @@ ${details} let result = [...accounts]; // 1. Tag 筛选 - const tagFilter = document.getElementById('tagFilter'); - const selectedTagId = tagFilter ? tagFilter.value : ''; - if (selectedTagId) { + // Get checked tags + const checkedBoxes = document.querySelectorAll('.tag-filter-checkbox:checked'); + const selectedTagIds = Array.from(checkedBoxes).map(cb => parseInt(cb.value)); + + if (selectedTagIds.length > 0) { result = result.filter(acc => { if (!acc.tags) return false; - return acc.tags.some(t => t.id === parseInt(selectedTagId)); + // Check if account has ANY of the selected tags (OR logic) + // If you want AND logic, use every() instead of some() + return acc.tags.some(t => selectedTagIds.includes(t.id)); }); } @@ -2712,7 +2716,7 @@ ${details} } // Tag Filter Change Handler - function filterAccountsByTag(tagId) { + function handleTagFilterChange() { if (accountsCache[currentGroupId]) { const filteredAccounts = applyFiltersAndSort(accountsCache[currentGroupId]); renderAccountList(filteredAccounts); @@ -5067,20 +5071,30 @@ ${details} // 更新标签筛选下拉框 function updateTagFilter() { - const filterEl = document.getElementById('tagFilter'); - if (!filterEl) return; + const container = document.getElementById('tagFilterContainer'); + if (!container) return; - const currentValue = filterEl.value; - let html = ''; - allTags.forEach(tag => { - html += ``; - }); - filterEl.innerHTML = html; - - // 尝试保持选中 - if (currentValue && allTags.find(t => t.id == currentValue)) { - filterEl.value = currentValue; + if (allTags.length === 0) { + container.style.display = 'none'; + return; } + + container.style.display = 'flex'; + + let html = ''; + allTags.forEach(tag => { + html += ` + + `; + }); + container.innerHTML = html; + /* Old dropdown code removed */ + + } // 渲染标签列表