一号事项(剩余令牌化):
- templates/user/profile.html 20 处、base.css 9 处 font-size 全部落到 --tb-fs-*
- base.css 导航四档(0.95/0.88/0.82/0.78)合并成「桌面 md → ≤1000 xs」;logo 三档统一 lg
- 有理由保留的:.tb-pin-flag / .md-body code 的 0.9em(相对父级)、验证码 1.4/1.2rem(等宽字形)、
chat 关闭按钮 40px、password 页 3em、mail/*(邮件客户端不保证 var());行内 12px/0.75rem 等值顺手换掉
二号事项(主色对比度):
- base/default #1e87f0(3.42) → #0e6fd0(4.70);shiroko #4a7fc1(3.69) → #3b6dac(4.74);
sakura #e5678f(2.80) → #ba3e66(4.70,饱和度压到 .5 保持柔和)。同色相只压亮度,目标 4.7 留余量
- 深色块 muted 之前只对 --tb-bg 算(4.7 以为过了),对压平后的卡片其实 4.31/4.17 → 提到 #a18790 / #7f8ea1
- 新增 --tb-on-primary(亮 #ffffff / 暗 #0b1016):深色模式主色偏亮,白字只有 2.2~3.1:1,
用同一个令牌兼顾「链接色要深」和「按钮底要深」是矛盾的。.uk-button-primary 与
.uk-label:not(语义色) 改吃它 → 深色按钮变成浅底深字(6.10 / 8.65 / 7.27)
- 顺手封掉几处主色泄漏:header 验证码 focus、profile .feat-day.is-on、explore 随机投票的 JS outline、
vote-results 行内色,全部改成令牌(否则换主题时不跟着变)
生成器同步:paletteVars 导出 --tb-on-primary(白字/深字择优);?dump=palette 增加
onPrimaryCr / onPrimaryPickedBest / warnings —— 主色卡中间调时只警告不判失败;
配色面板给橙色提示。_scaffold 令牌 29 → 30。StaticVersion → 20261021c。
验证:TestSwatch 断言同步为 #ba3e66;8 种组合(base 亮暗 + 三主题亮暗)对比度全部 ≥4.5;
go test internal/{theme,templates,conf,i18n} 全绿;生成器 validate/fit/quality/palette/layers/fx/fxpipe 全过。
233 lines
9.5 KiB
HTML
233 lines
9.5 KiB
HTML
{{ if .AskClosedMessage }}
|
||
<div>
|
||
<div uk-alert class="uk-text-center">
|
||
<p>{{ .AskClosedMessage }}</p>
|
||
{{ if .AskNextOpenAtStr }}
|
||
<p class="uk-text-small uk-text-muted">{{t .LangCode "ask.nextOpenAt" .AskNextOpenAtStr}}</p>
|
||
{{ end }}
|
||
{{ if .AskLastCloseAtStr }}
|
||
<p class="uk-text-small uk-text-muted">{{t .LangCode "ask.lastCloseAt" .AskLastCloseAtStr}}</p>
|
||
{{ end }}
|
||
</div>
|
||
|
||
<!-- 提问功能关闭但私聊开启:保留独立私聊入口(提问与私聊是独立功能) -->
|
||
{{ if .AllowChat }}
|
||
<div class="uk-margin-top">
|
||
{{ if .LoggedUser }}
|
||
<form method="post" action="/_/{{.PageUser.Domain}}" id="chat-only-form" enctype="multipart/form-data">
|
||
{{ .CSRFTokenHTML }}
|
||
{{ if .PageUser.AskPassword }}
|
||
<input type="hidden" name="ask_password" id="ask_password_input" value="">
|
||
{{ end }}
|
||
<input type="hidden" name="start_chat_val" value="true">
|
||
<input type="hidden" name="g-recaptcha-response" id="chat-g-recaptcha-response" value="">
|
||
<div class="uk-margin uk-text-center">
|
||
<textarea name="content" class="uk-textarea" rows="4" placeholder="{{t .LangCode "ask.chatPlaceholder"}}" maxlength="{{ .Limits.ChatMessageLen }}"></textarea>
|
||
</div>
|
||
<div class="uk-margin uk-text-center">
|
||
{{ if eq .CaptchaType "math" }}
|
||
<button type="button" class="uk-button uk-button-secondary math-captcha-trigger" id="chat-only-submit-btn">{{t .LangCode "ask.chat"}}</button>
|
||
{{ else }}
|
||
<button type="submit" class="uk-button uk-button-secondary g-recaptcha"
|
||
data-sitekey="{{.RecaptchaSiteKey}}" data-callback="onChatOnlySubmit"
|
||
id="chat-only-submit-btn">{{t .LangCode "ask.chat"}}</button>
|
||
{{ end }}
|
||
<div class="uk-text-meta">{{t .LangCode "ask.chatLimit" .PageUser.ChatMaxMessages}}</div>
|
||
</div>
|
||
</form>
|
||
{{ else }}
|
||
<!-- 未登录:私聊需登录 -->
|
||
<div class="uk-text-center uk-margin">
|
||
<p class="uk-text-small uk-text-muted">{{t .LangCode "ask.loginRequiredChat"}}</p>
|
||
<a class="uk-button uk-button-secondary uk-button-small" href="/login?to={{.CurrentURI}}">{{t .LangCode "ask.goLogin"}}</a>
|
||
</div>
|
||
{{ end }}
|
||
</div>
|
||
{{ end }}
|
||
|
||
{{ if and .AllowChat .LoggedUser }}
|
||
{{ if ne .CaptchaType "math" }}
|
||
<!-- 独立私聊表单的验证码回调(提问表单的 onSubmit 只提交 #form) -->
|
||
<script>
|
||
function onChatOnlySubmit(token) {
|
||
var form = document.getElementById('chat-only-form');
|
||
if (!form) return;
|
||
var btn = document.getElementById('chat-only-submit-btn');
|
||
if (btn) { btn.disabled = true; btn.innerText = '{{t .LangCode "common.verifying"}}'; }
|
||
var needPassword = {{ if .PageUser.AskPassword }}true{{ else }}false{{ end }};
|
||
if (needPassword) {
|
||
UIkit.modal.prompt('{{t .LangCode "ask.needPassword"}}', '', 'password')
|
||
.then(function(password) {
|
||
if (password) {
|
||
var pw = form.querySelector('#ask_password_input');
|
||
if (pw) pw.value = password;
|
||
form.submit();
|
||
} else {
|
||
if (btn) { btn.disabled = false; btn.innerText = '{{t .LangCode "ask.chat"}}'; }
|
||
}
|
||
});
|
||
} else {
|
||
form.submit();
|
||
}
|
||
}
|
||
</script>
|
||
{{ end }}
|
||
{{ end }}
|
||
</div>
|
||
{{ else if not .CanAsk }}
|
||
<div>
|
||
<div uk-alert class="uk-text-center">
|
||
<p>{{t .LangCode "ask.needLogin"}}</p>
|
||
</div>
|
||
<div class="uk-margin uk-text-center">
|
||
<a class="uk-button uk-button-primary" href="/login?to={{.CurrentURI}}">{{t .LangCode "ask.goLogin"}}</a>
|
||
</div>
|
||
</div>
|
||
{{ else }}
|
||
<form method="post" action="/_/{{.PageUser.Domain}}" enctype="multipart/form-data" id="form">
|
||
{{ .CSRFTokenHTML }}
|
||
|
||
{{if .PageUser.AskPassword}}
|
||
<input type="hidden" name="ask_password" id="ask_password_input" value="">
|
||
{{end}}
|
||
|
||
<input type="hidden" name="start_chat_val" id="start_chat_val" value="false">
|
||
<input type="hidden" name="g-recaptcha-response" id="g-recaptcha-response" value="">
|
||
|
||
<div class="uk-margin uk-text-center">
|
||
<textarea name="content" class="uk-textarea" rows="5" placeholder="{{t .LangCode "ask.placeholder"}}"
|
||
maxlength="{{ .Limits.QuestionContentLen }}">{{.content}}</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>
|
||
|
||
<div class="uk-margin uk-grid-small">
|
||
<label class="uk-text-small">
|
||
<input name="private" class="uk-checkbox" type="checkbox"> {{t .LangCode "ask.private"}}
|
||
</label>
|
||
<label class="uk-text-small">
|
||
<input name="receive_reply_via_email" id="notify-checkbox" class="uk-checkbox" type="checkbox"> {{t .LangCode "ask.notify"}}
|
||
</label>
|
||
<div id="email-container" style="width: 100%; display: none;" class="uk-margin-small-top">
|
||
<input class="uk-input uk-form-small" type="email" name="receive_reply_email" id="email-input" placeholder="{{t .LangCode "ask.emailPlaceholder"}}">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="uk-margin uk-text-center">
|
||
<div class="uk-grid-small uk-flex-center" uk-grid>
|
||
<div>
|
||
{{ if eq .CaptchaType "math" }}
|
||
<button type="button" class="uk-button uk-button-primary math-captcha-trigger" id="normal-submit-btn">{{t .LangCode "ask.submit"}}</button>
|
||
{{ else }}
|
||
<button type="submit" class="uk-button uk-button-primary g-recaptcha"
|
||
data-sitekey="{{.RecaptchaSiteKey}}" data-callback="onSubmit"
|
||
id="normal-submit-btn">{{t .LangCode "ask.submit"}}</button>
|
||
{{ end }}
|
||
</div>
|
||
{{ if not .AllowChat }}
|
||
<div>
|
||
<button type="button" class="uk-button uk-button-secondary" disabled
|
||
uk-tooltip="{{t .LangCode "ask.chatDisabled"}}">{{t .LangCode "ask.chat"}}</button>
|
||
</div>
|
||
{{ else if .LoggedUser }}
|
||
<div>
|
||
<button type="button" class="uk-button uk-button-secondary" id="chat-submit-btn">{{t .LangCode "ask.chat"}}</button>
|
||
<div class="uk-text-meta">{{t .LangCode "ask.chatLimit" .PageUser.ChatMaxMessages}}</div>
|
||
</div>
|
||
{{ else }}
|
||
<div>
|
||
<button type="button" class="uk-button uk-button-secondary" id="chat-submit-btn">{{t .LangCode "ask.chat"}}</button>
|
||
</div>
|
||
{{ end }}
|
||
</div>
|
||
</div>
|
||
</form>
|
||
|
||
<script>
|
||
// 邮箱显隐与验证逻辑
|
||
var notifyCheckbox = document.getElementById('notify-checkbox');
|
||
var emailContainer = document.getElementById('email-container');
|
||
var emailInput = document.getElementById('email-input');
|
||
|
||
if (notifyCheckbox && emailContainer && emailInput) {
|
||
notifyCheckbox.addEventListener('change', function() {
|
||
if (this.checked) {
|
||
emailContainer.style.display = 'block';
|
||
emailInput.setAttribute('required', 'required');
|
||
} else {
|
||
emailContainer.style.display = 'none';
|
||
emailInput.removeAttribute('required');
|
||
emailInput.value = '';
|
||
}
|
||
});
|
||
}
|
||
|
||
// 私聊按钮:设置标识后触发验证弹窗
|
||
var chatBtn = document.getElementById('chat-submit-btn');
|
||
if (chatBtn) {
|
||
chatBtn.addEventListener('click', function() {
|
||
var isLogged = {{ if .LoggedUser }}true{{ else }}false{{ end }};
|
||
if (!isLogged) {
|
||
var currentURI = '{{.CurrentURI}}';
|
||
UIkit.modal.dialog(
|
||
'<div class="uk-text-center uk-padding" style="min-height: 150px; display: flex; flex-direction: column; justify-content: center;">' +
|
||
'<h3 class="uk-modal-title uk-margin-small-bottom">{{t .LangCode "ask.loginRequired"}}</h3>' +
|
||
'<p class="uk-text-muted uk-margin-small-bottom">{{t .LangCode "ask.loginRequiredChat"}}</p>' +
|
||
'<a href="/login?to=' + encodeURIComponent(currentURI) + '" class="uk-button uk-button-primary">{{t .LangCode "ask.goLogin"}}</a>' +
|
||
'</div>',
|
||
{width: '400px'}
|
||
);
|
||
return;
|
||
}
|
||
document.getElementById('start_chat_val').value = 'true';
|
||
{{ if eq .CaptchaType "math" }}
|
||
var submitBtn = document.getElementById('normal-submit-btn');
|
||
if (submitBtn) submitBtn.click();
|
||
{{ else }}
|
||
document.getElementById('normal-submit-btn').click();
|
||
{{ end }}
|
||
});
|
||
}
|
||
|
||
{{ if ne .CaptchaType "math" }}
|
||
function onSubmit(token) {
|
||
var btn = document.querySelector('.g-recaptcha');
|
||
var originalText = btn.innerText;
|
||
|
||
if (notifyCheckbox && notifyCheckbox.checked && emailInput && emailInput.value.trim() === '') {
|
||
alert(CAPTCHA_I18N.needEmail);
|
||
grecaptcha.reset();
|
||
return;
|
||
}
|
||
|
||
btn.disabled = true;
|
||
btn.innerText = '{{t .LangCode "common.verifying"}}';
|
||
|
||
var needPassword = {{if .PageUser.AskPassword}}true{{else}}false{{end}};
|
||
if (needPassword) {
|
||
UIkit.modal.prompt(CAPTCHA_I18N.needPassword, '', 'password')
|
||
.then(function(password) {
|
||
if (password) {
|
||
document.getElementById('ask_password_input').value = password;
|
||
document.getElementById('form').submit();
|
||
} else {
|
||
btn.disabled = false;
|
||
btn.innerText = originalText;
|
||
grecaptcha.reset();
|
||
}
|
||
});
|
||
} else {
|
||
document.getElementById('form').submit();
|
||
}
|
||
}
|
||
{{ end }}
|
||
</script>
|
||
{{ end }}
|