备份与迷雾的差异分析、合并执行,附属数据改挂坐标键
备份(.rondbackup)与迷雾(.fwss)各加一层「先看差异再动手」: - 迷雾:上传先出差异页,说清叠加能补多少、整体替换会丢多少,并在真实底图上 用绿/灰/红标出会新增 / 不变 / 会丢的区域 - 备份:与当前数据集按 UUID 逐条比对(rond 每次导出都会重排 Z_PK,编号认不出人), 新增合入、冲突逐条选、仅站内有逐条勾选删除,全在一个事务里执行; 合入的行写回 entity_raw,保证再传同一份备份时差异归零 - 地点按坐标对齐:站点的「补全地址」会把地点改名,拿名称当键会让一整个地点的 到访全变成假冲突 - 附属数据(备注/结论/访客票/图片)从 dataset_id+src_pk 换成坐标键,换数据集不再失联; 访客票没有 dataset_id,回填按活跃数据集认领 - 修正 contentHash 拿 Z_PK 当指纹的隐患,以及两处「src_pk 跨快照稳定」的错误注释 导出格式(.rondbackup / .fwss)保持原样,往返测试通过。
This commit is contained in:
@@ -0,0 +1,310 @@
|
||||
{{define "content"}}
|
||||
<div class="wrap">
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>备份差异预览</h1>
|
||||
<div class="sub">
|
||||
这份备份与当前展示的「{{.DatasetName}}」逐条比对的结果。<strong>还没有导入</strong>,站内数据一个字节都没动。
|
||||
</div>
|
||||
</div>
|
||||
<div class="row"><a class="btn" href="/admin">← 返回后台</a></div>
|
||||
</div>
|
||||
|
||||
{{if .Identical}}
|
||||
<div class="card">
|
||||
<div class="alert ok">这份备份与当前数据集<strong>完全一致</strong>:没有新增、没有被删、也没有冲突。</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if .Truncated}}
|
||||
<div class="card"><div class="alert err">差异较多,下面每类只列出前 200 条,确切数量看概览表。</div></div>
|
||||
{{end}}
|
||||
|
||||
<div class="card">
|
||||
<h2>概览</h2>
|
||||
<div class="tw">
|
||||
<table class="tb tb-stack">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>实体</th><th class="num">备份</th><th class="num">站内</th>
|
||||
<th class="num">备份新增</th><th class="num">仅站内有</th><th class="num">冲突</th><th class="num">一致</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>到访</td>
|
||||
<td class="num" data-label="备份">{{num .BackupVisits}}</td>
|
||||
<td class="num" data-label="站内">{{num .StoredVisits}}</td>
|
||||
<td class="num" data-label="备份新增">{{num .VisitCounts.Added}}</td>
|
||||
<td class="num" data-label="仅站内有">{{num .VisitCounts.Removed}}</td>
|
||||
<td class="num" data-label="冲突">{{num .VisitCounts.Changed}}</td>
|
||||
<td class="num" data-label="一致">{{num .VisitCounts.Same}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>地点</td>
|
||||
<td class="num" data-label="备份">{{num .BackupPlaces}}</td>
|
||||
<td class="num" data-label="站内">{{num .StoredPlaces}}</td>
|
||||
<td class="num" data-label="备份新增">{{num .PlaceCounts.Added}}</td>
|
||||
<td class="num" data-label="仅站内有">{{num .PlaceCounts.Removed}}</td>
|
||||
<td class="num" data-label="冲突">{{num .PlaceCounts.Changed}}</td>
|
||||
<td class="num" data-label="一致">{{num .PlaceCounts.Same}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>活动类型</td>
|
||||
<td class="num" data-label="备份">{{num .BackupActs}}</td>
|
||||
<td class="num" data-label="站内">{{num .StoredActs}}</td>
|
||||
<td class="num" data-label="备份新增">{{num .ActCounts.Added}}</td>
|
||||
<td class="num" data-label="仅站内有">{{num .ActCounts.Removed}}</td>
|
||||
<td class="num" data-label="冲突">{{num .ActCounts.Changed}}</td>
|
||||
<td class="num" data-label="一致">{{num .ActCounts.Same}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-sub" style="margin-top:10px">
|
||||
对齐按每条记录的 <strong>UUID</strong> 做:rond 每次导出都会重排记录编号(同样的 822 条到访,两次导出的编号 100% 不同,UUID 却逐条一模一样),
|
||||
所以只有 UUID 认得出来是同一条;地点没有 UUID,退到「名称 + 坐标」。
|
||||
站内补录的记录不会出现在真机备份里,一律归到「仅站内有」。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>到访</h2>
|
||||
|
||||
<div class="card-sub"><strong>备份里多出来的({{num .VisitCounts.Added}})</strong> —— 目前站内没有这些记录</div>
|
||||
{{if .VisitAdded}}
|
||||
<div class="tw">
|
||||
<table class="tb tb-stack">
|
||||
<thead><tr><th>到达</th><th>离开</th><th>地点</th><th>活动</th><th>备注</th></tr></thead>
|
||||
<tbody>
|
||||
{{range .VisitAdded}}
|
||||
<tr>
|
||||
<td class="mono">{{dtt .Arrival}}</td>
|
||||
<td class="mono" data-label="离开">{{dtt .Departure}}</td>
|
||||
<td data-label="地点">{{if .Place}}{{.Place}}{{else}}<span class="muted">—</span>{{end}}</td>
|
||||
<td data-label="活动">{{if .Activity}}{{.Activity}}{{else}}<span class="muted">—</span>{{end}}</td>
|
||||
<td data-label="备注">{{if .Remark}}{{.Remark}}{{else}}<span class="muted">—</span>{{end}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{else}}<div class="muted" style="margin-bottom:14px">没有</div>{{end}}
|
||||
|
||||
<div class="card-sub" style="margin-top:16px">
|
||||
<strong>只在站内有({{num .VisitCounts.Removed}})</strong> —— 备份里找不到;可能是你在这里补录的,也可能是实机上删掉的。
|
||||
<strong>默认一条都不删</strong>;确实想清掉的,勾上左列的「合并时删除」。
|
||||
</div>
|
||||
{{if .VisitGone}}
|
||||
<div class="tw">
|
||||
<table class="tb tb-stack">
|
||||
<thead><tr><th>到达</th><th>离开</th><th>地点</th><th>活动</th><th>备注</th><th>来源</th><th>合并时删除</th></tr></thead>
|
||||
<tbody>
|
||||
{{range .VisitGone}}
|
||||
<tr>
|
||||
<td class="mono">{{dtt .Arrival}}</td>
|
||||
<td class="mono" data-label="离开">{{dtt .Departure}}</td>
|
||||
<td data-label="地点">{{if .Place}}{{.Place}}{{else}}<span class="muted">—</span>{{end}}</td>
|
||||
<td data-label="活动">{{if .Activity}}{{.Activity}}{{else}}<span class="muted">—</span>{{end}}</td>
|
||||
<td data-label="备注">{{if .Remark}}{{.Remark}}{{else}}<span class="muted">—</span>{{end}}</td>
|
||||
<td data-label="来源">{{if .UserAdded}}<span>站内补录</span>{{else}}<span class="muted">备份里已无</span>{{end}}</td>
|
||||
<td data-label="合并时删除">
|
||||
<input type="checkbox" form="merge-form" name="dv_{{.SrcPK}}" value="1" title="勾上=合并时删掉这条到访">
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{else}}<div class="muted" style="margin-bottom:14px">没有</div>{{end}}
|
||||
|
||||
<div class="card-sub" style="margin-top:16px"><strong>两边都有但不一样({{num .VisitCounts.Changed}})</strong> —— 左边是站内当前的值,右边是备份里的值</div>
|
||||
{{if .VisitClash}}
|
||||
<div class="tw">
|
||||
<table class="tb tb-stack">
|
||||
<thead><tr><th>到达</th><th>地点</th><th>活动</th><th>差异</th><th>合并时</th></tr></thead>
|
||||
<tbody>
|
||||
{{range .VisitClash}}
|
||||
<tr>
|
||||
<td class="mono">{{dtt .Arrival}}</td>
|
||||
<td data-label="地点">{{if .Place}}{{.Place}}{{else}}<span class="muted">—</span>{{end}}</td>
|
||||
<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">
|
||||
<span class="muted">{{.Label}}</span>
|
||||
<span style="text-decoration:line-through;opacity:.65">{{if .From}}{{.From}}{{else}}(空){{end}}</span>
|
||||
<span style="opacity:.5">→</span>
|
||||
<strong>{{if .To}}{{.To}}{{else}}(空){{end}}</strong>
|
||||
</div>
|
||||
{{end}}
|
||||
</td>
|
||||
<td data-label="合并时" style="white-space:nowrap">
|
||||
<label style="margin-right:8px"><input type="radio" form="merge-form" name="vc_{{.Key}}" value="backup" checked> 用实机</label>
|
||||
<label style="margin-right:8px"><input type="radio" form="merge-form" name="vc_{{.Key}}" value="db"> 用站内</label>
|
||||
<label><input type="radio" form="merge-form" name="vc_{{.Key}}" value="skip"> 跳过</label>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{else}}<div class="muted">没有</div>{{end}}
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>地点</h2>
|
||||
<div class="card-sub"><strong>备份里多出来的({{num .PlaceCounts.Added}})</strong></div>
|
||||
{{if .PlaceAdded}}
|
||||
<div class="tw">
|
||||
<table class="tb tb-stack">
|
||||
<thead><tr><th>名称</th><th>省 / 市</th><th>分类</th><th class="num">坐标</th></tr></thead>
|
||||
<tbody>
|
||||
{{range .PlaceAdded}}
|
||||
<tr>
|
||||
<td>{{if .Name}}{{.Name}}{{else}}<span class="muted">(未命名)</span>{{end}}</td>
|
||||
<td data-label="省 / 市">{{.Province}} {{.City}}</td>
|
||||
<td data-label="分类">{{if .POICategory}}{{.POICategory}}{{else}}<span class="muted">—</span>{{end}}</td>
|
||||
<td class="num mono" data-label="坐标">{{printf "%.5f" .Lat}}, {{printf "%.5f" .Lon}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{else}}<div class="muted" style="margin-bottom:14px">没有</div>{{end}}
|
||||
|
||||
<div class="card-sub" style="margin-top:16px">
|
||||
<strong>只在站内有({{num .PlaceCounts.Removed}})</strong>
|
||||
—— 名下还有到访的地点删不掉(会连带删掉那些到访),会直接报错让你先处理。
|
||||
</div>
|
||||
{{if .PlaceGone}}
|
||||
<div class="tw">
|
||||
<table class="tb tb-stack">
|
||||
<thead><tr><th>名称</th><th>省 / 市</th><th class="num">到访</th><th class="num">坐标</th><th>合并时删除</th></tr></thead>
|
||||
<tbody>
|
||||
{{range .PlaceGone}}
|
||||
<tr>
|
||||
<td>{{if .Name}}{{.Name}}{{else}}<span class="muted">(未命名)</span>{{end}}</td>
|
||||
<td data-label="省 / 市">{{.Province}} {{.City}}</td>
|
||||
<td class="num" data-label="到访">{{num .VisitCount}}</td>
|
||||
<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>
|
||||
{{else}}
|
||||
<input type="checkbox" form="merge-form" name="dp_{{.SrcPK}}" value="1">
|
||||
{{end}}
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{else}}<div class="muted" style="margin-bottom:14px">没有</div>{{end}}
|
||||
|
||||
<div class="card-sub" style="margin-top:16px"><strong>两边都有但地址不一样({{num .PlaceCounts.Changed}})</strong></div>
|
||||
{{if .PlaceClash}}
|
||||
<div class="tw">
|
||||
<table class="tb tb-stack">
|
||||
<thead><tr><th>名称</th><th>差异</th><th>合并时</th></tr></thead>
|
||||
<tbody>
|
||||
{{range .PlaceClash}}
|
||||
<tr>
|
||||
<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">
|
||||
<span class="muted">{{.Label}}</span>
|
||||
<span style="text-decoration:line-through;opacity:.65">{{if .From}}{{.From}}{{else}}(空){{end}}</span>
|
||||
<span style="opacity:.5">→</span>
|
||||
<strong>{{if .To}}{{.To}}{{else}}(空){{end}}</strong>
|
||||
</div>
|
||||
{{end}}
|
||||
</td>
|
||||
<td data-label="合并时" style="white-space:nowrap">
|
||||
<label style="margin-right:8px"><input type="radio" form="merge-form" name="pc_{{.DB.Key}}" value="backup" checked> 用实机</label>
|
||||
<label style="margin-right:8px"><input type="radio" form="merge-form" name="pc_{{.DB.Key}}" value="db"> 用站内</label>
|
||||
<label><input type="radio" form="merge-form" name="pc_{{.DB.Key}}" value="skip"> 跳过</label>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{else}}<div class="muted">没有</div>{{end}}
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>活动类型</h2>
|
||||
<div class="card-sub">
|
||||
新增 {{num .ActCounts.Added}} · 仅站内有 {{num .ActCounts.Removed}} · 冲突 {{num .ActCounts.Changed}} · 一致 {{num .ActCounts.Same}}
|
||||
</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>
|
||||
{{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>
|
||||
{{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>
|
||||
{{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}}
|
||||
<div style="margin-top:4px">
|
||||
<label style="margin-right:8px"><input type="radio" form="merge-form" name="ac_{{actKey .DB}}" value="backup" checked> 用实机</label>
|
||||
<label style="margin-right:8px"><input type="radio" form="merge-form" name="ac_{{actKey .DB}}" value="db"> 用站内</label>
|
||||
<label><input type="radio" form="merge-form" name="ac_{{actKey .DB}}" value="skip"> 跳过</label>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}<div class="muted">没有</div>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>其它实体(只列数量)</h2>
|
||||
<div class="tw">
|
||||
<table class="tb tb-stack">
|
||||
<thead><tr><th>实体</th><th class="num">备份</th><th class="num">站内</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>位移</td><td class="num" data-label="备份">{{num .BackupMovements}}</td><td class="num" data-label="站内">{{num .StoredMovements}}</td></tr>
|
||||
<tr><td>小时天气</td><td class="num" data-label="备份">{{num .BackupWeather}}</td><td class="num" data-label="站内">{{num .StoredWeather}}</td></tr>
|
||||
<tr><td>原始定位点</td><td class="num" data-label="备份">{{num .BackupRaw}}</td><td class="num" data-label="站内"><span class="muted">不单独落库</span></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-sub" style="margin-top:10px">
|
||||
位移跟着它的起止到访走、天气跟着到访走,所以这两项按到访对齐结果一并处理,不单独做逐条比对。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>执行合并</h2>
|
||||
<div class="card-sub">
|
||||
把选中的改动写进当前数据集「{{.DatasetName}}」。整个过程跑在一个事务里,任何一步失败都会<strong>整体回滚</strong>,数据不会半改。
|
||||
<br>「仅站内有」的记录<strong>默认一条都不删</strong>;确实要清掉的,在上面各表里逐条勾「合并时删除」。
|
||||
名下有到访的地点会被拒绝删除(否则会连带删掉那些到访),先处理到访再来。
|
||||
</div>
|
||||
<form id="merge-form" method="post" action="/admin/preview/{{.Token}}/merge"></form>
|
||||
<label style="display:flex;align-items:flex-start;gap:8px;margin:4px 0 12px">
|
||||
<input type="checkbox" form="merge-form" name="add_new" value="1" checked style="margin-top:3px">
|
||||
<span>把备份里多出来的
|
||||
<strong>{{num .VisitCounts.Added}}</strong> 条到访、
|
||||
<strong>{{num .PlaceCounts.Added}}</strong> 个地点、
|
||||
<strong>{{num .ActCounts.Added}}</strong> 个活动并进来
|
||||
<span class="muted">(新到访会连它的地点、活动、标签、位移与天气一起带过来)</span>
|
||||
</span>
|
||||
</label>
|
||||
<button class="btn primary" form="merge-form" type="submit"
|
||||
onclick="return confirm('确认把选中的改动写进当前数据集?出错会自动整体回滚。')">执行合并</button>
|
||||
<div class="card-sub" style="margin:10px 0 0">
|
||||
合并后再传一次同一份备份,差异应当变成「完全一致」——那说明对齐键(UUID 与坐标)都写进去了。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -0,0 +1,174 @@
|
||||
{{define "content"}}
|
||||
<div class="wrap">
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>迷雾导入预览</h1>
|
||||
<div class="sub">这份快照<strong>还没有写入</strong>。先看清两种导入方式各自的后果,再决定用哪一种。</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<a class="btn" href="/admin">← 返回后台</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>数量对比</h2>
|
||||
<div class="tw">
|
||||
<table class="tb tb-stack">
|
||||
<thead>
|
||||
<tr><th>状态</th><th class="num">块</th><th class="num">格</th><th class="num">面积</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>当前迷雾</td>
|
||||
<td class="num" data-label="块">{{num .Diff.CurBlocks}}</td>
|
||||
<td class="num" data-label="格">{{num .Diff.CurCells}}</td>
|
||||
<td class="num" data-label="面积">{{printf "%.1f" .Diff.CurAreaKm2}} km²</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>这份快照</td>
|
||||
<td class="num" data-label="块">{{num .Diff.IncBlocks}}</td>
|
||||
<td class="num" data-label="格">{{num .Diff.IncCells}}</td>
|
||||
<td class="num" data-label="面积">{{printf "%.1f" .Diff.IncAreaKm2}} km²</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>叠加导入后</td>
|
||||
<td class="num" data-label="块">{{num .Diff.CurBlocks}}<span class="muted"> +{{num .Diff.GainBlocks}}</span></td>
|
||||
<td class="num" data-label="格">{{num .Diff.CurCells}}<span class="muted"> +{{num .Diff.GainCells}}</span></td>
|
||||
<td class="num" data-label="面积">{{printf "%.1f" .Diff.MergeAreaKm2}} km²</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>整体替换后</td>
|
||||
<td class="num" data-label="块">{{num .Diff.IncBlocks}}</td>
|
||||
<td class="num" data-label="格">{{num .Diff.IncCells}}</td>
|
||||
<td class="num" data-label="面积">{{printf "%.1f" .Diff.CoverAreaKm2}} km²</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-sub" style="margin-top:10px">
|
||||
两个块坐标相同就只按位合并,所以「叠加后」的块数不会等于两边相加——快照里有 {{num .Diff.OverlapBlocks}} 个块与当前迷雾落在同一块,
|
||||
其中 {{num .Diff.SameBlocks}} 个一格都不会变。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{if .HasBounds}}
|
||||
<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">
|
||||
<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>
|
||||
<span style="display:flex;align-items:center;gap:6px">
|
||||
<i style="width:13px;height:13px;border-radius:3px;background:#7e8692;display:inline-block"></i>两边都有
|
||||
</span>
|
||||
<span style="display:flex;align-items:center;gap:6px">
|
||||
<i style="width:13px;height:13px;border-radius:3px;background:#d63838;display:inline-block"></i>整体替换会丢掉
|
||||
</span>
|
||||
</div>
|
||||
<div id="fog-diff-map" style="height:400px;border-radius:10px;background:var(--line-soft)"
|
||||
data-token="{{.Token}}"
|
||||
data-min-lat="{{printf "%.6f" .MinLat}}" data-min-lon="{{printf "%.6f" .MinLon}}"
|
||||
data-max-lat="{{printf "%.6f" .MaxLat}}" data-max-lon="{{printf "%.6f" .MaxLon}}"></div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="card">
|
||||
<h2>叠加导入</h2>
|
||||
{{if .Diff.Changed}}
|
||||
<div class="alert ok">
|
||||
会补上当前迷雾没有的 <strong>{{num .Diff.GainBlocks}}</strong> 块、<strong>{{num .Diff.GainCells}}</strong> 格
|
||||
(约 {{printf "%.1f" .Diff.GainAreaKm2}} km²)。叠加只做按位合并,<strong>不会丢失任何已有数据</strong>。
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="alert err">
|
||||
这份快照的点亮区域已经被当前迷雾<strong>完全覆盖</strong>——叠加导入不会带来任何变化,
|
||||
多半是同一批数据又导出了一次。
|
||||
</div>
|
||||
{{end}}
|
||||
<form method="post" action="/admin/fog?stash={{.Token}}"
|
||||
onsubmit="this.querySelector('button').disabled=true">
|
||||
<input type="hidden" name="mode" value="overlay">
|
||||
<button class="btn primary" type="submit">叠加导入</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>整体替换</h2>
|
||||
{{if gt .Diff.LostCells 0}}
|
||||
<div class="alert err">
|
||||
⚠️ 快照里<strong>没有</strong>当前迷雾的 <strong>{{num .Diff.LostBlocks}}</strong> 块、<strong>{{num .Diff.LostCells}}</strong> 格
|
||||
(约 {{printf "%.1f" .Diff.LostAreaKm2}} km²)。整体替换后,这些区域会<strong>从点亮变回未探索</strong>,且无法恢复。
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="alert ok">
|
||||
这份快照完整包含了当前迷雾的全部点亮区域,替换不会丢数据。
|
||||
</div>
|
||||
{{end}}
|
||||
{{if and (eq .TildeLayers 0) (eq .HashLayers 0)}}
|
||||
<div class="alert err">
|
||||
⚠️ 快照里没有 <span class="mono">Model/~</span> 与 <span class="mono">Model/#</span> 两层数据,替换会把现有的这两层一并清掉。
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="muted" style="font-size:12.5px;margin-bottom:8px">
|
||||
另外,站点不解析但会留存的 <span class="mono">Model/~</span>({{.TildeLayers}} 项)与
|
||||
<span class="mono">Model/#</span>({{.HashLayers}} 项)也会一并换成这份快照里的{{if .OtherLayers}},另有 {{.OtherLayers}} 项其它条目{{end}}。
|
||||
</div>
|
||||
{{end}}
|
||||
<form method="post" action="/admin/fog?stash={{.Token}}"
|
||||
onsubmit="return confirm('整体替换会清空当前迷雾。{{if gt .Diff.LostCells 0}}快照里缺 {{num .Diff.LostCells}} 格,替换后这些区域会变回未探索,且不可恢复。{{end}}确认替换?')">
|
||||
<input type="hidden" name="mode" value="cover">
|
||||
<button class="btn danger" type="submit">整体替换</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
// Leaflet 由 layout 在 content 之后引入,脚本执行时 window.L 还没有,
|
||||
// 必须等 load 之后再初始化,否则地图整个不出现(与迷雾遮罩页同一条约定)
|
||||
function whenReady(cb) {
|
||||
if (document.readyState === 'complete') { setTimeout(cb, 0); return; }
|
||||
window.addEventListener('load', cb);
|
||||
}
|
||||
|
||||
var el = document.getElementById('fog-diff-map');
|
||||
if (!el) return;
|
||||
|
||||
// 底图走站点配置(与迷雾遮罩页同一套取法):带密钥的图层由服务端转发,
|
||||
// 免密钥的是浏览器直连的 {s} 模板,不能硬写 /tiles/ 路由
|
||||
var fallback = {
|
||||
def: { url: 'https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', maxZoom: 18, attr: '© 高德地图', crs: 'gcj02' },
|
||||
subs: ['1', '2', '3', '4']
|
||||
};
|
||||
|
||||
function init(cfg) {
|
||||
var def = cfg.def;
|
||||
var map = L.map(el, { scrollWheelZoom: false });
|
||||
L.tileLayer(def.url, { subdomains: cfg.subs, maxZoom: def.maxZoom || 18, minZoom: 3, attribution: def.attr || '' }).addTo(map);
|
||||
// 差异瓦片必须和底图同坐标系:迷雾网格是 WGS-84,服务端按 crs 逐块纠偏
|
||||
L.tileLayer('/admin/fog/preview/' + el.dataset.token + '/{z}/{x}/{y}?crs=' + (def.crs || 'gcj02'), {
|
||||
minZoom: 3, maxZoom: 18, opacity: 0.95
|
||||
}).addTo(map);
|
||||
var b = [
|
||||
[parseFloat(el.dataset.minLat), parseFloat(el.dataset.minLon)],
|
||||
[parseFloat(el.dataset.maxLat), parseFloat(el.dataset.maxLon)]
|
||||
];
|
||||
// 只有一个块时两个角重合,不设上限会一路放大到最大级别
|
||||
map.fitBounds(b, { padding: [24, 24], maxZoom: 13 });
|
||||
L.control.scale({ imperial: false }).addTo(map);
|
||||
}
|
||||
|
||||
whenReady(function () {
|
||||
if (!window.L) return;
|
||||
fetch('/api/map-config', { headers: { 'Accept': 'application/json' } })
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (c) {
|
||||
var def = (c && c.base && c.base.length) ? c.base[0] : fallback.def;
|
||||
init({ def: def, subs: (c && c.subs && c.subs.length) ? c.subs : fallback.subs });
|
||||
})
|
||||
.catch(function () { init(fallback); });
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
{{end}}
|
||||
@@ -29,6 +29,7 @@
|
||||
<b>上传是以备份为准重建数据</b>:站点里删掉的地点、改过的记录,只要下次传一份
|
||||
<b>内容不同</b>的备份,就会被备份里的原样覆盖回来(重复传同一份文件不会,会提示「完全一致」)。
|
||||
在站点上新加的地点属于当时那个数据集,传新备份后要看它们,可在下面的数据集列表里切回。
|
||||
<br><b>拿不准就先用「分析差异」</b>:它会逐条比对这份备份与站内当前数据,告诉你哪些是新增、哪些只在站内有、哪些两边不一样,看完再决定要不要导入。
|
||||
</div>
|
||||
<form method="post" action="/admin/upload" enctype="multipart/form-data">
|
||||
<label class="f" style="max-width:520px">
|
||||
@@ -36,7 +37,10 @@
|
||||
<input type="file" name="backup" accept=".rondbackup,.zip" required
|
||||
style="padding:8px;border:1px dashed var(--line);border-radius:8px;background:var(--bg-2)">
|
||||
</label>
|
||||
<button class="btn primary" type="submit">上传并导入</button>
|
||||
<div class="row">
|
||||
<button class="btn primary" type="submit" formaction="/admin/preview">分析差异</button>
|
||||
<button class="btn" type="submit" formaction="/admin/upload">直接导入</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -53,21 +57,18 @@
|
||||
<b>尚未导入迷雾</b>——把 Fog of World 的 <span class="mono">.fwss</span>(在 App 内导出,通常在 zip 里)上传即可点亮地图。
|
||||
{{end}}
|
||||
</div>
|
||||
<form method="post" action="/admin/fog" enctype="multipart/form-data">
|
||||
<form method="post" action="/admin/fog/preview" enctype="multipart/form-data">
|
||||
<label class="f" style="max-width:520px">
|
||||
<span>选择快照</span>
|
||||
<input type="file" name="fwss" accept=".fwss,.zip" required
|
||||
style="padding:8px;border:1px dashed var(--line);border-radius:8px;background:var(--bg-2)">
|
||||
</label>
|
||||
<label class="f" style="max-width:520px">
|
||||
<span>导入方式</span>
|
||||
<select name="mode">
|
||||
<option value="overlay" selected>叠加 — 保留现有迷雾,把新路线合并进来</option>
|
||||
<option value="cover">覆盖 — 清空现有迷雾,整体替换</option>
|
||||
</select>
|
||||
</label>
|
||||
<button class="btn primary" type="submit">导入迷雾</button>
|
||||
<button class="btn primary" type="submit">分析差异</button>
|
||||
</form>
|
||||
<div class="muted" style="font-size:12.5px;margin-top:8px">
|
||||
上传后<strong>先不写入</strong>:会先给出这份快照能补多少新区域、以及「整体替换」会丢掉多少已有区域,
|
||||
确认之后才真正导入。
|
||||
</div>
|
||||
{{if gt .FogBlocks 0}}
|
||||
<div style="margin:10px 0 0;display:flex;gap:8px;flex-wrap:wrap">
|
||||
<a class="btn" href="/admin/fog/export">导出当前迷雾(.fwss)</a>
|
||||
|
||||
Reference in New Issue
Block a user