chore: baseline snapshot before moderation-strictness feature

Repo .git was damaged (refs/ missing, index truncated, object store near-empty).
Repaired via git init (rebuilt refs), stale index preserved as .git/index.corrupt-20260910.bak,
then full worktree committed as a rollback baseline.
This commit is contained in:
tamakyi
2026-09-10 10:56:27 +08:00
commit e3e59e7c69
296 changed files with 67458 additions and 0 deletions
+486
View File
@@ -0,0 +1,486 @@
{{template "base/header" .}}
<style>
.qlist-tabs {
display: flex;
justify-content: center;
gap: 4px;
padding: 4px;
margin: 6px auto 20px;
max-width: 320px;
border: 1px solid #e5e5e5;
border-radius: 9px;
background: #fafafa;
}
.qlist-tab {
flex: 1 1 0;
border: 0;
border-radius: 6px;
padding: 8px 15px;
background: transparent;
color: #666;
cursor: pointer;
font-size: 0.9rem;
transition: background 0.2s ease, color 0.2s ease;
}
.qlist-tab:hover { color: #1e87f0; }
.qlist-tab.is-active {
background: #fff;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.10);
color: #1e87f0;
font-weight: 600;
}
.qlist-panel[hidden] { display: none !important; }
.all-votes-list { display: grid; gap: 10px; }
.all-vote-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 14px 16px;
border-bottom: 1px solid #e9e9e9;
text-decoration: none;
}
.all-vote-item:first-child { border-top: 1px solid #e9e9e9; }
.all-vote-item:hover { background: #f8fbff; text-decoration: none; }
.all-vote-item .hot-vote-title { max-width: 100%; }
.hot-vote-title {
display: block;
overflow: hidden;
color: #333;
font-size: 0.98rem;
font-weight: 600;
line-height: 1.45;
text-overflow: ellipsis;
white-space: nowrap;
}
.hot-vote-title:hover { color: #1e87f0; }
.hot-vote-meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 7px;
margin-top: 8px;
}
.hot-vote-meta .uk-label { font-size: 0.72rem; line-height: 1.6; }
.hot-vote-action { flex: 0 0 auto; color: #1e87f0; font-size: 0.82rem; white-space: nowrap; }
/* 提问列表右上角:状态标签与操作按钮统一尺寸(等宽等高、垂直居中、完全对齐)。
每个元素显式带 .qlist-pill 类,选择器挂在 .qlist-pills 容器下提升优先级,
避免浏览器对 <button> 的默认样式或 UIkit .uk-label 造成差异。
移动端默认显示短文字标签(每语言短词,如 未读→新/New,宽度可控);
≥640px 显示完整文案(配合 title 悬浮提示,多语言安全)。 */
.qlist-pills .qlist-pill {
display: inline-flex;
align-items: center;
justify-content: center;
height: 24px;
min-width: 40px;
padding: 0 8px;
box-sizing: border-box;
font-size: 0.72rem;
line-height: 1;
white-space: nowrap;
border-radius: 2px;
border: 0;
vertical-align: middle;
}
.qlist-pills .tb-short { display: inline; }
.qlist-pills .tb-text { display: none; }
/* 按钮额外重置浏览器默认样式,保证与 span 标签渲染完全一致 */
.qlist-pills .qlist-pill.tb-label-btn {
cursor: pointer;
font-family: inherit;
-webkit-appearance: none;
appearance: none;
background-image: none;
outline: none;
margin: 0;
}
.qlist-pills .qlist-pill.tb-label-btn:hover { opacity: 0.85; }
@media (min-width: 640px) {
/* 桌面端纯文字:隐藏移动端短词,只显示完整文案 */
.qlist-pills .qlist-pill { min-width: 62px; padding: 0 10px; }
.qlist-pills .tb-short { display: none; }
.qlist-pills .tb-text { display: inline; }
}
html[data-theme="dark"] .qlist-tabs { border-color: var(--tb-border); background: var(--tb-surface-2); }
html[data-theme="dark"] .qlist-tab { color: var(--tb-text-muted); }
html[data-theme="dark"] .qlist-tab.is-active { background: var(--tb-surface); color: var(--tb-primary); }
html[data-theme="dark"] .all-vote-item { border-color: var(--tb-border); }
html[data-theme="dark"] .all-vote-item:hover { background: rgba(78, 168, 245, 0.10); }
html[data-theme="dark"] .hot-vote-title { color: var(--tb-text); }
/* 紧凑统计看板:替代原 UIkit 卡片网格。手机端 4 列(7 项=2 行)、桌面端 7 列 1 行,
轻量描边小格子、缩字号、去掉大卡片内边距,显著降低纵向高度,减少手机端滚动。
--4 修饰类用于项数较少的看板(如私聊 4 项),在所有断点固定 4 列,避免末尾留白过多。 */
.qlist-stats {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
margin-bottom: 16px;
}
@media (min-width: 768px) {
.qlist-stats { grid-template-columns: repeat(7, 1fr); }
}
.qlist-stats--4 { grid-template-columns: repeat(4, 1fr); }
@media (min-width: 768px) {
.qlist-stats--4 { grid-template-columns: repeat(4, 1fr); }
}
/* --2 修饰类用于项数很少的看板(如投票 2 项),固定 2 列,避免末尾留白 */
.qlist-stats--2 { grid-template-columns: repeat(2, 1fr); }
@media (min-width: 768px) {
.qlist-stats--2 { grid-template-columns: repeat(2, 1fr); }
}
.qlist-stat {
background: #fff;
border: 1px solid #e5e5e5;
border-radius: 9px;
padding: 8px 6px;
text-align: center;
}
.qlist-stat .qlist-stat-num {
display: block;
font-size: 1.15rem;
font-weight: 700;
line-height: 1.15;
color: #222;
}
.qlist-stat .qlist-stat-lbl {
display: block;
margin-top: 2px;
font-size: 0.7rem;
color: #888;
line-height: 1.15;
}
html[data-theme="dark"] .qlist-stat { background: var(--tb-surface); border-color: var(--tb-border); }
html[data-theme="dark"] .qlist-stat .qlist-stat-num { color: var(--tb-text); }
html[data-theme="dark"] .qlist-stat .qlist-stat-lbl { color: var(--tb-text-muted); }
</style>
<div class="uk-container">
<div class="uk-flex uk-flex-middle uk-flex-between uk-margin-small-bottom">
<h2 class="uk-margin-remove">{{t .LangCode "qlist.title"}}</h2>
<a class="uk-button uk-button-default uk-button-small" href="/user/profile">{{t .LangCode "admin.featuresEntry"}}</a>
</div>
<!-- 居中切换:提问 / 投票 / 私聊 -->
<div class="qlist-tabs" role="tablist" aria-label="{{t .LangCode "qlist.title"}}">
<button type="button" class="qlist-tab is-active" data-qlist-tab="questions" role="tab" aria-selected="true">{{t .LangCode "qlist.questionsTab"}}</button>
<button type="button" class="qlist-tab" data-qlist-tab="votes" role="tab" aria-selected="false">{{t .LangCode "qlist.votesTab"}}</button>
<button type="button" class="qlist-tab" data-qlist-tab="chats" role="tab" aria-selected="false">{{t .LangCode "qlist.chatsTab"}}</button>
</div>
<!-- 提问面板(默认展示) -->
<div class="qlist-panel" data-qlist-panel="questions">
<!-- 统一提示区:标记已读/未读、置顶等操作的成功/错误提示沿用 base/alert 样式 -->
{{template "base/alert" .}}
<!-- 提问箱统计看板(紧凑多列网格:手机4列/桌面7列,降低纵向高度) -->
<div class="qlist-stats">
<div class="qlist-stat"><span class="qlist-stat-num">{{ .Stats.total }}</span><span class="qlist-stat-lbl">{{t .LangCode "qlist.statsTotal"}}</span></div>
<div class="qlist-stat"><span class="qlist-stat-num">{{ .Stats.answered }}</span><span class="qlist-stat-lbl">{{t .LangCode "qlist.statsAnswered"}}</span></div>
<div class="qlist-stat"><span class="qlist-stat-num">{{ .Stats.unanswered }}</span><span class="qlist-stat-lbl">{{t .LangCode "qlist.statsUnanswered"}}</span></div>
<div class="qlist-stat"><span class="qlist-stat-num">{{ .Stats.read }}</span><span class="qlist-stat-lbl">{{t .LangCode "qlist.statsRead"}}</span></div>
<div class="qlist-stat"><span class="qlist-stat-num">{{ .Stats.unread }}</span><span class="qlist-stat-lbl">{{t .LangCode "qlist.statsUnread"}}</span></div>
<div class="qlist-stat"><span class="qlist-stat-num">{{ .Stats.private }}</span><span class="qlist-stat-lbl">{{t .LangCode "qlist.statsPrivate"}}</span></div>
<div class="qlist-stat"><span class="qlist-stat-num">{{ .Stats.pinned }}</span><span class="qlist-stat-lbl">{{t .LangCode "qlist.statsPinned"}}</span></div>
</div>
<div class="uk-margin-medium-bottom uk-text-center">
<!-- 全文搜索自己提问箱 -->
<form method="get" action="" class="uk-form-stacked">
<div class="uk-flex uk-flex-center uk-flex-nowrap uk-overflow-auto">
<div class="uk-margin-small-right uk-width-expand" style="max-width: 420px;">
<input type="text" name="q" class="uk-input uk-form-small" value="{{ .Keyword }}" placeholder="{{t .LangCode "qlist.searchPlaceholder"}}" aria-label="{{t .LangCode "qlist.searchPlaceholder"}}">
</div>
<div class="uk-margin-small-right">
<button type="submit" class="uk-button uk-button-primary uk-button-small">{{t .LangCode "qlist.searchBtn"}}</button>
</div>
{{ if ne .Keyword "" }}
<div>
<a class="uk-button uk-button-default uk-button-small" href="/user/questions">{{t .LangCode "qlist.searchClear"}}</a>
</div>
{{ end }}
</div>
</form>
<form method="get" action="" class="uk-form-stacked uk-margin-small-top">
<div class="uk-flex uk-flex-center uk-flex-nowrap uk-overflow-auto">
<div class="uk-margin-small-right" style="width: 120px;">
<label class="uk-form-label uk-text-small">{{t .LangCode "qlist.sort"}}</label>
<select name="sort" class="uk-select uk-form-small" onchange="this.form.submit()" style="width: 100%;">
<option value="latest" {{if eq .SelectedSort "latest"}}selected{{end}}>{{t .LangCode "qlist.latest"}}</option>
<option value="oldest" {{if eq .SelectedSort "oldest"}}selected{{end}}>{{t .LangCode "qlist.oldest"}}</option>
</select>
</div>
<div class="uk-margin-small-right" style="width: 120px;">
<label class="uk-form-label uk-text-small">{{t .LangCode "qlist.status"}}</label>
<select name="status" class="uk-select uk-form-small" onchange="this.form.submit()" style="width: 100%;">
<option value="all" {{if eq .SelectedStatus "all"}}selected{{end}}>{{t .LangCode "qlist.all"}}</option>
<option value="answered" {{if eq .SelectedStatus "answered"}}selected{{end}}>{{t .LangCode "qlist.answered"}}</option>
<option value="unanswered" {{if eq .SelectedStatus "unanswered"}}selected{{end}}>{{t $.LangCode "qlist.unanswered"}}</option>
</select>
</div>
<div class="uk-margin-small-right" style="width: 120px;">
<label class="uk-form-label uk-text-small">{{t .LangCode "qlist.type"}}</label>
<select name="type" class="uk-select uk-form-small" onchange="this.form.submit()" style="width: 100%;">
<option value="all" {{if eq .SelectedType "all"}}selected{{end}}>{{t .LangCode "qlist.all"}}</option>
<option value="public" {{if eq .SelectedType "public"}}selected{{end}}>{{t .LangCode "qlist.public"}}</option>
<option value="private_question" {{if eq .SelectedType "private_question"}}selected{{end}}>{{t .LangCode "qlist.privateQuestion"}}</option>
</select>
</div>
<div style="width: 120px;">
<label class="uk-form-label uk-text-small">{{t .LangCode "qlist.readStatus"}}</label>
<select name="read" class="uk-select uk-form-small" onchange="this.form.submit()" style="width: 100%;">
<option value="all" {{if eq .SelectedRead "all"}}selected{{end}}>{{t .LangCode "qlist.all"}}</option>
<option value="read" {{if eq .SelectedRead "read"}}selected{{end}}>{{t .LangCode "qlist.read"}}</option>
<option value="unread" {{if eq .SelectedRead "unread"}}selected{{end}}>{{t .LangCode "qlist.unread"}}</option>
</select>
</div>
</div>
</form>
</div>
<!-- 我收到的提问(标签右浮同行时间,内容下一行) -->
<h4>{{t .LangCode "qlist.received"}}</h4>
{{ if .ReceivedItems }}
{{ range .ReceivedItems }}
<div class="uk-margin-medium-bottom" id="question-{{.QuestionID}}">
<hr>
<!-- 标签在左、按钮在右:两组分开布局;移动端状态/按钮只显示 emoji 图标,保持单行紧凑 -->
<div class="uk-flex uk-flex-middle uk-flex-wrap qlist-pills" style="gap: 6px;">
<!-- 左:时间 + 状态标签 -->
<div class="uk-flex uk-flex-middle uk-flex-wrap" style="gap: 6px;">
<span class="uk-text-small uk-text-muted">{{Date .CreatedAt "Y-m-d H:i:s"}}</span>
{{ if .IsPinned }}
<span class="uk-label uk-label-warning qlist-pill" title="{{t $.LangCode "qlist.pinned"}}"><span class="tb-short">{{t $.LangCode "qlist.pinnedShort"}}</span><span class="tb-text">{{t $.LangCode "qlist.pinned"}}</span></span>
{{ end }}
{{ if eq .Type "question_private" }}
<span class="uk-label uk-label-warning qlist-pill" title="{{t $.LangCode "qlist.private"}}"><span class="tb-short">{{t $.LangCode "qlist.privateShort"}}</span><span class="tb-text">{{t $.LangCode "qlist.private"}}</span></span>
{{ end }}
{{ if not .IsAnswered }}
<span class="uk-label qlist-pill" title="{{t $.LangCode "qlist.unanswered"}}"><span class="tb-short">{{t $.LangCode "qlist.unansweredShort"}}</span><span class="tb-text">{{t $.LangCode "qlist.unanswered"}}</span></span>
{{ end }}
{{ if .IsRead }}
<span class="uk-label uk-label-success qlist-pill" title="{{t $.LangCode "qlist.read"}}"><span class="tb-short">{{t $.LangCode "qlist.readShort"}}</span><span class="tb-text">{{t $.LangCode "qlist.read"}}</span></span>
{{ else }}
<span class="uk-label qlist-pill" title="{{t $.LangCode "qlist.unread"}}"><span class="tb-short">{{t $.LangCode "qlist.unreadShort"}}</span><span class="tb-text">{{t $.LangCode "qlist.unread"}}</span></span>
{{ end }}
</div>
<!-- 右:操作按钮(margin-left:auto 推到最右,换行后仍保持右对齐) -->
<div class="uk-flex uk-flex-middle uk-flex-wrap" style="gap: 6px; margin-left: auto;">
{{ if not .IsRead }}
<!-- 快捷标记已读:沿用原 tb-label-btn 样式,display:contents 不破坏 flex 布局 -->
<form method="post" action="/_/{{$.LoggedUser.Domain}}/{{.QuestionID}}/read" style="margin: 0; display: contents;">
{{ $.CSRFTokenHTML }}
<button type="submit" class="uk-label tb-label-btn uk-label-success qlist-pill" title="{{t $.LangCode "qitem.markRead"}}"><span class="tb-short">{{t $.LangCode "qitem.markReadShort"}}</span><span class="tb-text">{{t $.LangCode "qitem.markRead"}}</span></button>
</form>
{{ end }}
<!-- form 用 display:contents 不产生盒子,置顶按钮直接参与 flex 对齐 -->
<form method="post" action="{{.PinLink}}" style="margin: 0; display: contents;">
{{ $.CSRFTokenHTML }}
<button type="submit" class="uk-label tb-label-btn {{ if .IsPinned }}uk-label-danger{{ else }}uk-label-primary{{ end }} qlist-pill" title="{{ if .IsPinned }}{{t $.LangCode "qlist.unpin"}}{{ else }}{{t $.LangCode "qlist.pin"}}{{ end }}"><span class="tb-short">{{ if .IsPinned }}{{t $.LangCode "qlist.unpinShort"}}{{ else }}{{t $.LangCode "qlist.pinShort"}}{{ end }}</span><span class="tb-text">{{ if .IsPinned }}{{t $.LangCode "qlist.unpin"}}{{ else }}{{t $.LangCode "qlist.pin"}}{{ end }}</span></button>
</form>
</div>
</div>
<a href="{{.Link}}">
<div class="uk-text-small uk-text-break tb-preline tb-clamp-5 uk-margin-small-top">{{MarkdownPlainText .Content}}</div>
</a>
</div>
{{ end }}
{{ if .HasMoreQuestions }}
<div class="uk-text-center uk-margin-medium-top">
<a class="uk-button uk-button-default uk-button-small" href="{{ .QuestionLoadMoreURL }}">{{t .LangCode "qlist.loadMore"}}</a>
</div>
{{ end }}
{{ else }}
<p class="uk-text-muted">{{t .LangCode "qlist.empty"}}</p>
{{ end }}
</div>
<!-- 投票面板 -->
<div class="qlist-panel" data-qlist-panel="votes" hidden>
<!-- 统一提示区:发布/结束/删除等操作的成功/错误提示沿用 base/alert 样式 -->
{{template "base/alert" .}}
<!-- 投票统计看板(与提问/私聊看板一致) -->
<div class="qlist-stats qlist-stats--2">
<div class="qlist-stat"><span class="qlist-stat-num">{{ .VoteStats.total }}</span><span class="qlist-stat-lbl">{{t .LangCode "vote.statsTotal"}}</span></div>
<div class="qlist-stat"><span class="qlist-stat-num">{{ .VoteStats.published }}</span><span class="qlist-stat-lbl">{{t .LangCode "vote.statsPublished"}}</span></div>
</div>
<div class="uk-card uk-card-default uk-card-small uk-card-body uk-margin-bottom uk-flex uk-flex-between uk-flex-middle">
<div>
<div class="uk-text-bold">{{t .LangCode "qlist.myVotes"}}</div>
<div class="uk-text-meta uk-text-small">{{t .LangCode "vote.manageDesc"}}</div>
</div>
<a class="uk-button uk-button-primary uk-button-small" href="/user/votes/new">{{t .LangCode "qlist.newVote"}}</a>
</div>
{{ if .MyVotes }}
{{ range .MyVotes }}
<div class="uk-margin-medium-bottom">
<hr>
<!-- 第一行:标题 + 类型 + 状态 + 元信息 -->
<div class="uk-flex uk-flex-middle uk-flex-wrap" style="gap: 8px;">
<span class="uk-text-bold">{{ if .Title }}{{ .Title }}{{ else }}{{t $.LangCode "qlist.untitledVote"}}{{ end }}</span>
{{ if eq .Type "single" }}<span class="uk-label uk-label-primary">{{t $.LangCode "vote.typeSingle"}}</span>{{ end }}
{{ if eq .Type "multiple" }}<span class="uk-label uk-label-primary">{{t $.LangCode "vote.typeMultiple"}}</span>{{ end }}
{{ if eq .Type "rating" }}<span class="uk-label uk-label-primary">{{t $.LangCode "vote.typeRating"}}</span>{{ end }}
{{ if eq .Type "rank" }}<span class="uk-label uk-label-primary">{{t $.LangCode "vote.typeRank"}}</span>{{ end }}
{{ if eq .Type "form" }}<span class="uk-label uk-label-primary">{{t $.LangCode "vote.typeForm"}}</span>{{ end }}
{{ if eq .EffectiveStatus "draft" }}<span class="uk-label">{{t $.LangCode "vote.statusDraft"}}</span>{{ end }}
{{ if eq .EffectiveStatus "published" }}<span class="uk-label uk-label-success">{{t $.LangCode "vote.statusPublished"}}</span>{{ end }}
{{ if eq .EffectiveStatus "not_started" }}<span class="uk-label uk-label-primary">{{t $.LangCode "vote.statusNotStarted"}}</span>{{ end }}
{{ if eq .EffectiveStatus "ended" }}<span class="uk-label uk-label-warning">{{t $.LangCode "vote.statusEnded"}}</span>{{ end }}
{{ if eq .EffectiveStatus "archived" }}<span class="uk-label uk-label-secondary">{{t $.LangCode "vote.statusArchived"}}</span>{{ end }}
<span class="uk-text-meta">{{t $.LangCode "vote.participants"}}: {{ .SubmissionCount }}</span>
{{ if .RemainingText }}<span class="uk-text-meta">{{ .RemainingText }}</span>{{ end }}
<span class="uk-text-meta">{{t $.LangCode "vote.createdAt"}}: {{ .CreatedAt.Format "2006-01-02 15:04" }}</span>
</div>
<!-- 第二行:操作按钮(统一右对齐,所有状态保持一致的两行布局) -->
<div class="uk-flex uk-flex-wrap uk-margin-small-top" style="gap: 6px; justify-content: flex-end;">
{{ if eq .EffectiveStatus "draft" }}
<form method="post" action="/user/votes/{{ .ID }}/publish" class="uk-display-inline">
{{ $.CSRFTokenHTML }}
<button class="uk-button uk-button-primary uk-button-small" onclick="return confirm('{{t $.LangCode "vote.confirmPublish"}}')">{{t $.LangCode "vote.publish"}}</button>
</form>
<a class="uk-button uk-button-default uk-button-small" href="/user/votes/{{ .ID }}/preview">{{t $.LangCode "vote.preview"}}</a>
{{ end }}
{{ if or (eq .EffectiveStatus "published") (eq .EffectiveStatus "not_started") }}
<form method="post" action="/user/votes/{{ .ID }}/close" class="uk-display-inline">
{{ $.CSRFTokenHTML }}
<button class="uk-button uk-button-warning uk-button-small" onclick="return confirm('{{t $.LangCode "vote.confirmClose"}}')">{{t $.LangCode "vote.close"}}</button>
</form>
{{ end }}
{{ if ne .EffectiveStatus "archived" }}
<a class="uk-button uk-button-default uk-button-small" href="/user/votes/{{ .ID }}/edit">{{t $.LangCode "vote.edit"}}</a>
{{ end }}
{{ if ne .EffectiveStatus "draft" }}
<a class="uk-button uk-button-default uk-button-small" href="/user/votes/{{ .ID }}/results">{{t $.LangCode "vote.results"}}</a>
<a class="uk-button uk-button-default uk-button-small" href="/user/votes/{{ .ID }}/export">{{t $.LangCode "vote.export"}}</a>
{{ end }}
{{ if or (eq .EffectiveStatus "published") (eq .EffectiveStatus "not_started") }}
<button class="uk-button uk-button-default uk-button-small" onclick="copyShareURL('{{ .LinkToken }}', this)">{{t $.LangCode "vote.share"}}</button>
{{ end }}
<form method="post" action="/user/votes/{{ .ID }}/delete" class="uk-display-inline">
{{ $.CSRFTokenHTML }}
<button class="uk-button uk-button-danger uk-button-small" onclick="return confirm('{{t $.LangCode "vote.confirmDelete"}}')">{{t $.LangCode "common.delete"}}</button>
</form>
</div>
</div>
{{ end }}
{{ if .HasMoreVotes }}
<div class="uk-text-center uk-margin-medium-top">
<a class="uk-button uk-button-default uk-button-small" href="{{ .VoteLoadMoreURL }}">{{t .LangCode "qlist.loadMore"}}</a>
</div>
{{ end }}
{{ else }}
<div class="uk-alert uk-alert-primary uk-margin-remove">{{t .LangCode "vote.empty"}}</div>
{{ end }}
</div>
<!-- 私聊面板 -->
<div class="qlist-panel" data-qlist-panel="chats" hidden>
<!-- 统一提示区:私聊相关操作的成功/错误提示沿用 base/alert 样式 -->
{{template "base/alert" .}}
<!-- 私聊统计看板(与提问/投票看板一致) -->
<div class="qlist-stats qlist-stats--4">
<div class="qlist-stat"><span class="qlist-stat-num">{{ .ChatStats.total }}</span><span class="qlist-stat-lbl">{{t .LangCode "qlist.chatStatsTotal"}}</span></div>
<div class="qlist-stat"><span class="qlist-stat-num">{{ .ChatStats.received }}</span><span class="qlist-stat-lbl">{{t .LangCode "qlist.chatStatsReceived"}}</span></div>
<div class="qlist-stat"><span class="qlist-stat-num">{{ .ChatStats.initiated }}</span><span class="qlist-stat-lbl">{{t .LangCode "qlist.chatStatsInitiated"}}</span></div>
<div class="qlist-stat"><span class="qlist-stat-num">{{ .ChatStats.closed }}</span><span class="qlist-stat-lbl">{{t .LangCode "qlist.chatStatsClosed"}}</span></div>
</div>
<!-- 我收到的私聊 -->
<h4>{{t .LangCode "qlist.receivedChats"}}({{ .ChatStats.received }})</h4>
{{ if .ReceivedChats }}
{{ range .ReceivedChats }}
<div class="uk-margin-medium-bottom">
<hr>
<div class="uk-float-right uk-flex uk-flex-middle uk-flex-wrap" style="gap: 6px;">
<span class="uk-label uk-label-success">{{t $.LangCode "chat.title"}}</span>
{{ if .IsBlocked }}
<span class="uk-label uk-label-danger">{{t $.LangCode "qlist.blocked"}}</span>
{{ end }}
</div>
<a href="{{.Link}}">
<div class="uk-text-left uk-text-small uk-text-muted">{{Date .CreatedAt "Y-m-d H:i:s"}}</div>
<div class="uk-text-small uk-text-break tb-preline tb-clamp-5 uk-margin-small-top">{{.Content}}</div>
</a>
</div>
{{ end }}
{{ else }}
<p class="uk-text-muted">{{t .LangCode "qlist.chatEmpty"}}</p>
{{ end }}
{{ if .HasMoreChats }}
<div class="uk-text-center uk-margin-medium-top">
<a class="uk-button uk-button-default uk-button-small" href="{{ .ChatLoadMoreURL }}">{{t .LangCode "qlist.loadMore"}}</a>
</div>
{{ end }}
<!-- 我发起的私聊 -->
{{ if .ConversationDetails }}
<h4 class="uk-margin-top">{{t .LangCode "qlist.myChats"}}({{ .ChatStats.initiated }})</h4>
{{ range .ConversationDetails }}
<a href="/_/{{.OwnerDomain}}/chat/{{.Conversation.Token}}" class="uk-display-block uk-overflow-hidden uk-margin-medium-bottom">
<div>
<hr>
<span class="uk-label uk-label-warning uk-float-right uk-margin-small-right">{{t $.LangCode "chat.title"}}</span>
{{ if .IsBlocked }}
<span class="uk-label uk-label-danger uk-float-right uk-margin-small-right">{{t $.LangCode "qlist.blocked"}}</span>
{{ end }}
<div class="uk-text-left uk-text-small uk-text-muted">{{Date .Conversation.CreatedAt "Y-m-d H:i:s"}}</div>
<div class="uk-text-small uk-text-break tb-preline tb-clamp-5 uk-margin-small-top">{{.FirstMessage}}</div>
</div>
</a>
{{ end }}
{{ end }}
{{ if .HasMoreAskedChats }}
<div class="uk-text-center uk-margin-medium-top">
<a class="uk-button uk-button-default uk-button-small" href="{{ .AskedChatsLoadMoreURL }}">{{t .LangCode "qlist.loadMore"}}</a>
</div>
{{ end }}
</div>
</div>
<script>
function copyShareURL(token, btn) {
var url = window.location.origin + "/vote/" + token;
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(url).then(function () {
var old = btn.textContent;
btn.textContent = '{{t .LangCode "vote.copied"}}';
setTimeout(function () { btn.textContent = old; }, 1500);
});
} else {
prompt('{{t .LangCode "vote.shareURL"}}', url);
}
}
document.addEventListener('DOMContentLoaded', function() {
const tabs = document.querySelectorAll('[data-qlist-tab]');
const panels = document.querySelectorAll('[data-qlist-panel]');
function showQlistPanel(name) {
tabs.forEach(function(tab) {
const active = tab.dataset.qlistTab === name;
tab.classList.toggle('is-active', active);
tab.setAttribute('aria-selected', active ? 'true' : 'false');
});
panels.forEach(function(panel) {
panel.hidden = panel.dataset.qlistPanel !== name;
});
if (window.history && window.history.replaceState) {
const hash = name === 'votes' ? '#votes' : (name === 'chats' ? '#chats' : '');
window.history.replaceState({}, document.title, window.location.pathname + hash);
}
}
tabs.forEach(function(tab) {
tab.addEventListener('click', function() { showQlistPanel(tab.dataset.qlistTab); });
});
if (window.location.hash === '#votes') showQlistPanel('votes');
if (window.location.hash === '#chats') showQlistPanel('chats');
});
</script>
{{template "base/footer" .}}