feat: 设置页面添加管理后台入口

- 在账户区域添加"进入管理后台"按钮(仅管理员可见)
- 使用 localStorage 缓存用户信息确保显示正确

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-22 15:17:09 +08:00
parent 9fa6616110
commit 5de6cfc9de
4 changed files with 64 additions and 2 deletions

View File

@@ -21,7 +21,8 @@
- **BMI 计算** - 自动计算并显示健康状态(偏瘦/正常/偏胖/肥胖)
- **用户切换** - 支持多用户数据隔离
- **数据管理** - 按日期范围或数据类型清除数据
- **JWT 认证** - 安全的 Token 认证机制
- **JWT 认证** - HTTPOnly Cookie + Authorization Header 混合认证
- **记住我** - 可选 1 天或 30 天登录有效期
- **邀请码注册** - 通过邀请码控制用户注册
- **管理员面板** - 用户管理、邀请码管理
@@ -93,8 +94,9 @@ uvicorn vitals.web.app:app --host 0.0.0.0 --port 8080
| 端点 | 方法 | 说明 |
|------|------|------|
| `/api/auth/login` | POST | 用户登录,返回 JWT Token |
| `/api/auth/login` | POST | 用户登录,返回 JWT Token + 设置 Cookie |
| `/api/auth/register` | POST | 用户注册(需邀请码) |
| `/api/auth/logout` | POST | 用户登出,清除 Cookie |
| `/api/auth/me` | GET | 获取当前用户信息 |
### 管理员
@@ -428,6 +430,36 @@ vitals/
- [ ] 部分页面在小屏幕上布局需要调整
- [ ] 书籍封面搜索有时无法找到匹配结果
## 更新日志
### 2026-01-22
#### 🔐 混合认证方案优化
- 修改 login/register API 使用 JSONResponse 正确设置 Cookie
- 添加 `path="/"` 确保 Cookie 在所有路径可用
- 前端同时使用 localStorage token 进行 API 认证
- 修复登录后闪屏返回登录页的问题
#### ✨ 登录功能全面升级
- **服务端认证中间件** - 未登录用户自动重定向到登录页
- **HTTPOnly Cookie** - 使用 HTTPOnly Cookie 存储 token比 localStorage 更安全)
- **"记住我"功能** - 勾选30 天有效期不勾选1 天有效期
- **登出 API** - 新增 `/api/auth/logout` 接口
- **登录后重定向** - 支持登录后返回原访问页面
#### 🎨 登录/注册页面 UI 升级
- 采用 **Neumorphism新拟态** 设计风格
- 健康主题配色(青色 + 绿色渐变)
- Lora + Raleway 字体组合
- 柔和阴影效果,提升视觉体验
### 历史版本
- **2026-01-20** - 阅读模块上线(书库、心情追踪、统计图表)
- **2026-01-19** - 设置页面重构(用户档案、数据管理)
- **2026-01-17** - 公网部署支持JWT 认证、邀请码、管理员面板)
- **2026-01-15** - 第一版发布(核心健康追踪功能)
## License
MIT

View File

@@ -7174,6 +7174,11 @@ def get_settings_page_html() -> str:
</div>
</div>
<!-- 管理后台入口(仅管理员可见) -->
<a href="/admin" id="admin-entry" class="btn btn-primary" style="width: 100%; display: none; text-align: center; text-decoration: none; margin-bottom: 12px;">
进入管理后台
</a>
<button type="button" class="btn btn-danger" onclick="logoutAccount()" style="width: 100%;">
退出登录
</button>
@@ -7631,12 +7636,37 @@ def get_settings_page_html() -> str:
document.getElementById('account-role').textContent = user.is_admin ? '管理员账户' : '普通用户';
if (user.is_admin) {
document.getElementById('account-badge').style.display = 'block';
// 显示管理后台入口
document.getElementById('admin-entry').style.display = 'block';
}
// 保存到 localStorage
localStorage.setItem('user', JSON.stringify(user));
} else {
// 如果获取失败,尝试从 localStorage 获取
const cachedUser = localStorage.getItem('user');
if (cachedUser) {
const user = JSON.parse(cachedUser);
document.getElementById('account-name').textContent = user.name;
document.getElementById('account-role').textContent = user.is_admin ? '管理员账户' : '普通用户';
if (user.is_admin) {
document.getElementById('account-badge').style.display = 'block';
document.getElementById('admin-entry').style.display = 'block';
}
}
}
} catch (error) {
console.error('加载账户信息失败:', error);
// 尝试从 localStorage 获取
const cachedUser = localStorage.getItem('user');
if (cachedUser) {
const user = JSON.parse(cachedUser);
document.getElementById('account-name').textContent = user.name;
document.getElementById('account-role').textContent = user.is_admin ? '管理员账户' : '普通用户';
if (user.is_admin) {
document.getElementById('account-badge').style.display = 'block';
document.getElementById('admin-entry').style.display = 'block';
}
}
}
}