一号事项(剩余令牌化):
- 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 全过。
175 lines
8.6 KiB
HTML
175 lines
8.6 KiB
HTML
{{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" .}}
|