изменен стиль v2 html

This commit is contained in:
FrigaT
2025-12-28 22:24:14 +03:00
parent bf6c0b9229
commit 119d94b0e8
4 changed files with 891 additions and 1134 deletions

View File

@@ -68,24 +68,24 @@ namespace SQLLinter.CLI
}
linter.Run(files);
//diagramer.Run("test.sql", reader.BaseStream);
diagramer.Run("test.sql", reader.BaseStream);
}
//linter.Run(@"C:\Users\frost\Desktop\DISTR-2599\test.sql");
IReportFormatter formatter = new F.v3.HtmlReportFormatter();
var content = formatter.Format(rep.Violations, null);
var content = formatter.Format(rep.Violations, bpmn);
File.WriteAllText(@"C:\Users\frost\Downloads\Telegram Desktop\test3.html", content);
formatter = new F.v2.HtmlReportFormatter();
content = formatter.Format(rep.Violations, null);
content = formatter.Format(rep.Violations, bpmn);
File.WriteAllText(@"C:\Users\frost\Downloads\Telegram Desktop\test2.html", content);
formatter = new F.v1.HtmlReportFormatter();
content = formatter.Format(rep.Violations, null);
content = formatter.Format(rep.Violations, bpmn);
File.WriteAllText(@"C:\Users\frost\Downloads\Telegram Desktop\test1.html", content);
}
}

View File

@@ -104,6 +104,14 @@ public abstract class BaseRuleVisitor : TSqlFragmentVisitor, IRule
case SchemaObjectFunctionTableReference:
return current;
// DML
case InsertStatement:
case UpdateStatement:
case DeleteStatement:
case MergeStatement:
return current;
// ---- Новые блоки для SqlDataTypeReference ----
// Определение столбца
@@ -160,10 +168,12 @@ public abstract class BaseRuleVisitor : TSqlFragmentVisitor, IRule
if (node == null || node.ScriptTokenStream == null)
return null;
var endLine = node.ScriptTokenStream.Where(t => t.Offset < node.StartOffset + node.FragmentLength).Max(t => t.Line) + 2;
// 1. Получаем токены для блока
var tokens = node.ScriptTokenStream
.Where(t => t.Line >= node.StartLine &&
t.Offset < node.StartOffset + node.FragmentLength)
.Where(t => t.Line >= node.StartLine - 2 &&
t.Line <= endLine)
.ToList();
if (tokens.Count == 0)

View File

@@ -23,37 +23,37 @@ class ReportRenderer {
this.setupTabsDragScroll();
this.renderAllReports();
this.setupEventListeners();
this.activateTab(0);
this.activateTab('summary_report');
}
setupEventListeners() {
// Делегирование для кнопок деталей
// Делегирование только для кнопок деталей (toggle)
document.addEventListener('click', (e) => {
const toggleBtn = e.target.closest('.detail-toggle-btn');
const closeBtn = e.target.closest('.detail-close-btn');
if (toggleBtn) {
e.preventDefault();
this.toggleDetail(toggleBtn.dataset.detailId, toggleBtn);
} else if (closeBtn) {
e.preventDefault();
this.hideDetail(closeBtn.dataset.detailId);
}
// Блок с closeBtn полностью удалён — он больше не нужен
});
// Переключение табов (делегирование)
this.tabsList.addEventListener('click', (e) => {
const tab = e.target.closest('.tab');
if (!tab) return;
e.preventDefault();
const targetId = tab.dataset.target;
if (!targetId) return;
this.activateTabById(targetId);
this.tabsList.querySelectorAll('.tab').forEach(t => t.classList.toggle('active', t === tab));
if (targetId === 'mermaid' && window.viewer) {
// Обновляем активную вкладку
this.tabsList.querySelectorAll('.tab').forEach(t =>
t.classList.toggle('active', t === tab)
);
// Автоматический рендер диаграммы при открытии вкладки mermaid
if (targetId === 'mermaid' && window.viewer && !window.viewer.isRendered) {
window.viewer.render().catch(console.error);
}
});
@@ -75,15 +75,26 @@ class ReportRenderer {
renderTabs() {
const fragment = document.createDocumentFragment();
// 1. Summary — первый таб
const summaryTab = document.createElement('button');
summaryTab.className = 'tab active'; // сразу делаем активным
summaryTab.dataset.target = 'summary_report';
summaryTab.dataset.index = '0'; // можно оставить 0 или любое значение
summaryTab.innerHTML = `
<div class="tab-inner">
<span class="tab-text">Summary</span>
</div>
`;
fragment.appendChild(summaryTab);
// 2. Табы файлов
this.files.forEach((file, i) => {
const critical = file.v?.c?.length || 0;
const warning = file.v?.w?.length || 0;
const tab = document.createElement('button');
tab.className = `tab ${i === 0 ? 'active' : ''}`;
tab.className = 'tab';
tab.dataset.target = `file_${i}`;
tab.dataset.index = i;
tab.dataset.index = i + 1;
tab.innerHTML = `
<div class="tab-inner">
<span class="tab-text" title="${this.escapeHtml(file.n)}">${this.escapeHtml(file.n)}</span>
@@ -96,19 +107,17 @@ class ReportRenderer {
fragment.appendChild(tab);
});
// Summary tab
const summaryTab = document.createElement('button');
summaryTab.className = 'tab';
summaryTab.dataset.target = 'summary_report';
summaryTab.innerHTML = `<div class="tab-inner"><span class="tab-text">Summary</span></div>`;
fragment.appendChild(summaryTab);
// Diagram tab
// 3. Диаграмма (если есть) — последняя
if (this.diagram.h || this.diagram.hasDiagram) {
const diagramTab = document.createElement('button');
diagramTab.className = 'tab';
diagramTab.dataset.target = 'mermaid';
diagramTab.innerHTML = `<div class="tab-inner"><span class="tab-text">Диаграмма</span></div>`;
diagramTab.dataset.index = this.files.length + 1;
diagramTab.innerHTML = `
<div class="tab-inner">
<span class="tab-text">Диаграмма</span>
</div>
`;
fragment.appendChild(diagramTab);
}
@@ -322,61 +331,51 @@ class ReportRenderer {
}
toggleDetail(detailId, toggleBtn) {
// Находим текущий видимый отчёт (активную вкладку)
const activeReport = document.querySelector('.file-report.active');
if (!activeReport) return;
// Ищем строку детали ТОЛЬКО внутри активного отчёта
const detailRow = activeReport.querySelector(`#${detailId}`);
if (!detailRow) return;
const mainRow = toggleBtn.closest('.violation-main-row');
if (!mainRow || !activeReport.contains(mainRow)) return; // защита от "чужих" кнопок
if (!mainRow || !activeReport.contains(mainRow)) return;
const isVisible = detailRow.style.display === 'table-row';
// Проверяем, открыта ли уже эта деталь
const isOpen = detailRow.style.display === 'table-row';
if (isVisible) {
this.hideDetail(detailId);
// Сначала закрываем ВСЕ открытые детали в ЭТОМ отчёте
activeReport.querySelectorAll('.violation-detail-row').forEach(row => {
row.style.display = 'none';
});
activeReport.querySelectorAll('.detail-toggle-btn.active').forEach(btn => {
btn.classList.remove('active');
const icon = btn.querySelector('.detail-toggle-icon');
if (icon) icon.style.transform = 'rotate(0deg)';
});
if (isOpen) {
// Если была открыта — теперь закрыта (мы уже всё закрыли выше)
return;
}
// Показываем текущую
// Открываем текущую
detailRow.style.display = 'table-row';
toggleBtn.classList.add('active');
const icon = toggleBtn.querySelector('.detail-toggle-icon');
if (icon) {
icon.style.transform = 'rotate(180deg)';
}
// Прокрутка к детали
// Прокрутка
setTimeout(() => {
detailRow.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}, 10);
}
hideDetail(detailId) {
const detailRow = document.getElementById(detailId);
if (!detailRow) return;
detailRow.style.display = 'none';
const toggleBtn = document.querySelector(`.detail-toggle-btn[data-detail-id="${detailId}"]`);
if (toggleBtn) {
toggleBtn.classList.remove('active');
const icon = toggleBtn.querySelector('.detail-toggle-icon');
if (icon) {
icon.style.transform = 'rotate(0deg)';
}
}
}, 100); // чуть больше задержки, чтобы браузер успел перерисовать
}
renderSummaryReport() {
const div = document.createElement('div');
div.id = 'summary_report';
div.className = 'file-report';
div.style.display = 'none';
let totalCritical = 0, totalWarning = 0, totalInfo = 0;
this.files.forEach(f => {
@@ -417,7 +416,7 @@ class ReportRenderer {
<div class="progress-fill warning" style="width:${fp[1]}%"></div>
<div class="progress-fill info" style="width:${fp[2]}%"></div>
</div>
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:var(--spacing-xs);font-size:var(--font-size-xs)">
<div class="file-card-footer">
<span>Всего: ${t}</span>
<div class="file-violations">
<span class="violation-badge critical">${c}</span>