изменен стиль v2 html
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user