Browse Source

Merge branch 'develop' into ZJ/develop220509

zhouj1203@hotmail.com 3 năm trước cách đây
mục cha
commit
9828d2a4f4
42 tập tin đã thay đổi với 1456 bổ sung1021 xóa
  1. 3 3
      TEAMModelBI/Controllers/BIAbility/AbilityMgmtController.cs
  2. 2 2
      TEAMModelBI/Controllers/BIAbility/AbilityTaskMgmtController.cs
  3. 123 34
      TEAMModelBI/Controllers/BIHome/OnLineController.cs
  4. 4 4
      TEAMModelBI/Controllers/BINormal/AppCompanyController.cs
  5. 2 2
      TEAMModelBI/Controllers/BINormal/BIOpenApiController.cs
  6. 2 2
      TEAMModelBI/Controllers/BINormal/CompanyController.cs
  7. 1 1
      TEAMModelBI/Controllers/BISchool/AreaRelevantController.cs
  8. 2 2
      TEAMModelBI/Controllers/BISchool/BatchAreaController.cs
  9. 2 2
      TEAMModelBI/Controllers/BISchool/BatchSchoolController.cs
  10. 1 1
      TEAMModelBI/Controllers/BISchool/ProductController.cs
  11. 1 1
      TEAMModelBI/Controllers/BISchool/RoomController.cs
  12. 3 3
      TEAMModelBI/Controllers/BISchool/SchoolController.cs
  13. 1 1
      TEAMModelBI/Controllers/BIServer/BiServersController.cs
  14. 1 1
      TEAMModelBI/Controllers/Core/BlobController.cs
  15. 3 3
      TEAMModelBI/Controllers/DingDingStruc/TableDingDingInfoController.cs
  16. 21 17
      TEAMModelOS.FunctionV4/ServiceBus/ActiveTaskTopic.cs
  17. 13 3
      TEAMModelOS.SDK/Models/Cosmos/School/SchoolProduct.cs
  18. 5 0
      TEAMModelOS/ClientApp/public/lang/en-US.js
  19. 5 0
      TEAMModelOS/ClientApp/public/lang/zh-CN.js
  20. 5 0
      TEAMModelOS/ClientApp/public/lang/zh-TW.js
  21. 2 20
      TEAMModelOS/ClientApp/src/assets/student-web/component_styles/common.css
  22. 6 0
      TEAMModelOS/ClientApp/src/assets/student-web/component_styles/course-list.less
  23. 37 19
      TEAMModelOS/ClientApp/src/assets/student-web/component_styles/event-list-new.less
  24. 22 2
      TEAMModelOS/ClientApp/src/assets/student-web/component_styles/paper-test.css
  25. 4 2
      TEAMModelOS/ClientApp/src/common/BaseLayout.vue
  26. 60 43
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContent.less
  27. 7 0
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReport.less
  28. 5 5
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReport.vue
  29. 7 7
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReportCharts/LessonTestReportCharts.vue
  30. 54 24
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperTest.vue
  31. 228 183
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/composePaper.vue
  32. 1 1
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventList.vue
  33. 1 1
      TEAMModelOS/ClientApp/src/components/student-web/HomeView/CourseListView.vue
  34. 3 1
      TEAMModelOS/ClientApp/src/store/module/config.js
  35. 6 6
      TEAMModelOS/ClientApp/src/utils/editorTools.js
  36. 592 603
      TEAMModelOS/ClientApp/src/view/ability/Review.vue
  37. 1 1
      TEAMModelOS/ClientApp/src/view/classrecord/ClassRecord.vue
  38. 1 1
      TEAMModelOS/ClientApp/src/view/classrecord/eventchart/PopQues.vue
  39. 15 2
      TEAMModelOS/ClientApp/src/view/jyzx/application.vue
  40. 159 0
      TEAMModelOS/Controllers/Client/HabbController.cs
  41. 44 17
      TEAMModelOS/Controllers/School/SchoolController.cs
  42. 1 1
      TEAMModelOS/Startup.cs

+ 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; }

+ 5 - 0
TEAMModelOS/ClientApp/public/lang/en-US.js

@@ -1106,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',
@@ -4211,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',
@@ -4245,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",

+ 5 - 0
TEAMModelOS/ClientApp/public/lang/zh-CN.js

@@ -1106,6 +1106,9 @@ const LANG_ZH_CN = {
         paperTagPlace: '选择标签或者手动输入后回车创建新标签',
         useTip: '请至少保留一种试题用途',
         editor: {
+            canvasTip:'空白画板',
+            canvasStemTip:'带题干画板',
+            addTextDot:'添加着重号',
             imagePasteTip: '检测到您粘贴的内容包含图片信息,为了更好的使用体验,建议您开启醍摩豆阅卷助手',
             uploadVideo: '上传本地视频',
             uploadAudio: '上传本地音频',
@@ -4211,6 +4214,7 @@ const LANG_ZH_CN = {
                 againPractice: "再次练习",
                 finish: '已完成',
                 showAns: '显示答案',
+                showPage: "查看题号",
                 hideAns: '隐藏答案',
                 hint: '重点提示',
                 previous: '上一题',
@@ -4245,6 +4249,7 @@ const LANG_ZH_CN = {
                 correction: '正解',
                 queNo: '题目:',
                 myAns: '我的作答:',
+                reAns:'重新作答:',
                 noExam: "暂没有试题",
                 wrongSub: "错题",
                 rightSub: "正确题目",

+ 5 - 0
TEAMModelOS/ClientApp/public/lang/zh-TW.js

@@ -1106,6 +1106,9 @@ const LANG_ZH_TW = {
         paperTagPlace: '選擇標籤或者手動輸入後新增新標簽',
         useTip: '請至少保留一種試題用途',
         editor: {
+            canvasTip:'空白畫闆',
+            canvasStemTip:'帶題幹畫闆',
+            addTextDot:'添加著重號',
             imagePasteTip: '檢測到您粘貼的內容包含圖片信息,為了更好的使用體驗,建議您開啟醍摩豆閱卷助手',
             uploadVideo: '上傳本機影片',
             uploadAudio: '上傳本機音訊',
@@ -4211,6 +4214,7 @@ const LANG_ZH_TW = {
                 againPractice: "再次練習",
                 finish: '已完成',
                 showAns: '顯示答案',
+                showPage: "查看題號",
                 hideAns: '隱藏答案',
                 hint: '重點提示',
                 previous: '上一題',
@@ -4245,6 +4249,7 @@ const LANG_ZH_TW = {
                 correction: '正解',
                 queNo: '題目:',
                 myAns: '我的作答:',
+                reAns:'重新作答:',
                 noExam: "暫沒有試題",
                 wrongSub: "錯題",
                 rightSub: "正確題目",

+ 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{

+ 4 - 2
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" : ""]

+ 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

+ 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

+ 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 调整菜单的顺序,参考【配置菜单】部分的文档

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 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])

+ 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>
 

+ 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;