csv export with UTF-8 BOM for Excel compatibility
This commit is contained in:
parent
f8883205aa
commit
ac969e09fc
79
index.html
79
index.html
@ -31,6 +31,13 @@ body{
|
|||||||
font-weight:700;font-size:14px;cursor:pointer;
|
font-weight:700;font-size:14px;cursor:pointer;
|
||||||
}
|
}
|
||||||
.btn:hover{opacity:.85}
|
.btn:hover{opacity:.85}
|
||||||
|
.btn-sm{
|
||||||
|
background:var(--gray-100);color:var(--ink);
|
||||||
|
border:none;padding:5px 12px;border-radius:6px;
|
||||||
|
font-weight:600;font-size:12px;cursor:pointer;
|
||||||
|
white-space:nowrap;
|
||||||
|
}
|
||||||
|
.btn-sm:hover{background:var(--cyan)}
|
||||||
.timer{font-size:13px;color:var(--gray-500)}
|
.timer{font-size:13px;color:var(--gray-500)}
|
||||||
.badge{padding:4px 14px;border-radius:20px;font-size:13px;font-weight:600}
|
.badge{padding:4px 14px;border-radius:20px;font-size:13px;font-weight:600}
|
||||||
.badge.ok{background:var(--green);color:var(--ink)}
|
.badge.ok{background:var(--green);color:var(--ink)}
|
||||||
@ -117,6 +124,7 @@ tr:hover td{background:var(--cyan-50)}
|
|||||||
<div class="controls">
|
<div class="controls">
|
||||||
<span id="statusBadge" class="badge ok">Загрузка...</span>
|
<span id="statusBadge" class="badge ok">Загрузка...</span>
|
||||||
<button class="btn" onclick="loadAll()">↻ Обновить</button>
|
<button class="btn" onclick="loadAll()">↻ Обновить</button>
|
||||||
|
<button class="btn" onclick="downloadAll()" style="background:var(--gray-700);color:var(--white)">📥 Скачать всё</button>
|
||||||
<span class="timer" id="timerLabel">60 сек</span>
|
<span class="timer" id="timerLabel">60 сек</span>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@ -256,9 +264,14 @@ function renderMain(results) {
|
|||||||
const s = r.sheet;
|
const s = r.sheet;
|
||||||
const openLink = `https://docs.google.com/spreadsheets/d/${s.id}/edit?gid=${s.gid}`;
|
const openLink = `https://docs.google.com/spreadsheets/d/${s.id}/edit?gid=${s.gid}`;
|
||||||
html += `<div class="sheet-section">`;
|
html += `<div class="sheet-section">`;
|
||||||
html += `<div class="sheet-header" onclick="toggleSheet(this)">
|
html += `<div class="sheet-header">
|
||||||
<h3>📋 ${s.label} <span class="arrow">▶</span></h3>
|
<h3 style="display:flex;align-items:center;gap:12px" onclick="toggleSheet(this.parentElement)">
|
||||||
<span class="sheet-meta">${r.error ? "❌ " + r.error : r.rows.length + " строк"}</span>
|
<span class="arrow">▶</span> 📋 ${s.label}
|
||||||
|
</h3>
|
||||||
|
<span class="sheet-meta" style="display:flex;align-items:center;gap:10px">
|
||||||
|
${r.error ? "❌ " + r.error : r.rows.length + " строк"}
|
||||||
|
${!r.error && r.rows.length > 0 ? `<button class="btn-sm" onclick="event.stopPropagation();downloadCSV(${i})" title="Скачать CSV (Excel-совместимый)">📥 CSV</button>` : ""}
|
||||||
|
</span>
|
||||||
</div>`;
|
</div>`;
|
||||||
html += `<div class="sheet-body ${r.rows.length === 0 ? 'collapsed' : ''}">`;
|
html += `<div class="sheet-body ${r.rows.length === 0 ? 'collapsed' : ''}">`;
|
||||||
|
|
||||||
@ -303,6 +316,66 @@ function toggleSheet(header) {
|
|||||||
arrow.classList.toggle("open");
|
arrow.classList.toggle("open");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function downloadCSV(sheetIndex) {
|
||||||
|
const r = allData[sheetIndex];
|
||||||
|
if (!r || r.error || r.rows.length === 0) return;
|
||||||
|
|
||||||
|
const rows = r.rows;
|
||||||
|
let csv = "\uFEFF"; // UTF-8 BOM — чтобы старый Excel сразу читал кириллицу
|
||||||
|
for (let i = 0; i < rows.length; i++) {
|
||||||
|
const cells = rows[i].map(c => {
|
||||||
|
const s = String(c);
|
||||||
|
if (s.includes(",") || s.includes("\"") || s.includes("\n")) {
|
||||||
|
return "\"" + s.replace(/\"/g, "\"\"") + "\"";
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
});
|
||||||
|
csv += cells.join(",") + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
|
const name = r.sheet.label.replace(/[^a-zA-Zа-яА-ЯёЁ0-9_\- ]/g, "").trim() || "export";
|
||||||
|
a.download = name + ".csv";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadAll() {
|
||||||
|
const ok = allData.filter(r => !r.error && r.rows.length > 0);
|
||||||
|
if (ok.length === 0) { alert("Нет данных для скачивания"); return; }
|
||||||
|
|
||||||
|
let csv = "\uFEFF";
|
||||||
|
ok.forEach((r, idx) => {
|
||||||
|
csv += "\n" + r.sheet.label + "\n";
|
||||||
|
r.rows.forEach(row => {
|
||||||
|
const cells = row.map(c => {
|
||||||
|
const s = String(c);
|
||||||
|
if (s.includes(",") || s.includes("\"") || s.includes("\n")) {
|
||||||
|
return "\"" + s.replace(/\"/g, "\"\"") + "\"";
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
});
|
||||||
|
csv += cells.join(",") + "\n";
|
||||||
|
});
|
||||||
|
if (idx < ok.length - 1) csv += "\n";
|
||||||
|
});
|
||||||
|
|
||||||
|
const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
|
a.download = "svodnaya_tablitsa.csv";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
function updateTimer() {
|
function updateTimer() {
|
||||||
document.getElementById("timerLabel").textContent = countdown + " сек";
|
document.getElementById("timerLabel").textContent = countdown + " сек";
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user