fix(ui): 提交防重复覆盖验证码路径、waline 按页加载、懒加载、safe-area、硬编码文案接字典

1) 防重复点击原来漏了最主要的一条路径
   footer 的全局 submit 监听会禁用按钮,但**程序化 form.submit() 不触发 submit 事件** ——
   走 reCAPTCHA / Turnstile 的流程是在回调里调 form.submit() 的,而默认配置
   captcha_type = recaptcha,也就是主提问流一直没有防重复。登录、注册、找回密码、
   投票、注销账号同样走这个回调。
   → 把「切提交中状态」抽成 tbBeginSubmit(form, trigger) 挂到 window,
     验证码回调先调它再 submit。7 个 data-callback="onSubmit" 的模板全部受益。
     顺带加了 data-tb-restore 的重复覆盖保护,避免 bfcache 复位后按钮卡在转圈。

2) waline.js / waline.css 从全站改按页加载
   只有 home.html 与 item.html 用评论区,但此前挂在 header 里 —— 登录、后台、投票、
   私聊每页都下载解析 waline.js(2.15 未压缩约 200KB)。实测 /login 已无引用。

3) 图片懒加载:explore 列表头像与题目配图加 loading="lazy"(首屏推荐位不加,避免影响 LCP)。

4) 移动端底栏:body 只预留固定 56px,而底栏 padding 含 safe-area-inset-bottom。
   带 Home Indicator 的 iPhone 上 inset 约 34px、底栏实际约 88px,最后约 32px
   (页脚备案、存活计时)被压在栏下,滚到底也看不到。改成 calc(56px + env(...))。

5) 硬编码中文接字典(en/ja 用户此前直接看到中文):
   history-template 的「查看回答 / 加载更多 / 加载中... / 无更多留言」、
   单题页「@xxx 以前回答过的问题」(字典里本来就有 ask.history)、
   投票页「(最多 N 项)」改为带参数的整句(原先前半走字典、后半手写中文)。
   新增 4 个 key × 4 语言。

实测:箱主语言设为 en 后箱主页显示 View answer ×2 / Load more / No more posts,中文零残留。
This commit is contained in:
tamakyi
2026-10-29 16:28:53 +08:00
parent f2e0c43083
commit 03cb5ddcf0
13 changed files with 98 additions and 41 deletions
+4
View File
@@ -299,6 +299,9 @@ var enDict = map[string]string{
"qlist.unpinShort": "Unpin", "qlist.unpinShort": "Unpin",
"qlist.myVotes": "My votes", "qlist.myVotes": "My votes",
"qlist.loadMore": "Load more", "qlist.loadMore": "Load more",
"qlist.noMore": "No more posts",
"qlist.loading": "Loading...",
"qlist.viewAnswer": "View answer",
"qlist.newVote": "New vote", "qlist.newVote": "New vote",
"qlist.edit": "Edit", "qlist.edit": "Edit",
"qlist.untitledVote": "Untitled vote", "qlist.untitledVote": "Untitled vote",
@@ -630,6 +633,7 @@ var enDict = map[string]string{
"vote.page.rankHint": "Rank each option 1-N (smaller = higher, no duplicates)", "vote.page.rankHint": "Rank each option 1-N (smaller = higher, no duplicates)",
"vote.page.rankPlaceholder": "Rank", "vote.page.rankPlaceholder": "Rank",
"vote.page.multipleHint": "Multiple choice", "vote.page.multipleHint": "Multiple choice",
"vote.page.multipleHintWithMax": "Multiple choice (up to %d)",
"vote.page.customOption": "Custom option (optional)", "vote.page.customOption": "Custom option (optional)",
"vote.page.customPlaceholder": "Type your own option", "vote.page.customPlaceholder": "Type your own option",
"vote.page.customOther": "Other", "vote.page.customOther": "Other",
+4
View File
@@ -299,6 +299,9 @@ var jaDict = map[string]string{
"qlist.unpinShort": "解除", "qlist.unpinShort": "解除",
"qlist.myVotes": "作成した投票", "qlist.myVotes": "作成した投票",
"qlist.loadMore": "もっと読み込む", "qlist.loadMore": "もっと読み込む",
"qlist.noMore": "これ以上の投稿はありません",
"qlist.loading": "読み込み中...",
"qlist.viewAnswer": "回答を見る",
"qlist.newVote": "投票を作成", "qlist.newVote": "投票を作成",
"qlist.edit": "編集", "qlist.edit": "編集",
"qlist.untitledVote": "無題の投票", "qlist.untitledVote": "無題の投票",
@@ -629,6 +632,7 @@ var jaDict = map[string]string{
"vote.page.rankHint": "各選択肢に 1-N の順位を入力してください(小さいほど上位、重複不可)", "vote.page.rankHint": "各選択肢に 1-N の順位を入力してください(小さいほど上位、重複不可)",
"vote.page.rankPlaceholder": "順位", "vote.page.rankPlaceholder": "順位",
"vote.page.multipleHint": "複数選択可", "vote.page.multipleHint": "複数選択可",
"vote.page.multipleHintWithMax": "複数選択可(最大 %d 件)",
"vote.page.customOption": "カスタム選択肢(任意)", "vote.page.customOption": "カスタム選択肢(任意)",
"vote.page.customPlaceholder": "あなたの選択肢を入力", "vote.page.customPlaceholder": "あなたの選択肢を入力",
"vote.page.customOther": "その他", "vote.page.customOther": "その他",
+4
View File
@@ -299,6 +299,9 @@ var zhCNDict = map[string]string{
"qlist.unpinShort": "取消置顶", "qlist.unpinShort": "取消置顶",
"qlist.myVotes": "我创建的投票", "qlist.myVotes": "我创建的投票",
"qlist.loadMore": "加载更多", "qlist.loadMore": "加载更多",
"qlist.noMore": "无更多留言",
"qlist.loading": "加载中...",
"qlist.viewAnswer": "查看回答",
"qlist.newVote": "新建投票", "qlist.newVote": "新建投票",
"qlist.edit": "编辑", "qlist.edit": "编辑",
"qlist.untitledVote": "未命名投票", "qlist.untitledVote": "未命名投票",
@@ -630,6 +633,7 @@ var zhCNDict = map[string]string{
"vote.page.rankHint": "请为每个选项填写 1-N 的排名(数字越小越靠前,不可重复)", "vote.page.rankHint": "请为每个选项填写 1-N 的排名(数字越小越靠前,不可重复)",
"vote.page.rankPlaceholder": "排名", "vote.page.rankPlaceholder": "排名",
"vote.page.multipleHint": "可多选", "vote.page.multipleHint": "可多选",
"vote.page.multipleHintWithMax": "可多选(最多 %d 项)",
"vote.page.customOption": "补充选项(可选)", "vote.page.customOption": "补充选项(可选)",
"vote.page.customPlaceholder": "填写你心中的其他选项", "vote.page.customPlaceholder": "填写你心中的其他选项",
"vote.page.customOther": "其他", "vote.page.customOther": "其他",
+4
View File
@@ -299,6 +299,9 @@ var zhTWDict = map[string]string{
"qlist.unpinShort": "取消置頂", "qlist.unpinShort": "取消置頂",
"qlist.myVotes": "我建立的投票", "qlist.myVotes": "我建立的投票",
"qlist.loadMore": "載入更多", "qlist.loadMore": "載入更多",
"qlist.noMore": "無更多留言",
"qlist.loading": "載入中...",
"qlist.viewAnswer": "查看回答",
"qlist.newVote": "新建投票", "qlist.newVote": "新建投票",
"qlist.edit": "編輯", "qlist.edit": "編輯",
"qlist.untitledVote": "未命名投票", "qlist.untitledVote": "未命名投票",
@@ -629,6 +632,7 @@ var zhTWDict = map[string]string{
"vote.page.rankHint": "請為每個選項填寫 1-N 的排名(數字越小越靠前,不可重複)", "vote.page.rankHint": "請為每個選項填寫 1-N 的排名(數字越小越靠前,不可重複)",
"vote.page.rankPlaceholder": "排名", "vote.page.rankPlaceholder": "排名",
"vote.page.multipleHint": "可多選", "vote.page.multipleHint": "可多選",
"vote.page.multipleHintWithMax": "可多選(最多 %d 項)",
"vote.page.customOption": "補充選項(可選)", "vote.page.customOption": "補充選項(可選)",
"vote.page.customPlaceholder": "填寫你心中的其他選項", "vote.page.customPlaceholder": "填寫你心中的其他選項",
"vote.page.customOther": "其他", "vote.page.customOther": "其他",
+5 -1
View File
@@ -854,6 +854,10 @@ a.tb-chip:hover {
} }
/* 给页面底部预留空间,避免页脚内容被固定底栏遮挡 */ /* 给页面底部预留空间,避免页脚内容被固定底栏遮挡 */
body { body {
padding-bottom: 56px; /* 必须加上 safe-area-inset-bottom:底栏自身的 padding 已经包含了它,
而 body 只预留了固定的 56px。带 Home Indicator 的 iPhone 上 inset 约 34px,
底栏实际高度约 88px —— 最后约 32px 内容(页脚备案、存活计时)会被压在栏下,
滚到底也看不到。 */
padding-bottom: calc(56px + env(safe-area-inset-bottom));
} }
} }
+44 -24
View File
@@ -62,6 +62,49 @@
}); });
} }
// tbBeginSubmit 把表单切到「提交中」状态:触发按钮换成 spinner + 本地化文案,
// 并禁用表单内所有按钮。
//
// 抽成具名函数(而不是内联在下面的事件监听里)是因为 **程序化 submit() 不触发
// submit 事件**:走 reCAPTCHA / Turnstile 的流程是在回调里调 form.submit() 的,
// 原本完全绕过下面那个监听器 —— 而默认部署 captcha_type = recaptcha,
// 也就是主提问流一直没有防重复点击保护。那些回调现在先调本函数再 submit()。
function tbBeginSubmit(form, trigger) {
if (!form) return;
// 下载 / 新窗口类表单(target="_blank",如导出):当前页不发生导航,也没有 bfcache
// 复位机会,进入“提交中”状态后按钮会永远卡在转圈 + 禁用。这类表单直接跳过提交反馈。
if (form.getAttribute('target') === '_blank') return;
// 触发提交的按钮(programmatic submit 时调用方传 null,回退到首个 submit 按钮)
var btn = trigger || form.querySelector('button[type="submit"]');
// 表单含已选中的文件 → 提示“上传中”,否则“提交中”
var hasFile = false;
form.querySelectorAll('input[type="file"]').forEach(function(f) {
if (f.files && f.files.length > 0) hasFile = true;
});
var label = hasFile ? SUBMIT_I18N.uploading : SUBMIT_I18N.submitting;
if (btn && btn.tagName === 'BUTTON') {
// 已经切过一次就不再覆盖 data-tb-restore,否则会把 spinner 的 HTML 当成原始内容,
// 导致 bfcache 复位后按钮永远停在转圈上。
if (!btn.hasAttribute('data-tb-restore')) {
btn.setAttribute('data-tb-restore', btn.innerHTML);
}
btn.innerHTML = '<span uk-spinner="ratio: 0.6"></span> ' + label;
}
// 禁用表单内所有按钮,防止重复提交
form.querySelectorAll('button').forEach(function(b) {
b.disabled = true;
b.setAttribute('data-tb-submitting', '1');
});
}
// 挂到 window 上供 header 里的验证码回调调用(脚本顺序:header → footer,
// 但回调发生在用户点击之后,此时本函数已定义)
window.tbBeginSubmit = tbBeginSubmit;
document.addEventListener('submit', function(e) { document.addEventListener('submit', function(e) {
var form = e.target; var form = e.target;
if (!form || form.tagName !== 'FORM') return; if (!form || form.tagName !== 'FORM') return;
@@ -73,30 +116,7 @@
return; return;
} }
// 下载 / 新窗口类表单(target="_blank",如导出):当前页不发生导航,也没有 bfcache tbBeginSubmit(form, e.submitter);
// 复位机会,进入“提交中”状态后按钮会永远卡在转圈 + 禁用。这类表单直接跳过提交反馈。
if (form.getAttribute('target') === '_blank') return;
// 触发提交的按钮(programmatic submit 时 e.submitter 为 null,回退到首个 submit 按钮)
var trigger = e.submitter || form.querySelector('button[type="submit"]');
// 表单含已选中的文件 → 提示“上传中”,否则“提交中”
var hasFile = false;
form.querySelectorAll('input[type="file"]').forEach(function(f) {
if (f.files && f.files.length > 0) hasFile = true;
});
var label = hasFile ? SUBMIT_I18N.uploading : SUBMIT_I18N.submitting;
if (trigger && trigger.tagName === 'BUTTON') {
trigger.setAttribute('data-tb-restore', trigger.innerHTML);
trigger.innerHTML = '<span uk-spinner="ratio: 0.6"></span> ' + label;
}
// 禁用表单内所有按钮,防止重复提交
form.querySelectorAll('button').forEach(function(b) {
b.disabled = true;
b.setAttribute('data-tb-submitting', '1');
});
}); });
// bfcache 还原时复位按钮 // bfcache 还原时复位按钮
+16 -5
View File
@@ -24,11 +24,9 @@
<link rel="stylesheet" href="/static/css/base.css?v={{ .StaticVersion }}"/> <link rel="stylesheet" href="/static/css/base.css?v={{ .StaticVersion }}"/>
{{ if .ThemeSlug }}<!-- 箱主主题:只覆盖配色/图案/页面背景,不涉及布局;仅提问箱相关页面加载 --> {{ if .ThemeSlug }}<!-- 箱主主题:只覆盖配色/图案/页面背景,不涉及布局;仅提问箱相关页面加载 -->
<link rel="stylesheet" href="/static/css/themes/{{ .ThemeSlug }}.css?v={{ .StaticVersion }}"/>{{ end }} <link rel="stylesheet" href="/static/css/themes/{{ .ThemeSlug }}.css?v={{ .StaticVersion }}"/>{{ end }}
<!-- Waline 评论区样式:非阻塞加载,不阻塞首屏渲染 -->
<link rel="stylesheet" href="https://r2.337845818.best/tamabox/cdn/waline/client/v2/waline-no-commits.css" media="print" onload="this.media='all'"/>
<script defer src="https://cdnjs.snrat.com/ajax/libs/uikit/3.3.3/js/uikit.min.js"></script> <script defer src="https://cdnjs.snrat.com/ajax/libs/uikit/3.3.3/js/uikit.min.js"></script>
<script defer src="https://cdnjs.snrat.com/ajax/libs/uikit/3.3.3/js/uikit-icons.min.js"></script> <script defer src="https://cdnjs.snrat.com/ajax/libs/uikit/3.3.3/js/uikit-icons.min.js"></script>
<!-- dayjs/alpinejs/vconsole/waline.js 已迁 cdnjs.snrat.com(sha256 与官方一致,国内实测快 2-7x) --> <!-- dayjs/alpinejs/vconsole 已迁 cdnjs.snrat.com(sha256 与官方一致,国内实测快 2-7x) -->
<script defer src="https://cdnjs.snrat.com/ajax/libs/dayjs/1.10.8/dayjs.min.js"></script> <script defer src="https://cdnjs.snrat.com/ajax/libs/dayjs/1.10.8/dayjs.min.js"></script>
<script defer src="https://cdnjs.snrat.com/ajax/libs/alpinejs/3.9.0/cdn.min.js"></script> <script defer src="https://cdnjs.snrat.com/ajax/libs/alpinejs/3.9.0/cdn.min.js"></script>
{{ if .VConsole }} {{ if .VConsole }}
@@ -36,7 +34,6 @@
<script>new window.VConsole()</script> <script>new window.VConsole()</script>
{{ end }} {{ end }}
<!-- jQuery 已改为按需加载:仅 easy.qrcode 二维码页(list/question-not-found/profile)自行引入 --> <!-- jQuery 已改为按需加载:仅 easy.qrcode 二维码页(list/question-not-found/profile)自行引入 -->
<script defer src="https://cdnjs.snrat.com/ajax/libs/waline/2.15.8/waline.js"></script>
{{ if eq .CaptchaType "math" }} {{ if eq .CaptchaType "math" }}
<!-- 算数验证码:弹窗模式,不影响原布局 --> <!-- 算数验证码:弹窗模式,不影响原布局 -->
<style> <style>
@@ -105,7 +102,21 @@
}</script> }</script>
{{ else }} {{ else }}
<script src="{{.RecaptchaDomain}}/recaptcha/api.js" async defer></script> <script src="{{.RecaptchaDomain}}/recaptcha/api.js" async defer></script>
<script>function onSubmit() {document.getElementById('form').submit();}</script> <script>
// reCAPTCHA v3 回调:拿到 token 后手动提交表单。
//
// 程序化 submit() **不触发 submit 事件**,所以 footer 里那个全局监听器
// (禁用按钮 + 转圈)对它完全无效 —— 而默认配置 captcha_type = recaptcha 时,
// 这就是主提问流的提交路径,等于一直没有防重复点击。
// 这里先显式切到「提交中」再 submit。tbBeginSubmit 定义在 footer.html,
// 脚本顺序在本文件之后;回调发生在用户点击之后,那时它已就绪,仍做一次判空兜底。
function onSubmit() {
var f = document.getElementById('form');
if (!f) return;
if (window.tbBeginSubmit) { window.tbBeginSubmit(f, null); }
f.submit();
}
</script>
{{ end }} {{ end }}
{{ if ne SentryDSN "" }} {{ if ne SentryDSN "" }}
<script <script
+3
View File
@@ -169,6 +169,9 @@
</ul> </ul>
</details> </details>
<!-- 评论区资源只在本页加载(此前挂在 header 里,等于每页都下载 ~200KB 的 waline.js) -->
<link rel="stylesheet" href="https://r2.337845818.best/tamabox/cdn/waline/client/v2/waline-no-commits.css" media="print" onload="this.media='all'"/>
<script defer src="https://cdnjs.snrat.com/ajax/libs/waline/2.15.8/waline.js"></script>
<div id="waline"></div> <div id="waline"></div>
<script> <script>
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
+5 -5
View File
@@ -4,7 +4,7 @@
<div data-qid="{{$elem.ID}}"> <div data-qid="{{$elem.ID}}">
<hr> <hr>
<a class="uk-button uk-button-default uk-button-small uk-float-right" <a class="uk-button uk-button-default uk-button-small uk-float-right"
href="/_/{{$.PageUser.Domain}}/{{$elem.ID}}">查看回答</a> href="/_/{{$.PageUser.Domain}}/{{$elem.ID}}">{{t $.LangCode "qlist.viewAnswer"}}</a>
<div class="uk-text-left uk-text-small uk-text-muted"> <div class="uk-text-left uk-text-small uk-text-muted">
{{if $elem.IsPinned}}<span class="tb-pin-flag" title="{{t $.LangCode "ask.pinned"}}">⭐</span>{{end}} {{if $elem.IsPinned}}<span class="tb-pin-flag" title="{{t $.LangCode "ask.pinned"}}">⭐</span>{{end}}
{{Date $elem.CreatedAt "Y-m-d H:i:s"}} {{Date $elem.CreatedAt "Y-m-d H:i:s"}}
@@ -58,7 +58,7 @@
<div> <div>
<hr> <hr>
<a class='uk-button uk-button-default uk-button-small uk-float-right' <a class='uk-button uk-button-default uk-button-small uk-float-right'
href='/_/{{.PageUser.Domain}}/${question.id}'>查看回答</a> href='/_/{{.PageUser.Domain}}/${question.id}'>{{t .LangCode "qlist.viewAnswer"}}</a>
<div class='uk-text-left uk-text-small uk-text-muted'> <div class='uk-text-left uk-text-small uk-text-muted'>
${question.is_pinned ? '<span class=\'tb-pin-flag\' title=\'{{$pinTitle}}\'>⭐</span>' : ''} ${question.is_pinned ? '<span class=\'tb-pin-flag\' title=\'{{$pinTitle}}\'>⭐</span>' : ''}
${dayjs(question.created_at).format('YYYY-MM-DD HH:mm:ss')} ${dayjs(question.created_at).format('YYYY-MM-DD HH:mm:ss')}
@@ -72,12 +72,12 @@
// 网络失败/响应非法 JSON 时复位 loading,避免按钮永远停留在加载中状态 // 网络失败/响应非法 JSON 时复位 loading,避免按钮永远停留在加载中状态
.catch(() => { loading = false }) .catch(() => { loading = false })
}" class="uk-button uk-button-default uk-width-1-1 uk-margin-small-bottom"> }" class="uk-button uk-button-default uk-width-1-1 uk-margin-small-bottom">
<span x-show="!loading">加载更多</span> <span x-show="!loading">{{t .LangCode "qlist.loadMore"}}</span>
<span x-show="loading"><div uk-spinner="ratio: 0.8"></div> 加载中...</span> <span x-show="loading"><div uk-spinner="ratio: 0.8"></div> {{t .LangCode "qlist.loading"}}</span>
</button> </button>
<div x-show="!more" class="uk-text-meta uk-text-center"> <div x-show="!more" class="uk-text-meta uk-text-center">
<hr> <hr>
无更多留言 {{t .LangCode "qlist.noMore"}}
<br><br> <br><br>
</div> </div>
</div> </div>
+5 -2
View File
@@ -78,7 +78,7 @@
{{ range $index, $elem := .AskUploadImages }} {{ range $index, $elem := .AskUploadImages }}
<div uk-lightbox> <div uk-lightbox>
<a href="https://{{ ImageBucketCDNHost }}/{{ $elem.Key }}"> <a href="https://{{ ImageBucketCDNHost }}/{{ $elem.Key }}">
<img class="uk-border-rounded uk-object-fill" src="https://{{ ImageBucketCDNHost }}/{{ $elem.Key }}" width="100" height="100" alt=""> <img class="uk-border-rounded uk-object-fill" src="https://{{ ImageBucketCDNHost }}/{{ $elem.Key }}" width="100" height="100" alt="" loading="lazy">
</a> </a>
</div> </div>
{{ end }} {{ end }}
@@ -96,7 +96,7 @@
{{ range $index, $elem := .AnswerUploadImages }} {{ range $index, $elem := .AnswerUploadImages }}
<div uk-lightbox> <div uk-lightbox>
<a href="https://{{ ImageBucketCDNHost }}/{{ $elem.Key }}"> <a href="https://{{ ImageBucketCDNHost }}/{{ $elem.Key }}">
<img class="uk-border-rounded uk-object-fill" src="https://{{ ImageBucketCDNHost }}/{{ $elem.Key }}" width="100" height="100" alt=""> <img class="uk-border-rounded uk-object-fill" src="https://{{ ImageBucketCDNHost }}/{{ $elem.Key }}" width="100" height="100" alt="" loading="lazy">
</a> </a>
</div> </div>
{{ end }} {{ end }}
@@ -147,6 +147,9 @@
{{ end }} {{ end }}
</div> </div>
{{ if not .CanDelete }} {{ if not .CanDelete }}
<!-- 评论区资源只在本页加载(此前挂在 header 里,等于每页都下载) -->
<link rel="stylesheet" href="https://r2.337845818.best/tamabox/cdn/waline/client/v2/waline-no-commits.css" media="print" onload="this.media='all'"/>
<script defer src="https://cdnjs.snrat.com/ajax/libs/waline/2.15.8/waline.js"></script>
<div id="waline"></div> <div id="waline"></div>
<script> <script>
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
+1 -1
View File
@@ -98,7 +98,7 @@ document.getElementById("introcolor").style.color = {{.PageUser.Introcolor}};
<hr class="uk-divider-icon"> <hr class="uk-divider-icon">
{{ if ne (len .PageQuestions) 0 }} {{ if ne (len .PageQuestions) 0 }}
<p class="uk-text-left uk-text-muted uk-text-small">@{{ .PageUser.Name }} 以前回答过的问题 ({{ .AnsweredCount }})</p> <p class="uk-text-left uk-text-muted uk-text-small">{{t .LangCode "ask.history" .PageUser.Name}} ({{ .AnsweredCount }})</p>
{{ template "question/history-template" . }} {{ template "question/history-template" . }}
{{ end }} {{ end }}
</div> </div>
+2 -2
View File
@@ -374,7 +374,7 @@
<div class="uk-card uk-card-default uk-card-hover uk-card-body"> <div class="uk-card uk-card-default uk-card-hover uk-card-body">
<div class="uk-flex uk-flex-top"> <div class="uk-flex uk-flex-top">
<div> <div>
<img class="explore-avatar" src="{{$user.Avatar}}" alt="avatar"> <img class="explore-avatar" src="{{$user.Avatar}}" alt="avatar" loading="lazy">
</div> </div>
<div class="explore-info"> <div class="explore-info">
<div class="explore-name truncate"> <div class="explore-name truncate">
@@ -587,7 +587,7 @@ document.addEventListener('DOMContentLoaded', function() {
html += ` html += `
<div> <div>
<a href="/_/${escapeHTML(user.domain)}" class="recommend-card"> <a href="/_/${escapeHTML(user.domain)}" class="recommend-card">
<img class="recommend-avatar" src="${safeAvatarURL(user.avatar)}" alt="avatar"> <img class="recommend-avatar" src="${safeAvatarURL(user.avatar)}" alt="avatar" loading="lazy">
<div class="recommend-info"> <div class="recommend-info">
<div class="recommend-name truncate">${escapeHTML(user.name)}</div> <div class="recommend-name truncate">${escapeHTML(user.name)}</div>
<div class="recommend-tags">${tagsHtml}</div> <div class="recommend-tags">${tagsHtml}</div>
+1 -1
View File
@@ -137,7 +137,7 @@
{{ end }} {{ end }}
{{ else if eq .Vote.Type "multiple" }} {{ else if eq .Vote.Type "multiple" }}
<div class="uk-text-meta uk-text-small uk-margin-small-bottom">{{t .LangCode "vote.page.multipleHint"}}(最多 {{ .Vote.MaxChoices }} 项)</div> <div class="uk-text-meta uk-text-small uk-margin-small-bottom">{{t .LangCode "vote.page.multipleHintWithMax" .Vote.MaxChoices}}</div>
<div id="multiple-options"> <div id="multiple-options">
{{ range .Options }} {{ range .Options }}
<label class="tb-choice"> <label class="tb-choice">