Explorar o código

Merge branch 'develop3.0' of http://106.12.23.251:10080/TEAMMODEL/TEAMModelOS into develop3.0

JAELYS %!s(int64=4) %!d(string=hai) anos
pai
achega
cb68a823e6
Modificáronse 53 ficheiros con 1358 adicións e 694 borrados
  1. 1 1
      TEAMModelFunction/MonitorCosmosDB.cs
  2. 20 9
      TEAMModelFunction/TriggerSurvey.cs
  3. 12 9
      TEAMModelFunction/TriggerVote.cs
  4. 16 0
      TEAMModelOS.SDK/Models/Cosmos/Common/Inner/SurveyRecord.cs
  5. 27 85
      TEAMModelOS.SDK/Models/Cosmos/Common/Survey.cs
  6. 8 3
      TEAMModelOS/ClientApp/src/api/http.js
  7. 1 1
      TEAMModelOS/ClientApp/src/api/questionnaire.js
  8. 3 3
      TEAMModelOS/ClientApp/src/common/UploadModal.vue
  9. 0 44
      TEAMModelOS/ClientApp/src/components/homework/BaseHwForm.vue
  10. 1 0
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseJudge.vue
  11. 1 0
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseMultiple.vue
  12. 29 71
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseQnForm.vue
  13. 15 0
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseQuestionnaire.less
  14. 40 5
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseQuestionnaire.vue
  15. 1 1
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseSingle.less
  16. 1 0
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseSingle.vue
  17. 102 0
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseSubjective.vue
  18. 0 3
      TEAMModelOS/ClientApp/src/components/syllabus/DragTree.vue
  19. 1 2
      TEAMModelOS/ClientApp/src/components/vote/BaseVoteBar.vue
  20. 7 6
      TEAMModelOS/ClientApp/src/components/vote/BaseVoteForm.vue
  21. 135 151
      TEAMModelOS/ClientApp/src/components/vote/BaseVotePie.vue
  22. 1 1
      TEAMModelOS/ClientApp/src/components/vote/BaseVoteSsTable.vue
  23. 1 1
      TEAMModelOS/ClientApp/src/components/vote/BaseVoteTable.vue
  24. 43 0
      TEAMModelOS/ClientApp/src/css/site.css
  25. 6 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/http.js
  26. 2 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/index.js
  27. 1 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/survey.js
  28. 3 3
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/evaluation.js
  29. 6 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/http.js
  30. 2 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/index.js
  31. 1 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/survey.js
  32. 6 0
      TEAMModelOS/ClientApp/src/router/routes.js
  33. 0 42
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseCreateChild.vue
  34. 0 41
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseEditExercise.vue
  35. 18 5
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseImport.vue
  36. 0 42
      TEAMModelOS/ClientApp/src/view/evaluation/index/CreateExercises.vue
  37. 0 2
      TEAMModelOS/ClientApp/src/view/evaluation/types/BaseCompletion.vue
  38. 0 11
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtPrivEva.vue
  39. 1 1
      TEAMModelOS/ClientApp/src/view/learnactivity/Scoring.vue
  40. 113 0
      TEAMModelOS/ClientApp/src/view/newcourse/EvDetail.less
  41. 131 0
      TEAMModelOS/ClientApp/src/view/newcourse/EvDetail.vue
  42. 17 0
      TEAMModelOS/ClientApp/src/view/newcourse/MyCourse.less
  43. 174 4
      TEAMModelOS/ClientApp/src/view/newcourse/MyCourse.vue
  44. 12 2
      TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.less
  45. 165 66
      TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.vue
  46. 1 1
      TEAMModelOS/ClientApp/src/view/schoolmgmt/ClassroomSetting/ClassroomSetting.vue
  47. 11 9
      TEAMModelOS/ClientApp/src/view/vote/ManageVote.vue
  48. 66 12
      TEAMModelOS/Controllers/Common/SurveyController.cs
  49. 29 11
      TEAMModelOS/Controllers/Common/VoteController.cs
  50. 0 16
      TEAMModelOS/Models/Dto/SurveyDto.cs
  51. 0 14
      TEAMModelOS/Models/Dto/VoteDto.cs
  52. 126 15
      TEAMModelOS/Services/Common/ActivityStudentService.cs
  53. 1 1
      TEAMModelOS/TEAMModelOS.csproj

+ 1 - 1
TEAMModelFunction/MonitorCosmosDB.cs

@@ -67,7 +67,7 @@ namespace TEAMModelFunction
                                 TriggerVote.Trigger(_azureCosmos, _serviceBus, _azureStorage, _dingDing, client, input, code, stime, etime, school, _azureRedis);
                                 break;
                             case "Survey":
-                                TriggerSurvey.Trigger(_azureCosmos, _serviceBus, _azureStorage, _dingDing, client, input, code, stime, etime, school);
+                                TriggerSurvey.Trigger(_azureCosmos, _serviceBus, _azureStorage, _dingDing, client, input, code, stime, etime, school, _azureRedis);
                                 break;
 
                         }

+ 20 - 9
TEAMModelFunction/TriggerSurvey.cs

@@ -8,6 +8,7 @@ using System.Text.Json;
 using System.Threading.Tasks;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK.Helper.Common.CollectionHelper;
 using TEAMModelOS.SDK.Models;
 using TEAMModelOS.SDK.Models.Cosmos;
 
@@ -16,7 +17,7 @@ namespace TEAMModelFunction
    public class TriggerSurvey
     {
         public static async void Trigger(AzureCosmosFactory _azureCosmos, AzureServiceBusFactory _serviceBus, AzureStorageFactory _azureStorage, DingDing _dingDing,
-               CosmosClient client, Document input, string code, long stime, long etime, string school)
+               CosmosClient client, Document input, string code, long stime, long etime, string school,AzureRedisFactory _azureRedis)
         {
             Survey survey = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Survey>(input.Id, new Azure.Cosmos.PartitionKey($"{code}"));
             List<ChangeRecord> changeRecords = await _azureStorage.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", input.Id }, { "PartitionKey", survey.progress } });
@@ -52,7 +53,7 @@ namespace TEAMModelFunction
                     break;
                 case "going":
                     ActivityData data;
-                    if (survey.scope == "school" || survey.scope == "teacher")
+                    if (survey.scope == "school")
                     {
                         data = new ActivityData
                         {
@@ -64,10 +65,11 @@ namespace TEAMModelFunction
                             endTime = survey.endTime,
                             scode = survey.code,
                             scope = survey.scope,
-                            classes = survey.classes,
-                            tmdids = survey.tmdids,
+                            classes = survey.classes.IsNotEmpty() ? survey.classes : new List<string> { "" },
+                            tmdids = survey.tmdids.IsNotEmpty() ? survey.tmdids : new List<string> { "" },
                             progress = "going",
-                            owner = survey.owner
+                            owner = survey.owner,
+                            subjects = new List<string> { "" }
                         };
                         await client.GetContainer("TEAMModelOS", "School").UpsertItemAsync<ActivityData>(data, new Azure.Cosmos.PartitionKey(data.code));
                     }
@@ -83,9 +85,11 @@ namespace TEAMModelFunction
                             endTime = survey.endTime,
                             scode = survey.code,
                             scope = survey.scope,
-                            classes = survey.classes,
                             progress = "going",
-                            owner = survey.owner
+                            classes = survey.classes.IsNotEmpty() ? survey.classes : new List<string> { "" },
+                            owner = survey.owner,
+                            tmdids = new List<string> { "" },
+                            subjects = new List<string> { "" }
                         };
                         await client.GetContainer("TEAMModelOS", "Teacher").UpsertItemAsync<ActivityData>(data, new Azure.Cosmos.PartitionKey(data.code));
                     }
@@ -101,7 +105,7 @@ namespace TEAMModelFunction
                     else
                     {
                         long end = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync("active-task", messageSurveyEnd, DateTimeOffset.FromUnixTimeMilliseconds(etime));
-                        ChangeRecord changeRecord = new ChangeRecord
+                        ChangeRecord changeRecord                                                                                                                                                                                                                                                   = new ChangeRecord
                         {
                             RowKey = input.Id,
                             PartitionKey = "going",
@@ -112,7 +116,14 @@ namespace TEAMModelFunction
                     }
                     break;
                 case "finish":
-
+                    var records =await  _azureRedis.GetRedisClient(8).HashGetAllAsync($"Survey:Record:{survey.id}_{survey.code}");
+                    List<dynamic> recs = new List<dynamic>();
+                    foreach (var rcd in records) {
+                        //var key =int.Parse(rcd.Name.ToString());
+                        var value = rcd.Value.ToString().ToObject<JsonElement>();
+                        recs.Add(new { index = rcd.Name.ToString(), count = value });
+                    }
+                    await  _azureStorage.UploadFileByContainer(survey.owner, recs.ToJsonString(), "vote", $"{survey.id}/record.json");
                     break;
             }
         }

+ 12 - 9
TEAMModelFunction/TriggerVote.cs

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

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

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

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

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

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

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

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

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

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

@@ -1,7 +1,7 @@
 <template>
     <Modal v-model="uploadStatus" :ok-text="textLoading ? $t('updModal.uploading') : isComplete ? $t('updModal.complete'):$t('updModal.comfirmUpd')" :cancel-text="$t('updModal.cancelUpd')" :title="$t('teachContent.btnUpload')" class="upload-modal dark-iview-modal" width="660" :mask-closable="false" :closable="false" @on-ok="modalOk" @on-cancel="modalCancel" :loading="modalLoading">
         <div class="upload-file-box">
-            <Upload type="drag" action="" :show-upload-list="false" multiple :before-upload="customUpload" class="upload-wrap" :disabled="textLoading" >
+            <Upload type="drag" action="" :show-upload-list="false" multiple :before-upload="customUpload" class="upload-wrap" :disabled="textLoading">
                 <Icon class="upload-icon" custom="iconfont icon-upload" v-show="!uploadedList.length" />
                 <p class="upload-text" :style="{marginTop: uploadedList.length ? '25px':'0px'}">
                     <Icon size="24" style="font-size: 22px;vertical-align: baseline;margin-right: 5px;" custom="iconfont icon-upload" v-show="uploadedList.length" />
@@ -294,7 +294,7 @@ export default {
                                 scope: this.routerScope
                             }).then(
                                 parseRes => {
-                                    this.containerClient.deleteBlob(delBlob)
+                                    this.containerClient.deleteBlob(delBlob, res.size)
                                     this.uploadedList[index].blob = `/res/${res.name}/index.json`
                                     this.uploadedList[index].extension = 'HTEX'
                                     this.uploadedList[index].name = res.name.replace('pptx', 'HTEX').replace('PPTX', 'HTEX')
@@ -349,7 +349,7 @@ export default {
                 this.textLoading = false
                 this.isComplete = true
                 this.modalLoading = false
-                
+
             }
         },
         //处理图片缩略图

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

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

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

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

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

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

+ 29 - 71
TEAMModelOS/ClientApp/src/components/questionnaire/BaseQnForm.vue

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

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

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

+ 40 - 5
TEAMModelOS/ClientApp/src/components/questionnaire/BaseQuestionnaire.vue

@@ -59,7 +59,7 @@
 		</div>
 
 		<!-- 新建题目弹窗 -->
-		<Modal v-model="addModal" width="880" footer-hide class="related-modal" @on-visible-change="onModalChange">
+		<Modal v-model="addModal" width="800" footer-hide class="related-modal survey-modal" @on-visible-change="onModalChange">
 			<div class="modal-header" slot="header">新增{{ typeList[curType] }}习题</div>
 			<template v-if="isUpdate">
 			<vuescroll>
@@ -67,6 +67,7 @@
 				<BaseSingle v-if="curType==='single'" ref="addItem" :editInfo="curItem" @onSave="onSave"></BaseSingle>
 				<BaseMultiple v-if="curType==='multiple'" ref="addItem" :editInfo="curItem" @onSave="onSave"></BaseMultiple>
 				<BaseJudge v-if="curType==='judge'" ref="addItem" :editInfo="curItem" @onSave="onSave"></BaseJudge>
+				<BaseSubjective v-if="curType==='subjective'" ref="addItem" :editInfo="curItem" @onSave="onSave"></BaseSubjective>
 				<Button class="modal-btn" @click="onConfirmAdd">确认</Button>
 			</vuescroll>
 			</template>
@@ -78,6 +79,7 @@
 	import BaseSingle from "./BaseSingle.vue"
 	import BaseMultiple from "./BaseMultiple.vue"
 	import BaseJudge from "./BaseJudge.vue"
+	import BaseSubjective from "./BaseSubjective.vue"
 	import BaseQnBar from "./BaseQnBar.vue"
 	
 	export default {
@@ -86,7 +88,8 @@
 			BaseQnBar,
 			BaseSingle,
 			BaseMultiple,
-			BaseJudge
+			BaseJudge,
+			BaseSubjective
 		},
 		props: ["editItem","isEdit"],
 		data() {
@@ -148,6 +151,7 @@
 				// 判断获取到的新题是否规范
 				if ( this.getSimpleText(addItem.stemContent) && this.checkOptionNull(addItem.optionsContent)){
 					let newItem = {
+						id: this.editIndex === null && !this.curItem ? this.$tools.guid() : this.curItem.id,
 						question: addItem.stemContent,
 						option: addItem.optionsContent,
 						type: this.editIndex === null ? this.curType : this.curItem.type,
@@ -231,6 +235,32 @@
 					this.curIndexList.push(index)
 				}
 				
+			},
+			
+			// 获取blob里的试题数据
+			getBlobItems(qnItem){
+				return new Promise(async (resolve,reject) => {
+					let blobHost = qnItem.scope === 'private' ?  JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8")).blob_uri : JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri
+					// 根据试卷的Blob地址 去读取JSON文件
+					let sasString = qnItem.scope === 'private' ?  await this.$tools.getPrivateSas() : await this.$tools.getSchoolSas()
+					let promiseArr = []
+					for (let item of qnItem.questionUrl) {
+						promiseArr.push(new Promise(async (r,j) => {
+							try{
+								let itemJson = JSON.parse(await this.$tools.getFile(blobHost + item + sasString.sas))
+								r(itemJson)
+							}catch(e){
+								j(e)
+							}
+						}))
+					}
+					Promise.all(promiseArr).then(result => {
+						resolve(result)
+					}).catch(err => {
+						reject(err)
+					})
+				})
+				
 			}
 		},
 
@@ -250,13 +280,18 @@
 		},
 		watch: {
 			editItem: {
-				handler(newValue) {
+				async handler(newValue) {
 					/** 编辑回显 */
 					if (newValue) {
-						this.items = newValue.questions || []
+						console.log('问卷接受到的数据 ==',newValue);
+						if(newValue.questionUrl && newValue.questionUrl.length){
+							this.items = await this.getBlobItems(newValue)
+							console.log(this.items)
+						}else{
+							this.items = []
+						}
 						this.isShowAllAnalysis = false
 						this.curIndexList = []
-						console.log(newValue);
 					} else {
 						/** 新增 */
 					}

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

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

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

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

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

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

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

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

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

@@ -78,7 +78,7 @@
                     series: [{
                         type: 'bar',
                         data: data.map(item => item.value),
-                        barMaxWidth: 80, //柱子宽度
+                        barMaxWidth: 50, //柱子宽度
                         itemStyle: {
                             normal: {
                                 color: new that.$echarts.graphic.LinearGradient(0, 0, 0, 1, [{
@@ -116,7 +116,6 @@
         },
         watch: {
             pieData: {
-                deep: true,
                 handler(val) {
                     console.log(val)
                     let arr = []

+ 7 - 6
TEAMModelOS/ClientApp/src/components/vote/BaseVoteForm.vue

@@ -1,12 +1,13 @@
 <template>
 	<div class="component-vote-form">
-		<Form ref="voteForm" :model="voteForm" label-position="top" :rules="ruleValidate" :disabled="!voteFormEdit" hide-required-mark>
+		<Form ref="voteForm" :model="voteForm" label-position="top" :rules="ruleValidate" :disabled="!voteFormEdit"
+		 hide-required-mark>
 			<FormItem :label="$t('vote.form.name')" prop="name">
 				<Input :class="!voteFormEdit ? 'vote-form-disabled':''" v-model="voteForm.name" placeholder="请输入投票名称"></Input>
 			</FormItem>
 			<FormItem :label="$t('vote.form.target')" prop="classes">
 				<RadioGroup v-model="classType" @on-change="onClassTypeChange" v-if="voteFormEdit">
-					<Radio label="private">{{ $t('vote.form.privateClass') }}</Radio>
+					<Radio label="private" v-if="getCurScope === 'private'">{{ $t('vote.form.privateClass') }}</Radio>
 					<Radio label="school">{{ $t('vote.form.schoolClass') }}</Radio>
 				</RadioGroup>
 				<div v-if="!voteFormEdit && curVoteItem" class="vote-class">
@@ -210,7 +211,7 @@
 				if (arr[0] === '') {
 					this.voteForm.rangeTime = null
 					this.ruleValidate.rangeTime[0].message = '日期不能为空!'
-				}else if(this.getTimestampByString(arr[0]) < Date.now()){
+				} else if (this.getTimestampByString(arr[0]) < Date.now()) {
 					this.voteForm.rangeTime = null
 					this.ruleValidate.rangeTime[0].message = '开始时间不能早于当前时间!'
 				} else {
@@ -241,9 +242,9 @@
 						let target = []
 						let fileName = this.$tools.guid()
 						let isReset = this.voteForm.isReset.length > 0
-						params.code = this.getCurCode,
-							params.scope = this.getCurScope,
-							params.name = this.voteForm.name
+						params.code = this.getCurCode
+						params.scope = this.getCurScope
+						params.name = this.voteForm.name
 						params.startTime = this.voteForm.startTime
 						params.endTime = this.voteForm.endTime
 						params.description = this.voteForm.description

+ 135 - 151
TEAMModelOS/ClientApp/src/components/vote/BaseVotePie.vue

@@ -1,170 +1,154 @@
 <template>
-    <div :id="pieId" class="myPie"></div>
+	<div :id="pieId" class="myPie"></div>
 </template>
 
 <script>
-    export default {
-        name: 'BasePie',
-        props: ['pieId', 'pieData'],
-        data() {
-            return {
-                pieDatas: [],
-                noChooseNum: 0,
-                chooseNum: 0
-            }
-        },
-        methods: {
+	export default {
+		name: 'BasePie',
+		props: ['pieId', 'pieData'],
+		data() {
+			return {
+				pieDatas: [],
+				noChooseNum: 0,
+				chooseNum: 0
+			}
+		},
+		methods: {
 
-            drawLine(data) {
-                let that = this
-                // 基于准备好的dom,初始化echarts实例
-                let myPie = this.$echarts.init(document.getElementById(this.pieId), 'chalk')
+			drawLine(data) {
+				let that = this
+				// 基于准备好的dom,初始化echarts实例
+				let myPie = this.$echarts.init(document.getElementById(this.pieId), 'chalk')
 
-                // 指定图表的配置项和数据
-                var option = {
-                    legend: {
-                        y: 'bottom',
-                        textStyle: { color: '#c3cad9' },
-                    },
-                    title: {
-                        text: '选项分布饼图',
-                        left: 'center',
-                        top: 20,
-                        textStyle: {
-                            color: 'White',
-                            fontFamily: '微軟正黑體'
-                        }
-                    },
+				// 指定图表的配置项和数据
+				var option = {
+					legend: {
+						y: 'bottom',
+						textStyle: {
+							color: '#c3cad9'
+						},
+					},
+					title: {
+						text: '选项分布饼图',
+						left: 'center',
+						top: 20,
+						textStyle: {
+							color: 'White',
+							fontFamily: '微軟正黑體'
+						}
+					},
 
-                    tooltip: {
-                        trigger: 'item',
-                        formatter: "{b} : {c} ({d}%)"
-                    },
+					tooltip: {
+						trigger: 'item',
+						formatter: "{b} : {c} ({d}%)"
+					},
 
-                    visualMap: {
-                        show: false,
-                        min: 500,
-                        max: 600,
-                        inRange: {
-                            //colorLightness: [0, 1]
-                        }
-                    },
-                    series: [{
-                        name: '访问来源',
-                        type: 'pie',
-                        radius: '50%',
-                        selectedMode: 'single',
-                        selectedOffset: 10,
-                        clockwise: true,
-                        center: ['50%', '50%'],
-                        color: ['#43cadd', '#3893e5', '#FCC708', '#03B48E'], //'#FBFE27','rgb(11,228,96)','#FE5050'
-                        data: [{
-                            name: '未投票',
-                            value: this.noChooseNum
-                        }, {
-                            name: '已投票',
-                            value: this.chooseNum
-                        }],
-                        roseType: 'radius',
+					visualMap: {
+						show: false,
+						min: 500,
+						max: 600,
+						inRange: {
+							//colorLightness: [0, 1]
+						}
+					},
+					series: [{
+						type: 'pie',
+						radius: '50%',
+						selectedMode: 'single',
+						selectedOffset: 10,
+						clockwise: true,
+						center: ['50%', '50%'],
+						color: ['#567c94', '#11d8ff', '#FCC708', '#03B48E'], //'#FBFE27','rgb(11,228,96)','#FE5050'
+						data: [{
+							name: '未投票',
+							value: this.noChooseNum
+						}, {
+							name: '已投票',
+							value: this.chooseNum
+						}],
 
-                        label: {
-                            normal: {
-                                // formatter: ['{c|{b}{d}%}', '{b|{b}}'].join('\n'),
-                                // formatter: ['{b|{b}{d}%}', '{c|{c}万元}'].join('\n'),
-                                rich: {
-                                    b: {
-                                        color: '#d9efff',
-                                        fontSize: 15,
-                                        height: 40
-                                    },
-                                    c: {
-                                        color: '#fff',
-                                        fontSize: 14,
-                                        fontWeight: 'bold',
-                                        lineHeight: 5
-                                    },
-                                },
-                            }
-                        },
-                        itemStyle: {
-                            borderWidth: '5',
-                        },
-                        labelLine: {
-                            normal: {
-                                backgroundColor: 'yellow',
-                                borderColor: 'skyblue',
-                                borderWidth: 10,
-                                lineStyle: {
-                                    borderColor: 'skyblue',
-                                    borderWidth: 10,
-                                    backgroundColor: 'yellow',
-                                }
-                            },
+						label: {
+							normal: {
+								rich: {
+									b: {
+										color: '#d9efff',
+										fontSize: 15,
+										height: 40
+									},
+									c: {
+										color: '#fff',
+										fontSize: 14,
+										fontWeight: 'bold',
+										lineHeight: 5
+									},
+								},
+							}
+						},
+						itemStyle: {
+							borderWidth: '5',
+						},
+						labelLine: {
+							normal: {
+								backgroundColor: 'yellow',
+								borderColor: 'skyblue',
+								borderWidth: 10,
+								lineStyle: {
+									borderColor: 'skyblue',
+									borderWidth: 10,
+									backgroundColor: 'yellow',
+								}
+							},
+						}
+					}]
+				};
 
-
-
-                        }
-                    }]
-                };
-
-                // 绘制图表
-                myPie.setOption(option)
-				this.$nextTick(()=>{
+				// 绘制图表
+				myPie.setOption(option)
+				this.$nextTick(() => {
 					myPie.resize()
 				})
-                window.addEventListener('resize', function () {
-                    myPie.resize()
-                })
-            }
-        },
-        mounted() {
-
-            if (this.pieData.length) {
-                let val = this.pieData
-                this.noChooseNum = val.filter(item => item.key === '').length ? val.filter(item => item.key === '')[0].result.length : 0
-                let sum = 0
-                val.forEach(item => {
-                    sum += item.result.length
-                })
-                this.chooseNum = sum - this.noChooseNum
-                this.drawLine(val)
-            }
-        },
-        computed: {
-            // 获取最新知识点占比饼图数据
-            getPieData() {
-                return this.$store.state.totalAnalysis.knowledgeData
-            }
-        },
-        watch: {
-            pieData: {
-                deep: true,
-                handler(val) {
-                    if (val.length) {
-						console.log("11111111111111111")
-                        this.noChooseNum = val.filter(item => item.key === '').length ? val.filter(item => item.key === '')[0].result.length : 0
-                        let sum = 0
-                        val.forEach(item => {
-                            sum += item.result.length
-                        })
-                        this.chooseNum = sum - this.noChooseNum
-                        this.drawLine(val)
+				window.addEventListener('resize', function() {
+					myPie.resize()
+				})
+			}
+		},
+		mounted() {
 
-                    }
-                }
-            }
-        }
+			if (this.pieData.length) {
+				let val = this.pieData
+				this.noChooseNum = val.filter(item => item.option === null).length
+				this.chooseNum = val.length - this.noChooseNum
+				this.drawLine(val)
+			}
+		},
+		computed: {
+			// 获取最新知识点占比饼图数据
+			getPieData() {
+				return this.$store.state.totalAnalysis.knowledgeData
+			}
+		},
+		watch: {
+			pieData: {
+				deep: true,
+				handler(val) {
+					if (val.length) {
+						this.noChooseNum = val.filter(item => item.option === null).length
+						this.chooseNum = val.length - this.noChooseNum
+						this.drawLine(val)
+					}
+				}
+			}
+		}
 
-    }
+	}
 </script>
 
 <!-- Add "scoped" attribute to limit CSS to this component only -->
 <style scoped>
-
-    .myPie {
-        width: 50%;
-        height: 380px;
-        margin: 0 auto;
-        display: block;
-    }
+	.myPie {
+		width: 50%;
+		height: 380px;
+		margin: 0 auto;
+		display: block;
+	}
 </style>

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

@@ -36,7 +36,7 @@
                     {
                         title: '班级',
                         render: (h, params) => {
-                            return h('span', params.row.className)
+                            return h('span', params.row.classroomName)
                         },
                     },
                     {

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

@@ -68,7 +68,7 @@
                         }
                     },
                     {
-                        title: '数',
+                        title: '数',
                         render: (h, params) => {
                             return h('span', params.row.result.length)
                         },

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

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

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

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

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

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

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

@@ -13,6 +13,7 @@ export default {
 	single:'单选题',
 	multiple:'多选题',
 	judge:'判断题',
+	subjective:'问答题',
 	defaultName:'预设问卷名称',
 	isExistTip:'已存在未保存的问卷活动!',
 	getDataFailTip:'获取数据失败!',

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

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

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

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

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

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

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

@@ -13,6 +13,7 @@ export default {
 	single: '單選題',
 	multiple: '复选题',
 	judge: '判斷題',
+	subjective:'問答題',
 	defaultName: '預設問卷名稱',
 	isExistTip: '已存在未保存的問卷活動!',
 	getDataFailTip: '獲取數據失敗!',

+ 6 - 0
TEAMModelOS/ClientApp/src/router/routes.js

@@ -267,6 +267,12 @@ export const routes = [
 				name: 'myCourse',
 				component: resolve => require(['@/view/newcourse/MyCourse.vue'], resolve)
 			},
+			//我的课程查看评测详细数据
+			{
+				path: 'EvDetail',
+				name: 'EvDetail',
+				component: resolve => require(['@/view/newcourse/EvDetail.vue'], resolve)
+			},
 			{
 				path: 'CourseTable',
 				name: 'CourseTable',

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

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

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

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

+ 18 - 5
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseImport.vue

@@ -15,11 +15,11 @@
                     </div>
                 </Upload>
 				<div class="import-tips">
-					<div style="display: flex;margin-bottom: 20px;">
+					<div style="display: flex;margin-bottom: 20px;justify-content: space-around;">
 						<Dropdown @on-click="onTemplateSelect">
 						        <Button type="primary">
 									<Icon type="md-cloud-download"  style="margin-right: 5px;"/>
-						            {{$t('evaluation.importFile.templateDownload')}}
+						            Word {{$t('evaluation.importFile.templateDownload')}}
 						            <Icon type="ios-arrow-down"></Icon>
 						        </Button>
 						        <DropdownMenu slot="list">
@@ -27,8 +27,18 @@
 						            <DropdownItem :name="1">中文繁体(zh-TW)</DropdownItem>
 						            <DropdownItem :name="2">英文(en-US)</DropdownItem>
 						        </DropdownMenu>
-						    </Dropdown>
-						<Button type="primary" icon="md-cloud-download" style="margin-left: 20px;" @click="onDownloadDetails">{{$t('evaluation.importFile.downloadDetails')}}</Button>	
+						</Dropdown>
+						<Dropdown @on-click="onTemplateSelect" style="margin-left: 15px;">
+						        <Button type="primary">
+									<Icon type="md-cloud-download"  style="margin-right: 5px;"/>
+						            Excel {{$t('evaluation.importFile.templateDownload')}}
+						            <Icon type="ios-arrow-down"></Icon>
+						        </Button>
+						        <DropdownMenu slot="list">
+						            <DropdownItem :name="3">中文简体(zh-CN)</DropdownItem>
+						        </DropdownMenu>
+						</Dropdown>
+						<Button type="primary" icon="md-cloud-download" style="margin-left: 15px;" @click="onDownloadDetails">{{$t('evaluation.importFile.downloadDetails')}}</Button>	
 					</div>
 					
 					<p style="font-size:18px;font-weight:bold;color: #fff;">{{$t('evaluation.importFile.importTips')}}</p>
@@ -77,7 +87,10 @@
 					fileName:'題目範本(繁体).docx'
 				},{
 					url:'/download/%E9%A2%98%E7%9B%AE%E6%A8%A1%E6%9D%BF%E8%8B%B1%E8%AF%AD.docx',
-					fileName:'TitleTemplate.docx'
+					fileName:'題目範本(英语).docx'
+				},{
+					url:'/download/%E9%A2%98%E7%9B%AE%E6%A8%A1%E6%9D%BF.xls',
+					fileName:'題目範本.xls'
 				}],
 				hostName:'',
 				fieldArr:['記憶记忆1','理解2','應用应用3','分析4','评价評鑒5','创造創造6']

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

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

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

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

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

@@ -64,15 +64,6 @@
                     <span :class="curBarIndex == 1 ? 'evalustion-bar-item line-bottom-active line-bottom':'evalustion-bar-item line-bottom'" @click="selectBar(1)">
                         {{$t('learnActivity.mgtScEv.tab2')}}
                     </span>
-                    <!-- <div style="float:right;" v-show="evaListShow[curEvaIndex] && evaListShow[curEvaIndex].progress == 'going'"> -->
-                    <!-- <div style="float:right;">
-                        <Tooltip :content="$t('learnActivity.mgtScEv.autoTips1')" :max-width="240">
-                            <Button type="success" size="small" :loading="answerLoading" class="mock-stu-answer" @click="mockAnswer">{{$t('learnActivity.mgtScEv.autoAnswer')}}</Button>
-                        </Tooltip>
-                        <Tooltip :content="$t('learnActivity.mgtScEv.autoTips2')" :max-width="240">
-                            <Button type="warning" size="small" :loading="scoreLoading" class="mock-tea-scoring" @click="mockScoring">{{$t('learnActivity.mgtScEv.autoScore')}}</Button>
-                        </Tooltip>
-                    </div> -->
                 </div>
                 <!--试卷信息-->
                 <div :class="curBarIndex == 1 ? 'animated fadeIn evaluation-base-info':'evaluation-base-info animated fadeOutRight'" v-show="curBarIndex == 1">
@@ -92,7 +83,6 @@
                 <div :class="curBarIndex == 0 ? 'animated fadeIn evaluation-base-info':'evaluation-base-info animated fadeOutRight'" v-show="curBarIndex == 0">
                     <Scoring :examInfo="examDetaiInfo" ref="score-box"></Scoring>
                 </div>
-
             </div>
         </Split>
     </div>
@@ -332,7 +322,6 @@ export default {
             var D = date.getDate() + ' '
             return Y + M + D;
         },
-
         //查询评测列表
         findEvaluation() {
             let requestData = {

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

@@ -258,7 +258,7 @@ export default {
             this.tableLoading = true
             if (!this.chooseClass) return;
             let requestData = {
-                id: this.chooseClass,
+                ids: [this.chooseClass],
                 scope: this.examInfo.scope == 'private' ? 'private' : 'school',
                 school_code: this.examInfo.scope == 'private' ? this.$store.state.userInfo.TEAMModelId : this.$store.state.userInfo.schoolCode,
             };

+ 113 - 0
TEAMModelOS/ClientApp/src/view/newcourse/EvDetail.less

@@ -0,0 +1,113 @@
+@first-bgColor: #141414;
+@second-bgColor: #1b1b1b;
+@third-bgColor: #222222;
+@borderColor: #424242;
+@primary-textColor: #fff; //文本主颜色
+@second-textColor: #a5a5a5; //文本副级颜色
+@primary-fontSize: 14px;
+@second-fontSize: 16px;
+.evaluation-detail-wrap {
+    /*width: ~"calc(100% - 400px)";*/
+    width:100%;
+    height: 100%;
+    padding-left: 15px;
+}
+.evaluation-detail-wrap {
+
+    .evaluation-detail-bar {
+        width: 100%;
+        height: 45px;
+        line-height: 45px;
+        border-bottom: 1px solid @borderColor;
+        color: @second-textColor;
+
+        .edit-evaluation {
+            float: right;
+            margin-right: 45px;
+            display: inline-block;
+            cursor: pointer;
+            color: white;
+        }
+
+        .edit-evaluation:hover {
+            color: aqua;
+        }
+
+        .evalustion-bar-item {
+            margin-right: 30px;
+            display: inline-block;
+            cursor: pointer;
+            line-height: 38px;
+        }
+
+        .evalustion-bar-item-active {
+            color: white;
+            border-bottom: 2px solid white;
+        }
+    }
+
+    .evaluation-base-info {
+        width: 100%;
+        height: ~"calc(100% - 45px)";
+
+        .evalustion-base-attr {
+            width: 350px;
+            height: 100%;
+            border-right: 1px solid @borderColor;
+
+            .evalustion-base-attr-header {
+                height: 45px;
+                line-height: 45px;
+                color: @second-textColor;
+                border-bottom: 1px solid @borderColor;
+                margin-bottom: 25px;
+            }
+
+            .evaluation-attr-form {
+                margin-right: 15px;
+                /*color: white;*/
+            }
+        }
+
+        .evaluation-test-paper-header {
+            height: 45px;
+            line-height: 45px;
+            color: @second-textColor;
+            border-bottom: 1px solid @borderColor;
+            margin-bottom:15px;
+        }
+    }
+}
+.test-paper-detail {
+    width: 100%;
+    height: 100%;
+    color: white;
+}
+.test-paper-detail .back-to-top {
+    position: fixed;
+    right: 50px;
+    bottom: 30px;
+    height: 48px;
+    width: 50px;
+    background: #595959;
+    z-index: 99999;
+    cursor: pointer;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+}
+
+.test-paper-detail .back-to-top:hover {
+    background: rgb(128,128,128);
+}
+
+.test-paper-detail .back-to-top .ivu-icon {
+    font-size: 26px;
+    color: white;
+}
+.back-to-cus{
+    float: right;
+    margin-right: 10px;
+    margin-top: 10px;
+}

+ 131 - 0
TEAMModelOS/ClientApp/src/view/newcourse/EvDetail.vue

@@ -0,0 +1,131 @@
+<template>
+    <div class="evaluation-detail-wrap">
+        <!--顶部菜单-->
+        <div class="evaluation-detail-bar">
+            <span :class="curBarIndex == 0 ? 'evalustion-bar-item line-bottom-active line-bottom':'evalustion-bar-item line-bottom'" @click="curBarIndex = 0">
+                {{$t('learnActivity.mgtScEv.tab1')}}
+            </span>
+            <span :class="curBarIndex == 1 ? 'evalustion-bar-item line-bottom-active line-bottom':'evalustion-bar-item line-bottom'" @click="curBarIndex = 1">
+                {{$t('learnActivity.mgtScEv.tab2')}}
+            </span>
+            <Button type="info" size="small" @click="goBack" class="back-to-cus">
+                <Icon custom="iconfont icon-arrow" size="12" style="vertical-align: baseline;" />
+                返回
+            </Button>
+        </div>
+        <!--试卷信息-->
+        <div :class="curBarIndex == 1 ? 'animated fadeIn evaluation-base-info':'evaluation-base-info animated fadeOutRight'" v-show="curBarIndex == 1">
+            <div class="test-paper-detail" style="margin-top:5px;">
+                <vuescroll ref="test-paper-detail" @handle-scroll="checkBackTop">
+                    <!--试卷题目信息-->
+                    <TestPaper v-if="examInfo && examInfo.papers" :paper="examInfo.papers[curSubIndex]" style="color:#515a6e;margin-top:-30px;" :isShowTools="false" isExamPaper></TestPaper>
+                    <EmptyData v-else style="margin-top:60px;"></EmptyData>
+                    <!--返回顶部-->
+                    <div class="back-to-top fl-col-center" :title="$t('learnActivity.mgtScEv.returnTop')" v-if="showBack" @click="handleBackToTop">
+                        <Icon type="ios-arrow-up" />
+                    </div>
+                </vuescroll>
+            </div>
+        </div>
+        <!-- 试卷评测打分 -->
+        <div :class="curBarIndex == 0 ? 'animated fadeIn evaluation-base-info':'evaluation-base-info animated fadeOutRight'" v-show="curBarIndex == 0">
+            <Scoring :examInfo="examInfo" ref="score-box"></Scoring>
+        </div>
+    </div>
+</template>
+<script>
+import TestPaper from '@/view/evaluation/index/TestPaper.vue'
+import Scoring from '@/view/learnactivity/Scoring.vue'
+export default {
+    components: {
+        TestPaper,
+        Scoring
+    },
+    data() {
+        return {
+            showBack: false,
+            curBarIndex: 0,
+            curSubIndex: 0,
+            isLoading: false,
+            examInfo: {}
+        }
+    },
+    methods: {
+        goBack() {
+            this.$router.push({
+                path: '/home/myCourse'
+            })
+        },
+        /**
+         * 判断是否显示回到顶部按钮
+         * @param vertical
+         * @param horizontal
+         * @param nativeEvent
+         */
+        checkBackTop(vertical, horizontal, nativeEvent) {
+            if (vertical.scrollTop > 100) {
+                this.showBack = true
+            } else {
+                this.showBack = false
+            }
+        },
+        /**vuescroll回到顶部 */
+        handleBackToTop() {
+            this.$refs['test-paper-detail'].scrollTo(
+                { y: '0' }, 300
+            )
+        },
+        //查询当前评测的完整信息
+        findExamInfo(examId, code) {
+            let requestData = {
+                id: examId,
+                code: code
+            }
+            this.isLoading = true
+            this.$api.learnActivity.FindExamInfos(requestData).then(
+                async res => {
+                    if (!res.error) {
+                        let resData = res.examInfo[0]
+                        resData.score = 0
+                        for (let index in resData.papers) {
+                            resData.score += resData.papers[index].point.reduce((total, item) => {
+                                return total + parseInt(item)
+                            }, 0)
+                            if (resData.papers[index].blob) {
+                                let blob = resData.papers[index].blob
+                                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
+                                resData.score += resData.papers[index].score
+                            }
+                        }
+                        this.examInfo = resData
+                    } else {
+                        this.$Message.error('API ERROR!')
+                    }
+                },
+                err => {
+                    this.$Message.error('API ERROR!')
+                }
+            ).finally(() => {
+                this.isLoading = false;
+            })
+        }
+    },
+    created() {
+        let examId = this.$route.query.examId
+        let code = this.$route.query.code
+        if (examId && code) {
+            this.findExamInfo(examId, code)
+        } else {
+            this.$Message.error('参数错误,无法查看当前评测数据')
+        }
+    }
+}
+</script>
+<style scoped lang="less">
+@import "./EvDetail.less";
+</style>
+<style lang="less">
+</style>

+ 17 - 0
TEAMModelOS/ClientApp/src/view/newcourse/MyCourse.less

@@ -131,4 +131,21 @@
 .action-btn-wrap {
     margin-left:20px;
     cursor:pointer;
+}
+.evaluation-status-tag {
+    padding: 1px 2px;
+    border: 1px solid #1CC0F3;
+    margin-left: 2px;
+    border-radius: 2px;
+    font-size:12px;
+    vertical-align: text-bottom;
+}
+.ev-attr-wrap{
+    margin-right: 20px;
+    color: white;
+    margin-top: 5px;
+    display: inline-block;
+    .attr-label{
+        color: #a5a5a5;
+    }
 }

+ 174 - 4
TEAMModelOS/ClientApp/src/view/newcourse/MyCourse.vue

@@ -35,7 +35,7 @@
                             <div v-for="(item,index) in courseListP" :key="index" @click="selectCourse(index)" :class="index === curCusIndex ? 'course-list-item block-bg block-bg-active':'course-list-item block-bg'">
                                 <p class="course-name">
                                     {{item.name}}
-                                    <Icon type="ios-information-circle-outline" @click="toggleCusInfo" :title="$t('cusMgt.cusInfo')"/>
+                                    <Icon type="ios-information-circle-outline" @click="toggleCusInfo" :title="$t('cusMgt.cusInfo')" />
                                 </p>
                                 <p class="course-code">
                                     <Icon type="md-pricetags" />
@@ -80,9 +80,11 @@
                 </div>
             </div>
             <div class="course-classroom-info" id="table-height">
-                <div class="course-classroom-info-header" style="padding-right:50px;">
+                <div class="course-classroom-info-header" style="padding-right:30px;">
                     <span @click="tabName = 'record'" :class="tabName == 'record' ? 'course-classroom-label line-bottom line-bottom-active':'course-classroom-label line-bottom'">课堂记录</span>
+                    <span @click="getActivityList()" :class="tabName == 'activity' ? 'course-classroom-label line-bottom line-bottom-active':'course-classroom-label line-bottom'">活动记录</span>
                     <span @click="tabName = 'stus'" :class="tabName == 'stus' ? 'course-classroom-label line-bottom line-bottom-active':'course-classroom-label line-bottom'">{{$t('courseManage.classroom.studentList')}}</span>
+
                     <div style="float:right;color:white;" v-if="(listType == 'private' && tabName == 'stus') || (listType == 'school' && tabName == 'stus' && classList[curClassIndex].openType == 2)">
                         <span class="action-btn-wrap" @click="delStudents">
                             <Icon type="md-trash" size="16" />
@@ -101,7 +103,16 @@
                             分组视图
                         </span>-->
                     </div>
+                    <div style="float:right;color:white;" v-if="tabName == 'activity'" class="dark-iview-select">
+                        <span class="action-btn-wrap" @click="delStudents">
+                            活动类型:
+                        </span>
+                        <Select v-model="curAcType" style="width:160px" size="small">
+                            <Option v-for="(item,index) in acTypeList" :disabled="index > 2" :value="item.value" :key="item.value">{{ item.label }}</Option>
+                        </Select>
+                    </div>
                 </div>
+                <!-- 学生名单 -->
                 <div class="course-classroom-info-content dark-iview-table animated fadeIn" v-show="tabName == 'stus'">
                     <vuescroll style="height:100%;">
                         <Table :columns="studentColumn" :data="students" @on-selection-change="(selections)=>{delSelection = selections}" :height="tableHeight" class="system-classroom-table" :loading="stuLoading" no-data-text="暂无学生">
@@ -118,6 +129,7 @@
                         </Table>
                     </vuescroll>
                 </div>
+                <!-- 课堂记录 -->
                 <div v-show="tabName == 'record'" class="animated fadeIn class-record-wrap">
                     <vuescroll>
                         <List>
@@ -147,6 +159,47 @@
                         <p style="width:100%;text-align:center;color:#808080;margin-top:20px;">暂未对接HiTeach上传数据</p>
                     </vuescroll>
                 </div>
+                <!-- 活动记录 -->
+                <div v-show="tabName == 'activity'" class="animated fadeIn class-record-wrap">
+                    <vuescroll>
+                        <List>
+                            <ListItem v-for="(item,index) in evList" :key="index" style="border-color:#505050;cursor: pointer;">
+                                <ListItemMeta @click.native="toEvDetail(index)">
+                                    <p slot="title" class="record-name">
+                                        {{item.name}}
+                                        <span class="evaluation-status-tag" :style="{ borderColor: item.progress == 'pending' ? '#0BADD4' : item.progress == 'going' ? '#1CC0F3' : '#ed4014', color: (item.progress == 'pending' ? '#0BADD4' : item.progress == 'going' ? '#1CC0F3' : '#ed4014')}">
+                                            {{ item.progress == 'pending' ? $t('learnActivity.mgtScEv.pending') : item.progress == 'going' ? $t('learnActivity.mgtScEv.going') : $t('learnActivity.mgtScEv.finish') }}
+                                        </span>
+                                    </p>
+                                    <span slot="avatar" style="margin-top:12px;display: inline-block;margin-left:10px;">
+                                        <Icon custom="iconfont icon-test" size="30" color="white" />
+                                    </span>
+                                    <div slot="description">
+                                        <span class="ev-attr-wrap">
+                                            <span class="attr-label">
+                                                <Icon type="md-time" size="16" />
+                                                {{$t('learnActivity.mgtScEv.createTime')}}
+                                            </span>
+                                            <span class="attr-value">
+                                                {{dateFormat(item.startTime)}}
+                                            </span>
+                                        </span>
+                                        <span class="ev-attr-wrap">
+                                            <span class="attr-label">
+                                                <Icon type="ios-cube" size="14" />
+                                                {{$t('learnActivity.mgtScEv.evType')}}
+                                            </span>
+                                            <span class="attr-value">
+                                                {{getTypeLabel(item.type)}}
+                                            </span>
+                                        </span>
+                                    </div>
+                                </ListItemMeta>
+                            </ListItem>
+                        </List>
+                        <!-- <p style="width:100%;text-align:center;color:#808080;margin-top:20px;">暂未对接活动列表数据</p> -->
+                    </vuescroll>
+                </div>
             </div>
         </div>
         <Drawer :title="$t('cusMgt.cusInfo')" class-name="dark-iview-drawer" width="450" :closable="false" v-model="showCusInfo" @on-close="baseEditStatus = true">
@@ -241,6 +294,34 @@ export default {
             }
         }
         return {
+            acTypeList: [
+                {
+                    label: '评测',
+                    value: 'ev'
+                },
+                {
+                    label: '投票',
+                    value: 'vote'
+                },
+                {
+                    label: '问卷',
+                    value: 'qu'
+                },
+                {
+                    label: '作业',
+                    value: 'hw'
+                },
+                {
+                    label: '自主学习',
+                    value: 'sl'
+                }
+            ],
+            curAcType: 'ev',
+            evList: [],//评测列表
+            voteList: [],//投票列表
+            quList: [],//问卷列表
+            hwList: [],//作业列表
+            slList: [],//自主学习
             delSelection: [],
             selections: [],
             schoolClassList: [],
@@ -270,7 +351,7 @@ export default {
                     sortable: true
                 },
                 {
-                    title:this.$t('cusMgt.stuClassCol4'),
+                    title: this.$t('cusMgt.stuClassCol4'),
                     slot: 'groupId',
                     align: 'center',
                     sortable: true
@@ -323,6 +404,84 @@ export default {
         }
     },
     methods: {
+        /**获取type对应的label */
+        getTypeLabel(code) {
+            for (let item of this.$GLOBAL.EV_TYPE()) {
+                if (item.value == code) {
+                    return item.label
+                }
+            }
+        },
+        //时间戳转换
+        dateFormat(timestamp) {
+            var date = new Date(timestamp)
+            var Y = date.getFullYear() + '-'
+            var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+            var D = date.getDate() + ' '
+            return Y + M + D;
+        },
+        //获取活动列表
+        getActivityList() {
+            this.tabName = 'activity'
+            switch (this.curAcType) {
+                case 'ev':
+                    if (this.evList.length) break //首次(数组为空)访问数据
+                    this.getEvList()
+                    break
+                case 'vote':
+                    if (this.evList.length) break
+                    this.getVoteList()
+                    break
+                case 'qu':
+                    if (this.evList.length) break
+                    this.getQuList()
+                    break
+                case 'hw':
+                    if (this.evList.length) break
+                    this.getHwList()
+                    break
+                case 'sl':
+                    if (this.evList.length) break
+                    this.getSlList()
+                    break
+                default:
+                    break
+            }
+        },
+        //获取评测列表
+        getEvList() {
+            let requestData = {
+                code: this.$store.state.userInfo.TEAMModelId
+            }
+            this.$api.learnActivity.FindExamInfo(requestData).then(
+                res => {
+                    if (!res.error) {
+                        res.examInfo = res.examInfo.sort((a, b) => {
+                            return a.createTime - b.createTime > 0 ? -1 : 1
+                        })
+                        this.evList = res.examInfo
+                    } else {
+                        this$Message.error('API ERROR!')
+                    }
+                }
+            )
+        },
+        //获取投票列表
+        getVoteList() {
+
+        },
+        //获取问卷列表
+        getQuList() {
+
+        },
+        //获取作业列表
+        getHwList() {
+
+        },
+        //获取自主学习列表
+        getSlList() {
+
+        },
         getClassType(scope, openType) {
             if (this.listType == 'private') {
                 return {
@@ -471,7 +630,7 @@ export default {
             this.stuLoading = true
             let params = {
                 'school_code': this.listType == 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId,
-                'id': this.classList[this.curClassIndex].id,
+                'ids': [this.classList[this.curClassIndex].id],
                 //school:校本班级 teacher:私人课程校本名单 private:私人课程动态名单
                 'scope': this.listType == 'school' ? 'school' : this.classList[this.curClassIndex].scope == 'school' ? 'teacher' : 'private'
             }
@@ -742,12 +901,23 @@ export default {
                 this.findClassStu()
             }
         },
+        //查看课堂记录详情
         toClassRecoerd() {
             this.listLoading = true
             setTimeout(() => {
                 this.$router.push({ path: '/home/classRecord' })
             }, 500)
         },
+        // 查看评测详情
+        toEvDetail(index) {
+            this.$router.push({
+                path: '/home/evDetail',
+                query: {
+                    examId: this.evList[index].id,
+                    code: this.evList[index].code
+                }
+            })
+        },
         //删除个人课程
         delCourse() {
             this.$Modal.confirm({

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

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

+ 165 - 66
TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.vue

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

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

@@ -598,7 +598,7 @@ export default {
                 } else {
                     let params = {
                         'school_code': this.$store.state.userInfo.schoolCode,
-                        'id': this.classroomListShow[this.curClassIndex].id,
+                        'ids': [this.classroomListShow[this.curClassIndex].id],
                         'scope': 'school'
                     }
                     this.stuLoading = true

+ 11 - 9
TEAMModelOS/ClientApp/src/view/vote/ManageVote.vue

@@ -76,8 +76,8 @@
 										<BaseVoteTable :tableDatas="tableData" v-show="isOptionView"></BaseVoteTable>
 										<BaseVoteSsTable :tableDatas="studentsTable" v-show="!isOptionView"></BaseVoteSsTable>
 										<div style="display:flex">
-											<BaseVotePie pieId="votePie" :pieData="tableData" v-show="tableData.length"></BaseVotePie>
-											<BaseVoteBar pieId="votePie2" v-show="tableData.length" :pieData="tableData"></BaseVoteBar>
+											<BaseVotePie pieId="votePie" :pieData="studentsTable" v-show="studentsTable.length"></BaseVotePie>
+											<BaseVoteBar pieId="votePie2" v-if="tableData.length" :pieData="tableData"></BaseVoteBar>
 										</div>
 									</vuescroll>
 								</div>
@@ -221,6 +221,7 @@
 					"easeInQuad"
 				);
 				this.hasNewAdd = false
+				this.isOptionView = true
 			},
 			
 			/* 获取投票活动的详细数据 */
@@ -342,12 +343,13 @@
 								})
 							})
 						})
-						console.log(records)
+						console.log('所有学生名单',list)
+						console.log('已投票学生名单',records.records)
 						
 						// 要根据作答情况 结合两张表 处理表格显示的数据 
 						if (records.options.length) {
 							let arr = []
-							this.studentsTable = records.records
+							this.studentsTable = list
 							records.options.forEach((item, index) => {
 								arr.push({
 									// option: this.getSimpleText(item.optionValue),
@@ -356,14 +358,14 @@
 									result: new Array(item.count).fill('1')
 								})
 							})
-							console.log(this.studentsTable)
 							this.studentsTable.forEach(i => {
-								let matchList = list.filter(j => j.id === i.userid)
-								i.name = matchList.length ? matchList[0].name : this.$t('vote.noData')
-								i.classroomName = matchList.length ? matchList[0].classroomName : this.$t('vote.noData')
-								i.option = i.opt.join(',')
+								let matchList = records.records.filter(j => j.userid === i.id)
+								i.option = matchList.length ? matchList[0].opt.join(',') : null
+								// i.classroomName = matchList.length ? matchList[0].classroomName : this.$t('vote.noData')
+								// i.option = i.opt.join(',')
 							})
 							console.log(arr)
+							console.log(this.studentsTable)
 							this.tableData = arr
 						} else {
 							this.tableData = []

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

@@ -18,6 +18,7 @@ using TEAMModelOS.SDK.Helper.Common.CollectionHelper;
 using TEAMModelOS.Models;
 using Microsoft.Extensions.Options;
 using TEAMModelOS.Filter;
+using TEAMModelOS.Services.Common;
 
 namespace TEAMModelOS.Controllers
 {
@@ -31,18 +32,22 @@ namespace TEAMModelOS.Controllers
     [ApiController]
     public class SurveyController : ControllerBase
     {
+        private readonly AzureRedisFactory _azureRedis;
         private readonly AzureCosmosFactory _azureCosmos;
         private readonly SnowflakeId _snowflakeId;
         private readonly AzureServiceBusFactory _serviceBus;
         private readonly DingDing _dingDing;
         private readonly Option _option;
-        public SurveyController(AzureCosmosFactory azureCosmos, AzureServiceBusFactory serviceBus, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option)
+        private readonly AzureStorageFactory _azureStorage;
+        public SurveyController(AzureCosmosFactory azureCosmos, AzureServiceBusFactory serviceBus, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option, AzureRedisFactory azureRedis, AzureStorageFactory azureStorage)
         {
             _snowflakeId= snowflakeId;
             _serviceBus = serviceBus;
             _azureCosmos = azureCosmos;
             _dingDing = dingDing;
             _option = option?.Value;
+            _azureRedis = azureRedis;
+            _azureStorage = azureStorage;
         }
 
 
@@ -73,13 +78,37 @@ namespace TEAMModelOS.Controllers
                     request = await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(request, new PartitionKey($"{request.code}"));
                 }
                 else {
-                    Survey info = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Survey>(request.id, new PartitionKey($"{request.code}"));
-                    if (info.progress.Equals("going"))
+                    var response = await client.GetContainer("TEAMModelOS", "Common").ReadItemStreamAsync(request.id, new PartitionKey($"{request.code}"));
+                    if (response.Status == 200)
                     {
-                        return Ok(new { v = "活动正在进行中" });
+                        using var json = await JsonDocument.ParseAsync(response.ContentStream);
+                        var info = json.ToObject<Survey>();
+                        if (info.progress.Equals("going"))
+                        {
+                            return Ok(new { v = "活动正在进行中" });
+                        }
+                        if (request.startTime > now)
+                        {
+                            request.progress = "pending";
+                        }
+                        else
+                        {
+                            request.progress = "going";
+                        }
+                        request.progress = info.progress;
+                        request = await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(request, info.id, new PartitionKey($"{info.code}"));
+                    }
+                    else {
+                        if (request.startTime > now)
+                        {
+                            request.progress = "pending";
+                        }
+                        else
+                        {
+                            request.progress = "going";
+                        }
+                        request = await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(request, new PartitionKey($"{request.code}"));
                     }
-                    request.progress = info.progress;
-                    request = await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(request, info.id, new PartitionKey($"{info.code}"));
                 }
                 return Ok(new { survey = request});
             } catch (Exception ex) {
@@ -112,7 +141,7 @@ namespace TEAMModelOS.Controllers
                 var stimestamp = DateTimeOffset.UtcNow.AddDays(-30).ToUnixTimeMilliseconds();
                 if (requert.TryGetProperty("stime", out JsonElement stime))
                 {
-                    if (!stime.ValueKind.Equals(JsonValueKind.Undefined) && stime.TryGetInt64(out long data))
+                    if (!stime.ValueKind.Equals(JsonValueKind.Undefined)&& !stime.ValueKind.Equals(JsonValueKind.Null)  && stime.TryGetInt64(out long data))
                     {
                         stimestamp = data;
                     };
@@ -121,14 +150,14 @@ namespace TEAMModelOS.Controllers
                 var etimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
                 if (requert.TryGetProperty("etime", out JsonElement etime))
                 {
-                    if (!etime.ValueKind.Equals(JsonValueKind.Undefined) && etime.TryGetInt64(out long data))
+                    if (!etime.ValueKind.Equals(JsonValueKind.Null)&&!etime.ValueKind.Equals(JsonValueKind.Undefined) && etime.TryGetInt64(out long data))
                     {
                         etimestamp = data;
                     };
 
                 };
                 var progresssql = "";
-                if (!requert.TryGetProperty("progress", out JsonElement progress))
+                if (requert.TryGetProperty("progress", out JsonElement progress))
                 {
 
                     if (!progress.ValueKind.Equals(JsonValueKind.Undefined) && !progress.ValueKind.Equals(JsonValueKind.Null) && progress.ValueKind.Equals(JsonValueKind.String))
@@ -141,7 +170,7 @@ namespace TEAMModelOS.Controllers
                 int? topcout = null;
                 if (requert.TryGetProperty("count", out JsonElement jcount))
                 {
-                    if (!jcount.ValueKind.Equals(JsonValueKind.Undefined) && jcount.TryGetInt32(out int data))
+                    if (!jcount.ValueKind.Equals(JsonValueKind.Undefined) && !jcount.ValueKind.Equals(JsonValueKind.Null) && jcount.TryGetInt32(out int data))
                     {
                         topcout = data;
                     }
@@ -160,9 +189,9 @@ namespace TEAMModelOS.Controllers
                 };
                 List<object> surveys = new List<object>();
                 var client = _azureCosmos.GetCosmosClient();
-                var query = $"select   c.id,c.name,c.code,c.startTime,c.endTime,c.progress from c where c.createTime >= {stimestamp} and c.createTime <= {etimestamp}  {progresssql } ";
+                var query = $"select c.id,c.name,c.code,c.startTime,c.endTime,c.progress from c where  c.createTime >= {stimestamp} and c.createTime <= {etimestamp}  {progresssql } ";
                 await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: query,
-                    requestOptions: new QueryRequestOptions() { MaxItemCount = topcout, PartitionKey = new PartitionKey($"Vote-{code}") }))
+                    requestOptions: new QueryRequestOptions() { MaxItemCount = topcout, PartitionKey = new PartitionKey($"Survey-{code}") }))
                 {
                     using var json = await JsonDocument.ParseAsync(item.ContentStream);
                     if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
@@ -273,5 +302,30 @@ namespace TEAMModelOS.Controllers
                 return BadRequest(e.StackTrace);
             }
         }
+
+        /// <summary>
+        /// 问卷答案提交
+        /// </summary>
+        /// <redis>
+        /// 投票活动选项计数器 使用SortedSet(有序集合)ZSET 数据集合 使用命令 ZINCRBY  key:"Vote:Count:AAA",value:"A",score:1
+        /// 投票活动 投票记录  使用Hash(哈希表)使用命令 key:"Vote:Record:AAA",feild:15283771540-20210105,value:"{"opt":["A","C","A"],"time":1608274766154}"
+        /// </redis>
+        /// <param name="request">
+        /// !"id":"aaaa"
+        /// !"code":"Vote-hbcn"/"code":"Vote-1606285227"
+        /// !"record":[["A","B"],["A"],["D"],[],["建议提升服务质量"]]
+        /// </param>
+        /// <returns>
+        /// msgid=0投票失败,1投票成功,2不在时间范围内,3不在发布范围内,4投票周期内重复投票,5周期内的可投票数不足,6未设置投票项
+        /// </returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("answer")]
+        [AuthToken(Roles = "teacher,student")]
+        public async Task<IActionResult> Answer(JsonElement request)
+        {
+            var (userid, _, _, _) = HttpContext.GetAuthTokenInfo();
+            int msgid = await ActivityStudentService.Answer(request, _azureCosmos, _azureRedis, userid, _azureStorage);
+            return Ok(new { msgid });
+        }
     }
 }

+ 29 - 11
TEAMModelOS/Controllers/Common/VoteController.cs

@@ -68,11 +68,12 @@ namespace TEAMModelOS.Controllers.Learn
             {
                 //新增Vote
                 var client = _azureCosmos.GetCosmosClient();
-                request.code = request.pk + "-" + request.code;
+               
                 long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
                 request.createTime = now;
                 if (string.IsNullOrEmpty(request.id))
                 {
+                    request.code = request.pk + "-" + request.code;
                     request.id = Guid.NewGuid().ToString();
                     if (request.startTime > now)
                     {
@@ -85,21 +86,38 @@ namespace TEAMModelOS.Controllers.Learn
                 }
                 else
                 {
-                    Vote info = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Vote>(request.id, new PartitionKey($"{request.code}"));
-                    if (info.progress.Equals("going"))
+                    var response = await client.GetContainer("TEAMModelOS", "Common").ReadItemStreamAsync(request.id, new PartitionKey($"{request.code}"));
+                    if (response.Status == 200)
                     {
-                        return Ok(new { v = "活动正在进行中" });
-                    }
-                    if (request.startTime > now)
-                    {
-                        request.progress = "pending";
+                        using var json = await JsonDocument.ParseAsync(response.ContentStream);
+                        var info = json.ToObject<Vote>();
+                        if (info.progress.Equals("going"))
+                        {
+                            return Ok(new { v = "活动正在进行中" });
+                        }
+                        if (request.startTime > now)
+                        {
+                            request.progress = "pending";
+                        }
+                        else
+                        {
+                            request.progress = "going";
+                        }
+                        request.progress = info.progress;
+                        request = await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(request, info.id, new PartitionKey($"{info.code}"));
                     }
                     else
                     {
-                        request.progress = "going";
+                        if (request.startTime > now)
+                        {
+                            request.progress = "pending";
+                        }
+                        else
+                        {
+                            request.progress = "going";
+                        }
+                        request = await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(request, new PartitionKey($"{request.code}"));
                     }
-                    request.progress = info.progress;
-                    request = await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(request, info.id, new PartitionKey($"{info.code}"));
                 }
                 return Ok(new { vote = request });
             }

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

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

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

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

+ 126 - 15
TEAMModelOS/Services/Common/ActivityStudentService.cs

@@ -223,7 +223,7 @@ namespace TEAMModelOS.Services.Common
             }
             //开始时间,默认最近三十天
             var stimestamp = DateTimeOffset.UtcNow.AddDays(-30).ToUnixTimeMilliseconds();
-            if (!requert.TryGetProperty("stime", out JsonElement stime))
+            if (requert.TryGetProperty("stime", out JsonElement stime))
             {
                 if (!stime.ValueKind.Equals(JsonValueKind.Undefined) && !stime.ValueKind.Equals(JsonValueKind.Null) &&stime.TryGetInt64(out long data))
                 {
@@ -235,28 +235,28 @@ namespace TEAMModelOS.Services.Common
             var etimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
             string etimesql = $" and c.startTime <= {etimestamp}   ";
             var progresssql = "";
-            if (!requert.TryGetProperty("progress", out JsonElement progress))
+            if (requert.TryGetProperty("progress", out JsonElement progress))
             {
 
                 if (!progress.ValueKind.Equals(JsonValueKind.Undefined) && !progress.ValueKind.Equals(JsonValueKind.Null) && progress.ValueKind.Equals(JsonValueKind.String))
                 {
-                    progresssql = $" and c.progress='{progresssql}' ";
+                    progresssql = $" and c.progress='{progress}' ";
                 }
             }
             var typesql = "";
-            if (!requert.TryGetProperty("type", out JsonElement type))
+            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}' ";
+                    typesql = $" and c.type='{type}' ";
                 }
             }
             string continuationTokenSchool = null;
             string continuationTokenTeacher = null;
             //默认不指定返回大小
             int? topcout = null;
-            if (!requert.TryGetProperty("count", out JsonElement jcount))
+            if (requert.TryGetProperty("count", out JsonElement jcount))
             {
                 if (!jcount.ValueKind.Equals(JsonValueKind.Undefined) && !jcount.ValueKind.Equals(JsonValueKind.Null) && jcount.TryGetInt32(out int data))
                 {
@@ -266,7 +266,7 @@ namespace TEAMModelOS.Services.Common
             //是否需要进行分页查询,默认不分页
             bool iscontinuation = false;
             //如果指定了返回大小
-            if (!requert.TryGetProperty("continuationTokenSchool", out JsonElement continuationSchool))
+            if (requert.TryGetProperty("continuationTokenSchool", out JsonElement continuationSchool))
             {
                 //指定了cancellationToken continuationSchool
                 if (!continuationSchool.ValueKind.Equals(JsonValueKind.Null) && continuationSchool.ValueKind.Equals(JsonValueKind.String))
@@ -276,7 +276,7 @@ namespace TEAMModelOS.Services.Common
                 }
             }
             //如果指定了返回大小
-            if (!requert.TryGetProperty("continuationTokenTeacher", out JsonElement continuationTeacher))
+            if (requert.TryGetProperty("continuationTokenTeacher", out JsonElement continuationTeacher))
             {
                 //指定了cancellationToken 表示需要进行分页
                 if (!continuationTeacher.ValueKind.Equals(JsonValueKind.Null) && continuationTeacher.ValueKind.Equals(JsonValueKind.String))
@@ -375,9 +375,9 @@ namespace TEAMModelOS.Services.Common
                 }
             }
             bool tips = false;
-            if (!requert.TryGetProperty("tips", out JsonElement jtips))
+            if (requert.TryGetProperty("tips", out JsonElement jtips))
             {
-                if (!jtips.ValueKind.Equals(JsonValueKind.Undefined) && !jtips.ValueKind.Equals(JsonValueKind.Null) && !jtips.ValueKind.Equals(JsonValueKind.True))
+                if (!jtips.ValueKind.Equals(JsonValueKind.Undefined) && !jtips.ValueKind.Equals(JsonValueKind.Null) && (jtips.ValueKind.Equals(JsonValueKind.True)|| jtips.ValueKind.Equals(JsonValueKind.False)))
                 {
                     tips = jtips.GetBoolean();
                 }
@@ -396,32 +396,32 @@ namespace TEAMModelOS.Services.Common
                             activityTips = DoVoteTips;
                             //msgid, //0不能投票,1可以投票,2不在时间范围内,3周期内的可投票数不足
                             //voteCount 可用投票数
-                            res = activityTips(data, _azureCosmos, id, _azureRedis);
+                            res =await activityTips(data, _azureCosmos, id, _azureRedis);
                             break;
                         //问卷
                         case "survey":
                             //msgid 0 已作答, 1未作答,2,未完成
                             activityTips = DoSurveyTips;
-                            res = activityTips(data, _azureCosmos, id, _azureRedis);
+                            res = await activityTips(data, _azureCosmos, id, _azureRedis);
                             break;
                         //评测
                         case "exam":
                             //msgid 0 已作答, 1未作答,2,未完成, 用时间控制 相关发布状态,并且展示相应的结果
                             activityTips = DoExamTips;
-                            res = activityTips(data, _azureCosmos, id, _azureRedis);
+                            res = await activityTips(data, _azureCosmos, id, _azureRedis);
                             break;
                         //学习活动
                         case "learn":
                             //msgid 0 已完成, 1未开始,2,未完成
                             activityTips = DoLearnTips;
-                            res = activityTips(data, _azureCosmos, id, _azureRedis);
+                            res = await activityTips(data, _azureCosmos, id, _azureRedis);
                             break;
                         //作业活动
                         case "homework":
                             //msgid 0 已作答, 1未作答,2,未完成,3已批改,且有错误,4已批改,已完成
                             //index:0,1,5 错误题序
                             activityTips = DoHomeworkTips;
-                            res = activityTips(data, _azureCosmos, id, _azureRedis);
+                            res = await activityTips(data, _azureCosmos, id, _azureRedis);
                             break;
                         default: break;
                     }
@@ -429,6 +429,117 @@ namespace TEAMModelOS.Services.Common
             }
             return (datas, continuationTokenSchool,continuationTokenTeacher);
         }
+
+        public static async Task<int> Answer(JsonElement request, AzureCosmosFactory _azureCosmos, AzureRedisFactory azureRedis, string userid, AzureStorageFactory _azureStorage)
+        {
+
+            DateTimeOffset now = DateTimeOffset.UtcNow;
+            long curr = now.ToUnixTimeMilliseconds();
+            byte msgid = 0;//
+            //活动id
+            if (!request.TryGetProperty("id", out JsonElement id))
+            {
+                return msgid;
+            }
+            //活动分区
+            if (!request.TryGetProperty("code", out JsonElement code))
+            {
+                return msgid;
+            }
+
+            try
+            {
+                //1.再次检查投票
+                var client = _azureCosmos.GetCosmosClient();
+                Survey survey = null;
+                ///TODO 检查是否在投票范围内,包括在tmdids 及班级  但是需要处理认证金钥中的班级问题
+                await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<Survey>(queryText: $"select c.id,c.code , c.progress,c.times,c.voteNum,c.startTime,c.endTime from c where c.id = '{id}'",
+                    requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{code}") }))
+                {
+                    survey = item;
+                    break;
+                }
+                if (survey != null)
+                {
+                    //判断投票时间是否在起止时间内
+                    if (curr >= survey.startTime && curr <= survey.endTime)
+                    {
+                        if (request.TryGetProperty("record", out JsonElement record)) 
+                        {
+                            var recs = record.ToObject<List<List<string>>>();
+                            if (recs.IsNotEmpty() && recs.Count == survey.ans.Count)
+                            {
+                               //处理问卷调查表的每一题选项数
+                                for (int index = 0; index < recs.Count; index++) {
+                                    Dictionary<string, int> dict = new Dictionary<string, int>();
+                                    if (recs[index].IsNotEmpty()) {
+                                        recs[index].ForEach(x => {
+                                            if (survey.ans[index].Contains(x))
+                                            {
+                                                if (dict.ContainsKey(x))
+                                                {
+                                                    dict[x] = dict[x] + 1;
+                                                }
+                                                else {
+                                                    dict[x] = 1;
+                                                }
+                                            }
+                                            else {
+                                                if (dict.ContainsKey("other"))
+                                                {
+                                                    dict["other"] = dict["other"] + 1;
+                                                }
+                                                else
+                                                {
+                                                    dict["other"] = 1;
+                                                }
+                                            }
+                                        });
+                                    }
+                                    var value= azureRedis.GetRedisClient(8).HashGet($"Survey:Record:{survey.id}_{survey.code}",index);
+                                    if (value != default && !value.IsNullOrEmpty)
+                                    {
+                                        Dictionary<string, int> dt = value.ToString().ToObject<Dictionary<string, int>>();
+                                        foreach (var kp in dict)
+                                        {
+                                            if (dt.ContainsKey(kp.Key))
+                                            {
+                                                dt[kp.Key] = dt[kp.Key] + kp.Value;
+                                            }
+                                            else
+                                            {
+                                                dt.Add(kp.Key, kp.Value);
+                                            }
+                                        }
+                                        await azureRedis.GetRedisClient(8).HashSetAsync($"Survey:Record:{survey.id}_{survey.code}", index, dt.ToJsonString());
+                                    }
+                                    else {
+                                       await azureRedis.GetRedisClient(8).HashSetAsync($"Survey:Record:{survey.id}_{survey.code}", index, dict.ToJsonString());
+                                    }
+                                }
+                                //保存当前提交人的记录
+                               await _azureStorage.UploadFileByContainer(survey.owner, record.ToJsonString(), "survey", $"{survey.name}/record/{userid}.json");
+                            }
+                            else {
+                                //提交的作答不符合问卷的答案长度。
+                                msgid = 3;
+                            }
+                        }
+                        
+                    }
+                    else
+                    {
+                        msgid = 2;
+                    }
+                }
+            }
+            catch (Exception e)
+            {
+                throw new Exception(e.StackTrace);
+            }
+            return msgid;
+        }
+
         private async static Task<dynamic> DoVoteTips(ActivityData commonData , AzureCosmosFactory _azureCosmos,string userid,AzureRedisFactory _azureRedis)
         {
             Vote vote=null;

+ 1 - 1
TEAMModelOS/TEAMModelOS.csproj

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