diff --git a/src/components/md-deck/DeckHeader.tsx b/src/components/md-deck/DeckHeader.tsx index 304fe40..b0b2a64 100644 --- a/src/components/md-deck/DeckHeader.tsx +++ b/src/components/md-deck/DeckHeader.tsx @@ -25,6 +25,14 @@ export function DeckHeader(props: DeckHeaderProps) { {store.state.isEditing ? '✓ 编辑中' : '✏️ 编辑'} + {/* 打印按钮 */} + + {/* Tab 选择器 */}
diff --git a/src/components/md-deck/PrintPreview.tsx b/src/components/md-deck/PrintPreview.tsx new file mode 100644 index 0000000..18c6c54 --- /dev/null +++ b/src/components/md-deck/PrintPreview.tsx @@ -0,0 +1,165 @@ +import { Show, 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 result: { pageIndex: number; cards: Array<{ data: typeof cards[0]; x: number; y: number }> }[] = []; + let currentPage: typeof result[0] = { pageIndex: 0, cards: [] }; + + 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: [] }; + } + + currentPage.cards.push({ + data: cards[i], + x: PRINT_MARGIN + col * cardWidth, + y: PRINT_MARGIN + row * cardHeight + }); + } + + if (currentPage.cards.length > 0) { + result.push(currentPage); + } + + return result; + }); + + const visibleLayers = createMemo(() => store.state.layerConfigs.filter((l) => l.visible)); + + return ( +
+
+ {/* 打印预览控制栏 */} +
+
+

打印预览

+

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

+
+
+ + +
+
+ + {/* A4 纸张预览 */} +
+ + {(page) => ( +
+ {/* 渲染该页的所有卡牌 */} +
+ + {(card) => ( +
+ {/* 网格区域容器 */} +
+ {/* 渲染每个 layer */} + + {(layer) => ( +
+ )} + +
+
+ )} + +
+
+ )} + +
+
+
+ ); +} diff --git a/src/components/md-deck/hooks/deckStore.ts b/src/components/md-deck/hooks/deckStore.ts index 75833a1..3f7bb5e 100644 --- a/src/components/md-deck/hooks/deckStore.ts +++ b/src/components/md-deck/hooks/deckStore.ts @@ -53,6 +53,9 @@ export interface DeckState { // 错误状态 error: string | null; + + // 打印状态 + isPrinting: boolean; } export interface DeckActions { @@ -94,6 +97,10 @@ export interface DeckActions { // 生成代码 generateCode: () => string; copyCode: () => Promise; + + // 打印操作 + setPrinting: (printing: boolean) => void; + printDeck: () => void; } export interface DeckStore { @@ -128,7 +135,8 @@ export function createDeckStore( selectStart: null, selectEnd: null, isLoading: false, - error: null + error: null, + isPrinting: false }); // 更新尺寸并重新计算 dimensions @@ -265,6 +273,12 @@ export function createDeckStore( } }; + const setPrinting = (printing: boolean) => setState({ isPrinting: printing }); + + const printDeck = () => { + setState({ isPrinting: true }); + }; + const actions: DeckActions = { setSizeW, setSizeH, @@ -290,7 +304,9 @@ export function createDeckStore( setError, clearError, generateCode, - copyCode + copyCode, + setPrinting, + printDeck }; return { state, actions }; diff --git a/src/components/md-deck/index.tsx b/src/components/md-deck/index.tsx index 04932b7..4551c70 100644 --- a/src/components/md-deck/index.tsx +++ b/src/components/md-deck/index.tsx @@ -4,6 +4,7 @@ import { resolvePath } from '../utils/path'; import { createDeckStore } from './hooks/deckStore'; import { DeckHeader } from './DeckHeader'; import { DeckContent } from './DeckContent'; +import { PrintPreview } from './PrintPreview'; import {DataEditorPanel, LayerEditorPanel, PropertiesEditorPanel} from './editor-panel'; interface DeckProps { @@ -102,6 +103,15 @@ customElement('md-deck', { return (
+ {/* 打印预览弹窗 */} + + store.actions.setPrinting(false)} + onPrint={() => window.print()} + /> + + {/* Tab 选择器和编辑按钮 */} 0 && !store.state.error}> diff --git a/src/styles.css b/src/styles.css index 3571db7..b585f83 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,2 +1,20 @@ @import "tailwindcss"; @plugin "@tailwindcss/typography"; + +/* 打印样式 */ +@media print { + body { + visibility: hidden; + } + + body .in-print { + visibility: visible; + -webkit-print-color-adjust: exact !important; + print-color-adjust: exact !important; + } + + @page { + size: A4; + margin: 0; + } +} \ No newline at end of file