2026-09-08 17:38:13 +03:00
|
|
|
|
<!-- lib/pages/print/PrintMenu.svelte -->
|
|
|
|
|
|
<script>
|
2026-09-17 03:50:12 +03:00
|
|
|
|
import { createPrintStore } from './store.svelte.js';
|
|
|
|
|
|
import PrintUI from './PrintUI.svelte';
|
|
|
|
|
|
import PreviewOverlay from './PreviewOverlay.svelte';
|
|
|
|
|
|
import PrintProgress from './PrintProgress.svelte';
|
|
|
|
|
|
import PaymentPage from '$lib/common/ui/PaymentPage.svelte';
|
|
|
|
|
|
import { plFiles, plCopies } from '$lib/common/utils/pricing.util.js';
|
2026-09-19 03:54:34 +03:00
|
|
|
|
import NetworkGate from '$lib/common/ui/NetworkGate.svelte';
|
2026-09-08 17:38:13 +03:00
|
|
|
|
|
2026-09-17 03:50:12 +03:00
|
|
|
|
let { onBack = () => {} } = $props();
|
|
|
|
|
|
const store = createPrintStore();
|
|
|
|
|
|
let fileInput = $state(null);
|
2026-09-08 17:38:13 +03:00
|
|
|
|
|
2026-09-17 03:50:12 +03:00
|
|
|
|
function handleFileSelect(event) {
|
|
|
|
|
|
store.addFiles(event.target.files);
|
|
|
|
|
|
event.target.value = '';
|
|
|
|
|
|
}
|
2026-09-08 17:38:13 +03:00
|
|
|
|
|
2026-09-19 03:05:36 +03:00
|
|
|
|
const hasDuplex = $derived(
|
|
|
|
|
|
store.files.some((f) => f.sides === 'Двусторонняя')
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const hasTierSmall = $derived(
|
|
|
|
|
|
store.files.some((f) => {
|
|
|
|
|
|
const s = f.selectedPages?.size || 0;
|
|
|
|
|
|
return f.sides !== 'Двусторонняя' && s > 0 && s < 25;
|
|
|
|
|
|
})
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const hasTierMedium = $derived(
|
|
|
|
|
|
store.files.some((f) => {
|
2026-09-17 03:50:12 +03:00
|
|
|
|
const s = f.selectedPages?.size || 0;
|
2026-09-19 03:05:36 +03:00
|
|
|
|
return f.sides !== 'Двусторонняя' && s >= 25;
|
|
|
|
|
|
})
|
|
|
|
|
|
);
|
2026-09-19 03:54:34 +03:00
|
|
|
|
|
|
|
|
|
|
/* ── Проверка сети перед отправкой печати ──
|
|
|
|
|
|
* Печать работает только в локальной сети принтера (домен → 192.168.20.1).
|
2026-09-19 17:36:33 +03:00
|
|
|
|
* Гейт открывается мгновенно по нажатию «Я оплатил(а)» и сам внутри себя
|
|
|
|
|
|
* делает первую проверку: если мы уже в локалке — короткая зелёная вспышка
|
|
|
|
|
|
* и печать ушла; если нет — инструкции и авто-проход при появлении сети.
|
2026-09-19 03:54:34 +03:00
|
|
|
|
*/
|
2026-09-19 17:36:33 +03:00
|
|
|
|
/** true → показываем экран подключения к принтеру */
|
|
|
|
|
|
let printNetGate = $state(false);
|
2026-09-19 03:54:34 +03:00
|
|
|
|
|
2026-09-19 17:36:33 +03:00
|
|
|
|
function handleConfirmPayment() {
|
|
|
|
|
|
// Гейт сам проверит сеть: если уже локальная — сразу пропустит и напечатает,
|
|
|
|
|
|
// если нет — покажет инструкции и будет ждать появления сети.
|
|
|
|
|
|
printNetGate = true;
|
2026-09-19 03:54:34 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleGateReady() {
|
|
|
|
|
|
// сеть принтера появилась — пропускаем пользователя и сразу печатаем
|
2026-09-19 17:36:33 +03:00
|
|
|
|
printNetGate = false;
|
2026-09-19 03:54:34 +03:00
|
|
|
|
store.submitPrint();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleGateBack() {
|
2026-09-19 17:36:33 +03:00
|
|
|
|
printNetGate = false; // возврат к странице оплаты
|
2026-09-19 03:54:34 +03:00
|
|
|
|
}
|
2026-09-08 17:38:13 +03:00
|
|
|
|
</script>
|
|
|
|
|
|
|
2026-09-17 03:50:12 +03:00
|
|
|
|
{#if store.job}
|
|
|
|
|
|
<!-- Страница прогресса печати (после оплаты) -->
|
|
|
|
|
|
<PrintProgress {store} onBack={onBack} />
|
|
|
|
|
|
{:else if store.showPayment}
|
2026-09-19 17:36:33 +03:00
|
|
|
|
{#if printNetGate}
|
2026-09-19 03:54:34 +03:00
|
|
|
|
<NetworkGate mode="need-local" onReady={handleGateReady} onBack={handleGateBack} />
|
|
|
|
|
|
{:else}
|
|
|
|
|
|
<PaymentPage
|
|
|
|
|
|
totalPrice={store.totalPrice}
|
|
|
|
|
|
filesCount={store.files.length}
|
|
|
|
|
|
onBack={store.hidePayment}
|
|
|
|
|
|
onConfirmPayment={handleConfirmPayment}
|
|
|
|
|
|
/>
|
|
|
|
|
|
{/if}
|
2026-09-17 03:50:12 +03:00
|
|
|
|
{:else if !store.activeFile}
|
|
|
|
|
|
<div class="page-container">
|
|
|
|
|
|
<header class="header">
|
|
|
|
|
|
<button class="back-btn" onclick={onBack}>←</button>
|
|
|
|
|
|
<h1>Печать</h1>
|
|
|
|
|
|
</header>
|
|
|
|
|
|
<main class="options">
|
|
|
|
|
|
{#if store.files.length === 0}
|
|
|
|
|
|
<PrintUI as="upload-zone" onSelect={() => fileInput.click()} />
|
|
|
|
|
|
{:else}
|
|
|
|
|
|
{#each store.files as file (file.id)}
|
|
|
|
|
|
<PrintUI
|
|
|
|
|
|
as="file-card"
|
|
|
|
|
|
{file}
|
|
|
|
|
|
onToggle={store.toggleExpand}
|
|
|
|
|
|
onRemove={store.removeFile}
|
|
|
|
|
|
onUpdate={store.updateFile}
|
|
|
|
|
|
onPreview={(f) => store.openPreview(f)}
|
|
|
|
|
|
onPageSelect={(f) => (f.fileType === 'pdf' ? store.openGallery(f) : store.openPreview(f))}
|
|
|
|
|
|
pagesLabel={store.pagesLabel}
|
|
|
|
|
|
/>
|
|
|
|
|
|
{/each}
|
|
|
|
|
|
<button class="add-file-btn" onclick={() => fileInput.click()}>+ Добавить файлы</button>
|
|
|
|
|
|
{/if}
|
|
|
|
|
|
</main>
|
|
|
|
|
|
<PrintUI
|
2026-09-19 03:05:36 +03:00
|
|
|
|
as="footer"
|
|
|
|
|
|
totalPrice={store.totalPrice}
|
|
|
|
|
|
filesCount={store.files.length}
|
|
|
|
|
|
extraCopies={store.extraCopies}
|
|
|
|
|
|
isPrinting={store.isPrinting}
|
|
|
|
|
|
onSubmit={store.openPayment}
|
|
|
|
|
|
hasDuplex={hasDuplex}
|
|
|
|
|
|
hasTierSmall={hasTierSmall}
|
|
|
|
|
|
hasTierMedium={hasTierMedium}
|
|
|
|
|
|
plFiles={plFiles}
|
|
|
|
|
|
plCopies={plCopies}
|
2026-09-17 03:50:12 +03:00
|
|
|
|
/>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="file"
|
|
|
|
|
|
bind:this={fileInput}
|
|
|
|
|
|
accept="image/*,.pdf,.doc,.docx,.odt,.xls,.xlsx,.ods,.ppt,.pptx,.odp"
|
|
|
|
|
|
multiple
|
|
|
|
|
|
onchange={handleFileSelect}
|
|
|
|
|
|
hidden
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2026-09-08 17:38:13 +03:00
|
|
|
|
{/if}
|
|
|
|
|
|
|
2026-09-17 03:50:12 +03:00
|
|
|
|
{#if !store.job}
|
|
|
|
|
|
<PreviewOverlay {store} />
|
2026-09-08 17:38:13 +03:00
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
|
|
<style>
|
2026-09-17 03:50:12 +03:00
|
|
|
|
.page-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; }
|
|
|
|
|
|
.header { padding: 24px 20px 8px; display: flex; align-items: center; position: relative; }
|
|
|
|
|
|
.back-btn { background: none; border: none; color: #6366f1; font-size: 15px; font-weight: 600; cursor: pointer; padding: 8px 4px; margin-right: auto; min-height: 44px; }
|
|
|
|
|
|
.header h1 { margin: 0; font-size: 28px; font-weight: 700; position: absolute; left: 50%; transform: translateX(-50%); white-space: nowrap; }
|
|
|
|
|
|
.options { flex: 1; padding: 24px 20px 16px; display: flex; flex-direction: column; gap: 14px; }
|
|
|
|
|
|
.add-file-btn { align-self: center; background: none; border: 1px dashed rgba(255,255,255,0.18); color: #a9b0c0; font-size: 14px; font-weight: 600; padding: 12px 18px; border-radius: 14px; cursor: pointer; margin-top: 8px; }
|
2026-09-08 17:38:13 +03:00
|
|
|
|
</style>
|