Al trabajar con editores de texto enriquecido en aplicaciones web, surge frecuentemente la necesidad de permitir que los usuarios adjunten archivos como documentos PDF o Excel. Por defecto, el componente de enlace de Quill Editor no está diseñado para manejar metadatos de archivos de forma que se generen enlaces de descarga automáticos con el nombre original del archivo. A continuación, se detala cómo extender la funcionalidad de Quill para lograr este comportamiento.
1. Extensión del Blot de Enlace
Para que Quill reconozca un objeto de datos complejo (URL y nombre del archivo) en lugar de solo una cadena de texto, es necesario sobrescribir el formato de enlace predeterminado (Link Blot). Crearemos un archivo llamado CustomLink.js.
import Quill from "quill";
const InlineBlot = Quill.import("blots/inline");
class AttachmentLink extends InlineBlot {
static blotName = "link";
static tagName = "A";
static create(data) {
let node = super.create(data);
// Si la data es un objeto con propiedades específicas para adjuntos
if (typeof data === "object" && data.url) {
node.setAttribute("href", data.url);
node.setAttribute("download", data.fileName || "archivo");
node.innerText = data.fileName || data.url;
node.setAttribute("target", "_blank");
} else {
// Comportamiento estándar para enlaces normales
const sanitizedUrl = typeof data === "string" ? data : data.url;
node.setAttribute("href", sanitizedUrl);
node.setAttribute("rel", "noopener noreferrer");
node.setAttribute("target", "_blank");
}
return node;
}
static formats(domNode) {
return domNode.getAttribute("href");
}
format(name, value) {
if (name !== this.statics.blotName || !value) {
super.format(name, value);
} else {
this.domNode.setAttribute("href", value);
}
}
}
export default AttachmentLink;
Posteriormente, en el componente de Vue donde inicializas el editor, debes registrar este nuevo formato:
import Quill from "quill";
import AttachmentLink from "./CustomLink";
// El segundo parámetro 'true' sobrescribe el registro existente
Quill.register(AttachmentLink, true);
2. Configuración de la Barra de Herramientas y Handlers
Para permitir que el usuario active la carga de archivos, añadiremos un botón personalizado a la interfaz del editor. En este ejemplo, se utiliza un componente externo (como el-upload de Element UI) oculto que se dispara al hacer clic en el icono de la barra de herramientas.
data() {
return {
editorOptions: {
modules: {
toolbar: {
container: [
["bold", "italic", "underline"],
["link", "image"],
["attachment"] // Identificador de nuestro botón personalizado
],
handlers: {
attachment: function() {
// Simular clic en un input file oculto o componente de carga
document.querySelector(".file-uploader input").click();
}
}
}
}
}
};
},
mounted() {
this.setupCustomIcon();
},
methods: {
setupCustomIcon() {
// Inyectar icono personalizado en el botón de la barra de herramientas
const attachBtn = this.$el.querySelector(".ql-attachment");
if (attachBtn) {
attachBtn.innerHTML = '<i class="el-icon-paperclip"></i>';
}
}
}
3. Procesamiento de la Carga y Generación del Enlace
Una vez que el servidor procesa el archivo y devuelve la URL, debemos insertar el enlace en la posición actual del cursor. Es importante capturar la selección del editor antes de que pierda el foco durante la carga.
methods: {
onFileUploadSuccess(response) {
const quillInstance = this.$refs.myQuillEditor.quill;
if (response.status === "success") {
// Obtenemos la posición actual del cursor
const range = quillInstance.getSelection();
const index = range ? range.index : 0;
// Insertamos el formato 'link' usando nuestro objeto de datos personalizado
quillInstance.insertEmbed(index, "link", {
url: response.fileUrl,
fileName: response.originalName
});
// Movemos el cursor después del nuevo enlace
quillInstance.setSelection(index + response.originalName.length);
} else {
this.$message.error("Error al subir el archivo");
}
}
}
Con esta implementación, el editor generará un elemento HTML <a> que incluye el atributo download. Cuando el usuario final visualice el contenido generado, al hacer clic en el nombre del archivo, el navegador iniciará la descarga automáticamente en lugar de simplemente intentar navegar a la URL del recurso.