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.

203 lines
8.0 KiB

const API_BASE_URL = window.location.origin + '/api';
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(1)) + ' ' + sizes[i];
}
function showSuccessMessage(message) {
const messageDiv = document.createElement('div');
messageDiv.style.cssText = `position: fixed; top: 20px; right: 20px; background: #d4edda; color: #155724; border: 1px solid #c3e6cb; border-radius: 6px; padding: 15px 20px; z-index: 3000; box-shadow: 0 4px 12px rgba(0,0,0,0.15); display: flex; align-items: center; gap: 10px;`;
messageDiv.innerHTML = `<i class="fas fa-check-circle"></i><span>${message}</span>`;
document.body.appendChild(messageDiv);
setTimeout(() => messageDiv.remove(), 3000);
}
function showErrorMessage(message) {
const messageDiv = document.createElement('div');
messageDiv.style.cssText = `position: fixed; top: 20px; right: 20px; background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; border-radius: 6px; padding: 15px 20px; z-index: 3000; box-shadow: 0 4px 12px rgba(0,0,0,0.15); display: flex; align-items: center; gap: 10px;`;
messageDiv.innerHTML = `<i class="fas fa-exclamation-circle"></i><span>${message}</span>`;
document.body.appendChild(messageDiv);
setTimeout(() => messageDiv.remove(), 5000);
}
async function loadRules() {
try {
const response = await fetch(`${API_BASE_URL}/rules`);
const data = await response.json();
if (!data.success) throw new Error(data.error || '加载失败');
renderRulesList(data.data || []);
} catch (e) {
console.error('加载规则失败:', e);
showErrorMessage('加载规则失败');
}
}
function renderRulesList(files) {
const container = document.getElementById('rulesList');
if (!container) return;
container.innerHTML = '';
if (!files.length) {
container.innerHTML = '<div style="text-align:center; padding: 20px; color:#666;">暂无规则文件</div>';
return;
}
files.forEach(f => {
const card = document.createElement('div');
card.className = 'project-card';
const size = formatFileSize(f.size || 0);
card.innerHTML = `
<div class="project-header">
<div>
<div class="project-title">${f.name}</div>
<div class="project-description">大小: ${size} | 更新: ${new Date(f.modified_at).toLocaleString()}</div>
</div>
</div>
<div class="project-actions">
<button class="btn btn-secondary">编辑</button>
<button class="btn btn-primary">下载</button>
<button class="btn btn-danger">删除</button>
</div>
`;
const [editBtn, downloadBtn, deleteBtn] = card.querySelectorAll('.project-actions .btn');
editBtn.addEventListener('click', (e) => { e.stopPropagation(); editRule(f.name); });
downloadBtn.addEventListener('click', (e) => { e.stopPropagation(); downloadRule(f.name); });
deleteBtn.addEventListener('click', (e) => { e.stopPropagation(); deleteRule(f.name); });
container.appendChild(card);
});
}
function showRuleUploadDialog() {
const input = document.createElement('input');
input.type = 'file';
input.multiple = true;
input.accept = '.cfg,.ini,.toml,.yaml,.yml,.json,.flake8,.pylintrc,setup.cfg,pyproject.toml,tox.ini';
input.onchange = async (e) => {
const files = Array.from(e.target.files || []);
if (!files.length) return;
const formData = new FormData();
files.forEach(f => formData.append('files', f));
try {
const resp = await fetch(`${API_BASE_URL}/rules/upload`, { method: 'POST', body: formData });
const data = await resp.json();
if (data.success) {
showSuccessMessage('规则上传成功');
loadRules();
} else {
showErrorMessage('规则上传失败:' + (data.error || ''));
}
} catch (e) {
console.error('上传规则失败:', e);
showErrorMessage('上传规则失败');
}
};
input.click();
}
let currentEditingRule = '';
async function editRule(name) {
try {
const resp = await fetch(`${API_BASE_URL}/rules/${encodeURIComponent(name)}`);
const data = await resp.json();
if (!data.success) throw new Error(data.error || '读取失败');
currentEditingRule = name;
document.getElementById('editRuleTitle').textContent = `编辑规则 - ${name}`;
document.getElementById('editRuleTextarea').value = data.data.content || '';
document.getElementById('editRuleModal').style.display = 'block';
} catch (e) {
console.error('读取规则失败:', e);
showErrorMessage('读取规则失败');
}
}
function hideEditRuleModal() {
const modal = document.getElementById('editRuleModal');
if (modal) modal.style.display = 'none';
currentEditingRule = '';
}
async function saveEditingRule() {
if (!currentEditingRule) return;
const content = document.getElementById('editRuleTextarea').value;
try {
const resp = await fetch(`${API_BASE_URL}/rules/${encodeURIComponent(currentEditingRule)}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content })
});
const data = await resp.json();
if (data.success) {
showSuccessMessage('保存成功');
hideEditRuleModal();
loadRules();
} else {
showErrorMessage('保存失败:' + (data.error || ''));
}
} catch (e) {
console.error('保存规则失败:', e);
showErrorMessage('保存失败');
}
}
async function deleteRule(name) {
if (!confirm(`确定删除规则文件 "${name}" 吗?`)) return;
try {
const resp = await fetch(`${API_BASE_URL}/rules/${encodeURIComponent(name)}`, { method: 'DELETE' });
const data = await resp.json();
if (data.success) {
showSuccessMessage('删除成功');
loadRules();
} else {
showErrorMessage('删除失败:' + (data.error || ''));
}
} catch (e) {
console.error('删除规则失败:', e);
showErrorMessage('删除失败');
}
}
async function downloadRule(name) {
try {
const resp = await fetch(`${API_BASE_URL}/rules/${encodeURIComponent(name)}`);
const data = await resp.json();
if (!data.success) throw new Error(data.error || '下载失败');
const blob = new Blob([data.data.content || ''], { type: 'text/plain;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = name;
document.body.appendChild(a);
a.click();
URL.revokeObjectURL(url);
a.remove();
} catch (e) {
console.error('下载规则失败:', e);
showErrorMessage('下载失败');
}
}
function bindRulesEvents() {
const uploadRuleBtn = document.getElementById('uploadRuleBtn');
const refreshRuleBtn = document.getElementById('refreshRuleBtn');
const closeEditRule = document.getElementById('closeEditRule');
const cancelEditRule = document.getElementById('cancelEditRule');
const saveRuleBtn = document.getElementById('saveRuleBtn');
if (uploadRuleBtn) uploadRuleBtn.addEventListener('click', showRuleUploadDialog);
if (refreshRuleBtn) refreshRuleBtn.addEventListener('click', loadRules);
if (closeEditRule) closeEditRule.addEventListener('click', hideEditRuleModal);
if (cancelEditRule) cancelEditRule.addEventListener('click', hideEditRuleModal);
if (saveRuleBtn) saveRuleBtn.addEventListener('click', saveEditingRule);
}
document.addEventListener('DOMContentLoaded', () => {
bindRulesEvents();
});
export { loadRules };