备份(.rondbackup)与迷雾(.fwss)各加一层「先看差异再动手」: - 迷雾:上传先出差异页,说清叠加能补多少、整体替换会丢多少,并在真实底图上 用绿/灰/红标出会新增 / 不变 / 会丢的区域 - 备份:与当前数据集按 UUID 逐条比对(rond 每次导出都会重排 Z_PK,编号认不出人), 新增合入、冲突逐条选、仅站内有逐条勾选删除,全在一个事务里执行; 合入的行写回 entity_raw,保证再传同一份备份时差异归零 - 地点按坐标对齐:站点的「补全地址」会把地点改名,拿名称当键会让一整个地点的 到访全变成假冲突 - 附属数据(备注/结论/访客票/图片)从 dataset_id+src_pk 换成坐标键,换数据集不再失联; 访客票没有 dataset_id,回填按活跃数据集认领 - 修正 contentHash 拿 Z_PK 当指纹的隐患,以及两处「src_pk 跨快照稳定」的错误注释 导出格式(.rondbackup / .fwss)保持原样,往返测试通过。
175 lines
8.1 KiB
HTML
175 lines
8.1 KiB
HTML
{{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}}
|