593 lines
21 KiB
JavaScript
593 lines
21 KiB
JavaScript
/**
|
||
* Git文件上传工具 - 主要JavaScript功能
|
||
* 支持文件上传、Git操作、文件管理等功能
|
||
*/
|
||
|
||
class GitUploader {
|
||
constructor() {
|
||
this.selectedFiles = new Set();
|
||
this.init();
|
||
}
|
||
|
||
// 初始化
|
||
init() {
|
||
this.setupEventListeners();
|
||
this.checkGitStatus();
|
||
this.loadFileList();
|
||
this.showRemotes(); // 加载远程仓库列表
|
||
this.loadBranches(); // 加载分支列表
|
||
}
|
||
|
||
// 设置事件监听器
|
||
setupEventListeners() {
|
||
// 文件上传相关
|
||
const uploadArea = document.getElementById('uploadArea');
|
||
const fileInput = document.getElementById('fileInput');
|
||
|
||
uploadArea.addEventListener('click', () => fileInput.click());
|
||
uploadArea.addEventListener('dragover', this.handleDragOver.bind(this));
|
||
uploadArea.addEventListener('dragleave', this.handleDragLeave.bind(this));
|
||
uploadArea.addEventListener('drop', this.handleDrop.bind(this));
|
||
fileInput.addEventListener('change', this.handleFileSelect.bind(this));
|
||
|
||
// Git操作相关
|
||
document.getElementById('gitAdd').addEventListener('click', () => this.gitOperation('add'));
|
||
document.getElementById('gitCommit').addEventListener('click', () => this.gitOperation('commit'));
|
||
document.getElementById('gitPush').addEventListener('click', () => this.gitOperation('push'));
|
||
document.getElementById('gitStatusBtn').addEventListener('click', () => this.gitOperation('status'));
|
||
document.getElementById('gitLog').addEventListener('click', () => this.gitOperation('log'));
|
||
|
||
// 远程仓库相关
|
||
document.getElementById('addRemote').addEventListener('click', () => this.addRemote());
|
||
document.getElementById('showRemotes').addEventListener('click', () => this.showRemotes());
|
||
|
||
// 分支管理相关
|
||
document.getElementById('createBranch').addEventListener('click', () => this.createBranch());
|
||
document.getElementById('switchBranch').addEventListener('click', () => this.switchBranch());
|
||
document.getElementById('deleteBranch').addEventListener('click', () => this.deleteBranch());
|
||
document.getElementById('refreshBranches').addEventListener('click', () => this.loadBranches());
|
||
|
||
// 文件列表相关
|
||
document.getElementById('refreshFiles').addEventListener('click', () => this.loadFileList());
|
||
document.getElementById('selectAllFiles').addEventListener('click', () => this.selectAllFiles());
|
||
document.getElementById('deselectAllFiles').addEventListener('click', () => this.deselectAllFiles());
|
||
}
|
||
|
||
// 拖拽处理
|
||
handleDragOver(e) {
|
||
e.preventDefault();
|
||
document.getElementById('uploadArea').classList.add('dragover');
|
||
}
|
||
|
||
handleDragLeave(e) {
|
||
e.preventDefault();
|
||
document.getElementById('uploadArea').classList.remove('dragover');
|
||
}
|
||
|
||
handleDrop(e) {
|
||
e.preventDefault();
|
||
document.getElementById('uploadArea').classList.remove('dragover');
|
||
|
||
const files = Array.from(e.dataTransfer.files);
|
||
this.uploadFiles(files);
|
||
}
|
||
|
||
handleFileSelect(e) {
|
||
const files = Array.from(e.target.files);
|
||
this.uploadFiles(files);
|
||
}
|
||
|
||
// 文件上传
|
||
async uploadFiles(files) {
|
||
if (files.length === 0) return;
|
||
|
||
const formData = new FormData();
|
||
formData.append('action', 'upload');
|
||
|
||
files.forEach(file => {
|
||
formData.append('files[]', file);
|
||
});
|
||
|
||
try {
|
||
this.showMessage('正在上传文件...', 'info');
|
||
|
||
const response = await fetch('index.php', {
|
||
method: 'POST',
|
||
body: formData
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
this.showMessage(`成功上传 ${result.files.length} 个文件`, 'success');
|
||
this.displayUploadedFiles(result.files);
|
||
this.loadFileList(); // 重新加载文件列表
|
||
} else {
|
||
this.showMessage(result.message || '上传失败', 'error');
|
||
}
|
||
} catch (error) {
|
||
this.showMessage('上传出错: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 显示已上传的文件
|
||
displayUploadedFiles(files) {
|
||
const container = document.getElementById('uploadedFiles');
|
||
container.innerHTML = '';
|
||
|
||
files.forEach(file => {
|
||
const fileItem = document.createElement('div');
|
||
fileItem.className = 'file-item success fade-in';
|
||
fileItem.innerHTML = `
|
||
<span>${file}</span>
|
||
<span style="color: #27ae60;">✓</span>
|
||
`;
|
||
container.appendChild(fileItem);
|
||
});
|
||
}
|
||
|
||
// 检查Git状态
|
||
async checkGitStatus() {
|
||
try {
|
||
const response = await fetch('index.php', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||
body: 'action=check_git'
|
||
});
|
||
|
||
const result = await response.json();
|
||
const indicator = document.getElementById('gitIndicator');
|
||
const statusText = document.getElementById('gitStatusText');
|
||
|
||
if (result.success && result.hasGit) {
|
||
indicator.classList.add('active');
|
||
statusText.textContent = 'Git仓库已初始化';
|
||
} else {
|
||
indicator.classList.remove('active');
|
||
statusText.textContent = 'Git仓库未初始化';
|
||
|
||
// 自动初始化Git仓库
|
||
if (confirm('Git仓库未初始化,是否现在初始化?')) {
|
||
await this.initGitRepo();
|
||
}
|
||
}
|
||
} catch (error) {
|
||
this.showMessage('检查Git状态失败: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 初始化Git仓库
|
||
async initGitRepo() {
|
||
try {
|
||
const response = await fetch('index.php', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||
body: 'action=init_git'
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
this.showMessage('Git仓库初始化成功', 'success');
|
||
this.checkGitStatus(); // 重新检查状态
|
||
} else {
|
||
this.showMessage('Git仓库初始化失败', 'error');
|
||
}
|
||
} catch (error) {
|
||
this.showMessage('初始化Git仓库失败: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// Git操作
|
||
async gitOperation(operation) {
|
||
const commitMessage = document.getElementById('commitMessage').value || '自动提交';
|
||
const selectedFiles = Array.from(this.selectedFiles);
|
||
const remoteName = document.getElementById('remoteName').value || 'origin';
|
||
const branchSelect = document.getElementById('branchSelect');
|
||
const currentBranch = branchSelect ? branchSelect.value : 'main';
|
||
|
||
const params = new URLSearchParams();
|
||
params.append('action', 'git_operation');
|
||
params.append('operation', operation);
|
||
|
||
if (operation === 'commit') {
|
||
params.append('message', commitMessage);
|
||
}
|
||
|
||
if (operation === 'push') {
|
||
params.append('remote', remoteName);
|
||
params.append('branch', currentBranch); // 使用当前选择的分支
|
||
}
|
||
|
||
if (operation === 'add' && selectedFiles.length > 0) {
|
||
selectedFiles.forEach(file => {
|
||
params.append('files[]', file);
|
||
});
|
||
}
|
||
|
||
try {
|
||
this.showMessage(`正在执行 Git ${operation}...`, 'info');
|
||
|
||
const response = await fetch('index.php', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||
body: params
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
this.showMessage(`Git ${operation} 执行成功`, 'success');
|
||
this.displayGitOutput(result.output);
|
||
|
||
if (operation === 'add' || operation === 'commit') {
|
||
this.loadFileList(); // 重新加载文件列表
|
||
}
|
||
} else {
|
||
this.showMessage(`Git ${operation} 执行失败: ${result.message || '未知错误'}`, 'error');
|
||
this.displayGitOutput(result.output || []);
|
||
}
|
||
} catch (error) {
|
||
this.showMessage(`Git ${operation} 执行出错: ${error.message}`, 'error');
|
||
}
|
||
}
|
||
|
||
// 显示Git输出
|
||
displayGitOutput(output) {
|
||
const outputElement = document.getElementById('gitOutput');
|
||
|
||
if (output && output.length > 0) {
|
||
outputElement.textContent = output.join('\n');
|
||
} else {
|
||
outputElement.textContent = '暂无输出';
|
||
}
|
||
|
||
outputElement.scrollTop = outputElement.scrollHeight;
|
||
}
|
||
|
||
// 添加远程仓库
|
||
async addRemote() {
|
||
const name = document.getElementById('remoteName').value.trim();
|
||
const url = document.getElementById('remoteUrl').value.trim();
|
||
|
||
if (!name || !url) {
|
||
this.showMessage('请输入远程仓库名称和地址', 'error');
|
||
return;
|
||
}
|
||
|
||
const params = new URLSearchParams();
|
||
params.append('action', 'add_remote');
|
||
params.append('name', name);
|
||
params.append('url', url);
|
||
|
||
try {
|
||
this.showMessage('正在添加远程仓库...', 'info');
|
||
|
||
const response = await fetch('index.php', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||
body: params
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
this.showMessage('远程仓库添加成功', 'success');
|
||
document.getElementById('remoteUrl').value = ''; // 清空输入框
|
||
this.showRemotes(); // 刷新远程仓库列表
|
||
} else {
|
||
this.showMessage('添加远程仓库失败: ' + (result.message || '未知错误'), 'error');
|
||
}
|
||
} catch (error) {
|
||
this.showMessage('添加远程仓库出错: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 显示远程仓库列表
|
||
async showRemotes() {
|
||
try {
|
||
const response = await fetch('index.php', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||
body: 'action=get_remotes'
|
||
});
|
||
|
||
const result = await response.json();
|
||
const remoteList = document.getElementById('remoteList');
|
||
|
||
if (result.success && result.remotes && Object.keys(result.remotes).length > 0) {
|
||
remoteList.innerHTML = '';
|
||
|
||
Object.entries(result.remotes).forEach(([name, info]) => {
|
||
const remoteItem = document.createElement('div');
|
||
remoteItem.className = 'remote-item';
|
||
remoteItem.innerHTML = `
|
||
<div>
|
||
<div class="remote-name">${name} (${info.type})</div>
|
||
<div class="remote-url">${info.url}</div>
|
||
</div>
|
||
`;
|
||
remoteList.appendChild(remoteItem);
|
||
});
|
||
} else {
|
||
remoteList.innerHTML = '<div class="loading">暂无远程仓库</div>';
|
||
}
|
||
} catch (error) {
|
||
this.showMessage('获取远程仓库列表失败: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 加载分支列表
|
||
async loadBranches() {
|
||
try {
|
||
const response = await fetch('index.php', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||
body: 'action=get_branches'
|
||
});
|
||
|
||
const result = await response.json();
|
||
const branchSelect = document.getElementById('branchSelect');
|
||
|
||
if (result.success && result.branches) {
|
||
branchSelect.innerHTML = '';
|
||
|
||
result.branches.forEach(branch => {
|
||
const option = document.createElement('option');
|
||
option.value = branch.name;
|
||
option.textContent = branch.name + (branch.current ? ' (当前)' : '') + (branch.remote ? ' (远程)' : '');
|
||
if (branch.current) {
|
||
option.selected = true;
|
||
}
|
||
branchSelect.appendChild(option);
|
||
});
|
||
} else {
|
||
branchSelect.innerHTML = '<option value="">暂无分支</option>';
|
||
}
|
||
} catch (error) {
|
||
this.showMessage('获取分支列表失败: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 创建新分支
|
||
async createBranch() {
|
||
const branchName = document.getElementById('newBranchName').value.trim();
|
||
|
||
if (!branchName) {
|
||
this.showMessage('请输入分支名称', 'error');
|
||
return;
|
||
}
|
||
|
||
const params = new URLSearchParams();
|
||
params.append('action', 'create_branch');
|
||
params.append('branch_name', branchName);
|
||
|
||
try {
|
||
this.showMessage('正在创建分支...', 'info');
|
||
|
||
const response = await fetch('index.php', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||
body: params
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
this.showMessage('分支创建成功', 'success');
|
||
document.getElementById('newBranchName').value = ''; // 清空输入框
|
||
this.loadBranches(); // 重新加载分支列表
|
||
this.displayGitOutput(result.output);
|
||
} else {
|
||
this.showMessage('创建分支失败: ' + (result.message || '未知错误'), 'error');
|
||
this.displayGitOutput(result.output || []);
|
||
}
|
||
} catch (error) {
|
||
this.showMessage('创建分支出错: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 切换分支
|
||
async switchBranch() {
|
||
const branchSelect = document.getElementById('branchSelect');
|
||
const branchName = branchSelect.value;
|
||
|
||
if (!branchName) {
|
||
this.showMessage('请选择要切换的分支', 'error');
|
||
return;
|
||
}
|
||
|
||
const params = new URLSearchParams();
|
||
params.append('action', 'switch_branch');
|
||
params.append('branch_name', branchName);
|
||
|
||
try {
|
||
this.showMessage('正在切换分支...', 'info');
|
||
|
||
const response = await fetch('index.php', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||
body: params
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
this.showMessage('分支切换成功', 'success');
|
||
this.loadBranches(); // 重新加载分支列表
|
||
this.loadFileList(); // 重新加载文件列表
|
||
this.displayGitOutput(result.output);
|
||
} else {
|
||
this.showMessage('切换分支失败: ' + (result.message || '未知错误'), 'error');
|
||
this.displayGitOutput(result.output || []);
|
||
}
|
||
} catch (error) {
|
||
this.showMessage('切换分支出错: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 删除分支
|
||
async deleteBranch() {
|
||
const branchSelect = document.getElementById('branchSelect');
|
||
const branchName = branchSelect.value;
|
||
|
||
if (!branchName) {
|
||
this.showMessage('请选择要删除的分支', 'error');
|
||
return;
|
||
}
|
||
|
||
if (!confirm(`确定要删除分支 "${branchName}" 吗?`)) {
|
||
return;
|
||
}
|
||
|
||
const params = new URLSearchParams();
|
||
params.append('action', 'delete_branch');
|
||
params.append('branch_name', branchName);
|
||
params.append('force', 'false'); // 默认不强制删除
|
||
|
||
try {
|
||
this.showMessage('正在删除分支...', 'info');
|
||
|
||
const response = await fetch('index.php', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||
body: params
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
this.showMessage('分支删除成功', 'success');
|
||
this.loadBranches(); // 重新加载分支列表
|
||
this.displayGitOutput(result.output);
|
||
} else {
|
||
this.showMessage('删除分支失败: ' + (result.message || '未知错误'), 'error');
|
||
this.displayGitOutput(result.output || []);
|
||
|
||
if (confirm('是否强制删除该分支?')) {
|
||
params.set('force', 'true');
|
||
const forceResponse = await fetch('index.php', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||
body: params
|
||
});
|
||
const forceResult = await forceResponse.json();
|
||
if (forceResult.success) {
|
||
this.showMessage('分支强制删除成功', 'success');
|
||
this.loadBranches();
|
||
this.displayGitOutput(forceResult.output);
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
this.showMessage('删除分支出错: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 加载文件列表
|
||
async loadFileList() {
|
||
try {
|
||
const response = await fetch('index.php', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||
body: 'action=get_files'
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
this.displayFileList(result.files);
|
||
} else {
|
||
this.showMessage('加载文件列表失败', 'error');
|
||
}
|
||
} catch (error) {
|
||
this.showMessage('加载文件列表出错: ' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 显示文件列表
|
||
displayFileList(files) {
|
||
const container = document.getElementById('fileList');
|
||
|
||
if (files.length === 0) {
|
||
container.innerHTML = '<div class="loading">暂无文件</div>';
|
||
return;
|
||
}
|
||
|
||
container.innerHTML = '';
|
||
|
||
files.forEach(file => {
|
||
const fileItem = document.createElement('div');
|
||
fileItem.className = 'file-checkbox';
|
||
|
||
const checkbox = document.createElement('input');
|
||
checkbox.type = 'checkbox';
|
||
checkbox.id = `file-${this.escapeId(file)}`;
|
||
checkbox.value = file;
|
||
checkbox.checked = this.selectedFiles.has(file);
|
||
checkbox.addEventListener('change', (e) => {
|
||
if (e.target.checked) {
|
||
this.selectedFiles.add(file);
|
||
} else {
|
||
this.selectedFiles.delete(file);
|
||
}
|
||
});
|
||
|
||
const label = document.createElement('label');
|
||
label.htmlFor = checkbox.id;
|
||
label.className = 'file-path';
|
||
label.textContent = file;
|
||
label.style.cursor = 'pointer';
|
||
label.style.flex = '1';
|
||
|
||
fileItem.appendChild(checkbox);
|
||
fileItem.appendChild(label);
|
||
container.appendChild(fileItem);
|
||
});
|
||
}
|
||
|
||
// 全选文件
|
||
selectAllFiles() {
|
||
const checkboxes = document.querySelectorAll('#fileList input[type="checkbox"]');
|
||
checkboxes.forEach(checkbox => {
|
||
checkbox.checked = true;
|
||
this.selectedFiles.add(checkbox.value);
|
||
});
|
||
}
|
||
|
||
// 取消全选
|
||
deselectAllFiles() {
|
||
const checkboxes = document.querySelectorAll('#fileList input[type="checkbox"]');
|
||
checkboxes.forEach(checkbox => {
|
||
checkbox.checked = false;
|
||
this.selectedFiles.delete(checkbox.value);
|
||
});
|
||
}
|
||
|
||
// 工具方法:转义ID
|
||
escapeId(str) {
|
||
return str.replace(/[^a-zA-Z0-9]/g, '_');
|
||
}
|
||
|
||
// 显示消息
|
||
showMessage(message, type = 'info') {
|
||
// 创建消息元素
|
||
const messageElement = document.createElement('div');
|
||
messageElement.className = `message ${type} fade-in`;
|
||
messageElement.textContent = message;
|
||
|
||
// 添加到页面顶部
|
||
const container = document.querySelector('.container');
|
||
container.insertBefore(messageElement, container.firstChild);
|
||
|
||
// 3秒后自动移除
|
||
setTimeout(() => {
|
||
messageElement.remove();
|
||
}, 3000);
|
||
}
|
||
}
|
||
|
||
// 页面加载完成后初始化
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
new GitUploader();
|
||
}); |