|
@@ -19,7 +19,23 @@ const DOM = {
|
|
|
markdownEditor: null,
|
|
markdownEditor: null,
|
|
|
editorDocName: null,
|
|
editorDocName: null,
|
|
|
navPrev: null,
|
|
navPrev: null,
|
|
|
- navNext: null
|
|
|
|
|
|
|
+ navNext: null,
|
|
|
|
|
+ contentArea: null,
|
|
|
|
|
+ searchContainer: null,
|
|
|
|
|
+ searchToggleBtn: null,
|
|
|
|
|
+ searchInput: null,
|
|
|
|
|
+ searchBtn: null,
|
|
|
|
|
+ searchResults: null,
|
|
|
|
|
+ closeSearchBoxBtn: null,
|
|
|
|
|
+ saveBtn: null,
|
|
|
|
|
+ cancelBtn: null,
|
|
|
|
|
+ backToTopBtn: null,
|
|
|
|
|
+ docNavigation: null,
|
|
|
|
|
+ toggleLeft: null,
|
|
|
|
|
+ toggleRight: null,
|
|
|
|
|
+ categoryNameSpan: null,
|
|
|
|
|
+ currentDocResults: null,
|
|
|
|
|
+ otherDocResults: null
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 配置 marked
|
|
// 配置 marked
|
|
@@ -60,9 +76,9 @@ async function loadDocList() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
currentCategory = category;
|
|
currentCategory = category;
|
|
|
- const categoryNameSpan = document.querySelector('#category-title .category-name');
|
|
|
|
|
- if (categoryNameSpan) {
|
|
|
|
|
- categoryNameSpan.textContent = category;
|
|
|
|
|
|
|
+ if (!DOM.categoryNameSpan) DOM.categoryNameSpan = document.querySelector('#category-title .category-name');
|
|
|
|
|
+ if (DOM.categoryNameSpan) {
|
|
|
|
|
+ DOM.categoryNameSpan.textContent = category;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
@@ -175,15 +191,8 @@ async function loadDocument(docName, scrollToText = null) {
|
|
|
img.onerror = function() {
|
|
img.onerror = function() {
|
|
|
console.error('Failed to load image:', newSrc);
|
|
console.error('Failed to load image:', newSrc);
|
|
|
// 显示占位图片或错误提示
|
|
// 显示占位图片或错误提示
|
|
|
- this.alt = '图片加载失败: ' + src;
|
|
|
|
|
- this.style.border = '1px dashed #ccc';
|
|
|
|
|
- this.style.padding = '10px';
|
|
|
|
|
- this.style.minHeight = '100px';
|
|
|
|
|
- this.style.display = 'block';
|
|
|
|
|
- this.style.backgroundColor = '#f5f5f5';
|
|
|
|
|
- this.style.color = '#666';
|
|
|
|
|
- this.style.textAlign = 'center';
|
|
|
|
|
- this.style.lineHeight = '100px';
|
|
|
|
|
|
|
+ this.alt = `图片加载失败: ${src}`;
|
|
|
|
|
+ this.classList.add('img-error');
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
@@ -371,8 +380,8 @@ function updateDocNavigation() {
|
|
|
|
|
|
|
|
if (!DOM.navPrev || !DOM.navNext || docList.length === 0) return;
|
|
if (!DOM.navPrev || !DOM.navNext || docList.length === 0) return;
|
|
|
|
|
|
|
|
- const docNavigation = document.getElementById('doc-navigation');
|
|
|
|
|
- if (!docNavigation) return;
|
|
|
|
|
|
|
+ if (!DOM.docNavigation) DOM.docNavigation = document.getElementById('doc-navigation');
|
|
|
|
|
+ if (!DOM.docNavigation) return;
|
|
|
|
|
|
|
|
// 找到当前文档在列表中的索引
|
|
// 找到当前文档在列表中的索引
|
|
|
const currentIndex = docList.findIndex(doc => doc.name === currentDoc);
|
|
const currentIndex = docList.findIndex(doc => doc.name === currentDoc);
|
|
@@ -380,71 +389,62 @@ function updateDocNavigation() {
|
|
|
if (currentIndex === -1) {
|
|
if (currentIndex === -1) {
|
|
|
DOM.navPrev.style.display = 'none';
|
|
DOM.navPrev.style.display = 'none';
|
|
|
DOM.navNext.style.display = 'none';
|
|
DOM.navNext.style.display = 'none';
|
|
|
- docNavigation.className = 'doc-navigation';
|
|
|
|
|
|
|
+ DOM.docNavigation.className = 'doc-navigation';
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const hasPrev = currentIndex > 0;
|
|
const hasPrev = currentIndex > 0;
|
|
|
const hasNext = currentIndex < docList.length - 1;
|
|
const hasNext = currentIndex < docList.length - 1;
|
|
|
|
|
|
|
|
- // 处理上一页
|
|
|
|
|
- if (hasPrev) {
|
|
|
|
|
- const prevDoc = docList[currentIndex - 1];
|
|
|
|
|
- DOM.navPrev.style.display = 'flex';
|
|
|
|
|
- DOM.navPrev.querySelector('.nav-doc-name').textContent = prevDoc.name;
|
|
|
|
|
- DOM.navPrev.onclick = () => {
|
|
|
|
|
- loadDocument(prevDoc.name);
|
|
|
|
|
- updateURL(currentCategory, prevDoc.name);
|
|
|
|
|
- // 滚动到顶部
|
|
|
|
|
- const contentArea = document.getElementById('content-area');
|
|
|
|
|
- if (contentArea) {
|
|
|
|
|
- contentArea.scrollTo({ top: 0, behavior: 'smooth' });
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
- } else {
|
|
|
|
|
- DOM.navPrev.style.display = 'none';
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 辅助函数:设置导航按钮
|
|
|
|
|
+ const setupNavButton = (button, doc) => {
|
|
|
|
|
+ if (doc) {
|
|
|
|
|
+ button.style.display = 'flex';
|
|
|
|
|
+ button.querySelector('.nav-doc-name').textContent = doc.name;
|
|
|
|
|
+ button.onclick = () => {
|
|
|
|
|
+ loadDocument(doc.name);
|
|
|
|
|
+ updateURL(currentCategory, doc.name);
|
|
|
|
|
+ // 滚动到顶部
|
|
|
|
|
+ if (!DOM.contentArea) DOM.contentArea = document.getElementById('content-area');
|
|
|
|
|
+ if (DOM.contentArea) {
|
|
|
|
|
+ DOM.contentArea.scrollTo({ top: 0, behavior: 'smooth' });
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+ } else {
|
|
|
|
|
+ button.style.display = 'none';
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- // 处理下一页
|
|
|
|
|
- if (hasNext) {
|
|
|
|
|
- const nextDoc = docList[currentIndex + 1];
|
|
|
|
|
- DOM.navNext.style.display = 'flex';
|
|
|
|
|
- DOM.navNext.querySelector('.nav-doc-name').textContent = nextDoc.name;
|
|
|
|
|
- DOM.navNext.onclick = () => {
|
|
|
|
|
- loadDocument(nextDoc.name);
|
|
|
|
|
- updateURL(currentCategory, nextDoc.name);
|
|
|
|
|
- // 滚动到顶部
|
|
|
|
|
- const contentArea = document.getElementById('content-area');
|
|
|
|
|
- if (contentArea) {
|
|
|
|
|
- contentArea.scrollTo({ top: 0, behavior: 'smooth' });
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
- } else {
|
|
|
|
|
- DOM.navNext.style.display = 'none';
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // 设置上一页和下一页按钮
|
|
|
|
|
+ setupNavButton(DOM.navPrev, hasPrev ? docList[currentIndex - 1] : null);
|
|
|
|
|
+ setupNavButton(DOM.navNext, hasNext ? docList[currentIndex + 1] : null);
|
|
|
|
|
|
|
|
// 根据导航按钮状态添加类,用于CSS定位
|
|
// 根据导航按钮状态添加类,用于CSS定位
|
|
|
if (hasPrev && hasNext) {
|
|
if (hasPrev && hasNext) {
|
|
|
- docNavigation.className = 'doc-navigation has-both';
|
|
|
|
|
|
|
+ DOM.docNavigation.className = 'doc-navigation has-both';
|
|
|
} else if (hasPrev) {
|
|
} else if (hasPrev) {
|
|
|
- docNavigation.className = 'doc-navigation has-prev-only';
|
|
|
|
|
|
|
+ DOM.docNavigation.className = 'doc-navigation has-prev-only';
|
|
|
} else if (hasNext) {
|
|
} else if (hasNext) {
|
|
|
- docNavigation.className = 'doc-navigation has-next-only';
|
|
|
|
|
|
|
+ DOM.docNavigation.className = 'doc-navigation has-next-only';
|
|
|
} else {
|
|
} else {
|
|
|
- docNavigation.className = 'doc-navigation';
|
|
|
|
|
|
|
+ DOM.docNavigation.className = 'doc-navigation';
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 显示错误
|
|
// 显示错误
|
|
|
function showError(message) {
|
|
function showError(message) {
|
|
|
- const content = document.getElementById('markdown-content');
|
|
|
|
|
- content.innerHTML = `<div class="error-message">${message}</div>`;
|
|
|
|
|
|
|
+ if (!DOM.content) DOM.content = document.getElementById('markdown-content');
|
|
|
|
|
+ if (DOM.content) {
|
|
|
|
|
+ DOM.content.innerHTML = `<div class="error-message">${message}</div>`;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 切换侧边栏(移动端)
|
|
// 切换侧边栏(移动端)
|
|
|
function setupSidebarToggles() {
|
|
function setupSidebarToggles() {
|
|
|
- const toggleLeft = document.getElementById('toggle-left');
|
|
|
|
|
- const toggleRight = document.getElementById('toggle-right');
|
|
|
|
|
|
|
+ if (!DOM.toggleLeft) DOM.toggleLeft = document.getElementById('toggle-left');
|
|
|
|
|
+ if (!DOM.toggleRight) DOM.toggleRight = document.getElementById('toggle-right');
|
|
|
|
|
+ const toggleLeft = DOM.toggleLeft;
|
|
|
|
|
+ const toggleRight = DOM.toggleRight;
|
|
|
|
|
|
|
|
// 使用 DOM 缓存
|
|
// 使用 DOM 缓存
|
|
|
if (!DOM.leftSidebar) DOM.leftSidebar = document.getElementById('left-sidebar');
|
|
if (!DOM.leftSidebar) DOM.leftSidebar = document.getElementById('left-sidebar');
|
|
@@ -527,26 +527,34 @@ function setupSidebarToggles() {
|
|
|
|
|
|
|
|
// 搜索功能
|
|
// 搜索功能
|
|
|
function initSearch() {
|
|
function initSearch() {
|
|
|
- const searchContainer = document.getElementById('search-container');
|
|
|
|
|
- const searchToggleBtn = document.getElementById('search-toggle-btn');
|
|
|
|
|
- const searchInput = document.getElementById('search-input');
|
|
|
|
|
- const searchBtn = document.getElementById('search-btn');
|
|
|
|
|
- const searchResults = document.getElementById('search-results');
|
|
|
|
|
- const closeSearchBoxBtn = document.getElementById('close-search-box');
|
|
|
|
|
|
|
+ // 初始化搜索相关的DOM缓存
|
|
|
|
|
+ if (!DOM.searchContainer) DOM.searchContainer = document.getElementById('search-container');
|
|
|
|
|
+ if (!DOM.searchToggleBtn) DOM.searchToggleBtn = document.getElementById('search-toggle-btn');
|
|
|
|
|
+ if (!DOM.searchInput) DOM.searchInput = document.getElementById('search-input');
|
|
|
|
|
+ if (!DOM.searchBtn) DOM.searchBtn = document.getElementById('search-btn');
|
|
|
|
|
+ if (!DOM.searchResults) DOM.searchResults = document.getElementById('search-results');
|
|
|
|
|
+ if (!DOM.closeSearchBoxBtn) DOM.closeSearchBoxBtn = document.getElementById('close-search-box');
|
|
|
|
|
|
|
|
// 检查元素是否存在
|
|
// 检查元素是否存在
|
|
|
- if (!searchContainer || !searchToggleBtn || !searchInput || !searchBtn || !searchResults || !closeSearchBoxBtn) {
|
|
|
|
|
|
|
+ if (!DOM.searchContainer || !DOM.searchToggleBtn || !DOM.searchInput || !DOM.searchBtn || !DOM.searchResults || !DOM.closeSearchBoxBtn) {
|
|
|
console.error('Search elements not found:', {
|
|
console.error('Search elements not found:', {
|
|
|
- searchContainer: !!searchContainer,
|
|
|
|
|
- searchToggleBtn: !!searchToggleBtn,
|
|
|
|
|
- searchInput: !!searchInput,
|
|
|
|
|
- searchBtn: !!searchBtn,
|
|
|
|
|
- searchResults: !!searchResults,
|
|
|
|
|
- closeSearchBoxBtn: !!closeSearchBoxBtn
|
|
|
|
|
|
|
+ searchContainer: !!DOM.searchContainer,
|
|
|
|
|
+ searchToggleBtn: !!DOM.searchToggleBtn,
|
|
|
|
|
+ searchInput: !!DOM.searchInput,
|
|
|
|
|
+ searchBtn: !!DOM.searchBtn,
|
|
|
|
|
+ searchResults: !!DOM.searchResults,
|
|
|
|
|
+ closeSearchBoxBtn: !!DOM.closeSearchBoxBtn
|
|
|
});
|
|
});
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ const searchContainer = DOM.searchContainer;
|
|
|
|
|
+ const searchToggleBtn = DOM.searchToggleBtn;
|
|
|
|
|
+ const searchInput = DOM.searchInput;
|
|
|
|
|
+ const searchBtn = DOM.searchBtn;
|
|
|
|
|
+ const searchResults = DOM.searchResults;
|
|
|
|
|
+ const closeSearchBoxBtn = DOM.closeSearchBoxBtn;
|
|
|
|
|
+
|
|
|
let searchTimeout;
|
|
let searchTimeout;
|
|
|
const SEARCH_HISTORY_KEY = 'cjydocs_search_history';
|
|
const SEARCH_HISTORY_KEY = 'cjydocs_search_history';
|
|
|
|
|
|
|
@@ -627,9 +635,7 @@ function initSearch() {
|
|
|
// 切换搜索框显示/隐藏
|
|
// 切换搜索框显示/隐藏
|
|
|
searchToggleBtn.addEventListener('click', (e) => {
|
|
searchToggleBtn.addEventListener('click', (e) => {
|
|
|
e.stopPropagation();
|
|
e.stopPropagation();
|
|
|
- console.log('Search button clicked');
|
|
|
|
|
const isActive = searchContainer.classList.toggle('active');
|
|
const isActive = searchContainer.classList.toggle('active');
|
|
|
- console.log('Search container active:', isActive);
|
|
|
|
|
|
|
|
|
|
if (isActive) {
|
|
if (isActive) {
|
|
|
// 加载搜索历史
|
|
// 加载搜索历史
|
|
@@ -675,13 +681,11 @@ function initSearch() {
|
|
|
|
|
|
|
|
// 点击外部关闭
|
|
// 点击外部关闭
|
|
|
document.addEventListener('click', (e) => {
|
|
document.addEventListener('click', (e) => {
|
|
|
- const searchToggle = document.getElementById('search-toggle-btn');
|
|
|
|
|
-
|
|
|
|
|
if (!searchResults.contains(e.target) &&
|
|
if (!searchResults.contains(e.target) &&
|
|
|
!searchInput.contains(e.target) &&
|
|
!searchInput.contains(e.target) &&
|
|
|
!searchBtn.contains(e.target) &&
|
|
!searchBtn.contains(e.target) &&
|
|
|
!searchContainer.contains(e.target) &&
|
|
!searchContainer.contains(e.target) &&
|
|
|
- !searchToggle.contains(e.target)) {
|
|
|
|
|
|
|
+ !searchToggleBtn.contains(e.target)) {
|
|
|
searchResults.style.display = 'none';
|
|
searchResults.style.display = 'none';
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
@@ -692,39 +696,46 @@ function initSearch() {
|
|
|
|
|
|
|
|
// 显示搜索错误
|
|
// 显示搜索错误
|
|
|
function displaySearchError(message) {
|
|
function displaySearchError(message) {
|
|
|
- const currentDocSection = document.querySelector('#current-doc-results .results-list');
|
|
|
|
|
- const otherDocsSection = document.querySelector('#other-docs-results .results-list');
|
|
|
|
|
- const searchResults = document.getElementById('search-results');
|
|
|
|
|
|
|
+ if (!DOM.currentDocResults) DOM.currentDocResults = document.getElementById('current-doc-results');
|
|
|
|
|
+ if (!DOM.otherDocResults) DOM.otherDocResults = document.getElementById('other-docs-results');
|
|
|
|
|
+ const currentDocSection = DOM.currentDocResults ? DOM.currentDocResults.querySelector('.results-list') : null;
|
|
|
|
|
+ const otherDocsSection = DOM.otherDocResults ? DOM.otherDocResults.querySelector('.results-list') : null;
|
|
|
|
|
|
|
|
// 清空之前的结果
|
|
// 清空之前的结果
|
|
|
currentDocSection.innerHTML = '';
|
|
currentDocSection.innerHTML = '';
|
|
|
otherDocsSection.innerHTML = '';
|
|
otherDocsSection.innerHTML = '';
|
|
|
|
|
|
|
|
// 隐藏分组标题
|
|
// 隐藏分组标题
|
|
|
- document.getElementById('current-doc-results').style.display = 'none';
|
|
|
|
|
- document.getElementById('other-docs-results').style.display = 'none';
|
|
|
|
|
|
|
+ DOM.currentDocResults.style.display = 'none';
|
|
|
|
|
+ DOM.otherDocResults.style.display = 'none';
|
|
|
|
|
|
|
|
// 显示错误信息
|
|
// 显示错误信息
|
|
|
- currentDocSection.innerHTML = `<p class="search-error" style="color: #d73a49; padding: 20px; text-align: center;">${message}</p>`;
|
|
|
|
|
- document.getElementById('current-doc-results').style.display = 'block';
|
|
|
|
|
|
|
+ if (currentDocSection) {
|
|
|
|
|
+ currentDocSection.innerHTML = `<p class="search-error" style="color: #d73a49; padding: 20px; text-align: center;">${message}</p>`;
|
|
|
|
|
+ }
|
|
|
|
|
+ DOM.currentDocResults.style.display = 'block';
|
|
|
|
|
|
|
|
// 显示搜索结果面板
|
|
// 显示搜索结果面板
|
|
|
- searchResults.style.display = 'block';
|
|
|
|
|
|
|
+ if (DOM.searchResults) {
|
|
|
|
|
+ DOM.searchResults.style.display = 'block';
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 显示搜索结果
|
|
// 显示搜索结果
|
|
|
function displaySearchResults(data) {
|
|
function displaySearchResults(data) {
|
|
|
- const currentDocSection = document.querySelector('#current-doc-results .results-list');
|
|
|
|
|
- const otherDocsSection = document.querySelector('#other-docs-results .results-list');
|
|
|
|
|
|
|
+ if (!DOM.currentDocResults) DOM.currentDocResults = document.getElementById('current-doc-results');
|
|
|
|
|
+ if (!DOM.otherDocResults) DOM.otherDocResults = document.getElementById('other-docs-results');
|
|
|
|
|
+ const currentDocSection = DOM.currentDocResults ? DOM.currentDocResults.querySelector('.results-list') : null;
|
|
|
|
|
+ const otherDocsSection = DOM.otherDocResults ? DOM.otherDocResults.querySelector('.results-list') : null;
|
|
|
|
|
|
|
|
// 清空之前的结果
|
|
// 清空之前的结果
|
|
|
currentDocSection.innerHTML = '';
|
|
currentDocSection.innerHTML = '';
|
|
|
otherDocsSection.innerHTML = '';
|
|
otherDocsSection.innerHTML = '';
|
|
|
|
|
|
|
|
// 隐藏/显示分组标题
|
|
// 隐藏/显示分组标题
|
|
|
- document.getElementById('current-doc-results').style.display =
|
|
|
|
|
|
|
+ DOM.currentDocResults.style.display =
|
|
|
data.currentDoc.length > 0 ? 'block' : 'none';
|
|
data.currentDoc.length > 0 ? 'block' : 'none';
|
|
|
- document.getElementById('other-docs-results').style.display =
|
|
|
|
|
|
|
+ DOM.otherDocResults.style.display =
|
|
|
data.otherDocs.length > 0 ? 'block' : 'none';
|
|
data.otherDocs.length > 0 ? 'block' : 'none';
|
|
|
|
|
|
|
|
// 渲染当前文档结果
|
|
// 渲染当前文档结果
|
|
@@ -767,7 +778,7 @@ function createDocResultElement(doc, isCurrent) {
|
|
|
matchItem.className = 'result-match';
|
|
matchItem.className = 'result-match';
|
|
|
|
|
|
|
|
// 高亮搜索词
|
|
// 高亮搜索词
|
|
|
- const highlightedSnippet = highlightSearchTerm(match.snippet, document.getElementById('search-input').value);
|
|
|
|
|
|
|
+ const highlightedSnippet = highlightSearchTerm(match.snippet, DOM.searchInput ? DOM.searchInput.value : '');
|
|
|
|
|
|
|
|
matchItem.innerHTML = `
|
|
matchItem.innerHTML = `
|
|
|
<div class="match-line-number">行 ${match.line}</div>
|
|
<div class="match-line-number">行 ${match.line}</div>
|
|
@@ -777,10 +788,12 @@ function createDocResultElement(doc, isCurrent) {
|
|
|
// 点击跳转
|
|
// 点击跳转
|
|
|
matchItem.onclick = () => {
|
|
matchItem.onclick = () => {
|
|
|
// 隐藏搜索框和搜索结果
|
|
// 隐藏搜索框和搜索结果
|
|
|
- const searchContainer = document.querySelector('.search-container');
|
|
|
|
|
- const searchResults = document.getElementById('search-results');
|
|
|
|
|
- searchContainer.classList.remove('active');
|
|
|
|
|
- searchResults.style.display = 'none';
|
|
|
|
|
|
|
+ if (DOM.searchContainer) {
|
|
|
|
|
+ DOM.searchContainer.classList.remove('active');
|
|
|
|
|
+ }
|
|
|
|
|
+ if (DOM.searchResults) {
|
|
|
|
|
+ DOM.searchResults.style.display = 'none';
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// 在移动端和平板上关闭所有侧边栏,确保用户能看到跳转后的内容
|
|
// 在移动端和平板上关闭所有侧边栏,确保用户能看到跳转后的内容
|
|
|
if (window.innerWidth <= 1024) {
|
|
if (window.innerWidth <= 1024) {
|
|
@@ -872,7 +885,8 @@ function scrollToSearchMatch(fullLine) {
|
|
|
|
|
|
|
|
// 设置回到顶部按钮
|
|
// 设置回到顶部按钮
|
|
|
function setupBackToTop() {
|
|
function setupBackToTop() {
|
|
|
- const backToTopBtn = document.getElementById('back-to-top');
|
|
|
|
|
|
|
+ if (!DOM.backToTopBtn) DOM.backToTopBtn = document.getElementById('back-to-top');
|
|
|
|
|
+ const backToTopBtn = DOM.backToTopBtn;
|
|
|
|
|
|
|
|
if (!backToTopBtn) return;
|
|
if (!backToTopBtn) return;
|
|
|
|
|
|
|
@@ -919,9 +933,12 @@ function setupEditFeature() {
|
|
|
if (!DOM.markdownEditor) DOM.markdownEditor = document.getElementById('markdown-editor');
|
|
if (!DOM.markdownEditor) DOM.markdownEditor = document.getElementById('markdown-editor');
|
|
|
if (!DOM.editorDocName) DOM.editorDocName = document.getElementById('editor-doc-name');
|
|
if (!DOM.editorDocName) DOM.editorDocName = document.getElementById('editor-doc-name');
|
|
|
|
|
|
|
|
- const saveBtn = document.getElementById('save-btn');
|
|
|
|
|
- const cancelBtn = document.getElementById('cancel-edit-btn');
|
|
|
|
|
- const contentArea = document.getElementById('content-area');
|
|
|
|
|
|
|
+ if (!DOM.saveBtn) DOM.saveBtn = document.getElementById('save-btn');
|
|
|
|
|
+ if (!DOM.cancelBtn) DOM.cancelBtn = document.getElementById('cancel-edit-btn');
|
|
|
|
|
+ if (!DOM.contentArea) DOM.contentArea = document.getElementById('content-area');
|
|
|
|
|
+ const saveBtn = DOM.saveBtn;
|
|
|
|
|
+ const cancelBtn = DOM.cancelBtn;
|
|
|
|
|
+ const contentArea = DOM.contentArea;
|
|
|
|
|
|
|
|
// 编辑按钮点击事件 - 切换编辑/查看模式
|
|
// 编辑按钮点击事件 - 切换编辑/查看模式
|
|
|
DOM.editBtn.addEventListener('click', () => {
|
|
DOM.editBtn.addEventListener('click', () => {
|
|
@@ -957,8 +974,8 @@ function enterEditMode() {
|
|
|
DOM.editBtn.title = '退出编辑';
|
|
DOM.editBtn.title = '退出编辑';
|
|
|
|
|
|
|
|
// 保存当前滚动位置
|
|
// 保存当前滚动位置
|
|
|
- const contentArea = document.getElementById('content-area');
|
|
|
|
|
- savedScrollPosition = contentArea.scrollTop;
|
|
|
|
|
|
|
+ if (!DOM.contentArea) DOM.contentArea = document.getElementById('content-area');
|
|
|
|
|
+ savedScrollPosition = DOM.contentArea.scrollTop;
|
|
|
|
|
|
|
|
// 找到当前视口中心附近的元素
|
|
// 找到当前视口中心附近的元素
|
|
|
const visibleElement = findVisibleElement();
|
|
const visibleElement = findVisibleElement();
|
|
@@ -978,7 +995,7 @@ function enterEditMode() {
|
|
|
positionCursorByElement(visibleElement);
|
|
positionCursorByElement(visibleElement);
|
|
|
} else {
|
|
} else {
|
|
|
// 如果找不到元素,使用滚动比例
|
|
// 如果找不到元素,使用滚动比例
|
|
|
- const scrollRatio = savedScrollPosition / contentArea.scrollHeight;
|
|
|
|
|
|
|
+ const scrollRatio = savedScrollPosition / DOM.contentArea.scrollHeight;
|
|
|
DOM.markdownEditor.scrollTop = DOM.markdownEditor.scrollHeight * scrollRatio;
|
|
DOM.markdownEditor.scrollTop = DOM.markdownEditor.scrollHeight * scrollRatio;
|
|
|
}
|
|
}
|
|
|
DOM.markdownEditor.focus();
|
|
DOM.markdownEditor.focus();
|
|
@@ -987,9 +1004,9 @@ function enterEditMode() {
|
|
|
|
|
|
|
|
// 找到当前视口中可见的元素
|
|
// 找到当前视口中可见的元素
|
|
|
function findVisibleElement() {
|
|
function findVisibleElement() {
|
|
|
- const contentArea = document.getElementById('content-area');
|
|
|
|
|
- const viewportTop = contentArea.scrollTop;
|
|
|
|
|
- const viewportMiddle = viewportTop + contentArea.clientHeight / 3; // 视口上方1/3处
|
|
|
|
|
|
|
+ if (!DOM.contentArea) DOM.contentArea = document.getElementById('content-area');
|
|
|
|
|
+ const viewportTop = DOM.contentArea.scrollTop;
|
|
|
|
|
+ const viewportMiddle = viewportTop + DOM.contentArea.clientHeight / 3; // 视口上方1/3处
|
|
|
|
|
|
|
|
// 查找所有重要元素(标题、段落等)
|
|
// 查找所有重要元素(标题、段落等)
|
|
|
const elements = DOM.content.querySelectorAll('h1, h2, h3, h4, h5, h6, p, li, blockquote, pre');
|
|
const elements = DOM.content.querySelectorAll('h1, h2, h3, h4, h5, h6, p, li, blockquote, pre');
|
|
@@ -1097,9 +1114,9 @@ function exitEditMode() {
|
|
|
DOM.content.style.display = 'block';
|
|
DOM.content.style.display = 'block';
|
|
|
|
|
|
|
|
// 恢复滚动位置
|
|
// 恢复滚动位置
|
|
|
- const contentArea = document.getElementById('content-area');
|
|
|
|
|
|
|
+ if (!DOM.contentArea) DOM.contentArea = document.getElementById('content-area');
|
|
|
setTimeout(() => {
|
|
setTimeout(() => {
|
|
|
- contentArea.scrollTop = savedScrollPosition;
|
|
|
|
|
|
|
+ DOM.contentArea.scrollTop = savedScrollPosition;
|
|
|
}, 0);
|
|
}, 0);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1113,9 +1130,9 @@ async function saveDocument() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 禁用保存按钮,防止重复点击
|
|
// 禁用保存按钮,防止重复点击
|
|
|
- const saveBtn = document.getElementById('save-btn');
|
|
|
|
|
- saveBtn.disabled = true;
|
|
|
|
|
- saveBtn.textContent = '保存中...';
|
|
|
|
|
|
|
+ if (!DOM.saveBtn) DOM.saveBtn = document.getElementById('save-btn');
|
|
|
|
|
+ DOM.saveBtn.disabled = true;
|
|
|
|
|
+ DOM.saveBtn.textContent = '保存中...';
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
const response = await fetch(`/api/doc/${encodeURIComponent(currentCategory)}/${encodeURIComponent(currentDoc)}`, {
|
|
const response = await fetch(`/api/doc/${encodeURIComponent(currentCategory)}/${encodeURIComponent(currentDoc)}`, {
|
|
@@ -1156,8 +1173,8 @@ async function saveDocument() {
|
|
|
alert(`保存失败:${error.message || '请稍后重试'}`);
|
|
alert(`保存失败:${error.message || '请稍后重试'}`);
|
|
|
} finally {
|
|
} finally {
|
|
|
// 恢复保存按钮
|
|
// 恢复保存按钮
|
|
|
- saveBtn.disabled = false;
|
|
|
|
|
- saveBtn.textContent = '保存';
|
|
|
|
|
|
|
+ DOM.saveBtn.disabled = false;
|
|
|
|
|
+ DOM.saveBtn.textContent = '保存';
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1219,7 +1236,7 @@ function addCopyButtonsToCodeBlocks() {
|
|
|
`;
|
|
`;
|
|
|
|
|
|
|
|
// 添加点击事件
|
|
// 添加点击事件
|
|
|
- copyBtn.addEventListener('click', function() {
|
|
|
|
|
|
|
+ copyBtn.addEventListener('click', () => {
|
|
|
copyCodeToClipboard(pre, copyBtn);
|
|
copyCodeToClipboard(pre, copyBtn);
|
|
|
});
|
|
});
|
|
|
|
|
|