Dev:施工中......

This commit is contained in:
NanGua-QWQ
2026-02-11 22:05:19 +08:00
parent b6023dbe62
commit 5984b203c3
15 changed files with 1280 additions and 48 deletions
+33
View File
@@ -139,10 +139,43 @@ function MainContent(): React.JSX.Element {
/>
</div>
</Dialog>
{/* 开发中画面水印 */}
<p
style={{
position: 'fixed',
bottom: '2px',
left: '20px',
opacity: 0.6,
color: '#de2611',
fontWeight: 'bold',
fontSize: '13px',
pointerEvents: 'none', // 防止水印干扰页面交互
zIndex: 9999 // 确保水印在最顶层显示
}}
>
({getArchitecture()})
</p>
</Layout>
)
}
function getArchitecture(): string {
// 尝试从 userAgent 中获取架构信息
const userAgent = navigator.userAgent.toLowerCase();
if (userAgent.includes('arm64') || userAgent.includes('aarch64')) {
return 'ARM64';
} else if (userAgent.includes('x86_64') || userAgent.includes('amd64')) {
return 'x86_64';
} else if (userAgent.includes('i386') || userAgent.includes('i686')) {
return 'x86';
}
// 默认返回未知架构
return 'Unknown';
}
function App(): React.JSX.Element {
return (
<ThemeProvider>
+149 -32
View File
@@ -1,5 +1,7 @@
import React, { useState, useEffect } from 'react'
import { MoveIcon } from 'tdesign-icons-react'
import { AddIcon, Delete1Icon, MoveIcon } from 'tdesign-icons-react'
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
import { prism } from 'react-syntax-highlighter/dist/esm/styles/prism'
import {
Card,
Form,
@@ -27,6 +29,7 @@ interface AutoScoreRule {
scoreValue: number
reason: string
lastExecuted?: string
triggers?: { event: string; value?: string }[]
}
interface AutoScoreRuleFormValues {
@@ -55,7 +58,7 @@ export const AutoScoreManager: React.FC = () => {
try {
const authRes = await (window as any).api.authGetStatus()
if (!authRes || !authRes.success || authRes.data?.permission !== 'admin') {
MessagePlugin.error('需要管理员权限以查看自动加分规则,请先登录管理员账号')
MessagePlugin.error('需要管理员权限以查看自动加分自动化,请先登录管理员账号')
setLoading(false)
return
}
@@ -71,14 +74,14 @@ export const AutoScoreManager: React.FC = () => {
if (rulesRes.success) {
setRules(rulesRes.data)
} else {
MessagePlugin.error(rulesRes.message || '获取规则失败')
MessagePlugin.error(rulesRes.message || '获取自动化失败')
}
if (studentsRes.success) {
setStudents(studentsRes.data)
}
} catch (error) {
console.error('Failed to fetch auto score rules:', error)
MessagePlugin.error('获取规则失败')
MessagePlugin.error('获取自动化失败')
} finally {
setLoading(false)
}
@@ -104,6 +107,8 @@ export const AutoScoreManager: React.FC = () => {
// 确保 studentNames 是数组类型
const studentNames = Array.isArray(values.studentNames) ? values.studentNames : [];
const triggersPayload = triggerList.map((t) => ({ event: t.eventName, value: t.value }))
const ruleData = {
enabled: true,
name: values.name,
@@ -111,13 +116,23 @@ export const AutoScoreManager: React.FC = () => {
studentNames,
scoreValue: values.scoreValue,
reason: values.reason || `自动化加分 - ${values.name}`,
triggers: triggersPayload
};
// 权限检查:仅管理员可创建/更新规则
// 权限检查:仅管理员可创建/更新自动化
try {
// 验证触发器必填项(如果某个触发器需要 value,则确保已填写)
for (const t of triggerList) {
const def = allTriggers.find((a) => a.eventName === t.eventName)
if (def?.haveValue && (!t.value || String(t.value).trim() === '')) {
MessagePlugin.warning(`触发器 ${def.label} 需要填写 value`)
return
}
}
const authRes = await (window as any).api.authGetStatus()
if (!authRes || !authRes.success || authRes.data?.permission !== 'admin') {
MessagePlugin.error('需要管理员权限以创建或更新自动加分规则')
MessagePlugin.error('需要管理员权限以创建或更新自动加分自动化')
return
}
} catch (e) {
@@ -127,18 +142,18 @@ export const AutoScoreManager: React.FC = () => {
try {
let res
if (editingRuleId !== null) {
// 更新现有规则
// 更新现有自动化
res = await (window as any).api.invoke('auto-score:updateRule', {
id: editingRuleId,
...ruleData
})
} else {
// 创建新规则
// 创建新自动化
res = await (window as any).api.invoke('auto-score:addRule', ruleData)
}
if (res.success) {
MessagePlugin.success(editingRuleId !== null ? '规则更新成功' : '规则创建成功')
MessagePlugin.success(editingRuleId !== null ? '自动化更新成功' : '自动化创建成功')
// 手动清空表单字段,避免 form.reset() 导致的栈溢出
form.setFieldsValue({
name: '',
@@ -149,13 +164,14 @@ export const AutoScoreManager: React.FC = () => {
timeUnit: 'minutes'
})
setEditingRuleId(null)
fetchRules() // 刷新规则列表
setTriggerList([])
fetchRules() // 刷新自动化列表
} else {
MessagePlugin.error(res.message || (editingRuleId !== null ? '更新规则失败' : '创建规则失败'))
MessagePlugin.error(res.message || (editingRuleId !== null ? '更新自动化失败' : '创建自动化失败'))
}
} catch (error) {
console.error('Failed to submit auto score rule:', error)
MessagePlugin.error(editingRuleId !== null ? '更新规则失败' : '创建规则失败')
MessagePlugin.error(editingRuleId !== null ? '更新自动化失败' : '创建自动化失败')
}
}
const handleEdit = (rule: AutoScoreRule) => {
@@ -167,6 +183,21 @@ export const AutoScoreManager: React.FC = () => {
scoreValue: rule.scoreValue,
reason: rule.reason
})
// 如果后端返回了 triggers 字段,把它加载到 triggerList
if (rule.triggers && Array.isArray(rule.triggers)) {
const mapped = rule.triggers.map((t, idx) => {
const found = allTriggers.find((a) => a.eventName === t.event)
return {
id: idx + 1,
eventName: t.event,
haveValue: !!found?.haveValue,
value: t.value ?? ''
}
})
setTriggerList(mapped)
} else {
setTriggerList([])
}
}
const handleDelete = async (ruleId: number) => {
@@ -175,7 +206,7 @@ export const AutoScoreManager: React.FC = () => {
try {
const authRes = await (window as any).api.authGetStatus()
if (!authRes || !authRes.success || authRes.data?.permission !== 'admin') {
MessagePlugin.error('需要管理员权限以删除自动加分规则')
MessagePlugin.error('需要管理员权限以删除自动加分自动化')
return
}
} catch (e) {
@@ -185,14 +216,14 @@ export const AutoScoreManager: React.FC = () => {
try {
const res = await (window as any).api.invoke('auto-score:deleteRule', ruleId)
if (res.success) {
MessagePlugin.success('规则删除成功')
fetchRules() // 刷新规则列表
MessagePlugin.success('自动化删除成功')
fetchRules() // 刷新自动化列表
} else {
MessagePlugin.error(res.message || '删除规则失败')
MessagePlugin.error(res.message || '删除自动化失败')
}
} catch (error) {
console.error('Failed to delete auto score rule:', error)
MessagePlugin.error('删除规则失败')
MessagePlugin.error('删除自动化失败')
}
}
@@ -202,7 +233,7 @@ export const AutoScoreManager: React.FC = () => {
try {
const authRes = await (window as any).api.authGetStatus()
if (!authRes || !authRes.success || authRes.data?.permission !== 'admin') {
MessagePlugin.error('需要管理员权限以启用/禁用自动加分规则')
MessagePlugin.error('需要管理员权限以启用/禁用自动加分自动化')
return
}
} catch (e) {
@@ -212,14 +243,14 @@ export const AutoScoreManager: React.FC = () => {
try {
const res = await (window as any).api.invoke('auto-score:toggleRule', { ruleId, enabled })
if (res.success) {
MessagePlugin.success(enabled ? '规则已启用' : '规则已禁用')
fetchRules() // 刷新规则列表
MessagePlugin.success(enabled ? '自动化已启用' : '自动化已禁用')
fetchRules() // 刷新自动化列表
} else {
MessagePlugin.error(res.message || (enabled ? '启用规则失败' : '禁用规则失败'))
MessagePlugin.error(res.message || (enabled ? '启用自动化失败' : '禁用自动化失败'))
}
} catch (error) {
console.error('Failed to toggle auto score rule:', error)
MessagePlugin.error(enabled ? '启用规则失败' : '禁用规则失败')
MessagePlugin.error(enabled ? '启用自动化失败' : '禁用自动化失败')
}
}
@@ -254,7 +285,7 @@ export const AutoScoreManager: React.FC = () => {
/>
)
},
{ colKey: 'name', title: '规则名称', width: 150 },
{ colKey: 'name', title: '自动化名称', width: 150 },
{
colKey: 'intervalMinutes',
title: '间隔',
@@ -326,7 +357,7 @@ export const AutoScoreManager: React.FC = () => {
</Button>
<Popconfirm
content="确定要删除这条规则吗?"
content="确定要删除这条自动化吗?"
onConfirm={() => handleDelete(row.id)}
>
<Button size="small" variant="outline" theme="danger">
@@ -337,8 +368,76 @@ export const AutoScoreManager: React.FC = () => {
)
}
]
const onDragSort = (params: any) => setRules(params.newData);
type TriggerDef = {
id: number
label: string
description: string
eventName: string
haveValue: boolean
valueType?: string
}
const allTriggers: TriggerDef[] = [
{ id: 1, label: '根据间隔时间触发', description: '当学生注册时触发自动化', eventName: 'interval_time_passed', haveValue: false, valueType: 'number' },
{ id: 2, label: '按照学生标签触发', description: '当学生完成作业时触发自动化', eventName: 'student_tag_matched', haveValue: true, valueType: 'number' },
{ id: 3, label: '随机时间触发', description: '当随机时间到达时触发自动化', eventName: 'random_time_reached', haveValue: false, valueType: 'number' }
]
const triggerOptions = allTriggers.map((t) => ({ label: t.label, value: t.eventName }))
// triggerList items only need id, eventName, haveValue and value
type TriggerItem = { id: number; eventName: string; haveValue: boolean; value?: string }
const [triggerList, setTriggerList] = useState<TriggerItem[]>([])
const handleTriggerChange = (id: number, value: string) => {
const found = allTriggers.find((a) => a.eventName === value)
setTriggerList((prev) => prev.map((t) => (t.id === id ? { ...t, eventName: value, haveValue: !!found?.haveValue, value: t.value ?? '' } : t)))
}
const handleValueChange = (id: number, val: string) => {
setTriggerList((prev) => prev.map((t) => (t.id === id ? { ...t, value: val } : t)))
}
const handleDeleteTrigger = (id: number) => {
setTriggerList((prev) => prev.filter((t) => t.id !== id))
}
const handleAddTrigger = () => {
const nextId = triggerList.length ? Math.max(...triggerList.map((t) => t.id)) + 1 : 1
const defaultTrigger = allTriggers[0]
setTriggerList((prev) => [...prev, { id: nextId, eventName: defaultTrigger.eventName, haveValue: defaultTrigger.haveValue, value: '' }])
}
const triggerItems = triggerList
.filter((t) => t.eventName !== null)
.map((triggerTest) => (
<div key={triggerTest.id} style={{ display: 'flex', gap: 5 }}>
<Button
theme="default"
variant="text"
icon={<Delete1Icon strokeWidth={2.4} />}
onClick={() => handleDeleteTrigger(triggerTest.id)}
/>
<Select
value={triggerTest.eventName}
style={{ width: '200px' }}
options={triggerOptions}
placeholder="请选择触发规则"
onChange={(value) => handleTriggerChange(triggerTest.id, value as string)}
/>
{triggerTest.haveValue ? (
<Input
placeholder="请输入Value"
style={{ width: '150px' }}
value={String(triggerTest.value ?? '')}
onChange={(v) => handleValueChange(triggerTest.id, String((v as any).target?.value ?? v))}
/>
) : null}
</div>
))
return (
<div style={{ padding: '24px' }}>
@@ -352,9 +451,9 @@ export const AutoScoreManager: React.FC = () => {
>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '24px' }}>
<Form.FormItem
label="规则名称"
label="自动化名称"
name="name"
rules={[{ required: true, message: '请输入规则名称' }]}
rules={[{ required: true, message: '请输入自动化名称' }]}
>
<Input placeholder="例如:每日签到加分" />
</Form.FormItem>
@@ -414,7 +513,7 @@ export const AutoScoreManager: React.FC = () => {
theme="primary"
onClick={handleSubmit}
>
{editingRuleId !== null ? '更新规则' : '添加规则'}
{editingRuleId !== null ? '更新自动化' : '添加自动化'}
</Button>
<Button
type="reset"
@@ -425,8 +524,7 @@ export const AutoScoreManager: React.FC = () => {
</div>
</Form>
</Card>
<Card style={{ backgroundColor: 'var(--ss-card-bg)' }}>
<Card style={{ marginBottom: '24px',backgroundColor: 'var(--ss-card-bg)' }}>
<Table
data={rules.slice((currentPage - 1) * pageSize, currentPage * pageSize)}
columns={columns}
@@ -446,13 +544,32 @@ export const AutoScoreManager: React.FC = () => {
/>
</Card>
<Card style={{ marginBottom: '24px', backgroundColor: 'var(--ss-card-bg)' }} title="当以下事件触发时" headerBordered>
<Space style={{ display: "grid" }}>
{triggerItems}
<Button
theme="default"
variant="text"
style={{ fontWeight: 'bolder', fontSize: 15 }}
icon={<AddIcon strokeWidth={3}/>}
onClick={handleAddTrigger}>
</Button>
</Space>
</Card>
<Card style={{ marginBottom: '24px' , backgroundColor: 'var(--ss-card-bg)' }}>
<SyntaxHighlighter language="javascript" style={prism} showLineNumbers>
println("这是一个示例代码块,展示如何使用自动化加分功能的API接口")
</SyntaxHighlighter>
</Card>
{/* <div style={{ marginTop: '24px', padding: '16px', backgroundColor: 'var(--ss-card-bg)', borderRadius: '8px' }}>
<h3 style={{ marginBottom: '12px', color: 'var(--ss-text-main)' }}>使用说明</h3>
<ul style={{ color: 'var(--ss-text-secondary)', lineHeight: '1.6' }}>
<li>自动化加分功能会按照设定的时间间隔自动为学生加分</li>
<li>间隔时间以分钟为单位,例如1440表示每24小时(一天)执行一次</li>
<li>如果"适用学生"字段为空,则规则适用于所有学生</li>
<li>可以随时启用/禁用规则,不会影响已保存的规则配置</li>
<li>如果"适用学生"字段为空,则自动化适用于所有学生</li>
<li>可以随时启用/禁用自动化,不会影响已保存的自动化配置</li>
</ul>
</div> */}
</div>
+6 -3
View File
@@ -76,11 +76,14 @@ export function Sidebar({ activeMenu, permission, onMenuChange }: SidebarProps):
<Menu.MenuItem value="score" icon={<HistoryIcon />}>
</Menu.MenuItem>
<Menu.MenuItem value="leaderboard" icon={<ViewListIcon />}>
<Menu.MenuItem value="auto-score" icon={<ReplayIcon />}>
</Menu.MenuItem>
<Menu.MenuItem value="auto-score" icon={<ReplayIcon />}>
<Menu.MenuItem value="leaderboard" icon={<ViewListIcon />}>
</Menu.MenuItem>
<Menu.MenuItem value="settlements" icon={<HistoryIcon />}>
<Menu.MenuItem value="settlements" icon={<HistoryIcon />}>
</Menu.MenuItem>
<Menu.MenuItem value="reasons" icon={<RootListIcon />} disabled={permission !== 'admin'}>