Files
workutils/src/main/resources/templates/apidoc/api-doc.js
T
2026-07-28 15:56:01 +08:00

273 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ============================================================
* 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 = '<html><head><meta charset="utf-8"><style>' + getDocStyles() + '</style></head><body>' + wrap.innerHTML + '</body></html>';
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><head><meta charset="utf-8"><style>' + css + '</style></head><body>' + html + '</body></html>');
e.preventDefault();
});
});