v3:추출 파이프라인_260122
This commit is contained in:
@@ -1073,6 +1073,7 @@
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="/static/css/editor.css">
|
||||
</head>
|
||||
<body>
|
||||
<!-- 상단 툴바 -->
|
||||
@@ -1081,10 +1082,10 @@
|
||||
|
||||
<div class="toolbar-spacer"></div>
|
||||
|
||||
<button class="toolbar-btn" id="editBtn" onclick="toggleEditMode()">✏️ 편집하기</button>
|
||||
|
||||
<button class="toolbar-btn" id="editModeBtn" onclick="toggleEditMode()">✏️ 편집하기</button>
|
||||
|
||||
<div class="toolbar-divider"></div>
|
||||
|
||||
|
||||
<select class="zoom-select" id="zoomSelect" onchange="setZoom(this.value)">
|
||||
<option value="50">50%</option>
|
||||
<option value="75">75%</option>
|
||||
@@ -1092,10 +1093,12 @@
|
||||
<option value="125">125%</option>
|
||||
<option value="150">150%</option>
|
||||
</select>
|
||||
|
||||
|
||||
<div class="toolbar-divider"></div>
|
||||
|
||||
|
||||
<button class="toolbar-btn" onclick="exportHwp()">📄 HWP 추출</button>
|
||||
<button class="toolbar-btn" onclick="saveHtml()">💾 HTML 저장</button>
|
||||
<button class="toolbar-btn" disabled title="준비중">📊 PPT 저장</button>
|
||||
<button class="toolbar-btn" onclick="printDoc()">🖨️ PDF/인쇄</button>
|
||||
</div>
|
||||
|
||||
@@ -1299,10 +1302,9 @@
|
||||
</div>
|
||||
|
||||
<!-- 보고서 -->
|
||||
<div class="doc-type-item disabled" data-type="report">
|
||||
<input type="radio" name="docType" disabled>
|
||||
<span class="label">📄 보고서</span>
|
||||
<span class="badge">준비중</span>
|
||||
<div class="doc-type-item" data-type="report" onclick="selectDocType('report')">
|
||||
<input type="radio" name="docType">
|
||||
<span class="label">📄 보고서</span>
|
||||
|
||||
<div class="doc-type-preview">
|
||||
<div class="preview-thumbnail report">
|
||||
@@ -1373,15 +1375,15 @@
|
||||
<div class="option-group">
|
||||
<div class="option-item" onclick="selectPageOption('1')">
|
||||
<input type="radio" name="pages" value="1" id="page1">
|
||||
<label for="page1">1p (본문만)</label>
|
||||
<label for="page1"> (본문) 1p</label>
|
||||
</div>
|
||||
<div class="option-item selected" onclick="selectPageOption('2')">
|
||||
<input type="radio" name="pages" value="2" id="page2" checked>
|
||||
<label for="page2">1p + 1p 첨부</label>
|
||||
<label for="page2"> (본문) 1p + (첨부) 1p</label>
|
||||
</div>
|
||||
<div class="option-item" onclick="selectPageOption('n')">
|
||||
<input type="radio" name="pages" value="n" id="pageN">
|
||||
<label for="pageN">1p + np 첨부 (자동)</label>
|
||||
<label for="pageN"> (본문) 1p + (첨부) np</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1393,6 +1395,43 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 보고서 옵션 -->
|
||||
<div id="reportOptions" style="display:none;">
|
||||
<!-- 보고서 구성 -->
|
||||
<div class="option-section">
|
||||
<div class="option-title">보고서 구성</div>
|
||||
<div class="option-group">
|
||||
<div class="option-item" style="cursor:default;">
|
||||
<input type="checkbox" id="reportCover" checked>
|
||||
<label for="reportCover">📘 표지</label>
|
||||
</div>
|
||||
<div class="option-item" style="cursor:default;">
|
||||
<input type="checkbox" id="reportToc" checked>
|
||||
<label for="reportToc">📑 목차</label>
|
||||
</div>
|
||||
<div class="option-item" style="cursor:default;">
|
||||
<input type="checkbox" id="reportDivider">
|
||||
<label for="reportDivider">📄 간지</label>
|
||||
</div>
|
||||
<div class="option-item" style="cursor:default; opacity:0.6;">
|
||||
<input type="checkbox" id="reportContent" checked disabled>
|
||||
<label for="reportContent">📝 내지 (필수)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 요청사항 -->
|
||||
<div class="option-section">
|
||||
<div class="option-title">요청사항</div>
|
||||
<textarea class="request-textarea" id="reportInstructionInput" placeholder="예: 요약을 상세하게 작성해줘 예: 표지에 로고 추가"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- 생성 버튼 -->
|
||||
<button class="generate-btn" id="generateBtn" onclick="generate()" disabled>
|
||||
<span id="generateBtnText">🚀 생성하기</span>
|
||||
@@ -1463,7 +1502,6 @@
|
||||
let generatedHTML = '';
|
||||
let currentDocType = 'briefing';
|
||||
let currentPageOption = '2';
|
||||
let isEditing = false;
|
||||
let currentZoom = 100;
|
||||
let folderPath = '';
|
||||
let referenceLinks = [];
|
||||
@@ -1472,6 +1510,53 @@
|
||||
let selectedText = '';
|
||||
let selectedRange = null;
|
||||
|
||||
// ===== HWP 추출 =====
|
||||
async function exportHwp() {
|
||||
if (!generatedHTML) {
|
||||
alert('먼저 문서를 생성해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 현재 편집된 HTML 가져오기
|
||||
const frame = document.getElementById('previewFrame');
|
||||
const html = frame.contentDocument ?
|
||||
'<!DOCTYPE html>' + frame.contentDocument.documentElement.outerHTML :
|
||||
generatedHTML;
|
||||
|
||||
setStatus('HWP 변환 중...', true);
|
||||
|
||||
try {
|
||||
const response = await fetch('/export-hwp', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
html: html,
|
||||
doc_type: currentDocType
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.error || 'HWP 변환 실패');
|
||||
}
|
||||
|
||||
// 파일 다운로드
|
||||
const blob = await response.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `report_${new Date().toISOString().slice(0,10)}.hwp`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
setStatus('HWP 변환 완료', true);
|
||||
|
||||
} catch (error) {
|
||||
alert('HWP 변환 오류: ' + error.message);
|
||||
setStatus('오류 발생', false);
|
||||
}
|
||||
}
|
||||
|
||||
// iframe 로드 후 선택 이벤트 연결
|
||||
function setupIframeSelection() {
|
||||
const frame = document.getElementById('previewFrame');
|
||||
@@ -1815,8 +1900,8 @@
|
||||
|
||||
// ===== 문서 유형 선택 =====
|
||||
function selectDocType(type) {
|
||||
if (type !== 'briefing') {
|
||||
return; // disabled 항목 클릭 무시
|
||||
if (type === 'presentation') {
|
||||
return; // PPT만 disabled
|
||||
}
|
||||
|
||||
currentDocType = type;
|
||||
@@ -1827,6 +1912,10 @@
|
||||
item.querySelector('input[type="radio"]').checked = true;
|
||||
}
|
||||
});
|
||||
|
||||
// 옵션 패널 표시/숨김
|
||||
document.getElementById('briefingOptions').style.display = (type === 'briefing') ? 'block' : 'none';
|
||||
document.getElementById('reportOptions').style.display = (type === 'report') ? 'block' : 'none';
|
||||
}
|
||||
|
||||
// ===== 템플릿 추가 =====
|
||||
@@ -1846,6 +1935,15 @@
|
||||
|
||||
// ===== 생성 =====
|
||||
async function generate() {
|
||||
if (currentDocType === 'briefing') {
|
||||
await generateBriefing();
|
||||
} else if (currentDocType === 'report') {
|
||||
await generateReport();
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 기획서 생성 (기존 로직) =====
|
||||
async function generateBriefing() {
|
||||
if (!inputContent && !folderPath && referenceLinks.length === 0) {
|
||||
alert('먼저 폴더 위치, 참고 링크, 또는 HTML을 입력해주세요.');
|
||||
return;
|
||||
@@ -1900,20 +1998,15 @@
|
||||
|
||||
if (data.success && data.html) {
|
||||
generatedHTML = data.html;
|
||||
|
||||
// 미리보기 표시
|
||||
document.getElementById('placeholder').style.display = 'none';
|
||||
const frame = document.getElementById('previewFrame');
|
||||
frame.classList.add('active');
|
||||
frame.srcdoc = generatedHTML;
|
||||
setTimeout(setupIframeSelection, 500);
|
||||
|
||||
// 피드백 바 표시
|
||||
setTimeout(setupIframeSelection, 500); // ← 이 줄 추가
|
||||
document.getElementById('feedbackBar').classList.add('show');
|
||||
|
||||
setStatus('생성 완료', true);
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
alert('생성 오류: ' + error.message);
|
||||
setStatus('오류 발생', false);
|
||||
@@ -1931,8 +2024,85 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 보고서 생성 (새로 추가) =====
|
||||
async function generateReport() {
|
||||
if (!folderPath && !inputContent) {
|
||||
alert('폴더 위치 또는 HTML을 입력해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
const btn = document.getElementById('generateBtn');
|
||||
const btnText = document.getElementById('generateBtnText');
|
||||
const spinner = document.getElementById('generateSpinner');
|
||||
|
||||
btn.disabled = true;
|
||||
btnText.textContent = '생성 중...';
|
||||
spinner.style.display = 'block';
|
||||
resetSteps();
|
||||
|
||||
// 체크박스 값 수집
|
||||
const options = {
|
||||
content: inputContent, // ← 추가!
|
||||
folder_path: folderPath,
|
||||
cover: document.getElementById('reportCover').checked,
|
||||
toc: document.getElementById('reportToc').checked,
|
||||
divider: document.getElementById('reportDivider').checked,
|
||||
instruction: document.getElementById('reportInstructionInput').value
|
||||
};
|
||||
|
||||
setStatus('보고서 생성 중...', true);
|
||||
|
||||
try {
|
||||
// Step 1~9 진행 표시
|
||||
for (let i = 1; i <= 9; i++) {
|
||||
updateStep(i, 'running');
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
// TODO: 실제 API 호출
|
||||
updateStep(i, 'done');
|
||||
}
|
||||
|
||||
const response = await fetch('/generate-report', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
content: inputContent, // HTML 내용 추가
|
||||
folder_path: folderPath,
|
||||
cover: document.getElementById('reportCover').checked,
|
||||
toc: document.getElementById('reportToc').checked,
|
||||
divider: document.getElementById('reportDivider').checked,
|
||||
instruction: document.getElementById('reportInstructionInput').value
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(data.error);
|
||||
}
|
||||
|
||||
if (data.success && data.html) {
|
||||
generatedHTML = data.html;
|
||||
document.getElementById('placeholder').style.display = 'none';
|
||||
const frame = document.getElementById('previewFrame');
|
||||
frame.classList.add('active');
|
||||
frame.srcdoc = generatedHTML;
|
||||
setTimeout(setupIframeSelection, 500); // ← 추가!
|
||||
document.getElementById('feedbackBar').classList.add('show');
|
||||
setStatus('생성 완료', true);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
alert('생성 오류: ' + error.message);
|
||||
setStatus('오류 발생', false);
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btnText.textContent = '🚀 생성하기';
|
||||
spinner.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 피드백 수정 =====
|
||||
async function submitFeedback() {
|
||||
async function submitFeedback() {
|
||||
const feedback = document.getElementById('feedbackInput').value.trim();
|
||||
if (!feedback) {
|
||||
alert('수정 내용을 입력해주세요.');
|
||||
@@ -1998,28 +2168,6 @@ async function submitFeedback() {
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 편집 모드 =====
|
||||
function toggleEditMode() {
|
||||
isEditing = !isEditing;
|
||||
const btn = document.getElementById('editBtn');
|
||||
const formatBar = document.getElementById('formatBar');
|
||||
const frame = document.getElementById('previewFrame');
|
||||
|
||||
btn.classList.toggle('active', isEditing);
|
||||
formatBar.classList.toggle('active', isEditing);
|
||||
|
||||
if (frame.contentDocument) {
|
||||
frame.contentDocument.designMode = isEditing ? 'on' : 'off';
|
||||
}
|
||||
}
|
||||
|
||||
function formatText(command) {
|
||||
const frame = document.getElementById('previewFrame');
|
||||
if (frame.contentDocument) {
|
||||
frame.contentDocument.execCommand(command, false, null);
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 줌 =====
|
||||
function setZoom(value) {
|
||||
currentZoom = parseInt(value);
|
||||
@@ -2094,6 +2242,6 @@ async function submitFeedback() {
|
||||
<textarea class="ai-edit-input" id="aiEditInput" rows="3" placeholder="예: 한 줄로 요약해줘 예: 표 형태로 만들어줘"></textarea>
|
||||
<button class="ai-edit-btn" onclick="submitAiEdit()">✨ 수정하기</button>
|
||||
</div>
|
||||
|
||||
<script src="/static/js/editor.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user