mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-19 19:04:23 +08:00
feat: 使用react-awesome-query-builder实现自动加分规则构建器
This commit is contained in:
@@ -19,8 +19,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^6.1.0",
|
"@ant-design/icons": "^6.1.0",
|
||||||
"@react-awesome-query-builder/antd": "6.7.0-alpha.0",
|
"@react-awesome-query-builder/antd": "6.7.0-alpha.0",
|
||||||
"@react-querybuilder/antd": "^8.14.0",
|
|
||||||
"@react-querybuilder/dnd": "^8.14.0",
|
|
||||||
"@tauri-apps/api": "^2.5.0",
|
"@tauri-apps/api": "^2.5.0",
|
||||||
"antd": "^6.3.1",
|
"antd": "^6.3.1",
|
||||||
"dayjs": "^1.11.20",
|
"dayjs": "^1.11.20",
|
||||||
@@ -28,9 +26,6 @@
|
|||||||
"json-rules-engine": "^7.3.1",
|
"json-rules-engine": "^7.3.1",
|
||||||
"pinyin-pro": "^3.27.0",
|
"pinyin-pro": "^3.27.0",
|
||||||
"react": "^19.2.1",
|
"react": "^19.2.1",
|
||||||
"react-dnd": "^16.0.1",
|
|
||||||
"react-dnd-html5-backend": "^16.0.1",
|
|
||||||
"react-dnd-touch-backend": "^16.0.1",
|
|
||||||
"react-dom": "^19.2.1",
|
"react-dom": "^19.2.1",
|
||||||
"react-i18next": "^16.5.6",
|
"react-i18next": "^16.5.6",
|
||||||
"react-querybuilder": "^8.14.0",
|
"react-querybuilder": "^8.14.0",
|
||||||
|
|||||||
Generated
+18
-122
@@ -14,12 +14,6 @@ importers:
|
|||||||
'@react-awesome-query-builder/antd':
|
'@react-awesome-query-builder/antd':
|
||||||
specifier: 6.7.0-alpha.0
|
specifier: 6.7.0-alpha.0
|
||||||
version: 6.7.0-alpha.0(@ant-design/icons@6.1.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(antd@6.3.2(moment@2.30.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(dayjs@1.11.20)(moment@2.30.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 6.7.0-alpha.0(@ant-design/icons@6.1.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(antd@6.3.2(moment@2.30.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(dayjs@1.11.20)(moment@2.30.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
'@react-querybuilder/antd':
|
|
||||||
specifier: ^8.14.0
|
|
||||||
version: 8.14.0(@ant-design/icons@6.1.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(antd@6.3.2(moment@2.30.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(dayjs@1.11.20)(react-querybuilder@8.14.0(@types/react@19.2.14)(json-logic-js@2.0.5)(react@19.2.4)(redux@5.0.1))(react@19.2.4)
|
|
||||||
'@react-querybuilder/dnd':
|
|
||||||
specifier: ^8.14.0
|
|
||||||
version: 8.14.0(react-dnd-html5-backend@16.0.1)(react-dnd-touch-backend@16.0.1)(react-dnd@16.0.1(@types/hoist-non-react-statics@3.3.7(@types/react@19.2.14))(@types/node@22.19.15)(@types/react@19.2.14)(react@19.2.4))(react-querybuilder@8.14.0(@types/react@19.2.14)(json-logic-js@2.0.5)(react@19.2.4)(redux@5.0.1))(react@19.2.4)
|
|
||||||
'@tauri-apps/api':
|
'@tauri-apps/api':
|
||||||
specifier: ^2.5.0
|
specifier: ^2.5.0
|
||||||
version: 2.10.1
|
version: 2.10.1
|
||||||
@@ -41,15 +35,6 @@ importers:
|
|||||||
react:
|
react:
|
||||||
specifier: ^19.2.1
|
specifier: ^19.2.1
|
||||||
version: 19.2.4
|
version: 19.2.4
|
||||||
react-dnd:
|
|
||||||
specifier: ^16.0.1
|
|
||||||
version: 16.0.1(@types/hoist-non-react-statics@3.3.7(@types/react@19.2.14))(@types/node@22.19.15)(@types/react@19.2.14)(react@19.2.4)
|
|
||||||
react-dnd-html5-backend:
|
|
||||||
specifier: ^16.0.1
|
|
||||||
version: 16.0.1
|
|
||||||
react-dnd-touch-backend:
|
|
||||||
specifier: ^16.0.1
|
|
||||||
version: 16.0.1
|
|
||||||
react-dom:
|
react-dom:
|
||||||
specifier: ^19.2.1
|
specifier: ^19.2.1
|
||||||
version: 19.2.4(react@19.2.4)
|
version: 19.2.4(react@19.2.4)
|
||||||
@@ -791,24 +776,6 @@ packages:
|
|||||||
react: ^16.8.4 || ^17.0.1 || ^18.0.0 || ^19.0.0
|
react: ^16.8.4 || ^17.0.1 || ^18.0.0 || ^19.0.0
|
||||||
react-dom: ^16.8.4 || ^17.0.1 || ^18.0.0 || ^19.0.0
|
react-dom: ^16.8.4 || ^17.0.1 || ^18.0.0 || ^19.0.0
|
||||||
|
|
||||||
'@react-dnd/asap@5.0.2':
|
|
||||||
resolution: {integrity: sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==}
|
|
||||||
|
|
||||||
'@react-dnd/invariant@4.0.2':
|
|
||||||
resolution: {integrity: sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==}
|
|
||||||
|
|
||||||
'@react-dnd/shallowequal@4.0.2':
|
|
||||||
resolution: {integrity: sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==}
|
|
||||||
|
|
||||||
'@react-querybuilder/antd@8.14.0':
|
|
||||||
resolution: {integrity: sha512-H6SMCmKtciOIJyXo29oLMMDIG1+ydE7eoIqAIhItsvJN7iLcAPhvKd5+CuHyOWj9tokEAHGpdm4BP+KPFAxTNw==}
|
|
||||||
peerDependencies:
|
|
||||||
'@ant-design/icons': '>=5'
|
|
||||||
antd: '>=5.11.0'
|
|
||||||
dayjs: '>=1'
|
|
||||||
react: '>=18'
|
|
||||||
react-querybuilder: 8.14.0
|
|
||||||
|
|
||||||
'@react-querybuilder/core@8.14.0':
|
'@react-querybuilder/core@8.14.0':
|
||||||
resolution: {integrity: sha512-j1pIY0Yyn/dXu9ZST/DVY7TqRmIO1hY/mZ8653DaeHaDzUF37tOdkm/NQDU9RfM0KXIWsJY5zlvYAR1DypZ+7g==}
|
resolution: {integrity: sha512-j1pIY0Yyn/dXu9ZST/DVY7TqRmIO1hY/mZ8653DaeHaDzUF37tOdkm/NQDU9RfM0KXIWsJY5zlvYAR1DypZ+7g==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -823,20 +790,6 @@ packages:
|
|||||||
sequelize:
|
sequelize:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@react-querybuilder/dnd@8.14.0':
|
|
||||||
resolution: {integrity: sha512-g0BHs9Vtbm7OhnhIq1pxJNY3JXNmSbA7NVTlOuqoAfFl0D46rZfGInVzoBfpwVpoLtQIXPO25Y5N/1/LcWuSNg==}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=18'
|
|
||||||
react-dnd: '>=14.0.0'
|
|
||||||
react-dnd-html5-backend: '>=14.0.0'
|
|
||||||
react-dnd-touch-backend: '>=14.0.0'
|
|
||||||
react-querybuilder: 8.14.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
react-dnd-html5-backend:
|
|
||||||
optional: true
|
|
||||||
react-dnd-touch-backend:
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@reduxjs/toolkit@2.11.2':
|
'@reduxjs/toolkit@2.11.2':
|
||||||
resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==}
|
resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -889,66 +842,79 @@ packages:
|
|||||||
resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==}
|
resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@rollup/rollup-linux-arm-musleabihf@4.59.0':
|
'@rollup/rollup-linux-arm-musleabihf@4.59.0':
|
||||||
resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==}
|
resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@rollup/rollup-linux-arm64-gnu@4.59.0':
|
'@rollup/rollup-linux-arm64-gnu@4.59.0':
|
||||||
resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==}
|
resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@rollup/rollup-linux-arm64-musl@4.59.0':
|
'@rollup/rollup-linux-arm64-musl@4.59.0':
|
||||||
resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==}
|
resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@rollup/rollup-linux-loong64-gnu@4.59.0':
|
'@rollup/rollup-linux-loong64-gnu@4.59.0':
|
||||||
resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==}
|
resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==}
|
||||||
cpu: [loong64]
|
cpu: [loong64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@rollup/rollup-linux-loong64-musl@4.59.0':
|
'@rollup/rollup-linux-loong64-musl@4.59.0':
|
||||||
resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==}
|
resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==}
|
||||||
cpu: [loong64]
|
cpu: [loong64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@rollup/rollup-linux-ppc64-gnu@4.59.0':
|
'@rollup/rollup-linux-ppc64-gnu@4.59.0':
|
||||||
resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==}
|
resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==}
|
||||||
cpu: [ppc64]
|
cpu: [ppc64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@rollup/rollup-linux-ppc64-musl@4.59.0':
|
'@rollup/rollup-linux-ppc64-musl@4.59.0':
|
||||||
resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==}
|
resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==}
|
||||||
cpu: [ppc64]
|
cpu: [ppc64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@rollup/rollup-linux-riscv64-gnu@4.59.0':
|
'@rollup/rollup-linux-riscv64-gnu@4.59.0':
|
||||||
resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==}
|
resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==}
|
||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@rollup/rollup-linux-riscv64-musl@4.59.0':
|
'@rollup/rollup-linux-riscv64-musl@4.59.0':
|
||||||
resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==}
|
resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==}
|
||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@rollup/rollup-linux-s390x-gnu@4.59.0':
|
'@rollup/rollup-linux-s390x-gnu@4.59.0':
|
||||||
resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==}
|
resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==}
|
||||||
cpu: [s390x]
|
cpu: [s390x]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@rollup/rollup-linux-x64-gnu@4.59.0':
|
'@rollup/rollup-linux-x64-gnu@4.59.0':
|
||||||
resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==}
|
resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@rollup/rollup-linux-x64-musl@4.59.0':
|
'@rollup/rollup-linux-x64-musl@4.59.0':
|
||||||
resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==}
|
resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@rollup/rollup-openbsd-x64@4.59.0':
|
'@rollup/rollup-openbsd-x64@4.59.0':
|
||||||
resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==}
|
resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==}
|
||||||
@@ -1012,30 +978,35 @@ packages:
|
|||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@tauri-apps/cli-linux-arm64-musl@2.10.1':
|
'@tauri-apps/cli-linux-arm64-musl@2.10.1':
|
||||||
resolution: {integrity: sha512-MIj78PDDGjkg3NqGptDOGgfXks7SYJwhiMh8SBoZS+vfdz7yP5jN18bNaLnDhsVIPARcAhE1TlsZe/8Yxo2zqg==}
|
resolution: {integrity: sha512-MIj78PDDGjkg3NqGptDOGgfXks7SYJwhiMh8SBoZS+vfdz7yP5jN18bNaLnDhsVIPARcAhE1TlsZe/8Yxo2zqg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@tauri-apps/cli-linux-riscv64-gnu@2.10.1':
|
'@tauri-apps/cli-linux-riscv64-gnu@2.10.1':
|
||||||
resolution: {integrity: sha512-X0lvOVUg8PCVaoEtEAnpxmnkwlE1gcMDTqfhbefICKDnOTJ5Est3qL0SrWxizDackIOKBcvtpejrSiVpuJI1kw==}
|
resolution: {integrity: sha512-X0lvOVUg8PCVaoEtEAnpxmnkwlE1gcMDTqfhbefICKDnOTJ5Est3qL0SrWxizDackIOKBcvtpejrSiVpuJI1kw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@tauri-apps/cli-linux-x64-gnu@2.10.1':
|
'@tauri-apps/cli-linux-x64-gnu@2.10.1':
|
||||||
resolution: {integrity: sha512-2/12bEzsJS9fAKybxgicCDFxYD1WEI9kO+tlDwX5znWG2GwMBaiWcmhGlZ8fi+DMe9CXlcVarMTYc0L3REIRxw==}
|
resolution: {integrity: sha512-2/12bEzsJS9fAKybxgicCDFxYD1WEI9kO+tlDwX5znWG2GwMBaiWcmhGlZ8fi+DMe9CXlcVarMTYc0L3REIRxw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [glibc]
|
||||||
|
|
||||||
'@tauri-apps/cli-linux-x64-musl@2.10.1':
|
'@tauri-apps/cli-linux-x64-musl@2.10.1':
|
||||||
resolution: {integrity: sha512-Y8J0ZzswPz50UcGOFuXGEMrxbjwKSPgXftx5qnkuMs2rmwQB5ssvLb6tn54wDSYxe7S6vlLob9vt0VKuNOaCIQ==}
|
resolution: {integrity: sha512-Y8J0ZzswPz50UcGOFuXGEMrxbjwKSPgXftx5qnkuMs2rmwQB5ssvLb6tn54wDSYxe7S6vlLob9vt0VKuNOaCIQ==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
|
libc: [musl]
|
||||||
|
|
||||||
'@tauri-apps/cli-win32-arm64-msvc@2.10.1':
|
'@tauri-apps/cli-win32-arm64-msvc@2.10.1':
|
||||||
resolution: {integrity: sha512-iSt5B86jHYAPJa/IlYw++SXtFPGnWtFJriHn7X0NFBVunF6zu9+/zOn8OgqIWSl8RgzhLGXQEEtGBdR4wzpVgg==}
|
resolution: {integrity: sha512-iSt5B86jHYAPJa/IlYw++SXtFPGnWtFJriHn7X0NFBVunF6zu9+/zOn8OgqIWSl8RgzhLGXQEEtGBdR4wzpVgg==}
|
||||||
@@ -1372,9 +1343,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
|
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
dnd-core@16.0.1:
|
|
||||||
resolution: {integrity: sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==}
|
|
||||||
|
|
||||||
doctrine@2.1.0:
|
doctrine@2.1.0:
|
||||||
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -2029,27 +1997,6 @@ packages:
|
|||||||
react: '>=16.9.0'
|
react: '>=16.9.0'
|
||||||
react-dom: '>=16.9.0'
|
react-dom: '>=16.9.0'
|
||||||
|
|
||||||
react-dnd-html5-backend@16.0.1:
|
|
||||||
resolution: {integrity: sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==}
|
|
||||||
|
|
||||||
react-dnd-touch-backend@16.0.1:
|
|
||||||
resolution: {integrity: sha512-NonoCABzzjyWGZuDxSG77dbgMZ2Wad7eQiCd/ECtsR2/NBLTjGksPUx9UPezZ1nQ/L7iD130Tz3RUshL/ClKLA==}
|
|
||||||
|
|
||||||
react-dnd@16.0.1:
|
|
||||||
resolution: {integrity: sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==}
|
|
||||||
peerDependencies:
|
|
||||||
'@types/hoist-non-react-statics': '>= 3.3.1'
|
|
||||||
'@types/node': '>= 12'
|
|
||||||
'@types/react': '>= 16'
|
|
||||||
react: '>= 16.14'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/hoist-non-react-statics':
|
|
||||||
optional: true
|
|
||||||
'@types/node':
|
|
||||||
optional: true
|
|
||||||
'@types/react':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
react-dom@19.2.4:
|
react-dom@19.2.4:
|
||||||
resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==}
|
resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -3210,20 +3157,6 @@ snapshots:
|
|||||||
- '@types/react-dom'
|
- '@types/react-dom'
|
||||||
- react-native
|
- react-native
|
||||||
|
|
||||||
'@react-dnd/asap@5.0.2': {}
|
|
||||||
|
|
||||||
'@react-dnd/invariant@4.0.2': {}
|
|
||||||
|
|
||||||
'@react-dnd/shallowequal@4.0.2': {}
|
|
||||||
|
|
||||||
'@react-querybuilder/antd@8.14.0(@ant-design/icons@6.1.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(antd@6.3.2(moment@2.30.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(dayjs@1.11.20)(react-querybuilder@8.14.0(@types/react@19.2.14)(json-logic-js@2.0.5)(react@19.2.4)(redux@5.0.1))(react@19.2.4)':
|
|
||||||
dependencies:
|
|
||||||
'@ant-design/icons': 6.1.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
|
||||||
antd: 6.3.2(moment@2.30.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
|
||||||
dayjs: 1.11.20
|
|
||||||
react: 19.2.4
|
|
||||||
react-querybuilder: 8.14.0(@types/react@19.2.14)(json-logic-js@2.0.5)(react@19.2.4)(redux@5.0.1)
|
|
||||||
|
|
||||||
'@react-querybuilder/core@8.14.0(json-logic-js@2.0.5)':
|
'@react-querybuilder/core@8.14.0(json-logic-js@2.0.5)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ts-jison/lexer': 0.4.1-alpha.1
|
'@ts-jison/lexer': 0.4.1-alpha.1
|
||||||
@@ -3233,15 +3166,6 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
json-logic-js: 2.0.5
|
json-logic-js: 2.0.5
|
||||||
|
|
||||||
'@react-querybuilder/dnd@8.14.0(react-dnd-html5-backend@16.0.1)(react-dnd-touch-backend@16.0.1)(react-dnd@16.0.1(@types/hoist-non-react-statics@3.3.7(@types/react@19.2.14))(@types/node@22.19.15)(@types/react@19.2.14)(react@19.2.4))(react-querybuilder@8.14.0(@types/react@19.2.14)(json-logic-js@2.0.5)(react@19.2.4)(redux@5.0.1))(react@19.2.4)':
|
|
||||||
dependencies:
|
|
||||||
react: 19.2.4
|
|
||||||
react-dnd: 16.0.1(@types/hoist-non-react-statics@3.3.7(@types/react@19.2.14))(@types/node@22.19.15)(@types/react@19.2.14)(react@19.2.4)
|
|
||||||
react-querybuilder: 8.14.0(@types/react@19.2.14)(json-logic-js@2.0.5)(react@19.2.4)(redux@5.0.1)
|
|
||||||
optionalDependencies:
|
|
||||||
react-dnd-html5-backend: 16.0.1
|
|
||||||
react-dnd-touch-backend: 16.0.1
|
|
||||||
|
|
||||||
'@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1))(react@19.2.4)':
|
'@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1))(react@19.2.4)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@standard-schema/spec': 1.1.0
|
'@standard-schema/spec': 1.1.0
|
||||||
@@ -3811,12 +3735,6 @@ snapshots:
|
|||||||
has-property-descriptors: 1.0.2
|
has-property-descriptors: 1.0.2
|
||||||
object-keys: 1.1.1
|
object-keys: 1.1.1
|
||||||
|
|
||||||
dnd-core@16.0.1:
|
|
||||||
dependencies:
|
|
||||||
'@react-dnd/asap': 5.0.2
|
|
||||||
'@react-dnd/invariant': 4.0.2
|
|
||||||
redux: 4.2.1
|
|
||||||
|
|
||||||
doctrine@2.1.0:
|
doctrine@2.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
esutils: 2.0.3
|
esutils: 2.0.3
|
||||||
@@ -4594,28 +4512,6 @@ snapshots:
|
|||||||
react-dom: 19.2.4(react@19.2.4)
|
react-dom: 19.2.4(react@19.2.4)
|
||||||
react-is: 18.3.1
|
react-is: 18.3.1
|
||||||
|
|
||||||
react-dnd-html5-backend@16.0.1:
|
|
||||||
dependencies:
|
|
||||||
dnd-core: 16.0.1
|
|
||||||
|
|
||||||
react-dnd-touch-backend@16.0.1:
|
|
||||||
dependencies:
|
|
||||||
'@react-dnd/invariant': 4.0.2
|
|
||||||
dnd-core: 16.0.1
|
|
||||||
|
|
||||||
react-dnd@16.0.1(@types/hoist-non-react-statics@3.3.7(@types/react@19.2.14))(@types/node@22.19.15)(@types/react@19.2.14)(react@19.2.4):
|
|
||||||
dependencies:
|
|
||||||
'@react-dnd/invariant': 4.0.2
|
|
||||||
'@react-dnd/shallowequal': 4.0.2
|
|
||||||
dnd-core: 16.0.1
|
|
||||||
fast-deep-equal: 3.1.3
|
|
||||||
hoist-non-react-statics: 3.3.2
|
|
||||||
react: 19.2.4
|
|
||||||
optionalDependencies:
|
|
||||||
'@types/hoist-non-react-statics': 3.3.7(@types/react@19.2.14)
|
|
||||||
'@types/node': 22.19.15
|
|
||||||
'@types/react': 19.2.14
|
|
||||||
|
|
||||||
react-dom@19.2.4(react@19.2.4):
|
react-dom@19.2.4(react@19.2.4):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.2.4
|
react: 19.2.4
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useState, useEffect } from "react"
|
import { useState, useEffect } from "react"
|
||||||
import { HolderOutlined } from "@ant-design/icons"
|
import { HolderOutlined } from "@ant-design/icons"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import RuleComponent from "./autoScore/ruleComponent"
|
import DemoQueryBuilder from "./autoScore/aaa"
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
Form,
|
Form,
|
||||||
@@ -426,44 +426,8 @@ export const AutoScoreManager: React.FC = () => {
|
|||||||
</Form>
|
</Form>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<RuleComponent />
|
<DemoQueryBuilder />
|
||||||
|
|
||||||
{/* <Card
|
|
||||||
style={{ marginBottom: '24px', backgroundColor: 'var(--ss-card-bg)' }}
|
|
||||||
title={t('autoScore.whenTriggered')}
|
|
||||||
>
|
|
||||||
<Space orientation="vertical" style={{ width: '100%' }}>
|
|
||||||
{triggerList.map((trigger, index) => renderTriggerItem(trigger, index))}
|
|
||||||
{' '}
|
|
||||||
|
|
||||||
<Button
|
|
||||||
type="dashed"
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
onClick={handleAddTrigger}
|
|
||||||
style={{ fontWeight: 'bolder', fontSize: 15 }}
|
|
||||||
>
|
|
||||||
{t('autoScore.addTrigger')}
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
</Card> */}
|
|
||||||
|
|
||||||
{/* <Card
|
|
||||||
style={{ marginBottom: '24px', backgroundColor: 'var(--ss-card-bg)' }}
|
|
||||||
title={t('autoScore.triggeredActions')}
|
|
||||||
>
|
|
||||||
<Space orientation="vertical" style={{ width: '100%' }}>
|
|
||||||
{actionList.map((action) => renderActionItem(action))}
|
|
||||||
<Button
|
|
||||||
type="dashed"
|
|
||||||
icon={<PlusOutlined />}
|
|
||||||
onClick={handleAddAction}
|
|
||||||
style={{ fontWeight: 'bolder', fontSize: 15 }}
|
|
||||||
>
|
|
||||||
{t('autoScore.addAction')}
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
</Card> */}
|
|
||||||
|
|
||||||
<div style={{ marginBottom: "24px", display: "flex", gap: "12px" }}>
|
<div style={{ marginBottom: "24px", display: "flex", gap: "12px" }}>
|
||||||
<Button type="primary" onClick={handleSubmit}>
|
<Button type="primary" onClick={handleSubmit}>
|
||||||
{editingRuleId !== null ? t("autoScore.updateAutomation") : t("autoScore.addAutomation")}
|
{editingRuleId !== null ? t("autoScore.updateAutomation") : t("autoScore.addAutomation")}
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
import type { Config, JsonGroup } from "@react-awesome-query-builder/antd"
|
||||||
|
import { Utils as QbUtils, AntdConfig } from "@react-awesome-query-builder/antd"
|
||||||
|
|
||||||
|
const InitialConfig = AntdConfig
|
||||||
|
|
||||||
|
export interface AutoScoreRuleConfig {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
type: "interval_time" | "student_tag"
|
||||||
|
config: Config
|
||||||
|
defaultQuery: JsonGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
export const intervalTimeConfig: Config = {
|
||||||
|
...InitialConfig,
|
||||||
|
ctx: InitialConfig.ctx,
|
||||||
|
fields: {
|
||||||
|
interval_minutes: {
|
||||||
|
label: "间隔时间(分钟)",
|
||||||
|
type: "number",
|
||||||
|
fieldSettings: {
|
||||||
|
min: 1,
|
||||||
|
max: 1440,
|
||||||
|
},
|
||||||
|
valueSources: ["value"],
|
||||||
|
preferWidgets: ["number"],
|
||||||
|
},
|
||||||
|
score_value: {
|
||||||
|
label: "加分数值",
|
||||||
|
type: "number",
|
||||||
|
fieldSettings: {
|
||||||
|
min: -100,
|
||||||
|
max: 100,
|
||||||
|
},
|
||||||
|
valueSources: ["value"],
|
||||||
|
preferWidgets: ["number"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const studentTagConfig: Config = {
|
||||||
|
...InitialConfig,
|
||||||
|
ctx: InitialConfig.ctx,
|
||||||
|
fields: {
|
||||||
|
tag_name: {
|
||||||
|
label: "学生标签",
|
||||||
|
type: "select",
|
||||||
|
valueSources: ["value"],
|
||||||
|
fieldSettings: {
|
||||||
|
listValues: [
|
||||||
|
{ value: "优秀", title: "优秀" },
|
||||||
|
{ value: "进步", title: "进步" },
|
||||||
|
{ value: "活跃", title: "活跃" },
|
||||||
|
{ value: "负责", title: "负责" },
|
||||||
|
{ value: "需关注", title: "需关注" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
score_value: {
|
||||||
|
label: "加分数值",
|
||||||
|
type: "number",
|
||||||
|
fieldSettings: {
|
||||||
|
min: -100,
|
||||||
|
max: 100,
|
||||||
|
},
|
||||||
|
valueSources: ["value"],
|
||||||
|
preferWidgets: ["number"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultIntervalTimeQuery: JsonGroup = {
|
||||||
|
id: QbUtils.uuid(),
|
||||||
|
type: "group",
|
||||||
|
children1: [
|
||||||
|
{
|
||||||
|
id: QbUtils.uuid(),
|
||||||
|
type: "rule",
|
||||||
|
properties: {
|
||||||
|
field: "interval_minutes",
|
||||||
|
operator: "greater",
|
||||||
|
value: [30],
|
||||||
|
valueSrc: ["value"],
|
||||||
|
valueType: ["number"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultStudentTagQuery: JsonGroup = {
|
||||||
|
id: QbUtils.uuid(),
|
||||||
|
type: "group",
|
||||||
|
children1: [
|
||||||
|
{
|
||||||
|
id: QbUtils.uuid(),
|
||||||
|
type: "rule",
|
||||||
|
properties: {
|
||||||
|
field: "tag_name",
|
||||||
|
operator: "select_equals",
|
||||||
|
value: ["优秀"],
|
||||||
|
valueSrc: ["value"],
|
||||||
|
valueType: ["select"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ruleConfigs: AutoScoreRuleConfig[] = [
|
||||||
|
{
|
||||||
|
id: "interval_time_rule",
|
||||||
|
name: "间隔时间加分",
|
||||||
|
description: "根据学生上次加分后的间隔时间自动加分",
|
||||||
|
type: "interval_time",
|
||||||
|
config: intervalTimeConfig,
|
||||||
|
defaultQuery: defaultIntervalTimeQuery,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "student_tag_rule",
|
||||||
|
name: "学生标签加分",
|
||||||
|
description: "根据学生拥有的标签自动加分",
|
||||||
|
type: "student_tag",
|
||||||
|
config: studentTagConfig,
|
||||||
|
defaultQuery: defaultStudentTagQuery,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const getRuleConfig = (
|
||||||
|
type: "interval_time" | "student_tag"
|
||||||
|
): AutoScoreRuleConfig | undefined => {
|
||||||
|
return ruleConfigs.find((r) => r.type === type)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const validateQuery = (tree: any, config: Config): boolean => {
|
||||||
|
try {
|
||||||
|
return QbUtils.isValidTree(tree, config)
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const exportQueryToJsonLogic = (tree: any, config: Config): string => {
|
||||||
|
try {
|
||||||
|
const result = QbUtils.jsonLogicFormat(tree, config)
|
||||||
|
return JSON.stringify(result.logic, null, 2)
|
||||||
|
} catch {
|
||||||
|
return "{}"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
import React, { useState, useCallback } from "react"
|
||||||
|
import { Card, Tabs, Button, Space, message, Form, Input, Select } from "antd"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
import type {
|
||||||
|
Config,
|
||||||
|
ImmutableTree,
|
||||||
|
BuilderProps,
|
||||||
|
JsonGroup,
|
||||||
|
} from "@react-awesome-query-builder/antd"
|
||||||
|
import { Utils as QbUtils, Query, Builder } from "@react-awesome-query-builder/antd"
|
||||||
|
import "@react-awesome-query-builder/antd/css/styles.css"
|
||||||
|
import { ruleConfigs, getRuleConfig, exportQueryToJsonLogic } from "./BuilderUtils"
|
||||||
|
|
||||||
|
interface RuleProps {
|
||||||
|
onRuleSubmit?: (ruleData: {
|
||||||
|
type: string
|
||||||
|
name: string
|
||||||
|
query: JsonGroup
|
||||||
|
jsonLogic: string
|
||||||
|
studentNames: string[]
|
||||||
|
}) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Rule: React.FC<RuleProps> = ({ onRuleSubmit }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const [messageApi, contextHolder] = message.useMessage()
|
||||||
|
const [form] = Form.useForm()
|
||||||
|
const [activeTab, setActiveTab] = useState<string>("interval_time")
|
||||||
|
const [students, setStudents] = useState<{ id: number; name: string }[]>([])
|
||||||
|
|
||||||
|
const [intervalTimeState, setIntervalTimeState] = useState(() => {
|
||||||
|
const config = getRuleConfig("interval_time")
|
||||||
|
return {
|
||||||
|
tree: QbUtils.loadTree(config?.defaultQuery || { id: QbUtils.uuid(), type: "group" }),
|
||||||
|
config: config?.config || ({} as Config),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const [studentTagState, setStudentTagState] = useState(() => {
|
||||||
|
const config = getRuleConfig("student_tag")
|
||||||
|
return {
|
||||||
|
tree: QbUtils.loadTree(config?.defaultQuery || { id: QbUtils.uuid(), type: "group" }),
|
||||||
|
config: config?.config || ({} as Config),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const fetchStudents = useCallback(async () => {
|
||||||
|
if (!(window as any).api) return
|
||||||
|
try {
|
||||||
|
const res = await (window as any).api.queryStudents({})
|
||||||
|
if (res.success) {
|
||||||
|
setStudents(res.data)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch students:", error)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchStudents()
|
||||||
|
}, [fetchStudents])
|
||||||
|
|
||||||
|
const handleIntervalTimeChange = useCallback((immutableTree: ImmutableTree, config: Config) => {
|
||||||
|
setIntervalTimeState((prev) => ({ ...prev, tree: immutableTree, config }))
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleStudentTagChange = useCallback((immutableTree: ImmutableTree, config: Config) => {
|
||||||
|
setStudentTagState((prev) => ({ ...prev, tree: immutableTree, config }))
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const renderBuilder = useCallback(
|
||||||
|
(props: BuilderProps) => (
|
||||||
|
<div className="query-builder-container" style={{ padding: "10px" }}>
|
||||||
|
<div className="query-builder qb-lite">
|
||||||
|
<Builder {...props} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
const values = form.getFieldsValue(true)
|
||||||
|
if (!values.name) {
|
||||||
|
messageApi.warning(t("autoScore.nameRequired"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const ruleConfig = getRuleConfig(activeTab as "interval_time" | "student_tag")
|
||||||
|
if (!ruleConfig) {
|
||||||
|
messageApi.error(t("autoScore.ruleConfigNotFound"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentState = activeTab === "interval_time" ? intervalTimeState : studentTagState
|
||||||
|
const jsonLogic = exportQueryToJsonLogic(currentState.tree, currentState.config)
|
||||||
|
|
||||||
|
if (onRuleSubmit) {
|
||||||
|
onRuleSubmit({
|
||||||
|
type: activeTab,
|
||||||
|
name: values.name,
|
||||||
|
query: QbUtils.getTree(currentState.tree) as unknown as JsonGroup,
|
||||||
|
jsonLogic,
|
||||||
|
studentNames: values.studentNames || [],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
messageApi.success(t("autoScore.ruleSaved"))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
form.resetFields()
|
||||||
|
const intervalConfig = getRuleConfig("interval_time")
|
||||||
|
const tagConfig = getRuleConfig("student_tag")
|
||||||
|
|
||||||
|
setIntervalTimeState({
|
||||||
|
tree: QbUtils.loadTree(intervalConfig?.defaultQuery || { id: QbUtils.uuid(), type: "group" }),
|
||||||
|
config: intervalConfig?.config || ({} as Config),
|
||||||
|
})
|
||||||
|
|
||||||
|
setStudentTagState({
|
||||||
|
tree: QbUtils.loadTree(tagConfig?.defaultQuery || { id: QbUtils.uuid(), type: "group" }),
|
||||||
|
config: tagConfig?.config || ({} as Config),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabItems = [
|
||||||
|
{
|
||||||
|
key: "interval_time",
|
||||||
|
label: t("autoScore.intervalTimeRule"),
|
||||||
|
children: (
|
||||||
|
<div>
|
||||||
|
<p style={{ marginBottom: 16, color: "var(--ss-text-secondary)" }}>
|
||||||
|
{ruleConfigs[0].description}
|
||||||
|
</p>
|
||||||
|
<Query
|
||||||
|
{...intervalTimeState.config}
|
||||||
|
value={intervalTimeState.tree}
|
||||||
|
onChange={handleIntervalTimeChange}
|
||||||
|
renderBuilder={renderBuilder}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "student_tag",
|
||||||
|
label: t("autoScore.studentTagRule"),
|
||||||
|
children: (
|
||||||
|
<div>
|
||||||
|
<p style={{ marginBottom: 16, color: "var(--ss-text-secondary)" }}>
|
||||||
|
{ruleConfigs[1].description}
|
||||||
|
</p>
|
||||||
|
<Query
|
||||||
|
{...studentTagState.config}
|
||||||
|
value={studentTagState.tree}
|
||||||
|
onChange={handleStudentTagChange}
|
||||||
|
renderBuilder={renderBuilder}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{contextHolder}
|
||||||
|
<Card style={{ marginBottom: 24, backgroundColor: "var(--ss-card-bg)" }}>
|
||||||
|
<Form form={form} layout="vertical">
|
||||||
|
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 24 }}>
|
||||||
|
<Form.Item
|
||||||
|
label={t("autoScore.ruleName")}
|
||||||
|
name="name"
|
||||||
|
rules={[{ required: true, message: t("autoScore.nameRequired") }]}
|
||||||
|
>
|
||||||
|
<Input placeholder={t("autoScore.ruleNamePlaceholder")} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label={t("autoScore.applicableStudents")} name="studentNames">
|
||||||
|
<Select
|
||||||
|
mode="multiple"
|
||||||
|
showSearch
|
||||||
|
placeholder={t("autoScore.studentPlaceholder")}
|
||||||
|
options={students.map((student) => ({ label: student.name, value: student.name }))}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card style={{ marginBottom: 24, backgroundColor: "var(--ss-card-bg)" }}>
|
||||||
|
<Tabs activeKey={activeTab} onChange={setActiveTab} items={tabItems} />
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Space>
|
||||||
|
<Button type="primary" onClick={handleSubmit}>
|
||||||
|
{t("autoScore.saveRule")}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleReset}>{t("autoScore.resetForm")}</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Rule
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import React, { useState, useCallback } from "react";
|
||||||
|
|
||||||
|
// >>>
|
||||||
|
import type { JsonGroup, Config, ImmutableTree, BuilderProps } from '@react-awesome-query-builder/antd';
|
||||||
|
import { Utils as QbUtils, Query, Builder, AntdConfig } from '@react-awesome-query-builder/antd';
|
||||||
|
const InitialConfig = AntdConfig;
|
||||||
|
// <<<
|
||||||
|
|
||||||
|
// You need to provide your own config. See below 'Config format'
|
||||||
|
const config: Config = {
|
||||||
|
...InitialConfig,
|
||||||
|
fields: {
|
||||||
|
...InitialConfig.fields,
|
||||||
|
qty: {
|
||||||
|
type: 'number',
|
||||||
|
label: 'Quantity',
|
||||||
|
fieldSettings: {
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// complex
|
||||||
|
user: {
|
||||||
|
type: '!struct', // special keyword for complex fields
|
||||||
|
label: 'User',
|
||||||
|
subfields: {
|
||||||
|
// subfields of complex field
|
||||||
|
name: {
|
||||||
|
type: 'text',
|
||||||
|
label: 'Name',
|
||||||
|
label2: 'User name', //optional, see below
|
||||||
|
fieldSettings: {
|
||||||
|
validateValue: (val: string, _fieldSettings: any) => (val.length <= 20),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// You can load query value from your backend storage (for saving see `Query.onChange()`)
|
||||||
|
const queryValue: JsonGroup = { id: QbUtils.uuid(), type: "group" };
|
||||||
|
|
||||||
|
const DemoQueryBuilder: React.FC = () => {
|
||||||
|
const [state, setState] = useState({
|
||||||
|
tree: QbUtils.loadTree(queryValue),
|
||||||
|
config: config
|
||||||
|
});
|
||||||
|
|
||||||
|
const onChange = useCallback((immutableTree: ImmutableTree, config: Config) => {
|
||||||
|
// Tip: for better performance you can apply `throttle` - see `packages/examples/src/demo`
|
||||||
|
setState(prevState => ({ ...prevState, tree: immutableTree, config: config }));
|
||||||
|
|
||||||
|
const jsonTree = QbUtils.getTree(immutableTree);
|
||||||
|
console.log(jsonTree);
|
||||||
|
// `jsonTree` can be saved to backend, and later loaded to `queryValue`
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const renderBuilder = useCallback((props: BuilderProps) => (
|
||||||
|
<div className="query-builder-container" style={{ padding: "10px" }}>
|
||||||
|
<div className="query-builder qb-lite">
|
||||||
|
<Builder {...props} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
), []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Query
|
||||||
|
{...config}
|
||||||
|
value={state.tree}
|
||||||
|
onChange={onChange}
|
||||||
|
renderBuilder={renderBuilder}
|
||||||
|
/>
|
||||||
|
<div className="query-builder-result">
|
||||||
|
<div>
|
||||||
|
Query string:{" "}
|
||||||
|
<pre>
|
||||||
|
{JSON.stringify(QbUtils.queryString(state.tree, state.config))}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
MongoDb query:{" "}
|
||||||
|
<pre>
|
||||||
|
{JSON.stringify(QbUtils.mongodbFormat(state.tree, state.config))}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
SQL where:{" "}
|
||||||
|
<pre>
|
||||||
|
{JSON.stringify(QbUtils.sqlFormat(state.tree, state.config))}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
JsonLogic:{" "}
|
||||||
|
<pre>
|
||||||
|
{JSON.stringify(QbUtils.jsonLogicFormat(state.tree, state.config))}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default DemoQueryBuilder;
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
import { Space, Input, InputNumber, Select, Button } from "antd"
|
|
||||||
import { PlusOutlined, DeleteOutlined } from "@ant-design/icons"
|
|
||||||
import { useTranslation } from "react-i18next"
|
|
||||||
|
|
||||||
const SCORE_RANGE = { MIN: -999, MAX: 999 }
|
|
||||||
|
|
||||||
const ACTION_DEFINITIONS = [
|
|
||||||
{ eventName: "add_score", labelKey: "autoScore.actionAddScore" },
|
|
||||||
{ eventName: "add_tag", labelKey: "autoScore.actionAddTag" },
|
|
||||||
]
|
|
||||||
|
|
||||||
interface ActionItem {
|
|
||||||
id: number
|
|
||||||
eventName: string
|
|
||||||
value?: string
|
|
||||||
reason?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ActionComponentProps {
|
|
||||||
actions: ActionItem[]
|
|
||||||
onAdd: () => void
|
|
||||||
onRemove: (id: number) => void
|
|
||||||
onChange: (id: number, field: keyof ActionItem, value: string | number) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ActionComponent: React.FC<ActionComponentProps> = ({
|
|
||||||
actions,
|
|
||||||
onAdd,
|
|
||||||
onRemove,
|
|
||||||
onChange,
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
const handleActionChange = (id: number, field: keyof ActionItem, value: string | number) => {
|
|
||||||
onChange(id, field, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Space direction="vertical" style={{ width: "100%" }}>
|
|
||||||
{actions.map((action) => (
|
|
||||||
<div
|
|
||||||
key={action.id}
|
|
||||||
style={{
|
|
||||||
padding: "12px",
|
|
||||||
border: "1px solid #d9d9d9",
|
|
||||||
borderRadius: "6px",
|
|
||||||
backgroundColor: "var(--ss-card-bg)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Space style={{ width: "100%" }} wrap>
|
|
||||||
<Select
|
|
||||||
value={action.eventName}
|
|
||||||
onChange={(value) => handleActionChange(action.id, "eventName", value)}
|
|
||||||
style={{ width: 150 }}
|
|
||||||
options={ACTION_DEFINITIONS.map((d) => ({
|
|
||||||
label: t(d.labelKey),
|
|
||||||
value: d.eventName,
|
|
||||||
}))}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{action.eventName === "add_score" && (
|
|
||||||
<InputNumber
|
|
||||||
value={action.value ? parseInt(action.value) : undefined}
|
|
||||||
onChange={(value) => handleActionChange(action.id, "value", String(value || 0))}
|
|
||||||
placeholder={t("autoScore.scorePlaceholder")}
|
|
||||||
min={SCORE_RANGE.MIN}
|
|
||||||
max={SCORE_RANGE.MAX}
|
|
||||||
style={{ width: 120 }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{action.eventName === "add_tag" && (
|
|
||||||
<Input
|
|
||||||
value={action.value}
|
|
||||||
onChange={(e) => handleActionChange(action.id, "value", e.target.value)}
|
|
||||||
placeholder={t("autoScore.tagNamePlaceholder")}
|
|
||||||
style={{ width: 200 }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Input
|
|
||||||
value={action.reason}
|
|
||||||
onChange={(e) => handleActionChange(action.id, "reason", e.target.value)}
|
|
||||||
placeholder={t("autoScore.reasonPlaceholder")}
|
|
||||||
style={{ flex: 1, minWidth: 200 }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
type="text"
|
|
||||||
danger
|
|
||||||
icon={<DeleteOutlined />}
|
|
||||||
onClick={() => onRemove(action.id)}
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<Button type="dashed" icon={<PlusOutlined />} onClick={onAdd} block>
|
|
||||||
{t("autoScore.addAction")}
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ActionComponent
|
|
||||||
@@ -1,251 +0,0 @@
|
|||||||
.ruleGroup {
|
|
||||||
background-color: var(--ss-card-bg, #fff);
|
|
||||||
border: 1px solid var(--ss-border-color, #d9d9d9);
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 16px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ruleGroup-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ruleGroup-combinators,
|
|
||||||
.ruleGroup-addRule,
|
|
||||||
.ruleGroup-addGroup {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ruleGroup-body {
|
|
||||||
padding-left: 12px;
|
|
||||||
border-left: 2px solid var(--ss-primary-color, #1677ff);
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rule {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
padding: 8px 12px;
|
|
||||||
background-color: var(--ss-item-bg, rgba(0, 0, 0, 0.02));
|
|
||||||
border-radius: 6px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rule:hover {
|
|
||||||
background-color: var(--ss-item-hover, rgba(0, 0, 0, 0.04));
|
|
||||||
}
|
|
||||||
|
|
||||||
.rule-fields,
|
|
||||||
.rule-operators,
|
|
||||||
.rule-value,
|
|
||||||
.rule-subfields {
|
|
||||||
min-width: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rule-fields .ant-select-selector,
|
|
||||||
.rule-operators .ant-select-selector,
|
|
||||||
.rule-subfields .ant-select-selector {
|
|
||||||
background-color: var(--ss-input-bg, #fff) !important;
|
|
||||||
border-color: var(--ss-border-color, #d9d9d9) !important;
|
|
||||||
color: var(--ss-text-main, rgba(0, 0, 0, 0.88)) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rule-fields .ant-select-selector:hover,
|
|
||||||
.rule-operators .ant-select-selector:hover,
|
|
||||||
.rule-subfields .ant-select-selector:hover {
|
|
||||||
border-color: var(--ss-primary-color, #1677ff) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rule-fields .ant-select-focused .ant-select-selector,
|
|
||||||
.rule-operators .ant-select-focused .ant-select-selector,
|
|
||||||
.rule-subfields .ant-select-focused .ant-select-selector {
|
|
||||||
border-color: var(--ss-primary-color, #1677ff) !important;
|
|
||||||
box-shadow: 0 0 0 2px rgba(var(--ss-primary-rgb, 22, 119, 255), 0.1) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rule-value .ant-input,
|
|
||||||
.rule-value .ant-input-number,
|
|
||||||
.rule-value .ant-select-selector {
|
|
||||||
background-color: var(--ss-input-bg, #fff) !important;
|
|
||||||
border-color: var(--ss-border-color, #d9d9d9) !important;
|
|
||||||
color: var(--ss-text-main, rgba(0, 0, 0, 0.88)) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rule-value .ant-input:hover,
|
|
||||||
.rule-value .ant-input-number:hover,
|
|
||||||
.rule-value .ant-select-selector:hover {
|
|
||||||
border-color: var(--ss-primary-color, #1677ff) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rule-value .ant-input:focus,
|
|
||||||
.rule-value .ant-input-number-focused,
|
|
||||||
.rule-value .ant-select-focused .ant-select-selector {
|
|
||||||
border-color: var(--ss-primary-color, #1677ff) !important;
|
|
||||||
box-shadow: 0 0 0 2px rgba(var(--ss-primary-rgb, 22, 119, 255), 0.1) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rule-value .ant-input-number-input {
|
|
||||||
color: var(--ss-text-main, rgba(0, 0, 0, 0.88)) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rule-remove,
|
|
||||||
.ruleGroup-remove {
|
|
||||||
color: #fff !important;
|
|
||||||
background-color: var(--ant-color-error, #ff4d4f) !important;
|
|
||||||
border-color: var(--ant-color-error, #ff4d4f) !important;
|
|
||||||
transition: all 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rule-remove:hover,
|
|
||||||
.ruleGroup-remove:hover {
|
|
||||||
color: #fff !important;
|
|
||||||
background-color: #ff7875 !important;
|
|
||||||
border-color: #ff7875 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ruleGroup-addRule,
|
|
||||||
.ruleGroup-addGroup {
|
|
||||||
color: var(--ss-primary-color, #1677ff) !important;
|
|
||||||
border-color: var(--ss-primary-color, #1677ff) !important;
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ruleGroup-addRule:hover,
|
|
||||||
.ruleGroup-addGroup:hover {
|
|
||||||
color: #fff !important;
|
|
||||||
background-color: var(--ss-primary-color, #1677ff) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.betweenRules {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
margin: 8px 0;
|
|
||||||
color: var(--ss-text-secondary, rgba(0, 0, 0, 0.45));
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.betweenRules::before,
|
|
||||||
.betweenRules::after {
|
|
||||||
content: "";
|
|
||||||
flex: 1;
|
|
||||||
height: 1px;
|
|
||||||
background-color: var(--ss-border-color, #d9d9d9);
|
|
||||||
}
|
|
||||||
|
|
||||||
.betweenRules::before {
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.betweenRules::after {
|
|
||||||
margin-left: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dragHandle {
|
|
||||||
cursor: grab;
|
|
||||||
color: var(--ss-text-secondary, rgba(0, 0, 0, 0.45));
|
|
||||||
padding: 4px;
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dragHandle:hover {
|
|
||||||
color: var(--ss-text-main, rgba(0, 0, 0, 0.88));
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .ant-select-dropdown {
|
|
||||||
background-color: var(--ss-card-bg, #fff);
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .ant-select-item {
|
|
||||||
color: var(--ss-text-main, rgba(0, 0, 0, 0.88));
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .ant-select-item-option-active {
|
|
||||||
background-color: var(--ss-item-hover, rgba(0, 0, 0, 0.04));
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .ant-select-item-option-selected {
|
|
||||||
background-color: rgba(var(--ss-primary-rgb, 22, 119, 255), 0.1);
|
|
||||||
color: var(--ss-primary-color, #1677ff);
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .ant-btn {
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryBuilder .ant-select-selector,
|
|
||||||
.queryBuilder .ant-input,
|
|
||||||
.queryBuilder .ant-input-number {
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
[theme-mode="dark"] .ruleGroup {
|
|
||||||
background-color: var(--ss-card-bg, #1f1f1f);
|
|
||||||
border-color: var(--ss-border-color, #424242);
|
|
||||||
}
|
|
||||||
|
|
||||||
[theme-mode="dark"] .rule {
|
|
||||||
background-color: var(--ss-item-bg, rgba(255, 255, 255, 0.04));
|
|
||||||
}
|
|
||||||
|
|
||||||
[theme-mode="dark"] .rule:hover {
|
|
||||||
background-color: var(--ss-item-hover, rgba(255, 255, 255, 0.08));
|
|
||||||
}
|
|
||||||
|
|
||||||
[theme-mode="dark"] .rule-fields .ant-select-selector,
|
|
||||||
[theme-mode="dark"] .rule-operators .ant-select-selector,
|
|
||||||
[theme-mode="dark"] .rule-subfields .ant-select-selector,
|
|
||||||
[theme-mode="dark"] .rule-value .ant-input,
|
|
||||||
[theme-mode="dark"] .rule-value .ant-input-number,
|
|
||||||
[theme-mode="dark"] .rule-value .ant-select-selector {
|
|
||||||
background-color: var(--ss-input-bg, #141414) !important;
|
|
||||||
border-color: var(--ss-border-color, #424242) !important;
|
|
||||||
color: var(--ss-text-main, rgba(255, 255, 255, 0.85)) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
[theme-mode="dark"] .rule-value .ant-input-number-input {
|
|
||||||
color: var(--ss-text-main, rgba(255, 255, 255, 0.85)) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
[theme-mode="dark"] .betweenRules {
|
|
||||||
color: var(--ss-text-secondary, rgba(255, 255, 255, 0.45));
|
|
||||||
}
|
|
||||||
|
|
||||||
[theme-mode="dark"] .betweenRules::before,
|
|
||||||
[theme-mode="dark"] .betweenRules::after {
|
|
||||||
background-color: var(--ss-border-color, #424242);
|
|
||||||
}
|
|
||||||
|
|
||||||
[theme-mode="dark"] .dragHandle {
|
|
||||||
color: var(--ss-text-secondary, rgba(255, 255, 255, 0.45));
|
|
||||||
}
|
|
||||||
|
|
||||||
[theme-mode="dark"] .dragHandle:hover {
|
|
||||||
color: var(--ss-text-main, rgba(255, 255, 255, 0.85));
|
|
||||||
}
|
|
||||||
|
|
||||||
[theme-mode="dark"] .queryBuilder .ant-select-dropdown {
|
|
||||||
background-color: var(--ss-card-bg, #1f1f1f);
|
|
||||||
}
|
|
||||||
|
|
||||||
[theme-mode="dark"] .queryBuilder .ant-select-item {
|
|
||||||
color: var(--ss-text-main, rgba(255, 255, 255, 0.85));
|
|
||||||
}
|
|
||||||
|
|
||||||
[theme-mode="dark"] .queryBuilder .ant-select-item-option-active {
|
|
||||||
background-color: var(--ss-item-hover, rgba(255, 255, 255, 0.08));
|
|
||||||
}
|
|
||||||
|
|
||||||
[theme-mode="dark"] .queryBuilder .ant-select-item-option-selected {
|
|
||||||
background-color: rgba(var(--ss-primary-rgb, 22, 119, 255), 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ruleGroup-header .ant-select-selector {
|
|
||||||
min-width: 80px;
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
import type { RuleGroupType, Field, RuleType } from "react-querybuilder"
|
|
||||||
import { defaultOperators } from "react-querybuilder"
|
|
||||||
import { fetchAllTags } from "../TagEditorDialog"
|
|
||||||
|
|
||||||
const tags = await fetchAllTags()
|
|
||||||
|
|
||||||
export interface AutoScoreTrigger {
|
|
||||||
event: string
|
|
||||||
value?: string
|
|
||||||
relation?: "AND" | "OR"
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AutoScoreAction {
|
|
||||||
event: string
|
|
||||||
value?: string
|
|
||||||
reason?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AutoScoreRuleData {
|
|
||||||
triggers: AutoScoreTrigger[]
|
|
||||||
actions: AutoScoreAction[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export const validator = (r: RuleType) => !!r.value
|
|
||||||
|
|
||||||
export const getFields = (t: (key: string) => string): Field[] => [
|
|
||||||
{
|
|
||||||
name: "student_has_tag",
|
|
||||||
label: t("triggers.studentTag.label"),
|
|
||||||
placeholder: t("triggers.studentTag.placeholder"),
|
|
||||||
valueEditorType: "multiselect",
|
|
||||||
values: tags.map((tag: { id: number; name: string }) => tag.name),
|
|
||||||
defaultValue: tags.length > 0 ? [tags[0].name] : [],
|
|
||||||
operators: defaultOperators.filter((op) => op.name === "in"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "interval_time_passed",
|
|
||||||
label: t("triggers.intervalTime.label"),
|
|
||||||
matchModes: ["all"],
|
|
||||||
subproperties: [
|
|
||||||
{
|
|
||||||
name: "month",
|
|
||||||
label: t("triggers.intervalTime.monthLabel"),
|
|
||||||
inputType: "number",
|
|
||||||
datatype: "month",
|
|
||||||
operators: ["="],
|
|
||||||
},
|
|
||||||
/* { name: 'week', label: t('triggers.intervalTime.weekLabel'), inputType: 'week', datatype: 'week', operators: ['='] },
|
|
||||||
*/ {
|
|
||||||
name: "time",
|
|
||||||
label: t("triggers.intervalTime.timeLabel"),
|
|
||||||
inputType: "time",
|
|
||||||
datatype: "time",
|
|
||||||
operators: ["="],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
export const defaultQuery: RuleGroupType = {
|
|
||||||
combinator: "and",
|
|
||||||
rules: [{ field: "interval_time_passed", operator: "=", value: "1440" }],
|
|
||||||
}
|
|
||||||
|
|
||||||
export function queryToAutoScoreRule(): AutoScoreRuleData {
|
|
||||||
const triggers: AutoScoreTrigger[] = []
|
|
||||||
|
|
||||||
/* const processRuleGroup = (group: RuleGroupType, relation: 'AND' | 'OR' = 'AND') => {
|
|
||||||
group.rules.forEach((rule, index) => {
|
|
||||||
if ('rules' in rule) {
|
|
||||||
processRuleGroup(rule, group.combinator === 'and' ? 'AND' : 'OR')
|
|
||||||
} else {
|
|
||||||
const trigger: AutoScoreTrigger = {
|
|
||||||
event: rule.field,
|
|
||||||
value: rule.value as string
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index > 0) {
|
|
||||||
trigger.relation = relation
|
|
||||||
}
|
|
||||||
|
|
||||||
triggers.push(trigger)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} */
|
|
||||||
|
|
||||||
/* processRuleGroup(query) */
|
|
||||||
|
|
||||||
return {
|
|
||||||
triggers,
|
|
||||||
actions: [],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function autoScoreRuleToQuery(ruleData: AutoScoreRuleData): RuleGroupType {
|
|
||||||
const rules: any[] = []
|
|
||||||
let currentCombinator: "and" | "or" = "and"
|
|
||||||
|
|
||||||
ruleData.triggers.forEach((trigger, index) => {
|
|
||||||
if (index === 0) {
|
|
||||||
currentCombinator = "and"
|
|
||||||
} else if (trigger.relation === "OR") {
|
|
||||||
currentCombinator = "or"
|
|
||||||
}
|
|
||||||
|
|
||||||
rules.push({
|
|
||||||
field: trigger.event,
|
|
||||||
operator: "=",
|
|
||||||
value: trigger.value || "",
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
combinator: currentCombinator,
|
|
||||||
rules: rules.length > 0 ? rules : defaultQuery.rules,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
import { formatQuery, QueryBuilder, RuleGroupType } from "react-querybuilder"
|
|
||||||
import { useState, useEffect } from "react"
|
|
||||||
import { useTranslation } from "react-i18next"
|
|
||||||
import { QueryBuilderDnD } from "@react-querybuilder/dnd"
|
|
||||||
import * as ReactDnD from "react-dnd"
|
|
||||||
import { QueryBuilderAntD } from "@react-querybuilder/antd"
|
|
||||||
import * as ReactDndHtml5Backend from "react-dnd-html5-backend"
|
|
||||||
import * as ReactDndTouchBackend from "react-dnd-touch-backend"
|
|
||||||
import {
|
|
||||||
getFields,
|
|
||||||
defaultQuery,
|
|
||||||
autoScoreRuleToQuery,
|
|
||||||
queryToAutoScoreRule,
|
|
||||||
type AutoScoreRuleData,
|
|
||||||
} from "./ruleBuilderUtils"
|
|
||||||
import { Card } from "antd"
|
|
||||||
|
|
||||||
import "./ruleBuilderOverride.css"
|
|
||||||
|
|
||||||
interface RuleComponentProps {
|
|
||||||
initialData?: AutoScoreRuleData
|
|
||||||
onChange?: (data: AutoScoreRuleData) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export const RuleComponent: React.FC<RuleComponentProps> = ({ initialData, onChange }) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const [query, setQuery] = useState<RuleGroupType>(
|
|
||||||
initialData ? autoScoreRuleToQuery(initialData) : defaultQuery
|
|
||||||
)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (initialData) {
|
|
||||||
setQuery(autoScoreRuleToQuery(initialData))
|
|
||||||
}
|
|
||||||
}, [initialData])
|
|
||||||
|
|
||||||
const handleQueryChange = (newQuery: RuleGroupType) => {
|
|
||||||
setQuery(newQuery)
|
|
||||||
if (onChange) {
|
|
||||||
const ruleData = queryToAutoScoreRule()
|
|
||||||
onChange(ruleData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Card
|
|
||||||
title={t("autoScore.triggerCondition")}
|
|
||||||
style={{ marginBottom: "24px", backgroundColor: "var(--ss-card-bg)" }}
|
|
||||||
>
|
|
||||||
<QueryBuilderDnD dnd={{ ...ReactDnD, ...ReactDndHtml5Backend, ...ReactDndTouchBackend }}>
|
|
||||||
<QueryBuilderAntD>
|
|
||||||
<QueryBuilder fields={getFields(t)} query={query} onQueryChange={handleQueryChange} />
|
|
||||||
</QueryBuilderAntD>
|
|
||||||
</QueryBuilderDnD>
|
|
||||||
<div style={{ marginTop: "8px" }}>
|
|
||||||
<pre style={{ fontSize: "12px", color: "#999" }}>{formatQuery(query, "json")}</pre>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default RuleComponent
|
|
||||||
Reference in New Issue
Block a user