Cambios minimos al canvas
This commit is contained in:
parent
21f5d3a761
commit
b2095e4559
@ -6,6 +6,7 @@ import TiptapEditor from "./TiptapEditor.vue";
|
||||
const props = defineProps({
|
||||
element: { type: Object, required: true },
|
||||
isSelected: { type: Boolean, default: false },
|
||||
pageDimensions: { type: Object, required: true },
|
||||
});
|
||||
const emit = defineEmits([
|
||||
"select",
|
||||
@ -100,13 +101,19 @@ const handleMouseMove = (event) => {
|
||||
if (isDragging.value) {
|
||||
const deltaX = event.clientX - dragStart.value.mouseX;
|
||||
const deltaY = event.clientY - dragStart.value.mouseY;
|
||||
const newX = dragStart.value.elementX + deltaX;
|
||||
const newY = dragStart.value.elementY + deltaY;
|
||||
emit("move", {
|
||||
id: props.element.id,
|
||||
x: Math.max(0, newX),
|
||||
y: Math.max(0, newY),
|
||||
});
|
||||
let newX = dragStart.value.elementX + deltaX;
|
||||
let newY = dragStart.value.elementY + deltaY;
|
||||
|
||||
// Límites
|
||||
const pageW = props.pageDimensions.width;
|
||||
const pageH = props.pageDimensions.height;
|
||||
const elW = props.element.width;
|
||||
const elH = props.element.height;
|
||||
|
||||
newX = Math.max(0, Math.min(newX, pageW - elW));
|
||||
newY = Math.max(0, Math.min(newY, pageH - elH));
|
||||
|
||||
emit("move", { id: props.element.id, x: newX, y: newY });
|
||||
} else if (isResizing.value) {
|
||||
handleResizeMove(event);
|
||||
}
|
||||
@ -138,8 +145,19 @@ const handleResizeMove = (event) => {
|
||||
if (!isResizing.value) return;
|
||||
const deltaX = event.clientX - resizeStart.value.x;
|
||||
const deltaY = event.clientY - resizeStart.value.y;
|
||||
const newWidth = Math.max(100, resizeStart.value.width + deltaX);
|
||||
const newHeight = Math.max(40, resizeStart.value.height + deltaY);
|
||||
|
||||
// Límites
|
||||
const pageW = props.pageDimensions.width;
|
||||
const pageH = props.pageDimensions.height;
|
||||
const elX = props.element.x;
|
||||
const elY = props.element.y;
|
||||
|
||||
let newWidth = resizeStart.value.width + deltaX;
|
||||
let newHeight = resizeStart.value.height + deltaY;
|
||||
|
||||
newWidth = Math.max(100, Math.min(newWidth, pageW - elX));
|
||||
newHeight = Math.max(40, Math.min(newHeight, pageH - elY));
|
||||
|
||||
emit("update", { id: props.element.id, width: newWidth, height: newHeight });
|
||||
};
|
||||
|
||||
@ -159,6 +177,17 @@ const handleFileSelect = (event) => {
|
||||
}
|
||||
event.target.value = null;
|
||||
};
|
||||
|
||||
const textStyles = computed(() => {
|
||||
if (props.element.type !== "text") return {};
|
||||
|
||||
return {
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
overflow: "hidden",
|
||||
position: "relative",
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -181,13 +210,19 @@ const handleFileSelect = (event) => {
|
||||
class="absolute inset-0 z-10 cursor-move"
|
||||
/>
|
||||
|
||||
<TiptapEditor
|
||||
<div
|
||||
v-if="element.type === 'text'"
|
||||
:model-value="element.content"
|
||||
:editable="isEditing"
|
||||
@update:model-value="handleContentUpdate"
|
||||
@focus="handleEditorFocus"
|
||||
/>
|
||||
:style="textStyles"
|
||||
class="text-container"
|
||||
>
|
||||
<TiptapEditor
|
||||
class="w-full h-full overflow-auto"
|
||||
:model-value="element.content"
|
||||
:editable="isEditing"
|
||||
@update:model-value="handleContentUpdate"
|
||||
@focus="handleEditorFocus"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="element.type === 'image'"
|
||||
|
||||
@ -17,13 +17,11 @@ const pageSize = ref('A4');
|
||||
|
||||
const pageSizes = {
|
||||
'A4': { width: 794, height: 1123, label: 'A4 (210 x 297 mm)' },
|
||||
'Letter': { width: 816, height: 1056, label: 'Carta (216 x 279 mm)' },
|
||||
'A3': { width: 1123, height: 1587, label: 'A3 (297 x 420 mm)' },
|
||||
'Legal': { width: 816, height: 1344, label: 'Oficio (216 x 356 mm)' },
|
||||
'Tabloid': { width: 1056, height: 1632, label: 'Tabloide (279 x 432 mm)' }
|
||||
};
|
||||
|
||||
const ZOOM_LEVEL = 0.65;
|
||||
const ZOOM_LEVEL = 1.0;
|
||||
|
||||
/** Propiedades computadas */
|
||||
const currentPageSize = computed(() => pageSizes[pageSize.value] || pageSizes['A4']);
|
||||
@ -134,7 +132,7 @@ const deletePage = (pageIndex) => {
|
||||
class="absolute inset-0"
|
||||
:style="{ transform: `scale(${ZOOM_LEVEL})`, transformOrigin: 'top left' }"
|
||||
>
|
||||
<slot name="elements" :page="page" />
|
||||
<slot name="elements" :page="page" :dimensions="currentPageSize" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -52,37 +52,70 @@ defineExpose({ editor });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<EditorContent :editor="editor" />
|
||||
<div class="tiptap-wrapper">
|
||||
<EditorContent :editor="editor" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.tiptap-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
:deep(.ProseMirror) {
|
||||
padding: 0.5rem;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
box-sizing: border-box;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Prevenir que el texto se desborde horizontalmente */
|
||||
:deep(.ProseMirror p) {
|
||||
margin: 0 0 0.5em 0;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
hyphens: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Asegurar que spans respeten el ancho */
|
||||
:deep(.ProseMirror span) {
|
||||
display: inline;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Estilos para cuando NO es editable */
|
||||
:deep(.ProseMirror[contenteditable="false"]) {
|
||||
cursor: default;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Estilos para cuando SÍ es editable */
|
||||
:deep(.ProseMirror[contenteditable="true"]:focus) {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
/* Reglas generales que ya teníamos */
|
||||
:deep(.ProseMirror span[style]) {
|
||||
display: inline !important;
|
||||
}
|
||||
:deep(.ProseMirror p) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Controlar listas */
|
||||
:deep(.ProseMirror ul),
|
||||
:deep(.ProseMirror ol) {
|
||||
padding-left: 1.5rem;
|
||||
margin: 0 0 0.5em 0;
|
||||
}
|
||||
|
||||
/* Alineación de texto */
|
||||
:deep(p[style*="text-align: center"]) {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@ -126,30 +126,46 @@ const handleKeydown = (event) => {
|
||||
onMounted(() => document.addEventListener("keydown", handleKeydown));
|
||||
onBeforeUnmount(() => document.removeEventListener("keydown", handleKeydown));
|
||||
|
||||
const PDF_CANVAS_SIZES = {
|
||||
A4: { width: 794, height: 1123 },
|
||||
A3: { width: 1123, height: 1587 },
|
||||
Tabloid: { width: 1056, height: 1632 },
|
||||
};
|
||||
|
||||
/* Tamaños en puntos */
|
||||
const PDF_POINTS = {
|
||||
A4: { width: 580.00, height: 841.89 },
|
||||
A3: { width: 841.89, height: 1190.55 },
|
||||
Tabloid: { width: 792, height: 1224 },
|
||||
};
|
||||
|
||||
const exportPDF = () => {
|
||||
isExporting.value = true;
|
||||
try {
|
||||
const content = [];
|
||||
|
||||
// calcular scale entre canvas-units y pdf-points
|
||||
const canvasSize = PDF_CANVAS_SIZES[currentPageSize.value] || PDF_CANVAS_SIZES.A4;
|
||||
const pdfSize = PDF_POINTS[currentPageSize.value] || PDF_POINTS.A4;
|
||||
const scale = pdfSize.width / canvasSize.width;
|
||||
|
||||
pages.value.forEach((page, pageIndex) => {
|
||||
const pageContent = page.elements
|
||||
.map((element) => {
|
||||
const position = { x: element.x * 0.75, y: element.y * 0.75 };
|
||||
// convertir posición y dimensiones usando 'scale'
|
||||
const position = { x: element.x * scale, y: element.y * scale };
|
||||
|
||||
if (element.type === "text" && element.content) {
|
||||
return {
|
||||
columns: [
|
||||
{
|
||||
stack: htmlToPdfMake(element.content),
|
||||
width: (element.width || 250) * 0.75,
|
||||
},
|
||||
],
|
||||
stack: htmlToPdfMake(element.content),
|
||||
width: (element.width || 250) * scale,
|
||||
absolutePosition: position,
|
||||
};
|
||||
}
|
||||
if (element.type === "image" && element.content) {
|
||||
return {
|
||||
image: element.content,
|
||||
width: (element.width || 150) * 0.75,
|
||||
width: (element.width || 150) * scale,
|
||||
absolutePosition: position,
|
||||
};
|
||||
}
|
||||
@ -163,19 +179,15 @@ const exportPDF = () => {
|
||||
})
|
||||
);
|
||||
const numCols = element.content.data[0]?.length || 1;
|
||||
const colWidth = (element.width * 0.75) / numCols;
|
||||
const colWidth = ((element.width || 400) * scale) / numCols;
|
||||
const widths = Array(numCols).fill(colWidth);
|
||||
|
||||
return {
|
||||
columns: [
|
||||
{
|
||||
table: {
|
||||
body: body,
|
||||
widths: widths,
|
||||
},
|
||||
width: element.width * 0.75,
|
||||
},
|
||||
],
|
||||
table: {
|
||||
body: body,
|
||||
widths: widths,
|
||||
},
|
||||
width: (element.width || 400) * scale,
|
||||
absolutePosition: position,
|
||||
layout: {
|
||||
hLineWidth: () => 0.5,
|
||||
@ -200,12 +212,15 @@ const exportPDF = () => {
|
||||
const docDefinition = {
|
||||
content,
|
||||
pageSize: currentPageSize.value.toUpperCase(),
|
||||
pageMargins: [0, 0, 0, 0],
|
||||
pageMargins: [40, 40, 40, 40],
|
||||
styles: {
|
||||
p: {
|
||||
margin: [0, 0, 0, 0],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
window.pdfMake
|
||||
.createPdf(docDefinition)
|
||||
.download(`${documentTitle.value || "documento"}.pdf`);
|
||||
window.pdfMake.createPdf(docDefinition).download(`${documentTitle.value || "documento"}.pdf`);
|
||||
} catch (e) {
|
||||
console.error("Error al exportar PDF:", e);
|
||||
alert("Hubo un error al generar el PDF.");
|
||||
@ -305,7 +320,7 @@ const availableElements = [
|
||||
@click="deselectAll"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #elements="{ page }">
|
||||
<template #elements="{ page, dimensions }">
|
||||
<CanvasElement
|
||||
v-for="element in page.elements"
|
||||
:key="element.id"
|
||||
@ -316,6 +331,7 @@ const availableElements = [
|
||||
"
|
||||
:element="element"
|
||||
:is-selected="selectedElementId === element.id"
|
||||
:page-dimensions="dimensions"
|
||||
@select="selectElement"
|
||||
@delete="deleteElement"
|
||||
@update="updateElement"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user