855 lines
18 KiB
HTML
855 lines
18 KiB
HTML
<!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; }
|
||
</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>
|
||
|
||
<script src="https://unpkg.com/x-data-spreadsheet@1.1.9/dist/xspreadsheet.js"></script>
|
||
<script>
|
||
const rawData = {
|
||
"name": "Table_0",
|
||
"rows": {
|
||
"0": {
|
||
"cells": {
|
||
"19": {
|
||
"text": "PD4.2.2",
|
||
"merge": [
|
||
0,
|
||
2
|
||
]
|
||
}
|
||
}
|
||
},
|
||
"1": {
|
||
"cells": {
|
||
"0": {
|
||
"text": "基本要求",
|
||
"merge": [
|
||
0,
|
||
1
|
||
]
|
||
},
|
||
"2": {
|
||
"text": "01.在路基用地和取土坑范国内,己清除地表植被、杂物、积水、淤泥和表土,处理坑塘,并按施工技术规范和设计要求对基底进行压实.表土己充分利用.口2.坝方路基分层坝筑压实,每层表面平整,路拱合适,排水良好,无明显碾压轮迹,无亏坡.口3.己设置施工临时排水系统,避免冲刷边坡,路床顶面无积水.4.在设定取土区内合理取土,无溢开滥挖.完工后己按要求对取土坑和奔土场进行依整.",
|
||
"merge": [
|
||
0,
|
||
19
|
||
]
|
||
}
|
||
}
|
||
},
|
||
"2": {
|
||
"cells": {
|
||
"1": {
|
||
"text": "项次",
|
||
"merge": [
|
||
1,
|
||
0
|
||
]
|
||
},
|
||
"2": {
|
||
"text": "检查项目",
|
||
"merge": [
|
||
1,
|
||
3
|
||
]
|
||
},
|
||
"6": {
|
||
"text": "规定值或允许偏差",
|
||
"merge": [
|
||
0,
|
||
2
|
||
]
|
||
},
|
||
"9": {
|
||
"text": "实测值或实测差值",
|
||
"merge": [
|
||
0,
|
||
9
|
||
]
|
||
},
|
||
"19": {
|
||
"text": "质量评定",
|
||
"merge": [
|
||
0,
|
||
2
|
||
]
|
||
}
|
||
}
|
||
},
|
||
"3": {
|
||
"cells": {
|
||
"6": {
|
||
"text": "商速公路一级公路"
|
||
},
|
||
"7": {
|
||
"text": "二级公路"
|
||
},
|
||
"8": {
|
||
"text": "三、四级公路"
|
||
},
|
||
"9": {
|
||
"text": "1"
|
||
},
|
||
"10": {
|
||
"text": "2"
|
||
},
|
||
"11": {
|
||
"text": "3"
|
||
},
|
||
"12": {
|
||
"text": "4"
|
||
},
|
||
"13": {
|
||
"text": "5"
|
||
},
|
||
"14": {
|
||
"text": "6"
|
||
},
|
||
"15": {
|
||
"text": "7"
|
||
},
|
||
"16": {
|
||
"text": "8"
|
||
},
|
||
"17": {
|
||
"text": "9"
|
||
},
|
||
"18": {
|
||
"text": "10"
|
||
},
|
||
"19": {
|
||
"text": "平均值、代表值"
|
||
},
|
||
"20": {
|
||
"text": "合格率(%)"
|
||
},
|
||
"21": {
|
||
"text": "合格判定"
|
||
}
|
||
}
|
||
},
|
||
"4": {
|
||
"cells": {
|
||
"1": {
|
||
"text": "1A",
|
||
"merge": [
|
||
6,
|
||
0
|
||
]
|
||
},
|
||
"2": {
|
||
"text": "医卖医卖",
|
||
"merge": [
|
||
6,
|
||
0
|
||
]
|
||
},
|
||
"3": {
|
||
"text": "上路床",
|
||
"merge": [
|
||
0,
|
||
1
|
||
]
|
||
},
|
||
"5": {
|
||
"text": "0~0.3m"
|
||
},
|
||
"6": {
|
||
"text": "≥96"
|
||
},
|
||
"7": {
|
||
"text": "≥95"
|
||
},
|
||
"8": {
|
||
"text": "≥94"
|
||
}
|
||
}
|
||
},
|
||
"5": {
|
||
"cells": {
|
||
"3": {
|
||
"text": "下路床",
|
||
"merge": [
|
||
1,
|
||
0
|
||
]
|
||
},
|
||
"4": {
|
||
"text": "经、中及交通荷载等级"
|
||
},
|
||
"5": {
|
||
"text": "0.3m~0.8m"
|
||
},
|
||
"6": {
|
||
"text": "96"
|
||
},
|
||
"7": {
|
||
"text": "≥95"
|
||
},
|
||
"8": {
|
||
"text": "≥94"
|
||
},
|
||
"9": {
|
||
"merge": [
|
||
0,
|
||
9
|
||
]
|
||
}
|
||
}
|
||
},
|
||
"6": {
|
||
"cells": {
|
||
"4": {
|
||
"text": "特重、极重交通荷载等级"
|
||
},
|
||
"5": {
|
||
"text": "0.3m~1.2m"
|
||
},
|
||
"6": {
|
||
"text": "296"
|
||
},
|
||
"7": {
|
||
"text": "≥95"
|
||
},
|
||
"8": {
|
||
"text": "-"
|
||
}
|
||
}
|
||
},
|
||
"7": {
|
||
"cells": {
|
||
"3": {
|
||
"text": "上路提",
|
||
"merge": [
|
||
1,
|
||
0
|
||
]
|
||
},
|
||
"4": {
|
||
"text": "轻、中及重交通荷载等级"
|
||
},
|
||
"5": {
|
||
"text": "0.8m~1.5m"
|
||
},
|
||
"6": {
|
||
"text": "≥94"
|
||
},
|
||
"7": {
|
||
"text": "≥94"
|
||
},
|
||
"8": {
|
||
"text": "≥93"
|
||
}
|
||
}
|
||
},
|
||
"8": {
|
||
"cells": {
|
||
"4": {
|
||
"text": "特薰、极薰交通荷载等级"
|
||
},
|
||
"5": {
|
||
"text": "1.2m~1.9m"
|
||
},
|
||
"6": {
|
||
"text": "≥94"
|
||
},
|
||
"7": {
|
||
"text": "≥94"
|
||
},
|
||
"8": {
|
||
"text": "-"
|
||
}
|
||
}
|
||
},
|
||
"9": {
|
||
"cells": {
|
||
"3": {
|
||
"text": "下路提",
|
||
"merge": [
|
||
1,
|
||
0
|
||
]
|
||
},
|
||
"4": {
|
||
"text": "轻、中及交通荷载等级"
|
||
},
|
||
"5": {
|
||
"text": "≥1.5m"
|
||
},
|
||
"6": {
|
||
"text": "≥93",
|
||
"merge": [
|
||
1,
|
||
0
|
||
]
|
||
},
|
||
"7": {
|
||
"text": "≥92",
|
||
"merge": [
|
||
1,
|
||
0
|
||
]
|
||
},
|
||
"8": {
|
||
"text": "≥90",
|
||
"merge": [
|
||
1,
|
||
0
|
||
]
|
||
}
|
||
}
|
||
},
|
||
"10": {
|
||
"cells": {
|
||
"4": {
|
||
"text": "特、极薰交通荷载等级"
|
||
},
|
||
"5": {
|
||
"text": "≥1.9m"
|
||
}
|
||
}
|
||
},
|
||
"11": {
|
||
"cells": {
|
||
"1": {
|
||
"text": "2A"
|
||
},
|
||
"2": {
|
||
"text": "弯沉(0.01mm)",
|
||
"merge": [
|
||
0,
|
||
3
|
||
]
|
||
},
|
||
"6": {
|
||
"text": "≤设计验收弯沉值",
|
||
"merge": [
|
||
0,
|
||
2
|
||
]
|
||
},
|
||
"9": {
|
||
"merge": [
|
||
0,
|
||
9
|
||
]
|
||
}
|
||
}
|
||
},
|
||
"12": {
|
||
"cells": {
|
||
"1": {
|
||
"text": "3"
|
||
},
|
||
"2": {
|
||
"text": "纵断商程(mm)",
|
||
"merge": [
|
||
0,
|
||
3
|
||
]
|
||
},
|
||
"6": {
|
||
"text": "+10,-15"
|
||
},
|
||
"7": {
|
||
"text": "+10.-20",
|
||
"merge": [
|
||
0,
|
||
1
|
||
]
|
||
}
|
||
}
|
||
},
|
||
"13": {
|
||
"cells": {
|
||
"1": {
|
||
"text": "4"
|
||
},
|
||
"2": {
|
||
"text": "中线偏位(mm)",
|
||
"merge": [
|
||
0,
|
||
3
|
||
]
|
||
},
|
||
"6": {
|
||
"text": "50"
|
||
},
|
||
"7": {
|
||
"text": "100",
|
||
"merge": [
|
||
0,
|
||
1
|
||
]
|
||
}
|
||
}
|
||
},
|
||
"14": {
|
||
"cells": {
|
||
"1": {
|
||
"text": "5"
|
||
},
|
||
"2": {
|
||
"text": "宽度(mm)",
|
||
"merge": [
|
||
0,
|
||
3
|
||
]
|
||
},
|
||
"6": {
|
||
"text": "满足设计要求",
|
||
"merge": [
|
||
0,
|
||
2
|
||
]
|
||
}
|
||
}
|
||
},
|
||
"15": {
|
||
"cells": {
|
||
"1": {
|
||
"text": "6"
|
||
},
|
||
"2": {
|
||
"text": "平整度(mm)",
|
||
"merge": [
|
||
0,
|
||
3
|
||
]
|
||
},
|
||
"6": {
|
||
"text": "415"
|
||
},
|
||
"7": {
|
||
"text": "520",
|
||
"merge": [
|
||
0,
|
||
1
|
||
]
|
||
}
|
||
}
|
||
},
|
||
"16": {
|
||
"cells": {
|
||
"1": {
|
||
"text": "7"
|
||
},
|
||
"2": {
|
||
"text": "横坡(%)",
|
||
"merge": [
|
||
0,
|
||
3
|
||
]
|
||
},
|
||
"6": {
|
||
"text": "±0.3"
|
||
},
|
||
"7": {
|
||
"text": "±0.5",
|
||
"merge": [
|
||
0,
|
||
1
|
||
]
|
||
}
|
||
}
|
||
},
|
||
"17": {
|
||
"cells": {
|
||
"1": {
|
||
"text": "8"
|
||
},
|
||
"2": {
|
||
"text": "边坡",
|
||
"merge": [
|
||
0,
|
||
3
|
||
]
|
||
},
|
||
"6": {
|
||
"text": "满足设计要求",
|
||
"merge": [
|
||
0,
|
||
2
|
||
]
|
||
}
|
||
}
|
||
},
|
||
"18": {
|
||
"cells": {
|
||
"0": {
|
||
"text": "外观质量",
|
||
"merge": [
|
||
0,
|
||
5
|
||
]
|
||
},
|
||
"6": {
|
||
"text": "01.路基边线与边坡未出现单向累计长度超过50m的弯折.口2.路基边坡、护坡道、碎离台无滑坡、爆方或深度超过100mm的冲沟.",
|
||
"merge": [
|
||
0,
|
||
8
|
||
]
|
||
},
|
||
"15": {
|
||
"text": "质量保证资料",
|
||
"merge": [
|
||
0,
|
||
3
|
||
]
|
||
}
|
||
}
|
||
},
|
||
"19": {
|
||
"cells": {
|
||
"0": {
|
||
"text": "工程质量等级评定",
|
||
"merge": [
|
||
0,
|
||
5
|
||
]
|
||
},
|
||
"6": {
|
||
"text": "口合格口不合格",
|
||
"merge": [
|
||
0,
|
||
13
|
||
]
|
||
}
|
||
}
|
||
},
|
||
"len": 20
|
||
},
|
||
"merges": [
|
||
"P19:S19",
|
||
"C17:F17",
|
||
"G19:O19",
|
||
"B3:B4",
|
||
"J12:S12",
|
||
"G18:I18",
|
||
"G3:I3",
|
||
"J3:S3",
|
||
"G10:G11",
|
||
"G15:I15",
|
||
"C13:F13",
|
||
"B5:B11",
|
||
"C18:F18",
|
||
"T1:V1",
|
||
"C15:F15",
|
||
"A20:F20",
|
||
"C14:F14",
|
||
"H17:I17",
|
||
"A2:B2",
|
||
"H16:I16",
|
||
"A19:F19",
|
||
"D8:D9",
|
||
"C16:F16",
|
||
"T3:V3",
|
||
"G12:I12",
|
||
"C2:V2",
|
||
"H13:I13",
|
||
"I10:I11",
|
||
"J6:S6",
|
||
"C5:C11",
|
||
"D10:D11",
|
||
"C3:F4",
|
||
"G20:T20",
|
||
"C12:F12",
|
||
"D5:E5",
|
||
"H14:I14",
|
||
"D6:D7",
|
||
"H10:H11"
|
||
],
|
||
"cols": {
|
||
"0": {
|
||
"width": 120
|
||
},
|
||
"1": {
|
||
"width": 120
|
||
},
|
||
"2": {
|
||
"width": 120
|
||
},
|
||
"3": {
|
||
"width": 120
|
||
},
|
||
"4": {
|
||
"width": 120
|
||
},
|
||
"5": {
|
||
"width": 120
|
||
},
|
||
"6": {
|
||
"width": 120
|
||
},
|
||
"7": {
|
||
"width": 120
|
||
},
|
||
"8": {
|
||
"width": 120
|
||
},
|
||
"9": {
|
||
"width": 120
|
||
},
|
||
"10": {
|
||
"width": 120
|
||
},
|
||
"11": {
|
||
"width": 120
|
||
},
|
||
"12": {
|
||
"width": 120
|
||
},
|
||
"13": {
|
||
"width": 120
|
||
},
|
||
"14": {
|
||
"width": 120
|
||
},
|
||
"15": {
|
||
"width": 120
|
||
},
|
||
"16": {
|
||
"width": 120
|
||
},
|
||
"17": {
|
||
"width": 120
|
||
},
|
||
"18": {
|
||
"width": 120
|
||
},
|
||
"19": {
|
||
"width": 120
|
||
},
|
||
"20": {
|
||
"width": 120
|
||
},
|
||
"21": {
|
||
"width": 120
|
||
}
|
||
}
|
||
};
|
||
// Normalize: x_spreadsheet.loadData accepts an array of sheets or a single sheet
|
||
const sheets = Array.isArray(rawData) ? rawData : [rawData];
|
||
|
||
// ---- 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 ----
|
||
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);
|
||
} catch (e) {
|
||
console.error('Error:', e);
|
||
document.body.innerHTML += '<p style="color:red;margin-top:10px;">Error: ' + e.message + '</p>';
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|