基于JSON响应的报表生成按钮失效问题求助
问题描述
我正在开发一个基于PostgreSQL的数据库项目,后端采用Python Flask,前端使用HTML/CSS/JavaScript。页面上有两个报表生成按钮,其中一个点击后无法正常工作,开发者工具报错:
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'value')
at fetchProdutoVendidoReport (script.js:50:61)
at HTMLButtonElement.onclick
尝试修复(比如给HTML表单添加id)时,另一个按钮也会失效,两者均返回500: Internal Server Error。
相关HTML代码
<body> <div id="header"></div> <script src="/frontend/components/header/header.js"></script> <h1>Gerar Relatórios</h1> <!-- Formulário para Relatório de Pedido Cliente --> <h2>Relatório de Pedido Cliente</h2> <form id="pedidoClienteForm"> <label>Categoria: <input type="text" id="categoria" name="categoria"></label><br> <label>Data Mínima: <input type="date" id="startDate" name="data_min"></label><br> <label>Data Máxima: <input type="date" id="endDate" name="data_max"></label><br> <label>Fabricante: <input type="text" id="marca" name="fabricante"></label><br> <label>Método de Pagamento: <input type="text" id="metodoPagamento" name="metodo"></label><br> <label>Preço Mínimo: <input type="number" id="precoMin" name="preco_min"></label><br> <label>Preço Máximo: <input type="number" id="precoMax" name="preco_max"></label><br> <button type="button" onclick="fetchPedidoClienteReport()">Gerar Relatório de Pedido Cliente</button> </form> <div id="pedidoClienteResultado"></div> <h2>Resultados</h2> <table id="TabelaPedidoCliente"> <thead> <tr> <th>Produto</th> <th>Categoria</th> <th>Marca</th> <th>Total Vendido</th> <th>Receita Total</th> <th>Método de Pagamento</th> <th>Data de Venda</th> </tr> </thead> <tbody></tbody> </table> <!-- Formulário para Relatório de Produto Vendido --> <h2>Relatório de Produto Vendido</h2> <form id="produtoVendidoForm"> <label>Data Mínima: <input type="date" name="data_min"></label><br> <label>Data Máxima: <input type="date" name="data_max"></label><br> <label>Preço Mínimo: <input type="number" name="preco_min"></label><br> <label>Preço Máximo: <input type="number" name="preco_max"></label><br> <button type="button" onclick="fetchProdutoVendidoReport()">Gerar Relatório de Produto Vendido</button> </form> <div id="produtoVendidoResultado"></div> <h2>Resultados</h2> <table id="TabelaProdutoVendido"> <thead> <tr> <th>Produto</th> <th>Faixa de Preço</th> <th>Total Vendido</th> <th>Receita Total</th> <th>Método de Pagamento</th> </tr> </thead> <tbody></tbody> </table> <script src="/frontend/pages/relatorios/script.js"></script> </body>
相关JS代码
async function fetchPedidoClienteReport() { const startDate = document.getElementById('startDate').value; const endDate = document.getElementById('endDate').value; const categoria = document.getElementById('categoria').value; const marca = document.getElementById('marca').value; const precoMin = document.getElementById('precoMin').value; const precoMax = document.getElementById('precoMax').value; const metodoPagamento = document.getElementById('metodoPagamento').value; const url = `http://127.0.0.1:8000/relatorio/pedido_cliente?categoria=${categoria}&data_min=${startDate}&data_max=${endDate}&fabricante=${marca}&metodo=${metodoPagamento}&preco_min=${precoMin}&preco_max=${precoMax}`; try { // Envia a requisição para o backend const response = await fetch(url, { method: 'GET' }); // Converte a resposta para JSON const data = await response.json(); // Atualiza a tabela com os dados recebidos const tbody = document.getElementById('TabelaPedidoCliente').getElementsByTagName('tbody')[0]; // Limpa a tabela antes de preencher com os novos dados tbody.innerHTML = ''; function formataData(dateString) { const date = new Date(dateString); return date.toLocaleDateString('pt-BR'); // Formato 'DD/MM/YYYY' } // Preenche a tabela com os dados do relatório data.forEach(item => { const row = tbody.insertRow(); row.insertCell(0).textContent = item.nome; // nome do produto row.insertCell(1).textContent = item.categoria; // categoria do produto row.insertCell(2).textContent = item.fabricante; // fabricante do produto row.insertCell(3).textContent = item.quantidade; // total vendido row.insertCell(4).textContent = item.total_venda; // receita total row.insertCell(5).textContent = item.metodo; // método de pagamento row.insertCell(6).textContent = formataData(item.data); // data da venda }) } catch (error) { console.error('Erro ao gerar o relatório:', error); } } // Função para buscar o Relatório de Produto Vendido async function fetchProdutoVendidoReport() { const DataComeco = document.getElementById('DataComeco').value; const DataFinal = document.getElementById('DataFinal').value; const Preco_Min = document.getElementById('preco_Min').value; const Preco_Max = document.getElementById('preco_Max').value; const url = `http://127.0.0.1:8000/relatorio/produto_vendido?data_min=${DataComeco}&data_max=${DataFinal}&preco_min=${Preco_Min}&preco_max=${Preco_Max}`; try { // Envia a requisição para o backend const response = await fetch(url, { method: 'GET' }); // Converte a resposta para JSON const data = await response.json(); // Atualiza a tabela com os dados recebidos const tbody = document.getElementById('TabelaProdutoVendido').getElementsByTagName('tbody')[0]; // Limpa a tabela antes de preencher com os novos dados tbody.innerHTML = ''; // Preenche a tabela com os dados do relatório data.forEach(item => { const row = tbody.insertRow(); row.insertCell(0).textContent = item.nome; // nome do produto row.insertCell(1).textContent = item.faixa_preco; // categoria do produto row.insertCell(2).textContent = item.total_vendido; // fabricante do produto row.insertCell(3).textContent = item.receita_total; // total vendido row.insertCell(4).textContent = item.metodo; // método de pagamento }) } catch (error) { console.error('Erro ao gerar o relatório:', error); } }
解决方案
一、修复前端TypeError问题
报错根源是fetchProdutoVendidoReport函数尝试获取的元素ID(DataComeco、DataFinal、preco_Min、preco_Max)在HTML中不存在,导致document.getElementById返回null,读取value时触发错误。
修正步骤:
给"Relatório de Produto Vendido"表单的输入框添加对应ID:
<!-- Formulário para Relatório de Produto Vendido --> <h2>Relatório de Produto Vendido</h2> <form id="produtoVendidoForm"> <label>Data Mínima: <input type="date" id="DataComeco" name="data_min"></label><br> <label>Data Máxima: <input type="date" id="DataFinal" name="data_max"></label><br> <label>Preço Mínimo: <input type="number" id="preco_Min" name="preco_min"></label><br> <label>Preço Máximo: <input type="number" id="preco_Max" name="preco_max"></label><br> <button type="button" onclick="fetchProdutoVendidoReport()">Gerar Relatório de Produto Vendido</button> </form>
可选优化:通过表单对象获取值
避免依赖元素ID,直接通过表单对象读取值,降低耦合:
async function fetchProdutoVendidoReport() { const form = document.getElementById('produtoVendidoForm'); const formData = new FormData(form); const DataComeco = formData.get('data_min'); const DataFinal = formData.get('data_max'); const Preco_Min = formData.get('preco_min'); const Preco_Max = formData.get('preco_max'); // 后续请求逻辑不变 }
二、排查后端500 Internal Server Error
修改后出现500错误,通常是前端参数不符合后端预期,或后端代码存在异常。
排查步骤:
- 开启Flask调试模式:启动后端时添加
app.run(debug=True),控制台会显示详细的报错信息(如SQL语法错误、参数缺失、类型转换失败等)。 - 验证接口参数:
- 确认前端传递的参数名与后端接口定义完全一致(比如
fabricante是否对应后端接收的参数名)。 - 检查空值处理:后端是否处理了用户未填写的参数(比如空字符串、
None),避免SQL查询因参数缺失报错。
- 确认前端传递的参数名与后端接口定义完全一致(比如
- 直接测试接口:在浏览器地址栏手动拼接参数访问接口,例如:
查看返回结果或错误详情。http://127.0.0.1:8000/relatorio/produto_vendido?data_min=2024-01-01&data_max=2024-06-01&preco_min=10&preco_max=100 - 检查SQL查询:确保后端生成的SQL语句语法正确,建议使用参数化查询(避免SQL注入,同时防止参数拼接导致的语法错误)。
三、额外优化建议
- 前端添加表单验证:发送请求前检查必填参数,避免无效请求。
- 后端添加参数校验:用Flask-WTF或手动校验参数类型、格式,返回明确的4xx错误(如400 Bad Request)而非500。
- 统一前后端命名规范:比如都使用蛇形命名(
data_min)或驼峰命名,减少混淆。
内容的提问来源于stack exchange,提问作者EckoLles
相关产品推荐
相关产品推荐

