Move print labels to (print) layout group to fix blank output

The (app) layout's flex h-screen + overflow-hidden container was
clipping label content in the print renderer. Print routes now use
a dedicated (print) layout group with no sidebar/header/flex shell,
just the raw label HTML. Routes changed to /print/device/[id] and
/print/component/[id].

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-07 11:45:59 +07:00
parent b33966b26e
commit 4e05910cd3
11 changed files with 202 additions and 208 deletions
+1 -1
View File
@@ -62,7 +62,7 @@
class="rounded-md border border-gray-300 px-3 py-1.5 text-sm text-gray-600 hover:bg-gray-100 dark:border-gray-600 dark:text-gray-400 dark:hover:bg-gray-700">
Label
</a>
<button onclick={() => window.open(`/devices/${data.device.id}/print`, '_blank', 'width=400,height=600')}
<button onclick={() => window.open(`/print/device/${data.device.id}`, '_blank', 'width=600,height=400')}
class="rounded-md border border-gray-300 px-3 py-1.5 text-sm text-gray-600 hover:bg-gray-100 dark:border-gray-600 dark:text-gray-400 dark:hover:bg-gray-700">
Print
</button>
@@ -1,31 +0,0 @@
import type { PageServerLoad } from './$types';
import { db } from '$lib/server/db/index.js';
import { devices } from '$lib/server/db/schema.js';
import { eq } from 'drizzle-orm';
import { error } from '@sveltejs/kit';
import { generateQrSvg } from '$lib/server/qr.js';
import { generateBarcodeSvg } from '$lib/server/barcode.js';
import { env } from '$env/dynamic/private';
export const load: PageServerLoad = async ({ params }) => {
const [device] = await db
.select({
id: devices.id,
title: devices.title,
brand: devices.brand,
model: devices.model,
serialNumber: devices.serialNumber,
category: devices.category
})
.from(devices)
.where(eq(devices.id, params.id));
if (!device) error(404, 'Device not found');
const url = `${env.BASE_URL ?? 'http://localhost:5173'}/devices/${params.id}`;
const qrSvg = await generateQrSvg(url);
const shortId = device.id.slice(0, 8).toUpperCase();
const barcodeDataUrl = await generateBarcodeSvg(shortId);
return { device, qrSvg, barcodeDataUrl, shortId };
};
@@ -1,104 +0,0 @@
<script lang="ts">
import { onMount } from 'svelte';
let { data } = $props();
let copies = $state(1);
function printNow() {
window.print();
}
onMount(() => {
if (window.opener) {
setTimeout(() => window.print(), 300);
}
});
</script>
<svelte:head>
<title>Print Label - {data.device.title}</title>
</svelte:head>
<!-- Screen controls -->
<div class="no-print mx-auto max-w-lg">
<div class="mb-4 flex items-center justify-between">
<a href="/devices/{data.device.id}" class="text-sm text-blue-600 hover:text-blue-700 dark:text-blue-400">&larr; Back</a>
<div class="flex items-center gap-3">
<label class="text-sm text-gray-600 dark:text-gray-400">
Copies:
<input type="number" bind:value={copies} min="1" max="20"
class="ml-1 w-14 rounded border border-gray-300 px-2 py-1 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-white" />
</label>
<button onclick={printNow}
class="rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700">
Print
</button>
</div>
</div>
<div class="mb-2 rounded-md bg-gray-100 p-3 text-xs text-gray-600 dark:bg-gray-800 dark:text-gray-400">
<strong>Printer setup:</strong> Select your Brother printer, paper size "DK-1201" (29mm x 90.3mm). Set margins to minimum.
</div>
<p class="mb-4 text-sm text-gray-500 dark:text-gray-400">Preview ({copies} label{copies > 1 ? 's' : ''}):</p>
</div>
<!-- Labels -->
{#each Array(copies) as _, i}
<div class="label" style="width: 90.3mm; height: 29mm; background: white; color: black; box-sizing: border-box; overflow: hidden; padding: 1mm; margin: 0 auto 8px auto; border: 1px dashed #ccc;">
<div style="display: flex; align-items: center; gap: 2mm; height: 100%; font-family: Arial, Helvetica, sans-serif;">
<div style="width: 22mm; height: 22mm; flex-shrink: 0;">
{@html data.qrSvg}
</div>
<div style="flex: 1; min-width: 0; overflow: hidden;">
<div style="font-size: 9pt; font-weight: bold; line-height: 1.2; color: black; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
{data.device.title}
</div>
{#if data.device.brand || data.device.model}
<div style="font-size: 7pt; color: #555; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
{[data.device.brand, data.device.model].filter(Boolean).join(' ')}
</div>
{/if}
{#if data.device.serialNumber}
<div style="font-size: 6pt; color: #777; margin-top: 0.5mm;">
S/N: {data.device.serialNumber}
</div>
{/if}
<div style="margin-top: 1mm;">
<img src={data.barcodeDataUrl} alt={data.shortId} style="height: 8mm; width: auto; max-width: 100%;" />
</div>
</div>
</div>
</div>
{/each}
<style>
@page {
size: 90.3mm 29mm;
margin: 1mm;
}
@media print {
:global(.no-print),
:global(nav),
:global(header),
:global(aside) {
display: none !important;
}
:global(main) {
padding: 0 !important;
overflow: visible !important;
}
:global(body) {
margin: 0;
padding: 0;
background: white !important;
}
.label {
border: none !important;
margin: 0 !important;
break-inside: avoid;
page-break-after: always;
}
}
</style>