Compare commits

..

236 Commits

Author SHA1 Message Date
qinqing 5df96fe26c Merge branch 'main' into qinqing_dev 2024-12-05 16:10:50 +08:00
朱浩 7b5988c979 Merge pull request 'zhuhao_dev' (#424) from zhuhao_dev into main
Reviewed-on: #424
2024-12-05 15:05:20 +08:00
朱浩 cabced383a 永川登录页面新增 2024-12-05 15:03:17 +08:00
朱浩 d400911bfc 永川登录页面新增 2024-12-05 14:07:48 +08:00
朱浩 66ff0ae6fd 永川登录页面新增 2024-12-05 14:07:12 +08:00
朱浩 5e2a2d90fc 永川登录页面新增 2024-12-05 10:51:39 +08:00
lyc 8bd1eec801 Merge pull request 'edit 章节' (#423) from lyc-dev into main 2024-12-04 15:08:27 +08:00
lyc 875cccae79 edit 章节 2024-12-04 15:07:52 +08:00
lyc cc39253a79 Merge pull request '修复章节选择问题' (#422) from lyc-dev into main 2024-12-02 16:26:47 +08:00
lyc 865574ec4c 修复章节选择问题 2024-12-02 16:26:19 +08:00
yangws 532e724351 Merge pull request 'fix:加个平均时长判断;' (#421) from yangws into main
Reviewed-on: #421
2024-11-26 16:57:27 +08:00
小杨 ead9ebd6eb fix:加个平均时长判断; 2024-11-26 16:57:03 +08:00
yangws a147c60d78 Merge pull request 'fix:推送分值丢失问题;' (#420) from yangws into main
Reviewed-on: #420
2024-11-26 14:56:06 +08:00
小杨 a0477a18df fix:推送分值丢失问题; 2024-11-26 14:55:02 +08:00
yangws f3f3ada1ed Merge pull request 'fix:放大饼图;' (#419) from yangws into main
Reviewed-on: #419
2024-11-25 16:02:47 +08:00
小杨 5ad37f60f0 fix:放大饼图; 2024-11-25 16:02:13 +08:00
yangws 2cecbc14fd Merge pull request 'yangws' (#418) from yangws into main
Reviewed-on: #418
2024-11-22 09:27:44 +08:00
小杨 8d8ff06c5e Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into yangws 2024-11-22 09:27:13 +08:00
小杨 0a3ab2eff5 fix:固定学科; 2024-11-22 09:27:02 +08:00
yangws 21a4f4cfac Merge pull request 'fix:实验室样式修改;' (#417) from yangws into main
Reviewed-on: #417
2024-11-21 17:42:24 +08:00
小杨 16f696bd4f fix:实验室样式修改; 2024-11-21 17:41:54 +08:00
朱浩 19494eb9e7 Merge pull request 'zhuhao_dev' (#416) from zhuhao_dev into main
Reviewed-on: #416
2024-11-21 17:40:04 +08:00
朱浩 4820405ca4 Merge branch 'main' into zhuhao_dev 2024-11-21 17:20:46 +08:00
朱浩 36f3018269 关闭自动同步文件,超过150M文件不允许加入课程 2024-11-21 17:20:10 +08:00
yangws b5a5381e5b Merge pull request 'fix:清除测试数据;' (#415) from yangws into main
Reviewed-on: #415
2024-11-21 09:34:19 +08:00
小杨 de8e058197 fix:清除测试数据; 2024-11-21 09:33:49 +08:00
yangws 35d07291d3 Merge pull request 'yangws' (#414) from yangws into main
Reviewed-on: #414
2024-11-20 17:07:22 +08:00
小杨 9418998ec5 fix:样式提交; 2024-11-20 17:07:00 +08:00
小杨 64d9e9c18b fix:样式调整; 2024-11-20 17:05:12 +08:00
yangws 28d6d2d705 Merge pull request 'fix:修改样式;' (#413) from yangws into main
Reviewed-on: #413
2024-11-20 17:03:06 +08:00
小杨 4ba1039ea9 fix:修改样式; 2024-11-20 17:00:04 +08:00
朱浩 1417919f74 Merge branch 'main' into zhuhao_dev 2024-11-20 15:58:31 +08:00
yangws c691612bff Merge pull request 'fix:获取所有的教材;' (#412) from yangws into main
Reviewed-on: #412
2024-11-20 15:58:11 +08:00
小杨 baa1fa7483 fix:获取所有的教材; 2024-11-20 15:57:50 +08:00
朱浩 0f211628c5 Merge branch 'main' into zhuhao_dev 2024-11-20 15:47:13 +08:00
yangws 9afd5f417d Merge pull request 'add:新增实验室;' (#411) from yangws into main
Reviewed-on: #411
2024-11-20 15:05:06 +08:00
小杨 fb4908ee1d add:新增实验室; 2024-11-20 15:04:39 +08:00
yangws fc181559e2 Merge pull request 'add:科学实验室;' (#410) from yangws into main
Reviewed-on: #410
2024-11-19 15:58:40 +08:00
小杨 24f2d0ef4b add:科学实验室; 2024-11-19 15:58:08 +08:00
朱浩 eba83889b0 Merge branch 'main' into zhuhao_dev 2024-11-19 15:25:39 +08:00
zhengdegang bd485409e3 Merge pull request '发送短信' (#409) from zdg into main
Reviewed-on: #409
2024-11-19 11:17:04 +08:00
zdg 6303b442a4 发送短信 2024-11-19 11:15:29 +08:00
yangws 5a92b6dedf Merge pull request 'fix:切换第三方资源格式;' (#408) from yangws into main
Reviewed-on: #408
2024-11-18 17:17:25 +08:00
小杨 09df401cf1 fix:切换第三方资源格式; 2024-11-18 17:16:43 +08:00
zouyf 6b5164048c Merge pull request 'zouyf_dev' (#407) from zouyf_dev into main
Reviewed-on: #407
2024-11-13 15:45:01 +08:00
“zouyf” 1f2eb722f1 1 2024-11-13 15:43:09 +08:00
“zouyf” b973fd685c 优化滚动条 2024-11-13 14:08:28 +08:00
朱浩 966a64faa3 bug修复 2024-11-13 11:22:28 +08:00
朱浩 f899729db0 Merge pull request '三方资源加入备课' (#406) from zhuhao_dev into main
Reviewed-on: #406
2024-11-13 11:08:51 +08:00
朱浩 249e1c047c 三方资源加入备课 2024-11-13 11:07:50 +08:00
baigl c9bfd41fc7 Merge pull request 'baigl' (#405) from baigl into main
Reviewed-on: #405
2024-11-12 16:47:48 +08:00
白了个白 51a8df474a Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into baigl 2024-11-12 16:46:51 +08:00
白了个白 fbbd84df93 作业批改页:已交未交选择背景颜色缺失修复 2024-11-12 16:45:56 +08:00
朱浩 5cc93bb093 Merge pull request 'zhuhao_dev' (#404) from zhuhao_dev into main
Reviewed-on: #404
2024-11-12 15:53:26 +08:00
朱浩 eefa9aff63 三方资源加入备课 2024-11-12 15:50:38 +08:00
朱浩 3ef73885e1 三方资源加入备课 2024-11-12 15:47:24 +08:00
zouyf bc63691645 Merge pull request 'zouyf_dev' (#403) from zouyf_dev into main
Reviewed-on: #403
2024-11-12 15:45:31 +08:00
“zouyf” fa54546f5e 1 2024-11-12 15:43:47 +08:00
朱浩 d50327bc17 去除测试 2024-11-12 15:34:14 +08:00
“zouyf” f20ddc1c58 Merge branch 'main' into zouyf_dev 2024-11-12 15:29:13 +08:00
“zouyf” 7c1c4e9b7d 1 2024-11-12 15:26:46 +08:00
白了个白 78eb65d532 Merge branch 'zouyf_dev' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into baigl 2024-11-12 15:01:18 +08:00
“zouyf” f2dbf12f3c [作业布置] - 更改为分页查询 2024-11-12 15:01:00 +08:00
朱浩 77921f7e61 Merge branch 'main' into zhuhao_dev 2024-11-12 14:49:26 +08:00
朱浩 ac1cad8fd0 添加回调 2024-11-12 14:49:16 +08:00
yangws 00b58644b1 Merge pull request 'fix:修改格式;' (#402) from yangws into main
Reviewed-on: #402
2024-11-12 14:48:52 +08:00
小杨 6de59fabfa fix:修改格式; 2024-11-12 14:48:14 +08:00
yangws 28a2f95903 Merge pull request 'fix:修改格式传值;' (#401) from yangws into main
Reviewed-on: #401
2024-11-12 11:04:31 +08:00
小杨 78dce6dc6a fix:修改格式传值; 2024-11-12 11:04:04 +08:00
yangws a79add8625 Merge pull request 'yangws' (#400) from yangws into main
Reviewed-on: #400
2024-11-12 10:45:15 +08:00
小杨 83212f4ec0 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into yangws
# Conflicts:
#	src/renderer/src/views/prepare/components/treeLog.vue
2024-11-12 10:44:36 +08:00
小杨 24706b8912 fix:不修改原本树结构; 2024-11-12 10:42:39 +08:00
朱浩 169d6d7c31 Merge pull request 'zhuhao_dev' (#399) from zhuhao_dev into main
Reviewed-on: #399
2024-11-12 10:36:30 +08:00
朱浩 a75f880b30 添加回调 2024-11-12 10:35:45 +08:00
朱浩 e31904ed9f Merge branch 'main' into zhuhao_dev
# Conflicts:
#	package.json
2024-11-12 09:59:52 +08:00
朱浩 2d75ae6cb8 版本管理 2024-11-12 09:59:17 +08:00
yangws f0fcbed61e Merge pull request 'add:章节节点添加;' (#398) from yangws into main
Reviewed-on: #398
2024-11-12 09:50:00 +08:00
小杨 0a080bd1f0 add:章节节点添加; 2024-11-12 09:47:05 +08:00
zouyf a4a839931d Merge pull request 'zouyf_dev' (#397) from zouyf_dev into main
Reviewed-on: #397
2024-11-11 16:58:11 +08:00
“zouyf” 1844af01da 1 2024-11-11 16:56:56 +08:00
“zouyf” 163764cf1c Merge branch 'main' into zouyf_dev 2024-11-11 16:54:10 +08:00
“zouyf” 7273f7f835 限制题目上线 2024-11-11 16:53:56 +08:00
lyc 28ba027407 Merge pull request '打包' (#395) from lyc-dev into main 2024-11-11 16:21:50 +08:00
lyc b35daeb3c7 打包 2024-11-11 16:15:15 +08:00
baigl a7ac61e32f Merge pull request 'baigl' (#394) from baigl into main
Reviewed-on: #394
2024-11-11 15:04:18 +08:00
白了个白 f58a38dceb 1 2024-11-11 15:03:17 +08:00
“zouyf” cb910068e0 正常查询10条 2024-11-11 14:49:05 +08:00
“zouyf” 01ee2c8c85 Merge branch 'baigl' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into baigl 2024-11-11 13:43:41 +08:00
“zouyf” 768b3e7abf Revert "1"
This reverts commit f504cee74b.
2024-11-11 13:43:35 +08:00
“zouyf” f504cee74b 1 2024-11-11 13:43:09 +08:00
白了个白 6c66c97e33 1 2024-11-11 12:53:16 +08:00
白了个白 2929997d17 作业设计:课程选择过快请求添加防抖 2024-11-11 10:02:23 +08:00
zouyf 96c074d396 [作业设计] - 下拉刷新试题 2024-11-09 16:50:28 +08:00
zouyf 15633f065f 1 2024-11-09 14:30:26 +08:00
zouyf 9d45b72771 Merge branch 'baigl' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into zouyf_dev 2024-11-09 12:22:55 +08:00
白了个白 f5c42958f4 1 2024-11-08 17:25:36 +08:00
白了个白 3b8e36454a 1 2024-11-08 17:22:42 +08:00
白了个白 dd64e4e085 习题查询:加上分页传参 2024-11-08 15:39:05 +08:00
“zouyf” 7740346b49 Merge branch 'main' into zouyf_dev 2024-11-08 15:08:17 +08:00
baigl dfd56d6f52 Merge pull request 'baigl' (#393) from baigl into main
Reviewed-on: #393
2024-11-08 09:58:36 +08:00
白了个白 ea3e416f8f Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into baigl 2024-11-08 09:56:47 +08:00
白了个白 5e948d9b95 作业设计:习题来源同步web端的菁优网 2024-11-08 09:51:17 +08:00
“zouyf” a75e4bcc52 Merge branch 'baigl' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into zouyf_dev 2024-11-06 16:00:37 +08:00
白了个白 201b10a91e 工作台作业:逻辑更改 2024-11-06 09:56:43 +08:00
白了个白 a918aa18e9 作业批改列表:逻辑代码优化 2024-11-05 17:28:16 +08:00
“zouyf” 6c87321900 Merge branch 'baigl' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into zouyf_dev 2024-11-05 14:04:35 +08:00
白了个白 1cc0bf965e 1 2024-11-05 11:15:06 +08:00
白了个白 2407e66e66 作业批改:已批改日期更改 2024-11-05 11:12:14 +08:00
zhengdegang 09914eb8f9 Merge pull request '修复bug' (#392) from zdg into main
Reviewed-on: #392
2024-11-04 21:55:00 +08:00
zdg f1cdb7b400 修复bug 2024-11-04 21:53:37 +08:00
白了个白 033c0a3b19 作业批阅:待继续完善 2024-11-04 17:31:30 +08:00
zhengdegang d4f58d8a05 Merge pull request 'zdg' (#391) from zdg into main
Reviewed-on: #391
2024-11-04 16:30:25 +08:00
zdg a4647ff828 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into zdg 2024-11-04 16:29:43 +08:00
zdg 7e18f2e9d9 ppt上课-资源更新 2024-11-04 16:29:34 +08:00
白了个白 b07a4e07b9 作业批改:需求更新,界面调整 2024-11-04 15:36:00 +08:00
白了个白 4be888e39e Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into baigl 2024-11-04 11:27:32 +08:00
白了个白 e1c8ab42d9 作业批改:需求变更修改 2024-11-04 11:25:58 +08:00
zhengdegang e79d554987 Merge pull request '优化ppt上课' (#390) from zdg into main
Reviewed-on: #390
2024-11-04 09:41:37 +08:00
zdg fbacb32940 优化ppt上课 2024-11-04 09:40:16 +08:00
zhengdegang 7d20a7ee50 Merge pull request 'zdg' (#389) from zdg into main
Reviewed-on: #389
2024-11-01 12:55:07 +08:00
zdg e494c303f0 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into zdg 2024-11-01 12:54:12 +08:00
zdg 0fcaf3f3e7 ppt 上课优化 2024-11-01 12:54:01 +08:00
zouyf 91f43987f7 Merge pull request 'zouyf_dev' (#388) from zouyf_dev into main
Reviewed-on: #388
2024-11-01 11:22:40 +08:00
“zouyf” 9ab2c17b3e Merge branch 'main' into zouyf_dev 2024-11-01 11:22:03 +08:00
“zouyf” 21dfced02e [考试分析] - 优化获取真题 2024-11-01 11:21:40 +08:00
zhengdegang c3d6c2a57b Merge pull request 'zdg' (#387) from zdg into main
Reviewed-on: #387
2024-10-31 20:17:23 +08:00
zdg 674f98a53a Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into zdg 2024-10-31 20:16:03 +08:00
zdg d4a02e85ce 自动关闭-屏蔽该功能 2024-10-31 20:15:45 +08:00
lyc 4fbd4d8f0f Merge pull request '默认用户头像' (#386) from lyc-dev into main 2024-10-31 16:54:44 +08:00
lyc 4578bbca58 默认用户头像 2024-10-31 16:55:19 +08:00
yangws 8cffee606a Merge pull request 'fix:知识点分数修改;' (#385) from yangws into main
Reviewed-on: #385
2024-10-31 16:37:26 +08:00
小杨 9e24b6111d fix:知识点分数修改; 2024-10-31 16:37:02 +08:00
yangws 0433febf06 Merge pull request 'fix:知识点得分率计算;' (#384) from yangws into main
Reviewed-on: #384
2024-10-31 16:02:13 +08:00
小杨 6370086c85 fix:知识点得分率计算; 2024-10-31 16:01:50 +08:00
zhengdegang 7da598b30e Merge pull request 'zdg' (#383) from zdg into main
Reviewed-on: #383
2024-10-31 15:07:49 +08:00
zdg eafdb718b6 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into zdg 2024-10-31 15:07:16 +08:00
zdg d44516226e 上课修改 2024-10-31 15:07:04 +08:00
yangws 4afa20808c Merge pull request 'fix:知识点分数计算;' (#382) from yangws into main
Reviewed-on: #382
2024-10-31 14:08:52 +08:00
小杨 a0327ad556 fix:知识点分数计算; 2024-10-31 14:08:20 +08:00
baigl ada6659666 Merge pull request 'baigl' (#381) from baigl into main
Reviewed-on: #381
2024-10-31 13:51:22 +08:00
白了个白 d65eb565e5 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into baigl 2024-10-31 13:49:44 +08:00
zouyf 678f43e548 Merge pull request 'zouyf_dev' (#380) from zouyf_dev into main
Reviewed-on: #380
2024-10-31 12:36:31 +08:00
“zouyf” 8f4845a37c [逐题讲解] - 兼容填空自建题 2024-10-31 12:33:16 +08:00
白了个白 2db7d3e872 作业批改 2024-10-30 17:06:57 +08:00
“zouyf” 4c10409e07 Merge branch 'main' into zouyf_dev 2024-10-30 15:33:24 +08:00
zhengdegang 4418d1ae27 Merge pull request 'zdg' (#379) from zdg into main
Reviewed-on: #379
2024-10-30 13:55:37 +08:00
zdg fc7868afa6 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into zdg 2024-10-30 13:54:28 +08:00
zdg 1b3d226412 点赞-颜色恢复 2024-10-30 13:54:18 +08:00
zhengdegang fb5daf221f Merge pull request '点赞颜色统一 apt' (#378) from zdg into main
Reviewed-on: #378
2024-10-30 10:37:18 +08:00
zdg eca5f8f815 点赞颜色统一 apt 2024-10-30 10:36:18 +08:00
“zouyf” d845377879 Merge branch 'main' into zouyf_dev 2024-10-29 16:29:02 +08:00
“zouyf” 90d37e725b [作业批改] - 学生答题提交 2024-10-29 16:28:37 +08:00
yangws adc4f99636 Merge pull request 'fix:新增学生的得分以及修改得分率;' (#377) from yangws into main
Reviewed-on: #377
2024-10-29 14:13:33 +08:00
小杨 531a62ad64 fix:新增学生的得分以及修改得分率; 2024-10-29 14:12:59 +08:00
lyc 6ca1fb0883 Merge pull request '更新打包' (#376) from lyc-dev into main 2024-10-28 21:04:49 +08:00
lyc f836085c68 更新打包 2024-10-28 21:04:13 +08:00
zouyf cf49fbf23f Merge pull request '[推送作业] - 优化作业说明' (#375) from zouyf_dev into main
Reviewed-on: #375
2024-10-28 18:09:42 +08:00
zouyf f3d90b7d95 [推送作业] - 优化作业说明 2024-10-28 18:08:15 +08:00
yangws 06cb96c7bc Merge pull request 'fix:地址修改;' (#374) from yangws into main
Reviewed-on: #374
2024-10-28 17:14:47 +08:00
小杨 8784c0cfe3 fix:地址修改; 2024-10-28 17:14:14 +08:00
yangws 9e13e3dfb5 Merge pull request 'fix:作业统计相关问题以及优化;' (#373) from yangws into main
Reviewed-on: #373
2024-10-28 17:12:52 +08:00
小杨 c8aae6f408 fix:作业统计相关问题以及优化; 2024-10-28 17:12:32 +08:00
baigl 13f7398d79 Merge pull request 'baigl' (#372) from baigl into main
Reviewed-on: #372
2024-10-28 14:15:11 +08:00
白了个白 6ef29f2b5a Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into baigl 2024-10-28 14:14:20 +08:00
白了个白 5cf7d737d6 批阅:习题学生答案背景颜色修改-对绿错红 2024-10-28 14:13:56 +08:00
baigl 346b07e47a Merge pull request '作业批阅:习题训练类型 新增客观题自动批阅逻辑更改' (#371) from baigl into main
Reviewed-on: #371
2024-10-28 10:50:14 +08:00
白了个白 743d8e05d3 作业批阅:习题训练类型 新增客观题自动批阅逻辑更改 2024-10-28 10:48:51 +08:00
yangws 7c9b2755f2 Merge pull request 'fix:价值透析数据修改;' (#370) from yangws into main
Reviewed-on: #370
2024-10-25 16:41:15 +08:00
小杨 25acd398ff fix:价值透析数据修改; 2024-10-25 16:40:53 +08:00
yangws fe078e7ce5 Merge pull request 'fix:价值透析数据修改;' (#369) from yangws into main
Reviewed-on: #369
2024-10-25 16:37:28 +08:00
小杨 eaaba2b5e8 fix:价值透析数据修改; 2024-10-25 16:37:06 +08:00
zhengdegang 4816a4565b Merge pull request 'zdg' (#368) from zdg into main
Reviewed-on: #368
2024-10-25 15:29:56 +08:00
zdg 8b55ebffa5 ppt点赞优化 2024-10-25 15:28:54 +08:00
zdg 9674c68d11 修复-侧边工具 2024-10-25 15:05:25 +08:00
朱浩 906559134e Merge pull request '版本管理' (#367) from zhuhao_dev into main
Reviewed-on: #367
2024-10-25 14:35:33 +08:00
朱浩 ed3559f34f 版本管理 2024-10-25 14:34:53 +08:00
yangws f224f8b048 Merge pull request 'fix:第三方资源图片预览修改;' (#366) from yangws into main
Reviewed-on: #366
2024-10-25 13:56:58 +08:00
小杨 5790e87a85 fix:第三方资源图片预览修改; 2024-10-25 13:56:34 +08:00
yangws 4b6a7918a9 Merge pull request 'add:表格自动批阅;' (#365) from yangws into main
Reviewed-on: #365
2024-10-25 10:53:15 +08:00
小杨 7e71af0cb7 add:表格自动批阅; 2024-10-25 10:52:42 +08:00
zouyf bdc508a0fa Merge pull request 'zouyf_dev' (#364) from zouyf_dev into main
Reviewed-on: #364
2024-10-25 10:23:59 +08:00
“zouyf” fab449cc96 Merge branch 'main' into zouyf_dev 2024-10-25 10:21:33 +08:00
“zouyf” 5879f3d5e8 [作业批改] - 逐题字体修改 2024-10-25 10:20:48 +08:00
朱浩 4d20a27177 Merge pull request 'zhuhao_dev' (#363) from zhuhao_dev into main
Reviewed-on: #363
2024-10-24 17:04:45 +08:00
朱浩 302c82503a Merge branch 'main' into zhuhao_dev 2024-10-24 17:03:31 +08:00
朱浩 afd1a1dcf9 版本管理 2024-10-24 17:03:18 +08:00
zhengdegang 9c1cd2a2fc Merge pull request 'zdg' (#362) from zdg into main
Reviewed-on: #362
2024-10-24 17:03:03 +08:00
zdg dc39486620 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into zdg 2024-10-24 17:02:21 +08:00
zdg 62881bc052 修复部分ppt上课无效 2024-10-24 17:02:11 +08:00
zouyf 812e799e2a Merge pull request 'zouyf_dev' (#361) from zouyf_dev into main
Reviewed-on: #361
2024-10-24 14:49:33 +08:00
“zouyf” b5b6272141 Merge branch 'main' into zouyf_dev 2024-10-24 14:45:07 +08:00
“zouyf” 027d0ebe07 [ppt上课] - 增加作业回答消息检测 2024-10-24 14:44:53 +08:00
yangws a731dff62c Merge pull request 'fix:url地址更改;' (#360) from yangws into main
Reviewed-on: #360
2024-10-24 14:38:02 +08:00
小杨 dac36e611d fix:url地址更改; 2024-10-24 14:37:38 +08:00
yangws b574464a1e Merge pull request 'add:用时分析改为柱状图;' (#359) from yangws into main
Reviewed-on: #359
2024-10-24 14:37:04 +08:00
小杨 eba6727dc0 add:用时分析改为柱状图; 2024-10-24 14:36:35 +08:00
朱浩 521247f817 Merge branch 'main' into zhuhao_dev 2024-10-23 17:45:37 +08:00
lyc 8a759479bd Merge pull request 'ppt 上课 推送玩作业 打开作业批改弹窗' (#358) from lyc-dev into main 2024-10-23 16:08:53 +08:00
lyc e891259413 ppt 上课 推送玩作业 打开作业批改弹窗 2024-10-23 16:09:18 +08:00
yangws 14340e211d Merge pull request 'yangws' (#357) from yangws into main
Reviewed-on: #357
2024-10-23 15:50:44 +08:00
小杨 24ce28f2e2 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into yangws 2024-10-23 15:50:17 +08:00
小杨 0de7a7356e add:作业批阅客观题自动评分; 2024-10-23 15:50:03 +08:00
lyc 0b4019957f 调整ppt上课 作业查询 2024-10-23 10:43:57 +08:00
lyc e457ac9baf Merge pull request '调整ppt上课 作业查询' (#356) from lyc-dev into main 2024-10-23 10:43:25 +08:00
baigl 7b2ad4ce64 Merge pull request 'baigl' (#355) from baigl into main
Reviewed-on: #355
2024-10-22 17:31:41 +08:00
白了个白 758081d9e5 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into baigl 2024-10-22 17:30:00 +08:00
白了个白 a7a84b3738 常规作业改为主题作业,课堂展示改为创作展示 暂不修改 2024-10-22 17:27:02 +08:00
yangws 6467203c26 Merge pull request 'yangws' (#354) from yangws into main
Reviewed-on: #354
2024-10-22 16:59:52 +08:00
小杨 49b4ba1f02 Merge branch 'main' of http://27.128.240.72:3000/zhuhao/AIx_Smarttalk into yangws 2024-10-22 16:59:28 +08:00
小杨 2c2c5b32e9 fix:修改作业批阅样式; 2024-10-22 16:59:23 +08:00
zouyf 2f726bccfb Merge pull request 'zouyf_dev' (#353) from zouyf_dev into main
Reviewed-on: #353
2024-10-22 16:54:40 +08:00
“zouyf” 6112cc8a92 1 2024-10-22 16:50:42 +08:00
“zouyf” d05388cb05 Merge branch 'main' into zouyf_dev 2024-10-22 16:46:45 +08:00
“zouyf” cca640e945 [逐题讲评] - 增加右侧试题显示 2024-10-22 16:45:55 +08:00
baigl 08ed5c9f2f Merge pull request 'baigl' (#352) from baigl into main
Reviewed-on: #352
2024-10-22 16:16:43 +08:00
白了个白 35a00e2aca 作业批阅:最小化再次打开会刷新的逻辑修改 2024-10-22 16:04:03 +08:00
“zouyf” ab127958f9 Merge branch 'main' into zouyf_dev 2024-10-22 14:54:57 +08:00
“zouyf” ef75fb7570 试题格式化样式优化 2024-10-22 14:47:49 +08:00
白了个白 e3ce510322 常规作业改为主题作业,课堂展示改为创作展示 2024-10-22 14:39:48 +08:00
朱浩 79cb126728 Merge branch 'main' into zhuhao_dev 2024-10-21 17:21:41 +08:00
朱浩 810a734fa5 上课APT错误修复,去除课程预约 2024-10-21 17:21:12 +08:00
zouyf bcc9a66df6 Merge pull request 'zouyf_dev' (#350) from zouyf_dev into main
Reviewed-on: #350
2024-10-21 16:46:40 +08:00
“zouyf” 0f425d9277 [作业布置] - 增加试题详情查看弹框 2024-10-21 16:41:13 +08:00
“zouyf” 8c36d59b19 Merge branch 'main' into zouyf_dev 2024-10-21 16:34:55 +08:00
“zouyf” a229e9933e [考试分析] - 优化试题详情弹框 2024-10-21 16:34:39 +08:00
朱浩 b43422f389 生成PPT秘钥更新 2024-10-21 10:41:43 +08:00
yangws 6c1bb40826 Merge pull request 'fix:获取学生id;' (#349) from yangws into main
Reviewed-on: #349
2024-10-20 17:46:51 +08:00
小杨 ea861dd2ea fix:获取学生id; 2024-10-20 17:45:42 +08:00
lyc ff2e05fb6b Merge pull request 'edit' (#348) from lyc-dev into main 2024-10-20 17:08:22 +08:00
lyc b7425da4da edit 2024-10-20 17:08:57 +08:00
zouyf b0075b86ba Merge pull request 'zouyf_dev' (#347) from zouyf_dev into main
Reviewed-on: #347
2024-10-20 15:28:42 +08:00
yangws 54d4896e18 Merge pull request 'fix:判断没有学生回答题目的情况;' (#346) from yangws into main
Reviewed-on: #346
2024-10-20 15:25:37 +08:00
小杨 3042e874e4 fix:判断没有学生回答题目的情况; 2024-10-20 15:23:48 +08:00
zouyf 92f23a35f9 Merge branch 'main' into zouyf_dev 2024-10-20 15:04:33 +08:00
zouyf 64e91dffbe 1 2024-10-20 15:03:30 +08:00
qinqing 80c38152d2 Merge pull request '屏蔽ocr识别' (#345) from qinqing_dev into main
Reviewed-on: #345
2024-10-20 12:11:50 +08:00
74 changed files with 3763 additions and 2077 deletions

View File

@ -18,4 +18,4 @@ VITE_APP_RES_FILE_PATH = 'https://prev.ysaix.com:7868/src/assets/textbook/booktx
VITE_APP_BUILD_BASE_PATH = 'https://prev.ysaix.com:7868/' VITE_APP_BUILD_BASE_PATH = 'https://prev.ysaix.com:7868/'
VITE_SHOW_DEV_TOOLS = 'true' VITE_SHOW_DEV_TOOLS = 'false'

View File

@ -17,3 +17,5 @@ VITE_BUILD_COMPRESS = gzip
VITE_APP_RES_FILE_PATH = 'https://file.ysaix.com:7868/src/assets/textbook/booktxt/' VITE_APP_RES_FILE_PATH = 'https://file.ysaix.com:7868/src/assets/textbook/booktxt/'
VITE_APP_BUILD_BASE_PATH = 'https://file.ysaix.com:7868/' VITE_APP_BUILD_BASE_PATH = 'https://file.ysaix.com:7868/'
VITE_SHOW_DEV_TOOLS = 'true'

View File

@ -1,5 +1,5 @@
# 页面标题 # 页面标题
VITE_APP_TITLE = AIX智慧课堂 VITE_APP_TITLE = 文枢课堂
# 生产环境配置 # 生产环境配置
VITE_APP_ENV = 'production' VITE_APP_ENV = 'production'
@ -17,3 +17,5 @@ VITE_BUILD_COMPRESS = gzip
VITE_APP_RES_FILE_PATH = 'https://prev.ysaix.com:7868/src/assets/textbook/booktxt/' VITE_APP_RES_FILE_PATH = 'https://prev.ysaix.com:7868/src/assets/textbook/booktxt/'
VITE_APP_BUILD_BASE_PATH = 'https://prev.ysaix.com:7868/' VITE_APP_BUILD_BASE_PATH = 'https://prev.ysaix.com:7868/'
VITE_SHOW_DEV_TOOLS = 'false'

View File

@ -1,10 +1,10 @@
appId: com.electron.app appId: com.electron.app
productName: AIx productName: 文枢课堂
directories: directories:
output: dist output: dist
buildResources: build buildResources: build
win: win:
executableName: AIx executableName: 文枢课堂
icon: resources/logo2.ico icon: resources/logo2.ico
files: files:
- '!**/.vscode/*' - '!**/.vscode/*'
@ -17,7 +17,7 @@ asarUnpack:
nsis: nsis:
oneClick: false oneClick: false
allowToChangeInstallationDirectory: true allowToChangeInstallationDirectory: true
artifactName: ${name}-${version}-setup.${ext} artifactName: ${name}-yc-${version}-setup.${ext}
shortcutName: ${productName} shortcutName: ${productName}
uninstallDisplayName: ${productName} uninstallDisplayName: ${productName}
createDesktopShortcut: always createDesktopShortcut: always
@ -30,7 +30,7 @@ mac:
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
notarize: false notarize: false
dmg: dmg:
artifactName: ${name}-${version}.${ext} artifactName: ${name}-yc-${version}.${ext}
linux: linux:
target: target:
- AppImage - AppImage
@ -39,11 +39,11 @@ linux:
maintainer: electronjs.org maintainer: electronjs.org
category: Utility category: Utility
appImage: appImage:
artifactName: ${name}-${version}.${ext} artifactName: ${name}-yc-${version}.${ext}
npmRebuild: false npmRebuild: false
publish: publish:
provider: generic provider: generic
url: https://prev.ysaix.com:7868/src/assets/smarttalk/ url: https://prev.ysaix.com:7868/src/assets/smarttalkyc/
electronDownload: electronDownload:
mirror: https://npmmirror.com/mirrors/electron/ mirror: https://npmmirror.com/mirrors/electron/
# 额外依赖打包到输出目录 # 额外依赖打包到输出目录

View File

@ -1,9 +1,9 @@
{ {
"name": "aix-win", "name": "aix-win",
"version": "2.1.12", "version": "2.1.36",
"description": "", "description": "",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "example.com", "author": "上海交大重庆人工智能研究院",
"homepage": "https://electron-vite.org", "homepage": "https://electron-vite.org",
"scripts": { "scripts": {
"format": "prettier --write .", "format": "prettier --write .",
@ -16,7 +16,7 @@
"build:dev": "npm run build && electron-builder --win --config ./electron-builder-test.yml", "build:dev": "npm run build && electron-builder --win --config ./electron-builder-test.yml",
"build:test": "electron-vite build --mode test && electron-builder --win --config ./electron-builder.yml", "build:test": "electron-vite build --mode test && electron-builder --win --config ./electron-builder.yml",
"build:prod": "electron-vite build --mode production && electron-builder --win --config ./electron-builder-prod.yml", "build:prod": "electron-vite build --mode production && electron-builder --win --config ./electron-builder-prod.yml",
"build:lt": "electron-vite build --mode lt && electron-builder --win --config ./electron-builder-lt.yml", "build:yc": "electron-vite build --mode yc && electron-builder --win --config ./electron-builder-yc.yml",
"build:mac": "electron-vite build --mode production && electron-builder --mac --config ./electron-builder-prod.yml", "build:mac": "electron-vite build --mode production && electron-builder --mac --config ./electron-builder-prod.yml",
"build:linux": "npm run build && electron-builder --linux" "build:linux": "npm run build && electron-builder --linux"
}, },

View File

@ -46,7 +46,7 @@ if(!gotTheLock){
function createLoginWindow() { function createLoginWindow() {
if (loginWindow) return if (loginWindow) return
loginWindow = new BrowserWindow({ loginWindow = new BrowserWindow({
width: 888, width: import.meta.env.MODE==='yc'?1160:888,
height: 520, height: 520,
show: false, show: false,
frame: false, frame: false,
@ -175,6 +175,7 @@ async function createLinkWin(data) {
data.fullPath += '?urlSource=smarttalk&t' + Date.now() data.fullPath += '?urlSource=smarttalk&t' + Date.now()
} }
linkWin[data.key].loadURL(data.fullPath) linkWin[data.key].loadURL(data.fullPath)
if (import.meta.env.VITE_SHOW_DEV_TOOLS === 'true') linkWin[data.key].webContents.openDevTools()
linkWin[data.key].once('ready-to-show', () => { linkWin[data.key].once('ready-to-show', () => {
linkWin[data.key].show() linkWin[data.key].show()
@ -271,12 +272,13 @@ app.on('window-all-closed', () => {
function handleAll() { function handleAll() {
const chatInstance = chat.initialize() // im-chat 实例 const chatInstance = chat.initialize() // im-chat 实例
// 新窗口创建-监听 // 新窗口创建-监听
ipcMain.on('new-window', (e, data) => { ipcMain.handle('new-window', (e, data) => {
const { id, type } = data const { id, type } = data
const win = BrowserWindow.fromId(id) const win = BrowserWindow.fromId(id)
win.type = type // 绑定独立标识 win.type = type // 绑定独立标识
remote.enable(win.webContents) // 开启远程服务 remote.enable(win.webContents) // 开启远程服务
chatInstance.enable(win.webContents) // 开启im-chat chatInstance.enable(win.webContents) // 开启im-chat
console.log(`主进程 [${type}]: 窗口注册-远程代理-完毕(${Date.now()})`)
}) })
// 用于监听-状态管理变化-同步所有窗口 // 用于监听-状态管理变化-同步所有窗口
ipcMain.handle('pinia-state-change', (e, storeName, jsonStr) => { ipcMain.handle('pinia-state-change', (e, storeName, jsonStr) => {

View File

@ -23,7 +23,9 @@ const defaultData = {
curNode: null, // 当前选中的节点 curNode: null, // 当前选中的节点
defaultExpandedKeys: [], //展开的节点 defaultExpandedKeys: [], //展开的节点
subjectTree: [] // "树结构" 章节 subjectTree: [] // "树结构" 章节
} },
env: {}, // 不走同步 Pinia - 变量
curr: {} // 不走同步 Pinia - 当前信息
}, },
local: { // 本地(永久localStorage) local: { // 本地(永久localStorage)
}, },

View File

@ -71,6 +71,16 @@ export function updateClassworkeval(data) {
}) })
} }
// 修改classworkeval
export function updateClassworkevalList(data) {
return request({
url: '/education/classworkeval/updateList',
method: 'put',
data: data
})
}
// 修改classworkdata // 修改classworkdata
export function updateClassworkdata(data) { export function updateClassworkdata(data) {
return request({ return request({
@ -79,6 +89,13 @@ export function updateClassworkdata(data) {
data: data data: data
}) })
} }
export function updateClassWorkDataAutoFinish(data) {
return request({
url: '/education/classworkdata/updAutoFinish',
method: 'put',
data: data
})
}
// 修改classwork // 修改classwork
export function updateClasswork(data) { export function updateClasswork(data) {

View File

@ -24,6 +24,13 @@ export const getPrepareById = (id) => {
}) })
} }
export const addFileToKj = (id) => {
return request({
url: '/smarttalk/file/addFileToKj/' + id,
method: 'get'
})
}
export function deleteSmarttalk(id) { export function deleteSmarttalk(id) {
return request({ return request({
url: '/smarttalk/file/' + id, url: '/smarttalk/file/' + id,
@ -53,3 +60,14 @@ export const moveSmarttalk = (params) => {
params params
}) })
} }
export const addFileToPrepareThird = (data) => {
return request({
url: '/smarttalk/file/addFileToPrepareThird',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data'
},
data
})
}

View File

@ -1,9 +1,9 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 2794390 */ font-family: "iconfont"; /* Project id 2794390 */
src: url('iconfont.woff2?t=1728543886557') format('woff2'), src: url('iconfont.woff2?t=1732002934577') format('woff2'),
url('iconfont.woff?t=1728543886557') format('woff'), url('iconfont.woff?t=1732002934577') format('woff'),
url('iconfont.ttf?t=1728543886557') format('truetype'), url('iconfont.ttf?t=1732002934577') format('truetype'),
url('iconfont.svg?t=1728543886557#iconfont') format('svg'); url('iconfont.svg?t=1732002934577#iconfont') format('svg');
} }
.iconfont { .iconfont {
@ -14,6 +14,26 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-a-shiyanpingshiyanyanjiu:before {
content: "\e9a0";
}
.icon-banji2:before {
content: "\e6c0";
}
.icon-set:before {
content: "\e691";
}
.icon-shouye:before {
content: "\e637";
}
.icon-gongzuotai:before {
content: "\e690";
}
.icon-A1:before { .icon-A1:before {
content: "\e635"; content: "\e635";
} }

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,45 @@
{ {
"id": "2794390", "id": "2794390",
"name": "electron", "name": "文枢2.1",
"font_family": "iconfont", "font_family": "iconfont",
"css_prefix_text": "icon-", "css_prefix_text": "icon-",
"description": "", "description": "",
"glyphs": [ "glyphs": [
{
"icon_id": "41507853",
"name": "实验瓶",
"font_class": "a-shiyanpingshiyanyanjiu",
"unicode": "e9a0",
"unicode_decimal": 59808
},
{
"icon_id": "1017928",
"name": "班级",
"font_class": "banji2",
"unicode": "e6c0",
"unicode_decimal": 59072
},
{
"icon_id": "376364",
"name": "设置",
"font_class": "set",
"unicode": "e691",
"unicode_decimal": 59025
},
{
"icon_id": "5835474",
"name": "首页",
"font_class": "shouye",
"unicode": "e637",
"unicode_decimal": 58935
},
{
"icon_id": "19108133",
"name": "工作台",
"font_class": "gongzuotai",
"unicode": "e690",
"unicode_decimal": 59024
},
{ {
"icon_id": "11657531", "icon_id": "11657531",
"name": "A", "name": "A",

View File

@ -14,6 +14,16 @@
/> />
<missing-glyph /> <missing-glyph />
<glyph glyph-name="a-shiyanpingshiyanyanjiu" unicode="&#59808;" d="M404.602646 837.887844l-16.783367-7.405737-128.245981-56.577561-16.787416-7.405736-16.787416-7.405737 100.716399-228.298334a18.342257 18.342257 0 0 1 24.189104-9.37763l161.816764 71.384985a18.338207 18.338207 0 0 1 9.377631 24.189104L421.390062 845.293581l-16.787416-7.405737zM422.325396 869.563666L202.995737 772.80725A18.354404 18.354404 0 0 1 193.614058 748.614097l25.735846-58.342952a18.350355 18.350355 0 0 1 24.193153-9.38168L462.872715 777.645881a18.350355 18.350355 0 0 1 9.38168 24.193153l-25.739895 58.347001a18.342257 18.342257 0 0 1-24.189104 9.377631zM921.226676 261.831663L517.555315 623.259148l-12.240318-13.669638-161.812715-71.384985-18.346306 0.076932 1.886863-540.157186c-0.149815-39.065362 14.386322-70.158119 40.940079-87.540747 26.549708-17.37858 60.853372-18.265324 96.590404-2.498272L895.547517 99.994654c35.765376 15.77515 58.193137 41.677008 63.157289 72.935776 4.956053 31.254719-8.353218 62.833364-37.47813 88.901233zM921.226676 261.831663l-136.445333 122.168337H325.694501l1.348338-385.879778c-0.149815-39.065362 14.386322-70.158119 40.940079-87.540747 26.549708-17.37858 60.853372-18.265324 96.590404-2.498272l430.974195 191.909402c35.765376 15.77515 58.193137 41.677008 63.157289 72.935776 4.956053 31.258768-8.353218 62.837413-37.47813 88.905282z" horiz-adv-x="1039" />
<glyph glyph-name="banji2" unicode="&#59072;" d="M865.793707 531.61984c0 12.357973-10.03008 22.386347-22.386347 22.386347-12.357973 0-22.386347-10.03008-22.386347-22.386347 0-61.743787-50.19136-111.93344-111.935147-111.93344s-111.935147 50.19136-111.935147 111.93344c0 12.357973-10.03008 22.386347-22.386347 22.386347-12.357973 0-22.386347-10.03008-22.386347-22.386347 0-86.413653 70.294187-156.70784 156.70784-156.70784S865.793707 445.206187 865.793707 531.61984zM1059.054933 208.631467c58.876587 23.237973 99.48672 81.08544 99.48672 145.916587 0 71.637333-47.684267 132.082347-115.24608 150.528 0.67072 8.820053 1.611093 17.55136 1.611093 26.550613 0 185.183573-150.662827 335.802027-335.802027 335.802027-185.183573 0-335.802027-150.618453-335.802027-335.802027 0-9.043627 1.477973-17.73056 2.194773-26.594987-67.428693-18.536107-115.024213-78.93504-115.024213-150.483627 0-64.832853 40.564053-122.68032 99.442347-145.916587L287.470933 1.59744c-4.118187-11.685547 2.059947-24.446293 13.70112-28.520107 2.46272-0.84992 4.969813-1.252693 7.386453-1.252693 9.222827 0 17.90976 5.77536 21.133653 14.999893l80.413013 229.643947c0.402773 1.02912 0.67072 2.148693 0.940373 3.268267 2.46272 11.55072-4.432213 23.058773-15.849813 26.238293-2.776747 0.807253-5.59616 0.896-8.193707 0.761173-47.72864 13.298347-81.75616 57.623893-81.75616 107.815253 0 49.206613 31.43168 91.24864 76.741973 106.069333 25.879893-117.306027 113.322667-215.048533 230.137173-250.374827l-72.174933-206.226773c-4.118187-11.685547 2.059947-24.446293 13.745493-28.520107 2.46272-0.896 4.92544-1.252693 7.386453-1.252693 9.268907 0 17.865387 5.77536 21.133653 14.999893l80.18944 229.15072c2.10432 6.045013 1.56672 12.670293-1.611093 18.26816-3.089067 5.59616-8.46336 9.581227-14.73024 10.970453-132.125013 29.146453-227.985067 148.60288-227.985067 283.997867 0 160.469333 130.56 291.027627 291.027627 291.027627s291.027627-130.56 291.027627-291.027627c0-12.043947-0.761173-23.86432-2.148693-35.551573-14.327467-116.993707-97.158827-213.03296-211.10784-244.642133-5.95456-1.657173-10.970453-5.686613-13.88032-11.19232-2.82112-5.46304-3.31264-11.910827-1.252693-17.73056l81.62304-233.270613c3.268267-9.222827 11.864747-14.999893 21.133653-14.999893 2.46272 0 4.92544 0.3584 7.386453 1.252693 11.685547 4.07552 17.819307 16.83456 13.745493 28.520107l-74.011307 211.46624c110.052693 38.99904 190.55616 131.76832 214.95808 245.225813 45.535573-14.685867 77.190827-56.818347 77.190827-106.15808 0-50.773333-34.833067-95.501653-83.457707-108.30848-2.194773 0-4.522667-0.31232-6.760107-1.02912-11.461973-3.403093-18.044587-15.849813-15.179093-27.446613 0.402773-1.431893 0.896-2.776747 1.477973-4.118187l79.428267-226.822827c3.223893-9.222827 11.820373-14.999893 21.087573-14.999893 2.46272 0 4.969813 0.402773 7.432533 1.252693 11.641173 4.07552 17.819307 16.83456 13.70112 28.520107L1059.054933 208.631467z" horiz-adv-x="1366" />
<glyph glyph-name="set" unicode="&#59025;" d="M512 558.182c-115.665 0-209.455-93.789-209.455-209.455 0-115.665 93.789-209.455 209.455-209.455s209.455 93.789 209.455 209.455c0 115.665-93.789 209.455-209.455 209.455zM512 185.818c-89.972 0-162.909 72.937-162.909 162.909 0 89.972 72.937 162.909 162.909 162.909s162.909-72.937 162.909-162.909c0-89.972-72.937-162.909-162.909-162.909zM907.636 465.091l-66.746 0c-4.143 11.59-8.797 22.9-14.057 33.932l47.197 47.197c18.199 18.199 18.199 47.663 0 65.815l-98.723 98.723c-18.199 18.199-47.663 18.199-65.815 0l-47.43-47.43c-10.938 5.213-22.109 10.1-33.652 14.196l0 66.839c0 25.693-20.852 46.545-46.545 46.545l-139.636 0c-25.693 0-46.545-20.852-46.545-46.545l0-66.839c-11.59-4.096-22.807-8.89-33.792-14.15l-47.383 47.383c-18.153 18.199-47.663 18.199-65.815 0l-98.723-98.723c-18.199-18.199-18.199-47.663 0-65.815l47.29-47.29c-5.26-10.985-10.1-22.249-14.196-33.839l-66.7 0c-25.693 0-46.545-20.852-46.545-46.545l0-139.636c0-25.693 20.852-46.545 46.545-46.545l66.746 0c4.143-11.59 8.797-22.9 14.057-33.885l-47.197-47.197c-18.199-18.153-18.199-47.663 0-65.815l98.723-98.723c18.199-18.199 47.663-18.199 65.815 0l47.43 47.43c10.938-5.213 22.109-10.1 33.652-14.196l0-66.886c0-25.693 20.852-46.545 46.545-46.545l139.636 0c25.693 0 46.545 20.852 46.545 46.545l0 66.746c11.59 4.143 22.9 8.797 33.885 14.057l47.197-47.197c18.199-18.199 47.663-18.199 65.815 0l98.723 98.723c18.199 18.199 18.199 47.663 0 65.815l-47.43 47.43c5.213 10.938 10.1 22.109 14.196 33.652l66.932 0c25.693 0 46.545 20.852 46.545 46.545l0 139.636c0 25.74-20.852 46.592-46.545 46.592zM907.636 302.182c0-12.847-10.426-23.273-23.273-23.273l-78.243 0c-7.54-31.837-20.108-61.673-36.817-88.762l55.343-55.389c9.076-9.076 9.076-23.831 0-32.908l-65.815-65.815c-9.076-9.076-23.831-9.076-32.908 0l-55.343 55.343c-27.089-16.71-56.925-29.277-88.762-36.817l0-78.196c0-12.847-10.426-23.273-23.273-23.273l-93.091 0c-12.847 0-23.273 10.426-23.273 23.273l0 78.243c-31.837 7.54-61.673 20.108-88.762 36.817l-55.343-55.343c-9.076-9.076-23.831-9.076-32.908 0l-65.815 65.815c-9.076 9.076-9.076 23.831 0 32.908l55.343 55.389c-16.71 27.043-29.277 56.879-36.817 88.716l-78.243 0c-12.847 0-23.273 10.426-23.273 23.273l0 93.091c0 12.847 10.426 23.273 23.273 23.273l78.243 0c7.54 31.837 20.108 61.673 36.817 88.762l-55.389 55.343c-9.076 9.076-9.076 23.831 0 32.908l65.815 65.815c9.076 9.076 23.831 9.076 32.908 0l55.343-55.343c27.136 16.71 56.972 29.277 88.809 36.817l0 78.243c0 12.847 10.426 23.273 23.273 23.273l93.091 0c12.847 0 23.273-10.426 23.273-23.273l0-78.243c31.837-7.54 61.673-20.108 88.762-36.817l55.343 55.343c9.076 9.076 23.831 9.076 32.908 0l65.815-65.815c9.076-9.076 9.076-23.831 0-32.908l-55.343-55.343c16.71-27.089 29.277-56.879 36.817-88.762l78.243 0c12.847 0 23.273-10.426 23.273-23.273l0-93.091z" horiz-adv-x="1024" />
<glyph glyph-name="shouye" unicode="&#58935;" d="M960 448c-6.4 0-12.9 1.9-18.6 6L512 760.7 82.6 454c-14.4-10.2-34.4-6.9-44.6 7.5-10.3 14.4-6.9 34.4 7.5 44.6l448 320c11.1 7.9 26.1 7.9 37.2 0l448-320c14.4-10.3 17.7-30.3 7.5-44.6-6.4-8.8-16.2-13.5-26.2-13.5zM608-64H416c-17.7 0-32 14.3-32 32V256c0 17.7 14.3 32 32 32h192c17.7 0 32-14.3 32-32v-288c0-17.7-14.3-32-32-32z m-160 64h128V224H448v-224zM832-64H192c-52.9 0-96 43.1-96 96V512c0 17.7 14.3 32 32 32s32-14.3 32-32v-480c0-17.6 14.4-32 32-32h640c17.6 0 32 14.4 32 32V512c0 17.7 14.3 32 32 32s32-14.3 32-32v-480c0-52.9-43.1-96-96-96z" horiz-adv-x="1024" />
<glyph glyph-name="gongzuotai" unicode="&#59024;" d="M320.284444 810.666667a85.333333 85.333333 0 0 0 85.333334-85.333334v-234.951111H170.666667a85.333333 85.333333 0 0 0-85.333334 85.333334V725.333333a85.333333 85.333333 0 0 0 85.333334 85.333334h149.617777m0 85.333333H170.666667a170.666667 170.666667 0 0 1-170.666667-170.666667v-149.617777a170.666667 170.666667 0 0 1 170.666667-170.666667h320.284444V725.333333a170.666667 170.666667 0 0 1-170.666667 170.666667zM405.617778 277.617778V42.666667a85.333333 85.333333 0 0 0-85.333334-85.333334H170.666667a85.333333 85.333333 0 0 0-85.333334 85.333334v149.617777a85.333333 85.333333 0 0 0 85.333334 85.333334h234.951111m85.333333 85.333333H170.666667a170.666667 170.666667 0 0 1-170.666667-170.666667V42.666667a170.666667 170.666667 0 0 1 170.666667-170.666667h149.617777a170.666667 170.666667 0 0 1 170.666667 170.666667V362.951111zM853.333333 277.617778a85.333333 85.333333 0 0 0 85.333334-85.333334V42.666667a85.333333 85.333333 0 0 0-85.333334-85.333334h-149.617777a85.333333 85.333333 0 0 0-85.333334 85.333334v234.951111H853.333333m0 85.333333H533.048889V42.666667a170.666667 170.666667 0 0 1 170.666667-170.666667H853.333333a170.666667 170.666667 0 0 1 170.666667 170.666667v149.617777a170.666667 170.666667 0 0 1-170.666667 170.666667zM853.333333 810.666667a85.333333 85.333333 0 0 0 85.333334-85.333334v-149.617777a85.333333 85.333333 0 0 0-85.333334-85.333334h-234.951111V725.333333a85.333333 85.333333 0 0 0 85.333334 85.333334H853.333333m0 85.333333h-149.617777a170.666667 170.666667 0 0 1-170.666667-170.666667v-320.284444H853.333333a170.666667 170.666667 0 0 1 170.666667 170.666667V725.333333a170.666667 170.666667 0 0 1-170.666667 170.666667z" horiz-adv-x="1024" />
<glyph glyph-name="A1" unicode="&#58933;" d="M449 339h121.5L507.5 505.5zM782 834H242C143 834 62 753 62 654v-540c0-99 81-180 180-180h540c99 0 180 81 180 180V654c0 99-81 180-180 180z m-139.5-675l-40.5 103.5H422L381.5 159H287L462.5 609H557l180-450h-94.5z" horiz-adv-x="1024" /> <glyph glyph-name="A1" unicode="&#58933;" d="M449 339h121.5L507.5 505.5zM782 834H242C143 834 62 753 62 654v-540c0-99 81-180 180-180h540c99 0 180 81 180 180V654c0 99-81 180-180 180z m-139.5-675l-40.5 103.5H422L381.5 159H287L462.5 609H557l180-450h-94.5z" horiz-adv-x="1024" />
<glyph glyph-name="A" unicode="&#59119;" d="M841.589844 779.507813a65.917969 65.917969 0 0 0 65.917969-65.917969v-659.179688a65.917969 65.917969 0 0 0-65.917969-65.917969H182.410156a65.917969 65.917969 0 0 0-65.917969 65.917969V713.589844a65.917969 65.917969 0 0 0 65.917969 65.917969h659.179688z m0-49.438477H182.410156a16.479492 16.479492 0 0 1-16.21582-13.513183L165.930664 713.589844v-659.179688a16.479492 16.479492 0 0 1 13.513183-16.21582L182.410156 37.930664h659.179688a16.479492 16.479492 0 0 1 16.21582 13.513183L858.069336 54.410156V713.589844a16.479492 16.479492 0 0 1-13.513183 16.21582L841.589844 730.069336zM539.059326 614.712891l188.85498-461.425782h-69.543456l-53.822022 139.746094H411.606933L360.916016 153.287109H296.118652l177.1875 461.425782h65.786133z m-34.277344-48.449707c-5.899658-29.597168-14.172364-58.996582-24.884033-88.165284L429.207031 342.80127h156.456299l-48.153076 127.781982-4.779053 12.689209a1170.900879 1170.900879 0 0 0-27.982178 82.990723z" horiz-adv-x="1024" /> <glyph glyph-name="A" unicode="&#59119;" d="M841.589844 779.507813a65.917969 65.917969 0 0 0 65.917969-65.917969v-659.179688a65.917969 65.917969 0 0 0-65.917969-65.917969H182.410156a65.917969 65.917969 0 0 0-65.917969 65.917969V713.589844a65.917969 65.917969 0 0 0 65.917969 65.917969h659.179688z m0-49.438477H182.410156a16.479492 16.479492 0 0 1-16.21582-13.513183L165.930664 713.589844v-659.179688a16.479492 16.479492 0 0 1 13.513183-16.21582L182.410156 37.930664h659.179688a16.479492 16.479492 0 0 1 16.21582 13.513183L858.069336 54.410156V713.589844a16.479492 16.479492 0 0 1-13.513183 16.21582L841.589844 730.069336zM539.059326 614.712891l188.85498-461.425782h-69.543456l-53.822022 139.746094H411.606933L360.916016 153.287109H296.118652l177.1875 461.425782h65.786133z m-34.277344-48.449707c-5.899658-29.597168-14.172364-58.996582-24.884033-88.165284L429.207031 342.80127h156.456299l-48.153076 127.781982-4.779053 12.689209a1170.900879 1170.900879 0 0 0-27.982178 82.990723z" horiz-adv-x="1024" />

Before

Width:  |  Height:  |  Size: 362 KiB

After

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -139,16 +139,23 @@ const handleNodeClick = (data) => {
* data : 当前节点数据 * data : 当前节点数据
*/ */
let nodeData = cloneDeep(toRaw(data)); let nodeData = cloneDeep(toRaw(data));
//label label //label label
nodeData.label = nodeData.itemtitle nodeData.label = nodeData.itemtitle
// null
let parent = { let parentNode
// children
if(nodeData.children){
//
parentNode = null
}
else{
parentNode = {
id: nodeData.parentid, id: nodeData.parentid,
label: nodeData.parenttitle, label: nodeData.parenttitle,
itemtitle: nodeData.parenttitle itemtitle: nodeData.parenttitle
} }
const parentNode = nodeData.parentid ? parent : null }
nodeData.parentNode = parentNode nodeData.parentNode = parentNode
let curData = { let curData = {
textBook: { textBook: {

View File

@ -0,0 +1,74 @@
<template>
<!-- 试题详情 -->
<el-drawer
v-model="activeExamInfoDrawer"
title="题目详情"
:with-header="false"
direction="rtl"
size="60%"
>
<div style="height: calc(100% - 50px);">
<el-scrollbar style="background: #F3F5F8;border-radius: 8px;">
<el-row class="drawer-main">
<el-col :span="24">
<span>{{activeExam.worktag}}</span>
<span style="margin-left: 4px" v-html="activeExam.titleFormat" ></span>
</el-col>
<el-col :span="24" style="padding: 4px" v-html="activeExam.workdescFormat">
</el-col>
<el-col :span="3" class="drawer-main-col"><em>答案</em></el-col>
<el-col :span="20" class="drawer-main-col" v-html="activeExam.workanswerFormat"></el-col>
<el-col :span="3" class="drawer-main-col"><em>分析</em></el-col>
<el-col :span="20" class="drawer-main-col" v-html="activeExam.method"></el-col>
<el-col :span="3" class="drawer-main-col"><em>解答</em></el-col>
<el-col :span="20" class="drawer-main-col" v-html="activeExam.analyse"></el-col>
<el-col :span="3" class="drawer-main-col" ><em>点评</em></el-col>
<el-col :span="20" class="drawer-main-col" v-html="activeExam.discuss"></el-col>
</el-row>
</el-scrollbar>
</div>
<div class="drawer-footer">
<el-button type="primary" @click="activeExamInfoDrawer = false">关闭</el-button>
</div>
</el-drawer>
</template>
<script setup name="examDetailsDrawerRef">
import {ref, reactive, watch} from 'vue'
const props = defineProps({
});
const activeExamInfoDrawer = ref(false);
const activeExam = ref({});
//
const acceptParams = (params) => {
activeExamInfoDrawer.value = true;
activeExam.value = params.activeExam;
}
defineExpose({
acceptParams
})
</script>
<style lang="scss" scoped>
.drawer-main{
margin: 1%;
padding: 2%;
display: flex;
text-align: left;
.drawer-main-col{
padding: 10px 0px;
}
}
.drawer-footer{
padding-top: 15px;
display: flex;
justify-content: flex-end;
box-sizing: border-box;
}
</style>

View File

@ -213,7 +213,7 @@ const delStudent = (index) => {
const onSubmit = (formEl) => { const onSubmit = (formEl) => {
if (!formEl) return if (!formEl) return
// id // id
const classRoomId = sessionStore.get('curClassRoom.id') const classRoomId = sessionStore.get('curr.curClassRoom.id')
formEl.validate((valid) => { formEl.validate((valid) => {
if (valid) { if (valid) {
/** /**
@ -244,7 +244,8 @@ const onSubmit = (formEl) => {
entpcourseworklist: '[' + props.rows[i].entpcourseworklist + ']', entpcourseworklist: '[' + props.rows[i].entpcourseworklist + ']',
needMsgNotifine: 'false', needMsgNotifine: 'false',
msgkey: 'newclasswork', msgkey: 'newclasswork',
title: '作业任务', //title: '',
title: props.rows[i].title,
msgcontent: '', msgcontent: '',
teachername: userInfo.nickName, teachername: userInfo.nickName,
unixstamp: new Date().getTime(), unixstamp: new Date().getTime(),
@ -254,14 +255,16 @@ const onSubmit = (formEl) => {
ary.push(obj) ary.push(obj)
} }
} }
console.log('ary->', ary)
setLoading.value = true setLoading.value = true
saveByClassWorkArray({ saveByClassWorkArray({
classworkarray: JSON.stringify(ary) classworkarray: JSON.stringify(ary)
}) })
.then((res) => { .then((res) => {
setLoading.value = false setLoading.value = false
ElMessage.success('操作成功') ElMessage.success('操作成功')
emit('on-success', res.data) emit('on-success', res)
cloneDialog(formEl) cloneDialog(formEl)
}) })
.catch(() => { .catch(() => {

View File

@ -0,0 +1,35 @@
<template>
<el-drawer v-model="model" class="preview-drawer" :modal="true" :destroy-on-close="true" :with-header="false" :append-to-body="true"
size="50%">
<div class="drawer-content">
<el-image v-for="(url,index) in srcList" :key="index" :src="url" lazy />
</div>
</el-drawer>
</template>
<script setup>
import {ref} from 'vue'
import {getImgPath} from "@/api/file/third";
const model = defineModel()
//
const srcList = ref([])
const init = (id) => {
srcList.value = []
getImgPath({itemId:id}).then(res => {
if(res.code === 200){
res.data.forEach(item => {
item.subsets.forEach(previewItem => {
previewItem.previewFiles.forEach(fileItem => {
srcList.value.push(fileItem.fileUrl)
})
})
})
}
})
}
defineExpose({
init
})
</script>

View File

@ -8,9 +8,10 @@
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import { ref, onMounted } from 'vue'
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
const Remote = require('@electron/remote')
const userStore = useUserStore() const userStore = useUserStore()
const { ipcRenderer } = window.electron || {} const { ipcRenderer } = window.electron || {}
@ -37,6 +38,7 @@ const closeWindow = () => {
ElMessageBox.confirm('确认退出系统吗?', '提示', { ElMessageBox.confirm('确认退出系统吗?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
customClass: 'login-close-tool',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
userStore.logOut().then(() => { userStore.logOut().then(() => {
@ -47,8 +49,16 @@ const closeWindow = () => {
}) })
}).catch(() => { }); }).catch(() => { });
} }
</script>
onMounted(() =>{
isMaxSize.value = Remote.getCurrentWindow().isMaximized()
})
</script>
<style>
.login-close-tool {
-webkit-app-region: no-drag;
}
</style>
<style lang="scss" scoped> <style lang="scss" scoped>
.header-tool { .header-tool {
width: 100%; width: 100%;

View File

@ -16,8 +16,9 @@ export const isJson = (str) => {
/** /**
* @description processList 格式化试题 * @description processList 格式化试题
* @param {*} row * @param {*} row
* @param {*} aloneOption 选择题中选项是否为每行一个
*/ */
export const processList = (row) => { export const processList = (row, aloneOption=false) => {
for (var i = 0; i < row.length; i++) { for (var i = 0; i < row.length; i++) {
if (isJson(row[i].workanalysis)) { if (isJson(row[i].workanalysis)) {
//1、先默认格式化 格式化各项内容(待优化, 后续界面显示的为format的值) //1、先默认格式化 格式化各项内容(待优化, 后续界面显示的为format的值)
@ -55,19 +56,25 @@ export const processList = (row) => {
const workDescArr = element.split('#&') const workDescArr = element.split('#&')
let tmp = '' let tmp = ''
let j = 0 let j = 0
for (; j < workDescArr.length; j++) { for(; j<workDescArr.length; j++){
if (j % 2 == 0) { const char = String.fromCharCode(65+j);
tmp += `<div style='width:80%;display:flex;'>` if (aloneOption) {
tmp += `<div style='width:100%;display:flex;padding: 2px 0'>${char}.${workDescArr[j]}</div>`;
}
else {
if(j%2 == 0){
tmp += `<div style='width:100%;display:flex;'>`;
}
tmp += `<div style='padding-left:10px;width:50%;overflow:hidden;text-overflow:ellipsis;font-size:0.9em;'>${char}.${workDescArr[j]}</div>`;
if(j%2 == 1){
tmp += '</div>';
} }
const char = String.fromCharCode(65 + j)
tmp += `<div style='display:flex;margin-left:2%;width:35%;overflow:hidden;text-overflow:ellipsis;font-size:0.9em;'>${char}.${workDescArr[j]}</div>`
if (j % 2 == 1) {
tmp += '</div>'
} }
} }
// j此刻已自增1, 故当选项为单数时, 需要补充结束标签 // j此刻已自增1, 故当选项为单数时, 需要补充结束标签
if (j % 2 == 1) { if(!aloneOption && j%2 == 1){
tmp += '</div>' tmp += '</div>';
} }
// workDescArr为 [''] 表示为 判断题或者填空题,这里不需要选项 // workDescArr为 [''] 表示为 判断题或者填空题,这里不需要选项
@ -136,31 +143,36 @@ export const processList = (row) => {
* ] * ]
*/ */
let workDescArr = JSON.parse(row[i].workdesc) let workDescArr = JSON.parse(row[i].workdesc)
let workDescHtml = `<div style='width:80%;display:flex;>` let workDescHtml = `<div style='width:100%;display:flex;>`
workDescArr.map((item, index) => { workDescArr.map((item, index) => {
if (item.type == '单选题' || item.type == '多选题') { if (item.type == '单选题' || item.type == '多选题') {
workDescHtml += `<div style='width:80%;display:flex;'>${index + 1}. ${item.title}</div>` workDescHtml += `<div style='width:100%;display:flex;'>${index + 1}. ${item.title}</div>`
let tmp = '' let tmp = ''
let j = 0 let j = 0
let optionsArr = item.options let optionsArr = item.options
for (; j < optionsArr.length; j++) { for(; j<optionsArr.length; j++){
if (j % 2 == 0) { const char = String.fromCharCode(65+j);
tmp += `<div style='width:80%;display:flex;'>` if (aloneOption) {
tmp += `<div style='width:100%;display:flex;padding: 2px 0'>${char}.${optionsArr[j]}</div>`;
}
else {
if(j%2 == 0){
tmp += `<div style='width:100%;display:flex;'>`;
}
tmp += `<div style='padding-left: 10px; width: 50%'>${char}.${optionsArr[j]}</div>`;
if(j%2 == 1){
tmp += '</div>';
} }
const char = String.fromCharCode(65 + j)
tmp += `<div style='display:flex;margin-left: 2%; width: 36%'>${char}.${optionsArr[j]}</div>`
if (j % 2 == 1) {
tmp += '</div>'
} }
} }
// j此刻已自增1, 故当选项为单数时, 需要补充结束标签 // j此刻已自增1, 故当选项为单数时, 需要补充结束标签
if (j % 2 == 1) { if(!aloneOption && j%2 == 1){
tmp += '</div>' tmp += '</div>';
} }
workDescHtml += tmp workDescHtml += tmp
} else if (item.type == '填空题' || item.type == '判断题' || item.type == '主观题') { } else if (item.type == '填空题' || item.type == '判断题' || item.type == '主观题') {
workDescHtml += `<div style='width:80%;display:flex;'>${index + 1}. ${item.title}</div>` workDescHtml += `<div style='width:100%;display:flex;'>${index + 1}. ${item.title}</div>`
} }
}) })
workDescHtml += '</div>' workDescHtml += '</div>'
@ -265,18 +277,24 @@ export const processList = (row) => {
// 处理[选项显示] - 拼接ABCD首序号 // 处理[选项显示] - 拼接ABCD首序号
let tmp = '' let tmp = ''
let j = 0 let j = 0
for (; j < workDescArr.length; j++) { for(; j<workDescArr.length; j++){
if (j % 2 == 0) { const char = String.fromCharCode(65+j);
tmp += `<div style='width:80%;display:flex;'>` if (aloneOption) {
tmp += `<div style='width:100%;display:flex;padding: 2px 0'>${char}.${workDescArr[j]}</div>`;
} }
const char = String.fromCharCode(65 + j) else {
tmp += `<div style='display:flex;margin-left: 2%; width: 36%'>${char}.${workDescArr[j]}</div>` if(j%2 == 0){
if (j % 2 == 1) { tmp += `<div style='width:100%;display:flex;'>`;
tmp += '</div>' }
tmp += `<div style='padding-left: 10px; width: 50%'>${char}.${workDescArr[j]}</div>`;
if(j%2 == 1){
tmp += '</div>';
} }
} }
if (j % 2 == 0) { }
tmp += '</div>' if(!aloneOption && j%2== 0){
tmp += '</div>';
} }
row[i].workdescFormat = tmp row[i].workdescFormat = tmp

View File

@ -29,11 +29,15 @@
<div class="avatar-container"> <div class="avatar-container">
<div class="avatar-wrapper flex"> <div class="avatar-wrapper flex">
<el-dropdown class="right-menu-item hover-effect" @command="handleCommand"> <el-dropdown class="right-menu-item hover-effect" @command="handleCommand">
<img :src="dev_api + userStore.user.avatar" class="user-avatar" style="float: left" /> <el-image :src="dev_api + userStore.user.avatar" class="user-avatar" style="float: left">
<template #error>
<el-image :src="defaultUserImg" class="user-avatar" style="float: left" />
</template>
</el-image>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item @click="changePage('/profile')">个人中心</el-dropdown-item> <el-dropdown-item @click="changePage('/profile')">个人中心</el-dropdown-item>
<el-dropdown-item @click="changePage('/classReserv')">课程预约</el-dropdown-item> <!-- <el-dropdown-item @click="changePage('/classReserv')">课程预约</el-dropdown-item>-->
<el-dropdown-item @click="changePage('/class')">班级中心</el-dropdown-item> <el-dropdown-item @click="changePage('/class')">班级中心</el-dropdown-item>
<el-dropdown-item divided command="logout"> <el-dropdown-item divided command="logout">
<span>退出登录</span> <span>退出登录</span>
@ -80,6 +84,7 @@ import { updateUserInfo } from '@/api/system/user'
import logoIco from '@/assets/images/logo.png' import logoIco from '@/assets/images/logo.png'
import { listEvaluation } from '@/api/classManage/index' import { listEvaluation } from '@/api/classManage/index'
import { sessionStore } from '@/utils/store' import { sessionStore } from '@/utils/store'
import defaultUserImg from '@/assets/images/img-avatar.png'
// import Chat from '@/utils/chat' // im // import Chat from '@/utils/chat' // im
// if (!Chat.imChat) Chat.init() // if (!Chat.imChat) Chat.init()
@ -116,6 +121,12 @@ const headerMenus = [
id: 4, id: 4,
icon: '#icon-iconfontzhizuobiaozhunbduan3-1', icon: '#icon-iconfontzhizuobiaozhunbduan3-1',
disabled: true disabled: true
},
{
name: '实验室',
id: 5,
icon: '#icon-a-shiyanpingshiyanyanjiu',
path: '/experiment'
} }
] ]

View File

@ -10,7 +10,7 @@ import _ from 'lodash'
// import { diff } from 'jsondiffpatch' // import { diff } from 'jsondiffpatch'
// const Remote = isNode?require('@electron/remote'):{} // 远程模块 // const Remote = isNode?require('@electron/remote'):{} // 远程模块
const exArrs = ['subject'] // 不需要同步key-排除 const exArrs = ['subject','env','curr'] // 不需要同步key-排除
export function shareStorePlugin({store}) { export function shareStorePlugin({store}) {
store.$subscribe((mutation, state) => { // 自动同步 store.$subscribe((mutation, state) => { // 自动同步
@ -60,7 +60,7 @@ function stateSyncWatch(storeName, newState) {
const diffData = findDifferences(oldState, newState) const diffData = findDifferences(oldState, newState)
if(!_.keys(diffData).length) return // 没有变化就终止执行 if(!_.keys(diffData).length) return // 没有变化就终止执行
// 数据处理: 找出差异 // 数据处理: 找出差异
// console.log('state-change-diffData', diffData) // console.log('state-change-diffData', diffData, newState)
try { try {
let pinaValue = {} // store pina状态管理需要的数据格式 let pinaValue = {} // store pina状态管理需要的数据格式
// 数据转换处理 // 数据转换处理
@ -83,7 +83,7 @@ function stateSyncWatch(storeName, newState) {
// 没变化也终止执行 // 没变化也终止执行
if (_.isEqual(oldValAll, newValAll)) return if (_.isEqual(oldValAll, newValAll)) return
// 更新本地数据-session // 更新本地数据-session
sessionStore.set(key, newValAll) sessionStore.set(key, newValAll || null)
// 数据处理: pina-store // 数据处理: pina-store
const jsonStr = JSON.stringify(pinaValue) // 从新组装-json数据 const jsonStr = JSON.stringify(pinaValue) // 从新组装-json数据

View File

@ -49,6 +49,12 @@ export const constantRoutes = [
name: 'resource', name: 'resource',
meta: {title: '资源库'} meta: {title: '资源库'}
}, },
{
path: '/experiment',
component: () => import('@/views/experiment/index.vue'),
name: 'experiment',
meta: {title: '实验室'}
},
{ {
path: '/prepare', path: '/prepare',
component: () => import('@/views/prepare/index.vue'), component: () => import('@/views/prepare/index.vue'),

View File

@ -1,28 +0,0 @@
import { defineStore } from 'pinia'
import { } from '@/api/classTask/index.js'
import { listClassmain } from '@/api/classManage/index'
const useClassTaskStore = defineStore('classTask',{
state: () => ({
classListIds: [],
}),
actions: {
listClassmain(params) {
// 获取班级列表
return new Promise((resolve, reject) => {
listClassmain(params)
.then((res) => {
this.classListIds = res.rows&&res.rows.map((item) => item.id)
resolve(res)
})
.catch((error) => {
reject(error)
})
})
},
},
persist: true
})
export default useClassTaskStore

View File

@ -4,12 +4,17 @@ const overviewStore = defineStore(
{ {
state: () => { state: () => {
return { return {
tableList:[] tableList:[],
allData:[]
} }
}, },
actions: { actions: {
getTableList(data){ getTableList(data){
this.tableList = [...data] this.tableList = [...data]
},
//整理的所有列表
getAllData(data){
this.allData = [...data]
} }
} }
}) })

View File

@ -6,7 +6,7 @@ import { sessionStore } from '@/utils/store'
// 默认数据 // 默认数据
const defData = sessionStore.store || {} const defData = sessionStore.store || {}
const exArrs = ['subject'] const exArrs = ['subject','env','curr']
exArrs.forEach(k => Object.keys(defData).includes(k) && (delete defData[k])) exArrs.forEach(k => Object.keys(defData).includes(k) && (delete defData[k]))
// 延时 // 延时

View File

@ -167,3 +167,61 @@ export function getTomorrow() {
return tomorrow; return tomorrow;
} }
/**
* 当前日期的 前几天
* @param {*} index 天数
* @param {*} format true 返回中国标准时间Wed Oct 02 2024 08:00:00 GMT+0800 (中国标准时间) 格式 false 返回标准时间格式 YYYY-MM-DD
* @returns
*/
export function getTheOtherDay(index, format=true) {
let date = new Date();
var year = date.getFullYear()
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
var day = date.getDate()- index < 10 ? '0' + (date.getDate()- index) : date.getDate()- index
// 前 index 天的时间
if(format){
let tomorrow = `${year}-${month}-${day}`;
return new Date(tomorrow);
}else{
let tomorrow = `${year}-${month}-${day}`;
return tomorrow;
}
}
/**
* 当前日期的 后几天
* @param {*} index 天数
* @param {*} format true 返回中国标准时间Wed Oct 02 2024 08:00:00 GMT+0800 (中国标准时间) 格式 false 返回标准时间格式 YYYY-MM-DD
* @returns
*/
export function getTheOtheNextDay(index, format=true) {
const date = new Date();
var year = date.getFullYear()
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
var day = date.getDate()+ index < 10 ? '0' + (date.getDate()+ index) : date.getDate()+ index
// 前 index 天的时间
if(format){
const tomorrow = `${year}-${month}-${day}`;
return new Date(tomorrow);
}else{
const tomorrow = `${year}-${month}-${day}`;
return tomorrow;
}
}
/**
* Wed Oct 02 2024 08:00:00 GMT+0800 (中国标准时间) 转为日期格式: YYYY-MM-DD
*
* @param {*} format
* @returns
*/
export const getDateFormatDate = (newDate)=> {
const now = newDate; // new Date();
const year = now.getFullYear();
const month = (now.getMonth() + 1).toString().padStart(2, '0');
const day = now.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
}

View File

@ -3,8 +3,8 @@ import { getSignature } from "./index";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import request from '@/utils/request' import request from '@/utils/request'
let appId = "01ec9aa3"; let appId = "a3c84da2";
let secret = "M2QxMDAxMjYyYTEzODMwMGRkZTQ4NmUy"; let secret = "OTZkZDdhNDQ4MDk0NTZiYjcyY2RjZmYz";
let timestamp = Math.floor(Date.now() / 1000); let timestamp = Math.floor(Date.now() / 1000);
let signature = getSignature(appId, secret, timestamp); let signature = getSignature(appId, secret, timestamp);
let req = (url, type, data)=>{ let req = (url, type, data)=>{

View File

@ -117,4 +117,8 @@ export const coursewareTypeList = [
label:'素材', label:'素材',
value:6 value:6
}, },
{
label:'视频',
value:12
},
] ]

View File

@ -131,7 +131,7 @@ export const createWindow = async (type, data) => {
data.isConsole = true // 是否开启控制台 data.isConsole = true // 是否开启控制台
data.isWeb = false // 是否开启web安全 data.isWeb = false // 是否开启web安全
data.option = {...defOption, ...option} data.option = {...defOption, ...option}
wins_tool = await toolWindow(data) wins_tool = await toolWindow(type, data)
wins_tool.type = type // 唯一标识 wins_tool.type = type // 唯一标识
wins_tool.show() wins_tool.show()
wins_tool.setFullScreen(true) // 设置窗口为全屏 wins_tool.setFullScreen(true) // 设置窗口为全屏
@ -161,7 +161,7 @@ export const createWindow = async (type, data) => {
} }
data.isConsole = true // 是否开启控制台 data.isConsole = true // 是否开启控制台
data.option = {...defOption, ...option} data.option = {...defOption, ...option}
const win = await toolWindow(data) const win = await toolWindow(type, data)
win.type = type // 唯一标识 win.type = type // 唯一标识
win.show() win.show()
win.setFullScreen(true) // 设置窗口为全屏 win.setFullScreen(true) // 设置窗口为全屏
@ -193,13 +193,32 @@ export const createWindow = async (type, data) => {
} }
data.isConsole = true // 是否开启控制台 data.isConsole = true // 是否开启控制台
data.option = {...defOption, ...option} data.option = {...defOption, ...option}
winChild = await toolWindow(data) winChild = await toolWindow(type, data)
winChild.type = type // 唯一标识 winChild.type = type // 唯一标识
winChild.show() winChild.show()
winChild.setFullScreen(false) // 设置窗口为全屏 winChild.setFullScreen(false) // 设置窗口为全屏
eventHandles(type, winChild) // 事件监听处理 eventHandles(type, winChild) // 事件监听处理
return winChild return winChild
} }
case 'open-win': { // 创建-新窗口
const option = data.option||{}
const defOption = {
show: false,
frame: true, // 无边框
autoHideMenuBar: true,
maximizable: false,
}
data.isConsole = true // 是否开启控制台
data.option = {...defOption, ...option}
const win = await toolWindow(type, data)
win.type = type // 唯一标识
win.show()
win.maximize();
// win.setFullScreen(true) // 设置窗口为全屏
if (import.meta.env.VITE_SHOW_DEV_TOOLS === 'true') win.webContents.openDevTools() // 打开调试工具
eventHandles(type, win) // 事件监听处理
break
}
default: default:
break break
} }
@ -213,7 +232,7 @@ export const createWindow = async (type, data) => {
* @author: zdg * @author: zdg
* @date 2021-07-05 14:07:01 * @date 2021-07-05 14:07:01
*/ */
export function toolWindow({url, isConsole, isWeb=true, option={}}) { export function toolWindow(type, {url, isConsole, isWeb=true, option={}}) {
// width = window.screen.width // width = window.screen.width
let width = option?.width || 800 let width = option?.width || 800
let height = option?.height || 600 let height = option?.height || 600
@ -221,7 +240,7 @@ export function toolWindow({url, isConsole, isWeb=true, option={}}) {
const devUrl = `${BaseUrl}${url}` const devUrl = `${BaseUrl}${url}`
const buildUrl = path.join(__dirname, 'index.html') const buildUrl = path.join(__dirname, 'index.html')
const urlAll = isDev ? devUrl : buildUrl const urlAll = isDev ? devUrl : buildUrl
return new Promise((resolve) => { return new Promise(async(resolve) => {
const config = { const config = {
width, height, width, height,
icon: path.join(appPath, '/resources/logo2.ico'), icon: path.join(appPath, '/resources/logo2.ico'),
@ -236,6 +255,9 @@ export function toolWindow({url, isConsole, isWeb=true, option={}}) {
} }
// 创建-新窗口 // 创建-新窗口
let win = new Remote.BrowserWindow(config) let win = new Remote.BrowserWindow(config)
// 新窗口-创建事件(如:主进程加载远程服务)
await ipcMsgInvoke('new-window', {id:win.id, type})
console.log(`渲染进程 [${type}]: 窗口创建-成功${Date.now()}`)
if (!isDev) win.loadFile(urlAll,{hash: url}) // 加载文件 if (!isDev) win.loadFile(urlAll,{hash: url}) // 加载文件
else win.loadURL(urlAll) // 加载url else win.loadURL(urlAll) // 加载url
win.once('ready-to-show', () => { // 窗口加载完成 win.once('ready-to-show', () => { // 窗口加载完成
@ -281,7 +303,7 @@ const eventHandles = (type, win) => {
}) })
// 新窗口-创建事件(如:主进程加载远程服务) // 新窗口-创建事件(如:主进程加载远程服务)
ipcRenderer.send('new-window', {id:win.id, type}) // ipcRenderer.send('new-window', {id:win.id, type})
} }
switch(type) { switch(type) {
case 'tool-sphere': { // 创建-悬浮球 case 'tool-sphere': { // 创建-悬浮球
@ -340,7 +362,7 @@ const eventHandles = (type, win) => {
// 监听窗口的激活事件 // 监听窗口的激活事件
win.on('focus', async () => { win.on('focus', async () => {
toolState.isTaskWin=true toolState.isTaskWin=true
win&&win.reload(); //刷新该窗口 // win&&win.reload(); //刷新该窗口
}); });
// 监听窗口关闭事件 // 监听窗口关闭事件
win.on('closed', function () { win.on('closed', function () {
@ -357,6 +379,18 @@ const eventHandles = (type, win) => {
publicMethods(on) // 加载公共方法 publicMethods(on) // 加载公共方法
break; break;
} }
case 'open-win': { // 打开新窗口
// 监听窗口关闭事件
win.on('closed', function () {
win&&win.destroy()
});
const on = {
onClosed: () => {
}
}
publicMethods(on) // 加载公共方法
break
}
default: default:
break break
} }

View File

@ -4,30 +4,26 @@
<div class="class-reserv-tabs"> <div class="class-reserv-tabs">
<el-segmented v-model="tabActive" block :options="tabOptions" size="large" /> <el-segmented v-model="tabActive" block :options="tabOptions" size="large" />
</div> </div>
<div v-if="tabActive === '已结束'"> <div v-if="tabActive === '已批改'">
<div class="demo-date-picker"> <div class="demo-date-picker">
<div class="block">
<el-date-picker <el-date-picker
v-model="EndDate" v-model="startEndDate"
type="date" type="daterange"
format="YYYY-MM-DD" start-placeholder="Start Date"
value-format="YYYY-MM-DD" end-placeholder="End Date"
placeholder="请选择截止日期" :default-time="defaultTime"
size="large" @change="changeStartEndDate"
:disabled-date="disabledDate"
@change="changeEndDate"
/> />
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="class-reserv-body"> <div class="class-reserv-body">
<!-- loading <el-skeleton :rows="5" animated />--> <!-- loading <el-skeleton :rows="5" animated />-->
<el-skeleton :rows="5" :loading="loading" animated /> <el-skeleton :rows="5" :loading="loading" animated />
<div v-if="classWorkList.length > 0"> <div v-if="classWorkList.length > 0">
<task-item <task-item
v-for="(item, index) in activeDataList" v-for="(item, index) in activeDataList"
v-show="tabActive === '进行中'" v-show="tabActive === '待批改'"
:key="index" :key="index"
:item="item" :item="item"
:tabactive="tabActive" :tabactive="tabActive"
@ -36,7 +32,7 @@
></task-item> ></task-item>
<task-item <task-item
v-for="(item, index) in doneDataList" v-for="(item, index) in doneDataList"
v-show="tabActive === '已结束'" v-show="tabActive === '已批改'"
:key="index" :key="index"
:item="item" :item="item"
:tabactive="tabActive" :tabactive="tabActive"
@ -52,7 +48,6 @@
></el-empty> ></el-empty>
</div> </div>
</div> </div>
<!-- <item-dialog ref="itemDialogRef" @cle-click="closeDialog"></item-dialog> -->
</el-container> </el-container>
</template> </template>
@ -61,25 +56,29 @@ import { ref, onMounted, onUnmounted, computed, watch } from 'vue'
import { listByDeadDate, listClassworkdata } from '@/api/classTask' import { listByDeadDate, listClassworkdata } from '@/api/classTask'
import TaskItem from '@/views/classTask/container/classTask/task-item.vue' import TaskItem from '@/views/classTask/container/classTask/task-item.vue'
// import ItemDialog from '@/views/classTask/container/item-dialog.vue'
import { useToolState } from '@/store/modules/tool' import { useToolState } from '@/store/modules/tool'
import { getCurrentTime } from '@/utils/date' import { getDateFormatDate, getTheOtherDay, getTheOtheNextDay } from '@/utils/date'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import useClassTaskStore from "@/store/modules/classTask";
import {createWindow} from '@/utils/tool' import {createWindow} from '@/utils/tool'
import {sessionStore} from '@/utils/store'
import {debounce } from '@/utils/comm' import {debounce } from '@/utils/comm'
const toolState = useToolState(); const toolState = useToolState();
const classTaskStore = useClassTaskStore()
const userStore = useUserStore().user const userStore = useUserStore().user
// const itemDialogRef = ref(null)
const tabOptions = ref(['进行中', '已结束']) const tabOptions = ref(['待批改', '已批改'])
const tabActive = ref('进行中') const tabActive = ref('待批改')
const dataList = ref([]) const dataList = ref([])
const EndDate = ref(getCurrentTime('YYYY-MM-DD')) // 23
const startEndDate = ref([
getTheOtherDay(3),
getTheOtheNextDay(3),
])
const defaultTime = ref<[Date, Date]>([
getTheOtherDay(3),
getTheOtheNextDay(3),
])
// //
const classWorkList = ref([]) const classWorkList = ref([])
@ -91,23 +90,18 @@ const loading = ref(false)
const activeDataList = computed(() => { const activeDataList = computed(() => {
return classWorkList.value return classWorkList.value
}) })
const doneDataList = computed(() => {
return classWorkList.value
})
const deleteReserv = (item) => { const deleteReserv = (item) => {
console.log('删除待开发', item) console.log('删除待开发', item)
// dataList.value = dataList.value.filter((is) => { // dataList.value = dataList.value.filter((is) => {
// return is.id !== item.id // return is.id !== item.id
// }) // })
} }
const doneDataList = computed(() => {
return classWorkList.value
})
// const changeStartEndDate = (val) => {
const disabledDate = (time) => { console.log('起止日期改变', val)
return time.getTime() > Date.now()
}
//
const changeEndDate = (val) => {
console.log('截止日期改变', val)
getData() // getData() //
} }
@ -115,63 +109,35 @@ const changeEndDate = (val) => {
const getData = async () => { const getData = async () => {
classWorkList.value = [] classWorkList.value = []
loading.value = true loading.value = true
// 1 // 1
// getClassList()
// 2
await getClassWorkList() await getClassWorkList()
// 3 // 2
getStudentClassWorkData() getStudentClassWorkData()
loading.value = false loading.value = false
} }
/** /**
* 1获取班级列表数据 * 1获取班级作业
* TODO 这里暂时取班级id的list后续需要在修改
*/
const getClassList = () => {
if(classTaskStore.classListIds.length==0){
// ids idlist
classTaskStore.listClassmain({ classuserid: userStore.userId, pageSize: 100, status: 'open' })
}
}
/**
* 2获取班级作业
*/ */
const getClassWorkList = async () => { const getClassWorkList = async () => {
//if(classTaskStore.classListIds.length>0){
{
// homeworklist // homeworklist
const response = await listByDeadDate({ const response = await listByDeadDate({
edituserid: userStore.userId, // id edituserid: userStore.userId, // id
edustage: userStore.edustage, // edustage: userStore.edustage, //
edusubject: userStore.edusubject,// edusubject: userStore.edusubject,//
// deaddate: tabActive.value === ''? getTomorrow() : EndDate.value,// startdate: tabActive.value === '待批改'? '' : getDateFormatDate(startEndDate.value[0]),
deaddate: EndDate.value,// deaddate: tabActive.value === '待批改'? '' : getDateFormatDate(startEndDate.value[1]),//
status: '1', // 1- status: tabActive.value === '待批改'? '1' : '2', // 1-
orderby: 'deaddate DESC', orderby: 'deaddate DESC',
pageSize: 100, pageSize: 100,
}) })
/** let list = response.rows || [];
* 2024-10-17 由于 后面截止时间加了 时分特加判断
* 1进行中以前是以明天判断现改为传当天的日期并根据当前日期的时分与截止日期进行判断
* 2已结束以前默认是以明天判断现依然以明天为判断并根据当前日期时分大于截止日期时分判断
*/
let list = [];
if(tabActive.value === '进行中'){
//
list = response.rows&&response.rows.filter(item => item.deaddate && getCurrentTime('YYYY-MM-DD HH:mm') < item.deaddate); //
}else{
list = response.rows&&response.rows.filter(item => item.deaddate && getCurrentTime('YYYY-MM-DD HH:mm') > item.deaddate); //
}
for (var i = 0; i < list.length; i++) { for (var i = 0; i < list.length; i++) {
// //
list[i].workdatalist = [] list[i].workdatalist = [] //
list[i].workdatacount = 0 //
list[i].workdatalistVisible = false list[i].workdatalistVisible = false
list[i].workdatafeedbackcount = 0 // list[i].feedtimelength = 0 //
list[i].feedtimelength = 0
list[i].rightAnswerCount = 0 list[i].rightAnswerCount = 0
list[i].scoingRate = 0 + '%' // list[i].scoingRate = 0 + '%' //
list[i].averagetime = 0 // list[i].averagetime = 0 //
@ -200,19 +166,11 @@ const getClassWorkList = async () => {
} else { } else {
list[i].entpcourseworklistarray = [] list[i].entpcourseworklistarray = []
} }
// classworkdatastudentids
if (
list[i].classworkdatastudentids != '' &&
list[i].classworkdatastudentids != null &&
list[i].classworkdatastudentids != 'null'
) {
const stuList = JSON.parse('[' + list[i].classworkdatastudentids + ']')
list[i].workdatacount = stuList.length
} }
}
// (workdatacount)>0 // (workdataresultsum)>0
if (list && list.length > 0) { if (list && list.length > 0) {
classWorkList.value = list && list.filter((item) => item.workdatacount > 0) classWorkList.value = list && list.filter((item) => item.workdataresultsum > 0)
//TODO total //TODO total
total.value = 0 total.value = 0
}else{ }else{
@ -220,38 +178,25 @@ const getClassWorkList = async () => {
total.value = 0 total.value = 0
} }
loading.value = false loading.value = false
}
} }
/** /**
* 3获取多个班级学生作业数据 * 2获取多个班级学生作业数据
* 查询已交的列表
* @param workList 需要更新的作业list
* @param Refresh true 不用刷新false 需要刷新
*/ */
const getStudentClassWorkData = async() => { const getStudentClassWorkData = async(workList = [], Refresh = true) => {
// const { chapterId } = await useGetHomework(props.bookobj.node) // (workdataresultcount )
// this.entpcourseid = chapterId let SubmitClWorkList = [];
//if(classTaskStore.classListIds.length>0){ if(Refresh){
// listClassworkdataByDeadDate({ SubmitClWorkList = classWorkList.value.filter((item) => item.workdataresultcount > 0) ;
// edituserid: userStore.userId, // id }else{
// classids: classTaskStore.classListIds.join(','), SubmitClWorkList = workList;
// edusubject: userStore.edusubject,// }
// deaddate: tabActive.value === ''? getTomorrow() : EndDate.value,//
// deaddate: EndDate.value,//
// //status: '1', // 1-
// orderby: "deaddate DESC",//
// pageSize: 1000,
// })
// listClassworkdataNew({ console.log('有提交的作业', SubmitClWorkList)
// classworkids: ids, // id const ids = SubmitClWorkList&&SubmitClWorkList.map((item) => item.id).join(',');
// edituserid: userStore.userId, // id
// edusubject: userStore.edusubject,//
// evalStatus: 1,
// pageSize: 1000,
// })
{
const ids = classWorkList.value.map((item) => item.id).join(',');
if (ids == '') { if (ids == '') {
return; return;
} }
@ -261,13 +206,14 @@ const getStudentClassWorkData = async() => {
}).then((res) => { }).then((res) => {
for (var t = 0; t < classWorkList.value.length; t++) { for (var t = 0; t < classWorkList.value.length; t++) {
for (var i = 0; i < res.rows.length; i++) { for (var i = 0; i < res.rows.length; i++) {
// finishtimelength != '0'
if (res.rows[i].classworkid == classWorkList.value[t].id && res.rows[i].finishtimelength != '0') { if (res.rows[i].classworkid == classWorkList.value[t].id && res.rows[i].finishtimelength != '0') {
console.log('==================') console.log('==================')
// / // /
// resultcount0 // resultcount0
classWorkList.value[t].workdatafeedbackcount++ //classWorkList.value[t].workdataresultcount++
// //
classWorkList.value[t].feedtimelength += parseInt(res.rows[i].finishtimelength) classWorkList.value[t].feedtimelength += parseInt(res.rows[i].finishtimelength)
// //
@ -305,36 +251,43 @@ const getStudentClassWorkData = async() => {
} }
} }
} }
// workdatacount // workdataresultsum
if (res.rows[i].classworkid == classWorkList.value[t].id) { if (res.rows[i].classworkid == classWorkList.value[t].id) {
classWorkList.value[t].workdatalist.push(res.rows[i]) classWorkList.value[t].workdatalist.push(res.rows[i])
} }
} }
// workdatacount0
if (
classWorkList.value[t].workdataresultcount > 0 && // : workdataresultcount ; workdataresultsum 0;
classWorkList.value[t].workdatacount > 0 if (classWorkList.value[t].workdataresultcount > 0 && classWorkList.value[t].workdataresultsum > 0 ) {
) {
classWorkList.value[t].finishpercent = parseInt( classWorkList.value[t].finishpercent = parseInt(
(classWorkList.value[t].workdataresultcount / classWorkList.value[t].workdatacount) * 100 (classWorkList.value[t].workdataresultcount / classWorkList.value[t].workdataresultsum) * 100
) )
} else { } else {
classWorkList.value[t].finishpercent = 0 classWorkList.value[t].finishpercent = 0
} }
/** 计算 已批阅进度 */
// workdataresultsum teacherrationgcount
// (-) / * 100
if (classWorkList.value[t].workdataresultsum > 0) {
classWorkList.value[t].teacherCorrectionProgress = parseInt(
((classWorkList.value[t].teacherrationgcount) / classWorkList.value[t].workdataresultsum) * 100
)
} else {
classWorkList.value[t].teacherCorrectionProgress = 0
}
// //
// 2024-04-12by jackyshen // 2024-04-12by jackyshen
// // /
if (classWorkList.value[t].workdatafeedbackcount > 0) { if (classWorkList.value[t].workdataresultcount > 0) {
classWorkList.value[t].averagetime = Math.ceil(classWorkList.value[t].feedtimelength / classWorkList.value[t].workdatafeedbackcount / 60).toFixed(0) classWorkList.value[t].averagetime = Math.ceil(classWorkList.value[t].feedtimelength / classWorkList.value[t].workdataresultcount / 60).toFixed(0)
} else { } else {
classWorkList.value[t].averagetime = 0 classWorkList.value[t].averagetime = 0
} }
//
//
// /**100 // /**100
if ( if (
classWorkList.value[t].entpcourseworklistarray && classWorkList.value[t].entpcourseworklistarray &&
@ -343,17 +296,15 @@ const getStudentClassWorkData = async() => {
var dd = var dd =
(classWorkList.value[t].rightAnswerCount / (classWorkList.value[t].rightAnswerCount /
(classWorkList.value[t].entpcourseworklistarray.length * (classWorkList.value[t].entpcourseworklistarray.length *
classWorkList.value[t].workdatacount)) * classWorkList.value[t].workdataresultsum)) *
100 100
classWorkList.value[t].scoingRate = dd.toFixed(0) + '%' classWorkList.value[t].scoingRate = dd.toFixed(0) + '%'
} else { } else {
classWorkList.value[t].scoingRate = '0%' classWorkList.value[t].scoingRate = '0%'
} }
//
//
} }
}) })
}
} }
@ -379,6 +330,7 @@ const getStudentClassWorkDataPolling = () => {
getStudentVisible() getStudentVisible()
// //
pollingST.value = setInterval(() => { pollingST.value = setInterval(() => {
console.log('轮询查询学生作业进度')
getStudentVisible() getStudentVisible()
}, 1000 * 10) }, 1000 * 10)
} }
@ -413,75 +365,56 @@ onUnmounted(() => {
// [] - // [] -
const getStudentVisible = async () => { const getStudentVisible = async () => {
if (classTaskStore.classListIds.length <= 0) { if(!classWorkList.value.length>0){
return return;
} }
//
const response = await listByDeadDate({ const response = await listByDeadDate({
classidarray: classTaskStore.classListIds.join(','),
edituserid: userStore.userId, // id edituserid: userStore.userId, // id
edustage: userStore.edustage,// edustage: userStore.edustage, //
edusubject: userStore.edusubject,// edusubject: userStore.edusubject,//
// deaddate: tabActive.value === ''? getTomorrow() : EndDate.value,// startdate: tabActive.value === '待批改'? '' : getDateFormatDate(startEndDate.value[0]),
deaddate: EndDate.value,// deaddate: tabActive.value === '待批改'? '' : getDateFormatDate(startEndDate.value[1]),//
status: '1', // 1- status: tabActive.value === '待批改'? '1' : '2', // 1-
// orderby: 'concat(deaddate,uniquekey) DESC',
orderby: 'deaddate DESC', orderby: 'deaddate DESC',
pageSize: 100 pageSize: 100,
}) })
/** let list = response.rows || [];
* 2024-10-17 由于 后面截止时间加了 时分特加判断
* 1进行中以前是以明天判断现改为传当天的日期并根据当前日期的时分与截止日期进行判断 let newList = [];
* 2已结束以前默认是以明天判断现依然以明天为判断并根据当前日期时分大于截止日期时分判断 for(let i = 0; i < classWorkList.value.length; i++){
*/ // listidlistid listlist
let list = []; const isList = list.filter((item) => item.id === classWorkList.value[i].id);
if(tabActive.value === '进行中'){ if(isList.length === 0){
// // listidlistidlist
list = response.rows&&response.rows.filter(item => item.deaddate && getCurrentTime('YYYY-MM-DD HH:mm') < item.deaddate); // classWorkList.value.splice(i,1);
}else{
list = response.rows&&response.rows.filter(item => item.deaddate && getCurrentTime('YYYY-MM-DD HH:mm') > item.deaddate); //
} }
for(let j = 0; j < list.length; j++){
const curWorkList = list // workdataresultcount ;
if(classWorkList.value[i].id === list[j].id && classWorkList.value[i].workdataresultcount != list[j].workdataresultcount){
/** // =
* warn: 这里仅更新了finishpercent(进度条), 且当前作业布置推送新任务时, curWorkList中会查到新的任务与当前页面中this.classWorkList长度不一致, newList.push(list[j]);
* 故这里需循环this.classWorkList且只更新当前页面中的存在的任务进度 }
*/ // teacherrationgcount
for (let t = 0; t < classWorkList.value.length; t++) { if(classWorkList.value[i].id === list[j].id && classWorkList.value[i].teacherrationgcount != list[j].teacherrationgcount){
// [] //
// if( getDateTime > classWorkList.value[t].deaddate ){ if (classWorkList.value[i].workdataresultsum > 0) {
// continue; //
// } classWorkList.value[i].teacherrationgcount = list[j].teacherrationgcount;
// (index) classWorkList.value[i].teacherCorrectionProgress = parseInt(
let curWork = curWorkList.find((work) => work.id === classWorkList.value[t].id) ((list[j].teacherrationgcount) / list[j].workdataresultsum) * 100
// workdataresultcount workdatacount0
if (curWork && curWork.workdataresultcount > 0 && classWorkList.value[t].workdatacount > 0) {
classWorkList.value[t].workdataresultcount = curWork.workdataresultcount
//
classWorkList.value[t].finishpercent = parseInt(
(classWorkList.value[t].workdataresultcount / classWorkList.value[t].workdatacount) * 100
) )
//
if (classWorkList.value[t].workdatafeedbackcount > 0) {
classWorkList.value[t].averagetime = Math.ceil(classWorkList.value[t].feedtimelength / classWorkList.value[t].workdatafeedbackcount / 60).toFixed(0)
} else { } else {
classWorkList.value[t].averagetime = 0 classWorkList.value[i].teacherCorrectionProgress = 0
}
//
classWorkList.value[t].teacherrationgcount = curWork.teacherrationgcount
} else {
//
if(curWork && curWork.workdataresultcount == 0){
//
classWorkList.value[t].teacherrationgcount = curWork.teacherrationgcount
}
classWorkList.value[t].finishpercent = 0
} }
} }
}
return 1 }
if(newList.length>0){
//
const list = newList&&newList.filter((item) => item.workdataresultcount > 0) ;
getStudentClassWorkData(list,false);
}
} }
@ -489,16 +422,27 @@ const getStudentVisible = async () => {
watch( watch(
() => [dataList, toolState.isTaskWin], () => [dataList, toolState.isTaskWin],
() => { () => {
console.log('=监听到批改窗口打开了===', toolState.isTaskWin) console.log('监听--批改窗口是否打开===', toolState.isTaskWin)
if(!toolState.isTaskWin){ if(!toolState.isTaskWin){
if(tabActive.value === '待批改'){
closeDialog();// closeDialog();//
} }
} }
}
) )
watch(tabActive, (newVal,oldVal)=>{ watch(tabActive, (newVal,oldVal)=>{
console.log('newVal',newVal); console.log('newVal',newVal);
getData() // getData() //
if(newVal === '待批改'){
//
console.log('监听---开启轮询')
closeDialog();
}else{
//
console.log('监听---关闭轮询')
clearInterval(pollingST.value);
}
}) })
</script> </script>

View File

@ -72,15 +72,15 @@ const props = defineProps({
}, },
}) })
let studentList = ref([]) // let studentList = ref([]) //
const stuHasAnswers = ref([]) //
// - // -
const initData = () => { const initData = () => {
console.log('xxx', props)
// window.test = activeCourse // window.test = activeCourse
studentList.value = props.activeData.studentList || [] studentList.value = props.activeData.studentList || []
const activeWorkFeedList = props.activeData.workFeedList || [] const activeWorkFeedList = props.activeData.workFeedList || []
const quizlist = props.activeData.quizlist || [] const quizlist = props.activeData.quizlist || []
console.log(quizlist,'quizlist');
// //
let data = quizlist.map(o => { let data = quizlist.map(o => {
// //
@ -90,7 +90,9 @@ const initData = () => {
let rightIds = [] // let rightIds = [] //
let hasAnswers= [] // let hasAnswers= [] //
let timeAnalyse = [] // let timeAnalyse = [] //
// let subjectCourese = [] //
const quizFeedList = activeWorkFeedList.filter(f => f.entpcourseworkid == o.id) // const quizFeedList = activeWorkFeedList.filter(f => f.entpcourseworkid == o.id) //
let children = [] let children = []
const allStudents = []; const allStudents = [];
if (o.worktype == '单选题') { // '','' if (o.worktype == '单选题') { // '',''
@ -101,6 +103,11 @@ const initData = () => {
// id // id
const studentIds = quizFeedList.filter(f => f.feedcontent==v&&f.finishtimelength!='0').map(f => f.studentid)||[]; const studentIds = quizFeedList.filter(f => f.feedcontent==v&&f.finishtimelength!='0').map(f => f.studentid)||[];
accSum += studentIds.length; accSum += studentIds.length;
//
timeAnalyse = quizFeedList.reduce((acc, cur) => {
return acc + (cur.timelength ? Number(cur.timelength) : 0);
},0)
if (isOk) { if (isOk) {
activeIds.push(...studentIds) activeIds.push(...studentIds)
} }
@ -128,6 +135,10 @@ const initData = () => {
if (studentIds.length>0) { if (studentIds.length>0) {
allStudents.push(...studentIds); allStudents.push(...studentIds);
} }
//
timeAnalyse = quizFeedList.reduce((acc, cur) => {
return acc + (cur.timelength ? Number(cur.timelength) : 0);
},0)
if(isOk) { if(isOk) {
activeIds=[...new Set(activeIds.concat(studentIds))] // activeIds=[...new Set(activeIds.concat(studentIds))] //
} }
@ -136,8 +147,12 @@ const initData = () => {
}) })
} }
else if (o.worktype == '填空题') { // else if (o.worktype == '填空题') { //
const regex = /<!--BA-->(.*?)<!--EA-->/g // <!--BA-->xxx<!--EA--> let title = o.title.replace(/_{3,}/g, '_____'); //3-10线5
children = (o.title||'').match(regex).map((v,i) => { let regex = /<!--BA-->(.*?)<!--EA-->/g // <!--BA-->xxx<!--EA-->
if (title.indexOf('_____') != -1) {
regex = /_{5}/g // <!--BA-->xxx<!--EA-->
}
children = (title||'').match(regex).map((v,i) => {
const def = `填空项 ${i+1}` const def = `填空项 ${i+1}`
//const code = '(&emsp;)' //const code = '(&emsp;)'
const code = '(略)', txt=v const code = '(略)', txt=v
@ -146,6 +161,10 @@ const initData = () => {
activeIds=[...new Set(activeIds.concat(studentIds))] // activeIds=[...new Set(activeIds.concat(studentIds))] //
hasAnswers=[...new Set(hasAnswers.concat(studentIds))] hasAnswers=[...new Set(hasAnswers.concat(studentIds))]
accSum = activeIds.length accSum = activeIds.length
//
timeAnalyse = quizFeedList.reduce((acc, cur) => {
return acc + (cur.timelength ? Number(cur.timelength) : 0);
},0)
return { def, code, txt, isOk:true, studentIds } return { def, code, txt, isOk:true, studentIds }
}) })
} else if (o.worktype == '判断题') { // } else if (o.worktype == '判断题') { //
@ -187,6 +206,10 @@ const initData = () => {
accSum += studentIds.length; accSum += studentIds.length;
if(isOk) activeIds.push(...studentIds) if(isOk) activeIds.push(...studentIds)
hasAnswers.push(...studentIds) hasAnswers.push(...studentIds)
//
timeAnalyse = quizFeedList.reduce((acc, cur) => {
return acc + (cur.timelength ? Number(cur.timelength) : 0);
},0)
return { def: v, code: v, isOk, studentIds } return { def: v, code: v, isOk, studentIds }
}) })
} else { // } else { //
@ -196,6 +219,10 @@ const initData = () => {
activeIds=[...new Set(activeIds.concat(studentIds))] // activeIds=[...new Set(activeIds.concat(studentIds))] //
hasAnswers=[...new Set(hasAnswers.concat(studentIds))] hasAnswers=[...new Set(hasAnswers.concat(studentIds))]
accSum = activeIds.length accSum = activeIds.length
//
timeAnalyse = quizFeedList.reduce((acc, cur) => {
return acc + (cur.timelength ? Number(cur.timelength) : 0);
},0)
children = [{ def, code, isOk:true, studentIds }] children = [{ def, code, isOk:true, studentIds }]
} }
@ -215,11 +242,10 @@ const initData = () => {
} }
// def: type active: points: , accSum // def: type active: points: , accSum
return { def: o, id: o.id, type: o.worktype, active: [], points, accSum, rightSum, children,hasAnswers } return { def: o, id: o.id, type: o.worktype, active: [], points, accSum, rightSum, children,hasAnswers,timeAnalyse,score:o.workScore }
}) })
console.log('获取数据: ', data) if (data.length === 0) return
stuHasAnswers.value = [...data[0].hasAnswers] useOverview.getAllData([...data])
provide('hasAnswer', stuHasAnswers.value)
} }
// 0-100 // 0-100
const percent = v => v > 1 ? 1 : v < 0 ? 0 : Math.round(v * 100) const percent = v => v > 1 ? 1 : v < 0 ? 0 : Math.round(v * 100)
@ -232,6 +258,20 @@ const isJson = str => {if(typeof str == 'string'){
if(typeof res == 'object' && res) return true if(typeof res == 'object' && res) return true
} catch (error) {}}return false } catch (error) {}}return false
} }
//
const groupByField = (array, field) => {
const groupedMap = {};
array.forEach(item => {
const key = item[field];
if (!groupedMap[key]) {
groupedMap[key] = [];
}
groupedMap[key].push(item);
});
//
return Object.values(groupedMap);
}
watch(() => props.tableList,() => { watch(() => props.tableList,() => {
useOverview.getTableList(props.tableList) useOverview.getTableList(props.tableList)

View File

@ -14,8 +14,8 @@
</template> </template>
<script setup> <script setup>
import Echarts from '@/views/classTask/container/classOverview/distribution/echarts.vue' import Echarts from './distribution/echarts.vue'
import StuList from "@/views/classTask/container/classOverview/distribution/stuList.vue"; import StuList from "./distribution/stuList.vue";
</script> </script>
<style scoped> <style scoped>

View File

@ -5,7 +5,7 @@
</template> </template>
<script setup> <script setup>
import { ref, nextTick, watch, inject,watchEffect } from 'vue'; import { ref, nextTick, watch } from 'vue';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import overviewStore from '@/store/modules/overview'; import overviewStore from '@/store/modules/overview';
const useOverview = overviewStore(); const useOverview = overviewStore();
@ -22,9 +22,9 @@ const dataList = ref([
{ name: '不及格', value: 0, rating: 5, max: 59, min: 0 }, { name: '不及格', value: 0, rating: 5, max: 59, min: 0 },
]); ]);
// //
const hasAnswersData = ref([]);
const hasStudents = ref([]) const hasStudents = ref([])
// //
function getColor(name) { function getColor(name) {
const colorMap = { const colorMap = {
@ -57,7 +57,7 @@ function initChart() {
series: [{ series: [{
name: '数据', name: '数据',
type: 'pie', type: 'pie',
radius: '50%', // radius: '90%', //
data: filteredData.map(item => ({ data: filteredData.map(item => ({
name: item.name, name: item.name,
value: item.value, value: item.value,
@ -105,18 +105,23 @@ const showEcharts = () => {
} }
// //
watch(() => useOverview.tableList, () => { watch(() => useOverview.tableList, () => {
hasStudents.value = useOverview.tableList.filter(item => hasAnswersData.value.includes(item.studentid)).map(item => item); //
const subType = useOverview.allData.map(item => item.type)
const objectiveQuestion = ['单选题','多选题','判断题']
if( !subType.every(item => objectiveQuestion.includes(item)) ){
hasStudents.value = useOverview.tableList.filter(item => {
if(item.rating > 0 && useOverview.allData[0].hasAnswers.includes(item.studentid)){
return item
}
})
}else{
hasStudents.value = useOverview.tableList.filter(item => useOverview.allData[0].hasAnswers.includes(item.studentid)).map(item => item);
}
showEcharts(); showEcharts();
nextTick(() => { nextTick(() => {
initChart(); initChart();
}); });
},{deep: true}) },{deep: true})
//
watchEffect(() => {
const stus = inject('hasAnswer');
if (!stus) return;
hasAnswersData.value = [...stus]
});
</script> </script>
<style scoped> <style scoped>

View File

@ -4,7 +4,7 @@
<el-tab-pane :label="item.label" style="text-align:left" stretch="true"> <el-tab-pane :label="item.label" style="text-align:left" stretch="true">
<template v-if="item.stuList.length > 0"> <template v-if="item.stuList.length > 0">
<template v-for="(stuItem,stuIndex) in item.stuList" :key="stuIndex"> <template v-for="(stuItem,stuIndex) in item.stuList" :key="stuIndex">
<el-tag style="margin:5px 10px 0 0" type="primary">{{stuItem.studentname}}</el-tag> <el-tag style="margin:5px 10px 0 0" type="primary">{{ stuItem.studentname }}:{{ stuItem.getScore }}</el-tag>
</template> </template>
</template> </template>
<template v-else> <template v-else>
@ -16,13 +16,13 @@
</template> </template>
<script setup> <script setup>
import {nextTick, ref, watch,inject,watchEffect} from 'vue' import {ref, watch} from 'vue'
import overviewStore from '@/store/modules/overview' import overviewStore from '@/store/modules/overview'
const useOverview = overviewStore() const useOverview = overviewStore()
const tabPosition = ref('left') const tabPosition = ref('left')
// //
const hasAnswersData = ref([])
const hasStudents = ref([]) const hasStudents = ref([])
const leftList = ref([ const leftList = ref([
{ {
@ -81,14 +81,20 @@ const showStudents = (index) => {
}) })
} }
watch(() => useOverview.tableList, () => { watch(() => useOverview.tableList, () => {
hasStudents.value = useOverview.tableList.filter(item => hasAnswersData.value.includes(item.studentid)).map(item => item); //
const subType = useOverview.allData.map(item => item.type)
const objectiveQuestion = ['单选题','多选题','判断题']
if( !subType.every(item => objectiveQuestion.includes(item)) ){
hasStudents.value = useOverview.tableList.filter(item => {
if(item.rating > 0 && useOverview.allData[0].hasAnswers.includes(item.studentid)){
return item
}
})
}else{
hasStudents.value = useOverview.tableList.filter(item => useOverview.allData[0].hasAnswers.includes(item.studentid)).map(item => item);
}
showStudents(0) showStudents(0)
},{deep: true}) },{deep: true})
watchEffect(() => {
const stus = inject('hasAnswer')
if(!stus) return
hasAnswersData.value = [...stus]
})
</script> </script>
<style scoped> <style scoped>

View File

@ -20,82 +20,71 @@
<script setup> <script setup>
import {ref, watch} from 'vue' import {ref, watch} from 'vue'
import overviewStore from '@/store/modules/overview' import overviewStore from '@/store/modules/overview'
import {listEntpcoursework} from '@/api/education/entpCourseWork'
const useOverview = overviewStore() const useOverview = overviewStore()
const tableData = ref([]) const tableData = ref([])
//id
const ids = ref('')
//
const allScore = ref(0)
// //
const konwledge = ref([]) const konwledge = ref([])
const hasStudents = ref([])
//
const allScore = ref(0)
//
const avatarScore = ref()
// //
const getKonwledge = () => { const getKonwledge = () => {
useOverview.tableList.forEach(item => { const getScoreRate = []
//
const ledges = []
hasStudents.value.forEach((item,index) => {
//
if(item.knowledgePoint){ if(item.knowledgePoint){
konwledge.value.push({...JSON.parse(item.knowledgePoint),...{scoingRate:Number(item.scoingRate),point:item.point,allPoint:allScore.value}}) const title = JSON.parse(item.knowledgePoint)
//
if(!ledges.includes(title.id)){
ledges.push(title.id)
// 0
konwledge.value.push({title:title.title,allPoint:allScore.value,id:title.id,point:avatarScore.value})
}
//
if(useOverview.allData[0].hasAnswers.includes(item.studentid))
getScoreRate.push({rate:item.scoingRate,id:title.id})
} }
}) })
tableData.value = getTableList(konwledge.value)
tableData.value = tableData.value.map(item => { //
return{ konwledge.value.forEach(item => {
tableData.value.push({
scoingRate:(item.point / item.allPoint * 100).toFixed(2),
...item, ...item,
allPoint: allScore.value
}
}) })
console.log(tableData.value,'tableData.value') })
}
//
const getScore = async () => {
const scoreId = useOverview.tableList[0].entpcourseworklist
const fixedJsonString = `[${scoreId}]`;
const objects = JSON.parse(fixedJsonString);
const id = objects.map(obj => obj.id);
ids.value = id.join(',')
const res = await listEntpcoursework({ids: ids.value, pageSize: 500})
if(res.code === 200){
allScore.value = res.rows.reduce((acc, cur) => acc + cur.workScore, 0);
getKonwledge()
}
} }
//tableList //tableList
const getTableList = (data) => {
const result = [];
data.forEach(item => {
const existingItem = result.find(i => i.id === item.id);
if (existingItem) {
// pointscoingRate
existingItem.pointTotal += parseInt(item.point);
existingItem.scoingRateTotal += parseFloat(item.scoingRate);
existingItem.count++;
} else {
//
result.push({
id: item.id,
title: item.title,
pointTotal: item.point,
scoingRateTotal: parseFloat(item.scoingRate),
count: 1
});
}
});
//
result.forEach(item => {
item.point = Math.round(item.pointTotal / item.count);
// item.scoingRate = Math.round((item.scoingRateTotal / item.count) * 100) / 100;
item.scoingRate = Math.round((item.point / allScore.value) * 100);
delete item.pointTotal;
delete item.scoingRateTotal;
delete item.count;
});
return result;
}
watch(() => useOverview.tableList,() => { watch(() => useOverview.tableList,() => {
console.log(useOverview.tableList,'useOverview.tableList') //
getScore() const subType = useOverview.allData.map(item => item.type)
const objectiveQuestion = ['单选题','多选题','判断题']
if( !subType.every(item => objectiveQuestion.includes(item)) ){
hasStudents.value = useOverview.tableList.filter(item => {
if(item.rating > 0 && useOverview.allData[0].hasAnswers.includes(item.studentid)){
return item
}
})
}else{
hasStudents.value = useOverview.tableList.filter(item => useOverview.allData[0].hasAnswers.includes(item.studentid)).map(item => item);
}
//
allScore.value = useOverview.allData.reduce((acc, cur) => {
return acc + Number(cur.score)
},0)
//
avatarScore.value = hasStudents.value.reduce((acc, cur) => {
return acc + Number(cur.getScore)
},0) / hasStudents.value.length
//
getKonwledge()
}) })
</script> </script>

View File

@ -9,166 +9,160 @@ import * as echarts from 'echarts';
import {ref, nextTick, watch} from 'vue' import {ref, nextTick, watch} from 'vue'
import overviewStore from '@/store/modules/overview' import overviewStore from '@/store/modules/overview'
const useOverview = overviewStore() const useOverview = overviewStore();
// //
const chartRef = ref(null); const chartRef = ref(null);
//
const estimateTime = ref([]); const expectedDuration = ref([]);
const avaterTime = ref([]);
// x
const xAxisData = ref([]);
// y
const getyAxisData = () => {
estimateTime.value = [];
avaterTime.value = [];
useOverview.tableList.forEach(item => {
if (item.rating !== 0) {
estimateTime.value.push({
name: item.scoingRate ? item.scoingRate + '%' : 0 + '%',
value: Number(item.timelength)
});
avaterTime.value.push({
name: item.scoingRate ? item.scoingRate + '%' : 0 + '%',
value: Number(item.finishtimelength)
});
}
});
// x
xAxisData.value.sort((a, b) => {
const aPercentage = parseInt(a.replace('%', ''));
const bPercentage = parseInt(b.replace('%', ''));
return aPercentage - bPercentage;
});
// x
generateXAxisData();
};
// x
function generateXAxisData() {
// 8x
if(estimateTime.value.length > 8){
const minScoreRate = 0;
const maxScoreRate = 100;
const numPoints = 6; // x
const step = (maxScoreRate - minScoreRate) / (numPoints - 1);
xAxisData.value = [];
for (let i = 0; i < numPoints; i++) {
const scoreRate = minScoreRate + i * step;
xAxisData.value.push(scoreRate + '%');
}
}else{
let uniqueXAxisData = new Set();
estimateTime.value.forEach(item => {
// Set
uniqueXAxisData.add(item.name);
});
// Set
xAxisData.value = Array.from(uniqueXAxisData);
//
xAxisData.value.sort((a, b) => {
const aPercentage = parseInt(a.replace('%', ''));
const bPercentage = parseInt(b.replace('%', ''));
return aPercentage - bPercentage;
});
}
}
// //
function initChart() { function initChart() {
const myChart = echarts.init(chartRef.value); const myChart = echarts.init(chartRef.value);
const options = {
//
let option = {
tooltip: { tooltip: {
trigger: 'axis', trigger: "axis",
axisPointer: { axisPointer: {
type: 'cross' type: "shadow", // 线'line' | 'shadow'
},
formatter: function (parms) {
let str = "";
parms.forEach(param => {
if (param.seriesType === 'bar') {
str += param.axisValue + "</br>" + param.marker + "平均用时:" + param.value + 's' + "</br>";
} else if (param.seriesType === 'line') {
str += param.marker + "预计用时:" + param.value + 's';
} }
});
return str;
}, },
legend: {
data: ['预估时长', '平均用时']
}, },
textStyle: {
color: "#333",
},
color: ["#7BA9FA", "#4690FA"],
grid: { grid: {
left: '3%', containLabel: true,
right: '4%', left: "10%",
bottom: '3%', top: "20%",
top: '10%', bottom: "10%",
containLabel: true right: "10%",
}, },
xAxis: { xAxis: {
type: 'category', type: "category",
boundaryGap: false, data: getXValue(),
name: '得分率', axisLine: {
nameTextStyle: { lineStyle: {
color: '#999', color: "#333",
fontSize: 12,
padding: [0, 0, 10, 0]
}, },
data: xAxisData.value },
axisTick: {
show: false,
},
axisLabel: {
margin: 20, //线
textStyle: {
color: "#000",
},
},
name: '题目编号'
}, },
yAxis: { yAxis: {
type: 'value', type: "value",
name: '作业时长', axisLine: {
nameTextStyle: { show: true,
color: '#999', lineStyle: {
fontSize: 12, color: "#B5B5B5",
padding: [0, 0, 10, 0]
}, },
}, },
name: '平均时长',
splitLine: {
lineStyle: {
// 使
color: ["#B5B5B5"],
type: "dashed",
opacity: 0.5,
},
},
axisLabel: {},
},
series: [ series: [
{ {
name: `预估时长`, data: getYValue(),
type: 'line', stack: "zs",
smooth: true, type: "bar",
symbol: 'circle', barMaxWidth: "auto",
symbolSize: 10, barWidth: 60,
lineStyle: { itemStyle: {
color: '#5793f3' color: {
x: 0,
y: 0,
x2: 0,
y2: 1,
type: "linear",
global: false,
colorStops: [
{ offset: 0, color: "#5EA1FF" },
{ offset: 1, color: "#90BEFF" },
],
}, },
data: estimateTime.value.map(item => ({
name: item.name,
value: item.value
}))
}, },
label: {
show: true,
position: 'top',
formatter: '{c}s',
color: '#333',
},
},
//线
{ {
name: `平均用时`, data: expectedDuration.value,
type: 'line', type: "line",
smooth: true, smooth: true,
symbol: 'circle', symbol: 'circle',
symbolSize: 10, symbolSize: 8,
lineStyle: { lineStyle: {
color: '#d14a61' color: '#FF7F50',
width: 2,
}, },
data: avaterTime.value.map(item => ({ itemStyle: {
name: item.name, color: '#FF7F50',
value: item.value },
})) },
} ],
]
}; };
myChart.setOption(options);
} myChart.setOption(option);
//
const getAvaterTime = () => {
return useOverview.tableList.reduce((acc, cur) => acc + cur.finishtimelength, 0) / useOverview.tableList.length;
}
const getEstimateTime = () => {
return useOverview.tableList.reduce((acc, cur) => acc + cur.timelength, 0) / useOverview.tableList.length;
} }
//
const getYValue = () => {
const arr = useOverview.allData.map(item => item.timeAnalyse)
const num = useOverview.allData[0].hasAnswers.length;
if (arr.length === 0) return [];
return arr.map(item => (item ? (item / num).toFixed(2) : 0));
};
//
const getXValue = () => {
return useOverview.allData.map((item, index) => `${index + 1}`);
};
watch(() => useOverview.tableList,() => { watch(() => useOverview.tableList, () => {
getyAxisData() const time = useOverview.tableList.map(item => Number(item.timelength))
if(time.length === 0) return;
const avatarTime = time.reduce((acc, cur) => {
return acc + cur
},0) / time.length
expectedDuration.value = useOverview.allData.map(() => (Number(avatarTime) * 60 / useOverview.allData.length).toFixed(2));
//
nextTick(() => { nextTick(() => {
initChart(); initChart();
}) });
}) });
</script> </script>
<style scoped> <style scoped>

View File

@ -1,7 +1,7 @@
<template> <template>
<el-form ref="classWorkFormScoreRef" :model="classWorkFormScore" style="height: 100%"> <el-form ref="classWorkFormScoreRef" :model="classWorkFormScore" style="height: 100%">
<!-- <div class="teacher_content" :style="{ height: dialogProps.maxheight + 'px' }"> --> <!-- <div class="teacher_content" :style="{ height: dialogProps.maxheight + 'px' }"> -->
<div class="teacher_content" :style="{ height: '100%' }"> <div class="teacher_content" :style="{ height: '100%',fontSize: '18px' }">
<div style="font-size: 18px; width: 100%; padding: 5px 10px; flex: 0 0 auto;"> <div style="font-size: 18px; width: 100%; padding: 5px 10px; flex: 0 0 auto;">
{{ classWorkFormScore.name }} 答题详情 {{ classWorkFormScore.name }} 答题详情
</div> </div>
@ -90,7 +90,7 @@
<span>学生答案 <span>学生答案
<span <span
v-if="stuItem.feedcontent !=''" v-if="stuItem.feedcontent !=''"
style="background-color: red; color: white; padding: 0 5px; border-radius: 5px;" :style="{backgroundColor: `${formatWorkAnswer(quItem) == formatFeedContent(stuItem, quItem)? '#0ed116' : 'red'}`,color: 'white', padding: '0 5px', borderRadius: '5px'}"
v-html="formatFeedContent(stuItem, quItem)" v-html="formatFeedContent(stuItem, quItem)"
> >
</span> </span>
@ -284,59 +284,27 @@
<!-- 批改评价与评语 --> <!-- 批改评价与评语 -->
<div class="tacher_conten_foot"> <div class="tacher_conten_foot">
<el-row style="padding: 1% 4%; border: 2px dotted"> <el-row style=" padding: 1% 4%; border: 2px dotted;">
<el-col :span="24" style="display: flex; flex-direction: column"> <el-col :span="24" style="display: flex;flex-direction: column;">
<el-row> <el-row>
<el-col :span="14"> <el-col :span="24">
<div style="display: flex; margin: 10px auto"> <div class="greenLine" style="text-align: left;" v-if="dialogProps.studentObj.worktype == '习题训练'">
<span style="display: flex; align-items: center"> <span style="font-weight: bold;">老师点评</span>
<span v-if="dialogProps.studentObj.worktype == '习题训练'"> <span style="margin: 0;">{{ classWorkFormScore.teacherRating.reduce((a, b) => a + b.score, 0).toFixed(2)}}</span>
<span>得分 </span> </div>
<span style="margin: 0; color: red">{{ <div style="display: flex; margin: 10px auto;align-items: center;justify-content: space-between;">
classWorkFormScore.teacherRating.reduce((a, b) => a + b.score, 0).toFixed(2)
}}</span>
<span></span>
</span>
<span v-else>
<span>得分 </span>
<span v-if="classWorkFormScore.teacherRating.length > 0">
<el-input-number
v-model="classWorkFormScore.teacherRating[0].score"
:controls="false"
type="number"
:min="0"
:max="classWorkFormScore.teacherRating[0].maxScore"
size="small"
style="width: 60px"
@change="handleChange"
></el-input-number>
</span>
<span></span>
</span>
</span>
<div class="score-container"> <div class="score-container">
<div <div
v-for="(score, index) in teacherRatingList" v-for="(score, index) in teacherRatingList"
:key="index" :key="index"
style="white-space: nowrap;" style="white-space: nowrap;"
:class="[ :class="['score-circle', { 'active': classWorkFormScore.rating == score.ratingKey }]"
'score-circle',
{ active: classWorkFormScore.rating == score.ratingKey }
]"
@click="selectScore(score)" @click="selectScore(score)"
> >
{{ score.ratingValue }} <el-text :style="{fontWeight:'bold', color: classWorkFormScore.rating == score.ratingKey ? 'rgb(225,12,8)':'rgb(131,131,131)' }">{{ score.ratingValue }}</el-text>
</div> </div>
</div> </div>
</div> <el-select v-model="value" placeholder="常用评语" style="width: 240px" @change="onSelectOption">
</el-col>
<el-col :span="10" style="display: flex; align-items: center">
<el-select
v-model="value"
placeholder="常用评语"
style="width: 240px"
@change="onSelectOption"
>
<el-option <el-option
v-for="item in cities" v-for="item in cities"
:key="item.value" :key="item.value"
@ -354,23 +322,21 @@
placeholder="输入新的常用语" placeholder="输入新的常用语"
size="small" size="small"
/> />
<el-button type="primary" size="small" @click="onConfirm"> 确定 </el-button> <el-button type="primary" size="small" @click="onConfirm">
确定
</el-button>
<el-button size="small" @click="clear">取消</el-button> <el-button size="small" @click="clear">取消</el-button>
</template> </template>
</template> </template>
</el-select> </el-select>
</div>
</el-col> </el-col>
</el-row> </el-row>
</el-col> </el-col>
<el-col :span="24" style="display: flex; flex-direction: column"> <el-col :span="24" style="display: flex;flex-direction: column;">
<el-form-item label="评语说明"> <el-form-item>
<el-col :span="15" style="padding: 0px"> <el-col :span="24" style="padding: 0px">
<el-input <el-input row="5" type="textarea" v-model="classWorkFormScore.teacherremark" rows="1" placeholder="输入评语" />
v-model="classWorkFormScore.teacherremark"
type="textarea"
rows="1"
placeholder="请输入评语说明"
/>
</el-col> </el-col>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -461,7 +427,7 @@ import useUserStore from '@/store/modules/user'
import { ref, reactive } from 'vue' import { ref, reactive } from 'vue'
// import { Plus } from '@element-plus/icons-vue' // import { Plus } from '@element-plus/icons-vue'
import { ElMessageBox, ElMessage } from 'element-plus' import { ElMessageBox, ElMessage } from 'element-plus'
import { updateClassworkeval, updateClassworkdata, getClassworkdata } from '@/api/classTask' import { updateClassworkeval,updateClasswork, updateClassWorkDataAutoFinish, getClassworkdata, updateClassworkevalList } from '@/api/classTask'
import { getTimeDate } from '@/utils/date' import { getTimeDate } from '@/utils/date'
import ReFilePreview from '@/components/refile-preview/index.vue' import ReFilePreview from '@/components/refile-preview/index.vue'
import { quizStrToList } from '@/utils/comm'; import { quizStrToList } from '@/utils/comm';
@ -644,7 +610,33 @@ const selectScore = (score) => {
console.log(score, 'score----') console.log(score, 'score----')
classWorkFormScore.rating = score.ratingKey classWorkFormScore.rating = score.ratingKey
} }
const checkWorkType = (params) => {
//
const subType = params.quizlist.map(item => item.worktype)
const objectiveQuestion = ['单选题','多选题','判断题']
//
if(subType.every(item => objectiveQuestion.includes(item))){
//
const score = extractedNumber(params.studentObj.scoingRate)
if(0<=score && score<=59){
classWorkFormScore.rating = 5
}else if(60<=score && score<=69){
classWorkFormScore.rating = 4
}else if(70<=score && score<=79){
classWorkFormScore.rating = 3
}else if(80<=score && score<=99){
classWorkFormScore.rating = 2
}else{
classWorkFormScore.rating = 1
}
}
}
//
const extractedNumber = (score) => {
const match = score.match(/\d+/);
return match ? parseInt(match[0], 10) : null;
}
// //
const acceptParams = async (params) => { const acceptParams = async (params) => {
console.log(params) console.log(params)
@ -665,7 +657,7 @@ const acceptParams = async (params) => {
teacherFeedContentList.value = [] teacherFeedContentList.value = []
teachImageList.value = [] teachImageList.value = []
teachFileList.value = [] teachFileList.value = []
checkWorkType(params)
// ----------------- // -----------------
dialogProps.value = params dialogProps.value = params
classWorkFormScore.name = params.studentObj.studentname classWorkFormScore.name = params.studentObj.studentname
@ -803,7 +795,7 @@ const acceptParams = async (params) => {
console.log(params.studentQuizAllList[0].rating, '----------------------------') console.log(params.studentQuizAllList[0].rating, '----------------------------')
// null 0 0 // null 0 0
classWorkFormScore.rating = classWorkFormScore.rating =
params.studentQuizAllList[0].rating == 0 ? 0 : params.studentQuizAllList[0].rating params.studentQuizAllList[0].rating == 0 ? classWorkFormScore.rating : params.studentQuizAllList[0].rating
} }
analysisScoreOpen.value = true analysisScoreOpen.value = true
@ -835,8 +827,10 @@ const formatFeedContent = (stuItem, quItem) => {
const list = quizStrToList(quItem.workdesc); const list = quizStrToList(quItem.workdesc);
format = list.map((item,index) =>{ format = list.map((item,index) =>{
if (quItem.worktype == '单选题') { if (quItem.worktype == '单选题') {
const workdesc = item.replace(/<[^>]*>/g,''); // const workdesc = item.replace(/<[^>]*>/g,'');
const feedcontent = stuItem.feedcontent.replace(/<[^>]*>/g,''); // const feedcontent = stuItem.feedcontent.replace(/<[^>]*>/g,'');
const workdesc = item;
const feedcontent = stuItem.feedcontent;
if(workdesc == feedcontent){ if(workdesc == feedcontent){
return (String.fromCharCode(65+Number(index))) return (String.fromCharCode(65+Number(index)))
} }
@ -877,6 +871,13 @@ const onSubmit = () => {
}) })
return return
} }
/** 1、 更新当前作业是否已经批阅完成 */
// TODO updateClasswork
/** 2、 更新每个学生的批阅 */
var formd = { var formd = {
id: dialogProps.value.studentObj.id, // this.activeClassWork.id; id: dialogProps.value.studentObj.id, // this.activeClassWork.id;
@ -884,7 +885,7 @@ const onSubmit = () => {
updatedate: getTimeDate(),// = year+'-'+month+'-'+day+' '+hh+':'+mm; updatedate: getTimeDate(),// = year+'-'+month+'-'+day+' '+hh+':'+mm;
}; };
// //
updateClassworkdata(formd).then(res => { updateClassWorkDataAutoFinish(formd).then(res => {
}) })
// //
@ -904,6 +905,21 @@ const onSubmit = () => {
// } // }
}) })
}) })
// let queryList = [];
// classWorkFormScore.teacherRating && classWorkFormScore.teacherRating.map((item, index) => {
// const queryParams = {
// id: item.id,
// teacherRating: item.score, //
// rating: classWorkFormScore.rating, //
// teacherremark: classWorkFormScore.teacherremark, //
// timestamp: getTimeDate() //
// }
// //console.log(queryParams);
// queryList.push(queryParams);
// })
// //console.log(queryList);
// updateClassworkevalList(queryList).then((res) => {
// })
ElMessage({ ElMessage({
type: 'success', type: 'success',
message: '提交成功!' message: '提交成功!'
@ -973,23 +989,23 @@ defineExpose({
} }
.score-circle { .score-circle {
width: 30px; border: 1px solid rgb(131,131,131,.5);
height: 30px; padding: 5px 0;
border-radius: 50%; background-color: #fff;
background-color: pink;
color: red;
display: flex;
justify-content: center;
align-items: center;
font-size: 13px;
margin: 0 10px;
cursor: pointer; cursor: pointer;
transition: background-color 0.3s; margin-right: 5px;
width: 60px;
text-align: center;
} }
.score-circle.active { .score-circle.active {
background-color: red; background-color: rgb(253, 236, 224);
color: white; color: white;
border: 1px solid rgb(253, 236, 224);
}
.greenLine{
border-left: 5px solid rgb(14, 209, 22);
padding-left: 5px
} }
.card-header{ .card-header{

View File

@ -14,31 +14,40 @@
&nbsp;|&nbsp; 截止时间{{ item.deaddate }} &nbsp;|&nbsp;{{ tabactive }} &nbsp;|&nbsp; 截止时间{{ item.deaddate }} &nbsp;|&nbsp;{{ tabactive }}
</div> </div>
</div> </div>
<div class="class-reserv-item-tool">
<div v-if=" tabactive == '待批改' " class="class-reserv-item-progress">
<el-progress :text-inside="true" :stroke-width="26" :percentage="item.finishpercent" :color="'#000fff'" style="cursor: pointer"></el-progress>
<span>
已交(
<span> <span>
<span v-if="item.workdataresultcount!=0" style="color:#000fff; font-weight: 900; font-size: 15px">{{ item.workdataresultcount }}</span> <span v-if="item.workdataresultcount!=0" style="color:#000fff; font-weight: 900; font-size: 15px">{{ item.workdataresultcount }}</span>
<span v-if="item.workdataresultcount==0">{{ item.workdataresultcount }}</span> <span v-if="item.workdataresultcount==0">{{ item.workdataresultcount }}</span>
/{{ item.workdatacount }}</span> /{{ item.workdataresultsum }}
<span>已交</span> </span>
)
</span>
</div> </div>
<div class="class-reserv-item-tool">
<!-- 总人数-已批阅人数 --> <div v-if=" tabactive == '待批改' " class="class-reserv-item-progress">
<span style="color: #ff7f00; font-weight: 900; font-size: 15px">{{ item.teacherrationgcount?item.workdatacount - item.teacherrationgcount:item.workdatacount }}</span> <el-progress :text-inside="true" :stroke-width="26" :percentage="item.teacherCorrectionProgress" :color="'#ff7f00'" style="cursor: pointer"></el-progress>
<span>待批阅</span> <span>
已批阅(<span style="color: #ff7f00; font-weight: 900; font-size: 15px">{{ item.teacherrationgcount}}</span>)
</span>
</div> </div>
<!-- TODO 练习次数引用次数 这里随便的假数据-->
<div v-if=" tabactive == '已批改' " class="class-reserv-item-tool">
<span style="color:#000fff; font-weight: 900; font-size: 15px">{{ item.workdataresultsum }}</span>
<span>练习次数</span>
</div>
<div v-if=" tabactive == '已批改' " class="class-reserv-item-tool">
<span style="color: #ff7f00; font-weight: 900; font-size: 15px">{{ item.teacherrationgcount?item.workdataresultsum - item.teacherrationgcount:item.workdataresultsum }}</span>
<span>引用次数</span>
</div>
<div class="class-reserv-item-tool"> <div class="class-reserv-item-tool">
<span> <span>
<!-- {{ item.averagetime?item.averagetime:0 }} -->
<!-- <span v-if=" item.averagetime<60 ">
<span style="color: #007fff; font-weight: 900; font-size: 15px">{{ item.averagetime }}</span>分钟
</span>
<span v-if=" item.averagetime==60 ">
<span style="color: #007fff; font-weight: 900; font-size: 15px">1</span>小时
</span>
<span v-if=" item.averagetime>60 ">
<span style="color: #007fff; font-weight: 900; font-size: 15px">{{ Math.floor(item.averagetime / 60)}}</span>小时
<span style="color: #007fff; font-weight: 900; font-size: 15px">{{ Math.floor(item.averagetime % 60)}}</span>分钟
</span> -->
<span style="color: #007fff; font-weight: 900; font-size: 15px">{{ item.averagetime }}</span>分钟 <span style="color: #007fff; font-weight: 900; font-size: 15px">{{ item.averagetime }}</span>分钟
</span> </span>
<span>平均用时</span> <span>平均用时</span>
@ -97,6 +106,11 @@ const props = defineProps({
} }
} }
} }
.class-reserv-item-progress {
width: 200px;
padding: 0 10px;
font-size: 14px;
}
.class-reserv-item-tool { .class-reserv-item-tool {
margin-left: 10px; margin-left: 10px;
display: flex; display: flex;

View File

@ -1,766 +0,0 @@
<template>
<el-dialog
v-model="classWorkAnalysis.open"
:modal-append-to-body="false"
class="clwk_dialog"
style="width: 90%; height: 85vh"
:show-close="false"
top="8vh"
append-to-body
destory-on-close
:before-close="onBeforeClose"
>
<template #header>
<div style="font-size: 18px; display: flex; flex-wrap: nowrap">
<div style="flex: 1">
{{ classWorkAnalysis.title }}完成情况
<el-tag :type="classWorkAnalysis.workclass" size="large" style="height: 25px">{{
classWorkAnalysis.worktype
}}</el-tag>
</div>
<!-- classWorkAnalysis.entpcourseworklistarray 当前学习任务所包含的试题ID -->
<el-row
v-if="classWorkAnalysis.entpcourseworklistarray.length > 0"
style="margin: 0 auto; flex: 1"
>
<el-button-group style="margin-bottom: 10px">
<el-button
:type="classWorkAnalysis.view == 'studentview' ? 'success' : ''"
@click="classWorkAnalysis.view = 'studentview'"
>作业批阅</el-button
>
<el-button
v-if="classWorkAnalysis.row.worktype == '习题训练'"
:type="classWorkAnalysis.view == 'quizStats' ? 'success' : ''"
@click="workHandle('quizStats')"
>逐题讲评</el-button
>
<el-button
v-if="classWorkAnalysis.row.worktype == '习题训练'"
:type="classWorkAnalysis.view == 'report' ? 'success' : ''"
@click="handleClassOverviewOpen('report')"
>训练报告</el-button
>
</el-button-group>
</el-row>
<div style="flex: 1">
<div
style="float: right; padding: 0 10px; cursor: pointer"
icon="el-icon-close"
@click="closeDialog"
>
x
</div>
</div>
</div>
</template>
<!-- 如果当前学习没有试题 :height="mainHeight"-->
<div
v-if="classWorkAnalysis.view == 'studentview'"
style="width: 100%; height:73vh; "
class="clwk_dialog_view"
>
<div class="view_table">
<el-radio-group
v-model="tableRadio.value"
style="margin-bottom: 1px"
@change="tableRadioChange"
>
<el-radio-button :value="1" :label="'已交' + '' + tableRadio.num1 + ''" />
<el-radio-button :value="0" :label="'未交' + '' + tableRadio.num0 + ''" />
</el-radio-group>
<!-- 学生列表classWorkAnalysis.classworkdata; 已交未交tableRadio.list -->
<el-table
v-loading="loading_dt_table"
:data="tableRadio.list"
row-key="id"
style="height: 69vh;"
highlight-current-row
@row-click="getStudentClassWorkDataDetail"
>
<el-table-column type="index" label="序号" width="52" reserve-selection align="center" />
<el-table-column label="姓名" prop="studentname" width="100" align="center" />
<el-table-column :label="tableRadio.value==0?'提交状态':'提交时间'" prop="updatedate" width="170" align="center">
<template #default="scope">
<span v-if="tableRadio.value==0" style="color: #2196f3">未提交</span>
<span v-if="tableRadio.value==1">{{ scope.row.updatedate }}</span>
</template>
</el-table-column>
<el-table-column label="批阅状态" prop="teacherRating" align="center" width="120" sortable>
<template #default="scope">
<template v-if="scope.row.teacherRating == 0">
<span v-if="tableRadio.value==1" style="color: #2196f3">待批阅</span>
</template>
<!-- 1- 2-优减 3- 4-良减 5- -->
<template v-if="scope.row.teacherRating == 1"
><el-tag type="danger">完美</el-tag></template
>
<template v-if="scope.row.teacherRating == 2"
><el-tag type="danger">优秀</el-tag></template
>
<template v-if="scope.row.teacherRating == 3"
><el-tag type="warning">良好</el-tag></template
>
<template v-if="scope.row.teacherRating == 4"
><el-tag type="info">及格</el-tag></template
>
<template v-if="scope.row.teacherRating == 5"
><el-tag type="info">不及格</el-tag></template
>
</template>
</el-table-column>
</el-table>
</div>
<div class="view_teachrting">
<div class="classwork-score">
<div v-if="classWorkAnalysis.activeStudentQuizlist.length == 0">
<el-empty
description="点击左侧表格学生信息可查看批阅详情"
style="width: 100%; height: 500px"
></el-empty>
</div>
<div v-else>
<div v-if="isopen_dtwk_table">
<div v-show="classWorkAnalysis.activeStudentQuizlist.length > 0">
<item-dialog-score
ref="classWorkAnalysisScoreDialogRef"
@class_work_score_submit="onClassWorkScoreSubmit"
/>
</div>
</div>
<div v-else>
<el-empty
description="点击左侧表格学生信息可查看批阅详情"
style="width: 100%; height: 500px"
></el-empty>
</div>
</div>
</div>
</div>
</div>
<!-- 逐题讲评 -->
<div v-else-if="classWorkAnalysis.view == 'quizStats'">
<quiz-stats :active-data="classWorkActiveData" />
</div>
<!-- 训练报告-->
<div v-else-if="classWorkAnalysis.view == 'report'" style="overflow-y: scroll">
<!-- <ClassOverview :table-list="overviewData" :eval-id="courseObj.evalid"></ClassOverview> -->
<ClassOverview :table-list="overviewData"></ClassOverview>
</div>
<!-- <template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="classWorkAnalysis.open=false"> </el-button>
</div>
</template> -->
</el-dialog>
</template>
<script setup name="itemDialogRef">
import { ref, defineExpose, onMounted, reactive, computed, watch, onUnmounted, nextTick, getCurrentInstance } from 'vue'
import { addSmartClassReserv, updateSmartClassReserv, listClassmain } from '@/api/classManage'
import { listClassworkdata, listEntpcoursework, listClassworkeval } from '@/api/classTask'
import useUserStore from '@/store/modules/user'
import { ElMessage } from 'element-plus'
import { getCurrentTime, getAfterMinutes } from '@/utils/date'
import { processList } from '@/hooks/useProcessList'
import ItemDialogScore from '@/views/classTask/container/classTask/item-dialog-score.vue'
// zdg:
import quizStats from '@/views/classTask/container/quizStats.vue'
import ClassOverview from '@/views/classTask/container/classOverview.vue'
const { proxy } = getCurrentInstance()
const emit = defineEmits(['cle-click'])
const props = defineProps({
bookId: {
type: Number,
default: 0
},
})
const mainHeight = ref(document.documentElement.clientHeight - 110)
const classWorkAnalysis = reactive({
open: false
})
const tableRadio = reactive({
value: '1', //
list: [], // list
num1: 0, //
num0: 0 //
}) //
const loading_dt_table = ref(false)
const isopen_dtwk_table = ref(false)
// zdg:
const classWorkActiveData = reactive({
quizlist: [], //
studentList: [], // -
workFeedList: [], // -
timerId: 0 // id
})
//-
const classWorkAnalysisScore = reactive({
studentObj: {}, //
studentQuizAllList: [], // list
quizlist: [] // list
})
//
const overviewData = ref([])
// watch(
// // () => props.currentNode,
// (newValue, oldValue) => {
// form.name = newValue.label
// }
// )
const openDialog = (data) => {
console.log(data, '点击的item完成情况')
classWorkAnalysis.title = data.uniquekey ? data.uniquekey + '--' : ''
classWorkAnalysis.worktype = data.worktype
classWorkAnalysis.workclass = data.workclass
//
tableRadio.list = []
tableRadio.value = '1'
tableRadio.num0 = 0
tableRadio.num1 = 0
classWorkAnalysis.open = true
//
classWorkAnalysis.view = 'studentview'
// ID
classWorkAnalysis.entpcourseworklistarray = data.entpcourseworklistarray
//
classWorkAnalysis.activeStudentQuizlist = []
//
classWorkAnalysis.activeQuizAnalysisData = []
classWorkAnalysis.row = data
// window.test = this
// zdg:
const studentArr = data.classworkdatastudentids
? JSON.parse(`[${data.classworkdatastudentids}]`)
: []
classWorkActiveData.studentList = studentArr
/** 学生完成情况分析--获取作业学生list数据 */
getClassWorkStudentList(data.id)
// idlist
var ids = []
for (var i = 0; i < data.entpcourseworklistarray.length; i++) {
ids.push(data.entpcourseworklistarray[i].id)
}
//
listEntpcoursework({ ids: ids.join(','), pageSize: 500 }).then((idres) => {
for (var i = 0; i < idres.rows.length; i++) {
// // + .replace(/!@#\$%/g,'')
idres.rows[i].titletext = idres.rows[i].title.replace(/!@#\$%/g, '')
}
classWorkAnalysis.quizlist = idres.rows
classWorkActiveData.quizlist = idres.rows // zdg: 使
//
// + , pageSize: 100
listClassworkeval({ workid: data.id, pageSize: 1000 }).then((wevalres) => {
for (var i = 0; i < classWorkAnalysis.quizlist.length; i++) {
//
var scoingCount = 0
var feedcount = 0
//
var evalCount = 0
for (var w = 0; w < wevalres.rows.length; w++) {
if (wevalres.rows[w].entpcourseworkid == classWorkAnalysis.quizlist[i].id) {
evalCount++
//
if (wevalres.rows[w].feedcontent != '') {
//
feedcount++
//
if (wevalres.rows[w].feedcontent == wevalres.rows[w].rightanswer) {
wevalres.rows[w].scoingStatus = true
scoingCount++
// =
wevalres.rows[w].teacherRating = wevalres.rows[w].score
} else {
wevalres.rows[w].scoingStatus = false
}
}
}
}
classWorkAnalysis.quizlist[i].evalCount = evalCount
//
classWorkAnalysis.quizlist[i].feedcount = feedcount
// NaN% scoingRate
if (scoingCount == 0 && feedcount == 0) {
classWorkAnalysis.quizlist[i].scoingRate = '0%'
} else {
classWorkAnalysis.quizlist[i].scoingRate =
((scoingCount / feedcount) * 100).toFixed(0) + '%'
}
}
// zdg:
const getStudentid = (workdataid) => {
// id
const classworkdata = (classWorkAnalysis.classworkdata || []).find(
(o) => o.id === workdataid
)
return classworkdata ? classworkdata.studentid : ''
}
wevalres.rows.forEach((o) => {
o.studentid = getStudentid(o.workdataid)
})
classWorkActiveData.workFeedList = wevalres.rows
})
})
console.log(classWorkAnalysis, '点击进度后获得的数据')
}
//#region
/** 1、获取作业学生列表 */
const getClassWorkStudentList = (rowId) => {
// rowid使
localStorage.setItem('activeClassWorkRowId', rowId)
//
classWorkAnalysis.classworkdata = []
// _
loading_dt_table.value = true
// classworkdata
listClassworkdata({ classworkid: rowId, pageSize: 100 })
.then((response) => {
for (var i = 0; i < response.rows.length; i++) {
if (response.rows[i].entpcourseworklist != '') {
response.rows[i].entpcourseworkarray = JSON.parse(
'[' + response.rows[i].entpcourseworklist + ']'
)
} else {
response.rows[i].entpcourseworkarray = []
}
// 0
response.rows[i].teacherRating = 0
//
if (
response.rows[i].classworkevallist != '' &&
response.rows[i].classworkevallist != null &&
response.rows[i].classworkevallist != 'null'
) {
// , : "{\"id\":172910, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"},{\"id\":172911, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363100, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"}"
// .replace(/"(\[.*\])"/g, '$1'); eg: "feedcontent\":\"[{\"name\":\"Bliss.jpg\",\"url\":\"https://wzyzoss.3b8daa474.jpg\"}]\",
// json .replace(/""/g, '"') eg: """"
response.rows[i].classworkevallist = escapeHtmlQuotes(response.rows[i].classworkevallist)
//console.log('classworkevallist', response.rows[i].classworkevallist)
const evalarray = JSON.parse('[' + response.rows[i].classworkevallist + ']')
var scoingCount = 0
var feedcount = 0
for (var e = 0; e < evalarray.length; e++) {
if (evalarray[e].feedcontent != '') {
feedcount++
//
if (evalarray[e].feedcontent == evalarray[e].rightanswer) {
scoingCount++
}
}
}
//console.log(evalarray, 'evalarray------------------------------------')
if (feedcount > 0) {
// : /*100
response.rows[i].scoingRate = ((scoingCount / feedcount) * 100).toFixed(0) + '%'
} else {
response.rows[i].scoingRate = '0%'
}
// :
if (evalarray[0].rating != '') {
response.rows[i].teacherRating = evalarray[0].rating
}
} else {
response.rows[i].scoingRate = '0%'
}
}
classWorkAnalysis.classworkdata = response.rows
loading_dt_table.value = false
//
tableRadio.list =
classWorkAnalysis.classworkdata &&
classWorkAnalysis.classworkdata.filter((item) => item.finishtimelength != '0')
tableRadio.value = '1'
tableRadio.num0 = classWorkAnalysis.classworkdata.length - tableRadio.list.length
tableRadio.num1 = tableRadio.list.length
})
.catch(() => {
loading_dt_table.value = false
})
}
/** 2、查看某一个学生的学习任务完成详情*/
const getStudentClassWorkDataDetail = (row) => {
//
// this.classWorkAnalysis.quizlist
console.log(row, '点击了左侧学生')
//
classWorkAnalysisScore.studentObj = row
listClassworkeval({ workdataid: row.id, pageSize: 100 })
.then((wevalres) => {
for (var i = 0; i < classWorkAnalysis.quizlist.length; i++) {
//
for (var w = 0; w < wevalres.rows.length; w++) {
if (wevalres.rows[w].entpcourseworkid == classWorkAnalysis.quizlist[i].id) {
wevalres.rows[w].quiztitle = classWorkAnalysis.quizlist[i].title
wevalres.rows[w].quiztitletext = classWorkAnalysis.quizlist[i].title.replace(
/<[^>]*>/g,
''
)
wevalres.rows[w].score = wevalres.rows[w].score ? wevalres.rows[w].score : 0
// html
// wevalres.rows[w].rightanswer =
// wevalres.rows[w].rightanswer != '' && wevalres.rows[w].rightanswer != null
// ? wevalres.rows[w].rightanswer.replace(/<[^>]+>/g, '')
// : wevalres.rows[w].rightanswer
// // html
// wevalres.rows[w].feedcontent =
// wevalres.rows[w].feedcontent != '' && wevalres.rows[w].feedcontent != null
// ? wevalres.rows[w].feedcontent.replace(/<[^>]+>/g, '')
// : wevalres.rows[w].feedcontent
if (classWorkAnalysis.row.worktype == '常规作业') {
wevalres.rows[w].feedcontent = JSON.parse(wevalres.rows[w].feedcontent)
}
if (wevalres.rows[w].feedcontent != '') {
if (wevalres.rows[w].feedcontent == wevalres.rows[w].rightanswer) {
wevalres.rows[w].scoingStatus = true
// =
wevalres.rows[w].teacherRating = wevalres.rows[w].score
} else {
wevalres.rows[w].scoingStatus = false
}
} else {
wevalres.rows[w].scoingStatus = ''
}
//
}
// "" prop="feedcontent" width="200" align="center"></el-table-column>
// <el-table-column label="" prop="rightanswer"
// +
wevalres.rows[w].worktitle = wevalres.rows[w].worktitle.replace(/!@#\$%/g, '')
// feedcontent\r<br />
wevalres.rows[w].feedcontent = wevalres.rows[w].feedcontent.replace(/(?<!\\)\n/g, '<br />'); //\n\\n \\n
}
}
classWorkAnalysis.activeStudentQuizlist = wevalres.rows
//
isopen_dtwk_table.value = true
//
if (wevalres.rows.length > 0) {
handleClassWorkAnalysissScoreOpen(row)
} else {
ElMessage({
type: 'warning',
message: '未获取到答题信息,请稍后再看,或者联系管理员查看情况!'
})
}
})
.catch(() => {
console.log('获取完成情况失败')
ElMessage({
type: 'warning',
message: '未获取到答题信息!'
})
})
}
/** 3、教师批改后返回的方法*/
const onClassWorkScoreSubmit = () => {
console.log('批改后返回的方法')
loading_dt_table.value = true
isopen_dtwk_table.value = false
// 1table- classWorkAnalysis.classworkdata- classWorkAnalysis.activeStudentQuizlist
// -
classWorkAnalysis.classworkdata = []
classWorkAnalysis.activeStudentQuizlist = []
// 2
const rowid = localStorage.getItem('activeClassWorkRowId')
getClassWorkStudentList(rowid)
}
// ()
const handleClassWorkAnalysissScoreOpen = (row) => {
console.log(row, '所选点击的信息')
// list
classWorkAnalysisScore.studentQuizAllList = classWorkAnalysis.activeStudentQuizlist
// list
classWorkAnalysisScore.quizlist = classWorkAnalysis.quizlist
//
processList(classWorkAnalysisScore.quizlist)
//
classWorkAnalysisScore.maxheight = mainHeight.value - 100
//
nextTick(() => {
proxy.$refs.classWorkAnalysisScoreDialogRef.acceptParams(classWorkAnalysisScore)
})
}
//#endregion
/** 批阅:已交未交事件 */
const tableRadioChange = (e) => {
// ui
isopen_dtwk_table.value = false;
console.log(e,'??????')
console.log("学生列表:", classWorkAnalysis.classworkdata)
if(e=='1'){
tableRadio.list = classWorkAnalysis.classworkdata.filter(item => item.finishtimelength != '0')
tableRadio.value = '1';
tableRadio.num0 = classWorkAnalysis.classworkdata.length - tableRadio.list.length;
tableRadio.num1 = tableRadio.list.length;
}else if(e=='0'){
tableRadio.list = classWorkAnalysis.classworkdata.filter(item => item.finishtimelength == '0')
tableRadio.value = '0';
tableRadio.num0 = tableRadio.list.length;
tableRadio.num1 = classWorkAnalysis.classworkdata.length - tableRadio.list.length;
}
}
//
const escapeHtmlQuotes = (str) => {
// replace,
const regex1 = /\\+/g; //
let result = str.replace(regex1, '\\');
result = str.replace(/(?<!\\)\n/g, '<br />'); //\n\\n \\n
return result;
}
//#region
// -
const workHandle = (type) => {
// ui
isopen_dtwk_table.value = false;
classWorkAnalysis.view = type
const isClose = type != 'quizStats' && !! classWorkActiveData.timerId
const isOpen = type == 'quizStats' && !classWorkActiveData.timerId
//
if(type == 'quizStats') {
getWorkFeedList();
}
if (isClose) clearInterval(classWorkActiveData.timerId) //
if (isOpen) {
//
classWorkActiveData.timerId = setInterval(() => {
console.log('zdg: 定时执行')
getWorkFeedList()
}, 20 * 1000);
}
}
// -
const getWorkFeedList = async() =>{
const workid = classWorkAnalysis.row.id
const res = await listClassworkeval({workid, isFinish: 1, pageSize: 1000})
const getStudentid = (workdataid) => { // id
const classworkdata = (classWorkAnalysis.classworkdata||[]).find(o => o.id === workdataid)
return classworkdata ? classworkdata.studentid : ''
}
res.rows.forEach(o => { o.studentid = getStudentid(o.workdataid) })
classWorkActiveData.workFeedList = res.rows
}
//#endregion
//#regin
/*
author: yangws
time: 2024-8-06 16:35:33
function: 训练报告的处理
*/
const handleClassOverviewOpen = (type) =>{
// ui
isopen_dtwk_table.value = false;
classWorkAnalysis.view = type
const data = classWorkAnalysis.row
//
listClassworkdata({classworkid: data.id, pageSize: 100}).then((response) => {
if(response.code === 200){
response.rows.forEach(item => {
let rightAnswer = 0
let answers = 0
if(!item.classworkevallist) return
// 使
let replacedString = item.classworkevallist.replace(/""/g, "\"");
// , : "{\"id\":172907, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":358520, \"feedcontent\":\"\", \"score\":4, \"rightanswer\":\"\"},{\"id\":172908, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":358521, \"feedcontent\":\"\", \"score\":4, \"rightanswer\":\"\"},{\"id\":172909, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363096, \"feedcontent\":\"\", \"score\":4, \"rightanswer\":\"\"},{\"id\":172910, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363098, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"},{\"id\":172911, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363100, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"}"
replacedString = escapeHtmlQuotes(item.classworkevallist);
let allTopic
try{
allTopic = JSON.parse(`[${item.classworkevallist}]`)
}catch{
allTopic = JSON.parse(`[${replacedString}]`)
}
if(item.classworkevallist != ''){
allTopic.forEach(itemTopic => {
if(itemTopic.feedcontent != ''){
answers ++
//
if(itemTopic.feedcontent === itemTopic.rightanswer){
rightAnswer ++
}
}
})
rightAnswer > 0?item.scoingRate = (rightAnswer/answers * 100).toFixed(0):item.scoingRate = ''
}else{
item.scoingRate = ''
}
//
const point = allTopic.reduce((acc, cur) => {
if(cur.rating !== 0){
return acc + cur.teacherRating;
}
},0)
// item.chapter = this.courseObj.evalid
item.point = point || 0
item.rating = allTopic[0].rating
})
overviewData.value = [...response.rows]
}
})
}
//#endregion
const onBeforeClose = () =>{
console.log('非正常关闭dialog?esc、dialog外部区域')
closeDialog()
}
const closeDialog = () => {
classWorkAnalysis.open = false
emit('cle-click')
}
watch(classWorkAnalysis, (newVal, oldVal) => {
if(newVal.view != 'quizStats'){
console.log('关闭zdg: 定时执行')
clearInterval(classWorkActiveData.timerId) //
}
})
onUnmounted(() => {
clearInterval(classWorkActiveData.timerId) //
})
defineExpose({
openDialog,
})
</script>
<style scoped lang="scss">
/*:deep(.reserv-date-pick) {
width: 140px;
}
:deep(.reserv-time-pick) {
width: 240px;
}*/
.clwk_dialog {
.clwk_dialog_view {
display: flex;
flex-direction: row;
justify-content: flex-start;
overflow: hidden;
}
.view_table {
flex: 0 0 auto;
height: 100%;
overflow: hidden;
}
.view_teachrting {
flex: 1;
height: 100%;
/*overflow-y: auto; */
overflow: hidden;
}
}
.clwk_dialog {
display: flex;
justify-content: center;
overflow: hidden;
}
.clwk_dialog .el-dialog {
margin: 0 auto !important;
height: 85%!important;
overflow: hidden;
}
.clwk_dialog .el-dialog__header {
/* position: absolute;
top: 0;
left: 0; */
width: 100%!important;
}
.clwk_dialog .el-dialog__body {
position: absolute;
left: 0;
top: 15px;
bottom: 1px;
right:0;
padding:5px;
z-index:1;
display: flex;
flex-direction: column;
overflow: hidden;
/* overflow:hidden;
overflow-y: auto; */
}
.clwk_dialog .el-dialog__footer{
position: absolute;
bottom: 10px;
right: 10px;
}
.clwk_dialog .classwork-score{
overflow-y: auto;
}
</style>
<style scoped>
.clwk_dialog {
display: flex;
justify-content: center;
overflow: hidden;
}
.clwk_dialog .el-dialog {
margin: 0 auto !important;
height: 85%!important;
overflow: hidden;
}
.clwk_dialog .el-dialog__header {
/* position: absolute;
top: 0;
left: 0; */
width: 100%!important;
}
.clwk_dialog .el-dialog__body {
position: absolute;
left: 0;
top: 15px;
bottom: 1px;
right:0;
padding:5px;
z-index:1;
display: flex;
flex-direction: column;
overflow: hidden;
/* overflow:hidden;
overflow-y: auto; */
}
.clwk_dialog .el-dialog__footer{
position: absolute;
bottom: 10px;
right: 10px;
}
.clwk_dialog .classwork-score{
overflow-y: auto;
}
</style>
<style src="@/assets/styles/JYStyle.css"></style>

View File

@ -55,6 +55,7 @@
<el-col :span="11"> <el-col :span="11">
<el-form-item label="知识点" label-width="70"> <el-form-item label="知识点" label-width="70">
<el-cascader <el-cascader
disabled
v-model="entpCourseWorkQueryParams.point" v-model="entpCourseWorkQueryParams.point"
clearable clearable
style="width: 100%" style="width: 100%"
@ -89,8 +90,13 @@
</el-col> </el-col>
</el-row> </el-row>
<!-- 习题表格 --> <!-- 习题表格 -->
<div class="middle" > <div class="infinite-list-wrapper" >
<el-table :data="workResource.entpCourseWorkList" style="width: 100%; height: 100%;"> <el-table
:data="workResource.entpCourseWorkList"
style="width: 100%; height: 100%;"
v-loading="pageParams.loading"
ref="tableRef"
>
<el-table-column type="index" width="60" /> <el-table-column type="index" width="60" />
<el-table-column align="left" > <el-table-column align="left" >
<template #header> <template #header>
@ -99,7 +105,7 @@
</div> </div>
</template> </template>
<template #default="scope"> <template #default="scope">
<div> <div @click="showExamAnalyseDrawer(scope.row)">
<div style="overflow: hidden; text-overflow: ellipsis" v-html="scope.row.titleFormat"></div> <div style="overflow: hidden; text-overflow: ellipsis" v-html="scope.row.titleFormat"></div>
<div style="overflow: hidden; text-overflow: ellipsis; font-size: 0.9em; margin-top: 6px;" v-html="scope.row.workdescFormat"></div> <div style="overflow: hidden; text-overflow: ellipsis; font-size: 0.9em; margin-top: 6px;" v-html="scope.row.workdescFormat"></div>
<el-col :span="24" style="display: flex"> <el-col :span="24" style="display: flex">
@ -115,17 +121,54 @@
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- <ul
v-infinite-scroll="pageLoad"
class="infinite-list"
infinite-scroll-immediate="false"
infinite-scroll-distance='100'
infinite-scroll-delay="1000"
:infinite-scroll-disabled="pageDisabled"
>
<li v-for="(item,index) in workResource.entpCourseWorkList" :key="item" class="infinite-list-item">
<div align="left" style="width: 100%;" >
<div @click="showExamAnalyseDrawer(item)">
<div>
<span style="width: 20px;">{{ index +1 }}. </span>
<span style="overflow: hidden; text-overflow: ellipsis" v-html="item.titleFormat"></span>
</div>
<div style="overflow: hidden; text-overflow: ellipsis; font-size: 0.9em; margin-top: 6px;" v-html="item.workdescFormat"></div>
<el-col :span="24" style="display: flex">
<div style="font-size: 1em; color: silver; padding-top: 5px">{{ item.entpname }} {{ item.editusername }}</div>
<div style="margin-left: 30px; font-size: 1em; color: silver; padding-top: 5px">{{ item.worktag }}</div>
</el-col>
</div>
</div>
<div align="right" style="width: 72px;">
<el-button type="primary" @click="handleClassWorkQuizAdd('entpcourseworklist', item.id)">添加</el-button>
</div>
</li>
</ul>
<p class="infinite-list-loading" v-if="pageParams.loading">加载中...</p>
<p class="infinite-list-noMove" v-if="pageNoMore">无更多试题...</p>
<div v-if="workResource.entpCourseWorkList.length == 0 && !pageParams.loading">
<el-empty
description="未找到相关试题"
style="width: 100%; height: 200px; margin-top: 20px;"
></el-empty>
</div> -->
</div> </div>
<!-- 分页 这里不用--> <!-- 分页 这里不用-->
<!-- <div style="height: 55px;"> <div style="height: 55px;">
<pagination <el-pagination
v-show="entpCourseWorkTotal > 0" v-show="pageParams.total > 0"
v-model:page="paginationParams.pageNum" v-model:page="paginationParams.pageNum"
v-model:limit="paginationParams.pageSize" v-model:limit="paginationParams.pageSize"
:total="entpCourseWorkTotal" :total="pageParams.total"
:style="{ position: 'relative', 'margin-top': '5px' }" :style="{ position: 'relative', 'margin-top': '5px' }"
@pagination="getPaginationList" /> @change="getPaginationList" />
</div> --> </div>
</div> </div>
<!-- 非习题训练常规作业 --> <!-- 非习题训练常规作业 -->
<div v-if="classWorkForm.worktype!='习题训练'"> <div v-if="classWorkForm.worktype!='习题训练'">
@ -224,29 +267,37 @@
<!-- 预览框 --> <!-- 预览框 -->
<prevReadMsgDialog ref="prevReadMsgDialogRef" :bookobj="props.bookobj"/> <prevReadMsgDialog ref="prevReadMsgDialogRef" :bookobj="props.bookobj"/>
<!-- 试题详细信息 -->
<examDetailsDrawer ref="examDetailsDrawerRef"></examDetailsDrawer>
</div> </div>
</template> </template>
<script setup> <script setup>
import { onMounted, ref, nextTick, watch, reactive, getCurrentInstance } from 'vue' import { onMounted, ref, nextTick, watch, reactive, getCurrentInstance, computed } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { cloneDeep } from 'lodash' import { cloneDeep } from 'lodash'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { Search } from '@element-plus/icons-vue'
import {listEntpcoursework, listEntpcourseworkNew, getEntpcoursework} from '@/api/education/entpCourseWork' import {listEntpcoursework, listEntpcourseworkNew, getEntpcoursework} from '@/api/education/entpCourseWork'
import { addClassworkReturnId } from '@/api/teaching/classwork' import { addClassworkReturnId } from '@/api/teaching/classwork'
import { updateClasswork, listEvaluationclue,readFile, listClassworkeval,delClassworkeval,addClassworkeval,updateClassworkeval } from '@/api/classTask' import { updateClasswork, listEvaluationclue, listClassworkeval,delClassworkeval,addClassworkeval,updateClassworkeval } from '@/api/classTask'
import { listEvaluation } from '@/api/subject' import { listEvaluation } from '@/api/subject'
import { listEntpcoursefile } from '@/api/education/entpcoursefile' import { listEntpcoursefile } from '@/api/education/entpcoursefile'
import { listKnowledgePoint } from "@/api/knowledge/knowledgePoint"; import { listKnowledgePoint } from "@/api/knowledge/knowledgePoint";
import { useGetHomework } from '@/hooks/useGetHomework' import { useGetHomework } from '@/hooks/useGetHomework'
import { processList } from '@/hooks/useProcessList' import { processList } from '@/hooks/useProcessList'
import { getCurrentTime } from '@/utils/date' import { getCurrentTime } from '@/utils/date'
import FileUpload from "@/components/FileUpload/index.vue"; import FileUpload from "@/components/FileUpload/index.vue";
import whiteboard from '@/components/whiteboard/whiteboard.vue' import whiteboard from '@/components/whiteboard/whiteboard.vue'
import prevReadMsgDialog from '@/views/classTask/container/newTask/prevReadMsg-Dialog.vue' import prevReadMsgDialog from '@/views/classTask/container/newTask/prevReadMsg-Dialog.vue'
import examDetailsDrawer from '@/components/exam-question/examDetailsDrawer.vue'
import { JYApiListCT, JYApiListOriginYear, JYApiListSO} from "@/utils/examQuestion/jyeoo"
import {throttle,debounce } from '@/utils/comm'
import { useToolState } from '@/store/modules/tool' import { useToolState } from '@/store/modules/tool'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
const userStore = useUserStore().user const userStore = useUserStore().user
@ -360,6 +411,24 @@ const boardLoading = ref(false);
//---------- //----------
const fileLoading = ref(false); // loading const fileLoading = ref(false); // loading
//
// const BASE_LIMIT_COUT = 50; //
// const pageNoMore = computed( () => {
// if (pageParams.value.total < 1) {
// return false;
// }
// let count = BASE_LIMIT_COUT >= pageParams.value.total ? pageParams.value.total : pageParams.value.originCount+BASE_LIMIT_COUT;
// return workResource.entpCourseWorkList.length >= count;
// });
// const pageDisabled = computed(() => pageParams.value.loading || pageNoMore.value);
const pageParams = ref({
loading: false, //
originCount: 0, //
isFirst: true, //
total: 0,
})
/*** /***
* 作业类型切换 * 作业类型切换
*/ */
@ -377,9 +446,33 @@ const changeFormType = (val) => {
const handleQueryParamFromEntpCourseWork = (queryType) => { const handleQueryParamFromEntpCourseWork = (queryType) => {
// //
// this.paginationParams = {pageNum: 1,pageSize: 10}; // this.paginationParams = {pageNum: 1,pageSize: 10};
//
initPageParams();
handleQueryFromEntpCourseWork(queryType); handleQueryFromEntpCourseWork(queryType);
}; };
let obj = {};
function Apis(key) {
obj[key] = [];
return function(task) {
return new Promise((resolve, reject) => {
obj[key].push(task);
Promise.all([...obj[key]]).then(res => {
const i = obj[key].findIndex(item => {
return item == task;
});
resolve(obj[key][i]);
//arr.splice(i, 1);
})
})
}
}
const client = new Apis('/paht');
/** /**
* @desc: 1习题训练 - 新查询试题 * @desc: 1习题训练 - 新查询试题
* @return: {*} * @return: {*}
@ -388,11 +481,12 @@ const handleQueryParamFromEntpCourseWork = (queryType) => {
* 1 - 按条件查询 * 1 - 按条件查询
* 2 - 按关键词查询 * 2 - 按关键词查询
*/ */
const handleQueryFromEntpCourseWork= (queryType) => { const t = function(name, time) {
//queryForm.pageNum = this.paginationParams.pageNum; return new Promise(resolve => {
//queryForm.pageSize = this.paginationParams.pageSize;
const queryForm = { const queryForm = {
//
currentPage: paginationParams.pageNum,
pageSize: paginationParams.pageSize,
// //
eid: props.bookobj.levelSecondId, eid: props.bookobj.levelSecondId,
sectionName: props.bookobj.coursetitle, sectionName: props.bookobj.coursetitle,
@ -407,15 +501,18 @@ const handleQueryFromEntpCourseWork= (queryType) => {
// //
yearStr: entpCourseWorkQueryParams.yearStr !== '-1' ? entpCourseWorkQueryParams.yearStr:'', yearStr: entpCourseWorkQueryParams.yearStr !== '-1' ? entpCourseWorkQueryParams.yearStr:'',
// //
thirdId: entpCourseWorkQueryParams.point.length > 0 ? entpCourseWorkQueryParams.point[0]:'', thirdId: entpCourseWorkQueryParams.point && entpCourseWorkQueryParams.point.length > 0 ? entpCourseWorkQueryParams.point[0]:'',
// //
keyword: entpCourseWorkQueryParams.keyWord && entpCourseWorkQueryParams.keyWord !== '' ? entpCourseWorkQueryParams.keyWord:'', keyword: entpCourseWorkQueryParams.keyWord && entpCourseWorkQueryParams.keyWord !== '' ? entpCourseWorkQueryParams.keyWord:'',
} }
const entpcourseworkres = listEntpcourseworkNew(queryForm);
// resolve(entpcourseworkres);
// pageNum: paginationParams.pageNum, })
// pageSize: paginationParams.pageSize, }
const handleQueryFromEntpCourseWork= async (queryType) => {
pageParams.value.loading = true;
// ( warn: ) // ( warn: )
// if (this.courseObj.edusubject=='' && this.courseObj.edustage=='') { // if (this.courseObj.edusubject=='' && this.courseObj.edustage=='') {
@ -423,35 +520,45 @@ const handleQueryFromEntpCourseWork= (queryType) => {
// queryForm.edusubject = ''; // queryForm.edusubject = '';
// } // }
listEntpcourseworkNew(queryForm).then(entpcourseworkres => { client(t('任务1', 1500)).then(res => {
// if (queryType == 1 && this.entpCourseWorkQueryParams.worktype == '') { //console.log("",res);
// // , // if(paginationParams.pageNum == 1){
// const allowedWorkTypes = ['', '', '', '', '']; // workResource.entpCourseWorkList = [];
// workResource.entpCourseWorkList = entpcourseworkres.rows.filter(item => { // workResource.entpCourseWorkTotal = 0;
// return !allowedWorkTypes.includes(item.worktype);
// }); // //
// } else { // // pageParams.value.loading = false;
// workResource.entpCourseWorkList = entpcourseworkres.rows; // // pageParams.value.isFirst = true;
// // pageParams.value.originCount = 0;
// } // }
const data = res.data || [];
if(data && data.length>0){
// workResource.entpCourseWorkList = entpcourseworkres.data;
// workResource.entpCourseWorkTotal = entpcourseworkres.data.length;
if(entpcourseworkres.data&&entpcourseworkres.data.length>0){ data.forEach(item=> {
workResource.entpCourseWorkList = entpcourseworkres.data;
workResource.entpCourseWorkTotal = entpcourseworkres.data.length;
workResource.entpCourseWorkList.forEach(item=> {
if (item.worktype == '选择题') { if (item.worktype == '选择题') {
item.worktype = '单选题' item.worktype = '单选题'
} }
}) })
//
processList(data);
//workResource.entpCourseWorkList.push(...data);
workResource.entpCourseWorkList = data;
// //
processList(workResource.entpCourseWorkList); if (pageParams.value.isFirst) {
}else{ pageParams.value.isFirst = false;
workResource.entpCourseWorkList = []; pageParams.value.originCount = workResource.entpCourseWorkList.length;
workResource.entpCourseWorkTotal = 0 pageParams.value.total = parseInt(res.msg);
paginationParams.pageNum = Math.ceil(parseInt(res.msg)/paginationParams.pageSize);
//console.log('first->', pageParams.value, paginationParams);
} }
}) }
pageParams.value.loading = false;
});
} }
// //
@ -858,26 +965,108 @@ const editWork = async (cform) =>{
} }
} }
/**
* 查看试题详细信息
* @param row 单题数据
*/
const showExamAnalyseDrawer = (row) => {
nextTick(() => {
const activeParams = {
activeExam: row,
}
proxy.$refs.examDetailsDrawerRef.acceptParams(activeParams);
})
}
const tableRef = ref();
const getPaginationList = ( page, limit ) => {
paginationParams.pageNum = page;
paginationParams.pageSize = limit;
//console.log(page, limit)
handleQueryFromEntpCourseWork(0);
//
tableRef.value.setScrollTop(0);
}
const pageLoad = async() => {
console.log("加载中...")
paginationParams.pageNum ++ ,
//paginationParams.pageSize = 2,
await handleQueryFromEntpCourseWork(0);
}
const initPageParams = () => {
//
workResource.entpCourseWorkList = [];
workResource.entpCourseWorkTotal = 0
//
pageParams.value.loading = false;
pageParams.value.isFirst = true;
pageParams.value.originCount = 0;
pageParams.value.total = 0;
//
paginationParams.pageNum = 1;
paginationParams.pageSize = 10;
}
onMounted(() => { onMounted(async() => {
//
const name = userStore.edustage + userStore.edusubject;
const jyCT = await JYApiListCT(proxy, name);
if (jyCT.length == 0) {
ElMessage.error('获取题型失败!');
return;
}
entpCourseWorkTypeList.value = jyCT;
//
entpCourseWorkYearList.value = JYApiListOriginYear();
entpCourseWorkGroupList.value = await JYApiListSO(proxy, name);
}) })
// const refreshData = () => {
// console.log("")
// //
// initPageParams();
// //
// handleQueryFromEntpCourseWork(0);
// //
// getQueryFromEvaluationclue();
// //
// getEntpCourseWorkPointList();
// }
// //
// const debounceQueryData = debounce(throttle(refreshData, 1000), 1000);
//
const debounceQueryData = debounce(() => {
console.log("防抖 加载数据中...")
//
initPageParams();
//
handleQueryFromEntpCourseWork(0);
//
getQueryFromEvaluationclue();
//
getEntpCourseWorkPointList();
}, 1000);
watch(() => props.propsformobj.uniquekey, (newVal) => { watch(() => props.propsformobj.uniquekey, (newVal) => {
console.log(props.propsformobj,'propsformobj') console.log(props.propsformobj,'propsformobj')
if(props.propsformobj.uniquekey){ if(props.propsformobj.uniquekey){
classWorkForm.uniquekey = props.propsformobj.uniquekey?cloneDeep(props.propsformobj.uniquekey):''; // classWorkForm.uniquekey = props.propsformobj.uniquekey?cloneDeep(props.propsformobj.uniquekey):''; //
} }
}) })
watch(() => props.bookobj.levelSecondId, (newVal) => { watch(() => props.bookobj.levelSecondId, (newVal, oldVal) => {
console.log(props.bookobj,'课程选择') console.log(props.bookobj,'课程选择')
// debounceQueryData();
handleQueryFromEntpCourseWork(0);
//
getQueryFromEvaluationclue();
//
getEntpCourseWorkPointList();
}) })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -965,6 +1154,46 @@ watch(() => props.bookobj.levelSecondId, (newVal) => {
box-sizing: border-box; box-sizing: border-box;
background-color: rgb(231, 231, 231) background-color: rgb(231, 231, 231)
} }
.infinite-list-wrapper{
height: 100%;
text-align: center;
overflow: auto;
.infinite-list {
padding: 0;
margin: 0;
list-style: none;
.infinite-list-item {
display: flex;
align-items: center;
//justify-content: center;
//height: 50px;
//background: var(--el-color-primary-light-9);
padding: 10px;
border-top: 1px solid #eee;
//color: var(--el-color-primary);
}
.infinite-list-item:hover {
background-color: #F3F5F8;
}
.infinite-list-item + .list-item {
margin-top: 10px;
}
}
.infinite-list-loading{
padding: 10px 0;
border-top: 1px solid #eee;
color: red;
}
.infinite-list-noMove{
padding: 10px 0;
border-top: 1px solid #eee;
color: #999;
}
}
} }
</style> </style>

View File

@ -1,9 +1,9 @@
<template> <template>
<el-row class="c-warp" :gutter="10"> <el-row class="c-warp" :gutter="10">
<el-col class="left" :span="14"> <el-col class="left" :span="10">
<el-collapse class="c-item" v-model="activeTopic"> <el-collapse class="c-item" v-model="activeTopic" accordion>
<template v-for="(item, index) in dataList"> <template v-for="(item, index) in dataList">
<el-collapse-item class="collapse-item" :name="index+1" :id="'collapse-'+(index+1)"> <el-collapse-item class="collapse-item" :name="index+1" :id="'collapse-'+(index+1)" @click="clickItem(index)">
<template #title> <template #title>
<el-popover :width="500" placement="right"> <el-popover :width="500" placement="right">
<p>{{item.def?.titletext}}</p> <p>{{item.def?.titletext}}</p>
@ -54,13 +54,13 @@
</template> </template>
</el-collapse> </el-collapse>
</el-col> </el-col>
<el-col class="right" :span="10"> <el-col class="right" :span="14">
<div class="c-item"> <div class="c-item">
<div class="title">提交情况</div> <!-- <div class="title">提交情况</div>
<div class="respond"> <div class="respond">
<el-space wrap> <el-space wrap> -->
<!-- <template v-for="it in 11"> --> <!-- <template v-for="it in 11"> -->
<template v-for="(item, index) in dataList"> <!-- <template v-for="(item, index) in dataList">
<el-card shadow="hover" class="card-warp"> <el-card shadow="hover" class="card-warp">
<div class="card-body"> <div class="card-body">
<el-progress type="dashboard" :color="colorArr" :width="80" :percentage="ratio_2(item)" /> <el-progress type="dashboard" :color="colorArr" :width="80" :percentage="ratio_2(item)" />
@ -70,7 +70,41 @@
</el-card> </el-card>
</template> </template>
</el-space> </el-space>
</div> -->
<div class="title">试题详情</div>
<!-- 习题训练 -->
<el-card class="item-card">
<el-row>
<el-col :span="24" style="padding: 10px">
<!-- 题源题目标题题目选项 -->
<span>{{ activeExam.worktag }}</span>
<span v-html="activeExam.titleFormat"></span>
<div :span="24" style="padding: 12px 6px 2px" v-html="activeExam.workdescFormat"></div>
<!-- 折叠 详情分析解答 -->
<div class="demo-collapse">
<el-collapse v-model="activeExamFlag" class="custom-collapse">
<el-collapse-item title="详情分析解答" name="1">
<el-row style=" padding: 4px; border: 2px dotted;">
<template #default="scope">
<el-col :span="3" style="padding: 4px 0px"><em>答案</em></el-col>
<el-col :span="21" style="padding: 4px 0px" v-html="activeExam.workanswerFormat"></el-col>
<el-col :span="3" style="padding: 4px 0px"><em>分析</em></el-col>
<el-col :span="21" style="padding: 4px 0px" v-html="activeExam.method"></el-col>
<el-col :span="3" style="padding: 4px 0px"><em>解答</em></el-col>
<el-col :span="21" style="padding: 4px 0px" v-html="activeExam.analyse"></el-col>
<el-col :span="3" style="padding: 4px 0px"><em>点评</em></el-col>
<el-col :span="21" style="padding: 4px 0px" v-html="activeExam.discuss"></el-col>
<!-- <el-col :span="21" style="padding: 4px 0px" v-html="dataList[activeTopic-1].def.discuss"></el-col> -->
</template>
</el-row>
</el-collapse-item>
</el-collapse>
</div> </div>
</el-col>
</el-row>
</el-card>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
@ -84,7 +118,9 @@ import { ref, defineExpose, onMounted, reactive, computed, watch, nextTick, watc
// import * as elementPlus from 'element-plus' // ElMessage ElMessageBox // import * as elementPlus from 'element-plus' // ElMessage ElMessageBox
let colorArr = [] // -- let colorArr = [] // --
// const attrs = useAttrs() // props // const attrs = useAttrs() // props
const activeTopic = ref(0) // const activeTopic = ref(1) //
const activeExam = ref({}) //
const activeExamFlag = ref(['1']) //
let dataList = ref([]) // let dataList = ref([]) //
let studentList = ref([]) // let studentList = ref([]) //
const props = defineProps({ // defineProps const props = defineProps({ // defineProps
@ -115,7 +151,10 @@ colorArr = [
// === === // === ===
onMounted(() => { onMounted(() => {
activeTopic.value = dataList.value.map((_, index) => index + 1); //activeTopic.value = dataList.value.map((_, index) => index + 1);
if (dataList.value[activeTopic.value-1].def != null && dataList.value[activeTopic.value-1].def != undefined) {
activeExam.value = dataList.value[activeTopic.value-1].def;
}
}) })
// === (methods) === // === (methods) ===
@ -185,8 +224,13 @@ const initData = () => {
}) })
} }
else if (o.worktype == '填空题') { // else if (o.worktype == '填空题') { //
const regex = /<!--BA-->(.*?)<!--EA-->/g // <!--BA-->xxx<!--EA--> //console.log('->', o.title);
children = (o.title||'').match(regex).map((v,i) => { let title = o.title.replace(/_{3,}/g, '_____'); //3-10线5
let regex = /<!--BA-->(.*?)<!--EA-->/g // <!--BA-->xxx<!--EA-->
if (title.indexOf('_____') != -1) {
regex = /_{5}/g // <!--BA-->xxx<!--EA-->
}
children = (title||'').match(regex).map((v,i) => {
const def = `填空项 ${i+1}` const def = `填空项 ${i+1}`
//const code = '(&emsp;)' //const code = '(&emsp;)'
const code = '(略)', txt=v const code = '(略)', txt=v
@ -285,6 +329,18 @@ const clickInfo = async ind => {
setTimeout(() => {scrollToElement('collapse-' + ind)}, 300); setTimeout(() => {scrollToElement('collapse-' + ind)}, 300);
// elementPlus.ElMessage.warning('!') // elementPlus.ElMessage.warning('!')
} }
//
const clickItem = async (index) => {
if (index > dataList.length-1 ) {
return
}
if (dataList.value[index].def == null || dataList.value[index].def == undefined) {
return;
}
activeExam.value = dataList.value[index].def;
}
// === === // === ===
// //
const scrollToElement = id => { const scrollToElement = id => {
@ -313,8 +369,14 @@ watchEffect(() => { initData() })
background: #F2F3F5; background: #F2F3F5;
height: 100%; height: 100%;
margin: 0 !important; margin: 0 !important;
.left{padding-left: 0 !important; height: 100%;} .left{
.right{padding-right: 0 !important;} padding-left: 0 !important;
height: 100%;
}
.right{
padding-right: 0 !important;
height: 100%;
}
.c-item{ .c-item{
padding: 10px; padding: 10px;
background: #fff; background: #fff;
@ -362,7 +424,7 @@ watchEffect(() => { initData() })
margin-bottom: 10px; margin-bottom: 10px;
} }
.respond{ .respond{
height: calc(100% - 65px); /* height: calc(100% - 65px);*/
overflow: auto; overflow: auto;
.el-space{padding: 5px;} .el-space{padding: 5px;}
.card-warp{ .card-warp{
@ -376,6 +438,17 @@ watchEffect(() => { initData() })
flex-direction: column; flex-direction: column;
} }
} }
.item-card {
max-width: 100%;
margin-bottom: 10px;
text-align: left;
font-size: 18px;
:deep(.el-collapse-item__content){
font-size: 18px;
}
}
} }
} }
</style> </style>

View File

@ -57,8 +57,8 @@
style="margin-bottom: 1px" style="margin-bottom: 1px"
@change="tableRadioChange" @change="tableRadioChange"
> >
<el-radio-button :value="1" :label="'已交' + '' + tableRadio.num1 + ''" /> <el-radio-button :value="1" :label="'已交' + '' + tableRadio.num1 + ''"/>
<el-radio-button :value="0" :label="'未交' + '' + tableRadio.num0 + ''" /> <el-radio-button :value="0" :label="'未交' + '' + tableRadio.num0 + ''"/>
</el-radio-group> </el-radio-group>
<!-- 学生列表classWorkAnalysis.classworkdata; 已交未交tableRadio.list --> <!-- 学生列表classWorkAnalysis.classworkdata; 已交未交tableRadio.list -->
<el-table <el-table
@ -77,25 +77,30 @@
<span v-if="tableRadio.value==1">{{ scope.row.updatedate }}</span> <span v-if="tableRadio.value==1">{{ scope.row.updatedate }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="批阅状态" prop="teacherRating" align="center" width="120" sortable> <el-table-column :label="tableRadio.value==0?'':'得分'" prop="score" width="80" align="center" >
<template #default="scope" v-if="tableRadio.value==1">
<span style="color: #2196f3">{{scope.row.getScore || 0}}</span>
</template>
</el-table-column>
<el-table-column label="批阅状态" prop="rating" align="center" width="120" sortable>
<template #default="scope"> <template #default="scope">
<template v-if="scope.row.teacherRating == 0"> <template v-if="scope.row.rating == 0">
<span v-if="tableRadio.value==1" style="color: #2196f3">待批阅</span> <span v-if="tableRadio.value==1" style="color: #2196f3">待批阅</span>
</template> </template>
<!-- 1- 2-优减 3- 4-良减 5- --> <!-- 1- 2-优减 3- 4-良减 5- -->
<template v-if="scope.row.teacherRating == 1" <template v-if="scope.row.rating == 1"
><el-tag type="danger">完美</el-tag></template ><el-tag type="danger">完美</el-tag></template
> >
<template v-if="scope.row.teacherRating == 2" <template v-if="scope.row.rating == 2"
><el-tag type="danger">优秀</el-tag></template ><el-tag type="danger">优秀</el-tag></template
> >
<template v-if="scope.row.teacherRating == 3" <template v-if="scope.row.rating == 3"
><el-tag type="warning">良好</el-tag></template ><el-tag type="warning">良好</el-tag></template
> >
<template v-if="scope.row.teacherRating == 4" <template v-if="scope.row.rating == 4"
><el-tag type="info">及格</el-tag></template ><el-tag type="info">及格</el-tag></template
> >
<template v-if="scope.row.teacherRating == 5" <template v-if="scope.row.rating == 5"
><el-tag type="info">不及格</el-tag></template ><el-tag type="info">不及格</el-tag></template
> >
</template> </template>
@ -157,6 +162,8 @@ import ItemDialogScore from '@/views/classTask/container/classTask/item-dialog-s
import quizStats from '@/views/classTask/container/quizStats.vue' import quizStats from '@/views/classTask/container/quizStats.vue'
import ClassOverview from '@/views/classTask/container/classOverview.vue' import ClassOverview from '@/views/classTask/container/classOverview.vue'
import {sessionStore} from '@/utils/store' import {sessionStore} from '@/utils/store'
import Chat from '@/utils/chat' // im
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const emit = defineEmits(['cle-click']) const emit = defineEmits(['cle-click'])
@ -172,7 +179,7 @@ const classWorkAnalysis = reactive({
entpcourseworklistarray: [], // ID entpcourseworklistarray: [], // ID
}) })
const tableRadio = reactive({ const tableRadio = reactive({
value: '1', // value: 1, //
list: [], // list list: [], // list
num1: 0, // num1: 0, //
num0: 0 // num0: 0 //
@ -203,15 +210,16 @@ const classWorkAnalysisScore = reactive({
// form.name = newValue.label // form.name = newValue.label
// } // }
// ) // )
const openDialog = (data) => { const openDialog = (data, isInit=true) => {
console.log(data, '点击的item完成情况') console.log(data, '点击的item完成情况')
if (isInit) {
classWorkAnalysis.title = data.uniquekey ? data.uniquekey + '--' : '' classWorkAnalysis.title = data.uniquekey ? data.uniquekey + '--' : ''
classWorkAnalysis.worktype = data.worktype classWorkAnalysis.worktype = data.worktype
classWorkAnalysis.workclass = data.workclass classWorkAnalysis.workclass = data.workclass
// //
tableRadio.list = [] tableRadio.list = []
tableRadio.value = '1' tableRadio.value = 1
tableRadio.num0 = 0 tableRadio.num0 = 0
tableRadio.num1 = 0 tableRadio.num1 = 0
@ -232,6 +240,8 @@ const openDialog = (data) => {
? JSON.parse(`[${data.classworkdatastudentids}]`) ? JSON.parse(`[${data.classworkdatastudentids}]`)
: [] : []
classWorkActiveData.studentList = studentArr classWorkActiveData.studentList = studentArr
}
/** 学生完成情况分析--获取作业学生list数据 */ /** 学生完成情况分析--获取作业学生list数据 */
getClassWorkStudentList(data.id) getClassWorkStudentList(data.id)
@ -248,6 +258,7 @@ const openDialog = (data) => {
} }
classWorkAnalysis.quizlist = idres.rows classWorkAnalysis.quizlist = idres.rows
classWorkActiveData.quizlist = idres.rows // zdg: 使 classWorkActiveData.quizlist = idres.rows // zdg: 使
processList(classWorkActiveData.quizlist);
// //
// + , pageSize: 100 // + , pageSize: 100
@ -334,7 +345,7 @@ const getClassWorkStudentList = (rowId) => {
} }
// 0 // 0
response.rows[i].teacherRating = 0 response.rows[i].rating = 0
// //
if ( if (
@ -343,35 +354,44 @@ const getClassWorkStudentList = (rowId) => {
response.rows[i].classworkevallist != 'null' response.rows[i].classworkevallist != 'null'
) { ) {
// , : "{\"id\":172910, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"},{\"id\":172911, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363100, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"}" // , : "{\"id\":172910, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"},{\"id\":172911, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363100, \"feedcontent\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\", \"score\":4, \"rightanswer\":\"<bdo class=\"mathjye-underpoint2\"></bdo>\"}"
// .replace(/"(\[.*\])"/g, '$1'); eg: "feedcontent\":\"[{\"name\":\"Bliss.jpg\",\"url\":\"https://wzyzoss.3b8daa474.jpg\"}]\", // .replace(/"(\[.*\])"/g, '$1'); eg: "feedcontent\":\"[{\"name\":\"Bliss.jpg\",\"url\":\"https://wzyzoss.3b8daa474.jpg\"}]\",
// json .replace(/""/g, '"') eg: """" // json .replace(/""/g, '"') eg: """"
response.rows[i].classworkevallist = escapeHtmlQuotes(response.rows[i].classworkevallist) response.rows[i].classworkevallist = escapeHtmlQuotes(response.rows[i].classworkevallist)
//console.log('classworkevallist', response.rows[i].classworkevallist) //console.log('classworkevallist', response.rows[i].classworkevallist)
const evalarray = JSON.parse('[' + response.rows[i].classworkevallist + ']') const evalarray = JSON.parse('[' + response.rows[i].classworkevallist + ']')
var scoingCount = 0 var scoingCount = 0
var feedcount = 0 var feedcount = 0
let score = 0
for (var e = 0; e < evalarray.length; e++) { for (var e = 0; e < evalarray.length; e++) {
if (evalarray[e].feedcontent != '') { if (evalarray[e].feedcontent != '') {
feedcount++ feedcount++
// //
if (evalarray[e].feedcontent == evalarray[e].rightanswer) { if (evalarray[e].feedcontent == evalarray[e].rightanswer) {
scoingCount++ scoingCount++
score += evalarray[e].score;
evalarray[e].teacherRating = evalarray[e].score
}
} }
} }
}
const allTeacherRating = evalarray.reduce((acc, cur) => acc + cur.teacherRating, 0) //
//console.log(evalarray, 'evalarray------------------------------------') //console.log(evalarray, 'evalarray------------------------------------')
if (feedcount > 0) { if (feedcount > 0) {
// : /*100 // : /*100
response.rows[i].scoingRate = ((scoingCount / feedcount) * 100).toFixed(0) + '%' response.rows[i].scoingRate = ((score / allTeacherRating) * 100).toFixed(0) + '%'
response.rows[i].getScore = allTeacherRating
} else { } else {
response.rows[i].scoingRate = '0%' response.rows[i].scoingRate = '0%'
response.rows[i].getScore = 0
} }
// : // :
if (evalarray[0].rating != '') { if (evalarray[0].rating != '') {
response.rows[i].teacherRating = evalarray[0].rating response.rows[i].rating = evalarray[0].rating
} }
} else { } else {
response.rows[i].scoingRate = '0%' response.rows[i].scoingRate = '0%'
response.rows[i].getScore = 0
} }
} }
classWorkAnalysis.classworkdata = response.rows classWorkAnalysis.classworkdata = response.rows
@ -381,14 +401,61 @@ const getClassWorkStudentList = (rowId) => {
tableRadio.list = tableRadio.list =
classWorkAnalysis.classworkdata && classWorkAnalysis.classworkdata &&
classWorkAnalysis.classworkdata.filter((item) => item.finishtimelength != '0') classWorkAnalysis.classworkdata.filter((item) => item.finishtimelength != '0')
tableRadio.value = '1' tableRadio.value = 1
tableRadio.num0 = classWorkAnalysis.classworkdata.length - tableRadio.list.length tableRadio.num0 = classWorkAnalysis.classworkdata.length - tableRadio.list.length
tableRadio.num1 = tableRadio.list.length tableRadio.num1 = tableRadio.list.length
//
teacherCriticism();
}) })
.catch(() => { .catch(() => {
loading_dt_table.value = false loading_dt_table.value = false
}) })
} }
/**
* 自动批阅判断
* 已交 作业类型为习题训练
*/
const teacherCriticism = ()=>{
// list
if(tableRadio.value == 1 && classWorkAnalysis.worktype == '习题训练'){
//
tableRadio.list = tableRadio.list.map((item) => {
return {
...item,
rating : item.rating || checkWorkType(item)
}
})
}
}
const checkWorkType = (item) => {
//
const subType = classWorkActiveData.quizlist.map(item => item.worktype)
const objectiveQuestion = ['单选题','多选题','判断题']
let rating = 0
//
if(subType.every(item => objectiveQuestion.includes(item))){
// scoingRate
const score = extractedNumber(item.scoingRate)
if(0<=score && score<=59){
rating = 5
}else if(60<=score && score<=69){
rating = 4
}else if(70<=score && score<=79){
rating = 3
}else if(80<=score && score<=99){
rating = 2
}else{
rating = 1
}
}
return rating
}
//
const extractedNumber = (score) => {
const match = score.match(/\d+/);
return match ? parseInt(match[0], 10) : null;
}
/** 2、查看某一个学生的学习任务完成详情*/ /** 2、查看某一个学生的学习任务完成详情*/
const getStudentClassWorkDataDetail = (row) => { const getStudentClassWorkDataDetail = (row) => {
// //
@ -505,14 +572,16 @@ const tableRadioChange = (e) => {
isopen_dtwk_table.value = false; isopen_dtwk_table.value = false;
console.log(e,'??????') console.log(e,'??????')
console.log("学生列表:", classWorkAnalysis.classworkdata) console.log("学生列表:", classWorkAnalysis.classworkdata)
if(e=='1'){ if(e==1){
tableRadio.list = classWorkAnalysis.classworkdata.filter(item => item.finishtimelength != '0') tableRadio.list = classWorkAnalysis.classworkdata.filter(item => item.finishtimelength != '0')
tableRadio.value = '1'; tableRadio.value = 1;
tableRadio.num0 = classWorkAnalysis.classworkdata.length - tableRadio.list.length; tableRadio.num0 = classWorkAnalysis.classworkdata.length - tableRadio.list.length;
tableRadio.num1 = tableRadio.list.length; tableRadio.num1 = tableRadio.list.length;
}else if(e=='0'){ //
teacherCriticism();
}else if(e==0){
tableRadio.list = classWorkAnalysis.classworkdata.filter(item => item.finishtimelength == '0') tableRadio.list = classWorkAnalysis.classworkdata.filter(item => item.finishtimelength == '0')
tableRadio.value = '0'; tableRadio.value = 0;
tableRadio.num0 = tableRadio.list.length; tableRadio.num0 = tableRadio.list.length;
tableRadio.num1 = classWorkAnalysis.classworkdata.length - tableRadio.list.length; tableRadio.num1 = classWorkAnalysis.classworkdata.length - tableRadio.list.length;
} }
@ -570,6 +639,10 @@ const getWorkFeedList = async() =>{
function: 训练报告的处理 function: 训练报告的处理
*/ */
const handleClassOverviewOpen = (type) =>{ const handleClassOverviewOpen = (type) =>{
//
if(type == 'report') {
getWorkFeedList();
}
// ui // ui
isopen_dtwk_table.value = false; isopen_dtwk_table.value = false;
classWorkAnalysis.view = type classWorkAnalysis.view = type
@ -580,6 +653,7 @@ const handleClassOverviewOpen = (type) =>{
response.rows.forEach(item => { response.rows.forEach(item => {
let rightAnswer = 0 let rightAnswer = 0
let answers = 0 let answers = 0
let score = 0
if(!item.classworkevallist) return if(!item.classworkevallist) return
// 使 // 使
let replacedString = item.classworkevallist.replace(/""/g, "\""); let replacedString = item.classworkevallist.replace(/""/g, "\"");
@ -598,12 +672,18 @@ const handleClassOverviewOpen = (type) =>{
// //
if(itemTopic.feedcontent === itemTopic.rightanswer){ if(itemTopic.feedcontent === itemTopic.rightanswer){
rightAnswer ++ rightAnswer ++
score += itemTopic.score
itemTopic.teacherRating = itemTopic.score
} }
} }
}) })
rightAnswer > 0?item.scoingRate = (rightAnswer/answers * 100).toFixed(0):item.scoingRate = '' const allTeacherRating = allTopic.reduce((acc, cur) => acc + cur.teacherRating, 0)
rightAnswer > 0?item.scoingRate = (score/allTeacherRating * 100).toFixed(0):item.scoingRate = ''
item.getScore = allTeacherRating
}else{ }else{
item.scoingRate = '' item.scoingRate = ''
item.getScore = 0
} }
// //
const point = allTopic.reduce((acc, cur) => { const point = allTopic.reduce((acc, cur) => {
@ -632,16 +712,62 @@ const closeDialog = () => {
emit('cle-click') emit('cle-click')
} }
onMounted(() => { // im
const msgHandle = (msg) => {
const { type, data } = msg
switch(type) {
case 'TIMAddRecvNewMsgCallback': // data=[]
{
(data||[]).forEach(o => {
const msgArr = o?.message_elem_array||[]
msgArr.forEach(info => {
const msgType = info?.elem_type // TIMElemType
const msgData = !!info.text_elem_content ? JSON.parse(info.text_elem_content)||'' : ''
//
//console.log('msgData->', msgData);
if (msgData.msgKey == "finishHomework"){
//
const data = JSON.parse(localStorage.getItem('teachClassWorkItem'));
//console.log('data->', data);
openDialog(data, false);
}
})
})
}
break
}
}
const reloadTimer = ref(0); // id
const cutid = ref(0); // id
onMounted(() => {
const data = JSON.parse(localStorage.getItem('teachClassWorkItem')); const data = JSON.parse(localStorage.getItem('teachClassWorkItem'));
// const data = sessionStore.get('teachClassWorkItem'); // const data = sessionStore.get('teachClassWorkItem');
// const data = localStorage.getItem('teachClassWorkItem');
console.log(data,'????????????????????' )
if(data){ if(data){
openDialog(data) openDialog(data)
} }
//
cutid.value = data.id;
isReloadTimer();
// im
if (!Chat.imChat) {
Chat.init(true, true, msgHandle);
} else {
Chat.listenMsg(msgHandle);
}
}) })
const isReloadTimer = () =>{
clearInterval(reloadTimer.value) //
// id
reloadTimer.value = setInterval(() => {
const data = JSON.parse(localStorage.getItem('teachClassWorkItem'));
if(cutid.value != data.id){
cutid.value = data.id;
openDialog(data)
}
}, 1000)
}
watch(classWorkAnalysis, (newVal, oldVal) => { watch(classWorkAnalysis, (newVal, oldVal) => {
if(newVal.view != 'quizStats'){ if(newVal.view != 'quizStats'){
@ -652,6 +778,7 @@ watch(classWorkAnalysis, (newVal, oldVal) => {
onUnmounted(() => { onUnmounted(() => {
clearInterval(classWorkActiveData.timerId) // clearInterval(classWorkActiveData.timerId) //
clearInterval(reloadTimer.value) // id
}) })
// defineExpose({ // defineExpose({
@ -697,7 +824,7 @@ onUnmounted(() => {
overflow: hidden; overflow: hidden;
.classwork-score{ .classwork-score{
// overflow-y: auto; /* overflow-y: auto; */
height: 100%; height: 100%;
} }
} }

View File

@ -14,14 +14,15 @@
<div class="class-left flex"> <div class="class-left flex">
<div class="class-name flex"> <div class="class-name flex">
<span class="name">{{ item.uniquekey }}</span> <span class="name">{{ item.uniquekey }}</span>
<el-tag class="tag" round :type="tagType(item.deaddate)" effect="dark" size="small">{{ <!-- <el-tag class="tag" round :type="tagType(item.deaddate)" effect="dark" size="small">{{
getCurrentTime('YYYY-MM-DD HH:mm') > item.deaddate ? '已结束' : '进行中' }}</el-tag> getCurrentTime('YYYY-MM-DD HH:mm') > item.deaddate ? '已批改' : '待批改' }}</el-tag> -->
<el-tag class="tag" round :type="tagType(item.deaddate)" effect="dark" size="small">待批改</el-tag>
<el-tag :type="item.workclass" size="large">{{ item.worktype }}</el-tag> <el-tag :type="item.workclass" size="large">{{ item.worktype }}</el-tag>
</div> </div>
<div class="class-time">{{ item.classcaption }} | 截止时间{{ item.deaddate }} </div> <div class="class-time">{{ item.classcaption }} | 截止时间{{ item.deaddate }} </div>
</div> </div>
<div class="class-right"> <div class="class-right">
<div><span class="num">{{ item.workdataresultcount }}</span> / {{ item.workdatacount }}</div> <div><span class="num">{{ item.workdataresultcount }}</span> / {{ item.workdataresultsum }}</div>
<div>已交</div> <div>已交</div>
</div> </div>
</li> </li>
@ -52,9 +53,10 @@ const getHomework = async () => {
const { edustage, edusubject } = user const { edustage, edusubject } = user
try { try {
const { rows } = await homeworklist({ edituserid: user.userId, edustage, edusubject, deaddate: getTomorrow(), status: '1', orderby: 'deaddate DESC', pageSize: 500 }) const { rows } = await homeworklist({ edituserid: user.userId, edustage, edusubject, deaddate: getTomorrow(), status: '1', orderby: 'deaddate DESC', pageSize: 500 })
// //
homeworkList.value = rows.filter(item => item.deaddate && item.uniquekey && getCurrentTime('YYYY-MM-DD HH:mm') < item.deaddate) // //homeworkList.value = rows.filter(item => item.deaddate && item.uniquekey && getCurrentTime('YYYY-MM-DD HH:mm') < item.deaddate) //
// homeworkList.value = rows.filter(item => item.deaddate && item.uniquekey && getTomorrow() <= item.deaddate) // // homeworkList.value = rows.filter(item => item.deaddate && item.uniquekey && getTomorrow() <= item.deaddate) //
homeworkList.value = rows || [];
homeworkList.value.forEach((item) => { homeworkList.value.forEach((item) => {
// UI // UI
if (item.worktype == '学习目标定位') { if (item.worktype == '学习目标定位') {
@ -72,7 +74,6 @@ const getHomework = async () => {
item.workclass = '' item.workclass = ''
} }
item.workdatacount = JSON.parse('[' + item.classworkdatastudentids + ']').length
// //
if (item.entpcourseworklist != '') { if (item.entpcourseworklist != '') {
item.entpcourseworklistarray = JSON.parse( item.entpcourseworklistarray = JSON.parse(
@ -102,7 +103,8 @@ const onClickItem = (item) => {
} }
const tagType = (time) => { const tagType = (time) => {
return getCurrentTime('YYYY-MM-DD HH:mm') > time ? 'info' : 'warning' return 'warning';
//return getCurrentTime('YYYY-MM-DD HH:mm') > time ? 'info' : 'warning'
} }

View File

@ -17,10 +17,12 @@
<el-table-column align="center" prop="worktag" width="120"></el-table-column> <el-table-column align="center" prop="worktag" width="120"></el-table-column>
</el-table> </el-table>
<!-- 试题详情 --> <!-- 试题详细信息 -->
<el-drawer v-model="activeExamInfoDrawer" title="题目详情" :with-header="false" direction="rtl" size="60%"> <examDetailsDrawer ref="examDetailsDrawerRef"></examDetailsDrawer>
<!-- <el-drawer v-model="activeExamInfoDrawer" title="题目详情" :with-header="false" direction="rtl" size="60%">
<div style="height: calc(100% - 50px);"> <div style="height: calc(100% - 50px);">
<el-scrollbar style="background: #e9e9e9;border-radius: 8px;"> <el-scrollbar style="background: #F3F5F8;border-radius: 8px;">
<el-row class="drawer-main"> <el-row class="drawer-main">
<el-col :span="24"> <el-col :span="24">
<span>{{activeExam.worktag}}</span> <span>{{activeExam.worktag}}</span>
@ -42,11 +44,13 @@
<div class="drawer-footer"> <div class="drawer-footer">
<el-button type="primary" @click="activeExamInfoDrawer = false">关闭</el-button> <el-button type="primary" @click="activeExamInfoDrawer = false">关闭</el-button>
</div> </div>
</el-drawer> </el-drawer> -->
</template> </template>
<script setup> <script setup>
import {ref, reactive} from 'vue' import {ref, reactive, getCurrentInstance, nextTick} from 'vue'
import examDetailsDrawer from '@/components/exam-question/examDetailsDrawer.vue'
const { proxy } = getCurrentInstance()
const props = defineProps({ const props = defineProps({
listExamQuestion: {type: Array}, listExamQuestion: {type: Array},
@ -57,8 +61,12 @@ const activeExamInfoDrawer = ref(false);
const activeExam = ref({}); const activeExam = ref({});
const showExamAnalyseDrawer = (row) => { const showExamAnalyseDrawer = (row) => {
activeExam.value = row; nextTick(() => {
activeExamInfoDrawer.value = true; const activeParams = {
activeExam: row,
}
proxy.$refs.examDetailsDrawerRef.acceptParams(activeParams);
})
} }

View File

@ -161,7 +161,7 @@ const getData = async (data) => {
const params = { const params = {
eid: curNode.value.id, eid: curNode.value.id,
workgroup: '1', workgroup: '1',
worktype: '不限', worktype: '999', // 使
workTypeId: '0', workTypeId: '0',
edusubject: curNode.value.edusubject, edusubject: curNode.value.edusubject,
edustage: curNode.value.edustage, edustage: curNode.value.edustage,

View File

@ -0,0 +1,333 @@
<template>
<div class="book-wrap">
<el-scrollbar height="100%">
<div class="book-name flex" @click="dialogVisible = true">
<span>{{ curBook.data.itemtitle }}</span>
<i class="iconfont icon-xiangyou"></i>
</div>
<div class="book-list" v-loading="treeLoading">
<el-tree :data="treeData" accordion :props="defaultProps" node-key="id"
:default-expanded-keys="defaultExpandedKeys" :current-node-key="curNode.data.id" highlight-current
@node-click="handleNodeClick">
<template #default="{ node }">
<span :title="node.label" class="tree-label">{{ node.label }}</span>
</template>
</el-tree>
</div>
</el-scrollbar>
</div>
<!--弹窗 选择教材-->
<el-dialog v-model="dialogVisible" append-to-body :show-close="false" width="550"
style="border-radius: 10px; padding: 10px 15px;">
<template #header>
<div class="choose-book-header flex">
<span>切换教材</span>
<i class="iconfont icon-guanbi" @click="dialogVisible = false"></i>
</div>
</template>
<div class="textbook-container">
<el-scrollbar height="450px">
<div class="textbook-item flex" v-for="item in subjectList" :class="curBook.data.id == item.id ? 'active-item' : ''"
:key="item.id" @click="changeBook(item)">
<img v-if="item.avartar" :src="item.avartar.indexOf('http') === 0 ? item.avartar : BaseUrl + item.avartar" class="textbook-img" alt="">
<div v-else class="textbook-img">
<i class="iconfont icon-jiaocaixuanze" style="font-size: 40px;"></i>
</div>
<span class="book-name">{{ item.itemtitle }}</span>
</div>
</el-scrollbar>
</div>
</el-dialog>
</template>
<script setup>
import { onMounted, ref, nextTick, toRaw, reactive } from 'vue';
import { cloneDeep } from 'lodash'
import { listEvaluation } from '@/api/subject'
import { sessionStore } from '@/utils/store'
const BaseUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
// emit
const emit = defineEmits(['nodeClick', 'changeBook'])
// List
const unitList = ref([])
const subjectList = ref([])
const dialogVisible = ref(false)
//
const treeData = ref([])
const defaultProps = {
children: 'children',
label: 'itemtitle',
class: 'textbook-tree'
}
//
const subjectParams = reactive(
{
edusubject: '科学',
edustage:'小学',
itemkey: 'version',
orderby: 'orderidx asc',
pageSize: 10000
}
)
//
const unitParams = reactive({
edusubject:'科学',
edustage:'小学',
itemgroup: 'textbook',
orderby: 'orderidx asc',
pageSize: 10000
})
//
const curBook = reactive({
data: {}
})
//
const curNode = reactive({
data:{}
})
const treeLoading = ref(false)
//
const defaultExpandedKeys = ref([])
//
const changeBook = (data) => {
curBook.data = data
treeData.value = getTreeData(data.id)
//
nextTick(() =>{
defaultExpandedKeys.value = [treeData.value[0].id]
curNode.data = getLastLevelData(treeData.value)[0]
handleNodeClick(curNode.data)
})
//
setTimeout(() => {
dialogVisible.value = false
}, 100);
}
const getLastLevelData = (tree) => {
let lastLevelData = [];
//
function traverseTree(nodes) {
nodes.forEach((node) => {
//
if (node.children && node.children.length > 0) {
traverseTree(node.children);
} else {
//
lastLevelData.push(node);
}
});
}
//
traverseTree(tree);
//
return lastLevelData;
}
// id
const findParentByChildId = (treeData, targetNodeId) => {
//
//
for (let node of treeData) {
// ID
if (node.children && node.children.some(child => child.id === targetNodeId)) {
// ID ID
return node;
}
//
if (node.children) {
let parentNode = findParentByChildId(node.children, targetNodeId);
if (parentNode) {
return parentNode;
}
}
}
// null
return null;
}
const handleNodeClick = (data) => {
/**
* data : 当前节点数据
*/
let nodeData = cloneDeep(toRaw(data));
//label label
nodeData.label = nodeData.itemtitle
// null
let parent = {
id: nodeData.parentid,
label: nodeData.parenttitle,
itemtitle: nodeData.parenttitle
}
const parentNode = nodeData.parentid ? parent : null
nodeData.parentNode = parentNode
let curData = {
textBook: {
curBookId: curBook.data.id,
curBookName: curBook.data.itemtitle,
curBookImg: BaseUrl + curBook.data.avartar,
curBookPath: curBook.data.fileurl
},
node: nodeData
}
// :electron-store
emit('nodeClick', curData)
}
//
const getTreeData = (bookId) =>{
// id
let data = unitList.value.filter(item => item.rootid == bookId && item.level == 1)
data.forEach( item => {
item.children = unitList.value.filter( item2 => item2.parentid == item.id && item2.level == 2)
})
return data
}
onMounted( async () => {
treeLoading.value = true
try{
//
const { rows } = await listEvaluation(subjectParams)
//
subjectList.value = rows
const res = await listEvaluation(unitParams)
unitList.value = [...res.rows]
//
curBook.data = rows[0]
// ""rows
treeData.value = getTreeData(rows[0].id)
nextTick(() =>{
//
defaultExpandedKeys.value = [treeData.value[0].id]
curNode.data = getLastLevelData(treeData.value)[0]
handleNodeClick(curNode.data)
})
} finally{
treeLoading.value = false
}
})
</script>
<style lang="scss" scoped>
.book-wrap {
width: 300px;
height: 100%;
background: #ffffff;
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
display: flex;
flex-direction: column;
position: relative;
.book-name {
background-color: #ffffff;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 45px;
padding: 0 15px;
z-index: 1;
justify-content: space-between;
align-items: center;
color: #3b3b3b;
cursor: pointer;
border-bottom: solid #f4f5f7 1px;
font-size: 15px;
font-weight: 600;
border-radius: 10px 10px 0 0;
}
.book-list {
padding: 45px 10px 0 10px;
flex: 1;
}
}
:deep(.choose-dialog) {
border-radius: 10px;
}
.choose-book-header {
justify-content: space-between;
font-size: 15px;
font-weight: bold;
.icon-guanbi {
font-size: 20px;
cursor: pointer;
}
}
.textbook-container {
.textbook-item {
padding: 10px 20px;
align-items: center;
border-radius: 5px;
cursor: pointer;
.book-name {
margin-left: 20px;
color: #3b3b3b;
font-size: 13px;
}
&:hover {
background: #f4f7f9;
}
}
.active-item {
background-color: #f4f7f9;
.book-name {
color: #368fff;
font-weight: bold
}
}
.textbook-img {
width: 55px;
height: 70px;
display: flex;
align-items: center;
justify-content: center;
}
}
:deep(.el-tree-node) {
.el-tree-node__content {
height: 40px;
border-radius: 10px;
&:hover {
background-color: #eaf3ff;
}
}
}
.tree-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
:deep(.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content) {
background-color: #eaf3ff !important;
color: #409EFF
}
</style>

View File

@ -0,0 +1,25 @@
<template>
<el-dialog v-model="model" class="preview-drawer" :title="row.fileShowName" :modal="true" :destroy-on-close="true" :with-header="false" :append-to-body="true"
width="60%">
<video style="margin: 0 auto;" :src="row.fileFullPath" controls autoplay></video>
</el-dialog>
</template>
<script setup>
const model = defineModel()
const props = defineProps({
row: {
type: Object,
default(){
return {}
}
},
})
</script>
<style scoped>
.header-close {
padding: 0;
cursor: pointer;
text-align: right;
}
</style>

View File

@ -0,0 +1,259 @@
<template>
<div class="page-resource flex">
<!-- 左侧 教材 目录 -->
<experimentBook @node-click="getData"/>
<div class="page-right">
<!-- 排序 -->
<div style="margin-left: 5px;margin-top: 10px;height: 45px;">
<el-form size="large">
<el-form-item label="排序:">
<div
:class="['score-circle', { 'active': active == item.active }]"
v-for="(item,index) in screenList" :key="index" @click="chooseItem(item)">
<el-text
:style="{fontWeight:'bold', color: active == item.active ? 'rgb(57, 184, 244)':'rgb(131,131,131)' }"
size="large">{{ item.title }}</el-text>
</div>
</el-form-item>
</el-form>
</div>
<div class="list-content">
<div class="list-container" v-loading="loading">
<div v-for="(item, index) in experimentList" :key="index" class="content">
<div class="content-list">
<!-- 封面 -->
<el-image style="width: 100%;border-radius: 8px;" :src="item.coverPic" fit="contain" @click="chooseVedio(item)"/>
</div>
<!-- 标题 -->
<div style="text-align: left;">
<el-text>{{ item.fileShowName }}</el-text>
</div>
<!-- 观看人数 -->
<!-- <div style="text-align: left;display: flex;align-items: center;">
<el-icon type="info"><View /></el-icon><el-text size="small" type="info">{{ item.nums }}</el-text>
</div> -->
</div>
</div>
<div class="pagination-box">
<el-pagination
v-model:current-page="query.pageNum"
v-model:page-size="query.pageSize"
:page-sizes="[20, 30, 50, 100]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="resultTotal"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</div>
</div>
<!-- 播放视频 -->
<VideoLog v-model="isShow" :row="curRow"></VideoLog>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import experimentBook from './components/experimentBook.vue';
import { View } from '@element-plus/icons-vue'
import { getSmarttalkPage } from '@/api/file/index'
import VideoLog from './components/VideoLog.vue'
//
const screenList = ref([
{
title: '最新发布',
active: 1,
}
])
const active = ref(1)
//
const resultTotal = ref(0)
//
const isShow = ref(false)
const curRow = ref({})
// loading
const loading = ref(false)
//
const experimentList = ref([])
//
const query = ref({
textbookId: '',
fileSource: '平台',
// mp3 ppt ...
fileSuffix: 'mp4',
fileFlags: "'素材'",
fileRoot: '资源',
fileName: '',
orderByColumn: 'uploadTime',
isAsc: 'desc',
pageNum: 1,
pageSize: 20,
levelFirstId: 0,
levelSecondId: 0
})
const getData = (data) => {
const { textBook, node } = data
if (node.parentNode) {
query.value.levelFirstId = node.parentNode.id
query.value.levelSecondId = node.id
} else {
query.value.levelFirstId = node.id
query.value.levelSecondId = ''
}
query.value.textbookId = node.rootid
getVideoList()
}
const chooseItem = (item) => {
active.value = item.active
}
//
const getVideoList = async () => {
loading.value = true
const res = await getSmarttalkPage(query.value)
loading.value = false
experimentList.value = [...res.rows]
resultTotal.value = res.total
}
const handleSizeChange = (limit) => {
query.pageNum = limit
getVideoList()
}
const handleCurrentChange = (page) => {
query.pageSize = page
getVideoList()
}
const chooseVedio = (item) => {
isShow.value = true
curRow.value = item
}
</script>
<style lang="scss" scoped>
.page-resource {
height: 100%;
padding: 10px 15px 0;
.page-right {
min-width: 0;
display: flex;
flex-direction: column;
flex: 1;
margin-left: 20px;
height: 100%;
background: #ffffff;
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
}
.icon-jiahao {
font-size: 12px;
margin-right: 3px;
font-weight: bold;
}
}
.create-btn {
font-size: 13px;
padding: 5px 13px;
}
.list-content {
border-radius: 8px;
height: 90%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.list-container {
display: flex;
flex-wrap: wrap;
overflow-y: auto;
}
.content {
border-radius: 8px;
// box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
width: calc(20%);
cursor: pointer;
transition: all 0.3s ease;
padding: 5px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.content:hover {
transform: translateY(-4px);
// box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.15);
}
.content-list{
height: 100%;
display: flex;
align-items: center
}
.item-content {
display: flex;
align-items: center;
}
.item-icon {
font-size: 24px;
color: #409eff;
margin-right: 16px;
}
.item-text {
flex: 1;
}
.item-title {
font-size: 16px;
font-weight: 500;
color: #303133;
margin-bottom: 4px;
font-weight: bold;
}
.title-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.item-bottom {
text-align: right;
}
/* 过渡动画 */
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
.score-circle {
background-color: #fff;
cursor: pointer;
margin-right: 5px;
width: auto;
text-align: center;
padding: 0 10px;
}
.score-circle.active {
background-color: rgb(218, 236, 255);
color: white;
}
.pagination-box {
display: flex;
justify-content: center;
height: 65px;
}
</style>

View File

@ -18,7 +18,10 @@ const loadPdfAnimation = (path) => {
},2000) },2000)
} }
onMounted(() => { onMounted(() => {
const bookpath = localStorage.getItem('PDF-LOCAL-PATH') const pdfUrl1 = localStorage.getItem('PDF-TOOL-PATH')
const pdfUrl2 = localStorage.getItem('PDF-LOCAL-PATH')
console.log('tool-pdf 地址: ',pdfUrl1, pdfUrl2)
const bookpath = pdfUrl1 || pdfUrl2
// const filepath = import.meta.env.VITE_APP_RES_FILE_PATH + bookpath // const filepath = import.meta.env.VITE_APP_RES_FILE_PATH + bookpath
// const isDev = process.env.NODE_ENV == 'development' // const isDev = process.env.NODE_ENV == 'development'
// if (isDev) // if (isDev)
@ -28,7 +31,7 @@ onMounted(() => {
// const newpath = getStaticUrl(bookpath, 'user', 'selfFile', true) // const newpath = getStaticUrl(bookpath, 'user', 'selfFile', true)
loadPdfAnimation(bookpath) loadPdfAnimation(bookpath)
// pdfUrl.value = filepath // pdfUrl.value = filepath
// console.log('',newpath); // console.log('',bookpath);
}) })
</script> </script>
<style> <style>

View File

@ -28,7 +28,7 @@
<el-input v-model="ruleForm.phoneNumber" placeholder="请输入手机号" /> <el-input v-model="ruleForm.phoneNumber" placeholder="请输入手机号" />
</el-form-item> </el-form-item>
<el-form-item label="验证码" prop="Code" v-if="activeIndex==1"> <el-form-item label="验证码" prop="Code" v-if="activeIndex==1">
<el-input style="width:60%" v-model="ruleForm.Code" :disabled="true" placeholder="请输入验证码" /> <el-input style="width:60%" v-model="ruleForm.Code" :disabled="false" placeholder="请输入验证码" />
<el-button type="primary" style="margin-left:10px" @click="sendcaptchaImg">发送验证码</el-button> <el-button type="primary" style="margin-left:10px" @click="sendcaptchaImg">发送验证码</el-button>
</el-form-item> </el-form-item>
<el-form-item label="设置密码" prop="password" v-if="activeIndex==1"> <el-form-item label="设置密码" prop="password" v-if="activeIndex==1">
@ -409,11 +409,13 @@ const sbmitImg=()=>{
type: type.value type: type.value
} }
sendCode(params).then(res=>{ sendCode(params).then(res=>{
if(res.code==200){
ruleForm.Code=res.data
isImg.value=false isImg.value=false
if(res.code==200){
// ruleForm.Code=res.data
ElMessage.success(res.msg||'验证码-已发送')
} }
}).catch(err=>{
isImg.value=false
}) })
}else{ }else{
ElMessage.error('请根据图片输入验证码') ElMessage.error('请根据图片输入验证码')

View File

@ -0,0 +1,264 @@
<template>
<div class="login-container">
<div class="box-item desc">
<div class="welcome">
<p>欢迎登录 {{ homeTitle }}</p>
</div>
<img class="welcome-img" :src="leftBg2" />
</div>
<div class="box-item login">
<WindowTools :is-has-max="false" />
<div class="login-title">账号登录</div>
<el-form ref="formRef" class="login-form" :model="loginForm" :rules="rules" size="large">
<el-form-item prop="username">
<el-input v-model.trim="loginForm.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item prop="password" style="margin-bottom: 15px">
<el-input v-model="loginForm.password" autocomplete="on" type="password" placeholder="请输入密码" />
</el-form-item>
<div class="flex mb-5">
<el-checkbox v-model="loginForm.rememberMe">记住密码</el-checkbox>
<!-- <el-checkbox >阅读并同意xxx</el-checkbox> -->
</div>
<el-form-item>
<el-button :loading="btnLoading" class="btn" type="primary" @click="submitForm(formRef)">登录</el-button>
</el-form-item>
<div class="flex mb-4" style="display: flex;justify-content: center;color: #ccc;cursor: pointer;">
<a class="hover:text-sky-500" style="margin-right: 10px;" @click="RegisterModel(1)">注册账号</a>
|
<a class="hover:text-sky-500" style="margin-left: 10px;" @click="RegisterModel(2)">忘记密码</a>
</div>
</el-form>
</div>
</div>
<el-dialog v-model="showDownLoading" width="500" :show-close="false" :close-on-click-modal="false"
:close-on-press-escape="false" align-center>
<el-progress :text-inside="true" :stroke-width="22" :percentage="downloadProp" :show-text="false"
status="success" />
</el-dialog>
<!--选择学科-->
<SelectSubject v-model="isSubject" :login-data="loginForm" />
<!--注册弹框-->
<Register ref="RegModel"></Register>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { encrypt, decrypt } from '@/utils/jsencrypt'
import useUserStore from '@/store/modules/user'
import leftBg2 from '@/assets/images/login/left-bg2.png'
import WindowTools from '@/components/window-tools/index.vue'
import SelectSubject from '@/components/select-subject/index.vue'
import Register from './components/Register.vue'
import { sessionStore } from '@/utils/store'
const { session } = require('@electron/remote')
const downloadProp = ref(0)
const showDownLoading = ref(false)
const { ipcRenderer } = window.electron || {}
const formRef = ref()
const userStore = useUserStore()
const btnLoading = ref(false)
const isSubject = ref(false)
const RegModel = ref(false)
//
const loginForm = reactive({
username: '',
password: '',
rememberMe: false
})
//
const rules = reactive({
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }]
})
let curWinUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
let homeTitle = ref(import.meta.env.VITE_APP_TITLE)
ipcRenderer.on('update-app-progress', (e, prop) => {
downloadProp.value = prop
showDownLoading.value = prop !== 100
})
//
const RegisterModel = type => {
RegModel.value.OpenModel(type)
}
//
const submitForm = async (formEl) => {
if (!formEl) return
await formEl.validate(async (valid) => {
if (valid) {
btnLoading.value = true
// cookie
if (loginForm.rememberMe) {
await setCookie('username', loginForm.username)
await setCookie('password', encrypt(loginForm.password))
await setCookie('rememberMe', loginForm.rememberMe.toString())
} else {
//
await session.defaultSession.clearStorageData({
origin: curWinUrl,
storages: ['cookies']
})
}
try {
await userStore.login(loginForm)
await userStore.getInfo()
if (userStore.user.edustage || userStore.user.edusubject) {
ElMessage.success('登录成功')
ipcRenderer && ipcRenderer.send('openMainWindow')
} else {
isSubject.value = true
}
} finally {
btnLoading.value = false
}
}
})
}
const getCookie = async () => {
const username = (await getCookieDetail('username'))[0]
const password = (await getCookieDetail('password'))[0]
const rememberMe = (await getCookieDetail('rememberMe'))[0]
loginForm.username = username ? username.value : loginForm.username
loginForm.password = password ? decrypt(password.value) : loginForm.password
loginForm.rememberMe = rememberMe ? Boolean(rememberMe.value) : false
}
// cookie
const getCookieDetail = (name) => {
return session.defaultSession.cookies.get({ url: curWinUrl, name })
}
// cookie
const setCookie = (name, value) => {
// 30
let Days = 30
let times = Math.round(Date.now() / 1000) + Days * 24 * 60 * 60
const cookie = {
url: curWinUrl,
name,
value,
expirationDate: times
}
return session.defaultSession.cookies.set(cookie)
}
onMounted(() => {
localStorage.clear()
sessionStore.set('subject', {
bookList: null,
curBook: null,
curNode: null,
defaultExpandedKeys: [],
subjectTree: []
})
getCookie()
})
</script>
<style lang="scss" scoped>
.login-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
-webkit-app-region: drag;
.box-item {
width: 444px;
height: 520px;
&.desc {
background: #ffffff;
border-radius: 12px 0px 0px 12px;
box-shadow: 0px 16px 73px 8px rgba(203, 203, 203, 0.2);
padding: 23px 25px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
background-color: #003b94;
}
&.login {
background: #ffffff;
border-radius: 0px 12px 12px 0px;
padding: 34px 42px;
position: relative;
}
.welcome {
padding-top: 35px;
p {
color: #ffffff;
line-height: 25px;
letter-spacing: 0.26px;
text-align: center;
font-weight: 700;
font-size: 26px;
}
}
.welcome-img {
margin-top: 20px;
width: 350px;
height: 350px;
}
.login-title {
font-size: 20px;
text-align: center;
color: #1e1e1e;
margin-bottom: 35px;
margin-top: 50px;
}
.login-form {
-webkit-app-region: no-drag;
.captcha-input {
width: 60%;
}
.captcha-img {
cursor: pointer;
}
}
.btn {
width: 350px;
height: 50px;
border-radius: 4px;
font-size: 16px;
font-weight: 700;
text-align: center;
color: #ffffff;
line-height: 50px;
cursor: pointer;
}
}
}
.header-tool {
position: absolute;
right: 0;
top: 0;
-webkit-app-region: no-drag;
span {
padding: 5px 10px;
cursor: pointer;
}
}
.el-form-item {
margin-bottom: 40px;
}
</style>

View File

@ -1,264 +1,13 @@
<template> <template>
<div class="login-container"> <ycLogin v-if="buildMode === 'yc'">
<div class="box-item desc"> </ycLogin>
<div class="welcome"> <defultLogin v-else>
<p>欢迎登录 {{ homeTitle }}</p> </defultLogin>
</div>
<img class="welcome-img" :src="leftBg2" />
</div>
<div class="box-item login">
<WindowTools :is-has-max="false" />
<div class="login-title">账号登录</div>
<el-form ref="formRef" class="login-form" :model="loginForm" :rules="rules" size="large">
<el-form-item prop="username">
<el-input v-model.trim="loginForm.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item prop="password" style="margin-bottom: 15px">
<el-input v-model="loginForm.password" autocomplete="on" type="password" placeholder="请输入密码" />
</el-form-item>
<div class="flex mb-5">
<el-checkbox v-model="loginForm.rememberMe">记住密码</el-checkbox>
<!-- <el-checkbox >阅读并同意xxx</el-checkbox> -->
</div>
<el-form-item>
<el-button :loading="btnLoading" class="btn" type="primary" @click="submitForm(formRef)">登录</el-button>
</el-form-item>
<div class="flex mb-4" style="display: flex;justify-content: center;color: #ccc;cursor: pointer;">
<a class="hover:text-sky-500" style="margin-right: 10px;" @click="RegisterModel(1)">注册账号</a>
|
<a class="hover:text-sky-500" style="margin-left: 10px;" @click="RegisterModel(2)">忘记密码</a>
</div>
</el-form>
</div>
</div>
<el-dialog v-model="showDownLoading" width="500" :show-close="false" :close-on-click-modal="false"
:close-on-press-escape="false" align-center>
<el-progress :text-inside="true" :stroke-width="22" :percentage="downloadProp" :show-text="false"
status="success" />
</el-dialog>
<!--选择学科-->
<SelectSubject v-model="isSubject" :login-data="loginForm" />
<!--注册弹框-->
<Register ref="RegModel"></Register>
</template> </template>
<script setup> <script setup>
import { onMounted, reactive, ref } from 'vue' import ycLogin from './yc-login.vue'
import { ElMessage } from 'element-plus' import defultLogin from './defult-login.vue'
import { encrypt, decrypt } from '@/utils/jsencrypt' const buildMode = import.meta.env.MODE
import useUserStore from '@/store/modules/user'
import leftBg2 from '@/assets/images/login/left-bg2.png'
import WindowTools from '@/components/window-tools/index.vue'
import SelectSubject from '@/components/select-subject/index.vue'
import Register from './components/Register.vue'
import { sessionStore } from '@/utils/store'
const { session } = require('@electron/remote')
const downloadProp = ref(0)
const showDownLoading = ref(false)
const { ipcRenderer } = window.electron || {}
const formRef = ref()
const userStore = useUserStore()
const btnLoading = ref(false)
const isSubject = ref(false)
const RegModel = ref(false)
//
const loginForm = reactive({
username: '',
password: '',
rememberMe: false
})
//
const rules = reactive({
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }]
})
let curWinUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
let homeTitle = ref(import.meta.env.VITE_APP_TITLE)
ipcRenderer.on('update-app-progress', (e, prop) => {
downloadProp.value = prop
showDownLoading.value = prop !== 100
})
//
const RegisterModel = type => {
RegModel.value.OpenModel(type)
}
//
const submitForm = async (formEl) => {
if (!formEl) return
await formEl.validate(async (valid) => {
if (valid) {
btnLoading.value = true
// cookie
if (loginForm.rememberMe) {
await setCookie('username', loginForm.username)
await setCookie('password', encrypt(loginForm.password))
await setCookie('rememberMe', loginForm.rememberMe.toString())
} else {
//
await session.defaultSession.clearStorageData({
origin: curWinUrl,
storages: ['cookies']
})
}
try {
await userStore.login(loginForm)
await userStore.getInfo()
if (userStore.user.edustage || userStore.user.edusubject) {
ElMessage.success('登录成功')
ipcRenderer && ipcRenderer.send('openMainWindow')
} else {
isSubject.value = true
}
} finally {
btnLoading.value = false
}
}
})
}
const getCookie = async () => {
const username = (await getCookieDetail('username'))[0]
const password = (await getCookieDetail('password'))[0]
const rememberMe = (await getCookieDetail('rememberMe'))[0]
loginForm.username = username ? username.value : loginForm.username
loginForm.password = password ? decrypt(password.value) : loginForm.password
loginForm.rememberMe = rememberMe ? Boolean(rememberMe.value) : false
}
// cookie
const getCookieDetail = (name) => {
return session.defaultSession.cookies.get({ url: curWinUrl, name })
}
// cookie
const setCookie = (name, value) => {
// 30
let Days = 30
let times = Math.round(Date.now() / 1000) + Days * 24 * 60 * 60
const cookie = {
url: curWinUrl,
name,
value,
expirationDate: times
}
return session.defaultSession.cookies.set(cookie)
}
onMounted(() => {
localStorage.clear()
sessionStore.set('subject', {
bookList: null,
curBook: null,
curNode: null,
defaultExpandedKeys: [],
subjectTree: []
})
getCookie()
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.login-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
-webkit-app-region: drag;
.box-item {
width: 444px;
height: 520px;
&.desc {
background: #ffffff;
border-radius: 12px 0px 0px 12px;
box-shadow: 0px 16px 73px 8px rgba(203, 203, 203, 0.2);
padding: 23px 25px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
background-color: #003b94;
}
&.login {
background: #ffffff;
border-radius: 0px 12px 12px 0px;
padding: 34px 42px;
position: relative;
}
.welcome {
padding-top: 35px;
p {
color: #ffffff;
line-height: 25px;
letter-spacing: 0.26px;
text-align: center;
font-weight: 700;
font-size: 26px;
}
}
.welcome-img {
margin-top: 20px;
width: 350px;
height: 350px;
}
.login-title {
font-size: 20px;
text-align: center;
color: #1e1e1e;
margin-bottom: 35px;
margin-top: 50px;
}
.login-form {
-webkit-app-region: no-drag;
.captcha-input {
width: 60%;
}
.captcha-img {
cursor: pointer;
}
}
.btn {
width: 350px;
height: 50px;
border-radius: 4px;
font-size: 16px;
font-weight: 700;
text-align: center;
color: #ffffff;
line-height: 50px;
cursor: pointer;
}
}
}
.header-tool {
position: absolute;
right: 0;
top: 0;
-webkit-app-region: no-drag;
span {
padding: 5px 10px;
cursor: pointer;
}
}
.el-form-item {
margin-bottom: 40px;
}
</style> </style>

View File

@ -0,0 +1,279 @@
<template>
<div class="login-container">
<div class="login-yc">
<img class="welcome-img" :src="leftBg1" />
</div>
<div class="box-item login">
<WindowTools :is-has-max="false" />
<div style="display: flex;justify-content: center;"><img class="title-logo" :src="yclogo" /></div>
<div class="login-title">永川中小学</div>
<div class="login-title2">人工智能赋能科学素养与劳动技能系统</div>
<el-form ref="formRef" class="login-form" :model="loginForm" :rules="rules" size="large">
<el-form-item prop="username">
<el-input v-model.trim="loginForm.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item prop="password" style="margin-bottom: 15px">
<el-input v-model="loginForm.password" autocomplete="on" type="password" placeholder="请输入密码" />
</el-form-item>
<div class="flex mb-5">
<el-checkbox v-model="loginForm.rememberMe">记住密码</el-checkbox>
<!-- <el-checkbox >阅读并同意xxx</el-checkbox> -->
</div>
<el-form-item style="margin-bottom: 20px;">
<el-button :loading="btnLoading" class="btn" type="primary" @click="submitForm(formRef)">登录</el-button>
</el-form-item>
<div class="flex mb-4" style="display: flex;justify-content: center;color: #ccc;cursor: pointer;">
<a class="hover:text-sky-500" style="margin-right: 10px;" @click="RegisterModel(1)">注册账号</a>
|
<a class="hover:text-sky-500" style="margin-left: 10px;" @click="RegisterModel(2)">忘记密码</a>
</div>
<div class="title-bottom">
重庆市永川区教育委员会
</div>
</el-form>
</div>
</div>
<el-dialog v-model="showDownLoading" width="500" :show-close="false" :close-on-click-modal="false"
:close-on-press-escape="false" align-center>
<el-progress :text-inside="true" :stroke-width="22" :percentage="downloadProp" :show-text="false"
status="success" />
</el-dialog>
<!--选择学科-->
<SelectSubject v-model="isSubject" :login-data="loginForm" />
<!--注册弹框-->
<Register ref="RegModel"></Register>
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { encrypt, decrypt } from '@/utils/jsencrypt'
import useUserStore from '@/store/modules/user'
import yclogo from '@/assets/images/login/yc-logo.png'
import leftBg1 from '@/assets/images/login/ycpeitu.png'
import WindowTools from '@/components/window-tools/index.vue'
import SelectSubject from '@/components/select-subject/index.vue'
import Register from './components/Register.vue'
import { sessionStore } from '@/utils/store'
const { session } = require('@electron/remote')
const downloadProp = ref(0)
const showDownLoading = ref(false)
const { ipcRenderer } = window.electron || {}
const formRef = ref()
const userStore = useUserStore()
const btnLoading = ref(false)
const isSubject = ref(false)
const RegModel = ref(false)
//
const loginForm = reactive({
username: '',
password: '',
rememberMe: false
})
//
const rules = reactive({
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }]
})
let curWinUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
ipcRenderer.on('update-app-progress', (e, prop) => {
downloadProp.value = prop
showDownLoading.value = prop !== 100
})
//
const RegisterModel = type => {
RegModel.value.OpenModel(type)
}
//
const submitForm = async (formEl) => {
if (!formEl) return
await formEl.validate(async (valid) => {
if (valid) {
btnLoading.value = true
// cookie
if (loginForm.rememberMe) {
await setCookie('username', loginForm.username)
await setCookie('password', encrypt(loginForm.password))
await setCookie('rememberMe', loginForm.rememberMe.toString())
} else {
//
await session.defaultSession.clearStorageData({
origin: curWinUrl,
storages: ['cookies']
})
}
try {
await userStore.login(loginForm)
await userStore.getInfo()
if (userStore.user.edustage || userStore.user.edusubject) {
ElMessage.success('登录成功')
ipcRenderer && ipcRenderer.send('openMainWindow')
} else {
isSubject.value = true
}
} finally {
btnLoading.value = false
}
}
})
}
const getCookie = async () => {
const username = (await getCookieDetail('username'))[0]
const password = (await getCookieDetail('password'))[0]
const rememberMe = (await getCookieDetail('rememberMe'))[0]
loginForm.username = username ? username.value : loginForm.username
loginForm.password = password ? decrypt(password.value) : loginForm.password
loginForm.rememberMe = rememberMe ? Boolean(rememberMe.value) : false
}
// cookie
const getCookieDetail = (name) => {
return session.defaultSession.cookies.get({ url: curWinUrl, name })
}
// cookie
const setCookie = (name, value) => {
// 30
let Days = 30
let times = Math.round(Date.now() / 1000) + Days * 24 * 60 * 60
const cookie = {
url: curWinUrl,
name,
value,
expirationDate: times
}
return session.defaultSession.cookies.set(cookie)
}
onMounted(() => {
localStorage.clear()
sessionStore.set('subject', {
bookList: null,
curBook: null,
curNode: null,
defaultExpandedKeys: [],
subjectTree: []
})
getCookie()
})
</script>
<style lang="scss" scoped>
.login-container {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
-webkit-app-region: drag;
.box-item {
width: 444px;
height: 520px;
&.desc {
background: #ffffff;
border-radius: 12px 0px 0px 12px;
box-shadow: 0px 16px 73px 8px rgba(203, 203, 203, 0.2);
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
background-color: #003b94;
}
&.login {
background: #ffffff;
border-radius: 0px 12px 12px 0px;
padding: 34px 15px;
position: relative;
.title-logo{
width: 50px;
}
}
.welcome {
padding-top: 35px;
p {
color: #ffffff;
line-height: 25px;
letter-spacing: 0.26px;
text-align: center;
font-weight: 700;
font-size: 26px;
}
}
/*.welcome-img {
margin-top: 20px;
width: 350px;
height: 350px;
}*/
.login-title {
font-size: 20px;
text-align: center;
color: #1e1e1e;
margin-bottom: 10px;
margin-top: 5px;
font-width: bold;
}
.login-title2 {
margin-bottom: 20px;
}
.login-form {
-webkit-app-region: no-drag;
.captcha-input {
width: 60%;
}
.captcha-img {
cursor: pointer;
}
.title-bottom{
text-align: center;
width: 100%;
font-size: 14px;
color: rgba(0, 0, 0, 0.45);
}
}
.btn {
width: 350px;
height: 50px;
border-radius: 4px;
font-size: 16px;
font-weight: 700;
text-align: center;
color: #ffffff;
line-height: 50px;
cursor: pointer;
}
}
}
.header-tool {
position: absolute;
right: 0;
top: 0;
-webkit-app-region: no-drag;
span {
padding: 5px 10px;
cursor: pointer;
}
}
.el-form-item {
margin-bottom: 40px;
}
</style>

View File

@ -0,0 +1,308 @@
<template>
<div class="book-wrap">
<el-scrollbar height="100%">
<div class="book-name flex" @click="dialogVisible = true">
<span>{{ curBook.data.itemtitle }}</span>
<i class="iconfont icon-xiangyou"></i>
</div>
<div class="book-list" v-loading="treeLoading">
<el-tree :data="treeData" accordion :props="defaultProps" node-key="id"
:default-expanded-keys="defaultExpandedKeys" :current-node-key="curNode.data.id" highlight-current
@node-click="handleNodeClick">
<template #default="{ node }">
<span :title="node.label" class="tree-label">{{ node.label }}</span>
</template>
</el-tree>
</div>
</el-scrollbar>
</div>
<!--弹窗 选择教材-->
<el-dialog v-model="dialogVisible" append-to-body :show-close="false" width="550"
style="border-radius: 10px; padding: 10px 15px;">
<template #header>
<div class="choose-book-header flex">
<span>切换教材</span>
<i class="iconfont icon-guanbi" @click="dialogVisible = false"></i>
</div>
</template>
<div class="textbook-container">
<el-scrollbar height="450px">
<div class="textbook-item flex" v-for="item in subjectList" :class="curBook.data.id == item.id ? 'active-item' : ''"
:key="item.id" @click="changeBook(item)">
<img v-if="item.avartar" :src="item.avartar.indexOf('http') === 0 ? item.avartar : BaseUrl + item.avartar" class="textbook-img" alt="">
<div v-else class="textbook-img">
<i class="iconfont icon-jiaocaixuanze" style="font-size: 40px;"></i>
</div>
<span class="book-name">{{ item.itemtitle }}</span>
</div>
</el-scrollbar>
</div>
</el-dialog>
</template>
<script setup>
import { onMounted, ref, nextTick, toRaw, reactive } from 'vue';
import { cloneDeep } from 'lodash'
import { sessionStore } from '@/utils/store'
import { useGetSubject } from '@/hooks/useGetSubject'
const BaseUrl = import.meta.env.VITE_APP_BUILD_BASE_PATH
// emit
const emit = defineEmits(['nodeClick', 'changeBook'])
let useSubject = null
const subjectList = ref([])
const dialogVisible = ref(false)
//
const treeData = ref([])
const defaultProps = {
children: 'children',
label: 'itemtitle',
class: 'textbook-tree'
}
//
const curBook = reactive({
data: {}
})
//
const curNode = reactive({
data:{}
})
const treeLoading = ref(false)
//
const defaultExpandedKeys = ref([])
//
const changeBook = (data) => {
curBook.data = data
treeData.value = useSubject.getTreeData(data.id)
//
nextTick(() =>{
defaultExpandedKeys.value = [treeData.value[0].id]
curNode.data = getLastLevelData(treeData.value)[0]
handleNodeClick(curNode.data)
})
//
setTimeout(() => {
dialogVisible.value = false
}, 100);
}
const getLastLevelData = (tree) => {
let lastLevelData = [];
//
function traverseTree(nodes) {
nodes.forEach((node) => {
//
if (node.children && node.children.length > 0) {
traverseTree(node.children);
} else {
//
lastLevelData.push(node);
}
});
}
//
traverseTree(tree);
//
return lastLevelData;
}
// id
const findParentByChildId = (treeData, targetNodeId) => {
//
//
for (let node of treeData) {
// ID
if (node.children && node.children.some(child => child.id === targetNodeId)) {
// ID ID
return node;
}
//
if (node.children) {
let parentNode = findParentByChildId(node.children, targetNodeId);
if (parentNode) {
return parentNode;
}
}
}
// null
return null;
}
const handleNodeClick = (data) => {
/**
* data : 当前节点数据
*/
let nodeData = cloneDeep(toRaw(data));
//label label
nodeData.label = nodeData.itemtitle
// null
let parent = {
id: nodeData.parentid,
label: nodeData.parenttitle,
itemtitle: nodeData.parenttitle
}
const parentNode = nodeData.parentid ? parent : null
nodeData.parentNode = parentNode
let curData = {
textBook: {
curBookId: curBook.data.id,
curBookName: curBook.data.itemtitle,
curBookImg: BaseUrl + curBook.data.avartar,
curBookPath: curBook.data.fileurl
},
node: nodeData
}
// :electron-store
emit('nodeClick', curData)
}
onMounted( async () => {
treeLoading.value = true
try{
useSubject = await useGetSubject()
subjectList.value = sessionStore.get('subject.bookList')
//
if(sessionStore.get('subject.curBook')){
curBook.data = sessionStore.get('subject.curBook')
}
else{
curBook.data = subjectList.value[0]
}
// ""
treeData.value = useSubject.getTreeData(curBook.data.id)
nextTick(() =>{
//
if(sessionStore.get('subject.curNode')){
defaultExpandedKeys.value = sessionStore.get('subject.defaultExpandedKeys')
curNode.data = sessionStore.get('subject.curNode')
}else{
defaultExpandedKeys.value = [treeData.value[0].id]
curNode.data = getLastLevelData(treeData.value)[0]
}
handleNodeClick(curNode.data)
})
} finally{
treeLoading.value = false
}
})
</script>
<style lang="scss" scoped>
.book-wrap {
width: 300px;
height: 100%;
background: #ffffff;
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(99, 99, 99, 0.06);
display: flex;
flex-direction: column;
position: relative;
.book-name {
background-color: #ffffff;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 45px;
padding: 0 15px;
z-index: 1;
justify-content: space-between;
align-items: center;
color: #3b3b3b;
cursor: pointer;
border-bottom: solid #f4f5f7 1px;
font-size: 15px;
font-weight: 600;
border-radius: 10px 10px 0 0;
}
.book-list {
padding: 45px 10px 0 10px;
flex: 1;
}
}
:deep(.choose-dialog) {
border-radius: 10px;
}
.choose-book-header {
justify-content: space-between;
font-size: 15px;
font-weight: bold;
.icon-guanbi {
font-size: 20px;
cursor: pointer;
}
}
.textbook-container {
.textbook-item {
padding: 10px 20px;
align-items: center;
border-radius: 5px;
cursor: pointer;
.book-name {
margin-left: 20px;
color: #3b3b3b;
font-size: 13px;
}
&:hover {
background: #f4f7f9;
}
}
.active-item {
background-color: #f4f7f9;
.book-name {
color: #368fff;
font-weight: bold
}
}
.textbook-img {
width: 55px;
height: 70px;
display: flex;
align-items: center;
justify-content: center;
}
}
:deep(.el-tree-node) {
.el-tree-node__content {
height: 40px;
border-radius: 10px;
&:hover {
background-color: #eaf3ff;
}
}
}
.tree-label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
:deep(.el-tree--highlight-current .el-tree-node.is-current>.el-tree-node__content) {
background-color: #eaf3ff !important;
color: #409EFF
}
</style>

View File

@ -0,0 +1,64 @@
<template>
<div style="padding: 10px;">
<el-dialog
v-model="dialogVisible"
width="350"
append-to-body
>
<div style="display: flex;justify-content: center;">
<ChooseTextbook @node-click="nodeClick" />
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click.stop="save">确定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, defineExpose,defineEmits } from 'vue'
import ChooseTextbook from './chooseTextbook.vue'
const emit = defineEmits(['onsuccess'])
const dialogVisible = ref(false)
const getNodeInfo = ref({})
const openDialog = () => {
dialogVisible.value = true
}
const getFullObj = (node) => {
const obj = []
const recursive = (currentNode) => {
//
if (currentNode.parentNode) {
obj.unshift({id: currentNode.id,title:currentNode.itemtitle})
recursive(currentNode.parentNode)
} else {
obj.unshift({id: currentNode.id,title:currentNode.itemtitle})
}
}
recursive(node)
return obj
}
const nodeClick = (data) => {
getNodeInfo.value = {
textbookId:data.node.rootid,
cataList:getFullObj(data.node)
}
console.log(getNodeInfo.value,'log')
}
const save = () => {
dialogVisible.value = false
emit('onsuccess', getNodeInfo.value)
}
defineExpose({
openDialog
})
</script>

View File

@ -620,7 +620,7 @@ const changeCursor = (cursorStyle) => {
}; };
onMounted(() => { onMounted(() => {
// let url = "https://bjcdn.openstorage.cn/xinghuo-privatedata/%2Ftmp/apiTempFiledf28bf990a4c40ffb7477ed4b65392c27232357022409613439/%E3%80%8A%E9%9D%99%E5%A5%B3%E3%80%8B%E6%B7%B1%E5%BA%A6%E8%A7%A3%E8%AF%BB%E4%B8%8E%E7%A0%94%E7%A9%B6.pptx" // let url = "https://bjcdn.openstorage.cn/xinghuo-privatedata/%2Ftmp/apiTempFileba724e0344f74e1480535eedf3ebec661601807661085006275/%E9%87%91%E9%A9%AC%E5%A5%96%E5%B0%B4%E5%B0%AC%E4%BA%8B%E4%BB%B6%E5%88%86%E6%9E%90%E4%B8%8E%E5%BA%94%E5%AF%B9%E7%AD%96%E7%95%A5.pptx"
// creatAIPPT(props.currentNode.itemtitle + '.pptx',url, props.uploadData).then((res) => { // creatAIPPT(props.currentNode.itemtitle + '.pptx',url, props.uploadData).then((res) => {
// emit('addSuccess',res) // emit('addSuccess',res)
// }) // })

View File

@ -226,7 +226,7 @@ const getClasscourseList = async type => {
const {classid} = classForm.form const {classid} = classForm.form
const {entpcourseid} = myClassActive.value const {entpcourseid} = myClassActive.value
if (!classid || !entpcourseid) return if (!classid || !entpcourseid) return
const params = {classid, entpcourseid, entpcoursefileid: 0} const params = {classid, entpcourseid, entpcoursefileid: 0, pageSize: 1000}
const res = await Http_Classcourse.listClasscourse(params) const res = await Http_Classcourse.listClasscourse(params)
if (res.code == 200) { if (res.code == 200) {
// zdg: id() // zdg: id()
@ -255,9 +255,9 @@ const createClasscourse = async () => {
entpcourseid, evalid, coursetitle, entpcourseid, evalid, coursetitle,
plandate: curDate, opendate: curDate plandate: curDate, opendate: curDate
} }
await Http_Classcourse.addClasscourseReturnId(params) teacherForm.form.classcourseid = await Http_Classcourse.addClasscourseReturnId(params)
dt.loading = false dt.loading = false
getClasscourseList('update') // // getClasscourseList('update') //
ElMessage.success('创建课程-成功') ElMessage.success('创建课程-成功')
} }

View File

@ -126,6 +126,12 @@
<span>下载</span> <span>下载</span>
</el-button> </el-button>
</div> </div>
<div v-if="item.fileSuffix === 'ppt' || item.fileSuffix === 'pptx'" class="item-popover-item">
<el-button text @click="adToKj(item)">
<i class="iconfont icon-jiahao"></i>
<span>加入课件</span>
</el-button>
</div>
<div class="item-popover-item"> <div class="item-popover-item">
<el-button text @click="moveSmarttalkFun(item)"> <el-button text @click="moveSmarttalkFun(item)">
<el-icon> <el-icon>
@ -153,7 +159,7 @@ import FileImage from '@/components/file-image/index.vue'
import { asyncLocalFile } from '@/utils/talkFile' import { asyncLocalFile } from '@/utils/talkFile'
import { toTimeText } from '@/utils/date' import { toTimeText } from '@/utils/date'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { deleteSmarttalk, updateSmarttalk, getPrepareById } from '@/api/file' import { deleteSmarttalk, updateSmarttalk, getPrepareById, addFileToKj } from '@/api/file'
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import outLink from '@/utils/linkConfig' import outLink from '@/utils/linkConfig'
@ -183,6 +189,14 @@ export default {
} }
}, },
methods: { methods: {
adToKj(item) {
addFileToKj(item.id).then(res=>{
console.log(res)
item.fileFlag = "课件"
},error=>{
console.log(error)
})
},
editTalk(item) { editTalk(item) {
console.log(item,this.userInfo,'this.userInfo') console.log(item,this.userInfo,'this.userInfo')
ElMessageBox.prompt('请输入新的名称', '重命名', { ElMessageBox.prompt('请输入新的名称', '重命名', {
@ -266,14 +280,14 @@ export default {
cookie, cookie,
fileType: item.fileType fileType: item.fileType
}) })
ipcRenderer.on('listen-file-change-on' + item.fileNewName, () => { /*ipcRenderer.on('listen-file-change-on' + item.fileNewName, () => {
items.async = 'on' items.async = 'on'
}) })
ipcRenderer.on('listen-file-change-success' + item.fileNewName, (e, { data, md5 }) => { ipcRenderer.on('listen-file-change-success' + item.fileNewName, (e, { data, md5 }) => {
items.fileSize = data.fileSize items.fileSize = data.fileSize
items.md5 = md5 items.md5 = md5
items.async = true items.async = true
}) })*/
} }
}) })
}) })

View File

@ -357,14 +357,14 @@ export default {
cookie, cookie,
fileType: item.fileType fileType: item.fileType
}) })
ipcRenderer.on('listen-file-change-on' + item.fileNewName, () => { /*ipcRenderer.on('listen-file-change-on' + item.fileNewName, () => {
items.async = 'on' items.async = 'on'
}) })
ipcRenderer.on('listen-file-change-success' + item.fileNewName, (e, { data, md5 }) => { ipcRenderer.on('listen-file-change-success' + item.fileNewName, (e, { data, md5 }) => {
items.fileSize = data.fileSize items.fileSize = data.fileSize
items.md5 = md5 items.md5 = md5
items.async = true items.async = true
}) })*/
} }
}) })
}) })

View File

@ -178,6 +178,7 @@ import ClassReserv from '@/views/classManage/classReserv.vue'
import classStart from './container/class-start.vue' // import classStart from './container/class-start.vue' //
import MsgEnum from '@/plugins/imChat/msgEnum' // im import MsgEnum from '@/plugins/imChat/msgEnum' // im
import Chat from '@/utils/chat' // im import Chat from '@/utils/chat' // im
import TreeLog from './components/treeLog.vue'
if (!Chat.imChat) Chat.init() if (!Chat.imChat) Chat.init()
const toolStore = useToolState() const toolStore = useToolState()
@ -235,7 +236,9 @@ export default {
isOpenHomework: false, isOpenHomework: false,
// //
activeClass: null, activeClass: null,
pptDialog: false pptDialog: false,
//
treelogRef:null
} }
}, },
computed: { computed: {
@ -534,7 +537,7 @@ export default {
} }
}, },
clickChoose(value) { clickChoose(value) {
this.checkFileList = value ? this.currentFileList : [] this.checkFileList = value ? this.currentSCFileList : []
}, },
deleteTalk(item) { deleteTalk(item) {
let index = this.currentFileList.indexOf(item) let index = this.currentFileList.indexOf(item)
@ -630,12 +633,18 @@ export default {
}) })
}, },
async nodeClick(data) { async nodeClick(data) {
console.log(data,'data');
if (this.currentNode.id === data.node.id) return if (this.currentNode.id === data.node.id) return
this.curBookImg = data.textBook.curBookImg this.curBookImg = data.textBook.curBookImg
this.curBookPath = data.textBook.curBookPath this.curBookPath = data.textBook.curBookPath
const path = await this.getBookPathFromServer(data.textBook.curBookPath) const path = await this.getBookPathFromServer(data.textBook.curBookPath)
const localpath = getAppInstallUrl('pdfjs-dist/web/viewer.html', 'user', '\\out\\renderer', true) + "?file=" const localpath = getAppInstallUrl('pdfjs-dist/web/viewer.html', 'user', '\\out\\renderer', true) + "?file="
localStorage.setItem('PDF-LOCAL-PATH',localpath + encodeURIComponent(import.meta.env.VITE_APP_RES_FILE_PATH + path)) localStorage.setItem('PDF-LOCAL-PATH',localpath + encodeURIComponent(import.meta.env.VITE_APP_RES_FILE_PATH + path))
// session
sessionStore.set('curr.textBook', data.textBook)
sessionStore.set('env.pdfBasePath', localpath)
sessionStore.set('env.fileBasePath', import.meta.env.VITE_APP_RES_FILE_PATH)
this.checkFileList = [] this.checkFileList = []
this.currentWorkList = [] this.currentWorkList = []
let cata = parseCataByNode(data.node) let cata = parseCataByNode(data.node)

View File

@ -161,7 +161,11 @@ const delRow = (item) => {
} }
// //
const addLesson = ({ id }) => { const addLesson = ({ id, fileSize }) => {
if (fileSize>1024*1024*150) {
ElMessage.warning('文件超过150M暂停超过150M资源的下载请重新选择')
return
}
let data = { let data = {
id, id,
fileRoot: '备课', fileRoot: '备课',
@ -190,6 +194,8 @@ const handleRow = (item) => {
}) })
curRow.value = item curRow.value = item
isShow.value = true isShow.value = true
console.log(item,'item');
} }
</script> </script>

View File

@ -18,21 +18,17 @@
</el-col> </el-col>
</el-row> </el-row>
<!-- 第三方资源筛选-->
<!-- <el-row class="resoure-btns" v-if="isThird">
<el-col :span="24" class="query-row flex">
<div class="flex row-left">
<el-button v-for="item in coursewareTypeList" :key="item.id"
:type="sourceStore.thirdQuery.type == item.value ? 'primary' : ''" round
@click="sourceStore.thirdChangeType(item.value)">
{{item.label }}
</el-button>
</div>
</el-col>
</el-row> -->
<el-row class="resoure-btns"> <el-row class="resoure-btns">
<el-col :span="24" class="query-row flex"> <el-col :span="24" class="query-row flex">
<div class="flex row-left"> <el-select v-model="sourceStore.query.fileSuffix" @change="sourceStore.changeSuffix" <div class="flex row-left">
<!-- 第三方资源筛选-->
<el-select v-if="isThird" v-model="sourceStore.thirdQuery.type" @change="sourceStore.thirdChangeType"
style="width: 110px">
<el-option v-for="item in coursewareTypeList" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
<el-select v-else v-model="sourceStore.query.fileSuffix" @change="sourceStore.changeSuffix"
style="width: 110px"> style="width: 110px">
<el-option v-for="item in sourceStore.resourceFormatList" :key="item.value" :label="item.label" <el-option v-for="item in sourceStore.resourceFormatList" :key="item.value" :label="item.label"
:value="item.value" /> :value="item.value" />

View File

@ -7,12 +7,11 @@
v-for="item in sourceStore.thirdResult.list" v-for="item in sourceStore.thirdResult.list"
:key="item.itemId" :key="item.itemId"
class="list-item" class="list-item"
@click="handleRow" @click="handleRow(item)"
> >
<div class="item-left flex"> <div class="item-left flex">
<div style="position: relative" @click="clickImg(item.itemId)"> <div style="position: relative">
<FileImage :file-name="item.fileType" :size="50" /> <FileImage :file-name="item.fileType" :size="50" />
<el-icon style="position: absolute;top: 1px;right: 5px"><Search /></el-icon>
</div> </div>
<div class="flex item-left-content"> <div class="flex item-left-content">
<div class="name flex" :title="item.title"> <div class="name flex" :title="item.title">
@ -51,6 +50,10 @@
</div> </div>
</div> </div>
</div> </div>
<el-button v-loading="item.loading" size="small" plain round type="primary" @click.stop="openChapter(item)">
<i class="iconfont icon-jiahao"></i>
备课</el-button
>
</li> </li>
</ul> </ul>
</el-scrollbar> </el-scrollbar>
@ -66,35 +69,28 @@
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
/> />
</div> </div>
<el-dialog v-model="isViewImg" width="80%" :modal-append-to-body="false" :append-to-body="false"> <FilePreview ref="thirdPreview" v-model="isViewImg"></FilePreview>
<div class="demo-image__lazy"> <TreeLog ref="treelogRef" @onsuccess="addToPrepare"/>
<el-image v-for="(url,index) in srcList" :key="index" :src="url" lazy />
</div>
</el-dialog>
<!-- <video width="550" controls autoplay loop>-->
<!-- <source src="https://sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-360p.mp4" type="video/mp4" />-->
<!-- </video>-->
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref } from 'vue' import { ref } from 'vue'
// import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage } from 'element-plus'
import { Clock,View,Folder,Search } from '@element-plus/icons-vue' import { Clock,View,Folder } from '@element-plus/icons-vue'
import FileImage from '@/components/file-image/index.vue' import FileImage from '@/components/file-image/index.vue'
import { getFileSuffix } from '@/utils/ruoyi' import FilePreview from '@/components/thirdFile-preview/index.vue'
import useUserStore from '@/store/modules/user'
import useResoureStore from '../store' import useResoureStore from '../store'
import {getImgPath} from "@/api/file/third"; import { addFileToPrepareThird } from '@/api/file'
import TreeLog from '@/views/prepare/components/treeLog.vue'
const userstore = useUserStore()
const sourceStore = useResoureStore() const sourceStore = useResoureStore()
// const userInfo = userstore.user
// //
const isViewImg = ref(false) const isViewImg = ref(false)
// const thirdPreview = ref()
const srcList = ref([]) const treelogRef = ref()
const currentItem = ref()
// change // change
const handleSizeChange = (limit) => { const handleSizeChange = (limit) => {
@ -120,18 +116,36 @@ function timestampToDate(timestamp) {
return `${year}-${month}-${day}`; return `${year}-${month}-${day}`;
} }
// //
const clickImg = (id) => { const handleRow = (item) => {
srcList.value = []
getImgPath({itemId:id}).then(res => {
if(res.code === 200){
isViewImg.value = true isViewImg.value = true
res.data.forEach(item => { thirdPreview.value.init(item.itemId)
item.subsets.forEach(previewItem => { }
previewItem.previewFiles.forEach(fileItem => { const openChapter = (item)=>{
srcList.value.push(fileItem.fileUrl) if (item.size>1024*1024*150) {
}) ElMessage.warning('文件超过150M暂停超过150M资源的下载请重新选择')
}) return
}) }
currentItem.value = item
//
treelogRef.value.openDialog()
}
const addToPrepare = (data) => {
console.log(data)
let chapterArr = []
for (let i = 0; i < data.cataList.length; i++) {
chapterArr.push({id: data.cataList[i].id,name: data.cataList[i].title})
}
let postData = {
itemId: currentItem.value.itemId,
textBookId: data.textbookId,
chapter: JSON.stringify(chapterArr)
}
currentItem.value.loading = true
addFileToPrepareThird(postData).then((res) => {
currentItem.value.loading = false
console.log(res)
if (res.code === 200) {
ElMessage.success("加入备课成功")
} }
}) })
} }

View File

@ -85,10 +85,11 @@ import FileImage from '@/components/file-image/index.vue'
import { useGetHomework } from '@/hooks/useGetHomework' import { useGetHomework } from '@/hooks/useGetHomework'
import { ipcMsgSend, ipcMsgInvoke } from '@/utils/tool' import { ipcMsgSend, ipcMsgInvoke } from '@/utils/tool'
import { useToolState } from '@/store/modules/tool' import { useToolState } from '@/store/modules/tool'
import {createWindow} from '@/utils/tool'
import Lesson from './lesson.vue'; import Lesson from './lesson.vue';
import { parseCataByNode } from '@/utils/talkFile' import { parseCataByNode } from '@/utils/talkFile'
import MsgEnum from '@/plugins/imChat/msgEnum' // import MsgEnum from '@/plugins/imChat/msgEnum' //
import { createHomework } from '../createHomework' import { createHomework, getClassWorkList, getStudentClassWorkData } from '../createHomework'
import { sessionStore } from '@/utils/store' import { sessionStore } from '@/utils/store'
const route = useRoute(); const route = useRoute();
@ -110,7 +111,13 @@ const curNode = reactive({
data: {} data: {}
}) })
const props = defineProps(['curNode'])
const emit = defineEmits(['closeActive'])
const sendHomework = (row,type) => { const sendHomework = (row,type) => {
if(type == 'item'){ if(type == 'item'){
// //
curRow.value = [row] curRow.value = [row]
@ -122,13 +129,30 @@ const sendHomework = (row,type) => {
} }
// //
const closeHomework = async() => { const closeHomework = async() => {
ipcMsgSend('tool-sphere:set:ignore', true) // ipcMsgSend('tool-sphere:set:ignore', true)
} }
// //
const successHomework = (data)=>{ const successHomework = async (data)=>{
// data.msg
// console.log('', data) // console.log('', data)
// im-(app|) // im-(app|)
// ipcMsgInvoke('im-chat:msg', data, MsgEnum.HEADS.MSG_0016) // ipcMsgInvoke('im-chat:msg', data, MsgEnum.HEADS.MSG_0016)
/**
* 推送成功 打开作业批改
* 2024/10/23
* 以下两个函数 代码 参照"作业批改" 页面
*/
let result = await getClassWorkList(data.msg)
result = await getStudentClassWorkData()
localStorage.setItem('teachClassWorkItem', JSON.stringify(result[0]));
toolStore.isTaskWin=true; //
emit('closeActive')
createWindow('open-taskwin',{url:'/teachClassTask'}); //
} }
// change // change
const changeChapter = async (data)=>{ const changeChapter = async (data)=>{
@ -222,7 +246,8 @@ onMounted(async () => {
entpcourseid.value = route.query.entpcourseid entpcourseid.value = route.query.entpcourseid
lesson.value = route.query.label lesson.value = route.query.label
// mounted // mounted
curNode.data = sessionStore.get('subject.curNode') // curNode.data = sessionStore.get('subject.curNode')
curNode.data = props.curNode
getHomework() getHomework()
getResource() getResource()
}) })

View File

@ -25,7 +25,24 @@
<div class="c-popover" :style="`--top: ${topPos}px;--height:${hPost}px;`" v-show="isVisible"> <div class="c-popover" :style="`--top: ${topPos}px;--height:${hPost}px;`" v-show="isVisible">
<div class="content" v-if="isVisible"> <div class="content" v-if="isVisible">
<slot name="content"> <slot name="content">
<homework v-if="activeObj?.prop === 'resource'" /> <homework v-if="activeObj?.prop === 'resource'" :curNode="curNode" @closeActive="closeActive" />
<template v-else-if="activeObj?.prop === 'book'">
<div class="item">
<div class="item-title">
<h3>{{ curNode.label }}</h3>
<span class="sub">{{ curNode.edusubject }}</span>
</div>
<div class="item-content" v-loading="loading">
<el-scrollbar height="300px">
<div class="item-content-item" v-for="item in cData">
<el-tag effect="dark">{{ item.tag }}</el-tag>
<span>{{ item.name }}</span>
<el-button :color="item.color||'#349d44'" size="small" @click="openFile(item)">打开</el-button>
</div>
</el-scrollbar>
</div>
</div>
</template>
<span v-else style="color:red;">{{activeObj}}</span> <span v-else style="color:red;">{{activeObj}}</span>
</slot> </slot>
</div> </div>
@ -34,8 +51,11 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import { computed, defineProps, ref, reactive, watchEffect, onMounted } from 'vue' import { computed, defineProps, ref, reactive, watchEffect, onMounted} from 'vue'
import { sessionStore } from '@/utils/store'
import homework from './homework.vue'; import homework from './homework.vue';
import { ElMessage } from 'element-plus';
import { toRoter, createWindow } from '@/utils/tool' //
// - // -
const colors = ['#409EFF','#00f389', '#ff7f00', '#ffff00', '#00baff', '#13b189', '#F56C6C'] const colors = ['#409EFF','#00f389', '#ff7f00', '#ffff00', '#00baff', '#13b189', '#F56C6C']
@ -48,7 +68,7 @@ const props = defineProps({
data: { // data: { //
type: Array, type: Array,
default: () => [ default: () => [
{ label: '课件', prop: 'book', isExtra: true, icon: 'icon--kejian' }, { label: '资源', prop: 'book', icon: 'icon--kejian' },
{ label: '活动', prop: 'resource', icon: 'icon-kechengziyuan1' }, { label: '活动', prop: 'resource', icon: 'icon-kechengziyuan1' },
// { label: '', prop: 'interact', icon: 'icon-hudong' }, // { label: '', prop: 'interact', icon: 'icon-hudong' },
// { label: '', prop: 'win', icon: 'icon-tubiaozhizuomobanyihuifu-' }, // { label: '', prop: 'win', icon: 'icon-tubiaozhizuomobanyihuifu-' },
@ -62,7 +82,10 @@ const btnRef = ref(null) // 按钮元素-ref
const topPos = ref(30) // - const topPos = ref(30) // -
const hPost = ref(0) // - const hPost = ref(0) // -
const isFold = ref(false) // const isFold = ref(false) //
const cData = ref(null) //
const loading = ref(false) //
let posBtnAll = {} // let posBtnAll = {} //
let curNode = null //
// === === // === ===
const list = computed(() => props.data.map((o,i) => { const list = computed(() => props.data.map((o,i) => {
o.style = getStyle(o.style, i) o.style = getStyle(o.style, i)
@ -70,7 +93,9 @@ const list = computed(() => props.data.map((o,i) => {
})) }))
onMounted(() => { onMounted(() => {
posBtnAll = btnRef.value.getBoundingClientRect() posBtnAll = btnRef.value.getBoundingClientRect()
hPost.value = posBtnAll.height hPost.value = Math.round(posBtnAll.height)
// btnRef.value.style.marginTop = -hPost.value / 2 + 'px'
curNode = sessionStore?.get?.('subject.curNode')
}) })
// === === // === ===
// //
@ -84,7 +109,16 @@ const getStyle = (style,index) => {
return `${style}${style.endsWith(';')?'':';'}color:${color};` return `${style}${style.endsWith(';')?'':';'}color:${color};`
} }
} }
// //
const getContent = o => {
loading.value = true
const { roottitle, edustage, edusubject } = curNode
const textbook = { type: 'book', tag: '教材', name: roottitle }
const course = { type: 'course', tag: '课标', name: `${edustage}-${edusubject}-课标` }
cData.value = [textbook, course]
loading.value = false
}
// :
const clickHandel = (o, e) => { const clickHandel = (o, e) => {
if (!o.isExtra) { // : if (!o.isExtra) { // :
const node = e.target.parentNode.getBoundingClientRect() const node = e.target.parentNode.getBoundingClientRect()
@ -92,10 +126,44 @@ const clickHandel = (o, e) => {
isVisible.value = !isColse // isVisible.value = !isColse //
activeObj.value = o activeObj.value = o
const nodeH = parseInt(node.height / 2) // const nodeH = parseInt(node.height / 2) //
topPos.value = parseInt(node.top) - posBtnAll.top + nodeH topPos.value = Math.round(parseInt(node.top) - posBtnAll.top + nodeH)
//
if (['book'].includes(o.prop)) getContent(o)
} }
emit('change', o) emit('change', o)
} }
// :
const openFile = item => {
if (['book', 'course'].includes(item.type)) {
const isBool = sessionStore.has('curr.textBook')
if (!isBool) return ElMessage.error('打开失败,请重试!')
const textBook = sessionStore.get('curr.textBook')
const pdfBasePath = sessionStore.get('env.pdfBasePath')
const fileBasePath = sessionStore.get('env.fileBasePath')
let path = ''
if (item.type == 'book') path = (textBook.curBookPath||'').replace('.txt', '.pdf')
else path = textBook.curBookPath.replace(/([^-]*)$/, '课标.pdf')
const url = pdfBasePath + encodeURIComponent(`${fileBasePath}${path}`)
console.log(url)
localStorage.setItem('PDF-TOOL-PATH', url)
//
// toRoter(`${process.env['ELECTRON_RENDERER_URL']}/#/fullscreenpdf`)
console.log('pdf_old:' + localStorage.getItem('PDF-LOCAL-PATH'))
console.log('pdf_new:' + localStorage.getItem('PDF-TOOL-PATH'))
// emit('change', { prop: 'bookOpen' })
createWindow('open-win', {url: '/fullscreenpdf'})
}
}
// :
const closeActive = () =>{
if (isVisible.value) { //
isVisible.value = false
emit('change', { prop: 'close' })
}
}
defineExpose({closeActive})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.warp{ .warp{
@ -103,11 +171,17 @@ const clickHandel = (o, e) => {
position: fixed; position: fixed;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
z-index: 1;
right: 10px; right: 10px;
//min-height: 40vh;
min-width: 4em; min-width: 4em;
border-radius: 4em; border-radius: 4em;
background-color: #121212; background-color: rgba(18,18,18,0.3);
border-color: rgba(1, 1, 1, 0.1);
&:hover{
opacity: 1;
background-color: rgba(18,18,18,1);
border-color: rgba(1, 1, 1, 1);
}
.el-space{margin: 20px 0 0;} .el-space{margin: 20px 0 0;}
.c-btn{ .c-btn{
color: #d9dce3; color: #d9dce3;
@ -140,8 +214,8 @@ const clickHandel = (o, e) => {
--top: 30px; --top: 30px;
--height: 40vh; --height: 40vh;
position: fixed; position: fixed;
inset: 50% 75px auto auto; inset: 0 75px auto auto;
transform: translateY(-50%); // transform: translateY(-50%);
background-color: #121212; background-color: #121212;
padding: 10px; padding: 10px;
border-radius: 4px; border-radius: 4px;
@ -159,5 +233,33 @@ const clickHandel = (o, e) => {
top: var(--top); top: var(--top);
transform: rotate(45deg); transform: rotate(45deg);
} }
.content{
//
.item{
margin: 0 15px;
&-title{
margin-bottom: 20px;
h3{
font-size: 20px;
font-weight: bold;
color: #fff;
}
.sub{
color: #cecece;
font-size: 15px;
}
}
&-content-item{
display: flex;
background-color: #384342;
padding: 15px 10px;
margin-bottom: 10px;
justify-content: space-between;
align-items: center;
color: #fff;
border-radius: 5px;
}
}
}
} }
</style> </style>

View File

@ -1,6 +1,6 @@
<template> <template>
<div v-if="props.test"> <div v-if="props.test">
<el-button type="primary" @click="trigger" v-tap:trigger="">点赞</el-button> <el-button type="primary" @click="trigger(1)" v-tap:trigger="[1,'']">点赞</el-button>
<el-button type="primary" @click="trigger(2, '学生A')" v-tap:trigger="[2,'学生A']">疑惑</el-button> <el-button type="primary" @click="trigger(2, '学生A')" v-tap:trigger="[2,'学生A']">疑惑</el-button>
</div> </div>
<!-- 温度计-模式 --> <!-- 温度计-模式 -->
@ -257,12 +257,12 @@ defineExpose({ trigger })
border-radius: 3px; border-radius: 3px;
min-width: 15px; min-width: 15px;
// height: 500px; // height: 500px;
&.like{ &.like{ //
background-image: linear-gradient(to top, #fef0f0, #f56c6c); background-image: linear-gradient(to top, #d2f0cb, #2f9e44);
// animation: striped-flow 5s linear infinite; // animation: striped-flow 5s linear infinite;
} }
&.doubt{ &.doubt{ //
background-image: linear-gradient(to top, #fdf6ec, #e6a23c); background-image: linear-gradient(to top, #ebc6c6, #ff0000);
} }
} }
} }

View File

@ -1,37 +1,225 @@
import useUserStore from '@/store/modules/user' import useUserStore from '@/store/modules/user'
import { addClassworkReturnId } from '@/api/teaching/classwork' import { addClassworkReturnId } from '@/api/teaching/classwork'
import { listClassworkdata } from '@/api/classTask'
import { homeworklist } from '@/api/teaching/classwork'
export const createHomework = ({ uniquekey, evalid, data, entpcourseid }) =>{
export const createHomework = ({ uniquekey, evalid, data, entpcourseid }) => {
const usertore = useUserStore().user const usertore = useUserStore().user
var formObj = {}; var formObj = {}
formObj.id = 0; formObj.id = 0
formObj.deaddate = ''; formObj.deaddate = ''
formObj.entpid = usertore.deptId; formObj.entpid = usertore.deptId
formObj.level = 1; formObj.level = 1
formObj.parentid = 0; formObj.parentid = 0
formObj.worktype = '常规作业'; formObj.worktype = '常规作业'
formObj.workkey = ''; formObj.workkey = ''
formObj.worktag = ''; formObj.worktag = ''
formObj.uniquekey = uniquekey; formObj.uniquekey = uniquekey
formObj.classid = 0; formObj.classid = 0
formObj.classcourseid = 0; formObj.classcourseid = 0
formObj.entpcourseid = entpcourseid; formObj.entpcourseid = entpcourseid
formObj.slideid = 0; formObj.slideid = 0
formObj.workcodes = JSON.stringify(data); formObj.workcodes = JSON.stringify(data)
formObj.edusubject = usertore.edusubject; formObj.edusubject = usertore.edusubject
formObj.evalid = evalid formObj.evalid = evalid
formObj.edustage = usertore.edustage; formObj.edustage = usertore.edustage
formObj.status = ''; formObj.status = ''
formObj.edituserid = usertore.userId; formObj.edituserid = usertore.userId
formObj.entpcourseworklist = JSON.stringify([{'id':-2, 'score': '10'}]); formObj.entpcourseworklist = JSON.stringify([{ id: -2, score: '10' }])
return addClassworkReturnId(formObj) return addClassworkReturnId(formObj)
} }
let classWorkList = []
// 将标签中的双引号增加转义
let escapeHtmlQuotes = (str) => {
// 后端已replace双引号, 故前端不用在处理
const regex1 = /\\+/g; // 匹配多个反斜杠
let result = str.replace(regex1, '\\');
result = str.replace(/(?<!\\)\n/g, '<br />'); //替换\n而不替换\\n 为 \\n
return result;
}
/**
* 获取班级作业
*/
export const getClassWorkList = async (id) => {
// 班级作业数据,包含多个班级 homeworklist
const response = await homeworklist({ id })
/**
* 2024-10-17 由于 后面截止时间加了 时分特加判断
* 1待批改以前是以明天判断现改为传当天的日期并根据当前日期的时分与截止日期进行判断
* 2已批改以前默认是以明天判断现依然以明天为判断并根据当前日期时分大于截止日期时分判断
*/
let list = response.rows
for (var i = 0; i < list.length; i++) {
// 初始化部分新增字段值
list[i].workdatalist = []
list[i].workdatalistVisible = false
list[i].workdatafeedbackcount = 0 // 已交人数
list[i].feedtimelength = 0
list[i].rightAnswerCount = 0
list[i].scoingRate = 0 + '%' // 得分率
list[i].averagetime = 0 // 平均用时
// ----------------------------------------------
// 处理任务类型的UI
if (list[i].worktype == '学习目标定位') {
list[i].workclass = 'success'
list[i].workcodesList = JSON.parse(list[i].workcodes)
} else if (list[i].worktype == '教材研读') {
list[i].workclass = 'primary'
} else if (list[i].worktype == '框架梳理') {
list[i].workclass = 'warning'
} else if (list[i].worktype == '学科定位') {
list[i].workclass = 'info'
} else if (list[i].worktype == '习题训练') {
list[i].workclass = 'danger'
} else {
list[i].workclass = ''
}
// 如果是习题训练任务,则检查一共有多少道
if (list[i].entpcourseworklist != '') {
list[i].entpcourseworklistarray = JSON.parse('[' + list[i].entpcourseworklist + ']')
} else {
list[i].entpcourseworklistarray = []
}
}
// 显示分配人数(workdataresultsum)>0 的
if (list && list.length > 0) {
classWorkList = list && list.filter((item) => item.workdataresultsum > 0)
//TODO 这里没分页,貌似这个 total 不重要,后续看
} else {
classWorkList = []
}
return classWorkList
}
export const getStudentClassWorkData = async () => {
const ids = classWorkList.map((item) => item.id).join(',')
if (ids == '') {
return
}
const res = await listClassworkdata({
classworkids: ids,
pageSize: 1000
})
for (var t = 0; t < classWorkList.length; t++) {
for (var i = 0; i < res.rows.length; i++) {
if (
res.rows[i].classworkid == classWorkList[t].id &&
res.rows[i].finishtimelength != '0'
) {
console.log('==================')
// 有几个学生完成/正在完成学习任务
// 至少resultcount不是0
classWorkList[t].workdatafeedbackcount++
// 在参与学习任务的人中,汇总计算用时
classWorkList[t].feedtimelength += parseInt(res.rows[i].finishtimelength)
// 计算得分率
if (
res.rows[i].classworkevallist != '' &&
res.rows[i].classworkevallist != null &&
res.rows[i].classworkevallist != 'null'
) {
let replacedString = res.rows[i].classworkevallist.replace(/""/g, '"')
// 将标签中双引号改为转义, 测试数据: "{\"id\":172907, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":358520, \"feedcontent\":\"④①⑤③②\", \"score\":4, \"rightanswer\":\"④①⑤③②\"},{\"id\":172908, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":358521, \"feedcontent\":\"气壮山威,鲲鹏展翅楚云飞\", \"score\":4, \"rightanswer\":\"志远天高,春风杨柳麓山青\"},{\"id\":172909, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363096, \"feedcontent\":\"《红烛》化用“蜡矩”这一古典意象,赋予它新的含义,赞美了红烛以“蜡炬成灰”来点亮世界的奉献精神。\", \"score\":4, \"rightanswer\":\"《立在地球边上放号》中,全诗采用间接抒情的方式,描绘了太平洋的浪潮,吟唱了一曲惊心动魄的力的颂歌,意在赞美摧毁旧世界、创造新生活的“五四”精神。\"},{\"id\":172910, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363098, \"feedcontent\":\"毛泽东重游橘子洲,面对如画的秋色和大好的革命形势,回忆过去战斗的岁月,不禁心潮起伏,<bdo class=\"mathjye-underpoint2\">浮想联翩</bdo>。\", \"score\":4, \"rightanswer\":\"毛泽东重游橘子洲,面对如画的秋色和大好的革命形势,回忆过去战斗的岁月,不禁心潮起伏,<bdo class=\"mathjye-underpoint2\">浮想联翩</bdo>。\"},{\"id\":172911, \"rating\":0, \"teacherRating\":0, \"entpcourseworkid\":363100, \"feedcontent\":\"毛泽东重游橘子洲,面对如画的秋色和大好的革命形势,回忆过去战斗的岁月,不禁心潮起伏,<bdo class=\"mathjye-underpoint2\">浮想联翩</bdo>。\", \"score\":4, \"rightanswer\":\"毛泽东重游橘子洲,面对如画的秋色和大好的革命形势,回忆过去战斗的岁月,不禁心潮起伏,<bdo class=\"mathjye-underpoint2\">浮想联翩</bdo>。\"}"
replacedString = escapeHtmlQuotes(res.rows[i].classworkevallist).replace(
/"(\[.*\])"/g,
'$1'
)
replacedString = escapeHtmlQuotes(res.rows[i].classworkevallist)
var evalarray
try {
evalarray = JSON.parse('[' + res.rows[i].classworkevallist + ']')
} catch {
evalarray = JSON.parse('[' + replacedString + ']')
}
for (var e = 0; e < evalarray.length; e++) {
if (res.rows[i].worktype == '常规作业') {
evalarray[e].feedcontent = escapeHtmlQuotes(evalarray[e].feedcontent).replace(
/"(\[.*\])"/g,
'$1'
)
evalarray[e].feedcontent = escapeHtmlQuotes(evalarray[e].feedcontent)
}
if (evalarray[e].feedcontent == evalarray[e].rightanswer) {
// 正确,得分
classWorkList[t].rightAnswerCount++
}
}
}
}
// 当前这个学习任务共推送给了几个学生workdataresultsum
if (res.rows[i].classworkid == classWorkList[t].id) {
classWorkList[t].workdatalist.push(res.rows[i])
}
}
// 计算完成进度 workdataresultsum 人数要大于0
if (
classWorkList[t].workdataresultcount > 0 &&
classWorkList[t].workdataresultsum > 0
) {
classWorkList[t].finishpercent = parseInt(
(classWorkList[t].workdataresultcount / classWorkList[t].workdataresultsum) * 100
)
} else {
classWorkList[t].finishpercent = 0
}
// 以下四个参数,都要计算
// 2024-04-12酉阳by jackyshen
// 计算参与学习任务的平均用时
if (classWorkList[t].workdatafeedbackcount > 0) {
classWorkList[t].averagetime = Math.ceil(
classWorkList[t].feedtimelength / classWorkList[t].workdatafeedbackcount / 60
).toFixed(0)
} else {
classWorkList[t].averagetime = 0
}
// 计算批阅异常,需要获取每个题目的类型,找出主观题
// 暂缓
// 计算平均得分率: 正确题数/(题目总数*学生人数)*100
if (
classWorkList[t].entpcourseworklistarray &&
classWorkList[t].entpcourseworklistarray.length > 0
) {
var dd =
(classWorkList[t].rightAnswerCount /
(classWorkList[t].entpcourseworklistarray.length *
classWorkList[t].workdataresultsum)) *
100
classWorkList[t].scoingRate = dd.toFixed(0) + '%'
} else {
classWorkList[t].scoingRate = '0%'
}
// 设定典型作答,需要获取每个题目的类型,找出主观题
// 暂缓
}
return classWorkList
}

View File

@ -1,10 +1,12 @@
<template> <template>
<div class="warp-all"> <div class="warp-all">
<!-- 遮罩层 -->
<div class="mask" v-show="isMask" @click="maskChange(true)"></div>
<!-- 画板 --> <!-- 画板 -->
<board-vue v-model="tabActive" v-show="isShow" ref="boardVueRef"></board-vue> <board-vue v-model="tabActive" v-show="isShow" ref="boardVueRef"></board-vue>
<!-- 侧边工具栏 --> <!-- 侧边工具栏 -->
<side-vue v-ignore @ignore-mounted="sideMouse" @change="sideChange"></side-vue> <side-vue ref="sideVueRef" v-ignore @ignore-mounted="sideMouse" @change="sideChange"></side-vue>
<!-- 点赞组件 --> <!-- 点赞组件 -->
<upvote-vue ref="upvoteRef" type="2"></upvote-vue> <upvote-vue ref="upvoteRef" type="2"></upvote-vue>
@ -13,7 +15,7 @@
<im-chat ref="imChatRef" @change="chatChange" group /> <im-chat ref="imChatRef" @change="chatChange" group />
<!-- 底部工具栏 --> <!-- 底部工具栏 -->
<div class="tool-bottom-all" <!-- <div class="tool-bottom-all"
@mouseenter="mouseChange(0)" @mouseleave="mouseChange(1)"> @mouseenter="mouseChange(0)" @mouseleave="mouseChange(1)">
<div v-drag="{handle:'.tool-bottom-all', dragtime}" <div v-drag="{handle:'.tool-bottom-all', dragtime}"
@v-drag-start="dragtime = Date.now()"> @v-drag-start="dragtime = Date.now()">
@ -34,7 +36,7 @@
</el-segmented> </el-segmented>
</div> </div>
</transition> </transition>
</div> </div> -->
</div> </div>
</template> </template>
@ -62,11 +64,13 @@ const isDrag = ref(false) // 开始拖拽
const dragtime = ref(0) // - const dragtime = ref(0) // -
const isShow = ref(false) // - const isShow = ref(false) // -
const isOver = ref(false) // const isOver = ref(false) //
const isOpenBook = ref(false) const isOpenBook = ref(false) // pdf
const isMask = ref(false) //
const toolStore = useToolState() // const toolStore = useToolState() //
const boardVueRef=ref(null) // ref const boardVueRef=ref(null) // ref
const upvoteRef = ref(null) // ref const upvoteRef = ref(null) // ref
const imChatRef = ref(null) // im-chat ref const imChatRef = ref(null) // im-chat ref
const sideVueRef = ref(null) // ref
const classObj = reactive({ // const classObj = reactive({ //
id: route.query.reservId, // id id: route.query.reservId, // id
data: {} // data: {} //
@ -83,6 +87,8 @@ const btnList = [ // 工具栏按钮列表
// { label: '', value: 'focus', icon: 'icon-jujiao' }, // { label: '', value: 'focus', icon: 'icon-jujiao' },
// { label: '', value: 'more', icon: 'icon-xiazai9' }, // { label: '', value: 'more', icon: 'icon-xiazai9' },
] ]
let timingSide = null // -
// === === // === ===
onMounted(async() => { onMounted(async() => {
if (!electron) return // if (!electron) return //
@ -90,7 +96,6 @@ onMounted(async() => {
// window.test1 = toolStore // window.test1 = toolStore
getClassInfo() // ex3 getClassInfo() // ex3
resetStatus() // - resetStatus() // -
}) })
// ==== === // ==== ===
@ -98,7 +103,7 @@ onMounted(async() => {
const getClassInfo = async () => { const getClassInfo = async () => {
const { data } = await classManageApi.getClassInfo(classObj.id) const { data } = await classManageApi.getClassInfo(classObj.id)
classObj.data = data classObj.data = data
sessionStore.set('curClassRoom', classObj) // - sessionStore.set('curr.curClassRoom', classObj) // -
// id // id
let timGroupId = data?.ex3 || '' let timGroupId = data?.ex3 || ''
console.log('获取群ID:', timGroupId) console.log('获取群ID:', timGroupId)
@ -140,7 +145,8 @@ const logoHandle = (e,t) => {
// -穿 // -穿
const mouseChange = (bool) => { const mouseChange = (bool) => {
let resBool = false let resBool = false
if (bool == 0) return setIgnore(resBool) // 穿 // console.log('mouseChange:', bool, resBool)
if (!bool) return setIgnore(resBool) // 穿
if (tabActive.value == 'select') resBool = !!bool if (tabActive.value == 'select') resBool = !!bool
else { else {
if (!isShow.value) resBool = !!bool if (!isShow.value) resBool = !!bool
@ -148,12 +154,10 @@ const mouseChange = (bool) => {
const isPdf = !resBool && toolStore.isPdfWin const isPdf = !resBool && toolStore.isPdfWin
if (isPdf) resBool = true if (isPdf) resBool = true
} }
console.log('mouseChange:', bool, resBool) // console.log('mouseChange:', bool, resBool)
setIgnore(resBool) setIgnore(resBool)
} }
const touchChange = (e) => {
console.log(e)
}
// im-chat: {type, data} // im-chat: {type, data}
const chatChange = (type, data, ...args) => { const chatChange = (type, data, ...args) => {
if (type == 'createGroup') { // - if (type == 'createGroup') { // -
@ -185,14 +189,6 @@ const setIgnore = (bool) => {ipcMsgSend('tool-sphere:set:ignore', bool)}
// : | // : |
const resetStatus = () => { const resetStatus = () => {
if (toolStore.isToolWin) return // - if (toolStore.isToolWin) return // -
// -
// ipcMain?.removeHandler('tool-sphere:reset') //
// ipcMain?.handle?.('tool-sphere:reset', () => {
// setTimeout(() => {
// boardVueRef.value.handleMode(tabActive.value)
// mouseChange(1)
// }, 500)
// })
toolStore.isToolWin = true // toolStore.isToolWin = true //
} }
// : // :
@ -202,13 +198,16 @@ const sideMouse = e => {
setIgnore(false) // -穿 setIgnore(false) // -穿
return return
} }
mouseChange(type == 'mouseleave') //
const bool = isMask.value && type =='mouseleave'
if (bool) mouseChange(false) // 穿
else mouseChange(type == 'mouseleave')
} }
// : // :
const sideChange = async o => { const sideChange = async o => {
// console.log(o) // console.log(o)
switch(o.prop) { switch(o.prop) {
case 'book': case 'bookOpen':
if(isOpenBook.value) { if(isOpenBook.value) {
isOpenBook.value = false isOpenBook.value = false
ElMessage.info('已经打开课本了哦') ElMessage.info('已经打开课本了哦')
@ -218,7 +217,14 @@ const sideChange = async o => {
isOpenBook.value = true isOpenBook.value = true
} }
break break
case 'book':
isMask.value = !isMask.value
break
case 'close': //
maskChange(false)
break
case 'resource': // case 'resource': //
isMask.value = !isMask.value
break break
case 'interact': // case 'interact': //
break break
@ -255,6 +261,13 @@ const sideChange = async o => {
} }
} }
// -
const maskChange = (bool) => {
isMask.value = false
bool && sideVueRef.value.closeActive() //
mouseChange(true) // 穿
}
// === === // === ===
watchEffect(() => { watchEffect(() => {
if (isOver.value) return // , if (isOver.value) return // ,
@ -274,6 +287,12 @@ watchEffect(() => {
.warp-all{ .warp-all{
position: relative; position: relative;
} }
//
.mask{
position: fixed;
inset: 0;
background: rgba(1, 1, 1, 0.3);
}
// //
.tool-bottom-all{ .tool-bottom-all{
// width: 45vw; // width: 45vw;
@ -316,6 +335,7 @@ watchEffect(() => {
} }
} }
} }
//
.a-fade-leave-active,.a-fade-enter-active{ .a-fade-leave-active,.a-fade-enter-active{
transition: all .3s; transition: all .3s;
} }