首页底部那张「站点统计」卡片下线,完整数据移到公开只读页 /stats: 今日实时数、概览六格、近 30 天折线与 12 个月柱状、三个占比环、 7×24 时段热力图、提问字数与回答耗时分布。 - 聚合在 internal/db/analytics.go(SUM(CASE WHEN),方言差异收敛在 analyticsDialectFor) - 结果按自然日缓存,跨日重算一次后全站共用;重算失败降级返回上一份快照 - 图表零前端依赖:服务端算坐标的 SVG + CSS grid,禁用 JS 页面仍完整 - 环比取「上月 vs 上上月」:本月未过完,否则每月 1 号会得出 -95% - 入口只在页脚「社区规则」与「找站长」之间;route.Home 不再计算统计 - 补 questions(created_at) 索引;启动后异步预热当天快照 - 测试:analytics 连真库验证软删除不计入、分桶下标、DOW 对齐
562 lines
17 KiB
HTML
562 lines
17 KiB
HTML
{{template "base/header" .}}
|
||
<style>
|
||
/* 全站数据页:只用令牌与 CSS 画图(折线 SVG、柱状/分布条用百分比高度、环形用 pathLength、
|
||
热力图用 grid + 主色透明度)。不引图表库,页面在禁用 JS 时也完整。 */
|
||
.tb-dash {
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.tb-dash-head {
|
||
padding: 4px 2px 14px;
|
||
}
|
||
.tb-dash-head h2 {
|
||
margin: 0;
|
||
font-size: var(--tb-fs-xl);
|
||
font-weight: 600;
|
||
color: var(--tb-text);
|
||
}
|
||
.tb-dash-head p {
|
||
margin: 8px 0 0;
|
||
font-size: var(--tb-fs-md);
|
||
line-height: 1.7;
|
||
color: var(--tb-text-muted);
|
||
}
|
||
|
||
/* 今日条 */
|
||
.tb-today {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: baseline;
|
||
gap: 6px 18px;
|
||
padding: 12px 16px;
|
||
margin-bottom: 12px;
|
||
background: var(--tb-primary-weak);
|
||
border-radius: var(--tb-radius-sm);
|
||
font-size: var(--tb-fs-md);
|
||
color: var(--tb-text-secondary);
|
||
}
|
||
.tb-today b {
|
||
font-size: var(--tb-fs-lg);
|
||
color: var(--tb-primary);
|
||
font-weight: 700;
|
||
}
|
||
.tb-today .tb-today-tag {
|
||
font-size: var(--tb-fs-xs);
|
||
color: var(--tb-text-muted);
|
||
}
|
||
|
||
/* 卡片 */
|
||
.tb-card {
|
||
background: var(--tb-surface);
|
||
border: 1px solid var(--tb-border);
|
||
border-radius: var(--tb-radius);
|
||
padding: 18px 18px 20px;
|
||
margin-bottom: 12px;
|
||
}
|
||
.tb-card-title {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: baseline;
|
||
gap: 8px;
|
||
margin: 0 0 4px;
|
||
font-size: var(--tb-fs-lg);
|
||
font-weight: 600;
|
||
color: var(--tb-text);
|
||
}
|
||
.tb-card-note {
|
||
margin: 0 0 16px;
|
||
font-size: var(--tb-fs-xs);
|
||
line-height: 1.6;
|
||
color: var(--tb-text-muted);
|
||
}
|
||
|
||
/* KPI 网格 */
|
||
.tb-kpis {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 8px;
|
||
margin-bottom: 12px;
|
||
}
|
||
@media (min-width: 560px) {
|
||
.tb-kpis { grid-template-columns: repeat(3, 1fr); }
|
||
}
|
||
.tb-kpi {
|
||
padding: 14px 12px;
|
||
background: var(--tb-surface);
|
||
border: 1px solid var(--tb-border);
|
||
border-radius: var(--tb-radius-sm);
|
||
}
|
||
.tb-kpi-num {
|
||
display: block;
|
||
font-size: 1.375rem;
|
||
line-height: 1.2;
|
||
font-weight: 700;
|
||
color: var(--tb-text);
|
||
}
|
||
.tb-kpi-num.tb-kpi-accent { color: var(--tb-primary); }
|
||
.tb-kpi-lbl {
|
||
display: block;
|
||
margin-top: 4px;
|
||
font-size: var(--tb-fs-xs);
|
||
color: var(--tb-text-muted);
|
||
}
|
||
|
||
/* 洞察 */
|
||
.tb-insights {
|
||
margin: 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
}
|
||
.tb-insights li {
|
||
position: relative;
|
||
padding: 6px 0 6px 18px;
|
||
font-size: var(--tb-fs-md);
|
||
line-height: 1.7;
|
||
color: var(--tb-text-secondary);
|
||
}
|
||
.tb-insights li::before {
|
||
content: "";
|
||
position: absolute;
|
||
left: 4px;
|
||
top: 14px;
|
||
width: 5px;
|
||
height: 5px;
|
||
border-radius: 50%;
|
||
background: var(--tb-primary);
|
||
opacity: .55;
|
||
}
|
||
|
||
/* 折线图 */
|
||
.tb-line-wrap { margin-top: 4px; }
|
||
.tb-line {
|
||
display: block;
|
||
width: 100%;
|
||
height: 148px;
|
||
}
|
||
.tb-line .tb-line-area {
|
||
/* 比 --tb-primary-weak(0.10)略深一档:折线下方的面积要能看出「有」,但不能抢走线条本身 */
|
||
fill: var(--tb-primary);
|
||
fill-opacity: .14;
|
||
stroke: none;
|
||
}
|
||
.tb-line .tb-line-path {
|
||
fill: none;
|
||
stroke: var(--tb-primary);
|
||
stroke-width: 2;
|
||
stroke-linejoin: round;
|
||
stroke-linecap: round;
|
||
/* viewBox 与显示宽度不一致,必须锁定描边像素,否则窄屏上线会被压细或拉粗 */
|
||
vector-effect: non-scaling-stroke;
|
||
}
|
||
.tb-axis {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-top: 6px;
|
||
font-size: var(--tb-fs-xs);
|
||
color: var(--tb-text-muted);
|
||
}
|
||
|
||
/* 月度柱状 */
|
||
.tb-bars {
|
||
display: grid;
|
||
grid-template-columns: repeat(12, 1fr);
|
||
gap: 5px;
|
||
align-items: end;
|
||
}
|
||
.tb-bar-col {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
height: 100%;
|
||
}
|
||
.tb-bar-track {
|
||
display: flex;
|
||
align-items: flex-end;
|
||
width: 100%;
|
||
height: 108px;
|
||
}
|
||
.tb-bar-track i {
|
||
display: block;
|
||
width: 100%;
|
||
min-height: 2px;
|
||
border-radius: 3px 3px 0 0;
|
||
background: var(--tb-primary);
|
||
opacity: .82;
|
||
}
|
||
.tb-bar-col:hover .tb-bar-track i { opacity: 1; }
|
||
.tb-bar-lbl {
|
||
margin-top: 6px;
|
||
font-size: 0.6875rem;
|
||
color: var(--tb-text-muted);
|
||
}
|
||
.tb-bar-col.is-current .tb-bar-lbl { color: var(--tb-primary); font-weight: 600; }
|
||
|
||
/* 构成环 */
|
||
.tb-donuts {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 10px;
|
||
}
|
||
.tb-donut {
|
||
text-align: center;
|
||
}
|
||
.tb-donut-ring {
|
||
position: relative;
|
||
width: 76px;
|
||
margin: 0 auto;
|
||
}
|
||
.tb-donut-ring svg {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
}
|
||
.tb-donut-ring .tb-ring-track {
|
||
fill: none;
|
||
stroke: var(--tb-surface-2);
|
||
stroke-width: 6;
|
||
}
|
||
.tb-donut-ring .tb-ring-val {
|
||
fill: none;
|
||
stroke: var(--tb-primary);
|
||
stroke-width: 6;
|
||
stroke-linecap: round;
|
||
}
|
||
.tb-donut-pct {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: var(--tb-fs-md);
|
||
font-weight: 700;
|
||
color: var(--tb-text);
|
||
}
|
||
.tb-donut-cap {
|
||
margin-top: 8px;
|
||
font-size: var(--tb-fs-xs);
|
||
line-height: 1.5;
|
||
color: var(--tb-text-muted);
|
||
}
|
||
.tb-donut-cap b {
|
||
display: block;
|
||
margin-top: 2px;
|
||
color: var(--tb-text-secondary);
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* 热力图 */
|
||
.tb-heat {
|
||
/* 24 列在 750px 容器里会把方块撑到 26px 见方,整块图比上面的折线还高。
|
||
限宽后每格约 20px,既看得清又不至于把页面拉得太长。 */
|
||
max-width: 620px;
|
||
margin: 0 auto;
|
||
}
|
||
.tb-heat-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-bottom: 3px;
|
||
}
|
||
.tb-heat-lbl {
|
||
flex: 0 0 26px;
|
||
font-size: 0.6875rem;
|
||
color: var(--tb-text-muted);
|
||
text-align: center;
|
||
}
|
||
.tb-heat-cells {
|
||
display: grid;
|
||
grid-template-columns: repeat(24, 1fr);
|
||
gap: 2px;
|
||
flex: 1 1 auto;
|
||
}
|
||
.tb-heat-cells i {
|
||
display: block;
|
||
aspect-ratio: 1 / 1;
|
||
border-radius: 2px;
|
||
background: var(--tb-surface-2);
|
||
}
|
||
.tb-heat-cells i.lv1 { background: var(--tb-primary); opacity: .18; }
|
||
.tb-heat-cells i.lv2 { background: var(--tb-primary); opacity: .38; }
|
||
.tb-heat-cells i.lv3 { background: var(--tb-primary); opacity: .62; }
|
||
.tb-heat-cells i.lv4 { background: var(--tb-primary); opacity: 1; }
|
||
.tb-heat-hours {
|
||
display: grid;
|
||
grid-template-columns: repeat(24, 1fr);
|
||
gap: 2px;
|
||
margin: 6px 0 0 34px;
|
||
font-size: 0.625rem;
|
||
color: var(--tb-text-muted);
|
||
}
|
||
.tb-heat-hours span { text-align: center; }
|
||
.tb-heat-legend {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
gap: 4px;
|
||
margin-top: 10px;
|
||
font-size: var(--tb-fs-xs);
|
||
color: var(--tb-text-muted);
|
||
}
|
||
.tb-heat-legend i {
|
||
display: block;
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 2px;
|
||
background: var(--tb-surface-2);
|
||
}
|
||
.tb-heat-legend i.lv1 { background: var(--tb-primary); opacity: .18; }
|
||
.tb-heat-legend i.lv2 { background: var(--tb-primary); opacity: .38; }
|
||
.tb-heat-legend i.lv3 { background: var(--tb-primary); opacity: .62; }
|
||
.tb-heat-legend i.lv4 { background: var(--tb-primary); opacity: 1; }
|
||
|
||
/* 分布条 */
|
||
.tb-dist + .tb-dist { margin-top: 20px; }
|
||
.tb-dist-title {
|
||
margin: 0 0 10px;
|
||
font-size: var(--tb-fs-md);
|
||
font-weight: 600;
|
||
color: var(--tb-text-secondary);
|
||
}
|
||
.tb-dist-row {
|
||
/* 标签列用 em 而不是 px:英文桶名("100–199 chars")比中文长一截,
|
||
写死 84px 会让它折成两行,把整组分布条的高度带偏。 */
|
||
display: grid;
|
||
grid-template-columns: 7.5em 1fr 3.6em;
|
||
gap: 10px;
|
||
align-items: center;
|
||
margin-bottom: 6px;
|
||
font-size: var(--tb-fs-xs);
|
||
color: var(--tb-text-muted);
|
||
}
|
||
.tb-dist-track {
|
||
height: 8px;
|
||
border-radius: 4px;
|
||
background: var(--tb-surface-2);
|
||
overflow: hidden;
|
||
}
|
||
.tb-dist-track i {
|
||
display: block;
|
||
height: 100%;
|
||
min-width: 2px;
|
||
border-radius: 4px;
|
||
background: var(--tb-primary);
|
||
opacity: .78;
|
||
}
|
||
.tb-dist-num { text-align: right; }
|
||
|
||
.tb-dash-foot {
|
||
margin: 4px 2px 0;
|
||
font-size: var(--tb-fs-xs);
|
||
line-height: 1.8;
|
||
color: var(--tb-text-muted);
|
||
}
|
||
.tb-dash-empty {
|
||
padding: 40px 16px;
|
||
text-align: center;
|
||
font-size: var(--tb-fs-md);
|
||
color: var(--tb-text-muted);
|
||
}
|
||
</style>
|
||
|
||
{{ with .Overview }}
|
||
{{ $ov := . }}
|
||
<div class="tb-dash">
|
||
|
||
<div class="tb-dash-head">
|
||
<h2>{{t $.LangCode "stats.title"}}</h2>
|
||
<p>{{t $.LangCode "stats.lead"}}</p>
|
||
</div>
|
||
|
||
<!-- 今日实时:不参与「每天一次」的缓存,每次打开都是当下数字 -->
|
||
<div class="tb-today">
|
||
<span>{{t $.LangCode "stats.today.questions"}} <b>{{NumInt .TodayQuestions}}</b></span>
|
||
<span>{{t $.LangCode "stats.today.answers"}} <b>{{NumInt .TodayAnswers}}</b></span>
|
||
<span class="tb-today-tag">{{t $.LangCode "stats.today.tag"}}</span>
|
||
</div>
|
||
|
||
<!-- 累计概览 -->
|
||
<div class="tb-kpis">
|
||
<div class="tb-kpi">
|
||
<span class="tb-kpi-num">{{NumInt .RegisteredUsers}}</span>
|
||
<span class="tb-kpi-lbl">{{t $.LangCode "stats.kpi.users"}}</span>
|
||
</div>
|
||
<div class="tb-kpi">
|
||
<span class="tb-kpi-num">{{NumInt .TotalQuestions}}</span>
|
||
<span class="tb-kpi-lbl">{{t $.LangCode "stats.kpi.questions"}}</span>
|
||
</div>
|
||
<div class="tb-kpi">
|
||
<span class="tb-kpi-num">{{NumInt .Answered}}</span>
|
||
<span class="tb-kpi-lbl">{{t $.LangCode "stats.kpi.answered"}}</span>
|
||
</div>
|
||
<div class="tb-kpi">
|
||
<span class="tb-kpi-num tb-kpi-accent">{{PctText .Answered .TotalQuestions}}%</span>
|
||
<span class="tb-kpi-lbl">{{t $.LangCode "stats.kpi.answerRate"}}</span>
|
||
</div>
|
||
<div class="tb-kpi">
|
||
<span class="tb-kpi-num">{{NumInt .ActiveBoxes}}</span>
|
||
<span class="tb-kpi-lbl">{{t $.LangCode "stats.kpi.activeBoxes"}}</span>
|
||
</div>
|
||
<div class="tb-kpi">
|
||
<span class="tb-kpi-num">{{NumInt .TotalMessages}}</span>
|
||
<span class="tb-kpi-lbl">{{t $.LangCode "stats.kpi.messages"}}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 文字结论 -->
|
||
{{ if .Insights }}
|
||
<div class="tb-card">
|
||
<h3 class="tb-card-title">{{t $.LangCode "stats.insight.title"}}</h3>
|
||
<p class="tb-card-note">{{t $.LangCode "stats.insight.note"}}</p>
|
||
<ul class="tb-insights">
|
||
{{ range .Insights }}<li>{{ . }}</li>{{ end }}
|
||
</ul>
|
||
</div>
|
||
{{ end }}
|
||
|
||
<!-- 近 30 天趋势 -->
|
||
<div class="tb-card">
|
||
<h3 class="tb-card-title">{{t $.LangCode "stats.trend.dailyTitle"}}</h3>
|
||
<p class="tb-card-note">{{t $.LangCode "stats.trend.dailyNote"}}</p>
|
||
<div class="tb-line-wrap">
|
||
<svg class="tb-line" viewBox="0 0 640 148" preserveAspectRatio="none" role="img"
|
||
aria-label="{{t $.LangCode "stats.trend.dailyTitle"}}">
|
||
<path class="tb-line-area" d="{{ AreaPath .DailyValues 640 148 12 10 }}"></path>
|
||
<polyline class="tb-line-path" points="{{ Polyline .DailyValues 640 148 12 10 }}"></polyline>
|
||
</svg>
|
||
{{ if eq (len .Daily) 30 }}
|
||
<div class="tb-axis">
|
||
<span>{{ (index .Daily 0).Label }}</span>
|
||
<span>{{ (index .Daily 15).Label }}</span>
|
||
<span>{{ (index .Daily 29).Label }}</span>
|
||
</div>
|
||
{{ end }}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 近 12 个月 -->
|
||
<div class="tb-card">
|
||
<h3 class="tb-card-title">{{t $.LangCode "stats.trend.monthlyTitle"}}</h3>
|
||
<p class="tb-card-note">{{t $.LangCode "stats.trend.monthlyNote"}}</p>
|
||
<div class="tb-bars">
|
||
{{ range $i, $p := .Monthly }}
|
||
<div class="tb-bar-col{{ if $p.Current }} is-current{{ end }}" title="{{ $p.Month }} · {{ NumInt $p.N }}">
|
||
<div class="tb-bar-track"><i style="height:{{ printf "%.1f" (index $ov.MonthlyPcts $i) }}%"></i></div>
|
||
<span class="tb-bar-lbl">{{ $p.Label }}</span>
|
||
</div>
|
||
{{ end }}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 构成 -->
|
||
<div class="tb-card">
|
||
<h3 class="tb-card-title">{{t $.LangCode "stats.mix.title"}}</h3>
|
||
<p class="tb-card-note">{{t $.LangCode "stats.mix.note"}}</p>
|
||
<div class="tb-donuts">
|
||
<div class="tb-donut">
|
||
<div class="tb-donut-ring">
|
||
<svg viewBox="0 0 42 42" role="img" aria-label="{{t $.LangCode "stats.mix.public"}}">
|
||
<circle class="tb-ring-track" cx="21" cy="21" r="15.915" pathLength="100"></circle>
|
||
<circle class="tb-ring-val" cx="21" cy="21" r="15.915" pathLength="100"
|
||
stroke-dasharray="{{ PctText .Public .TotalQuestions }} 100"
|
||
transform="rotate(-90 21 21)"></circle>
|
||
</svg>
|
||
<span class="tb-donut-pct">{{ PctText .Public .TotalQuestions }}%</span>
|
||
</div>
|
||
<div class="tb-donut-cap">{{t $.LangCode "stats.mix.public"}}<b>{{ NumInt .Public }}</b></div>
|
||
</div>
|
||
<div class="tb-donut">
|
||
<div class="tb-donut-ring">
|
||
<svg viewBox="0 0 42 42" role="img" aria-label="{{t $.LangCode "stats.mix.answered"}}">
|
||
<circle class="tb-ring-track" cx="21" cy="21" r="15.915" pathLength="100"></circle>
|
||
<circle class="tb-ring-val" cx="21" cy="21" r="15.915" pathLength="100"
|
||
stroke-dasharray="{{ PctText .Answered .TotalQuestions }} 100"
|
||
transform="rotate(-90 21 21)"></circle>
|
||
</svg>
|
||
<span class="tb-donut-pct">{{ PctText .Answered .TotalQuestions }}%</span>
|
||
</div>
|
||
<div class="tb-donut-cap">{{t $.LangCode "stats.mix.answered"}}<b>{{ NumInt .Answered }}</b></div>
|
||
</div>
|
||
<div class="tb-donut">
|
||
<div class="tb-donut-ring">
|
||
<svg viewBox="0 0 42 42" role="img" aria-label="{{t $.LangCode "stats.mix.login"}}">
|
||
<circle class="tb-ring-track" cx="21" cy="21" r="15.915" pathLength="100"></circle>
|
||
<circle class="tb-ring-val" cx="21" cy="21" r="15.915" pathLength="100"
|
||
stroke-dasharray="{{ PctText .LoggedIn .TotalQuestions }} 100"
|
||
transform="rotate(-90 21 21)"></circle>
|
||
</svg>
|
||
<span class="tb-donut-pct">{{ PctText .LoggedIn .TotalQuestions }}%</span>
|
||
</div>
|
||
<div class="tb-donut-cap">{{t $.LangCode "stats.mix.login"}}<b>{{ NumInt .LoggedIn }}</b></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 时段热力图 -->
|
||
<div class="tb-card">
|
||
<h3 class="tb-card-title">{{t $.LangCode "stats.heat.title"}}</h3>
|
||
<p class="tb-card-note">{{t $.LangCode "stats.heat.note" .HeatmapDays}}</p>
|
||
<div class="tb-heat">
|
||
{{ range $i, $row := .HeatmapRows }}
|
||
<div class="tb-heat-row">
|
||
<span class="tb-heat-lbl">{{ index $ov.WeekdayLabels $i }}</span>
|
||
<div class="tb-heat-cells">
|
||
{{ range $row }}<i class="lv{{ HeatLevel . $ov.HeatmapMax }}" title="{{ . }}"></i>{{ end }}
|
||
</div>
|
||
</div>
|
||
{{ end }}
|
||
<div class="tb-heat-hours">
|
||
<span>0</span><span></span><span></span><span></span><span></span><span></span><span>6</span>
|
||
<span></span><span></span><span></span><span></span><span></span><span>12</span>
|
||
<span></span><span></span><span></span><span></span><span></span><span>18</span>
|
||
<span></span><span></span><span></span><span></span><span>23</span>
|
||
</div>
|
||
<div class="tb-heat-legend">
|
||
<span>{{t $.LangCode "stats.heat.less"}}</span>
|
||
<i></i><i class="lv1"></i><i class="lv2"></i><i class="lv3"></i><i class="lv4"></i>
|
||
<span>{{t $.LangCode "stats.heat.more"}}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 内容与节奏 -->
|
||
<div class="tb-card">
|
||
<h3 class="tb-card-title">{{t $.LangCode "stats.content.title"}}</h3>
|
||
<p class="tb-card-note">{{t $.LangCode "stats.content.note"}}</p>
|
||
|
||
<div class="tb-dist">
|
||
<h4 class="tb-dist-title">{{t $.LangCode "stats.content.lengthTitle"}}</h4>
|
||
{{ range $i, $n := .LengthBins }}
|
||
<div class="tb-dist-row">
|
||
<span>{{ index $ov.LengthLabels $i }}</span>
|
||
<div class="tb-dist-track"><i style="width:{{ printf "%.1f" (index $ov.LengthPcts $i) }}%"></i></div>
|
||
<span class="tb-dist-num">{{ NumInt $n }}</span>
|
||
</div>
|
||
{{ end }}
|
||
</div>
|
||
|
||
<div class="tb-dist">
|
||
<h4 class="tb-dist-title">{{t $.LangCode "stats.content.replyTitle"}}</h4>
|
||
{{ range $i, $n := .ReplyBins }}
|
||
<div class="tb-dist-row">
|
||
<span>{{ index $ov.ReplyLabels $i }}</span>
|
||
<div class="tb-dist-track"><i style="width:{{ printf "%.1f" (index $ov.ReplyPcts $i) }}%"></i></div>
|
||
<span class="tb-dist-num">{{ NumInt $n }}</span>
|
||
</div>
|
||
{{ end }}
|
||
</div>
|
||
</div>
|
||
|
||
<p class="tb-dash-foot">
|
||
{{t $.LangCode "stats.foot.dataDate"}} {{ .DataDate }} · {{t $.LangCode "stats.foot.updated"}} {{ .UpdatedAt }}
|
||
<br>{{t $.LangCode "stats.foot.prevMonth"}}{{ if .LastQuestionAt }}{{ .LastQuestionAt }}{{ else }}-{{ end }}
|
||
· {{t $.LangCode "stats.foot.prevAnswer"}}{{ if .LastAnswerAt }}{{ .LastAnswerAt }}{{ else }}-{{ end }}
|
||
<br>{{t $.LangCode "stats.foot.cacheNote"}}
|
||
<br>{{t $.LangCode "stats.foot.privacy"}}
|
||
</p>
|
||
</div>
|
||
{{ else }}
|
||
<div class="tb-dash-empty">{{t .LangCode "stats.unavailable"}}</div>
|
||
{{ end }}
|
||
|
||
{{template "base/footer" .}}
|