Самир начал работу
This commit is contained in:
323
src/lib/PaymentPage.svelte
Normal file
323
src/lib/PaymentPage.svelte
Normal file
@ -0,0 +1,323 @@
|
||||
<script>
|
||||
let {
|
||||
onBack = () => {},
|
||||
totalPrice = 0,
|
||||
onConfirmPayment = () => {}
|
||||
} = $props();
|
||||
const phoneNumber = '+7 (999) 123-45-67'; // Замените на ваш номер
|
||||
let copySuccess = $state(false);
|
||||
async function copyPhoneNumber() {
|
||||
const cleanNumber = phoneNumber.replace(/[^0-9+]/g, '');
|
||||
let success = false;
|
||||
// Основной метод (Clipboard API)
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(cleanNumber);
|
||||
success = true;
|
||||
} catch (err) {
|
||||
console.warn('Clipboard API failed:', err);
|
||||
}
|
||||
}
|
||||
// Fallback для мобильных браузеров и HTTP
|
||||
if (!success) {
|
||||
try {
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = cleanNumber;
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.left = '-9999px';
|
||||
textArea.style.opacity = '0';
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
const deprecated = /** @type {any} */ (document).execCommand;
|
||||
if (deprecated && deprecated.call(document, 'copy')) {
|
||||
success = true;
|
||||
}
|
||||
document.body.removeChild(textArea);
|
||||
} catch (err) {
|
||||
console.error('Fallback copy failed:', err);
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
copySuccess = true;
|
||||
setTimeout(() => {
|
||||
copySuccess = false;
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<div class="payment-container">
|
||||
<header class="payment-header">
|
||||
<button class="back-btn" onclick={onBack}>← Назад</button>
|
||||
<h1>Оплата печати</h1>
|
||||
</header>
|
||||
<main class="payment-content">
|
||||
<!-- Сумма -->
|
||||
<div class="amount-block">
|
||||
<span class="amount-label">К оплате</span>
|
||||
<span class="amount-value">{totalPrice}₽</span>
|
||||
</div>
|
||||
<!-- Информация о переводе -->
|
||||
<div class="transfer-info">
|
||||
<!-- Сначала номер телефона -->
|
||||
<div class="phone-row">
|
||||
<span class="phone-number">{phoneNumber}</span>
|
||||
<button
|
||||
class="copy-btn"
|
||||
onclick={copyPhoneNumber}
|
||||
class:success={copySuccess}
|
||||
title={copySuccess ? 'Скопировано' : 'Копировать номер'}
|
||||
aria-label={copySuccess ? 'Скопировано' : 'Копировать номер телефона'}
|
||||
>
|
||||
{copySuccess ? '✓' : '📋'}
|
||||
</button>
|
||||
</div>
|
||||
<!-- Под номером — информация о банке -->
|
||||
<p class="info-text">
|
||||
Переводить на <span class="bank-name">Т-Банк</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- Шаги оплаты -->
|
||||
<div class="steps-block">
|
||||
<h3 class="steps-title">Как оплатить:</h3>
|
||||
<ol class="steps-list">
|
||||
<li>Скопируйте номер телефона</li>
|
||||
<li>Откройте банковское приложение</li>
|
||||
<li>Выберите перевод по номеру телефона</li>
|
||||
<li>Вставьте номер <strong>{phoneNumber}</strong></li>
|
||||
<li>Укажите банк: <span class="bank-name">Т-Банк</span></li>
|
||||
<li>Введите сумму <strong>{totalPrice}₽</strong></li>
|
||||
<li>Подтвердите перевод</li>
|
||||
<li>Вернитесь сюда и нажмите кнопку ниже</li>
|
||||
</ol>
|
||||
</div>
|
||||
<!-- Кнопка подтверждения -->
|
||||
<div class="payment-actions">
|
||||
<button
|
||||
type="button"
|
||||
class="confirm-btn"
|
||||
onclick={onConfirmPayment}
|
||||
>
|
||||
Я оплатил(а) — Печать
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<style>
|
||||
.payment-container {
|
||||
width: 100%;
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: radial-gradient(120% 60% at 50% -10%, rgba(99, 102, 241, 0.18) 0%, transparent 60%);
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
color: #f5f7fa;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 2000;
|
||||
/* Предотвращает горизонтальный скролл на мобильных */
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.payment-header {
|
||||
padding: 20px 16px 8px;
|
||||
display: flex; /* Делаем контейнер флексом */
|
||||
align-items: center; /* Выравниваем по вертикали */
|
||||
position: relative; /* Для позиционирования, если понадобится */
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #6366f1;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
padding: 12px 8px;
|
||||
margin: -8px -8px -8px 0; /* Убираем лишние отступы, оставляем нулевой слева */
|
||||
min-height: 44px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
order: -1; /* Принудительно ставим кнопку первой в потоке */
|
||||
margin-right: auto; /* Теперь это сработает и прижмет кнопку к левому краю */
|
||||
}
|
||||
|
||||
.payment-header h1 {
|
||||
margin: 0;
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
flex-grow: 1; /* Заставляем заголовок занимать оставшееся место */
|
||||
text-align: center; /* Центрируем текст заголовка */
|
||||
}
|
||||
.payment-content {
|
||||
flex: 1;
|
||||
padding: 24px 16px 32px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 28px;
|
||||
max-width: 520px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* ── Сумма ── */
|
||||
.amount-block {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.amount-label {
|
||||
font-size: 13px;
|
||||
color: #8b93a1;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.amount-value {
|
||||
font-size: 48px;
|
||||
font-weight: 800;
|
||||
color: #fff;
|
||||
letter-spacing: -0.03em;
|
||||
line-height: 1;
|
||||
}
|
||||
/* ── Информация о переводе ── */
|
||||
.transfer-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
.phone-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 16px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 14px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
justify-content: center;
|
||||
}
|
||||
.phone-number {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.copy-btn {
|
||||
/* Минимум 44px для touch target на мобильных */
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(99, 102, 241, 0.2);
|
||||
border: 1px solid rgba(99, 102, 241, 0.4);
|
||||
border-radius: 12px;
|
||||
color: #fff;
|
||||
font-size: 22px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
padding: 0;
|
||||
/* Убирает задержку тапа на мобильных */
|
||||
touch-action: manipulation;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.copy-btn:hover {
|
||||
background: rgba(99, 102, 241, 0.35);
|
||||
border-color: #6366f1;
|
||||
}
|
||||
.copy-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
.copy-btn.success {
|
||||
background: rgba(34, 197, 94, 0.25);
|
||||
border-color: rgba(34, 197, 94, 0.5);
|
||||
}
|
||||
.info-text {
|
||||
color: #a9b0c0;
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
.bank-name {
|
||||
color: #fbbf24;
|
||||
font-weight: 700;
|
||||
}
|
||||
/* ── Шаги ── */
|
||||
.steps-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.steps-title {
|
||||
margin: 0;
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
color: #f5f7fa;
|
||||
text-align: left;
|
||||
}
|
||||
.steps-list {
|
||||
margin: 0;
|
||||
padding-left: 20px;
|
||||
color: #a9b0c0;
|
||||
font-size: 15px;
|
||||
line-height: 1.9;
|
||||
text-align: left;
|
||||
}
|
||||
.steps-list li {
|
||||
padding-left: 4px;
|
||||
}
|
||||
.steps-list strong {
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
}
|
||||
/* ── Кнопка подтверждения ── */
|
||||
.payment-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
margin-top: auto;
|
||||
padding-top: 8px;
|
||||
}
|
||||
.confirm-btn {
|
||||
width: 100%;
|
||||
/* Увеличенная высота для мобильных */
|
||||
padding: 20px 24px;
|
||||
border-radius: 16px;
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
color: #fff;
|
||||
background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%);
|
||||
box-shadow: 0 8px 24px -6px rgba(34, 197, 94, 0.4);
|
||||
transition: all 0.2s ease;
|
||||
touch-action: manipulation;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
min-height: 56px;
|
||||
}
|
||||
.confirm-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 12px 28px -6px rgba(34, 197, 94, 0.5);
|
||||
}
|
||||
.confirm-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
/* Адаптация для очень маленьких экранов */
|
||||
@media (max-width: 360px) {
|
||||
.amount-value {
|
||||
font-size: 40px;
|
||||
}
|
||||
.phone-number {
|
||||
font-size: 18px;
|
||||
}
|
||||
.payment-header h1 {
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -2,6 +2,7 @@
|
||||
import { onDestroy, tick } from 'svelte';
|
||||
import SelectionPage from './preview/SelectionPage.svelte';
|
||||
import PreviewPage from './preview/PreviewPage.svelte';
|
||||
import PaymentPage from './PaymentPage.svelte';
|
||||
|
||||
let { onBack = () => {} } = $props();
|
||||
|
||||
@ -16,7 +17,10 @@
|
||||
/** Режим просмотра: 'gallery' (выбор страниц) или 'preview' (чтение) */
|
||||
let viewMode = $state(/** @type {'gallery' | 'preview'} */ ('gallery'));
|
||||
|
||||
// ── Данные для галереи (выбор страниц) ──
|
||||
// ── Состояние оплаты ──
|
||||
let showPayment = $state(false);
|
||||
|
||||
// ── Данные для галереи ( выбор страниц) ──
|
||||
let galleryThumbnails = $state([]);
|
||||
let galleryLoading = $state(false);
|
||||
|
||||
@ -298,12 +302,12 @@
|
||||
|
||||
// ── Расчет цены ──
|
||||
const totalPrice = $derived(() => {
|
||||
let sum = 0;
|
||||
for (const f of files) {
|
||||
const pagesCount = f.selectedPages ? f.selectedPages.size : 0;
|
||||
sum += pagesCount * f.copies;
|
||||
}
|
||||
return sum * PRICE_PER_PAGE;
|
||||
let sum = 0;
|
||||
for (const f of files) {
|
||||
const pagesCount = f.selectedPages ? f.selectedPages.size : 0;
|
||||
sum += pagesCount * f.copies;
|
||||
}
|
||||
return sum * PRICE_PER_PAGE;
|
||||
});
|
||||
|
||||
const extraCopies = $derived(files.reduce((sum, f) => sum + (f.copies - 1), 0));
|
||||
@ -327,8 +331,14 @@
|
||||
else openPreview(file);
|
||||
}
|
||||
|
||||
// ── НОВАЯ ФУНКЦИЯ: Отправка на сервер ──
|
||||
async function submitPrint() {
|
||||
// ── Показ страницы оплаты ──
|
||||
function showPaymentPage() {
|
||||
if (files.length === 0 || isPrinting) return;
|
||||
showPayment = true;
|
||||
}
|
||||
|
||||
// ── Реальная отправка на сервер (после оплаты) ──
|
||||
async function realSubmitPrint() {
|
||||
if (files.length === 0 || isPrinting) return;
|
||||
|
||||
isPrinting = true;
|
||||
@ -365,125 +375,135 @@
|
||||
alert(`⚠️ Не удалось связаться с сервером печати.`);
|
||||
} finally {
|
||||
isPrinting = false;
|
||||
showPayment = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !activeFile}
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<!-- СТРАНИЦА ОПЛАТЫ (полный экран) -->
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
{#if showPayment}
|
||||
<PaymentPage
|
||||
totalPrice={totalPrice()}
|
||||
filesCount={files.length}
|
||||
onBack={() => showPayment = false}
|
||||
onConfirmPayment={realSubmitPrint}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<!-- ОСНОВНАЯ СТРАНИЦА ПЕЧАТИ -->
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
{#if !activeFile && !showPayment}
|
||||
<div class="page-container">
|
||||
<header class="header">
|
||||
<button class="back-btn" onclick={onBack}>← Назад</button>
|
||||
<h1>Печать</h1>
|
||||
</header>
|
||||
|
||||
<main class="options">
|
||||
{#if files.length === 0}
|
||||
<div class="empty-upload">
|
||||
<p class="empty-hint">Загрузите файлы для печати</p>
|
||||
<button class="action-btn primary" onclick={triggerFileInput}>Загрузить файл(ы)</button>
|
||||
</div>
|
||||
{:else}
|
||||
{#each files as file (file.id)}
|
||||
<section class="option-group file-card" class:expanded={file.expanded}>
|
||||
<div class="file-row" onclick={() => toggleExpanded(file)}>
|
||||
<span class="chevron">{#if file.expanded}▼{:else}►{/if}</span>
|
||||
<span class="file-name" title={file.file.name}>{file.file.name}</span>
|
||||
<span class="file-row-actions" onclick={(e) => e.stopPropagation()}>
|
||||
{#if file.previewUrl}
|
||||
<button type="button" class="icon-btn" onclick={() => openPreview(file)} title="Предпросмотр">🔍</button>
|
||||
{/if}
|
||||
<button type="button" class="icon-btn danger" onclick={() => removeFile(file)} title="Удалить">✕</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{#if file.expanded}
|
||||
<div class="file-options">
|
||||
<div class="option-sub print-params-row">
|
||||
<div class="pages-control">
|
||||
<label class="option-title compact">Страницы:</label>
|
||||
<button type="button" class="pages-btn" onclick={() => handlePagesClick(file)}>
|
||||
{pagesLabel(file)}
|
||||
</button>
|
||||
</div>
|
||||
<div class="copies-control-inline">
|
||||
<label class="option-title compact">Кол-во:</label>
|
||||
<div class="mini-counter">
|
||||
<button type="button" class="circle-btn small" onclick={() => decrementCopies(file)} disabled={file.copies <= 1}>−</button>
|
||||
<span class="copies-value">{file.copies} шт.</span>
|
||||
<button type="button" class="circle-btn small" onclick={() => incrementCopies(file)}>+</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="option-sub">
|
||||
<label class="option-title">Формат</label>
|
||||
<div class="toggle-group horizontal">
|
||||
{#each formats as f}
|
||||
<button type="button" class="toggle-btn {file.format === f ? 'active' : ''}" onclick={() => toggleFormat(file, f)}>{f}</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="option-sub">
|
||||
<label class="option-title">Цветность</label>
|
||||
<div class="toggle-group horizontal">
|
||||
{#each colorModes as c}
|
||||
<button type="button" class="toggle-btn {file.colorMode === c.id ? 'active' : ''}" onclick={() => toggleColor(file, c)}>{c.label}</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="option-sub quality-sub">
|
||||
<label class="option-title">Качество – {qualities[file.qualityIndex].label}</label>
|
||||
<input
|
||||
type="range"
|
||||
min={0}
|
||||
max={qualities.length - 1}
|
||||
step={1}
|
||||
value={file.qualityIndex}
|
||||
oninput={(e) => updateQuality(file, parseInt(e.target.value))}
|
||||
class="quality-slider"
|
||||
style="--fill: {qualityFillFor(file)}%"
|
||||
/>
|
||||
<div class="quality-marks-inline">
|
||||
{#each qualities as q}<span class="quality-mark">{q.label}</span>{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
{/each}
|
||||
<button type="button" class="add-file-btn" onclick={triggerFileInput}>+ Добавить файлы</button>
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
<footer class="actions">
|
||||
{#if files.length > 0}
|
||||
<p class="file-count">
|
||||
{files.length} {plFiles(files.length)}
|
||||
{#if extraCopies > 0}, {extraCopies} {plCopies(extraCopies)}{/if}
|
||||
</p>
|
||||
{/if}
|
||||
<div class="price-summary">
|
||||
<span class="summary-itogo">Итого</span>
|
||||
<span class="summary-price">{totalPrice()}₽</span>
|
||||
</div>
|
||||
<!-- ИЗМЕНЕННАЯ КНОПКА -->
|
||||
<button
|
||||
type="button"
|
||||
class="action-btn secondary"
|
||||
onclick={submitPrint}
|
||||
disabled={isPrinting || files.length === 0}
|
||||
>
|
||||
{isPrinting ? 'Отправка...' : 'Печать'}
|
||||
</button>
|
||||
<input type="file" bind:this={fileInput} accept="image/*,.pdf" multiple onchange={handleFileSelect} style="display:none" />
|
||||
</footer>
|
||||
<main class="options">
|
||||
{#if files.length === 0}
|
||||
<div class="empty-upload">
|
||||
<p class="empty-hint">Загрузите файлы для печати</p>
|
||||
<button class="action-btn primary" onclick={triggerFileInput}>Загрузить файл(ы)</button>
|
||||
</div>
|
||||
{:else}
|
||||
{#each files as file (file.id)}
|
||||
<section class="option-group file-card" class:expanded={file.expanded}>
|
||||
<div class="file-row" onclick={() => toggleExpanded(file)}>
|
||||
<span class="chevron">{#if file.expanded}▼{:else}►{/if}</span>
|
||||
<span class="file-name" title={file.file.name}>{file.file.name}</span>
|
||||
<span class="file-row-actions" onclick={(e) => e.stopPropagation()}>
|
||||
{#if file.previewUrl}
|
||||
<button type="button" class="icon-btn" onclick={() => openPreview(file)} title="Предпросмотр">🔍</button>
|
||||
{/if}
|
||||
<button type="button" class="icon-btn danger" onclick={() => removeFile(file)} title="Удалить">✕</button>
|
||||
</span>
|
||||
</div>
|
||||
{#if file.expanded}
|
||||
<div class="file-options">
|
||||
<div class="option-sub print-params-row">
|
||||
<div class="pages-control">
|
||||
<label class="option-title compact">Страницы:</label>
|
||||
<button type="button" class="pages-btn" onclick={() => handlePagesClick(file)}>
|
||||
{pagesLabel(file)}
|
||||
</button>
|
||||
</div>
|
||||
<div class="copies-control-inline">
|
||||
<label class="option-title compact">Кол-во:</label>
|
||||
<div class="mini-counter">
|
||||
<button type="button" class="circle-btn small" onclick={() => decrementCopies(file)} disabled={file.copies <= 1}>−</button>
|
||||
<span class="copies-value">{file.copies} шт.</span>
|
||||
<button type="button" class="circle-btn small" onclick={() => incrementCopies(file)}>+</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="option-sub">
|
||||
<label class="option-title">Формат</label>
|
||||
<div class="toggle-group horizontal">
|
||||
{#each formats as f}
|
||||
<button type="button" class="toggle-btn {file.format === f ? 'active' : ''}" onclick={() => toggleFormat(file, f)}>{f}</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class="option-sub">
|
||||
<label class="option-title">Цветность</label>
|
||||
<div class="toggle-group horizontal">
|
||||
{#each colorModes as c}
|
||||
<button type="button" class="toggle-btn {file.colorMode === c.id ? 'active' : ''}" onclick={() => toggleColor(file, c)}>{c.label}</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class="option-sub quality-sub">
|
||||
<label class="option-title">Качество – {qualities[file.qualityIndex].label}</label>
|
||||
<input
|
||||
type="range"
|
||||
min={0}
|
||||
max={qualities.length - 1}
|
||||
step={1}
|
||||
value={file.qualityIndex}
|
||||
oninput={(e) => updateQuality(file, parseInt(e.target.value))}
|
||||
class="quality-slider"
|
||||
style="--fill: {qualityFillFor(file)}%"
|
||||
/>
|
||||
<div class="quality-marks-inline">
|
||||
{#each qualities as q}<span class="quality-mark">{q.label}</span>{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
{/each}
|
||||
<button type="button" class="add-file-btn" onclick={triggerFileInput}>+ Добавить файлы</button>
|
||||
{/if}
|
||||
</main>
|
||||
<footer class="actions">
|
||||
{#if files.length > 0}
|
||||
<p class="file-count">
|
||||
{files.length} {plFiles(files.length)}
|
||||
{#if extraCopies > 0}, {extraCopies} {plCopies(extraCopies)}{/if}
|
||||
</p>
|
||||
{/if}
|
||||
<div class="price-summary">
|
||||
<span class="summary-itogo">Итого</span>
|
||||
<span class="summary-price">{totalPrice()}₽</span>
|
||||
</div>
|
||||
<!-- КНОПКА ОТКРЫВАЕТ СТРАНИЦУ ОПЛАТЫ -->
|
||||
<button
|
||||
type="button"
|
||||
class="action-btn secondary"
|
||||
onclick={showPaymentPage}
|
||||
disabled={isPrinting || files.length === 0}
|
||||
>
|
||||
{isPrinting ? 'Отправка...' : 'Далее'}
|
||||
</button>
|
||||
<input type="file" bind:this={fileInput} accept="image/*,.pdf" multiple onchange={handleFileSelect} style="display:none" />
|
||||
</footer>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
<!-- ДЕЛЕГИРОВАНИЕ В ДОЧЕРНИЕ КОМПОНЕНТЫ -->
|
||||
<!-- ОВЕРЛЕЙ ПРОСМОТРА/ГАЛЕРЕИ -->
|
||||
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||
{#if activeFile}
|
||||
<div class="overlay-backdrop" onclick={closeOverlay} role="dialog" aria-modal="true">
|
||||
|
||||
Reference in New Issue
Block a user