Browse Source

Merge branch 'develop3.0' into TPE/develop

jeff 4 years ago
parent
commit
704b0676dd
51 changed files with 1511 additions and 696 deletions
  1. 28 28
      TEAMModelFunction/ClassChangeServiceBus.cs
  2. 14 9
      TEAMModelFunction/TriggerSurvey.cs
  3. 46 5
      TEAMModelFunction/TriggerVote.cs
  4. 40 1
      TEAMModelOS.SDK/Extension/Utils.cs
  5. 18 0
      TEAMModelOS.SDK/Models/Cosmos/Common/ActivityData.cs
  6. 2 2
      TEAMModelOS.SDK/Models/Cosmos/Common/Vote.cs
  7. 10 8
      TEAMModelOS/ClientApp/src/common/BaseLayout.vue
  8. 11 11
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseQnForm.vue
  9. 3 1
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReport.vue
  10. 22 22
      TEAMModelOS/ClientApp/src/components/vote/BaseVoteForm.vue
  11. 22 0
      TEAMModelOS/ClientApp/src/locale/lang/en-US/home.js
  12. 3 0
      TEAMModelOS/ClientApp/src/locale/lang/en-US/index.js
  13. 4 4
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/evaluation.js
  14. 22 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/home.js
  15. 6 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/index.js
  16. 54 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/survey.js
  17. 49 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/vote.js
  18. 4 4
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/evaluation.js
  19. 22 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/home.js
  20. 6 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/index.js
  21. 54 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/survey.js
  22. 49 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/vote.js
  23. 34 9
      TEAMModelOS/ClientApp/src/store/index.js
  24. 17 21
      TEAMModelOS/ClientApp/src/utils/evTools.js
  25. 1 1
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseExerciseList.vue
  26. 158 12
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseImport.vue
  27. 14 8
      TEAMModelOS/ClientApp/src/view/homepage/AcCountPie.vue
  28. 8 8
      TEAMModelOS/ClientApp/src/view/homepage/HomePage.vue
  29. 10 3
      TEAMModelOS/ClientApp/src/view/homepage/TeachScore.vue
  30. 1 1
      TEAMModelOS/ClientApp/src/view/homepage/TechScore.vue
  31. 40 17
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtPrivEva.vue
  32. 7 5
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.vue
  33. 1 1
      TEAMModelOS/ClientApp/src/view/learnactivity/Scoring.vue
  34. 2 0
      TEAMModelOS/ClientApp/src/view/login/Index.vue
  35. 3 0
      TEAMModelOS/ClientApp/src/view/newcourse/NewCoursePlan.less
  36. 26 30
      TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.vue
  37. 3 3
      TEAMModelOS/ClientApp/src/view/schoolmgmt/ClassroomSetting/ClassroomSetting.vue
  38. 1 0
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/TestAnalysis/QuestionList.vue
  39. 20 28
      TEAMModelOS/ClientApp/src/view/vote/ManageVote.vue
  40. 29 18
      TEAMModelOS/Controllers/Common/ExamController.cs
  41. 159 376
      TEAMModelOS/Controllers/Common/SurveyController.cs
  42. 38 22
      TEAMModelOS/Controllers/Common/VoteController.cs
  43. 44 6
      TEAMModelOS/Controllers/Core/CoreController.cs
  44. 4 4
      TEAMModelOS/Controllers/Core/ImportController.cs
  45. 73 1
      TEAMModelOS/Controllers/Knowledge/KnowledgesController.cs
  46. 5 4
      TEAMModelOS/Controllers/School/StudentCommonController.cs
  47. 30 5
      TEAMModelOS/Controllers/Teacher/TeacherCommonController.cs
  48. 6 3
      TEAMModelOS/JsonFile/Core/LangConfigV3.json
  49. 47 13
      TEAMModelOS/Services/Common/ActivityService.cs
  50. 237 0
      TEAMModelOS/Services/Common/ActivityTeacherService.cs
  51. 4 1
      TEAMModelOS/TEAMModelOS.csproj

+ 28 - 28
TEAMModelFunction/ClassChangeServiceBus.cs

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

+ 14 - 9
TEAMModelFunction/TriggerSurvey.cs

@@ -19,10 +19,12 @@ namespace TEAMModelFunction
                CosmosClient client, Document input, string code, long stime, long etime, string school)
         {
             Survey survey = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Survey>(input.Id, new Azure.Cosmos.PartitionKey($"{code}"));
-            //messageSurvey.ScheduledEnqueueTime = DateTimeOffset.FromUnixTimeMilliseconds(stime);
-            //string msgid = messageSurvey.MessageId;
             List<ChangeRecord> changeRecords = await _azureStorage.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", input.Id }, { "PartitionKey", survey.progress } });
-            //ChangeRecord surveyRecord = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<ChangeRecord>(input.Id, new Azure.Cosmos.PartitionKey($"{survey.progress}"));
+            if (survey.ttl >= 1)
+            {
+                //TODO  处理TTL删除业务
+                return;
+            }
             switch (survey.progress)
             {
                 case "pending":
@@ -34,7 +36,6 @@ namespace TEAMModelFunction
                         long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync("active-task", messageSurvey, DateTimeOffset.FromUnixTimeMilliseconds(stime));
                         changeRecords[0].sequenceNumber = start;
                         await _azureStorage.SaveOrUpdate<ChangeRecord>(changeRecords[0]);
-                        //await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(surveyRecord, surveyRecord.id, new Azure.Cosmos.PartitionKey($"{surveyRecord.code}"));
                     }
                     else
                     {
@@ -47,18 +48,16 @@ namespace TEAMModelFunction
                             msgId = messageSurvey.MessageId
                         };
                         await _azureStorage.Save<ChangeRecord>(changeRecord);
-                        //await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(changeRecord, new Azure.Cosmos.PartitionKey($"{changeRecord.code}"));
                     }
                     break;
                 case "going":
-                    var tcode = code.Replace("Survey-", "");
                     ActivityData data;
                     if (survey.scope == "school" || survey.scope == "teacher")
                     {
                         data = new ActivityData
                         {
                             id = survey.id,
-                            code = $"Activity-{tcode}",
+                            code = $"Activity-{survey.owner}",
                             type = "survey",
                             name = survey.name,
                             startTime = survey.startTime,
@@ -66,7 +65,9 @@ namespace TEAMModelFunction
                             scode = survey.code,
                             scope = survey.scope,
                             classes = survey.classes,
-                            tmdids = survey.tmdids
+                            tmdids = survey.tmdids,
+                            progress = "going",
+                            owner = survey.owner
                         };
                         await client.GetContainer("TEAMModelOS", "School").UpsertItemAsync<ActivityData>(data, new Azure.Cosmos.PartitionKey(data.code));
                     }
@@ -83,7 +84,8 @@ namespace TEAMModelFunction
                             scode = survey.code,
                             scope = survey.scope,
                             classes = survey.classes,
-                            // tmdids = survey.tmdids
+                            progress = "going",
+                            owner = survey.owner
                         };
                         await client.GetContainer("TEAMModelOS", "Teacher").UpsertItemAsync<ActivityData>(data, new Azure.Cosmos.PartitionKey(data.code));
                     }
@@ -108,6 +110,9 @@ namespace TEAMModelFunction
                         };
                         await _azureStorage.Save<ChangeRecord>(changeRecord);
                     }
+                    break;
+                case "finish":
+
                     break;
             }
         }

+ 46 - 5
TEAMModelFunction/TriggerVote.cs

@@ -21,7 +21,6 @@ namespace TEAMModelFunction
         {
             Vote vote = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Vote>(input.Id, new Azure.Cosmos.PartitionKey($"{code}"));
             List<ChangeRecord> voteRecords = await _azureStorage.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", input.Id }, { "PartitionKey", vote.progress } });
-            //ChangeRecord voteRecord = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<ChangeRecord>(input.Id, new Azure.Cosmos.PartitionKey($"{vote.progress}"));
             if (vote.ttl >= 1)
             {
                 //TODO  处理TTL删除业务
@@ -58,14 +57,13 @@ namespace TEAMModelFunction
                         }
                         break;
                     case "going":
-                        var tcode = code.Replace("Vote-", "");
                         ActivityData data;
                         if (vote.scope == "school" || vote.scope == "teacher")
                         {
                             data = new ActivityData
                             {
                                 id = vote.id,
-                                code = $"Activity-{tcode}",
+                                code = $"Activity-{vote.owner}",
                                 type = "vote",
                                 name = vote.name,
                                 startTime = vote.startTime,
@@ -74,7 +72,8 @@ namespace TEAMModelFunction
                                 scope = vote.scope,
                                 classes = vote.classes,
                                 tmdids = vote.tmdids,
-                                owner=vote.owner
+                                progress= "going",
+                                owner =vote.owner
                             };
                             await client.GetContainer("TEAMModelOS", "School").UpsertItemAsync<ActivityData>(data, new Azure.Cosmos.PartitionKey(data.code));
                         }
@@ -90,6 +89,7 @@ namespace TEAMModelFunction
                                 endTime = vote.endTime,
                                 scode = vote.code,
                                 scope = vote.scope,
+                                progress = "going",
                                 classes = vote.classes,
                                 owner = vote.owner
                                 // tmdids = vote.tmdids
@@ -155,7 +155,48 @@ 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")
+                        {
+                            data = new ActivityData
+                            {
+                                id = vote.id,
+                                code = $"Activity-{vote.owner}",
+                                type = "vote",
+                                name = vote.name,
+                                startTime = vote.startTime,
+                                endTime = vote.endTime,
+                                scode = vote.code,
+                                scope = vote.scope,
+                                classes = vote.classes,
+                                tmdids = vote.tmdids,
+                                progress = "finish",
+                                owner = vote.owner
+                            };
+                            await client.GetContainer("TEAMModelOS", "School").ReplaceItemAsync<ActivityData>(data,data.id, new Azure.Cosmos.PartitionKey(data.code));
+                        }
+                        else if (vote.scope == "private")
+                        {
+                            //更新结束状态
+                            data = new ActivityData
+                            {
+                                id = vote.id,
+                                code = $"Activity-Common",
+                                type = "vote",
+                                name = vote.name,
+                                startTime = vote.startTime,
+                                endTime = vote.endTime,
+                                scode = vote.code,
+                                scope = vote.scope,
+                                progress = "finish",
+                                classes = vote.classes,
+                                owner = vote.owner
+                                // tmdids = vote.tmdids
+                            };
+                            await client.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<ActivityData>(data,data.id, new Azure.Cosmos.PartitionKey(data.code));
+                        }
                         break;
                 }
             }

+ 40 - 1
TEAMModelOS.SDK/Extension/Utils.cs

@@ -3,6 +3,7 @@ using Microsoft.IdentityModel.Tokens;
 using System;
 using System.Collections.Generic;
 using System.Drawing;
+using System.Drawing.Drawing2D;
 using System.Drawing.Imaging;
 using System.IdentityModel.Tokens.Jwt;
 using System.IO;
@@ -144,14 +145,52 @@ namespace TEAMModelOS.SDK.Extension
 
                 return (true, "png", depth);
             }
-
+            
             if (fileheader.StartsWith("47,49,46,38,39,61,") || fileheader.StartsWith("47,49,46,38,37,61,"))
                 return (true, "gif", 0);
+
+            if (fileheader.StartsWith("1,0,0,0,"))
+                return (true, "emf", 0);
+
+            if (fileheader.StartsWith("1,0,9,0,0,3"))
+                return (true, "wmf", 0);
             //if (fileheader.StartsWith("4D,4D") || fileheader.StartsWith("49,49") || fileheader.StartsWith("46,4F,52,4D"))
             //    return (true, "tif");
             return (false, "", 0);
 
         }
+
+        /// <summary>
+        /// 轉換EMF為PNG格式
+        /// </summary>
+        /// <param name="stream"></param>
+        /// <returns></returns>
+        public static string ConvertEMFtoPNG(Stream stream) 
+        {
+            using var image = Image.FromStream(stream);
+
+            //GDI+報錯,Azure Web App沙箱不支持GDI+部分權限
+            //using var nbase64ms = new MemoryStream();
+            //image.Save(nbase64ms, ImageFormat.Png); //保存為PNG格式
+            //byte[] data = nbase64ms.ToArray();
+            //string base64 = Convert.ToBase64String(data);
+            //return base64;
+
+            //Azure Web App沙箱不支持GDI+渲染部分EMF指令,可以出圖但會是空白
+            var pngimage = new Bitmap(image.Width, image.Height);
+            using (var graphics = Graphics.FromImage(pngimage))
+            {
+                graphics.CompositingQuality = CompositingQuality.HighSpeed;
+                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+                graphics.CompositingMode = CompositingMode.SourceCopy;
+                graphics.DrawImage(image, 0, 0, image.Width, image.Height);
+                using var nbase64ms = new MemoryStream();
+                pngimage.Save(nbase64ms, ImageFormat.Png); //保存為PNG格式
+                byte[] data = nbase64ms.ToArray();
+                string base64 = Convert.ToBase64String(data);
+                return base64;
+            }
+        }
         #endregion
 
 

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

@@ -42,9 +42,22 @@ namespace TEAMModelOS.SDK.Models.Cosmos
     //        "tmdids":[]
     //    }
     //]
+    ///简单阐述说明:
+    /// 1.  对于学校产生的活动 即scope=school  或scope=teacher  则对应活动原本的数据的owner 则直接为学校的编码,且ActivityData的code为Activity-学校编码 ActivityData数据存在School表
+    ///     如果scope=private,则活动是老师个人活动,owner=tmdid  ,ActivityData的code则为Activity-Common ActivityData且数据存在Teacher表
+    ///     
+    /// 2.  学生端查询包含 tmdid登录者(可能包含加入学校的教师)以学生身份登入学生端,
+    ///     则需要知道 登录人加入的私人教室id  则输入条件是classes=['私人教室id'],!!!
+    ///     或者用id身份去查询是否存在于tmdids 表示是否是单独被邀请加入活动的。!!!但是这种情况只存在于加入学校的教师,因为私人教室不允许随意邀请别的tmdid,只能加入私人教室。
+    ///     如果是学校学生以学生身份登入,则需要知道该学生加入的学校的班级和私人教室的班级 则classes=['私人教室id','学校教室id']
+    ///     
+    /// 3.  教师端查询ActivityData,则 查询权限为班主任或任课教师等,且这种查询只会存在于校园内的活动,包含scope=school,teacher 发布对象为校园班级班级
+    ///     则需要知道班主任,任课教室管理的班级 则classes=["学校班级id1","学校班级id2"]
+    ///     查询条件 code=Activity-hbcn   classes=["学校班级id1","学校班级id2"]  任课教师的科目id
     /// </summary>
     public class ActivityData : CosmosEntity
     {
+       
         public ActivityData() {
             pk = "Activity";
         }
@@ -56,6 +69,10 @@ namespace TEAMModelOS.SDK.Models.Cosmos
         public long  startTime { get; set; }
         public long endTime { get; set; }
         /// <summary>
+        /// pending 待发布|going 已发布|finish 已结束
+        /// </summary>
+        public string progress { get; set; }
+        /// <summary>
         /// 活动的分区键 Vote-hbcn/Vote-1606294378
         /// </summary>
         public string scode { get; set; }
@@ -66,5 +83,6 @@ namespace TEAMModelOS.SDK.Models.Cosmos
         public List<string> classes { get; set; }
         public List<string> tmdids { get; set; }
         public string owner { get; set; }
+        public List<string> subjects { get; set; }
     }
 }

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

@@ -21,7 +21,7 @@ namespace TEAMModelOS.SDK.Models
 
         }
         /// <summary>
-        /// 学校编码或教tmdid
+        /// 学校编码或教tmdid
         /// </summary>
         [Required(ErrorMessage = "owner 必须设置")]
         public string owner { get; set; }
@@ -42,7 +42,7 @@ namespace TEAMModelOS.SDK.Models
         /// <summary>
         /// pending 待发布|going 已发布|finish 已结束
         /// </summary>
-        [Required(ErrorMessage = "progress 必须设置")]
+        //[Required(ErrorMessage = "progress 必须设置")]
         public string progress { get; set; }
         /// <summary>
         /// 投票选项

+ 10 - 8
TEAMModelOS/ClientApp/src/common/BaseLayout.vue

@@ -214,32 +214,34 @@
                         icon: 'iconfont icon-activityS',
                         name: this.$t('system.menu.scAc'),
                         router: '',
-                        role: 'admin',
-                        permission: 'schoolAc-read|schoolAc-upd',
+                        // role: 'admin',
+                        // permission: 'schoolAc-read|schoolAc-upd',
+                        role: 'teacher|admin',
+                        permission: '',
                         child: [
                             {
                                 icon: 'iconfont icon-test',
                                 name: this.$t('system.menu.scEv'),
                                 router: '/home/schoolEvaluation',
                                 tag: '*',
-                                role: 'admin',
-                                permission: 'schoolAc-read|schoolAc-upd'
+                                role: 'teacher|admin',
+                                permission: ''
                             },
                             {
                                 icon: 'iconfont icon-vote',
                                 name: this.$t('system.menu.scVote'),
                                 router: '/home/manageVote',
                                 tag: '*',
-                                role: 'admin',
-                                permission: 'schoolAc-read|schoolAc-upd'
+                                role: 'teacher|admin',
+                                permission: ''
                             },
                             {
                                 icon: 'iconfont icon-questionnaire',
                                 name: this.$t('system.menu.scQu'),
                                 router: '/home/manageQuestionnaire',
                                 tag: '*',
-                                role: 'admin',
-                                permission: 'schoolAc-read|schoolAc-upd'
+                                role: 'teacher|admin',
+                                permission: ''
                             },
                         ]
                     },

+ 11 - 11
TEAMModelOS/ClientApp/src/components/questionnaire/BaseQnForm.vue

@@ -1,34 +1,34 @@
 <template>
 	<div class="component-qn-form">
 		<Form ref="qnForm" :model="qnForm" label-position="top" :rules="ruleValidate" :disabled="!qnFormEdit">
-			<FormItem label="问卷名称" prop="name">
-				<Input :class="!qnFormEdit ? 'qn-form-disabled':''" v-model="qnForm.name" placeholder="请输入问卷名称"></Input>
+			<FormItem :label="$t('survey.form.name')" prop="name">
+				<Input :class="!qnFormEdit ? 'qn-form-disabled':''" v-model="qnForm.name" :placeholder="$t('survey.form.namePlace')"></Input>
 			</FormItem>
 
-			<FormItem label="问卷对象" prop="targetClassIds">
+			<FormItem :label="$t('survey.form.target')" prop="targetClassIds">
 				<RadioGroup v-model="classType" @on-change="onClassTypeChange" v-if="qnFormEdit">
-				        <Radio label="private">个人班级</Radio>
-				        <Radio label="school">校本班级</Radio>
+				        <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>
 				</div>
-				<Select multiple v-model="qnForm.targetClassIds" :class="!qnFormEdit ? 'qn-form-disabled':''" placeholder="请选择问卷发布对象" v-else>
+				<Select multiple v-model="qnForm.targetClassIds" :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> -->
 				</Select>
 			</FormItem>
 
-			<FormItem label="起止时间" prop="rangeTime">
+			<FormItem :label="$t('survey.form.time')" prop="rangeTime">
 				<DatePicker type="datetimerange" @on-change="onChangeRange" format="yyyy-MM-dd HH:mm" :class="!qnFormEdit ? 'qn-form-disabled':''"
-				 :editable="isDateEdit" placeholder="请选择问卷起止时间" :value="[qnForm.startTime,qnForm.endTime]">
+				 :editable="isDateEdit" :placeholder="$t('survey.form.endTimePlace')" :value="[qnForm.startTime,qnForm.endTime]">
 
 				</DatePicker>
 			</FormItem>
 
 
-			<FormItem label="问卷描述" prop="description">
+			<FormItem :label="$t('survey.form.description')" prop="description">
 				<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>
@@ -179,7 +179,7 @@
 							console.log(params)
 							resolve(params)
 						} else {
-							this.$Message.error('请将信息填写完整')
+							this.$Message.error(this.$t('survey.form.noCompleteTip'))
 						}
 					})
 				})
@@ -199,7 +199,7 @@
 							r(res.courses)
 						} else {
 							j(500)
-							this.$Message.error('获取数据失败')
+							this.$Message.error(this.$t('survey.getDataFailTip'))
 						}
 					})
 				})

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

@@ -319,7 +319,9 @@
                 if (data.file) {
                     this.previewFile = this._.cloneDeep(data)
                     this.previewFile.blobUrl = await this.getFileSas(data)
-                    if (this.previewFile.fileType == 'doc' || this.previewFile.fileType == 'csv' || this.previewFile.fileType == 'xls'|| this.previewFile.fileType == 'pptx') {
+                    if (this.previewFile.fileType == 'doc' || this.previewFile.fileType == 'csv'
+                        || this.previewFile.fileType == 'xls' || this.previewFile.fileType == 'xlsx' ||
+                        this.previewFile.fileType == 'pptx' || this.previewFile.fileType == 'ppt' || this.previewFile.fileType == 'docx') {
                         let url = escape(this.previewFile.blobUrl)
                         this.previewFile.blobUrl = url
                     }

+ 22 - 22
TEAMModelOS/ClientApp/src/components/vote/BaseVoteForm.vue

@@ -1,33 +1,33 @@
 <template>
     <div class="component-vote-form">
         <Form ref="voteForm" :model="voteForm" label-position="top" :rules="ruleValidate" :disabled="!voteFormEdit">
-            <FormItem label="投票名称" prop="name">
+            <FormItem :label="$t('vote.form.name')" prop="name">
                 <Input :class="!voteFormEdit ? 'vote-form-disabled':''" v-model="voteForm.name" placeholder="请输入投票名称"></Input>
             </FormItem>
-            <FormItem label="投票对象" prop="targetClassIds">
+            <FormItem :label="$t('vote.form.target')" prop="targetClassIds">
 				<RadioGroup v-model="classType" @on-change="onClassTypeChange" v-if="voteFormEdit">
-				        <Radio label="private">个人班级</Radio>
-				        <Radio label="school">校本班级</Radio>
+				        <Radio label="private">{{ $t('vote.form.privateClass') }}</Radio>
+				        <Radio label="school">{{ $t('vote.form.schoolClass') }}</Radio>
 				</RadioGroup>
 				<div v-if="!voteFormEdit && curVoteItem" class="vote-class">
 					<span v-for="item in curVoteItem.targetClassIds" class="vote-class-item">{{ getTargetName(item) }}</span>
 				</div>
-				<Select multiple v-model="voteForm.targetClassIds" :class="!voteFormEdit ? 'vote-form-disabled':''" placeholder="请选择投票发布对象" not-found-text="暂未创建班级" v-else>
+				<Select multiple v-model="voteForm.targetClassIds" :class="!voteFormEdit ? 'vote-form-disabled':''" :placeholder="$t('vote.form.targetPlace')" :not-found-text="$t('vote.form.noFoundText')" v-else>
 				   	<Option v-for="item in classRooms.filter(i=>i.scope === classType)" :value="item.id" :key="item.id">{{ item.name }}</Option>
 				</Select>
             </FormItem>
 			
-			<FormItem label="起止时间" prop="rangeTime">
+			<FormItem :label="$t('vote.form.time')" prop="rangeTime">
 			    <!-- <DatePicker type="datetime" :class="!voteFormEdit ? 'vote-form-disabled':''" :editable="isDateEdit" placeholder="请选择投票结束时间" v-model="voteForm.endTime"  :options="endTimeOptions"></DatePicker> -->
-				<DatePicker type="datetimerange" transfer @on-change="onChangeRange" format="yyyy-MM-dd HH:mm" :class="!voteFormEdit ? 'vote-form-disabled':''" :editable="isDateEdit" :value="[voteForm.startTime,voteForm.endTime]" placeholder="请选择投票结束时间"></DatePicker>
+				<DatePicker type="datetimerange" transfer @on-change="onChangeRange" format="yyyy-MM-dd HH:mm" :class="!voteFormEdit ? 'vote-form-disabled':''" :editable="isDateEdit" :value="[voteForm.startTime,voteForm.endTime]" :placeholder="$t('vote.form.endTimePlace')"></DatePicker>
 			</FormItem>
 
-            <FormItem label="投票描述" prop="description">
+            <FormItem :label="$t('vote.form.description')" prop="description">
                 <div ref="descriptionEditor" style="text-align:left" v-show="voteFormEdit"></div>
                 <div v-html="voteForm.description" v-show="!voteFormEdit" style="margin:10px;font-size:16px;font-weight:bold;color:#fff"></div>
             </FormItem>
 
-            <FormItem label="选项设置" prop="attachment" ref="optionsBox">
+            <FormItem :label="$t('vote.form.optionSetting')" prop="attachment" ref="optionsBox">
 				<div v-if="voteOptions.length">
 					<div v-for="(item,index) in voteOptions" :key="index" class="option-editor-wrap">
 						<div v-show="voteFormEdit" style="display: flex;">
@@ -41,7 +41,7 @@
 					</div>
 				</div>
                 
-                <p style="float:right;color:#BDBDBD;cursor:pointer" @click="onAddOption" v-show="voteFormEdit"><Icon type="md-add" />添加选项</p>
+                <p style="float:right;color:#BDBDBD;cursor:pointer" @click="onAddOption" v-show="voteFormEdit"><Icon type="md-add" />{{ $t('vote.form.addOption') }}</p>
             </FormItem>
 
             <!--<FormItem label="投票记录" prop="isReset" v-show="editable && isEdit">
@@ -50,20 +50,20 @@
                 </CheckboxGroup>
             </FormItem>-->
 
-            <FormItem label="可投票数" prop="selectMax">
+            <FormItem :label="$t('vote.form.selectNum')" prop="selectMax">
                 <!--<Input :class="!voteFormEdit ? 'vote-form-disabled':''" v-model="voteForm.count" placeholder="请输入投票名称"></Input>-->
                 <InputNumber :max="10" :min="1" v-model="voteForm.selectMax"></InputNumber>
             </FormItem>
 
-            <FormItem label="其他" prop="secret">
+            <FormItem :label="$t('vote.form.other')" prop="secret">
                 <CheckboxGroup v-model="voteForm.secret">
-                    <Checkbox label="secret">开启匿名投票</Checkbox>
+                    <Checkbox label="secret">{{ $t('vote.form.openSecret') }}</Checkbox>
                 </CheckboxGroup>
             </FormItem>
 
             <FormItem v-show="voteFormEdit">
-                <Button type="primary" class="btn-save" @click="handleSubmit('voteForm')" :loading="isBtnLoading">保存</Button>
-                <Button @click="handleCancel('voteForm')" class="btn-reset" style="margin-left: 8px">取消</Button>
+                <Button type="primary" class="btn-save" @click="handleSubmit('voteForm')" :loading="isBtnLoading">{{ $t('vote.form.save') }}</Button>
+                <Button @click="handleCancel('voteForm')" class="btn-reset" style="margin-left: 8px">{{ $t('vote.form.cancel') }}</Button>
             </FormItem>
         </Form>
     </div>
@@ -218,7 +218,7 @@
 						console.log(params)
 						/* 保存BLOB以及COSMOS */
 						this.saveorUpdateVote({ vote: params, reset: isReset }).then(res => {
-						    this.$Message.success((this.isEdit && this.editInfo.id) ? '修改成功!' : '添加成功!')
+						    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 => {
@@ -229,9 +229,9 @@
                     } else {
 						this.isBtnLoading = false
 						if(this.voteOptionsContent.length){
-							this.$Message.error('请将信息填写完整')
+							this.$Message.error(this.$t('vote.form.noCompleteTip'))
 						}else{
-							this.$Message.error('投票选项个数不能为空!')
+							this.$Message.error(this.$t('vote.form.noOptionTip'))
 						}
                     }
                 })
@@ -340,7 +340,7 @@
                         editor.create()
                     })
                 } else {
-                    this.$Message.warning('最多只有10个选项!')
+                    this.$Message.warning(this.$t('vote.form.optionNumsTip'))
                 }
             },
 
@@ -355,7 +355,7 @@
             				r(res.courses)
             			} else {
             				j(500)
-            				this.$Message.error('获取数据失败')
+            				this.$Message.error(this.$t('vote.form.getDataFailTip'))
             			}
             		})
             	})
@@ -397,7 +397,7 @@
                 const check = this.uploadList.length < 5;
                 if (!check) {
                     this.$Notice.warning({
-                        title: '最多只能上传5个附件'
+                        title: this.$t('vote.form.attachmentMaxTip')
                     });
                 }
                 return check;
@@ -458,7 +458,7 @@
             getTargetName(classId){
 				if(this.classRooms.length){
 					let targetIndex = this.classRooms.map(i => i.id).indexOf(classId)
-					return targetIndex > -1 ?  this.classRooms[targetIndex].name : '未匹配数据'
+					return targetIndex > -1 ?  this.classRooms[targetIndex].name : this.$t('vote.form.noMatchDataTip')
 				}else{
 					this.getClassrooms(this.userInfo.TEAMModelId).then(res => {
 						return res.filter(i => i.id === classId)[0].name

+ 22 - 0
TEAMModelOS/ClientApp/src/locale/lang/en-US/home.js

@@ -0,0 +1,22 @@
+export default{
+    previewStudy:'课前预习',
+    classData:'课堂数据',
+    recentRecord:'近期课堂记录',
+    acCount:'活动概览',
+    goingList:'进行中的活动列表',
+    scNotice:'学校公告',
+    msg:'消息通知',
+    sysMsg:'小豆助手',
+    avgScore:'平均互动指数',
+    col1:'小组学习',
+    col2:'多元评价',
+    col3:'个人学习',
+    col4:'生本决策',
+    col5:'全班互动',
+    col6:'全班测验',
+    ac1:'评量测验',
+    ac2:'自主学习',
+    ac3:'作业活动',
+    ac4:'投票活动',
+    ac5:'问卷调查',
+}

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

@@ -23,6 +23,8 @@ import learnActivity from './learnActivity'
 import global from './global'
 import system from './system'
 import cusMgt from './cusMgt'
+import home from './home'
+
 export default {
   schoolBaseInfo,
   schoolMgmt,
@@ -49,6 +51,7 @@ export default {
   global,
   system,
   cusMgt,
+  home,
   learnActivity,
   formConfigP: {
     input: 'Please Enter ',

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

@@ -42,8 +42,8 @@ export default {
 	level2:'理解',
 	level3:'应用',
 	level4:'分析',
-	level5:'创造',
-	level6:'评价',
+	level5:'评价',
+	level6:'创造',
 	noData:'暂无数据',
 	answer:'答案',
 	noAnswer:'未设置答案',
@@ -181,11 +181,11 @@ export default {
 		downloadDetails:'下载模板制作详情说明',
 		importTips:'导入注意事项',
 		tips1:'点击上方上传图标选择文件',
-		tips2:'只支持".docx"格式的文件导入,请按照模板格式导入',
+		tips2:'只支持".docx,.xlsx,.xls"格式的文件导入,请按照模板格式导入',
 		tips3:'导入题型暂时只支持单选、多选、判断、填空、问答以及综合题型',
 		tips4:'请保持模板语言与当前浏览器语言一致',
 		tips5:'更多注意事项请查看模板制作详情说明',
-		warningTips1:'上传格式仅支持 docx,请重新上传!',
+		warningTips1:'上传格式仅支持 docx/xlsx/xls ,请重新上传!',
 		warningTips2:'最大上传大小为10M,请重新上传!',
 		warningTips3:'试题数据读取完成!',
 		warningTips4:'未解析出符合条件的试题,请检查导入格式是否按照模板文件修改!',

+ 22 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/home.js

@@ -0,0 +1,22 @@
+export default{
+    previewStudy:'课前预习',
+    classData:'课堂数据',
+    recentRecord:'近期课堂记录',
+    acCount:'活动概览',
+    goingList:'进行中的活动列表',
+    scNotice:'学校公告',
+    msg:'消息通知',
+    sysMsg:'小豆助手',
+    avgScore:'平均互动指数',
+    col1:'小组学习',
+    col2:'多元评价',
+    col3:'个人学习',
+    col4:'生本决策',
+    col5:'全班互动',
+    col6:'全班测验',
+    ac1:'评量测验',
+    ac2:'自主学习',
+    ac3:'作业活动',
+    ac4:'投票活动',
+    ac5:'问卷调查',
+}

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

@@ -24,6 +24,9 @@ import learnActivity from './learnActivity'
 import global from './global'
 import system from './system'
 import cusMgt from './cusMgt'
+import home from './home'
+import vote from './vote'
+import survey from './survey'
 export default {
   schoolBaseInfo,
   classMgmt,
@@ -51,6 +54,9 @@ export default {
   global,
   system,
   cusMgt,
+  home,
+  vote,
+  survey,
   test: '测试',
   formConfigP: {
     input: '请输入',

+ 54 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/survey.js

@@ -0,0 +1,54 @@
+export default {
+    list:'问卷活动列表',
+	pending:'待发布',
+	going:'进行中',
+	finish:'已结束',
+	surveyDetails:'问卷详情',
+	undo:'撤销发布',
+	edit:'编辑问卷',
+	save:'保存问卷',
+	cancelEdit:'取消编辑',
+	surveyResult:'问卷数据',
+	addItem:'新增题目',
+	single:'单选题',
+	multiple:'多选题',
+	judge:'判断题',
+	defaultName:'预设问卷名称',
+	isExistTip:'已存在未保存的问卷活动!',
+	getDataFailTip:'获取数据失败!',
+	deletesurvey:'删除问卷',
+	deleteConfirmTip:'确认删除该问卷活动?',
+	cancelSurvey:'取消发布',
+	cancelConfirmTip:'确认取消发布该问卷活动?',
+	deleteSuc:'删除成功',
+	deleteFail:'删除失败',
+	doSuc:'操作成功',
+	doFail:'操作失败',
+	noData:'暂无数据',
+	form:{
+		name:'问卷名称',
+		namePlace:'请输入问卷名称',
+		target:'问卷对象',
+		privateClass:'个人班级',
+		schoolClass:'校本班级',
+		targetPlace:'请选择问卷发布对象',
+		noFoundText:'暂未创建班级',
+		time:'起止时间',
+		endTimePlace:'请选择问卷结束时间',
+		description:'问卷描述',
+		optionSetting:'选项设置',
+		addOption:'添加选项',
+		selectNum:'可问卷数',
+		other:'其他',
+		openSecret:'开启匿名问卷',
+		save:'保存',
+		cancel:'取消',
+		editSuc:'修改成功',
+		addSuc:'添加成功',
+		noCompleteTip:'请将信息填写完整',
+		noOptionTip:'问卷选项个数不能为空!',
+		attachmentMaxTip:'最多只能上传5个附件',
+		optionNumsTip:'最多只能有10个选项',
+		noMatchDataTip:'未匹配数据'
+	}
+}

+ 49 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/vote.js

@@ -0,0 +1,49 @@
+export default {
+    list:'投票活动列表',
+	pending:'待发布',
+	going:'进行中',
+	finish:'已结束',
+	voteDetails:'投票详情',
+	edit:'编辑',
+	voteResult:'投票数据',
+	optionView:'选项清单试图',
+	stuListView:'学生清单视图',
+	defaultName:'预设投票名称',
+	isExistTip:'已存在未保存的投票活动!',
+	getDataFailTip:'获取数据失败!',
+	serverErrorTip:'投票活动接口调整中,请稍后再试!',
+	deleteVote:'删除投票',
+	deleteConfirmTip:'确认删除该投票活动?',
+	deleteSuc:'删除成功',
+	deleteFail:'删除失败',
+	option:'选项',
+	noVote:'未投票',
+	noData:'暂无数据',
+	getClassDataFailTip:'获取班级学生数据异常',
+	form:{
+		name:'投票名称',
+		namePlace:'请输入投票名称',
+		target:'投票对象',
+		privateClass:'个人班级',
+		schoolClass:'校本班级',
+		targetPlace:'请选择投票发布对象',
+		noFoundText:'暂未创建班级',
+		time:'起止时间',
+		endTimePlace:'请选择投票结束时间',
+		description:'投票描述',
+		optionSetting:'选项设置',
+		addOption:'添加选项',
+		selectNum:'可投票数',
+		other:'其他',
+		openSecret:'开启匿名投票',
+		save:'保存',
+		cancel:'取消',
+		editSuc:'修改成功',
+		addSuc:'添加成功',
+		noCompleteTip:'请将信息填写完整',
+		noOptionTip:'投票选项个数不能为空!',
+		attachmentMaxTip:'最多只能上传5个附件',
+		optionNumsTip:'最多只能有10个选项',
+		noMatchDataTip:'未匹配数据'
+	}
+}

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

@@ -42,8 +42,8 @@ export default {
 	level2: '理解',
 	level3: '應用',
 	level4: '分析',
-	level5: '創造',
-	level6: '評鑒',
+	level5: '評鑒',
+	level6: '創造',
 	noData: '暫無數據',
 	answer: '答案',
 	noAnswer: '未設定答案',
@@ -181,11 +181,11 @@ export default {
 		downloadDetails: '下載範本製作詳情說明',
 		importTips: '導入注意事項',
 		tips1: '點擊上方上傳圖標選擇檔案',
-		tips2: '只支持“.docx”格式的檔案導入,請按照範本格式導入',
+		tips2: '只支持“.docx,.xlsx,.xls”格式的檔案導入,請按照範本格式導入',
 		tips3: '導入題型暫時只支持單選、多選、判斷、填空、問答以及綜合題型',
 		tips4: '請保持範本語言與當前瀏覽器語言一致',
 		tips5: '更多注意事項請查看範本製作詳情說明',
-		warningTips1: '上傳格式僅支持docx,請重新上傳!',
+		warningTips1: '上傳格式僅支持 docx/xlsx/xls ,請重新上傳!',
 		warningTips2: '最大上傳大小為10M,請重新上傳!',
 		warningTips3: '試題數據讀取完成!',
 		warningTips4: '未解析出符合條件的試題,請檢查導入格式是否按照範本檔案修改!',

+ 22 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/home.js

@@ -0,0 +1,22 @@
+export default {
+    previewStudy: '課前預習',
+    classData: '課堂數據',
+    recentRecord: '近期課堂記錄',
+    acCount: '活動概覽',
+    goingList: '進行中的活動清單',
+    scNotice: '學校公告',
+    msg: '消息通知',
+    sysMsg: '小豆助手',
+    avgScore: '平均互動指數',
+    col1: '小組學習',
+    col2: '多元評估',
+    col3: '個人學習',
+    col4: '生本決策',
+    col5: '全班互動',
+    col6: '全班測驗',
+    ac1: '評量測驗',
+    ac2: '自主學習',
+    ac3: '工作活動',
+    ac4: '投票活動',
+    ac5: '問卷調查',
+}

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

@@ -24,7 +24,9 @@ import learnActivity from './learnActivity'
 import global from './global'
 import system from './system'
 import cusMgt from './cusMgt'
-
+import home from './home'
+import vote from './vote'
+import survey from './survey'
 export default {
   
   schoolBaseInfo,
@@ -53,6 +55,9 @@ export default {
   global,
   system,
   cusMgt,
+  home,
+  vote,
+  survey,
   test: '測試',
   formConfigP: {
     input: '請輸入',

+ 54 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/survey.js

@@ -0,0 +1,54 @@
+export default {
+	list: '問卷活動清單',
+	pending: '待發佈',
+	going: '進行中',
+	finish: '已結束',
+	surveyDetails: '問卷詳情',
+	undo: '撤銷發佈',
+	edit: '編輯問卷',
+	save: '保存問卷',
+	cancelEdit: '取消編輯',
+	surveyResult: '問卷數據',
+	addItem: '新增題目',
+	single: '單選題',
+	multiple: '复选题',
+	judge: '判斷題',
+	defaultName: '預設問卷名稱',
+	isExistTip: '已存在未保存的問卷活動!',
+	getDataFailTip: '獲取數據失敗!',
+	deletesurvey: '删除問卷',
+	deleteConfirmTip: '確認删除該問卷活動?',
+	cancelSurvey: '取消發佈',
+	cancelConfirmTip: '確認取消發佈該問卷活動?',
+	deleteSuc: '删除成功',
+	deleteFail: '删除失敗',
+	doSuc: '操作成功',
+	doFail: '操作失敗',
+	noData: '暫無數據',
+	form: {
+		name: '問卷名稱',
+		namePlace: '請輸入問卷名稱',
+		target: '問卷對象',
+		privateClass: '個人班級',
+		schoolClass: '校本班級',
+		targetPlace: '請選擇問卷發佈對象',
+		noFoundText: '暫未創建班級',
+		time: '起止時間',
+		endTimePlace: '請選擇問卷結束時間',
+		description: '問卷描述',
+		optionSetting: '選項設定',
+		addOption: '添加選項',
+		selectNum: '可問卷數',
+		other: '其他',
+		openSecret: '開啟匿名問卷',
+		save: '保存',
+		cancel: '取消',
+		editSuc: '修改成功',
+		addSuc: '添加成功',
+		noCompleteTip: '請將資訊填寫完整',
+		noOptionTip: '問卷選項個數不能為空!',
+		attachmentMaxTip: '最多只能上傳5個附件',
+		optionNumsTip: '最多只能有10個選項',
+		noMatchDataTip: '未匹配數據'
+	}
+}

+ 49 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/vote.js

@@ -0,0 +1,49 @@
+export default {
+	list: '投票活動清單',
+	pending: '待發佈',
+	going: '進行中',
+	finish: '已結束',
+	voteDetails: '投票詳情',
+	edit: '編輯',
+	voteResult: '投票數據',
+	optionView: '選項清單試圖',
+	stuListView: '學生清單視圖',
+	defaultName: '預設投票名稱',
+	isExistTip: '已存在未保存的投票活動!',
+	getDataFailTip: '獲取數據失敗!',
+	serverErrorTip: '投票活動介面調整中,請稍後再試!',
+	deleteVote: '删除投票',
+	deleteConfirmTip: '確認删除該投票活動?',
+	deleteSuc: '删除成功',
+	deleteFail: '删除失敗',
+	option: '選項',
+	noVote: '未投票',
+	noData: '暫無數據',
+	getClassDataFailTip: '獲取班級學生數據异常',
+	form: {
+		name: '投票名稱',
+		namePlace: '請輸入投票名稱',
+		target: '投票對象',
+		privateClass: '個人班級',
+		schoolClass: '校本班級',
+		targetPlace: '請選擇投票發佈對象',
+		noFoundText: '暫未創建班級',
+		time: '起止時間',
+		endTimePlace: '請選擇投票結束時間',
+		description: '投票描述',
+		optionSetting: '選項設定',
+		addOption: '添加選項',
+		selectNum: '可投票數',
+		other: '其他',
+		openSecret: '開啟匿名投票',
+		save: '保存',
+		cancel: '取消',
+		editSuc: '修改成功',
+		addSuc: '添加成功',
+		noCompleteTip: '請將資訊填寫完整',
+		noOptionTip: '投票選項個數不能為空!',
+		attachmentMaxTip: '最多只能上傳5個附件',
+		optionNumsTip: '最多只能有10個選項',
+		noMatchDataTip: '未匹配數據'
+	}
+}

+ 34 - 9
TEAMModelOS/ClientApp/src/store/index.js

@@ -12,7 +12,7 @@ import teachers from './module/teachers'
 import studentWeb from './module/studentWeb'
 import scboard from './module/scboard'
 import serviceDriveAuth from './module/serviceDriveAuth'
-import  { GLOBAL }  from '@/static/Global.js'
+import { GLOBAL } from '@/static/Global.js'
 import spaceAuth from './module/spaceAuth'
 import studentAclassOneAuth from './module/studentAclassOneAuth'
 
@@ -33,15 +33,40 @@ const mutations = {
     setSchoolSas(state, obj) {
         state.schoolSas = obj
     },
-    setUserInfo(state, obj) {
+    setUserInfo(state, obj) { //obj还是原来的逻辑传参,没有变化。只是这里内部逻辑添加了判断是否为班主任,班主任对应的班级id,以及授课班级id三个字段的处理逻辑
         obj.schoolCode = obj.schoolCode || GLOBAL.DEFAULT_SCHOOL_CODE
         obj.hasSchool = obj.schoolCode !== GLOBAL.DEFAULT_SCHOOL_CODE
-		state.userInfo = obj
-	},
-	setSchoolCode(state,obj){
-		state.userInfo.schoolCode = obj || GLOBAL.DEFAULT_SCHOOL_CODE
-		state.userInfo.hasSchool = obj !== GLOBAL.DEFAULT_SCHOOL_CODE
-	}
+        obj.isHeadmaster = false      //默认不是班主任
+        obj.mgtClasses = []           //班主任管理的班级id
+        obj.teachClasses = []         //授课班级id
+        let schoolStr = localStorage.getItem('school_profile')
+        if (schoolStr) {
+            schoolStr = decodeURIComponent(schoolStr, "utf-8")
+            let schoolJson = JSON.parse(schoolStr)
+            let mgtClasses = schoolJson.school_classes.filter(item=>{
+                return item.teacher.id == obj.TEAMModelId
+            })
+            if(mgtClasses.length){
+                obj.isHeadmaster = true //是班主任
+                obj.mgtClasses = mgtClasses.map(item=>{
+                    return item.id
+                })
+            }
+            if(schoolJson.school_courses){
+                schoolJson.school_courses.forEach(item => {
+                    let classIds = item.classes.map(classItem =>{
+                        return classItem.id
+                    })
+                    obj.teachClasses.push(...classIds)
+                })
+            }
+        }
+        state.userInfo = obj
+    },
+    setSchoolCode(state, obj) {
+        state.userInfo.schoolCode = obj || GLOBAL.DEFAULT_SCHOOL_CODE
+        state.userInfo.hasSchool = obj !== GLOBAL.DEFAULT_SCHOOL_CODE
+    }
 }
 
 // ACTIONS
@@ -64,7 +89,7 @@ export default new Vuex.Store({
             TEAMModelId: '',
             name: '',
             schoolCode: '',
-			hasSchool:false
+            hasSchool: false
         }
     },
     modules: {

+ 17 - 21
TEAMModelOS/ClientApp/src/utils/evTools.js

@@ -165,9 +165,10 @@ export default {
 	},
 	
 	/* 给富文本添加 */
-	doAddHost(exerciseItem,paperName,examItem){
-		//console.log('addHost接收的examItem',examItem)
-		let container = exerciseItem.scope === 'school' ? store.state.userInfo.schoolCode : store.state.userInfo.TEAMModelId
+	doAddHost(exerciseItem,paperItem){
+		/* 如果操作的是试卷内的试题 则需要拿试卷的code来作为containerName */
+		let curScope = paperItem ? paperItem.scope : exerciseItem.scope
+		let container = curScope === 'school' ? store.state.userInfo.schoolCode : store.state.userInfo.TEAMModelId
 		let isSubjective = exerciseItem.type === 'complete' || exerciseItem.type === 'subjective' || exerciseItem.type === 'compose'
 		let richTextObj = {
 			question: exerciseItem.question,
@@ -188,17 +189,17 @@ export default {
 						for(let i = 0;i<srcList.length;i++){
 							let src = srcList[i]
 							/* 如果是来自试卷的题目 则需要匹配试卷HOST */
-							if(examItem){
-								let examContainer = examItem.examId ?  (examItem.scope === 'school' ? store.state.userInfo.schoolCode : store.state.userInfo.TEAMModelId) : examItem.code
-								let blobUrl = this.getBlobHost() +  '/' + examContainer  + examItem.blob +  '/' + src
+							if(paperItem && paperItem.examId){
+								let examContainer = paperItem.examId
+								let blobUrl = this.getBlobHost() +  '/' + examContainer  + paperItem.blob +  '/' + src
 								try{
 									let addSasUrl = await $tools.getFileSas(blobUrl)
 									richTextObj[key] = richTextObj[key].replace(`src="${ src }"`, `src="${ addSasUrl.url }"`);
 								}catch(e){
 									j(500)
 								}
-							}else if(paperName){
-								let blobUrl = this.getBlobHost() +  '/'  + container + '/paper/' + paperName + '/' + src
+							}else if(paperItem){
+								let blobUrl = this.getBlobHost() +  '/'  + container + '/paper/' + paperItem.name + '/' + src
 								try{
 									let addSasUrl = await $tools.getFileSas(blobUrl)
 									richTextObj[key] = richTextObj[key].replace(`src="${ src }"`, `src="${ addSasUrl.url }"`);
@@ -311,18 +312,12 @@ export default {
 	/* 获取完整的试卷数据 */
 	getFullPaper(paper,examScope){
 		console.log('evTools换取试卷' , paper)
-		let curScope = examScope || paper.scope
+		let curScope = examScope || paper.examScope || paper.scope
 		console.log(curScope)
 		return new Promise(async (r,j) => {
 			let blobHost = curScope === 'school' ?  JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri :  JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8")).blob_uri
 			// 根据试卷的Blob地址 去读取JSON文件
 			let sasString = curScope === 'school' ?  await $tools.getSchoolSas() : await $tools.getPrivateSas()
-			console.log(curScope)
-			console.log(sasString.sas)
-			console.log(sasString.name)
-			console.log(await $tools.getSchoolSas())
-			console.log(await $tools.getPrivateSas())
-			
 			try{
 				let jsonInfo = await $tools.getFile(blobHost + paper.blob + '/index.json' + sasString.sas)
 				let jsonData = JSON.parse(jsonInfo)
@@ -344,11 +339,12 @@ export default {
 								itemJson.exercise.blob = path + '/' + item.url // 添加blob是方便在保存试卷是 refresh 与导入的试题区分开
 								itemJson.exercise.score = item.scoring ? item.scoring.score : 0
 								try{
-									if(paper.examId){
-										itemJson.exercise = await this.doAddHost(itemJson.exercise,paper.name,paper) // 检测试题中的富文本 为有src为相对路径的音视频文件添加blob的HOST地址
-									}else{
-										itemJson.exercise = await this.doAddHost(itemJson.exercise,paper.name) // 检测试题中的富文本 为有src为相对路径的音视频文件添加blob的HOST地址
-									}
+									// if(paper.examId){
+									// 	itemJson.exercise = await this.doAddHost(itemJson.exercise,paper) // 检测试题中的富文本 为有src为相对路径的音视频文件添加blob的HOST地址
+									// }else{
+									// 	itemJson.exercise = await this.doAddHost(itemJson.exercise,paper) // 检测试题中的富文本 为有src为相对路径的音视频文件添加blob的HOST地址
+									// }
+									itemJson.exercise = await this.doAddHost(itemJson.exercise,paper) // 检测试题中的富文本 为有src为相对路径的音视频文件添加blob的HOST地址
 									resolve(itemJson.exercise)
 								}catch(e){
 									reject(e)
@@ -411,7 +407,7 @@ export default {
 							itemJson.exercise.score = item.scoring ? item.scoring.score : 0
 							// jsonData.item.push(itemJson.exercise)
 							try{
-								itemJson.exercise = await this.doAddHost(itemJson.exercise,paper.name,paper)
+								itemJson.exercise = await this.doAddHost(itemJson.exercise,paper)
 								resolve(itemJson.exercise)
 							}catch(e){
 								reject(e)

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

@@ -123,7 +123,7 @@
 								<div class="item-explain" v-show="isShowAnswer">
 									<span class="explain-title">【{{$t('evaluation.filter.level')}}】</span>
 									<div class="item-explain-details">
-										{{ item.level ? exersicesField[item.level - 1] : exersicesField[0]}}
+										{{ item.field ? exersicesField[item.field - 1] : exersicesField[0]}}
 									</div>
 								</div>
 							</div>

+ 158 - 12
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseImport.vue

@@ -4,7 +4,7 @@
                 <Upload multiple
                         :action="uploadUrl"
                         :headers="headers"
-                        :format="['docx']"
+                        :format="['docx','xlsx','xls']"
                         :on-format-error="handleFormatError"
                         :show-upload-list="isShowList"
                         :before-upload="beforeUpload"
@@ -37,18 +37,30 @@
 					<p>3.{{$t('evaluation.importFile.tips3')}}</p>
 					<p>4.{{$t('evaluation.importFile.tips4')}}</p>
 					<p>5.{{$t('evaluation.importFile.tips5')}}</a></p>
-					
 				</div>
             </div>
+			
+			<!-- 添加小题弹窗 -->
+			<Modal v-model="errorModal" width="800" class="bmf-error-modal" ok-text="重新导入" cancel-text="继续预览" @on-ok="onErrorOk" @on-cancel="onErrorCancel">
+				<div class="modal-header" slot="header">图片异常提示</div>
+				<p class="error-title">解析数据存在以下 <mark>{{ errorList.length }}</mark> 处图片异常,请选择<mark> 忽略并继续预览 </mark>还是<mark> 修改后重新导入模板 </mark>?</p>
+				<div class="error-item-list">
+					<p v-for="(item,index) in errorList" :key="index" v-html="item" class="error-item"></p>
+				</div>
+			</Modal>
     </div>
 </template>
 <script>
+	import excel from '@/utils/excel.js'
 	import FileSaver from "file-saver";
     export default {
         props: ['period', 'subject'],
         data() {
             return {
 				curLang:'',
+				errorList:[],
+				importList:[],
+				errorModal:false,
                 isBtnLoading: false,
                 isSelectFinish: false,
                 importLoading: false,
@@ -67,7 +79,8 @@
 					url:'/download/%E9%A2%98%E7%9B%AE%E6%A8%A1%E6%9D%BF%E8%8B%B1%E8%AF%AD.docx',
 					fileName:'TitleTemplate.docx'
 				}],
-				hostName:''
+				hostName:'',
+				fieldArr:['記憶记忆1','理解2','應用应用3','分析4','评价評鑒5','创造創造6']
             }
         },
         created() {
@@ -133,11 +146,94 @@
             },
 
             /** 选择文件上传之前 */
-            beforeUpload() {
-                this.isBtnLoading = true
-                this.isSelectFinish = true
+            beforeUpload(file) {
+				this.isBtnLoading = true
+				this.isSelectFinish = true
+				// 如果是excel格式文档 则需要进行对应解析
+				if(this.isExcel(file)){
+					let excelResult = []
+					this.readExcel(file,data => {
+						if(data.results.length){
+							data.results.forEach(item => {
+								excelResult.push({
+									question:item.Question || '',
+									answer:item.Answer ? [item.Answer] : [],
+									knowledge:item.Concept ? item.Concept.split(',') : [],
+									field:this.getItemField(item),
+									score:item.Point || 0,
+									type:item.Type,
+									children:[],
+									option:this.getItemOptions(item)
+								})
+							})
+							this.$Message.success(this.$t('evaluation.importFile.warningTips3'))
+							this.$emit('importFinish',excelResult)
+							this.isImportFinish = false
+							this.isBtnLoading = false
+							this.exerciseList = []
+						}
+					})
+					return false
+				}
             },
 
+			
+			/* 获取表格解析试题的选项 */
+			getItemOptions(item){
+				let options = []
+				let optionIndex = 0
+				if(item.Type === 'judge'){
+					return [{
+						code:'A',
+						value:'对'
+					},{
+						code:'B',
+						value:'错'
+					}]
+				}else{
+					for(let key in item){
+						if(key.includes('Option')){
+							options.push({
+								code: String.fromCharCode(64 + parseInt(optionIndex + 1)),
+								value:item[key]
+							})
+							optionIndex++
+						}
+					}
+					return options
+				}
+				
+				
+			},
+			
+			/* 获取表格解析试题的认知层次 */
+			getItemField(item){
+				for (let i in this.fieldArr) {
+					if(this.fieldArr[i].includes(item.Edu_Goal)){
+						return + i + 1
+						break
+					}
+				}
+			},
+			
+			
+			/* 判断上传的是否为excel表格文件 */
+			isExcel(file){
+				let  fileType = file.name.split('.')[file.name.split('.').length - 1]
+				return  fileType === 'xlsx' || fileType === 'xls'
+			},
+			
+			/* 解析excel表格 */
+			readExcel(file, callback) {
+			    var reader = new FileReader();
+			    reader.onload = function (e) {
+			        var data = e.target.result;
+			        var workbook = excel.read(data, 'binary');                
+			        if (callback) callback(workbook);
+			    };
+			    reader.readAsBinaryString(file);
+			},
+
             /**
              * 当上传文件格式错误
              * @param
@@ -157,29 +253,54 @@
                 this.isBtnLoading = false
 				this.isSelectFinish = false
             },
+			
+			/* 有异常图片情况下选择重新导入模板 */
+			onErrorOk(){
+				this.$Message.warning('请重新导入正确模板!')
+				this.isImportFinish = false
+				this.isBtnLoading = false
+				this.errorList = []
+				this.importList = []
+				this.exerciseList = []
+			},
+			
+			/* 有异常图片情况下选择继续预览 */
+			onErrorCancel(){
+				this.$Message.success(this.$t('evaluation.importFile.warningTips3'))
+				this.$emit('importFinish',this.importList)
+				this.isImportFinish = false
+				this.isBtnLoading = false
+				this.exerciseList = []
+				this.errorList = []
+				this.importList = []
+			},
 
             /**
              * 上传文件成功回调
              * @param response
              */
             uploadSuccess(response) {
-				if(Array.isArray(response) && response.length){
+				console.log(response)
+				if(response.tests.length && !response.emferror.length){
 					this.$Message.success(this.$t('evaluation.importFile.warningTips3'))
-					this.$emit('importFinish',response)
+					this.$emit('importFinish',response.tests)
 					this.isImportFinish = false
 					this.isBtnLoading = false
 					this.exerciseList = []
+				}else if(response.emferror.length){
+					this.errorModal = true
+					this.importList = response.tests
+					this.errorList = response.emferror
+					this.isImportFinish = false
+					this.isBtnLoading = false
 				}else{
 					this.$Message.error(this.$t('evaluation.importFile.warningTips4'))
 					this.isBtnLoading = false
 				}
+				
 				this.isSelectFinish = false
             },
         },
-
-        mounted() {
-
-        },
         computed: {
             headers() {
                 let hd = {}
@@ -201,6 +322,31 @@
 		height: 100%;
 	}
 	
+	.bmf-error-modal{
+		.error-title{
+			margin: 15px 15px 0 15px;
+			letter-spacing: 1px;
+			font-size: 16px;
+			font-weight: bold;
+		}
+		
+		mark{
+			color: red;
+		}
+		
+		.error-item-list{
+			max-height: 500px;
+			overflow-y:scroll;
+			padding: 15px;
+			
+			.error-item{
+				margin-top: 20px;
+				background: #efefef;
+				padding: 10px;
+			}
+		}
+	}
+	
     .cp-import-container,
     .cp-import-container .ivu-upload {
 		display: flex;

+ 14 - 8
TEAMModelOS/ClientApp/src/view/homepage/AcCountPie.vue

@@ -11,7 +11,13 @@ export default {
             option: {
                 backgroundColor: "#27262b",
                 legend: {
-                    data: ['评量测验', '自主学习', '作业活动', '投票活动', '问卷调查'],
+                    data: [
+                        this.$t('home.ac1'),
+                        this.$t('home.ac2'),
+                        this.$t('home.ac3'),
+                        this.$t('home.ac4'),
+                        this.$t('home.ac5')
+                    ],
                     textStyle: {
                         color: '#DDDDDD',
                         padding: [5, 0, 5, 0]
@@ -22,8 +28,8 @@ export default {
                 grid: {
                     left: '20px',
                     right: '20px',
-                    top:'20px',
-                    bottom:'20px',
+                    top: '20px',
+                    bottom: '20px',
                     containLabel: true
                 },
                 series: [
@@ -53,11 +59,11 @@ export default {
                             show: false
                         },
                         data: [
-                            { value: 335, name: '评量测验', itemStyle: { color: "#00f492" } },
-                            { value: 310, name: '自主学习', itemStyle: { color: "#fa8d38" } },
-                            { value: 234, name: '作业活动', itemStyle: { color: "#f862bb" } },
-                            { value: 135, name: '投票活动', itemStyle: { color: "#65dcda" } },
-                            { value: 1548, name: '问卷调查', itemStyle: { color: "#d6b8ff" } }
+                            { value: 335, name: this.$t('home.ac1'), itemStyle: { color: "#00f492" } },
+                            { value: 310, name: this.$t('home.ac2'), itemStyle: { color: "#fa8d38" } },
+                            { value: 234, name: this.$t('home.ac3'), itemStyle: { color: "#f862bb" } },
+                            { value: 135, name: this.$t('home.ac4'), itemStyle: { color: "#65dcda" } },
+                            { value: 1548, name: this.$t('home.ac5'), itemStyle: { color: "#d6b8ff" } }
                         ],
                         itemStyle: {
                             borderWidth: 5,

+ 8 - 8
TEAMModelOS/ClientApp/src/view/homepage/HomePage.vue

@@ -2,7 +2,7 @@
     <div class="home-page-container dark-iview-split dark-iview-card">
         <div class="prepare-area box-item">
             <p class="list-title">
-                课前预习
+                {{$t('home.previewStudy')}}
             </p>
             <div class="card-content-box">
                 <vuescroll>
@@ -30,7 +30,7 @@
         <div class="class-data-area" style="display:flex;flex-direction:column;">
             <div class="calss-chart-box box-item">
                 <p class="chart-title">
-                    课堂数据
+                    {{$t('home.classData')}}
                 </p>
                 <!-- <i-circle :percent="80" style="margin:auto;display:block;" trail-color="#242328" :stroke-color="['#1E82E0','#1CC0F3','#1C9AE7']" :trail-width="9" :stroke-width="9" :size="180">
                     <p class="demo-Circle-inner" style="font-size:50px;font-weight:bold;color:white;">80</p>
@@ -41,7 +41,7 @@
             </div>
             <div class="upload-record-box box-item" style="flex:1;">
                 <p class="list-title">
-                    近期课堂记录
+                    {{$t('home.recentRecord')}}
                 </p>
                 <div class="card-content-box">
                     <vuescroll>
@@ -63,13 +63,13 @@
         <div class="activity-area" style="display:flex;flex-direction:column;">
             <div class="ac-count-box box-item">
                 <p class="chart-title">
-                    活动概览
+                    {{$t('home.acCount')}}
                 </p>
                 <AcCountPie style="margin-top:-20px;"></AcCountPie>
             </div>
             <div class="ac-list-box box-item" style="flex:1;">
                 <p class="list-title">
-                    进行中活动列表
+                    {{$t('home.goingList')}}
                 </p>
                 <div class="in-pro-detail">
                     <vuescroll>
@@ -161,7 +161,7 @@
         <div class="notice-area">
             <div class="school-notice-box box-item">
                 <p class="list-title">
-                    <span>学校公告</span>
+                    <span>{{$t('home.scNotice')}}</span>
                     <span class="notice-count-tag">2</span>
                 </p>
                 <div class="card-content-box">
@@ -183,7 +183,7 @@
             </div>
             <div class="common-notice-box box-item">
                 <p class="list-title">
-                    <span>消息通知</span>
+                    <span>{{$t('home.msg')}}</span>
                     <span class="notice-count-tag">1</span>
                 </p>
                 <div class="card-content-box">
@@ -233,7 +233,7 @@
             </div>
             <div class="system-notice-box box-item">
                 <p class="list-title">
-                    <span>小豆助手</span>
+                    <span>{{$t('home.sysMsg')}}</span>
                     <span class="notice-count-tag">{{$store.state.userInfo.hasSchool ? 1 : 2}}</span>
                 </p>
                 <div class="card-content-box">

+ 10 - 3
TEAMModelOS/ClientApp/src/view/homepage/TeachScore.vue

@@ -17,8 +17,8 @@ export default {
                 grid: {
                     left: '3%',
                     right: '3%',
-                    top:'20px',
-                    bottom:'20px',
+                    top: '20px',
+                    bottom: '20px',
                     containLabel: true
                 },
                 xAxis: {
@@ -53,7 +53,14 @@ export default {
                         interval: 0
                     },
                     type: 'category',
-                    data: ['小组学习', '多元评价', '个人学习', '生本决策', '全班互动', '全班测验'],
+                    data: [
+                        this.$t('home.col1'),
+                        this.$t('home.col2'),
+                        this.$t('home.col3'),
+                        this.$t('home.col4'),
+                        this.$t('home.col5'),
+                        this.$t('home.col6')
+                    ],
                     axisLabel: {
                         color: '#DDDDDD'
                     },

+ 1 - 1
TEAMModelOS/ClientApp/src/view/homepage/TechScore.vue

@@ -20,7 +20,7 @@ export default {
                         textAlign: 'center',
                     },
                 }, {
-                    text: '平均互动指数',
+                    text: this.$t('home.avgScore'),
                     left: '50%',
                     top: '65%',
                     textAlign: 'center',

+ 40 - 17
TEAMModelOS/ClientApp/src/view/learnactivity/MgtPrivEva.vue

@@ -5,7 +5,17 @@
             <!--评测列表-->
             <div class="evaluation-list-wrap" slot="left">
                 <div class="evaluation-list-title">
-                    <span>{{$t('learnActivity.mgtScEv.listLabel')}}</span>
+                    <span v-if="!$store.state.userInfo.isHeadmaster">{{$t('learnActivity.mgtScEv.listLabel')}}</span>
+                    <Dropdown v-else class="sort-dropdown" trigger="click" placement="bottom-start" @on-click="function(e){ curEvValue = e }" @on-visible-change="dropdownStates">
+                        <span style="cursor: pointer;">
+                            <!-- {{$t('learnActivity.mgtScEv.period')}} -->
+                            <b class="title">{{ curEvLabel }}</b>
+                            <Icon type="ios-arrow-down" style="margin-left:8px;"></Icon>
+                        </span>
+                        <DropdownMenu slot="list" v-for="(item,index) in evFilter" :value="item.value" :key="index">
+                            <DropdownItem :name="item.value">{{ item.label }}</DropdownItem>
+                        </DropdownMenu>
+                    </Dropdown>
                     <Icon type="md-add" class=" to-create-icon" @click="goToCreate" :title="$t('learnActivity.mgtScEv.create')" />
                     <Icon type="md-trash" v-show="evaListShow.length" class="to-create-icon" :title="$t('learnActivity.mgtScEv.delete')" @click="deleteEvaluation" />
                     <Icon type="md-create" v-show="evaListShow.length && evaListShow[curEvaIndex].progress == 'pending'" class="to-create-icon" @click="editEvaluation" :title="$t('learnActivity.mgtScEv.edit')" />
@@ -98,6 +108,7 @@ export default {
     inject: ['reload'],
     data() {
         return {
+            curEvValue:0,
             split1: 0.2,
             scope: '',//school 校本 private 个人
             showBack: false,
@@ -109,15 +120,34 @@ export default {
             examDetaiInfo: {},
             targetList: [],
             isLoading: false,
-            filterPeriod: undefined,
             schoolBase: {
                 period: []
             },
+            evFilter:[
+                {
+                    label:'我发布的评测',
+                    value:0
+                },
+                {
+                    label:'数学评测',
+                    value:1
+                },
+                {
+                    label:'英语评测',
+                    value:2
+                }
+            ],
             scoreLoading: false,
             answerLoading: false
         }
     },
     methods: {
+        dropdownStates(flag) {
+            if (!flag) this.filterByTag()
+        },
+        filterByTag() {
+            this.curEvaIndex = 0
+        },
         // 模拟教师评分数据
         mockScoring() {
             this.scoreLoading = true
@@ -356,7 +386,7 @@ export default {
                             }, 0)
                             if (resData.papers[index].blob) {
                                 let blob = resData.papers[index].blob
-                                resData.papers[index].scope = resData.scope
+                                resData.papers[index].examScope = resData.scope
                                 resData.papers[index].examId = resData.id
                                 resData.papers[index] = await this.$evTools.getFullPaper(resData.papers[index])
                                 resData.papers[index].blob = blob
@@ -387,10 +417,6 @@ export default {
             (res) => {
                 if (res) {
                     this.schoolBase = res.school_base
-                    // 預設搜尋給第一個
-                    if (res.school_base.period && res.school_base.period.length) {
-                        this.filterPeriod = res.school_base.period[0].id
-                    }
                 }
             }
         ).finally(() => {
@@ -410,16 +436,13 @@ export default {
         }
     },
     computed: {
-        filterPeriodName: function () {
-            let pId = this.filterPeriod
-            let name = ''
-            if (pId) {
-                let temp = this.$store.state.user.schoolProfile.school_base.period.filter(item => {
-                    return pId == item.id
-                })
-                if (temp.length > 0) name = temp[0].name
-            }
-            return name
+        curEvLabel: function () {
+            let value = this.curEvValue
+            console.log(this.curEvValue)
+            let curObj = this.evFilter.find(item=>{
+                return item.value == value
+            })
+            return curObj.label
         },
     }
 }

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

@@ -15,9 +15,9 @@
                             <DropdownItem :name="item.id">{{ item.name }}</DropdownItem>
                         </DropdownMenu>
                     </Dropdown>
-                    <Icon type="md-add" class=" to-create-icon" @click="goToCreate" :title="$t('learnActivity.mgtScEv.create')" />
-                    <Icon type="md-trash" v-show="evaListShow.length" class="to-create-icon" :title="$t('learnActivity.mgtScEv.delete')" @click="deleteEvaluation" />
-                    <Icon type="md-create" v-show="evaListShow.length && evaListShow[curEvaIndex] && evaListShow[curEvaIndex].progress == 'pending'" class="to-create-icon" @click="editEvaluation" :title="$t('learnActivity.mgtScEv.edit')" />
+                    <Icon type="md-add" class=" to-create-icon" @click="goToCreate" :title="$t('learnActivity.mgtScEv.create')" v-if="$access.can('admin.*|schoolAc-upd')"/>
+                    <Icon type="md-trash" v-show="evaListShow.length" class="to-create-icon" :title="$t('learnActivity.mgtScEv.delete')" @click="deleteEvaluation" v-if="$access.can('admin.*|schoolAc-upd')"/>
+                    <Icon type="md-create" v-show="evaListShow.length && evaListShow[curEvaIndex] && evaListShow[curEvaIndex].progress == 'pending'" class="to-create-icon" @click="editEvaluation" :title="$t('learnActivity.mgtScEv.edit')" v-if="$access.can('admin.*|schoolAc-upd')"/>
                 </div>
                 <div class="evaluation-list-main">
                     <vuescroll>
@@ -330,8 +330,10 @@ export default {
 
         //查询评测列表
         findEvaluation() {
+            console.log('123456',this.$access.hasRole('admin'))
             let requestData = {
-                code: this.scope == 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId
+                code: this.scope == 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId,
+                classIds:this.$access.hasRole('admin') ? undefined : this.$store.state.userInfo.teachClasses
             }
             this.$api.learnActivity.FindExamInfo(requestData).then(
                 res => {
@@ -398,7 +400,7 @@ export default {
                         resData.score = 0
                         for (let index in resData.papers) {
                             let blob = resData.papers[index].blob
-                            resData.papers[index].scope = resData.scope
+                            resData.papers[index].examScope = resData.scope
                             resData.papers[index].examId = resData.id
                             resData.papers[index] = await this.$evTools.getFullPaper(resData.papers[index])
                             if(!resData.papers[index].subjectId){

+ 1 - 1
TEAMModelOS/ClientApp/src/view/learnactivity/Scoring.vue

@@ -48,7 +48,7 @@
                     <Loading slot="loading" :top="-50"></Loading>
                 </Table>
                 <!-- 分页 -->
-                <div class="page-wrap dark-ivew-select">
+                <div class="page-wrap dark-ivew-select" v-show="!showTest">
                     <Page show-total size="small" :current="currentPage" :total="studentScore.length" :page-size="pageSize" :page-size-opts="pageSizeOpts" @on-change="pageChange" @on-page-size-change="pageSizeChange" show-sizer />
                 </div>
                 <div class="dark-iview-table scoring-handle-box" v-show="showTest">

+ 2 - 0
TEAMModelOS/ClientApp/src/view/login/Index.vue

@@ -409,6 +409,7 @@ export default {
               }).catch(err=>{
                 isFail = true
               })
+              console.log('res***',result)
               if(isFail){
                 this.loginErrText = this.$t('login.apiError.text1')
                 this.loading = false
@@ -538,6 +539,7 @@ export default {
       await this.$api.login.loginIES(item, schoolCode).then( res => {
         result = res
       })
+      console.log('////',result)
 
       //設定權限並登入
       let identity = localStorage.getItem('identity')

+ 3 - 0
TEAMModelOS/ClientApp/src/view/newcourse/NewCoursePlan.less

@@ -127,4 +127,7 @@
         border-color:#1cc0f3;
         color:#1cc0f3;
     }
+}
+.cus-table-content{
+    height: ~"calc(100% - 45px)";
 }

+ 26 - 30
TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.vue

@@ -13,7 +13,7 @@
 					<!-- 问卷活动列表 -->
 					<div class="qn-col qn-list-box">
 						<div class="qn-box-header">
-							<span>问卷列表</span>
+							<span>{{ $t('survey.list') }}</span>
 							<div>
 								<Icon type="md-trash" class="to-create-icon" @click="onDeleteQn" style="margin-left: 10px" v-if="qnList.length && ($access.can('admin.*|schoolAc-upd') || isPrivate)" />
 								<Icon type="md-add" class="to-create-icon" @click="goToCreate" v-if="($access.can('admin.*|schoolAc-upd') || isPrivate)" />
@@ -33,7 +33,7 @@
 											<span class="qn-item-nums">
 												<Icon type="md-time" size="14" style="margin-right: 5px" />{{ $tools.formatTime(item.startTime) }}</span>
 											<span class="qn-item-status" :style="{ background: item.progress === 'pending' ? '#0BADD4'  : item.progress === 'going' ? '#12a568' : '#949594', }">
-												{{ item.progress === "pending" ? "待发布" : item.progress === "going"  ? "进行中" : "已结束"  }}
+												{{ item.progress === "pending" ? $t('survey.pending') : item.progress === "going"  ? $t('survey.going') : $t('survey.finish')  }}
 											</span>
 										</div>
 									</div>
@@ -48,12 +48,12 @@
 							<!-- 问卷基础信息展示 -->
 							<div class="qn-col qn-info-box">
 								<div class="qn-box-header">
-									<span>问卷详情</span>
+									<span>{{ $t('survey.surveyDetails') }}</span>
 									<div class="qn-box-header-tools">
 										<!-- <span class="qn-box-header-tools-tool" v-show="currentQn.status !== 300 && qnList.length">
 											<Icon type="md-create" size="18" title="编辑" @click="onEditQn" /></span> -->
 										<span class="qn-box-header-tools-tool" v-show="currentQn.progress === 'going' && qnList.length">
-											<Icon type="md-undo" size="18" title="取消发布" @click="onCancelQn" style="margin-left: 8px" v-if="($access.can('admin.*|schoolAc-upd') || isPrivate)" /></span>
+											<Icon type="md-undo" size="18" :title="$t('survey.undo')" @click="onCancelQn" style="margin-left: 8px" v-if="($access.can('admin.*|schoolAc-upd') || isPrivate)" /></span>
 									</div>
 								</div>
 								<vuescroll ref="qnDetailsScroll">
@@ -70,19 +70,19 @@
 							<!-- 问卷提交数据 -->
 							<div class="qn-col qn-data-box">
 								<div class="qn-box-header">
-									<span>问卷数据</span>
+									<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">
 											<Icon type="md-list-box" color="#dcdcdc" />
 											<Dropdown @on-click="onAddItem">
 												<Button>
-													新增题目
+													{{ $t('survey.addItem') }}
 													<Icon type="ios-arrow-down"></Icon>
 												</Button>
 												<DropdownMenu slot="list">
-													<DropdownItem name="single">单选题</DropdownItem>
-													<DropdownItem name="multiple">多选题</DropdownItem>
-													<DropdownItem name="judge">判断题</DropdownItem>
+													<DropdownItem name="single">{{ $t('survey.single') }}</DropdownItem>
+													<DropdownItem name="multiple">{{ $t('survey.multiple') }}</DropdownItem>
+													<DropdownItem name="judge">{{ $t('survey.judge') }}</DropdownItem>
 												</DropdownMenu>
 											</Dropdown>
 										</div>
@@ -92,16 +92,16 @@
 										</div> -->
 										<div class="qn-box-header-tools-tool" @click="onEditQn" v-show="currentQn.progress === 'pending' && qnList.length && !editable && ($access.can('admin.*|schoolAc-upd') || isPrivate)">
 											<Icon type="md-create" color="#209460" />
-											<span>编辑问卷</span>
+											<span>{{ $t('survey.edit') }}</span>
 										</div>
 							
 										<div class="qn-box-header-tools-tool" v-show="editable" style="margin-right: 20px">
-											<Button class="btn-save" icon="md-folder" :loading="isBtnLoading" @click="onSaveQn">保存问卷</Button>
+											<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>取消编辑</span>
+											<span>{{ $t('survey.cancelEdit') }}</span>
 										</div>
 									</div>
 								</div>
@@ -160,10 +160,10 @@
 			goToCreate() {
 				let hasNoSaveQn = this.qnList.filter(i => !i.id).length
 				if (hasNoSaveQn) {
-					this.$Message.warning("已存在未保存的问卷!");
+					this.$Message.warning(this.$t('survey.isExistTip'));
 				} else {
 					let defaultQn = {
-						name: "预设问卷名称",
+						name: this.$t('survey.defaultName'),
 						targetClassIds: [],
 						endTime: "",
 						startTime: "",
@@ -186,10 +186,8 @@
 			/** 删除当前问卷 */
 			onDeleteQn() {
 				this.$Modal.confirm({
-					title: "删除问卷",
-					content: "<p>确认删除该问卷?</p>",
-					okText: "确认",
-					cancelText: "取消",
+					title: this.$t('survey.deletesurvey'),
+					content: this.$t('survey.deleteConfirmTip'),
 					onOk: () => {
 						this.isLoading = true;
 
@@ -203,19 +201,19 @@
 								.then((res) => {
 									if (!res.error) {
 										this.isLoading = false;
-										this.$Message.success("操作成功");
+										this.$Message.success(this.$t('survey.deleteSuc'));
 										this.handleTabClick(
 											this.$route.name === "manageQuestionnaire" ? 0 : 1
 										);
 									} else {
 										this.isLoading = false;
-										this.$Message.success("操作失败");
+										this.$Message.error(this.$t('survey.deleteFail'));
 									}
 								});
 						} else {
 							this.qnList.splice(this.qnList.indexOf(this.currentQn), 1);
 							this.isLoading = false;
-							this.$Message.success("操作成功");
+							this.$Message.success(this.$t('survey.deleteSuc'));
 							if (this.qnList.length) this.onQnClick(this.qnList[0], 0);
 						}
 					},
@@ -241,7 +239,7 @@
 						}
 						this.isLoadList = false;
 					} else {
-						this.$Message.error("获取数据失败");
+						this.$Message.error(this.$t('survey.getDataFailTip'));
 					}
 				});
 			},
@@ -259,7 +257,7 @@
 							if (!res.error && res.surveys) {
 								r(res.surveys[0]);
 							} else {
-								this.$Message.error("获取数据失败");
+								this.$Message.error(this.$t('survey.getDataFailTip'));
 							}
 						});
 				});
@@ -313,7 +311,7 @@
 						reset: false,
 					})
 					.then((res) => {
-						this.$Message.success("操作成功");
+						this.$Message.success(this.$t('survey.doSuc'));
 						this.$refs.qnForm.qnFormEdit = false;
 						this.editable = false;
 						this.isLoading = false;
@@ -429,10 +427,8 @@
 			/* 取消发布问卷 */
 			onCancelQn() {
 				this.$Modal.confirm({
-					title: "取消发布",
-					content: "<p>确认取消发布该问卷?</p>",
-					okText: "确认",
-					cancelText: "取消",
+					title: this.$t('survey.cancelSurvey'),
+					content: this.$t('survey.cancelConfirmTip'),
 					onOk: () => {
 						this.isLoading = true;
 						this.$api.questionnaire
@@ -442,11 +438,11 @@
 							.then((res) => {
 								if (!res.error) {
 									this.isLoading = false;
-									this.$Message.success("操作成功");
+									this.$Message.success(this.$t('survey.doSuc'));
 									this.getQnList();
 								} else {
 									this.isLoading = false;
-									this.$Message.success("操作失败");
+									this.$Message.success(this.$t('survey.doFail'));
 								}
 							});
 					},

+ 3 - 3
TEAMModelOS/ClientApp/src/view/schoolmgmt/ClassroomSetting/ClassroomSetting.vue

@@ -626,7 +626,7 @@ export default {
             this.resetNoBef = this.students[index].no
         },
         confirmSetNo() {
-            this.$Message.warning('暂未对接API')
+            this.$Message.warning('暂未对接API,请到学生账号管理处理')
             this.editIndex = -1
         },
         cancelSetNo() {
@@ -652,7 +652,7 @@ export default {
                     title: this.$t('schoolBaseInfo.removeTile'),
                     content: `${this.$t('schoolBaseInfo.removeContent')}${names.join(', ')}?`,
                     onOk: () => {
-                        this.$Message.warning('暂未对接API')
+                        this.$Message.warning('暂未对接API,请到学生账号管理处理')
                         // this.$api.stuAccount.removeStudent(requestParams).then(
                         //     res => {
                         //         this.$Message.success(this.$t('schoolBaseInfo.removeOk'))
@@ -669,7 +669,7 @@ export default {
         confirmAddStu() {
             if (this.selections.length > 0) {
                 console.log(this.selections)
-                this.$Message.warning('暂未对接API')
+                this.$Message.warning('暂未对接API,请到学生账号管理处理')
                 //保存操作
                 // this.listLoading = true
                 // this.courseListP[this.curCusIndex].code = this.$store.state.userInfo.TEAMModelId

+ 1 - 0
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/TestAnalysis/QuestionList.vue

@@ -323,6 +323,7 @@
 			
 			async initFullPaper(examPaper,exam){
 				console.log(examPaper.name)
+				examPaper.examId = exam.code.replace('Exam-','')
 				let fullPaperJson =  await this.$evTools.getFullPaper(examPaper,exam.scope)
 				this.questionList = fullPaperJson.item
 				this.SingleList = this.questionList.filter(item => item.type === 'single')

+ 20 - 28
TEAMModelOS/ClientApp/src/view/vote/ManageVote.vue

@@ -1,12 +1,6 @@
 <template>
 	<div class="hw-container">
 		<Loading :top="200" bgColor="rgba(103, 103, 103, 0.27)" type="1" v-show="isLoading"></Loading>
-		<div class="hw-header" style="display: none;">
-			<div>
-				<span :class="tabIndex == 0 ? 'tab-active' : ''" @click="handleTabClick(0)">校本投票</span>
-				<span :class="tabIndex == 1 ? 'tab-active' : ''" @click="handleTabClick(1)">个人投票</span>
-			</div>
-		</div>
 		<div class="hw-box dark-iview-split">
 
 			<Split v-model="split1">
@@ -14,7 +8,7 @@
 					<!-- 投票活动列表 -->
 					<div class="hw-col hw-list-box">
 						<div class="hw-box-header">
-							<span>投票活动列表</span>
+							<span>{{ this.$t('vote.list') }}</span>
 							<span>
 								<Icon type="md-trash" class="to-create-icon" @click="onDeleteVote" style="margin-left:10px" v-if="voteList.length && ($access.can('admin.*|schoolAc-upd') || isPrivate)" />
 								<Icon type="md-add" class="to-create-icon" @click="goToCreate" v-if="($access.can('admin.*|schoolAc-upd') || isPrivate)" />
@@ -33,7 +27,7 @@
 										<div class="hw-item-info">
 											<span class="hw-item-nums">
 												<Icon type="md-time" size="14" style="margin-right:5px" />{{ $tools.formatTime(item.startTime) }}</span>
-											<span class="hw-item-status" :style="{ background: (item.progress === 'pending' ? '#0BADD4' : item.progress === 'going' ? '#088951' : '#949594')}">{{ item.progress === 'pending' ? '待发布' : item.progress === 'going' ? '进行中' : '已结束' }}</span>
+											<span class="hw-item-status" :style="{ background: (item.progress === 'pending' ? '#0BADD4' : item.progress === 'going' ? '#088951' : '#949594')}">{{ item.progress === 'pending' ? $t('vote.pending') : item.progress === 'going' ? $t('vote.going') : $t('vote.finish') }}</span>
 										</div>
 									</div>
 								</div>
@@ -48,10 +42,10 @@
 							<!-- 投票基础信息展示 -->
 							<div class="hw-col hw-info-box">
 								<div class="hw-box-header">
-									<span>投票详情</span>
+									<span>{{ $t('vote.voteDetails') }}</span>
 									<div class="hw-box-header-tools">
 										<span class="hw-box-header-tools-tool" v-show="currentVote.progress === 'pending'">
-											<Icon type="md-create" size="18" title="编辑" @click="onEditVote" v-if="($access.can('admin.*|schoolAc-upd') || isPrivate)" /></span>
+											<Icon type="md-create" size="18" :title="$t('vote.edit')" @click="onEditVote" v-if="($access.can('admin.*|schoolAc-upd') || isPrivate)" /></span>
 									</div>
 								</div>
 								<vuescroll>
@@ -68,10 +62,10 @@
 							<!-- 投票提交数据 -->
 							<div class="hw-col hw-data-box">
 								<div class="hw-box-header">
-									<span>投票数据</span>
+									<span>{{ $t('vote.voteResult') }}</span>
 									<div class="hw-box-header-tools">
 										<span class="hw-box-header-tools-tool" @click="changeTableView" v-show="tableData.length && currentVote.progress !== 'pending'">
-											<Icon type="md-podium" />{{ isOptionView ? '学生清单视图' : '选项清单视图' }}</span>
+											<Icon type="md-podium" />{{ isOptionView ? $t('vote.optionView') : $t('vote.optionView') }}</span>
 									</div>
 								</div>
 								<div v-if="voteList.length === 0 || hasNewAdd || currentVote.progress === 'pending'">
@@ -143,10 +137,10 @@
 			goToCreate() {
 				let hasNoSaveVote = this.voteList.filter(i => !i.id).length
 				if (hasNoSaveVote) {
-					this.$Message.warning("已存在未保存的投票活动!");
+					this.$Message.warning(this.$t('vote.isExistTip'));
 				} else {
 					let defaultVote = {
-						name: '预设投票名称',
+						name: this.$t('vote.defaultName'),
 						code: this.tabIndex === 0 ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId,
 						targetClassIds: [],
 						options: [{
@@ -194,10 +188,10 @@
 						}
 						this.isLoadList = false
 					} else {
-						this.$Message.error('获取数据失败')
+						this.$Message.error(this.$t('vote.getDataFailTip'))
 					}
 				}).catch(err => {
-					this.$Message.error('投票活动接口调整中,请稍后再试~')
+					this.$Message.error(this.$t('vote.serverErrorTip'))
 					this.isLoadList = false
 				})
 			},
@@ -230,10 +224,8 @@
 			/** 删除当前投票 */
 			onDeleteVote() {
 				this.$Modal.confirm({
-					title: '删除投票',
-					content: '<p>确认删除该投票活动?</p>',
-					okText: '确认',
-					cancelText: '取消',
+					title: this.$t('vote.deleteVote'),
+					content: this.$t('vote.deleteConfirmTip'),
 					onOk: () => {
 						this.isLoading = true
 						if (this.currentVote.id) {
@@ -244,20 +236,20 @@
 							}).then(res => {
 								if (!res.error) {
 									this.isLoading = false
-									this.$Message.success('操作成功')
+									this.$Message.success(this.$t('vote.deleteSuc'))
 									this.handleTabClick(this.$route.name === 'manageVote' ? 0 : 1)
 								} else {
 									this.isLoading = false
-									this.$Message.error('操作失败')
+									this.$Message.error(this.$t('vote.deleteFail'))
 								}
 							}).catch(err => {
 								this.isLoading = false
-								this.$Message.error('删除失败')
+								this.$Message.error(this.$t('vote.deleteFail'))
 							})
 						} else {
 							this.voteList.splice(this.voteList.indexOf(this.currentVote), 1)
 							this.isLoading = false
-							this.$Message.success('操作成功')
+							this.$Message.success(this.$t('vote.deleteSuc'))
 							if (this.voteList.length) this.onVoteClick(this.voteList[0], 0)
 						}
 					}
@@ -302,7 +294,7 @@
 							records.forEach((item, index) => {
 								arr.push({
 									// option: this.getSimpleText(item.optionValue),
-									option: item.optionKey ? '选项' + (index + 1) : '未投票',
+									option: item.optionKey ? this.$t('vote.option') + (index + 1) : this.$t('vote.noVote'),
 									key: item.optionKey || '',
 									result: item.students
 								})
@@ -311,7 +303,7 @@
 							console.log(this.studentsTable)
 							this.studentsTable.forEach(i => {
 								let matchList = list.filter(j => j.id === i.code)
-								i.name = matchList.length ? matchList[0].name : '暂无数据'
+								i.name = matchList.length ? matchList[0].name : this.$t('vote.noData')
 							})
 							console.log(arr)
 							this.tableData = arr
@@ -320,11 +312,11 @@
 						}
 						this.isLoading = false
 					} else {
-						this.$Message.error('获取数据失败')
+						this.$Message.error(this.$t('vote.getDataFailTip'))
 						this.isLoading = false
 					}
 				}).catch(err => {
-					this.$Message.error('获取班级学生数据异常')
+					this.$Message.error(this.$t('vote.getClassDataFailTip'))
 					this.isLoading = false
 				})
 			},

+ 29 - 18
TEAMModelOS/Controllers/Common/ExamController.cs

@@ -216,17 +216,27 @@ namespace TEAMModelOS.Controllers
         {
             try
             {
-                //ResponseBuilder builder = ResponseBuilder.custom();
-                //if (!requert.TryGetProperty("id_token", out JsonElement id_token)) return BadRequest();
                 if (!requert.TryGetProperty("code", out JsonElement code)) return BadRequest();
-                //if (!requert.TryGetProperty("school", out JsonElement school_code)) return BadRequest();
-                //if (!requert.TryGetProperty("scope", out JsonElement scope)) return BadRequest();
-                //var jwt = new JwtSecurityToken(id_token.GetString());
-                //if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
-                //var id = jwt.Payload.Sub;
-                var client = _azureCosmos.GetCosmosClient();
-                List<ExamInfo> examInfo = new List<ExamInfo>();
                 var query = $"select c.id,c.name,c.code,c.period,c.startTime,c.endTime,c.stuCount,c.type,c.progress,c.examType,c.createTime, c.subjects, c.grades, c.scope from c ";
+                if (requert.TryGetProperty("classIds", out JsonElement classIds)) {
+                    List<string> ids = classIds.ToObject<List<string>>();
+                    HashSet<string> strs = new HashSet<string>();
+                    if (ids.Count > 1)
+                    {
+                        foreach (string id in ids) {
+                            strs.Add($"array_contains(c.targetClassIds,'{id}')");
+                        }                           
+                    }
+                    else
+                    {
+                        string ssr = ids.Count > 0 ? ids[0] : "";
+                        strs.Add($"array_contains(c.targetClassIds,'{ssr}')");
+                    }
+                    string ss = string.Join(" or ", strs);
+                    query = $"select c.id,c.name,c.code,c.period,c.startTime,c.endTime,c.stuCount,c.type,c.progress,c.examType,c.createTime, c.subjects, c.grades, c.scope from c where ({ss})";
+                };
+                var client = _azureCosmos.GetCosmosClient();
+                List<ExamInfo> examInfo = new List<ExamInfo>();                
                 await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{code}") }))
                 {
                     using var json = await JsonDocument.ParseAsync(item.ContentStream);
@@ -385,16 +395,17 @@ namespace TEAMModelOS.Controllers
                         }
                     }    */                
                     int newIndex = result.studentIds.IndexOf(studentId.ToString());
-                    /*if (flagCount != standard.Count)
+/*                    if (flagCount != standard.Count)
                     {*/
-/*                    StringBuilder builder = new StringBuilder();
-                    builder.Append(result.examId + "/");
-                    builder.Append(result.subjectId + "/");
-                    builder.Append(studentId);*/
-                        string FileName = result.examId + "/" + result.subjectId + "/" + studentId;
-                        string blob = FileName + "/" + "ans.json";
-                        tasks.Add(_azureStorage.UploadFileByContainer(school.ToString(), ans.ToJsonString(), "exam", FileName + "/" + "ans.json", false));
-                        result.studentAnswers[newIndex].Add(blob);
+                        StringBuilder builder = new StringBuilder();
+                        builder.Append(result.examId).Append("/");
+                        builder.Append(result.subjectId).Append("/");
+                        builder.Append(studentId).Append("/");
+                        builder.Append("ans.json");
+                        /*string FileName = result.examId + "/" + result.subjectId + "/" + studentId;
+                        string blob = FileName + "/" + "ans.json";*/
+                        tasks.Add(_azureStorage.UploadFileByContainer(school.ToString(), ans.ToJsonString(), "exam", builder.ToString(), false));
+                        result.studentAnswers[newIndex].Add(builder.ToString());
                     //}                  
                     for (int i = 0; i < ans.Count; i++)
                     {                         

+ 159 - 376
TEAMModelOS/Controllers/Common/SurveyController.cs

@@ -15,6 +15,9 @@ using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.DI.AzureCosmos.Inner;
 using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Helper.Common.CollectionHelper;
+using TEAMModelOS.Models;
+using Microsoft.Extensions.Options;
+using TEAMModelOS.Filter;
 
 namespace TEAMModelOS.Controllers
 {
@@ -31,14 +34,18 @@ namespace TEAMModelOS.Controllers
         private readonly AzureCosmosFactory _azureCosmos;
         private readonly SnowflakeId _snowflakeId;
         private readonly AzureServiceBusFactory _serviceBus;
-        public SurveyController(AzureCosmosFactory azureCosmos, AzureServiceBusFactory serviceBus, SnowflakeId snowflakeId)
+        private readonly DingDing _dingDing;
+        private readonly Option _option;
+        public SurveyController(AzureCosmosFactory azureCosmos, AzureServiceBusFactory serviceBus, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option)
         {
             _snowflakeId= snowflakeId;
-            //  _timerWorkService = timerWorkService;
             _serviceBus = serviceBus;
-              _azureCosmos = azureCosmos;
+            _azureCosmos = azureCosmos;
+            _dingDing = dingDing;
+            _option = option?.Value;
         }
 
+
         /// <summary>
         /// 新增 或 修改投票活动
         /// </summary>
@@ -46,449 +53,225 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("upsert")]
-        public async Task<IActionResult> Upsert(SurveyDto request)
-        {
-            //ResponseBuilder builder = ResponseBuilder.custom();
-            //新增
-            //string code = request.survey.code;
-            var client = _azureCosmos.GetCosmosClient();
-            Survey survey;            
-            //request.survey.school = request.survey.code;
-            request.survey.code =  "Survey-" + request.survey.code;
-            request.survey.createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
-            /*if (request.survey.publishModel.Equals("0"))
-            {
-                //request.survey.startTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
-                request.survey.progress = "going";
-            }
-            else if (request.survey.publishModel.Equals("1"))
-            {
-
-                string msgId = _snowflakeId.NextId() + "";
-                long SequenceNumber = await _serviceBus.GetServiceBusClient().SendLeamMessage<Survey>(Constants.TopicName, request.survey.id, request.survey.code, request.survey.startTime, "going", msgId);
-                request.survey.sequenceNumber = SequenceNumber;
-
-            }*/
-            if (string.IsNullOrEmpty(request.survey.id))
-            {
-                request.survey.id = Guid.NewGuid().ToString();
-                //request.survey.status = 100;
-                request.survey.progress = "pending";
-                //await _serviceBus.GetServiceBusClient().cancelMessage(Constants.TopicName, info.sequenceNumber);
-               /* long SequenceNumber = await _serviceBus.GetServiceBusClient().SendLeamMessage<Survey>(Constants.TopicName, request.survey.id, request.survey.code, request.survey.startTime);
-                request.survey.sequenceNumber = SequenceNumber;*/
-                survey = await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(request.survey, new PartitionKey($"{request.survey.code}"));
-                /*if (request.survey.scope.Equals("school"))
-                {
-                    survey = await client.GetContainer("TEAMModelOS", "School").CreateItemAsync(request.survey, new PartitionKey($"Survey-{code}"));
-                }
-                else
-                {
-                    survey = await client.GetContainer("TEAMModelOS", "Teacher").CreateItemAsync(request.survey, new PartitionKey($"Survey-{code}"));
-                }*/
-                //await _serviceBus.GetServiceBusClient().SendLeamMessage<Survey>(Constants.TopicName, request.id, request.code, request.startTime);
-
-            }
-            else {
-                Survey info = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Survey>(request.survey.id, new PartitionKey($"{request.survey.code}"));
-                if (info.progress.Equals("going"))
-                {
-                    return Ok(new { v = "活动正在进行中" });
-                }
-                //request.survey.code = info.code;
-                request.survey.progress = info.progress;
-                /*try {
-                    await _serviceBus.GetServiceBusClient().cancelMessage(Constants.TopicName, info.sequenceNumber);
-                    long SequenceNumber = await _serviceBus.GetServiceBusClient().SendLeamMessage<Survey>(Constants.TopicName, request.survey.id, request.survey.code, request.survey.startTime);
-                    request.survey.sequenceNumber = SequenceNumber;
-                } catch (Exception e) {
-                    return BadRequest();
-                    //await _dingDing.SendBotMsg($"ServiceBusㄛExamBus()\n{e.Message}", GroupNames.醍摩豆服務運維群組);
-                }        */       
-                
-                survey = await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(request.survey, info.id, new PartitionKey($"{info.code}"));
-                //request.survey.createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
-                /*if (request.survey.scope.Equals("school"))
-                {
-                    survey = await client.GetContainer("TEAMModelOS", "School").ReplaceItemAsync(request.survey, request.survey.id, new PartitionKey($"{request.survey.code}"));
-                }
-                else
-                {
-                    survey = await client.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync(request.survey, request.survey.id, new PartitionKey($"{request.survey.code}"));
-                }*/
-
-
-            }            
-            //Survey homeWork = await _azureCosmos.SaveOrUpdate<Survey>(request.survey);
-
-            //设定结束时间
-            //await _serviceBus.GetServiceBusClient().SendLeamMessage<Survey>(Constants.TopicName, request.survey.id, request.survey.code, request.survey.endTime);
-
-            //清除作业
-            /*if (!request.reset)
-            {
-                //查询之前是否有 关联关系表 HomeWorkStudent 有则删除
-                List<SurveyRecord> surveyRecords = await _azureCosmos.FindByDict<SurveyRecord>(new Dictionary<string, object> { { "id", request.survey.id } });
-                if (surveyRecords.IsNotEmpty())
-                {
-                    await _azureCosmos.DeleteAll(surveyRecords);
-                }
-
-                ////根据作业发布对象查找到每一个具体学生生成关联关系表 HomeWorkStudent
-                //List<Target> targets = request.@params.survey.target;
-                //List<SurveyRecord> surveykStudents = new List<SurveyRecord>();
-                //foreach (Target target in targets)
-                //{
-                   
-                //    List<ClassStudent> classroom = await azureCosmosDBV3Repository.FindByDict<ClassStudent>(new Dictionary<string, object> { { "id", target.classroomCode } });
-                //    if (classroom.IsNotEmpty() && classroom[0].code != null)
-                //    {
-                //        foreach (ClassStudent student in classroom)
-                //        {
-                //            SurveyRecord surveyStudent = new SurveyRecord();
-                //            surveyStudent.id = request.@params.survey.id;
-                //            surveyStudent.code = student.code;
-                //            surveyStudent.classroom.code = target.classroomCode;
-                //            surveyStudent.classroom.name = target.classroomName;
-                //            surveykStudents.Add(surveyStudent);
-                //        }
-                //    }
-                //}
-                //if (surveykStudents.IsNotEmpty())
-                //{
-                //    foreach (SurveyRecord surveyRecord in surveykStudents)
-                //    {
-                //        List<Student> student = await azureCosmosDBV3Repository.FindById<Student>(surveyRecord.code);
-                //        if (student.IsNotEmpty())
-                //        {
-                //            surveyRecord.name = student[0].name;
-                //            surveyRecord.code = student[0].TEAMModelId;
-                //        }
-                //    }
-                //    await azureCosmosDBV3Repository.SaveOrUpdateAll(surveykStudents);
-                //}
-            }*/
-            //return builder.Data(homeWork).build();
-            return Ok(survey);
-        }
-        /// <summary>
-        /// 查询投票活动
-        /// </summary>
-        /// <param name="request"></param>
-        /// <returns></returns>
-        [ProducesDefaultResponseType]
-        [HttpPost("find-summary")]
-        public async Task<IActionResult> FindSummary(JsonElement requert)
-        {
-
-            var client = _azureCosmos.GetCosmosClient();
-            if (!requert.TryGetProperty("code", out JsonElement code)) return BadRequest();
-            if (!requert.TryGetProperty("scope", out JsonElement scope)) return BadRequest();
-            //var (id, name, picture, _) = HttpContext.GetAuthTokenInfo();
-            List<object> surveys = new List<object>();
-            StringBuilder sql = new StringBuilder();
-            sql.Append("select c.id,c.code,c.name,c.startTime,c.endTime,c.description,c.progress from c ");
-            Dictionary<string, object> dict = new Dictionary<string, object>();
-            var emobj = requert.EnumerateObject();
-            while (emobj.MoveNext())
-            {
-                dict[emobj.Current.Name] = emobj.Current.Value;
-            }
-            //处理code
-            if (dict.TryGetValue("code", out object _))
-            {
-                dict.Remove("code");
-            }
-            AzureCosmosQuery cosmosDbQuery = SQLHelper.GetSQL(dict, sql);
-            await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryDefinition: cosmosDbQuery.CosmosQueryDefinition, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Survey-{code}") }))
-            {
-                using var json = await JsonDocument.ParseAsync(item.ContentStream);
-
-                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+        public async Task<IActionResult> Upsert(Survey request) {
+            try {
+                var client = _azureCosmos.GetCosmosClient();
+                request.code = request.pk + "-" + request.code;
+                long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+                request.createTime = now;
+                if (string.IsNullOrEmpty(request.id))
                 {
-                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                    request.id = Guid.NewGuid().ToString();
+                    if (request.startTime < now)
                     {
-                        surveys.Add(obj.ToObject<object>());
+                        request.progress = "pending";
                     }
-                }
-            }
-            /*if (scope.ToString().Equals("school"))
-            {
-                await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryDefinition: cosmosDbQuery.CosmosQueryDefinition, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Survey-{code}") }))
-                {
-                    using var json = await JsonDocument.ParseAsync(item.ContentStream);
-
-                    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                    else
                     {
-                        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
-                        {
-                            surveys.Add(obj.ToObject<object>());
-                        }
+                        request.progress = "going";
                     }
+                    request = await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(request, new PartitionKey($"{request.code}"));
                 }
-            }
-            else
-            {
-                await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryStreamIterator(queryDefinition: cosmosDbQuery.CosmosQueryDefinition, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Survey-{code}") }))
-                {
-                    using var json = await JsonDocument.ParseAsync(item.ContentStream);
-
-                    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                else {
+                    Survey info = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Survey>(request.id, new PartitionKey($"{request.code}"));
+                    if (info.progress.Equals("going"))
                     {
-                        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
-                        {
-                            surveys.Add(obj.ToObject<object>());
-                        }
+                        return Ok(new { v = "活动正在进行中" });
                     }
+                    request.progress = info.progress;
+                    request = await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(request, info.id, new PartitionKey($"{info.code}"));
                 }
-            }*/
-
-            return Ok(new { surveys, surveys.Count });
-            /*ResponseBuilder builder = ResponseBuilder.custom();
-            Dictionary<string, object> dict = new Dictionary<string, object>();
-            var emobj = request.EnumerateObject();
-            while (emobj.MoveNext())
-            {
-                dict[emobj.Current.Name] = emobj.Current.Value;
-            }
-            List<Survey> data = new List<Survey>();
-            List<string> props = new List<string> { "id", "code", "name", "type", "status", "startTime" };
-            if (dict.Keys.Count > 0)
-            { 
-                
-                data = await _azureCosmos.FindByDict<Survey>(dict, props);
-            }
-            else
-            {
-                return builder.Error(ResponseCode.PARAMS_ERROR, "参数异常!").build();
-
+                return Ok(new { survey = request});
+            } catch (Exception ex) {
+                await _dingDing.SendBotMsg($"OS,{_option.Location},common/survey/save()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
+                return BadRequest(ex.StackTrace);
             }
-            return builder.Data(data).Extend(new Dictionary<string, object> { { "count", data.Count },{ "props", props } }).build();*/
+           
         }
         /// <summary>
-        /// 查询投票活动
+        /// 查询问卷调查,用于列表,编辑,查看
         /// </summary>
+        /// <data>
+        ///Survey-学校/教师编码  活动分区        !"code":"hbcn"/1606285227 
+        ///时间筛选范围开始时间 默认30天之前   ?"stime":1608274766154  
+        ///时间筛选范围结束时间 默认当前时间   ?"etime":1608274766666 
+        ///每页大小     ?"count":10/null/Undefined  
+        ///分页Token    ?"continuationToken":Undefined/null/"[{\"token\":\"+RID:~omxMAP3ipcSEEwAAAAAAAA==#RT:2#TRC:20#ISV:2#IEO:65551#QCF:1#FPC:AYQTAAAAAAAAiRMAAAAAAAA=\",\"range\":{\"min\":\"\",\"max\":\"FF\"}}]"
+        /// 当前状态    ?"progress":Undefined/null/"" 表示两种状态都要查询/ "going"/"finish" 表示查询进行中/ 或者已完成 学生端只能查询正在进行或已经结束 going 已发布|finish 已结束  
+        /// </data>
         /// <param name="request"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("find")]
         public async Task<IActionResult> Find(JsonElement requert)
         {
-            var client = _azureCosmos.GetCosmosClient();
-            if (!requert.TryGetProperty("code", out JsonElement code)) return BadRequest();
-            if (!requert.TryGetProperty("scope", out JsonElement scope)) return BadRequest();
-            //var (id, name, picture, _) = HttpContext.GetAuthTokenInfo();
-            List<object> surveys = new List<object>();
-            StringBuilder sql = new StringBuilder();
-            sql.Append("select c.id,c.code,c.name,c.questions,c.classes,c.startTime,c.endTime,c.description,c.progress,c.targetClassIds from c ");
-            Dictionary<string, object> dict = new Dictionary<string, object>();
-            var emobj = requert.EnumerateObject();
-            while (emobj.MoveNext())
-            {
-                dict[emobj.Current.Name] = emobj.Current.Value;
-            }
-            //处理code
-            if (dict.TryGetValue("code", out object _))
-            {
-                dict.Remove("code");
-            }
-            AzureCosmosQuery cosmosDbQuery = SQLHelper.GetSQL(dict, sql);
-            await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryDefinition: cosmosDbQuery.CosmosQueryDefinition, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Survey-{code}") }))
-            {
-                using var json = await JsonDocument.ParseAsync(item.ContentStream);
+            try {
+                //必须有学校或者教师编码
+                if (!requert.TryGetProperty("code", out JsonElement code)) return BadRequest();
+                //开始时间,默认最近三十天
+                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))
+                    {
+                        stimestamp = data;
+                    };
+                };
+                //默认当前时间
+                var etimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+                if (requert.TryGetProperty("etime", out JsonElement etime))
+                {
+                    if (!etime.ValueKind.Equals(JsonValueKind.Undefined) && etime.TryGetInt64(out long data))
+                    {
+                        etimestamp = data;
+                    };
 
-                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                };
+                var progresssql = "";
+                if (!requert.TryGetProperty("progress", out JsonElement progress))
                 {
-                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+
+                    if (!progress.ValueKind.Equals(JsonValueKind.Undefined) && !progress.ValueKind.Equals(JsonValueKind.Null) && progress.ValueKind.Equals(JsonValueKind.String))
                     {
-                        surveys.Add(obj.ToObject<object>());
+                        progresssql = $" and c.progress='{progresssql}' ";
                     }
                 }
-            }
-            /*if (scope.ToString().Equals("school"))
-            {
-                await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryDefinition: cosmosDbQuery.CosmosQueryDefinition, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Survey-{code}") }))
+                string continuationToken = null;
+                //默认不指定返回大小
+                int? topcout = null;
+                if (requert.TryGetProperty("count", out JsonElement jcount))
                 {
-                    using var json = await JsonDocument.ParseAsync(item.ContentStream);
-
-                    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                    if (!jcount.ValueKind.Equals(JsonValueKind.Undefined) && jcount.TryGetInt32(out int data))
                     {
-                        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
-                        {
-                            surveys.Add(obj.ToObject<object>());
-                        }
+                        topcout = data;
                     }
-                }
-            }
-            else {
-                await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryStreamIterator(queryDefinition: cosmosDbQuery.CosmosQueryDefinition, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Survey-{code}") }))
+                };
+                //是否需要进行分页查询,默认不分页
+                bool iscontinuation = false;
+                //如果指定了返回大小
+                if (requert.TryGetProperty("continuationToken", out JsonElement continuation))
+                {
+                    //指定了cancellationToken 表示需要进行分页
+                    if (!continuation.ValueKind.Equals(JsonValueKind.Null) && !continuation.ValueKind.Equals(JsonValueKind.Undefined))
+                    {
+                        continuationToken = continuation.GetString();
+                        iscontinuation = true;
+                    }
+                };
+                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 } ";
+                await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: query,
+                    requestOptions: new QueryRequestOptions() { MaxItemCount = topcout, PartitionKey = new PartitionKey($"Vote-{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())
                         {
-                            surveys.Add(obj.ToObject<object>());
+                            surveys.Add(obj.ToObject<JsonElement>());
+                        }
+                        //如果需要分页则跳出
+                        if (iscontinuation)
+                        {
+                            continuationToken = item.GetContinuationToken();
+                            break;
                         }
                     }
                 }
-            }*/
-
-
-            return Ok(new { surveys, surveys.Count });
-            /*ResponseBuilder builder = ResponseBuilder.custom();
-            Dictionary<string, object> dict = new Dictionary<string, object>();
-            var emobj = request.EnumerateObject();
-            while (emobj.MoveNext())
-            {
-                dict[emobj.Current.Name] = emobj.Current.Value;
-            }
-            List<Survey> data = new List<Survey>();
-            if (dict.Keys.Count > 0)
-            {
-                data = await _azureCosmos.FindByDict<Survey>(dict);
+                return Ok(new { surveys, continuationToken });
+            } catch (Exception ex) {
+                await _dingDing.SendBotMsg($"OS,{_option.Location},common/survey/find()\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                return BadRequest(ex.StackTrace);
             }
-            else
-            {
-                return builder.Error(ResponseCode.PARAMS_ERROR, "参数异常!").build();
-
-            }
-            return builder.Data(data).Extend(new Dictionary<string, object> { { "count", data.Count } }).build();*/
+          
         }
-
-        /// <summary>
-        /// 删除投票活动
+        ///<summary>
+        /// 查询问卷调查,用于创建者列表,编辑,查看,作答人员查看
         /// </summary>
+        /// <data>
+        ///    ! "id":"3c075347-75ef-4bcb-ae03-68678d02d5ef",
+        ///    ! "code":"Survey-hbcn"/"code":"Survey-1606285227"
+        /// </data>
         /// <param name="request"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [HttpPost("delete")]
-        public async Task<IActionResult> Delete(JsonElement request)
+        [HttpPost("find-id")]
+        public async Task<IActionResult> FindById(JsonElement requert)
         {
             try
             {
-                if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
-                if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
-                if (!request.TryGetProperty("scope", out JsonElement scope)) return BadRequest();
                 var client = _azureCosmos.GetCosmosClient();
-                var response = await client.GetContainer("TEAMModelOS", "Common").DeleteItemStreamAsync(id.ToString(), new PartitionKey($"{code}"));
-                return Ok(new { code = response.Status });
-                /*if (scope.ToString().Equals("school"))
+                //活动id
+                if (!requert.TryGetProperty("id", out JsonElement id)) return BadRequest();
+                //活动分区
+                if (!requert.TryGetProperty("code", out JsonElement code)) return BadRequest();
+                Survey survey = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Survey>(id.GetString(), new PartitionKey($"{code}"));
+                if (survey != null)
                 {
-                    var response = await client.GetContainer("TEAMModelOS", "School").DeleteItemStreamAsync(id.ToString(), new PartitionKey($"{code}"));
-                    return Ok(new { code = response.Status });
+
+                    return Ok(new { survey });
                 }
                 else
                 {
-                    var response = await client.GetContainer("TEAMModelOS", "Teacher").DeleteItemStreamAsync(id.ToString(), new PartitionKey($"{code}"));
-                    return Ok(new { code = response.Status });
-                }*/
-
+                    return BadRequest("id,code不存在!");
+                }
             }
-            catch
+            catch (Exception ex)
             {
-                return BadRequest();
+                await _dingDing.SendBotMsg($"OS,{_option.Location},common/survey/find-id()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
+                return BadRequest(ex.StackTrace);
             }
-            /* ResponseBuilder builder = ResponseBuilder.custom();
-             List<IdPk> idPks = await _azureCosmos.DeleteAll<Survey>(request);
-             if (idPks.IsNotEmpty())
-             {
-                 foreach (IdPk idPk in idPks)
-                 {
-                     List<SurveyRecord> surveys = await _azureCosmos.FindByDict<SurveyRecord>(new Dictionary<string, object> { { "id", idPk.id } });
-                     await _azureCosmos.DeleteAll(surveys);
-                 }
-                 //builder.Data(idPks);
-             }
-             else
-             {
-                 return Ok("删除失败!");
-                 //return builder.Error(ResponseCode.FAILED, "删除失败!").build();
-             }
-             return Ok(idPks);*/
-            //return builder.build();
-        }
-
-        // TODO 代码优化
-
-        /// <summary>
-        /// 撤消问卷
-        /// </summary>
-        /// <param name="request"></param>
-        /// <returns></returns>
-        [ProducesDefaultResponseType]
-        [HttpPost("cancel")]
-        public async Task<IActionResult> Cancel(JsonElement request)
-        {
-            //request.TryGetProperty("id", out JsonElement surveyId);
-            //ResponseBuilder builder = ResponseBuilder.custom();
-            //List<Survey> surveys = await _azureCosmos.FindByDict<Survey>(new Dictionary<string, object> { { "id", surveyId } });
-
-            //foreach (Survey survey in surveys)
-            //{
-            //    survey.status = 100;
-            //    survey.progress = "pending";
-            //}
-            //List<Survey> survey1 = await _azureCosmos.UpdateAll<Survey>(surveys);
 
-            ////查询之前是否有 关联关系表  有则删除
-            //List<SurveyRecord> surveyStudents = await _azureCosmos.FindByDict<SurveyRecord>(new Dictionary<string, object> { { "id", surveyId } });
-            //if (surveyStudents.IsNotEmpty())
-            //{
-            //    await _azureCosmos.DeleteAll(surveyStudents);
-            //}
-            return Ok();
         }
 
         /// <summary>
-        /// 查询学生问卷调查记录
+        /// 删除问卷调查   TODO 使用ttl删除,并处理相关事务逻辑
         /// </summary>
         /// <param name="request"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [HttpPost("find-record")]
-        public async Task<IActionResult> FindRecord(JsonElement requert)
+        [HttpPost("delete")]
+        [AuthToken(Roles = "admin,teacher")]
+        public async Task<IActionResult> Delete(JsonElement request)
         {
-            var client = _azureCosmos.GetCosmosClient();
-            if (!requert.TryGetProperty("id", out JsonElement id)) return BadRequest();
-            //var (id, name, picture, _) = HttpContext.GetAuthTokenInfo();
-
-            List<object> surveys = new List<object>();
-            var query = $"select c.id,c.code,c.name,c.type,c.status,c.startTime from c where {id}";
-            await foreach (var item in client.GetContainer("TEAMModelOS", "Student").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"SurveyRecord-{id}") }))
+            try
             {
-                using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                var (userid, _, _, school) = HttpContext.GetAuthTokenInfo();
+                if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
+                if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
+                var client = _azureCosmos.GetCosmosClient();
 
-                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                Survey survey = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Survey>(id.GetString(), new PartitionKey($"{code}"));
+                bool flag = false;
+                //必须是本人或者这个学校的管理者才能删除
+                if (survey.creatorId == userid)
                 {
-                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                    flag = true;
+                }
+                else
+                {
+                    if (survey.scope == "school" && survey.owner.Equals(school))
                     {
-                        surveys.Add(obj.ToObject<object>());
+                        flag = true;
                     }
                 }
+                if (flag)
+                {
+                    //使用ttl删除,并处理相关事务逻辑
+                    survey.ttl = 1;
+                    survey = await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(survey, survey.id, new PartitionKey($"{survey.code}"));
+                    return Ok(new { flag });
+                }
+                else
+                {
+                    return Ok(new { flag });
+                }
             }
-
-            return Ok(new { surveys, surveys.Count });
-            /*ResponseBuilder builder = ResponseBuilder.custom();
-            Dictionary<string, object> dict = new Dictionary<string, object>();
-            var emobj = request.EnumerateObject();
-            while (emobj.MoveNext())
-            {
-                dict[emobj.Current.Name] = emobj.Current.Value;
-            }
-            List<SurveyRecord> data = new List<SurveyRecord>();
-
-            if (dict.Keys.Count > 0)
-            {
-                data = await _azureCosmos.FindByDict<SurveyRecord>(dict);
-            }
-            else
+            catch (Exception e)
             {
-                return builder.Error(ResponseCode.PARAMS_ERROR, "参数异常!").build();
+                return BadRequest(e.StackTrace);
             }
-            return builder.Data(data).Extend(new Dictionary<string, object> { { "count", data.Count } }).build();*/
-        }        
-         
+        }
     }
 }

+ 38 - 22
TEAMModelOS/Controllers/Common/VoteController.cs

@@ -61,20 +61,28 @@ namespace TEAMModelOS.Controllers.Learn
         /// <param name="request"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [HttpPost("save")]
-        public async Task<IActionResult> Save(Vote request)
+        [HttpPost("upsert")]
+        public async Task<IActionResult> Upsert(Vote request)
         {
             try
             {
                 //新增Vote
                 var client = _azureCosmos.GetCosmosClient();
                 request.code = request.pk + "-" + request.code;
-                request.createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
-
+                
+                long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+                request.createTime = now;
                 if (string.IsNullOrEmpty(request.id))
                 {
                     request.id = Guid.NewGuid().ToString();
-                    request.progress = "pending";
+                    if (request.startTime < now)
+                    {
+                        request.progress = "pending";
+                    }
+                    else { 
+                        request.progress = "going"; 
+                    }
+                        
                     request = await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(request, new PartitionKey($"{request.code}"));
                 }
                 else
@@ -86,9 +94,8 @@ namespace TEAMModelOS.Controllers.Learn
                     }
                     request.progress = info.progress;
                     request = await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(request, info.id, new PartitionKey($"{info.code}"));
-                   
                 }
-                return Ok(new { request });
+                return Ok(new { vote = request });
             }
             catch (Exception e)
             {
@@ -105,6 +112,7 @@ namespace TEAMModelOS.Controllers.Learn
         ///时间筛选范围结束时间 默认当前时间   ?"etime":1608274766666 
         ///每页大小     ?"count":10/null/Undefined  
         ///分页Token    ?"continuationToken":Undefined/null/"[{\"token\":\"+RID:~omxMAP3ipcSEEwAAAAAAAA==#RT:2#TRC:20#ISV:2#IEO:65551#QCF:1#FPC:AYQTAAAAAAAAiRMAAAAAAAA=\",\"range\":{\"min\":\"\",\"max\":\"FF\"}}]"
+        /// 当前状态    ?"progress":Undefined/null/"" 表示两种状态都要查询/ "going"/"finish" 表示查询进行中/ 或者已完成 学生端只能查询正在进行或已经结束 going 已发布|finish 已结束  
         /// </data>
         /// <param name="request"></param>
         /// <returns></returns>
@@ -118,39 +126,47 @@ namespace TEAMModelOS.Controllers.Learn
                 if (!requert.TryGetProperty("code", out JsonElement code)) return BadRequest();
                 //开始时间,默认最近三十天
                 var stimestamp = DateTimeOffset.UtcNow.AddDays(-30).ToUnixTimeMilliseconds();
-                if (!requert.TryGetProperty("stime", out JsonElement stime)) {
-                    if (stime.TryGetInt64(out long data))
+                if (requert.TryGetProperty("stime", out JsonElement stime)) {
+                    if (!stime.ValueKind.Equals(JsonValueKind.Undefined)&&stime.TryGetInt64(out long data))
                     {
                         stimestamp = data;
                     };
                 };
                 //默认当前时间
                 var etimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
-                if (!requert.TryGetProperty("etime", out JsonElement etime))
+                if (requert.TryGetProperty("etime", out JsonElement etime))
                 {
-                    if (stime.TryGetInt64(out long data))
+                    if (!etime.ValueKind.Equals(JsonValueKind.Undefined)&&etime.TryGetInt64(out long data))
                     {
                         etimestamp = data;
                     };
 
                 };
-                
+                var progresssql = "";
+                if (!requert.TryGetProperty("progress", out JsonElement progress))
+                {
+
+                    if (!progress.ValueKind.Equals(JsonValueKind.Undefined) && !progress.ValueKind.Equals(JsonValueKind.Null) && progress.ValueKind.Equals(JsonValueKind.String))
+                    {
+                        progresssql = $" and c.progress='{progresssql}' ";
+                    }
+                }
                 string continuationToken = null;
                 //默认不指定返回大小
                 int? topcout=null;
-                if (!requert.TryGetProperty("count", out JsonElement jcount)) {
-                    if(jcount.TryGetInt32(out int data))
+                if (requert.TryGetProperty("count", out JsonElement jcount)) {
+                    if(!jcount.ValueKind.Equals(JsonValueKind.Undefined) && jcount.TryGetInt32(out int data))
                     {
                         topcout = data;
-                    };
+                    }
                 };
                 //是否需要进行分页查询,默认不分页
                 bool iscontinuation = false;
                 //如果指定了返回大小
-                if (!requert.TryGetProperty("continuationToken", out JsonElement continuation))
+                if (requert.TryGetProperty("continuationToken", out JsonElement continuation))
                 {
                     //指定了cancellationToken 表示需要进行分页
-                    if (!continuation.ValueKind.Equals(JsonValueKind.Null) && !continuation.ValueKind.Equals(JsonValueKind.String))
+                    if (!continuation.ValueKind.Equals(JsonValueKind.Null) && !continuation.ValueKind.Equals(JsonValueKind.Undefined))
                     {
                         continuationToken = continuation.GetString();
                         iscontinuation = true;
@@ -159,7 +175,7 @@ namespace TEAMModelOS.Controllers.Learn
                 List<object> votes = 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} ";
+                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}") }))
                 {
@@ -181,7 +197,7 @@ namespace TEAMModelOS.Controllers.Learn
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"OS,{_option.Location},common/vote/find()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
+                await _dingDing.SendBotMsg($"OS,{_option.Location},common/vote/find()\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
                 return BadRequest(ex.StackTrace);
             }
         }
@@ -208,8 +224,8 @@ namespace TEAMModelOS.Controllers.Learn
                 Vote vote = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Vote>(id.GetString(), new PartitionKey($"{code}"));
                 if (vote != null)
                 {
-                   
-                    return Ok(vote);
+                    
+                    return Ok(new { vote });
                 }
                 else
                 {
@@ -293,7 +309,7 @@ namespace TEAMModelOS.Controllers.Learn
         public async Task<IActionResult> UpsertRecord(JsonElement request)
         {
             var (userid, _, _, _) = HttpContext.GetAuthTokenInfo();
-            int msgid = await ActivityService.Decide(request, _azureCosmos, _azureRedis, userid);
+            int msgid = await ActivityStudentService.Decide(request, _azureCosmos, _azureRedis, userid);
             return Ok(new { msgid });
         }
 

+ 44 - 6
TEAMModelOS/Controllers/Core/CoreController.cs

@@ -1,6 +1,7 @@
 using Azure.Storage.Blobs.Models;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Options;
 using System;
 using System.Collections.Generic;
 using System.Drawing.Imaging;
@@ -9,6 +10,7 @@ using System.Linq;
 using System.Text.Json;
 using System.Text.RegularExpressions;
 using System.Threading.Tasks;
+using TEAMModelOS.Models;
 using TEAMModelOS.Models.Request;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
@@ -21,10 +23,14 @@ namespace TEAMModelOS.Controllers.Core
     public class CoreController : ControllerBase
     {
         private readonly AzureStorageFactory _azureStorage;
+        private readonly DingDing _dingDing;
+        private readonly Option _option;
 
-        public CoreController(AzureStorageFactory azureStorage)
+        public CoreController(AzureStorageFactory azureStorage , DingDing dingDing, IOptionsSnapshot<Option> option)
         {
             _azureStorage = azureStorage;
+            _dingDing = dingDing;
+            _option = option?.Value;
         }
 
         /// <summary>
@@ -32,6 +38,38 @@ namespace TEAMModelOS.Controllers.Core
         /// </summary>
         /// <param name="request"></param>
         /// <returns></returns>
+        [HttpPost("convert-emf")]
+        [RequestSizeLimit(100_000_000)] //最大100m左右
+        public async Task<IActionResult> ConvertEmf(ImageQuangRequest request)
+        {
+            try
+            {
+                if (string.IsNullOrWhiteSpace(request.base64)) return BadRequest();
+                byte[] obase64data = Convert.FromBase64String(request.base64);
+                using var obase64ms = new MemoryStream(obase64data);
+                var (isimg, type, dupe) = Utils.ImageValidateByStream(obase64ms);
+                if (isimg && type.Equals("emf"))
+                {
+                    var pngimagebase64 = Utils.ConvertEMFtoPNG(obase64ms);
+                    return Ok(pngimagebase64);
+                }
+                else
+                {
+                    return BadRequest("非EMF圖片格式");
+                }
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"Core,{_option.Location},convert-emf()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
+                return BadRequest();
+            }
+        }
+
+        /// <summary>
+        /// PNG JPF 圖片輕量化
+        /// </summary>
+        /// <param name="request">支持html base64標籤解析</param>
+        /// <returns></returns>
         [HttpPost("image-quant")]
         [RequestSizeLimit(100_000_000)] //最大100m左右
         public async Task<IActionResult> ImageQuang(List<ImageQuangRequest> request)
@@ -44,17 +82,17 @@ namespace TEAMModelOS.Controllers.Core
             foreach (var item in request)
             {
                 //正則處理,移除空白
-                string trim = Regex.Replace(item.base64, @"\s", ""); 
+                string trim = Regex.Replace(item.base64, @"\s", "");
                 Match match = rex.Match(trim);
                 string otype = match.Groups["key1"].Value;
                 string obase64 = match.Groups["key2"].Value;
                 byte[] obase64data = Convert.FromBase64String(obase64);
                 using var obase64ms = new MemoryStream(obase64data);
                 //驗證圖片格式及位深,判斷是否要處理量化演算
-                var (isimg, type, dupe) = Utils.ImageValidateByStream(obase64ms);                
+                var (isimg, type, dupe) = Utils.ImageValidateByStream(obase64ms);
                 if (isimg && (type.Equals("png") || type.Equals("jpg")) && dupe > 8)
                 {
-                    string img = string.Empty;                    
+                    string img = string.Empty;
                     using var quantized = quantizer.QuantizeImage(obase64ms, item.width, item.height); //JPEG,PNG輕量化
                     if (quantized != null)
                     {
@@ -66,12 +104,12 @@ namespace TEAMModelOS.Controllers.Core
                             string blobpath = $"{item.blob[(item.blob.Trim('/').IndexOf("/") + 1)..]}/{img}"; //處理路徑,避免多餘的字符
                             await _azureStorage.GetBlobContainerClient(Container).GetBlobClient(blobpath).UploadAsync(obase64ms, new BlobHttpHeaders { ContentType = otype });
                         }
-                        using var nbase64ms = new MemoryStream(); 
+                        using var nbase64ms = new MemoryStream();
                         quantized.Save(nbase64ms, ImageFormat.Png); //保存為PNG格式
                         byte[] data = nbase64ms.ToArray();
                         string base64 = $"data:image/png;base64,{Convert.ToBase64String(data)}"; //創建新的img src base64
                         respons.Add(new { base64 = base64, blob = img });
-                        
+
                         // TODO 下面註解,無法解決保存時,真正改變Format32bppArgb為Format8bppIndexed,需要再研究
                         //ImageCodecInfo imageCodecInfo = ImageCodecInfo.GetImageEncoders().FirstOrDefault(info => info.MimeType == "image/png");
                         //var parameters = new EncoderParameters(1);

+ 4 - 4
TEAMModelOS/Controllers/Core/ImportController.cs

@@ -264,8 +264,8 @@ namespace TEAMModelOS.Controllers
                 return BadRequest(new Dictionary<string, object> { { "msg", "type is not docx!" }, { "code", ResponseCode.FAILED } });
             }
             var doc = _DOXC2HTMLTranslator.Translate(file.OpenReadStream());
-            var exercises = _HTML2ITEMV3Translator.Translate(doc);
-            return Ok(exercises);
+             (List<HTEXLib.DOCX.Models.ItemInfo> tests, List<string> error) = _HTML2ITEMV3Translator.Translate(doc);
+            return Ok(new { tests, emferror= error });
         }
         /// <summary>
         /// htmlString AnalyzeHtml
@@ -276,8 +276,8 @@ namespace TEAMModelOS.Controllers
         public IActionResult AnalyzeHtml(JsonElement request)
         {
             if (!request.TryGetProperty("htmlString", out JsonElement htmlString)) { return BadRequest(); }
-            var exercises = _HTML2ITEMV3Translator.Translate(htmlString.GetString());
-            return Ok(exercises);
+            (List<HTEXLib.DOCX.Models.ItemInfo> tests, List<string> error) = _HTML2ITEMV3Translator.Translate(htmlString.GetString());
+            return Ok(new { tests, emferror= error });
         }
 
 

+ 73 - 1
TEAMModelOS/Controllers/Knowledge/KnowledgesController.cs

@@ -30,12 +30,14 @@ namespace TEAMModelOS.Controllers
         private readonly AzureCosmosFactory _azureCosmos;
         private readonly DingDing _dingDing;
         private readonly Option _option;
-        public KnowledgesController(AzureCosmosFactory azureCosmos, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option)
+        private readonly AzureRedisFactory _azureRedis;
+        public KnowledgesController(AzureCosmosFactory azureCosmos, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option, AzureRedisFactory azureRedis)
         {
             _azureCosmos = azureCosmos;
             _snowflakeId = snowflakeId;
             _dingDing = dingDing;
             _option = option?.Value;
+            _azureRedis = azureRedis;
         }
         /**
          * 
@@ -94,8 +96,78 @@ namespace TEAMModelOS.Controllers
                 knowledge.id = Guid.NewGuid().ToString();
                 knowledge = await client.GetContainer("TEAMModelOS", "School").CreateItemAsync(knowledge, new PartitionKey($"{knowledge.code}"));
             }
+            var count= new { pcount = knowledge.points!=null ? knowledge.points.Count:0,bcount= knowledge.blocks!=null? knowledge.blocks.Count:0 };
+            //处理知识点,知识块计数问题
+            await _azureRedis.GetRedisClient(8).HashSetAsync($"Knowledge:Count:{knowledge.owner}-{knowledge.subjectId}", knowledge.periodId, count.ToJsonString());
             return Ok(knowledge);
         }
+        /// <summary>
+        /**
+        
+        {
+            !"hbcn-ac73f07d-2cc8-4174-85ae-b39cc5b6beef":"ca484aa8-e7b5-4a7c-8ef3-bd9e7b7d4fp2",
+        }
+       
+        单个Item查询一个学校某个科目知识点,知识块数量,如果需要确定某一个学段,则需要加学段。
+         **/
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("find-count")]
+        public async Task<IActionResult> FindCount(Dictionary<string,string> request) 
+        {
+            List<dynamic> datas = new List<dynamic>();
+            foreach (var kp in request) {
+                var countPoint = 0;
+                var countBlock = 0;
+                if (!string.IsNullOrWhiteSpace(kp.Value))
+                {
+                    var value = _azureRedis.GetRedisClient(8).HashGet($"Knowledge:Count:{kp.Key}", kp.Value);
+                    if (value != default && !value.IsNullOrEmpty)
+                    {
+                        
+                        JsonElement record = value.ToString().ToObject<JsonElement>();
+                        if (record.TryGetProperty("pcount", out JsonElement pcout))
+                        {
+                            int.TryParse($"{pcout}", out countPoint);
+                        }
+                        if (record.TryGetProperty("bcount", out JsonElement bcout))
+                        {
+                            int.TryParse($"{bcout}", out countBlock);
+                        }
+                    }
+                }
+                else {
+                    var values = _azureRedis.GetRedisClient(8).HashGetAll($"Knowledge:Count:{kp.Key}");
+                    if (values != null) {
+                        foreach (var value in values) 
+                        {
+                            JsonElement record = value.ToString().ToObject<JsonElement>();
+                            if (record.TryGetProperty("pcount", out JsonElement pcout))
+                            {
+                                if (int.TryParse($"{pcout}", out int countP)) 
+                                {
+                                    countPoint = countPoint + countP;
+                                }
+                            }
+                            if (record.TryGetProperty("bcount", out JsonElement bcout))
+                            {
+                                if(int.TryParse($"{bcout}", out int countB)) 
+                                {
+                                    countBlock = countBlock + countB;
+                                }
+                            }
+                        }
+                    }
+                }
+                datas.Add( new { key = kp.Key, countPoint, countBlock });
+            }
+            return Ok(new { datas });
+        }
+
+
+          
         /**
          * {
             ?"periodId": "ca484aa8-e7b5-4a7c-8ef3-bd9e7b7d4fp2",

+ 5 - 4
TEAMModelOS/Controllers/School/StudentCommonController.cs

@@ -40,15 +40,16 @@ namespace TEAMModelOS.Controllers
         ///是否展示列表的 Tips                 ? "tips":true/false
         ///每页大小     ?"count":10/null/Undefined  
         ///分页Token    ?"continuationToken":Undefined/null/"[{\"token\":\"+RID:~omxMAP3ipcSEEwAAAAAAAA==#RT:2#TRC:20#ISV:2#IEO:65551#QCF:1#FPC:AYQTAAAAAAAAiRMAAAAAAAA=\",\"range\":{\"min\":\"\",\"max\":\"FF\"}}]"
+        /// 当前状态    ?"progress":Undefined/null/"" 表示两种状态都要查询/ "going"/"finish" 表示查询进行中/ 或者已完成 学生端只能查询正在进行或已经结束 going 已发布|finish 已结束  
         /// </param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [HttpPost("find-activity")]
+        [HttpPost("stu-find-activity")]
         [AuthToken(Roles = "student")]
-        public async Task<IActionResult> FindTch(JsonElement request)
+        public async Task<IActionResult> FindStu(JsonElement request)
         {
-            var (id, _, _, _) = HttpContext.GetAuthTokenInfo();
-            (List<ActivityData> datas, string continuationToken) = await ActivityService.FindByRole("School", request, id, _azureCosmos,_azureRedis);
+            var (id, name, pic,school) = HttpContext.GetAuthTokenInfo();
+            (List<ActivityData> datas, string continuationToken) = await ActivityStudentService.FindAsStu("School", request, school, _azureCosmos,_azureRedis);
             return Ok(new { datas, continuationToken });
         }
         

+ 30 - 5
TEAMModelOS/Controllers/Teacher/TeacherCommonController.cs

@@ -31,7 +31,7 @@ namespace TEAMModelOS.Controllers
             _azureRedis = azureRedis;
         }
         /// <summary>
-        /// 查询活动所有活动类型的列表,教师
+        /// 学生端查询活动所有活动类型的列表,教师以学生身份登入学生端获取活动列表接口
         /// </summary>
         /// <param name="request">
         ///加入的班级信息                      ?classes:[{"classid":"S-C-00001","scope":"school"},{"classid":"P-C-00004","scope":"private"}]
@@ -41,17 +41,42 @@ namespace TEAMModelOS.Controllers
         ///是否展示列表的 Tips                 ? "tips":true/false
         ///每页大小     ?"count":10/null/Undefined  
         ///分页Token    ?"continuationToken":Undefined/null/"[{\"token\":\"+RID:~omxMAP3ipcSEEwAAAAAAAA==#RT:2#TRC:20#ISV:2#IEO:65551#QCF:1#FPC:AYQTAAAAAAAAiRMAAAAAAAA=\",\"range\":{\"min\":\"\",\"max\":\"FF\"}}]"
+        /// 当前状态    ?"progress":Undefined/null/"" 表示两种状态都要查询/ "going"/"finish" 表示查询进行中/ 或者已完成 学生端只能查询正在进行或已经结束 going 已发布|finish 已结束  
         /// </param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [HttpPost("find-activity")]
+        [HttpPost("stu-find-activity")]
         [AuthToken(Roles = "teacher")] 
-        public async Task<IActionResult> FindTch(JsonElement requert)
+        public async Task<IActionResult> FindStu(JsonElement requert)
         {
             var (id, _, _, _) = HttpContext.GetAuthTokenInfo();
-            (List<ActivityData> datas, string continuationToken) = await ActivityService.FindByRole("Teacher", requert, id, _azureCosmos,_azureRedis);
+            (List<ActivityData> datas, string continuationToken) = await ActivityStudentService.FindAsStu("Teacher", requert, id, _azureCosmos,_azureRedis);
+            return Ok(new { datas, continuationToken });
+        }
+        /// <summary>
+        /// 教师端,查询活动所有活动类型的列表,班主任,任课教师等
+        /// 执教班级
+        /// </summary>
+        /// <param name="request">
+        ///执教的班级信息                      !classes:[{"classid":"S-C-00001","scope":"school"},{"classid":"P-C-00004","scope":"private"}]   TODO  需要排查 对象和班级字符串id设计原因 {"classid":"S-C-00001","scope":"school"}
+        ///执教的科目                          ?subjects:["subjectid1","subjectid2"]
+        ///活动类型                            ?"type":"vote"/"exam"/"homework"/"learn"/"survey"" // vote投票 survey问卷 exam评测 learn学习活动 homework作业活动
+        ///时间筛选范围开始时间 默认30天之前   ?"stime":1608274766154  
+        ///时间筛选范围结束时间 默认当前时间   ?"etime":1608274766666 
+        ///是否展示列表的 Tips                 ? "tips":true/false
+        ///每页大小     ?"count":10/null/Undefined  
+        ///分页Token    ?"continuationToken":Undefined/null/"[{\"token\":\"+RID:~omxMAP3ipcSEEwAAAAAAAA==#RT:2#TRC:20#ISV:2#IEO:65551#QCF:1#FPC:AYQTAAAAAAAAiRMAAAAAAAA=\",\"range\":{\"min\":\"\",\"max\":\"FF\"}}]"
+        ///当前状态     ?"progress":Undefined/null/"" 表示两种状态都要查询/ "going"/"finish" 表示查询进行中/ 或者已完成 学生端只能查询正在进行或已经结束 going 已发布|finish 已结束  
+        /// </param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("tch-find-activity")]
+        [AuthToken(Roles = "teacher")]
+        public async Task<IActionResult> FindTch(JsonElement requert)
+        {
+            var (id, _, _, school) = HttpContext.GetAuthTokenInfo();
+            (List<ActivityData> datas, string continuationToken) = await ActivityTeacherService.FindAsTch(  requert, school, _azureCosmos, _azureRedis);
             return Ok(new { datas, continuationToken });
         }
-       
     }
 }

+ 6 - 3
TEAMModelOS/JsonFile/Core/LangConfigV3.json

@@ -11,6 +11,7 @@
           "compose": "综合题",
           "single": "单选题",
           "multiple": "多选题",
+          "sortmultiple": "有序多选题",
           "judge": "判断题",
           "complete": "填空题",
           "subjective": "问答题",
@@ -21,7 +22,7 @@
         "Analysis": "解析",
         "Score": "配分",
         "Point": "知识点",
-        "Filed": "记忆|理解|应用|分析|创造|评价",
+        "Filed": "记忆|理解|应用|分析|评价|创造",
         "Judge": "对|错",
         "Ended": "结束",
         "Level": "难度"
@@ -35,6 +36,7 @@
           "compose": "綜合題",
           "single": "選擇題",
           "multiple": "複選題",
+          "sortmultiple": "有序複選題",
           "judge": "是非題",
           "complete": "填充題",
           "subjective": "問答題",
@@ -45,7 +47,7 @@
         "Analysis": "解析",
         "Score": "配分",
         "Point": "知識點",
-        "Filed": "記憶|理解|應用|分析|創造|評鑒",
+        "Filed": "記憶|理解|應用|分析|評鑒|創造",
         "Judge": "對|錯",
         "Ended": "結束",
         "Level": "難度"
@@ -59,6 +61,7 @@
           "compose": "QuestionGroup",
           "single": "MultipleChoice",
           "multiple": "MultipleAnswers",
+          "sortmultiple": "SortMultipleAnswers",
           "judge": "TrueFalse",
           "complete": "Blank",
           "subjective": "Essay",
@@ -69,7 +72,7 @@
         "Analysis": "Explain",
         "Score": "Score",
         "Point": "Point",
-        "Filed": "Remember|Understand|Apply|Analyze|Create|Evaluate",
+        "Filed": "Remember|Understand|Apply|Analyze|Evaluate|Create",
         "Judge": "True|False",
         "Ended": "Ended",
         "Level": "Level"

+ 47 - 13
TEAMModelOS/Services/Common/ActivityService.cs

@@ -15,7 +15,7 @@ using StackExchange.Redis;
 
 namespace TEAMModelOS.Services.Common
 {
-    public static class ActivityService
+    public static class ActivityStudentService
     {
 
         /// <summary>
@@ -188,32 +188,47 @@ namespace TEAMModelOS.Services.Common
             return week;
         }
 
+
         /// <summary>
-        /// 
+        /// 学生端查询
         /// </summary>
         /// <param name="containerId">容器</param>
         /// <param name="requert"></param>
         /// <param name="id">登录者ID</param>
         /// <param name="_azureCosmos"></param>
         /// <returns></returns>
-        public static async Task<(List<ActivityData> datas, string continuationToken)> FindByRole(string containerId, JsonElement requert, string id, AzureCosmosFactory _azureCosmos,AzureRedisFactory _azureRedis)
+        public static async Task<(List<ActivityData> datas, string continuationToken)> FindAsStu(string containerId, JsonElement requert, string id, AzureCosmosFactory _azureCosmos,AzureRedisFactory _azureRedis)
         {
             //开始时间,默认最近三十天
             var stimestamp = DateTimeOffset.UtcNow.AddDays(-30).ToUnixTimeMilliseconds();
+           
+           
             if (!requert.TryGetProperty("stime", out JsonElement stime))
             {
                 if (stime.TryGetInt64(out long data))
                 {
                     stimestamp = data;
                 } 
-            } 
+            }
+          
+            var progresssql = "";
+            if (!requert.TryGetProperty("progress", out JsonElement progress))
+            {
+
+                if (!progress.ValueKind.Equals(JsonValueKind.Undefined) && !progress.ValueKind.Equals(JsonValueKind.Null) && progress.ValueKind.Equals(JsonValueKind.String))
+                {
+                    progresssql = $" and c.progress='{progresssql}' ";
+                }
+            }
+            var typesql = "";
             if (!requert.TryGetProperty("type", out JsonElement type))
             {
-                //if (stime.TryGetInt64(out long data))
-                //{
-                //    stimestamp = data;
-                //} 
-            } 
+
+                if (!type.ValueKind.Equals(JsonValueKind.Undefined) && !type.ValueKind.Equals(JsonValueKind.Null) && type.ValueKind.Equals(JsonValueKind.String))
+                {
+                    typesql = $" and c.type='{typesql}' ";
+                }
+            }
             //默认当前时间,  未开始的不能查询
             var etimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
             //if (!requert.TryGetProperty("etime", out JsonElement etime))
@@ -252,22 +267,40 @@ namespace TEAMModelOS.Services.Common
                 if (jclasses.ValueKind is JsonValueKind.Array ) {
                     classes = jclasses.ToObject<List<string>>();
                 }
-            } 
+            }
+            //班级
+           
+            string joinSqlSubjects = "";
+            string andSqlSubjects = "";
+            if (!requert.TryGetProperty("subjects", out JsonElement jsubjects))
+            {
+                if (jsubjects.ValueKind is JsonValueKind.Array)
+                {
+                    List<string> subjects  = jsubjects.ToObject<List<string>>();
+                    if (subjects.IsNotEmpty()) {
+                        joinSqlSubjects = " join A2 in c.subjects ";
+                        List<string> sqlList = new List<string>();
+                        subjects.ForEach(x => { sqlList.Add($" '{x}' "); });
+                        string sql = string.Join(" , ", sqlList);
+                        andSqlSubjects = $" and A2 in ('{sql}') ";
+                    }
+                }
+            }
             string query = null;
             if (classes.IsNotEmpty() )
             {
                 List<string> sqlList = new List<string>();
                 classes.ForEach( x => { sqlList.Add($" '{x}' "); });
                 string sql = string.Join(" , ", sqlList);
-                query = $" SELECT distinct  value c   FROM c   JOIN A0 IN c.classes join A1 in c.tmdids   where c.startTime >= {stimestamp} and c.startTime <= {etimestamp} and  c.pk='Activity'  and   (A0 in('{sql}') or A1 in ('{id}')) ";
+                query = $" SELECT distinct  value c   FROM c   JOIN A0 IN c.classes join A1 in c.tmdids {joinSqlSubjects}  where c.startTime >= {stimestamp} and c.startTime <= {etimestamp} and  c.pk='Activity' {typesql} {progresssql}  {andSqlSubjects}  and   (A0 in('{sql}') or A1 ='{id} ') ";
             }
             else {
-                query = $" SELECT distinct  value c   FROM c    join A1 in c.tmdids   where c.startTime >= {stimestamp} and c.startTime <= {etimestamp}  and c.pk='Activity'  and   A1 ='{id}' ";
+                query = $" SELECT distinct  value c   FROM c    join A1 in c.tmdids  {joinSqlSubjects}  where c.startTime >= {stimestamp} and c.startTime <= {etimestamp}  and c.pk='Activity' {typesql} {progresssql}  {andSqlSubjects}  and   A1 ='{id}' ";
             }
             
             List<ActivityData> datas = new List<ActivityData>();
             var client = _azureCosmos.GetCosmosClient();
-            await foreach (var item in client.GetContainer("TEAMModelOS", containerId).GetItemQueryStreamIterator(query, continuationToken: continuationToken, requestOptions: new QueryRequestOptions() { MaxItemCount = topcout, PartitionKey = new PartitionKey($"Common-{id}") }))
+            await foreach (var item in client.GetContainer("TEAMModelOS", containerId).GetItemQueryStreamIterator(query, continuationToken: continuationToken, requestOptions: new QueryRequestOptions() { MaxItemCount = topcout, PartitionKey = new PartitionKey($"Activity-{id}") }))
             {
                 using var json = await JsonDocument.ParseAsync(item.ContentStream);
                 if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
@@ -295,6 +328,7 @@ namespace TEAMModelOS.Services.Common
             if (tips)
             {
                 DoActivityTips activityTips;
+                //TODO 处理活动tips 的res
                 dynamic res = default;
                 foreach (var data in datas)
                 {

+ 237 - 0
TEAMModelOS/Services/Common/ActivityTeacherService.cs

@@ -0,0 +1,237 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.Models.Cosmos;
+using TEAMModelOS.SDK.Extension;
+using Azure.Cosmos;
+using TEAMModelOS.SDK.DI;
+using HTEXLib.COMM.Helpers;
+using System.Text;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK.Models.Cosmos.Common.Inner;
+using StackExchange.Redis;
+
+namespace TEAMModelOS.Services.Common
+{
+    public class ActivityTeacherService
+    { 
+        
+        /// <summary>
+        /// 活动委托
+        /// </summary>
+        /// <param name="data"></param>
+        /// <returns></returns>
+        delegate dynamic DoActivityTips(ActivityData data, AzureCosmosFactory _azureCosmos, string id, AzureRedisFactory _azureRedis);
+
+        /// <summary>
+        /// 教师端查询
+        /// </summary>
+        /// <param name="containerId">容器</param>
+        /// <param name="requert"></param>
+        /// <param name="id">登录者ID</param>
+        /// <param name="_azureCosmos"></param>
+        /// <returns></returns>
+        public static async Task<(List<ActivityData> datas, string continuationToken)> FindAsTch(JsonElement requert, string school, AzureCosmosFactory _azureCosmos, AzureRedisFactory _azureRedis)
+        {
+            //开始时间,默认最近三十天
+            var stimestamp = DateTimeOffset.UtcNow.AddDays(-30).ToUnixTimeMilliseconds();
+
+
+            if (!requert.TryGetProperty("stime", out JsonElement stime))
+            {
+                if (stime.TryGetInt64(out long data))
+                {
+                    stimestamp = data;
+                }
+            }
+
+            var progresssql = "";
+            if (!requert.TryGetProperty("progress", out JsonElement progress))
+            {
+
+                if (!progress.ValueKind.Equals(JsonValueKind.Undefined) && !progress.ValueKind.Equals(JsonValueKind.Null) && progress.ValueKind.Equals(JsonValueKind.String))
+                {
+                    progresssql = $" and c.progress='{progresssql}' ";
+                }
+            }
+            var typesql = "";
+            if (!requert.TryGetProperty("type", out JsonElement type))
+            {
+
+                if (!type.ValueKind.Equals(JsonValueKind.Undefined) && !type.ValueKind.Equals(JsonValueKind.Null) && type.ValueKind.Equals(JsonValueKind.String))
+                {
+                    typesql = $" and c.type='{typesql}' ";
+                }
+            }
+            //默认当前时间,  未开始的不能查询
+            var etimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+            //if (!requert.TryGetProperty("etime", out JsonElement etime))
+            //{
+            //    if (etime.TryGetInt64(out long data))
+            //    {
+            //        etimestamp = data;
+            //    };
+            //};
+            string continuationToken = null;
+            //默认不指定返回大小
+            int? topcout = null;
+            if (!requert.TryGetProperty("count", out JsonElement jcount))
+            {
+                if (jcount.TryGetInt32(out int data))
+                {
+                    topcout = data;
+                }
+            }
+            //是否需要进行分页查询,默认不分页
+            bool iscontinuation = false;
+            //如果指定了返回大小
+            if (!requert.TryGetProperty("continuationToken", out JsonElement continuation))
+            {
+                //指定了cancellationToken 表示需要进行分页
+                if (!continuation.ValueKind.Equals(JsonValueKind.Null) && continuation.ValueKind.Equals(JsonValueKind.String))
+                {
+                    continuationToken = continuation.GetString();
+                    iscontinuation = true;
+                }
+            }
+            //班级
+            List<string> classes = null;
+            if (!requert.TryGetProperty("classes", out JsonElement jclasses))
+            {
+                if (jclasses.ValueKind is JsonValueKind.Array)
+                {
+                    classes = jclasses.ToObject<List<string>>();
+                }
+            }
+            //班级
+            string joinSqlSubjects = "";
+            string andSqlSubjects = "";
+            if (!requert.TryGetProperty("subjects", out JsonElement jsubjects))
+            {
+                if (jsubjects.ValueKind is JsonValueKind.Array)
+                {
+                    List<string> subjects = jsubjects.ToObject<List<string>>();
+                    if (subjects.IsNotEmpty())
+                    {
+                        joinSqlSubjects = " join A2 in c.subjects ";
+                        List<string> sqlList = new List<string>();
+                        subjects.ForEach(x => { sqlList.Add($" '{x}' "); });
+                        string sql = string.Join(" , ", sqlList);
+                        andSqlSubjects = $" and A2 in ('{sql}') ";
+                    }
+                }
+            }
+            string query = null;
+            if (classes.IsNotEmpty())
+            {
+                List<string> sqlList = new List<string>();
+                classes.ForEach(x => { sqlList.Add($" '{x}' "); });
+                string sql = string.Join(" , ", sqlList);
+                query = $" SELECT distinct  value c   FROM c   JOIN A0 IN c.classes join A1 in c.tmdids {joinSqlSubjects}  where c.startTime >= {stimestamp} and c.startTime <= {etimestamp} and  c.pk='Activity' {typesql} {progresssql}  {andSqlSubjects}  and   (A0 in('{sql}') or A1 ='{school} ') ";
+            }
+            else
+            {
+                query = $" SELECT distinct  value c   FROM c    join A1 in c.tmdids  {joinSqlSubjects}  where c.startTime >= {stimestamp} and c.startTime <= {etimestamp}  and c.pk='Activity' {typesql} {progresssql}  {andSqlSubjects}  and   A1 ='{school}' ";
+            }
+
+            List<ActivityData> datas = new List<ActivityData>();
+            var client = _azureCosmos.GetCosmosClient();
+            //班主任 ,任课教师只需要查询两种校园活动 和班级活动 ,  不查询私人教室创建的活动。  
+            await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(query, continuationToken: continuationToken, requestOptions: new QueryRequestOptions() { MaxItemCount = topcout, PartitionKey = new PartitionKey($"Activity-{school}") }))
+            {
+                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())
+                    {
+                        datas.Add(obj.ToObject<ActivityData>());
+                    }
+                    //如果需要分页则跳出
+                    if (iscontinuation)
+                    {
+                        continuationToken = item.GetContinuationToken();
+                        break;
+                    }
+                }
+            }
+            bool tips = false;
+            if (!requert.TryGetProperty("tips", out JsonElement jtips))
+            {
+                if (!jtips.ValueKind.Equals(JsonValueKind.Null) && !jtips.ValueKind.Equals(JsonValueKind.True))
+                {
+                    tips = jtips.GetBoolean();
+                }
+            }
+            if (tips)
+            {
+                DoActivityTips activityTips;
+                dynamic res = default;
+                foreach (var data in datas)
+                {
+                    //处理参与度,
+                    switch (data.type)
+                    {
+                        //投票
+                        case "vote":
+                            activityTips = DoVoteTips;
+                            //msgid, //0不能投票,1可以投票,2不在时间范围内,3周期内的可投票数不足
+                            //voteCount 可用投票数
+                            res = activityTips(data, _azureCosmos, school, _azureRedis);
+                            break;
+                        //问卷
+                        case "survey":
+                            //msgid 0 已作答, 1未作答,2,未完成
+                            activityTips = DoSurveyTips;
+                            res = activityTips(data, _azureCosmos, school, _azureRedis);
+                            break;
+                        //评测
+                        case "exam":
+                            //msgid 0 已作答, 1未作答,2,未完成, 用时间控制 相关发布状态,并且展示相应的结果
+                            activityTips = DoExamTips;
+                            res = activityTips(data, _azureCosmos, school, _azureRedis);
+                            break;
+                        //学习活动
+                        case "learn":
+                            //msgid 0 已完成, 1未开始,2,未完成
+                            activityTips = DoLearnTips;
+                            res = activityTips(data, _azureCosmos, school, _azureRedis);
+                            break;
+                        //作业活动
+                        case "homework":
+                            //msgid 0 已作答, 1未作答,2,未完成,3已批改,且有错误,4已批改,已完成
+                            //index:0,1,5 错误题序
+                            activityTips = DoHomeworkTips;
+                            res = activityTips(data, _azureCosmos, school, _azureRedis);
+                            break;
+                        default: break;
+                    }
+                }
+            }
+            return (datas, continuationToken);
+        }
+
+        private async static Task<dynamic> DoVoteTips(ActivityData commonData, AzureCosmosFactory _azureCosmos, string userid, AzureRedisFactory _azureRedis)
+        {
+            return null;
+        }
+
+        private static dynamic DoHomeworkTips(ActivityData commonData, AzureCosmosFactory _azureCosmos, string id, AzureRedisFactory _azureRedis)
+        {
+            return null;
+        }
+        private static dynamic DoLearnTips(ActivityData commonData, AzureCosmosFactory _azureCosmos, string id, AzureRedisFactory _azureRedis)
+        {
+            return null;
+        }
+        private static dynamic DoExamTips(ActivityData commonData, AzureCosmosFactory _azureCosmos, string id, AzureRedisFactory _azureRedis)
+        {
+            return null;
+        }
+        private static dynamic DoSurveyTips(ActivityData commonData, AzureCosmosFactory _azureCosmos, string id, AzureRedisFactory _azureRedis)
+        {
+            return null;
+        }
+    }
+}

+ 4 - 1
TEAMModelOS/TEAMModelOS.csproj

@@ -7,14 +7,16 @@
     <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.1" />
+    <PackageReference Include="HTEXLib" Version="2.4.7" />
     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.6" />
       <PackageReference Include="VueCliMiddleware" Version="5.0.0" />
   </ItemGroup>  
   
   <ItemGroup>
     <Folder Include="JwtRsaFile\" />
+    <Folder Include="logfile\" />
     <Folder Include="Services\Evaluation\" />
+    <Folder Include="wwwroot\" />
   </ItemGroup>  
   <ItemGroup>
     <ProjectReference Include="..\TEAMModelOS.SDK\TEAMModelOS.SDK.csproj" />
@@ -24,6 +26,7 @@
     <!-- Typescript/Javascript Client Configuration -->
     <SpaRoot>ClientApp\</SpaRoot>
     <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
+    <UserSecretsId>078b5d89-7d90-4f6a-88fc-7d96025990a8</UserSecretsId>
   </PropertyGroup>
   <Target Name="DebugEnsureNodeEnv" BeforeTargets="Build">
     <!-- Build Target:  Ensure Node.js is installed -->