refactor: svg page
This commit is contained in:
parent
1845576b18
commit
ec5a00fa94
|
|
@ -179,6 +179,56 @@ export function PrintPreview(props: PrintPreviewProps) {
|
||||||
|
|
||||||
const visibleLayers = createMemo(() => store.state.layerConfigs.filter((l) => l.visible));
|
const visibleLayers = createMemo(() => store.state.layerConfigs.filter((l) => l.visible));
|
||||||
|
|
||||||
|
// 渲染单个卡片的 SVG 内容(使用 foreignObject)
|
||||||
|
const renderCardInSvg = (card: { data: typeof store.state.cards[0]; x: number; y: number }, pageIndex: number) => {
|
||||||
|
const cardWidth = store.state.dimensions?.cardWidth || 56;
|
||||||
|
const cardHeight = store.state.dimensions?.cardHeight || 88;
|
||||||
|
const gridOriginX = store.state.dimensions?.gridOriginX || 0;
|
||||||
|
const gridOriginY = store.state.dimensions?.gridOriginY || 0;
|
||||||
|
const gridAreaWidth = store.state.dimensions?.gridAreaWidth || cardWidth;
|
||||||
|
const gridAreaHeight = store.state.dimensions?.gridAreaHeight || cardHeight;
|
||||||
|
const fontSize = store.state.dimensions?.fontSize || 3;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g class="card-group">
|
||||||
|
<foreignObject
|
||||||
|
x={`${card.x}mm`}
|
||||||
|
y={`${card.y}mm`}
|
||||||
|
width={`${cardWidth}mm`}
|
||||||
|
height={`${cardHeight}mm`}
|
||||||
|
>
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml" class="w-full h-full bg-white">
|
||||||
|
{/* 网格区域容器 */}
|
||||||
|
<div
|
||||||
|
class="absolute"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
left: `${gridOriginX}mm`,
|
||||||
|
top: `${gridOriginY}mm`,
|
||||||
|
width: `${gridAreaWidth}mm`,
|
||||||
|
height: `${gridAreaHeight}mm`
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* 渲染每个 layer */}
|
||||||
|
<For each={visibleLayers()}>
|
||||||
|
{(layer) => (
|
||||||
|
<div
|
||||||
|
class="absolute flex items-center justify-center text-center prose prose-sm"
|
||||||
|
style={{
|
||||||
|
...getLayerStyle(layer, store.state.dimensions!),
|
||||||
|
'font-size': `${fontSize}mm`
|
||||||
|
}}
|
||||||
|
innerHTML={renderLayerContent(layer, card.data)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</foreignObject>
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="fixed inset-0 bg-black/50 z-50 overflow-auto print:overflow-visible print:absolute">
|
<div class="fixed inset-0 bg-black/50 z-50 overflow-auto print:overflow-visible print:absolute">
|
||||||
{/* 打印样式:根据方向设置 @page 规则 */}
|
{/* 打印样式:根据方向设置 @page 规则 */}
|
||||||
|
|
@ -268,125 +318,87 @@ export function PrintPreview(props: PrintPreviewProps) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* A4 纸张预览 */}
|
{/* A4 纸张预览:每页都是一个完整的 SVG */}
|
||||||
<div class="flex flex-col items-center gap-8 print-root">
|
<div class="flex flex-col items-center gap-8 print-root">
|
||||||
<For each={pages()}>
|
<For each={pages()}>
|
||||||
{(page) => (
|
{(page) => (
|
||||||
<div
|
<svg
|
||||||
class="bg-white shadow-xl print:shadow-none print:w-full"
|
class="bg-white shadow-xl print:shadow-none"
|
||||||
|
viewBox={`0 0 ${getA4Size().width}mm ${getA4Size().height}mm`}
|
||||||
style={{
|
style={{
|
||||||
width: `${getA4Size().width}mm`,
|
width: `${getA4Size().width}mm`,
|
||||||
height: `${getA4Size().height}mm`
|
height: `${getA4Size().height}mm`
|
||||||
}}
|
}}
|
||||||
data-page={page.pageIndex + 1}
|
data-page={page.pageIndex + 1}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
|
{/* 外围边框:黑色 0.2mm */}
|
||||||
|
<rect
|
||||||
|
x={`${cropMarks()[page.pageIndex]?.frameBoundsWithMargin.x}mm`}
|
||||||
|
y={`${cropMarks()[page.pageIndex]?.frameBoundsWithMargin.y}mm`}
|
||||||
|
width={`${cropMarks()[page.pageIndex]?.frameBoundsWithMargin.width}mm`}
|
||||||
|
height={`${cropMarks()[page.pageIndex]?.frameBoundsWithMargin.height}mm`}
|
||||||
|
fill="none"
|
||||||
|
stroke="black"
|
||||||
|
stroke-width="0.2"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 水平裁切线 */}
|
||||||
|
<For each={cropMarks()[page.pageIndex]?.horizontalLines}>
|
||||||
|
{(line) => (
|
||||||
|
<>
|
||||||
|
{/* 左侧裁切线(外围框外部) */}
|
||||||
|
<line
|
||||||
|
x1={`${line.xStart}mm`}
|
||||||
|
y1={`${line.y}mm`}
|
||||||
|
x2={`${page.frameBounds.minX}mm`}
|
||||||
|
y2={`${line.y}mm`}
|
||||||
|
stroke="#888"
|
||||||
|
stroke-width="0.1"
|
||||||
|
/>
|
||||||
|
{/* 右侧裁切线(外围框外部) */}
|
||||||
|
<line
|
||||||
|
x1={`${page.frameBounds.maxX}mm`}
|
||||||
|
y1={`${line.y}mm`}
|
||||||
|
x2={`${line.xEnd}mm`}
|
||||||
|
y2={`${line.y}mm`}
|
||||||
|
stroke="#888"
|
||||||
|
stroke-width="0.1"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
{/* 垂直裁切线 */}
|
||||||
|
<For each={cropMarks()[page.pageIndex]?.verticalLines}>
|
||||||
|
{(line) => (
|
||||||
|
<>
|
||||||
|
{/* 上方裁切线(外围框外部) */}
|
||||||
|
<line
|
||||||
|
x1={`${line.x}mm`}
|
||||||
|
y1={`${line.yStart}mm`}
|
||||||
|
x2={`${line.x}mm`}
|
||||||
|
y2={`${page.frameBounds.minY}mm`}
|
||||||
|
stroke="#888"
|
||||||
|
stroke-width="0.1"
|
||||||
|
/>
|
||||||
|
{/* 下方裁切线(外围框外部) */}
|
||||||
|
<line
|
||||||
|
x1={`${line.x}mm`}
|
||||||
|
y1={`${page.frameBounds.maxY}mm`}
|
||||||
|
x2={`${line.x}mm`}
|
||||||
|
y2={`${line.yEnd}mm`}
|
||||||
|
stroke="#888"
|
||||||
|
stroke-width="0.1"
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
|
||||||
{/* 渲染该页的所有卡牌 */}
|
{/* 渲染该页的所有卡牌 */}
|
||||||
<div class="relative w-full h-full">
|
<For each={page.cards}>
|
||||||
{/* 裁切线和外围框层 */}
|
{(card) => renderCardInSvg(card, page.pageIndex)}
|
||||||
<svg class="absolute inset-0 w-full h-full pointer-events-none" style={{ overflow: 'visible' }}>
|
</For>
|
||||||
{/* 外围边框:黑色 0.2mm */}
|
</svg>
|
||||||
<rect
|
|
||||||
x={`${cropMarks()[page.pageIndex]?.frameBoundsWithMargin.x}mm`}
|
|
||||||
y={`${cropMarks()[page.pageIndex]?.frameBoundsWithMargin.y}mm`}
|
|
||||||
width={`${cropMarks()[page.pageIndex]?.frameBoundsWithMargin.width}mm`}
|
|
||||||
height={`${cropMarks()[page.pageIndex]?.frameBoundsWithMargin.height}mm`}
|
|
||||||
fill="none"
|
|
||||||
stroke="black"
|
|
||||||
stroke-width="0.2"
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* 水平裁切线 */}
|
|
||||||
<For each={cropMarks()[page.pageIndex]?.horizontalLines}>
|
|
||||||
{(line) => (
|
|
||||||
<>
|
|
||||||
{/* 左侧裁切线(外围框外部) */}
|
|
||||||
<line
|
|
||||||
x1={`${line.xStart}mm`}
|
|
||||||
y1={`${line.y}mm`}
|
|
||||||
x2={`${page.frameBounds.minX}mm`}
|
|
||||||
y2={`${line.y}mm`}
|
|
||||||
stroke="#888"
|
|
||||||
stroke-width="0.1"
|
|
||||||
/>
|
|
||||||
{/* 右侧裁切线(外围框外部) */}
|
|
||||||
<line
|
|
||||||
x1={`${page.frameBounds.maxX}mm`}
|
|
||||||
y1={`${line.y}mm`}
|
|
||||||
x2={`${line.xEnd}mm`}
|
|
||||||
y2={`${line.y}mm`}
|
|
||||||
stroke="#888"
|
|
||||||
stroke-width="0.1"
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
{/* 垂直裁切线 */}
|
|
||||||
<For each={cropMarks()[page.pageIndex]?.verticalLines}>
|
|
||||||
{(line) => (
|
|
||||||
<>
|
|
||||||
{/* 上方裁切线(外围框外部) */}
|
|
||||||
<line
|
|
||||||
x1={`${line.x}mm`}
|
|
||||||
y1={`${line.yStart}mm`}
|
|
||||||
x2={`${line.x}mm`}
|
|
||||||
y2={`${page.frameBounds.minY}mm`}
|
|
||||||
stroke="#888"
|
|
||||||
stroke-width="0.1"
|
|
||||||
/>
|
|
||||||
{/* 下方裁切线(外围框外部) */}
|
|
||||||
<line
|
|
||||||
x1={`${line.x}mm`}
|
|
||||||
y1={`${page.frameBounds.maxY}mm`}
|
|
||||||
x2={`${line.x}mm`}
|
|
||||||
y2={`${line.yEnd}mm`}
|
|
||||||
stroke="#888"
|
|
||||||
stroke-width="0.1"
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<For each={page.cards}>
|
|
||||||
{(card) => (
|
|
||||||
<div
|
|
||||||
class="absolute bg-white"
|
|
||||||
style={{
|
|
||||||
left: `${card.x}mm`,
|
|
||||||
top: `${card.y}mm`,
|
|
||||||
width: `${store.state.dimensions?.cardWidth}mm`,
|
|
||||||
height: `${store.state.dimensions?.cardHeight}mm`
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* 网格区域容器 */}
|
|
||||||
<div
|
|
||||||
class="absolute"
|
|
||||||
style={{
|
|
||||||
left: `${store.state.dimensions?.gridOriginX}mm`,
|
|
||||||
top: `${store.state.dimensions?.gridOriginY}mm`,
|
|
||||||
width: `${store.state.dimensions?.gridAreaWidth}mm`,
|
|
||||||
height: `${store.state.dimensions?.gridAreaHeight}mm`
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* 渲染每个 layer */}
|
|
||||||
<For each={visibleLayers()}>
|
|
||||||
{(layer) => (
|
|
||||||
<div
|
|
||||||
class="absolute flex items-center justify-center text-center prose prose-sm"
|
|
||||||
style={{
|
|
||||||
...getLayerStyle(layer, store.state.dimensions!),
|
|
||||||
'font-size': `${store.state.dimensions?.fontSize}mm`
|
|
||||||
}}
|
|
||||||
innerHTML={renderLayerContent(layer, card.data)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue