v94: Jarvis AI, per-sub-item notes, HTML export files per event
This commit is contained in:
parent
5bfab18e7e
commit
c132f20bdd
78
index.html
78
index.html
@ -83,7 +83,7 @@ tr:hover{background:#FAFBFC}
|
||||
<a class=" active" id="snav_events" onclick="switchTab('events')"><span>Мероприятия</span></a>
|
||||
<a class="" id="snav_analytics" onclick="switchTab('analytics')"><span>Аналитика</span></a>
|
||||
<a class="" id="snav_reports" onclick="switchTab('reports')"><span>Отчётность</span></a>
|
||||
<a class="" id="snav_ai" onclick="switchTab('ai')"><span>ИИ-помощник</span></a>
|
||||
<a class="" id="snav_ai" onclick="switchTab('ai')"><span>Джарвис</span></a>
|
||||
<a class="" id="snav_hse" onclick="switchTab('hse')"><span>HSE.sk.kz</span></a>
|
||||
<a class="" id="snav_users" onclick="switchTab('users')"><span>Учётные записи</span></a>
|
||||
<div class="logout"><button class="btn btn-sm btn-r" style="width:100%" onclick="doLogout()">Выйти</button></div>
|
||||
@ -190,9 +190,10 @@ tr:hover{background:#FAFBFC}
|
||||
<button onclick="aiAsk('сводка')">Сводка</button>
|
||||
<button onclick="aiAsk('просроченные')">Просроченные</button>
|
||||
<button onclick="aiAsk('риски')">Риски</button>
|
||||
<button onclick="aiAsk('рейтинг')">Рейтинг филиалов</button>
|
||||
<button onclick="aiAsk('совет')">Советник</button>
|
||||
<button onclick="aiAsk('аудит')">Аудит</button>
|
||||
<button onclick="aiAsk('рейтинг')">Рейтинг</button>
|
||||
<button onclick="aiAsk('прогноз')">Прогноз</button>
|
||||
<button onclick="aiAsk('статус')">Статус</button>
|
||||
<button onclick="aiAsk('план')">План действий</button>
|
||||
</div>
|
||||
<div class="chat-box" id="ai_chat"></div>
|
||||
<div class="chat-inp"><input id="ai_inp" placeholder="Спроси про план ПБ..." onkeydown="if(event.key=='Enter')aiSend()">
|
||||
@ -323,7 +324,7 @@ function saveEv(){
|
||||
function switchTab(t){
|
||||
tab=t;
|
||||
var tabs=["events","analytics","reports","ai","users","hse"];
|
||||
var tn={events:"Мероприятия",analytics:"Аналитика",reports:"Отчётность",ai:"ИИ-помощник",users:"Учётные записи",hse:"HSE.sk.kz"};
|
||||
var tn={events:"Мероприятия",analytics:"Аналитика",reports:"Отчётность",ai:"Джарвис",users:"Учётные записи",hse:"HSE.sk.kz"};
|
||||
for(var i=0;i<tabs.length;i++){
|
||||
var el=document.getElementById("tab_"+tabs[i]);
|
||||
if(el)el.style.display="none";
|
||||
@ -472,7 +473,7 @@ function openEv(id,subIdx){
|
||||
h+=">"+mnames[mi]+"</option>"
|
||||
}
|
||||
h+="</select></div>";
|
||||
h+="<div style='margin-bottom:12px'><textarea id='evn_"+e.id+"' placeholder='Примечание / описание выполнения...' style='width:100%;padding:8px;border:1px solid #E2E8F0;border-radius:6px;font-size:12px;resize:vertical;min-height:50px'>"+esc(e.n||"")+"</textarea></div>";
|
||||
h+="<div style='margin-bottom:12px'><textarea id='evn_"+e.id+"' placeholder='Примечание / описание выполнения...' style='width:100%;padding:8px;border:1px solid #E2E8F0;border-radius:6px;font-size:12px;resize:vertical;min-height:50px'>"+esc(subIdx!==undefined?((localStorage.getItem("sn_"+e.id+"_s"+subIdx)||"")):(e.n||""))+"</textarea></div>";
|
||||
h+="<div style='margin-bottom:12px'><strong>Файлы:</strong>";
|
||||
if(cu&&cu.bg===0){
|
||||
for(var bi=0;bi<brs.length;bi++){
|
||||
@ -539,7 +540,7 @@ function saveEvModal(id){
|
||||
if(evs[i].id===id){
|
||||
if(sel)evs[i].s=sel.value;
|
||||
if(inq)evs[i].q=parseInt(inq.value,10)||0;
|
||||
if(inn)evs[i].n=inn.value.trim();
|
||||
if(inn){if(curSub!==null){try{localStorage.setItem("sn_"+id+"_s"+curSub,inn.value.trim())}catch(e){}}else{evs[i].n=inn.value.trim()}}
|
||||
if(sel&&sel.value==="done"&&(evs[i].done==="\u2014"||!evs[i].done)){
|
||||
var d=new Date();
|
||||
evs[i].done=d.getDate()+"."+String(d.getMonth()+1).padStart(2,"0")+"."+d.getFullYear()
|
||||
@ -754,11 +755,36 @@ function dlHTML(){
|
||||
var fl=getFilteredEvs();
|
||||
var month=parseInt(document.getElementById("rp_month").value,10)+1;
|
||||
var year=document.getElementById("rp_year").value;
|
||||
var hh="<!DOCTYPE html><html><head><meta charset='utf-8'><title>Отчёт План ПБ "+month+"."+year+"</title><style>body{font:14px Arial;padding:20px}table{border-collapse:collapse;width:100%}th,td{border:1px solid #ccc;padding:6px 10px;font-size:12px;text-align:left}th{background:#0B1A2E;color:#fff}.s{background:#F1F5F9;font-weight:700}</style></head><body><h2>План производственной безопасности</h2><p>AO Казахтелеком за "+month+"."+year+"</p><br><table><tr><th>N</th><th>Мероприятие</th><th>Срок</th><th>Статус</th><th>Кол-во</th><th>Примечание</th></tr>";
|
||||
var hh="<!DOCTYPE html><html><head><meta charset='utf-8'><title>Отчёт План ПБ "+month+"."+year+"</title><style>body{font:14px Arial;padding:20px}table{border-collapse:collapse;width:100%}th,td{border:1px solid #ccc;padding:6px 10px;font-size:12px;text-align:left;vertical-align:top}th{background:#0B1A2E;color:#fff}.files{margin-top:4px;font-size:11px}.files a{color:#00B4D8;display:block}</style></head><body><h2>План производственной безопасности</h2><p>АО Казахтелеком за "+month+"."+year+"</p><br><table><tr><th>N</th><th>Мероприятие</th><th>Срок</th><th>Статус</th><th>Кол-во</th><th>Примечание / Файлы</th></tr>";
|
||||
for(var i=0;i<fl.length;i++){
|
||||
var e=fl[i];
|
||||
hh+="<tr><td>"+e.id+"</td><td>"+esc(e.t)+"</td><td>"+e.due+"</td><td>"+stn[e.s]+"</td><td>"+(e.q||"")+"</td><td>"+esc(e.n||"")+"</td></tr>"
|
||||
var notes=esc(e.n||"");
|
||||
var fhtml="";
|
||||
for(var si=-1;si<(e.sub?e.sub.length:0);si++){
|
||||
var sk=si>=0?"_s"+si:"";
|
||||
for(var bk=0;bk<brs.length;bk++){
|
||||
var key="sf_"+e.id+sk+"_b"+bk;
|
||||
var fd=localStorage.getItem(key);
|
||||
if(fd){
|
||||
try{var arr=JSON.parse(fd);
|
||||
for(var fi=0;fi<arr.length;fi++){
|
||||
var f=arr[fi];
|
||||
var lb=si>=0?e.sub[si].l:"";
|
||||
fhtml+="<a href='"+f.data+"' download='"+esc(f.n)+"'>"+esc((lb?lb+". ":"")+f.n)+" ("+Math.round(f.s/1024)+" KB"+", "+esc(f.u||"")+")</a>"
|
||||
}}catch(ex){}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(fhtml)fhtml="<div class='files'>"+fhtml+"</div>";
|
||||
hh+="<tr><td>"+e.id+"</td><td>"+esc(e.t)+"</td><td>"+e.due+"</td><td>"+stn[e.s]+"</td><td>"+(e.q||"")+"</td><td>"+notes+fhtml+"</td></tr>"
|
||||
}
|
||||
hh+="</table><p><br><em>Отчёт сформирован: "+new Date().toLocaleDateString("ru-RU")+"</em></p></body></html>";
|
||||
var blob=new Blob([hh],{type:"text/html"});
|
||||
var a=document.createElement("a");
|
||||
a.href=URL.createObjectURL(blob);
|
||||
a.download="report_pb_"+year+"_"+month+".html";
|
||||
a.click()
|
||||
}
|
||||
hh+="</table>";
|
||||
hh+="<br><h3>Приложенные файлы</h3>";
|
||||
var hasFiles=false;
|
||||
@ -848,7 +874,7 @@ function renderAI(){
|
||||
var box=document.getElementById("ai_chat");
|
||||
if(box){
|
||||
box.innerHTML="";
|
||||
addMsg("b","\u041F\u0440\u0438\u0432\u0435\u0442! \u042F AI-\u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043D\u0442 \u043F\u043E \u043F\u043B\u0430\u043D\u0443 \u043F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0441\u0442\u0432\u0435\u043D\u043D\u043E\u0439 \u0431\u0435\u0437\u043E\u043F\u0430\u0441\u043D\u043E\u0441\u0442\u0438. \u0427\u0442\u043E \u0442\u0435\u0431\u044F \u0438\u043D\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442?","\u0411\u043E\u0442")
|
||||
addMsg("b","Джарвис к вашим услугам. Я анализирую 35 мероприятий ПБ по 9 филиалам. Спросите: сводка, просроченные, риски, рейтинг, аудит, прогноз, советник.","Джарвис")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -923,7 +949,33 @@ function aiResp(q){
|
||||
ans+="\n"+(i+1)+". "+brd[i].n+": "+brd[i].d+"/"+brd[i].t+" ("+pct+"%)"
|
||||
}
|
||||
|
||||
}else if(ql.indexOf("совет")!==-1||ql.indexOf("рекоменд")!==-1){
|
||||
}else if(ql.indexOf("прогноз")!==-1||ql.indexOf("прогн")!==-1){
|
||||
var atRisk=0,crit=0,onTrack=0;
|
||||
for(var i=0;i<evs.length;i++){
|
||||
var dr=daysRem(evs[i].due);
|
||||
if(evs[i].s==="done")onTrack++;
|
||||
else if(dr<=0)crit++;
|
||||
else if(dr<=30)atRisk++;
|
||||
else onTrack++
|
||||
}
|
||||
ans="Прогноз выполнения плана ПБ:\n- Выполнено: "+onTrack+"\n- В зоне риска (<30 дн): "+atRisk+"\n- Критические (просрочено): "+crit;
|
||||
ans+="\n\nПрогнозируемый % выполнения к концу года: "+Math.round((onTrack+atRisk*0.5)/evs.length*100)+"%";
|
||||
if(crit>3)ans+="\n\nРекомендация: срочный штаб по "+crit+" просроченным пунктам."
|
||||
|
||||
}else if(ql.indexOf("статус")!==-1||ql.indexOf("состоян")!==-1||ql.indexOf("обстан")!==-1){
|
||||
var bySec=[];
|
||||
for(var si=0;si<secs.length;si++){bySec.push({n:secs[si].split(".")[0],t:0,d:0,l:0})}
|
||||
for(var i=0;i<evs.length;i++){
|
||||
var e=evs[i];bySec[e.sec].t++;
|
||||
if(e.s==="done")bySec[e.sec].d++;
|
||||
else if(e.s==="late")bySec[e.sec].l++
|
||||
}
|
||||
ans="Состояние по разделам:";
|
||||
for(var i=0;i<bySec.length;i++){
|
||||
ans+="\n"+bySec[i].n+": "+bySec[i].d+"/"+bySec[i].t+" ("+Math.round(bySec[i].d/bySec[i].t*100)+"%)"+(bySec[i].l?" просрочено:"+bySec[i].l:"")
|
||||
}
|
||||
|
||||
}else if(ql.indexOf("план")!==-1||ql.indexOf("действ")!==-1||ql.indexOf("рекоменд")!==-1||ql.indexOf("совет")!==-1){
|
||||
var pct=Math.round(done/total*100);
|
||||
if(pct<30)ans="Рекомендация: выполнено менее 30%. Рекомендуется усилить контроль за просроченными и провести штаб с ответственными лицами";
|
||||
else if(pct<60)ans="Рекомендация: выполнено "+pct+"%. Обратить внимание на процент выполнения в филиалах с низким показателем";
|
||||
@ -956,10 +1008,10 @@ function aiResp(q){
|
||||
}else{ans="Напиши номер пункта, например: пункт 5"}
|
||||
|
||||
}else{
|
||||
ans="Я могу ответить на вопросы по плану ПБ. Попробуй:\n\n- сводка\n- просроченные\n- риски\n- рейтинг филиалов\n- советник\n- аудит\n- статус пункта 10"
|
||||
ans="Я — Джарвис, ваш аналитический ассистент. Могу ответить:\n\n• сводка — общая статистика\n• просроченные — список просрочек\n• риски — зона риска (<30 дней)\n• рейтинг — рейтинг филиалов\n• аудит — полный аудит\n• прогноз — прогноз исполнения\n• статус — состояние по разделам\n• план — план действий и рекомендации\n• пункт N — детали конкретного мероприятия"
|
||||
}
|
||||
|
||||
addMsg("b",ans,"ИИ-помощник")
|
||||
addMsg("b",ans,"Джарвис")
|
||||
}
|
||||
function renderUsers(){
|
||||
if(!cu||cu.bg!==0){document.getElementById("tab_users").innerHTML="<div class='card'><p style='color:#EF4444'>Доступ запрещён</p></div>";return}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user