From c46ec1827e037959c6537cdf55947c50f135988e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=B4=8B?= <2146658457@qq.com> Date: Thu, 30 Oct 2025 16:01:40 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend.js | 150 ++++- src/frontend/css/style.css | 11 + src/frontend/index.html | 164 +++-- src/frontend/js/app.js | 1000 ++++++++++++++++++++---------- src/frontend/js/modules/rules.js | 202 ++++++ src/package.json | 6 +- src/rule/setup.cfg | 62 ++ src/server/routes/rules.js | 125 ++++ src/view/API.md | 42 -- src/view/README.md | 36 -- src/view/backend.js | 405 ------------ src/view/frontend.html | 575 ----------------- src/view/simple_test.html | 347 ----------- src/view/test_refresh.html | 193 ------ 14 files changed, 1311 insertions(+), 2007 deletions(-) create mode 100644 src/frontend/js/modules/rules.js create mode 100644 src/rule/setup.cfg create mode 100644 src/server/routes/rules.js delete mode 100644 src/view/API.md delete mode 100644 src/view/README.md delete mode 100644 src/view/backend.js delete mode 100644 src/view/frontend.html delete mode 100644 src/view/simple_test.html delete mode 100644 src/view/test_refresh.html diff --git a/src/backend.js b/src/backend.js index ad6bdb2..3f47e85 100644 --- a/src/backend.js +++ b/src/backend.js @@ -54,6 +54,9 @@ const upload = multer({ } }); +// 装载模块化路由 +const rulesRouter = require(path.join(__dirname, 'server', 'routes', 'rules.js')); + // 项目存储目录 const PROJECTS_DIR = path.join(__dirname, 'projects_data'); if (!fs.existsSync(PROJECTS_DIR)) { @@ -121,7 +124,7 @@ const TOOL_CONFIG = { parseResult: (stdout) => { try { if (!stdout || stdout.trim() === '') return []; - + const lines = stdout.trim().split('\n').filter(line => line.trim()); const issues = []; @@ -184,7 +187,7 @@ async function runTool(tool, filePath) { exec(command, { shell: true, timeout: 60000 }, (error, stdout, stderr) => { console.log(`${tool} 执行完成`); - + try { const issues = config.parseResult(stdout || ''); resolve({ @@ -204,7 +207,7 @@ async function runTool(tool, filePath) { }); } -// 运行代码检查 +// 运行代码检查(单文件) async function runCodeCheck(filePath) { const tools = ['pylint', 'flake8', 'bandit']; const toolsStatus = {}; @@ -236,6 +239,39 @@ async function runCodeCheck(filePath) { }; } +// 运行代码检查(多文件,生成相对路径) +async function runCodeCheckOnFiles(filePaths, baseDir) { + const aggregateIssues = []; + const toolsStatusAggregate = { pylint: 'completed', flake8: 'completed', bandit: 'completed' }; + + for (const filePath of filePaths) { + const result = await runCodeCheck(filePath); + // 汇总工具状态(若任一文件失败则标记) + for (const k of Object.keys(result.tools_status)) { + if (result.tools_status[k] !== 'completed') { + toolsStatusAggregate[k] = result.tools_status[k]; + } + } + const rel = baseDir ? path.relative(baseDir, filePath).replace(/\\/g, '/') : path.basename(filePath); + result.all_issues.forEach(issue => { + aggregateIssues.push({ ...issue, relative_path: rel }); + }); + } + + const errorCount = aggregateIssues.filter(i => i.type === 'error').length; + const warningCount = aggregateIssues.filter(i => i.type === 'warning').length; + const infoCount = aggregateIssues.filter(i => i.type === 'info').length; + + return { + tools_status: toolsStatusAggregate, + all_issues: aggregateIssues, + total_issues: aggregateIssues.length, + error_count: errorCount, + warning_count: warningCount, + info_count: infoCount + }; +} + // ==================== API 端点 ==================== // 健康检查 @@ -246,6 +282,54 @@ app.get('/api/health', (req, res) => { }); }); +// 掛载 /api 下路由模块 +app.use('/api', rulesRouter); + +// 仪表板统计 +app.get('/api/stats', (req, res) => { + try { + const numProjects = projects.length; + let totalIssues = 0; + let errorCount = 0; + let warningCount = 0; + let infoCount = 0; + let checkedProjects = 0; + + projects.forEach(p => { + const lc = p.latest_check; + if (lc) { + checkedProjects += 1; + totalIssues += lc.total_issues || 0; + errorCount += lc.error_count || 0; + warningCount += lc.warning_count || 0; + infoCount += lc.info_count || 0; + } + }); + + // 估算“已检查文件”数量:按每个已检查项目记为1,或可拓展统计真实文件数 + const totalFilesChecked = checkedProjects; + const complianceRate = totalIssues === 0 && checkedProjects > 0 ? 100 : Math.max(0, 100 - totalIssues * 2); + + res.json({ + success: true, + data: { + num_projects: numProjects, + checked_projects: checkedProjects, + total_files_checked: totalFilesChecked, + total_issues: totalIssues, + error_count: errorCount, + warning_count: warningCount, + info_count: infoCount, + compliance_rate: Number(complianceRate.toFixed(1)) + } + }); + } catch (error) { + console.error('统计接口失败:', error); + res.status(500).json({ success: false, error: error.message }); + } +}); + + // 文件上传端点 app.post('/api/upload', upload.array('files'), (req, res) => { try { @@ -296,9 +380,18 @@ app.post('/api/check', async (req, res) => { }); } - // 获取所有Python文件 - const files = fs.readdirSync(temp_path).filter(f => f.endsWith('.py')); - + // 获取所有Python文件(包含子目录) + const files = []; + (function walk(dir) { + const items = fs.readdirSync(dir); + items.forEach(item => { + const p = path.join(dir, item); + const st = fs.statSync(p); + if (st.isDirectory()) return walk(p); + if (item.endsWith('.py')) files.push(p); + }); + })(temp_path); + if (files.length === 0) { return res.json({ success: true, @@ -313,9 +406,8 @@ app.post('/api/check', async (req, res) => { }); } - // 检查第一个文件(简化版,可以扩展为检查所有文件) - const firstFile = path.join(temp_path, files[0]); - const result = await runCodeCheck(firstFile); + // 检查所有文件 + const result = await runCodeCheckOnFiles(files, temp_path); // 清理临时文件 setTimeout(() => { @@ -463,7 +555,7 @@ app.post('/api/projects/:id/check', async (req, res) => { // 获取项目中的所有Python文件 const projectPath = project.path; const files = []; - + function getAllPythonFiles(dir) { const items = fs.readdirSync(dir); items.forEach(item => { @@ -491,8 +583,8 @@ app.post('/api/projects/:id/check', async (req, res) => { }); } - // 检查第一个文件作为示例 - const result = await runCodeCheck(files[0]); + // 检查所有文件并生成相对路径 + const result = await runCodeCheckOnFiles(files, projectPath); // 更新项目的最新检查记录 project.latest_check = { @@ -570,6 +662,40 @@ app.post('/api/projects/:id/upload-files', upload.array('files'), (req, res) => } }); +// 按路径上传单个文件到项目(保留目录结构) +app.post('/api/projects/:id/files/upload', upload.single('file'), (req, res) => { + try { + const projectId = parseInt(req.params.id); + const project = projects.find(p => p.id === projectId); + + if (!project) { + return res.status(404).json({ success: false, error: '项目不存在' }); + } + + if (!req.file) { + return res.status(400).json({ success: false, error: '没有上传文件' }); + } + + const targetSubPath = req.body.path || ''; + const safeSubPath = targetSubPath.replace(/\\/g, '/').replace(/^\/+|\/+$/g, ''); + const destDir = path.join(project.path, safeSubPath); + if (!fs.existsSync(destDir)) { + fs.mkdirSync(destDir, { recursive: true }); + } + const destPath = path.join(destDir, req.file.originalname); + fs.copyFileSync(req.file.path, destPath); + fs.unlinkSync(req.file.path); + + project.updated_at = new Date().toISOString(); + saveProjects(); + + res.json({ success: true, path: path.join(safeSubPath, req.file.originalname) }); + } catch (error) { + console.error('按路径上传文件失败:', error); + res.status(500).json({ success: false, error: error.message }); + } +}); + // 获取项目文件列表 app.get('/api/projects/:id/files', (req, res) => { try { diff --git a/src/frontend/css/style.css b/src/frontend/css/style.css index c181939..1af6225 100644 --- a/src/frontend/css/style.css +++ b/src/frontend/css/style.css @@ -1267,6 +1267,17 @@ input:checked + .slider:before { padding: 20px; } +/* 编辑模态框分栏与拖拽条 */ +#modalResizer { + transition: background 0.1s; +} +#modalResizer:hover { + background: rgba(30,60,114,0.08); +} +.resizing #modalResizer { + background: rgba(30,60,114,0.15); +} + .modal-footer { display: flex; justify-content: flex-end; diff --git a/src/frontend/index.html b/src/frontend/index.html index eebe868..212ff31 100644 --- a/src/frontend/index.html +++ b/src/frontend/index.html @@ -1,5 +1,6 @@ + @@ -7,6 +8,7 @@ +