Files
tamabox/templates/question/item.html
T
2026-10-22 18:26:30 +08:00

489 lines
24 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{template "base/header" .}}
<link rel="shortcut icon"
href="{{.PageUser.Avatar}}?x-oss-process=image/auto-orient,1/quality,q_70/sharpen,200/resize,limit_0,m_fill,w_200,h_200"/>
<link rel="apple-touch-icon"
href="{{.PageUser.Avatar}}?x-oss-process=image/auto-orient,1/quality,q_70/sharpen,200/resize,limit_0,m_fill,w_200,h_200"/>
<link rel="icon" sizes="192x192"
href="{{.PageUser.Avatar}}?x-oss-process=image/auto-orient,1/quality,q_70/sharpen,200/resize,limit_0,m_fill,w_200,h_200">
<style>
a.uk-button.uk-button-link {
text-transform: none;
}
/* 回答行:左侧「回答」与右侧「标记为已读/标记为未读」按钮等高 */
.tb-ans-row .uk-button {
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;
border-radius: var(--tb-radius-sm);
padding: 12px 14px;
background: #fbfbfc;
}
html[data-theme="dark"] .tb-followup-card {
background: #1b1e23;
border-color: #2c2f36;
border-left-color: #3a4250;
}
html[data-theme="dark"] .tb-followup-card .uk-button-default {
background-color: #2c2f36;
color: #e5e9ec;
border-color: #3a4250;
}
html[data-theme="dark"] .tb-followup-card .uk-button-default:hover {
background-color: #353941;
color: #ffffff;
}
</style>
<div>
<div class="uk-card uk-card-default">
<div class="uk-card-header">
<div class="uk-text-left uk-text-small uk-text-muted">{{Date .Question.CreatedAt "Y-m-d H:i:s"}}</div>
<div class="md-body uk-text-break uk-margin-top uk-margin-bottom">{{MarkdownFormat .Question.Content}}</div>
{{ if .AskUploadImages }}
<ul class="uk-thumbnav" uk-margin>
{{ range $index, $elem := .AskUploadImages }}
<div uk-lightbox>
<a href="https://{{ ImageBucketCDNHost }}/{{ $elem.Key }}">
<img class="uk-border-rounded uk-object-fill" src="https://{{ ImageBucketCDNHost }}/{{ $elem.Key }}" width="100" height="100" alt="">
</a>
</div>
{{ end }}
</ul>
{{ end }}
</div>
{{if ne .Question.Answer ""}}
<div class="uk-card-body">
<!--<div class="uk-text-left uk-text-small uk-text-muted">{{Date .Question.UpdatedAt "Y-m-d H:i:s"}}</div>-->
<div id="answer-display">
<div class="md-body uk-text-break">{{MarkdownFormat .Question.Answer}}</div>
{{ if .AnswerUploadImages }}
<ul class="uk-thumbnav" uk-margin>
{{ range $index, $elem := .AnswerUploadImages }}
<div uk-lightbox>
<a href="https://{{ ImageBucketCDNHost }}/{{ $elem.Key }}">
<img class="uk-border-rounded uk-object-fill" src="https://{{ ImageBucketCDNHost }}/{{ $elem.Key }}" width="100" height="100" alt="">
</a>
</div>
{{ end }}
</ul>
{{ end }}
</div>
<p class="uk-text-small uk-text-right uk-text-muted">{{t .LangCode "qitem.answeredBy" .PageUser.Name (Date .Question.UpdatedAt "Y-m-d H:i:s")}}</p>
{{ if not .CanDelete }}
<!-- 举报入口:仅对非箱主显示;默认折叠,避免在每条回答下方都堆一个显眼按钮。
理由为固定枚举(不接受自由文本),重复举报服务端幂等处理。 -->
<div class="uk-margin-small-top">
<details>
<summary class="uk-text-small uk-text-muted" style="cursor: pointer;">⚑ {{t .LangCode "report.entry"}}</summary>
<form method="post" action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/report" class="uk-margin-small-top uk-flex uk-flex-middle uk-flex-wrap" style="gap: 8px;">
{{ .CSRFTokenHTML }}
<select name="reason" class="uk-select uk-form-small" style="max-width: 200px;">
<option value="harassment">{{t .LangCode "report.reason.harassment"}}</option>
<option value="doxxing">{{t .LangCode "report.reason.doxxing"}}</option>
<option value="divisive">{{t .LangCode "report.reason.divisive"}}</option>
<option value="other">{{t .LangCode "report.reason.other"}}</option>
</select>
{{ if eq .CaptchaType "math" }}
<!-- 算数验证码:点击后由 header 的全局弹窗接管,答案回填到本表单的隐藏域 -->
<input type="hidden" name="g-recaptcha-response" value="">
<button type="button" class="uk-button uk-button-default uk-button-small math-captcha-trigger">{{t .LangCode "report.submit"}}</button>
{{ else }}
<button type="submit" class="uk-button uk-button-default uk-button-small g-recaptcha" data-sitekey="{{.RecaptchaSiteKey}}" data-callback="tbReportCaptchaOk">{{t .LangCode "report.submit"}}</button>
<script>
function tbReportCaptchaOk() {
var f = document.querySelector('form[action$="/report"]');
if (f) { f.submit(); }
}
</script>
{{ end }}
</form>
</details>
</div>
{{ end }}
<!-- 分享此提问(左)与 更新回答(右)一左一右分开;箱主未登录(无更新回答按钮)时分享按钮居中;编辑区在下方由 tbToggleAnswerEditor 控制 -->
<div class="uk-flex uk-flex-middle uk-flex-wrap uk-margin-small-top {{ if not .CanDelete }}uk-flex-center{{ end }}" style="gap: 8px;">
{{ if and (not .Question.IsPrivate) (ne .Question.Answer "") }}
<a class="uk-button uk-button-default uk-button-small" href="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/share-card" target="_blank">📤 {{t .LangCode "qitem.shareCard"}}</a>
{{ end }}
{{ if .CanDelete }}
<div id="answer-edit-trigger" style="margin-left: auto;">
<button type="button" class="uk-button uk-button-default uk-button-small" onclick="tbToggleAnswerEditor()">{{t .LangCode "qitem.updateAnswer"}}</button>
</div>
{{ end }}
</div>
{{ if not .CanDelete }}
<div id="waline"></div>
<script>
document.addEventListener('DOMContentLoaded', function () {
const waline = Waline.init({
el: '#waline',
//后端服务器地址
serverURL: 'https://waline.337845818.best/',
path: window.location.href,
// 中文默认
locale: {
nick: '昵称',
nickError: '昵称不能少于3个字符',
mail: '邮箱',
mailError: '请填写正确的邮件地址',
link: '网址',
optional: '可选',
placeholder: '欢迎评论(需要手工审核,不会立即展现)',
sofa: '来发评论吧~',
submit: '提交',
like: '喜欢',
cancelLike: '取消喜欢',
reply: '回复',
cancelReply: '取消回复',
comment: '评论',
refresh: '刷新',
more: '加载更多...',
preview: '预览',
emoji: '表情',
uploadImage: '上传图片',
seconds: '秒前',
minutes: '分钟前',
hours: '小时前',
days: '天前',
now: '刚刚',
uploading: '正在上传',
login: '登录',
logout: '退出',
admin: '博主',
sticky: '置顶',
word: '字',
wordHint: '评论字数应在 $0 到 $1 字之间!\n当前字数:$2',
anonymous: '无名小狼',
level0: '潜水',
level1: '冒泡',
level2: '吐槽',
level3: '活跃',
level4: '话痨',
level5: '传说',
gif: '表情包',
gifSearchPlaceholder: '搜索表情包',
profile: '个人资料',
approved: '通过',
waiting: '待审核',
spam: '垃圾',
unsticky: '取消置顶',
oldest: '按倒序',
latest: '按正序',
hottest: '按热度',
reactionTitle: '',
reaction0: '哇哦',
reaction1: 'prpr',
reaction2: '流汗',
reaction3: '生气',
reaction4: '流鼻血',
reaction5: 'あべ',
},
reaction: [
'https://ali-imgs.acfun.cn/kimg/bs2/zt-image-host/ChYwOGQ3OTRhYzRjMTBkZmZhZDVmZTA1EIjM1y8.gif',
'https://ali-imgs.acfun.cn/kimg/bs2/zt-image-host/ChQwOGEyOTlhYzRjMTBmOWFkZjAwYRCIzNcv.gif',
'https://ali-imgs.acfun.cn/kimg/bs2/zt-image-host/ChYwOGRhOTVhYzRjMTBkNmViYTVlNTA2EIjM1y8.gif',
'https://ali-imgs.acfun.cn/kimg/bs2/zt-image-host/ChYwOGU1OTJhYzRjMTBmMzk3ZmVmNTAyEIjM1y8.gif',
'https://ali-imgs.acfun.cn/kimg/bs2/zt-image-host/ChYwOGNkODdhYzRjMTBjYmEwOTFjNjAxEIjM1y8.gif',
'https://ali-imgs.acfun.cn/kimg/bs2/zt-image-host/ChQwODliOGVhYzRjMTBmOGM0ZWY1ZRCIzNcv.gif',
],
meta: ['nick','mail'],
requiredMeta: ['mail'],
pageview: true,
search: false,
comment: false,
login: 'disable',
wordLimit: 50,
pageSize: 5,
avatar: 'monsterid',
emoji: [
'https://gcore.jsdelivr.net/gh/tamakyi/emoji-collection@main/acfun-color/60x60',
],
//禁止图片上传按钮
imageUploader: false,
//不显示版权标志
copyright: false,
});
});
</script>
{{ end }}
{{ if .CanDelete }}
<div id="answer-editor" style="display:none;">
<hr>
<form method="post" action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/answer" enctype="multipart/form-data">
{{ .CSRFTokenHTML }}
<div class="uk-margin uk-text-center">
<textarea name="answer" class="uk-textarea" rows="5" maxlength="{{ .Limits.AnswerContentLen }}"
placeholder="{{t .LangCode "qitem.answerPlaceholder"}}">{{ .Question.Answer }}</textarea>
<div x-data="{ files: null }" class="tb-img-uploader">
<div uk-form-custom>
<a href="#" class="uk-icon-link" uk-icon="image" style="margin-left: 10px"></a>
<span style="font-size: var(--tb-fs-xs)" x-text="files ? files[0].name : '{{t .LangCode "ask.addImage"}}'"></span>
<input id="uploader" name="images" type="file" accept="image/*" x-on:change="files = Object.values($event.target.files)">
</div>
<button x-show="files" type="button" class="uk-icon-link" uk-icon="close" style="margin-left: 10px;"
@click="files = null"></button>
</div>
</div>
{{ if ne .Question.ReceiveReplyEmail "" }}
<div class="uk-alert-warning uk-text-small" uk-alert>
<p>{{t .LangCode "qitem.emailNotifyHint"}}</p>
</div>
{{ end }}
<div class="uk-margin uk-text-center">
<button type="submit" class="uk-button uk-button-primary uk-button-small">{{t .LangCode "qitem.updateAnswer"}}</button>
<button type="button" class="uk-button uk-button-default uk-button-small uk-margin-small-left" onclick="tbToggleAnswerEditor()">{{t .LangCode "common.cancel"}}</button>
</div>
</form>
</div>
{{ end }}
</div>
{{ else if .CanDelete }}
<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="{{ if .AnswerAction }}{{ .AnswerAction }}{{ else }}/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/answer{{ end }}" enctype="multipart/form-data">
{{ .CSRFTokenHTML }}
<div class="uk-margin uk-text-center">
<textarea name="answer" class="uk-textarea" rows="5" maxlength="{{ .Limits.AnswerContentLen }}"
placeholder="{{t .LangCode "qitem.answerPlaceholder"}}">{{ .answer }}</textarea>
<div x-data="{ files: null }" class="tb-img-uploader">
<div uk-form-custom>
<a href="#" class="uk-icon-link" uk-icon="image" style="margin-left: 10px"></a>
<span style="font-size: var(--tb-fs-xs)" x-text="files ? files[0].name : '{{t .LangCode "ask.addImage"}}'"></span>
<input id="uploader" name="images" type="file" accept="image/*" x-on:change="files = Object.values($event.target.files)">
</div>
<button x-show="files" type="button" class="uk-icon-link" uk-icon="close" style="margin-left: 10px;"
@click="files = null"></button>
</div>
</div>
{{ if ne .Question.ReceiveReplyEmail "" }}
<div class="uk-alert-warning uk-text-small" uk-alert>
<p>{{t .LangCode "qitem.emailNotifyHint"}}</p>
</div>
{{ end }}
</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;">
<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">
{{ .CSRFTokenHTML }}
<button type="submit" class="uk-button uk-button-default">{{t .LangCode "qitem.markUnread"}}</button>
</form>
{{ else }}
<form method="post" action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/read">
{{ .CSRFTokenHTML }}
<button type="submit" class="uk-button uk-button-default">{{t .LangCode "qitem.markRead"}}</button>
</form>
{{ end }}
</div>
</div>
</div>
{{ end }}
<!-- 已回答的阅读状态(箱主):该行仅此一个元素,居中显示 -->
{{ if and .CanDelete (ne .Question.Answer "") }}
<div class="uk-card-body uk-padding-remove-top">
<div class="uk-text-center">
<span class="uk-label uk-label-success">✓ {{t .LangCode "qlist.read"}}</span>
<span class="uk-text-small uk-text-muted">{{t .LangCode "qitem.readLocked"}}</span>
</div>
</div>
{{ end }}
{{ if .FollowUps }}
<div class="uk-card-body uk-padding-remove-top">
{{ range $index, $elem := .FollowUps }}
<div class="tb-followup-card uk-margin-small-bottom">
<p class="uk-text-small uk-text-break uk-margin-remove-bottom"><strong>{{t $.LangCode "followup.content"}}</strong>{{ $elem.Content }}</p>
{{ if ne $elem.Answer "" }}
{{ if $.CanDelete }}
<div id="fu-display-{{ $index }}" class="uk-text-small uk-text-muted uk-text-break uk-margin-small-top">@{{ $.PageUser.Name }}:{{ MarkdownFormat $elem.Answer }}</div>
<div class="uk-margin-small-top uk-text-right">
<button type="button" class="uk-button uk-button-default uk-button-small" onclick="tbToggleFollowUpEditor({{ $index }})">{{t $.LangCode "qitem.updateAnswer"}}</button>
</div>
<div id="fu-editor-{{ $index }}" style="display:none;">
<hr>
<form method="post" action="/_/{{ $.PageUser.Domain }}/{{ $.Question.ID }}/follow-up/{{ $elem.ID }}/answer" class="uk-margin-small-top">
{{ $.CSRFTokenHTML }}
<textarea name="answer" class="uk-textarea" rows="3" maxlength="{{ $.Limits.AnswerContentLen }}" placeholder="{{t $.LangCode "followup.answerPlaceholder"}}">{{ $elem.Answer }}</textarea>
<div class="uk-margin-small-top uk-text-right">
<button type="submit" class="uk-button uk-button-primary uk-button-small">{{t $.LangCode "qitem.updateAnswer"}}</button>
<button type="button" class="uk-button uk-button-default uk-button-small uk-margin-small-left" onclick="tbToggleFollowUpEditor({{ $index }})">{{t $.LangCode "common.cancel"}}</button>
</div>
</form>
</div>
{{ else }}
<div class="uk-text-small uk-text-muted uk-text-break uk-margin-small-top">@{{ $.PageUser.Name }}:{{ MarkdownFormat $elem.Answer }}</div>
{{ end }}
{{ else if $.CanDelete }}
<form method="post" action="/_/{{ $.PageUser.Domain }}/{{ $.Question.ID }}/follow-up/{{ $elem.ID }}/answer" class="uk-margin-small-top">
{{ $.CSRFTokenHTML }}
<textarea name="answer" class="uk-textarea" rows="3" maxlength="{{ $.Limits.AnswerContentLen }}" placeholder="{{t $.LangCode "followup.answerPlaceholder"}}"></textarea>
<div class="uk-margin-small-top uk-text-right">
<button type="submit" class="uk-button uk-button-primary uk-button-small">{{t $.LangCode "followup.answerBtn"}}</button>
</div>
</form>
{{ else }}
<p class="uk-text-small uk-text-muted uk-margin-small-top">{{t $.LangCode "followup.waiting"}}</p>
{{ end }}
</div>
{{ end }}
</div>
{{ end }}
{{ if and .CanFollowUp (not .CanDelete) }}
<div class="uk-card-body uk-padding-remove-top">
<div class="tb-followup-card">
<h5 class="uk-margin-remove-bottom">{{t .LangCode "followup.askMore"}}</h5>
<p class="uk-text-small uk-text-muted">{{t .LangCode "followup.remaining" .FollowUpRemaining .FollowUpLimit}}</p>
<form method="post" action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/follow-up">
{{ .CSRFTokenHTML }}
<input type="hidden" name="token" value="{{ .QuestionToken }}">
<textarea name="content" class="uk-textarea" rows="3" maxlength="{{ .Limits.FollowUpContentLen }}" placeholder="{{t .LangCode "followup.contentPlaceholder"}}"></textarea>
<div class="uk-margin uk-text-center">
<button type="submit" class="uk-button uk-button-primary">{{t .LangCode "followup.submit"}}</button>
</div>
</form>
</div>
</div>
{{ end }}
{{ if and .WaitingFollowUpReply (not .CanDelete) }}
<div class="uk-card-body uk-padding-remove-top">
<div class="tb-followup-card uk-text-small uk-text-muted uk-text-center">{{t .LangCode "followup.waitingReply"}}</div>
</div>
{{ end }}
<div class="uk-card-footer">
{{template "base/alert" .}}
{{ if .CanDelete }}
{{ if .Question.IsPrivate }}
<form class="uk-float-right" method="post" action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/set-public">
{{ .CSRFTokenHTML }}
<button type="submit" class="uk-button uk-button-default uk-button-small">{{t .LangCode "qitem.public"}}</button>
</form>
{{ else }}
<form class="uk-float-right" method="post" action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/set-private">
{{ .CSRFTokenHTML }}
<button type="submit" class="uk-button uk-button-default uk-button-small">{{t .LangCode "qitem.private"}}</button>
</form>
{{ end }}
<a class="uk-button uk-button-danger uk-button-small" href="#">{{t .LangCode "qitem.delete"}}</a>
<div class="uk-dropbar uk-dropbar-top" uk-drop="stretch: x; mode: click">
<div class="uk-card uk-card-default uk-card-body">
<h3 class="uk-card-title">{{t .LangCode "qitem.danger"}}</h3>
<p>{{t .LangCode "qitem.deleteConfirm"}}</p>
<form class="uk-float-right"
method="post"
action="/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/delete">
{{ .CSRFTokenHTML }}
<button class="uk-button uk-button-danger">{{t .LangCode "qitem.deleteConfirmBtn"}}</button>
</form>
</div>
</div>
{{ end }}
<div class="uk-margin uk-text-center">
{{ $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="{{ if .NextUnreadAction }}{{ .NextUnreadAction }}{{ else }}/_/{{ .PageUser.Domain }}/{{ .Question.ID }}/next-unread{{ 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. -->
{{if not .IsOwnPage }}
<h5 class="uk-text-center">{{t .LangCode "qitem.askMore"}}</h5>
{{template "question/new-question-template" .}}
{{ end }}
<hr class="uk-divider-icon">
<p class="uk-text-left uk-text-muted uk-text-small">{{t .LangCode "ask.history" .PageUser.Name}} ({{ .AnsweredCount }})</p>
{{ template "question/history-template" . }}
</div>
</div>
</div>
<script>
function tbToggleAnswerEditor() {
var disp = document.getElementById('answer-display');
var trig = document.getElementById('answer-edit-trigger');
var ed = document.getElementById('answer-editor');
if (!ed) return;
if (ed.style.display === 'none') {
ed.style.display = 'block';
if (disp) disp.style.display = 'none';
if (trig) trig.style.display = 'none';
} else {
ed.style.display = 'none';
if (disp) disp.style.display = 'block';
if (trig) trig.style.display = 'block';
}
}
function tbToggleFollowUpEditor(idx) {
var disp = document.getElementById('fu-display-' + idx);
var ed = document.getElementById('fu-editor-' + idx);
if (!ed) return;
if (ed.style.display === 'none') {
ed.style.display = 'block';
if (disp) disp.style.display = 'none';
} else {
ed.style.display = 'none';
if (disp) disp.style.display = 'block';
}
}
</script>
{{template "base/footer" .}}