Форма ввода по 15 колонкам из Google Sheets + экспорт/импорт CSV

This commit is contained in:
Dauren777 2026-06-05 08:28:42 +00:00
parent 2477e26c30
commit d2ea436d89

View File

@ -1147,35 +1147,34 @@ body {
<div class="toggle-panel active" id="panel-input"> <div class="toggle-panel active" id="panel-input">
<div class="form-grid"> <div class="form-grid">
<div class="form-group"> <div class="form-group">
<label>Дата проверки</label> <label></label>
<input type="number" id="fNum" placeholder="Авто">
</div>
<div class="form-group">
<label>Дата</label>
<input type="date" id="fDate" required> <input type="date" id="fDate" required>
</div> </div>
<div class="form-group">
<label>№ указания</label>
<input type="text" id="fDirective" placeholder="Номер указания">
</div>
<div class="form-group"> <div class="form-group">
<label>Филиал</label> <label>Филиал</label>
<select id="fFilial"> <select id="fFilial">
<option value="">Выберите...</option> <option value="">Выберите...</option>
<option>ОДС</option> <option>ОДС</option><option>ДРБ</option><option>ДИТ</option>
<option>ДРБ</option> <option>Сервисная фабрика</option><option>ДКБ</option>
<option>ДИТ</option> <option>ДУП</option><option>ДТК</option><option>СФ</option>
<option>Сервисная фабрика</option> <option>ЦТО СФ</option><option>ЦЭиК</option>
<option>ДКБ</option>
<option>ДУП</option>
<option>ДТК</option>
<option>СФ</option>
<option>ЦТО СФ</option>
<option>ЦЭиК</option>
</select> </select>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Область</label> <label>Область</label>
<select id="fRegion"> <select id="fRegion">
<option value="">Выберите...</option> <option value="">Выберите...</option>
<option>Акмолинская</option> <option>Акмолинская</option><option>Астана</option>
<option>Астана</option> <option>Карагандинская</option><option>Улытауская</option>
<option>Карагандинская</option> <option>Костанайская</option><option>Павлодарская</option>
<option>Улытауская</option>
<option>Костанайская</option>
<option>Павлодарская</option>
<option>СКО</option> <option>СКО</option>
</select> </select>
</div> </div>
@ -1187,10 +1186,6 @@ body {
<label>Объект с адресом</label> <label>Объект с адресом</label>
<input type="text" id="fAddr" placeholder="Например: ул. Казахстанская 100а"> <input type="text" id="fAddr" placeholder="Например: ул. Казахстанская 100а">
</div> </div>
<div class="form-group">
<label>Кому выдано</label>
<input type="text" id="fIssuedTo" placeholder="Например: Начальник ЛТЦ">
</div>
<div class="form-group"> <div class="form-group">
<label>Категория нарушения</label> <label>Категория нарушения</label>
<select id="fCat"> <select id="fCat">
@ -1204,27 +1199,38 @@ body {
<option>Санитарно-бытовые условия</option> <option>Санитарно-бытовые условия</option>
<option>организационные требования БиОТ</option> <option>организационные требования БиОТ</option>
<option>Производственная санитария</option> <option>Производственная санитария</option>
<option>Инструкция по проведению 4-х ступенчатого контроля</option>
<option>Наряды-допуски</option> <option>Наряды-допуски</option>
<option>Инструкция по проведению 4-х ступенчатого контроля</option>
<option>Не обеспечение СИЗ</option>
<option>Политика в области ТСС</option>
<option>Требовании по организации в управлении транспортных средств</option>
</select> </select>
</div> </div>
<div class="form-group full">
<label>Категория нарушения (описание)</label>
<textarea id="fDesc" placeholder="Подробное описание выявленного нарушения..."></textarea>
</div>
<div class="form-group full">
<label>Ссылка на законодательство</label>
<textarea id="fLaw" rows="2" placeholder="Например: пп.4, п.2 статьи 182 Трудового кодекса РК..."></textarea>
</div>
<div class="form-group"> <div class="form-group">
<label>Статус</label> <label>Срок исполнения</label>
<input type="text" id="fDeadline" placeholder="Например: 14.01.2026г.">
</div>
<div class="form-group">
<label>Исполнено</label>
<select id="fStatus"> <select id="fStatus">
<option value="на контроле">На контроле</option> <option value="на контроле">На контроле</option>
<option value="исполнено">Исполнено</option> <option value="исполнено">Исполнено</option>
</select> </select>
</div> </div>
<div class="form-group full"> <div class="form-group full">
<label>Описание нарушения</label> <label>Примечание</label>
<textarea id="fDesc" placeholder="Кратко опишите нарушение..."></textarea> <input type="text" id="fNote" placeholder="Примечание (необязательно)">
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Срок исполнения</label> <label>Кто выдал</label>
<input type="date" id="fDeadline">
</div>
<div class="form-group">
<label>Проверяющий (кто выдал)</label>
<select id="fInspector"> <select id="fInspector">
<option value="">Выберите...</option> <option value="">Выберите...</option>
<option>Инженер по БиОТ Семидоцкий С.А.</option> <option>Инженер по БиОТ Семидоцкий С.А.</option>
@ -1235,8 +1241,13 @@ body {
<option>Инженер ОБиОТ Садыков М.Ф.</option> <option>Инженер ОБиОТ Садыков М.Ф.</option>
<option>Ведущий инженер ОБиОТ Тумабаева С.А.</option> <option>Ведущий инженер ОБиОТ Тумабаева С.А.</option>
<option>Инженер по БиОТ Джусупова А.Т.</option> <option>Инженер по БиОТ Джусупова А.Т.</option>
<option>Ведущий инженер ОБиОТ Федоськин А.Н.</option>
</select> </select>
</div> </div>
<div class="form-group">
<label>Кому выдано</label>
<input type="text" id="fIssuedTo" placeholder="Например: Начальник ЛТЦ">
</div>
</div> </div>
<div class="form-actions"> <div class="form-actions">
<button class="btn btn-blue" id="btnAdd">Добавить запись</button> <button class="btn btn-blue" id="btnAdd">Добавить запись</button>
@ -1625,13 +1636,13 @@ body {
var fixed = isFixed(r.status); var fixed = isFixed(r.status);
html += '<div class="entry-card">' + html += '<div class="entry-card">' +
'<span class="entry-date">' + escHtml(r.date || '—') + '</span>' + '<span class="entry-date">' + escHtml(r.date || '—') + '</span>' +
'<span class="entry-cat">' + escHtml(normalizeCategory(r.category || r.cat || '')) + '</span>' + '<span class="entry-cat">' + escHtml(normalizeCategory(r.cat || r.category || '')) + '</span>' +
'<span class="entry-desc">' + escHtml((r.desc || r.description || '').slice(0, 80)) + '</span>' + '<span class="entry-desc">' + escHtml((r.desc || r.description || '').slice(0, 80)) + '</span>' +
'<span class="entry-status ' + (fixed ? 'fixed' : 'pending') + '">' + (fixed ? 'Исполнено' : 'На контроле') + '</span>' + '<span class="entry-status ' + (fixed ? 'fixed' : 'pending') + '">' + (fixed ? 'Исполнено' : 'На контроле') + '</span>' +
'<button class="entry-del" data-id="' + r.id + '" title="Удалить">&times;</button>' + '<button class="entry-del" data-id="' + r.id + '" title="Удалить">&times;</button>' +
'</div>'; '</div>';
}); });
if (data.length === 0) html = '<p style="color:var(--gray-500);text-align:center;padding:40px">Нет записей. Добавьте через форму «Ввод данных».</p>'; if (data.length === 0) html = '<p style="color:var(--gray-500);text-align:center;padding:40px">Нет записей. Добавьте через вкладку «Ввод данных».</p>';
if (data.length > 200) html += '<p style="font-size:13px;color:var(--gray-500);text-align:center">Показаны последние 200 из ' + data.length + '</p>'; if (data.length > 200) html += '<p style="font-size:13px;color:var(--gray-500);text-align:center">Показаны последние 200 из ' + data.length + '</p>';
document.getElementById('entriesList').innerHTML = html; document.getElementById('entriesList').innerHTML = html;
@ -1646,32 +1657,39 @@ body {
// ---- Form handling ---- // ---- Form handling ----
function submitForm() { function submitForm() {
var entry = { var entry = {
num: document.getElementById('fNum').value,
date: document.getElementById('fDate').value, date: document.getElementById('fDate').value,
directive: document.getElementById('fDirective').value,
filial: document.getElementById('fFilial').value, filial: document.getElementById('fFilial').value,
region: document.getElementById('fRegion').value, region: document.getElementById('fRegion').value,
city: document.getElementById('fCity').value, city: document.getElementById('fCity').value,
addr: document.getElementById('fAddr').value, addr: document.getElementById('fAddr').value,
issuedTo: document.getElementById('fIssuedTo').value,
cat: document.getElementById('fCat').value, cat: document.getElementById('fCat').value,
status: document.getElementById('fStatus').value,
desc: document.getElementById('fDesc').value, desc: document.getElementById('fDesc').value,
law: document.getElementById('fLaw').value,
deadline: document.getElementById('fDeadline').value, deadline: document.getElementById('fDeadline').value,
inspector: document.getElementById('fInspector').value status: document.getElementById('fStatus').value,
note: document.getElementById('fNote').value,
inspector: document.getElementById('fInspector').value,
issuedTo: document.getElementById('fIssuedTo').value
}; };
if (!entry.date || !entry.filial || !entry.cat || !entry.desc) { if (!entry.date || !entry.filial || !entry.cat || !entry.desc) {
showMsg('Заполните обязательные поля: дата, филиал, категория, описание', 'err'); showMsg('Заполните обязательные поля: Дата, Филиал, Категория, Описание', 'err');
return; return;
} }
addEntry(entry); addEntry(entry);
showMsg('Запись добавлена!', 'ok'); showMsg('Запись добавлена!', 'ok');
document.getElementById('fDesc').value = ''; // Clear text fields, keep dropdowns
document.getElementById('fNum').value = '';
document.getElementById('fDate').value = ''; document.getElementById('fDate').value = '';
document.getElementById('fIssuedTo').value = ''; document.getElementById('fDirective').value = '';
document.getElementById('fAddr').value = ''; document.getElementById('fDesc').value = '';
document.getElementById('fCity').value = ''; document.getElementById('fLaw').value = '';
document.getElementById('fDeadline').value = ''; document.getElementById('fDeadline').value = '';
document.getElementById('fNote').value = '';
document.getElementById('fIssuedTo').value = '';
renderAll(); renderAll();
setTimeout(function() { showMsg('', ''); }, 2500); setTimeout(function() { showMsg('', ''); }, 2500);
} }
@ -1686,13 +1704,14 @@ body {
function exportCSV() { function exportCSV() {
var data = loadData(); var data = loadData();
if (data.length === 0) { alert('Нет данных для экспорта.'); return; } if (data.length === 0) { alert('Нет данных для экспорта.'); return; }
var header = 'Дата,Филиал,Область,Город/Район,Объект,Категория нарушения,Описание,Статус,Срок,Проверяющий,Кому выдано'; var header = '№,Дата,№ указания,Филиал,Область,Город/Район,Объект с адресом,Категория нарушения,Категория нарушения,Ссылка на законодательство,Срок исполнения,исполнено,Примечание,кто выдал,кому выдано';
var lines = [header]; var lines = [header];
data.forEach(function(r) { data.forEach(function(r) {
lines.push([ lines.push([
r.date || '', r.filial || '', r.region || '', r.city || '', r.num || '', r.date || '', r.directive || '', r.filial || '', r.region || '',
r.addr || '', r.cat || r.category || '', r.desc || r.description || '', r.city || '', r.addr || '', r.cat || r.category || '', r.desc || r.description || '',
r.status || '', r.deadline || '', r.inspector || r.issuedBy || '', r.issuedTo || '' r.law || '', r.deadline || '', r.status || '', r.note || '',
r.inspector || r.issuedBy || '', r.issuedTo || ''
].map(function(v) { return '"' + String(v).replace(/"/g, '""') + '"'; }).join(',')); ].map(function(v) { return '"' + String(v).replace(/"/g, '""') + '"'; }).join(','));
}); });
var blob = new Blob(['\uFEFF' + lines.join('\n')], { type: 'text/csv;charset=utf-8' }); var blob = new Blob(['\uFEFF' + lines.join('\n')], { type: 'text/csv;charset=utf-8' });
@ -1740,36 +1759,47 @@ body {
return -1; return -1;
} }
var ciNum = idx(['№']);
var ciDate = idx(['дата']); var ciDate = idx(['дата']);
var ciDirective = idx(['указания','№ указания']);
var ciFilial = idx(['филиал']); var ciFilial = idx(['филиал']);
var ciRegion = idx(['область','регион']); var ciRegion = idx(['область','регион']);
var ciCity = idx(['город','район']); var ciCity = idx(['город','район','населен']);
var ciAddr = idx(['объект','адрес']); var ciAddr = idx(['объект','адрес']);
var ciCat = idx(['категор','нарушен']); var ciCat = idx(['категор','нарушен']);
// Second "Категория нарушения" = description
var ciDesc = -1; var ciDesc = -1;
for (var hi = 0; hi < headers.length; hi++) { for (var hi = 0; hi < headers.length; hi++) {
if (headers[hi].indexOf('категор') !== -1 && hi !== ciCat) { ciDesc = hi; break; } if (headers[hi].indexOf('категор') !== -1 && hi !== ciCat) { ciDesc = hi; break; }
if (headers[hi].indexOf('описан') !== -1 || headers[hi].indexOf('суть') !== -1) { ciDesc = hi; break; } if (headers[hi].indexOf('описан') !== -1) { ciDesc = hi; break; }
} }
if (ciDesc < 0) ciDesc = ciCat >= 0 ? ciCat + 1 : -1; if (ciDesc < 0 && ciCat >= 0) ciDesc = ciCat + 1;
var ciStatus = idx(['исполнен','статус','устран']);
var ciLaw = idx(['законодательств','ссылка','основание']);
var ciDeadline = idx(['срок']); var ciDeadline = idx(['срок']);
var ciInspector = idx(['выдал','кто выдал','проверяющ']); var ciStatus = idx(['исполнен','статус','устран']);
var ciNote = idx(['примечан']);
var ciInspector = idx(['выдал','кто выдал']);
var ciIssued = idx(['кому','выдано']); var ciIssued = idx(['кому','выдано']);
var entries = []; var entries = [];
data.forEach(function(r) { data.forEach(function(r) {
if (r.length < 2) return; if (r.length < 2) return;
entries.push({ entries.push({
num: ciNum >= 0 ? r[ciNum] || '' : '',
date: ciDate >= 0 ? r[ciDate] || '' : '', date: ciDate >= 0 ? r[ciDate] || '' : '',
directive: ciDirective >= 0 ? r[ciDirective] || '' : '',
filial: ciFilial >= 0 ? r[ciFilial] || '' : '', filial: ciFilial >= 0 ? r[ciFilial] || '' : '',
region: ciRegion >= 0 ? r[ciRegion] || '' : '', region: ciRegion >= 0 ? r[ciRegion] || '' : '',
city: ciCity >= 0 ? r[ciCity] || '' : '', city: ciCity >= 0 ? r[ciCity] || '' : '',
addr: ciAddr >= 0 ? r[ciAddr] || '' : '', addr: ciAddr >= 0 ? r[ciAddr] || '' : '',
cat: ciCat >= 0 ? r[ciCat] || '' : '', cat: ciCat >= 0 ? r[ciCat] || '' : '',
desc: ciDesc >= 0 && ciDesc < r.length ? r[ciDesc] || '' : '', desc: ciDesc >= 0 && ciDesc < r.length ? r[ciDesc] || '' : '',
status: ciStatus >= 0 ? r[ciStatus] || '' : '', law: ciLaw >= 0 ? r[ciLaw] || '' : '',
deadline: ciDeadline >= 0 ? r[ciDeadline] || '' : '', deadline: ciDeadline >= 0 ? r[ciDeadline] || '' : '',
status: ciStatus >= 0 ? r[ciStatus] || '' : '',
note: ciNote >= 0 ? r[ciNote] || '' : '',
inspector: ciInspector >= 0 ? r[ciInspector] || '' : '', inspector: ciInspector >= 0 ? r[ciInspector] || '' : '',
issuedTo: ciIssued >= 0 ? r[ciIssued] || '' : '' issuedTo: ciIssued >= 0 ? r[ciIssued] || '' : ''
}); });
@ -1804,21 +1834,40 @@ body {
} }
function generateSampleCSV() { function generateSampleCSV() {
var header = 'Дата,Филиал,Область,Город/Район,Объект с адресом,Категория нарушения,Описание нарушения,Срок исполнения,исполнено,кто выдал,кому выдано'; var header = '№,Дата,№ указания,Филиал,Область,Город/Район,Объект с адресом,Категория нарушения,Категория нарушения,Ссылка на законодательство,Срок исполнения,исполнено,Примечание,кто выдал,кому выдано';
var filials = ['ОДС','ДРБ','ДИТ','Сервисная фабрика','ДКБ','ДУП','ЦТО СФ','ЦЭиК']; var filials = ['ОДС','ДРБ','ДИТ','Сервисная фабрика','ДКБ','ДУП','ЦТО СФ','ЦЭиК'];
var oblasts = ['Акмолинская','Карагандинская','Астана','Астана','Улытауская','Карагандинская','Акмолинская']; var oblasts = ['Акмолинская','Карагандинская','Астана','Улытауская'];
var cities = ['Шахтинск','Астана','Караганда','Темиртау','Щучинск','Атбасар','Балхаш','Кокшетау']; var cities = ['Шахтинск','Астана','Караганда','Темиртау','Щучинск','Атбасар','Балхаш','Кокшетау'];
var inspectors = [ var cats = ['основные док-ты по БиОТ','Пожарная безопасность','Электробезопасность','СИЗ','Транспортная безопасность','Промышленная безопасность','Санитарно-бытовые условия','организационные требования БиОТ'];
'Инженер по БиОТ Семидоцкий С.А.','Ведущий инженер ОБиОТ Туржанов А.Т.',
'Инженер ОБиОТ Бачинская Н.В.','Ведущий инженер ОБиОТ Ажакметов М.З.',
'Ведущий инженер ОБиОТ Баяхметова Ж.Т.','Инженер ОБиОТ Садыков М.Ф.'
];
var realCats = ['основные док-ты по БиОТ','Пожарная безопасность','Электробезопасность','СИЗ','Транспортная безопасность','Промышленная безопасность','Санитарно-бытовые условия','организационные требования БиОТ'];
var descs = [ var descs = [
'Отсутствует журнал инструктажа','Не проведён повторный инструктаж','Работники не ознакомлены с регламентом СИЗ', 'Отсутствует журнал инструктажа на рабочем месте',
'Просроченные диэлектрические перчатки','Неактуальные инструкции по ТБ','Отсутствует аптечка', 'Не проведён повторный инструктаж за 1 квартал 2026',
'Пожарный кран не проверялся','Работники без касок','Прошёл срок поверки инструмента', 'Работники не ознакомлены с регламентом обеспечения СИЗ',
'Аварийный выход заблокирован','На территории скопление снега' 'Форма журнала наряд-допусков не соответствует правилам',
'Просроченные диэлектрические перчатки',
'Неактуальные инструкции по ТБ по видам работ',
'Отсутствует аптечка для оказания первой помощи',
'Пожарный кран не проверялся на работоспособность',
'Работники не применяли СИЗ (без касок, пояса)',
'Прошёл срок поверки инструмента',
'Аварийный выход заблокирован',
'На территории скопление снега, требуется вывоз'
];
var laws = [
'пп.4, п.2 статьи 182 Трудового кодекса РК',
'Приказ №55 МЧС РК от 21.02.2022',
'Приказ Министра энергетики РК от 19.03.2015 №222',
'Распоряжение №253 от 31.10.2025',
'Распоряжение №271 от 19.11.2025',
'Приказ №262 от 14.11.2018'
];
var inspectors = [
'Инженер по БиОТ Семидоцкий С.А.',
'Ведущий инженер ОБиОТ Туржанов А.Т.',
'Инженер ОБиОТ Бачинская Н.В.',
'Ведущий инженер ОБиОТ Ажакметов М.З.',
'Ведущий инженер ОБиОТ Баяхметова Ж.Т.',
'Инженер ОБиОТ Садыков М.Ф.'
]; ];
var statuses = ['исполнено','исполнено','исполнено','на контроле','на контроле','исполнено','на контроле']; var statuses = ['исполнено','исполнено','исполнено','на контроле','на контроле','исполнено','на контроле'];
var months = ['01','02','03','04','05','06']; var months = ['01','02','03','04','05','06'];
@ -1826,11 +1875,18 @@ body {
var lines = [header]; var lines = [header];
for (var i = 0; i < 200; i++) { for (var i = 0; i < 200; i++) {
var d = String(Math.floor(Math.random()*28)+1).padStart(2,'0') + '.' + months[Math.floor(Math.random()*months.length)] + '.2026'; var d = String(Math.floor(Math.random()*28)+1).padStart(2,'0') + '.' + months[Math.floor(Math.random()*months.length)] + '.2026';
lines.push([d, filials[Math.floor(Math.random()*filials.length)], oblasts[Math.floor(Math.random()*oblasts.length)], lines.push([
cities[Math.floor(Math.random()*cities.length)], 'ул. Примерная '+(Math.floor(Math.random()*50)+1), i+1, d, Math.floor(Math.random()*20)+1,
realCats[Math.floor(Math.random()*realCats.length)], descs[Math.floor(Math.random()*descs.length)], filials[Math.floor(Math.random()*filials.length)],
'', statuses[Math.floor(Math.random()*statuses.length)], oblasts[Math.floor(Math.random()*oblasts.length)],
inspectors[Math.floor(Math.random()*inspectors.length)], ''].join(',')); cities[Math.floor(Math.random()*cities.length)],
'ул. Примерная ' + (Math.floor(Math.random()*50)+1),
cats[Math.floor(Math.random()*cats.length)],
descs[Math.floor(Math.random()*descs.length)],
laws[Math.floor(Math.random()*laws.length)],
d, statuses[Math.floor(Math.random()*statuses.length)],
'', inspectors[Math.floor(Math.random()*inspectors.length)], ''
].join(','));
} }
return lines.join('\n'); return lines.join('\n');
} }