Files
printerCentral/app/components/templates/TemplateList.vue
josedario87 413ec6d27e feat: Agregar visualizador de preview para templates
- Nuevo composable usePreview.ts para procesar operaciones en líneas de preview
- Nuevo componente PaperSimulator.vue que simula el papel térmico
- Nuevo modal PreviewModal.vue para vista previa con edición inline de variables
- Botón "Vista previa" agregado a TemplateCard.vue
- Integración del modal en TemplateList.vue
2025-11-26 17:29:10 -06:00

159 lines
4.8 KiB
Vue

<script setup lang="ts">
import type { PrintTemplate } from '~/composables/useTemplates'
import type { Operation } from '~/composables/usePrintQueue'
import { resolveVariables } from '~/composables/useTemplates'
const templates = useTemplates()
const queue = usePrintQueue()
const toast = useToast()
// Estado para el drawer de variables
const variablesDrawerOpen = ref(false)
const selectedTemplate = ref<PrintTemplate | null>(null)
// Estado para el modal de preview
const previewModalOpen = ref(false)
const previewTemplate = ref<PrintTemplate | null>(null)
// Cargar templates al montar
onMounted(() => {
templates.fetchTemplates()
})
function loadTemplate(id: string) {
const template = templates.templates.value.find(t => t.id === id)
if (!template) return
// Si tiene variables, abrir el drawer
if (template.variables && template.variables.length > 0) {
selectedTemplate.value = template
variablesDrawerOpen.value = true
} else {
// Sin variables, cargar directo
const ops = templates.loadTemplate(id)
if (ops) {
queue.loadFromTemplate(ops)
toast.add({ title: 'Template cargado en la cola', color: 'success' })
}
}
}
function handleLoadWithVariables(values: Record<string, string>) {
if (!selectedTemplate.value) return
const ops = resolveVariables(selectedTemplate.value.operations, values)
queue.loadFromTemplate(ops)
toast.add({ title: 'Template cargado en la cola', color: 'success' })
variablesDrawerOpen.value = false
selectedTemplate.value = null
}
async function duplicateTemplate(id: string) {
const result = await templates.duplicateTemplate(id)
if (result) {
toast.add({ title: 'Template duplicado', color: 'success' })
} else {
toast.add({ title: 'Error al duplicar template', color: 'error' })
}
}
async function deleteTemplate(id: string) {
if (!confirm('¿Eliminar este template?')) return
const result = await templates.deleteTemplate(id)
if (result) {
toast.add({ title: 'Template eliminado', color: 'success' })
} else {
toast.add({ title: 'Error al eliminar template', color: 'error' })
}
}
// Abrir preview de un template
function openPreview(id: string) {
const template = templates.templates.value.find(t => t.id === id)
if (template) {
previewTemplate.value = template
previewModalOpen.value = true
}
}
// Imprimir desde el preview
async function handlePrintFromPreview(operations: Operation[], variables: Record<string, string>) {
// Resolver variables en las operaciones
const resolvedOps = resolveVariables(operations, variables)
// Enviar a imprimir directamente
try {
const result = await $fetch('/api/print', {
method: 'POST',
body: { operations: resolvedOps }
})
if (result.ok) {
toast.add({ title: 'Enviado a imprimir', color: 'success' })
} else {
toast.add({ title: result.error || 'Error al imprimir', color: 'error' })
}
} catch (error: any) {
toast.add({ title: error.message || 'Error al imprimir', color: 'error' })
}
previewModalOpen.value = false
previewTemplate.value = null
}
</script>
<template>
<div>
<div class="flex items-center justify-between mb-4">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">
Templates guardados
</h2>
<UBadge v-if="templates.templates.value.length > 0" variant="subtle">
{{ templates.templates.value.length }}
</UBadge>
</div>
<div v-if="templates.loading.value" class="flex justify-center py-12">
<UIcon name="i-heroicons-arrow-path" class="w-8 h-8 animate-spin text-gray-400" />
</div>
<div v-else-if="templates.templates.value.length === 0" class="text-center py-12">
<UIcon name="i-heroicons-document-duplicate" class="w-12 h-12 text-gray-400 dark:text-gray-600 mx-auto mb-3" />
<p class="text-gray-500 dark:text-gray-400 mb-2">
No hay templates guardados
</p>
<p class="text-sm text-gray-400 dark:text-gray-500">
Crea comandos en el constructor y guárdalos como template desde la cola
</p>
</div>
<div v-else class="grid gap-3 sm:grid-cols-2">
<TemplatesTemplateCard
v-for="template in templates.templates.value"
:key="template.id"
:template="template"
@load="loadTemplate"
@preview="openPreview"
@duplicate="duplicateTemplate"
@delete="deleteTemplate"
/>
</div>
<!-- Drawer para completar variables -->
<TemplatesVariablesDrawer
:template="selectedTemplate"
:open="variablesDrawerOpen"
@update:open="variablesDrawerOpen = $event"
@load="handleLoadWithVariables"
/>
<!-- Modal de vista previa -->
<PreviewModal
v-model="previewModalOpen"
:template="previewTemplate"
@print="handlePrintFromPreview"
/>
</div>
</template>