clearlove2.1
1{{/* ============ 首页:瀑布流 + 无限滚动 ============ */}}
2{{define "pg_index"}}
3<div class="page-head">
4 {{if .topics}}<div class="topic-bar">
5 <div class="topic-chips" id="topic-chips">
6 <a class="chip {{if not .topic}}active{{end}}" href="/">全部</a>
7 {{range .topics}}<a class="chip {{if eq $.topic .name}}active{{end}}" href="/?topic={{.name}}"># {{.name}}</a>{{end}}
8 </div>
9 {{/* 话题较多时由脚本收起为单行,此处按钮用于展开全部 */}}
10 <button type="button" class="chip-toggle" id="topic-toggle" hidden aria-expanded="false" aria-controls="topic-chips">
11 <span>展开</span>
12 <svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
13 </button>
14 </div>{{end}}
15</div>
16<div class="masonry" id="masonry" data-topic="{{.topic}}" aria-live="polite"></div>
17<div class="load-state" id="load-state">加载中...</div>
18<div id="sentinel" class="sentinel"></div>
19{{end}}
20
21{{/* ============ 帖子详情 ============ */}}
22{{define "pg_detail"}}
23{{with .post}}
24<article class="detail-card card">
25 <div class="card-head">
26 <span class="avatar">{{substr0 .Nickname}}</span>
27 <div class="meta">
28 <div class="nick-row">
29 {{if .UserID}}<a class="nickname" href="/u/{{.UserID}}">{{.Nickname}}</a>{{else}}<span class="nickname">{{.Nickname}}</span>{{end}}
30 {{range .Badges}}<span class="post-badge" title="该帖带有管理员或插件标识">{{.}}</span>{{end}}
31 </div>
32 <time>{{datefmt .CreatedAt}}</time>
33 </div>
34 {{if $.canEdit}}
35 <div class="row-actions">
36 <a class="mini-btn" href="/post/{{.ID}}/edit">编辑</a>
37 <form method="post" action="/post/{{.ID}}/delete" class="inline-form" data-confirm="确定删除该帖子吗?">
38 <input type="hidden" name="_csrf" value="{{$.csrf}}">
39 <button class="mini-btn danger" type="submit">删除</button>
40 </form>
41 </div>
42 {{end}}
43 </div>
44 {{if .Topic}}<a class="chip" href="/?topic={{.Topic}}"># {{.Topic}}</a>{{end}}
45 <div class="detail-content">{{nl2br .Content}}</div>
46 <div class="media-grid media-{{len .Images}}">
47 {{range .Images}}<img class="media" loading="lazy" src="{{.}}" alt="帖子图片">{{end}}
48 </div>
49 {{range .Videos}}<video class="video" src="{{.}}" controls preload="metadata"></video>{{end}}
50 <div class="card-actions">
51 <button class="act like-btn" data-id="{{.ID}}">
52 <svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 21s-8-5.2-8-10.4C4 7 6.2 5 8.8 5c1.6 0 2.7.8 3.2 1.7C12.5 5.8 13.6 5 15.2 5 17.8 5 20 7 20 10.6 20 15.8 12 21 12 21z"/></svg>
53 <span class="like-count">{{.Likes}}</span>
54 </button>
55 <span class="act"><svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a8 8 0 1 1-3.2-6.4L21 4l-1 4.4c.6 1.1 1 2.3 1 3.6z"/></svg>{{.CommentCount}}</span>
56 <button class="act report-btn" data-id="{{.ID}}" type="button">举报</button>
57 </div>
58 {{$.detail_actions_after}}
59</article>
60{{$.detail_after}}
61{{end}}
62
63<section class="comments card">
64 <h3>全部评论({{len .comments}})</h3>
65 <div class="comment-list" id="comment-list">
66 {{range .comments}}
67 <div class="comment-item">
68 <span class="avatar small">{{substr0 .nickname}}</span>
69 <div class="comment-body">
70 <span class="nickname">{{.nickname}}</span>
71 <p>{{nl2br .content}}</p>
72 <time>{{datefmt .created_at}}</time>
73 </div>
74 </div>
75 {{else}}<p class="empty">还没有评论,来抢沙发</p>{{end}}
76 </div>
77 <form class="comment-form" id="comment-form" data-post="{{.post.ID}}">
78 <input type="text" name="nickname" maxlength="20" placeholder="昵称(可留空为匿名)">
79 <textarea name="content" maxlength="500" required placeholder="写下你的评论(500字以内)"></textarea>
80 <button class="btn btn-primary" type="submit">发表评论</button>
81 </form>
82</section>
83{{.comments_after}}
84
85{{if .rules}}
86<details class="rules card">
87 <summary>社区守则</summary>
88 <p>{{nl2br .rules}}</p>
89</details>
90{{end}}
91{{end}}
92
93{{/* ============ 发帖页 ============ */}}
94{{define "pg_compose"}}
95<form class="compose card" id="compose-form" method="post" action="/compose" enctype="multipart/form-data"
96 data-admin-nicks="{{.adminNicks}}">
97 <h2>发布新帖</h2>
98 <input type="hidden" name="_csrf" value="{{.csrf}}">
99 <label>昵称
100 <input type="text" name="nickname" maxlength="20" placeholder="留空则显示为匿名" {{if and .user .requireLogin}}value="匿名"{{end}}>
101 </label>
102 <label>内容(3000 字以内)
103 <textarea name="content" maxlength="3000" rows="7" required placeholder="写下你想说的..."></textarea>
104 </label>
105 <label>话题(可选)
106 <select name="topic_id">
107 <option value="0">不选择话题</option>
108 {{range .topics}}<option value="{{.id}}"># {{.name}}</option>{{end}}
109 </select>
110 </label>
111 <label>或新建话题(可选)
112 <input type="text" name="topic_new" maxlength="30" placeholder="输入新话题名称">
113 </label>
114 {{/* 图片 / 视频合并上传区:PC 拖拽,移动端点击选择 */}}
115 <div class="media-field">
116 <span class="field-title">图片 / 视频</span>
117 <div class="drop-zone" id="drop-zone" role="button" tabindex="0"
118 data-max-images="{{.maxImages}}" data-max-videos="{{.maxVideos}}"
119 aria-label="点击选择或拖拽上传图片和视频">
120 <input type="file" id="media-input" accept="image/*,video/*" multiple hidden>
121 <svg viewBox="0 0 24 24" width="30" height="30" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true">
122 <path d="M4 15.5V18a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2.5"/>
123 <path d="M12 3v12"/><path d="M7.5 7.5 12 3l4.5 4.5"/>
124 </svg>
125 <p class="drop-title">点击上传,或将文件拖拽到此处</p>
126 <p class="drop-hint">图片最多 {{.maxImages}} 张(自动转 WebP,单张 ≤10MB);视频最多 {{.maxVideos}} 个(单个 ≤70MB)</p>
127 </div>
128 <div class="media-previews" id="media-previews"></div>
129 <div class="progress" id="upload-progress" hidden><span></span></div>
130 </div>
131
132 <p class="form-err" id="compose-err" hidden></p>
133 <button class="btn btn-primary btn-block" type="submit" id="compose-submit">发布</button>
134 {{if .aiPrecheck}}<p class="muted ai-note"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3l1.8 4.6L18.5 9l-4.7 1.4L12 15l-1.8-4.6L5.5 9l4.7-1.4z"/></svg>发布后 AI 将自动审核内容,违规内容会被拦截</p>{{end}}
135</form>
136
137{{/* 发布前验证码弹窗:点击发布后弹出,验证通过再提交 */}}
138<div class="modal" id="captcha-modal" hidden>
139 <div class="modal-mask"></div>
140 <div class="modal-box captcha-modal-box">
141 <h3>安全验证</h3>
142 <p class="muted">请输入下方 4 位数字验证码后完成发布</p>
143 <div class="captcha-row modal-captcha">
144 <input type="text" id="captcha-code" inputmode="numeric" maxlength="4" autocomplete="off" placeholder="验证码">
145 <div class="captcha-box" id="captcha-box" title="点击刷新"></div>
146 </div>
147 <p class="form-err" id="captcha-err" hidden></p>
148 <div class="btn-row">
149 <button type="button" class="btn btn-ghost btn-block" id="captcha-cancel">取消</button>
150 <button type="button" class="btn btn-primary btn-block" id="captcha-confirm">确认发布</button>
151 </div>
152 </div>
153</div>
154
155{{/* 管理员专享昵称:点击发布后弹出后台账号验证 */}}
156<div class="modal" id="admin-verify-modal" hidden>
157 <div class="modal-mask"></div>
158 <div class="modal-box admin-verify-box">
159 <h3>管理员身份验证</h3>
160 <p class="muted">昵称「<b id="admin-verify-nick"></b>」为管理员专享,请验证后台账号后发布</p>
161 <label>后台账号
162 <input type="text" id="admin-user" maxlength="64" autocomplete="off" placeholder="管理员用户名">
163 </label>
164 <label>后台密码
165 <input type="password" id="admin-pass" maxlength="128" autocomplete="off" placeholder="管理员密码">
166 </label>
167 <p class="form-err" id="admin-verify-err" hidden></p>
168 <div class="btn-row">
169 <button type="button" class="btn btn-ghost btn-block" id="admin-verify-cancel">取消</button>
170 <button type="button" class="btn btn-primary btn-block" id="admin-verify-ok">验证并继续</button>
171 </div>
172 </div>
173</div>
174
175{{if .aiPrecheck}}
176{{/* 发帖 AI 预审加载动画 */}}
177<div class="scan-overlay" id="ai-scan" hidden>
178 <div class="scan-mask"></div>
179 <div class="scan-card compact">
180 <div class="scan-rings">
181 <svg class="ring" viewBox="0 0 120 120" width="108" height="108" aria-hidden="true">
182 <circle class="ring-bg" cx="60" cy="60" r="52"></circle>
183 <circle class="ring-fg spin" cx="60" cy="60" r="52"></circle>
184 </svg>
185 <div class="scan-sweep"></div>
186 <div class="ring-center">
187 <svg viewBox="0 0 24 24" width="26" height="26" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M12 3l1.8 4.6L18.5 9l-4.7 1.4L12 15l-1.8-4.6L5.5 9l4.7-1.4z"/><path d="M18.5 15.5l.8 2 2 .8-2 .8-.8 2-.8-2-2-.8 2-.8z"/></svg>
188 </div>
189 </div>
190 <h3 class="scan-title">AI 智能审核中</h3>
191 <p class="scan-sub" id="ai-scan-sub">正在提取内容特征</p>
192 <div class="scan-bar"><span class="indeterminate"></span></div>
193 <p class="scan-hint">审核通过后将自动发布,请勿关闭页面</p>
194 </div>
195</div>
196{{end}}
197{{.compose_after}}
198{{end}}
199
200{{/* ============ 编辑帖子 ============ */}}
201{{define "pg_edit"}}
202<form class="compose card" method="post" action="/post/{{.post.ID}}/edit">
203 <h2>编辑帖子</h2>
204 <input type="hidden" name="_csrf" value="{{.csrf}}">
205 <label>内容(3000 字以内)
206 <textarea name="content" maxlength="3000" rows="8" required>{{.post.Content}}</textarea>
207 </label>
208 <div class="btn-row">
209 <a class="btn btn-ghost" href="/post/{{.post.ID}}">取消</a>
210 <button class="btn btn-primary" type="submit">保存</button>
211 </div>
212</form>
213{{end}}
214
215{{/* ============ 登录 ============ */}}
216{{define "pg_login"}}
217<form class="auth card" method="post" action="/login">
218 <h2>登录</h2>
219 <input type="hidden" name="_csrf" value="{{.csrf}}">
220 {{with .err}}<p class="form-err">{{.}}</p>{{end}}
221 <input type="hidden" name="next" value="{{.next}}">
222 <label>用户名<input type="text" name="username" required maxlength="20"></label>
223 <label>密码<input type="password" name="password" required minlength="6"></label>
224 <button class="btn btn-primary btn-block" type="submit">登录</button>
225 <p class="auth-alt">还没有账号?<a href="/register">立即注册</a></p>
226</form>
227{{end}}
228
229{{/* ============ 注册 ============ */}}
230{{define "pg_register"}}
231<form class="auth card" method="post" action="/register">
232 <h2>注册</h2>
233 <input type="hidden" name="_csrf" value="{{.csrf}}">
234 {{with .err}}<p class="form-err">{{.}}</p>{{end}}
235 {{if .allow}}
236 <label>用户名<input type="text" name="username" required maxlength="20" placeholder="3-20 位字母数字下划线或中文"></label>
237 <label>邮箱<input type="email" name="email" required></label>
238 {{if .needCode}}
239 <label>邮箱验证码
240 <div class="code-row">
241 <input type="text" name="code" required maxlength="6" inputmode="numeric" placeholder="6 位验证码">
242 <button class="btn btn-ghost" type="button" id="send-code" data-email="email">发送验证码</button>
243 </div>
244 </label>
245 {{end}}
246 <label>密码<input type="password" name="password" required minlength="6"></label>
247 <button class="btn btn-primary btn-block" type="submit">注册</button>
248 {{else}}
249 <p class="empty">本站已关闭注册</p>
250 {{end}}
251 <p class="auth-alt">已有账号?<a href="/login">去登录</a></p>
252</form>
253{{end}}
254
255{{/* ============ 个人主页 ============ */}}
256{{define "pg_profile"}}
257<div class="profile-head card">
258 <span class="avatar big">{{substr0 .owner.Username}}</span>
259 <div class="meta">
260 <h2>{{.owner.Username}}</h2>
261 <p class="muted">共 {{len .posts}} 条帖子</p>
262 </div>
263 {{if .self}}<a class="btn btn-ghost" href="/logout">退出登录</a>{{end}}
264</div>
265<div class="masonry">
266 {{range .posts}}
267 <article class="card post-card">
268 <div class="card-head">
269 <span class="avatar">{{substr0 .Nickname}}</span>
270 <div class="meta">
271 <div class="nick-row">
272 <span class="nickname">{{.Nickname}}</span>
273 {{range .Badges}}<span class="post-badge">{{.}}</span>{{end}}
274 </div>
275 <time>{{datefmt .CreatedAt}}</time>
276 </div>
277 {{if or $.self $.admin}}
278 <div class="row-actions">
279 <a class="mini-btn" href="/post/{{.ID}}/edit">编辑</a>
280 <form method="post" action="/post/{{.ID}}/delete" class="inline-form" data-confirm="确定删除该帖子吗?">
281 <input type="hidden" name="_csrf" value="{{$.csrf}}">
282 <button class="mini-btn danger" type="submit">删除</button>
283 </form>
284 </div>
285 {{end}}
286 </div>
287 <a class="card-link" href="/post/{{.ID}}">
288 <p class="content">{{nl2br (snippet .Content 120)}}</p>
289 </a>
290 <div class="card-foot">
291 <span class="act"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 21s-8-5.2-8-10.4C4 7 6.2 5 8.8 5c1.6 0 2.7.8 3.2 1.7C12.5 5.8 13.6 5 15.2 5 17.8 5 20 7 20 10.6 20 15.8 12 21 12 21z"/></svg>{{.LikeCount}}</span>
292 <span class="act"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a8 8 0 1 1-3.2-6.4L21 4l-1 4.4c.6 1.1 1 2.3 1 3.6z"/></svg>{{.CommentCount}}</span>
293 </div>
294 </article>
295 {{else}}<p class="empty">暂无帖子</p>{{end}}
296</div>
297{{.profile_after}}
298{{end}}