Files
time-tracking-system/static/js/dashboard.js
bf1942 8938ce2708 refactor(api): 重构数据库访问为SQLAlchemy绑定的session
- 统一移除手动创建的数据库session,统一使用models模块中的db.session
- 修正项目创建接口,增加开始和结束日期的格式验证与处理
- 更新导入项目接口,使用枚举类型校验项目类型并优化异常处理
- 更新统计接口,避免多次查询假期数据,优化日期字符串处理
- 删除回滚前多余的session关闭调用,改为使用db.session.rollback()
- app.py中重构数据库初始化:统一配置SQLAlchemy,动态创建数据库路径和表
- 项目模型新增开始日期和结束日期字段支持
- 添加导入批次历史记录模型支持
- 优化工具函数中日期类型提示,移除无用导入
- 更新requirements.txt依赖版本回退,确保兼容性
- 前端菜单添加导入历史导航入口,实现页面访问路由绑定
2025-09-04 18:12:24 +08:00

210 lines
6.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 首页面板JavaScript
// 页面加载时初始化
document.addEventListener('DOMContentLoaded', function() {
loadDashboardStats();
loadRecentRecords();
});
// 加载仪表板统计数据
async function loadDashboardStats() {
try {
// 并行加载项目和工时统计
const [projectsResponse, recentRecordsResponse] = await Promise.all([
apiGet('/api/projects'),
loadThisWeekHours()
]);
// 更新活跃项目数
const activeProjects = projectsResponse.data.filter(p => p.is_active).length;
updateStatValue('total-projects', activeProjects);
// 更新本周工时
updateStatValue('this-week-hours', recentRecordsResponse.weeklyHours || '0:00');
// 加载本月记录数
const thisMonthCount = await loadThisMonthRecordsCount();
updateStatValue('this-month-records', thisMonthCount);
} catch (error) {
console.error('加载仪表板统计失败:', error);
// 显示默认值
updateStatValue('total-projects', '0');
updateStatValue('this-week-hours', '0:00');
updateStatValue('this-month-records', '0');
}
}
// 更新统计值
function updateStatValue(elementId, value) {
const element = document.getElementById(elementId);
if (element) {
element.textContent = value;
}
}
// 加载本周工时
async function loadThisWeekHours() {
try {
const weekRange = getThisWeekRange();
const url = `/api/timerecords?start_date=${weekRange.start}&end_date=${weekRange.end}`;
const response = await apiGet(url);
// 计算本周总工时
let totalHours = 0;
response.data.forEach(record => {
if (record.hours && record.hours !== '-') {
totalHours += hoursToDecimal(record.hours);
}
});
return {
weeklyHours: decimalToHours(totalHours),
recordCount: response.data.length
};
} catch (error) {
console.error('加载本周工时失败:', error);
return { weeklyHours: '0:00', recordCount: 0 };
}
}
// 加载本月记录数
async function loadThisMonthRecordsCount() {
try {
const today = new Date();
const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
const lastDayOfMonth = new Date(today.getFullYear(), today.getMonth() + 1, 0);
const startDate = firstDayOfMonth.toISOString().split('T')[0];
const endDate = lastDayOfMonth.toISOString().split('T')[0];
const url = `/api/timerecords?start_date=${startDate}&end_date=${endDate}`;
const response = await apiGet(url);
return response.data.length;
} catch (error) {
console.error('加载本月记录数失败:', error);
return 0;
}
}
// 加载最近记录
async function loadRecentRecords() {
try {
// 获取最近7天的记录
const endDate = new Date();
const startDate = new Date();
startDate.setDate(endDate.getDate() - 6); // 最近7天
const url = `/api/timerecords?start_date=${startDate.toISOString().split('T')[0]}&end_date=${endDate.toISOString().split('T')[0]}`;
const response = await apiGet(url);
renderRecentRecords(response.data.slice(0, 5)); // 只显示最近5条
} catch (error) {
console.error('加载最近记录失败:', error);
const container = document.getElementById('recent-records-list');
if (container) {
container.innerHTML = '<p class="text-center">加载失败</p>';
}
}
}
// 渲染最近记录
function renderRecentRecords(records) {
const container = document.getElementById('recent-records-list');
if (!container) return;
if (records.length === 0) {
container.innerHTML = '<p class="text-center">暂无最近记录</p>';
return;
}
container.innerHTML = `
<div class="recent-records-table">
<table class="data-table">
<thead>
<tr>
<th>日期</th>
<th>事件</th>
<th>项目</th>
<th>工时</th>
</tr>
</thead>
<tbody>
${records.map(record => {
const projectDisplay = record.project
? (record.project.project_type === 'traditional'
? `${record.project.customer_name} ${record.project.project_code}`
: `${record.project.customer_name} ${record.project.contract_number}`)
: '-';
const rowClass = getRowClass(record);
return `
<tr class="${rowClass}">
<td>
${formatDate(record.date)}
${isToday(record.date) ? '<span class="today-badge">今天</span>' : ''}
</td>
<td>${escapeHtml(record.event_description || '-')}</td>
<td>${escapeHtml(projectDisplay)}</td>
<td>${record.hours || '-'}</td>
</tr>
`;
}).join('')}
</tbody>
</table>
</div>
`;
}
// 获取行的CSS类名根据是否为休息日
function getRowClass(record) {
if (record.is_holiday) {
if (record.is_working_on_holiday) {
return 'working-holiday-row'; // 休息日工作
} else {
return 'holiday-row'; // 休息日休息
}
}
return '';
}
// HTML转义函数
function escapeHtml(text) {
if (!text) return '';
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 添加今天徽章样式
if (!document.querySelector('#today-badge-styles')) {
const style = document.createElement('style');
style.id = 'today-badge-styles';
style.textContent = `
.today-badge {
background-color: #3498db;
color: white;
padding: 2px 6px;
border-radius: 3px;
font-size: 10px;
font-weight: 600;
margin-left: 5px;
}
.recent-records-table {
overflow-x: auto;
}
.recent-records-table .data-table {
margin-bottom: 0;
}
.recent-records-table .data-table td {
font-size: 13px;
padding: 10px 8px;
}
`;
document.head.appendChild(style);
}