window.addEventListener('load', ()=>{ console.log(window.material); console.log(window.lesson); editLesson(window.material.material_id, window.lesson.chapter_name, window.lesson.lesson_name); }); function parseHeadings(mdText) { const lines = mdText.split('\n'); const tokens = []; for (let i=0;i tokens[i].level) j++; // 找到第一个 >= 当前level 的 heading 或到文末 let k = i+1; while (k tokens[i].level) k++; const nextSameOrHigher = k x.level===level && x.text===headingText); if (!t) return ''; return parsed.lines.slice(t.start, t.end+1).join('\n'); } // 根据三个 md,构造统一大纲树(一级唯一“主题”,其下多个阶段##,其下多个步骤###) function buildUnifiedOutline(parsedLesson, parsedPrompt, parsedScore) { // 取 lesson 的结构为主(也可做三者交集/并集策略,这里简单以 lesson 为基准) const root1 = parsedLesson.tokens.find(t => t.level===1); const phases = parsedLesson.tokens.filter(t => t.level===2); const stepsByPhase = {}; phases.forEach(ph => { stepsByPhase[ph.text] = parsedLesson.tokens.filter(t => t.level===3 && t.start>ph.start && t.end<=ph.end) .map(s => s.text); }); return { theme: root1 ? root1.text : '主题', phases, stepsByPhase }; } let CURRENT = { material_id: null, chapter_name: null, lesson_name: null, links: { lesson:'', prompt:'', score:'' }, raw: { lesson:'', prompt:'', score:'' }, parsed:{ lesson:null, prompt:null, score:null }, outline:null }; // 入口:从你的列表调用 async function editLesson(material_id, chapter_name, lesson_name) { CURRENT.material_id = material_id; CURRENT.chapter_name = chapter_name; CURRENT.lesson_name = lesson_name; // 打开右侧课程详情先占位 const details = document.getElementById('courseDetails'); const detailsContent = document.getElementById('details-content'); detailsContent.innerHTML = '

...

'; details.classList.add('open'); // 拉课时详情 metaRes = window.lesson; const meta = metaRes; document.getElementById('course-title').textContent = meta.name || meta.material_name || lesson_name; // 右侧面板描述/目录(简化) detailsContent.innerHTML = `

描述

${meta.description || meta.material_description || '暂无描述'}

`; const chapterList = document.getElementById('chapter-list'); chapterList.innerHTML = ''; (meta.chapters||[]).forEach(ch=>{ const li = document.createElement('li'); li.textContent = ch.chapter_name; chapterList.appendChild(li); }); // 保存三份链接 CURRENT.links.lesson = meta.markdown_lesson_file_link; CURRENT.links.prompt = meta.markdown_prompt_file_name; CURRENT.links.score = meta.markdown_score_prompt_file_link; console.log(CURRENT.links); // 拉三份 Markdown const [t1, t2, t3] = await Promise.all([ fetch(CURRENT.links.lesson).then(r=>r.text()), fetch(CURRENT.links.prompt).then(r=>r.text()), fetch(CURRENT.links.score ).then(r=>r.text()) ]); CURRENT.raw.lesson = t1; CURRENT.raw.prompt = t2; CURRENT.raw.score = t3; CURRENT.parsed.lesson = parseHeadings(t1); CURRENT.parsed.prompt = parseHeadings(t2); CURRENT.parsed.score = parseHeadings(t3); // 构建大纲并渲染 CURRENT.outline = buildUnifiedOutline(CURRENT.parsed.lesson, CURRENT.parsed.prompt, CURRENT.parsed.score); renderOutline(CURRENT.outline); // 默认选中第一个“步骤”(若无步骤则整个文档) const firstPhase = CURRENT.outline.phases?.[0]?.text; const firstStep = firstPhase && CURRENT.outline.stepsByPhase[firstPhase]?.[0]; if (firstPhase && firstStep) { loadStepToEditors(firstPhase, firstStep); highlightActive(firstPhase, firstStep); } else { // 无###时,直接把全文放到 Lesson 标签 document.getElementById('editor-lesson').value = CURRENT.raw.lesson; document.getElementById('editor-prompt').value = CURRENT.raw.prompt; document.getElementById('editor-score').value = CURRENT.raw.score; } } // 渲染左侧大纲 function renderOutline(outline) { const tree = document.getElementById('outline-tree'); tree.innerHTML = ''; if (outline.theme) { const li = document.createElement('li'); li.className='lvl1'; li.textContent = `# ${outline.theme}`; tree.appendChild(li); } outline.phases.forEach(ph=>{ const li2 = document.createElement('li'); li2.className='lvl2'; li2.textContent = `## ${ph.text}`; tree.appendChild(li2); const steps = outline.stepsByPhase[ph.text] || []; steps.forEach(st=>{ const li3 = document.createElement('li'); li3.className='lvl3'; li3.textContent = `### ${st}`; li3.dataset.phase = ph.text; li3.dataset.step = st; li3.onclick = () => { loadStepToEditors(ph.text, st); highlightActive(ph.text, st); }; tree.appendChild(li3); }); }); } function highlightActive(phase, step) { document.querySelectorAll('#outline-tree li').forEach(li=>li.classList.remove('active')); const target = Array.from(document.querySelectorAll('#outline-tree li.lvl3')) .find(li => li.dataset.phase===phase && li.dataset.step===step); if (target) target.classList.add('active'); } // 把“某个步骤”对应的三个片段载入到三个编辑器 function loadStepToEditors(phaseText, stepText) { // 三份文档里都取对应 phase/step 的片段(不存在则回退) const L = CURRENT.parsed.lesson, P = CURRENT.parsed.prompt, S = CURRENT.parsed.score; const lessonPhase = sliceSection(L, phaseText, 2) || CURRENT.raw.lesson; const promptPhase = sliceSection(P, phaseText, 2) || CURRENT.raw.prompt; const scorePhase = sliceSection(S, phaseText, 2) || CURRENT.raw.score; // 在各 phase 片段里再找 ### step const subL = parseHeadings(lessonPhase); const subP = parseHeadings(promptPhase); const subS = parseHeadings(scorePhase); const lText = sliceSection(subL, stepText, 3) || lessonPhase; const pText = sliceSection(subP, stepText, 3) || promptPhase; const sText = sliceSection(subS, stepText, 3) || scorePhase; document.getElementById('editor-lesson').value = lText; document.getElementById('editor-prompt').value = pText; document.getElementById('editor-score').value = sText; } // Tab 切换 document.addEventListener('click', (e)=>{ if (e.target.classList.contains('tab-btn')) { document.querySelectorAll('.tab-btn').forEach(b=>b.classList.remove('active')); e.target.classList.add('active'); const id = e.target.dataset.target; document.querySelectorAll('.tab').forEach(t=>t.classList.remove('active')); document.getElementById(id).classList.add('active'); } }); // 保存全部(把当前三个编辑器的文本整体 PUT 回各自文件) document.getElementById('save-all-btn')?.addEventListener('click', async ()=>{ const l = document.getElementById('editor-lesson').value; const p = document.getElementById('editor-prompt').value; const s = document.getElementById('editor-score').value; try { const [r1, r2, r3] = await Promise.all([ fetch(CURRENT.links.lesson, {method:'PUT', body:l}), fetch(CURRENT.links.prompt, {method:'PUT', body:p}), fetch(CURRENT.links.score , {method:'PUT', body:s}), ]); if (r1.ok && r2.ok && r3.ok) alert('已保存'); else alert('保存失败,请重试'); } catch (err) { console.error(err); alert('保存出错'); } }); // 右侧详情开/关 function closeCourseDetails(){ document.getElementById('courseDetails').classList.remove('open'); } function showInputForNewChapter(){ /* 省略:你的新增逻辑 */ }