feat: visor PDF inline con iframe colapsable + botón descarga
This commit is contained in:
@@ -1040,6 +1040,58 @@ body {
|
||||
color: var(--color-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
.pdf-viewer-wrap {
|
||||
margin: 1rem 0;
|
||||
border: 1px solid #d5e8f0;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
background: #f8fbfd;
|
||||
}
|
||||
.pdf-viewer-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.7rem 1rem;
|
||||
background: linear-gradient(135deg, #eaf2f8, #d4e6f1);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
font-size: 0.9rem;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.pdf-viewer-header:hover {
|
||||
background: linear-gradient(135deg, #d4e6f1, #aed6f1);
|
||||
}
|
||||
.pdf-toggle {
|
||||
transition: transform 0.3s;
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.pdf-viewer-wrap.collapsed .pdf-toggle {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
.pdf-viewer {
|
||||
width: 100%;
|
||||
height: 70vh;
|
||||
border: none;
|
||||
display: block;
|
||||
}
|
||||
.pdf-viewer-wrap.collapsed .pdf-viewer,
|
||||
.pdf-viewer-wrap.collapsed .pdf-download-link {
|
||||
display: none;
|
||||
}
|
||||
.pdf-download-link {
|
||||
display: block;
|
||||
text-align: center;
|
||||
padding: 0.5rem;
|
||||
font-size: 0.82rem;
|
||||
color: var(--color-primary);
|
||||
text-decoration: none;
|
||||
background: #eaf2f8;
|
||||
border-top: 1px solid #d5e8f0;
|
||||
}
|
||||
.pdf-download-link:hover {
|
||||
background: #d4e6f1;
|
||||
}
|
||||
.nota-vacio {
|
||||
color: var(--color-text-muted);
|
||||
font-style: italic;
|
||||
@@ -1121,18 +1173,35 @@ body {
|
||||
|
||||
// Recurso descargable o enlace
|
||||
if (tema.recursoURL) {
|
||||
const icono = tema.recursoTexto === "Abrir Archivo" ? "📄" : "🔗";
|
||||
const etiqueta = tema.recursoTexto === "Abrir Archivo" ? "Archivo" : "Enlace";
|
||||
// Buscar archivo local descargado
|
||||
let recursoHref = tema.recursoURL;
|
||||
let recursoLocal = null;
|
||||
if (tema.recursoID) {
|
||||
const secDir = path.join(__dirname, "descargas", "secciones", modulo.carpeta, seccion.carpeta);
|
||||
const archivos = fs.existsSync(secDir) ? fs.readdirSync(secDir).filter(f => f.startsWith(`recurso_${tema.recursoID}.`)) : [];
|
||||
if (archivos.length > 0) {
|
||||
recursoHref = `secciones/${modulo.carpeta}/${seccion.carpeta}/${archivos[0]}`;
|
||||
recursoLocal = archivos[0];
|
||||
recursoHref = `secciones/${modulo.carpeta}/${seccion.carpeta}/${recursoLocal}`;
|
||||
}
|
||||
}
|
||||
bodyHTML += `<div class="nota-recurso">${icono} <strong>${etiqueta}:</strong> <a href="${recursoHref}" target="_blank">${tema.titulo}</a></div>\n`;
|
||||
|
||||
if (recursoLocal && recursoLocal.endsWith(".pdf")) {
|
||||
// PDF inline con visor embebido
|
||||
const pdfId = `pdf-${tema.id}-${tema.recursoID}`;
|
||||
bodyHTML += `<div class="pdf-viewer-wrap">
|
||||
<div class="pdf-viewer-header" onclick="this.parentElement.classList.toggle('collapsed')">
|
||||
<span>📄 <strong>${tema.titulo}</strong></span>
|
||||
<span class="pdf-toggle">▼</span>
|
||||
</div>
|
||||
<iframe class="pdf-viewer" src="${recursoHref}" id="${pdfId}"></iframe>
|
||||
<a href="${recursoHref}" download class="pdf-download-link">⬇ Descargar PDF</a>
|
||||
</div>\n`;
|
||||
} else {
|
||||
// Otros archivos (ZIP, BIN) o enlaces — link normal
|
||||
const icono = recursoLocal ? "📦" : "🔗";
|
||||
const etiqueta = recursoLocal ? "Descargar" : "Enlace";
|
||||
bodyHTML += `<div class="nota-recurso">${icono} <strong>${etiqueta}:</strong> <a href="${recursoHref}" target="_blank">${tema.titulo}</a></div>\n`;
|
||||
}
|
||||
}
|
||||
|
||||
// Separador entre temas
|
||||
|
||||
Reference in New Issue
Block a user