A implementação de um componente de upload de imagens é uma funcionalidade comum em aplicações web. Utilizando o componente el-upload do Element UI, é possível gerenciar o envio de arquivos, a reexibição de imagens existentes e a conversão de arquivos para o formato Base64. O fluxo geral envolve o upload automático das imagens (embora o upload manual seja uma opção), o recebimento do URL da imagem do servidor após um envio bem-sucedido e, finalmente, a inclusão desses URLs nos dados do formulário principal para submissão.
Estrutura HTML do Componente
O componente el-upload é altamente configurável. Abaixo está um exemplo básico de sua estrutura, com atribtuos essenciais para o upload de imagens:
<!-- Componente para upload de imagens -->
<div style="margin: 4px 0">Upload de Imagens (Apenas formatos JPG, PNG)</div>
<el-upload
class="image-uploader-wrapper"
:class="{ 'hide-add-button': hideAddImageButton }"
action="#"
list-type="picture-card"
:auto-upload="true"
:limit="3"
:http-request="handleCustomUpload"
:before-upload="validateImageForUpload"
:on-change="onFileUploadChange"
:on-success="onUploadSuccess"
:on-remove="onFileRemove"
:file-list="imageUploadList"
accept="image/jpeg,image/png"
>
<i slot="default" class="el-icon-plus"></i>
</el-upload>
Os principais atributos utilizados são:
action="#": Define a URL para onde o arquivo seria enviado. Usamos#aqui porque o upload é tratado por:http-request.list-type="picture-card": Exibe os arquivos como cartões de imagem.:auto-upload="true": Inicia o upload automaticamente após a seleção do arquivo.:limit="3": Limita o número de arquivos que podem ser carregados a 3.:http-request="handleCustomUpload": Função personalizada para lidar com o processo de upload, substituindo o comportamento padrão do Element UI.:before-upload="validateImageForUpload": Função para validar o arquivo antes do upload.:on-change="onFileUploadChange": Disparado quando o estado do arquivo muda (selecionado, upload, removido).:on-success="onUploadSuccess": Chamado quando o upload é bem-sucedido.:on-remove="onFileRemove": Chamado quando um arquivo é removido da lista.:file-list="imageUploadList": Array de objetos que representa a lista atual de arquivos carregados, útil para reexibição.accept="image/jpeg,image/png": Restringe os tipos de arquivo aceitos.
Funções Auxiliares Essencaiis
1. Conversão de Arquivos para Base64
Para pré-visualizações ou outras necessidades, converter um arquivo de imagem para o formato Base64 pode ser útil. A função a seguir utiliza FileReader para realizar essa conversão de forma assíncrona:
// Converte um objeto File para uma string Base64
async fileToBase64(fileObject) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(fileObject); // Inicia a leitura do arquivo como URL de dados
reader.onload = () => {
resolve(reader.result); // Resolve a promessa com a string Base64
};
reader.onerror = (error) => {
reject(error); // Rejeita a promessa em caso de erro
};
});
},
2. Carregamento e Exibição de Imagens Existentes
Ao editar um registro que já possui imagens, é necessário pré-carregar essas imagens no componente de upload. O array vinculado a :file-list deve ser populado com os dados das imagens existentes, formatados de acordo com o esperado pelo Element UI (geralmente contendo name e url).
// Prepara a lista de arquivos para exibição no componente de upload
// `record.images` seria um array de objetos de imagem vindos do backend
loadImagesForEdit(record) {
this.imageUploadList = []; // Limpa a lista atual
if (record && record.images && record.images.length > 0) {
record.images.forEach((img, index) => {
// Cada item deve ter 'name' e 'url'. 'uid' é importante para o Element UI.
this.imageUploadList.push({
name: `existing-image-${index + 1}`, // Um nome único para a imagem
url: `/api/assets/images/${img.id}?path=${img.filename}`, // Adapte a URL conforme sua API e estrutura de dados
uid: Date.now() + index, // Um ID único temporário
});
});
}
},
Certifique-se de que a estrutura dos objetos no array imageUploadList seja compatível com o que o el-upload espera, geralmente name e url. O uid é importnate para o componente rastrear os itens de forma única.
3. Envio Personalizado de Arquivos com FormData
O atributo :http-request permite interceptar o processo de upload e enviar o arquivo de forma personalizada, por exemplo, usando FormData para uma requisição AJAX.
// Função para envio personalizado de arquivos via http-request
async handleCustomUpload(requestOptions) {
const fileToProcess = requestOptions.file;
const formDataPayload = new FormData();
formDataPayload.append('uploadFile', fileToProcess);
formDataPayload.append('fileSize', fileToProcess.size);
// Adicione outros dados necessários para o backend
// formDataPayload.append('projectId', this.currentProjectId);
try {
const apiResponse = await yourBackendService.uploadAsset(formDataPayload); // Chame sua API de upload
this.$message.success('Upload de imagem concluído!');
// O método onSuccess do Element UI precisa ser acionado para atualizar o estado do componente
requestOptions.onSuccess(apiResponse.data);
} catch (error) {
this.$message.error('Falha ao enviar a imagem.');
console.error('Erro no upload customizado:', error);
requestOptions.onError(error); // Aciona o erro no componente el-upload
}
},
Ganchos de Evento do Componente el-upload
1. before-upload: Validação Pré-Upload
Esta função é executada antes de cada upload e permite validar o arquivo (tipo, tamanho, etc.). Retorne false para abortar o upload.
// Validação de arquivo antes do upload
validateImageForUpload(file) {
const acceptedMimeTypes = ['image/png', 'image/jpeg'];
const isAllowedType = acceptedMimeTypes.includes(file.type);
const isSizeUnder2MB = file.size / 1024 / 1024 < 2; // Verifica se o tamanho é menor que 2MB
if (!isAllowedType) {
this.$message.error('Tipo de arquivo inválido! Apenas JPG e PNG são aceitos.');
}
if (!isSizeUnder2MB) {
this.$message.error('O tamanho da imagem não pode ser superior a 2MB.');
}
return isAllowedType && isSizeUnder2MB;
},
2. on-change: Resposta à Mudança no Estado do Arquivo
Disparado sempre que o estado de um arquivo na lista muda (adicionado, removido, upload iniciado/concluído). É útil para atualizar a interface, como esconder o botão de upload quando o limite é atingido.
// Gerencia mudanças na lista de arquivos
onFileUploadChange(file, updatedFileList) {
this.imageUploadList = updatedFileList; // Mantém a lista interna sincronizada
this.hideAddImageButton = updatedFileList.length >= this.maxUploadLimit; // Atualiza a visibilidade do botão
// Lógicas adicionais, como pré-visualização instantânea, podem ser inseridas aqui.
},
3. on-success: Upload Bem-sucedido
Chamado quando um arquivo é carregado com sucesso. O argumento updatedFileList contém a lista atualizada de arquivos.
// Lógica após um upload bem-sucedido
onUploadSuccess(response, file, updatedFileList) {
this.imageUploadList = updatedFileList; // Garante que a lista interna esteja atualizada
this.$message.success(`A imagem '${file.name}' foi carregada com sucesso.`);
// Aqui você pode processar a resposta do servidor, como salvar a URL final da imagem em um array de URLs para o formulário.
},
4. on-remove: Arquivo Removido
Executado quando um arquivo é removido da lista. O updatedFileList contém a lista de arquivos após a remoção.
// Lógica após a remoção de um arquivo
onFileRemove(removedFile, updatedFileList) {
this.imageUploadList = updatedFileList; // Sincroniza a lista interna
this.hideAddImageButton = updatedFileList.length >= this.maxUploadLimit; // Reavalia a visibilidade do botão
this.$message.warning(`A imagem '${removedFile.name}' foi removida.`);
// Se necessário, envie uma requisição ao backend para remover a imagem correspondente do servidor.
},
Estilização: Ocultando o Botão de Adição
Para ocultar o botão "Adicionar imagem" (o ícone de '+') quando o número máximo de uploads for atingido, podemos usar uma classe dinâmica e CSS. O componente el-upload gera um elemento que pode ser estilizado.
<el-upload
...
:class="{ 'hide-add-button': hideAddImageButton }"
>
<i slot="default" class="el-icon-plus"></i>
</el-upload>
No CSS, a classe .hide-add-button pode ser usada para direcionar o elemento do botão de upload. É importante notar que, dependendo da configuração do seu projeto Vue (especialmente ao usar scoped styles), pode ser necessário usar seletores de profundidade como /deep/, ::v-deep (Vue 2) ou :deep() (Vue 3).
/* Estilo para ocultar o botão de upload quando o limite for atingido */
.hide-add-button .el-upload--picture-card {
display: none;
}
/* Exemplo para Vue 2 com scoped styles: */
/*
.image-uploader-wrapper ::v-deep .el-upload--picture-card {
display: none;
}
*/
Certifique-se de que a propriedade hideAddImageButton (ou o nome que você escolher para sua variável reativa) seja atualizada corretamente nos ganchos onFileUploadChange e onFileRemove, com base em this.imageUploadList.length e this.maxUploadLimit.