Files
tamabox/templates/user/vote-results.html
T
tamakyi 27856dd4a2 字号阶梯收尾 + 主色补到 WCAG AA(新增 --tb-on-primary)
一号事项(剩余令牌化):
- 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 全过。
2026-10-21 18:39:08 +08:00

175 lines
8.6 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" .}}
<div class="uk-flex uk-flex-between uk-flex-middle uk-margin-bottom">
<h3 class="uk-margin-remove">{{t .LangCode "vote.resultsTitle"}}:{{ .Vote.Title }}</h3>
<div class="uk-flex uk-flex-middle" style="gap: 8px;">
<a class="uk-button uk-button-default uk-button-small" href="/user/votes/{{ .Vote.ID }}/export">{{t .LangCode "vote.export"}}</a>
<a class="uk-button uk-button-default uk-button-small" href="/user/questions#votes">{{t .LangCode "common.back"}}</a>
</div>
</div>
<div class="uk-card uk-card-default uk-card-small uk-card-body uk-margin-bottom">
<div class="uk-text-large uk-text-bold">{{ .Total }}</div>
<div class="uk-text-meta">{{t .LangCode "vote.resultsTotal"}}</div>
</div>
{{ if eq .Vote.Type "form" }}
<!-- 表单收集(混合问卷)字段级聚合统计 -->
{{ if .Results.FormFieldStats }}
<h4>{{t .LangCode "vote.resultsOptions"}}</h4>
<div class="uk-card uk-card-default uk-card-small uk-card-body uk-margin-bottom">
{{ range $si, $stat := .Results.FormFieldStats }}
<div class="uk-margin">
<div class="uk-flex uk-flex-middle uk-margin-small-bottom" style="gap: 8px;">
<span class="tb-chip">{{ add $si 1 }}</span>
<span class="tb-chip">{{ fieldTypeLabel $.LangCode $stat.Field.FieldType }}</span>
<span class="uk-text-bold">{{ $stat.Field.Label }}</span>
<span class="uk-text-meta uk-text-small">{{t $.LangCode "vote.resultsAnswered"}}: {{ $stat.Total }}</span>
</div>
{{ if or (eq $stat.Field.FieldType "radio") (eq $stat.Field.FieldType "select") (eq $stat.Field.FieldType "checkbox") }}
{{ $denom := $stat.Total }}
{{ if eq $stat.Field.FieldType "checkbox" }}{{ $denom = $stat.TotalSelections }}{{ end }}
{{ range $o := $stat.OptionStats }}
<div class="uk-margin-small-top">
<div class="uk-flex uk-flex-between">
<span>{{ $o.Option.Content }}</span>
<span class="uk-text-meta">{{ $o.Count }} {{t $.LangCode "vote.resultsVotes"}}({{ if eq $denom 0 }}0{{ else }}{{ percentOf $o.Count $denom }}{{ end }}%)</span>
</div>
<progress class="uk-progress uk-margin-small-top" value="{{ if eq $denom 0 }}0{{ else }}{{ percentOf $o.Count $denom }}{{ end }}" max="100"></progress>
</div>
{{ end }}
{{ if $stat.CustomStats }}
<div class="uk-margin-small-top">
<div class="uk-text-meta">{{t $.LangCode "vote.page.customStats"}}:</div>
{{ range $c := $stat.CustomStats }}
<div class="uk-flex uk-flex-between uk-margin-small-top">
<span>{{ $c.Text }}</span>
<span class="uk-text-meta">{{ $c.Count }} {{t $.LangCode "vote.resultsVotes"}}</span>
</div>
{{ end }}
</div>
{{ end }}
{{ else if eq $stat.Field.FieldType "rating" }}
<div class="uk-text-meta uk-margin-small-top">{{t $.LangCode "vote.page.avgRating"}}: {{ printf "%.1f" $stat.AvgRating }}({{ $stat.Total }} {{t $.LangCode "vote.resultsVotes"}})</div>
{{ range $r := $stat.RatingCounts }}
<div class="uk-flex uk-flex-between uk-margin-small-top">
<span>{{ $r.Score }} {{t $.LangCode "vote.page.ratingUnit"}}</span>
<span class="uk-text-meta">{{ $r.Count }} {{t $.LangCode "vote.resultsVotes"}}({{ if eq $stat.Total 0 }}0{{ else }}{{ percentOf $r.Count $stat.Total }}{{ end }}%)</span>
</div>
<progress class="uk-progress uk-margin-small-top" value="{{ if eq $stat.Total 0 }}0{{ else }}{{ percentOf $r.Count $stat.Total }}{{ end }}" max="100"></progress>
{{ end }}
{{ else if eq $stat.Field.FieldType "rank" }}
<div class="uk-text-meta uk-margin-small-top">{{t $.LangCode "vote.page.avgRank"}}: {{ printf "%.2f" $stat.AvgRank }}({{ $stat.Total }} {{t $.LangCode "vote.resultsVotes"}})</div>
{{ range $o := $stat.OptionStats }}
<div class="uk-flex uk-flex-between uk-margin-small-top">
<span>{{ $o.Option.Content }}</span>
<span class="uk-text-meta">{{t $.LangCode "vote.page.avgRank"}}: {{ if gt $o.Count 0 }}{{ printf "%.2f" $o.AvgRank }}{{ else }}-{{ end }} · {{t $.LangCode "vote.page.rankedTimes"}}: {{ $o.Count }}</span>
</div>
{{ end }}
{{ else if eq $stat.Field.FieldType "number" }}
<div class="uk-text-meta uk-margin-small-top">{{t $.LangCode "vote.page.avgNumber"}}: {{ printf "%.1f" $stat.AvgNumber }}({{ $stat.Total }} {{t $.LangCode "vote.resultsVotes"}})</div>
{{ else }}
<div class="uk-text-meta uk-margin-small-top">{{t $.LangCode "vote.page.fieldFilled"}}: {{ $stat.TextCount }}</div>
{{ $replies := index $.TextReplies $stat.Field.Label }}
{{ if $replies }}
<div class="uk-margin-small-top">
<div class="uk-text-small" style="cursor:pointer; user-select:none; color:var(--tb-primary);" onclick="toggleFieldReplies('{{ $stat.Field.ID }}')" title="{{t $.LangCode "vote.resultsToggleHint"}}">
<span id="caret-{{ $stat.Field.ID }}" style="display:inline-block; transition:transform .15s; transform:rotate(-90deg);">▾</span>
{{t $.LangCode "vote.resultsViewTexts"}}({{ len $replies }})
</div>
<div id="replies-{{ $stat.Field.ID }}" hidden>
<ul class="uk-list uk-list-divider uk-margin-small-top">
{{ range $r := $replies }}
<li>
<div class="uk-text-meta uk-text-small">{{ $r.CreatedAt.Format "2006-01-02 15:04" }}</div>
<div style="white-space:pre-wrap;">{{ $r.Text }}</div>
</li>
{{ end }}
</ul>
</div>
</div>
{{ end }}
{{ end }}
</div>
{{ end }}
</div>
{{ end }}
<!-- 文本类字段的逐条提交内容已内联在各题目下方(默认收起),此处无需额外区块 -->
{{ else if .Results }}
<!-- 选项 / 评分 / 排序统计 -->
<h4>{{t .LangCode "vote.resultsOptions"}}</h4>
<div class="uk-card uk-card-default uk-card-small uk-card-body">
{{ if or (eq .Results.Type "single") (eq .Results.Type "multiple") }}
{{ range .Results.Options }}
<div class="uk-margin">
<div class="uk-flex uk-flex-between">
<span>{{ .Option.Content }}</span>
<span class="uk-text-meta">{{ .Count }} {{t $.LangCode "vote.resultsVotes"}}({{ .Percent }}%)</span>
</div>
<progress class="uk-progress uk-margin-small-top" value="{{ .Percent }}" max="100"></progress>
</div>
{{ end }}
{{ else if eq .Results.Type "rating" }}
<div class="uk-text-meta uk-margin-small-bottom">{{t .LangCode "vote.page.avgRating"}}: {{ printf "%.1f" .Results.AvgRating }}</div>
{{ range .Results.Ratings }}
<div class="uk-margin">
<div class="uk-flex uk-flex-between">
<span>{{ .Score }} {{t $.LangCode "vote.page.ratingUnit"}}</span>
<span class="uk-text-meta">{{ .Count }} {{t $.LangCode "vote.resultsVotes"}}</span>
</div>
<progress class="uk-progress uk-margin-small-top" value="{{ if eq $.Total 0 }}0{{ else }}{{ percentOf .Count $.Total }}{{ end }}" max="100"></progress>
</div>
{{ end }}
{{ else if eq .Results.Type "rank" }}
<table class="uk-table uk-table-small uk-table-divider">
<thead><tr><th>{{t .LangCode "vote.page.optionColumn"}}</th><th>{{t .LangCode "vote.page.avgRank"}}</th></tr></thead>
<tbody>
{{ range .Results.Rankings }}
<tr><td>{{ .Option.Content }}</td><td>{{ if gt .AvgRank 0.0 }}{{ printf "%.2f" .AvgRank }}{{ else }}-{{ end }}</td></tr>
{{ end }}
</tbody>
</table>
{{ end }}
{{ if .Results.CustomStats }}
<div class="uk-margin-top">
<div class="uk-text-bold">{{t .LangCode "vote.page.customStats"}}</div>
{{ range .Results.CustomStats }}
<div class="uk-flex uk-flex-between uk-margin-small-top">
<span>{{ .Text }}</span>
<span class="uk-text-meta">{{ .Count }} {{t $.LangCode "vote.resultsVotes"}}</span>
</div>
{{ end }}
</div>
{{ end }}
</div>
{{ else }}
<div class="uk-alert uk-alert-primary">{{t .LangCode "vote.resultsEmpty"}}</div>
{{ end }}
<script>
// 文本类字段:点击"查看文本提交(N)"按该字段 ID 展开/收起逐条内容
function toggleFieldReplies(id) {
var box = document.getElementById('replies-' + id);
var caret = document.getElementById('caret-' + id);
if (!box) { return; }
if (box.hasAttribute('hidden')) {
box.removeAttribute('hidden');
if (caret) { caret.style.transform = 'rotate(0deg)'; }
} else {
box.setAttribute('hidden', '');
if (caret) { caret.style.transform = 'rotate(-90deg)'; }
}
}
</script>
{{template "base/footer" .}}