All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 36s
- Fijar ancho del papel a 33ch (referencia Font A) - Agregar transform-origin basado en alineación (left/center/right) - Reducir interlineado (line-height 1.2, margins ajustados) - Compensar altura visual de elementos con doble alto
271 lines
6.6 KiB
Vue
271 lines
6.6 KiB
Vue
<script setup lang="ts">
|
|
import type { PreviewLine, TextSegment, FontType } from '~/composables/usePreview'
|
|
import { CHAR_LIMITS } from '~/composables/usePreview'
|
|
|
|
const props = defineProps<{
|
|
lines: PreviewLine[]
|
|
font: FontType
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
'edit-variable': [variableName: string, currentValue: string]
|
|
}>()
|
|
|
|
// Ancho del papel FIJO basado en Font A (33ch) como referencia
|
|
// Font B se escala para caber en el mismo ancho físico
|
|
const paperWidth = computed(() => {
|
|
// Siempre usamos 33ch (Font A) como referencia para mantener alineaciones consistentes
|
|
return `calc(33ch + 80px)` // 40px padding cada lado para acomodar doble ancho
|
|
})
|
|
|
|
// Obtener clases CSS para el contenido de una línea (sin alineación, eso va en el padre)
|
|
function getLineClasses(line: PreviewLine): string[] {
|
|
const classes: string[] = []
|
|
|
|
if (line.bold) classes.push('font-bold')
|
|
if (line.underline) classes.push('underline')
|
|
if (line.exceedsLimit) classes.push('line-exceeds')
|
|
if (line.doubleWidth) classes.push('text-double-width')
|
|
if (line.doubleHeight) classes.push('text-double-height')
|
|
|
|
// Fuente (Font B es más angosta)
|
|
if (line.font === 'font_b') classes.push('font-b')
|
|
|
|
return classes
|
|
}
|
|
|
|
// Manejar click en variable
|
|
function handleVariableClick(segment: TextSegment) {
|
|
if (segment.type === 'variable' && segment.variableName) {
|
|
emit('edit-variable', segment.variableName, segment.content)
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
class="paper-simulator"
|
|
:style="{ width: paperWidth }"
|
|
>
|
|
<template v-for="(line, index) in lines" :key="index">
|
|
<!-- Línea vacía (feed) -->
|
|
<div v-if="line.isFeed" class="line-feed"> </div>
|
|
|
|
<!-- Línea con contenido -->
|
|
<div
|
|
v-else
|
|
class="line"
|
|
:class="'align-' + line.align"
|
|
>
|
|
<span class="line-content" :class="getLineClasses(line)">
|
|
<template v-for="(segment, segIndex) in line.segments" :key="segIndex">
|
|
<!-- Segmento de variable (clickeable) -->
|
|
<span
|
|
v-if="segment.type === 'variable'"
|
|
class="variable"
|
|
@click="handleVariableClick(segment)"
|
|
:title="`Variable: ${segment.variableName} (click para editar)`"
|
|
>{{ segment.content }}</span>
|
|
|
|
<!-- Segmento de texto normal -->
|
|
<span v-else>{{ segment.content }}</span>
|
|
</template>
|
|
</span>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Indicador de papel vacío -->
|
|
<div v-if="lines.length === 0" class="empty-paper">
|
|
<span class="text-gray-400">Vista previa vacía</span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
/* Contenedor del papel - simula papel térmico */
|
|
.paper-simulator {
|
|
font-family: 'Courier New', Courier, monospace;
|
|
font-size: 14px;
|
|
line-height: 1.2;
|
|
background: linear-gradient(to bottom, #fefefe 0%, #f8f8f8 100%);
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
padding: 20px 40px;
|
|
min-height: 200px;
|
|
max-height: 400px;
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
box-shadow:
|
|
0 2px 8px rgba(0, 0, 0, 0.1),
|
|
inset 0 0 20px rgba(0, 0, 0, 0.02);
|
|
/* Simular textura de papel */
|
|
background-image:
|
|
linear-gradient(to bottom, #fefefe 0%, #f8f8f8 100%),
|
|
repeating-linear-gradient(
|
|
0deg,
|
|
transparent,
|
|
transparent 1px,
|
|
rgba(0, 0, 0, 0.01) 1px,
|
|
rgba(0, 0, 0, 0.01) 2px
|
|
);
|
|
}
|
|
|
|
/* Cada línea del ticket - usa flexbox para alineación */
|
|
.line {
|
|
display: flex;
|
|
min-height: 1.2em;
|
|
color: #1a1a1a;
|
|
}
|
|
|
|
/* Alineación usando flexbox (permite que transforms funcionen correctamente) */
|
|
.align-left {
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
.align-center {
|
|
justify-content: center;
|
|
}
|
|
|
|
.align-right {
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
/* Contenido de la línea */
|
|
.line-content {
|
|
white-space: pre;
|
|
display: inline-block;
|
|
}
|
|
|
|
/* ========================================
|
|
TRANSFORMS BASE (sin considerar alineación)
|
|
======================================== */
|
|
|
|
/* Font A - Normal (sin transform) */
|
|
/* Font A es la referencia base, no necesita scaling */
|
|
|
|
/* Font A - Doble ancho */
|
|
.line-content.text-double-width {
|
|
transform: scaleX(2);
|
|
}
|
|
|
|
/* Font A - Doble alto */
|
|
.line-content.text-double-height {
|
|
transform: scaleY(2);
|
|
}
|
|
|
|
/* Font A - Doble ancho Y alto */
|
|
.line-content.text-double-width.text-double-height {
|
|
transform: scale(2, 2);
|
|
}
|
|
|
|
/* Font B - Normal (33/40 = 0.825 para caber en mismo ancho de papel) */
|
|
.line-content.font-b {
|
|
transform: scaleX(0.825);
|
|
}
|
|
|
|
/* Font B - Doble ancho (0.825 * 2 = 1.65) */
|
|
.line-content.font-b.text-double-width {
|
|
transform: scaleX(1.65);
|
|
}
|
|
|
|
/* Font B - Doble alto */
|
|
.line-content.font-b.text-double-height {
|
|
transform: scaleX(0.825) scaleY(2);
|
|
}
|
|
|
|
/* Font B - Doble ancho Y alto */
|
|
.line-content.font-b.text-double-width.text-double-height {
|
|
transform: scale(1.65, 2);
|
|
}
|
|
|
|
/* ========================================
|
|
TRANSFORM-ORIGIN SEGÚN ALINEACIÓN
|
|
Para que el punto de anclaje sea consistente
|
|
======================================== */
|
|
|
|
/* IZQUIERDA: el texto se ancla a la izquierda */
|
|
.align-left .line-content {
|
|
transform-origin: left top;
|
|
}
|
|
|
|
/* CENTRO: el texto se ancla al centro */
|
|
.align-center .line-content {
|
|
transform-origin: center top;
|
|
}
|
|
|
|
/* DERECHA: el texto se ancla a la derecha */
|
|
.align-right .line-content {
|
|
transform-origin: right top;
|
|
}
|
|
|
|
/* ========================================
|
|
COMPENSACIÓN DE ALTURA PARA DOBLE ALTO
|
|
El scaleY(2) expande visualmente pero no afecta layout,
|
|
agregamos margen para compensar el espacio extra
|
|
======================================== */
|
|
|
|
/* Doble alto ocupa 2x el espacio vertical */
|
|
.line-content.text-double-height {
|
|
margin-bottom: 0.6em; /* Espacio reducido para doble alto */
|
|
}
|
|
|
|
/* Línea vacía (feed) */
|
|
.line-feed {
|
|
height: 1.2em;
|
|
}
|
|
|
|
/* Línea que excede el límite */
|
|
.line-content.line-exceeds {
|
|
background: rgba(239, 68, 68, 0.15);
|
|
position: relative;
|
|
}
|
|
|
|
.line-content.line-exceeds::after {
|
|
content: '⚠';
|
|
position: absolute;
|
|
right: -20px;
|
|
color: #ef4444;
|
|
font-size: 12px;
|
|
}
|
|
|
|
/* Variable editable - fondo azul suave */
|
|
.variable {
|
|
background: rgba(59, 130, 246, 0.2);
|
|
cursor: pointer;
|
|
border-radius: 2px;
|
|
transition: background 0.15s ease;
|
|
}
|
|
|
|
.variable:hover {
|
|
background: rgba(59, 130, 246, 0.4);
|
|
}
|
|
|
|
/* Papel vacío */
|
|
.empty-paper {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100px;
|
|
font-style: italic;
|
|
}
|
|
|
|
/* Scrollbar estilizado */
|
|
.paper-simulator::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
|
|
.paper-simulator::-webkit-scrollbar-track {
|
|
background: #f1f1f1;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.paper-simulator::-webkit-scrollbar-thumb {
|
|
background: #ccc;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.paper-simulator::-webkit-scrollbar-thumb:hover {
|
|
background: #aaa;
|
|
}
|
|
</style>
|