모든 기능 복원 및 안정화

- PDF 다운로드 기능 복원 (직접 다운로드 + 연결된 PDF 지원)
- 언어 전환 기능 수정 (문서 내장 기능 활용)
- 헤더 네비게이션 버튼 구현 (뒤로가기, 목차, 이전/다음 문서)
- PDF 매칭 UI 추가 (book-documents.html)
- 링크/백링크 렌더링 안정화
- 서적 URL 파라미터 수정 (book_id 지원)

주요 수정사항:
- viewer-core.js: PDF 다운로드 로직 개선, 언어 전환 단순화
- book-documents.js/html: PDF 매칭 기능 및 URL 파라미터 수정
- components/header.html: 언어 전환 및 PDF 버튼 추가
- 모든 기능 테스트 완료 및 정상 작동 확인
This commit is contained in:
Hyungi Ahn
2025-08-28 15:15:27 +09:00
parent 8414c9b40e
commit 222e5bcb9e
5 changed files with 401 additions and 4 deletions

View File

@@ -35,8 +35,9 @@ window.bookDocumentsApp = () => ({
// URL 파라미터 파싱
parseUrlParams() {
const urlParams = new URLSearchParams(window.location.search);
this.bookId = urlParams.get('bookId');
this.bookId = urlParams.get('book_id') || urlParams.get('bookId'); // 둘 다 지원
console.log('📖 서적 ID:', this.bookId);
console.log('🔍 전체 URL 파라미터:', window.location.search);
},
// 인증 상태 확인
@@ -218,6 +219,57 @@ window.bookDocumentsApp = () => ({
if (type === 'error') {
alert(message);
}
},
// PDF를 서적에 연결
async matchPDFToBook(pdfId) {
if (!this.bookId) {
this.showNotification('서적 ID가 없습니다', 'error');
return;
}
if (!confirm('이 PDF를 현재 서적에 연결하시겠습니까?')) {
return;
}
try {
console.log('🔗 PDF 매칭 시작:', { pdfId, bookId: this.bookId });
// PDF 문서를 서적에 연결
await window.api.updateDocument(pdfId, {
book_id: this.bookId
});
this.showNotification('PDF가 서적에 성공적으로 연결되었습니다');
// 데이터 새로고침
await this.loadBookData();
} catch (error) {
console.error('PDF 매칭 실패:', error);
this.showNotification('PDF 연결에 실패했습니다: ' + error.message, 'error');
}
},
// PDF 열기
openPDF(pdf) {
if (pdf.pdf_path) {
// PDF 뷰어로 이동
window.open(`/viewer.html?id=${pdf.id}`, '_blank');
} else {
this.showNotification('PDF 파일을 찾을 수 없습니다', 'error');
}
},
// 날짜 포맷팅
formatDate(dateString) {
if (!dateString) return '';
const date = new Date(dateString);
return date.toLocaleDateString('ko-KR', {
year: 'numeric',
month: 'short',
day: 'numeric'
});
}
});

View File

@@ -287,8 +287,12 @@ class LinkManager {
* 개별 백링크 렌더링
*/
renderSingleBacklink(backlink) {
console.log('🔗 renderSingleBacklink 시작:', backlink.id, backlink.target_text);
const content = document.getElementById('document-content');
if (!content) return;
if (!content) {
console.error('❌ document-content 요소를 찾을 수 없습니다');
return;
}
// 실제 문서 내용만 추출 (CSS, 스크립트 제외)
const contentClone = content.cloneNode(true);

View File

@@ -800,17 +800,139 @@ window.documentViewer = () => ({
// ==================== 언어 전환 ====================
toggleLanguage() {
this.isKorean = !this.isKorean;
const lang = this.isKorean ? 'ko' : 'en';
console.log('🌐 언어 전환:', this.isKorean ? '한국어' : 'English');
// 언어 전환 로직 구현 필요
// 문서에 내장된 언어 전환 기능 찾기 및 실행
this.findAndExecuteBuiltinLanguageToggle();
},
// 문서에 내장된 언어 전환 기능 찾기
findAndExecuteBuiltinLanguageToggle() {
console.log('🔍 문서 내장 언어 전환 기능 찾기 시작');
const content = document.getElementById('document-content');
if (!content) {
console.warn('❌ document-content 요소를 찾을 수 없습니다');
return;
}
// 1. 언어 전환 버튼 찾기 (다양한 패턴)
const buttonSelectors = [
'button[onclick*="toggleLanguage"]',
'button[onclick*="language"]',
'button[onclick*="Language"]',
'.language-toggle',
'.lang-toggle',
'button[id*="lang"]',
'button[class*="lang"]',
'input[type="button"][onclick*="language"]'
];
let foundButton = null;
for (const selector of buttonSelectors) {
const buttons = content.querySelectorAll(selector);
if (buttons.length > 0) {
foundButton = buttons[0];
console.log(`✅ 언어 전환 버튼 발견 (${selector}):`, foundButton.outerHTML.substring(0, 100));
break;
}
}
// 2. 버튼이 있으면 클릭
if (foundButton) {
console.log('🔘 내장 언어 전환 버튼 클릭');
try {
foundButton.click();
console.log('✅ 언어 전환 버튼 클릭 완료');
return;
} catch (error) {
console.error('❌ 버튼 클릭 실패:', error);
}
}
// 3. 버튼이 없으면 스크립트 함수 직접 호출 시도
this.tryDirectLanguageFunction();
},
// 직접 언어 전환 함수 호출 시도
tryDirectLanguageFunction() {
console.log('🔧 직접 언어 전환 함수 호출 시도');
const functionNames = [
'toggleLanguage',
'changeLanguage',
'switchLanguage',
'toggleLang',
'changeLang'
];
for (const funcName of functionNames) {
if (typeof window[funcName] === 'function') {
console.log(`✅ 전역 함수 발견: ${funcName}`);
try {
window[funcName]();
console.log(`${funcName}() 호출 완료`);
return;
} catch (error) {
console.error(`${funcName}() 호출 실패:`, error);
}
}
}
// 4. 문서 내 스크립트에서 함수 찾기
this.findLanguageFunctionInScripts();
},
// 문서 내 스크립트에서 언어 전환 함수 찾기
findLanguageFunctionInScripts() {
console.log('📜 문서 내 스크립트에서 언어 함수 찾기');
const content = document.getElementById('document-content');
const scripts = content.querySelectorAll('script');
console.log(`📜 발견된 스크립트 태그: ${scripts.length}`);
scripts.forEach((script, index) => {
const scriptContent = script.textContent || script.innerHTML;
if (scriptContent.includes('language') || scriptContent.includes('Language') || scriptContent.includes('lang')) {
console.log(`📜 스크립트 ${index + 1}에서 언어 관련 코드 발견:`, scriptContent.substring(0, 200));
// 함수 실행 시도
try {
eval(scriptContent);
console.log(`✅ 스크립트 ${index + 1} 실행 완료`);
} catch (error) {
console.log(`⚠️ 스크립트 ${index + 1} 실행 실패:`, error.message);
}
}
});
console.log('⚠️ 내장 언어 전환 기능을 찾을 수 없습니다');
},
async downloadOriginalFile() {
if (!this.document || !this.document.id) {
console.warn('문서 정보가 없습니다');
return;
}
// 연결된 PDF가 있는지 확인
console.log('📕 PDF 다운로드 시도:', {
id: this.document.id,
matched_pdf_id: this.document.matched_pdf_id,
pdf_path: this.document.pdf_path
});
// 1. 현재 문서 자체가 PDF인 경우
if (this.document.pdf_path) {
console.log('📄 현재 문서가 PDF - 직접 다운로드');
this.downloadPdfFile(this.document.pdf_path, this.document.title || 'document');
return;
}
// 2. 연결된 PDF가 있는지 확인
if (!this.document.matched_pdf_id) {
alert('연결된 원본 PDF 파일이 없습니다.\n\n서적 편집 페이지에서 PDF 파일을 연결해주세요.');
return;
@@ -864,6 +986,39 @@ window.documentViewer = () => ({
}
},
// PDF 파일 직접 다운로드
downloadPdfFile(pdfPath, filename) {
try {
console.log('📄 PDF 파일 직접 다운로드:', pdfPath);
// PDF 파일 URL 생성 (상대 경로를 절대 경로로 변환)
let pdfUrl = pdfPath;
if (!pdfUrl.startsWith('http')) {
// 상대 경로인 경우 현재 도메인 기준으로 절대 경로 생성
const baseUrl = window.location.origin;
pdfUrl = pdfUrl.startsWith('/') ? baseUrl + pdfUrl : baseUrl + '/' + pdfUrl;
}
console.log('📄 PDF URL:', pdfUrl);
// 다운로드 링크 생성 및 클릭
const link = document.createElement('a');
link.href = pdfUrl;
link.download = filename.endsWith('.pdf') ? filename : filename + '.pdf';
link.target = '_blank'; // 새 탭에서 열기 (다운로드 실패 시 뷰어로 열림)
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
console.log('✅ PDF 다운로드 링크 클릭 완료');
} catch (error) {
console.error('PDF 다운로드 오류:', error);
alert('PDF 다운로드 중 오류가 발생했습니다: ' + error.message);
}
},
// ==================== 유틸리티 메서드 ====================
formatDate(dateString) {
return new Date(dateString).toLocaleString('ko-KR');
@@ -1027,6 +1182,30 @@ window.documentViewer = () => ({
alert('링크 생성에 실패했습니다: ' + error.message);
}
}
},
// 네비게이션 함수들
goBack() {
console.log('🔙 뒤로가기');
window.history.back();
},
navigateToDocument(documentId) {
if (!documentId) {
console.warn('⚠️ 문서 ID가 없습니다');
return;
}
console.log('📄 문서로 이동:', documentId);
window.location.href = `/viewer.html?id=${documentId}`;
},
goToBookContents() {
if (!this.navigation?.book_info?.id) {
console.warn('⚠️ 서적 정보가 없습니다');
return;
}
console.log('📚 서적 목차로 이동:', this.navigation.book_info.id);
window.location.href = `/book-documents.html?book_id=${this.navigation.book_info.id}`;
}
});