历史记录中,增加按配置名称搜索
This commit is contained in:
@@ -202,3 +202,123 @@ def batch_download():
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return jsonify({'success': False, 'message': str(e)}), 500
|
||||
|
||||
|
||||
@download_bp.route('/download/batch-filter', methods=['POST'])
|
||||
def batch_download_by_filter():
|
||||
"""按筛选条件批量打包下载
|
||||
接收筛选条件(config_name, start_date, end_date, status),
|
||||
将所有匹配的成功记录的报表文件打包成zip后下载。
|
||||
与历史记录列表的搜索条件保持同步。
|
||||
"""
|
||||
try:
|
||||
data = request.get_json(silent=True) or {}
|
||||
config_name = data.get('config_name', '').strip()
|
||||
start_date = data.get('start_date', '').strip()
|
||||
end_date = data.get('end_date', '').strip()
|
||||
status = data.get('status', '')
|
||||
|
||||
log_info(f'[下载API] 按筛选批量下载,配置名称: {config_name or "全部"},日期范围: {start_date or "开始"} ~ {end_date or "结束"},状态: {status or "全部"}', 'download')
|
||||
|
||||
# 清理旧的临时文件
|
||||
cleanup_old_temp_zips()
|
||||
|
||||
# 构建查询条件(与历史记录列表查询条件完全一致)
|
||||
where_clauses = ['status = 1'] # 只下载成功的记录
|
||||
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)
|
||||
|
||||
if status is not None and status != '':
|
||||
where_clauses.append('status = %s')
|
||||
params.append(int(status))
|
||||
|
||||
if config_name:
|
||||
where_clauses.append('config_name LIKE %s')
|
||||
params.append(f'%{config_name}%')
|
||||
|
||||
where_sql = ' AND '.join(where_clauses)
|
||||
|
||||
# 查询所有匹配的成功记录
|
||||
result = execute_query(
|
||||
f'SELECT id, file_path, config_name, report_date, status FROM t_daily_report_history WHERE {where_sql} ORDER BY create_time DESC',
|
||||
tuple(params)
|
||||
)
|
||||
|
||||
if not result:
|
||||
return jsonify({'success': False, 'message': '未找到符合条件的历史记录'}), 404
|
||||
|
||||
reports_dir = get_reports_dir()
|
||||
temp_dir = get_temp_dir()
|
||||
|
||||
# 收集要打包的文件
|
||||
files_to_zip = []
|
||||
failed_names = []
|
||||
|
||||
for item in result:
|
||||
file_path = item.get('file_path')
|
||||
if not file_path:
|
||||
failed_names.append(f"{item.get('config_name', '未知')} (无文件)")
|
||||
continue
|
||||
|
||||
# 提取文件名
|
||||
if file_path.startswith('/public/reports/'):
|
||||
filename = file_path.replace('/public/reports/', '')
|
||||
elif file_path.startswith('/reports/'):
|
||||
filename = file_path.replace('/reports/', '')
|
||||
else:
|
||||
filename = os.path.basename(file_path)
|
||||
|
||||
full_path = os.path.join(reports_dir, filename)
|
||||
if not os.path.exists(full_path):
|
||||
failed_names.append(f"{item.get('config_name', '未知')} (文件不存在)")
|
||||
continue
|
||||
|
||||
# 使用配置名作为zip内的文件名,避免重名
|
||||
config_name_val = item.get('config_name', '未知')
|
||||
report_date = item.get('report_date', '')
|
||||
ext = os.path.splitext(filename)[1]
|
||||
if report_date:
|
||||
zip_inner_name = f"{config_name_val}_{report_date}{ext}"
|
||||
else:
|
||||
zip_inner_name = f"{config_name_val}{ext}"
|
||||
|
||||
files_to_zip.append({
|
||||
'full_path': full_path,
|
||||
'inner_name': zip_inner_name
|
||||
})
|
||||
|
||||
if len(files_to_zip) == 0:
|
||||
return jsonify({'success': False, 'message': '没有可下载的文件'}), 400
|
||||
|
||||
# 生成zip文件名
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
zip_filename = f'批量导出_{timestamp}.zip'
|
||||
zip_path = os.path.join(temp_dir, zip_filename)
|
||||
|
||||
# 打包
|
||||
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:
|
||||
for file_info in files_to_zip:
|
||||
zf.write(file_info['full_path'], file_info['inner_name'])
|
||||
|
||||
log_info(f'[下载API] 按筛选批量打包完成,共 {len(files_to_zip)} 个文件,失败 {len(failed_names)} 个', 'download')
|
||||
|
||||
# 返回zip文件
|
||||
from flask import send_file
|
||||
return send_file(
|
||||
zip_path,
|
||||
as_attachment=True,
|
||||
download_name=zip_filename,
|
||||
mimetype='application/zip'
|
||||
)
|
||||
except Exception as e:
|
||||
log_error(f'[下载API] 按筛选批量下载错误: {e}', 'download')
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return jsonify({'success': False, 'message': str(e)}), 500
|
||||
|
||||
@@ -54,7 +54,8 @@ def get_report_history():
|
||||
start_date = request.args.get('start_date')
|
||||
end_date = request.args.get('end_date')
|
||||
status = request.args.get('status')
|
||||
log_info(f'[报表API] 获取历史记录,第{page}页,状态: {status or "全部"},日期范围: {start_date or "开始"} ~ {end_date or "结束"}', 'api')
|
||||
config_name = request.args.get('config_name')
|
||||
log_info(f'[报表API] 获取历史记录,第{page}页,状态: {status or "全部"},配置名称: {config_name or "全部"},日期范围: {start_date or "开始"} ~ {end_date or "结束"}', 'api')
|
||||
|
||||
offset = (page - 1) * page_size
|
||||
|
||||
@@ -74,6 +75,11 @@ def get_report_history():
|
||||
where_clauses.append('status = %s')
|
||||
params.append(int(status))
|
||||
|
||||
# 支持按配置名称模糊搜索
|
||||
if config_name is not None and config_name.strip() != '':
|
||||
where_clauses.append('config_name LIKE %s')
|
||||
params.append(f'%{config_name.strip()}%')
|
||||
|
||||
where_sql = ' AND '.join(where_clauses) if where_clauses else '1=1'
|
||||
|
||||
# 查询总数
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
let historyStartDate = '';
|
||||
let historyEndDate = '';
|
||||
let historyStatus = '';
|
||||
let historyConfigName = '';
|
||||
let historyPageSize = 10;
|
||||
|
||||
// 每页显示条数变化
|
||||
@@ -75,8 +76,10 @@ async function batchDownloadHistory() {
|
||||
function queryHistoryByDate() {
|
||||
const startDate = document.getElementById('history-start-date').value;
|
||||
const endDate = document.getElementById('history-end-date').value;
|
||||
const configNameInput = document.getElementById('history-config-name');
|
||||
historyStartDate = startDate;
|
||||
historyEndDate = endDate;
|
||||
historyConfigName = configNameInput ? configNameInput.value.trim() : '';
|
||||
loadHistoryWithDate(1);
|
||||
}
|
||||
|
||||
@@ -85,9 +88,12 @@ function clearHistoryFilter() {
|
||||
document.getElementById('history-status-filter').value = '';
|
||||
document.getElementById('history-start-date').value = '';
|
||||
document.getElementById('history-end-date').value = '';
|
||||
const configNameInput = document.getElementById('history-config-name');
|
||||
if (configNameInput) configNameInput.value = '';
|
||||
historyStatus = '';
|
||||
historyStartDate = '';
|
||||
historyEndDate = '';
|
||||
historyConfigName = '';
|
||||
loadHistory(1);
|
||||
}
|
||||
|
||||
@@ -281,10 +287,16 @@ async function loadHistory(page = 1) {
|
||||
|
||||
async function loadHistoryWithDate(page) {
|
||||
try {
|
||||
// 同步配置名称搜索框的值(翻页时保留筛选条件)
|
||||
const configNameInput = document.getElementById('history-config-name');
|
||||
if (configNameInput) {
|
||||
historyConfigName = configNameInput.value.trim();
|
||||
}
|
||||
let url = `/api/report/history?page=${page}&page_size=${historyPageSize}`;
|
||||
if (historyStatus) url += `&status=${historyStatus}`;
|
||||
if (historyStartDate) url += `&start_date=${historyStartDate}`;
|
||||
if (historyEndDate) url += `&end_date=${historyEndDate}`;
|
||||
if (historyConfigName) url += `&config_name=${encodeURIComponent(historyConfigName)}`;
|
||||
const response = await fetch(url);
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
@@ -362,3 +374,70 @@ async function batchDeleteHistory() {
|
||||
alert('删除失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 批量导出当前筛选条件下的所有记录(与搜索条件同步)
|
||||
async function batchDownloadFilteredHistory() {
|
||||
// 同步当前筛选条件
|
||||
const configNameInput = document.getElementById('history-config-name');
|
||||
if (configNameInput) {
|
||||
historyConfigName = configNameInput.value.trim();
|
||||
}
|
||||
historyStatus = document.getElementById('history-status-filter').value;
|
||||
historyStartDate = document.getElementById('history-start-date').value;
|
||||
historyEndDate = document.getElementById('history-end-date').value;
|
||||
|
||||
// 构建筛选条件描述
|
||||
const filterDescParts = [];
|
||||
if (historyStatus) {
|
||||
filterDescParts.push(historyStatus === '1' ? '成功' : '失败');
|
||||
}
|
||||
if (historyConfigName) {
|
||||
filterDescParts.push(`配置名称含"${historyConfigName}"`);
|
||||
}
|
||||
if (historyStartDate) {
|
||||
filterDescParts.push(`开始日期 ${historyStartDate}`);
|
||||
}
|
||||
if (historyEndDate) {
|
||||
filterDescParts.push(`结束日期 ${historyEndDate}`);
|
||||
}
|
||||
const filterDesc = filterDescParts.length > 0 ? filterDescParts.join(',') : '全部记录';
|
||||
|
||||
if (!confirm(`将导出符合当前筛选条件的所有成功记录(${filterDesc})。\n注意:仅导出生成成功的报表。\n是否继续?`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/download/batch-filter', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
config_name: historyConfigName,
|
||||
start_date: historyStartDate,
|
||||
end_date: historyEndDate,
|
||||
status: historyStatus
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
const timestamp = new Date().toISOString().slice(0, 19).replace(/[-T:]/g, '');
|
||||
a.download = `批量导出_${timestamp}.zip`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
window.URL.revokeObjectURL(url);
|
||||
} else {
|
||||
try {
|
||||
const result = await response.json();
|
||||
alert('导出失败: ' + (result.message || '未知错误'));
|
||||
} catch (e) {
|
||||
alert('导出失败: 服务器返回错误');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
alert('导出失败: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +127,7 @@
|
||||
<input type="date" id="history-start-date" class="form-control" style="width: auto;">
|
||||
<span style="line-height: 38px;">至</span>
|
||||
<input type="date" id="history-end-date" class="form-control" style="width: auto;">
|
||||
<input type="text" id="history-config-name" class="form-control" style="width: 180px;" placeholder="配置名称搜索" autocomplete="off" onkeyup="if(event.key==='Enter') queryHistoryByDate()">
|
||||
<button class="btn btn-primary" onclick="queryHistoryByDate()">查询</button>
|
||||
<button class="btn" onclick="clearHistoryFilter()">清除</button>
|
||||
<div style="margin-left: auto; display: flex; gap: 10px; align-items: center;">
|
||||
@@ -140,6 +141,7 @@
|
||||
</div>
|
||||
<button class="btn btn-danger" onclick="batchDeleteHistory()">批量删除</button>
|
||||
<button class="btn btn-danger" onclick="batchDownloadHistory()">批量下载</button>
|
||||
<button class="btn btn-success" onclick="batchDownloadFilteredHistory()">批量导出(当前筛选)</button>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
|
||||
Reference in New Issue
Block a user