feat: Agregar exportación PDF del formulario de catación EVC-IH01
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 2m51s
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 2m51s
- Implementar módulo de generación PDF con jsPDF - Crear composable usePdfExport para exportar muestras y sesiones - Añadir botones de exportación PDF en header y por muestra - Replicar layout exacto del formulario físico IHCAFE - Soportar máximo 3 formularios por hoja carta
This commit is contained in:
223
nuxt4/app/utils/pdf/pdfLayout.ts
Normal file
223
nuxt4/app/utils/pdf/pdfLayout.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* Constantes y configuración del layout para el formulario PDF EVC-IH01
|
||||
* Tamaño: Carta (Letter) - 8.5" x 11" (215.9mm x 279.4mm)
|
||||
* Máximo 3 formularios por página
|
||||
*/
|
||||
|
||||
export const PDF_CONFIG = {
|
||||
// Página Carta (Letter)
|
||||
pageWidth: 215.9, // mm (8.5")
|
||||
pageHeight: 279.4, // mm (11")
|
||||
|
||||
// Márgenes
|
||||
marginTop: 8,
|
||||
marginBottom: 8,
|
||||
marginLeft: 8,
|
||||
marginRight: 8,
|
||||
|
||||
// Formulario individual (3 por página)
|
||||
formHeight: 85, // ~85mm cada formulario
|
||||
formWidth: 199.9, // pageWidth - marginLeft - marginRight
|
||||
formSpacing: 2, // Espacio entre formularios
|
||||
|
||||
// Tipografía
|
||||
fontSize: {
|
||||
title: 9,
|
||||
header: 7,
|
||||
body: 6,
|
||||
small: 5,
|
||||
tiny: 4.5,
|
||||
},
|
||||
|
||||
// Colores
|
||||
colors: {
|
||||
black: '#000000',
|
||||
gray: '#666666',
|
||||
lightGray: '#CCCCCC',
|
||||
},
|
||||
|
||||
// Grosor de líneas
|
||||
lineWidth: {
|
||||
thin: 0.1,
|
||||
normal: 0.2,
|
||||
thick: 0.4,
|
||||
},
|
||||
|
||||
// Tamaño de checkbox
|
||||
checkboxSize: 2.5,
|
||||
checkboxSpacing: 0.8,
|
||||
} as const
|
||||
|
||||
/**
|
||||
* Calcula la posición Y inicial para un formulario según su posición en la página
|
||||
* @param posicion - Posición del formulario (0, 1, o 2)
|
||||
* @returns Coordenada Y inicial en mm
|
||||
*/
|
||||
export function calcularYInicio(posicion: 0 | 1 | 2): number {
|
||||
const { marginTop, formHeight, formSpacing } = PDF_CONFIG
|
||||
return marginTop + posicion * (formHeight + formSpacing)
|
||||
}
|
||||
|
||||
/**
|
||||
* Secciones del formulario con sus posiciones relativas
|
||||
*/
|
||||
export const FORM_SECTIONS = {
|
||||
// Encabezado
|
||||
header: {
|
||||
y: 0,
|
||||
height: 10,
|
||||
},
|
||||
// Tabla de intensidades (lado izquierdo)
|
||||
intensidades: {
|
||||
x: 0,
|
||||
y: 10,
|
||||
width: 48,
|
||||
height: 32,
|
||||
},
|
||||
// Fragancia-Aroma (centro-derecha superior)
|
||||
fraganciaAroma: {
|
||||
x: 48,
|
||||
y: 10,
|
||||
width: 76,
|
||||
height: 18,
|
||||
},
|
||||
// Notas Fragancia-Aroma (derecha superior)
|
||||
notasFragancia: {
|
||||
x: 124,
|
||||
y: 10,
|
||||
width: 75.9,
|
||||
height: 18,
|
||||
},
|
||||
// Sabores (centro-derecha medio)
|
||||
sabores: {
|
||||
x: 48,
|
||||
y: 28,
|
||||
width: 76,
|
||||
height: 14,
|
||||
},
|
||||
// Notas Sabores (derecha medio)
|
||||
notasSabor: {
|
||||
x: 124,
|
||||
y: 28,
|
||||
width: 75.9,
|
||||
height: 14,
|
||||
},
|
||||
// Sensación en boca y gustos
|
||||
sensacionGustos: {
|
||||
x: 48,
|
||||
y: 42,
|
||||
width: 151.9,
|
||||
height: 12,
|
||||
},
|
||||
// Tazas y defectos
|
||||
tazasDefectos: {
|
||||
x: 0,
|
||||
y: 54,
|
||||
width: 199.9,
|
||||
height: 12,
|
||||
},
|
||||
// Otras notas
|
||||
otrasNotas: {
|
||||
x: 0,
|
||||
y: 66,
|
||||
width: 199.9,
|
||||
height: 8,
|
||||
},
|
||||
// Sub total
|
||||
subTotal: {
|
||||
x: 0,
|
||||
y: 74,
|
||||
width: 199.9,
|
||||
height: 10,
|
||||
},
|
||||
} as const
|
||||
|
||||
/**
|
||||
* Nombres de los parámetros de intensidad para la tabla
|
||||
*/
|
||||
export const PARAMETROS_INTENSIDAD = [
|
||||
{ key: 'fragancia', label: 'Fragancia' },
|
||||
{ key: 'aroma', label: 'Aroma' },
|
||||
{ key: 'sabor', label: 'Sabor' },
|
||||
{ key: 'saborResidual', label: 'Sabor Residual' },
|
||||
{ key: 'acidez', label: 'Acidez' },
|
||||
{ key: 'dulzor', label: 'Dulzor' },
|
||||
{ key: 'sensacionBoca', label: 'Sensación Boca' },
|
||||
{ key: 'impresionGlobal', label: 'Impresión Global' },
|
||||
] as const
|
||||
|
||||
/**
|
||||
* Tipo para categoría de nota en PDF
|
||||
*/
|
||||
export interface CategoriaPdf {
|
||||
key: string
|
||||
label: string
|
||||
indent?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Categorías de notas para el formulario PDF
|
||||
* Columna izquierda y derecha de checkboxes
|
||||
*/
|
||||
export const CATEGORIAS_PDF: {
|
||||
columnaIzquierda: CategoriaPdf[]
|
||||
columnaDerecha: CategoriaPdf[]
|
||||
} = {
|
||||
columnaIzquierda: [
|
||||
{ key: 'Floral', label: 'Floral' },
|
||||
{ key: 'Afrutado', label: 'Afrutado' },
|
||||
{ key: 'Bayas', label: 'Bayas', indent: true },
|
||||
{ key: 'Ácido', label: 'Ácido', indent: true },
|
||||
{ key: 'Frutas Deshidratadas', label: 'Frutas Deshid.', indent: true },
|
||||
{ key: 'Fermentado', label: 'Fermentado', indent: true },
|
||||
{ key: 'Cítricos', label: 'Cítricos' },
|
||||
{ key: 'Verde Vegetal', label: 'Verde/Vegetal' },
|
||||
{ key: 'Químico', label: 'Químico' },
|
||||
{ key: 'Humedad/Tierra', label: 'Humedad/Tierra' },
|
||||
{ key: 'Madera', label: 'Madera' },
|
||||
],
|
||||
columnaDerecha: [
|
||||
{ key: 'Tostado', label: 'Tostado' },
|
||||
{ key: 'Cereal', label: 'Cereal', indent: true },
|
||||
{ key: 'Quemado', label: 'Quemado', indent: true },
|
||||
{ key: 'Tabaco', label: 'Tabaco', indent: true },
|
||||
{ key: 'Nueces/Cacao', label: 'Nueces/Cacao' },
|
||||
{ key: 'Nueces', label: 'Nueces', indent: true },
|
||||
{ key: 'Cacao', label: 'Cacao', indent: true },
|
||||
{ key: 'Especias', label: 'Especias' },
|
||||
{ key: 'Dulce', label: 'Dulce' },
|
||||
{ key: 'Vainilla', label: 'Vainilla', indent: true },
|
||||
{ key: 'Azúcar Morena', label: 'Azúcar Morena', indent: true },
|
||||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* Sensaciones en boca para el formulario PDF
|
||||
*/
|
||||
export const SENSACIONES_PDF = [
|
||||
{ key: 'Áspero', label: 'Áspero (Arenoso, Rugoso, Rasposo)' },
|
||||
{ key: 'Aceitoso', label: 'Aceitoso' },
|
||||
{ key: 'Metálico', label: 'Metálico' },
|
||||
{ key: 'Suave', label: 'Suave (Aterciopelado, Sedoso)' },
|
||||
{ key: 'Deja seca la boca', label: 'Deja seca la boca (Astringente)' },
|
||||
] as const
|
||||
|
||||
/**
|
||||
* Gustos predominantes para el formulario PDF
|
||||
*/
|
||||
export const GUSTOS_PDF = [
|
||||
{ key: 'Salado', label: 'Salado' },
|
||||
{ key: 'Amargo', label: 'Amargo' },
|
||||
{ key: 'Ácido', label: 'Ácido' },
|
||||
{ key: 'Dulce', label: 'Dulce' },
|
||||
{ key: 'Umami', label: 'Umami' },
|
||||
] as const
|
||||
|
||||
/**
|
||||
* Defectos para el formulario PDF
|
||||
*/
|
||||
export const DEFECTOS_PDF = [
|
||||
{ key: 'Mohoso', label: 'Mohoso' },
|
||||
{ key: 'Fenólico', label: 'Fenólico' },
|
||||
{ key: 'Papa', label: 'Papa' },
|
||||
] as const
|
||||
Reference in New Issue
Block a user