You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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错误,通常是前端参数不符合后端预期,或后端代码存在异常。

排查步骤:

  1. 开启Flask调试模式:启动后端时添加app.run(debug=True),控制台会显示详细的报错信息(如SQL语法错误、参数缺失、类型转换失败等)。
  2. 验证接口参数:
    • 确认前端传递的参数名与后端接口定义完全一致(比如fabricante是否对应后端接收的参数名)。
    • 检查空值处理:后端是否处理了用户未填写的参数(比如空字符串、None),避免SQL查询因参数缺失报错。
  3. 直接测试接口:在浏览器地址栏手动拼接参数访问接口,例如:
    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
    
    查看返回结果或错误详情。
  4. 检查SQL查询:确保后端生成的SQL语句语法正确,建议使用参数化查询(避免SQL注入,同时防止参数拼接导致的语法错误)。

三、额外优化建议

  • 前端添加表单验证:发送请求前检查必填参数,避免无效请求。
  • 后端添加参数校验:用Flask-WTF或手动校验参数类型、格式,返回明确的4xx错误(如400 Bad Request)而非500。
  • 统一前后端命名规范:比如都使用蛇形命名(data_min)或驼峰命名,减少混淆。

内容的提问来源于stack exchange,提问作者EckoLles

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 06:44:56