소스 검색

Merge branch 'develop' into hhb/develop-2205

CrazyIter_Bin 3 년 전
부모
커밋
574230c648
62개의 변경된 파일3538개의 추가작업 그리고 1111개의 파일을 삭제
  1. 17 8
      TEAMModelBI/ClientApp/src/view/index/index.vue
  2. 3 3
      TEAMModelBI/Controllers/BIAbility/AbilityMgmtController.cs
  3. 2 2
      TEAMModelBI/Controllers/BIAbility/AbilityTaskMgmtController.cs
  4. 123 34
      TEAMModelBI/Controllers/BIHome/OnLineController.cs
  5. 4 4
      TEAMModelBI/Controllers/BINormal/AppCompanyController.cs
  6. 2 2
      TEAMModelBI/Controllers/BINormal/BIOpenApiController.cs
  7. 2 2
      TEAMModelBI/Controllers/BINormal/CompanyController.cs
  8. 1 1
      TEAMModelBI/Controllers/BISchool/AreaRelevantController.cs
  9. 2 2
      TEAMModelBI/Controllers/BISchool/BatchAreaController.cs
  10. 2 2
      TEAMModelBI/Controllers/BISchool/BatchSchoolController.cs
  11. 1 1
      TEAMModelBI/Controllers/BISchool/ProductController.cs
  12. 1 1
      TEAMModelBI/Controllers/BISchool/RoomController.cs
  13. 3 3
      TEAMModelBI/Controllers/BISchool/SchoolController.cs
  14. 1 1
      TEAMModelBI/Controllers/BIServer/BiServersController.cs
  15. 1 1
      TEAMModelBI/Controllers/Core/BlobController.cs
  16. 3 3
      TEAMModelBI/Controllers/DingDingStruc/TableDingDingInfoController.cs
  17. 21 17
      TEAMModelOS.FunctionV4/ServiceBus/ActiveTaskTopic.cs
  18. 13 3
      TEAMModelOS.SDK/Models/Cosmos/School/SchoolProduct.cs
  19. 1 1
      TEAMModelOS.SDK/TEAMModelOS.SDK.csproj
  20. 26 20
      TEAMModelOS/ClientApp/public/lang/en-US.js
  21. 20 15
      TEAMModelOS/ClientApp/public/lang/zh-CN.js
  22. 21 18
      TEAMModelOS/ClientApp/public/lang/zh-TW.js
  23. 1 3
      TEAMModelOS/ClientApp/src/access/login.js
  24. 2 20
      TEAMModelOS/ClientApp/src/assets/student-web/component_styles/common.css
  25. 6 0
      TEAMModelOS/ClientApp/src/assets/student-web/component_styles/course-list.less
  26. 37 19
      TEAMModelOS/ClientApp/src/assets/student-web/component_styles/event-list-new.less
  27. 22 2
      TEAMModelOS/ClientApp/src/assets/student-web/component_styles/paper-test.css
  28. 5 3
      TEAMModelOS/ClientApp/src/common/BaseLayout.vue
  29. 1 1
      TEAMModelOS/ClientApp/src/components/coursemgt/StudentList.vue
  30. 60 43
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContent.less
  31. 7 0
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReport.less
  32. 5 5
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReport.vue
  33. 7 7
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReportCharts/LessonTestReportCharts.vue
  34. 54 24
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperTest.vue
  35. 228 183
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/composePaper.vue
  36. 1 1
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventList.vue
  37. 165 0
      TEAMModelOS/ClientApp/src/components/student-web/HomeView/ChartHome/ClassPoint.vue
  38. 270 0
      TEAMModelOS/ClientApp/src/components/student-web/HomeView/ChartHome/ExamPerform.vue
  39. 263 0
      TEAMModelOS/ClientApp/src/components/student-web/HomeView/ChartHome/HomeworkPoint.vue
  40. 1 1
      TEAMModelOS/ClientApp/src/components/student-web/HomeView/CourseListView.vue
  41. 97 0
      TEAMModelOS/ClientApp/src/components/student-web/HomeView/HomeViewnnnnew.less
  42. 509 0
      TEAMModelOS/ClientApp/src/components/student-web/HomeView/HomeViewnnnnew.vue
  43. BIN
      TEAMModelOS/ClientApp/src/components/student-web/HomeView/noData.png
  44. 3 1
      TEAMModelOS/ClientApp/src/store/module/config.js
  45. 6 6
      TEAMModelOS/ClientApp/src/utils/editorTools.js
  46. 51 11
      TEAMModelOS/ClientApp/src/utils/js-fn.js
  47. 592 603
      TEAMModelOS/ClientApp/src/view/ability/Review.vue
  48. 1 1
      TEAMModelOS/ClientApp/src/view/classrecord/ClassRecord.vue
  49. 1 1
      TEAMModelOS/ClientApp/src/view/classrecord/eventchart/PopQues.vue
  50. 43 2
      TEAMModelOS/ClientApp/src/view/dashboard/Index.vue
  51. 15 2
      TEAMModelOS/ClientApp/src/view/jyzx/application.vue
  52. 42 6
      TEAMModelOS/ClientApp/src/view/learnactivity/CreatePrivEva.vue
  53. 6 1
      TEAMModelOS/ClientApp/src/view/login/page/Student.vue
  54. 1 1
      TEAMModelOS/ClientApp/src/view/student-account/class/ClassMgt.vue
  55. 1 1
      TEAMModelOS/ClientApp/src/view/student-account/stuMgt/ImportStudent.vue
  56. 1 1
      TEAMModelOS/ClientApp/src/view/student-account/stuMgt/StuMgt.vue
  57. 178 0
      TEAMModelOS/ClientApp/src/view/student-web/App.less
  58. 327 0
      TEAMModelOS/ClientApp/src/view/student-web/AppNew.vue
  59. 55 0
      TEAMModelOS/ClientApp/src/view/student-web/AppiView.less
  60. 159 0
      TEAMModelOS/Controllers/Client/HabbController.cs
  61. 44 17
      TEAMModelOS/Controllers/School/SchoolController.cs
  62. 1 1
      TEAMModelOS/Startup.cs

+ 17 - 8
TEAMModelBI/ClientApp/src/view/index/index.vue

@@ -168,7 +168,8 @@
                                 </svg>
                             </div>
                             </p>
-                            <div class="commonbox-versions">
+                            <div class="commonbox-versions" v-loading="loading.versions"
+                                element-loading-background="rgba(0, 0, 0, 0.2)">
                                 <div class="commonbox-versions-title">
                                     <p class="versions-title-name">基础版</p>
                                     <p class="versions-title-num">数量:
@@ -180,7 +181,8 @@
                                 </div>
 
                             </div>
-                            <div class="commonbox-versions">
+                            <div class="commonbox-versions" v-loading="loading.versions"
+                                element-loading-background="rgba(0, 0, 0, 0.2)">
                                 <div class="commonbox-versions-title">
                                     <p class="versions-title-name">标准版</p>
                                     <p class="versions-title-num">数量:
@@ -192,7 +194,8 @@
                                 </div>
 
                             </div>
-                            <div class="commonbox-versions">
+                            <div class="commonbox-versions" v-loading="loading.versions"
+                                element-loading-background="rgba(0, 0, 0, 0.2)">
                                 <div class="commonbox-versions-title">
                                     <p class="versions-title-name">专业版</p>
                                     <p class="versions-title-num">数量:
@@ -215,7 +218,8 @@
                             </svg>
                         </div>
                         </p>
-                        <div class="bottom-rightbox-right">
+                        <div class="bottom-rightbox-right" v-loading="loading.sizeType"
+                            element-loading-background="rgba(0, 0, 0, 0.2)">
                             <Size :lineData="totalArea.sizePie" :sizenum="allSize"></Size>
                         </div>
                     </div>
@@ -331,9 +335,11 @@ export default {
         let loading = ref({
             basics: true,
             basics2: true,
-            lessonData: true,
-            lessonAndactivity: true,
+            // lessonData: true,
+            // lessonAndactivity: true,
             active: true,
+            versions: true,
+            sizeType: true,
         })
         let onlineModels = ref('all')
         let allSize = ref({
@@ -1220,6 +1226,7 @@ export default {
                             let nums = parseInt(i) + 1
                             replaceTime[i].value == onlineData[onlineData.length - 1].key ? replaceTime.splice(nums) : ''
                         }
+                        console.log(onlineData, '查看内容')
                         //在线人数趋势
                         onlineData.forEach((x) => {
                             for (let s in replaceTime) {
@@ -1235,13 +1242,13 @@ export default {
                         //处理在线人员区分(学生)
                         onlineStudent.forEach((u) => {
                             for (let s in replaceTime) {
-                                replaceTime[s].value === u.key ? (replaceTime[s].num = u.value) : ''
+                                replaceTime[s].value === u.key ? (replaceTime[s].student = u.value) : ''
                             }
                         })
                         //处理在线人员区分(醍摩豆学生)
                         onlineTmdstudent.forEach((t) => {
                             for (let s in replaceTime) {
-                                replaceTime[s].value === t.key ? (replaceTime[s].num = t.value) : ''
+                                replaceTime[s].value === t.key ? (replaceTime[s].tmdStudent = t.value) : ''
                             }
                         })
                         replaceTime.forEach((e) => {
@@ -1581,6 +1588,7 @@ export default {
                     totalArea.value.pie1 = dataBasics
                     totalArea.value.pie2 = dataStandard
                     totalArea.value.pie3 = dataMajor
+                    loading.value.versions = false
                 }
             }).catch((error) => {
                 ElMessage.error('各版本获取异常')
@@ -1639,6 +1647,7 @@ export default {
                 })
                 console.log(dataArr, '饼图数据统计')
                 totalArea.value.sizePie.series[0].data = dataArr.various
+                loading.value.sizeType = false
                 // let ss = proxy.$common.convertSize(res.typeStics.video)
                 // console.log(ss, '大小')
             })

+ 3 - 3
TEAMModelBI/Controllers/BIAbility/AbilityMgmtController.cs

@@ -101,7 +101,7 @@ namespace TEAMModelBI.Controllers.BIAbility
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("del-ability")]
         public async Task<IActionResult> DelAbility(JsonElement jsonElement)
         {
@@ -152,7 +152,7 @@ namespace TEAMModelBI.Controllers.BIAbility
         /// <param name="request"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("upd-ability")]
         public async Task<IActionResult> UpdAbility(Ability ability)
         {
@@ -241,7 +241,7 @@ namespace TEAMModelBI.Controllers.BIAbility
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("upd-currency")]
         public async Task<IActionResult> UpdateCurrency(JsonElement jsonElement) 
         {

+ 2 - 2
TEAMModelBI/Controllers/BIAbility/AbilityTaskMgmtController.cs

@@ -118,7 +118,7 @@ namespace TEAMModelBI.Controllers.BIAbility
         /// <param name="abilityTask"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles= "assist")]
+        [AuthToken(Roles= "admin,assist")]
         [HttpPost("upd-abilitytask")]
         public async Task<IActionResult> UpdAbilityTask(RecordAbilityTask recordAbilityTask)//AbilityTask abilityTask) 
         {
@@ -227,7 +227,7 @@ namespace TEAMModelBI.Controllers.BIAbility
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("del-abilitytask")]
         public async Task<IActionResult> DelAbilityTask(JsonElement jsonElement) 
         {

+ 123 - 34
TEAMModelBI/Controllers/BIHome/OnLineController.cs

@@ -73,22 +73,28 @@ namespace TEAMModelBI.Controllers.BIHome
             todayTchCnt = await CommonFind.GetSqlValueCount(cosmosClient, "Teacher", addSql, "Base");
             todayStuCnt = await CommonFind.GetSqlValueCount(cosmosClient, "Student", addSql, "Base");
 
-            List<RecOnLine> recStuOnLines = new();
-            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "Student").GetItemQueryIterator<RecOnLine>(queryText: "select c.id,c.name,c.code,c.loginInfos from c where c.pk='Base' and array_length(c.loginInfos) > 0 ", requestOptions:new QueryRequestOptions() { }))
-            {
-                recStuOnLines.Add(item);
-            }
-            onStuCnt = (from rs in recStuOnLines from l in rs.loginInfos where l.expire >= hour1 select rs).ToList().Count();  //linq查询
-            //onStuCnt = recStuOnLines.Select(rss => new RecOnLine { id = rss.id,code=rss.code, name =rss.name,loginInfos = new List<Teacher.LoginInfo> { rss.loginInfos.Find(f => f.expire >= hour1) } }).Where(w => w.loginInfos.FirstOrDefault() != null).ToList().Count();  //lambda 表达式查询 
+            string onStuSql = $"select value(count(c.id)) from c join t in c.loginInfos where array_length(c.loginInfos) > 0 and t.expire > {hour1}";
+            onTchCnt = await CommonFind.GetSqlValueCount(cosmosClient, "Teacher", onStuSql, "Base");
+            onStuCnt = await CommonFind.GetSqlValueCount(cosmosClient, "Student", onStuSql, "Base");
+
+            //List<RecOnLine> recStuOnLines = new();
+            //await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "Student").GetItemQueryIterator<RecOnLine>(queryText: "select c.id,c.name,c.code,c.loginInfos from c where c.pk='Base' and array_length(c.loginInfos) > 0 ", requestOptions:new QueryRequestOptions() { }))
+            //{
+            //    recStuOnLines.Add(item);
+            //}
+
+            //List<RecOnLine> recTecOnLines = new();
+            //await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIterator<RecOnLine>(queryText: "select c.id,c.name,c.code,c.loginInfos from c where array_length(c.loginInfos) > 0 ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
+            //{
+            //    recTecOnLines.Add(item);
+            //}
+
+            ////onStuCnt = (from rs in recStuOnLines from l in rs.loginInfos where l.expire >= hour1 select rs).ToList().Count();  //linq查询 学生在线人数
+            //onStuCnt = recStuOnLines.Select(rss => new RecOnLine { id = rss.id,code=rss.code, name =rss.name,loginInfos = new List<Teacher.LoginInfo> { rss.loginInfos.Find(f => f.expire >= hour1) } }).Where(w => w.loginInfos.FirstOrDefault() != null).ToList().Count();  //lambda 表达式查询   教师查询人数
+
+            ////onTchCnt = (from rs in recTecOnLines from l in rs.loginInfos where l.expire >= hour1 select rs).ToList().Count();  //linq查询  教师查询人数
+            //onTchCnt = recTecOnLines.Select(rss => new RecOnLine { id = rss.id,code=rss.code, name =rss.name,loginInfos = new List<Teacher.LoginInfo> { rss.loginInfos.Find(f => f.expire >= hour1) } }).Where(w => w.loginInfos.FirstOrDefault() != null).ToList().Count();  //lambda 表达式查询    教师查询人数
 
-            List<RecOnLine> recTecOnLines = new();
-            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIterator<RecOnLine>(queryText: "select c.id,c.name,c.code,c.loginInfos from c where array_length(c.loginInfos) > 0 ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
-            {
-                recTecOnLines.Add(item);
-            }                        
-            onTchCnt = (from rs in recTecOnLines from l in rs.loginInfos where l.expire >= hour1 select rs).ToList().Count();  //linq查询
-            //onStuCnt = recTecOnLines.Select(rss => new RecOnLine { id = rss.id,code=rss.code, name =rss.name,loginInfos = new List<Teacher.LoginInfo> { rss.loginInfos.Find(f => f.expire >= hour1) } }).Where(w => w.loginInfos.FirstOrDefault() != null).ToList().Count();  //lambda 表达式查询 
-            
             return Ok(new { state = 200, areaCnt, scCnt, tchCnt, stuCnt, todayScCnt, todayTchCnt, todayStuCnt, onStuCnt, onTchCnt});
         }
 
@@ -239,30 +245,80 @@ namespace TEAMModelBI.Controllers.BIHome
             Dictionary<int, int> tchLessCnt = new();   //教师课例
             Dictionary<int, int> yesterdayCnt = new();  //昨天24小时课例
 
+            var (daySt, dayEt) = TimeHelper.GetStartOrEnd(dateTime);  //今天开始时间    13位
+            var (lastDayS, lastdayE) = TimeHelper.GetStartOrEnd(DateTimeOffset.Parse($"{dateTime.Year}-{dateTime.Month}-{dateTime.Day - 1}"));   //昨天开始时间
+
+            List<RecLesn> scRecLesn = new(); //学校课例
+            List<RecLesn> tchRecLesn = new(); //个人课例
+            List<RecLesn> allRecLesn = new();  //昨天所有课例
+            string lesnSql = $"select c.id,c.name,c.code,c.school,c.scope,c.startTime  from c where c.pk='LessonRecord' and c.startTime >={daySt} and c.startTime <= {dayEt}";
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<RecLesn>(queryText: lesnSql, requestOptions: new QueryRequestOptions() { }))
+            {
+                scRecLesn.Add(item);
+            }
+
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIterator<RecLesn>(queryText: lesnSql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("LessonRecord") }))
+            {
+                tchRecLesn.Add(item);
+            }
+
+            string allLesnSql = $"select c.id,c.name,c.code,c.school,c.scope,c.startTime  from c where c.pk='LessonRecord' and c.startTime >={lastDayS} and c.startTime <= {lastdayE}";
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<RecLesn>(queryText: allLesnSql, requestOptions: new QueryRequestOptions() { }))
+            {
+                allRecLesn.Add(item);
+            }
+
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIterator<RecLesn>(queryText: allLesnSql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("LessonRecord") }))
+            {
+                allRecLesn.Add(item);
+            }
+
             for (int i = 0; i < 24; i++)
             {
                 if (hour >= i)
                 {
                     DateTimeOffset timeHour = new DateTime(year, month, day, i, 0, 0);
                     var (hourS, hourE) = TimeHelper.GetStartOrEnd(timeHour, type: "hour");
-                    await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<int>(queryText: $"select value(count(c.id)) from c where c.pk='LessonRecord' and c.startTime >={hourS} and c.startTime <= {hourE}", requestOptions: new QueryRequestOptions() { }))
-                    {
-                        scLessCnt.Add(i, item);
-                    }
 
-                    await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIterator<int>(queryText: $"select value(count(c.id)) from c where c.startTime >={hourS} and c.startTime <= {hourE}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("LessonRecord") }))
-                    {
-                        tchLessCnt.Add(i, item);
-                    }
+                    var scLesn = scRecLesn.Where(item => item.startTime >= hourS && item.startTime <= hourE).ToList();
+                    scLessCnt.Add(i, scLesn.Count());
+
+                    var tchLesn = tchRecLesn.Where(item => item.startTime >= hourS && item.startTime <= hourE).ToList();
+                    tchLessCnt.Add(i, tchLesn.Count());
                 }
 
                 DateTimeOffset yesterday = new DateTime(year, month, day - 1, i, 0, 0);
                 var (yHourS, yHourE) = TimeHelper.GetStartOrEnd(yesterday, type: "hour");
-                string sql = $"select value(count(c.id)) from c where c.pk='LessonRecord' and c.startTime >= {yHourS} and c.startTime <= {yHourE}";
-                int hourLessCnt = await CommonFind.GetSqlValueCount(cosmosClient, new List<string> { "School", "Teacher" }, sql);
-                yesterdayCnt.Add(i, hourLessCnt);
+
+                var allLesn = allRecLesn.Where(item => item.startTime >= yHourS && item.startTime <= yHourE).ToList();
+                yesterdayCnt.Add(i, allLesn.Count());
             }
 
+            ////通过循环实时查询课例统计
+            //for (int i = 0; i < 24; i++)
+            //{
+            //    if (hour >= i)
+            //    {
+            //        DateTimeOffset timeHour = new DateTime(year, month, day, i, 0, 0);
+            //        var (hourS, hourE) = TimeHelper.GetStartOrEnd(timeHour, type: "hour");
+            //        await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<int>(queryText: $"select value(count(c.id)) from c where c.pk='LessonRecord' and c.startTime >={hourS} and c.startTime <= {hourE}", requestOptions: new QueryRequestOptions() { }))
+            //        {
+            //            scLessCnt.Add(i, item);
+            //        }
+
+            //        await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIterator<int>(queryText: $"select value(count(c.id)) from c where c.startTime >={hourS} and c.startTime <= {hourE}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("LessonRecord") }))
+            //        {
+            //            tchLessCnt.Add(i, item);
+            //        }
+            //    }
+
+            //    DateTimeOffset yesterday = new DateTime(year, month, day - 1, i, 0, 0);
+            //    var (yHourS, yHourE) = TimeHelper.GetStartOrEnd(yesterday, type: "hour");
+            //    string sql = $"select value(count(c.id)) from c where c.pk='LessonRecord' and c.startTime >= {yHourS} and c.startTime <= {yHourE}";
+            //    int hourLessCnt = await CommonFind.GetSqlValueCount(cosmosClient, new List<string> { "School", "Teacher" }, sql);
+            //    yesterdayCnt.Add(i, hourLessCnt);
+            //}
+
             return Ok(new { state = 200, scLessCnt = scLessCnt.ToList(), tchLessCnt = tchLessCnt.ToList(), yesterdayCnt = yesterdayCnt.ToList() });
         }
 
@@ -274,6 +330,10 @@ namespace TEAMModelBI.Controllers.BIHome
         public async Task<IActionResult> GetEdition() 
         {
             var cosmosClient = _azureCosmos.GetCosmosClient();
+            int beCnt = 0; //基础班
+            int seCnt = 0; //标准版
+            int peCnt = 0; //专业版
+
             List<RecScEd> scEdCnt = new();
             var ScSql = $"select c.id,c.name,c.size,c.scale from c";
             await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<RecScEd>(queryText: ScSql, requestOptions:new QueryRequestOptions() { PartitionKey= new PartitionKey("Base")}))
@@ -281,22 +341,37 @@ namespace TEAMModelBI.Controllers.BIHome
                 scEdCnt.Add(item);
             }
 
-            scEdCnt.ForEach(async scProductCnt =>
+            scEdCnt.ForEach(async scProCnt =>
             {
-                var response = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(scProductCnt.id, new PartitionKey("ProductSum"));
+                var response = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(scProCnt.id, new PartitionKey("ProductSum"));
                 if (response.Status == 200)
                 {
                     using var json = await JsonDocument.ParseAsync(response.ContentStream);
                     SchoolProductSum ScProductSum = json.ToObject<SchoolProductSum>();
-                    scProductCnt.serial = ScProductSum.serial.Count();
-                    scProductCnt.service = ScProductSum.service.Count();
-                    scProductCnt.hard = ScProductSum.hard.Count();
+                    //scProCnt.serial = ScProductSum.serial.Count();
+                    //scProCnt.service = ScProductSum.service.Count();
+                    //scProCnt.hard = ScProductSum.hard.Count();
+                    int pSeriCnt = ScProductSum.serial.Count();
+                    int pServCnt = ScProductSum.service.Count();
+                    int pHardCnt = ScProductSum.hard.Count();
+                    scProCnt.serial = pSeriCnt;
+                    scProCnt.service = pServCnt;
+                    scProCnt.hard = pHardCnt;
+                    if (scProCnt.scale >= 500 && (pSeriCnt > 0 || pServCnt > 0 || pHardCnt > 0)) peCnt += 1;
                 }
+
+                if (scProCnt.scale >= 500 && scProCnt.serial == 0 && scProCnt.service == 0 && scProCnt.hard == 0) seCnt += 1;
+                if (scProCnt.scale == 0) beCnt += 1;
+
+
             });
 
-            return Ok(new { state = 200, scEdCnt });
+            return Ok(new { state = 200, beCnt, seCnt, peCnt, scEdCnt });
         }
 
+        /// <summary>
+        /// 记录在线人数
+        /// </summary>
         public record RecOnLine
         {
             public string id { get; set; }
@@ -305,7 +380,10 @@ namespace TEAMModelBI.Controllers.BIHome
             public List<Teacher.LoginInfo> loginInfos { get; set; }
         }
 
-        public class RecScEd
+        /// <summary>
+        /// 记录学校版本信息
+        /// </summary>
+        public record RecScEd
         {
             public string id { get; set; }
             public string name { get; set; }
@@ -316,6 +394,17 @@ namespace TEAMModelBI.Controllers.BIHome
             public int hard { get; set; } = 0; //硬体
         }
 
-
+        /// <summary>
+        /// 记录课例
+        /// </summary>
+        public record RecLesn
+        {
+            public string id { get; set; }
+            public string name { get; set; }
+            public string code { get; set; }
+            public string school { get; set; }
+            public string scope{get;set;}
+            public long startTime { get; set; }
+        }
     }
 }

+ 4 - 4
TEAMModelBI/Controllers/BINormal/AppCompanyController.cs

@@ -106,7 +106,7 @@ namespace TEAMModelBI.Controllers.BINormal
         /// <param name="appCompany"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist,company")]
+        [AuthToken(Roles = "admin,assist,company")]
         [HttpPost("set-info")]
         public async Task<IActionResult> SetAppInfo(AppCompany appCompany)
         {
@@ -214,7 +214,7 @@ namespace TEAMModelBI.Controllers.BINormal
         /// </summary>
         /// <param name="jsonElement"></param>
         /// <returns></returns>
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("get-apply")]
         public async Task<IActionResult> SetAuditApp(JsonElement jsonElement)
         {
@@ -282,7 +282,7 @@ namespace TEAMModelBI.Controllers.BINormal
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist,company")]
+        [AuthToken(Roles = "admin,assist,company")]
         [HttpPost("set-applyapi")]
         public async Task<IActionResult> SetApplyApi(JsonElement jsonElement)
         {
@@ -409,7 +409,7 @@ namespace TEAMModelBI.Controllers.BINormal
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist,company")]
+        [AuthToken(Roles = "admin,assist,company")]
         [HttpPost("set-applyschool")]
         public async Task<IActionResult> SetAuditSchool(JsonElement jsonElement)
         {

+ 2 - 2
TEAMModelBI/Controllers/BINormal/BIOpenApiController.cs

@@ -39,7 +39,7 @@ namespace TEAMModelBI.Controllers.BINormal
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("get-infos")]
         public async Task<IActionResult> GetOpenApi(JsonElement jsonElement) 
         {
@@ -87,7 +87,7 @@ namespace TEAMModelBI.Controllers.BINormal
         /// <param name="openApi"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("set-api")]
         public async Task<IActionResult> SetOpenApi(BIOpenApi  openApi) 
         {

+ 2 - 2
TEAMModelBI/Controllers/BINormal/CompanyController.cs

@@ -44,7 +44,7 @@ namespace TEAMModelBI.Controllers.BINormal
         /// <param name="jsonElenent"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist,company")]
+        [AuthToken(Roles = "admin,assist,company")]
         [HttpPost("get-info")]
         public async Task<IActionResult> GetInfo(JsonElement jsonElenent)
         {
@@ -89,7 +89,7 @@ namespace TEAMModelBI.Controllers.BINormal
         /// <param name="appCompany"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist,company")]
+        [AuthToken(Roles = "admin,assist,company")]
         [HttpPost("set-info")]
         public async Task<IActionResult> SetCompany(Company company)
         {

+ 1 - 1
TEAMModelBI/Controllers/BISchool/AreaRelevantController.cs

@@ -89,7 +89,7 @@ namespace TEAMModelBI.Controllers.BISchool
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("set-areashiftschool")]
         public async Task<IActionResult> SetAreaShiftSchool(JsonElement jsonElement)
         {

+ 2 - 2
TEAMModelBI/Controllers/BISchool/BatchAreaController.cs

@@ -145,7 +145,7 @@ namespace TEAMModelBI.Controllers.BISchool
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("batch-area")]
         public async Task<IActionResult> batchArea(JsonElement jsonElement)
         {
@@ -476,7 +476,7 @@ namespace TEAMModelBI.Controllers.BISchool
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("cut-standard")]
         public async Task<IActionResult> CutStandard(JsonElement jsonElement)
         {

+ 2 - 2
TEAMModelBI/Controllers/BISchool/BatchSchoolController.cs

@@ -125,7 +125,7 @@ namespace TEAMModelBI.Controllers.BISchool
         /// <param name="school"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("batch-school")]
         public async Task<IActionResult> BatchCreateSchool(FoundSchools foundSchools)
         {
@@ -543,7 +543,7 @@ namespace TEAMModelBI.Controllers.BISchool
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist,admin")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("upd-schoolassist")]
         public async Task<IActionResult> UpdSchoolAssist(JsonElement jsonElement)
         {

+ 1 - 1
TEAMModelBI/Controllers/BISchool/ProductController.cs

@@ -54,7 +54,7 @@ namespace TEAMModelBI.Controllers.BISchool
             SchoolProductSum productSum = new SchoolProductSum(); //产品状态
             List<SchoolProductSumProdInfo> prodinfo = new List<SchoolProductSumProdInfo>(); //学校的产品信息
             List<SchoolProductSumData> serialRecord = new List<SchoolProductSumData>(); //软体购买记录
-            List<SchoolProductSumData> serviceRecord = new List<SchoolProductSumData>(); //服务购买记录
+            List<SchoolProductSumDataService> serviceRecord = new List<SchoolProductSumDataService>(); //服务购买记录
             List<SchoolProductSumDataHard> hardRecord = new List<SchoolProductSumDataHard>(); //硬体购买记录
 
 

+ 1 - 1
TEAMModelBI/Controllers/BISchool/RoomController.cs

@@ -43,7 +43,7 @@ namespace TEAMModelBI.Controllers.BISchool
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("set-bind")]
         public async Task<IActionResult> SetBindProduct(JsonElement jsonElement) 
         {

+ 3 - 3
TEAMModelBI/Controllers/BISchool/SchoolController.cs

@@ -103,7 +103,7 @@ namespace TEAMModelBI.Controllers.BISchool
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("set-schooljoinarea")]
         public async Task<IActionResult> SetSchoolJoinArea(JsonElement jsonElement)
         {
@@ -232,7 +232,7 @@ namespace TEAMModelBI.Controllers.BISchool
         /// <param name="school"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist,admin")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("upd-school")]
         public async Task<IActionResult> UpdSchool(School school)
         {
@@ -481,7 +481,7 @@ namespace TEAMModelBI.Controllers.BISchool
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "admin")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("set-aistschool")]
         public async Task<IActionResult> SetAssistSchool(JsonElement jsonElement)
         {

+ 1 - 1
TEAMModelBI/Controllers/BIServer/BiServersController.cs

@@ -52,7 +52,7 @@ namespace TEAMModelBI.Controllers.BIServer
         /// </summary>
         /// <param name="request"></param>
         /// <returns></returns>
-        [AuthToken(Roles = "admin")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("get-coreinfo")]
         public async Task<IActionResult> GetCoreInfo(JsonElement jsonElement)
         {

+ 1 - 1
TEAMModelBI/Controllers/Core/BlobController.cs

@@ -50,7 +50,7 @@ namespace TEAMModelBI.Controllers.Core
         /// <param name="file"></param>
         /// <returns></returns>
         [HttpPost("upload-public")]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "admin,assist")]
         [RequestSizeLimit(102_400_000_00)]//最大10000m左右
         public async Task<IActionResult> UploadPublic([FromForm] IFormFile file)
         {

+ 3 - 3
TEAMModelBI/Controllers/DingDingStruc/TableDingDingInfoController.cs

@@ -316,7 +316,7 @@ namespace TEAMModelBI.Controllers.DingDingStruc
         /// </summary>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("set-ddinductionuser")]
         public async Task<IActionResult> SetDingDingInductionUser(JsonElement jsonElement)
         {
@@ -391,7 +391,7 @@ namespace TEAMModelBI.Controllers.DingDingStruc
         /// </summary>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("del-ddquituser")]
         public async Task<IActionResult> DeleteDDQuitUser(JsonElement jsonElement)
         {
@@ -668,7 +668,7 @@ namespace TEAMModelBI.Controllers.DingDingStruc
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "admin,assist")]
         [HttpPost("set-backenbind")]
         public async Task<IActionResult> SetBackenBind(JsonElement jsonElement) 
         {

+ 21 - 17
TEAMModelOS.FunctionV4/ServiceBus/ActiveTaskTopic.cs

@@ -602,7 +602,8 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
             }
         }
 
-        //更新產品一覽表
+        //更新學校產品一覽表
+        //處理內容:取得所有序號購買紀錄,服務週期、硬體購買紀錄後,更新ProductSum
         [Function("Product")]
         public async Task ProductFunc([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "product", Connection = "Azure:ServiceBus:ConnectionString")] string msg, ILogger log)
         {
@@ -648,37 +649,40 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                     }
                 }
                 ////服務
-                List<SchoolProductSumData> servicesProductSumOrg = new List<SchoolProductSumData>();
-                long timestampToday = DateTimeOffset.UtcNow.AddSeconds(1).ToUnixTimeSeconds(); //比現實時間延遲1秒
-                strQuery = $"SELECT * FROM c WHERE c.dataType = 'service' AND c.startDate <= {timestampToday} AND {timestampToday} <= c.endDate AND c.ttl < 0"; //在授權期間、ttl < 0 才取
-                await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: strQuery, requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Product-{schoolId}") }))
+                List<SchoolProductSumDataService> servicesProductSumOrg = new List<SchoolProductSumDataService>();
+                //////取得學校產品 授權週期:授權中 條件: startDate <= now <= endDate
+                long timestampToday = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
+                string strQueryM = $"SELECT * FROM c WHERE c.dataType = 'servicePeriod' AND c.startDate <= {timestampToday} AND {timestampToday} <= c.endDate AND c.ttl < 0";
+                await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: strQueryM, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Product-{schoolId}") }))
                 {
                     using var json = await JsonDocument.ParseAsync(item.ContentStream);
                     if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
                     {
                         foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
                         {
-                            SchoolProductService serviceInfo = obj.ToObject<SchoolProductService>();
-                            SchoolProductSumData serviceProd = servicesProductSumOrg.Where(sp => sp.prodCode == serviceInfo.prodCode).FirstOrDefault();
+                            SchoolProductServicePeriod servicePeriodInfo = obj.ToObject<SchoolProductServicePeriod>();
+                            SchoolProductSumDataService serviceProd = servicesProductSumOrg.Where(sp => sp.prodCode == servicePeriodInfo.prodCode).FirstOrDefault();
                             if (serviceProd == null)
                             {
-                                SchoolProductSumData serviceProdAdd = new SchoolProductSumData();
-                                serviceProdAdd.prodCode = serviceInfo.prodCode;
+                                SchoolProductSumDataService serviceProdAdd = new SchoolProductSumDataService();
+                                serviceProdAdd.prodCode = servicePeriodInfo.prodCode;
                                 serviceProdAdd.avaliable = 0;
-                                serviceProdAdd.ids.Add(serviceInfo.id);
-                                serviceProdAdd.avaliable += serviceInfo.number;
+                                serviceProdAdd.ids.Add(servicePeriodInfo.id);
+                                serviceProdAdd.avaliable += servicePeriodInfo.number;
+                                serviceProdAdd.startDate = servicePeriodInfo.startDate;
+                                serviceProdAdd.endDate = servicePeriodInfo.endDate;
                                 servicesProductSumOrg.Add(serviceProdAdd);
                             }
                             else
                             {
-                                if (!serviceProd.ids.Contains(serviceInfo.id))
-                                {
-                                    serviceProd.ids.Add(serviceInfo.id);
-                                    serviceProd.avaliable += serviceInfo.number;
-                                }
+                                serviceProd.ids = new List<string>();
+                                serviceProd.ids.Add(servicePeriodInfo.id);
+                                serviceProd.startDate = servicePeriodInfo.startDate;
+                                serviceProd.endDate = servicePeriodInfo.endDate;
                             }
                         }
                     }
+                    
                 }
                 ////服務產品特別對應項
                 School school = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>($"{schoolId}", new PartitionKey("Base")); //學校基本資料取得
@@ -689,7 +693,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                 {
                     if (servicesProductSumOrg.Count > 0)
                     {
-                        foreach (SchoolProductSumData servicesProductSumOrgRow in servicesProductSumOrg)
+                        foreach (SchoolProductSumDataService servicesProductSumOrgRow in servicesProductSumOrg)
                         {
                             //更新學校空間
                             if (servicesProductSumOrgRow.prodCode.Equals("IPALJ6NY"))

+ 13 - 3
TEAMModelOS.SDK/Models/Cosmos/School/SchoolProduct.cs

@@ -25,6 +25,7 @@ namespace TEAMModelOS.SDK.Models
         public string serial { get; set; }
         public List<deviceBound> deviceBound { get; set; }
         public int clientQty { get; set; }
+        public long orderDate { get; set; }
         public long regDate { get; set; }
         public long startDate { get; set; }
         public long endDate { get; set; }
@@ -36,6 +37,7 @@ namespace TEAMModelOS.SDK.Models
     //產品購買紀錄-服務
     public class SchoolProductService : SchoolProductCommon
     {
+        public long orderDate { get; set; }
         public long startDate { get; set; }
         public long endDate { get; set; }
         public int number { get; set; }
@@ -44,6 +46,7 @@ namespace TEAMModelOS.SDK.Models
     //產品購買紀錄-硬體
     public class SchoolProductHard : SchoolProductCommon
     {
+        public long orderDate { get; set; }
         public string model { get; set; }
         public string serial { get; set; }
     }
@@ -56,7 +59,7 @@ namespace TEAMModelOS.SDK.Models
             code = "ProductSum";
             prodinfo = new List<SchoolProductSumProdInfo>();
             serial = new List<SchoolProductSumData>();
-            service = new List<SchoolProductSumData>();
+            service = new List<SchoolProductSumDataService>();
             hard = new List<SchoolProductSumDataHard>();
         }
         public string pk { get; set; }
@@ -64,7 +67,7 @@ namespace TEAMModelOS.SDK.Models
         public string id { get; set; }
         public List<SchoolProductSumProdInfo> prodinfo { get; set; }
         public List<SchoolProductSumData> serial { get; set; }
-        public List<SchoolProductSumData> service { get; set; }
+        public List<SchoolProductSumDataService> service { get; set; }
         public List<SchoolProductSumDataHard> hard { get; set; }
         //public Aclassone aclassone { get; set; } //是否獨立出來,未定
     }
@@ -83,6 +86,7 @@ namespace TEAMModelOS.SDK.Models
         space = 2
     }
 
+    //產品一覽 產品內容 基本項
     public class SchoolProductSumData
     {
         public SchoolProductSumData()
@@ -92,10 +96,14 @@ namespace TEAMModelOS.SDK.Models
         public string prodCode { get; set; }
         public List<string> ids { get; set; }
         public int avaliable { get; set; }
+    }
+    //產品一覽 產品內容 服務
+    public class SchoolProductSumDataService : SchoolProductSumData
+    {
         public long startDate { get; set; }
         public long endDate { get; set; }
     }
-
+    //產品一覽 產品內容 硬體
     public class SchoolProductSumDataHard : SchoolProductSumData
     {
         public string model { get; set; }
@@ -114,6 +122,7 @@ namespace TEAMModelOS.SDK.Models
     public class SchoolProductOrderList
     {
         public string id { get; set; }
+        public long orderDate { get; set; }
         public long startDate { get; set; }
         public long endDate { get; set; }
         public int number { get; set; }
@@ -129,6 +138,7 @@ namespace TEAMModelOS.SDK.Models
     //服務授權期限(主週期資訊) ※code='Product' code='Product-{SchoolID}' dataType='servicePeriod'
     public class SchoolProductServicePeriod : SchoolProductCommon
     {
+        public int type { get; set; } //授權週期類型 0:銷售 1:試用
         public long startDate { get; set; }
         public long endDate { get; set; }
         public int number { get; set; }

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

@@ -27,7 +27,7 @@
     <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.3" />
     <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
     <PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="6.0.3" />
-    <PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="6.0.4" />
+    <PackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="6.0.5" />
     <PackageReference Include="Microsoft.Identity.Client" Version="4.39.0" />
     <PackageReference Include="MSTest.TestFramework" Version="2.2.8" />
     <PackageReference Include="NUnit" Version="3.13.2" />

+ 26 - 20
TEAMModelOS/ClientApp/public/lang/en-US.js

@@ -580,7 +580,7 @@ const LANG_EN_US = {
             studentTableC8: 'Type',
             studentTableC9: 'IRS Number',
             studentTableC10: 'Admin Class',
-            studentTableC11: '昵稱',
+            studentTableC11: 'Nickname',
             addClassroomProp1: 'Select Default classroom',
             addClassroomProp2: 'New personal classroom',
             chooseClassroom: 'Please select system classroom',
@@ -608,7 +608,6 @@ const LANG_EN_US = {
     },
     // 课程管理
     cusMgt: {
-        // ManageCourse.vue
         searchHolder: 'Search',
         delBatch: 'Batch delete',
         delCus: 'Delete Course',
@@ -1107,6 +1106,9 @@ const LANG_EN_US = {
         paperTagPlace: 'Manual input to create new tabs',
         useTip: 'Please keep at least one use of the test question',
         editor: {
+            canvasTip:'空白畫闆',
+            canvasStemTip:'帶題幹畫闆',
+            addTextDot:'添加著重號',
             imagePasteTip: 'You can open the TM Teaching Assistant to get a better picture/text paste experience.',
             uploadVideo: 'Upload Local Video',
             uploadAudio: 'Upload Local Audio',
@@ -3728,6 +3730,16 @@ const LANG_EN_US = {
             vote: 'Poll',
             survey: 'Survey',
             homework: 'Homework',
+            title: {
+                classRecord: "老師發佈的課堂記錄",
+                activity: "老師發佈的活動",
+                notice1: "班級公告",
+                notice2: "課程公告",
+                exam: "評測成績分析",
+                homework: "作業評分",
+                classScore: "課例記分",
+            },
+            toLook: "現在去看",
         },
         public: {
             noData: 'No data yet',
@@ -3750,6 +3762,7 @@ const LANG_EN_US = {
             hour: "H",
             minute: "M",
             mustFile: "Must Upload",
+            uploadTime: "上傳時間",
         },
         type: {
             home: "Home",
@@ -4201,6 +4214,7 @@ const LANG_EN_US = {
                 againPractice: "Practice Again",
                 finish: 'Completed',
                 showAns: 'Show the answer',
+                showPage: "查看題號",
                 hideAns: 'Hide the answer',
                 hint: 'Key Hint',
                 previous: 'Previous Question',
@@ -4235,6 +4249,7 @@ const LANG_EN_US = {
                 correction: 'Correct Answer',
                 queNo: 'Question:',
                 myAns: 'My Answer:',
+                reAns:'New Answer:',
                 noExam: "No question yet",
                 wrongSub: "Incorrect",
                 rightSub: "Correct",
@@ -4543,6 +4558,9 @@ const LANG_EN_US = {
             wrongNum: "No. of Incorrect",
         },
         myAchievement: {
+            examAch: "評量成績",
+            hwAch: "作業成績",
+            cusAch: "老師給分",
             examMode: "Assessment Mode",
             examIn: "Source",
             name: "Name",
@@ -5117,8 +5135,8 @@ const LANG_EN_US = {
         props6: 'File uploaded successfully!',
         props7: 'Not enough storage space!',
         uploadText: 'Tap or drag to upload',
-
         resTips: 'HiTeach generates teaching materials that only support HTEX format previews',
+        space: '可用空間:',
         calcing: 'Calculating...',
         blobFull: '(Full)',
         otherType: 'Other types',
@@ -5229,19 +5247,18 @@ const LANG_EN_US = {
         },
         authority: {
             'classroom': 'Classroom Settings',
-            'system': 'School Basic Data Management',
-            'studentAccount': 'Student Account Management',
-            'teachermgmt': 'Teacher Account Management',
             'classroom-read': 'View Classroom Information',
             'classroom-upd': 'Change Classroom Settings',
+            'schoolSetting': 'School Basic Data Management',
             'schoolSetting-read': 'View School Basic Settings',
             'schoolSetting-upd': 'Change School Basic Settings',
+            'student': 'Student Account Management',
             'student-read': 'View Student Account',
             'student-upd': 'Change Student Account',
-            'teacher-checkTeacher': 'Check if the teacher can view',
+            'teacher': 'Teacher Account Management',
             'teacher-read': 'View Teacher Account',
             'teacher-upd': 'Change Teacher Account',
-            'manageCourse': ' Course Setting Management',
+            'course': ' Course Setting Management',
             'course-read': 'View Course Setting Information',
             'course-upd': 'Modify Course Setting Information',
             'newCoursePlan': 'Course Scheduling Management',
@@ -5433,6 +5450,7 @@ const LANG_EN_US = {
         importTips7: '5. note: Note about the teacher, optional',
         importTips8: "Tip: If the imported teacher's user ID, cell phone, or email has already registered a TEAM Model account, the system will automatically invite the teacher to join the school.",
         importTips9: 'Select or drag files to this area to import',
+        impText: '無效數據(沒有名字):',
         impText1: 'No. of imported:',
         impText2: 'No. of only name is imported:',
         impText3: 'No. of auto-invite by system:',
@@ -5594,7 +5612,6 @@ const LANG_EN_US = {
         ach_text10: 'Currently Class',
         ach_text11: 'Total Number',
         ach_text12: 'Back',
-
         ach_table_text1: 'Grade Ranking',
         ach_table_text2: 'Total Score',
         ach_table_text3: 'Average Score',
@@ -5611,7 +5628,6 @@ const LANG_EN_US = {
         ach_table_text14: 'Progressive Class',
         ach_table_text15: 'Regressive Class',
         ach_table_text16: 'Stable Class',
-
         ach_chart_text1: 'Sort by',
         ach_chart_text2: 'Default sort',
         ach_chart_text3: 'Score from high to low',
@@ -5644,7 +5660,6 @@ const LANG_EN_US = {
         ta_title2: 'Grade Single-question Scoring Rate Statistics',
         ta_title3: 'Question Analysis Summary Table',
         ta_title4: 'Question Scoring Rate Table',
-
         ta_text1: 'Area',
         ta_text2: 'Area Characteristics',
         ta_text3: 'Number Of Question',
@@ -5654,7 +5669,6 @@ const LANG_EN_US = {
         ta_text7: 'The test questions may contain abnormal components and need partial correction',
         ta_text8: 'The difficulty of test questions is high, which is suitable for distinguishing students with high achievement',
         ta_text9: 'The test questions contain abnormal components, which may be wrong in data or unclear in meaning, and must be modified',
-
         ta_chart_text1: 'Question',
         ta_chart_text2: 'Correctly Answered Rate',
         ta_chart_text3: 'Stability',
@@ -5663,7 +5677,6 @@ const LANG_EN_US = {
         ta_chart_text6: 'District Scoring Rate',
         ta_chart_text7: 'Maximum Value',
         ta_chart_text8: 'Minimum Value',
-
         ta_table_text1: 'No.',
         ta_table_text2: 'Type',
         ta_table_text3: 'Key Concept',
@@ -5675,7 +5688,6 @@ const LANG_EN_US = {
         ta_table_text9: 'District',
         ta_table_text10: 'High Group',
         ta_table_text11: 'Low Group',
-
         ta_table_tip1: '* Unit: Scoring Rate',
         ta_table_tip2: '* R1-R6: Ranking Range',
 
@@ -5687,22 +5699,18 @@ const LANG_EN_US = {
         ka_title5: 'Key Concept Scoring Details',
         ka_title6: 'Incorrectly Answered Question Rate Relation Table',
         ka_title7: 'Key Concept Scoring Rate Distribution Chart',
-
         ka_text1: 'Current key concept set',
         ka_text2: 'Current key concept',
         ka_text3: 'Current cognitive level',
         ka_text4: 'Cognitive Level Ratio Status',
-
         ka_chart_text1: 'Switch to histogram',
         ka_chart_text2: 'Switch to radar',
-
         ka_radar_text1: 'Remember',
         ka_radar_text2: 'Apply',
         ka_radar_text3: 'Analyze',
         ka_radar_text4: 'Create',
         ka_radar_text5: 'Evaluate',
         ka_radar_text6: 'Understand',
-
         ka_table_text1: 'Key Concept',
         ka_table_text2: 'Cognitive Level',
         ka_table_text3: 'Grade Scoring Rate',
@@ -5714,7 +5722,6 @@ const LANG_EN_US = {
         ka_table_text9: 'Incorrectly answered question person-time',
         ka_table_text10: 'PH Incorrectly answered question person-time',
         ka_table_text11: 'PL Incorrectly answered question person-time',
-
         ka_tip1: '* PH: High Group / LL: Low Group',
 
         // LevelAnalysis.vue
@@ -5742,7 +5749,6 @@ const LANG_EN_US = {
         ql_text13: 'Return',
         ql_text14: 'Connecting Questions',
         ql_text15: 'Matching Question',
-
         baseExport: {
             title: 'Select the form to export',
             checkAll: 'Select All',

+ 20 - 15
TEAMModelOS/ClientApp/public/lang/zh-CN.js

@@ -1102,11 +1102,13 @@ const LANG_ZH_CN = {
         orderTip2: '(例:Ⅰ、Ⅱ、Ⅲ、Ⅳ、Ⅴ......)',
         order3: '中文小写',
         orderTip3: '例:一、二、三、四、五......',
-
         paperTag: '试卷标签',
         paperTagPlace: '选择标签或者手动输入后回车创建新标签',
         useTip: '请至少保留一种试题用途',
         editor: {
+            canvasTip:'空白画板',
+            canvasStemTip:'带题干画板',
+            addTextDot:'添加着重号',
             imagePasteTip: '检测到您粘贴的内容包含图片信息,为了更好的使用体验,建议您开启醍摩豆阅卷助手',
             uploadVideo: '上传本地视频',
             uploadAudio: '上传本地音频',
@@ -1332,7 +1334,6 @@ const LANG_ZH_CN = {
             warningTips5: '抱歉,文档解析失败!',
             tips6: '操作失败,请先完善当前学段的科目信息!',
             tips7: '操作失败,请先完善当前学段的年级信息!'
-
         },
         createPaper: {
             setAutoConditions: '设置自动出题条件',
@@ -3656,6 +3657,7 @@ const LANG_ZH_CN = {
         addErr: '添加失败',
         updErr: '修改失败',
         sltClassTips: '请设置班级',
+        setNoErr: '学生座位号不能为空',
         classErr: '请设置学生所在班级',
         pwErr: '密码不能为空',
         nameErr: '学生姓名不能为空',
@@ -3728,6 +3730,16 @@ const LANG_ZH_CN = {
             vote: '投票',
             survey: '问卷',
             homework: '作业',
+            title: {
+                classRecord: "老师发布的课堂记录",
+                activity: "老师发布的活动",
+                notice1: "班级公告",
+                notice2: "课程公告",
+                exam: "评测成绩分析",
+                homework: "作业评分",
+                classScore: "课例记分",
+            },
+            toLook: "现在去看",
         },
         public: {
             noData: '暂无数据',
@@ -3750,6 +3762,7 @@ const LANG_ZH_CN = {
             hour: "时",
             minute: "分",
             mustFile: "必须上传作业",
+            uploadTime: "上传时间",
         },
         type: {
             home: "首页",
@@ -4201,6 +4214,7 @@ const LANG_ZH_CN = {
                 againPractice: "再次练习",
                 finish: '已完成',
                 showAns: '显示答案',
+                showPage: "查看题号",
                 hideAns: '隐藏答案',
                 hint: '重点提示',
                 previous: '上一题',
@@ -4235,6 +4249,7 @@ const LANG_ZH_CN = {
                 correction: '正解',
                 queNo: '题目:',
                 myAns: '我的作答:',
+                reAns:'重新作答:',
                 noExam: "暂没有试题",
                 wrongSub: "错题",
                 rightSub: "正确题目",
@@ -4543,6 +4558,9 @@ const LANG_ZH_CN = {
             wrongNum: "错误次数",
         },
         myAchievement: {
+            examAch: "评测成绩",
+            hwAch: "作业成绩",
+            cusAch: "老师给分",
             examMode: "评测模式",
             examIn: "发布来源",
             name: "名称",
@@ -5117,7 +5135,6 @@ const LANG_ZH_CN = {
         props6: '文件上传成功!',
         props7: '存储空间不足!',
         uploadText: '点击或者拖拽上传',
-
         resTips: 'HiTeach生成的课件,只支持HTEX格式的教材预览',
         space: '可用空间:',
         calcing: '计算中...',
@@ -5595,7 +5612,6 @@ const LANG_ZH_CN = {
         ach_text10: '当前班级',
         ach_text11: '总人数',
         ach_text12: '返回',
-
         ach_table_text1: '年级排名',
         ach_table_text2: '总分',
         ach_table_text3: '平均分',
@@ -5612,7 +5628,6 @@ const LANG_ZH_CN = {
         ach_table_text14: '进步班级',
         ach_table_text15: '退步班级',
         ach_table_text16: '稳定班级',
-
         ach_chart_text1: '排序方式',
         ach_chart_text2: '默认排序',
         ach_chart_text3: '分数从高到低',
@@ -5645,7 +5660,6 @@ const LANG_ZH_CN = {
         ta_title2: '年级单题得分率统计',
         ta_title3: '试题分析总表',
         ta_title4: '试题得分率表',
-
         ta_text1: '区域',
         ta_text2: '区域特性',
         ta_text3: '题数',
@@ -5655,7 +5669,6 @@ const LANG_ZH_CN = {
         ta_text7: '试题可能含有异质成分在内,需要局部修正',
         ta_text8: '试题困难度较高,适合区分高成就者学生',
         ta_text9: '试题含有异质成分,可能数据错误或者题意不清,必须加以修改',
-
         ta_chart_text1: '试题',
         ta_chart_text2: '答对率',
         ta_chart_text3: '稳定度',
@@ -5664,7 +5677,6 @@ const LANG_ZH_CN = {
         ta_chart_text6: '区级得分率',
         ta_chart_text7: '最大值',
         ta_chart_text8: '最小值',
-
         ta_table_text1: '题号',
         ta_table_text2: '题型',
         ta_table_text3: '知识点',
@@ -5676,7 +5688,6 @@ const LANG_ZH_CN = {
         ta_table_text9: '区级',
         ta_table_text10: '高分组',
         ta_table_text11: '低分组',
-
         ta_table_tip1: '* 单位:得分率',
         ta_table_tip2: '* R1-R6:排名区间',
 
@@ -5688,22 +5699,18 @@ const LANG_ZH_CN = {
         ka_title5: '知识点得分详情',
         ka_title6: '错题率关系表',
         ka_title7: '知识点得分率分布图',
-
         ka_text1: '当前知识块',
         ka_text2: '当前知识点',
         ka_text3: '当前认知层次',
         ka_text4: '认知层次占比情况',
-
         ka_chart_text1: '切换柱状图',
         ka_chart_text2: '切换雷达图',
-
         ka_radar_text1: '知识',
         ka_radar_text2: '应用',
         ka_radar_text3: '分析',
         ka_radar_text4: '综合',
         ka_radar_text5: '评鉴',
         ka_radar_text6: '理解',
-
         ka_table_text1: '知识点',
         ka_table_text2: '认知层次',
         ka_table_text3: '年级得分率',
@@ -5715,7 +5722,6 @@ const LANG_ZH_CN = {
         ka_table_text9: '错题人次',
         ka_table_text10: 'PH错题人次',
         ka_table_text11: 'PL错题人次',
-
         ka_tip1: '* PH:高分区段 / PL:低分区段',
 
         // LevelAnalysis.vue
@@ -5743,7 +5749,6 @@ const LANG_ZH_CN = {
         ql_text13: '返回',
         ql_text14: '连线题',
         ql_text15: '改错题',
-
         baseExport: {
             title: '选择要导出的表格',
             checkAll: '全选',

+ 21 - 18
TEAMModelOS/ClientApp/public/lang/zh-TW.js

@@ -580,7 +580,7 @@ const LANG_ZH_TW = {
             studentTableC8: '類型',
             studentTableC9: 'IRS號碼',
             studentTableC10: '編制班',
-            studentTableC11: '稱',
+            studentTableC11: '稱',
             addClassroomProp1: '選擇預設教室',
             addClassroomProp2: '新建個人教室',
             chooseClassroom: '請選擇系統的教室',
@@ -608,7 +608,6 @@ const LANG_ZH_TW = {
     },
     // 课程管理
     cusMgt: {
-        //ManageCourse.vue
         searchHolder: '搜尋',
         delBatch: '批量刪除',
         delCus: '刪除課程',
@@ -1107,6 +1106,9 @@ const LANG_ZH_TW = {
         paperTagPlace: '選擇標籤或者手動輸入後新增新標簽',
         useTip: '請至少保留一種試題用途',
         editor: {
+            canvasTip:'空白畫闆',
+            canvasStemTip:'帶題幹畫闆',
+            addTextDot:'添加著重號',
             imagePasteTip: '檢測到您粘貼的內容包含圖片信息,為了更好的使用體驗,建議您開啟醍摩豆閱卷助手',
             uploadVideo: '上傳本機影片',
             uploadAudio: '上傳本機音訊',
@@ -2087,7 +2089,6 @@ const LANG_ZH_TW = {
     },
     // 活动模块
     learnActivity: {
-        //MgtSchoolEva.vue
         noStartTimeTip:'不設置則默認為立即發佈',
         school: '校',
         area: '區',
@@ -3039,7 +3040,6 @@ const LANG_ZH_TW = {
     },
     // 基础信息
     schoolBaseInfo: {
-        // SystemSetting.vue
         commonset: '通用設定',
         pdMgt: '學制管理',
         cgMgt: '學院管理',
@@ -3730,6 +3730,16 @@ const LANG_ZH_TW = {
             vote: '投票',
             survey: '問卷',
             homework: '作業',
+            title: {
+                classRecord: "老師發佈的課堂記錄",
+                activity: "老師發佈的活動",
+                notice1: "班級公告",
+                notice2: "課程公告",
+                exam: "評測成績分析",
+                homework: "作業評分",
+                classScore: "課例記分",
+            },
+            toLook: "現在去看",
         },
         public: {
             noData: '暫無資料',
@@ -3752,6 +3762,7 @@ const LANG_ZH_TW = {
             hour: "時",
             minute: "分",
             mustFile: "必須上傳作業",
+            uploadTime: "上傳時間",
         },
         type: {
             home: "首頁",
@@ -4203,6 +4214,7 @@ const LANG_ZH_TW = {
                 againPractice: "再次練習",
                 finish: '已完成',
                 showAns: '顯示答案',
+                showPage: "查看題號",
                 hideAns: '隱藏答案',
                 hint: '重點提示',
                 previous: '上一題',
@@ -4237,6 +4249,7 @@ const LANG_ZH_TW = {
                 correction: '正解',
                 queNo: '題目:',
                 myAns: '我的作答:',
+                reAns:'重新作答:',
                 noExam: "暫沒有試題",
                 wrongSub: "錯題",
                 rightSub: "正確題目",
@@ -4545,6 +4558,9 @@ const LANG_ZH_TW = {
             wrongNum: "錯誤次數",
         },
         myAchievement: {
+            examAch: "評量成績",
+            hwAch: "作業成績",
+            cusAch: "老師給分",
             examMode: "評量模式",
             examIn: "發佈來源",
             name: "名稱",
@@ -5119,7 +5135,6 @@ const LANG_ZH_TW = {
         props6: '檔案上傳成功!',
         props7: '儲存空間不足!',
         uploadText: '點按或者拖移上傳',
-
         resTips: 'HiTeach生成的教材,只支援HTEX格式',
         space: '可用空間:',
         calcing: '計算中…',
@@ -5435,6 +5450,7 @@ const LANG_ZH_TW = {
         importTips7: '5. note: 教師備註內容,選填;',
         importTips8: '溫馨提示:如果匯入的教師用戶編號,或者匯入的手機或信箱已註冊醍摩豆帳號,系統會自動邀請老師加入學校。',
         importTips9: '點擊或者拖拽文件匯入',
+        impText: '無效數據(沒有名字):',
         impText1: '匯入總人數:',
         impText2: '僅匯入姓名:',
         impText3: '系統將自動邀請:',
@@ -5596,7 +5612,6 @@ const LANG_ZH_TW = {
         ach_text10: '當前班級',
         ach_text11: '總人數',
         ach_text12: '返回',
-
         ach_table_text1: '年級排名',
         ach_table_text2: '總分',
         ach_table_text3: '平均分數',
@@ -5613,7 +5628,6 @@ const LANG_ZH_TW = {
         ach_table_text14: '進步班級',
         ach_table_text15: '退步班級',
         ach_table_text16: '穩定班級',
-
         ach_chart_text1: '排序方式',
         ach_chart_text2: '預設排序',
         ach_chart_text3: '分數從高到低',
@@ -5646,7 +5660,6 @@ const LANG_ZH_TW = {
         ta_title2: '年級單題得分率統計',
         ta_title3: '試題分析總表',
         ta_title4: '試題得分率表',
-
         ta_text1: '區域',
         ta_text2: '區域特性',
         ta_text3: '題數',
@@ -5656,7 +5669,6 @@ const LANG_ZH_TW = {
         ta_text7: '試題可能含有異質成分在內需要局部修正',
         ta_text8: '試題困難度較高適合區分高成就學生',
         ta_text9: '試題含有異質成分可能數據錯誤或者題意不清必須加以修改',
-
         ta_chart_text1: '試題',
         ta_chart_text2: '答對率',
         ta_chart_text3: '穩定度',
@@ -5665,7 +5677,6 @@ const LANG_ZH_TW = {
         ta_chart_text6: '區級得分率',
         ta_chart_text7: '最大值',
         ta_chart_text8: '最小值',
-
         ta_table_text1: '題號',
         ta_table_text2: '題型',
         ta_table_text3: '知識點',
@@ -5677,7 +5688,6 @@ const LANG_ZH_TW = {
         ta_table_text9: '區級',
         ta_table_text10: '高分组',
         ta_table_text11: '低分组',
-
         ta_table_tip1: '* 組織:得分率',
         ta_table_tip2: '* R1-R6:成就區分組',
 
@@ -5689,22 +5699,18 @@ const LANG_ZH_TW = {
         ka_title5: '知識點得分詳情',
         ka_title6: '錯題率關係表',
         ka_title7: '知識點得分率分佈圖',
-
         ka_text1: '當前知識塊',
         ka_text2: '當前知識點',
         ka_text3: '當前認知層次',
         ka_text4: '認知層次占比情况',
-
         ka_chart_text1: '切換長條圖',
         ka_chart_text2: '切換雷達圖',
-
         ka_radar_text1: '記憶',
         ka_radar_text2: '應用',
         ka_radar_text3: '分析',
         ka_radar_text4: '創造',
         ka_radar_text5: '評鑑',
         ka_radar_text6: '理解',
-
         ka_table_text1: '知識點',
         ka_table_text2: '認知層次',
         ka_table_text3: '年級得分率',
@@ -5716,7 +5722,6 @@ const LANG_ZH_TW = {
         ka_table_text9: '錯題人次',
         ka_table_text10: 'PH錯題人次',
         ka_table_text11: 'PL錯題人次',
-
         ka_tip1: '* PH:高分組 / PL:低分組',
 
         // LevelAnalysis.vue
@@ -5744,7 +5749,6 @@ const LANG_ZH_TW = {
         ql_text13: '返回',
         ql_text14: '連線題',
         ql_text15: '配合題',
-
         baseExport: {
             title: '選擇要匯出的表格',
             checkAll: '全選',
@@ -5765,7 +5769,6 @@ const LANG_ZH_TW = {
         paperSubject: '試卷學科',
         paperItemsCount: '試卷題數',
         backUp: '回到上層'
-
     },
     // 研修活动
     train: {

+ 1 - 3
TEAMModelOS/ClientApp/src/access/login.js

@@ -106,9 +106,7 @@ export default {
 			try{
 				let studentInfo = withToken ? await $api.login.getStuInfoByToken(params) : await $api.login.getStudentInfoByAccount(params)
 				if(studentInfo.error){
-					if(studentInfo.error === 2){
-						app.$Message.warning(studentInfo.message)
-					}
+					app.$Message.warning(studentInfo.message)
 					j(studentInfo)
 					return
 				}

+ 2 - 20
TEAMModelOS/ClientApp/src/assets/student-web/component_styles/common.css

@@ -289,9 +289,6 @@ body,
   clear: both;
   margin-bottom: 100px;
 }
-.course-list .list {
-  width: 35%;
-}
 .courselistEn .list {
   width: 50%;
 }
@@ -801,14 +798,9 @@ body,
   }
 }
 @media screen and (max-width: 1280px) {
-  .list {
-    width: 40%;
-  }
-  .eventContentArea-view,
   .study-content,
   .hiteachNote-content,
-  .inform-content,
-  .course-content {
+  .inform-content {
     /* width: 60% !important; */
   }
   .courseContentEn {
@@ -825,15 +817,9 @@ body,
   }
 }
 @media screen and (max-width: 1500px) {
-  .course-list .list {
-    width: 40%;
-  }
   .courselistEn .list {
     width: 50%;
   }
-  .course-content {
-    /* width: 60% !important; */
-  }
   .courseContentEn {
     width: 50% !important;
   }
@@ -855,14 +841,10 @@ body,
   .list {
     width: 40%;
   }
-  .course-list .list {
-    width: 100%;
-  }
   .eventContentArea-view,
   .study-content,
   .hiteachNote-content,
-  .inform-content,
-  .course-content {
+  .inform-content {
     /* width: 60% !important; */
   }
   .courseContentEn {

+ 6 - 0
TEAMModelOS/ClientApp/src/assets/student-web/component_styles/course-list.less

@@ -156,3 +156,9 @@
         }
     }
 }
+
+@media screen and (max-width: 768px) {
+    .course-list .list {
+      width: 100%;
+    }
+}

+ 37 - 19
TEAMModelOS/ClientApp/src/assets/student-web/component_styles/event-list-new.less

@@ -1,28 +1,28 @@
-.list-new{
+.list-new {
     display: flex;
-    border-bottom: 1px solid rgba(0,0,0,.1);
+    border-bottom: 1px solid rgba(0, 0, 0, .1);
     padding: 15px 10px 15px 25px;
 
-    &:hover{
+    &:hover {
         background: linear-gradient(-270deg, #fafafa, #d4ede1);
         color: #03966a;
         cursor: pointer;
     }
 
-    &-icon{
+    &-icon {
         width: 10%;
         margin-right: 30px;
         display: flex;
         justify-content: center;
         flex-direction: column;
 
-        .svg-icon{
+        .svg-icon {
             width: 40px;
             height: 40px;
         }
     }
 
-    &-test{
+    &-test {
         width: 60%;
         font-weight: bolder;
         font-size: 14px;
@@ -30,7 +30,7 @@
         margin-right: 20px;
         clear: both;
 
-        .list-item-typeMark{
+        .list-item-typeMark {
             text-align: center;
             background-color: #dfdfdf;
             border-radius: 10px;
@@ -48,18 +48,18 @@
             margin: 5px 0;
         } */
 
-        .isScore{
+        .isScore {
             float: right;
         }
     }
 
-    &-type{
+    &-type {
         width: 20%;
         display: flex;
         justify-content: center;
         flex-direction: column;
 
-        .list-new-unDone{
+        .list-new-unDone {
             font-size: 10px;
             font-weight: bolder;
             // color: #fff;
@@ -68,21 +68,21 @@
             text-align: center;
             border: 1px solid;
 
-            
+
         }
-        
-        .isAllowRetry{
+
+        .isAllowRetry {
             color: #fff;
             background-color: #64ae16;
             border: none;
         }
 
-        .isWrongPra{
+        .isWrongPra {
             margin-top: 10px;
         }
     }
 
-    
+
     .tag-style {
         border: 1px solid;
         padding: 0 5px;
@@ -114,25 +114,43 @@
             border-radius: 5px 0 0 5px;
         }
 
-        .paper-extType{
+        .paper-extType {
             border: 1px solid #ababab;
             border-radius: 0 5px 5px 0;
             border-left: none;
         }
 
-        .paper-source{
+        .paper-source {
             border: 1px solid #ababab;
             // border-radius: 0 5px 5px 0;
         }
 
-        & > span{
+        &>span {
             padding: 0 5px;
             white-space: pre;
         }
 
-        & > p {
+        &>p {
             margin-left: 10px;
             font-size: 14px;
         }
     }
+}
+
+@media screen and (max-width: 1280px) {
+    .list {
+        width: 33%;
+    }
+}
+
+@media screen and (max-width: 1024px) {
+    .list {
+        width: 50%;
+    }
+}
+
+@media screen and (max-width: 768px) {
+    .list {
+        width: 100%;
+    }
 }

+ 22 - 2
TEAMModelOS/ClientApp/src/assets/student-web/component_styles/paper-test.css

@@ -191,6 +191,11 @@
   /* font-size: 20px; */
 }
 
+.lesson-test-pop .questionArea .small-view {
+  display: none;
+  text-align: center;
+}
+
 .lesson-test-pop .questionContent {
   padding: 5px 50px;
 }
@@ -347,6 +352,7 @@
   background-color: #fafafa;
   box-shadow: -5px 2px 13px rgba(0, 0, 0, 0.1);
   right: 0px;
+  z-index: 2;
 }
 .lesson-test-pop .pageCtl2 {
   position: relative;
@@ -741,8 +747,22 @@
         display: none;
     }
 }
-@media screen and (max-width: 500px) {
-    
+@media screen and (max-width: 576px) {
+    .lesson-test-pop .questionArea .small-view {
+        display: block;
+        margin-top: 10px;
+    }
+    .lesson-test-pop .pageCtl2 {
+        margin-top: 10px;
+        justify-content: center;
+    }
+    .lesson-test-pop .ansArea {
+      width: 100%;
+    }
+    .lesson-test-pop .analysis .item-explain .explain-title {
+        width: 100%;
+        max-width: none;
+    }
 }
 
 .lesson-test-pop .analysis .item-explain .item-explain-details-repair{

+ 5 - 3
TEAMModelOS/ClientApp/src/common/BaseLayout.vue

@@ -206,7 +206,8 @@ export default {
             this.$api.login.getCode(idToken).then(
                 res => {
                     this.loginCode = res.code
-                    window.location = `https://sokrates.teammodel.cn/auth/login/callback-habook?code=${this.loginCode}`
+                    let url = `${this.curSiteConfig.sokrateUrl}/auth/login/callback-habook?code=${this.loginCode}`
+                    window.open(url)
                 },
                 err => {
                     console.log('获取code失败', err)
@@ -225,7 +226,8 @@ export default {
     },
     computed: {
         ...mapGetters({
-            isHeadmaster: 'isHeadmaster'
+            isHeadmaster: 'isHeadmaster',
+            curSiteConfig: 'config/getCurSiteConfig',
         }),
         rotateIcon() {
             return ["collapse-icon", this.isCollapsed ? "rotate-icon" : ""]
@@ -471,7 +473,7 @@ export default {
                             role: 'admin',
                             permission: 'research-read|research-upd',
                             menuName: 'courseCenter',
-                            isShow: this.$store.state.config.srvAdrType != 'product'
+                            isShow: this.$store.state.config.srvAdrType === 'test'
                         },
                         // 名师课堂
                         {

+ 1 - 1
TEAMModelOS/ClientApp/src/components/coursemgt/StudentList.vue

@@ -28,7 +28,7 @@
                         <PersonalPhoto :name="row.name" :picture="row.picture"></PersonalPhoto>
                     </template>
                     <template slot-scope="{ row }" slot="gradeName">
-                        <span>{{ $jsFn.getGradeNameByYear(schoolBase, searchPeriod, row.classYear) }}</span>
+                        <span>{{ $jsFn.getGradeNameByYear(row.classYear) }}</span>
                     </template>
                     <template slot-scope="{ row }" slot="classId">
                         <span>{{ getClassName(row.classId)}}</span>

+ 60 - 43
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContent.less

@@ -1,53 +1,70 @@
 .eventContentArea-view {
-  float: right;
-  width: 75%;
-  padding: 1% 3%;
-  height:100%;
-  // padding-bottom: 50px;
-  overflow: auto;
-  overflow-x: hidden;
-
-  .noSelected {
-    text-align: center;
-    top: 35%;
-    right: 30%;
-    position: absolute;
-
-    h3 {
-      margin-top: 40%;
-      color: rgb(65, 65, 65);
-    }
-  }
-
-  .mybutton {
-    width: 100px;
-    margin-top: 20px;
-  }
-
-  img {
-    border-radius: 8px;
-    margin-right: 20px;
-    border: 1px solid rgba(173, 173, 173, 0.233);
-    //box-shadow: 0px 5px 13px rgb(189, 189, 189);
-  }
-
-  .star {
-    color: rgb(255, 115, 0);
-  }
+    float: right;
+    width: 75%;
+    padding: 1% 3%;
+    height: 100%;
+    // padding-bottom: 50px;
+    overflow: auto;
+    overflow-x: hidden;
+
+    .noSelected {
+        text-align: center;
+        top: 35%;
+        right: 30%;
+        position: absolute;
+
+        h3 {
+            margin-top: 40%;
+            color: rgb(65, 65, 65);
+        }
+    }
+
+    .mybutton {
+        width: 100px;
+        margin-top: 20px;
+    }
+
+    img {
+        border-radius: 8px;
+        margin-right: 20px;
+        border: 1px solid rgba(173, 173, 173, 0.233);
+        //box-shadow: 0px 5px 13px rgb(189, 189, 189);
+    }
+
+    .star {
+        color: rgb(255, 115, 0);
+    }
+
     //送出訊息動畫
     .slide-fade-enter-active {
-      transition: all 0.3s ease;
+        transition: all 0.3s ease;
     }
+
     .slide-fade-leave-active {
-      transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
+        transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
     }
-    .slide-fade-enter, .slide-fade-leave-to
-    /* .slide-fade-leave-active for below version 2.1.8 */ {
-      transform: translateY(-5%);
-      opacity: 0;
+
+    .slide-fade-enter,
+    .slide-fade-leave-to
+
+    /* .slide-fade-leave-active for below version 2.1.8 */
+        {
+        transform: translateY(-5%);
+        opacity: 0;
     }
 }
 
-.no-bar {
-  width: 100%;
+@media screen and (max-width: 1280px) {
+    .eventContentArea-view {
+        width: 67%;
+    }
 }
+
+@media screen and (max-width: 1024px) {
+    .eventContentArea-view {
+        width: 100%;
+    }
+}
+.no-bar {
+    width: 100%;
+}

+ 7 - 0
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReport.less

@@ -495,4 +495,11 @@
     border-radius: 5px;
     padding: 1px 5px;
     margin-right: 10px;
+}
+
+@media screen and (max-width: 576px) {
+    .qcontent .item-explain .explain-title {
+        width: 100%;
+        max-width: none;
+    }
 }

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

@@ -31,7 +31,7 @@
                 <i-col :xs="24" :sm="24" :md="24" :lg="24">
                     <Row :gutter="20">
                         <!-- 得分题目数 -->
-                        <i-col :xs="24" :sm="24" :md="12" :lg="6">
+                        <i-col :xs="24" :sm="24" :md="12" :lg="6" :xl="6">
                             <Card class="score-card">
                                 <p class="card-title">{{$t("studentWeb.exam.report.getScore")}}</p>
                                 <div class="card-content">
@@ -40,7 +40,7 @@
                             </Card>
                         </i-col>
                         <!-- 得分 -->
-                        <i-col :xs="24" :sm="24" :md="12" :lg="6">
+                        <i-col :xs="24" :sm="24" :md="12" :lg="6" :xl="6">
                             <Card class="score-card">
                                 <p class="card-title">{{$t('studentWeb.exam.score')}}</p>
                                 <div class="card-content">
@@ -49,7 +49,7 @@
                             </Card>
                         </i-col>
                         <!-- 班平均分 -->
-                        <i-col :xs="24" :sm="24" :md="12" :lg="6">
+                        <i-col :xs="24" :sm="24" :md="12" :lg="6" :xl="6">
                             <Card class="score-card">
                                 <p class="card-title">{{$t('studentWeb.exam.average')}}</p>
                                 <div class="card-content">
@@ -60,7 +60,7 @@
                             </Card>
                         </i-col>
                         <!-- 难易度 -->
-                        <i-col :xs="24" :sm="24" :md="12" :lg="6">
+                        <i-col :xs="24" :sm="24" :md="12" :lg="6" :xl="6">
                             <Card class="score-card">
                                 <p class="card-title">{{$t('studentWeb.exam.difficulty')}}</p>
                                 <div class="difficulty-rate">
@@ -77,7 +77,7 @@
                     <!-- </Card> -->
                 </i-col>
                 <i-col :xs="24" :sm="24" :md="24" :lg="24">
-                    <Card style="margin-top: 20px;">
+                    <Card>
                         <QuCount :quData="quDataSub" />
                     </Card>
                 </i-col>

+ 7 - 7
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReportCharts/LessonTestReportCharts.vue

@@ -14,25 +14,25 @@
       <CarouselItem class="demo-carousel"><RecognizePerformChart></RecognizePerformChart></CarouselItem>
     </Carousel> -->
         <Row :gutter="20">
-            <i-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8" :xxl="8">
-                <Card>
+            <i-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" :xxl="8">
+                <Card style="margin-bottom: 20px;">
                     <ScoreBarChart :total="chartsData.total"></ScoreBarChart>
                 </Card>
             </i-col>
-            <i-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8" :xxl="8">
-                <Card v-if="chartsData.knowledge.kn.length">
+            <i-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" :xxl="8">
+                <Card v-if="chartsData.knowledge.kn.length" style="margin-bottom: 20px;">
                     <!-- <KeyPerformChart :knowledge="chartsData.knowledge"></KeyPerformChart> -->
                     <KeyPerformChart :knowledge="chartsData.knowledge"></KeyPerformChart>
                 </Card>
-                <Card v-else>
+                <Card v-else style="margin-bottom: 20px;">
                     <div class="no-data">
                         <p class="no-data-title">{{ $t('studentWeb.exam.chart.keyPointPerformance') }}</p>
                         <div>{{ $t("studentWeb.public.noData") }}</div>
                     </div>
                 </Card>
             </i-col>
-            <i-col :xs="24" :sm="24" :md="12" :lg="8" :xl="8" :xxl="8">
-                <Card>
+            <i-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" :xxl="8">
+                <Card style="margin-bottom: 20px;">
                     <RecognizePerformChart :filed="chartsData.filed"></RecognizePerformChart>
                 </Card>
             </i-col>

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

@@ -126,6 +126,26 @@
                    :md="18"
                    :lg="20"
             >
+                <!--切換頁-->
+                <div class="small-view">
+                    <div class="countDown" v-if="needCountDown">
+                        <div>{{$t("studentWeb.exam.examTime")}}:{{ surplus }}</div>
+                    </div>
+                    <div class="pageCtl2">
+                        <button @click="preQ()" :disabled="!queNo" :class="{disable: !queNo}">
+                            <Icon type="ios-arrow-back" />{{$t("studentWeb.exam.testpop.previous")}}
+                        </button>
+                        <button @click="showPage(true)">
+                            {{$t("studentWeb.exam.testpop.showPage")}}
+                        </button>
+                        <button @click="nextQ()"
+                                :disabled="queNo == (showExam.length-1)"
+                                :class="{ hintClick:queNo != (showExam.length-1) && checkers[queNo] != '',disable: queNo == (showExam.length-1)}">
+                            {{$t("studentWeb.exam.testpop.next")}}
+                            <Icon type="ios-arrow-forward" />
+                        </button>
+                    </div>
+                </div>
                 <div class="questionContent" ref="questionBox" v-if="showExam.length">
                     <!-- <span class="hintwrap">
                         <Tooltip :content="$t('studentWeb.exam.report.noSource')" theme="light" placement="left">
@@ -139,7 +159,7 @@
                             <span>{{getTestType(getQue(queNo).type)}}</span>
                         </div>
                         <div class="que-item" v-if="getQue(queNo).type != 'compose' && getQue(queNo).parent === undefined">
-                            <span @click="changeStar(!getQue(queNo).star, queNo)" style="margin-right: 10px; cursor: pointer;">
+                            <span v-if="isWrong" @click="changeStar(!getQue(queNo).star, queNo)" style="margin-right: 10px; cursor: pointer;">
                                 <Icon custom="iconfont icon-shoucang1" size="25" v-show="!getQue(queNo).star" />
                                 <Icon custom="iconfont icon-shoucang2" size="25" color="#FF7A4E" v-show="getQue(queNo).star" />
                             </span>
@@ -154,7 +174,7 @@
                                     <span>{{getTestType(getQue(queNo).parentInfo.type)}}</span>
                                 </div>
                                 <div class="compose-box">
-                                    <span @click="changeStar(!getQue(queNo).star, queNo, true)" style="cursor: pointer;">
+                                    <span v-if="isWrong" @click="changeStar(!getQue(queNo).star, queNo, true)" style="cursor: pointer;">
                                         <Icon custom="iconfont icon-shoucang1" size="25" v-show="!getQue(queNo).star" />
                                         <Icon custom="iconfont icon-shoucang2" size="25" color="#FF7A4E" v-show="getQue(queNo).star" />
                                     </span>
@@ -300,7 +320,7 @@
                 <div v-else style="text-align: center; margin-top: 25%; font-size: 20px;">{{ $t("studentWeb.exam.testpop.noExam") }}</div>
             </i-col>
             <!--答题卡-->
-            <i-col class="ansArea"
+            <i-col class="ansArea" v-show="widthLimit || (!widthLimit && isShow)"
                    :xs="24"
                    :sm="12"
                    :md="6"
@@ -309,29 +329,29 @@
                     <div>{{$t("studentWeb.exam.examTime")}}&nbsp;</div>
                     <span>{{ surplus }}</span>
                 </div>
-                <div style="margin-top: 30px; font-weight: 800">{{$t("studentWeb.exam.testpop.myAnswerSheet")}}</div>
-                <!--切換頁-->
-                <div class="pageCtl2">
-                    <button @click="preQ()" :disabled="!queNo" :class="{disable: !queNo}">
-                        <Icon type="ios-arrow-back" />{{$t("studentWeb.exam.testpop.previous")}}
-                    </button>
-                    <!-- <button @click="changeAnalysisType(true)"
-                            v-if="isWrong"
-                            :disabled="!checkers[queNo].length > 0"
-                            :class="checkers[queNo] == '' ? 'disable' : ''"
-                    >
-                        {{$t("studentWeb.exam.testpop.showAns")}}
-                    </button> -->
-                    <button @click="nextQ()"
-                            :disabled="queNo == (showExam.length-1)"
-                            :class="{ hintClick:queNo != (showExam.length-1) && checkers[queNo] != '',disable: queNo == (showExam.length-1)}">
-                        {{$t("studentWeb.exam.testpop.next")}}
-                        <Icon type="ios-arrow-forward" />
-                    </button>
+                <div v-if="widthLimit">
+                    <div style="margin-top: 30px; font-weight: 800">{{$t("studentWeb.exam.testpop.myAnswerSheet")}}</div>
+                    <!--切換頁-->
+                    <div class="pageCtl2">
+                        <button @click="preQ()" :disabled="!queNo" :class="{disable: !queNo}">
+                            <Icon type="ios-arrow-back" />{{$t("studentWeb.exam.testpop.previous")}}
+                        </button>
+                        <!-- <button @click="changeAnalysisType(true)"
+                                v-if="isWrong"
+                                :disabled="!checkers[queNo].length > 0"
+                                :class="checkers[queNo] == '' ? 'disable' : ''"
+                        >
+                            {{$t("studentWeb.exam.testpop.showAns")}}
+                        </button> -->
+                        <button @click="nextQ()"
+                                :disabled="queNo == (showExam.length-1)"
+                                :class="{ hintClick:queNo != (showExam.length-1) && checkers[queNo] != '',disable: queNo == (showExam.length-1)}">
+                            {{$t("studentWeb.exam.testpop.next")}}
+                            <Icon type="ios-arrow-forward" />
+                        </button>
+                    </div>
                 </div>
                 <div class="checkAnswer">
-                    <!-- <br />
-                    <br /> -->
                     <span style="margin-top:10px;font-weight:800">{{$t("studentWeb.exam.testpop.qNo")}}</span>
                     <div class="que-box" v-if="showExam.length">
                         <div v-for="(item,index) in showExam" :key="index"
@@ -406,6 +426,7 @@
             }
         },
         mounted() {
+            this.widthLimit = window.innerWidth > 577
             this.getPaper()
             this.formPaper()
             //如果进入页面不进行点击等操作,直接关闭,会不触发
@@ -502,6 +523,8 @@
                     this.$t("evaluation.level5"),
                     this.$t("evaluation.level6")
                 ],
+                isShow: false,
+                widthLimit: false,
             };
         },
         methods: {
@@ -727,6 +750,10 @@
             // 点击答题卡跳转
             gototheQues(index) {
                 this.queNo = index - 1
+                // 页面宽度<576,才会调用
+                if(!this.widthLimit) {
+                    this.showPage(false)
+                }
                 // this.changeAnalysisType(false)
             },
             preQ() {
@@ -735,6 +762,9 @@
                     // this.changeAnalysisType(false)
                 }
             },
+            showPage(type) {
+                this.isShow = type
+            },
             nextQ() {
                 if (this.queNo < (this.showExam.length - 1)) {
                     this.queNo++

+ 228 - 183
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/composePaper.vue

@@ -1,192 +1,237 @@
 <template>
-    <div class="content">
-        <div id="textArea"> </div>
-        <Modal v-model="markStatus" fullscreen :title="$t('studentWeb.exam.answer')" footer-hide  ref="compose">
-                <!-- <BaseMyCanvas v-if="markStatus" :bgImg="markBg" @onCloseModal="closeModal" :isStudent="markStatus" @onSaveCanvas="saveMark"></BaseMyCanvas> -->
-                <BaseCanvas v-if="markStatus" :vm="vm" :bgImg="markBg" @onCloseModal="closeModal" :isStudent="markStatus" @onSaveCanvas="saveMark"></BaseCanvas>
-        </Modal>
-        <iframe class="frame" id="answerIframe" :srcdoc="itemInfo.question"></iframe>
+  <div class="content">
+    <div class="editor-wrap" v-show="!isConnector">
+      <div id="textArea"></div>
     </div>
+    <div class="connector-wrap" v-if="isConnector">
+      <div v-if="answerImg">
+        <img :src="answerImg" alt="">
+      </div>
+      <p v-if="answerImg" style="font-weight: bold;margin-bottom:20px">{{ $t("studentWeb.exam.testpop.reAns") }}</p>
+      <BaseCanvas :vm="vm" :bgImg="markBg" @onCloseModal="closeModal" :isStudent="markStatus" @onSaveCanvas="saveMark"></BaseCanvas>
+    </div>
+    <Modal v-model="markStatus" fullscreen :title="$t('studentWeb.exam.answer')" footer-hide ref="compose">
+      <BaseCanvas v-if="markStatus" :vm="vm" :bgImg="markBg" @onCloseModal="closeModal" :isStudent="markStatus" @onSaveCanvas="saveMark"></BaseCanvas>
+    </Modal>
+    <iframe class="frame" id="answerIframe" :srcdoc="itemInfo.question"></iframe>
+  </div>
 </template>
 <script>
-    import html2canvas from 'html2canvas';
-    import E from 'wangeditor';
-    import {editor_tw_config} from '@/utils/editorLangTw.js';
-    import {editor_en_config} from '@/utils/editorLangEn.js';
-    import i18next from 'i18next';
-    export default {
-        components: {
+import html2canvas from 'html2canvas';
+import E from 'wangeditor';
+import { editor_tw_config } from '@/utils/editorLangTw.js';
+import { editor_en_config } from '@/utils/editorLangEn.js';
+import i18next from 'i18next';
+export default {
+  components: {
 
-        },
-        props: {
-            index: {
-                type: Number,
-                default: -1
-            },
-            textData: {
-                type: Array,
-                default: () => {
-                    return []
-                }
-            },
-            itemInfo: {
-                type: Object,
-                default: () => {
-                    return {}
-                }
-            },     
-            close: {
-                type: Boolean,
-                default: false
-            }
-        },
-        data(vm) {
-            return {
-                vm:vm,
-                tabName: 'exercise',
-                editorContent: "",
-                examInfo: [],
-                editor: null,
-                markStatus: false,
-                markBg:""
-            }
-        },
-        methods: {
-            async getInfo() {
-                this.examInfo = []
-                this.initEditor()
-                if (this.textData.length > 0) {
-                    this.examInfo = [...this.textData]
-                    this.editor.txt.html(this.examInfo[0])
-                }
-            },
-            closeModal() {
-                this.markStatus = false
-                this.markBg = ""
-            },
-            saveMark(data) {
-                if (data) {
-                    data.height = 400
-                    data.width = 400
-                    let img = document.createElement('img')
-                    img.src = data.base64
-                    console.log(img)
-                    this.editor.txt.html(img.outerHTML)
-                    this.markStatus = false
-                }
-            },
-            initEditor() {
-                this.editorContent = ""
-                this.editor = new E("#textArea");
-                this.editor.config.onchange = (html) => {
-                    this.editorContent = html;
-                };
-                this.editor.config.showFullScreen = false
-                this.editor.config.menus = [
-                    "link", // 插入链接
-                    "justify", // 对齐方式
-                    "image", // 插入图片
-                ]
-                this.editor.config.zIndex = 1
-                this.editor.config.placeholder = this.$t('studentWeb.exam.inputAnswers')
-                this.editor.config.height = 300
-                this.editor.config.showLinkImg = false;
-                this.editor.config.uploadImgShowBase64 = true; // 使用 base64 保存图片不建议使用这种,我只是图个方便
-                if (this.itemInfo.type == 'correct' || this.itemInfo.type == 'connector') {
-                    this.editor.config.menus.push('connector')
-                    this.$editorTools.addStuBgBtn(this, this.editor)
-                } else {
-                    this.$editorTools.addCanvas(this, this.editor)
-                }
-                let curLang = localStorage.getItem('local') || 'zh-cn'
-                if (curLang === 'zh-tw') {
-                    // 自定义语言
-                    this.editor.config.languages['tw'] = editor_tw_config
-                    // 选择语言
-                    this.editor.config.lang = 'tw'
-                    // 引入 i18next 插件
-                    this.editor.i18next = i18next
-                }
-                if (curLang === 'en-us') {
-                    // 自定义语言
-                    this.editor.config.languages['en'] = editor_en_config
-                    // 选择语言
-                    this.editor.config.lang = 'en'
-                    // 引入 i18next 插件
-                    this.editor.i18next = i18next
-                }
-                this.editor.create();
-                if (this.close) {
-                    this.editor.disable()
-                }
-                this.editor.txt.clear()
-                if (this.examInfo.length > 0) {
-                    this.editor.txt.html(this.examInfo[0])
-                } 
-            },
-            markStuAnswer() {
-                let answerIframe = ''
-                answerIframe = document.getElementById('answerIframe')
-                answerIframe.contentWindow.document.body.style.width = 'fit-content'
-                answerIframe.contentWindow.document.body.style.minWidth = '600px'
-                answerIframe.contentWindow.document.body.style.backgroundColor = '#f5f5f5'
-                console.log(document.getElementById('answerIframe').contentWindow.document)
-                let iframe = document.getElementById('answerIframe').contentWindow.document.getElementsByTagName('p')
-                if (iframe.length > 0) {
-                    for (let i = 0; i < iframe.length -1; i++) {
-                        iframe[i].style.lineHeight = '50px'
-                        iframe[i].style.paddingBottom = '30px'
-                    }
-                    iframe[iframe.length -1].style.paddingBottom = '100px'
-                    iframe[iframe.length - 1].style.lineHeight = '50px'
-                }
-                html2canvas(answerIframe.contentWindow.document.body).then(canvas => {
-                    this.markStatus = true
-                    this.markBg = canvas.toDataURL("image/png");
-                })
-            },
-        },
-        mounted() {
-            this.initEditor()
-            this.getInfo()
-            // 监听富文本画板功能
-            this.$EventBus.$off('onStuCanvas')
-            this.$EventBus.$on('onStuCanvas', editor => {
-                this.markBg = ""
-                this.markStuAnswer()
-            })
-        },
-        watch: {
-            index() {
-                this.getInfo()
-                deep: true
-                immediate:true
-            },
-            editorContent() {
-                this.$emit("dataGet", this.editorContent, this.index)
-            }
-        }
-	}
-</script>
-<style scoped>
-	.content{
-		width:100%;
-		height:100%;
-	}
-    .textArea {
-        width: 100%;
-        height: 100%;
+  },
+  props: {
+    index: {
+      type: Number,
+      default: -1
+    },
+    textData: {
+      type: Array,
+      default: () => {
+        return []
+      }
+    },
+    itemInfo: {
+      type: Object,
+      default: () => {
+        return {}
+      }
+    },
+    close: {
+      type: Boolean,
+      default: false
     }
-    .frame {
-        /*height: 100px;*/
-        border: none;
-        position:fixed;
-        height:0.1px;
-        width: 50%;
-        /*margin-left:10px;*/
-        /*line-height: 50px;*/
+  },
+  data(vm) {
+    return {
+      answerImg: null,
+      vm: vm,
+      tabName: 'exercise',
+      editorContent: "",
+      examInfo: [],
+      editor: null,
+      markStatus: false,
+      markBg: ""
     }
-    .canvas-tools {
-        bottom: -50px;
+  },
+  methods: {
+    async getInfo() {
+      this.examInfo = []
+      this.initEditor()
+      console.error(this.textData)
+      this.answerImg = null
+      if (this.textData.length > 0) {
+        this.examInfo = [...this.textData]
+        if (this.isConnector) {
+          this.answerImg = this.textData[0]
+        } else {
+          this.answerImg = null
+          this.editor.txt.html(this.examInfo[0])
+        }
+      }
+      setTimeout(() => {
+        if (this.isConnector) {
+          this.markBg = ""
+          this.markStuAnswer()
+        }
+      }, 100)
+    },
+    closeModal() {
+      this.markStatus = false
+      this.markBg = ""
+    },
+    saveMark(data) {
+      if (data) {
+        data.height = 400
+        data.width = 400
+        let img = document.createElement('img')
+        img.src = data.base64
+        if (this.isConnector) {
+          this.answerImg = data.base64
+          this.markBg = ''
+          setTimeout(() => {
+            this.markStuAnswer()
+          }, 100)
+          this.$emit("dataGet", data.base64, this.index)
+          this.$Message.success(this.$t('settings.submitSucTips'))
+        } else {
+          this.editor.txt.html(img.outerHTML)
+        }
+        this.markStatus = false
+      }
+    },
+    initEditor() {
+      this.editorContent = ""
+      this.editor = new E("#textArea");
+      this.editor.config.onchange = (html) => {
+        this.editorContent = html;
+      };
+      this.editor.config.showFullScreen = false
+      this.editor.config.menus = [
+        "link", // 插入链接
+        "justify", // 对齐方式
+        "image", // 插入图片
+      ]
+      this.editor.config.zIndex = 1
+      this.editor.config.placeholder = this.$t('studentWeb.exam.inputAnswers')
+      this.editor.config.height = 300
+      this.editor.config.showLinkImg = false;
+      this.editor.config.uploadImgShowBase64 = true; // 使用 base64 保存图片不建议使用这种,我只是图个方便
+      this.$editorTools.addCanvas(this, this.editor)
+      this.editor.config.menus.push('connector')
+      this.$editorTools.addStuBgBtn(this, this.editor)
+      // if (this.itemInfo.type == 'correct' || this.itemInfo.type == 'connector') {
+      //     this.editor.config.menus.push('connector')
+      //     this.$editorTools.addStuBgBtn(this, this.editor)
+      // } else {
+      //     this.$editorTools.addCanvas(this, this.editor)
+      // }
+      let curLang = localStorage.getItem('local') || 'zh-cn'
+      if (curLang === 'zh-tw') {
+        // 自定义语言
+        this.editor.config.languages['tw'] = editor_tw_config
+        // 选择语言
+        this.editor.config.lang = 'tw'
+        // 引入 i18next 插件
+        this.editor.i18next = i18next
+      }
+      if (curLang === 'en-us') {
+        // 自定义语言
+        this.editor.config.languages['en'] = editor_en_config
+        // 选择语言
+        this.editor.config.lang = 'en'
+        // 引入 i18next 插件
+        this.editor.i18next = i18next
+      }
+      this.editor.create();
+      if (this.close) {
+        this.editor.disable()
+      }
+      this.editor.txt.clear()
+      if (this.examInfo.length > 0) {
+        this.editor.txt.html(this.examInfo[0])
+      }
+    },
+    markStuAnswer() {
+      let answerIframe = ''
+      answerIframe = document.getElementById('answerIframe')
+      answerIframe.contentWindow.document.body.style.width = 'fit-content'
+      answerIframe.contentWindow.document.body.style.minWidth = '600px'
+      answerIframe.contentWindow.document.body.style.backgroundColor = '#f5f5f5'
+      console.log(document.getElementById('answerIframe').contentWindow.document)
+      let iframe = document.getElementById('answerIframe').contentWindow.document.getElementsByTagName('p')
+
+      if (iframe.length > 0) {
+        for (let i = 0; i < iframe.length - 1; i++) {
+          iframe[i].style.lineHeight = '50px'
+          iframe[i].style.paddingBottom = '30px'
+        }
+        iframe[iframe.length - 1].style.paddingBottom = '100px'
+        iframe[iframe.length - 1].style.lineHeight = '50px'
+      }
+      html2canvas(answerIframe.contentWindow.document.body).then(canvas => {
+        if(!this.isConnector){
+            this.markStatus = true
+        }
+        this.markBg = canvas.toDataURL("image/png");
+      })
+    },
+  },
+  mounted() {
+    this.initEditor()
+    this.getInfo()
+    // 监听富文本画板功能
+    this.$EventBus.$off('onStuCanvas')
+    this.$EventBus.$on('onStuCanvas', editor => {
+      this.markBg = ""
+      this.markStuAnswer()
+    })
+  },
+  computed: {
+    isConnector() {
+      return this.itemInfo.type == 'connector' || this.itemInfo.type == 'correct'
     }
+  },
+  watch: {
+    index() {
+      this.getInfo()
+      deep: true
+      immediate: true
+    },
+    editorContent() {
+      this.$emit("dataGet", this.editorContent, this.index)
+    }
+  }
+}
+</script>
+<style scoped>
+.content {
+  width: 100%;
+  height: 100%;
+}
+.connector-wrap /deep/ .demo {
+  justify-content: flex-start !important;
+}
+.textArea {
+  width: 100%;
+  height: 100%;
+}
+.frame {
+  /*height: 100px;*/
+  border: none;
+  position: fixed;
+  height: 0.1px;
+  width: 50%;
+  /*margin-left:10px;*/
+  /*line-height: 50px;*/
+}
+.canvas-tools {
+  bottom: -50px;
+}
 </style>

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

@@ -805,7 +805,7 @@ import QuesNaire from "./EventContentTypeTemplate/QuesNaire";
                 // 活动信息
                 this.$store.commit("SetPaperInfo", item)
                 ////螢幕寬度<767px時,直接關掉sidebar
-                if (window.innerWidth <= 768) {
+                if (window.innerWidth <= 1024) {
                    this.$store.commit("ToggleSidebar", false);
                 }
                 ////改變ItemName的狀態 vuex mutations

+ 165 - 0
TEAMModelOS/ClientApp/src/components/student-web/HomeView/ChartHome/ClassPoint.vue

@@ -0,0 +1,165 @@
+<template>
+    <div class="class-echarts">
+        <div class="home-title-name">
+            {{ $t("studentWeb.home.title.classScore") }}
+        </div>
+        <div id="classPoint"></div>
+        <div class="typeAll">
+            <div class="typeDef" @click="changeType(false)">
+                <span :class="{'activeBg': !type}"></span>
+                <span>{{ $t("studentWeb.public.school") }}</span>
+            </div>
+            <div class="typeDef" @click="changeType(true)">
+                <span :class="{'activeBg': type}"></span>
+                <span>{{ $t("studentWeb.public.private") }}</span>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+export default {
+    name: "HomeworkPoint",
+    data() {
+        return {
+            type: false, //个人的类型
+            school: ["数学", "语文", "英语", "化学", "生物", "物理", "地理"],
+            private: ["个人", "培训课", "健身课", "鉴赏课", "解剖课", "盆栽课", "人文历史概述"],
+            schoolData: ['87', '75', '100', '12', '46', 97, 57],
+            privateData: ['55', '99', '12', '74', '88', 37, 67],
+        }
+    },
+    methods: {
+        setEcharts() {
+            let myChart = this.$echarts.init(document.getElementById("classPoint"))
+            let option = {
+                color: ["#5470C6", "#91CC75", '#00905a'],
+                /* title: {
+                    text: this.$t('studentWeb.exam.chart.keyPointPerformance'),
+                    left: 'left',
+                    textStyle: {
+                        // fontWeight: "normal",
+                        fontSize: "14",
+                    }
+                }, */
+                /* legend: {
+                    data: ["罗老师", "陈老师", "辅导员"],
+                    bottom: 0,
+                }, */
+                grid: {
+                    top: '10%',
+                    bottom: '15%',
+                    // left: '80',
+                    // right: '80',
+                },
+                dataZoom: [
+                    {
+						'show': true,
+						'height': 3,
+						'xAxisIndex': [0],
+						bottom: 5,
+						'start': 0,
+						'end': 100,
+						handleIcon: 'M512 497.821538m-418.264615 0a418.264615 418.264615 0 1 0 836.52923 0 418.264615 418.264615 0 1 0-836.52923 0Z',
+						handleSize: '160%',
+						handleStyle: {
+							color: '#d3dee5'
+						},
+						textStyle: {
+							color: '#717171'
+						},
+						// borderColor: '#90979c'
+					}, {
+						'type': 'inside',
+						'show': true,
+						'height': 15,
+						'start': 0,
+						'end': 100
+					}
+                ],
+                xAxis: {
+                    show: true,
+                    // name:'作业次数',
+                    nameLocation: 'end',
+                    type: 'category',
+                    data: this.type ? this.private : this.school,
+                    axisLine: {
+                        lineStyle: {
+                            color: "#AAA"
+                        }
+                    },
+                    axisLabel: {
+                        rotate: 0
+                    }
+                },
+                yAxis: {
+                    // name:'正确率',
+                    type: 'value',
+                    axisLine: {
+                        lineStyle: {
+                            color: "#AAA"
+                        }
+                    },
+                },
+                tooltip: {
+                    trigger: 'axis',
+                    padding: [4, 12],
+                },
+                series: [
+                    {
+                        name: '罗老师',
+                        type: 'line',
+                        data: this.type ? this.privateData : this.schoolData
+                    }
+                ],
+            }
+            myChart.setOption(option)
+        },
+        changeType(type) {
+            this.type = type
+            this.setEcharts()
+        },
+    },
+    mounted () {
+        this.setEcharts()
+    }
+}
+</script>
+
+<style lang="less" scoped>
+#classPoint {
+    width: 100%;
+    height: 235px;
+}
+.class-echarts {
+    .typeAll {
+        display: flex;
+        justify-content: center;
+
+        & > div:first-child {
+            margin-right: 20px;
+        }
+
+        .typeDef {
+            cursor: pointer;
+            display: flex;
+            align-items: center;
+            font-size: 12px;
+
+            & > span:first-of-type {
+                display: inline-block;
+                background: #ccc;
+                height: 12px;
+                width: 20px;
+                margin-right: 5px;
+                border-radius: 4px;
+            }
+        }
+
+        .activeBg {
+            background-color: #00ad6cd1 !important;
+            // color: white;
+        }
+    }
+}
+</style>

+ 270 - 0
TEAMModelOS/ClientApp/src/components/student-web/HomeView/ChartHome/ExamPerform.vue

@@ -0,0 +1,270 @@
+<template>
+    <div class="exam-echarts">
+        <div class="home-title-name">
+            {{ $t("studentWeb.home.title.exam") }}
+        </div>
+        <template v-id="nowRadar.length">
+            <div id="examPerform"></div>
+        </template>
+        <div v-show="!nowRadar.length" class="no-data">
+            <img src="../noData.png" alt="">
+            <p>暂没有数据</p>
+        </div>
+        <div class="typeAll">
+            <div class="typeDef" @click="changeType(false)">
+                <span :class="{'activeBg': !type}"></span>
+                <span>{{ $t("studentWeb.public.school") }}</span>
+            </div>
+            <div class="typeDef" @click="changeType(true)">
+                <span :class="{'activeBg': type}"></span>
+                <span>{{ $t("studentWeb.public.private") }}</span>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import { mapState } from 'vuex'
+export default {
+    name: "ExamPerform",
+    data() {
+        return {
+            type: false, //个人的类型
+            school: [],
+            privates: [],
+            schoolData: [{
+                name: "",
+                value: []
+            }],
+            privateData: [{
+                name: "",
+                value: []
+            }],
+            nowRadar: [],
+        }
+    },
+    methods: {
+        getExamAch() {
+            let param = {
+                userid: this.userInfo.sub,
+                userType: "",
+                school: this.userInfo.azp,
+                type: 'Exam',
+                continuationToken: null,
+                // count: 20,
+            }
+            param.userType = this.userInfo.scope === "tmduser" ? "tmdid" : "schoolid"
+            this.$api.studentWeb.getExamAch(param).then(async res => {
+                /* 
+                    科目的数组,里面有总的分数百分比,总的评测次数
+                    [{
+                        id、name,
+                        total: 310%,
+                        num: 4,
+                        average: 0
+                    }]
+                    得出总的数据后,再算平均分
+                    average = (total / average) * 100
+                */
+                if(!res.code) {
+                    let examArr = {
+                        school: [],
+                        privates: [],
+                    }
+                    if(res.result.length) {
+                        res.result.forEach(item => {
+                            if(item.subject) {
+                                item.subject.forEach((sub, sIndex) => {
+                                    // 找出我的成绩
+                                    let myAch = item.result.filter(result => {
+                                        return result.sub === sub.id
+                                    })
+                                    let score = 0
+                                    if(myAch.length) {
+                                        myAch.forEach(myAch => {
+                                            let myId = myAch.sIds.findIndex(sId => {
+                                                return sId === this.userInfo.sub
+                                            })
+                                            if(myId != -1) {
+                                                score = myAch.sum[myId]
+                                            }
+                                        })
+                                    }
+                                    let subjectData = {
+                                        id: sub.id,
+                                        name: sub.name,
+                                        total: Number((score / item.point[sIndex]).toFixed(2)),
+                                        num: 1,
+                                        average: 0
+                                    }
+                                    if(item.owner === 'school') {
+                                        if(examArr.school.length) {
+                                            let subIndex = examArr.school.findIndex(sch => {
+                                                return sch.id === subjectData.id
+                                            })
+                                            if(subIndex != -1) {
+                                                examArr.school[subIndex].num += 1
+                                                examArr.school[subIndex].total += subjectData.total
+                                            } else {
+                                                examArr.school.push(subjectData)
+                                            }
+                                        } else {
+                                            examArr.school.push(subjectData)
+                                        }
+                                    } else if(item.owner === 'teacher') {
+                                        if(examArr.privates.length) {
+                                            let subIndex = examArr.privates.findIndex(sch => {
+                                                return sch.id === subjectData.id
+                                            })
+                                            if(subIndex != -1) {
+                                                examArr.privates[subIndex].num += 1
+                                                examArr.privates[subIndex].total += subjectData.total
+                                            } else {
+                                                examArr.privates.push(subjectData)
+                                            }
+                                        } else {
+                                            examArr.privates.push(subjectData)
+                                        }
+                                    }
+                                })
+                            }
+                        })
+                    }
+                    examArr.school.forEach(item => {
+                        this.school.push({
+                            max: 100,
+                            name: item.name
+                        })
+                        let average = Number((item.total / item.num * 100).toFixed(0))
+                        console.log(item.total, item.num, average);
+                        this.schoolData[0].value.push(average)
+                    })
+                    examArr.privates.forEach(item => {
+                        this.privates.push({
+                            max: 100,
+                            name: item.name
+                        })
+                        let average = Number((item.total / item.num * 100).toFixed(0))
+                        console.log(item.total, item.num, average);
+                        this.privateData[0].value.push(average)
+                    })
+                    this.nowRadar = this.school
+                    if(this.nowRadar.length) {
+                        this.setEcharts()
+                    }
+                }
+            })
+        },
+        setEcharts() {
+            let myChart = this.$echarts.init(document.getElementById("examPerform"), 'macarons')
+            let option = {
+                color: ["#5470C6", "#91CC75", '#00905a'],
+                /* title: {
+                    text: this.$t('studentWeb.exam.chart.keyPointPerformance'),
+                    left: 'left',
+                    textStyle: {
+                        // fontWeight: "normal",
+                        fontSize: "14",
+                    }
+                }, */
+                /* legend: {
+                    data: [this.$t("studentWeb.exam.chart.me"), this.$t("studentWeb.exam.chart.participantClass"), this.$t("studentWeb.exam.chart.allClass")],
+                    bottom: 0,
+                }, */
+                grid: {
+                    top: '10%',
+                    bottom: '15%',
+                    // left: '80',
+                    // right: '80',
+                },
+                tooltip: {
+                    trigger: 'item',
+                    padding: [4, 12],
+                },
+                radar: {
+                    // shape: 'circle',
+                    indicator: this.nowRadar
+                },
+                series: [
+                    {
+                        name: '科目平均分',
+                        type: 'radar',
+                        data: this.type ? this.privateData : this.schoolData
+                    },
+                ],
+            }
+            myChart.setOption(option)
+        },
+        changeType(type) {
+            this.type = type
+            this.nowRadar = type ? this.privates : this.school
+            if(this.nowRadar.length) {
+                this.$nextTick(() => {
+                    this.setEcharts()
+                })
+            }
+        },
+    },
+    mounted () {
+        this.getExamAch()
+    },
+    computed: {
+        ...mapState({
+            userInfo: state => state.userInfo,
+        })
+    }
+}
+</script>
+
+<style lang="less" scoped>
+#examPerform {
+    width: 100%;
+    height: 235px;
+}
+.exam-echarts {
+    position: relative;
+
+    .typeAll {
+        display: flex;
+        justify-content: center;
+
+        & > div:first-child {
+            margin-right: 20px;
+        }
+
+        .typeDef {
+            cursor: pointer;
+            display: flex;
+            align-items: center;
+            font-size: 12px;
+
+            & > span:first-of-type {
+                display: inline-block;
+                background: #ccc;
+                height: 12px;
+                width: 20px;
+                margin-right: 5px;
+                border-radius: 4px;
+            }
+        }
+
+        .activeBg {
+            background-color: #00ad6cd1 !important;
+            // color: white;
+        }
+    }
+    .no-data {
+        background-color: #fff;
+        width: 100%;
+        height: 235px;
+        position: absolute;
+        top: 30px;
+        text-align: center;
+        padding-top: 35px;
+
+        img {
+            width: 50%;
+        }
+    }
+}
+</style>

+ 263 - 0
TEAMModelOS/ClientApp/src/components/student-web/HomeView/ChartHome/HomeworkPoint.vue

@@ -0,0 +1,263 @@
+<template>
+    <div class="homework-echarts">
+        <div class="home-title-name">
+            {{ $t("studentWeb.home.title.homework") }}
+        </div>
+        <div id="homeworkPoint"></div>
+    </div>
+</template>
+
+<script>
+import { mapState } from 'vuex'
+export default {
+    name: "HomeworkPoint",
+    data() {
+        return {
+            hwAch: []
+        }
+    },
+    computed: {
+        ...mapState({
+            userInfo: state => state.userInfo,
+        })
+    },
+    methods: {
+        getHwAch() {
+            let param = {
+                userid: this.userInfo.sub,
+                userType: "",
+                school: this.userInfo.azp,
+                type: 'Homework',
+                continuationToken: null,
+                // count: 20,
+            }
+            param.userType = this.userInfo.scope === "tmduser" ? "tmdid" : "schoolid"
+            this.$api.studentWeb.getHwAch(param).then(async res => {
+                let hwnum = []
+                let teaId = []
+                res.works.forEach(item => {
+                    teaId.push(item.createId)
+                    let haveTea = hwnum.findIndex(tea => {
+                        return tea.createId === item.createId
+                    })
+                    // 没有这个老师的数据
+                    if(haveTea === -1) {
+                        hwnum.push({
+                            createId: item.createId,
+                            score: [item.score.length ? item.score[0].score : 0]
+                        })
+                    } else {
+                        // 有这个老师的数据,只需要往score添加这次的成绩
+                        hwnum[haveTea].score.push(item.score.length ? item.score[0].score : 0)
+                    }
+                })
+                let teaName = await this.getTeacherName(teaId)
+                hwnum.forEach(item => {
+                    let tName = teaName.find(tea => {
+                        return tea.id === item.createId
+                    })
+                    item.name = tName ? tName.name : '-'
+                    this.hwAch.push(item)
+                })
+                this.setEcharts()
+            })
+        },
+        setEcharts() {
+            // legend名字
+            let legendData = []
+            // 最大次数
+            let maxNum = 0
+            this.hwAch.forEach(item => {
+                legendData.push(item.name)
+                maxNum = item.score.length > maxNum ? item.score.length : maxNum
+            });
+            // x轴的类别
+            let xAxisData = []
+            for (let i = 0; i < maxNum; i++) {
+                xAxisData.push(`第${i + 1}次`)
+            }
+            // 补充到最大次数的数据
+            let newAch = []
+            this.hwAch.map(item => {
+                if(item.score.length < maxNum) {
+                    let new111 = new Array(maxNum - item.score.length).fill(0)
+                    newAch.push({
+                        createId: item.createId,
+                        score: item.score.concat(new111)
+                    })
+                } else {
+                    newAch.push(item)
+                }
+            });
+            // 成绩数组
+            let tasfdsf = []
+            let newArr = new Array(maxNum)
+            this.hwAch.forEach((item, index) => {
+                tasfdsf.push([])
+                item.score.forEach((score, sIndex) => {
+                    tasfdsf[index].push(score)
+                })
+            });
+            let seriesData = []
+            this.hwAch.forEach((item, index) => {
+                seriesData.push(
+                    {
+                        name: item.name,
+                        symbol: "circle",
+                        // symbolSize: 12,
+                        type: "line",
+                        smooth: false,
+                        data: tasfdsf[index].concat(newArr.slice(0, maxNum - tasfdsf[index].length)),
+                    },
+                    {
+                        name: item.name,
+                        symbol: "circle",
+                        // symbolSize: 12,
+                        type: "line",
+                        smooth: false,
+                        itemStyle: {
+                            normal: {
+                                // color: "#95EBE1",
+                                lineStyle: {
+                                    // width: 2,
+                                    type: "dashed",
+                                },
+                            },
+                        },
+                        data: newArr.slice(0, item.score.length - 1).concat(newAch[index].score.slice(tasfdsf[index].length - 1, maxNum)),
+                    },
+                )
+            })
+            let myChart = this.$echarts.init(document.getElementById("homeworkPoint"), 'macarons')
+            let option = {
+                // color: ["#5470C6", "#5470C6", "#91CC75", "#91CC75", '#00905a', '#00905a'],
+                /* title: {
+                    text: this.$t('studentWeb.exam.chart.keyPointPerformance'),
+                    left: 'left',
+                    textStyle: {
+                        // fontWeight: "normal",
+                        fontSize: "14",
+                    }
+                }, */
+                grid: {
+                    top: '10%',
+                    bottom: '21%',
+                    // left: '80',
+                    // right: '80',
+                },
+                legend: {
+                    data: legendData,
+                    // data: ["罗老师", "陈老师", "辅导员"],
+                    bottom: 0,
+                },
+                dataZoom: [
+                    {
+						'show': true,
+						'height': 3,
+						'xAxisIndex': [0],
+						bottom: 25,
+						'start': 0,
+						'end': 100,
+						handleIcon: 'M512 497.821538m-418.264615 0a418.264615 418.264615 0 1 0 836.52923 0 418.264615 418.264615 0 1 0-836.52923 0Z',
+						handleSize: '160%',
+						handleStyle: {
+							color: '#d3dee5'
+						},
+						textStyle: {
+							color: '#717171'
+						},
+						// borderColor: '#90979c'
+					}, {
+						'type': 'inside',
+						'show': true,
+						'height': 15,
+						'start': 0,
+						'end': 100
+					}
+                ],
+                xAxis: {
+                    show: true,
+                    // name:'作业次数',
+                    nameLocation: 'end',
+                    type: 'category',
+                    data: xAxisData,
+                    axisLine: {
+                        lineStyle: {
+                            color: "#AAA"
+                        }
+                    },
+                    axisLabel: {
+                        rotate: 0
+                    }
+                },
+                yAxis: {
+                    // name:'正确率',
+                    type: 'value',
+                    axisLine: {
+                        lineStyle: {
+                            color: "#AAA"
+                        }
+                    },
+                },
+                tooltip: {
+                    trigger: 'axis',
+                    padding: [4, 12],
+                    formatter: function (params, ticket, callback) {
+                        var htmlStr = "";
+                        for (let i = 0; i < params.length; i++) {
+                            if (i === 0) {
+                                htmlStr += params[i].name + "<br/>"; //x轴的名称
+                            }
+                            if(!(i % 2)) {
+                                htmlStr += "<div>";
+                                htmlStr += '<span style="margin-right:5px;display:inline-block;width:10px;height:10px;border-radius:5px;background-color:' +
+                                            params[i].color + ';"></span>';
+                                htmlStr += params[i].seriesName + ":" + (params[i].data ? params[i].data : 0);
+                                htmlStr += "</div>";
+                            }
+                        }
+                        return htmlStr
+                    },
+                },
+                series: seriesData
+                /* [
+                    {
+                        name: '罗老师',
+                        type: 'line',
+                        data: ['87', '75', '100', '12', '46', 97, 57]
+                    },{
+                        name: '陈老师',
+                        type: 'line',
+                        data: ['42', '99', '75', '55', '76', 100, 88]
+                    },{
+                        name: '辅导员',
+                        type: 'line',
+                        data: ['97', '100', '86', '88', '33', 79, 56]
+                    },
+                ] */,
+            }
+            myChart.setOption(option)
+        },
+        getTeacherName(id) {
+            return new Promise((r, j) => {
+                this.$api.studentWeb.getTeacherName({id}).then(res => {
+                    r(res)
+                }).catch(e => {
+                    j(e)
+                })
+            })
+        },
+    },
+    mounted () {
+        this.getHwAch()
+        
+    }
+}
+</script>
+
+<style lang="less" scoped>
+#homeworkPoint {
+    width: 100%;
+    height: 253px;
+}
+</style>

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

@@ -1033,7 +1033,7 @@ export default {
             })
             param.scope === "school" ? param.school = this.userInfo.azp : param.tmdid = this.courseNow.creatorId
             this.$api.studentWeb.getClassRecord(param).then(res => {
-                if(res.lessonRecords.length) {
+                if(res.lessonRecords && res.lessonRecords.length) {
                     res.lessonRecords.forEach(item => {
                         item.startTime = this.dateFormat(item.startTime)
                         let sec = item.duration % 60

+ 97 - 0
TEAMModelOS/ClientApp/src/components/student-web/HomeView/HomeViewnnnnew.less

@@ -0,0 +1,97 @@
+.home-view {
+    padding: 0.5rem 1.7rem;
+    
+    .home-card {
+        margin-top: 15px;
+        box-shadow: 0px 2px 13px rgba(0, 0, 0, 0.1);
+        border: 1px solid rgba(0, 0, 0, 0.2);
+        border-radius: 5px;
+        background-color: #fff;
+        padding: 15px;
+
+        &:hover {
+            box-shadow: 0px 2px 30px rgba(0, 0, 0, 0.2);
+        }
+    }
+
+    .home-list-title {
+        font-size: 16px;
+        // margin-bottom: 15px;
+        display: flex;
+        justify-content: space-between;
+
+        .list-type:not(:last-child) {
+            margin-right: 20px;
+        }
+    }
+    .home-head {
+        margin-top: 15px;
+        // display: flex;
+
+        .home-echarts {
+            // width: 100%;
+            // height: 400px;
+        }
+
+        /* .class-echarts,
+        .homework-echarts {
+            width: 40%;
+        }
+
+        .exam-echarts {
+            width: 30%;
+        } */
+    }
+
+    .no-data {
+        text-align: center;
+        min-height: 455px;
+        padding-top: 80px;
+
+        img {
+            width: 50%;
+        }
+    }
+
+    .no-data-notice {
+        
+        text-align: center;
+        min-height: 212px;
+        // padding-top: 40px;
+
+        img {
+            width: 40%;
+        }
+    }
+
+    .class-record {
+        display: flex;
+        justify-content: space-between;
+        margin-bottom: 20px;
+
+        & > div > p:first-child {
+            margin-bottom: 5px;
+        }
+
+        & > div:first-child {
+            & > p:first-child {
+                font-size: 18px;
+                font-weight: bold;
+            }
+        }
+
+        & > div:last-child{
+            text-align: right;
+        }
+
+        img {
+            width: 50%;
+        }
+    }
+}
+
+@media screen and (max-width: 520px) {
+    .home-view .home-list-title {
+        display: block;
+    }
+}

+ 509 - 0
TEAMModelOS/ClientApp/src/components/student-web/HomeView/HomeViewnnnnew.vue

@@ -0,0 +1,509 @@
+<template>
+    <div class="home-view">
+        <Loading v-show="isLoading" bgColor="rgba(0, 0, 0, 0.3)"></Loading>
+        <div class="home-head home-card">
+        <Row :gutter="30">
+            <Col :xs="24" :sm="12" :md="12" :lg="8">
+                <ExamPerform />
+            </Col>
+            <Col :xs="24" :sm="12" :md="12" :lg="8">
+                <HomeworkPoint />
+            </Col>
+            <Col :xs="24" :sm="12" :md="12" :lg="8">
+                <ClassPoint />
+            </Col>
+        </Row>
+        </div>
+        <Row :gutter="30">
+            <Col :xs="24" :sm="24" :md="24" :lg="12" :xl="8">
+                <div class="home-card">
+                    <p class="home-title-name">{{ $t("studentWeb.home.title.classRecord") }}</p>
+                    <div v-if="classRecord.length" style="height: 455px;">
+                        <vuescroll>
+                            <div v-for="(classRecord, cIndex) in classRecord" :key="cIndex" class="list-new">
+                                <div class="list-new-icon">
+                                    <img src="./noData.png" alt="">
+                                </div>
+                                <div class="list-new-test">
+                                    <p>{{ classRecord.name }}</p>
+                                    <p class="list-item-time">{{ $t("studentWeb.public.uploadTime") }}:{{ classRecord.startTime }}</p>
+                                </div>
+                                <div class="list-new-type">
+                                    <p>
+                                        <Icon type="md-time" color="#24B880" />
+                                        {{ classRecord.time }}
+                                    </p>
+                                    <p style="color: #24B880;">
+                                        {{ $t("studentWeb.home.toLook") }}
+                                        <Icon type="md-arrow-round-forward" />
+                                    </p>
+                                </div>
+                            </div>
+                        </vuescroll>
+                    </div>
+                    <div v-else class="no-data">
+                        <img src="./noData.png" alt="">
+                        <p>{{ $t("studentWeb.public.noData") }}</p>
+                    </div>
+                </div>
+            </Col>
+            <Col :xs="24" :sm="24" :md="24" :lg="12" :xl="9">
+                <div class="home-card ">
+                    <div class="home-list-title">
+                        <p class="home-title-name">{{ $t("studentWeb.home.title.activity") }}</p>
+                        <p>
+                            <span class="list-type">{{ $t("studentWeb.home.exam") }}:{{ activityType.exam }}</span>
+                            <span class="list-type">{{ $t("studentWeb.home.homework") }}:{{ activityType.homework }}</span>
+                            <span class="list-type">{{ $t("studentWeb.home.vote") }}:{{ activityType.vote }}</span>
+                            <span class="list-type">{{ $t("studentWeb.home.survey") }}:{{ activityType.survey }}</span>
+                        </p>
+                    </div>
+                    <div v-if="activityList.length" style="height: 455px;">
+                        <vuescroll>
+                            <div v-for="(activity, aIndex) in activityList" :key="aIndex" class="list-new" @click="sentSelectedEventTitle(activity)">
+                                <div class="list-new-icon">
+                                    <svg-icon v-if="activity.type == 'Homework'" icon-class="doc" color="#f0be72" />
+                                    <svg-icon v-if="activity.type == 'Exam'" color="#94a1e4" icon-class="test" class="reset-testIcon" />
+                                    <svg-icon v-if="activity.type == 'Vote'" icon-class="vote" color="#6a9a8b" />
+                                    <svg-icon v-if="activity.type == 'Survey'" icon-class="quesnaire" color="#d19f9c" />
+                                </div>
+                                <div class="list-new-test">
+                                    <div class="paper-item-school">
+                                        <span class="paper-owner" v-if="activity.owner === 'school'" style="background-color: #88a1d8;">{{ $t('studentWeb.public.school') }}</span>
+                                        <span class="paper-owner" v-else>{{ $t('studentWeb.public.private') }}</span>
+                                        <template v-if="activity.type == 'Exam'">
+                                            <span class="paper-extType" :style="{'border-color': activity.owner === 'school' ? '#88a1d8' :'#ababab'}" v-if="activity.source === '0'">
+                                                {{ $t("studentWeb.exam.source.evMode1") }}
+                                            </span>
+                                            <span class="paper-source" :style="{'border-color': activity.owner === 'school' ? '#88a1d8' :'#ababab', color: '#2f98a9'}" v-if="activity.source === '1'">
+                                                {{ $t("studentWeb.exam.source.evMode2") }}
+                                            </span>
+                                            <span class="paper-extType" :style="{'border-color': activity.owner === 'school' ? '#88a1d8' :'#ababab', color: '#b68268'}" v-if="activity.source === '2'">
+                                                {{ $t("studentWeb.exam.source.evMode3") }}
+                                            </span>
+                                            <!-- 课中评量才判断:qamode:0(书面问答),1(纸本测验) -->
+                                            <template v-if="activity.source === '1'">
+                                                <span class="paper-extType" :style="{'border-color': activity.owner === 'school' ? '#88a1d8' :'#ababab', color: '#b68268'}"
+                                                    v-if="activity.qamode"
+                                                >
+                                                    {{ $t("studentWeb.exam.source.evMode21") }}
+                                                </span>
+                                                <span class="paper-extType" :style="{'border-color': activity.owner === 'school' ? '#88a1d8' :'#ababab', color: '#b68268'}"
+                                                    v-else
+                                                >
+                                                    {{ $t("studentWeb.exam.source.evMode22") }}
+                                                </span>
+                                            </template>
+                                        </template>
+                                        <template v-else>
+                                            <span class="paper-extType">
+                                                {{ activity.type === 'Vote' ? $t('studentWeb.home.vote') : (activity.type === 'Survey' ? $t('studentWeb.home.survey') : $t('studentWeb.home.homework')) }}
+                                            </span>
+                                        </template>
+                                    </div>
+                                    <p class="list-item-title">
+                                        <span>{{ activity.name }}</span>
+                                    </p>
+                                    <p style="font-size:12px; margin-top: 5px; word-break: keep-all;">
+                                        <span v-if="activity.ext">
+                                            <span v-if="activity.ext.subjects">
+                                                <span v-for="(item, index) in activity.ext.subjects" :key="index" class="tag-style"
+                                                    style="border-color: #499c8d; color: #499c8d;"
+                                                >
+                                                    {{ item.name }}
+                                                </span>
+                                            </span>
+                                        </span>
+                                        <span v-if="activity.className.length">
+                                            <span v-for="(clasNa, index) in activity.className" :key="index" class="tag-style"
+                                                style="border-color: #8f8787; color: #8f8787;">
+                                                {{ clasNa }}
+                                            </span>
+                                        </span>
+                                        <span class="tag-style" style="border-color: #6b6ba9; color: #6b6ba9;" v-if="activity.teacherName">{{ activity.teacherName }}</span>
+                                    </p>
+                                    <p class="list-item-time">
+                                        {{ dateFormat(activity.startTime) }} ~ {{ dateFormat(activity.endTime) }}
+                                    </p>
+                                </div>
+                                <div class="list-new-type">
+                                    <span style="font-size: 12px; text-align: center;">{{ activity.tempsub }}</span>
+                                    <div class="list-new-unDone isAllowRetry">
+                                        <span>去查看</span>
+                                    </div>
+                                </div>
+                            </div>
+                        </vuescroll>
+                    </div>
+                    <div v-else class="no-data">
+                        <img src="./noData.png" alt="">
+                        <p>{{ $t("studentWeb.public.noData") }}</p>
+                    </div>
+                </div>
+            </Col>
+            <Col :xs="24" :sm="24" :md="24" :lg="12" :xl="7">
+                <div class="home-card">
+                    <div>
+                        <p class="home-title-name">{{ $t("studentWeb.home.title.notice1") }}</p>
+                        <div v-if="classNotice.length" style="height: 200px; margin-bottom: 10px;">
+                            <vuescroll>
+                                <div v-for="(cNotice, nIndex) in classNotice" :key="nIndex" class="list-new">
+                                    <div class="list-new-icon">
+                                        <Icon custom="iconfont icon-notify" size="30" />
+                                    </div>
+                                    <div class="list-new-test">
+                                        <p class="list-item-title">
+                                            <span>{{ cNotice.name }}</span>
+                                        </p>
+                                        <p class="list-item-time">
+                                            {{ cNotice.time }}
+                                        </p>
+                                    </div>
+                                </div>
+                            </vuescroll>
+                        </div>
+                        <div v-else class="no-data-notice">
+                            <img src="./noData.png" alt="">
+                            <p>{{ $t("studentWeb.public.noData") }}</p>
+                        </div>
+                    </div>
+                    <div>
+                        <p class="home-title-name">{{ $t("studentWeb.home.title.notice2") }}</p>
+                        <div v-if="courseNotice.length" style="height: 200px; margin-bottom: 10px;">
+                            <vuescroll>
+                                <div v-for="(couNotice, nIndex) in courseNotice" :key="nIndex" class="list-new">
+                                    <div class="list-new-icon">
+                                        <Icon custom="iconfont icon-notify" size="30" />
+                                    </div>
+                                    <div class="list-new-test">
+                                        <p class="list-item-title">
+                                            <span>{{ couNotice.name }}</span>
+                                        </p>
+                                        <p style="font-size:12px; margin-top: 5px; word-break: keep-all;">
+                                            <span v-if="couNotice.className">
+                                                <span class="tag-style" style="border-color: #499c8d; color: #499c8d;">
+                                                    {{ couNotice.className }}
+                                                </span>
+                                            </span>
+                                            <span v-if="couNotice.teaName">
+                                                <span class="tag-style" style="border-color: #8f8787; color: #8f8787;">
+                                                    {{ couNotice.teaName }}
+                                                </span>
+                                            </span>
+                                        </p>
+                                        <p class="list-item-time">
+                                            {{ couNotice.time }}
+                                        </p>
+                                    </div>
+                                </div>
+                            </vuescroll>
+                        </div>
+                        <div v-else class="no-data-notice">
+                            <img src="./noData.png" alt="">
+                            <p>{{ $t("studentWeb.public.noData") }}</p>
+                        </div>
+                    </div>
+                </div>
+            </Col>
+        </Row>
+    </div>
+</template>
+
+<script>
+import ExamPerform from './ChartHome/ExamPerform.vue';
+import HomeworkPoint from './ChartHome/HomeworkPoint.vue';
+import ClassPoint from './ChartHome/ClassPoint.vue';
+import { mapState } from 'vuex';
+
+export default {
+    name: "HomeViewnnnnew",
+    components: {
+        ExamPerform,
+        HomeworkPoint,
+        ClassPoint,
+    },
+    // inject: ['reload'],
+    data() {
+        return {
+            MyNo: "1", //接收NavBar 選定的那一頁icon標示
+            MyName: "",
+            isLoading: false,
+            classRecord: [], //课堂记录
+            activityList: [], //活动
+            classNotice: [], //班级公告
+            courseNotice: [], //课程公告
+            countDown: null,
+            activityType: {
+                exam: 0,
+                homework: 0,
+                vote: 0,
+                survey: 0,
+            },
+            examAch: [],
+        }
+    },
+    mounted () {
+        this.MyName = this.$t("studentWeb.homeView-title")
+        this.$emit("onNavNo", this.MyNo)
+        this.$emit("onNavName", this.MyName)
+        this.getNotice()
+        this.getClaRecord()
+        this.getActivity()
+    },
+    methods: {
+        getNotice() {
+            this.courseNotice = [
+                /* {
+                    name: "测试",
+                    time: "2021-05-05 ~ 2021-05-06",
+                    content: "测试内容",
+                    className: "数学",
+                    teaName: "罗老师",
+                },{
+                    name: "测试",
+                    time: "2021-05-05 ~ 2021-05-06",
+                    content: "测试内容",
+                    className: "数学",
+                    teaName: "罗老师",
+                },{
+                    name: "测试",
+                    time: "2021-05-05 ~ 2021-05-06",
+                    content: "测试内容",
+                    className: "数学",
+                    teaName: "罗老师",
+                },{
+                    name: "测试",
+                    time: "2021-05-05 ~ 2021-05-06",
+                    content: "测试内容",
+                    className: "数学",
+                    teaName: "罗老师",
+                }, */
+            ]
+        },
+        getClaRecord() {
+            this.classRecord = [
+                /* {
+                    name: "罗老师的课堂记录",
+                    startTime: "2022-04-29",
+                    time: "00:10:58",
+                },
+                {
+                    name: "罗老师的课堂记录",
+                    startTime: "2022-04-29",
+                    time: "00:10:58",
+                },
+                {
+                    name: "罗老师的课堂记录",
+                    startTime: "2022-04-29",
+                    time: "00:10:58",
+                },
+                {
+                    name: "罗老师的课堂记录",
+                    startTime: "2022-04-29",
+                    time: "00:10:58",
+                },
+                {
+                    name: "罗老师的课堂记录",
+                    startTime: "2022-04-29",
+                    time: "00:10:58",
+                }, */
+            ]
+        },
+        getActivity(time) {
+            this.isLoading = true
+            // this.testData = []
+            // this.voteandSur = []
+            // this.examandHw = []
+            let params = {
+                userid: this.userInfo.sub,
+                userType: "",
+                school: this.userInfo.azp
+            }
+            // 醍摩豆登陆——> roles有teacher
+            params.userType = this.userInfo.scope === "tmduser" ? "tmdid" : "schoolid"
+            if(time) {
+                params.stime = time
+            }
+            this.$api.studentWeb.getActivityInfo(params).then(async res => {
+                if (res.datas.length) {
+                    let data = []
+                    let ids = []
+                    let teaIds = []
+                    let datasss = res.datas.filter(item => {
+                        item.progress = this.timeStatus(item, true)
+                        return item.progress === 'going'
+                    })
+                    datasss.forEach(item => {
+                        ids.push(item.classIds)
+                        teaIds.push(item.creatorId)
+                    })
+                    let teaidNames = []
+                    teaidNames = await this.getTeacherName(teaIds)
+                    ids = [].concat.apply([], ids)
+                    let names = ids.length ? await this.getClassName(ids, true) : []
+                    for (let item of datasss) {
+                        item.className = []
+                        item.classIds.forEach(classId => {
+                            let arr = names.filter(na => {
+                                return na.id === classId
+                            })
+                            if(arr.length) {
+                                item.className.push(arr[0].name)
+                            }
+                        });
+                        item.tempsub = ""
+                        let nameIds = undefined
+                        nameIds = teaidNames.find(names => {
+                            return names.id === item.creatorId
+                        })
+                        item.teacherName = !nameIds ? undefined : nameIds.name
+                        this.activityList.push(item)
+                        if(item.type === 'Exam') {
+                            this.activityType.exam += 1
+                        } else if(item.type === 'Homework') {
+                            this.activityType.homework += 1
+                        } else if(item.type === 'Vote') {
+                            this.activityType.vote += 1
+                        } else if(item.type === 'Survey') {
+                            this.activityType.survey += 1
+                        }
+                    }
+                    // this.activityList = [...data]
+                    let twoDay = 24 * 3600 * 1000 * 2
+                    this.countDown = setInterval(() => {
+                        let timeNow = new Date()
+                        for (const item of this.activityList) {
+                            let timeSub = item.endTime - timeNow
+                            item.tempsub = (item.taskStatus === -1 && timeSub > 0 && timeSub < twoDay) ? this.getTimeSub(timeSub) : ""
+                        }
+                    }, 1000)
+                    this.$forceUpdate()
+                }
+            }).finally(()=>{
+                this.isLoading = false
+            })
+        },
+        getClassName(ids, type) {
+            return new Promise((r, j) => {
+                let params = {ids}
+                if(type) {
+                    params.schoolId = this.userInfo.azp
+                }
+                this.$api.learnActivity.getClassNameByIds(params).then(res => {
+                    if(res.groups) {
+                        r(res.groups)
+                    } else {
+                        r([])
+                    }
+                }).catch(e => {
+                    j(e)
+                })
+            })
+        },
+        timeStatus(data, type) {
+            // type:只获取活动是否结束
+            let date = (new Date()).getTime() //当前时间
+            if (date >= data.endTime) {
+                if(type) {
+                    return 'finish'
+                } else if(data.type != 'Exam'){
+                    return 'finish'
+                } else {
+                    // 线上评测、未作答
+                    if(data.taskStatus === -1 && data.source === '0') {
+                        // 缺考
+                        return 'noAns'
+                    } else if(!data.sStatus) {
+                        // 以前的sStatus都为0,所以全是未评分
+                        // 未评分
+                        return 'noScore'
+                    } else {
+                        // 已结束
+                        return 'finish'
+                    }
+                }
+            } else {
+                return 'going'
+            }
+        },
+        //时间格式化处理
+        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() < 10 ? '0' + (date.getDate()) : date.getDate()) + ' '
+            var H = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ":"
+            var Min = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes())
+            var S = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()) + " "
+            return Y + M + D + H + Min;
+        },
+        getTimeSub(sub) {
+            // 天数
+            var days = Math.floor(sub / (24 * 3600 * 1000))
+            // 小时
+            var leave1 = sub % (24 * 3600 *1000)
+            var hours = Math.floor(leave1 / (3600 * 1000))
+            if(hours < 10) {
+                hours = "0" + hours
+            }
+            // 分钟
+            var leave2 = leave1 % (3600 * 1000)
+            var minutes = Math.floor(leave2 / (60 * 1000))
+            if(minutes < 10) {
+                minutes = "0" + minutes
+            }
+            // 秒
+            var leave3 = leave2 % (60 * 1000);
+            var seconds = Math.round(leave3 / 1000);
+            if (seconds < 10) {
+                seconds = "0" + seconds;
+            }
+            
+            return (days ? (days + this.$t("studentWeb.public.day")) : '') + hours + this.$t("studentWeb.public.hour") + minutes + this.$t("studentWeb.public.minute")
+        },
+        getTeacherName(id) {
+            return new Promise((r, j) => {
+                this.$api.studentWeb.getTeacherName({id}).then(res => {
+                    r(res)
+                }).catch(e => {
+                    j(e)
+                })
+            })
+        },
+        sentSelectedEventTitle(item) {
+            let path = item.type === 'Exam' ? '/studentWeb/examView' : (item.type === 'Homework' ? '/studentWeb/homeworkView' : '/studentWeb/eventView')
+            this.$router.push({
+                path,
+                query: {aId: item.id}
+            })
+        },
+    },
+    computed: {
+        ...mapState({
+            userInfo: state => state.userInfo,
+        })
+    },
+    beforeDestroy () {
+        clearInterval(this.countDown)
+        this.countDown = null
+    },
+}
+</script>
+
+<style lang="less" scoped>
+@import './HomeViewnnnnew.less';
+@import "~@/assets/student-web/component_styles/event-list-new.less";
+</style>
+
+<style lang="less">
+.home-view {
+    .home-title-name {
+        font-size: 16px;
+        border-left: 5px solid #24b880;
+        padding-left: 10px;
+        height: 20px;
+        line-height: 20px;
+        margin-bottom: 10px;
+    }
+}
+</style>

BIN
TEAMModelOS/ClientApp/src/components/student-web/HomeView/noData.png


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

@@ -11,6 +11,7 @@ export default {
             coreAPIUrl: 'https://api2.teammodel.cn',
             irsUrl: 'https://irs5.teammodel.cn',
 			docUrl:'http://doc.teammodel.cn:4999/web/#/7/34',
+            sokrateUrl:'https://sokrates.teammodel.cn',
             domainUrl: [
                 {
                     station: 'product',
@@ -38,6 +39,7 @@ export default {
             coreAPIUrl: 'https://api2.teammodel.net',
             irsUrl: 'https://irs5.teammodel.net',
 			docUrl:'http://doc.teammodel.cn:4999/web/#/9/37',
+            sokrateUrl:'https://sokrates.teammodel.org',
             domainUrl: [
                 {
                     station: 'product',
@@ -91,7 +93,7 @@ export default {
 
             if (!stationSetting) {
                 stationSetting = {}
-                stationSetting.station = 'localhost'
+                stationSetting.station = 'test'
                 stationSetting.srvAdr = 'China'
                 //这里异常如果本地为 null 本地测试会重复设置为null
                 // if (localStorage.getItem('srvAdr')) stationSetting.srvAdr = localStorage.getItem('srvAdr')

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

@@ -527,7 +527,7 @@ export default {
 				// 菜单栏中,标题菜单的 DOM 元素
 				// 注意,这里的 $ 不是 jQuery ,是 E.$ (wangEditor 自带的 DOM 操作工具,类似于 jQuery)
 				const $elem = $(
-					'<div class="w-e-menu" style="color:red"><i class="ivu-icon ivu-icon-md-color-palette" style="font-size: 20px;"></i></div>'
+					`<div class="w-e-menu" title="${app.$t('evaluation.editor.canvasTip')}" style="color:red"><i class="ivu-icon ivu-icon-md-color-palette" style="font-size: 20px;"></i></div>`
 				);
 				super($elem, editor);
 			}
@@ -691,7 +691,7 @@ export default {
 		class TextDot extends BtnMenu{
 			constructor(editor) {
 				const $elem = $(
-					'<div class="w-e-menu" style="color:red" title="添加着重号"><i class="icon iconfont icon-heavy" style="font-size: 20px;display: inline-block;margin-top: 3px;"></i></div>'
+					`<div class="w-e-menu" style="color:red" :title="${app.$t('evaluation.editor.addTextDot')}"><i class="icon iconfont icon-heavy" style="font-size: 20px;display: inline-block;margin-top: 3px;"></i></div>`
 				)
 				super($elem, editor)
 			}
@@ -788,7 +788,7 @@ export default {
 		// 也可以通过配置 menus 调整菜单的顺序,参考【配置菜单】部分的文档
 		editor.config.menus = editor.config.menus.concat(videoUpload);
 	},
-	/* 添加学生端画板自定义功能 */
+	/* 添加学生端画板自定义功能(含题干图片元素) */
 	addStuBgBtn(vm, editor) {
 		// 获取必要的变量,这些在下文中都会用到
 		const {
@@ -797,11 +797,11 @@ export default {
 			Panel
 		} = E;
 
-		class Kityformula extends PanelMenu {
+		class StuBgCanvas extends PanelMenu {
 			// 公式输入插件
 			constructor(editors) {
 				const $elem = $(
-					'<div class="w-e-menu" style="color:red"><i class="ivu-icon ivu-icon-logo-tumblr" style="font-size: 20px;"></i></div>'
+					`<div class="w-e-menu" title="${ app.$t('evaluation.editor.canvasStemTip') }" style="color:red"><i class="icon iconfont icon-brush" style="font-size: 20px;"></i></div>`
 				);
 				super($elem, editors);
 			}
@@ -815,7 +815,7 @@ export default {
 
 		// 注册菜单
 		const videoUpload = "connector"; // 菜单 key ,各个菜单不能重复
-		editor.menus.extend("connector", Kityformula);
+		editor.menus.extend("connector", StuBgCanvas);
 
 		// 将菜单加入到 editor.config.menus 中
 		// 也可以通过配置 menus 调整菜单的顺序,参考【配置菜单】部分的文档

+ 51 - 11
TEAMModelOS/ClientApp/src/utils/js-fn.js

@@ -226,28 +226,24 @@ function dateFormat(timestamp) {
 
 /**
  * 根据学年获取年级名称
- * @param data 学校基础数据 schoolProfile.school_base
- * @param curPd 当前学段id
  * @param year 学年
  */
-function getGradeNameByYear(data, curPd, year) {
-    if (year && year > 0 && data && data.period && curPd) {
-        let pData = data.period.find(item => {
-            return item.id == curPd
-        })
-        if (pData) {
+function getGradeNameByYear(year) {
+    if (year && year > 0) {
+        let curPeriod = store.state.user?.curPeriod
+        if (curPeriod) {
             let date = new Date()
             let curYear = date.getFullYear()
             let month = date.getMonth() + 1
-            let start = pData.semesters.find(item => {
+            let start = curPeriod.semesters.find(item => {
                 return item.start == 1
             })
             // 根据入学月份确定当前年级和学级的关系
             if (start && month < start.month) {
                 curYear--
             }
-            let res = pData.grades[curYear - year]
-            if (curYear - year >= pData.grades.length) {
+            let res = curPeriod.grades[curYear - year]
+            if (curYear - year >= curPeriod.grades.length) {
                 return app.$t('schoolBaseInfo.graduated')
             }
             return res ? res : app.$t('schoolBaseInfo.untimed')
@@ -258,6 +254,49 @@ function getGradeNameByYear(data, curPd, year) {
         return '--'
     }
 }
+
+/**
+ * 根据学年获取年级信息
+ * @param year 学年
+ * @param periodId 学段id(非必填)
+ */
+function getGradeInfoByYear(year, periodId) {
+    if (year && year > 0) {
+        let curPeriod
+        if (periodId) {
+            curPeriod = store.state.user?.schoolProfile?.school_base?.period?.find(item => item.id == periodId)
+        } else {
+            curPeriod = store.state.user?.curPeriod
+        }
+        if (curPeriod) {
+            let date = new Date()
+            let curYear = date.getFullYear()
+            let month = date.getMonth() + 1
+            let start = curPeriod.semesters.find(item => {
+                return item.start == 1
+            })
+            // 根据入学月份确定当前年级和学级的关系
+            if (start && month < start.month) {
+                curYear--
+            }
+            let res = curPeriod.grades[curYear - year]
+            let gradeName
+            if (curYear - year >= curPeriod.grades.length) {
+                gradeName = app.$t('schoolBaseInfo.graduated')
+            }
+            gradeName = res ? res : app.$t('schoolBaseInfo.untimed')
+            return {
+                id: curYear - year,
+                name: gradeName
+            }
+        } else {
+            return undefined
+        }
+    } else {
+        return undefined
+    }
+}
+
 /**
  * 根据班级学年年级名称
  * @param data 学校基础数据 schoolProfile.school_base
@@ -341,6 +380,7 @@ export default {
     uuid,
     findChartIndex,
     getGradeNameByYear,
+    getGradeInfoByYear,
     getYearByGrade,
     dateFormat,
     timeFormat,

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 592 - 603
TEAMModelOS/ClientApp/src/view/ability/Review.vue


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

@@ -248,7 +248,7 @@ export default {
             this.markers = []
             let blobInfo = this.recordInfo.scope == 'school' ? this.$store.state.user.schoolProfile : this.$store.state.user.userProfile
             // 这里需要兼容原来没有TimeLine.json的课例(优先度读timeLine.json,没有则读SokratesRecords.json)
-            let url = `${blobInfo.blob_uri}/records/${this.recordInfo.id}/IES/timeLine.json?${blobInfo.blob_sas}`
+            let url = `${blobInfo.blob_uri}/records/${this.recordInfo.id}/IES/TimeLine.json?${blobInfo.blob_sas}`
             let hasTimeLine = true
             let dataErr = false
             try {

+ 1 - 1
TEAMModelOS/ClientApp/src/view/classrecord/eventchart/PopQues.vue

@@ -66,7 +66,7 @@ export default {
                     })
                     let answerData
                     //单选题
-                    if (type === 'Single') {
+                    if (type.toLowerCase() === 'single') {
                         // 首次作答
                         if (this.evtType === 'PopQuesLoad') {
                             answerData = this._.cloneDeep(n.clientAnswers[0])

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 43 - 2
TEAMModelOS/ClientApp/src/view/dashboard/Index.vue


+ 15 - 2
TEAMModelOS/ClientApp/src/view/jyzx/application.vue

@@ -96,7 +96,7 @@
         </template>
       </Table>
     </div>
-    <Review v-if="isReview" :mode="curReviewMode" :reviewData="reviewData" @goBack="goBack"></Review>
+    <Review ref="reviewRef" v-if="isReview" :mode="curReviewMode" :reviewData="reviewData" @goBack="goBack"></Review>
     <TestPaper v-if="isShowPaper" :abilityData="curData" @goBack="goBack"></TestPaper>
   </div>
 </template>
@@ -473,7 +473,20 @@ export default {
         return (row.uploads.length && row.schoolAppraise === -1) || row.schoolAppraise === 0
       }
     }
-  }
+  },
+  beforeRouteLeave(to, from, next) {
+      let reviewRef = this.$refs.reviewRef
+			// 如果在提交材料页面并且有提交变更 则离开的时候 需要询问用户
+      if(this.isReview && reviewRef.hasModify){
+        this.$Modal.confirm({
+					title: '温馨提示',
+					content: '您存在未提交保存的修改内容,是否确认离开?',
+					onOk: () => {
+						next();
+					}
+				});
+      }
+	}
 }
 </script>
 

+ 42 - 6
TEAMModelOS/ClientApp/src/view/learnactivity/CreatePrivEva.vue

@@ -238,11 +238,12 @@ export default {
                 this.evaluationInfo.classes = []
             }
             //学校课程教学班
-            else if (this.evaluationInfo.scope == 'school' && this.evaluationInfo.targets.length && this.evaluationInfo.targets[0][1] == 'stulist') {
+            else if (this.evaluationInfo.scope == 'school' && this.evaluationInfo.targets.length && this.evaluationInfo.targets[0][1] == 'teach') {
                 this.evaluationInfo.stuLists = this.evaluationInfo.targets.map(item => {
                     return item[2]
                 })
                 this.evaluationInfo.classes = []
+                this.evaluationInfo.grades = []
             }
             // 学校课程行政班
             else {
@@ -250,15 +251,48 @@ export default {
                     return item[2]
                 })
                 this.evaluationInfo.stuLists = []
+                //补充年级信息
+                this.evaluationInfo.grades = []
+                let courseClassList = data.course?.children?.find(item => item.id == 'class')?.children
+                if (courseClassList && courseClassList.length) {
+                    let sltClass = courseClassList.filter(item => this.evaluationInfo.classes.includes(item.id))
+                    let grades = Array.from(new Set(sltClass?.map(item => item.year)))
+
+                    let gradesInfo = grades.map(item => {
+                        return this.$jsFn.getGradeInfoByYear(item, data.course.periodId)
+                    })
+                    gradesInfo = gradesInfo.filter(item => !!item)
+                    gradesInfo.forEach(item=>{
+                        item.id = item.id.toString()
+                    })
+                    this.evaluationInfo.grades = gradesInfo
+                }
             }
 
             //2、设置评测“学科”
             this.evaluationInfo.subjects = []
-            if (this.evaluationInfo.targets.length && data.course) {
-                this.evaluationInfo.subjects.push({
-                    id: data.course.id,
-                    name: data.course.name
-                })
+            // 个人课程用课程名称代替学科
+            if (this.evaluationInfo.scope == 'private') {
+                if (data.targets.length && data.course) {
+                    this.evaluationInfo.subjects.push({
+                        id: data.course.id,
+                        name: data.course.name
+                    })
+                }
+                this.evaluationInfo.period = undefined
+            }
+            //校本课程用课程关联的学生,并补充学段信息
+            else {
+                if (data.targets.length && data.course) {
+                    this.evaluationInfo.subjects.push({
+                        id: data.course.subjectId,
+                        name: data.course.subject
+                    })
+                    this.evaluationInfo.period = {
+                        id: data.course.periodId,
+                        name: data.course.period
+                    }
+                }
             }
         },
         /*
@@ -380,7 +414,9 @@ export default {
                 name: this.evaluationInfo.name,
                 creatorId: this.$store.state.userInfo.TEAMModelId,
                 type: this.evaluationInfo.type,
+                period: this.evaluationInfo.period,
                 subjects: this.evaluationInfo.subjects,
+                grades: this.evaluationInfo.grades,
                 papers: apiPapers,
                 examType: this.evaluationInfo.examType,
                 year: new Date().getFullYear(),

+ 6 - 1
TEAMModelOS/ClientApp/src/view/login/page/Student.vue

@@ -449,7 +449,12 @@
 									id: this.loginForm.id,
 									pass: this.loginForm.pass
 								}).then(res => {
-									result = res
+									if(!res.error){
+										result = res
+									}else{
+										this.$Message.warning(res.message)
+										this.loading = false
+									}
 								}).catch(err => {
 									isFail = true
 								})

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

@@ -31,7 +31,7 @@
                         <div class="class-list-item-left">
                             <p style="color:var(--primary-text-color);font-size: 12px;">
                                 {{item.year ? item.year+$t('unit.gradeYear') : $t('schoolBaseInfo.noSet')}}
-                                <span v-show="item.year" style="color: var(--second-text-color)">{{`(${$jsFn.getGradeNameByYear(schoolBase, filterPeriod, item.year)})`}}</span>
+                                <span v-show="item.year" style="color: var(--second-text-color)">{{`(${$jsFn.getGradeNameByYear(item.year)})`}}</span>
                             </p>
                             <p class="class-name">
                                 <span class="class-id-tag">

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

@@ -94,7 +94,7 @@
                     <p>{{(row.pw == '' || row.pw == null)? '— —' : row.pw+''}}</p>
                 </template>
                 <template slot-scope="{ row }" slot="classYear">
-                    <p>{{ `${$jsFn.getGradeNameByYear(schoolBase, period, row.classYear)}(${row.classYear}${$t('unit.gradeYear')})` }}</p>
+                    <p>{{ `${$jsFn.getGradeNameByYear(row.classYear)}(${row.classYear}${$t('unit.gradeYear')})` }}</p>
                 </template>
                 <template slot-scope="{ row,index }" slot="status">
                     <!-- Excel 錯誤 -->

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

@@ -73,7 +73,7 @@
                     </template>
                     <template slot-scope="{ row }" slot="gradeName">
                         <span :style="{color: row.classYear > 0 ? '#515a6e':'#ed4014'}">
-                            {{ row.classYear > 0 ? $jsFn.getGradeNameByYear(schoolBase, searchPeriod, row.classYear) : $t('stuAccount.noRelClass')}}
+                            {{ row.classYear > 0 ? $jsFn.getGradeNameByYear(row.classYear) : $t('stuAccount.noRelClass')}}
                         </span>
                     </template>
                     <template slot-scope="{ row }" slot="action">

+ 178 - 0
TEAMModelOS/ClientApp/src/view/student-web/App.less

@@ -0,0 +1,178 @@
+.myNav {
+    height: 45px;
+    line-height: 45px;
+    background-color: #FFF;
+    border-bottom: 1px solid #dcdee2;
+
+    display: flex;
+    justify-content: space-between;
+
+
+    .sidebar-toggle {
+        padding: 0px 10px;
+        position: relative;
+        top: -3px;
+        left: 10px;
+
+        .menu-icon {
+            color: rgb(2, 179, 90) !important;
+            cursor: pointer;
+        }
+
+        .menu-icon-close {
+            color: rgb(105, 105, 105) !important;
+        }
+    }
+
+    .mytitle {
+        padding: 0rem 1rem;
+        color: rgb(2, 179, 90);
+        font-weight: bolder;
+        position: relative;
+        font-size: 20px;
+    }
+
+    .no-show {
+        margin-left: 6px;
+    }
+
+    .tabIcon1 {
+        position: relative;
+        font-size: 18px;
+    }
+
+    .tabIcon2 {
+        position: relative;
+        font-size: 18px;
+        top: 2px;
+    }
+
+    .tabIcon3 {
+        position: relative;
+        font-size: 18px;
+        top: 1px;
+    }
+
+    .tabIcon4 {
+        position: relative;
+        font-size: 18px;
+        top: 1px;
+        margin-left: 4px;
+    }
+
+    .tab-list {
+        cursor: pointer;
+        margin: 0 20px;
+    }
+
+
+    .stu-photo {
+        margin: 0px 25px 0 10px;
+        cursor: pointer;
+        display: flex;
+
+        img {
+            margin-top: 5px;
+            border-radius: 50%;
+        }
+    }
+
+    .addClass {
+        margin-top: 8px;
+
+        button {
+            color: #ffffff;
+            background-color: #64AE16;
+            border-top-left-radius: 0;
+            border-bottom-left-radius: 0;
+        }
+    }
+
+    .profile-dropdown {
+
+        #stuPhoto {
+            margin: 0px 25px 0 10px;
+            cursor: pointer;
+            display: flex;
+
+            img {
+                border-radius: 50%;
+                margin-top: 5px;
+            }
+        }
+
+        &>p {
+            float: right;
+            margin-right: 48px;
+            font-weight: bold;
+        }
+
+        &:hover {
+            #profile-pop {
+                display: block;
+                position: fixed;
+                border-radius: 5px;
+                background-color: rgb(255, 255, 255);
+                z-index: 902;
+                top: 44px;
+                right: 15px;
+                width: 170px;
+                height: auto;
+                box-shadow: 1px 8px 20px rgba(0, 0, 0, 0.2);
+                padding: 2px 0px;
+            }
+        }
+    }
+
+
+    /***個人設定彈窗 */
+    #profile-pop {
+        display: none;
+
+        .profile-pop-item {
+            cursor: pointer;
+            font-weight: bold;
+            padding: 0px 10px;
+            // border-top: 1px solid rgba(0, 0, 0, 0.1);
+            color: #575757;
+
+            &:hover {
+                background: linear-gradient(-0.75turn, #fafafa, #d4ede1);
+                color: #03966a;
+                cursor: pointer;
+            }
+        }
+
+        .profile-name {
+            font-size: 12px;
+            padding: 0px 10px;
+            color: #03966a;
+            border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+            margin-bottom: 5px;
+        }
+
+        .profile-pop-icon {
+            width: 25px;
+            text-align: center;
+
+            &-1 {
+                font-size: 17px;
+            }
+
+            &-2 {
+                font-size: 15px;
+            }
+        }
+    }
+}
+
+@media screen and (max-width: 1367px) {
+    .myNav .no-show {
+        display: none;
+    }
+}
+@media screen and (max-width: 520px) {
+    .myNav .no-show-title {
+        display: none;
+    }
+}

+ 327 - 0
TEAMModelOS/ClientApp/src/view/student-web/AppNew.vue

@@ -0,0 +1,327 @@
+<template>
+    <div id="app" :style="{ 'background-color': pagebgc }">
+        <!--導航列-->
+        <div class="myNav">
+            <div>
+                <span @click="clickSidebarToggle()"
+                    v-if="MyNo != '1' && MyNo != '4' && MyNo != 'X' && MyNo != '7'"
+                    class="sidebar-toggle">
+                    <Icon class="menu-icon" type="md-menu"
+                        :class="{'menu-icon-close': this.$store.getters.getSidebarisOpen == false}" />
+                </span>
+                <span :class="['mytitle', {'no-show-title': MyNo === '1'}]">{{ MyName }}</span>
+            </div>
+            <div class="addClass" v-show="MyNo === '1'">
+                <Input v-special-char clearable
+                    v-model="addClassNo"
+                    :placeholder="$t('studentWeb.home.classPla')"
+                >
+                    <Button slot="append" @click="addClass">{{ $t('studentWeb.home.joinClass') }}</Button>
+                </Input>
+            </div>
+            <div style="display: flex;">
+                <Menu mode="horizontal" :active-name="MyNo" class="menubar" v-if="windowWidth > 851">
+                    <MenuItem name="1" to="/studentWeb/homeView" :title="$t('studentWeb.type.home')">
+                        <svg-icon icon-class="home" class="tabIcon2" />
+                        <span class="no-show" v-show="MyNo != 1">{{ $t('studentWeb.type.home') }}</span>
+                    </MenuItem>
+                    <MenuItem name="2" to="/studentWeb/courseList" :title="$t('studentWeb.courseList-title')">
+                        <svg-icon icon-class="course" class="tabIcon1" />
+                        <span class="no-show" v-show="MyNo != 2">{{ $t('studentWeb.course') }}</span>
+                    </MenuItem>
+                    <MenuItem name="5" to="/studentWeb/examView" :title="$t('studentWeb.type.exam')">
+                        <svg-icon icon-class="test" class="tabIcon3" />
+                        <span class="no-show" v-show="MyNo != 5">{{ $t('studentWeb.type.exam') }}</span>
+                    </MenuItem>
+                    <MenuItem name="6" to="/studentWeb/homeworkView" :title="$t('studentWeb.type.homework')">
+                        <svg-icon icon-class="doc" class="tabIcon3" />
+                        <span class="no-show" v-show="MyNo != 6">{{ $t('studentWeb.type.homework') }}</span>
+                    </MenuItem>
+                    <MenuItem name="3" to="/studentWeb/eventView" :title="$t('studentWeb.type.activity')">
+                        <svg-icon icon-class="selflearning" class="tabIcon4" />
+                        <span class="no-show" v-show="MyNo != 3">{{ $t('studentWeb.type.activity') }}</span>
+                    </MenuItem>
+                    <MenuItem name="7" to="/studentWeb/achievement" :title="$t('studentWeb.type.achievement')">
+                        <Icon custom="iconfont icon-chengjitongji" size="18" style="font-weight: bold" />
+                        <span class="no-show" v-show="MyNo != 7">{{ $t('studentWeb.type.achievement') }}</span>
+                    </MenuItem>
+                    <MenuItem name="4" :to="getSrvAdr" target="_blank" :title="$t('studentWeb.type.classInteraction')">
+                        <Icon custom="iconfont icon-ketanghudonghangwei" size="18" style="font-weight: bold" />
+                        <span class="no-show" v-show="MyNo != 4">{{ $t('studentWeb.type.classInteraction') }}</span>
+                    </MenuItem>
+                </Menu>
+                <!-- 头像 -->
+                <div class="profile-dropdown">
+                    <div id="stuPhoto" v-if="userInfo.picture">
+                        <img :src="userInfo.picture" alt="" width="35" height="35">
+                        <span style="margin-left: 5px;">{{ userInfo.name }}</span>
+                    </div>
+                    <div id="profile-pop">
+                        <p class="profile-name">{{ userInfo.name }} ({{ userInfo.studentId }})</p>
+                        <template v-if="windowWidth < 852">
+                            <div class="profile-pop-item" @click="goto('/studentWeb/homeView')">
+                                <svg-icon icon-class="home" class="profile-pop-icon profile-pop-icon-1" />
+                                {{ $t('studentWeb.type.home') }}
+                            </div>
+                            <div class="profile-pop-item" @click="goto('/studentWeb/courseList')">
+                                <svg-icon icon-class="course" class="profile-pop-icon profile-pop-icon-1" />
+                                {{ $t('studentWeb.course') }}
+                            </div>
+                            <div class="profile-pop-item" @click="goto('/studentWeb/examView')">
+                                <svg-icon icon-class="test" style="margin-left: 2px; margin-right: -2px;" class="profile-pop-icon profile-pop-icon-1" />
+                                {{ $t('studentWeb.type.exam') }}
+                            </div>
+                            <div class="profile-pop-item" @click="goto('/studentWeb/homeworkView')">
+                                <svg-icon icon-class="doc" class="profile-pop-icon profile-pop-icon-1" />
+                                {{ $t('studentWeb.type.homework') }}
+                            </div>
+                            <div class="profile-pop-item" @click="goto('/studentWeb/eventView')">
+                                <svg-icon icon-class="selflearning" style="margin-left: 3px; margin-right: -3px;" class="profile-pop-icon profile-pop-icon-1" />
+                                {{ $t('studentWeb.type.activity') }}
+                            </div>
+                            <div class="profile-pop-item" @click="goto('/studentWeb/achievement')">
+                                <Icon custom="iconfont icon-chengjitongji" size="16" class="profile-pop-icon" style="font-weight: bold;" />
+                                {{ $t('studentWeb.type.achievement') }}
+                            </div>
+                            <div class="profile-pop-item" @click="goto(getSrvAdr, true)">
+                                <Icon custom="iconfont icon-ketanghudonghangwei" size="16" class="profile-pop-icon" style="font-weight: bold;" />
+                                {{ $t('studentWeb.type.classInteraction') }}
+                            </div>
+                        </template>
+                        <div class="profile-pop-item" @click="onSet">
+                            <svg-icon icon-class="setting" class="profile-pop-icon profile-pop-icon-2" />
+                            {{ $t("studentWeb.home.setting") }}
+                        </div>
+                        <div class="profile-pop-item" @click="onQuit">
+                            <svg-icon icon-class="logout" class="profile-pop-icon profile-pop-icon-1" />
+                            {{ $t("studentWeb.home.logout") }}
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <!--內容區-->
+        <div class="myContent">
+            <router-view @onNavNo="getNavNo"
+                         @onNavName="getNavName"
+                         @onMessage="getmessage"
+                         v-if="isRouterAlive">
+            </router-view>
+        </div>
+    </div>
+</template>
+
+<script>
+    import jwtDecode from 'jwt-decode'
+    import PersonalPhoto from '@/components/public/personalPhoto/Index.vue'
+    export default {
+        name: "App",
+        components: {
+            jwtDecode,
+            PersonalPhoto
+        },
+        provide() {
+            return {
+                reload: this.reload,
+            };
+        },
+        data() {
+            return {
+                courseID: "",
+                MyNo: "", //接收NavBar 選定的那一頁icon標示
+                MyName: "", //接收NavBar 那一頁的名字
+                isRouterAlive: true,
+                windowWidth: window.innerWidth,
+                windowHeight: window.innerHeight,
+                isTyping: false,
+                curRole: '',
+                users: '',
+                identity: sessionStorage.getItem('identity'),
+                userInfo: {},
+                isAddClass: false,
+                addClassNo: '',
+            };
+        },
+        computed: {
+            getRoleName() {
+                return val => {
+                    // return val === 'student' ? '学生' : '教师'
+                    return val === 'student' ? this.$t("studentWeb.home.student") : this.$t("studentWeb.home.teacher")
+                }
+            },
+            //判断用户权限
+            isTeacher() {
+                let state = false
+                if (this.users.roles.length) {
+                    for (let item of this.users.roles) {
+                        if (item == 'teacher' || item == 'admin') {
+                            state = true
+                        }
+                    }
+                }
+                return state
+            },
+            getSrvAdr() {
+                let id_token = localStorage.getItem("stu_id_token")
+                let auth_token = localStorage.getItem("stu_auth_token")
+                // 醍摩豆id传id,学校账号传auth
+                let params = this.$store.state.userInfo.scope === "tmduser" ? ('id=' + id_token) : ('auth=' + auth_token)
+                let srvAdr = this.$store.state.config.srvAdr // Global:国际站   China:正式站/测试站
+                let url = srvAdr === 'Global' ? this.$store.state.config.Global.irsUrl : this.$store.state.config.China.irsUrl
+				return `${url}?${params}`
+            },
+            pagebgc() {
+                if (this.MyNo == "1") {
+                    return "#f0f0f0";
+                } else {
+                    return ""
+                }
+            },
+        },
+        watch: {
+            courseID: function (value) {
+                if (value != "") {
+                    this.isTyping = true
+                } else if (value == "") {
+                    this.isTyping = false
+                }
+            },
+        },
+        created() {
+            this.getUsers();
+            this.$store.dispatch('user/checkStudentProfile');// 檢查學生的詳細資訊,刷新后同步localStorage数据
+        },
+        mounted() {
+            this.getStudentData()
+            this.$nextTick(() => {
+                window.addEventListener("resize", this.onResize);
+            });
+        },
+        beforeDestroy() {
+            window.removeEventListener("resize", this.onResize);
+        },
+        methods: {
+            sentcourseID() {
+                this.$store.commit('searchCourseIDforAdd', this.courseID.trim())
+            },
+            onResize() {
+                this.windowWidth = window.innerWidth;
+                this.windowHeight = window.innerHeight;
+            },
+            clickSidebarToggle() {
+                this.$store.commit("ToggleSidebar");
+            },
+            noData() {
+                this.$Message.warning(this.$t("studentWeb.public.notice"))
+            },
+            reload() {
+                // 刷新页面的作用
+                this.isRouterAlive = false;
+                this.$nextTick(() => (this.isRouterAlive = true));
+            },
+            getNavNo: function (MyNo) {
+                this.MyNo = MyNo;
+            },
+            getNavName: function (MyName) {
+                this.MyName = MyName;
+                if(MyName == this.$t("studentWeb.courseList-title") || MyName == this.$t("studentWeb.homeView-title")) {
+                    this.isAddClass = true
+                } else {
+                    this.isAddClass = false
+                }
+            },
+            getUsers() {
+                this.users = JSON.parse(decodeURIComponent(sessionStorage.userInfo, "utf-8"));
+                let data = jwtDecode(localStorage.getItem('stu_auth_token'))
+                if (data.name !== "") {
+                    this.$store.commit("setUserInfo", data)
+                    this.$store.commit("setStuUserInfo", data)
+                    sessionStorage.setItem("userInfo", encodeURIComponent(JSON.stringify(data)))
+                }
+                if (this.users.roles.length) {
+                    this.curRole = sessionStorage.getItem('identity')
+                }
+            },
+            getStudentData() {
+                this.userInfo = this.$store.getters.getStuUserInfo
+            },
+            onRoleSelect(val) {
+                if (sessionStorage.getItem('identity') != val) {
+                    this.curRole = val
+                    let path = val === 'student' ? '/studentWeb' : '/home'
+                    sessionStorage.setItem('identity', val)
+                    this.$router.push({ path: path })
+                }
+            },
+            onQuit() {
+                this.$store.commit('user/resetSchoolProfile')
+                this.$store.commit("ChangeItemName", null)
+                localStorage.removeItem('Item')
+                localStorage.removeItem('subjectNow')
+                localStorage.removeItem('examInfo')
+                localStorage.removeItem('paperInfo')
+                this.$User.logout()
+                this.$router.push({
+                    path: '/login'
+                })
+            },
+            onSet() {
+                this.$router.push({
+                    path: '/studentWeb/setting'
+                })
+            },
+            getmessage(val) {
+                this.userInfo = val
+                this.$forceUpdate()
+            },
+            // 加入课程
+            addClass() {
+                if(this.addClassNo) {
+                    let isStu = this.userInfo.scope != "tmduser"
+                    let req = {
+                        stuListNo: this.addClassNo,
+                        studentId: "",
+                        tmdId: ""
+                    }
+                    if(isStu) {
+                        req.studentId = this.userInfo.sub
+                    } else {
+                        req.tmdId = this.userInfo.sub
+                    }
+                    this.$api.studentWeb.getAddClass(req).then(res => {
+                        // -1 课程编码错误,0加入成功,1参数异常,2重复加入,4表示个人名单未开放加入 
+                        if(res.status == 0) {
+                            this.$Message.success(this.$t('studentWeb.courseType.success'))
+                            this.addClassNo = ""
+                            // 加入成功后,刷新首页
+                            this.reload()
+                        } else if(res.status == 2) {
+                            this.$Message.warning(this.$t('studentWeb.courseType.warning'))
+                        } else  if(res.status == -1){
+                            this.$Message.warning(this.$t('studentWeb.courseType.error'))
+                        } else if(res.status === 4) {
+                            this.$Message.warning(this.$t('studentWeb.courseType.noAgree'))
+                        } else {
+                            this.$Message.error(this.$t('studentWeb.courseType.api'))
+                        }
+                    })
+                }
+            },
+            goto(path, blank) {
+                if(blank) {
+                    window.open(path, '_blank');
+                } else {
+                    this.$router.push(path)
+                }
+            },
+        },
+    };
+</script>
+
+<style lang="less" scoped>
+@import "./App.less";
+</style>
+
+<style lang="less">
+@import "./AppiView.less";
+</style>

+ 55 - 0
TEAMModelOS/ClientApp/src/view/student-web/AppiView.less

@@ -0,0 +1,55 @@
+.myNav {
+    .ivu-menu-horizontal {
+        height: 45px !important;
+        // padding-top: 4px;
+        line-height: 45px !important;
+        // padding-bottom: 4px;
+
+        .ivu-menu-item {
+            padding: 0px 20px !important;
+            border-bottom: 3px solid transparent !important;
+            // bottom: 1px;
+        }
+    }
+
+    .ivu-menu-light.ivu-menu-horizontal .ivu-menu-item-active,
+    .ivu-menu-light.ivu-menu-horizontal .ivu-menu-item:hover,
+    .ivu-menu-light.ivu-menu-horizontal .ivu-menu-submenu-active,
+    .ivu-menu-light.ivu-menu-horizontal .ivu-menu-submenu:hover {
+        color: rgb(2, 179, 90) !important;
+        border-bottom: 3px solid rgb(2, 179, 90) !important;
+        transition: 0.5s;
+        // bottom: 2px;
+    }
+
+    .ivu-select-dropdown {
+        top: 39px !important;
+        // margin-right: 20px;
+        // margin-top: -13px;
+    }
+    .ivu-dropdown-menu{
+        min-width: 200px;
+    }
+
+    .ivu-dropdown {
+        margin-right: 20px;
+    }
+
+    .addClass {
+        .ivu-input-icon {
+            line-height: 30px;
+            right: 90px;
+        }
+
+        .ivu-input,
+        .ivu-btn {
+            height: 28px;
+        }
+    }
+}
+
+@media screen and (max-width: 1367px) {
+    .myNav .ivu-menu-horizontal .ivu-menu-item {
+        padding: 0 11px !important;
+    }
+}

+ 159 - 0
TEAMModelOS/Controllers/Client/HabbController.cs

@@ -0,0 +1,159 @@
+using Azure.Cosmos;
+using Azure.Storage.Blobs.Models;
+using Azure.Storage.Sas;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Options;
+using System;
+using System.Collections.Generic;
+using System.IdentityModel.Tokens.Jwt;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.Models;
+using TEAMModelOS.Filter;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Extension;
+using Microsoft.Azure.Cosmos.Table;
+using TEAMModelOS.SDK.Models;
+
+namespace TEAMModelOS.Controllers.Client
+{
+    [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status400BadRequest)]
+    [Route("habb")]
+    [ApiController]
+    
+    public class HabbController : ControllerBase
+    {
+        private readonly AzureCosmosFactory _azureCosmos;
+        private readonly DingDing _dingDing;
+        private readonly Option _option;
+
+        public HabbController(
+            AzureCosmosFactory azureCosmos,
+            DingDing dingDing,
+            IOptionsSnapshot<Option> option)
+        {
+            _azureCosmos = azureCosmos;
+            _dingDing = dingDing;
+            _option = option?.Value;
+        }
+
+        [ProducesDefaultResponseType]
+        [Authorize(Roles = "BB")]
+        [HttpPost("get-school-prod-service")]
+        public async Task<IActionResult> GetSchoolProd(JsonElement request)
+        {
+            //Debug
+            //string json = System.Text.Json.JsonSerializer.Serialize(id_token);
+            try
+            {
+                if (!request.TryGetProperty("school_code", out JsonElement schoolCodeJson)) return BadRequest();
+                string schoolCode = schoolCodeJson.ToString();
+                if(string.IsNullOrWhiteSpace(schoolCode)) return BadRequest();
+
+                int err = 0;
+                string msg = string.Empty;
+                List<HabbServiceProd> prod = new List<HabbServiceProd>();
+
+                //取得學校基本資料
+                int schoolSize = 0;
+                var client = _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School");
+                var responsesch = await client.ReadItemStreamAsync(schoolCode, new PartitionKey($"Base"));
+                if (responsesch.Status == 200)
+                {
+                    var jsons = await JsonDocument.ParseAsync(responsesch.ContentStream);
+                    if (jsons.RootElement.TryGetProperty("size", out JsonElement sizeJobj))
+                    {
+                        if(sizeJobj.TryGetInt32(out int sizeJobjInt))
+                        {
+                            schoolSize = sizeJobjInt;
+                        }
+                    }
+                }
+                else //無此學校資料
+                {
+                    err = 1;
+                    msg = "Invalid School code";
+                }
+
+                //取得各產品目前可得數量 (目前先只取服務,序號/硬體有需要再追加)
+                List<SchoolProductSumDataService> service = new List<SchoolProductSumDataService>();
+                var responseSchPdsm = await client.ReadItemStreamAsync(schoolCode, new PartitionKey($"ProductSum"));
+                if (responseSchPdsm.Status == 200)
+                {
+                    using var json = await JsonDocument.ParseAsync(responseSchPdsm.ContentStream);
+                    SchoolProductSum productSum = json.ToObject<SchoolProductSum>();
+                    service = productSum.service;
+                }
+                //取得服務產品週期(只取授權效期中)
+                long today = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
+                Dictionary<string, List<HabbServiceProdPeriod>> prodPeriodDic = new Dictionary<string, List<HabbServiceProdPeriod>>();
+                await foreach (var itemsv in client.GetItemQueryStreamIterator(queryText: $"SELECT * FROM c WHERE c.dataType = 'servicePeriod' AND c.startDate <= {today} AND {today} <= c.endDate", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Product-{schoolCode}") }))
+                {
+                    using var json = await JsonDocument.ParseAsync(itemsv.ContentStream);
+                    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                    {
+                        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                        {
+                            SchoolProductServicePeriod servicePeriodRow = obj.ToObject<SchoolProductServicePeriod>();
+                            string prodCode = servicePeriodRow.prodCode;
+                            if (!prodPeriodDic.ContainsKey(prodCode))
+                            {
+                                prodPeriodDic[prodCode] = new List<HabbServiceProdPeriod>();
+                            }
+                            HabbServiceProdPeriod HabbPeriodNow = new HabbServiceProdPeriod();
+                            HabbPeriodNow.id = servicePeriodRow.id;
+                            HabbPeriodNow.startDate = servicePeriodRow.startDate;
+                            HabbPeriodNow.endDate = servicePeriodRow.endDate;
+                            HabbPeriodNow.number = servicePeriodRow.number;
+                            HabbPeriodNow.unit = servicePeriodRow.unit;
+                            prodPeriodDic[prodCode].Add(HabbPeriodNow);
+                        }
+                    }
+                }
+
+                //結果生成
+                foreach (SchoolProductSumData serviceRow in service)
+                {
+                    HabbServiceProd resultRow = new HabbServiceProd();
+                    resultRow.avaliable = serviceRow.avaliable;
+                    resultRow.prodCode = serviceRow.prodCode;
+                    if(prodPeriodDic.ContainsKey(serviceRow.prodCode))
+                    {
+                        resultRow.period = prodPeriodDic[serviceRow.prodCode];
+                    }
+                    prod.Add(resultRow);
+                }
+
+                return Ok(new { err, msg, prod, schoolSize });
+            }
+            catch (Exception ex)
+            {
+               // await _dingDing.SendBotMsg($"CoreAPI2,{_option.Location},hiteach/GetTeacherInfo()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
+                return BadRequest();
+            }
+        }
+
+        public class HabbServiceProd
+        {
+            public string prodCode { get; set; }
+            public int avaliable { get; set; }
+            public List<HabbServiceProdPeriod> period { get; set; }
+        }
+
+        public class HabbServiceProdPeriod
+        {
+            public string id { get; set; }
+            public long startDate { get; set; }
+            public long endDate { get; set; }
+            public int number { get; set; }
+            public string unit { get; set; }
+        }
+
+    }
+}

+ 44 - 17
TEAMModelOS/Controllers/School/SchoolController.cs

@@ -293,7 +293,7 @@ namespace TEAMModelOS.Controllers
         }
 
         /// <summary>
-        /// 取得某學校產品資料 
+        /// 取得某學校產品購買紀錄
         /// </summary>
         /// <param name="request"></param>
         /// <returns></returns>
@@ -311,7 +311,7 @@ namespace TEAMModelOS.Controllers
                 List<SerialInfoBaseWithdeviceBoundExt> serialResult = new List<SerialInfoBaseWithdeviceBoundExt>(); //最後要輸出的序號結果
                 List<SchoolProductService> serviceProduct = new List<SchoolProductService>(); //承接DB資料用:服務
                 List<SchoolProductOrder> serviceOrder = new List<SchoolProductOrder>(); //服務各產品購買紀錄
-                List<SchoolProductSumData> serviceSum = new List<SchoolProductSumData>(); //服務各產品購買紀錄
+                List<SchoolProductSumDataService> serviceSum = new List<SchoolProductSumDataService>(); //服務各產品購買紀錄
                 List<SchoolProductHard> hard = new List<SchoolProductHard>(); //承接DB資料用:硬體
                 SchoolProductSum productSum = new SchoolProductSum(); //承接DB資料用:產品目前狀態 
                 List<SchoolProductSumProdInfo> prodinfo = new List<SchoolProductSumProdInfo>(); //產品資訊列表(名稱、八碼)
@@ -386,20 +386,49 @@ namespace TEAMModelOS.Controllers
                             }
                         }
                     }
+                    if (deviceBoundArray.Count.Equals(0)) //無法取得CS的硬體資訊,則用序號的硬體資訊帶入
+                    {
+                        if(serialRow.deviceBound != null)
+                        {
+                            foreach (deviceBound serialRowDeviceBound in serialRow.deviceBound)
+                            {
+                                if(!string.IsNullOrEmpty(serialRowDeviceBound.uuid) || !string.IsNullOrEmpty(serialRowDeviceBound.uuid2))
+                                {
+                                    deviceBoundExt deviceBoundExt = new deviceBoundExt();
+                                    deviceBoundExt.uuid = serialRowDeviceBound.uuid;
+                                    deviceBoundExt.uuid2 = serialRowDeviceBound.uuid2;
+                                    deviceBoundExt.classId = serialRowDeviceBound.classId;
+                                    deviceBoundExt.deviceId = serialRowDeviceBound.deviceId;
+                                    deviceBoundArray.Add(deviceBoundExt);
+                                }
+                            }
+                        }
+                    }
                     //序號更新
                     await clientContainer.ReplaceItemAsync<SchoolProductSerial>(serialRow, serialRow.id, new PartitionKey($"Product-{school_code}"));
                     //回傳值
                     SerialInfoBaseWithdeviceBoundExt serialResultRow = new SerialInfoBaseWithdeviceBoundExt();
+                    serialResultRow.code = serialRow.code;
+                    serialResultRow.id = serialRow.id;
+                    serialResultRow.dataType = serialRow.dataType;
                     serialResultRow.serial = serialRow.serial;
+                    serialResultRow.prodCode = serialRow.prodCode;
                     serialResultRow.clientQty = serialRow.clientQty;
+                    serialResultRow.orderDate = serialRow.orderDate;
                     serialResultRow.regDate = serialRow.regDate;
                     serialResultRow.startDate = serialRow.startDate;
                     serialResultRow.endDate = serialRow.endDate;
                     serialResultRow.deviceMax = serialRow.deviceMax;
-                    serialResultRow.aprule = serialRow.aprule;
                     serialResultRow.expireStatus = serialRow.expireStatus;
                     serialResultRow.status = serialRow.status;
                     serialResultRow.deviceBound = deviceBoundArray;
+                    if (serialRow.aprule != null)
+                    {
+                        string apruleJsonString = serialRow.aprule.ToJsonString();
+                        var apruleDc = JsonDocument.Parse(apruleJsonString);
+                        serialResultRow.aprule = (apruleDc.RootElement.TryGetProperty("json", out JsonElement apruleJson)) ? apruleJson : null;
+                    }
+                    else serialResultRow.aprule = null;
                     serialResult.Add(serialResultRow);
                 }
 
@@ -519,8 +548,10 @@ namespace TEAMModelOS.Controllers
                 foreach (SchoolProductService serviceProductRow in serviceProduct)
                 {
                     //取得此產品現在可用數
-                    SchoolProductSumData serviceSumNow = serviceSum.Where(ss => ss.prodCode.Equals(serviceProductRow.prodCode)).FirstOrDefault();
+                    SchoolProductSumDataService serviceSumNow = serviceSum.Where(ss => ss.prodCode.Equals(serviceProductRow.prodCode)).FirstOrDefault();
                     int serviceAvaliableNow = (serviceSumNow != null) ? serviceSumNow.avaliable : 0;
+                    long serviceAvaliableStartDate = (serviceSumNow != null) ? serviceSumNow.startDate : 0;
+                    long serviceAvaliableEndDate = (serviceSumNow != null) ? serviceSumNow.endDate : 0;
                     //取得此產品購買履歷
                     SchoolProductOrder serviceOrderNow = serviceOrder.Where(so => so.prodCode == serviceProductRow.prodCode).FirstOrDefault();
                     if (serviceOrderNow == null) //無此產品項
@@ -530,33 +561,29 @@ namespace TEAMModelOS.Controllers
                         serviceOrderRow.order = new List<SchoolProductOrderList>();
                         SchoolProductOrderList serviceOrderRowOrderList = new SchoolProductOrderList();
                         serviceOrderRowOrderList.id = serviceProductRow.id;
+                        serviceOrderRowOrderList.orderDate = serviceProductRow.orderDate;
                         serviceOrderRowOrderList.startDate = serviceProductRow.startDate;
                         serviceOrderRowOrderList.endDate = serviceProductRow.endDate;
                         serviceOrderRowOrderList.number = serviceProductRow.number;
                         serviceOrderRowOrderList.unit = serviceProductRow.unit;
                         serviceOrderRow.order.Add(serviceOrderRowOrderList);
                         serviceOrderRow.avaliable = serviceAvaliableNow;
-                        if (serviceProductRow.startDate < UTCNow && UTCNow < serviceProductRow.endDate)
-                        {
-                            serviceOrderRow.avaliableStartDate = serviceProductRow.startDate;
-                            serviceOrderRow.avaliableEndDate = serviceProductRow.endDate;
-                        }
+                        serviceOrderRow.avaliableStartDate = serviceAvaliableStartDate;
+                        serviceOrderRow.avaliableEndDate = serviceAvaliableEndDate;
                         serviceOrder.Add(serviceOrderRow);
                     }
                     else //有此產品項 => 看是否有此購買紀錄
                     {
                         //記入可用起始終止日
-                        if (serviceProductRow.startDate < UTCNow && UTCNow < serviceProductRow.endDate)
-                        {
-                            serviceOrderNow.avaliableStartDate = serviceProductRow.startDate;
-                            serviceOrderNow.avaliableEndDate = serviceProductRow.endDate;
-                        }
+                        serviceOrderNow.avaliableStartDate = serviceAvaliableStartDate;
+                        serviceOrderNow.avaliableEndDate = serviceAvaliableEndDate;
                         //記入購買紀錄
                         SchoolProductOrderList SchoolProductOrderListNow = serviceOrderNow.order.Where(Sol => Sol.id.Equals(serviceProductRow.id)).FirstOrDefault();
                         if (SchoolProductOrderListNow == null) //無此購買紀錄
                         {
                             SchoolProductOrderList serviceOrderRowOrderList = new SchoolProductOrderList();
                             serviceOrderRowOrderList.id = serviceProductRow.id;
+                            serviceOrderRowOrderList.orderDate = serviceProductRow.orderDate;
                             serviceOrderRowOrderList.startDate = serviceProductRow.startDate;
                             serviceOrderRowOrderList.endDate = serviceProductRow.endDate;
                             serviceOrderRowOrderList.number = serviceProductRow.number;
@@ -828,11 +855,11 @@ namespace TEAMModelOS.Controllers
             string AccessToken = "";
             try
             {
-                string Url = _configuration.GetValue<string>("HaBookAuth:IES5Auth:url");
+                string Url = _configuration.GetValue<string>("HaBookAuth:CoreAPI") + "/oauth2/token";
                 HttpClient client = new HttpClient();
                 string GrantType = "device";
-                string ClientID = _configuration.GetValue<string>("HaBookAuth:IES5Auth:token:client_id");
-                string Secret = _configuration.GetValue<string>("HaBookAuth:IES5Auth:token:client_secret");
+                string ClientID = _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
+                string Secret = _configuration.GetValue<string>("HaBookAuth:CoreService:clientSecret");
                 var content = new { grant_type = GrantType, client_id = ClientID, client_secret = Secret };
                 var response = await client.PostAsJsonAsync($"{Url}", content);
                 if (response.IsSuccessStatusCode)

+ 1 - 1
TEAMModelOS/Startup.cs

@@ -9,7 +9,7 @@ using System.Threading;
 using System.Threading.Tasks;
 using Azure.Storage.Blobs.Models;
 using HTEXLib.Builders;
-using HTEXLib.Translator;
+using HTEXLib.Translator;   
 using Lib.AspNetCore.ServerSentEvents;
 using Microsoft.AspNetCore.Authentication.JwtBearer;
 using Microsoft.AspNetCore.Builder;