Преглед изворни кода

Merge remote-tracking branch 'origin/develop3.0-tmd' into develop4.0-tmd

liqk пре 4 година
родитељ
комит
35ffc34eba
70 измењених фајлова са 1865 додато и 1113 уклоњено
  1. 2 2
      TEAMModelFunction/MonitorCosmosDB.cs
  2. 1 1
      TEAMModelFunction/MonitorServicesBus.cs
  3. 2 2
      TEAMModelFunction/TEAMModelFunction.csproj
  4. 2 2
      TEAMModelFunction/TriggerExam.cs
  5. 30 9
      TEAMModelFunction/TriggerSurvey.cs
  6. 12 9
      TEAMModelFunction/TriggerVote.cs
  7. 17 0
      TEAMModelOS.SDK/DI/AzureStorage/AzureStorageBlobExtensions.cs
  8. 3 1
      TEAMModelOS.SDK/DI/DingDing/DingDing.cs
  9. 16 0
      TEAMModelOS.SDK/Models/Cosmos/Common/Inner/SurveyRecord.cs
  10. 1 1
      TEAMModelOS.SDK/Models/Cosmos/Common/Inner/VoteRecord.cs
  11. 27 85
      TEAMModelOS.SDK/Models/Cosmos/Common/Survey.cs
  12. 1 1
      TEAMModelOS.SDK/Models/Cosmos/Common/Vote.cs
  13. 5 4
      TEAMModelOS.SDK/TEAMModelOS.SDK.csproj
  14. 8 3
      TEAMModelOS/ClientApp/src/api/http.js
  15. 2 2
      TEAMModelOS/ClientApp/src/api/questionnaire.js
  16. 12 1
      TEAMModelOS/ClientApp/src/api/studentWeb.js
  17. 21 2
      TEAMModelOS/ClientApp/src/assets/student-web/component_styles/vote.css
  18. 110 99
      TEAMModelOS/ClientApp/src/assets/student-web/component_styles/vote.less
  19. 0 44
      TEAMModelOS/ClientApp/src/components/homework/BaseHwForm.vue
  20. 1 0
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseJudge.vue
  21. 1 0
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseMultiple.vue
  22. 36 82
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseQnForm.vue
  23. 15 0
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseQuestionnaire.less
  24. 119 33
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseQuestionnaire.vue
  25. 1 1
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseSingle.less
  26. 1 0
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseSingle.vue
  27. 102 0
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseSubjective.vue
  28. 4 44
      TEAMModelOS/ClientApp/src/components/student-web/EventBasicInfo.vue
  29. 16 18
      TEAMModelOS/ClientApp/src/components/student-web/EventView/BillBoardandLightBox.vue
  30. 6 6
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContent.vue
  31. 283 156
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/Vote.vue
  32. 84 83
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventList.vue
  33. 2 2
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventView.vue
  34. 43 1
      TEAMModelOS/ClientApp/src/components/student-web/HomeView/MissionListCard.vue
  35. 0 3
      TEAMModelOS/ClientApp/src/components/syllabus/DragTree.vue
  36. 1 1
      TEAMModelOS/ClientApp/src/components/vote/BaseVoteBar.vue
  37. 56 46
      TEAMModelOS/ClientApp/src/components/vote/BaseVoteForm.vue
  38. 43 0
      TEAMModelOS/ClientApp/src/css/site.css
  39. 6 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/http.js
  40. 2 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/index.js
  41. 6 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/studentWeb.js
  42. 24 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/survey.js
  43. 7 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/vote.js
  44. 3 3
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/evaluation.js
  45. 6 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/http.js
  46. 2 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/index.js
  47. 24 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/survey.js
  48. 7 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/vote.js
  49. 1 3
      TEAMModelOS/ClientApp/src/store/module/studentWeb.js
  50. 2 1
      TEAMModelOS/ClientApp/src/utils/evTools.js
  51. 26 12
      TEAMModelOS/ClientApp/src/view/classmgt/ManageClass.vue
  52. 0 42
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseCreateChild.vue
  53. 0 41
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseEditExercise.vue
  54. 0 42
      TEAMModelOS/ClientApp/src/view/evaluation/index/CreateExercises.vue
  55. 0 2
      TEAMModelOS/ClientApp/src/view/evaluation/types/BaseCompletion.vue
  56. 1 1
      TEAMModelOS/ClientApp/src/view/newcourse/MyCourse.vue
  57. 12 2
      TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.less
  58. 164 102
      TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.vue
  59. 9 1
      TEAMModelOS/ClientApp/src/view/student-account/AddStudent.vue
  60. 10 8
      TEAMModelOS/ClientApp/src/view/student-account/Index.vue
  61. 49 6
      TEAMModelOS/Controllers/Client/HiTeachController.cs
  62. 106 12
      TEAMModelOS/Controllers/Common/SurveyController.cs
  63. 36 17
      TEAMModelOS/Controllers/Common/VoteController.cs
  64. 1 0
      TEAMModelOS/Controllers/Core/ImportController.cs
  65. 96 12
      TEAMModelOS/Controllers/School/StudentController.cs
  66. 15 2
      TEAMModelOS/Controllers/XTest/TestController.cs
  67. 0 16
      TEAMModelOS/Models/Dto/SurveyDto.cs
  68. 0 14
      TEAMModelOS/Models/Dto/VoteDto.cs
  69. 163 25
      TEAMModelOS/Services/Common/ActivityStudentService.cs
  70. 1 1
      TEAMModelOS/TEAMModelOS.csproj

+ 2 - 2
TEAMModelFunction/MonitorCosmosDB.cs

@@ -57,7 +57,7 @@ namespace TEAMModelFunction
                         await _dingDing.SendBotMsg($"CosmosDBTrigger,{pk}\n" +
                                         $"Start Time:{DateTimeOffset.FromUnixTimeMilliseconds(stime).AddHours(8)}\n" +
                                         $"End Time:{DateTimeOffset.FromUnixTimeMilliseconds(etime).AddHours(8)}",
-                                        GroupNames.醍摩豆服務運維群組);
+                                        GroupNames.成都开发測試群組);
                         switch (pk)
                         {
                             case "Exam":
@@ -67,7 +67,7 @@ namespace TEAMModelFunction
                                 TriggerVote.Trigger(_azureCosmos, _serviceBus, _azureStorage, _dingDing, client, input, code, stime, etime, school, _azureRedis);
                                 break;
                             case "Survey":
-                                TriggerSurvey.Trigger(_azureCosmos, _serviceBus, _azureStorage, _dingDing, client, input, code, stime, etime, school);
+                                TriggerSurvey.Trigger(_azureCosmos, _serviceBus, _azureStorage, _dingDing, client, input, code, stime, etime, school, _azureRedis);
                                 break;
 
                         }

+ 1 - 1
TEAMModelFunction/MonitorServicesBus.cs

@@ -164,7 +164,7 @@ namespace TEAMModelFunction
                         await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Blob:Catalog:{name}", key, size.Item2[key].HasValue ? size.Item2[key].Value : 0);
                     }
                     await _dingDing.SendBotMsg($"ServiceBus,Blob() 容器:{name}使用:{size.Item1},文件分类:{size.Item2.ToJsonString()}",
-                            GroupNames.醍摩豆服務運維群組);
+                            GroupNames.成都开发測試群組);
                 }
             }
             catch (Exception ex)

+ 2 - 2
TEAMModelFunction/TEAMModelFunction.csproj

@@ -9,8 +9,8 @@
   </ItemGroup>
   <ItemGroup>
     <PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
-    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.CosmosDB" Version="3.0.8" />
-    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.ServiceBus" Version="4.2.0" />
+    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.CosmosDB" Version="3.0.9" />
+    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.ServiceBus" Version="4.2.1" />
     <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.7" />
   </ItemGroup>
   <ItemGroup>

+ 2 - 2
TEAMModelFunction/TriggerExam.cs

@@ -87,7 +87,7 @@ namespace TEAMModelFunction
                     break;
                 case "going":                    
                     ActivityData data;
-                    if (info.scope == "school" || info.scope == "teacher")
+                    if (info.scope == "school")
                     {
                         data = new ActivityData
                         {
@@ -267,7 +267,7 @@ namespace TEAMModelFunction
                         fno++;
                     }
                     //ActivityData data;
-                    if (info.scope == "school" || info.scope == "teacher")
+                    if (info.scope == "school")
                     {
                         data = new ActivityData
                         {

+ 30 - 9
TEAMModelFunction/TriggerSurvey.cs

@@ -1,5 +1,6 @@
 using Azure.Cosmos;
 using Azure.Messaging.ServiceBus;
+using Azure.Storage.Blobs.Models;
 using Microsoft.Azure.Documents;
 using System;
 using System.Collections.Generic;
@@ -8,6 +9,7 @@ using System.Text.Json;
 using System.Threading.Tasks;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK.Helper.Common.CollectionHelper;
 using TEAMModelOS.SDK.Models;
 using TEAMModelOS.SDK.Models.Cosmos;
 
@@ -16,7 +18,7 @@ namespace TEAMModelFunction
    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)
+               CosmosClient client, Document input, string code, long stime, long etime, string school,AzureRedisFactory _azureRedis)
         {
             Survey survey = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Survey>(input.Id, new Azure.Cosmos.PartitionKey($"{code}"));
             List<ChangeRecord> changeRecords = await _azureStorage.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", input.Id }, { "PartitionKey", survey.progress } });
@@ -52,7 +54,7 @@ namespace TEAMModelFunction
                     break;
                 case "going":
                     ActivityData data;
-                    if (survey.scope == "school" || survey.scope == "teacher")
+                    if (survey.scope == "school")
                     {
                         data = new ActivityData
                         {
@@ -64,10 +66,11 @@ namespace TEAMModelFunction
                             endTime = survey.endTime,
                             scode = survey.code,
                             scope = survey.scope,
-                            classes = survey.classes,
-                            tmdids = survey.tmdids,
+                            classes = survey.classes.IsNotEmpty() ? survey.classes : new List<string> { "" },
+                            tmdids = survey.tmdids.IsNotEmpty() ? survey.tmdids : new List<string> { "" },
                             progress = "going",
-                            owner = survey.owner
+                            owner = survey.owner,
+                            subjects = new List<string> { "" }
                         };
                         await client.GetContainer("TEAMModelOS", "School").UpsertItemAsync<ActivityData>(data, new Azure.Cosmos.PartitionKey(data.code));
                     }
@@ -83,9 +86,11 @@ namespace TEAMModelFunction
                             endTime = survey.endTime,
                             scode = survey.code,
                             scope = survey.scope,
-                            classes = survey.classes,
                             progress = "going",
-                            owner = survey.owner
+                            classes = survey.classes.IsNotEmpty() ? survey.classes : new List<string> { "" },
+                            owner = survey.owner,
+                            tmdids = new List<string> { "" },
+                            subjects = new List<string> { "" }
                         };
                         await client.GetContainer("TEAMModelOS", "Teacher").UpsertItemAsync<ActivityData>(data, new Azure.Cosmos.PartitionKey(data.code));
                     }
@@ -101,7 +106,7 @@ namespace TEAMModelFunction
                     else
                     {
                         long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync("active-task", messageSurveyEnd, DateTimeOffset.FromUnixTimeMilliseconds(etime));
-                        ChangeRecord changeRecord = new ChangeRecord
+                        ChangeRecord changeRecord                                                                                                                                                                                                                                                   = new ChangeRecord
                         {
                             RowKey = input.Id,
                             PartitionKey = "going",
@@ -112,7 +117,23 @@ namespace TEAMModelFunction
                     }
                     break;
                 case "finish":
-
+                    var records =await  _azureRedis.GetRedisClient(8).HashGetAllAsync($"Survey:Record:{survey.id}");
+                    List<dynamic> recs = new List<dynamic>();
+                    foreach (var rcd in records) {
+                        var value = rcd.Value.ToString().ToObject<JsonElement>();
+                        recs.Add(new { index = rcd.Name.ToString(), ans = value });
+                    }
+                    var cods = new { records = recs };
+                    //问卷整体情况
+                    await  _azureStorage.UploadFileByContainer(survey.owner, cods.ToJsonString(), "survey", $"{survey.id}/record.json");
+                    //结算每道题的答题情况
+                   
+                    var ContainerClient =   _azureStorage.GetBlobContainerClient(survey.owner);
+                    var route = ContainerClient.Uri.ToString();
+                    List<BlobItem> items = await ContainerClient.List($"survey/{survey.id}/urecord");
+                    items.ForEach(x => {
+                        var url = $"{route}/{x.Name}";
+                    });
                     break;
             }
         }

+ 12 - 9
TEAMModelFunction/TriggerVote.cs

@@ -59,7 +59,7 @@ namespace TEAMModelFunction
                         break;
                     case "going":
                         ActivityData data;
-                        if (vote.scope == "school" || vote.scope == "teacher")
+                        if (vote.scope == "school" )
                         {
                             data = new ActivityData
                             {
@@ -150,7 +150,7 @@ namespace TEAMModelFunction
                         tasks.Add(_azureStorage.UploadFileByContainer(vote.owner, recordsBlob.ToJsonString(), "vote", $"{vote.id}/index.json"));
                         //处理投票者的记录
                         await Task.WhenAll(tasks);
-                        if (vote.scope == "school" || vote.scope == "teacher")
+                        if (vote.scope == "school")
                         {
                             await client.GetContainer("TEAMModelOS", "School").ReplaceItemAsync<Vote>(vote,vote.id, new Azure.Cosmos.PartitionKey(vote.code));
                         }
@@ -159,7 +159,7 @@ namespace TEAMModelFunction
                             await client.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Vote>(vote, vote.id, new Azure.Cosmos.PartitionKey(vote.code));
                         }
                         //更新结束状态
-                        if (vote.scope == "school" || vote.scope == "teacher")
+                        if (vote.scope == "school")
                         {
                             data = new ActivityData
                             {
@@ -171,10 +171,12 @@ namespace TEAMModelFunction
                                 endTime = vote.endTime,
                                 scode = vote.code,
                                 scope = vote.scope,
-                                classes = vote.classes,
-                                tmdids = vote.tmdids,
                                 progress = "finish",
-                                owner = vote.owner
+                                classes = vote.classes.IsNotEmpty() ? vote.classes : new List<string> { "" },
+                                tmdids = vote.tmdids.IsNotEmpty() ? vote.tmdids : new List<string> { "" },
+                                owner = vote.owner,
+                                subjects = new List<string> { "" }
+                                
                             };
                             await client.GetContainer("TEAMModelOS", "School").ReplaceItemAsync<ActivityData>(data,data.id, new Azure.Cosmos.PartitionKey(data.code));
                         }
@@ -192,9 +194,10 @@ namespace TEAMModelFunction
                                 scode = vote.code,
                                 scope = vote.scope,
                                 progress = "finish",
-                                classes = vote.classes,
-                                owner = vote.owner
-                                // tmdids = vote.tmdids
+                                classes = vote.classes.IsNotEmpty() ? vote.classes : new List<string> { "" },
+                                owner = vote.owner,
+                                tmdids = new List<string> { "" },
+                                subjects = new List<string> { "" }
                             };
                             await client.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<ActivityData>(data,data.id, new Azure.Cosmos.PartitionKey(data.code));
                         }

+ 17 - 0
TEAMModelOS.SDK/DI/AzureStorage/AzureStorageBlobExtensions.cs

@@ -40,6 +40,23 @@ namespace TEAMModelOS.SDK.DI
                 return size;
             }
         }
+
+        public static async Task<List<BlobItem>> List(this BlobContainerClient client, string prefix = null) {
+            try
+            {
+                List<BlobItem> items = new List<BlobItem>();
+                await foreach (BlobItem item in client.GetBlobsAsync(BlobTraits.None, BlobStates.None, prefix)) {
+                    items.Add(item);
+                }
+                return items;
+            }
+            catch
+            {
+                return null;
+            }
+
+        }
+
         /// <summary>
         /// 取得指定前置詞的 Blob 名稱的總大小(Bytes),例如指定目錄名稱為前置詞
         /// </summary>      

+ 3 - 1
TEAMModelOS.SDK/DI/DingDing/DingDing.cs

@@ -105,7 +105,9 @@ namespace TEAMModelOS.SDK.DI
         [Description("f8bf19c9363e3b288e018856014bcbf89708f19b3aae009e203edd68af25c9fe")]
         BB訂單群組,
         [Description("a27b099b15a054374da41b9f66f72e5fc6b378e98418859f7c0ef46408941808")]
-        測試群組,
+        台北開發測試群組,
+        [Description("82aba2fccfa8442c575db3ac0442fa5aea90cd7574bb9a71d5abf210ea72a3aa,SEC3f38eca87cd4fd10505d136f91071a2e8b14cd863bd6bbafb24c612fc751a59a")]
+        成都开发測試群組,
         [Description("1a316ce4edc2db88231d40d80072b00f2751d7d9e2e5871c5dc061885b01c48d,SECff60201ac9b219943b9f8fc397fda1a617d0cbc140850f5ea9cb4f131479d39a")]
         醍摩豆服務運維群組
     }

+ 16 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/Inner/SurveyRecord.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Models.Cosmos.Common.Inner
+{
+    public  class SurveyRecord
+    {
+        /// <summary>
+        /// 问卷列表
+        /// </summary>
+        public List<List<string>> ans { get; set; }
+        public long time { get; set; }
+        public string userid { get; set; }
+    }
+}

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

@@ -6,7 +6,7 @@ namespace TEAMModelOS.SDK.Models.Cosmos.Common.Inner
 {
     public class VoteRecord
     {
-        public List<string> opt { get; set; }
+        public Dictionary<string, int> opt { get; set; } = new Dictionary<string, int>();
         public long time { get; set; }
         public string userid { get; set; }
     }

+ 27 - 85
TEAMModelOS.SDK/Models/Cosmos/Common/Survey.cs

@@ -43,28 +43,12 @@ namespace TEAMModelOS.SDK.Models
         /// <summary>
         /// pending 待发布|going 已发布|finish 已结束
         /// </summary>
-        [Required(ErrorMessage = "progress 必须设置")]
+        //[Required(ErrorMessage = "progress 必须设置")]
         public string progress { get; set; }
         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>
-        //[ProtoMember(4)]
-        //public List<Target> target { get; set; }
-
-        /// <summary>
-        /// 发布模式 0 立即发布 1 定时
-        /// </summary>
-        //public string publishModel { get; set; }
 
         /// <summary>
         /// 开始时间
@@ -76,94 +60,52 @@ namespace TEAMModelOS.SDK.Models
         /// </summary>
         public long endTime { get; set; }
         public long createTime { get; set; } // 问卷发布时间
-
         /// <summary>
         /// 更新时间
         /// </summary>
         public long updateTime { get; set; }
-        //public long sequenceNumber { get; set; }
-
-        public string blobUrl { get; set; }
-        //将问题放入Blob
-        //public List<Question> questions { get; set; }
+        //将问题放入Blob  hbcn/survey/问卷调查id.json  存放内容 Question的数组
+        public List<string> questionUrl { get; set; }
+        // public List<Question> questions { get; set; }
+        /// <summary>
+        /// 学生作答记录/ 状态为finish时进行结算
+        /// </summary>
+        public string recordUrl { get; set; }
+        /// <summary>
+        /// 如果本题不是客观题 则为[]空数组
+        /// 客观题所有选项[["A","B","C","D"],["A","B"],[],["A","B"]]
+        /// </summary>
+        public List<List<string>> ans { get; set; }
 
     }
 
     /// <summary>
-    ///
+    ///问卷题目
     /// </summary>
     public class Question {
+        /// <summary>
+        /// 题目id
+        /// </summary>
         public string qid { get; set; }
-        public string question { get; set; }
-        public List<OptionSurvey> options { get; set; }
-        public string type { get; set; }
-        //public QuestionResult result { get; set; }
-
-    }
-
-    public class OptionSurvey{
         /// <summary>
-        /// 选项编码
+        /// 问卷题目
         /// </summary>
-        public string code { get; set; }
+        public string question { get; set; }
         /// <summary>
-        /// 选项文本
+        /// 问卷题目的描述
         /// </summary>
-        public string value { get; set; }
+        public string description { get; set; }
         /// <summary>
-        /// 选项描述
+        /// 问卷选项
         /// </summary>
-        //public string desc { get; set; }
+        public List<CodeValue> options { get; set; }
         /// <summary>
-        /// 选择数
+        /// 判断judge  多选multiple 单选single
         /// </summary>
-        public int? count { get; set; }
+        public string type { get; set; }
         /// <summary>
-        /// 其他答案数量
+        /// 是否必需作答
         /// </summary>
-        public int? other { get; set; }
-    }
-
-
-
-
-
-    public class QuestionResult
-    {
-        public double finish { get; set; }
-        public double finishRate { get; set; }
-    }
-    public class Result { 
-        public double count { get; set; }
-        public double rate { get; set; }
-    }
-    public class Classes { 
-        public string code { get; set; }
-        public string id { get; set; }
-        public string name { get; set; }
-        public string scope { get; set; }
-        public Result result { get; set; }
-        public List<StudentInfo> students { get; set; }
-        public List<AnswerRate> answers { get; set; }
-    }
-    public class AnswerRate {
-        public string qid { get; set; }
-        public double answerRate { get; set; }
-        public List<CodeValue> option { get; set; }
-    }
-    public class StudentInfo {
-        public string id { get; set; }
-        public string name { get; set; }
-        public long finishTime { get; set; }
-        public List<AnswerInfo> answers { get; set; }
-        public ResultInfo result { get; set; }
-    }
-    public class ResultInfo
-    {
-        public double answerRate { get; set; }
-    }
-    public class AnswerInfo { 
-        public string qid { get; set; }
-        public string answer { get; set; }
+        public bool required { get; set; }
     }
 }

+ 1 - 1
TEAMModelOS.SDK/Models/Cosmos/Common/Vote.cs

@@ -119,7 +119,7 @@ namespace TEAMModelOS.SDK.Models
         /// <summary>
         /// 得票数量
         /// </summary>
-        public int? count { get; set; } 
+        public int? count { get; set; } = 0;
     }
     
 }

+ 5 - 4
TEAMModelOS.SDK/TEAMModelOS.SDK.csproj

@@ -13,9 +13,9 @@
   <ItemGroup>
     <PackageReference Include="AspectCore.Extensions.Reflection" Version="2.1.0" />
     <PackageReference Include="Azure.Cosmos" Version="4.0.0-preview3" />
-    <PackageReference Include="Azure.Messaging.ServiceBus" Version="7.0.0" />
-    <PackageReference Include="Azure.Storage.Blobs.Batch" Version="12.4.0" />
-    <PackageReference Include="Azure.Storage.Queues" Version="12.5.0" />
+    <PackageReference Include="Azure.Messaging.ServiceBus" Version="7.1.0" />
+    <PackageReference Include="Azure.Storage.Blobs.Batch" Version="12.5.0" />
+    <PackageReference Include="Azure.Storage.Queues" Version="12.6.0" />
     <PackageReference Include="ClouDASLibx" Version="1.1.4" />
     <PackageReference Include="DocumentFormat.OpenXml" Version="2.11.3" />
     <PackageReference Include="HtmlAgilityPack" Version="1.11.28" />
@@ -23,7 +23,8 @@
     <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
     <PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="3.1.10" />
     <PackageReference Include="StackExchange.Redis" Version="2.2.4" />
-    <PackageReference Include="System.Drawing.Common" Version="5.0.0" />
+    <PackageReference Include="SvgNet" Version="2.1.1" />
+    <PackageReference Include="System.Drawing.Common" Version="5.0.1" />
     <PackageReference Include="Microsoft.Azure.Cosmos.Table" Version="2.0.0-preview" /> 
     <PackageReference Include="Caching.CSRedis" Version="3.6.50" />
   </ItemGroup>

+ 8 - 3
TEAMModelOS/ClientApp/src/api/http.js

@@ -2,6 +2,7 @@ import axios from 'axios'
 import { Message } from 'view-design'
 import router from '@/router/index'
 import config from '@/store/module/config'
+import { app } from '@/boot-app.js'
 
 
 let refreshing = false
@@ -92,10 +93,14 @@ axios.interceptors.response.use(
         if (error.response && error.response.status === 401) {
             localStorage.clear()
             window.location.href = window.location.origin + '/login'
-            Message.error('登录状态已过期!请重新登录!')
+            Message.error(app.$t('http.error401'))
         } else if (error.response.status === 500) {
-            Message.error('服务器错误!')
-        }
+            Message.error(app.$t('http.error500'))
+        } else if (error.response.status === 404) {
+            Message.error(app.$t('http.error404'))
+        } else{
+			Message.error(app.$t('http.error'))
+		}
         return Promise.reject(error)
     }
 )

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

@@ -6,7 +6,7 @@ export default {
     },
 	// 查询问卷列表基础信息
     FindSurveysSummary: function(data) {
-        return post('/common/survey/find-summary', data)
+        return post('/common/survey/find-id', data)
     },
 	// 保存或者更新问卷
 	UpsertSurvey: function(data) {
@@ -22,7 +22,7 @@ export default {
 	},
 	// 查询问卷作答记录
 	FindRecordSurvey: function(data) {
-	    return post('/common/survey/find-record', data)
+	    return post('/common/survey/record', data)
 	},
 	// 查询问卷作答记录
 	UpsertRecord: function(data) {

+ 12 - 1
TEAMModelOS/ClientApp/src/api/studentWeb.js

@@ -164,6 +164,17 @@ export default {
     getFileSas: function (data) {
         return post('/blob/sas-url-r', data)
     },
-
+    //查詢學生端活動信息
+    getActivityInfo: function (data) {
+        return post('/student/stu-find-activity',data)
+    },
+    //查詢學生端投票活动
+    getVoteInfo: function (data) {
+        return post('/common/vote/find-id',data)
+    },
+    //查詢學生端投票结果
+    getVoteResult: function (data) {
+        return post('/common/vote/record',data)
+    }
 }
 

+ 21 - 2
TEAMModelOS/ClientApp/src/assets/student-web/component_styles/vote.css

@@ -1,12 +1,14 @@
 .vote .checkAnswer {
-  display: block;
-  margin-top: 10px;
+  display: flex;
+  width:100%;
+  margin-top: 20px;
   margin-left: 0px;
 }
 .vote .checkAnswer .testBtn {
   margin: 20px 0px;
   position: relative;
   z-index: 2;
+  width:50%;
   font-weight: bolder;
   padding-left: 20px;
   cursor: pointer;
@@ -33,6 +35,18 @@
   z-index: 1;
   border-radius: 4px;
   border: 1px solid #c5c5c5;
+  display:flex;
+}
+    .vote  .vote-title {
+        width: 100%;
+        display: flex;
+    }
+    .vote .vote-title .title-rect-group{
+        display:flex;
+    }
+.vote .checkAnswer .testbg .vote-info{
+    width:95%;
+    display:flex;
 }
 .vote .slide-fade-enter-active {
   transition: all 0.3s ease;
@@ -45,6 +59,11 @@
   transform: translateY(-5%);
   opacity: 0;
 }
+.vote .question-box {
+    display: flex;
+    margin-top: 10px;
+    font-size: 16px;
+}
 .vote .votefinished-icon {
   font-size: 80px;
   position: relative;

+ 110 - 99
TEAMModelOS/ClientApp/src/assets/student-web/component_styles/vote.less

@@ -1,108 +1,119 @@
 @import 'color.less';
 .vote {
-  //投票區按鈕
-  .checkAnswer {
-    display: block;
-
-    margin-top: 10px;
-    margin-left: 0px;
-
-    .testBtn {
-      margin: 20px 0px;
-      position: relative;
-      z-index: 2;
-      font-weight: bolder;
-      padding-left: 20px;
-      cursor: pointer;
-
-      input[type="checkbox"] {
-        visibility: hidden;
-        margin-left: -10px;
-
-        &:checked {
-          ~ .testbg {
-            background-color: @primary !important;
-            border: none;
-            span {
-              color: #fff;
-              font-weight: bolder;
+    //投票區按鈕
+    .checkAnswer {
+        display: block;
+        margin-top: 20px;
+        margin-left: 0px;
+
+        .testBtn {
+            margin: 20px 0px;
+            position: relative;
+            z-index: 2;
+            font-weight: bolder;
+            padding-left: 20px;
+            cursor: pointer;
+
+            input[type="checkbox"] {
+                visibility: hidden;
+                margin-left: -10px;
+
+                &:checked {
+                    ~ .testbg {
+                        background-color: @primary !important;
+                        border: none;
+
+                        span {
+                            color: #fff;
+                            font-weight: bolder;
+                        }
+                    }
+                }
             }
-          }
         }
-      }
+
+
+        .testbg {
+            cursor: pointer;
+            background-color: #fff;
+            position: relative;
+            height: auto;
+            top: -31px;
+            padding: 10px;
+            z-index: 1;
+            display: flex;
+            border-radius: 4px;
+            border: 1px solid rgb(197, 197, 197);
+        }
+    }
+    //送出訊息動畫
+    .slide-fade-enter-active {
+        transition: all 0.3s ease;
+    }
+
+    .slide-fade-leave-active {
+        transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
+    }
+
+    .slide-fade-enter, .slide-fade-leave-to
+    /* .slide-fade-leave-active for below version 2.1.8 */ {
+        transform: translateY(-5%);
+        opacity: 0;
+    }
+
+    .votefinished-icon {
+        font-size: 80px;
+        position: relative;
+        left: -3px;
+        margin-bottom: 10px;
+    }
+    //活動結果
+    .voteResultsItem {
+        padding: 10px 20px;
+        padding-left: 80px;
+        z-index: 0 !important;
+        position: relative;
     }
-    .testbg {
-      cursor: pointer;
-      background-color: #fff;
-      position: relative;
-      height: auto;
-      top: -31px;
-      padding: 10px;
-      z-index: 1;
-      border-radius: 4px;
-      border: 1px solid rgb(197, 197, 197);
+
+    .voteResultsIcon {
+        font-size: 20px;
+        color: @primary;
+        position: absolute;
+        position: absolute;
+        top: 0;
+        right: 0;
+        bottom: 0;
+        left: 2%;
+        margin: auto 0;
     }
-  }
-
-  //送出訊息動畫
-  .slide-fade-enter-active {
-    transition: all 0.3s ease;
-  }
-  .slide-fade-leave-active {
-    transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
-  }
-  .slide-fade-enter, .slide-fade-leave-to
-  /* .slide-fade-leave-active for below version 2.1.8 */ {
-    transform: translateY(-5%);
-    opacity: 0;
-  }
-  .votefinished-icon {
-    font-size: 80px;
-    position: relative;
-    left: -3px;
-    margin-bottom: 10px;
-  }
-
-  //活動結果
-  .voteResultsItem {
-    padding: 10px 20px;
-    padding-left: 80px;
-    z-index: 0 !important;
-    position: relative;
-  }
-  .voteResultsIcon {
-    font-size: 20px;
-    color: @primary;
-    position: absolute;
-    position: absolute;
-    top: 0;
-    right: 0;
-    bottom: 0;
-    left: 2%;
-    margin: auto 0;
-  }
-  .voteResultSelect {
-    background-color: rgba(229, 229, 229, 0.56);
-    border-radius: 4px;
-  }
-  .voteResultsMainRow {
-    background-color: rgba(229, 229, 229, 0.56);
-    padding: 20px;
-    border-radius: 4px;
-  }
-  .voteResultsMainKeyWord {
-    color: rgb(36, 184, 128);
-    font-size: 24px;
-    position: relative;
-    top: 2px;
-  }
-  .vote-chart-card{
-    width:50%;
-    @media screen and (max-width: 1200px){
-      width:70%;
+
+    .voteResultSelect {
+        background-color: rgba(229, 229, 229, 0.56);
+        border-radius: 4px;
     }
-    @media screen and (max-width: 767px){
-      width:100%;
+
+    .voteResultsMainRow {
+        background-color: rgba(229, 229, 229, 0.56);
+        padding: 20px;
+        border-radius: 4px;
+    }
+
+    .voteResultsMainKeyWord {
+        color: rgb(36, 184, 128);
+        font-size: 24px;
+        position: relative;
+        top: 2px;
+    }
+
+    .vote-chart-card {
+        width: 50%;
+
+        @media screen and (max-width: 1200px) {
+            width: 70%;
+        }
+
+        @media screen and (max-width: 767px) {
+            width: 100%;
+        }
     }
-  }
 }

+ 0 - 44
TEAMModelOS/ClientApp/src/components/homework/BaseHwForm.vue

@@ -398,47 +398,3 @@
 <style lang="less">
 @import "./BaseHwForm.less";
 </style>
-
-<style>
-    /* 修改iview Modal样式 */
-
-    .related-modal .ivu-modal-content {
-        background: #3c3c3c;
-        overflow: hidden;
-        color: #fff;
-    }
-
-    .related-modal .ivu-modal-body {
-        height: 400px;
-        padding: 20px;
-    }
-
-    .related-modal .ivu-modal-body {
-        height: 700px;
-    }
-
-    .related-modal .ivu-modal-header {
-        border-bottom: none;
-        font-size: 18px;
-        font-weight: bold;
-        padding: 25px;
-    }
-
-    .related-modal .modal-content {
-        padding: 0 35px 30px 35px;
-    }
-
-    .related-modal .modal-btn {
-        margin-left: 2%;
-        width: 96%;
-        height: 40px;
-        background: rgb(11, 151, 117);
-        border: none;
-        color: #fff;
-        margin-top: 30px;
-    }
-
-    .related-modal .choose-content {
-        height:85%;
-    }
-</style>

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

@@ -212,6 +212,7 @@
 			stemEditor.config.onchange = (html) => {
 				this.stemContent = html
 			}
+			this.$editorTools.initSimpleEditor(stemEditor)
 					stemEditor.create()
 			this.stemEditor = stemEditor
 			this.initEditors()

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

@@ -211,6 +211,7 @@
 			stemEditor.config.onchange = (html) => {
 				this.stemContent = html
 			}
+			this.$editorTools.initSimpleEditor(stemEditor)
 					stemEditor.create()
 			this.stemEditor = stemEditor
 			this.initEditors()

+ 36 - 82
TEAMModelOS/ClientApp/src/components/questionnaire/BaseQnForm.vue

@@ -5,15 +5,15 @@
 				<Input :class="!qnFormEdit ? 'qn-form-disabled':''" v-model="qnForm.name" :placeholder="$t('survey.form.namePlace')"></Input>
 			</FormItem>
 
-			<FormItem :label="$t('survey.form.target')" prop="targetClassIds">
+			<FormItem :label="$t('survey.form.target')" prop="classes">
 				<RadioGroup v-model="classType" @on-change="onClassTypeChange" v-if="qnFormEdit">
 				        <Radio label="private">{{ $t('survey.form.privateClass') }}</Radio>
 				        <Radio label="school">{{ $t('survey.form.schoolClass') }}</Radio>
 				</RadioGroup>
 				<div v-if="!qnFormEdit && curQnItem" class="vote-class">
-					<span v-for="item in curQnItem.targetClassIds" class="vote-class-item">{{ getTargetName(item) }}</span>
+					<span v-for="item in curQnItem.classes" class="vote-class-item">{{ getTargetName(item) }}</span>
 				</div>
-				<Select multiple v-model="qnForm.targetClassIds" :class="!qnFormEdit ? 'qn-form-disabled':''" :placeholder="$t('survey.form.targetPlace')" v-else>
+				<Select multiple v-model="qnForm.classes" :class="!qnFormEdit ? 'qn-form-disabled':''" :placeholder="$t('survey.form.targetPlace')" v-else>
 					<!-- <Option v-for="(item,index) in classRooms" :value="item.id" :key="index">{{ item.name }}</Option> -->
 						<Option v-for="item in classRooms.filter(i=>i.scope === classType)" :value="item.id" :key="item.id">{{ item.name }}</Option>
 						<!-- <Option v-for="item in classRooms" :value="item.id" :key="item.id">{{ item.name }}</Option> -->
@@ -32,12 +32,6 @@
 				<div ref="descriptionEditor" style="text-align:left" v-show="qnFormEdit"></div>
 				<div v-html="qnForm.description" v-show="!qnFormEdit" style="margin:10px;font-size:16px;font-weight:bold;color:#fff"></div>
 			</FormItem>
-
-
-			<!-- <FormItem v-show="qnFormEdit">
-				<Button type="primary" class="btn-save" @click="handleSubmit('qnForm')" :loading="isBtnLoading">保存</Button>
-				<Button @click="handleCancel('qnForm')" class="btn-reset" style="margin-left: 8px">取消</Button>
-			</FormItem> -->
 		</Form>
 
 	</div>
@@ -56,7 +50,7 @@
 				type: Boolean
 			}
 		},
-		data() {
+		data(vm) {
 			return {
 				curQnItem:null,
 				classType:'private',
@@ -79,7 +73,7 @@
 				uploadUrl: '',
 				qnForm: {
 					name: '',
-					targetClassIds: [],
+					classes: [],
 					endTime: '',
 					publishModel: '0',
 					rangeTime: [],
@@ -89,9 +83,10 @@
 					other: []
 				},
 				defaultParams: {
+					id:"",
 					code: "",
 					name: "",
-					targetClassIds: [],
+					classes: [],
 					publishModel: "0",
 					startTime: 0,
 					endTime: 0,
@@ -101,26 +96,21 @@
 				ruleValidate: {
 					name: [{
 						required: true,
-						message: '问卷名称不能为空',
+						message: vm.$t('survey.form.ruleName'),
 						trigger: 'blur'
 					}],
-					publishModel: [{
-						required: true,
-						message: '问卷发布模式不能为空',
-						trigger: 'change'
-					}],
 					description: [{
 						required: true,
-						message: '问卷描述不能为空',
+						message: vm.$t('survey.form.ruleDescription'),
 						trigger: 'blur'
 					}],
-					targetClassIds: [{
+					classes: [{
 						required: true,
-						message: '问卷对象不能为空'
+						message: vm.$t('survey.form.ruleClasses')
 					}],
 					rangeTime: [{
 						required: true,
-						message: '日期不能为空!',
+						message: vm.$t('survey.form.ruleTime'),
 						trigger: 'change',
 						type: 'array'
 					}]
@@ -129,18 +119,21 @@
 		},
 		methods: {
 			onClassTypeChange(val){
-				this.qnForm.targetClassIds = []
+				this.qnForm.classes = []
 			},
 
 			onChangeRange(arr) {
-				if(arr[0] === ''){
+				if (arr[0] === '') {
 					this.qnForm.rangeTime = null
-					this.qnForm.startTime = ''
-					this.qnForm.rangeTime = ''
-				}else{
+					this.ruleValidate.rangeTime[0].message = this.$t('survey.form.ruleTime')
+				} else if (this.getTimestampByString(arr[0]) < Date.now()) {
+					this.qnForm.rangeTime = null
+					this.ruleValidate.rangeTime[0].message = this.$t('survey.form.ruleStartTime')
+				} else {
 					this.qnForm.startTime = this.getTimestampByString(arr[0])
 					this.qnForm.endTime = this.getTimestampByString(arr[1])
 					this.qnForm.rangeTime = arr
+					this.ruleValidate.rangeTime[0].message = this.$t('survey.form.ruleTime')
 				}
 			},
 
@@ -158,24 +151,28 @@
 				return new Promise((resolve, reject) => {
 					this.$refs[name].validate((valid) => {
 						if (valid && this.getSimpleText(this.qnForm.description)) {
-							console.log(this.qnForm)
-							console.log(this.editInfo)
-							console.log(this.isEdit)
 							let params = JSON.parse(JSON.stringify(this.defaultParams))
 							let target = []
 							params.code = this.getCurCode
-							params.scope = this.getCurScope
+							// 如果个人问卷的班级是校本班级 那么也要把scope置为school
+							params.scope = this.$route.name === 'personalSurvey' && this.classType === 'private' ? 'private' : 'school'
 							params.name = this.qnForm.name
-							// params.publishModel = this.qnForm.publishModel
 							params.startTime = this.qnForm.startTime
 							params.endTime = this.qnForm.endTime
 							params.description = this.qnForm.description
-							// params.other = this.qnForm.other
+							// 新增参数
+							params.creatorId = this.$store.state.userInfo.TEAMModelId
+							params.owner = this.$route.name === 'personalSurvey' && this.classType === 'private' ? this.$store.state.userInfo
+								.TEAMModelId : this.$store.state.userInfo.schoolCode
+							
+							// 如果是编辑状态 则直接复制ID 如果是新增 则直接赋值新ID
 							if (this.isEdit && this.editInfo.id && this.editInfo.code) {
 								params.id = this.editInfo.id
 								params.createTime = this.editInfo.createTime
+							}else{
+								params.id = this.$tools.guid()
 							}
-							params.targetClassIds = this.qnForm.targetClassIds
+							params.classes = this.qnForm.classes
 							console.log(params)
 							resolve(params)
 						} else {
@@ -244,7 +241,7 @@
 
 
 
-			/**
+			/** 
 			 * 回显问卷详情
 			 * @param item
 			 */
@@ -252,7 +249,7 @@
 				console.log(item)
 				this.qnForm = {
 					name: item.name,
-					targetClassIds: item.targetClassIds || [],
+					classes: item.classes || [],
 					startTime: item.endTime ? item.startTime : '',
 					endTime: item.endTime ? item.endTime : '',
 					description: item.description,
@@ -261,8 +258,8 @@
 				this.currentState = item.state
 				this.$nextTick(() => {
 					this.curQnItem = JSON.parse(JSON.stringify(item))
-					if(item.targetClassIds.length){
-						let isExist = this.classRooms.filter(i => i.id === item.targetClassIds[0])
+					if(item.classes.length){
+						let isExist = this.classRooms.filter(i => i.id === item.classes[0])
 						this.classType = isExist ? isExist[0].scope : 'private'
 					}
 				})
@@ -291,7 +288,7 @@
 			descriptionEditor.config.onchange = (html) => {
 				this.qnForm.description = html
 			}
-			this.$editorTools.initMyEditor(descriptionEditor)
+			this.$editorTools.initSimpleEditor(descriptionEditor)
 			descriptionEditor.create()
 			this.descriptionEditor = descriptionEditor
 		},
@@ -333,46 +330,3 @@
 	@import "./BaseQnForm.less";
 </style>
 
-<style>
-	/* 修改iview Modal样式 */
-
-	.related-modal .ivu-modal-content {
-		background: #3c3c3c;
-		overflow: hidden;
-		color: #fff;
-	}
-
-	.related-modal .ivu-modal-body {
-		height: 400px;
-		padding: 20px;
-	}
-
-	.related-modal .ivu-modal-body {
-		height: 700px;
-	}
-
-	.related-modal .ivu-modal-header {
-		border-bottom: none;
-		font-size: 18px;
-		font-weight: bold;
-		padding: 25px;
-	}
-
-	.related-modal .modal-content {
-		padding: 0 35px 30px 35px;
-	}
-
-	.related-modal .modal-btn {
-		margin-left: 2%;
-		width: 96%;
-		height: 40px;
-		background: rgb(11, 151, 117);
-		border: none;
-		color: #fff;
-		margin-top: 30px;
-	}
-
-	.related-modal .choose-content {
-		height: 85%;
-	}
-</style>

+ 15 - 0
TEAMModelOS/ClientApp/src/components/questionnaire/BaseQuestionnaire.less

@@ -15,6 +15,8 @@
     padding: 30px 6%;
 	padding-bottom: 100px;
 	
+
+	
 	.tools-bar{
 		width: 100%;
 		border-bottom: 1px dashed  #CCCCCC;
@@ -133,3 +135,16 @@
 	  opacity: 0;
 	}
 }
+
+
+	.survey-modal{
+		
+		.ivu-modal-body{
+			height: 600px !important;
+		}
+		
+		.modal-header{
+			font-weight: 400;
+			font-size: 16px;
+		}
+	}

+ 119 - 33
TEAMModelOS/ClientApp/src/components/questionnaire/BaseQuestionnaire.vue

@@ -6,7 +6,7 @@
 		<div class="qn-item-box">
 			<div v-if="items.length === 0" class="no-data-text">
 			    <img src="@/assets/icon/no_data.svg" width="120" />
-			    <span style="margin-top:15px;color:#808080">暂无题目数据</span>
+			    <span style="margin-top:15px;color:#808080">{{ $t('survey.questionaire.noItemData') }}</span>
 			</div>
 			<!-- 问卷题目单元 -->
 			<draggable class="list-group" tag="div" v-model="items" v-bind="dragOptions" @start="drag = true" @end="onDragEnd" v-else>
@@ -18,10 +18,6 @@
 							<span>{{ index + 1 }}. </span>
 							<span>{{ getSimpleText(item.question) }}</span>
 							<span>( {{ typeList[item.type] }} )</span>
-							<!-- <Tooltip placement="right" v-if="item.description">
-								<Icon type="md-help-circle" size='20' color='#7a7a7a' style="margin-left: 10px;" />
-								<span slot="content">{{ item.description }}</span>
-							</Tooltip> -->
 						</p>
 						<!-- 单选题-选项 -->
 						<RadioGroup v-model="radioModel" v-if="item.type === 'single' || item.type === 'judge'">
@@ -34,23 +30,26 @@
 						</CheckboxGroup>
 						<!-- 数据分析 -->
 						<transition name="indexFade">
-						      <div v-show="curIndexList.includes(index)">
-								  <BaseQnBar :barId="'bar' + index" :barData="item.result || []" v-if="curIndexList.includes(index) && (item.type === 'single' || item.type ==='multiple')"></BaseQnBar>
+						      <div>
+<!-- 						      <div v-show="curIndexList.includes(index)"> -->
+								  <!-- <BaseQnBar :barId="'bar' + index" :barData="item.result || []" v-if="curIndexList.includes(index) && (item.type === 'single' || item.type ==='multiple')"></BaseQnBar> -->
+								  <BaseQnBar :barId="'bar' + index" :barData="item.result || []" v-if="curIndexList.includes(index) && item.type !== 'subjective'"></BaseQnBar>
 							  </div>
 						</transition>
 						<!-- 工具栏 -->
-						<div class="qn-tools" v-show="editable">
-							<div class="qn-tools-item" @click="onItemEdit(item,index)">
+						<div class="qn-tools">
+							<div class="qn-tools-item" @click="onItemEdit(item,index)" v-if="editable">
 								<Icon type="md-create" />
-								<span>编辑</span>
+								<span>{{ $t('survey.questionaire.edit') }}</span>
 							</div>
-							<div class="qn-tools-item" @click="onItemDelete(index)">
+							<div class="qn-tools-item" @click="onItemDelete(index)"  v-if="editable">
 								<Icon type="md-trash" />
-								<span>移除</span>
+								<span>{{ $t('survey.questionaire.remove') }}</span>
 							</div>
-							<div class="qn-tools-item" @click="onItemAnalysis(index)" v-if="editItem.status === 300 && (item.type === 'Single' || item.type ==='Multiple')">
+							<!-- <div class="qn-tools-item" @click="onItemAnalysis(index)"> -->
+							<div class="qn-tools-item" @click="onItemAnalysis(index)" v-if="editItem.progress !== 'pending' && item.type !== 'subjective'">
 								<Icon type="md-podium" />
-								<span>统计数据</span>
+								<span>{{ $t('survey.questionaire.analysisData') }}</span>
 							</div>
 						</div>
 					</div>
@@ -59,15 +58,16 @@
 		</div>
 
 		<!-- 新建题目弹窗 -->
-		<Modal v-model="addModal" width="880" footer-hide class="related-modal" @on-visible-change="onModalChange">
-			<div class="modal-header" slot="header">新增{{ typeList[curType] }}习题</div>
+		<Modal v-model="addModal" width="800" footer-hide class="related-modal survey-modal" @on-visible-change="onModalChange">
+			<div class="modal-header" slot="header">{{ $t('survey.questionaire.add') }}{{ typeList[curType] }}{{ $t('survey.questionaire.item') }}</div>
 			<template v-if="isUpdate">
 			<vuescroll>
 				<!-- <BaseAddItem ref="addItem" :type="curType" :editItem="curItem"></BaseAddItem> -->
 				<BaseSingle v-if="curType==='single'" ref="addItem" :editInfo="curItem" @onSave="onSave"></BaseSingle>
 				<BaseMultiple v-if="curType==='multiple'" ref="addItem" :editInfo="curItem" @onSave="onSave"></BaseMultiple>
 				<BaseJudge v-if="curType==='judge'" ref="addItem" :editInfo="curItem" @onSave="onSave"></BaseJudge>
-				<Button class="modal-btn" @click="onConfirmAdd">确认</Button>
+				<BaseSubjective v-if="curType==='subjective'" ref="addItem" :editInfo="curItem" @onSave="onSave"></BaseSubjective>
+				<Button class="modal-btn" @click="onConfirmAdd">{{ $t('survey.questionaire.confirm') }}</Button>
 			</vuescroll>
 			</template>
 		</Modal>
@@ -78,6 +78,7 @@
 	import BaseSingle from "./BaseSingle.vue"
 	import BaseMultiple from "./BaseMultiple.vue"
 	import BaseJudge from "./BaseJudge.vue"
+	import BaseSubjective from "./BaseSubjective.vue"
 	import BaseQnBar from "./BaseQnBar.vue"
 	
 	export default {
@@ -86,10 +87,11 @@
 			BaseQnBar,
 			BaseSingle,
 			BaseMultiple,
-			BaseJudge
+			BaseJudge,
+			BaseSubjective
 		},
 		props: ["editItem","isEdit"],
-		data() {
+		data(vm) {
 			return {
 				curIndexList:[],
 				curItem:null,
@@ -107,11 +109,10 @@
 				itemAnswers: [],
 				curType: '',
 				typeList: {
-					'single': '单选',
-					'multiple': '多选',
-					'complete': '填空',
-					'judge':'判断',
-					'subjective': '问答'
+					'single': vm.$t('survey.questionaire.single'),
+					'multiple': vm.$t('survey.questionaire.multiple'),
+					'judge':vm.$t('survey.questionaire.judge'),
+					'subjective': vm.$t('survey.questionaire.subjective')
 				}
 			};
 		},
@@ -148,6 +149,7 @@
 				// 判断获取到的新题是否规范
 				if ( this.getSimpleText(addItem.stemContent) && this.checkOptionNull(addItem.optionsContent)){
 					let newItem = {
+						id: this.editIndex === null && !this.curItem ? this.$tools.guid() : this.curItem.id,
 						question: addItem.stemContent,
 						option: addItem.optionsContent,
 						type: this.editIndex === null ? this.curType : this.curItem.type,
@@ -163,7 +165,7 @@
 					}
 					this.addModal = false
 				}else{
-					this.$Message.warning('请填写完整!')
+					this.$Message.warning(this.$t('survey.questionaire.noCompleteTip'))
 				}
 			},
 
@@ -205,10 +207,8 @@
 			/* 删除单个题目 */
 			onItemDelete(index) {
 				this.$Modal.confirm({
-					title: '提示',
-					content: '<p>确认删除该题目?</p>',
-					okText: '确认',
-					cancelText: '取消',
+					title: this.$t('survey.questionaire.confirmTitle'),
+					content: this.$t('survey.questionaire.confirmText'),
 					onOk: () => {
 						this.items.splice(index, 1)
 					}
@@ -231,7 +231,83 @@
 					this.curIndexList.push(index)
 				}
 				
-			}
+			},
+			
+			// 获取blob里的试题数据
+			getBlobItems(qnItem){
+				return new Promise(async (resolve,reject) => {
+					let blobHost = qnItem.scope === 'private' ?  JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8")).blob_uri : JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri
+					// 根据试卷的Blob地址 去读取JSON文件
+					let sasString = qnItem.scope === 'private' ?  await this.$tools.getPrivateSas() : await this.$tools.getSchoolSas()
+					let promiseArr = []
+					for (let item of qnItem.questionUrl) {
+						promiseArr.push(new Promise(async (r,j) => {
+							try{
+								let itemJson = JSON.parse(await this.$tools.getFile(blobHost + item + sasString.sas))
+								r(itemJson)
+							}catch(e){
+								j(e)
+							}
+						}))
+					}
+					Promise.all(promiseArr).then(result => {
+						resolve(result)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+				
+			},
+			
+			/* 根据学生的作答记录 处理成 图表需要的记录 */
+			makeItemResult(result,items) {
+				let arr = [];
+				items.forEach((item, index) => {
+					let obj = {};
+					let resultArr = [];
+					item.option.forEach((opt) => {
+						obj.code = this.getSimpleText(opt.value);
+						obj.value = this.getSelectNum(result, index, opt.code);
+						resultArr.push(obj);
+						obj = {};
+					});
+					item.result = resultArr;
+				});
+				this.items = items
+			},
+			
+			/* 查找指定题目指定选项在作答数据里出现的次数 */
+			getSelectNum(origin, itemIndex, option) {
+				let target = origin[itemIndex].ans
+				if(Object.keys(target).includes(option)){
+					return target[option]
+				}else{
+					return 0 
+				}
+			},
+			
+			// 提取富文本内容中的文本
+			getSimpleText(html) {
+				var r = /<(?!img|video|audio).*?>/g;
+				return html.replace(r, "");
+			},
+			
+			async getQnRecord(item) {
+				return new Promise((r, j) => {
+					this.$api.questionnaire
+						.FindRecordSurvey({
+							id: item.id,
+							code: item.code
+						})
+						.then((res) => {
+							if (!res.error && res.records) {
+								r(res.records);
+							} else {
+								j(500);
+							}
+						});
+				});
+			},
 		},
 
 		mounted() {
@@ -250,13 +326,23 @@
 		},
 		watch: {
 			editItem: {
-				handler(newValue) {
+				async handler(newValue) {
 					/** 编辑回显 */
 					if (newValue) {
-						this.items = newValue.questions || []
+						console.log('问卷接受到的数据 ==',newValue);
+						if(newValue.questionUrl && newValue.questionUrl.length){
+							if(newValue.progress !== 'pending'){
+								let records = await this.getQnRecord(newValue)
+								let items = await this.getBlobItems(newValue)
+								this.makeItemResult(records,items)
+							}else{
+								this.items = await this.getBlobItems(newValue)
+							}
+						}else{
+							this.items = []
+						}
 						this.isShowAllAnalysis = false
 						this.curIndexList = []
-						console.log(newValue);
 					} else {
 						/** 新增 */
 					}

+ 1 - 1
TEAMModelOS/ClientApp/src/components/questionnaire/BaseSingle.less

@@ -10,7 +10,7 @@
 		padding: 0 20px;
 		
 		.w-e-toolbar {
-		    background: @main-bgColor !important;
+		    background: #414141 !important;
 		    border-color: @borderColor !important;
 		    margin-top: 10px;
 		    z-index: 1 !important;

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

@@ -213,6 +213,7 @@
 			this.$editorTools.addVideoUpload(this, stemEditor)
 			this.$editorTools.addAudio(this, stemEditor)
 			this.$editorTools.initMyEditor(stemEditor)
+			this.$editorTools.initSimpleEditor(stemEditor)
 			stemEditor.create()
 			this.stemEditor = stemEditor
 			this.initEditors()

+ 102 - 0
TEAMModelOS/ClientApp/src/components/questionnaire/BaseSubjective.vue

@@ -0,0 +1,102 @@
+<template>
+	<div class="question-single question-subjective">
+		<div class="exersices-content">
+			<IconText :text="'题目'" :color="'#2d8cf0'" :icon="'ios-create'" style="margin-bottom:15px;"></IconText>
+			<div ref="singleEditor" style="text-align:left"></div>
+		</div>
+	</div>
+</template>
+<script>
+	import E from 'wangeditor'
+	import IconText from '@/components/evaluation/IconText.vue'
+	export default {
+		components: {
+			IconText
+		},
+		props: ['editInfo'],
+		data() {
+			return {
+				options: [...new Array(2).keys()], // 默认四个选项
+				existOptions: [...new Array(2).keys()],
+				initFlag: true,
+				trueIndex: 0,
+				editSingleInfo: {},
+				stemEditor: null,
+				stemContent: '',
+				optionsContent: [],
+				optionEditors: [],
+				defaultConfig: {
+					uploadImgShowBase64: true,
+					menus: this.$tools.wangEditorMenuSimple
+				}
+			}
+		},
+		created() {},
+		methods: {
+			resetContent() {
+				console.log('重置')
+				this.options = [...new Array(2).keys()],
+					this.optionsContent = []
+				this.optionEditors.forEach(i => {
+					i.txt.clear()
+				})
+				this.stemEditor.txt.clear()
+			},
+			onRichTextClick(e) {
+				this.$parent.onRichTextClick(e)
+			},
+			// 保存试题 获取最新选项数据
+			doSave() {
+				this.$emit('onSave', {
+					stemContent: this.stemContent,
+					optionsContent: []
+				})
+			},
+		},
+		mounted() {
+			let stemEditor = new E(this.$refs.singleEditor)
+			stemEditor.config.onchange = (html) => {
+				this.stemContent = html
+			}
+			this.$editorTools.initSimpleEditor(stemEditor)
+			stemEditor.create()
+			this.stemEditor = stemEditor
+			
+			if (this.editInfo && this.editInfo.question) {
+				console.log('进入判断题Mounted编辑')
+				this.editSingleInfo = JSON.parse(JSON.stringify(this.editInfo))
+				this.stemContent = this.editSingleInfo.question
+				this.$nextTick(() => {
+					this.stemEditor.txt.html(this.editSingleInfo.question)
+				})
+			}
+		},
+		watch: {
+			editInfo: {
+				handler(newValue, oldValue) {
+					if (newValue) {
+						this.editSingleInfo = JSON.parse(JSON.stringify(newValue))
+						if (Object.keys(newValue).length > 0) {
+							this.stemContent = this.editSingleInfo.question
+							this.$nextTick(() => {
+								this.stemEditor.txt.html(this.editSingleInfo.question)
+							})
+						}
+					}
+				},
+				// immediate:true
+			},
+
+		}
+	}
+</script>
+<style lang="less">
+	@import "./BaseSingle.less";
+</style>
+
+<style>
+	.question-subjective .w-e-text-container{
+		height: 300px !important;
+	}
+	
+</style>

+ 4 - 44
TEAMModelOS/ClientApp/src/components/student-web/EventBasicInfo.vue

@@ -2,15 +2,9 @@
   <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) }}
-        </span>
-        <span class="title-mark" v-if=" eventType!='自主學習' &&from=='hiteach'">
-          {{ 'T'+this.$store.getters.getItemTitle.eventID.substr(0, 5) }}
-        </span>-->
-        <span
-          class="title-mark"
-        >{{$t("studentWeb.home.exam")}}</span>
+        <span class="title-mark" v-show="this.$store.getters.getItemTitle.eventType == 'exam'" >{{$t("studentWeb.home.exam")}}</span>
+        <span class="title-mark" v-show="this.$store.getters.getItemTitle.eventType == 'vote'" >{{$t("studentWeb.home.vote")}}</span>
+        <span class="title-mark" v-show="this.$store.getters.getItemTitle.eventType == 'survey'" >{{$t("studentWeb.home.survey")}}</span>
         {{ this.$store.getters.getItemTitle.name }}
       </h2>
     </i-col>
@@ -23,37 +17,19 @@
         <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>
           <svg-icon icon-class="teacher" class="base-info-icon" />{{ $t('studentWeb.baseInfo.teacher')}}
           <span class="base-info-text">{{ this.$store.getters.getItemTitle.teacher }}</span>
         </li>-->
-
         <li>
           <svg-icon icon-class="time" class="base-info-icon" />
-
-          <!--<span v-if="from == '通知' || from == 'hiteach'">
-            {{$t('studentWeb.baseInfo.postTime')}}
-          </span>-->
           <span>{{ !eventType ?  $t('studentWeb.baseInfo.period') :  $t('studentWeb.baseInfo.postTime') }}</span>
-
-
-
-
-
-
-
           <span class="base-info-text" v-if="from != '通知'&&from != 'hiteach'">
-              <span v-if="this.$store.getters.getItemTitle.eventType = 'exam'">
+              <span>
                   {{ !eventType ? dateFormat(this.$store.getters.getItemTitle.startTime) + "~" : ""}}
               </span>
               {{ dateFormat(this.$store.getters.getItemTitle.endTime) }}
-              <!--<span v-if="dateFormat(this.$store.getters.getItemTitle.endTime)>='2020.12.10'&& !eventType">{{$t('studentWeb.baseInfo.Closed')}}</span>-->
           </span>
-
-
-
-
           <!--<span
             class="base-info-text"
             v-if="from == '通知'||from == 'hiteach'"
@@ -115,22 +91,6 @@ 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";
-    //}
   },
 };
 </script>

+ 16 - 18
TEAMModelOS/ClientApp/src/components/student-web/EventView/BillBoardandLightBox.vue

@@ -30,19 +30,7 @@
 
     <div class="dec">
       <p>
-        <a
-          target="_blank"
-          class="referlink"
-          href="https://zh.wikipedia.org/wiki/%E6%AA%80%E9%A6%99%E5%B1%B1"
-        >{{$t('studentWeb.billboard.referlink')}}</a>
-        {{ this.$store.getters.getItemTitle.eDes
-        }}
-        <a
-          target="_blank"
-          class="referlink"
-          href="https://zh.wikipedia.org/wiki/%E6%AA%80%E9%A6%99%E5%B1%B1"
-        >{{$t('studentWeb.billboard.referlink')}}</a>
-        {{ this.$store.getters.getItemTitle.eDes }}
+          <span>{{voteData.name}}</span>
       </p>
 
       <Row :gutter="10" class="decImgs">
@@ -95,13 +83,14 @@
 
     <!--w><-->
     <div class="dec">
-      <li v-for="item in Math.floor(Math.random() * 3) + 1" :key="item">
+        <span>暂无附件</span>
+      <!--<li v-for="item in Math.floor(Math.random() * 3) + 1" :key="item">
         <a class="referdoclink" download href="./PlayerAPI_v1.0.6.pdf">
           <svg-icon icon-class="doc2" class="docIcon" />
           {{$t('studentWeb.billboard.reference')}} {{ item }} -
           {{ docname() }}
         </a>
-      </li>
+      </li>-->
     </div>
     <!--w><-->
   </div>
@@ -110,15 +99,24 @@
 <script>
 import { Random } from "mockjs";
 export default {
-  name: "BillBardandLightBox",
+        name: "BillBardandLightBox",
+        props: {
+            voteData: {
+                type: Object,
+                default: () => {
+                    return {}
+                }
+            }
+        },
   created() {
-    this.openLightBox = false;
+      this.openLightBox = false;
+      console.log(this.voteData)
   },
   data() {
     return {
       value1: 0,
       openLightBox: false,
-      targetImg: "",
+        targetImg: "",
       setting: {
         autoplay: false,
         dots: "outside",

+ 6 - 6
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContent.vue

@@ -9,20 +9,20 @@
       <div v-if="this.$store.getters.getItemTitle.eventType=='作業'">
         <Homework/>
       </div>-->
-      <div v-if="this.$store.getters.getItemTitle.eventType=='exam'">
+      <div v-if="this.$store.getters.getItemTitle.eventType == 'exam'">
         <PaperView/>
       </div>
-      <!--<div v-if="this.$store.getters.getItemTitle.eventType=='投票'">
+      <div v-if="this.$store.getters.getItemTitle.eventType == 'vote'">
         <Vote/>
       </div>
-      <div v-if="this.$store.getters.getItemTitle.eventType=='問卷'">
+      <div v-if="this.$store.getters.getItemTitle.eventType == 'survey'">
         <QuesNaire/>
-      </div>-->
+      </div>
     </div>
         <!-- 如果篩選時未選的時候預設顯示列表第一個-->
-    <div class="noSelected" v-if="!this.$store.getters.getIsSelectedNow">
+    <!--<div class="noSelected" v-if="!this.$store.getters.getIsSelectedNow">
         <h3>{{$t("studentWeb.event.selectActivity")}}</h3>
-    </div>
+    </div>-->
 
   </div>
 </template>

+ 283 - 156
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/Vote.vue

@@ -1,6 +1,5 @@
 <template>
   <div class="vote">
-   
       <div v-if="WarmMessageisOpen" class="warmMessage ">
         <div class="messageCard animate__animated animate__fadeInDown ">
           <h2>{{ $t("studentWeb.vote.voteSuccess") }}</h2>
@@ -9,8 +8,7 @@
           <p>{{ $t("studentWeb.vote.voteDes") }}</p>
         </div>
       </div>
- 
-    
+   
       <div v-if="isHintNextItem == true" class="warmMessage">
         <div class="messageCard previewNextItemCard animate__animated animate__fadeInDown" >
           <h2>前往下一個活動</h2>
@@ -55,7 +53,8 @@
     <br />
     <EventBasicInfo />
 
-    <div v-if="this.$store.getters.getItemTitle.isDone == false">
+    <div >
+       <!--超时-->
       <div v-if="this.$store.getters.getItemTitle.endTime <= '2020.02.10'">
         <br />
         <div class="dec animate__animated animate__bounceInLeft">
@@ -63,57 +62,55 @@
           <span class="warm-hint">{{ $t("studentWeb.vote.timeoutHint") }}</span>
         </div>
       </div>
-      <div v-if="this.$store.getters.getItemTitle.endTime > '2020.02.10'">
-        <BillBoardandLightBox />
-
-        <div class="title-rect-group">
-          <div class="title-rect" />
-          <h2 class="title-rect-name">{{ $t("studentWeb.vote.bollotbox") }}</h2>
-        </div>
-        <!--和評測模組一樣-->
-        <br />
-        Q:{{ this.$store.getters.getItemTitle.vote.voteDes }}
-        <br />
-        <br />
-        <div class="checkAnswer">
-          <label
-            class="testBtn"
-            v-for="(item, index) in this.$store.getters.getItemTitle.vote.voteQA
-              .ansDesc"
-            :key="index"
-          >
-            <input type="checkbox" :value="item" v-model="voteChecked" />
-            <div class="testbg">
-              <span>{{ index + 1 }}. {{ item }}</span>
-            </div>
-          </label>
-        </div>
-        <button class="uploadBtn" @click="submitMessage()">
-          <svg-icon icon-class="vote" class="uploadBtn-icon" />
-          <span v-if="isFirstVoted == false">{{ $t("studentWeb.vote.submit") }}</span>
-          <span v-else>{{ $t("studentWeb.vote.reVote") }}</span>
-        </button>
-        <span
-          class="clickbutnoChoosehint"
-          v-if="clickbutnoChoose == true && voteChecked == ''"
-          >請至少選一個選項,再按送出</span
-        >
+      <div>
+          <BillBoardandLightBox :voteData="voteInfo" />
+          <div class="vote-title">
+              <div class="title-rect-group">
+                  <div class="title-rect" />
+                  <h2 class="title-rect-name">{{ $t("studentWeb.vote.bollotbox") }}</h2>
+                  <p style="margin-left:15px;margin-top:2px">剩余票数 <span @click="getVote" style="font-size:16px">{{voteInfo.voteNum}}</span><span> 票</span></p>
+                  </div>
+          </div>
+          <!--和評測模組一樣-->
+          <div class="question-box"><p>1.</p><span v-html="voteInfo.description"></span></div>
+          <div class="checkAnswer">
+              <label class="testBtn"
+                     v-for="(item, index) in  voteInfo.options"
+                     :key="index">
+                  <input type="checkbox" :value="item" v-model="voteChecked" @click="getVote(item)" />
+                  <div class="testbg">
+                      <div class="vote-info">
+                          <span style="display:flex;margin-right:5px">{{item.code}}.<span v-html="item.value"></span></span>
+                      </div>
+                      <InputNumber v-model="item.count" 
+                                   :formatter="value => `${value}票`"
+                                   :parser="value => value.replace('票', '')"
+                                   :min="1"
+                                   v-if="voteInfo.repeat"
+                                   controls-outside
+                      >
+                      </InputNumber>
+                  </div>
+              </label>
+          </div>
+          <button class="uploadBtn" @click="submitMessage()">
+              <svg-icon icon-class="vote" class="uploadBtn-icon" />
+              <span v-if="isFirstVoted == false">{{ $t("studentWeb.vote.submit") }}</span>
+              <span v-else>{{ $t("studentWeb.vote.reVote") }}</span>
+          </button>
+          <span class="clickbutnoChoosehint"
+                v-if="clickbutnoChoose == true && voteChecked == ''">請至少選一個選項,再按送出</span>
       </div>
     </div>
 
-    <div
-      class="voteResults"
-      v-if="this.$store.getters.getItemTitle.isDone == true"
-    >
+    <!--<div class="voteResults" >
       <br />
       <h3 class="voteResultsMainRow ">
         {{ $t("studentWeb.vote.voteResult") }}
         <span class="voteResultsMainKeyWord animate__animated animate__flash">B</span>
       </h3>
-
       <br />
       <h3>1.{{ $t("studentWeb.vote.voteDesDefault") }}</h3>
-
       <br />
       <div v-for="item in item" :key="item">
         <div class="voteResultsItem" :class="{ voteResultSelect: item == 'B','animate__animated animate__headShake':item == 'B' }">
@@ -122,10 +119,8 @@
             class="voteResultsIcon"
             v-if="item == 'B'"
           />
-
           <p>{{ item }}. {{ itemRandomDesc() }}</p>
         </div>
-
         <br />
       </div>
       <br />
@@ -135,7 +130,7 @@
         </Card>
       </div>
       <br />
-    </div>
+    </div>-->
   </div>
 </template>
 
@@ -144,8 +139,6 @@ import EventBasicInfo from "../../EventBasicInfo";
 import BillBoardandLightBox from "../../EventView/BillBoardandLightBox";
 import { Random } from "mockjs";
 import VoteResultChart from "./VoteResultChart";
-import mockdata from "@/api/newData";
-import mockdataEn from "@/api/newDataEn";
 
 export default {
   name: "Vote",
@@ -154,115 +147,249 @@ export default {
     VoteResultChart,
     BillBoardandLightBox,
   },
-  created() {
-    this.WarmMessageisOpen = false;
-    this.voteChecked = [];
-    this.clickbutnoChoose = false;
-    this.isFirstVoted = false;
-    if(localStorage.getItem("lang") == ""){
-      this.mockdata = mockdata.eventList;
-    }
-    if (localStorage.getItem("lang") == "tw") {
-      this.mockdata = mockdata.eventList;
-    } else this.mockdata = mockdataEn.eventList;
-    
-  },
+        mounted() {
+            this.getVoteInfo()
+            this.WarmMessageisOpen = false;
+            this.voteChecked = [];
+            this.clickbutnoChoose = false;
+            this.isFirstVoted = false;
+        },
   data() {
-    return {
-      unhintNextItem: false,
-      mockdata: "",
-      nextItem: "", //存放下個活動預告
-      isHintNextItem: false,
-      isFirstVoted: false,
-      item: ["A", "B", "C", "D", "E"],
-      voteChecked: [],
-      WarmMessageisOpen: false,
-      clickbutnoChoose: false,
-    };
-  },
+      return {
+          unhintNextItem: false,
+          mockdata: "",
+          nextItem: "", //存放下個活動預告
+          isHintNextItem: false,
+          isFirstVoted: false,
+          item: ["A", "B", "C", "D", "E"],
+          voteChecked: [],
+          WarmMessageisOpen: false,
+          clickbutnoChoose: false,
+          voteInfo: {},
+          voteRes: {},
+          isVote: false,
+          value11: 1,
+          voteNum: 0,
 
-  methods: {
-    itemRandomDesc: function () {
-      if (localStorage.getItem("lang") == "tw") {
-        return Random.cparagraph(1, 7);
-      } else return Random.paragraph(1, 2);
-    },
-    previewNext() {
-      this.isPreviewNextItem = true;
-      let eventPageType = ["課前預習", "評量", "作業", "投票"];
-      let nextItems = [];
-      for (let i = 0; i < this.mockdata.length; i++) {
-        if (
-          eventPageType.includes(this.mockdata[i].eventType) &&
-          this.mockdata[i].isDone == false &&
-          this.$store.getters.getFinishedItem.includes(
-            this.mockdata[i].eventID
-          ) == false
-        ) {
-          nextItems.push(this.mockdata[i]); //引導使用者前往下一個未完成的項目
-        }
-      }
-      if (nextItems != "") {
-        if (
-          nextItems[0].eventName ==
-          this.mockdata[this.mockdata.length - 1].eventName
-        ) {
-          console.log("沒有下一個!");
-          this.nextItem = "allfinished";
-        }
-        console.log("下一個:" + nextItems[0].eventName);
-        this.nextItem = nextItems[0];
-      }
-    },
-    submitMessage() {
-      if (this.voteChecked != "") {
-        this.clickbutnoChoose = false;
-        this.WarmMessageisOpen = true;
-        this.isFirstVoted = true;
-        this.$store.commit(
-          "SavefinishedItemID",
-          this.$store.getters.getItemTitle.eventID
-        );
-        setTimeout(() => {
-          this.WarmMessageisOpen = false;
-        }, 2000);
-        this.previewNext();
-        //加入下一個活動的提示框
-        console.log(localStorage.getItem("hintNextItem")); //讀取是否顯示提示下一個
-        if (localStorage.getItem("hintNextItem") != "false") {
-          setTimeout(() => {
-            this.isHintNextItem = true;
-          }, 4000);
-        }
-      } else if (this.voteChecked == "") {
-        this.clickbutnoChoose = true;
-      }
-    },
-    cancelPreview() {
-      this.isHintNextItem = false;
-    },
-    gotoNextItem() {
-      if (this.unhintNextItem == true) {
-        this.$store.commit("sethintNextItem", false);
-      }
-       
-      if (this.nextItem != "allfinished") {
-        this.$store.commit("ChangeItemName", this.nextItem);
-          this.$router.push("/studentWeb/eventView#" + this.nextItem.eventID);
-        this.$store.commit("SetisFromInfoPoptpScroll", true);
-        //重置預習活動
-        this.$store.commit("PassPhaseTest", 0);
-        this.$store.commit("ChangeCurrentPhase", 0);
-        this.$store.commit("ChangeCurrentSelectedReadingName", 1);
-        this.$store.commit("ToggleSentCurrentphase", false);
-        this.$store.commit("SetCurrentQuestionNo", 0);
-        this.$store.commit("SetTrytestCount", [0, 0, 0]);
-        this.isHintNextItem = false;
-      } else {
-          this.$router.push("/studentWeb/homeView");
-      }
-    },
+      };
   },
+
+        methods: {
+            //获取投票结果数据
+            getVoteResult() {
+                if (this.$store.getters.getItemTitle.id) {
+                    this.voteRes = {}
+                    
+                    let params = {
+                        "id": "ff86005f-fc57-4a73-a5b7-551b45e446e7",
+                        "code": "Vote-1595321354",
+                        "userid": "123132123"
+                    }
+                    this.$api.studentWeb.getVoteResult(params).then(res => {
+                        if (res) {
+                            console.log('获取投票结果数据')
+                            console.log(res)
+                            this.voteRes = res
+                            this.timeStatus(this.voteInfo, this.voteRes)
+                            //this.voteInfo = res.vote
+                        }
+                    })
+                }
+            },
+            //获取投票数据
+            getVoteInfo() {
+                if (this.$store.getters.getItemTitle.id) {
+                    let params = {
+                        "id": this.$store.getters.getItemTitle.id,
+                        "code": this.$store.getters.getItemTitle.scode
+                    }
+                    this.$api.studentWeb.getVoteInfo(params).then(res => {
+                        if (res) {
+                            console.log('获取投票数据')
+                            this.voteInfo = res.vote
+                            console.log(this.voteInfo)
+                            this.getVoteResult()
+                        }
+                        })
+                }
+            },
+            //获取投票结果
+            getVote(data) {
+                if (!this.voteInfo.repeat) {
+                    this.voteChecked.length = 0
+                    this.voteChecked.push(data)
+                }
+                console.log(this.voteChecked)
+            },
+            //获取当前时间状态
+            getDates(day, type) {
+                let zdate = new Date();
+                let edate;
+                if (type == 1) {
+                    edate = new Date(zdate.getTime() - (day * 24 * 60 * 60 * 1000));
+                } else {
+                    edate = new Date(zdate.getTime() + (day * 24 * 60 * 60 * 1000));
+                }
+                return edate;
+            },
+            //投票活动的时间状态 data投票活动信息  time 已投票数据
+            timeStatus(data, time) {
+                console.log(data,time)
+                let date = (new Date()).getTime() //当前时间
+                let status = false //判断时间是否过期或处于当前时间段
+                //处于活动时间内
+                let inDate = new Date(date)
+                let year = inDate.getFullYear()
+                let month = inDate.getMonth()
+                console.log('时间判定测试')
+                if (data.startTime <= date <= data.endTime && time.records.length > 0) {
+                    switch (data.times) {
+                        case 'day':
+                            let daySart = new Date(new Date(new Date().toLocaleDateString()).getTime()).getTime()
+                            let dayEnd = new Date(new Date(new Date().toLocaleDateString()).getTime() + 24 * 60 * 60 * 1000 - 1).getTime()
+                            if (daySart <= date <= dayEnd) {
+                                console.log('day')
+                            } 
+                            break;
+                        //是否为本周
+                        case 'week':  
+                            let curWeek = date.getDay();           //获取当前星期几    
+                            let monday = this.getDates((curWeek), 1).getTime(); //计算出星期一    
+                            let sunday = this.getDates((7 - curWeek), 2).getTime(); //计算出星期天
+                            if (monday <= date <= sunday) {
+                                status = true
+                                console.log('week')
+                            } 
+                            break;
+                        //是否为本月
+                        case 'month': 
+                            let firstDay = new Date(year, month, 1).getTime()
+                            let lastDay = new Date(year, month + 1, 1).getTime() - 1
+                            if (firstDay <= date <= lastDay) {
+                                console.log('month')
+                            }
+                            break;
+                        case 'year':
+                            let firstYear = new Date(year,0,1).getTime()
+                            let lastYear = new Date(year + 1, 0, 0).getTime()
+                            if (firstYear <= date <= lastYear) {
+                                console.log('year')
+                            }
+                            break;
+                        default:
+                    }
+                } else {
+                    status = false
+                    return status
+                }
+            },
+            itemRandomDesc: function () {
+                if (localStorage.getItem("lang") == "tw") {
+                    return Random.cparagraph(1, 7);
+                } else return Random.paragraph(1, 2);
+            },
+            previewNext() {
+                this.isPreviewNextItem = true;
+                let eventPageType = ["課前預習", "評量", "作業", "投票"];
+                let nextItems = [];
+                for (let i = 0; i < this.mockdata.length; i++) {
+                    if (
+                        eventPageType.includes(this.mockdata[i].eventType) &&
+                        this.mockdata[i].isDone == false &&
+                        this.$store.getters.getFinishedItem.includes(
+                            this.mockdata[i].eventID
+                        ) == false
+                    ) {
+                        nextItems.push(this.mockdata[i]); //引導使用者前往下一個未完成的項目
+                    }
+                }
+                if (nextItems != "") {
+                    if (
+                        nextItems[0].eventName ==
+                        this.mockdata[this.mockdata.length - 1].eventName
+                    ) {
+                        console.log("沒有下一個!");
+                        this.nextItem = "allfinished";
+                    }
+                    console.log("下一個:" + nextItems[0].eventName);
+                    this.nextItem = nextItems[0];
+                }
+            },
+            submitMessage() {
+                if (this.voteChecked != "") {
+                    this.clickbutnoChoose = false;
+                    this.WarmMessageisOpen = true;
+                    this.isFirstVoted = true;
+                    this.$store.commit(
+                        "SavefinishedItemID",
+                        this.$store.getters.getItemTitle.eventID
+                    );
+                    setTimeout(() => {
+                        this.WarmMessageisOpen = false;
+                    }, 2000);
+                    this.previewNext();
+                    //加入下一個活動的提示框
+                    console.log(localStorage.getItem("hintNextItem")); //讀取是否顯示提示下一個
+                    if (localStorage.getItem("hintNextItem") != "false") {
+                        setTimeout(() => {
+                            this.isHintNextItem = true;
+                        }, 4000);
+                    }
+                } else if (this.voteChecked == "") {
+                    this.clickbutnoChoose = true;
+                }
+            },
+            cancelPreview() {
+                this.isHintNextItem = false;
+            },
+            gotoNextItem() {
+                if (this.unhintNextItem == true) {
+                    this.$store.commit("sethintNextItem", false);
+                }
+
+                if (this.nextItem != "allfinished") {
+                    this.$store.commit("ChangeItemName", this.nextItem);
+                    this.$router.push("/studentWeb/eventView#" + this.nextItem.eventID);
+                    this.$store.commit("SetisFromInfoPoptpScroll", true);
+                    //重置預習活動
+                    this.$store.commit("PassPhaseTest", 0);
+                    this.$store.commit("ChangeCurrentPhase", 0);
+                    this.$store.commit("ChangeCurrentSelectedReadingName", 1);
+                    this.$store.commit("ToggleSentCurrentphase", false);
+                    this.$store.commit("SetCurrentQuestionNo", 0);
+                    this.$store.commit("SetTrytestCount", [0, 0, 0]);
+                    this.isHintNextItem = false;
+                } else {
+                    this.$router.push("/studentWeb/homeView");
+                }
+            },
+        },
+        computed: {
+            listData() {
+                return this.$store.getters.getItemTitle.id;
+            }
+        },
+        watch: {
+            $route: {
+                handler() {
+                    this.isExamDown = false
+                    this.voteInfo = {}
+                    this.getVoteInfo()
+                },
+                // 深度观察监听
+                deep: true
+            },
+            listData: {
+                handler() {
+                    if (this.$store.getters.getItemTitle.id !== '') {
+                        console.log('88888888888')
+                        this.getVoteInfo()
+                    }
+                },
+                // 深度观察监听
+                deep: true
+            },
+        },
 };
 </script>
 

+ 84 - 83
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventList.vue

@@ -5,7 +5,7 @@
             <div class="event-selector-block">
                 <!---搜尋--->
                 <div class="search">
-                    <div class="search-btn" @click="showSearch()">
+                    <div class="search-btn" @click="openSearch = !openSearch">
                         <svg-icon icon-class="search"
                                   class="searchIcon"
                                   :class="{ openSearch: openSearch }" />
@@ -49,7 +49,8 @@
                 <ul class="icon-selector" v-if="hideIconbtn == false">
                     <li class="icon-btn"
                         @click="selectAllType()"
-                        :class="{'icon-btn-selected': eventTypeCheckers == ''}">
+                        :class="{'icon-btn-selected': eventTypeCheckers == ''}"
+                        >
                         <svg-icon icon-class="alltext" class="innerIcon Icon-0" />
                     </li>
                     <label v-for="(iconBtn, index) in typeNametoIcon" :key="index">
@@ -75,30 +76,45 @@
                     <br />
                     {{ $t("studentWeb.empty") }}
                 </div>
-                <div :id="`event${item.eventID}`"
+                <div :id="`event${item.id}`"
                     class="list-item"
                     @click="sentSelectedEventTitle(item)"
                     :class="{ 'list-item-selected': selectedCondition(item) }"
-                    v-for="(item, index) in eventList"
+                    v-for="(item, index) in eventShow"
                     v-if="isListNoItem == false"
                     :key="index">
                     <ul>
                         <li class="list-item-icon">
                             <svg-icon v-if="item.eventType == 'homeWork'" icon-class="doc" />
-                            <svg-icon v-if="item.eventType == 'preview'"
-                                      icon-class="selflearninginTime" />
-                            <svg-icon v-if="item.eventType == 'exam'"
-                                      icon-class="multiTest" />
-                            <svg-icon v-if="item.eventType == 'vote'" icon-class="vote" />
-                            <svg-icon v-if="item.eventType == 'que'"
-                                      icon-class="quesnaire" />
+                            <svg-icon v-if="item.eventType == 'preview'"  icon-class="selflearninginTime" />
+                            <svg-icon v-if="item.eventType == 'exam'"     icon-class="multiTest" />
+                            <svg-icon v-if="item.eventType == 'vote'"     icon-class="vote" />
+                            <svg-icon v-if="item.eventType == 'survey'"   icon-class="quesnaire" />
                         </li>
                         <li class="list-item-info"
                             v-if="item.eventType == 'exam'">
                             <p class="list-item-title">
                                 <span class="list-item-typeMark">{{item.scope == 'school' ? $t('studentWeb.public.schoolExam'):$t('studentWeb.public.privateExam')}}</span>
-                                <span class="list-item-typeMark"
-                                      v-if="getCurrentLang() == 'en'">{{ transTypetoEn(item.eventType) }}</span>
+                                <span>{{ item.name }}</span>
+                            </p>
+                            <p class="list-item-time">
+                                {{ dateFormat(item.startTime) }} ~ {{ dateFormat(item.endTime) }}
+                            </p>
+                        </li>             
+                        <li class="list-item-info"
+                            v-if="item.eventType == 'vote'">
+                            <p class="list-item-title">
+                                <span class="list-item-typeMark">{{item.scope == 'school' ? $t('studentWeb.public.schoolVote'):$t('studentWeb.public.privateVote')}}</span>
+                                <span>{{ item.name }}</span>
+                            </p>
+                            <p class="list-item-time">
+                                {{ dateFormat(item.startTime) }} ~ {{ dateFormat(item.endTime) }}
+                            </p>
+                        </li>       
+                        <li class="list-item-info"
+                            v-if="item.eventType == 'survey'">
+                            <p class="list-item-title">
+                                <span class="list-item-typeMark">{{item.scope == 'school' ? $t('studentWeb.public.schoolSurvey'):$t('studentWeb.public.privateSurvey')}}</span>
                                 <span>{{ item.name }}</span>
                             </p>
                             <p class="list-item-time">
@@ -163,13 +179,12 @@
                         iconClass: "vote",
                     },
                     {
-                        eventType: "que",
+                        eventType: "survey",
                         iconClass: "quesnaire",
                     },
                 ],
-
                 mockdata: "",
-                eventPageType: ["preview", "exam", "homeWork", "vote", "que"], //本頁出現的類型
+                eventPageType: ["preview", "exam", "homeWork", "vote", "survey"], //本頁出現的類型
                 openSearch: false, //打開搜尋器
                 search: "",
                 alreadyScrolltimes: 0,
@@ -177,7 +192,8 @@
                 selectedEventStatusNow: this.$t('studentWeb.event.allStatus'),
                 hideIconbtn: false,
                 isListNoItem: false, //清單為空
-                eventList: []
+                eventList: [],
+                eventShow:[]
             };
         },
 
@@ -213,6 +229,7 @@
             },
         },
         methods: {
+            //获取评测信息
             getPaper() {
                 this.eventList.length = 0
                 this.isListNoItem = true;
@@ -221,9 +238,7 @@
                         'id': this.$store.state.user.studentProfile.classinfo.id,
                         'studentId': this.$store.state.userInfo.sub
                     }
-                    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('-')
@@ -232,13 +247,41 @@
                                 data.push(item)
                             }
                         }
-                        this.eventList = data.sort(function (a, b) {
-                            return b.startTime - a.startTime //时间正序
-                        });
-                        this.isListNoItem = false
+                        this.eventList = [...data]
+                        this.getActivityInfo()
                     })
                 }
             },
+            //***临时获取投票数据
+            getActivityInfo() {
+                if (this.$store.state.user.studentProfile.classinfo.id !== "") {
+                    let params = {
+                        "userid": "123",
+                        "classes": [
+                            "hbcn0906",
+                            "hbcn0910",
+                            "hbcn0989"
+                        ],
+                        "school": "hbcn"
+                    }
+                    this.$api.studentWeb.getActivityInfo(params).then(res => {
+                        if (res.datas.length) {
+                            let data = []
+                            for (let item of res.datas) {
+                                item.eventType = item.type
+                                data.push(item)
+                            }
+                            this.eventList = [...this.eventList, ...data]
+                            this.eventList = this.eventList.sort(function (a, b) {
+                                return b.startTime - a.startTime //时间正序
+                            });
+                            this.eventShow = [...this.eventList]
+                            this.isListNoItem = false
+                        }
+                    })
+                }
+            },
+            //时间格式化处理
             dateFormat(timestamp) {
                 var date = new Date(timestamp)
                 var Y = date.getFullYear() + '-'
@@ -253,15 +296,6 @@
                     item.isDone == false
                 );
             },
-            getCurrentLang() {
-                return localStorage.getItem("lang");
-            },
-            transTypetoEn(type) {
-                if (type == "vote") return "Vote";
-                else if (type == "exam") return "Exam";
-                else if (type == "homework") return "Homework";
-                else if (type == "preview") return "Preview";
-            },
             //取得目前滑鼠座標
             getCursorPosition() {
                 var x = 0;
@@ -307,9 +341,6 @@
 
                 this.$store.commit("voteResulthover", hoverItem);
             },
-            randomScore: function () {
-                return Random.integer(70, 96);
-            },
             sentEventStatus(status) {
                 if (status == "reMake" || status == "reExam") {
                     this.eventTypeCheckers = [];
@@ -320,43 +351,32 @@
             predealMockdatafirstItem() {
                 //選中第一個
                 let currentfilterArray = [];
+                this.eventShow.length = 0
                 for (let i = 0; i < this.eventList.length; i++) {
                     //沒有篩選類型+沒有篩選狀態的情況
                     if (this.eventPageType.includes(this.eventList[i].eventType)) {
-                        if (
-                            this.eventTypeCheckers == ""
-                            //&&
-                            //this.selectedEventStatusNow == "所有活動狀態"
-                        ) {
+                        if ( this.eventTypeCheckers.length == 0) {
                             currentfilterArray.push(this.eventList[i]);
                         }
                         //沒篩選類型+有篩選狀態的情況
-                        else if (
-                            this.eventTypeCheckers == "" &&
-                            this.dropDownShowCondition(
-                                this.selectedEventStatusNow,
-                                this.eventList[i]
-                            )
-                        ) {
+                        else if (this.eventTypeCheckers.length == 0 &&
+                            this.dropDownShowCondition(this.selectedEventStatusNow, this.eventList[i])) {
                             currentfilterArray.push(this.eventList[i]);
                         }
                         //有篩選類型+有篩選狀態的情況
                         else if (
-                            this.eventTypeCheckers != "" &&
+                            this.eventTypeCheckers.length !== 0 &&
                             this.eventTypeCheckers.includes(this.eventList[i].eventType) &&
-                            this.dropDownShowCondition(
-                                this.selectedEventStatusNow,
-                                this.eventList[i]
-                            )
+                            this.dropDownShowCondition(this.selectedEventStatusNow,this.eventList[i])
                         ) {
                             currentfilterArray.push(this.eventList[i]);
                         }
                     }
                 }
-                if (currentfilterArray != "") {
-
+                if (currentfilterArray.length !== 0) {
+                    this.eventShow = [...currentfilterArray]
                     this.isListNoItem = false;
-                    this.sentSelectedEventTitle(currentfilterArray[0]);
+                    //this.sentSelectedEventTitle(currentfilterArray[0]);
                     document.querySelectorAll(".event-list .list-block")[0].scrollBy({
                         top: -document.querySelectorAll(".event-list .list-block")[0]
                             .scrollHeight,
@@ -364,10 +384,10 @@
                     });
                 } else this.isListNoItem = true; //清單空值樣式配置
             },
+            //下拉数据筛选
             dropDownShowCondition(status, item) {
                 if (status == "reMake") {
                     this.hideIconbtn = true;
-
                     return (
                         item.endTime <= "2020.02.10" &&
                         item.eventType == "homework" &&
@@ -407,13 +427,12 @@
                     return true;
                 }
             },
+            //选择所有
             selectAllType() {
                 this.eventTypeCheckers = [];
                 this.predealMockdatafirstItem();
             },
-            showSearch() {
-                this.openSearch = !this.openSearch;
-            },
+
             sentEvenType(eventype) {
                 this.$store.commit("ChangeFilterType", eventype);
                 if (this.$store.getters.getFilterType == "all") {
@@ -452,7 +471,6 @@
             },
 
             sentSelectedEventTitle(item) {
-                console.log(item)
                 this.$router.push({
                     path: "/studentWeb/eventView",
                     query: {
@@ -461,15 +479,15 @@
                     }
                 })
                 this.$store.commit("SetPaperInfo", item)
-                //螢幕寬度<767px時,直接關掉sidebar
+                ////螢幕寬度<767px時,直接關掉sidebar
                 if (window.innerWidth <= 768) {
-                    this.$store.commit("ToggleSidebar", false);
+                   this.$store.commit("ToggleSidebar", false);
                 }
-                //改變ItemName的狀態 vuex mutations
+                ////改變ItemName的狀態 vuex mutations
                 this.$store.commit("ChangeItemName", item);
-                //重置問卷
+                ////重置問卷
                 this.$store.commit('resetSurvey', true)
-                //重置預習活動
+                ////重置預習活動
                 this.$store.commit("PassPhaseTest", 0);
                 this.$store.commit("ChangeCurrentPhase", 0);
                 this.$store.commit("ChangeCurrentSelectedReadingName", 1);
@@ -507,29 +525,12 @@
             },
 
             scrollList() {
-                //let targetItem = document.getElementById(
-                //    `event${this.$store.getters.getItemTitle.eventID}`
-                //);
-                //if (targetItem.style.display == "none") {
-                //    targetItem.style.display = "block";
-                //}
-                //console.log(
-                //    this.$store.getters.getScrollvalue,
-                //    document.getElementById(
-                //        `event${this.$store.getters.getItemTitle.eventID}`
-                //    ).offsetTop
-                //);
-                //document.querySelectorAll(".event-list .list-block")[0].scrollBy({
-                //    //畫面中有多個tabpane,指定為event-list中的進行捲動
-                //    top: targetItem.offsetTop > 600 ? targetItem.offsetTop - 128 : 0,
-                //    behavior: "smooth",
-                //});
             },
 
             selectedCondition(item) {
                 if (
                     this.$store.getters.getIsSelectedNow == true &&
-                    this.$store.getters.getItemTitle.name == item.name
+                    this.$store.getters.getItemTitle.id == item.id
                 ) {
                     return true;
                 } else return false;

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

@@ -1,7 +1,7 @@
 <template>
     <div class="event-view">
         <EventList :class="{ 'hide-sidebars': $store.getters.getSidebarisOpen == false}"></EventList>
-        <EventContentArea :class="{'eventContentArea-Span': $store.getters.getSidebarisOpen == false,}"></EventContentArea>
+        <EventContentArea :class="{'eventContentArea-Span': $store.getters.getSidebarisOpen == false}"></EventContentArea>
         <!--<CommentList v-if=" $store.getters.getOpenCommentList == true && $store.getters.getSidebarisOpen == false" />-->
     </div>
 </template>
@@ -22,7 +22,7 @@
                 MyNo: "4", //接收NavBar 選定的那一頁icon標示
                 MyName: "",
                 mockdata: "",
-                eventPageType: ["preview", "exam", "homework", "vote", "quesnaire"] //本頁出現的類型
+                eventPageType: ["preview", "exam", "homework", "vote", "survey"] //本頁出現的類型
             };
         },
         created() {

+ 43 - 1
TEAMModelOS/ClientApp/src/components/student-web/HomeView/MissionListCard.vue

@@ -27,7 +27,8 @@
                         </li>
                         <li class="list-item-info" :class="{ 'list-item-infonoPie': isNoPie(item) }">
                             <p class="list-item-title" :class="{ 'list-item-titleEn': getCurrentLang() == 'en' }">
-                                <span class="list-item-typeMark">{{item.scope == 'school'? $t('studentWeb.public.schoolExam'):$t('studentWeb.public.privateExam')}}</span>
+                                <span v-show="item.eventType == 'exam'" class="list-item-typeMark">{{item.scope == 'school'? $t('studentWeb.public.schoolExam'):$t('studentWeb.public.privateExam')}}</span>
+                                <span v-show="item.eventType == 'vote'" class="list-item-typeMark">{{item.scope == 'school'? $t('studentWeb.public.schoolVote'):$t('studentWeb.public.privateVote')}}</span>
                                 <span>{{ item.name }}</span>
                                 <div style="float:right;margin-top:-20px">
                                     <div class="list-item-unDone" v-show="item.progress == 'going'">
@@ -114,6 +115,47 @@
                         this.testData = data.sort(function (a, b) {
                             return b.startTime - a.startTime //时间正序
                         });
+                        this.getActivityInfo()
+
+                    })
+                }
+            },
+            getActivityInfo() {
+                if (this.$store.state.user.studentProfile.classinfo.id !== "") {
+                    let params = {
+                        "userid": "123",
+                        "classes": [
+                            "hbcn0906",
+                            "hbcn0910",
+                            "hbcn0989"
+                        ],
+                        "school": "hbcn"
+                    }
+                    this.$api.studentWeb.getActivityInfo(params).then(res => {
+                        console.log(res)
+                        if (res.datas.length) {
+                            let data = []
+                            for (let item of res.datas) {
+                                item.eventType = 'vote'
+                                data.push(item)
+                            }
+                            this.testData = [...this.testData, ...data]
+                            this.testData = this.testData.sort(function (a, b) {
+                                return b.startTime - a.startTime //时间正序
+                            });
+                        }
+                        console.log(this.testData)
+                        //let data = []
+                        //for (let item of res.props) {
+                        //    let code = item.code.split('-')
+                        //    if (code[0] == 'Exam') {
+                        //        item.eventType = 'exam'
+                        //        data.push(item)
+                        //    }
+                        //}
+                        //this.testData = data.sort(function (a, b) {
+                        //    return b.startTime - a.startTime //时间正序
+                        //});
                     })
                 }
             },

+ 0 - 3
TEAMModelOS/ClientApp/src/components/syllabus/DragTree.vue

@@ -436,9 +436,6 @@
         padding: 20px;
     }
 
-    .related-modal .ivu-modal-body {
-        height: 700px;
-    }
 
     .tree-modal .ivu-modal-header {
         border-bottom: none;

+ 1 - 1
TEAMModelOS/ClientApp/src/components/vote/BaseVoteBar.vue

@@ -78,7 +78,7 @@
                     series: [{
                         type: 'bar',
                         data: data.map(item => item.value),
-                        barMaxWidth: 80, //柱子宽度
+                        barMaxWidth: 50, //柱子宽度
                         itemStyle: {
                             normal: {
                                 color: new that.$echarts.graphic.LinearGradient(0, 0, 0, 1, [{

+ 56 - 46
TEAMModelOS/ClientApp/src/components/vote/BaseVoteForm.vue

@@ -94,7 +94,7 @@
 				default: false
 			}
 		},
-		data() {
+		data(vm) {
 			return {
 				classType: 'private',
 				curVoteItem: null,
@@ -160,7 +160,7 @@
 				ruleValidate: {
 					name: [{
 						required: true,
-						message: '投票名称不能为空',
+						message: vm.$t('vote.form.ruleName'),
 						trigger: 'blur'
 					}],
 					publishModel: [{
@@ -169,28 +169,28 @@
 					}],
 					description: [{
 						required: true,
-						message: '投票描述不能为空',
+						message: vm.$t('vote.form.ruleDescription'),
 						trigger: 'blur'
 					}],
 					classes: [{
 						required: true,
-						message: '投票对象不能为空'
+						message: vm.$t('vote.form.ruleClasses')
 					}],
 					endTime: [{
 						required: true,
 						type: 'date',
-						message: '请设置起止时间',
+						message:vm.$t('vote.form.ruleTime'),
 						trigger: 'change'
 					}],
 					startTime: [{
 						required: true,
 						type: 'date',
-						message: '请设置起止时间',
+						message: vm.$t('vote.form.ruleTime'),
 						trigger: 'change'
 					}],
 					rangeTime: [{
 						required: true,
-						message: '日期不能为空!',
+						message: vm.$t('vote.form.ruleDate'),
 						trigger: 'change',
 						type: 'array'
 					}]
@@ -210,15 +210,15 @@
 				console.log(arr)
 				if (arr[0] === '') {
 					this.voteForm.rangeTime = null
-					this.ruleValidate.rangeTime[0].message = '日期不能为空!'
+					this.ruleValidate.rangeTime[0].message = this.$t('vote.form.ruleDate')
 				} else if (this.getTimestampByString(arr[0]) < Date.now()) {
 					this.voteForm.rangeTime = null
-					this.ruleValidate.rangeTime[0].message = '开始时间不能早于当前时间!'
+					this.ruleValidate.rangeTime[0].message = this.$t('vote.form.ruleStartTime')
 				} else {
 					this.voteForm.startTime = this.getTimestampByString(arr[0])
 					this.voteForm.endTime = this.getTimestampByString(arr[1])
 					this.voteForm.rangeTime = arr
-					this.ruleValidate.rangeTime[0].message = '日期不能为空!'
+					this.ruleValidate.rangeTime[0].message = this.$t('vote.form.ruleDate')
 				}
 				console.log(this.rangeTime)
 			},
@@ -237,43 +237,53 @@
 				this.isBtnLoading = true
 				this.$refs[name].validate(async (valid) => {
 					if (valid && this.getSimpleText(this.voteForm.description) && this.voteOptionsContent.length) {
-						console.log(this.voteForm)
-						let params = Object.assign({}, this.defaultParams)
-						let target = []
-						let fileName = this.$tools.guid()
-						let isReset = this.voteForm.isReset.length > 0
-						params.code = this.getCurCode
-						params.scope = this.$route.name === 'personalVote' && this.classType === 'school' ? 'teacher' : this.getCurScope
-						params.name = this.voteForm.name
-						params.startTime = this.voteForm.startTime
-						params.endTime = this.voteForm.endTime
-						params.description = this.voteForm.description
-						params.selectMax = this.voteForm.selectMax || 1
-						params.secret = this.voteForm.secret.indexOf('secret') > -1
-						params.repeat = this.voteForm.repeat.indexOf('repeat') > -1
-						params.options = this.voteOptionsContent
-						// 新增参数
-						params.creatorId = this.$store.state.userInfo.TEAMModelId
-						params.owner = this.$route.name === 'personalVote' && this.classType === 'private' ? this.$store.state.userInfo
-							.TEAMModelId : this.$store.state.userInfo.schoolCode
-						params.voteNum = this.voteForm.selectMax
-						params.times = this.voteForm.times
-
-						if (this.isEdit && this.editInfo.id && this.editInfo.code) {
-							params.id = this.editInfo.id
-						}
-						params.classes = this.voteForm.classes
-						console.log(params)
-						/* 保存BLOB以及COSMOS */
-						this.saveorUpdateVote(params).then(res => {
-							this.$Message.success((this.isEdit && this.editInfo.id) ? this.$t('vote.form.editSuc') : this.$t(
-								'vote.form.addSuc'))
-							this.$emit('onAddSuccess')
-							this.isBtnLoading = false
-						}).catch(error => {
+						if(this.voteForm.selectMax > this.voteOptionsContent.length && this.voteForm.repeat.indexOf('repeat') === -1){
+							this.$Message.warning('当前投票数大于选项数,请务必勾选 允许重复投票 !')
 							this.isBtnLoading = false
-							console.log(error)
-						})
+						}else{
+							if(this.voteForm.selectMax === 1){
+								this.voteForm.repeat = []
+							}
+							console.log(this.voteForm)
+							let params = Object.assign({}, this.defaultParams)
+							let target = []
+							let fileName = this.$tools.guid()
+							let isReset = this.voteForm.isReset.length > 0
+							params.code = this.getCurCode
+							params.scope = this.getCurScope
+							params.name = this.voteForm.name
+							params.startTime = this.voteForm.startTime
+							params.endTime = this.voteForm.endTime
+							params.description = this.voteForm.description
+							params.selectMax = this.voteForm.selectMax || 1
+							params.secret = this.voteForm.secret.indexOf('secret') > -1
+							params.repeat = this.voteForm.repeat.indexOf('repeat') > -1
+							params.options = this.voteOptionsContent
+							// 新增参数
+							params.creatorId = this.$store.state.userInfo.TEAMModelId
+							params.owner = this.$route.name === 'personalVote' && this.classType === 'private' ? this.$store.state.userInfo
+								.TEAMModelId : this.$store.state.userInfo.schoolCode
+							params.voteNum = this.voteForm.selectMax
+							params.times = this.voteForm.times
+							
+							if (this.isEdit && this.editInfo.id && this.editInfo.code) {
+								params.id = this.editInfo.id
+							}
+							params.classes = this.voteForm.classes
+							console.log(params)
+							/* 保存BLOB以及COSMOS */
+							this.saveorUpdateVote(params).then(res => {
+								this.$Message.success((this.isEdit && this.editInfo.id) ? this.$t('vote.form.editSuc') : this.$t(
+									'vote.form.addSuc'))
+								this.$emit('onAddSuccess')
+								this.isBtnLoading = false
+							}).catch(error => {
+								this.isBtnLoading = false
+								console.log(error)
+							})
+						}
+						
+						
 
 					} else {
 						this.isBtnLoading = false

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

@@ -179,6 +179,49 @@ html[white]{ /*白色主題*/
 		bottom: 0;
 	}
 	
+	
+	/* 修改iview Modal样式 */
+	
+	.related-modal .ivu-modal-content {
+		background: #3c3c3c;
+		overflow: hidden;
+		color: #fff;
+	}
+	
+	.related-modal .ivu-modal-body {
+		height: 400px;
+		padding: 20px;
+	}
+	
+	.related-modal .ivu-modal-body {
+		height: 700px;
+	}
+	
+	.related-modal .ivu-modal-header {
+		border-bottom: none;
+		font-size: 18px;
+		font-weight: bold;
+		padding: 25px;
+	}
+	
+	.related-modal .modal-content {
+		padding: 0 35px 30px 35px;
+	}
+	
+	.related-modal .modal-btn {
+		margin-left: 2%;
+		width: 96%;
+		height: 40px;
+		background: rgb(11, 151, 117);
+		border: none;
+		color: #fff;
+		margin-top: 30px;
+	}
+	
+	.related-modal .choose-content {
+		height: 85%;
+	}
+	
 	/*横向垂直水平居中*/
 	.flex-row-center {
 	    display: flex;

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

@@ -0,0 +1,6 @@
+export default{
+    error500:'服务器错误!',
+    error401:'登录状态已过期!请重新登录!',
+    error404:'未访问到接口地址!',
+	error:'接口异常!'
+}

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

@@ -28,6 +28,7 @@ import home from './home'
 import vote from './vote'
 import survey from './survey'
 import updModal from './updModal'
+import http from './http'
 
 export default {
   schoolBaseInfo,
@@ -60,6 +61,7 @@ export default {
   vote,
   survey,
   updModal,
+  http,
   test: '测试',
   formConfigP: {
     input: '请输入',

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

@@ -6,12 +6,18 @@ export default {
         "logout": "登出",
         "joinClass": "加入课程",
         "exam": "评量",
+        "vote": "投票",
+        "survey": "问卷",
     },
     "public": {
         "going": "进行中",
         "finish": "已结束",
         "schoolExam": "校级评测",
         "privateExam": "个人评测",
+        "schoolVote": "校级投票",
+        "privateVote": "个人投票",
+        "schoolSurvey": "校级问卷",
+        "privateSurvey":"个人问卷",
         "search":"请输入查询内容..."
     },
     "event": {

+ 24 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/survey.js

@@ -13,6 +13,7 @@ export default {
 	single:'单选题',
 	multiple:'多选题',
 	judge:'判断题',
+	subjective:'问答题',
 	defaultName:'预设问卷名称',
 	isExistTip:'已存在未保存的问卷活动!',
 	getDataFailTip:'获取数据失败!',
@@ -49,6 +50,28 @@ export default {
 		noOptionTip:'问卷选项个数不能为空!',
 		attachmentMaxTip:'最多只能上传5个附件',
 		optionNumsTip:'最多只能有10个选项',
-		noMatchDataTip:'未匹配数据'
+		noMatchDataTip:'未匹配数据',
+		ruleName:'问卷名称不能为空',
+		ruleDescription:'问卷描述不能为空',
+		ruleClasses:'问卷对象不能为空',
+		ruleTime:'请设置起止时间',
+		ruleDate:'日期不能为空',
+		ruleStartTime:'开始时间不能早于当前时间'
+	},
+	questionaire:{
+		noItemData:'暂无题目数据',
+		edit:'编辑',
+		remove:'移除',
+		analysisData:'统计数据',
+		add:'新增',
+		item:'题目',
+		confirm:'确认',
+		single:'单选',
+		multiple:'多选',
+		judge:'判断',
+		subjective:'问答',
+		noCompleteTip:'请填写完整',
+		confirmTitle:'温馨提示',
+		confirmText:'确认删除该题目?'
 	}
 }

+ 7 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/vote.js

@@ -51,6 +51,12 @@ export default {
 		noOptionTip:'投票选项个数不能为空!',
 		attachmentMaxTip:'最多只能上传5个附件',
 		optionNumsTip:'最多只能有10个选项',
-		noMatchDataTip:'未匹配数据'
+		noMatchDataTip:'未匹配数据',
+		ruleName:'投票名称不能为空',
+		ruleDescription:'投票描述不能为空',
+		ruleClasses:'投票对象不能为空',
+		ruleTime:'请设置起止时间',
+		ruleDate:'日期不能为空',
+		ruleStartTime:'开始时间不能早于当前时间'
 	}
 }

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

@@ -27,8 +27,8 @@ export default {
 	},
 	single: '單選題',
 	multiple: '复选题',
-	judge: '判斷題',
-	complete: '填題',
+	judge: '是非題',
+	complete: '填題',
 	subjective: '問答題',
 	connector: '連線題',
 	correct: '改錯題',
@@ -182,7 +182,7 @@ export default {
 		importTips: '導入注意事項',
 		tips1: '點擊上方上傳圖標選擇檔案',
 		tips2: '只支持“.docx,.xlsx,.xls”格式的檔案導入,請按照範本格式導入',
-		tips3: '導入題型暫時只支持單選、多選、判斷、填空、問答以及綜合題型',
+		tips3: '導入題型暫時只支持單選、多選、是非、填充、問答以及綜合題型',
 		tips4: '請保持範本語言與當前瀏覽器語言一致',
 		tips5: '更多注意事項請查看範本製作詳情說明',
 		warningTips1: '上傳格式僅支持 docx/xlsx/xls ,請重新上傳!',

+ 6 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/http.js

@@ -0,0 +1,6 @@
+export default {
+	error500: '服務器錯誤!',
+	error401: '登入狀態已過期!請重新登入!',
+	error404: '未訪問到介面地址!',
+	error: '介面异常!'
+}

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

@@ -28,6 +28,7 @@ import home from './home'
 import vote from './vote'
 import survey from './survey'
 import updModal from './updModal'
+import http from './http'
 export default {
   
   schoolBaseInfo,
@@ -60,6 +61,7 @@ export default {
   vote,
   survey,
   updModal,
+  http,
   test: '測試',
   formConfigP: {
     input: '請輸入',

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

@@ -13,6 +13,7 @@ export default {
 	single: '單選題',
 	multiple: '复选题',
 	judge: '判斷題',
+	subjective: '問答題',
 	defaultName: '預設問卷名稱',
 	isExistTip: '已存在未保存的問卷活動!',
 	getDataFailTip: '獲取數據失敗!',
@@ -49,6 +50,28 @@ export default {
 		noOptionTip: '問卷選項個數不能為空!',
 		attachmentMaxTip: '最多只能上傳5個附件',
 		optionNumsTip: '最多只能有10個選項',
-		noMatchDataTip: '未匹配數據'
+		noMatchDataTip: '未匹配數據',
+		ruleName: '問卷名稱不能為空',
+		ruleDescription: '問卷描述不能為空',
+		ruleClasses: '問卷對象不能為空',
+		ruleTime: '請設定起止時間',
+		ruleDate: '日期不能為空',
+		ruleStartTime: '開始時間不能早於當前時間'
+	},
+	questionaire: {
+		noItemData: '暫無題目數據',
+		edit: '編輯',
+		remove: '移除',
+		analysisData: '統計資料',
+		add: '新增',
+		item: '題目',
+		confirm: '確認',
+		single: '單選',
+		multiple: '多選',
+		judge: '判斷',
+		subjective: '問答',
+		noCompleteTip: '請填寫完整',
+		confirmTitle: '溫馨提示',
+		confirmText: '確認删除該題目?'
 	}
 }

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

@@ -51,6 +51,12 @@ export default {
 		noOptionTip: '投票選項個數不能為空!',
 		attachmentMaxTip: '最多只能上傳5個附件',
 		optionNumsTip: '最多只能有10個選項',
-		noMatchDataTip: '未匹配數據'
+		noMatchDataTip: '未匹配數據',
+		ruleName: '投票名稱不能為空',
+		ruleDescription: '投票描述不能為空',
+		ruleClasses: '投票對象不能為空',
+		ruleTime: '請設定起止時間',
+		ruleDate: '日期不能為空',
+		ruleStartTime: '開始時間不能早於當前時間'
 	}
 }

+ 1 - 3
TEAMModelOS/ClientApp/src/store/module/studentWeb.js

@@ -196,6 +196,7 @@ export default {
             console.log(state.tempfinishedItem, state.tempfinishedItemtime);
         },
         ChangeItemName(state, item) {
+            console.log('5656566666666666666666666')
             state.Item = item;
             //state.Item.isDone = true;
             state.isSelectedNow = true;
@@ -204,7 +205,6 @@ export default {
             }
         },
         ShowCountedIsDone(state, mockdata) {
-            //console.log(mockdata);
             var count = 0;
 
             for (var i = 0; i < mockdata.length; i++) {
@@ -219,12 +219,10 @@ export default {
         //動作是使用者點擊下拉選單篩選類型,回傳目前顯示的類型狀態
         ChangeFilterType(state, evenType) {
             state.filterType = evenType;
-            console.log("目前的活動類型:" + state.filterType);
             state.isSelectedNow = false;
         },
         ChangeFilterClassName(state, className) {
             state.filterClassName = className;
-            console.log("目前的課程類型:" + state.filterClassName);
         },
         //**存放活動相關的函式
         ChangeCurrentSelectedReadingName(state, item) {

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

@@ -39,8 +39,9 @@ export default {
 					score: item.score||0,
 					repair:item.repair,
 				},
-				render:2,
 				item:[{
+					type:'Html',
+					uid:item.id,
 					question:item.question,
 					option:item.option
 				}]

+ 26 - 12
TEAMModelOS/ClientApp/src/view/classmgt/ManageClass.vue

@@ -5,7 +5,7 @@
             <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;">
+            <span v-if="classList[curClassIndex] && classList[curClassIndex].students" style="margin-left: 10px;color: #aaaaaa;">
                 {{$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>
@@ -177,7 +177,7 @@ export default {
                 },
                 {
                     slot: 'action',
-                    title: this.$t('cusMgt.searchHolder'),
+                    title: ' ',
                     align: 'center'
                 }
             ]
@@ -190,6 +190,9 @@ export default {
         //切换班级
         selectClass(index) {
             this.curClassIndex = index
+            if(!this.classList[this.curClassIndex].students){
+                this.findClassStu()
+            }
             if (this.viewType == 0) {
                 this.handelGroup()
             }
@@ -436,19 +439,35 @@ export default {
         },
         //查询自己管理的班级
         findClass() {
+            this.$store.dispatch('user/getSchoolProfile').then(
+                res => {
+                    if (this.$store.state.userInfo.isHeadmaster) {
+                        let classes = res.school_classes
+                        console.log('class', classes)
+                        this.classList = classes.filter(item => {
+                            return this.$store.state.userInfo.mgtClasses.indexOf(item.id) > -1
+                        })
+                        if (this.classList.length > 0) {
+                            this.curClassCode = this.classList[0].id
+                            this.findClassStu()
+                        }
+                        console.log('list', this.classList)
+                    }
+                }
+            )
+        },
+        //查询班级学生
+        findClassStu(){
             this.tableLoading = true
             let params = {
                 'school_code': this.$store.state.userInfo.schoolCode,
-                'teacher.id': this.$store.state.userInfo.TEAMModelId,
+                'ids': [this.curClassCode],
                 '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
-                        }
+                        this.classList[this.curClassIndex].students = res.classrooms[0] ? res.classrooms[0].students : []
                     } else {
                         this.$Message.error('API error!')
                     }
@@ -469,11 +488,6 @@ export default {
             let dom = document.getElementById('table-wrap')
             this.tableHeight = dom.offsetHeight - 30
         })
-    },
-    mounted() {
-
-    },
-    computed: {
     }
 }
 </script>

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

@@ -556,48 +556,6 @@
 	.exersices-attr .ivu-select-multiple .ivu-select-selection .ivu-select-placeholder {
 		line-height: 38px;
 	}
-
-	/* 修改iview Modal样式 */
-
-	.related-modal .ivu-modal-content {
-		background: #3c3c3c;
-		overflow: hidden;
-		color: #fff;
-	}
-
-	.related-modal .ivu-modal-body {
-		height: 400px;
-		padding: 20px;
-	}
-
-	.related-modal .ivu-modal-body {
-		height: 700px;
-	}
-
-	.related-modal .ivu-modal-header {
-		border-bottom: none;
-		font-size: 18px;
-		font-weight: bold;
-		padding: 25px;
-	}
-
-	.related-modal .modal-content {
-		padding: 0 35px 30px 35px;
-	}
-
-	.related-modal .modal-btn {
-		margin-left: 2%;
-		width: 96%;
-		height: 40px;
-		background: rgb(11, 151, 117);
-		border: none;
-		color: #fff;
-		margin-top: 30px;
-	}
-
-	.related-modal .choose-content {
-		height: 85%;
-	}
 	
 	.child-exercise-analysis .w-e-toolbar{
 		z-index: 0 !important;

+ 0 - 41
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseEditExercise.vue

@@ -894,47 +894,6 @@
 		line-height: 38px;
 	}
 
-	/* 修改iview Modal样式 */
-
-	.related-modal .ivu-modal-content {
-		background: #3c3c3c;
-		overflow: hidden;
-		color: #fff;
-	}
-
-	.related-modal .ivu-modal-body {
-		height: 400px;
-		padding: 20px;
-	}
-
-	.related-modal .ivu-modal-body {
-		height: 700px;
-	}
-
-	.related-modal .ivu-modal-header {
-		border-bottom: none;
-		font-size: 18px;
-		font-weight: bold;
-		padding: 25px;
-	}
-
-	.related-modal .modal-content {
-		padding: 0 35px 30px 35px;
-	}
-
-	.related-modal .modal-btn {
-		margin-left: 2%;
-		width: 96%;
-		height: 40px;
-		background: rgb(11, 151, 117);
-		border: none;
-		color: #fff;
-		margin-top: 30px;
-	}
-
-	.related-modal .choose-content {
-		height: 85%;
-	}
 
 	.btn-relate-content {
 		right: 0;

+ 0 - 42
TEAMModelOS/ClientApp/src/view/evaluation/index/CreateExercises.vue

@@ -742,46 +742,4 @@
 	.exersices-attr .ivu-select-multiple .ivu-select-selection .ivu-select-placeholder {
 		line-height: 38px;
 	}
-
-	/* 修改iview Modal样式 */
-
-	.related-modal .ivu-modal-content {
-		background: #3c3c3c;
-		overflow: hidden;
-		color: #fff;
-	}
-
-	.related-modal .ivu-modal-body {
-		height: 400px;
-		padding: 20px;
-	}
-
-	.related-modal .ivu-modal-body {
-		height: 700px;
-	}
-
-	.related-modal .ivu-modal-header {
-		border-bottom: none;
-		font-size: 18px;
-		font-weight: bold;
-		padding: 25px;
-	}
-
-	.related-modal .modal-content {
-		padding: 0 35px 30px 35px;
-	}
-
-	.related-modal .modal-btn {
-		margin-left: 2%;
-		width: 96%;
-		height: 40px;
-		background: rgb(11, 151, 117);
-		border: none;
-		color: #fff;
-		margin-top: 30px;
-	}
-
-	.related-modal .choose-content {
-		height: 85%;
-	}
 </style>

+ 0 - 2
TEAMModelOS/ClientApp/src/view/evaluation/types/BaseCompletion.vue

@@ -49,7 +49,6 @@
 			this.$editorTools.addAudio(this, stemEditor)
 			this.$editorTools.addCanvas(this, stemEditor)
 			this.$editorTools.initMyEditor(stemEditor)
-			
 			stemEditor.create()
 
 			let answerEditor = new E(this.$refs.answerEditor)
@@ -61,7 +60,6 @@
 			this.$editorTools.addAudio(this, answerEditor)
 			this.$editorTools.addCanvas(this, answerEditor)
 			this.$editorTools.initMyEditor(answerEditor)
-			
 			answerEditor.create()
 
 			this.stemEditor = stemEditor

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

@@ -108,7 +108,7 @@
                             活动类型:
                         </span>
                         <Select v-model="curAcType" style="width:160px" size="small">
-                            <Option v-for="item in acTypeList" :value="item.value" :key="item.value">{{ item.label }}</Option>
+                            <Option v-for="(item,index) in acTypeList" :disabled="index > 2" :value="item.value" :key="item.value">{{ item.label }}</Option>
                         </Select>
                     </div>
                 </div>

+ 12 - 2
TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.less

@@ -76,6 +76,8 @@
 
                 &-tool {
                     cursor: pointer;
+					font-weight: 500;
+					color: #f6f6f6 !important;
 					
 					.ivu-btn:before{
 						background-color: none;
@@ -85,15 +87,23 @@
                 .ivu-icon {
                     font-size: 20px;
                     margin-right: 5px;
-                    vertical-align: text-bottom;
+                    vertical-align: sub;
                 }
 				
+				.ivu-dropdown-rel{
+					display: flex;
+					align-items: center;
+					
+					.ivu-btn{
+						padding: 0;
+					}
+				}
+				
 				.ivu-btn{
 					background: transparent;
 					border: none;
 					color:#DCDCDC;
 					padding: 0 10px;
-					margin-bottom: 3px;
 					
 					&::before{
 						background-color: none;

+ 164 - 102
TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.vue

@@ -72,7 +72,7 @@
 								<div class="qn-box-header">
 									<span>{{ $t('survey.surveyResult') }}</span>
 									<div class="qn-box-header-tools" v-show="!isEmptyData">
-										<div class="qn-box-header-tools-tool" style="margin-right: 25px" v-show="currentQn.progress !== 'finish' && editable">
+										<div class="qn-box-header-tools-tool" style="margin-right: 10px" v-show="currentQn.progress !== 'finish' && editable">
 											<Icon type="md-list-box" color="#dcdcdc" />
 											<Dropdown @on-click="onAddItem">
 												<Button>
@@ -83,6 +83,7 @@
 													<DropdownItem name="single">{{ $t('survey.single') }}</DropdownItem>
 													<DropdownItem name="multiple">{{ $t('survey.multiple') }}</DropdownItem>
 													<DropdownItem name="judge">{{ $t('survey.judge') }}</DropdownItem>
+													<DropdownItem name="subjective">{{ $t('survey.subjective') }}</DropdownItem>
 												</DropdownMenu>
 											</Dropdown>
 										</div>
@@ -94,11 +95,11 @@
 											<Icon type="md-create" color="#209460" />
 											<span>{{ $t('survey.edit') }}</span>
 										</div>
-							
-										<div class="qn-box-header-tools-tool" v-show="editable" style="margin-right: 20px">
+
+										<div class="qn-box-header-tools-tool" v-show="editable" style="margin-right: 10px">
 											<Button class="btn-save" icon="md-folder" :loading="isBtnLoading" @click="onSaveQn">{{ $t('survey.save') }}</Button>
 										</div>
-							
+
 										<div class="qn-box-header-tools-tool" @click="onCancelEditQn" v-show="editable">
 											<Icon type="md-create" color="#209460" />
 											<span>{{ $t('survey.cancelEdit') }}</span>
@@ -119,6 +120,7 @@
 	</div>
 </template>
 <script>
+	import blobTool from "@/utils/blobTool.js";
 	import BaseQuestionnaire from "@/components/questionnaire/BaseQuestionnaire.vue";
 	import BaseQnForm from "@/components/questionnaire/BaseQnForm.vue";
 	export default {
@@ -153,7 +155,9 @@
 			// 校本课纲与个人课纲切换
 			handleTabClick(index) {
 				this.tabIndex = index;
-				this.getQnList(index === 0 ? "school" : "private");
+				setTimeout(() => {
+					this.getQnList(index === 0 ? "school" : "private");
+				}, 500)
 			},
 
 			/** 新增问卷 */
@@ -164,13 +168,14 @@
 				} else {
 					let defaultQn = {
 						name: this.$t('survey.defaultName'),
-						targetClassIds: [],
+						classes: [],
 						endTime: "",
 						startTime: "",
 						description: "",
 						rangeTime: [],
 						isReset: [],
 						progress: "pending",
+						questionUrl: [],
 						other: [],
 					};
 
@@ -183,42 +188,7 @@
 				}
 			},
 
-			/** 删除当前问卷 */
-			onDeleteQn() {
-				this.$Modal.confirm({
-					title: this.$t('survey.deletesurvey'),
-					content: this.$t('survey.deleteConfirmTip'),
-					onOk: () => {
-						this.isLoading = true;
 
-						if (this.currentQn.id) {
-							this.$api.questionnaire
-								.DeleteSurvey({
-									id: this.currentQn.id,
-									code: this.currentQn.code,
-									scope: this.getCurScope,
-								})
-								.then((res) => {
-									if (!res.error) {
-										this.isLoading = false;
-										this.$Message.success(this.$t('survey.deleteSuc'));
-										this.handleTabClick(
-											this.$route.name === "manageQuestionnaire" ? 0 : 1
-										);
-									} else {
-										this.isLoading = false;
-										this.$Message.error(this.$t('survey.deleteFail'));
-									}
-								});
-						} else {
-							this.qnList.splice(this.qnList.indexOf(this.currentQn), 1);
-							this.isLoading = false;
-							this.$Message.success(this.$t('survey.deleteSuc'));
-							if (this.qnList.length) this.onQnClick(this.qnList[0], 0);
-						}
-					},
-				});
-			},
 
 			/** 获取问卷列表 */
 			getQnList(type) {
@@ -227,7 +197,7 @@
 					code: this.getCurCode,
 					scope: this.getCurScope,
 				};
-				this.$api.questionnaire.FindSurveysSummary(params).then(async (res) => {
+				this.$api.questionnaire.FindSurveys(params).then(async (res) => {
 					if (!res.error && res.surveys) {
 						let list = res.surveys.reverse();
 						this.qnList = list;
@@ -241,21 +211,23 @@
 					} else {
 						this.$Message.error(this.$t('survey.getDataFailTip'));
 					}
+				}).catch(err => {
+					this.isLoadList = false
 				});
 			},
 
 			/** 获取问卷列表 */
-			async getQnDetails(qnId) {
+			async getQnDetails(qnItem) {
+				console.log(qnItem)
 				return new Promise(async (r, j) => {
 					this.$api.questionnaire
-						.FindSurveys({
-							id: qnId,
-							code: this.getCurCode,
-							scope: this.getCurScope,
+						.FindSurveysSummary({
+							id: qnItem.id,
+							code: qnItem.code,
 						})
 						.then(async (res) => {
-							if (!res.error && res.surveys) {
-								r(res.surveys[0]);
+							if (!res.error && res.survey) {
+								r(res.survey);
 							} else {
 								this.$Message.error(this.$t('survey.getDataFailTip'));
 							}
@@ -280,52 +252,108 @@
 							this.qnList.splice(this.qnList.indexOf(item), 1);
 						});
 				}
-				// 如果当前点击活动是已结束 则获取问卷的作答数据
-				this.currentRecord =
-					item.status === 300 && item.id ? await this.getQnRecord(item.id) : null;
-				this.currentQn = item.id ?
-					await this.getQnDetails(item.id) :
-					this.currentQn;
-				this.currentQn.status = item.status;
+				this.currentQn = item.id ? await this.getQnDetails(item) : this.currentQn;
+				this.currentQn.progress = item.progress;
 				console.log("获取问卷数据");
 				console.log(this.currentQn);
-				this.currentRecord && this.makeItemResult(this.currentRecord);
 				this.activeQnIndex = hasNewQn ? this.qnList.indexOf(item) : index;
 				this.$refs.qnForm.qnFormEdit = false;
 				this.editable = false;
 			},
 
+			/* 获取问卷所有试题的选项内容 */
+			getSurveyAns(items) {
+				let result = []
+				items.forEach(item => {
+					result.push(item.option.map(i => i.code))
+				})
+				return result
+			},
+
 			/* 保存问卷操作 */
 			async onSaveQn() {
-				console.log(this.$refs.qnForm.qnFormEdit);
+				this.isBtnLoading = true;
 				let isQnFormEdit = this.$refs.qnForm.qnFormEdit; // 判断当前表单是否为编辑状态
-				let qnBaseInfo = isQnFormEdit ?
-					await this.$refs.qnForm.handleSubmit("qnForm") :
-					this.currentQn;
+				let qnBaseInfo = isQnFormEdit ? await this.$refs.qnForm.handleSubmit("qnForm") : this.currentQn;
 				let qnItems = this.$refs.qnPaper.items || [];
-				// 获取到基础信息 以及 题目数据 拼接保存
-				qnBaseInfo.questions = qnItems;
-				this.isBtnLoading = true;
-				this.saveorUpdataQn({
-						survey: qnBaseInfo,
-						reset: false,
-					})
+				// 将问卷试题内容保存到blob 用blobUrl来替换quesitons字段
+				qnBaseInfo.ans = this.getSurveyAns(qnItems)
+				qnBaseInfo.questionUrl = await this.doUploadBlob(qnBaseInfo, qnItems);
+				// 开始保存问卷
+				this.saveorUpdataQn(qnBaseInfo)
 					.then((res) => {
 						this.$Message.success(this.$t('survey.doSuc'));
-						this.$refs.qnForm.qnFormEdit = false;
 						this.editable = false;
-						this.isLoading = false;
-						this.isBtnLoading = false;
 						this.onAddSuccess();
 					})
 					.catch((error) => {
 						this.$Message.error(`操作失败,错误信息为${error}`);
+					}).finally(data => {
 						this.$refs.qnForm.qnFormEdit = false;
 						this.isLoading = false;
 						this.isBtnLoading = false;
 					});
 			},
 
+			async doUploadBlob(qnBaseInfo, items) {
+				console.log('上传参数', qnBaseInfo)
+				return new Promise(async (resolve, reject) => {
+					let promiseArr = []
+					// 获取初始化Blob需要的数据
+					let sasData = qnBaseInfo.scope === 'private' ? await this.$tools.getPrivateSas() : await this.$tools.getSchoolSas();
+					//初始化Blob
+					let containerClient = new blobTool(
+						sasData.url,
+						sasData.name,
+						sasData.sas,
+						qnBaseInfo.scope
+					);
+					
+					// 上传index.json
+					promiseArr.push(new Promise(async (r, j) => {
+						// let itemJsonFile = await this.$evTools.createBlobItem(exerciseItem);
+						let file = new File([JSON.stringify(qnBaseInfo)], "index.json");
+						try {
+							// 等待上传blob的返回结果
+							let blobFile = await containerClient.upload(file, 'survey/' + qnBaseInfo.id);
+							if (blobFile.blob) {
+								console.log('上传Blob成功', blobFile)
+								r(blobFile.blob)
+							} else {
+								this.$Message.error(this.$t('evaluation.newExercise.uploadErrorTip'));
+							}
+						} catch (e) {
+							this.$Message.error(e.spaceError);
+						}
+					}))
+					
+					
+					for (let item of items) {
+						let curId = item.id || this.$tools.guid()
+						promiseArr.push(new Promise(async (r, j) => {
+							// let itemJsonFile = await this.$evTools.createBlobItem(exerciseItem);
+							let file = new File([JSON.stringify(item)], curId + ".json");
+							try {
+								// 等待上传blob的返回结果
+								let blobFile = await containerClient.upload(file, 'survey/' + qnBaseInfo.id);
+								if (blobFile.blob) {
+									console.log('上传Blob成功', blobFile)
+									r(blobFile.blob)
+								} else {
+									this.$Message.error(this.$t('evaluation.newExercise.uploadErrorTip'));
+								}
+							} catch (e) {
+								this.$Message.error(e.spaceError);
+							}
+						}))
+					}
+					Promise.all(promiseArr).then(result => {
+						console.log(result)
+						resolve(result.slice(1))
+					})
+				})
+			},
+
 			onShowAllAnalysis() {
 				this.$refs.qnPaper.onShowAllAnalysis();
 			},
@@ -334,37 +362,6 @@
 				this.$refs.qnPaper.onMenuClick(name);
 			},
 
-			async getQnRecord(id) {
-				return new Promise((r, j) => {
-					this.$api.questionnaire
-						.FindRecordSurvey({
-							id: id,
-						})
-						.then((res) => {
-							if (!res.error && res.result.data) {
-								r(res.result.data);
-							} else {
-								j(500);
-							}
-						});
-				});
-			},
-
-			/* 根据学生的作答记录 处理成 图表需要的记录 */
-			makeItemResult(result) {
-				let arr = [];
-				this.currentQn.items.forEach((item, index) => {
-					let obj = {};
-					let resultArr = [];
-					item.options.forEach((option) => {
-						obj.code = this.getSimpleText(option.value);
-						obj.value = this.getSelectNum(result, index, option.code);
-						resultArr.push(obj);
-						obj = {};
-					});
-					item.result = resultArr;
-				});
-			},
 
 			/* 查找指定题目指定选项在作答数据里出现的次数 */
 			getSelectNum(origin, itemIndex, option) {
@@ -454,6 +451,71 @@
 				this.handleTabClick(this.$route.name === "manageQuestionnaire" ? 0 : 1);
 			},
 
+			/** 删除当前问卷 */
+			onDeleteQn() {
+				this.$Modal.confirm({
+					title: this.$t('survey.deletesurvey'),
+					content: this.$t('survey.deleteConfirmTip'),
+					onOk: () => {
+						this.isLoading = true;
+						if (this.currentQn.id) {
+							this.$api.questionnaire
+								.DeleteSurvey({
+									id: this.currentQn.id,
+									code: this.currentQn.code,
+									scope: this.getCurScope,
+								})
+								.then((res) => {
+									if (!res.error) {
+										// 获取初始化Blob需要的数据
+										this.deleteBlobPrefix(this.currentQn).then(status => {
+											if (status === 200) {
+												this.isLoading = false;
+												this.$Message.success(this.$t('survey.deleteSuc'));
+												this.handleTabClick(
+													this.$route.name === "manageQuestionnaire" ? 0 : 1
+												);
+											} else {
+												console.log(err)
+												this.isLoading = false;
+											}
+										})
+									} else {
+										this.isLoading = false;
+										this.$Message.error(this.$t('survey.deleteFail'));
+									}
+								});
+						} else {
+							this.qnList.splice(this.qnList.indexOf(this.currentQn), 1);
+							this.isLoading = false;
+							this.$Message.success(this.$t('survey.deleteSuc'));
+							if (this.qnList.length) this.onQnClick(this.qnList[0], 0);
+						}
+					},
+				});
+			},
+
+			/* 删除blob指定试题目录下所有 */
+			deleteBlobPrefix(item) {
+				return new Promise((resolve, reject) => {
+					this.$api.blob.deletePrefix({
+						"cntr": item.scope === 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId,
+						"prefix": "survey/" + item.id
+					}).then(
+						(res) => {
+							if (!res.error) {
+								resolve(200)
+							} else {
+								resolve(500)
+							}
+						},
+						(err) => {
+							reject(err)
+						}
+					)
+				})
+			},
+
 			/**
 			 * 滚动条返回顶部
 			 * @param refName

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

@@ -173,6 +173,7 @@
 <script>
     import fn from '@/utils/js-fn.js'
     import { mapGetters } from 'vuex'
+// import { delete } from 'vuedraggable'
 
     export default {
         props: {
@@ -391,7 +392,14 @@
                             }) 
                             apiData = temp
                         }
-                        if(apiData.length > 0){
+                        if(apiData.length > 0){                 
+                            // 新增 如果密碼沒被改變就拿掉此Key           
+                            apiData.forEach( (item) =>{
+                                if(item.pw == '******') {
+                                    delete item.pw
+                                }
+                            })
+
                             this.$api.stuAccount.saveAllStudent(this.schoolCode, apiData).then(
                                 (res) => {
                                     if (res.error == null) {

+ 10 - 8
TEAMModelOS/ClientApp/src/view/student-account/Index.vue

@@ -175,7 +175,6 @@ export default {
          */
         resetPW(row) {
             //重置单个学生
-            console.log(row)
             if (row != -1) {
                 this.$Modal.confirm({
                     title: this.$t('重置密碼'),
@@ -206,20 +205,23 @@ export default {
                         content: '<p>确认批量重置' + " <strong style='color:red;'>" + this.selections.length + '</strong>个学生密碼</p>',
                         onOk: () => {
                             this.tableLoading = true
-                            // let ids = this.selections.map(item => {
-                            //     return { id: item.id, pw: item.id }
-                            // })
-                            this.$api.stuAccount.saveAllStudent(this.$store.state.user.schoolCode, this.selections).then(
+                            let apiData = this.selections.map( (item) =>{
+                                let d = item
+                                d.pw = d.id
+                                return d
+                            })
+
+                            this.$api.stuAccount.saveAllStudent(this.$store.state.user.schoolCode, apiData).then(
                                 res => {
                                     this.selections.length = 0
                                     this.$Message.success('重置成功!')
-                                    this.tableLoading = false
                                 },
                                 err => {
                                     this.$Message.error('API ERROR!')
-                                    this.tableLoading = false
                                 }
-                            )
+                            ).finally(() => {
+                                this.tableLoading = false
+                            })
                         }
                     })
                 }

+ 49 - 6
TEAMModelOS/Controllers/Client/HiTeachController.cs

@@ -102,7 +102,6 @@ namespace TEAMModelOS.Controllers.Client
                 else
                 {
                     //如果沒有,則初始化Teacher基本資料到Cosmos
-                   
                     using var stream = new MemoryStream();
                     using var writer = new Utf8JsonWriter(stream); //new JsonWriterOptions() { Indented = true }
                     writer.WriteStartObject();
@@ -121,14 +120,58 @@ namespace TEAMModelOS.Controllers.Client
 
                 //老師個人課程清單
                 List<object> courses = new List<object>();
-                await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryStreamIterator(queryText: $"select c.id, c.name, c.classes, c.notice ,c.scope from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{id}") }))
+                await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.schedule, c.scope FROM c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{id}") }))
                 {
-                    using var json = await JsonDocument.ParseAsync(item.ContentStream);
-                    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                    var jsontcs = await JsonDocument.ParseAsync(item.ContentStream);
+                    if (jsontcs.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
                     {
-                        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                        foreach (var obj in jsontcs.RootElement.GetProperty("Documents").EnumerateArray())
                         {
-                            courses.Add(obj.ToObject<object>());
+                            dynamic courseExtobj = new ExpandoObject();
+                            courseExtobj.id = Convert.ToString(obj.GetProperty("id"));
+                            courseExtobj.name = Convert.ToString(obj.GetProperty("name"));
+                            courseExtobj.scope = Convert.ToString(obj.GetProperty("scope"));
+                            List<object> classes = new List<object>();
+                            if(obj.TryGetProperty("schedule", out JsonElement schedule))
+                            {
+                                foreach (var scheduleobj in schedule.EnumerateArray())
+                                {
+                                    dynamic teacherExtobj = new ExpandoObject();
+                                    dynamic classExtobj = new ExpandoObject();
+                                    var classinfo = scheduleobj.GetProperty("class");
+                                    classExtobj.id = classinfo.GetProperty("id");
+                                    classExtobj.name = classinfo.GetProperty("name");
+                                    classExtobj.code = id;
+                                    if (scheduleobj.TryGetProperty("teacher", out JsonElement teacher))
+                                    {
+                                        classExtobj.teacher = new ExpandoObject();
+                                        classExtobj.teacher.id = Convert.ToString(teacher.GetProperty("id"));
+                                        classExtobj.teacher.name = Convert.ToString(teacher.GetProperty("name"));
+                                    } else
+                                    {
+                                        classExtobj.teacher = null;
+                                    }
+                                    classExtobj.scope = "private";
+                                    int stuCount = 0;
+                                    string stuListId = Convert.ToString(scheduleobj.GetProperty("stulist"));
+                                    await foreach (var stuitem in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryStreamIterator(queryText: $"SELECT ARRAY_LENGTH(c.students) as stucnt FROM c WHERE c.id = '{stuListId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("StuList") }))
+                                    {
+                                        var jsonstu = await JsonDocument.ParseAsync(stuitem.ContentStream);
+                                        foreach (var stuobj in jsonstu.RootElement.GetProperty("Documents").EnumerateArray())
+                                        {
+                                            stuCount = stuobj.GetProperty("stucnt").GetInt32();
+                                        }
+                                    }
+                                    classExtobj.stuCnt = stuCount;
+                                    classExtobj.grpCnt = 0;
+                                    classExtobj.gradeId = null;
+
+                                    classes.Add(classExtobj);
+                                }
+                            }
+                            courseExtobj.classes = classes;
+
+                            courses.Add(courseExtobj);
                         }
                     }
                 }

+ 106 - 12
TEAMModelOS/Controllers/Common/SurveyController.cs

@@ -18,6 +18,7 @@ using TEAMModelOS.SDK.Helper.Common.CollectionHelper;
 using TEAMModelOS.Models;
 using Microsoft.Extensions.Options;
 using TEAMModelOS.Filter;
+using TEAMModelOS.Services.Common;
 
 namespace TEAMModelOS.Controllers
 {
@@ -31,18 +32,22 @@ namespace TEAMModelOS.Controllers
     [ApiController]
     public class SurveyController : ControllerBase
     {
+        private readonly AzureRedisFactory _azureRedis;
         private readonly AzureCosmosFactory _azureCosmos;
         private readonly SnowflakeId _snowflakeId;
         private readonly AzureServiceBusFactory _serviceBus;
         private readonly DingDing _dingDing;
         private readonly Option _option;
-        public SurveyController(AzureCosmosFactory azureCosmos, AzureServiceBusFactory serviceBus, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option)
+        private readonly AzureStorageFactory _azureStorage;
+        public SurveyController(AzureCosmosFactory azureCosmos, AzureServiceBusFactory serviceBus, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option, AzureRedisFactory azureRedis, AzureStorageFactory azureStorage)
         {
             _snowflakeId= snowflakeId;
             _serviceBus = serviceBus;
             _azureCosmos = azureCosmos;
             _dingDing = dingDing;
             _option = option?.Value;
+            _azureRedis = azureRedis;
+            _azureStorage = azureStorage;
         }
 
 
@@ -73,13 +78,37 @@ namespace TEAMModelOS.Controllers
                     request = await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(request, new PartitionKey($"{request.code}"));
                 }
                 else {
-                    Survey info = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Survey>(request.id, new PartitionKey($"{request.code}"));
-                    if (info.progress.Equals("going"))
+                    var response = await client.GetContainer("TEAMModelOS", "Common").ReadItemStreamAsync(request.id, new PartitionKey($"{request.code}"));
+                    if (response.Status == 200)
                     {
-                        return Ok(new { v = "活动正在进行中" });
+                        using var json = await JsonDocument.ParseAsync(response.ContentStream);
+                        var info = json.ToObject<Survey>();
+                        if (info.progress.Equals("going"))
+                        {
+                            return Ok(new { v = "活动正在进行中" });
+                        }
+                        if (request.startTime > now)
+                        {
+                            request.progress = "pending";
+                        }
+                        else
+                        {
+                            request.progress = "going";
+                        }
+                        request.progress = info.progress;
+                        request = await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(request, info.id, new PartitionKey($"{info.code}"));
+                    }
+                    else {
+                        if (request.startTime > now)
+                        {
+                            request.progress = "pending";
+                        }
+                        else
+                        {
+                            request.progress = "going";
+                        }
+                        request = await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(request, new PartitionKey($"{request.code}"));
                     }
-                    request.progress = info.progress;
-                    request = await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(request, info.id, new PartitionKey($"{info.code}"));
                 }
                 return Ok(new { survey = request});
             } catch (Exception ex) {
@@ -112,7 +141,7 @@ namespace TEAMModelOS.Controllers
                 var stimestamp = DateTimeOffset.UtcNow.AddDays(-30).ToUnixTimeMilliseconds();
                 if (requert.TryGetProperty("stime", out JsonElement stime))
                 {
-                    if (!stime.ValueKind.Equals(JsonValueKind.Undefined) && stime.TryGetInt64(out long data))
+                    if (!stime.ValueKind.Equals(JsonValueKind.Undefined)&& !stime.ValueKind.Equals(JsonValueKind.Null)  && stime.TryGetInt64(out long data))
                     {
                         stimestamp = data;
                     };
@@ -121,14 +150,14 @@ namespace TEAMModelOS.Controllers
                 var etimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
                 if (requert.TryGetProperty("etime", out JsonElement etime))
                 {
-                    if (!etime.ValueKind.Equals(JsonValueKind.Undefined) && etime.TryGetInt64(out long data))
+                    if (!etime.ValueKind.Equals(JsonValueKind.Null)&&!etime.ValueKind.Equals(JsonValueKind.Undefined) && etime.TryGetInt64(out long data))
                     {
                         etimestamp = data;
                     };
 
                 };
                 var progresssql = "";
-                if (!requert.TryGetProperty("progress", out JsonElement progress))
+                if (requert.TryGetProperty("progress", out JsonElement progress))
                 {
 
                     if (!progress.ValueKind.Equals(JsonValueKind.Undefined) && !progress.ValueKind.Equals(JsonValueKind.Null) && progress.ValueKind.Equals(JsonValueKind.String))
@@ -141,7 +170,7 @@ namespace TEAMModelOS.Controllers
                 int? topcout = null;
                 if (requert.TryGetProperty("count", out JsonElement jcount))
                 {
-                    if (!jcount.ValueKind.Equals(JsonValueKind.Undefined) && jcount.TryGetInt32(out int data))
+                    if (!jcount.ValueKind.Equals(JsonValueKind.Undefined) && !jcount.ValueKind.Equals(JsonValueKind.Null) && jcount.TryGetInt32(out int data))
                     {
                         topcout = data;
                     }
@@ -160,9 +189,9 @@ namespace TEAMModelOS.Controllers
                 };
                 List<object> surveys = new List<object>();
                 var client = _azureCosmos.GetCosmosClient();
-                var query = $"select   c.id,c.name,c.code,c.startTime,c.endTime,c.progress from c where c.createTime >= {stimestamp} and c.createTime <= {etimestamp}  {progresssql } ";
+                var query = $"select c.id,c.name,c.code,c.startTime,c.endTime,c.progress from c where  c.createTime >= {stimestamp} and c.createTime <= {etimestamp}  {progresssql } ";
                 await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: query,
-                    requestOptions: new QueryRequestOptions() { MaxItemCount = topcout, PartitionKey = new PartitionKey($"Vote-{code}") }))
+                    requestOptions: new QueryRequestOptions() { MaxItemCount = topcout, PartitionKey = new PartitionKey($"Survey-{code}") }))
                 {
                     using var json = await JsonDocument.ParseAsync(item.ContentStream);
                     if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
@@ -273,5 +302,70 @@ namespace TEAMModelOS.Controllers
                 return BadRequest(e.StackTrace);
             }
         }
+
+        /// <summary>
+        /// 问卷答案提交
+        /// </summary>
+        /// <redis>
+        /// Survey:Record:e6b887f8-adb5-3677-bcc7-3b36450909df_Survey-1595321354 {"C":2,"A":2,"other":2}
+        /// </redis>
+        /// <param name="request">
+        /// !"id":"aaaa"
+        /// !"code":"Survey-hbcn"/"code":"Survey-1606285227"
+        /// !"record":[["A","B"],["A"],["D"],[],["建议提升服务质量"]]
+        /// </param>
+        /// <returns>
+        /// msgid=0投票失败,1提交成功,2不在时间范围内,3不在发布范围内,6未设置投票项
+        /// </returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("answer")]
+        //[AuthToken(Roles = "teacher,student")]
+        public async Task<IActionResult> Answer(JsonElement request)
+        {
+            //  var (userid, _, _, _) = HttpContext.GetAuthTokenInfo();
+            if (request.TryGetProperty("userid", out JsonElement userid)) {
+                int msgid = await ActivityStudentService.Answer(request, _azureCosmos, _azureRedis, $"{userid}", _azureStorage);
+                return Ok(new { msgid });
+            }
+            else { return Ok(new { msgid = 0 }); }
+        }
+
+
+        /// <summary>
+        /// 问卷记录 当活动没结算且没有BlobUrl时则调用此接口
+        /// </summary>
+        /// <redis>
+        /// {"C":2,"A":2,"other":2}
+        /// </redis>
+        /// <param name="request">
+        /// !"id":"aaaa"
+        /// !"code":"Survey-hbcn"/"code":"Survey-1606285227"
+        /// </param>
+        /// <returns>
+        /// </returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("record")]
+        //[AuthToken(Roles = "teacher,student")]
+        public async Task<IActionResult> Record(JsonElement request)
+        {
+            if (!request.TryGetProperty("id", out JsonElement id))
+            {
+                return BadRequest();
+            }
+            //活动分区
+            if (!request.TryGetProperty("code", out JsonElement code))
+            {
+                return BadRequest();
+            }
+            //获取问卷记录
+            var records = await _azureRedis.GetRedisClient(8).HashGetAllAsync($"Survey:Record:{id}");
+            List<dynamic> res = new List<dynamic>();
+            foreach (var rcd in records)
+            {
+                var value = rcd.Value.ToString().ToObject<JsonElement>();
+                res.Add(new { index = rcd.Name.ToString(), ans = value }) ;
+            }
+            return Ok(new {records = res});
+        }
     }
 }

+ 36 - 17
TEAMModelOS/Controllers/Common/VoteController.cs

@@ -68,11 +68,12 @@ namespace TEAMModelOS.Controllers.Learn
             {
                 //新增Vote
                 var client = _azureCosmos.GetCosmosClient();
-                request.code = request.pk + "-" + request.code;
+               
                 long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
                 request.createTime = now;
                 if (string.IsNullOrEmpty(request.id))
                 {
+                    request.code = request.pk + "-" + request.code;
                     request.id = Guid.NewGuid().ToString();
                     if (request.startTime > now)
                     {
@@ -85,21 +86,38 @@ namespace TEAMModelOS.Controllers.Learn
                 }
                 else
                 {
-                    Vote info = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Vote>(request.id, new PartitionKey($"{request.code}"));
-                    if (info.progress.Equals("going"))
+                    var response = await client.GetContainer("TEAMModelOS", "Common").ReadItemStreamAsync(request.id, new PartitionKey($"{request.code}"));
+                    if (response.Status == 200)
                     {
-                        return Ok(new { v = "活动正在进行中" });
-                    }
-                    if (request.startTime > now)
-                    {
-                        request.progress = "pending";
+                        using var json = await JsonDocument.ParseAsync(response.ContentStream);
+                        var info = json.ToObject<Vote>();
+                        if (info.progress.Equals("going"))
+                        {
+                            return Ok(new { v = "活动正在进行中" });
+                        }
+                        if (request.startTime > now)
+                        {
+                            request.progress = "pending";
+                        }
+                        else
+                        {
+                            request.progress = "going";
+                        }
+                        request.progress = info.progress;
+                        request = await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(request, info.id, new PartitionKey($"{info.code}"));
                     }
                     else
                     {
-                        request.progress = "going";
+                        if (request.startTime > now)
+                        {
+                            request.progress = "pending";
+                        }
+                        else
+                        {
+                            request.progress = "going";
+                        }
+                        request = await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(request, new PartitionKey($"{request.code}"));
                     }
-                    request.progress = info.progress;
-                    request = await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(request, info.id, new PartitionKey($"{info.code}"));
                 }
                 return Ok(new { vote = request });
             }
@@ -334,9 +352,9 @@ namespace TEAMModelOS.Controllers.Learn
                 return BadRequest();
             }
             //获取投票活动的所有投票记录
-            var records = await _azureRedis.GetRedisClient(8).HashGetAllAsync($"Vote:Record:{id}_{code}");
+            var records = await _azureRedis.GetRedisClient(8).HashGetAllAsync($"Vote:Record:{id}");
             //获取投票活动的选项及投票数
-            var counts = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Vote:Count:{id}_{code}");
+            var counts = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Vote:Count:{id}");
             List<OptionVote> options = new List<OptionVote>();
             if (counts != null && counts.Length > 0)
             {
@@ -362,7 +380,7 @@ namespace TEAMModelOS.Controllers.Learn
                     res.Add(value);
                 }
             }
-            return Ok(new { options, records= res });
+            return Ok(new { options, records= res,now =DateTimeOffset.UtcNow });
         }
 
         /// <summary>
@@ -375,10 +393,11 @@ namespace TEAMModelOS.Controllers.Learn
         /// <param name="request">
         /// !"id":"aaaa"
         /// !"code":"Vote-hbcn"/"code":"Vote-1606285227"
-        /// !"option":["A","B","B"]/["1","2","3"]
+        /// !"option":{"A":5,"B":6}/{"1":5,"2":8}
         /// </param>
         /// <returns>
-        /// msgid=0投票失败,1投票成功,2不在时间范围内,3不在发布范围内,4投票周期内重复投票,5周期内的可投票数不足,6未设置投票项
+        /// msgid=0投票失败,1投票成功,2不在时间范围内,3不在发布范围内,4投票周期内重复投票,5周期内的可投票数不足
+        /// 
         /// </returns>
         [ProducesDefaultResponseType]
         [HttpPost("decide")]
@@ -399,7 +418,7 @@ namespace TEAMModelOS.Controllers.Learn
         /// <param name="request">
         /// !"id":"aaaa"
         /// !"code":"Vote-hbcn"/"code":"Vote-1606285227"
-        /// !"option":["A","B","B"]/["1","2","3"]
+        /// !"option":{"A":5,"B":6}/{"1":5,"2":8}
         /// !"userid":"15283771540"
         /// </param>
         /// <returns>

+ 1 - 0
TEAMModelOS/Controllers/Core/ImportController.cs

@@ -38,6 +38,7 @@ namespace TEAMModelOS.Controllers
     [ApiController]
     public class ImportController : ControllerBase
     {
+
         public PPTX2HTEXTranslator _PPTX2HTEXTranslator { get; set; }
         public DOXC2HTMLTranslator _DOXC2HTMLTranslator { get; set; }
 

+ 96 - 12
TEAMModelOS/Controllers/School/StudentController.cs

@@ -1895,44 +1895,67 @@ namespace TEAMModelOS.Controllers
             try
             {
                 var client = _azureCosmos.GetCosmosClient();
+                var schoolClient = client.GetContainer("TEAMModelOS", "School");
+                var teacherClient = client.GetContainer("TEAMModelOS", "Teacher");
+                var studentClient = client.GetContainer("TEAMModelOS", "Student");
                 //參數取得
                 if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
                 if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
                 if (!request.TryGetProperty("pw", out JsonElement pw)) return BadRequest();  
 
-                var response = await client.GetContainer("TEAMModelOS", "Student").ReadItemStreamAsync(id.GetString(), new PartitionKey($"Base-{school_code.GetString().ToLower()}"));
+                var response = await studentClient.ReadItemStreamAsync(id.GetString(), new PartitionKey($"Base-{school_code.GetString().ToLower()}"));
                 if (response.Status == 200)
                 {
                     var rjson = await JsonDocument.ParseAsync(response.ContentStream);
                     rjson.RootElement.TryGetProperty("salt", out JsonElement salt);
                     rjson.RootElement.TryGetProperty("pw", out JsonElement dbpw);
                     rjson.RootElement.TryGetProperty("name", out JsonElement name);
-                    rjson.RootElement.TryGetProperty("picture", out JsonElement picture);                    
+                    rjson.RootElement.TryGetProperty("picture", out JsonElement picture);
+                    rjson.RootElement.TryGetProperty("classId", out JsonElement classId);
+                    rjson.RootElement.TryGetProperty("no", out JsonElement no);
+                    rjson.RootElement.TryGetProperty("groupId", out JsonElement groupId);
+                    rjson.RootElement.TryGetProperty("groupName", out JsonElement groupName);
 
                     var HashedPW = Utils.HashedPassword(pw.ToString(), salt.ToString());
                     if (HashedPW.Equals(dbpw.GetString()))
                     {
-                        string classid = string.Empty;
+                        //班級課程
                         object classinfo = null;
                         List<object> courses = new List<object>();
-                        //去學校找出所屬校本預設班級信息
-                        var query = $"SELECT c.code, c.id, c.name, c.periodId, c.gradeId FROM c JOIN cs IN c.students WHERE cs.id = '{id}'";
-                        var school = client.GetContainer("TEAMModelOS", "School");
-                        await foreach (var item in school.GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{school_code}") }))
+                        ////校本
+                        //取得所屬預設班級信息
+                        if (!classId.ValueKind.Equals(JsonValueKind.Null) && classId.ValueKind.Equals(JsonValueKind.String))
+                        {
+                            var query = $"SELECT c.code, c.id, c.name, c.periodId, c.gradeId FROM c WHERE c.id = '{classId.GetString()}'";
+                            await foreach (var item in schoolClient.GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{school_code}") }))
+                            {
+                                using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                                {
+                                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                                    {
+                                        classinfo = obj.ToObject<object>();
+                                    }
+                                }
+                            }
+                        }
+                        //取得該學生跑班課名單ID
+                        List<string> stulistidsSch = new List<string>();
+                        var querysl = $"SELECT c.id FROM c JOIN students IN c.students WHERE students.id = '{id.GetString()}' AND students.code = 'Base-{school_code}'";
+                        await foreach (var item in schoolClient.GetItemQueryStreamIterator(queryText: querysl, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"StuList-{school_code}") }))
                         {
                             using var json = await JsonDocument.ParseAsync(item.ContentStream);
                             if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
                             {
                                 foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
                                 {
-                                    classid = obj.GetProperty("id").GetString();
-                                    classinfo = obj.ToObject<object>();                                    
+                                    stulistidsSch.Add(obj.GetProperty("id").ToString());
                                 }
                             }
                         }
-                        //找出所屬班級的課程列表                                    
-                        var queryc = $"SELECT VALUE cc.course FROM c JOIN cc IN c.courses WHERE c.id = '{classid}'";
-                        await foreach (var item in school.GetItemQueryStreamIterator(queryText: queryc, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"CourseManagement-{school_code}") }))
+                        //取得該學生的學校課程名單
+                        var queryc = $"SELECT DISTINCT c.id, c.name, schedule.class, schedule.time, schedule.notice, c.scope FROM c JOIN schedule IN c.schedule WHERE (schedule.class.id = '{classId}' AND schedule.stulist = null) OR (ARRAY_CONTAINS({JsonSerializer.Serialize(stulistidsSch)}, schedule.stulist, true))";
+                        await foreach (var item in schoolClient.GetItemQueryStreamIterator(queryText: queryc, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{school_code}") }))
                         {
                             using var json = await JsonDocument.ParseAsync(item.ContentStream);
                             if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
@@ -1943,10 +1966,71 @@ namespace TEAMModelOS.Controllers
                                 }
                             }
                         }
+                        ////個人
+                        //取得該學生跑班課名單ID
+                        Dictionary<string, Dictionary<string, string>> stulistidsTea = new Dictionary<string, Dictionary<string, string>>();
+                        var queryslt = $"SELECT c.id, c.course.id as courseId, c.course.code as courseCode FROM c JOIN students IN c.students WHERE students.id = '{id.GetString()}' AND students.code = 'Base-{school_code}'";
+                        await foreach (var item in teacherClient.GetItemQueryStreamIterator(queryText: queryslt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("StuList") }))
+                        {
+                            using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                            if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                            {
+                                foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                                {
+                                    string courseCode = obj.GetProperty("courseCode").ToString();
+                                    string courseId = obj.GetProperty("courseId").ToString();
+                                    string stulistId = obj.GetProperty("id").ToString();
+                                    if (!stulistidsTea.ContainsKey(courseCode))
+                                    {
+                                        Dictionary<string, string> pCourseIdDic = new Dictionary<string, string>();
+                                        pCourseIdDic.Add(courseId, stulistId);
+                                        stulistidsTea.Add(courseCode, pCourseIdDic);
+                                    }
+                                    else
+                                    {
+                                        if(!stulistidsTea[courseCode].ContainsKey(courseId))
+                                        {
+                                            stulistidsTea[courseCode].Add(courseId, stulistId);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                        //取得該學生的老師個人課程名單
+                        foreach (KeyValuePair<string, Dictionary<string, string>> item in stulistidsTea)
+                        {
+                            string courseCode = item.Key;
+                            Dictionary<string, string> courseIdDic = item.Value;
+                            string stucourseWhere = string.Empty;
+                            foreach (KeyValuePair<string, string> itemDic in courseIdDic) {
+                                string courseId = itemDic.Key;
+                                string stuListId = itemDic.Value;
+                                if(!string.IsNullOrWhiteSpace(stucourseWhere))
+                                {
+                                    stucourseWhere += " OR ";
+                                }
+                                stucourseWhere += $"( c.id = '{courseId}' AND schedule.stulist = '{stuListId}' )";
+                            }
+                            var querycst = $"SELECT DISTINCT c.id, c.name, schedule.class, schedule.time, schedule.notice, c.scope FROM c JOIN schedule IN c.schedule WHERE {stucourseWhere}";
+                            await foreach (var itemcs in teacherClient.GetItemQueryStreamIterator(queryText: querycst, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{courseCode}") }))
+                            {
+                                using var json = await JsonDocument.ParseAsync(itemcs.ContentStream);
+                                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                                {
+                                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                                    {
+                                        courses.Add(obj.ToObject<object>());
+                                    }
+                                }
+                            }
+                        }
+
                         // BLOB(學校,唯讀)
                         var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(school_code.GetString().ToLower(), BlobContainerSasPermissions.Read);
+                        
                         //換取AuthToken,提供給前端
                         var auth_token = JwtAuthExtension.CreateAuthToken(_option.HostName, id.GetString(), name.GetString(), picture.GetString(), _option.JwtSecretKey, schoolID: school_code.GetString(), roles: new[] { "student" });
+                        
                         return Ok(new { error = 0, auth_token, blob_uri, blob_sas, classinfo, courses });
                     }
                     else

+ 15 - 2
TEAMModelOS/Controllers/XTest/TestController.cs

@@ -13,12 +13,25 @@ namespace TEAMModelOS.Controllers.XTest
     [ApiController]
     public class TestController :ControllerBase
     {
+        private readonly AzureStorageFactory _azureStorage;
         private readonly AzureRedisFactory _azureRedis;
         private readonly AzureCosmosFactory _azureCosmos;
-        public TestController(AzureCosmosFactory azureCosmos, AzureRedisFactory azureRedis) {
+        public TestController(AzureCosmosFactory azureCosmos, AzureRedisFactory azureRedis, AzureStorageFactory azureStorage) {
             _azureCosmos = azureCosmos;
             _azureRedis = azureRedis;
+            _azureStorage = azureStorage;
         }
+        /// <summary>
+        /// 测试blob多线程写入同一个文件
+        /// </summary>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpGet("multiple-blob")]
+        public async Task<IActionResult> MultipleBlob() {
+            await  _azureStorage.GetBlobContainerClient("hbcn").List("other");
+            return Ok();
+        }
+
 
         /// <summary>
         /// 测试redis通配符
@@ -27,7 +40,7 @@ namespace TEAMModelOS.Controllers.XTest
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpGet("test-redis")]
-        public async Task<IActionResult> DictNull( ) {
+        public async Task<IActionResult> TestRedis( ) {
 
            var db  = _azureRedis.GetRedisClient(8);
             //var keys = server.Keys(pattern: "Vote*", database: db.Database);

+ 0 - 16
TEAMModelOS/Models/Dto/SurveyDto.cs

@@ -1,16 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using TEAMModelOS.Models;
-using TEAMModelOS.SDK.Models;
-
-namespace TEAMModelOS.Models.Dto
-{
-    public class SurveyDto
-    {
-        public Survey survey { get; set; }
-
-         public bool reset { get; set; }
-    }
-}

+ 0 - 14
TEAMModelOS/Models/Dto/VoteDto.cs

@@ -1,14 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using TEAMModelOS.SDK.Models;
-
-namespace TEAMModelOS.Models.Dto
-{
-    public class VoteDto
-    {
-        public Vote vote { get; set; }
-        public bool reset { get; set; }
-    }
-}

+ 163 - 25
TEAMModelOS/Services/Common/ActivityStudentService.cs

@@ -38,10 +38,10 @@ namespace TEAMModelOS.Services.Common
             if (!request.TryGetProperty("code", out JsonElement code)) {
                 return msgid;
             }
-            List<string> option = new List<string>();
+            Dictionary<string, int> option = new Dictionary<string, int>();
             if (request.TryGetProperty("option", out JsonElement joption))
             {
-                option = joption.ToObject<List<string>>();
+                option = joption.ToObject<Dictionary<string, int>>();
                 if (option.IsEmpty())
                 {
                     msgid = 6;
@@ -76,7 +76,7 @@ namespace TEAMModelOS.Services.Common
                             case "once":
                                 // //如果是只能投票一次的活动则直接获取Redis的第一条 只能投一次
                                 Field = $"{userid}-once";
-                                HashEntry[] values = _azureRedis.GetRedisClient(8).HashGetAll($"Vote:Record:{vote.id}_{vote.code}");
+                                HashEntry[] values = _azureRedis.GetRedisClient(8).HashGetAll($"Vote:Record:{vote.id}");
                                 if (values != null  && values.Length > 0)
                                 {
                                     value = new RedisValue();
@@ -95,22 +95,22 @@ namespace TEAMModelOS.Services.Common
                                 break;
                             case "day": //周期内每天
                                 Field = $"{userid}-day-{now.ToString("yyyyMMdd")}";
-                                value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}_{vote.code}", Field);
+                                value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}", Field);
                                 msgid = await VoteIng(vote, value, msgid, option, Field, curr, _azureRedis,userid);
                                 break;
                             case "week": //自然周
                                 Field = $"{userid}-week-{now.ToString("yyyy")}{GetWeek(now)}";
-                                value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}_{vote.code}", Field);
+                                value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}", Field);
                                 msgid = await VoteIng(vote, value, msgid, option, Field, curr, _azureRedis, userid); 
                                 break;
                             case "month":  //月份
                                 Field = $"{userid}-month-{now.ToString("yyyyMM")}";
-                                value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}_{vote.code}", Field);
+                                value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}", Field);
                                 msgid = await VoteIng(vote, value, msgid, option, Field, curr, _azureRedis, userid); 
                                 break;
                             case "year"://年份
                                 Field = $"{userid}-year-{now.ToString("yyyy")}";
-                                value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}_{vote.code}", Field);
+                                value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}", Field);
                                 msgid = await VoteIng(vote, value, msgid, option, Field, curr, _azureRedis, userid);
                                 break;
                         }
@@ -127,25 +127,43 @@ namespace TEAMModelOS.Services.Common
             }
             return msgid;
         }
-        public static async Task<byte> VoteIng(Vote vote, RedisValue value, byte msgid, List<string> option, string Field, long curr, AzureRedisFactory _azureRedis,string userid)
+        public static async Task<byte> VoteIng(Vote vote, RedisValue value, byte msgid, Dictionary<string, int> option, string Field, long curr, AzureRedisFactory _azureRedis,string userid)
         {
             if (!value.IsNullOrEmpty)
             {
                 VoteRecord record=value.ToString().ToObject<VoteRecord>();
+                int addCount = 0;
+                foreach (var op in option) {
+                    addCount +=op.Value;
+                }
+                int crdCount = 0;
+                foreach (var op in record.opt)
+                {
+                    crdCount += op.Value;
+                }
                 //处理记录投票+当前设置的投票是否小于等于周期内最大投票数
-                if (record.opt.Count + option.Count <= vote.voteNum)
+                if (addCount + crdCount <= vote.voteNum)
                 {
-                    record.opt.AddRange(option);
+                    foreach (var op in option)
+                    {
+                        if (record.opt.ContainsKey(op.Key))
+                        {
+                            record.opt[op.Key] = record.opt[op.Key] + op.Value;
+                        }
+                        else {
+                            record.opt.Add(op.Key, op.Value);
+                        }
+                    }
                     record.time = curr;
                     record.userid = userid;
                     //保存投票记录
-                    bool status = await _azureRedis.GetRedisClient(8).HashSetAsync($"Vote:Record:{vote.id}_{vote.code}", Field, record.ToJsonString());
+                    bool status = await _azureRedis.GetRedisClient(8).HashSetAsync($"Vote:Record:{vote.id}", Field, record.ToJsonString());
                     //单独保存每个人方便查询的记录
-                    bool stuallstatus = await _azureRedis.GetRedisClient(8).HashSetAsync($"Vote:Record:{vote.id}_{vote.code}:{userid}", Field, record.ToJsonString());
+                    bool stuallstatus = await _azureRedis.GetRedisClient(8).HashSetAsync($"Vote:Record:{vote.id}:{userid}", Field, record.ToJsonString());
                     //当前投票分组计数存入活动的Redis
-                    var group_opt= option.GroupBy(x => x);
-                    foreach (var opt in group_opt) {
-                        await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Vote:Count:{vote.id}_{vote.code}", opt.Key, opt.Count());
+                    foreach (var opt in option)
+                    {
+                        await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Vote:Count:{vote.id}", opt.Key, opt.Value);
                     }
                     msgid = 1;
                 }
@@ -160,14 +178,13 @@ namespace TEAMModelOS.Services.Common
                 {
                     //保存投票记录
                     VoteRecord record = new VoteRecord { opt = option, time = curr, userid = userid };
-                    bool status = await _azureRedis.GetRedisClient(8).HashSetAsync($"Vote:Record:{vote.id}_{vote.code}", Field, record.ToJsonString());
+                    bool status = await _azureRedis.GetRedisClient(8).HashSetAsync($"Vote:Record:{vote.id}", Field, record.ToJsonString());
                     //单独保存每个人方便查询的记录
-                    bool stuallstatus = await _azureRedis.GetRedisClient(8).HashSetAsync($"Vote:Record:{vote.id}_{vote.code}:{userid}", Field, record.ToJsonString());
+                    bool stuallstatus = await _azureRedis.GetRedisClient(8).HashSetAsync($"Vote:Record:{vote.id}:{userid}", Field, record.ToJsonString());
                     //当前投票分组计数存入活动的Redis
-                    var group_opt = option.GroupBy(x => x);
-                    foreach (var opt in group_opt)
+                    foreach (var opt in option)
                     {
-                        await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Vote:Count:{vote.id}_{vote.code}", opt.Key, opt.Count());
+                        await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Vote:Count:{vote.id}", opt.Key, opt.Value);
                     }
                     if (status)
                     {
@@ -429,6 +446,127 @@ namespace TEAMModelOS.Services.Common
             }
             return (datas, continuationTokenSchool,continuationTokenTeacher);
         }
+
+        public static async Task<int> Answer(JsonElement request, AzureCosmosFactory _azureCosmos, AzureRedisFactory azureRedis, string userid, AzureStorageFactory _azureStorage)
+        {
+
+            DateTimeOffset now = DateTimeOffset.UtcNow;
+            long curr = now.ToUnixTimeMilliseconds();
+            byte msgid = 0;//
+            //活动id
+            if (!request.TryGetProperty("id", out JsonElement id))
+            {
+                return msgid;
+            }
+            //活动分区
+            if (!request.TryGetProperty("code", out JsonElement code))
+            {
+                return msgid;
+            }
+
+            try
+            {
+                //1.再次检查投票
+                var client = _azureCosmos.GetCosmosClient();
+                Survey survey = null;
+                ///TODO 检查是否在投票范围内,包括在tmdids 及班级  但是需要处理认证金钥中的班级问题
+                await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<Survey>(queryText: $"select c.id,c.owner, c.code ,c.ans , c.progress,c.times,c.startTime,c.endTime from c where c.id = '{id}'",
+                    requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{code}") }))
+                {
+                    survey = item;
+                    break;
+                }
+                if (survey != null)
+                {
+                    //判断投票时间是否在起止时间内
+                    if (curr >= survey.startTime && curr <= survey.endTime)
+                    {
+                        if (request.TryGetProperty("record", out JsonElement record)) 
+                        {
+                            var recs = record.ToObject<List<List<string>>>();
+                            if (recs.IsNotEmpty() && recs.Count == survey.ans.Count)
+                            {
+                                //处理问卷调查表的每一题选项数
+                               // List<Task<string>> tasks = new List<Task<string>>();
+                                for (int index = 0; index < recs.Count; index++) {
+                                    Dictionary<string, int> dict = new Dictionary<string, int>();
+                                    if (recs[index].IsNotEmpty()) {
+                                        recs[index].ForEach(x => {
+                                            if (survey.ans[index].Contains(x))
+                                            {
+                                                if (dict.ContainsKey(x))
+                                                {
+                                                    dict[x] = dict[x] + 1;
+                                                }
+                                                else {
+                                                    dict[x] = 1;
+                                                }
+                                            }
+                                            else {
+                                                if (dict.ContainsKey("other"))
+                                                {
+                                                    dict["other"] = dict["other"] + 1;
+                                                }
+                                                else
+                                                {
+                                                    dict["other"] = 1;
+                                                }
+                                                //这里暂不处理, 结算再处理other 
+                                                //  tasks.Add(_azureStorage.UploadFileByContainer(survey.owner,new { other=x, userid, time =curr }.ToJsonString(), "survey", $"{survey.id}/other/{index}/{userid}.json", false));
+                                            }
+                                        });
+                                    }
+                                    var value= azureRedis.GetRedisClient(8).HashGet($"Survey:Record:{survey.id}",index);
+                                    if (value != default && !value.IsNullOrEmpty)
+                                    {
+                                        Dictionary<string, int> dt = value.ToString().ToObject<Dictionary<string, int>>();
+                                        foreach (var kp in dict)
+                                        {   //不建议放在reids
+                                            if (dt.ContainsKey(kp.Key))
+                                            {
+                                                dt[kp.Key] = dt[kp.Key] + kp.Value;
+                                            }
+                                            else
+                                            {
+                                                dt.Add(kp.Key, kp.Value);
+                                            }
+                                        }
+                                        await azureRedis.GetRedisClient(8).HashSetAsync($"Survey:Record:{survey.id}", index, dt.ToJsonString());
+                                    }
+                                    else {
+                                       await azureRedis.GetRedisClient(8).HashSetAsync($"Survey:Record:{survey.id}", index, dict.ToJsonString());
+                                    }
+                                }
+
+                                //处理other ,这里暂不处理, 结算再处理other 
+                                //await Task.WhenAll(tasks);
+                                //保存当前提交人的记录
+                                await _azureStorage.UploadFileByContainer(survey.owner,new { record= record, userid, time = curr }.ToJsonString(), "survey", $"{survey.id}/urecord/{userid}.json");
+                                msgid = 1;
+                            }
+                            else {
+                                //提交的作答不符合问卷的答案长度。
+                                msgid = 3;
+                            }
+                        }
+                        
+                    }
+                    else
+                    {
+                        msgid = 2;
+                    }
+                }
+            }
+            catch (Exception e)
+            {
+                throw new Exception(e.StackTrace);
+            }
+            return msgid;
+        }
+        public class RdsRecord {
+            public Dictionary<string, int> srecord { get; set; } = new Dictionary<string, int>();
+            public Dictionary<string, string[]> urecord { get; set; } = new Dictionary<string, string[]>();
+        }
         private async static Task<dynamic> DoVoteTips(ActivityData commonData , AzureCosmosFactory _azureCosmos,string userid,AzureRedisFactory _azureRedis)
         {
             Vote vote=null;
@@ -454,7 +592,7 @@ namespace TEAMModelOS.Services.Common
                         case "once":
                             // //如果是只能投票一次的活动则直接获取Redis的第一条 只能投一次
                             Field = $"{userid}-once";
-                            RedisValue[] values = _azureRedis.GetRedisClient(8).HashValues($"Vote:Record:{vote.id}_{vote.code}");
+                            RedisValue[] values = _azureRedis.GetRedisClient(8).HashValues($"Vote:Record:{vote.id}");
                             if (values != null && values.Length>0)
                             {
                                 value = values[0];
@@ -462,21 +600,21 @@ namespace TEAMModelOS.Services.Common
                             break;
                         case "day": //周期内每天
                             Field = $"{userid}-day-{now.ToString("yyyyMMdd")}";
-                            value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}_{vote.code}", Field);
+                            value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}", Field);
                             break;
                         case "week": //自然周
                             Field = $"{userid}-week-{now.ToString("yyyy")}{GetWeek(now)}";
-                            value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}_{vote.code}", Field);
+                            value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}", Field);
                             
                             break;
                         case "month":  //月份
                             Field = $"{userid}-month-{now.ToString("yyyyMM")}";
-                            value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}_{vote.code}", Field);
+                            value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}", Field);
                            
                             break;
                         case "year"://年份
                             Field = $"{userid}-year-{now.ToString("yyyy")}";
-                            value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}_{vote.code}", Field);
+                            value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}", Field);
                            
                             break;
                     }

+ 1 - 1
TEAMModelOS/TEAMModelOS.csproj

@@ -7,7 +7,7 @@
     <PackageReference Include="Caching.CSRedis" Version="3.6.50" />
     <PackageReference Include="CSRedisCore" Version="3.6.5" />
     <PackageReference Include="DotNetZip" Version="1.15.0" />
-    <PackageReference Include="HTEXLib" Version="2.4.7" />
+    <PackageReference Include="HTEXLib" Version="2.4.9" />
     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.6" />
       <PackageReference Include="VueCliMiddleware" Version="5.0.0" />
   </ItemGroup>