Skip to content

Commit dc4873e

Browse files
committed
feat:自动加分更新逻辑
1 parent 094bd63 commit dc4873e

File tree

10 files changed

+153
-96
lines changed

10 files changed

+153
-96
lines changed

src/main/db/migrations/AddTagsSchema2026020600000.ts

Lines changed: 0 additions & 53 deletions
This file was deleted.

src/main/db/migrations/InitSchema2026011800000.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,47 @@ export class InitSchema2026011800000 implements MigrationInterface {
8181
('迟到','纪律',-1,1,CURRENT_TIMESTAMP),
8282
('未交作业','作业情况',-2,1,CURRENT_TIMESTAMP)
8383
`)
84+
85+
if (!(await queryRunner.hasTable('tags'))) {
86+
await queryRunner.query(`
87+
CREATE TABLE "tags" (
88+
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
89+
"name" text NOT NULL UNIQUE,
90+
"created_at" text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
91+
"updated_at" text NOT NULL DEFAULT (CURRENT_TIMESTAMP)
92+
)
93+
`)
94+
}
95+
96+
if (!(await queryRunner.hasTable('student_tags'))) {
97+
await queryRunner.query(`
98+
CREATE TABLE "student_tags" (
99+
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
100+
"student_id" integer NOT NULL,
101+
"tag_id" integer NOT NULL,
102+
"created_at" text NOT NULL DEFAULT (CURRENT_TIMESTAMP),
103+
FOREIGN KEY ("student_id") REFERENCES "students"("id") ON DELETE CASCADE,
104+
FOREIGN KEY ("tag_id") REFERENCES "tags"("id") ON DELETE CASCADE,
105+
UNIQUE("student_id", "tag_id")
106+
)
107+
`)
108+
}
109+
110+
await queryRunner.query(
111+
`CREATE INDEX IF NOT EXISTS "IDX_student_tags_student_id" ON "student_tags" ("student_id")`
112+
)
113+
await queryRunner.query(
114+
`CREATE INDEX IF NOT EXISTS "IDX_student_tags_tag_id" ON "student_tags" ("tag_id")`
115+
)
116+
117+
await queryRunner.query(`
118+
INSERT OR IGNORE INTO "tags" ("name") VALUES
119+
('优秀生'),
120+
('班干部'),
121+
('勤奋'),
122+
('活跃'),
123+
('进步快')
124+
`)
84125
}
85126

86127
async down(queryRunner: QueryRunner): Promise<void> {
@@ -89,5 +130,7 @@ export class InitSchema2026011800000 implements MigrationInterface {
89130
await queryRunner.query(`DROP TABLE IF EXISTS "students"`)
90131
await queryRunner.query(`DROP TABLE IF EXISTS "reasons"`)
91132
await queryRunner.query(`DROP TABLE IF EXISTS "settings"`)
133+
await queryRunner.query(`DROP TABLE IF EXISTS "student_tags"`)
134+
await queryRunner.query(`DROP TABLE IF EXISTS "tags"`)
92135
}
93136
}

src/main/db/migrations/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
import { InitSchema2026011800000 } from './InitSchema2026011800000'
2-
import { AddTagsSchema2026020600000 } from './AddTagsSchema2026020600000'
32

4-
export const migrations = [InitSchema2026011800000, AddTagsSchema2026020600000]
3+
export const migrations = [InitSchema2026011800000]

src/renderer/src/components/AutoScoreManager.tsx

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -440,13 +440,14 @@ export const AutoScoreManager: React.FC = () => {
440440

441441
const triggerItems = triggerList
442442
.filter((t) => t.eventName !== null)
443-
.map((item) => (
443+
.map((item, idx) => (
444444
<TriggerItemComponent
445445
key={item.id}
446446
item={item}
447447
onDelete={handleDeleteTrigger}
448448
onChange={handleTriggerChange}
449449
onValueChange={handleTriggerValueChange}
450+
isFirst={idx === 0}
450451
/>
451452
))
452453

@@ -498,25 +499,6 @@ export const AutoScoreManager: React.FC = () => {
498499
</div>
499500
</Form>
500501
</Card>
501-
<Card style={{ marginBottom: '24px', backgroundColor: 'var(--ss-card-bg)' }}>
502-
<Table
503-
data={rules.slice((currentPage - 1) * pageSize, currentPage * pageSize)}
504-
columns={columns}
505-
rowKey="id"
506-
resizable
507-
loading={loading}
508-
dragSort="row-handler"
509-
onDragSort={onDragSort}
510-
pagination={{
511-
current: currentPage,
512-
pageSize,
513-
total: rules.length,
514-
onChange: (pageInfo) => setCurrentPage(pageInfo.current),
515-
onPageSizeChange: (size) => setPageSize(size)
516-
}}
517-
style={{ color: 'var(--ss-text-main)' }}
518-
/>
519-
</Card>
520502

521503
<Card
522504
style={{ marginBottom: '24px', backgroundColor: 'var(--ss-card-bg)' }}
@@ -555,6 +537,27 @@ export const AutoScoreManager: React.FC = () => {
555537
</Button>
556538
</Space>
557539
</Card>
540+
541+
<Card style={{ marginBottom: '24px', backgroundColor: 'var(--ss-card-bg)' }}>
542+
<Table
543+
data={rules.slice((currentPage - 1) * pageSize, currentPage * pageSize)}
544+
columns={columns}
545+
rowKey="id"
546+
resizable
547+
loading={loading}
548+
dragSort="row-handler"
549+
onDragSort={onDragSort}
550+
pagination={{
551+
current: currentPage,
552+
pageSize,
553+
total: rules.length,
554+
onChange: (pageInfo) => setCurrentPage(pageInfo.current),
555+
onPageSizeChange: (size) => setPageSize(size)
556+
}}
557+
style={{ color: 'var(--ss-text-main)' }}
558+
/>
559+
</Card>
560+
558561
<Card style={{ marginBottom: '24px', backgroundColor: 'var(--ss-card-bg)' }}>
559562
<Code
560563
code={(() => {

src/renderer/src/components/com.automatically/ActionItem.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const ActionItem: React.FC<ActionItemProps> = ({
3131
/>
3232
<Select
3333
value={item.eventName}
34-
style={{ width: '200px' }}
34+
style={{ width: '200px', marginRight: 12 }}
3535
options={allActions.options}
3636
placeholder="请选择触发行动"
3737
onChange={(value) => onChange(item.id, value as string)}

src/renderer/src/components/com.automatically/TriggerItem.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react'
1+
import { useState } from 'react'
22
import { Button, Select } from 'tdesign-react'
33
import { Delete1Icon } from 'tdesign-icons-react'
44
import { triggerRegistry, allTriggers } from './registry'
@@ -9,14 +9,31 @@ interface TriggerItemProps {
99
onDelete: (id: number) => void
1010
onChange: (id: number, eventName: string) => void
1111
onValueChange: (id: number, value: string) => void
12+
isFirst?: boolean
1213
}
1314

14-
const TriggerItem: React.FC<TriggerItemProps> = ({ item, onDelete, onChange, onValueChange }) => {
15+
const TriggerItem: React.FC<TriggerItemProps> = ({
16+
item,
17+
onDelete,
18+
onChange,
19+
onValueChange,
20+
isFirst = false
21+
}) => {
1522
const definition = triggerRegistry.get(item.eventName)
1623
const Component = definition?.component
24+
const [andFilled, setAndFilled] = useState(false)
1725

1826
return (
1927
<div style={{ display: 'flex', gap: 5 }}>
28+
{!isFirst && (
29+
<Button
30+
theme="primary"
31+
variant={andFilled ? 'base' : 'outline'}
32+
onClick={() => setAndFilled((v) => !v)}
33+
>
34+
35+
</Button>
36+
)}
2037
<Button
2138
theme="default"
2239
variant="text"
@@ -25,7 +42,7 @@ const TriggerItem: React.FC<TriggerItemProps> = ({ item, onDelete, onChange, onV
2542
/>
2643
<Select
2744
value={item.eventName}
28-
style={{ width: '200px' }}
45+
style={{ width: '200px', marginRight: 12 }}
2946
options={allTriggers.options}
3047
placeholder="请选择触发规则"
3148
onChange={(value) => onChange(item.id, value as string)}

src/renderer/src/components/com.automatically/triggers/IntervalTimeTrigger.tsx

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { InputNumber } from 'tdesign-react'
1+
import { useState } from 'react'
2+
import { InputNumber, Space, Radio, Form } from 'tdesign-react'
23
import type { TriggerComponentProps } from '../types'
34

45
export const eventName = 'interval_time_passed'
@@ -19,21 +20,59 @@ export const triggerLogic = {
1920

2021
const IntervalTimeTrigger: React.FC<TriggerComponentProps> = ({ value, onChange }) => {
2122
const numValue = value ? parseInt(value, 10) : undefined
23+
const [unit, setUnit] = useState<'minutes' | 'days'>('minutes')
2224

2325
const handleChange = (v: any) => {
2426
const numV = typeof v === 'number' ? v : v ? Number(v) : undefined
25-
onChange(numV !== undefined && numV !== null && !isNaN(numV) ? String(numV) : '')
27+
if (numV === undefined || numV === null || isNaN(numV)) {
28+
onChange('')
29+
return
30+
}
31+
const minutes = unit === 'minutes' ? numV : numV * 1440
32+
onChange(String(Math.round(minutes)))
2633
}
2734

35+
const displayValue =
36+
numValue === undefined || isNaN(numValue)
37+
? undefined
38+
: unit === 'minutes'
39+
? numValue
40+
: Math.max(1, Math.round(numValue / 1440))
41+
2842
return (
29-
<InputNumber
30-
placeholder="请输入时间间隔(分钟)"
31-
style={{ width: '150px' }}
32-
value={numValue}
33-
onChange={handleChange}
34-
min={1}
35-
theme="column"
36-
/>
43+
<Space>
44+
<Form.FormItem
45+
name="intervalMinutes"
46+
rules={[
47+
{ required: true, message: '请输入时间' },
48+
{ min: 1, message: unit === 'minutes' ? '间隔时间至少为1分钟' : '间隔时间至少为1天' }
49+
]}
50+
style={{ marginBottom: 0 }}
51+
>
52+
<InputNumber
53+
placeholder={unit === 'minutes' ? '请输入时间间隔(分钟)' : '请输入时间间隔(天)'}
54+
style={{ width: '100px' }}
55+
value={displayValue}
56+
onChange={handleChange}
57+
min={1}
58+
theme="column"
59+
/>
60+
</Form.FormItem>
61+
<Form.FormItem
62+
name="timeUnit"
63+
initialData="minutes"
64+
style={{ marginBottom: 0, marginLeft: -12 }}
65+
>
66+
<Radio.Group
67+
variant="default-filled"
68+
value={unit}
69+
onChange={(v) => setUnit(String(v) as 'minutes' | 'days')}
70+
>
71+
<Radio.Button value="days"></Radio.Button>
72+
<Radio.Button value="minutes">分钟</Radio.Button>
73+
</Radio.Group>
74+
</Form.FormItem>
75+
</Space>
3776
)
3877
}
3978

src/renderer/src/components/com.automatically/triggers/RandomTimeTrigger.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { InputNumber, Row, Col } from 'tdesign-react'
22
import type { TriggerComponentProps } from '../types'
33

4-
export const eventName = 'random_time'
4+
export const eventName = 'random_time_reached'
55
export const label = '随机时间触发'
66
export const description = '在指定时间范围内随机触发自动化'
77
export const triggerLogic = {

src/renderer/src/components/com.automatically/triggers/StudentTagTrigger.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { useState, useEffect } from 'react'
22
import { Select } from 'tdesign-react'
33
import type { TriggerComponentProps } from '../types'
4-
5-
export const eventName = 'student_tag_added'
6-
export const label = '当学生添加标签时触发'
7-
export const description = '当学生添加特定标签时触发自动化'
4+
export const eventName = 'student_tag_matched'
5+
export const label = '当学生匹配标签时触发'
6+
export const description = '当学生匹配特定标签时触发自动化'
87
export const triggerLogic = {
98
eventName,
109
label,
@@ -23,7 +22,7 @@ const StudentTagTrigger: React.FC<TriggerComponentProps> = ({ value, onChange })
2322
useEffect(() => {
2423
const fetchTags = async () => {
2524
try {
26-
const res = await (window as any).api.invoke('tag:getAll', {})
25+
const res = await (window as any).api.tagsGetAll()
2726
if (res.success && res.data) {
2827
setTags(res.data.map((tag: any) => ({ label: tag.name, value: tag.name })))
2928
}

src/shared/triggers/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,15 @@ export const allTriggerLogics: TriggerLogic[] = [
1313
]
1414

1515
export function getTriggerLogic(eventName: string): TriggerLogic | undefined {
16-
return allTriggerLogics.find((t) => t.eventName === eventName)
16+
const direct = allTriggerLogics.find((t) => t.eventName === eventName)
17+
if (direct) return direct
18+
const aliasMap: Record<string, string> = {
19+
random_time: 'random_time_reached',
20+
student_tag_added: 'student_tag_matched'
21+
}
22+
const mapped = aliasMap[eventName]
23+
if (mapped) {
24+
return allTriggerLogics.find((t) => t.eventName === mapped)
25+
}
26+
return undefined
1727
}

0 commit comments

Comments
 (0)