【新增】试卷任务训练、考试、高考模拟前端
This commit is contained in:
17479
package-lock.json
generated
17479
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -47,8 +47,10 @@
|
||||
"driver.js": "^1.3.1",
|
||||
"echarts": "^5.5.0",
|
||||
"echarts-wordcloud": "^2.1.0",
|
||||
"ele-admin-pro": "^1.11.1",
|
||||
"element-plus": "2.9.1",
|
||||
"fast-xml-parser": "^4.3.2",
|
||||
"form-create": "^1.6.6",
|
||||
"highlight.js": "^11.9.0",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"lodash-es": "^4.17.21",
|
||||
@@ -123,7 +125,7 @@
|
||||
"stylelint-order": "^6.0.4",
|
||||
"terser": "^5.28.1",
|
||||
"typescript": "5.3.3",
|
||||
"unocss": "^0.58.5",
|
||||
"unocss": "^0.58.9",
|
||||
"unplugin-auto-import": "^0.16.7",
|
||||
"unplugin-element-plus": "^0.8.0",
|
||||
"unplugin-vue-components": "^0.25.2",
|
||||
|
||||
177
pnpm-lock.yaml
generated
177
pnpm-lock.yaml
generated
@@ -74,12 +74,18 @@ importers:
|
||||
echarts-wordcloud:
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.0(echarts@5.5.1)
|
||||
ele-admin-pro:
|
||||
specifier: ^1.11.1
|
||||
version: 1.11.1(ant-design-vue@4.2.6(vue@3.5.12(typescript@5.3.3)))(vue@3.5.12(typescript@5.3.3))
|
||||
element-plus:
|
||||
specifier: 2.9.1
|
||||
version: 2.9.1(vue@3.5.12(typescript@5.3.3))
|
||||
fast-xml-parser:
|
||||
specifier: ^4.3.2
|
||||
version: 4.5.0
|
||||
form-create:
|
||||
specifier: ^1.6.6
|
||||
version: 1.6.6(vue@3.5.12(typescript@5.3.3))
|
||||
highlight.js:
|
||||
specifier: ^11.9.0
|
||||
version: 11.10.0
|
||||
@@ -298,7 +304,7 @@ importers:
|
||||
specifier: 5.3.3
|
||||
version: 5.3.3
|
||||
unocss:
|
||||
specifier: ^0.58.5
|
||||
specifier: ^0.58.9
|
||||
version: 0.58.9(postcss@8.4.49)(rollup@4.27.4)(vite@5.1.4(@types/node@20.17.9)(sass@1.81.0)(terser@5.36.0))
|
||||
unplugin-auto-import:
|
||||
specifier: ^0.16.7
|
||||
@@ -346,6 +352,17 @@ packages:
|
||||
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==, tarball: https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.3.0.tgz}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@ant-design/colors@6.0.0':
|
||||
resolution: {integrity: sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==}
|
||||
|
||||
'@ant-design/icons-svg@4.4.2':
|
||||
resolution: {integrity: sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==}
|
||||
|
||||
'@ant-design/icons-vue@7.0.1':
|
||||
resolution: {integrity: sha512-eCqY2unfZK6Fe02AwFlDHLfoyEFreP6rBwAZMIJ1LugmfMiVgwWDYlp1YsRugaPtICYOabV1iWxXdP12u9U43Q==}
|
||||
peerDependencies:
|
||||
vue: '>=3.0.3'
|
||||
|
||||
'@antfu/install-pkg@0.4.1':
|
||||
resolution: {integrity: sha512-T7yB5QNG29afhWVkVq7XeIMBa5U/vs9mX69YqayXypPRmYzUmzwnYltplHmPtZ4HPCn+sQKeXW8I47wCbuBOjw==, tarball: https://registry.npmmirror.com/@antfu/install-pkg/-/install-pkg-0.4.1.tgz}
|
||||
|
||||
@@ -1019,6 +1036,12 @@ packages:
|
||||
peerDependencies:
|
||||
vue: ^3.2.0
|
||||
|
||||
'@emotion/hash@0.9.2':
|
||||
resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==}
|
||||
|
||||
'@emotion/unitless@0.8.1':
|
||||
resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==}
|
||||
|
||||
'@esbuild/aix-ppc64@0.19.12':
|
||||
resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -1583,6 +1606,9 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@simonwep/pickr@1.8.2':
|
||||
resolution: {integrity: sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==}
|
||||
|
||||
'@sinclair/typebox@0.27.8':
|
||||
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==, tarball: https://registry.npmmirror.com/@sinclair/typebox/-/typebox-0.27.8.tgz}
|
||||
|
||||
@@ -2371,6 +2397,12 @@ packages:
|
||||
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==, tarball: https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.1.tgz}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
ant-design-vue@4.2.6:
|
||||
resolution: {integrity: sha512-t7eX13Yj3i9+i5g9lqFyYneoIb3OzTvQjq9Tts1i+eiOd3Eva/6GagxBSXM1fOCjqemIu0FYVE1ByZ/38epR3Q==}
|
||||
engines: {node: '>=12.22.0'}
|
||||
peerDependencies:
|
||||
vue: '>=3.2.0'
|
||||
|
||||
anymatch@3.1.3:
|
||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==, tarball: https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -2388,6 +2420,9 @@ packages:
|
||||
resolution: {integrity: sha512-+RY54S8OuVvg94THpneQvFRmqWdAHeqtMzgMW6JNurHxe8rsS07cHQdfGkXnTUXiBcyZ0j3SiDIxxj0RPiqCkQ==, tarball: https://registry.npmmirror.com/array-move/-/array-move-4.0.0.tgz}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
array-tree-filter@2.1.0:
|
||||
resolution: {integrity: sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==}
|
||||
|
||||
array-union@2.1.0:
|
||||
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==, tarball: https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz}
|
||||
engines: {node: '>=8'}
|
||||
@@ -2951,6 +2986,12 @@ packages:
|
||||
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==, tarball: https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
dom-align@1.12.4:
|
||||
resolution: {integrity: sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==}
|
||||
|
||||
dom-scroll-into-view@2.0.1:
|
||||
resolution: {integrity: sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==}
|
||||
|
||||
dom-serializer@2.0.0:
|
||||
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==, tarball: https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz}
|
||||
|
||||
@@ -3006,6 +3047,12 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
hasBin: true
|
||||
|
||||
ele-admin-pro@1.11.1:
|
||||
resolution: {integrity: sha512-3gTCTrQ6XCnfMP8nNZZFXaoRcNpOMLpK2mEsu+21f3w5ID/BFhyOxWSngJL/LMPelwSpK+PwRclp0r3Wu16J7Q==}
|
||||
peerDependencies:
|
||||
ant-design-vue: '>=3.1.0'
|
||||
vue: '>=3.1.0'
|
||||
|
||||
electron-to-chromium@1.5.67:
|
||||
resolution: {integrity: sha512-nz88NNBsD7kQSAGGJyp8hS6xSPtWwqNogA0mjtc2nUYeEf3nURK9qpV18TuBdDmEDgVWotS8Wkzf+V52dSQ/LQ==, tarball: https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.67.tgz}
|
||||
|
||||
@@ -3287,6 +3334,12 @@ packages:
|
||||
resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==, tarball: https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.0.tgz}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
form-create@1.6.6:
|
||||
resolution: {integrity: sha512-p20W5EsVtXz7B21Ax7pPyoGml4IR/EbBNrgqzo/fVIxKlUOqfvJDLhK0vVojU7WkzfCF9eQPvX/6qVNkKFC20A==}
|
||||
engines: {node: '>=8.9.1', npm: '>=5.5.1', yarn: '>=1.3.2'}
|
||||
peerDependencies:
|
||||
vue: ^2.5.2
|
||||
|
||||
form-data@4.0.1:
|
||||
resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==, tarball: https://registry.npmmirror.com/form-data/-/form-data-4.0.1.tgz}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -3561,6 +3614,10 @@ packages:
|
||||
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==, tarball: https://registry.npmmirror.com/is-path-inside/-/is-path-inside-3.0.3.tgz}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
is-plain-object@3.0.1:
|
||||
resolution: {integrity: sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-plain-object@5.0.0:
|
||||
resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==, tarball: https://registry.npmmirror.com/is-plain-object/-/is-plain-object-5.0.0.tgz}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -3802,6 +3859,10 @@ packages:
|
||||
resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==, tarball: https://registry.npmmirror.com/loglevel/-/loglevel-1.9.2.tgz}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
|
||||
loose-envify@1.4.0:
|
||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||
hasBin: true
|
||||
|
||||
lru-cache@10.4.3:
|
||||
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==, tarball: https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz}
|
||||
|
||||
@@ -3980,6 +4041,9 @@ packages:
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
nanopop@2.4.2:
|
||||
resolution: {integrity: sha512-NzOgmMQ+elxxHeIha+OG/Pv3Oc3p4RU2aBhwWwAqDpXrdTbtRylbRLQztLy8dMMwfl6pclznBdfUhccEn9ZIzw==}
|
||||
|
||||
natural-compare@1.4.0:
|
||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, tarball: https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz}
|
||||
|
||||
@@ -4385,6 +4449,9 @@ packages:
|
||||
require-relative@0.8.7:
|
||||
resolution: {integrity: sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==, tarball: https://registry.npmmirror.com/require-relative/-/require-relative-0.8.7.tgz}
|
||||
|
||||
resize-observer-polyfill@1.5.1:
|
||||
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
|
||||
|
||||
resolve-from@4.0.0:
|
||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, tarball: https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz}
|
||||
engines: {node: '>=4'}
|
||||
@@ -4482,6 +4549,9 @@ packages:
|
||||
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==, tarball: https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
shallow-equal@1.2.1:
|
||||
resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==}
|
||||
|
||||
shebang-command@2.0.0:
|
||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, tarball: https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz}
|
||||
engines: {node: '>=8'}
|
||||
@@ -4547,7 +4617,7 @@ packages:
|
||||
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==, tarball: https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz}
|
||||
|
||||
source-map@0.6.1:
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, tarball: https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz}
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
split2@4.2.0:
|
||||
@@ -4638,6 +4708,9 @@ packages:
|
||||
engines: {node: '>=18.12.0'}
|
||||
hasBin: true
|
||||
|
||||
stylis@4.3.6:
|
||||
resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==}
|
||||
|
||||
supports-color@2.0.0:
|
||||
resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==, tarball: https://registry.npmmirror.com/supports-color/-/supports-color-2.0.0.tgz}
|
||||
engines: {node: '>=0.8.0'}
|
||||
@@ -4692,6 +4765,10 @@ packages:
|
||||
text-table@0.2.0:
|
||||
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==, tarball: https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz}
|
||||
|
||||
throttle-debounce@5.0.2:
|
||||
resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==}
|
||||
engines: {node: '>=12.22'}
|
||||
|
||||
through@2.3.8:
|
||||
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==, tarball: https://registry.npmmirror.com/through/-/through-2.3.8.tgz}
|
||||
|
||||
@@ -4987,6 +5064,12 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
|
||||
vue-types@3.0.2:
|
||||
resolution: {integrity: sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==}
|
||||
engines: {node: '>=10.15.0'}
|
||||
peerDependencies:
|
||||
vue: ^3.0.0
|
||||
|
||||
vue-types@5.1.3:
|
||||
resolution: {integrity: sha512-3Wy6QcZl0VusCCHX3vYrWSILFlrOB2EQDoySnuYmASM5cUp1FivJGfkS5lp1CutDgyRb41g32r/1QCmiBj5i1Q==, tarball: https://registry.npmmirror.com/vue-types/-/vue-types-5.1.3.tgz}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@@ -5020,6 +5103,9 @@ packages:
|
||||
wangeditor@4.7.15:
|
||||
resolution: {integrity: sha512-aPTdREd8BxXVyJ5MI+LU83FQ7u1EPd341iXIorRNYSOvoimNoZ4nPg+yn3FGbB93/owEa6buLw8wdhYnMCJQLg==, tarball: https://registry.npmmirror.com/wangeditor/-/wangeditor-4.7.15.tgz}
|
||||
|
||||
warning@4.0.3:
|
||||
resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==}
|
||||
|
||||
web-storage-cache@1.1.1:
|
||||
resolution: {integrity: sha512-D0MieGooOs8RpsrK+vnejXnvh4OOv/+lTFB35JRkJJQt+uOjPE08XpaE0QBLMTRu47B1KGT/Nq3Gbag3Orinzw==, tarball: https://registry.npmmirror.com/web-storage-cache/-/web-storage-cache-1.1.1.tgz}
|
||||
|
||||
@@ -5143,6 +5229,18 @@ snapshots:
|
||||
'@jridgewell/gen-mapping': 0.3.5
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
|
||||
'@ant-design/colors@6.0.0':
|
||||
dependencies:
|
||||
'@ctrl/tinycolor': 3.6.1
|
||||
|
||||
'@ant-design/icons-svg@4.4.2': {}
|
||||
|
||||
'@ant-design/icons-vue@7.0.1(vue@3.5.12(typescript@5.3.3))':
|
||||
dependencies:
|
||||
'@ant-design/colors': 6.0.0
|
||||
'@ant-design/icons-svg': 4.4.2
|
||||
vue: 3.5.12(typescript@5.3.3)
|
||||
|
||||
'@antfu/install-pkg@0.4.1':
|
||||
dependencies:
|
||||
package-manager-detector: 0.2.5
|
||||
@@ -6061,6 +6159,10 @@ snapshots:
|
||||
dependencies:
|
||||
vue: 3.5.12(typescript@5.3.3)
|
||||
|
||||
'@emotion/hash@0.9.2': {}
|
||||
|
||||
'@emotion/unitless@0.8.1': {}
|
||||
|
||||
'@esbuild/aix-ppc64@0.19.12':
|
||||
optional: true
|
||||
|
||||
@@ -6556,6 +6658,11 @@ snapshots:
|
||||
'@rollup/rollup-win32-x64-msvc@4.27.4':
|
||||
optional: true
|
||||
|
||||
'@simonwep/pickr@1.8.2':
|
||||
dependencies:
|
||||
core-js: 3.39.0
|
||||
nanopop: 2.4.2
|
||||
|
||||
'@sinclair/typebox@0.27.8': {}
|
||||
|
||||
'@sphinxxxx/color-conversion@2.2.2': {}
|
||||
@@ -7603,6 +7710,32 @@ snapshots:
|
||||
|
||||
ansi-styles@6.2.1: {}
|
||||
|
||||
ant-design-vue@4.2.6(vue@3.5.12(typescript@5.3.3)):
|
||||
dependencies:
|
||||
'@ant-design/colors': 6.0.0
|
||||
'@ant-design/icons-vue': 7.0.1(vue@3.5.12(typescript@5.3.3))
|
||||
'@babel/runtime': 7.26.0
|
||||
'@ctrl/tinycolor': 3.6.1
|
||||
'@emotion/hash': 0.9.2
|
||||
'@emotion/unitless': 0.8.1
|
||||
'@simonwep/pickr': 1.8.2
|
||||
array-tree-filter: 2.1.0
|
||||
async-validator: 4.2.5
|
||||
csstype: 3.1.3
|
||||
dayjs: 1.11.13
|
||||
dom-align: 1.12.4
|
||||
dom-scroll-into-view: 2.0.1
|
||||
lodash: 4.17.21
|
||||
lodash-es: 4.17.21
|
||||
resize-observer-polyfill: 1.5.1
|
||||
scroll-into-view-if-needed: 2.2.31
|
||||
shallow-equal: 1.2.1
|
||||
stylis: 4.3.6
|
||||
throttle-debounce: 5.0.2
|
||||
vue: 3.5.12(typescript@5.3.3)
|
||||
vue-types: 3.0.2(vue@3.5.12(typescript@5.3.3))
|
||||
warning: 4.0.3
|
||||
|
||||
anymatch@3.1.3:
|
||||
dependencies:
|
||||
normalize-path: 3.0.0
|
||||
@@ -7618,6 +7751,8 @@ snapshots:
|
||||
|
||||
array-move@4.0.0: {}
|
||||
|
||||
array-tree-filter@2.1.0: {}
|
||||
|
||||
array-union@2.1.0: {}
|
||||
|
||||
astral-regex@2.0.0: {}
|
||||
@@ -8240,6 +8375,10 @@ snapshots:
|
||||
dependencies:
|
||||
esutils: 2.0.3
|
||||
|
||||
dom-align@1.12.4: {}
|
||||
|
||||
dom-scroll-into-view@2.0.1: {}
|
||||
|
||||
dom-serializer@2.0.0:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
@@ -8295,6 +8434,11 @@ snapshots:
|
||||
dependencies:
|
||||
jake: 10.9.2
|
||||
|
||||
ele-admin-pro@1.11.1(ant-design-vue@4.2.6(vue@3.5.12(typescript@5.3.3)))(vue@3.5.12(typescript@5.3.3)):
|
||||
dependencies:
|
||||
ant-design-vue: 4.2.6(vue@3.5.12(typescript@5.3.3))
|
||||
vue: 3.5.12(typescript@5.3.3)
|
||||
|
||||
electron-to-chromium@1.5.67: {}
|
||||
|
||||
element-plus@2.9.1(vue@3.5.12(typescript@5.3.3)):
|
||||
@@ -8664,6 +8808,10 @@ snapshots:
|
||||
cross-spawn: 7.0.6
|
||||
signal-exit: 4.1.0
|
||||
|
||||
form-create@1.6.6(vue@3.5.12(typescript@5.3.3)):
|
||||
dependencies:
|
||||
vue: 3.5.12(typescript@5.3.3)
|
||||
|
||||
form-data@4.0.1:
|
||||
dependencies:
|
||||
asynckit: 0.4.0
|
||||
@@ -8906,6 +9054,8 @@ snapshots:
|
||||
|
||||
is-path-inside@3.0.3: {}
|
||||
|
||||
is-plain-object@3.0.1: {}
|
||||
|
||||
is-plain-object@5.0.0: {}
|
||||
|
||||
is-stream@3.0.0: {}
|
||||
@@ -9138,6 +9288,10 @@ snapshots:
|
||||
|
||||
loglevel@1.9.2: {}
|
||||
|
||||
loose-envify@1.4.0:
|
||||
dependencies:
|
||||
js-tokens: 4.0.0
|
||||
|
||||
lru-cache@10.4.3: {}
|
||||
|
||||
lru-cache@5.1.1:
|
||||
@@ -9328,6 +9482,8 @@ snapshots:
|
||||
|
||||
nanoid@3.3.8: {}
|
||||
|
||||
nanopop@2.4.2: {}
|
||||
|
||||
natural-compare@1.4.0: {}
|
||||
|
||||
next-tick@1.1.0: {}
|
||||
@@ -9674,6 +9830,8 @@ snapshots:
|
||||
|
||||
require-relative@0.8.7: {}
|
||||
|
||||
resize-observer-polyfill@1.5.1: {}
|
||||
|
||||
resolve-from@4.0.0: {}
|
||||
|
||||
resolve-from@5.0.0: {}
|
||||
@@ -9788,6 +9946,8 @@ snapshots:
|
||||
gopd: 1.0.1
|
||||
has-property-descriptors: 1.0.2
|
||||
|
||||
shallow-equal@1.2.1: {}
|
||||
|
||||
shebang-command@2.0.0:
|
||||
dependencies:
|
||||
shebang-regex: 3.0.0
|
||||
@@ -9971,6 +10131,8 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
stylis@4.3.6: {}
|
||||
|
||||
supports-color@2.0.0: {}
|
||||
|
||||
supports-color@7.2.0:
|
||||
@@ -10029,6 +10191,8 @@ snapshots:
|
||||
|
||||
text-table@0.2.0: {}
|
||||
|
||||
throttle-debounce@5.0.2: {}
|
||||
|
||||
through@2.3.8: {}
|
||||
|
||||
tiny-svg@3.1.3: {}
|
||||
@@ -10371,6 +10535,11 @@ snapshots:
|
||||
semver: 7.6.3
|
||||
typescript: 5.3.3
|
||||
|
||||
vue-types@3.0.2(vue@3.5.12(typescript@5.3.3)):
|
||||
dependencies:
|
||||
is-plain-object: 3.0.1
|
||||
vue: 3.5.12(typescript@5.3.3)
|
||||
|
||||
vue-types@5.1.3(vue@3.5.12(typescript@5.3.3)):
|
||||
dependencies:
|
||||
is-plain-object: 5.0.0
|
||||
@@ -10406,6 +10575,10 @@ snapshots:
|
||||
'@babel/runtime-corejs3': 7.26.0
|
||||
tslib: 2.8.1
|
||||
|
||||
warning@4.0.3:
|
||||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
|
||||
web-storage-cache@1.1.1: {}
|
||||
|
||||
webidl-conversions@3.0.1: {}
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
/**
|
||||
* 分页查询试卷人员分配
|
||||
*/
|
||||
export async function pagePersons(params) {
|
||||
const res = await request.get('/exam/person/list', { params });
|
||||
if (res.data.code === 200) {
|
||||
return res.data;
|
||||
}
|
||||
return Promise.reject(new Error(res.data.msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询全部试卷人员分配
|
||||
*/
|
||||
export async function listPersons(params) {
|
||||
const res = await request.get('/exam/person/list', { params });
|
||||
if (res.data.code === 200) {
|
||||
return res.data.data;
|
||||
}
|
||||
return Promise.reject(new Error(res.data.msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据id查询试卷人员分配
|
||||
*/
|
||||
export async function getPerson(id) {
|
||||
const res = await request.get('/exam/person/' + id);
|
||||
if (res.data.code === 200) {
|
||||
return res.data;
|
||||
}
|
||||
return Promise.reject(new Error(res.data.msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加试卷人员分配
|
||||
*/
|
||||
export async function addPerson(data) {
|
||||
const res = await request.post('/exam/person', data);
|
||||
if (res.data.code === 200) {
|
||||
return res.data.msg;
|
||||
}
|
||||
return Promise.reject(new Error(res.data.msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改试卷人员分配
|
||||
*/
|
||||
export async function updatePerson(data) {
|
||||
const res = await request.put('/exam/person', data);
|
||||
if (res.data.code === 200) {
|
||||
return res.data.msg;
|
||||
}
|
||||
return Promise.reject(new Error(res.data.msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除试卷人员分配
|
||||
*/
|
||||
export async function removePerson(id) {
|
||||
const res = await request.delete('/exam/person/' + id);
|
||||
if (res.data.code === 200) {
|
||||
return res.data.msg;
|
||||
}
|
||||
return Promise.reject(new Error(res.data.msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除试卷人员分配
|
||||
*/
|
||||
export async function removePersons(ids) {
|
||||
const res = await request.delete('/exam/person/' + ids.join());
|
||||
if (res.data.code === 200) {
|
||||
return res.data.msg;
|
||||
}
|
||||
return Promise.reject(new Error(res.data.msg));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出试卷人员分配
|
||||
*/
|
||||
export async function exportPersons(params) {
|
||||
// const res = await request({
|
||||
// url: '/exam/person/export',
|
||||
// method: 'POST',
|
||||
// data: toFormData(params),
|
||||
// responseType: 'blob'
|
||||
// });
|
||||
// await checkDownloadRes(res);
|
||||
// download(res.data, `person_${Date.now()}.xlsx`);
|
||||
}
|
||||
70
src/api/system/person/index.ts
Normal file
70
src/api/system/person/index.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
/**
|
||||
* 分页查询试卷人员分配
|
||||
*/
|
||||
|
||||
export const pagePersons = (params) => {
|
||||
return request.get({ url: '/exam/person/getList', params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据id查询试卷人员分配
|
||||
*/
|
||||
export async function getPerson(id) {
|
||||
return await request.get({ url: '/exam/person' + id })
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加试卷人员分配
|
||||
*/
|
||||
export async function addPerson(data) {
|
||||
return await request.post({ url: '/exam/person' , data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改试卷人员分配
|
||||
*/
|
||||
export async function updatePerson(data) {
|
||||
return await request.put({ url: '/exam/person' , data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除试卷人员分配
|
||||
*/
|
||||
export async function removePerson(id) {
|
||||
return await request.delete({ url: '/exam/person/' + id })
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 导出试卷人员分配
|
||||
*/
|
||||
export async function exportPersons(params) {
|
||||
// const res = await request({
|
||||
// url: '/exam/person/export',
|
||||
// method: 'POST',
|
||||
// data: toFormData(params),
|
||||
// responseType: 'blob'
|
||||
// });
|
||||
// await checkDownloadRes(res);
|
||||
// download(res.data, `person_${Date.now()}.xlsx`);
|
||||
}
|
||||
export const getSessionStuBySearch = (params) => {
|
||||
return request.get({ url: '/exam/person/getSessionStuBySearch', params })
|
||||
}
|
||||
|
||||
export async function setSessionStu(data) {
|
||||
return await request.post({ url: '/exam/person/setSessionStu' , data })
|
||||
}
|
||||
export async function removeSessionStu(data) {
|
||||
return await request.delete({ url: '/exam/person/removeSessionStu' ,data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除试卷人员分配
|
||||
*/
|
||||
export async function removePersons(data) {
|
||||
return await request.delete({ url: '/exam/person/removeTaskStu' ,data })
|
||||
}
|
||||
@@ -85,5 +85,16 @@ export async function updateSessionStatus(sessionId: string, status: number) {
|
||||
})
|
||||
}
|
||||
|
||||
export async function getSessionStu(params) {
|
||||
|
||||
return await request.get({ url: '/exam/person/getSessionStu' , params })
|
||||
|
||||
}
|
||||
|
||||
export async function fetch(taskId) {
|
||||
return await request.get({ url: '/exam/param/check-can-enter-step4/' + taskId })
|
||||
}
|
||||
|
||||
export async function fetchNoMes(taskId) {
|
||||
return await request.get({ url: '/exam/param/check-can-enter-step4NoMsg/' + taskId })
|
||||
}
|
||||
@@ -85,19 +85,12 @@ export async function updateTaskStatus(taskId: string, status: number) {
|
||||
// download(res.data, `task_${Date.now()}.xlsx`);
|
||||
}
|
||||
export async function getSpecialtyList() {
|
||||
|
||||
|
||||
return await request.get({ url: '/exam/task/getSpeciality' })
|
||||
|
||||
}
|
||||
|
||||
|
||||
export async function submitSelection(data) {
|
||||
|
||||
|
||||
|
||||
return await request.post({ url: '/exam/task/submitSelection' , data })
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,260 +0,0 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
title="人员分配"
|
||||
v-model:visible="visible"
|
||||
width="80%"
|
||||
@close="handleDialogClose"
|
||||
>
|
||||
<ele-page>
|
||||
<el-row :gutter="16">
|
||||
<!-- 左侧任务列表 -->
|
||||
<el-col :span="10">
|
||||
<ele-card title="试卷任务列表" :body-style="{ paddingTop: '8px' }">
|
||||
<ele-pro-table
|
||||
ref="taskTableRef"
|
||||
row-key="id"
|
||||
:columns="taskColumns"
|
||||
:datasource="loadTaskList"
|
||||
:highlight-current-row="true"
|
||||
@row-click="handleTaskRowClick"
|
||||
/>
|
||||
</ele-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧人员分配列表 -->
|
||||
<el-col :span="14">
|
||||
<ele-card :body-style="{ paddingTop: '8px' }">
|
||||
<ele-pro-table
|
||||
ref="personTableRef"
|
||||
row-key="key"
|
||||
:columns="personColumns"
|
||||
:datasource="loadPersonList"
|
||||
:show-overflow-tooltip="true"
|
||||
v-model:selections="selections"
|
||||
:highlight-current-row="true"
|
||||
:export-config="{ fileName: '试卷人员分配' }"
|
||||
cache-key="systemPersonTable"
|
||||
>
|
||||
<!-- 工具栏模板 -->
|
||||
<template #toolbar>
|
||||
<el-button
|
||||
type="primary"
|
||||
class="ele-btn-icon"
|
||||
@click="openEdit()"
|
||||
:disabled="!selectedTaskId"
|
||||
>
|
||||
分配场次
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
class="ele-btn-icon"
|
||||
@click="removeBatch()"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<el-button
|
||||
class="ele-btn-icon"
|
||||
@click="exportData"
|
||||
>
|
||||
导出
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<!-- 操作列模板 -->
|
||||
<template #action="{ row }">
|
||||
<el-link type="primary" :underline="false" @click="openEdit(row)">修改</el-link>
|
||||
<el-divider direction="vertical" />
|
||||
<el-link type="danger" :underline="false" @click="removeBatch(row)">删除</el-link>
|
||||
</template>
|
||||
|
||||
<!-- 空状态模板 -->
|
||||
<template #empty>
|
||||
<el-empty description="暂无分配人员" />
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</ele-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 编辑组件 -->
|
||||
<person-edit
|
||||
v-model="showEdit"
|
||||
:task-id="selectedTaskId"
|
||||
:data="current"
|
||||
@done="reloadPerson"
|
||||
/>
|
||||
</ele-page>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, nextTick, onMounted } from 'vue';
|
||||
const message = useMessage() // 消息弹窗
|
||||
// import {
|
||||
// PlusOutlined,
|
||||
// DeleteOutlined,
|
||||
// DownloadOutlined
|
||||
// } from '@/components/icons';
|
||||
|
||||
// 组件状态
|
||||
const visible = ref(true); // 对话框可见状态
|
||||
const selectedTaskId = ref(null); // 当前选中的任务ID
|
||||
const selections = ref([]); // 右侧表格选中项
|
||||
const showEdit = ref(false); // 编辑对话框状态
|
||||
const current = ref(null); // 当前操作的行数据
|
||||
|
||||
// 表格引用
|
||||
const taskTableRef = ref(null); // 左侧任务表格实例
|
||||
const personTableRef = ref(null); // 右侧人员表格实例
|
||||
|
||||
// 模拟数据
|
||||
const mockTaskList = [
|
||||
{ id: 1, title: '数学试卷任务' },
|
||||
{ id: 2, title: '英语试卷任务' },
|
||||
{ id: 3, title: '物理试卷任务' }
|
||||
];
|
||||
|
||||
const mockPersonList = [
|
||||
{ key: 101, taskId: 1, personId: 'S001', sessionId: 'A' },
|
||||
{ key: 102, taskId: 1, personId: 'S002', sessionId: 'B' },
|
||||
{ key: 103, taskId: 2, personId: 'S003', sessionId: 'A' },
|
||||
{ key: 104, taskId: 3, personId: 'S004', sessionId: 'C' }
|
||||
];
|
||||
|
||||
// 表格列配置
|
||||
const taskColumns = [
|
||||
{ type: 'index', label: '#', width: 50, align: 'center' },
|
||||
{ prop: 'id', label: '任务ID', align: 'center' },
|
||||
{ prop: 'title', label: '任务名称', align: 'center' }
|
||||
];
|
||||
|
||||
// 人员列
|
||||
const personColumns = [
|
||||
{ type: 'selection', width: 50, align: 'center' },
|
||||
{ type: 'index', width: 50, align: 'center' },
|
||||
{ prop: 'taskId', label: '试卷任务ID', align: 'center' },
|
||||
{ prop: 'personId', label: '学生ID', align: 'center' },
|
||||
{ prop: 'sessionId', label: '场次', align: 'center' },
|
||||
{
|
||||
columnKey: 'action',
|
||||
label: '操作',
|
||||
width: 180,
|
||||
align: 'center',
|
||||
slot: 'action',
|
||||
hideInPrint: true,
|
||||
hideInExport: true
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
// 设置默认选中第一个任务
|
||||
if (mockTaskList.length > 0) {
|
||||
selectedTaskId.value = mockTaskList[0].id;
|
||||
// 等待表格渲染完成后设置选中样式
|
||||
nextTick(() => {
|
||||
// 确保正确引用表格实例,确保调用 setCurrentRow 或类似方法
|
||||
if (taskTableRef.value) {
|
||||
// 替换为实际可用的方法来选中行
|
||||
taskTableRef.value.select([mockTaskList[0].id]); // 如果有 select 方法
|
||||
reloadPerson(); // 加载人员列表
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 加载任务列表
|
||||
const loadTaskList = async () => {
|
||||
try {
|
||||
// 模拟接口请求延迟
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
return mockTaskList; // 直接返回数组
|
||||
} catch (error) {
|
||||
message.error('任务加载失败');
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
// 加载人员列表(带分页)
|
||||
const loadPersonList = async ({ page = 1, limit = 10, taskId }) => {
|
||||
if (!taskId) return { data: [], total: 0 }; // 如果没有任务ID,返回空数据
|
||||
try {
|
||||
// 过滤当前任务的人员
|
||||
const filtered = mockPersonList.filter(
|
||||
item => item.taskId === taskId
|
||||
);
|
||||
|
||||
// 模拟分页
|
||||
const start = (page - 1) * limit;
|
||||
const end = start + limit;
|
||||
|
||||
return {
|
||||
data: filtered.slice(start, end), // 直接返回分页数据
|
||||
total: filtered.length // 返回过滤后的总数
|
||||
};
|
||||
} catch (error) {
|
||||
message.error('人员数据加载失败');
|
||||
return { data: [], total: 0 };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 处理任务行点击
|
||||
function handleTaskRowClick(row) {
|
||||
selectedTaskId.value = row.id;
|
||||
nextTick(() => {
|
||||
reloadPerson(); // 在数据更新后确保重新加载人员列表
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 刷新人员表格
|
||||
function reloadPerson() {
|
||||
// 调用 loadPersonList 来加载人员数据
|
||||
console.log(selectedTaskId.value+"selectedTaskId.valueselectedTaskId.value")
|
||||
personTableRef.value?.reload?.({ page: 1, limit: 10, taskId: selectedTaskId.value });
|
||||
}
|
||||
|
||||
// 打开编辑弹窗
|
||||
function openEdit(row) {
|
||||
current.value = row ? { ...row } : null;
|
||||
showEdit.value = true;
|
||||
}
|
||||
|
||||
// 批量删除
|
||||
function removeBatch(row) {
|
||||
const rows = row ? [row] : selections.value;
|
||||
if (!rows.length) return message.error('请至少选择一条数据');
|
||||
|
||||
ElMessageBox.confirm(
|
||||
`确认删除选中的${rows.length}条记录?`,
|
||||
'删除确认',
|
||||
{ type: 'warning' }
|
||||
).then(() => {
|
||||
// 执行删除操作
|
||||
rows.forEach(target => {
|
||||
const index = mockPersonList.findIndex(
|
||||
item => item.key === target.key
|
||||
);
|
||||
if (index !== -1) mockPersonList.splice(index, 1);
|
||||
});
|
||||
|
||||
message.success('删除成功');
|
||||
reloadPerson();
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
// 导出数据
|
||||
function exportData() {
|
||||
message.success('导出功能需对接后端实现');
|
||||
}
|
||||
|
||||
// 处理对话框关闭
|
||||
function handleDialogClose() {
|
||||
showEdit.value = false;
|
||||
current.value = null;
|
||||
selectedTaskId.value = null;
|
||||
selections.value = [];
|
||||
}
|
||||
</script>
|
||||
@@ -1,224 +0,0 @@
|
||||
<template>
|
||||
<ele-page>
|
||||
<!-- 搜索表单 -->
|
||||
<ele-card :body-style="{ paddingTop: '8px' }">
|
||||
<!-- 表格 -->
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
row-key="key"
|
||||
:columns="columns"
|
||||
:datasource="datasource"
|
||||
:show-overflow-tooltip="true"
|
||||
v-model:selections="selections"
|
||||
:highlight-current-row="true"
|
||||
:export-config="{ fileName: '试卷人员分配' }"
|
||||
cache-key="systemPersonTable"
|
||||
>
|
||||
<template #toolbar>
|
||||
|
||||
<el-button
|
||||
v-permission="'system:person:add'"
|
||||
type="primary"
|
||||
class="ele-btn-icon"
|
||||
|
||||
@click="openEdit()"
|
||||
>
|
||||
分配场次
|
||||
</el-button>
|
||||
<el-button
|
||||
v-permission="'system:person:remove'"
|
||||
type="danger"
|
||||
class="ele-btn-icon"
|
||||
|
||||
@click="removeBatch()"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<el-button
|
||||
v-permission="'system:person:export'"
|
||||
class="ele-btn-icon"
|
||||
|
||||
@click="exportData"
|
||||
>
|
||||
导出
|
||||
</el-button>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-link
|
||||
v-permission="'system:person:edit'"
|
||||
type="primary"
|
||||
:underline="false"
|
||||
@click="openEdit(row)"
|
||||
>
|
||||
修改
|
||||
</el-link>
|
||||
<el-divider
|
||||
v-permission="['system:person:edit', 'system:person:remove']"
|
||||
direction="vertical"
|
||||
/>
|
||||
<el-link
|
||||
v-permission="'system:person:remove'"
|
||||
type="danger"
|
||||
:underline="false"
|
||||
@click="removeBatch(row)"
|
||||
>
|
||||
删除
|
||||
</el-link>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</ele-card>
|
||||
<!-- 编辑弹窗 -->
|
||||
<person-edit v-model="showEdit" :task-Id="taskId" @done="reload" />
|
||||
</ele-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
// import {
|
||||
// PlusOutlined,
|
||||
// DeleteOutlined,
|
||||
// DownloadOutlined
|
||||
// } from '@/components/icons';
|
||||
import PersonEdit from './components/person-edit.vue';
|
||||
import { pagePersons, removePersons, exportPersons } from '@/api/system/person';
|
||||
import { pageStus, removeStus, exportStus } from '@/api/system/stu';
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
defineOptions({ name: 'SystemPerson' });
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
type: String,
|
||||
default: '',
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const tableRef = ref(null);
|
||||
const selections = ref([]);
|
||||
const current = ref(null);
|
||||
const showEdit = ref(false);
|
||||
const showSet = ref(false);
|
||||
|
||||
/** 班级映射表:deptId => { deptName, teacherName } */
|
||||
const classMap = ref({});
|
||||
|
||||
/** 表格列配置 */
|
||||
const columns = ref([
|
||||
{ type: 'selection', columnKey: 'selection', width: 50, align: 'center', fixed: 'left' },
|
||||
{ type: 'index', columnKey: 'index', width: 50, align: 'center', fixed: 'left' },
|
||||
{ prop: 'stuCode', label: '学生编码', align: 'center', minWidth: 110 },
|
||||
{ prop: 'stuUsername', label: '学生账号', align: 'center', minWidth: 110 },
|
||||
|
||||
|
||||
{
|
||||
prop: 'stuClass',
|
||||
label: '学生班级',
|
||||
align: 'center',
|
||||
minWidth: 110,
|
||||
slot: 'stuClass' // 👈 使用自定义 slot 显示班级 + 悬浮教师名
|
||||
},
|
||||
{ prop: 'status', label: '帐号状态(0正常 1停用)', align: 'center', minWidth: 110 },
|
||||
{ prop: 'batch', label: '分配场次', align: 'center', minWidth: 110 }
|
||||
]);
|
||||
|
||||
/** 表格数据源:处理学生 + 班级 */
|
||||
const datasource = async ({ pages, where }) => {
|
||||
// 获取学生和班级信息
|
||||
const { examStuList, teacherClassDtos } = await pageStus({ ...where, ...pages });
|
||||
|
||||
// 如果没有数据,直接返回空列表
|
||||
if (!examStuList || !teacherClassDtos) {
|
||||
message.error('数据加载失败');
|
||||
return [];
|
||||
}
|
||||
|
||||
console.log("examStuList:", examStuList);
|
||||
console.log("teacherClassDtos:", teacherClassDtos);
|
||||
|
||||
// 构建班级映射表,使用 dept_id 作为 key
|
||||
const classMap = {};
|
||||
teacherClassDtos.forEach(item => {
|
||||
classMap[item.dept_id] = {
|
||||
deptName: item.dept_name,
|
||||
teacherName: item.nick_name || item.user_name || '未知教师'
|
||||
};
|
||||
});
|
||||
|
||||
// 将映射表存储到 `classMap`,供后续使用
|
||||
classMap.value = classMap;
|
||||
|
||||
// 合并学生信息和班级信息
|
||||
const processedList = examStuList.map(stu => {
|
||||
// 使用 stuClass 匹配 teacherClassDtos 的 dept_id
|
||||
const classInfo = classMap[stu.stuClass] || {}; // 如果找不到匹配,返回默认值
|
||||
return {
|
||||
...stu,
|
||||
deptName: classInfo.deptName || '未知班级', // 如果没有班级信息,显示默认值
|
||||
teacherName: classInfo.teacherName || '未知教师' // 如果没有教师信息,显示默认值
|
||||
};
|
||||
});
|
||||
|
||||
console.log("processedList:", processedList);
|
||||
return processedList; // 返回合并后的学生信息
|
||||
};
|
||||
|
||||
|
||||
/** 搜索 */
|
||||
const reload = (where) => {
|
||||
tableRef.value?.reload?.({ page: 1, where });
|
||||
};
|
||||
|
||||
/** 打开编辑弹窗 */
|
||||
const openEdit = (row) => {
|
||||
current.value = row ?? null;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
/** 打开班级设置弹窗 */
|
||||
const openSet = (row) => {
|
||||
current.value = row ?? null;
|
||||
showSet.value = true;
|
||||
};
|
||||
|
||||
/** 删除学生(单条或多条) */
|
||||
const removeBatch = (row) => {
|
||||
const rows = row == null ? selections.value : [row];
|
||||
if (!rows.length) {
|
||||
message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
ElMessageBox.confirm(
|
||||
`是否确认删除学生编码为"${rows.map((d) => d.stuCode).join()}"的数据项?`,
|
||||
'系统提示',
|
||||
{ type: 'warning', draggable: true }
|
||||
)
|
||||
.then(() => {
|
||||
const loading = message.loading({ message: '请求中..', plain: true });
|
||||
removeStus(rows.map((d) => d.stuId))
|
||||
.then(() => {
|
||||
loading.close();
|
||||
message.success('删除成功');
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.close();
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
/** 导出数据 */
|
||||
const exportData = () => {
|
||||
const loading = message.loading({ message: '请求中..', plain: true });
|
||||
tableRef.value?.fetch?.(({ where }) => {
|
||||
exportStus(where)
|
||||
.then(() => loading.close())
|
||||
.catch((e) => {
|
||||
loading.close();
|
||||
message.error(e.message);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
</script>
|
||||
@@ -1,260 +0,0 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
title="人员分配"
|
||||
v-model:visible="visible"
|
||||
width="80%"
|
||||
@close="handleDialogClose"
|
||||
>
|
||||
<ele-page>
|
||||
<el-row :gutter="16">
|
||||
<!-- 左侧任务列表 -->
|
||||
<el-col :span="10">
|
||||
<ele-card title="试卷任务列表" :body-style="{ paddingTop: '8px' }">
|
||||
<ele-pro-table
|
||||
ref="taskTableRef"
|
||||
row-key="id"
|
||||
:columns="taskColumns"
|
||||
:datasource="loadTaskList"
|
||||
:highlight-current-row="true"
|
||||
@row-click="handleTaskRowClick"
|
||||
/>
|
||||
</ele-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧人员分配列表 -->
|
||||
<el-col :span="14">
|
||||
<ele-card :body-style="{ paddingTop: '8px' }">
|
||||
<ele-pro-table
|
||||
ref="personTableRef"
|
||||
row-key="key"
|
||||
:columns="personColumns"
|
||||
:datasource="loadPersonList"
|
||||
:show-overflow-tooltip="true"
|
||||
v-model:selections="selections"
|
||||
:highlight-current-row="true"
|
||||
:export-config="{ fileName: '试卷人员分配' }"
|
||||
cache-key="systemPersonTable"
|
||||
>
|
||||
<!-- 工具栏模板 -->
|
||||
<template #toolbar>
|
||||
<el-button
|
||||
type="primary"
|
||||
class="ele-btn-icon"
|
||||
@click="openEdit()"
|
||||
:disabled="!selectedTaskId"
|
||||
>
|
||||
分配场次
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
class="ele-btn-icon"
|
||||
@click="removeBatch()"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<el-button
|
||||
class="ele-btn-icon"
|
||||
@click="exportData"
|
||||
>
|
||||
导出
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<!-- 操作列模板 -->
|
||||
<template #action="{ row }">
|
||||
<el-link type="primary" :underline="false" @click="openEdit(row)">修改</el-link>
|
||||
<el-divider direction="vertical" />
|
||||
<el-link type="danger" :underline="false" @click="removeBatch(row)">删除</el-link>
|
||||
</template>
|
||||
|
||||
<!-- 空状态模板 -->
|
||||
<template #empty>
|
||||
<el-empty description="暂无分配人员" />
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</ele-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 编辑组件 -->
|
||||
<person-edit
|
||||
v-model="showEdit"
|
||||
:task-id="selectedTaskId"
|
||||
:data="current"
|
||||
@done="reloadPerson"
|
||||
/>
|
||||
</ele-page>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, nextTick, onMounted } from 'vue';
|
||||
const message = useMessage() // 消息弹窗
|
||||
// import {
|
||||
// PlusOutlined,
|
||||
// DeleteOutlined,
|
||||
// DownloadOutlined
|
||||
// } from '@/components/icons';
|
||||
|
||||
// 组件状态
|
||||
const visible = ref(true); // 对话框可见状态
|
||||
const selectedTaskId = ref(null); // 当前选中的任务ID
|
||||
const selections = ref([]); // 右侧表格选中项
|
||||
const showEdit = ref(false); // 编辑对话框状态
|
||||
const current = ref(null); // 当前操作的行数据
|
||||
|
||||
// 表格引用
|
||||
const taskTableRef = ref(null); // 左侧任务表格实例
|
||||
const personTableRef = ref(null); // 右侧人员表格实例
|
||||
|
||||
// 模拟数据
|
||||
const mockTaskList = [
|
||||
{ id: 1, title: '数学试卷任务' },
|
||||
{ id: 2, title: '英语试卷任务' },
|
||||
{ id: 3, title: '物理试卷任务' }
|
||||
];
|
||||
|
||||
const mockPersonList = [
|
||||
{ key: 101, taskId: 1, personId: 'S001', sessionId: 'A' },
|
||||
{ key: 102, taskId: 1, personId: 'S002', sessionId: 'B' },
|
||||
{ key: 103, taskId: 2, personId: 'S003', sessionId: 'A' },
|
||||
{ key: 104, taskId: 3, personId: 'S004', sessionId: 'C' }
|
||||
];
|
||||
|
||||
// 表格列配置
|
||||
const taskColumns = [
|
||||
{ type: 'index', label: '#', width: 50, align: 'center' },
|
||||
{ prop: 'id', label: '任务ID', align: 'center' },
|
||||
{ prop: 'title', label: '任务名称', align: 'center' }
|
||||
];
|
||||
|
||||
// 人员列
|
||||
const personColumns = [
|
||||
{ type: 'selection', width: 50, align: 'center' },
|
||||
{ type: 'index', width: 50, align: 'center' },
|
||||
{ prop: 'taskId', label: '试卷任务ID', align: 'center' },
|
||||
{ prop: 'personId', label: '学生ID', align: 'center' },
|
||||
{ prop: 'sessionId', label: '场次', align: 'center' },
|
||||
{
|
||||
columnKey: 'action',
|
||||
label: '操作',
|
||||
width: 180,
|
||||
align: 'center',
|
||||
slot: 'action',
|
||||
hideInPrint: true,
|
||||
hideInExport: true
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
// 设置默认选中第一个任务
|
||||
if (mockTaskList.length > 0) {
|
||||
selectedTaskId.value = mockTaskList[0].id;
|
||||
// 等待表格渲染完成后设置选中样式
|
||||
nextTick(() => {
|
||||
// 确保正确引用表格实例,确保调用 setCurrentRow 或类似方法
|
||||
if (taskTableRef.value) {
|
||||
// 替换为实际可用的方法来选中行
|
||||
taskTableRef.value.select([mockTaskList[0].id]); // 如果有 select 方法
|
||||
reloadPerson(); // 加载人员列表
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 加载任务列表
|
||||
const loadTaskList = async () => {
|
||||
try {
|
||||
// 模拟接口请求延迟
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
return mockTaskList; // 直接返回数组
|
||||
} catch (error) {
|
||||
message.error('任务加载失败');
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
// 加载人员列表(带分页)
|
||||
const loadPersonList = async ({ page = 1, limit = 10, taskId }) => {
|
||||
if (!taskId) return { data: [], total: 0 }; // 如果没有任务ID,返回空数据
|
||||
try {
|
||||
// 过滤当前任务的人员
|
||||
const filtered = mockPersonList.filter(
|
||||
item => item.taskId === taskId
|
||||
);
|
||||
|
||||
// 模拟分页
|
||||
const start = (page - 1) * limit;
|
||||
const end = start + limit;
|
||||
|
||||
return {
|
||||
data: filtered.slice(start, end), // 直接返回分页数据
|
||||
total: filtered.length // 返回过滤后的总数
|
||||
};
|
||||
} catch (error) {
|
||||
message.error('人员数据加载失败');
|
||||
return { data: [], total: 0 };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 处理任务行点击
|
||||
function handleTaskRowClick(row) {
|
||||
selectedTaskId.value = row.id;
|
||||
nextTick(() => {
|
||||
reloadPerson(); // 在数据更新后确保重新加载人员列表
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 刷新人员表格
|
||||
function reloadPerson() {
|
||||
// 调用 loadPersonList 来加载人员数据
|
||||
console.log(selectedTaskId.value+"selectedTaskId.valueselectedTaskId.value")
|
||||
personTableRef.value?.reload?.({ page: 1, limit: 10, taskId: selectedTaskId.value });
|
||||
}
|
||||
|
||||
// 打开编辑弹窗
|
||||
function openEdit(row) {
|
||||
current.value = row ? { ...row } : null;
|
||||
showEdit.value = true;
|
||||
}
|
||||
|
||||
// 批量删除
|
||||
function removeBatch(row) {
|
||||
const rows = row ? [row] : selections.value;
|
||||
if (!rows.length) return message.error('请至少选择一条数据');
|
||||
|
||||
ElMessageBox.confirm(
|
||||
`确认删除选中的${rows.length}条记录?`,
|
||||
'删除确认',
|
||||
{ type: 'warning' }
|
||||
).then(() => {
|
||||
// 执行删除操作
|
||||
rows.forEach(target => {
|
||||
const index = mockPersonList.findIndex(
|
||||
item => item.key === target.key
|
||||
);
|
||||
if (index !== -1) mockPersonList.splice(index, 1);
|
||||
});
|
||||
|
||||
message.success('删除成功');
|
||||
reloadPerson();
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
// 导出数据
|
||||
function exportData() {
|
||||
message.success('导出功能需对接后端实现');
|
||||
}
|
||||
|
||||
// 处理对话框关闭
|
||||
function handleDialogClose() {
|
||||
showEdit.value = false;
|
||||
current.value = null;
|
||||
selectedTaskId.value = null;
|
||||
selections.value = [];
|
||||
}
|
||||
</script>
|
||||
@@ -1,224 +0,0 @@
|
||||
<template>
|
||||
<ele-page>
|
||||
<!-- 搜索表单 -->
|
||||
<ele-card :body-style="{ paddingTop: '8px' }">
|
||||
<!-- 表格 -->
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
row-key="key"
|
||||
:columns="columns"
|
||||
:datasource="datasource"
|
||||
:show-overflow-tooltip="true"
|
||||
v-model:selections="selections"
|
||||
:highlight-current-row="true"
|
||||
:export-config="{ fileName: '试卷人员分配' }"
|
||||
cache-key="systemPersonTable"
|
||||
>
|
||||
<template #toolbar>
|
||||
|
||||
<el-button
|
||||
v-permission="'system:person:add'"
|
||||
type="primary"
|
||||
class="ele-btn-icon"
|
||||
|
||||
@click="openEdit()"
|
||||
>
|
||||
分配场次
|
||||
</el-button>
|
||||
<el-button
|
||||
v-permission="'system:person:remove'"
|
||||
type="danger"
|
||||
class="ele-btn-icon"
|
||||
|
||||
@click="removeBatch()"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<el-button
|
||||
v-permission="'system:person:export'"
|
||||
class="ele-btn-icon"
|
||||
|
||||
@click="exportData"
|
||||
>
|
||||
导出
|
||||
</el-button>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-link
|
||||
v-permission="'system:person:edit'"
|
||||
type="primary"
|
||||
:underline="false"
|
||||
@click="openEdit(row)"
|
||||
>
|
||||
修改
|
||||
</el-link>
|
||||
<el-divider
|
||||
v-permission="['system:person:edit', 'system:person:remove']"
|
||||
direction="vertical"
|
||||
/>
|
||||
<el-link
|
||||
v-permission="'system:person:remove'"
|
||||
type="danger"
|
||||
:underline="false"
|
||||
@click="removeBatch(row)"
|
||||
>
|
||||
删除
|
||||
</el-link>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</ele-card>
|
||||
<!-- 编辑弹窗 -->
|
||||
<person-edit v-model="showEdit" :task-Id="taskId" @done="reload" />
|
||||
</ele-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
// import {
|
||||
// PlusOutlined,
|
||||
// DeleteOutlined,
|
||||
// DownloadOutlined
|
||||
// } from '@/components/icons';
|
||||
import PersonEdit from './components/person-edit.vue';
|
||||
import { pagePersons, removePersons, exportPersons } from '@/api/system/person';
|
||||
import { pageStus, removeStus, exportStus } from '@/api/system/stu';
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
defineOptions({ name: 'SystemPerson' });
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
type: String,
|
||||
default: '',
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const tableRef = ref(null);
|
||||
const selections = ref([]);
|
||||
const current = ref(null);
|
||||
const showEdit = ref(false);
|
||||
const showSet = ref(false);
|
||||
|
||||
/** 班级映射表:deptId => { deptName, teacherName } */
|
||||
const classMap = ref({});
|
||||
|
||||
/** 表格列配置 */
|
||||
const columns = ref([
|
||||
{ type: 'selection', columnKey: 'selection', width: 50, align: 'center', fixed: 'left' },
|
||||
{ type: 'index', columnKey: 'index', width: 50, align: 'center', fixed: 'left' },
|
||||
{ prop: 'stuCode', label: '学生编码', align: 'center', minWidth: 110 },
|
||||
{ prop: 'stuUsername', label: '学生账号', align: 'center', minWidth: 110 },
|
||||
|
||||
|
||||
{
|
||||
prop: 'stuClass',
|
||||
label: '学生班级',
|
||||
align: 'center',
|
||||
minWidth: 110,
|
||||
slot: 'stuClass' // 👈 使用自定义 slot 显示班级 + 悬浮教师名
|
||||
},
|
||||
{ prop: 'status', label: '帐号状态(0正常 1停用)', align: 'center', minWidth: 110 },
|
||||
{ prop: 'batch', label: '分配场次', align: 'center', minWidth: 110 }
|
||||
]);
|
||||
|
||||
/** 表格数据源:处理学生 + 班级 */
|
||||
const datasource = async ({ pages, where }) => {
|
||||
// 获取学生和班级信息
|
||||
const { examStuList, teacherClassDtos } = await pageStus({ ...where, ...pages });
|
||||
|
||||
// 如果没有数据,直接返回空列表
|
||||
if (!examStuList || !teacherClassDtos) {
|
||||
message.error('数据加载失败');
|
||||
return [];
|
||||
}
|
||||
|
||||
console.log("examStuList:", examStuList);
|
||||
console.log("teacherClassDtos:", teacherClassDtos);
|
||||
|
||||
// 构建班级映射表,使用 dept_id 作为 key
|
||||
const classMap = {};
|
||||
teacherClassDtos.forEach(item => {
|
||||
classMap[item.dept_id] = {
|
||||
deptName: item.dept_name,
|
||||
teacherName: item.nick_name || item.user_name || '未知教师'
|
||||
};
|
||||
});
|
||||
|
||||
// 将映射表存储到 `classMap`,供后续使用
|
||||
classMap.value = classMap;
|
||||
|
||||
// 合并学生信息和班级信息
|
||||
const processedList = examStuList.map(stu => {
|
||||
// 使用 stuClass 匹配 teacherClassDtos 的 dept_id
|
||||
const classInfo = classMap[stu.stuClass] || {}; // 如果找不到匹配,返回默认值
|
||||
return {
|
||||
...stu,
|
||||
deptName: classInfo.deptName || '未知班级', // 如果没有班级信息,显示默认值
|
||||
teacherName: classInfo.teacherName || '未知教师' // 如果没有教师信息,显示默认值
|
||||
};
|
||||
});
|
||||
|
||||
console.log("processedList:", processedList);
|
||||
return processedList; // 返回合并后的学生信息
|
||||
};
|
||||
|
||||
|
||||
/** 搜索 */
|
||||
const reload = (where) => {
|
||||
tableRef.value?.reload?.({ page: 1, where });
|
||||
};
|
||||
|
||||
/** 打开编辑弹窗 */
|
||||
const openEdit = (row) => {
|
||||
current.value = row ?? null;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
/** 打开班级设置弹窗 */
|
||||
const openSet = (row) => {
|
||||
current.value = row ?? null;
|
||||
showSet.value = true;
|
||||
};
|
||||
|
||||
/** 删除学生(单条或多条) */
|
||||
const removeBatch = (row) => {
|
||||
const rows = row == null ? selections.value : [row];
|
||||
if (!rows.length) {
|
||||
message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
ElMessageBox.confirm(
|
||||
`是否确认删除学生编码为"${rows.map((d) => d.stuCode).join()}"的数据项?`,
|
||||
'系统提示',
|
||||
{ type: 'warning', draggable: true }
|
||||
)
|
||||
.then(() => {
|
||||
const loading = message.loading({ message: '请求中..', plain: true });
|
||||
removeStus(rows.map((d) => d.stuId))
|
||||
.then(() => {
|
||||
loading.close();
|
||||
message.success('删除成功');
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.close();
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
/** 导出数据 */
|
||||
const exportData = () => {
|
||||
const loading = message.loading({ message: '请求中..', plain: true });
|
||||
tableRef.value?.fetch?.(({ where }) => {
|
||||
exportStus(where)
|
||||
.then(() => loading.close())
|
||||
.catch((e) => {
|
||||
loading.close();
|
||||
message.error(e.message);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
</script>
|
||||
@@ -1,260 +0,0 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
title="人员分配"
|
||||
v-model:visible="visible"
|
||||
width="80%"
|
||||
@close="handleDialogClose"
|
||||
>
|
||||
<ele-page>
|
||||
<el-row :gutter="16">
|
||||
<!-- 左侧任务列表 -->
|
||||
<el-col :span="10">
|
||||
<ele-card title="试卷任务列表" :body-style="{ paddingTop: '8px' }">
|
||||
<ele-pro-table
|
||||
ref="taskTableRef"
|
||||
row-key="id"
|
||||
:columns="taskColumns"
|
||||
:datasource="loadTaskList"
|
||||
:highlight-current-row="true"
|
||||
@row-click="handleTaskRowClick"
|
||||
/>
|
||||
</ele-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧人员分配列表 -->
|
||||
<el-col :span="14">
|
||||
<ele-card :body-style="{ paddingTop: '8px' }">
|
||||
<ele-pro-table
|
||||
ref="personTableRef"
|
||||
row-key="key"
|
||||
:columns="personColumns"
|
||||
:datasource="loadPersonList"
|
||||
:show-overflow-tooltip="true"
|
||||
v-model:selections="selections"
|
||||
:highlight-current-row="true"
|
||||
:export-config="{ fileName: '试卷人员分配' }"
|
||||
cache-key="systemPersonTable"
|
||||
>
|
||||
<!-- 工具栏模板 -->
|
||||
<template #toolbar>
|
||||
<el-button
|
||||
type="primary"
|
||||
class="ele-btn-icon"
|
||||
@click="openEdit()"
|
||||
:disabled="!selectedTaskId"
|
||||
>
|
||||
分配场次
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
class="ele-btn-icon"
|
||||
@click="removeBatch()"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<el-button
|
||||
class="ele-btn-icon"
|
||||
@click="exportData"
|
||||
>
|
||||
导出
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<!-- 操作列模板 -->
|
||||
<template #action="{ row }">
|
||||
<el-link type="primary" :underline="false" @click="openEdit(row)">修改</el-link>
|
||||
<el-divider direction="vertical" />
|
||||
<el-link type="danger" :underline="false" @click="removeBatch(row)">删除</el-link>
|
||||
</template>
|
||||
|
||||
<!-- 空状态模板 -->
|
||||
<template #empty>
|
||||
<el-empty description="暂无分配人员" />
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</ele-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 编辑组件 -->
|
||||
<person-edit
|
||||
v-model="showEdit"
|
||||
:task-id="selectedTaskId"
|
||||
:data="current"
|
||||
@done="reloadPerson"
|
||||
/>
|
||||
</ele-page>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, nextTick, onMounted } from 'vue';
|
||||
const message = useMessage() // 消息弹窗
|
||||
// import {
|
||||
// PlusOutlined,
|
||||
// DeleteOutlined,
|
||||
// DownloadOutlined
|
||||
// } from '@/components/icons';
|
||||
|
||||
// 组件状态
|
||||
const visible = ref(true); // 对话框可见状态
|
||||
const selectedTaskId = ref(null); // 当前选中的任务ID
|
||||
const selections = ref([]); // 右侧表格选中项
|
||||
const showEdit = ref(false); // 编辑对话框状态
|
||||
const current = ref(null); // 当前操作的行数据
|
||||
|
||||
// 表格引用
|
||||
const taskTableRef = ref(null); // 左侧任务表格实例
|
||||
const personTableRef = ref(null); // 右侧人员表格实例
|
||||
|
||||
// 模拟数据
|
||||
const mockTaskList = [
|
||||
{ id: 1, title: '数学试卷任务' },
|
||||
{ id: 2, title: '英语试卷任务' },
|
||||
{ id: 3, title: '物理试卷任务' }
|
||||
];
|
||||
|
||||
const mockPersonList = [
|
||||
{ key: 101, taskId: 1, personId: 'S001', sessionId: 'A' },
|
||||
{ key: 102, taskId: 1, personId: 'S002', sessionId: 'B' },
|
||||
{ key: 103, taskId: 2, personId: 'S003', sessionId: 'A' },
|
||||
{ key: 104, taskId: 3, personId: 'S004', sessionId: 'C' }
|
||||
];
|
||||
|
||||
// 表格列配置
|
||||
const taskColumns = [
|
||||
{ type: 'index', label: '#', width: 50, align: 'center' },
|
||||
{ prop: 'id', label: '任务ID', align: 'center' },
|
||||
{ prop: 'title', label: '任务名称', align: 'center' }
|
||||
];
|
||||
|
||||
// 人员列
|
||||
const personColumns = [
|
||||
{ type: 'selection', width: 50, align: 'center' },
|
||||
{ type: 'index', width: 50, align: 'center' },
|
||||
{ prop: 'taskId', label: '试卷任务ID', align: 'center' },
|
||||
{ prop: 'personId', label: '学生ID', align: 'center' },
|
||||
{ prop: 'sessionId', label: '场次', align: 'center' },
|
||||
{
|
||||
columnKey: 'action',
|
||||
label: '操作',
|
||||
width: 180,
|
||||
align: 'center',
|
||||
slot: 'action',
|
||||
hideInPrint: true,
|
||||
hideInExport: true
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
// 设置默认选中第一个任务
|
||||
if (mockTaskList.length > 0) {
|
||||
selectedTaskId.value = mockTaskList[0].id;
|
||||
// 等待表格渲染完成后设置选中样式
|
||||
nextTick(() => {
|
||||
// 确保正确引用表格实例,确保调用 setCurrentRow 或类似方法
|
||||
if (taskTableRef.value) {
|
||||
// 替换为实际可用的方法来选中行
|
||||
taskTableRef.value.select([mockTaskList[0].id]); // 如果有 select 方法
|
||||
reloadPerson(); // 加载人员列表
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 加载任务列表
|
||||
const loadTaskList = async () => {
|
||||
try {
|
||||
// 模拟接口请求延迟
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
return mockTaskList; // 直接返回数组
|
||||
} catch (error) {
|
||||
message.error('任务加载失败');
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
// 加载人员列表(带分页)
|
||||
const loadPersonList = async ({ page = 1, limit = 10, taskId }) => {
|
||||
if (!taskId) return { data: [], total: 0 }; // 如果没有任务ID,返回空数据
|
||||
try {
|
||||
// 过滤当前任务的人员
|
||||
const filtered = mockPersonList.filter(
|
||||
item => item.taskId === taskId
|
||||
);
|
||||
|
||||
// 模拟分页
|
||||
const start = (page - 1) * limit;
|
||||
const end = start + limit;
|
||||
|
||||
return {
|
||||
data: filtered.slice(start, end), // 直接返回分页数据
|
||||
total: filtered.length // 返回过滤后的总数
|
||||
};
|
||||
} catch (error) {
|
||||
message.error('人员数据加载失败');
|
||||
return { data: [], total: 0 };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 处理任务行点击
|
||||
function handleTaskRowClick(row) {
|
||||
selectedTaskId.value = row.id;
|
||||
nextTick(() => {
|
||||
reloadPerson(); // 在数据更新后确保重新加载人员列表
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 刷新人员表格
|
||||
function reloadPerson() {
|
||||
// 调用 loadPersonList 来加载人员数据
|
||||
console.log(selectedTaskId.value+"selectedTaskId.valueselectedTaskId.value")
|
||||
personTableRef.value?.reload?.({ page: 1, limit: 10, taskId: selectedTaskId.value });
|
||||
}
|
||||
|
||||
// 打开编辑弹窗
|
||||
function openEdit(row) {
|
||||
current.value = row ? { ...row } : null;
|
||||
showEdit.value = true;
|
||||
}
|
||||
|
||||
// 批量删除
|
||||
function removeBatch(row) {
|
||||
const rows = row ? [row] : selections.value;
|
||||
if (!rows.length) return message.error('请至少选择一条数据');
|
||||
|
||||
ElMessageBox.confirm(
|
||||
`确认删除选中的${rows.length}条记录?`,
|
||||
'删除确认',
|
||||
{ type: 'warning' }
|
||||
).then(() => {
|
||||
// 执行删除操作
|
||||
rows.forEach(target => {
|
||||
const index = mockPersonList.findIndex(
|
||||
item => item.key === target.key
|
||||
);
|
||||
if (index !== -1) mockPersonList.splice(index, 1);
|
||||
});
|
||||
|
||||
message.success('删除成功');
|
||||
reloadPerson();
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
// 导出数据
|
||||
function exportData() {
|
||||
message.success('导出功能需对接后端实现');
|
||||
}
|
||||
|
||||
// 处理对话框关闭
|
||||
function handleDialogClose() {
|
||||
showEdit.value = false;
|
||||
current.value = null;
|
||||
selectedTaskId.value = null;
|
||||
selections.value = [];
|
||||
}
|
||||
</script>
|
||||
@@ -1,224 +0,0 @@
|
||||
<template>
|
||||
<ele-page>
|
||||
<!-- 搜索表单 -->
|
||||
<ele-card :body-style="{ paddingTop: '8px' }">
|
||||
<!-- 表格 -->
|
||||
<ele-pro-table
|
||||
ref="tableRef"
|
||||
row-key="key"
|
||||
:columns="columns"
|
||||
:datasource="datasource"
|
||||
:show-overflow-tooltip="true"
|
||||
v-model:selections="selections"
|
||||
:highlight-current-row="true"
|
||||
:export-config="{ fileName: '试卷人员分配' }"
|
||||
cache-key="systemPersonTable"
|
||||
>
|
||||
<template #toolbar>
|
||||
|
||||
<el-button
|
||||
v-permission="'system:person:add'"
|
||||
type="primary"
|
||||
class="ele-btn-icon"
|
||||
|
||||
@click="openEdit()"
|
||||
>
|
||||
分配场次
|
||||
</el-button>
|
||||
<el-button
|
||||
v-permission="'system:person:remove'"
|
||||
type="danger"
|
||||
class="ele-btn-icon"
|
||||
|
||||
@click="removeBatch()"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<el-button
|
||||
v-permission="'system:person:export'"
|
||||
class="ele-btn-icon"
|
||||
|
||||
@click="exportData"
|
||||
>
|
||||
导出
|
||||
</el-button>
|
||||
</template>
|
||||
<template #action="{ row }">
|
||||
<el-link
|
||||
v-permission="'system:person:edit'"
|
||||
type="primary"
|
||||
:underline="false"
|
||||
@click="openEdit(row)"
|
||||
>
|
||||
修改
|
||||
</el-link>
|
||||
<el-divider
|
||||
v-permission="['system:person:edit', 'system:person:remove']"
|
||||
direction="vertical"
|
||||
/>
|
||||
<el-link
|
||||
v-permission="'system:person:remove'"
|
||||
type="danger"
|
||||
:underline="false"
|
||||
@click="removeBatch(row)"
|
||||
>
|
||||
删除
|
||||
</el-link>
|
||||
</template>
|
||||
</ele-pro-table>
|
||||
</ele-card>
|
||||
<!-- 编辑弹窗 -->
|
||||
<person-edit v-model="showEdit" :task-Id="taskId" @done="reload" />
|
||||
</ele-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
// import {
|
||||
// PlusOutlined,
|
||||
// DeleteOutlined,
|
||||
// DownloadOutlined
|
||||
// } from '@/components/icons';
|
||||
import PersonEdit from './components/person-edit.vue';
|
||||
import { pagePersons, removePersons, exportPersons } from '@/api/system/person';
|
||||
import { pageStus, removeStus, exportStus } from '@/api/system/stu';
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
defineOptions({ name: 'SystemPerson' });
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
type: String,
|
||||
default: '',
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const tableRef = ref(null);
|
||||
const selections = ref([]);
|
||||
const current = ref(null);
|
||||
const showEdit = ref(false);
|
||||
const showSet = ref(false);
|
||||
|
||||
/** 班级映射表:deptId => { deptName, teacherName } */
|
||||
const classMap = ref({});
|
||||
|
||||
/** 表格列配置 */
|
||||
const columns = ref([
|
||||
{ type: 'selection', columnKey: 'selection', width: 50, align: 'center', fixed: 'left' },
|
||||
{ type: 'index', columnKey: 'index', width: 50, align: 'center', fixed: 'left' },
|
||||
{ prop: 'stuCode', label: '学生编码', align: 'center', minWidth: 110 },
|
||||
{ prop: 'stuUsername', label: '学生账号', align: 'center', minWidth: 110 },
|
||||
|
||||
|
||||
{
|
||||
prop: 'stuClass',
|
||||
label: '学生班级',
|
||||
align: 'center',
|
||||
minWidth: 110,
|
||||
slot: 'stuClass' // 👈 使用自定义 slot 显示班级 + 悬浮教师名
|
||||
},
|
||||
{ prop: 'status', label: '帐号状态(0正常 1停用)', align: 'center', minWidth: 110 },
|
||||
{ prop: 'batch', label: '分配场次', align: 'center', minWidth: 110 }
|
||||
]);
|
||||
|
||||
/** 表格数据源:处理学生 + 班级 */
|
||||
const datasource = async ({ pages, where }) => {
|
||||
// 获取学生和班级信息
|
||||
const { examStuList, teacherClassDtos } = await pageStus({ ...where, ...pages });
|
||||
|
||||
// 如果没有数据,直接返回空列表
|
||||
if (!examStuList || !teacherClassDtos) {
|
||||
message.error('数据加载失败');
|
||||
return [];
|
||||
}
|
||||
|
||||
console.log("examStuList:", examStuList);
|
||||
console.log("teacherClassDtos:", teacherClassDtos);
|
||||
|
||||
// 构建班级映射表,使用 dept_id 作为 key
|
||||
const classMap = {};
|
||||
teacherClassDtos.forEach(item => {
|
||||
classMap[item.dept_id] = {
|
||||
deptName: item.dept_name,
|
||||
teacherName: item.nick_name || item.user_name || '未知教师'
|
||||
};
|
||||
});
|
||||
|
||||
// 将映射表存储到 `classMap`,供后续使用
|
||||
classMap.value = classMap;
|
||||
|
||||
// 合并学生信息和班级信息
|
||||
const processedList = examStuList.map(stu => {
|
||||
// 使用 stuClass 匹配 teacherClassDtos 的 dept_id
|
||||
const classInfo = classMap[stu.stuClass] || {}; // 如果找不到匹配,返回默认值
|
||||
return {
|
||||
...stu,
|
||||
deptName: classInfo.deptName || '未知班级', // 如果没有班级信息,显示默认值
|
||||
teacherName: classInfo.teacherName || '未知教师' // 如果没有教师信息,显示默认值
|
||||
};
|
||||
});
|
||||
|
||||
console.log("processedList:", processedList);
|
||||
return processedList; // 返回合并后的学生信息
|
||||
};
|
||||
|
||||
|
||||
/** 搜索 */
|
||||
const reload = (where) => {
|
||||
tableRef.value?.reload?.({ page: 1, where });
|
||||
};
|
||||
|
||||
/** 打开编辑弹窗 */
|
||||
const openEdit = (row) => {
|
||||
current.value = row ?? null;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
/** 打开班级设置弹窗 */
|
||||
const openSet = (row) => {
|
||||
current.value = row ?? null;
|
||||
showSet.value = true;
|
||||
};
|
||||
|
||||
/** 删除学生(单条或多条) */
|
||||
const removeBatch = (row) => {
|
||||
const rows = row == null ? selections.value : [row];
|
||||
if (!rows.length) {
|
||||
message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
ElMessageBox.confirm(
|
||||
`是否确认删除学生编码为"${rows.map((d) => d.stuCode).join()}"的数据项?`,
|
||||
'系统提示',
|
||||
{ type: 'warning', draggable: true }
|
||||
)
|
||||
.then(() => {
|
||||
const loading = message.loading({ message: '请求中..', plain: true });
|
||||
removeStus(rows.map((d) => d.stuId))
|
||||
.then(() => {
|
||||
loading.close();
|
||||
message.success('删除成功');
|
||||
reload();
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.close();
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
/** 导出数据 */
|
||||
const exportData = () => {
|
||||
const loading = message.loading({ message: '请求中..', plain: true });
|
||||
tableRef.value?.fetch?.(({ where }) => {
|
||||
exportStus(where)
|
||||
.then(() => loading.close())
|
||||
.catch((e) => {
|
||||
loading.close();
|
||||
message.error(e.message);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
</script>
|
||||
@@ -296,7 +296,11 @@ const fetchCount = async () => {
|
||||
|
||||
/** 保存 */
|
||||
const save = async () => {
|
||||
|
||||
// 判断试题数量是否小于等于可用题目数量
|
||||
if (Number(form.value.quNumbers) > Number(availableCount.value)) {
|
||||
message.warning('试题数量不能超过可用题目数量');
|
||||
return;
|
||||
}
|
||||
if (Number(availableCount.value) === 0) {
|
||||
message.warning('当前条件下没有可用的试题,请调整条件后再试');
|
||||
return;
|
||||
@@ -8,7 +8,16 @@
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
|
||||
<el-form-item label="题型" prop="spName">
|
||||
<el-select v-model="queryParams.spName" placeholder="请选择题型" clearable class="!w-240px">
|
||||
<el-option
|
||||
v-for="item in CourseOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
@@ -89,7 +98,10 @@ import * as SmsChannelApi from '@/api/system/scheme';
|
||||
|
||||
import SchemeEdit from './components/step-edit.vue';
|
||||
import SchemeSearch from './components/step-search.vue';
|
||||
|
||||
const CourseOptions = ref<string[]>([])
|
||||
import {
|
||||
getCourseList,
|
||||
} from '@/api/system/scheme'
|
||||
|
||||
defineOptions({ name: 'SystemSmsChannel' })
|
||||
|
||||
@@ -138,11 +150,18 @@ const queryParams = reactive({
|
||||
signature: undefined,
|
||||
status: undefined,
|
||||
createTime: [],
|
||||
taskId: props.taskId
|
||||
taskId: props.taskId ,
|
||||
spName:undefined
|
||||
})
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
// 1. 并行拉下拉所需的选项
|
||||
const [courses] = await Promise.all([
|
||||
getCourseList(),
|
||||
|
||||
])
|
||||
CourseOptions.value = courses
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await SmsChannelApi.pageSchemes(queryParams)
|
||||
@@ -16,6 +16,16 @@
|
||||
@change="handleFormChange" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 是否使用监考密码验证 -->
|
||||
<el-form-item label="是否启用考场设置">
|
||||
<el-switch
|
||||
v-model="form.isSession"
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
@change="handleFormChange" />
|
||||
</el-form-item>
|
||||
<!-- 监考密码 -->
|
||||
<el-form-item label="监考密码" v-if="form.isExamPassword === '0'">
|
||||
<el-input v-model="form.examPassword" placeholder="请输入监考密码" @input="handleFormChange" />
|
||||
@@ -89,6 +99,8 @@
|
||||
<el-radio-group v-model="form.driver" @change="handleFormChange">
|
||||
<el-radio label="C">C</el-radio>
|
||||
<el-radio label="D">D</el-radio>
|
||||
<el-radio label="E">E</el-radio>
|
||||
<el-radio label="F">F</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
@submit.prevent=""
|
||||
>
|
||||
<el-form-item label="试卷ID" prop="taskId">
|
||||
<el-input clearable v-model="form.taskId" placeholder="请输入试卷ID" />
|
||||
<el-input clearable v-model="form.taskId" placeholder="请输入试卷ID" disabled/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="批次" prop="batch">
|
||||
<el-input clearable v-model="form.batch" placeholder="请输入批次" />
|
||||
<el-input clearable v-model="form.batch" placeholder="请输入批次" disabled/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="考试开始时间" prop="startTime">
|
||||
@@ -0,0 +1,264 @@
|
||||
<template>
|
||||
<Dialog
|
||||
title="人员分配"
|
||||
v-model:visible="visible"
|
||||
width="80%"
|
||||
append-to-body
|
||||
lock-scroll
|
||||
:destroy-on-close="false"
|
||||
:draggable="false"
|
||||
@close="handleDialogClose"
|
||||
>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
|
||||
<ele-page style="height: 65vh;">
|
||||
<el-row :gutter="16" style="height: 100%;">
|
||||
<!-- 左侧任务列表 -->
|
||||
<el-col :span="10" style="height: 100%;">
|
||||
<div style="height: 100%; overflow: auto;">
|
||||
<ele-card title="试卷任务列表" :body-style="{ paddingTop: '8px' }">
|
||||
|
||||
<ContentWrap style="overflow-x: auto;">
|
||||
<el-table
|
||||
table-layout="fixed"
|
||||
ref="taskTableRef"
|
||||
:data="mockTaskList"
|
||||
highlight-current-row
|
||||
@row-click="handleTaskRowClick"
|
||||
style="min-width: 1000px; width: 100%; "
|
||||
>
|
||||
<el-table-column type="index" label="#" width="50" align="center" />
|
||||
<el-table-column label="考试批次" align="center" prop="batch" />
|
||||
<el-table-column label="考试开始时间" align="center" prop="startTime" />
|
||||
<el-table-column label="考试结束时间" align="center" prop="endTime" />
|
||||
<el-table-column label="开始考试前分钟,允许入场" align="center" prop="allowEntry" />
|
||||
<el-table-column label="开始考试后分钟,禁止入场" align="center" prop="endAllowEntry" />
|
||||
<el-table-column label="开始考试后分钟,允许交卷" align="center" prop="allowSubmission" />
|
||||
</el-table>
|
||||
<Pagination
|
||||
:total="taskTotal"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
</ele-card>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧人员分配列表 -->
|
||||
<el-col :span="14" style="height: 100%;">
|
||||
<div style="height: 100%; overflow: auto;">
|
||||
<ele-card :body-style="{ paddingTop: '8px' }">
|
||||
<div class="mb-2">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="openAssignDialog()"
|
||||
>
|
||||
分配人员
|
||||
</el-button>
|
||||
<el-button type="danger" @click="removeBatch()">删除</el-button>
|
||||
<el-button @click="exportData()">导出</el-button>
|
||||
</div>
|
||||
<ContentWrap style="overflow-x: auto;">
|
||||
<el-table
|
||||
table-layout="fixed"
|
||||
ref="personTableRef"
|
||||
:data="filteredPersonList"
|
||||
style="min-width: 1000px; width: 100%"
|
||||
highlight-current-row
|
||||
@selection-change="selections = $event"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column type="index" label="#" width="50" align="center" />
|
||||
<el-table-column label="用户名称" align="center" prop="username" />
|
||||
<el-table-column label="用户昵称" align="center" prop="nickname" />
|
||||
<el-table-column label="班级名称" align="center" prop="className"/>
|
||||
<el-table-column label="分配考场" align="center" prop="batch"/>
|
||||
<!-- <el-table-column label="操作" align="center" width="180">
|
||||
<template #default="{ row }">
|
||||
<el-link type="primary" @click="openEdit(row)">修改</el-link>
|
||||
<el-divider direction="vertical" />
|
||||
<el-link type="danger" @click="removeBatch(row)">删除</el-link>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
</el-table>
|
||||
<Pagination
|
||||
:stutotal="stutotal"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<el-empty
|
||||
v-if="filteredPersonList.length === 0"
|
||||
description="暂无分配人员"
|
||||
/>
|
||||
</ele-card>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</ele-page>
|
||||
|
||||
|
||||
|
||||
<PersonSearch :session-id="selectedSessionId" :session-batch="selectedSessionBanch" :task-id="props.taskId" v-model="showPersonEdit" ref="stuAddRef" @done="reload" />
|
||||
|
||||
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick, computed } from 'vue';
|
||||
import PersonSearch from './person-serach.vue'
|
||||
|
||||
import * as SmsChannelApi from '@/api/system/session';
|
||||
import * as PersonApi from '@/api/system/person'
|
||||
const visible = ref(true);
|
||||
const selectedSessionId = ref(null);
|
||||
|
||||
const selectedSessionBanch = ref(null);
|
||||
|
||||
const selections = ref([]);
|
||||
const showEdit = ref(false);
|
||||
const current = ref(null);
|
||||
|
||||
const taskTableRef = ref();
|
||||
const personTableRef = ref();
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
status: '0',
|
||||
taskId: props.taskId,
|
||||
sessionId:''
|
||||
})
|
||||
const stuAddRef = ref();
|
||||
|
||||
const showPersonEdit = ref(false)
|
||||
const emit = defineEmits(['done'])
|
||||
const openAssignDialog = () => {
|
||||
if (!selectedSessionId.value) {
|
||||
ElMessage.warning('请先选择左侧任务!')
|
||||
return
|
||||
}
|
||||
showPersonEdit.value = true
|
||||
nextTick(() => {
|
||||
stuAddRef.value?.open();
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
const submitAssign = () => {
|
||||
// 提交逻辑
|
||||
console.log('提交分配人员')
|
||||
assignVisible.value = false
|
||||
}
|
||||
|
||||
|
||||
const mockTaskList = ref([]) ;
|
||||
|
||||
const filteredPersonList = ref([]) ;
|
||||
|
||||
|
||||
const handleTaskRowClick = async (row) => {
|
||||
selectedSessionId.value = row.sessionId;
|
||||
selectedSessionBanch.value=row.batch;
|
||||
|
||||
queryParams.sessionId = row.sessionId;
|
||||
const res = await SmsChannelApi.getSessionStu(queryParams);
|
||||
filteredPersonList.value = res.list;
|
||||
stutotal.value=res.total;
|
||||
};
|
||||
|
||||
|
||||
const openEdit = (row = null) => {
|
||||
current.value = row;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
const removeBatch = async (row) => {
|
||||
const selectedIds = selections.value.map(item => item.id);
|
||||
console.log('选中的 ID 列表:', selectedIds);
|
||||
console.log('选中的 考场 id:' + selectedSessionId.value);
|
||||
console.log('选中的 试卷任务id:', props.taskId);
|
||||
|
||||
// 弹出确认提示框
|
||||
ElMessageBox.confirm(
|
||||
'确定要删除选中的人员吗?',
|
||||
'警告',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
).then(async () => {
|
||||
// 赋值参数
|
||||
const data = {
|
||||
studentIds: selectedIds,
|
||||
sessionId: selectedSessionId.value,
|
||||
taskId: props.taskId,
|
||||
batch: selectedSessionBanch.value,
|
||||
};
|
||||
|
||||
await PersonApi.removeSessionStu(data);
|
||||
|
||||
// 删除选中的人员
|
||||
for (const sel of selections.value) {
|
||||
const index = filteredPersonList.value.findIndex(p => p.id === sel.id);
|
||||
if (index !== -1) filteredPersonList.value.splice(index, 1);
|
||||
}
|
||||
emit('done')
|
||||
ElMessage.success('删除成功');
|
||||
}).catch(() => {
|
||||
|
||||
console.log('删除操作已取消');
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const getList = async () => {
|
||||
const res = await SmsChannelApi.pageSessions(queryParams)
|
||||
mockTaskList.value = res
|
||||
total.value = res.total
|
||||
}
|
||||
const reload = () => {
|
||||
getList()
|
||||
emit('done')
|
||||
}
|
||||
|
||||
const exportData = () => {
|
||||
console.log('导出功能触发');
|
||||
};
|
||||
|
||||
const reloadPerson = () => {
|
||||
// 模拟刷新
|
||||
console.log('刷新人员列表');
|
||||
};
|
||||
|
||||
const handleDialogClose = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,331 @@
|
||||
<template>
|
||||
<!-- 搜索 -->
|
||||
<Dialog
|
||||
title="人员分配"
|
||||
v-model="visible"
|
||||
width="80%"
|
||||
append-to-body
|
||||
lock-scroll
|
||||
:destroy-on-close="false"
|
||||
:draggable="false"
|
||||
@close="handleDialogClose"
|
||||
>
|
||||
<ContentWrap>
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="学生名称" prop="username">
|
||||
<el-input
|
||||
v-model="queryParams.username"
|
||||
placeholder="请输入学生名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="班级" prop="className">
|
||||
<el-select
|
||||
v-model="queryParams.className"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
placeholder="请选择或输入"
|
||||
style="width: 300px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in classNameList"
|
||||
:key="item.className"
|
||||
:label="item.className"
|
||||
:value="item.className"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="请选择学生状态"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="datetimerange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
|
||||
<!-- <el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
>
|
||||
<Icon icon="ep:plus" /> 新增
|
||||
</el-button> -->
|
||||
<!-- <el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="handleImport"
|
||||
>
|
||||
<Icon icon="ep:upload" /> 导入
|
||||
</el-button> -->
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
>
|
||||
<Icon icon="ep:download" />导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" ref="tableRef" :data="list" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="用户编号" align="center" key="id" prop="id" />
|
||||
<el-table-column
|
||||
label="用户名称"
|
||||
align="center"
|
||||
prop="username"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column
|
||||
label="用户昵称"
|
||||
align="center"
|
||||
prop="nickname"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column
|
||||
label="班级名称"
|
||||
align="center"
|
||||
prop="className"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column label="状态" key="status" align="center">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
|
||||
disabled
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180"
|
||||
/>
|
||||
</el-table>
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="open"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleDialogClose">取消</el-button>
|
||||
<el-button type="primary" @click="confirmSelection">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
<!-- 用户导入对话框 -->
|
||||
<!-- <StudentImportForm ref="importFormRef" @success="open" /> -->
|
||||
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as ClassApi from '@/api/exam/class'
|
||||
import * as PersonApi from '@/api/system/person'
|
||||
defineOptions({ name: 'SystemUser' })
|
||||
const props = defineProps({
|
||||
sessionId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
taskId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
sessionBatch:{
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const tableRef = ref()
|
||||
|
||||
const visible = defineModel({ type: Boolean });
|
||||
|
||||
const classNameList = ref();
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
className: undefined,
|
||||
nickname: undefined,
|
||||
username: undefined,
|
||||
mobile: undefined,
|
||||
status: undefined,
|
||||
deptId: undefined,
|
||||
userType: "2",
|
||||
createTime: [],
|
||||
taskId:'',
|
||||
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
/** 查询列表 */
|
||||
const open = async () => {
|
||||
loading.value = true
|
||||
classNameList.value = await ClassApi.ClassApi.getClassName()
|
||||
queryParams.taskId=props.taskId
|
||||
|
||||
console.log(props.taskId+"props.taskId")
|
||||
console.log(props.sessionId+"props.sessionId")
|
||||
try {
|
||||
const params: any = {
|
||||
...queryParams,
|
||||
sessionId:props.sessionId,
|
||||
startTime: queryParams.createTime?.[0] || null,
|
||||
endTime: queryParams.createTime?.[1] || null,
|
||||
}
|
||||
|
||||
const data = await PersonApi.getSessionStuBySearch(params)
|
||||
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
const emit = defineEmits(['done'])
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
open()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 用户导入 */
|
||||
const importFormRef = ref()
|
||||
const handleImport = () => {
|
||||
importFormRef.value.open()
|
||||
}
|
||||
const multipleSelection = ref<UserApi.UserVO[]>([])
|
||||
|
||||
const handleSelectionChange = (val: UserApi.UserVO[]) => {
|
||||
multipleSelection.value = val
|
||||
}
|
||||
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const exportLoading = ref(false)
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await UserApi.exportUser(queryParams)
|
||||
download.excel(data, '用户数据.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleDialogClose = () => {
|
||||
visible.value = false;
|
||||
multipleSelection.value = [] // 清空选中的数据
|
||||
tableRef.value?.clearSelection()
|
||||
|
||||
};
|
||||
const confirmSelection = async () => {
|
||||
const selectedIds = multipleSelection.value.map(item => item.id)
|
||||
const count = selectedIds.length
|
||||
|
||||
if (count === 0) {
|
||||
ElMessage.warning('请先选择学生')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`已选中 ${count} 个学生,是否确认分配?`,
|
||||
'确认分配',
|
||||
{
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
|
||||
const data = {
|
||||
studentIds: selectedIds,
|
||||
sessionId: props.sessionId,
|
||||
taskId: props.taskId,
|
||||
batch:props.sessionBatch
|
||||
}
|
||||
await PersonApi.setSessionStu(data)
|
||||
|
||||
ElMessage.success(`成功分配 ${count} 个学生`)
|
||||
emit('done')
|
||||
visible.value = false
|
||||
multipleSelection.value = []
|
||||
tableRef.value?.clearSelection()
|
||||
|
||||
} catch (err) {
|
||||
// 用户取消时会进入这里,不需要处理
|
||||
console.log('用户取消了分配')
|
||||
}
|
||||
}
|
||||
defineExpose({ open })
|
||||
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
open()
|
||||
})
|
||||
</script>
|
||||
250
src/views/task/collegeexam/components/steps/step5/index.vue
Normal file
250
src/views/task/collegeexam/components/steps/step5/index.vue
Normal file
@@ -0,0 +1,250 @@
|
||||
<template>
|
||||
|
||||
<ContentWrap>
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
|
||||
type="primary"
|
||||
plain
|
||||
@click="openSearch('create')"
|
||||
v-hasPermi="['system:sms-channel:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 添加学生</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="showAssignButton"
|
||||
type="primary"
|
||||
plain
|
||||
@click="openEdit('create')"
|
||||
v-hasPermi="['system:sms-channel:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 分配场次</el-button
|
||||
>
|
||||
<el-button
|
||||
type="danger"
|
||||
class="ele-btn-del"
|
||||
:disabled="!selections.length"
|
||||
@click="handleDeletes()"
|
||||
>
|
||||
批量删除
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="用户名称" align="center" prop="username" />
|
||||
<el-table-column label="用户昵称" align="center" prop="nickname" />
|
||||
<el-table-column label="班级名称" align="center" prop="className"/>
|
||||
<el-table-column label="分配考场" align="center" prop="batch"/>
|
||||
|
||||
<!-- <el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openEdit('update', scope.row)"
|
||||
v-hasPermi="['system:sms-channel:update']"
|
||||
>
|
||||
查看
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.schemeId)"
|
||||
v-hasPermi="['system:sms-channel:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- 编辑弹窗 -->
|
||||
<person-edit v-model="showEdit" :task-Id="props.taskId" @done="reload" />
|
||||
<PersonSearch :task-id="props.taskId" v-model="showPersonEdit" ref="stuAddRef" @done="reload" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
|
||||
import * as SmsPersonlApi from '@/api/system/person';
|
||||
import PersonEdit from './components/person-edit.vue';
|
||||
import PersonSearch from './components/person-serach.vue'
|
||||
import * as SmsChannelApi from '@/api/system/session';
|
||||
import { fa } from 'element-plus/es/locale';
|
||||
|
||||
defineOptions({ name: 'SystemSmsChannel' })
|
||||
const showAssignButton = ref(true) // 控制按钮显示
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps({
|
||||
taskSpecialty: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
taskId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
formData:{
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
})
|
||||
const showPersonEdit = ref(false)
|
||||
/** 当前编辑数据 */
|
||||
const current = ref<object>();
|
||||
|
||||
/** 是否显示编辑弹窗 */
|
||||
const showEdit = ref(false);
|
||||
|
||||
const stuAddRef = ref();
|
||||
|
||||
|
||||
|
||||
const taskEditRef = ref()
|
||||
|
||||
|
||||
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
signature: undefined,
|
||||
status: undefined,
|
||||
createTime: [],
|
||||
taskId: props.taskId
|
||||
})
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const resButton = await SmsChannelApi.fetchNoMes(props.taskId)
|
||||
|
||||
if (resButton==='200') {
|
||||
showAssignButton.value = true
|
||||
} else {
|
||||
showAssignButton.value = false
|
||||
}
|
||||
|
||||
|
||||
|
||||
const res = await SmsPersonlApi.pagePersons(queryParams)
|
||||
console.log(res)
|
||||
list.value = res.list
|
||||
total.value = res.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
const reload = () => {
|
||||
getList()
|
||||
}
|
||||
/** 表格选中数据 */
|
||||
const selections = ref([]);
|
||||
const handleSelectionChange = (rows) => {
|
||||
selections.value = rows;
|
||||
}
|
||||
|
||||
|
||||
const selectedRows = ref<string[]>([]);
|
||||
|
||||
const handleDeletes = async () => {
|
||||
try {
|
||||
const rows = selections.value;
|
||||
if (!rows.length) {
|
||||
message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
|
||||
selectedRows.value = rows.map((d: any) => d.id); // 保存选中的行数据
|
||||
|
||||
const deleteData = {
|
||||
studentIds: selectedRows.value, // 选中的 ID 列表
|
||||
taskId: props.taskId // 任务 ID
|
||||
}
|
||||
await SmsPersonlApi.removePersons(deleteData)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
|
||||
const openEdit = (type: string, row?: object) => {
|
||||
showEdit.value = true
|
||||
current.value = row
|
||||
nextTick(() => {
|
||||
taskEditRef.value?.open(type, row)
|
||||
})
|
||||
}
|
||||
const openSearch = (type: string, row?: object) => {
|
||||
showPersonEdit.value = true
|
||||
current.value = row
|
||||
nextTick(() => {
|
||||
stuAddRef.value?.open(type, row)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
console.log(id+"idididid")
|
||||
await SmsPersonlApi.removePerson(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
</script>
|
||||
@@ -1,6 +1,6 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<Dialog v-model="visible" :title="添加试卷任务" width="460" @open="handleOpen" center>
|
||||
<Dialog v-model="visible" :title="'添加试卷任务'" width="460" @open="handleOpen" center>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
@@ -9,9 +9,6 @@
|
||||
|
||||
>
|
||||
<div>
|
||||
<div style="margin-bottom: 16px; color: #666;">
|
||||
<el-text>当前任务 ID:<strong>{{ props.data.taskId }}</strong></el-text>
|
||||
</div>
|
||||
<!-- 步骤条 -->
|
||||
<el-steps :active="activeStep" finish-status="success" align-center>
|
||||
<template v-for="(title, index) in stepTitles" :key="index">
|
||||
@@ -47,19 +44,32 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useFormData } from '@/utils/use-form-data'
|
||||
import { ref, computed, nextTick } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useFormData } from '@/utils/use-form-data'
|
||||
import StepOne from './steps/step1/index.vue'
|
||||
import StepTwo from './steps/step2/index.vue'
|
||||
import StepThree from './steps/step3/index.vue'
|
||||
import StepFour from './steps/step4/index.vue'
|
||||
import StepFive from './steps/step5/index.vue'
|
||||
import * as SmsChannelApi from '@/api/system/session';
|
||||
// 模拟接口(你需要改成实际请求)
|
||||
async function validateBeforeStepFour(taskId) {
|
||||
// 示例:替换为你实际的 API 请求
|
||||
const res = await SmsChannelApi.fetch(taskId)
|
||||
console.log(res+"resres")
|
||||
if (res==='200') {
|
||||
return true
|
||||
} else {
|
||||
ElMessage.error(res || '无法进入考场设置步骤')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
data: Object
|
||||
})
|
||||
const isVisible = defineModel({ type: Boolean });
|
||||
|
||||
const isVisible = defineModel({ type: Boolean })
|
||||
|
||||
const isUpdate = ref(false)
|
||||
const activeStep = ref(0)
|
||||
@@ -74,38 +84,20 @@ const [form, resetFields, assignFields] = useFormData({
|
||||
taskSpecialty: ''
|
||||
})
|
||||
|
||||
function handleStepClick(index) {
|
||||
async function handleStepClick(index) {
|
||||
// 点击第四步(index=3)时先校验
|
||||
if (index === 3) {
|
||||
const pass = await validateBeforeStepFour(props.data.taskId)
|
||||
if (!pass) return
|
||||
}
|
||||
activeStep.value = index
|
||||
}
|
||||
|
||||
// function onOpen() {
|
||||
// console.log(props.data+"=======================")
|
||||
// activeStep.value = 0
|
||||
// if (props.data) {
|
||||
// assignFields(props.data)
|
||||
// isUpdate.value = true
|
||||
// } else {
|
||||
// resetFields()
|
||||
// isUpdate.value = false
|
||||
// }
|
||||
|
||||
// nextTick(() => {
|
||||
// formRef.value?.clearValidate?.()
|
||||
// })
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type, row) => {
|
||||
isVisible.value = true
|
||||
console.log(props.data+"=======================")
|
||||
assignFields(row)
|
||||
isUpdate.value = true
|
||||
|
||||
activeStep.value = 0
|
||||
|
||||
nextTick(() => {
|
||||
@@ -114,13 +106,9 @@ const open = async (type, row) => {
|
||||
}
|
||||
defineExpose({ open })
|
||||
|
||||
|
||||
|
||||
const handleCancel = () => {
|
||||
console.log(isVisible.value+"isVisible.value")
|
||||
isVisible.value = false
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -35,7 +35,7 @@
|
||||
<el-checkbox label="2">试卷管理</el-checkbox>
|
||||
<el-checkbox label="3">参数设置</el-checkbox>
|
||||
<el-checkbox label="4">考场设置</el-checkbox>
|
||||
<el-checkbox label="5">人员设置</el-checkbox>
|
||||
<!-- <el-checkbox label="5">人员设置</el-checkbox> -->
|
||||
</el-checkbox-group>
|
||||
<!-- 全选、取消全选按钮 -->
|
||||
<el-button @click="selectAll">全选</el-button>
|
||||
@@ -77,7 +77,7 @@ const queryParams = reactive({
|
||||
pageSize: 10,
|
||||
status: '0',
|
||||
isTemplate: '0',
|
||||
taskType:'1'
|
||||
taskType:'2'
|
||||
});
|
||||
|
||||
// 获取数据
|
||||
@@ -111,7 +111,7 @@ const openEdit = (action, row) => {
|
||||
|
||||
// 全选按钮的处理方法
|
||||
const selectAll = () => {
|
||||
selectedOptions.value = ['1', '2', '3', '4', '5']; // 选择所有复选框
|
||||
selectedOptions.value = ['1', '2', '3', '4']; // 选择所有复选框
|
||||
};
|
||||
|
||||
// 取消全选按钮的处理方法
|
||||
@@ -296,7 +296,11 @@ const fetchCount = async () => {
|
||||
|
||||
/** 保存 */
|
||||
const save = async () => {
|
||||
|
||||
// 判断试题数量是否小于等于可用题目数量
|
||||
if (Number(form.value.quNumbers) > Number(availableCount.value)) {
|
||||
message.warning('试题数量不能超过可用题目数量');
|
||||
return;
|
||||
}
|
||||
if (Number(availableCount.value) === 0) {
|
||||
message.warning('当前条件下没有可用的试题,请调整条件后再试');
|
||||
return;
|
||||
@@ -8,7 +8,16 @@
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
|
||||
<el-form-item label="题型" prop="spName">
|
||||
<el-select v-model="queryParams.spName" placeholder="请选择题型" clearable class="!w-240px">
|
||||
<el-option
|
||||
v-for="item in CourseOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
@@ -89,7 +98,10 @@ import * as SmsChannelApi from '@/api/system/scheme';
|
||||
|
||||
import SchemeEdit from './components/step-edit.vue';
|
||||
import SchemeSearch from './components/step-search.vue';
|
||||
|
||||
const CourseOptions = ref<string[]>([])
|
||||
import {
|
||||
getCourseList,
|
||||
} from '@/api/system/scheme'
|
||||
|
||||
defineOptions({ name: 'SystemSmsChannel' })
|
||||
|
||||
@@ -138,11 +150,18 @@ const queryParams = reactive({
|
||||
signature: undefined,
|
||||
status: undefined,
|
||||
createTime: [],
|
||||
taskId: props.taskId
|
||||
taskId: props.taskId,
|
||||
spName:undefined
|
||||
})
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
// 1. 并行拉下拉所需的选项
|
||||
const [courses] = await Promise.all([
|
||||
getCourseList(),
|
||||
|
||||
])
|
||||
CourseOptions.value = courses
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await SmsChannelApi.pageSchemes(queryParams)
|
||||
@@ -16,6 +16,16 @@
|
||||
@change="handleFormChange" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 是否启用考场设置 -->
|
||||
<el-form-item label="是否启用考场设置">
|
||||
<el-switch
|
||||
v-model="form.isSession"
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
@change="handleFormChange" />
|
||||
</el-form-item>
|
||||
<!-- 监考密码 -->
|
||||
<el-form-item label="监考密码" v-if="form.isExamPassword === '0'">
|
||||
<el-input v-model="form.examPassword" placeholder="请输入监考密码" @input="handleFormChange" />
|
||||
@@ -89,6 +99,8 @@
|
||||
<el-radio-group v-model="form.driver" @change="handleFormChange">
|
||||
<el-radio label="C">C</el-radio>
|
||||
<el-radio label="D">D</el-radio>
|
||||
<el-radio label="E">E</el-radio>
|
||||
<el-radio label="F">F</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
@submit.prevent=""
|
||||
>
|
||||
<el-form-item label="试卷ID" prop="taskId">
|
||||
<el-input clearable v-model="form.taskId" placeholder="请输入试卷ID" />
|
||||
<el-input clearable v-model="form.taskId" placeholder="请输入试卷ID" disabled/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="批次" prop="batch">
|
||||
<el-input clearable v-model="form.batch" placeholder="请输入批次" />
|
||||
<el-input clearable v-model="form.batch" placeholder="请输入批次" disabled/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="考试开始时间" prop="startTime">
|
||||
@@ -0,0 +1,264 @@
|
||||
<template>
|
||||
<Dialog
|
||||
title="人员分配"
|
||||
v-model:visible="visible"
|
||||
width="80%"
|
||||
append-to-body
|
||||
lock-scroll
|
||||
:destroy-on-close="false"
|
||||
:draggable="false"
|
||||
@close="handleDialogClose"
|
||||
>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
|
||||
<ele-page style="height: 65vh;">
|
||||
<el-row :gutter="16" style="height: 100%;">
|
||||
<!-- 左侧任务列表 -->
|
||||
<el-col :span="10" style="height: 100%;">
|
||||
<div style="height: 100%; overflow: auto;">
|
||||
<ele-card title="试卷任务列表" :body-style="{ paddingTop: '8px' }">
|
||||
|
||||
<ContentWrap style="overflow-x: auto;">
|
||||
<el-table
|
||||
table-layout="fixed"
|
||||
ref="taskTableRef"
|
||||
:data="mockTaskList"
|
||||
highlight-current-row
|
||||
@row-click="handleTaskRowClick"
|
||||
style="min-width: 1000px; width: 100%; "
|
||||
>
|
||||
<el-table-column type="index" label="#" width="50" align="center" />
|
||||
<el-table-column label="考试批次" align="center" prop="batch" />
|
||||
<el-table-column label="考试开始时间" align="center" prop="startTime" />
|
||||
<el-table-column label="考试结束时间" align="center" prop="endTime" />
|
||||
<el-table-column label="开始考试前分钟,允许入场" align="center" prop="allowEntry" />
|
||||
<el-table-column label="开始考试后分钟,禁止入场" align="center" prop="endAllowEntry" />
|
||||
<el-table-column label="开始考试后分钟,允许交卷" align="center" prop="allowSubmission" />
|
||||
</el-table>
|
||||
<Pagination
|
||||
:total="taskTotal"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
</ele-card>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧人员分配列表 -->
|
||||
<el-col :span="14" style="height: 100%;">
|
||||
<div style="height: 100%; overflow: auto;">
|
||||
<ele-card :body-style="{ paddingTop: '8px' }">
|
||||
<div class="mb-2">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="openAssignDialog()"
|
||||
>
|
||||
分配人员
|
||||
</el-button>
|
||||
<el-button type="danger" @click="removeBatch()">删除</el-button>
|
||||
<el-button @click="exportData()">导出</el-button>
|
||||
</div>
|
||||
<ContentWrap style="overflow-x: auto;">
|
||||
<el-table
|
||||
table-layout="fixed"
|
||||
ref="personTableRef"
|
||||
:data="filteredPersonList"
|
||||
style="min-width: 1000px; width: 100%"
|
||||
highlight-current-row
|
||||
@selection-change="selections = $event"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column type="index" label="#" width="50" align="center" />
|
||||
<el-table-column label="用户名称" align="center" prop="username" />
|
||||
<el-table-column label="用户昵称" align="center" prop="nickname" />
|
||||
<el-table-column label="班级名称" align="center" prop="className"/>
|
||||
<el-table-column label="分配考场" align="center" prop="batch"/>
|
||||
<!-- <el-table-column label="操作" align="center" width="180">
|
||||
<template #default="{ row }">
|
||||
<el-link type="primary" @click="openEdit(row)">修改</el-link>
|
||||
<el-divider direction="vertical" />
|
||||
<el-link type="danger" @click="removeBatch(row)">删除</el-link>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
</el-table>
|
||||
<Pagination
|
||||
:stutotal="stutotal"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<el-empty
|
||||
v-if="filteredPersonList.length === 0"
|
||||
description="暂无分配人员"
|
||||
/>
|
||||
</ele-card>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</ele-page>
|
||||
|
||||
|
||||
|
||||
<PersonSearch :session-id="selectedSessionId" :session-batch="selectedSessionBanch" :task-id="props.taskId" v-model="showPersonEdit" ref="stuAddRef" @done="reload" />
|
||||
|
||||
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick, computed } from 'vue';
|
||||
import PersonSearch from './person-serach.vue'
|
||||
|
||||
import * as SmsChannelApi from '@/api/system/session';
|
||||
import * as PersonApi from '@/api/system/person'
|
||||
const visible = ref(true);
|
||||
const selectedSessionId = ref(null);
|
||||
|
||||
const selectedSessionBanch = ref(null);
|
||||
|
||||
const selections = ref([]);
|
||||
const showEdit = ref(false);
|
||||
const current = ref(null);
|
||||
|
||||
const taskTableRef = ref();
|
||||
const personTableRef = ref();
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
status: '0',
|
||||
taskId: props.taskId,
|
||||
sessionId:''
|
||||
})
|
||||
const stuAddRef = ref();
|
||||
|
||||
const showPersonEdit = ref(false)
|
||||
const emit = defineEmits(['done'])
|
||||
const openAssignDialog = () => {
|
||||
if (!selectedSessionId.value) {
|
||||
ElMessage.warning('请先选择左侧任务!')
|
||||
return
|
||||
}
|
||||
showPersonEdit.value = true
|
||||
nextTick(() => {
|
||||
stuAddRef.value?.open();
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
const submitAssign = () => {
|
||||
// 提交逻辑
|
||||
console.log('提交分配人员')
|
||||
assignVisible.value = false
|
||||
}
|
||||
|
||||
|
||||
const mockTaskList = ref([]) ;
|
||||
|
||||
const filteredPersonList = ref([]) ;
|
||||
|
||||
|
||||
const handleTaskRowClick = async (row) => {
|
||||
selectedSessionId.value = row.sessionId;
|
||||
selectedSessionBanch.value=row.batch;
|
||||
|
||||
queryParams.sessionId = row.sessionId;
|
||||
const res = await SmsChannelApi.getSessionStu(queryParams);
|
||||
filteredPersonList.value = res.list;
|
||||
stutotal.value=res.total;
|
||||
};
|
||||
|
||||
|
||||
const openEdit = (row = null) => {
|
||||
current.value = row;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
const removeBatch = async (row) => {
|
||||
const selectedIds = selections.value.map(item => item.id);
|
||||
console.log('选中的 ID 列表:', selectedIds);
|
||||
console.log('选中的 考场 id:' + selectedSessionId.value);
|
||||
console.log('选中的 试卷任务id:', props.taskId);
|
||||
|
||||
// 弹出确认提示框
|
||||
ElMessageBox.confirm(
|
||||
'确定要删除选中的人员吗?',
|
||||
'警告',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
).then(async () => {
|
||||
// 赋值参数
|
||||
const data = {
|
||||
studentIds: selectedIds,
|
||||
sessionId: selectedSessionId.value,
|
||||
taskId: props.taskId,
|
||||
batch: selectedSessionBanch.value,
|
||||
};
|
||||
|
||||
await PersonApi.removeSessionStu(data);
|
||||
|
||||
// 删除选中的人员
|
||||
for (const sel of selections.value) {
|
||||
const index = filteredPersonList.value.findIndex(p => p.id === sel.id);
|
||||
if (index !== -1) filteredPersonList.value.splice(index, 1);
|
||||
}
|
||||
emit('done')
|
||||
ElMessage.success('删除成功');
|
||||
}).catch(() => {
|
||||
|
||||
console.log('删除操作已取消');
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const getList = async () => {
|
||||
const res = await SmsChannelApi.pageSessions(queryParams)
|
||||
mockTaskList.value = res
|
||||
total.value = res.total
|
||||
}
|
||||
const reload = () => {
|
||||
getList()
|
||||
emit('done')
|
||||
}
|
||||
|
||||
const exportData = () => {
|
||||
console.log('导出功能触发');
|
||||
};
|
||||
|
||||
const reloadPerson = () => {
|
||||
// 模拟刷新
|
||||
console.log('刷新人员列表');
|
||||
};
|
||||
|
||||
const handleDialogClose = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,331 @@
|
||||
<template>
|
||||
<!-- 搜索 -->
|
||||
<Dialog
|
||||
title="人员分配"
|
||||
v-model="visible"
|
||||
width="80%"
|
||||
append-to-body
|
||||
lock-scroll
|
||||
:destroy-on-close="false"
|
||||
:draggable="false"
|
||||
@close="handleDialogClose"
|
||||
>
|
||||
<ContentWrap>
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="学生名称" prop="username">
|
||||
<el-input
|
||||
v-model="queryParams.username"
|
||||
placeholder="请输入学生名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="班级" prop="className">
|
||||
<el-select
|
||||
v-model="queryParams.className"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
placeholder="请选择或输入"
|
||||
style="width: 300px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in classNameList"
|
||||
:key="item.className"
|
||||
:label="item.className"
|
||||
:value="item.className"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="请选择学生状态"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="datetimerange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
|
||||
<!-- <el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
>
|
||||
<Icon icon="ep:plus" /> 新增
|
||||
</el-button> -->
|
||||
<!-- <el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="handleImport"
|
||||
>
|
||||
<Icon icon="ep:upload" /> 导入
|
||||
</el-button> -->
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
>
|
||||
<Icon icon="ep:download" />导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" ref="tableRef" :data="list" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="用户编号" align="center" key="id" prop="id" />
|
||||
<el-table-column
|
||||
label="用户名称"
|
||||
align="center"
|
||||
prop="username"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column
|
||||
label="用户昵称"
|
||||
align="center"
|
||||
prop="nickname"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column
|
||||
label="班级名称"
|
||||
align="center"
|
||||
prop="className"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column label="状态" key="status" align="center">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
|
||||
disabled
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180"
|
||||
/>
|
||||
</el-table>
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="open"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleDialogClose">取消</el-button>
|
||||
<el-button type="primary" @click="confirmSelection">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
<!-- 用户导入对话框 -->
|
||||
<!-- <StudentImportForm ref="importFormRef" @success="open" /> -->
|
||||
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as ClassApi from '@/api/exam/class'
|
||||
import * as PersonApi from '@/api/system/person'
|
||||
defineOptions({ name: 'SystemUser' })
|
||||
const props = defineProps({
|
||||
sessionId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
taskId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
sessionBatch:{
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const tableRef = ref()
|
||||
|
||||
const visible = defineModel({ type: Boolean });
|
||||
|
||||
const classNameList = ref();
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
className: undefined,
|
||||
nickname: undefined,
|
||||
username: undefined,
|
||||
mobile: undefined,
|
||||
status: undefined,
|
||||
deptId: undefined,
|
||||
userType: "2",
|
||||
createTime: [],
|
||||
taskId:'',
|
||||
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
/** 查询列表 */
|
||||
const open = async () => {
|
||||
loading.value = true
|
||||
classNameList.value = await ClassApi.ClassApi.getClassName()
|
||||
queryParams.taskId=props.taskId
|
||||
|
||||
console.log(props.taskId+"props.taskId")
|
||||
console.log(props.sessionId+"props.sessionId")
|
||||
try {
|
||||
const params: any = {
|
||||
...queryParams,
|
||||
sessionId:props.sessionId,
|
||||
startTime: queryParams.createTime?.[0] || null,
|
||||
endTime: queryParams.createTime?.[1] || null,
|
||||
}
|
||||
|
||||
const data = await PersonApi.getSessionStuBySearch(params)
|
||||
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
const emit = defineEmits(['done'])
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
open()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 用户导入 */
|
||||
const importFormRef = ref()
|
||||
const handleImport = () => {
|
||||
importFormRef.value.open()
|
||||
}
|
||||
const multipleSelection = ref<UserApi.UserVO[]>([])
|
||||
|
||||
const handleSelectionChange = (val: UserApi.UserVO[]) => {
|
||||
multipleSelection.value = val
|
||||
}
|
||||
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const exportLoading = ref(false)
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await UserApi.exportUser(queryParams)
|
||||
download.excel(data, '用户数据.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleDialogClose = () => {
|
||||
visible.value = false;
|
||||
multipleSelection.value = [] // 清空选中的数据
|
||||
tableRef.value?.clearSelection()
|
||||
|
||||
};
|
||||
const confirmSelection = async () => {
|
||||
const selectedIds = multipleSelection.value.map(item => item.id)
|
||||
const count = selectedIds.length
|
||||
|
||||
if (count === 0) {
|
||||
ElMessage.warning('请先选择学生')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`已选中 ${count} 个学生,是否确认分配?`,
|
||||
'确认分配',
|
||||
{
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
|
||||
const data = {
|
||||
studentIds: selectedIds,
|
||||
sessionId: props.sessionId,
|
||||
taskId: props.taskId,
|
||||
batch:props.sessionBatch
|
||||
}
|
||||
await PersonApi.setSessionStu(data)
|
||||
|
||||
ElMessage.success(`成功分配 ${count} 个学生`)
|
||||
emit('done')
|
||||
visible.value = false
|
||||
multipleSelection.value = []
|
||||
tableRef.value?.clearSelection()
|
||||
|
||||
} catch (err) {
|
||||
// 用户取消时会进入这里,不需要处理
|
||||
console.log('用户取消了分配')
|
||||
}
|
||||
}
|
||||
defineExpose({ open })
|
||||
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
open()
|
||||
})
|
||||
</script>
|
||||
250
src/views/task/exam/components/steps/step5/index.vue
Normal file
250
src/views/task/exam/components/steps/step5/index.vue
Normal file
@@ -0,0 +1,250 @@
|
||||
<template>
|
||||
|
||||
<ContentWrap>
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
|
||||
type="primary"
|
||||
plain
|
||||
@click="openSearch('create')"
|
||||
v-hasPermi="['system:sms-channel:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 添加学生</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="showAssignButton"
|
||||
type="primary"
|
||||
plain
|
||||
@click="openEdit('create')"
|
||||
v-hasPermi="['system:sms-channel:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 分配场次</el-button
|
||||
>
|
||||
<el-button
|
||||
type="danger"
|
||||
class="ele-btn-del"
|
||||
:disabled="!selections.length"
|
||||
@click="handleDeletes()"
|
||||
>
|
||||
批量删除
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="用户名称" align="center" prop="username" />
|
||||
<el-table-column label="用户昵称" align="center" prop="nickname" />
|
||||
<el-table-column label="班级名称" align="center" prop="className"/>
|
||||
<el-table-column label="分配考场" align="center" prop="batch"/>
|
||||
|
||||
<!-- <el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openEdit('update', scope.row)"
|
||||
v-hasPermi="['system:sms-channel:update']"
|
||||
>
|
||||
查看
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.schemeId)"
|
||||
v-hasPermi="['system:sms-channel:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- 编辑弹窗 -->
|
||||
<person-edit v-model="showEdit" :task-Id="props.taskId" @done="reload" />
|
||||
<PersonSearch :task-id="props.taskId" v-model="showPersonEdit" ref="stuAddRef" @done="reload" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
|
||||
import * as SmsPersonlApi from '@/api/system/person';
|
||||
import PersonEdit from './components/person-edit.vue';
|
||||
import PersonSearch from './components/person-serach.vue'
|
||||
import * as SmsChannelApi from '@/api/system/session';
|
||||
import { fa } from 'element-plus/es/locale';
|
||||
|
||||
defineOptions({ name: 'SystemSmsChannel' })
|
||||
const showAssignButton = ref(true) // 控制按钮显示
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps({
|
||||
taskSpecialty: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
taskId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
formData:{
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
})
|
||||
const showPersonEdit = ref(false)
|
||||
/** 当前编辑数据 */
|
||||
const current = ref<object>();
|
||||
|
||||
/** 是否显示编辑弹窗 */
|
||||
const showEdit = ref(false);
|
||||
|
||||
const stuAddRef = ref();
|
||||
|
||||
|
||||
|
||||
const taskEditRef = ref()
|
||||
|
||||
|
||||
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
signature: undefined,
|
||||
status: undefined,
|
||||
createTime: [],
|
||||
taskId: props.taskId
|
||||
})
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const resButton = await SmsChannelApi.fetchNoMes(props.taskId)
|
||||
|
||||
if (resButton==='200') {
|
||||
showAssignButton.value = true
|
||||
} else {
|
||||
showAssignButton.value = false
|
||||
}
|
||||
|
||||
|
||||
|
||||
const res = await SmsPersonlApi.pagePersons(queryParams)
|
||||
console.log(res)
|
||||
list.value = res.list
|
||||
total.value = res.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
const reload = () => {
|
||||
getList()
|
||||
}
|
||||
/** 表格选中数据 */
|
||||
const selections = ref([]);
|
||||
const handleSelectionChange = (rows) => {
|
||||
selections.value = rows;
|
||||
}
|
||||
|
||||
|
||||
const selectedRows = ref<string[]>([]);
|
||||
|
||||
const handleDeletes = async () => {
|
||||
try {
|
||||
const rows = selections.value;
|
||||
if (!rows.length) {
|
||||
message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
|
||||
selectedRows.value = rows.map((d: any) => d.id); // 保存选中的行数据
|
||||
|
||||
const deleteData = {
|
||||
studentIds: selectedRows.value, // 选中的 ID 列表
|
||||
taskId: props.taskId // 任务 ID
|
||||
}
|
||||
await SmsPersonlApi.removePersons(deleteData)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
|
||||
const openEdit = (type: string, row?: object) => {
|
||||
showEdit.value = true
|
||||
current.value = row
|
||||
nextTick(() => {
|
||||
taskEditRef.value?.open(type, row)
|
||||
})
|
||||
}
|
||||
const openSearch = (type: string, row?: object) => {
|
||||
showPersonEdit.value = true
|
||||
current.value = row
|
||||
nextTick(() => {
|
||||
stuAddRef.value?.open(type, row)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
console.log(id+"idididid")
|
||||
await SmsPersonlApi.removePerson(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
</script>
|
||||
@@ -1,6 +1,6 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<Dialog v-model="visible" :title="添加试卷任务" width="460" @open="handleOpen" center>
|
||||
<Dialog v-model="visible" :title="'添加试卷任务'" width="460" @open="handleOpen" center>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
@@ -9,9 +9,6 @@
|
||||
|
||||
>
|
||||
<div>
|
||||
<div style="margin-bottom: 16px; color: #666;">
|
||||
<el-text>当前任务 ID:<strong>{{ props.data.taskId }}</strong></el-text>
|
||||
</div>
|
||||
<!-- 步骤条 -->
|
||||
<el-steps :active="activeStep" finish-status="success" align-center>
|
||||
<template v-for="(title, index) in stepTitles" :key="index">
|
||||
@@ -47,19 +44,32 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useFormData } from '@/utils/use-form-data'
|
||||
import { ref, computed, nextTick } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useFormData } from '@/utils/use-form-data'
|
||||
import StepOne from './steps/step1/index.vue'
|
||||
import StepTwo from './steps/step2/index.vue'
|
||||
import StepThree from './steps/step3/index.vue'
|
||||
import StepFour from './steps/step4/index.vue'
|
||||
import StepFive from './steps/step5/index.vue'
|
||||
import * as SmsChannelApi from '@/api/system/session';
|
||||
// 模拟接口(你需要改成实际请求)
|
||||
async function validateBeforeStepFour(taskId) {
|
||||
// 示例:替换为你实际的 API 请求
|
||||
const res = await SmsChannelApi.fetch(taskId)
|
||||
console.log(res+"resres")
|
||||
if (res==='200') {
|
||||
return true
|
||||
} else {
|
||||
ElMessage.error(res || '无法进入考场设置步骤')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
data: Object
|
||||
})
|
||||
const isVisible = defineModel({ type: Boolean });
|
||||
|
||||
const isVisible = defineModel({ type: Boolean })
|
||||
|
||||
const isUpdate = ref(false)
|
||||
const activeStep = ref(0)
|
||||
@@ -74,38 +84,20 @@ const [form, resetFields, assignFields] = useFormData({
|
||||
taskSpecialty: ''
|
||||
})
|
||||
|
||||
function handleStepClick(index) {
|
||||
async function handleStepClick(index) {
|
||||
// 点击第四步(index=3)时先校验
|
||||
if (index === 3) {
|
||||
const pass = await validateBeforeStepFour(props.data.taskId)
|
||||
if (!pass) return
|
||||
}
|
||||
activeStep.value = index
|
||||
}
|
||||
|
||||
// function onOpen() {
|
||||
// console.log(props.data+"=======================")
|
||||
// activeStep.value = 0
|
||||
// if (props.data) {
|
||||
// assignFields(props.data)
|
||||
// isUpdate.value = true
|
||||
// } else {
|
||||
// resetFields()
|
||||
// isUpdate.value = false
|
||||
// }
|
||||
|
||||
// nextTick(() => {
|
||||
// formRef.value?.clearValidate?.()
|
||||
// })
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type, row) => {
|
||||
isVisible.value = true
|
||||
console.log(props.data+"=======================")
|
||||
assignFields(row)
|
||||
isUpdate.value = true
|
||||
|
||||
activeStep.value = 0
|
||||
|
||||
nextTick(() => {
|
||||
@@ -114,13 +106,9 @@ const open = async (type, row) => {
|
||||
}
|
||||
defineExpose({ open })
|
||||
|
||||
|
||||
|
||||
const handleCancel = () => {
|
||||
console.log(isVisible.value+"isVisible.value")
|
||||
isVisible.value = false
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -35,7 +35,7 @@
|
||||
<el-checkbox label="2">试卷管理</el-checkbox>
|
||||
<el-checkbox label="3">参数设置</el-checkbox>
|
||||
<el-checkbox label="4">考场设置</el-checkbox>
|
||||
<el-checkbox label="5">人员设置</el-checkbox>
|
||||
<!-- <el-checkbox label="5">人员设置</el-checkbox> -->
|
||||
</el-checkbox-group>
|
||||
<!-- 全选、取消全选按钮 -->
|
||||
<el-button @click="selectAll">全选</el-button>
|
||||
@@ -111,7 +111,7 @@ const openEdit = (action, row) => {
|
||||
|
||||
// 全选按钮的处理方法
|
||||
const selectAll = () => {
|
||||
selectedOptions.value = ['1', '2', '3', '4', '5']; // 选择所有复选框
|
||||
selectedOptions.value = ['1', '2', '3', '4']; // 选择所有复选框
|
||||
};
|
||||
|
||||
// 取消全选按钮的处理方法
|
||||
@@ -296,7 +296,11 @@ const fetchCount = async () => {
|
||||
|
||||
/** 保存 */
|
||||
const save = async () => {
|
||||
|
||||
// 判断试题数量是否小于等于可用题目数量
|
||||
if (Number(form.value.quNumbers) > Number(availableCount.value)) {
|
||||
message.warning('试题数量不能超过可用题目数量');
|
||||
return;
|
||||
}
|
||||
if (Number(availableCount.value) === 0) {
|
||||
message.warning('当前条件下没有可用的试题,请调整条件后再试');
|
||||
return;
|
||||
@@ -9,6 +9,16 @@
|
||||
label-width="68px"
|
||||
>
|
||||
|
||||
<el-form-item label="题型" prop="spName">
|
||||
<el-select v-model="queryParams.spName" placeholder="请选择题型" clearable class="!w-240px">
|
||||
<el-option
|
||||
v-for="item in CourseOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
@@ -27,7 +37,7 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="方案ID" align="center" prop="taskId" />
|
||||
<el-table-column label="方案ID" align="center" prop="schemeId" />
|
||||
<el-table-column label="题型" align="center" prop="spName" />
|
||||
<el-table-column label="难度" align="center" prop="quLevel">
|
||||
<template #default="scope">
|
||||
@@ -83,14 +93,14 @@
|
||||
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
|
||||
import * as SmsChannelApi from '@/api/system/scheme';
|
||||
|
||||
import SchemeEdit from './components/step-edit.vue';
|
||||
import SchemeSearch from './components/step-search.vue';
|
||||
|
||||
|
||||
const CourseOptions = ref<string[]>([])
|
||||
import {
|
||||
getCourseList,
|
||||
} from '@/api/system/scheme'
|
||||
defineOptions({ name: 'SystemSmsChannel' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
@@ -138,11 +148,18 @@ const queryParams = reactive({
|
||||
signature: undefined,
|
||||
status: undefined,
|
||||
createTime: [],
|
||||
taskId: props.taskId
|
||||
taskId: props.taskId ,
|
||||
spName:undefined
|
||||
})
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
// 1. 并行拉下拉所需的选项
|
||||
const [courses] = await Promise.all([
|
||||
getCourseList(),
|
||||
|
||||
])
|
||||
CourseOptions.value = courses
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await SmsChannelApi.pageSchemes(queryParams)
|
||||
@@ -198,6 +215,7 @@ console.log(id+"idididid")
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
|
||||
getList()
|
||||
})
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<!-- 第一个 tab - 通用参数 -->
|
||||
<el-tab-pane label="通用参数" name="tab1">
|
||||
<el-form :model="form" label-width="200px" style="margin-top: 20px;">
|
||||
|
||||
<!-- 是否使用监考密码验证 -->
|
||||
<el-form-item label="是否使用监考密码验证">
|
||||
<el-switch
|
||||
@@ -16,6 +17,15 @@
|
||||
@change="handleFormChange" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="是否启用考场设置">
|
||||
<el-switch
|
||||
v-model="form.isSession"
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
@change="handleFormChange" />
|
||||
</el-form-item>
|
||||
<!-- 监考密码 -->
|
||||
<el-form-item label="监考密码" v-if="form.isExamPassword === '0'">
|
||||
<el-input v-model="form.examPassword" placeholder="请输入监考密码" @input="handleFormChange" />
|
||||
@@ -89,6 +99,8 @@
|
||||
<el-radio-group v-model="form.driver" @change="handleFormChange">
|
||||
<el-radio label="C">C</el-radio>
|
||||
<el-radio label="D">D</el-radio>
|
||||
<el-radio label="E">E</el-radio>
|
||||
<el-radio label="F">F</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
@submit.prevent=""
|
||||
>
|
||||
<el-form-item label="试卷ID" prop="taskId">
|
||||
<el-input clearable v-model="form.taskId" placeholder="请输入试卷ID" />
|
||||
<el-input clearable v-model="form.taskId" placeholder="请输入试卷ID" disabled/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="批次" prop="batch">
|
||||
<el-input clearable v-model="form.batch" placeholder="请输入批次" />
|
||||
<el-input clearable v-model="form.batch" placeholder="请输入批次" disabled/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="考试开始时间" prop="startTime">
|
||||
@@ -0,0 +1,264 @@
|
||||
<template>
|
||||
<Dialog
|
||||
title="人员分配"
|
||||
v-model:visible="visible"
|
||||
width="80%"
|
||||
append-to-body
|
||||
lock-scroll
|
||||
:destroy-on-close="false"
|
||||
:draggable="false"
|
||||
@close="handleDialogClose"
|
||||
>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
|
||||
<ele-page style="height: 65vh;">
|
||||
<el-row :gutter="16" style="height: 100%;">
|
||||
<!-- 左侧任务列表 -->
|
||||
<el-col :span="10" style="height: 100%;">
|
||||
<div style="height: 100%; overflow: auto;">
|
||||
<ele-card title="试卷任务列表" :body-style="{ paddingTop: '8px' }">
|
||||
|
||||
<ContentWrap style="overflow-x: auto;">
|
||||
<el-table
|
||||
table-layout="fixed"
|
||||
ref="taskTableRef"
|
||||
:data="mockTaskList"
|
||||
highlight-current-row
|
||||
@row-click="handleTaskRowClick"
|
||||
style="min-width: 1000px; width: 100%; "
|
||||
>
|
||||
<el-table-column type="index" label="#" width="50" align="center" />
|
||||
<el-table-column label="考试批次" align="center" prop="batch" />
|
||||
<el-table-column label="考试开始时间" align="center" prop="startTime" />
|
||||
<el-table-column label="考试结束时间" align="center" prop="endTime" />
|
||||
<el-table-column label="开始考试前分钟,允许入场" align="center" prop="allowEntry" />
|
||||
<el-table-column label="开始考试后分钟,禁止入场" align="center" prop="endAllowEntry" />
|
||||
<el-table-column label="开始考试后分钟,允许交卷" align="center" prop="allowSubmission" />
|
||||
</el-table>
|
||||
<Pagination
|
||||
:total="taskTotal"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
</ele-card>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧人员分配列表 -->
|
||||
<el-col :span="14" style="height: 100%;">
|
||||
<div style="height: 100%; overflow: auto;">
|
||||
<ele-card :body-style="{ paddingTop: '8px' }">
|
||||
<div class="mb-2">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="openAssignDialog()"
|
||||
>
|
||||
分配人员
|
||||
</el-button>
|
||||
<el-button type="danger" @click="removeBatch()">删除</el-button>
|
||||
<el-button @click="exportData()">导出</el-button>
|
||||
</div>
|
||||
<ContentWrap style="overflow-x: auto;">
|
||||
<el-table
|
||||
table-layout="fixed"
|
||||
ref="personTableRef"
|
||||
:data="filteredPersonList"
|
||||
style="min-width: 1000px; width: 100%"
|
||||
highlight-current-row
|
||||
@selection-change="selections = $event"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column type="index" label="#" width="50" align="center" />
|
||||
<el-table-column label="用户名称" align="center" prop="username" />
|
||||
<el-table-column label="用户昵称" align="center" prop="nickname" />
|
||||
<el-table-column label="班级名称" align="center" prop="className"/>
|
||||
<el-table-column label="分配考场" align="center" prop="batch"/>
|
||||
<!-- <el-table-column label="操作" align="center" width="180">
|
||||
<template #default="{ row }">
|
||||
<el-link type="primary" @click="openEdit(row)">修改</el-link>
|
||||
<el-divider direction="vertical" />
|
||||
<el-link type="danger" @click="removeBatch(row)">删除</el-link>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
</el-table>
|
||||
<Pagination
|
||||
:stutotal="stutotal"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<el-empty
|
||||
v-if="filteredPersonList.length === 0"
|
||||
description="暂无分配人员"
|
||||
/>
|
||||
</ele-card>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</ele-page>
|
||||
|
||||
|
||||
|
||||
<PersonSearch :session-id="selectedSessionId" :session-batch="selectedSessionBanch" :task-id="props.taskId" v-model="showPersonEdit" ref="stuAddRef" @done="reload" />
|
||||
|
||||
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick, computed } from 'vue';
|
||||
import PersonSearch from './person-serach.vue'
|
||||
|
||||
import * as SmsChannelApi from '@/api/system/session';
|
||||
import * as PersonApi from '@/api/system/person'
|
||||
const visible = ref(true);
|
||||
const selectedSessionId = ref(null);
|
||||
|
||||
const selectedSessionBanch = ref(null);
|
||||
|
||||
const selections = ref([]);
|
||||
const showEdit = ref(false);
|
||||
const current = ref(null);
|
||||
|
||||
const taskTableRef = ref();
|
||||
const personTableRef = ref();
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
status: '0',
|
||||
taskId: props.taskId,
|
||||
sessionId:''
|
||||
})
|
||||
const stuAddRef = ref();
|
||||
|
||||
const showPersonEdit = ref(false)
|
||||
const emit = defineEmits(['done'])
|
||||
const openAssignDialog = () => {
|
||||
if (!selectedSessionId.value) {
|
||||
ElMessage.warning('请先选择左侧任务!')
|
||||
return
|
||||
}
|
||||
showPersonEdit.value = true
|
||||
nextTick(() => {
|
||||
stuAddRef.value?.open();
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
const submitAssign = () => {
|
||||
// 提交逻辑
|
||||
console.log('提交分配人员')
|
||||
assignVisible.value = false
|
||||
}
|
||||
|
||||
|
||||
const mockTaskList = ref([]) ;
|
||||
|
||||
const filteredPersonList = ref([]) ;
|
||||
|
||||
|
||||
const handleTaskRowClick = async (row) => {
|
||||
selectedSessionId.value = row.sessionId;
|
||||
selectedSessionBanch.value=row.batch;
|
||||
|
||||
queryParams.sessionId = row.sessionId;
|
||||
const res = await SmsChannelApi.getSessionStu(queryParams);
|
||||
filteredPersonList.value = res.list;
|
||||
stutotal.value=res.total;
|
||||
};
|
||||
|
||||
|
||||
const openEdit = (row = null) => {
|
||||
current.value = row;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
const removeBatch = async (row) => {
|
||||
const selectedIds = selections.value.map(item => item.id);
|
||||
console.log('选中的 ID 列表:', selectedIds);
|
||||
console.log('选中的 考场 id:' + selectedSessionId.value);
|
||||
console.log('选中的 试卷任务id:', props.taskId);
|
||||
|
||||
// 弹出确认提示框
|
||||
ElMessageBox.confirm(
|
||||
'确定要删除选中的人员吗?',
|
||||
'警告',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
).then(async () => {
|
||||
// 赋值参数
|
||||
const data = {
|
||||
studentIds: selectedIds,
|
||||
sessionId: selectedSessionId.value,
|
||||
taskId: props.taskId,
|
||||
batch: selectedSessionBanch.value,
|
||||
};
|
||||
|
||||
await PersonApi.removeSessionStu(data);
|
||||
|
||||
// 删除选中的人员
|
||||
for (const sel of selections.value) {
|
||||
const index = filteredPersonList.value.findIndex(p => p.id === sel.id);
|
||||
if (index !== -1) filteredPersonList.value.splice(index, 1);
|
||||
}
|
||||
emit('done')
|
||||
ElMessage.success('删除成功');
|
||||
}).catch(() => {
|
||||
|
||||
console.log('删除操作已取消');
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const getList = async () => {
|
||||
const res = await SmsChannelApi.pageSessions(queryParams)
|
||||
mockTaskList.value = res
|
||||
total.value = res.total
|
||||
}
|
||||
const reload = () => {
|
||||
getList()
|
||||
emit('done')
|
||||
}
|
||||
|
||||
const exportData = () => {
|
||||
console.log('导出功能触发');
|
||||
};
|
||||
|
||||
const reloadPerson = () => {
|
||||
// 模拟刷新
|
||||
console.log('刷新人员列表');
|
||||
};
|
||||
|
||||
const handleDialogClose = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,331 @@
|
||||
<template>
|
||||
<!-- 搜索 -->
|
||||
<Dialog
|
||||
title="人员分配"
|
||||
v-model="visible"
|
||||
width="80%"
|
||||
append-to-body
|
||||
lock-scroll
|
||||
:destroy-on-close="false"
|
||||
:draggable="false"
|
||||
@close="handleDialogClose"
|
||||
>
|
||||
<ContentWrap>
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="学生名称" prop="username">
|
||||
<el-input
|
||||
v-model="queryParams.username"
|
||||
placeholder="请输入学生名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="班级" prop="className">
|
||||
<el-select
|
||||
v-model="queryParams.className"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
placeholder="请选择或输入"
|
||||
style="width: 300px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in classNameList"
|
||||
:key="item.className"
|
||||
:label="item.className"
|
||||
:value="item.className"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="请选择学生状态"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="datetimerange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
|
||||
<!-- <el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
>
|
||||
<Icon icon="ep:plus" /> 新增
|
||||
</el-button> -->
|
||||
<!-- <el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="handleImport"
|
||||
>
|
||||
<Icon icon="ep:upload" /> 导入
|
||||
</el-button> -->
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
>
|
||||
<Icon icon="ep:download" />导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" ref="tableRef" :data="list" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="用户编号" align="center" key="id" prop="id" />
|
||||
<el-table-column
|
||||
label="用户名称"
|
||||
align="center"
|
||||
prop="username"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column
|
||||
label="用户昵称"
|
||||
align="center"
|
||||
prop="nickname"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column
|
||||
label="班级名称"
|
||||
align="center"
|
||||
prop="className"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column label="状态" key="status" align="center">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
|
||||
disabled
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180"
|
||||
/>
|
||||
</el-table>
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="open"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleDialogClose">取消</el-button>
|
||||
<el-button type="primary" @click="confirmSelection">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
<!-- 用户导入对话框 -->
|
||||
<!-- <StudentImportForm ref="importFormRef" @success="open" /> -->
|
||||
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as ClassApi from '@/api/exam/class'
|
||||
import * as PersonApi from '@/api/system/person'
|
||||
defineOptions({ name: 'SystemUser' })
|
||||
const props = defineProps({
|
||||
sessionId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
taskId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
sessionBatch:{
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const tableRef = ref()
|
||||
|
||||
const visible = defineModel({ type: Boolean });
|
||||
|
||||
const classNameList = ref();
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
className: undefined,
|
||||
nickname: undefined,
|
||||
username: undefined,
|
||||
mobile: undefined,
|
||||
status: undefined,
|
||||
deptId: undefined,
|
||||
userType: "2",
|
||||
createTime: [],
|
||||
taskId:'',
|
||||
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
/** 查询列表 */
|
||||
const open = async () => {
|
||||
loading.value = true
|
||||
classNameList.value = await ClassApi.ClassApi.getClassName()
|
||||
queryParams.taskId=props.taskId
|
||||
|
||||
console.log(props.taskId+"props.taskId")
|
||||
console.log(props.sessionId+"props.sessionId")
|
||||
try {
|
||||
const params: any = {
|
||||
...queryParams,
|
||||
sessionId:props.sessionId,
|
||||
startTime: queryParams.createTime?.[0] || null,
|
||||
endTime: queryParams.createTime?.[1] || null,
|
||||
}
|
||||
|
||||
const data = await PersonApi.getSessionStuBySearch(params)
|
||||
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
const emit = defineEmits(['done'])
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
open()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 用户导入 */
|
||||
const importFormRef = ref()
|
||||
const handleImport = () => {
|
||||
importFormRef.value.open()
|
||||
}
|
||||
const multipleSelection = ref<UserApi.UserVO[]>([])
|
||||
|
||||
const handleSelectionChange = (val: UserApi.UserVO[]) => {
|
||||
multipleSelection.value = val
|
||||
}
|
||||
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const exportLoading = ref(false)
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await UserApi.exportUser(queryParams)
|
||||
download.excel(data, '用户数据.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleDialogClose = () => {
|
||||
visible.value = false;
|
||||
multipleSelection.value = [] // 清空选中的数据
|
||||
tableRef.value?.clearSelection()
|
||||
|
||||
};
|
||||
const confirmSelection = async () => {
|
||||
const selectedIds = multipleSelection.value.map(item => item.id)
|
||||
const count = selectedIds.length
|
||||
|
||||
if (count === 0) {
|
||||
ElMessage.warning('请先选择学生')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`已选中 ${count} 个学生,是否确认分配?`,
|
||||
'确认分配',
|
||||
{
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
|
||||
const data = {
|
||||
studentIds: selectedIds,
|
||||
sessionId: props.sessionId,
|
||||
taskId: props.taskId,
|
||||
batch:props.sessionBatch
|
||||
}
|
||||
await PersonApi.setSessionStu(data)
|
||||
|
||||
ElMessage.success(`成功分配 ${count} 个学生`)
|
||||
emit('done')
|
||||
visible.value = false
|
||||
multipleSelection.value = []
|
||||
tableRef.value?.clearSelection()
|
||||
|
||||
} catch (err) {
|
||||
// 用户取消时会进入这里,不需要处理
|
||||
console.log('用户取消了分配')
|
||||
}
|
||||
}
|
||||
defineExpose({ open })
|
||||
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
open()
|
||||
})
|
||||
</script>
|
||||
250
src/views/task/trans/components/steps/step5/index.vue
Normal file
250
src/views/task/trans/components/steps/step5/index.vue
Normal file
@@ -0,0 +1,250 @@
|
||||
<template>
|
||||
|
||||
<ContentWrap>
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
<el-button
|
||||
|
||||
type="primary"
|
||||
plain
|
||||
@click="openSearch('create')"
|
||||
v-hasPermi="['system:sms-channel:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 添加学生</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="showAssignButton"
|
||||
type="primary"
|
||||
plain
|
||||
@click="openEdit('create')"
|
||||
v-hasPermi="['system:sms-channel:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 分配场次</el-button
|
||||
>
|
||||
<el-button
|
||||
type="danger"
|
||||
class="ele-btn-del"
|
||||
:disabled="!selections.length"
|
||||
@click="handleDeletes()"
|
||||
>
|
||||
批量删除
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="用户名称" align="center" prop="username" />
|
||||
<el-table-column label="用户昵称" align="center" prop="nickname" />
|
||||
<el-table-column label="班级名称" align="center" prop="className"/>
|
||||
<el-table-column label="分配考场" align="center" prop="batch"/>
|
||||
|
||||
<!-- <el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openEdit('update', scope.row)"
|
||||
v-hasPermi="['system:sms-channel:update']"
|
||||
>
|
||||
查看
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row.schemeId)"
|
||||
v-hasPermi="['system:sms-channel:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- 编辑弹窗 -->
|
||||
<person-edit v-model="showEdit" :task-Id="props.taskId" @done="reload" />
|
||||
<PersonSearch :task-id="props.taskId" v-model="showPersonEdit" ref="stuAddRef" @done="reload" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
|
||||
import * as SmsPersonlApi from '@/api/system/person';
|
||||
import PersonEdit from './components/person-edit.vue';
|
||||
import PersonSearch from './components/person-serach.vue'
|
||||
import * as SmsChannelApi from '@/api/system/session';
|
||||
import { fa } from 'element-plus/es/locale';
|
||||
|
||||
defineOptions({ name: 'SystemSmsChannel' })
|
||||
const showAssignButton = ref(true) // 控制按钮显示
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps({
|
||||
taskSpecialty: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
taskId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
formData:{
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
})
|
||||
const showPersonEdit = ref(false)
|
||||
/** 当前编辑数据 */
|
||||
const current = ref<object>();
|
||||
|
||||
/** 是否显示编辑弹窗 */
|
||||
const showEdit = ref(false);
|
||||
|
||||
const stuAddRef = ref();
|
||||
|
||||
|
||||
|
||||
const taskEditRef = ref()
|
||||
|
||||
|
||||
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
signature: undefined,
|
||||
status: undefined,
|
||||
createTime: [],
|
||||
taskId: props.taskId
|
||||
})
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const resButton = await SmsChannelApi.fetchNoMes(props.taskId)
|
||||
|
||||
if (resButton==='200') {
|
||||
showAssignButton.value = true
|
||||
} else {
|
||||
showAssignButton.value = false
|
||||
}
|
||||
|
||||
|
||||
|
||||
const res = await SmsPersonlApi.pagePersons(queryParams)
|
||||
console.log(res)
|
||||
list.value = res.list
|
||||
total.value = res.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
const reload = () => {
|
||||
getList()
|
||||
}
|
||||
/** 表格选中数据 */
|
||||
const selections = ref([]);
|
||||
const handleSelectionChange = (rows) => {
|
||||
selections.value = rows;
|
||||
}
|
||||
|
||||
|
||||
const selectedRows = ref<string[]>([]);
|
||||
|
||||
const handleDeletes = async () => {
|
||||
try {
|
||||
const rows = selections.value;
|
||||
if (!rows.length) {
|
||||
message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
|
||||
selectedRows.value = rows.map((d: any) => d.id); // 保存选中的行数据
|
||||
|
||||
const deleteData = {
|
||||
studentIds: selectedRows.value, // 选中的 ID 列表
|
||||
taskId: props.taskId // 任务 ID
|
||||
}
|
||||
await SmsPersonlApi.removePersons(deleteData)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
|
||||
const openEdit = (type: string, row?: object) => {
|
||||
showEdit.value = true
|
||||
current.value = row
|
||||
nextTick(() => {
|
||||
taskEditRef.value?.open(type, row)
|
||||
})
|
||||
}
|
||||
const openSearch = (type: string, row?: object) => {
|
||||
showPersonEdit.value = true
|
||||
current.value = row
|
||||
nextTick(() => {
|
||||
stuAddRef.value?.open(type, row)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
console.log(id+"idididid")
|
||||
await SmsPersonlApi.removePerson(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
</script>
|
||||
@@ -1,6 +1,6 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<Dialog v-model="visible" :title="添加试卷任务" width="460" @open="handleOpen" center>
|
||||
<Dialog v-model="visible" :title="'添加试卷任务'" width="460" @open="handleOpen" center>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
@@ -9,9 +9,6 @@
|
||||
|
||||
>
|
||||
<div>
|
||||
<div style="margin-bottom: 16px; color: #666;">
|
||||
<el-text>当前任务 ID:<strong>{{ props.data.taskId }}</strong></el-text>
|
||||
</div>
|
||||
<!-- 步骤条 -->
|
||||
<el-steps :active="activeStep" finish-status="success" align-center>
|
||||
<template v-for="(title, index) in stepTitles" :key="index">
|
||||
@@ -47,19 +44,32 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useFormData } from '@/utils/use-form-data'
|
||||
import { ref, computed, nextTick } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useFormData } from '@/utils/use-form-data'
|
||||
import StepOne from './steps/step1/index.vue'
|
||||
import StepTwo from './steps/step2/index.vue'
|
||||
import StepThree from './steps/step3/index.vue'
|
||||
import StepFour from './steps/step4/index.vue'
|
||||
import StepFive from './steps/step5/index.vue'
|
||||
import * as SmsChannelApi from '@/api/system/session';
|
||||
// 模拟接口(你需要改成实际请求)
|
||||
async function validateBeforeStepFour(taskId) {
|
||||
// 示例:替换为你实际的 API 请求
|
||||
const res = await SmsChannelApi.fetch(taskId)
|
||||
|
||||
if (res==='200') {
|
||||
return true
|
||||
} else {
|
||||
ElMessage.error(res || '无法进入考场设置步骤')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
data: Object
|
||||
})
|
||||
const isVisible = defineModel({ type: Boolean });
|
||||
|
||||
const isVisible = defineModel({ type: Boolean })
|
||||
|
||||
const isUpdate = ref(false)
|
||||
const activeStep = ref(0)
|
||||
@@ -74,38 +84,20 @@ const [form, resetFields, assignFields] = useFormData({
|
||||
taskSpecialty: ''
|
||||
})
|
||||
|
||||
function handleStepClick(index) {
|
||||
async function handleStepClick(index) {
|
||||
// 点击第四步(index=3)时先校验
|
||||
if (index === 3) {
|
||||
const pass = await validateBeforeStepFour(props.data.taskId)
|
||||
if (!pass) return
|
||||
}
|
||||
activeStep.value = index
|
||||
}
|
||||
|
||||
// function onOpen() {
|
||||
// console.log(props.data+"=======================")
|
||||
// activeStep.value = 0
|
||||
// if (props.data) {
|
||||
// assignFields(props.data)
|
||||
// isUpdate.value = true
|
||||
// } else {
|
||||
// resetFields()
|
||||
// isUpdate.value = false
|
||||
// }
|
||||
|
||||
// nextTick(() => {
|
||||
// formRef.value?.clearValidate?.()
|
||||
// })
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type, row) => {
|
||||
isVisible.value = true
|
||||
console.log(props.data+"=======================")
|
||||
assignFields(row)
|
||||
isUpdate.value = true
|
||||
|
||||
activeStep.value = 0
|
||||
|
||||
nextTick(() => {
|
||||
@@ -114,13 +106,9 @@ const open = async (type, row) => {
|
||||
}
|
||||
defineExpose({ open })
|
||||
|
||||
|
||||
|
||||
const handleCancel = () => {
|
||||
console.log(isVisible.value+"isVisible.value")
|
||||
isVisible.value = false
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -111,7 +111,7 @@ const openEdit = (action, row) => {
|
||||
|
||||
// 全选按钮的处理方法
|
||||
const selectAll = () => {
|
||||
selectedOptions.value = ['1', '2', '3', '4', '5']; // 选择所有复选框
|
||||
selectedOptions.value = ['1', '2', '3', '4']; // 选择所有复选框
|
||||
};
|
||||
|
||||
// 取消全选按钮的处理方法
|
||||
Reference in New Issue
Block a user