feat(export): 新增活动时长总表(矩阵)+ 移除Logo
Deploy to Server / deploy (push) Canceled after 14m38s

- 后端 POST /api/export 新增 include_activity_matrix:部员×活动矩阵行,列=所有活动(start_time DESC排),_total尾列
- 前端 Export 新增 06 卡片「活动时长总表(矩阵)」:部员+义工号 左固定列,活动列160px宽表头显示日期副标题,值>0高亮主色+浅底,总时长bg-primary-50
- Excel 新增「活动时长总表(矩阵)」Sheet:列名=活动标题,末列总时长
- 移除Brand组件所有img Logo渲染,纯标题,减小侧栏移动端头部视觉噪音
- 前后端 tsc --noEmit exit 0 + vite build exit 0;浏览器 E2E 验证矩阵张三/校园志愿宣传活动=2.00/总时长 2.00 h
This commit is contained in:
Trae Assistant
2026-10-04 23:17:19 +08:00
parent 1a51a854a3
commit a84a8b19ac
4 changed files with 118 additions and 12 deletions
+32 -1
View File
@@ -5,7 +5,7 @@ import { authMiddleware, AuthRequest } from '../middleware/auth';
const router = Router();
router.post('/', authMiddleware, (req: AuthRequest, res) => {
const { include_users, include_activities, include_hours, include_user_totals, include_user_activities } = req.body || {};
const { include_users, include_activities, include_hours, include_user_totals, include_user_activities, include_activity_matrix } = req.body || {};
const db = getDb();
const result: any = {};
@@ -65,6 +65,37 @@ router.post('/', authMiddleware, (req: AuthRequest, res) => {
return { ...u, activities: acts };
});
}
if (include_activity_matrix) {
const activities = db.prepareCompat(`
SELECT id, title, start_time FROM activities ORDER BY start_time DESC, id DESC
`).all() as any[];
const users = db.prepareCompat(`
SELECT u.id, u.class, u.name, u.volunteer_no, g.name AS group_name
FROM users u LEFT JOIN work_groups g ON u.group_id = g.id
ORDER BY u.class, u.name
`).all() as any[];
const hoursRows = db.prepareCompat(`
SELECT user_id, activity_id, hours FROM hours
`).all() as any[];
const matrix: Record<string, number> = {};
for (const r of hoursRows) {
const key = `${r.user_id}|${r.activity_id}`;
matrix[key] = (matrix[key] || 0) + Number(r.hours || 0);
}
result.activity_matrix = {
activities,
users: users.map((u) => {
const row: Record<string, any> = { id: u.id, class: u.class, name: u.name, volunteer_no: u.volunteer_no, group_name: u.group_name };
for (const a of activities) {
row[String(a.id)] = Number((matrix[`${u.id}|${a.id}`] || 0).toFixed(2));
}
row._total = Number(
activities.reduce((s: number, a: any) => s + (Number(row[String(a.id)]) || 0), 0).toFixed(2)
);
return row;
}),
};
}
res.json(result);
});