v27: просмотрщик бекапов — открывает JSON и даёт скачать файлы
This commit is contained in:
parent
9e900d1c3d
commit
e43471efcc
117
viewer.html
Normal file
117
viewer.html
Normal file
@ -0,0 +1,117 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Просмотр бекапа ИИ-Агент ПБ</title>
|
||||
<style>
|
||||
:root{--ink:#0F1218;--cyan:#00E5FF;--cyan-50:#E8FCFF;--white:#fff;--gray-500:#5B6573;--gray-100:#F2F4F7;--gray-200:#E5E7EB;--green:#10B981;--red:#EF4444;--amber:#F59E0B;--blue:#3B82F6}
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{font:14px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;color:var(--ink);background:var(--gray-100);min-height:100vh;padding:40px 24px}
|
||||
.container{max-width:900px;margin:0 auto}
|
||||
h1{font-size:24px;font-weight:800;margin-bottom:8px}h1 span{color:var(--cyan)}
|
||||
p.sub{color:var(--gray-500);margin-bottom:24px}
|
||||
.drop-zone{border:3px dashed var(--cyan);border-radius:16px;padding:60px 24px;text-align:center;cursor:pointer;background:var(--white);margin-bottom:24px;transition:.15s}
|
||||
.drop-zone:hover{background:var(--cyan-50)}
|
||||
.drop-zone .icon{font-size:48px;display:block;margin-bottom:8px}
|
||||
.drop-zone .txt{font-size:18px;font-weight:600;color:var(--ink);margin-bottom:4px}
|
||||
.drop-zone .hint{font-size:14px;color:var(--gray-500)}
|
||||
.ev{border:1px solid var(--gray-200);border-radius:12px;padding:20px;margin-bottom:16px;background:var(--white)}
|
||||
.ev h3{font-size:16px;margin-bottom:8px}
|
||||
.meta{display:flex;gap:16px;flex-wrap:wrap;font-size:12px;color:var(--gray-500);margin-bottom:8px}
|
||||
.meta strong{color:var(--ink)}
|
||||
.badge{display:inline-block;padding:3px 8px;border-radius:4px;font-size:11px;font-weight:700}
|
||||
.g{background:#D1FAE5;color:#065F46}.a{background:#FEF3C7;color:#92400E}.r{background:#FEE2E2;color:#991B1B}.w{background:#eee;color:#666}
|
||||
.month{background:var(--gray-100);padding:10px 14px;border-radius:8px;margin:8px 0}
|
||||
.month strong{font-size:13px;color:var(--ink)}
|
||||
.month .rep{font-size:13px;color:var(--gray-500);margin:4px 0}
|
||||
.file-item{display:flex;align-items:center;gap:8px;padding:4px 0;font-size:13px}
|
||||
.file-item a{color:var(--ink);font-weight:600;cursor:pointer;text-decoration:underline}
|
||||
.file-item a:hover{color:var(--cyan)}
|
||||
.file-item .sz{font-size:11px;color:var(--gray-500)}
|
||||
.sub-blk{border-left:3px solid var(--cyan);padding-left:14px;margin:8px 0 8px 12px}
|
||||
.sub-blk strong{font-size:13px}
|
||||
.no-data{text-align:center;padding:40px;color:var(--gray-500);font-size:16px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1><span>📂 Просмотр бекапа</span> ИИ-Агент ПБ</h1>
|
||||
<p class="sub">Загрузите файл backup_pb_*.json, сохранённый из платформы</p>
|
||||
|
||||
<div class="drop-zone" id="dropZone" onclick="document.getElementById('fileInput').click()">
|
||||
<span class="icon">📥</span>
|
||||
<div class="txt">Нажмите или перетащите файл бекапа</div>
|
||||
<div class="hint">Файл backup_pb_YYYY-MM-DD.json</div>
|
||||
</div>
|
||||
<input type="file" id="fileInput" accept=".json" style="display:none" onchange="loadBackup(this.files[0])">
|
||||
|
||||
<div id="content"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var statusMap={done:"Исполнено",warn:"На контроле",late:"Просрочено",wait:"В процессе"};
|
||||
var sections=["I. Люди","II. Оборудование","III. Аварии и ЧС","IV. Информ. работа","V. ИИ и цифровизация"];
|
||||
var branches=["Дирекция ПБ","Дивизион «Сеть»","Корпоративный бизнес","Розничный бизнес","Сервисная фабрика","Телеком Комплект","Корпоративный университет","Управление проектами","Цифровой бизнес"];
|
||||
var monthNames=["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"];
|
||||
function esc(s){return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}
|
||||
function M(k){var p=k.split("-");return monthNames[parseInt(p[1])-1]+" "+p[0]}
|
||||
|
||||
var dropZone=document.getElementById("dropZone");
|
||||
dropZone.addEventListener("dragover",function(e){e.preventDefault();this.style.background="var(--cyan-50)"});
|
||||
dropZone.addEventListener("dragleave",function(e){this.style.background="var(--white)"});
|
||||
dropZone.addEventListener("drop",function(e){e.preventDefault();this.style.background="var(--white)";loadBackup(e.dataTransfer.files[0])});
|
||||
|
||||
function loadBackup(file){
|
||||
if(!file){alert("Выберите файл");return}
|
||||
var reader=new FileReader();
|
||||
reader.onload=function(ev){
|
||||
try{
|
||||
var data=JSON.parse(ev.target.result);
|
||||
if(!data.events||!data.files){alert("❌ Неверный формат файла");return}
|
||||
renderBackup(data);
|
||||
}catch(e){alert("❌ Ошибка чтения файла: "+e.message)}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
function getMD(data,id,si){var k=si>=0?'sf_'+id+'_s'+si:'sf_'+id;var raw=data.files[k];return raw?JSON.parse(raw):{}}
|
||||
|
||||
function renderBackup(data){
|
||||
var h='';
|
||||
if(data.date)h+='<p style="margin-bottom:16px;color:var(--gray-500)">Дата бекапа: <strong>'+new Date(data.date).toLocaleString()+'</strong> · Мероприятий: '+data.events.length+'</p>';
|
||||
data.events.forEach(function(e){
|
||||
var scls={done:"g",warn:"a",late:"r",wait:"w"}[e.s];
|
||||
h+='<div class="ev"><h3>'+e.id+'. '+esc(e.t)+'</h3>';
|
||||
h+='<div class="meta"><span>Раздел: <strong>'+sections[e.sec]+'</strong></span><span>Дивизион: <strong>'+branches[e.b]+'</strong></span><span>Срок: <strong>'+e.due+'</strong></span><span>Факт: <strong>'+(e.done||"—")+'</strong></span><span>Прогресс: <strong>'+e.p+'%</strong></span><span class="badge '+scls+'">'+statusMap[e.s]+'</span></div>';
|
||||
|
||||
// Main event files
|
||||
var md=getMD(data,e.id,-1);
|
||||
for(var key in md){if(md.hasOwnProperty(key)&&md[key]){
|
||||
var d=md[key];if(d.report||(d.files&&d.files.length)){
|
||||
h+='<div class="month"><strong>'+M(key)+'</strong>';
|
||||
if(d.report)h+='<div class="rep">'+esc(d.report)+'</div>';
|
||||
if(d.files&&d.files.length)d.files.forEach(function(f){h+='<div class="file-item"><a href="'+f.data+'" download="'+esc(f.name)+'">📄 '+esc(f.name)+'</a><span class="sz">'+esc(f.desc||'')+' ('+(f.size/1024).toFixed(0)+' КБ)</span></div>'});
|
||||
h+='</div>'}}}}
|
||||
|
||||
// Sub-items
|
||||
if(e.sub)e.sub.forEach(function(s,i){
|
||||
var sd=getMD(data,e.id,i),hasSub=false;
|
||||
for(var k in sd){if(sd.hasOwnProperty(k)&&sd[k]&&(sd[k].report||(sd[k].files&&sd[k].files.length)))hasSub=true}
|
||||
if(!hasSub)return;
|
||||
h+='<div class="sub-blk"><strong>'+s.l+') '+esc(s.t)+'</strong>';
|
||||
for(var key in sd){if(sd.hasOwnProperty(key)&&sd[key]){
|
||||
var d=sd[key];if(d.report||(d.files&&d.files.length)){
|
||||
h+='<div class="month"><strong>'+M(key)+'</strong>';
|
||||
if(d.report)h+='<div class="rep">'+esc(d.report)+'</div>';
|
||||
if(d.files&&d.files.length)d.files.forEach(function(f){h+='<div class="file-item"><a href="'+f.data+'" download="'+esc(f.name)+'">📄 '+esc(f.name)+'</a><span class="sz">'+esc(f.desc||'')+' ('+(f.size/1024).toFixed(0)+' КБ)</span></div>'});
|
||||
h+='</div>'}}}
|
||||
h+='</div>'});
|
||||
h+='</div>';
|
||||
});
|
||||
document.getElementById("content").innerHTML=h||'<div class="no-data">Нет данных для отображения</div>';
|
||||
document.getElementById("dropZone").style.display="none";
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user