feat: 添加结算功能并优化界面

- 新增结算历史页面,支持查看历史结算记录和排行榜
- 实现结算功能,可将当前积分记录归档并清零学生分数
- 优化学生搜索功能,支持拼音匹配
- 更新应用图标和品牌标识
- 改进主题系统,支持自定义侧边栏文本颜色
- 重构积分管理表单,优化用户体验
- 修复若干已知问题并改进代码结构
This commit is contained in:
Fox_block
2026-01-16 18:57:06 +08:00
parent f27e2f21bb
commit b41139b941
25 changed files with 828 additions and 95 deletions
+102
View File
@@ -0,0 +1,102 @@
import { Database } from 'better-sqlite3'
export interface SettlementSummary {
id: number
start_time: string
end_time: string
event_count: number
}
export interface SettlementLeaderboardRow {
name: string
score: number
}
export class SettlementRepository {
constructor(private db: Database) {}
findAll(): SettlementSummary[] {
const rows = this.db
.prepare(
`
SELECT
s.id as id,
s.start_time as start_time,
s.end_time as end_time,
(
SELECT COUNT(1)
FROM score_events e
WHERE e.settlement_id = s.id
) as event_count
FROM settlements s
ORDER BY julianday(s.end_time) DESC
`
)
.all() as SettlementSummary[]
return rows
}
settleNow() {
return this.db.transaction(() => {
const unassigned = this.db
.prepare(`SELECT COUNT(1) as count FROM score_events WHERE settlement_id IS NULL`)
.get() as { count: number }
const eventCount = Number(unassigned?.count ?? 0)
if (eventCount <= 0) {
throw new Error('暂无可结算记录')
}
const endTime = new Date().toISOString()
const lastSettlement = this.db
.prepare(`SELECT end_time FROM settlements ORDER BY julianday(end_time) DESC LIMIT 1`)
.get() as { end_time: string } | undefined
const minEvent = this.db
.prepare(`SELECT MIN(event_time) as min_time FROM score_events WHERE settlement_id IS NULL`)
.get() as { min_time: string } | undefined
const startTime = lastSettlement?.end_time || minEvent?.min_time || endTime
const info = this.db
.prepare(`INSERT INTO settlements (start_time, end_time) VALUES (?, ?)`)
.run(startTime, endTime)
const settlementId = info.lastInsertRowid as number
this.db
.prepare(`UPDATE score_events SET settlement_id = ? WHERE settlement_id IS NULL`)
.run(settlementId)
this.db.prepare(`UPDATE students SET score = 0, updated_at = CURRENT_TIMESTAMP`).run()
return { settlementId, startTime, endTime, eventCount }
})()
}
getLeaderboard(settlementId: number) {
const settlement = this.db
.prepare(`SELECT id, start_time, end_time FROM settlements WHERE id = ?`)
.get(settlementId) as { id: number; start_time: string; end_time: string } | undefined
if (!settlement) {
throw new Error('结算记录不存在')
}
const rows = this.db
.prepare(
`
SELECT
e.student_name as name,
COALESCE(SUM(e.delta), 0) as score
FROM score_events e
WHERE e.settlement_id = ?
GROUP BY e.student_name
ORDER BY score DESC, name ASC
`
)
.all(settlementId) as SettlementLeaderboardRow[]
return { settlement, rows }
}
}