Доработан js и минимизация json

This commit is contained in:
FrigaT
2025-12-26 22:45:08 +03:00
parent 4a0e9d7d6b
commit 52ac8f509e
3 changed files with 171 additions and 70 deletions

View File

@@ -1,5 +1,7 @@
using SQLLinter.Common;
using SQLLinter.Infrastructure.Diagram;
using SQLLinter.Infrastructure.Rules.RuleViolations;
using System.Data;
using System.Reflection;
using System.Text;
using System.Text.Json;
@@ -66,38 +68,76 @@ public class HtmlReportFormatter : IReportFormatter
foreach (var fileGroup in groupedByFile)
{
var fileData = new FileReportData
var fileData = new FileReport
{
FileName = fileGroup.Key
Name = fileGroup.Key,
};
// Группировка по severity
var severityGroups = fileGroup
.GroupBy(v => v.Severity)
.OrderByDescending(g => g.Key)
.ToList();
foreach (var severityGroup in severityGroups)
foreach (var violation in fileGroup.Select(t => t).OrderBy(v => v.Line).ThenBy(v => v.Column))
{
var violationsList = severityGroup
.OrderBy(v => v.Line)
.ThenBy(v => v.Column)
.Select(v => new ViolationData
{
Line = v.Line,
Column = v.Column,
RuleName = v.RuleName,
Text = EscapeHtml(v.Text),
Index = severityGroup.ToList().IndexOf(v) + 1
})
.ToList();
int ruleId;
List<string> args = new();
if (severityGroup.Key == RuleViolationSeverity.Critical)
fileData.CriticalViolations = violationsList;
else if (severityGroup.Key == RuleViolationSeverity.Warning)
fileData.WarningViolations = violationsList;
else if (severityGroup.Key == RuleViolationSeverity.Info)
fileData.InfoViolations = violationsList;
if (violation is RuleTemplateViolation templateRule)
{
args = templateRule.Params.Select(p => EscapeHtml(p)).ToList();
if (reportData.Rules.Any(t => t.Value.Name == templateRule.RuleName))
{
ruleId = reportData.Rules.First(t => t.Value.Name == templateRule.RuleName).Key;
}
else
{
ruleId = reportData.Rules.Count + 1;
reportData.Rules.Add(ruleId, new Rule
{
Name = templateRule.RuleName,
Template = templateRule.RuleTemplate
});
}
}
else
{
ruleId = reportData.Rules.Count + 1;
reportData.Rules.Add(ruleId, new Rule
{
Name = violation.RuleName,
Template = violation.Text,
});
}
var v = new Violation()
{
RuleId = ruleId,
Args = args,
Column = violation.Column,
Line = violation.Line,
};
if (violation.Severity == RuleViolationSeverity.Critical)
{
v.Index = fileData.Violations.Critical.Count + 1;
fileData.Violations.Critical.Add(v);
}
else if (violation.Severity == RuleViolationSeverity.Warning)
{
v.Index = fileData.Violations.Warning.Count + 1;
fileData.Violations.Warning.Add(v);
}
else if (violation.Severity == RuleViolationSeverity.Info)
{
v.Index = fileData.Violations.Info.Count + 1;
fileData.Violations.Info.Add(v);
}
}
reportData.Files.Add(fileData);
@@ -106,9 +146,9 @@ public class HtmlReportFormatter : IReportFormatter
// Добавление диаграммы, если есть
if (diagram != null)
{
reportData.Diagram = new DiagramData
reportData.Diagram = new Diagram
{
MermaidContent = MermaidRenderer.ToMermaidContent(diagram),
Content = MermaidRenderer.ToMermaidContent(diagram),
HasDiagram = true
};
}
@@ -173,36 +213,70 @@ public class HtmlReportFormatter : IReportFormatter
// Классы для сериализации
private class ReportData
{
public List<FileReportData> Files { get; set; } = new();
public DiagramData? Diagram { get; set; }
[JsonPropertyName("f")] // files
public List<FileReport> Files { get; set; } = new();
[JsonPropertyName("r")] // rules
public Dictionary<int, Rule> Rules { get; set; } = new();
[JsonPropertyName("d")] // diagram
public Diagram Diagram { get; set; } = new();
}
private class FileReportData
private class FileReport
{
public string FileName { get; set; } = string.Empty;
public List<ViolationData>? CriticalViolations { get; set; }
public List<ViolationData>? WarningViolations { get; set; }
public List<ViolationData>? InfoViolations { get; set; }
[JsonPropertyName("n")] // name
public string Name { get; set; } = string.Empty;
[JsonIgnore]
public int TotalViolations =>
(CriticalViolations?.Count ?? 0) +
(WarningViolations?.Count ?? 0) +
(InfoViolations?.Count ?? 0);
[JsonPropertyName("v")] // violations
public Violations Violations { get; set; } = new();
}
private class ViolationData
private class Violations
{
[JsonPropertyName("c")] // critical
public List<Violation> Critical { get; set; } = new();
[JsonPropertyName("w")] // warning
public List<Violation> Warning { get; set; } = new();
[JsonPropertyName("i")] // info
public List<Violation> Info { get; set; } = new();
}
private class Violation
{
[JsonPropertyName("i")] // index
public int Index { get; set; }
[JsonPropertyName("l")] // line
public int Line { get; set; }
[JsonPropertyName("c")] // column
public int Column { get; set; }
public string RuleName { get; set; } = string.Empty;
public string Text { get; set; } = string.Empty;
[JsonPropertyName("r")] // ruleId
public int RuleId { get; set; }
[JsonPropertyName("a")] // args (optional)
public List<string>? Args { get; set; }
}
private class DiagramData
private class Rule
{
public string MermaidContent { get; set; } = string.Empty;
[JsonPropertyName("n")] // name
public string Name { get; set; } = string.Empty;
[JsonPropertyName("t")] // template
public string Template { get; set; } = string.Empty;
}
private class Diagram
{
[JsonPropertyName("c")] // content
public string Content { get; set; } = string.Empty;
[JsonPropertyName("h")] // hasDiagram
public bool HasDiagram { get; set; }
}
}

View File

@@ -393,7 +393,7 @@ td.index {
td.rule {
font-weight: 600;
color: var(--color-text-primary);
min-width: 200px;
width: 250px;
}
td.description {

View File

@@ -7,8 +7,9 @@ import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.mi
class ReportRenderer {
constructor(data) {
this.data = data;
this.files = data.files || [];
this.diagram = data.diagram;
this.files = data.files || data.f || [];
this.rules = data.rules || data.r || {};
this.diagram = data.diagram || data.d || {};
this.currentFileIndex = 0;
this.reportsContainer = document.getElementById('reports-container');
@@ -24,12 +25,13 @@ class ReportRenderer {
this.renderTabs();
this.renderFileReports();
this.setupTabNavigation();
// Активируем первый таб
this.activateTab(0);
}
renderTabs() {
const tabsList = document.getElementById('tabs-list');
if (!tabsList) return;
this.tabsList.innerHTML = '';
// Табы для файлов
@@ -39,25 +41,30 @@ class ReportRenderer {
tab.dataset.target = `file_${index}`;
tab.dataset.index = index;
const total = (file.v.c?.length || 0) + (file.v.w?.length || 0) + (file.v.i?.length || 0);
tab.innerHTML = `
${this.escapeHtml(file.fileName)}
<span class="tab-badge">${file.totalViolations || this.calculateTotalViolations(file)}</span>
${this.escapeHtml(file.n)}
<span class="tab-badge">${total}</span>
`;
this.tabsList.appendChild(tab);
});
// Таб для диаграммы (если есть)
if (this.diagram?.hasDiagram) {
if (this.diagram.h || this.diagram.hasDiagram) {
const diagramTab = document.createElement('button');
diagramTab.className = 'tab';
diagramTab.dataset.target = 'mermaid';
diagramTab.textContent = 'Диаграмма';
this.tabsList.appendChild(diagramTab);
tabsList.appendChild(diagramTab);
}
}
renderFileReports() {
const container = document.getElementById('reports-container');
if (!container) return;
this.reportsContainer.innerHTML = '';
// Рендеринг отчетов по файлам
@@ -67,6 +74,9 @@ class ReportRenderer {
reportDiv.className = 'file-report';
reportDiv.style.display = 'none';
const fileName = file.n || file.fileName || '';
const violations = file.v || file.violations || {};
// Заголовок файла
reportDiv.innerHTML = `
<div class="file-title-container">
@@ -75,27 +85,28 @@ class ReportRenderer {
<path d="M14 2H6C4.9 2 4 2.9 4 4V20C4 21.1 4.9 22 6 22H18C19.1 22 20 21.1 20 20V8L14 2Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 2V8H20" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<span class="file-name">${this.escapeHtml(file.fileName)}</span>
<span class="file-name">${this.escapeHtml(fileName)}</span>
</div>
</div>
`;
// Секции по severity
if (file.criticalViolations?.length > 0) {
reportDiv.appendChild(this.createSeveritySection('critical', file.criticalViolations));
// Добавляем секции нарушений
if (file.v.c?.length > 0) {
reportDiv.appendChild(this.createViolationSection('critical', file.v.c));
}
if (file.warningViolations?.length > 0) {
reportDiv.appendChild(this.createSeveritySection('warning', file.warningViolations));
if (file.v.w?.length > 0) {
reportDiv.appendChild(this.createViolationSection('warning', file.v.w));
}
if (file.infoViolations?.length > 0) {
reportDiv.appendChild(this.createSeveritySection('info', file.infoViolations));
if (file.v.i?.length > 0) {
reportDiv.appendChild(this.createViolationSection('info', file.v.i));
}
this.reportsContainer.appendChild(reportDiv);
});
// Контейнер для диаграммы (если есть)
if (this.diagram?.hasDiagram) {
if (this.diagram.h || this.diagram.hasDiagram) {
const diagramContent = this.diagram.c || this.diagram.Content || '';
const diagramDiv = document.createElement('div');
diagramDiv.id = 'mermaid';
diagramDiv.className = 'file-report';
@@ -104,7 +115,7 @@ class ReportRenderer {
diagramDiv.innerHTML = `
<div class="diagram-toolbar">
<div class="toolbar-search">
<input type="text" id="diagramSearch" placeholder="Поиск по узлам (нажмите Enter)" />
<input type="text" id="diagramSearch" placeholder="Поиск по узлам" />
<button class="search-clear" type="button">✕</button>
</div>
<div class="toolbar-actions">
@@ -131,7 +142,7 @@ class ReportRenderer {
<div id="minimap"></div>
</div>
<div id="mermaidSource" style="display:none">
${this.escapeHtml(this.diagram.mermaidContent)}
${this.escapeHtml(diagramContent)}
</div>
`;
@@ -139,7 +150,7 @@ class ReportRenderer {
}
}
createSeveritySection(severity, violations) {
createViolationSection(severity, violations) {
const severityTitle = {
critical: 'Critical',
warning: 'Warning',
@@ -149,15 +160,31 @@ class ReportRenderer {
const section = document.createElement('div');
section.className = `severity-section ${severity}`;
const tableRows = violations.map(v => `
const tableRows = violations.map(violation => {
// Получаем правило по ID
const ruleId = violation.r || violation.ruleId;
const rule = this.rules[ruleId] || { n: 'Unknown', t: 'Описание отсутствует' };
// Формируем текст с подстановкой параметров
let text = rule.t || '';
const args = violation.a || violation.args || [];
if (args.length > 0 && text.includes('{')) {
args.forEach((arg, index) => {
text = text.replace(new RegExp(`\\{${index}\\}`, 'g'), arg);
});
}
return `
<tr>
<td class="index">${v.index}</td>
<td class="line">${v.line}</td>
<td class="column">${v.column}</td>
<td class="rule">${this.escapeHtml(v.ruleName)}</td>
<td class="description">${this.escapeHtml(v.text)}</td>
<td class="index">${violation.i || violation.index}</td>
<td class="line">${violation.l || violation.line}</td>
<td class="column">${violation.c || violation.column}</td>
<td class="rule">${this.escapeHtml(rule.n || rule.name)}</td>
<td class="description">${this.escapeHtml(text)}</td>
</tr>
`).join('');
`;
}).join('');
section.innerHTML = `
<div class="severity-header">