Files
Shudao/sheet.html
T

233 lines
7.6 KiB
HTML
Raw Normal View History

2026-06-29 15:32:32 +08:00
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>x-data-spreadsheet</title>
<style>
body { margin:0; padding:20px; font-family:Arial,sans-serif; background-color:#f5f5f5; }
.layout { display:flex; gap:20px; align-items:flex-start; }
.main { flex:1 1 auto; min-width:0; }
#xspreadsheet { width:100%; height:calc(100vh - 140px); min-height:600px; box-shadow:0 2px 10px rgba(0,0,0,0.1); background-color:white; }
.toc-sidebar {
flex: 0 0 200px;
position: sticky;
top: 20px;
max-height: calc(100vh - 40px);
overflow-y: auto;
background: #ffffff;
border-radius: 6px;
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
padding: 12px 0;
box-sizing: border-box;
}
.toc-sidebar h3 {
margin: 0 14px 8px 0;
font-size: 12px;
font-weight: 600;
color: #333;
border-bottom: 1px solid #eee;
padding: 0 14px 8px;
letter-spacing: 0.5px;
text-transform: uppercase;
}
.toc-sheet-group { margin-bottom: 10px; }
.toc-sheet-name {
font-size: 11px;
color: #999;
margin: 4px 14px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.toc-list { list-style: none; padding: 0; margin: 0; }
.toc-list li { margin: 0; }
.toc-link {
display: block;
padding: 5px 14px;
color: #555;
text-decoration: none;
font-size: 12.5px;
line-height: 1.5;
cursor: pointer;
border-left: 2px solid transparent;
transition: background 0.12s, color 0.12s, border-color 0.12s;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.toc-link:hover { background: #f0f7ff; color: #1677ff; }
.toc-link.active {
background: #e6f4ff;
color: #1677ff;
border-left-color: #1677ff;
font-weight: 600;
}
.toc-empty { font-size: 12px; color: #999; padding: 8px 14px; }
2026-06-29 15:32:32 +08:00
</style>
<link rel="stylesheet" href="https://unpkg.com/x-data-spreadsheet@1.1.9/dist/xspreadsheet.css">
</head>
<body>
<h1>x-data-spreadsheet</h1>
<div class="layout">
<div class="main">
<div id="xspreadsheet"></div>
</div>
<aside class="toc-sidebar">
<h3>目录</h3>
<div id="toc-container"></div>
</aside>
</div>
2026-06-29 15:32:32 +08:00
<script src="https://unpkg.com/x-data-spreadsheet@1.1.9/dist/xspreadsheet.js"></script>
<script>
const rawData = {{DATA}};
// Normalize: x_spreadsheet.loadData accepts an array of sheets or a single sheet
const sheets = Array.isArray(rawData) ? rawData : [rawData];
2026-06-29 15:32:32 +08:00
// ---- TOC builder ----
function cellText(row, col) {
if (!row || !row.cells) return null;
const c = row.cells[col];
if (!c) return null;
return (c.text || '').trim() || null;
}
function extractToc(sheet) {
const items = [];
const rows = sheet.rows || {};
const len = rows.len || 0;
const seen = new Set();
for (let i = 0; i < len; i++) {
const r = rows[i];
if (!r || !r.cells) continue;
const c0 = cellText(r, 0);
const c1 = cellText(r, 1);
const c2 = cellText(r, 2);
// 1) Top-level labels in column 0 (基本要求 / 外观质量 / 工程质量等级评定 / ...)
if (c0 && !seen.has('c0:' + c0)) {
items.push({ label: c0, row: i });
seen.add('c0:' + c0);
continue;
}
// 2) Numbered items in column 1 (项次: 1A, 2A, 3, 4, 5, 6, 7, 8, ...)
if (c1 && /^[0-9]+[A-Za-z]?$/.test(c1)) {
const label = c1 + (c2 ? ' ' + c2 : '');
if (!seen.has('c1:' + c1)) {
items.push({ label, row: i });
seen.add('c1:' + c1);
}
continue;
}
}
return items;
}
function renderToc(sheets) {
const container = document.getElementById('toc-container');
container.innerHTML = '';
let hasAny = false;
sheets.forEach((sheet, sheetIdx) => {
const items = extractToc(sheet);
if (!items.length && sheets.length === 1) return;
hasAny = true;
const group = document.createElement('div');
group.className = 'toc-sheet-group';
if (sheets.length > 1) {
const name = document.createElement('div');
name.className = 'toc-sheet-name';
name.textContent = sheet.name || ('Sheet ' + (sheetIdx + 1));
group.appendChild(name);
}
const ul = document.createElement('ul');
ul.className = 'toc-list';
items.forEach(item => {
const li = document.createElement('li');
const a = document.createElement('a');
a.className = 'toc-link';
a.href = 'javascript:void(0)';
a.dataset.sheet = sheetIdx;
a.dataset.row = item.row;
a.title = item.label + ' (row ' + item.row + ')';
a.innerHTML = item.label + '<span class="row-tag" style="color:#bbb;font-size:10.5px;margin-left:4px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;">·' + item.row + '</span>';
a.addEventListener('click', () => scrollToRow(sheetIdx, item.row, a));
li.appendChild(a);
ul.appendChild(li);
});
group.appendChild(ul);
container.appendChild(group);
});
if (!hasAny) {
const empty = document.createElement('div');
empty.className = 'toc-empty';
empty.textContent = '(未识别到目录项)';
container.appendChild(empty);
}
}
// ---- Scroll handling ----
// x-data-spreadsheet renders the grid on a canvas; rows are NOT in the DOM.
// We scroll by setting the vertical scrollbar's scrollTop, which fires
// the library's internal moveFn -> scrolly() -> re-render.
let __ssRef = null; // populated after load
function getSheetData(sheetIdx) {
if (!__ssRef) return null;
// spreadsheet.datas[] holds the pt instances; .data is shorthand for datas[0]
if (Array.isArray(__ssRef.datas) && __ssRef.datas[sheetIdx]) return __ssRef.datas[sheetIdx];
if (sheetIdx === 0 && __ssRef.data) return __ssRef.data;
return null;
}
function scrollToRow(sheetIdx, rowIndex, linkEl) {
if (!__ssRef) return;
// Mark active
document.querySelectorAll('.toc-link.active').forEach(el => el.classList.remove('active'));
if (linkEl) linkEl.classList.add('active');
try {
const liveData = getSheetData(sheetIdx);
if (!liveData || !liveData.rows || typeof liveData.rows.sumHeight !== 'function') {
console.warn('No live sheet data for index', sheetIdx);
return;
}
const y = liveData.rows.sumHeight(0, rowIndex);
const vbar = __ssRef.sheet && __ssRef.sheet.verticalScrollbar;
if (vbar && vbar.el && vbar.el[0]) {
vbar.el[0].scrollTop = y;
}
} catch (e) {
console.warn('scrollToRow failed:', e);
}
}
// ---- Init ----
2026-06-29 15:32:32 +08:00
document.addEventListener('DOMContentLoaded', function () {
if (typeof x_spreadsheet === 'undefined') {
document.body.innerHTML += '<p style="color:red;margin-top:10px;">Error: x-spreadsheet library failed to load. Check internet connection.</p>';
return;
}
try {
const spreadsheet = x_spreadsheet('#xspreadsheet');
spreadsheet.loadData(sheets);
__ssRef = spreadsheet;
renderToc(sheets);
2026-06-29 15:32:32 +08:00
} catch (e) {
console.error('Error:', e);
document.body.innerHTML += '<p style="color:red;margin-top:10px;">Error: ' + e.message + '</p>';
}
});
</script>
</body>
</html>