v3
This commit is contained in:
parent
3de696a092
commit
93ce22780f
95
index.html
95
index.html
@ -54,8 +54,8 @@ th{background:var(--gray-100);font-weight:600}
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<h3 style="margin-bottom:16px">Загрузка списка работников</h3>
|
<h3 style="margin-bottom:16px">Загрузка списка работников</h3>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="fileUpload">Загрузите файл (TSV/CSV - разделитель Tab или запятая)</label>
|
<label for="fileUpload">Загрузите файл Excel (.xlsx)</label>
|
||||||
<input type="file" id="fileUpload" accept=".tsv,.csv,.txt">
|
<input type="file" id="fileUpload" accept=".xlsx,.xls">
|
||||||
<small style="color:var(--gray-500)">Ожидаемые столбцы: Табельный номер, ФИО, Штатная должность, Грейд, МВЗ, ОрганизационнЕдиница, Текст Орг.Единицы, Факт-дни, Оклад по ШР, Оклад факт, ДатаВзыскания, ВидВзыскания, ДатаПриема (формат: ДД.ММ.ГГГГ)</small>
|
<small style="color:var(--gray-500)">Ожидаемые столбцы: Табельный номер, ФИО, Штатная должность, Грейд, МВЗ, ОрганизационнЕдиница, Текст Орг.Единицы, Факт-дни, Оклад по ШР, Оклад факт, ДатаВзыскания, ВидВзыскания, ДатаПриема (формат: ДД.ММ.ГГГГ)</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -116,6 +116,7 @@ th{background:var(--gray-100);font-weight:600}
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<script src="https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
let workers = [];
|
let workers = [];
|
||||||
|
|
||||||
@ -162,61 +163,74 @@ function loadFromFile() {
|
|||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|
||||||
reader.onload = function(e) {
|
reader.onload = function(e) {
|
||||||
const content = e.target.result;
|
try {
|
||||||
const lines = content.split('\n').filter(line => line.trim());
|
const data = new Uint8Array(e.target.result);
|
||||||
|
const workbook = XLSX.read(data, {type: 'array'});
|
||||||
|
|
||||||
// Skip header row if exists (check if first line contains "ФИО" or similar)
|
// Get first sheet
|
||||||
const startIndex = lines[0].includes('ФИО') || lines[0].includes('Табельный') ? 1 : 0;
|
const firstSheetName = workbook.SheetNames[0];
|
||||||
|
const worksheet = workbook.Sheets[firstSheetName];
|
||||||
|
|
||||||
|
// Convert to JSON
|
||||||
|
const jsonData = XLSX.utils.sheet_to_json(worksheet, {header: 1});
|
||||||
|
|
||||||
|
if (jsonData.length === 0) {
|
||||||
|
alert('Файл пуст');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find header row index (look for "ФИО" in any cell)
|
||||||
|
let headerRowIndex = -1;
|
||||||
|
for (let i = 0; i < Math.min(5, jsonData.length); i++) {
|
||||||
|
const row = jsonData[i];
|
||||||
|
if (row && row.some(cell => cell && cell.toString().includes('ФИО'))) {
|
||||||
|
headerRowIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headerRowIndex === -1) {
|
||||||
|
// Assume first row is header
|
||||||
|
headerRowIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get data rows (skip header)
|
||||||
|
const dataRows = jsonData.slice(headerRowIndex + 1);
|
||||||
|
|
||||||
let loadedCount = 0;
|
let loadedCount = 0;
|
||||||
|
|
||||||
for (let i = startIndex; i < lines.length; i++) {
|
for (let i = 0; i < dataRows.length; i++) {
|
||||||
const line = lines[i].trim();
|
const row = dataRows[i];
|
||||||
if (!line) continue;
|
|
||||||
|
|
||||||
// Detect separator: tab or comma
|
// Skip empty rows
|
||||||
const separator = line.includes('\t') ? '\t' : ',';
|
if (!row || row.length < 13) {
|
||||||
const columns = line.split(separator).map(col => col.trim());
|
console.log(`Строка ${i + headerRowIndex + 2}: недостаточно столбцов, пропускаем`);
|
||||||
|
|
||||||
// Expected structure based on user's columns:
|
|
||||||
// 0: Табельный номер
|
|
||||||
// 1: ФИО
|
|
||||||
// 2: Штатная должность
|
|
||||||
// 3: Грейд
|
|
||||||
// 4: МВЗ
|
|
||||||
// 5: ОрганизационнЕдиница
|
|
||||||
// 6: Текст Орг.Единицы
|
|
||||||
// 7: Факт-дни
|
|
||||||
// 8: Оклад по ШР
|
|
||||||
// 9: Оклад факт
|
|
||||||
// 10: ДатаВзыскания
|
|
||||||
// 11: ВидВзыскания
|
|
||||||
// 12: ДатаПриема
|
|
||||||
|
|
||||||
if (columns.length < 13) {
|
|
||||||
console.log(`Строка ${i+1}: недостаточно столбцов (${columns.length}), пропускаем`);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fullName = columns[1];
|
const fullName = row[1] ? row[1].toString().trim() : '';
|
||||||
const workedDays = parseInt(columns[7]);
|
const workedDays = row[7] ? parseInt(row[7]) : NaN;
|
||||||
const salary = parseFloat(columns[9].replace(/\s/g, ''));
|
const salary = row[9] ? parseFloat(row[9].toString().replace(/\s/g, '')) : NaN;
|
||||||
const hireDateRaw = columns[12];
|
const hireDateRaw = row[12] ? row[12].toString().trim() : '';
|
||||||
|
|
||||||
// Parse hire date (format: DD.MM.YYYY or YYYY-MM-DD)
|
// Parse hire date (format: DD.MM.YYYY or Excel date)
|
||||||
let hireDate;
|
let hireDate;
|
||||||
if (hireDateRaw.includes('.')) {
|
if (hireDateRaw.includes('.')) {
|
||||||
const [day, month, year] = hireDateRaw.split('.');
|
const [day, month, year] = hireDateRaw.split('.');
|
||||||
hireDate = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
|
hireDate = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
|
||||||
} else if (hireDateRaw.includes('-')) {
|
} else if (hireDateRaw.includes('-')) {
|
||||||
hireDate = hireDateRaw;
|
hireDate = hireDateRaw;
|
||||||
|
} else if (typeof row[12] === 'number') {
|
||||||
|
// Excel date serial number
|
||||||
|
const excelDate = new Date((row[12] - 25569) * 86400 * 1000);
|
||||||
|
hireDate = excelDate.toISOString().split('T')[0];
|
||||||
} else {
|
} else {
|
||||||
console.log(`Строка ${i+1}: неверный формат даты "${hireDateRaw}", пропускаем`);
|
console.log(`Строка ${i + headerRowIndex + 2}: неверный формат даты "${hireDateRaw}", пропускаем`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fullName || isNaN(workedDays) || isNaN(salary)) {
|
if (!fullName || isNaN(workedDays) || isNaN(salary)) {
|
||||||
console.log(`Строка ${i+1}: некорректные данные, пропускаем`);
|
console.log(`Строка ${i + headerRowIndex + 2}: некорректные данные, пропускаем`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,9 +249,14 @@ function loadFromFile() {
|
|||||||
updateTable();
|
updateTable();
|
||||||
alert(`Загружено ${loadedCount} работников`);
|
alert(`Загружено ${loadedCount} работников`);
|
||||||
fileInput.value = '';
|
fileInput.value = '';
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка чтения файла:', error);
|
||||||
|
alert('Ошибка чтения файла. Убедитесь, что файл в формате Excel (.xlsx)');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
reader.readAsText(file);
|
reader.readAsArrayBuffer(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeWorker(id) {
|
function removeWorker(id) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user