增加序号,配置导出,导入,求和采集数据导出
This commit is contained in:
@@ -272,3 +272,150 @@ def move_config(config_id):
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
print(f"[move_config] 异常: {e}")
|
print(f"[move_config] 异常: {e}")
|
||||||
return jsonify({'success': False, 'message': str(e)}), 500
|
return jsonify({'success': False, 'message': str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
|
@config_bp.route('/config/export', methods=['GET'])
|
||||||
|
def export_configs():
|
||||||
|
"""导出所有配置为JSON文件"""
|
||||||
|
try:
|
||||||
|
from flask import Response
|
||||||
|
import os
|
||||||
|
|
||||||
|
configs = execute_query(
|
||||||
|
'SELECT * FROM t_daily_report_config ORDER BY sort_order ASC, id ASC'
|
||||||
|
)
|
||||||
|
|
||||||
|
export_list = []
|
||||||
|
for config in configs:
|
||||||
|
export_item = {}
|
||||||
|
for key, value in config.items():
|
||||||
|
if key == 'id':
|
||||||
|
continue
|
||||||
|
export_item[key] = value
|
||||||
|
export_list.append(export_item)
|
||||||
|
|
||||||
|
export_data = {
|
||||||
|
'version': '1.0',
|
||||||
|
'export_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
|
'total': len(export_list),
|
||||||
|
'configs': export_list
|
||||||
|
}
|
||||||
|
|
||||||
|
json_str = json.dumps(export_data, ensure_ascii=False, indent=2)
|
||||||
|
filename = f'日报配置导出_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json'
|
||||||
|
|
||||||
|
response = Response(json_str, mimetype='application/json')
|
||||||
|
response.headers['Content-Disposition'] = f'attachment; filename="{filename}"'
|
||||||
|
return response
|
||||||
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return jsonify({'success': False, 'message': f'导出失败: {str(e)}'}), 500
|
||||||
|
|
||||||
|
|
||||||
|
@config_bp.route('/config/import', methods=['POST'])
|
||||||
|
def import_configs():
|
||||||
|
"""从JSON文件导入配置"""
|
||||||
|
try:
|
||||||
|
if 'file' not in request.files:
|
||||||
|
return jsonify({'success': False, 'message': '请选择要导入的文件'}), 400
|
||||||
|
|
||||||
|
file = request.files['file']
|
||||||
|
if file.filename == '':
|
||||||
|
return jsonify({'success': False, 'message': '请选择要导入的文件'}), 400
|
||||||
|
|
||||||
|
if not file.filename.endswith('.json'):
|
||||||
|
return jsonify({'success': False, 'message': '只支持 JSON 格式的配置文件'}), 400
|
||||||
|
|
||||||
|
file_content = file.read().decode('utf-8')
|
||||||
|
import_data = json.loads(file_content)
|
||||||
|
|
||||||
|
if 'configs' not in import_data:
|
||||||
|
return jsonify({'success': False, 'message': '文件格式不正确,缺少 configs 字段'}), 400
|
||||||
|
|
||||||
|
configs_to_import = import_data['configs']
|
||||||
|
if not isinstance(configs_to_import, list) or len(configs_to_import) == 0:
|
||||||
|
return jsonify({'success': False, 'message': '没有可导入的配置'}), 400
|
||||||
|
|
||||||
|
max_sort_result = execute_query('SELECT MAX(sort_order) as max_sort FROM t_daily_report_config')
|
||||||
|
current_max_sort = max_sort_result[0]['max_sort'] or 0
|
||||||
|
|
||||||
|
success_count = 0
|
||||||
|
fail_count = 0
|
||||||
|
fail_messages = []
|
||||||
|
|
||||||
|
for idx, config_data in enumerate(configs_to_import):
|
||||||
|
try:
|
||||||
|
config_name = config_data.get('config_name', '')
|
||||||
|
split_type = config_data.get('split_type', '')
|
||||||
|
split_value = config_data.get('split_value', '')
|
||||||
|
|
||||||
|
if not config_name or not split_type or not split_value:
|
||||||
|
fail_count += 1
|
||||||
|
fail_messages.append(f'第{idx+1}条:缺少必填字段')
|
||||||
|
continue
|
||||||
|
|
||||||
|
new_id = int(datetime.now().timestamp() * 1000) + idx
|
||||||
|
new_name = f"{config_name}_导入"
|
||||||
|
sort_order = current_max_sort + 1
|
||||||
|
current_max_sort += 1
|
||||||
|
|
||||||
|
selected_fields = config_data.get('selected_fields', '[]')
|
||||||
|
if isinstance(selected_fields, str):
|
||||||
|
selected_fields = json.loads(selected_fields) if selected_fields else []
|
||||||
|
|
||||||
|
sum_fields = config_data.get('sum_fields', '[]')
|
||||||
|
if isinstance(sum_fields, str):
|
||||||
|
sum_fields = json.loads(sum_fields) if sum_fields else []
|
||||||
|
|
||||||
|
time_periods = config_data.get('time_periods', '{}')
|
||||||
|
if isinstance(time_periods, str):
|
||||||
|
time_periods = json.loads(time_periods) if time_periods else {}
|
||||||
|
|
||||||
|
field_custom_names = config_data.get('field_custom_names')
|
||||||
|
if isinstance(field_custom_names, str):
|
||||||
|
field_custom_names = json.loads(field_custom_names) if field_custom_names else None
|
||||||
|
|
||||||
|
sql = """
|
||||||
|
INSERT INTO t_daily_report_config
|
||||||
|
(id, config_name, split_type, split_value, split_name, selected_fields,
|
||||||
|
sum_fields, time_periods, merge_telecom, telecom_vehicle_no, field_custom_names,
|
||||||
|
show_monthly_total, is_active, sort_order, create_time, update_time)
|
||||||
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 0, %s, NOW(), NOW())
|
||||||
|
"""
|
||||||
|
|
||||||
|
params = (
|
||||||
|
new_id,
|
||||||
|
new_name,
|
||||||
|
split_type,
|
||||||
|
split_value,
|
||||||
|
config_data.get('split_name', ''),
|
||||||
|
json.dumps(selected_fields, ensure_ascii=False),
|
||||||
|
json.dumps(sum_fields, ensure_ascii=False),
|
||||||
|
json.dumps(time_periods, ensure_ascii=False),
|
||||||
|
config_data.get('merge_telecom', 0),
|
||||||
|
config_data.get('telecom_vehicle_no', ''),
|
||||||
|
json.dumps(field_custom_names, ensure_ascii=False) if field_custom_names else None,
|
||||||
|
config_data.get('show_monthly_total', 0),
|
||||||
|
sort_order
|
||||||
|
)
|
||||||
|
|
||||||
|
execute_insert(sql, params)
|
||||||
|
success_count += 1
|
||||||
|
except Exception as item_error:
|
||||||
|
fail_count += 1
|
||||||
|
fail_messages.append(f'第{idx+1}条:{str(item_error)}')
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'success': True,
|
||||||
|
'message': f'导入完成:成功 {success_count} 条,失败 {fail_count} 条',
|
||||||
|
'data': {
|
||||||
|
'success_count': success_count,
|
||||||
|
'fail_count': fail_count,
|
||||||
|
'fail_messages': fail_messages
|
||||||
|
}
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return jsonify({'success': False, 'message': f'导入失败: {str(e)}'}), 500
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ from lib.sum_data_collector import (
|
|||||||
update_sum_data,
|
update_sum_data,
|
||||||
delete_sum_data,
|
delete_sum_data,
|
||||||
batch_delete_sum_data,
|
batch_delete_sum_data,
|
||||||
get_monthly_charge_degree_total
|
get_monthly_charge_degree_total,
|
||||||
|
export_sum_data_to_excel
|
||||||
)
|
)
|
||||||
from . import sum_data_bp
|
from . import sum_data_bp
|
||||||
|
|
||||||
@@ -38,7 +39,8 @@ def list_sum_data():
|
|||||||
try:
|
try:
|
||||||
page = int(request.args.get('page', 1))
|
page = int(request.args.get('page', 1))
|
||||||
page_size = int(request.args.get('page_size', 20))
|
page_size = int(request.args.get('page_size', 20))
|
||||||
report_date = request.args.get('report_date')
|
start_date = request.args.get('start_date')
|
||||||
|
end_date = request.args.get('end_date')
|
||||||
config_id = request.args.get('config_id')
|
config_id = request.args.get('config_id')
|
||||||
config_name = request.args.get('config_name')
|
config_name = request.args.get('config_name')
|
||||||
sum_field_key = request.args.get('sum_field_key')
|
sum_field_key = request.args.get('sum_field_key')
|
||||||
@@ -47,7 +49,8 @@ def list_sum_data():
|
|||||||
result = get_sum_data_list(
|
result = get_sum_data_list(
|
||||||
page=page,
|
page=page,
|
||||||
page_size=page_size,
|
page_size=page_size,
|
||||||
report_date=report_date,
|
start_date=start_date,
|
||||||
|
end_date=end_date,
|
||||||
config_id=config_id,
|
config_id=config_id,
|
||||||
config_name=config_name,
|
config_name=config_name,
|
||||||
sum_field_key=sum_field_key,
|
sum_field_key=sum_field_key,
|
||||||
@@ -152,3 +155,39 @@ def monthly_charge_degree_total():
|
|||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return jsonify({'success': False, 'message': str(e)}), 500
|
return jsonify({'success': False, 'message': str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
|
@sum_data_bp.route('/sum-data/export', methods=['GET'])
|
||||||
|
def export_sum_data():
|
||||||
|
"""导出求和数据为Excel
|
||||||
|
|
||||||
|
参数:
|
||||||
|
start_date: 开始日期(可选)
|
||||||
|
end_date: 结束日期(可选)
|
||||||
|
config_id: 配置ID(可选)
|
||||||
|
data_source: 数据来源(可选)
|
||||||
|
keyword: 配置名称关键词(可选)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
start_date = request.args.get('start_date')
|
||||||
|
end_date = request.args.get('end_date')
|
||||||
|
config_id = request.args.get('config_id')
|
||||||
|
data_source = request.args.get('data_source')
|
||||||
|
keyword = request.args.get('keyword')
|
||||||
|
|
||||||
|
result = export_sum_data_to_excel(
|
||||||
|
start_date=start_date,
|
||||||
|
end_date=end_date,
|
||||||
|
config_id=config_id,
|
||||||
|
data_source=data_source,
|
||||||
|
keyword=keyword
|
||||||
|
)
|
||||||
|
|
||||||
|
if result['success']:
|
||||||
|
return jsonify(result)
|
||||||
|
else:
|
||||||
|
return jsonify(result), 400
|
||||||
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return jsonify({'success': False, 'message': str(e)}), 500
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ def init_database_tables():
|
|||||||
sum_field_name VARCHAR(100) NULL COMMENT '求和字段中文名称',
|
sum_field_name VARCHAR(100) NULL COMMENT '求和字段中文名称',
|
||||||
sum_value DECIMAL(18,3) NULL COMMENT '求和值',
|
sum_value DECIMAL(18,3) NULL COMMENT '求和值',
|
||||||
total_orders INT NULL COMMENT '订单数',
|
total_orders INT NULL COMMENT '订单数',
|
||||||
|
sort_order INT NULL COMMENT '配置排序号',
|
||||||
data_source VARCHAR(20) NULL COMMENT '数据来源 auto=自动采集 manual=手动编辑',
|
data_source VARCHAR(20) NULL COMMENT '数据来源 auto=自动采集 manual=手动编辑',
|
||||||
remark VARCHAR(500) NULL COMMENT '备注',
|
remark VARCHAR(500) NULL COMMENT '备注',
|
||||||
create_time DATETIME NULL COMMENT '创建时间',
|
create_time DATETIME NULL COMMENT '创建时间',
|
||||||
@@ -107,6 +108,18 @@ def init_database_tables():
|
|||||||
else:
|
else:
|
||||||
print(f"[数据库] 新增 show_monthly_total 字段时出错(可能已存在): {e}")
|
print(f"[数据库] 新增 show_monthly_total 字段时出错(可能已存在): {e}")
|
||||||
|
|
||||||
|
# 给求和数据表增加 sort_order 字段
|
||||||
|
try:
|
||||||
|
execute_update(
|
||||||
|
"ALTER TABLE t_daily_report_sum_data ADD COLUMN sort_order INT NULL COMMENT '配置排序号' "
|
||||||
|
)
|
||||||
|
print("[数据库] 求和数据表新增 sort_order 字段成功")
|
||||||
|
except Exception as e:
|
||||||
|
if "Duplicate" in str(e) or "duplicate" in str(e) or "Exists" in str(e) or "exists" in str(e):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
print(f"[数据库] 求和数据表新增 sort_order 字段时出错(可能已存在): {e}")
|
||||||
|
|
||||||
# 数据迁移:给 sort_order 为 NULL 的配置记录按 id 顺序赋值
|
# 数据迁移:给 sort_order 为 NULL 的配置记录按 id 顺序赋值
|
||||||
try:
|
try:
|
||||||
null_count = execute_query(
|
null_count = execute_query(
|
||||||
|
|||||||
@@ -474,7 +474,8 @@ def generate_daily_report(config_id, start_time=None, end_time=None):
|
|||||||
has_supplement=has_supplement,
|
has_supplement=has_supplement,
|
||||||
show_monthly_total=config.get('show_monthly_total', 0) == 1,
|
show_monthly_total=config.get('show_monthly_total', 0) == 1,
|
||||||
config_id=config['id'],
|
config_id=config['id'],
|
||||||
report_date=start_time.strftime('%Y-%m-%d')
|
report_date=start_time.strftime('%Y-%m-%d'),
|
||||||
|
sort_order=config.get('sort_order')
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"[日报生成] Excel文件已生成: {file_path}")
|
print(f"[日报生成] Excel文件已生成: {file_path}")
|
||||||
@@ -564,7 +565,7 @@ def generate_daily_report(config_id, start_time=None, end_time=None):
|
|||||||
def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
||||||
config_name, split_name, start_time, end_time,
|
config_name, split_name, start_time, end_time,
|
||||||
id_name_maps=None, field_custom_names=None, has_supplement=False,
|
id_name_maps=None, field_custom_names=None, has_supplement=False,
|
||||||
show_monthly_total=False, config_id=None, report_date=None):
|
show_monthly_total=False, config_id=None, report_date=None, sort_order=None):
|
||||||
"""
|
"""
|
||||||
生成Excel文件
|
生成Excel文件
|
||||||
|
|
||||||
@@ -585,7 +586,7 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
|||||||
reports_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'public', 'reports')
|
reports_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'public', 'reports')
|
||||||
os.makedirs(reports_dir, exist_ok=True)
|
os.makedirs(reports_dir, exist_ok=True)
|
||||||
|
|
||||||
# 生成文件名 - 格式:配置名称_几月几日消费记录.xlsx
|
# 生成文件名 - 格式:序号_配置名称_几月几日消费记录.xlsx
|
||||||
# 从 start_time 提取日期(报表数据对应的是开始时间那天的数据)
|
# 从 start_time 提取日期(报表数据对应的是开始时间那天的数据)
|
||||||
try:
|
try:
|
||||||
start_dt = datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S')
|
start_dt = datetime.strptime(start_time, '%Y-%m-%d %H:%M:%S')
|
||||||
@@ -595,7 +596,13 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
|||||||
|
|
||||||
# 如果有补单,添加标记
|
# 如果有补单,添加标记
|
||||||
supplement_suffix = '_有补单' if has_supplement else ''
|
supplement_suffix = '_有补单' if has_supplement else ''
|
||||||
filename = f'{config_name}_{date_str}消费记录{supplement_suffix}.xlsx'
|
|
||||||
|
# 顺序号前缀(两位数字,不足补零)
|
||||||
|
sort_prefix = ''
|
||||||
|
if sort_order:
|
||||||
|
sort_prefix = f'{sort_order:02d}_'
|
||||||
|
|
||||||
|
filename = f'{sort_prefix}{config_name}_{date_str}消费记录{supplement_suffix}.xlsx'
|
||||||
file_path = os.path.join(reports_dir, filename)
|
file_path = os.path.join(reports_dir, filename)
|
||||||
|
|
||||||
# 创建工作簿
|
# 创建工作簿
|
||||||
@@ -668,7 +675,15 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
|||||||
decimal3_fields = numeric_fields
|
decimal3_fields = numeric_fields
|
||||||
|
|
||||||
# 写入表头
|
# 写入表头
|
||||||
for col_idx, field in enumerate(selected_fields, 1):
|
# 第1列:序号
|
||||||
|
cell = ws.cell(row=header_row, column=1)
|
||||||
|
cell.value = '序号'
|
||||||
|
cell.font = header_font
|
||||||
|
cell.fill = header_fill
|
||||||
|
cell.alignment = header_alignment
|
||||||
|
cell.border = thin_border
|
||||||
|
|
||||||
|
for col_idx, field in enumerate(selected_fields, 2):
|
||||||
cell = ws.cell(row=header_row, column=col_idx)
|
cell = ws.cell(row=header_row, column=col_idx)
|
||||||
if field in field_custom_names and field_custom_names[field]:
|
if field in field_custom_names and field_custom_names[field]:
|
||||||
display_name = field_custom_names[field]
|
display_name = field_custom_names[field]
|
||||||
@@ -688,7 +703,17 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
|||||||
data_start_row = header_row + 1
|
data_start_row = header_row + 1
|
||||||
for row_idx, order in enumerate(orders, data_start_row):
|
for row_idx, order in enumerate(orders, data_start_row):
|
||||||
is_even = (row_idx - data_start_row) % 2 == 1
|
is_even = (row_idx - data_start_row) % 2 == 1
|
||||||
for col_idx, field in enumerate(selected_fields, 1):
|
|
||||||
|
# 第1列:序号
|
||||||
|
seq_num = row_idx - data_start_row + 1
|
||||||
|
seq_cell = ws.cell(row=row_idx, column=1)
|
||||||
|
seq_cell.value = seq_num
|
||||||
|
seq_cell.border = thin_border
|
||||||
|
seq_cell.alignment = Alignment(horizontal='center', vertical='center')
|
||||||
|
if is_even:
|
||||||
|
seq_cell.fill = even_row_fill
|
||||||
|
|
||||||
|
for col_idx, field in enumerate(selected_fields, 2):
|
||||||
cell = ws.cell(row=row_idx, column=col_idx)
|
cell = ws.cell(row=row_idx, column=col_idx)
|
||||||
value = order.get(field, '')
|
value = order.get(field, '')
|
||||||
|
|
||||||
@@ -734,7 +759,7 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
|||||||
ws.cell(row=sum_row, column=1).alignment = Alignment(horizontal='center', vertical='center')
|
ws.cell(row=sum_row, column=1).alignment = Alignment(horizontal='center', vertical='center')
|
||||||
ws.cell(row=sum_row, column=1).border = thin_border
|
ws.cell(row=sum_row, column=1).border = thin_border
|
||||||
|
|
||||||
for col_idx, field in enumerate(selected_fields, 1):
|
for col_idx, field in enumerate(selected_fields, 2):
|
||||||
cell = ws.cell(row=sum_row, column=col_idx)
|
cell = ws.cell(row=sum_row, column=col_idx)
|
||||||
cell.fill = sum_fill
|
cell.fill = sum_fill
|
||||||
cell.border = thin_border
|
cell.border = thin_border
|
||||||
@@ -767,9 +792,9 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
|||||||
# 月度总计行(在合计行下面空一行)
|
# 月度总计行(在合计行下面空一行)
|
||||||
monthly_row = sum_row + 2 if sum_fields else sum_row + 1
|
monthly_row = sum_row + 2 if sum_fields else sum_row + 1
|
||||||
|
|
||||||
# 找 charge_degree 字段所在的列
|
# 找 charge_degree 字段所在的列(+1因为序号占了第1列)
|
||||||
charge_degree_col = None
|
charge_degree_col = None
|
||||||
for col_idx, field in enumerate(selected_fields, 1):
|
for col_idx, field in enumerate(selected_fields, 2):
|
||||||
if field == 'charge_degree':
|
if field == 'charge_degree':
|
||||||
charge_degree_col = col_idx
|
charge_degree_col = col_idx
|
||||||
break
|
break
|
||||||
@@ -782,7 +807,7 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
|||||||
ws.cell(row=monthly_row, column=1).border = thin_border
|
ws.cell(row=monthly_row, column=1).border = thin_border
|
||||||
|
|
||||||
# 其他列也填充背景色
|
# 其他列也填充背景色
|
||||||
for col_idx in range(2, len(selected_fields) + 1):
|
for col_idx in range(2, len(selected_fields) + 2):
|
||||||
cell = ws.cell(row=monthly_row, column=col_idx)
|
cell = ws.cell(row=monthly_row, column=col_idx)
|
||||||
cell.fill = monthly_fill
|
cell.fill = monthly_fill
|
||||||
cell.border = thin_border
|
cell.border = thin_border
|
||||||
@@ -799,7 +824,11 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
|||||||
print(f"[日报生成] 写入月度总计时出错: {e}")
|
print(f"[日报生成] 写入月度总计时出错: {e}")
|
||||||
|
|
||||||
# 调整列宽(中文按2个字符宽度计算)
|
# 调整列宽(中文按2个字符宽度计算)
|
||||||
for col_idx, field in enumerate(selected_fields, 1):
|
# 序号列固定宽度
|
||||||
|
from openpyxl.utils import get_column_letter
|
||||||
|
ws.column_dimensions['A'].width = 8
|
||||||
|
|
||||||
|
for col_idx, field in enumerate(selected_fields, 2):
|
||||||
header_name = get_field_display_name(field)
|
header_name = get_field_display_name(field)
|
||||||
if field in field_custom_names and field_custom_names[field]:
|
if field in field_custom_names and field_custom_names[field]:
|
||||||
header_name = field_custom_names[field]
|
header_name = field_custom_names[field]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
日报求和数据采集与管理
|
日报求和数据采集与管理
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from lib.db import execute_query, execute_update, execute_insert
|
from lib.db import execute_query, execute_update, execute_insert
|
||||||
from lib.field_mapping import get_field_display_name
|
from lib.field_mapping import get_field_display_name
|
||||||
@@ -35,6 +36,7 @@ def collect_sum_data_from_history(history_id=None):
|
|||||||
|
|
||||||
collected_count = 0
|
collected_count = 0
|
||||||
skipped_count = 0
|
skipped_count = 0
|
||||||
|
config_sort_cache = {} # 缓存配置的排序号
|
||||||
|
|
||||||
for history in history_list:
|
for history in history_list:
|
||||||
sum_results = history.get('sum_results')
|
sum_results = history.get('sum_results')
|
||||||
@@ -45,6 +47,24 @@ def collect_sum_data_from_history(history_id=None):
|
|||||||
elif sum_results is None:
|
elif sum_results is None:
|
||||||
sum_results = {}
|
sum_results = {}
|
||||||
|
|
||||||
|
# 获取配置的排序号(缓存)
|
||||||
|
config_id = history.get('config_id')
|
||||||
|
sort_order = None
|
||||||
|
if config_id:
|
||||||
|
if config_id in config_sort_cache:
|
||||||
|
sort_order = config_sort_cache[config_id]
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
cfg_result = execute_query(
|
||||||
|
'SELECT sort_order FROM t_daily_report_config WHERE id = %s LIMIT 1',
|
||||||
|
(config_id,)
|
||||||
|
)
|
||||||
|
if cfg_result and cfg_result[0]['sort_order'] is not None:
|
||||||
|
sort_order = cfg_result[0]['sort_order']
|
||||||
|
config_sort_cache[config_id] = sort_order
|
||||||
|
except Exception:
|
||||||
|
config_sort_cache[config_id] = None
|
||||||
|
|
||||||
# 强制确保有充电电量(charge_degree)
|
# 强制确保有充电电量(charge_degree)
|
||||||
if 'charge_degree' not in sum_results or sum_results['charge_degree'] is None:
|
if 'charge_degree' not in sum_results or sum_results['charge_degree'] is None:
|
||||||
# 从订单表重新计算充电电量
|
# 从订单表重新计算充电电量
|
||||||
@@ -75,9 +95,9 @@ def collect_sum_data_from_history(history_id=None):
|
|||||||
sql = '''
|
sql = '''
|
||||||
INSERT INTO t_daily_report_sum_data
|
INSERT INTO t_daily_report_sum_data
|
||||||
(id, report_date, config_id, config_name, split_type, split_value, split_name,
|
(id, report_date, config_id, config_name, split_type, split_value, split_name,
|
||||||
sum_field_key, sum_field_name, sum_value, total_orders, data_source, remark,
|
sum_field_key, sum_field_name, sum_value, total_orders, sort_order, data_source, remark,
|
||||||
create_time, update_time)
|
create_time, update_time)
|
||||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
|
||||||
'''
|
'''
|
||||||
params = (
|
params = (
|
||||||
sum_data_id,
|
sum_data_id,
|
||||||
@@ -91,6 +111,7 @@ def collect_sum_data_from_history(history_id=None):
|
|||||||
get_field_display_name(field_key),
|
get_field_display_name(field_key),
|
||||||
float(field_value) if field_value is not None else 0,
|
float(field_value) if field_value is not None else 0,
|
||||||
history.get('total_orders', 0),
|
history.get('total_orders', 0),
|
||||||
|
sort_order,
|
||||||
'auto',
|
'auto',
|
||||||
f'从历史记录自动采集,历史ID: {history["id"]}'
|
f'从历史记录自动采集,历史ID: {history["id"]}'
|
||||||
)
|
)
|
||||||
@@ -161,7 +182,7 @@ def _calculate_charge_degree(history):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_sum_data_list(page=1, page_size=20, report_date=None, config_id=None,
|
def get_sum_data_list(page=1, page_size=20, start_date=None, end_date=None, config_id=None,
|
||||||
config_name=None, sum_field_key=None, data_source=None):
|
config_name=None, sum_field_key=None, data_source=None):
|
||||||
"""
|
"""
|
||||||
查询求和数据列表
|
查询求和数据列表
|
||||||
@@ -169,7 +190,8 @@ def get_sum_data_list(page=1, page_size=20, report_date=None, config_id=None,
|
|||||||
Args:
|
Args:
|
||||||
page: 页码
|
page: 页码
|
||||||
page_size: 每页大小
|
page_size: 每页大小
|
||||||
report_date: 报表日期(可选)
|
start_date: 开始日期(可选)
|
||||||
|
end_date: 结束日期(可选)
|
||||||
config_id: 配置ID(可选)
|
config_id: 配置ID(可选)
|
||||||
config_name: 配置名称(可选,模糊搜索)
|
config_name: 配置名称(可选,模糊搜索)
|
||||||
sum_field_key: 求和字段键名(可选)
|
sum_field_key: 求和字段键名(可选)
|
||||||
@@ -184,9 +206,13 @@ def get_sum_data_list(page=1, page_size=20, report_date=None, config_id=None,
|
|||||||
where_clauses = ['1=1']
|
where_clauses = ['1=1']
|
||||||
params = []
|
params = []
|
||||||
|
|
||||||
if report_date:
|
if start_date:
|
||||||
where_clauses.append('report_date = %s')
|
where_clauses.append('report_date >= %s')
|
||||||
params.append(report_date)
|
params.append(start_date)
|
||||||
|
|
||||||
|
if end_date:
|
||||||
|
where_clauses.append('report_date <= %s')
|
||||||
|
params.append(end_date)
|
||||||
|
|
||||||
if config_id:
|
if config_id:
|
||||||
where_clauses.append('config_id = %s')
|
where_clauses.append('config_id = %s')
|
||||||
@@ -217,7 +243,7 @@ def get_sum_data_list(page=1, page_size=20, report_date=None, config_id=None,
|
|||||||
list_result = execute_query(
|
list_result = execute_query(
|
||||||
f'''SELECT * FROM t_daily_report_sum_data
|
f'''SELECT * FROM t_daily_report_sum_data
|
||||||
WHERE {where_sql}
|
WHERE {where_sql}
|
||||||
ORDER BY report_date DESC, config_id, id DESC
|
ORDER BY report_date DESC, sort_order ASC, config_id ASC, id DESC
|
||||||
LIMIT %s OFFSET %s''',
|
LIMIT %s OFFSET %s''',
|
||||||
tuple(params) + (page_size, offset)
|
tuple(params) + (page_size, offset)
|
||||||
)
|
)
|
||||||
@@ -249,12 +275,27 @@ def add_sum_data(data):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
sum_data_id = int(datetime.now().timestamp() * 1000)
|
sum_data_id = int(datetime.now().timestamp() * 1000)
|
||||||
|
|
||||||
|
# 获取配置的排序号
|
||||||
|
sort_order = None
|
||||||
|
config_id = data.get('config_id')
|
||||||
|
if config_id:
|
||||||
|
try:
|
||||||
|
cfg_result = execute_query(
|
||||||
|
'SELECT sort_order FROM t_daily_report_config WHERE id = %s LIMIT 1',
|
||||||
|
(config_id,)
|
||||||
|
)
|
||||||
|
if cfg_result and cfg_result[0]['sort_order'] is not None:
|
||||||
|
sort_order = cfg_result[0]['sort_order']
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
sql = '''
|
sql = '''
|
||||||
INSERT INTO t_daily_report_sum_data
|
INSERT INTO t_daily_report_sum_data
|
||||||
(id, report_date, config_id, config_name, split_type, split_value, split_name,
|
(id, report_date, config_id, config_name, split_type, split_value, split_name,
|
||||||
sum_field_key, sum_field_name, sum_value, total_orders, data_source, remark,
|
sum_field_key, sum_field_name, sum_value, total_orders, sort_order, data_source, remark,
|
||||||
create_time, update_time)
|
create_time, update_time)
|
||||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
|
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, NOW(), NOW())
|
||||||
'''
|
'''
|
||||||
params = (
|
params = (
|
||||||
sum_data_id,
|
sum_data_id,
|
||||||
@@ -268,6 +309,7 @@ def add_sum_data(data):
|
|||||||
data.get('sum_field_name', ''),
|
data.get('sum_field_name', ''),
|
||||||
float(data.get('sum_value', 0)),
|
float(data.get('sum_value', 0)),
|
||||||
data.get('total_orders', 0),
|
data.get('total_orders', 0),
|
||||||
|
sort_order,
|
||||||
'manual',
|
'manual',
|
||||||
data.get('remark', '')
|
data.get('remark', '')
|
||||||
)
|
)
|
||||||
@@ -409,13 +451,176 @@ def batch_delete_sum_data(ids):
|
|||||||
return {'success': False, 'message': str(e)}
|
return {'success': False, 'message': str(e)}
|
||||||
|
|
||||||
|
|
||||||
|
def export_sum_data_to_excel(start_date=None, end_date=None, config_id=None, data_source=None, keyword=None):
|
||||||
|
"""
|
||||||
|
导出求和数据为Excel
|
||||||
|
|
||||||
|
Args:
|
||||||
|
start_date: 开始日期(可选)
|
||||||
|
end_date: 结束日期(可选)
|
||||||
|
config_id: 配置ID(可选)
|
||||||
|
data_source: 数据来源(可选)
|
||||||
|
keyword: 配置名称关键词(可选)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: {success, message, file_path}
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from openpyxl import Workbook
|
||||||
|
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
|
||||||
|
|
||||||
|
# 构建查询条件
|
||||||
|
where_clauses = []
|
||||||
|
params = []
|
||||||
|
|
||||||
|
if start_date:
|
||||||
|
where_clauses.append('report_date >= %s')
|
||||||
|
params.append(start_date)
|
||||||
|
|
||||||
|
if end_date:
|
||||||
|
where_clauses.append('report_date <= %s')
|
||||||
|
params.append(end_date)
|
||||||
|
|
||||||
|
if config_id:
|
||||||
|
where_clauses.append('config_id = %s')
|
||||||
|
params.append(config_id)
|
||||||
|
|
||||||
|
if data_source:
|
||||||
|
where_clauses.append('data_source = %s')
|
||||||
|
params.append(data_source)
|
||||||
|
|
||||||
|
if keyword:
|
||||||
|
where_clauses.append('config_name LIKE %s')
|
||||||
|
params.append(f'%{keyword}%')
|
||||||
|
|
||||||
|
where_sql = ''
|
||||||
|
if where_clauses:
|
||||||
|
where_sql = 'WHERE ' + ' AND '.join(where_clauses)
|
||||||
|
|
||||||
|
# 查询数据(不分页,全量导出)
|
||||||
|
sql = f'''
|
||||||
|
SELECT id, report_date, config_id, config_name, split_type, split_value, split_name,
|
||||||
|
sum_field_key, sum_field_name, sum_value, total_orders, sort_order, data_source, remark,
|
||||||
|
create_time, update_time
|
||||||
|
FROM t_daily_report_sum_data
|
||||||
|
{where_sql}
|
||||||
|
ORDER BY report_date DESC, sort_order ASC, config_id ASC, sum_field_key ASC
|
||||||
|
'''
|
||||||
|
rows = execute_query(sql, tuple(params) if params else ())
|
||||||
|
|
||||||
|
# 创建Excel
|
||||||
|
wb = Workbook()
|
||||||
|
ws = wb.active
|
||||||
|
ws.title = '求和数据'
|
||||||
|
|
||||||
|
# 表头
|
||||||
|
headers = ['序号', '报表日期', '配置名称', '拆分方式', '拆分值', '求和字段', '求和值', '订单数', '数据来源', '修改原因/备注', '创建时间', '更新时间']
|
||||||
|
|
||||||
|
header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
|
||||||
|
header_font = Font(bold=True, color='FFFFFF', size=11)
|
||||||
|
header_alignment = Alignment(horizontal='center', vertical='center')
|
||||||
|
|
||||||
|
thin_border = Border(
|
||||||
|
left=Side(style='thin', color='000000'),
|
||||||
|
right=Side(style='thin', color='000000'),
|
||||||
|
top=Side(style='thin', color='000000'),
|
||||||
|
bottom=Side(style='thin', color='000000')
|
||||||
|
)
|
||||||
|
|
||||||
|
even_fill = PatternFill(start_color='F2F2F2', end_color='F2F2F2', fill_type='solid')
|
||||||
|
|
||||||
|
# 写入表头
|
||||||
|
for col_idx, header in enumerate(headers, 1):
|
||||||
|
cell = ws.cell(row=1, column=col_idx)
|
||||||
|
cell.value = header
|
||||||
|
cell.font = header_font
|
||||||
|
cell.fill = header_fill
|
||||||
|
cell.alignment = header_alignment
|
||||||
|
cell.border = thin_border
|
||||||
|
|
||||||
|
ws.row_dimensions[1].height = 25
|
||||||
|
|
||||||
|
# 拆分方式映射
|
||||||
|
split_type_map = {
|
||||||
|
'company_id': '按企业ID',
|
||||||
|
'user_id': '按用户ID',
|
||||||
|
'station_id': '按场站ID'
|
||||||
|
}
|
||||||
|
|
||||||
|
# 数据来源映射
|
||||||
|
source_map = {
|
||||||
|
'auto': '自动采集',
|
||||||
|
'manual': '手动编辑'
|
||||||
|
}
|
||||||
|
|
||||||
|
# 写入数据
|
||||||
|
for row_idx, row in enumerate(rows, 2):
|
||||||
|
is_even = (row_idx - 2) % 2 == 1
|
||||||
|
|
||||||
|
values = [
|
||||||
|
row.get('sort_order') or '',
|
||||||
|
row.get('report_date', ''),
|
||||||
|
row.get('config_name', ''),
|
||||||
|
split_type_map.get(row.get('split_type', ''), row.get('split_type', '')),
|
||||||
|
row.get('split_name', '') or row.get('split_value', ''),
|
||||||
|
row.get('sum_field_name', ''),
|
||||||
|
float(row.get('sum_value', 0) or 0),
|
||||||
|
int(row.get('total_orders', 0) or 0),
|
||||||
|
source_map.get(row.get('data_source', ''), row.get('data_source', '')),
|
||||||
|
row.get('remark', ''),
|
||||||
|
str(row.get('create_time', '')) if row.get('create_time') else '',
|
||||||
|
str(row.get('update_time', '')) if row.get('update_time') else ''
|
||||||
|
]
|
||||||
|
|
||||||
|
for col_idx, val in enumerate(values, 1):
|
||||||
|
cell = ws.cell(row=row_idx, column=col_idx)
|
||||||
|
cell.value = val
|
||||||
|
cell.border = thin_border
|
||||||
|
|
||||||
|
# 对齐方式:数字靠右,其他居中或靠左
|
||||||
|
if col_idx in [7, 8]: # 求和值、订单数
|
||||||
|
cell.alignment = Alignment(horizontal='right', vertical='center')
|
||||||
|
if col_idx == 7:
|
||||||
|
cell.number_format = '0.000'
|
||||||
|
elif col_idx in [1, 2, 4, 9]: # 序号、日期、拆分方式、数据来源
|
||||||
|
cell.alignment = Alignment(horizontal='center', vertical='center')
|
||||||
|
else:
|
||||||
|
cell.alignment = Alignment(horizontal='left', vertical='center')
|
||||||
|
|
||||||
|
if is_even:
|
||||||
|
cell.fill = even_fill
|
||||||
|
|
||||||
|
ws.row_dimensions[row_idx].height = 22
|
||||||
|
|
||||||
|
# 调整列宽
|
||||||
|
col_widths = [8, 14, 20, 12, 20, 18, 14, 10, 12, 30, 20, 20]
|
||||||
|
for col_idx, width in enumerate(col_widths, 1):
|
||||||
|
from openpyxl.utils import get_column_letter
|
||||||
|
col_letter = get_column_letter(col_idx)
|
||||||
|
ws.column_dimensions[col_letter].width = width
|
||||||
|
|
||||||
|
# 冻结首行
|
||||||
|
ws.freeze_panes = 'A2'
|
||||||
|
|
||||||
|
# 保存文件
|
||||||
|
reports_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'public', 'reports')
|
||||||
|
os.makedirs(reports_dir, exist_ok=True)
|
||||||
|
|
||||||
|
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||||
|
filename = f'求和数据导出_{timestamp}.xlsx'
|
||||||
|
file_path = os.path.join(reports_dir, filename)
|
||||||
|
wb.save(file_path)
|
||||||
|
|
||||||
|
return {'success': True, 'message': '导出成功', 'file_path': f'/public/reports/{filename}', 'filename': filename, 'total': len(rows)}
|
||||||
|
except Exception as e:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return {'success': False, 'message': str(e)}
|
||||||
|
|
||||||
|
|
||||||
def get_monthly_charge_degree_total(config_id=None, month=None):
|
def get_monthly_charge_degree_total(config_id=None, month=None):
|
||||||
"""
|
"""
|
||||||
获取当月充电电量累计(从求和数据表查询,对账后的准确数据)
|
获取当月充电电量累计(从求和数据表查询,对账后的准确数据)
|
||||||
|
|
||||||
注意:
|
|
||||||
- 数据来源于 t_daily_report_sum_data 表(线下对账后的数据,可能被手动修改过)
|
|
||||||
- 只统计 sum_field_key = 'charge_degree' 的记录
|
|
||||||
- 如果当天已经生成了日报并采集,自动包含在内,不会重复计算
|
- 如果当天已经生成了日报并采集,自动包含在内,不会重复计算
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
@@ -208,6 +208,15 @@ color: white;
|
|||||||
background: #d35400;
|
background: #d35400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-info {
|
||||||
|
background: #17a2b8;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-info:hover {
|
||||||
|
background: #138496;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-secondary {
|
.btn-secondary {
|
||||||
background: #eef2f7;
|
background: #eef2f7;
|
||||||
color: #555;
|
color: #555;
|
||||||
|
|||||||
@@ -23,15 +23,17 @@ function renderConfigList(configs) {
|
|||||||
const tbody = document.getElementById('config-list');
|
const tbody = document.getElementById('config-list');
|
||||||
|
|
||||||
if (configs.length === 0) {
|
if (configs.length === 0) {
|
||||||
tbody.innerHTML = '<tr><td colspan="6" class="empty">暂无配置,请点击"创建配置"按钮</td></tr>';
|
tbody.innerHTML = '<tr><td colspan="7" class="empty">暂无配置,请点击"创建配置"按钮</td></tr>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody.innerHTML = configs.map((config, index) => {
|
tbody.innerHTML = configs.map((config, index) => {
|
||||||
const isFirst = index === 0;
|
const isFirst = index === 0;
|
||||||
const isLast = index === configs.length - 1;
|
const isLast = index === configs.length - 1;
|
||||||
|
const sortOrder = index + 1;
|
||||||
return `
|
return `
|
||||||
<tr>
|
<tr>
|
||||||
|
<td style="text-align: center; font-weight: bold; color: #4472C4;">${sortOrder}</td>
|
||||||
<td>${config.config_name}</td>
|
<td>${config.config_name}</td>
|
||||||
<td>${config.split_type === 'company_id' ? '按企业 ID' : config.split_type === 'user_id' ? '按用户 ID' : '按场站 ID'}</td>
|
<td>${config.split_type === 'company_id' ? '按企业 ID' : config.split_type === 'user_id' ? '按用户 ID' : '按场站 ID'}</td>
|
||||||
<td>${config.split_name || config.split_value}</td>
|
<td>${config.split_name || config.split_value}</td>
|
||||||
@@ -879,3 +881,56 @@ function onSplitTypeChange() {
|
|||||||
document.getElementById('entity-list').innerHTML = '<div class="empty">请先选择拆分方式</div>';
|
document.getElementById('entity-list').innerHTML = '<div class="empty">请先选择拆分方式</div>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 导出配置
|
||||||
|
function exportConfigs() {
|
||||||
|
try {
|
||||||
|
window.open('/api/config/export', '_blank');
|
||||||
|
} catch (error) {
|
||||||
|
alert('导出失败: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入配置
|
||||||
|
async function importConfigs(event) {
|
||||||
|
const fileInput = event.target;
|
||||||
|
const file = fileInput.files[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
if (!file.name.endsWith('.json')) {
|
||||||
|
alert('只支持 JSON 格式的配置文件');
|
||||||
|
fileInput.value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!confirm('确定要导入配置吗?\n导入的配置会自动命名为"原名称_导入",并默认禁用。')) {
|
||||||
|
fileInput.value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/config/import', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
});
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
let message = result.message;
|
||||||
|
if (result.data && result.data.fail_messages && result.data.fail_messages.length > 0) {
|
||||||
|
message += '\n\n失败详情:\n' + result.data.fail_messages.join('\n');
|
||||||
|
}
|
||||||
|
alert(message);
|
||||||
|
loadConfigs();
|
||||||
|
} else {
|
||||||
|
alert('导入失败: ' + (result.message || '未知错误'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
alert('导入失败: ' + error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
fileInput.value = '';
|
||||||
|
}
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ function renderSumDataList(rawList) {
|
|||||||
split_type: item.split_type,
|
split_type: item.split_type,
|
||||||
split_value: item.split_value,
|
split_value: item.split_value,
|
||||||
split_name: item.split_name,
|
split_name: item.split_name,
|
||||||
|
sort_order: item.sort_order || 9999,
|
||||||
total_orders: item.total_orders || 0,
|
total_orders: item.total_orders || 0,
|
||||||
data_source: item.data_source,
|
data_source: item.data_source,
|
||||||
remark: item.remark || '',
|
remark: item.remark || '',
|
||||||
@@ -97,7 +98,7 @@ function renderSumDataList(rawList) {
|
|||||||
|
|
||||||
sumDataGroupedList = Object.values(groupMap).sort((a, b) => {
|
sumDataGroupedList = Object.values(groupMap).sort((a, b) => {
|
||||||
if (a.report_date !== b.report_date) return b.report_date.localeCompare(a.report_date);
|
if (a.report_date !== b.report_date) return b.report_date.localeCompare(a.report_date);
|
||||||
return (a.config_name || '').localeCompare(b.config_name || '');
|
return (a.sort_order || 9999) - (b.sort_order || 9999);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 分页处理
|
// 分页处理
|
||||||
@@ -176,12 +177,21 @@ function renderSumDataPagination(total) {
|
|||||||
// 加载求和数据
|
// 加载求和数据
|
||||||
async function loadSumData(page = 1) {
|
async function loadSumData(page = 1) {
|
||||||
sumDataCurrentPage = page;
|
sumDataCurrentPage = page;
|
||||||
const reportDate = document.getElementById('sum-data-report-date').value;
|
const startDate = document.getElementById('sum-data-start-date').value;
|
||||||
|
const endDate = document.getElementById('sum-data-end-date').value;
|
||||||
const configName = document.getElementById('sum-data-config-name').value;
|
const configName = document.getElementById('sum-data-config-name').value;
|
||||||
const dataSource = document.getElementById('sum-data-source').value;
|
const dataSource = document.getElementById('sum-data-source').value;
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append('page', '1');
|
||||||
|
params.append('page_size', '5000');
|
||||||
|
if (startDate) params.append('start_date', startDate);
|
||||||
|
if (endDate) params.append('end_date', endDate);
|
||||||
|
if (configName) params.append('config_name', configName);
|
||||||
|
if (dataSource) params.append('data_source', dataSource);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/sum-data?page=1&page_size=5000&report_date=${encodeURIComponent(reportDate)}&config_name=${encodeURIComponent(configName)}&data_source=${encodeURIComponent(dataSource)}`);
|
const response = await fetch(`/api/sum-data?${params.toString()}`);
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
renderSumDataList(result.data.list || []);
|
renderSumDataList(result.data.list || []);
|
||||||
@@ -195,7 +205,8 @@ async function loadSumData(page = 1) {
|
|||||||
|
|
||||||
// 清除查询
|
// 清除查询
|
||||||
function clearSumDataQuery() {
|
function clearSumDataQuery() {
|
||||||
document.getElementById('sum-data-report-date').value = '';
|
document.getElementById('sum-data-start-date').value = '';
|
||||||
|
document.getElementById('sum-data-end-date').value = '';
|
||||||
document.getElementById('sum-data-config-name').value = '';
|
document.getElementById('sum-data-config-name').value = '';
|
||||||
document.getElementById('sum-data-source').value = '';
|
document.getElementById('sum-data-source').value = '';
|
||||||
loadSumData(1);
|
loadSumData(1);
|
||||||
@@ -525,3 +536,33 @@ async function batchDeleteSumData() {
|
|||||||
alert('删除失败: ' + error.message);
|
alert('删除失败: ' + error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function exportSumData() {
|
||||||
|
const startDate = document.getElementById('sum-data-start-date').value;
|
||||||
|
const endDate = document.getElementById('sum-data-end-date').value;
|
||||||
|
const configName = document.getElementById('sum-data-config-name').value;
|
||||||
|
const dataSource = document.getElementById('sum-data-source').value;
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
if (startDate) params.append('start_date', startDate);
|
||||||
|
if (endDate) params.append('end_date', endDate);
|
||||||
|
if (configName) params.append('keyword', configName);
|
||||||
|
if (dataSource) params.append('data_source', dataSource);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/sum-data/export?${params.toString()}`);
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
if (result.total === 0) {
|
||||||
|
alert('没有可导出的数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.open(result.file_path, '_blank');
|
||||||
|
} else {
|
||||||
|
alert('导出失败: ' + (result.message || '未知错误'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
alert('导出失败: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>订单日报系统</title>
|
<title>订单日报系统</title>
|
||||||
<link rel="stylesheet" href="/public/static/css/style.css?v=2026071402">
|
<link rel="stylesheet" href="/public/static/css/style.css?v=2026071404">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -21,12 +21,16 @@
|
|||||||
<div id="config-tab" class="tab-content active">
|
<div id="config-tab" class="tab-content active">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<button class="btn btn-primary" onclick="showCreateModal()">创建配置</button>
|
<button class="btn btn-primary" onclick="showCreateModal()">创建配置</button>
|
||||||
|
<button class="btn btn-info" onclick="exportConfigs()">导出配置</button>
|
||||||
|
<button class="btn btn-success" onclick="document.getElementById('import-config-file').click()">导入配置</button>
|
||||||
|
<input type="file" id="import-config-file" accept=".json" style="display: none;" onchange="importConfigs(event)">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th style="min-width: 60px; width: 60px;">序号</th>
|
||||||
<th>配置名称</th>
|
<th>配置名称</th>
|
||||||
<th>拆分方式</th>
|
<th>拆分方式</th>
|
||||||
<th>拆分值</th>
|
<th>拆分值</th>
|
||||||
@@ -36,7 +40,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="config-list">
|
<tbody id="config-list">
|
||||||
<tr><td colspan="6" class="loading">加载中...</td></tr>
|
<tr><td colspan="7" class="loading">加载中...</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -122,7 +126,9 @@
|
|||||||
<div id="sum-data-tab" class="tab-content">
|
<div id="sum-data-tab" class="tab-content">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div style="display: flex; gap: 10px; margin-bottom: 20px; flex-wrap: wrap; align-items: center;">
|
<div style="display: flex; gap: 10px; margin-bottom: 20px; flex-wrap: wrap; align-items: center;">
|
||||||
<input type="date" id="sum-data-report-date" class="form-control" style="width: auto;">
|
<input type="date" id="sum-data-start-date" class="form-control" style="width: auto;">
|
||||||
|
<span style="color: #999;">至</span>
|
||||||
|
<input type="date" id="sum-data-end-date" class="form-control" style="width: auto;">
|
||||||
<input type="text" id="sum-data-config-name" class="form-control" style="width: 180px;" placeholder="配置名称搜索">
|
<input type="text" id="sum-data-config-name" class="form-control" style="width: 180px;" placeholder="配置名称搜索">
|
||||||
<select id="sum-data-source" class="form-control" style="width: auto;" onchange="loadSumData()">
|
<select id="sum-data-source" class="form-control" style="width: auto;" onchange="loadSumData()">
|
||||||
<option value="">全部来源</option>
|
<option value="">全部来源</option>
|
||||||
@@ -133,6 +139,7 @@
|
|||||||
<button class="btn" onclick="clearSumDataQuery()">清除</button>
|
<button class="btn" onclick="clearSumDataQuery()">清除</button>
|
||||||
<button class="btn btn-success" onclick="showAddSumDataModal()">手动添加</button>
|
<button class="btn btn-success" onclick="showAddSumDataModal()">手动添加</button>
|
||||||
<button class="btn btn-warning" onclick="collectSumDataFromHistory()">从历史采集</button>
|
<button class="btn btn-warning" onclick="collectSumDataFromHistory()">从历史采集</button>
|
||||||
|
<button class="btn btn-info" onclick="exportSumData()">导出</button>
|
||||||
<button class="btn btn-danger" onclick="batchDeleteSumData()" style="margin-left: auto;">批量删除</button>
|
<button class="btn btn-danger" onclick="batchDeleteSumData()" style="margin-left: auto;">批量删除</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -332,11 +339,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- JavaScript 文件 -->
|
<!-- JavaScript 文件 -->
|
||||||
<script src="/public/static/js/common.js?v=2026071402"></script>
|
<script src="/public/static/js/common.js?v=2026071404"></script>
|
||||||
<script src="/public/static/js/config.js?v=2026071402"></script>
|
<script src="/public/static/js/config.js?v=2026071404"></script>
|
||||||
<script src="/public/static/js/entity.js?v=2026071402"></script>
|
<script src="/public/static/js/entity.js?v=2026071404"></script>
|
||||||
<script src="/public/static/js/report.js?v=2026071402"></script>
|
<script src="/public/static/js/report.js?v=2026071404"></script>
|
||||||
<script src="/public/static/js/history.js?v=2026071402"></script>
|
<script src="/public/static/js/history.js?v=2026071404"></script>
|
||||||
<script src="/public/static/js/sum_data.js?v=2026071402"></script>
|
<script src="/public/static/js/sum_data.js?v=2026071404"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user