File diff suppressed because it is too large
Load Diff
@ -0,0 +1,202 @@
|
||||
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 };
|
||||
|
||||
|
||||
@ -0,0 +1,125 @@
|
||||
const express = require('express');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const multer = require('multer');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const RULE_DIR = path.join(__dirname, '..', '..', 'rule');
|
||||
if (!fs.existsSync(RULE_DIR)) {
|
||||
fs.mkdirSync(RULE_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
const ruleStorage = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
cb(null, RULE_DIR);
|
||||
},
|
||||
filename: (req, file, cb) => {
|
||||
cb(null, file.originalname);
|
||||
}
|
||||
});
|
||||
|
||||
const ruleUpload = multer({
|
||||
storage: ruleStorage,
|
||||
limits: { fileSize: 5 * 1024 * 1024 },
|
||||
fileFilter: (req, file, cb) => {
|
||||
const allowed = [
|
||||
'setup.cfg', '.pylintrc', '.flake8', 'pyproject.toml',
|
||||
'pylintrc', 'flake8.cfg', 'tox.ini'
|
||||
];
|
||||
const extAllowed = /\.(cfg|ini|toml|yaml|yml|json)$/i;
|
||||
if (allowed.includes(file.originalname) || extAllowed.test(file.originalname)) {
|
||||
return cb(null, true);
|
||||
}
|
||||
return cb(new Error('不支持的规则文件类型'));
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/rules/upload', ruleUpload.array('files'), (req, res) => {
|
||||
try {
|
||||
if (!req.files || req.files.length === 0) {
|
||||
return res.status(400).json({ success: false, error: '没有上传文件' });
|
||||
}
|
||||
const saved = req.files.map(f => f.originalname);
|
||||
res.json({ success: true, data: { saved_files: saved, directory: RULE_DIR } });
|
||||
} catch (error) {
|
||||
console.error('规则文件上传失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/rules', (req, res) => {
|
||||
try {
|
||||
if (!fs.existsSync(RULE_DIR)) {
|
||||
return res.json({ success: true, data: [] });
|
||||
}
|
||||
const files = fs.readdirSync(RULE_DIR).map(name => {
|
||||
const p = path.join(RULE_DIR, name);
|
||||
const stat = fs.statSync(p);
|
||||
return {
|
||||
name,
|
||||
size: stat.size,
|
||||
modified_at: stat.mtime.toISOString(),
|
||||
is_directory: stat.isDirectory()
|
||||
};
|
||||
}).filter(item => !item.is_directory);
|
||||
res.json({ success: true, data: files });
|
||||
} catch (error) {
|
||||
console.error('读取规则列表失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/rules/:name', (req, res) => {
|
||||
try {
|
||||
const name = req.params.name;
|
||||
const target = path.join(RULE_DIR, name);
|
||||
if (!fs.existsSync(target)) {
|
||||
return res.status(404).json({ success: false, error: '规则文件不存在' });
|
||||
}
|
||||
const content = fs.readFileSync(target, 'utf8');
|
||||
res.json({ success: true, data: { name, content } });
|
||||
} catch (error) {
|
||||
console.error('读取规则文件失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/rules/:name', (req, res) => {
|
||||
try {
|
||||
const name = req.params.name;
|
||||
const { content } = req.body || {};
|
||||
if (!name) {
|
||||
return res.status(400).json({ success: false, error: '规则文件名不能为空' });
|
||||
}
|
||||
const target = path.join(RULE_DIR, name);
|
||||
const dir = path.dirname(target);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(target, content ?? '', 'utf8');
|
||||
res.json({ success: true, message: '保存成功' });
|
||||
} catch (error) {
|
||||
console.error('保存规则文件失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.delete('/rules/:name', (req, res) => {
|
||||
try {
|
||||
const name = req.params.name;
|
||||
const target = path.join(RULE_DIR, name);
|
||||
if (!fs.existsSync(target)) {
|
||||
return res.status(404).json({ success: false, error: '规则文件不存在' });
|
||||
}
|
||||
fs.unlinkSync(target);
|
||||
res.json({ success: true, message: '已删除' });
|
||||
} catch (error) {
|
||||
console.error('删除规则文件失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
|
||||
@ -1,347 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>简化的代码质量检查工具</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Microsoft YaHei', sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #2c3e50;
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.tool-checkbox {
|
||||
margin: 15px 0;
|
||||
padding: 10px;
|
||||
background-color: #f1f8ff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.file-upload {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.download-btn {
|
||||
display: inline-block;
|
||||
padding: 10px 20px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
transition: background-color 0.3s;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.download-btn:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
#report-area {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
display: none;
|
||||
background-color: #e8f5e9;
|
||||
}
|
||||
|
||||
#result {
|
||||
background-color: #f5f5f5;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
max-height: 400px;
|
||||
overflow: auto;
|
||||
white-space: pre-wrap;
|
||||
font-family: monospace;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.loader {
|
||||
border: 4px solid #f3f3f3;
|
||||
border-top: 4px solid #3498db;
|
||||
border-radius: 50%;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 20px auto;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
background-color: #95a5a6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.debug-panel {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
margin-top: 20px;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
max-height: 200px;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>简化的代码质量检查工具</h1>
|
||||
|
||||
<div id="toolContainer">
|
||||
<fieldset>
|
||||
<legend>选择工具:</legend>
|
||||
<div class="tool-checkbox">
|
||||
<input type="checkbox" id="bandit" name="tools" value="bandit" checked>
|
||||
<label for="bandit">Bandit</label>
|
||||
</div>
|
||||
<div class="tool-checkbox">
|
||||
<input type="checkbox" id="flake8" name="tools" value="flake8" checked>
|
||||
<label for="flake8">Flake8</label>
|
||||
</div>
|
||||
<div class="tool-checkbox">
|
||||
<input type="checkbox" id="pylint" name="tools" value="pylint" checked>
|
||||
<label for="pylint">Pylint</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div class="file-upload">
|
||||
<label for="file">上传代码文件:</label>
|
||||
<input type="file" id="file" name="file" accept=".py" aria-label="选择Python文件">
|
||||
</div>
|
||||
|
||||
<button type="button" id="submitBtn" class="btn">提交检查</button>
|
||||
</div>
|
||||
|
||||
<div class="loader" id="loader"></div>
|
||||
|
||||
<div id="report-area" aria-live="polite">
|
||||
<h2>检查报告</h2>
|
||||
<p>报告已生成,点击下载:</p>
|
||||
<a id="report-download" href="#" class="download-btn">下载报告</a>
|
||||
<button id="close-report" class="btn" style="margin-left: 10px;">关闭</button>
|
||||
</div>
|
||||
|
||||
<h2>结果:</h2>
|
||||
<pre id="result" role="log" aria-live="polite"></pre>
|
||||
|
||||
<div class="debug-panel">
|
||||
<strong>调试信息:</strong><br>
|
||||
<div id="debug-log"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 简单的调试日志函数
|
||||
function debugLog(message, data = null) {
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
const logEntry = `[${timestamp}] ${message}`;
|
||||
if (data) {
|
||||
console.log(logEntry, data);
|
||||
} else {
|
||||
console.log(logEntry);
|
||||
}
|
||||
|
||||
const debugDiv = document.getElementById('debug-log');
|
||||
debugDiv.innerHTML += logEntry + '<br>';
|
||||
debugDiv.scrollTop = debugDiv.scrollHeight;
|
||||
}
|
||||
|
||||
// 页面加载
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
debugLog('页面DOM加载完成');
|
||||
|
||||
const submitBtn = document.getElementById('submitBtn');
|
||||
const resultPre = document.getElementById('result');
|
||||
const reportArea = document.getElementById('report-area');
|
||||
const reportLink = document.getElementById('report-download');
|
||||
const closeReportBtn = document.getElementById('close-report');
|
||||
const loader = document.getElementById('loader');
|
||||
const fileInput = document.getElementById('file');
|
||||
|
||||
let isProcessing = false;
|
||||
|
||||
// 初始隐藏报告区域
|
||||
reportArea.style.display = 'none';
|
||||
|
||||
// 关闭报告区域
|
||||
closeReportBtn.addEventListener('click', function (event) {
|
||||
event.preventDefault();
|
||||
reportArea.style.display = 'none';
|
||||
debugLog('报告区域已关闭');
|
||||
});
|
||||
|
||||
// 提交按钮处理
|
||||
submitBtn.addEventListener('click', async function (event) {
|
||||
debugLog('提交按钮被点击');
|
||||
|
||||
// 阻止所有默认行为
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
debugLog('阻止了默认行为和冒泡');
|
||||
|
||||
// 防止重复提交
|
||||
if (isProcessing) {
|
||||
debugLog('正在处理中,忽略重复点击');
|
||||
return;
|
||||
}
|
||||
isProcessing = true;
|
||||
debugLog('开始处理请求');
|
||||
|
||||
// 更新UI状态
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.textContent = '检查中...';
|
||||
loader.style.display = 'block';
|
||||
resultPre.textContent = '正在检查...';
|
||||
reportArea.style.display = 'none';
|
||||
|
||||
try {
|
||||
// 验证文件
|
||||
if (fileInput.files.length === 0) {
|
||||
throw new Error('请上传一个文件');
|
||||
}
|
||||
debugLog('文件验证通过');
|
||||
|
||||
// 获取选中的工具
|
||||
const tools = [];
|
||||
if (document.getElementById('bandit').checked) tools.push('bandit');
|
||||
if (document.getElementById('flake8').checked) tools.push('flake8');
|
||||
if (document.getElementById('pylint').checked) tools.push('pylint');
|
||||
|
||||
if (tools.length === 0) {
|
||||
throw new Error('请至少选择一个工具');
|
||||
}
|
||||
debugLog('工具选择:', tools);
|
||||
|
||||
// 准备表单数据
|
||||
const formData = new FormData();
|
||||
formData.append('file', fileInput.files[0]);
|
||||
formData.append('tools', tools.join(','));
|
||||
debugLog('表单数据准备完成');
|
||||
|
||||
// 发送请求
|
||||
debugLog('开始发送网络请求...');
|
||||
const response = await fetch('http://localhost:3000/check', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
debugLog('收到响应,状态码:', response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`HTTP错误! 状态码: ${response.status}\n${errorText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
debugLog('成功解析响应数据');
|
||||
debugLog('响应数据结构:', data);
|
||||
|
||||
// 显示结果
|
||||
resultPre.textContent = JSON.stringify(data, null, 2);
|
||||
debugLog('结果已显示在页面上');
|
||||
|
||||
// 显示报告下载区域
|
||||
if (data.reportUrl) {
|
||||
const downloadUrl = 'http://localhost:3000' + data.reportUrl;
|
||||
reportLink.href = downloadUrl;
|
||||
reportLink.download = `code_report_${Date.now()}.md`;
|
||||
reportArea.style.display = 'block';
|
||||
debugLog('报告下载链接已设置');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
debugLog('处理过程中发生错误:', error.message);
|
||||
resultPre.textContent = `错误: ${error.message}`;
|
||||
console.error('详细错误信息:', error);
|
||||
} finally {
|
||||
// 恢复UI状态
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = '提交检查';
|
||||
loader.style.display = 'none';
|
||||
isProcessing = false;
|
||||
debugLog('UI状态已恢复');
|
||||
}
|
||||
});
|
||||
|
||||
// 下载链接处理
|
||||
reportLink.addEventListener('click', function (event) {
|
||||
event.preventDefault();
|
||||
debugLog('下载链接被点击');
|
||||
|
||||
// 使用简单的下载方式
|
||||
const link = document.createElement('a');
|
||||
link.href = reportLink.href;
|
||||
link.download = reportLink.download;
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
debugLog('下载已启动');
|
||||
});
|
||||
|
||||
debugLog('事件监听器设置完成');
|
||||
});
|
||||
|
||||
// 全局错误处理
|
||||
window.addEventListener('error', function (event) {
|
||||
console.error('全局JavaScript错误:', event.error);
|
||||
debugLog('捕获到全局错误: ' + event.message);
|
||||
});
|
||||
|
||||
window.addEventListener('unhandledrejection', function (event) {
|
||||
console.error('未处理的Promise错误:', event.reason);
|
||||
debugLog('捕获到未处理Promise错误: ' + event.reason);
|
||||
});
|
||||
|
||||
debugLog('脚本初始化完成');
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -1,193 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>防刷新测试页面</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.test-section {
|
||||
margin: 20px 0;
|
||||
padding: 15px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>防刷新测试页面</h1>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>防刷新状态控制</h2>
|
||||
<button id="toggleRefresh" class="btn">启用防刷新</button>
|
||||
<span id="refreshStatus">防刷新: 禁用</span>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>测试按钮</h2>
|
||||
<button id="testBtn1" class="btn">测试按钮1</button>
|
||||
<button id="testBtn2" class="btn">测试按钮2</button>
|
||||
<button id="testBtn3" class="btn">测试按钮3</button>
|
||||
<a id="testLink" href="#" class="btn" style="text-decoration: none;">测试链接</a>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>结果显示</h2>
|
||||
<div id="results" class="status">
|
||||
点击按钮查看结果...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>键盘测试</h2>
|
||||
<p>尝试按 F5 或 Ctrl+R 键测试防刷新功能</p>
|
||||
<div id="keyboardResults" class="status">
|
||||
键盘事件将在这里显示...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 防刷新状态
|
||||
let preventRefresh = false;
|
||||
let eventCount = 0;
|
||||
|
||||
// DOM元素
|
||||
const elements = {
|
||||
toggleRefresh: document.getElementById('toggleRefresh'),
|
||||
refreshStatus: document.getElementById('refreshStatus'),
|
||||
testBtn1: document.getElementById('testBtn1'),
|
||||
testBtn2: document.getElementById('testBtn2'),
|
||||
testBtn3: document.getElementById('testBtn3'),
|
||||
testLink: document.getElementById('testLink'),
|
||||
results: document.getElementById('results'),
|
||||
keyboardResults: document.getElementById('keyboardResults')
|
||||
};
|
||||
|
||||
// 全局防刷新拦截器
|
||||
function setupRefreshPrevention() {
|
||||
// 拦截所有点击事件
|
||||
document.addEventListener('click', function (event) {
|
||||
if (preventRefresh) {
|
||||
eventCount++;
|
||||
console.log(`拦截到点击事件 #${eventCount}:`, event.target.tagName, event.target.id || event.target.textContent);
|
||||
elements.results.innerHTML += `<br>点击事件 #${eventCount}: ${event.target.tagName} - ${event.target.id || event.target.textContent}`;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
}, true);
|
||||
|
||||
// 拦截键盘事件
|
||||
document.addEventListener('keydown', function (event) {
|
||||
if (preventRefresh) {
|
||||
console.log('拦截到键盘事件:', event.key, event.ctrlKey ? 'Ctrl+' : '');
|
||||
elements.keyboardResults.innerHTML += `<br>键盘事件: ${event.key}${event.ctrlKey ? ' (Ctrl)' : ''}`;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
}, true);
|
||||
|
||||
// 拦截链接点击
|
||||
document.addEventListener('click', function (event) {
|
||||
if (preventRefresh && event.target.tagName === 'A') {
|
||||
console.log('拦截到链接点击:', event.target.href);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
// 切换防刷新状态
|
||||
elements.toggleRefresh.addEventListener('click', function (event) {
|
||||
event.preventDefault();
|
||||
preventRefresh = !preventRefresh;
|
||||
|
||||
if (preventRefresh) {
|
||||
elements.toggleRefresh.textContent = '禁用防刷新';
|
||||
elements.toggleRefresh.style.backgroundColor = '#27ae60';
|
||||
elements.refreshStatus.textContent = '防刷新: 启用';
|
||||
console.log('防刷新已启用');
|
||||
} else {
|
||||
elements.toggleRefresh.textContent = '启用防刷新';
|
||||
elements.toggleRefresh.style.backgroundColor = '#3498db';
|
||||
elements.refreshStatus.textContent = '防刷新: 禁用';
|
||||
eventCount = 0;
|
||||
elements.results.innerHTML = '点击按钮查看结果...';
|
||||
elements.keyboardResults.innerHTML = '键盘事件将在这里显示...';
|
||||
console.log('防刷新已禁用');
|
||||
}
|
||||
});
|
||||
|
||||
// 添加测试按钮事件
|
||||
[elements.testBtn1, elements.testBtn2, elements.testBtn3].forEach((btn, index) => {
|
||||
btn.addEventListener('click', function (event) {
|
||||
if (!preventRefresh) {
|
||||
elements.results.innerHTML += `<br>按钮 ${index + 1} 被点击`;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 添加测试链接事件
|
||||
elements.testLink.addEventListener('click', function (event) {
|
||||
if (!preventRefresh) {
|
||||
elements.results.innerHTML += '<br>链接被点击';
|
||||
}
|
||||
});
|
||||
|
||||
// 页面事件监听器
|
||||
window.addEventListener('beforeunload', function (event) {
|
||||
console.log('页面即将被刷新或关闭');
|
||||
if (preventRefresh) {
|
||||
event.preventDefault();
|
||||
event.returnValue = '';
|
||||
return '';
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化
|
||||
setupRefreshPrevention();
|
||||
console.log('防刷新测试页面初始化完成');
|
||||
|
||||
// 添加页面加载时间戳
|
||||
const loadTime = new Date().toLocaleString();
|
||||
console.log('页面加载时间:', loadTime);
|
||||
document.body.insertAdjacentHTML('afterbegin',
|
||||
`<div style="background: #e8f5e9; padding: 10px; margin-bottom: 20px; border-radius: 4px;">
|
||||
页面加载时间: ${loadTime}
|
||||
</div>`
|
||||
);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Loading…
Reference in new issue