前端改造:设计令牌 + 卡片层级 + 图表可读性 + 类型色收口 + 窄屏导航
先出方案(frontend-plan-2026-10-09.md)再动手,四批按顺序落地。方法上先存基线、再用同一份 探针在改动前后逐值 diff —— 这批里有 6 处问题只有「量」才看得见,看截图全是正常的。 A 设计令牌(app.css 为主) - 字阶收敛到 6 档 --fs-xs/sm/md/lg/xl/2xl = 11/13/15/21/26/34px。原来 app.css 有 19 种字号、 模板里另有 54 处内联字号,光 12~15px 之间就挤了 4 档 —— 层级挤不出来,只能靠加粗硬顶。 - 两个「字外档」不是漏的:--fs-control: 16px(窄屏表单控件必须 ≥16px,否则 iOS 聚焦放大整页, 属功能约束);--glyph-*(✕/箭头/空态图标按控件尺寸定,硬塞进字阶只会把按钮撑坏)。 - 字重收敛到 400/500/600(删掉 650 与 700)。 - 圆角 4 档 --r-*、阴影 3 档 --sh-1/2/3(暗色整套覆盖)、间距 --sp-1..5、 卡片内边距单列 --card-pad(与折叠卡片标题行的负 margin 绑在一起,改一个即改两处)。 - 门禁 TestNoBareFontSizePx:静态扫 app.css 与全部模板,出现 font-size: <数字>px 即红。 B 卡片分主次 - .card--hero / .card(默认)/ .card--quiet 三档。quiet 走「页面底色 + 细边 + 无阴影」—— 第一版拿 --bg-2 当底色,实测它与页面底 #f6f7f8 只差 4/255,看不出边界、像破了个洞。 - 首屏 KPI 从「4×2 等重白盒」改成 .kpi-lead(主数 + 一行上下文)+ 5 个常规指标,8 个数字一个没少。 C 图表说人话 - 月度分布:柱底加基线;峰值柱不透明拉满、柱顶标次数(.pv),副标题里不再重复写「最高 N 次」。 - 一天中的分布:把「0/6/12/18 四个数字」换成 6 小时一段的标尺(深夜/上午/下午/晚上), 用同一套 24 列网格,段边界与柱子严格对齐。 - 首页月度图同样加峰值锚点(给 HomeData 补 MaxMonthly,与统计页同口径 maxOf)。 - 空态没动:查下来已经统一(8 页共用 emptystate、局部走 .empty)。 D1 类型色语义化(结论与计划不同,按实测改了) - 先算对比度:白底上 yellow 1.87 / mint 2.12 / orange 2.20 / green 2.22 / cyan 2.54 / teal 2.57, 6 个色连图形对象 3:1 都到不了;暗底只有 indigo 2.90 不够。 - 所以**没有**把类型色用到 chips / 表格类型列 / KPI 强调 —— 那几处要的是文字色,只会更难读。 这也解释了全站为什么一直只用 8px 小圆点做身份标记。浅色那 6 个色一个没动: 它们是手机端与地图上的同一套身份色,改暗会破坏对应关系。 - 实际做的:色值收口成 --cat-* / --cat-*-soft(26 个变量);模板 colorof → csscolor / csssoft(19 处); 暗色只提亮 indigo(2.90 → 3.55:1);colorOf 成了死代码,删掉。 - ⚠️ 返回值必须是 template.CSS:html/template 的 CSS 过滤器不认识 var(),普通 string 会被静默 换成 ZgotmplZ —— 页面上所有圆点当场变透明,而且不报错、模板测试照过。挂了它等于关掉那道过滤, 所以数据侧色值先过严格校验(只有 # + 6 位 hex 放行),否则 #"><img… 能逃出 style 属性。 守卫 TestCSSColorSurvivesTemplateSanitizer 把两个方向都钉住(做过变异验证)。 D2 窄屏导航 - 改前 ≤620px 靠横向滚动兜底:390 溢 63px、320 溢 133px,「统计」被切成半个字紧贴后面的按钮, 渐隐提示实际看不出来。现在链接本体抽成 {{define "navitems"}} 渲染两遍:桌面 .nav-links 平铺、 窄屏 .nav-menu(原生 <details>,不靠 JS 也能开合);JS 只补点外面 / Esc / 点链接后收起。 - ⚠️ 顶栏 --z-nav 抬到 1200:菜单是 .nav 的子元素、自己抬不动层叠上下文,不抬就会被地图页的 --z-map-overlay(1100) 盖住。已在 /map 上用 elementFromPoint 实测。 - 守卫 TestNarrowNavCollapsesToMenu 钉「CSS 两半 + 模板两个类 + 渲染两遍」的成对关系。 途中发现并一并修掉的(都只有量 DOM 才看得见) - Leaflet 的 .leaflet-container 字号是 0.75rem、跟着根字号漂:根 14 → 13px 后地图署名/比例尺 掉到 9.8px(低于本站 11px 下限)、弹窗正文也跟着缩。已按自己的字阶钉死。 - h1..h4 原来只设了字重与 margin、没设字号,靠浏览器默认的 2em/1.5em/1.17em —— 同样跟着根字号漂: /stats 那两个 h2 从 21px 变成 19.5px,比同页卡片标题大一圈。已显式给出字号。 - <b>/<strong> 浏览器默认 700,比全站最高层级(标题 600)还重,已钉成 600。 回归 - ./scripts/verify.sh:PASS=348 FAIL=0 SKIP=0 - internal/fog 加 -race:PASS=22 FAIL=0 SKIP=0 - JS 单测 34/34 - 移动端 320 / 390 溢出全 0(改前 /admin 在 320 还溢 34px);桌面 1440 与改前一致(−15 是滚动条宽) - 全站 7 个页面都没有低于 11px 的文字 说明:A/B 与 C/D 本想拆两笔提交,但 app.css / layout.html / view.go 里两批改动交织,按文件拆不开、 按 hunk 拆又容易做出「中间那笔编译不过」的历史,所以合成一笔、在消息里分段。
This commit is contained in:
@@ -131,7 +131,7 @@
|
||||
<td data-label="活动">{{if .Activity}}{{.Activity}}{{else}}<span class="muted">—</span>{{end}}</td>
|
||||
<td data-label="差异">
|
||||
{{range .Fields}}
|
||||
<div style="font-size:12.5px;line-height:1.7">
|
||||
<div style="font-size:var(--fs-sm);line-height:1.7">
|
||||
<span class="muted">{{.Label}}</span>
|
||||
<span style="text-decoration:line-through;opacity:.65">{{if .From}}{{.From}}{{else}}(空){{end}}</span>
|
||||
<span style="opacity:.5">→</span>
|
||||
@@ -190,7 +190,7 @@
|
||||
<td class="num mono" data-label="坐标">{{printf "%.5f" .Lat}}, {{printf "%.5f" .Lon}}</td>
|
||||
<td data-label="合并时删除">
|
||||
{{if gt .VisitCount 0}}
|
||||
<span class="muted" style="font-size:12.5px">还有到访,不能删</span>
|
||||
<span class="muted" style="font-size:var(--fs-sm)">还有到访,不能删</span>
|
||||
{{else}}
|
||||
<input type="checkbox" form="merge-form" name="dp_{{.SrcPK}}" value="1">
|
||||
{{end}}
|
||||
@@ -213,7 +213,7 @@
|
||||
<td>{{if .DB.Name}}{{.DB.Name}}{{else}}<span class="muted">(未命名)</span>{{end}}</td>
|
||||
<td data-label="差异">
|
||||
{{range .Fields}}
|
||||
<div style="font-size:12.5px;line-height:1.7">
|
||||
<div style="font-size:var(--fs-sm);line-height:1.7">
|
||||
<span class="muted">{{.Label}}</span>
|
||||
<span style="text-decoration:line-through;opacity:.65">{{if .From}}{{.From}}{{else}}(空){{end}}</span>
|
||||
<span style="opacity:.5">→</span>
|
||||
@@ -241,17 +241,17 @@
|
||||
</div>
|
||||
<div class="row" style="gap:24px;align-items:flex-start;flex-wrap:wrap">
|
||||
<div style="flex:1 1 220px">
|
||||
<div class="muted" style="font-size:12.5px;margin-bottom:4px">备份新增</div>
|
||||
<div class="muted" style="font-size:var(--fs-sm);margin-bottom:4px">备份新增</div>
|
||||
{{range .ActAdded}}<div>{{.Name}}</div>{{else}}<div class="muted">没有</div>{{end}}
|
||||
</div>
|
||||
<div style="flex:1 1 220px">
|
||||
<div class="muted" style="font-size:12.5px;margin-bottom:4px">仅站内有(勾上=合并时删除)</div>
|
||||
<div class="muted" style="font-size:var(--fs-sm);margin-bottom:4px">仅站内有(勾上=合并时删除)</div>
|
||||
{{range .ActGone}}
|
||||
<label style="display:block"><input type="checkbox" form="merge-form" name="da_{{.SrcPK}}" value="1"> {{.Name}}</label>
|
||||
{{else}}<div class="muted">没有</div>{{end}}
|
||||
</div>
|
||||
<div style="flex:1 1 260px">
|
||||
<div class="muted" style="font-size:12.5px;margin-bottom:4px">名称不同</div>
|
||||
<div class="muted" style="font-size:var(--fs-sm);margin-bottom:4px">名称不同</div>
|
||||
{{range .ActClash}}
|
||||
<div style="margin-bottom:6px">
|
||||
{{range .Fields}}<div><span style="text-decoration:line-through;opacity:.65">{{.From}}</span> → <strong>{{.To}}</strong></div>{{end}}
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
<input type="file" name="backup" accept=".rondbackup,.zip" required>
|
||||
<button class="btn primary" type="submit">上传并换底座</button>
|
||||
</div>
|
||||
<label class="muted" style="display:block;margin-top:10px;font-size:13px">
|
||||
<label class="muted" style="display:block;margin-top:10px;font-size:var(--fs-sm)">
|
||||
<input type="checkbox" name="force" value="1">
|
||||
即使与当前数据集对不上也强制替换(重合率偏低时会再让你确认一次)
|
||||
</label>
|
||||
|
||||
@@ -518,7 +518,7 @@
|
||||
<td class="muted mono">{{if .Span}}{{.Span}}{{else}}(无时间){{end}}
|
||||
{{if .DurationMin}}<br><span class="muted">{{dur .DurationMin}}</span>{{end}}</td>
|
||||
<td class="mv-route" data-label="路线" title="{{.FromLabel}}{{if .FromCity}} · {{.FromCity}}{{end}} → {{.ToLabel}}{{if .ToCity}} · {{.ToCity}}{{end}}">{{.FromLabel}}<span class="mv-city">{{if .FromCity}} · {{.FromCity}}{{end}}</span><br><span class="muted">→ {{.ToLabel}}<span class="mv-city">{{if .ToCity}} · {{.ToCity}}{{end}}</span></span></td>
|
||||
<td class="muted" data-label="方式">{{if .TransportName}}<span class="dot" style="display:inline-block;background:{{colorof .TransportColor}}"></span> {{.TransportName}} <span class="muted mono">#{{.TransportSrc}}</span>{{else}}(无方式){{end}}</td>
|
||||
<td class="muted" data-label="方式">{{if .TransportName}}<span class="dot" style="display:inline-block;background:{{csscolor .TransportColor}}"></span> {{.TransportName}} <span class="muted mono">#{{.TransportSrc}}</span>{{else}}(无方式){{end}}</td>
|
||||
<td class="num" data-label="里程">{{if .DistanceKm}}{{km .DistanceKm}}{{else}}—{{end}}</td>
|
||||
<td class="ops">
|
||||
<details class="inline-edit">
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
{{if .Rows}}
|
||||
<div class="card">
|
||||
<h2>待补全({{num .Total}}){{if gt .PageCount 1}}<span class="muted" style="font-size:13px;font-weight:400"> 第 {{.PageNo}} / {{.PageCount}} 页</span>{{end}}</h2>
|
||||
<h2>待补全({{num .Total}}){{if gt .PageCount 1}}<span class="muted" style="font-size:var(--fs-sm);font-weight:400"> 第 {{.PageNo}} / {{.PageCount}} 页</span>{{end}}</h2>
|
||||
<div class="card-sub">
|
||||
<strong>默认都是「不动」</strong>,你改过的才会写进去。
|
||||
活动可以直接打字(会提示已有的类型),也可以点「采纳本页建议」一键填上;
|
||||
@@ -79,7 +79,7 @@
|
||||
<td data-label="建议来源">{{if $row.Source}}{{$row.Source}}{{else}}<span class="muted">—</span>{{end}}</td>
|
||||
<td data-label="高德参考">
|
||||
{{if $row.AmapType}}
|
||||
<span style="font-size:12.5px">{{if $row.AmapPoi}}{{$row.AmapPoi}}<br>{{end}}<span class="muted">{{$row.AmapType}}</span></span>
|
||||
<span style="font-size:var(--fs-sm)">{{if $row.AmapPoi}}{{$row.AmapPoi}}<br>{{end}}<span class="muted">{{$row.AmapType}}</span></span>
|
||||
{{else}}<span class="muted">—</span>{{end}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<button class="btn primary" type="submit" id="mask-save">保存</button>
|
||||
<button class="btn" type="button" id="mask-reset">清除选择</button>
|
||||
</div>
|
||||
<div class="muted" id="mask-hint" style="font-size:12.5px;margin-top:6px">在下方地图上点选圆心</div>
|
||||
<div class="muted" id="mask-hint" style="font-size:var(--fs-sm);margin-top:6px">在下方地图上点选圆心</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
<div class="card">
|
||||
<h2>差异地图</h2>
|
||||
<div class="card-sub">在真实底图上核对差异落在哪些区域,可以缩放平移。</div>
|
||||
<div class="row" style="gap:16px;margin:2px 0 10px;font-size:12.5px;flex-wrap:wrap">
|
||||
<div class="row" style="gap:16px;margin:2px 0 10px;font-size:var(--fs-sm);flex-wrap:wrap">
|
||||
<span style="display:flex;align-items:center;gap:6px">
|
||||
<i style="width:13px;height:13px;border-radius:3px;background:#24a45a;display:inline-block"></i>叠加会新增
|
||||
</span>
|
||||
@@ -110,7 +110,7 @@
|
||||
⚠️ 快照里没有 <span class="mono">Model/~</span> 与 <span class="mono">Model/#</span> 两层数据,替换会把现有的这两层一并清掉。
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="muted" style="font-size:12.5px;margin-bottom:8px">
|
||||
<div class="muted" style="font-size:var(--fs-sm);margin-bottom:8px">
|
||||
另外,站点不解析但会留存的 <span class="mono">Model/~</span>({{.TildeLayers}} 项)与
|
||||
<span class="mono">Model/#</span>({{.HashLayers}} 项)也会一并换成这份快照里的{{if .OtherLayers}},另有 {{.OtherLayers}} 项其它条目{{end}}。
|
||||
</div>
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
</label>
|
||||
<button class="btn primary" type="submit">分析差异</button>
|
||||
</form>
|
||||
<div class="muted" style="font-size:12.5px;margin-top:8px">
|
||||
<div class="muted" style="font-size:var(--fs-sm);margin-top:8px">
|
||||
上传后<strong>先不写入</strong>:会先给出这份快照能补多少新区域、以及「整体替换」会丢掉多少已有区域,
|
||||
确认之后才真正导入。
|
||||
</div>
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
<div style="min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px">
|
||||
<div style="width:100%;max-width:368px">
|
||||
<div style="text-align:center;margin-bottom:22px">
|
||||
<div style="font-size:30px">{{.Settings.OwnerAvatar}}</div>
|
||||
<h1 style="font-size:19px;margin-top:6px">{{.Settings.SiteTitle}}</h1>
|
||||
<div class="muted" style="font-size:13px">后台管理</div>
|
||||
<div style="font-size:var(--glyph-xl)">{{.Settings.OwnerAvatar}}</div>
|
||||
<h1 style="font-size:var(--fs-lg);margin-top:6px">{{.Settings.SiteTitle}}</h1>
|
||||
<div class="muted" style="font-size:var(--fs-sm)">后台管理</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin:0">
|
||||
@@ -46,7 +46,7 @@
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div style="text-align:center;margin-top:16px"><a class="muted" href="/" style="font-size:13px">← 返回主页</a></div>
|
||||
<div style="text-align:center;margin-top:16px"><a class="muted" href="/" style="font-size:var(--fs-sm)">← 返回主页</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
<form id="bulk-form" method="post" action="/admin/notes/bulk"></form>
|
||||
<div class="card" style="position:sticky;top:8px;z-index:3;display:flex;gap:10px;align-items:center;flex-wrap:wrap">
|
||||
<label class="check" style="margin:0"><input type="checkbox" id="chk-all"> 全选本页</label>
|
||||
<span class="muted" style="font-size:12.5px">批量:</span>
|
||||
<span class="muted" style="font-size:var(--fs-sm)">批量:</span>
|
||||
<select name="verdict" form="bulk-form" style="padding:5px 8px;border:1px solid var(--line);border-radius:8px">
|
||||
<option value="1">👍 推荐</option>
|
||||
<option value="-1">👎 踩雷</option>
|
||||
@@ -100,7 +100,7 @@
|
||||
data-verdict-btn data-src="{{.SrcPK}}" data-v="-1" title="标为踩雷">👎</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="muted" style="font-size:12px;margin-top:4px">
|
||||
<div class="muted" style="font-size:var(--fs-sm);margin-top:4px">
|
||||
地点 ID {{.SrcPK}}{{if .VisitCount}} · 到访 {{num .VisitCount}} 次{{end}}
|
||||
<span class="vote-tip" data-verdict-status="{{.SrcPK}}">{{if eq .Verdict 1}} · 已标推荐{{else if eq .Verdict -1}} · 已标踩雷{{end}}</span>
|
||||
{{if or .Up .Down}} · 访客 👍{{.Up}} 👎{{.Down}}{{end}}
|
||||
@@ -171,7 +171,7 @@
|
||||
<div class="item" style="grid-template-columns:1fr auto;gap:10px;align-items:center">
|
||||
<div>
|
||||
<div>{{if .Alias}}{{.Alias}}{{else}}<span class="muted">(无别名)</span>{{end}}{{if .Content}} · <span class="muted">{{.Content}}</span>{{end}}{{if .RondNote}} · <span class="muted">来自 rond:{{.RondNote}}</span>{{end}}</div>
|
||||
<div class="muted" style="font-size:12px">地点 ID {{.SrcPK}} · 更新于 {{dt .UpdatedAt}}</div>
|
||||
<div class="muted" style="font-size:var(--fs-sm)">地点 ID {{.SrcPK}} · 更新于 {{dt .UpdatedAt}}</div>
|
||||
</div>
|
||||
<form method="post" action="/admin/place/note">
|
||||
<input type="hidden" name="src_pk" value="{{.SrcPK}}">
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 style="font-size:14px;margin:0 0 10px">用户名</h2>
|
||||
<h2 style="font-size:var(--fs-sm);margin:0 0 10px">用户名</h2>
|
||||
{{if eq .Flash "name"}}<div class="alert ok">用户名已更新</div>{{end}}
|
||||
{{if .Err}}<div class="alert err">{{.Err}}</div>{{end}}
|
||||
<form method="post" action="/admin/username">
|
||||
@@ -19,7 +19,7 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 style="font-size:14px;margin:0 0 10px">密码</h2>
|
||||
<h2 style="font-size:var(--fs-sm);margin:0 0 10px">密码</h2>
|
||||
{{if and .Flash (ne .Flash "name")}}<div class="alert ok">密码已更新</div>{{end}}
|
||||
<form method="post" action="/admin/password">
|
||||
<label class="f"><span>当前密码</span><input type="password" name="current" autocomplete="current-password" required></label>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
一次几十张、几百 MB 都没有上传耗时或存储压力。
|
||||
</div>
|
||||
<div class="row" style="align-items:center;margin-bottom:10px">
|
||||
<span class="muted" style="font-size:12.5px">照片里的坐标是</span>
|
||||
<span class="muted" style="font-size:var(--fs-sm)">照片里的坐标是</span>
|
||||
<label class="check" style="margin:0"><input type="radio" name="coord-sel" value="wgs84" checked> WGS-84(绝大多数手机)</label>
|
||||
<label class="check" style="margin:0"><input type="radio" name="coord-sel" value="gcj02"> 已经是 GCJ-02(部分国产手机会这样写)</label>
|
||||
</div>
|
||||
@@ -46,7 +46,7 @@
|
||||
<div class="row" style="align-items:center;gap:10px">
|
||||
<label class="btn primary" for="photo-input" style="cursor:pointer">+ 选择照片</label>
|
||||
<input type="file" id="photo-input" accept="image/*" multiple hidden>
|
||||
<span class="muted" id="pick-count" style="font-size:12.5px">可多选,JPEG / HEIC 都行</span>
|
||||
<span class="muted" id="pick-count" style="font-size:var(--fs-sm)">可多选,JPEG / HEIC 都行</span>
|
||||
</div>
|
||||
<div id="parse-status" class="alert info"></div>
|
||||
</div>
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
</label>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="muted" style="font-size:12.5px;margin-top:8px">
|
||||
<div class="muted" style="font-size:var(--fs-sm);margin-top:8px">
|
||||
关闭的页面在访客导航中隐藏,直接输入网址也会得到 404。主页不受此开关影响。
|
||||
</div>
|
||||
<label class="f" style="max-width:320px;margin-top:14px">
|
||||
@@ -64,7 +64,7 @@
|
||||
{{range .Pages}}<option value="{{.Key}}" {{if eq $.Settings.VisitorHome .Key}}selected{{end}}>{{.Name}}</option>{{end}}
|
||||
</select>
|
||||
</label>
|
||||
<div class="muted" style="font-size:12.5px;margin-top:6px">
|
||||
<div class="muted" style="font-size:var(--fs-sm);margin-top:6px">
|
||||
访客访问站点根路径时直接落到这里;你登录后访问根路径仍然看主页。
|
||||
选择了一个没对访客开放的页面时不会跳转(会留在主页)。
|
||||
</div>
|
||||
@@ -92,7 +92,7 @@
|
||||
<label class="check"><input type="checkbox" name="hide_work" {{if .Settings.HideWork}}checked{{end}}> 隐藏类型为「工作」的记录</label>
|
||||
<label class="check"><input type="checkbox" name="notes_public" {{if .Settings.NotesPublic}}checked{{end}}> 地点备注对访客公开(不勾选则只有你自己能看到)</label>
|
||||
<label class="check"><input type="checkbox" name="marks_public" {{if .Settings.MarksPublic}}checked{{end}}> 「推荐 / 踩雷」标记与票数对访客公开</label>
|
||||
<div class="muted" style="font-size:12.5px">
|
||||
<div class="muted" style="font-size:var(--fs-sm)">
|
||||
隐藏后匿名访客看不到这两类记录。你登录后可在任意公开页面用筛选器上的
|
||||
<span class="mono">含家/工作</span> 开关临时显示给自己看。
|
||||
</div>
|
||||
@@ -105,10 +105,10 @@
|
||||
<option value="10" {{if eq .Settings.MinDwellMin 10}}selected{{end}}>少于 10 分钟</option>
|
||||
</select>
|
||||
</label>
|
||||
<div class="muted" style="font-size:12.5px">该设置作用于全站统计与地图,可有效过滤路过点。</div>
|
||||
<div class="muted" style="font-size:var(--fs-sm)">该设置作用于全站统计与地图,可有效过滤路过点。</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card card--quiet">
|
||||
<h2>地图底图</h2>
|
||||
<div class="card-sub">在 <span class="mono">conf/app.ini</span> 中配置,改完重启生效</div>
|
||||
<div class="kv"><span class="k">当前生效</span><span class="v">{{.MapInfo}}</span></div>
|
||||
@@ -122,20 +122,20 @@
|
||||
<div class="card-sub" style="margin-top:0">放大后底图自带的 POI 与路名标注太密、看不清自己的点时,换成<b>纯路网</b>。
|
||||
访客在地图上手动切换过的话,浏览器会记住他自己的选择。</div>
|
||||
{{end}}
|
||||
<div class="muted" style="font-size:12.5px;margin-top:8px">
|
||||
<div class="muted" style="font-size:var(--fs-sm);margin-top:8px">
|
||||
<div><span class="mono">map_provider</span>:<span class="mono">amap</span>(默认,免密钥)/ <span class="mono">tianditu</span>(需 <span class="mono">map_key</span>)/ <span class="mono">custom</span>(自备 <span class="mono">map_tile_url</span>)</div>
|
||||
<div><span class="mono">map_key</span>:填写后瓦片改由服务端代理,密钥不会出现在页面源码里。</div>
|
||||
<div><span class="mono">map_crs</span>:仅 <span class="mono">custom</span> 使用,填 <span class="mono">gcj02</span> 或 <span class="mono">wgs84</span>;站点数据本身是 GCJ-02,换成 WGS-84 底图(天地图 / OSM)时会自动纠偏。</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card card--quiet">
|
||||
<h2>图片存储</h2>
|
||||
<div class="card-sub">地点图片存到哪儿。图片在<b>浏览器里</b>转成 WebP 后才上传,服务器只负责收下与转存</div>
|
||||
<label class="check"><input type="radio" name="media_backend" value="local" {{if ne .Settings.MediaBackend "s3"}}checked{{end}}> 服务器本地磁盘</label>
|
||||
<label class="check"><input type="radio" name="media_backend" value="s3" {{if eq .Settings.MediaBackend "s3"}}checked{{end}}> 对象存储(S3 兼容,如 Cloudflare R2)</label>
|
||||
<div class="kv" style="margin-top:8px"><span class="k">对象存储</span><span class="v">{{.S3Info}}</span></div>
|
||||
<div class="muted" style="font-size:12.5px;margin-top:8px">
|
||||
<div class="muted" style="font-size:var(--fs-sm);margin-top:8px">
|
||||
<div>凭据放在 <span class="mono">conf/app.ini</span>,不进数据库、也不会下发到前端:</div>
|
||||
<div><span class="mono">s3_endpoint</span>(R2 形如 <span class="mono">https://<账号>.r2.cloudflarestorage.com</span>)、<span class="mono">s3_bucket</span>、<span class="mono">s3_access_key</span>、<span class="mono">s3_secret_key</span>、<span class="mono">s3_region</span>(R2 填 <span class="mono">auto</span>)。</div>
|
||||
<div><span class="mono">s3_public_base</span>:桶的公开域名(自定义域或 r2.dev),拼上对象名就是 <span class="mono"><img src></span>;不填的话图片存得进去但浏览器取不到。</div>
|
||||
@@ -152,7 +152,7 @@
|
||||
<textarea name="track_script" rows="4"
|
||||
placeholder="https://example.com/analytics.js">{{.Settings.TrackScript}}</textarea>
|
||||
</label>
|
||||
<div class="muted" style="font-size:12.5px">
|
||||
<div class="muted" style="font-size:var(--fs-sm)">
|
||||
<div>填一个 <span class="mono">https://…</span> 地址会包成 <span class="mono"><script src=…></span>;
|
||||
需要初始化代码的服务(Umami、百度统计等)可以把整段 <span class="mono"><script></span> 贴进来。</div>
|
||||
<div>整段脚本的 <span class="mono"><script></span> 与 <span class="mono"></script></span> 必须数量相同:
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
<div style="min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px">
|
||||
<div style="width:100%;max-width:368px">
|
||||
<div style="text-align:center;margin-bottom:22px">
|
||||
<div style="font-size:30px">🔐</div>
|
||||
<h1 style="font-size:19px;margin-top:6px">两步验证</h1>
|
||||
<div class="muted" style="font-size:13px">输入验证器 App 中的 6 位验证码</div>
|
||||
<div style="font-size:var(--glyph-xl)">🔐</div>
|
||||
<h1 style="font-size:var(--fs-lg);margin-top:6px">两步验证</h1>
|
||||
<div class="muted" style="font-size:var(--fs-sm)">输入验证器 App 中的 6 位验证码</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin:0">
|
||||
@@ -26,13 +26,13 @@
|
||||
<label class="f"><span>验证码</span>
|
||||
<input type="text" name="code" inputmode="numeric" pattern="[0-9]*" maxlength="6"
|
||||
autocomplete="one-time-code" autofocus required
|
||||
style="text-align:center;font-size:20px;letter-spacing:8px">
|
||||
style="text-align:center;font-size:var(--glyph-lg);letter-spacing:8px">
|
||||
</label>
|
||||
<button class="btn primary" type="submit" style="width:100%;justify-content:center">验证并登录</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div style="text-align:center;margin-top:16px"><a class="muted" href="/admin/login" style="font-size:13px">← 重新登录</a></div>
|
||||
<div style="text-align:center;margin-top:16px"><a class="muted" href="/admin/login" style="font-size:var(--fs-sm)">← 重新登录</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<form method="post" action="/admin/twofa/disable" style="max-width:360px">
|
||||
<label class="f"><span>输入当前验证码以关闭</span>
|
||||
<input type="text" name="code" inputmode="numeric" maxlength="6" required
|
||||
style="text-align:center;letter-spacing:6px;font-size:16px">
|
||||
style="text-align:center;letter-spacing:6px;font-size:var(--fs-control)">
|
||||
</label>
|
||||
<button class="btn danger" type="submit">关闭两步验证</button>
|
||||
</form>
|
||||
@@ -35,13 +35,13 @@
|
||||
<input type="text" readonly value="{{.Secret}}" onclick="this.select()"
|
||||
style="font-family:monospace;letter-spacing:1px">
|
||||
</label>
|
||||
<div class="muted" style="font-size:12.5px">账户名:rond-with-you · 算法 SHA1 · 6 位 · 30 秒</div>
|
||||
<div class="muted" style="font-size:var(--fs-sm)">账户名:rond-with-you · 算法 SHA1 · 6 位 · 30 秒</div>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" action="/admin/twofa/confirm" style="max-width:360px;margin-top:6px">
|
||||
<label class="f"><span>输入验证码确认开启</span>
|
||||
<input type="text" name="code" inputmode="numeric" maxlength="6" required
|
||||
style="text-align:center;letter-spacing:6px;font-size:16px">
|
||||
style="text-align:center;letter-spacing:6px;font-size:var(--fs-control)">
|
||||
</label>
|
||||
<button class="btn primary" type="submit">确认开启</button>
|
||||
</form>
|
||||
@@ -53,7 +53,7 @@
|
||||
<form method="post" action="/admin/twofa/setup">
|
||||
<button class="btn primary" type="submit">开始绑定验证器</button>
|
||||
</form>
|
||||
<div class="muted" style="font-size:12.5px;margin-top:10px">
|
||||
<div class="muted" style="font-size:var(--fs-sm);margin-top:10px">
|
||||
需要一个 TOTP 验证器 App(Google Authenticator、Microsoft Authenticator、1Password、Aegis 等均可)。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,10 +16,14 @@
|
||||
</div>
|
||||
|
||||
{{if .Settings.Has "overview"}}
|
||||
{{/* 首屏主数:8 个数字全都在,但只让「去过的地方」占主位,城市/省份降为它下面的一行
|
||||
上下文。原来 8 个一样大一样重,扫过去不知道先看哪个。 */}}
|
||||
<div class="kpi-lead">
|
||||
<div class="kl-v">{{num .Overview.Places}}<small>个</small></div>
|
||||
<div class="kl-k">去过的地点</div>
|
||||
<div class="kl-x">走过 {{num .Overview.Cities}} 座城市、{{num .Overview.Provinces}} 个省级行政区</div>
|
||||
</div>
|
||||
<div class="kpis" style="margin-bottom:16px">
|
||||
<div class="kpi"><div class="v">{{num .Overview.Places}}<small>个</small></div><div class="k">去过的地点</div></div>
|
||||
<div class="kpi"><div class="v">{{num .Overview.Cities}}<small>座</small></div><div class="k">到过的城市</div></div>
|
||||
<div class="kpi"><div class="v">{{num .Overview.Provinces}}<small>个</small></div><div class="k">覆盖省份</div></div>
|
||||
<div class="kpi"><div class="v">{{num .Overview.Days}}<small>天</small></div><div class="k">有记录的日子</div></div>
|
||||
<div class="kpi"><div class="v">{{num .Overview.Visits}}<small>次</small></div><div class="k">累计到访</div></div>
|
||||
<div class="kpi"><div class="v">{{dur .Overview.DwellMin}}</div><div class="k">累计停留</div></div>
|
||||
@@ -29,11 +33,11 @@
|
||||
{{end}}
|
||||
|
||||
{{if and (.Settings.Has "map") .Points}}
|
||||
<div class="card">
|
||||
<div class="card card--hero">
|
||||
<h2>足迹地图</h2>
|
||||
<div class="card-sub">共 {{num (len .Points)}} 个地点{{if .Settings.HideHome}} · 已隐藏「家」{{end}}{{if .Settings.HideWork}} · 已隐藏「工作」{{end}}</div>
|
||||
<div id="home-map" class="map-preview"></div>
|
||||
<div style="margin-top:10px;font-size:12.5px" class="muted">滚轮被禁用,点击地图进入全屏模式查看细节与筛选。</div>
|
||||
<div style="margin-top:10px;font-size:var(--fs-sm)" class="muted">滚轮被禁用,点击地图进入全屏模式查看细节与筛选。</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@@ -46,7 +50,7 @@
|
||||
{{range .ActivityStats}}
|
||||
<div class="bar-row">
|
||||
<div class="nm" title="{{.Name}}">{{with icon .Icon}}{{.}} {{end}}{{.Name}}</div>
|
||||
<div class="bar-track"><div class="bar-fill" style="width:{{pct .Count $.MaxActivity}}%;background:{{colorof .Color}}"></div></div>
|
||||
<div class="bar-track"><div class="bar-fill" style="width:{{pct .Count $.MaxActivity}}%;background:{{csscolor .Color}}"></div></div>
|
||||
<div class="vv">{{num .Count}} 次</div>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -77,7 +81,7 @@
|
||||
<h2>月度活跃度</h2>
|
||||
<div class="card-sub">每月到访次数</div>
|
||||
<div class="cols">
|
||||
{{range .Monthly}}<div class="col" style="height:{{.Pct}}%" title="{{.Key}}:{{.Count}} 次"></div>{{end}}
|
||||
{{range .Monthly}}<div class="col{{if eq .Count $.MaxMonthly}} peak{{end}}" style="height:{{.Pct}}%" title="{{.Key}}:{{.Count}} 次">{{if eq .Count $.MaxMonthly}}<span class="pv">{{num .Count}}</span>{{end}}</div>{{end}}
|
||||
</div>
|
||||
<div class="cols-x">{{range .Monthly}}<span>{{.Label}}</span>{{end}}</div>
|
||||
</div>
|
||||
@@ -103,7 +107,7 @@
|
||||
{{range .Transports}}
|
||||
<div class="bar-row">
|
||||
<div class="nm">{{with icon .Icon}}{{.}} {{end}}{{.Name}}</div>
|
||||
<div class="bar-track"><div class="bar-fill" style="width:{{pct .Count $.MaxTransport}}%;background:{{colorof .Color}}"></div></div>
|
||||
<div class="bar-track"><div class="bar-fill" style="width:{{pct .Count $.MaxTransport}}%;background:{{csscolor .Color}}"></div></div>
|
||||
<div class="vv">{{num .Count}} 次</div>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -118,7 +122,7 @@
|
||||
<div class="chips">
|
||||
{{range .Tags}}
|
||||
<a class="chip" href="{{$.TagLink .ID}}" title="{{num .Count}} 次">
|
||||
<span class="dot" style="background:{{colorof .Color}}"></span>{{.Name}}
|
||||
<span class="dot" style="background:{{csscolor .Color}}"></span>{{.Name}}
|
||||
<span class="muted">{{num .Count}}</span>
|
||||
</a>
|
||||
{{end}}
|
||||
@@ -156,7 +160,7 @@
|
||||
<div class="list">
|
||||
{{range .Recent}}
|
||||
<div class="item">
|
||||
<div class="ic" style="background:{{colorof .Color}}22">{{iconor .Icon "📍"}}</div>
|
||||
<div class="ic" style="background:{{csssoft .Color}}">{{iconor .Icon "📍"}}</div>
|
||||
<div>
|
||||
<div class="t">{{if .PlaceID}}<a href="{{$.PlaceLink .PlaceID}}">{{.PlaceName}}</a>{{else}}未命名地点{{end}}</div>
|
||||
<div class="m">{{day .Arrival}} · {{hm .Arrival}}{{if .City}} · {{.City}}{{end}}{{if .ActivityName}} · {{.ActivityName}}{{end}}{{if .Tags}} · {{range $i, $t := .Tags}}{{if $i}}、{{end}}<span class="tag">{{$t}}</span>{{end}}{{end}}</div>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
{{define "nav"}}
|
||||
<nav class="nav">
|
||||
<div class="nav-inner">
|
||||
<a class="brand" href="/" title="{{.Settings.SiteTitle}}"><span class="avatar">{{.Settings.OwnerAvatar}}</span><span class="t">{{.Settings.SiteTitle}}</span></a>
|
||||
<div class="nav-links">
|
||||
{{/* 页面开关只管访客:站长登录后始终能访问全部页面(guardPage 也是这么放的),
|
||||
导航必须跟着一起放行,否则关掉页面开关后站长自己也只剩「主页」一个入口,
|
||||
明明能访问却点不到。 */}}
|
||||
{{/* navitems 是导航链接本体,抽出来是因为它要渲染两遍:桌面上在 .nav-links 里平铺,
|
||||
窄屏整行一定放不下(实测 390 溢 63px、320 溢 133px,最深处的「统计」会被切成半个字),
|
||||
所以另收进 .nav-menu 那个折叠菜单。两份靠 display 二选一,DOM 里不会同时可见。
|
||||
|
||||
页面开关只管访客:站长登录后始终能访问全部页面(guardPage 也是这么放的),
|
||||
导航必须跟着一起放行,否则关掉页面开关后站长自己也只剩「主页」一个入口。 */}}
|
||||
{{define "navitems"}}
|
||||
<a href="/" class="{{if eq .Nav "home"}}on{{end}}">主页</a>
|
||||
{{if or .IsAdmin (.Settings.HasPage "map")}}<a href="/map" class="{{if eq .Nav "map"}}on{{end}}">地图</a>{{end}}
|
||||
{{if or .IsAdmin (.Settings.HasPage "places")}}<a href="/places" class="{{if eq .Nav "places"}}on{{end}}">地点</a>{{end}}
|
||||
@@ -13,7 +12,17 @@
|
||||
{{if or .IsAdmin (.Settings.HasPage "stats")}}<a href="/stats" class="{{if eq .Nav "stats"}}on{{end}}">统计</a>{{end}}
|
||||
{{if .HasTopics}}<a href="/topics" class="{{if eq .Nav "topics"}}on{{end}}">专题</a>{{end}}
|
||||
{{if or .IsAdmin (.Settings.HasPage "report")}}<a href="/report" class="{{if eq .Nav "report"}}on{{end}}">报告</a>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{define "nav"}}
|
||||
<nav class="nav">
|
||||
<div class="nav-inner">
|
||||
<a class="brand" href="/" title="{{.Settings.SiteTitle}}"><span class="avatar">{{.Settings.OwnerAvatar}}</span><span class="t">{{.Settings.SiteTitle}}</span></a>
|
||||
<div class="nav-links">{{template "navitems" .}}</div>
|
||||
<details class="nav-menu">
|
||||
<summary aria-label="导航菜单">菜单</summary>
|
||||
<nav class="nav-menu-body">{{template "navitems" .}}</nav>
|
||||
</details>
|
||||
<div class="nav-right">
|
||||
{{if .User}}
|
||||
<span class="who">{{.User.DisplayName}}</span>
|
||||
@@ -26,17 +35,26 @@
|
||||
</div>
|
||||
</nav>
|
||||
<script>
|
||||
/* 手机上导航装不下这几项,可以横向滑动(滚动条已隐藏)——右侧加一层渐隐,提示「还能往右滑」;
|
||||
滑到末端就收掉,免得让人以为后面还有。桌面端没有横向溢出,这段等于不做事。 */
|
||||
/* 1) 620px 以上:链接行万一还是放不下(以后加了入口),横向可滑 + 右缘渐隐提示。
|
||||
2) 620px 及以下:整行已经收进 .nav-menu,这里只管菜单开合——原生 <details> 不会
|
||||
点外面自动收、也不响应 Esc,补上;点链接后也收掉,免得盖着刚打开的页面。 */
|
||||
(function () {
|
||||
var el = document.querySelector('.nav-links');
|
||||
if (!el) return;
|
||||
var update = function () {
|
||||
el.classList.toggle('overflow-right', el.scrollWidth - el.clientWidth - el.scrollLeft > 2);
|
||||
};
|
||||
update();
|
||||
el.addEventListener('scroll', update, { passive: true });
|
||||
window.addEventListener('resize', update);
|
||||
if (el) {
|
||||
var update = function () {
|
||||
el.classList.toggle('overflow-right', el.scrollWidth - el.clientWidth - el.scrollLeft > 2);
|
||||
};
|
||||
update();
|
||||
el.addEventListener('scroll', update, { passive: true });
|
||||
window.addEventListener('resize', update);
|
||||
}
|
||||
var menu = document.querySelector('.nav-menu');
|
||||
if (!menu) return;
|
||||
document.addEventListener('click', function (e) { if (!menu.contains(e.target)) menu.open = false; });
|
||||
menu.addEventListener('click', function (e) {
|
||||
if (e.target.closest && e.target.closest('a')) menu.open = false;
|
||||
});
|
||||
document.addEventListener('keydown', function (e) { if (e.key === 'Escape') menu.open = false; });
|
||||
})();
|
||||
</script>
|
||||
{{end}}
|
||||
@@ -73,7 +91,7 @@
|
||||
<div class="chips" style="margin-bottom:{{if gt (len .Activities) 14}}8{{else}}12{{end}}px">
|
||||
{{range $i, $a := .Activities}}{{if or (lt $i 14) ($.IsAct $a.ID)}}
|
||||
<a class="chip {{if $.IsAct $a.ID}}on{{end}}" href="{{$.ActLink $a.ID}}" title="{{num $a.Count}} 次到访">
|
||||
<span class="dot" style="background:{{colorof $a.Color}}"></span>{{$a.Name}}
|
||||
<span class="dot" style="background:{{csscolor $a.Color}}"></span>{{$a.Name}}
|
||||
<span class="muted">{{num $a.Count}}</span>
|
||||
</a>
|
||||
{{end}}{{end}}
|
||||
@@ -84,7 +102,7 @@
|
||||
<div class="chips">
|
||||
{{range $i, $a := .Activities}}{{if and (ge $i 14) (not ($.IsAct $a.ID))}}
|
||||
<a class="chip" href="{{$.ActLink $a.ID}}">
|
||||
<span class="dot" style="background:{{colorof $a.Color}}"></span>{{$a.Name}}
|
||||
<span class="dot" style="background:{{csscolor $a.Color}}"></span>{{$a.Name}}
|
||||
<span class="muted">{{num $a.Count}}</span>
|
||||
</a>
|
||||
{{end}}{{end}}
|
||||
@@ -166,7 +184,7 @@
|
||||
<div class="card">
|
||||
<div class="empty">
|
||||
<div class="big">🗺️</div>
|
||||
<div style="font-size:15px;color:var(--text);margin-bottom:6px">还没有导入任何数据</div>
|
||||
<div style="font-size:var(--fs-md);color:var(--text);margin-bottom:6px">还没有导入任何数据</div>
|
||||
<div>登录后台,上传 rond 导出的 <span class="mono">.rondbackup</span> 备份文件即可生成足迹地图。</div>
|
||||
<div style="margin-top:14px"><a class="btn primary" href="/admin">前往后台导入</a></div>
|
||||
</div>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
<div class="chips" style="margin-bottom:10px">
|
||||
{{range $i, $a := .Activities}}{{if or (lt $i 12) ($.IsAct $a.ID)}}
|
||||
<a class="chip {{if $.IsAct $a.ID}}on{{end}}" href="{{$.ActLink $a.ID}}">
|
||||
<span class="dot" style="background:{{colorof $a.Color}}"></span>{{$a.Name}}
|
||||
<span class="dot" style="background:{{csscolor $a.Color}}"></span>{{$a.Name}}
|
||||
</a>
|
||||
{{end}}{{end}}
|
||||
</div>
|
||||
@@ -69,7 +69,7 @@
|
||||
<summary>展开其余 {{len .Activities}} 个类型</summary>
|
||||
<div class="chips">
|
||||
{{range $i, $a := .Activities}}{{if and (ge $i 12) (not ($.IsAct $a.ID))}}
|
||||
<a class="chip" href="{{$.ActLink $a.ID}}"><span class="dot" style="background:{{colorof $a.Color}}"></span>{{$a.Name}}</a>
|
||||
<a class="chip" href="{{$.ActLink $a.ID}}"><span class="dot" style="background:{{csscolor $a.Color}}"></span>{{$a.Name}}</a>
|
||||
{{end}}{{end}}
|
||||
</div>
|
||||
</details>
|
||||
@@ -136,7 +136,7 @@
|
||||
<div class="map-legend">
|
||||
<div style="font-weight:500;color:var(--text-2)">类型分布</div>
|
||||
{{range .Legend}}
|
||||
<div class="lg"><i style="background:{{colorof .Color}}"></i>{{.Name}} <span class="muted">{{num .Count}}</span></div>
|
||||
<div class="lg"><i style="background:{{csscolor .Color}}"></i>{{.Name}} <span class="muted">{{num .Count}}</span></div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
<div class="row" style="margin-top:12px;gap:10px">
|
||||
<label class="btn primary" for="imgs-input" id="imgs-pick" style="cursor:pointer">+ 选择图片</label>
|
||||
<input type="file" id="imgs-input" accept="image/*" multiple hidden>
|
||||
<span class="muted" id="imgs-count" style="font-size:12.5px"></span>
|
||||
<span class="muted" id="imgs-count" style="font-size:var(--fs-sm)"></span>
|
||||
</div>
|
||||
<div class="imgs-hint" id="imgs-hint"></div>
|
||||
</div>
|
||||
@@ -200,7 +200,7 @@
|
||||
<div class="list" style="max-height:280px;overflow-y:auto">
|
||||
{{range .Visits}}
|
||||
<div class="item" style="grid-template-columns:34px 1fr auto">
|
||||
<div class="ic" style="background:{{colorof .Color}}22;width:30px;height:30px;font-size:15px">{{iconor .Icon "📍"}}</div>
|
||||
<div class="ic" style="background:{{csssoft .Color}};width:30px;height:30px;font-size:var(--fs-md)">{{iconor .Icon "📍"}}</div>
|
||||
<div>
|
||||
<div class="t" style="font-weight:400">{{day .Arrival}}</div>
|
||||
<div class="m">{{hm .Arrival}} – {{if .Departure}}{{hm .Departure}}{{else}}进行中{{end}}{{if .Remark}} · {{.Remark}}{{end}}</div>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
<div class="card">
|
||||
<div class="row" style="margin-bottom:12px">
|
||||
<span class="muted" style="font-size:12.5px">排序</span>
|
||||
<span class="muted" style="font-size:var(--fs-sm)">排序</span>
|
||||
<div class="chips">
|
||||
<a class="chip {{if eq .Sort ""}}on{{end}}" href="{{.Link .FilterBase "sort" ""}}">到访次数</a>
|
||||
<a class="chip {{if eq .Sort "dwell"}}on{{end}}" href="{{.Link .FilterBase "sort" "dwell"}}">停留时长</a>
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
{{if or .IsAdmin .Settings.MarksPublic}}
|
||||
<div class="row" style="margin-bottom:12px">
|
||||
<span class="muted" style="font-size:12.5px">标记</span>
|
||||
<span class="muted" style="font-size:var(--fs-sm)">标记</span>
|
||||
<div class="chips">
|
||||
<a class="chip {{if eq .Verdict 0}}on{{end}}" href="{{.VerdictLink 0}}">全部</a>
|
||||
<a class="chip {{if eq .Verdict 1}}on{{end}}" href="{{.VerdictLink 1}}">👍 推荐</a>
|
||||
@@ -55,11 +55,11 @@
|
||||
{{if .IsHome}}<span class="tag">家</span>{{end}}
|
||||
{{if .IsWork}}<span class="tag">工作</span>{{end}}
|
||||
{{if eq .Verdict 1}}<span class="tag vote-up">👍 推荐</span>{{else if eq .Verdict -1}}<span class="tag vote-down">👎 踩雷</span>{{end}}
|
||||
{{if or .Up .Down}}<span class="muted" style="font-size:12px">👍{{.Up}} · 👎{{.Down}}</span>{{end}}
|
||||
{{if or .Up .Down}}<span class="muted" style="font-size:var(--fs-sm)">👍{{.Up}} · 👎{{.Down}}</span>{{end}}
|
||||
</td>
|
||||
<td class="muted" data-label="城市">{{if .City}}{{.City}}{{else}}—{{end}}</td>
|
||||
<td data-label="主要类型">
|
||||
{{if .TopActivity}}<span class="chip" style="padding:1px 8px"><span class="dot" style="background:{{colorof .TopColor}}"></span>{{.TopActivity}}</span>{{else}}<span class="muted">—</span>{{end}}
|
||||
{{if .TopActivity}}<span class="chip" style="padding:1px 8px"><span class="dot" style="background:{{csscolor .TopColor}}"></span>{{.TopActivity}}</span>{{else}}<span class="muted">—</span>{{end}}
|
||||
</td>
|
||||
<td data-label="图片">{{if .HasImage}}<span title="有图片">📷</span>{{else}}<span class="muted" title="还没有上传图片">—</span>{{end}}</td>
|
||||
<td class="num" data-label="到访">{{num .VisitCount}}</td>
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
<tbody>
|
||||
{{range .Transports}}
|
||||
<tr>
|
||||
<td><span class="dot" style="display:inline-block;background:{{colorof .Color}}"></span> {{with icon .Icon}}{{.}} {{end}}{{.Name}}</td>
|
||||
<td><span class="dot" style="display:inline-block;background:{{csscolor .Color}}"></span> {{with icon .Icon}}{{.}} {{end}}{{.Name}}</td>
|
||||
<td class="num" data-label="次数">{{num .Count}}</td>
|
||||
<td class="num" data-label="里程">{{km .Distance}} km</td>
|
||||
</tr>
|
||||
|
||||
@@ -27,14 +27,14 @@
|
||||
<div class="card" style="margin-bottom:16px">
|
||||
<div class="row" style="justify-content:space-between;align-items:center;flex-wrap:wrap">
|
||||
<h2 style="margin:0">推荐 & 踩雷({{if .IsAdmin}}我的结论{{else}}站长结论{{end}})</h2>
|
||||
<span class="muted" style="font-size:12.5px">推荐 {{num .VoteRec}} · 踩雷 {{num .VoteAvoid}}</span>
|
||||
<span class="muted" style="font-size:var(--fs-sm)">推荐 {{num .VoteRec}} · 踩雷 {{num .VoteAvoid}}</span>
|
||||
</div>
|
||||
<div class="card-sub">
|
||||
只统计<strong>{{if .IsAdmin}}你自己{{else}}站长{{end}}</strong>在地点页或地图气泡里标的 👍/👎——按坐标键存,重新上传备份也不会丢。
|
||||
</div>
|
||||
<div class="grid g2">
|
||||
<div>
|
||||
<div class="muted" style="font-size:12.5px;margin-bottom:6px">👍 {{if .IsAdmin}}我{{else}}站长{{end}}推荐的地点</div>
|
||||
<div class="muted" style="font-size:var(--fs-sm);margin-bottom:6px">👍 {{if .IsAdmin}}我{{else}}站长{{end}}推荐的地点</div>
|
||||
{{if .Recommended}}
|
||||
<div class="chips">
|
||||
{{range .Recommended}}<a class="chip" href="{{$.PlaceLink .ID}}"><span class="dot" style="background:#34c759"></span>{{if .Name}}{{.Name}}{{else}}未命名{{end}}{{if .City}}<span class="muted"> · {{.City}}</span>{{end}}</a>{{end}}
|
||||
@@ -42,7 +42,7 @@
|
||||
{{else}}<div class="empty" style="padding:10px 0">还没有标记</div>{{end}}
|
||||
</div>
|
||||
<div>
|
||||
<div class="muted" style="font-size:12.5px;margin-bottom:6px">👎 {{if .IsAdmin}}我{{else}}站长{{end}}踩雷的地点</div>
|
||||
<div class="muted" style="font-size:var(--fs-sm);margin-bottom:6px">👎 {{if .IsAdmin}}我{{else}}站长{{end}}踩雷的地点</div>
|
||||
{{if .Avoided}}
|
||||
<div class="chips">
|
||||
{{range .Avoided}}<a class="chip" href="{{$.PlaceLink .ID}}"><span class="dot" style="background:#ff3b30"></span>{{if .Name}}{{.Name}}{{else}}未命名{{end}}{{if .City}}<span class="muted"> · {{.City}}</span>{{end}}</a>{{end}}
|
||||
@@ -56,14 +56,14 @@
|
||||
<div class="card" style="margin-bottom:16px">
|
||||
<div class="row" style="justify-content:space-between;align-items:center;flex-wrap:wrap">
|
||||
<h2 style="margin:0">访客点赞</h2>
|
||||
<span class="muted" style="font-size:12.5px">👍 {{num .VoteUp}} 票 · 👎 {{num .VoteDown}} 票</span>
|
||||
<span class="muted" style="font-size:var(--fs-sm)">👍 {{num .VoteUp}} 票 · 👎 {{num .VoteDown}} 票</span>
|
||||
</div>
|
||||
<div class="card-sub">
|
||||
访客在地点页点的 👍/👎,一人一票、可改可撤。与上面的站长结论<strong>互不影响</strong>:各存各的表,谁也不会覆盖谁。
|
||||
</div>
|
||||
<div class="grid g2">
|
||||
<div>
|
||||
<div class="muted" style="font-size:12.5px;margin-bottom:6px">👍 被点赞最多的地点</div>
|
||||
<div class="muted" style="font-size:var(--fs-sm);margin-bottom:6px">👍 被点赞最多的地点</div>
|
||||
{{if .VisitorUp}}
|
||||
<div class="chips">
|
||||
{{range .VisitorUp}}<a class="chip" href="{{$.PlaceLink .ID}}"><span class="dot" style="background:#34c759"></span>{{if .Name}}{{.Name}}{{else}}未命名{{end}}<span class="muted"> · 👍 {{.Up}}{{if .Down}} · 👎 {{.Down}}{{end}}</span></a>{{end}}
|
||||
@@ -71,7 +71,7 @@
|
||||
{{else}}<div class="empty" style="padding:10px 0">还没有访客点赞</div>{{end}}
|
||||
</div>
|
||||
<div>
|
||||
<div class="muted" style="font-size:12.5px;margin-bottom:6px">👎 被点踩最多的地点</div>
|
||||
<div class="muted" style="font-size:var(--fs-sm);margin-bottom:6px">👎 被点踩最多的地点</div>
|
||||
{{if .VisitorDown}}
|
||||
<div class="chips">
|
||||
{{range .VisitorDown}}<a class="chip" href="{{$.PlaceLink .ID}}"><span class="dot" style="background:#ff3b30"></span>{{if .Name}}{{.Name}}{{else}}未命名{{end}}<span class="muted"> · 👎 {{.Down}}{{if .Up}} · 👍 {{.Up}}{{end}}</span></a>{{end}}
|
||||
@@ -84,10 +84,11 @@
|
||||
|
||||
<div class="card">
|
||||
<h2>月度分布</h2>
|
||||
<div class="card-sub">每月到访次数,最高 {{num .MaxMonthly}} 次</div>
|
||||
<div class="card-sub">每月到访次数 · 点柱条只看那个月</div>
|
||||
<div class="cols">
|
||||
{{/* 点柱条直接筛出那个月;title 是悬停读数 */}}
|
||||
{{range .Monthly}}<a class="col" href="{{$.MonthLink .Key}}" title="{{.Key}}:{{.Count}} 次到访。点一下只看这个月" style="height:{{.Pct}}%"></a>{{end}}
|
||||
{{/* 点柱条直接筛出那个月;title 是悬停读数。最高的那根在柱顶标出次数(.pv),
|
||||
副标题里就不再重复写一遍「最高 N 次」。 */}}
|
||||
{{range .Monthly}}<a class="col{{if eq .Count $.MaxMonthly}} peak{{end}}" href="{{$.MonthLink .Key}}" title="{{.Key}}:{{.Count}} 次到访。点一下只看这个月" style="height:{{.Pct}}%">{{if eq .Count $.MaxMonthly}}<span class="pv">{{num .Count}}</span>{{end}}</a>{{end}}
|
||||
</div>
|
||||
<div class="cols-x">{{range .Monthly}}<span>{{.Label}}</span>{{end}}</div>
|
||||
</div>
|
||||
@@ -100,7 +101,7 @@
|
||||
{{range .ActivityStats}}
|
||||
<a class="bar-row" href="{{$.ActLink .ID}}" title="{{.Name}}:{{num .Count}} 次,累计 {{dur .Dwell}}。点一下按这个类型筛选">
|
||||
<div class="nm">{{with icon .Icon}}{{.}} {{end}}{{.Name}}</div>
|
||||
<div class="bar-track"><div class="bar-fill" style="width:{{pct .Count $.MaxActivity}}%;background:{{colorof .Color}}"></div></div>
|
||||
<div class="bar-track"><div class="bar-fill" style="width:{{pct .Count $.MaxActivity}}%;background:{{csscolor .Color}}"></div></div>
|
||||
<div class="vv">{{num .Count}} 次 · {{dur .Dwell}}</div>
|
||||
</a>
|
||||
{{end}}
|
||||
@@ -111,10 +112,12 @@
|
||||
<h2>一天中的分布</h2>
|
||||
<div class="card-sub">按到访开始时间统计</div>
|
||||
<div class="cols" style="height:130px">
|
||||
{{range $h, $c := .Hourly}}<div class="col" style="height:{{pct $c $.MaxHourly}}%" title="{{$h}} 时:{{$c}} 次"></div>{{end}}
|
||||
{{range $h, $c := .Hourly}}<div class="col{{if eq $c $.MaxHourly}} peak{{end}}" style="height:{{pct $c $.MaxHourly}}%" title="{{$h}} 时:{{$c}} 次"></div>{{end}}
|
||||
</div>
|
||||
<div class="cols-x">
|
||||
{{range $h, $c := .Hourly}}<span>{{if eq $h 0}}0{{else if eq $h 6}}6{{else if eq $h 12}}12{{else if eq $h 18}}18{{else}} {{end}}</span>{{end}}
|
||||
{{/* 24 根柱光看高度分不出上午/下午:换成一行 6 小时一段的标尺,段边界与柱子严格
|
||||
对齐(同一套 24 列网格),每段直接把时段与小时区间写出来,就不必再留坐标轴。 */}}
|
||||
<div class="cols-band">
|
||||
<span>深夜 0-6</span><span>上午 6-12</span><span>下午 12-18</span><span>晚上 18-24</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -181,7 +184,7 @@
|
||||
<tbody>
|
||||
{{range .Transports}}
|
||||
<tr>
|
||||
<td><span class="dot" style="display:inline-block;background:{{colorof .Color}}"></span> {{with icon .Icon}}{{.}} {{end}}{{.Name}}</td>
|
||||
<td><span class="dot" style="display:inline-block;background:{{csscolor .Color}}"></span> {{with icon .Icon}}{{.}} {{end}}{{.Name}}</td>
|
||||
<td class="num" data-label="次数">{{num .Count}}</td>
|
||||
<td class="num" data-label="里程">{{km .Distance}} km</td>
|
||||
<td class="num" data-label="在途时间">{{dur .DwellMin}}</td>
|
||||
@@ -202,7 +205,7 @@
|
||||
<div class="bars">
|
||||
{{range .Tags}}
|
||||
<a class="bar-row" href="{{$.TagLink .ID}}" title="#{{.Name}}:{{num .Count}} 次到访。点一下按这个标签筛选">
|
||||
<div class="nm"><span class="dot" style="display:inline-block;background:{{colorof .Color}}"></span> {{.Name}}</div>
|
||||
<div class="nm"><span class="dot" style="display:inline-block;background:{{csscolor .Color}}"></span> {{.Name}}</div>
|
||||
<div class="bar-track"><div class="bar-fill" style="width:{{pct .Count $.MaxTag}}%"></div></div>
|
||||
<div class="vv">{{num .Count}} 次</div>
|
||||
</a>
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
{{range .Items}}
|
||||
<div class="tl-item">
|
||||
<div class="tm">{{hm .Arrival}}</div>
|
||||
<div class="ln"><i style="background:{{colorof .Color}}"></i></div>
|
||||
<div class="ln"><i style="background:{{csscolor .Color}}"></i></div>
|
||||
<div class="bd">
|
||||
<div>
|
||||
{{if .PlaceID}}<a href="{{$.PlaceLink .PlaceID}}">{{if .PlaceName}}{{.PlaceName}}{{else}}未命名地点{{end}}</a>{{else}}未命名地点{{end}}
|
||||
{{if .ActivityName}}<span class="chip" style="padding:1px 8px;font-size:11.5px"><span class="dot" style="background:{{colorof .Color}}"></span>{{.ActivityName}}</span>{{end}}
|
||||
{{if .ActivityName}}<span class="chip" style="padding:1px 8px;font-size:var(--fs-xs)"><span class="dot" style="background:{{csscolor .Color}}"></span>{{.ActivityName}}</span>{{end}}
|
||||
{{range .Tags}}<span class="tag">{{.}}</span>{{end}}
|
||||
</div>
|
||||
<div class="meta">
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
<tr>
|
||||
<td>{{if .Name}}{{.Name}}{{else}}<span class="muted">(未命名)</span>{{end}}</td>
|
||||
<td class="muted" data-label="城市">{{.City}}</td>
|
||||
<td class="muted" data-label="类型">{{if .Act}}<span class="dot" style="background:{{colorof .Color}}"></span>{{.Act}}{{end}}</td>
|
||||
<td class="muted" data-label="类型">{{if .Act}}<span class="dot" style="background:{{csscolor .Color}}"></span>{{.Act}}{{end}}</td>
|
||||
<td class="num" data-label="到访">{{num .Count}}</td>
|
||||
<td class="num muted" data-label="停留">{{dur .Dwell}}</td>
|
||||
<td class="muted mono" data-label="最近一次">{{.LastAt}}</td>
|
||||
|
||||
Reference in New Issue
Block a user