NEW:Now can select many many students!

This commit is contained in:
NanGua-QWQ
2026-02-04 14:54:35 +08:00
parent 817c3e701c
commit bdd3ab3724
2 changed files with 90 additions and 43 deletions
@@ -13,6 +13,8 @@ import {
Switch, Switch,
Popconfirm, Popconfirm,
Radio, Radio,
Select,
TooltipLite
} from 'tdesign-react' } from 'tdesign-react'
interface AutoScoreRule { interface AutoScoreRule {
@@ -36,6 +38,7 @@ interface AutoScoreRuleFormValues {
export const AutoScoreManager: React.FC = () => { export const AutoScoreManager: React.FC = () => {
const [rules, setRules] = useState<AutoScoreRule[]>([]) const [rules, setRules] = useState<AutoScoreRule[]>([])
const [students, setStudents] = useState<{ id: number; name: string }[]>([])
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [form] = Form.useForm() const [form] = Form.useForm()
const [editingRuleId, setEditingRuleId] = useState<number | null>(null) const [editingRuleId, setEditingRuleId] = useState<number | null>(null)
@@ -58,11 +61,17 @@ export const AutoScoreManager: React.FC = () => {
console.warn('Auth check failed', e) console.warn('Auth check failed', e)
} }
const res = await (window as any).api.invoke('auto-score:getRules', {}) const [rulesRes, studentsRes] = await Promise.all([
if (res.success) { (window as any).api.invoke('auto-score:getRules', {}),
setRules(res.data) (window as any).api.queryStudents({})
])
if (rulesRes.success) {
setRules(rulesRes.data)
} else { } else {
MessagePlugin.error(res.message || '获取规则失败') MessagePlugin.error(rulesRes.message || '获取规则失败')
}
if (studentsRes.success) {
setStudents(studentsRes.data)
} }
} catch (error) { } catch (error) {
console.error('Failed to fetch auto score rules:', error) console.error('Failed to fetch auto score rules:', error)
@@ -77,29 +86,29 @@ export const AutoScoreManager: React.FC = () => {
}, []) }, [])
const handleSubmit = async () => { const handleSubmit = async () => {
if (!(window as any).api) return if (!(window as any).api) return;
// 修复类型转换错误,使用更安全的方式获取表单值 const values = form.getFieldsValue(true) as unknown as AutoScoreRuleFormValues & { timeUnit: string };
const values = form.getFieldsValue(true) as unknown as AutoScoreRuleFormValues
if (!values.name || values.intervalMinutes == null || values.scoreValue == null) { if (!values.name || values.intervalMinutes == null || values.scoreValue == null) {
MessagePlugin.warning('请填写完整信息') MessagePlugin.warning('请填写完整信息');
return return;
} }
// 解析学生姓名列表(以逗号分隔) // 根据单位转换间隔时间
const studentNames = values.studentNames const intervalMinutes = values.timeUnit === 'days' ? values.intervalMinutes * 1440 : values.intervalMinutes;
? values.studentNames.split(',').map(name => name.trim()).filter(name => name)
: [] // 确保 studentNames 是数组类型
const studentNames = Array.isArray(values.studentNames) ? values.studentNames : [];
const ruleData = { const ruleData = {
enabled: true, // 默认启用新规则 enabled: true,
name: values.name, name: values.name,
intervalMinutes: values.intervalMinutes, intervalMinutes,
studentNames, studentNames,
scoreValue: values.scoreValue, scoreValue: values.scoreValue,
reason: values.reason || `自动化加分 - ${values.name}` reason: values.reason || `自动化加分 - ${values.name}`,
} };
// 权限检查:仅管理员可创建/更新规则 // 权限检查:仅管理员可创建/更新规则
try { try {
@@ -133,7 +142,8 @@ export const AutoScoreManager: React.FC = () => {
intervalMinutes: undefined, intervalMinutes: undefined,
studentNames: '', studentNames: '',
scoreValue: undefined, scoreValue: undefined,
reason: '' reason: '',
timeUnit: 'minutes'
}) })
setEditingRuleId(null) setEditingRuleId(null)
fetchRules() // 刷新规则列表 fetchRules() // 刷新规则列表
@@ -240,7 +250,12 @@ export const AutoScoreManager: React.FC = () => {
colKey: 'intervalMinutes', colKey: 'intervalMinutes',
title: '间隔', title: '间隔',
width: 100, width: 100,
cell: ({ row }) => `${row.intervalMinutes} 分钟` cell: ({ row }) => {
const isDays = row.intervalMinutes >= 1440
const value = isDays ? row.intervalMinutes / 1440 : row.intervalMinutes
const unit = isDays ? '天' : '分钟'
return `${value} ${unit}`
}
}, },
{ {
colKey: 'scoreValue', colKey: 'scoreValue',
@@ -260,7 +275,17 @@ export const AutoScoreManager: React.FC = () => {
if (row.studentNames.length === 0) { if (row.studentNames.length === 0) {
return <span></span> return <span></span>
} }
return <span title={row.studentNames.join(', ')}>{row.studentNames.length} </span> const studentList = row.studentNames.join(',\n')
return (
<TooltipLite
content={studentList}
showArrow
placement="mouse"
theme="default"
>
{row.studentNames.length}
</TooltipLite>
)
} }
}, },
{ colKey: 'reason', title: '理由', ellipsis: true }, { colKey: 'reason', title: '理由', ellipsis: true },
@@ -336,7 +361,7 @@ export const AutoScoreManager: React.FC = () => {
> >
<InputNumber min={1} placeholder="例如:1(每隔1分钟/天执行一次)" /> <InputNumber min={1} placeholder="例如:1(每隔1分钟/天执行一次)" />
</Form.FormItem> </Form.FormItem>
<Form.FormItem name="timeUnit" style={{ marginBottom: 0 }}> <Form.FormItem name="timeUnit" initialData="minutes" style={{ marginBottom: 0 }}>
<Radio.Group variant="default-filled"> <Radio.Group variant="default-filled">
<Radio.Button value="days"></Radio.Button> <Radio.Button value="days"></Radio.Button>
<Radio.Button value="minutes"></Radio.Button> <Radio.Button value="minutes"></Radio.Button>
@@ -357,7 +382,12 @@ export const AutoScoreManager: React.FC = () => {
label="适用学生" label="适用学生"
name="studentNames" name="studentNames"
> >
<Input placeholder="留空表示所有学生,多个学生用逗号分隔" /> <Select
filterable
multiple
placeholder="请选择或搜索学生(留空表示所有学生)"
options={students.map((student) => ({ label: student.name, value: student.name }))}
/>
</Form.FormItem> </Form.FormItem>
<Form.FormItem <Form.FormItem
+22 -5
View File
@@ -138,7 +138,10 @@ export const ScoreManager: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
return return
} }
const values = form.getFieldsValue(true) as any const values = form.getFieldsValue(true) as any
if (!values.student_name || !values.reason_content) {
// 支持多选学生
const studentNames = Array.isArray(values.student_name) ? values.student_name : [values.student_name]
if (!studentNames || studentNames.length === 0 || !values.reason_content) {
MessagePlugin.warning('请填写完整信息') MessagePlugin.warning('请填写完整信息')
return return
} }
@@ -161,15 +164,24 @@ export const ScoreManager: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
: Math.abs(deltaInput) : Math.abs(deltaInput)
: Number(selectedReason?.delta ?? 0) : Number(selectedReason?.delta ?? 0)
// 为每个选中的学生创建事件
try {
let successCount = 0
for (const studentName of studentNames) {
const res = await (window as any).api.createEvent({ const res = await (window as any).api.createEvent({
student_name: values.student_name, student_name: studentName,
reason_content: values.reason_content, reason_content: values.reason_content,
delta: delta delta: delta
}) })
if (res.success) { if (res.success) {
MessagePlugin.success('积分提交成功') successCount++
}
}
if (successCount === studentNames.length) {
MessagePlugin.success(`已为 ${successCount} 名学生提交积分`)
form.setFieldsValue({ form.setFieldsValue({
student_name: [],
delta: undefined, delta: undefined,
reason_content: '', reason_content: '',
reason_id: undefined, reason_id: undefined,
@@ -178,10 +190,14 @@ export const ScoreManager: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
fetchData() fetchData()
emitDataUpdated('events') emitDataUpdated('events')
} else { } else {
MessagePlugin.error(res.message || '提交失败') MessagePlugin.warning(`成功提交 ${successCount}/${studentNames.length} 名学生的积分`)
fetchData()
emitDataUpdated('events')
} }
} finally {
setSubmitLoading(false) setSubmitLoading(false)
} }
}
const handleUndo = async (uuid: string) => { const handleUndo = async (uuid: string) => {
if (!(window as any).api) return if (!(window as any).api) return
@@ -250,6 +266,7 @@ export const ScoreManager: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
<Form.FormItem label="姓名" name="student_name"> <Form.FormItem label="姓名" name="student_name">
<Select <Select
filterable filterable
multiple
placeholder="请选择或搜索学生" placeholder="请选择或搜索学生"
filter={(filterWords, option) => filter={(filterWords, option) =>
matchStudentName(getOptionLabel(option), filterWords) matchStudentName(getOptionLabel(option), filterWords)