mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-19 19:04:23 +08:00
修你妈的
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
该项目使用pnpm作为包管理器
|
||||
每次写完代码后请务必跑一遍格式化和lint,typecheck,一个warning都不可以有
|
||||
每次完成任务请详细告知用户“你干了什么”和要确认的问题(如有)。
|
||||
@@ -0,0 +1,19 @@
|
||||
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
|
||||
|
||||
android {
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_21
|
||||
targetCompatibility JavaVersion.VERSION_21
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
|
||||
dependencies {
|
||||
implementation project(':capacitor-preferences')
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (hasProperty('postBuildExtras')) {
|
||||
postBuildExtras()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
|
||||
include ':capacitor-android'
|
||||
project(':capacitor-android').projectDir = new File('../node_modules/.pnpm/@capacitor+android@8.1.0_@capacitor+core@8.1.0/node_modules/@capacitor/android/capacitor')
|
||||
|
||||
include ':capacitor-preferences'
|
||||
project(':capacitor-preferences').projectDir = new File('../node_modules/.pnpm/@capacitor+preferences@8.0.1_@capacitor+core@8.1.0/node_modules/@capacitor/preferences/android')
|
||||
@@ -77,6 +77,7 @@
|
||||
"prettier": "^3.7.4",
|
||||
"react": "^19.2.1",
|
||||
"react-dom": "^19.2.1",
|
||||
"terser": "^5.46.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.2.6"
|
||||
},
|
||||
|
||||
Generated
+37
-21
@@ -120,7 +120,7 @@ importers:
|
||||
version: 11.0.0
|
||||
'@vitejs/plugin-react':
|
||||
specifier: ^5.1.1
|
||||
version: 5.1.2(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1))
|
||||
version: 5.1.2(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(terser@5.46.0))
|
||||
electron:
|
||||
specifier: ^39.2.6
|
||||
version: 39.2.7
|
||||
@@ -129,7 +129,7 @@ importers:
|
||||
version: 26.4.0(electron-builder-squirrel-windows@26.4.0)
|
||||
electron-vite:
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1))
|
||||
version: 5.0.0(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(terser@5.46.0))
|
||||
eslint:
|
||||
specifier: ^9.39.1
|
||||
version: 9.39.2(jiti@2.6.1)
|
||||
@@ -151,12 +151,15 @@ importers:
|
||||
react-dom:
|
||||
specifier: ^19.2.1
|
||||
version: 19.2.3(react@19.2.3)
|
||||
terser:
|
||||
specifier: ^5.46.0
|
||||
version: 5.46.0
|
||||
typescript:
|
||||
specifier: ^5.9.3
|
||||
version: 5.9.3
|
||||
vite:
|
||||
specifier: ^7.2.6
|
||||
version: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)
|
||||
version: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)(terser@5.46.0)
|
||||
|
||||
packages:
|
||||
|
||||
@@ -825,6 +828,9 @@ packages:
|
||||
resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@jridgewell/source-map@0.3.11':
|
||||
resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5':
|
||||
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
||||
|
||||
@@ -1243,79 +1249,66 @@ packages:
|
||||
resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.55.1':
|
||||
resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.55.1':
|
||||
resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.55.1':
|
||||
resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-loong64-gnu@4.55.1':
|
||||
resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-loong64-musl@4.55.1':
|
||||
resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.55.1':
|
||||
resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-musl@4.55.1':
|
||||
resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.55.1':
|
||||
resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-musl@4.55.1':
|
||||
resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.55.1':
|
||||
resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.55.1':
|
||||
resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.55.1':
|
||||
resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-openbsd-x64@4.55.1':
|
||||
resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==}
|
||||
@@ -1905,6 +1898,9 @@ packages:
|
||||
resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
commander@2.20.3:
|
||||
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
|
||||
|
||||
commander@5.1.0:
|
||||
resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -3885,6 +3881,11 @@ packages:
|
||||
resolution: {integrity: sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
terser@5.46.0:
|
||||
resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
text-hex@1.0.0:
|
||||
resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==}
|
||||
|
||||
@@ -4941,6 +4942,11 @@ snapshots:
|
||||
|
||||
'@jridgewell/resolve-uri@3.1.2': {}
|
||||
|
||||
'@jridgewell/source-map@0.3.11':
|
||||
dependencies:
|
||||
'@jridgewell/gen-mapping': 0.3.13
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5': {}
|
||||
|
||||
'@jridgewell/trace-mapping@0.3.31':
|
||||
@@ -5696,7 +5702,7 @@ snapshots:
|
||||
'@use-gesture/core': 10.3.0
|
||||
react: 19.2.3
|
||||
|
||||
'@vitejs/plugin-react@5.1.2(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1))':
|
||||
'@vitejs/plugin-react@5.1.2(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(terser@5.46.0))':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.5
|
||||
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5)
|
||||
@@ -5704,7 +5710,7 @@ snapshots:
|
||||
'@rolldown/pluginutils': 1.0.0-beta.53
|
||||
'@types/babel__core': 7.20.5
|
||||
react-refresh: 0.18.0
|
||||
vite: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)
|
||||
vite: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)(terser@5.46.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -6220,6 +6226,8 @@ snapshots:
|
||||
|
||||
commander@12.1.0: {}
|
||||
|
||||
commander@2.20.3: {}
|
||||
|
||||
commander@5.1.0: {}
|
||||
|
||||
commander@9.5.0:
|
||||
@@ -6421,7 +6429,7 @@ snapshots:
|
||||
|
||||
electron-to-chromium@1.5.267: {}
|
||||
|
||||
electron-vite@5.0.0(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)):
|
||||
electron-vite@5.0.0(vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(terser@5.46.0)):
|
||||
dependencies:
|
||||
'@babel/core': 7.28.5
|
||||
'@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.5)
|
||||
@@ -6429,7 +6437,7 @@ snapshots:
|
||||
esbuild: 0.25.12
|
||||
magic-string: 0.30.21
|
||||
picocolors: 1.1.1
|
||||
vite: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)
|
||||
vite: 7.3.1(@types/node@22.19.5)(jiti@2.6.1)(terser@5.46.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -8529,6 +8537,13 @@ snapshots:
|
||||
mkdirp: 0.5.6
|
||||
rimraf: 2.6.3
|
||||
|
||||
terser@5.46.0:
|
||||
dependencies:
|
||||
'@jridgewell/source-map': 0.3.11
|
||||
acorn: 8.15.0
|
||||
commander: 2.20.3
|
||||
source-map-support: 0.5.21
|
||||
|
||||
text-hex@1.0.0: {}
|
||||
|
||||
throttle-debounce@5.0.2: {}
|
||||
@@ -8717,7 +8732,7 @@ snapshots:
|
||||
extsprintf: 1.4.1
|
||||
optional: true
|
||||
|
||||
vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1):
|
||||
vite@7.3.1(@types/node@22.19.5)(jiti@2.6.1)(terser@5.46.0):
|
||||
dependencies:
|
||||
esbuild: 0.27.2
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
@@ -8729,6 +8744,7 @@ snapshots:
|
||||
'@types/node': 22.19.5
|
||||
fsevents: 2.3.3
|
||||
jiti: 2.6.1
|
||||
terser: 5.46.0
|
||||
|
||||
void-elements@3.1.0: {}
|
||||
|
||||
|
||||
@@ -225,9 +225,7 @@ export class EventRepository extends Service {
|
||||
return { startTime, rows }
|
||||
}
|
||||
|
||||
async getLastScoreTimeByStudents(
|
||||
studentNames: string[]
|
||||
): Promise<Map<string, Date>> {
|
||||
async getLastScoreTimeByStudents(studentNames: string[]): Promise<Map<string, Date>> {
|
||||
if (studentNames.length === 0) return new Map()
|
||||
|
||||
const repo = this.ctx.db.dataSource.getRepository(ScoreEventEntity)
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { Service } from '../../shared/kernel'
|
||||
import { MainContext } from '../context'
|
||||
import { AutoScoreRuleEngine, type RuleConfig, type AutoScoreContext } from '../../shared/autoScore/AutoScoreRuleEngine'
|
||||
import {
|
||||
AutoScoreRuleEngine,
|
||||
type RuleConfig,
|
||||
type AutoScoreContext
|
||||
} from '../../shared/autoScore/AutoScoreRuleEngine'
|
||||
|
||||
interface AutoScoreRule extends RuleConfig {
|
||||
id: number
|
||||
@@ -385,7 +389,7 @@ export class AutoScoreService extends Service {
|
||||
id: s.id,
|
||||
name: s.name,
|
||||
tags: s.tags || [],
|
||||
lastScoreTime: lastScoreTimeMap.get(s.name) || new Date(0),
|
||||
lastScoreTime: lastScoreTimeMap.get(s.name) || new Date(0)
|
||||
})),
|
||||
events: [],
|
||||
rule: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Outlet, useLocation, useNavigate } from 'react-router-dom'
|
||||
import { Layout, TabBar } from 'antd-mobile'
|
||||
import { TabBar } from 'antd-mobile'
|
||||
import {
|
||||
HomeOutlined,
|
||||
PlusCircleOutlined,
|
||||
@@ -8,8 +8,6 @@ import {
|
||||
} from '@ant-design/icons'
|
||||
import { useTheme } from '../App'
|
||||
|
||||
const { Content } = Layout
|
||||
|
||||
const tabs = [
|
||||
{ key: '/', title: '主页', icon: <HomeOutlined /> },
|
||||
{ key: '/score', title: '积分', icon: <PlusCircleOutlined /> },
|
||||
@@ -23,15 +21,15 @@ export function MobileLayout(): React.JSX.Element {
|
||||
const { isDark } = useTheme()
|
||||
|
||||
return (
|
||||
<Layout
|
||||
<div
|
||||
style={{
|
||||
minHeight: '100vh',
|
||||
backgroundColor: isDark ? '#1a1a1a' : '#f5f5f5'
|
||||
}}
|
||||
>
|
||||
<Content style={{ paddingBottom: '50px' }}>
|
||||
<div style={{ paddingBottom: '50px' }}>
|
||||
<Outlet />
|
||||
</Content>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
@@ -57,6 +55,6 @@ export function MobileLayout(): React.JSX.Element {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Layout>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -42,10 +42,6 @@ interface ActionItem {
|
||||
value: string
|
||||
reason: string
|
||||
}
|
||||
interface TagItem {
|
||||
id: number
|
||||
name: string
|
||||
}
|
||||
|
||||
interface AutoScoreRuleFormValues {
|
||||
name: string
|
||||
@@ -65,7 +61,6 @@ const ACTION_DEFINITIONS = [
|
||||
export const AutoScoreManager: React.FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const [rules, setRules] = useState<AutoScoreRule[]>([])
|
||||
const [allTags, setAllTags] = useState<TagItem[]>([])
|
||||
const [students, setStudents] = useState<{ id: number; name: string }[]>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [currentPage, setCurrentPage] = useState(1)
|
||||
@@ -85,19 +80,6 @@ export const AutoScoreManager: React.FC = () => {
|
||||
return def ? t(def.labelKey) : eventName
|
||||
}
|
||||
|
||||
async function fetchAllTags() {
|
||||
if (!(window as any).api) return
|
||||
try {
|
||||
const res = await (window as any).api.tagsGetAll()
|
||||
if (res.success && res.data) {
|
||||
setAllTags(res.data)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch tags:', e)
|
||||
messageApi.error(t('tags.fetchFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
const fetchRules = async () => {
|
||||
if (!(window as any).api) return
|
||||
|
||||
@@ -333,20 +315,6 @@ export const AutoScoreManager: React.FC = () => {
|
||||
])
|
||||
}
|
||||
|
||||
const handleRemoveTrigger = (id: number) => {
|
||||
setTriggerList((prev) => prev.filter((t) => t.id !== id))
|
||||
}
|
||||
|
||||
const handleTriggerChange = (
|
||||
id: number,
|
||||
field: keyof TriggerItem,
|
||||
value: string | number | 'AND' | 'OR'
|
||||
) => {
|
||||
setTriggerList((prev) =>
|
||||
prev.map((t) => (t.id === id ? { ...t, [field]: value } : t))
|
||||
)
|
||||
}
|
||||
|
||||
const handleAddAction = () => {
|
||||
const nextId = actionList.length ? Math.max(...actionList.map((a) => a.id)) + 1 : 1
|
||||
const defaultAction = ACTION_DEFINITIONS[0]
|
||||
@@ -369,14 +337,8 @@ export const AutoScoreManager: React.FC = () => {
|
||||
setActionList((prev) => prev.filter((a) => a.id !== id))
|
||||
}
|
||||
|
||||
const handleActionChange = (
|
||||
id: number,
|
||||
field: keyof ActionItem,
|
||||
value: string | number
|
||||
) => {
|
||||
setActionList((prev) =>
|
||||
prev.map((a) => (a.id === id ? { ...a, [field]: value } : a))
|
||||
)
|
||||
const handleActionChange = (id: number, field: keyof ActionItem, value: string | number) => {
|
||||
setActionList((prev) => prev.map((a) => (a.id === id ? { ...a, [field]: value } : a)))
|
||||
}
|
||||
|
||||
const columns: ColumnsType<AutoScoreRule> = [
|
||||
@@ -481,72 +443,6 @@ export const AutoScoreManager: React.FC = () => {
|
||||
}
|
||||
]
|
||||
|
||||
const renderTriggerItem = (trigger: TriggerItem, index: number) => (
|
||||
<div
|
||||
key={trigger.id}
|
||||
style={{
|
||||
padding: '12px',
|
||||
border: '1px solid #d9d9d9',
|
||||
borderRadius: '6px',
|
||||
backgroundColor: 'var(--ss-card-bg)',
|
||||
marginBottom: '8px'
|
||||
}}
|
||||
>
|
||||
<Space wrap>
|
||||
{index > 0 && (
|
||||
<Select
|
||||
value={trigger.relation}
|
||||
onChange={(value) => handleTriggerChange(trigger.id, 'relation', value)}
|
||||
style={{ width: 80 }}
|
||||
options={[
|
||||
{ label: 'AND', value: 'AND' },
|
||||
{ label: 'OR', value: 'OR' }
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
<Select
|
||||
value={trigger.eventName}
|
||||
onChange={(value) => handleTriggerChange(trigger.id, 'eventName', value)}
|
||||
style={{ width: 150 }}
|
||||
options={TRIGGER_DEFINITIONS.map((d) => ({
|
||||
label: t(d.labelKey),
|
||||
value: d.eventName
|
||||
}))}
|
||||
/>
|
||||
{trigger.eventName === 'interval_time_passed' && (
|
||||
<InputNumber
|
||||
value={trigger.value ? parseInt(trigger.value) : undefined}
|
||||
onChange={(value) => handleTriggerChange(trigger.id, 'value', String(value || 0))}
|
||||
placeholder={t('autoScore.minutesPlaceholder')}
|
||||
min={1}
|
||||
style={{ width: 120 }}
|
||||
addonAfter={t('autoScore.minutes')}
|
||||
/>
|
||||
)}
|
||||
{trigger.eventName === 'student_has_tag' && (
|
||||
<Select
|
||||
mode="multiple"
|
||||
allowClear
|
||||
value={trigger.value}
|
||||
onChange={(value) => handleTriggerChange(trigger.id, 'value', value)}
|
||||
onClick={fetchAllTags}
|
||||
style={{ width: 380 }}
|
||||
options={allTags.map((tag) => ({
|
||||
label: tag.name,
|
||||
value: tag.name
|
||||
}))}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
type="text"
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={() => handleRemoveTrigger(trigger.id)}
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
)
|
||||
|
||||
const renderActionItem = (action: ActionItem) => (
|
||||
<div
|
||||
key={action.id}
|
||||
@@ -635,9 +531,10 @@ export const AutoScoreManager: React.FC = () => {
|
||||
title={t('autoScore.whenTriggered')}
|
||||
>
|
||||
<Space orientation="vertical" style={{ width: '100%' }}>
|
||||
{/* {triggerList.map((trigger, index) => renderTriggerItem(trigger, index))}
|
||||
*/} <RuleComponent />
|
||||
<Button
|
||||
{/* {triggerList.map((trigger, index) => renderTriggerItem(trigger, index))}
|
||||
*/}{' '}
|
||||
<RuleComponent />
|
||||
<Button
|
||||
type="dashed"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={handleAddTrigger}
|
||||
@@ -652,7 +549,7 @@ export const AutoScoreManager: React.FC = () => {
|
||||
style={{ marginBottom: '24px', backgroundColor: 'var(--ss-card-bg)' }}
|
||||
title={t('autoScore.triggeredActions')}
|
||||
>
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<Space orientation="vertical" style={{ width: '100%' }}>
|
||||
{actionList.map((action) => renderActionItem(action))}
|
||||
<Button
|
||||
type="dashed"
|
||||
@@ -667,9 +564,7 @@ export const AutoScoreManager: React.FC = () => {
|
||||
|
||||
<div style={{ marginBottom: '24px', display: 'flex', gap: '12px' }}>
|
||||
<Button type="primary" onClick={handleSubmit}>
|
||||
{editingRuleId !== null
|
||||
? t('autoScore.updateAutomation')
|
||||
: t('autoScore.addAutomation')}
|
||||
{editingRuleId !== null ? t('autoScore.updateAutomation') : t('autoScore.addAutomation')}
|
||||
</Button>
|
||||
<Button onClick={handleResetForm}>
|
||||
{editingRuleId !== null ? t('autoScore.cancelEdit') : t('autoScore.resetForm')}
|
||||
@@ -701,4 +596,4 @@ export const AutoScoreManager: React.FC = () => {
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { Suspense, lazy } from 'react'
|
||||
import { Layout, Space, Button, Tag } from 'antd'
|
||||
import { Layout, Space, Button, Tag, Spin } from 'antd'
|
||||
import { Routes, Route, Navigate } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
@@ -104,19 +104,12 @@ export function ContentArea({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: '100%'
|
||||
height: '100%',
|
||||
flexDirection: 'column',
|
||||
gap: 16
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: '40px',
|
||||
height: '40px',
|
||||
border: '3px solid var(--ss-border-color)',
|
||||
borderTopColor: 'var(--ant-color-primary)',
|
||||
borderRadius: '50%',
|
||||
animation: 'spin 1s linear infinite'
|
||||
}}
|
||||
/>
|
||||
<Spin size="large" />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -598,7 +598,9 @@ export const OOBE: React.FC<oobeProps> = ({ visible, onComplete }) => {
|
||||
}}
|
||||
>
|
||||
<span style={{ color: '#52c41a' }}>✓</span>
|
||||
<span>{t('oobe.steps.start.features.score')}</span>
|
||||
<span style={{ color: isDark ? '#fff' : 'rgba(0, 0, 0, 0.88)' }}>
|
||||
{t('oobe.steps.start.features.score')}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
@@ -611,7 +613,9 @@ export const OOBE: React.FC<oobeProps> = ({ visible, onComplete }) => {
|
||||
}}
|
||||
>
|
||||
<span style={{ color: '#52c41a' }}>✓</span>
|
||||
<span>{t('oobe.steps.start.features.history')}</span>
|
||||
<span style={{ color: isDark ? '#fff' : 'rgba(0, 0, 0, 0.88)' }}>
|
||||
{t('oobe.steps.start.features.history')}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
@@ -624,7 +628,9 @@ export const OOBE: React.FC<oobeProps> = ({ visible, onComplete }) => {
|
||||
}}
|
||||
>
|
||||
<span style={{ color: '#52c41a' }}>✓</span>
|
||||
<span>{t('oobe.steps.start.features.settlement')}</span>
|
||||
<span style={{ color: isDark ? '#fff' : 'rgba(0, 0, 0, 0.88)' }}>
|
||||
{t('oobe.steps.start.features.settlement')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -659,7 +665,9 @@ export const OOBE: React.FC<oobeProps> = ({ visible, onComplete }) => {
|
||||
width: 480,
|
||||
maxWidth: '90vw',
|
||||
boxShadow: isDark ? '0 8px 32px rgba(0, 0, 0, 0.3)' : '0 8px 32px rgba(0, 0, 0, 0.1)',
|
||||
border: isDark ? '1px solid rgba(255, 255, 255, 0.08)' : '1px solid rgba(0, 0, 0, 0.06)'
|
||||
border: isDark ? '1px solid rgba(255, 255, 255, 0.08)' : '1px solid rgba(0, 0, 0, 0.06)',
|
||||
fontFamily:
|
||||
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", sans-serif, "Apple Color Emoji", "Segoe UI Emoji"'
|
||||
}}
|
||||
>
|
||||
{contextHolder}
|
||||
|
||||
@@ -226,7 +226,7 @@ export function Sidebar({ activeMenu, permission, onMenuChange }: SidebarProps):
|
||||
backgroundColor: 'var(--ss-card-bg)',
|
||||
border: '1px solid var(--ss-border-color)'
|
||||
}}
|
||||
bodyStyle={{ padding: '12px' }}
|
||||
styles={{ body: { padding: '12px' } }}
|
||||
>
|
||||
{contextHolder}
|
||||
<Space orientation="vertical" size={4} style={{ width: '100%' }}>
|
||||
|
||||
@@ -477,7 +477,7 @@ export const StudentManager: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
|
||||
footer={null}
|
||||
destroyOnHidden
|
||||
>
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<Space orientation="vertical" style={{ width: '100%' }}>
|
||||
<Button
|
||||
loading={xlsxLoading}
|
||||
disabled={!canEdit}
|
||||
|
||||
@@ -96,4 +96,4 @@ export const ActionComponent: React.FC<ActionComponentProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
export default ActionComponent
|
||||
export default ActionComponent
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
.queryBuilder {
|
||||
--querybuilder-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
|
||||
--querybuilder-font-family:
|
||||
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans',
|
||||
sans-serif;
|
||||
--querybuilder-background: var(--ss-card-bg, #ffffff);
|
||||
--querybuilder-color: var(--ss-text-main, rgba(0, 0, 0, 0.85));
|
||||
--querybuilder-border-radius: 6px;
|
||||
|
||||
@@ -42,15 +42,15 @@ export const ACTION_TYPE_KEYS = {
|
||||
|
||||
// Function to get fields with i18n support
|
||||
export const getFields = (t: (key: string) => string): Field[] => [
|
||||
{
|
||||
name: 'interval_time_passed',
|
||||
label: t('autoScore.triggerIntervalTime'),
|
||||
placeholder: t('autoScore.intervalMinutesPlaceholder')
|
||||
{
|
||||
name: 'interval_time_passed',
|
||||
label: t('autoScore.triggerIntervalTime'),
|
||||
placeholder: t('autoScore.intervalMinutesPlaceholder')
|
||||
},
|
||||
{
|
||||
name: 'student_has_tag',
|
||||
label: t('autoScore.triggerStudentTag'),
|
||||
placeholder: t('autoScore.tagNamesPlaceholder')
|
||||
{
|
||||
name: 'student_has_tag',
|
||||
label: t('autoScore.triggerStudentTag'),
|
||||
placeholder: t('autoScore.tagNamesPlaceholder')
|
||||
}
|
||||
]
|
||||
|
||||
@@ -116,4 +116,4 @@ export function autoScoreRuleToQuery(ruleData: AutoScoreRuleData): RuleGroupType
|
||||
combinator: currentCombinator,
|
||||
rules: rules.length > 0 ? rules : defaultQuery.rules
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,4 +81,4 @@ export const RuleComponent: React.FC<RuleComponentProps> = ({ initialData, onCha
|
||||
)
|
||||
}
|
||||
|
||||
export default RuleComponent
|
||||
export default RuleComponent
|
||||
|
||||
@@ -90,10 +90,11 @@ export class AutoScoreRuleEngine {
|
||||
students: AutoScoreContext['students'],
|
||||
value: string | undefined
|
||||
): Set<number> {
|
||||
const requiredTags = value
|
||||
?.split(',')
|
||||
.map((t) => t.trim())
|
||||
.filter(Boolean) || []
|
||||
const requiredTags =
|
||||
value
|
||||
?.split(',')
|
||||
.map((t) => t.trim())
|
||||
.filter(Boolean) || []
|
||||
|
||||
if (requiredTags.length === 0) return new Set()
|
||||
|
||||
@@ -136,11 +137,7 @@ export class AutoScoreRuleEngine {
|
||||
|
||||
switch (trigger.event) {
|
||||
case 'interval_time_passed':
|
||||
matchedIds = this.checkIntervalTimeTrigger(
|
||||
context.students,
|
||||
trigger.value,
|
||||
context.now
|
||||
)
|
||||
matchedIds = this.checkIntervalTimeTrigger(context.students, trigger.value, context.now)
|
||||
break
|
||||
case 'student_has_tag':
|
||||
matchedIds = this.checkStudentTagTrigger(context.students, trigger.value)
|
||||
@@ -241,4 +238,4 @@ export class AutoScoreRuleEngine {
|
||||
console.warn(`Unknown action event: ${actionType}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user