diff --git a/app.py b/app.py index 73069d9..65ef65d 100644 --- a/app.py +++ b/app.py @@ -294,16 +294,35 @@ def get_report_history(): try: page = int(request.args.get('page', 1)) page_size = int(request.args.get('page_size', 20)) + start_date = request.args.get('start_date') + end_date = request.args.get('end_date') offset = (page - 1) * page_size + # 构建查询条件 + where_clauses = [] + params = [] + + if start_date: + where_clauses.append('DATE(start_time) >= %s') + params.append(start_date) + + if end_date: + where_clauses.append('DATE(end_time) <= %s') + params.append(end_date) + + where_sql = ' AND '.join(where_clauses) if where_clauses else '1=1' + # 查询总数 - total_result = execute_query('SELECT COUNT(*) as total FROM t_daily_report_history') + total_result = execute_query( + f'SELECT COUNT(*) as total FROM t_daily_report_history WHERE {where_sql}', + tuple(params) + ) total = total_result[0]['total'] # 查询数据 history = execute_query( - 'SELECT * FROM t_daily_report_history ORDER BY create_time DESC LIMIT %s OFFSET %s', - (page_size, offset) + f'SELECT * FROM t_daily_report_history WHERE {where_sql} ORDER BY create_time DESC LIMIT %s OFFSET %s', + tuple(params) + (page_size, offset) ) # 解析JSON字段并确保file_path正确 @@ -332,6 +351,105 @@ def get_report_history(): return jsonify({'success': False, 'message': str(e)}), 500 +# ==================== 历史记录管理API ==================== + +@app.route('/api/report/history/', methods=['DELETE']) +def delete_report_history(history_id): + """删除单个历史记录""" + try: + # 先查询文件路径 + result = execute_query( + 'SELECT file_path FROM t_daily_report_history WHERE id = %s', + (history_id,) + ) + + if result: + file_path = result[0].get('file_path', '') + # 删除文件 + if file_path and os.path.exists(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'public', file_path.lstrip('/'))): + os.remove(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'public', file_path.lstrip('/'))) + + # 删除数据库记录 + execute_update('DELETE FROM t_daily_report_history WHERE id = %s', (history_id,)) + + return jsonify({'success': True, 'message': '删除成功'}) + except Exception as e: + print(f"[删除历史记录] 错误: {e}") + import traceback + traceback.print_exc() + return jsonify({'success': False, 'message': str(e)}), 500 + + +@app.route('/api/report/history/batch-delete', methods=['POST']) +def batch_delete_report_history(): + """批量删除历史记录""" + try: + data = request.get_json() + ids = data.get('ids', []) + + if not ids: + return jsonify({'success': False, 'message': '请选择要删除的记录'}), 400 + + # 查询文件路径 + results = execute_query( + f'SELECT file_path FROM t_daily_report_history WHERE id IN ({",".join(["%s"] * len(ids))})', + tuple(ids) + ) + + # 删除文件 + for result in results: + file_path = result.get('file_path', '') + if file_path and os.path.exists(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'public', file_path.lstrip('/'))): + os.remove(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'public', file_path.lstrip('/'))) + + # 删除数据库记录 + execute_update( + f'DELETE FROM t_daily_report_history WHERE id IN ({",".join(["%s"] * len(ids))})', + tuple(ids) + ) + + return jsonify({'success': True, 'message': f'成功删除 {len(ids)} 条记录'}) + except Exception as e: + print(f"[批量删除历史记录] 错误: {e}") + import traceback + traceback.print_exc() + return jsonify({'success': False, 'message': str(e)}), 500 + + +@app.route('/api/report/history//download', methods=['GET']) +def download_report_by_id(history_id): + """根据ID下载历史记录""" + try: + # 查询历史记录 + result = execute_query( + 'SELECT file_path, split_name, report_date FROM t_daily_report_history WHERE id = %s', + (history_id,) + ) + + if not result: + return jsonify({'success': False, 'message': '记录不存在'}), 404 + + file_path = result[0]['file_path'] + if not file_path: + return jsonify({'success': False, 'message': '文件不存在'}), 404 + + # 构建完整路径 + full_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'public', file_path.lstrip('/')) + + if not os.path.exists(full_path): + return jsonify({'success': False, 'message': '文件不存在'}), 404 + + # 返回文件 + directory = os.path.dirname(full_path) + filename = os.path.basename(full_path) + return send_from_directory(directory, filename, as_attachment=True) + except Exception as e: + print(f"[下载历史记录] 错误: {e}") + import traceback + traceback.print_exc() + return jsonify({'success': False, 'message': str(e)}), 500 + + # ==================== 数据库初始化API ==================== @app.route('/api/init-tables', methods=['POST']) diff --git a/assets/image_20260710105803928.png b/assets/image_20260710105803928.png new file mode 100644 index 0000000..25f5abe Binary files /dev/null and b/assets/image_20260710105803928.png differ diff --git a/templates/index.html b/templates/index.html index 52e3ae2..e66f5d9 100644 --- a/templates/index.html +++ b/templates/index.html @@ -409,13 +409,24 @@
- +
+ + + + 日期查询: + + + + + +
+ @@ -427,7 +438,7 @@ - +
报表日期 时间范围 配置名称
加载中...
加载中...
@@ -869,6 +880,19 @@ return String(dateTimeStr); } + // 格式化日期为数字格式(只显示日期,用于显示) + function formatDateDisplay(dateStr) { + if (!dateStr) return '-'; + const date = new Date(dateStr); + if (!isNaN(date.getTime())) { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + return `${year}-${month}-${day}`; + } + return String(dateStr); + } + // 生成日报 async function generateReport() { const configId = document.getElementById('generate-config').value; @@ -1008,7 +1032,8 @@ return ` - ${item.report_date} + + ${formatDateDisplay(item.report_date)} ${formatDateTimeDisplay(item.start_time)}

${formatDateTimeDisplay(item.end_time)} ${item.config_name || '-'} ${item.split_name || item.split_value} @@ -1023,6 +1048,7 @@ ${hasFile ? `📥 下载` : `-`} + `}).join(''); @@ -1050,6 +1076,98 @@ loadHistory(currentPage); } + // 全选/取消全选 + function toggleSelectAll() { + const selectAll = document.getElementById('select-all'); + const checkboxes = document.querySelectorAll('.history-checkbox'); + checkboxes.forEach(cb => cb.checked = selectAll.checked); + } + + // 获取选中的ID列表 + function getSelectedIds() { + const checkboxes = document.querySelectorAll('.history-checkbox:checked'); + return Array.from(checkboxes).map(cb => parseInt(cb.value)); + } + + // 批量删除 + async function batchDeleteHistory() { + const ids = getSelectedIds(); + if (ids.length === 0) { + alert('请先选择要删除的记录'); + return; + } + if (!confirm(`确定要删除选中的 ${ids.length} 条记录吗?`)) { + return; + } + try { + const response = await fetch('/api/report/history/batch-delete', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ ids }) + }); + const result = await response.json(); + if (result.success) { + alert(`成功删除 ${result.deleted_count} 条记录`); + loadHistory(currentPage); + } else { + alert('批量删除失败: ' + (result.error || '未知错误')); + } + } catch (error) { + alert('批量删除失败: ' + error.message); + } + } + + // 批量下载 + function batchDownloadHistory() { + const ids = getSelectedIds(); + if (ids.length === 0) { + alert('请先选择要下载的记录'); + return; + } + // 逐个下载 + ids.forEach(id => { + window.open(`/api/download?id=${id}`, '_blank'); + }); + } + + // 按日期查询 + function queryHistoryByDate() { + const startDate = document.getElementById('query-start-date').value; + const endDate = document.getElementById('query-end-date').value; + if (!startDate && !endDate) { + alert('请至少选择一个日期'); + return; + } + // 重新加载历史记录,带上日期参数 + loadHistoryWithDate(1, startDate, endDate); + } + + // 清除日期查询 + function clearDateQuery() { + document.getElementById('query-start-date').value = ''; + document.getElementById('query-end-date').value = ''; + loadHistory(1); + } + + // 带日期参数加载历史记录 + async function loadHistoryWithDate(page, startDate, endDate) { + try { + let url = `/api/report/history?page=${page}&page_size=10`; + if (startDate) url += `&start_date=${startDate}`; + if (endDate) url += `&end_date=${endDate}`; + const response = await fetch(url); + const result = await response.json(); + if (result.success) { + renderHistory(result.data.list); + renderPagination(result.data.total, page); + } else { + alert('加载失败: ' + (result.error || '未知错误')); + } + } catch (error) { + alert('加载失败: ' + error.message); + } + } + // 初始化 loadConfigs(); loadFields();