156 lines
4.3 KiB
JavaScript
156 lines
4.3 KiB
JavaScript
// ===== CORE.JS - API и утилиты =====
|
||
|
||
/**
|
||
* API wrapper для запросов к серверу
|
||
*/
|
||
async function api(url, method = 'GET', body) {
|
||
const opts = { method, headers: { 'Content-Type': 'application/json' } };
|
||
if (body) opts.body = JSON.stringify(body);
|
||
|
||
const res = await fetch(url, opts);
|
||
|
||
// Пытаемся распарсить как JSON
|
||
const contentType = res.headers.get('content-type');
|
||
let data;
|
||
|
||
if (contentType && contentType.includes('application/json')) {
|
||
data = await res.json();
|
||
} else {
|
||
const txt = await res.text();
|
||
// Пытаемся извлечь сообщение из HTML ошибки Slim
|
||
const msgMatch = txt.match(/<strong>Message:<\/strong>\s*([^<]+)/);
|
||
if (msgMatch) {
|
||
throw new Error(msgMatch[1].trim());
|
||
}
|
||
throw new Error(`Ошибка сервера: ${res.status}`);
|
||
}
|
||
|
||
if (!res.ok) {
|
||
// Сервер вернул JSON с ошибкой
|
||
const rawMessage = data.message || data.error || 'Неизвестная ошибка';
|
||
const cleanMessage = String(rawMessage).replace(/^CONFLICT:\s*/i, '');
|
||
throw new Error(cleanMessage);
|
||
}
|
||
|
||
// Проверяем на ошибку в успешном ответе
|
||
if (data.error) {
|
||
throw new Error(data.message || 'Ошибка');
|
||
}
|
||
|
||
return data;
|
||
}
|
||
|
||
/**
|
||
* Обновить счётчик выделенных строк
|
||
*/
|
||
function updateSelectionCounter() {
|
||
const counter = document.getElementById('selectionCounter');
|
||
|
||
let count = 0;
|
||
if (table) {
|
||
const allSelectedData = new Map();
|
||
table.getSelectedData().forEach(rowData => {
|
||
const key = getRowKey(rowData);
|
||
allSelectedData.set(key, rowData);
|
||
});
|
||
selectedRowsDataGlobal.forEach((rowData, key) => {
|
||
allSelectedData.set(key, rowData);
|
||
});
|
||
count = allSelectedData.size;
|
||
} else {
|
||
count = selectedRowsDataGlobal.size;
|
||
}
|
||
|
||
if (count > 0) {
|
||
counter.textContent = `Выбрано: ${count}`;
|
||
counter.style.display = 'block';
|
||
} else {
|
||
counter.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Создать модальное окно с прогрессом
|
||
*/
|
||
function createProgressModal(message) {
|
||
const modal = document.createElement('div');
|
||
modal.style.cssText = `
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0,0,0,0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 10000;
|
||
`;
|
||
|
||
const dialog = document.createElement('div');
|
||
dialog.style.cssText = `
|
||
background: white;
|
||
padding: 30px;
|
||
border-radius: 8px;
|
||
min-width: 300px;
|
||
text-align: center;
|
||
`;
|
||
|
||
dialog.innerHTML = `
|
||
<div style="font-size: 16px; margin-bottom: 20px;">${message}</div>
|
||
<div style="width: 100%; height: 30px; background: #e0e0e0; border-radius: 15px; overflow: hidden;">
|
||
<div class="progress-bar" style="width: 0%; height: 100%; background: linear-gradient(90deg, #4CAF50, #45a049); transition: width 0.3s;"></div>
|
||
</div>
|
||
<div style="margin-top: 10px; font-size: 14px; color: #666;">
|
||
<span class="spinner" style="display: inline-block; animation: spin 1s linear infinite;">⏳</span>
|
||
Пожалуйста, подождите...
|
||
</div>
|
||
<style>
|
||
@keyframes spin {
|
||
from { transform: rotate(0deg); }
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
</style>
|
||
`;
|
||
|
||
modal.appendChild(dialog);
|
||
|
||
const progressBar = dialog.querySelector('.progress-bar');
|
||
let progress = 0;
|
||
const interval = setInterval(() => {
|
||
progress += Math.random() * 15;
|
||
if (progress > 90) progress = 90;
|
||
progressBar.style.width = progress + '%';
|
||
}, 200);
|
||
|
||
modal.stopProgress = () => {
|
||
clearInterval(interval);
|
||
progressBar.style.width = '100%';
|
||
};
|
||
|
||
return modal;
|
||
}
|
||
|
||
/**
|
||
* Экранировать HTML
|
||
*/
|
||
function escapeHtml(text) {
|
||
const div = document.createElement('div');
|
||
div.textContent = text;
|
||
return div.innerHTML;
|
||
}
|
||
|
||
/**
|
||
* Получить уникальный ключ строки по первичному ключу
|
||
*/
|
||
function getRowKey(rowData) {
|
||
if (!currentMeta || !currentMeta.primaryKey || currentMeta.primaryKey.length === 0) {
|
||
return JSON.stringify(rowData);
|
||
}
|
||
|
||
const pkValues = currentMeta.primaryKey.map(pk => rowData[pk]).join('|');
|
||
return pkValues;
|
||
}
|
||
|
||
console.log('✅ core.js загружен');
|