/* ============================================================ * API 接口文档交互脚本 * 从 ApiDocGenerator.appendJs() 提取,版块化便于独立维护 * * AI 定位指引: * - 搜索过滤 → 搜索 "filterDocs" * - 展开/折叠 → 搜索 "expandAll" / "collapseAll" * - 复制功能 → 搜索 "copyToClip" / "copyImage" / "copyRtfFromEl" * - DTO/字段展开 → 搜索 "toggleDto" / "toggleChildren" * ============================================================ */ /* ===== 导航分组折叠 ===== */ document.querySelectorAll('.nav-group-title').forEach(function(el) { el.classList.add('open'); }); function toggleGroup(el) { el.classList.toggle('open'); document.querySelectorAll('.nav-group-title.open + .nav-group-items').forEach(function(i) { i.style.maxHeight = i.scrollHeight + 'px'; }); } document.querySelectorAll('.nav-group-title.open + .nav-group-items').forEach(function(i) { i.style.maxHeight = i.scrollHeight + 'px'; }); /* ===== 方法卡片折叠 ===== */ function toggleMethod(el) { el.classList.toggle('collapsed'); } function toggleDto(el) { el.classList.toggle('collapsed'); } /* ===== 字段子行折叠 ===== */ function toggleChildren(el) { el.classList.toggle('open'); var row = el.closest('tr'); if (!row) return; var d = parseInt(row.dataset.depth) || 0; var hide = el.classList.contains('open'); var next = row.nextElementSibling; while (next && next.classList.contains('field-row')) { var nd = parseInt(next.dataset.depth) || 0; if (nd <= d) break; next.style.display = hide ? '' : 'none'; next = next.nextElementSibling; } } /* ===== 展开/折叠全部 ===== */ function expandAll() { document.querySelectorAll('.method-header').forEach(function(h) { h.classList.remove('collapsed'); }); document.querySelectorAll('.dto-block-title').forEach(function(t) { t.classList.remove('collapsed'); }); } function collapseAll() { document.querySelectorAll('.method-header').forEach(function(h) { h.classList.add('collapsed'); }); document.querySelectorAll('.dto-block-title').forEach(function(t) { t.classList.add('collapsed'); }); } /* ===== 导航高亮(IntersectionObserver) ===== */ var observer = new IntersectionObserver(function(entries) { entries.forEach(function(e) { if (e.isIntersecting) { document.querySelectorAll('.nav-item.active').forEach(function(a) { a.classList.remove('active'); }); var n = document.querySelector('.nav-item[href="#' + e.target.id + '"]'); if (n) n.classList.add('active'); } }); }); document.querySelectorAll('.api-card[id]').forEach(function(c) { observer.observe(c); }); /* ===== 侧栏导航点击展开对应方法 ===== */ document.querySelectorAll('.nav-method').forEach(function(link) { link.addEventListener('click', function(e) { var targetId = this.getAttribute('href'); if (!targetId || !targetId.startsWith('#')) return; var el = document.getElementById(targetId.substring(1)); if (!el) return; var methodCard = el.closest('.method-card'); if (!methodCard) return; var header = methodCard.querySelector('.method-header'); if (header) header.classList.remove('collapsed'); var body = methodCard.querySelector('.method-body'); if (body) body.style.display = ''; var apiCard = methodCard.closest('.api-card'); if (apiCard) { apiCard.classList.remove('collapsed'); var apiBody = apiCard.querySelector('.api-body'); if (apiBody) apiBody.style.display = ''; } }); }); /* ===== 复制到剪贴板(纯文本) ===== */ function copyToClip(text, btn) { if (navigator.clipboard) { navigator.clipboard.writeText(text).then(function() { markCopied(btn, '✔ 已复制'); }).catch(function() { fallbackCopy(text, btn); }); } else { fallbackCopy(text, btn); } } function fallbackCopy(text, btn) { var ta = document.createElement('textarea'); ta.value = text; ta.style.position = 'fixed'; ta.style.left = '-999px'; document.body.appendChild(ta); ta.select(); try { document.execCommand('copy'); markCopied(btn, '✔ 已复制'); } catch (e) { markCopied(btn, '⚠ 失败'); } document.body.removeChild(ta); } function markCopied(btn, msg) { var orig = btn.textContent; btn.textContent = msg; btn.classList.add('copied'); setTimeout(function() { btn.textContent = orig; btn.classList.remove('copied'); }, 1500); } /* ===== 复制代码签名 ===== */ function copyCode(btn) { var pre = btn.closest('.method-signature'); if (!pre) { var card = btn.closest('.method-card'); if (card) pre = card.querySelector('.method-signature'); } var codeEl = pre ? pre.querySelector('code') : null; var text = codeEl ? codeEl.textContent : (pre ? pre.textContent.replace('复制', '') : ''); copyToClip(text.trim(), btn); } /* ===== 复制为图片(需要 html2canvas) ===== */ function copyImage(el, btn) { if (!el || typeof html2canvas === 'undefined') { markCopied(btn, '⚠ 不可用'); return; } var clone = el.cloneNode(true); clone.querySelectorAll('.method-header').forEach(function(h) { h.classList.remove('collapsed'); }); clone.querySelectorAll('.method-body').forEach(function(b) { b.style.display = ''; }); clone.querySelectorAll('.dto-block-title').forEach(function(t) { t.classList.remove('collapsed'); }); clone.style.position = 'fixed'; clone.style.left = '-9999px'; clone.style.top = '0'; clone.style.zIndex = '-1'; document.body.appendChild(clone); html2canvas(clone, { scale: 2, useCORS: true, allowTaint: true, backgroundColor: '#ffffff', windowWidth: clone.scrollWidth, windowHeight: clone.scrollHeight }).then(function(canvas) { canvas.toBlob(function(blob) { if (blob && navigator.clipboard && navigator.clipboard.write) { var item = new ClipboardItem({ 'image/png': blob }); navigator.clipboard.write([item]).then(function() { markCopied(btn, '✔ 图片已复制'); }).catch(function() { markCopied(btn, '⚠ 请粘贴'); }); } else { markCopied(btn, '⚠ 不支持'); } }, 'image/png'); }).catch(function() { markCopied(btn, '⚠ html2canvas 失败'); }).finally(function() { if (clone.parentNode) document.body.removeChild(clone); }); } /* ===== 复制为 Word 富文本 (RTF) ===== */ function copyRtfFromEl(el, btn) { if (!el) { markCopied(btn, '⚠ 无内容'); return; } var wrap = document.createElement('div'); wrap.innerHTML = el.innerHTML; // 清除按钮 wrap.querySelectorAll('button').forEach(function(b) { b.remove(); }); wrap.querySelectorAll('.copy-btn,.api-copy-btn,.method-copy-btn').forEach(function(b) { b.remove(); }); wrap.querySelectorAll('.field-expand-toggle').forEach(function(b) { b.remove(); }); // 展开所有折叠区域 wrap.querySelectorAll('.method-header').forEach(function(h) { h.classList.remove('collapsed'); }); wrap.querySelectorAll('.method-body').forEach(function(b) { b.style.display = ''; }); wrap.querySelectorAll('.dto-block-title').forEach(function(t) { t.classList.remove('collapsed'); }); // 清除 script/style wrap.querySelectorAll('script,style').forEach(function(s) { s.remove(); }); var html = '' + wrap.innerHTML + ''; if (navigator.clipboard && navigator.clipboard.write) { var blob = new Blob([html], { type: 'text/html' }); navigator.clipboard.write([new ClipboardItem({ 'text/html': blob, 'text/plain': new Blob([wrap.textContent || ''], { type: 'text/plain' }) })]) .then(function() { markCopied(btn, '✔ 富文本已复制'); }) .catch(function() { legacyRich(html, btn); markCopied(btn, '⚠ 已复制富文本'); }); } else { legacyRich(html, btn); markCopied(btn, '⚠ 已复制富文本'); } } /* ===== 获取文档样式 ===== */ function getDocStyles() { var css = ''; document.querySelectorAll('style').forEach(function(s) { css += s.textContent; }); return css; } /* ===== 旧版富文本复制兼容 ===== */ function legacyRich(html, btn) { var listener = function(e) { e.clipboardData.setData('text/html', html); e.clipboardData.setData('text/plain', ''); e.preventDefault(); document.removeEventListener('copy', listener); }; document.addEventListener('copy', listener); document.execCommand('copy'); document.removeEventListener('copy', listener); } /* ===== 搜索过滤 ===== */ function filterDocs() { var kw = (document.getElementById('searchInput') || {}).value; if (!kw || !kw.trim()) { resetSearch(); return; } kw = kw.trim().toLowerCase(); var visible = 0; // 导航过滤 document.querySelectorAll('.nav-group').forEach(function(g) { var hasVisible = false; g.querySelectorAll('.nav-item').forEach(function(item) { var txt = (item.textContent || '').toLowerCase(); if (txt.indexOf(kw) >= 0) { item.classList.remove('search-hidden'); hasVisible = true; visible++; } else { item.classList.add('search-hidden'); } }); if (hasVisible) { g.classList.remove('search-hidden'); var t = g.querySelector('.nav-group-title'); if (t) t.classList.add('open'); var items = g.querySelector('.nav-group-items'); if (items) items.style.maxHeight = items.scrollHeight + 'px'; } else { g.classList.add('search-hidden'); } }); // 内容过滤 document.querySelectorAll('.api-card').forEach(function(card) { var txt = (card.textContent || '').toLowerCase(); var match = txt.indexOf(kw) >= 0; if (!match) { card.querySelectorAll('.method-card').forEach(function(mc) { var mt = (mc.textContent || '').toLowerCase(); if (mt.indexOf(kw) >= 0) { match = true; mc.classList.remove('search-hidden'); mc.querySelector('.method-header').classList.remove('collapsed'); mc.querySelector('.method-body').style.display = ''; } else { mc.classList.add('search-hidden'); } }); } if (match) { card.classList.remove('search-hidden'); var body = card.querySelector('.api-body'); if (body) { body.style.display = ''; card.classList.remove('collapsed'); } } else { card.classList.add('search-hidden'); } }); var searchCount = document.getElementById('searchCount'); if (searchCount) searchCount.textContent = visible > 0 ? '匹配 ' + visible + ' 项' : '无匹配'; } function resetSearch() { document.querySelectorAll('.search-hidden').forEach(function(el) { el.classList.remove('search-hidden'); }); document.querySelectorAll('.search-highlight').forEach(function(el) { var p = el.parentNode; if (p) p.replaceChild(document.createTextNode(el.textContent), el); }); var searchCount = document.getElementById('searchCount'); if (searchCount) searchCount.textContent = ''; } /* ===== ESC 清空搜索 ===== */ (function() { var si = document.getElementById('searchInput'); if (si) { si.addEventListener('keydown', function(e) { if (e.key === 'Escape') { this.value = ''; resetSearch(); this.blur(); } }); } })(); /* ===== 手动选择区域复制富文本兼容 ===== */ document.querySelectorAll('.api-card .api-body, .method-body').forEach(function(el) { el.addEventListener('copy', function(e) { var sel = window.getSelection(); if (!sel.rangeCount) return; var range = sel.getRangeAt(0); var wrapper = document.createElement('div'); wrapper.appendChild(range.cloneContents()); wrapper.querySelectorAll('button,.copy-btn,.api-copy-btn,.method-copy-btn').forEach(function(b) { b.remove(); }); var html = wrapper.innerHTML; var css = getDocStyles(); e.clipboardData.setData('text/html', '' + html + ''); e.preventDefault(); }); });