This commit is contained in:
tamakyi
2026-09-08 10:10:44 +08:00
commit 2ed27dd55e
288 changed files with 67061 additions and 0 deletions
+135
View File
@@ -0,0 +1,135 @@
<!--
功能控制表单部分模板:提问 / 私聊 / 投票 三个功能面板共用。
渲染时「点」为单个功能视图(Key / Control / State / Mode / CSRFTokenHTML ...),
因此所有文案与令牌都可自取,无需访问根数据。
收集模式(mode)三选一,保存时互斥、后端同步清理未选模式的字段:
- always 长期开放:不限时间,保持开放直到手动关闭(仍受收集上限约束);
- window 定时收集:一次性开放 / 关闭时间窗口;
- recurring 循环定时:每周固定开放日 + 每日时间段循环。
私聊(chat)不提供收集上限:配额字段与重置计数按钮按 Key 条件隐藏,后端强制清零。
「保存」与「重置计数」同行:重置按钮通过 HTML5 form 属性指向独立的 reset 表单。
-->
<div class="feat-card">
<div class="feat-card-head">
<h3 class="uk-margin-remove">{{ .NameText }}</h3>
<span class="feat-status {{ if .State.Open }}is-open{{ else }}is-closed{{ end }}">{{ .StatusText }}</span>
</div>
<span class="feat-hint">{{t .LangCode (print "admin.featureHint." .Key)}}</span>
<form method="post" action="/user/profile/features/update">
{{ .CSRFTokenHTML }}
<input type="hidden" name="feature" value="{{ .Key }}">
<div class="uk-margin-small">
<label>
<input name="enabled" class="uk-checkbox" type="checkbox" {{ if .Control.Enabled }}checked{{ end }}>
<span class="uk-text-small">{{t .LangCode "admin.featureEnabled"}}</span>
</label>
</div>
<div class="uk-margin-small feat-field">
<span class="feat-field-label">{{t .LangCode "admin.featureMode"}}</span>
<div class="feat-modes">
<label class="feat-day{{ if eq .Mode "always" }} is-on{{ end }}">
<input name="mode" type="radio" value="always" {{ if eq .Mode "always" }}checked{{ end }}>
<span>{{t .LangCode "admin.featureModeAlways"}}</span>
</label>
<label class="feat-day{{ if eq .Mode "window" }} is-on{{ end }}">
<input name="mode" type="radio" value="window" {{ if eq .Mode "window" }}checked{{ end }}>
<span>{{t .LangCode "admin.featureModeWindow"}}</span>
</label>
<label class="feat-day{{ if eq .Mode "recurring" }} is-on{{ end }}">
<input name="mode" type="radio" value="recurring" {{ if eq .Mode "recurring" }}checked{{ end }}>
<span>{{t .LangCode "admin.featureModeRecurring"}}</span>
</label>
</div>
<span class="feat-hint">{{t .LangCode "admin.featureModeHint"}}</span>
</div>
<div class="uk-margin-small feat-field feat-window-fields">
<label>
<span>{{t .LangCode "admin.featureOpenAt"}}</span>
<input name="open_at" class="uk-input" type="datetime-local" value="{{ .OpenAtStr }}">
</label>
<label>
<span>{{t .LangCode "admin.featureCloseAt"}}</span>
<input name="close_at" class="uk-input" type="datetime-local" value="{{ .CloseAtStr }}">
</label>
<span class="feat-hint">{{t .LangCode "admin.featureTimeHint"}}</span>
</div>
<div class="uk-margin-small feat-field feat-recurring-fields">
<label>
<span>{{t .LangCode "admin.featureSchedule"}}</span>
</label>
<div class="feat-schedule">
<div class="feat-schedule-days">
{{ range $i := loop 7 }}
<label class="feat-day{{ if hasDay $.ScheduleDays (sub $i 1) }} is-on{{ end }}">
<input name="schedule_days" class="uk-checkbox" type="checkbox" value="{{ sub $i 1 }}" {{ if hasDay $.ScheduleDays (sub $i 1) }}checked{{ end }}>
<span>{{t $.LangCode (printf "weekday.%d" (sub $i 1))}}</span>
</label>
{{ end }}
</div>
<div class="feat-schedule-times">
<label>
<span>{{t .LangCode "admin.featureScheduleStart"}}</span>
<input name="schedule_start" class="uk-input" type="time" value="{{ .ScheduleStart }}">
</label>
<label>
<span>{{t .LangCode "admin.featureScheduleEnd"}}</span>
<input name="schedule_end" class="uk-input" type="time" value="{{ .ScheduleEnd }}">
</label>
</div>
<span class="feat-hint">{{t .LangCode "admin.featureScheduleHint"}}</span>
</div>
</div>
{{ if ne .Key "chat" }}
<div class="uk-margin-small feat-field">
<label>
<span>{{t .LangCode "admin.featureQuotaLimit"}}</span>
<input name="quota_limit" class="uk-input" type="number" min="0" max="1000000" value="{{ .Control.QuotaLimit }}">
</label>
<span class="feat-hint">{{t .LangCode "admin.featureQuotaHint"}}</span>
</div>
<div class="feat-quota-line">
{{t .LangCode "admin.featureQuotaCount"}}:{{ .State.QuotaCount }}
{{ if .Control.QuotaLimit }}/ {{ .Control.QuotaLimit }}{{ else }}<span class="uk-text-muted">({{t .LangCode "admin.featureQuotaUnlimited"}})</span>{{ end }}
{{ if .Control.QuotaResetAt }}<br>{{t .LangCode "admin.featureQuotaResetAt"}} {{ .QuotaResetAtStr }}{{ end }}
</div>
{{ end }}
<div class="feat-actions">
{{ if ne .Key "chat" }}
<button type="submit" class="uk-button uk-button-default" form="feat-reset-{{ .Key }}">{{t .LangCode "admin.featureResetQuota"}}</button>
{{ end }}
<button type="submit" class="uk-button uk-button-primary">{{t .LangCode "admin.featureSave"}}</button>
</div>
</form>
{{ if ne .Key "chat" }}
<form id="feat-reset-{{ .Key }}" method="post" action="/user/profile/features/reset-quota">
{{ .CSRFTokenHTML }}
<input type="hidden" name="feature" value="{{ .Key }}">
</form>
{{ end }}
<script>
(function () {
var card = document.currentScript && document.currentScript.closest('.feat-card');
if (!card) return;
var radios = card.querySelectorAll('input[name="mode"]');
var windowFields = card.querySelector('.feat-window-fields');
var recurringFields = card.querySelector('.feat-recurring-fields');
if (!radios.length) return;
function sync() {
var mode = '';
radios.forEach(function (r) {
var pill = r.closest('.feat-day');
if (pill) pill.classList.toggle('is-on', r.checked);
if (r.checked) mode = r.value;
});
if (windowFields) windowFields.classList.toggle('is-hidden', mode !== 'window');
if (recurringFields) recurringFields.classList.toggle('is-hidden', mode !== 'recurring');
}
radios.forEach(function (r) { r.addEventListener('change', sync); });
sync();
})();
</script>
</div>