Browse Source

Merge remote-tracking branch 'origin/TPE/develop' into TPE/develop

osbert 4 years ago
parent
commit
ea8d74947d
100 changed files with 4225 additions and 1882 deletions
  1. 40 0
      TEAMModelFunction/ClassChangeServiceBus.cs
  2. 6 5
      TEAMModelFunction/MonitorCosmosDB.cs
  3. 1 1
      TEAMModelFunction/ExamTrigger.cs
  4. 38 2
      TEAMModelFunction/SurveyTrigger.cs
  5. 164 0
      TEAMModelFunction/TriggerVote.cs
  6. 0 79
      TEAMModelFunction/VoteTrigger.cs
  7. 14 10
      TEAMModelOS.SDK/DI/AzureStorage/AzureStorageBlobExtensions.cs
  8. 70 0
      TEAMModelOS.SDK/Models/Cosmos/Common/ActivityData.cs
  9. 0 21
      TEAMModelOS.SDK/Models/Cosmos/Common/CommonData.cs
  10. 12 0
      TEAMModelOS.SDK/Models/Cosmos/Common/Inner/VoteRecord.cs
  11. 77 37
      TEAMModelOS.SDK/Models/Cosmos/School/Survey.cs
  12. 1 12
      TEAMModelOS.SDK/Models/Cosmos/Common/Syllabus.cs
  13. 76 46
      TEAMModelOS.SDK/Models/Cosmos/Common/Vote.cs
  14. 8 1
      TEAMModelOS.SDK/Models/Cosmos/School/Class.cs
  15. 23 13
      TEAMModelOS.SDK/Models/Cosmos/School/Knowledge.cs
  16. 23 23
      TEAMModelOS.SDK/Models/Cosmos/Student/VoteRecord.cs
  17. 1 1
      TEAMModelOS/ClientApp/package.json
  18. 1 1
      TEAMModelOS/ClientApp/public/index.html
  19. 8 1
      TEAMModelOS/ClientApp/src/api/uploadFile.js
  20. 2 2
      TEAMModelOS/ClientApp/src/api/index.js
  21. 253 0
      TEAMModelOS/ClientApp/src/assets/iconfont/demo_index.html
  22. 51 8
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.css
  23. BIN
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.eot
  24. 1 1
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.js
  25. 77 0
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.json
  26. 61 28
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.svg
  27. BIN
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.ttf
  28. BIN
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff
  29. BIN
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff2
  30. 1 1
      TEAMModelOS/ClientApp/src/boot-app.js
  31. 88 42
      TEAMModelOS/ClientApp/src/common/BaseCanvas.vue
  32. 398 0
      TEAMModelOS/ClientApp/src/common/BaseKonva.vue
  33. 74 56
      TEAMModelOS/ClientApp/src/common/BaseMyCanvas.vue
  34. 1 1
      TEAMModelOS/ClientApp/src/common/BasePackage.vue
  35. 2 4
      TEAMModelOS/ClientApp/src/common/BaseUserPoptip.vue
  36. 3 2
      TEAMModelOS/ClientApp/src/common/UploadModal.vue
  37. 15 2
      TEAMModelOS/ClientApp/src/components/evaluation/ExerciseList.vue
  38. 1 1
      TEAMModelOS/ClientApp/src/components/homework/BaseHwTable.vue
  39. 1 1
      TEAMModelOS/ClientApp/src/components/selflearn/ContentFileList.vue
  40. 3 15
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseMyTable.vue
  41. 25 26
      TEAMModelOS/ClientApp/src/components/student-web/EventBasicInfo.vue
  42. 122 77
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReport.vue
  43. 51 96
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReportCharts/StudentScore.vue
  44. 4 4
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperTest.vue
  45. 3 1
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperView.vue
  46. 2 0
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventList.vue
  47. 105 0
      TEAMModelOS/ClientApp/src/locale/lang/en-US/cusMgt.js
  48. 46 4
      TEAMModelOS/ClientApp/src/locale/lang/en-US/global.js
  49. 4 0
      TEAMModelOS/ClientApp/src/locale/lang/en-US/index.js
  50. 188 170
      TEAMModelOS/ClientApp/src/locale/lang/en-US/learnActivity.js
  51. 47 3
      TEAMModelOS/ClientApp/src/locale/lang/en-US/studentWeb.js
  52. 53 32
      TEAMModelOS/ClientApp/src/locale/lang/en-US/teachContent.js
  53. 106 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/cusMgt.js
  54. 3 3
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/evaluation.js
  55. 35 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/global.js
  56. 2 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/index.js
  57. 18 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/learnActivity.js
  58. 46 7
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/studentWeb.js
  59. 25 4
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/teachContent.js
  60. 107 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/cusMgt.js
  61. 2 2
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/evaluation.js
  62. 46 4
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/global.js
  63. 4 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/index.js
  64. 19 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/learnActivity.js
  65. 46 2
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/studentWeb.js
  66. 26 5
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/teachContent.js
  67. 1 1
      TEAMModelOS/ClientApp/src/router/routes.js
  68. 1 1
      TEAMModelOS/ClientApp/src/store/index.js
  69. 12 6
      TEAMModelOS/ClientApp/src/store/module/totalAnalysis.js
  70. 235 32
      TEAMModelOS/ClientApp/src/utils/blobTool.js
  71. 2 1
      TEAMModelOS/ClientApp/src/utils/editorTools.js
  72. 20 10
      TEAMModelOS/ClientApp/src/utils/evTools.js
  73. 6 6
      TEAMModelOS/ClientApp/src/utils/public.js
  74. 392 398
      TEAMModelOS/ClientApp/src/view/classmgt/ManageClass.vue
  75. 2 2
      TEAMModelOS/ClientApp/src/view/classrecord/ClassRecord.vue
  76. 33 37
      TEAMModelOS/ClientApp/src/view/evaluation/bank/ExerciseList.vue
  77. 1 2
      TEAMModelOS/ClientApp/src/view/evaluation/bank/TestPaperList.vue
  78. 1 1
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseCreateChild.vue
  79. 4 3
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseEditExercise.vue
  80. 52 31
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseExerciseList.vue
  81. 11 4
      TEAMModelOS/ClientApp/src/view/evaluation/components/BasePointPie.vue
  82. 1 1
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseRepair.vue
  83. 16 8
      TEAMModelOS/ClientApp/src/view/evaluation/index/CreateExercises.vue
  84. 116 41
      TEAMModelOS/ClientApp/src/view/evaluation/index/CreatePaper.vue
  85. 1 1
      TEAMModelOS/ClientApp/src/view/evaluation/types/BaseConnector.vue
  86. 93 79
      TEAMModelOS/ClientApp/src/view/homepage/AcCountPie.vue
  87. 6 6
      TEAMModelOS/ClientApp/src/view/homepage/HomePage.less
  88. 9 10
      TEAMModelOS/ClientApp/src/view/homepage/HomePage.vue
  89. 111 101
      TEAMModelOS/ClientApp/src/view/homepage/TeachScore.vue
  90. 86 76
      TEAMModelOS/ClientApp/src/view/homepage/TechScore.vue
  91. 5 2
      TEAMModelOS/ClientApp/src/view/learnactivity/AutoCreateNew.vue
  92. 96 32
      TEAMModelOS/ClientApp/src/view/learnactivity/CreatePrivEva.vue
  93. 118 103
      TEAMModelOS/ClientApp/src/view/learnactivity/CreateSchoolEva.vue
  94. 9 8
      TEAMModelOS/ClientApp/src/view/learnactivity/ManualCreate.vue
  95. 10 11
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtPrivEva.vue
  96. 5 1
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.vue
  97. 2 0
      TEAMModelOS/ClientApp/src/view/learnactivity/PaperScore.less
  98. 1 4
      TEAMModelOS/ClientApp/src/view/learnactivity/PaperScore.vue
  99. 37 5
      TEAMModelOS/ClientApp/src/view/learnactivity/Scoring.vue
  100. 0 0
      TEAMModelOS/ClientApp/src/view/learnactivity/SimpleAnalysis.vue

+ 40 - 0
TEAMModelFunction/ClassChangeServiceBus.cs

@@ -0,0 +1,40 @@
+using Microsoft.Azure.WebJobs;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.DI;
+
+namespace TEAMModelFunction
+{
+    public class ClassChangeServiceBus
+    {
+        private readonly AzureCosmosFactory _azureCosmos;
+        private readonly DingDing _dingDing;
+        delegate void DoActivityTarget(string ids,string opt,string no,string source);
+        public ClassChangeServiceBus(AzureCosmosFactory azureCosmos, DingDing dingDing)
+        {
+            _azureCosmos = azureCosmos;
+            _dingDing = dingDing;
+        }
+        /// <summary>
+        /// 完善学生名单变更影响的活动
+        /// </summary>
+        /// <data msg>
+        /// "ids":["s111","t111"]//学生
+        /// "opt":"join/leave",//状态
+        /// "no":"CLASS001"//教室编号
+        /// "source":1/2  //学生名单数据来源 1是不同学校的学生账号,2是扫码加入的醍摩豆ID
+        /// </data>
+        /// <param name="msg"></param>
+        /// <returns></returns>
+        [FunctionName("ImproveActivity")]
+        public async Task ImproveActivity([ServiceBusTrigger("active-task", "classchange", Connection = "Azure:ServiceBus:ConnectionString")] string msg) {
+            try {
+                var json = JsonDocument.Parse(msg);
+
+            } catch (Exception e) { }
+        }
+    }
+}

+ 6 - 5
TEAMModelFunction/MonitorCosmosDB.cs

@@ -21,14 +21,15 @@ namespace TEAMModelFunction
         private readonly AzureServiceBusFactory _serviceBus;
         private readonly AzureStorageFactory _azureStorage;
         private readonly DingDing _dingDing;
-
-        public MonitorCosmosDB(IHttpClientFactory clientFactory, AzureCosmosFactory azureCosmos, AzureServiceBusFactory azureServiceBus, AzureStorageFactory azureStorage, DingDing dingDing)
+        private readonly AzureRedisFactory _azureRedis;
+        public MonitorCosmosDB(IHttpClientFactory clientFactory, AzureCosmosFactory azureCosmos, AzureServiceBusFactory azureServiceBus, AzureStorageFactory azureStorage, DingDing dingDing, AzureRedisFactory azureRedis)
         {
             _clientFactory = clientFactory;
             _azureCosmos = azureCosmos;
             _serviceBus = azureServiceBus;
             _azureStorage = azureStorage;
             _dingDing = dingDing;
+            _azureRedis = azureRedis;
         }
 
         [FunctionName("Common")]
@@ -60,13 +61,13 @@ namespace TEAMModelFunction
                         switch (pk)
                         {
                             case "Exam":
-                                ExamTrigger.Trigger(_azureCosmos, _serviceBus, _azureStorage, _dingDing, client,input,code,stime,etime,school);
+                                TriggerExam.Trigger(_azureCosmos, _serviceBus, _azureStorage, _dingDing, client,input,code,stime,etime,school);
                                 break;
                             case "Vote":
-                                VoteTrigger.Trigger(_azureCosmos, _serviceBus, _azureStorage, _dingDing, client, input, code, stime, etime, school);
+                                TriggerVote.Trigger(_azureCosmos, _serviceBus, _azureStorage, _dingDing, client, input, code, stime, etime, school, _azureRedis);
                                 break;
                             case "Survey":
-                                SurveyTrigger.Trigger(_azureCosmos, _serviceBus, _azureStorage, _dingDing, client, input, code, stime, etime, school);
+                                TriggerSurvey.Trigger(_azureCosmos, _serviceBus, _azureStorage, _dingDing, client, input, code, stime, etime, school);
                                 break;
 
                         }

+ 1 - 1
TEAMModelFunction/ExamTrigger.cs

@@ -12,7 +12,7 @@ using TEAMModelOS.SDK.Models;
 
 namespace TEAMModelFunction
 {
-    public class ExamTrigger
+    public class TriggerExam
     {
         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)

+ 38 - 2
TEAMModelFunction/SurveyTrigger.cs

@@ -9,10 +9,11 @@ using System.Threading.Tasks;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK.Models.Cosmos;
 
 namespace TEAMModelFunction
 {
-   public class SurveyTrigger
+   public class TriggerSurvey
     {
         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)
@@ -50,6 +51,42 @@ namespace TEAMModelFunction
                     }
                     break;
                 case "going":
+                    var tcode = code.Replace("Survey-", "");
+                    ActivityData data;
+                    if (survey.scope == "school" || survey.scope == "teacher")
+                    {
+                        data = new ActivityData
+                        {
+                            id = survey.id,
+                            code = $"Activity-{tcode}",
+                            type = "survey",
+                            name = survey.name,
+                            startTime = survey.startTime,
+                            endTime = survey.endTime,
+                            scode = survey.code,
+                            scope = survey.scope,
+                            classes = survey.classes,
+                            tmdids = survey.tmdids
+                        };
+                        await client.GetContainer("TEAMModelOS", "School").UpsertItemAsync<ActivityData>(data, new Azure.Cosmos.PartitionKey(data.code));
+                    }
+                    else if (survey.scope == "private")
+                    {
+                        data = new ActivityData
+                        {
+                            id = survey.id,
+                            code = $"Activity-Common",
+                            type = "survey",
+                            name = survey.name,
+                            startTime = survey.startTime,
+                            endTime = survey.endTime,
+                            scode = survey.code,
+                            scope = survey.scope,
+                            classes = survey.classes,
+                            // tmdids = survey.tmdids
+                        };
+                        await client.GetContainer("TEAMModelOS", "Teacher").UpsertItemAsync<ActivityData>(data, new Azure.Cosmos.PartitionKey(data.code));
+                    }
                     var messageSurveyEnd = new ServiceBusMessage(new { id = input.Id, progress = "finish", code = code }.ToJsonString());
                     messageSurveyEnd.ApplicationProperties.Add("name", "Survey");
                     if (changeRecords.Count > 0)
@@ -70,7 +107,6 @@ namespace TEAMModelFunction
                             msgId = messageSurveyEnd.MessageId
                         };
                         await _azureStorage.Save<ChangeRecord>(changeRecord);
-                        //await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(changeRecord, new Azure.Cosmos.PartitionKey($"{changeRecord.code}"));
                     }
                     break;
             }

+ 164 - 0
TEAMModelFunction/TriggerVote.cs

@@ -0,0 +1,164 @@
+using Azure.Cosmos;
+using Azure.Messaging.ServiceBus;
+using Microsoft.Azure.Documents;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK.Models.Cosmos;
+
+namespace TEAMModelFunction
+{
+    public static class TriggerVote
+    {
+
+        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, AzureRedisFactory _azureRedis)
+        {
+            Vote vote = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Vote>(input.Id, new Azure.Cosmos.PartitionKey($"{code}"));
+            List<ChangeRecord> voteRecords = await _azureStorage.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", input.Id }, { "PartitionKey", vote.progress } });
+            //ChangeRecord voteRecord = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<ChangeRecord>(input.Id, new Azure.Cosmos.PartitionKey($"{vote.progress}"));
+            if (vote.ttl >= 1)
+            {
+                //TODO  处理TTL删除业务
+                _azureRedis.GetRedisClient(8).KeyDelete($"Vote:Record:{vote.id}_{vote.code}");
+                _azureRedis.GetRedisClient(8).KeyDelete($"Vote:Count:{vote.id}_{vote.code}");
+                return;
+            }
+            else {
+                switch (vote.progress)
+                {
+                    case "pending":
+                        var messageVote = new ServiceBusMessage(new { id = input.Id, progress = "going", code = code }.ToJsonString());
+                        messageVote.ApplicationProperties.Add("name", "Vote");
+                        if (voteRecords.Count > 0)
+                        {
+                            long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync("active-task", messageVote, DateTimeOffset.FromUnixTimeMilliseconds(stime));
+                            await _serviceBus.GetServiceBusClient().cancelMessage("active-task", voteRecords[0].sequenceNumber);
+                            voteRecords[0].sequenceNumber = start;
+                            await _azureStorage.SaveOrUpdate<ChangeRecord>(voteRecords[0]);
+                            //await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(voteRecord, voteRecord.id, new Azure.Cosmos.PartitionKey($"{voteRecord.code}"));
+                        }
+                        else
+                        {
+                            long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync("active-task", messageVote, DateTimeOffset.FromUnixTimeMilliseconds(stime));
+                            ChangeRecord changeRecord = new ChangeRecord
+                            {
+                                RowKey = input.Id,
+                                PartitionKey = "pending",
+                                sequenceNumber = start,
+                                msgId = messageVote.MessageId
+                            };
+                            await _azureStorage.Save<ChangeRecord>(changeRecord);
+                            //await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(changeRecord, new Azure.Cosmos.PartitionKey($"{changeRecord.code}"));
+                        }
+                        break;
+                    case "going":
+                        var tcode = code.Replace("Vote-", "");
+                        ActivityData data;
+                        if (vote.scope == "school" || vote.scope == "teacher")
+                        {
+                            data = new ActivityData
+                            {
+                                id = vote.id,
+                                code = $"Activity-{tcode}",
+                                type = "vote",
+                                name = vote.name,
+                                startTime = vote.startTime,
+                                endTime = vote.endTime,
+                                scode = vote.code,
+                                scope = vote.scope,
+                                classes = vote.classes,
+                                tmdids = vote.tmdids,
+                                owner=vote.owner
+                            };
+                            await client.GetContainer("TEAMModelOS", "School").UpsertItemAsync<ActivityData>(data, new Azure.Cosmos.PartitionKey(data.code));
+                        }
+                        else if (vote.scope == "private")
+                        {
+                            data = new ActivityData
+                            {
+                                id = vote.id,
+                                code = $"Activity-Common",
+                                type = "vote",
+                                name = vote.name,
+                                startTime = vote.startTime,
+                                endTime = vote.endTime,
+                                scode = vote.code,
+                                scope = vote.scope,
+                                classes = vote.classes,
+                                owner = vote.owner
+                                // tmdids = vote.tmdids
+                            };
+                            await client.GetContainer("TEAMModelOS", "Teacher").UpsertItemAsync<ActivityData>(data, new Azure.Cosmos.PartitionKey(data.code));
+                        }
+                        var messageVoteEnd = new ServiceBusMessage(new { id = input.Id, progress = "finish", code = code }.ToJsonString());
+                        messageVoteEnd.ApplicationProperties.Add("name", "Vote");
+                        if (voteRecords.Count > 0)
+                        {
+                            long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync("active-task", messageVoteEnd, DateTimeOffset.FromUnixTimeMilliseconds(etime));
+                            await _serviceBus.GetServiceBusClient().cancelMessage("active-task", voteRecords[0].sequenceNumber);
+                            voteRecords[0].sequenceNumber = end;
+                            await _azureStorage.SaveOrUpdate<ChangeRecord>(voteRecords[0]);
+                        }
+                        else
+                        {
+                            long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync("active-task", messageVoteEnd, DateTimeOffset.FromUnixTimeMilliseconds(etime));
+                            ChangeRecord changeRecord = new ChangeRecord
+                            {
+                                RowKey = input.Id,
+                                PartitionKey = "going",
+                                sequenceNumber = end,
+                                msgId = messageVoteEnd.MessageId
+                            };
+                            await _azureStorage.Save<ChangeRecord>(changeRecord);
+                        }
+                        break;
+                    case "finish":
+                        //获取投票活动的所有投票记录
+                        var records= await _azureRedis.GetRedisClient(8).HashGetAllAsync($"Vote:Record:{vote.id}_{vote.code}");
+                        //获取投票活动的选项及投票数
+                        var counts= _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Vote:Count:{vote.id}_{vote.code}");
+                        if (counts != null && counts.Length > 0) {
+                            foreach (var count in counts) {
+                                vote.options.ForEach(x => {
+                                    //重新赋值
+                                    if (x.code.Equals(count.Element.ToString())) {
+                                        x.count = (int)count.Score;
+                                    }
+                                });
+                            }
+                        }
+                        List<Task<string>> tasks = new List<Task<string>>();
+                        List<dynamic> recordsBlob = new List<dynamic>();
+                        foreach (var rcd in records) {
+                            var key = rcd.Name.ToString().Split("-")[0];
+                            var value = rcd.Value.ToString().ToObject<JsonElement>();
+                            recordsBlob.Add(new { key,value});
+                            tasks.Add(_azureStorage.UploadFileByContainer(vote.owner, value.ToJsonString(), "vote", $"{vote.id}/{key}.json"));
+                        }
+                        //处理活动方的记录
+                        string url = $"vote/{vote.id}/index.json";
+                        vote.recordUrl = url;
+                        tasks.Add(_azureStorage.UploadFileByContainer(vote.owner, recordsBlob.ToJsonString(), "vote", $"{vote.id}/index.json"));
+                        //处理投票者的记录
+                        await Task.WhenAll(tasks);
+                        if (vote.scope == "school" || vote.scope == "teacher")
+                        {
+                            await client.GetContainer("TEAMModelOS", "School").ReplaceItemAsync<Vote>(vote,vote.id, new Azure.Cosmos.PartitionKey(vote.code));
+                        }
+                        else if (vote.scope == "private")
+                        {
+                            await client.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Vote>(vote, vote.id, new Azure.Cosmos.PartitionKey(vote.code));
+                        }
+                           
+                        break;
+                }
+            }
+        }
+    }
+}

+ 0 - 79
TEAMModelFunction/VoteTrigger.cs

@@ -1,79 +0,0 @@
-using Azure.Cosmos;
-using Azure.Messaging.ServiceBus;
-using Microsoft.Azure.Documents;
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.Json;
-using System.Threading.Tasks;
-using TEAMModelOS.SDK.DI;
-using TEAMModelOS.SDK.Extension;
-using TEAMModelOS.SDK.Models;
-
-namespace TEAMModelFunction
-{
-  public static  class VoteTrigger
-    {
-
-        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)
-        {
-            Vote vote = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Vote>(input.Id, new Azure.Cosmos.PartitionKey($"{code}"));
-            List<ChangeRecord> voteRecords = await _azureStorage.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", input.Id }, { "PartitionKey", vote.progress } });
-            //ChangeRecord voteRecord = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<ChangeRecord>(input.Id, new Azure.Cosmos.PartitionKey($"{vote.progress}"));
-            switch (vote.progress)
-            {
-                case "pending":
-                    var messageVote = new ServiceBusMessage(new { id = input.Id, progress = "going", code = code }.ToJsonString());
-                    messageVote.ApplicationProperties.Add("name", "Vote");
-                    if (voteRecords.Count > 0)
-                    {
-                        long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync("active-task", messageVote, DateTimeOffset.FromUnixTimeMilliseconds(stime));
-                        await _serviceBus.GetServiceBusClient().cancelMessage("active-task", voteRecords[0].sequenceNumber);
-                        voteRecords[0].sequenceNumber = start;
-                        await _azureStorage.SaveOrUpdate<ChangeRecord>(voteRecords[0]);
-                        //await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(voteRecord, voteRecord.id, new Azure.Cosmos.PartitionKey($"{voteRecord.code}"));
-                    }
-                    else
-                    {
-                        long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync("active-task", messageVote, DateTimeOffset.FromUnixTimeMilliseconds(stime));
-                        ChangeRecord changeRecord = new ChangeRecord
-                        {
-                            RowKey = input.Id,
-                            PartitionKey = "pending",
-                            sequenceNumber = start,
-                            msgId = messageVote.MessageId
-                        };
-                        await _azureStorage.Save<ChangeRecord>(changeRecord);
-                        //await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(changeRecord, new Azure.Cosmos.PartitionKey($"{changeRecord.code}"));
-                    }
-                    break;
-                case "going":
-                    var messageVoteEnd = new ServiceBusMessage(new { id = input.Id, progress = "finish", code = code }.ToJsonString());
-                    messageVoteEnd.ApplicationProperties.Add("name", "Vote");
-                    if (voteRecords.Count > 0)
-                    {
-                        long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync("active-task", messageVoteEnd, DateTimeOffset.FromUnixTimeMilliseconds(etime));
-                        await _serviceBus.GetServiceBusClient().cancelMessage("active-task", voteRecords[0].sequenceNumber);
-                        voteRecords[0].sequenceNumber = end;
-                        await _azureStorage.SaveOrUpdate<ChangeRecord>(voteRecords[0]);
-                        //await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(voteRecord, voteRecord.id, new Azure.Cosmos.PartitionKey($"{voteRecord.code}"));
-                    }
-                    else
-                    {
-                        long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync("active-task", messageVoteEnd, DateTimeOffset.FromUnixTimeMilliseconds(etime));
-                        ChangeRecord changeRecord = new ChangeRecord
-                        {
-                            RowKey = input.Id,
-                            PartitionKey = "going",
-                            sequenceNumber = end,
-                            msgId = messageVoteEnd.MessageId
-                        };
-                        await _azureStorage.Save<ChangeRecord>(changeRecord);
-                        //await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(changeRecord, new Azure.Cosmos.PartitionKey($"{changeRecord.code}"));
-                    }
-                    break;
-            }
-        }
-    }
-}

+ 14 - 10
TEAMModelOS.SDK/DI/AzureStorage/AzureStorageBlobExtensions.cs

@@ -85,18 +85,22 @@ namespace TEAMModelOS.SDK.DI
             List<OptUrl> optUrls = new List<OptUrl>();
             try
             {
-               
-                foreach (var url in urls) {
-                    OptUrl optUrl = new OptUrl { url = url, size =0};
-                    var eurl = System.Web.HttpUtility.UrlDecode(url, Encoding.UTF8);
-                    var blob = client.GetBlobClient(eurl);
-                    if (blob.Exists()) {
-                        var props = await blob.GetPropertiesAsync();
-                        var size= props.Value.ContentLength;
-                        optUrl.size = size;
+                if (urls != null) {
+                    foreach (var url in urls)
+                    {
+                        OptUrl optUrl = new OptUrl { url = url, size = 0 };
+                        var eurl = System.Web.HttpUtility.UrlDecode(url, Encoding.UTF8);
+                        var blob = client.GetBlobClient(eurl);
+                        if (blob.Exists())
+                        {
+                            var props = await blob.GetPropertiesAsync();
+                            var size = props.Value.ContentLength;
+                            optUrl.size = size;
+                        }
+                        optUrls.Add(optUrl);
                     }
-                    optUrls.Add(optUrl);
                 }
+                
                 return optUrls;
             }
             catch

+ 70 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/ActivityData.cs

@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Models.Cosmos
+{
+
+    /// <summary>
+    /// 
+    /// [
+    //      Activity|-----问卷调查
+    //        |-----投票活动
+    //        |-----评测活动
+    //        |-----作业活动
+    //        |-----学习活动
+    //]
+    //[
+    //    {
+    //        "type": "vote",//"vote"/"exam"/"homework"/"learn"/"survey"
+    //        "name": "第一届紫藤小学人气教师投票",
+    //        "startTime": 1608274766154,
+    //        "endTime": 1608912000000,
+    //        "scode": "Vote-hbcn",
+    //        "id": "aaaaaaaaaaaaaaaaaaa",
+    //        "code": "Activity-hbcn",
+    //        "pk": "Activity",
+    //        "scope":"private/school/teacher",
+    //        "classes":["S-C-00001","S-C-00002","S-C-00003","S-C-00004"],
+    //        "tmdids":["TMDID0001","TMDID0002","TMDID0003","TMDID0004",]//只有问卷调查和投票活动设置,且分区键必须为学校的
+    //},
+    //    {
+    //        "type": "vote",//"vote"/"exam"/"homework"/"learn"/"survey"
+    //        "name": "第一届醍摩豆杯人气教师投票",
+    //        "startTime": 1608274766154,
+    //        "endTime": 1608912000000,
+    //        "scode": "Vote-TMDID0001",
+    //        "id": "bbbbbbbbbbbbbbbbbbbbb",
+    //        "code": "Activity-Common",//教师个人发布的活动统一使用Common,用班级子查询
+    //        "pk": "Activity",
+    //        "scope":"private/school/teacher",
+    //        "classes":["P-C-00004","S-C-00001"],//如果是醍摩豆ID则直接搜寻加入的私人教师编码,如果是校内学生则需要多搜寻一次。
+    //        "tmdids":[]
+    //    }
+    //]
+    /// </summary>
+    public class ActivityData : CosmosEntity
+    {
+        public ActivityData() {
+            pk = "Activity";
+        }
+        /// <summary>
+        /// 业务类型  vote投票 survey问卷 exam评测 learn学习活动 homework作业活动
+        /// </summary>
+        public string type { get; set; }
+        public string name { get; set; }
+        public long  startTime { get; set; }
+        public long endTime { get; set; }
+        /// <summary>
+        /// 活动的分区键 Vote-hbcn/Vote-1606294378
+        /// </summary>
+        public string scode { get; set; }
+        /// <summary>
+        /// private/school/teacher
+        /// </summary>
+        public string scope { get; set; }
+        public List<string> classes { get; set; }
+        public List<string> tmdids { get; set; }
+        public string owner { get; set; }
+    }
+}

+ 0 - 21
TEAMModelOS.SDK/Models/Cosmos/Common/CommonData.cs

@@ -1,21 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace TEAMModelOS.SDK.Models.Cosmos.Student
-{
-    public class CommonData : CosmosEntity
-    {
-        public CommonData() {
-            pk = "Common";
-        }
-        /// <summary>
-        /// 业务类型  vote投票 survey问卷 exam评测 learn学习活动 homework作业活动
-        /// </summary>
-        public string type { get; set; }
-        public string name { get; set; }
-        public string startTime { get; set; }
-        public string endTime { get; set; }
-        public string scode { get; set; }
-    }
-}

+ 12 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/Inner/VoteRecord.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Models.Cosmos.Common.Inner
+{
+    public class VoteRecord
+    {
+        public List<string> opt { get; set; }
+        public long time { get; set; }
+    }
+}

+ 77 - 37
TEAMModelOS.SDK/Models/Cosmos/School/Survey.cs

@@ -15,21 +15,46 @@ namespace TEAMModelOS.SDK.Models
     {
         public Survey() {
             pk = "Survey";
-            questions = new List<Questions>();
-            classes = new List<Classes>();
+            // questions = new List<Question>();
+            classes = new List<string>();
+            tmdids = new List<string>();
         }
-       
-        public string name { get; set; }   //测试问卷名称', // 问卷名称
-        public string description { get; set; }   //测试问卷描述', // 问卷描述
+        /// <summary>
+        /// 学校编码或教室tmdid
+        /// </summary>
+        [Required(ErrorMessage = "owner 必须设置")]
+        public string owner { get; set; }
+        /// <summary>
+        /// 问卷名称
+        /// </summary>
+        [Required(ErrorMessage = "name 必须设置")]
+        public string name { get; set; }
+        /// <summary>
+        ///  问卷描述
+        /// </summary>
+        public string description { get; set; }
         //public string type { get; set; }   //normal', // 问卷类型
-        public string school { get; set; }
+        /// <summary>
+        /// 创建者的id 
+        /// </summary>
+        [Required(ErrorMessage = "creatorId 必须设置")]
         public string creatorId { get; set; }
-        public int year { get; set; }
+        // public int year { get; set; }
+        /// <summary>
+        /// pending 待发布|going 已发布|finish 已结束
+        /// </summary>
+        [Required(ErrorMessage = "progress 必须设置")]
         public string progress { get; set; }
-        public List<Questions> questions { get; set; }
-        public List<string> targetClassIds { get; set; }
-        public int stuCount { get; set; }
-        public int status { get; set; } // 问卷状态(100:待发布 200:已发布 300:已结束)
+        public string scope { get; set; }
+
+        public List<string> tmdids { get; set; }
+        public List<string> classes { get; set; }
+        // public List<string> targetClassIds { get; set; }
+        //  public int stuCount { get; set; }
+        /// <summary>
+        /// 问卷状态(100:待发布 200:已发布 300:已结束)
+        /// </summary>
+        public int status { get; set; }
         /// <summary>
         /// 发布对象
         /// </summary>
@@ -39,7 +64,7 @@ namespace TEAMModelOS.SDK.Models
         /// <summary>
         /// 发布模式 0 立即发布 1 定时
         /// </summary>
-        public string publishModel { get; set; }
+        //public string publishModel { get; set; }
 
         /// <summary>
         /// 开始时间
@@ -51,39 +76,58 @@ namespace TEAMModelOS.SDK.Models
         /// </summary>
         public long endTime { get; set; }
         public long createTime { get; set; } // 问卷发布时间
-        public List<Classes> classes { get; set; }
 
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        public long updateTime { get; set; }
         //public long sequenceNumber { get; set; }
 
-        public string url { get; set; }
-        public string scope { get; set; }
+        public string blobUrl { get; set; }
+        //将问题放入Blob
+        //public List<Question> questions { get; set; }
 
     }
-    /*public class Item {
-        public string stem { get; set; }
-        /// <summary>
-        ///  Complete Single Multiple Subjective, Judge判断
-        /// </summary>
-        //public string type { get; set; }
-        public bool required { get; set; }
-        public int order { get; set; }
-        public string description { get; set; } = null;
-        public List<CodeValue> options { get; set; }
-        public List<CodeVal> result { get; set; }
-    }*/
-     public class Questions { 
+
+    /// <summary>
+    ///
+    /// </summary>
+    public class Question {
         public string qid { get; set; }
         public string question { get; set; }
-        public List<Options> option { get; set; }
+        public List<OptionSurvey> options { get; set; }
         public string type { get; set; }
-        public QuestionResult result { get; set; }
+        //public QuestionResult result { get; set; }
 
     }
-    public class Options { 
+
+    public class OptionSurvey{
+        /// <summary>
+        /// 选项编码
+        /// </summary>
         public string code { get; set; }
+        /// <summary>
+        /// 选项文本
+        /// </summary>
         public string value { get; set; }
-        public Result result { get; set; }
+        /// <summary>
+        /// 选项描述
+        /// </summary>
+        //public string desc { get; set; }
+        /// <summary>
+        /// 选择数
+        /// </summary>
+        public int? count { get; set; }
+        /// <summary>
+        /// 其他答案数量
+        /// </summary>
+        public int? other { get; set; }
     }
+
+
+
+
+
     public class QuestionResult
     {
         public double finish { get; set; }
@@ -105,7 +149,7 @@ namespace TEAMModelOS.SDK.Models
     public class AnswerRate {
         public string qid { get; set; }
         public double answerRate { get; set; }
-        public List<Options> option { get; set; }
+        public List<CodeValue> option { get; set; }
     }
     public class StudentInfo {
         public string id { get; set; }
@@ -122,8 +166,4 @@ namespace TEAMModelOS.SDK.Models
         public string qid { get; set; }
         public string answer { get; set; }
     }
-    /*public class CodeVal{
-        public string code { get; set; }
-        public int value { get; set; }
-    }*/
 }

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

@@ -12,18 +12,7 @@ namespace TEAMModelOS.SDK.Models
     /// </summary>
     
     public class Syllabus : CosmosEntity
-    {
-        [Required(ErrorMessage = "{0} 必须填写")]
-        public string code { get; set; }
-        //[PartitionKey(name = "Syllabuses")]
-        public string pk { get; set; }
-        public int? ttl { get; set; }
-        /// <summary>
-        /// 
-        /// </summary>
-        [Required(ErrorMessage = "{0} 必须填写")]
-        public string id { get; set; }
-
+    {  
         /// <summary>
         /// 册别编码
         /// </summary>

+ 76 - 46
TEAMModelOS.SDK/Models/Cosmos/Common/Vote.cs

@@ -1,3 +1,4 @@
+
 using System;
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
@@ -16,81 +17,110 @@ namespace TEAMModelOS.SDK.Models
         public Vote()
         {
             pk = "Vote";
-            options = new List<OptionsVote>();
+            options = new List<OptionVote>();
 
         }
-
+        /// <summary>
+        /// 学校编码或教室tmdid
+        /// </summary>
+        [Required(ErrorMessage = "owner 必须设置")]
+        public string owner { get; set; }
         /// <summary>
         /// 投票名称
         /// </summary>
+        [Required(ErrorMessage = "name 必须设置")]
         public string name { get; set; }
-        public string school { get; set; }
+        /// <summary>
+        /// 创建者的id 
+        /// </summary>
+        [Required(ErrorMessage = "creatorId 必须设置")]
         public string creatorId { get; set; }
-        public int year { get; set; }
+        /// <summary>
+        /// 投票描述
+        /// </summary>
+        public string description { get; set; }
+        /// <summary>
+        /// pending 待发布|going 已发布|finish 已结束
+        /// </summary>
+        [Required(ErrorMessage = "progress 必须设置")]
         public string progress { get; set; }
-        public List<OptionsVote> options { get; set; }
+        /// <summary>
+        /// 投票选项
+        /// </summary>
+        public List<OptionVote> options { get; set; }
+        /// <summary>
+        /// //匿名投票,不公布投票人相关信息
+        /// </summary>
         public bool secret { get; set; }
-        public int selectMax { get; set; }
-        public int stuCount { get; set; }
+        /// <summary>
+        /// 投票周期/once一次,day天,week周,month月,年year等
+        /// </summary>
+        [Required(ErrorMessage = "times 必须设置")]
+        public string times { get; set; }
+        //周期内可投票数
+        public int voteNum { get; set; }
+        //周期内是否允许重复投相同一个选项。
+        public bool repeat { get; set; } 
+        /// <summary>
+        /// school|private
+        /// </summary>
+        [Required(ErrorMessage = "scope 必须设置")]
         public string scope { get; set; }
-        public List<string> targetClassIds { get; set; }
         /// <summary>
-        /// 发布模式 0 立即发布 1 定时
+        /// 参与投票的教师醍摩豆id
         /// </summary>
-        public string publishModel { get; set; }
-
+        public List<string> tmdids { get; set; }
+        /// <summary>
+        ///  scope 为school时 是学校的班级  为private 时是私人班级
+        /// </summary>
+        public List<string> classes { get; set; }
+        /// <summary>
+        /// (100:待发布 200:已发布 300:已结束)
+        /// </summary>
+        public int status { get; set; }
         /// <summary>
         /// 开始时间
         /// </summary>
         public long startTime { get; set; }
+        /// <summary>
+        /// 创建时间
+        /// </summary>
         public long createTime { get; set; }
-
         /// <summary>
         /// 结束时间
         /// </summary>
         public long endTime { get; set; }
-
-
         /// <summary>
-        /// 投票描述
+        /// 更新时间
         /// </summary>
-        public string description { get; set; }
-
-
-        /*        /// <summary>
-                /// 投票附件
-                /// </summary>
-                [ProtoMember(9)]
-                public List<ProcessRes> resource { get; set; }
-        */
-
+        public long updateTime { get; set; }
         /// <summary>
-        /// 状态 (100:待发布 200:已发布 300:已结束)
+        /// 投票记录
         /// </summary>
-        public int status { get; set; }
-
-
-        /*        /// <summary>
-                /// 模式
-                /// </summary>
-                [ProtoMember(11)]
-                public List<string> other { get; set; }*/
-
-        /*
-                public string url { get; set; }
-
-                /// <summary>
-                /// 选项
-                /// </summary>
-                [ProtoMember(12)]
-                public List<Option> option { get; set; }*/
-
-        //public long sequenceNumber { get; set; }
+        public string recordUrl { get; set; }
     }
-    public class OptionsVote
+    /// <summary>
+    /// 投票选项
+    /// </summary>
+    public class OptionVote
     {
+        /// <summary>
+        /// 投票编号
+        /// </summary>
         public string code { get; set; }
+        /// <summary>
+        /// 投票对象
+        /// </summary>
         public string value { get; set; }
+        /// <summary>
+        /// 投票对象描述
+        /// </summary>
         public string desc { get; set; }
+        /// <summary>
+        /// 得票数量
+        /// </summary>
+        public int? count { get; set; } 
     }
+    
 }
+

+ 8 - 1
TEAMModelOS.SDK/Models/Cosmos/School/Class.cs

@@ -43,6 +43,10 @@ namespace TEAMModelOS.SDK.Models
         /// </summary>
         public string openType { get; set; }
         public string scope { get; set; }
+        /// <summary>
+        /// 学生名单数据来源 1是不同学校的学生账号,2是扫码加入的醍摩豆ID
+        /// </summary>
+        public int source { get; set; }
         
     }
 }
@@ -54,7 +58,10 @@ public class Point
 }
 public class StudentSimple
 {
-
+    /// <summary>
+    /// 数据来源的分区键 如果是学校的编码 则是学校编码,如果是tmdid则是Base
+    /// </summary>
+    public string scode { get; set; }
     public string id { get; set; }
 
     public string name { get; set; }

+ 23 - 13
TEAMModelOS.SDK/Models/Cosmos/School/Knowledge.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
 using System.Text;
 
 namespace TEAMModelOS.SDK.Models.Cosmos.School
@@ -11,18 +12,22 @@ namespace TEAMModelOS.SDK.Models.Cosmos.School
     {
         public Knowledge() {
             points = new List<string>();
-            blocks = new List<CodeValue>();
+            blocks = new List<Block>();
             pk = "Knowledge";
         }
-
+        [Required(ErrorMessage = "owner 必须设置")]
+        public string owner { get; set; }
+        [Required(ErrorMessage = "scope 必须设置")]
         public string scope { get; set; }
         /// <summary>
         /// 学段id
         /// </summary>
+       [Required(ErrorMessage = "periodId 必须设置")]
         public string periodId { get; set; }
         /// <summary>
         /// 学科id
         /// </summary>
+        [Required(ErrorMessage = "subjectId 必须设置")]
         public string subjectId { get; set; }
         /// <summary>
         /// 知识点
@@ -31,8 +36,13 @@ namespace TEAMModelOS.SDK.Models.Cosmos.School
         /// <summary>
         /// 知识块
         /// </summary>
-        public List<CodeValue> blocks { get; set; }
+        public List<Block> blocks { get; set; }
+
+    }
 
+    public class Block { 
+        public string name { get; set; }
+        public List<string> points { get; set; }
     }
     /*
     {
@@ -47,24 +57,24 @@ namespace TEAMModelOS.SDK.Models.Cosmos.School
         ],
         "block":[
             {
-                "code": "方程式" ,
-                "value":["一元一次方程","二元一次方程","一元二次方程","直线方程","三元一次方程","鸡兔同笼问题","微积分方程"]
+                "name": "方程式" ,
+                "points":["一元一次方程","二元一次方程","一元二次方程","直线方程","三元一次方程","鸡兔同笼问题","微积分方程"]
             },
             {
-                "code": "函数的特性" ,
-                "value": ["函数有界性","函数单调性","函数奇偶性","函数周期性","函数连续性","函数凹凸性"]
+                "name": "函数的特性" ,
+                "points": ["函数有界性","函数单调性","函数奇偶性","函数周期性","函数连续性","函数凹凸性"]
             },
             {
-                "code": "多项式函数" ,
-                "value": ["常函数","一次函数","二次函数","三次函数","四次函数","五次函数"]
+                "name": "多项式函数" ,
+                "points": ["常函数","一次函数","二次函数","三次函数","四次函数","五次函数"]
             },
             {
-                "code":  "基本初等函数" ,
-                "value": ["幂函数","指数函数","对数函数","三角函数","反三角函数","常数函数"]
+                "name":  "基本初等函数" ,
+                "points": ["幂函数","指数函数","对数函数","三角函数","反三角函数","常数函数"]
             },
             {
-                "code": "三角函数" ,
-                "value":["正弦函数","余弦函数","正切函数","余切函数","正割函数","余割函数","正矢函数","余矢函数","半正矢函数","半余矢函数","外正割函数","外余割函数"]
+                "name": "三角函数" ,
+                "points":["正弦函数","余弦函数","正切函数","余切函数","正割函数","余割函数","正矢函数","余矢函数","半正矢函数","半余矢函数","外正割函数","外余割函数"]
             }
         ]
     }

+ 23 - 23
TEAMModelOS.SDK/Models/Cosmos/Student/VoteRecord.cs

@@ -7,35 +7,35 @@ using TEAMModelOS.SDK.DI;
 
 namespace TEAMModelOS.SDK.Models
 {    
-    public class VoteRecord : CosmosEntity
-    {
+    //public class VoteRecord : CosmosEntity
+    //{
 
-        public VoteRecord()
-        {
-            classroom = new ClassroomItem();
-        }
+    //    public VoteRecord()
+    //    {
+    //        classroom = new ClassroomItem();
+    //    }
 
-        /// <summary>
-        /// 姓名
-        /// </summary>
-        public string name { get; set; }
+    //    /// <summary>
+    //    /// 姓名
+    //    /// </summary>
+    //    public string name { get; set; }
 
-        /// <summary>
-        /// 上课班级
-        /// </summary>
-        public ClassroomItem classroom { get; set; }
+    //    /// <summary>
+    //    /// 上课班级
+    //    /// </summary>
+    //    public ClassroomItem classroom { get; set; }
 
 
 
-        /// <summary>
-        /// 提交时间
-        /// </summary>
-        public long submitTime { get; set; }
+    //    /// <summary>
+    //    /// 提交时间
+    //    /// </summary>
+    //    public long submitTime { get; set; }
 
-        /// <summary>
-        /// 选项
-        /// </summary>
-        public string option { get; set; }
+    //    /// <summary>
+    //    /// 选项
+    //    /// </summary>
+    //    public string option { get; set; }
 
-    }
+    //}
 }

+ 1 - 1
TEAMModelOS/ClientApp/package.json

@@ -32,7 +32,6 @@
 		"file-saver": "^2.0.2",
 		"firebase": "^7.19.0",
 		"firestore": "^1.1.6",
-		"hammer-touchemulator": "0.0.2",
 		"html2canvas": "^1.0.0-rc.7",
 		"increase-memory-limit": "^1.0.7",
 		"js-sha1": "^0.6.0",
@@ -67,6 +66,7 @@
 		"vue-infinite-loading": "^2.4.4",
 		"vue-loading-overlay": "^3.3.3",
 		"vue-msal": "^2.0.0",
+		"vue-pdf": "^4.2.0",
 		"vue-router": "^3.0.6",
 		"vue-scroll": "^2.1.12",
 		"vue-server-renderer": "^2.6.12",

+ 1 - 1
TEAMModelOS/ClientApp/public/index.html

@@ -6,7 +6,7 @@
 		<meta name="viewport" content="width=device-width,initial-scale=1.0">
 		<link rel="icon" href="<%= BASE_URL %>favicon.ico">
 		<link id="theme" type="text/css" rel="stylesheet" href="<%= BASE_URL %>theme/dark-theme.css">
-		<title>醍摩豆IES5智慧大云</title>
+		<title>IES5 云平台</title>
 		<script type="text/javascript" id="MathJax-script" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML"></script>
 		<script>
 			let cloudSetting = localStorage.getItem('cloudSetting')

+ 8 - 1
TEAMModelOS/ClientApp/src/api/uploadFile.js

@@ -31,5 +31,12 @@ export default {
 	deleteBlobs: function (data) {
         return post('/blob/delete-blobs', data)
     },
-
+    // 上传文件之前需要先检查文件是否存在
+	checkBlobs: function (data) {
+        return post('/blob/check-blobsize', data)
+    },
+    // 上传文件之后需要更新blob空间
+	updateSize: function (data) {
+        return post('/blob/update-blobsize', data)
+    }
 }

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

@@ -10,7 +10,7 @@ import stuAccount from './stuAccount'
 import syllabus from './syllabus'
 import knowledge from './knowledge'
 import teachContent from './teachContent'
-import uploadFile from './uploadFile'
+import blob from './blob'
 import courseMgmt from './courseMgmt'
 import login from './login'
 import schoolList from './schoolList'
@@ -41,7 +41,7 @@ export default {
     talMgmt,
     teachContent,
     // cloudApi,
-    uploadFile,
+    blob,
     courseMgmt,
     newEvaluation,
     login,

+ 253 - 0
TEAMModelOS/ClientApp/src/assets/iconfont/demo_index.html

@@ -30,6 +30,72 @@
       <div class="content unicode" style="display: block;">
           <ul class="icon_lists dib-box">
           
+            <li class="dib">
+              <span class="icon iconfont">&#xe61a;</span>
+                <div class="name">链接</div>
+                <div class="code-name">&amp;#xe61a;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe671;</span>
+                <div class="name">链接</div>
+                <div class="code-name">&amp;#xe671;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe61d;</span>
+                <div class="name">jpg</div>
+                <div class="code-name">&amp;#xe61d;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe8b7;</span>
+                <div class="name">zip</div>
+                <div class="code-name">&amp;#xe8b7;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe64d;</span>
+                <div class="name">PDF</div>
+                <div class="code-name">&amp;#xe64d;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe608;</span>
+                <div class="name">ppt</div>
+                <div class="code-name">&amp;#xe608;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe617;</span>
+                <div class="name">mp3</div>
+                <div class="code-name">&amp;#xe617;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe66e;</span>
+                <div class="name">video</div>
+                <div class="code-name">&amp;#xe66e;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6ff;</span>
+                <div class="name">文件</div>
+                <div class="code-name">&amp;#xe6ff;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe601;</span>
+                <div class="name">word</div>
+                <div class="code-name">&amp;#xe601;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe61c;</span>
+                <div class="name">xlsx</div>
+                <div class="code-name">&amp;#xe61c;</div>
+              </li>
+          
             <li class="dib">
               <span class="icon iconfont">&#xe666;</span>
                 <div class="name">转换</div>
@@ -470,6 +536,105 @@
       <div class="content font-class">
         <ul class="icon_lists dib-box">
           
+          <li class="dib">
+            <span class="icon iconfont icon-share_link"></span>
+            <div class="name">
+              链接
+            </div>
+            <div class="code-name">.icon-share_link
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-link2"></span>
+            <div class="name">
+              链接
+            </div>
+            <div class="code-name">.icon-link2
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-jpg"></span>
+            <div class="name">
+              jpg
+            </div>
+            <div class="code-name">.icon-jpg
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-zip"></span>
+            <div class="name">
+              zip
+            </div>
+            <div class="code-name">.icon-zip
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-pdf"></span>
+            <div class="name">
+              PDF
+            </div>
+            <div class="code-name">.icon-pdf
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-ppt"></span>
+            <div class="name">
+              ppt
+            </div>
+            <div class="code-name">.icon-ppt
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-mp3"></span>
+            <div class="name">
+              mp3
+            </div>
+            <div class="code-name">.icon-mp3
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-video1"></span>
+            <div class="name">
+              video
+            </div>
+            <div class="code-name">.icon-video1
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-V"></span>
+            <div class="name">
+              文件
+            </div>
+            <div class="code-name">.icon-V
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-word"></span>
+            <div class="name">
+              word
+            </div>
+            <div class="code-name">.icon-word
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-xlsx"></span>
+            <div class="name">
+              xlsx
+            </div>
+            <div class="code-name">.icon-xlsx
+            </div>
+          </li>
+          
           <li class="dib">
             <span class="icon iconfont icon-convert"></span>
             <div class="name">
@@ -1084,6 +1249,94 @@
       <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-share_link"></use>
+                </svg>
+                <div class="name">链接</div>
+                <div class="code-name">#icon-share_link</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-link2"></use>
+                </svg>
+                <div class="name">链接</div>
+                <div class="code-name">#icon-link2</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-jpg"></use>
+                </svg>
+                <div class="name">jpg</div>
+                <div class="code-name">#icon-jpg</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-zip"></use>
+                </svg>
+                <div class="name">zip</div>
+                <div class="code-name">#icon-zip</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-pdf"></use>
+                </svg>
+                <div class="name">PDF</div>
+                <div class="code-name">#icon-pdf</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-ppt"></use>
+                </svg>
+                <div class="name">ppt</div>
+                <div class="code-name">#icon-ppt</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-mp3"></use>
+                </svg>
+                <div class="name">mp3</div>
+                <div class="code-name">#icon-mp3</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-video1"></use>
+                </svg>
+                <div class="name">video</div>
+                <div class="code-name">#icon-video1</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-V"></use>
+                </svg>
+                <div class="name">文件</div>
+                <div class="code-name">#icon-V</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-word"></use>
+                </svg>
+                <div class="name">word</div>
+                <div class="code-name">#icon-word</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-xlsx"></use>
+                </svg>
+                <div class="name">xlsx</div>
+                <div class="code-name">#icon-xlsx</div>
+            </li>
+          
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
                   <use xlink:href="#icon-convert"></use>

File diff suppressed because it is too large
+ 51 - 8
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.css


BIN
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.eot


File diff suppressed because it is too large
+ 1 - 1
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.js


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

@@ -5,6 +5,83 @@
   "css_prefix_text": "icon-",
   "description": "",
   "glyphs": [
+    {
+      "icon_id": "551219",
+      "name": "链接",
+      "font_class": "share_link",
+      "unicode": "e61a",
+      "unicode_decimal": 58906
+    },
+    {
+      "icon_id": "6537196",
+      "name": "链接",
+      "font_class": "link2",
+      "unicode": "e671",
+      "unicode_decimal": 58993
+    },
+    {
+      "icon_id": "19145271",
+      "name": "jpg",
+      "font_class": "jpg",
+      "unicode": "e61d",
+      "unicode_decimal": 58909
+    },
+    {
+      "icon_id": "1344658",
+      "name": "zip",
+      "font_class": "zip",
+      "unicode": "e8b7",
+      "unicode_decimal": 59575
+    },
+    {
+      "icon_id": "1973778",
+      "name": "PDF",
+      "font_class": "pdf",
+      "unicode": "e64d",
+      "unicode_decimal": 58957
+    },
+    {
+      "icon_id": "2295972",
+      "name": "ppt",
+      "font_class": "ppt",
+      "unicode": "e608",
+      "unicode_decimal": 58888
+    },
+    {
+      "icon_id": "2295990",
+      "name": "mp3",
+      "font_class": "mp3",
+      "unicode": "e617",
+      "unicode_decimal": 58903
+    },
+    {
+      "icon_id": "7684652",
+      "name": "video",
+      "font_class": "video1",
+      "unicode": "e66e",
+      "unicode_decimal": 58990
+    },
+    {
+      "icon_id": "11755918",
+      "name": "文件",
+      "font_class": "V",
+      "unicode": "e6ff",
+      "unicode_decimal": 59135
+    },
+    {
+      "icon_id": "14559710",
+      "name": "word",
+      "font_class": "word",
+      "unicode": "e601",
+      "unicode_decimal": 58881
+    },
+    {
+      "icon_id": "19145279",
+      "name": "xlsx",
+      "font_class": "xlsx",
+      "unicode": "e61c",
+      "unicode_decimal": 58908
+    },
     {
       "icon_id": "2604478",
       "name": "转换",

File diff suppressed because it is too large
+ 61 - 28
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.svg


BIN
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.ttf


BIN
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff


BIN
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff2


+ 1 - 1
TEAMModelOS/ClientApp/src/boot-app.js

@@ -1,5 +1,5 @@
 import Vue from 'vue'
-import '@babel/polyfill'
+//import '@babel/polyfill'
 import i18n from '@/locale'
 import router from './router/index'
 import store from './store'

+ 88 - 42
TEAMModelOS/ClientApp/src/common/BaseCanvas.vue

@@ -1,9 +1,16 @@
 <template>
-	<canvas :ref="domId" class="app-sign-canvas" :id="domId" @mousedown.prevent.stop="handleMousedown"
-	 @mousemove.prevent.stop="handleMousemove" @mouseup.prevent.stop="handleMouseup" @mouseleave.prevent.stop="handleMouseleave"
-	 @touchstart.prevent.stop="handleTouchstart" @touchmove.prevent.stop="handleTouchmove" @touchend.prevent.stop="handleTouchend">
-		您的浏览器不支持canvas技术,请升级浏览器!
-	</canvas>
+	<div class="canvas-main">
+		<canvas :ref="domId" class="app-sign-canvas" :id="domId" @mousedown.prevent.stop="handleMousedown"
+		 @mousemove.prevent.stop="handleMousemove" @mouseup.prevent.stop="handleMouseup" @mouseleave.prevent.stop="handleMouseleave"
+		 @touchstart.prevent.stop="handleTouchstart" @touchmove.prevent.stop="handleTouchmove" @touchend.prevent.stop="handleTouchend">
+			您的浏览器不支持canvas技术,请升级浏览器!
+		</canvas>
+		<div class="float-text" @mousedown.prevent.stop="move">
+			{{positionX}}
+			        {{positionY}}
+		</div>
+	</div>
+
 </template>
 <script>
 	export default {
@@ -28,6 +35,8 @@
 		},
 		data() {
 			return {
+				positionX:0,
+				positionY:0,
 				value: null, //base64
 				domId: `sign-canvas-${Math.random().toString(36).substr(2)}`, //生成唯一dom标识
 				canvas: null, //canvas dom对象
@@ -54,41 +63,6 @@
 				},
 			};
 		},
-		mounted() {
-			this.init();
-		},
-
-		watch: {
-			'options.writeWidth': {
-				handler(n) {
-					this.context.lineWidth = n
-					this.config.writeWidth = n
-				},
-				deep: true
-			},
-			'options.writeColor': {
-				handler(n) {
-					this.context.strokeStyle = n
-					this.config.writeColor = n
-				},
-				deep: true
-			},
-			'options.canvasWidth': {
-				handler(n) {
-					this.canvas.width = n
-					this.config.canvasWidth = n
-					this.canvasInit();
-				},
-			},
-			'options.canvasHeight': {
-				handler(n) {
-					this.canvas.height = n
-					this.config.canvasHeight = n
-					this.canvasInit();
-				},
-			},
-		},
-
 		methods: {
 			init() {
 				const options = this.options;
@@ -112,7 +86,32 @@
 				})
 
 			},
-			
+
+			move(e) {
+				let odiv = e.target; //获取目标元素
+
+				//算出鼠标相对元素的位置
+				let disX = e.clientX - odiv.offsetLeft;
+				let disY = e.clientY - odiv.offsetTop;
+				document.onmousemove = (e) => { //鼠标按下并移动的事件
+					//用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
+					let left = e.clientX - disX;
+					let top = e.clientY - disY;
+
+					//绑定元素位置到positionX和positionY上面
+					this.positionX = top;
+					this.positionY = left;
+
+					//移动当前元素
+					odiv.style.left = left + 'px';
+					odiv.style.top = top + 'px';
+				};
+				document.onmouseup = (e) => {
+					document.onmousemove = null;
+					document.onmouseup = null;
+				};
+			},
+
 			/* 绘制输入的文本 */
 			doAddText(val) {
 				let context = this.context
@@ -383,6 +382,53 @@
 				}
 				return realNum;
 			}
-		}
+		},
+		mounted() {
+			this.init();
+		},
+		
+		watch: {
+			'options.writeWidth': {
+				handler(n) {
+					this.context.lineWidth = n
+					this.config.writeWidth = n
+				},
+				deep: true
+			},
+			'options.writeColor': {
+				handler(n) {
+					this.context.strokeStyle = n
+					this.config.writeColor = n
+				},
+				deep: true
+			},
+			'options.canvasWidth': {
+				handler(n) {
+					this.canvas.width = n
+					this.config.canvasWidth = n
+					this.canvasInit();
+				},
+			},
+			'options.canvasHeight': {
+				handler(n) {
+					this.canvas.height = n
+					this.config.canvasHeight = n
+					this.canvasInit();
+				},
+			},
+		},
 	};
 </script>
+<style lang="less" scoped>
+	.canvas-main {
+		position: relative;
+
+		.float-text {
+			position: absolute;
+			font-size: 20px;
+			color: red;
+			cursor: pointer;
+			user-select: none;
+		}
+	}
+</style>

+ 398 - 0
TEAMModelOS/ClientApp/src/common/BaseKonva.vue

@@ -0,0 +1,398 @@
+<template>
+	<div class="app-container">
+		<div id="container" ref="container" class="container" />
+	</div>
+</template>
+
+<script>
+	import Konva from 'konva'
+
+	export default {
+		name: 'DragAndDrop',
+		props: {
+			image: {
+				required: false,
+				type: [String],
+				default: null
+			},
+
+			options: { //配置项
+				required: false,
+				type: [Object],
+				default: () => null
+			},
+		},
+		data() {
+			return {
+				myStage: null,
+				myLayer: null,
+				isText: false,
+				mode: 'paint',
+				config: {
+					textColor: 'red',
+					textSize: 16,
+					isDpr: false, //是否使用dpr兼容高分屏 [Boolean] 可选
+					lastWriteSpeed: 1, //书写速度 [Number] 可选
+					lastWriteWidth: 2, //下笔的宽度 [Number] 可选
+					lineCap: 'round', //线条的边缘类型 [butt]平直的边缘 [round]圆形线帽 [square]	正方形线帽
+					lineJoin: 'round', //线条交汇时边角的类型  [bevel]创建斜角 [round]创建圆角 [miter]创建尖角。
+					canvasWidth: 1200, //canvas宽高 [Number] 可选
+					canvasHeight: 600, //高度  [Number] 可选
+					isShowBorder: true, //是否显示边框 [可选]
+					bgColor: '#fcc', //背景色 [String] 可选 注:这背景色仅仅只是canvas背景,保存的图片仍然是透明的
+					borderWidth: 1, // 网格线宽度  [Number] 可选
+					borderColor: "#ff787f", //网格颜色  [String] 可选
+					writeWidth: 5, //基础轨迹宽度  [Number] 可选
+					maxWriteWidth: 30, // 写字模式最大线宽  [Number] 可选
+					minWriteWidth: 5, // 写字模式最小线宽  [Number] 可选
+					writeColor: 'red', // 轨迹颜色  [String] 可选
+					isSign: false, //签名模式 [Boolean] 默认为非签名模式,有线框, 当设置为true的时候没有任何线框
+					imgType: 'png' //下载的图片格式  [String] 可选为 jpeg  canvas本是透明背景的
+				},
+			}
+		},
+		created() {
+			this.$nextTick(() => {
+				this.initStage()
+			})
+		},
+		methods: {
+			initStage() {
+				let that = this
+				var stage = new Konva.Stage({
+					container: 'container',
+					width: this.config.canvasWidth,
+					height: this.config.canvasHeight
+				});
+				this.$refs.container.style.width = this.config.canvasWidth + 'px'
+				var layer = new Konva.Layer();
+
+				var isPaint = false;
+				var mode = 'brush';
+				var lastLine;
+
+				stage.on('mousedown.1 touchstart.1', function(e) {
+					if (that.isText || (that.mode !== 'paint' && that.mode !== 'transform')) {
+						that.isText = false
+						that.mode = 'paint'
+						let textNode = new Konva.Text({
+							text: "",
+							x: stage.getPointerPosition().x,
+							y: stage.getPointerPosition().y,
+							fontSize: that.config.textSize,
+							fill: that.config.textColor,
+							draggable: true,
+							width: 200,
+							// height: 50,
+							name: "text",
+						});
+						layer.add(textNode);
+						let tr = new Konva.Transformer({
+							node: textNode,
+							enabledAnchors: ["middle-left", "middle-right"],
+							centeredScaling: true,
+							anchorStroke: "#808080",
+							anchorFill: "#fff",
+							anchorSize: 12,
+							anchorCornerRadius: 5,
+							anchorStrokeWidth: 2,
+							borderStroke: "#000000",
+							borderStrokeWidth: 2,
+							borderDash: [3, 3],
+							padding: 10,
+							// boundBoxFunc: (oldBox, newBox) => {
+							// 	if (newBox.width < 20) {
+							// 		return oldBox
+							// 	}
+							// 	return newBox
+							// }
+
+						});
+						layer.add(tr);
+						textNode.on("transformstart", function() {
+							that.isPaint = false
+							that.mode = 'transform'
+						});
+						textNode.on("transform", function() {
+							that.isPaint = false
+							textNode.setAttrs({
+								width: Math.max(textNode.width() * textNode.scaleX(), 20),
+								height: Math.max(textNode.height() * textNode.scaleY(), 20),
+								scaleX: 1,
+								scaleY: 1
+							})
+						});
+						textNode.on("transformend", function() {
+							that.isPaint = true
+							that.mode = 'paint'
+						});
+						layer.draw();
+						textNode.on("dblclick", (e) => {
+							e.evt.preventDefault();
+							that.$parent.curMode = 'text'
+							that.isPaint = false
+							textNode.hide();
+							tr.show();
+							layer.draw();
+							let textPosition = textNode.absolutePosition();
+							let stageBox = stage.container().getBoundingClientRect();
+							let areaPosition = {
+								x: stageBox.left + textPosition.x,
+								y: stageBox.top + textPosition.y,
+							};
+							let textarea = document.createElement("textarea");
+							document.body.appendChild(textarea);
+							textarea.value = textNode.text();
+							textarea.style.position = "absolute";
+							textarea.style.top = areaPosition.y + "px";
+							textarea.style.left = areaPosition.x + "px";
+							textarea.style.width = textNode.width() - textNode.padding() * 2 + "px";
+							textarea.style.height = textNode.height() - textNode.padding() * 2 + 5 + "px";
+							textarea.style.fontSize = textNode.fontSize() + "px";
+							textarea.style.border = "none";
+							textarea.style.padding = "0px";
+							textarea.style.margin = "0px";
+							textarea.style.overflow = "hidden";
+							textarea.style.background = "none";
+							textarea.style.zIndex = "9999999";
+							textarea.style.outline = "none";
+							textarea.style.resize = "none";
+							textarea.style.lineHeight = textNode.lineHeight();
+							textarea.style.fontFamily = textNode.fontFamily();
+							textarea.style.transformOrigin = "left top";
+							textarea.style.textAlign = textNode.align();
+							textarea.style.color = textNode.fill();
+							let rotation = textNode.rotation();
+							let transform = "";
+							if (rotation) {
+								transform += "rotateZ(" + rotation + "deg)";
+							}
+
+							let px = 0;
+							let isFirefox =
+								navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
+							if (isFirefox) {
+								px += 2 + Math.round(textNode.fontSize() / 20);
+							}
+							transform += "translateY(-" + px + "px)";
+							textarea.style.transform = transform;
+							// reset height
+							textarea.style.height = "auto";
+							// after browsers resized it we can set actual value
+							textarea.style.height = textarea.scrollHeight + 3 + "px";
+							textarea.focus();
+
+							function removeTextarea() {
+								textarea.parentNode.removeChild(textarea);
+								window.removeEventListener("click", handleOutsideClick);
+								textNode.show();
+								tr.hide();
+								tr.forceUpdate();
+								layer.draw();
+							}
+
+							function setTextareaWidth(newWidth) {
+								if (!newWidth) {
+									// set width for placeholder
+									newWidth = textNode.placeholder.length * textNode.fontSize();
+								}
+								// some extra fixes on different browsers
+								let isSafari = /^((?!chrome|android).)*safari/i.test(
+									navigator.userAgent
+								);
+								let isFirefox =
+									navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
+								if (isSafari || isFirefox) {
+									newWidth = Math.ceil(newWidth);
+								}
+
+								let isEdge =
+									document.documentMode || /Edge/.test(navigator.userAgent);
+								if (isEdge) {
+									newWidth += 1;
+								}
+								textarea.style.width = newWidth + "px";
+							}
+
+							console.log(textarea)
+							textarea.addEventListener("keydown", function(e) {
+								if (e.keyCode === 27) {
+									removeTextarea();
+								}
+							});
+
+							textarea.addEventListener("keydown", function(e) {
+								that.$parent.curMode = 'text'
+								that.mode = 'paint'
+								that.isText = false
+								let scale = textNode.getAbsoluteScale().x;
+								setTextareaWidth(textNode.width() * scale);
+								textarea.style.height = "auto";
+								textarea.style.height =
+									textarea.scrollHeight + textNode.fontSize() + "px";
+							});
+
+							function handleOutsideClick(e) {
+								if (e.target !== textarea) {
+									textNode.text(textarea.value);
+									that.$parent.curMode = 'paint'
+									that.mode = 'paint'
+									removeTextarea();
+								}
+							}
+							setTimeout(() => {
+								window.addEventListener("click", handleOutsideClick);
+							});
+						});
+					} else if(that.mode === 'paint') {
+						console.log('进入绘制')
+						isPaint = true;
+						var pos = stage.getPointerPosition();
+						lastLine = new Konva.Line({
+							stroke: that.config.writeColor,
+							strokeWidth: that.config.writeWidth,
+							globalCompositeOperation: mode === 'brush' ? 'source-over' : 'destination-out',
+							points: [pos.x, pos.y],
+						});
+						layer.add(lastLine);
+					}else{
+						// 如果是点击的变形框 则不处理
+					}
+
+				});
+
+				stage.on('mouseup touchend', function() {
+					isPaint = false;
+				});
+
+				stage.on('mousemove touchmove', function(e) {
+					// e.evt.stopPropagation();
+					if (!isPaint || that.mode === 'text') {
+						return;
+					}
+					const pos = stage.getPointerPosition();
+					var newPoints = lastLine.points().concat([pos.x, pos.y]);
+					lastLine.points(newPoints);
+					layer.batchDraw();
+				});
+
+				stage.add(layer);
+				this.myLayer = layer
+				this.myStage = stage
+			},
+			
+			/* 渲染背景图 */
+			doRenderImg(src){
+				console.log(src)
+				const image = new Image()
+				image.src = src
+				image.setAttribute('crossOrigin', 'anonymous')
+				image.onload = () => {
+					const renderWidth = image.width > 1200 ? 1200 : image.width
+					const renderHeight = (image.height / image.width) * renderWidth
+					this.config.canvasWidth = renderWidth
+					this.config.canvasHeight = renderHeight
+					this.initStage()
+					const graph = new Konva.Image({
+						x: (this.myStage.attrs.width - renderWidth) / 2,
+						y: (this.myStage.attrs.height - renderHeight) / 2,
+						image: image,
+						width: renderWidth,
+						height: renderHeight
+					})
+					this.myLayer.add(graph)
+					this.myLayer.batchDraw()
+				}
+			},
+
+			/*白板缩放后 重新获取新的坐标未知*/
+			getRelativePointerPosition(node) {
+				// 函数将返回相对于传递的节点的指针位置
+				var transform = node.getAbsoluteTransform().copy();
+				// 为了检测相对位置,需要进行逆变换
+				transform.invert();
+				// 获取指针(如鼠标或触摸)位置
+				var pos = node.getStage().getPointerPosition();
+				// now we find relative point
+				return transform.point(pos);
+			},
+
+			/* 撤销操作 */
+			doUndo() {
+				this.myLayer.children.pop()
+				this.myLayer.draw()
+			},
+
+			/* 绘制输入的文本 */
+			doAddText(val) {
+				this.isText = true
+			},
+
+			/* 清屏操作 */
+			canvasClear() {
+				this.initStage()
+			},
+
+			/* 截图保存图片 */
+			saveAsImg() {
+				return new Promise(async (r, j) => {
+					console.log(this.myStage)
+					const image = new Image();
+					let compressImg = await this.$editorTools.compressImg(this.myStage.toDataURL(),
+						this.myStage.attrs.width, this.myStage.attrs.height)
+					r({
+						base64: compressImg,
+						width: this.myStage.attrs.width,
+						height: this.myStage.attrs.height
+					})
+				})
+			}
+		},
+
+		watch: {
+			'options.writeWidth': {
+				handler(n) {
+					this.config.writeWidth = n
+				},
+				immediate: true
+			},
+			'options.writeColor': {
+				handler(n) {
+					this.config.writeColor = n
+				},
+				immediate: true
+			},
+			'options.textColor': {
+				handler(n) {
+					this.config.textColor = n
+				},
+				immediate: true
+			},
+			'options.textSize': {
+				handler(n) {
+					this.config.textSize = n
+				},
+				immediate: true
+			},
+			// 'options.canvasWidth': {
+			// 	handler(n) {
+			// 		this.config.canvasWidth = n
+			// 		this.initStage();
+			// 	},
+			// },
+			// 'options.canvasHeight': {
+			// 	handler(n) {
+			// 		this.config.canvasHeight = n
+			// 		this.initStage();
+			// 	},
+			// },
+		},
+	}
+</script>
+<style>
+	.container {
+		max-width: 1200px;
+		margin: 0 auto;
+		background-color: #dadada;
+	}
+</style>

+ 74 - 56
TEAMModelOS/ClientApp/src/common/BaseMyCanvas.vue

@@ -1,46 +1,44 @@
 <template>
 	<div id="baseCanvas" @mouseover="mouseOver" @mouseleave="mouseLeave">
-		<BaseCanvas class="sign-canvas" ref="SignCanvas" :options="options"/>
+		<!-- <BaseCanvas class="sign-canvas" ref="SignCanvas" :options="options"/> -->
+		<BaseKonva class="sign-canvas" ref="SignCanvas" :options="options"/>
 		<div class="canvas-tools animated fadeIn" ref="canvasTools">
-			<span class="canvas-tools-item" style="border-radius: 15px 0 0 0;">
-				<Poptip trigger="hover">
+			<span :class="['canvas-tools-item',curMode === 'paint' ? 'tools-active' : '']" @click="doPaint()"  style="border-radius: 15px 0 0 0;">
+				<Poptip trigger="click">
 					<div class="flex-center">
-						<Icon type="md-brush" />
-						<span>粗细</span>
+						<Icon type="md-create" />
+						<span>绘制</span>
 					</div>
-					<div slot="content" class="widthDotBox">
-						<span class="widthDot widthDot5" :style="{ backgroundColor: (activeDot === 2 ? 'red' : '#827a7a')}" @click="onSelectWriteWidth(2,$event)"></span>
-						<span class="widthDot widthDot1" :style="{ backgroundColor: (activeDot === 5 ? 'red' : '#827a7a')}" @click="onSelectWriteWidth(5,$event)"></span>
-						<span class="widthDot widthDot2" :style="{ backgroundColor: (activeDot === 10 ? 'red' : '#827a7a')}" @click="onSelectWriteWidth(10,$event)"></span>
-						<span class="widthDot widthDot3" :style="{ backgroundColor: (activeDot === 15 ? 'red' : '#827a7a')}" @click="onSelectWriteWidth(15,$event)"></span>
-						<span class="widthDot widthDot4" :style="{ backgroundColor: (activeDot === 20 ? 'red' : '#827a7a')}" @click="onSelectWriteWidth(20,$event)"></span>
+					<div slot="content" class="textBox">
+						 <p class="text-tools-title">画笔粗细 <span style="font-weight: bold;color: #0086E6;margin-left: 15px;font-size: 18px;">{{ options.textSize }}</span> </p>
+						 <Slider v-model="options.writeWidth"></Slider>
+						 <p class="text-tools-title">画笔颜色</p>
+						 <ColorPicker v-model="options.writeColor" />
 					</div>
 				</Poptip>
 			</span>
-			<span class="canvas-tools-item" v-if="!isStudent">
-				<Poptip trigger="hover">
+			<span :class="['canvas-tools-item',curMode === 'text' ? 'tools-active' : '']" @click="addText()">
+				<Poptip trigger="click">
 					<div class="flex-center">
-						<Icon type="md-color-palette" :color="activeColor" />
-						<span>画笔颜色</span>
+						<Icon type="md-text" />
+						<span>文本输入</span>
 					</div>
-					<div slot="content" class="widthDotBox">
-						<span class="color-item color-item-black" :style="{ borderColor: (activeColor === 'black' ? 'black' : 'transparent')}"
-						 @click="onSelectWriteColor('black',$event)"></span>
-						<span class="color-item color-item-blue" :style="{ borderColor: (activeColor === 'blue' ? 'black' : 'transparent')}"
-						 @click="onSelectWriteColor('blue',$event)"></span>
-						<span class="color-item color-item-red" :style="{ borderColor: (activeColor === 'red' ? 'black' : 'transparent')}"
-						 @click="onSelectWriteColor('red',$event)"></span>
+					<div slot="content" class="textBox">
+						 <p class="text-tools-title">字体大小 <span style="font-weight: bold;color: #0086E6;margin-left: 15px;font-size: 18px;">{{ options.textSize }}</span> </p>
+						 <Slider v-model="options.textSize"></Slider>
+						 <p class="text-tools-title">字体颜色</p>
+						 <ColorPicker v-model="options.textColor" />
 					</div>
 				</Poptip>
 			</span>
-			<span class="canvas-tools-item" @click="addText()">
-				<Icon type="md-text" />
-				<span>文本输入</span>
-			</span>
 			<span class="canvas-tools-item" @click="canvasClear()">
 				<Icon type="md-refresh" />
 				<span>清屏</span>
 			</span>
+			<span class="canvas-tools-item" @click="undo()">
+				<Icon type="md-undo" />
+				<span>撤销</span>
+			</span>
 			<span class="canvas-tools-item" @click="closeModal()">
 				<Icon type="md-close" />
 				<span>关闭</span>
@@ -50,14 +48,6 @@
 				<span>保存</span>
 			</span>
 		</div>
-		
-		<Modal v-model="addTextModal" title="添加文本" ref="pointRef" width="400px" class="related-point-modal" style="z-index:99999">
-			<span>输入文本内容</span>
-			<Input v-model="addTextVal"  style="margin:10px 0" />
-			<div slot="footer">
-				<Button type="primary" style="width: 100px;" @click="doAddText">确认</Button>
-			</div>
-		</Modal>
 	</div>
 </template>
 <script>
@@ -91,7 +81,10 @@
 				addTextVal:'',
 				activeDot: 5,
 				activeColor: 'black',
+				curMode:'paint',
 				options: {
+					textColor:'red',
+					textSize:16,
 					isDpr: false, //是否使用dpr兼容高分屏 [Boolean] 可选
 					lastWriteSpeed: 1, //书写速度 [Number] 可选
 					lastWriteWidth: 2, //下笔的宽度 [Number] 可选
@@ -104,7 +97,7 @@
 					borderWidth: 2, // 网格线宽度  [Number] 可选
 					borderColor: "#7c7c7c", //网格颜色  [String] 可选
 					writeWidth: 5, //基础轨迹宽度  [Number] 可选
-					writeColor: '#000', // 轨迹颜色  [String] 可选
+					writeColor: 'red', // 轨迹颜色  [String] 可选
 					isSign: true, //签名模式 [Boolean] 默认为非签名模式,有线框, 当设置为true的时候没有任何线框
 					imgType: 'png' //下载的图片格式  [String] 可选为 jpeg  canvas本是透明背景的
 				}
@@ -116,18 +109,25 @@
 		methods: {
 			// 移入
 			mouseOver() {
-				this.$refs.canvasTools.style.display = 'flex'
+				// this.$refs.canvasTools.style.display = 'flex'
 			},
 			// 移出
 			mouseLeave() {
-				this.$refs.canvasTools.style.display = 'none'
+				// this.$refs.canvasTools.style.display = 'none'
 			},
 			onChangeWriteColor(val) {
 				this.options.writeColor = val
 			},
 			
 			addText(){
-				this.addTextModal = true
+				this.curMode = 'text'
+				this.$refs.SignCanvas.isText = true
+				this.$refs.SignCanvas.mode = 'text'
+			},
+			
+			doPaint(){
+				this.curMode = 'paint'
+				this.$refs.SignCanvas.mode = 'paint'
 			},
 			
 			doAddText(){
@@ -136,7 +136,6 @@
 			},
 
 			onSelectWriteColor(val) {
-				console.log(val)
 				this.options.writeColor = val
 				this.activeColor = val
 			},
@@ -146,7 +145,6 @@
 			},
 
 			onSelectWriteWidth(val) {
-				console.log(val)
 				this.options.writeWidth = val
 				this.activeDot = val
 			},
@@ -154,6 +152,11 @@
 			closeModal() {
 				this.$emit('onCloseModal')
 			},
+			
+			undo(){
+				this.$refs.SignCanvas.doUndo();
+			},
+			
 			/**
 			 * 清除画板
 			 */
@@ -165,18 +168,19 @@
 			initBgImg() {
 				if (this.bgImg) {
 					this.$nextTick(() => {
-						var img = new Image()
-						img.setAttribute('crossOrigin', 'anonymous');
-						img.src = this.bgImg
-						img.onload = () => {
-							let renderWidth = img.width > 1200 ? 1200 : img.width
-							let renderHeight = renderWidth * (img.height / img.width)
-							this.options.canvasWidth = renderWidth
-							this.options.canvasHeight = renderHeight
-							setTimeout(() => {
-								this.$refs.SignCanvas.context.drawImage(img, 0, 0, img.width, img.height, 0, 0, renderWidth, renderHeight);
-							}, 10)
-						}
+						this.$refs.SignCanvas.doRenderImg(this.bgImg)
+						// var img = new Image()
+						// img.setAttribute('crossOrigin', 'anonymous');
+						// img.src = this.bgImg
+						// img.onload = () => {
+						// 	let renderWidth = img.width > 1200 ? 1200 : img.width
+						// 	let renderHeight = renderWidth * (img.height / img.width)
+						// 	this.options.canvasWidth = renderWidth
+						// 	this.options.canvasHeight = renderHeight
+						// 	setTimeout(() => {
+						// 		// this.$refs.SignCanvas.context.drawImage(img, 0, 0, img.width, img.height, 0, 0, renderWidth, renderHeight);
+						// 	}, 10)
+						// }
 					})
 				}
 			},
@@ -191,7 +195,7 @@
 
 		},
 		mounted() {
-			this.activeColor = this.writeColor || 'black'
+			this.options.writeColor = this.writeColor || 'black'
 			/* 如果有传背景图片 则接收后绘制作为Canvas的背景 */
 
 		},
@@ -199,6 +203,7 @@
 			writeColor: {
 				handler(n, o) {
 					this.onSelectWriteColor(n)
+					this.options.writeColor = n || 'black'
 				},
 				immediate: true
 			},
@@ -234,9 +239,13 @@
 		bottom: 0;
 		left: 50%;
 		transform: translate(-50%, 0);
-		display: none;
+		display: flex;
 		background-color: #696969;
 		border-radius: 15px 15px 0 0;
+		
+		.tools-active{
+			background-color: #2d8cf0;
+		}
 
 		&-item {
 			display: flex;
@@ -249,8 +258,8 @@
 			cursor: pointer;
 
 			&:hover {
-				background-color: rgba(255, 255, 255, .6);
-				color: #000;
+				background-color: #2d8cf0;
+				// color: #000;
 			}
 
 			.ivu-icon {
@@ -321,6 +330,15 @@
 				}
 			}
 		}
+		
+		.textBox{
+			color: #717171;
+			width: 200px;
+			
+			.text-tools-title{
+				margin: 10px 0;
+			}
+		}
 
 		.flex-center {
 			display: flex;

+ 1 - 1
TEAMModelOS/ClientApp/src/common/BasePackage.vue

@@ -23,7 +23,7 @@
              * 获取blob授权信息
              * */
             getSasStr(list) {
-                this.$api.uploadFile.getContainerSAS().then(
+                this.$api.blob.getContainerSAS().then(
                     (res) => {
                         if (res.error == null) {
                             this.sasString = res.result.data.SAS

+ 2 - 4
TEAMModelOS/ClientApp/src/common/BaseUserPoptip.vue

@@ -121,10 +121,8 @@
 				this.getSize()
 			},
 			//获取Blob空间信息
-			async getSize() {
-				let sasRes = await this.$tools.getPrivateSas()
-                let containerClient = new BlobTool(sasRes.url, sasRes.name, sasRes.sas, 'private')
-                containerClient.getSize().then(
+			getSize() {
+                BlobTool.getSizeByServe(this.$store.state.userInfo.TEAMModelId).then(
                     res => {
                         this.sizeInfo = res
                     },

+ 3 - 2
TEAMModelOS/ClientApp/src/common/UploadModal.vue

@@ -1,7 +1,7 @@
 <template>
     <Modal v-model="uploadStatus" :ok-text="textLoading ? '上传中': isComplete ? '完成':'确认上传'" cancel-text="取消上传" :title="$t('teachContent.btnUpload')" class="upload-modal dark-iview-modal" width="660" :mask-closable="false" :closable="false" @on-ok="modalOk" @on-cancel="modalCancel" :loading="modalLoading">
         <div class="upload-file-box">
-            <Upload type="drag" action="" :show-upload-list="false" multiple :before-upload="customUpload" class="upload-wrap">
+            <Upload type="drag" action="" :show-upload-list="false" multiple :before-upload="customUpload" class="upload-wrap" :disabled="textLoading" >
                 <Icon class="upload-icon" custom="iconfont icon-upload" v-show="!uploadedList.length" />
                 <p class="upload-text" :style="{marginTop: uploadedList.length ? '25px':'0px'}">
                     <Icon size="24" style="font-size: 22px;vertical-align: baseline;margin-right: 5px;" custom="iconfont icon-upload" v-show="uploadedList.length" />
@@ -224,7 +224,7 @@ export default {
 
                 if (prefixFiles.length) {
                     prefixFiles.forEach(item => {
-                        this.$api.uploadFile.deletePrefix({
+                        this.$api.blob.deletePrefix({
                             cntr: this.routerScope == 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId,
                             prefix: `res/${item.name.replace('.HTEX', '')}`
                         }).then(
@@ -337,6 +337,7 @@ export default {
                 this.textLoading = false
                 this.isComplete = true
                 this.modalLoading = false
+                
             }
         },
         //处理图片缩略图

+ 15 - 2
TEAMModelOS/ClientApp/src/components/evaluation/ExerciseList.vue

@@ -84,7 +84,7 @@
 					<span class="item-tools-info">难度:{{ exersicesDiff[item.level - 1] }}</span>
 					<span class="item-tools-info">认知层次:{{ exersicesField[item.field - 1] }}</span>
 					<span class="item-tools-info">使用次数:{{ item.usageCount || 0 }} 次</span>
-					<span class="item-tools-info">更新时间:{{ $tools.formatTime(item.createTime)  || 0 }} </span>
+					<span class="item-tools-info" v-if="item.createTime">更新时间:{{ $tools.formatTime(item.createTime)  || 0 }} </span>
 					<Button type="info" v-if="!isAnalysis" :style="{backgroundColor:selectList.map(i => i.id).indexOf(item.id) > -1 ? '#bbbbbb' : '#2db7f5'}"
 					 @click.stop="onSelectItem(item,index)">{{ selectList.map(i => i.id).indexOf(item.id) > -1 ? '移除' : '选题'}}</Button>
 				</div>
@@ -117,10 +117,16 @@
 			isAnalysis:{
 				type:Boolean,
 				default:false
+			},
+			
+			examScope:{
+				type:String,
+				default:null
 			}
 		},
 		data() {
 			return {
+				examPropScope:null,
 				dataLoading: false,
 				exersicesType: this.$GLOBAL.EXERCISE_TYPES(),
 				exersicesDiff: this.$GLOBAL.EXERCISE_DIFFS(),
@@ -147,7 +153,8 @@
 			getBlobList(list){
 				return new Promise(async (r,j) => {
 					if(list.length){
-						let blobList = await this.$evTools.getFullItem(list)
+						/* 如果是评测里面的试题 则不需要从getFullItem取 */
+						let blobList = this.examPropScope ? list : await this.$evTools.getFullItem(list)
 						r(blobList)
 					}else{
 						r([])
@@ -315,6 +322,12 @@
 					}
 				}
 			},
+			examScope:{
+				handler(n,o){
+					this.examPropScope = n
+				},
+				immediate:true
+			},
 			selQue:{
 				handler(n,o){
 					if(n){

+ 1 - 1
TEAMModelOS/ClientApp/src/components/homework/BaseHwTable.vue

@@ -569,7 +569,7 @@
              * 获取blob授权信息
              * */
             getSasStr() {
-                this.$api.uploadFile.getContainerSAS().then(
+                this.$api.blob.getContainerSAS().then(
                     (res) => {
                         if (res.error == null) {
                             this.sasString = res.result.data.SAS

+ 1 - 1
TEAMModelOS/ClientApp/src/components/selflearn/ContentFileList.vue

@@ -52,7 +52,7 @@
         },
         methods: {
             getSas() {
-                this.$api.uploadFile.getContainerSAS().then(
+                this.$api.blob.getContainerSAS().then(
                     (res) => {
                         if (res.error == null) {
                             this.sasString = res.result.data.SAS

+ 3 - 15
TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseMyTable.vue

@@ -215,18 +215,6 @@
                             className: 'table-rank-value'
                         }
                     }, params.row.classId),
-                    // h('Icon', {
-                    //     props: {
-                    //         type: (row.changesStatus === 1 && row.changesVal !== 0) ? 'md-trending-up' : (row.changesStatus === -1 && row.changesVal !== 0) ? 'md-trending-down' : 'md-git-commit',
-                    //         color: (row.changesStatus === 1 && row.changesVal !== 0) ? '#13ff13' : (row.changesStatus === -1 && row.changesVal !== 0) ? '#fd4e4e' : 'yellow',
-                    //         size: '22'
-                    //     },
-                    //     style: {
-                    //         cursor: 'pointer',
-                    //         marginLeft: '10px'
-                    //     }
-                    // })
-
                 ])
             },
 			
@@ -236,7 +224,7 @@
                     on: {
                         click: function() {
                             that.$parent.$parent.$parent.isShowQuestions = true
-                            that.$router.push({ path: '/total/questionList', query: { QIndex: params.row.id } })
+							that.$router.push({ path: '/total/questionList', query: { QIndex: params.row.id } })
                         }
                     },
                     style: {
@@ -390,7 +378,7 @@
                         on: {
                             'click': function() {
                                 that.$parent.$parent.$parent.isShowQuestions = true
-                                that.$router.push({ path: '/total/questionList', query: { QIndex: item } })
+                                that.$router.push({ path: '/total/questionList', query: { QIndex: isNaN(item) ? 1 : item } })
                             }
                         }
                     }, item + (index === list.length - 1 ? '' : ' , '))
@@ -414,7 +402,7 @@
                         on: {
                             'click': function() {
                                 that.$parent.$parent.$parent.isShowQuestions = true
-                                that.$router.push({ path: '/total/questionList', query: { QIndex: item } })
+                                that.$router.push({ path: '/total/questionList', query: { QIndex: isNaN(item) ? 1 : item } })
                             }
                         }
                     }, item + (index === list.length - 1 ? '' : ' , '))

+ 25 - 26
TEAMModelOS/ClientApp/src/components/student-web/EventBasicInfo.vue

@@ -2,16 +2,15 @@
   <Row :gutter="30">
     <i-col :xs="24" :sm="24" :md="24" :lg="getCurrentLang()=='tw'?12:24" class="title-part">
       <h2 class="event-title">
-        <span class="title-mark" v-if=" eventType=='自主學習' &&from!='hiteach'">
-          {{ getCurrentLang()=='tw'?'评量':transTypetoEn(eventType) }}456
+        <!--<span class="title-mark" v-if=" eventType=='自主學習' &&from!='hiteach'">
+          {{ getCurrentLang()=='tw'?'评量':transTypetoEn(eventType) }}
         </span>
         <span class="title-mark" v-if=" eventType!='自主學習' &&from=='hiteach'">
-          {{ 'T'+this.$store.getters.getItemTitle.eventID.substr(0, 5) }}456
-        </span>
+          {{ 'T'+this.$store.getters.getItemTitle.eventID.substr(0, 5) }}
+        </span>-->
         <span
           class="title-mark"
-          v-if="eventType!='自主學習' &&from!='hiteach'"
-        >评量</span>
+        >{{$t("studentWeb.home.exam")}}</span>
         {{ this.$store.getters.getItemTitle.name }}
       </h2>
     </i-col>
@@ -21,8 +20,8 @@
           v-if="this.$store.getters.getItemTitle.eventType == 'exam' && paper.length !== 0 "
         >
           <svg-icon icon-class="subject" class="base-info-icon" />{{ $t('studentWeb.baseInfo.subject')}}
-          <span class="base-info-text">{{paper.length > 1? '综合科目':paper[0].subject.name }}</span>
-          <span class="base-info-text" v-if="getCurrentLang()=='en'">{{ transSubjecttoEn(this.$store.getters.getItemTitle.eventSubject) }}</span>
+        <span class="base-info-text">{{paper.length > 1? $t('studentWeb.event.allSubject'):paper[0].subject.name }}</span>
+          <!--<span class="base-info-text" v-if="getCurrentLang()=='en'">{{ transSubjecttoEn(this.$store.getters.getItemTitle.eventSubject) }}</span>-->
         </li>
 
         <!--<li>
@@ -55,10 +54,10 @@
 
 
 
-          <span
+          <!--<span
             class="base-info-text"
             v-if="from == '通知'||from == 'hiteach'"
-          >{{ this.$store.getters.getItemTitle.endTime + " 14:20" }}54345</span>
+          >{{ this.$store.getters.getItemTitle.endTime + " 14:20" }}54345</span>-->
         </li>
         <!--<li v-if=" from == 'hiteach'">
            <svg-icon icon-class="time" class="base-info-icon" />{{$t('studentWeb.baseInfo.classTime')}}:
@@ -116,22 +115,22 @@ export default {
     getCurrentLang() {
       return localStorage.getItem('lang');
     },
-     transTypetoEn(type) {
-      if (type == "投票") return "Vote";
-      else if (type == "評量") return "Exam";
-      else if (type == "作業") return "Homework";
-      else if (type == "課前預習") return "Preview";
-      else if (type == "公告") return "Billboard";
-      else if (type == "教師私訊") return "Message";
-      else if(type == "自主學習") return "Self-study";
-      else if (type == "Hiteach電子筆記") return "HiTeach Note";
-    },
-    transSubjecttoEn(type){
-      if (type == "國文") return "Chinese";
-      else if (type == "英文") return "English";
-      else if (type == "數學") return "Math";
-      else if (type == "綜合學科") return "Comprehensive";
-    }
+    // transTypetoEn(type) {
+    //  if (type == "投票") return "Vote";
+    //  else if (type == "評量") return "Exam";
+    //  else if (type == "作業") return "Homework";
+    //  else if (type == "課前預習") return "Preview";
+    //  else if (type == "公告") return "Billboard";
+    //  else if (type == "教師私訊") return "Message";
+    //  else if(type == "自主學習") return "Self-study";
+    //  else if (type == "Hiteach電子筆記") return "HiTeach Note";
+    //},
+    //transSubjecttoEn(type){
+    //  if (type == "國文") return "Chinese";
+    //  else if (type == "英文") return "English";
+    //  else if (type == "數學") return "Math";
+    //  else if (type == "綜合學科") return "Comprehensive";
+    //}
   },
 };
 </script>

+ 122 - 77
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReport.vue

@@ -148,10 +148,9 @@
                                 </div>
                                 <div class="TitleRec1"><span style="margin:5px;color:#1472c7">{{$t("studentWeb.exam.report.ansRes")}}:</span></div>
                                 <br />
-                                <div v-if="ansData[index]" style="margin-left:10px" v-html="ansData[index].length > 0 ? ansData[index][0] : '未作答'"></div>
+                                <div v-if="ansData[index]" style="margin-left:10px" v-html="ansData[index].length > 0 ? ansData[index][0] : $t('studentWeb.exam.report.noAns')"></div>
                             </div>
                         </div>
-                        <!--<br />-->
                         <div class="rightAnalys">
                             <div class="TitleRec2"><span style="margin-left:5px">{{$t("studentWeb.exam.report.testAns")}}:</span></div>
                             <br />
@@ -163,11 +162,12 @@
                             <div style="margin-left:10px;" v-html="question.explain != '' ?  question.explain : $t('studentWeb.exam.report.noAnalyse') "></div>
                             <div v-show="examInfo.stuScore[index] != -1 && examInfo.stuScore[index] != question.score" class="TitleRec2"><span style="margin-left:5px">{{$t("studentWeb.exam.report.repairSource")}}:</span></div>
                             <br />
-                            <div style="margin-left:10px;display:flex" v-show="examInfo.stuScore[index] != -1 && examInfo.stuScore[index] != question.score">
-                                <div v-if="question.repair" class="repair-box">
+                            <div style="margin-left:10px;display:flex" v-show="examInfo.stuScore[index] != -1 && examInfo.stuScore[index] != question.score ">
+                                <span v-if="question.repair.length == 0">{{$t("studentWeb.exam.report.noSource")}}</span>
+                                <div v-if="question.repair && question.repair.length > 0" class="repair-box">
                                     <Collapse style="width:85%" accordion @on-change="getSource(question.repair,question)">
                                         <Panel name="1">
-                                            链接资源
+                                            {{$t("studentWeb.exam.report.linkSource")}}
                                             <p slot="content">
                                                 <List border size="small">
                                                     <ListItem v-for="(item,normalIndex) in repairSource.normal" :key="normalIndex">
@@ -176,13 +176,13 @@
                                                         </span><a :href="item.blobUrl" target="_blank">{{item.blobUrl}}</a>
                                                     </ListItem>
                                                     <ListItem v-show="repairSource.normal.length == 0">
-                                                        <span>暂无资源</span>
+                                                        <span>{{$t("studentWeb.exam.report.noSource")}}</span>
                                                     </ListItem>
                                                 </List>
                                             </p>
                                         </Panel>
                                         <Panel name="2">
-                                            文件资源
+                                            {{$t("studentWeb.exam.report.fileSource")}}
                                             <p slot="content">
                                                 <List border size="small">
                                                     <ListItem v-for="(item,fileIndex) in repairSource.file" :key="fileIndex">
@@ -204,11 +204,9 @@
                                                                 <Icon type="md-download" size="18" @click="downloadFile(item)" />
                                                             </span>
                                                         </div>
-                                                       
-                                                    
                                                     </ListItem>
                                                     <ListItem v-show="repairSource.file.length == 0">
-                                                        <span>暂无资源</span>
+                                                        <span>{{$t("studentWeb.exam.report.noSource")}}</span>
                                                     </ListItem>
                                                 </List>
                                             </p>
@@ -221,31 +219,48 @@
                 </div>
             </div>
         </div>
-        <!--<div v-if="previewStatus" class="image-viewer">
-            <div style="width:fit-content;position:relative;margin:auto;">
-                <Icon type="md-close" class="close-icon" @click="closePreview" />
-                <video v-if="previewFile.type == 'video'" id="previewVideo" :src="previewFile.url" width="870" controls="controls" style="max-height: 800px;">
+        <Modal v-model="previewStatus" footer-hide width="65%" @on-cancel="closePreview">
+            <p slot="header" 
+               style="color:#f60;text-align:center">
+                <Icon type="ios-information-circle"></Icon>
+                <span>{{$t("studentWeb.exam.report.fileView")}}</span>
+            </p>
+            <div class="file-box" v-if="previewStatus">
+                <video v-if="previewFile.file == 'video'"
+                       id="previewVideo"
+                       :src="previewFile.blobUrl"
+                       width="870"
+                       controls="controls"
+                       style="max-height: 800px;">
                     {{$t('teachContent.tips8')}}
                 </video>
-                <audio v-else-if="previewFile.type == 'audio'" controls>
-                    <source :src="previewFile.url">
-                    您的浏览器不支持 audio 元素。
+                <audio v-else-if="previewFile.file == 'audio'" controls>
+                    <source :src="previewFile.blobUrl">
                 </audio>
-                <img v-else-if="previewFile.type == 'image'" :src="previewFile.url" style="border-radius: 5px;max-height: 800px;max-width:870px;" />
-                <embed v-else-if="previewFile.extension == 'PDF'" :src="previewFile.url" width="870" height="720" />
-                <iframe v-else :src="'https://view.officeapps.live.com/op/view.aspx?src=' + escapeBlobUrl" width='870' height='700' frameborder='1'></iframe>
+                <img v-else-if="previewFile.fileType == 'jpg'||previewFile.fileType =='png'||previewFile.fileType =='jpeg' "
+                     :src="previewFile.blobUrl"
+                     style="border-radius: 5px;max-height: 800px;max-width:870px;" />
+                <pdf ref="pdf"
+                     v-else-if="previewFile.fileType == 'pdf'"
+                     v-for="(page,index) in numPages"
+                     :key="index"
+                     :src="previewFile.blobUrl"
+                     :page="page"></pdf>
+                <!--<span v-else-if="previewFile.fileType == 'doc'||previewFile.fileType == 'csv'||previewFile.fileType == 'pptx' ">{{'https://view.officeapps.live.com/op/view.aspx?src=' + previewFile.blobUrl}}</span>-->
+                <iframe v-else-if="previewFile.fileType == 'doc'||previewFile.fileType == 'csv'||previewFile.fileType == 'pptx'||previewFile.fileType == 'xls' " :src="'https://view.officeapps.live.com/op/view.aspx?src=' + previewFile.blobUrl" width='870' height='700' frameborder='1'></iframe>
+                <span v-else>{{$t("studentWeb.exam.report.noReview")}}</span>
             </div>
-        </div>-->
+        </Modal>
     </div>
 </template>
 
 <script>
-    import BlobTool from '@/utils/blobTool.js';
-    import FileSaver from "file-saver";
+    //import BlobTool from '@/utils/blobTool.js';
+    //import FileSaver from "file-saver";
     //import JSZip from "jszip";
-    import elementResizeDetectorMaker from "element-resize-detector"
+    //import elementResizeDetectorMaker from "element-resize-detector"
+    import pdf from 'vue-pdf'
     import LessonTestReportCharts from "./LessonTestReportCharts/LessonTestReportCharts";
-    import { Random } from "mockjs";
     import Loading from "vue-loading-overlay";
     import "vue-loading-overlay/dist/vue-loading.css";
     export default {
@@ -253,6 +268,7 @@
         components: {
             LessonTestReportCharts,
             Loading,
+            pdf
         },
         props: {
             examInfo: {
@@ -277,40 +293,6 @@
                 closeAnsDetail: false,
                 checkedAns: ["right", "wrong", "noAns"],
                 testState: 0,
-                testType: [
-                    {
-                        label: "单选",
-                        value: "single"
-                    },
-                    {
-                        label: "多选",
-                        value: "multiple"
-                    },
-                    {
-                        label: "判断",
-                        value: "judge"
-                    },
-                    {
-                        label: "填空",
-                        value: "complete"
-                    },
-                    {
-                        label: "问答",
-                        value: "subjective"
-                    },
-                    {
-                        label: "综合",
-                        value: "compose"
-                    },
-                    {
-                        label: "改错",
-                        value: "correct"
-                    },
-                    {
-                        label: "连线",
-                        value: "connector"
-                    },
-                ],
                 paperData: [],
                 ansData: [],
                 repairSource: {
@@ -318,7 +300,9 @@
                     file: []
                 },
                 repairData: [],
-                previewStatus: false
+                previewStatus: false,
+                previewFile: {},
+                numPages:null
             };
         },
         mounted() {
@@ -326,19 +310,49 @@
             this.testJudge()
         },
         methods: {
-            getItemData(data) {
+            closePreview() {
+                this.previewStatus = !this.previewStatus
+                this.previewFile = {}
             },
-           async downloadFile(data) {
-                let code = data.blobUrl.split('/')
-                let req = {
-                    url: data.blobUrl
+           async getItemData(data) {
+               this.previewStatus = false
+                if (data.file) {
+                    this.previewFile = this._.cloneDeep(data)
+                    this.previewFile.blobUrl = await this.getFileSas(data)
+                    if (this.previewFile.fileType == 'doc' || this.previewFile.fileType == 'csv' || this.previewFile.fileType == 'xls'|| this.previewFile.fileType == 'pptx') {
+                        let url = escape(this.previewFile.blobUrl)
+                        this.previewFile.blobUrl = url
+                    }
+                    if (this.previewFile.fileType == 'pdf') {
+                        await this.getPdf(this.previewFile.blobUrl)
+                    }
+                    this.previewStatus = true
                 }
-                let curFile = ""
-                await this.$api.studentWeb.getFileSas(req).then(res => {
-                    curFile = res.url
+            },
+            getPdf(data) {
+                let loadingTask = pdf.createLoadingTask(data)
+                loadingTask.promise.then(pdf => {
+                    this.numPages = pdf.numPages
+                }).catch(err => {
+                    this.Message.warning(this.$t('studentWeb.exam.report.pdfErr'))
                 })
+            },
+            async getFileSas(data) {
+                if (data) {
+                    let req = {
+                        url: data.blobUrl
+                    }
+                    let curFile = ""
+                    await this.$api.studentWeb.getFileSas(req).then(res => {
+                         curFile = res.url
+                     })
+                    return curFile
+                }
+            },
+            async downloadFile(data) {
+                let blobData = await this.getFileSas(data)
                 const downloadRes = async () => {
-                    let response = await fetch(curFile); // 内容转变成blob地址
+                    let response = await fetch(blobData); // 内容转变成blob地址
                     let blob = await response.blob();  // 创建隐藏的可下载链接
                     let objectUrl = window.URL.createObjectURL(blob);
                     let a = document.createElement('a');
@@ -371,8 +385,6 @@
                     }
                 }
                 this.repairSource = source
-                //let code = data.blobUrl.split('/')
-                //console.log(code)
             },
             showTest() {
                 if (this.examInfo.subject !== undefined) {
@@ -403,13 +415,13 @@
                 }
             },
             getTestType(data) {
-                for (let item of this.testType) {
+                for (let item of this.$t('global.testType')) {
                     if (item.value == data) {
                         return item.label
                     }
                 }
             },
-            async getItem(data) {
+            async getItem(data) { //获取学生作答数据
                 let datas = []
                 if (data !== undefined) {
                     let key = this.$store.getters.getExamInfo.code.split('-')
@@ -426,7 +438,7 @@
                     return []
                 }
             },
-            formUrl(data) {
+            formUrl(data) {  //处理学生作答数据blob地址
                 let container = data.code
                 let a = ""
                 if (data.blob.indexOf('https://teammodelstorage') > -1) {
@@ -438,16 +450,22 @@
             },
            async formPaper() {
                 let paper = []
-                this.paperData.length = []
-                this.ansData.length = []
+                this.paperData.length = 0
+                this.ansData.length = 0
                 let data = this.$store.getters.getPaperInfo.item
                 let exam = [...data]
                 if (this.$store.getters.getPaperInfo.item.length) {
                     for (let i = 0; i < exam.length; i++) {
+                        if (exam[i].repair == undefined) {
+                            exam[i].repair = []
+                        }
                         if (exam[i].type == 'compose') {
                             let k = 1
                             if (exam[i].children.length > 0) {
                                 for (let items of exam[i].children) {
+                                    if (items.repair == undefined) {
+                                        items.repair = []
+                                    }
                                     items.parentInfo = exam[i]
                                     items.parent = i
                                     items.paperIndex = (i + 1) + '-' + k
@@ -460,7 +478,7 @@
                             paper.push(exam[i])
                         }
                     }
-                }
+               }
                this.paperData = [...paper]
                if (this.paperData.length) {
                    this.ansData = await this.getItem(this.examInfo.stuAns[0])
@@ -874,4 +892,31 @@
             max-height:300px;
             overflow:scroll;
         }
+        .file-box{
+            text-align:center;
+            height:100%;
+            overflow:scroll;
+        }
+    .lesson-test-report /deep/ .ivu-modal {
+        top: 50px !important;
+    }
+
+    .lesson-test-report .file-box /deep/ .ivu-modal-body {
+        padding: 16px;
+        font-size: 14px;
+        line-height: 1.5;
+        height: 800px;
+        overflow: scroll;
+    }
+    /*.image-viewer .close-icon {
+        position: absolute;
+        right: -16px;
+        top: -16px;
+        font-size: 24px;
+        color: black;
+        cursor: pointer;
+        border-radius: 50px;
+        background: white;
+        padding: 2px;
+    }*/
 </style>

+ 51 - 96
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReportCharts/StudentScore.vue

@@ -4,59 +4,59 @@
             <div class="stu-info">
                 <ul>
                     <li>
-                        <p><Icon type="ios-home" size="24" />本次考试名称:<span>--</span></p>
+                        <p><Icon type="ios-home" size="24" />{{$t("studentWeb.exam.studentScore.examName")}}:<span>{{$store.getters.getItemTitle.name}}</span></p>
                     </li>
                     <li>
-                        <p><Icon type="ios-paper" size="24" />考试类型:<span>--</span></p>
+                        <p><Icon type="ios-paper" size="24" />{{$t("studentWeb.exam.studentScore.examType")}}:<span>--</span></p>
                     </li>
                     <li>
-                        <p><Icon type="ios-list-box" size="24" />学习稳定系数:<span>--</span></p>
+                        <p><Icon type="ios-list-box" size="24" />{{$t("studentWeb.exam.studentScore.stableIndex")}}:<span>--</span></p>
                     </li>
                     <li>
-                        <p><Icon type="ios-person" size="24" />姓名:<span>{{$store.state.userInfo.name}}</span></p>
+                        <p><Icon type="ios-person" size="24" />{{$t("studentWeb.exam.studentScore.name")}}:<span>{{$store.state.userInfo.name}}</span></p>
                     </li>
                     <li>
-                        <p style="margin-top:5px">学号:<span>{{$store.state.userInfo.sub}}</span></p>
+                        <p style="margin-top:5px">{{$t("studentWeb.exam.studentScore.stuNo")}}:<span>{{$store.state.userInfo.sub}}</span></p>
                     </li>
                     <li>
-                        <p style="margin-top:5px">班级:<span>{{$store.state.user.studentProfile.classinfo.name}}</span></p>
+                        <p style="margin-top:5px">{{$t("studentWeb.exam.studentScore.class")}}:<span>{{$store.state.user.studentProfile.classinfo.name}}</span></p>
                     </li>
                 </ul>
             </div>
             <div class="stu-score">
-                <p class="all-sub">全学科成绩</p>
+                <p class="all-sub">{{$t("studentWeb.exam.studentScore.allSubScore")}}</p>
                 <ul>
                     <li style="margin-right:5%;margin-top:15px;margin-left:3%">
-                        <h2 style="color:cornflowerblue;font-size:18px">个人总分</h2>
+                        <h2 style="color:cornflowerblue;font-size:18px">{{$t("studentWeb.exam.studentScore.total")}}</h2>
                         <p style="color: cornflowerblue;font-size: 30px;font-weight: 800">{{scoreInfo.total}}</p>
                     </li>
                     <li class="sub-ave">
-                        <h2>班级平均分</h2>
+                        <h2>{{$t("studentWeb.exam.studentScore.cAverages")}}</h2>
                         <p>{{scoreInfo.classAverage}}</p>
                     </li>
                     <li class="sub-ave">
-                        <h2>年级平均分</h2>
+                        <h2>{{$t("studentWeb.exam.studentScore.gAverages")}}</h2>
                         <p>{{scoreInfo.gradeAverage}}</p>
                     </li>
                     <li class="sub-ave">
-                        <h2>区级平均分</h2>
+                        <h2>{{$t("studentWeb.exam.studentScore.aAverages")}}</h2>
                         <p>--</p>
                     </li>
                     <li class="sub-ave">
-                        <h2>班级排名</h2>
+                        <h2>{{$t("studentWeb.exam.studentScore.cIndex")}}</h2>
                         <p>{{scoreInfo.rankc}}<span>/{{scoreInfo.classCount}}</span></p>
                     </li>
                     <li class="sub-ave">
-                        <h2>年级排名</h2>
+                        <h2>{{$t("studentWeb.exam.studentScore.gIndex")}}</h2>
                         <p>{{scoreInfo.rankg}}<span>/{{scoreInfo.gradeCount}}</span></p>
                     </li>
                     <li class="sub-ave">
-                        <h2>区级排名</h2>
+                        <h2>{{$t("studentWeb.exam.studentScore.aIndex")}}</h2>
                         <!--<p>-<span>/1456</span></p>-->
                         <p>--</p>
                     </li>
                     <li>
-                        <span style="font-size:16px;margin-top:20px;display:block">是否进线</span>
+                        <span style="font-size:16px;margin-top:20px;display:block">{{$t("studentWeb.exam.studentScore.onLine")}}</span>
                         <p style="font-size:24px;font-weight:600;width:60px;text-align:center">--</p>
                     </li>
                 </ul>
@@ -64,34 +64,34 @@
             <div class="sub-title">
                 <ul>
                     <li class="sub-item">
-                        <span>学科</span>
+                        <span>{{$t("studentWeb.exam.studentScore.subject")}}</span>
                     </li>
                     <li class="sub-item">
-                        <span>个人得分</span>
+                        <span>{{$t("studentWeb.exam.studentScore.score")}}</span>
                     </li>
                     <li class="sub-item">
-                        <span>班级平均</span>
+                        <span>{{$t("studentWeb.exam.studentScore.cAverage")}}</span>
                     </li>
                     <li class="sub-item">
-                        <span>年级平均</span>
+                        <span>{{$t("studentWeb.exam.studentScore.gAverage")}}</span>
                     </li>
                     <li class="sub-item">
-                        <span>区级平均</span>
+                        <span>{{$t("studentWeb.exam.studentScore.aAverage")}}</span>
                     </li>
                     <li class="sub-item">
-                        <span>班级排名</span>
+                        <span>{{$t("studentWeb.exam.studentScore.cIndex")}}</span>
                     </li>
                     <li class="sub-item">
-                        <span>年级排名</span>
+                        <span>{{$t("studentWeb.exam.studentScore.gIndex")}}</span>
                     </li>
                     <li class="sub-item">
-                        <span>区级排名</span>
+                        <span>{{$t("studentWeb.exam.studentScore.aIndex")}}</span>
                     </li>
                     <li class="sub-item">
-                        <span>答对题数</span>
+                        <span>{{$t("studentWeb.exam.studentScore.rAns")}}</span>
                     </li>
                     <li class="sub-item">
-                        <span>综合难度</span>
+                        <span>{{$t("studentWeb.exam.studentScore.comIndex")}}</span>
                     </li>
                 </ul>
                 <ul v-for="(item,index) in scoreInfo.averageMap">
@@ -122,11 +122,11 @@
                     <li class="sub-item">
                         <div class="ans">
                             <div class="obj-ans">
-                                <h5>客观</h5>
+                                <h5>{{$t("studentWeb.exam.studentScore.objItem")}}</h5>
                                 <p>{{item.sub.objIndex}}<span>/{{item.sub.objItem.length}}</span></p>
                             </div>
                             <div class="sub-ans">
-                                <h5>主观</h5>
+                                <h5>{{$t("studentWeb.exam.studentScore.subItem")}}</h5>
                                 <p>{{item.sub.subIndex}}<span>/{{item.sub.subItem.length}}</span></p>
                             </div>
                         </div>
@@ -141,17 +141,17 @@
                 <div class="table-list">
                     <table class="gridtable" v-if="stuScore.length" style="width: 100%; max-width: 100%;margin-bottom: 20px;">
                         <tr>
-                            <th>科目</th>
-                            <th>题目</th>
-                            <th>客观题</th>
-                            <th>主观题</th>
+                            <th>{{$t("studentWeb.exam.studentScore.subjects")}}</th>
+                            <th>{{$t("studentWeb.exam.studentScore.item")}}</th>
+                            <th>{{$t("studentWeb.exam.studentScore.objItems")}}</th>
+                            <th>{{$t("studentWeb.exam.studentScore.subItems")}}</th>
                         </tr>
                         <tbody v-for="(que,index) in stuScore" :key="index">
                             <tr>
                                 <td width="4%" rowspan="4">{{que.name}}</td>
                             </tr>
                             <tr>
-                                <td width="8%">题号</td>
+                                <td width="8%">{{$t("studentWeb.exam.studentScore.itemIndex")}}</td>
                                 <td>
                                     <div class="score-box">
                                         <span v-for="(objQue,obj) in que.item.objItem" :key="obj">{{objQue.paperIndex}}</span>
@@ -164,7 +164,7 @@
                                 </td>
                             </tr>
                             <tr>
-                                <td width="8%">标准答案/配分</td>
+                                <td width="8%">{{$t("studentWeb.exam.studentScore.standardIndex")}}</td>
                                 <td>
                                     <div class="score-box">
                                         <span v-for="(objAns,obja) in que.item.objItem" :key="obja">{{objAns.answer.length > 1 ? '[?]': objAns.answer[0]}}</span>
@@ -177,7 +177,7 @@
                                 </td>
                             </tr>
                             <tr>
-                                <td width="8%">学生作答/得分</td>
+                                <td width="8%">{{$t("studentWeb.exam.studentScore.stuAnsIndex")}}</td>
                                 <td>
                                     <div class="score-box">
                                         <span v-for="(objScore,objs) in que.item.objItem" :key="objs">{{objScore.stuScore == 0 ? objScore.stuAns[0] : '-'}}</span>
@@ -195,7 +195,7 @@
 
             </div>
             <div class="score-table">
-                <p style="color:#24b880">【-】--答对,【#】--未作答,【?】--多选题</p>
+                <p style="color:#24b880">{{$t("studentWeb.exam.studentScore.notice")}}</p>
             </div>
         </div>
     </div>
@@ -219,64 +219,6 @@
         },
         data() {
             return {
-                data: {
-                    "paperInfo": {
-                        "personScore": 483,
-                        "classScore": 472,
-                        "gradeScore": 468,
-                        "areaScore": 457,
-                        "classNum": 56,
-                        "gradeNum": 456,
-                        "areaNum": 1456,
-                        "classIndex": 5,
-                        "gradeIndex": 65,
-                        "areaIndex": 296,
-                        "pr": 0.8
-                    },
-                    "examInfo": [
-                        {
-                            "subject": "语文",
-                            "subCode": "5465456-hjghj-5464564-23123",
-                            "score": 150,
-                            "stuScore": 120,
-                            "classAve": 104,
-                            "gradeAve": 102,
-                            "areaAve": 105,
-                            "classIndex": 8,
-                            "gradeIndex": 45,
-                            "areaIndex": 465,
-                            "type": "normal"
-                        },
-                        {
-                            "subject": "数学",
-                            "subCode": "5465456-hjghj-5464564-23123",
-                            "score": 150,
-                            "stuScore": 116,
-                            "classAve": 110,
-                            "gradeAve": 102,
-                            "areaAve": 108,
-                            "classIndex": 10,
-                            "gradeIndex": 87,
-                            "areaIndex": 494,
-                            "type": "normal"
-                        },
-                        {
-                            "subject": "英语",
-                            "subCode": "5465456-hjghj-5464564-23123",
-                            "score": 150,
-                            "stuScore": 138,
-                            "classAve": 114,
-                            "gradeAve": 112,
-                            "areaAve": 115,
-                            "classIndex": 3,
-                            "gradeIndex": 35,
-                            "areaIndex": 365,
-                            "type": "normal"
-                        }
-                    ]
-                },
-                data10: [],
-
                 testData: [],
                 stuScore: [],
                 scoreInfo: {}
@@ -303,7 +245,7 @@
                         "code": this.$store.state.user.schoolCode,
                         "id": this.examData[0].id,
                         "sId": this.$store.state.userInfo.sub,
-                        "cId": this.$store.state.user.studentProfile.classinfo.id,
+                        "cId": this.stuData.claId[0],
                         "gId": this.$store.state.user.studentProfile.classinfo.gradeId
                     }
                     this.$api.studentWeb.getStudentAnalysis(req).then(res => {
@@ -326,11 +268,21 @@
                             res.classCount = classCount
                             this.scoreInfo = res
                         } else {
-                            this.$Message.warning('成绩信息错误!')
+                            this.$Message.warning(this.$t('studentWeb.exam.report.gradeErr'))
                         }
                     })
                 }
             },
+            formUrl(data) {  //处理学生作答数据blob地址
+                let container = data.code
+                let a = ""
+                if (data.blob.indexOf('https://teammodelstorage') > -1) {
+                    a = data.blob
+                } else {
+                    a = `${this.$GLOBAL.BLOB_URL}/${container}/exam/${data.blob}`
+                }
+                return a
+            },
             async getStudentData(data,score,ans) {
                 let paper = []
                 let item = {
@@ -346,6 +298,7 @@
                     }
                     let papers = await this.$evTools.getStuPaper(code)
                     let answer = await this.getItem(ans)
+
                     paper = await this.formPaper(papers)
                     this.testData = paper
                     let objIndex = 0
@@ -378,8 +331,10 @@
                     let code = {
                         scope: this.stuData.papers[0].scope,
                         code: key[(key.length - 1)],
-                        blob: data
+                        blob: data[0]
                     }
+                    let blob = this.formUrl(code)
+                    code.blob = blob
                     datas = await this.$evTools.getComposeItem(code)
                     return datas
                 } else {

+ 4 - 4
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperTest.vue

@@ -380,7 +380,7 @@
             nextQ() {
                 if (this.queNo < (this.examInfo.length - 1)) this.queNo++
             },
-            openWarmMessage: function (showMessageNum) {
+            openWarmMessage(showMessageNum) {
                 this.WarmMessageisOpen = true
                 if (showMessageNum == 1) {
                     this.showMessageNum = showMessageNum
@@ -396,10 +396,10 @@
                     }
                 } else return;
             },
-            closeWarmMessage: function () {
+            closeWarmMessage() {
                 this.WarmMessageisOpen = false
             },
-            closetest: function () {
+            closetest() {
                 this.WarmMessageisOpen = false
                 if (this.checkers.length) {
                     this.isLoading = true;
@@ -408,7 +408,7 @@
                         id: this.$store.getters.getItemTitle.id,
                         answer: this.checkers,
                         studentId: this.$store.state.userInfo.sub,
-                        classId: this.$store.state.user.studentProfile.classinfo.id,
+                        classId: this.$store.getters.getExamInfo.allClass,
                         subjectId: this.$store.getters.getExamInfo.subject.id,
                         multipleRule: this.$store.getters.getExamInfo.multipleRule,
                         paperId: this.$store.getters.getPaperInfo.id,

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

@@ -116,6 +116,7 @@
                     }
                     let isTest = 0
                     this.$api.studentWeb.FindStudentPaper(req).then(res => {
+                        console.log(res)
                         this.stuData = res
                         let resData = res
                         for (let item of resData.papers) {
@@ -124,8 +125,8 @@
                             } 
                         }
                         for (let i = 0; i < this.paperData.length; i++) {
-                            console.log()
                             this.paperData[i].subject = resData.subjects[i]
+                            this.paperData[i].allClass = resData.claId
                             if (resData.stuAns[i] == undefined) {
                                 this.paperData[i].stuAns = []
                                 this.paperData[i].stuScore = []
@@ -171,6 +172,7 @@
                 }
             },
             async getPaper(data) {
+                console.log(data)
                 this.isExamDown = false
                 this.isLoad = true
                 this.selectData = {}

+ 2 - 0
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventList.vue

@@ -223,6 +223,7 @@
                     }
                     this.eventList = []
                     this.$api.studentWeb.FindExamPaper(params).then(res => {
+                        console.log(res)
                         let data = []
                         for (let item of res.props) {
                             let code = item.code.split('-')
@@ -451,6 +452,7 @@
             },
 
             sentSelectedEventTitle(item) {
+                console.log(item)
                 this.$router.push({
                     path: "/studentWeb/eventView",
                     query: {

+ 105 - 0
TEAMModelOS/ClientApp/src/locale/lang/en-US/cusMgt.js

@@ -0,0 +1,105 @@
+export default {
+    // ManageCourse.vue
+    searchHolder:'搜索课程...',
+    delBatch:'批量删除',
+    delCus:'删除课程',
+    addCus:'添加课程',
+    editCus:'编辑课程',
+    noTeacher:'暂无',
+    cusName:'课程名称',
+    nameHolder:'请输入课程名称',
+    cusCode:'课程编码',
+    codeHolder:'请输入课程编码',
+    cusPd:'课程学段',
+    pdHolder:'请选择学段',
+    cusSubject:'课程学科',
+    sjHolder:'请选择学科',
+    cusTeachers:'授课教师',
+    teacherHolder:'请选择授课教师',
+    delContent:'是否确认删除',
+    codeErr1:'课程编码不能为空',
+    codeErr2:'课程编码只能由字母和数字组成',
+    tableCol1:'序号',
+    tableCol2:'课程名称',
+    tableCol3:'课程编码',
+    tableCol4:'学段',
+    tableCol5:'学科',
+    tableCol6:'教师人数',
+    tableCol7:'授课教师',
+    delOk:'删除成功!',
+    delErr:'删除失败!',
+    addOk:'添加成功!',
+    editOk:'修改成功!',
+    formTips:'请先完善信息,再保存!',
+    noSchool:'尚未加入学校,没有学校数据',
+    sltCusTips:'请选择课程',
+
+     //NewCoursePlan.vue
+     spClass:'专科教室',
+     cusPlan:'课程安排',
+     importPlan:'导入安排',
+     course:'课程',
+     addCusPlan:'添加课程安排',
+     saveTips:'保存提醒',
+     tipsContent:'当前班级排课数据尚未保存。如果离开,修改的数据将不会保存!',
+     leaveText:'离开',
+     fullTips:'请先完成已添加的课程设置!',
+     saveOk:'保存成功',
+     saveErr:'保存失败',
+ 
+     //MyCourse.vue
+     scCus:'标准课程',
+     cusInfo:'课程信息',
+     noScCus:'暂无校本课程',
+     privCus:'个人课程',
+     noPrivCus:'暂未创建课程',
+     noCusClass:'暂无上课班级',
+     delStu:'删除学生',
+     addStu:'添加学生',
+     noGroup:'未分组',
+     noNotice:'暂无课程公告!',
+     edit:'编辑',
+     save:'保存',
+     className:'班级名称',
+     editClassInfo:'修改班级信息',
+     dyClass:'动态名单',
+     scClass:'常规教室',
+     delStuContent:'是否确认删除',
+     delStuTips:'请先选择需要删除的学生!',
+     delClassTitle:'删除班级',
+     stuClassCol1:'姓名',
+     stuClassCol2:'账号',
+     stuClassCol3:'座号',
+     stuClassCol4:'组别',
+     stuClassCol5:'组名',
+ 
+     //ManageClass.vue
+     classLabel:'班级:',
+     stuCount:'学生人数:',
+     autoGroup:'自动分组',
+     exportStu:'导出名单',
+     viewport1:'分组视图',
+     viewport2:'列表视图',
+     noStu:'暂无学生',
+     resetPw:'重置密码',
+     noMgtClass:'暂无您管理的班级',
+     groupNameHolder:'请设置组名',
+     edtiGroupName:'修改组名',
+     addGroup:'新增组别',
+     peopleUnit:'人',
+     studentCountLabel: '学生人数',
+     groupCountLabel: '组别数量',
+     groupTypeLabel: '分组方式',
+     groupType1: '随机分组',
+     groupType2: '依座号分组',
+     groupType3: '依座顺序S形分组',
+     action:'操作',
+     atLeast:'至少保留一组',
+     checkName:'请先输入组名再创建组别',
+     noStuTips:'暂无学生可以进行分组',
+     groupCount:'分组数量最少为1',
+     groupTypeTips:'请设置分组方式',
+     groupUnit:'组',
+     nameList:'学生名单'
+
+}

+ 46 - 4
TEAMModelOS/ClientApp/src/locale/lang/en-US/global.js

@@ -1,5 +1,47 @@
-export default{
-    examType1:'期中考试',
-    examType2:'期末考试',
-    examType3:'模拟考试'
+export default {
+    examType1: '期中考试',
+    examType2: '期末考试',
+    examType3: '模拟考试',
+    evType1: '正规考',
+    evType2: '模拟考',
+    evType3: '普通考',
+    evMode1: '线上评量',
+    evMode2: '课中评量',
+    evMode3: '阅卷评量',
+    publishType1: '立即发布',
+    publishType2: '定时发布',
+    testType: [
+        {
+            label: "Single",
+            value: "single"
+        },
+        {
+            label: "Multiple",
+            value: "multiple"
+        },
+        {
+            label: "Judge",
+            value: "judge"
+        },
+        {
+            label: "Complete",
+            value: "complete"
+        },
+        {
+            label: "Q&A",
+            value: "subjective"
+        },
+        {
+            label: "Integrated",
+            value: "compose"
+        },
+        {
+            label: "Correction",
+            value: "correct"
+        },
+        {
+            label: "Connect",
+            value: "connector"
+        },
+    ],
 }

+ 4 - 0
TEAMModelOS/ClientApp/src/locale/lang/en-US/index.js

@@ -19,8 +19,10 @@ import studentWeb from './studentWeb'
 import settings from './settings'
 import serviceDriveAuth from './serviceDriveAuth'
 import elui from './elui'
+import learnActivity from './learnActivity'
 import global from './global'
 import system from './system'
+import cusMgt from './cusMgt'
 export default {
   schoolBaseInfo,
   schoolMgmt,
@@ -46,6 +48,8 @@ export default {
   elui,
   global,
   system,
+  cusMgt,
+  learnActivity,
   formConfigP: {
     input: 'Please Enter ',
     select: 'Please Sele',

+ 188 - 170
TEAMModelOS/ClientApp/src/locale/lang/en-US/learnActivity.js

@@ -1,182 +1,200 @@
-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:'此账号暂未加入任何学校!'
+    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: '此账号暂未加入任何学校!'
     },
 
     //创建评测校本/个人
-    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: {
+        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:'暂无试卷'
+    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:'错误',
+    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:'请先选择学生!'
+        subjectLabel: '学科:',
+        gradeLabel: '年级:',
+        classLabel: '班级:',
+        stuLabel: '学生:',
+        scoreView: '分数概览',
+        scoring: '试卷评分',
+        classNoStu: '班级暂无学生',
+        status1: '暂未作答',
+        status2: '前往评分',
+        status3: '查看分数',
+        column1: '姓名',
+        column2: '总分',
+        column3: '状态',
+        finishScore: '已完成所有学生作答的评分!',
+        unableScore: '学生尚未作答,无法评分',
+        stStuWarning: '请先选择学生!'
+    },
+
+    //SimpleAnalysis.vue
+    simple: {
+        total: '总人数',
+        missExam: '缺考数',
+        classLabel: '班级',
+        sjLabel: '学科',
+        avgScore: '平均分',
+        classStuCount: '班级人数',
+        answered: '已作答',
+        unanswer: '未作答',
+        scored: '已评分',
+        unscore: '未评分',
+        noPublish: '评测尚未发布,暂无统计数据',
+        calcing: '成绩数据结算中,',
+        clickFresh: '点此刷新',
+        inCalc: '数据结算中,请稍后查看'
     }
 
 }

+ 47 - 3
TEAMModelOS/ClientApp/src/locale/lang/en-US/studentWeb.js

@@ -21,7 +21,8 @@ export default {
         "Timeout": "Timeout",
         "makeupExam": "Makeup Exam",
         "makeupHw": "Makeup available",
-        "selectActivity": "Please select an activity from the list"
+        "selectActivity": "Please select an activity from the list",
+        "allSubject": "All Subjects"
     },
     "settingView-title": "Personal Settings",
     "teammodel-account-management": {
@@ -225,7 +226,7 @@ export default {
     "exam": {
         "examLink": "Exam Link",
         "isSubject": "",
-        "subjectNow": "Current Subject",
+        "subjectNow": "Subject",
         "examData": "Evaluation Content",
         "gradeReport": "Grade Report",
         "gradeAnalyse": "Grade Analysis",
@@ -275,7 +276,14 @@ export default {
             "testAns": "Reference Answer",
             "testAnalyse": "Analyze",
             "repairSource": "Repair Source",
-            "noAnalyse": "No analysis"
+            "noAns": "No Answer",
+            "linkSource": "Network Resources",
+            "noSource": "No Resources",
+            "fileSource": "File Source",
+            "fileView": "File Preview",
+            "noReview": "This file does not support preview, please download and view!",
+            "pdfErr": "'pdf failed to load'",
+            "noAnalyse": "No analysis yet"
         },
         "timeoutHint": "The evaluation activity time has ended. Overtime will be calculated as 0 points, or wait for the teacher to open the make-up exam.",
         "contentPage": "Review Content",
@@ -301,6 +309,42 @@ export default {
             "participantAverage": "The whole school",
             "recognizePerformance": "Recognition Level Radar Chart"
         },
+        "studentScore": {
+            "examName": "Exam Name",
+            "examType": "Exam Type",
+            "stableIndex": "Learning stability coefficient",
+            "name": "Name",
+            "stuNo": "Student Number",
+            "class": "class",
+            "allSubScore": "All Subject Scores",
+            "total": "Personal Total Score",
+            "cAverages": "Class Averages",
+            "gAverages": "Grade Averages",
+            "aAverages": "District Averages",
+            "cIndex": "Class Ranking",
+            "gIndex": "Grade Ranking",
+            "aIndex": "District Rank",
+            "onLine": "Whether to enter the line",
+            "subject": "Subject",
+            "score": "Personal Score",
+            "cAverage": "Class Average",
+            "gAverage": "Grade Average",
+            "aAverage": "District-level average",
+            "rAns": "The number of correct answers",
+            "wAns": "Number of wrong answers",
+            "comIndex": "Comprehensive Difficulty",
+            "objItem": "Objective",
+            "subItem": "Subjective",
+            "subjects": "Subjects",
+            "item": "Title",
+            "objItems": "Objective Questions",
+            "subItems": "Subjective questions",
+            "itemIndex": "item number",
+            "standardIndex": "standard answer/partition points",
+            "stuAnsIndex": "Student answer/score",
+            "notice": "【-】--correct answer, 【#】--not answered, 【?】--multiple choice question",
+            "gradeErr": "The grade information is wrong!"
+        },
         "correctAnswer": "correctAnswer",
         "wrongAnswer": "Wrong Answer",
         "unAnswered": "Unanswered",

+ 53 - 32
TEAMModelOS/ClientApp/src/locale/lang/en-US/teachContent.js

@@ -1,34 +1,55 @@
 export default {
-  personalResource: 'Private resource',
-  schoolResource: 'School based resource',
-  filterAll: 'All',
-  filterPicture: 'Picture',
-  filterVideo: 'Video',
-  filterDoc: 'Document',
-  filterOther: 'Other',
-  space: 'Space:',
-  tableC1: 'File name',
-  tableC2: 'Operation',
-  tableC3: 'Knowledge points',
-  tableC4: 'File size',
-  tableC5: 'Relation number',
-  tableC6: 'Upload date',
-  searchText: 'please enter keyword search',
-  btnUpload: 'upload resources',
-  tips1: 'Card',
-  tips2: 'List',
-  tips3: 'download file',
-  tips4: 'preview file',
-  tips5: 'bind knowledge point',
-  tips6: 'rename',
-  tips7: 'delete file',
-  tips8: 'your browser does not support video tags. ',
-  trops1: 'confirm deletion',
-  props2: 'file deleted successfully! ',
-  props3: 'file deletion failed! ',
-  props4: 'Cancel deletion! ',
-  props5: 'file name modified successfully! ',
-  props6: 'file upload succeeded! ',
-  props7: 'not enough storage space! ',
-  uploadText: 'Click or drag to upload'
+  filterRes: '教材',
+  filterPicture: '圖片',
+  filterVideo: '視頻',
+  filterAudio: '音訊',
+  filterDoc: '檔案',
+  filterOther: '其他',
+  space: '空間:',
+  tableC1: '檔案名稱',
+  tableC2: '操作',
+  tableC3: '知識點',
+  tableC4: '文件大小',
+  tableC5: '關聯數量',
+  tableC6: '上傳日期',
+  searchText: '請輸入關鍵字蒐索',
+  btnUpload: '上傳資源',
+  tips1: '大圖',
+  tips2: '清單',
+  tips3: '下載檔案',
+  tips4: '預覽檔案',
+  tips5: '綁定知識點',
+  tips6: '重命名',
+  tips7: '刪除檔',
+  tips8: '您的瀏覽器不支持video標籤。',
+  props1: '確認删除',
+  props2: '檔案删除成功!',
+  props3: '檔案删除失敗!',
+  props4: '取消删除!',
+  props5: '檔名修改成功!',
+  props6: '文件上傳成功!',
+  props7: '存儲空間不足!',
+  uploadText: '點擊或者拖拽上傳',
+
+  resTips: 'HiTeach生成的課件,不包含PPT等其他檔案',
+  space: '空間:',
+  calcing: '計算中…',
+  blobFull: '(已滿)',
+  otherType: '其他類型',
+  appData: '應用數據',
+  delBatch: '批量删除',
+  viewport1: '清單模式',
+  viewport2: '圖片模式',
+  bottomTips: '--------我也是有底線的--------',
+  notAudio: '您的瀏覽器不支持audio元素。',
+  nameOk: '名稱修改成功',
+  nameErr: '名稱修改失敗',
+  fullTips: '存儲空間已滿,無法上傳',
+  loadAll: '已經加載全部',
+  authErr: '獲取Blob授權失敗',
+  sizeErr: '空間計算异常',
+  delBatchTips1: '請在清單模式使用批量删除!',
+  delBatchTips2: '請先選擇需要删除的檔案!',
+  specialChart: '不能包含特殊字元',
+  noData: '暫無數據'
 }

+ 106 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/cusMgt.js

@@ -0,0 +1,106 @@
+export default {
+    // ManageCourse.vue
+    searchHolder:'搜索...',
+    delBatch:'批量删除',
+    delCus:'删除课程',
+    addCus:'添加课程',
+    editCus:'编辑课程',
+    noTeacher:'暂无',
+    cusName:'课程名称',
+    nameHolder:'请输入课程名称',
+    cusCode:'课程编码',
+    codeHolder:'请输入课程编码',
+    cusPd:'课程学段',
+    pdHolder:'请选择学段',
+    cusSubject:'课程学科',
+    sjHolder:'请选择学科',
+    cusTeachers:'授课教师',
+    teacherHolder:'请选择授课教师',
+    delContent:'是否确认删除',
+    codeErr1:'课程编码不能为空',
+    codeErr2:'课程编码只能由字母和数字组成',
+    tableCol1:'序号',
+    tableCol2:'课程名称',
+    tableCol3:'课程编码',
+    tableCol4:'学段',
+    tableCol5:'学科',
+    tableCol6:'教师人数',
+    tableCol7:'授课教师',
+    delOk:'删除成功!',
+    delErr:'删除失败!',
+    addOk:'添加成功!',
+    addErr:'添加成功!',
+    editOk:'修改成功!',
+    editErr:'修改失败!',
+    formTips:'请先完善信息,再保存!',
+    noSchool:'尚未加入学校,没有学校数据',
+    sltCusTips:'请选择课程',
+
+    //NewCoursePlan.vue
+    spClass:'专科教室',
+    cusPlan:'课程安排',
+    importPlan:'导入安排',
+    course:'课程',
+    addCusPlan:'添加课程安排',
+    saveTips:'保存提醒',
+    tipsContent:'当前班级排课数据尚未保存。如果离开,修改的数据将不会保存!',
+    leaveText:'离开',
+    fullTips:'请先完成已添加的课程设置!',
+    saveOk:'保存成功',
+    saveErr:'保存失败',
+
+    //MyCourse.vue
+    scCus:'标准课程',
+    cusInfo:'课程信息',
+    noScCus:'暂无校本课程',
+    privCus:'个人课程',
+    noPrivCus:'暂未创建课程',
+    noCusClass:'暂无上课班级',
+    delStu:'删除学生',
+    addStu:'添加学生',
+    noGroup:'未分组',
+    noNotice:'暂无课程公告!',
+    edit:'编辑',
+    save:'保存',
+    className:'班级名称',
+    editClassInfo:'修改班级信息',
+    dyClass:'动态名单',
+    scClass:'常规教室',
+    delStuContent:'是否确认删除',
+    delStuTips:'请先选择需要删除的学生!',
+    delClassTitle:'删除班级',
+    stuClassCol1:'姓名',
+    stuClassCol2:'账号',
+    stuClassCol3:'座号',
+    stuClassCol4:'组别',
+    stuClassCol5:'组名',
+
+    //ManageClass.vue
+    classLabel:'班级:',
+    stuCount:'学生人数:',
+    autoGroup:'自动分组',
+    exportStu:'导出名单',
+    viewport1:'分组视图',
+    viewport2:'列表视图',
+    noStu:'暂无学生',
+    resetPw:'重置密码',
+    noMgtClass:'暂无您管理的班级',
+    groupNameHolder:'请设置组名',
+    edtiGroupName:'修改组名',
+    addGroup:'新增组别',
+    peopleUnit:'人',
+    studentCountLabel: '学生人数',
+    groupCountLabel: '组别数量',
+    groupTypeLabel: '分组方式',
+    groupType1: '随机分组',
+    groupType2: '依座号分组',
+    groupType3: '依座顺序S形分组',
+    action:'操作',
+    atLeast:'至少保留一组',
+    checkName:'请先输入组名再创建组别',
+    noStuTips:'暂无学生可以进行分组',
+    groupCount:'分组数量最少为1',
+    groupTypeTips:'请设置分组方式',
+    groupUnit:'组',
+    nameList:'学生名单'
+}

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

@@ -38,12 +38,12 @@ export default {
 	diff3:'一般',
 	diff4:'较难',
 	diff5:'困难',
-	level1:'知识',
+	level1:'记忆',
 	level2:'理解',
 	level3:'应用',
 	level4:'分析',
-	level5:'综合',
-	level6:'评',
+	level5:'创造',
+	level6:'评',
 	noData:'暂无数据',
 	answer:'答案',
 	noAnswer:'未设置答案',

+ 35 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/global.js

@@ -9,5 +9,39 @@ export default{
     evMode2:'课中评量',
     evMode3:'阅卷评量',
     publishType1:'立即发布',
-    publishType2:'定时发布',
+    publishType2: '定时发布',
+    testType: [
+        {
+            label: "单选",
+            value: "single"
+        },
+        {
+            label: "多选",
+            value: "multiple"
+        },
+        {
+            label: "判断",
+            value: "judge"
+        },
+        {
+            label: "填空",
+            value: "complete"
+        },
+        {
+            label: "问答",
+            value: "subjective"
+        },
+        {
+            label: "综合",
+            value: "compose"
+        },
+        {
+            label: "改错",
+            value: "correct"
+        },
+        {
+            label: "连线",
+            value: "connector"
+        }
+        ]
 }

+ 2 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/index.js

@@ -23,6 +23,7 @@ import evaluation from './evaluation'
 import learnActivity from './learnActivity'
 import global from './global'
 import system from './system'
+import cusMgt from './cusMgt'
 export default {
   schoolBaseInfo,
   classMgmt,
@@ -49,6 +50,7 @@ export default {
   learnActivity,
   global,
   system,
+  cusMgt,
   test: '测试',
   formConfigP: {
     input: '请输入',

+ 18 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/learnActivity.js

@@ -177,6 +177,24 @@ export default{
         finishScore:'已完成所有学生作答的评分!',
         unableScore:'学生尚未作答,无法评分',
         stStuWarning:'请先选择学生!'
+    },
+
+    //SimpleAnalysis.vue
+    simple:{
+        total:'总人数',
+        missExam:'缺考数',
+        classLabel:'班级',
+        sjLabel:'学科',
+        avgScore:'平均分',
+        classStuCount:'班级人数',
+        answered:'已作答',
+        unanswer:'未作答',
+        scored:'已评分',
+        unscore:'未评分',
+        noPublish:'评测尚未发布,暂无统计数据',
+        calcing:'成绩数据结算中,',
+        clickFresh:'点此刷新',
+        inCalc:'数据结算中,请稍后查看'
     }
 
 }

+ 46 - 7
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/studentWeb.js

@@ -21,7 +21,8 @@ export default {
         "Timeout": "已逾时",
         "makeupExam": "可补考",
         "makeupHw": "可补交",
-        "selectActivity":"请从列表挑选一个活动"
+        "selectActivity": "请从列表挑选一个活动",
+        "allSubject":"综合科目"
     },
     "settingView-title": "个人设定",
     "teammodel-account-management": {
@@ -137,8 +138,6 @@ export default {
     },
     "latestNotification": "最新通知",
     "postAt": "发布于",
-
-
     "______________": "______________",
     "eventView-title": "活动任务",
     "nextTask": "下个活动",
@@ -225,7 +224,7 @@ export default {
         "submitBtn": "送出"
     },
     "exam": {
-        "examLink": "试卷连结",
+        "examLink": "试卷链接",
         "isSubject": "科试卷",
         "subjectNow": "当前科目",
         "examData": "评测内容",
@@ -277,6 +276,13 @@ export default {
             "testAns": "参考答案",
             "testAnalyse": "解析",
             "repairSource": "补救资源",
+            "noAns":"未作答",
+            "linkSource": "网络资源",
+            "noSource": "暂无资源",
+            "fileSource": "文件资源",
+            "fileView": "文件预览",
+            "noReview": "该文件暂不支持预览,请下载查看!",
+            "pdfErr":"'pdf 加载失败'",
             "noAnalyse":"暂无解析"
         },
         "timeoutHint": "评量活动时间已结束,逾时将以0分计算,或等待教师开放补考。",
@@ -294,8 +300,6 @@ export default {
         "rightNum": "我的答对题数",
         "smartComment": "智能点评",
         "keypoint": "建议复习知识点:",
-
-
         "chart": {
             "scoreDistribution": "评量成绩分布图",
             "participant": "应考人数:",
@@ -305,6 +309,42 @@ export default {
             "participantAverage": "全校",
             "recognizePerformance": "认知层次雷达图"
         },
+        "studentScore": {
+            "examName": "考试名称",
+            "examType":"考试类型",
+            "stableIndex":"学习稳定系数",
+            "name":"姓名",
+            "stuNo":"学号",
+            "class": "班级",
+            "allSubScore": "全学科成绩",
+            "total":"个人总分",
+            "cAverages":"班级平均分",
+            "gAverages":"年级平均分",
+            "aAverages":"区级平均分",
+            "cIndex":"班级排名",
+            "gIndex":"年级排名",
+            "aIndex":"区级排名",
+            "onLine": "是否进线",
+            "subject":"学科",
+            "score": "个人得分",
+            "cAverage": "班级平均",
+            "gAverage": "年级平均",
+            "aAverage": "区级平均",
+            "rAns":"答对题数",
+            "wAns":"答错题数",
+            "comIndex":"综合难度",
+            "objItem":"客观",
+            "subItem":"主观",
+            "subjects":"科目",
+            "item":"题目",
+            "objItems": "客观题",
+            "subItems": "主观题",
+            "itemIndex":"题号",
+            "standardIndex":"标准答案/配分",
+            "stuAnsIndex":"学生作答/得分",
+            "notice": "【-】--答对,【#】--未作答,【?】--多选题",
+            "gradeErr":"成绩信息错误!"
+        },
         "correctAnswer": "答对",
         "wrongAnswer": "答错",
         "unAnswered": "未答",
@@ -312,7 +352,6 @@ export default {
         "analysis": "解析",
         "correctRate": "答对率",
         "relatedAQues": "关联题目",
-       
     },
     "informview-title": "通知总览",
     "view": "前往检视",

+ 25 - 4
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/teachContent.js

@@ -1,9 +1,8 @@
 export default {
-  personalResource: '个人资源',
-  schoolResource: '校本资源',
-  filterAll: '全部',
+  filterRes: '教材',
   filterPicture: '图片',
   filterVideo: '视频',
+  filterAudio: '音频',
   filterDoc: '文档',
   filterOther: '其他',
   space: '空间:',
@@ -30,5 +29,27 @@ export default {
   props5: '文件名修改成功!',
   props6: '文件上传成功!',
   props7: '存储空间不足!',
-  uploadText: '点击或者拖拽上传'
+  uploadText: '点击或者拖拽上传',
+
+  resTips:'HiTeach生成的课件,不包含PPT等其他文件',
+  space:'空间:',
+  calcing:'计算中...',
+  blobFull:'(已满)',
+  otherType:'其他类型',
+  appData:'应用数据',
+  delBatch:'批量删除',
+  viewport1:'列表模式',
+  viewport2:'图片模式',
+  bottomTips:'--------我也是有底线的--------',
+  notAudio:'您的浏览器不支持 audio 元素。',
+  nameOk:'名称修改成功',
+  nameErr:'名称修改失败',
+  fullTips:'存储空间已满,无法上传',
+  loadAll:'已经加载全部',
+  authErr:'获取Blob授权失败',
+  sizeErr:'空间计算异常',
+  delBatchTips1:'请在列表模式使用批量删除!',
+  delBatchTips2:'请先选择需要删除的文件!',
+  specialChart:'不能包含特殊字符',
+  noData:'暂无数据'
 }

+ 107 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/cusMgt.js

@@ -0,0 +1,107 @@
+export default {
+    // ManageCourse.vue
+    searchHolder: '蒐索課程…',
+    delBatch: '批量删除',
+    delCus: '删除課程',
+    addCus: '添加課程',
+    editCus: '編輯課程',
+    noTeacher: '暫無',
+    cusName: '課程名稱',
+    nameHolder: '請輸入課程名稱',
+    cusCode: '課程編碼',
+    codeHolder: '請輸入課程編碼',
+    cusPd: '課程學段',
+    pdHolder: '請選擇學段',
+    cusSubject: '課程學科',
+    sjHolder: '請選擇學科',
+    cusTeachers: '授課教師',
+    teacherHolder: '請選擇授課教師',
+    delContent: '是否確認删除',
+    codeErr1: '課程編碼不能為空',
+    codeErr2: '課程編碼只能由字母和數位組成',
+    tableCol1: '序號',
+    tableCol2: '課程名稱',
+    tableCol3: '課程編碼',
+    tableCol4: '學段',
+    tableCol5: '學科',
+    tableCol6: '教師人數',
+    tableCol7: '授課教師',
+    delOk: '删除成功!',
+    delErr: '删除失敗!',
+    addOk: '添加成功!',
+    editOk: '修改成功!',
+    formTips: '請先完善資訊,再保存!',
+    noSchool: '尚未加入學校,沒有學校數據',
+    sltCusTips: '請選擇課程',
+
+    //NewCoursePlan.vue
+    spClass: '專科教室',
+    cusPlan: '課程安排',
+    importPlan: '導入安排',
+    course: '課程',
+    addCusPlan: '添加課程安排',
+    saveTips: '保存提醒',
+    tipsContent: '當前班級排課數據尚未保存。如果離開,修改的數據將不會保存!',
+    leaveText: '離開',
+    fullTips: '請先完成已添加的課程設置!',
+    saveOk: '保存成功',
+    saveErr: '保存失敗',
+
+
+    //MyCourse.vue
+    scCus: '標準課程',
+    cusInfo: '課程資訊',
+    noScCus: '暫無校本課程',
+    privCus: '個人課程',
+    noPrivCus: '暫未創建課程',
+    noCusClass: '暫無上課班級',
+    delStu: '删除學生',
+    addStu: '添加學生',
+    noGroup: '未分組',
+    noNotice: '暫無課程公告!',
+    edit: '編輯',
+    save: '保存',
+    className: '班級名稱',
+    editClassInfo: '修改班級資訊',
+    dyClass: '動態名單',
+    scClass: '常規教室',
+    delStuContent: '是否確認删除',
+    delStuTips: '請先選擇需要删除的學生!',
+    delClassTitle: '删除班級',
+    stuClassCol1: '姓名',
+    stuClassCol2: '帳號',
+    stuClassCol3: '座號',
+    stuClassCol4: '組別',
+    stuClassCol5: '組名',
+
+
+    //ManageClass.vue
+    classLabel: '班級:',
+    stuCount: '學生人數:',
+    autoGroup: '自動分組',
+    exportStu: '匯出名單',
+    viewport1: '分組視圖',
+    viewport2: '清單視圖',
+    noStu: '暫無學生',
+    resetPw: '重置密碼',
+    noMgtClass: '暫無您管理的班級',
+    groupNameHolder: '請設定組名',
+    edtiGroupName: '修改組名',
+    addGroup: '新增組別',
+    peopleUnit: '人',
+    studentCountLabel: '學生人數',
+    groupCountLabel: '組別數量',
+    groupTypeLabel: '分組管道',
+    groupType1: '隨機分組',
+    groupType2: '依座號分組',
+    groupType3: '依座順序S形分組',
+    action: '操作',
+    atLeast: '至少保留一組',
+    checkName: '請先輸入組名再創建組別',
+    noStuTips: '暫無學生可以進行分組',
+    groupCount: '分組數量最少為1',
+    groupTypeTips: '請設定分組管道',
+    groupUnit: '組',
+    nameList: '學生名單'
+
+}

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

@@ -38,11 +38,11 @@ export default {
 	diff3: '一般',
 	diff4: '較難',
 	diff5: '困難',
-	level1: '知識',
+	level1: '記憶',
 	level2: '理解',
 	level3: '應用',
 	level4: '分析',
-	level5: '綜合',
+	level5: '創造',
 	level6: '評鑒',
 	noData: '暫無數據',
 	answer: '答案',

+ 46 - 4
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/global.js

@@ -1,5 +1,47 @@
-export default{
-    examType1:'期中考試',
-    examType2:'期末考試',
-    examType3:'模擬考試'
+export default {
+    xamType1: '期中考試',
+    examType2: '期末考試',
+    examType3: '模擬考試',
+    evType1: '正規考',
+    evType2: '模擬考',
+    evType3: '普通考',
+    evMode1: '線上評量',
+    evMode2: '課中評量',
+    evMode3: '閱卷評量',
+    publishType1: '立即發佈',
+    publishType2: '定時發佈',
+    testType: [
+        {
+            label: "單選",
+            value: "single"
+        },
+        {
+            label: "多選",
+            value: "multiple"
+        },
+        {
+            label: "判斷",
+            value: "judge"
+        },
+        {
+            label: "填空",
+            value: "complete"
+        },
+        {
+            label: "問答",
+            value: "subjective"
+        },
+        {
+            label: "綜合",
+            value: "compose"
+        },
+        {
+            label: "改錯",
+            value: "correct"
+        },
+        {
+            label: "連線",
+            value: "connector"
+        }
+    ]
 }

+ 4 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/index.js

@@ -20,8 +20,10 @@ import settings from './settings'
 import serviceDriveAuth from './serviceDriveAuth'
 import elui from './elui'
 import evaluation from './evaluation'
+import learnActivity from './learnActivity'
 import global from './global'
 import system from './system'
+import cusMgt from './cusMgt'
 
 export default {
   
@@ -47,8 +49,10 @@ export default {
   serviceDriveAuth, //授權管理頁面
   elui,
   evaluation,
+  learnActivity,
   global,
   system,
+  cusMgt,
   test: '測試',
   formConfigP: {
     input: '請輸入',

+ 19 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/learnActivity.js

@@ -182,6 +182,25 @@ export default {
         finishScore: '已完成所有學生作答的評分!',
         unableScore: '學生尚未作答,無法評分',
         stStuWarning: '請先選擇學生!'
+    },
+
+    //SimpleAnalysis.vue
+    simple: {
+        total: '總人數',
+        missExam: '缺考數',
+        classLabel: '班級',
+        sjLabel: '學科',
+        avgScore: '平均分',
+        classStuCount: '班級人數',
+        answered: '已作答',
+        unanswer: '未作答',
+        scored: '已評分',
+        unscore: '未評分',
+        noPublish: '評測尚未發佈,暫無統計資料',
+        calcing: '成績數據結算中,',
+        clickFresh: '點此重繪',
+        inCalc: '數據結算中,請稍後查看'
+
     }
 
 }

+ 46 - 2
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/studentWeb.js

@@ -21,7 +21,8 @@ export default {
         "Timeout": "已逾時",
         "makeupExam": "可補考",
         "makeupHw": "可補交",
-        "selectActivity": "請從列表挑選一個活動"
+        "selectActivity": "請從列表挑選一個活動",
+        "allSubject": "綜合科目"
     },
     "settingView-title": "個人設定",
     "teammodel-account-management": {
@@ -223,7 +224,7 @@ export default {
         "submitBtn": "送出"
     },
     "exam": {
-        "examLink": "試卷連結",
+        "examLink": "試卷鏈接",
         "isSubject": "科試卷",
         "subjectNow": "當前科目",
         "examData": "評測內容",
@@ -275,6 +276,13 @@ export default {
             "testAns": "參考答案",
             "testAnalyse": "解析",
             "repairSource": "補救資源",
+            "noAns": "未作答",
+            "linkSource": "網絡資源",
+            "noSource": "暫無資源",
+            "fileSource": "文件資源",
+            "fileView": "文件預覽",
+            "noReview": "該文件暫不支持預覽,請下載查看!",
+            "pdfErr": "'pdf 加載失敗'",
             "noAnalyse": "暫無解析"
         },
         "timeoutHint": "評量活動時間已結束,逾時將以0分計算,或等待教師開放補考。",
@@ -303,6 +311,42 @@ export default {
             "participantAverage": "全校",
             "recognizePerformance": "認知層次雷達圖"
         },
+        "studentScore": {
+            "examName": "考試名稱",
+            "examType": "考試類型",
+            "stableIndex": "學習穩定係數",
+            "name": "姓名",
+            "stuNo": "學號",
+            "class": "班級",
+            "allSubScore": "全學科成績",
+            "total": "個人總分",
+            "cAverages": "班級平均分",
+            "gAverages": "年級平均分",
+            "aAverages": "區級平均分",
+            "cIndex": "班級排名",
+            "gIndex": "年級排名",
+            "aIndex": "區級排名",
+            "onLine": "是否進線",
+            "subject": "學科",
+            "score": "個人得分",
+            "cAverage": "班級平均",
+            "gAverage": "年級平均",
+            "aAverage": "區級平均",
+            "rAns": "答對題數",
+            "wAns": "答錯題數",
+            "comIndex": "綜合難度",
+            "objItem": "客觀",
+            "subItem": "主觀",
+            "subjects": "科目",
+            "item": "題目",
+            "objItems": "客觀題",
+            "subItems": "主觀題",
+            "itemIndex": "題號",
+            "standardIndex": "標準答案/配分",
+            "stuAnsIndex": "學生作答/得分",
+            "notice": "【-】--答對,【#】--未作答,【?】--多選題",
+            "gradeErr": "成績信息錯誤!"
+        },
         "correctAnswer": "答對",
         "wrongAnswer": "答錯",
         "unAnswered": "未答",

+ 26 - 5
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/teachContent.js

@@ -1,10 +1,9 @@
 export default {
-  personalResource: '个人資源',
-  schoolResource: '校本資源',
-  filterAll: '全部',
+  filterRes: '教材',
   filterPicture: '圖片',
   filterVideo: '視頻',
-  filterDoc: '文件',
+  filterAudio: '音訊',
+  filterDoc: '檔案',
   filterOther: '其他',
   space: '空間:',
   tableC1: '檔案名稱',
@@ -30,5 +29,27 @@ export default {
   props5: '檔名修改成功!',
   props6: '文件上傳成功!',
   props7: '存儲空間不足!',
-  uploadText: '點擊或者拖拽上傳'
+  uploadText: '點擊或者拖拽上傳',
+
+  resTips: 'HiTeach生成的課件,不包含PPT等其他檔案',
+  space: '空間:',
+  calcing: '計算中…',
+  blobFull: '(已滿)',
+  otherType: '其他類型',
+  appData: '應用數據',
+  delBatch: '批量删除',
+  viewport1: '清單模式',
+  viewport2: '圖片模式',
+  bottomTips: '--------我也是有底線的--------',
+  notAudio: '您的瀏覽器不支持audio元素。',
+  nameOk: '名稱修改成功',
+  nameErr: '名稱修改失敗',
+  fullTips: '存儲空間已滿,無法上傳',
+  loadAll: '已經加載全部',
+  authErr: '獲取Blob授權失敗',
+  sizeErr: '空間計算异常',
+  delBatchTips1: '請在清單模式使用批量删除!',
+  delBatchTips2: '請先選擇需要删除的檔案!',
+  specialChart: '不能包含特殊字元',
+  noData: '暫無數據'
 }

+ 1 - 1
TEAMModelOS/ClientApp/src/router/routes.js

@@ -265,7 +265,7 @@ export const routes = [
 			{
 				path: 'myCourse',
 				name: 'myCourse',
-				component: resolve => require(['@/view/newcourse/NewMyCourse.vue'], resolve)
+				component: resolve => require(['@/view/newcourse/MyCourse.vue'], resolve)
 			},
 			{
 				path: 'CourseTable',

+ 1 - 1
TEAMModelOS/ClientApp/src/store/index.js

@@ -12,7 +12,7 @@ import teachers from './module/teachers'
 import studentWeb from './module/studentWeb'
 import scboard from './module/scboard'
 import serviceDriveAuth from './module/serviceDriveAuth'
-import GLOBAL from '@/static/Global.js'
+import  { GLOBAL }  from '@/static/Global.js'
 import spaceAuth from './module/spaceAuth'
 import studentAclassOneAuth from './module/studentAclassOneAuth'
 

+ 12 - 6
TEAMModelOS/ClientApp/src/store/module/totalAnalysis.js

@@ -77,6 +77,9 @@ export default {
 		
 		clearAnalysis(state,val){
 			state.analysisJson = val
+			state.subjectList = val
+			state.currentSubject = val
+			state.classExerciseData = val
 		},
 
         // 更新当前评测CODE
@@ -237,18 +240,21 @@ export default {
 			        r(context.state.analysisJson)
 			    } else {
 			        apiTools.totalAnalysis.getAnalysis(params).then(res => { // api请求
-			            context.commit('updateAnalysisJson', res) // 提交到vuex保存数据
-			            context.commit('updateSubjectList', res.subjects.map(i => i.name)) // 提交到vuex保存数据
-			            context.commit('updateCurSubject', res.subjects.map(i => i.name)[0]) // 提交到vuex保存数据
-			            console.log('API-analysisJson', res)
-			            r(res)
+						if(res.classes.length){
+							context.commit('updateAnalysisJson', res) // 提交到vuex保存数据
+							context.commit('updateSubjectList', res.subjects.map(i => i.name)) // 提交到vuex保存数据
+							context.commit('updateCurSubject', res.subjects.map(i => i.name)[0]) // 提交到vuex保存数据
+							console.log('API-analysisJson', res)
+							r(res)
+						}else{
+							j(500)
+						}
 			        }).catch(err => {
 			            j(err)
 			        })
 			    }
 			})
 		},
-		
 
         // 异步获取 评测列表数据
         getExamList(context) {

+ 235 - 32
TEAMModelOS/ClientApp/src/utils/blobTool.js

@@ -1,7 +1,7 @@
 import { GLOBAL } from '@/static/Global.js';
 import JsFn from '@/utils/js-fn.js';
+import API from '@/api/index.js';
 const { BlobServiceClient, BlobClient } = require("@azure/storage-blob")
-//const s = "?sv=2019-12-12&ss=b&srt=sco&st=2020-12-22T10%3A09%3A08Z&se=2020-12-23T10%3A09%3A08Z&sp=rwdxftlacup&sig=EHyNiA5uoBPHQAHfISLei%2BWdWnldHzOYYR7ZYdtFtRo%3D"
 //获取文件后缀和类型
 function getExAndType(fileName) {
     let ex = fileName.substring(fileName.lastIndexOf('.') + 1)
@@ -77,21 +77,128 @@ export default class BlobTool {
             )
         })
     }
+    /**
+     * 上传文件之前检查文件是否存在,如果存在返回文件大小
+     * @params {urls} 需要上传文件地址 相对地址
+     * @params {containerName} 容器名称,如果直接对象调用则必传;如果已经初始化BlobTool对象,则优先使用参数,没有则使用对象容器
+     */
+    static checkBlobs(urls, containerName) {
+        return new Promise((r, j) => {
+            if ((containerName || this.container) && urls.length) {
+                API.blob.checkBlobs({
+                    urls,
+                    containerName: containerName || this.container
+                }).then(
+                    res => {
+                        r(res.urlsSize)
+                    },
+                    err => {
+                        console.log(err)
+                    }
+                )
+            } else {
+                throw new Error("参数错误")
+            }
+        })
+    }
+
+    /**
+     * 上传文件之前检查文件夹的大小
+     * @params {prefix} 需要上传文件地址 相对地址
+     * @params {containerName} 容器名称,如果直接对象调用则必传;如果已经初始化BlobTool对象,则优先使用参数,没有则使用对象容器
+     * 
+     * @return size
+     */
+    static checkPrefixSize(prefix, containerName) {
+        return new Promise((r, j) => {
+            if (containerName && prefix) {
+                API.blob.checkBlobs({
+                    prefix,
+                    containerName,
+                    urls: []
+                }).then(
+                    res => {
+                        r(res.prefixSize)
+                    },
+                    err => {
+                        console.log(err)
+                    }
+                )
+            } else {
+                throw new Error("参数错误")
+            }
+        })
+    }
+
+    /**
+     * 更新Blob空间数据 
+     * @params {urls} checkBlobs方法返回的数据处理size后
+     * @params {containerName} 容器名称,如果直接对象调用则必传;如果已经初始化BlobTool对象,则优先使用参数,没有则使用初始化对象的容器
+     */
+    static updateSize(optUrls, containerName) {
+        return new Promise((r, j) => {
+            if ((containerName || this.container) && optUrls.length) {
+                API.blob.updateSize({
+                    optUrls,
+                    containerName: containerName || this.container
+                }).then(
+                    res => {
+                        r(res.urlsSize)
+                    },
+                    err => {
+                        console.log(err)
+                    }
+                )
+            } else {
+                throw new Error("参数错误")
+            }
+        })
+    }
+
+    /**
+     * 计算上传前后size
+     * @param {any}  before 上传之前的文件大小
+     * @param {any}  after 上传之后的文件大小
+     */
+    calcSize(before, after) {
+        if (before.length == after.length) {
+            let res = []
+            before.forEach((item, index) => {
+                let afterBlob = after.find((afterItem, afterIndex) => {
+                    return afterItem.url == item.url
+                })
+                if (after) {
+                    res.push({
+                        url: item.url,
+                        size: afterBlob.size - item.size
+                    })
+                } else {
+                    throw new Error(`未找到${item.url}上传之后的大小`)
+                }
+            })
+            return res
+        } else {
+            throw new Error('beforeSize/afterSize上传前后数据不对应')
+        }
+    }
+
     /**
      * 上传文件方法,带回调上传进度
      * @param {any} file 文件对象
      * @param {any} path 文件夹路径
      * @param {any} option 官方可配置项
-     * @param {any} checkSize 上传时是否检查容器空间 官方可配置项。默认需要检查大小
+     * @param {any} checkSize 上传时是否检查容器空间 默认需要检查大小
+     * @param {any} handleSize 是否需要更新size,建议多文件上传时在外部批量处理
      * @returns {object} {url, name,size,createTime,extension,type}
      */
-    upload(file, path, option, checkSize = true) {
+    upload(file, path, option, checkSize = true, handleSize = true) {
         return new Promise(async (r, j) => {
             //检查容器空间大小
             if (checkSize) {
                 if (!this.totalSize) {
                     try {
-                        this.totalSize = await this.getContainerSize()
+                        let sizeRes = await BlobTool.getSizeByServe(this.container)
+                        this.totalSize = sizeRes.size
                     } catch (e) {
                         this.totalSize = -1
                         j({
@@ -109,14 +216,35 @@ export default class BlobTool {
                     })
                 }
             }
+            let blobSize = 0
+            // 上传之前检查size
+            if (handleSize) {
+                let u = path + "/" + file.name
+                try {
+                    let checkRes = await BlobTool.checkBlobs([u], this.container)
+                    blobSize = checkRes && checkRes.urlsSize ? checkRes.urlsSize[0].size : 0
+                } catch (e) {
+                    throw new Error(e)
+                }
+            }
             const blockBlobClient = this.containerClient.getBlockBlobClient(path + "/" + file.name)
             blockBlobClient.uploadBrowserData(file, option).then(
                 res => {
                     let url = decodeURIComponent(res._response.request.url)
                     url = url.substring(0, url.lastIndexOf('?'))
                     let info = getExAndType(file.name)
+
+                    let updateSize = res._response.request.body.size - blobSize
+
                     if (this.totalSize) {
-                        this.totalSize += res._response.request.body.size
+                        this.totalSize += updateSize
+                    }
+                    // 上传成功后更新size
+                    if (handleSize) {
+                        BlobTool.updateSize([{
+                            url: path + "/" + file.name,
+                            size: updateSize
+                        }], this.container)
                     }
                     r({
                         url: url,
@@ -125,7 +253,8 @@ export default class BlobTool {
                         size: res._response.request.body.size,
                         createTime: res.lastModified.getTime(),
                         extension: info.ex,
-                        type: info.type
+                        type: info.type,
+                        updateSize: updateSize
                     })
                 },
                 err => {
@@ -149,7 +278,7 @@ export default class BlobTool {
                 let name = item.blob.substring(startIndex + 1, endIndex)
                 let nameIndex = names.indexOf(name)
                 if (nameIndex == -1) {
-                    fileItem.url = this.blobUrl+ '/' + this.container +'/res/' + name
+                    fileItem.url = this.blobUrl + '/' + this.container + '/res/' + name
                     fileItem.blob = `/res/${name}/index.json`
                     fileItem.name = name + '.HTEX'
                     fileItem.size = item.size
@@ -260,21 +389,30 @@ export default class BlobTool {
      * 删除Blob
      * @param {string} filePath 文件url + 容器 之后的路径
      */
-    deleteBlob(filePath) {
-        if (filePath) {
+    deleteBlob(filePath, size) {
+        console.log(typeof size)
+        console.log(typeof size == 'number')
+        if (filePath && typeof size == 'number') {
             filePath = filePath.substring(1)
+            return new Promise((r, j) => {
+                this.containerClient.deleteBlob(filePath).then(
+                    async res => {
+                        let sizeInfo = await BlobTool.updateSize([{
+                            url: filePath,
+                            size: -size
+                        }], this.container)
+                        r(sizeInfo)
+                    },
+                    err => {
+                        j(err)
+                    }
+                )
+
+            })
+        } else {
+            throw new Error("filePath或size参数不完整")
         }
-        return new Promise((r, j) => {
-            this.containerClient.deleteBlob(filePath).then(
-                res => {
-                    r(res)
-                },
-                err => {
-                    j(err)
-                }
-            )
 
-        })
     }
 
     /**
@@ -330,9 +468,9 @@ export default class BlobTool {
         return new Promise((r, j) => {
             let newBlob = this.containerClient.getBlobClient(targetUrl)
             let encodeUrl = encodeURI(sourceUrl)
-            // sas = "?sv=2020-02-10&st=2021-01-18T11%3A29%3A36Z&se=2021-01-19T11%3A59%3A36Z&sr=c&sp=rcwdl&sig=BXbsiVZ6ZogeWfyr3u2PgPVdeLlMMPOqvsh3%2BDEwnq4%3D"
             newBlob.beginCopyFromURL(encodeUrl + sas).then(
                 res => {
+                    console.log('复制成功返回数据', res)
                     r(200)
                 },
                 err => {
@@ -347,8 +485,9 @@ export default class BlobTool {
      * @param {string} targetFolder eg:'exam/评测id/paper/8b94c6b6-2572-41e5-89b9-a82fcf13891e/' 注意开头不加‘/’, 结尾需要加‘/’
      * @param {string} sourceFolder eg:'paper/JEFF組卷測試01'
      * @param {BlobTool} blobTool 非必传参数, 当目标文件和源文件不在同一个容器的时候,需要传源文件容器初始化的BlobTool
+     * @param {boolean} handleSize 是否内部处理blobsize
      */
-    copyFolder(targetFolder, sourceFolder, blobTool) {
+    copyFolder(targetFolder, sourceFolder, blobTool, handleSize = true) {
         return new Promise(async (r, j) => {
             let blobs = undefined
             let sasString = ''
@@ -363,15 +502,33 @@ export default class BlobTool {
                 }, false)
                 sasString = this.sasString
             }
+            //上传之前检查文件夹大小
+            let beforeSize = 0
+            let cont = ''
+            if (handleSize) {
+                cont = this.container
+                beforeSize = await BlobTool.checkPrefixSize(targetFolder.substring(0, targetFolder.length - 1), cont)
+            }
+
             if (blobs && blobs.blobList) {
                 let count = 0
                 blobs.blobList.forEach(blobItem => {
                     let newUrl = targetFolder + blobItem.name
                     let newBlob = this.containerClient.getBlobClient(newUrl)
                     let resourceUrl = encodeURI(blobItem.url)
-                    newBlob.beginCopyFromURL(resourceUrl + sasString).then().finally(() => {
-                        count++
-                        if (count == (blobs.blobList.length)) {
+                    newBlob.beginCopyFromURL(resourceUrl + sasString).then().finally(async () => {
+                        if (++count == (blobs.blobList.length)) {
+                            if (handleSize) {
+                                //复制之后更新大小
+                                let afterSize = await BlobTool.checkPrefixSize(targetFolder, cont)
+                                let updSize = afterSize - beforeSize
+                                if (updSize !== 0) {
+                                    BlobTool.updateSize([{
+                                        url: targetFolder,
+                                        size: updSize
+                                    }], cont)
+                                }
+                            }
                             r('复制结束后')
                         }
                     })
@@ -404,7 +561,7 @@ export default class BlobTool {
      */
     async getContainerSize() {
         return new Promise((r, j) => {
-            this.listBlob({},false).then(
+            this.listBlob({}, false).then(
                 res => {
                     let totalSize = res.blobList.reduce((total, item) => {
                         return total + parseInt(item.size)
@@ -427,12 +584,14 @@ export default class BlobTool {
     async isContainerFull() {
         return new Promise(async (r, j) => {
             try {
-                let size = await this.getContainerSize()
-                if (size > this.blobSpace) {
-                    r(true)
-                } else {
-                    r(false)
-                }
+                // let size = await this.getContainerSize()
+                let sizeRes = await BlobTool.getSizeByServe(this.container)
+                if (sizeRes)
+                    if (sizeRes.size > this.blobSpace) {
+                        r(true)
+                    } else {
+                        r(false)
+                    }
             } catch (e) {
                 j('容器空间判断失败!')
             }
@@ -447,7 +606,7 @@ export default class BlobTool {
      */
     getSize(option) {
         return new Promise((r, j) => {
-            this.listBlob(option,false).then(
+            this.listBlob(option, false).then(
                 res => {
                     let sizeInfo = {
                         total: 0,   //所有
@@ -497,4 +656,48 @@ export default class BlobTool {
             )
         })
     }
+    /**
+     * 从后端获取blob空间大小
+     * @params {containerName} 容器名称
+     * @params {cache} 是否从缓存读取
+     */
+    static getSizeByServe(containerName, cache = true) {
+        return new Promise((r, j) => {
+            if (containerName) {
+                API.blob.getContainerSize({
+                    cache,
+                    containerName
+                }).then(
+                    res => {
+                        let sizeInfo = {
+                            total: 0,   //所有
+                            image: 0,   //内容模块图片
+                            res: 0,     //内容模块教材
+                            video: 0,   //内容模块视频
+                            audio: 0,   //内容模块音频
+                            doc: 0,     //内容模块文档
+                            other: 0,   //内容模块其他文件
+                            data: 0      //除了内容模块之外的文件
+                        }
+                        sizeInfo.total = res.size
+                        let defined = ['image', 'res', 'video', 'audio', 'doc', 'other']
+                        for (let key in res.catalog) {
+                            if (defined.indexOf(key) > -1) {
+                                sizeInfo[key] = res.catalog[key]
+                            } else {
+                                sizeInfo.data += res.catalog[key]
+                            }
+                        }
+                        r(sizeInfo)
+                    },
+                    err => {
+                        console.log(err)
+                    }
+                )
+            } else {
+                throw new Error("containerName 不正确")
+            }
+        })
+    }
+
 }

+ 2 - 1
TEAMModelOS/ClientApp/src/utils/editorTools.js

@@ -442,7 +442,7 @@ export default {
 	
 	/* 移除上传的音视频富文本src中的HOST */
 	doRemoveMideaHost(exerciseItem) {
-		console.log(exerciseItem)
+		console.log('removeHost',exerciseItem.question)
 		let isSubjective = exerciseItem.type === 'complete' || exerciseItem.type === 'subjective' || exerciseItem.type ===
 			'compose'
 		let richTextObj = {
@@ -477,6 +477,7 @@ export default {
 				}))
 			}
 			Promise.all(promiseArr).then(result => {
+				console.log('removeHost2',exerciseItem.question)
 				resolve(exerciseItem)
 			})
 		})

+ 20 - 10
TEAMModelOS/ClientApp/src/utils/evTools.js

@@ -36,7 +36,7 @@ export default {
 					subjectId:item.subjectId,
 					children:item.children || [],
 					scope:item.scope,
-					score: 0,
+					score: item.score||0,
 					repair:item.repair,
 				},
 				render:2,
@@ -57,7 +57,7 @@ export default {
 				pid:item.pid || null,
 				code:item.code,
 				scope:item.scope,
-				score:0,
+				score:item.score||0,
 				type:item.type,
 				question:this.getSimpleText(item.question),
 				knowledge:item.knowledge,
@@ -117,7 +117,8 @@ export default {
 	},
 	
 	/* 获取完整的试题数据 */
-	getFullItem(list){
+	getFullItem(list,examScope){
+		console.log('接受到的examScope',examScope)
 		return new Promise(async (resolve,reject) => {
 			if(list.length === 0) return
 			let promiseArr = []
@@ -126,9 +127,10 @@ export default {
 			for(let i=0; i<list.length; i++){
 				promiseArr.push(new Promise(async (r,j) => {
 					if(list[i].blob){
-						const blobHost = list[i].scope === 'school' ?  JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri :  JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8")).blob_uri
+						let curScope = list[i].scope
+						const blobHost = curScope === 'school' ?  JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri :  JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8")).blob_uri
 						// 根据试题的Blob地址 去读取JSON文件
-						let sasString = list[i].scope === 'school' ?  await $tools.getSchoolSas() : await $tools.getPrivateSas()
+						let sasString = curScope === 'school' ?  await $tools.getSchoolSas() : await $tools.getPrivateSas()
 						let jsonInfo = list[i].blob.includes('https://') ? await $tools.getFile(list[i].blob + sasString.sas) : await $tools.getFile(blobHost + list[i].blob + sasString.sas)
 						let jsonData = JSON.parse(jsonInfo)
 						// 如果是综合题 那就拿到children里面的小题id集合 去换取小题的blobJSON文件 然后替换children的内容
@@ -307,12 +309,19 @@ export default {
 	},
 	
 	/* 获取完整的试卷数据 */
-	getFullPaper(paper){
+	getFullPaper(paper,examScope){
 		console.log('evTools换取试卷' , paper)
+		let curScope = examScope || paper.scope
+		console.log(curScope)
 		return new Promise(async (r,j) => {
-			let blobHost = paper.scope === 'school' ?  JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri :  JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8")).blob_uri
+			let blobHost = curScope === 'school' ?  JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri :  JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8")).blob_uri
 			// 根据试卷的Blob地址 去读取JSON文件
-			let sasString = paper.scope === 'school' ?  await $tools.getSchoolSas() : await $tools.getPrivateSas()
+			let sasString = curScope === 'school' ?  await $tools.getSchoolSas() : await $tools.getPrivateSas()
+			console.log(curScope)
+			console.log(sasString.sas)
+			console.log(sasString.name)
+			console.log(await $tools.getSchoolSas())
+			console.log(await $tools.getPrivateSas())
 			
 			try{
 				let jsonInfo = await $tools.getFile(blobHost + paper.blob + '/index.json' + sasString.sas)
@@ -375,13 +384,14 @@ export default {
 	
 	
 	/* 获取完整的试卷数据 */
-	getStuPaper(paper){
+	getStuPaper(paper,examScope){
 		//console.log('evTools换取学生试卷' , paper)
+		let curScope = examScope || paper.scope
 		return new Promise(async (r,j) => {
 			let blobHost = JSON.parse(decodeURIComponent(localStorage.student_profile, "utf-8")).blob_uri
 			let splitHost = blobHost.split('/')
 			// 根据试卷的Blob地址 去读取JSON文件
-			let sasString = paper.scope === 'school' ?  await $tools.getSchoolSas(paper.code) : await $tools.getPrivateSas(paper.code)
+			let sasString = curScope === 'school' ?  await $tools.getSchoolSas(paper.code) : await $tools.getPrivateSas(paper.code)
 			try {
 				let jsonInfo = await $tools.getFile(sasString.url + '/' + paper.code + paper.blob + '/index.json' + sasString.sas)
 				let jsonData = JSON.parse(jsonInfo)

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

@@ -288,7 +288,7 @@ export default {
 			switch (param.type) {
 				case 'file': //单文件授权
 					if (!store.state.fileSas || checkSas(store.state.fileSas.timeout)) {
-						$api.uploadFile.urlSasR({
+						$api.blob.urlSasR({
 							params: param.data
 						}).then(
 							(res) => {
@@ -308,7 +308,7 @@ export default {
 					break
 				case 'blobR': //容器只读权限
 					if (!store.state.blobR || checkSas(store.state.blobR.timeout)) {
-						$api.uploadFile.blobSasR(param.data).then(
+						$api.blob.blobSasR(param.data).then(
 							(res) => {
 								if (res.error == null) {
 									//store.commit('setBlobR')
@@ -327,7 +327,7 @@ export default {
 					break
 				case 'blobRW':
 					if (!store.state.blobRW || checkSas(store.state.blobRW.timeout)) {
-						$api.uploadFile.blobSasRCW(param.data).then(
+						$api.blob.blobSasRCW(param.data).then(
 							(res) => {
 								if (res.error == null) {
 									store.commit('setBlobRW', res.result.data)
@@ -357,7 +357,7 @@ export default {
 	getPrivateSas(code) {
 		return new Promise((r, j) => {
 			if (!store.state.privateSas || checkSas(store.state.privateSas.timeout) || store.state.privateSas.name !== store.state.userInfo.TEAMModelId) {
-				$api.uploadFile.blobSasRCW({
+				$api.blob.blobSasRCW({
 					name: code || store.state.userInfo.TEAMModelId,
 					role: 'teacher'
 				}).then(
@@ -385,7 +385,7 @@ export default {
 	getSchoolSas(code) {
 		return new Promise((r, j) => {
 			if (!store.state.schoolSas || checkSas(store.state.schoolSas.timeout)  || store.state.schoolSas.name !== store.state.userInfo.schoolCode) {
-				$api.uploadFile.blobSasRCW({
+				$api.blob.blobSasRCW({
 					name: code || store.state.userInfo.schoolCode,
 					role: 'school'
 				}).then(
@@ -412,7 +412,7 @@ export default {
 	 */
 	getFileSas(param) {
 		return new Promise((r, j) => {
-			$api.uploadFile.urlSasR({
+			$api.blob.urlSasR({
 				url: param
 			}).then(
 				(res) => {

+ 392 - 398
TEAMModelOS/ClientApp/src/view/classmgt/ManageClass.vue

@@ -1,47 +1,47 @@
 <template>
     <div class="mgt-class-container dark-iview-select dark-iview-checkbox custom-scroll-bar common-save-btn" @click="nameEdStatus = true">
         <div class="mgt-class-header">
-            <span class="text-label">班级:</span>
+            <span class="text-label">{{$t('cusMgt.classLabel')}}</span>
             <Select v-model="curClassCode" style="width:200px">
                 <Option v-for="(item,index) in classList" :value="item.id" :key="index" @click.native="selectClass(index)">{{ item.name }}</Option>
             </Select>
             <span v-if="classList[curClassIndex]" style="margin-left: 10px;color: #aaaaaa;">
-                学生人数:{{classList[curClassIndex].students.length}}
+                {{$t('cusMgt.stuCount')}}{{classList[curClassIndex].students.length}}
             </span>
             <Button :disabled="!updated" size="small" style="float:right;margin-right:30px;margin-top:10px;" @click="saveGroup" icon="ios-albums-outline">保存分组</Button>
             <span :class="classList.length > 0 ? 'action-btn-wrap': 'disable-text-icon action-btn-wrap'" @click="customGroupStatus = true">
                 <Icon type="md-shuffle" size="14" />
-                自动分组
+                {{$t('cusMgt.autoGroup')}}
             </span>
             <span :class="classList.length > 0 ? 'action-btn-wrap': 'disable-text-icon action-btn-wrap'" @click="exportStudents">
                 <Icon type="md-arrow-round-down" size="16" />
-                导出名单
+                {{$t('cusMgt.exportStu')}}
             </span>
             <span :class="classList.length > 0 ? 'action-btn-wrap': 'disable-text-icon action-btn-wrap'" @click="toggleView()">
                 <Icon :type="viewType ? 'md-card':'md-list'" size="16" />
-                {{viewType ? '分组视图':'列表视图' }}
+                {{viewType ? $t('cusMgt.viewport1'):$t('cusMgt.viewport2') }}
             </span>
         </div>
         <div class="mgt-class-body dark-iview-table animated fadeIn" id="table-wrap" v-if="viewType == 1">
             <vuescroll>
-                <Table :columns="columns" v-if="classList[curClassIndex]" :data="classList[curClassIndex].students" :loading="tableLoading" ref="students" :height="tableHeight" no-data-text="暂无学生">
+                <Table :columns="columns" v-if="classList[curClassIndex]" :data="classList[curClassIndex].students" :loading="tableLoading" ref="students" :height="tableHeight" :no-data-text="$t('cusMgt.noStu')">
                     <Loading slot="loading" bgColor="rgba(103, 103, 103, 0.27)"></Loading>
                     <template slot-scope="{ row,index }" slot="header">
                         <span class="name-header" :style="{background:bgColor[index % 12]}">{{getFirstChart(row.name)}}</span>
                     </template>
                     <template slot-scope="{ row,index }" slot="action">
                         <div class="item-tools">
-                            <Icon type="md-refresh" size="18" style="cursor:pointer;" title="重置密码" @click="resetPassword(index)"/>
+                            <Icon type="md-refresh" size="18" style="cursor:pointer;" :title="$t('cusMgt.resetPw')" @click="resetPassword(index)" />
                         </div>
                     </template>
                     <template slot-scope="{ row, index }" slot="groupId">
                         <span>{{row.groupId ? row.groupId : '- -'}}</span>
                     </template>
                     <template slot-scope="{ row, index }" slot="groupName">
-                        <span>{{row.groupName ? row.groupName : '未分组'}}</span>
+                        <span>{{row.groupName ? row.groupName : $t('cusMgt.noGroup')}}</span>
                     </template>
                 </Table>
-                <EmptyData v-else textContent="暂无您管理的班级" :top="150"></EmptyData>
+                <EmptyData v-else :textContent="$t('cusMgt.noMgtClass')" :top="150"></EmptyData>
             </vuescroll>
         </div>
         <div class="mgt-class-body dark-iview-table animated fadeIn dark-iview-input disabled-iview-input" id="table-wrap" v-else>
@@ -49,8 +49,7 @@
                 <div class="group-wrap-item" v-for="(item,index) in groupData" :key="index">
                     <div class="group-title-wrap">
                         <span class="group-num-tag">{{parseInt(item.groupId)}}</span>
-                        <Input v-model="item.groupName" class="group-name-tag" placeholder="组名..." 
-                               :disabled="nameEdStatus" @click.native.stop="nameEdStatus = false" title="修改组名" @on-change="$jsFn.debounce(setGroupName,1000)(index)"/>
+                        <Input v-model="item.groupName" class="group-name-tag" :placeholder="$t('cusMgt.groupNameHolder')" :disabled="nameEdStatus" @click.native.stop="nameEdStatus = false" :title="$t('cusMgt.edtiGroupName')" @on-change="$jsFn.debounce(setGroupName,1000)(index)" />
                         <Icon type="md-close" class="close-group-icon" @click="delGroup(index)" />
                     </div>
                     <Draggable ghost-class="ghost" group="student" class="list-group" :list="item.students" :animation='200' @end="groupToList">
@@ -58,37 +57,34 @@
                             <span>{{stuItem.no+ '-' +stuItem.name}}</span>
                             <span>{{'('+stuItem.id+')'}}</span>
                         </div>
-                        <EmptyData textContent="暂无学生" v-if="item.students.length == 0"></EmptyData>
+                        <EmptyData :textContent="$t('cusMgt.noStu')" v-if="item.students.length == 0"></EmptyData>
                     </Draggable>
                 </div>
                 <div id="add-group-box">
-                    <Input v-model="groupName" placeholder="输入组名......" style="width: 200px;margin:40px 50px 10px 50px;" />
+                    <Input v-model="groupName" :placeholder="$t('cusMgt.groupNameHolder')" style="width: 200px;margin:40px 50px 10px 50px;" />
                     <Icon type="md-add-circle" class="add-group-icon" @click="addGroup" />
-                    <p class="add-group-label" @click="addGroup">新增组别</p>
+                    <p class="add-group-label" @click="addGroup">{{$t('cusMgt.addGroup')}}</p>
                 </div>
             </vuescroll>
         </div>
-        <Modal v-model="customGroupStatus"
-               :title="$t('courseManage.classroom.autoGroupBtn')"
-               @on-ok="comfirmCustomRules" class="custom-group"
-               class-name="dark-iview-modal dark-iview-form">
+        <Modal v-model="customGroupStatus" :title="$t('cusMgt.autoGroup')" @on-ok="comfirmCustomRules" class="custom-group" class-name="dark-iview-modal dark-iview-form">
             <Form :label-width="80" :label-colon="true" style="color:white;">
-                <FormItem :label="$t('courseManage.classroom.studentCountLabel')">
+                <FormItem :label="$t('cusMgt.studentCountLabel')">
                     <span v-if="customGroupStatus">{{classList[curClassIndex].students.length}}人</span>
                 </FormItem>
-                <FormItem :label="$t('courseManage.classroom.groupCountLabel')">
+                <FormItem :label="$t('cusMgt.groupCountLabel')">
                     <InputNumber :max="10" :min="1" v-model="groupNum"></InputNumber>
                 </FormItem>
-                <FormItem :label="$t('courseManage.classroom.groupTypeLabel')">
+                <FormItem :label="$t('cusMgt.groupTypeLabel')">
                     <RadioGroup v-model="groupType">
                         <Radio label="1">
-                            <span>{{$t('courseManage.classroom.groupType1')}}</span>
+                            <span>{{$t('cusMgt.groupType1')}}</span>
                         </Radio>
                         <Radio label="2">
-                            <span>{{$t('courseManage.classroom.groupType2')}}</span>
+                            <span>{{$t('cusMgt.groupType2')}}</span>
                         </Radio>
                         <Radio label="3">
-                            <span>{{$t('courseManage.classroom.groupType3')}}</span>
+                            <span>{{$t('cusMgt.groupType3')}}</span>
                         </Radio>
                     </RadioGroup>
                 </FormItem>
@@ -97,413 +93,411 @@
     </div>
 </template>
 <script>
-    import Draggable from 'vuedraggable'
-    import excel from '@/utils/excel.js'
-    export default {
-        components: {
-            Draggable
-        },
-        data() {
-            return {
-                curClassIndex:0,
-                groupName:'',
-                groupData: [],
-                tableHeight:0,
-                nameEdStatus: true,
-                viewType: 1,
-                tableLoading: false,
-                customGroupStatus: false,
-                updated: false,
-                tableLoading: false,
-                classList: [],
-                groupNum: 2,
-                groupType: '1',
-                bgColor: ['#4ECDC4', '#F99406', '#075CD0', '#F7CA17', '#F2774B', '#67809F', '#BF55ED', '#00B169', '#F72459', '#F15A22', '#03C9A8', '#9A1294'],
-                curClassCode: '',
-                columns: [
-                    {
-                        type: 'selection',
-                        width: 80,
-                        align: 'center'
-                    },
-                    {
-                        slot: 'header',
-                        title: ' ',
-                        align: 'center',
-                        //width:120
-                    },
-                    {
-                        key: 'name',
-                        title: this.$t('stuAccount.stuName'),
-                        align: 'center',
-                        //width:200
-                    },
-                    {
-                        key: 'id',
-                        title: this.$t('stuAccount.account'),
-                        align: 'center',
-                        //width:200
-                    },
-                    {
-                        key: 'no',
-                        title: this.$t('stuAccount.seatNo'),
-                        align: 'center',
-                        sortable: true,
-                        sortMethod: function (a, b, type) {
-                            if (type == 'asc') {
-                                return parseInt(a) > parseInt(b) ? 1 : -1
-                            } else if (type == 'desc') {
-                                return parseInt(a) > parseInt(b) ? -1 : 1
-                            }
+import Draggable from 'vuedraggable'
+import excel from '@/utils/excel.js'
+export default {
+    components: {
+        Draggable
+    },
+    data() {
+        return {
+            curClassIndex: 0,
+            groupName: '',
+            groupData: [],
+            tableHeight: 0,
+            nameEdStatus: true,
+            viewType: 1,
+            tableLoading: false,
+            customGroupStatus: false,
+            updated: false,
+            tableLoading: false,
+            classList: [],
+            groupNum: 2,
+            groupType: '1',
+            bgColor: ['#4ECDC4', '#F99406', '#075CD0', '#F7CA17', '#F2774B', '#67809F', '#BF55ED', '#00B169', '#F72459', '#F15A22', '#03C9A8', '#9A1294'],
+            curClassCode: '',
+            columns: [
+                {
+                    type: 'selection',
+                    width: 80,
+                    align: 'center'
+                },
+                {
+                    slot: 'header',
+                    title: ' ',
+                    align: 'center',
+                    //width:120
+                },
+                {
+                    key: 'name',
+                    title: this.$t('stuAccount.stuName'),
+                    align: 'center',
+                    //width:200
+                },
+                {
+                    key: 'id',
+                    title: this.$t('stuAccount.account'),
+                    align: 'center',
+                    //width:200
+                },
+                {
+                    key: 'no',
+                    title: this.$t('stuAccount.seatNo'),
+                    align: 'center',
+                    sortable: true,
+                    sortMethod: function (a, b, type) {
+                        if (type == 'asc') {
+                            return parseInt(a) > parseInt(b) ? 1 : -1
+                        } else if (type == 'desc') {
+                            return parseInt(a) > parseInt(b) ? -1 : 1
                         }
-                    },
-                    {
-                        slot: 'groupId',
-                        title: '组别',
-                        key:'groupId',
-                        align: 'center',
-                        sortable: true,
-                        sortMethod: function (a, b, type) {
-                            if (type == 'asc') {
-                                return parseInt(a) > parseInt(b) ? 1 : -1
-                            } else if (type == 'desc') {
-                                return parseInt(a) > parseInt(b) ? -1 : 1
-                            }
+                    }
+                },
+                {
+                    slot: 'groupId',
+                    title: this.$t('cusMgt.stuClassCol4'),
+                    key: 'groupId',
+                    align: 'center',
+                    sortable: true,
+                    sortMethod: function (a, b, type) {
+                        if (type == 'asc') {
+                            return parseInt(a) > parseInt(b) ? 1 : -1
+                        } else if (type == 'desc') {
+                            return parseInt(a) > parseInt(b) ? -1 : 1
                         }
-                    },
-                    {
-                        slot: 'groupName',
-                        title: '组名',
-                        key: 'groupName',
-                        align: 'center',
-                        sortable: true,
-                        //width: 80
-                    },
-                    {
-                        slot: 'action',
-                        title: '操作',
-                        align: 'center'
                     }
-                ]
+                },
+                {
+                    slot: 'groupName',
+                    title: this.$t('cusMgt.action'),
+                    key: 'groupName',
+                    align: 'center',
+                    sortable: true,
+                    //width: 80
+                },
+                {
+                    slot: 'action',
+                    title: this.$t('cusMgt.searchHolder'),
+                    align: 'center'
+                }
+            ]
+        }
+    },
+    methods: {
+        resetPassword(index) {
+            this.$Message.warning('暂未对接重置密码API')
+        },
+        //切换班级
+        selectClass(index) {
+            this.curClassIndex = index
+            if (this.viewType == 0) {
+                this.handelGroup()
             }
         },
-        methods: {
-            resetPassword(index) {
-                this.$Message.warning('暂未对接重置密码API')
-            },
-            //切换班级
-            selectClass(index) {
-                this.curClassIndex = index
-                if (this.viewType == 0) {
-                    this.handelGroup()
-                    console.log('123')
-                }
-            },
-            //设置组名
-            setGroupName(index) {
-                console.log('执行函数')
-                for (let i in this.classList[this.curClassIndex].students) {
-                    if (this.classList[this.curClassIndex].students[i].groupId == this.groupData[index].groupId) {
-                        this.classList[this.curClassIndex].students[i].groupName = this.groupData[index].groupName
-                        console.log(this.groupData[index].groupName)
-                        console.log(this.classList[this.curClassIndex].students[i])
-                    }
-                }
-                this.updated = true
-            },
-            //分组视图修改后对应调整表格视图
-            groupToList() {
-                this.classList[this.curClassIndex].students = []
-                for (let i in this.groupData) {
-                    for (let j in this.groupData[i].students) {
-                        this.groupData[i].students[j].groupId = this.groupData[i].groupId
-                        this.groupData[i].students[j].groupName = this.groupData[i].groupName
-                        this.classList[this.curClassIndex].students.push(this.groupData[i].students[j])
-                    }
-                }
-                console.log(this.groupData)
-                this.updated = true
-            },
-            //删除组别
-            delGroup(index) {
-                if (this.groupData.length > 1) {
-                    let students = JSON.parse(JSON.stringify(this.groupData[index].students))
-                    for (let i in students) {
-                        students[i].groupId = this.groupData[0].groupId
-                        students[i].groupName = this.groupData[0].groupName
-                    }
-                    this.groupData.splice(index, 1)
-                    this.groupData[0].students.push(...students)
-                    this.groupToList()
-                } else {
-                    this.$Message.warning('至少保留一组')
+        //设置组名
+        setGroupName(index) {
+            for (let i in this.classList[this.curClassIndex].students) {
+                if (this.classList[this.curClassIndex].students[i].groupId == this.groupData[index].groupId) {
+                    this.classList[this.curClassIndex].students[i].groupName = this.groupData[index].groupName
                 }
-                console.log(this.groupData)
-                this.updated = true
-            },
-            //新增分组
-            addGroup() {
-                if (this.groupName == '') {
-                    this.$Message.warning('请先输入组名再创建组别')
-                } else if (this.classList[this.curClassIndex].students.length == 0) {
-                    this.$Message.warning('暂无学生可以进行分组')
-                } else {
-                    this.groupData.push({
-                        groupId: (this.groupData.length + 1) + '',
-                        groupName: this.groupName,
-                        students: []
-                    })
-                    this.groupName = ''
-                }
-                this.updated = true
-            },
-            toggleView() {
-                this.viewType = 1 - this.viewType
-                if (!this.viewType) {
-                    this.handelGroup()
+            }
+            this.updated = true
+        },
+        //分组视图修改后对应调整表格视图
+        groupToList() {
+            this.classList[this.curClassIndex].students = []
+            for (let i in this.groupData) {
+                for (let j in this.groupData[i].students) {
+                    this.groupData[i].students[j].groupId = this.groupData[i].groupId
+                    this.groupData[i].students[j].groupName = this.groupData[i].groupName
+                    this.classList[this.curClassIndex].students.push(this.groupData[i].students[j])
                 }
-                
-            },
-            //列表视图转分组视图
-            handelGroup() {
-                let groupRes = this.$jsFn.groupBy(this.classList[this.curClassIndex].students, 'groupId')
-                this.groupData.length = 0
-                for (let index in groupRes) {
-                    this.groupData.push({
-                        groupName: groupRes[index][0].groupName,
-                        groupId: groupRes[index][0].groupId,
-                        students: groupRes[index]
-                    })
+            }
+            this.updated = true
+        },
+        //删除组别
+        delGroup(index) {
+            if (this.groupData.length > 1) {
+                let students = JSON.parse(JSON.stringify(this.groupData[index].students))
+                for (let i in students) {
+                    students[i].groupId = this.groupData[0].groupId
+                    students[i].groupName = this.groupData[0].groupName
                 }
-                this.groupData.sort((a, b) => {
-                    return parseInt(a.groupId) - parseInt(b.groupId)
-                })
-                //this.updated = true
-            },
-            saveGroup() {
-                console.log('保存分组。。。')
-                console.log(this.classList[this.curClassIndex])
-                this.tableLoading = true
-                this.$api.schoolSetting.upsertGroup({
-                    classroom: this.classList[this.curClassIndex]
-                }).then(
-                    (res) => {
-                        if (!res.error) {
-                            this.$Message.success('保存成功!')
-                        } else {
-                            this.$Message.error('API error!')
-                        }
-                    },
-                    (err) => {
-                        this.$Message.error('API error!')
-                    }
-                ).finally(() => {
-                    setTimeout(() => {
-                        this.tableLoading = false
-                    }, 500)
+                this.groupData.splice(index, 1)
+                this.groupData[0].students.push(...students)
+                this.groupToList()
+            } else {
+                this.$Message.warning(this.$t('cusMgt.atLeast'))
+            }
+            this.updated = true
+        },
+        //新增分组
+        addGroup() {
+            if (this.groupName == '') {
+                this.$Message.warning(this.$t('cusMgt.checkName'))
+            } else if (this.classList[this.curClassIndex].students.length == 0) {
+                this.$Message.warning(this.$t('cusMgt.noStuTips'))
+            } else {
+                this.groupData.push({
+                    groupId: (this.groupData.length + 1) + '',
+                    groupName: this.groupName,
+                    students: []
                 })
+                this.groupName = ''
+            }
+            this.updated = true
+        },
+        toggleView() {
+            this.viewType = 1 - this.viewType
+            if (!this.viewType) {
+                this.handelGroup()
+            }
 
-            },
-            comfirmCustomRules() {
-                if (this.groupNum === 0) {
-                    this.$Message.warning('分组数量不能为0')
-                } else if (this.groupType == 0) {
-                    this.$Message.warning('请设置分组方式')
-                } else {
-                    switch (this.groupType) {
-                        case '1':
-                            this.randomGroup()
-                            break
-                        case '2':
-                            this.orderGroup()
-                            break
-                        case '3':
-                            this.orderGroupS()
-                            break
-                        default:
-                            break
-                    }
-                    this.tableLoading = true
-                    this.updated = true
-                    this.handelGroup()
-                }
-            },
-            orderGroupS() {
-                let stuLen = this.classList[this.curClassIndex].students.length
-                let surplus = stuLen % this.groupNum// 余数
-                let maxCount = surplus == 0 ? stuLen / this.groupNum : Math.ceil(stuLen / this.groupNum)// 每组最大人数
-                this.classList[this.curClassIndex].students = this.classList[this.curClassIndex].students.sort((a, b) => {
-                    a.seatNo > b.seatNo
-                })
-                for (let i = 0; i < maxCount; i++) {
-                    for (let j = 0; j < this.groupNum; j++) {
-                        let startIndex = this.groupNum * i
-                        if (startIndex + j < stuLen) {
-                            this.$set(this.classList[this.curClassIndex].students[startIndex + j], 'groupId', i + 1)
-                            this.$set(this.classList[this.curClassIndex].students[startIndex + j], 'groupName', '第'+ (i + 1)+ '组' )
-                        } else {
-                            break
-                        }
-                    }
-                }
-                setTimeout(() => {
-                    this.tableLoading = false
-                }, 500)
-            },
-            orderGroup() {
-                let stuLen = this.classList[this.curClassIndex].students.length
-                let surplus = stuLen % this.groupNum// 余数
-                let maxCount = surplus == 0 ? stuLen / this.groupNum : Math.ceil(stuLen / this.groupNum)// 每组最大人数
-                this.classList[this.curClassIndex].students = this.classList[this.curClassIndex].students.sort((a, b) => {
-                    a.no > b.no
+        },
+        //列表视图转分组视图
+        handelGroup() {
+            let groupRes = this.$jsFn.groupBy(this.classList[this.curClassIndex].students, 'groupId')
+            this.groupData.length = 0
+            for (let index in groupRes) {
+                this.groupData.push({
+                    groupName: groupRes[index][0].groupName,
+                    groupId: groupRes[index][0].groupId,
+                    students: groupRes[index]
                 })
-                let flag = 0
-                let startIndex = 0
-                for (let i = 0; i < this.groupNum; i++) {
-                    for (let j = 0; j < maxCount; j++) {
-                        if (startIndex + j < stuLen) {
-                            this.$set(this.classList[this.curClassIndex].students[startIndex + j], 'groupId', i + 1)
-                            this.$set(this.classList[this.curClassIndex].students[startIndex + j], 'groupName', '第' + (i + 1)+ '组')
-                        } else {
-                            break
-                        }
-                    }
-                    startIndex += maxCount
-                    flag++
-                    if (flag == surplus) {
-                        maxCount--
+            }
+            this.groupData.sort((a, b) => {
+                return parseInt(a.groupId) - parseInt(b.groupId)
+            })
+            //this.updated = true
+        },
+        saveGroup() {
+            this.tableLoading = true
+            this.$api.schoolSetting.upsertGroup({
+                classroom: this.classList[this.curClassIndex]
+            }).then(
+                (res) => {
+                    if (!res.error) {
+                        this.$Message.success(this.$t('cusMgt.saveOk'))
+                    } else {
+                        this.$Message.error('API error!')
                     }
+                },
+                (err) => {
+                    this.$Message.error('API error!')
                 }
+            ).finally(() => {
                 setTimeout(() => {
                     this.tableLoading = false
                 }, 500)
-            },
-            randomGroup() {
-                let stuLen = this.classList[this.curClassIndex].students.length
-                let surplus = stuLen % this.groupNum// 余数
-                let surplusCount = surplus// 余数
-                let maxCount = surplus == 0 ? stuLen / this.groupNum : Math.ceil(stuLen / this.groupNum)// 每组最大人数
-                let record = {}// 记录每个组已经分配的人数
-                for (let i = 1; i <= this.groupNum; i++) {
-                    record[i] = 0
+            })
+
+        },
+        comfirmCustomRules() {
+            if (this.groupNum === 0) {
+                this.$Message.warning(this.$t('cusMgt.groupCount'))
+            } else if (this.groupType == 0) {
+                this.$Message.warning(this.$t('cusMgt.groupTypeTips'))
+            } else {
+                switch (this.groupType) {
+                    case '1':
+                        this.randomGroup()
+                        break
+                    case '2':
+                        this.orderGroup()
+                        break
+                    case '3':
+                        this.orderGroupS()
+                        break
+                    default:
+                        break
                 }
-                let flag = true
-                for (let index in this.classList[this.curClassIndex].students) {
-                    let groupIndex = this.$jsFn.getBtwRandom(1, this.groupNum)
-                    if (record[groupIndex] < maxCount) {
-                        record[groupIndex] = record[groupIndex] + 1
+                this.tableLoading = true
+                this.updated = true
+                this.handelGroup()
+            }
+        },
+        orderGroupS() {
+            let stuLen = this.classList[this.curClassIndex].students.length
+            let surplus = stuLen % this.groupNum// 余数
+            let maxCount = surplus == 0 ? stuLen / this.groupNum : Math.ceil(stuLen / this.groupNum)// 每组最大人数
+            this.classList[this.curClassIndex].students = this.classList[this.curClassIndex].students.sort((a, b) => {
+                a.seatNo > b.seatNo
+            })
+            for (let i = 0; i < maxCount; i++) {
+                for (let j = 0; j < this.groupNum; j++) {
+                    let startIndex = this.groupNum * i
+                    if (startIndex + j < stuLen) {
+                        this.$set(this.classList[this.curClassIndex].students[startIndex + j], 'groupId', i + 1)
+                        this.$set(this.classList[this.curClassIndex].students[startIndex + j], 'groupName', (i + 1) + this.$t('cusMgt.groupUnit'))
                     } else {
-                        for (let key in record) {
-                            if (record[key] < maxCount) {
-                                record[key] = record[key] + 1
-                                groupIndex = key
-                                break
-                            }
-                        }
-                    }
-                    this.$set(this.classList[this.curClassIndex].students[index], 'groupId', groupIndex+'')
-                    this.$set(this.classList[this.curClassIndex].students[index], 'groupName', '第' + groupIndex + '组')
-                    if (record[groupIndex] == maxCount) {
-                        surplusCount--
+                        break
                     }
-                    if (surplusCount <= 0 && surplus > 0 && flag) {
-                        maxCount--
-                        flag = false
+                }
+            }
+            setTimeout(() => {
+                this.tableLoading = false
+            }, 500)
+        },
+        orderGroup() {
+            let stuLen = this.classList[this.curClassIndex].students.length
+            let surplus = stuLen % this.groupNum// 余数
+            let maxCount = surplus == 0 ? stuLen / this.groupNum : Math.ceil(stuLen / this.groupNum)// 每组最大人数
+            this.classList[this.curClassIndex].students = this.classList[this.curClassIndex].students.sort((a, b) => {
+                a.no > b.no
+            })
+            let flag = 0
+            let startIndex = 0
+            for (let i = 0; i < this.groupNum; i++) {
+                for (let j = 0; j < maxCount; j++) {
+                    if (startIndex + j < stuLen) {
+                        this.$set(this.classList[this.curClassIndex].students[startIndex + j], 'groupId', i + 1)
+                        this.$set(this.classList[this.curClassIndex].students[startIndex + j], 'groupName', (i + 1) + this.$t('cusMgt.groupUnit'))
+                    } else {
+                        break
                     }
                 }
-                setTimeout(() => {
-                    this.tableLoading = false
-                }, 500)
-            },
-            getFirstChart(name) {
-                if (name) {
-                    return name.substr(0, 1)
+                startIndex += maxCount
+                flag++
+                if (flag == surplus) {
+                    maxCount--
+                }
+            }
+            setTimeout(() => {
+                this.tableLoading = false
+            }, 500)
+        },
+        randomGroup() {
+            let stuLen = this.classList[this.curClassIndex].students.length
+            let surplus = stuLen % this.groupNum// 余数
+            let surplusCount = surplus// 余数
+            let maxCount = surplus == 0 ? stuLen / this.groupNum : Math.ceil(stuLen / this.groupNum)// 每组最大人数
+            let record = {}// 记录每个组已经分配的人数
+            for (let i = 1; i <= this.groupNum; i++) {
+                record[i] = 0
+            }
+            let flag = true
+            for (let index in this.classList[this.curClassIndex].students) {
+                let groupIndex = this.$jsFn.getBtwRandom(1, this.groupNum)
+                if (record[groupIndex] < maxCount) {
+                    record[groupIndex] = record[groupIndex] + 1
                 } else {
-                    return '--'
+                    for (let key in record) {
+                        if (record[key] < maxCount) {
+                            record[key] = record[key] + 1
+                            groupIndex = key
+                            break
+                        }
+                    }
                 }
-            },
-            //导出名单
-            exportStudents() {
-                /*const params = {
-                    title: this.columns.map(i => i.title),
-                    key: this.columns.map(i => i.key),
-                    data: this.classList[this.curClassIndex].students,
-                    autoWidth: true,
-                    filename: '学生名单'
-                }*/
-                const params = {
-                    title: ['姓名','账号','座号','组别','组名'],
-                    key: ['name','id','no','groupId','groupName'],
-                    data: this.classList[this.curClassIndex].students,
-                    autoWidth: true,
-                    filename: '学生名单'
+                this.$set(this.classList[this.curClassIndex].students[index], 'groupId', groupIndex + '')
+                this.$set(this.classList[this.curClassIndex].students[index], 'groupName', groupIndex + this.$t('cusMgt.groupUnit'))
+                if (record[groupIndex] == maxCount) {
+                    surplusCount--
                 }
-                excel.export_array_to_excel(params)
-            },
-            //查询自己管理的班级
-            findClass() {
-                this.tableLoading = true
-                let params = {
-                    'school_code': this.$store.state.userInfo.schoolCode,
-                    'teacher.id': this.$store.state.userInfo.TEAMModelId,
-                    'scope':'school'
+                if (surplusCount <= 0 && surplus > 0 && flag) {
+                    maxCount--
+                    flag = false
                 }
-                this.$api.schoolSetting.getClassroomStudent(params).then(
-                    (res) => {
-                        if (!res.error) {
-                            this.classList = res.classrooms
-                            if (this.classList.length > 0) {
-                                this.curClassCode = this.classList[0].id
-                            }
-                        } else {
-                            this.$Message.error('API error!')
-                        }
-                    },
-                    (err) => {
-                        this.$Message.error('API error!')
-                    }
-                ).finally(() => {
-                    setTimeout(() => {
-                        this.tableLoading = false
-                    },500)
-                })
             }
+            setTimeout(() => {
+                this.tableLoading = false
+            }, 500)
         },
-        created() {
-            this.findClass()
-            this.$nextTick(() => {
-                let dom = document.getElementById('table-wrap')
-                this.tableHeight = dom.offsetHeight - 30
-            })
+        getFirstChart(name) {
+            if (name) {
+                return name.substr(0, 1)
+            } else {
+                return '--'
+            }
         },
-        mounted() {
-            
+        //导出名单
+        exportStudents() {
+            /*const params = {
+                title: this.columns.map(i => i.title),
+                key: this.columns.map(i => i.key),
+                data: this.classList[this.curClassIndex].students,
+                autoWidth: true,
+                filename: '学生名单'
+            }*/
+            const params = {
+                title: [
+                    this.$t('cusMgt.stuClassCol1'),
+                    this.$t('cusMgt.stuClassCol2'),
+                    this.$t('cusMgt.stuClassCol3'),
+                    this.$t('cusMgt.stuClassCol4'),
+                    this.$t('cusMgt.stuClassCol5')
+                ],
+                key: ['name', 'id', 'no', 'groupId', 'groupName'],
+                data: this.classList[this.curClassIndex].students,
+                autoWidth: true,
+                filename: this.$t('cusMgt.nameList')
+            }
+            excel.export_array_to_excel(params)
         },
-        computed: {
+        //查询自己管理的班级
+        findClass() {
+            this.tableLoading = true
+            let params = {
+                'school_code': this.$store.state.userInfo.schoolCode,
+                'teacher.id': this.$store.state.userInfo.TEAMModelId,
+                'scope': 'school'
+            }
+            this.$api.schoolSetting.getClassroomStudent(params).then(
+                (res) => {
+                    if (!res.error) {
+                        this.classList = res.classrooms
+                        if (this.classList.length > 0) {
+                            this.curClassCode = this.classList[0].id
+                        }
+                    } else {
+                        this.$Message.error('API error!')
+                    }
+                },
+                (err) => {
+                    this.$Message.error('API error!')
+                }
+            ).finally(() => {
+                setTimeout(() => {
+                    this.tableLoading = false
+                }, 500)
+            })
         }
+    },
+    created() {
+        this.findClass()
+        this.$nextTick(() => {
+            let dom = document.getElementById('table-wrap')
+            this.tableHeight = dom.offsetHeight - 30
+        })
+    },
+    mounted() {
+
+    },
+    computed: {
     }
+}
 </script>
 <style scoped lang="less">
-    @import "./ManageClass.less";
+@import "./ManageClass.less";
 </style>
 <style>
-    
-    .mgt-class-container #loadingBox {
-        margin-top: 0px !important;
-    }
-    .group-title-wrap .ivu-input[disabled] {
-        color: white;
-        vertical-align: super;
-        font-weight: 800;
-    }
-    .ghost {
-        opacity: 0;
-        background: #c8ebfb;
-    }
-    #add-group-box .ivu-input {
-        background:#2B2B2E;
-    }
-    .group-title-wrap .ivu-input[disabled], .group-title-wrap fieldset[disabled] .ivu-input {
-        cursor:pointer;
-    }
+.mgt-class-container #loadingBox {
+    margin-top: 0px !important;
+}
+.group-title-wrap .ivu-input[disabled] {
+    color: white;
+    vertical-align: super;
+    font-weight: 800;
+}
+.ghost {
+    opacity: 0;
+    background: #c8ebfb;
+}
+#add-group-box .ivu-input {
+    background: #2b2b2e;
+}
+.group-title-wrap .ivu-input[disabled],
+.group-title-wrap fieldset[disabled] .ivu-input {
+    cursor: pointer;
+}
 </style>

+ 2 - 2
TEAMModelOS/ClientApp/src/view/classrecord/ClassRecord.vue

@@ -688,7 +688,7 @@
                 //} else {
                 //    this.$Message.error('获取Blob授权失败')
                 //}
-                this.$api.uploadFile.blobSasRCW({
+                this.$api.blob.blobSasRCW({
                     name: "1595321354",
                     role: 'teacher'
                 }).then(
@@ -705,7 +705,7 @@
                     }
                 )
 
-                this.$api.uploadFile.blobSasRCW({
+                this.$api.blob.blobSasRCW({
                     name: 'hbcn',
                     role: 'school'
                 }).then(

+ 33 - 37
TEAMModelOS/ClientApp/src/view/evaluation/bank/ExerciseList.vue

@@ -193,8 +193,6 @@
 	</div>
 </template>
 <script>
-	import blobTool from "@/utils/blobTool.js";
-
 	export default {
 		data() {
 			return {
@@ -277,32 +275,32 @@
 			},
 
 			/* 获取BLOB所有试题LIST */
-			async getBlobOrigin() {
-				// 获取初始化Blob需要的数据
-				let sasData = this.isShowSchoolBank ?
-					await this.$tools.getSchoolSas() :
-					await this.$tools.getPrivateSas();
-				//初始化Blob
-				let containerClient = new blobTool(
-					sasData.url,
-					sasData.name,
-					sasData.sas,
-					this.isShowSchoolBank ? "school" : "private"
-				);
-				// 等待blob的返回结果
-				containerClient
-					.listBlob({
-						prefix: "paper",
-					})
-					.then(
-						(res) => {
-							console.log(res);
-						},
-						(err) => {
-							this.$Message.error("API Error");
-						}
-					);
-			},
+			// async getBlobOrigin() {
+			// 	// 获取初始化Blob需要的数据
+			// 	let sasData = this.isShowSchoolBank ?
+			// 		await this.$tools.getSchoolSas() :
+			// 		await this.$tools.getPrivateSas();
+			// 	//初始化Blob
+			// 	let containerClient = new blobTool(
+			// 		sasData.url,
+			// 		sasData.name,
+			// 		sasData.sas,
+			// 		this.isShowSchoolBank ? "school" : "private"
+			// 	);
+			// 	// 等待blob的返回结果
+			// 	containerClient
+			// 		.listBlob({
+			// 			prefix: "paper",
+			// 		})
+			// 		.then(
+			// 			(res) => {
+			// 				console.log(res);
+			// 			},
+			// 			(err) => {
+			// 				this.$Message.error("API Error");
+			// 			}
+			// 		);
+			// },
 
 			/** 执行筛选条件获取数据 */
 			doFilter() {
@@ -338,16 +336,14 @@
 					if(res.items.length){
 						let list = res.items;
 						/* 获取试题总数 */
-						this.totalNum = res.items.length || 0;
-						/* 查找当前页面所有知识点ID换名称 */
-						// this.getPointsByIds(this.getPointIds(list)).then(res => {
-						// 	this.allPointList = res
-						// })
-						// this.exerciseList = list;
+						this.totalNum = res.items.length;
 						this.originData = list;
 						this.pageChange(1);
 					}else{
-						this.$message.warning('获取试题为空')
+						this.originData = []
+						this.exerciseList = []
+						this.totalNum = 0
+						this.$Message.warning('获取试题为空')
 					}
 					setTimeout(() => {
 						that.dataLoading = false;
@@ -642,7 +638,7 @@
 						let deleteIds = [item.id,...item.children.map(i => i.id)]
 						deleteIds.forEach(id => {
 							promiseArr.push(new Promise((r,j) => {
-								this.$api.uploadFile.deletePrefix({
+								this.$api.blob.deletePrefix({
 									"cntr": item.scope === 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId,
 									"prefix": "item/" + id
 								}).then(
@@ -665,7 +661,7 @@
 							reject(err)
 						})
 					}else{
-						this.$api.uploadFile.deletePrefix({
+						this.$api.blob.deletePrefix({
 							"cntr": item.scope === 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId,
 							"prefix": "item/" + item.id
 						}).then(

+ 1 - 2
TEAMModelOS/ClientApp/src/view/evaluation/bank/TestPaperList.vue

@@ -66,7 +66,6 @@
 	</div>
 </template>
 <script>
-	import blobTool from '@/utils/blobTool.js'
 	import Loading from '@/common/Loading.vue'
 	import BaseFilter from '../components/BaseFilter'
 	import BaseImport from '../components/BaseImport'
@@ -263,7 +262,7 @@
 			/* 删除blob指定试题目录下所有 */
 			deleteBlobPrefix(paper) {
 				return new Promise((resolve,reject) => {
-					this.$api.uploadFile.deletePrefix({
+					this.$api.blob.deletePrefix({
 						"cntr": paper.scope === 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId,
 						"prefix": "paper/" + paper.name
 					}).then(

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

@@ -442,7 +442,7 @@
 					editItem.code === this.$store.state.userInfo.TEAMModelId ? 0 : 1;
 				this.exersicesType = editItem.type;
 				this.exerciseField = editItem.field - 1;
-				this.exercisePoints = editItem.points;
+				this.exercisePoints = editItem.knowledge || editItem.points;
 
 				if (editItem.level) {
 					let ac = document

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

@@ -49,7 +49,7 @@
 				<div v-else style="margin-top: 10px">
 					<span v-for="(item, index) in exercisePoints" :key="index" class="exercise-item-point">
 						{{ item }}
-						<span class="exercise-item-point-close">
+						<span class="exercise-item-point-close"> 
 							<Icon type="md-close" @click="onDeletePoint(index)" /></span>
 					</span>
 					<span class="exercise-item-point-modify" @click="selectPointsModal = true">修改</span>
@@ -413,6 +413,7 @@
 							this.$Message.error(e.spaceError);
 						}
 					} else {
+						exerciseItem = await this.$evTools.doAddHost(exerciseItem)
 						// 如果是试卷内编辑试题 则返回编辑好的数据 再统一进行保存
 						this.saveExercise(exerciseItem);
 					}
@@ -537,7 +538,7 @@
 			/* 删除blob指定试题目录下所有 */
 			deleteBlobPrefix(item) {
 				return new Promise((resolve,reject) => {
-					this.$api.uploadFile.deletePrefix({
+					this.$api.blob.deletePrefix({
 						"cntr": item.scope === 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId,
 						"prefix": "item/" + item.id
 					}).then(
@@ -764,7 +765,7 @@
 				this.exerciseScope = editItem.scope === "private" ? 0 : 1;
 				this.exersicesType = editItem.type;
 				this.exerciseField = editItem.field - 1;
-				this.exercisePoints = editItem.points;
+				this.exercisePoints = editItem.knowledge || editItem.points;
 				if (editItem.level) {
 					let ac = document
 						.getElementById(this.refId)

+ 52 - 31
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseExerciseList.vue

@@ -112,13 +112,20 @@
 									<span class="explain-title">【{{$t('evaluation.knowledgePoints')}}】</span>
 									<div class="item-explain-details">
 										<span v-if="!item.knowledge || !(_.compact(item.knowledge).length)">{{$t('evaluation.noPoints')}}</span>
-										<div v-else>
+										<div v-else> 
 											<span v-for="(point,index) in item.knowledge" :key="index" class="item-point-tag">
 												{{ point }}
 											</span>
 										</div>
 									</div>
 								</div>
+								<!-- 认知层次部分 -->
+								<div class="item-explain" v-show="isShowAnswer">
+									<span class="explain-title">【{{$t('evaluation.filter.level')}}】</span>
+									<div class="item-explain-details">
+										{{ item.level ? exersicesField[item.level - 1] : exersicesField[0]}}
+									</div>
+								</div>
 							</div>
 							<!-- 如果是综合题 则加载子题渲染组件 -->
 							<div v-else>
@@ -187,7 +194,7 @@
 		</Modal>
 
 		<!-- 编辑试题弹窗 -->
-		<Modal v-model="editExerciseModal" class-name="edit-exercise-modal" width="1200px" footer-hide :title="$t('evaluation.exerciseList.editExercise')">
+		<Modal v-model="editExerciseModal" class-name="edit-exercise-modal" width="1200px" footer-hide :title="$t('evaluation.exerciseList.editExercise')"  @on-visible-change="editModalChange">
 			<BaseEditExercise :exerciseItem="currentExercise" @onEditSuccess="onEditSuccess" refId="paperEdit" ref="paperEdit"></BaseEditExercise>
 			<div slot="footer">
 				<Button type="success">{{$t('evaluation.confirm')}}</Button>
@@ -221,6 +228,7 @@
 				schoolCode: '',
 				dataLoading: false,
 				curEditItemId: null,
+				curOrderIndex:0,
 				exerciseList: [],
 				schoolInfo: {},
 				isShowUploadList: false,
@@ -364,6 +372,12 @@
 			handleMoveDown(arr, index) {
 				this.moveItems(arr, index, index + 1)
 			},
+			
+			editModalChange(val) {
+				this.$nextTick(() => {
+					this.$refs.paperEdit.backToTop();
+				})
+			},
 
 
 			/**
@@ -395,6 +409,7 @@
 			 */
 			handleToolEdit(arr, item, index) {
 				this.currentExerciseIndex = this.exerciseList.indexOf(item) // 清单列表下的index
+				this.curOrderIndex = index
 				if(item.scope === 'school'){
 					this.getSchoolBaseInfo().then(res => {
 						if (!res) return
@@ -545,30 +560,31 @@
 
 			/** 编辑成功 */
 			onEditSuccess(item) {
-				if (item.id) {
-					this.$refs.paperEdit.isLoading = false
-					this.editExerciseModal = false
-					this.$Message.success("修改成功!")
-					this.exerciseList.splice(this.currentExerciseIndex, 1, item)
-					this.curTypeItems.splice(this.curIndex, 1, item)
-					this.$emit('dataUpdate', this.exerciseList)
-
-					let existIndex = this.modifyItems.map(i => i.id).indexOf(item.id)
-					if (existIndex < 0) {
-						this.modifyItems.push(item)
-					} else {
-						this.modifyItems[existIndex] = item
-					}
+				let preOrderList = JSON.parse(JSON.stringify(this.orderList[0].list))
+				
+				this.$refs.paperEdit.isLoading = false
+				this.editExerciseModal = false
+				this.$Message.success("修改成功!")
+				
+				this.exerciseList.splice(this.currentExerciseIndex, 1, item)
+				this.curTypeItems.splice(this.curIndex, 1, item)
+				this.$emit('dataUpdate', this.exerciseList)
+
+				let existIndex = this.modifyItems.map(i => i.id).indexOf(item.id)
+				if (existIndex < 0) {
+					this.modifyItems.push(item)
 				} else {
-					this.editExerciseModal = false
-					this.exerciseList.splice(this.currentExerciseIndex, 1, item)
-					this.curTypeItems.splice(this.curIndex, 1, item)
-					let listIndex = this.collapseList.indexOf(this.currentExerciseIndex);
-					if (listIndex > -1) {
-						this.collapseList.splice(listIndex, 1)
-					}
-					this.$Message.success("修改成功!")
+					this.modifyItems[existIndex] = item
+				}
+				
+				if(this.viewModel === 'list' && this.orderList.length){
+					preOrderList.splice(this.curOrderIndex, 1, item)
+					console.log(preOrderList[this.curOrderIndex])
+					this.paper.item = preOrderList
 				}
+				
+				
+				
 			},
 
 
@@ -661,22 +677,23 @@
 			paper: {
 				handler(newPaper) {
 					if (newPaper) {
-						console.log('获取的题目信息')
+						console.log('获取的试卷信息')
                         console.log(newPaper)
 						let that = this
-						this.groupList = []
+						this.groupList = [] // 题型排序的数据
+						this.orderList = [] //顺序排列的数据
 						this.exerciseList = []
-						this.orderList = []
 						this.paperInfo = newPaper
-						this.multipleRule = newPaper.multipleRule || 1
+						this.multipleRule = newPaper.multipleRule || 1 // 配分规则
 						if (newPaper.item.length) {
 							newPaper.item.forEach(i => {
 								if (!i.score) i.score = 0
 								// 如果有综合题 则将小题的分数进行累加作为综合题的分数
-								if(i.type === 'compose' && i.children.length){
-									i.score = i.children.reduce((a,b) => a + b.score , 0)
-								}
+								// if(i.type === 'compose' && i.children.length){
+								// 	i.score = i.children.reduce((a,b) => a + b.score , 0)
+								// }
 							})
+							// 给顺序题目排序
 							this.orderList.push({
 								list: newPaper.item
 							})
@@ -695,11 +712,15 @@
 							});
 						}
 						
+						// 重新赋值
 						this.originData = this.exerciseList
 						this.groupTypeList = this.groupList
 						this.totalNum = newPaper.item.length
+						
+						// 剩余可分配分数 更新
 						this.surPlusScore = newPaper.score - newPaper.item.reduce((p, e) => parseInt(p) + parseInt(e.score), 0);
 						this.$emit('scoreUpdate', this.surPlusScore)
+						
 						this.pageScrollTo(0)
 						this.$nextTick(() => {
 							this.$MathJax.MathQueue(this.$refs.mathJaxContainer);

+ 11 - 4
TEAMModelOS/ClientApp/src/view/evaluation/components/BasePointPie.vue

@@ -80,10 +80,17 @@
 			})
 			let typeList = this._.groupBy(tempArr, 'knowledge')
 			for (let key in typeList) {
-				arr.push({
-					value: typeList[key].length,
-					name: key === 'undefined' || !key ?  '未绑定知识点' : key
-				})
+				let newKey = key === 'undefined' || !key ?  '未绑定知识点' : key
+				let isExistIndex = arr.map(i => i.name).indexOf(newKey)
+				if(arr.length && isExistIndex > -1){
+					arr[isExistIndex].value = arr[isExistIndex].value + typeList[key].length
+				}else{
+					arr.push({
+						value: typeList[key].length,
+						name: newKey
+					})
+				}
+				
 			}
 			this.drawLine(arr)
 		},

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

@@ -68,7 +68,7 @@
 			</Select>
 			<p style="margin: 15px 2px;">资源描述</p>
 		    <Input v-model="curRepair.name" placeholder="请输入资源描述..."/>
-			<p style="margin: 15px 2px;">资源链接地址</p>
+			<p style="margin: 15px 2px;">资源链接地址{{ isSiteLink ?  '' : '(输入后回车即可添加成功)'}}</p>
 			
 			<!-- 选择内容 -->
 			<Button type="info" @click="isRelatedContent = true" v-if="isSiteLink">{{$t('evaluation.newExercise.chooseContent')}}</Button>

+ 16 - 8
TEAMModelOS/ClientApp/src/view/evaluation/index/CreateExercises.vue

@@ -377,8 +377,9 @@
 							child.code = exerciseItem.code
 							child.pid = exerciseItem.id
 						})
+						let containerName = exerciseItem.scope === 'private' ? this.$store.state.userInfo.TEAMModelId : this.$store.state.userInfo.schoolCode
 						// 保存完题目返回子题的ID集合 作为cosmos内综合题的children字段
-						exerciseItem.children = await this.saveChildrens(exerciseItem.children)
+						exerciseItem.children = await this.saveChildrens(exerciseItem.children,containerName)
 					}
 					// 将当前的试题数据转化为BLOB内部的试题JSON格式
 					const itemJsonFile = await this.$evTools.createBlobItem(exerciseItem);
@@ -432,8 +433,8 @@
 			},
 
 			/* 保存综合题的子题 */
-			saveChildrens(childrens) {
-				return new Promise((resolve, reject) => {
+			saveChildrens(childrens,containerName) {
+				return new Promise(async (resolve, reject) => {
 					let promiseArr = []
 					childrens.forEach(exerciseItem => {
 						promiseArr.push(new Promise(async (r, j) => {
@@ -455,7 +456,7 @@
 							);
 							try {
 								// 等待上传blob的返回结果
-								let blobFile = await containerClient.upload(file, "item/" + exerciseItem.id);
+								let blobFile = await containerClient.upload(file, "item/" + exerciseItem.id,{},true,false);
 								if (blobFile.blob) {
 									// 保存到COSMOS是不含base64图片编码的数据 避免数据量过大
 									exerciseItem.blob = blobFile.blob;
@@ -463,7 +464,10 @@
 										itemInfo: await this.$evTools.createCosmosItem(exerciseItem),
 										option: "insert",
 									}).then((res) => {
-										r(res.itemInfo)
+										r({
+											cosmosItem:res.itemInfo,
+											blobItem:blobFile
+										})
 									});
 								} else {
 									this.$Message.error(this.$t('evaluation.newExercise.uploadErrorTip'));
@@ -473,11 +477,15 @@
 							}
 						}))
 					})
-
+					
 					Promise.all(promiseArr).then(result => {
-						console.log('子题保存后的返回结果promiseArr', result)
 						if (result.length) {
-							resolve(result.map(i => i.id))
+							// 子题保存之后 统一更新blobSize
+							blobTool.updateSize(result.map(i => i.blobItem),containerName).then(res => {
+								resolve(result.map(i => i.cosmosItem.id))
+							}).catch(err => {
+								this.$Message.error(this.$t('evaluation.newExercise.uploadErrorTip'));
+							})
 						} else {
 							resolve([])
 						}

+ 116 - 41
TEAMModelOS/ClientApp/src/view/evaluation/index/CreatePaper.vue

@@ -252,7 +252,7 @@
 							continue;
 						}
 						/* 如果是导入的试题 没有ID 则试题的信息和当前试卷的学段信息保持一致 */
-						i.id = this.$tools.guid()
+						i.id = i.id || this.$tools.guid()
 						i.code = code
 						i.level = i.level || 3
 						i.field = i.field || 1
@@ -262,7 +262,7 @@
 						i.periodId = periodId
 						if (i.children.length) {
 							i.children.forEach(j => {
-								j.id = this.$tools.guid()
+								j.id = j.id || this.$tools.guid()
 								j.pid = i.id
 								j.scope = i.scope
 								j.code = code
@@ -274,7 +274,8 @@
 							})
 						}
 						// 如果导入的是客观题 则需要检测答案与选项是否为空
-						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.length || !i.answer.length ||
+								!i.option || !i.answer)) {
 							this.errorList.push(i)
 						}
 					}
@@ -365,9 +366,17 @@
 				let that = this
 				console.log('保存的试题列表')
 				console.log(list)
+
+				let containerName = this.isSchool ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId
+				/* 上传前进行试卷blob查询size */
+				// let prefixArr = this.getListPrefixs(list)
+				// let beforeSize = await blobTool.checkPrefixSize(prefixArr, containerName)
+				// console.log('试题上传前的size', beforeSize)
+				// return
 				// 获取初始化Blob需要的数据
 				let sasData = this.isSchool ? await this.$tools.getSchoolSas() : await this.$tools.getPrivateSas()
 				let itemJsonFiles = []
+				let blobFiles = []
 				//初始化Blob
 				let containerClient = new blobTool(sasData.url, sasData.name, sasData.sas, this.evaluationInfo.type)
 				return new Promise(async (resolve, j) => {
@@ -388,8 +397,12 @@
 										// 保存试题的blob链接
 										const itemJsonFile = await this.$evTools.createBlobItem(composeItem)
 										let file = new File([JSON.stringify(itemJsonFile)], composeItem.id + ".json");
+										blobFiles.push(file)
 										itemJsonFiles.push(composeItem)
-										r(composeItem)
+										r({
+											cosmosItem: composeItem,
+											blobItem: itemJsonFile
+										})
 										console.log(composeItem)
 									}))
 								})
@@ -399,8 +412,12 @@
 									// 保存试题的blob链接
 									const itemJsonFile = await this.$evTools.createBlobItem(exerciseItem)
 									let file = new File([JSON.stringify(itemJsonFile)], exerciseItem.id + ".json");
+									blobFiles.push(file)
 									itemJsonFiles.push(exerciseItem)
-									r(exerciseItem)
+									r({
+										cosmosItem: exerciseItem,
+										blobItem: itemJsonFile
+									})
 								}))
 							}
 						} else if (exerciseItem.children.length) { //如果是导入的 综合题
@@ -412,13 +429,15 @@
 										composeItem.children = composeItem.children.map(i => i.id)
 									}
 									// 将当前的试题数据转化为BLOB内部的试题JSON格式
-									const itemJsonFile = await this.$evTools.createBlobItem(composeItem)
+									let removeItem = await this.$editorTools.doRemoveMideaHost(composeItem)
+									const itemJsonFile = await this.$evTools.createBlobItem(removeItem)
 									const cosmosItem = await this.$evTools.createCosmosItem(composeItem)
 									// 首先保存新题目的JSON文件到Blob 然后返回URL链接
 									let file = new File([JSON.stringify(itemJsonFile)], composeItem.id + ".json");
+									blobFiles.push(file)
 									try {
 										// 等待上传blob的返回结果
-										let blobFile = await containerClient.upload(file, 'item/' + composeItem.id, undefined, false)
+										let blobFile = await containerClient.upload(file, 'item/' + composeItem.id, undefined, false, false)
 										if (blobFile.blob) {
 											// 保存试题JSON文件到试卷文件夹需要
 											itemJsonFiles.push(composeItem)
@@ -426,7 +445,10 @@
 											cosmosItem.blob = blobFile.blob
 											// 保存当前试题到数据库
 											that.saveExercise(cosmosItem).then(res => {
-												r(composeItem)
+												r({
+													cosmosItem: cosmosItem,
+													blobItem: blobFile
+												})
 											})
 										} else {
 											j(500)
@@ -441,13 +463,15 @@
 							// 如果没有blob字段 说明试题还没有保存到item目录下 则需要进行保存item文件夹后再保存到paper文件夹(导入试题情况)
 							promiseArr.push(new Promise(async (r, j) => {
 								// 将当前的试题数据转化为BLOB内部的试题JSON格式
-								const itemJsonFile = await this.$evTools.createBlobItem(exerciseItem)
+								let removeItem = await this.$editorTools.doRemoveMideaHost(exerciseItem)
+								const itemJsonFile = await this.$evTools.createBlobItem(removeItem)
 								const cosmosItem = await this.$evTools.createCosmosItem(exerciseItem)
 								// 首先保存新题目的JSON文件到Blob 然后返回URL链接
 								let file = new File([JSON.stringify(itemJsonFile)], exerciseItem.id + ".json");
+								blobFiles.push(file)
 								try {
 									// 等待上传blob的返回结果
-									let blobFile = await containerClient.upload(file, 'item/' + exerciseItem.id, undefined, false)
+									let blobFile = await containerClient.upload(file, 'item/' + exerciseItem.id, undefined, false, false)
 									if (blobFile.blob) {
 										// 保存试题JSON文件到试卷文件夹需要
 										itemJsonFiles.push(exerciseItem)
@@ -455,7 +479,10 @@
 										cosmosItem.blob = blobFile.blob
 										// 保存当前试题到数据库
 										that.saveExercise(cosmosItem).then(res => {
-											r(exerciseItem)
+											r({
+												cosmosItem: exerciseItem,
+												blobItem: blobFile
+											})
 										})
 									} else {
 										j(500)
@@ -468,27 +495,37 @@
 						}
 					}
 					Promise.all(promiseArr).then(result => {
+						let updateSize = this._.sum(blobFiles.map(file => file.size))
 						console.log(result)
-						let slides = []
-						// 主观题的answer为空数组
-						let nullType = ['complete', 'subjective', 'compose', 'correct', 'connector']
-						result.forEach(item => {
-							let o = {
-								url: item.id + '.json',
-								type: item.type,
-								scoring: {
-									score: item.score,
-									knowledge: item.knowledge || [],
-									field: item.field || 1,
-									ans: nullType.includes(item.type) ? [] : item.answer
+						// 子题保存之后 统一更新blobSize
+						blobTool.updateSize([{
+							url: 'item',
+							size: updateSize
+						}], containerName).then(res => {
+							let slides = []
+							// 主观题的answer为空数组
+							let nullType = ['complete', 'subjective', 'compose', 'correct', 'connector']
+							result.map(i => i.cosmosItem).forEach(item => {
+								console.log(item)
+								let o = {
+									url: item.id + '.json',
+									type: item.type,
+									scoring: {
+										score: item.score,
+										knowledge: item.knowledge || [],
+										field: item.field || 1,
+										ans: nullType.includes(item.type) ? [] : item.answer
+									}
 								}
-							}
-							item.type === 'compose' && delete o.scoring
-							slides.push(o)
-						})
-						resolve({
-							slides: slides,
-							files: itemJsonFiles
+								item.type === 'compose' && delete o.scoring
+								slides.push(o)
+							})
+							resolve({
+								slides: slides,
+								files: itemJsonFiles
+							})
+						}).catch(err => {
+							this.$Message.error(this.$t('evaluation.newExercise.uploadErrorTip'));
 						})
 					}).catch(err => {
 						console.log(err)
@@ -497,6 +534,21 @@
 				})
 			},
 
+			/* 获取所有试题Blob前缀 */
+			getListPrefixs(list) {
+				let prefixArr = []
+				list.forEach(item => {
+					if (item.type === 'compose' && item.children.length) {
+						prefixArr.push('item/' + item.id)
+						prefixArr = prefixArr.concat(item.children.map(i => 'item/' + i.id))
+					} else {
+						prefixArr.push('item/' + item.id)
+					}
+				})
+				console.log('试题的前缀集合', prefixArr)
+				return prefixArr
+			},
+
 			// 提取富文本内容中的文本
 			getSimpleText(html) {
 				var r = /<(?!img|video|audio).*?>/g;
@@ -754,6 +806,14 @@
 					let sasData = this.evaluationInfo.type === 'private' ? await this.$tools.getPrivateSas() : await this.$tools.getSchoolSas()
 					//初始化Blob
 					let containerClient = new blobTool(sasData.url, sasData.name, sasData.sas, this.evaluationInfo.type)
+
+					let containerName = paperItem.scope === 'private' ? this.$store.state.userInfo.TEAMModelId : this.$store.state
+						.userInfo.schoolCode
+					/* 上传前进行试卷blob查询size */
+					let beforeSizeArr = await blobTool.checkPrefixSize(['paper/' + paperItem.name], containerName)
+					let beforeSize = this._.sum(beforeSizeArr.map(i => i.size))
+					console.log('试卷上传前的size',beforeSize)
+
 					try {
 						let promiseArr = []
 						let blobFile = null
@@ -774,13 +834,17 @@
 
 								let item = res.files[i]
 								if (item.scope == 'school') {
-									containerClient.copyFolder('paper/' + paperItem.name + '/', 'item/' + item.id, schoolBlob).then(res => { r(200) })
+									containerClient.copyFolder('paper/' + paperItem.name + '/', 'item/' + item.id, schoolBlob, null, false)
+										.then(res => {
+											r(200)
+										})
 								} else {
-									containerClient.copyFolder('paper/' + paperItem.name + '/', 'item/' + item.id, privateBlob).then(res => { r(200) })
+									containerClient.copyFolder('paper/' + paperItem.name + '/', 'item/' + item.id, privateBlob, null,
+										false).then(res => {
+										r(200)
+									})
 								}
 							}))
-
-
 						}
 						promiseArr.push(new Promise(async (r, j) => {
 							try {
@@ -792,11 +856,13 @@
 								this.$Message.error(e.spaceError)
 								this.isLoading = false
 							}
-
 						}))
 						// 进行试卷文件上传Blob
 						Promise.all(promiseArr).then(async result => {
-							console.log('试卷上传文件的promise结果', result)
+							/* 上传前进行试卷blob查询size */
+							let afterSizeArr = await blobTool.checkPrefixSize(['paper/' + paperItem.name], containerName)
+							let afterSize = this._.sum(afterSizeArr.map(i => i.size))
+							console.log('试卷上传后的size', afterSize)
 							if (blobFile.blob) {
 								// 保存到COSMOS是不含base64图片编码的数据 避免数据量过大
 								paperItem.blob = blobFile.blob.split('/index.json')[0]
@@ -811,11 +877,20 @@
 											this.$Message.success(this.isEditPaper ? this.$t('evaluation.paperList.editSuc') : this.$t(
 												'evaluation.paperList.saveSuc'))
 											this.isLoading = false
-											this.$router.push({
-												name: this.evaluationInfo.type === 'private' ? 'personalBank' : 'schoolBank',
-												params: {
-													tabName: 'paper'
-												}
+
+											// 子题保存之后 统一更新blobSize
+											blobTool.updateSize([{
+												url: 'paper/' + paperItem.name,
+												size: afterSize - beforeSize
+											}], containerName).then(res => {
+												this.$router.push({
+													name: this.evaluationInfo.type === 'private' ? 'personalBank' : 'schoolBank',
+													params: {
+														tabName: 'paper'
+													}
+												})
+											}).catch(err => {
+												this.$Message.error(this.$t('evaluation.newExercise.uploadErrorTip'));
 											})
 										} else {
 											this.$Message.error(this.$t('evaluation.paperList.saveFail'))

+ 1 - 1
TEAMModelOS/ClientApp/src/view/evaluation/types/BaseConnector.vue

@@ -1,7 +1,7 @@
 <template>
 	<div>
 		<div class="exersices-content">
-			<IconText :text="$t('evaluation.corrector') + $t('evaluation.newExercise.stem')" :color="'#2d8cf0'" :icon="'ios-create'" style="margin-bottom:15px;"></IconText>
+			<IconText :text="$t('evaluation.connector') + $t('evaluation.newExercise.stem')" :color="'#2d8cf0'" :icon="'ios-create'" style="margin-bottom:15px;"></IconText>
 			<div>
 				<div ref="editor" style="text-align:left"></div>
 			</div>

+ 93 - 79
TEAMModelOS/ClientApp/src/view/homepage/AcCountPie.vue

@@ -2,97 +2,111 @@
     <div id="ac-type-count"></div>
 </template>
 <script>
-    export default {
-        data() {
-            return {
-                index:0,
-                typeCountPie: undefined,
-                option: {
-                    backgroundColor:"#27262b",
-                    legend: {
-                        data: ['评量测验', '自主学习', '作业活动', '投票活动', '问卷调查'],
-                        textStyle: {
-                            color: '#DDDDDD',
-                            padding:[5,0,5,0]
-                        },
-                        icon: 'circle',
-                        bottom: 10,
-                        width: 240
+import elementResizeDetectorMaker from "element-resize-detector"
+export default {
+    data() {
+        return {
+            index: 0,
+            typeCountPie: undefined,
+            option: {
+                backgroundColor: "#27262b",
+                legend: {
+                    data: ['评量测验', '自主学习', '作业活动', '投票活动', '问卷调查'],
+                    textStyle: {
+                        color: '#DDDDDD',
+                        padding: [5, 0, 5, 0]
                     },
-                    series: [
-                        {
+                    icon: 'circle',
+                    bottom: 10
+                },
+                grid: {
+                    left: '20px',
+                    right: '20px',
+                    top:'20px',
+                    bottom:'20px',
+                    containLabel: true
+                },
+                series: [
+                    {
 
-                            name: '数量',
-                            type: 'pie',
-                            top: -60,
-                            radius: [95, 120],
-                            avoidLabelOverlap: false,
-                            legendHoverLink: false,
+                        name: '数量',
+                        type: 'pie',
+                        top: -60,
+                        radius: [95, 120],
+                        avoidLabelOverlap: false,
+                        legendHoverLink: false,
+                        label: {
+                            show: false,
+                            position: 'center',
+                            formatter: function (data) { // 设置圆饼图中间文字排版
+                                return data.value + "\n" + data.name
+                            }
+                        },
+                        emphasis: {
                             label: {
-                                show: false,
-                                position: 'center',
-                                formatter: function (data) { // 设置圆饼图中间文字排版
-                                    return data.value + "\n" + data.name
-                                }
-                            },
-                            emphasis: {
-                                label: {
-                                    show: true,
-                                    fontSize: '24',
-                                    fontWeight: 'bold'
-                                },
+                                show: true,
+                                fontSize: '24',
+                                fontWeight: 'bold'
                             },
-                            labelLine: {
-                                show: false
-                            },
-                            data: [
-                                { value: 335, name: '评量测验',itemStyle:{color:"#00f492"} },
-                                { value: 310, name: '自主学习',itemStyle:{color:"#fa8d38"} },
-                                { value: 234, name: '作业活动',itemStyle:{color:"#f862bb"} },
-                                { value: 135, name: '投票活动',itemStyle:{color:"#65dcda"} },
-                                { value: 1548, name: '问卷调查',itemStyle:{color:"#d6b8ff"} }
-                            ],
-                            itemStyle: {
-                                borderWidth: 5,
-                                borderColor:'#2b2a2f'
-                            }
+                        },
+                        labelLine: {
+                            show: false
+                        },
+                        data: [
+                            { value: 335, name: '评量测验', itemStyle: { color: "#00f492" } },
+                            { value: 310, name: '自主学习', itemStyle: { color: "#fa8d38" } },
+                            { value: 234, name: '作业活动', itemStyle: { color: "#f862bb" } },
+                            { value: 135, name: '投票活动', itemStyle: { color: "#65dcda" } },
+                            { value: 1548, name: '问卷调查', itemStyle: { color: "#d6b8ff" } }
+                        ],
+                        itemStyle: {
+                            borderWidth: 5,
+                            borderColor: '#2b2a2f'
                         }
-                    ]
-                }
+                    }
+                ]
             }
-        },
-        created() {
-
-        },
-        mounted() {
-            this.typeCountPie = this.$echarts.init(document.getElementById('ac-type-count'))
-            this.typeCountPie.setOption(this.option)
-            this.typeCountPie.dispatchAction({ type: 'highlight', seriesIndex: 0, dataIndex: 0 })
-            this.typeCountPie.on('mouseover', (e)=> {
-                this.typeCountPie.dispatchAction({ type: 'downplay', seriesIndex: 0, dataIndex: 0 })
-                if (e.dataIndex != this.index) {
-                    this.typeCountPie.dispatchAction({ type: 'downplay', seriesIndex: 0, dataIndex: this.index })
-                }
-                if (e.dataIndex == 0) {
-                    this.typeCountPie.dispatchAction({ type: 'highlight', seriesIndex: 0, dataIndex: e.dataIndex })
-                }
-            })
+        }
+    },
+    created() {
 
-            //当鼠标离开时,把当前项置为选中 
-            this.typeCountPie.on('mouseout', (e) => {
-                this.index = e.dataIndex
+    },
+    mounted() {
+        this.typeCountPie = this.$echarts.init(document.getElementById('ac-type-count'))
+        this.typeCountPie.setOption(this.option)
+        this.typeCountPie.dispatchAction({ type: 'highlight', seriesIndex: 0, dataIndex: 0 })
+        this.typeCountPie.on('mouseover', (e) => {
+            this.typeCountPie.dispatchAction({ type: 'downplay', seriesIndex: 0, dataIndex: 0 })
+            if (e.dataIndex != this.index) {
+                this.typeCountPie.dispatchAction({ type: 'downplay', seriesIndex: 0, dataIndex: this.index })
+            }
+            if (e.dataIndex == 0) {
                 this.typeCountPie.dispatchAction({ type: 'highlight', seriesIndex: 0, dataIndex: e.dataIndex })
+            }
+        })
+
+        //当鼠标离开时,把当前项置为选中 
+        this.typeCountPie.on('mouseout', (e) => {
+            this.index = e.dataIndex
+            this.typeCountPie.dispatchAction({ type: 'highlight', seriesIndex: 0, dataIndex: e.dataIndex })
+        })
+        this.erd = elementResizeDetectorMaker()
+        this.erd.listenTo(document.getElementById("ac-type-count"), () => {
+            this.$nextTick(() => {
+                //监听到事件后执行的业务逻辑
+                this.typeCountPie.resize()
             })
-        }
+        })
     }
+}
 </script>
 <style scoped lang="less">
-    #ac-type-count {
-        width: 100%;
-        height: 380px;
-        display: flex;
-        justify-content: center;
-    }
+#ac-type-count {
+    width: 100%;
+    height: 360px;
+    display: flex;
+    justify-content: center;
+}
 </style>
 <style>
 </style>

+ 6 - 6
TEAMModelOS/ClientApp/src/view/homepage/HomePage.less

@@ -1,7 +1,7 @@
-@main-bgColor: rgb(40,40,40); //主背景颜
-@borderColor: #424242; //边框颜
-@primary-textColor: #fff; //文本主颜
-@second-textColor: #a5a5a5; //文本副级颜
+@main-bgColor: rgb(40,40,40); //锟斤拷锟斤拷锟斤拷锟斤拷
+@borderColor: #424242; //锟竭匡拷锟斤拷
+@primary-textColor: #fff; //锟侥憋拷锟斤拷锟斤拷
+@second-textColor: #a5a5a5; //锟侥憋拷锟斤拷锟斤拷锟斤拷
 @nothing-textColor: #5f5f5f;
 @primary-fontSize: 14px;
 @second-fontSize: 16px;
@@ -123,7 +123,7 @@
         }
 
         .ac-info-box {
-            width: ~"calc(100% - 170px)";
+            width: ~"calc(100% - 150px)";
 
             p {
                 text-overflow: ellipsis;
@@ -156,7 +156,7 @@
         }
 
         .ac-pro-box {
-            width: 100px;
+            width: 80px;
             text-align: center;
             color: #1cc1f1;
             font-size: 30px;

+ 9 - 10
TEAMModelOS/ClientApp/src/view/homepage/HomePage.vue

@@ -27,19 +27,19 @@
                 </vuescroll>
             </div>
         </div>
-        <div class="class-data-area">
+        <div class="class-data-area" style="display:flex;flex-direction:column;">
             <div class="calss-chart-box box-item">
                 <p class="chart-title">
                     课堂数据
                 </p>
-                <i-circle :percent="80" style="margin:auto;display:block;" trail-color="#242328" :stroke-color="['#1E82E0','#1CC0F3','#1C9AE7']" :trail-width="9" :stroke-width="9" :size="240">
-                    <p class="demo-Circle-inner" style="font-size:100px;font-weight:bold;color:white;">80</p>
+                <!-- <i-circle :percent="80" style="margin:auto;display:block;" trail-color="#242328" :stroke-color="['#1E82E0','#1CC0F3','#1C9AE7']" :trail-width="9" :stroke-width="9" :size="180">
+                    <p class="demo-Circle-inner" style="font-size:50px;font-weight:bold;color:white;">80</p>
                     <span class="demo-Circle-inner" style="font-size:14px;color:#1cc0f3;">平均互动指数</span>
-                    <span class="circle-tag"></span>
-                </i-circle>
+                </i-circle> -->
+                <TechScore></TechScore>
                 <TeachScore></TeachScore>
             </div>
-            <div class="upload-record-box box-item">
+            <div class="upload-record-box box-item" style="flex:1;">
                 <p class="list-title">
                     近期课堂记录
                 </p>
@@ -55,20 +55,19 @@
                                 </p>
                             </div>
                         </div>
-
                     </vuescroll>
                 </div>
             </div>
 
         </div>
-        <div class="activity-area">
+        <div class="activity-area" style="display:flex;flex-direction:column;">
             <div class="ac-count-box box-item">
                 <p class="chart-title">
                     活动概览
                 </p>
                 <AcCountPie style="margin-top:-20px;"></AcCountPie>
             </div>
-            <div class="ac-list-box box-item">
+            <div class="ac-list-box box-item" style="flex:1;">
                 <p class="list-title">
                     进行中活动列表
                 </p>
@@ -351,6 +350,6 @@
     }
 
     .home-page-container .__panel {
-        padding-right: 17px;
+        /* padding-right: 17px; */
     }
 </style>

+ 111 - 101
TEAMModelOS/ClientApp/src/view/homepage/TeachScore.vue

@@ -2,123 +2,133 @@
     <div id="teach-method"></div>
 </template>
 <script>
-    export default {
-        data() {
-            return {
-                teachMethod: undefined,
-                option: {
-                    tooltip: {
-                        trigger: 'axis',
-                        axisPointer: {
-                            type: 'shadow'
-                        }
-                    },
-                    grid: {
-                        left: '3%',
-                        right: '3%',
-                        containLabel: true
+import elementResizeDetectorMaker from "element-resize-detector"
+export default {
+    data() {
+        return {
+            teachMethod: undefined,
+            option: {
+                tooltip: {
+                    trigger: 'axis',
+                    axisPointer: {
+                        type: 'shadow'
+                    }
+                },
+                grid: {
+                    left: '3%',
+                    right: '3%',
+                    top:'20px',
+                    bottom:'20px',
+                    containLabel: true
+                },
+                xAxis: {
+                    type: 'value',
+                    boundaryGap: [0, 0.01],
+                    axisLabel: {
+                        color: 'white',
+                        show: false,
                     },
-                    xAxis: {
-                        type: 'value',
-                        boundaryGap: [0, 0.01],
-                        axisLabel: {
-                            color: 'white',
-                            show: false,
-                        },
-                        axisLine: {
-                            lineStyle: {
-                                color:'#403f44'
-                            }
-                        },
-                        splitLine: {
-                            show: true,
-                            lineStyle: {
-                                color: '#403f44'
-                            },
-                            interval: 2
+                    axisLine: {
+                        lineStyle: {
+                            color: '#403f44'
                         }
                     },
-                    yAxis: {
-                        splitArea: {
-                            show: false,
+                    splitLine: {
+                        show: true,
+                        lineStyle: {
+                            color: '#403f44'
                         },
-                        splitLine: {
-                            show: true,
-                            lineStyle: {
-                                color: '#403f44'
-                            },
-                            interval: 0
-                        },
-                        type: 'category',
-                        data: ['小组学习', '多元评价', '个人学习', '生本决策','全班互动','全班测验'],
-                        axisLabel: {
-                            color:'#DDDDDD'
+                        interval: 2
+                    }
+                },
+                yAxis: {
+                    splitArea: {
+                        show: false,
+                    },
+                    splitLine: {
+                        show: true,
+                        lineStyle: {
+                            color: '#403f44'
                         },
-                        axisLine: {
-                            lineStyle: {
-                                color:'#403f44'
-                            }
-                        }
+                        interval: 0
                     },
-                    series: [
-                        {
-                            type: 'bar',
-                            barWidth: 15,
-                            data: [
-                                {
-                                    value: 20,
-                                    itemStyle: {
-                                        color: '#daba63'
-                                    },
+                    type: 'category',
+                    data: ['小组学习', '多元评价', '个人学习', '生本决策', '全班互动', '全班测验'],
+                    axisLabel: {
+                        color: '#DDDDDD'
+                    },
+                    axisLine: {
+                        lineStyle: {
+                            color: '#403f44'
+                        }
+                    }
+                },
+                series: [
+                    {
+                        type: 'bar',
+                        barWidth: 15,
+                        data: [
+                            {
+                                value: 20,
+                                itemStyle: {
+                                    color: '#daba63'
                                 },
-                                {
-                                    value: 18,
-                                    itemStyle: {
-                                        color: '#b8a0dc'
-                                    }
+                            },
+                            {
+                                value: 18,
+                                itemStyle: {
+                                    color: '#b8a0dc'
+                                }
+                            },
+                            {
+                                value: 5,
+                                itemStyle: {
+                                    color: '#65dcda'
                                 },
-                                {
-                                    value: 5,
-                                    itemStyle: {
-                                        color: '#65dcda'
-                                    },
+                            },
+                            {
+                                value: 1,
+                                itemStyle: {
+                                    color: '#d658a2'
                                 },
-                                {
-                                    value: 1,
-                                    itemStyle: {
-                                        color: '#d658a2'
-                                    },
+                            },
+                            {
+                                value: 1,
+                                itemStyle: {
+                                    color: '#d77c35'
                                 },
-                                {
-                                    value: 1,
-                                    itemStyle: {
-                                        color: '#d77c35'
-                                    },
+                            },
+                            {
+                                value: 1,
+                                itemStyle: {
+                                    color: '#07d280'
                                 },
-                                {
-                                    value: 1,
-                                    itemStyle: {
-                                        color: '#07d280'
-                                    },
-                                }]
-                        }
-                    ]
-                }
+                            }]
+                    }
+                ]
             }
-        },
-        mounted() {
-            this.typeCountPie = this.$echarts.init(document.getElementById('teach-method'))
-            this.typeCountPie.setOption(this.option)
         }
+    },
+    mounted() {
+        this.typeCountPie = this.$echarts.init(document.getElementById('teach-method'))
+        this.typeCountPie.setOption(this.option)
+        this.erd = elementResizeDetectorMaker()
+        this.erd.listenTo(document.getElementById("teach-method"), () => {
+            this.$nextTick(() => {
+                //监听到事件后执行的业务逻辑
+                this.typeCountPie.resize()
+            })
+        })
     }
+}
 </script>
 <style lang="less" scoped>
 </style>
 <style lang="less">
-    #teach-method {
-        width: 100%;
-        height: 280px;
-        display: flex;
-        justify-content: center;
-    }
+#teach-method {
+    width: 100%;
+    height: 200px;
+    display: flex;
+    justify-content: center;
+}
 </style>

+ 86 - 76
TEAMModelOS/ClientApp/src/view/homepage/TechScore.vue

@@ -2,92 +2,102 @@
     <div id="tech-score"></div>
 </template>
 <script>
-    export default {
-        data() {
-            return {
-                techScoreGau: undefined,
-                option: {
-                    title: {
-                        text: 78,
-                        x: 'center',
-                        y: 'center',
-                        textStyle: {
-                            fontWeight: 'bold',
-                            color: '#fff',
-                            fontSize: '80'
-                        }
+import elementResizeDetectorMaker from "element-resize-detector"
+export default {
+    data() {
+        return {
+            techScoreGau: undefined,
+            option: {
+                title: [{
+                    text: '82',
+                    x: '50%',
+                    y: '30%',
+                    textAlign: 'center',
+                    textStyle: {
+                        fontSize: '76',
+                        fontWeight: '600',
+                        color: '#ffffff',
+                        textAlign: 'center',
+                    },
+                }, {
+                    text: '平均互动指数',
+                    left: '50%',
+                    top: '65%',
+                    textAlign: 'center',
+                    textStyle: {
+                        fontSize: '14',
+                        fontWeight: '400',
+                        color: '#0FF',
+                        textAlign: 'center',
                     },
-                    color: ['#242328'],
-                    legend: {
+                }],
+                polar: {
+                    radius: ['90%', '75%'],
+                    center: ['50%', '50%'],
+                },
+                angleAxis: {
+                    max: 100,
+                    show: false,
+                },
+                radiusAxis: {
+                    type: 'category',
+                    show: true,
+                    axisLabel: {
                         show: false,
-                        data: []
                     },
+                    axisLine: {
+                        show: false,
 
-                    series: [{
-                        name: 'Line 1',
-                        type: 'pie',
-                        clockWise: true,
-                        radius: ['75%', '90%'],
-                        itemStyle: {
-                            normal: {
-                                label: {
-                                    show: false
-                                },
-                                labelLine: {
-                                    show: false
-                                }
-                            }
-                        },
-                        hoverAnimation: false,
-                        data: [{
-                            value: 78,
-                            name: '',
-                            itemStyle: {
-                                normal: {
-                                    borderWidth: 10,
-                                    color: { // 完成的圆环的颜色
-                                        colorStops: [{
-                                            offset: 0,
-                                            color: '#1CC0F3' // 0% 处的颜色
-                                        }, {
-                                            offset: 1,
-                                            color: '#1E82E0' // 100% 处的颜色
-                                        }]
-                                    },
-                                    label: {
-                                        show: false
-                                    },
-                                    labelLine: {
-                                        show: false
-                                    },
-                                    borderColor: new this.$echarts.graphic.LinearGradient(0, 0, 0, 1, [{
-                                        offset: 0,
-                                        color: '#1CC0F3',
-                                    }, {
-                                        offset: 1,
-                                        color: '#1E82E0'
-                                    }])
-                                }
-                            }
-                        }, {
-                            name: '',
-                            value: 22
-                        }]
-                    }]
+                    },
+                    axisTick: {
+                        show: false
+                    },
+                },
+                series: [{
+                    name: '',
+                    type: 'bar',
+                    roundCap: true,
+                    barWidth: 60,
+                    showBackground: true,
+                    backgroundStyle: {
+                        color: '#2e2856',
+                    },
+                    data: [80],
+                    coordinateSystem: 'polar',
+                    itemStyle: {
+                        normal: {
+                            color: new this.$echarts.graphic.LinearGradient(0, 1, 0, 0, [{
+                                offset: 0,
+                                color: '#585fe1'
+                            }, {
+                                offset: 1,
+                                color: '#0ff'
+                            }]),
+                        }
+                    }
                 }
+                ]
             }
-        },
-        mounted() {
-            this.techScoreGau = this.$echarts.init(document.getElementById('tech-score'))
-            this.techScoreGau.setOption(this.option)
         }
+    },
+    mounted() {
+        this.techScoreGau = this.$echarts.init(document.getElementById('tech-score'))
+        this.techScoreGau.setOption(this.option)
+        this.erd = elementResizeDetectorMaker()
+        this.erd.listenTo(document.getElementById("tech-score"), () => {
+            this.$nextTick(() => {
+                //监听到事件后执行的业务逻辑
+                this.techScoreGau.resize()
+            })
+        })
     }
+}
 </script>
 <style lang="less" scoped>
-    #tech-score {
-        width:100%;
-        height:260px;
-    }
+#tech-score {
+    width: 100%;
+    height: 240px;
+}
 </style>
 <style>
 </style>

+ 5 - 2
TEAMModelOS/ClientApp/src/view/learnactivity/AutoCreateNew.vue

@@ -7,7 +7,7 @@
 				<div class="filter-content">
 					<CheckboxGroup v-model="filterOrigin">
 						<Checkbox label="private" v-if="!isSchoolPaper" :disabled="filterOrigin.length === 1 && filterOrigin[0] === 'private'">{{$t('evaluation.filter.privateBank')}}</Checkbox>
-						<Checkbox label="school" :disabled="filterOrigin.length === 1 && filterOrigin[0] === 'school'">{{$t('evaluation.filter.schoolBank')}}</Checkbox>
+						<Checkbox label="school" :disabled="filterOrigin.length === 1 && filterOrigin[0] === 'school'" v-if="hasSchool">{{$t('evaluation.filter.schoolBank')}}</Checkbox>
 					</CheckboxGroup>
 				</div>
 			</div>
@@ -304,7 +304,10 @@
 			},
 			isSchoolPaper() {
 				return this.$route.name === 'newSchoolPaper'
-			}
+			},
+			hasSchool() {
+				return this.$store.state.userInfo.hasSchool;
+			},
 		},
 		watch:{
 			period:{

+ 96 - 32
TEAMModelOS/ClientApp/src/view/learnactivity/CreatePrivEva.vue

@@ -18,8 +18,8 @@
                             <FormItem :label="$t('learnActivity.createEv.evName')" prop="name">
                                 <Input v-model="evaluationInfo.name" :placeholder="$t('learnActivity.createEv.evName')"></Input>
                             </FormItem>
-                            <FormItem :label="$t('learnActivity.createEv.evMode')" prop="evaType">
-                                <Select v-model="evaluationInfo.evaType">
+                            <FormItem :label="$t('learnActivity.createEv.evMode')" prop="source">
+                                <Select v-model="evaluationInfo.source">
                                     <Option v-for="(item,index) in $GLOBAL.EV_MODE()" :value="item.value" :key="index">{{ item.label }}</Option>
                                 </Select>
                             </FormItem>
@@ -102,7 +102,7 @@ export default {
     },
     data() {
         return {
-
+            isEdit: false,
             props: {
                 multiple: true,
             },
@@ -123,7 +123,7 @@ export default {
                 'type.id': [
                     { required: true, message: this.$t('learnActivity.createEv.errTips2'), trigger: 'change' }
                 ],
-                evaType: [
+                source: [
                     { required: true, message: this.$t('learnActivity.createEv.errTips3'), trigger: 'change' }
                 ],
                 'type': [
@@ -155,7 +155,7 @@ export default {
                 targetClassIds: [],
                 scope: 'teacher',
                 type: '',  //测试类别
-                evaType: '',
+                source: '',
                 publish: '0',
                 examType: {}, //测试类型
                 startTime: undefined,
@@ -198,6 +198,10 @@ export default {
                             this.privateTree.push(treeItem)
                         })
                         console.log('privateTree', this.privateTree)
+                        //编辑评测需要回显已选对象
+                        if (this.isEdit) {
+
+                        }
                     } else {
                         this.$Message.error('API error!')
                     }
@@ -235,12 +239,31 @@ export default {
                             item.forEach(classItem => {
                                 treeItem.children.push({
                                     label: classItem.name,
-                                    value: classItem.no
+                                    value: classItem.id
                                 })
                             })
                             this.schoolTree.push(treeItem)
                         })
                         console.log('schoolTree', this.schoolTree)
+                        //编辑评测需要回显已选对象
+                        if (this.isEdit) {
+                            this.$set(this.evaluationInfo, 'targets', [])
+                            let defCus = this.schoolTree.find(item => {
+                                if (this.evaluationInfo.subjects.length) {
+                                    return item.value == this.evaluationInfo.subjects[0].id
+                                }
+                            })
+                            if (defCus) {
+                                defCus.children.forEach(item => {
+                                    if (this.evaluationInfo.targetClassIds.indexOf(item.value) > -1) {
+                                        let arr = [defCus.value, item.value]
+                                        this.evaluationInfo.targets.push(arr)
+                                    }
+                                })
+                                this.selectBefore = this._.cloneDeep(this.evaluationInfo.targets)
+                                console.log('设置单选', this.selectBefore)
+                            }
+                        }
                     } else {
                         this.$Message.error('API ERROR!')
                     }
@@ -273,6 +296,7 @@ export default {
                     let newData = data.filter(item => {
                         return item[0] != originCus
                     })
+                    console.log('筛选之后的数据', newData)
                     this.selectBefore = newData
                     this.evaluationInfo.targets = newData
                 }
@@ -413,6 +437,7 @@ export default {
             this.isLoading = true
             let requestData = {
                 pk: 'Exam',
+                id: this.evaluationInfo.id || null,
                 code: this.$store.state.userInfo.TEAMModelId,
                 school: this.$store.state.userInfo.schoolCode,
                 name: this.evaluationInfo.name,
@@ -425,7 +450,7 @@ export default {
                 examType: this.evaluationInfo.examType,
                 year: new Date().getFullYear(),
                 // range: this.mode,
-                source: this.evaluationInfo.evaType,
+                source: this.evaluationInfo.source,
                 targetClassIds: this.evaluationInfo.targetClassIds,
                 publish: this.evaluationInfo.publish,
                 startTime: this.evaluationInfo.publish == 0 ? Math.round(new Date()) : this.evaluationInfo.startTime,
@@ -451,10 +476,23 @@ export default {
                         let schoolBlob = undefined
                         let targetFolder = 'exam/' + examId + '/paper/'
                         let count = 0
-                        requestData.papers.forEach(async item => {
+                        requestData.papers.forEach(async (item,index) => {
+                            if (item.blob.indexOf('/exam/') == 0) {
+                                if (++count == (requestData.papers.length)) {
+                                    setTimeout(() => {
+                                        this.$Message.success(this.$t('learnActivity.createEv.publishOk'))
+                                        this.isLoading = false
+                                        let route = this.mode + 'Evaluation'
+                                        this.$router.push({
+                                            name: route
+                                        })
+                                    }, 2000)
+                                }
+                                return
+                            }
                             if (item.scope == 'school') {
                                 if (!schoolBlob) schoolBlob = new BlobTool(schoolSas.url, schoolSas.name, schoolSas.sas, 'school')
-                                privateBlob.copyFolder(targetFolder + item.id + '/', item.blob.substring(1), schoolBlob).then().finally(
+                                privateBlob.copyFolder(targetFolder + this.evaluationInfo.subjects[index].id + '/', item.blob.substring(1), schoolBlob).then().finally(
                                     () => {
                                         if (++count == requestData.papers.length) {
                                             setTimeout(() => {
@@ -470,7 +508,7 @@ export default {
                                 )
                             } else if (item.scope == 'private') {
                                 if (!privateBlob) privateBlob = new BlobTool(privateSas.url, privateSas.name, privateSas.sas, 'private')
-                                privateBlob.copyFolder(targetFolder + item.id + '/', item.blob.substring(1)).then().finally(
+                                privateBlob.copyFolder(targetFolder + this.evaluationInfo.subjects[index].id + '/', item.blob.substring(1)).then().finally(
                                     () => {
                                         if (++count == requestData.papers.length) {
                                             setTimeout(() => {
@@ -505,26 +543,30 @@ export default {
             if (data) {
                 let paperDto = []
                 for (let i = 0; i < data.length; i++) {
-                    let paper = {}
-                    paper.id = data[i].id
-                    paper.code = data[i].code
-                    paper.name = data[i].name
-                    paper.blob = data[i].blob
-                    paper.scope = data[i].scope
-                    paper.multipleRule = rule[i].multipleRule
-                    paper.answers = []
-                    paper.point = []
-                    paper.knowledge = []
-                    paper.field = []
-                    for (let k = 0; k < rule[i].slides.length; k++) {
-                        if (rule[i].slides[k].type !== 'compose') {
-                            paper.answers.push(rule[i].slides[k].scoring.ans)
-                            paper.point.push(rule[i].slides[k].scoring.score)
-                            paper.knowledge.push(rule[i].slides[k].scoring.knowledge ? rule[i].slides[k].scoring.knowledge : [])
-                            if (rule[i].slides[k].scoring.field) paper.field.push(rule[i].slides[k].scoring.field)
+                    if (data[i].blob.indexOf('/exam/') == 0) {
+                        paperDto.push(data[i])
+                    } else {
+                        let paper = {}
+                        paper.id = data[i].id
+                        paper.code = data[i].code
+                        paper.name = data[i].name
+                        paper.blob = data[i].blob
+                        paper.scope = data[i].scope
+                        paper.multipleRule = rule[i].multipleRule
+                        paper.answers = []
+                        paper.point = []
+                        paper.knowledge = []
+                        paper.field = []
+                        for (let k = 0; k < rule[i].slides.length; k++) {
+                            if (rule[i].slides[k].type !== 'compose') {
+                                paper.answers.push(rule[i].slides[k].scoring.ans)
+                                paper.point.push(rule[i].slides[k].scoring.score)
+                                paper.knowledge.push(rule[i].slides[k].scoring.knowledge ? rule[i].slides[k].scoring.knowledge : [])
+                                if (rule[i].slides[k].scoring.field) paper.field.push(rule[i].slides[k].scoring.field)
+                            }
                         }
+                        paperDto.push(paper)
                     }
-                    paperDto.push(paper)
                 }
                 return paperDto
             } else {
@@ -533,6 +575,12 @@ export default {
         }
     },
     created() {
+        // 处理默认时间
+        this.startTime = new Date()
+        this.evaluationInfo.startTime = new Date()
+        this.endTime = new Date(new Date(new Date().toLocaleDateString()).getTime() + 2 * 24 * 60 * 60 * 1000 - 1)
+        this.evaluationInfo.endTime = new Date(new Date().toLocaleDateString()).getTime() + 2 * 24 * 60 * 60 * 1000 - 1
+
         this.$store.dispatch('user/getSchoolProfile').then(
             res => {
                 this.schoolBase = res.school_base
@@ -545,15 +593,31 @@ export default {
         } else {
             this.mode = 'private'
         }
+
+        //编辑评测逻辑
+        let routerData = this.$route.params.evaluationInfo
+        if (routerData !== undefined) {
+            this.isEdit = true
+            console.log(routerData)
+            this.startTime = new Date(routerData.startTime)
+            this.endTime = new Date(routerData.endTime)
+            routerData.paperInfo = []
+            for (let i = 0; i < routerData.papers.length; i++) {
+                let paper = this._.cloneDeep(routerData.papers[i])
+                routerData.paperInfo.push(paper)
+                routerData.paperInfo[i].subjectId = routerData.subjects[i].id
+                routerData.paperInfo[i].subjectName = routerData.subjects[i].name
+            }
+            this.evaluationInfo = routerData
+            this.activeTab = 'preview'
+        }
+
         this.getCourseList()
         this.getPrivateCus()
 
     },
     mounted() {
-        this.startTime = new Date()
-        this.evaluationInfo.startTime = new Date()
-        this.endTime = new Date(new Date(new Date().toLocaleDateString()).getTime() + 2 * 24 * 60 * 60 * 1000 - 1)
-        this.evaluationInfo.endTime = new Date(new Date().toLocaleDateString()).getTime() + 2 * 24 * 60 * 60 * 1000 - 1
+
     }
 }
 </script>

+ 118 - 103
TEAMModelOS/ClientApp/src/view/learnactivity/CreateSchoolEva.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="create-school-container">
         <div class="create-header">
-            <p class="create-header-title" @click="consData">{{$t('learnActivity.createEv.createLabel')}}</p>
+            <p class="create-header-title">{{$t('learnActivity.createEv.createLabel')}}</p>
             <Button class="btn-save" type="text" :loading="isLoading" ghost icon="ios-albums-outline" @click="saveEvaluation" style="margin-right:30px;">
                 {{$t('learnActivity.createEv.publishEv')}}
             </Button>
@@ -40,7 +40,9 @@
                             </FormItem>
                             <!-- 校园评测施测对象为系统班级 -->
                             <FormItem :label="$t('learnActivity.createEv.evTarget')" prop="targets">
-                                <el-cascader size="small" :show-all-levels="false" clearable filterable v-model="evaluationInfo.targets" :options="curGrades" :props="props" @change="treeChange" style="width:100%;">
+                                <!-- <el-cascader ref="elCs" size="small" :show-all-levels="false" clearable filterable v-model="evaluationInfo.targets" :options="curGrades" :props="props" @change="treeChange" style="width:100%;">
+                                </el-cascader> -->
+                                <el-cascader size="small" :show-all-levels="false" clearable filterable v-model="evaluationInfo.targets" :options="csOptions" :props="props" @change="treeChange" style="width:100%;">
                                 </el-cascader>
                             </FormItem>
                             <FormItem :label="$t('learnActivity.createEv.publishType')" prop="publish">
@@ -121,58 +123,12 @@ export default {
         ManualPaper,
     },
     data() {
-        const _this = this
         return {
+            schoolClasses: [],
             props: {
                 multiple: true,
                 value: 'id',
-                label: 'name',
-                lazy: true,
-                //动态获取当前年级下面的班级数据
-                lazyLoad: function (node, resolve) {
-                    let level = -1
-                    if (node) {
-                        level = node.level
-                    }
-                    switch (level) {
-                        case 0:
-                            if (_this.evaluationInfo.period) {
-                                _this.$store.dispatch('user/getSchoolProfile').then(
-                                    res => {
-                                        let f = res.school_base.period.filter((item) => {
-                                            return item.id == _this.evaluationInfo.period
-                                        })
-                                        if (f.length > 0) {
-                                            resolve(f[0].grades)
-                                        } else {
-                                            resolve([])
-                                        }
-                                    }
-                                )
-                            } else {
-                                resolve([])
-                            }
-                            break
-                        case 1:
-                            _this.$store.dispatch('user/getSchoolProfile').then(
-                                res => {
-                                    let f = res.school_classes.filter((item) => {
-                                        return item.gradeId == node.data.id
-                                    })
-                                    f.forEach(item => {
-                                        item.leaf = true
-                                    })
-                                    resolve(f)
-                                }
-                            )
-                            break
-                        case 2:
-                            resolve([])
-                            break
-                        default:
-                            break
-                    }
-                }
+                label: 'name'
             },
             schoolBase: {
                 period: []
@@ -204,12 +160,9 @@ export default {
                 'examType.id': [
                     { required: true, message: this.$t('learnActivity.createEv.errTips5'), trigger: 'change' }
                 ],
-                targets: [
-                    { required: true, message: this.$t('learnActivity.createEv.errTips6'), type: 'array', trigger: 'change' }
-                ],
-                targetClassIds: [
-                    { required: true, message: this.$t('learnActivity.createEv.errTips6'), type: 'array', trigger: 'change' }
-                ],
+                // targets: [
+                //     { required: true, message: this.$t('learnActivity.createEv.errTips6'), trigger: 'change' }
+                // ],
                 publish: [
                     { required: true, message: this.$t('learnActivity.createEv.errTips7'), trigger: 'change' }
                 ],
@@ -400,6 +353,7 @@ export default {
                         }
                     }
                     this.evaluationInfo.paperInfo.splice(index, 1)
+                    this.evaluationInfo.papers.splice(index, 1)
                     this.curSubIndex = 0
                     this.$Message.success(this.$t('learnActivity.createEv.delOk'))
                 },
@@ -525,12 +479,10 @@ export default {
                 )
             }
         },
-        consData() {
-            console.log('evaluationInfo', this.evaluationInfo)
-        },
         async saveEvaluation() {
             //表单验证
             let flag = true
+            console.log(this.evaluationInfo)
             this.$refs['evaForm'].validate((valid) => {
                 if (!valid) {
                     this.$Message.warning(this.$t('learnActivity.createEv.formWarning'))
@@ -567,6 +519,7 @@ export default {
             this.isLoading = true
             let requestData = {
                 pk: 'Exam',
+                id: this.evaluationInfo.id || null,
                 code: this.$store.state.userInfo.schoolCode,
                 school: this.$store.state.userInfo.schoolCode,
                 name: this.evaluationInfo.name,
@@ -604,9 +557,22 @@ export default {
                         let privateBlob = undefined
                         let targetFolder = 'exam/' + examId + '/paper/'
                         let count = 0
-                        requestData.papers.forEach(async item => {
+                        requestData.papers.forEach(async (item,index) => {
+                            if (item.blob.indexOf('/exam/') == 0) {
+                                if (++count == (requestData.papers.length)) {
+                                    setTimeout(() => {
+                                        this.$Message.success(this.$t('learnActivity.createEv.publishOk'))
+                                        this.isLoading = false
+                                        let route = this.mode + 'Evaluation'
+                                        this.$router.push({
+                                            name: route
+                                        })
+                                    }, 2000)
+                                }
+                                return
+                            }
                             if (item.scope == 'school') {
-                                schoolBlob.copyFolder(targetFolder + item.id + '/', item.blob.substring(1)).then().finally(
+                                schoolBlob.copyFolder(targetFolder + requestData.subjects[index].id + '/', item.blob.substring(1)).then().finally(
                                     () => {
                                         if (++count == (requestData.papers.length)) {
                                             setTimeout(() => {
@@ -622,7 +588,7 @@ export default {
                                 )
                             } else if (item.scope == 'private') {
                                 if (!privateBlob) privateBlob = new BlobTool(privateSas.url, privateSas.name, privateSas.sas, 'private')
-                                schoolBlob.copyFolder(targetFolder + item.id + '/', item.blob.substring(1), privateBlob).then().finally(
+                                schoolBlob.copyFolder(targetFolder + requestData.subjects[index].id + '/', item.blob.substring(1), privateBlob).then().finally(
                                     () => {
                                         if (++count == (requestData.papers.length)) {
                                             setTimeout(() => {
@@ -638,14 +604,6 @@ export default {
                                 )
                             }
                         })
-                        // setTimeout(() => {
-                        //     this.$Message.success(this.$t('learnActivity.createEv.publishOk'))
-                        //     this.isLoading = false
-                        //     let route = this.mode + 'Evaluation'
-                        //     this.$router.push({
-                        //         name: route
-                        //     })
-                        // }, 2000)
                     } else {
                         this.$Message.error('API ERROR!')
                         this.isLoading = false
@@ -664,26 +622,31 @@ export default {
             if (data) {
                 let paperDto = []
                 for (let i = 0; i < data.length; i++) {
-                    let paper = {}
-                    paper.id = data[i].id
-                    paper.code = data[i].code
-                    paper.name = data[i].name
-                    paper.blob = data[i].blob
-                    paper.scope = data[i].scope
-                    paper.multipleRule = rule[i].multipleRule
-                    paper.answers = []
-                    paper.point = []
-                    paper.knowledge = []
-                    paper.field = []
-                    for (let k = 0; k < rule[i].slides.length; k++) {
-                        if (rule[i].slides[k].type !== 'compose') {
-                            paper.answers.push(rule[i].slides[k].scoring.ans)
-                            paper.point.push(rule[i].slides[k].scoring.score)
-                            paper.knowledge.push(rule[i].slides[k].scoring.knowledge ? rule[i].slides[k].scoring.knowledge : [])
-                            if (rule[i].slides[k].scoring.field) paper.field.push(rule[i].slides[k].scoring.field)
+                    console.log(data[i])
+                    if (data[i].blob.indexOf('/exam/') == 0) {
+                        paperDto.push(data[i])
+                    } else {
+                        let paper = {}
+                        paper.id = data[i].id
+                        paper.code = data[i].code
+                        paper.name = data[i].name
+                        paper.blob = data[i].blob
+                        paper.scope = data[i].scope
+                        paper.multipleRule = rule[i].multipleRule
+                        paper.answers = []
+                        paper.point = []
+                        paper.knowledge = []
+                        paper.field = []
+                        for (let k = 0; k < rule[i].slides.length; k++) {
+                            if (rule[i].slides[k].type !== 'compose') {
+                                paper.answers.push(rule[i].slides[k].scoring.ans)
+                                paper.point.push(rule[i].slides[k].scoring.score)
+                                paper.knowledge.push(rule[i].slides[k].scoring.knowledge ? rule[i].slides[k].scoring.knowledge : [])
+                                if (rule[i].slides[k].scoring.field) paper.field.push(rule[i].slides[k].scoring.field)
+                            }
                         }
+                        paperDto.push(paper)
                     }
-                    paperDto.push(paper)
                 }
                 return paperDto
             } else {
@@ -692,9 +655,16 @@ export default {
         }
     },
     created() {
+        // 处理默认时间
+        this.startTime = new Date()
+        this.evaluationInfo.startTime = new Date()
+        this.endTime = new Date(new Date(new Date().toLocaleDateString()).getTime() + 2 * 24 * 60 * 60 * 1000 - 1)
+        this.evaluationInfo.endTime = new Date(new Date().toLocaleDateString()).getTime() + 2 * 24 * 60 * 60 * 1000 - 1
+
         this.$store.dispatch('user/getSchoolProfile').then(
             res => {
                 this.schoolBase = res.school_base
+                this.schoolClasses = res.school_classes
             }
         )
         //判断创建个人还是校本评测
@@ -702,28 +672,73 @@ export default {
             this.mode = 'school'
         }
 
-        //编辑评测逻辑(暂时注释)
+        //编辑评测逻辑
         let routerData = this.$route.params.evaluationInfo
-        console.log(routerData)
         if (routerData !== undefined) {
-           routerData.startTime = new Date(routerData.startTime)
-           routerData.endTime = new Date(routerData.endTime)
-           routerData.paperInfo = []
-        //    for (let i = 0; i < routerData.paperInfo.length; i++) {
-        //        routerData.paperInfo[i].createType = 'manualPaper'
-        //    }
-           this.evaluationInfo = routerData
-           this.activeTab = 'preview'
-           this.$Message.warning('编辑评测功能尚未完善')
+            this.startTime = new Date(routerData.startTime)
+            this.endTime = new Date(routerData.endTime)
+            routerData.subjectIds = routerData.subjects.map(item=>{
+                return item.id
+            })
+            routerData.paperInfo = []
+            for (let i = 0; i < routerData.papers.length; i++) {
+                let paper = this._.cloneDeep(routerData.papers[i])
+                routerData.paperInfo.push(paper)
+                routerData.paperInfo[i].createType = 'manualPaper'
+                routerData.paperInfo[i].subjectId = routerData.subjects[i].id
+                routerData.paperInfo[i].subjectName = routerData.subjects[i].name
+            }
+            this.evaluationInfo = routerData
+            this.$store.dispatch('user/getSchoolProfile').then(
+                res => {
+                    let f = res.school_classes.filter((item) => {
+                        return routerData.targetClassIds.indexOf(item.id) > -1
+                    })
+                    this.evaluationInfo.targets = []
+                    f.forEach((item, index) => {
+                        let arr = [
+                            item.gradeId,
+                            item.id
+                        ]
+                        this.evaluationInfo.targets.push(arr)
+                    })
+                }
+            )
+            this.activeTab = 'preview'
+            this.$Message.warning('编辑评测功能尚未完善')
         }
     },
     mounted() {
-        this.startTime = new Date()
-        this.evaluationInfo.startTime = new Date()
-        this.endTime = new Date(new Date(new Date().toLocaleDateString()).getTime() + 2 * 24 * 60 * 60 * 1000 - 1)
-        this.evaluationInfo.endTime = new Date(new Date().toLocaleDateString()).getTime() + 2 * 24 * 60 * 60 * 1000 - 1
+
     },
     computed: {
+        //级联选择年级班级
+        csOptions() {
+            let data = []
+            if (this.evaluationInfo.period.id && this.schoolBase.period.length && this.schoolClasses.length) {
+                let curPd = this.schoolBase.period.filter((item) => {
+                    return item.id == this.evaluationInfo.period.id
+                })
+                console.log(curPd)
+                if (curPd && curPd.length) {
+                    curPd[0].grades.forEach((item, index) => {
+                        let dataItem = {
+                            id: item.id,
+                            name: item.name,
+                            children: []
+                        }
+                        let child = this.schoolClasses.filter(classItem => {
+                            return classItem.gradeId == item.id
+                        })
+                        console.log('child', child)
+                        dataItem.children = child
+                        data.push(dataItem)
+                    })
+                }
+            }
+            console.log('数据', data)
+            return data
+        },
         //计算当前学段下面的年级信息
         curGrades() {
             if (this.evaluationInfo.period) {

+ 9 - 8
TEAMModelOS/ClientApp/src/view/learnactivity/ManualCreate.vue

@@ -349,7 +349,7 @@
 						if (schoolBaseInfo) {
 							r(schoolBaseInfo)
 						} else {
-							r(null)
+							r('noSchool')
 						}
 					});
 				})
@@ -369,15 +369,16 @@
 			this.manualFilter.code = this.isSchool ? [this.$store.state.userInfo.schoolCode] : [this.$store.state.userInfo.TEAMModelId]
 			
 			this.getSchoolBaseInfo().then(res => {
-				if (!res) return
-				this.schoolPeriod = res.period
-				if(this.isSchool && this.periodCode && this.subjectCode){
-					this.manualFilter.periodCode = [this.periodCode]
-					this.subjectList = this.schoolPeriod.filter(i => i.id === this.periodCode)[0].subjects
-					this.manualFilter.subjectCode = [this.subjectCode]
+				if (res === 'noSchool'){
 					this.queryQuestionByPage()
 				}else{
-					this.queryQuestionByPage()
+					this.schoolPeriod = res.period
+					if(this.isSchool && this.periodCode && this.subjectCode){
+						this.manualFilter.periodCode = [this.periodCode]
+						this.subjectList = this.schoolPeriod.filter(i => i.id === this.periodCode)[0].subjects
+						this.manualFilter.subjectCode = [this.subjectCode]
+						this.queryQuestionByPage()
+					}
 				}
 			})
 			

+ 10 - 11
TEAMModelOS/ClientApp/src/view/learnactivity/MgtPrivEva.vue

@@ -8,7 +8,7 @@
                     <span>{{$t('learnActivity.mgtScEv.listLabel')}}</span>
                     <Icon type="md-add" class=" to-create-icon" @click="goToCreate" :title="$t('learnActivity.mgtScEv.create')" />
                     <Icon type="md-trash" v-show="evaListShow.length" class="to-create-icon" :title="$t('learnActivity.mgtScEv.delete')" @click="deleteEvaluation" />
-                    <Icon type="md-create" v-show="evaListShow.length" class="to-create-icon" @click="editEvaluation" :title="$t('learnActivity.mgtScEv.edit')" />
+                    <Icon type="md-create" v-show="evaListShow.length && evaListShow[curEvaIndex].progress == 'pending'" class="to-create-icon" @click="editEvaluation" :title="$t('learnActivity.mgtScEv.edit')" />
                 </div>
                 <div class="evaluation-list-main">
                     <vuescroll>
@@ -55,14 +55,14 @@
                         {{$t('learnActivity.mgtScEv.tab2')}}
                     </span>
                     <!-- <div style="float:right;" v-show="evaListShow[curEvaIndex] && evaListShow[curEvaIndex].progress == 'going'"> -->
-                    <div style="float:right;">
+                    <!-- <div style="float:right;">
                         <Tooltip :content="$t('learnActivity.mgtScEv.autoTips1')" :max-width="240">
                             <Button type="success" size="small" :loading="answerLoading" class="mock-stu-answer" @click="mockAnswer">{{$t('learnActivity.mgtScEv.autoAnswer')}}</Button>
                         </Tooltip>
                         <Tooltip :content="$t('learnActivity.mgtScEv.autoTips2')" :max-width="240">
                             <Button type="warning" size="small" :loading="scoreLoading" class="mock-tea-scoring" @click="mockScoring">{{$t('learnActivity.mgtScEv.autoScore')}}</Button>
                         </Tooltip>
-                    </div>
+                    </div> -->
                 </div>
                 <!--试卷信息-->
                 <div :class="curBarIndex == 1 ? 'animated fadeIn evaluation-base-info':'evaluation-base-info animated fadeOutRight'" v-show="curBarIndex == 1">
@@ -284,19 +284,15 @@ export default {
                 this.$Message.warning(this.$t('learnActivity.mgtScEv.noJoin'))
             }
         },
-        confirmEdit() {
-            let evaluationInfo = this.evaluationList[this.curEvaIndex]
-            evaluationInfo.testPaper = this.examDetaiInfo
+        editEvaluation() {
+            this.$Message.warning('暂未处理编辑评测功能!')
             this.$router.push({
-                name: 'createEvaluation',
+                name: 'createPrivEva',
                 params: {
-                    evaluationInfo
+                    evaluationInfo: this.examDetaiInfo
                 }
             })
         },
-        editEvaluation() {
-            this.$Message.warning('暂未处理编辑评测功能!')
-        },
         selectSubject(index) {
             this.curSubIndex = index
         },
@@ -359,9 +355,12 @@ export default {
                                 return total + parseInt(item)
                             }, 0)
                             if (resData.papers[index].blob) {
+                                let blob = resData.papers[index].blob
                                 resData.papers[index].scope = resData.scope
                                 resData.papers[index].examId = resData.id
                                 resData.papers[index] = await this.$evTools.getFullPaper(resData.papers[index])
+                                resData.papers[index].blob = blob
+                                resData.score += resData.papers[index].score
                             }
                         }
                         this.evaListShow.splice(this.curEvaIndex, 1, resData)

+ 5 - 1
TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.vue

@@ -397,10 +397,14 @@ export default {
                         let resData = res.examInfo[0]
                         resData.score = 0
                         for (let index in resData.papers) {
+                            let blob = resData.papers[index].blob
                             resData.papers[index].scope = resData.scope
                             resData.papers[index].examId = resData.id
                             resData.papers[index] = await this.$evTools.getFullPaper(resData.papers[index])
-                            console.log('试卷',resData.papers[index])
+                            if(!resData.papers[index].subjectId){
+                                resData.papers[index].subjectId = blob.substring(blob.lastIndexOf('/')+1)
+                            }
+                            resData.papers[index].blob = blob
                             resData.score += resData.papers[index].score
                         }
                         this.evaListShow.splice(this.curEvaIndex, 1, resData)

+ 2 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/PaperScore.less

@@ -204,6 +204,7 @@
     position: sticky;
     top: 44px;
     z-index: 9999;
+    overflow: auto;
 }
 .question-index-box{
     line-height: 26px;
@@ -211,6 +212,7 @@
     background: white;
     width: 100%;
     display: flex;
+    margin-top: 10px;
 }
 .base-info-btn {
     margin-left: 5px;

+ 1 - 4
TEAMModelOS/ClientApp/src/view/learnactivity/PaperScore.vue

@@ -394,7 +394,6 @@ export default {
         markStuAnswer(index) {
             this.curAnIndex = index
             let answerIframe = document.getElementById('answerIframe')
-            let markBody = answerIframe.contentWindow.document.body
             answerIframe.onload = () => {
                 answerIframe.style.width = '850px'
                 answerIframe.contentWindow.document.body.style.margin = '0px 20px'
@@ -403,14 +402,12 @@ export default {
                 answerIframe.contentWindow.document.body.style.minHeight = '240px'
                 answerIframe.contentWindow.document.body.style.height = 'fit-content'
                 answerIframe.contentWindow.document.body.style.width = 'fit-content'
-
                 let bodyWidth = answerIframe.contentWindow.document.body.clientWidth
                 answerIframe.style.width = (bodyWidth + 20) + 'px'
                 answerIframe.contentWindow.document.body.style.backgroundColor = '#f5f5f5'
                 html2canvas(answerIframe.contentWindow.document.body, {
                     onclone: (data) => {
-                    },
-                    // removeContainer: false
+                    }
                 }).then((canvas) => {
                     canvas.id = "canvas" + index
                     this.markBg = canvas.toDataURL()

+ 37 - 5
TEAMModelOS/ClientApp/src/view/learnactivity/Scoring.vue

@@ -208,9 +208,9 @@ export default {
         getCurPaper() {
             let paperInfo = this.examInfo.papers.find((item) => {
                 return item.subjectId == this.chooseSubject;
-            });
+            })
             this.paperInfo = this._.cloneDeep(paperInfo)
-            this.getClassStudent();
+            this.getClassStudent()
         },
         //点击学生题号前往评分页面
         getStuScore(data, qIndex) {
@@ -264,6 +264,7 @@ export default {
             };
             this.$api.schoolSetting.getClassroomStudent(requestData).then((res) => {
                 if (!res.error) {
+                    console.log('paper',this.paperInfo)
                     if (!this.paperInfo[this.chooseClass]) {
                         this.paperInfo[this.chooseClass] = {}
                     }
@@ -271,7 +272,37 @@ export default {
                     this.students = []
                     this.tableData = []
                     this.studentScore = []
-                    this.getStudentAnswer();
+                    this.tableColumn = [...this.scoreList]
+                    let defSocre = []
+                    if (this.examInfo.progress == 'pending') {//如果评测未发布,没有学生数据,则直接渲染表格
+                        this.quCount = this.paperInfo.item ? this.paperInfo.item.length : 0
+                        for (let i = 0; i < this.quCount; i++) {
+                            let data = {
+                                title: "Q" + (i + 1),
+                                slot: "qu" + i,
+                                align: "center",
+                                minWidth: 65,
+                            }
+                            this.tableColumn.push(data)
+                            defSocre.push(-1)
+                        }
+                        let classStu = this.paperInfo[this.chooseClass].students.students
+                        console.log('classStu',classStu)
+                        for (let k = 0; k < classStu.length; k++) {
+                            let score = {}
+                            score.name = classStu[k].name
+                            score.id = classStu[k].id
+                            score.data = defSocre
+                            score.total = 0
+                            score.status = 1
+                            this.studentScore.push(score)
+                        }
+                        this.pageChange(1)
+                        this.tableLoading = false
+                    } else {//如果获取进行中或已结束则需要拉取学生数据
+                        this.getStudentAnswer()
+                    }
+
                 } else {
                     this.$Message.error("API ERROR!");
                 }
@@ -317,7 +348,6 @@ export default {
                         if (res.examClassResults[0]) {
                             this.calcOverView(res.examClassResults[0])
                         }
-
                     }
                 },
                 (err) => {
@@ -337,7 +367,8 @@ export default {
                 let studentAns = this.paperInfo[this.chooseClass]["studentAns"]
                 this.studentScore = []
                 this.tableColumn = [...this.scoreList]
-                this.quCount = studentAns.studentScores[0] ? studentAns.studentScores[0].length : 0
+                // this.quCount = studentAns.studentScores[0] ? studentAns.studentScores[0].length : 0
+                this.quCount = this.paperInfo.item ? this.paperInfo.item.length : 0
                 for (let i = 0; i < this.quCount; i++) {
                     let data = {
                         title: "Q" + (i + 1),
@@ -449,6 +480,7 @@ export default {
                 if (n.grades && n.grades.length) {
                     this.chooseGrade = n.grades[0].id;
                 }
+                console.log('新的Paper',n.papers)
                 if (n.papers && n.papers.length) {
                     if (n.scope == 'school') {
                         let res = n.papers.find((item) => {

+ 0 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/SimpleAnalysis.vue


Some files were not shown because too many files changed in this diff