oimp
Version:
A CLI tool for generating OI problem and packages
226 lines (211 loc) • 9.51 kB
JavaScript
// 合并唯一 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);
}
});
});