refactor: 重构项目结构,按功能模块拆分文件
- 后端:将 app.py 拆分为 lib/api/ 目录下的多个蓝图模块 - config_api.py: 配置管理 API - entity_api.py: 实体管理 API - field_api.py: 字段列表 API - report_api.py: 日报生成和历史记录 API - init_api.py: 数据库初始化 API - download_api.py: 文件下载 API - 前端:将 index.html 拆分为多个文件 - static/css/style.css: CSS 样式 - static/js/common.js: 通用工具函数 - static/js/config.js: 配置管理 - static/js/entity.js: 实体管理 - static/js/report.js: 日报生成 - static/js/history.js: 历史记录 - 更新 app.py 使用蓝图注册 API - 创建 AGENTS.md 项目文档 Coze-Commit-Type: user Coze-User-ID: 3722323274763196 Coze-Conversation-ID: 9894087
This commit is contained in:
225
static/js/history.js
Normal file
225
static/js/history.js
Normal file
@@ -0,0 +1,225 @@
|
||||
// 批量下载历史记录
|
||||
async function batchDownloadHistory() {
|
||||
const checkboxes = document.querySelectorAll('.history-checkbox:checked');
|
||||
if (checkboxes.length === 0) {
|
||||
alert('请至少选择一条记录');
|
||||
return;
|
||||
}
|
||||
|
||||
const ids = Array.from(checkboxes).map(cb => parseInt(cb.value));
|
||||
|
||||
// 逐个下载
|
||||
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);
|
||||
}
|
||||
|
||||
// 带日期参数加载历史记录
|
||||
// 渲染历史记录列表
|
||||
function renderHistory(list) {
|
||||
const tbody = document.getElementById('history-list');
|
||||
if (!list || list.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="9" class="empty">暂无历史记录</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析日期字符串(支持多种格式)
|
||||
const parseDate = (dateStr) => {
|
||||
if (!dateStr) return null;
|
||||
|
||||
// 尝试匹配 "2026-07-12" 格式
|
||||
let match = dateStr.match(/(\d{4})-(\d{1,2})-(\d{1,2})/);
|
||||
if (match) {
|
||||
return {
|
||||
year: parseInt(match[1]),
|
||||
month: parseInt(match[2]),
|
||||
day: parseInt(match[3])
|
||||
};
|
||||
}
|
||||
|
||||
// 尝试匹配 "Sun, 12 Jul 2026 00:00:00 GMT" 格式
|
||||
match = dateStr.match(/(\d{1,2})\s+(\w+)\s+(\d{4})/);
|
||||
if (match) {
|
||||
const months = {
|
||||
'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6,
|
||||
'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12
|
||||
};
|
||||
return {
|
||||
year: parseInt(match[3]),
|
||||
month: months[match[2]] || 1,
|
||||
day: parseInt(match[1])
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// 解析时间字符串(支持多种格式)
|
||||
const parseTime = (timeStr) => {
|
||||
if (!timeStr) return null;
|
||||
|
||||
// 尝试匹配 "2026-07-12 08:00:00" 格式
|
||||
let match = timeStr.match(/(\d{4})-(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})/);
|
||||
if (match) {
|
||||
return {
|
||||
year: match[1],
|
||||
month: match[2].padStart(2, '0'),
|
||||
day: match[3].padStart(2, '0'),
|
||||
hour: match[4].padStart(2, '0'),
|
||||
minute: match[5].padStart(2, '0'),
|
||||
second: match[6].padStart(2, '0')
|
||||
};
|
||||
}
|
||||
|
||||
// 尝试匹配 "Sun, 12 Jul 2026 08:00:00 GMT" 格式
|
||||
match = timeStr.match(/(\d{1,2})\s+(\w+)\s+(\d{4})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})/);
|
||||
if (match) {
|
||||
const months = {
|
||||
'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06',
|
||||
'Jul': '07', 'Aug': '08', 'Sep': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'
|
||||
};
|
||||
return {
|
||||
year: match[3],
|
||||
month: months[match[2]] || '01',
|
||||
day: match[1].padStart(2, '0'),
|
||||
hour: match[4].padStart(2, '0'),
|
||||
minute: match[5].padStart(2, '0'),
|
||||
second: match[6].padStart(2, '0')
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
tbody.innerHTML = list.map(item => {
|
||||
const statusText = item.status === 1 ? '成功' : '失败';
|
||||
const statusClass = item.status === 1 ? 'status-success' : 'status-failed';
|
||||
|
||||
// 格式化报表日期为中文
|
||||
let reportDate = '-';
|
||||
const dateObj = parseDate(item.report_date);
|
||||
if (dateObj) {
|
||||
reportDate = `${dateObj.year}年${dateObj.month}月${dateObj.day}日`;
|
||||
} else if (item.report_date) {
|
||||
reportDate = item.report_date;
|
||||
}
|
||||
|
||||
// 格式化时间范围
|
||||
let timeRange = '-';
|
||||
const startObj = parseTime(item.start_time);
|
||||
const endObj = parseTime(item.end_time);
|
||||
if (startObj && endObj) {
|
||||
const formatTimeObj = (obj) => `${obj.year}-${obj.month}-${obj.day} ${obj.hour}:${obj.minute}:${obj.second}`;
|
||||
timeRange = `${formatTimeObj(startObj)} 至 ${formatTimeObj(endObj)}`;
|
||||
} else if (item.start_time && item.end_time) {
|
||||
timeRange = `${item.start_time} 至 ${item.end_time}`;
|
||||
}
|
||||
|
||||
const configName = item.config_name || '-';
|
||||
const splitValue = item.split_name || '-';
|
||||
const totalOrders = item.total_orders || 0;
|
||||
const totalAmount = item.total_amount ? parseFloat(item.total_amount).toFixed(2) : '0.00';
|
||||
|
||||
return `
|
||||
<tr>
|
||||
<td><input type="checkbox" class="history-checkbox" value="${item.id}"></td>
|
||||
<td>${reportDate}</td>
|
||||
<td>${timeRange}</td>
|
||||
<td>${configName}</td>
|
||||
<td>${splitValue}</td>
|
||||
<td>${totalOrders}</td>
|
||||
<td>¥${totalAmount}</td>
|
||||
<td><span class="${statusClass}">${statusText}</span></td>
|
||||
<td>
|
||||
${item.status === 1 && item.file_path ?
|
||||
`<a href="/api/download?path=${encodeURIComponent(item.file_path)}" class="btn btn-sm btn-success">下载</a>` :
|
||||
'-'}
|
||||
<button class="btn btn-sm btn-danger" onclick="deleteHistory(${item.id})">删除</button>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// 渲染分页控件
|
||||
function renderPagination(page, pageSize, total) {
|
||||
const container = document.getElementById('history-pagination');
|
||||
if (!container) return;
|
||||
|
||||
const totalPages = Math.ceil(total / pageSize);
|
||||
if (totalPages <= 1) {
|
||||
container.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '<div class="pagination">';
|
||||
|
||||
// 上一页
|
||||
if (page > 1) {
|
||||
html += `<button onclick="loadHistoryWithDate(${page - 1})">上一页</button>`;
|
||||
}
|
||||
|
||||
// 页码
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
if (i === page) {
|
||||
html += `<button class="active">${i}</button>`;
|
||||
} else {
|
||||
html += `<button onclick="loadHistoryWithDate(${i})">${i}</button>`;
|
||||
}
|
||||
}
|
||||
|
||||
// 下一页
|
||||
if (page < totalPages) {
|
||||
html += `<button onclick="loadHistoryWithDate(${page + 1})">下一页</button>`;
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
// 加载历史记录(无日期筛选)
|
||||
async function loadHistory(page = 1) {
|
||||
loadHistoryWithDate(page);
|
||||
}
|
||||
|
||||
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();
|
||||
</script>
|
||||
Reference in New Issue
Block a user