refactor: 重写导航栏布局与交互

- 导航项重新排序:首页、投稿、信封图标、头像下拉菜单
- 铃铛图标替换为信封图标
- 用户名/设置/退出移入头像 hover 下拉菜单
- 设置改名为个人中心,退出改名为退出登录
- 头像点击跳转 /space,hover 展开下拉菜单
- 无头像时显示用户名首字彩色圆形占位
- Avatar 数据链路打通:Session→Middleware→BuildPageData→模板
- FindByIDForAuth 查询增加 avatar 列
- 新增 substr 模板函数
This commit is contained in:
2026-05-31 16:17:35 +08:00
parent a3f787ddaf
commit 9fe87a7003
8 changed files with 174 additions and 44 deletions

View File

@ -86,6 +86,9 @@ func BuildPageData(c *gin.Context, extra gin.H) gin.H {
data["IsLoggedIn"] = true
data["Username"] = username
}
if avatar, exists := c.Get("avatar"); exists {
data["Avatar"] = avatar
}
// 注入 CSRF token由 CSRF 中间件设置到上下文中)
if token, exists := c.Get("csrf_token"); exists {
data["CSRFToken"] = token

View File

@ -77,6 +77,7 @@ func injectSessionContext(c *gin.Context, s *session.Session) {
c.Set("uid", s.UserID)
c.Set("email", s.Email)
c.Set("username", s.Username)
c.Set("avatar", s.Avatar)
c.Set("role", s.Role)
c.Set("sid", s.ID)
}

View File

@ -74,11 +74,10 @@ func (r *UserRepo) ExistsByEmailExclude(email string, excludeUID uint) (bool, er
return count > 0, err
}
// FindByIDForAuth 认证专用查询:返回 uid/email/username/role/status
// 比 FindByID 轻量(不查 avatar、bio 等无用字段),避免全量 SELECT *
// FindByIDForAuth 认证专用查询:返回 uid/email/username/avatar/role/status
func (r *UserRepo) FindByIDForAuth(userID uint) (*model.User, error) {
var user model.User
err := r.db.Select("uid", "email", "username", "role", "status").
err := r.db.Select("uid", "email", "username", "avatar", "role", "status").
First(&user, userID).Error
if err != nil {
return nil, err

View File

@ -37,6 +37,7 @@ func (m *Manager) Create(user *model.User, rememberMe bool, ip, userAgent string
UserID: user.ID,
Email: user.Email,
Username: user.Username,
Avatar: user.Avatar,
Role: user.Role,
RememberMe: rememberMe,
IP: ip,

View File

@ -12,6 +12,7 @@ type Session struct {
UserID uint // 用户 ID
Email string // 邮箱
Username string // 用户名
Avatar string // 头像 URL
Role string // 角色
RememberMe bool // 是否持久化(决定 cookie maxAge
IP string // 登录 IP

View File

@ -24,6 +24,17 @@ func LoadTemplates(roots ...TemplateRoot) (*template.Template, error) {
"subtract": func(a, b int) int { return a - b },
"formatTTL": formatTTL,
"assetV": common.AssetV,
"substr": func(s string, start, length int) string {
runes := []rune(s)
if start >= len(runes) {
return ""
}
end := start + length
if end > len(runes) {
end = len(runes)
}
return string(runes[start:end])
},
// derefUint 安全解引用 *uint 指针。
// Go 1.26 的模板引擎在将 *uint 传给 printf/print 时不会自动解引用,
// 导致打印指针地址而非值。通过此函数显式解引用后传给 printf。