Files
hsa/code-server-like/static/js/file.js
2025-09-17 07:46:38 +00:00

328 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

let fileTree = [];
let editor = null;
let showdotfile = false;
// 隐藏右键菜单
function hideContextMenu() {
const contextMenu = document.getElementById("contextMenu");
contextMenu.style.display = "none";
}
// 加载文件树
function loadFileTree() {
fetch('/vsc-like/file-tree')
.then(response => response.json())
.then(data => {
fileTree = sortFileTree(data);
const fileTreeDiv = document.getElementById('fileTreeContent');
fileTreeDiv.innerHTML = '';
renderFileTree(fileTree, fileTreeDiv);
for (const item of fileTree) {
if (item.type === 'directory') {
// 如果选中的文件夹是当前文件夹,则显示或选中的文件处于当前文件夹(祖先)则显示
if (selectedItem && (selectedItem.path+'/'+selectedItem.name).includes(item.path+'/'+item.name)) {
}
else{
showOrHideChildren(item.div);
showOrHideChildren(item.div);
}
}
if (showdotfile==false) {
if (item.name.startsWith('.')) {
item.div.style.display = 'none';
}
}
}
bindRightClickMenu(fileTree);
});
}
let selectedItem = null; // 用来保存当前选中的文件/文件夹
let copiedItem = null; // 用于存储复制的文件或文件夹
function selectItem(item) {
if (selectedItem) {
selectedItem.div.classList.remove('selected');
}
item.div.classList.add('selected');
selectedItem = item;
}
// 右键点击文件或文件夹时显示菜单
function showContextMenu(event, item) {
event.preventDefault(); // 阻止默认右键菜单
// 记录当前右键点击的文件/文件夹
selectedItem = item;
if (selectedItem) {
selectedItem.div.classList.remove('selected');
}
item.div.classList.add('selected');
// 获取右键菜单元素
const contextMenu = document.getElementById("contextMenu");
// 设置菜单位置
contextMenu.style.top = `${event.clientY}px`;
contextMenu.style.left = `${event.clientX}px`;
// 显示右键菜单
contextMenu.style.display = "block";
}
// 绑定右键点击事件到文件/文件夹
function bindRightClickMenu(treeData) {
treeData.forEach(item => {
item.div.oncontextmenu = (event) => {
event.preventDefault()
event.stopPropagation();
selectItem(item);
showContextMenu(event, item);
};
if (item.children) {
bindRightClickMenu(item.children);
}
});
}
function checkValidName(name) {
// 检查是否为空或仅包含空格
if (!name.trim()) {
return false;
}
// 检查是否包含非法字符
const invalidChars = /[\/\\*?<>|":]/;
if (invalidChars.test(name)) {
return false;
}
// 检查文件名是否过长(例如,限制为 255 字符)
if (name.length > 255) {
return false;
}
// 检查是否使用保留的文件名Windows 系统常见保留名称)
const reservedNames = ['CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9'];
if (reservedNames.includes(name.toUpperCase())) {
return false;
}
// 不允许以空格结尾
if (name.endsWith(' ')) {
return false;
}
return true;
}
// 右键菜单项点击事件
document.getElementById("renameOption").onclick = () => {
if (selectedItem && selectedItem.type !== 'directory') {
const newName = prompt("Enter new name for the file:", selectedItem.name);
if (newName && checkValidName(newName)) {
fetch(`/vsc-like/rename-file`, { method: 'POST', body: JSON.stringify({ path: selectedItem.path+'/'+selectedItem.name, newName: newName }) , headers: { 'Content-Type': 'application/json' } })
.then(() => {
selectedItem.name = newName;
selectedItem.div.textContent = newName;
loadFileTree();
});
}
}
if (selectedItem && selectedItem.type === 'directory') {
const newName = prompt("Enter new name for the folder:", selectedItem.name);
if (newName && checkValidName(newName)) {
fetch(`/vsc-like/rename-folder`, { method: 'POST', body: JSON.stringify({ path: selectedItem.path+'/'+selectedItem.name, newName: newName }) , headers: { 'Content-Type': 'application/json' } })
.then(() => {
selectedItem.name = newName;
selectedItem.div.textContent = newName;
loadFileTree();
});
}
}
hideContextMenu();
};
document.getElementById("copyOption").onclick = () => {
if (selectedItem) {
copiedItem = selectedItem;
console.log("Item copied:", copiedItem);
}
hideContextMenu();
};
document.getElementById("pasteOption").onclick = (event) => {
event.stopPropagation();
if (copiedItem) {
if (selectedItem.type === 'directory') {
newPath = selectedItem.path+'/'+selectedItem.name+'/';
}
else{
newPath = selectedItem.path;
}
fetch(`/vsc-like/copy-files`, { method: 'POST', body: JSON.stringify({ oldPath: copiedItem.path+'/'+copiedItem.name, newPath: newPath }) , headers: { 'Content-Type': 'application/json' } })
.then(() => loadFileTree());
console.log("Item pasted:", selectedItem);
}
hideContextMenu();
};
document.getElementById("deleteOption").onclick = (event) => {
event.stopPropagation();
//二次确认
if (confirm("Are you sure you want to delete selected file/folder?")) {
if (selectedItem) {
fetch(`/vsc-like/delete-file`, { method: 'POST', body: JSON.stringify({ path: selectedItem.path+'/'+selectedItem.name }) , headers: { 'Content-Type': 'application/json' } })
.then(() => loadFileTree());
}
}
hideContextMenu();
};
// 隐藏右键菜单的点击事件
document.addEventListener("click", (event) => {
const contextMenu = document.getElementById("contextMenu");
if (!contextMenu.contains(event.target)) {
hideContextMenu();
}
});
function renderFileTree(treeData, container, level = 0, parent = null) {
treeData.forEach(item => {
const div = document.createElement('div');
div.textContent = item.name;
if (parent) {
item.path = parent + '/';
} else {
item.path = '.'
}
// 根据类型添加不同的类
if (item.type === 'directory') {
div.classList.add('tree-item', 'directory', `tree-item-level-${level}`); // 文件夹样式,添加层级类
const iconDiv = document.createElement('i');
div.prepend(iconDiv); // 为文件夹添加图标
const childrenDiv = document.createElement('div');
renderFileTree(item.children, childrenDiv, level + 1, item.path + '/' + item.name); // 递归调用层级加1
div.appendChild(childrenDiv);
item.div = div;
} else {
div.classList.add('tree-item', 'file', `tree-item-level-${level}`); // 文件样式,添加层级类
const iconDiv = document.createElement('i');
div.prepend(iconDiv); // 为文件添加图标
item.div = div;
}
div.onclick = (event) => {
event.stopPropagation();
if (selectedItem) {
selectedItem.div.classList.remove('selected'); // 取消之前选中的文件夹/文件的选中状态
}
div.classList.add('selected'); // 为当前点击的文件夹/文件添加选中样式
selectedItem = item; // 更新选中项
hideContextMenu();
// 获取选中的文件或文件夹的详细信息
console.log("Selected Item:", item);
if (item.type === 'directory') {
showOrHideChildren(div);
} else {
loadFileContent(item);
}
};
container.appendChild(div);
});
}
function selectEmpty(div) {
if (selectedItem) {
selectedItem.div.classList.remove('selected');
}
selectedItem = {type: 'directory', path: '.', name: '.', div:div};
}
function showOrHideChildren(d) {
const childrenDiv = d.querySelector('div');
if (childrenDiv) {
// 切换显示和隐藏
childrenDiv.style.display = (childrenDiv.style.display === 'none' || childrenDiv.style.display === '') ? 'block' : 'none';
}
}
// 加载文件内容
function loadFileContent(item) {
fetch(`/vsc-like/file-content`, { method: 'POST', body: JSON.stringify({ path: item.path+'/'+item.name }) , headers: { 'Content-Type': 'application/json' } })
.then(response => response.json())
.then(data => {
if (data.content) {
editor.setValue(data.content);
}
});
}
// 新建文件
function createNewFile(event) {
event.stopPropagation();
const fileName = prompt('Enter new file name:');
if (fileName) {
if (selectedItem) {
if (selectedItem.type === 'directory') {
fetch(`/vsc-like/create-file`, { method: 'POST', body: JSON.stringify({ path: fileName, parent: selectedItem.path+'/'+selectedItem.name }) , headers: { 'Content-Type': 'application/json' } })
.then(() => loadFileTree());
} else {
console.log("selectedItem.path",selectedItem.path);
fetch(`/vsc-like/create-file`, { method: 'POST', body: JSON.stringify({ path: fileName, parent: selectedItem.path }) , headers: { 'Content-Type': 'application/json' } })
.then(() => loadFileTree());
}
}
}
}
// 新建文件夹
function createNewFolder(event) {
event.stopPropagation();
const folderName = prompt('Enter new folder name:');
if (folderName) {
if (selectedItem) {
if (selectedItem.type === 'directory') {
fetch(`/vsc-like/create-folder`, { method: 'POST', body: JSON.stringify({ path: fileName, parent: selectedItem.path+'/'+selectedItem.name }) , headers: { 'Content-Type': 'application/json' } })
.then(() => loadFileTree());
} else {
fetch(`/vsc-like/create-folder`, { method: 'POST', body: JSON.stringify({ path: fileName, parent: selectedItem.path }) , headers: { 'Content-Type': 'application/json' } })
.then(() => loadFileTree());
}
}
}
}
// 保存文件
function saveFile(event) {
event.stopPropagation();
const content = editor.getValue();
console.log(selectItem)
if (selectedItem.path==undefined){
selectedItem.path='.';
}
fetch(`/vsc-like/save-file`, { method: 'POST', body: JSON.stringify({ path: selectedItem.path+'/'+selectedItem.name, content: content }) , headers: { 'Content-Type': 'application/json' } })
.then(() => loadFileTree());
}
function sortFileTree(treeData) {
// 排序函数
return treeData.sort((a, b) => {
// 首先按照 type 排序: 'directory' 优先
if (a.type === b.type) {
// 如果 type 相同,则按 name 排序
return a.name.localeCompare(b.name);
}
// 'directory' 在前,'file' 在后
return a.type === 'directory' ? -1 : 1;
}).map(item => {
// 对每个子元素children也进行排序
if (item.children && item.children.length > 0) {
item.children = sortFileTree(item.children); // 递归排序子树
}
return item;
});
}