From 56ef543e9a5c834924ab73e60b39e83cc2d60c95 Mon Sep 17 00:00:00 2001 From: Sergey Antropoff Date: Thu, 23 Jul 2026 09:09:12 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D1=83=20=D1=81=D0=BA?= =?UTF-8?q?=D0=B0=D1=87=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20QR=20=D0=B2?= =?UTF-8?q?=20lightbox?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Файл сохраняется как <сервер>-<пользователь>.png. --- .../templates/export/global-index.html.j2 | 49 ++++++++++++++++++- .../hysteria2/templates/export/index.html.j2 | 49 ++++++++++++++++++- 2 files changed, 94 insertions(+), 4 deletions(-) diff --git a/roles/hysteria2/templates/export/global-index.html.j2 b/roles/hysteria2/templates/export/global-index.html.j2 index 1da825e..8829461 100644 --- a/roles/hysteria2/templates/export/global-index.html.j2 +++ b/roles/hysteria2/templates/export/global-index.html.j2 @@ -313,6 +313,34 @@ font-size: 0.75rem; color: var(--muted); } + .lightbox-actions { + margin-top: 1rem; + display: flex; + justify-content: center; + } + .lightbox-download { + display: inline-flex; + align-items: center; + gap: 0.45rem; + padding: 0.55rem 1rem; + border-radius: 999px; + border: 1px solid var(--border); + background: var(--bg-input); + color: var(--accent); + text-decoration: none; + font-size: 0.85rem; + font-weight: 650; + transition: color 0.15s, border-color 0.15s, background 0.15s; + } + .lightbox-download:hover { + border-color: var(--accent); + background: var(--accent-soft); + } + .lightbox-download svg { + width: 16px; + height: 16px; + flex-shrink: 0; + } .lightbox-close { position: absolute; top: 0.65rem; @@ -459,6 +487,7 @@ tabindex="0" data-lightbox-src="{{ server.dir | e }}/{{ user.name | e }}.png" data-lightbox-caption="{{ server.name | e }} · {{ user.name | e }}" + data-lightbox-download="{{ server.name | e }}-{{ user.name | e }}.png" aria-label="Открыть QR-код {{ user.name | e }}" > @@ -487,6 +516,12 @@ + @@ -521,11 +556,15 @@ var img = document.getElementById('lightbox-img'); var caption = document.getElementById('lightbox-caption'); var closeBtn = document.getElementById('lightbox-close'); + var downloadBtn = document.getElementById('lightbox-download'); - function openLightbox(src, text, alt) { + function openLightbox(src, text, alt, filename) { img.src = src; img.alt = alt || text || 'QR'; caption.textContent = text || ''; + downloadBtn.href = src; + downloadBtn.setAttribute('download', filename || 'qr.png'); + downloadBtn.setAttribute('aria-label', 'Скачать ' + (filename || 'qr.png')); box.classList.add('open'); box.setAttribute('aria-hidden', 'false'); document.body.classList.add('lightbox-open'); @@ -537,11 +576,17 @@ box.setAttribute('aria-hidden', 'true'); document.body.classList.remove('lightbox-open'); img.removeAttribute('src'); + downloadBtn.removeAttribute('href'); } document.querySelectorAll('[data-lightbox-src]').forEach(function (el) { function openFromEl() { - openLightbox(el.getAttribute('data-lightbox-src'), el.getAttribute('data-lightbox-caption'), el.getAttribute('alt')); + openLightbox( + el.getAttribute('data-lightbox-src'), + el.getAttribute('data-lightbox-caption'), + el.getAttribute('alt'), + el.getAttribute('data-lightbox-download') + ); } el.addEventListener('click', openFromEl); el.addEventListener('keydown', function (e) { diff --git a/roles/hysteria2/templates/export/index.html.j2 b/roles/hysteria2/templates/export/index.html.j2 index 9c843a2..6a5d469 100644 --- a/roles/hysteria2/templates/export/index.html.j2 +++ b/roles/hysteria2/templates/export/index.html.j2 @@ -244,6 +244,34 @@ font-size: 0.75rem; color: var(--muted); } + .lightbox-actions { + margin-top: 1rem; + display: flex; + justify-content: center; + } + .lightbox-download { + display: inline-flex; + align-items: center; + gap: 0.45rem; + padding: 0.55rem 1rem; + border-radius: 999px; + border: 1px solid var(--border); + background: var(--bg-input); + color: var(--accent); + text-decoration: none; + font-size: 0.85rem; + font-weight: 650; + transition: color 0.15s, border-color 0.15s, background 0.15s; + } + .lightbox-download:hover { + border-color: var(--accent); + background: var(--accent-soft); + } + .lightbox-download svg { + width: 16px; + height: 16px; + flex-shrink: 0; + } .lightbox-close { position: absolute; top: 0.65rem; @@ -381,6 +409,7 @@ tabindex="0" data-lightbox-src="{{ user.name | e }}.png" data-lightbox-caption="{{ user.name | e }}" + data-lightbox-download="{{ hysteria2_output_name | e }}-{{ user.name | e }}.png" aria-label="Открыть QR-код {{ user.name | e }}" > @@ -409,6 +438,12 @@ + @@ -443,11 +478,15 @@ var img = document.getElementById('lightbox-img'); var caption = document.getElementById('lightbox-caption'); var closeBtn = document.getElementById('lightbox-close'); + var downloadBtn = document.getElementById('lightbox-download'); - function openLightbox(src, text, alt) { + function openLightbox(src, text, alt, filename) { img.src = src; img.alt = alt || text || 'QR'; caption.textContent = text || ''; + downloadBtn.href = src; + downloadBtn.setAttribute('download', filename || 'qr.png'); + downloadBtn.setAttribute('aria-label', 'Скачать ' + (filename || 'qr.png')); box.classList.add('open'); box.setAttribute('aria-hidden', 'false'); document.body.classList.add('lightbox-open'); @@ -459,11 +498,17 @@ box.setAttribute('aria-hidden', 'true'); document.body.classList.remove('lightbox-open'); img.removeAttribute('src'); + downloadBtn.removeAttribute('href'); } document.querySelectorAll('[data-lightbox-src]').forEach(function (el) { function openFromEl() { - openLightbox(el.getAttribute('data-lightbox-src'), el.getAttribute('data-lightbox-caption'), el.getAttribute('alt')); + openLightbox( + el.getAttribute('data-lightbox-src'), + el.getAttribute('data-lightbox-caption'), + el.getAttribute('alt'), + el.getAttribute('data-lightbox-download') + ); } el.addEventListener('click', openFromEl); el.addEventListener('keydown', function (e) {