- 替换全部 11 个文件中 36 处 alert() 为 showToast() - API 错误/异常使用 showToast(msg, 'error'),3秒自动消失 - 表单校验提示使用 showToast(msg, 'warning'),3秒自动消失 - 成功确认使用 showToast(msg, 'success'),3秒自动消失 - 移除 post-energize.js 中与全局 showToast 冲突的本地实现 - 删除 posts.css 中已失去引用的 .energize-toast 样式 - 保留 showConfirm() 自定义确认弹窗(需用户确认的操作)
754 lines
38 KiB
HTML
754 lines
38 KiB
HTML
{{template "layout/header.html" .}}
|
||
<body>
|
||
|
||
{{template "layout/nav.html" .}}
|
||
|
||
{{if .Error}}
|
||
<div class="space-error">
|
||
<div class="container">
|
||
<h1>{{.Error}}</h1>
|
||
<a href="/" class="btn btn-primary">返回首页</a>
|
||
</div>
|
||
</div>
|
||
{{else}}
|
||
<div class="space-page">
|
||
|
||
<!-- ====== 顶部信息栏(访客+本人共用) ====== -->
|
||
<div class="space-profile-header">
|
||
<div class="space-header-inner">
|
||
<!-- 左:头像 + 用户名 -->
|
||
<div class="space-header-user">
|
||
<a href="/space/{{.SpaceUser.ID}}" class="space-avatar-link">
|
||
{{if .SpaceUser.Avatar}}
|
||
<img src="{{.SpaceUser.Avatar}}" alt="{{.SpaceUser.Username}}" class="space-avatar-img">
|
||
{{else}}
|
||
<div class="space-avatar-placeholder"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg></div>
|
||
{{end}}
|
||
</a>
|
||
<div class="space-header-info">
|
||
<span class="space-username">{{.SpaceUser.Username}}</span>
|
||
{{if .SpaceUser.Bio}}
|
||
<p class="space-header-bio">{{.SpaceUser.Bio}}</p>
|
||
{{end}}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右:操作按钮 -->
|
||
<div class="space-header-actions">
|
||
<button id="space-follow-btn" class="space-action-btn primary" data-uid="{{.SpaceUser.ID}}" data-current-uid="{{.CurrentUID}}">+ 关注</button>
|
||
{{if not .IsOwnSpace}}
|
||
<button class="space-action-btn">发消息</button>
|
||
<button class="space-action-btn icon-only" title="更多">⋮</button>
|
||
{{end}}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ====== Tab 导航栏(区分本人/访客) ====== -->
|
||
{{if .IsOwnSpace}}
|
||
<!-- 本人视图:左Tab + 右统计 -->
|
||
<div class="space-tab-bar owner">
|
||
<div class="space-tabs-left">
|
||
<a href="/space/{{.SpaceUser.ID}}?tab=articles"
|
||
class="space-tab{{if eq .ActiveTab "articles"}} active{{end}}">
|
||
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zM6 20V4h7v5h5v11H6z"/></svg>
|
||
文章
|
||
<span class="tab-count">{{.PostCount}}</span>
|
||
</a>
|
||
<a href="/space/{{.SpaceUser.ID}}?tab=collections"
|
||
class="space-tab{{if eq .ActiveTab "collections"}} active{{end}}">
|
||
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M17 3H7c-1.1 0-2 .9-2 2v16l7-3 7 3V5c0-1.1-.9-2-2-2z"/></svg>
|
||
收藏
|
||
<span class="tab-count">{{.TotalFavorites}}</span>
|
||
</a>
|
||
<a href="/space/{{.SpaceUser.ID}}?tab=settings"
|
||
class="space-tab{{if eq .ActiveTab "settings"}} active{{end}}">
|
||
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M19.14 12.94c.04-.31.06-.63.06-.94 0-.31-.02-.63-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94L14.4 2.81c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>
|
||
设置
|
||
</a>
|
||
</div>
|
||
<div class="space-stats-right">
|
||
<div class="stat-item">
|
||
<span class="stat-label">关注</span>
|
||
<span class="stat-value">{{.SpaceUser.FollowingCount}}</span>
|
||
</div>
|
||
<div class="stat-item">
|
||
<span class="stat-label">粉丝</span>
|
||
<span class="stat-value">{{.SpaceUser.FollowersCount}}</span>
|
||
</div>
|
||
<div class="stat-item">
|
||
<span class="stat-label">获赞</span>
|
||
<span class="stat-value">{{formatCount .TotalLikes}}</span>
|
||
</div>
|
||
<div class="stat-item">
|
||
<span class="stat-label">阅读量</span>
|
||
<span class="stat-value">{{formatCount .TotalReads}}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{{else}}
|
||
<!-- 访客视图:统计栏 -->
|
||
<div class="space-tab-bar visitor">
|
||
<div class="space-tabs-left">
|
||
<a href="/space/{{.SpaceUser.ID}}?tab=articles"
|
||
class="space-tab{{if eq .ActiveTab "articles"}} active{{end}}">
|
||
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zM6 20V4h7v5h5v11H6z"/></svg>
|
||
文章
|
||
<span class="tab-count">{{.PostCount}}</span>
|
||
</a>
|
||
<a href="/space/{{.SpaceUser.ID}}?tab=following"
|
||
class="space-tab{{if or (eq .ActiveTab "following") (eq .ActiveTab "followers")}} active{{end}}">
|
||
<span class="stat-label-inline">关注数</span>
|
||
<span class="tab-count">{{.SpaceUser.FollowingCount}}</span>
|
||
</a>
|
||
<a href="/space/{{.SpaceUser.ID}}?tab=followers" class="space-tab">
|
||
<span class="stat-label-inline">粉丝数</span>
|
||
<span class="tab-count">{{.SpaceUser.FollowersCount}}</span>
|
||
</a>
|
||
<div class="stat-item-inline">
|
||
<span class="stat-label-inline">获赞数</span>
|
||
<span class="stat-value-inline">{{formatCount .TotalLikes}}</span>
|
||
</div>
|
||
<div class="stat-item-inline">
|
||
<span class="stat-label-inline">阅读量</span>
|
||
<span class="stat-value-inline">{{formatCount .TotalReads}}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{{end}}
|
||
|
||
<!-- ====== 主内容区(左右分栏) ====== -->
|
||
<div class="space-card-layout">
|
||
|
||
{{/* ========== 文章 Tab ==========*/}}
|
||
{{if eq .ActiveTab "articles"}}
|
||
<main class="space-main-full">
|
||
<h1 class="space-section-title">{{if .IsOwnSpace}}我的文章{{else}}Ta 的文章{{end}}</h1>
|
||
{{if .Posts}}
|
||
{{if gt (len .Posts) 0}}
|
||
<div class="space-post-list">
|
||
{{range $i, $post := .Posts}}
|
||
<article class="space-post-card">
|
||
<a href="/posts/{{$post.ID}}" class="space-post-title">{{$post.Title}}</a>
|
||
<p class="space-post-excerpt">{{if $post.Excerpt}}{{printf "%.120s" $post.Excerpt}}{{else}}{{printf "%.120s" $post.Body}}{{end}}</p>
|
||
<div class="space-post-meta">
|
||
<span class="post-meta-item">
|
||
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></svg>
|
||
{{$post.LikesCount}}
|
||
</span>
|
||
<span class="post-meta-item">
|
||
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M17 3H7c-1.1 0-2 .9-2 2v16l7-3 7 3V5c0-1.1-.9-2-2-2z"/></svg>
|
||
{{$post.FavoritesCount}}
|
||
</span>
|
||
<span class="post-meta-item">
|
||
<svg viewBox="0 0 24 24" width="14" height="14"><path fill="currentColor" d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></svg>
|
||
{{$post.CommentsCount}}
|
||
</span>
|
||
<span class="post-meta-time">{{$post.CreatedAt.Format "01-02 15:04"}}</span>
|
||
</div>
|
||
</article>
|
||
{{end}}
|
||
</div>
|
||
{{if gt .TotalPages 1}}
|
||
<div class="pagination">
|
||
{{if gt .Page 1}}
|
||
<a href="?tab=articles&page={{.PrevPage}}" class="page-link">上一页</a>
|
||
{{end}}
|
||
<span class="page-info">第 {{.Page}} / {{.TotalPages}} 页</span>
|
||
{{if le .Page .TotalPages}}
|
||
<a href="?tab=articles&page={{.NextPage}}" class="page-link">下一页</a>
|
||
{{end}}
|
||
</div>
|
||
{{end}}
|
||
{{else}}
|
||
<div class="space-empty-state">
|
||
<p>{{if .IsOwnSpace}}还没有发布过文章,去创作吧~{{else}}Ta 还没有发布文章{{end}}</p>
|
||
</div>
|
||
{{end}}
|
||
{{else}}
|
||
<div class="space-empty-state">
|
||
<p>暂无数据</p>
|
||
</div>
|
||
{{end}}
|
||
</main>
|
||
|
||
{{/* ========== 关注列表 Tab(含本人+访客) ==========*/}}
|
||
{{end}}
|
||
{{if or (eq .ActiveTab "following") (eq .ActiveTab "followers")}}
|
||
<!-- 左侧导航 -->
|
||
<aside class="space-sidebar">
|
||
<h2 class="space-sidebar-title">{{if .IsOwnSpace}}我的关注{{else}}Ta的关注{{end}}</h2>
|
||
<nav class="space-sidebar-nav">
|
||
<a href="/space/{{.SpaceUser.ID}}?tab=following"
|
||
class="space-sidebar-link{{if eq .ActiveTab "following"}} active{{end}}">
|
||
<svg class="space-sidebar-icon" viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></svg>
|
||
{{if .IsOwnSpace}}全部关注{{else}}全部关注{{end}}
|
||
<span class="space-sidebar-count">{{.SpaceUser.FollowingCount}}</span>
|
||
</a>
|
||
<a href="/space/{{.SpaceUser.ID}}?tab=followers"
|
||
class="space-sidebar-link{{if eq .ActiveTab "followers"}} active{{end}}">
|
||
<svg class="space-sidebar-icon" viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></svg>
|
||
{{if .IsOwnSpace}}粉丝{{else}}Ta的粉丝{{end}}
|
||
<span class="space-sidebar-count">{{.SpaceUser.FollowersCount}}</span>
|
||
</a>
|
||
</nav>
|
||
</aside>
|
||
|
||
<!-- 右侧内容区 -->
|
||
<main class="space-main">
|
||
<h1 class="space-main-title">{{if eq .ActiveTab "following"}}全部关注{{else}}{{if .IsOwnSpace}}粉丝{{else}}Ta的粉丝{{end}}{{end}}</h1>
|
||
|
||
{{/* ---- 关注列表 ---- */}}
|
||
{{if eq .ActiveTab "following"}}
|
||
{{if .FollowingResult}}
|
||
{{if .FollowingResult.Accessible}}
|
||
{{if eq (len .FollowingResult.Items) 0}}
|
||
<div class="space-empty-state">
|
||
<p>{{if .IsOwnSpace}}暂未关注任何人,去发现有趣的创作者吧~{{else}}Ta 暂未关注任何人{{end}}</p>
|
||
</div>
|
||
{{else}}
|
||
{{if .IsOwnSpace}}
|
||
<!-- 本人:带搜索的用户列表 -->
|
||
<div class="space-follow-search">
|
||
<input type="text" class="follow-search-input" placeholder="输入关键词">
|
||
<button class="follow-search-btn">
|
||
<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
|
||
</button>
|
||
</div>
|
||
{{end}}
|
||
<div class="space-user-grid">
|
||
{{range $i, $item := .FollowingResult.Items}}
|
||
<div class="space-user-card" data-uid="{{$item.FolloweeID}}">
|
||
<a href="/space/{{$item.FolloweeID}}" class="space-user-avatar-wrap">
|
||
{{if $item.FolloweeAvatar}}
|
||
<img src="{{$item.FolloweeAvatar}}" alt="{{$item.FolloweeUsername}}" class="space-user-avatar-img">
|
||
{{else}}
|
||
<div class="space-user-avatar-placeholder"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg></div>
|
||
{{end}}
|
||
</a>
|
||
<div class="space-user-info">
|
||
<a href="/space/{{$item.FolloweeID}}" class="space-user-name">{{$item.FolloweeUsername}}</a>
|
||
{{if $item.FolloweeBio}}
|
||
<p class="space-user-bio">{{printf "%.50s" $item.FolloweeBio}}</p>
|
||
{{end}}
|
||
</div>
|
||
{{if $.IsOwnSpace}}
|
||
<button class="space-follow-btn followed" disabled>已关注</button>
|
||
{{else}}
|
||
<button class="space-follow-btn" data-uid="{{$item.FolloweeID}}">关注</button>
|
||
{{end}}
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
{{end}}
|
||
{{else}}
|
||
<!-- 隐私关闭 -->
|
||
<div class="space-private-empty">
|
||
<div class="space-private-illustration">
|
||
<svg viewBox="0 0 120 100" width="120" height="100">
|
||
<rect x="15" y="25" width="90" height="60" rx="6" fill="#f0f0f0" stroke="#e0e0e0" stroke-width="2"/>
|
||
<rect x="25" y="35" width="40" height="6" rx="3" fill="#e0e0e0"/>
|
||
<rect x="25" y="47" width="70" height="4" rx="2" fill="#eee"/>
|
||
<rect x="25" y="57" width="55" height="4" rx="2" fill="#eee"/>
|
||
<circle cx="95" cy="42" r="10" fill="#e8e8e8"/>
|
||
<path d="M91 42 L94 45 L99 39" stroke="#bbb" stroke-width="2" fill="none" stroke-linecap="round"/>
|
||
<circle cx="82" cy="18" r="8" fill="#fff" stroke="#ddd" stroke-width="1.5"/>
|
||
<line x1="86" y1="14" x2="92" y2="10" stroke="#ddd" stroke-width="1.5" stroke-linecap="round"/>
|
||
</svg>
|
||
</div>
|
||
<p class="space-private-text">{{if .IsOwnSpace}}你关闭了展示关注列表~{{else}}Ta 关闭了展示关注列表~{{end}}</p>
|
||
{{if .IsOwnSpace}}
|
||
<a href="/settings/profile#privacy" class="space-private-setting-btn">去设置开启</a>
|
||
{{end}}
|
||
</div>
|
||
{{end}}
|
||
{{else}}
|
||
<div class="space-empty-state"><p>暂无数据</p></div>
|
||
{{end}}
|
||
{{end}}
|
||
|
||
{{/* ---- 粉丝列表 ---- */}}
|
||
{{if eq .ActiveTab "followers"}}
|
||
{{if .FollowersResult}}
|
||
{{if .FollowersResult.Accessible}}
|
||
{{if eq (len .FollowersResult.Items) 0}}
|
||
<div class="space-empty-state">
|
||
<p>{{if .IsOwnSpace}}暂无粉丝{{else}}Ta 暂无粉丝{{end}}</p>
|
||
</div>
|
||
{{else}}
|
||
{{if .IsOwnSpace}}
|
||
<div class="space-follow-search">
|
||
<input type="text" class="follow-search-input" placeholder="输入关键词">
|
||
<button class="follow-search-btn">
|
||
<svg viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
|
||
</button>
|
||
</div>
|
||
{{end}}
|
||
<div class="space-user-grid">
|
||
{{range $i, $item := .FollowersResult.Items}}
|
||
<div class="space-user-card" data-uid="{{$item.FollowerID}}">
|
||
<a href="/space/{{$item.FollowerID}}" class="space-user-avatar-wrap">
|
||
{{if $item.FollowerAvatar}}
|
||
<img src="{{$item.FollowerAvatar}}" alt="{{$item.FollowerUsername}}" class="space-user-avatar-img">
|
||
{{else}}
|
||
<div class="space-user-avatar-placeholder"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg></div>
|
||
{{end}}
|
||
</a>
|
||
<div class="space-user-info">
|
||
<a href="/space/{{$item.FollowerID}}" class="space-user-name">{{$item.FollowerUsername}}</a>
|
||
{{if $item.FollowerBio}}
|
||
<p class="space-user-bio">{{printf "%.50s" $item.FollowerBio}}</p>
|
||
{{end}}
|
||
</div>
|
||
{{if not $.IsOwnSpace}}
|
||
<button class="space-follow-btn" data-uid="{{$item.FollowerID}}">关注</button>
|
||
{{end}}
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
{{end}}
|
||
{{else}}
|
||
<div class="space-private-empty">
|
||
<div class="space-private-illustration">
|
||
<svg viewBox="0 0 120 100" width="120" height="100">
|
||
<rect x="15" y="25" width="90" height="60" rx="6" fill="#f0f0f0" stroke="#e0e0e0" stroke-width="2"/>
|
||
<rect x="25" y="35" width="40" height="6" rx="3" fill="#e0e0e0"/>
|
||
<rect x="25" y="47" width="70" height="4" rx="2" fill="#eee"/>
|
||
<rect x="25" y="57" width="55" height="4" rx="2" fill="#eee"/>
|
||
<circle cx="95" cy="42" r="10" fill="#e8e8e8"/>
|
||
<path d="M91 42 L94 45 L99 39" stroke="#bbb" stroke-width="2" fill="none" stroke-linecap="round"/>
|
||
</svg>
|
||
</div>
|
||
<p class="space-private-text">{{if .IsOwnSpace}}你关闭了展示粉丝列表~{{else}}Ta 关闭了展示粉丝列表~{{end}}</p>
|
||
{{if .IsOwnSpace}}
|
||
<a href="/settings/profile#privacy" class="space-private-setting-btn">去设置开启</a>
|
||
{{end}}
|
||
</div>
|
||
{{end}}
|
||
{{else}}
|
||
<div class="space-empty-state"><p>暂无数据</p></div>
|
||
{{end}}
|
||
{{end}}
|
||
</main>
|
||
|
||
{{/* ========== 收藏夹 Tab(仅本人) ==========*/}}
|
||
{{end}}
|
||
{{if eq .ActiveTab "collections"}}
|
||
<!-- 左侧收藏夹列表 -->
|
||
<aside class="space-sidebar collection-sidebar">
|
||
<h2 class="space-sidebar-title collapsible" id="folderSectionToggle">
|
||
我创建的收藏夹
|
||
<svg class="collapse-arrow" viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"/></svg>
|
||
</h2>
|
||
<nav class="space-sidebar-nav" id="folder-nav">
|
||
<a href="#" class="space-sidebar-link new-folder-btn" id="showNewFolderBtn">
|
||
<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
||
新建收藏夹
|
||
</a>
|
||
<div id="new-folder-input" style="display:none;">
|
||
<input type="text" id="new-folder-name" placeholder="收藏夹名称" maxlength="50" class="new-folder-input">
|
||
<button id="createFolderBtn" class="new-folder-submit">确定</button>
|
||
</div>
|
||
{{if .FoldersResult}}
|
||
{{range $f := .FoldersResult.Folders}}
|
||
<a href="/space/{{$.SpaceUser.ID}}?tab=collections&folder={{$f.ID}}"
|
||
class="space-sidebar-link{{if eq $.ActiveFolderID $f.ID}} active{{end}}"
|
||
data-folder-id="{{$f.ID}}">
|
||
<svg class="space-sidebar-icon" viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M17 3H7c-1.1 0-2 .9-2 2v16l7-3 7 3V5c0-1.1-.9-2-2-2zm0 15l-5-2.18L7 18V5h10v13z"/></svg>
|
||
{{$f.Name}}
|
||
<span class="space-sidebar-count">{{$f.ItemCount}}</span>
|
||
</a>
|
||
{{end}}
|
||
{{end}}
|
||
</nav>
|
||
</aside>
|
||
|
||
<!-- 右侧收藏内容 -->
|
||
<main class="space-main">
|
||
{{if .FoldersResult}}
|
||
{{if gt (len .FoldersResult.Folders) 0}}
|
||
{{if .ActiveFolder}}
|
||
<!-- 收藏夹头信息 -->
|
||
<div class="collection-header">
|
||
<div class="collection-info-row">
|
||
<div>
|
||
<h1 class="collection-name">{{.ActiveFolder.Name}}</h1>
|
||
<p class="collection-meta">
|
||
{{if .ActiveFolder.IsPublic}}公开{{else}}私密{{end}}
|
||
· 文章数 {{.FolderItems.Total}}
|
||
</p>
|
||
</div>
|
||
<div class="collection-actions">
|
||
<button class="space-action-btn outline small">批量操作</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 文章卡片网格 -->
|
||
{{if .FolderItems}}
|
||
{{if gt (len .FolderItems.Items) 0}}
|
||
<div class="space-article-grid">
|
||
{{range $item := .FolderItems.Items}}
|
||
<article class="space-article-card">
|
||
<a href="/posts/{{$item.PostID}}" class="article-card-link">
|
||
<h3 class="article-card-title">{{if $item.PostTitle}}{{$item.PostTitle}}{{else}}无标题文章{{end}}</h3>
|
||
</a>
|
||
<div class="article-card-footer">
|
||
<span class="article-card-author">
|
||
<svg viewBox="0 0 24 24" width="12" height="12"><path fill="currentColor" d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>
|
||
{{$.SpaceUser.Username}}
|
||
</span>
|
||
<span class="article-card-time">收藏于{{$item.CreatedAt.Format "01-02 15:04"}}</span>
|
||
</div>
|
||
</article>
|
||
{{end}}
|
||
</div>
|
||
{{else}}
|
||
<div class="space-empty-state">
|
||
<p>这个收藏夹还是空的</p>
|
||
</div>
|
||
{{end}}
|
||
{{else}}
|
||
<div class="space-empty-state"><p>暂无数据</p></div>
|
||
{{end}}
|
||
|
||
{{else}}
|
||
<div class="space-empty-state">
|
||
<p>还没有创建收藏夹,点击左侧「新建收藏夹」开始吧~</p>
|
||
</div>
|
||
{{end}}
|
||
{{else}}
|
||
<div class="space-empty-state"><p>加载失败</p></div>
|
||
{{end}}
|
||
{{end}}
|
||
</main>
|
||
|
||
{{/* ========== 设置 Tab(仅本人) ==========*/}}
|
||
{{end}}
|
||
{{if eq .ActiveTab "settings"}}
|
||
<main class="space-main-full settings-main">
|
||
<h1 class="space-section-title">隐私设置</h1>
|
||
<div class="privacy-settings-grid">
|
||
|
||
<!-- 公开我的收藏 -->
|
||
<div class="privacy-row">
|
||
<label class="privacy-label">公开我的收藏</label>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="privacy-collections"{{if .SpaceUser.PublicCollections}} checked{{end}}>
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- 公开我的生日、个人标签 -->
|
||
<div class="privacy-row">
|
||
<label class="privacy-label">公开我的生日、个人标签</label>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="privacy-bio"{{if .SpaceUser.PublicBio}} checked{{end}}>
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- 公开我的追番追剧 -->
|
||
<div class="privacy-row">
|
||
<label class="privacy-label">公开我的追番追剧</label>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="privacy-tracks"{{if .SpaceUser.PublicTracks}} checked{{end}}>
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- 公开最近投市的视频 -->
|
||
<div class="privacy-row">
|
||
<label class="privacy-label">公开最近投稿的文章</label>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="privacy-posts"{{if .SpaceUser.PublicPosts}} checked{{end}}>
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- 公开最近点赞的视频 -->
|
||
<div class="privacy-row">
|
||
<label class="privacy-label">公开最近点赞的文章</label>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="privacy-likes"{{if .SpaceUser.PublicLikes}} checked{{end}}>
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- 公开我的关注列表 -->
|
||
<div class="privacy-row">
|
||
<label class="privacy-label">公开我的关注列表</label>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="privacy-follow-list"{{if .SpaceUser.FollowListPublic}} checked{{end}}>
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- 公开我的粉丝列表 -->
|
||
<div class="privacy-row">
|
||
<label class="privacy-label">公开我的粉丝列表</label>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="privacy-follower-list"{{if .SpaceUser.FollowerListPublic}} checked{{end}}>
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- 公开学校信息 -->
|
||
<div class="privacy-row">
|
||
<label class="privacy-label">公开学校信息</label>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="privacy-school"{{if .SpaceUser.PublicSchool}} checked{{end}}>
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- 公开最近玩过的游戏 -->
|
||
<div class="privacy-row">
|
||
<label class="privacy-label">公开最近玩过的游戏</label>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="privacy-games"{{if .SpaceUser.PublicGames}} checked{{end}}>
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- 公开拥有的粉丝装扮(仅APP) -->
|
||
<div class="privacy-row">
|
||
<label class="privacy-label">公开拥有的粉丝装扮(仅APP)</label>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="privacy-costumes">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- 公开我的追漫(仅APP) -->
|
||
<div class="privacy-row">
|
||
<label class="privacy-label">公开我的追漫(仅APP)</label>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="privacy-anime"{{if .SpaceUser.PublicAnime}} checked{{end}}>
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- 公开隐藏的粉丝勋章 -->
|
||
<div class="privacy-row">
|
||
<label class="privacy-label">公开隐藏的粉丝勋章</label>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="privacy-medals">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- 投稿视频列表中展现直播播放 -->
|
||
<div class="privacy-row">
|
||
<label class="privacy-label">投稿视频列表中展现直播回放</label>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="privacy-live-replay">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- 投稿视频列表中展现课堂视频 -->
|
||
<div class="privacy-row">
|
||
<label class="privacy-label">投稿视频列表中展现课堂视频</label>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="privacy-classroom">
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- 投稿视频列表中展现月充电专属视频 -->
|
||
<div class="privacy-row">
|
||
<label class="privacy-label">投稿视频列表中展现月充电专属视频</label>
|
||
<label class="toggle-switch">
|
||
<input type="checkbox" id="privacy-monthly-video" checked>
|
||
<span class="toggle-slider"></span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
{{end}}
|
||
</div><!-- /.space-card-layout -->
|
||
|
||
</div><!-- /.space-page -->
|
||
{{end}}
|
||
|
||
{{template "layout/footer.html" .}}
|
||
|
||
<!-- 关注按钮交互 -->
|
||
{{if or (eq .ActiveTab "following") (eq .ActiveTab "followers")}}
|
||
<script nonce="{{.CSPNonce}}">
|
||
(function() {
|
||
var currentUID = {{.CurrentUID}} || 0;
|
||
var cards = document.querySelectorAll('.space-user-card');
|
||
if (!cards.length) return;
|
||
|
||
// 关注按钮状态文字映射
|
||
var labels = {
|
||
none: '关注',
|
||
following: '已关注',
|
||
followBack: '回关',
|
||
mutual: '已互粉'
|
||
};
|
||
|
||
function buttonState(status) {
|
||
if (status.i_follow && status.they_follow) return 'mutual';
|
||
if (status.i_follow) return 'following';
|
||
if (status.they_follow) return 'followBack';
|
||
return 'none';
|
||
}
|
||
|
||
function updateBtn(btn, status) {
|
||
var state = buttonState(status);
|
||
btn.textContent = labels[state];
|
||
btn.className = 'space-follow-btn';
|
||
if (state === 'following' || state === 'mutual') btn.classList.add('followed');
|
||
}
|
||
|
||
// 批量获取并更新每个卡片关注状态(仅非自己)
|
||
cards.forEach(function(card) {
|
||
var btn = card.querySelector('.space-follow-btn');
|
||
if (!btn || btn.disabled) return;
|
||
var targetUid = card.getAttribute('data-uid');
|
||
|
||
fetch('/api/users/' + targetUid + '/follow-status')
|
||
.then(function(res) { return res.json(); })
|
||
.then(function(data) {
|
||
if (data && data.success && data.data) { updateBtn(btn, data.data); }
|
||
});
|
||
|
||
btn.addEventListener('click', function() {
|
||
if (currentUID && parseInt(targetUid) === currentUID) {
|
||
showToast('不能关注自己');
|
||
return;
|
||
}
|
||
btn.classList.add('waiting');
|
||
var xhr = new XMLHttpRequest();
|
||
xhr.open('POST', '/api/users/' + targetUid + '/follow', true);
|
||
xhr.onreadystatechange = function() {
|
||
if (xhr.readyState === 4) {
|
||
btn.classList.remove('waiting');
|
||
if (xhr.status === 200) {
|
||
try { var resp = JSON.parse(xhr.responseText); if (resp.data && resp.data.status) updateBtn(btn, resp.data.status); } catch(e) {}
|
||
}
|
||
}
|
||
};
|
||
xhr.send();
|
||
});
|
||
});
|
||
|
||
// 顶部关注按钮
|
||
var topFollowBtn = document.getElementById('space-follow-btn');
|
||
if (topFollowBtn) {
|
||
var topUid = topFollowBtn.getAttribute('data-uid');
|
||
if (currentUID && parseInt(topUid) === currentUID) {
|
||
topFollowBtn.textContent = '+ 关注';
|
||
topFollowBtn.addEventListener('click', function() {
|
||
showToast('不能关注自己');
|
||
});
|
||
} else {
|
||
fetch('/api/users/' + topUid + '/follow-status')
|
||
.then(function(res) { return res.json(); })
|
||
.then(function(data) {
|
||
if (data && data.success && data.data) {
|
||
var s = data.data;
|
||
topFollowBtn.textContent = (s.i_follow ? (s.they_follow ? '已互粉' : '已关注') : (s.they_follow ? '回关' : '+ 关注'));
|
||
if (s.i_follow) topFollowBtn.classList.add('followed');
|
||
}
|
||
});
|
||
topFollowBtn.addEventListener('click', function() {
|
||
var xhr = new XMLHttpRequest();
|
||
xhr.open('POST', '/api/users/' + topUid + '/follow', true);
|
||
xhr.onreadystatechange = function() {
|
||
if (xhr.readyState === 4 && xhr.status === 200) {
|
||
try {
|
||
var resp = JSON.parse(xhr.responseText);
|
||
if (resp.data && resp.data.status) {
|
||
var st = resp.data.status;
|
||
topFollowBtn.textContent = (st.i_follow ? (st.they_follow ? '已互粉' : '已关注') : (st.they_follow ? '回关' : '+ 关注'));
|
||
topFollowBtn.classList.toggle('followed', st.i_follow);
|
||
// 同时更新下方卡片中的对应按钮
|
||
cards.forEach(function(c) {
|
||
if (c.getAttribute('data-uid') === topUid) {
|
||
var cb = c.querySelector('.space-follow-btn'); if(cb) updateBtn(cb, st);
|
||
}
|
||
});
|
||
}
|
||
} catch(e){}
|
||
}
|
||
};
|
||
xhr.send();
|
||
});
|
||
}
|
||
}
|
||
})();
|
||
</script>
|
||
{{end}}
|
||
|
||
<!-- 收藏夹交互脚本 -->
|
||
{{if eq .ActiveTab "collections"}}
|
||
<script nonce="{{.CSPNonce}}">
|
||
function toggleFolderSection(el) {
|
||
el.parentElement.querySelector('#folder-nav').classList.toggle('collapsed');
|
||
el.classList.toggle('collapsed');
|
||
}
|
||
|
||
function showNewFolderInput() {
|
||
document.getElementById('new-folder-input').style.display = '';
|
||
document.getElementById('new-folder-name').focus();
|
||
}
|
||
|
||
function createFolder() {
|
||
var name = document.getElementById('new-folder-name').value.trim();
|
||
if (!name) return;
|
||
fetch('/api/folders', {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: JSON.stringify({name: name, is_public: false})
|
||
}).then(function(r) { return r.json(); }).then(function(d) {
|
||
if (d.success) { location.reload(); } else { showToast(d.message || '创建失败', 'error'); }
|
||
});
|
||
}
|
||
|
||
// 事件绑定(替代内联 onclick,满足 CSP 无 unsafe-inline)
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
var toggle = document.getElementById('folderSectionToggle');
|
||
if (toggle) toggle.addEventListener('click', function() { toggleFolderSection(this); });
|
||
|
||
var showBtn = document.getElementById('showNewFolderBtn');
|
||
if (showBtn) showBtn.addEventListener('click', function(e) { e.preventDefault(); showNewFolderInput(); });
|
||
|
||
var createBtn = document.getElementById('createFolderBtn');
|
||
if (createBtn) createBtn.addEventListener('click', function() { createFolder(); });
|
||
});
|
||
|
||
// 隐私开关变更保存
|
||
document.querySelectorAll('.privacy-settings-grid .toggle-switch input').forEach(function(cb) {
|
||
cb.addEventListener('change', function() {
|
||
// TODO: 调用 API 保存隐私设置
|
||
});
|
||
});
|
||
</script>
|
||
{{end}}
|
||
|
||
<!-- 设置页交互脚本 -->
|
||
{{if eq .ActiveTab "settings"}}
|
||
<script nonce="{{.CSPNonce}}">
|
||
// 隐私开关变更时自动保存到后端
|
||
document.querySelectorAll('#privacy-follow-list, #privacy-follower-list').forEach(function(cb) {
|
||
cb.addEventListener('change', function() {
|
||
var field = this.id.replace('privacy-', '').replace(/-/g, '_');
|
||
var value = this.checked;
|
||
fetch('/api/space/privacy', {
|
||
method: 'PUT',
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: JSON.stringify({field: field, value: value})
|
||
}).then(function(r){return r.json()}).then(function(d){
|
||
if(!d.success) { this.checked=!value; showToast(d.message||'保存失败', 'error'); }
|
||
}.bind(this));
|
||
});
|
||
});
|
||
</script>
|
||
{{end}}
|
||
</body>
|
||
</html>
|