import { For, createMemo } from 'solid-js'; import { marked } from '../../markdown'; import { getLayerStyle } from './hooks/dimensions'; import type { DeckStore } from './hooks/deckStore'; export interface PrintPreviewProps { store: DeckStore; onClose: () => void; onPrint: () => void; } /** * 渲染 layer 内容 */ function renderLayerContent(layer: { prop: string }, cardData: { [key: string]: string }): string { const content = cardData[layer.prop] || ''; return marked.parse(content) as string; } /** * 打印预览组件:在 A4 纸张上排列所有卡牌 */ export function PrintPreview(props: PrintPreviewProps) { const { store } = props; // A4 纸张尺寸(mm):210 x 297 const A4_WIDTH = 210; const A4_HEIGHT = 297; const PRINT_MARGIN = 5; // 打印边距 // 计算每张卡牌在 A4 纸上的位置(居中布局) const pages = createMemo(() => { const cards = store.state.cards; const cardWidth = store.state.dimensions?.cardWidth || 56; const cardHeight = store.state.dimensions?.cardHeight || 88; // 每行可容纳的卡牌数量 const usableWidth = A4_WIDTH - PRINT_MARGIN * 2; const cardsPerRow = Math.floor(usableWidth / cardWidth); // 每页可容纳的行数 const usableHeight = A4_HEIGHT - PRINT_MARGIN * 2; const rowsPerPage = Math.floor(usableHeight / cardHeight); // 每页的卡牌数量 const cardsPerPage = cardsPerRow * rowsPerPage; // 计算最大卡牌区域的尺寸(用于居中和外围框) const maxGridWidth = cardsPerRow * cardWidth; const maxGridHeight = rowsPerPage * cardHeight; // 居中偏移量(使卡牌区域在 A4 纸上居中) const offsetX = (A4_WIDTH - maxGridWidth) / 2; const offsetY = (A4_HEIGHT - maxGridHeight) / 2; // 分页 const result: { pageIndex: number; cards: Array<{ data: typeof cards[0]; x: number; y: number }>; bounds: { minX: number; minY: number; maxX: number; maxY: number }; }[] = []; let currentPage: typeof result[0] = { pageIndex: 0, cards: [], bounds: { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity } }; for (let i = 0; i < cards.length; i++) { const pageIndex = Math.floor(i / cardsPerPage); const indexInPage = i % cardsPerPage; const row = Math.floor(indexInPage / cardsPerRow); const col = indexInPage % cardsPerRow; if (pageIndex !== currentPage.pageIndex) { result.push(currentPage); currentPage = { pageIndex, cards: [], bounds: { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity } }; } // 使用居中偏移量计算卡牌位置 const cardX = offsetX + col * cardWidth; const cardY = offsetY + row * cardHeight; currentPage.cards.push({ data: cards[i], x: cardX, y: cardY }); // 更新边界(含 1mm 边距) currentPage.bounds.minX = Math.min(currentPage.bounds.minX, cardX); currentPage.bounds.minY = Math.min(currentPage.bounds.minY, cardY); currentPage.bounds.maxX = Math.max(currentPage.bounds.maxX, cardX + cardWidth); currentPage.bounds.maxY = Math.max(currentPage.bounds.maxY, cardY + cardHeight); } if (currentPage.cards.length > 0) { result.push(currentPage); } // 为每页添加固定的外围框尺寸(基于最大网格) return result.map(page => ({ ...page, frameBounds: { minX: offsetX, minY: offsetY, maxX: offsetX + maxGridWidth, maxY: offsetY + maxGridHeight } })); }); // 计算裁切线和外围框位置 const cropMarks = createMemo(() => { const pagesData = pages(); return pagesData.map(page => { const { frameBounds, cards } = page; const cardWidth = store.state.dimensions?.cardWidth || 56; const cardHeight = store.state.dimensions?.cardHeight || 88; // 收集所有唯一的裁切线位置 const xPositions = new Set(); const yPositions = new Set(); cards.forEach(card => { xPositions.add(card.x); xPositions.add(card.x + cardWidth); yPositions.add(card.y); yPositions.add(card.y + cardHeight); }); const sortedX = Array.from(xPositions).sort((a, b) => a - b); const sortedY = Array.from(yPositions).sort((a, b) => a - b); // 裁切线超出外围框的距离 const OVERLAP = 3; // 3mm // 生成水平裁切线(沿 Y 轴) const horizontalLines = sortedY.map(y => ({ y, xStart: frameBounds.minX - OVERLAP, xEnd: frameBounds.maxX + OVERLAP })); // 生成垂直裁切线(沿 X 轴) const verticalLines = sortedX.map(x => ({ x, yStart: frameBounds.minY - OVERLAP, yEnd: frameBounds.maxY + OVERLAP })); // 外围框边界(离卡牌区域边缘 1mm) const frameBoundsWithMargin = { x: frameBounds.minX - 1, y: frameBounds.minY - 1, width: frameBounds.maxX - frameBounds.minX + 2, height: frameBounds.maxY - frameBounds.minY + 2 }; return { horizontalLines, verticalLines, frameBounds, frameBoundsWithMargin }; }); }); const visibleLayers = createMemo(() => store.state.layerConfigs.filter((l) => l.visible)); return (
{/* 打印预览控制栏 */}

打印预览

共 {pages().length} 页,{store.state.cards.length} 张卡牌

{/* A4 纸张预览 */}
); }