Para implementar a importação de arquivos Excel em uma aplicação Vue.js 2, é necessário uitlizar a biblioteca xlsx. A seguir, apresenta-se uma abordagem completa para o frontend e backend.
1. Configuração do Frontend
Primeiramente, instale a dependência no projeto:
npm install xlsx
No componente Vue, utilize um componnete de upload para selecionar o arquivo:
<el-upload
style="display: inline-block"
action=""
accept=".xlsx, .xls"
:auto-upload="false"
:show-file-list="false"
:on-change="selecionarArquivo"
>
<el-button type="primary" icon="el-icon-upload2" round>Importar</el-button>
</el-upload>
2. Lógica de Processamento no Frontend
Implemente o método para ler e processar o arquivo Excel:
import * as XLSX from "xlsx";
export default {
methods: {
selecionarArquivo(evento) {
const periodo = this.formulario.periodo;
const temDadosExistentes = this.quantidadeDados > 0;
const processarLeitura = (arquivo) => {
const leitor = new FileReader();
leitor.readAsArrayBuffer(arquivo);
leitor.onload = (eventoLeitura) => {
const dadosBinarios = new Uint8Array(eventoLeitura.target.result);
const planilha = XLSX.read(dadosBinarios, { type: 'array' });
const nomePrimeiraAba = planilha.SheetNames[0];
const aba = planilha.Sheets[nomePrimeiraAba];
const intervalo = { s: { r: 2, c: 3 }, e: { r: 32, c: 18 } };
const dadosConvertidos = XLSX.utils.sheet_to_json(aba, {
header: 1,
defval: '',
range: intervalo
});
const corpoRequisicao = {
registros: JSON.parse(JSON.stringify(dadosConvertidos)),
periodo: periodo
};
this.enviarDadosParaApi(corpoRequisicao);
};
};
if (temDadosExistentes) {
this.$confirm(`${periodo} já contém dados. Deseja substituí-los?`, 'Confirmação', {
confirmButtonText: 'Confirmar',
cancelButtonText: 'Cancelar',
type: 'warning'
}).then(() => processarLeitura(evento.raw));
} else {
processarLeitura(evento.raw);
}
},
enviarDadosParaApi(dados) {
importarDados(dados).then((resposta) => {
if (resposta.codigo === 200) {
this.carregarDados();
this.notificarSucesso("Importação concluída com sucesso");
} else {
this.notificarErro("Falha na importação. Verifique o modelo do arquivo.");
}
});
}
}
};
3. Serviço de API
Defina a função para comunicação com o backend:
import axios from 'axios';
export function importarDados(payload) {
return axios.post('/api/relatorio-operacional/importar', payload);
}
4. Implementação do Backend (Spring Boot)
No controlador, defina o endpoint para receber os dados:
@RestController
@RequestMapping("/api/relatorio-operacional")
public class RelatorioController {
@Autowired
private RelatorioServico servico;
@PostMapping("/importar")
public ResponseEntity<?> importarDados(@RequestBody @Valid RequisicaoImportacao requisicao) {
return servico.processarImportacao(requisicao.getRegistros(), requisicao.getPeriodo());
}
}
No serviço, implemente a lógica de negócio e persistência:
@Service
public class RelatorioServico {
@Autowired
private RelatorioRepositorio repositorio;
@Transactional(rollbackFor = Exception.class)
public ResponseEntity<?> processarImportacao(List<String> registros, String periodo) {
try {
List<RegistroRelatorio> entidades = new ArrayList<>();
for (String linha : registros) {
JSONArray valores = JSON.parseArray(linha);
String nomeEmpresa = valores.getString(0);
Integer codigoEmpresa = MapeamentoEmpresas.obterCodigo(nomeEmpresa);
if (codigoEmpresa == null) {
throw new RuntimeException("Empresa não encontrada: " + nomeEmpresa);
}
RegistroRelatorio entidade = new RegistroRelatorio();
entidade.setId(GeradorId.gerarNovo());
entidade.setPeriodo(periodo);
entidade.setEmpresaId(codigoEmpresa);
preencherCamposNumericos(entidade, valores);
entidades.add(entidade);
}
if (!entidades.isEmpty()) {
repositorio.deletarPorPeriodo(periodo);
repositorio.inserirEmLote(entidades);
}
return ResponseEntity.ok().build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
private void preencherCamposNumericos(RegistroRelatorio entidade, JSONArray valores) {
// Lógica para preencher campos numéricos a partir do JSONArray
entidade.setTotalPessoas(parseValorInteiro(valores.getString(1)));
entidade.setPessoasAdministrativas(parseValorInteiro(valores.getString(2)));
// ... outros campos
}
private Integer parseValorInteiro(String valor) {
return StringUtils.isNotBlank(valor) ? Integer.parseInt(valor) : 0;
}
}
Crie uma classe auxiliar para mapeamento de empresas:
public final class MapeamentoEmpresas {
private static final Map<String, Integer> MAPEAMENTO = new HashMap<>();
static {
MAPEAMENTO.put("Empresa A", 1001);
MAPEAMENTO.put("Empresa B", 1002);
// ... outros mapeamentos
}
public static Integer obterCodigo(String nomeEmpresa) {
return MAPEAMENTO.get(nomeEmpresa);
}
}
Para a inserção em lote no banco de dados, configure o Mapper XML do MyBatis:
<mapper namespace="com.projeto.dao.RelatorioRepositorio">
<insert id="inserirEmLote">
INSERT INTO relatorio_operacional
(id, empresa_id, periodo, total_pessoas, pessoas_administrativas)
VALUES
<foreach collection="lista" item="item" separator=",">
(#{item.id}, #{item.empresaId}, #{item.periodo}, #{item.totalPessoas}, #{item.pessoasAdministrativas})
</foreach>
</insert>
<delete id="deletarPorPeriodo">
DELETE FROM relatorio_operacional WHERE periodo = #{periodo}
</delete>
</mapper>