瀏覽代碼

Merge branch 'develop5.0' into TPE/develop5.0

# Conflicts:
#	TEAMModelOS/ClientApp/src/locale/lang/zh-TW/schoolBaseInfo.js
#	TEAMModelOS/ClientApp/src/locale/lang/zh-TW/totalAnalysis.js
jeff 4 年之前
父節點
當前提交
2b4a173b94
共有 100 個文件被更改,包括 6121 次插入1945 次删除
  1. 76 8
      TEAMModelFunction/ActivityHttpTrigger.cs
  2. 9 5
      TEAMModelFunction/MQActivity.cs
  3. 1 1
      TEAMModelFunction/MonitorCosmosDB.cs
  4. 6 6
      TEAMModelFunction/MonitorServicesBus.cs
  5. 22 18
      TEAMModelFunction/NoticeServiceBus.cs
  6. 7 3
      TEAMModelFunction/StuListService.cs
  7. 430 380
      TEAMModelFunction/TriggerExam.cs
  8. 2 2
      TEAMModelFunction/TriggerStuActivity.cs
  9. 6 6
      TEAMModelFunction/TriggerSurvey.cs
  10. 3 5
      TEAMModelFunction/TriggerVote.cs
  11. 1 0
      TEAMModelFunction/local.settings.json
  12. 1 0
      TEAMModelOS.SDK/Context/Configuration/Option.cs
  13. 0 110
      TEAMModelOS.SDK/Models/Cosmos/Common/Inner/SyllabusNode.cs
  14. 11 1
      TEAMModelOS.SDK/Models/Cosmos/Common/Inner/SyllabusTree.cs
  15. 19 28
      TEAMModelOS.SDK/Models/Cosmos/Common/Notice.cs
  16. 53 0
      TEAMModelOS.SDK/Models/Cosmos/Common/Snode.cs
  17. 3 1
      TEAMModelOS.SDK/Models/Cosmos/Common/Syllabus.cs
  18. 90 0
      TEAMModelOS.SDK/Models/Cosmos/Common/Volume.cs
  19. 21 27
      TEAMModelOS.SDK/Models/Cosmos/School/Class.cs
  20. 1 1
      TEAMModelOS.SDK/Models/Cosmos/School/ExamInfo.cs
  21. 21 0
      TEAMModelOS.SDK/Models/Cosmos/School/Inner/Period.cs
  22. 1 1
      TEAMModelOS.SDK/Models/Cosmos/School/Inner/Semester.cs
  23. 4 2
      TEAMModelOS.SDK/Models/Cosmos/School/Inner/Subject.cs
  24. 37 0
      TEAMModelOS.SDK/Models/Cosmos/School/Room.cs
  25. 4 0
      TEAMModelOS.SDK/Models/Cosmos/School/School.cs
  26. 1 1
      TEAMModelOS.SDK/TEAMModelOS.SDK.csproj
  27. 1 1
      TEAMModelOS/ClientApp/package.json
  28. 2 0
      TEAMModelOS/ClientApp/src/api/index.js
  29. 19 0
      TEAMModelOS/ClientApp/src/api/room.js
  30. 99 13
      TEAMModelOS/ClientApp/src/assets/iconfont/demo_index.html
  31. 17 7
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.css
  32. 二進制
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.eot
  33. 1 1
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.js
  34. 21 0
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.json
  35. 0 344
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.svg
  36. 二進制
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.ttf
  37. 二進制
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff
  38. 二進制
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff2
  39. 16 6
      TEAMModelOS/ClientApp/src/common/BaseLayout.vue
  40. 2 2
      TEAMModelOS/ClientApp/src/common/BaseSelectSchool.vue
  41. 7 7
      TEAMModelOS/ClientApp/src/components/evaluation/AnalysisItemTable.vue
  42. 4 3
      TEAMModelOS/ClientApp/src/components/evaluation/ExerciseList.vue
  43. 1 1
      TEAMModelOS/ClientApp/src/components/evaluation/OptionsTable.vue
  44. 4 4
      TEAMModelOS/ClientApp/src/components/public/frontEndMain/Index.vue
  45. 9 0
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseJudge.vue
  46. 9 0
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseMultiple.vue
  47. 9 0
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseSingle.vue
  48. 2 14
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseKnowledgeDetail.vue
  49. 296 0
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseLevelDetail.vue
  50. 2 2
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseLevelPie.vue
  51. 16 7
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseMyTable.vue
  52. 1 1
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BasePie.vue
  53. 11 2
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseRadar.vue
  54. 1 1
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseScatter.vue
  55. 12 9
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseScoreRateBar.vue
  56. 5 2
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReportCharts/StudentScore.vue
  57. 6 3
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperView.vue
  58. 9 0
      TEAMModelOS/ClientApp/src/components/vote/BaseVoteForm.vue
  59. 1 1
      TEAMModelOS/ClientApp/src/css/disabled-iview-form.less
  60. 4 0
      TEAMModelOS/ClientApp/src/css/site.css
  61. 255 249
      TEAMModelOS/ClientApp/src/locale/lang/en-US/learnActivity.js
  62. 2 1
      TEAMModelOS/ClientApp/src/locale/lang/en-US/system.js
  63. 10 3
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/learnActivity.js
  64. 26 4
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/schoolBaseInfo.js
  65. 2 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/system.js
  66. 6 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/totalAnalysis.js
  67. 9 3
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/learnActivity.js
  68. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/schoolBaseInfo.js
  69. 2 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/system.js
  70. 8 2
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/totalAnalysis.js
  71. 13 2
      TEAMModelOS/ClientApp/src/router/routes.js
  72. 9 0
      TEAMModelOS/ClientApp/src/store/module/totalAnalysis.js
  73. 3 2
      TEAMModelOS/ClientApp/src/utils/blobTool.js
  74. 6 6
      TEAMModelOS/ClientApp/src/utils/public.js
  75. 1 1
      TEAMModelOS/ClientApp/src/view/Home.vue
  76. 1 0
      TEAMModelOS/ClientApp/src/view/evaluation/bank/index.vue
  77. 0 1
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseImport.vue
  78. 16 9
      TEAMModelOS/ClientApp/src/view/evaluation/index/CreatePaper.vue
  79. 3 0
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.less
  80. 88 19
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.vue
  81. 3 3
      TEAMModelOS/ClientApp/src/view/learnactivity/PaperScore.vue
  82. 18 15
      TEAMModelOS/ClientApp/src/view/learnactivity/markpaper/MarkData.vue
  83. 99 85
      TEAMModelOS/ClientApp/src/view/learnactivity/markpaper/MarkSetting.vue
  84. 17 98
      TEAMModelOS/ClientApp/src/view/learnactivity/markpaper/MarkView.vue
  85. 10 8
      TEAMModelOS/ClientApp/src/view/newcourse/TimeSetting.less
  86. 1 1
      TEAMModelOS/ClientApp/src/view/newcourse/TimeSetting.vue
  87. 312 187
      TEAMModelOS/ClientApp/src/view/schoolmgmt/ClassroomSetting/ClassroomSetting.vue
  88. 614 0
      TEAMModelOS/ClientApp/src/view/schoolmgmt/SystemSetting/NewSystemSetting.less
  89. 1505 0
      TEAMModelOS/ClientApp/src/view/schoolmgmt/SystemSetting/NewSystemSetting.vue
  90. 462 0
      TEAMModelOS/ClientApp/src/view/student-account/ClassMgt.less
  91. 876 0
      TEAMModelOS/ClientApp/src/view/student-account/ClassMgt.vue
  92. 1 1
      TEAMModelOS/ClientApp/src/view/student-account/ImportStudent.vue
  93. 26 0
      TEAMModelOS/ClientApp/src/view/student-account/Index.less
  94. 246 177
      TEAMModelOS/ClientApp/src/view/student-account/Index.vue
  95. 6 6
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/EvaluationList/TotalIndex.vue
  96. 9 6
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/KnowledgeAnalysis/KnowledgeAnalysis.vue
  97. 15 5
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/KnowledgeAnalysis/ScoreDetails.vue
  98. 5 2
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/LevelAnalysis/LevelAnalysis.vue
  99. 18 9
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/LevelAnalysis/ScoreDetails.vue
  100. 0 0
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/ScatterAnalysis/ScatterAnalysis.vue

+ 76 - 8
TEAMModelFunction/ActivityHttpTrigger.cs

@@ -353,7 +353,9 @@ namespace TEAMModelFunction
                                 school = info.school,
                                 creatorId = info.creatorId,
                                 subjects = new List<string>() { "" },
-                                blob = info.blob
+                                blob = info.blob,
+                                owner=info.owner
+                                
                             });
                         });
                     }
@@ -374,7 +376,8 @@ namespace TEAMModelFunction
                                 school = info.school,
                                 creatorId = info.creatorId,
                                 subjects = new List<string>() { "" },
-                                blob=info.blob
+                                blob=info.blob,
+                                owner = info.owner
                             });
                         });
                     }
@@ -394,13 +397,78 @@ namespace TEAMModelFunction
             [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
             ILogger log)
         {
-            string id = "";
-            string code = "";
+            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
+            dynamic json = JsonConvert.DeserializeObject<dynamic>(requestBody);
+            string id = json.id;
+            string code = json.code;
+            if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(code)) {
+                return new BadRequestResult();
+            }
             var client = _azureCosmos.GetCosmosClient();
-            List<MQActivity> datas = new List<MQActivity>();
-            var item = client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<MQActivity>(id , new Azure.Cosmos.PartitionKey($"{code}"));
-            return null;
+           // var query = $"SELECT distinct c.owner, c.id,c.code, c.classes,c.subjects,c.progress,c.scope,c.startTime,c.school,c.creatorId,c.name,c.pk ,c.endTime   FROM c  join A1 in c.classes  where  c.pk='{type}' and A1 in('{stuListChange.listid}') "; 
+            MQActivity activity = null;
+            try {
+                var aactivity= await client.GetContainer("TEAMModelOS", "Common").ReadItemStreamAsync(id, new Azure.Cosmos.PartitionKey(code));
+                using var da = await JsonDocument.ParseAsync(aactivity.ContentStream);
+                activity = da.ToObject<MQActivity>();
+            } catch (Exception ex) { 
+            }
+            if (activity != null)
+            {
+                (List<string> tmdids, List<Students> students) = await TriggerStuActivity.GetStuList(client, _dingDing, activity.classes, activity.school);
+                if (tmdids.IsNotEmpty())
+                {
+                    foreach (string tmdid in tmdids)
+                    {
+                        var stucourse = new StuActivity
+                        {
+                            id = activity.id,
+                            scode = activity.code,
+                            name = activity.name,
+                            code = $"Activity-{tmdid}",
+                            scope = activity.scope,
+                            school = activity.school,
+                            creatorId = activity.creatorId,
+                            pk = "Activity",
+                            type = activity.pk,
+                            subjects = activity.pk.ToLower().Equals("exam") && activity.subjects.IsNotEmpty() ? new List<string>() { activity.subjects[0].id } : new List<string>() { "" },
+                            startTime = activity.startTime,
+                            endTime = activity.endTime,
+                            blob = activity.blob,
+                            owner = activity.owner
+                        };
+                        await client.GetContainer("TEAMModelOS", "Teacher").UpsertItemAsync(stucourse, new PartitionKey(stucourse.code));
+                    }
+                }
+                if (students.IsNotEmpty())
+                {
+                    foreach (Students student in students)
+                    {
+                        var stucourse = new StuActivity
+                        {
+                            id = activity.id,
+                            scode = activity.code,
+                            name = activity.name,
+                            code = $"Activity-{activity.school}-{student.id}",
+                            scope = activity.scope,
+                            school = activity.school,
+                            creatorId = activity.creatorId,
+                            pk = "Activity",
+                            type = activity.pk,
+                            subjects = activity.pk.ToLower().Equals("exam") && activity.subjects.IsNotEmpty() ? new List<string>() { activity.subjects[0].id } : new List<string>() { "" },
+                            startTime = activity.startTime,
+                            endTime = activity.endTime,
+                            blob = activity.blob,
+                            owner = activity.owner
+                        };
+                        await client.GetContainer("TEAMModelOS", "Student").UpsertItemAsync(stucourse, new PartitionKey(stucourse.code));
+                    }
+                }
+                return new OkObjectResult(new { code = 200 });
+            }
+            else {
+                return new BadRequestResult();
+            }
         }
-
     }
 }

+ 9 - 5
TEAMModelFunction/MQActivity.cs

@@ -1,14 +1,15 @@
 using System;
 using System.Collections.Generic;
 using System.Text;
+using TEAMModelOS.SDK.Models;
 
 namespace TEAMModelFunction
 {
-    public class MQActivity
+    public class MQActivity :CosmosEntity
     {
 
-        public string id { get; set; }
-        public string code { get; set; }
+       // public string id { get; set; }
+       // public string code { get; set; }
         public string owner { get; set; }
         public List<string> classes { get; set; }
         //如果已经完成则不写入???
@@ -16,12 +17,15 @@ namespace TEAMModelFunction
         public string scope { get; set; }
         public string school { get; set; }
         public string creatorId { get; set; }
-        public string pk { get; set; }
+       // public string pk { get; set; }
         public string name { get; set; }
-        public List<string> subjects { get; set; }
+        public List<Sub> subjects { get; set; }
         public string blob { get; set; }
         public long startTime { get; set; }
         public long endTime { get; set; }
 
     }
+    public class Sub { 
+        public string id { get; set; }
+    }
 }

+ 1 - 1
TEAMModelFunction/MonitorCosmosDB.cs

@@ -85,7 +85,7 @@ namespace TEAMModelFunction
                                 status = status
                             };
 #if DEBUG
-                            await _dingDing.SendBotMsg($"CosmosDBTrigger,{pk}触发变更\n{data.ToJsonString()}",
+                            await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-CosmosDBTrigger,{pk}触发变更\n{data.ToJsonString()}",
                                             GroupNames.成都开发測試群組);
 #endif
                             switch (pk)

+ 6 - 6
TEAMModelFunction/MonitorServicesBus.cs

@@ -49,7 +49,7 @@ namespace TEAMModelFunction
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"ServiceBus,ExamBus()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,ExamBus()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
             }
 
         }
@@ -70,7 +70,7 @@ namespace TEAMModelFunction
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"ServiceBus,VoteBus()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,VoteBus()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
             }
 
         }
@@ -91,7 +91,7 @@ namespace TEAMModelFunction
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"ServiceBus,SurveyBus()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,SurveyBus()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
             }
         }
         [FunctionName("Blob")]
@@ -111,14 +111,14 @@ namespace TEAMModelFunction
                         await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Blob:Catalog:{name}", key, size.Item2[key].HasValue ? size.Item2[key].Value : 0);
                     }
 #if DEBUG
-                    await _dingDing.SendBotMsg($"ServiceBus,Blob() 容器:{name}使用:{size.Item1},文件分类:{size.Item2.ToJsonString()}",
+                    await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,Blob() 容器:{name}使用:{size.Item1},文件分类:{size.Item2.ToJsonString()}",
                             GroupNames.成都开发測試群組);
 #endif
                 }
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"ServiceBus,Blob()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,Blob()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
             }
         }
 
@@ -155,7 +155,7 @@ namespace TEAMModelFunction
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"StuListServiceBus-StuList\n{ex.Message}{ex.StackTrace}", GroupNames.成都开发測試群組);
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-StuListServiceBus-StuList\n{ex.Message}{ex.StackTrace}", GroupNames.成都开发測試群組);
             }
         }
 

+ 22 - 18
TEAMModelFunction/NoticeServiceBus.cs

@@ -37,13 +37,11 @@ namespace TEAMModelFunction
             try
             {
 
-                Console.WriteLine(msg);
                 // List<Task<string>> tasks = new List<Task<string>>();
                 //List<Task> sessionTasks = new List<Task>();
                 var jsonMsg = JsonDocument.Parse(msg);
                 Notice notice = msg.ToObject<Notice>();
                 var blobcntr = "";
-
                 if (notice.scope.Equals("school"))
                 {
                     blobcntr = notice.school;
@@ -57,19 +55,31 @@ namespace TEAMModelFunction
                     return;
                 }
 #if DEBUG
-                await _dingDing.SendBotMsg($"NoticeServiceBus-Notice:\n发起通知{msg}", GroupNames.成都开发測試群組);
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-NoticeServiceBus-Notice:\n发起通知{msg}", GroupNames.成都开发測試群組);
 #endif
+                long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+                long day30 = 2592000L;
+                //处理过期时间大于30天
+                if (notice.expire - now > day30) {
+                    notice.expire = now + day30;
+                }
+                //通知的主体内容
                 var urlNotice = $"{notice.msgId}.json";
                 var blobNotice = new
                 {
                     notice.type,
                     notice.priority,
+                    notice.from,
+                    notice.toservice,
+                    notice.creation,//创建时间
+                    notice.expire,//到期时间
+                    notice.msgId,
+                    notice.creatorId,
+                    notice.school,
+                    notice.scope,
                     notice.body,
-                    notice.creation,
-                    notice.expire,
-                    notice.creatorId
-
                 };
+                //通知的接收人的集合信息
                 await _azureStorage.UploadFileByContainer(blobcntr, blobNotice.ToJsonString(), "notice", urlNotice);
                 var urlReceiver = $"{notice.msgId}_receiver.json";
                 var blobReceiver = new
@@ -78,7 +88,9 @@ namespace TEAMModelFunction
                     notice.tmdids
                 };
                 await _azureStorage.UploadFileByContainer(blobcntr, blobReceiver.ToJsonString(), "notice", urlReceiver);
-                long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+
+                //发送通知给用户
+               
                 // "ttl":2592000,不能超过30天(2592000),一天(3600),一周(25200)
 
                 if (notice.stuids.IsNotEmpty())
@@ -89,12 +101,9 @@ namespace TEAMModelFunction
                         Receiver receiver = new Receiver
                         {
                             id = notice.msgId,
-                            pk = "Receiver",
                             status = 0,
-                            scope = "school",
                             ctime = now,
                             urlNotice = $"/notice/{urlNotice}",
-                            rid = $"{stu.schoolId}-{stu.id}"
                         };
                         var url = $"{stu.id}/receive/{notice.msgId}.json";
                         await client.HashSetAsync($"Notice:Receiver:{stu.schoolId}-{stu.id}", notice.msgId, $"/student/{url}");
@@ -115,17 +124,12 @@ namespace TEAMModelFunction
                         Receiver receiver = new Receiver
                         {
                             id = notice.msgId,
-                            pk = "Receiver",
                             status = 0,
-                            //school = stu.schoolId,
-                            scope = "school",
-
                             ctime = now,
                             urlNotice = $"/notice/{urlNotice}",
-                            rid = $"{tmdid}"
                         };
                         var url = $"{notice.msgId}.json";
-                        await client.HashSetAsync($"Notice:Receiver:{tmdid}", notice.msgId, new { now, url = $"/receive/{url}" }.ToJsonString());
+                        await client.HashSetAsync($"Notice:Receiver:{tmdid}", notice.msgId, receiver.ToJsonString());
                         //await client.GetContainer("TEAMModelOS", "Common").UpsertItemAsync<Receiver>(receiver, new Azure.Cosmos.PartitionKey(receiver.code));
                         //  /student/{stuid}/receive/{notice.sid}.json
                         //存放通知到学生容器空间
@@ -139,7 +143,7 @@ namespace TEAMModelFunction
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"NoticeServiceBus-Notice\n{ex.Message}{ex.StackTrace}", GroupNames.成都开发測試群組);
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-NoticeServiceBus-Notice\n{ex.Message}{ex.StackTrace}", GroupNames.成都开发測試群組);
             }
         }
 

+ 7 - 3
TEAMModelFunction/StuListService.cs

@@ -40,14 +40,15 @@ namespace TEAMModelFunction
                         scode = activity.code,
                         name = activity.name,
                         code = $"Activity-{activity.school}-{students.id}",
-                        scope = activity.scope,
+                        scope = activity.scope, 
                         school = activity.school,
                         creatorId = activity.creatorId,
                         pk = "Activity",
                         type = type,
-                        subjects = type.Equals("Exam") ? activity.subjects : new List<string>() { "" },
+                        subjects = activity.pk.ToLower().Equals("exam") && activity.subjects.IsNotEmpty() ? new List<string>() { activity.subjects[0].id } : new List<string>() { "" },
                         startTime = activity.startTime,
                         endTime = activity.endTime,
+                        blob=activity.blob,
                         owner= activity.owner
                     };
                     await client.GetContainer("TEAMModelOS", "Student").UpsertItemAsync(stucourse, new PartitionKey(stucourse.code));
@@ -65,7 +66,10 @@ namespace TEAMModelFunction
                         creatorId = activity.creatorId,
                         pk = "Activity",
                         type = type,
-                        subjects = type.ToLower().Equals("Exam") ? activity.subjects : new List<string>() { "" },
+                        subjects = activity.pk.ToLower().Equals("exam") && activity.subjects.IsNotEmpty() ? new List<string>() { activity.subjects[0].id } : new List<string>() { "" },
+                        startTime = activity.startTime,
+                        endTime = activity.endTime,
+                        blob = activity.blob,
                         owner = activity.owner
                     };
                     await client.GetContainer("TEAMModelOS", "Teacher").UpsertItemAsync(stucourse, new PartitionKey(stucourse.code));

+ 430 - 380
TEAMModelFunction/TriggerExam.cs

@@ -18,398 +18,431 @@ namespace TEAMModelFunction
 {
     public class TriggerExam
     {
-        public static async void Trigger( AzureCosmosFactory _azureCosmos, AzureServiceBusFactory _serviceBus, AzureStorageFactory _azureStorage, DingDing _dingDing,
+        public static async void Trigger(AzureCosmosFactory _azureCosmos, AzureServiceBusFactory _serviceBus, AzureStorageFactory _azureStorage, DingDing _dingDing,
             CosmosClient client, Document input, string code, long stime, long etime, string school)
         {
             ExamInfo info = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<ExamInfo>(input.Id, new Azure.Cosmos.PartitionKey($"{code}"));
             List<ExamClassResult> examClassResults = new List<ExamClassResult>();
             List<ExamSubject> examSubjects = new List<ExamSubject>();
-            if (info.scope.Equals("teacher", StringComparison.OrdinalIgnoreCase) || info.scope.Equals("private", StringComparison.OrdinalIgnoreCase))
+            try
             {
-                await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: $"select value(c) from c where c.examId = '{info.id}'", requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"ExamClassResult-{info.creatorId}") }))
+                if (info.scope.Equals("teacher", StringComparison.OrdinalIgnoreCase) || info.scope.Equals("private", StringComparison.OrdinalIgnoreCase))
                 {
-                    using var json = await JsonDocument.ParseAsync(item.ContentStream);
-                    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                    await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: $"select value(c) from c where c.examId = '{info.id}'", requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"ExamClassResult-{info.creatorId}") }))
                     {
-                        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                        using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                        if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
                         {
-                            examClassResults.Add(obj.ToObject<ExamClassResult>());
+                            foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                            {
+                                examClassResults.Add(obj.ToObject<ExamClassResult>());
+                            }
                         }
                     }
                 }
-            }
-            else
-            {
-                await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: $"select value(c) from c where c.examId = '{info.id}'", requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"ExamClassResult-{school}") }))
+                else
                 {
-                    using var json = await JsonDocument.ParseAsync(item.ContentStream);
-                    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                    await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: $"select value(c) from c where c.examId = '{info.id}'", requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"ExamClassResult-{school}") }))
                     {
-                        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                        using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                        if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
                         {
-                            examClassResults.Add(obj.ToObject<ExamClassResult>());
+                            foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                            {
+                                examClassResults.Add(obj.ToObject<ExamClassResult>());
+                            }
                         }
                     }
                 }
-            }
 
 
-            List<ChangeRecord> records = await _azureStorage.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", input.Id }, { "PartitionKey", info.progress } });
-            //处理科目信息
-            List<string> sub = new List<string>();
-            foreach (ExamSubject subject in info.subjects)
-            {
-                sub.Add(subject.id);
-            }
-            //ChangeRecord record = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<ChangeRecord>(input.Id, new Azure.Cosmos.PartitionKey($"{info.progress}"));
-            switch (info.progress)
-            {
-                case "pending":
-                    var message = new ServiceBusMessage(new { id = input.Id, progress = "going", code = code }.ToJsonString());
-                    message.ApplicationProperties.Add("name", "Exam");
-                    if (records.Count > 0)
-                    {
-                        await _serviceBus.GetServiceBusClient().cancelMessage(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), records[0].sequenceNumber);
-                        long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), message, DateTimeOffset.FromUnixTimeMilliseconds(stime));
-                        records[0].sequenceNumber = start;
-                        await _azureStorage.SaveOrUpdate<ChangeRecord>(records[0]);
-                        //await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(record, record.id, new Azure.Cosmos.PartitionKey($"{record.code}"));
-                    }
-                    else
-                    {
-                        long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), message, DateTimeOffset.FromUnixTimeMilliseconds(stime));
-                        ChangeRecord changeRecord = new ChangeRecord
+                List<ChangeRecord> records = await _azureStorage.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", input.Id }, { "PartitionKey", info.progress } });
+                //处理科目信息
+                List<string> sub = new List<string>();
+                foreach (ExamSubject subject in info.subjects)
+                {
+                    sub.Add(subject.id);
+                }
+                //ChangeRecord record = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<ChangeRecord>(input.Id, new Azure.Cosmos.PartitionKey($"{info.progress}"));
+                switch (info.progress)
+                {
+                    case "pending":
+                        var message = new ServiceBusMessage(new { id = input.Id, progress = "going", code = code }.ToJsonString());
+                        message.ApplicationProperties.Add("name", "Exam");
+                        if (records.Count > 0)
                         {
-                            RowKey = input.Id,
-                            PartitionKey = "pending",
-                            sequenceNumber = start,
-                            msgId = message.MessageId
-                        };
-                        await _azureStorage.Save<ChangeRecord>(changeRecord);
-                        //await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(changeRecord, new Azure.Cosmos.PartitionKey($"{changeRecord.code}"));
-                    }
-                    break;
-                case "going":
-                    (List<string> tmdids, List<Students> studentss) = await TriggerStuActivity.GetStuList(client, _dingDing, info.classes, info.school);
-                    List<StuActivity> stuActivities = new List<StuActivity>();
-                    List<StuActivity> tmdActivities = new List<StuActivity>();
-                    if (tmdids.IsNotEmpty())
-                    {
-                        tmdids.ForEach(x =>
+                            await _serviceBus.GetServiceBusClient().cancelMessage(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), records[0].sequenceNumber);
+                            long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), message, DateTimeOffset.FromUnixTimeMilliseconds(stime));
+                            records[0].sequenceNumber = start;
+                            await _azureStorage.SaveOrUpdate<ChangeRecord>(records[0]);
+                            //await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(record, record.id, new Azure.Cosmos.PartitionKey($"{record.code}"));
+                        }
+                        else
                         {
-                            tmdActivities.Add(new StuActivity
+                            long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), message, DateTimeOffset.FromUnixTimeMilliseconds(stime));
+                            ChangeRecord changeRecord = new ChangeRecord
                             {
-                                pk = "Activity",
-                                id = info.id,
-                                code = $"Activity-{x}",
-                                type = "Exam",
-                                name = info.name,
-                                startTime = info.startTime,
-                                endTime = info.endTime,
-                                scode = info.code,
-                                scope = info.scope,
-                                school = info.school,
-                                creatorId = info.creatorId,
-                                subjects = sub,
-                                blob = null,
-                                owner = info.owner
-
-                            });
-                        });
-                    }
-                    if (studentss.IsNotEmpty())
-                    {
-                        studentss.ForEach(x =>
+                                RowKey = input.Id,
+                                PartitionKey = "pending",
+                                sequenceNumber = start,
+                                msgId = message.MessageId
+                            };
+                            await _azureStorage.Save<ChangeRecord>(changeRecord);
+                            //await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(changeRecord, new Azure.Cosmos.PartitionKey($"{changeRecord.code}"));
+                        }
+                        break;
+                    case "going":
+                        (List<string> tmdids, List<Students> studentss) = await TriggerStuActivity.GetStuList(client, _dingDing, info.classes, info.school);
+                        List<StuActivity> stuActivities = new List<StuActivity>();
+                        List<StuActivity> tmdActivities = new List<StuActivity>();
+                        try
                         {
-                            stuActivities.Add(new StuActivity
+                            if (tmdids.IsNotEmpty())
                             {
-                                pk = "Activity",
-                                id = info.id,
-                                code = $"Activity-{info.school}-{x.id}",
-                                type = "Exam",
-                                name = info.name,
-                                startTime = info.startTime,
-                                endTime = info.endTime,
-                                scode = info.code,
-                                scope = info.scope,
-                                school = info.school,
-                                creatorId = info.creatorId,
-                                subjects = sub,
-                                blob = null,
-                                owner = info.owner
-                            });
-                        });
-                    }
-                    await TriggerStuActivity.SaveStuActivity(client, _dingDing, stuActivities, tmdActivities);
-                    //向学生或醍摩豆账号发起通知
-                    #region
-                    Notice notice = new Notice()
-                    {
-                        msgId=info.id,
-                        creation = info.startTime,
-                        expire = info.endTime,
-                        creatorId = info.creatorId,
-                        stuids = studentss,
-                        tmdids = tmdids,
-                        type = "notice",//评测参加通知
-                        priority = "normal",
-                        school = info.school,
-                        scope = info.scope,
-                        //data = new { }.ToJsonString()
-                        body = new Body {sid=info.id,scode=info.code,spk=info.pk,biztype= "exam-join" }
+                                tmdids.ForEach(x =>
+                                {
+                                    tmdActivities.Add(new StuActivity
+                                    {
+                                        pk = "Activity",
+                                        id = info.id,
+                                        code = $"Activity-{x}",
+                                        type = "Exam",
+                                        name = info.name,
+                                        startTime = info.startTime,
+                                        endTime = info.endTime,
+                                        scode = info.code,
+                                        scope = info.scope,
+                                        school = info.school,
+                                        creatorId = info.creatorId,
+                                        subjects = sub,
+                                        blob = null,
+                                        owner = info.owner
 
-                    };
-                    //var messageBlob = new ServiceBusMessage(notice.ToJsonString());
-                    //messageBlob.ApplicationProperties.Add("name", "Notice");
-                    //await _serviceBus.GetServiceBusClient().SendMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageBlob);
-                    #endregion
-                    if (examClassResults.Count == 0)
-                    {
-                        foreach (string cla in info.classes)
-                        {
-                            int m = 0;
-                            foreach (ExamSubject subject in info.subjects)
+                                    });
+                                });
+                            }
+                            if (studentss.IsNotEmpty())
                             {
-                                string classCode = "";
-                                if (string.IsNullOrEmpty(info.school) || !info.scope.Equals("school", StringComparison.OrdinalIgnoreCase))
+                                studentss.ForEach(x =>
                                 {
-                                    classCode = "ExamClassResult-" + info.creatorId;
-                                }
-                                else
-                                {
-                                    classCode = "ExamClassResult-" + info.school;
-                                }
-                                ExamClassResult result = new ExamClassResult
-                                {
-                                    code = classCode,
-                                    examId = info.id,
-                                    id = Guid.NewGuid().ToString(),
-                                    subjectId = subject.id,
-                                    year = info.year,
-                                    scope = info.scope
-                                };
-                                result.info.id = cla;
-                                List<string> ans = new List<string>();
-                                List<List<string>> anses = new List<List<string>>();
-                                List<double> ansPoint = new List<double>();
-                                List<string> ids = new List<string>();
-                                foreach (double p in info.papers[m].point)
-                                {
-                                    //ans.Add(new List<string>());
-                                    anses.Add(new List<string>());
-                                    ansPoint.Add(-1);
-                                }
-                                var sresponse = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(cla, new Azure.Cosmos.PartitionKey($"Class-{info.school}"));
-                                if (sresponse.Status == 200)
+                                    stuActivities.Add(new StuActivity
+                                    {
+                                        pk = "Activity",
+                                        id = info.id,
+                                        code = $"Activity-{info.school}-{x.id}",
+                                        type = "Exam",
+                                        name = info.name,
+                                        startTime = info.startTime,
+                                        endTime = info.endTime,
+                                        scode = info.code,
+                                        scope = info.scope,
+                                        school = info.school,
+                                        creatorId = info.creatorId,
+                                        subjects = sub,
+                                        blob = null,
+                                        owner = info.owner
+                                    });
+                                });
+                            }
+                            await TriggerStuActivity.SaveStuActivity(client, _dingDing, stuActivities, tmdActivities);
+                            //向学生或醍摩豆账号发起通知
+                            #region
+                            Notice notice = new Notice()
+                            {
+                                msgId = info.id,
+                                creation = info.startTime,
+                                expire = info.endTime,
+                                creatorId = info.creatorId,
+                                stuids = studentss,
+                                tmdids = tmdids,
+                                type = "notice",//评测参加通知
+                                priority = "normal",
+                                school = info.school,
+                                scope = info.scope,
+                                //data = new { }.ToJsonString()
+                                body = new Body { sid = info.id, scode = info.code, spk = info.pk, biztype = "exam-join" }
+
+                            };
+                            //var messageBlob = new ServiceBusMessage(notice.ToJsonString());
+                            //messageBlob.ApplicationProperties.Add("name", "Notice");
+                            //await _serviceBus.GetServiceBusClient().SendMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageBlob);
+                            #endregion
+                            if (examClassResults.Count == 0)
+                            {
+                                foreach (string cla in info.classes)
                                 {
-                                    using var json = await JsonDocument.ParseAsync(sresponse.ContentStream);
-                                    Class classroom = json.ToObject<Class>();
-                                    //result.info.id = classroom.id;
-                                    result.info.name = classroom.name;
-                                    result.gradeId = classroom.gradeId;
-                                    //处理班级人数
-                                    await foreach (var item in client.GetContainer("TEAMModelOS", "Student").GetItemQueryStreamIterator(queryText: $"select c.id from c where c.classId = '{classroom.id}'", requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Base-{info.school}") }))
+                                    int m = 0;
+                                    foreach (ExamSubject subject in info.subjects)
                                     {
-                                        using var json_stu = await JsonDocument.ParseAsync(item.ContentStream);
-                                        if (json_stu.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                                        string classCode = "";
+                                        if (string.IsNullOrEmpty(info.school) || !info.scope.Equals("school", StringComparison.OrdinalIgnoreCase))
+                                        {
+                                            classCode = "ExamClassResult-" + info.creatorId;
+                                        }
+                                        else
+                                        {
+                                            classCode = "ExamClassResult-" + info.school;
+                                        }
+                                        ExamClassResult result = new ExamClassResult
                                         {
-                                            var accounts = json_stu.RootElement.GetProperty("Documents").EnumerateArray();
-                                            while (accounts.MoveNext())
+                                            code = classCode,
+                                            examId = info.id,
+                                            id = Guid.NewGuid().ToString(),
+                                            subjectId = subject.id,
+                                            year = info.year,
+                                            scope = info.scope
+                                        };
+                                        result.info.id = cla;
+                                        List<string> ans = new List<string>();
+                                        List<List<string>> anses = new List<List<string>>();
+                                        List<double> ansPoint = new List<double>();
+                                        List<string> ids = new List<string>();
+                                        foreach (double p in info.papers[m].point)
+                                        {
+                                            //ans.Add(new List<string>());
+                                            anses.Add(new List<string>());
+                                            ansPoint.Add(-1);
+                                        }
+                                        var sresponse = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(cla, new Azure.Cosmos.PartitionKey($"Class-{info.school}"));
+                                        if (sresponse.Status == 200)
+                                        {
+                                            using var json = await JsonDocument.ParseAsync(sresponse.ContentStream);
+                                            Class classroom = json.ToObject<Class>();
+                                            //result.info.id = classroom.id;
+                                            result.info.name = classroom.name;
+                                            result.gradeId = classroom.gradeId;
+                                            //处理班级人数
+                                            await foreach (var item in client.GetContainer("TEAMModelOS", "Student").GetItemQueryStreamIterator(queryText: $"select c.id from c where c.classId = '{classroom.id}'", requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Base-{info.school}") }))
                                             {
-                                                JsonElement account = accounts.Current;
-                                                ids.Add(account.GetProperty("id").GetString());
+                                                using var json_stu = await JsonDocument.ParseAsync(item.ContentStream);
+                                                if (json_stu.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                                                {
+                                                    var accounts = json_stu.RootElement.GetProperty("Documents").EnumerateArray();
+                                                    while (accounts.MoveNext())
+                                                    {
+                                                        JsonElement account = accounts.Current;
+                                                        ids.Add(account.GetProperty("id").GetString());
+                                                    }
+                                                }
                                             }
                                         }
-                                    }
-                                }
-                                if (info.scope.Equals("private", StringComparison.OrdinalIgnoreCase))
-                                {
-                                    var stuResponse = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemStreamAsync(cla, new Azure.Cosmos.PartitionKey($"StuList"));
-                                    if (stuResponse.Status == 200)
-                                    {
-                                        using var json = await JsonDocument.ParseAsync(stuResponse.ContentStream);
-                                        StuList stuList = json.ToObject<StuList>();
-                                        //result.info.id = stuList.id;
-                                        result.info.name = stuList.name;
-                                        //处理发布对象为自选名单(个人)
-
-                                        foreach (Students stus in stuList.students)
+                                        if (info.scope.Equals("private", StringComparison.OrdinalIgnoreCase))
                                         {
+                                            var stuResponse = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemStreamAsync(cla, new Azure.Cosmos.PartitionKey($"StuList"));
+                                            if (stuResponse.Status == 200)
+                                            {
+                                                using var json = await JsonDocument.ParseAsync(stuResponse.ContentStream);
+                                                StuList stuList = json.ToObject<StuList>();
+                                                //result.info.id = stuList.id;
+                                                result.info.name = stuList.name;
+                                                //处理发布对象为自选名单(个人)
+
+                                                foreach (Students stus in stuList.students)
+                                                {
 
-                                            ids.Add(stus.id);
+                                                    ids.Add(stus.id);
+                                                }
+                                                if (stuList.tmids.Count > 0)
+                                                {
+                                                    foreach (string tid in stuList.tmids)
+                                                    {
+                                                        ids.Add(tid);
+                                                    }
+                                                }
+                                            }
                                         }
-                                        if (stuList.tmids.Count > 0)
+                                        else
                                         {
-                                            foreach (string tid in stuList.tmids)
+                                            var stuResponse = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(cla, new Azure.Cosmos.PartitionKey($"StuList-{info.school}"));
+                                            if (stuResponse.Status == 200)
                                             {
-                                                ids.Add(tid);
+                                                using var json = await JsonDocument.ParseAsync(stuResponse.ContentStream);
+                                                StuList stuList = json.ToObject<StuList>();
+                                                //result.info.id = stuList.id;
+                                                result.info.name = stuList.name;
+                                                //处理发布对象为自选名单(校本)
+                                                foreach (Students stus in stuList.students)
+                                                {
+                                                    ids.Add(stus.id);
+                                                }
                                             }
                                         }
+                                        foreach (string stu in ids)
+                                        {
+                                            result.mark.Add("");
+                                            result.studentIds.Add(stu);
+                                            result.studentAnswers.Add(ans);
+                                            result.studentScores.Add(ansPoint);
+                                            result.ans.Add(anses);
+                                            result.sum.Add(0);
+                                        }
+
+                                        //result.progress = info.progress;
+                                        result.school = info.school;
+                                        m++;
+                                        await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(result, new Azure.Cosmos.PartitionKey($"{result.code}"));
                                     }
                                 }
+                                // 发送信息通知
+                                var messageEnd = new ServiceBusMessage(new { id = input.Id, progress = "finish", code = code }.ToJsonString());
+                                messageEnd.ApplicationProperties.Add("name", "Exam");
+                                if (records.Count > 0)
+                                {
+                                    long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageEnd, DateTimeOffset.FromUnixTimeMilliseconds(etime));
+                                    await _serviceBus.GetServiceBusClient().cancelMessage(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), records[0].sequenceNumber);
+                                    records[0].sequenceNumber = end;
+                                    await _azureStorage.SaveOrUpdate<ChangeRecord>(records[0]);
+                                    //await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(record, record.id, new Azure.Cosmos.PartitionKey($"{record.code}"));
+                                }
                                 else
                                 {
-                                    var stuResponse = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(cla, new Azure.Cosmos.PartitionKey($"StuList-{info.school}"));
-                                    if (stuResponse.Status == 200)
+                                    long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageEnd, DateTimeOffset.FromUnixTimeMilliseconds(etime));
+                                    ChangeRecord changeRecord = new ChangeRecord
                                     {
-                                        using var json = await JsonDocument.ParseAsync(stuResponse.ContentStream);
-                                        StuList stuList = json.ToObject<StuList>();
-                                        //result.info.id = stuList.id;
-                                        result.info.name = stuList.name;
-                                        //处理发布对象为自选名单(校本)
-                                        foreach (Students stus in stuList.students)
-                                        {
-                                            ids.Add(stus.id);
-                                        }
-                                    }
+                                        RowKey = input.Id,
+                                        PartitionKey = "going",
+                                        sequenceNumber = end,
+                                        msgId = messageEnd.MessageId
+                                    };
+                                    await _azureStorage.Save<ChangeRecord>(changeRecord);
+                                    //await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(changeRecord, new Azure.Cosmos.PartitionKey($"{changeRecord.code}"));
                                 }
-                                foreach (string stu in ids)
+                            }
+                            else
+                            {
+                                //处理单科结算时科目与试卷信息匹配的问题
+                                int gno = 0;
+                                foreach (ExamSubject subject in info.subjects)
                                 {
-                                    result.mark.Add("");
-                                    result.studentIds.Add(stu);
-                                    result.studentAnswers.Add(ans);
-                                    result.studentScores.Add(ansPoint);
-                                    result.ans.Add(anses);
-                                    result.sum.Add(0);
+                                    if (subject.classCount == info.classes.Count)
+                                    {
+                                        await createClassResultAsync(info, examClassResults, subject, gno, _azureCosmos, _dingDing, _azureStorage);
+                                    }
+                                    gno++;
                                 }
-
-                                //result.progress = info.progress;
-                                result.school = info.school;
-                                m++;
-                                await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(result, new Azure.Cosmos.PartitionKey($"{result.code}"));
                             }
                         }
-                        // 发送信息通知
-                        var messageEnd = new ServiceBusMessage(new { id = input.Id, progress = "finish", code = code }.ToJsonString());
-                        messageEnd.ApplicationProperties.Add("name", "Exam");
-                        if (records.Count > 0)
+                        catch (Exception e)
                         {
-                            long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageEnd, DateTimeOffset.FromUnixTimeMilliseconds(etime));
-                            await _serviceBus.GetServiceBusClient().cancelMessage(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), records[0].sequenceNumber);
-                            records[0].sequenceNumber = end;
-                            await _azureStorage.SaveOrUpdate<ChangeRecord>(records[0]);
-                            //await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(record, record.id, new Azure.Cosmos.PartitionKey($"{record.code}"));
+                            await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-评测going状态异常{e.Message}\n{e.StackTrace}", GroupNames.成都开发測試群組);
                         }
-                        else
+                        break;
+                    case "finish":
+                        int fno = 0;
+                        try
                         {
-                            long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageEnd, DateTimeOffset.FromUnixTimeMilliseconds(etime));
-                            ChangeRecord changeRecord = new ChangeRecord
-                            {
-                                RowKey = input.Id,
-                                PartitionKey = "going",
-                                sequenceNumber = end,
-                                msgId = messageEnd.MessageId
-                            };
-                            await _azureStorage.Save<ChangeRecord>(changeRecord);
-                            //await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(changeRecord, new Azure.Cosmos.PartitionKey($"{changeRecord.code}"));
-                        }
-                    }
-                    else
-                    {
-                        //处理单科结算时科目与试卷信息匹配的问题
-                        int gno = 0;
-                        foreach (ExamSubject subject in info.subjects)
-                        {
-                            if (subject.classCount == info.classes.Count)
+                            foreach (ExamSubject subject in info.subjects)
                             {
-                                await createClassResultAsync(info, examClassResults, subject, gno, _azureCosmos, _dingDing, _azureStorage);
+                                await createClassResultAsync(info, examClassResults, subject, fno, _azureCosmos, _dingDing, _azureStorage);
+                                fno++;
                             }
-                            gno++;
-                        }
-                    }
-                    break;
-                case "finish":
-                    int fno = 0;
-                    foreach (ExamSubject subject in info.subjects)
-                    {
-                        await createClassResultAsync(info, examClassResults, subject, fno, _azureCosmos, _dingDing, _azureStorage);
-                        fno++;
-                    }
 
-                    //计算单次考试简易统计信息
-                    List<ExamResult> examResults = new List<ExamResult>();
-                    await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<ExamResult>(
-                                       queryText: $"select value(c) from c where c.examId  = '{info.id}'", requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"ExamResult-{info.id}") }))
-                    {
-                        examResults.Add(item);
-                    }
-                    //结算单科单班的标准差和平均分
-                    foreach (ExamClassResult classResult in examClassResults)
-                    {
-                        //标记单科单班总得分
-                        double subScore = 0;
-                        //标准差
-                        double sPowSum = 0;
-                        var scount = classResult.studentIds.Count;
-                        foreach (List<double> sc in classResult.studentScores)
-                        {
-                            subScore += sc.Sum();
-                        }
-                        foreach (string sid in classResult.studentIds)
-                        {
-                            double ssc = classResult.studentScores[classResult.studentIds.IndexOf(sid)].Sum();
-                            sPowSum += Math.Pow(ssc - scount > 0 ? Math.Round(subScore * 1.0 / scount, 2) : 0, 2);
+                            //计算单次考试简易统计信息
+                            List<ExamResult> examResults = new List<ExamResult>();
+                            await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<ExamResult>(
+                                               queryText: $"select value(c) from c where c.examId  = '{info.id}'", requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"ExamResult-{info.id}") }))
+                            {
+                                examResults.Add(item);
+                            }
+                            //结算单科单班的标准差和平均分
+                            foreach (ExamClassResult classResult in examClassResults)
+                            {
+                                //标记单科单班总得分
+                                double subScore = 0;
+                                //标准差
+                                double sPowSum = 0;
+                                var scount = classResult.studentIds.Count;
+                                foreach (List<double> sc in classResult.studentScores)
+                                {
+                                    List<double> newSc = new List<double>();
+                                    foreach (double ssc in sc)
+                                    {
+                                        if (ssc == -1)
+                                        {
+                                            newSc.Add(0);
+                                        }
+                                        else
+                                        {
+                                            newSc.Add(ssc);
+                                        }
+                                    }
+                                    subScore += newSc.Sum();
+                                }
+                                foreach (string sid in classResult.studentIds)
+                                {
+                                    double ssc = classResult.studentScores[classResult.studentIds.IndexOf(sid)].Sum();
+                                    sPowSum += Math.Pow(ssc - scount > 0 ? Math.Round(subScore * 1.0 / scount, 2) : 0, 2);
 
-                        }
-                        classResult.standard = Math.Round(scount > 0 ? Math.Pow(sPowSum / scount, 0.5) : 0, 2);
-                        classResult.average = scount > 0 ? Math.Round(subScore / scount, 2) : 0;
-                        await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(classResult, classResult.id, new Azure.Cosmos.PartitionKey($"{classResult.code}"));
-                    }
-                    //记录某次考试所有学生得分总分
-                    double score = 0;
-                    double allScore = 0;
-                    int stuCount = 0;
-                    //整体平均分
-                    double average = 0;
-                    //标准差
-                    double powSum = 0;
-                    List<string> losStu = new List<string>();
-                    //先与第一个值取并集
-                    if (examResults.Count > 0)
-                    {
-                        losStu.Union(examResults[0].lostStus);
-                        foreach (ExamResult examResult in examResults)
-                        {
-                            if (info.id == examResult.examId)
+                                }
+                                classResult.standard = Math.Round(scount > 0 ? Math.Pow(sPowSum / scount, 0.5) : 0, 2);
+                                classResult.average = scount > 0 ? Math.Round(subScore / scount, 2) : 0;
+                                classResult.progress = true;
+                                await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(classResult, classResult.id, new Azure.Cosmos.PartitionKey($"{classResult.code}"));
+                            }
+                            //记录某次考试所有学生得分总分
+                            double score = 0;
+                            double allScore = 0;
+                            int stuCount = 0;
+                            //标准差
+                            double powSum = 0;
+                            List<string> losStu = new List<string>();
+                            //先与第一个值取并集
+                            if (examResults.Count > 0)
                             {
-                                foreach (List<double> sc in examResult.studentScores)
+                                losStu = losStu.Union(examResults[0].lostStus).ToList();
+                                foreach (ExamResult examResult in examResults)
                                 {
-                                    score += sc.Sum();
+                                    if (info.id == examResult.examId)
+                                    {
+                                        foreach (List<double> sc in examResult.studentScores)
+                                        {
+                                            score += sc.Sum();
+                                        }
+                                        stuCount = examResult.studentIds.Count;
+                                    }
+                                    //powSum += Math.Pow(score - examResult.studentIds.Count > 0 ? Math.Round(score * 1.0 / examResult.studentIds.Count, 2) : 0, 2);
+                                    //取交集
+                                    losStu = losStu.Intersect(examResult.lostStus).ToList();
                                 }
-                                stuCount = examResult.studentIds.Count;
                             }
-                            //powSum += Math.Pow(score - examResult.studentIds.Count > 0 ? Math.Round(score * 1.0 / examResult.studentIds.Count, 2) : 0, 2);
-                            //取交集
-                            losStu = losStu.Intersect(examResult.lostStus).ToList();
+                            double NewsRateScore = stuCount > 0 ? Math.Round(score * 1.0 / stuCount, 2) : 0;
+                            foreach (PaperSimple simple in info.papers)
+                            {
+                                allScore += simple.point.Sum();
+                            }
+                            //计算全科标准差
+                            foreach (string id in examResults[0].studentIds)
+                            {
+                                double sc = 0;
+                                foreach (ExamResult result in examResults)
+                                {
+                                    sc += result.studentScores[result.studentIds.IndexOf(id)].Sum();
+                                }
+                                powSum += Math.Pow(sc - NewsRateScore, 2);
+                            }
+                            info.standard = Math.Round(examResults[0].studentIds.Count > 0 ? Math.Pow(powSum / examResults[0].studentIds.Count, 0.5) : 0, 2);
+                            double NewsRate = allScore > 0 ? Math.Round(NewsRateScore / allScore * 100, 2) : 0;
+                            info.lostStu = losStu;
+                            //判断均分是否发生变化,便于实时的更新评测基本信息
+                            if (info.sRate != NewsRate || info.average != NewsRateScore)
+                            {
+                                info.sRate = NewsRate;
+                                info.average = NewsRateScore;
+                                await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync<ExamInfo>(info, info.id, new Azure.Cosmos.PartitionKey(info.code));
+                            }
                         }
-                    }
-                    double NewsRateScore = stuCount > 0 ? Math.Round(score * 1.0 / stuCount, 2) : 0;
-                    foreach (PaperSimple simple in info.papers)
-                    {
-                        allScore += simple.point.Sum();
-                    }
-                    //计算全科标准差
-                    foreach (string id in examResults[0].studentIds)
-                    {
-                        double sc = 0;
-                        foreach (ExamResult result in examResults)
+                        catch (Exception e)
                         {
-                            sc += result.studentScores[result.studentIds.IndexOf(id)].Sum();
+                            await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-评测finish状态异常{e.Message}\n{e.StackTrace}", GroupNames.成都开发測試群組);
                         }
-                        powSum += Math.Pow(sc - NewsRateScore, 2);
-                    }
-                    info.standard = Math.Round(examResults[0].studentIds.Count > 0 ? Math.Pow(powSum / examResults[0].studentIds.Count, 0.5) : 0, 2);
-                    double NewsRate = allScore > 0 ? Math.Round(NewsRateScore / allScore * 100, 2) : 0;
-                    info.lostStu = losStu;
-                    //判断均分是否发生变化,便于实时的更新评测基本信息
-                    if (info.sRate != NewsRate || info.average != NewsRateScore)
-                    {
-                        info.sRate = NewsRate;
-                        info.average = NewsRateScore;
-                        await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync<ExamInfo>(info, info.id, new Azure.Cosmos.PartitionKey(info.code));
-                    }
-                    break;
+                        break;
+                }
             }
+            catch (Exception e)
+            {
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-评测结算异常{e.Message}\n{e.StackTrace}", GroupNames.成都开发測試群組);
+            }
+
         }
         //处理全部学生选题计数
         public static async Task examRecordCount(ExamInfo info, ExamSubject subject, DingDing _dingDing, int no, ExamResult result, List<ExamClassResult> classResults, AzureCosmosFactory _azureCosmos)
@@ -445,25 +478,30 @@ namespace TEAMModelFunction
                         foreach (string id in classResult.studentIds)
                         {
                             int index = classResult.studentIds.IndexOf(id);
-                            if (classResult.studentScores[index].Sum() >= rhw && PHCount < rhwCount)
+                            if (classResult.studentScores.Count > 0)
                             {
-                                if (classResult.ans.Count > 0)
+                                if (classResult.studentScores[index].Sum() >= rhw && PHCount < rhwCount)
                                 {
-                                    opth.Add(classResult.ans[index]);
-                                    PHCount++;
-                                    continue;
-                                }
+                                    if (classResult.ans.Count > 0)
+                                    {
+                                        opth.Add(classResult.ans[index]);
+                                        PHCount++;
+                                        continue;
+                                    }
 
-                            }
-                            if (classResult.studentScores[index].Sum() <= rhl && PLCount < (scores.Count - rhlCount))
-                            {
-                                if (classResult.ans.Count > 0)
+                                }
+                                if (classResult.studentScores[index].Sum() <= rhl && PLCount < (scores.Count - rhlCount))
                                 {
-                                    optl.Add(classResult.ans[index]);
-                                    PLCount++;
-                                    continue;
+                                    if (classResult.ans.Count > 0)
+                                    {
+                                        optl.Add(classResult.ans[index]);
+                                        PLCount++;
+                                        continue;
+                                    }
                                 }
+
                             }
+
                         }
                     }
                 }
@@ -472,7 +510,7 @@ namespace TEAMModelFunction
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"评测作答记录结算异常{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-评测作答记录结算异常{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
             }
         }
 
@@ -532,32 +570,36 @@ namespace TEAMModelFunction
                                     foreach (string id in classResult.studentIds)
                                     {
                                         int index = classResult.studentIds.IndexOf(id);
-                                        if (classResult.studentScores[index].Count > 0)
+                                        if (classResult.studentScores.Count > 0)
                                         {
-                                            score += classResult.studentScores[index][n];
-                                            if (classResult.studentScores[index].Sum() >= rhw && phcount < rhwCount)
+                                            if (classResult.studentScores[index].Count > 0)
                                             {
-                                                if (classResult.studentScores[index][n] == 0)
+                                                score += classResult.studentScores[index][n] == -1 ? 0 : classResult.studentScores[index][n];
+                                                if (classResult.studentScores[index].Sum() >= rhw && phcount < rhwCount)
                                                 {
-                                                    phCount++;
+                                                    if (classResult.studentScores[index][n] <= 0)
+                                                    {
+                                                        phCount++;
+                                                    }
+                                                    phcount++;
+                                                    continue;
                                                 }
-                                                phcount++;
-                                                continue;
-                                            }
-                                            if (classResult.studentScores[index].Sum() <= rhl && plcount < (info.stuCount - rhlCount))
-                                            {
-                                                if (classResult.studentScores[index][n] == 0)
+                                                if (classResult.studentScores[index].Sum() <= rhl && plcount < (info.stuCount - rhlCount))
                                                 {
-                                                    plCount++;
+                                                    if (classResult.studentScores[index][n] <= 0)
+                                                    {
+                                                        plCount++;
+                                                    }
+                                                    plcount++;
+                                                    continue;
+                                                }
+                                                if (classResult.studentScores[index][n] <= 0)
+                                                {
+                                                    pCount++;
                                                 }
-                                                plcount++;
-                                                continue;
-                                            }
-                                            if (classResult.studentScores[index][n] == 0)
-                                            {
-                                                pCount++;
                                             }
                                         }
+
                                     }
                                 }
                                 n++;
@@ -578,7 +620,7 @@ namespace TEAMModelFunction
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"评测知识点结算异常{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-评测知识点结算异常{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
             }
         }
 
@@ -624,30 +666,33 @@ namespace TEAMModelFunction
                                     foreach (string id in classResult.studentIds)
                                     {
                                         int index = classResult.studentIds.IndexOf(id);
-                                        if (classResult.studentScores[index].Count > 0)
+                                        if (classResult.studentScores.Count > 0)
                                         {
-                                            score += classResult.studentScores[index][n];
-                                            if (classResult.studentScores[index].Sum() >= rhw && phcount < rhwCount)
+                                            if (classResult.studentScores[index].Count > 0)
                                             {
-                                                if (classResult.studentScores[index][n] == 0)
+                                                score += classResult.studentScores[index][n] == -1 ? 0 : classResult.studentScores[index][n];
+                                                if (classResult.studentScores[index].Sum() >= rhw && phcount < rhwCount)
                                                 {
-                                                    phCount++;
+                                                    if (classResult.studentScores[index][n] <= 0)
+                                                    {
+                                                        phCount++;
+                                                    }
+                                                    phcount++;
+                                                    continue;
                                                 }
-                                                phcount++;
-                                                continue;
-                                            }
-                                            if (classResult.studentScores[index].Sum() <= rhl && plcount < (info.stuCount - rhlCount))
-                                            {
-                                                if (classResult.studentScores[index][n] == 0)
+                                                if (classResult.studentScores[index].Sum() <= rhl && plcount < (info.stuCount - rhlCount))
                                                 {
-                                                    plCount++;
+                                                    if (classResult.studentScores[index][n] <= 0)
+                                                    {
+                                                        plCount++;
+                                                    }
+                                                    plcount++;
+                                                    continue;
+                                                }
+                                                if (classResult.studentScores[index][n] <= 0)
+                                                {
+                                                    pCount++;
                                                 }
-                                                plcount++;
-                                                continue;
-                                            }
-                                            if (classResult.studentScores[index][n] == 0)
-                                            {
-                                                pCount++;
                                             }
                                         }
                                     }
@@ -670,7 +715,7 @@ namespace TEAMModelFunction
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"评测认知层次结算异常{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-评测认知层次结算异常{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
             }
         }
         //处理选题计数
@@ -791,7 +836,12 @@ namespace TEAMModelFunction
                     {
                         result.studentIds.Add(id);
                     }
-                    csRate.Add(result.studentIds.Count > 0 ? Math.Round(classSrate * 1.0 / classResult.studentIds.Count, 2) : 0 / allScore);
+                    if (allScore > 0)
+                    {
+                        csRate.Add(classResult.studentIds.Count > 0 ? Math.Round(classSrate * 1.0 / classResult.studentIds.Count, 2) : 0 / allScore);
+
+                    }
+
                     //powSum += Math.Pow(classSrate - result.average, 2);
                     //处理选项计数内容
                 }
@@ -817,7 +867,7 @@ namespace TEAMModelFunction
             result.csRate = csRate;
             result.lostStus = lostStu;
 
-            result.sRate = Math.Round(result.average / allScore * 100, 2);
+            result.sRate = allScore > 0 ? Math.Round(result.average / allScore * 100, 2) : 0;
             result.classes = classRanges;
             result.code = "ExamResult-" + info.id;
             result.school = info.school;

+ 2 - 2
TEAMModelFunction/TriggerStuActivity.cs

@@ -35,7 +35,7 @@ namespace TEAMModelFunction
                     }
                 }
             } catch (Exception ex) {
-                await _dingDing.SendBotMsg($"OS,TriggerStuActivity-SaveStuActivity\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-OS,TriggerStuActivity-SaveStuActivity\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
             }
             return ""; 
         }
@@ -98,7 +98,7 @@ namespace TEAMModelFunction
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"OS,TriggerStuActivity-SaveStuActivity\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-OS,TriggerStuActivity-SaveStuActivity\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
             }
             return (null, null);
         }

+ 6 - 6
TEAMModelFunction/TriggerSurvey.cs

@@ -72,7 +72,7 @@ namespace TEAMModelFunction
                     case "going":
                         (List<string> tmdids,List<Students> students) =   await TriggerStuActivity. GetStuList(client, _dingDing, survey.classes, survey.school);
 #if DEBUG
-                        await _dingDing.SendBotMsg($"问卷调查{tdata.id}写入学生表作为活动列表!", GroupNames.成都开发測試群組);
+                        await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}问卷调查{tdata.id}写入学生表作为活动列表!", GroupNames.成都开发測試群組);
 #endif
                         List<StuActivity> stuActivities = new List<StuActivity>();
                         List<StuActivity> tmdActivities = new List<StuActivity>();
@@ -142,7 +142,7 @@ namespace TEAMModelFunction
                         //await _serviceBus.GetServiceBusClient().SendMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageBlob);
                         #endregion
 #if DEBUG
-                        await _dingDing.SendBotMsg($"问卷调查{tdata.id}写入完成!", GroupNames.成都开发測試群組);
+                        await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}问卷调查{tdata.id}写入完成!", GroupNames.成都开发測試群組);
 #endif
                         var messageSurveyEnd = new ServiceBusMessage(new { id = input.Id, progress = "finish", code = tdata.code }.ToJsonString());
                         messageSurveyEnd.ApplicationProperties.Add("name", "Survey");
@@ -166,12 +166,12 @@ namespace TEAMModelFunction
                             await _azureStorage.Save<ChangeRecord>(changeRecord);
                         }
 #if DEBUG
-                        await _dingDing.SendBotMsg($"问卷调查{tdata.id}将于:{tdata.etime}完成并结算!", GroupNames.成都开发測試群組);
+                        await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}问卷调查{tdata.id}将于:{tdata.etime}完成并结算!", GroupNames.成都开发測試群組);
 #endif
                         break;
                     case "finish":
 #if DEBUG
-                        await _dingDing.SendBotMsg($"问卷调查{tdata.id}开始结算{tdata.etime}!", GroupNames.成都开发測試群組);
+                        await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}问卷调查{tdata.id}开始结算{tdata.etime}!", GroupNames.成都开发測試群組);
 #endif
                         var records = await _azureRedis.GetRedisClient(8).HashGetAllAsync($"Survey:Record:{survey.id}");
                         var submits = await _azureRedis.GetRedisClient(8).SetMembersAsync($"Survey:Submit:{survey.id}");
@@ -205,7 +205,7 @@ namespace TEAMModelFunction
                                 surveyRecords.Add(Record);
                             }
 #if DEBUG
-                            await _dingDing.SendBotMsg($"问卷调查问题结算数据{surveyRecords.ToJsonString()}", GroupNames.成都开发測試群組);
+                            await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}问卷调查问题结算数据{surveyRecords.ToJsonString()}", GroupNames.成都开发測試群組);
 #endif
                             for (int index = 0; index < survey.answers.Count; index++)
                             {
@@ -250,7 +250,7 @@ namespace TEAMModelFunction
                             }
                            // await Task.WhenAll(tasks);
                         } catch (Exception ex) {
-                            await _dingDing.SendBotMsg($"问卷调查问题结算异常{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
+                            await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}问卷调查问题结算异常{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
                         }
                         var cods = new { records = recs, userids, question= questionRecords };
                         //问卷整体情况

+ 3 - 5
TEAMModelFunction/TriggerVote.cs

@@ -40,9 +40,7 @@ namespace TEAMModelFunction
                 adcode = $"Activity-{tdata.creatorId}";
                 blobcntr = tdata.creatorId;
             }
-#if DEBUG
-            await _dingDing.SendBotMsg($"投票活动【{tdata.name}-{tdata.id}-ttl={tdata.ttl}】正在操作", GroupNames.成都开发測試群組);
-#endif
+            await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}投票活动【{tdata.name}-{tdata.id}-ttl={tdata.ttl}】正在操作", GroupNames.成都开发測試群組);
             Vote vote = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Vote>(input.Id, new Azure.Cosmos.PartitionKey($"{tdata.code}"));
             List<ChangeRecord> voteRecords = await _azureStorage.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", input.Id }, { "PartitionKey", vote.progress } });
             if (vote != null) {
@@ -171,12 +169,12 @@ namespace TEAMModelFunction
                             await _azureStorage.Save<ChangeRecord>(changeRecord);
                         }
 #if DEBUG
-                        await _dingDing.SendBotMsg($"投票活动{tdata.id}将于:{tdata.etime}完成并结算!", GroupNames.成都开发測試群組);
+                        await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}投票活动{tdata.id}将于:{tdata.etime}完成并结算!", GroupNames.成都开发測試群組);
 #endif
                         break;
                     case "finish":
 #if DEBUG
-                        await _dingDing.SendBotMsg($"投票活动{tdata.id}开始结算{tdata.etime}!", GroupNames.成都开发測試群組);
+                        await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}投票活动{tdata.id}开始结算{tdata.etime}!", GroupNames.成都开发測試群組);
 #endif
                         //获取投票活动的所有投票记录
                         var records = await _azureRedis.GetRedisClient(8).HashGetAllAsync($"Vote:Record:{vote.id}");

+ 1 - 0
TEAMModelFunction/local.settings.json

@@ -12,6 +12,7 @@
     "Azure:Redis:ConnectionString": "106.12.23.251:6379,password=habook,ssl=false,abortConnect=False,writeBuffer=10240",
     "Azure:ServiceBus:ActiveTask": "dep-active-task",
     "Azure:ServiceBus:NoticeTask": "dep-notice-task",
+    "Option:Location": "China-Dep",
     "FUNCTIONS_WORKER_RUNTIME": "dotnet"
   }  
 }

+ 1 - 0
TEAMModelOS.SDK/Context/Configuration/Option.cs

@@ -18,6 +18,7 @@ namespace TEAMModelOS.Models
         public string LocationNum { get; set; }  
         public string BlobDomain { get; set; }
         public string HostName { get; set; }
+        public string HttpTrigger { get; set; }
         public IList<string> AllowedHosts { get; }
         public IList<StringSegment> AllowedRedirects
         {

+ 0 - 110
TEAMModelOS.SDK/Models/Cosmos/Common/Inner/SyllabusNode.cs

@@ -1,110 +0,0 @@
-
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using TEAMModelOS.SDK.Context.Attributes.Azure;
-using TEAMModelOS.SDK.DI;
-
-namespace TEAMModelOS.SDK.Models
-{
-
-    public class SyllabusNode
-    {
-
-        public SyllabusNode() {
-            //resources = new List<string>();
-            //knowledges = new List<string>();
-        }
-        /// <summary>
-        /// 
-        /// </summary>
-        [Required(ErrorMessage = "{0} 必须填写")]
-
-        public string id { get; set; }
-        /// <summary>
-        /// 节点名称
-        /// </summary>
-        [Required(ErrorMessage = "{0} 必须填写")]
-
-        public string title { get; set; }
-        /// <summary>
-        /// 是否展开
-        /// </summary>
-        public bool expand { get; set; }
-        /// <summary>
-        /// 是否编辑
-        /// </summary>
-        public bool editable { get; set; } = true;
-        /// <summary>
-        /// 版本
-        /// </summary>
-        public string version { get; set; }
-        /// <summary>
-        /// 排序
-        /// </summary>
-        [Required(ErrorMessage = "{0} 必须填写")]
-
-        public int order { get; set; }
-        /// <summary>
-        /// 类型  0分支节点 1资源节点
-        /// </summary>
-        [Required(ErrorMessage = "{0} 必须填写")]
-
-        public int type { get; set; }
-
-        /// <summary>
-        /// 节点Key
-        /// </summary>
-        [Required(ErrorMessage = "{0} 必须填写")]
-
-        public string nodeKey { get; set; }
-
-        /// <summary>
-        /// 父级
-        /// </summary>
-        [Required(ErrorMessage = "{0} 必须填写")]
-
-        public string pid { get; set; }
-
-        /// <summary>
-        /// 册别编码
-        /// </summary>
-        [Required(ErrorMessage = "{0} 必须填写")]
-
-        public string code { get; set; }
-        /// <summary>
-        /// 数据状态
-        /// </summary>
-        [Required(ErrorMessage = "{0} 必须填写")]
-
-        public int status { get; set; } = 1;
-
-       // public List<CodeValue> resources { get; set; }
-        /// <summary>
-        /// 关联知识点
-        /// </summary>
-        public List<string> points { get; set; } = new List<string>();
-
-        public List<SyllabusContent> items { get; set; }
-
-
-        //public bool resource { get; set; }
-        //[ProtoMember(15)]
-        //public bool knowledge { get; set; }
-        //public bool item { get; set; }
-
-    }
-
-    /// <summary>
-    /// 课纲内容结构
-    /// </summary>
-    public class SyllabusContent{
-        public string id { get; set; }
-        public string name { get; set; }
-        public string code { get; set; }
-        public string link { get; set; }
-        /// <summary>
-        /// 试题 ,试卷,内容资源,
-        /// </summary>
-        public string type { get; set; }
-    }
-}

+ 11 - 1
TEAMModelOS.SDK/Models/Cosmos/Common/Inner/SyllabusTree.cs

@@ -1,12 +1,22 @@
 using System.Collections.Generic;
+using TEAMModelOS.SDK.Models.Cosmos.Common;
 
 namespace TEAMModelOS.SDK.Models
 {
-    public class SyllabusTree :SyllabusNode
+    public class SyllabusTree : Tnode
     {
         public SyllabusTree() {
             children = new List<SyllabusTree>();
         }
         public List<SyllabusTree> children { get; set; }
     }
+    public class SyllabusTreeNode{
+        public string id { get; set; }
+        public string code { get; set; }
+        public SyllabusTreeNode() {
+            trees = new List<SyllabusTree>();
+        }
+        public List<SyllabusTree> trees { get; set; }
+    }
+
 }

+ 19 - 28
TEAMModelOS.SDK/Models/Cosmos/Common/Notice.cs

@@ -39,32 +39,40 @@ namespace TEAMModelOS.SDK.Models
     ///             tmdid:/receiver/xxxx时间戳排序.json
     /// </summary>
     public class Notice
-    {
+    { 
         /// <summary>
-        /// 消息id 
+        ///  訊息種類 message:訊息,收取會另存至儲存體(blob or table) notice:通知,收取後不另存 
+        /// XXXX通知的业务类型,做什么事情用,具体业务类型再定义。 vote-join 
         /// </summary>
-        public string msgId { get; set; }
+        public string type { get; set; }
+        /// <summary>
+        /// 重要等级 emergency、high、normal、low 1置顶,2非常重要,3重要,4普通消息,5不重要
+        /// </summary>
+        public string priority { get; set; }
         /// <summary>
         /// 發信源服務  IES5
         /// </summary>
         public string from { get; set; } = "IES5";
         /// <summary>
-        ///  訊息種類 message:訊息,收取會另存至儲存體(blob or table) notice:通知,收取後不另存 
-       /// XXXX通知的业务类型,做什么事情用,具体业务类型再定义。 vote-join 
+        /// 服務收信對象 若任一服務皆可收,則不填;指定某服務才收,則填服務名稱 例:HiT
         /// </summary>
-        public string type { get; set; }
+        public string toservice { get; set; }
         /// <summary>
-        /// 重要等级 emergency、high、normal、low 1置顶,2非常重要,3重要,4普通消息,5不重要
+        /// 到期时间,发给接收者的cosmosDB ttl是从当前时间到结束时间为止
         /// </summary>
-        public string priority { get; set; }
+        public long expire { get; set; }
         /// <summary>
         /// 创建时间
         /// </summary>
         public long creation { get; set; }
         /// <summary>
-        /// 到期时间,发给接收者的cosmosDB ttl是从当前时间到结束时间为止
+        /// 消息id 
         /// </summary>
-        public long expire { get; set; }
+        public string msgId { get; set; }
+        /// <summary>
+        /// 正文內容
+        /// </summary>
+        public Body body { get; set; }
         /// <summary>
         /// 创建者
         /// </summary>
@@ -86,11 +94,7 @@ namespace TEAMModelOS.SDK.Models
         /// 被通知的学校学生账号
         /// </summary>
         public List<Students> stuids { get; set; }
-        public  int ttl { get; set;  }
-        /// <summary>
-        /// 正文內容
-        /// </summary>
-        public Body body { get; set; }
+       
     }
 
     /// <summary>
@@ -134,24 +138,11 @@ namespace TEAMModelOS.SDK.Models
     public class Receiver  
     {
         public string id { get; set; }
-        public string pk { get; set; } = "Receiver";
         /// <summary>
         /// 0 已发送,1已查看,2已处理,-1已过期
         /// </summary>
         public int status { get; set; }=0;
         /// <summary>
-        /// 通知接收者
-        /// </summary>
-        public string rid { get; set; }
-        /// <summary>
-        /// 当rid是学校的学生时,学校编码不能为空
-        /// </summary>
-        ///public string school { get; set; }
-        /// <summary>
-        /// 源数据的scope   school则接收者是学校的学生/teacher接收者是醍摩豆账号
-        /// </summary>
-        public string scope { get; set; }
-        /// <summary>
         /// 创建时间
         /// </summary>
         public long ctime { get; set; }

+ 53 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/Snode.cs

@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Models.Cosmos.Common
+{
+    /// <summary>
+    /// 课纲节点父类
+    /// </summary>
+    public  class Snode
+    {  
+        /// <summary>
+        /// 试题 ,试卷,内容资源,外部链接(只有link,和name),
+        /// </summary>
+        public int type { get; set; }
+        public string title { get; set; }
+
+    }
+    /// <summary>
+    /// 分支节点
+    /// </summary>
+    public class Tnode : Snode
+    {
+        [Required(ErrorMessage = "{0} 必须填写")]
+
+        public string id { get; set; } /// <summary>
+                                       /// 父级
+                                       /// </summary>
+        [Required(ErrorMessage = "{0} 必须填写")]
+
+        public string pid { get; set; }
+        /// <summary>
+        /// 排序
+        /// </summary>
+        [Required(ErrorMessage = "{0} 必须填写")]
+
+        public int order { get; set; }
+        public List<string> points { get; set; } = new List<string> { "" };
+        public List<Rnode> rnodes { get; set; }
+        //public string code { get; set; }
+
+    }
+    /// <summary>
+    /// 资源节点
+    /// </summary>
+    public class Rnode : Snode
+    {
+        public string id { get; set; }
+        public string code { get; set; }
+        public List<string> link { get; set; }
+    }
+}

+ 3 - 1
TEAMModelOS.SDK/Models/Cosmos/Common/Syllabus.cs

@@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations;
 using System.Text;
 using TEAMModelOS.SDK.Context.Attributes.Azure;
 using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Models.Cosmos.Common;
 
 namespace TEAMModelOS.SDK.Models
 {
@@ -20,6 +21,7 @@ namespace TEAMModelOS.SDK.Models
       //  [PartitionKey]
       //  public string volumeCode { get; set; }
 
-        public List<SyllabusNode> children { get; set; }
+        public List<Tnode> children { get; set; }
+
     }
 }

+ 90 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/Volume.cs

@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Models.Cosmos.Common
+{
+    public class Volume: CosmosEntity
+    { 
+        /// <summary>
+        /// 0默认教学课纲的册别 1个人或单独的专题课纲册别 2,系统课纲
+        /// </summary>
+       // public int type { get; set; }
+        /// <summary>
+        /// 学段
+        /// </summary>
+
+        public string periodId { get; set; }
+
+
+        /// <summary>
+        /// 学科
+        /// </summary>
+
+        public string subjectId { get; set; }
+
+
+        /// <summary>
+        /// 年级
+        /// </summary>
+
+        public string gradeId { get; set; }
+
+
+        /// <summary>
+        /// 学期code
+        /// </summary>
+
+        public string semesterId { get; set; }
+
+
+        /// <summary>
+        /// 状态
+        /// </summary>
+
+        public int status { get; set; } = 1;
+
+
+        /// <summary>
+        /// 册别name
+        /// </summary>
+        [Required(ErrorMessage = "{0} 必须填写")]
+        public string name { get; set; }
+
+
+        /// <summary>
+        /// 册别code
+        /// </summary>
+      //  public string volumeCode { get; set; }
+
+        /// <summary>
+        /// 创建者醍摩豆id
+        /// </summary>
+       
+        public string creatorId { get; set; }
+
+        /// <summary>
+        /// 共编使用者 的醍摩豆id
+        /// </summary>
+        public List<string> editors { get; set; }
+
+        //public int resourceCount { get; set; }
+        //public int itemCount { get; set; }
+        /// <summary>
+        /// 发布层级 类型 school  teacher
+        /// </summary>
+        public string owner { get; set; }
+        /// <summary>
+        /// 学校编码或教师tmdid
+        /// </summary>
+        [Required(ErrorMessage = "school 必须设置")]
+        public string school { get; set; }
+        public bool repeat { get; set; }
+        /// <summary>
+        /// school|private
+        /// </summary>
+        [Required(ErrorMessage = "scope 必须设置")]
+        public string scope { get; set; }
+    }
+}

+ 21 - 27
TEAMModelOS.SDK/Models/Cosmos/School/Class.cs

@@ -10,6 +10,9 @@ using TEAMModelOS.SDK.DI;
 
 namespace TEAMModelOS.SDK.Models
 {    
+    /// <summary>
+    /// 班级
+    /// </summary>
     public class Class : CosmosEntity
     {
         public Class()
@@ -19,19 +22,31 @@ namespace TEAMModelOS.SDK.Models
             //students = new List<StudentSimple>();
         }
         /// <summary>
-        /// 教室编号
+        /// 班级编号
         /// </summary>
         public string no { get; set; }
 
-        public float? x { get; set; }
-        public float? y { get; set; }
+       // public float? x { get; set; }
+       // public float? y { get; set; }
+       /// <summary>
+       /// 班级名称
+       /// </summary>
         public string name { get; set; }
+        /// <summary>
+        /// 教师
+        /// </summary>
         public Teachers teacher { get; set; }
+        /// <summary>
+        /// 学段
+        /// </summary>
         public string periodId { get; set; }
+        /// <summary>
+        /// 年级
+        /// </summary>
         public string gradeId { get; set; }
-        public string sn { get; set; }
-
-        //public List<StudentSimple> students { get; set; }
+        //入学年
+        public int year { get; set; }
+        //public string sn { get; set; }
         /// <summary>
         /// TBL IRS 类型区分
         /// </summary>
@@ -50,24 +65,3 @@ namespace TEAMModelOS.SDK.Models
         
     }
 }
-
-public class Point
-{
-    public float x { get; set; }
-    public float y { get; set; }
-}
-public class StudentSimple
-{
-    /// <summary>
-    /// 数据来源的分区键 如果是学校的编码 则是学校编码,如果是tmdid则是Base
-    /// </summary>
-    public string scode { get; set; }
-    public string id { get; set; }
-
-    public string name { get; set; }
-
-    public string no { get; set; }
-
-    public string groupId { get; set; }
-    public string groupName { get; set; }
-}

+ 1 - 1
TEAMModelOS.SDK/Models/Cosmos/School/ExamInfo.cs

@@ -83,7 +83,7 @@ namespace TEAMModelOS.SDK.Models
         /// TTL删除改变状态使用
         /// </summary>
         public int? status { get; set; } = 0;
-        public double average { get; set; }
+        public double average { get; set; } = -1;
         //得分率
         public double sRate { get; set; }
         //缺考人数

+ 21 - 0
TEAMModelOS.SDK/Models/Cosmos/School/Inner/Period.cs

@@ -13,7 +13,12 @@ namespace TEAMModelOS.SDK.Models
             subjects = new List<Subject>();
             semesters = new List<Semester>();
             analysis = new Analysis();
+            majors = new List<Major>();
         }
+        /// <summary>
+        /// 高职教专业
+        /// </summary>
+        public List<Major> majors { get; set; }
         public List<Grade> grades { get; set; }
         public List<Subject> subjects { get; set; }
         public List<Semester> semesters { get; set; }
@@ -30,6 +35,22 @@ namespace TEAMModelOS.SDK.Models
         /// </summary>
         public List<TimeTable> timetable { get; set; }
     }
+
+    /// <summary>
+    /// 高职教的专业
+    /// </summary>
+    public class Major
+    {
+        /// <summary>
+        /// id
+        /// </summary>
+        public string id { get; set; }
+        /// <summary>
+        /// 名称
+        /// </summary>
+        public string name { get; set; }
+    }
+
     public class Analysis
     {
         public List<ExamSimple> type { get; set; }

+ 1 - 1
TEAMModelOS.SDK/Models/Cosmos/School/Inner/Semester.cs

@@ -7,7 +7,7 @@ namespace TEAMModelOS.SDK.Models
     public class Semester
     {
         public string name { get; set; }
-        public string count { get; set; }
+        public int start { get; set; }
         public int month { get; set; }
         public int day { get; set; }
         public string id { get; set; }

+ 4 - 2
TEAMModelOS.SDK/Models/Cosmos/School/Inner/Subject.cs

@@ -8,7 +8,9 @@ namespace TEAMModelOS.SDK.Models
     {
         public string id { get; set; }
         public string name { get; set; }
-        
-
+        /// <summary>
+        ///学科类型: 通用1  学科2  专业3
+        /// </summary>
+        public int type { get; set; }
     }
 }

+ 37 - 0
TEAMModelOS.SDK/Models/Cosmos/School/Room.cs

@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Models.Cosmos
+{
+    /// <summary>
+    /// 教室,只存在于学校。
+    /// </summary>
+    public class Room : CosmosEntity
+    {
+        public Room(){
+            pk = "Room";
+        }
+        public string name { get; set; }
+        /// <summary>
+        /// 教室编号
+        /// </summary>
+        public string no { get; set; }
+        public float? x { get; set; }
+        public float? y { get; set; }
+        public string sn { get; set; }
+        /// <summary>
+        /// 教室属性,普通 /专设的教室
+        /// </summary>
+        public string openType { get; set; }
+       // public string scope { get; set; }
+        /// <summary>
+        /// TBL IRS 类型区分
+        /// </summary>
+        public string style { get; set; }
+        /// <summary>
+        /// 
+        /// </summary>
+        public string area { get; set; }
+    }
+}

+ 4 - 0
TEAMModelOS.SDK/Models/Cosmos/School/School.cs

@@ -33,6 +33,10 @@ namespace TEAMModelOS.SDK.Models
         public string picture { get; set; }
 
         public TimeZone timeZone { get; set; }
+        /// <summary>
+        /// 1 普教,2 高职教
+        /// </summary>
+        public int type { get; set; }
 
     }
 

+ 1 - 1
TEAMModelOS.SDK/TEAMModelOS.SDK.csproj

@@ -16,7 +16,7 @@
     <PackageReference Include="Azure.Messaging.ServiceBus" Version="7.1.1" />
     <PackageReference Include="Azure.Storage.Blobs.Batch" Version="12.5.1" />
     <PackageReference Include="Azure.Storage.Queues" Version="12.6.1" />
-    <PackageReference Include="ClouDASLibx" Version="1.2.4" />
+    <PackageReference Include="ClouDASLibx" Version="1.2.6" />
     <PackageReference Include="DocumentFormat.OpenXml" Version="2.12.3" />
     <PackageReference Include="HtmlAgilityPack" Version="1.11.32" />
     <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.10" />

+ 1 - 1
TEAMModelOS/ClientApp/package.json

@@ -43,7 +43,7 @@
 		"less": "^3.9.0",
 		"lodash": "^4.17.20",
 		"mockjs": "^1.0.1-beta3",
-		"mux.js": "^5.6.4",
+		"mux.js": "^5.11.0",
 		"node-fetch": "^2.6.1",
 		"oidc-client": "^1.9.1",
 		"qrcodejs2": "0.0.2",

+ 2 - 0
TEAMModelOS/ClientApp/src/api/index.js

@@ -26,6 +26,7 @@ import forgetPW from './forgetPW'
 import classroom from './classroom'
 import serviceDriveAuth from './serviceDriveAuth'
 import spaceAuth from './spaceAuth'
+import room from './room'
 
 export default {
     accessToken,
@@ -53,6 +54,7 @@ export default {
     classroom,
     serviceDriveAuth,
     spaceAuth,
+    room,
 
     // 获取登录跳转链接
     getLoginLink: function (data) {

+ 19 - 0
TEAMModelOS/ClientApp/src/api/room.js

@@ -0,0 +1,19 @@
+import { post } from '@/api/http'
+export default {
+    // 新增/修改 教室
+    RoomUpsert: function(data) {
+        return post('/school/room/upsert', data)
+    },
+    //查询教室
+    FindRoomList: function(data) {
+        return post('/school/room/find', data)
+    },
+    //根据id查询教室
+    FindRoomById: function(data) {
+        return post('/school/room/find-id', data)
+    },
+    //删除教室
+    DelRoom: function(data) {
+        return post('/school/room/delete', data)
+    }
+}

+ 99 - 13
TEAMModelOS/ClientApp/src/assets/iconfont/demo_index.html

@@ -2,7 +2,7 @@
 <html>
 <head>
   <meta charset="utf-8"/>
-  <title>IconFont Demo</title>
+  <title>iconfont Demo</title>
   <link rel="shortcut icon" href="//img.alicdn.com/imgextra/i2/O1CN01ZyAlrn1MwaMhqz36G_!!6000000001499-73-tps-64-64.ico" type="image/x-icon"/>
   <link rel="icon" type="image/svg+xml" href="//img.alicdn.com/imgextra/i4/O1CN01EYTRnJ297D6vehehJ_!!6000000008020-55-tps-64-64.svg"/>
   <link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
@@ -13,10 +13,33 @@
   <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
   <!-- 代码高亮 -->
   <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
+  <style>
+    .main .logo {
+      margin-top: 0;
+      height: auto;
+    }
+
+    .main .logo a {
+      display: flex;
+      align-items: center;
+    }
+
+    .main .logo .sub-title {
+      margin-left: 0.5em;
+      font-size: 22px;
+      color: #fff;
+      background: linear-gradient(-45deg, #3967FF, #B500FE);
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+    }
+  </style>
 </head>
 <body>
   <div class="main">
-    <h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">&#xe86b;</a></h1>
+    <h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">
+      <img width="200" src="https://img.alicdn.com/imgextra/i3/O1CN01Mn65HV1FfSEzR6DKv_!!6000000000514-55-tps-228-59.svg">
+      
+    </a></h1>
     <div class="nav-tabs">
       <ul id="tabs" class="dib-box">
         <li class="dib active"><span>Unicode</span></li>
@@ -31,6 +54,24 @@
       <div class="content unicode" style="display: block;">
           <ul class="icon_lists dib-box">
           
+            <li class="dib">
+              <span class="icon iconfont">&#xe6ac;</span>
+                <div class="name">做任务</div>
+                <div class="code-name">&amp;#xe6ac;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe629;</span>
+                <div class="name">专业课程</div>
+                <div class="code-name">&amp;#xe629;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6bd;</span>
+                <div class="name">通用</div>
+                <div class="code-name">&amp;#xe6bd;</div>
+              </li>
+          
             <li class="dib">
               <span class="icon iconfont">&#xe6f1;</span>
                 <div class="name">退出</div>
@@ -674,24 +715,20 @@
 
           <p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
           <ul>
-            <li>兼容性最好,支持 IE6+,及所有现代浏览器。</li>
             <li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
-            <li>但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。</li>
+            <li>默认情况下不支持多色,直接添加多色图标会自动去色。</li>
           </ul>
           <blockquote>
-            <p>注意:新版 iconfont 支持多色图标,这些多色图标在 Unicode 模式下将不能使用,如果有需求建议使用symbol 的引用方式</p>
+            <p>注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)</p>
           </blockquote>
           <p>Unicode 使用步骤如下:</p>
           <h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
 <pre><code class="language-css"
 >@font-face {
   font-family: 'iconfont';
-  src: url('iconfont.eot');
-  src: url('iconfont.eot?#iefix') format('embedded-opentype'),
-      url('iconfont.woff2') format('woff2'),
-      url('iconfont.woff') format('woff'),
-      url('iconfont.ttf') format('truetype'),
-      url('iconfont.svg#iconfont') format('svg');
+  src: url('iconfont.woff2?t=1620299264680') format('woff2'),
+       url('iconfont.woff?t=1620299264680') format('woff'),
+       url('iconfont.ttf?t=1620299264680') format('truetype');
 }
 </code></pre>
           <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -717,6 +754,33 @@
       <div class="content font-class">
         <ul class="icon_lists dib-box">
           
+          <li class="dib">
+            <span class="icon iconfont icon-task"></span>
+            <div class="name">
+              做任务
+            </div>
+            <div class="code-name">.icon-task
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-profession"></span>
+            <div class="name">
+              专业课程
+            </div>
+            <div class="code-name">.icon-profession
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-general"></span>
+            <div class="name">
+              通用
+            </div>
+            <div class="code-name">.icon-general
+            </div>
+          </li>
+          
           <li class="dib">
             <span class="icon iconfont icon-quit1"></span>
             <div class="name">
@@ -1679,10 +1743,8 @@
         <p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
         <p>与 Unicode 使用方式相比,具有如下特点:</p>
         <ul>
-          <li>兼容性良好,支持 IE8+,及所有现代浏览器。</li>
           <li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
           <li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
-          <li>不过因为本质上还是使用的字体,所以多色图标还是不支持的。</li>
         </ul>
         <p>使用步骤如下:</p>
         <h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
@@ -1700,6 +1762,30 @@
       <div class="content symbol">
           <ul class="icon_lists dib-box">
           
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-task"></use>
+                </svg>
+                <div class="name">做任务</div>
+                <div class="code-name">#icon-task</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-profession"></use>
+                </svg>
+                <div class="name">专业课程</div>
+                <div class="code-name">#icon-profession</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-general"></use>
+                </svg>
+                <div class="name">通用</div>
+                <div class="code-name">#icon-general</div>
+            </li>
+          
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
                   <use xlink:href="#icon-quit1"></use>

文件差異過大導致無法顯示
+ 17 - 7
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.css


二進制
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.eot


文件差異過大導致無法顯示
+ 1 - 1
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.js


+ 21 - 0
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.json

@@ -5,6 +5,27 @@
   "css_prefix_text": "icon-",
   "description": "",
   "glyphs": [
+    {
+      "icon_id": "5241258",
+      "name": "做任务",
+      "font_class": "task",
+      "unicode": "e6ac",
+      "unicode_decimal": 59052
+    },
+    {
+      "icon_id": "5924477",
+      "name": "专业课程",
+      "font_class": "profession",
+      "unicode": "e629",
+      "unicode_decimal": 58921
+    },
+    {
+      "icon_id": "7470974",
+      "name": "通用",
+      "font_class": "general",
+      "unicode": "e6bd",
+      "unicode_decimal": 59069
+    },
     {
       "icon_id": "20402185",
       "name": "退出",

文件差異過大導致無法顯示
+ 0 - 344
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.svg


二進制
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.ttf


二進制
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff


二進制
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff2


+ 16 - 6
TEAMModelOS/ClientApp/src/common/BaseLayout.vue

@@ -224,10 +224,10 @@ export default {
                     icon: 'iconfont icon-activityS',
                     name: this.$t('system.menu.scAc'),
                     router: '',
-                    // role: 'admin',
-                    // permission: 'schoolAc-read|schoolAc-upd',
                     role: 'admin',
-                    permission: '',
+                    permission: 'schoolAc-read|schoolAc-upd',
+                    // role: 'admin',
+                    // permission: '',
                     subName:'scAc',
                     child: [
                         {
@@ -236,7 +236,7 @@ export default {
                             router: '/home/schoolEvaluation',
                             tag: '',
                             role: 'admin',
-                            permission: '',
+                            permission: 'schoolAc-read|schoolAc-upd',
                             menuName: 'schoolEvaluation'
                         },
                         {
@@ -245,7 +245,7 @@ export default {
                             router: '/home/manageVote',
                             tag: '',
                             role: 'admin',
-                            permission: '',
+                            permission: 'schoolAc-read|schoolAc-upd',
                             menuName: 'manageVote'
                         },
                         {
@@ -254,7 +254,7 @@ export default {
                             router: '/home/manageQuestionnaire',
                             tag: '',
                             role: 'admin',
-                            permission: '',
+                            permission: 'schoolAc-read|schoolAc-upd',
                             menuName: 'manageQuestionnaire'
                         },
                     ]
@@ -370,6 +370,16 @@ export default {
                     permission: '',
                     child: [],
                     menuName: 'myCourse'
+                },
+                {
+                    icon: 'iconfont icon-task',
+                    name: this.$t('system.menu.taskList'),
+                    router: '/home/taskList',
+                    tag: '',
+                    role: 'teacher',
+                    permission: '',
+                    child: [],
+                    menuName: 'taskList'
                 }
             ]
         },

+ 2 - 2
TEAMModelOS/ClientApp/src/common/BaseSelectSchool.vue

@@ -2,7 +2,7 @@
 	<div class="base-school-select">
 		<div v-if="!joinSchools || !joinSchools.length">{{ $t('utils.noJoinSchool') }}</div>
 		<Dropdown @on-click="onSchoolSelect" v-else>
-			<img class="school-logo" :src="curSchool.logo || defaultLogo" />
+			<img class="school-logo" :src="curSchool.picture || defaultLogo" />
 			<a href="javascript:void(0)" :class="['base-user-post', joinSchools && joinSchools.length === 1 ? 'single-school' : '']">
 				{{ curSchool.name }}
 				<Icon type="ios-arrow-down"></Icon>
@@ -11,7 +11,7 @@
 				<div v-for="(item,index) in joinSchools" :key="index">
 					<DropdownItem :name="index">
 						<div class="school-item">
-							<img :src="item.logo || defaultLogo" alt="">
+							<img :src="item.picture || defaultLogo" alt="">
 							<span>{{ item.name }}</span>
 						</div>
 					</DropdownItem>

+ 7 - 7
TEAMModelOS/ClientApp/src/components/evaluation/AnalysisItemTable.vue

@@ -21,17 +21,17 @@
 				tableData: [],
 				dataColumns: [
 					{
-						title: '落点区域',
+						title: this.$t('totalAnalysis.sca_table_text6'),
 						key: 'areaName',
 						align: 'center'
 					},
 					{
-						title: '配分',
+						title: this.$t('totalAnalysis.ta_table_text4'),
 						key: 'score',
 						align: 'center'
 					},
 					{
-						title: '难易度',
+						title: this.$t('totalAnalysis.ta_table_text5'),
 						key: 'diff',
 						align: 'center',
 						render: function(h, params) {
@@ -39,7 +39,7 @@
 						}
 					},
 					{
-						title: '鉴别度',
+						title: this.$t('totalAnalysis.ta_table_text6'),
 						key: 'identify',
 						align: 'center',
 						render: function(h, params) {
@@ -47,12 +47,12 @@
 						}
 					},
 					{
-						title: '正答率',
+						title: this.$t('totalAnalysis.trueAnswerRate'),
 						align: 'center',
 						key: 'classScoreRate'
 					},
 					{
-						title: '高分组正答率',
+						title: this.$t('totalAnalysis.PHAnswerRate'),
 						align: 'center',
 						key: 'PH',
 						render: function(h, params) {
@@ -60,7 +60,7 @@
 						}
 					},
 					{
-						title: '低分组正答率',
+						title: this.$t('totalAnalysis.PLAnswerRate'),
 						align: 'center',
 						key: 'PL',
 						render: function(h, params) {

+ 4 - 3
TEAMModelOS/ClientApp/src/components/evaluation/ExerciseList.vue

@@ -8,9 +8,9 @@
 			<span style="margin-top: 15px; color: #808080">{{$t('evaluation.noData')}}</span>
 		</div>
 		<div class="cp-content-wrap" ref="mathJaxContainer" v-else>
-			<div class="cp-exercise-item" v-for="(item, index) of exerciseList" :key="index" @click="onQuestionToggle(index, item.id, $event)">
+			<div class="cp-exercise-item" v-for="(item, index) of exerciseList" :key="index">
 				<!-- 题干部分 -->
-				<div class="item-question">
+				<div class="item-question"  @click="onQuestionToggle(index, item.id, $event)">
 					<div>
 						<div class="item-question-order">
 							{{ pageSize * (pageNum - 1) + index + 1 }} :
@@ -22,7 +22,7 @@
 					</span>
 				</div>
 				<!-- 选项部分 -->
-				<div v-for="(option, optionIndex) in item.option" :key="optionIndex" class="item-options">
+				<div v-for="(option, optionIndex) in item.option" :key="optionIndex" class="item-options"  @click="onQuestionToggle(index, item.id, $event)">
 					<div class="item-option-content">
 						<div class="item-option-order">
 							<!-- {{ String.fromCharCode(64 + parseInt(optionIndex + 1)) }} : -->
@@ -285,6 +285,7 @@
 			 * @param id
 			 */
 			onQuestionToggle(index, id, e) {
+				console.log(e)
 				let curClassName = e.target.className;
 				if (
 					curClassName === "item-tools" ||

+ 1 - 1
TEAMModelOS/ClientApp/src/components/evaluation/OptionsTable.vue

@@ -82,7 +82,6 @@
 					if (Object.keys(n).length) {
 						let total = this.$store.state.totalAnalysis.analysisJson.all.total // 取总人数
 						let phCount = Math.floor(total * 0.27) //取高分组人数
-						console.log(n)
 						this.options.forEach(key => {
 							this.optionsData.push({
 								option: key,
@@ -94,6 +93,7 @@
 								PL: n.pl[key] ? Number((n.pl[key] / phCount) * 100).toFixed(1) + '%' : 0,
 							})
 						})
+						console.log(this.optionsData)
 					}
 				},
 				immediate: true,

文件差異過大導致無法顯示
+ 4 - 4
TEAMModelOS/ClientApp/src/components/public/frontEndMain/Index.vue


+ 9 - 0
TEAMModelOS/ClientApp/src/components/questionnaire/BaseJudge.vue

@@ -131,6 +131,15 @@
 								this.optionsContent.push(option)
 							}
 						}
+						// 选项编辑器失焦隐藏工具栏
+						editor.config.onblur = function() {
+							let allToolbars = document.getElementsByClassName('qn-option-editor')
+							for (let i = 0; i < allToolbars.length; i++) {
+								if (allToolbars[i].children.length) {
+									allToolbars[i].children[0].style.visibility = 'hidden'
+								}
+							}
+						}
 						editor.create()
 						this.optionEditors.push(editor);
 						this.$refs["singleOption" + newIndex][0].dataset.editorId = editor.id

+ 9 - 0
TEAMModelOS/ClientApp/src/components/questionnaire/BaseMultiple.vue

@@ -130,6 +130,15 @@
 								this.optionsContent.push(option)
 							}
 						}
+						// 选项编辑器失焦隐藏工具栏
+						editor.config.onblur = function() {
+							let allToolbars = document.getElementsByClassName('qn-option-editor')
+							for (let i = 0; i < allToolbars.length; i++) {
+								if (allToolbars[i].children.length) {
+									allToolbars[i].children[0].style.visibility = 'hidden'
+								}
+							}
+						}
 						editor.create()
 						this.optionEditors.push(editor);
 						this.$refs["singleOption" + newIndex][0].dataset.editorId = editor.id

+ 9 - 0
TEAMModelOS/ClientApp/src/components/questionnaire/BaseSingle.vue

@@ -125,6 +125,15 @@
 								this.optionsContent.push(option)
 							}
 						}
+						// 选项编辑器失焦隐藏工具栏
+						editor.config.onblur = function() {
+							let allToolbars = document.getElementsByClassName('qn-option-editor')
+							for (let i = 0; i < allToolbars.length; i++) {
+								if (allToolbars[i].children.length) {
+									allToolbars[i].children[0].style.visibility = 'hidden'
+								}
+							}
+						}
 						editor.create()
 						this.optionEditors.push(editor);
 						this.$refs["singleOption" + newIndex][0].dataset.editorId = editor.id

+ 2 - 14
TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseKnowledgeDetail.vue

@@ -167,6 +167,7 @@
                                     }
                                 },
                             },
+							barMaxWidth:40,
                             emphasis: {
                                 itemStyle: {
                                     color: '#ff9999'
@@ -195,7 +196,6 @@
 
             doRender(data,classIndexs) {
 				let classIndex = this.getCurClassIndex
-				console.log(classIndex)
 				// 区分全部班级以及单个班级的数据
                 this.x = data.pointList
                 this.y = classIndex === -1 ? data.stupercent.grade.map(item => (Number(item)).toFixed(2)) : data.pointList.map(pointName => data.classpercent[pointName][classIndex].toFixed(2))
@@ -223,19 +223,7 @@
             // 获取最新散点图数据
             getKnowledgeData() {
 				let curclassIndex = this.$store.state.totalAnalysis.analysisJson.subjects.map(i => i.name).indexOf(this.$store.state.totalAnalysis.currentSubject)
-                let curJson = this.echartsId === 'knowDetailBar' ? this.$store.state.totalAnalysis.analysisJson.pointLevelKey[curclassIndex].pointKey : 
-				this.$store.state.totalAnalysis.analysisJson.pointLevelKey[curclassIndex].levelKey
-				if(this.echartsId !== 'knowDetailBar'){
-					let transArr = this.$GLOBAL.EXERCISE_LEVELS()
-					curJson.pointList = curJson.pointList.map((i,index) => transArr[index])
-					for(let key in curJson.classpercent){
-						if(!isNaN(key)){
-							let newKey = transArr[+key - 1]
-							curJson.classpercent[newKey] = curJson.classpercent[key]
-							curJson.stupercent[newKey] = curJson.stupercent[key]
-						}
-					}
-				}
+                let curJson = this.$store.state.totalAnalysis.analysisJson.pointLevelKey[curclassIndex].pointKey
 				return curJson
             },
 			getAnalysisJson() {

+ 296 - 0
TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseLevelDetail.vue

@@ -0,0 +1,296 @@
+<template>
+    <div :id="echartsId" class="knowledgeBar"></div>
+</template>
+
+<script>
+    export default {
+        name: 'hello',
+        props: ['echartsId','classIndex'],
+        data() {
+            return {
+                knowledgeData: [],
+                x: [],
+                y: [],
+                activeItemIndex: 0,
+				curClassIndex:-1,
+            }
+        },
+
+        created() {
+
+        },
+
+        methods: {
+            drawLine() {
+                let _this = this
+                // 基于准备好的dom,初始化echarts实例
+                let myBar = this.$echarts.init(document.getElementById(this.echartsId))
+                // 指定图表的配置项和数据
+                var option = {
+                    tooltip: {
+                        show: true, // 是否显示提示框,默认为true
+                        trigger: 'axis', // 数据项图形触发
+                        axisPointer: {
+                            // 指示样式
+                            type: 'shadow',
+                            axis: 'auto',
+                            shadowStyle: {
+                                color: 'rgba(128,128,128,0.1)'
+                            }
+                        },
+                        padding: 15,
+                        textStyle: {
+                            // 提示框内容的样式
+                            color: '#fff'
+                        },
+                        formatter: function(value) {
+                            // console.log(value);
+                            return value[0].name + ' : ' + value[0].data + '%'
+                        }
+                    },
+                    grid: {
+                        show: false, // 是否显示直角坐标系网格
+                        top: 80, // 相对位置 top\bottom\left\right
+						left:'center',
+						width:'75%',
+                        height: 440,
+                        containLabel: true // gird 区域是否包含坐标轴的刻度标签
+                    },
+                    dataZoom: [{
+                        'show': true,
+                        'height': 10,
+                        'xAxisIndex': [
+                            0
+                        ],
+                        bottom:36,
+                        'start': 0,
+                        'end': 100,
+                        handleIcon: 'M512 497.821538m-418.264615 0a418.264615 418.264615 0 1 0 836.52923 0 418.264615 418.264615 0 1 0-836.52923 0Z',
+                        handleSize: '160%',
+                        handleStyle: {
+                            color: '#d3dee5'
+
+                        },
+                        textStyle: {
+                            color: '#fff'
+                        },
+                        borderColor: '#90979c'
+                    }, {
+                        'type': 'inside',
+                        'show': true,
+                        'height': 15,
+                        'start': 0,
+                        'end': 100
+                    }],
+                    xAxis: {
+                        show: true, // 是否显示
+                        position: 'bottom', // x轴的位置
+                        offset: 0, // x轴相对于默认位置的偏移
+                        type: 'category', // 轴类型, 默认为 'category'
+                        name: '月份', // 轴名称
+                        nameLocation: 'end', // 轴名称相对位置
+                        nameTextStyle: {
+                            color: 'transparent',
+                            padding: [5, 0, 10, -5]
+                        },
+                        nameGap: 35, // 坐标轴名称与轴线之间的距离
+                        nameRotate: 0, // 坐标轴名字旋转
+                        axisLabel: {
+                            // 坐标轴标签
+                            show: true, // 是否显示
+                            inside: false, // 是否朝内
+                            margin: 15,
+                            rotate: 60,
+                            color: '#989898' // 默认取轴线的颜色,
+                        },
+                        splitLine: {
+                            show: false,
+                            lineStyle: {
+                                color: '#4c504a'
+                            }
+                        },
+                        splitArea: {
+                            show: false // 是否显示,默认为false
+                        },
+                        data: this.x
+                    },
+                    yAxis: {
+                        show: true, // 是否显示
+                        position: 'left', // y轴位置
+                        offset: 0, // y轴相对于默认位置的偏移
+                        type: 'value', // 轴类型,默认为 ‘category’
+                        nameLocation: 'end', // 轴名称相对位置value
+                        nameTextStyle: {
+                            color: '#fff',
+                            padding: [5, 0, 0, 5] // 坐标轴名称相对位置
+                        },
+                        nameGap: 15, // 坐标轴名称与轴线之间的距离
+                        nameRotate: 270, // 坐标轴名字旋转
+                        axisLine: {
+                            show: false, // 是否显示
+                            lineStyle: {
+                                color: '#595959',
+                                width: 1,
+                                type: 'solid'
+                            }
+                        },
+                        axisLabel: {
+                            show: true, // 是否显示
+                            inside: false, // 是否朝内
+                            rotate: 0, // 旋转角度
+                            margin: 8, // 刻度标签与轴线之间的距离
+                            color: '#989898', // 默认轴线的颜色
+                            fontSize: 12,
+                            formatter: '{value} %'
+
+                        },
+                        splitLine: {
+                            show: true,
+                            lineStyle: {
+                                color: '#4c504a',
+                                width: 0.5,
+                                type: 'solid'
+                            }
+                        }
+                    },
+                    series: [
+                        {
+                            type: 'bar',
+                            animation: false,
+                            itemStyle: {
+                                color: function(params) {
+                                    var key = params.dataIndex
+                                    if (key === _this.activeItemIndex) {
+                                        return '#ff9999'
+                                    } else {
+                                        return '#1b9dff'
+                                    }
+                                },
+                            },
+							barMaxWidth:40,
+                            emphasis: {
+                                itemStyle: {
+                                    color: '#ff9999'
+                                }
+                            },
+                            data: this.y
+                        }]
+                }
+
+                // 绘制图表
+                myBar.setOption(option)
+                myBar.resize()
+                window.addEventListener('resize', function() {
+                    myBar.resize()
+                })
+
+                let that = this
+                myBar.on('click', function(params) {
+                    that.activeItemIndex = params.dataIndex
+                    that.$emit('handleItemClick', params)
+                    myBar.setOption(option)
+                })
+				
+				
+            },
+
+            doRender(data,classIndexs) {
+				let classIndex = this.getCurClassIndex
+				console.log(classIndex)
+				// 区分全部班级以及单个班级的数据
+                this.x = data.pointList
+                this.y = classIndex === -1 ? data.stupercent.grade.map(item => (Number(item)).toFixed(2)) : data.pointList.map(pointName => data.classpercent[pointName][classIndex].toFixed(2))
+                this.drawLine()
+            },
+			
+			doSubjectChange(subject){
+				let curclassIndex = this.$store.state.totalAnalysis.analysisJson.subjects.map(i => i.name).indexOf(subject)
+				let curJson = this.$store.state.totalAnalysis.analysisJson.pointLevelKey[curclassIndex].levelKey
+					let transArr = this.$GLOBAL.EXERCISE_LEVELS()
+					curJson.pointList = curJson.pointList.map((i,index) => transArr[index])
+					for(let key in curJson.classpercent){
+						if(!isNaN(key)){
+							let newKey = transArr[+key - 1]
+							curJson.classpercent[newKey] = curJson.classpercent[key]
+							curJson.stupercent[newKey] = curJson.stupercent[key]
+						}
+					}
+				return curJson
+			}
+        },
+
+        mounted() {
+            if (this.getKnowledgeData) {
+				// this.classList = [this.$t('totalAnalysis.allClasses')].concat([...new Set(this.getAnalysisJson.classes.map(item => item.className))]) // 获取班级列表
+                this.doRender(this.getKnowledgeData,this.curClassIndex)
+            }
+			
+			this.$EventBus.$on('onCollapseChange',val => {
+				const myBar = this.$echarts.init(document.getElementById(this.echartsId))
+				myBar.setOption({
+				     grid: {
+						 width: val ? '90%' : '80%'
+					 }
+				})
+			})
+        },
+
+        computed: {
+            // 获取最新散点图数据
+            getKnowledgeData() {
+				let curclassIndex = this.$store.state.totalAnalysis.analysisJson.subjects.map(i => i.name).indexOf(this.$store.state.totalAnalysis.currentSubject)
+                let curJson = this.$store.state.totalAnalysis.analysisJson.pointLevelKey[curclassIndex].levelKey
+					let transArr = this.$GLOBAL.EXERCISE_LEVELS()
+					curJson.pointList = curJson.pointList.map((i,index) => transArr[index])
+					for(let key in curJson.classpercent){
+						if(!isNaN(key)){
+							let newKey = transArr[+key - 1]
+							curJson.classpercent[newKey] = curJson.classpercent[key]
+							curJson.stupercent[newKey] = curJson.stupercent[key]
+						}
+					}
+				return curJson
+            },
+			getCurSubject(){
+				return this.$store.state.totalAnalysis.currentSubject
+			},
+			getAnalysisJson() {
+			    return this.$store.state.totalAnalysis.analysisJson
+			},
+			getCurClassIndex(){
+				return this.$store.state.totalAnalysis.curClassIndex
+			}
+        },
+		
+		watch: {
+			getCurSubject(n,o){
+				console.log(n,o)
+				this.doRender(this.doSubjectChange(n),this.curClassIndex)
+			},
+		    // getKnowledgeData: {
+		    //     handler(n,o) {
+		    //         if (n) {
+						// console.log(n,o)
+		    //             this.doRender(n,this.curClassIndex)
+		    //         }
+		    //     }
+		    // },
+			classIndex(n,o){
+				console.log('柱状图变化了', n - 1)
+				this.curClassIndex = n - 1
+				this.doRender(this.getKnowledgeData,n - 1)
+			},
+		}
+    }
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+
+    .knowledgeBar {
+        width:100%;
+        height: 600px;
+        margin: 0 auto;
+        display: block;
+    }
+</style>

+ 2 - 2
TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseLevelPie.vue

@@ -30,8 +30,8 @@
 					series: {
 						name: that.$t('totalAnalysis.le_title1'),
 						type: 'pie',
-						radius: [40, 150],
-						center: ['45%', '60%'],
+						radius: [40, 140],
+						center: ['45%', '55%'],
 						max: 100, // for funnel
 						sort: 'ascending', // for funnel
 						data: data

+ 16 - 7
TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseMyTable.vue

@@ -106,13 +106,22 @@
 
 			// 导出表格
 			exportData(type) {
+				let classList = [this.$t('totalAnalysis.allClasses')].concat([...new Set(this.$store.state.totalAnalysis.analysisJson.classes.map(item => item.className))])
+				let subjectName = null
+				let className = classList[this.$store.state.totalAnalysis.curClassIndex + 1]
+				if(this.$route.path === '/total' || this.$route.path === '/total/achievement/earlyWarning'){
+					subjectName = this.$store.state.totalAnalysis.indexSubject
+				}else{
+					subjectName = this.$store.state.totalAnalysis.currentSubject
+				}
+				
 				if (type === 3) {
 					const params = {
 						title: this.columns.map(i => i.title),
 						key: this.columns.map(i => i.key),
 						data: this.originData,
 						autoWidth: true,
-						filename: this.tableName
+						filename: this.tableName + '(' + subjectName + '、' + className + ')'
 					}
 					excel.export_array_to_excel(params)
 				} else {
@@ -171,10 +180,10 @@
 					const touchScore = this.$store.state.totalAnalysis.analysisJson.touchScore
 					switch (filterValue) {
 						case 1:
-							this.originData = this.tableDatas.filter(item => item.score > ipoint)
+							this.originData = this.tableDatas.filter(item => item.score >= ipoint)
 							break
 						case 2:
-							this.originData = this.tableDatas.filter(item => item.score > ipoint && item.score < (ipoint +
+							this.originData = this.tableDatas.filter(item => item.score >= ipoint && item.score < (ipoint +
 								touchScore))
 							break
 						default:
@@ -348,10 +357,10 @@
 							},
 							style: {
 								cursor: 'pointer',
-								background: row.score > (ipoint + touchScore) ? '#209a31' : row.score > ipoint ?
+								background: row.score >= (ipoint + touchScore) ? '#209a31' : row.score >= ipoint ?
 									'#00767d' : 'transparent'
 							}
-						}, row.score > (ipoint + touchScore) ? that.$t('totalAnalysis.myTable.inner') : row.score >
+						}, row.score >= (ipoint + touchScore) ? that.$t('totalAnalysis.myTable.inner') : row.score >=
 						ipoint ? that.$t('totalAnalysis.myTable.outer') : ''),
 					h('span', {
 						domProps: {
@@ -388,7 +397,7 @@
 					style: {
 						textAlign: 'left'
 					}
-				}, list.map(function(item, index) {
+				}, list.sort((a,b) => a - b).map(function(item, index) {
 					return h('span', {
 						style: {
 							fontSize: '16px',
@@ -418,7 +427,7 @@
 				let that = this
 				const row = params.row
 				const list = params.row.carefulList ? params.row.carefulList.split(',') : []
-				return h('span', list.map(function(item, index) {
+				return h('span', list.sort((a,b) => a - b).map(function(item, index) {
 					return h('span', {
 						style: {
 							fontSize: '16px',

+ 1 - 1
TEAMModelOS/ClientApp/src/components/student-analysis/total/BasePie.vue

@@ -30,7 +30,7 @@
                     series: {
                         name: that.$t('totalAnalysis.ka_title1'),
                         type: 'pie',
-                        radius: [40, 150],
+                        radius: [40, 140],
                         center: ['45%', '50%'],
                         max: 100, // for funnel
                         sort: 'ascending', // for funnel

+ 11 - 2
TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseRadar.vue

@@ -44,8 +44,8 @@
                                 padding: [13, 15]
                             }
                         },
-                        center: ['50%', '62%'],
-						radius:'60%',
+                        center: ['50%', '45%'],
+						radius:'55%',
                         tooltip: {
                             trigger: 'item'
                         },
@@ -117,12 +117,16 @@
 
                 // 绘制图表
                 myRadar.setOption(option)
+				window.addEventListener('resize', function() {
+				    myRadar.resize()
+				})
             },
 
             doRender(data,classIndexs) {
 				let classIndex = this.getCurClassIndex
                 let indicator = []
 				let rateArr = classIndex === -1 ? data.stupercent.grade.map(item => (Number(item)).toFixed(2)) : data.pointList.map(pointName => data.classpercent[pointName][classIndex].toFixed(2))
+				console.log(data)
 				// 获取最大值 +5 设置到雷达边界最大值
 				let maxNum = Math.max(...rateArr.map(i => Number(i))) + 5
                 data.pointList.forEach(item => {
@@ -166,6 +170,11 @@
         },
 		
 		watch:{
+			getKnowledgeData:{
+				handler(n,o){
+					this.doRender(this.getKnowledgeData,this.curClassIndex)
+				}
+			},
 			classIndex(n,o){
 				console.log('雷达图变化了', n - 1)
 				this.curClassIndex = n - 1

+ 1 - 1
TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseScatter.vue

@@ -29,7 +29,7 @@
 				analysisJson.forEach(item => {
 					let arr2 = []
 					arr2.push((+item.x).toFixed(2))
-					arr2.push((item.y * 100).toFixed(2))
+					arr2.push((item.y))
 					arr2.push(item.name)
 					arr2.push(item.memberId)
 					newArr.push(arr2)

+ 12 - 9
TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseScoreRateBar.vue

@@ -198,16 +198,19 @@
 				let sRateArr = isAll ? list.map(i => i.sRate) : list.map(i => i.subjects[this.subjectIndex].sRate)
 				let splitArr = [...new Array(10).keys()]
 				let result = new Array(10).fill(0)
-				console.log(sRateArr)
 				sRateArr.forEach(rate => {
-					splitArr.forEach((j, index) => {
-						if(rate === 100){
-							result[9]++
-						}else if(rate.toString().slice(0, 1) === j.toString()) {
-							result[index]++
-						}
-						
-					})
+					if(rate === 100){
+						result[9]++
+					}else if(rate < 10){
+						result[0]++
+					}else{
+						splitArr.forEach((j, index) => {
+							if(rate.toString().slice(0, 1) === j.toString()) {
+								result[index]++
+							}
+						})
+					}
+					
 				})
 				this.scoreRateCountArr = result
 				this.drawLine()

+ 5 - 2
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReportCharts/StudentScore.vue

@@ -305,11 +305,14 @@
                     objItem: [],
                     subItem: [],
                 }
+                console.log('5636123322',data)
                 if (data !== undefined) {
+                    let codes = this.$store.getters.getItemTitle.scope == 'school' ? data.school : data.creatorId
                         let code = {
                             scope: data.scope,
-                            code: this.$store.getters.getItemTitle.scope == 'school' ? data.school : data.creatorId,
-                            blob: data.blob
+                            code: codes,
+                            blob: data.blob,
+                            examId:codes
                         }
                     let papers = await this.$evTools.getStuPaper(code)
                     let answer = await this.getItem(ans)

+ 6 - 3
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperView.vue

@@ -181,15 +181,18 @@
                 }
             },
             async getPaper(data) {
+                console.log('321651231',data)
                 this.isExamDown = false
                 this.isLoad = true
                 this.selectData = {}
                 this.chooseData = {}
+                let codes = this.$store.getters.getItemTitle.scope == 'school' ? data.school : data.creatorId
                 if (data.blob !== undefined && data.blob !== "") {
                     let code = {
                         scope: data.scope,
-                        code: this.$store.getters.getItemTitle.scope == 'school' ? data.school : data.creatorId,
-                        blob: data.blob
+                        code: codes,
+                        blob: data.blob,
+                        examId: codes
                     }
                     let exam = {}
                     for (let item of this.examData) {
@@ -197,7 +200,7 @@
                             exam = item
                         }
                     }
-                    if (exam.id == undefined) {
+                    if (exam.id == undefined) { 
                         this.selectData = await this.$evTools.getStuPaper(code)
                         this.examData.push(this.selectData)
                     } else {

+ 9 - 0
TEAMModelOS/ClientApp/src/components/vote/BaseVoteForm.vue

@@ -352,6 +352,7 @@
 			optionClick(index) {
 				console.log(index)
 				let allToolbars = document.getElementsByClassName('option-editor')
+				console.log(allToolbars)
 				let that = this
 				for (let i = 0; i < allToolbars.length; i++) {
 					allToolbars[i].children[0].style.visibility = 'hidden'
@@ -401,6 +402,14 @@
 								this.voteOptionsContent.push(option)
 							}
 						}
+						editor.config.onblur = function() {
+							let allToolbars = document.getElementsByClassName('option-editor')
+							for (let i = 0; i < allToolbars.length; i++) {
+								if (allToolbars[i].children.length) {
+									allToolbars[i].children[0].style.visibility = 'hidden'
+								}
+							}
+						}
 						editor.create()
 					})
 				} else {

+ 1 - 1
TEAMModelOS/ClientApp/src/css/disabled-iview-form.less

@@ -36,7 +36,7 @@
     .ivu-input[disabled] {
         border: none;
         background: none !important;
-        font-size: 16px !important;
+        font-size: 14px !important;
     }
 
     .ivu-select-disabled .ivu-select-selection {

+ 4 - 0
TEAMModelOS/ClientApp/src/css/site.css

@@ -169,6 +169,10 @@ audio::-internal-media-controls-overflow-button {
 	vertical-align: middle;
 }
 
+.richText-audio audio{
+	width: auto;
+}
+
 .richText-audio .audio-info {
 	padding: 8px;
 	background-color: #f1f3f4;

+ 255 - 249
TEAMModelOS/ClientApp/src/locale/lang/en-US/learnActivity.js

@@ -1,262 +1,268 @@
-export default {
+export default{
     //MgtSchoolEva.vue
-    mgtScEv: {
-        listLabel: '評測清單',
-        period: '學段:',
-        create: '新建',
-        delete: '删除',
-        edit: '編輯',
-        createTime: '施測時間:',
-        pending: '待發佈',
-        going: '進行中',
-        finish: '已結束',
-        endTime: '結束時間:',
-        stop: '立即結束',
-        evType: '測試類型:',
-        stuCount: '施測人數:',
-        nodata: '暫無評測',
-        tab1: '評測數據',
-        tab2: '評測試卷',
-        autoTips1: '此功能僅用於展示情景快速類比學生作答數據,且學生作答為隨機生成,僅供參考!',
-        autoTips2: '此功能僅用於展示情景快速類比教師評分數據,且分數為隨機生成,僅供參考!',
-        autoScore: '一鍵評分',
-        autoAnswer: '一鍵作答',
-        evSubject: '測試科目:',
-        returnTop: '返回頂部',
-        mockOk: '類比成功',
-        mockErr: '類比失敗',
-        stopTitle: '結束評測',
-        stopContent: '結束後學生將不能繼續作答,是否立即結束',
-        stopOk: '評測已結束!',
-        deleteTitle: '删除評測',
-        deleteContent: '是否確認删除',
-        deleteOk: '删除成功',
-        deleteErr: '删除失敗',
-        noJoin: '此帳號暫未加入任何學校!',
-        myEv: '我發佈的評測',
-        teaEv: '任課老師發佈的評測',
-        markSetting: '閱卷配寘',
-        markData: '閱卷數據'
+    mgtScEv:{
+        listLabel:'评测列表',
+        period:'学段:',
+        create:'新建',
+        delete:'删除',
+        edit:'编辑',
+        createTime:'施测时间:',
+        pending:'待发布',
+        going:'进行中',
+        finish:'已结束',
+        endTime:'结束时间:',
+        stop:'立即结束',
+        evType:'测试类型:',
+        stuCount:'施测人数:',
+        nodata:'暂无评测',
+        tab1:'评测数据',
+        tab2:'评测试卷',
+        autoTips1:'此功能仅用于展示情景快速模拟学生作答数据,且学生作答为随机生成,仅供参考!',
+        autoTips2:'此功能仅用于展示情景快速模拟教师评分数据,且分数为随机生成,仅供参考!',
+        autoScore:'一键评分',
+        autoAnswer:'一键作答',
+        evSubject:'测试科目:',
+        returnTop:'返回顶部',
+        mockOk:'模拟成功',
+        mockErr:'模拟失败',
+        stopTitle:'结束评测',
+        stopContent:'结束后学生将不能继续作答,是否立即结束',
+        stopOk:'评测已结束!',
+        deleteTitle:'删除评测',
+        deleteContent:'是否确认删除',
+        deleteOk:'删除成功',
+        deleteErr:'删除失败',
+        noJoin:'此账号暂未加入任何学校!',
+        myEv:'我发布的评测',
+        teaEv:'任课老师发布的评测',
+        markSetting:'阅卷设置',
+        markData:'阅卷数据',
+        ftStatus:'状态:',
+        ftType:'类型:',
+        ftMode:'模式:',
+        search:'搜索'
     },
 
-    //創建評測校本/個人
-    createEv: {
-        createLabel: '創建評測',
-        publishEv: '發佈評測',
-        return: '返回上級',
-        baseInfo: '基礎資訊',
-        evName: '評測名稱',
-        evPeriod: '測試學段',
-        evMode: '評測模式',
-        evType: '評測類型',
-        examType: '考試類別',
-        courseType: '課程類別',
-        cusLabel1: '校本課程',
-        cusLabel2: '個人課程',
-        evTarget: '施測對象',
-        publishType: '發佈管道',
-        startTime: '開始時間',
-        sTimeHolder: '請選擇開始時間',
-        endTime: '結束時間',
-        eTimeHolder: '請選擇結束時間',
-        addSubject: '添加學科',
-        noSubject: '暫無科目,請添加科目',
-        noSubject1: '*請先選擇測試學段',
-        papersLabel: '試卷庫',
-        perviewLabel: '試卷預覽',
-        importLabel: '導入說明',
-        answerPreview: '作答預覽',
-        errTips1: '評測名稱不能為空!',
-        errTips2: '測試類型不能為空!',
-        errTips3: '評量模式不能為空!',
-        errTips4: '請設定類別!',
-        errTips5: '請設定測試類型!',
-        errTips6: '請設定測試對象!',
-        errTips7: '請設定發佈管道!',
-        errTips8: '請設定發佈時間!',
-        errTips9: '請設定結束時間!',
-        errTips10: '請設定測試學段!',
-        stPaperTitle: '挑選試卷',
-        stPaperContent: '是否確認挑選',
-        inDev: '功能正在開發中,敬請期待!',
-        formWarning: '請先完善評測基礎資訊!',
-        paperWarning: '請挑選或導入試卷!',
-        paperWarning1: '還沒有試卷,請挑選或導入試卷!',
-        pdWarning: '請添加測試科目!',
-        publishOk: '評測發佈成功!',
-        togglePeriod: '切換學段',
-        togglePdTip1: '您已添加',
-        togglePdTip2: '的測試科目,如果現在切換學段會清空已選科目,確定切換學段嗎?',
-        toggleOkText: '切換',
-        delPdTitle: '删除科目',
-        delPdContent: '是否確認删除',
-        delOk: '删除成功',
-        pdTips: '請先選擇測試學段!',
-        defaultPaper: '(請先挑選或導入試卷)',
+    //CreateEv
+    createEv:{
+        createLabel:'创建评测',
+        publishEv:'发布评测',
+        return:'返回上级',
+        baseInfo:'基础信息',
+        evName:'评测名称',
+        evPeriod:'测试学段',
+        evMode:'评测模式',
+        evType:'评测类型',
+        examType:'考试类别',
+        courseType:'课程类别',
+        cusLabel1:'校本课程',
+        cusLabel2:'个人课程',
+        evTarget:'施测对象',
+        publishType:'发布方式',
+        startTime:'开始时间',
+        sTimeHolder:'请选择开始时间',
+        endTime:'结束时间',
+        eTimeHolder:'请选择结束时间',
+        addSubject:'添加学科',
+        noSubject:'暂无科目,请添加科目',
+        noSubject1:'* 请先选择测试学段',
+        papersLabel:'试卷库',
+        perviewLabel:'试卷预览',
+        importLabel:'导入说明',
+        answerPreview:'作答预览',
+        errTips1:'评测名称不能为空!',
+        errTips2:'测试类型不能为空!',
+        errTips3:'评量模式不能为空!',
+        errTips4:'请设置类别!',
+        errTips5:'请设置测试类型!',
+        errTips6:'请设置测试对象!',
+        errTips7:'请设置发布方式!',
+        errTips8:'请设置发布时间!',
+        errTips9:'请设置结束时间!',
+        errTips10:'请设置测试学段!',
+        stPaperTitle:'挑选试卷',
+        stPaperContent:'是否确认挑选',
+        inDev:'功能正在开发中,敬请期待!',
+        formWarning:'请先完善评测基础信息!',
+        paperWarning:'请挑选或导入试卷!',
+        paperWarning1:'还没有试卷,请挑选或导入试卷!',
+        pdWarning:'请添加测试科目!',
+        publishOk:'评测发布成功!',
+        togglePeriod:'切换学段',
+        togglePdTip1:'您已添加',
+        togglePdTip2:'的测试科目,如果现在切换学段会清空已选科目,确定切换学段吗?',
+        toggleOkText:'切换',
+        delPdTitle:'删除科目',
+        delPdContent:'是否确认删除',
+        delOk:'删除成功',
+        pdTips:'请先选择测试学段!',
+        defaultPaper:'(请先挑选或导入试卷)',
     },
-    //ManualPaper.vue
-    manual: {
-        source: '來源:',
-        sourceP: '個人試卷庫',
-        sourceS: '校本試卷庫',
-        pdLabel: '學段:',
-        subjectLabel: '學科:',
-        gradeLabel: '年級:',
-        fitPd: '適用學段:',
-        fitSubject: '適用科目:',
-        quCount: '題量:',
-        useCount: '使用次數:',
-        stdPaper: '已選試卷',
-        stPaper: '選擇試卷',
-        previewPaper: '預覽試卷',
-        noPaper: '暫無對應的試卷',
-        noPaper1: '暫無試卷'
+    // ManualPaper.vue
+    manual:{
+        source:'来源:',
+        sourceP:'个人试卷库',
+        sourceS:'校本试卷库',
+        pdLabel:'学段:',
+        subjectLabel:'学科:',
+        gradeLabel:'年级:',
+        fitPd:'适用学段:',
+        fitSubject:'适用科目:',
+        quCount:'题量:',
+        useCount:'使用次数:',
+        stdPaper:'已选试卷',
+        stPaper:'选择试卷',
+        previewPaper:'预览试卷',
+        noPaper:'暂无对应的试卷',
+        noPaper1:'暂无试卷'
     },
-    //PaperScore.vue、Scoring.vue
-    score: {
-        stuName: '學生姓名:',
-        score: '總分:',
-        scoreUnit: '分',
-        saveScore: '保存打分',
-        showAns: '顯示答案',
-        hideAns: '隱藏答案',
-        showQu: '顯示題幹',
-        hideQu: '隱藏題幹',
-        quIndex: '題號:',
-        quIndex1: '題號',
-        fullScore: '滿分',
-        zeroScore: '零分',
-        zeroScore1: '0分',
-        stuAns: '【學生作答】',
-        mark: '批註',
-        quAns: '【答ㅤ案】',
-        noAnswer: '未設定答案',
-        anaLabel: '【解ㅤ析】',
-        noAna: '暫無解析',
-        kdLabel: '【知識點】',
-        noKd: '暫未綁定知識點',
-        sQuLabel1: '【小題',
-        sQuLabel2: '】',
-        quIndexLabel: '【題號',
-        nextStu: '下一位學生>>>',
-        markOk: '批註成功!',
-        markErr: '批註失敗!',
-        isFullTips: '已經是小題滿分了',
-        isZeroTips: '已經是零分了',
-        saveScoreOk: '成績保存成功!',
-        saveSocreErr: '成績保存失敗!',
-        zero: '零',
-        one: '一',
-        two: '二',
-        three: '三',
-        four: '四',
-        five: '五',
-        six: '六',
-        seven: '七',
-        eight: '八',
-        nine: '九',
-        ten: '十',
-        hundred: '百',
-        thousand: '千',
-        tenThd: '萬',
-        hMillion: '億',
-        noStuAns: '未作答',
-        noStuAns1: '暫未作答',
-        trueAns: '正確',
-        falseAns: '錯誤',
+    // PaperScore.vue、Scoring.vue
+    score:{
+        stuName:'学生姓名:',
+        score:'总分:',
+        scoreUnit:'分',
+        saveScore:'保存打分',
+        showAns:'显示答案',
+        hideAns:'隐藏答案',
+        showQu:'显示题干',
+        hideQu:'隐藏题干',
+        quIndex:'题号:',
+        quIndex1:'题号',
+        fullScore:'满分',
+        zeroScore:'零分',
+        zeroScore1:'0分',
+        stuAns:'【学 生 作 答】',
+        mark:'批注',
+        quAns:'【答ㅤ案】',
+        noAnswer:'未设置答案',
+        anaLabel:'【解ㅤ析】',
+        noAna:'暂无解析',
+        kdLabel:'【知识点】',
+        noKd:'暂未绑定知识点',
+        sQuLabel1:'【小题',
+        sQuLabel2:'】',
+        quIndexLabel:'【题号',
+        nextStu:'下一位学生>>>',
+        markOk:'批注成功!',
+        markErr:'批注失败!',
+        isFullTips:'已经是小题满分了',
+        isZeroTips:'已经是零分了',
+        saveScoreOk:'成绩保存成功!',
+        saveSocreErr:'成绩保存失败!',
+        zero:'零',
+        one:'一',
+        two:'二',
+        three:'三',
+        four:'四',
+        five:'五',
+        six:'六',
+        seven:'七',
+        eight:'八',
+        nine:'九',
+        ten:'十',
+        hundred:'百',
+        thousand:'千',
+        tenThd:'万',
+        hMillion:'亿',
+        noStuAns:'未作答',
+        noStuAns1:'暂未作答',
+        trueAns:'正确',
+        falseAns:'错误',
         //Scoring.vue
-        subjectLabel: '學科:',
-        gradeLabel: '年級:',
-        classLabel: '班級:',
-        stuLabel: '學生:',
-        scoreView: '分數概覽',
-        scoring: '試卷評分',
-        classNoStu: '班級暫無學生',
-        status1: '暫未作答',
-        status2: '前往評分',
-        status3: '查看分數',
-        column1: '姓名',
-        column2: '總分',
-        column3: '狀態',
-        finishScore: '已完成所有學生作答的評分!',
-        unableScore: '學生尚未作答,無法評分',
-        stStuWarning: '請先選擇學生!',
-        lastQu: '上一',
-        nextQu: '下一',
+        subjectLabel:'学科:',
+        gradeLabel:'年级:',
+        classLabel:'班级:',
+        stuLabel:'学生:',
+        scoreView:'分数概览',
+        scoring:'试卷评分',
+        classNoStu:'班级暂无学生',
+        status1:'暂未作答',
+        status2:'前往评分',
+        status3:'查看分数',
+        column1:'姓名',
+        column2:'总分',
+        column3:'状态',
+        finishScore:'已完成所有学生作答的评分!',
+        unableScore:'学生尚未作答,无法评分',
+        stStuWarning: '请先选择学生!',
+        lastQu: '上一',
+        nextQu: '下一',
     },
 
     //SimpleAnalysis.vue
-    simple: {
-        totalPeople: '總人數',
-        missExam: '缺考數',
-        classLabel: '班級',
-        sjLabel: '學科',
-        avgScore: '平均分',
-        classStuCount: '班級人數',
-        answered: '已作答',
-        unanswer: '未作答',
-        scored: '已評分',
-        unscore: '未評分',
-        noPublish: '評測尚未發佈,暫無統計資料',
-        calcing: '成績數據結算中,',
-        clickFresh: '點此重繪',
-        inCalc: '數據結算中,請稍後查看',
-        total: '總分',
-        avgScore: '均分統計',
-        totalLabel: '總量',
-        scoreMat: '分數段統計'
+    simple:{
+        totalPeople:'总人数',
+        missExam:'缺考数',
+        classLabel:'班级',
+        sjLabel:'学科',
+        avgScore:'平均分',
+        classStuCount:'班级人数',
+        answered:'已作答',
+        unanswer:'未作答',
+        scored:'已评分',
+        unscore:'未评分',
+        noPublish:'评测尚未发布,暂无统计数据',
+        calcing:'成绩数据结算中,',
+        clickFresh:'点此刷新',
+        inCalc:'数据结算中,请稍后查看',
+        total:'总分',
+        avgScore:'均分统计',
+        totalLabel:'总量',
+        scoreMat:'分数段统计'
     },
 
-    mark: {
-        baseSetting: '基礎設定',
-        markMode: '閱卷模式',
-        full: '完整閱卷',
-        question: '指定題目',
-        markNum: '閱卷次數',
-        allocation: '分配管道',
-        random: '隨機分配',
-        class: '按班分配',
-        scoreDiff: '分差',
-        markRole: '閱卷角色',
-        errRole: '異常處理:',
-        arb: '仲裁教師:',
-        markRole: '閱卷教師:',
-        noTea: '暫無教師',
-        remove: '移除',
-        addTea: '添加閱卷老師',
-        quDistrub: '題目分配',
-        quIndex: '題號',
-        markNum: '閱卷人數',
-        markTea: '閱卷教師',
-        stage: '活動階段',
-        test: '施測',
-        complete: '已完成',
-        scan: '掃描',
-        success: '成功',
-        fail: '失敗',
-        miss: '缺考',
-        mark: '閱卷',
-        markProg: '閱卷進度',
-        data: '資料統計',
-        dataView: '數據概覽',
-        scanOk: '掃描成功',
-        scanErr: '掃描失敗',
-        arbPaper: '仲裁試卷',
-        subData: '各科概覽',
-        assigned: '已分配',
-        unassigned: '未分配',
-        teaProgress: '教師閱卷進度',
-        schedule: '調度',
-        subject: '科目',
-        stuNum: '考試人數',
-        scanProg: '掃描進度',
-        assignStatus: '閱卷分配',
-        errDeclare: '异常申報',
-        arbDeclare: '仲裁申報',
-        markProg: '閱卷進度',
-        name: '姓名',
-        progress: '進度',
-        action: '操作'
-
+    mark:{
+        baseSetting:'基础设置',
+        markMode:'阅卷模式',
+        full:'完整阅卷',
+        question:'指定题目',
+        markNum:'阅卷次数',
+        allocation:'分配方式',
+        random:'随机分配',
+        class:'按班分配',
+        scoreDiff:'分差',
+        markRole:'阅卷角色',
+        errRole:'异常处理:',
+        arb:'仲裁教师:',
+        markRole:'阅卷教师:',
+        noTea:'暂无教师',
+        remove:'移除',
+        addTea:'添加阅卷老师',
+        quDistrub:'题目分配',
+        quIndex:'题号',
+        markNum:'阅卷人数',
+        markTea:'阅卷教师',
+        stage:'活动阶段',
+        test:'施测',
+        complete:'已完成',
+        scan:'扫描',
+        success:'成功',
+        fail:'失败',
+        miss:'缺考',
+        mark:'阅卷',
+        markProg:'阅卷进度',
+        data:'数据统计',
+        dataView:'数据概览',
+        scanOk:'扫描成功',
+        scanErr:'扫描失败',
+        arbPaper:'仲裁试卷',
+        subData:'各科概览',
+        assigned:'已分配',
+        unassigned:'未分配',
+        teaProgress:'教师阅卷进度',
+        schedule:'调度',
+        subject:'科目',
+        stuNum:'考试人数',
+        scanProg:'扫描进度',
+        assignStatus:'阅卷分配',
+        errDeclare:'异常申报',
+        arbDeclare:'仲裁申报',
+        markProg:'阅卷进度',
+        name:'姓名',
+        markTea:'阅卷老师',
+        progress:'进度',
+        action:'操作',
+        addTeaTitle:'添加阅卷老师',
+        markNum:'阅卷量'
     }
 }

+ 2 - 1
TEAMModelOS/ClientApp/src/locale/lang/en-US/system.js

@@ -34,7 +34,8 @@ export default {
         selfLearn:'自主学习',
         homework:'作业活动',
         acRecord: '活动记录',
-        cusMgt:'课程管理'
+        cusMgt:'课程管理',
+        taskList:'任务列表'
     },
     compt: {
         cusWare: '课件',

+ 10 - 3
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/learnActivity.js

@@ -35,8 +35,12 @@ export default{
         noJoin:'此账号暂未加入任何学校!',
         myEv:'我发布的评测',
         teaEv:'任课老师发布的评测',
-        markSetting:'阅卷配置',
-        markData:'阅卷数据'
+        markSetting:'阅卷设置',
+        markData:'阅卷数据',
+        ftStatus:'状态:',
+        ftType:'类型:',
+        ftMode:'模式:',
+        search:'搜索'
     },
 
     //CreateEv
@@ -255,7 +259,10 @@ export default{
         arbDeclare:'仲裁申报',
         markProg:'阅卷进度',
         name:'姓名',
+        markTea:'阅卷老师',
         progress:'进度',
-        action:'操作'
+        action:'操作',
+        addTeaTitle:'添加阅卷老师',
+        markNum:'阅卷量'
     }
 }

+ 26 - 4
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/schoolBaseInfo.js

@@ -1,7 +1,27 @@
 export default {
   // SystemSetting.vue
+  commonset:'通用设置',
+  pdMgt:'学段管理',
+  cgMgt:'学院管理',
+  updBadge:'上传校徽',
+  scType:'学校类型:',
+  scType1:'普教',
+  scType2:'高教',
+  proSetting:'专业设置',
+  curSemSta:'当前状态:',
   schoolNameLabel: '学校名称:',
-  periodSettingLabel: '学制设置',
+  addSubject:'添加学科',
+  subType:'学科类型',
+  subType1:'通用',
+  subType2:'学科',
+  subType3:'专业',
+  acType:'操作类型',
+  acType1:'选择已有通用学科',
+  acType2:'新增通用学科',
+  sltSubect:'选择学科',
+  subName:'学科名称',
+  applyPd:'应用学制',
+  periodSettingLabel: '学制',
   order: '依建立时间排序',
   semesterSetting: '学期(季)设置',
   gradeSetting: '年级设置',
@@ -57,12 +77,13 @@ export default {
   scoreUnit: '分',
   delExamTitle: '删除考试类型',
   delGradeTitle: '删除年级',
+  delMajorTitle: '删除年级',
   delSubjectTitle: '删除学科',
   delPdTitle: '删除学段',
   delCampusTitle: '删除校区',
   delSmTitle: '删除学期',
   delContent: '后与之关联的数据将不能查询,确认删除吗?',
-  campusWarning: '对不起,校区数量已到系统授权最多数量!',
+  campusWarning: '对不起,学制/学院数量已到系统授权最多数量!',
   authWarning: '您暂无此操作权限!',
   saveErr: '保存失败!',
   saveWarning: '保存提醒',
@@ -72,6 +93,7 @@ export default {
   gdNameWarning:'年级名称不能重复',
   subWarning:'学科名称不能重复',
   exWarning:'考试名称不能重复',
+  majorWarning:'专业名称不能重复',
 
   // ClassroomSetting.vue
   classroomList: '教室列表',
@@ -138,14 +160,14 @@ export default {
   single: '单一',
   mach: '大量',
   expireLabel: '序号到期日',
-  related: '已被關聯',
+  related: '已被关联',
   noEnable: '此序号尚未启用',
   onClassStu: '专科教室没有固定学生名单!',
   classNoErr: '教室编码不能为空!',
   classNoErr1: '教室编码只能由字母和数字组成!',
   classAttr1: '常规教室(有固定学生)',
   classAttr2: '专科教室(无固定学生)',
-  nameWarning: '请输入教室名称',
+  nameWarning: '请输入名称',
   typeWarning: '请设置教室属性',
   gradeWarning: '请设置年级',
   expireLabel1: '已到期',

+ 2 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/system.js

@@ -34,7 +34,8 @@ export default {
         selfLearn:'自主学习',
         homework:'作业活动',
         acRecord: '活动记录',
-        cusMgt:'课程管理'
+        cusMgt:'课程管理',
+        taskList:'任务列表'
     },
     compt: {
         cusWare: '课件',

+ 6 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/totalAnalysis.js

@@ -5,7 +5,11 @@ export default {
 		tab2:'评测数据',
 	},
 	rateLineTitle:'选项选答率分析',
+	trueAnswerRate:'正答率',
+	PHAnswerRate:'高分组正答率',
+	PLAnswerRate:'低分组正答率',
 	R1R6LineTitle:'R1-R6作答曲线',
+	noKnowPointTip:'该科目下未收集到知识点数据',
 	lostStu:'缺考人数',
 	showAnalysis:'数据分析',
 	allSubjects:'全科',
@@ -203,6 +207,7 @@ export default {
     ka_title4: '知识点得分率统计',
     ka_title5: '知识点得分详情',
     ka_title6: '错题率关系表',
+    ka_title7: '知识点得分率分布图',
 
     ka_text1: '当前知识块',
     ka_text2: '当前知识点',
@@ -240,6 +245,7 @@ export default {
     le_title4: '认知层次得分率统计',
     le_title5: '认知层次得分详情',
     le_title6: '错题率关系表',
+    le_title7: '认知层次得分率分布图',
 
     // QuestionList.vue
     ql_text1: '试卷总分',

+ 9 - 3
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/learnActivity.js

@@ -36,7 +36,11 @@ export default {
         myEv: '我發佈的評量',
         teaEv: '任課老師發佈的評量',
         markSetting: '閱卷配寘',
-        markData: '閱卷數據'
+        markData: '閱卷數據',
+        ftStatus: '狀態:',
+        ftType: '類型:',
+        ftMode: '模式:',
+        search: '蒐索'
     },
 
     //創建評量學校/個人
@@ -255,8 +259,10 @@ export default {
         arbDeclare: '仲裁申報',
         markProg: '閱卷進度',
         name: '姓名',
+        markTea:'閱卷老師',
         progress: '進度',
-        action: '操作'
-
+        action: '操作',
+        addTeaTitle: '添加閱卷老師',
+        markNum: '閱卷量'
     }
 }

+ 1 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/schoolBaseInfo.js

@@ -1,7 +1,7 @@
 export default {
   //SystemSetting.vue
   schoolNameLabel: '學校名稱:',
-  periodSettingLabel: '學制設定',
+  periodSettingLabel: '學制',
   order: '依建立時間排序',
   semesterSetting: '學期(季)設定',
   gradeSetting: '年級設定',

+ 2 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/system.js

@@ -34,7 +34,8 @@ export default {
         selfLearn: '自主學習',
         homework: '工作活動',
         acRecord: '活動記錄',
-        cusMgt: '課程管理'
+        cusMgt: '課程管理',
+        taskList:'任務清單'
     },
     compt: {
         cusWare: '教材',

+ 8 - 2
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/totalAnalysis.js

@@ -5,8 +5,12 @@ export default {
 		tab2: '評量數據',
 		noData:'暫時找不到資料!'
 	},
-	rateLineTitle: '選答率分析',
-	R1R6LineTitle: 'R1-R6作答曲線',
+	rateLineTitle:'選答率分析',
+	trueAnswerRate:'正答率',
+	PHAnswerRate:'高分組正答率',
+	PLAnswerRate:'低分組正答率',
+	R1R6LineTitle:'R1-R6作答曲線',
+	noKnowPointTip:'該科目下未收集到知識點數據',
 	lostStu: '缺考人數',
 	showAnalysis: '資料分析',
 	allSubjects: '全科',
@@ -203,6 +207,7 @@ export default {
 	ka_title4: '知識點得分率統計',
 	ka_title5: '知識點得分詳情',
 	ka_title6: '錯題率關係表',
+	ka_title7: '知識點得分率分佈圖',
 
 	ka_text1: '當前知識塊',
 	ka_text2: '當前知識點',
@@ -240,6 +245,7 @@ export default {
 	le_title4: '認知層次得分率統計',
 	le_title5: '認知層次得分詳情',
 	le_title6: '錯題率關系表',
+	le_title7: '認知層次得分率分佈圖',
 
 	// QuestionList.vue
 	ql_text1: '試卷總分',

+ 13 - 2
TEAMModelOS/ClientApp/src/router/routes.js

@@ -218,9 +218,11 @@ export const routes = [
 			path: 'system',
 			meta: {
 				middleware: ['login', 'ability:admin,schoolSetting-read|schoolSetting-upd'],
-				activeName: 'system'
+				activeName: 'system',
+				// isKeep: true
 			},
-			component: resolve => require(['@/view/schoolmgmt/SystemSetting/SystemSetting.vue'], resolve)
+			// component: resolve => require(['@/view/schoolmgmt/SystemSetting/SystemSetting.vue'], resolve) //原来的基础设置UI
+			component: resolve => require(['@/view/schoolmgmt/SystemSetting/NewSystemSetting.vue'], resolve) //调整中的UI
 		},
 		//班级教室管理
 		{
@@ -338,6 +340,15 @@ export const routes = [
 				isKeep: true
 			}
 		},
+		{
+			path: 'taskList',
+			name: 'taskList',
+			component: resolve => require(['@/view/task/index.vue'], resolve),
+			meta: {
+				activeName: 'taskList',
+				isKeep: true
+			}
+		},
 		//自定义名单管理页面
 		{
 			path: 'MgtStuList',

+ 9 - 0
TEAMModelOS/ClientApp/src/store/module/totalAnalysis.js

@@ -14,6 +14,7 @@ export default {
 
         // 真实数据部分
 		currentExam:null,
+		indexSubject:null,
         examList: null,
         subjectList: null,
         currentSubject: null,
@@ -40,6 +41,11 @@ export default {
     },
     mutations: {
 		
+		resetSubjectSelect(state){
+			state.indexSubject = null
+			state.currentSubject = null
+		},
+		
 		// 更新最新学情分析数据
 		updateAnalysisJson(state,val){
 			let pointLevelKey = []
@@ -73,6 +79,9 @@ export default {
 				})
 			})
 			val.pointLevelKey = pointLevelKey
+			state.currentSubject = null
+			state.indexSubject = null
+			state.curClassIndex = -1
 			console.log('转换后的AnalysisJson',val)
 			state.analysisJson = val
 		},

+ 3 - 2
TEAMModelOS/ClientApp/src/utils/blobTool.js

@@ -23,7 +23,8 @@ export default class BlobTool {
      * 初始化Blob,需要先调用授权API
      * @param {string} blobUrl blob地址
      * @param {string} container 容器名称
-     * @param {string} sasString 授权
+     * @param {string} sasString 授权 需要前面有 “ ? ”
+     * @param {string} scope 学校(school)/个人(private) 计算空间大小
      * */
     constructor(blobUrl, container, sasString, scope) {
         this.initBlob(blobUrl, container, sasString, scope)
@@ -185,7 +186,7 @@ export default class BlobTool {
     /**
      * 上传文件方法,带回调上传进度
      * @param {any} file 文件对象
-     * @param {any} path 文件夹路径
+     * @param {any} path 文件夹路径 只需要文件夹名称 前后都不需要加‘/’
      * @param {any} option 官方可配置项
      * @param {any} checkSize 上传时是否检查容器空间 默认需要检查大小
      * @param {any} handleSize 是否需要更新size,建议多文件上传时在外部批量处理

+ 6 - 6
TEAMModelOS/ClientApp/src/utils/public.js

@@ -661,7 +661,7 @@ export default {
 				stu.no || '-',
 				val.fScores[subjectIndex].value[index],
 				stu.subjects[subjectIndex].fieldPoint[index],
-				(stu.subjects[subjectIndex].fieldPoint[index] / val.fScores[subjectIndex].value[index])
+				val.fScores[subjectIndex].value[index] == 0 ? 0 : (stu.subjects[subjectIndex].fieldPoint[index] / val.fScores[subjectIndex].value[index])
 				.toFixed(2)
 			])
 		})
@@ -671,7 +671,7 @@ export default {
 	getLevelClassPercent(val, subjectIndex, index) {
 		let result = []
 		val.classes.forEach(classItem => {
-			result.push(classItem.subjects[subjectIndex].field.map((score,index) => Number(score / val.fieldwrong[subjectIndex].value[index][1]) * 100)[index])
+			result.push(classItem.subjects[subjectIndex].field.map((score,index) => val.fieldwrong[subjectIndex].value[index][1] == 0 ? 0 : Number(score / val.fieldwrong[subjectIndex].value[index][1]) * 100)[index])
 		})
 		return result
 	},
@@ -683,7 +683,7 @@ export default {
 			stuResult[item] = this.getLevelStuPercent(val, subjectIndex, index)
 			classResult[item] = this.getLevelClassPercent(val, subjectIndex, index)
 		})
-		stuResult.grade = val.fieldAllPer[subjectIndex].value.map((score,index) => (Number(score / val.fieldwrong[subjectIndex].value[index][1]).toFixed(2)) * 100)
+		stuResult.grade = val.fieldAllPer[subjectIndex].value.map((score,index) => (val.fieldwrong[subjectIndex].value[index][1] == 0 ? 0 : Number(score / val.fieldwrong[subjectIndex].value[index][1]).toFixed(2)) * 100)
 		stuResult.keys = val.knowKey
 		classResult.keys = val.knowkey
 		classResult.className = val.classes.map(i => i.className)
@@ -704,7 +704,7 @@ export default {
 				stu.no || '-',
 				val.kScores[subjectIndex].value[index],
 				stu.subjects[subjectIndex].point[index],
-				(stu.subjects[subjectIndex].point[index] / val.kScores[subjectIndex].value[index])
+				val.kScores[subjectIndex].value[index] == 0 ? 0 : (stu.subjects[subjectIndex].point[index] / val.kScores[subjectIndex].value[index])
 				.toFixed(2)
 			])
 		})
@@ -715,7 +715,7 @@ export default {
 		let result = []
 		val.classes.forEach(classItem => {
 			// 取当前班级在每个知识点的得分 除以知识点的总分 得到每个班在该知识点的得分率 index=>知识点下标
-			result.push(classItem.subjects[subjectIndex].point.map((score,pointIndex) => Number(score / val.wrong[subjectIndex].value[pointIndex][1]) * 100)[index])
+			result.push(classItem.subjects[subjectIndex].point.map((score,pointIndex) => val.wrong[subjectIndex].value[pointIndex][1] == 0 ? 0 : Number(score / val.wrong[subjectIndex].value[pointIndex][1]) * 100)[index])
 		})
 		return result
 	},
@@ -728,7 +728,7 @@ export default {
 			classResult[item] = this.getKnowClassPercent(val, subjectIndex, index)
 		})
 		// 取当前年级在每个知识点的得分 除以知识点的总分 得到年级在该知识点的得分率
-		stuResult.grade = val.knowAllper[subjectIndex].value.map((score,index) => Number(score / val.wrong[subjectIndex].value[index][1]) * 100)
+		stuResult.grade = val.knowAllper[subjectIndex].value.map((score,index) => val.wrong[subjectIndex].value[index][1] == 0 ? 0 : Number(score / val.wrong[subjectIndex].value[index][1]) * 100)
 		stuResult.keys = val.knowKey
 		classResult.keys = val.knowkey
 		classResult.className = val.classes.map(i => i.className)

+ 1 - 1
TEAMModelOS/ClientApp/src/view/Home.vue

@@ -186,7 +186,7 @@ body {
 
 #content {
     height: 100%;
-    /*background: #2B2B2E;*/
+    background: #242328;
     overflow: hidden;
 }
 

+ 1 - 0
TEAMModelOS/ClientApp/src/view/evaluation/bank/index.vue

@@ -71,6 +71,7 @@
 			},
 			onTabClick(val) {
 				this.currentTab = val
+				this.onBackToTop()
 			},
 			
 			onShowPaperList(){

+ 0 - 1
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseImport.vue

@@ -309,7 +309,6 @@
 				// 解析成题目列表
 				this.$api.SaveAnalyzeHtml({ html : html }).then(response => {
 					if(response.tests.length && !response.emferror.length){
-						this.$Message.success(this.$t('evaluation.importFile.warningTips3'))
 						this.$emit('importFinish',response.tests)
 						this.isImportFinish = false
 						this.isBtnLoading = false

+ 16 - 9
TEAMModelOS/ClientApp/src/view/evaluation/index/CreatePaper.vue

@@ -223,18 +223,24 @@
 				this.errorList = []
 				this.$refs.testPaper.$refs.exList.errorList = []
 				this.evaluationInfo.item = []
-				let importList = await this.refreshImportItems(list)
-				this.evaluationInfo.item = importList
-				this.activeTab = 'preview'
-				console.log(this.evaluationInfo)
-				if (this.errorList.length) {
-					this.$EventBus.$emit('importFinish', this.errorList)
+				try{
+					let importList = await this.refreshImportItems(list)
+					this.evaluationInfo.item = importList
+					this.activeTab = 'preview'
+					this.$Message.success(this.$t('evaluation.importFile.warningTips3'))
+					console.log(this.evaluationInfo)
+					if (this.errorList.length) {
+						this.$EventBus.$emit('importFinish', this.errorList)
+					}
+				}catch(e){
+					this.$Message.error(this.$t('evaluation.importFile.warningTips5'))
 				}
+				
 			},
 
 			/* 给导入的试题 补充最新的试卷学段年级以及科目信息 */
 			refreshImportItems(list) {
-				return new Promise((r, j) => {
+				return new Promise((r, reject) => {
 					let objectiveTypes = ['single', 'multiple']
 					let code = this.isSchool ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId
 					let gradeIds = this.isSchool ? this.evaluationInfo.paperGrade.length ? this.evaluationInfo.paperGrade : this.gradeList
@@ -270,9 +276,10 @@
 							})
 						}
 						// 如果导入的是客观题 则需要检测答案与选项是否为空
-						if (objectiveTypes.includes(i.type) && (!i.question.replace(/\s*/g, "") || !i.option.length || !i.answer.length ||
-								!i.option || !i.answer)) {
+						if (objectiveTypes.includes(i.type) && (!i.question.replace(/\s*/g, "") || !i.option || !i.answer ||
+								!i.option.length || !i.answer.length)) {
 							this.errorList.push(i)
+							reject(500)
 						}
 					}
 					r(list)

+ 3 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.less

@@ -233,4 +233,7 @@
 }
 .mock-tea-scoring{
     margin-right: 40px;
+}
+.filter-item{
+    margin: 10px 5px;
 }

+ 88 - 19
TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.vue

@@ -15,9 +15,41 @@
                             <DropdownItem :name="item.id">{{ item.name }}</DropdownItem>
                         </DropdownMenu>
                     </Dropdown>
-                    <Icon type="md-add" class=" to-create-icon" @click="goToCreate" :title="$t('learnActivity.mgtScEv.create')" v-if="$access.can('admin.*|schoolAc-upd')" />
-                    <Icon type="md-trash" v-show="evaListShow.length" class="to-create-icon" :title="$t('learnActivity.mgtScEv.delete')" @click="deleteEvaluation" v-if="$access.can('admin.*|schoolAc-upd')" />
-                    <Icon type="md-create" v-show="evaListShow.length && evaListShow[curEvaIndex] && evaListShow[curEvaIndex].progress == 'pending'" class="to-create-icon" @click="editEvaluation" :title="$t('learnActivity.mgtScEv.edit')" v-if="$access.can('admin.*|schoolAc-upd')" />
+                    <div style="float:right" v-if="!isSearch">
+                        <Icon type="md-add" class=" to-create-icon" @click="goToCreate" :title="$t('learnActivity.mgtScEv.create')" v-if="$access.can('admin.*|schoolAc-upd')" />
+                        <Icon type="md-trash" v-show="evaListShow.length" class="to-create-icon" :title="$t('learnActivity.mgtScEv.delete')" @click="deleteEvaluation" v-if="$access.can('admin.*|schoolAc-upd')" />
+                        <Icon type="md-create" v-show="evaListShow.length && evaListShow[curEvaIndex] && evaListShow[curEvaIndex].progress == 'pending'" class="to-create-icon" @click="editEvaluation" :title="$t('learnActivity.mgtScEv.edit')" v-if="$access.can('admin.*|schoolAc-upd')" />
+                        <!-- 筛选 -->
+                        <Poptip style="float:right" trigger="hover" :offset="-10" theme="light">
+                            <Icon type="ios-funnel" class="to-create-icon" :color="filter.status || filter.type || filter.mode ? '#2d8cf0':'#fff'" />
+                            <div slot="content">
+                                <div class="filter-item">
+                                    <span>{{$t('learnActivity.mgtScEv.ftStatus')}}</span>
+                                    <Select v-model="filter.status" style="width:100px" size="small" clearable @on-change="filterEv">
+                                        <Option value="pending">{{$t('learnActivity.mgtScEv.pending')}}</Option>
+                                        <Option value="going">{{$t('learnActivity.mgtScEv.going')}}</Option>
+                                        <Option value="finish">{{$t('learnActivity.mgtScEv.finish')}}</Option>
+                                    </Select>
+                                </div>
+                                <div class="filter-item">
+                                    <span>{{$t('learnActivity.mgtScEv.ftType')}}</span>
+                                    <Select v-model="filter.type" style="width:100px" size="small" clearable @on-change="filterEv">
+                                        <Option v-for="(item,index) in $GLOBAL.EV_TYPE()" :value="item.value" :key="index">{{ item.label }}</Option>
+                                    </Select>
+                                </div>
+                                <div class="filter-item">
+                                    <span>{{$t('learnActivity.mgtScEv.ftMode')}}</span>
+                                    <Select v-model="filter.mode" style="width:100px" size="small" clearable @on-change="filterEv">
+                                        <Option v-for="(item,index) in $GLOBAL.EV_MODE()" :value="item.value" :key="index">{{ item.label }}</Option>
+                                    </Select>
+                                </div>
+                            </div>
+                        </Poptip>
+                        <Icon type="md-search" class="to-create-icon" @click="isSearch = !isSearch" :title="$t('learnActivity.mgtScEv.search')" />
+                    </div>
+                    <div v-else class="dark-iview-input" style="float:right;width:calc(100% - 150px);padding-right:10px;">
+                        <Input icon="ios-close" v-model="keyword" :placeholder="$t('schoolBaseInfo.codeSearchHolder')" autofocus style="width:100%" @on-click="closeKeySearch" @on-change="filterByName" />
+                    </div>
                 </div>
                 <div class="evaluation-list-main">
                     <vuescroll>
@@ -63,11 +95,17 @@
                     <span :class="curBarIndex == 1 ? 'evalustion-bar-item line-bottom-active line-bottom':'evalustion-bar-item line-bottom'" @click="selectBar(1)">
                         {{$t('learnActivity.mgtScEv.tab2')}}
                     </span>
-                    <span v-show="evaListShow[curEvaIndex] && evaListShow[curEvaIndex].source == '2'" :class="curBarIndex == 2 ? 'evalustion-bar-item line-bottom-active line-bottom':'evalustion-bar-item line-bottom'" @click="selectBar(2)">
+                    <!-- <span v-show="evaListShow[curEvaIndex] && evaListShow[curEvaIndex].source == '2'" :class="curBarIndex == 2 ? 'evalustion-bar-item line-bottom-active line-bottom':'evalustion-bar-item line-bottom'" @click="selectBar(2)">
                         {{$t('learnActivity.mgtScEv.markSetting')}}
                     </span>
                     <span v-show="evaListShow[curEvaIndex] && evaListShow[curEvaIndex].source == '2'" :class="curBarIndex == 3 ? 'evalustion-bar-item line-bottom-active line-bottom':'evalustion-bar-item line-bottom'" @click="selectBar(3)">
                         {{$t('learnActivity.mgtScEv.markData')}}
+                    </span> -->
+                    <span :class="curBarIndex == 2 ? 'evalustion-bar-item line-bottom-active line-bottom':'evalustion-bar-item line-bottom'" @click="selectBar(2)">
+                        {{$t('learnActivity.mgtScEv.markSetting')}}
+                    </span>
+                    <span :class="curBarIndex == 3 ? 'evalustion-bar-item line-bottom-active line-bottom':'evalustion-bar-item line-bottom'" @click="selectBar(3)">
+                        {{$t('learnActivity.mgtScEv.markData')}}
                     </span>
                     <!--取消一键作答和一键评分功能-->
                     <!--<div style="float:right;" v-if="$access.ability('admin','mock-eva').validateAll" v-show="evaListShow[curEvaIndex] && evaListShow[curEvaIndex].progress == 'going'">
@@ -134,6 +172,8 @@ export default {
     inject: ['reload'],
     data() {
         return {
+            keyword: '',
+            isSearch: false,
             answerLoading: false,
             scoreLoading: false,
             split1: 0.2,
@@ -150,10 +190,51 @@ export default {
             filterPeriod: undefined,
             schoolBase: {
                 period: []
+            },
+            filter: {
+                status: '',
+                mode: '',
+                type: ''
             }
         }
     },
     methods: {
+        filterByName() {
+            let curPdEv = this.evaluationList.filter(item => item.period.id === this.filterPeriod)
+            this.evaListShow = curPdEv.filter(item => {
+                return item.name.indexOf(this.keyword) > -1
+            })
+
+        },
+        closeKeySearch() {
+            this.isSearch = false
+            this.keyword = ''
+            this.filterByName()
+        },
+        //根据学段筛选评测
+        filterByPeriod() {
+            this.curEvaIndex = 0
+            sessionStorage.setItem('evPeriod', this.filterPeriod)
+            if (this.filterPeriod) {
+                this.evaListShow = this.evaluationList.filter(item => item.period.id === this.filterPeriod)
+            } else {
+                this.evaListShow = [...this.evaluationList]
+            }
+            if (this.evaListShow.length) {
+                this.selectEvaluation(0)
+            }
+        },
+
+        //筛选评测
+        filterEv() {
+            let curPdEv = this.evaluationList.filter(item => item.period.id === this.filterPeriod)
+            this.evaListShow = curPdEv.filter(item => {
+                let status = !this.filter.status || (this.filter.status == item.progress)
+                let type = !this.filter.type || (this.filter.type == item.type)
+                let mode = !this.filter.mode || (this.filter.type == item.source)
+                return status && type && mode
+            })
+        },
         // 模拟教师评分数据
         mockScoring() {
             this.scoreLoading = true
@@ -377,25 +458,12 @@ export default {
                         }
 
                     } else {
-                        this$Message.error('API ERROR!')
+                        this.$Message.error('API ERROR!')
                     }
                 }
             )
         },
-        //根据学段筛选评测
-        filterByPeriod() {
-            this.curEvaIndex = 0
-            sessionStorage.setItem('evPeriod', this.filterPeriod)
-            if (this.filterPeriod) {
-                this.evaListShow = this.evaluationList.filter(item => item.period.id === this.filterPeriod)
-            } else {
-                this.evaListShow = [...this.evaluationList]
-            }
-            if (this.evaListShow.length) {
-                this.selectEvaluation(0)
-            }
 
-        },
         selectEvaluation(index) {
             this.curSubIndex = 0
             this.curEvaIndex = index
@@ -421,7 +489,8 @@ export default {
                         for (let index in resData.papers) {
                             let blob = resData.papers[index].blob
                             resData.papers[index].examScope = resData.scope
-                            resData.papers[index].examId = resData.id
+                            // resData.papers[index].examId = resData.id
+                            resData.papers[index].examId = resData.scope === 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId //试卷容器名字
                             resData.papers[index] = await this.$evTools.getFullPaper(resData.papers[index])
                             if (!resData.papers[index].subjectId) {
                                 resData.papers[index].subjectId = blob.substring(blob.lastIndexOf('/') + 1)

+ 3 - 3
TEAMModelOS/ClientApp/src/view/learnactivity/PaperScore.vue

@@ -76,10 +76,10 @@
                             </div>
                             <!-- 加分 减分 -->
                             <div style="display:flex;justify-content: space-evenly;margin-top:5px;">
-                                <span class="fast-score-tag" @click="scoreUp((getScoreIndex(typeIndex,index)),item.score)">
+                                <span class="fast-score-tag" @click="scoreUp(item.index,item.score)">
                                     <Icon type="md-add" />
                                 </span>
-                                <span class="fast-score-tag" @click="scoreDown(getScoreIndex(typeIndex,index))" style="background:#ed4014">
+                                <span class="fast-score-tag" @click="scoreDown(item.index)" style="background:#ed4014">
                                     <Icon type="md-remove" />
                                 </span>
                             </div>
@@ -202,7 +202,7 @@
                                 </div>
                                 <!-- 加分 减分 -->
                                 <div style="display:flex;justify-content: space-evenly;margin-top:5px;">
-                                    <span class="fast-score-tag" @click="scoreUp(childItem.index,item.score)">
+                                    <span class="fast-score-tag" @click="scoreUp(childItem.index,childItem.score)">
                                         <Icon type="md-add" />
                                     </span>
                                     <span class="fast-score-tag" @click="scoreDown(childItem.index)" style="background:#ed4014">

+ 18 - 15
TEAMModelOS/ClientApp/src/view/learnactivity/markpaper/MarkData.vue

@@ -70,7 +70,7 @@
                         <template slot-scope="{ row }" slot="progress">
                             <div style="display: flex;justify-content: center;">
                                 <i-circle :percent="20" :size="40" :stroke-width="8" :trail-width="7" stroke-color="#ff5500">
-                                    <span>20%</span>
+                                    <span style="font-size:12px">20%</span>
                                 </i-circle>
                             </div>
                         </template>
@@ -207,7 +207,7 @@ export default {
                     align: 'center'
                 },
                 {
-                    title: this.$t('learnActivity.mark.name'),
+                    title: this.$t('learnActivity.mark.markTea'),
                     key: 'name',
                     align: 'center'
                 },
@@ -303,7 +303,9 @@ export default {
 }
 
 .setting-content {
-    background: #404042;
+    background: #2b2a2f;
+    box-shadow: 1px 1px 13px #141414;
+    border: 1px solid rgba(98, 97, 101, 0.4);
     padding: 15px 10px;
     border-radius: 5px;
     margin-right: 12px;
@@ -315,8 +317,8 @@ export default {
     padding-right: 15px;
 }
 .block-title {
-    color: white;
-    border-left: 2px solid #1cc0f3;
+    color: rgb(190, 190, 190);
+    // border-left: 2px solid #1cc0f3;
     line-height: 12px;
     padding-left: 4px;
     user-select: none;
@@ -356,14 +358,6 @@ export default {
 .count-content {
     display: flex;
 }
-.chart-wrap {
-    flex: 1;
-    color: #fff;
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    justify-content: center;
-}
 .mark-step {
     padding: 0px 40px 0px 20px;
 }
@@ -372,14 +366,14 @@ export default {
 .step-content {
     .ivu-steps .ivu-steps-title,
     .ivu-steps .ivu-steps-head {
-        background: #404042;
+        background: #2b2a2f;
     }
     .ivu-steps-item.ivu-steps-status-finish .ivu-steps-title,
     .ivu-steps-item.ivu-steps-status-process .ivu-steps-title {
         color: white;
     }
     .ivu-steps-item.ivu-steps-status-finish .ivu-steps-head-inner {
-        background-color: #404042;
+        background-color: #2b2a2f;
         border-color: #1cc0f3;
     }
     .ivu-steps-item.ivu-steps-status-finish .ivu-steps-tail > i:after {
@@ -395,4 +389,13 @@ export default {
         color: white;
     }
 }
+.mark-data-wrap .ivu-table:before{
+    height: 0px;
+}
+.sub-info-table .ivu-table td{
+    border-color: #323232;
+}
+.sub-info-table .ivu-table-header thead tr th{
+    border-color: #323232;
+}
 </style>

+ 99 - 85
TEAMModelOS/ClientApp/src/view/learnactivity/markpaper/MarkSetting.vue

@@ -1,34 +1,17 @@
 <template>
     <div class="mark-setting-wrap dark-iview-form">
         <vuescroll>
-            <div class="setting-block">
-                <p class="block-title">
+            <!-- 基础设置 -->
+            <div class="setting-block" style="display:none">
+                <!-- <p class="block-title">
                     {{$t('learnActivity.mark.baseSetting')}}
-                </p>
+                </p> -->
                 <div class="setting-content">
                     <Form :model="setting" label-colon :label-width="80">
                         <Row>
                             <Col :md="24" :lg="24" :xl="12" :xxl="12">
-                            <FormItem :label="$t('learnActivity.mark.markMode')" class="setting-item-wrap">
-                                <RadioGroup v-model="setting.mode">
-                                    <Radio :label="$t('learnActivity.mark.full')"></Radio>
-                                    <Radio :label="$t('learnActivity.mark.question')"></Radio>
-                                </RadioGroup>
-                            </FormItem>
-                            </Col>
-                            <Col :md="24" :lg="24" :xl="12" :xxl="12">
-                            <FormItem :label="$t('learnActivity.mark.markNum')" class="setting-item-wrap">
-                                <InputNumber :max="2" :min="1" v-model="setting.num"></InputNumber>
-                            </FormItem>
-                            </Col>
-                        </Row>
-                        <Row>
-                            <Col :md="24" :lg="24" :xl="12" :xxl="12">
-                            <FormItem :label="$t('learnActivity.mark.allocation')" class="setting-item-wrap">
-                                <RadioGroup v-model="setting.mode">
-                                    <Radio :label="$t('learnActivity.mark.random')"></Radio>
-                                    <Radio :label="$t('learnActivity.mark.class')"></Radio>
-                                </RadioGroup>
+                            <FormItem label="阅卷次数" class="setting-item-wrap">
+                                <InputNumber :max="2" :min="1" v-model="setting.point"></InputNumber>
                             </FormItem>
                             </Col>
                             <Col :md="24" :lg="24" :xl="12" :xxl="12">
@@ -42,13 +25,14 @@
             </div>
 
             <div class="setting-block">
-                <p class="block-title">
+                <!-- <p class="block-title">
                     {{$t('learnActivity.mark.markRole')}}
-                </p>
+                </p> -->
                 <div class="setting-content">
                     <Row>
                         <Col :md="24" :lg="24" :xl="12" :xxl="12">
-                        <div class="role-item-wrap">
+                        <!-- 异常处理老师 -->
+                        <div class="role-item-wrap" style="display:none">
                             <span class="role-label">{{$t('learnActivity.mark.errRole')}}</span>
                             <Select v-model="setting.err" style="width:400px" multiple>
                                 <Option value="beijing">New York</Option>
@@ -58,7 +42,8 @@
                         </div>
                         </Col>
                         <Col :md="24" :lg="24" :xl="12" :xxl="12">
-                        <div class="role-item-wrap">
+                        <!-- 仲裁老师 -->
+                        <div class="role-item-wrap" style="display:none">
                             <span class="role-label">{{$t('learnActivity.mark.arb')}}</span>
                             <Select v-model="setting.err1" style="width:400px" multiple>
                                 <Option value="beijing">New York</Option>
@@ -68,6 +53,7 @@
                         </div>
                         </Col>
                     </Row>
+                    <!-- 阅卷老师设置 -->
                     <div class="role-item-wrap dark-iview-table">
                         <span class="role-label" style="vertical-align: top;">{{$t('learnActivity.mark.markRole')}}</span>
                         <div class="scan-tea-table">
@@ -78,8 +64,11 @@
                                 <template slot-scope="{ row }" slot="action">
                                     <Button type="error" size="small">{{$t('learnActivity.mark.remove')}}</Button>
                                 </template>
+                                <template slot-scope="{ row }" slot="num">
+                                    <span>{{`${row.num}/24`}}</span>
+                                </template>
                             </Table>
-                            <span class="add-tea-btn">
+                            <span class="add-tea-btn" @click="addTeaStatus = true">
                                 <Icon type="md-add" />
                                 {{$t('learnActivity.mark.addTea')}}
                             </span>
@@ -87,10 +76,11 @@
                     </div>
                 </div>
             </div>
-            <div class="setting-block">
-                <p class="block-title">
+            <!-- 题号分配 -->
+            <div class="setting-block" style="display:none">
+                <!-- <p class="block-title">
                     {{$t('learnActivity.mark.quDistrub')}}
-                </p>
+                </p> -->
                 <div class="setting-content dark-iview-table">
                     <Table :columns="quCol" :data="quData" border :no-data-text="$t('learnActivity.mark.noTea')">
                         <template slot-scope="{ row }" slot="quNo">
@@ -106,6 +96,16 @@
                 </div>
             </div>
         </vuescroll>
+        <Modal v-model="addTeaStatus" :title="$t('learnActivity.mark.addTeaTitle')" class-name="dark-iview-modal dark-iview-table" @on-ok="okAddTea" :width="1000">
+            <Table ref="sltTea" :columns="teacherCol" :data="teacherList" style="margin-top:10px" @on-selection-change="(selection)=>{sltTeachers = selection}" height="600">
+                <template slot-scope="{ row }" slot="picture">
+                    <PersonalPhoto :name="row.name" :picture="row.picture" />
+                </template>
+                <template slot-scope="{ row }" slot="job">
+                    <span>{{row.job || '--'}}</span>
+                </template>
+            </Table>
+        </Modal>
     </div>
 </template>
 <script>
@@ -125,6 +125,37 @@ export default {
                 point: 2,//分差(仲裁条件)
                 quRule: [],//题目分配规则
             },
+            addTeaStatus: false,
+            teacherList: [],
+            sltTeachers: [],
+            teacherCol: [
+                {
+                    type: 'selection',
+                    width: 60,
+                    align: 'center'
+                },
+                {
+                    title: ' ',
+                    slot: 'picture',
+                    align: 'center ',
+                    width: '120'
+                },
+                {
+                    title: this.$t('cusMgt.teaName'),
+                    key: 'name',
+                    align: 'center '
+                },
+                {
+                    title: 'id',
+                    key: 'id',
+                    align: 'center '
+                },
+                {
+                    title: this.$t('cusMgt.job'),
+                    slot: 'job',
+                    align: 'center '
+                }
+            ],
             teaCol: [
                 {
                     title: ' ',
@@ -132,18 +163,18 @@ export default {
                     align: 'center'
                 },
                 {
-                    title: '姓名',
-                    key: 'name',
+                    title: 'id',
+                    key: 'id',
                     align: 'center'
                 },
                 {
-                    title: '手机',
-                    key: 'phone',
+                    title: this.$t('learnActivity.mark.name'),
+                    key: 'name',
                     align: 'center'
                 },
                 {
-                    title: '阅卷量',
-                    key: 'num',
+                    title: this.$t('learnActivity.mark.markNum'),
+                    slot: 'num',
                     align: 'center'
                 },
                 {
@@ -152,23 +183,7 @@ export default {
                     align: 'center'
                 },
             ],
-            teaData: [
-                {
-                    name: '测试老师',
-                    phone: '13096300695',
-                    num: '100/563'
-                },
-                {
-                    name: '测试老师',
-                    phone: '13096300695',
-                    num: '100/563'
-                },
-                {
-                    name: '测试老师',
-                    phone: '13096300695',
-                    num: '100/563'
-                }
-            ],
+            teaData: [],
             quCol: [
                 {
                     title: this.$t('learnActivity.mark.quIndex'),
@@ -199,33 +214,6 @@ export default {
                         'jefftest',
                         '李芷萱',
                     ]
-                },
-                {
-                    quNo: [4, 5, 6],
-                    teachers: [
-                        'JK',
-                        '尹航',
-                        '向奕然',
-                        '高嘉妍',
-                        '刘雨菡',
-                        'jefftest',
-                        '李芷萱',
-                        '刘雨菡',
-                        'jefftest',
-                        '李芷萱',
-                    ]
-                },
-                {
-                    quNo: [7, 8],
-                    teachers: [
-                        'JK',
-                        '尹航',
-                        '向奕然',
-                        '高嘉妍',
-                        '刘雨菡',
-                        'jefftest',
-                        '李芷萱',
-                    ]
                 }
             ]
         }
@@ -234,10 +222,34 @@ export default {
         MarkProgress, CptCount, ScanProgress, PersonalPhoto
     },
     methods: {
-
+        //确认添加老师
+        okAddTea() {
+            let ids = this.teaData.map(item => {
+                return item.id
+            })
+            this.sltTeachers.forEach(item => {
+                if (ids.indexOf(item.id) == -1) {
+                    this.teaData.push(item)
+                }
+            })
+            this.teaData.map(item => {
+                item.num = 24 / this.teaData.length
+            })
+            this.cancel()
+        },
+        cancel() {
+            this.sltTeachers = []
+            this.$refs.sltTea.selectAll(false)
+        },
+        
     },
     created() {
-
+        this.$store.dispatch('teachers/getTeacherList').then(res => {
+            this.teacherList = this.$store.state.teachers.teacherList.filter(item => {
+                return item.status == 'join'
+            })
+            console.log(this.teacherList)
+        })
     }
 }
 </script>
@@ -252,7 +264,9 @@ export default {
 }
 
 .setting-content {
-    background: #404042;
+    background: #2b2a2f;
+    box-shadow: 1px 1px 13px #141414;
+    // border: 1px solid rgba(98, 97, 101, 0.4);
     padding: 10px;
     border-radius: 5px;
     margin-right: 12px;
@@ -317,7 +331,7 @@ export default {
     .role-label {
         color: #a5a5a5;
         display: inline-block;
-        width: 120px;
+        // width: 120px;
         text-align: right;
         padding-right: 10px;
     }

+ 17 - 98
TEAMModelOS/ClientApp/src/view/learnactivity/markpaper/MarkView.vue

@@ -1,9 +1,11 @@
 <template>
+    <!-- 未做多语言 -->
     <div class="mark-area">
         <!-- 头部基础信息 -->
         <div class="mark-header" v-show="markMode == 'paper'">
             <span class="quit-marking-text">
-                <Icon custom="iconfont icon-quit2" class="quit-marking-icon" title="退出阅卷" @click="test"/>
+                <!-- <Icon custom="iconfont icon-quit2" class="quit-marking-icon" title="退出阅卷" @click="test"/> -->
+                <Icon type="ios-arrow-back" class="quit-marking-icon" title="退出阅卷" @click="test" />
                 <!-- 退出阅卷 -->
             </span>
             <span class="info-label">考试名称:</span>
@@ -17,31 +19,15 @@
             <div class="btn-wrap">
                 <!-- <Button type="text" custom-icon="iconfont icon-exception" class="action-btn" @click="test">异常申报</Button> -->
                 <span class="action-btn">
-                    <Icon custom="iconfont icon-exception" class="action-btn-icon"/>
+                    <Icon custom="iconfont icon-exception" class="action-btn-icon" />
                     异常申报
                 </span>
             </div>
         </div>
-        <!-- 头部基础信息 -->
-        <!-- <div class="mark-header" v-show="markMode == 'qu'">
-            <span class="info-label">考试名称:</span>
-            <span class="info-value">一年级数学期末考试</span>
-            <span class="info-label">考号:</span>
-            <span class="info-value">140015511064</span>
-            <span class="info-label">当前题号:</span>
-            <span class="info-value">{{quIndex}}</span>
-            <span class="info-label">试卷类型:</span>
-            <span class="info-value">正常卷</span>
-            <div class="btn-wrap">
-                <Button icon="md-arrow-round-back" class="action-btn" @click="test">退出阅卷</Button>
-            </div>
-        </div> -->
         <div class="mark-main">
             <!-- 工具条 -->
             <div class="mark-tools-wrap">
                 <Icon custom="iconfont icon-move" class="tool-icon" title="移动" @click="move" />
-                <!-- <Icon custom="iconfont icon-larger" class="tool-icon" title="放大" @click="larger" />
-                <Icon custom="iconfont icon-smaller" class="tool-icon" title="缩小" @click="smaller" /> -->
                 <Icon custom="iconfont icon-text" class="tool-icon" title="文字批注" @click="drawText" />
                 <Icon custom="iconfont icon-mark" class="tool-icon" title="画笔" @click="drawLine" />
                 <Icon custom="iconfont icon-arrow-mark" class="tool-icon" title="箭头" @click="drawArrow" />
@@ -55,9 +41,7 @@
                 </Poptip>
                 <Icon custom="iconfont icon-fresh" class="tool-icon" title="清除批注" @click="clear" />
                 <Icon custom="iconfont icon-review" class="tool-icon" title="重阅" />
-                <Icon :custom="isFull ? 'iconfont icon-cancel-full' : 'iconfont icon-full-screen'" class="tool-icon" :title="isFull ? '取消全屏' : '全屏'" @click="togglefull"/>
-                <!-- <Icon custom="iconfont icon-exception" class="tool-icon exception-icon" title="异常申报" color="#ed4014" /> -->
-                <!-- <Icon v-for="(item,index) in iconList" :key="index" :custom="item.icon" :class="['tool-icon', activeIcon == index ? 'tool-icon-active':'']" :title="item.title" @click="(item.click)(index)" :color="item.color" /> -->
+                <Icon :custom="isFull ? 'iconfont icon-cancel-full' : 'iconfont icon-full-screen'" class="tool-icon" :title="isFull ? '取消全屏' : '全屏'" @click="togglefull" />
             </div>
             <div class="mark-stage">
                 <vuescroll ref="canvasScroll">
@@ -116,76 +100,7 @@ import Konva from 'konva'
 export default {
     name: 'Home',
     data() {
-        let _this = this
         return {
-            iconList: [
-                {
-                    icon: 'iconfont icon-move',
-                    title: '还原比例',
-                    click: _this.move,
-                    color: ''
-                },
-                {
-                    icon: 'iconfont icon-text',
-                    title: '文字批注',
-                    click: _this.drawText,
-                    color: ''
-                },
-                {
-                    icon: 'iconfont icon-mark',
-                    title: '画笔',
-                    click: _this.drawLine,
-                    color: ''
-                },
-                {
-                    icon: 'iconfont icon-arrow-mark',
-                    title: '箭头',
-                    click: _this.drawArrow,
-                    color: ''
-                },
-                {
-                    icon: 'iconfont icon-oval',
-                    title: '椭圆',
-                    click: _this.drawOval,
-                    color: ''
-                },
-                {
-                    icon: 'iconfont icon-rect',
-                    title: '方框',
-                    click: _this.drawRect,
-                    color: ''
-                },
-                {
-                    icon: 'iconfont icon-smile',
-                    title: '图标',
-                    click: _this.drawIcon,
-                    color: ''
-                },
-                {
-                    icon: 'iconfont icon-fresh',
-                    title: '清除批注',
-                    click: _this.clear,
-                    color: ''
-                },
-                {
-                    icon: 'iconfont icon-review',
-                    title: '重阅',
-                    click: _this.review,
-                    color: ''
-                },
-                {
-                    icon: 'iconfont icon-cancel-full',
-                    title: '取消全屏',
-                    click: _this.cancelFull,
-                    color: ''
-                },
-                {
-                    icon: 'iconfont icon-exception',
-                    title: '异常申报',
-                    click: _this.cancelFull,
-                    color: '#ed4014'
-                }
-            ],
             activeIcon: -1,
             score: 0,
             isShowNum: true,
@@ -220,12 +135,19 @@ export default {
             imgs: [],
             curImg: undefined,
             counter: 0,
-            markMode: 'paper', //阅卷模式
-            isFull:false, //是否为全屏模式
+            markMode: 'paper', //阅卷模式 paper:按人阅卷 qu:按题阅卷
+            isFull: false, //是否为全屏模式
+            examData:{
+                name:'期末考试',
+            },
+            stuData:{
+                name:'',
+                id:'',
+            }
         }
     },
     methods: {
-        togglefull(){
+        togglefull() {
             this.$router.push({
                 name: this.isFull ? 'MarkView' : 'FullMarkView'
             })
@@ -775,9 +697,6 @@ export default {
                     case 'line':
                         this.startLine(current)
                         break
-                    // case 'img':
-                    //     this.startImg(current)
-                    //     break
                     default:
                         break
                 }
@@ -849,7 +768,7 @@ export default {
         //模拟按题阅卷和整体阅卷
         let routeData = this.$route
         // this.mode = routeData.params.type
-        if(routeData.name == 'FullMarkView') this.isFull = true
+        if (routeData.name == 'FullMarkView') this.isFull = true
         this.mode = 1
 
         //监听删除按键,删除图形
@@ -1022,7 +941,7 @@ export default {
     margin-left: 10px;
     cursor: pointer;
 }
-.action-btn-icon{
+.action-btn-icon {
     display: inline-block;
     margin-right: 5px;
 }

+ 10 - 8
TEAMModelOS/ClientApp/src/view/newcourse/TimeSetting.less

@@ -39,19 +39,20 @@
 .time-line-content{
     width:100%;
     padding-top:15px;
-    height:~"calc(100% - 45px)";
+    height:~"calc(100% - 5px)";
 }
 .time-line-item {
     display: flex;
     align-items: center;
     cursor: pointer;
-    width:338px;
-    background:rgba(100,100,100,.5);
+    width:300px;
+    background:rgb(43, 43, 46);
     padding:10px 10px;
     position:relative;
     margin-left:5px;
     color:white;
     border-radius:2px;
+    box-shadow: 0 0px 8px 1px #1e1f21;
 }
 .time-line-item-active {
     background: #808080;
@@ -64,24 +65,25 @@
 }
 .time-line-item:hover {
     background: #808080;
-    // transform: translateY(-2px);
-    // transition: all .1s;
     box-shadow: 1px 4px 5px #191919;
 }
+.time-line-item:hover::before {
+    border-bottom: 6px solid #808080 !important;
+}
 .time-line-item::before {
     position: absolute;
     content: '';
     left: -9px;
-    top: 8px;
+    top: 6px;
     border-right: 6px solid transparent;
     border-left: 6px solid transparent;
-    border-bottom: 6px solid rgba(100,100,100,.5);
+    border-bottom: 6px solid #1e1f21;
     transform: rotate(270deg);
 }
 
 .time-setting-item {
     .time {
-        font-size: 28px;
+        font-size: 22px;
         font-weight:400;
     }
 

+ 1 - 1
TEAMModelOS/ClientApp/src/view/newcourse/TimeSetting.vue

@@ -248,7 +248,7 @@ export default {
     padding: 0px;
 }
 .time-line-content .ivu-timeline-item-head {
-    background: #404040;
+    background: transparent;
 }
 .time-line-content .ivu-timeline-item-tail {
     border-color: #909090;

+ 312 - 187
TEAMModelOS/ClientApp/src/view/schoolmgmt/ClassroomSetting/ClassroomSetting.vue

@@ -2,9 +2,9 @@
     <div class="class-mgt-container">
         <Loading v-show="isListLoading"></Loading>
         <div class="class-list-wrap">
-            <!--班级列表-->
+            <!--教室列表头部-->
             <div class="class-list-header">
-                <Dropdown class="sort-dropdown" trigger="click" placement="bottom-start" @on-click="function(e){ filterPeriod = e }" @on-visible-change="dropdownStates" v-if="$store.state.user.schoolProfile.school_base">
+                <!-- <Dropdown class="sort-dropdown" trigger="click" placement="bottom-start" @on-click="function(e){ filterPeriod = e }" @on-visible-change="dropdownStates" v-if="$store.state.user.schoolProfile.school_base">
                     <span style="cursor: pointer;">
                         {{$t('schoolBaseInfo.pdLabel')}}
                         <b class="title">{{ filterPeriodName }}</b>
@@ -13,7 +13,8 @@
                     <DropdownMenu slot="list" v-for="(item,index) in periods" :value="item.id" :key="index">
                         <DropdownItem :name="item.id">{{ item.name }}</DropdownItem>
                     </DropdownMenu>
-                </Dropdown>
+                </Dropdown> -->
+                <span>{{$t('schoolBaseInfo.classroomList')}}</span>
                 <div v-if="!isSearch" style="float:right;">
                     <Icon class="action-btn-icon" type="ios-search" @click="isSearch = true" />
                     <Icon v-if="$access.can('admin.*|classroom-upd')" class="action-btn-icon" type="md-trash" @click.stop="showConfirmDelete()" />
@@ -23,6 +24,7 @@
                     <Input icon="ios-close" v-model="keyword" :placeholder="$t('schoolBaseInfo.codeSearchHolder')" autofocus style="width:100%" @on-click="closeKeySearch" @on-change="filterClassname" />
                 </div>
             </div>
+            <!-- 排序条件 -->
             <div class="class-list-filter">
                 <div class="class-list-filter-box">
                     <Dropdown class="sort-dropdown" trigger="click" placement="bottom-start" @on-click="function(e){ orderBy = e }" @on-visible-change="restOrderBy">
@@ -38,11 +40,12 @@
                     </Dropdown>
                 </div>
             </div>
+            <!-- 教室数据列表 -->
             <div class="class-list" id="class-list">
                 <vuescroll>
                     <div class="class-list-item" v-for="(item,index) in classroomListShow" :key="index" @click="chooseClassroom(index)" :class="curClassIndex == index ? 'block-bg block-bg-active':'block-bg'">
                         <div class="class-list-item-left">
-                            <p style="color: #a5a5a5;">{{$jsFn.getGradeName(schoolBase,item.gradeId)}}</p>
+                            <!-- <p style="color: #a5a5a5;">{{$jsFn.getGradeName(schoolBase,item.gradeId)}}</p> -->
                             <p class="class-name">
                                 <span class="class-id-tag">
                                     {{item.no}}
@@ -66,13 +69,14 @@
                                         {{$t('schoolBaseInfo.normalClass')}}
                                     </span>
                                 </template>
-                            <p class="second-text-color" v-if="item.openType == '1'">
+                            </p>
+                            <!-- <p class="second-text-color" v-if="item.openType == '1'">
                                 <span>{{ $t('schoolBaseInfo.headmaster') }}</span>
                                 <span class="primary-text-color">{{item.teacher.name}}</span>
                             </p>
                             <p class="second-text-color" v-else>
                                 <span class="primary-text-color">{{$t('schoolBaseInfo.dClass')}}</span>
-                            </p>
+                            </p> -->
                         </div>
                     </div>
                     <EmptyData v-if="classroomListShow.length == 0" style="padding-top:120px;"></EmptyData>
@@ -80,21 +84,23 @@
             </div>
         </div>
         <div class="class-info-wrap">
+            <!-- tab菜单 -->
             <div class="class-info-header common-save-btn">
-                <span :class="currentTabIndex == 0 ? 'main-header-tab line-bottom line-bottom-active':'main-header-tab line-bottom'" @click="selectTab(0)">
+                <!-- <span :class="currentTabIndex == 0 ? 'main-header-tab line-bottom line-bottom-active':'main-header-tab line-bottom'" @click="selectTab(0)">
                     {{$t('schoolBaseInfo.tab1')}}
-                </span>
-                <span :class="currentTabIndex == 2 ? 'main-header-tab line-bottom line-bottom-active':'main-header-tab line-bottom'" @click="selectTab(2)">
-                    {{$t('schoolBaseInfo.tab2')}}
-                </span>
+                </span> -->
+
                 <span :class="currentTabIndex == 1 ? 'main-header-tab line-bottom line-bottom-active':'main-header-tab line-bottom'" @click="selectTab(1)">
                     {{$t('schoolBaseInfo.tab3')}}
                 </span>
                 <span :class="currentTabIndex == 3 ? 'main-header-tab line-bottom line-bottom-active':'main-header-tab line-bottom'" @click="selectTab(3)">
                     {{$t('schoolBaseInfo.tab4')}}
                 </span>
+                <span :class="currentTabIndex == 2 ? 'main-header-tab line-bottom line-bottom-active':'main-header-tab line-bottom'" @click="selectTab(2)">
+                    {{$t('schoolBaseInfo.tab2')}}
+                </span>
                 <!-- 编辑教室 -->
-                <Button v-if="$access.can('admin.*|classroom-upd')" class="save-btn" :loading="isSaveLoading" :disabled="!updated" icon="ios-albums-outline" @click="saveClassroom()" v-show="currentTabIndex == 0">
+                <Button v-if="$access.can('admin.*|classroom-upd')" class="save-btn" :loading="isSaveLoading" :disabled="!updated" icon="ios-albums-outline" @click="saveClassroom()" v-show="currentTabIndex == 1">
                     {{$t('schoolBaseInfo.saveInfo')}}
                 </Button>
                 <!-- 添加学生 -->
@@ -107,7 +113,7 @@
                 </Button>
             </div>
             <div class="class-info-content">
-                <!--基础信息-->
+                <!--基础信息 基础信息很少,暂时去掉-->
                 <div v-show="currentTabIndex == 0" style="display:flex;flex-direction:row;width:100%;height:100%;">
                     <!--班级属性-->
                     <div class="class-attr-wrap disabled-iview-select dark-iview-select">
@@ -121,33 +127,32 @@
                                     <span slot="label" class="class-attr-wrap-label">{{$t('schoolBaseInfo.classroomCode')}}</span>
                                     <Input @on-change="watchUpdate" :disabled="editStatus" v-model="classroomListShow[curClassIndex].no" clearable :placeholder="$t('schoolBaseInfo.classroomCodeHolder')" />
                                 </FormItem>
-                                <FormItem prop="gradeId" :label="$t('schoolBaseInfo.setGrade')" @click.native.stop class="requird-color" v-if="classroomListShow[curClassIndex].openType == '1'">
+                                <!-- <FormItem prop="gradeId" :label="$t('schoolBaseInfo.setGrade')" @click.native.stop class="requird-color" v-if="classroomListShow[curClassIndex].openType == '1'">
                                     <span slot="label" class="class-attr-wrap-label">{{$t('schoolBaseInfo.setGrade')}}</span>
                                     <Select @on-change="watchUpdate" :disabled="editStatus" v-model="classroomListShow[curClassIndex].gradeId" clearable>
                                         <Option v-for="(item,index) in $jsFn.getPeriod($store.state.user.schoolProfile.school_base,classroomListShow[curClassIndex].periodId).grades" :value="item.id" :key="index">
                                             {{ item.name }}
                                         </Option>
                                     </Select>
-                                </FormItem>
+                                </FormItem> -->
                                 <FormItem prop="openType" @click.native.stop class="requird-color">
                                     <span slot="label" class="class-attr-wrap-label">{{$t('schoolBaseInfo.classAttr')}}</span>
                                     <Select @on-change="watchUpdate" :disabled="editStatus" v-model="classroomListShow[curClassIndex].openType" clearable>
                                         <Option v-for="(item,index) in attributeList" :value="item.value" :key="index">{{ item.label }}</Option>
                                     </Select>
                                 </FormItem>
-                                <FormItem prop="teacher" :label="$t('schoolBaseInfo.headmaster')" @click.native.stop class="requird-color" v-if="classroomListShow[curClassIndex].openType == '1'">
+                                <!-- <FormItem prop="teacher" :label="$t('schoolBaseInfo.headmaster')" @click.native.stop class="requird-color" v-if="classroomListShow[curClassIndex].openType == '1'">
                                     <span slot="label" class="class-attr-wrap-label">{{$t('schoolBaseInfo.headmaster')}}</span>
                                     <Select @on-change="watchUpdate" :disabled="editStatus" v-model="classroomListShow[curClassIndex].teacher.id" clearable filterable>
                                         <Option v-for="(item,index) in $store.state.teachers.teacherList.filter(item=>item.status == 'join')" :value="item.id" :key="index" @click.native="classroomListShow[curClassIndex].teacher = {id:item.id, name:item.name}">
                                             {{ item.name }}
                                         </Option>
                                     </Select>
-                                </FormItem>
+                                </FormItem> -->
                             </Form>
                         </vuescroll>
                     </div>
                 </div>
-              
 
                 <!-- 關聯 Hiteach -->
                 <div id="hiteach-link" style="height: 100%;" v-show="currentTabIndex == 3">
@@ -178,7 +183,7 @@
                             </div>
                         </div>
                         <div class="hiteach-code-wrap-list">
-                            <vuescroll >
+                            <vuescroll>
                                 <div class="hiteach-collapse" v-for="(item, index) in showHiteachData" :key="index">
                                     <div @click="openHiSub(index)" class="hiteach-collapse-main">
                                         <Icon class="arrowIcon" v-if="item.deviceMax > 1" :type="item.subOpen ? 'ios-arrow-up' : 'ios-arrow-down'" size="30" color="white" />
@@ -211,7 +216,7 @@
                                         <template v-if="item.deviceBound.length >0">
                                             <div v-for="(dbItem, dbIndex) in item.deviceBound" :key="dbIndex" class="hiteach-collapse-sub gradient" :class="{'linked': dbItem.classId != '' && dbItem.classId != null && classroomListShow[curClassIndex].id != dbItem.classId}">
                                                 <!-- 被關聯 disabled -->
-                                                <Button class="hiteach-collapse-sub-linkBtn" v-model="dbItem.hiteachLink" @click="confirmLink(dbItem.hiteachLink, item.id, dbItem.uuid, dbItem.uuid2, classroomListShow[curClassIndex].id)" >{{ dbItem.hiteachLink ? $t('schoolBaseInfo.deLink') : $t('schoolBaseInfo.link') }}</Button>
+                                                <Button class="hiteach-collapse-sub-linkBtn" v-model="dbItem.hiteachLink" @click="confirmLink(dbItem.hiteachLink, item.id, dbItem.uuid, dbItem.uuid2, classroomListShow[curClassIndex].id)">{{ dbItem.hiteachLink ? $t('schoolBaseInfo.deLink') : $t('schoolBaseInfo.link') }}</Button>
                                                 <!-- <Checkbox :id="'checkbox-' + (dbItem.uuid + dbItem.uuid2)" @on-change="whatUpdHiLink(dbItem, classroomListShow[curClassIndex].id, item.id)" style="margin-right: 70px;" v-model="dbItem.hiteachLink" :disabled="dbItem.classId != '' && dbItem.classId != null && classroomListShow[curClassIndex].id != dbItem.classId"></Checkbox> -->
                                                 <div class="hiteach-collapse-sub-detail">
                                                     <ul>
@@ -314,6 +319,25 @@
         <Modal v-model="addStuStatus" :title="$t('schoolBaseInfo.addStuBtn')" width="1200" @on-ok="confirmAddStu" class-name="dark-iview-modal">
             <StudentList @getSelectInfo="(selction)=>{selections = selction}"></StudentList>
         </Modal>
+        <Modal v-model="addRoomStatus" :title="$t('schoolBaseInfo.addClassroom')" @on-ok="confirmAddRoom" class-name="dark-iview-modal dark-iview-form">
+            <Form ref="classInfo" :model="room" :rules="classValidate">
+                <FormItem prop="name" :label="$t('schoolBaseInfo.classroomName')" @click.native.stop class="requird-color">
+                    <span slot="label" class="class-attr-wrap-label">{{$t('schoolBaseInfo.classroomName')}}</span>
+                    <Input @on-change="watchUpdate" :disabled="editStatus" v-model="room.name" clearable :placeholder="$t('schoolBaseInfo.classroomNameHolder')" />
+                </FormItem>
+                <FormItem prop="no" @click.native.stop class="requird-color">
+                    <span slot="label" class="class-attr-wrap-label">{{$t('schoolBaseInfo.classroomCode')}}</span>
+                    <Input @on-change="watchUpdate" :disabled="editStatus" v-model="room.no" clearable :placeholder="$t('schoolBaseInfo.classroomCodeHolder')" />
+                </FormItem>
+                <FormItem prop="openType" @click.native.stop class="requird-color">
+                    <span slot="label" class="class-attr-wrap-label">{{$t('schoolBaseInfo.classAttr')}}</span>
+                    <Select @on-change="watchUpdate" :disabled="editStatus" v-model="room.openType" clearable>
+                        <Option v-for="(item,index) in attributeList" :value="item.value" :key="index">{{ item.label }}</Option>
+                    </Select>
+                </FormItem>
+            </Form>
+        </Modal>
+
     </div>
 </template>
 
@@ -344,6 +368,13 @@ export default {
             }
         }
         return {
+            room: {
+                id: '',
+                name: '',
+                no: '',
+                openType: ''
+            },
+            addRoomStatus: false,
             studentColumn: [
                 {
                     title: ' ',
@@ -413,7 +444,7 @@ export default {
             editStatus: true,//可切换编辑状态
             noStatus: false,
             isSearch: false,
-            currentTabIndex: 0,
+            currentTabIndex: 1,
             isListLoading: false,
             isSaveLoading: false,
             hiteachListLoading: true,
@@ -528,12 +559,12 @@ export default {
             let data = this.hiteachData
             let filterText = this.filterHiteachVer
             let nowSeletedClassId = null
-            if(this.classroomListShow.length > 0){
+            if (this.classroomListShow.length > 0) {
                 nowSeletedClassId = this.classroomListShow[this.curClassIndex].id
             }
 
-            if(nowSeletedClassId != null && this.isSmart(nowSeletedClassId)){
-                
+            if (nowSeletedClassId != null && this.isSmart(nowSeletedClassId)) {
+
                 // 篩選有 deviceBound 且classID 和現在的教室相同的
                 data = data.filter(item => {
                     return Array.isArray(item.deviceBound) && item.deviceBound.length > 0 && item.deviceBound.some(s => { return s.classId == nowSeletedClassId })
@@ -542,7 +573,7 @@ export default {
                 // 整形 拿掉不相關的
                 data = data.map(item => {
                     let newD = item
-                    newD.deviceBound = newD.deviceBound.filter( f => {
+                    newD.deviceBound = newD.deviceBound.filter(f => {
                         return f.classId != null && f.classId != ''
                     })
                     return newD
@@ -551,15 +582,15 @@ export default {
                 // 整形 拿掉沒被關聯的
                 data = data.map(item => {
                     let newD = item
-                    if(Array.isArray(item.deviceBound) && item.deviceBound.length > 0){
-                        newD.deviceBound = newD.deviceBound.filter( f => {
+                    if (Array.isArray(item.deviceBound) && item.deviceBound.length > 0) {
+                        newD.deviceBound = newD.deviceBound.filter(f => {
                             return f.classId == null || f.classId == ''
                         })
                     }
                     return newD
                 })
             }
-                    
+
 
             if (Array.isArray(data) && data.length > 0 && filterText != 'ALL') {
                 data = data.filter(item => {
@@ -610,7 +641,7 @@ export default {
                         err => {
                             this.$Message.error(this.$t('schoolBaseInfo.findStuErr'))
                         }
-                    ).finally(()=>{
+                    ).finally(() => {
                         this.stuLoading = false
                     })
                 }
@@ -637,14 +668,14 @@ export default {
             let requestParams = {
                 schoolId: this.$store.state.userInfo.schoolCode,
                 students: [],
-                grant_type:'remove'
+                grant_type: 'remove'
             }
             let names = []
             if (index > -1) {
                 requestParams.students.push(this.students[index].id)
                 names = [this.students[index].name]
             } else if (this.delSelections.length > 0) {
-                requestParams.students = this.delSelections.map(item=>{
+                requestParams.students = this.delSelections.map(item => {
                     return item.id
                 })
                 names = this.delSelections.map(item => {
@@ -659,12 +690,12 @@ export default {
                         this.$api.stuAccount.removeStudent(requestParams).then(
                             res => {
                                 this.$Message.success(this.$t('schoolBaseInfo.removeOk'))
-                                if(index > -1){
+                                if (index > -1) {
                                     this.students.splice(index, 1)
-                                }else{
-                                    for(let i = 0; i < this.students.length; i){
-                                        if(requestParams.students.indexOf(this.students[i].id) > -1){
-                                            this.students.splice(i,1)
+                                } else {
+                                    for (let i = 0; i < this.students.length; i) {
+                                        if (requestParams.students.indexOf(this.students[i].id) > -1) {
+                                            this.students.splice(i, 1)
                                             i--
                                         }
 
@@ -685,12 +716,12 @@ export default {
             if (this.selections.length > 0) {
                 console.log(this.selections)
                 let data = this._.cloneDeep(this.selections)
-                data.forEach(item=>{
+                data.forEach(item => {
                     item.classId = this.classroomListShow[this.curClassIndex].id
                     item.className = this.classroomListShow[this.curClassIndex].name
                     item.classNo = this.classroomListShow[this.curClassIndex].no
                 })
-                
+
                 this.listLoading = true
                 this.$api.stuAccount.updateStudent({
                     students: data,
@@ -1046,78 +1077,126 @@ export default {
             return false
         },
         saveClassroom() {
-            this.$refs['classInfo'].validate((valid) => {
-                if (!valid) {
-                    this.$Message.error(this.$t('schoolBaseInfo.formWarning'))
-                } else {
-                    let option = this.classroomListShow[this.curClassIndex].option
-                    if (!option) {
-                        option = 'update'
-                    } else {
-                        this.classroomListShow[this.curClassIndex]['code'] = this.$store.state.userInfo.schoolCode
-                    }
-                    delete this.classroomListShow[this.curClassIndex].option
-                    this.isSaveLoading = true
-                    this.isListLoading = true
-                    if (this.classroomListShow[this.curClassIndex].openType == '2') {
-                        this.classroomListShow[this.curClassIndex].teacher.id = ''
-                        this.classroomListShow[this.curClassIndex].teacher.name = ''
-                    }
-                    this.$api.schoolSetting.classUpsert({
-                        classroom: this.classroomListShow[this.curClassIndex],
-                        option: option,
-                        school_code: this.$store.state.userInfo.schoolCode
-                    }).then(
-                        res => {
-                            if (res) {
-                                if (res.error) {
-                                    this.classroomListShow[this.curClassIndex].option = option
-                                    this.$Message.error(res.v)
-                                } else {
-                                    this.$Message.success(this.$t('schoolBaseInfo.csTips3'))
-                                    this.updated = false
-                                    this.updHiteachLink = []
-                                    if (option == 'insert') {
-                                        this.classroomListShow[this.curClassIndex].code = 'Class-' + this.classroomListShow[this.curClassIndex].code
-                                        this.classroomList.unshift(this.classroomListShow[this.curClassIndex])
-                                        this.$store.dispatch('user/addSchoolClasses', this.classroomListShow[this.curClassIndex]);
-                                    }
-                                }
-                            } else {
-                                this.$Message.error('API error!')
+            // this.$refs['room'].validate((valid) => {
+            //     if (!valid) {
+            //         this.$Message.error(this.$t('schoolBaseInfo.formWarning'))
+            //     } else {
+            this.classroomListShow[this.curClassIndex]['code'] = this.$store.state.userInfo.schoolCode
+            this.isSaveLoading = true
+            this.isListLoading = true
+            // if (this.classroomListShow[this.curClassIndex].openType == '2') {
+            //     this.classroomListShow[this.curClassIndex].teacher.id = ''
+            //     this.classroomListShow[this.curClassIndex].teacher.name = ''
+            // }
+            // this.$api.schoolSetting.classUpsert({
+            //     classroom: this.classroomListShow[this.curClassIndex],
+            //     option: option,
+            //     school_code: this.$store.state.userInfo.schoolCode
+            // }).then(
+            //     res => {
+            //         if (res) {
+            //             if (res.error) {
+            //                 this.classroomListShow[this.curClassIndex].option = option
+            //                 this.$Message.error(res.v)
+            //             } else {
+            //                 this.$Message.success(this.$t('schoolBaseInfo.csTips3'))
+            //                 this.updated = false
+            //                 this.updHiteachLink = []
+            //                 if (option == 'insert') {
+            //                     this.classroomListShow[this.curClassIndex].code = 'Class-' + this.classroomListShow[this.curClassIndex].code
+            //                     this.classroomList.unshift(this.classroomListShow[this.curClassIndex])
+            //                     this.$store.dispatch('user/addSchoolClasses', this.classroomListShow[this.curClassIndex]);
+            //                 }
+            //             }
+            //         } else {
+            //             this.$Message.error('API error!')
+            //         }
+            //     },
+            //     err => {
+            //         this.$Message.error('API error!')
+            //     }
+            // ).finally(
+            //     () => {
+            //         this.isSaveLoading = false
+            //         this.isListLoading = false
+            //     }
+            // )
+            this.$api.room.RoomUpsert(this.classroomListShow[this.curClassIndex]).then(
+                res => {
+                    if (res) {
+                        if (res.error) {
+                            this.classroomListShow[this.curClassIndex].option = option
+                            this.$Message.error(res.v)
+                        } else {
+                            this.$Message.success(this.$t('schoolBaseInfo.csTips3'))
+                            this.updated = false
+                            this.updHiteachLink = []
+                            if (option == 'insert') {
+                                this.classroomListShow[this.curClassIndex].code = 'Class-' + this.classroomListShow[this.curClassIndex].code
+                                this.classroomList.unshift(this.classroomListShow[this.curClassIndex])
+                                this.$store.dispatch('user/addSchoolClasses', this.classroomListShow[this.curClassIndex]);
                             }
-                        },
-                        err => {
-                            this.$Message.error('API error!')
                         }
-                    ).finally(
-                        () => {
-                            this.isSaveLoading = false
-                            this.isListLoading = false
-                        }
-                    )
+                    } else {
+                        this.$Message.error('API error!')
+                    }
+                },
+                err => {
+                    this.$Message.error('API error!')
                 }
-            })
+            ).finally(
+                () => {
+                    this.isSaveLoading = false
+                    this.isListLoading = false
+                }
+            )
+            //     }
+            // })
         },
         getClassroom() {
             this.isListLoading = true
-            this.$store.dispatch('user/getSchoolProfile').then(
-                (res) => {
-                    if (res) {
-                        this.classroomList = res.school_classes
-                        this.schoolBase = res.school_base
-                        if (this.classroomList.length > 0) {
-                            this.updateBefore = JSON.stringify(this.classroomList[0])
-                        }
-                        this.filterClassname()
-                        // 預設搜尋給第一個
-                        // this.filterPeriod = res.school_base.period[0].id
-                        if (this.periods) this.filterPeriod = this.periods[0].id
-                        this.filterByPeriod()
-                    }
+            // this.$store.dispatch('user/getSchoolProfile').then(
+            //     (res) => {
+            //         if (res) {
+            //             this.classroomList = res.school_classes
+            //             this.schoolBase = res.school_base
+            //             if (this.classroomList.length > 0) {
+            //                 this.updateBefore = JSON.stringify(this.classroomList[0])
+            //             }
+            //             this.filterClassname()
+            //             // 預設搜尋給第一個
+            //             // this.filterPeriod = res.school_base.period[0].id
+            //             if (this.periods) this.filterPeriod = this.periods[0].id
+            //             this.filterByPeriod()
+            //         }
+            //     },
+            //     (err) => {
+            //         this.$Message.error('API error!')
+            //     }
+            // ).finally(() => {
+            //     setTimeout(() => {
+            //         this.isListLoading = false
+            //     }, 500)
+            // })
+            let requstData = {
+                code: this.$store.state.userInfo.schoolCode
+            }
+            this.$api.room.FindRoomList(requstData).then(
+                res => {
+                    this.classroomList = res.rooms
+                    this.classroomListShow = res.rooms
+                    console.log(this.classroomListShow)
+                    // this.schoolBase = res.school_base
+                    // if (this.classroomList.length > 0) {
+                    //     this.updateBefore = JSON.stringify(this.classroomList[0])
+                    // }
+                    // this.filterClassname()
+                    // // 預設搜尋給第一個
+                    // if (this.periods) this.filterPeriod = this.periods[0].id
+                    // this.filterByPeriod()
                 },
-                (err) => {
-                    this.$Message.error('API error!')
+                err => {
+                    console.log(err)
                 }
             ).finally(() => {
                 setTimeout(() => {
@@ -1141,55 +1220,78 @@ export default {
         },
         delClassroom(index) {
             this.isListLoading = true
-            if (this.classroomListShow[index].option !== 'insert') {
-                this.$api.schoolSetting.delClassroom({
-                    id: this.classroomListShow[index].id,
-                    scope: this.classroomListShow[index].scope,
-                    school_code: this.$store.state.userInfo.schoolCode
-                }).then(
-                    (res) => {
-                        if (res.error == null) {
-                            if (this.curClassIndex >= index && index > 0) {
-                                this.curClassIndex = 0
-                                this.updateBefore = JSON.stringify(this.classroomListShow[this.curClassIndex])
-                            }
-                            let originIndex = -1
-                            for (let i in this.classroomList) {
-                                if (this.classroomList[i].id == this.classroomListShow[index].id) {
-                                    originIndex = i
-                                    break
-                                }
-                            }
-                            this.$api.schoolSetting.hiteachUnlinkByClassId({
-                                classId: this.classroomListShow[index].id,
-                                school_code: this.$store.state.userInfo.schoolCode
-                            })
-                            this.unlinkHiteachByClassId(this.classroomListShow[index].id) // 刪掉指定的classId                                
-                            this.$store.dispatch('user/delSchoolClasses', this.classroomListShow[index].id);
-                            this.classroomList.splice(originIndex, 1)
-                            this.classroomListShow.splice(index, 1)
-                            this.$Message.success(this.$t('schoolBaseInfo.csTips7'))
-                            this.updated = false
-                            this.updHiteachLink = []
+            // if (this.classroomListShow[index].option !== 'insert') {
+            //     this.$api.schoolSetting.delClassroom({
+            //         id: this.classroomListShow[index].id,
+            //         scope: this.classroomListShow[index].scope,
+            //         school_code: this.$store.state.userInfo.schoolCode
+            //     }).then(
+            //         (res) => {
+            //             if (res.error == null) {
+            //                 if (this.curClassIndex >= index && index > 0) {
+            //                     this.curClassIndex = 0
+            //                     this.updateBefore = JSON.stringify(this.classroomListShow[this.curClassIndex])
+            //                 }
+            //                 let originIndex = -1
+            //                 for (let i in this.classroomList) {
+            //                     if (this.classroomList[i].id == this.classroomListShow[index].id) {
+            //                         originIndex = i
+            //                         break
+            //                     }
+            //                 }
+            //                 this.$api.schoolSetting.hiteachUnlinkByClassId({
+            //                     classId: this.classroomListShow[index].id,
+            //                     school_code: this.$store.state.userInfo.schoolCode
+            //                 })
+            //                 this.unlinkHiteachByClassId(this.classroomListShow[index].id) // 刪掉指定的classId                                
+            //                 this.$store.dispatch('user/delSchoolClasses', this.classroomListShow[index].id);
+            //                 this.classroomList.splice(originIndex, 1)
+            //                 this.classroomListShow.splice(index, 1)
+            //                 this.$Message.success(this.$t('schoolBaseInfo.csTips7'))
+            //                 this.updated = false
+            //                 this.updHiteachLink = []
+            //             }
+            //         },
+            //         (err) => {
+            //             this.$Message.error('API error!')
+            //         }
+            //     ).finally(() => {
+            //         this.isListLoading = false
+            //     })
+            // } else {
+            //     if (this.curClassIndex >= index && index > 0) {
+            //         this.curClassIndex = 0
+            //         this.updateBefore = JSON.stringify(this.classroomListShow[this.curClassIndex])
+            //     }
+            //     this.classroomListShow.splice(index, 1)
+            //     this.unlinkHiteachByClassId(this.classroomListShow[index].id)// 刪掉指定的classId
+            //     this.isListLoading = false
+            //     this.updated = false
+            //     this.updHiteachLink = []
+            // }
+            let requestData = {
+                id: this.classroomListShow[index].id,
+                code: this.$store.state.userInfo.schoolCode
+            }
+            this.$api.room.DelRoom(requestData).then(
+                res => {
+                    this.$Message.success(this.$t('schoolBaseInfo.csTips7'))
+                    let originIndex = -1
+                    for (let i in this.classroomList) {
+                        if (this.classroomList[i].id == this.classroomListShow[index].id) {
+                            originIndex = i
+                            break
                         }
-                    },
-                    (err) => {
-                        this.$Message.error('API error!')
                     }
-                ).finally(() => {
-                    this.isListLoading = false
-                })
-            } else {
-                if (this.curClassIndex >= index && index > 0) {
-                    this.curClassIndex = 0
-                    this.updateBefore = JSON.stringify(this.classroomListShow[this.curClassIndex])
+                    this.classroomList.splice(originIndex, 1)
+                    this.classroomListShow.splice(index, 1)
+                },
+                err => {
+                    this.$Message.error('API error!')
                 }
-                this.classroomListShow.splice(index, 1)
-                this.unlinkHiteachByClassId(this.classroomListShow[index].id)// 刪掉指定的classId
+            ).finally(() => {
                 this.isListLoading = false
-                this.updated = false
-                this.updHiteachLink = []
-            }
+            })
         },
         uploadSchoolPlan(res, file) {
             this.$Message.success(this.$t('schoolBaseInfo.csTips5'))
@@ -1250,32 +1352,55 @@ export default {
             }
         },
         addClassroom() {
-            if (this.$access.can('admin.*|classroom-upd')) {
-                this.keyword = ''
-                this.classroomListShow.unshift({
-                    id: this.$jsFn.uuid(),
-                    name: this.$t('schoolBaseInfo.presetClassroomName') + (this.classroomList.length + 1),
-                    style: 'normal',
-                    sn: '',
-                    no: '',
-                    openType: '1',
-                    teacher: {
-                        id: '',
-                        name: ''
-                    },
-                    periodId: this.filterPeriod,
-                    gradeId: '',
-                    x: 20 * (this.classroomList.length % 5),
-                    y: 20 * (this.classroomList.length / 5).toFixed(0),
-                    scope: 'school',
-                    option: 'insert'
-                })
-                this.drawIcon(5, 6)
-                this.curClassIndex = 0
-                this.updateBefore = JSON.stringify(this.classroomListShow[this.curClassIndex])
-            } else {
-                this.$Message.warning(this.$t('schoolBaseInfo.authWarning'))
-            }
+            // if (this.$access.can('admin.*|classroom-upd')) {
+            //     this.keyword = ''
+            //     this.classroomListShow.unshift({
+            //         id: this.$jsFn.uuid(),
+            //         name: this.$t('schoolBaseInfo.presetClassroomName') + (this.classroomList.length + 1),
+            //         style: 'normal',
+            //         sn: '',
+            //         no: '',
+            //         openType: '1',
+            //         teacher: {
+            //             id: '',
+            //             name: ''
+            //         },
+            //         periodId: this.filterPeriod,
+            //         gradeId: '',
+            //         x: 20 * (this.classroomList.length % 5),
+            //         y: 20 * (this.classroomList.length / 5).toFixed(0),
+            //         scope: 'school',
+            //         option: 'insert'
+            //     })
+            //     this.drawIcon(5, 6)
+            //     this.curClassIndex = 0
+            //     this.updateBefore = JSON.stringify(this.classroomListShow[this.curClassIndex])
+            // } else {
+            //     this.$Message.warning(this.$t('schoolBaseInfo.authWarning'))
+            // }
+            this.addRoomStatus = true
+        },
+        confirmAddRoom() {
+            this.room.code = this.$store.state.userInfo.schoolCode
+            this.$api.room.RoomUpsert(this.room).then(
+                res => {
+                    if (!res.error) {
+                        this.$Message.success(this.$t('schoolBaseInfo.csTips3'))
+                        this.classroomListShow.unshift(this._.cloneDeep(res.room))
+                        console.log(this.classroomListShow)
+                    } else {
+                        this.$Message.error('API error!')
+                    }
+                },
+                err => {
+                    this.$Message.error('API error!')
+                }
+            ).finally(
+                () => {
+                    this.isSaveLoading = false
+                    this.isListLoading = false
+                }
+            )
         },
         initHiteachData: async function () {
             let temp;
@@ -1400,21 +1525,21 @@ export default {
             this.orgHiteachData = JSON.stringify(orgHiData)
         },
         // 詢問視窗
-        confirmLink: function(linkFlag, pid, uuid, uuid2, classId){
+        confirmLink: function (linkFlag, pid, uuid, uuid2, classId) {
             let action = linkFlag ? 'unLink' : 'link';
-            if(action == 'unLink'){
+            if (action == 'unLink') {
                 this.$Modal.confirm({
                     title: this.$t('schoolBaseInfo.deLink'),
                     content: `${this.$t('schoolBaseInfo.confirmDelink')}?`,
                     onOk: () => {
-                        this.executeHiteachLink(action, pid, uuid, uuid2, classId)                        
+                        this.executeHiteachLink(action, pid, uuid, uuid2, classId)
                     }
                 })
             } else {
                 this.executeHiteachLink(action, pid, uuid, uuid2, classId)
             }
         },
-        executeHiteachLink: function(action, pid, uuid, uuid2, classId){
+        executeHiteachLink: function (action, pid, uuid, uuid2, classId) {
             // Hiteach 與教室綁定
             this.hiteachListLoading = true
             this.$api.schoolSetting.hiteachLink({
@@ -1436,10 +1561,10 @@ export default {
                     })
                     temp.deviceBound.forEach((dbItem, dbIndex, dbArray) => {
                         if ((dbItem.uuid + dbItem.uuid2) == (uuid + uuid2)) {
-                            if(action == 'link'){
+                            if (action == 'link') {
                                 dbItem.classId = classId
                                 dbItem.hiteachLink = true
-                            } else if(action == 'unLink'){
+                            } else if (action == 'unLink') {
                                 dbItem.classId = null
                                 dbItem.hiteachLink = false
                             }
@@ -1447,19 +1572,19 @@ export default {
                     })
 
                     orgHiData[tempIndex] = temp
-                    
+
                     // 新增加
                     this.hiteachData = orgHiData
                     this.orgHiteachData = JSON.stringify(orgHiData)
-                } else if (res.error == 1){
+                } else if (res.error == 1) {
                     this.$Message.error(this.$t('無法關聯已被關聯的Hiteach'))
                 }
             },
-            err => {
-                this.$Message.error(this.$t('參數錯誤'))
-            }).finally(()=>{
-                this.hiteachListLoading = false
-            })
+                err => {
+                    this.$Message.error(this.$t('參數錯誤'))
+                }).finally(() => {
+                    this.hiteachListLoading = false
+                })
         }
     },
     mounted() {

+ 614 - 0
TEAMModelOS/ClientApp/src/view/schoolmgmt/SystemSetting/NewSystemSetting.less

@@ -0,0 +1,614 @@
+@nothing-textColor: #5f5f5f;
+@large-fontSize: 20px;
+
+
+.sm-system {
+    width: 100%;
+    height: 100%;
+    color: white;
+}
+.sm {
+    &-school-header {
+        height: 50px;
+        border-bottom: 1px solid #353535;
+        padding: 0px 20px;
+        .school-tools {
+            font-size: var(--font-size-normal);
+            // margin-right: 30px;
+            margin-top: 8px;
+            float: right;
+        }
+
+        .school-tools .ivu-icon {
+            font-size: @font-size-large;
+            margin-right: 5px;
+            vertical-align: text-top;
+        }
+    }
+}
+.tab-box {
+    display: inline-block;
+    width: 30%;
+    min-width: 303px;
+    .pane{
+        font-weight: bold;
+        margin-right: 50px;
+        color: #777777;
+        line-height: 50px;
+        padding: 5px 0;
+        cursor: pointer;
+        vertical-align: middle;
+        &.active{
+            color: #e2e2e2;
+            border-bottom: 1px #777777 solid;
+        }
+    }
+}
+.base-info-item{
+    width: 766px;
+    margin: 0px auto;
+    padding: 15px 50px;
+    display: flex;
+    .setting-label{
+        color: #cdcdcd;
+        width: 150px;
+        font-weight: 600;
+        display: inline-block;
+    }
+    .base-value-wrap{
+        flex: 1;
+    }
+}
+.base-info-wrap{
+    padding-top: 4%;
+    display: flex;
+    flex-direction: column;
+}
+.badge-wrap{
+    position: relative;
+    width: 100px;
+    &:hover{
+        .upd-mask{
+            opacity: 1;
+        }
+    }
+    .upd-mask{
+        position: absolute;
+        left: 0px;
+        top: 0px;
+        bottom: 0px;
+        width: 100px;
+        border-radius: 4px;
+        display: flex;
+        background: rgba(68, 68, 68, 0.8);
+        justify-content: center;
+        align-items: center;
+        opacity: 0;
+        .upd-badge{
+            cursor: pointer;
+            text-align: center;
+            font-size: 20px;
+        }
+    }
+}
+
+.sm-system {
+    &-body {
+        height:~"calc(100% - 50px)";
+        width: 100%;
+        user-select: none !important;
+    }
+    .col-title {
+        
+        height: 40px;
+        line-height:40px;
+        width: 100%;
+        padding: 0 10px 0 0px;
+        font-size: var(--font-size-normal);
+        color: var(--label-text-color);
+        border-bottom: 1px solid var(--border-color);
+
+        .ivu-icon {
+            color: var(--primary-text-color);
+            cursor: pointer;
+            margin-right: 10px;
+        }
+    }
+}
+
+.editable {
+    display:inline-block;
+    &:hover {
+        .edit-btn {
+            display: inline-block;
+        }
+    }
+
+    .edit-btn {
+        display: none;
+        font-size: var(--font-size-normal);
+        margin-left: 10px;
+        margin-bottom: 5px;
+    }
+
+    .ivu-icon{
+        cursor:pointer;
+        color:white;
+    }
+}
+.time-zone-label{
+    width:300px;
+}
+.time-zone-label:hover + .edit-time-btn{
+    display:inline-block;
+}
+.school-badge{
+    vertical-align: middle;
+    width: 100px;
+    border-radius: 5px;
+}
+.campus-modal {
+    text-align: center;
+    color: #DDDDDD;
+    .campus-name-item {
+        margin-top: 10px;
+        position: relative;
+
+        &:hover .campus-btn-edit {
+            display: inline-block;
+        }
+
+        &:hover {
+            color: white;
+            font-weight: 500;
+        }
+
+        .campus-name {
+            font-size: 16px;
+            cursor: pointer;
+        }
+
+        .campus-btn-edit {
+            margin-left: 15px;
+            display: none;
+            cursor: pointer;
+            position: absolute;
+            right: 15px;
+        }
+    }
+}
+.semester-name-label {
+    display: inline-block;
+    vertical-align: super;
+}
+.period-wrap {
+    height: 100%;
+    padding-left: 15px;
+}
+
+.period-wrap {
+
+    .period-list {
+        width: 100%;
+        overflow: auto;
+
+        &::-webkit-scrollbar { /*滚动条整体样式*/
+            width: 5px; /*高宽分别对应横竖滚动条的尺寸*/
+            height: 1px;
+        }
+
+        &::-webkit-scrollbar-thumb { /*滚动条里面小方块*/
+            background: rgb(124,124,124);
+        }
+
+        &::-webkit-scrollbar-track { /*滚动条里面轨道*/
+            background: rgba(68,68,68,.5);
+        }
+
+        .campus-label {
+            color: aqua;
+            border: 1px solid aqua;
+            width: fit-content;
+            padding: 0px 5px;
+            border-radius: 4px;
+            font-size: 10px;
+            float: right;
+            margin-right:5px;
+            margin-top: 5px;
+        }
+    }
+
+    .period-item {
+        position: relative;
+        padding: 10px 10px 10px 0px;
+        border-bottom: 1px solid var(--border-color);
+        cursor: pointer;
+
+        &-name {
+            font-size: @large-fontSize;
+            color: var(--primary-text-color);
+            font-weight: bold;
+            margin: 2px 0px;
+            width:100%;
+            .period-btn-edit {
+                font-size: var(--font-size-normal);
+                margin-left: 10px;
+                margin-bottom: 5px;
+                display: none;
+            }
+        }
+
+        &-num {
+            padding-left: 5px;
+            color: var(--label-text-color);
+            font-size: var(--font-size-normal);
+
+            span {
+                display: inline-block;
+                width: 3px;
+                height: 10px;
+                margin-right: 5px;
+                background: var(--label-text-color);
+            }
+        }
+    }
+}
+
+.setting-scroll{
+    height: 100%;
+    .block-title {
+        margin-bottom: 20px;
+        margin-left: 10px;
+        color: #aeaeae;
+        position: relative;
+        &::before{
+            content: '';
+            display: block;
+            position: absolute;
+            width: 6px;
+            height: 6px;
+            border-radius: 50%;
+            background: #aeaeae;
+            left: -12px;
+            top: 7px;
+        }
+    }
+    .bf-color1{
+        &::before{
+            background: #F16C6A;
+        }
+    }
+    .bf-color2{
+        &::before{
+            background: #68CDF1;
+        }
+    }
+    .bf-color3{
+        &::before{
+            background: #7C4DFF;
+        }
+    }
+    .bf-color4{
+        &::before{
+            background: #00796B;
+        }
+    }
+    .bf-color5{
+        &::before{
+            background: #D32F2F;
+        }
+    }
+    .bf-color6{
+        &::before{
+            background: #0288D1;
+        }
+    }
+    .setting-content {
+        padding: 12px 16px 16px 16px;
+        min-height: 150px;
+        border-radius: 5px;
+        margin-right: 12px;
+        margin: 0px 20px 20px 0px;
+        box-shadow: 0 0px 8px 1px #1e1f21;
+        overflow: hidden;
+        display: inline-block;
+        background: #2b2a2f;
+        border: 1px solid #393939;
+        &:hover{
+            background: #333237;
+        }
+        &:hover .block-action-box{
+            display: inline-block;
+        }
+    }
+}
+.semster-item{
+    border-bottom: 1px solid #393939;
+    padding: 5px 10px 5px 5px;
+
+    &:last-child{
+        border: none;
+    }
+
+    &:hover{
+        background: #404042;
+        .hover-show-icon{
+            display: inline-block;;
+        }
+    }
+}
+.action-btx-box{
+    float: right;
+    height: 38px;
+    line-height: 38px;
+}
+.action-btn-icon{
+    color:white;
+    margin-left:8px;
+    font-size:16px;
+    cursor: pointer;
+    &:hover{
+        color:aqua;
+    }
+}
+.hover-show-icon{
+    display: none;
+}
+.grade-content{
+    width: ~"calc(100% - 20px)";
+    height: 280px;
+}
+@media screen and (max-width: 1600px) {
+    .grade-content{
+        width: 100%;
+    }
+}
+.subject-content{
+    width: 100%;
+    height: 280px;
+}
+
+.tag-item {
+    display: inline-block;
+    background: #5d5d5d;
+    color: #9b9b9b;
+    padding: 0px 5px 0px 10px;
+    font-size: 16px;
+    border-radius: 3px;
+    margin-right: 10px;
+    margin-bottom:10px;
+    position: relative;
+    overflow: hidden;
+    &-icon {
+        display: inline-block;
+        width: 10px;
+        height: 10px;
+        background: #404040;
+        border-radius: 50%;
+        margin-right: 2px;
+    }
+}
+.attr-box-item {
+    color: white;
+    margin-bottom: 10px;
+    display: flex;
+
+
+    .add-type-iocn {
+        cursor: pointer;
+        font-size: 16px;
+        margin-top: 8px;
+    }
+
+    p {
+        min-width: 80px;
+        line-height: 32px;
+    }
+}
+.block-action-box{
+    float: right;
+    display: none;
+}
+
+.time-content{
+    width: 100%;
+    height: 800px;
+}
+.analysis-content{
+    width: 100%;
+}
+.setting-right-side{
+    width: 400px;
+    padding-top: 15px;
+}
+.time-setting-wrap{
+    height: 100%;
+    padding-left: 15px;
+}
+.time-setting-body{
+    height: ~"calc(100% - 40px)";
+}
+.label-box{
+    width: 150px;
+    height: 30px;
+    border-radius: 4px;
+    background: #808080;
+    overflow: hidden;
+}
+
+.subject-label-conmon{
+    font-size: 16px;
+    display: block;
+    width: fit-content;
+    position: absolute;
+    left: -12px;
+    top: -3px;
+    border-left: 13px solid transparent;
+    border-right: 13px solid transparent;
+    transform: rotate(-45deg);
+}
+.profession-label{
+    border-bottom: 13px solid #ff9900;
+}
+.subject-label{
+    border-bottom: 13px solid #1cc0f3;
+}
+.general-label{
+    border-bottom: 13px solid #19be6b;
+}
+.subject-item{
+    padding-left: 25px;
+    &:hover .subject-action-iocn{
+        // display: inline-block;
+        opacity: 1;
+    }
+}
+
+.add-subject-form{
+    color: white;
+}
+.grade-item{
+    border-bottom: 1px solid #323232;
+    padding:0px 10px;
+    &:hover .grade-action-icon{
+        display: inline-block;
+    }
+    &:hover{
+        background: #404042;
+        border-color: transparent;
+    }
+}
+.grade-order{
+    display: inline-block;
+    width: 20px;
+    height: 20px;
+    border-radius: 50%;
+    background: #a5a5a5;
+    text-align: center;
+    font-size: 12px;
+    line-height: 20px;
+    margin-right: 20px;
+    
+}
+.semester-content{
+    width: 100%;
+    margin-top: 8px !important;
+}
+.term-item-time-line {
+    position: relative;
+    width: 100%;
+    height: ~"calc(100% - 50px)";
+    color: var(--label-text-color);
+    ul {
+        list-style: none;
+        width: fit-content;
+        margin: auto;
+        width: 100%;
+        margin-top: 20px;
+    }
+
+    li {
+        margin: 27px 0;
+        font-size: @font-size-large;
+        font-weight: bold;
+        justify-content: flex-start;
+        display: inline-block;
+        width: 8.3%;
+        &:last-child .time-line-tail {
+            display: none;
+        }
+    }
+
+    .time-label {
+        width: fit-content;
+        text-align: center;
+        font-size: 12px;
+        margin-top: 5px;
+        margin-left: -1px;
+    }
+    .arrow-box{
+        position: absolute;
+        top: 2px;
+        margin-left: 1px;
+    }
+    .line-box{
+        display: flex;
+        width: 100%;
+    }
+    .time-dot {
+        position: relative;
+        width: 18px;
+        height: 18px;
+        border-radius: 50%;
+        padding: 5px;
+        border: 2px solid #959595;
+        z-index: 2;
+        display: inline-block;
+    }
+
+    .time-inner-dot {
+        display: inline-block;
+        width: 6px;
+        height: 6px;
+        background: #959595;
+        border-radius: 50%;
+        position: absolute;
+        top: 4px;
+        left: 4px;
+    }
+    .second-arrow{
+        margin-top: -10px;
+    }
+
+    .time-line-tail {
+        width: 35px;
+        margin-top: 9px;
+        border-top: 2px solid #606060;
+        width: ~"calc(100% - 18px)";
+    }
+    .arrow-item{
+        display: block;
+        transform: rotate(180deg);
+    }
+}
+.cur-semester-name{
+    font-size: 12px;
+    color: #a5a5a5;
+    padding-left: 10px;
+    margin-top: -20px;
+}
+.grade-action-icon{
+    cursor:pointer;
+    flex:0.1;
+    display: none;
+    float: right;
+    margin-left: 10px;
+    margin-top: 8px;
+}
+.subject-action-iocn{
+    color: white;
+    margin-left: 2px;
+    font-size: 12px;
+    cursor: pointer;
+    opacity: 0;
+    z-index: 999;
+    position: relative;
+}
+.profession-content{
+    width: 100%;
+    
+}
+.semester-start-warning{
+    color: #ed4014;
+    margin-left: 15px;
+}
+.year-start-tag{
+    font-size: 12px;
+    color: #1cc0f3;
+    border: 1px solid #1cc0f3;
+    padding: 1px 2px;
+    border-radius: 2px;
+    margin-left: 15px;
+    display: inline-block;
+    transform: scale(0.8);
+}

文件差異過大導致無法顯示
+ 1505 - 0
TEAMModelOS/ClientApp/src/view/schoolmgmt/SystemSetting/NewSystemSetting.vue


+ 462 - 0
TEAMModelOS/ClientApp/src/view/student-account/ClassMgt.less

@@ -0,0 +1,462 @@
+@main-bgColor: rgb(40,40,40); //主背景颜色
+@borderColor: #424242; //边框颜色
+@primary-textColor: #fff; //文本主颜色
+@second-textColor: #a5a5a5; //文本副级颜色
+@third-textColor: #dddddd;//其他颜色
+@fourth-textColor: #1cc0f3;
+@primary-fontSize: 14px;
+@second-fontSize: 16px;
+@large-fontSize: 20px;
+.class-mgt-container {
+    height: 100%;
+    width: 100%;
+    display: flex;
+    flex-direction: row;
+
+    .class-list-wrap {
+        width: 400px;
+        height: 100%;
+        border-right: 1px solid @borderColor;
+    }
+
+    .class-info-wrap {
+        width: ~"calc(100% - 400px)";
+        height: 100%;
+        padding-left:15px;
+    }
+}
+.class-list-header{
+    width:100%;
+    height:45px;
+    line-height:45px;
+    border-bottom:1px solid @borderColor;
+    padding-left:15px;
+    color:@second-textColor;
+}
+.action-btn {
+    float: right;
+    display: flex;
+    align-items: center;
+    margin-right: 15px;
+    &-icon {
+        color: white;
+        font-size: 16px;
+        margin-right: 12px;
+        margin-left: 10px;
+        cursor: pointer;
+
+        &.hide-icon {
+            display: none;
+        }
+    }
+}
+.class-info-header{
+    width:100%;
+    height:45px;
+    line-height:45px;
+    border-bottom:1px solid @borderColor;
+}
+.class-info-content{
+    width:100%;
+    height:~"calc(100% - 45px)";
+    position:relative;
+}
+.main-header-tab {
+    color: @second-textColor;
+    margin-right: 15px;
+    cursor: pointer;
+    position: relative;
+    display: inline-block;
+    line-height: 25px;
+}
+
+.border(right) {
+    border-right: 1px solid @borderColor;
+}
+.border(top) {
+    border-top: 1px solid @borderColor;
+}
+.border(left) {
+    border-left: 1px solid @borderColor;
+}
+.border(bottom) {
+    border-bottom: 1px solid @borderColor;
+}
+.container-left {
+    width: 59%;
+    height:100%;
+    .border(right);
+
+    &-title {
+        width: 100%;
+        padding-left: 15px;
+        height: 50px;
+        .border(bottom);
+        line-height: 50px;
+        padding-right: 35px;
+    }
+}
+.container-right {
+    width: 41%;
+    height:100%;
+
+    &-title {
+        width: 100%;
+        height: 45px;
+        line-height: 45px;
+        .border(bottom);
+    }
+}
+.first-title {
+    font-size: @primary-fontSize + 1;
+    color: @second-textColor;
+    font-weight:900;
+}
+
+.label-style {
+    font-size: @primary-fontSize - 1;
+    color: @second-textColor;
+    float: right;
+    margin-right: 40px;
+    cursor: pointer;
+}
+.label-style .label-icon{
+    margin-right:15px;
+}
+.save-btn {
+    margin-top: 5px;
+    /*color: black;*/
+    float: right;
+    margin-right: 30px;
+}
+.school-plan-wrap {
+    height: 100%;
+    width: 100%;
+    display: flex;
+    flex-flow: row;
+    align-items: center;
+    justify-content: center;
+}
+.school-plan-wrap p{
+    position:absolute;
+    top:0px;
+    right:0px;
+    padding:8px 35px 6px 20px;
+    .border(bottom);
+    color:white;
+    cursor:pointer;
+}
+.school-plan-wrap .label-icon {
+    margin-right: 15px;
+
+}
+.container-left-wrap{
+    width:100%;
+    height:~"calc(100% - 50px)";
+    display:flex;
+    flex-direction:row;
+}
+.container-left-box {
+    width: 38%;
+    height: 100%;
+    .border(right);
+    
+    .list-order {
+        padding: 5px 0px 5px 15px;
+        color: white;
+
+        .label-icon {
+            margin-right: 10px;
+        }
+    }
+}
+.container-right-box {
+    width: 62%;
+    height: 100%;
+    padding-left:15px;
+}
+.class-search-input{
+    width:~"calc(100% - 15px)";
+    margin-left:10px;
+    height:45px;
+    line-height:45px;
+}
+.class-list {
+    width: 100%;
+    height:~"calc(100% - 50px)";
+    padding-left: 15px;
+    
+}
+.active-item-bg {
+    background-image: linear-gradient(90deg, rgba(30,30,30,0) 0%, rgba(110,110,110,.2) 50%, rgba(110,110,110,.4) 100%);
+}
+.class-list-item {
+    padding: 8px 0px;
+    .border(bottom);
+    cursor:pointer;
+    position:relative;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;    
+    &:hover {
+        .active-item-bg,
+        .period-btn-edit {
+            display: inline-block;
+        }
+    }
+}
+.class-name {
+    font-size: @large-fontSize;
+    color: white;
+    font-weight: 500;
+}
+.second-text-color {
+    color: @second-textColor;
+    font-size: 12px;
+}
+.third-text-color{
+    color:@third-textColor;
+}
+.fourth-text-color {
+    color: #1cc0f3 !important;
+    /*text-shadow:1px 1px 1px white;*/
+}
+.class-hiteach-code {
+    margin-top:5px;
+}
+.class-type {
+    margin-top:6px;
+    margin-bottom:4px;
+    align-items: center;
+    display: flex;
+    span{
+        color: rgb(28, 192, 243);
+    }
+}
+.iconHi{
+    display: inline-block;
+    margin-right: 5px;
+    width: 14px;
+    height: 14px;
+}
+.iconBoard {
+    width: 14px;
+    height: 14px;
+    background: rgb(165, 165, 165);
+    border-radius: 16%;
+    display: inline-flex;
+    justify-content: center;
+    align-items: center;
+    margin-right: 5px;
+}
+.primary-text-color{
+    color:@primary-textColor;
+}
+
+.class-attr-wrap{
+    width:35%;
+    height:100%;
+    .border(right);
+    &-label{
+        display:inline-block;
+        color:@second-textColor;
+        font-size:@second-fontSize - 4px;
+    }
+}
+.hiteach-code-wrap {
+    width: 100%;
+    height: 100%;
+    position: relative;
+    &-header {
+        width: ~"calc(100% - 30px)";
+        margin-left: 20px;
+
+        p {
+            padding: 10px 0px;
+            color: @second-textColor;
+            .border(bottom);
+        }
+    }
+
+    &-list {
+        .border(top);
+        width: 100%;
+        height: ~"calc(100% - 72px)";
+        padding-left: 25px;
+        h1 {
+            font-weight: 400;
+        }
+
+        ul {
+            list-style: none;
+
+            li {
+                color: #dbdbdb;
+                display: flex;
+                align-items: center;
+                margin-bottom: 5px;
+            }
+        }
+    }
+}
+#school-plan {
+    background: none;
+}
+.school-plan-zoom {
+    position: absolute;
+    right: 150px;
+    top: 10px;
+    -webkit-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+    .ivu-icon {
+        margin-right: 10px;
+        cursor: pointer;
+    }
+
+    span {
+        color: white;
+        font-size: 16px;
+        margin-right: 15px;
+        display: inline-block;
+        letter-spacing: 3px;
+        cursor: pointer;
+    }
+}
+.school-plan-box {
+    width: ~"calc(100% - 60px)";
+    max-height: 90%;
+    overflow: scroll;
+    text-align: center;
+
+    &::-webkit-scrollbar { /*滚动条整体样式*/
+        width: 5px; /*高宽分别对应横竖滚动条的尺寸*/
+        height: 8px;
+    }
+
+    &::-webkit-scrollbar-thumb { /*滚动条里面小方块*/
+        -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
+        background: rgb(124,124,124);
+    }
+
+    &::-webkit-scrollbar-track { /*滚动条里面轨道*/
+        -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
+        background: rgba(68,68,68,.5);
+    }
+}
+.hiteach-collapse{
+    border-bottom: 1px solid #424242;
+    position: relative;
+    cursor: pointer;
+    &-main{
+        display: flex;
+        align-items: center;
+        padding: 20px 0 20px 20px;
+        &:hover{
+            background-image: linear-gradient(90deg, rgba(30,30,30,0) 0%, rgba(110,110,110,.2) 50%, rgba(110,110,110,.4) 100%);
+        }
+        .arrowIcon{
+            position: absolute;
+            right: 5px;
+            top: 20px;
+        }
+        .proIcon{
+            width: 60px;
+            height: 60px;
+            margin-right: 30px;
+        }
+        .proCont{
+            font-size: 12px;
+            letter-spacing: 1px;
+            ul li{
+                .title{
+                    display: inline-block;
+                    margin-right: 12px;
+                    font-size: 16px;
+                    font-weight: 500;
+                    color: #bdbdbd;
+                    color:white;
+                }
+                .serialType{
+                    margin-right: 12px;
+                    padding: 2px 5px;
+                    background: #1cc0f3;
+                    border-radius: 3px;
+                    color: white;
+                }
+                .tag{
+                    display: inline-block;
+                    border: 1px solid #8d8d8d;
+                    padding: 3px 10px;
+                    color: #8d8d8d;
+                    margin-top: 5px;
+                    margin-right: 10px;
+                    border-radius: 5px;
+                    &.active{
+                        color: #1cc0f3!important;
+                        border-color: #1cc0f3!important;
+                    }
+                }
+            }
+        }
+    }
+    .hiteach-collapse-sub{
+        display: flex;
+        align-items: center;
+        border-top: 1px solid rgb(66, 66, 66);
+        margin-left: 20px;
+        letter-spacing: 1px;
+        font-size: 12px;
+        padding: 20px;        
+        &:hover{
+            background-image: linear-gradient(90deg, #2a2a2e, #293942);
+        }
+        &-detail{
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            width: 100%;
+        }
+        &.linked{
+            ul li{
+                color: #888888;
+            }            
+            cursor: not-allowed;
+        }
+        &-linkBtn{
+            cursor: pointer;
+            border-radius: 5px;
+            padding: 5px 12px;
+            font-size: 12px;
+            background-color: #1cc0f3;
+            color: white;
+            text-align: center;
+            font-weight: 700;
+            margin-right: 70px;
+            border-color: transparent;
+            &:hover{
+                background-color: #0ba2d1;
+            }
+        }
+    }
+}
+
+.class-id-tag {
+    color: aqua;
+    font-size: 14px;
+    margin-right: 5px;
+    border: 1px solid aqua;
+    padding: 1px 3px;
+    border-radius: 3px;
+    vertical-align:middle;
+}
+.item-tools{
+    display:none;
+}
+
+.reset-no-btn{
+    display: inline-block;
+    margin-left: 10px;
+    cursor: pointer;
+}

+ 876 - 0
TEAMModelOS/ClientApp/src/view/student-account/ClassMgt.vue

@@ -0,0 +1,876 @@
+<template>
+    <div class="class-mgt-container">
+        <Loading v-show="isListLoading"></Loading>
+        <div class="class-list-wrap">
+            <!--班级列表-->
+            <div class="class-list-header">
+                <Dropdown class="sort-dropdown" trigger="click" placement="bottom-start" @on-click="function(e){ filterPeriod = e }" @on-visible-change="dropdownStates" v-if="$store.state.user.schoolProfile.school_base">
+                    <span style="cursor: pointer;">
+                        <!-- {{$t('schoolBaseInfo.pdLabel')}} -->
+                        <b class="title">{{ filterPeriodName }}</b>
+                        <Icon type="ios-arrow-down" style="margin-left:8px;"></Icon>
+                    </span>
+                    <DropdownMenu slot="list" v-for="(item,index) in periods" :value="item.id" :key="index">
+                        <DropdownItem :name="item.id">{{ item.name }}</DropdownItem>
+                    </DropdownMenu>
+                </Dropdown>
+                <Dropdown class="sort-dropdown" trigger="click" placement="bottom-start" @on-click="function(e){ filterYear = e }" style="margin-left:15px">
+                    <span style="cursor: pointer;">
+                        <!-- {{$t('schoolBaseInfo.pdLabel')}} -->
+                        <!-- <b class="title">{{ filterYear + '级' }}</b> -->
+                        <b class="title">{{ filterYear}}</b>
+                        <Icon type="ios-arrow-down" style="margin-left:8px;"></Icon>
+                    </span>
+                    <DropdownMenu slot="list" v-for="(item,index) in years" :value="item.value" :key="index">
+                        <DropdownItem :name="item.label">{{ item.label }}</DropdownItem>
+                    </DropdownMenu>
+                </Dropdown>
+                <div v-if="!isSearch" style="float:right;">
+                    <Icon class="action-btn-icon" type="ios-search" @click="isSearch = true" />
+                    <Icon v-if="$access.can('admin.*|classroom-upd')" class="action-btn-icon" type="md-trash" @click.stop="showConfirmDelete()" />
+                    <Icon v-if="$access.can('admin.*|classroom-upd')" class="action-btn-icon" type="md-add" @click="addClassroom()" />
+                </div>
+                <div v-else class="dark-iview-input" style="float:right;width:calc(100% - 200px);padding-right:10px;">
+                    <Input icon="ios-close" v-model="keyword" :placeholder="$t('schoolBaseInfo.codeSearchHolder')" autofocus style="width:100%" @on-click="closeKeySearch" @on-change="filterClassname" />
+                </div>
+            </div>
+            <div class="class-list">
+                <vuescroll>
+                    <div class="class-list-item" v-for="(item,index) in classroomListShow" :key="index" @click="chooseClassroom(index)" :class="curClassIndex == index ? 'block-bg block-bg-active':'block-bg'">
+                        <div class="class-list-item-left">
+                            <!-- <p style="color: #a5a5a5;">{{$jsFn.getGradeName(schoolBase,item.gradeId)}}</p> -->
+                            <p style="color: #a5a5a5;">2020级</p>
+                            <p class="class-name">
+                                <span class="class-id-tag">
+                                    {{item.no}}
+                                </span>
+                                <span>
+                                    {{item.name}}
+                                </span>
+                            </p>
+                            <p class="class-type">
+                            <p class="second-text-color">
+                                <span>{{ $t('schoolBaseInfo.headmaster') }}</span>
+                                <span class="primary-text-color">{{item.teacher.name}}</span>
+                                <span style="margin-left:15px">学生人数:</span>
+                                <span class="primary-text-color">40人</span>
+                            </p>
+                        </div>
+                    </div>
+                    <EmptyData v-if="classroomListShow.length == 0" style="padding-top:120px;"></EmptyData>
+                </vuescroll>
+            </div>
+        </div>
+        <div class="class-info-wrap">
+            <div class="class-info-header common-save-btn">
+                <span :class="currentTabIndex == 0 ? 'main-header-tab line-bottom line-bottom-active':'main-header-tab line-bottom'" @click="selectTab(0)">
+                    {{$t('schoolBaseInfo.tab1')}}
+                </span>
+                <span :class="currentTabIndex == 2 ? 'main-header-tab line-bottom line-bottom-active':'main-header-tab line-bottom'" @click="selectTab(2)">
+                    {{$t('schoolBaseInfo.tab2')}}
+                </span>
+                <!-- 编辑教室 -->
+                <Button v-if="$access.can('admin.*|classroom-upd')" class="save-btn" :loading="isSaveLoading" :disabled="!updated" icon="ios-albums-outline" @click="saveClassroom()" v-show="currentTabIndex == 0">
+                    {{$t('schoolBaseInfo.saveInfo')}}
+                </Button>
+                <!-- 添加学生 -->
+                <Button v-if="$access.can('admin.*|student-upd')" class="save-btn" :loading="isSaveLoading" icon="md-add" @click="addStuStatus = true" v-show="currentTabIndex == 2">
+                    {{$t('schoolBaseInfo.addStuBtn')}}
+                </Button>
+                <!-- 移除学生 -->
+                <Button v-if="$access.can('admin.*|student-upd')" class="save-btn" style="margin-right:10px;" :loading="isSaveLoading" icon="md-remove-circle" @click="removeStudent(-1)" v-show="currentTabIndex == 2">
+                    {{$t('schoolBaseInfo.delStuBtn')}}
+                </Button>
+            </div>
+            <div class="class-info-content">
+                <!--基础信息-->
+                <div v-show="currentTabIndex == 0" style="display:flex;flex-direction:row;width:100%;height:100%;">
+                    <!--班级属性-->
+                    <div class="class-attr-wrap disabled-iview-select dark-iview-select">
+                        <vuescroll>
+                            <Form v-if="classroomListShow[curClassIndex]" ref="classInfo" :model="classroomListShow[curClassIndex]" :rules="classValidate" style="padding-top:20px;">
+                                <FormItem prop="name" :label="$t('schoolBaseInfo.classroomName')" @click.native.stop class="requird-color">
+                                    <span slot="label" class="class-attr-wrap-label">名称</span>
+                                    <Input @on-change="watchUpdate" :disabled="editStatus" v-model="classroomListShow[curClassIndex].name" clearable :placeholder="$t('schoolBaseInfo.classroomNameHolder')" />
+                                </FormItem>
+                                <FormItem prop="no" @click.native.stop class="requird-color">
+                                    <span slot="label" class="class-attr-wrap-label">编码</span>
+                                    <Input @on-change="watchUpdate" :disabled="editStatus" v-model="classroomListShow[curClassIndex].no" clearable :placeholder="$t('schoolBaseInfo.classroomCodeHolder')" />
+                                </FormItem>
+                                <FormItem prop="gradeId" :label="$t('schoolBaseInfo.setGrade')" @click.native.stop class="requird-color">
+                                    <span slot="label" class="class-attr-wrap-label">学级</span>
+                                    <Select @on-change="watchUpdate" :disabled="editStatus" v-model="classroomListShow[curClassIndex].gradeId" clearable>
+                                        <Option v-for="(item,index) in $jsFn.getPeriod($store.state.user.schoolProfile.school_base,classroomListShow[curClassIndex].periodId).grades" :value="item.id" :key="index">
+                                            {{ item.name }}
+                                        </Option>
+                                    </Select>
+                                </FormItem>
+                                <FormItem prop="teacher" :label="$t('schoolBaseInfo.headmaster')" @click.native.stop class="requird-color">
+                                    <span slot="label" class="class-attr-wrap-label">{{$t('schoolBaseInfo.headmaster')}}</span>
+                                    <Select @on-change="watchUpdate" :disabled="editStatus" v-model="classroomListShow[curClassIndex].teacher.id" clearable filterable>
+                                        <Option v-for="(item,index) in $store.state.teachers.teacherList.filter(item=>item.status == 'join')" :value="item.id" :key="index" @click.native="classroomListShow[curClassIndex].teacher = {id:item.id, name:item.name}">
+                                            {{ item.name }}
+                                        </Option>
+                                    </Select>
+                                </FormItem>
+                                <FormItem prop="profession" @click.native.stop class="requird-color">
+                                    <span slot="label" class="class-attr-wrap-label">专业</span>
+                                    <Select @on-change="watchUpdate" :disabled="editStatus" v-model="classroomListShow[curClassIndex].marjorId" clearable not-found-text="暂未设置专业">
+                                        <Option v-for="(item,index) in $jsFn.getPeriod($store.state.user.schoolProfile.school_base,classroomListShow[curClassIndex].periodId).majors" :value="item.id" :key="index">
+                                            {{ item.name }}
+                                        </Option>
+                                    </Select>
+                                </FormItem>
+                                <FormItem prop="profession" @click.native.stop class="requird-color">
+                                    <span slot="label" class="class-attr-wrap-label">默认教室</span>
+                                    <Select @on-change="watchUpdate" :disabled="editStatus" v-model="classroomListShow[curClassIndex].roomId" clearable not-found-text="暂无教室">
+                                        <Option v-for="(item,index) in $jsFn.getPeriod($store.state.user.schoolProfile.school_base,classroomListShow[curClassIndex].periodId).majors" :value="item.id" :key="index">
+                                            {{ item.name }}
+                                        </Option>
+                                    </Select>
+                                </FormItem>
+                            </Form>
+                        </vuescroll>
+                    </div>
+                </div>
+
+                <!--学生名单-->
+                <div id="sut-list-box" class="dark-iview-table dark-iview-input" style="width:100%;height:100%;" v-show="currentTabIndex == 2">
+                    <vuescroll style="height:100%;" v-if="classroomListShow[curClassIndex]">
+                        <Table :columns="studentColumn" :data="students" @on-selection-change="(selections)=>{delSelections = selections}" :height="tableHeight" class="system-classroom-table" :loading="stuLoading" :no-data-text="$t('schoolBaseInfo.noStu')">
+                            <Loading slot="loading" :top="0" bgColor="rgba(103, 103, 103, 0.27)"></Loading>
+                            <template slot-scope="{ row }" slot="picture">
+                                <PersonalPhoto :name="row.name" :picture="row.picture" />
+                            </template>
+                            <template slot-scope="{ row,index }" slot="no">
+                                <span v-show="editIndex !== index">{{row.no}}</span>
+                                <Input v-model="students[index].no" v-show="editIndex == index" style="width: 60px;" type="number" />
+                                <Icon type="md-checkmark" v-show="editIndex == index" @click="confirmSetNo()" class="reset-no-btn" />
+                                <Icon type="md-close" v-show="editIndex == index" @click="cancelSetNo()" class="reset-no-btn" />
+                            </template>
+                            <template slot-scope="{ row ,index}" slot="action">
+                                <div class="item-tools" v-if="$access.can('admin.*|student-upd')">
+                                    <Icon type="md-create" size="18" color="white" @click="resetNo(index)" :title="$t('schoolBaseInfo.editSeat')" />
+                                    <Icon type="md-remove-circle" size="18" color="white" style="margin-left:10px" @click="removeStudent(index)" :title="$t('schoolBaseInfo.delStuBtn')" />
+                                </div>
+                            </template>
+                        </Table>
+                    </vuescroll>
+                </div>
+            </div>
+        </div>
+        <Modal v-model="addStuStatus" :title="$t('schoolBaseInfo.addStuBtn')" width="1200" @on-ok="confirmAddStu" class-name="dark-iview-modal">
+            <StudentList @getSelectInfo="(selction)=>{selections = selction}"></StudentList>
+        </Modal>
+    </div>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import PersonalPhoto from "@/components/public/personalPhoto/Index.vue"
+import StudentList from '@/components/coursemgt/StudentList.vue'
+export default {
+    components: {
+        PersonalPhoto, StudentList
+    },
+    data() {
+        // 验证只能是字母和数字
+        const validateCode = (rule, value, callback) => {
+            if (!value) {
+                return callback(new Error(this.$t('schoolBaseInfo.classNoErr')))
+            }
+            let zg = /^[0-9a-zA-Z]+$/
+            if (zg.test(value)) {
+                callback()
+            } else {
+                callback(new Error(this.$t('schoolBaseInfo.classNoErr1')))
+            }
+        }
+        return {
+            filterYear:'',
+            // years:[2021,2020,2019,2018,2017,2016,2015,2014],
+            studentColumn: [
+                {
+                    title: ' ',
+                    type: 'selection',
+                    width: 60,
+                    align: 'center'
+                },
+                {
+                    title: ' ',
+                    slot: 'picture',
+                    align: 'center ',
+                    width: '150'
+                },
+                {
+                    title: this.$t('courseManage.classroom.studentTableC2'),
+                    key: 'name',
+                    align: 'center '
+                },
+                {
+                    title: this.$t('courseManage.classroom.studentTableC7'),
+                    key: 'id',
+                    align: 'center',
+                    sortable: true
+                },
+                {
+                    title: this.$t('courseManage.classroom.studentTableC1'),
+                    slot: 'no',
+                    align: 'center',
+                    sortable: true
+                },
+                {
+                    title: ' ',
+                    slot: 'action',
+                    align: 'center',
+                    width: '150'
+                }
+            ],
+            editIndex: -1,
+            resetNoBef: 0,
+            schoolBase: {
+                period: []
+            },
+            delSelections: [],//需要移除的学生
+            selections: [],//添加的学生
+            addStuStatus: false,
+            stuLoading: false,
+            orderBy: 'id',
+            updateBefore: '',
+            filterPeriod: undefined,
+            editStatus: true,//可切换编辑状态
+            noStatus: false,
+            isSearch: false,
+            currentTabIndex: 0,
+            isListLoading: false,
+            isSaveLoading: false,
+            isInit: true,
+            updated: false,
+            classroomList: [],
+            classroomListShow: [],
+            curClassIndex: 0,
+            keyword: '',
+            serchCode: '',
+            attributeList: [
+                {
+                    value: '1',
+                    label: this.$t('schoolBaseInfo.classAttr1')
+                },
+                {
+                    value: '2',
+                    label: this.$t('schoolBaseInfo.classAttr2')
+                }
+            ],
+            classValidate: {
+                no: [
+                    { required: true, validator: validateCode, trigger: 'change' }
+                ],
+                name: [
+                    { required: true, message: this.$t('schoolBaseInfo.nameWarning'), trigger: 'change' }
+                ],
+                openType: [
+                    { required: true, message: this.$t('schoolBaseInfo.typeWarning'), trigger: 'change' }
+                ],
+                gradeId: [
+                    { required: true, message: this.$t('schoolBaseInfo.gradeWarning'), trigger: 'change' }
+                ]
+            }
+        }
+    },
+    computed: {
+        ...mapGetters({
+            periods: 'user/getPeriods', // 學制s
+        }),
+        filterPeriodName: function () {
+            let data = this.periods
+            let pId = this.filterPeriod
+            let name = ''
+            if (pId !== '') {
+                let temp = data.filter(item => {
+                    return pId == item.id
+                })
+                if (temp.length > 0) name = temp[0].name
+            }
+            return name
+        },
+
+        students() {
+            if (this.currentTabIndex == '2') {
+                if (this.classroomListShow[this.curClassIndex] && this.classroomListShow[this.curClassIndex].students) {
+                    return this.classroomListShow[this.curClassIndex].students
+                } else {
+                    let params = {
+                        'school_code': this.$store.state.userInfo.schoolCode,
+                        'ids': [this.classroomListShow[this.curClassIndex].id],
+                        'scope': 'school'
+                    }
+                    this.stuLoading = true
+                    this.$api.schoolSetting.getClassroomStudent(params).then(
+                        res => {
+                            if (res.stus.length > 0) {
+                                this.$set(this.classroomListShow[this.curClassIndex], 'students', res.stus[0])
+                            }
+                        },
+                        err => {
+                            this.$Message.error(this.$t('schoolBaseInfo.findStuErr'))
+                        }
+                    ).finally(() => {
+                        this.stuLoading = false
+                    })
+                }
+            } else {
+                return []
+            }
+        },
+
+        years(){
+            if(this.schoolBase && this.schoolBase.period.length && this.filterPeriod){
+                let pData = this.schoolBase.period.find(item=>{
+                    return item.id == this.filterPeriod
+                })
+                if(pData){
+                    let date = new Date()
+                    let year = date.getFullYear()
+                    let month = date.getMonth() + 1
+                    let start = pData.semesters.find(item => {
+                        return item.start == 1
+                    })
+                    // 根据入学月份确定当前年级和学级的关系
+                    if (start && month < start.month) {
+                        year--
+                    }
+                    let res = pData.grades.map(item=>{
+                        return {
+                            label:`${item.name}(${year}级)`,
+                            value:year--
+                        }
+                    })
+                    this.filterYear = res[0].label
+                    return res
+                }else{
+                    return []
+                }
+            }
+            return []
+        }
+    },
+    methods: {
+        resetNo(index) {
+            this.editIndex = index
+            this.resetNoBef = this.students[index].no
+        },
+        confirmSetNo() {
+            this.$Message.warning('暂未对接API')
+            this.editIndex = -1
+        },
+        cancelSetNo() {
+            this.students[this.editIndex].no = this.resetNoBef
+            this.editIndex = -1
+        },
+        //移除班级里面的学生
+        removeStudent(index) {
+            let requestParams = {
+                schoolId: this.$store.state.userInfo.schoolCode,
+                students: [],
+                grant_type: 'remove'
+            }
+            let names = []
+            if (index > -1) {
+                requestParams.students.push(this.students[index].id)
+                names = [this.students[index].name]
+            } else if (this.delSelections.length > 0) {
+                requestParams.students = this.delSelections.map(item => {
+                    return item.id
+                })
+                names = this.delSelections.map(item => {
+                    return item.name
+                })
+            }
+            if (requestParams.students.length) {
+                this.$Modal.confirm({
+                    title: this.$t('schoolBaseInfo.removeTile'),
+                    content: `${this.$t('schoolBaseInfo.removeContent')}${names.join(', ')}?`,
+                    onOk: () => {
+                        this.$api.stuAccount.removeStudent(requestParams).then(
+                            res => {
+                                this.$Message.success(this.$t('schoolBaseInfo.removeOk'))
+                                if (index > -1) {
+                                    this.students.splice(index, 1)
+                                } else {
+                                    for (let i = 0; i < this.students.length; i) {
+                                        if (requestParams.students.indexOf(this.students[i].id) > -1) {
+                                            this.students.splice(i, 1)
+                                            i--
+                                        }
+
+                                    }
+                                    this.students.forEach()
+                                }
+                            },
+                            err => {
+                                this.$Message.error(this.$t('schoolBaseInfo.removeErr'))
+                            }
+                        )
+                    }
+                })
+            }
+        },
+        //确认添加学生
+        confirmAddStu() {
+            if (this.selections.length > 0) {
+                console.log(this.selections)
+                let data = this._.cloneDeep(this.selections)
+                data.forEach(item => {
+                    item.classId = this.classroomListShow[this.curClassIndex].id
+                    item.className = this.classroomListShow[this.curClassIndex].name
+                    item.classNo = this.classroomListShow[this.curClassIndex].no
+                })
+
+                this.listLoading = true
+                this.$api.stuAccount.updateStudent({
+                    students: data,
+                    grant_type: 'update',
+                    schoolId: this.$store.state.userInfo.schoolCode
+                }).then(
+                    (res) => {
+                        if (!res.error) {
+                            this.selections.length = 0
+                            this.students.push(...data)
+                            this.$Message.success(this.$t('schoolBaseInfo.addOk'))
+                        } else {
+                            this.$Message.error(this.$t('schoolBaseInfo.addErr'))
+                        }
+                    },
+                    (err) => {
+                        this.$Message.error(this.$t('schoolBaseInfo.addErr'))
+                    }
+                ).finally(() => {
+                    this.listLoading = false
+                    this.baseEditStatus = !this.baseEditStatus
+                })
+            }
+        },
+
+        dropdownStates(flag) {
+            if (!flag) this.filterByPeriod()
+        },
+        restOrderBy(flag) {
+            if (!flag) this.filterByPeriod()
+        },
+        closeKeySearch() {
+            this.isSearch = false
+            this.keyword = ''
+            this.filterClassname()
+        },
+        watchUpdate(data) {
+            if (data) {
+                this.updated = true
+            }
+        },
+        dataSort(data) {
+            switch (this.orderBy) {
+                case 'id': // no=>编号 排序
+                    data.sort(function (a, b) {
+                        let nameA = a.no.toUpperCase(); // ignore upper and lowercase
+                        let nameB = b.no.toUpperCase(); // ignore upper and lowercase
+                        if (nameA < nameB) {
+                            return -1;
+                        }
+                        if (nameA > nameB) {
+                            return 1;
+                        }
+
+                        // names must be equal
+                        return 0;
+                    })
+                    break;
+                case 'total':
+                    data.sort(function (a, b) {
+                        return b.studCount - a.studCount;
+                    })
+                    break;
+            }
+            return data
+        },
+        filterByPeriod() {
+            this.curClassIndex = 0
+            if (this.filterPeriod) {
+                this.classroomListShow = this.classroomList.filter(item => item.periodId == this.filterPeriod || !item.periodId)
+            } else {
+                this.classroomListShow = [...this.classroomList]
+            }
+            // 排序
+            this.classroomListShow = this.dataSort(this.classroomListShow)
+
+        },
+        filterClassname() {
+            if (this.keyword == '' || this.keyword == undefined) {
+                this.filterByPeriod()
+            } else {
+                let filterRes = []
+                if (this.filterPeriod) {
+                    filterRes = this.classroomList.filter(item => item.periodId == this.filterPeriod)
+                } else {
+                    filterRes = [...this.classroomList]
+                }
+                this.classroomListShow = filterRes.filter(item => item.name.indexOf(this.keyword) != -1)
+                // 排序
+                this.classroomListShow = this.dataSort(this.classroomListShow)
+                this.curClassIndex = 0
+                this.updateBefore = JSON.stringify(this.classroomListShow[this.curClassIndex])
+            }
+        },
+        selectTab(index) {
+            this.currentTabIndex = index
+        },
+        saveClassroom() {
+            this.$refs['classInfo'].validate((valid) => {
+                if (!valid) {
+                    this.$Message.error(this.$t('schoolBaseInfo.formWarning'))
+                } else {
+                    let option = this.classroomListShow[this.curClassIndex].option
+                    if (!option) {
+                        option = 'update'
+                    } else {
+                        this.classroomListShow[this.curClassIndex]['code'] = this.$store.state.userInfo.schoolCode
+                    }
+                    delete this.classroomListShow[this.curClassIndex].option
+                    this.isSaveLoading = true
+                    this.isListLoading = true
+                    if (this.classroomListShow[this.curClassIndex].openType == '2') {
+                        this.classroomListShow[this.curClassIndex].teacher.id = ''
+                        this.classroomListShow[this.curClassIndex].teacher.name = ''
+                    }
+                    this.$api.schoolSetting.classUpsert({
+                        classroom: this.classroomListShow[this.curClassIndex],
+                        option: option,
+                        school_code: this.$store.state.userInfo.schoolCode
+                    }).then(
+                        res => {
+                            if (res) {
+                                if (res.error) {
+                                    this.classroomListShow[this.curClassIndex].option = option
+                                    this.$Message.error(res.v)
+                                } else {
+                                    this.$Message.success(this.$t('schoolBaseInfo.csTips3'))
+                                    this.updated = false
+                                    if (option == 'insert') {
+                                        this.classroomListShow[this.curClassIndex].code = 'Class-' + this.classroomListShow[this.curClassIndex].code
+                                        this.classroomList.unshift(this.classroomListShow[this.curClassIndex])
+                                        this.$store.dispatch('user/addSchoolClasses', this.classroomListShow[this.curClassIndex]);
+                                    }
+                                }
+                            } else {
+                                this.$Message.error('API error!')
+                            }
+                        },
+                        err => {
+                            this.$Message.error('API error!')
+                        }
+                    ).finally(
+                        () => {
+                            this.isSaveLoading = false
+                            this.isListLoading = false
+                        }
+                    )
+                }
+            })
+        },
+        getClassroom() {
+            this.isListLoading = true
+            this.$store.dispatch('user/getSchoolProfile').then(
+                (res) => {
+                    if (res) {
+                        this.classroomList = res.school_classes
+                        this.schoolBase = res.school_base
+                        if (this.classroomList.length > 0) {
+                            this.updateBefore = JSON.stringify(this.classroomList[0])
+                        }
+                        this.filterClassname()
+                        // 預設搜尋給第一個
+                        if (this.periods) this.filterPeriod = this.periods[0].id
+                        this.filterByPeriod()
+                    }
+                },
+                (err) => {
+                    this.$Message.error('API error!')
+                }
+            ).finally(() => {
+                setTimeout(() => {
+                    this.isListLoading = false
+                }, 500)
+            })
+        },
+        /**显示确认删除班级对话框 */
+        showConfirmDelete() {
+            if (this.$access.can('admin.*|classroom-upd')) {
+                this.$Modal.confirm({
+                    title: this.$t('schoolBaseInfo.delClass'),
+                    content: this.$t('schoolBaseInfo.delete') + this.classroomListShow[this.curClassIndex].name + this.$t('schoolBaseInfo.delContent'),
+                    onOk: () => {
+                        this.delClassroom(this.curClassIndex)
+                    }
+                })
+            } else {
+                this.$Message.warning(this.$t('schoolBaseInfo.authWarning'))
+            }
+        },
+        delClassroom(index) {
+            this.isListLoading = true
+            if (this.classroomListShow[index].option !== 'insert') {
+                this.$api.schoolSetting.delClassroom({
+                    id: this.classroomListShow[index].id,
+                    scope: this.classroomListShow[index].scope,
+                    school_code: this.$store.state.userInfo.schoolCode
+                }).then(
+                    (res) => {
+                        if (res.error == null) {
+                            if (this.curClassIndex >= index && index > 0) {
+                                this.curClassIndex = 0
+                                this.updateBefore = JSON.stringify(this.classroomListShow[this.curClassIndex])
+                            }
+                            let originIndex = -1
+                            for (let i in this.classroomList) {
+                                if (this.classroomList[i].id == this.classroomListShow[index].id) {
+                                    originIndex = i
+                                    break
+                                }
+                            }
+                            this.$api.schoolSetting.hiteachUnlinkByClassId({
+                                classId: this.classroomListShow[index].id,
+                                school_code: this.$store.state.userInfo.schoolCode
+                            })                             
+                            this.$store.dispatch('user/delSchoolClasses', this.classroomListShow[index].id);
+                            this.classroomList.splice(originIndex, 1)
+                            this.classroomListShow.splice(index, 1)
+                            this.$Message.success(this.$t('schoolBaseInfo.csTips7'))
+                            this.updated = false
+                        }
+                    },
+                    (err) => {
+                        this.$Message.error('API error!')
+                    }
+                ).finally(() => {
+                    this.isListLoading = false
+                })
+            } else {
+                if (this.curClassIndex >= index && index > 0) {
+                    this.curClassIndex = 0
+                    this.updateBefore = JSON.stringify(this.classroomListShow[this.curClassIndex])
+                }
+                this.classroomListShow.splice(index, 1)
+                this.isListLoading = false
+                this.updated = false
+            }
+        },
+        chooseClassroom(index) {
+            if (index != this.curClassIndex) {
+                if (this.updated) {
+                    let config = {
+                        title: this.$t('schoolBaseInfo.saveWarning'),
+                        content: this.$t('schoolBaseInfo.saveClassWarning'),
+                        okText: this.$t('schoolBaseInfo.leaveText'),
+                        onOk: () => {
+                            if (this.classroomListShow[this.curClassIndex].option == 'insert') {
+                                this.delClassroom(this.curClassIndex)
+                            } else {
+                                this.updated = false
+                                this.$set(this.classroomListShow, this.curClassIndex, JSON.parse(this.updateBefore))
+                                this.curClassIndex = index
+                                this.updateBefore = JSON.stringify(this.classroomListShow[this.curClassIndex])
+                            }
+                        }
+                    }
+                    this.$Modal.confirm(config)
+                } else {
+                    this.curClassIndex = index
+                    this.updateBefore = JSON.stringify(this.classroomListShow[this.curClassIndex])
+                }
+            }
+        },
+        addClassroom() {
+            if (this.$access.can('admin.*|classroom-upd')) {
+                this.keyword = ''
+                this.classroomListShow.unshift({
+                    id: this.$jsFn.uuid(),
+                    name: this.$t('schoolBaseInfo.presetClassroomName') + (this.classroomList.length + 1),
+                    no: '',
+                    teacher: {
+                        id: '',
+                        name: ''
+                    },
+                    periodId: this.filterPeriod,
+                    gradeId: '',
+                    scope: 'school',
+                    option: 'insert'
+                })
+                this.curClassIndex = 0
+                this.updateBefore = JSON.stringify(this.classroomListShow[this.curClassIndex])
+            } else {
+                this.$Message.warning(this.$t('schoolBaseInfo.authWarning'))
+            }
+        },
+
+    },
+    mounted() {
+        this.filterClassname()
+        this.tableHeight = document.getElementById('class-mgt-list').clientHeight - 45
+    },
+    beforeRouteLeave(to, from, next) {
+        if (this.updated) {
+            let config = {
+                title: this.$t('schoolBaseInfo.saveWarning'),
+                content: this.$t('schoolBaseInfo.saveClassWarning'),
+                okText: this.$t('schoolBaseInfo.leaveText'),
+                onOk: () => {
+                    next()
+                },
+                onCancel: () => {
+                    next(false)
+                }
+            }
+            this.$Modal.confirm(config)
+        } else {
+            next()
+        }
+    },
+    created() {
+        this.$store.dispatch('user/getSchoolProfile').then(
+            res => {
+                this.schoolBase = res.school_base
+            }
+        )
+        if (this.$access.can('admin.*|classroom-upd')) this.editStatus = this.noStatus
+        this.getClassroom()
+        this.$store.dispatch('teachers/getTeacherList').then(res => { })
+    }
+}
+</script>
+<style lang="less" scoped>
+@import "./ClassMgt.less";
+</style>
+<style>
+.class-mgt-container .ivu-table-row-hover .item-tools {
+    display: inline-block;
+    cursor: pointer;
+}
+.class-info-content
+    .ivu-select-single
+    .ivu-select-selection
+    .ivu-select-placeholder,
+.class-info-content
+    .ivu-select-single
+    .ivu-select-selection
+    .ivu-select-selected-value {
+    color: white;
+}
+
+.class-search-input .ivu-input {
+    height: 30px;
+    background: none;
+    line-height: 30px;
+    border: none;
+    color: white;
+    font-size: 15px;
+}
+
+.class-attr-wrap .ivu-input {
+    width: calc(100% - 10px);
+    background: none;
+    border: none;
+    border-radius: 0px;
+    font-size: 16px;
+    color: white;
+    margin-left: 2px;
+    border-bottom: 1px solid #424242;
+}
+
+.class-attr-wrap .ivu-select {
+    width: calc(100% - 10px);
+    color: white;
+}
+
+.class-attr-wrap .ivu-select-selected-value {
+    font-size: 16px !important;
+}
+
+.class-attr-wrap .ivu-select-selection {
+    background: none;
+    border-color: #424242;
+}
+
+.class-attr-wrap .ivu-divider {
+    background-color: #424242;
+}
+
+</style>
+<style lang="less">
+.gradient {
+    @keyframes Itemfadein {
+        0% {
+            opacity: 0;
+            transform: translate(-40px);
+        }
+        100% {
+            opacity: 1;
+            transform: translate(0px);
+        }
+    }
+    animation: 0.5s Itemfadein;
+}
+
+.sort-dropdown {
+    .title {
+        color: white;
+        font-size: 14px;
+    }
+    .ivu-select-dropdown {
+        background-color: #2d2d2d;
+        border-radius: 2px;
+        border: 1px #464646 solid;
+        .ivu-dropdown-menu {
+            li {
+                color: #ccc;
+                font-size: 12px !important;
+                &:hover {
+                    color: #2d2d2d;
+                }
+            }
+        }
+    }
+}
+.requird-color {
+    .ivu-form-item-label:before {
+        color: #1cc0f3;
+    }
+}
+.hiteach-collapse {
+    //     background: transparent;
+    //     color: white;
+    //     border-top: none;
+    //     .ivu-collapse-item{
+    //         .ivu-collapse-header{
+    //             height: 200px;
+    //             line-height: 200px;
+    //             display: flex;
+    //             align-items: center;
+    //             padding-left: 15px;
+    .ivu-tag-border {
+        background: transparent !important;
+        margin-right: 10px;
+        border-radius: 5px;
+        letter-spacing: 1px;
+    }
+    //             .ivu-icon-ios-arrow-forward{
+    //                 position: absolute;
+    //                 right: 2px;
+    //                 font-size: 30px;
+    //                 color: white;
+    //             }
+    //         }
+    //         .ivu-collapse-content{
+    //             background: transparent;
+    //         }
+    //     }
+}
+</style>

+ 1 - 1
TEAMModelOS/ClientApp/src/view/student-account/ImportStudent.vue

@@ -12,7 +12,7 @@
             </Upload>
             <p class="import-tips" style="margin-top:40px;">{{$t('stuAccount.importTips1')}}</p>
             <p class="import-tips">{{$t('stuAccount.importTips2') + $t('stuAccount.importTips3')}}</p>
-            <a style="margin-top:10px;color:turquoise;display:inline-block;" href="https://teammodelstorage.blob.core.chinacloudapi.cn/teammodelcontest/20191107/%E5%AD%A6%E7%94%9F%E8%B4%A6%E5%8F%B7%E5%90%8D%E5%8D%95%20%E2%80%94%E6%AD%A3%E7%A1%AE%E6%95%B0%E6%8D%AE.xlsx">(下载名单模板)</a>
+            <a style="margin-top:10px;color:turquoise;display:inline-block;" href="https://teammodelstorage.blob.core.chinacloudapi.cn/download/%E5%AD%A6%E7%94%9F%E5%90%8D%E5%8D%95%E6%A8%A1%E6%9D%BF.xlsx">(下载名单模板)</a>
         </div>
         <div class="form-body" style="background:none;" v-else>
             <div class="ivu-upload-list-file">

+ 26 - 0
TEAMModelOS/ClientApp/src/view/student-account/Index.less

@@ -116,4 +116,30 @@
     line-height: 33px;
     font-size: 18px;
     font-weight: 600;
+}
+.stu-mgt-header{
+    height: 50px;
+    border-bottom: 1px solid #353535;
+    padding: 0 20px;
+    .tab-box {
+        display: inline-block;
+        width: 30%;
+        min-width: 303px;
+        .pane{
+            font-weight: bold;
+            margin-right: 50px;
+            color: #777777;
+            line-height: 50px;
+            padding: 5px 0;
+            cursor: pointer;
+            vertical-align: middle;
+            &.active{
+                color: #e2e2e2;
+                border-bottom: 1px #777777 solid;
+            }
+        }
+    }
+}
+.class-mgt-wrap{
+    height: ~"calc(100% - 50px)";
 }

+ 246 - 177
TEAMModelOS/ClientApp/src/view/student-account/Index.vue

@@ -1,105 +1,118 @@
 <style lang="less" scoped>
-@import './Index.less';
+@import "./Index.less";
 </style>
 <style lang="less">
-@import './IndexIview.less';
+@import "./IndexIview.less";
 </style>
 
 <template>
     <div class="sc-container">
         <Loading v-if="isloading == true"></Loading>
-        <!--菜单栏-->
-        <div class="sc-menu">
-            <div class="sc-menu-left dark-iview-select">
-                <!-- 學制Select -->
-                <Select v-model="searchPeriod" style="width: 120px" :placeholder="$t('stuAccount.periodHolder')" @on-change="filterData">
-                    <Option v-for="(item, index) in periods" :value="item.id" :key="index">{{ item.name }}</Option>
-                </Select>
-                <!-- 學級Select -->
-                <Select v-model="searchGrade" style="width: 120px; margin-left: 5px" :placeholder="$t('stuAccount.gradeHolder')" :not-found-text="$t('stuAccount.sltPdFirst')" clearable @on-change="filterData">
-                    <Option v-for="(item, index) in filterGrades" :value="item.id" :key="index">{{ item.name }}</Option>
-                </Select>
-                <!-- 教室Select -->
-                <Select v-model="searchClassroom" ref="classroom" style="width: 150px; margin-left: 5px" :placeholder="$t('stuAccount.classroomHolder')" clearable @on-change="filterData" :not-found-text="searchGrade ? $t('stuAccount.noClass') : $t('stuAccount.sltGdFirst')">
-                    <Option v-for="(item, index) in filterClasses" :value="item.id" :key="index">{{ item.name }}</Option>
-                    <Option value="noclass">{{$t('stuAccount.noRelClass')}}</Option>
-                </Select>
-                <!-- 字串模糊搜尋 -->
-                <Input v-model="searchText" clearable :placeholder="$t('stuAccount.searchHolder')" style="width: 220px; margin-left: 20px" search @on-search="filterData" @on-clear="filterData" />
-            </div>
-            <div class="sc-menu-right sc-text-no-select" v-if="authorizationStatus == false">
-                <ul v-if="$access.can('admin.*|student-upd')">
-                    <!--授權管理開關-->
-                    <li @click="showAuthorization()">
-                        <Icon custom="iconfont icon-auth-key" color="white" size="18" />
-                        <span>{{ $t('stuAccount.menuAuth') }}</span>
-                    </li>
-                    <!--授權管理開關-->
-                    <li @click="importStudent()">
-                        <Icon type="md-arrow-round-up" color="white" size="18" />
-                        <span>{{ $t('stuAccount.importStu') }}</span>
-                    </li>
-                    <li @click="addStudent()">
-                        <Icon type="md-add" color="white" size="18" />
-                        <span>{{ $t('stuAccount.addStu') }}</span>
-                    </li>
-                    <li @click="delStudent(-1)" :class="selections.length > 0 ? '' : 'sc-disable-cursor'">
-                        <Icon type="md-trash" :color="selections.length > 0 ? 'white' : '#606060'" size="18" />
-                        <span>{{ $t('stuAccount.delStu') }}</span>
-                    </li>
-                    <li @click="editStudent()" :class="selections.length > 0 ? '' : 'sc-disable-cursor'">
-                        <Icon type="md-create" :color="selections.length > 0 ? 'white' : '#606060'" size="18" />
-                        <span>{{ $t('stuAccount.editInfo') }}</span>
-                    </li>
-                    <li @click="resetPW(-1)" :class="selections.length > 0 ? '' : 'sc-disable-cursor'">
-                        <Icon custom="iconfont icon-reset" :color="selections.length > 0 ? 'white' : '#606060'" size="18" />
-                        <span>{{$t('stuAccount.resetPw')}}</span>
-                    </li>
-                </ul>
+        <!-- tab切换 -->
+        <div class="stu-mgt-header">
+            <div class="tab-box">
+                <span class="pane" @click="tab = 'list'" :class="{ active: tab === 'list' }">
+                    学生管理
+                </span>
+                <span class="pane" @click="tab = 'class'" :class="{ active: tab === 'class' }">
+                    班级管理
+                </span>
             </div>
         </div>
-        <!--学生账号table-->
-        <div class="sc-content dark-iview-table"
-             style="position: relative; padding-left: 20px"
-             :style="{width: authorizationStatus == true ? '70%' : '100%',transition: 'all 0.7s'}">
-            <Scroll :on-reach-bottom="scrollLoad" height="750" :distance-to-edge="[15, 15]">
-                <Table ref="selection" :columns="tableColumns" :data="tableShowData" :loading="tableLoading" @on-selection-change="getSelectInfo">
-                    <template slot-scope="{ row }" slot="status">
-                        <Icon custom="iconfont icon-auth-key"
-                              size="25"
-                              :style="{
+        <!-- 账号管理 -->
+        <div v-show="tab == 'list'">
+            <!--菜单栏-->
+            <div class="sc-menu">
+                <div class="sc-menu-left dark-iview-select">
+                    <!-- 學制Select -->
+                    <Select v-model="searchPeriod" style="width: 120px" :placeholder="$t('stuAccount.periodHolder')" @on-change="filterData">
+                        <Option v-for="(item, index) in periods" :value="item.id" :key="index">{{ item.name }}</Option>
+                    </Select>
+                    <!-- 學級Select -->
+                    <Select v-model="searchGrade" style="width: 150px; margin-left: 5px" :placeholder="$t('stuAccount.gradeHolder')" :not-found-text="$t('stuAccount.sltPdFirst')" clearable @on-change="filterData">
+                        <Option v-for="(item, index) in years" :value="item.value" :key="index">{{ item.label }}</Option>
+                    </Select>
+                    <!-- 教室Select -->
+                    <Select v-model="searchClassroom" ref="classroom" style="width: 150px; margin-left: 5px" :placeholder="$t('stuAccount.classroomHolder')" clearable @on-change="filterData" :not-found-text="searchGrade ? $t('stuAccount.noClass') : $t('stuAccount.sltGdFirst')">
+                        <Option v-for="(item, index) in filterClasses" :value="item.id" :key="index">{{ item.name }}</Option>
+                        <Option value="noclass">{{$t('stuAccount.noRelClass')}}</Option>
+                    </Select>
+                    <!-- 字串模糊搜尋 -->
+                    <Input v-model="searchText" clearable :placeholder="$t('stuAccount.searchHolder')" style="width: 220px; margin-left: 20px" search @on-search="filterData" @on-clear="filterData" />
+                </div>
+                <div class="sc-menu-right sc-text-no-select" v-if="authorizationStatus == false">
+                    <ul v-if="$access.can('admin.*|student-upd')">
+                        <!--授權管理開關-->
+                        <li @click="showAuthorization()">
+                            <Icon custom="iconfont icon-auth-key" color="white" size="18" />
+                            <span>{{ $t('stuAccount.menuAuth') }}</span>
+                        </li>
+                        <!--授權管理開關-->
+                        <li @click="importStudent()">
+                            <Icon type="md-arrow-round-up" color="white" size="18" />
+                            <span>{{ $t('stuAccount.importStu') }}</span>
+                        </li>
+                        <li @click="addStudent()">
+                            <Icon type="md-add" color="white" size="18" />
+                            <span>{{ $t('stuAccount.addStu') }}</span>
+                        </li>
+                        <li @click="delStudent(-1)" :class="selections.length > 0 ? '' : 'sc-disable-cursor'">
+                            <Icon type="md-trash" :color="selections.length > 0 ? 'white' : '#606060'" size="18" />
+                            <span>{{ $t('stuAccount.delStu') }}</span>
+                        </li>
+                        <li @click="editStudent()" :class="selections.length > 0 ? '' : 'sc-disable-cursor'">
+                            <Icon type="md-create" :color="selections.length > 0 ? 'white' : '#606060'" size="18" />
+                            <span>{{ $t('stuAccount.editInfo') }}</span>
+                        </li>
+                        <li @click="resetPW(-1)" :class="selections.length > 0 ? '' : 'sc-disable-cursor'">
+                            <Icon custom="iconfont icon-reset" :color="selections.length > 0 ? 'white' : '#606060'" size="18" />
+                            <span>{{$t('stuAccount.resetPw')}}</span>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+            <!--学生账号table-->
+            <div class="sc-content dark-iview-table" style="position: relative; padding-left: 20px" :style="{width: authorizationStatus == true ? '70%' : '100%',transition: 'all 0.7s'}">
+                <Scroll :on-reach-bottom="scrollLoad" height="750" :distance-to-edge="[15, 15]">
+                    <Table ref="selection" :columns="tableColumns" :data="tableShowData" :loading="tableLoading" @on-selection-change="getSelectInfo">
+                        <template slot-scope="{ row }" slot="status">
+                            <Icon custom="iconfont icon-auth-key" size="25" :style="{
                 color: aclassOneInfoNum != undefined && (aclassOneInfoNum.staIds.includes(row.id) == true || aclassOneInfoNum.dyncIds.includes(row.id)) == true ? '#00f492' : '#565656'
               }" />
-                    </template>
-                    <template slot-scope="{ row, index }" slot="header">
-                        <!--<span class="name-header" :style="{background:bgColor[index % 12]}">{{getFirstChart(row.name)}}</span>-->
-                        <PersonalPhoto :name="row.name" :picture="row.picture" :color="bgColor[index % 12]" />
-                    </template>
-                    <template slot-scope="{ row }" slot="gradeId" v-if="authorizationStatus == false">
-                        <span>{{ getGradeName(row.gradeId) }}</span>
-                    </template>
-                    <template slot-scope="{ row }" slot="classId">
-                        <span>{{ row.className }}</span>
-                    </template>
-                    <template slot-scope="{ row }" slot="no">
-                        <span>{{ row.no }}</span>
-                    </template>
-                    <template slot-scope="{ row }" slot="gradeName">
-                        <span>{{ row.gradeName }}</span>
-                    </template>
-                    <template slot-scope="{ row }" slot="action" v-if="authorizationStatus == false">
-                        <div class="item-tools" v-if="$access.can('admin.*|student-upd')">
-                            <Icon type="md-create" size="18" color="white" @click="editStudent(row)" title="修改" />
-                            <Icon custom="iconfont icon-reset" size="18" color="white" @click="resetPW(row)" :title=" $t('stuAccount.resetPw')" />
-                            <Icon type="md-trash" size="18" color="white" @click="delStudent(row)" :title="$t('stuAccount.delStu')" />
-                        </div>
-                    </template>
-                </Table>
-            </Scroll>
+                        </template>
+                        <template slot-scope="{ row, index }" slot="header">
+                            <!--<span class="name-header" :style="{background:bgColor[index % 12]}">{{getFirstChart(row.name)}}</span>-->
+                            <PersonalPhoto :name="row.name" :picture="row.picture" :color="bgColor[index % 12]" />
+                        </template>
+                        <template slot-scope="{ row }" slot="gradeId" v-if="authorizationStatus == false">
+                            <span>{{ getGradeName(row.gradeId) }}</span>
+                        </template>
+                        <template slot-scope="{ row }" slot="classId">
+                            <span>{{ row.className }}</span>
+                        </template>
+                        <template slot-scope="{ row }" slot="no">
+                            <span>{{ row.no }}</span>
+                        </template>
+                        <template slot-scope="{ row }" slot="gradeName">
+                            <span>{{ row.gradeName }}</span>
+                        </template>
+                        <template slot-scope="{ row }" slot="action" v-if="authorizationStatus == false">
+                            <div class="item-tools" v-if="$access.can('admin.*|student-upd')">
+                                <Icon type="md-create" size="18" color="white" @click="editStudent(row)" title="修改" />
+                                <Icon custom="iconfont icon-reset" size="18" color="white" @click="resetPW(row)" :title=" $t('stuAccount.resetPw')" />
+                                <Icon type="md-trash" size="18" color="white" @click="delStudent(row)" :title="$t('stuAccount.delStu')" />
+                            </div>
+                        </template>
+                    </Table>
+                </Scroll>
 
-            <AclassOneAuth :students="students" :selected="selections" @closeAuth="closeAuth" :closefromBtn="closefromBtn" />
+                <AclassOneAuth :students="students" :selected="selections" @closeAuth="closeAuth" :closefromBtn="closefromBtn" />
+            </div>
+        </div>
+        <!-- 班级管理 -->
+        <div v-show="tab == 'class'" class="class-mgt-wrap" id="class-mgt-list">
+            <ClassMgt></ClassMgt>
         </div>
-
         <Modal v-model="addStudentStatus" width="520" class-name="add-student dark-iview-modal" :mask-closable="false">
             <add-student v-if="addStudentStatus" :schoolCode="$store.state.user.schoolCode" :isShow="addStudentStatus" :editStudentInfo="editStudentInfo" @saveStudentInfo="closeAddStudent" :bizType="bizType"></add-student>
             <div slot="footer"></div>
@@ -127,11 +140,11 @@
 <script>
 import PersonalPhoto from '@/components/public/personalPhoto/Index.vue'
 import AddStudent from './AddStudent'
+import ClassMgt from './ClassMgt'
 import ImportStudent from './ImportStudent'
 import Authorization from './Authorization'
 import AclassOneAuth from './AclassOneAuth'
 import { mapGetters, mapMutations } from 'vuex'
-import Loading from '@/common/Loading.vue'
 export default {
     components: {
         AddStudent,
@@ -139,10 +152,11 @@ export default {
         Authorization,
         PersonalPhoto,
         AclassOneAuth,
-        Loading
+        ClassMgt
     },
     data() {
         return {
+            tab: 'list',
             totalNum: 0,
             bgColor: ['#4ECDC4', '#F99406', '#075CD0', '#F7CA17', '#F2774B', '#67809F', '#BF55ED', '#00B169', '#F72459', '#F15A22', '#03C9A8', '#9A1294'],
             classroomList: [],
@@ -151,7 +165,9 @@ export default {
             searchPeriod: '',
             searchGrade: '',
             searchClassroom: '',
-            schoolData: {},
+            schoolBase: {
+                period: []
+            },
             tableLoading: true,
             searchText: '',
             currentPage: 1,
@@ -184,11 +200,11 @@ export default {
             if (row != -1) {
                 this.$Modal.confirm({
                     title: this.$t('cusMgt.resetPw'),
-                    content: '<p>' + this.$t('cusMgt.resetPwContent1') + " <strong style='color:red;'>" + row.name + '</strong>'+ this.$t('cusMgt.resetPwContent1') +'?</p>',
+                    content: '<p>' + this.$t('cusMgt.resetPwContent1') + " <strong style='color:red;'>" + row.name + '</strong>' + this.$t('cusMgt.resetPwContent1') + '?</p>',
                     onOk: () => {
                         let ids = []
                         row.pw = row.id
-                        row.year = row.year ? row.year.toString() :''
+                        row.year = row.year ? row.year.toString() : ''
                         delete row.no
                         ids.push(row)
                         this.tableLoading = true
@@ -209,10 +225,10 @@ export default {
                 if (this.selections.length > 0) {
                     this.$Modal.confirm({
                         title: this.$t('cusMgt.resetPw'),
-                        content: '<p>' + this.$t('cusMgt.resetPwContent3') + " <strong style='color:red;'>" + this.selections.length + '</strong>'+ this.$t('cusMgt.resetPwContent4') +'</p>',
+                        content: '<p>' + this.$t('cusMgt.resetPwContent3') + " <strong style='color:red;'>" + this.selections.length + '</strong>' + this.$t('cusMgt.resetPwContent4') + '</p>',
                         onOk: () => {
                             this.tableLoading = true
-                            let apiData = this.selections.map( (item) =>{
+                            let apiData = this.selections.map((item) => {
                                 let d = item
                                 d.pw = d.id
                                 delete d.no
@@ -290,7 +306,7 @@ export default {
                 data.studentInfos.year = parseInt(data.studentInfos.year)
                 newStudents.push(data.studentInfos)
             } else if (data.action == 2) {  // 修改
-                newStudents = data.studentInfos.map(function(item) {
+                newStudents = data.studentInfos.map(function (item) {
                     let temp = item
                     temp.pw = '******'
                     temp.year = parseInt(temp.year)
@@ -304,7 +320,7 @@ export default {
         },
         closeImportStudent(data) {
             // 給一個 PW 讓AddStudent.vue 的Watch 可以動作
-            let newStudents = data.map(function(item) {
+            let newStudents = data.map(function (item) {
                 let temp = item
                 temp.pw = '******'
                 temp.year = parseInt(temp.year)
@@ -324,7 +340,7 @@ export default {
                 this.isloading = false
             }, 1000)
         },
-   
+
         addStudent() {
             this.bizType = 1
             this.$refs.selection.clearCurrentRow()
@@ -340,63 +356,63 @@ export default {
             this.isloading = true
             setTimeout(() => {
                 this.tableColumns = [
-                {
-                    type: 'selection',
-                    width: 80,
-                    align: 'center'
-                },
-                {
-                    slot: 'header',
-                    title: ' ',
-                    align: 'center',
-                    width: 120
-                },
-                {
-                    key: 'id',
-                    title: this.$t('stuAccount.account'),
-                    align: 'center',
-                    sortable: true,
-                    sortMethod: (a, b, type) => {
-                        if (type == 'asc') {
-                            return a.localeCompare(b)
-                        } else {
-                            return b.localeCompare(a)
+                    {
+                        type: 'selection',
+                        width: 80,
+                        align: 'center'
+                    },
+                    {
+                        slot: 'header',
+                        title: ' ',
+                        align: 'center',
+                        width: 120
+                    },
+                    {
+                        key: 'id',
+                        title: this.$t('stuAccount.account'),
+                        align: 'center',
+                        sortable: true,
+                        sortMethod: (a, b, type) => {
+                            if (type == 'asc') {
+                                return a.localeCompare(b)
+                            } else {
+                                return b.localeCompare(a)
+                            }
                         }
-                    }
-                },
-                {
-                    key: 'name',
-                    title: this.$t('stuAccount.stuName'),
-                    align: 'center',
-                    sortable: true
-                },
-                {
-                    slot: 'classId',
-                    title: this.$t('stuAccount.classroomName'),
-                    align: 'center',
-                    sortable: true
-                },
-                {
-                    key: 'no',
-                    title: this.$t('stuAccount.seatNo'),
-                    align: 'center',
-                    width: 120,
-                    sortable: true,
-                    sortMethod: (a, b, type) => {
-                        if (type == 'desc') {
-                            return parseInt(a) > parseInt(b) ? -1 : 1
-                        } else {
-                            return parseInt(a) < parseInt(b) ? -1 : 1
+                    },
+                    {
+                        key: 'name',
+                        title: this.$t('stuAccount.stuName'),
+                        align: 'center',
+                        sortable: true
+                    },
+                    {
+                        slot: 'classId',
+                        title: this.$t('stuAccount.classroomName'),
+                        align: 'center',
+                        sortable: true
+                    },
+                    {
+                        key: 'no',
+                        title: this.$t('stuAccount.seatNo'),
+                        align: 'center',
+                        width: 120,
+                        sortable: true,
+                        sortMethod: (a, b, type) => {
+                            if (type == 'desc') {
+                                return parseInt(a) > parseInt(b) ? -1 : 1
+                            } else {
+                                return parseInt(a) < parseInt(b) ? -1 : 1
+                            }
                         }
+                    },
+                    {
+                        slot: 'status',
+                        title: this.$t('stuAccount.authStatus'),
+                        align: 'center',
+                        width: 150,
+                        sortable: true
                     }
-                },
-                {
-                    slot: 'status',
-                    title: this.$t('stuAccount.authStatus'),
-                    align: 'center',
-                    width: 150,
-                    sortable: true
-                }
                 ]
                 this.isloading = false
             }, 1000)
@@ -459,7 +475,7 @@ export default {
                 if (this.selections.length > 0) {
                     this.$Modal.confirm({
                         title: this.$t('stuAccount.tips2Title'),
-                        content: '<p>' + this.$t('stuAccount.tips2Content3') + " <strong style='color:red;'>" + this.selections.length + '</strong>'+ this.$t('stuAccount.tips2Content4') +'</p>',
+                        content: '<p>' + this.$t('stuAccount.tips2Content3') + " <strong style='color:red;'>" + this.selections.length + '</strong>' + this.$t('stuAccount.tips2Content4') + '</p>',
                         onOk: () => {
                             this.tableLoading = true
                             let delIds = this.selections.map(item => {
@@ -517,15 +533,34 @@ export default {
                     sortable: true
                 },
                 {
-                    slot: 'gradeId',
-                    title: this.$t('stuAccount.grade'),
+                    key: 'stuId',
+                    title: '学号',
+                    align: 'center',
+                    sortable: true
+                },
+                // {
+                //     slot: 'gradeId',
+                //     title: this.$t('stuAccount.grade'),
+                //     align: 'center',
+                //     width: 140,
+                //     sortable: true
+                // },
+                {
+                    key: 'year',
+                    title: '学级',
                     align: 'center',
                     width: 140,
                     sortable: true
                 },
+                // {
+                //     slot: 'classId',
+                //     title: this.$t('stuAccount.classroomName'),
+                //     align: 'center',
+                //     sortable: true
+                // },
                 {
                     slot: 'classId',
-                    title: this.$t('stuAccount.classroomName'),
+                    title: '班级',
                     align: 'center',
                     sortable: true
                 },
@@ -547,7 +582,7 @@ export default {
                     slot: 'status',
                     title: this.$t('stuAccount.authStatus'),
                     align: 'center',
-                    width: 100,
+                    width: 120,
                     sortable: true
                 },
                 {
@@ -564,7 +599,7 @@ export default {
                 res => {
                     this.tableLoading = false
                     // 給一個 PW 讓AddStudent.vue 的Watch 可以動作
-                    let newStudents = res.students.map(function(item) {
+                    let newStudents = res.students.map(function (item) {
                         let temp = item
                         temp.pw = '******'
                         return temp
@@ -587,9 +622,9 @@ export default {
             )
         },
         /** 取得年級Name */
-        getGradeName: function(gradeId) {
+        getGradeName: function (gradeId) {
             if (gradeId != null) {
-                let gradeData = this.grades.filter(function(item) {
+                let gradeData = this.grades.filter(function (item) {
                     return item.id == gradeId
                 })
                 return gradeData[0].name
@@ -610,7 +645,7 @@ export default {
             )
         },
 
-        scrollLoad: function() {
+        scrollLoad: function () {
             setTimeout(() => {
                 let maxLength = this.tableShowData.length
                 let temp = this.tableFilterData.slice(this.pointNum + 1, this.pointNum + this.basicCount)
@@ -625,14 +660,14 @@ export default {
             }, 1500)
         },
         /**根据学段、年级、班级搜索 */
-        filterData: function() {
+        filterData: function () {
             let data = this.students
 
             // 筛选没有关联班级的学生
             if (this.searchClassroom == 'noclass') {
                 if (this.searchClassroom) {
                     let id = this.searchClassroom
-                    data = data.filter(function(item) {
+                    data = data.filter(function (item) {
                         return item.classId == null
                     })
                 }
@@ -646,7 +681,7 @@ export default {
             // 帳號資訊搜尋
             if (this.searchText) {
                 let text = this.searchText
-                data = data.filter(function(item) {
+                data = data.filter(function (item) {
                     return item.name.indexOf(text) >= 0 || item.id.indexOf(text) >= 0
                 })
             }
@@ -654,23 +689,23 @@ export default {
             // 學制篩選
             if (this.searchPeriod) {
                 let id = this.searchPeriod
-                data = data.filter(function(item) {
-                return item.periodId == id
+                data = data.filter(function (item) {
+                    return item.periodId == id
                 })
             }
 
             // 年級篩選
             if (this.searchGrade) {
                 let id = this.searchGrade
-                data = data.filter(function(item) {
-                    return item.gradeId == id
+                data = data.filter(function (item) {
+                    return item.year == id
                 })
             }
 
             // 教室篩選
             if (this.searchClassroom) {
                 let id = this.searchClassroom
-                data = data.filter(function(item) {
+                data = data.filter(function (item) {
                     return item.classId == id
                 })
             }
@@ -684,6 +719,11 @@ export default {
         })
     },
     created() {
+        this.$store.dispatch('user/getSchoolProfile').then(
+            res => {
+                this.schoolBase = res.school_base
+            }
+        )
         this.initData()
         this.baseFindStudent()
     },
@@ -695,11 +735,11 @@ export default {
             students: 'schoolBaseInfo/getStudent', // 學生List
             aclassOneInfoNum: 'studentAclassOneAuth/getSchoolAclassOneInfoNum'
         }),
-        filterGrades: function() {
+        filterGrades: function () {
             var data = this.grades
             if (this.searchPeriod) {
                 let periodId = this.searchPeriod
-                data = data.filter(function(item) {
+                data = data.filter(function (item) {
                     return item.periodId == periodId
                 })
                 return data
@@ -707,11 +747,11 @@ export default {
                 return []
             }
         },
-        filterClasses: function() {
+        filterClasses: function () {
             var data = this.classes
             if (this.searchGrade) {
                 let gradeId = this.searchGrade
-                data = data.filter(function(item) {
+                data = data.filter(function (item) {
                     return item.gradeId == gradeId
                 })
                 return data
@@ -719,20 +759,49 @@ export default {
                 return []
             }
         },
-        getPeriodName: function() {
+        getPeriodName: function () {
             let str = ''
-            if(this.periods){
+            if (this.periods) {
                 let id = this.searchPeriod
                 let period = this.periods.filter((item) => {
                     return item.id == id
                 })
-                if(period[0]) str = period[0].name
+                if (period[0]) str = period[0].name
             }
             return str
+        },
+        years() {
+            if (this.schoolBase && this.schoolBase.period.length && this.searchPeriod) {
+                let pData = this.schoolBase.period.find(item => {
+                    return item.id == this.searchPeriod
+                })
+                if (pData) {
+                    let date = new Date()
+                    let year = date.getFullYear()
+                    let month = date.getMonth() + 1
+                    let start = pData.semesters.find(item => {
+                        return item.start == 1
+                    })
+                    // 根据入学月份确定当前年级和学级的关系
+                    if (start && month < start.month) {
+                        year--
+                    }
+                    let res = pData.grades.map(item => {
+                        return {
+                            label: `${item.name}(${year}级)`,
+                            value: year--
+                        }
+                    })
+                    return res
+                } else {
+                    return []
+                }
+            }
+            return []
         }
     },
     watch: {
-        selections: function(value) {
+        selections: function (value) {
             console.log(value)
         }
     },

+ 6 - 6
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/EvaluationList/TotalIndex.vue

@@ -138,7 +138,7 @@
 									{{ item.stuCount }}</span>
 								<span>{{ $t("totalAnalysis.echarts_text13") }}:
 									<!-- 暂无实际参与人数数据 -->
-									{{ item.stuCount }}
+									{{ item.stuCount - item.lostStu.length }}
 								</span>
 								<span>{{ $t("totalAnalysis.lostStu") }}:
 									<!-- 缺考人数数据 -->
@@ -273,8 +273,8 @@
 							this.filterData.typeList = this.typeList;
 						}
 						
-						if(localStorage.getItem('filterConditions')){
-							this.filterConditions = JSON.parse(localStorage.getItem('filterConditions'))
+						if(sessionStorage.getItem('filterConditions')){
+							this.filterConditions = JSON.parse(sessionStorage.getItem('filterConditions'))
 							this.filterPeriod = this.filterConditions.period || this.$t('totalAnalysis.all')
 							this.filterGrade = this.filterConditions.grade || this.$t('totalAnalysis.all')
 							this.filterSubject = this.filterConditions.subject || this.$t('totalAnalysis.all')
@@ -283,7 +283,7 @@
 						}
 						console.log(this.filterConditions)
 						//默认选中第一个学段
-						if (this.schoolData.period && this.schoolData.period.length && !localStorage.getItem('filterConditions')) {
+						if (this.schoolData.period && this.schoolData.period.length && !sessionStorage.getItem('filterConditions')) {
 								this.filterPeriod = this.schoolData.period[0].name
 								this.filterPeriodChange()
 						}
@@ -452,7 +452,7 @@
 			},
 
 			doFilter() {
-				localStorage.setItem('filterConditions',JSON.stringify(this.filterConditions))
+				sessionStorage.setItem('filterConditions',JSON.stringify(this.filterConditions))
 				// 将条件转换成数组便于渲染
 				this.filterList = this.objToArr(this.filterConditions);
 				// 根据条件过滤所有的测验数据
@@ -547,7 +547,7 @@
 		computed: {
 			getJoinRate() {
 				return (item) => {
-					return item.stuCount === 0 ? 0 : (item.stuCount / item.stuCount).toFixed(2) * 100 + "%";
+					return item.stuCount === 0 ? 0 : ((item.stuCount - item.lostStu.length) / item.stuCount).toFixed(2) * 100 + "%";
 				};
 			},
 			filterGradeList() {

+ 9 - 6
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/KnowledgeAnalysis/KnowledgeAnalysis.vue

@@ -1,16 +1,18 @@
 <template>
     <div class="scatter-container">
-		<div v-if="!hasKnowledge" class="analysis-no-data">
-			<EmptyData :top="200" textContent="本次考试未收集到知识点数据"></EmptyData>
+		<div v-show="!hasKnowledge" class="analysis-no-data">
+			<EmptyData :top="200" :textContent="$t('totalAnalysis.noKnowPointTip')"></EmptyData>
 		</div>
-		<div v-else>
+		<div v-show='hasKnowledge'>
 			<Row>
 			    <Col span="12">
 			        <span class="component-title">{{$t('totalAnalysis.ka_title1')}}</span>
 			        <BasePie pieId="knowPie"></BasePie>
 			    </Col>
 			    <Col span="12" style="position:relative">
-			        <BaseKnowledgeRadar echartsId="knowPie2"></BaseKnowledgeRadar>
+			        <!-- <BaseKnowledgeRadar echartsId="knowPie2"></BaseKnowledgeRadar> -->
+					<span class="component-title">{{$t('totalAnalysis.ka_title7')}}</span>
+					<BaseRadar echartsId="knowRadar" :classIndex="isAllClasses"></BaseRadar>
 			    </Col>
 			</Row>
 			<Row class-name="base-table-row" v-show="isAllClasses === 0">
@@ -31,12 +33,13 @@
 
 <script>
     import BasePie from '@/components/student-analysis/total/BasePie.vue'
+	import BaseRadar from '@/components/student-analysis/total/BaseRadar.vue'
     import BaseKnowledgeRadar from '@/components/student-analysis/total/BaseKnowledgeRadar.vue'
     import BaseMyTable from '@/components/student-analysis/total/BaseMyTable.vue'
     import ScoreDetails from '@/view/student-analysis/total-analysis/KnowledgeAnalysis/ScoreDetails.vue'
     export default {
         components: {
-            BasePie, BaseKnowledgeRadar, BaseMyTable, ScoreDetails
+            BasePie, BaseKnowledgeRadar, BaseMyTable, ScoreDetails,BaseRadar
         },
         data() {
             return {
@@ -81,7 +84,7 @@
 				        sortable: 'custom',
 				        key: item.className,
 				        renderType: (h, params) => {
-				            return h('span', ((Number(origin[params.row.name][index]))).toFixed(2) + '%')
+				            return h('span', (( isNaN(origin[params.row.name][index]) ? 0 : Number(origin[params.row.name][index]))).toFixed(2) + '%')
 				        },
 				        minWidth: 150
 				    }

+ 15 - 5
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/KnowledgeAnalysis/ScoreDetails.vue

@@ -3,17 +3,17 @@
         <Row>
             <Col span="12">
                 <span class="component-title">{{$t('totalAnalysis.ka_title4')}}</span>
-                <span class="pie-to-bar" @click="changeRadarOrBar"><Icon :type="isShowRadar ? 'ios-podium' : 'ios-pie'" />{{isShowRadar?$t('totalAnalysis.ka_chart_text1'):$t('totalAnalysis.ka_chart_text2')}}</span>
-                <div v-if="!isShowRadar">
+                <!-- <span class="pie-to-bar" @click="changeRadarOrBar"><Icon :type="isShowRadar ? 'ios-podium' : 'ios-pie'" />{{isShowRadar?$t('totalAnalysis.ka_chart_text1'):$t('totalAnalysis.ka_chart_text2')}}</span> -->
+                <div>
                     <BaseDetailBar echartsId="knowDetailBar" :classIndex="classIndex"  @handleItemClick="handleItemClick" ref="detailsBar"></BaseDetailBar>
                 </div>
-                <div v-if="isShowRadar">
+               <!-- <div v-if="isShowRadar">
                     <BaseRadar echartsId="knowRadar" :classIndex="classIndex"></BaseRadar>
-                </div>
+                </div> -->
             </Col>
             <Col span="12"  style="padding: 30px 100px 0 0">
             <!-- 知识点得分详情 -->
-                <!-- <span class="component-title"><span style="font-size:14px;font-weight:bold;margin-left:20px;color:#66cccc">{{$t('totalAnalysis.ka_text2')}}:{{currentPoint}}</span></span> -->
+                <span class="component-title-point"><span>{{$t('totalAnalysis.ka_text2')}}:{{currentPoint}}</span></span>
                 <div>
                     <BaseMyTable :columns="detailsColumns"
                                  :tableName="$t('totalAnalysis.ka_title5')"
@@ -247,3 +247,13 @@
 </script>
 
 <style src="./KnowledgeAnalysis.css" scoped></style>
+<style>
+	.component-title-point{
+		position: absolute;
+		font-size: 14px;
+		font-weight: 600;
+		right: 200px;
+		top: 40px;
+		color: #47dbfc;
+	}
+</style>

+ 5 - 2
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/LevelAnalysis/LevelAnalysis.vue

@@ -6,7 +6,9 @@
                 <BasePie pieId="levelPie"></BasePie>
             </Col>
             <Col span="12" style="position:relative">
-                <BaseLevelRadar echartsId="levelPie2" v-if="isShowPie"></BaseLevelRadar>
+                <!-- <BaseLevelRadar echartsId="levelPie2" v-if="isShowPie"></BaseLevelRadar> -->
+				<span class="component-title">{{$t('totalAnalysis.le_title7')}}</span>
+				<BaseRadar echartsId="levelRadar" :classIndex="isAllClasses"></BaseRadar>
             </Col>
         </Row>
         <Row class-name="base-table-row"  v-show="isAllClasses === 0">
@@ -21,13 +23,14 @@
 </template>
 
 <script>
+	import BaseRadar from '@/components/student-analysis/total/BaseRadar.vue'
     import BasePie from '@/components/student-analysis/total/BaseLevelPie.vue'
     import BaseLevelRadar from '@/components/student-analysis/total/BaseLevelRadar.vue'
     import BaseMyTable from '@/components/student-analysis/total/BaseMyTable.vue'
     import ScoreDetails from '@/view/student-analysis/total-analysis/LevelAnalysis/ScoreDetails.vue'
     export default {
         components: {
-            BasePie, BaseLevelRadar, BaseMyTable, ScoreDetails
+            BasePie,BaseRadar, BaseLevelRadar, BaseMyTable, ScoreDetails
         },
         data() {
             return {

+ 18 - 9
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/LevelAnalysis/ScoreDetails.vue

@@ -3,21 +3,20 @@
 		<Row>
 			<Col span="12">
 			<span class="component-title">{{$t('totalAnalysis.le_title4')}}</span>
-			<span class="pie-to-bar" @click="changeRadarOrBar">
+			<!-- <span class="pie-to-bar" @click="changeRadarOrBar">
 				<Icon :type="isShowRadar ? 'ios-podium' : 'ios-pie'" />
 				{{isShowRadar?$t('totalAnalysis.ka_chart_text1'):$t('totalAnalysis.ka_chart_text2')}}
-			</span>
-			<div v-if="!isShowRadar">
+			</span> -->
+			<div>
 				<BaseDetailBar echartsId="levelDetailBar" :classIndex="classIndex" @handleItemClick="handleItemClick"
-					ref="detailsBar"></BaseDetailBar>
+					ref="levelDetailBar"></BaseDetailBar>
 			</div>
-			<div v-if="isShowRadar">
+<!-- 			<div v-if="isShowRadar">
 				<BaseRadar echartsId="levelRadar" :classIndex="classIndex"></BaseRadar>
-			</div>
+			</div> -->
 			</Col>
 			<Col span="12">
-			<!-- <span class="component-title">{{$t('totalAnalysis.le_title5')}}<span
-					style="font-size:14px;font-weight:bold;margin-left:20px;color:#66cccc">{{$t('totalAnalysis.ka_text3')}}:{{transArr[+currentPoint-1]}}</span></span> -->
+			<span class="component-title-point"><span>{{$t('totalAnalysis.ka_text3')}}:{{transArr[+currentPoint-1]}}</span></span>
 			<div style="padding:30px 100px 0 0">
 				<BaseMyTable :columns="detailsColumns" :tableName="$t('totalAnalysis.le_title5')"
 					tableRef="levelScoreTable" :tableDatas="tableData" ref="detailsTable"></BaseMyTable>
@@ -33,7 +32,7 @@
 </template>
 
 <script>
-	import BaseDetailBar from '@/components/student-analysis/total/BaseKnowledgeDetail.vue'
+	import BaseDetailBar from '@/components/student-analysis/total/BaseLevelDetail.vue'
 	import BaseMyTable from '@/components/student-analysis/total/BaseMyTable.vue'
 	import BaseRadar from '@/components/student-analysis/total/BaseRadar.vue'
 	export default {
@@ -266,3 +265,13 @@
 </script>
 
 <style src="./LevelAnalysis.css" scoped></style>
+<style>
+	.component-title-point{
+		position: absolute;
+		font-size: 14px;
+		font-weight: 600;
+		right: 200px;
+		top: 40px;
+		color: #47dbfc;
+	}
+</style>

+ 0 - 0
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/ScatterAnalysis/ScatterAnalysis.vue


部分文件因文件數量過多而無法顯示