142 lines
7.5 KiB
HTML
142 lines
7.5 KiB
HTML
<!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}
|
||
*{box-sizing:border-box;margin:0;padding:0}
|
||
body{font:14px/1.5 Arial,sans-serif;color:var(--ink);background:var(--gray-100);padding:20px}
|
||
.container{max-width:960px;margin:0 auto}
|
||
h1{font-size:22px;margin-bottom:16px}h1 span{color:var(--cyan)}
|
||
.drop-zone{border:3px dashed var(--cyan);border-radius:12px;padding:50px 24px;text-align:center;cursor:pointer;background:var(--white);margin-bottom:20px}
|
||
.drop-zone:hover{background:var(--cyan-50)}
|
||
.drop-zone .icon{font-size:40px;display:block;margin-bottom:8px}
|
||
.drop-zone .txt{font-size:16px;font-weight:600;margin-bottom:4px}
|
||
.drop-zone .hint{font-size:13px;color:var(--gray-500)}
|
||
.drop-zone input{display:none}
|
||
.ev{border:1px solid var(--gray-200);border-radius:10px;padding:18px;margin-bottom:14px;background:var(--white)}
|
||
.ev h3{font-size:15px;margin-bottom:6px}
|
||
.meta{display:flex;gap:12px;flex-wrap:wrap;font-size:12px;color:var(--gray-500);margin-bottom:6px}
|
||
.meta strong{color:var(--ink)}
|
||
.badge{display:inline-block;padding:2px 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:6px;margin:6px 0}
|
||
.month strong{font-size:12px}.month .rep{font-size:13px;color:var(--gray-500);margin:2px 0}
|
||
.file-item{display:flex;align-items:center;gap:8px;padding:3px 0;font-size:13px}
|
||
.file-item a{color:var(--ink);font-weight:600;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:6px 0 6px 16px}
|
||
.sub-blk strong{font-size:12px}
|
||
.err{color:var(--red);text-align:center;padding:20px}
|
||
.empty{text-align:center;padding:40px;color:var(--gray-500)}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<h1><span>📂 Просмотр бекапа</span> ИИ-Агент ПБ</h1>
|
||
|
||
<div class="drop-zone" id="dropZone">
|
||
<span class="icon">📥</span>
|
||
<div class="txt">Нажмите или перетащите файл бекапа</div>
|
||
<div class="hint">Файл backup_pb_*.json из папки Загрузки</div>
|
||
<input type="file" id="fileInput" accept=".json">
|
||
</div>
|
||
|
||
<div id="result"></div>
|
||
</div>
|
||
|
||
<script>
|
||
var sm={done:"Исполнено",warn:"На контроле",late:"Просрочено",wait:"В процессе"};
|
||
var sec=["I. Люди","II. Оборудование","III. Аварии и ЧС","IV. Информ. работа","V. ИИ и цифровизация"];
|
||
var br=["Дирекция ПБ","Дивизион Сеть","Корп. бизнес","Розн. бизнес","Сервисная фабрика","Телеком Комплект","Корп. университет","Упр. проектами","Цифровой бизнес"];
|
||
var mn=["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"];
|
||
function esc(s){return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}
|
||
function M(k){var p=k.split("-");return mn[parseInt(p[1])-1]+" "+p[0]}
|
||
|
||
var dz=document.getElementById("dropZone"),fi=document.getElementById("fileInput");
|
||
|
||
dz.addEventListener("click",function(){fi.click()});
|
||
fi.addEventListener("change",function(){if(this.files.length)processFile(this.files[0])});
|
||
dz.addEventListener("dragover",function(e){e.preventDefault();dz.style.background="var(--cyan-50)"});
|
||
dz.addEventListener("dragleave",function(){dz.style.background="var(--white)"});
|
||
dz.addEventListener("drop",function(e){e.preventDefault();dz.style.background="var(--white)";if(e.dataTransfer.files.length)processFile(e.dataTransfer.files[0])});
|
||
|
||
function processFile(file){
|
||
if(!file){showErr("Файл не выбран");return}
|
||
if(!file.name.endsWith(".json")){showErr("Выберите .json файл");return}
|
||
var r=new FileReader();
|
||
r.onload=function(ev){
|
||
try{
|
||
var data=JSON.parse(ev.target.result);
|
||
if(!data.events){showErr("Неверный формат: нет поля events");return}
|
||
render(data);
|
||
}catch(e){showErr("Ошибка чтения JSON: "+e.message)}
|
||
};
|
||
r.onerror=function(){showErr("Не удалось прочитать файл")};
|
||
r.readAsText(file);
|
||
}
|
||
|
||
function showErr(msg){document.getElementById("result").innerHTML='<div class="err">❌ '+esc(msg)+'</div>'}
|
||
|
||
function getFiles(data,id,si){
|
||
var k=si>=0?"sf_"+id+"_s"+si:"sf_"+id;
|
||
if(!data.files||!data.files[k])return{};
|
||
var raw=data.files[k];
|
||
if(typeof raw==="string"){try{return JSON.parse(raw)}catch(e){return{}}}
|
||
return raw;
|
||
}
|
||
|
||
function render(data){
|
||
var h="";
|
||
h+="<p style='margin-bottom:16px;color:var(--gray-500)'>Дата: <strong>"+(data.date?new Date(data.date).toLocaleString():"—")+"</strong> · Мероприятий: <strong>"+data.events.length+"</strong></p>";
|
||
|
||
data.events.forEach(function(e){
|
||
var cl={done:"g",warn:"a",late:"r",wait:"w"}[e.s]||"w";
|
||
h+="<div class='ev'><h3>"+e.id+". "+esc(e.t)+"</h3>";
|
||
h+="<div class='meta'><span>Раздел: <strong>"+sec[e.sec]+"</strong></span><span>Дивизион: <strong>"+br[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 "+cl+"'>"+sm[e.s]+"</span></div>";
|
||
|
||
// Main event files
|
||
var fd=getFiles(data,e.id,-1);
|
||
var keys=Object.keys(fd).sort();
|
||
keys.forEach(function(key){
|
||
var d=fd[key];
|
||
if(!d.report&&(!d.files||!d.files.length))return;
|
||
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'>"+(f.data?"<a href='"+f.data+"' download='"+esc(f.name)+"'>📄 "+esc(f.name)+"</a>":"📄 "+esc(f.name))+"<span class='sz'>"+(f.desc?esc(f.desc)+" · ":"")+(f.size?(f.size/1024).toFixed(0)+" КБ":"")+"</span></div>";
|
||
});
|
||
h+="</div>";
|
||
});
|
||
|
||
// Sub-items
|
||
if(e.sub)e.sub.forEach(function(s,i){
|
||
var sd=getFiles(data,e.id,i);
|
||
var skeys=Object.keys(sd).sort(),hasSub=false;
|
||
skeys.forEach(function(k){if(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>";
|
||
skeys.forEach(function(key){
|
||
var d=sd[key];
|
||
if(!d.report&&(!d.files||!d.files.length))return;
|
||
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'>"+(f.data?"<a href='"+f.data+"' download='"+esc(f.name)+"'>📄 "+esc(f.name)+"</a>":"📄 "+esc(f.name))+"<span class='sz'>"+(f.desc?esc(f.desc)+" · ":"")+(f.size?(f.size/1024).toFixed(0)+" КБ":"")+"</span></div>";
|
||
});
|
||
h+="</div>";
|
||
});
|
||
h+="</div>";
|
||
});
|
||
|
||
h+="</div>";
|
||
});
|
||
|
||
document.getElementById("result").innerHTML=h||"<div class='empty'>Нет данных</div>";
|
||
dz.style.display="none";
|
||
}
|
||
</script>
|
||
</body>
|
||
</html> |