UNPKG

oimp

Version:

A CLI tool for generating OI problem and packages

226 lines (211 loc) 9.51 kB
// 合并唯一 loadFile 逻辑,增加调试日志和错误处理 async function loadFile(path, skipTreeSelect) { // console.log('loadFile', path); if (isDirty || isMdDirty) { // 将原来的 confirm 替换为自定义模态对话框 const saveResult = await window.showSaveConfirmModal(); if (saveResult === 'save') { await saveFile(); } else if (saveResult === 'cancel') { // 用户取消操作,保持当前文件不变 // 恢复文件树的选中状态为当前文件 if (currentFile && !skipTreeSelect) { // 使用 isRestoringTreeSelection 标志防止触发新的 loadFile 调用 isRestoringTreeSelection = true; const tree = $('#file-tree').jstree(true); tree.deselect_all(); // 查找当前文件对应的节点并选中 const nodes = tree.get_json('#', { flat: true }); const currentNode = nodes.find(node => node.data && node.data.path === currentFile); if (currentNode) { tree.select_node(currentNode.id, false, false); } // 延迟重置标志,确保操作完成 setTimeout(() => { isRestoringTreeSelection = false; }, 0); } return; } // 如果是 'nosave',则继续执行加载新文件 } try { const res = await fetch('/api/file?path=' + encodeURIComponent(path)); if (!res.ok) throw new Error('文件加载失败: ' + res.status); const text = await res.text(); // 先设置当前文件,这样addCppToolbar()就能获取到正确的文件名 currentFile = path; const ext = path.split('.').pop(); let lang = 'plaintext'; let mdContent = ''; if (ext === 'md') { switchToMarkdownEditor(text); removeCppToolbar(); // 移除C++工具栏 mdContent = window.mdTextarea.value; } else { const isCpp = ext === 'cpp' || ext === 'cc' || ext === 'cxx'; switchToMonacoEditor(text, isCpp ? 'cpp' : ext === 'js' ? 'javascript' : ext === 'json' ? 'json' : (ext === 'yaml' || ext === 'yml') ? 'yaml' : 'plaintext'); // 如果是C++文件,添加工具栏 if (isCpp) { addCppToolbar(); if (problemFile === '' || problemFileContent === "") { const tree = $('#file-tree').jstree(true); tree.deselect_all(); // 查找problem文件 const nodes = tree.get_json('#', { flat: true }); //console.log('nodes', nodes); const node = nodes.find(n => { return n.data && n.data.type === 'file' && n.text.toLowerCase().startsWith('problem') && n.text.toLowerCase().endsWith('.md') }); problemFile = node.data.path; const res = await fetch('/api/file?path=' + encodeURIComponent(problemFile)); if (!res.ok) throw new Error('文件加载失败: ' + res.status); const text = await res.text(); //console.log('text', text); problemFileContent = text } mdContent = problemFileContent; } else { removeCppToolbar(); // 移除C++工具栏 } } window.updatePreviewFromMdTextarea(mdContent); lastSavedContent = text; isDirty = false; isMdDirty = false; updateCurrentFileDisplay(); // 预览 } catch (err) { showSaveMsg('加载文件失败: ' + err.message, true); console.error('加载文件失败', err); } } window.loadFile = loadFile; // 新建文件/文件夹按钮逻辑 document.getElementById('btn-new-file').onclick = async function () { const tree = $('#file-tree').jstree(true); const sel = tree.get_selected()[0]; let parentPath = ''; if (sel) { const node = tree.get_node(sel); parentPath = node.data && node.data.type === 'dir' ? node.data.path : (node.parent === '#' ? '' : tree.get_node(node.parent).data.path); } const filename = prompt('请输入新文件名(如 newfile.md):'); if (!filename) return; const relPath = parentPath ? parentPath + '/' + filename : filename; // 创建空文件 const res = await fetch('/api/file', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: relPath, content: '' }) }); if (res.ok) { showSaveMsg('新建文件成功'); } else { showSaveMsg('新建文件失败', true); } }; document.getElementById('btn-new-folder').onclick = async function () { const tree = $('#file-tree').jstree(true); const sel = tree.get_selected()[0]; let parentPath = ''; if (sel) { const node = tree.get_node(sel); parentPath = node.data && node.data.type === 'dir' ? node.data.path : (node.parent === '#' ? '' : tree.get_node(node.parent).data.path); } const foldername = prompt('请输入新文件夹名(如 newfolder):'); if (!foldername) return; const relPath = parentPath ? parentPath + '/' + foldername : foldername; // 创建空文件夹(通过新建一个 .keep 文件实现) const res = await fetch('/api/file', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: relPath + '/.keep', content: '' }) }); if (res.ok) { showSaveMsg('新建文件夹成功'); } else { showSaveMsg('新建文件夹失败', true); } }; document.addEventListener('DOMContentLoaded', async function () { // 文件树懒加载 $('#file-tree').jstree({ 'core': { 'themes': { 'name': 'default-dark', 'dots': true, 'icons': true }, 'data': function (obj, cb) { if (obj.id === '#') { fetch('/api/tree').then(r => r.json()).then(data => cb(data)); } else { // 用真实目录名(data.path)作为 path 参数 const path = obj.data && typeof obj.data.path === 'string' ? obj.data.path : obj.id; fetch('/api/tree?path=' + encodeURIComponent(path)).then(r => r.json()).then(data => cb(data)); } }, 'check_callback': true }, 'plugins': ['wholerow'] }); // 自动展开题目ID目录并自动打开第一个md文件 $('#file-tree').on('ready.jstree', function (e, data) { const tree = $('#file-tree').jstree(true); const root = tree.get_node('#').children[0]; tree.open_node(root, function () { // 查找第一个md文件 const allNodes = tree.get_json(root, { flat: true }); const firstMd = allNodes.find(n => n.data && n.data.type === 'file' && n.text.toLowerCase().endsWith('.md')); if (firstMd) { setTimeout(function () { tree.deselect_all(); tree.select_node(firstMd.id); // 强制加载 loadFile(firstMd.data.path); }, 100); } }); }); // jstree节点渲染时,灰色不可编辑文件 $('#file-tree').on('after_open.jstree refresh.jstree', function (e, data) { const tree = $('#file-tree').jstree(true); tree.get_json(data.node || '#', { flat: true }).forEach(function (n) { if (n.data && n.data.type === 'file') { const ext = n.text.split('.').pop().toLowerCase(); // id 已为安全 id const anchorId = '#' + n.id + '_anchor'; if (['md', 'cpp', 'cc', 'in', 'out', 'ans', 'json', 'yaml'].indexOf(ext) === -1) { $(anchorId).css({ 'color': '#1f1f1f', 'pointer-events': 'none', 'cursor': 'not-allowed' }); } else { $(anchorId).css({ 'color': '', 'pointer-events': '', 'cursor': '' }); } } }); }); // 只在 editor 初始化后绑定 select_node 事件 $('#file-tree').off('select_node.jstree'); $('#file-tree').on('select_node.jstree', function (e, data) { if (isRestoringTreeSelection) { // console.log('跳过 select_node 事件(正在恢复选中)'); return; } if (data.node && data.node.data && data.node.data.type === 'file') { const path = data.node.data.path; const ext = path.split('.').pop().toLowerCase(); // 只允许特定后缀可编辑 if (["md", "in", "ans", "out", "txt", "cpp", "cc", "cxx", "json", "yaml", "yml", "js"].indexOf(ext) !== -1) { loadFile(path); } else { // 取消选中 setTimeout(() => $('#file-tree').jstree('deselect_node', data.node), 0); } } else if (data.node && data.node.data && data.node.data.type === 'dir') { $('#file-tree').jstree('toggle_node', data.node); } }); });