fix templates v1

This commit is contained in:
tamakyi
2026-10-22 17:04:10 +08:00
parent 8fcb23f7b1
commit bd02eb7ed2
12 changed files with 293 additions and 12 deletions
+1
View File
@@ -187,6 +187,7 @@
<details class="uk-card uk-card-default uk-card-body tb-changelog">
<summary>近期更新</summary>
<ul class="tb-list">
<li><strong>2026.10</strong> 优化前端显示,增加主题编辑器功能,预置3套主题,并添加了问题页面“进入下一条未读提问”的功能,优化可用性。</li>
<li><strong>2026.9</strong> 新增审核严格度选项,增加提问、投票、私聊的总开关,可设置定时收集、定量收集或关闭功能,前后端显示也更易用了。</li>
<li><strong>2026.8</strong> 新增<a href="https://box.shiroko.one/vote/sANyLkiBNUGN">投票功能</a>、追加提问、多语言、<a href="https://box.shiroko.one/_/tamakyi/2894">Markdown 显示</a>、置顶显示等,详情见<a href="https://blog.shiroko.one/record/tamabox_changelog_20260813">这里</a>。</li>
<li><strong>2026.8</strong> 放弃使用 recaptcha 等对国内用户不友好的验证方式,更新密码加密方式,修复图片上传等一系列问题。</li>
+47 -3
View File
@@ -14,6 +14,37 @@
height: 40px;
line-height: 38px;
}
/* 页脚次级操作之间的分隔:返回提问箱 / 下一条未读 靠在一起会连成一片 */
.tb-foot-sep {
margin: 0 4px;
color: #b0b6bd;
}
html[data-theme="dark"] .tb-foot-sep {
color: #5c6674;
}
/* 手机端三个按钮在 300px 内放不下一行(实测需 416px):
保留原来的「回答 / 标记为已读」一行不动,让「回答并下一条未读」独占下一行整行宽度。
display:contents 把两个提交按钮提升为该行的直接子项,才能用 order 把它排到状态按钮之后。 */
@media (max-width: 640px) {
.tb-ans-actions {
display: contents;
}
.tb-ans-actions .uk-button {
order: 1;
}
.tb-ans-state {
order: 2;
margin-left: auto;
}
.tb-ans-next {
order: 3;
flex: 1 1 100%;
}
/* 上面 .tb-ans-actions .uk-button 的 order:1 特异性更高,这里必须同级别盖回去 */
.tb-ans-actions .uk-button.tb-ans-next {
order: 3;
}
}
.tb-followup-card {
border: 1px solid #e7e9ec;
border-left: 3px solid #b0b6bd;
@@ -245,7 +276,7 @@
<div class="uk-card-body uk-padding-remove-top">
<hr>
<h5 class="uk-text-center">{{t .LangCode "qitem.answerTitle"}}</h5>
<form id="tb-answer-form" method="post" action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/answer" enctype="multipart/form-data">
<form id="tb-answer-form" method="post" action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/answer{{ if .NextQuery }}?{{ .NextQuery }}{{ end }}" enctype="multipart/form-data">
{{ .CSRFTokenHTML }}
<div class="uk-margin uk-text-center">
<textarea name="answer" class="uk-textarea" rows="5" maxlength="{{ .Limits.AnswerContentLen }}"
@@ -268,8 +299,13 @@
</form>
<!-- 回答(左)+ 阅读状态(右)同一行,压缩空间;tb-ans-row 保证左右按钮等高 -->
<div class="uk-flex uk-flex-middle uk-flex-wrap uk-margin-small-top tb-ans-row" style="gap: 12px; justify-content: space-between;">
<button type="submit" form="tb-answer-form" class="uk-button uk-button-primary">{{t .LangCode "qitem.submitAnswer"}}</button>
<div class="uk-flex uk-flex-middle uk-flex-wrap" style="gap: 8px;">
<div class="uk-flex uk-flex-middle uk-flex-wrap tb-ans-actions" style="gap: 8px;">
<button type="submit" form="tb-answer-form" class="uk-button uk-button-primary">{{t .LangCode "qitem.submitAnswer"}}</button>
{{ if .NextUnreadID }}
<button type="submit" form="tb-answer-form" name="next" value="1" class="uk-button uk-button-default tb-ans-next">{{t .LangCode "qitem.submitAndNext"}}</button>
{{ end }}
</div>
<div class="uk-flex uk-flex-middle uk-flex-wrap tb-ans-state" style="gap: 8px;">
{{ if .Question.IsRead }}
<span class="uk-text-small uk-text-muted">✓ {{t .LangCode "qlist.read"}}</span>
<form class="uk-display-inline" method="post" action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/unread">
@@ -397,6 +433,14 @@
{{ $backTo := printf "/_/%s" .PageUser.Domain }}
{{ if eq .BackFrom "admin" }}{{ $backTo = .BackTo }}{{ end }}
<a class="uk-button uk-button-link" href="{{ $backTo }}">{{t .LangCode "qitem.backToBox" .PageUser.Name}}</a>
{{ if and .CanDelete .NextUnreadID }}
<!-- 下一条未读:标记本条已读并跳转,会改状态,故用 POST 表单而非链接 -->
<span class="tb-foot-sep" aria-hidden="true">·</span>
<form class="uk-display-inline" method="post" action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/next-unread{{ if .NextQuery }}?{{ .NextQuery }}{{ end }}">
{{ .CSRFTokenHTML }}
<button type="submit" class="uk-button uk-button-link" title="{{t .LangCode "qitem.nextUnreadTitle"}}">{{t .LangCode "qitem.nextUnread"}} ›</button>
</form>
{{ end }}
</div>
<!-- Box owner can't create new question here. -->
+39
View File
@@ -178,6 +178,7 @@ func TestGenPreview(t *testing.T) {
// 每个页面作用域(data-page-scope)各一份。新增页面时要同步 dev/theme-builder/index.html
// 的 SCOPES 列表,否则生成器里选不到。
writeQuestionItemPreviews(t, outDir)
writeQuestionOwnerPreviews(t, outDir)
writeAdminListPreviews(t, outDir)
writeVotePreviews(t, outDir)
}
@@ -213,6 +214,44 @@ func writeQuestionItemPreviews(t *testing.T, outDir string) {
}
}
// writeQuestionOwnerPreviews 单题页的箱主视角:回答行有「回答并下一条」、页脚有「下一条未读」,
// 未回答 / 已回答两种状态都要能肉眼核对排版。
func writeQuestionOwnerPreviews(t *testing.T, outDir string) {
t.Helper()
for _, state := range []struct {
name string
answer string
}{
{"unanswered", ""},
{"answered", "沿着河堤一直往西,到海边的旧仓库就掉头。"},
} {
for _, theme := range []string{"light", "dark"} {
data := publicBaseData()
data["VConsole"] = false
data["CanDelete"] = true
data["IsOwnPage"] = true
data["NextUnreadID"] = uint(2)
data["Question"] = map[string]interface{}{
"ID": 1, "Content": "你平时一个人骑车会去哪里?", "Answer": state.answer,
"CreatedAt": time.Now(), "UpdatedAt": time.Now(),
"IsPrivate": false, "IsPinned": false, "IsRead": state.answer != "",
"Token": "abc123", "ReceiveReplyEmail": "",
}
data["QuestionToken"] = "abc123"
data["AskUploadImages"] = []interface{}{}
data["AnswerUploadImages"] = []interface{}{}
data["FollowUps"] = []interface{}{}
data["CanFollowUp"] = false
data["IsQuestioner"] = false
page, err := renderToString(t, "question/item", data)
if err != nil {
t.Fatalf("render question/item owner: %v", err)
}
writePreview(t, outDir, "preview-question-owner-"+state.name+"-"+theme+".html", page, theme, "../static")
}
}
}
// writeAdminListPreviews 箱主后台的问题列表(/user/questions)。
func writeAdminListPreviews(t *testing.T, outDir string) {
t.Helper()