You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

521 lines
16 KiB

// 全局变量
let socket = null;
let currentModalType = '';
let selectedItem = null;
let currentPath = '';
let searchResults = [];
// 初始化Socket连接
document.addEventListener('DOMContentLoaded', function() {
socket = io();
// 监听发送输出
socket.on('send_output', function(data) {
const output = document.getElementById('send-output');
output.textContent += data.message;
output.scrollTop = output.scrollHeight;
});
// 监听发送状态
socket.on('send_status', function(data) {
document.getElementById('send-status').textContent = data.status;
});
// 监听接收输出
socket.on('receive_output', function(data) {
const output = document.getElementById('receive-output');
output.textContent += data.message;
output.scrollTop = output.scrollHeight;
});
// 监听接收状态
socket.on('receive_status', function(data) {
document.getElementById('receive-status').textContent = data.status;
});
// 监听文件路径输入变化
const filePathInput = document.getElementById('send-file-path');
filePathInput.addEventListener('input', updateFileInfo);
filePathInput.addEventListener('change', updateFileInfo);
// 初始更新文件信息
updateFileInfo();
});
// 更新文件信息
function updateFileInfo() {
const filePath = document.getElementById('send-file-path').value.trim();
const fileSizeElement = document.getElementById('file-size');
const fileMtimeElement = document.getElementById('file-mtime');
const fileStatusElement = document.getElementById('file-status');
if (!filePath) {
fileSizeElement.textContent = '-';
fileMtimeElement.textContent = '-';
fileStatusElement.textContent = '未选择';
fileStatusElement.className = 'file-status-unknown';
return;
}
// 检查文件是否存在
fetch('/check/file', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({ file_path: filePath })
})
.then(response => {
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
return response.text().then(text => {
throw new Error('服务器返回了非JSON响应');
});
}
return response.json();
})
.then(data => {
if (data.status === 'success') {
fileSizeElement.textContent = formatFileSize(data.size);
fileMtimeElement.textContent = new Date(data.mtime * 1000).toLocaleString();
fileStatusElement.textContent = '文件有效';
fileStatusElement.className = 'file-status-valid';
} else {
fileSizeElement.textContent = '-';
fileMtimeElement.textContent = '-';
fileStatusElement.textContent = '文件不存在或无法访问';
fileStatusElement.className = 'file-status-invalid';
}
})
.catch(error => {
console.error('检查文件错误:', error);
fileSizeElement.textContent = '-';
fileMtimeElement.textContent = '-';
fileStatusElement.textContent = '检查失败';
fileStatusElement.className = 'file-status-invalid';
});
}
// 手动浏览文件
function browseFileManually() {
currentModalType = 'send';
currentPath = document.getElementById('send-file-path').value || '/';
const filePath = document.getElementById('send-file-path').value;
if (filePath) {
try {
const dirPath = require('path').dirname(filePath);
if (require('fs').existsSync(dirPath)) {
currentPath = dirPath;
}
} catch (e) {
// 忽略错误,使用默认路径
}
}
fetchFileList(currentPath, 'file');
document.getElementById('file-browser-modal').style.display = 'block';
}
// 显示指定界面
function showScreen(screenId) {
document.querySelectorAll('.screen').forEach(screen => {
screen.classList.remove('active');
});
document.getElementById(screenId).classList.add('active');
}
// 发送连接
function sendConnect() {
const key = document.getElementById('send-key').value;
fetch('/send/connect', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ key: key })
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
document.getElementById('send-connect-btn').disabled = true;
document.getElementById('send-stop-btn').disabled = false;
// 3秒后启用发送文件按钮
setTimeout(() => {
document.getElementById('send-file-btn').disabled = false;
}, 3000);
} else {
alert('错误: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('连接失败: ' + error);
});
}
// 发送文件
function sendFile() {
const filePath = document.getElementById('send-file-path').value;
const key = document.getElementById('send-key').value;
const parts = key.split('|');
const psk = parts.length >= 3 ? parts[2].trim() : '';
fetch('/send/file', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
file_path: filePath,
psk: psk
})
})
.then(response => response.json())
.then(data => {
if (data.status !== 'success') {
alert('错误: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('发送失败: ' + error);
});
}
// 停止发送
function sendStop() {
fetch('/send/stop', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
document.getElementById('send-connect-btn').disabled = false;
document.getElementById('send-file-btn').disabled = true;
document.getElementById('send-stop-btn').disabled = true;
}
})
.catch(error => {
console.error('Error:', error);
});
}
// 接收连接
function receiveConnect() {
const key = document.getElementById('receive-key').value;
const savePath = document.getElementById('receive-save-path').value;
fetch('/receive/connect', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
key: key,
save_path: savePath
})
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
document.getElementById('receive-connect-btn').disabled = true;
document.getElementById('receive-stop-btn').disabled = false;
} else {
alert('错误: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('连接失败: ' + error);
});
}
// 停止接收
function receiveStop() {
fetch('/receive/stop', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
document.getElementById('receive-connect-btn').disabled = false;
document.getElementById('receive-stop-btn').disabled = true;
}
})
.catch(error => {
console.error('Error:', error);
});
}
// 打开搜索模态框
function openSearchModal() {
document.getElementById('file-search-modal').style.display = 'block';
document.getElementById('search-path').value = '/';
document.getElementById('search-keyword').value = '*';
document.getElementById('search-results-list').innerHTML = '';
document.getElementById('result-count').textContent = '0';
document.getElementById('search-select-btn').disabled = true;
selectedItem = null;
searchResults = [];
}
// 关闭搜索模态框
function closeSearchModal() {
document.getElementById('file-search-modal').style.display = 'none';
}
// 浏览搜索路径
function browseSearchPath() {
currentModalType = 'search_path';
currentPath = document.getElementById('search-path').value || '/';
fetchFileList(currentPath, 'folder');
document.getElementById('file-browser-modal').style.display = 'block';
}
// 开始搜索
function startSearch() {
const searchPath = document.getElementById('search-path').value;
const keyword = document.getElementById('search-keyword').value;
const maxResults = document.getElementById('max-results').value;
if (!keyword) {
alert('请输入搜索关键词');
return;
}
// 显示加载状态
const resultsList = document.getElementById('search-results-list');
resultsList.innerHTML = '<div class="loading">搜索中...</div>';
fetch('/search/files', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
path: searchPath,
pattern: keyword,
max_results: parseInt(maxResults)
})
})
.then(response => {
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
return response.text().then(text => {
throw new Error(`服务器返回了非JSON响应: ${text.substring(0, 100)}...`);
});
}
return response.json();
})
.then(data => {
if (data.status === 'success') {
searchResults = data.results;
displaySearchResults(data.results);
document.getElementById('result-count').textContent = data.count;
} else {
alert('搜索失败: ' + data.message);
resultsList.innerHTML = '<div class="no-results">搜索失败</div>';
}
})
.catch(error => {
console.error('搜索错误:', error);
alert('搜索失败: ' + error.message);
resultsList.innerHTML = '<div class="no-results">搜索错误</div>';
});
}
// 显示搜索结果
function displaySearchResults(results) {
const resultsList = document.getElementById('search-results-list');
resultsList.innerHTML = '';
if (results.length === 0) {
resultsList.innerHTML = '<div class="no-results">未找到匹配的文件</div>';
return;
}
results.forEach(result => {
const div = document.createElement('div');
div.className = 'file-item';
const fileSize = formatFileSize(result.size || 0);
div.innerHTML = `
<div class="file-name">📄 ${result.name}</div>
<div class="file-details">
<span class="file-path">${result.relative_path}</span>
<span class="file-size">${fileSize}</span>
</div>
`;
div.dataset.path = result.path;
div.addEventListener('click', function() {
// 取消之前的选择
document.querySelectorAll('.file-item.selected').forEach(el => {
el.classList.remove('selected');
});
// 选择当前项
this.classList.add('selected');
selectedItem = this;
document.getElementById('search-select-btn').disabled = false;
});
resultsList.appendChild(div);
});
}
// 格式化文件大小
function formatFileSize(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// 选择搜索结果
function selectSearchResult() {
if (!selectedItem) {
alert('请先选择一个文件');
return;
}
const filePath = selectedItem.dataset.path;
document.getElementById('send-file-path').value = filePath;
closeSearchModal();
updateFileInfo(); // 更新文件信息显示
}
// 获取文件列表
function fetchFileList(path, type) {
fetch('/browse', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
path: path,
type: type
})
})
.then(response => {
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
return response.text().then(text => {
throw new Error(`服务器返回了非JSON响应: ${text.substring(0, 100)}...`);
});
}
return response.json();
})
.then(data => {
if (data.status === 'success') {
currentPath = data.current_path;
document.getElementById('current-path').textContent = currentPath;
const fileList = document.getElementById('file-list');
fileList.innerHTML = '';
data.items.forEach(item => {
const div = document.createElement('div');
div.className = 'file-item';
div.innerHTML = item.type === 'folder' ?
`📁 ${item.name}` : `📄 ${item.name}`;
div.dataset.path = item.path;
div.dataset.type = item.type;
div.addEventListener('click', function() {
// 取消之前的选择
document.querySelectorAll('.file-item.selected').forEach(el => {
el.classList.remove('selected');
});
// 选择当前项
this.classList.add('selected');
selectedItem = this;
// 如果是文件夹,双击进入
this.addEventListener('dblclick', function() {
if (item.type === 'folder') {
if (currentModalType === 'search_path') {
document.getElementById('search-path').value = item.path;
closeModal();
} else {
fetchFileList(item.path, type);
}
}
});
});
fileList.appendChild(div);
});
} else {
alert('错误: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('获取文件列表失败: ' + error.message);
});
}
// 导航到上级目录
function navigateUp() {
const parentPath = currentPath.split('/').slice(0, -1).join('/');
if (parentPath === '') {
currentPath = '/';
} else {
currentPath = parentPath;
}
const type = currentModalType === 'send' ? 'file' : 'folder';
fetchFileList(currentPath, type);
}
// 关闭模态框
function closeModal() {
document.getElementById('file-browser-modal').style.display = 'none';
selectedItem = null;
}
// 选择文件或文件夹
function selectItem() {
if (!selectedItem) {
alert('请先选择一个项目');
return;
}
const path = selectedItem.dataset.path;
const type = selectedItem.dataset.type;
if (currentModalType === 'send') {
if (type === 'file') {
document.getElementById('send-file-path').value = path;
closeModal();
updateFileInfo(); // 更新文件信息显示
} else {
alert('请选择一个文件');
}
} else if (currentModalType === 'folder') {
document.getElementById('receive-save-path').value = path;
closeModal();
} else if (currentModalType === 'search_path') {
document.getElementById('search-path').value = path;
closeModal();
}
}