изменен стиль 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); 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"); //linter.Run(@"C:\Users\frost\Desktop\DISTR-2599\test.sql");
IReportFormatter formatter = new F.v3.HtmlReportFormatter(); 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); File.WriteAllText(@"C:\Users\frost\Downloads\Telegram Desktop\test3.html", content);
formatter = new F.v2.HtmlReportFormatter(); 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); File.WriteAllText(@"C:\Users\frost\Downloads\Telegram Desktop\test2.html", content);
formatter = new F.v1.HtmlReportFormatter(); 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); 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: case SchemaObjectFunctionTableReference:
return current; return current;
// DML
case InsertStatement:
case UpdateStatement:
case DeleteStatement:
case MergeStatement:
return current;
// ---- Новые блоки для SqlDataTypeReference ---- // ---- Новые блоки для SqlDataTypeReference ----
// Определение столбца // Определение столбца
@@ -160,10 +168,12 @@ public abstract class BaseRuleVisitor : TSqlFragmentVisitor, IRule
if (node == null || node.ScriptTokenStream == null) if (node == null || node.ScriptTokenStream == null)
return null; return null;
var endLine = node.ScriptTokenStream.Where(t => t.Offset < node.StartOffset + node.FragmentLength).Max(t => t.Line) + 2;
// 1. Получаем токены для блока // 1. Получаем токены для блока
var tokens = node.ScriptTokenStream var tokens = node.ScriptTokenStream
.Where(t => t.Line >= node.StartLine && .Where(t => t.Line >= node.StartLine - 2 &&
t.Offset < node.StartOffset + node.FragmentLength) t.Line <= endLine)
.ToList(); .ToList();
if (tokens.Count == 0) if (tokens.Count == 0)

View File

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