chenmy 3 years ago
parent
commit
2c00ff6742
60 changed files with 2685 additions and 6026 deletions
  1. 1 0
      TEAMModelAPI/Controllers/School/ExamController.cs
  2. 67 0
      TEAMModelBI/Controllers/BITest/Ies5TestController.cs
  3. 6 5
      TEAMModelOS.FunctionV4/CosmosDB/TriggerExam.cs
  4. 3 3
      TEAMModelOS.FunctionV4/TEAMModelOS.FunctionV4.csproj
  5. 4 0
      TEAMModelOS.SDK/Models/Cosmos/Common/LessonRecord.cs
  6. 19 2
      TEAMModelOS.SDK/Models/Cosmos/School/SchoolSetting.cs
  7. 1 1
      TEAMModelOS.SDK/Models/Service/ActivityService.cs
  8. 4 1
      TEAMModelOS.SDK/Models/Service/Common/TeacherService.cs
  9. 199 28
      TEAMModelOS.SDK/Models/Service/LoginService.cs
  10. 1 1
      TEAMModelOS.SDK/Models/Service/StudentService.cs
  11. 5 1
      TEAMModelOS.SDK/Models/Service/Third/Xkw/XkwOAuthModel.cs
  12. 19 20
      TEAMModelOS.SDK/Models/Table/IESLogin.cs
  13. 10 2
      TEAMModelOS/ClientApp/public/index.html
  14. 7 3
      TEAMModelOS/ClientApp/public/lang/en-US.js
  15. 8 4
      TEAMModelOS/ClientApp/public/lang/zh-CN.js
  16. 7 3
      TEAMModelOS/ClientApp/public/lang/zh-TW.js
  17. 1 1
      TEAMModelOS/ClientApp/src/api/lessonRecord.js
  18. 8 0
      TEAMModelOS/ClientApp/src/api/studentWeb.js
  19. 38 0
      TEAMModelOS/ClientApp/src/assets/student-web/component_styles/paper-test.css
  20. 46 72
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperTest.vue
  21. 28 0
      TEAMModelOS/ClientApp/src/components/student-web/achievement/MyAchievement.less
  22. 414 373
      TEAMModelOS/ClientApp/src/components/student-web/achievement/MyAchievement.vue
  23. 6 20
      TEAMModelOS/ClientApp/src/locale/index.js
  24. 47 3
      TEAMModelOS/ClientApp/src/utils/public.js
  25. 1 1
      TEAMModelOS/ClientApp/src/view/areaMgmt/AreaData.vue
  26. 0 360
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtPrivEva.less
  27. 0 871
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtPrivEva.vue
  28. 0 313
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.less
  29. 0 900
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.vue
  30. 0 150
      TEAMModelOS/ClientApp/src/view/learnactivity/Scoring.less
  31. 0 1170
      TEAMModelOS/ClientApp/src/view/learnactivity/Scoring.vue
  32. 0 371
      TEAMModelOS/ClientApp/src/view/learnactivity/SimpleAnalysis.vue
  33. 1 1
      TEAMModelOS/ClientApp/src/view/learnactivity/markpaper/MarkData.vue
  34. 101 35
      TEAMModelOS/ClientApp/src/view/learnactivity/markpaper/PublishTask.vue
  35. 51 20
      TEAMModelOS/ClientApp/src/view/learnactivity/tabs/AnswerTable.less
  36. 37 39
      TEAMModelOS/ClientApp/src/view/learnactivity/tabs/AnswerTable.vue
  37. 25 18
      TEAMModelOS/ClientApp/src/view/newcourse/MyCourse.vue
  38. 153 116
      TEAMModelOS/ClientApp/src/view/research-center/BaseCleanCond.vue
  39. 814 802
      TEAMModelOS/ClientApp/src/view/research-center/ResearchMgt.vue
  40. 224 223
      TEAMModelOS/ClientApp/src/view/settings/Index.vue
  41. 2 1
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/EvaluationList/TotalIndex.vue
  42. 17 3
      TEAMModelOS/ClientApp/src/view/task/mark/ByStu.vue
  43. 15 8
      TEAMModelOS/ClientApp/src/view/user/BandPhone.vue
  44. 3 1
      TEAMModelOS/Controllers/Analysis/AnalysisController.cs
  45. 15 1
      TEAMModelOS/Controllers/Client/HiScanController.cs
  46. 15 1
      TEAMModelOS/Controllers/Client/HiTAControlller.cs
  47. 27 1
      TEAMModelOS/Controllers/Client/HiTeachController.cs
  48. 1 1
      TEAMModelOS/Controllers/Common/ExamController.cs
  49. 4 0
      TEAMModelOS/Controllers/School/SchoolSettingController.cs
  50. 30 3
      TEAMModelOS/Controllers/Student/StudentCommonController.cs
  51. 26 8
      TEAMModelOS/Controllers/Student/StudentController.cs
  52. 19 3
      TEAMModelOS/Controllers/Student/TmdUserController.cs
  53. 7 3
      TEAMModelOS/Controllers/Teacher/InitController.cs
  54. 39 2
      TEAMModelOS/Controllers/Third/OAuth2Controller.cs
  55. 32 17
      TEAMModelOS/Controllers/Third/Sc/ScController.cs
  56. 12 4
      TEAMModelOS/Controllers/Third/Xkw/OpenAuthClient.cs
  57. 28 11
      TEAMModelOS/Controllers/Third/Xkw/XkwOAuth2Controller.cs
  58. 34 21
      TEAMModelOS/Controllers/XTest/TestController.cs
  59. 3 3
      TEAMModelOS/TEAMModelOS.csproj
  60. 0 1
      TEAMModelOS/appsettings.Development.json

+ 1 - 0
TEAMModelAPI/Controllers/School/ExamController.cs

@@ -202,6 +202,7 @@ namespace TEAMModelAPI.Controllers
                             builder.Append("ans.json");
                             result.studentAnswers[newIndex].Clear();
                             result.studentAnswers[newIndex].Add(builder.ToString());
+                            result.status[newIndex] = 0;
                             for (int i = 0; i < ans.Count; i++)
                             {
                                 if (ans[i] == null)

+ 67 - 0
TEAMModelBI/Controllers/BITest/Ies5TestController.cs

@@ -0,0 +1,67 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Options;
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Threading.Tasks;
+using TEAMModelOS.Models;
+using TEAMModelOS.SDK;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK.Models.Service;
+using static TEAMModelOS.SDK.Models.Teacher;
+
+namespace TEAMModelBI.Controllers.BITest
+{
+    [Route("iesapitest")]
+    [ApiController]
+    public class Ies5TestController : ControllerBase
+    {
+
+        private readonly AzureCosmosFactory _azureCosmos;
+        private readonly AzureRedisFactory _azureRedis;
+        private readonly DingDing _dingDing;
+        private readonly Option _option;
+        private readonly AzureStorageFactory _azureStorage;
+        private readonly IWebHostEnvironment _environment; //读取文件
+        //读取配置文件
+        private readonly IConfiguration _configuration;
+        private readonly CoreAPIHttpService _coreAPIHttpService;
+        private readonly HttpClient _httpClient;
+
+        public Ies5TestController(AzureCosmosFactory azureCosmos, AzureRedisFactory azureRedis, DingDing dingDing, AzureStorageFactory azureStorage, IOptionsSnapshot<Option> option, IWebHostEnvironment hostingEnvironment, IConfiguration configuration, CoreAPIHttpService coreAPIHttpService, HttpClient httpClient)
+        {
+            _azureCosmos = azureCosmos;
+            _dingDing = dingDing;
+            _azureStorage = azureStorage;
+            _option = option?.Value;
+            _environment = hostingEnvironment;
+            _configuration = configuration;
+            _coreAPIHttpService = coreAPIHttpService;
+            _httpClient = httpClient;
+            _azureRedis = azureRedis;
+        }
+
+        [HttpPost("get-datetime")]
+        public async Task<IActionResult> GetDateTime() 
+        {
+           var dateHours =  DateTimeOffset.UtcNow.Hour;
+           var dateHours1 = DateTime.Now.Hour;
+           var dateHours2 = DateTimeOffset.Now.Hour;
+           var dateDays = DateTimeOffset.UtcNow.Month;
+
+           var dateDay = DateTimeOffset.UtcNow.ToString("yyyyMMdd");
+           var dateMonth = DateTimeOffset.UtcNow.ToString("yyyyMM");
+           long expire = DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds();
+           long now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
+            var cosmosClient = _azureCosmos.GetCosmosClient();
+           List<LoginInfo> loginInfos = new() { new LoginInfo (){ time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),ip="172.54.81.101",expire = expire } };
+            var tets = await LoginService.DoLoginInfo(loginInfos: loginInfos, school: "hbcn", scope: "teacher", id: "1636016499", ip: "172.168.52.102", _azureRedis, _azureStorage, cosmosClient, expire: 1);
+           return Ok(new { state = 200, dateHours, dateHours1, dateHours2, dateDay,dateDays, dateMonth, });
+        }
+
+    }
+}

+ 6 - 5
TEAMModelOS.FunctionV4/CosmosDB/TriggerExam.cs

@@ -977,7 +977,7 @@ namespace TEAMModelOS.FunctionV4
                                 ph.Add(phCount);
                                 pl.Add(plCount);
                                 double per = classResult.studentIds.Count > 0 ? Math.Round(score / classResult.studentIds.Count, 2) : 0;
-                                persent.Add(allScore > 0 ? per / allScore : 0);
+                                persent.Add(allScore > 0 ? Math.Round(per / allScore,2) : 0);
                             }
                             classResult.phc = ph;
                             classResult.plc = pl;
@@ -1113,7 +1113,7 @@ namespace TEAMModelOS.FunctionV4
                                 ph.Add(phCount);
                                 pl.Add(plCount);
                                 double per = classResult.studentIds.Count > 0 ? Math.Round(score / classResult.studentIds.Count, 2) : 0;
-                                persent.Add(allScore > 0 ? per / allScore : 0);
+                                persent.Add(allScore > 0 ? Math.Round(per / allScore, 2) : 0);
                             }
                         }
                         classResult.fphc = ph;
@@ -1337,6 +1337,7 @@ namespace TEAMModelOS.FunctionV4
             result.average = result.studentIds.Count > 0 ? Math.Round(score * 1.0 / result.studentIds.Count, 2) : 0;
             double stand = 0;
             int sco = 0;
+            List<Task<ItemResponse<ExamClassResult>>> tasks = new();
             foreach (ExamClassResult classResult in examClassResults)
             {
 
@@ -1346,8 +1347,9 @@ namespace TEAMModelOS.FunctionV4
                     stand += classResult.standard;
                     sco++;
                 }
-
+                tasks.Add(_azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(classResult, classResult.id, new PartitionKey($"{classResult.code}")));
             }
+            Task.WhenAll(tasks);
             result.standard = sco > 0 ? Math.Round(stand / sco, 2) : 0;
             result.csRate = csRate;
             result.lostStus = lostStu;
@@ -1364,8 +1366,7 @@ namespace TEAMModelOS.FunctionV4
             //result.point = info.papers[j].point;
             result.scope = info.scope;
             result.name = info.name;
-            result.time = info.startTime;
-
+            result.time = info.startTime;            
             await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync(result, new Azure.Cosmos.PartitionKey($"ExamResult-{info.id}"));
 
         }

+ 3 - 3
TEAMModelOS.FunctionV4/TEAMModelOS.FunctionV4.csproj

@@ -5,9 +5,9 @@
 		<OutputType>Exe</OutputType>
 		<_FunctionsSkipCleanOutput>true</_FunctionsSkipCleanOutput>
 		<SignAssembly>true</SignAssembly>
-		<AssemblyVersion>5.2205.6.1</AssemblyVersion>
-		<FileVersion>5.2205.6.1</FileVersion>
-		<Version>5.2205.6</Version>
+		<AssemblyVersion>5.2205.7.1</AssemblyVersion>
+		<FileVersion>5.2205.7.1</FileVersion>
+		<Version>5.2205.7</Version>
 		<PackageId>TEAMModelOS.FunctionV4</PackageId>
 		<Authors>teammodel</Authors>
 		<Company>醍摩豆(成都)信息技术有限公司</Company>

+ 4 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/LessonRecord.cs

@@ -189,6 +189,10 @@ namespace TEAMModelOS.SDK.Models
         /// 暂不 开放 teacher【开放给部分教师查看】醍摩豆id 
         /// </summary>
         public List<string> showTchs { get; set; } = new List<string>();
+        /// <summary>
+        /// 设置强制保留的 =1 ,不会被自动清理的。但是可以被手动清理。
+        /// </summary>
+        public int save { get; set; } = -1;
     }
     public class LessonTC
     {

+ 19 - 2
TEAMModelOS.SDK/Models/Cosmos/School/SchoolSetting.cs

@@ -34,7 +34,24 @@ namespace TEAMModelOS.SDK.Models
         /// 课堂记录的标签
         /// </summary>
         public HashSet<string> lessonTag { get; set; } = new HashSet<string>();
+        public LessonSetting lessonSetting { get; set; }
+    }
+    public class LessonSetting
+    {
+        /// <summary>
+        /// 是否开启自动清理  1 开启,0未开启
+        /// </summary>
+        public int openAutoClean { get; set; }
+        /// <summary>
+        /// 保留多少天
+        /// </summary>
+        public int expireDays { get; set; }
+        public List<LessonSettingCond> conds { get; set; }
+    }
+    public class LessonSettingCond
+    {
+        public string key { get; set; }
+        public double val { get; set; }
+        public string type { get; set; }
     }
-
-   
 }

+ 1 - 1
TEAMModelOS.SDK/Models/Service/ActivityService.cs

@@ -701,7 +701,7 @@ namespace TEAMModelOS.SDK
                                 item.studentAnswers.Add(new List<string>());
                                 item.sum.Add(0);
                                 //item.scIds.Add(member.code??"");
-                                item.status.Add(0);
+                                item.status.Add(1);
                             }
                             else
                             {

+ 4 - 1
TEAMModelOS.SDK/Models/Service/Common/TeacherService.cs

@@ -29,7 +29,7 @@ namespace TEAMModelOS.Services
 {
     public static class TeacherService
     {
-        public static async Task<TeacherInfo> TeacherInfo(AzureCosmosFactory _azureCosmos, Teacher teacher, string name, string picture, string id, AzureStorageFactory _azureStorage, Option _option)
+        public static async Task<TeacherInfo> TeacherInfo(AzureCosmosFactory _azureCosmos, Teacher teacher, string name, string picture, string id, AzureStorageFactory _azureStorage, Option _option,AzureRedisFactory _azureRedis,string ip)
         {
             List<object> schools = new List<object>();
             List<AreaDto> areas = new List<AreaDto>();
@@ -277,6 +277,9 @@ namespace TEAMModelOS.Services
             //換取AuthToken,提供給前端
             var auth_token = JwtAuthExtension.CreateAuthToken(_option.HostName, id, name?.ToString(), picture?.ToString(), _option.JwtSecretKey, Website: "IES", scope: Constant.ScopeTeacher, standard: areaa != null ? areaa.standard : "", roles: roles.ToArray(), expire: 1);
 
+            //用户在线记录
+            teacher.loginInfos = await LoginService.DoLoginInfo(teacher.loginInfos, "", Constant.ScopeTeacher, id, ip, _azureRedis, _azureStorage, client, expire: 1);
+
             await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
             //取得Teacher Blob 容器位置及SAS 
             await _azureStorage.GetBlobContainerClient(id).CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建Teacher私有容器,如存在則不做任何事,保障容器一定存在

+ 199 - 28
TEAMModelOS.SDK/Models/Service/LoginService.cs

@@ -1,5 +1,7 @@
-using Microsoft.AspNetCore.Http;
+using Azure.Cosmos;
+using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Logging;
+using StackExchange.Redis;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -7,6 +9,7 @@ using System.Text;
 using System.Threading.Tasks;
 using TEAMModelOS.Models;
 using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Models.Table;
 using static TEAMModelOS.SDK.Models.Teacher;
 
 namespace TEAMModelOS.SDK.Models.Service
@@ -14,19 +17,33 @@ namespace TEAMModelOS.SDK.Models.Service
     public static class LoginService
     {
         /// <summary>
-        /// 
+        /// //添加用户登录信息和在线登录
         /// </summary>
-        /// <param name="loginInfos"></param>
-        /// <param name="expire"></param>
-        /// <param name="now"></param>
-        /// <param name="school"></param>
-        /// <param name="scope">Teacher Student  TmdUser</param>
-        /// <param name="id"></param>
-        /// <param name="ip"></param>
-        /// <param name="region"></param>
+        /// <param name="school">学校</param>
+        /// <param name="scope">登录类型Teacher Student  TmdUser</param>
+        /// <param name="id">登录者的ID</param>
+        /// <param name="ip">登陆者的IP地址</param>
+        /// <param name="_azureRedis">redis</param>
+        /// <param name="_azureStorage">table表</param>
+        /// <param name="client">cosmosDB数据库连接</param>
+        /// <param name="expire">到期时间</param>
+        /// <param name="region">上次登录地址</param>
         /// <returns></returns>
-        public static async Task<List<LoginInfo>> DoLoginInfo(List<LoginInfo> loginInfos, long expire, long now, string school, string scope, string id, string ip, string region)
+        public static async Task<List<LoginInfo>> DoLoginInfo(List<LoginInfo> loginInfos, string school, string scope, string id, string ip, AzureRedisFactory _azureRedis, AzureStorageFactory _azureStorage, CosmosClient client ,int expire =1, string region = null)
         {
+            var table = _azureStorage.GetCloudTableClient().GetTableReference("IESLogin");
+            DateTimeOffset dateTime = DateTimeOffset.UtcNow;
+            var dateHour = dateTime.ToString("yyyyMMddHH"); //获取当天的小时
+            var dateDay = dateTime.ToString("yyyyMMdd"); //获取当天的日期
+            var dateMonth = dateTime.ToString("yyyyMM");//获取当月的日期
+            var currentHour = dateTime.Hour;
+            var currentDay = dateTime.Day;
+            long Expire = dateTime.AddHours(expire).ToUnixTimeMilliseconds();  //token到期时间
+            long now = dateTime.ToUnixTimeMilliseconds();   //时间戳
+            
+            DateTime hour = DateTime.UtcNow.AddHours(25);   //25小时到期
+            DateTime month = DateTime.UtcNow.AddDays(32);   //一个月到期
+
             if (loginInfos.Any())
             {
                 if (loginInfos.Count() >= 5)
@@ -34,29 +51,183 @@ namespace TEAMModelOS.SDK.Models.Service
                     loginInfos = loginInfos.OrderBy(x => x.expire).ToList();
                     loginInfos.RemoveRange(4, loginInfos.Count() - 4);
                 }
-                loginInfos.Add(new LoginInfo { expire = expire, ip = ip, time = now });
+                loginInfos.Add(new LoginInfo { expire = Expire, ip = ip, time = now });
             }
             else
+                loginInfos = new List<LoginInfo> { new LoginInfo { expire = Expire, ip = ip, time = now } };
+            
+            if (!string.IsNullOrWhiteSpace(school))
             {
-                loginInfos = new List<LoginInfo> { new LoginInfo { expire = expire, ip = ip, time = now } };
+                await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:School:{school}:{scope}:{dateDay}", $"{currentHour}", 1);//当天当前小时在线人加1
+                await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:School:{school}:{scope}:{dateMonth}", $"{currentDay}", 1); //当天的在线加1
+
+                var resDay = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"Login:School:{school}:{scope}:{dateDay}");
+                if (resDay == null)
+                {
+                    await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:School:{school}:{scope}:{dateDay}", hour);  //设置到期时间
+
+                }
+
+                var rspMonth = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"Login:School:{school}:{scope}:{dateMonth}");
+                if (rspMonth != null)
+                {
+                    await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:School:{school}:{scope}:{dateMonth}", month);  //设置到期时间
+                }
+
+
+                var scDay = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:School:{school}:{scope}:{dateDay}");
+                List<dynamic> scDayCount = new();
+                if (scDay != null && scDay.Length > 0)
+                {
+                    foreach (var count in scDay)
+                    {
+                        scDayCount.Add(new { code = count.Element.ToString(), count = (int)count.Score });
+                    }
+                }
+
+                //学校小时峰值
+                HourLoginSchool hourLoginSc = new() { PartitionKey = $"HourLogin-{school}", RowKey = now.ToString(), Hour = int.Parse(dateHour), School = school };
+                if (scope.Equals("teacher"))
+                {
+                    hourLoginSc.Teacher = int.Parse(id);
+                    hourLoginSc.Student = 0;
+                }
+                else if (scope.Equals("student"))
+                {
+                    hourLoginSc.Teacher = 0;
+                    hourLoginSc.Student = int.Parse(id);
+                }
+
+                try
+                {
+                    await table.SaveOrUpdate<HourLoginSchool>(hourLoginSc);//保存在线数据
+                }
+                catch
+                {                    
+                }
+
+                var ScMonth = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:School:{school}:{scope}:{dateMonth}");
+                List<dynamic> scMonthCount = new();
+                if (ScMonth != null && ScMonth.Length > 0)
+                {
+                    foreach (var count in ScMonth)
+                    {
+                        scMonthCount.Add(new { code = count.Element.ToString(), count = (int)count.Score });
+                    }
+                }
+
+                //学校天峰值
+                DayLoginSchool dayLoginSc = new() { PartitionKey = $"DayLogin-{school}", RowKey = now.ToString(), Day = int.Parse(dateDay), School = school };
+                if (scope.Equals("teacher"))
+                {
+                    dayLoginSc.Teacher = int.Parse(id);
+                    dayLoginSc.Student = 0;
+                }
+                else if (scope.Equals("student"))
+                {
+                    dayLoginSc.Teacher = 0;
+                    dayLoginSc.Student = int.Parse(id);
+                }
+
+                try
+                {
+                    await table.SaveOrUpdate<DayLoginSchool>(dayLoginSc);//保存在线数据
+                }
+                catch
+                {
+                }
             }
-            if (!string.IsNullOrWhiteSpace(school)) {
-                //Login:School:School_ID:Teacher:20220506   value 15   1
-                //await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:School:{school}:20220506", "15",1);
+            else 
+            {
+                await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:IES:{scope}:{dateDay}", $"{currentHour}", 1);//一天24小时
+                await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:IES:{scope}:{dateMonth}", $"{currentDay}", 1); //当天的累计
+
+                var resDay = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"Login:IES:{scope}:{dateDay}");
+                if (resDay == null)
+                {
+                    await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:IES:{scope}:{dateDay}", hour);  //设置到期时间
+                }
+
+                var rspMonth = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"Login:IES:{scope}:{dateMonth}");
+                if (rspMonth == null)
+                {
+                    await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:IES:{scope}:{dateMonth}", month);  //设置到期时间
+                }
+
+                var scDay = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:IES:{scope}:{dateDay}");
+                List<dynamic> scDayCount = new();
+                if (scDay != null && scDay.Length > 0)
+                {
+                    foreach (var count in scDay)
+                    {
+                        scDayCount.Add(new { code = count.Element.ToString(), count = (int)count.Score });
+                    }
+                }
+
+                var ScMonth = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:IES:{scope}:{dateMonth}");
+                List<dynamic> scMonthCount = new();
+                if (ScMonth != null && ScMonth.Length > 0)
+                {
+                    foreach (var count in ScMonth)
+                    {
+                        scMonthCount.Add(new { code = count.Element.ToString(), count = (int)count.Score });
+                    }
+                }
+
+                //个人或者TMDuser小时峰值
+                HourLogin hourLogin = new() { PartitionKey = $"HourLogin", RowKey = now.ToString(), Hour = int.Parse(dateHour) };
+                if (scope.Equals("teacher"))
+                {
+                    hourLogin.Teacher = int.Parse(id);
+                    hourLogin.Student = 0;
+                    hourLogin.TmdUser = 0;
+                }
+                else if (scope.Equals("student"))
+                {
+                    hourLogin.Teacher = 0;
+                    hourLogin.Student = int.Parse(id);
+                    hourLogin.TmdUser = 0;
+                }
+                else
+                {
+                    hourLogin.Teacher = 0;
+                    hourLogin.Student = 0;
+                    hourLogin.TmdUser = int.Parse(id);
+                }
+                try
+                {
+                    await table.SaveOrUpdate<HourLogin>(hourLogin);//保存在线数据
+
+                }
+                catch
+                {
+                }
+
+                //个人或者TMDuser天峰值
+                DayLogin dayLogin = new() { PartitionKey = $"DayLogin", RowKey = now.ToString(), Day = int.Parse(dateDay) };
+                if (scope.Equals("teacher"))
+                {
+                    dayLogin.Teacher = int.Parse(id);
+                    dayLogin.Student = 0;
+                    dayLogin.TmdUser = 0;
+                }
+                else if (scope.Equals("student"))
+                {
+                    dayLogin.Teacher = 0;
+                    dayLogin.Student = int.Parse(id);
+                    dayLogin.TmdUser = 0;
+                }
+                else
+                {
+                    dayLogin.Teacher = 0;
+                    dayLogin.Student = 0;
+                    dayLogin.TmdUser = int.Parse(id);
+                }
+
+                    await table.SaveOrUpdate<DayLogin>(dayLogin);//保存在线数据
 
-                //await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:School:{school}:20220506", "15", 1);
             }
-            //Login:IES:20220506
-
-            //var counts = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Vote:Count:{vote.id}");
-            //List<dynamic> countcds = new List<dynamic>();
-            //if (counts != null && counts.Length > 0)
-            //{
-            //    foreach (var count in counts)
-            //    {
-            //        countcds.Add(new { code = count.Element.ToString(), count = (int)count.Score });
-            //    }
-            //}
+
             return loginInfos;
         }
 

+ 1 - 1
TEAMModelOS.SDK/Models/Service/StudentService.cs

@@ -906,7 +906,7 @@ namespace TEAMModelOS.SDK
                 writer.WriteNull("mail");
                 writer.WriteNull("mobile");
                 writer.WriteNull("country");
-
+             
                 //Password,若沒給則使用學號當密碼
                 string salt = Utils.CreatSaltString(8);
                 string hashPw = string.IsNullOrWhiteSpace(studCreateInfo.pw)

+ 5 - 1
TEAMModelOS.SDK/Models/Service/Third/Xkw/XkwOAuthModel.cs

@@ -98,10 +98,14 @@ namespace TEAMModelOS.SDK.Models
         public string userId { get; set; }
         public string domain { get; set; }
     }
-
+    public record ServiceModule { 
+        public string module { get; set; }
+        public string url { get; set; }
+    }
     public record OAuthCode
     {
         public string code { get; set; }
+        public string tmdid { get; set; }
         public string state { get; set; }
         //模块,res,item,paper
         public string module { get; set; }

+ 19 - 20
TEAMModelOS.SDK/Models/Table/IESLogin.cs

@@ -50,13 +50,13 @@ namespace TEAMModelOS.SDK.Models.Table
     public class HourLogin : TableEntity
     {
         /// <summary>
-        /// HourLogin
+        /// HourLogin   继承table不需要重复命名
         /// </summary>
-        public string PartitionKey { get; set; }
+        //public string PartitionKey { get; set; }
         /// <summary>
-        /// 2022050511  yyyyMMddHH
+        /// 2022050511  yyyyMMddHH   继承table不需要重复命名
         /// </summary>
-        public string RowKey { get; set; }
+        //public string RowKey { get; set; }
         /// <summary>
         /// 2022050512  yyyyMMddHH
         /// </summary>
@@ -73,15 +73,15 @@ namespace TEAMModelOS.SDK.Models.Table
     public class DayLogin : TableEntity
     {
         /// <summary>
-        /// DayLogin
+        /// DayLogin  继承table不需要重复命名
         /// </summary>
-        public string PartitionKey { get; set; }
+        //public string PartitionKey { get; set; }
         /// <summary>
-        /// 20220505  yyyyMMdd
+        /// 20220505  yyyyMMdd  继承table不需要重复命名
         /// </summary>
-        public string RowKey { get; set; }
+        //public string RowKey { get; set; }
         /// <summary>
-        /// 20220505  yyyyMMdd
+        /// 20220505  yyyyMMdd  
         /// </summary>
         public int Day { get; set; }
         public int Teacher { get; set; }
@@ -93,16 +93,16 @@ namespace TEAMModelOS.SDK.Models.Table
     /// 学校按天统计登录信息
     /// </summary>
     [TableName(Name = "IESLogin")]
-    public class DayLoginSchool : DayLogin
+    public class DayLoginSchool : TableEntity
     {
         /// <summary>
-        /// DayLogin-hbcn学校编码
+        /// DayLogin-hbcn学校编码 继承table不需要重复命名
         /// </summary>
-        public string PartitionKey { get; set; }
+        //public string PartitionKey { get; set; }
         /// <summary>
-        /// 20220505  yyyyMMdd
+        /// 20220505  yyyyMMdd   继承table不需要重复命名
         /// </summary>
-        public string RowKey { get; set; }
+        //public string RowKey { get; set; }
         /// <summary>
         /// 20220505  yyyyMMdd
         /// </summary>
@@ -116,23 +116,22 @@ namespace TEAMModelOS.SDK.Models.Table
     ///  学校小时峰值统计登录信息
     /// </summary>
     [TableName(Name = "IESLogin")]
-    public class HourLoginSchool : HourLogin
+    public class HourLoginSchool : TableEntity
     {
         /// <summary>
-        /// HourLogin-hbcn学校编码
+        /// HourLogin-hbcn学校编码  继承table不需要重复命名
         /// </summary>
-        public string PartitionKey { get; set; }
+        //public string PartitionKey { get; set; }
         /// <summary>
         /// 2022050511  yyyyMMddHH
         /// </summary>
-        public string RowKey { get; set; }
+        //public string RowKey { get; set; }
         /// <summary>
         /// 20220505  yyyyMMdd
         /// </summary>
         public int Hour { get; set; }
         public int Teacher { get; set; }
-        public int Student { get; set; }
-       
+        public int Student { get; set; }       
         public string School { get; set; }
     }
 }

+ 10 - 2
TEAMModelOS/ClientApp/public/index.html

@@ -1,16 +1,24 @@
 <!DOCTYPE html>
 <html lang="en">
+
 <head>
 	<meta charset="utf-8">
 	<meta http-equiv="X-UA-Compatible" content="IE=edge">
 	<meta name="viewport" content="width=device-width,initial-scale=1.0">
 	<link rel="icon" href="<%= BASE_URL %>favicon.ico">
 	<link id="theme" type="text/css" rel="stylesheet" href="<%= BASE_URL %>theme/dark-theme.css">
-	<script src="<%= BASE_URL %>lang/zh-CN.js"></script>
+	<!-- <script src="<%= BASE_URL %>lang/zh-CN.js"></script>
 	<script src="<%= BASE_URL %>lang/zh-TW.js"></script>
-	<script src="<%= BASE_URL %>lang/en-US.js"></script>
+	<script src="<%= BASE_URL %>lang/en-US.js"></script> -->
 	<title></title>
 	<script>
+		// 自动根据浏览器系统语言设置语言(优先判断本地设置的语言,如果有则使用本地设置的语言,如果没有则使用浏览器系统语言)
+		const curLang = localStorage.getItem('cloudSetting') ? JSON.parse(localStorage.getItem('cloudSetting')).curLang : null
+		const navLang = curLang || navigator.language.toLowerCase()
+		const localLang = (navLang === 'zh' || navLang === 'zh-tw' || navLang === 'zh-cn' || navLang === 'zh-hk') ? navLang : false
+		const lang = localLang || 'en-us'
+		const langJsUrl = 'https://' + window.location.host + '/lang/' + lang + '.js'
+		document.write('<script  src="' + langJsUrl + '"><\/script>')
 		MathJax = {
 			loader: {
 				load: ["input/tex", "output/svg"]

+ 7 - 3
TEAMModelOS/ClientApp/public/lang/en-US.js

@@ -1,4 +1,4 @@
-const LANG_EN_US = {
+window.LANG_EN_US = {
     // 研修模块
     ability: {
         points: {
@@ -768,7 +768,7 @@ const LANG_EN_US = {
         fvtErr: 'Failed to collect',
         unfvtOk: 'Removed from collection',
         unfvtErr: 'Failed to remove',
-        recordTips: 'Note: Only the last 30 records will be kept for personal courses. The earliest records will be deleted automatically if exceed 30.',
+        recordTips: 'Note: Only the last 50 records will be kept for personal courses. The earliest records will be deleted automatically if exceed 50.',
         shareToStu: 'Send to students',
         unShare: 'Cancel Sending',
         shareOk: 'Send successfully',
@@ -2503,6 +2503,7 @@ const LANG_EN_US = {
             selectTeaTips: 'Please set the marking teacher',
             lastQu: 'Questions are not all assigned',
             reapQu: 'There are questions that are repeatedly assigned',
+            setQuBlock: '請設置題目分塊',
             objectiveLabel: '(Objective)',
             alreadyLabel: '(Already)',
             exTag: 'Abnormal',
@@ -2565,7 +2566,7 @@ const LANG_EN_US = {
             saveErr: 'Failed to save',
             noSocreErr: '請先打分',
             completeQu: '當前題目已閱完,請切換題目',
-            completeStu: '當前完成當前學生評分,如果繼續評分,請切換學生',
+            completeStu: '完成當前學生評分,如果繼續評分,請切換學生',
             noAnswer: 'Not Answered',
             stuInfoErr: 'Student Information Error',
             ummarkQu: 'Unmark Questions',
@@ -4507,6 +4508,9 @@ const LANG_EN_US = {
             wrongNum: "No. of Incorrect",
         },
         myAchievement: {
+            examAch: "評量成績",
+            hwAch: "作業成績",
+            cusAch: "老師給分",
             examMode: "Assessment Mode",
             examIn: "Source",
             name: "Name",

+ 8 - 4
TEAMModelOS/ClientApp/public/lang/zh-CN.js

@@ -1,4 +1,4 @@
-const LANG_ZH_CN = {
+window.LANG_ZH_CN = {
     // 研修模块
     ability: {
         points: {
@@ -767,7 +767,7 @@ const LANG_ZH_CN = {
         fvtErr: '收藏失败',
         unfvtOk: '已取消收藏',
         unfvtErr: '取消失败',
-        recordTips: '温馨提示:个人课程课堂记录只保留最近30条记录,超过30条会自动删除最早的记录。',
+        recordTips: '温馨提示:个人课程课堂记录只保留最近50条记录,超过50条会自动删除最早的记录。',
         shareToStu: '发布给学生',
         unShare: '取消发布',
         shareOk: '发布成功',
@@ -2504,6 +2504,7 @@ const LANG_ZH_CN = {
             selectTeaTips: '请设置当前题块的阅卷老师',
             lastQu: '题目尚未完全分配',
             reapQu: '题目划块设置存在重复题目',
+            setQuBlock: '请设置题目分块',
             objectiveLabel: '(客)',
             alreadyLabel: '(已)',
             exTag: '异常',
@@ -2566,7 +2567,7 @@ const LANG_ZH_CN = {
             saveErr: '保存失败',
             noSocreErr: '请先打分',
             completeQu: '当前题目已阅完,请切换题目',
-            completeStu: '当前完成当前学生评分,如果继续评分,请切换学生',
+            completeStu: '完成当前学生评分,如果继续评分,请切换学生',
             noAnswer: '未作答',
             stuInfoErr: '学生信息异常',
             ummarkQu: '未阅题目',
@@ -4507,6 +4508,9 @@ const LANG_ZH_CN = {
             wrongNum: "错误次数",
         },
         myAchievement: {
+            examAch: "评测成绩",
+            hwAch: "作业成绩",
+            cusAch: "老师给分",
             examMode: "评测模式",
             examIn: "发布来源",
             name: "名称",
@@ -6127,4 +6131,4 @@ const LANG_ZH_CN = {
         className: '课程名单',
         rate: '占比'
     }
-}
+}

+ 7 - 3
TEAMModelOS/ClientApp/public/lang/zh-TW.js

@@ -1,4 +1,4 @@
-const LANG_ZH_TW = {
+window.LANG_ZH_TW = {
     // 研修模块
     ability: {
         points: {
@@ -768,7 +768,7 @@ const LANG_ZH_TW = {
         fvtErr: '收藏失敗',
         unfvtOk: '已取消收藏',
         unfvtErr: '取消失敗',
-        recordTips: '溫馨提示:個人課程課堂記錄只保留最近30條記錄,超過30條會自動刪除最早的記錄。',
+        recordTips: '溫馨提示:個人課程課堂記錄只保留最近50條記錄,超過50條會自動刪除最早的記錄。',
         shareToStu: '發布給學生',
         unShare: '取消發布',
         shareOk: '發布成功',
@@ -2504,6 +2504,7 @@ const LANG_ZH_TW = {
             selectTeaTips: '請設定當前題塊的閱卷老師',
             lastQu: '題目尚未完全分配',
             reapQu: '題目劃塊設定存在重複題目',
+            setQuBlock: '請設置題目分塊',
             objectiveLabel: '(客)',
             alreadyLabel: '(已)',
             exTag: '異常',
@@ -2566,7 +2567,7 @@ const LANG_ZH_TW = {
             saveErr: '保存失敗',
             noSocreErr: '請先打分',
             completeQu: '當前題目已閱完,請切換題目',
-            completeStu: '當前完成當前學生評分,如果繼續評分,請切換學生',
+            completeStu: '完成當前學生評分,如果繼續評分,請切換學生',
             noAnswer: '未作答',
             stuInfoErr: '學生資訊異常',
             ummarkQu: '未閱題目',
@@ -4509,6 +4510,9 @@ const LANG_ZH_TW = {
             wrongNum: "錯誤次數",
         },
         myAchievement: {
+            examAch: "評量成績",
+            hwAch: "作業成績",
+            cusAch: "老師給分",
             examMode: "評量模式",
             examIn: "發佈來源",
             name: "名稱",

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

@@ -26,7 +26,7 @@ export default {
         return post('/common/lesson-record/delete-lesson-record', data)
     }, 
 	// 查询学校设置的课例类别
-	findLessonTags: function (data) {
+	findLessonSettings: function (data) {
         return post('/school/setting/find-id', data)
     }, 
 	// 设置学校的课例类别

+ 8 - 0
TEAMModelOS/ClientApp/src/api/studentWeb.js

@@ -331,4 +331,12 @@ export default {
     setNickName: function(data) {
         return post("/grouplist/upsert-group-member", data)
     },
+    // 获取评测成绩
+    getExamAch: function(data) {
+        return post("/student/stu-score", data)
+    },
+    // 获取作业成绩
+    getHwAch: function(data) {
+        return post("/student/stu-hw-score", data)
+    },
 }

+ 38 - 0
TEAMModelOS/ClientApp/src/assets/student-web/component_styles/paper-test.css

@@ -743,4 +743,42 @@
 }
 @media screen and (max-width: 500px) {
     
+}
+
+.lesson-test-pop .analysis .item-explain .item-explain-details-repair{
+  vertical-align: top;
+  display: inline-block;
+  width: calc(100% - 130px);
+}
+.lesson-test-pop .analysis .item-explain .item-explain-details-repair .repair-link-wrap-item-box{
+  display: flex;
+  position: relative;
+  /* // background-color: #e3e3e3; */
+  border-radius: 5px;
+  padding: 10px 0;
+  font-size: 14px;
+  
+}
+.lesson-test-pop .analysis .item-explain .item-explain-details-repair .repair-link-wrap-item-box:hover{
+    background-color: #ebe9e9;
+}
+
+
+.lesson-test-pop .analysis .item-explain .item-explain-details-repair .file-info{
+    margin-left: 10px;
+    
+}
+.lesson-test-pop .analysis .item-explain .item-explain-details-repair .file-info .file-name{
+    font-weight: bold;
+    margin-bottom: 5px;
+}
+
+.lesson-test-pop .analysis .item-explain .item-explain-details-repair .file-info span{
+    color: #16a3b5;
+    margin-right: 15px;
+    cursor: pointer;
+}
+
+.lesson-test-pop .analysis .item-explain .item-explain-details-repair .repair-link-wrap-item-box .file-icon > img{
+    width: 45px !important;
 }

+ 46 - 72
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperTest.vue

@@ -275,57 +275,20 @@
                         </div>
                         <div class="item-explain">
                             <span class="explain-title">【{{ $t("studentWeb.exam.report.repairSource") }}】</span>
-                            <div class="item-explain-details">
+                            <div class="item-explain-details-repair">
                                 <div v-if="showExam[queNo].repair.length != 0">
-                                    <Collapse style="width:85%" accordion @on-change="getSource(showExam[queNo].repair)">
-                                        <!-- 网络资源 -->
-                                        <Panel name="1">
-                                            {{$t("studentWeb.exam.report.linkSource")}}
-                                            <p slot="content">
-                                                <List border size="small">
-                                                    <ListItem v-for="(item, normalIndex) in repairSource.normal" :key="normalIndex">
-                                                        <span style="margin-right: 10px;" v-show="item.blobUrl">
-                                                            <Icon color="#0066FF" custom="iconfont icon-share_link" size="20" />
-                                                        </span><a :href="item.blobUrl" target="_blank">{{item.name}}</a>
-                                                    </ListItem>
-                                                    <ListItem v-show="!repairSource.normal.length">
-                                                        <span>{{$t("studentWeb.exam.report.noSource")}}</span>
-                                                    </ListItem>
-                                                </List>
-                                            </p>
-                                        </Panel>
-                                        <!-- 文件资源 -->
-                                        <Panel name="2">
-                                            {{$t("studentWeb.exam.report.fileSource")}}
-                                            <p slot="content">
-                                                <List border size="small">
-                                                    <ListItem v-for="(item, fileIndex) in repairSource.file" :key="fileIndex">
-                                                        <div style="width:100%">
-                                                            <span style="margin-right:10px;">
-                                                                <Icon v-if="item.fileType == 'zip'" custom="iconfont icon-zip" size="20" />
-                                                                <!-- <Icon v-else-if="item.fileType == 'zip'" color="#8199AF" custom="iconfont icon-zip" size="20" /> -->
-                                                                <Icon v-else-if="item.fileType == 'pdf'" color="#FF6464" custom="iconfont icon-pdf" size="20" />
-                                                                <Icon v-else-if="item.fileType == 'ppt'|| item.fileType == 'pptx'" color="#FF8976" custom="iconfont icon-ppt" size="20" />
-                                                                <Icon v-else-if="item.fileType == 'mp3'" color="#FF5562" custom="iconfont icon-mp3" size="20" />
-                                                                <Icon v-else-if="item.fileType == 'mp4'" color="#8E4C9E" custom="iconfont icon-video1" size="20" />
-                                                                <!-- <Icon v-else-if="item.fileType == 'zip'" custom="iconfont icon-video1" size="20" /> -->
-                                                                <Icon v-else-if="item.fileType == 'doc'||item.fileType =='docx'" color="#6CCBFF" custom="iconfont icon-word" size="20" />
-                                                                <Icon v-else-if="item.fileType == 'csv'||item.fileType =='xlsx'||item.fileType =='xls'" color="#25C273" custom="iconfont icon-xlsx" size="20" />
-                                                                <Icon v-else-if="item.fileType == 'jpg'||item.fileType =='png'||item.fileType =='jpeg'" color="#30D1CA" custom="iconfont icon-jpg" size="20" />
-                                                                <Icon v-else custom="iconfont icon-V" size="20" />
-                                                            </span><a @click="getItemData(item)">{{item.name}}</a>
-                                                            <span style="display:block;float:right;cursor:pointer">
-                                                                <Icon type="md-download" size="18" @click="downloadFile(item)" />
-                                                            </span>
-                                                        </div>
-                                                    </ListItem>
-                                                    <ListItem v-show="!repairSource.file.length">
-                                                        <span>{{$t("studentWeb.exam.report.noSource")}}</span>
-                                                    </ListItem>
-                                                </List>
-                                            </p>
-                                        </Panel>
-                                    </Collapse>
+                                    <div v-for="(repairSource, normalIndex) in showExam[queNo].repair" :key="normalIndex" class="repair-link-wrap-item-box">
+                                        <div class="file-icon">
+                                            <img :src="$tools.getFileThum(repairSource.type, repairSource.name)"/>
+                                        </div>
+                                        <div class="file-info">
+                                            <p class="file-name">{{ repairSource.name }}</p>
+                                            <div>
+                                                <span @click.stop="onPreview(repairSource)" v-if="repairSource.type !== 'other'">{{ $t('ability.review.preview')}}</span>
+                                                <span @click.stop="onDownload(repairSource)" v-if="repairSource.type !== 'link'">{{ $t('ability.review.download')}}</span>
+                                            </div>
+                                        </div>
+                                    </div>
                                 </div>
                                 <div v-else-if="!showExam[queNo].repair.length">
                                     {{ $t('studentWeb.exam.report.noSource') }}
@@ -400,28 +363,14 @@
                 <span>{{$t("studentWeb.exam.report.fileView")}}</span>
             </p>
             <div class="file-box" v-if="previewStatus">
-                <video v-if="previewFile.file == 'video'"
-                       id="previewVideo"
-                       :src="previewFile.blobUrl"
-                       width="870"
-                       controls="controls"
-                       style="max-height: 800px;">
-                    {{$t('teachContent.tips8')}}
-                </video>
-                <audio v-else-if="previewFile.file == 'audio'" controls>
-                    <source :src="previewFile.blobUrl">
-                </audio>
-                <img v-else-if="previewFile.fileType == 'jpg'||previewFile.fileType =='png'||previewFile.fileType =='jpeg' "
-                     :src="previewFile.blobUrl"
-                     style="border-radius: 5px;max-height: 800px;max-width:870px;" />
-                <pdf ref="pdf"
-                     v-else-if="previewFile.fileType == 'pdf'"
-                     v-for="(page,index) in numPages"
-                     :key="index"
-                     :src="previewFile.blobUrl"
-                     :page="page"></pdf>
-                <!--<span v-else-if="previewFile.fileType == 'doc'||previewFile.fileType == 'csv'||previewFile.fileType == 'pptx' ">{{'https://view.officeapps.live.com/op/view.aspx?src=' + previewFile.blobUrl}}</span>-->
-                <iframe v-else-if="previewFile.fileType == 'doc'||previewFile.fileType == 'csv'||previewFile.fileType == 'pptx'||previewFile.fileType == 'xls' " :src="'https://view.officeapps.live.com/op/view.aspx?src=' + previewFile.blobUrl" width='870' height='700' frameborder='1'></iframe>
+                <video v-if="previewFile.type == 'video'" id="previewVideo" :src="previewFile.blobUrl" width="870"
+					controls="controls" style="max-height: 800px;">
+					{{$t('teachContent.tips8')}}
+				</video>
+                <audio v-else-if="previewFile.type == 'audio'" controls>
+					<source :src="previewFile.blobUrl">
+					{{$t('teachContent.notAudio')}}
+				</audio>
                 <span v-else>{{$t("studentWeb.exam.report.noReview")}}</span>
             </div>
         </Modal>
@@ -1036,6 +985,26 @@
                 this.showExam[index].star = type
                 this.$forceUpdate()
             },
+            /* 预览 */
+            async onPreview(item){
+                let url = item.blobUrl
+                if (this.getSuffix(item.name) === 'pdf') {
+                    window.open('/web/viewer.html?file=' + encodeURIComponent(url));
+                } else if(item.type === 'doc') {
+                    window.open('https://view.officeapps.live.com/op/view.aspx?src=' + escape(url));
+                } else if(item.type === 'image') {
+                    this.$hevueImgPreview(url)
+                } else if(item.type === 'link') {
+					window.open(/^(http:|https:)/i.test(url) ? url : "http://" + url)
+                } else {
+                    this.previewFile = item
+                    this.previewStatus = true
+                }
+            },
+            /* 下载 */
+            async onDownload(item) {
+                this.$tools.doDownloadByUrl(item.blobUrl, item.name)
+            },
 
             /* ======未调用====== */
             getJudge() {
@@ -1183,6 +1152,11 @@
                     return false
                 }
             },
+            getSuffix() {
+                return name => {
+                    return name.substr(name.lastIndexOf(".") + 1)
+                }
+            },
         },
         // 导航守卫监听
         beforeRouteLeave(to, from, next) {

+ 28 - 0
TEAMModelOS/ClientApp/src/components/student-web/achievement/MyAchievement.less

@@ -0,0 +1,28 @@
+.achievement-report {
+    .report-head {
+        margin: 0 20px;
+    }
+
+    .filter-type {
+        display: flex;
+        width: 95%;
+        justify-content: space-between;
+        padding: 0;
+    }
+
+    .tag-style {
+        border: 1px solid;
+        padding: 0 5px;
+        border-radius: 3px;
+        margin-right: 10px;
+        border-color: rgb(143, 135, 135);
+        color: rgb(143, 135, 135);
+        font-size: 12px;
+    }
+
+    .search-exam {
+        color: #fff;
+        background-color: #24B880;
+        border-color: #24B880;
+    }
+}

+ 414 - 373
TEAMModelOS/ClientApp/src/components/student-web/achievement/MyAchievement.vue

@@ -1,11 +1,12 @@
 <template>
     <div class="achievement-report">
+        <Loading v-show="isLoading" bgColor="rgba(0, 0, 0, 0.3)"></Loading>
         <Tabs value="classrecord">
-            <TabPane label="评测成绩" name="exam">
+            <TabPane :label="$t('studentWeb.myAchievement.examAch')" name="exam">
                 <div class="report-head student-check">
                     <div class="filter-type">
                         <div>
-                            <span class="type-name">{{ $t('studentWeb.baseInfo.examMode') }}:</span>
+                            <span class="type-name">{{ $t('studentWeb.myAchievement.examMode') }}:</span>
                             <RadioGroup v-model="filterType.sourceType" type="button" button-style="solid">
                                 <Radio v-for="(item, index) in sourceTypeList" :key="index" :label="item.type">{{ item.name }}</Radio>
                             </RadioGroup>
@@ -17,72 +18,129 @@
                             </RadioGroup>
                         </div> -->
                         <div>
-                            <span class="type-name">{{ $t('studentWeb.baseInfo.examStatus') }}:</span>
+                            <span class="type-name">{{ $t('studentWeb.myAchievement.examIn') }}:</span>
                             <RadioGroup v-model="filterType.ownerType" type="button" button-style="solid">
                                 <Radio v-for="(item, index) in ownerTypeList" :key="index" :label="item.type">{{ item.name }}</Radio>
                             </RadioGroup>
                         </div>
                         <div>
-                            <span class="type-name">{{ '课程' }}:</span>
-                            <Select v-model="filterType.class" style="width:200px">
-                                <Option v-for="item in classList" :value="item.value" :key="item.value">{{ item.label }}</Option>
-                            </Select>
+                            <span class="type-name">{{ $t('learnActivity.createEv.evName') }}:</span>
+                            <Input v-model="filterType.examName" style="width: 300px" />
                         </div>
                     </div>
+                    <Button :class="{'search-exam': isSearch}" :disabled="!isSearch" @click="searchExam">
+                        {{ $t("cusMgt.searchHolder") }}
+                    </Button>
                 </div>
-                <Table :columns="examCol" :data="showSchoolRep" height="550" :span-method="handleSpan">
+                <Table :columns="examCol" :data="examRep" height="550">
+                    <template slot-scope="{ row }" slot="name">
+                        <p style="word-break: keep-all;">
+                            <template v-if="row.subjects.length">
+                                <span class="tag-style" style="border-color: #499c8d; color: #499c8d;"
+                                        v-for="sub in row.subjects" :key="sub.id"
+                                >
+                                    {{ sub.name }}
+                                </span>
+                            </template>
+                            <span>{{ row.name }}</span>
+                        </p>
+                    </template>
                     <template slot-scope="{ row }" slot="tag">
-                        <!-- <p>{{ row.name }}</p> -->
-                        <span class="tag-style">{{ row.tag.owner === 'school' ? $t('studentWeb.public.school') : $t('studentWeb.public.private')}}</span>
-                        <span class="tag-style" v-if="row.tag.source">{{ row.tag.source }}</span>
-                        <span class="tag-style" v-if="row.tag.type">{{ row.tag.type }}</span>
-                        <!-- <span class="tag-style">{{ row.tag.class }}</span> -->
-                        <span class="tag-style">{{ row.tag.list }}</span>
+                        <p style="word-break: keep-all;">
+                            <span class="tag-style" style="border-color: #88a1d8; color: #88a1d8" v-if="row.owner === 'school'">{{ $t('studentWeb.public.school') }}</span>
+                            <span class="tag-style" v-else>{{ $t('studentWeb.public.private') }}</span>
+                            <template v-if="row.source">
+                                <span class="tag-style" v-if="row.source === '0'">
+                                    {{ $t("studentWeb.exam.source.evMode1") }}
+                                </span>
+                                <span class="tag-style" style="border-color: #2f98a9; color: #2f98a9;" v-if="row.source === '2'">
+                                    {{ $t("studentWeb.exam.source.evMode3") }}
+                                </span>
+                                <template v-if="row.source === '1'">
+                                    <span class="tag-style" style="border-color: #2f98a9; color: #2f98a9;">
+                                        {{ $t("studentWeb.exam.source.evMode2") }}
+                                    </span>
+                                    <span class="tag-style" style="border-color: #76ae38; color: #76ae38;" v-if="row.qamode">
+                                        {{ $t("studentWeb.exam.source.evMode21") }}
+                                    </span>
+                                    <span class="tag-style" style="border-color: #b68268; color: #b68268;">
+                                        {{ $t("studentWeb.exam.source.evMode22") }}
+                                    </span>
+                                </template>
+                            </template>
+                            <span class="tag-style" v-if="row.type">{{ row.type }}</span>
+                            <template v-if="row.class.length">
+                                <span class="tag-style" v-for="cla in row.class" :key="cla.cId">
+                                    {{ cla.cname }}
+                                </span>
+                            </template>
+                        </p>
+                    </template>
+                    <template slot-scope="{ row }" slot="score">
+                        <template v-if="row.subjects.length">
+                            <span v-if="row.subjects.length === 1">
+                                {{ row.subjects[0].score ? row.subjects[0].score : 0 }}
+                            </span>
+                            <template v-else>
+                                <p>总分:{{ row.total }}</p>
+                                <p>
+                                    <span v-for="(sub, index) in row.subjects" :key="index" style="margin: 5px;">
+                                        {{ sub.name }}:{{ sub.score }}
+                                    </span>
+                                </p>
+                            </template>
+                        </template>
+                        <span v-else>-</span>
+                    </template>
+                    <template slot-scope="{ row }" slot="action">
+                        <Icon type="md-return-right" color="#24B880" style="cursor: pointer;" @click="toHwExam(row)" />
                     </template>
                 </Table>
             </TabPane>
-            <TabPane label="作业给分" name="homework">
+            <TabPane :label="$t('studentWeb.myAchievement.hwAch')" name="homework">
                 <div>
                     <p style="text-align: right; font-size: 18px; margin-right: 30px; margin-bottom: 10px;">
-                        平均分:{{ hwAverage }}
+                        {{ $t("studentWeb.myAchievement.average") }}:{{ hwAverage }}
                     </p>
                 </div>
                 <Table :columns="homeworkCol" :data="homeworkRep" height="550">
+                    <template slot-scope="{ row }" slot="score">
+                        <span v-if="row.score">{{ row.score }}</span>
+                        <span v-else>-</span>
+                    </template>
                     <template slot-scope="{ row }" slot="star">
-                        <Rate v-model="row.star" />
+                        <Rate disabled v-model="row.star" v-if="row.star" />
+                        <span v-else>-</span>
+                    </template>
+                    <template slot-scope="{ row }" slot="replies">
+                        <template v-if="row.replies.length">
+                            <div v-for="(replies, index) in row.replies" :key="index">
+                                {{ replies }}
+                            </div>
+                        </template>
+                        <span v-else>-</span>
+                    </template>
+                    <template slot-scope="{ row }" slot="stuStar">
+                        <Rate disabled v-model="row.stuStar" v-if="row.stuStar" />
+                        <span v-else>-</span>
+                    </template>
+                    <template slot-scope="{ row }" slot="action">
+                        <Icon type="md-return-right" color="#24B880" style="cursor: pointer;" @click="toHwExam(row, 'hw')" />
                     </template>
                 </Table>
             </TabPane>
-            <TabPane label="老师给分" name="classrecord">
+            <TabPane :label="$t('studentWeb.myAchievement.cusAch')" name="classrecord">
                 <div style="margin-left: 20px;">
                     <div class="filter-type">
-                        <!-- <div>
-                            <span class="type-name">{{ $t('studentWeb.baseInfo.examMode') }}:</span>
-                            <RadioGroup v-model="filterType.sourceType" type="button" button-style="solid">
-                                <Radio v-for="(item, index) in sourceTypeList" :key="index" :label="item.type">{{ item.name }}</Radio>
-                            </RadioGroup>
-                        </div> -->
-                        <!-- <div>
-                            <span class="type-name">{{ $t('studentWeb.baseInfo.examStatus') }}:</span>
-                            <RadioGroup v-model="examType" type="button" button-style="solid">
-                                <Radio v-for="(item, index) in examTypeList" :key="index + 1" :label="item.type">{{ item.name }}</Radio>
-                            </RadioGroup>
-                        </div> -->
-                        <!-- <div>
-                            <span class="type-name">{{ $t('studentWeb.baseInfo.examStatus') }}:</span>
-                            <RadioGroup v-model="filterType.ownerType" type="button" button-style="solid">
-                                <Radio v-for="(item, index) in ownerTypeList" :key="index" :label="item.type">{{ item.name }}</Radio>
-                            </RadioGroup>
-                        </div> -->
                         <div>
-                            <span class="type-name">{{ '课程' }}:</span>
+                            <span class="type-name">{{ $t("studentWeb.myAchievement.class") }}:</span>
                             <Select v-model="filterType.class" style="width:200px">
                                 <Option v-for="item in classList" :value="item.value" :key="item.value">{{ item.label }}</Option>
                             </Select>
                         </div>
                     </div>
                     <p style="text-align: right; font-size: 18px; margin-right: 30px; margin-bottom: 10px;">
-                        总分:{{ classRecTotal }}
+                        {{ $t("learnActivity.simple.total") }}:{{ classRecTotal }}
                     </p>
                 </div>
                 <Table :columns="classCol" :data="classRep" height="550">
@@ -96,45 +154,37 @@
 </template>
 
 <script>
+import { mapState } from 'vuex';
 export default {
     name: "MyAchievement",
     data () {
         return {
             MyNo: "7",
             MyName: this.$t('studentWeb.type.achievement'),
+            isLoading: false,
             examCol: [
-                /* {
-                    width: 50,
-                    type: 'expand',
-                }, */
                 {
-                    title: '评测名称',
-                    // slot: "name",
-                    key: 'examName',
-                    align: "center"
+                    title: this.$t("studentWeb.myAchievement.name"),
+                    slot: "name",
+                    // key: 'examName',
+                    align: "center",
                     // tree: true,
                     // width: 500,
                 },
                 {
-                    title: '创建人',
-                    key: 'creator',
-                    align: "center"
-                },
-                {
-                    title: '学科',
-                    key: 'class',
-                    align: "center"
+                    title: this.$t("studentWeb.myAchievement.tag"),
+                    slot: "tag",
+                    align: "center",
                 },
                 {
-                    title: '分数',
-                    // slot: "score",
-                    key: "score",
-                    align: "center"
+                    title: this.$t("studentWeb.myAchievement.creator"),
+                    key: 'creator',
+                    align: "center",
                 },
                 {
-                    title: '类型',
-                    slot: "tag",
-                    width: 500,
+                    title: this.$t("studentWeb.myAchievement.score"),
+                    slot: "score",
+                    // key: "score",
                     align: "center"
                 },
                 /* {
@@ -142,18 +192,22 @@ export default {
                     key: 'list'
                 }, */
                 {
-                    title: '时间',
-                    key: 'time',
-                    align: "center"
-                }
+                    title: this.$t("studentWeb.myAchievement.createTime"),
+                    key: 'startTime',
+                    align: "center",
+                    maxWidth: 160,
+                },
+                {
+                    title: this.$t("studentWeb.myAchievement.action"),
+                    slot: "action",
+                    align: "center",
+                    maxWidth: 100,
+                },
             ],
+            examRep: [],
             homeworkCol: [
-                /* {
-                    width: 50,
-                    type: 'expand',
-                }, */
                 {
-                    title: '作业名称',
+                    title: this.$t("studentWeb.myAchievement.name"),
                     // slot: "name",
                     key: 'name',
                     align: "center"
@@ -161,304 +215,99 @@ export default {
                     // width: 500,
                 },
                 {
-                    title: '创建人',
+                    title: this.$t("studentWeb.myAchievement.creator"),
                     key: 'creator',
                     align: "center"
                 },
-                /* {
-                    title: '学科',
-                    key: 'class',
-                    align: "center"
-                }, */
                 {
-                    title: '评分',
-                    // slot: "score",
-                    key: "score",
+                    title: this.$t("studentWeb.myAchievement.teaScore"),
+                    slot: "score",
+                    // key: "score",
                     align: "center"
                 },
                 {
-                    title: '评星',
+                    title: this.$t("studentWeb.myAchievement.star"),
                     slot: "star",
                     // key: "star",
                     align: "center"
                 },
                 {
-                    title: '评语',
-                    // slot: "score",
-                    key: "replies",
+                    title: this.$t("studentWeb.myAchievement.comment"),
+                    slot: "replies",
+                    // key: "replies",
                     align: "center"
                 },
-                /* {
-                    title: '类型',
-                    slot: "tag",
-                    width: 500,
-                    align: "center"
-                }, */
-                /* {
-                    title: '名单',
-                    key: 'list'
-                }, */
                 {
-                    title: '时间',
-                    key: 'time',
+                    title: this.$t("studentWeb.myAchievement.averageStar"),
+                    slot: "stuStar",
+                    // key: "star",
                     align: "center"
-                }
+                },
+                {
+                    title: this.$t("studentWeb.myAchievement.createTime"),
+                    key: 'startTime',
+                    align: "center",
+                    // maxWidth: 170,
+                },
+                {
+                    title: this.$t("studentWeb.myAchievement.action"),
+                    slot: "action",
+                    align: "center",
+                    maxWidth: 100,
+                },
             ],
+            homeworkRep: [],
             classCol: [
                 /* {
                     width: 50,
                     type: 'expand',
                 }, */
                 {
-                    title: '名称',
+                    title: this.$t("studentWeb.myAchievement.name"),
                     // slot: "name",
                     key: 'name',
-                    align: "center"
+                    align: "center",
                     // tree: true,
                     // width: 500,
                 },
                 {
-                    title: '授课老师',
+                    title: this.$t("studentWeb.myAchievement.creator"),
                     key: 'creator',
                     align: "center"
                 },
                 {
-                    title: '课程',
+                    title: this.$t("studentWeb.myAchievement.class"),
                     key: 'class',
                     align: "center"
                 },
                 {
-                    title: '记分',
+                    title: this.$t("studentWeb.myAchievement.point"),
                     // slot: "score",
                     key: "score",
                     align: "center"
                 },
-                {
+                /* {
                     title: '时长',
                     key: "time",
                     width: 500,
                     align: "center"
-                },
+                }, */
                 /* {
                     title: '名单',
                     key: 'list'
                 }, */
                 {
-                    title: '时间',
+                    title: this.$t("studentWeb.myAchievement.createTime"),
                     key: 'startTime',
                     align: "center"
                 }
             ],
-            schoolRep: [
-                {
-                    id: "1111111111111111",
-                    examName: '评测名称11111111',
-                    class: '语文',
-                    tag: {
-                        owner: "school",
-                        source: "课中评测",
-                        type: "正规考",
-                        class: '课程111',
-                        list: '名单1111111111',
-                    },
-                    creator: '罗老师',
-                    list: '名单1111111111',
-                    score: 100,
-                    time: '2021-06-27',
-                    children: [
-                        {
-                            id: "1111111111111112",
-                            name: '英语',
-                            class: '课程111',
-                            tag: {
-                                owner: "校级",
-                                source: "课中评测",
-                                type: "正规考",
-                                class: '课程111',
-                                list: '名单1111111111',
-                            },
-                            creator: '罗老师',
-                            list: '名单1111111111',
-                            score: 67,
-                            time: '2021-06-27',
-                        },
-                        {
-                            id: "1111111111111113",
-                            name: '数学',
-                            class: '课程111',
-                            tag: {
-                                owner: "校级",
-                                source: "课中评测",
-                                type: "正规考",
-                                class: '课程111',
-                                list: '名单1111111111',
-                            },
-                            creator: '罗老师',
-                            list: '名单1111111111',
-                            score: 85,
-                            time: '2021-06-27',
-                        },
-                        {
-                            id: "1111111111111113",
-                            name: '语文',
-                            class: '课程111',
-                            tag: {
-                                owner: "校级",
-                                source: "课中评测",
-                                type: "正规考",
-                                class: '课程111',
-                                list: '名单1111111111',
-                            },
-                            creator: '罗老师',
-                            list: '名单1111111111',
-                            score: 99,
-                            time: '2021-06-27',
-                        },
-                    ]
-                },
-                {
-                    id: "2222222222222222",
-                    examName: '评测名称222222222',
-                    class: '课程3222222222',
-                    tag: {
-                        owner: "个人",
-                        source: "线上评测",
-                        type: "模拟考",
-                        class: '课程3222222222',
-                        list: '名单22222',
-                    },
-                    creator: '罗老师',
-                    list: '名单22222',
-                    score: 97,
-                    time: '2022-01-14',
-                    children: []
-                },
-                {
-                    id: "33333333333",
-                    examName: '评测名称3333333',
-                    class: '课程3333333',
-                    tag: {
-                        owner: "个人",
-                        source: "线上评测",
-                        type: "模拟考",
-                        class: '课程3333333',
-                        list: '名单43333333333',
-                    },
-                    creator: '罗老师',
-                    list: '名单43333333333',
-                    score: 10,
-                    time: '2022-01-14',
-                    children: []
-                },
-                {
-                    id: "66666666666",
-                    examName: '学校考试',
-                    class: '语文',
-                    tag: {
-                        owner: "school",
-                        source: "线上评测",
-                        type: "月月考",
-                        class: '课程111',
-                        list: '名单1111111111',
-                    },
-                    creator: '罗老师',
-                    list: '名单1111111111',
-                    score: 100,
-                    time: '2021-06-27',
-                    children: [
-                        {
-                            id: "1111111111111112",
-                            name: '地理',
-                            class: '课程111',
-                            tag: {
-                                owner: "校级",
-                                source: "课中评测",
-                                type: "正规考",
-                                class: '课程111',
-                                list: '名单1111111111',
-                            },
-                            creator: '罗老师',
-                            list: '名单1111111111',
-                            score: 20,
-                            time: '2021-06-27',
-                        },
-                        {
-                            id: "1111111111111113",
-                            name: '化学',
-                            class: '课程111',
-                            tag: {
-                                owner: "校级",
-                                source: "课中评测",
-                                type: "正规考",
-                                class: '课程111',
-                                list: '名单1111111111',
-                            },
-                            creator: '罗老师',
-                            list: '名单1111111111',
-                            score: 15,
-                            time: '2021-06-27',
-                        },
-                        {
-                            id: "1111111111111113",
-                            name: '生物',
-                            class: '课程111',
-                            tag: {
-                                owner: "校级",
-                                source: "课中评测",
-                                type: "正规考",
-                                class: '课程111',
-                                list: '名单1111111111',
-                            },
-                            creator: '罗老师',
-                            list: '名单1111111111',
-                            score: 100,
-                            time: '2021-06-27',
-                        },
-                    ]
-                },
-                {
-                    id: "4444444444",
-                    examName: '评测名称4444',
-                    class: '课程444',
-                    tag: {
-                        owner: "个人",
-                        source: "线上评测",
-                        type: "模拟考",
-                        class: '课程444',
-                        list: '名单4444',
-                    },
-                    creator: '罗老师',
-                    list: '名单4444',
-                    score: 61,
-                    time: '2022-01-14',
-                    children: []
-                },
-                {
-                    id: "555555555555",
-                    examName: '评测名称55',
-                    class: '课程5555555555555555555',
-                    tag: {
-                        owner: "个人",
-                        source: "线上评测",
-                        type: "模拟考",
-                        class: '课程5555555555555555555',
-                        list: '名单5555555',
-                    },
-                    creator: '罗老师',
-                    list: '名单5555555',
-                    score: 27,
-                    time: '2022-01-14',
-                    children: []
-                },
-            ],
-            showSchoolRep: [],
-            num: [],
-            homeworkRep: [],
             classRep: [],
+            num: [],
             filterType: {
                 ownerType: "all",
                 sourceType: "all",
-                class: ""
+                examName: ""
             },
             ownerTypeList: [
                 {
@@ -512,6 +361,9 @@ export default {
             ],
             classRecTotal: 0,
             hwAverage: 0,
+            continuationTokenHw: null,
+            continuationTokenExam: null,
+            isSearch: false,
         }
     },
     created () {
@@ -520,31 +372,10 @@ export default {
         this.$store.commit("ToggleSidebar", true);
     },
     mounted () {
-        this.schoolRep.forEach(item => {
-            if(item.children.length) {
-                item.children.forEach(child => {
-                    let data = {...item}
-                    delete data.children
-                    data.mergeCol = item.children.length
-                    data.class = child.name
-                    data.score = child.score
-                    this.showSchoolRep.push(data)
-                })
-            } else {
-                let data = {...item}
-                delete data.children
-                this.showSchoolRep.push(data)
-            }
-        });
-        this.showSchoolRep = this.showSchoolRep.map((item, index) => {
-            for (let i = index + 1; i < this.showSchoolRep.length; i++) {
-                if(item.id === this.showSchoolRep[i].id && !this.num.includes(index)) {
-                    this.num.push(i)
-                }
-            }
-            return item
-        });
+        document.getElementsByClassName("ivu-table-body")[0].addEventListener("scroll", this.examScroll)
+        document.getElementsByClassName("ivu-table-body")[1].addEventListener("scroll", this.hwScroll)
         this.getRecordList()
+        this.getExamList()
         this.getHwList()
     },
     methods: {
@@ -599,54 +430,264 @@ export default {
                 this.classRecTotal += item.score
             })
         },
+        getExamList() {
+            let param = {
+                userid: this.userInfo.sub,
+                userType: "",
+                school: this.userInfo.azp,
+                type: 'Exam',
+                continuationToken: this.continuationTokenExam,
+                count: 20,
+            }
+            if(this.isSearch) {
+                if(this.filterType.ownerType != 'all') {
+                    param.owner = this.filterType.ownerType
+                }
+                if(this.filterType.sourceType != 'all') {
+                    param.source = this.filterType.sourceType
+                }
+                if(this.filterType.examName != '') {
+                    param.name = this.filterType.examName
+                }
+            }
+            param.userType = this.userInfo.scope === "tmduser" ? "tmdid" : "schoolid"
+            this.$api.studentWeb.getExamAch(param).then(async res => {
+                if(!res.code) {
+                    this.continuationTokenExam = res.continuationToken
+                    if(res.result.length) {
+                        let teaId = []
+                        res.result.forEach(results => {
+                            teaId.push(results.createId)
+                        })
+                        let teaName = teaId.length ? await this.getTeacherName(teaId) : []
+                        res.result.forEach(result => {
+                            let examData = {...result}
+                            examData.startTime = this.dateFormat(result.time)
+                            let nameIds = teaName.find(names => {
+                                return names.id === result.createId
+                            })
+                            examData.creator = !nameIds ? undefined : nameIds.name
+                            examData.type = ""
+                            examData.total = 0
+                            examData.class = []
+                            if(result.ext) {
+                                examData.subjects = result.ext.subjects ? result.ext.subjects : []
+                                examData.type = result.ext.type
+                            }
+                            if(result.result.length) {
+                                result.result.forEach(score => {
+                                    // 当前班级是否有本人,有才加入examData.class
+                                    let myId = score.sIds.findIndex(sId => {
+                                        return sId === this.userInfo.sub
+                                    })
+                                    if(myId != -1) {
+                                        // examData.class有值,判断当前班级是否存在
+                                        if(examData.class.length) {
+                                            let haveCid = examData.class.find(myClass => {
+                                                return myClass.cId === score.cId
+                                            })
+                                            if(!haveCid) {
+                                                examData.class.push({
+                                                    cId: score.cId,
+                                                    cname: score.cname
+                                                })
+                                            }
+                                        } else {
+                                            examData.class.push({
+                                                cId: score.cId,
+                                                cname: score.cname
+                                            })
+                                        }
+                                        if(examData.subjects.length) {
+                                            // 查找当前科目
+                                            let subjIn = examData.subjects.findIndex(subj => {
+                                                return subj.id === score.sub
+                                            })
+                                            if(subjIn != -1) {
+                                                examData.subjects[subjIn].score = score.sum[myId]
+                                            }
+                                        } else {
+                                            examData.subjects.push({
+                                                id: score.sub,
+                                                name: null,
+                                                score: 0
+                                            })
+                                        }
+                                    }
+                                })
+                                let totalScore = 0
+                                examData.subjects.forEach(subje => {
+                                    totalScore += (subje.score ? subje.score : 0)
+                                })
+                                examData.total = totalScore
+                            }
+                            this.examRep.push(examData)
+                        })
+                    }
+                }
+            }).finally(() => {
+                this.isLoading = false
+            })
+        },
         getHwList() {
-            this.homeworkRep = [
-                {
-                    id: "1111111111",
-                    name: '作业1111111',
-                    class: '课程5555555555555555555',
-                    creator: '罗老师',
-                    list: '名单5555555',
-                    score: 27,
-                    star: 0,
-                    replies: "非常棒",
-                    time: '2022-01-14',
-                },
-                {
-                    id: "22222222",
-                    name: '作业22222222',
-                    class: '课程5555555555555555555',
-                    creator: '张老师',
-                    list: '名单5555555',
-                    score: 97,
-                    star: 4.5,
-                    replies: "非常棒!!!!!!!!!",
-                    time: '2022-01-14',
-                },
-                {
-                    id: "3333333",
-                    name: '作业作业作业3333333',
-                    class: '课程5555555555555555555',
-                    creator: '助教',
-                    list: '名单5555555',
-                    score: 77,
-                    star: 1.5,
-                    replies: "##########",
-                    time: '2022-01-14',
-                },
-            ]
-            let total = 0
-            this.homeworkRep.forEach(item => {
-                total += item.score
+            let param = {
+                userid: this.userInfo.sub,
+                userType: "",
+                school: this.userInfo.azp,
+                type: 'Homework',
+                // source: "1",
+                // owner: "teacher",
+                // name: "测",
+                continuationToken: this.continuationTokenHw,
+                count: 20,
+            }
+            param.userType = this.userInfo.scope === "tmduser" ? "tmdid" : "schoolid"
+            this.$api.studentWeb.getHwAch(param).then(async res => {
+                this.continuationTokenHw = res.continuationToken
+                if(res.works.length) {
+                    let teaId = []
+                    res.works.forEach(works => {
+                        teaId.push(works.createId)
+                    })
+                    let teaName = teaId.length ? await this.getTeacherName(teaId) : []
+                    res.works.forEach(work => {
+                        let hwData = {
+                            id: work.id,
+                            name: work.name,
+                            startTime: this.dateFormat(work.time),
+                            score: 0,
+                            star: 0,
+                            creator: null,
+                            replies: [],
+                            stuStar: 0,
+                        }
+                        let nameIds = teaName.find(names => {
+                            return names.id === work.createId
+                        })
+                        hwData.creator = !nameIds ? undefined : nameIds.name
+                        if(work.score.length && work.score[0].comments.length) {
+                            hwData.score = work.score[0].score
+                            let comments = work.score[0].comments.find(item => {
+                                return item.identity === 'teacher'
+                            })
+                            if(comments) {
+                                hwData.star = comments.star
+                                work.comment.forEach(comment => {
+                                    comment.reply.forEach(reply => {
+                                        if(comments.replyIds.includes(reply.id)){
+                                            hwData.replies.push(reply.comment)
+                                        }
+                                    })
+                                })
+                            }
+                            let starTotal = 0
+                            let starNum = 0
+                            work.score[0].comments.forEach(item => {
+                                if(item.identity === 'student') {
+                                    starTotal += item.star
+                                    starNum += 1
+                                }
+                            })
+                            hwData.stuStar = starNum ? (Number((starTotal / starNum).toFixed(1))) : 0
+                        }
+                        this.homeworkRep.push(hwData)
+                    })
+                    let total = 0
+                    this.homeworkRep.forEach(item => {
+                        total += item.score
+                    })
+                    this.hwAverage = (total / this.homeworkRep.length).toFixed(1)
+                }
+            }).finally(() => {
+                this.isLoading = false
             })
-            this.hwAverage = total / this.homeworkRep.length
+        },
+        //时间格式化处理
+        dateFormat(timestamp) {
+            var date = new Date(timestamp)
+            var Y = date.getFullYear() + '-'
+            var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'
+            var D = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate()) + ' '
+            var H = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ":"
+            var Min = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes())
+            var S = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()) + " "
+            return Y + M + D + H + Min;
+        },
+        getTeacherName(id) {
+            return new Promise((r, j) => {
+                this.$api.studentWeb.getTeacherName({id}).then(res => {
+                    r(res)
+                }).catch(e => {
+                    j(e)
+                })
+            })
+        },
+        examScroll() {
+            let scrollTop = document.getElementsByClassName('ivu-table-body')[0].scrollTop;
+            let clientHeight = document.getElementsByClassName('ivu-table-body')[0].clientHeight;
+            let scrollHeight = document.getElementsByClassName('ivu-table-body')[0].scrollHeight;
+            if (scrollHeight - (scrollTop + clientHeight) <= 1) {//判断滚动条是否滚动到底部
+                if(!this.continuationTokenExam) {
+                    this.$Message.warning("已经到底了")
+                } else {
+                    this.isLoading = true
+                    this.getExamList()
+                }
+            }
+        },
+        hwScroll() {
+            let scrollTop = document.getElementsByClassName('ivu-table-body')[1].scrollTop;
+            let clientHeight = document.getElementsByClassName('ivu-table-body')[1].clientHeight;
+            let scrollHeight = document.getElementsByClassName('ivu-table-body')[1].scrollHeight;
+            if (scrollHeight - (scrollTop + clientHeight) <= 1) {//判断滚动条是否滚动到底部
+                if(!this.continuationTokenHw) {
+                    this.$Message.warning("已经到底了")
+                } else {
+                    this.isLoading = true
+                    this.getHwList()
+                }
+            }
+        },
+        toHwExam(row, type) {
+            if(type === 'hw') {
+                this.$router.push({
+                    path: "/studentWeb/homeworkView",
+                    query: {aId: row.id}
+                })
+            } else {
+                this.$router.push({
+                    path: "/studentWeb/examView",
+                    query: {aId: row.id}
+                })
+            }
+        },
+        searchExam() {
+            this.continuationTokenExam = null
+            this.examRep = []
+            this.getExamList()
         },
     },
+    watch: {
+        filterType: {
+            deep: true,
+            handler(n, o) {
+                if(n) {
+                    this.isSearch = true
+                }
+            }
+        },
+    },
+    computed: {
+        ...mapState({
+            userInfo: state => state.userInfo,
+        })
+    }
 }
 </script>
 
 <style lang="less" scoped>
 @import "../HomeView/CourseView/SchoolReport.less";
+@import "./MyAchievement.less";
 </style>
 
 

+ 6 - 20
TEAMModelOS/ClientApp/src/locale/index.js

@@ -1,35 +1,21 @@
 import Vue from 'vue'
 import VueI18n from 'vue-i18n'
-// 引入iView多语言包
-import zhLocale from 'view-design/src/locale/lang/zh-CN'
-import enLocale from 'view-design/src/locale/lang/en-US'
-import twLocale from 'view-design/src/locale/lang/zh-TW'
-// 引入elementUI多语言包
-import enEl from 'element-ui/lib/locale/lang/en'
-import zhEl from 'element-ui/lib/locale/lang/zh-CN'
-import twEl from 'element-ui/lib/locale/lang/zh-TW'
+import tools from '@/utils/public.js'
 import ElementLocale from 'element-ui/lib/locale'
 ElementLocale.i18n((key, value) => i18n.t(key, value))
 Vue.use(VueI18n)
 // 自动根据浏览器系统语言设置语言(优先判断本地设置的语言,如果有则使用本地设置的语言,如果没有则使用浏览器系统语言)
-const curLang  = localStorage.getItem('cloudSetting') ? JSON.parse(localStorage.getItem('cloudSetting')).curLang : null
+const curLang = localStorage.getItem('cloudSetting') ? JSON.parse(localStorage.getItem('cloudSetting')).curLang : null
 const navLang = curLang || navigator.language.toLowerCase()
 const localLang = (navLang === 'zh' || navLang === 'zh-tw' || navLang === 'zh-cn' || navLang === 'zh-hk') ? navLang : false
 let lang = localLang || 'en-us'
 localStorage.setItem('local', lang)
 Vue.config.lang = lang
 Vue.locale = () => { }
-// 将本地语言包、elementUI语言包、iview语言包合并
-const messages = {
-    'zh-cn': Object.assign(LANG_ZH_CN, zhLocale, zhEl),
-    'zh': Object.assign(LANG_ZH_CN, zhLocale,zhEl),
-    'zh-tw': Object.assign(LANG_ZH_TW, twLocale,twEl),
-    'zh-hk': Object.assign(LANG_ZH_TW, twLocale, twEl),
-    'en-us': Object.assign(LANG_EN_US, enLocale, enEl)
-}
 const i18n = new VueI18n({
-  locale: lang,
-  messages
+  locale: lang
+})
+setTimeout(() => {
+  tools.seti18nLang(lang)
 })
-
 export default i18n

+ 47 - 3
TEAMModelOS/ClientApp/src/utils/public.js

@@ -8,12 +8,22 @@ import JsPDF from 'jspdf'
 import 'jspdf-autotable'
 import domtoimage from '@/utils/dom_to_image';
 import excel from '@/utils/excel.js'
+import i18n from '@/locale'
+// 引入iView多语言包
+import zhLocale from 'view-design/src/locale/lang/zh-CN'
+import enLocale from 'view-design/src/locale/lang/en-US'
+import twLocale from 'view-design/src/locale/lang/zh-TW'
+// 引入elementUI多语言包
+import enEl from 'element-ui/lib/locale/lang/en'
+import zhEl from 'element-ui/lib/locale/lang/zh-CN'
+import twEl from 'element-ui/lib/locale/lang/zh-TW'
 import {
 	app
 } from '@/boot-app.js'
 import {
 	Message
 } from 'view-design'
+
 /**
  * 校验blob授权是否过期
  * */
@@ -178,6 +188,40 @@ export default {
 		'justify', // 对齐方式
 		'table', // 表格
 	],
+	/* 切换语系 */
+	changeLang(lang) {
+		// 如果已经加载过语言包 则直接设置 否则加载语言包
+		if (lang.toLowerCase() === 'zh-cn' && window.LANG_ZH_CN) {
+			this.seti18nLang(lang)
+		} else if (lang.toLowerCase() === 'zh-tw' && window.LANG_ZH_TW) {
+			this.seti18nLang(lang)
+		} else if (lang.toLowerCase() === 'en-us' && window.LANG_EN_US) {
+			this.seti18nLang(lang)
+		} else {
+			this.loadLangJs(lang)
+		}
+	},
+	/* 加载语言包 */
+	loadLangJs(lang) {
+		let script = document.createElement('script')
+		script.type = 'text/javascript'
+		script.src = 'https://' + window.location.host + '/lang/' + lang + '.js'
+		document.getElementsByTagName('head')[0].appendChild(script)
+		script.onload = () => {
+			this.seti18nLang(lang)
+		}
+	},
+	/* 设置到i18n语系 */
+	seti18nLang(lang) {
+		if (lang.toLowerCase() === 'zh-cn') {
+			i18n.setLocaleMessage(lang, Object.assign(LANG_ZH_CN, zhLocale, zhEl))
+		} else if (lang.toLowerCase() === 'zh-tw') {
+			i18n.setLocaleMessage(lang, Object.assign(LANG_ZH_TW, twLocale, twEl))
+		} else {
+			i18n.setLocaleMessage(lang, Object.assign(LANG_EN_US, enLocale, enEl))
+		}
+		document.title = app.$t('system.title')
+	},
 	/* 全屏 */
 	fullScreen(element) {
 		// var element = document.documentElement;
@@ -266,14 +310,14 @@ export default {
 						if (videoTrack && audioTrack) {
 							let videoFormat = videoTrack.Format.toLowerCase()
 							let audioFormat = audioTrack.Format.toLowerCase()
-							if((videoFormat === 'avc' && audioFormat === 'aac') || (['vp8','vp9'].includes(videoFormat) && audioFormat === 'vorbis')) {
+							if ((videoFormat === 'avc' && audioFormat === 'aac') || (['vp8', 'vp9'].includes(videoFormat) && audioFormat === 'vorbis')) {
 								r(videoTrack.Format_Profile || true)
-							}else{
+							} else {
 								r(false)
 							}
 						}
 						// 如果是音频文件则需要满足 mp3(MPEG Audio),ogg(Vorbis),wav(PCM) 任意一种编码格式
-						else if (!videoTrack && audioTrack && ['mpeg audio','vorbis','pcm'].includes(audioTrack.Format.toLowerCase())) {
+						else if (!videoTrack && audioTrack && ['mpeg audio', 'vorbis', 'pcm'].includes(audioTrack.Format.toLowerCase())) {
 							r(true)
 						} else {
 							r(false)

+ 1 - 1
TEAMModelOS/ClientApp/src/view/areaMgmt/AreaData.vue

@@ -287,7 +287,7 @@ export default {
                     key: 'online'
                 },
                 {
-                    title: '选修能力点',
+                    title: '所选微能力点',
                     key: 'points'
                 },
                 {

+ 0 - 360
TEAMModelOS/ClientApp/src/view/learnactivity/MgtPrivEva.less

@@ -1,360 +0,0 @@
-@main-bgColor: rgb(40,40,40); //主背景颜色
-@primary-textColor: #fff; //文本主颜色
-@second-textColor: var(--second-text-color); //文本副级颜色
-@primary-fontSize: 14px;
-@second-fontSize: 16px;
-@status-pending: #1CC0F3;
-
-.manage-evaluation-container {
-    width: 100%;
-    height: 100%;
-    display: flex;
-    flex-direction: row;
-
-    .evaluation-list-wrap {
-        width:100%;
-        height: 100%;
-        color: white;
-    }
-
-    .evaluation-detail-wrap {
-        width:100%;
-        height: 100%;
-        padding-left: 15px;
-    }
-}
-
-.test-paper-analysis {
-    color: white;
-    cursor: pointer;
-    float: right;
-    margin-right: 45px;
-    &:hover {
-        color: aqua;
-    }
-}
-
-.evaluation-list-wrap {
-    .evaluation-list-title {
-        width: 100%;
-        height: 45px;
-        padding-left: 15px;
-        line-height: 45px;
-        // border-bottom: 1px solid var(--border-color);
-        color: var(--second-text-color);
-    }
-
-    .evaluation-list-main {
-        width: 100%;
-        height: ~"calc(100% - 45px)";
-
-        .evaluation-item {
-            // border-bottom: 1px solid var(--border-color);
-            padding: 15px 10px 15px 15px;
-            cursor: pointer;
-
-            &:hover{
-                background-color: var(--hover-text-color);
-            }
-
-            .evaluation-name {
-                font-size: 16px;
-                color: var(--primary-text-color);
-            }
-
-            .evaluation-type {
-                color: @second-textColor;
-                margin-top: 4px;
-                font-size: 12px;
-
-                .ivu-icon{
-                    color: #70B1E7;
-                }
-            }
-
-            .evaluation-type span {
-                margin-right: 10px;
-            }
-        }
-    }
-}
-
-.evaluation-detail-wrap {
-
-    .evaluation-detail-bar {
-        width: 100%;
-        height: 45px;
-        line-height: 45px;
-        // border-bottom: 1px solid var(--border-color);
-        color: @second-textColor;
-
-        .edit-evaluation {
-            float: right;
-            margin-right: 45px;
-            display: inline-block;
-            cursor: pointer;
-            color: white;
-        }
-
-        .edit-evaluation:hover {
-            color: aqua;
-        }
-
-        .evalustion-bar-item {
-            margin-right: 40px;
-            cursor: pointer;
-        }
-    }
-
-    .evaluation-base-info {
-        width: 100%;
-        height: ~"calc(100% - 45px)";
-
-        .evalustion-base-attr {
-            width: 350px;
-            height: 100%;
-            border-right: 1px solid var(--border-color);
-
-            .evalustion-base-attr-header {
-                height: 45px;
-                line-height: 45px;
-                color: @second-textColor;
-                border-bottom: 1px solid var(--border-color);
-                margin-bottom: 25px;
-            }
-
-            .evaluation-attr-form {
-                margin-right: 15px;
-                /*color: white;*/
-            }
-        }
-
-        .evaluation-test-paper-header {
-            height: 45px;
-            line-height: 45px;
-            color: @second-textColor;
-            border-bottom: 1px solid var(--border-color);
-            margin-bottom:15px;
-        }
-    }
-}
-
-.subject-item {
-    display: inline-block;
-    margin-right: 15px;
-    color: @second-textColor;
-    cursor: pointer;
-    line-height: 40px;
-    min-width: 50px;
-    font-size: 15px;
-    text-align: center;
-    margin-left: 15px;
-}
-
-.subject-item-active {
-    color: @primary-textColor;
-    border-bottom: 2px solid white;
-    font-weight: 500;
-}
-
-.test-paper-detail {
-    width: 100%;
-    // height: ~"calc(100% - 45px)";
-    height: 100%;
-    color: white;
-}
-
-.question-type-count {
-    color: white;
-    font-size: 16px;
-    display: inline-block;
-    margin-top: 15px;
-    margin-bottom: 5px;
-}
-.to-create-icon {
-    float: right;
-    margin-right: 20px;
-    margin-top: 12px;
-    cursor: pointer;
-    color: var(--normal-icon-color);
-    font-size: 18px;
-}
-
-.test-paper-detail .back-to-top {
-    position: fixed;
-    right: 50px;
-    bottom: 30px;
-    height: 48px;
-    width: 50px;
-    background: #595959;
-    z-index: 99999;
-    cursor: pointer;
-    display: flex;
-    flex-direction: column;
-    justify-content: center;
-    align-items: center;
-}
-
-.test-paper-detail .back-to-top:hover {
-    background: rgb(128,128,128);
-}
-
-.test-paper-detail .back-to-top .ivu-icon {
-    font-size: 26px;
-    color: white;
-}
-
-.overview-box{
-    width: 100%;
-    // min-height: 400px;
-    padding: 10px 20px 10px 10px;
-    display: flex;
-    justify-content: space-between;
-    background: #404040;
-    // margin-top: 5px;
-    .count-box{
-        width: 120px;
-        height: fit-content;
-        text-align: center;
-        background: #404040;
-        padding: 10px 0px;
-        .count-icon{
-            color: white;
-            font-size: 18px;
-            margin-right: 5px;
-            vertical-align: sub;
-            // display: block;
-        }
-        .count-subject-text{
-            display: block;
-            color: white;
-            font-size: 12px;
-        }
-        .count-subject-num{
-            display: block;
-            color: white;
-            font-size: 35px;
-            font-weight: 800;
-        }
-    }
-    
-}
-.mock-stu-answer{
-    margin-right: 20px;
-}
-.mock-tea-scoring{
-    margin-right: 40px;
-}
-.ev-tag-common{
-    border-radius: 2px;
-    padding: 1px 2px;
-    white-space: nowrap;
-    font-size:12px;
-    margin-right: 4px;
-    margin-bottom: 5px;
-}
-.ev-info-tag{
-    color: #2d8cf0;
-    border: 1px solid #2d8cf0;
-}
-.evaluation-status-tag {
-    border: 1px solid #1CC0F3;
-    white-space: nowrap;
-}
-.handle-end-tag{
-    background:#ed4014;
-    color: white;
-}
-.tags-wrap{
-    display: flex;
-    flex-wrap: wrap;
-    margin-top: 5px;
-}
-.evaluation-item:hover{
-    .edit-end-time{
-        display: inline-block;
-    }
-}
-.edit-end-time{
-    display: none;
-    font-size: 14px;
-    color: #2d8cf0;
-}
-.toggle-school-box{
-    width: fit-content;
-    margin: auto;
-    text-align: center;
-    margin-top: 150px;
-}
-.unauth-img {
-    width: 100px;
-    margin-bottom: 32px;
-}
-.toggle-school-text{
-    // margin-top: 10px;
-    color: #515a6e;
-    font-size: 16px;
-}
-.toggle-school-btn{
-    margin: auto;
-    width: fit-content;
-    background: #2b85e4;
-    color: white;
-    padding: 8px 40px;
-    border-radius: 30px;
-    font-size: 16px;
-    margin-top: 16px;
-    cursor: pointer;
-    user-select: none;
-}
-.ev-qr-tag{
-    text-align: center;
-    padding: 1px 2px 1px 1px;
-    color: #2d8cf0;
-    position: absolute;
-    right: 15px;
-    top: 50%;
-    margin-top: -10px;
-}
-.qr-code-wrap {
-    position: fixed;
-    left: 0px;
-    right: 0px;
-    top: 0px;
-    bottom: 0px;
-    background: rgba(103, 103, 103, 0.27);
-    z-index: 99999;
-    display:flex;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-}
-.qr-code-info{
-    width: fit-content;
-    height: fit-content;
-    background: white;
-    padding: 30px 40px;;
-    margin-top: -150px;
-}
-.qr-code-title{
-    display: block;
-    margin: auto;
-    margin-bottom: 10px;
-    text-align: center;
-    color: black;
-    font-size: 18px;
-}
-.invite-url-text{
-    margin-top: 10px;
-    text-overflow: ellipsis;
-    overflow: hidden;
-    white-space: nowrap;
-    // width: 300px;
-    cursor: pointer;
-    color: #2d8cf0;
-    user-select: none;
-    text-align: center;
-}
-.filter-item{
-    margin: 10px 5px;
-}

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

@@ -1,871 +0,0 @@
-<template>
-    <div class="manage-evaluation-container custom-iview-split">
-        <Loading :top="200" type="1" style="text-align:center" v-show="isLoading"></Loading>
-        <Split v-model="split1">
-            <!--评测列表-->
-            <div class="evaluation-list-wrap" slot="left">
-                <div class="evaluation-list-title">
-                    <span>{{$t('learnActivity.mgtScEv.listLabel')}}</span>
-                    <template v-if="!isSearch">
-                        <Icon type="md-add" class=" to-create-icon" @click="goToCreate" :title="$t('learnActivity.mgtScEv.create')" />
-                        <Icon type="md-copy" class=" to-create-icon" @click="copyEv" :title="$t('learnActivity.mgtScEv.copy')" />
-                        <Icon type="md-trash" v-show="evaListShow.length" class="to-create-icon" :title="$t('learnActivity.mgtScEv.delete')" @click="deleteEvaluation" />
-                        <!-- 筛选 -->
-                        <Poptip style="float:right" trigger="click" :offset="-10" theme="light">
-                            <Icon type="ios-funnel" class="to-create-icon" />
-                            <div slot="content">
-                                <div class="filter-item">
-                                    <span>{{$t('learnActivity.mgtScEv.ftStatus')}}</span>
-                                    <Select v-model="filter.status" style="width:100px" size="small" clearable @on-change="filterEv">
-                                        <Option value="pending">{{$t('learnActivity.mgtScEv.pending')}}</Option>
-                                        <Option value="going">{{$t('learnActivity.mgtScEv.going')}}</Option>
-                                        <Option value="finish">{{$t('learnActivity.mgtScEv.finish')}}</Option>
-                                    </Select>
-                                </div>
-                                <div class="filter-item">
-                                    <span>{{$t('learnActivity.mgtScEv.ftMode')}}</span>
-                                    <Select v-model="filter.mode" style="width:100px" size="small" clearable @on-change="filterEv">
-                                        <Option v-for="(item,index) in $GLOBAL.EV_MODE()" :value="item.value" :key="index">{{ item.label }}</Option>
-                                    </Select>
-                                </div>
-                                <div class="filter-item">
-                                    <span>{{$t('learnActivity.mgtScEv.ftType')}}</span>
-                                    <Select v-model="filter.type" style="width:100px" size="small" clearable @on-change="filterEv">
-                                        <Option v-for="(item,index) in $GLOBAL.EV_TYPE()" :value="item.value" :key="index">{{ item.label }}</Option>
-                                    </Select>
-                                </div>
-                            </div>
-                        </Poptip>
-                        <Icon type="md-search" class="to-create-icon" @click="isSearch = !isSearch" :title="$t('learnActivity.mgtScEv.search')" />
-                    </template>
-                    <div v-else style="float:right;width:calc(100% - 150px);padding-right:10px;" class="light-iview-input">
-                        <Input v-special-char icon="ios-close" v-model="keyword" :placeholder="$t('schoolBaseInfo.codeSearchHolder')" autofocus style="width:100%" @on-click="closeKeySearch" @on-change="filterByName" />
-                    </div>
-                </div>
-                <div class="evaluation-list-main">
-                    <vuescroll>
-                        <div v-for="(item,index) in evaListShow" @click.capture="selectEvaluation(index)" :class="['evaluation-item','block-bg',index == curEvaIndex ? 'block-bg-active':'']" :key="index">
-                            <p class="evaluation-name">
-                                {{item.name}}
-                                <!-- 修改评测名称 -->
-                                <Icon type="md-create" class="edit-end-time" @click="editEvName(index)" :title="$t('learnActivity.mgtScEv.edName')" />
-                            </p>
-                            <p class="evaluation-type">
-                                <Icon type="md-time" style="margin-right:5px;" size="16" />
-                                <span>{{$t('learnActivity.mgtScEv.createTime')}}{{dateFormat(item.startTime)}}</span>
-                            </p>
-                            <p class="evaluation-type">
-                                <Icon type="md-time" style="margin-right:5px;" size="16" />
-                                <span>{{$t('learnActivity.mgtScEv.endTime')}}{{dateFormat(item.endTime)}}</span>
-                                <!-- 修改评测结束时间 -->
-                                <Icon type="md-create" class="edit-end-time" v-if="item.progress == 'going'" @click="editEvEndtime(index)" :title="$t('learnActivity.mgtScEv.editEndTime')" />
-                            </p>
-                            <div class="tags-wrap">
-                                <span class="ev-info-tag ev-tag-common">{{getTypeLabel(item.type)}}</span>
-                                <!-- 评测模式 -->
-                                <span class="ev-info-tag ev-tag-common">{{getModeLabel(item.source)}}</span>
-                                <!-- 活动进度状态 -->
-                                <span class="evaluation-status-tag ev-tag-common" :style="{ borderColor: item.progColor, color: item.progColor}">
-                                    {{ item.progText }}
-                                </span>
-                                <!-- 活动评分状态 -->
-                                <span v-show="item.scoreText" class="evaluation-status-tag ev-tag-common" :style="{ borderColor: item.scoreColor, color: item.scoreColor}">
-                                    {{ item.scoreText }}
-                                </span>
-                                <!-- 立即结束 -->
-                                <span v-if="item.progress == 'going'" class="handle-end-tag ev-tag-common" @click="handleEnd(index)">
-                                    {{$t('learnActivity.mgtScEv.stop')}}
-                                </span>
-                            </div>
-                            <!-- 二维码分享 -->
-                            <span class="ev-qr-tag" @click="openQrcode(index)" v-show="item.source != '1'">
-                                <Icon size="25" custom="iconfont icon-qr-code" class="qr-code-icon" />
-                            </span>
-                        </div>
-                        <EmptyData v-if="evaListShow.length == 0" style="margin-top:180px;" :textContent="$t('learnActivity.mgtScEv.nodata')"></EmptyData>
-                    </vuescroll>
-                </div>
-            </div>
-            <!-- 评测正常显示数据 -->
-            <div slot="right" class="evaluation-detail-wrap" v-show="evaListShow[curEvaIndex] && evaListShow[curEvaIndex].curSchool">
-                <!--顶部菜单-->
-                <div class="evaluation-detail-bar tab-box">
-                    <span :class="curBarIndex == 0 ? 'evalustion-bar-item pane active':'evalustion-bar-item pane'" @click="selectBar(0)">
-                        {{$t('learnActivity.mgtScEv.tab1')}}
-                    </span>
-                    <span :class="curBarIndex == 1 ? 'evalustion-bar-item pane active':'evalustion-bar-item pane'" @click="selectBar(1)">
-                        {{$t('learnActivity.mgtScEv.tab2')}}
-                    </span>
-                </div>
-                <!--试卷信息-->
-                <div :class="curBarIndex == 1 ? 'animated fadeIn evaluation-base-info':'evaluation-base-info animated fadeOutRight'" v-show="curBarIndex == 1">
-                    <div class="test-paper-detail" style="margin-top:5px;">
-                        <vuescroll ref="test-paper-detail" @handle-scroll="checkBackTop">
-                            <!--试卷题目信息-->
-                            <TestPaper v-if="evaListShow[curEvaIndex] && evaListShow[curEvaIndex].papers && evaListShow[curEvaIndex].papers[curSubIndex] && evaListShow[curEvaIndex].papers[curSubIndex].item" :paper="evaListShow[curEvaIndex].papers[curSubIndex]" style="color:#515a6e;margin-top:-30px;" :isShowTools="false" isExamPaper canFix :examId="evaListShow[curEvaIndex].id"></TestPaper>
-                            <EmptyData v-else style="height:450px"></EmptyData>
-                            <!--返回顶部-->
-                            <!-- <div class="back-to-top fl-col-center" :title="$t('learnActivity.mgtScEv.returnTop')" v-if="showBack" @click="handleBackToTop">
-                                <Icon type="ios-arrow-up" />
-                            </div> -->
-                            <BackToTop v-if="showBack" @on-to-top="handleBackToTop"></BackToTop>
-                        </vuescroll>
-                    </div>
-                </div>
-                <!-- 试卷评测打分 -->
-                <div :class="curBarIndex == 0 ? 'animated fadeIn evaluation-base-info':'evaluation-base-info animated fadeOutRight'" v-show="curBarIndex == 0">
-                    <Scoring v-if="evaListShow.length" :examInfo="examDetaiInfo" ref="score-box"></Scoring>
-                    <EmptyData v-show="!evaListShow.length" style="height:450px"></EmptyData>
-                </div>
-            </div>
-            <!-- 跨校个人评测提示UI -->
-            <div slot="right" class="evaluation-detail-wrap" v-if="evaListShow[curEvaIndex] && !evaListShow[curEvaIndex].curSchool">
-                <div class="toggle-school-box">
-                    <img src="@/assets/image/unauth.svg" alt="" class="unauth-img">
-                    <p class="toggle-school-text">
-                        {{$t('learnActivity.mgtScEv.tst1')}}
-                        <strong style="color:#2d8cf0">{{getSchoolName(evaListShow[curEvaIndex].school)}}</strong>
-                        {{$t('learnActivity.mgtScEv.tst2')}}
-                    </p>
-                </div>
-            </div>
-        </Split>
-        <Modal v-model="editTimeStatus" footer-hide className="ed-name-modal" @on-ok="confirmEditEndtime">
-            <div slot="header" class="modal-header">
-                {{$t('learnActivity.mgtScEv.editEndTime')}}
-            </div>
-            <div class="edit-name-content">
-                <p class="edit-name-label">
-                    {{$t('cusMgt.listName')}}
-                </p>
-                <DatePicker v-model="editTime" :options="dateOpt1" type="datetime" @on-change="handleTime" format="yyyy-MM-dd HH:mm" :placeholder="$t('learnActivity.mgtScEv.endTimeHolder')" style="width: 100%"></DatePicker>
-                <Button :loading="btnLoading" @click="confirmEditEndtime" long type="primary" class="confirm-btn">{{ $t('syllabus.confirm') }}</Button>
-            </div>
-        </Modal>
-        <!-- 修改评测名称 -->
-        <Modal v-model="editNameStatus" footer-hide className="ed-name-modal" @on-ok="confirmEditName" :loading="modalLoading">
-            <div slot="header" class="modal-header">
-                {{$t('learnActivity.mgtScEv.edName')}}
-            </div>
-            <div class="edit-name-content">
-                <p class="edit-name-label">
-                    {{$t('cusMgt.listName')}}
-                </p>
-                <Input v-model="editName" :placeholder="$t('learnActivity.mgtScEv.edNameHolder')" />
-                <Button :loading="btnLoading" @click="confirmEditName" long type="primary" class="confirm-btn">{{ $t('syllabus.confirm') }}</Button>
-            </div>
-        </Modal>
-        <div class="qr-code-wrap" v-show="showQrStatus" @click="showQrStatus = false">
-            <div class="qr-code-info" @click.stop>
-                <p class="qr-code-title">
-                    {{$t('learnActivity.mgtScEv.shareText8')}}
-                </p>
-                <div id="qrcode" :class="showQrStatus ? 'animated fadeIn':'animated fadeOut'" ref="qrcode" style="padding:15px 25px 20px 25px;background-color:white;width:330px;margin:auto;">
-                </div>
-                <p class="invite-url-text" @click="copyUrl">
-                    <Icon type="md-copy" class="copy-link-icon" :title="$t('cusMgt.copyUrl')" />
-                    <span style="font-size:14px">{{$t('cusMgt.inviteUrl')}}</span>
-                </p>
-            </div>
-        </div>
-    </div>
-</template>
-<script>
-import QRCode from 'qrcodejs2'
-import TestPaper from '@/view/evaluation/index/TestPaper.vue'
-import Scoring from './Scoring.vue'
-export default {
-    components: {
-        TestPaper,
-        Scoring
-    },
-    inject: ['reload'],
-    data() {
-        return {
-            btnLoading:false,
-            shareUrl: '',
-            showQrStatus: false,
-            modalLoading: true,
-            editTime: null,
-            editTimeStatus: false,
-            editName: '',
-            editNameStatus: false,
-            curEvValue: 0,
-            split1: 0.2,
-            scope: '',//school 校本 private 个人
-            showBack: false,
-            curSubIndex: 0,
-            curBarIndex: 0,
-            curEvaIndex: 0,
-            evaluationList: [],
-            evaListShow: [],
-            examDetaiInfo: {},
-            targetList: [],
-            isLoading: false,
-            schoolBase: {
-                period: []
-            },
-            evFilter: [
-                {
-                    label: this.$t('learnActivity.mgtScEv.myEv'),
-                    value: 0
-                },
-                {
-                    label: this.$t('learnActivity.mgtScEv.teaEv'),
-                    value: 1
-                }
-            ],
-            scoreLoading: false,
-            answerLoading: false,
-            filter: {
-                status: '',
-                mode: '',
-                type: ''
-            },
-            isSearch: false,
-            keyword: ''
-        }
-    },
-    methods: {
-        filterByName() {
-            // let curPdEv = this.evaluationList.filter(item => item.period.id === this.filterPeriod)
-            this.evaListShow = this.evaluationList.filter(item => {
-                return item.name.indexOf(this.keyword) > -1
-            })
-        },
-        closeKeySearch() {
-            this.isSearch = false
-            this.keyword = ''
-            this.filterByName()
-        },
-        //筛选评测
-        filterEv() {
-            // let curPdEv = this.evaluationList.filter(item => item.period.id === this.filterPeriod)
-            this.evaListShow = this.evaluationList.filter(item => {
-                let status = !this.filter.status || (this.filter.status == item.progress)
-                let type = !this.filter.type || (this.filter.type == item.type)
-                let mode = !this.filter.mode || (this.filter.mode == item.source)
-                return status && type && mode
-            })
-        },
-        // 复制评测
-        copyEv() {
-            // 课中评测不提供复制操作
-            if (this.evaListShow[this.curEvaIndex]?.source === '1') {
-                this.$Modal.info({
-                    title: this.$t('learnActivity.mgtScEv.copyTitle'),
-                    content: this.$t('learnActivity.mgtScEv.copyContent1')
-                })
-            } else if (this.evaListShow[this.curEvaIndex]) {
-                this.$Modal.confirm({
-                    title: this.$t('learnActivity.mgtScEv.copyTitle'),
-                    content: this.$t('learnActivity.mgtScEv.copyContent'),
-                    onOk: () => {
-                        console.log(this.evaListShow[this.curEvaIndex])
-                        this.$router.push({
-                            name: 'createPrivEva',
-                            params: {
-                                evaluationInfo: this.evaListShow[this.curEvaIndex]
-                            }
-                        })
-                    }
-                })
-            }
-        },
-        async copyUrl() {
-            let evName = this.evaListShow[this.curEvaIndex].name
-            let evType = this.getModeLabel(this.evaListShow[this.curEvaIndex].source)
-            let soc = this.evaListShow[this.curEvaIndex].owner === 'school' ? this.$t('learnActivity.mgtScEv.shareText4') : this.$t('learnActivity.mgtScEv.shareText5')
-            let socName = this.evaListShow[this.curEvaIndex].subjects.map(item => item.name).join('、')
-            let shortUrl = await this.$api.getShortUrl(encodeURI(this.shareUrl)) //暂不替换,等路由处理好了再替换 
-            let shareText = `${this.$t('learnActivity.mgtScEv.shareText1')}\n\n${soc}${socName}\n${this.$t('learnActivity.mgtScEv.shareText3')}${evType}\n${this.$t('cusMgt.inviteInfo8')}${this.$store.state.userInfo.name}\n${this.$t('learnActivity.mgtScEv.shareText2')}${evName}\n\n${this.$t('learnActivity.mgtScEv.shareText6')}\n${shortUrl.result || encodeURI(this.shareUrl)}\n\n${this.$t('learnActivity.mgtScEv.shareText7')}\nURL:https://${window.location.host}/login/student`
-            this.$copyText(shareText).then(
-                ok => {
-                    this.$Message.success(this.$t("settings.copyModal1"))
-                },
-                fail => {
-                    this.$Message.error(this.$t("settings.copyModal2"))
-                }
-            )
-        },
-        openQrcode() {
-            this.$nextTick(async () => {
-                this.shareUrl = `https://${window.location.host}/studentWeb/eventView?aId=${this.evaListShow[this.curEvaIndex].id}`
-                let shortUrl = await this.$api.getShortUrl(encodeURI(this.shareUrl)) //暂不替换,等路由处理好了再替换
-                // 此时已经渲染完成
-                if (this.shareQRcode == undefined) {
-                    let qrcode = new QRCode('qrcode', {
-                        width: 280, // 设置宽度,单位像素
-                        height: 280, // 设置高度,单位像素
-                        // text: shortUrl.result || encodeURI(this.shareUrl), // 编码处理
-                        text: encodeURI(this.shareUrl), // 编码处理
-                        correctLevel: QRCode.CorrectLevel.Q //解决编码后网址太长的问题
-                    })
-                    this.shareQRcode = qrcode
-                } else {
-                    this.shareQRcode.clear()
-                    // this.shareQRcode.makeCode(shortUrl.result || encodeURI(this.shareUrl))
-                    this.shareQRcode.makeCode(encodeURI(this.shareUrl))
-                }
-                let dom = document.getElementById('qrcode')
-                if (dom) dom.title = ''
-            })
-            this.showQrStatus = true
-        },
-        getSchoolName(school) {
-            let schools = this.$store.state?.user?.userProfile?.schools
-            if (schools) {
-                let s = schools.find(item => item.schoolId === school)
-                return s ? s.name : school
-            }
-            return school
-        },
-        editEvName(index) {
-            this.editNameStatus = true
-            this.editName = this.evaListShow[index]?.name
-        },
-        confirmEditName() {
-            if (this.editName) {
-                this.evaListShow[this.curEvaIndex].code = this.evaListShow[this.curEvaIndex].code.replace('Exam-', '')
-                this.btnLoading = true
-                this.$api.learnActivity.updExamEndtime({
-                    id: this.evaListShow[this.curEvaIndex].id,
-                    code: this.evaListShow[this.curEvaIndex].code,
-                    name: this.editName
-                }).then(
-                    res => {
-                        if (res.code == 200) {
-                            this.evaListShow[this.curEvaIndex].name = this.editName
-                            this.$Message.success(this.$t('learnActivity.mgtScEv.updOk'))
-                        } else {
-                            this.$Message.error(this.$t('learnActivity.mgtScEv.updErr'))
-                        }
-                    },
-                    err => {
-                        this.$Message.error(this.$t('learnActivity.mgtScEv.updErr'))
-                    }
-                ).finally(() => {
-                    this.editNameStatus = false
-                    this.btnLoading = false
-                })
-            } else {
-                this.$Message.warning(this.$t('learnActivity.mgtScEv.edNameHolder'))
-                this.btnLoading = false
-            }
-        },
-        handleTime(value) {
-            if (value.indexOf('00:00') > 0) {
-                value = value.replace('00:00', '23:59:59')
-                this.editTime = value
-            }
-        },
-        confirmEditEndtime() {
-            if (this.editTime) {
-                this.evaListShow[this.curEvaIndex].code = this.evaListShow[this.curEvaIndex].code.replace('Exam-', '')
-                this.btnLoading = true
-                this.$api.learnActivity.updExamEndtime({
-                    id: this.evaListShow[this.curEvaIndex].id,
-                    code: this.evaListShow[this.curEvaIndex].code,
-                    time: this.editTime.getTime()
-                }).then(
-                    res => {
-                        if (res.code == 200) {
-                            this.evaListShow[this.curEvaIndex].endTime = this.editTime.getTime()
-                            this.$Message.success(this.$t('learnActivity.mgtScEv.updOk'))
-                        } else {
-                            this.$Message.error(this.$t('learnActivity.mgtScEv.updErr'))
-                        }
-                    },
-                    err => {
-                        this.$Message.error(this.$t('learnActivity.mgtScEv.updErr'))
-                    }
-                ).finally(() => {
-                    this.editTimeStatus = false
-                    this.btnLoading = false
-                })
-            } else {
-                this.$Message.warning(this.$t('learnActivity.mgtScEv.endTimeHolder'))
-            }
-        },
-        editEvEndtime(index) {
-            this.editTimeStatus = true
-            this.editTime = new Date(this.evaListShow[index]?.endTime)
-        },
-        /**获取mode对应的label */
-        getModeLabel(code) {
-            for (let item of this.$GLOBAL.EV_MODE()) {
-                if (item.value == code) {
-                    return item.label
-                }
-            }
-        },
-        dropdownStates(flag) {
-            if (!flag) this.filterByTag()
-        },
-        filterByTag() {
-            this.curEvaIndex = 0
-        },
-        // 模拟教师评分数据
-        mockScoring() {
-            this.scoreLoading = true
-            this.$api.learnActivity.mockScoring({
-                id: this.evaListShow[this.curEvaIndex].id,
-                code: this.evaListShow[this.curEvaIndex].code.replace('Exam-', '')
-            }).then(
-                res => {
-                    setTimeout(() => {
-                        this.$Message.success(this.$t('learnActivity.mgtScEv.mockOk'))
-                    }, 500)
-                },
-                err => {
-                    this.$Message.error(this.$t('learnActivity.mgtScEv.mockErr'))
-                }
-            ).finally(() => {
-                setTimeout(() => {
-                    this.scoreLoading = false
-                    this.reload()
-                }, 500)
-            })
-        },
-        // 模拟学生作答数据
-        mockAnswer() {
-            this.answerLoading = true
-            this.$api.learnActivity.mockAnswer({
-                id: this.evaListShow[this.curEvaIndex].id,
-                code: this.evaListShow[this.curEvaIndex].code.replace('Exam-', '')
-            }).then(
-                res => {
-                    setTimeout(() => {
-                        this.$Message.success(this.$t('learnActivity.mgtScEv.mockOk'))
-                    }, 500)
-                },
-                err => {
-                    this.$Message.error(this.$t('learnActivity.mgtScEv.mockErr'))
-                }
-            ).finally(() => {
-                setTimeout(() => {
-                    this.answerLoading = false
-                    this.reload()
-                }, 500)
-            })
-        },
-        handleEnd(index) {
-            this.$Modal.confirm({
-                title: this.$t('learnActivity.mgtScEv.stopTitle'),
-                content: `${this.$t('learnActivity.mgtScEv.stopContent')}${this.evaListShow[index].name}?`,
-                onOk: () => {
-                    let code = this.evaListShow[index].code
-                    this.$api.learnActivity.FinishEva({
-                        id: this.evaListShow[index].id,
-                        // code: this.evaListShow[index].code.replace('Exam-', '')
-                        code: code.includes('Exam') ? code : `Exam-${code}`
-                    }).then(
-                        res => {
-                            if (!res.error) {
-                                this.$Message.success(this.$t('learnActivity.mgtScEv.stopOk'))
-                                this.evaListShow[index].progress = 'finish'
-                            } else {
-                                // this.$Message.error('API ERROR!')
-                                this.$Message.error(this.$t('learnActivity.mgtScEv.actionErr'))
-                            }
-                        },
-                        err => {
-                            // this.$Message.error('API ERROR!')
-                        }
-                    )
-                }
-            })
-        },
-        /**获取type对应的label */
-        getTypeLabel(code) {
-            for (let item of this.$GLOBAL.EV_TYPE()) {
-                if (item.value == code) {
-                    return item.label
-                }
-            }
-        },
-        /**
-         * 查找班级课程下的班级
-         * */
-        findClassroom() {
-            if (this.targetList.length == 0) {
-                let requestData = this.$store.state.userInfo.TEAMModelId
-                this.$api.learnActivity.FindClassroomByTeacherId({
-                    code: requestData
-                }).then(
-                    res => {
-                        if (!res.error) {
-                            this.targetList = res.result.data
-                        } else {
-                            // this.$Message.error('API ERROR!')
-                            this.$Message.error(this.$t('learnActivity.mgtScEv.classInfoErr'))
-                        }
-                    },
-                    err => {
-
-                    }
-                )
-            }
-        },
-        /**
-         * 判断是否显示回到顶部按钮
-         * @param vertical
-         * @param horizontal
-         * @param nativeEvent
-         */
-        checkBackTop(vertical, horizontal, nativeEvent) {
-            if (vertical.scrollTop > 100) {
-                this.showBack = true
-            } else {
-                this.showBack = false
-            }
-        },
-        /**vuescroll回到顶部 */
-        handleBackToTop() {
-            this.$refs['test-paper-detail'].scrollTo(
-                { y: '0' }, 300
-            )
-        },
-        /**删除评测信息 */
-        deleteEvaluation() {
-            this.$Modal.confirm({
-                title: this.$t('learnActivity.mgtScEv.deleteTitle'),
-                content: `${this.$t('learnActivity.mgtScEv.deleteContent')}${this.evaListShow[this.curEvaIndex].name}?`,
-                onOk: () => {
-                    let params = {
-                        id: this.evaListShow[this.curEvaIndex].id,
-                        code: this.evaListShow[this.curEvaIndex].code.replace('Exam-', ''),
-                        scope: this.evaListShow[this.curEvaIndex].scope
-                    }
-                    this.isLoading = true
-                    this.$api.learnActivity.DeleteExamInfo(params).then(
-                        res => {
-                            if (res.error == null) {
-                                let index = this.curEvaIndex
-                                this.selectEvaluation(0)
-                                this.evaListShow.splice(index, 1)
-                                this.$Message.success(this.$t('learnActivity.mgtScEv.deleteOk'))
-                            } else {
-                                this.$Message.error(this.$t('learnActivity.mgtScEv.deleteErr'))
-                            }
-                        },
-                        err => {
-                            this.$Message.error(this.$t('learnActivity.mgtScEv.deleteErr'))
-                        }
-                    ).finally(() => {
-                        setTimeout(() => {
-                            this.isLoading = false
-                        }, 500)
-                    })
-                }
-            })
-        },
-        goToCreate() {
-            // if (this.$store.state.user.schoolProfile.school_base) { //这里判断必须要加入学校才能创建评测
-            if (this.scope == 'school') {
-                this.$router.push({
-                    name: 'createSchoolEva'
-                })
-            } else if (this.scope == 'private') {
-                this.$router.push({
-                    name: 'createPrivEva'
-                })
-            }
-            // } else {
-            //     this.$Message.warning(this.$t('learnActivity.mgtScEv.noJoin'))
-            // }
-        },
-        editEvaluation() {
-            this.$router.push({
-                name: 'createPrivEva',
-                params: {
-                    evaluationInfo: this.examDetaiInfo
-                }
-            })
-        },
-        selectSubject(index) {
-            this.curSubIndex = index
-        },
-        dateFormat(timestamp) {
-            let date = new Date(timestamp)
-            let Y = date.getFullYear()
-            let M = date.getMonth()
-            let D = date.getDate()
-            let H = date.getHours()
-            let MIN = date.getMinutes()
-            return `${Y}-${M < 9 ? '0' + (M + 1) : M + 1}-${D} ${H < 10 ? '0' + H : H}:${MIN < 10 ? '00' : MIN}`
-        },
-        getEvStatusInfo(progress, isScore) {
-            let info = {}
-            if (progress == 'pending') {
-                info.progText = this.$t('learnActivity.mgtScEv.pending')
-                info.progColor = '#2d8cf0'
-            } else if (progress == 'going') {
-                info.progText = this.$t('learnActivity.mgtScEv.going')
-                info.progColor = '#19be6b'
-            } else if (progress == 'finish') {
-                info.progText = this.$t('learnActivity.mgtScEv.finish')
-                info.progColor = '#515a6e'
-            }
-            if (isScore === 0) {
-                info.scoreText = this.$t('learnActivity.mgtScEv.scoreStatus')
-                info.scoreColor = '#ff9900'
-            } else if (isScore === 1) {
-                info.scoreText = this.$t('learnActivity.mgtScEv.scoreStatus1')
-                info.scoreColor = '#515a6e'
-            }
-            return info
-        },
-        //查询评测列表
-        findEvaluation() {
-            let requestData = {
-                code: this.scope == 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId
-            }
-            this.$api.learnActivity.FindExamList(requestData).then(
-                res => {
-                    if (!res.error) {
-                        res.examInfo = res.examInfo.sort((a, b) => {
-                            return a.createTime - b.createTime > 0 ? -1 : 1
-                        })
-                        res.examInfo.forEach(item => {
-                            let statusInfo = this.getEvStatusInfo(item.progress, item.sStatus)
-                            item.progText = statusInfo.progText
-                            item.progColor = statusInfo.progColor
-                            item.scoreText = statusInfo.scoreText
-                            item.scoreColor = statusInfo.scoreColor
-                            item.curSchool = !(item.scope === 'school' && item.school != this.$store.state.userInfo.schoolCode)
-                        })
-                        console.error(res.examInfo)
-                        this.evaluationList = res.examInfo
-                        this.evaListShow = res.examInfo
-                        if (this.evaListShow.length) this.selectEvaluation(0)
-
-                    } else {
-                        // this.$Message.error('API ERROR!')
-                        this.$Message.error(this.$t('learnActivity.mgtScEv.evListErr'))
-                    }
-                }
-            )
-        },
-        checkScoreSave(fn, index) {
-            let compStu = this.$refs['score-box'] ? this.$refs['score-box'].$refs['byStuMark'] : null
-            let compQu = this.$refs['score-box'] ? this.$refs['score-box'].$refs['byQuMark'] : null
-            if ((compStu && compStu.isUpd) || (compQu && compQu.isUpd)) {
-                this.$Modal.confirm({
-                    title: this.$t('learnActivity.score.saveScoreTitle'),
-                    content: this.$t('learnActivity.score.saveScoreContent'),
-                    okText: this.$t('learnActivity.mark.yes'),
-                    cancelText: this.$t('learnActivity.mark.no'),
-                    onOk: () => {
-                        if (compStu && compStu.isUpd) {
-                            compStu.saveScore()
-                        } else if (compQu && compQu.isUpd) {
-                            compQu.saveScore()
-                        }
-                        if (fn) fn(index)
-                    },
-                    onCancel: () => {
-                        if (compStu && compStu.isUpd) {
-                            compStu.isUpd = false
-                        } else if (compQu && compQu.isUpd) {
-                            compStu.preSaveList = {}
-                        }
-                        if (fn) fn(index)
-                    }
-                })
-            } else {
-                if (fn) fn(index)
-            }
-        },
-        selectEvaluation(index) {
-            this.checkScoreSave(this.toEvaluation, index)
-        },
-        selectBar(index) {
-            this.checkScoreSave(this.handleSelectBar, index)
-        },
-        handleSelectBar(index) {
-            this.curBarIndex = index
-            this.$nextTick(() => {
-                if (this.$refs['score-box']) this.$refs['score-box'].showTest = false
-            })
-        },
-        toEvaluation(index) {
-            this.curSubIndex = 0
-            this.curEvaIndex = index
-            sessionStorage.setItem('examScope', this.evaListShow[index].scope)
-            this.$nextTick(() => {
-                this.$refs['score-box'].showTest = false
-            })
-            if (!this.evaListShow[this.curEvaIndex].curSchool) return
-            if (this.evaListShow[this.curEvaIndex] && this.evaListShow[this.curEvaIndex].papers.length == 0) {
-                this.findExamPaper()
-            } else {
-                this.examDetaiInfo = this.evaListShow[this.curEvaIndex]
-            }
-        },
-        //查询当前评测的试卷信息
-        findExamPaper() {
-            let requestData = {
-                id: this.evaListShow[this.curEvaIndex].id,
-                code: this.evaListShow[this.curEvaIndex].code
-            }
-            this.isLoading = true
-            this.$api.learnActivity.FindExamInfo(requestData).then(
-                async res => {
-                    if (!res.error) {
-                        let resData = res.examInfo[0]
-                        resData.score = 0
-                        for (let index in resData.papers) {
-                            resData.score += resData.papers[index].point.reduce((total, item) => {
-                                return total + parseInt(item)
-                            }, 0)
-                            if (resData.papers[index].blob) {
-                                let blob = resData.papers[index].blob
-                                let sheetNo = resData.papers[index].sheetNo
-                                resData.papers[index].examScope = this.evaListShow[this.curEvaIndex].scope
-                                resData.papers[index].examCode = this.evaListShow[this.curEvaIndex].code
-                                resData.papers[index].examId = this.evaListShow[this.curEvaIndex].id
-                                resData.papers[index].owner = this.evaListShow[this.curEvaIndex].owner
-                                resData.papers[index].sheetNo = sheetNo
-                                resData.papers[index] = await this.$evTools.getFullPaper(resData.papers[index], this.evaListShow[this.curEvaIndex].scope)
-                                resData.papers[index].blob = blob
-                                resData.papers[index].examScope = this.evaListShow[this.curEvaIndex].scope
-                                resData.papers[index].examCode = this.evaListShow[this.curEvaIndex].code
-                                resData.papers[index].examId = this.evaListShow[this.curEvaIndex].id
-                                resData.papers[index].owner = this.evaListShow[this.curEvaIndex].owner
-                                resData.papers[index].sheetNo = sheetNo
-                                //补充评测状态信息
-                                let statusInfo = this.getEvStatusInfo(resData.progress, resData.sStatus)
-                                resData.progText = statusInfo.progText
-                                resData.progColor = statusInfo.progColor
-                                resData.scoreText = statusInfo.scoreText
-                                resData.scoreColor = statusInfo.scoreColor
-                                resData.curSchool = !(resData.scope === 'school' && resData.school != this.$store.state.userInfo.schoolCode)
-                            }
-                        }
-                        this.evaListShow.splice(this.curEvaIndex, 1, resData)
-                        this.examDetaiInfo = resData
-                    } else {
-                        // this.$Message.error('API ERROR!')
-                        this.$Message.error(this.$t('learnActivity.mgtScEv.exInfoErr'))
-                    }
-                },
-                err => {
-                    // this.$Message.error('API ERROR!')
-                }
-            ).finally(() => {
-                this.isLoading = false;
-            })
-        },
-        beforeunloadHandler(e) {
-            let compStu = this.$refs['score-box'] ? this.$refs['score-box'].$refs['byStuMark'] : null
-            let compQu = this.$refs['score-box'] ? this.$refs['score-box'].$refs['byQuMark'] : null
-            if ((compStu && compStu.isUpd) || (compQu && compQu.isUpd)) {
-                e = e || window.event
-                if (e) {
-                    e.returnValue = '关闭提示'
-                }
-                return '关闭提示'
-            }
-        },
-    },
-    mounted() {
-        // 监听浏览器刷新事件
-        window.addEventListener('beforeunload', e => this.beforeunloadHandler(e))
-        // 处理浏览器返回事件
-        // if (window.history && window.history.pushState) {
-        //     // 向历史记录中插入了当前页
-        //     history.pushState(null, null, document.URL);
-        //     window.addEventListener('popstate', this.goBack, false);
-        // }
-    },
-    destroyed() {
-        window.removeEventListener('beforeunload', e => this.beforeunloadHandler(e))
-        window.removeEventListener('popstate', this.goBack, false);
-    },
-    created() {
-        if (this.$route.name == 'privateEvaluation') {
-            this.scope = 'private'
-        } else if (this.$route.name == 'schoolEvaluation') {
-            this.scope = 'school'
-        }
-        this.$store.dispatch('user/getSchoolProfile').then(
-            (res) => {
-                if (res) {
-                    this.schoolBase = res.school_base
-                }
-            }
-        ).finally(() => {
-            this.findEvaluation()
-        })
-    },
-    watch: {
-        $route: {
-            handler: function (to, from) {
-                if (this.$route.name == 'privateEvaluation') {
-                    this.scope = 'private'
-                } else if (this.$route.name == 'schoolEvaluation') {
-                    this.scope = 'school'
-                }
-            },
-            immediate: true
-        }
-    },
-    computed: {
-        curEvLabel: function () {
-            let value = this.curEvValue
-            console.log(this.curEvValue)
-            let curObj = this.evFilter.find(item => {
-                return item.value == value
-            })
-            return curObj.label
-        },
-        dateOpt1() {
-            let _this = this
-            return {
-                disabledDate(date) {
-                    let d = _this.evaListShow[_this.curEvaIndex]?.startTime ? _this.evaListShow[_this.curEvaIndex].startTime : Date.now()
-                    return d && d > date.valueOf() + 86400000
-                }
-            }
-        },
-    },
-    beforeRouteLeave(to, from, next) {
-        this.checkScoreSave(next)
-    }
-}
-</script>
-<style lang="less" scoped>
-@import "./MgtPrivEva.less";
-</style>
-<style lang="less">
-.evalustion-base-attr .ivu-form .ivu-form-item-label {
-    color: #a5a5a5;
-}
-.evaluation-attr-form .ivu-select-disabled .ivu-select-selection {
-    background: none;
-}
-.sort-dropdown {
-    .title {
-        // color: white;
-        font-size: 14px;
-    }
-    .ivu-select-dropdown {
-        // background-color: #2d2d2d;
-        border-radius: 2px;
-        // border: 1px #464646 solid;
-        .ivu-dropdown-menu {
-            li {
-                // color: #ccc;
-                font-size: 12px !important;
-                &:hover {
-                    color: #2d2d2d;
-                }
-            }
-        }
-    }
-}
-// .test-paper-detail .paper-container{
-//     padding-bottom: 30px;
-// }
-</style>

+ 0 - 313
TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.less

@@ -1,313 +0,0 @@
-@main-bgColor: rgb(40,40,40); //主背景颜色
-@borderColor: var(--border-color);
-@primary-textColor: #fff; //文本主颜色
-@second-textColor: var(--second-text-color); //文本副级颜色
-@primary-fontSize: 14px;
-@second-fontSize: 16px;
-@borderColor: #424242; 
-
-.manage-evaluation-container {
-    width: 100%;
-    height: 100%;
-    display: flex;
-    flex-direction: row;
-
-    .evaluation-list-wrap {
-        width:100%;
-        height: 100%;
-        padding-left: 0px;
-        color: var(--second-text-color);
-    }
-
-    .evaluation-detail-wrap {
-        width:100%;
-        height: 100%;
-        // padding-left: 15px;
-    }
-}
-
-.test-paper-analysis {
-    color: white;
-    cursor: pointer;
-    float: right;
-    margin-right: 45px;
-
-    &:hover {
-        color: aqua;
-    }
-}
-
-.evaluation-list-wrap {
-    .evaluation-list-title {
-        width: 100%;
-        height: 45px;
-        line-height: 45px;
-        padding-left: 15px;
-        // border-bottom: 1px solid var(--border-color);
-        // color: @second-textColor;
-    }
-
-    .evaluation-list-main {
-        width: 100%;
-        height: ~"calc(100% - 45px)";
-
-        .evaluation-item {
-            // border-bottom: 1px solid var(--border-color);
-            padding: 15px 10px 15px 15px;
-            cursor: pointer;
-
-            &:hover{
-                background: var(--hover-text-color);
-            }
-
-            .evaluation-name {
-                font-size: 16px;
-                color: var(--primary-text-color);
-            }
-
-            .evaluation-type {
-                color: @second-textColor;
-                margin-top: 4px;
-                font-size: 12px;
-
-                .ivu-icon{
-                    color: var(--assist-color-light);
-                }
-            }
-
-            .evaluation-type span {
-                margin-right: 10px;
-            }
-        }
-    }
-}
-
-.evaluation-detail-wrap {
-
-    .evaluation-detail-bar {
-        width: 100%;
-        height: 45px;
-        line-height: 45px;
-        box-shadow: 0 2px 5px #e9e9e9;
-        margin-bottom: 5px;
-        padding-left: 15px;
-        // border-bottom: 1px solid var(--border-color);
-        color: @second-textColor;
-
-        .edit-evaluation {
-            float: right;
-            margin-right: 45px;
-            display: inline-block;
-            cursor: pointer;
-            color: white;
-        }
-
-        .edit-evaluation:hover {
-            color: aqua;
-        }
-
-        .evalustion-bar-item {
-            display: inline-block;
-            cursor: pointer;
-            line-height: 38px;
-        }
-
-        .evalustion-bar-item-active {
-            color: white;
-            border-bottom: 2px solid white;
-        }
-    }
-
-    .evaluation-base-info {
-        width: 100%;
-        height: ~"calc(100% - 45px)";
-        padding-left: 15px;
-
-        .evalustion-base-attr {
-            width: 350px;
-            height: 100%;
-            border-right: 1px solid var(--border-color);
-
-            .evalustion-base-attr-header {
-                height: 45px;
-                line-height: 45px;
-                color: @second-textColor;
-                border-bottom: 1px solid var(--border-color);
-                margin-bottom: 25px;
-            }
-
-            .evaluation-attr-form {
-                margin-right: 15px;
-                /*color: white;*/
-            }
-        }
-
-        .evaluation-test-paper-header {
-            height: 35px;
-            line-height: 35px;
-            color: @second-textColor;
-            border-bottom: 1px solid var(--border-color);
-            margin: 5px 0 5px 15px;
-            // padding-left: 15px;
-        }
-    }
-}
-
-.subject-item {
-    display: inline-block;
-    margin-right: 15px;
-    color: var(--second-text-color);
-    cursor: pointer;
-    line-height: 22px;
-    min-width: 45px;
-    letter-spacing: 1px;
-    font-size: 14px;
-    text-align: center;
-    margin-left: 8px;
-}
-
-.subject-item-active {
-    color: var(--primary-text-color);
-    border-bottom: 2px solid #70B1E7;
-}
-
-.test-paper-detail {
-    width: ~"calc(100% - 5px)";
-    height: ~"calc(100% - 45px)";
-    color: white;
-    overflow: hidden;
-    border-radius: 5px;
-    background: white;
-}
-
-.question-type-count {
-    color: white;
-    font-size: 16px;
-    display: inline-block;
-    margin-top: 15px;
-    margin-bottom: 5px;
-}
-.to-create-icon {
-    float: right;
-    margin-right: 20px;
-    margin-top: 12px;
-    cursor: pointer;
-    color: var(--normal-icon-color);
-    font-size: 18px;
-}
-
-.test-paper-detail .back-to-top {
-    position: fixed;
-    right: 50px;
-    bottom: 30px;
-    height: 48px;
-    width: 50px;
-    background: #595959;
-    z-index: 99999;
-    cursor: pointer;
-    display: flex;
-    flex-direction: column;
-    justify-content: center;
-    align-items: center;
-}
-
-.test-paper-detail .back-to-top:hover {
-    background: rgb(128,128,128);
-}
-
-.test-paper-detail .back-to-top .ivu-icon {
-    font-size: 26px;
-    color: white;
-}
-.mock-stu-answer{
-    margin-right: 20px;
-}
-.mock-tea-scoring{
-    margin-right: 40px;
-}
-.filter-item{
-    margin: 10px 5px;
-}
-.ev-tag-common{
-    border-radius: 2px;
-    padding: 1px 2px;
-    white-space: nowrap;
-    font-size:12px;
-    margin-right: 4px;
-    // margin-bottom: 5px;
-}
-.ev-info-tag{
-    color: #2d8cf0;
-    border: 1px solid #2d8cf0;
-}
-.evaluation-status-tag {
-    border: 1px solid #1CC0F3;
-    white-space: nowrap;
-}
-.handle-end-tag{
-    background:#ed4014;
-    color: white;
-}
-.tags-wrap{
-    display: flex;
-    flex-wrap: wrap;
-    margin-top: 5px;
-}
-.evaluation-item:hover{
-    .edit-end-time{
-        display: inline-block;
-    }
-}
-.edit-end-time{
-    display: none;
-    font-size: 14px;
-    color: #2d8cf0;
-}
-.ev-qr-tag{
-    text-align: center;
-    padding: 1px 2px 1px 1px;
-    color: #2d8cf0;
-    position: absolute;
-    right: 15px;
-    top: 50%;
-    margin-top: -10px;
-}
-.qr-code-wrap {
-    position: fixed;
-    left: 0px;
-    right: 0px;
-    top: 0px;
-    bottom: 0px;
-    background: rgba(103, 103, 103, 0.27);
-    z-index: 99999;
-    display:flex;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-}
-.qr-code-info{
-    width: fit-content;
-    height: fit-content;
-    background: white;
-    padding: 30px 40px;;
-    margin-top: -150px;
-}
-.qr-code-title{
-    display: block;
-    margin: auto;
-    margin-bottom: 10px;
-    text-align: center;
-    color: black;
-    font-size: 18px;
-}
-.invite-url-text{
-    margin-top: 10px;
-    text-overflow: ellipsis;
-    overflow: hidden;
-    white-space: nowrap;
-    // width: 300px;
-    cursor: pointer;
-    color: #2d8cf0;
-    user-select: none;
-    text-align: center;
-}

+ 0 - 900
TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.vue

@@ -1,900 +0,0 @@
-<template>
-    <div class="manage-evaluation-container custom-iview-split">
-        <Loading :top="200" type="1" style="text-align:center" v-show="isLoading"></Loading>
-        <Split v-model="split1">
-            <!--评测列表-->
-            <div class="evaluation-list-wrap" slot="left">
-                <div class="evaluation-list-title">
-                    <b class="title">{{ $t('system.menu.scEv') }}</b>
-                    <div style="float:right" v-if="!isSearch">
-                        <Icon type="md-add" class=" to-create-icon" @click="goToCreate" :title="$t('learnActivity.mgtScEv.create')" v-if="$access.can('admin.*|schoolAc-upd')" />
-                        <Icon type="md-copy" class=" to-create-icon" @click="copyEv" :title="$t('learnActivity.mgtScEv.copy')" />
-                        <Icon type="md-trash" v-show="evaListShow.length" class="to-create-icon" :title="$t('learnActivity.mgtScEv.delete')" @click="deleteEvaluation" v-if="$access.can('admin.*|schoolAc-upd')" />
-                        <Icon type="md-create" v-show="evaListShow.length && evaListShow[curEvaIndex] && evaListShow[curEvaIndex].progress == 'pending'" class="to-create-icon" @click="editEvaluation" :title="$t('learnActivity.mgtScEv.edit')" v-if="$access.can('admin.*|schoolAc-upd')" />
-                        <!-- 筛选 -->
-                        <Poptip style="float:right" trigger="click" :offset="-10" theme="light">
-                            <Icon type="ios-funnel" class="to-create-icon" />
-                            <div slot="content">
-                                <div class="filter-item">
-                                    <span>{{$t('learnActivity.mgtScEv.ftStatus')}}</span>
-                                    <Select v-model="filter.status" style="width:100px" size="small" clearable @on-change="filterEv">
-                                        <Option value="pending">{{$t('learnActivity.mgtScEv.pending')}}</Option>
-                                        <Option value="going">{{$t('learnActivity.mgtScEv.going')}}</Option>
-                                        <Option value="finish">{{$t('learnActivity.mgtScEv.finish')}}</Option>
-                                    </Select>
-                                </div>
-                                <div class="filter-item">
-                                    <span>{{$t('learnActivity.mgtScEv.ftMode')}}</span>
-                                    <Select v-model="filter.mode" style="width:100px" size="small" clearable @on-change="filterEv">
-                                        <Option v-for="(item,index) in $GLOBAL.EV_MODE()" :value="item.value" :key="index">{{ item.label }}</Option>
-                                    </Select>
-                                </div>
-                                <div class="filter-item">
-                                    <span>{{$t('learnActivity.mgtScEv.ftType')}}</span>
-                                    <Select v-model="filter.type" style="width:100px" size="small" clearable @on-change="filterEv">
-                                        <Option v-for="(item,index) in $GLOBAL.EV_TYPE()" :value="item.value" :key="index">{{ item.label }}</Option>
-                                    </Select>
-                                </div>
-                            </div>
-                        </Poptip>
-                        <Icon type="md-search" class="to-create-icon" @click="isSearch = !isSearch" :title="$t('learnActivity.mgtScEv.search')" />
-                    </div>
-                    <div v-else style="float:right;width:calc(100% - 150px);padding-right:10px;" class="light-iview-input">
-                        <Input v-special-char icon="ios-close" v-model="keyword" :placeholder="$t('schoolBaseInfo.codeSearchHolder')" autofocus style="width:100%" @on-click="closeKeySearch" @on-change="filterByName" />
-                    </div>
-                </div>
-                <div class="evaluation-list-main">
-                    <vuescroll>
-                        <div v-for="(item,index) in evaListShow" @click.capture="selectEvaluation(index)" :class="['evaluation-item','block-bg',index == curEvaIndex ? 'block-bg-active':'']" :key="index">
-                            <p class="evaluation-name">
-                                {{item.name}}
-                                <!-- 修改评测名称 -->
-                                <Icon type="md-create" class="edit-end-time" @click="editEvName(index)" :title="$t('learnActivity.mgtScEv.edName')" />
-                            </p>
-                            <p class="evaluation-type">
-                                <Icon type="md-time" style="margin-right:5px;" size="16" />
-                                <span>{{$t('learnActivity.mgtScEv.createTime')}}{{dateFormat(item.startTime)}}</span>
-                            </p>
-                            <p class="evaluation-type">
-                                <Icon type="md-time" style="margin-right:5px;" size="16" />
-                                <span>{{$t('learnActivity.mgtScEv.endTime')}}{{dateFormat(item.endTime)}}</span>
-                                <!-- 修改评测结束时间 -->
-                                <Icon type="md-create" class="edit-end-time" v-if="item.progress == 'going'" @click="editEvEndtime(index)" :title="$t('learnActivity.mgtScEv.editEndTime')" />
-                            </p>
-                            <div class="tags-wrap">
-                                <!-- 评测模式 -->
-                                <span class="ev-info-tag ev-tag-common">{{getModeLabel(item.source)}}</span>
-                                <span class="ev-info-tag ev-tag-common">{{getTypeLabel(item.type)}}</span>
-                                <!-- 活动进度状态 -->
-                                <span class="evaluation-status-tag ev-tag-common" :style="{ borderColor: item.progColor, color: item.progColor}">
-                                    {{ item.progText }}
-                                </span>
-                                <!-- 活动评分状态 -->
-                                <span class="evaluation-status-tag ev-tag-common" :style="{ borderColor: item.scoreColor, color: item.scoreColor}">
-                                    {{ item.scoreText }}
-                                </span>
-                                <!-- 立即结束 -->
-                                <span v-if="item.progress == 'going'" class="handle-end-tag ev-tag-common" @click="handleEnd(index)">
-                                    {{$t('learnActivity.mgtScEv.stop')}}
-                                </span>
-                            </div>
-                            <!-- 二维码分享 -->
-                            <span class="ev-qr-tag" @click="openQrcode(index)" v-show="item.source != '1'">
-                                <Icon size="25" custom="iconfont icon-qr-code" class="qr-code-icon" />
-                            </span>
-                        </div>
-                        <!-- <EmptyData v-if="evaListShow.length == 0" style="margin-top:100px;" :textContent="`${scope == 'school' ? curPdInfo.name || '' : ''}${$t('learnActivity.mgtScEv.nodata')}`"></EmptyData> -->
-                        <EmptyData v-if="evaListShow.length == 0" style="margin-top:100px;" :textContent="`${$t('learnActivity.mgtScEv.nodata')}`"></EmptyData>
-                    </vuescroll>
-                </div>
-            </div>
-            <div slot="right" class="evaluation-detail-wrap">
-                <!--顶部菜单-->
-                <div class="evaluation-detail-bar">
-                    <!-- 评测数据 -->
-                    <span :class="curBarIndex == 0 ? 'evalustion-bar-item line-bottom-active line-bottom':'evalustion-bar-item line-bottom'" @click="selectBar(0)">
-                        {{$t('learnActivity.mgtScEv.tab1')}}
-                    </span>
-                    <!-- 评测试卷 -->
-                    <span :class="curBarIndex == 1 ? 'evalustion-bar-item line-bottom-active line-bottom':'evalustion-bar-item line-bottom'" @click="selectBar(1)">
-                        {{$t('learnActivity.mgtScEv.tab2')}}
-                    </span>
-                    <!-- 阅卷设置-->
-                    <span :class="curBarIndex == 2 ? 'evalustion-bar-item line-bottom-active line-bottom':'evalustion-bar-item line-bottom'" @click="selectBar(2)">
-                        {{$t('learnActivity.mgtScEv.markSetting')}}
-                    </span>
-
-                    <!-- 启动扫描助手 -->
-                    <!-- <div style="float:right;" v-if="$access.ability('admin','mock-eva').validateAll" v-show="evaListShow[curEvaIndex] && evaListShow[curEvaIndex].source == '2'">
-                        <Button type="success" size="small" :loading="answerLoading" class="mock-stu-answer" @click="startUp">
-                            启动扫描助手
-                        </Button>
-                    </div> -->
-
-                </div>
-                <!--试卷信息-->
-                <div :class="curBarIndex == 1 ? 'animated fadeIn evaluation-base-info':'evaluation-base-info animated fadeOutRight'" v-show="curBarIndex == 1">
-                    <div class="evaluation-test-paper-header" v-if="evaListShow[curEvaIndex]">
-                        <span>{{$t('learnActivity.mgtScEv.evSubject')}}</span>
-                        <span v-for="(item,index) in evaListShow[curEvaIndex].subjects" :key="index" :class="index == curSubIndex ? 'subject-item subject-item-active':'subject-item'" @click="selectSubject(index)">
-                            {{item.name}}
-                        </span>
-                    </div>
-                    <div class="test-paper-detail">
-                        <vuescroll ref="test-paper-detail" @handle-scroll="checkBackTop">
-                            <!--试卷题目信息-->
-                            <TestPaper v-if="evaListShow[curEvaIndex] && evaListShow[curEvaIndex].papers && evaListShow[curEvaIndex].papers[curSubIndex] && evaListShow[curEvaIndex].papers[curSubIndex].item" :paper="evaListShow[curEvaIndex].papers[curSubIndex]" style="color:#515a6e;margin-top:-30px;" :isShowTools="false" isExamPaper :examId="evaListShow[curEvaIndex].id" canFix></TestPaper>
-                            <EmptyData v-else style="margin-top:60px;"></EmptyData>
-                            <!--返回顶部-->
-                            <!-- <div class="back-to-top fl-col-center" :title="$t('learnActivity.mgtScEv.returnTop')" v-if="showBack" @click="handleBackToTop">
-                                <Icon type="ios-arrow-up" />
-                            </div> -->
-                            <BackToTop v-if="showBack" @on-to-top="handleBackToTop"></BackToTop>
-                        </vuescroll>
-                    </div>
-                </div>
-                <!-- 试卷数据 -->
-                <div :class="curBarIndex == 0 ? 'animated fadeIn evaluation-base-info':'evaluation-base-info animated fadeOutRight'" v-show="curBarIndex == 0">
-                    <Scoring v-if="evaListShow.length" :examInfo="examDetaiInfo" :periodId="filterPeriod" ref="score-box"></Scoring>
-                    <EmptyData v-show="!evaListShow.length" :top="100"></EmptyData>
-                </div>
-                <!-- 阅卷助手 -->
-                <div :class="curBarIndex == 2 ? 'animated fadeIn evaluation-base-info':'evaluation-base-info animated fadeOutRight'" v-show="curBarIndex == 2">
-                    <MarkSetting ref="markSetting" v-if="evaListShow[curEvaIndex]" :evInfo="evaListShow[curEvaIndex]" v-model="isSetting"></MarkSetting>
-                </div>
-            </div>
-        </Split>
-        <!-- 修改评测时间 -->
-        <Modal v-model="editTimeStatus" footer-hide className="ed-name-modal">
-            <div slot="header" class="modal-header">
-                {{$t('learnActivity.mgtScEv.editEndTime')}}
-            </div>
-            <div class="edit-name-content">
-                <p class="edit-name-label">
-                    {{$t('cusMgt.listName')}}
-                </p>
-                <DatePicker v-model="editTime" :options="dateOpt1" type="datetime" @on-change="handleTime" format="yyyy-MM-dd HH:mm" :placeholder="$t('learnActivity.mgtScEv.endTimeHolder')" style="width: 100%"></DatePicker>
-                <Button :loading="btnLoading" @click="confirmEditEndtime" long type="primary" class="confirm-btn">{{ $t('syllabus.confirm') }}</Button>
-            </div>
-        </Modal>
-        <!-- 修改评测名称 -->
-        <Modal v-model="editNameStatus" footer-hide className="ed-name-modal" @on-ok="confirmEditName" :loading="modalLoading">
-            <div slot="header" class="modal-header">
-                {{$t('learnActivity.mgtScEv.edName')}}
-            </div>
-            <div class="edit-name-content">
-                <p class="edit-name-label">
-                    {{$t('cusMgt.listName')}}
-                </p>
-                <Input v-model="editName" :placeholder="$t('learnActivity.mgtScEv.edNameHolder')" />
-                <Button :loading="btnLoading" @click="confirmEditName" long type="primary" class="confirm-btn">{{ $t('syllabus.confirm') }}</Button>
-            </div>
-        </Modal>
-        <div class="qr-code-wrap" v-show="showQrStatus" @click="showQrStatus = false">
-            <div class="qr-code-info" @click.stop>
-                <p class="qr-code-title">
-                    {{$t('learnActivity.mgtScEv.shareText8')}}
-                </p>
-                <div id="qrcode" :class="showQrStatus ? 'animated fadeIn':'animated fadeOut'" ref="qrcode" style="padding:15px 25px 20px 25px;background-color:white;width:330px;margin:auto;">
-                </div>
-                <p class="invite-url-text" @click="copyUrl">
-                    <Icon type="md-copy" class="copy-link-icon" :title="$t('cusMgt.copyUrl')" />
-                    <span style="font-size:14px">{{$t('cusMgt.inviteUrl')}}</span>
-                </p>
-            </div>
-        </div>
-    </div>
-</template>
-<script>
-import QRCode from 'qrcodejs2'
-import TestPaper from '@/view/evaluation/index/TestPaper.vue'
-import Scoring from './Scoring.vue'
-// import MarkSetting from './markpaper/MarkSetting.vue'
-import MarkData from './markpaper/MarkData.vue'
-export default {
-    components: {
-        TestPaper, Scoring, MarkData
-    },
-    inject: ['reload'],
-    data() {
-        return {
-            btnLoading: false,
-            shareUrl: '',
-            showQrStatus: false,
-            modalLoading: true,
-            editTime: null,
-            editTimeStatus: false,
-            editName: '',
-            editNameStatus: false,
-            isSetting: false,
-            keyword: '',
-            isSearch: false,
-            answerLoading: false,
-            scoreLoading: false,
-            split1: 0.2,
-            scope: '',//school 校本 private 个人
-            showBack: false,
-            curSubIndex: 0,
-            curBarIndex: 0,
-            curEvaIndex: 0,
-            evaluationList: [],
-            evaListShow: [],
-            examDetaiInfo: {},
-            targetList: [],
-            isLoading: false,
-            filterPeriod: undefined,
-            curPdInfo: {},
-            filter: {
-                status: '',
-                mode: '',
-                type: ''
-            }
-        }
-    },
-    computed: {
-        dateOpt1() {
-            let _this = this
-            return {
-                disabledDate(date) {
-                    let d = _this.evaListShow[_this.curEvaIndex]?.startTime ? _this.evaListShow[_this.curEvaIndex].startTime : Date.now()
-                    return d && d > date.valueOf() + 86400000
-                }
-            }
-        },
-    },
-    methods: {
-        // 复制评测
-        copyEv() {
-            // 课中评测不提供复制操作
-            if (this.evaListShow[this.curEvaIndex]?.source === '1') {
-                this.$Modal.info({
-                    title: this.$t('learnActivity.mgtScEv.copyTitle'),
-                    content: this.$t('learnActivity.mgtScEv.copyContent1')
-                })
-            } else if (this.evaListShow[this.curEvaIndex]) {
-                this.$Modal.confirm({
-                    title: this.$t('learnActivity.mgtScEv.copyTitle'),
-                    content: this.$t('learnActivity.mgtScEv.copyContent'),
-                    onOk: () => {
-                        console.log(this.evaListShow[this.curEvaIndex])
-                        this.$router.push({
-                            name: 'createSchoolEva',
-                            params: {
-                                evaluationInfo: this.evaListShow[this.curEvaIndex]
-                            }
-                        })
-                    }
-                })
-            }
-
-        },
-        async copyUrl() {
-            let evName = this.evaListShow[this.curEvaIndex].name
-            let evType = this.getModeLabel(this.evaListShow[this.curEvaIndex].source)
-            let soc = this.evaListShow[this.curEvaIndex].owner === 'school' ? this.$t('learnActivity.mgtScEv.shareText4') : this.$t('learnActivity.mgtScEv.shareText5')
-            let socName = this.evaListShow[this.curEvaIndex].subjects.map(item => item.name).join('、')
-            let shortUrl = await this.$api.getShortUrl(encodeURI(this.shareUrl))
-            let shareText = `${this.$t('learnActivity.mgtScEv.shareText1')}\n\n${soc}${socName}\n${this.$t('learnActivity.mgtScEv.shareText3')}${evType}\n${this.$t('cusMgt.inviteInfo8')}${this.$store.state.userInfo.name}\n${this.$t('learnActivity.mgtScEv.shareText2')}${evName}\n\n${this.$t('learnActivity.mgtScEv.shareText6')}\n${shortUrl.result || encodeURI(this.shareUrl)}\n\n${this.$t('learnActivity.mgtScEv.shareText7')}\nURL:https://${window.location.host}/login/student`
-            this.$copyText(shareText).then(
-                ok => {
-                    this.$Message.success(this.$t("settings.copyModal1"))
-                },
-                fail => {
-                    this.$Message.error(this.$t("settings.copyModal2"))
-                }
-            )
-        },
-        openQrcode() {
-            this.$nextTick(async () => {
-                this.shareUrl = `https://${window.location.host}/studentWeb/eventView?aId=${this.evaListShow[this.curEvaIndex].id}`
-                let shortUrl = await this.$api.getShortUrl(encodeURI(this.shareUrl))
-                // 此时已经渲染完成
-                if (this.shareQRcode == undefined) {
-                    let qrcode = new QRCode('qrcode', {
-                        width: 280, // 设置宽度,单位像素
-                        height: 280, // 设置高度,单位像素
-                        // text: shortUrl.result || encodeURI(this.shareUrl), // 编码处理
-                        text: encodeURI(this.shareUrl), // 编码处理
-                        correctLevel: QRCode.CorrectLevel.Q //解决编码后网址太长的问题
-                    })
-                    this.shareQRcode = qrcode
-                } else {
-                    this.shareQRcode.clear()
-                    // this.shareQRcode.makeCode(shortUrl.result || encodeURI(this.shareUrl))
-                    this.shareQRcode.makeCode(encodeURI(this.shareUrl))
-                }
-                let dom = document.getElementById('qrcode')
-                if (dom) dom.title = ''
-            })
-            this.showQrStatus = true
-        },
-        handleTime(value) {
-            if (value.indexOf('00:00') > 0) {
-                value = value.replace('00:00', '23:59:59')
-                this.editTime = value
-            }
-        },
-        confirmEditEndtime() {
-            if (this.editTime) {
-                this.evaListShow[this.curEvaIndex].code = this.evaListShow[this.curEvaIndex].code.replace('Exam-', '')
-                this.btnLoading = true
-                this.$api.learnActivity.updExamEndtime({
-                    id: this.evaListShow[this.curEvaIndex].id,
-                    code: this.evaListShow[this.curEvaIndex].code,
-                    time: this.editTime.getTime()
-                }).then(
-                    res => {
-                        if (res.code == 200) {
-                            this.evaListShow[this.curEvaIndex].endTime = this.editTime.getTime()
-                            this.$Message.success(this.$t('learnActivity.mgtScEv.updOk'))
-                        } else {
-                            this.$Message.error(this.$t('learnActivity.mgtScEv.updErr'))
-                        }
-                    },
-                    err => {
-                        this.$Message.error(this.$t('learnActivity.mgtScEv.updErr'))
-                    }
-                ).finally(() => {
-                    this.editTimeStatus = false
-                    this.btnLoading = false
-                })
-            } else {
-                this.$Message.warning(this.$t('learnActivity.mgtScEv.endTimeHolder'))
-                this.modalLoading = false
-                setTimeout(() => {
-                    this.modalLoading = true
-                })
-            }
-        },
-        editEvEndtime(index) {
-            this.editTimeStatus = true
-            this.editTime = new Date(this.evaListShow[index]?.endTime)
-        },
-        confirmEditName() {
-            if (this.editName) {
-                this.evaListShow[this.curEvaIndex].code = this.evaListShow[this.curEvaIndex].code.replace('Exam-', '')
-                this.btnLoading = true
-                this.$api.learnActivity.updExamEndtime({
-                    id: this.evaListShow[this.curEvaIndex].id,
-                    code: this.evaListShow[this.curEvaIndex].code,
-                    name: this.editName
-                }).then(
-                    res => {
-                        if (res.code == 200) {
-                            this.evaListShow[this.curEvaIndex].name = this.editName
-                            this.$Message.success(this.$t('learnActivity.mgtScEv.updOk'))
-                        } else {
-                            this.$Message.error(this.$t('learnActivity.mgtScEv.updErr'))
-                        }
-                    },
-                    err => {
-                        this.$Message.error(this.$t('learnActivity.mgtScEv.updErr'))
-                    }
-                ).finally(() => {
-                    this.editNameStatus = false
-                    this.btnLoading = false
-                })
-            } else {
-                this.$Message.warning(this.$t('learnActivity.mgtScEv.edNameHolder'))
-                this.modalLoading = false
-                setTimeout(() => {
-                    this.modalLoading = true
-                })
-            }
-        },
-        editEvName(index) {
-            this.editNameStatus = true
-            this.editName = this.evaListShow[index]?.name
-        },
-        // 启动扫描助手
-        startUp() {
-            //1、直接启动
-            // window.open("tencent://message/?uin=346915968")
-            window.open("hitools://")
-            //2、如果没有启动成功,判断是否下载安装
-        },
-        filterByName() {
-            let curPdEv = this.evaluationList.filter(item => item.period.id === this.filterPeriod)
-            this.evaListShow = curPdEv.filter(item => {
-                return item.name.indexOf(this.keyword) > -1
-            })
-        },
-        closeKeySearch() {
-            this.isSearch = false
-            this.keyword = ''
-            this.filterByName()
-        },
-        //根据学段筛选评测
-        filterByPeriod() {
-            this.curEvaIndex = 0
-            sessionStorage.setItem('evPeriod', this.filterPeriod)
-            if (this.filterPeriod) {
-                this.evaListShow = this.evaluationList.filter(item => item.period.id === this.filterPeriod)
-            } else {
-                this.evaListShow = [...this.evaluationList]
-            }
-            if (this.evaListShow.length) {
-                this.selectEvaluation(0)
-            } else {
-                this.isLoading = false
-            }
-        },
-
-        //筛选评测
-        filterEv() {
-            let curPdEv = this.evaluationList.filter(item => item.period.id === this.filterPeriod)
-            this.evaListShow = curPdEv.filter(item => {
-                let status = !this.filter.status || (this.filter.status == item.progress)
-                let type = !this.filter.type || (this.filter.type == item.type)
-                let mode = !this.filter.mode || (this.filter.mode == item.source)
-                return status && type && mode
-            })
-        },
-        // 模拟教师评分数据
-        mockScoring() {
-            this.scoreLoading = true
-            this.$api.learnActivity.mockScoring({
-                id: this.evaListShow[this.curEvaIndex].id,
-                code: this.evaListShow[this.curEvaIndex].code.replace('Exam-', '')
-            }).then(
-                res => {
-                    setTimeout(() => {
-                        this.$Message.success(this.$t('learnActivity.mgtScEv.mockOk'))
-                    }, 500)
-                },
-                err => {
-                    this.$Message.error(this.$t('learnActivity.mgtScEv.mockErr'))
-                }
-            ).finally(() => {
-                setTimeout(() => {
-                    this.scoreLoading = false
-                    this.reload()
-                }, 500)
-            })
-        },
-        // 模拟学生作答数据
-        mockAnswer() {
-            this.answerLoading = true
-            this.$api.learnActivity.mockAnswer({
-                id: this.evaListShow[this.curEvaIndex].id,
-                code: this.evaListShow[this.curEvaIndex].code.replace('Exam-', '')
-            }).then(
-                res => {
-                    setTimeout(() => {
-                        this.$Message.success(this.$t('learnActivity.mgtScEv.mockOk'))
-                    }, 500)
-                },
-                err => {
-                    this.$Message.error(this.$t('learnActivity.mgtScEv.mockErr'))
-                }
-            ).finally(() => {
-                setTimeout(() => {
-                    this.answerLoading = false
-                    this.reload()
-                }, 500)
-            })
-        },
-        handleEnd(index) {
-            this.$Modal.confirm({
-                title: this.$t('learnActivity.mgtScEv.stopTitle'),
-                content: `${this.$t('learnActivity.mgtScEv.stopContent')}${this.evaListShow[index].name}?`,
-                onOk: () => {
-                    let code = this.evaListShow[index].code
-                    this.$api.learnActivity.FinishEva({
-                        id: this.evaListShow[index].id,
-                        code: code.includes('Exam') ? code : `Exam-${code}`
-                        // code: this.evaListShow[index].code.replace('Exam-', '')
-                    }).then(
-                        res => {
-                            if (!res.error) {
-                                this.$Message.success(this.$t('learnActivity.mgtScEv.stopOk'))
-                                this.evaListShow[index].progress = 'finish'
-                                this.reload()
-                            } else {
-                                // this.$Message.error('API ERROR!')
-                                this.$Message.error(this.$t('learnActivity.mgtScEv.actionErr'))
-                            }
-                        },
-                        err => {
-                            // this.$Message.error('API ERROR!')
-                        }
-                    )
-                }
-            })
-        },
-        dropdownStates(flag) {
-            if (!flag) this.filterByPeriod()
-        },
-        /**获取type对应的label */
-        getTypeLabel(code) {
-            for (let item of this.$GLOBAL.EV_TYPE()) {
-                if (item.value == code) {
-                    return item.label
-                }
-            }
-        },
-        /**获取mode对应的label */
-        getModeLabel(code) {
-            for (let item of this.$GLOBAL.EV_MODE()) {
-                if (item.value == code) {
-                    return item.label
-                }
-            }
-        },
-        /**
-         * 查找班级课程下的班级
-         * */
-        findClassroom() {
-            if (this.targetList.length == 0) {
-                let requestData = this.$store.state.userInfo.TEAMModelId
-                this.$api.learnActivity.FindClassroomByTeacherId({
-                    code: requestData
-                }).then(
-                    res => {
-                        if (!res.error) {
-                            this.targetList = res.result.data
-                        } else {
-                            // this.$Message.error('API ERROR!')
-                            this.$Message.error(this.$t('learnActivity.mgtScEv.classInfoErr'))
-                        }
-                    },
-                    err => {
-
-                    }
-                )
-            }
-        },
-        /**
-         * 判断是否显示回到顶部按钮
-         * @param vertical
-         * @param horizontal
-         * @param nativeEvent
-         */
-        checkBackTop(vertical, horizontal, nativeEvent) {
-            if (vertical.scrollTop > 100) {
-                this.showBack = true
-            } else {
-                this.showBack = false
-            }
-        },
-        /**vuescroll回到顶部 */
-        handleBackToTop() {
-            this.$refs['test-paper-detail'].scrollTo(
-                {
-                    y: '0'
-                },
-                300
-            )
-        },
-        /**删除评测信息 */
-        deleteEvaluation() {
-            this.$Modal.confirm({
-                title: this.$t('learnActivity.mgtScEv.deleteTitle'),
-                content: `${this.$t('learnActivity.mgtScEv.deleteContent')}${this.evaListShow[this.curEvaIndex].name}?`,
-                onOk: () => {
-                    let params = {
-                        id: this.evaListShow[this.curEvaIndex].id,
-                        scope: this.evaListShow[this.curEvaIndex].scope,
-                        code: this.evaListShow[this.curEvaIndex].code.replace('Exam-', '')
-                    }
-                    this.isLoading = true
-                    this.$api.learnActivity.DeleteExamInfo(params).then(
-                        res => {
-                            if (res.error == null) {
-                                let index = this.curEvaIndex
-                                this.selectEvaluation(0)
-                                this.evaListShow.splice(index, 1)
-                                this.$Message.success(this.$t('learnActivity.mgtScEv.deleteOk'))
-                            } else {
-                                this.$Message.error(this.$t('learnActivity.mgtScEv.deleteErr'))
-                            }
-                        },
-                        err => {
-                            this.$Message.error(this.$t('learnActivity.mgtScEv.deleteErr'))
-                        }
-                    ).finally(() => {
-                        setTimeout(() => {
-                            this.isLoading = false
-                        }, 500)
-                    })
-                }
-            })
-        },
-        goToCreate() {
-            if (this.$store.state.user.schoolProfile.school_base != undefined) {
-                if (this.scope == 'school') {
-                    this.$router.push({
-                        name: 'createSchoolEva'
-                    })
-                } else if (this.scope == 'private') {
-                    this.$router.push({
-                        name: 'createPrivEva'
-                    })
-                }
-            } else {
-                this.$Message.warning(this.$t('learnActivity.mgtScEv.noJoin'))
-            }
-        },
-        editEvaluation() {
-            this.$router.push({
-                name: 'createSchoolEva',
-                params: {
-                    evaluationInfo: this.examDetaiInfo
-                }
-            })
-        },
-        selectSubject(index) {
-            this.curSubIndex = index
-        },
-
-        dateFormat(timestamp) {
-            let date = new Date(timestamp)
-            let Y = date.getFullYear()
-            let M = date.getMonth()
-            let D = date.getDate()
-            let H = date.getHours()
-            let MIN = date.getMinutes()
-            return `${Y}-${M < 9 ? '0' + (M + 1) : M + 1}-${D} ${H < 10 ? '0' + H : H}:${MIN < 10 ? '00' : MIN}`
-        },
-        getEvStatusInfo(progress, isScore) {
-            let info = {}
-            if (progress == 'pending') {
-                info.progText = this.$t('learnActivity.mgtScEv.pending')
-                info.progColor = '#2d8cf0'
-            } else if (progress == 'going') {
-                info.progText = this.$t('learnActivity.mgtScEv.going')
-                info.progColor = '#19be6b'
-            } else if (progress == 'finish') {
-                info.progText = this.$t('learnActivity.mgtScEv.finish')
-                info.progColor = '#515a6e'
-            }
-            if (isScore === 0) {
-                info.scoreText = this.$t('learnActivity.mgtScEv.scoreStatus')
-                info.scoreColor = '#ff9900'
-            } else if (isScore === 1) {
-                info.scoreText = this.$t('learnActivity.mgtScEv.scoreStatus1')
-                info.scoreColor = '#515a6e'
-            }
-            return info
-        },
-        //查询评测列表
-        findEvaluation() {
-            this.isLoading = true
-            let requestData = {
-                code: this.scope == 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId
-            }
-            this.$api.learnActivity.FindExamList(requestData).then(
-                res => {
-                    if (!res.error) {
-                        res.examInfo = res.examInfo.sort((a, b) => {
-                            return a.createTime - b.createTime > 0 ? -1 : 1
-                        })
-                        res.examInfo.forEach(item => {
-                            let statusInfo = this.getEvStatusInfo(item.progress, item.sStatus)
-                            item.progText = statusInfo.progText
-                            item.progColor = statusInfo.progColor
-                            item.scoreText = statusInfo.scoreText
-                            item.scoreColor = statusInfo.scoreColor
-                        })
-                        this.evaluationList = res.examInfo
-                        this.evaListShow = res.examInfo
-                        if (this.scope == 'school') {
-                            this.filterByPeriod()
-                        } else {
-                            if (res.examInfo.length) {
-                                this.selectEvaluation(0)
-                            } else {
-                                this.isLoading = false
-                            }
-                        }
-                    } else {
-                        // this.$Message.error('API ERROR!')
-                        this.$Message.error(this.$t('learnActivity.mgtScEv.evListErr'))
-                        this.isLoading = false
-                    }
-                },
-                err => {
-                    this.isLoading = false
-                }
-            )
-        },
-        checkScoreSave(fn, index) {
-            let compStu = this.$refs['score-box'] ? this.$refs['score-box'].$refs['byStuMark'] : null
-            let compQu = this.$refs['score-box'] ? this.$refs['score-box'].$refs['byQuMark'] : null
-            if ((compStu && compStu.isUpd) || (compQu && compQu.isUpd)) {
-                this.$Modal.confirm({
-                    title: this.$t('learnActivity.score.saveScoreTitle'),
-                    content: this.$t('learnActivity.score.saveScoreContent'),
-                    okText: this.$t('learnActivity.mark.yes'),
-                    cancelText: this.$t('learnActivity.mark.no'),
-                    onOk: () => {
-                        if (compStu && compStu.isUpd) {
-                            compStu.saveScore()
-                        } else if (compQu && compQu.isUpd) {
-                            compQu.saveScore()
-                        }
-                        if (fn) fn(index)
-                    },
-                    onCancel: () => {
-                        if (compStu && compStu.isUpd) {
-                            compStu.isUpd = false
-                        } else if (compQu && compQu.isUpd) {
-                            compStu.preSaveList = {}
-                        }
-                        if (fn) fn(index)
-                    }
-                })
-            } else {
-                if (fn) fn(index)
-            }
-        },
-        selectEvaluation(index) {
-            this.checkScoreSave(this.toEvaluation, index)
-        },
-        selectBar(index) {
-            this.checkScoreSave(this.handleSelectBar, index)
-        },
-        handleSelectBar(index) {
-            this.curBarIndex = index
-            this.$nextTick(() => {
-                if (this.$refs['score-box']) this.$refs['score-box'].showTest = false
-            })
-        },
-        toEvaluation(index) {
-            this.curSubIndex = 0
-            this.curEvaIndex = index
-            sessionStorage.setItem('examScope', this.evaListShow[index].scope)
-            this.$nextTick(() => {
-                this.$refs['score-box'].showTest = false
-            })
-            if (this.evaListShow[this.curEvaIndex] && this.evaListShow[this.curEvaIndex].papers.length == 0) {
-                this.findExamInfo()
-            } else {
-                this.examDetaiInfo = this.evaListShow[this.curEvaIndex]
-            }
-        },
-        //查询当前评测的详细信息
-        findExamInfo() {
-            let requestData = {
-                id: this.evaListShow[this.curEvaIndex].id,
-                code: this.evaListShow[this.curEvaIndex].code
-            }
-            this.isLoading = true
-            this.$api.learnActivity.FindExamInfo(requestData).then(
-                async res => {
-                    if (!res.error) {
-                        let resData = res.examInfo[0]
-                        resData.score = 0
-                        for (let index in resData.papers) {
-                            let blob = resData.papers[index].blob
-                            let sheetNo = resData.papers[index].sheetNo
-                            resData.papers[index].examScope = this.evaListShow[this.curEvaIndex].scope
-                            resData.papers[index].examCode = this.evaListShow[this.curEvaIndex].code
-                            resData.papers[index].examId = this.evaListShow[this.curEvaIndex].id
-                            resData.papers[index].owner = this.evaListShow[this.curEvaIndex].owner
-                            resData.papers[index].sheetNo = sheetNo
-                            resData.papers[index] = await this.$evTools.getFullPaper(resData.papers[index])
-                            resData.papers[index].examScope = this.evaListShow[this.curEvaIndex].scope
-                            resData.papers[index].examCode = this.evaListShow[this.curEvaIndex].code
-                            resData.papers[index].examId = this.evaListShow[this.curEvaIndex].id
-                            resData.papers[index].owner = this.evaListShow[this.curEvaIndex].owner
-                            resData.papers[index].sheetNo = sheetNo
-                            if (!resData.papers[index].subjectId) {
-                                resData.papers[index].subjectId = blob.substring(blob.lastIndexOf('/') + 1)
-                            }
-                            resData.papers[index].blob = blob
-                            resData.score += resData.papers[index].score
-
-                            //补充评测状态信息
-                            let statusInfo = this.getEvStatusInfo(resData.progress, resData.sStatus)
-                            resData.progText = statusInfo.progText
-                            resData.progColor = statusInfo.progColor
-                            resData.scoreText = statusInfo.scoreText
-                            resData.scoreColor = statusInfo.scoreColor
-                        }
-                        this.evaListShow.splice(this.curEvaIndex, 1, resData)
-                        this.examDetaiInfo = resData
-                    } else {
-                        this.$Message.error(this.$t('learnActivity.mgtScEv.exInfoErr'))
-                        // this.$Message.error('API ERROR!')
-                    }
-                },
-                err => {
-                    // this.$Message.error('API ERROR!')
-                }
-            ).finally(() => {
-                this.isLoading = false;
-            })
-        },
-        beforeunloadHandler(e) {
-            let compStu = this.$refs['score-box'] ? this.$refs['score-box'].$refs['byStuMark'] : null
-            let compQu = this.$refs['score-box'] ? this.$refs['score-box'].$refs['byQuMark'] : null
-            if ((compStu && compStu.isUpd) || (compQu && compQu.isUpd)) {
-                e = e || window.event
-                if (e) {
-                    e.returnValue = '关闭提示'
-                }
-                return '关闭提示'
-            }
-        },
-        // goBack(){}
-    },
-    mounted() {
-        // 监听浏览器刷新事件
-        window.addEventListener('beforeunload', e => this.beforeunloadHandler(e))
-        // 处理浏览器返回事件
-        // if (window.history && window.history.pushState) {
-        //     // 向历史记录中插入了当前页
-        //     history.pushState(null, null, document.URL);
-        //     window.addEventListener('popstate', this.goBack, false);
-        // }
-    },
-    destroyed() {
-        window.removeEventListener('beforeunload', e => this.beforeunloadHandler(e))
-        window.removeEventListener('popstate', this.goBack, false);
-    },
-    created() {
-        this.scope = 'school'
-        this.findEvaluation()
-    },
-    watch: {
-        curBarIndex(n, o) {
-            if (n == 0) {
-                if (this.$refs['score-box'] && this.$refs['score-box'].$refs['score-table']) {
-                    this.$nextTick(() => {
-                        this.$refs['score-box'].$refs['score-table'].handleResize()
-                    })
-                }
-            }
-        },
-        '$store.state.user.curPeriod': {
-            deep: true,
-            immediate: true,
-            handler(n, o) {
-                if (n) {
-                    this.filterPeriod = n.id
-                    this.curPdInfo = n
-                    this.filterByPeriod()
-                }
-            }
-        }
-    },
-    beforeRouteLeave(to, from, next) {
-        this.checkScoreSave(next)
-    }
-}
-</script>
-<style lang="less" scoped>
-@import "./MgtSchoolEva.less";
-</style>
-<style lang="less">
-.evalustion-base-attr .ivu-form .ivu-form-item-label {
-    color: #a5a5a5;
-}
-.evaluation-attr-form .ivu-select-disabled .ivu-select-selection {
-    background: none;
-}
-.sort-dropdown {
-    .title {
-        color: var(--primary-text-color);
-        font-size: 14px;
-    }
-    .ivu-select-dropdown {
-        // background-color: #2d2d2d;
-        border-radius: 2px;
-        // border: 1px #464646 solid;
-        .ivu-dropdown-menu {
-            li {
-                // color: #ccc;
-                font-size: 12px !important;
-                &:hover {
-                    color: #2d2d2d;
-                }
-            }
-        }
-    }
-}
-// .test-paper-detail .paper-container {
-//     padding-bottom: 30px;
-// }
-</style>

+ 0 - 150
TEAMModelOS/ClientApp/src/view/learnactivity/Scoring.less

@@ -1,150 +0,0 @@
-@main-bgColor: rgb(40,40,40); //主背景颜色
-@borderColor: #424242;
-@primary-textColor: #fff; //文本主颜色
-@second-textColor: #a5a5a5; //文本副级颜色
-@primary-fontSize: 14px;
-@second-fontSize: 16px;
-
-.ev-scoring {
-    width: 100%;
-    height: 100%;
-    padding: 0px 0px 20px 0px;
-}
-.ev-scoring {
-    .ev-scoring-header {
-        width: 100%;
-        height: 36px;
-        line-height: 36px;
-        color: @second-textColor;
-        border-bottom: 1px solid @borderColor;
-    }
-
-    .subject-item {
-        display: inline-block;
-        margin-right: 15px;
-        color: @second-textColor;
-        cursor: pointer;
-        line-height: 30px;
-        padding:0px 5px;
-        font-size: 15px;
-        text-align: center;
-        margin-left: 10px;
-    }
-
-    .subject-item-active {
-        color: @primary-textColor;
-        border-bottom: 2px solid white;
-        font-weight: 500;
-    }
-}
-.common-icon-text {
-    color: var(--primary-text-color);
-    cursor: pointer;
-    user-select: none;
-    float: right; 
-    margin-right: 15px;
-}
-.scoring-main-wrap {
-    width: ~"calc(100% - 10px)";
-    height: ~"calc(100% - 45px)";
-}
-
-.ev-target-box {
-    width: ~"calc(100% - 10px)";
-    color: var(--label-text-color);
-    padding: 10px 10px 10px 0;
-    position: sticky;
-    top: 0px;
-    z-index: 9999;
-    background: white;
-    .filter-select {
-        display: inline-block;
-        width: 120px;
-        margin-right: 25px;
-    }
-}
-
-.scoring-handle-box {
-    height: 100%;
-    background:white;
-    padding-bottom:15px;
-}
-
-.stu-status-tag{
-    cursor:pointer;
-    background: #ed4014;
-    font-size: 12px;
-    padding: 3px 8px;
-    font-weight: 800;
-    border-radius: 4px;
-    color: white;
-}
-.select-status-tag{
-    display: inline-block;
-    width: 7px;
-    height: 7px;
-    border-radius: 50%;
-    margin-right: 5px;
-}
-.simple-analysis-box{
-    width: 100%;
-    height: 200px;
-}
-.page-wrap{
-    float: right;
-    margin-top: 15px;
-    color: white;
-}
-.mark-viewer{
-    z-index: 9999;
-    width: 100%;
-    height: 100%;
-}
-.header-tips{
-    color: #515a6e;
-    float: right;
-    margin-top: 5px;
-}
-.option-info{
-    display: flex;
-    margin-top: 5px;
-}
-.correct-rate-label{
-    margin-top: 15px;
-    color: #17233d;
-    font-weight: 600;
-}
-.correct-tips{
-    width: 300px;
-}
-.chart-wrap{
-    display: flex;
-    // align-items: center;
-}
-.correct-tips-label{
-    color: black;
-    margin-bottom: 15px;
-    font-weight: 600;
-}
-.qu-type-tag{
-    background: #2db7f5;
-    padding: 0px 4px;
-    color: white;
-    border-radius: 2px;
-    width: fit-content;
-    word-break: keep-all;
-    height: fit-content;
-}
-.export-btn{
-    float: right;
-    margin-left: 20px;
-    margin-top: 5px;
-}
-.stu-name{
-    color: #FFFFFF;
-    background: #2d8cf0;
-    padding: 2px 10px;
-    border-radius: 2px;
-    font-size: 16px;
-    margin-right: 20px;
-}

File diff suppressed because it is too large
+ 0 - 1170
TEAMModelOS/ClientApp/src/view/learnactivity/Scoring.vue


+ 0 - 371
TEAMModelOS/ClientApp/src/view/learnactivity/SimpleAnalysis.vue

@@ -1,371 +0,0 @@
-<template>
-    <div>
-        <div v-if="dataErr" class="data-err-wrap">
-            <EmptyData :textContent="$t('learnActivity.simple.dataErr')"></EmptyData>
-        </div>
-        <div class="overview-box" v-if="examInfo.progress == 'finish' && !dataErr">
-            <!-- 总人数 -->
-            <div class="count-box">
-                <span class="count-subject-num">{{examInfo.stuCount}}</span>
-                <span class="count-subject-text">
-                    <Icon type="ios-people" class="count-icon" />
-                    {{$t('learnActivity.simple.totalPeople')}}
-                </span>
-            </div>
-            <!-- 缺考数 -->
-            <div class="count-box">
-                <span class="count-subject-num">{{examInfo.lostStu ? examInfo.lostStu.length : 0}}</span>
-                <span class="count-subject-text">
-                    <Icon type="ios-paper" class="count-icon" />
-                    {{$t('learnActivity.simple.missExam')}}
-                </span>
-            </div>
-            <!-- 班级数 -->
-            <div class="count-box">
-                <span class="count-subject-num">{{examInfo.classes.length || examInfo.stuLists.length}}</span>
-                <span class="count-subject-text">
-                    <Icon custom="iconfont icon-class-self" class="count-icon" />
-                    {{$t('learnActivity.simple.classLabel')}}
-                </span>
-            </div>
-            <!-- 学科数 -->
-            <div class="count-box">
-                <span class="count-subject-num">{{examInfo.subjects.length}}</span>
-                <span class="count-subject-text">
-                    <Icon custom="iconfont icon-course-self" class="count-icon" />
-                    {{$t('learnActivity.simple.sjLabel')}}
-                </span>
-            </div>
-            <!-- 平均分 -->
-            <div class="count-box">
-                <span class="count-subject-num">{{avgScore}}</span>
-                <span class="count-subject-text">
-                    <Icon type="md-analytics" class="count-icon" size="14" />
-                    {{$t('learnActivity.simple.avgScore')}}
-                </span>
-            </div>
-        </div>
-        <div class="overview-box" style="justify-content: space-around;padding-top:40px" v-if="examInfo.progress == 'finish' && !dataErr">
-            <ScoreMatrix :pieData="scoreSegment"></ScoreMatrix>
-            <Comply :complyData="complyData"></Comply>
-            <AvgCompare :pieData="simpleData.averageMap"></AvgCompare>
-        </div>
-        <div class="overview-box" v-if="examInfo.progress == 'going'" style="margin-bottom:30px;">
-            <!-- 班级人数 -->
-            <div class="count-box">
-                <span class="count-subject-num">{{overviewInfo.total}}</span>
-                <span class="count-subject-text">
-                    <Icon type="ios-people" class="count-icon" />
-                    {{$t('learnActivity.simple.classStuCount')}}
-                </span>
-            </div>
-            <!-- 已作答人数 -->
-            <div class="count-box" style="color:#2db7f5">
-                <span class="count-subject-num">{{overviewInfo.answered}}</span>
-                <span class="count-subject-text">
-                    <Icon type="md-checkmark-circle" class="count-icon" />
-                    {{$t('learnActivity.simple.answered')}}
-                </span>
-            </div>
-            <!-- 未作答人数 -->
-            <div class="count-box" style="color:#ed4014">
-                <span class="count-subject-num">{{overviewInfo.noAnswer}}</span>
-                <span class="count-subject-text">
-                    <Icon type="md-remove-circle" class="count-icon" />
-                    {{$t('learnActivity.simple.unanswer')}}
-                </span>
-            </div>
-            <!-- 已评分人数 -->
-            <div class="count-box" style="color:#19be6b">
-                <span class="count-subject-num">{{overviewInfo.scored}}</span>
-                <span class="count-subject-text">
-                    <Icon type="md-star" class="count-icon" />
-                    {{$t('learnActivity.simple.scored')}}
-                </span>
-            </div>
-            <!-- 未评分人数 -->
-            <div class="count-box" style="color:#ff9900">
-                <span class="count-subject-num">{{overviewInfo.noScore}}</span>
-                <span class="count-subject-text">
-                    <Icon type="ios-star-outline" class="count-icon" />
-                    {{$t('learnActivity.simple.unscore')}}
-                </span>
-            </div>
-        </div>
-        <div class="overview-box" v-if="examInfo.progress == 'pending'" style="margin-bottom:30px;">
-            <p class="pending-text">{{$t('learnActivity.simple.noPublish')}}</p>
-        </div>
-        <div v-show="calculating">
-            <p style="text-align: center;color: white;margin: 20px 0px;">
-                {{$t('learnActivity.simple.calcing')}}
-                <span style="text-decoration: underline;color: #1cc0f3;cursor: pointer;" @click="reload()">
-                    {{$t('learnActivity.simple.clickFresh')}}
-                </span>
-            </p>
-        </div>
-    </div>
-</template>
-<script>
-import ScoreMatrix from './echarts/ScoreMatrix.vue'
-import Comply from './echarts/Comply.vue'
-import AvgCompare from './echarts/AvgCompare.vue'
-export default {
-    components: {
-        AvgCompare,
-        ScoreMatrix,
-        Comply
-    },
-    inject: ['reload'],
-    props: {
-        examInfo: {
-            default: () => {
-                return {
-                    subjects: [],
-                    classes: []
-                }
-            },
-            type: Object
-        },
-        overviewInfo: {
-            type: Object,
-            default: () => {
-                return {
-                    total: 0,
-                    answered: 0,
-                    noAnswer: 0,
-                    scored: 0,
-                    noScore: 0
-                }
-            }
-        }
-    },
-    data() {
-        return {
-            dataErr:false,
-            simpleData: {},
-            allData: {},
-            calculating: false
-        }
-    },
-    methods: {
-        //获取已结束评测简要数据分析
-        findSimpleAna() {
-            this.simpleData = {}
-            let requestData = {
-                id: this.examInfo.id,
-                code: this.examInfo.code.replace('Exam-', '')
-            }
-            this.$api.learnActivity.simpleAna(requestData).then(
-                res => {
-                    if (!res.error) {
-                        if (res.averageMap.length && res.averageMap.length) {
-                            this.dataErr = false
-                            this.calculating = false
-                            for (let i in res.averageMap) {
-                                for (let j in res.averageMap[i].ClassAverage) {
-                                    res.averageMap[i].ClassAverage[j] = parseFloat(res.averageMap[i].ClassAverage[j].toFixed(2))
-                                }
-                                let subject = this.examInfo.subjects.find(item => {
-                                    return item.id == res.averageMap[i].subjectId
-                                })
-                                if (subject) {
-                                    res.averageMap[i].subjectId = subject.name
-                                } else {
-                                    res.averageMap[i].subjectId = '-'
-                                }
-                            }
-                            res.averageTotal.forEach(item => {
-                                let subjectInfo = this.examInfo.papers.find(subItem => {
-                                    return subItem.subjectId == item.subjectId
-                                })
-                                if (subjectInfo) {
-                                    item.subjectName = subjectInfo.subjectName
-                                    item.paperScore = subjectInfo.score
-                                }else{
-                                    item.subjectName = this.examInfo.subjects && this.examInfo.subjects.length ? this.examInfo.subjects[0].name : ''
-                                    item.paperScore = this.examInfo.papers && this.examInfo.papers[0] ? this.examInfo.papers[0].score : ''
-                                }
-                            })
-                            this.allData[this.examInfo.id] = res
-                            this.simpleData = res
-                            this.$emit('on-corect-data',this.simpleData)
-                        } else {
-                            this.calculating = true
-                            this.$Message.warning(this.$t('learnActivity.simple.inCalc'))
-                        }
-
-                    } else {
-                        this.dataErr = true
-                        this.$Message.error(this.$t('learnActivity.simple.simpleErr'))
-                    }
-                },
-                err => {
-                    this.dataErr = true
-                }
-            ).finally(() => {
-                this.isLoading = false
-            })
-        },
-    },
-    watch: {
-        examInfo: {
-            handler() {
-                if (this.examInfo.progress == 'finish') {
-                    this.findSimpleAna()
-                }
-            },
-            deep: true
-        }
-    },
-    computed: {
-        examScore() {
-            if (this.examInfo && this.examInfo.score) {
-                return this.examInfo.score
-            } else {
-                return 0
-            }
-        },
-        avgScore() {
-            if (this.simpleData && this.simpleData.averageMap) {
-                let avg = 0
-                this.simpleData.averageMap.forEach(item => {
-                    let total = item.ClassAverage.reduce((total, num) => {
-                        return total + num
-                    }, 0)
-                    let sAvg = total / item.ClassAverage.length
-                    avg += sAvg
-                })
-                return avg.toFixed(2)
-            } else {
-                return '--'
-            }
-        },
-        stuTotalScores() {
-            if (this.simpleData && this.simpleData.averageTotal) {
-                let total = []
-                this.simpleData.averageTotal.forEach((subItem, i) => {
-                    subItem.total.forEach((sItem, j) => {
-                        if (!total[j]) total[j] = 0
-                        total[j] += sItem
-                    })
-                })
-                return total
-            } else {
-                return []
-            }
-        },
-        scoreSegment() {
-            if (this.stuTotalScores.length) {
-                let segment = []
-                let unit = this.examScore / 10
-                let startScore = 0
-                for (let i = 0; i < 10; i++) {
-                    let endScore = Math.ceil(unit * (i + 1))
-                    let s = this.stuTotalScores.filter(item => {
-                        return item >= startScore && item <= endScore
-                    })
-                    segment.push({
-                        name: `${startScore}-${endScore}`,
-                        value: s.length
-                    })
-                    startScore = endScore + 1
-                }
-                console.log('分数段统计:', segment)
-                return segment
-            } else {
-                return []
-            }
-        },
-        complyData() {
-            if (this.stuTotalScores.length && this.examInfo && this.examInfo.papers) {
-                console.log('评测信息:', this.examInfo)
-                console.log('分数', this.simpleData)
-                let data = []
-                this.simpleData.averageTotal.forEach(item => {
-                    let passScore = item.paperScore * 0.6
-                    let pass = item.total.filter(i => i >= passScore)
-                    let passCount = pass.length
-                    data.push({
-                        id: item.subjectId,
-                        name: item.subjectName,
-                        passCount: passCount,
-                        failCount: item.total.length - passCount
-                    })
-                })
-                console.log('及格人数统计', data)
-                return data
-                // let passScore = this.examScore * 0.6
-                // let passCount = this.stuTotalScores.filter(item => {
-                //     return item >= passScore
-                // })
-                // let failedCount = this.stuTotalScores.filter(item => {
-                //     return item < passScore
-                // })
-                // return [
-                //     {
-                //         name: this.$t('learnActivity.simple.complyNum'),
-                //         value: passCount.length
-                //     },
-                //     {
-                //         name: this.$t('learnActivity.simple.failedNum'),
-                //         value: failedCount.length
-                //     }
-                // ]
-            } else {
-                return []
-            }
-        }
-    }
-}
-</script>
-<style lang="less" scoped>
-.data-err-wrap{
-    background: #f9f9f9;
-    padding: 10px 0px 40px 0px;
-}
-.overview-box {
-    width: 100%;
-    flex-wrap: wrap;
-    padding: 10px 10px 0px 0px;
-    display: flex;
-    justify-content: space-between;
-    // background: #404040;
-    .count-box {
-        min-width: 150px;
-        width: 16%;
-        height: fit-content;
-        text-align: center;
-        background-color: #00b2c1;
-        background-image: linear-gradient(120deg, #00b2c1 0%, #54adff 100%);
-        border-radius: 5px;
-        padding: 15px 0px;
-        color: white !important;
-        .count-icon {
-            font-size: 18px;
-            margin-right: 5px;
-            // vertical-align: sub;
-        }
-        .count-subject-text {
-            display: block;
-            font-size: 12px;
-        }
-        .count-subject-num {
-            display: block;
-            font-size: 30px;
-            font-weight: 800;
-        }
-    }
-}
-.finish-count-box {
-    .count-box {
-        margin-bottom: 10px;
-    }
-}
-.pending-text {
-    color: #eee;
-    font-size: 16px;
-    padding-top: 15px;
-    width: 100%;
-    text-align: center;
-}
-</style>

+ 1 - 1
TEAMModelOS/ClientApp/src/view/learnactivity/markpaper/MarkData.vue

@@ -66,7 +66,7 @@
                         </template>
                         <template slot-scope="{ row }" slot="progress">
                             <div style="display: flex;justify-content: center;">
-                                <i-circle :percent="row.percent" :size="40" :stroke-width="8" :trail-width="7" stroke-color="#ff5500">
+                                <i-circle :percent="row.percent || 0" :size="40" :stroke-width="8" :trail-width="7" stroke-color="#5cb85c">
                                     <span v-if="row.percent != 100" style="font-size:12px">{{row.percent}}%</span>
                                     <Icon v-else type="ios-checkmark" size="30" style="color:#5cb85c"></Icon>
                                 </i-circle>

+ 101 - 35
TEAMModelOS/ClientApp/src/view/learnactivity/markpaper/PublishTask.vue

@@ -99,7 +99,7 @@
                     </span>
                 </FormItem>
                 <!-- 题目划块 -->
-                <FormItem :label="$t('learnActivity.mark.quBlock')" class="setting-item-wrap" v-else>
+                <FormItem :label="$t('learnActivity.mark.quBlock')" prop="quBlockes" class="setting-item-wrap" v-else>
                     <Table :columns="blockCol" :data="quBlockData" border>
                         <template slot-scope="{ row,index }" slot="action">
                             <Button type="info" size="small" style="margin-right:10px" @click="editBlock(row)">
@@ -193,6 +193,38 @@ export default {
     },
     data() {
         const _this = this
+        // 验证题目分块
+        const validateBlockes = (rule, value, callback) => {
+            let curQuBlock = this.quBlockes[this.curSubIndex]
+            if (curQuBlock.quBlock && curQuBlock.quBlock.length) {
+                //验证按题分配模式 题目分块数据
+                let allNo = []
+                curQuBlock.quBlock.forEach(item => {
+                    allNo.push(...item.quNo)
+                })
+                let quListData = this.paperQuList.find(pQu => {
+                    return pQu.id == curQuBlock.id
+                })
+                //1、验证题目是否完全分配 some方法返回true的时候就会break循环, forEach方法不能break
+                let last = quListData.quList.some(item => {
+                    return !allNo.includes(item.value) && !item.disabled
+                })
+                if (last) {
+                    callback(new Error(this.$t('learnActivity.mark.lastQu')))
+                }
+
+                //2、验证题目是否重复分配
+                let rep = allNo.some((item, index) => {
+                    return allNo.indexOf(item) != index
+                })
+                if (rep) {
+                    callback(new Error(this.$t('learnActivity.mark.reapQu')))
+                }
+                callback()
+            } else {
+                callback(new Error(this.$t('learnActivity.mark.setQuBlock')))
+            }
+        }
         return {
             editId: '',
             paperQuList: [],
@@ -327,6 +359,9 @@ export default {
                 markers: [
                     { required: true, type: 'array', message: this.$t('learnActivity.mark.markerErr'), trigger: 'change' }
                 ],
+                quBlockes: [
+                    { validator: validateBlockes, trigger: 'change' }
+                ],
             },
             setting: {
                 id: '',
@@ -472,6 +507,7 @@ export default {
                     if (blockData) {
                         blockData.quBlock.splice(index, 1)
                     }
+                    this.$refs['teacherSet'].validateField('markers', (valid) => { })
                 }
             })
         },
@@ -559,6 +595,7 @@ export default {
             this.quIds = []
             this.quTeachers = []
             this.$refs.quTea.selectAll(false)
+            this.$refs['teacherSet'].validateField('quBlockes', (valid) => { })
         },
         //移除阅卷老师
         removeMarker(row, index) {
@@ -621,6 +658,7 @@ export default {
                 })
             }
             this.cancel()
+            this.$refs['teacherSet'].validateField('markers', (valid) => { })
         },
         cancel() {
             this.sltTeachers = []
@@ -636,46 +674,74 @@ export default {
                 }
             })
             if (!full) return
-            // 验证学科阅卷相关老师设置 待完善,目前验证了当前学科
-            this.$refs['teacherSet'].validate((valid) => {
-                if (!valid) {
-                    full = false
-                    this.$Message.error(this.$t('learnActivity.mark.teacherErr'))
-                }
-            })
-            if (!full) return
 
+            console.log(this.setting)
+            console.log(this.quBlockes)
+            // 验证所有学科阅卷相关老师设置  按题分配检查quBlockes
             if (this.setting.mode == 'qu') {
-                //验证按题分配模式 题目分块数据
-                let res = this.quBlockes.some(dataItem => {
-                    let allNo = []
-                    dataItem.quBlock.forEach(item => {
-                        allNo.push(...item.quNo)
-                    })
-                    let quListData = this.paperQuList.find(pQu => {
-                        return pQu.id == dataItem.id
-                    })
-                    //1、验证题目是否完全分配 some方法返回true的时候就会break循环, forEach方法不能break
-                    let last = quListData.quList.some(item => {
-                        return !allNo.includes(item.value) && !item.disabled
-                    })
-                    if (last) {
-                        this.$Message.error(this.$t('learnActivity.mark.lastQu'))
-                        return true
+                for (let i = 0; i < this.quBlockes.length; i++) {
+                    let subjectInfo = this.setting.subs[i]
+                    let curQuBlock = this.quBlockes[i]
+                    let isSet = subjectInfo.err && subjectInfo.err.length && curQuBlock.quBlock && curQuBlock.quBlock.length
+                    //暂无分块
+                    if (!isSet) {
+                        full = false
+                        this.curSubIndex = i
+                        setTimeout(() => { this.$refs['teacherSet'].validate() })
+                        break
                     }
+                    //分块详细验证
+                    else {
+                        //验证按题分配模式 题目分块数据
+                        let allNo = []
+                        curQuBlock.quBlock.forEach(item => {
+                            allNo.push(...item.quNo)
+                        })
+                        let quListData = this.paperQuList.find(pQu => {
+                            return pQu.id == curQuBlock.id
+                        })
+                        //1、验证题目是否完全分配 some方法返回true的时候就会break循环, forEach方法不能break
+                        let last = quListData.quList.some(item => {
+                            return !allNo.includes(item.value) && !item.disabled
+                        })
+                        if (last) {
+                            full = false
+                            this.curSubIndex = i
+                            setTimeout(() => { this.$refs['teacherSet'].validate() })
+                            break
+                        }
 
-                    //2、验证题目是否重复分配
-                    let rep = allNo.some((item, index) => {
-                        return allNo.indexOf(item) != index
-                    })
-                    if (rep) {
-                        this.$Message.error(this.$t('learnActivity.mark.reapQu'))
-                        return true
+                        //2、验证题目是否重复分配
+                        let rep = allNo.some((item, index) => {
+                            return allNo.indexOf(item) != index
+                        })
+                        if (rep) {
+                            full = false
+                            this.curSubIndex = i
+                            setTimeout(() => { this.$refs['teacherSet'].validate() })
+                            break
+                        }
                     }
-                })
-                if (res) return
-                //通过验证,将按题分配数据结构转成标准API结构
+                }
+            }
+            // 验证所有学科阅卷相关老师设置  按题分配检查 this.setting.subs
+            else {
+                for (let i = 0; i < this.setting.subs.length; i++) {
+                    let subjectInfo = this.setting.subs[i]
+                    let isSet = subjectInfo.err && subjectInfo.err.length && subjectInfo.markers.length && subjectInfo.markers.length
+                    if (!isSet) {
+                        full = false
+                        this.curSubIndex = i
+                        setTimeout(() => { this.$refs['teacherSet'].validate() })
+                        break
+                    }
+                }
+            }
+
+            if (!full) return
 
+            if (this.setting.mode == 'qu') {
+                //通过验证,将按题分配数据结构转成标准API结构
                 this.quBlockes.forEach(subItem => {
                     let curSubData = this.setting.subs.find(item => {
                         return item.id == subItem.id

+ 51 - 20
TEAMModelOS/ClientApp/src/view/learnactivity/tabs/AnswerTable.less

@@ -1,20 +1,23 @@
-.data-count-wrap{
+.data-count-wrap {
     padding: 20px 0px;
     display: flex;
     background: white;
     justify-content: space-between;
     box-shadow: 0 0 10px 2px var(--card-shadow);
 }
-.data-count-item{
+
+.data-count-item {
     width: 150px;
     text-align: center;
-    .data-value{
+
+    .data-value {
         font-size: 30px;
         font-weight: 600;
         color: #17233d;
     }
 }
-.table-data-wrap{
+
+.table-data-wrap {
     width: 100%;
     box-shadow: 0 0 10px 2px var(--card-shadow);
     margin-top: 10px;
@@ -23,30 +26,35 @@
     background: white;
     padding: 10px 10px 60px 10px;
 }
+
 .ev-target-box {
     color: var(--label-text-color);
     padding: 10px 10px 10px 0;
     position: sticky;
     top: 0px;
     background: white;
+
     .filter-select {
         display: inline-block;
         width: 120px;
         margin-right: 25px;
     }
 }
-.export-btn{
+
+.export-btn {
     float: right;
     margin-left: 20px;
     margin-top: 5px;
 }
-.header-tips{
+
+.header-tips {
     color: #515a6e;
     float: right;
     margin-top: 5px;
 }
-.stu-status-tag{
-    cursor:pointer;
+
+.stu-status-tag {
+    cursor: pointer;
     background: #ed4014;
     font-size: 12px;
     padding: 3px 8px;
@@ -54,33 +62,50 @@
     border-radius: 4px;
     color: white;
 }
-.score-box{
+
+.score-box {
     margin-top: 10px;
 }
-.page-wrap{
+
+.page-wrap {
     float: right;
     margin-top: 15px;
 }
+
 .common-icon-text {
     color: var(--primary-text-color);
     cursor: pointer;
     user-select: none;
-    float: right; 
+    float: right;
     margin-right: 11px;
 }
-.mark-view-header{
+
+.mark-view-header {
     top: 0px;
     z-index: 9;
     padding: 15px;
+    color: var(--label-text-color);
+    position: sticky;
+    top: 0px;
+    background: white;
+    overflow:visible;
+
+    .filter-select {
+        display: inline-block;
+        width: 120px;
+        margin-right: 25px;
+    }
 }
-.select-status-tag{
+
+.select-status-tag {
     display: inline-block;
     width: 7px;
     height: 7px;
     border-radius: 50%;
     margin-right: 5px;
 }
-.qu-type-tag{
+
+.qu-type-tag {
     background: #2db7f5;
     padding: 0px 4px;
     color: white;
@@ -89,25 +114,31 @@
     word-break: keep-all;
     height: fit-content;
 }
-.correct-tips{
+
+.correct-tips {
     width: 300px;
 }
-.chart-wrap{
+
+.chart-wrap {
     display: flex;
     // align-items: center;
 }
-.correct-tips-label{
+
+.correct-tips-label {
     color: black;
     margin-bottom: 15px;
     font-weight: 600;
 }
-.stu-info{
+
+.stu-info {
     margin-right: 30px;
     font-size: 16px;
 }
-.stu-value{
+
+.stu-value {
     font-weight: 600;
 }
-.stu-label{
+
+.stu-label {
     color: #808695;
 }

+ 37 - 39
TEAMModelOS/ClientApp/src/view/learnactivity/tabs/AnswerTable.vue

@@ -104,45 +104,43 @@
         </vuescroll>
         <!-- 打分视图 -->
         <vuescroll v-show="isMarkView">
-            <div class="ev-target-box light-iview-select light-iview-input mark-view-header">
-                <!-- 筛选班级 -->
-                <span>{{$t('learnActivity.score.classLabel')}}</span>
-                <Cascader v-model="chooseTarget" :data="targetsData" :clearable="false" transfer @on-change="targetChange" style="width:200px;display:inline-block;margin-right:15px"></Cascader>
-                <!-- 筛选学科 应该根据owner判断是否需要筛选学科 -->
-                <span class="filter-label" v-if="examInfo.owner == 'school'">{{$t('learnActivity.score.subjectLabel')}}</span>
-                <Select filterable v-model="chooseSubject" class="filter-select" @on-change="getCurPaper" v-if="examInfo.owner == 'school'" transfer>
-                    <Option v-for="(item,index) in examInfo.subjects" :value="item.id" :key="index">{{ item.name }}</Option>
-                </Select>
-                <!-- 筛选学生 -->
-                <span style="margin-left:5px" v-show="isMarkView && markType== 'byStu'">{{$t('learnActivity.score.stuLabel')}}</span>
-                <Select v-model="chooseStudent.id" label-in-value class="filter-select" style="width:140px;" @on-change="setStuInfo" v-show="isMarkView && markType== 'byStu'" transfer>
-                    <Option v-for="(item,index) in studentScore" :value="item.id" :key="index">
-                        <span class="select-status-tag" :style="{'background':getStudentStatus(item) == 1 ? '#ed4014' : getStudentStatus(item) == 2 ? '#ff9900' : '#19be6b'}"></span>
-                        {{ item.name }}
-                    </Option>
-                </Select>
-                <!-- 导出表格 -->
-                <Button size="small" @click="exportData" :loading="exportLoading" custom-icon="iconfont icon-download" type="primary" v-show="examInfo.progress != 'pending' && !isMarkView" class="export-btn">
-                    {{exportLoading ? $t('learnActivity.score.exporting') : $t('learnActivity.score.exportData')}}
-                </Button>
-                <!-- 切换打分UI -->
-                <span class="common-icon-text" @click="toggleScoreStatus">
-                    <Icon :custom="isMarkView ? 'iconfont icon-table':'iconfont icon-scoring'" size="14" />
-                    {{isMarkView ? $t('learnActivity.score.scoreView'):$t('learnActivity.score.scoring')}}
-                </span>
-                <!-- 切换打分模式 -->
-                <span class="common-icon-text" style="margin-right: 18px;" @click="toggleMarkType">
-                    <Icon type="ios-create" />
-                    {{markType== 'byQu' ? $t('learnActivity.score.byStuMark') : $t('learnActivity.score.byQuMark')}}
-                </span>
-            </div>
-            <!-- 按人批阅 -->
-            <div class="scoring-handle-box" v-show="isMarkView && markType== 'byStu'">
-                <ByStuMark @updScore="updScore" ref="byStuMark" :examInfo="examInfo" :defaultIndex="defaultIndex" :examId="examInfo.id" :owner="examInfo.owner" :examScope="examInfo.scope" :paper="paperInfo" :pStudentAnswer="chooseStudent" :subjectId="chooseSubject" @nextStu="getNextStu" style="color:#515a6e;"></ByStuMark>
-            </div>
-            <!-- 按题批阅 -->
-            <div class="scoring-handle-box" v-show="isMarkView && markType== 'byQu'">
-                <ByQuMark @updScore="updScore" :paper="paperInfo" :examInfo="examInfo" ref="byQuMark" :stusInfo="studentScore" :classId="chooseClass" :subjectId="chooseSubject"></ByQuMark>
+            <div>
+                <div class="light-iview-select light-iview-input mark-view-header">
+                    <!-- 筛选班级 -->
+                    <span>{{$t('learnActivity.score.classLabel')}}</span>
+                    <Cascader v-model="chooseTarget" :data="targetsData" :clearable="false" transfer @on-change="targetChange" style="width:200px;display:inline-block;margin-right:15px"></Cascader>
+                    <!-- 筛选学科 应该根据owner判断是否需要筛选学科 -->
+                    <span class="filter-label" v-if="examInfo.owner == 'school'">{{$t('learnActivity.score.subjectLabel')}}</span>
+                    <Select filterable v-model="chooseSubject" class="filter-select" @on-change="getCurPaper" v-if="examInfo.owner == 'school'" transfer>
+                        <Option v-for="(item,index) in examInfo.subjects" :value="item.id" :key="index">{{ item.name }}</Option>
+                    </Select>
+                    <!-- 筛选学生 -->
+                    <span style="margin-left:5px" v-show="isMarkView && markType== 'byStu'">{{$t('learnActivity.score.stuLabel')}}</span>
+                    <Select v-model="chooseStudent.id" label-in-value class="filter-select" style="width:140px;" @on-change="setStuInfo" v-show="isMarkView && markType== 'byStu'" transfer>
+                        <Option v-for="(item,index) in studentScore" :value="item.id" :key="index">
+                            <span class="select-status-tag" :style="{'background':getStudentStatus(item) == 1 ? '#ed4014' : getStudentStatus(item) == 2 ? '#ff9900' : '#19be6b'}"></span>
+                            {{ item.name }}
+                        </Option>
+                    </Select>
+                    <!-- 导出表格 -->
+                    <Button size="small" @click="exportData" :loading="exportLoading" custom-icon="iconfont icon-download" type="primary" v-show="examInfo.progress != 'pending' && !isMarkView" class="export-btn">
+                        {{exportLoading ? $t('learnActivity.score.exporting') : $t('learnActivity.score.exportData')}}
+                    </Button>
+                    <!-- 切换打分UI -->
+                    <span class="common-icon-text" @click="toggleScoreStatus">
+                        <Icon :custom="isMarkView ? 'iconfont icon-table':'iconfont icon-scoring'" size="14" />
+                        {{isMarkView ? $t('learnActivity.score.scoreView'):$t('learnActivity.score.scoring')}}
+                    </span>
+                    <!-- 切换打分模式 -->
+                    <span class="common-icon-text" style="margin-right: 18px;" @click="toggleMarkType">
+                        <Icon type="ios-create" />
+                        {{markType== 'byQu' ? $t('learnActivity.score.byStuMark') : $t('learnActivity.score.byQuMark')}}
+                    </span>
+                </div>
+                <!-- 按人批阅 -->
+                <ByStuMark v-show="isMarkView && markType== 'byStu'" @updScore="updScore" ref="byStuMark" :examInfo="examInfo" :defaultIndex="defaultIndex" :examId="examInfo.id" :owner="examInfo.owner" :examScope="examInfo.scope" :paper="paperInfo" :pStudentAnswer="chooseStudent" :subjectId="chooseSubject" @nextStu="getNextStu" style="color:#515a6e;"></ByStuMark>
+                <!-- 按题批阅 -->
+                <ByQuMark v-show="isMarkView && markType== 'byQu'" @updScore="updScore" :paper="paperInfo" :examInfo="examInfo" ref="byQuMark" :stusInfo="studentScore" :classId="chooseClass" :subjectId="chooseSubject"></ByQuMark>
             </div>
         </vuescroll>
         <!-- 题目正确率统计 -->

+ 25 - 18
TEAMModelOS/ClientApp/src/view/newcourse/MyCourse.vue

@@ -194,7 +194,7 @@
                                     <RcdPoster class="record-poster-wrap" :poster="item.poster"></RcdPoster>
                                     <div style="flex:1">
                                         <p class="record-name" style="padding-left:10px">
-                                            {{item.name}}
+                                            {{item.name}}****{{item.id}}
                                             <span class="item-icon-wrap">
                                                 <Icon type="md-create" class="common-item-icon ed-name" @click.stop="editRecordName(index)" :title="$t('cusMgt.edRdName')" />
                                                 <Icon type="md-trash" class="common-item-icon delete-item" @click.stop="delRecord(item.id)" :title="$t('cusMgt.delRcd')" />
@@ -241,6 +241,11 @@
                                                 <span>{{$t('cusMgt.duration')}}</span>
                                                 <span class="record-info-value">{{handleDuration(item.duration)}}</span>
                                             </span>
+                                            <!-- 过期时间 -->
+                                            <span class="record-info" v-if="item.expire > -1">
+                                                <span style="color:red">过期时间:</span>
+                                                <span style="color:red" class="record-info-value">{{$jsFn.timeFormat(item.expire)}}</span>
+                                            </span>
                                             <!-- 时间 -->
                                             <span class="record-info" style="float:right">
                                                 <span>
@@ -2333,22 +2338,24 @@ export default {
                 }
                 this.$api.lessonRecord.getLessonList(params).then(
                     res => {
-                        res.lessonRecords.forEach(item => {
-                            item.show = item.show ? item.show : []
-                            item.isShare = item.show.includes('student')
-                        })
-                        this.recordList = res.lessonRecords
-                        let sasInfo = {}
-                        let blobInfo = this.listType === 'school' ? this.$store.state.user.schoolProfile : this.$store.state.user.userProfile
-                        sasInfo.sas = '?' + blobInfo.blob_sas
-                        sasInfo.name = this.listType === 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId
-                        sasInfo.url = blobInfo.blob_uri.slice(0, blobInfo.blob_uri.lastIndexOf(sasInfo.name) - 1)
-                        this.recordList.forEach(item => {
-                            item.sokrateImg = `${sasInfo.url}/${sasInfo.name}/records/${item.id}/Sokrates/SokratesResults/event.png${sasInfo.sas}`
-                            item.eNote = `${sasInfo.url}/${sasInfo.name}/records/${item.id}/Note.pdf${sasInfo.sas}`
-                            item.video = `${sasInfo.url}/${sasInfo.name}/records/${item.id}/Record/CourseRecord.mp4${sasInfo.sas}`
-                            item.poster = `${sasInfo.url}/${sasInfo.name}/records/${item.id}/Record/CoverImage.jpg${sasInfo.sas}`
-                        })
+                        if (res.lessonRecords) {
+                            res.lessonRecords.forEach(item => {
+                                item.show = item.show ? item.show : []
+                                item.isShare = item.show.includes('student')
+                            })
+                            this.recordList = res.lessonRecords
+                            let sasInfo = {}
+                            let blobInfo = this.listType === 'school' ? this.$store.state.user.schoolProfile : this.$store.state.user.userProfile
+                            sasInfo.sas = '?' + blobInfo.blob_sas
+                            sasInfo.name = this.listType === 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId
+                            sasInfo.url = blobInfo.blob_uri.slice(0, blobInfo.blob_uri.lastIndexOf(sasInfo.name) - 1)
+                            this.recordList.forEach(item => {
+                                item.sokrateImg = `${sasInfo.url}/${sasInfo.name}/records/${item.id}/Sokrates/SokratesResults/event.png${sasInfo.sas}`
+                                item.eNote = `${sasInfo.url}/${sasInfo.name}/records/${item.id}/Note.pdf${sasInfo.sas}`
+                                item.video = `${sasInfo.url}/${sasInfo.name}/records/${item.id}/Record/CourseRecord.mp4${sasInfo.sas}`
+                                item.poster = `${sasInfo.url}/${sasInfo.name}/records/${item.id}/Record/CoverImage.jpg${sasInfo.sas}`
+                            })
+                        }
                     },
                     err => {
                         this.$Message.error(this.$t('cusMgt.rcdErr'))
@@ -2443,7 +2450,7 @@ export default {
             schoolBase: 'user/getSchoolBase', // 学校基础设置
             periods: 'user/getPeriods',
             lessonShow: 'user/getTeacherLessonShow',//是否自动发布课堂记录
-            teachClasses:'getTeachClasses'
+            teachClasses: 'getTeachClasses'
         }),
         //查询成绩表所需参数(courseId, cId, code)
         paramsInfo() {

+ 153 - 116
TEAMModelOS/ClientApp/src/view/research-center/BaseCleanCond.vue

@@ -1,124 +1,161 @@
 <template>
-	<div class="base-clean-container">
-		<p style="font-weight: bold;color: #189df0;font-size: 14px;margin-bottom: 10px;"><Icon type="md-information-circle" /> 自动清除时间设置</p>
-		<div class="clean-item">
-			<span style="font-weight: bold;margin-right: 20px;">课堂记录保留期限</span>
-			<InputNumber :formatter="value => `${parseInt(Math.floor(value*100)/100)}`" :max="30" :min="1" :step="1" v-model="expireDays"></InputNumber>
-			<span style="margin-left: 10px;">天</span>
-		</div>
-		<p style="font-weight: bold;color: #F06431;font-size: 14px;margin-bottom: 10px;"><Icon type="md-information-circle" /> 符合以下条件的课堂记录,将会进行保留,不会被系统自动清除</p>
-		<div class="clean-item" v-for="(item,index) in condArr">
-			<span class="clean-item-name">{{ item.name }}</span>
-			<Select v-model="item.type" style="width:120px;margin: 0 20px;">
-				<Option value=">=">大于等于</Option>
-				<Option value="<=">小于等于</Option>
-			</Select>
-			<InputNumber :formatter="value => `${parseInt(Math.floor(value*100)/100)}`" :max="item.unit === 'rate' ? 100 : 10000" :min="0" :step="1" v-model="item.value"></InputNumber>
-			<span style="margin-left: 10px;">{{ item.unit === 'rate' ? '%' : item.unit === 'point' ? '分' : '个' }}</span>
-		</div>
-	</div>
+  <div class="base-clean-container">
+    <div style="display:flex;align-items:center">
+      <span style="font-weight: bold;font-size: 14px;">
+        自定义设置
+      </span>
+      <i-switch size="large" v-model="openAutoClean" style="margin-left:10px">
+        <span slot="open">开启</span>
+        <span slot="close">关闭</span>
+      </i-switch>
+      <Tooltip content="默认课堂记录仅保留7天(被收藏的课例除外),若开启自定义设置,则需要您设置保留期限以及保留条件" :max-width="300" theme="light" placement="right" style="margin-left: 10px">
+        <Icon type="md-information-circle" color="#888" size="16"/>
+      </Tooltip>
+    </div>
+    <div v-if="openAutoClean" style="margin-top:20px;">
+      <p style="font-weight: bold;color: #189df0;font-size: 14px;margin-bottom: 10px;">
+        <Icon type="md-information-circle" /> 课例保留时间设置
+      </p>
+      <div class="clean-item">
+        <span style="font-weight: bold;margin-right: 20px;">课堂记录保留期限</span>
+        <InputNumber :formatter="val => `${parseInt(Math.floor(val*100)/100)}`" :max="30" :min="1" :step="1" v-model="expireDays"></InputNumber>
+        <span style="margin-left: 10px;">天</span>
+      </div>
+      <p style="font-weight: bold;color: #F06431;font-size: 14px;margin-bottom: 10px;">
+        <Icon type="md-information-circle" /> 符合以下条件的课堂记录,将会进行保留,不会被系统自动清除
+      </p>
+      <div class="clean-item" v-for="(item,index) in condArr">
+        <span class="clean-item-name">{{ item.name }}</span>
+        <Select v-model="item.type" style="width:120px;margin: 0 20px;">
+          <Option value=">=">大于等于</Option>
+          <Option value="<=">小于等于</Option>
+        </Select>
+        <InputNumber :formatter="val => `${parseInt(Math.floor(val*100)/100)}`" :max="item.unit === 'rate' ? 100 : 10000" :min="0" :step="1" v-model="item.val"></InputNumber>
+        <span style="margin-left: 10px;">{{ item.unit === 'rate' ? '%' : item.unit === 'point' ? '分' : '个' }}</span>
+      </div>
+    </div>
+
+  </div>
 </template>
 
 <script>
-	export default {
-		data(){
-			return {
-				expireDays:7,
-				condArr:[
-					{
-						name:this.$t('lessonRecord.attendCount'),
-						key:'attendRate',
-						value:0, 
-						unit:'rate', 
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.groupCount'),
-						key:'groupCount',
-						value:0, 
-						unit:'count',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.totalPoint'),
-						key:'totalPoint',
-						value:0, 
-						unit:'point',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.collateTaskCount'),
-						key:'collateTaskCount',
-						value:0, 
-						unit:'count',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.collateCount'),
-						key:'collateCount',
-						value:0, 
-						unit:'count',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.pushCount'),
-						key:'pushCount',
-						value:0, 
-						unit:'count',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.score'),
-						key:'score',
-						value:0, 
-						unit:'point',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.interactionCount'),
-						key:'interactionCount',
-						value:0, 
-						unit:'count',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.clientInteractionCount'),
-						key:'clientInteractionCount',
-						value:0, 
-						unit:'count',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.examQuizCount'),
-						key:'examQuizCount',
-						value:0, 
-						unit:'count',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.examPointRate'),
-						key:'examPointRate',
-						value:0, 
-						unit:'rate',
-						type:'>='
-					}
-				]
-			}
-		}
-	}
+export default {
+  data() {
+    return {
+      openAutoClean: false,
+      expireDays: 7,
+      condArr: [
+        {
+          name: this.$t('lessonRecord.attendCount'),
+          key: 'attendRate',
+          val: 0,
+          unit: 'rate',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.groupCount'),
+          key: 'groupCount',
+          val: 0,
+          unit: 'count',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.totalPoint'),
+          key: 'totalPoint',
+          val: 0,
+          unit: 'point',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.collateTaskCount'),
+          key: 'collateTaskCount',
+          val: 0,
+          unit: 'count',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.collateCount'),
+          key: 'collateCount',
+          val: 0,
+          unit: 'count',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.pushCount'),
+          key: 'pushCount',
+          val: 0,
+          unit: 'count',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.score'),
+          key: 'score',
+          val: 0,
+          unit: 'point',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.interactionCount'),
+          key: 'interactionCount',
+          val: 0,
+          unit: 'count',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.clientInteractionCount'),
+          key: 'clientInteractionCount',
+          val: 0,
+          unit: 'count',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.examQuizCount'),
+          key: 'examQuizCount',
+          val: 0,
+          unit: 'count',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.examPointRate'),
+          key: 'examPointRate',
+          val: 0,
+          unit: 'rate',
+          type: '>='
+        }
+      ]
+    }
+  },
+  created() {
+    this.$api.lessonRecord.findLessonSettings({
+      schoolId: this.$store.state.userInfo.schoolCode
+    }).then(res => {
+      if (!res.error) {
+        this.openAutoClean = res.setting.lessonSetting.openAutoClean === 1
+        this.expireDays = res.setting.lessonSetting.expireDays
+        this.condArr.map(item => {
+          let findItem = res.setting.lessonSetting.conds.find(i => i.key === item.key)
+          if (findItem) {
+            item.val = findItem.val
+            item.type = findItem.type
+          }
+        })
+      }
+    })
+  }
+}
 </script>
 
 <style lang="less">
-	.base-clean-container{
-		.clean-item{
-			margin: 15px 0;
-			display: flex;
-			align-items: center;
-			&-name{
-				display: inline-block;
-				font-weight: bold;
-				width: 80px;
-			}
-		}
-	}
+.base-clean-container {
+  .clean-item {
+    margin: 15px 0;
+    display: flex;
+    align-items: center;
+    &-name {
+      display: inline-block;
+      font-weight: bold;
+      width: 80px;
+    }
+  }
+}
 </style>

File diff suppressed because it is too large
+ 814 - 802
TEAMModelOS/ClientApp/src/view/research-center/ResearchMgt.vue


+ 224 - 223
TEAMModelOS/ClientApp/src/view/settings/Index.vue

@@ -1,26 +1,26 @@
 <template>
-    <div class="settings-container">
-        <div class="settings-header">
-            <span :class="['settings-header-item',activeTab === '1' ?  'active-item' : '']" @click="onTabChange('1')">{{ $t('settings.setting_title1')}}</span>
-            <span :class="['settings-header-item',activeTab === '0' ?  'active-item' : '']" @click="onTabChange('0')">{{ $t('settings.setting_title2')}}</span>
-            <!-- <span :class="['settings-header-item',activeTab === '2' ?  'active-item' : '']" @click="onTabChange('2')">{{ $t('settings.setting_title3')}}</span> -->
-        </div>
+  <div class="settings-container">
+    <div class="settings-header">
+      <span :class="['settings-header-item',activeTab === '1' ?  'active-item' : '']" @click="onTabChange('1')">{{ $t('settings.setting_title1')}}</span>
+      <span :class="['settings-header-item',activeTab === '0' ?  'active-item' : '']" @click="onTabChange('0')">{{ $t('settings.setting_title2')}}</span>
+      <!-- <span :class="['settings-header-item',activeTab === '2' ?  'active-item' : '']" @click="onTabChange('2')">{{ $t('settings.setting_title3')}}</span> -->
+    </div>
 
-        <div class="settings-body">
-            <div class="normal-settings animated fadeIn" v-if="activeTab === '0'">
-                <div class="normal-settings-item">
-                    <span class="item-title">{{ $t('settings.langSetting')}}</span>
-                    <span class="item-description">{{ $t('settings.langTips')}}</span>
-                    <span class="item-content light-iview-select">
-                        <Select v-model="cloudSetting.curLang" style="width:200px" @on-change="onSelectLang">
-                            <Option value="zh-cn">中文(简体)</Option>
-                            <Option value="zh-tw">中文(繁體)</Option>
-                            <Option value="en-us">English</Option>
-                        </Select>
-                        <Checkbox v-model="cloudSetting.isSystemLang" @on-change="onMenuStatusChange">{{ $t('settings.langCheck')}}</Checkbox>
-                    </span>
-                </div>
-                <!-- <div class="normal-settings-item">
+    <div class="settings-body">
+      <div class="normal-settings animated fadeIn" v-if="activeTab === '0'">
+        <div class="normal-settings-item">
+          <span class="item-title">{{ $t('settings.langSetting')}}</span>
+          <span class="item-description">{{ $t('settings.langTips')}}</span>
+          <span class="item-content light-iview-select">
+            <Select v-model="cloudSetting.curLang" style="width:200px" @on-change="onSelectLang">
+              <Option value="zh-cn">中文(简体)</Option>
+              <Option value="zh-tw">中文(繁體)</Option>
+              <Option value="en-us">English</Option>
+            </Select>
+            <Checkbox v-model="cloudSetting.isSystemLang" @on-change="onMenuStatusChange">{{ $t('settings.langCheck')}}</Checkbox>
+          </span>
+        </div>
+        <!-- <div class="normal-settings-item">
                     <span class="item-title">{{ $t('settings.themeSetting')}}</span>
                     <span class="item-description">{{ $t('settings.themeTips')}}</span>
                     <span class="item-content">
@@ -28,36 +28,36 @@
                         <span :class="['color-item',cloudSetting.curTheme === 'light' ?  'color-item-active' : '']" @click="onTips('light')"></span>
                     </span>
                 </div> -->
-                <div class="normal-settings-item">
-                    <span class="item-title">{{ $t('settings.menuSetting')}}</span>
-                    <span class="item-description">{{ $t('settings.menuTips')}}</span>
-                    <span class="item-content">
-                        <RadioGroup v-model="cloudSetting.menuStatus" @on-change="onMenuStatusChange">
-                            <Radio label="open">{{ $t('settings.menuOpen')}}</Radio>
-                            <Radio label="close">{{ $t('settings.menuClose')}}</Radio>
-                        </RadioGroup>
-                    </span>
-                </div>
-                <div class="normal-settings-item">
-                    <span class="item-title">{{ $t('settings.logoSetting')}}</span>
-                    <span class="item-description">{{ $t('settings.logoTips')}}</span>
-                    <span class="item-content">
-                        <RadioGroup v-model="cloudSetting.logoStatus" @on-change="onLogoStatusChange">
-                            <Radio label="open">{{ $t('settings.logoOpen')}}</Radio>
-                            <Radio label="close">{{ $t('settings.logoHide')}}</Radio>
-                        </RadioGroup>
-                    </span>
-                </div>
-                <div class="normal-settings-item">
-                    <!-- <Button @click="saveSetting">保存变更</Button> -->
-                </div>
-            </div>
-            <SchoolMgmt v-if="activeTab === '1'" class="animated fadeIn"></SchoolMgmt>
-            <!-- <OpenMgmt v-if="activeTab === '2'" class="animated fadeIn"></OpenMgmt> -->
-            <!-- 换成独立菜单了 -->
-            <!-- <OpenMgmt2 v-if="activeTab === '2'" class="animated fadeIn"></OpenMgmt2> -->
+        <div class="normal-settings-item">
+          <span class="item-title">{{ $t('settings.menuSetting')}}</span>
+          <span class="item-description">{{ $t('settings.menuTips')}}</span>
+          <span class="item-content">
+            <RadioGroup v-model="cloudSetting.menuStatus" @on-change="onMenuStatusChange">
+              <Radio label="open">{{ $t('settings.menuOpen')}}</Radio>
+              <Radio label="close">{{ $t('settings.menuClose')}}</Radio>
+            </RadioGroup>
+          </span>
+        </div>
+        <div class="normal-settings-item">
+          <span class="item-title">{{ $t('settings.logoSetting')}}</span>
+          <span class="item-description">{{ $t('settings.logoTips')}}</span>
+          <span class="item-content">
+            <RadioGroup v-model="cloudSetting.logoStatus" @on-change="onLogoStatusChange">
+              <Radio label="open">{{ $t('settings.logoOpen')}}</Radio>
+              <Radio label="close">{{ $t('settings.logoHide')}}</Radio>
+            </RadioGroup>
+          </span>
         </div>
+        <div class="normal-settings-item">
+          <!-- <Button @click="saveSetting">保存变更</Button> -->
+        </div>
+      </div>
+      <SchoolMgmt v-if="activeTab === '1'" class="animated fadeIn"></SchoolMgmt>
+      <!-- <OpenMgmt v-if="activeTab === '2'" class="animated fadeIn"></OpenMgmt> -->
+      <!-- 换成独立菜单了 -->
+      <!-- <OpenMgmt2 v-if="activeTab === '2'" class="animated fadeIn"></OpenMgmt2> -->
     </div>
+  </div>
 </template>
 
 <script>
@@ -66,121 +66,122 @@ import SchoolMgmt from './SchoolMgmt.vue'
 import OpenMgmt from './OpenMgmt.vue'
 // import OpenMgmt2 from './OpenMgmt2.vue';
 export default {
-    components: {
-        SchoolMgmt,
-        OpenMgmt,
-        // OpenMgmt2
-    },
-    data() {
-        return {
-            activeTab: '1',
-            activeTheme: '0',
-            menuStatus: 'open',
-            curLang: 'zh-cn',
-            isHomeworkLang: true,
-            cloudSetting: {
-                curLang: localStorage.getItem('local'),
-                curTheme: 'dark',
-                isSystemLang: true,
-                menuStatus: 'open',
-                logoStatus: 'open'
-            }
-        }
-    },
-    created() {
-        if (localStorage.getItem('cloudSetting')) {
-            this.cloudSetting = JSON.parse(localStorage.getItem('cloudSetting'))
-        }
-		
-    },
-    methods: {
-
-        onSelectLang(val) {
-            localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
-            this.$store.commit("setLanguage", val);
-        },
+  components: {
+    SchoolMgmt,
+    OpenMgmt,
+    // OpenMgmt2
+  },
+  data() {
+    return {
+      activeTab: '1',
+      activeTheme: '0',
+      menuStatus: 'open',
+      curLang: 'zh-cn',
+      isHomeworkLang: true,
+      cloudSetting: {
+        curLang: localStorage.getItem('local'),
+        curTheme: 'dark',
+        isSystemLang: true,
+        menuStatus: 'open',
+        logoStatus: 'open'
+      }
+    }
+  },
+  created() {
+    if (localStorage.getItem('cloudSetting')) {
+      this.cloudSetting = JSON.parse(localStorage.getItem('cloudSetting'))
+    }
 
-        onMenuStatusChange(val) {
-            localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
-        },
+  },
+  methods: {
 
-        onLogoStatusChange(val) {
-            localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
-            this.$EventBus.$emit('onLogoStatusChange', val)
-        },
+    onSelectLang(val) {
+      localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
+      this.$tools.changeLang(val)
+      this.$store.commit("setLanguage", val);
+    },
 
-        /* 一般设置与学校管理切换 */
-        onTabChange(index) {
-            this.activeTab = index
-        },
+    onMenuStatusChange(val) {
+      localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
+    },
 
-        /* 主题切换 */
-        onThemeChange(index) {
-            this.cloudSetting.curTheme = index
-            //this.$less.modifyVars({  // 调用 `less.modifyVars` 方法来改变变量值'
-            //    '@border-color': '#000',
-            //    '@header-bg': 'white',
-            //})
-            //    .then(() => {
-            //        console.log('修改成功');
-            //    });
-            localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
-            if (index == 'dark') {
-                let url = '/theme/dark-theme.css'
-                var link = document.getElementById('theme') ? document.getElementById('theme') : document.createElement('link');
-                if (document.getElementById('theme')) {
-                    link.href = url;
-                } else {
-                    var head = document.getElementsByTagName('head')[0];
-                    link.type = 'text/css';
-                    link.rel = 'stylesheet';
-                    link.id = 'theme';
-                    link.href = url;
-                    head.appendChild(link);
-                }
-            } else {
-                let url = '/theme/light-theme.css'
-                var link = document.getElementById('theme') ? document.getElementById('theme') : document.createElement('link');
-                if (document.getElementById('theme')) {
-                    link.href = url;
-                } else {
-                    var head = document.getElementsByTagName('head')[0];
-                    link.type = 'text/css';
-                    link.rel = 'stylesheet';
-                    link.id = 'theme';
-                    link.href = url;
-                    head.appendChild(link);
-                }
-            }
-        },
+    onLogoStatusChange(val) {
+      localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
+      this.$EventBus.$emit('onLogoStatusChange', val)
+    },
 
-        onTips() {
-            this.$Message.warning('换肤功能即将上线')
-        },
+    /* 一般设置与学校管理切换 */
+    onTabChange(index) {
+      this.activeTab = index
+    },
 
-        /* 保存设置 */
-        saveSetting() {
-            this.$Modal.confirm({
-                title: '提示',
-                content: '设置保存成功,刷新后即可生效,是否前往?',
-                onOk: () => {
-                    localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
-                    location.reload()
-                }
-            })
+    /* 主题切换 */
+    onThemeChange(index) {
+      this.cloudSetting.curTheme = index
+      //this.$less.modifyVars({  // 调用 `less.modifyVars` 方法来改变变量值'
+      //    '@border-color': '#000',
+      //    '@header-bg': 'white',
+      //})
+      //    .then(() => {
+      //        console.log('修改成功');
+      //    });
+      localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
+      if (index == 'dark') {
+        let url = '/theme/dark-theme.css'
+        var link = document.getElementById('theme') ? document.getElementById('theme') : document.createElement('link');
+        if (document.getElementById('theme')) {
+          link.href = url;
+        } else {
+          var head = document.getElementsByTagName('head')[0];
+          link.type = 'text/css';
+          link.rel = 'stylesheet';
+          link.id = 'theme';
+          link.href = url;
+          head.appendChild(link);
         }
-    },
-    watch: {
-        $route: {
-            handler: function (val, oldVal) {
-                let routeData = this.$route.query
-                this.activeTab = routeData.tab || this.activeTab
-            },
-            // 深度观察监听
-            deep: true,
-            immediate: true
+      } else {
+        let url = '/theme/light-theme.css'
+        var link = document.getElementById('theme') ? document.getElementById('theme') : document.createElement('link');
+        if (document.getElementById('theme')) {
+          link.href = url;
+        } else {
+          var head = document.getElementsByTagName('head')[0];
+          link.type = 'text/css';
+          link.rel = 'stylesheet';
+          link.id = 'theme';
+          link.href = url;
+          head.appendChild(link);
         }
+      }
     },
+
+    onTips() {
+      this.$Message.warning('换肤功能即将上线')
+    },
+
+    /* 保存设置 */
+    saveSetting() {
+      this.$Modal.confirm({
+        title: '提示',
+        content: '设置保存成功,刷新后即可生效,是否前往?',
+        onOk: () => {
+          localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
+          location.reload()
+        }
+      })
+    }
+  },
+  watch: {
+    $route: {
+      handler: function (val, oldVal) {
+        let routeData = this.$route.query
+        this.activeTab = routeData.tab || this.activeTab
+      },
+      // 深度观察监听
+      deep: true,
+      immediate: true
+    }
+  },
 }
 </script>
 
@@ -190,85 +191,85 @@ export default {
 
 <style lang="less">
 .settings-container {
-    .ivu-select-selection {
-        /* background: transparent;
+  .ivu-select-selection {
+    /* background: transparent;
         border-color: #363738;
         color: #a6a6a6; */
-    }
+  }
 
-    .ivu-checkbox-wrapper {
-        margin-left: 40px;
-        margin-top: 5px;
-        // color: #a6a6a6;
-    }
+  .ivu-checkbox-wrapper {
+    margin-left: 40px;
+    margin-top: 5px;
+    // color: #a6a6a6;
+  }
 
-    .ivu-checkbox {
-        margin-right: 5px;
-    }
+  .ivu-checkbox {
+    margin-right: 5px;
+  }
 
-    .ivu-radio-wrapper {
-        margin-right: 20px;
-        // color: #a5a5a5;
-    }
+  .ivu-radio-wrapper {
+    margin-right: 20px;
+    // color: #a5a5a5;
+  }
 
-    .ivu-radio-inner {
-        width: 18px;
-        height: 18px;
-        border-radius: 4px;
-        /* background-color: #0f0f0f;
+  .ivu-radio-inner {
+    width: 18px;
+    height: 18px;
+    border-radius: 4px;
+    /* background-color: #0f0f0f;
         border-color: #4d4d4d; */
-        border-width: 1px;
-        margin-right: 5px;
-        &::after {
-            content: "";
-            display: block;
-            width: 10px;
-            height: 16px;
-            border-right: #ffffff solid 3px;
-            border-bottom: #ffffff solid 3px;
-            transform: rotate(35deg);
-            position: absolute;
-            top: -4px;
-            left: 4px;
-            border-radius: 0;
-            background-color: transparent;
-            border-color: #0094ff;
-        }
+    border-width: 1px;
+    margin-right: 5px;
+    &::after {
+      content: "";
+      display: block;
+      width: 10px;
+      height: 16px;
+      border-right: #ffffff solid 3px;
+      border-bottom: #ffffff solid 3px;
+      transform: rotate(35deg);
+      position: absolute;
+      top: -4px;
+      left: 4px;
+      border-radius: 0;
+      background-color: transparent;
+      border-color: #0094ff;
     }
+  }
 
-    .ivu-checkbox-inner {
-        width: 18px;
-        height: 18px;
-        border-radius: 4px;
-        /* background-color: #0f0f0f;
+  .ivu-checkbox-inner {
+    width: 18px;
+    height: 18px;
+    border-radius: 4px;
+    /* background-color: #0f0f0f;
         border-color: #4d4d4d; */
-        border-width: 1px;
-        margin-right: 5px;
-    }
+    border-width: 1px;
+    margin-right: 5px;
+  }
 
-    .ivu-checkbox-checked .ivu-checkbox-inner {
-        width: 18px;
-        height: 18px;
-        border-radius: 4px;
-        background-color: #FFF;
-        /* border-color: #4d4d4d; */
-        border-width: 1px;
-        margin-right: 5px;
-        &::after {
-            content: "";
-            display: block;
-            width: 10px;
-            height: 16px;
-            border-right: #ffffff solid 3px;
-            border-bottom: #ffffff solid 3px;
-            transform: rotate(35deg);
-            position: absolute;
-            top: -4px;
-            left: 4px;
-            border-radius: 0;
-            background-color: transparent;
-            border-color: #0094ff;
-        }
+  .ivu-checkbox-checked .ivu-checkbox-inner {
+    width: 18px;
+    height: 18px;
+    border-radius: 4px;
+    background-color: #fff;
+    /* border-color: #4d4d4d; */
+    border-width: 1px;
+    margin-right: 5px;
+    &::after {
+      content: "";
+      display: block;
+      width: 10px;
+      height: 16px;
+      border-right: #ffffff solid 3px;
+      border-bottom: #ffffff solid 3px;
+      transform: rotate(35deg);
+      position: absolute;
+      top: -4px;
+      left: 4px;
+      border-radius: 0;
+      background-color: transparent;
+      border-color: #0094ff;
     }
+  }
 }
 </style>

+ 2 - 1
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/EvaluationList/TotalIndex.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="index-container">
     <Loading v-show="isLoadingList"></Loading>
-    <Scroll :on-reach-bottom="handleReachBottom" height="880" :distance-to-edge="[0,0]">
+    <Scroll :on-reach-bottom="handleReachBottom" :height="scrollHeight" :distance-to-edge="[0,0]">
       <div class="section" id="analysisList">
         <!-- 评测列表数据部分 -->
         <div class="section exam-list-wrap">
@@ -155,6 +155,7 @@ export default {
   },
   data(vm) {
     return {
+      scrollHeight:document.documentElement.clientHeight * 0.95,
       filterResultCount: 0,
       typeCountArr: [],
       latestYear: null,

+ 17 - 3
TEAMModelOS/ClientApp/src/view/task/mark/ByStu.vue

@@ -109,6 +109,12 @@
                 <Button type="success" class="submit-score" @click="submit()">
                     {{$t('learnActivity.mark.submit')}}
                 </Button>
+                <div class="mark-set-wrap">
+                    <span>
+                        自动检测是否完成打分:
+                    </span>
+                    <i-switch v-model="isCheck" class="switch-set"/>
+                </div>
             </div>
         </div>
         <!-- 完成阅卷的提示信息 -->
@@ -167,6 +173,7 @@ export default {
     },
     data() {
         return {
+            isCheck: true,
             originalStatus: false,
             sourceList: [],
             errText: '',
@@ -180,7 +187,6 @@ export default {
             ansImg: '',
             imgs: [],
             autoQu: true,//自动切换下一题
-            autoStu: true,//自动获取下一学生
             toggleStatus: false,
             activeIcon: -1,
             isShowNum: true,
@@ -405,9 +411,8 @@ export default {
                 })
             }
 
-            console.log('ssss', s)
             if (!s.includes(-1)) {
-                if (this.autoStu) {
+                if (this.isCheck) {
                     this.isComplete = true
                 } else {
                     this.$Message.warning(this.$t('learnActivity.mark.completeStu'))
@@ -491,6 +496,7 @@ export default {
                         }
                     }
                 }
+                this.score = this.stuScore[this.quIndex]
             }
             // 随机获取一名学生
             else {
@@ -671,6 +677,14 @@ export default {
 }
 </script>
 <style scoped lang="less">
+.switch-set{
+    float:right;
+    margin-top: 2px;
+}
+.mark-set-wrap{
+    margin-top: 15px;
+    padding: 0px 5px;
+}
 .objective-tips {
     font-size: 12px;
     color: #ff9900;

+ 15 - 8
TEAMModelOS/ClientApp/src/view/user/BandPhone.vue

@@ -189,20 +189,27 @@ export default {
                         this.$Message.error(this.$t('login.codeerr'))
                         return
                     }
+                    //20220509调整:已有账号和新建账号都只传手机,由后端通过手机获取token进行绑定
                     //已有账号直接绑定,通过bind进行隐式登录
-                    else if (res.error === 2) {
+                    else if (res.error === 2 || res.id_token) {
                         data = {
                             phone: this.accFormat
                         }
+                        this.bandingID(data)
+                    }else{
+                        this.$Message.error(this.$t('login.phoneerr'))
                     }
                     // 新账号会返回登录信息
-                    else if (res.id_token) {
-                        data = {
-                            id_token: res.id_token,
-                            loginData: res
-                        }
-                    }
-                    this.bandingID(data)
+                    // else if (res.id_token) {
+                    //     // data = {
+                    //     //     id_token: res.id_token,
+                    //     //     loginData: res
+                    //     // }
+                    //     data = {
+                    //         phone: this.accFormat
+                    //     }
+                    // }
+                    // this.bandingID(data)
                 },
                 err => {
                     this.$Message.error(this.$t('login.phoneerr'))

+ 3 - 1
TEAMModelOS/Controllers/Analysis/AnalysisController.cs

@@ -169,7 +169,9 @@ namespace TEAMModelOS.Controllers.Analysis
 
                     }
                 }
-               
+                if (examClassResults.Count ==0) {
+                    return Ok(new { code = 404 ,msg = "数据还在结算中!"});
+                }
                 touch = info.touch;
                 income = info.income;
                 /*foreach (Period period in school.period)

+ 15 - 1
TEAMModelOS/Controllers/Client/HiScanController.cs

@@ -30,6 +30,7 @@ using Azure.Storage.Sas;
 using Lib.AspNetCore.ServerSentEvents;
 using TEAMModelOS.SDK.Models.Cosmos.Common;
 using HTEXLib.COMM.Helpers;
+using TEAMModelOS.SDK.Models.Service;
 
 namespace TEAMModelOS.Controllers.Core
 {
@@ -49,8 +50,10 @@ namespace TEAMModelOS.Controllers.Core
         private readonly AzureStorageFactory _azureStorage;
         // private readonly ServerSentEventsService _sse;
         private readonly CoreAPIHttpService _coreAPIHttpService;
+        private readonly IPSearcher _searcher;
+
         public HiScanController(CoreAPIHttpService coreAPIHttpService, AzureCosmosFactory azureCosmos, AzureServiceBusFactory serviceBus, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option,
-           AzureRedisFactory azureRedis, AzureStorageFactory azureStorage//, ServerSentEventsService sse
+           AzureRedisFactory azureRedis, AzureStorageFactory azureStorage, IPSearcher searcher//, ServerSentEventsService sse
                                                                          )
         {
             _azureCosmos = azureCosmos;
@@ -61,6 +64,7 @@ namespace TEAMModelOS.Controllers.Core
             _azureRedis = azureRedis;
             _coreAPIHttpService = coreAPIHttpService;
             _azureStorage = azureStorage;
+            _searcher = searcher;
             //_sse = sse;
         }
 
@@ -78,6 +82,7 @@ namespace TEAMModelOS.Controllers.Core
         public async Task<IActionResult> GetSchoolinfo(JsonElement request)
         {
             List<dynamic> schools = new List<dynamic>();
+            (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
             if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
             var client = _azureCosmos.GetCosmosClient();
             try
@@ -114,6 +119,15 @@ namespace TEAMModelOS.Controllers.Core
                         catch { continue; }
                     }
                 }
+                
+                response.loginInfos = await LoginService.DoLoginInfo(response.loginInfos, "", Constant.ScopeTeacher, $"{id}", ip, _azureRedis, _azureStorage, client, expire: 1);
+                try
+                {
+                    await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(response, $"{id}", new PartitionKey("Base"));
+                }
+                catch
+                {
+                }
                 var (tblob_uri, tblob_sas) = _azureStorage.GetBlobContainerSAS($"{id}", BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete);
                 return Ok(new { schools, teacher = new { name, picture, id, bloburl = tblob_uri, blobsas = tblob_sas } });
             }

+ 15 - 1
TEAMModelOS/Controllers/Client/HiTAControlller.cs

@@ -15,6 +15,7 @@ using System.Linq;
 using System.Text.Json;
 using System.Threading.Tasks;
 using TEAMModelOS.Models;
+using TEAMModelOS.SDK;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models;
@@ -36,13 +37,14 @@ namespace TEAMModelOS.Controllers.Client
         private readonly SnowflakeId _snowflakeId;
         private readonly IConfiguration _configuration;
         private readonly NotificationService _notificationService;
+        private readonly IPSearcher _searcher;
         public HiTAControlller(
             AzureStorageFactory azureStorage,
             AzureRedisFactory azureRedis,
             AzureCosmosFactory azureCosmos,
             DingDing dingDing,
             SnowflakeId snowflakeId,
-            IOptionsSnapshot<Option> option, IConfiguration configuration, NotificationService notificationService)
+            IOptionsSnapshot<Option> option, IConfiguration configuration, NotificationService notificationService, IPSearcher searcher)
         {
             _azureStorage = azureStorage;
             _azureRedis = azureRedis;
@@ -52,6 +54,7 @@ namespace TEAMModelOS.Controllers.Client
             _option = option?.Value;
             _configuration = configuration;
             _notificationService = notificationService;
+            _searcher = searcher;
         }
 
         public class HiTAJoinSchool
@@ -219,6 +222,7 @@ namespace TEAMModelOS.Controllers.Client
             try
             {
                 string id_token = HttpContext.GetXAuth("IdToken");
+                (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
                 if (string.IsNullOrEmpty(id_token)) return BadRequest();
                 var jwt = new JwtSecurityToken(id_token);
                 if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
@@ -462,6 +466,16 @@ namespace TEAMModelOS.Controllers.Client
                     //    }
                     //}
 
+                    Teacher teacher = jsonsc.ToObject<Teacher>();
+                    teacher.loginInfos = await LoginService.DoLoginInfo(teacher.loginInfos, defaultschool, Constant.ScopeTeacher, id, ip, _azureRedis, _azureStorage, clientc, expire: 1);
+                    try
+                    {
+                        await clientc.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
+                    }
+                    catch
+                    {
+                    }
+
                     return Ok(new { schools, defaultschool, courses, size, resCount, itemCount, paperCount, activityCount });
                 }
                 else //無此老師

+ 27 - 1
TEAMModelOS/Controllers/Client/HiTeachController.cs

@@ -28,6 +28,7 @@ using TEAMModelOS.SDK;
 using Microsoft.Extensions.Configuration;
 using Azure.Messaging.ServiceBus;
 using TEAMModelOS.SDK.Services;
+using TEAMModelOS.SDK.Models.Service;
 
 namespace TEAMModelOS.Controllers.Client
 {
@@ -48,6 +49,7 @@ namespace TEAMModelOS.Controllers.Client
         private readonly AzureServiceBusFactory _serviceBus;
         private readonly IConfiguration _configuration;
         private readonly CoreAPIHttpService _coreAPIHttpService;
+        private readonly IPSearcher _searcher;
 
         public HiTeachController(
             AzureStorageFactory azureStorage,
@@ -58,7 +60,7 @@ namespace TEAMModelOS.Controllers.Client
             IOptionsSnapshot<Option> option,
             AzureServiceBusFactory serviceBus,
             IConfiguration configuration,
-            CoreAPIHttpService coreAPIHttpService)
+            CoreAPIHttpService coreAPIHttpService, IPSearcher searcher)
         {
             _azureStorage = azureStorage;
             _azureRedis = azureRedis;
@@ -69,6 +71,7 @@ namespace TEAMModelOS.Controllers.Client
             _serviceBus = serviceBus;
             _configuration = configuration;
             _coreAPIHttpService = coreAPIHttpService;
+            _searcher = searcher;
         }
         /// <summary>
         /// 更新课堂记录
@@ -205,6 +208,7 @@ namespace TEAMModelOS.Controllers.Client
             try
             {
                 string id_token = HttpContext.GetXAuth("IdToken");
+                (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
                 if (string.IsNullOrEmpty(id_token)) return BadRequest();
                 var jwt = new JwtSecurityToken(id_token);
                 if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
@@ -243,6 +247,16 @@ namespace TEAMModelOS.Controllers.Client
                     {
                         defaultschool = valueD.ToString();
                     }
+
+                    Teacher teacher = jsonsc.ToObject<Teacher>();
+                    teacher.loginInfos = await LoginService.DoLoginInfo(teacher.loginInfos, defaultschool, Constant.ScopeTeacher, id, ip, _azureRedis, _azureStorage, client, expire: 1);
+                    try
+                    {
+                        await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
+                    }
+                    catch
+                    {
+                    }
                 }
                 else
                 {
@@ -260,6 +274,7 @@ namespace TEAMModelOS.Controllers.Client
                         defaultSchool = null,
                         schools = new List<Teacher.TeacherSchool>(),
                     };
+                    teacher.loginInfos = await LoginService.DoLoginInfo(teacher.loginInfos, "", Constant.ScopeTeacher, id, ip, _azureRedis, _azureStorage, client, expire: 1);
                     teacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").CreateItemAsync<Teacher>(teacher, new PartitionKey("Base"));
                 }
 
@@ -426,6 +441,7 @@ namespace TEAMModelOS.Controllers.Client
             try
             {
                 string id_token = HttpContext.GetXAuth("IdToken");
+                (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
                 if (string.IsNullOrEmpty(id_token)) return BadRequest();
                 if (!requert.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
                 var jwt = new JwtSecurityToken(id_token);
@@ -720,6 +736,16 @@ namespace TEAMModelOS.Controllers.Client
                     }
                 }
 
+                try
+                {
+                    Teacher teacher = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<Teacher>($"{id}", new PartitionKey("Base"));
+                    teacher.loginInfos = await LoginService.DoLoginInfo(teacher.loginInfos, school_code.GetString(), Constant.ScopeTeacher, id, ip, _azureRedis, _azureStorage, client, expire: 1);
+                    await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
+                }
+                catch
+                {
+                }
+
                 //取得School Blob 容器位置及SAS
                 string school_code_blob = school_code.GetString().ToLower();
                 var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Write); //讀列

+ 1 - 1
TEAMModelOS/Controllers/Common/ExamController.cs

@@ -689,7 +689,7 @@ namespace TEAMModelOS.Controllers
                         }
                     }                   
                 }
-                var groups = exams.Where(e => !string.IsNullOrEmpty(e.examType.id)).GroupBy(x =>x.examType).Select(y => new { key = y.Key.name,count = y.ToList().Count}).ToList();
+                var groups = exams.Where(e => null != e.examType && !string.IsNullOrWhiteSpace(e.examType.id)).GroupBy(x =>x.examType).Select(y => new { key = y.Key.name,count = y.ToList().Count}).ToList();
                 return Ok(new { examInfo, token = token, year,count = exams.Count, groups });
             }
             catch (Exception e)

+ 4 - 0
TEAMModelOS/Controllers/School/SchoolSettingController.cs

@@ -139,6 +139,10 @@ namespace TEAMModelOS.Controllers
                     HashSet <string> lessonTag = _lessonTag.ToObject<HashSet<string>>();
                     setting.lessonTag = lessonTag;
                     break;
+                case bool when $"{_opt}".Equals("UpsertLessonSetting", StringComparison.OrdinalIgnoreCase) && (json.TryGetProperty("lessonSetting", out JsonElement _lessonSetting)):
+                    LessonSetting lessonSetting = _lessonSetting.ToObject<LessonSetting>();
+                    setting.lessonSetting = lessonSetting;
+                    break;
                 default: break;
             }
             await client.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<SchoolSetting>(setting, partitionKey: new Azure.Cosmos.PartitionKey("SchoolSetting"));

+ 30 - 3
TEAMModelOS/Controllers/Student/StudentCommonController.cs

@@ -75,11 +75,37 @@ namespace TEAMModelOS.Controllers
             {
                 eIds.Add((data.id, data.scope, data.name, data.source, data.classIds, data.qamode, data.creatorId, data.ext, data.createTime, data.owner));
             }
-            if (eIds.Count() == 0) {
+            if (eIds.Count == 0) {
                 return Ok(new { code = 404, msg = "暂无数据" });
             }
             var client = _azureCosmos.GetCosmosClient();
             List<(string eId, string sub, string cId, string cname, List<string> sIds, List<double> sum)> classResults = await getExamClassResult(eIds, client);
+            //每份试卷总分
+            List<(string id, List<PaperSimple> ps,List<ExamSubject> sub)> papers = new();
+            await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(
+                queryText: $"select c.papers,c.subjects,c.id from c where c.id in ({string.Join(",", eIds.Select(o => $"'{o.id}'"))})"))
+            {
+                using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                {
+                    var accounts = json.RootElement.GetProperty("Documents").EnumerateArray();
+                    while (accounts.MoveNext())
+                    {
+                        JsonElement account = accounts.Current;
+                        List<PaperSimple> ps = new();
+                        List<ExamSubject> sub = new();
+                        if (account.TryGetProperty("papers", out JsonElement paper))
+                        {
+                            ps = paper.ToObject<List<PaperSimple>>();
+                        }
+                        if (account.TryGetProperty("subjects", out JsonElement element))
+                        {
+                            sub = element.ToObject<List<ExamSubject>>();
+                        }
+                        papers.Add((account.GetProperty("id").GetString(), ps, sub));
+                    }
+                }
+            }           
             var exam = eIds.Select(e => new
             {
                 e.id,
@@ -91,6 +117,8 @@ namespace TEAMModelOS.Controllers
                 e.ext,
                 e.time,
                 e.owner,
+                point = papers.Where(p => p.id == e.id).FirstOrDefault().ps,
+                subject = papers.Where(s => s.id == e.id).FirstOrDefault().sub,
                 result = classResults.Where(c => c.eId == e.id).Select(s => new { s.sub, s.cId, s.cname, s.sum, s.sIds })
             });
             var result = exam.Where(e => e.result.Any());
@@ -290,8 +318,7 @@ namespace TEAMModelOS.Controllers
                         classResults.Add((account.GetProperty("eId").GetString(), sub, cid, cname, sIds, sums));
                     }
                 }
-            }
-
+            }           
             return classResults;
         }
         /// <summary>

+ 26 - 8
TEAMModelOS/Controllers/Student/StudentController.cs

@@ -28,6 +28,7 @@ using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models;
 using TEAMModelOS.SDK.Models.Cosmos;
 using TEAMModelOS.SDK.Models.Cosmos.Common;
+using TEAMModelOS.SDK.Models.Service;
 using static TEAMModelOS.SDK.StudentService;
 
 namespace TEAMModelOS.Controllers
@@ -38,19 +39,19 @@ namespace TEAMModelOS.Controllers
     {
         private readonly AzureCosmosFactory _azureCosmos;
         private readonly AzureStorageFactory _azureStorage;
+        private readonly AzureRedisFactory _azureRedis;
         private readonly DingDing _dingDing;
         private readonly Option _option;
+        private readonly IPSearcher _searcher;
         public IConfiguration _configuration { get; set; }
         private readonly AzureServiceBusFactory _serviceBus;
-        public StudentController(
-            AzureCosmosFactory azureCosmos,
-            AzureStorageFactory azureStorage,
-            DingDing dingDing,
-            IOptionsSnapshot<Option> option, IConfiguration configuration, AzureServiceBusFactory serviceBus
+        public StudentController(AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage, AzureRedisFactory azureRedis, DingDing dingDing, IPSearcher searcher, IOptionsSnapshot<Option> option,IConfiguration configuration, AzureServiceBusFactory serviceBus
             )
         {
+            _searcher = searcher;
             _azureCosmos = azureCosmos;
             _azureStorage = azureStorage;
+            _azureRedis = azureRedis;
             _dingDing = dingDing;
             _option = option?.Value;
             _configuration = configuration;
@@ -338,11 +339,13 @@ namespace TEAMModelOS.Controllers
             //權限token
             jwt.Payload.TryGetValue("name", out object _name);
             jwt.Payload.TryGetValue("picture", out object _picture);
+            (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
             School school = await schoolClient.ReadItemAsync<School>($"{school_code}", new PartitionKey("Base"));
             var response = await studentClient.ReadItemStreamAsync(id, new PartitionKey($"Base-{school_code.ToLower()}"));
             if (response.Status == 200)
             {
                 var rjson = await JsonDocument.ParseAsync(response.ContentStream);
+                Student student = rjson.ToObject<Student>();
                 rjson.RootElement.TryGetProperty("salt", out JsonElement salt);
                 rjson.RootElement.TryGetProperty("pw", out JsonElement dbpw);
                 rjson.RootElement.TryGetProperty("name", out JsonElement name);
@@ -351,7 +354,7 @@ namespace TEAMModelOS.Controllers
                 rjson.RootElement.TryGetProperty("no", out JsonElement no);
                 rjson.RootElement.TryGetProperty("groupId", out JsonElement groupId);
                 rjson.RootElement.TryGetProperty("groupName", out JsonElement groupName);
-                (string auth_token, string blob_uri, string blob_sas, object classinfo, List<object> courses, AuthenticationResult token) = await StudentCheck($"{id}", $"{classId}", $"{school_code}", $"{picture}", $"{name}", schoolClient, teacherClient, school.areaId);
+                (string auth_token, string blob_uri, string blob_sas, object classinfo, List<object> courses, AuthenticationResult token) = await StudentCheck($"{id}", $"{classId}", $"{school_code}", $"{picture}", $"{name}", schoolClient, teacherClient, school.areaId, ip, client, student);
                 return Ok(new { location = _option.Location, error = 0, auth_token, blob_uri, blob_sas, classinfo, courses, token = new { access_token = token.AccessToken, expires_in = token.ExpiresOn, id_token = auth_token, token_type = token.TokenType } });
             }
             else
@@ -379,11 +382,13 @@ namespace TEAMModelOS.Controllers
                 if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
                 if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
                 if (!request.TryGetProperty("pw", out JsonElement pw)) return BadRequest();
+                (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
                 School school = await schoolClient.ReadItemAsync<School>($"{school_code}", new PartitionKey("Base"));
                 var response = await studentClient.ReadItemStreamAsync(id.GetString(), new PartitionKey($"Base-{school_code.GetString().ToLower()}"));
                 if (response.Status == 200)
                 {
                     var rjson = await JsonDocument.ParseAsync(response.ContentStream);
+                    Student student = rjson.ToObject<Student>();
                     rjson.RootElement.TryGetProperty("salt", out JsonElement salt);
                     rjson.RootElement.TryGetProperty("pw", out JsonElement dbpw);
                     rjson.RootElement.TryGetProperty("name", out JsonElement name);
@@ -396,7 +401,7 @@ namespace TEAMModelOS.Controllers
                     var HashedPW = Utils.HashedPassword(pw.ToString(), salt.ToString());
                     if (HashedPW.Equals(dbpw.GetString()))
                     {
-                        (string auth_token, string blob_uri, string blob_sas, object classinfo, List<object> courses, AuthenticationResult token) = await StudentCheck($"{id}", $"{classId}", $"{school_code}", $"{picture}", $"{name}", schoolClient, teacherClient, school.areaId);
+                        (string auth_token, string blob_uri, string blob_sas, object classinfo, List<object> courses, AuthenticationResult token) = await StudentCheck($"{id}", $"{classId}", $"{school_code}", $"{picture}", $"{name}", schoolClient, teacherClient,  school.areaId,ip, client, student);
                         return Ok(new { location = _option.Location, error = 0, auth_token, blob_uri, blob_sas, classinfo, courses, token = new { access_token = token.AccessToken, expires_in = token.ExpiresOn, id_token = auth_token, token_type = token.TokenType } });
                     }
                     else
@@ -415,7 +420,7 @@ namespace TEAMModelOS.Controllers
                 return BadRequest();
             }
         }
-        private async Task<(string auth_token, string blob_uri, string blob_sas, object classinfo, List<object> courses, AuthenticationResult token)> StudentCheck(string id, string classId, string school_code, string picture, string name, CosmosContainer schoolClient, CosmosContainer teacherClient, string areaId)
+        private async Task<(string auth_token, string blob_uri, string blob_sas, object classinfo, List<object> courses, AuthenticationResult token)> StudentCheck(string id, string classId, string school_code, string picture, string name, CosmosContainer schoolClient, CosmosContainer teacherClient, string areaId,string ip,CosmosClient cosmosClient,Student student)
         {
             //班級課程
             object classinfo = null;
@@ -545,6 +550,12 @@ namespace TEAMModelOS.Controllers
 
             //換取AuthToken,提供給前端
             var auth_token = JwtAuthExtension.CreateAuthToken(_option.HostName, id, name, picture, _option.JwtSecretKey, scope: Constant.ScopeStudent, Website: "IES", areaId: areaId, schoolID: school_code, roles: new[] { "student" }, expire: 1);
+
+            //用户在线记录
+            student.loginInfos = await LoginService.DoLoginInfo(student.loginInfos, school_code, Constant.ScopeStudent, id, ip, _azureRedis, _azureStorage, cosmosClient, expire: 1);
+
+            await cosmosClient.GetContainer("TEAMModelOS", "Student").ReplaceItemAsync<Student>(student, id, new PartitionKey($"Base-{school_code}"));
+
             var clientID = _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
             var clientSecret = _configuration.GetValue<string>("HaBookAuth:CoreService:clientSecret");
             var token = await CoreTokenExtensions.CreateAccessToken(clientID, clientSecret, _option.Location.Replace("-Dep", "").Replace("-Test", ""));
@@ -629,11 +640,13 @@ namespace TEAMModelOS.Controllers
                 if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
                 if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
                 if (!request.TryGetProperty("pw", out JsonElement pw)) return BadRequest();
+                (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
 
                 var response = await studentClient.ReadItemStreamAsync(id.GetString(), new PartitionKey($"Base-{school_code.GetString().ToLower()}"));
                 if (response.Status == 200)
                 {
                     var rjson = await JsonDocument.ParseAsync(response.ContentStream);
+                    Student student = rjson.ToObject<Student>();
                     rjson.RootElement.TryGetProperty("salt", out JsonElement salt);
                     rjson.RootElement.TryGetProperty("pw", out JsonElement dbpw);
                     rjson.RootElement.TryGetProperty("name", out JsonElement name);
@@ -670,6 +683,11 @@ namespace TEAMModelOS.Controllers
                         }
                         //換取AuthToken,提供給前端
                         var auth_token = JwtAuthExtension.CreateAuthToken(_option.HostName, id.GetString(), name.GetString(), picture.GetString(), _option.JwtSecretKey, Website: "IES", areaId: schoolInfo.areaId, scope: Constant.ScopeStudent, schoolID: school_code.GetString(), roles: new[] { "student" }, expire: 1);
+                        //在线人数记录
+                        student.loginInfos = await LoginService.DoLoginInfo(student.loginInfos, school_code.GetString(), Constant.ScopeStudent, id.GetString(), ip, _azureRedis, _azureStorage, client, expire: 1);
+                        //保存学生登录信息
+                        await client.GetContainer("TEAMModelOS", "Student").ReplaceItemAsync<Student>(student, student.id, new PartitionKey($"{student.code}"));
+
                         //其他訊息
                         dynamic school = new ExpandoObject();
                         //回傳

+ 19 - 3
TEAMModelOS/Controllers/Student/TmdUserController.cs

@@ -25,6 +25,7 @@ using System.Net.Http;
 using TEAMModelOS.SDK;
 using Microsoft.AspNetCore.Authorization;
 using System.Text;
+using static TEAMModelOS.SDK.Models.Teacher;
 
 namespace TEAMModelOS.Controllers
 {
@@ -37,12 +38,14 @@ namespace TEAMModelOS.Controllers
     {
         private readonly AzureCosmosFactory _azureCosmos;
         private readonly AzureStorageFactory _azureStorage;
+        private readonly AzureRedisFactory _azureRedis;
         private readonly DingDing _dingDing;
         private readonly Option _option;
         private readonly IConfiguration _configuration;
         private readonly CoreAPIHttpService _coreAPIHttpService;
+        private readonly IPSearcher _searcher;
 
-        public TmdUserController(CoreAPIHttpService coreAPIHttpService, AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage, DingDing dingDing, IOptionsSnapshot<Option> option, IConfiguration configuration)
+        public TmdUserController(CoreAPIHttpService coreAPIHttpService, AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage, DingDing dingDing, IOptionsSnapshot<Option> option, IConfiguration configuration, IPSearcher searcher, AzureRedisFactory azureRedis)
         {
             _azureCosmos = azureCosmos;
             _azureStorage = azureStorage;
@@ -50,6 +53,8 @@ namespace TEAMModelOS.Controllers
             _option = option?.Value;
             _configuration = configuration;
             _coreAPIHttpService = coreAPIHttpService;
+            _searcher = searcher;
+            _azureRedis = azureRedis;
         }
         [ProducesDefaultResponseType]
         [HttpPost("get-school-info")]
@@ -79,10 +84,13 @@ namespace TEAMModelOS.Controllers
                 if (!request.TryGetProperty("id_token", out JsonElement id_token)) return BadRequest();
                 var jwt = new JwtSecurityToken(id_token.GetString());
                 if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
-                var id = jwt.Payload.Sub;
+                var id = jwt.Payload.Sub; 
+                var Schoolid = jwt.Payload.Azp;
                 jwt.Payload.TryGetValue("name", out object name);
                 jwt.Payload.TryGetValue("picture", out object picture);
+                (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
                 List<object> schools = new List<object>();
+                List<LoginInfo> loginInfos = new();
                 string defaultschool = null;
                 var client = _azureCosmos.GetCosmosClient();
                 try
@@ -116,7 +124,12 @@ namespace TEAMModelOS.Controllers
                         rm.ForEach(x => { tmdUser.schools.Remove(x); });
                         tmdUser.defaultSchool = string.IsNullOrEmpty(tmdUser.defaultSchool) ? tmdUser.schools[0].schoolId : tmdUser.defaultSchool;
                     }
-                    await client.GetContainer("TEAMModelOS", "Student").ReplaceItemAsync<TmdUser>(tmdUser, id, new PartitionKey("Base"));
+                    loginInfos = tmdUser.loginInfos;
+
+                   //在线人数记录
+                   tmdUser.loginInfos = await LoginService.DoLoginInfo(tmdUser.loginInfos, Schoolid, Constant.ScopeTmdUser, id, ip, _azureRedis, _azureStorage, client, expire: 1);
+
+                   await client.GetContainer("TEAMModelOS", "Student").ReplaceItemAsync<TmdUser>(tmdUser, id, new PartitionKey("Base"));
                     //預設學校ID
                     defaultschool = tmdUser.defaultSchool;
                 }
@@ -138,11 +151,14 @@ namespace TEAMModelOS.Controllers
                         };
                         var container = _azureStorage.GetBlobContainerClient(id);
                         await container.CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建Teacher私有容器,如存在則不做任何事,保障容器一定存在
+                        //在线人数记录
+                        teacher.loginInfos = await LoginService.DoLoginInfo(teacher.loginInfos, "", Constant.ScopeTmdUser, id, ip, _azureRedis, _azureStorage, client, expire: 1);
                         teacher = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Student").CreateItemAsync<TmdUser>(teacher, new PartitionKey("Base"));
                     }
                 }
                 //換取AuthToken,提供給前端
                 var auth_token = JwtAuthExtension.CreateAuthToken(_option.HostName, id, name?.ToString(), picture?.ToString(), _option.JwtSecretKey, Website: "IES", scope: Constant.ScopeTmdUser, roles: new[] { "student" }, expire: 1);
+
                 if (!string.IsNullOrEmpty(defaultschool))
                 {
 

+ 7 - 3
TEAMModelOS/Controllers/Teacher/InitController.cs

@@ -42,6 +42,7 @@ namespace TEAMModelOS.Controllers
     {
         private readonly AzureCosmosFactory _azureCosmos;
         private readonly AzureStorageFactory _azureStorage;
+        private readonly AzureRedisFactory _azureRedis;
         private readonly DingDing _dingDing;
         private readonly Option _option;
         private readonly IConfiguration _configuration;
@@ -50,11 +51,12 @@ namespace TEAMModelOS.Controllers
         private readonly IPSearcher _searcher;
         private readonly ILogger<InitController> _logger;
 
-        public InitController(ILogger<InitController> logger, IPSearcher searcher, CoreAPIHttpService coreAPIHttpService, AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage, DingDing dingDing, IOptionsSnapshot<Option> option, IConfiguration configuration, NotificationService notificationService)
+        public InitController(ILogger<InitController> logger, IPSearcher searcher, CoreAPIHttpService coreAPIHttpService, AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage, AzureRedisFactory azureRedis, DingDing dingDing, IOptionsSnapshot<Option> option, IConfiguration configuration, NotificationService notificationService)
         {
             _searcher = searcher;
             _azureCosmos = azureCosmos;
             _azureStorage = azureStorage;
+            _azureRedis = azureRedis;
             _dingDing = dingDing;
             _option = option?.Value;
             _configuration = configuration;
@@ -266,9 +268,8 @@ namespace TEAMModelOS.Controllers
             (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
             try
             {
-
                 Teacher teacher = null;
-                TeacherInfo teacherInfo = await TeacherService.TeacherInfo(_azureCosmos, teacher, $"{name}", $"{picture}", id, _azureStorage, _option);
+                TeacherInfo teacherInfo = await TeacherService.TeacherInfo(_azureCosmos, teacher, $"{name}", $"{picture}", id, _azureStorage, _option,_azureRedis,ip);
                 teacherInfo.areas.ForEach(x => { if (x.setting != null) { x.setting.accessConfig = x.setting.accessConfig; } });
                 LoginService.LoginLog(HttpContext, _option, _logger, _dingDing, ip, region, id, $"{name}", 200);
                 int lessonLimit = teacherInfo.teacher.lessonLimit;
@@ -336,6 +337,7 @@ namespace TEAMModelOS.Controllers
                 var jwt = new JwtSecurityToken(id_token.GetString());
                 var id = jwt.Payload.Sub;
 
+                (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
                 var client = _azureCosmos.GetCosmosClient();
 
                 //權限token
@@ -405,6 +407,8 @@ namespace TEAMModelOS.Controllers
                 {
                     school_code = teacher.schools[0].schoolId;
                     teacher.defaultSchool = school_code;
+                    //在线人数记录
+                    teacher.loginInfos = await LoginService.DoLoginInfo(teacher.loginInfos, school_code, Constant.ScopeTeacher, id, ip, _azureRedis, _azureStorage, client, expire: 1);
                     teacher = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
                 }
                 var response = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(id, new PartitionKey($"Teacher-{school_code}"));

+ 39 - 2
TEAMModelOS/Controllers/Third/OAuth2Controller.cs

@@ -47,7 +47,7 @@ namespace TEAMModelOS.Controllers.Third
     [ProducesResponseType(StatusCodes.Status400BadRequest)]
     //
     //[Route("")]
-    [Route("oauth")]
+   
     [ApiController]
     public class OAuth2Controller : ControllerBase
     {
@@ -95,7 +95,44 @@ namespace TEAMModelOS.Controllers.Third
             _httpTrigger = httpTrigger;
             _environment = environment;
         }
-        [HttpPost("check-bind")]
+        [HttpGet("authorized/xkw")]
+        [AllowAnonymous]
+        public   IActionResult AuthorizedXkw([FromQuery] OAuthCode authCode) {
+            OAuthCode code = new OAuthCode();
+            string QueryString = HttpContext?.Request?.QueryString.Value;
+            if (!string.IsNullOrWhiteSpace(QueryString)) {
+                QueryString = HttpUtility.UrlDecode(QueryString).Substring(1);
+                string [] ps= QueryString.Split("&");
+                foreach (var p in ps) {
+                    string[] pp=p.Split("=");
+                    if (pp.Length == 2) {
+                        switch (true)
+                        {
+                            case bool when $"{pp[0]}".Equals("tmdid", StringComparison.OrdinalIgnoreCase):
+                                code.tmdid = pp[1];
+                                break;
+                            case bool when $"{pp[0]}".Equals("code", StringComparison.OrdinalIgnoreCase):
+                                code.code = pp[1];
+                                break;
+                            case bool when $"{pp[0]}".Equals("module", StringComparison.OrdinalIgnoreCase):
+                                code.module = pp[1];
+                                break;
+                                default: break;
+                        }
+                    }
+                }
+            }
+            var  path =$"{HttpContext?.Request?.Scheme}://{HttpContext?.Request?.Host}/xkw/authorize?code={code.code}&tmdid={code.tmdid}&module={code.module}";
+            StringValues accessToken;//应该从别的地方获取 不是mvc 无法从Session 获取 
+            HttpContext.Request.Headers.TryGetValue($"xkw-AccessToken", out accessToken);
+            if (!_option.Location.Contains("China"))
+            {
+                return BadRequest();
+            }
+            return Redirect(path);
+        }
+
+        [HttpPost("oauth/check-bind")]
         [Authorize(Roles = "IES")]
         [AuthToken(Roles = "teacher,admin,area,student")]
         public async Task<IActionResult> CheckBind(JsonElement json)

+ 32 - 17
TEAMModelOS/Controllers/Third/Sc/ScController.cs

@@ -170,7 +170,7 @@ namespace TEAMModelOS.Controllers
 
             foreach (var teacher in teachers)
             {
-                var a = teacher.binds.SelectMany(y => y.data).ToList().Find(x => !string.IsNullOrEmpty(x));
+                var a = teacher.binds.SelectMany(y => y.data).ToList().Find(x => !string.IsNullOrWhiteSpace(x));
                 if (a != null)
                 {
                     await _azureStorage.UploadFileByContainer("teammodelos", a, $"yxpt/scpjx/scbind", $"{teacher.id}.json");
@@ -183,7 +183,7 @@ namespace TEAMModelOS.Controllers
                 {
                     foreach (var school in teacher.schools)
                     {
-                        if (!string.IsNullOrEmpty(school.schoolId))
+                        if (!string.IsNullOrWhiteSpace(school.schoolId))
                         {
 
                             if (school.status.Equals("join"))
@@ -276,7 +276,7 @@ namespace TEAMModelOS.Controllers
             {
                 Teacher teacher = null;
                 TmdidImplicit tmdidImplicit = null;
-                if (string.IsNullOrEmpty(sso.id_token) && !string.IsNullOrEmpty(sso.mobile))
+                if (!string.IsNullOrWhiteSpace(sso.mobile))
                 {
                     var coreUser = await _coreAPIHttpService.GetUserInfo(new Dictionary<string, string> { { "key", sso.mobile } }, _option.Location, _configuration);
                     if (coreUser != null)
@@ -286,13 +286,28 @@ namespace TEAMModelOS.Controllers
                                         { "client_id",clientID },
                                         { "account",coreUser.id },
                                         { "nonce",Guid.NewGuid().ToString()} }, _option.Location, _configuration);
-                        if (tmdidImplicit != null && !string.IsNullOrEmpty(tmdidImplicit.id_token))
+                        if (tmdidImplicit != null && !string.IsNullOrWhiteSpace(tmdidImplicit.id_token))
                         {
                             sso.id_token = tmdidImplicit.id_token;
                         }
+                        else
+                        {
+                            return Ok(new
+                            {
+                                location = _option.Location,
+                                status = 2,
+                            });
+                        }
+                    }
+                    else {
+                        return Ok(new
+                        {
+                            location = _option.Location,
+                            status = 2,
+                        });
                     }
                 }
-                if (string.IsNullOrEmpty(sso.id_token))
+                if (string.IsNullOrWhiteSpace(sso.id_token))
                 {
                     return Ok(new
                     {
@@ -398,7 +413,7 @@ namespace TEAMModelOS.Controllers
                         }
                     }
                 }
-                catch (CosmosException ex)
+                catch (CosmosException ex) when (ex.Status ==404)
                 {
                     teacher = new Teacher
                     {
@@ -416,7 +431,6 @@ namespace TEAMModelOS.Controllers
                     };
                     var container = _azureStorage.GetBlobContainerClient(id);
                     await container.CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建Teacher私有容器,如存在則不做任何事,保障容器一定存在
-
                     teacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").CreateItemAsync<Teacher>(teacher, new PartitionKey("Base"));
 
                     ScBindData bindData = scsso.data.ToObject<ScBindData>();
@@ -524,7 +538,7 @@ namespace TEAMModelOS.Controllers
                     setting = item;
                     break;
                 }
-                if (setting == null || (setting != null && string.IsNullOrEmpty(setting.accessConfig)))
+                if (setting == null || (setting != null && string.IsNullOrWhiteSpace(setting.accessConfig)))
                 {
                     return Redirect(rurl.Append($"?status=1").ToString());
                 }
@@ -544,7 +558,7 @@ namespace TEAMModelOS.Controllers
                         Account = $"{ scTeacher.Account}";
                     }
                 }
-                if (string.IsNullOrEmpty(SchoolID))
+                if (string.IsNullOrWhiteSpace(SchoolID))
                 {
                     (int status, string json) = await _httpTrigger.RequestHttpTrigger(dict, _option.Location, "GetSingleTeacherByProject");
                     if (status == 200)
@@ -620,7 +634,7 @@ namespace TEAMModelOS.Controllers
                         }, location, _configuration);
                     if (implicit_token != null)
                     {
-                        if (string.IsNullOrEmpty(implicit_token.id_token))
+                        if (string.IsNullOrWhiteSpace(implicit_token.id_token))
                         {
                             await _dingDing.SendBotMsg($"OS,隐式登录获得信息为空:{_option.Location}-\n{scsso.ToJsonString()} \npath:{path}\n{implicit_token.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
                             return Redirect(rurl.Append($"?status=1").ToString());
@@ -664,13 +678,14 @@ namespace TEAMModelOS.Controllers
                     else
                     {
                         //绑定失效
-                        if (teacher.binds.IsNotEmpty())
-                        {
-                            teacher.binds.RemoveAll(x => x.userid.Equals(sso.tid));
-                            await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, teacher.id, new PartitionKey(teacher.code));
-                        }
-                        string enurl = $"status=4&param={HttpUtility.UrlEncode(sso.ToJsonString(), Encoding.UTF8)}&type={type}&bindurl=sc/bind";
-                        return Redirect(rurl.Append($"?{enurl}").ToString());
+                        //if (teacher.binds.IsNotEmpty())
+                        //{
+                        //    teacher.binds.RemoveAll(x => x.userid.Equals(sso.tid));
+                        //    await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, teacher.id, new PartitionKey(teacher.code));
+                        //}
+                        //string enurl = $"status=4&param={HttpUtility.UrlEncode(sso.ToJsonString(), Encoding.UTF8)}&type={type}&bindurl=sc/bind";
+                        // return Redirect(rurl.Append($"?{enurl}").ToString());
+                        return Redirect(rurl.Append($"?status=1").ToString());
                     }
                 }
             }

+ 12 - 4
TEAMModelOS/Controllers/Third/Xkw/OpenAuthClient.cs

@@ -4,6 +4,7 @@ using System.IO;
 using System.Net;
 using System.Security.Cryptography;
 using System.Text;
+using System.Web;
 using TEAMModelOS.SDK.Extension;
 
 namespace TEAMModelOS.Controllers.Third.Xkw
@@ -52,12 +53,13 @@ namespace TEAMModelOS.Controllers.Third.Xkw
         public string SERVICE_URL { get; set; }
         public string OpenId { get; set; }
         public string UserId { get; set; }
+        public string Param { get; set; }
         /// <summary>
         /// 静默注册参数
         /// </summary>
         public string Extra { get; set; }
         public string ErrorMessage { get; set; }
-        public XkwOAuthClient(string appKey, string appSecret, string redirectUrl, string oauthHost,string domain, string accessToken = null, string openid = null, string userId = null)
+        public XkwOAuthClient(string appKey, string appSecret, string redirectUrl, string oauthHost, string domain, string accessToken = null, string openid = null, string userId = null, string param = null)
             : base(appKey, appSecret, redirectUrl, accessToken)
         {
             ClientName = "Xkw Demo Client";
@@ -72,6 +74,7 @@ namespace TEAMModelOS.Controllers.Third.Xkw
             {
                 isAccessTokenSet = true;
             }
+            Param = param;
         }
 
         /// <summary>
@@ -80,6 +83,8 @@ namespace TEAMModelOS.Controllers.Third.Xkw
         /// <returns></returns>
         public override string GetAuthorizationUrl()
         {
+         
+            string redirect_uri= $"http://kong.sso.com:5000/authorized/xkw?{HttpUtility.UrlEncode(Param) }";
             string openSecret = "";
             if (!string.IsNullOrEmpty(OpenId))
             {
@@ -87,13 +92,16 @@ namespace TEAMModelOS.Controllers.Third.Xkw
             }
             string timespan = CryptoUtils.EncryptAES(GetTimeStamp(), AppSecret);
             string url = string.Format(AUTH_URL + "?client_id={0}&open_id={1}&service={2}&redirect_uri={3}&timespan={4}",
-                  AppKey, openSecret, SERVICE_URL, RedirectUrl, timespan);
+                  AppKey, openSecret, SERVICE_URL, redirect_uri, timespan);
             if (!string.IsNullOrEmpty(Extra))
             {
                 url = string.Format("{0}&extra={1}", url, Extra);
             }
-            string retUrl = url + "&signature=" + SignatureHelper.GenerateSignature(url, AppSecret);
-            return retUrl.Replace("+", "%2B");
+            //string retUrl = url + "&signature=" + SignatureHelper.GenerateSignature(url, AppSecret);
+            string URLEncoder = string.Format(AUTH_URL + "?client_id={0}&open_id={1}&service={2}&redirect_uri={3}&timespan={4}&extra={5}&signature={6}",
+               HttpUtility.UrlEncode(AppKey), HttpUtility.UrlEncode(openSecret), HttpUtility.UrlEncode(SERVICE_URL), HttpUtility.UrlEncode(redirect_uri),
+               HttpUtility.UrlEncode(timespan), HttpUtility.UrlEncode(Extra), HttpUtility.UrlEncode(SignatureHelper.GenerateSignature(url, AppSecret)));
+            return URLEncoder.Replace("+", "%2B");
         }
 
         /// <summary>

+ 28 - 11
TEAMModelOS/Controllers/Third/Xkw/XkwOAuth2Controller.cs

@@ -114,7 +114,7 @@ namespace TEAMModelOS.Controllers
             {
                 return BadRequest();
             }
-            var client = await GetOpenAuthClient(tmdid, accessToken, domain);
+            var client = await GetOpenAuthClient(tmdid,authCode.module, accessToken, domain);
             if (authCode.agree == 1) {
                 //获取醍摩豆id的手机号
                 var keys =new List<string> { tmdid};
@@ -133,12 +133,12 @@ namespace TEAMModelOS.Controllers
             string url = client.GetAuthorizationUrl();
             return Ok(new { redirect = url });
         }
-        [HttpPost("authorize")]
-        [Authorize(Roles = "IES")]
-        [AuthToken(Roles = "teacher,admin,area,student")]
-        public async Task<IActionResult> Authorize(OAuthCode authCode  )
+        [HttpGet("authorize")]
+        //[Authorize(Roles = "IES")]
+        //[AuthToken(Roles = "teacher,admin,area,student")]
+        public async Task<IActionResult> Authorize([FromQuery] OAuthCode authCode  )
         {
-            var (tmdid, _, _, school) = HttpContext.GetAuthTokenInfo();
+            //var (tmdid, _, _, school) = HttpContext.GetAuthTokenInfo();
             StringValues accessToken ;//应该从别的地方获取 不是mvc 无法从Session 获取 
             HttpContext.Request.Headers.TryGetValue($"xkw-AccessToken", out accessToken);
             if (!_option.Location.Contains("China"))
@@ -151,7 +151,7 @@ namespace TEAMModelOS.Controllers
                 return RedirectToAction("Index");
             }
             string domain = HttpContext?.Request?.Host.Host;
-            var client =await GetOpenAuthClient(tmdid, accessToken, domain);
+            var client =await GetOpenAuthClient(authCode.tmdid, authCode.module, accessToken, domain);
             string schoolId = "teammodel.cn";
             //学科网测试
             //if (schoolId.Equals("kong.sso.com"))
@@ -186,8 +186,8 @@ namespace TEAMModelOS.Controllers
                     Type = "xkw"
                 };
                 await table.SaveOrUpdate<OAuthUser>(authUser);
-                return Ok(new { status = 1, url=client.SERVICE_URL});
-                //return Redirect($"bind?status=1&accessToken={client.AccessToken}&openId={client.OpenId}&userId={client.UserId}&domain={domain}&msg={HttpUtility.UrlEncode("认证成功")}");
+                //return Ok(new { status = 1, url=client.SERVICE_URL});
+                return Redirect(client.SERVICE_URL);
             }
             else
             {
@@ -258,7 +258,7 @@ namespace TEAMModelOS.Controllers
         /// 封装一个方法来初始化OpenAuth客户端
         /// </summary>
         /// <returns></returns>
-        private async Task<XkwOAuthClient> GetOpenAuthClient(string tmdid,string accessToken,string domain)
+        private async Task<XkwOAuthClient> GetOpenAuthClient(string tmdid,string module , string accessToken,string domain)
         {
             var table = _azureStorage.GetCloudTableClient().GetTableReference("IESOAuth");
             //var accessToken = Session["access_token"] == null ? string.Empty : (string)Session["access_token"];
@@ -282,8 +282,25 @@ namespace TEAMModelOS.Controllers
                 string OAuth_Xkw_RedirectUrl = configs[0].RedirectUrl;
                 string OAuth_Xkw_OAuthHost = configs[0].OAuthHost;
                 string OAuth_Xkw_ServiceUrl = configs[0].ServiceUrl;
+                try {
+                    List<ServiceModule> services= configs[0].ServiceUrl.ToObject<List<ServiceModule>>();
+                    ServiceModule serviceModule= services.Find(x => x.module.Equals(module));
+                    if (serviceModule != null)
+                    {
+                        OAuth_Xkw_ServiceUrl = serviceModule.url;
+                    }
+                    else {
+                        OAuth_Xkw_ServiceUrl = "http://www.zxxk.com/";
+                    }
+                } catch (Exception ex) {
+                    if (!OAuth_Xkw_ServiceUrl.StartsWith("http://www.zxxk.com"))
+                    {
+                        OAuth_Xkw_ServiceUrl = "http://www.zxxk.com/";
+                    }
+                }
                 string OAuth_Xkw_Domain= configs[0].Domain;
-                var client = new XkwOAuthClient(OAuth_Xkw_AppKey, OAuth_Xkw_AppSecret, OAuth_Xkw_RedirectUrl, OAuth_Xkw_OAuthHost, OAuth_Xkw_Domain, accessToken, openId, userId);
+                string OAuth_Xkw_Param =$"tmdid={tmdid}&module={module}&state={Guid.NewGuid().ToString()}";
+                var client = new XkwOAuthClient(OAuth_Xkw_AppKey, OAuth_Xkw_AppSecret, OAuth_Xkw_RedirectUrl, OAuth_Xkw_OAuthHost, OAuth_Xkw_Domain, accessToken, openId, userId, OAuth_Xkw_Param);
                 client.SERVICE_URL = OAuth_Xkw_ServiceUrl;
                 return client;
             }

+ 34 - 21
TEAMModelOS/Controllers/XTest/TestController.cs

@@ -1046,31 +1046,44 @@ namespace TEAMModelOS.Controllers
         [HttpPost("test-blob-folder")]
         public async Task<IActionResult> TestBlobFolder(JsonElement json)
         {
-            var client = _azureStorage.GetBlobContainerClient("1595321354");
-            List<BlobItem> blobItems = new List<BlobItem>();
-            HashSet<string> ids = new HashSet<string>();
-            string path = $"records";
-            await foreach (BlobItem item in client.GetBlobsAsync(BlobTraits.None, BlobStates.None, path))
+            List<string> tchids = new List<string>();
+            //string tchsql = "select value(c.id) from c ";
+            //await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher)
+            //    .GetItemQueryIterator<string>(queryText: tchsql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey("Base") }))
+            //{
+            //    tchids.Add(item);
+            //}
+            tchids.Add("1530782422");
+            Dictionary<string, object> dict = new Dictionary<string, object>();
+            foreach (var tchid in tchids)
             {
-                var p = item.Name.Split("/");
-                if (p.Length > 2)
+                var client = _azureStorage.GetBlobContainerClient(tchid);
+                List<BlobItem> blobItems = new List<BlobItem>();
+                HashSet<string> ids = new HashSet<string>();
+                string path = $"records";
+                await foreach (BlobItem item in client.GetBlobsAsync(BlobTraits.None, BlobStates.None, path))
                 {
-                    ids.Add(p[1]);
+                    var p = item.Name.Split("/");
+                    if (p.Length > 2)
+                    {
+                        ids.Add(p[1]);
+                    }
                 }
+                List<string> lessonIds = new List<string>();
+                string sql = $"select value(c.id) from c where c.tmdid='{tchid}' ";
+                await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher)
+                    .GetItemQueryIterator<string>(queryText: sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey("LessonRecord") }))
+                {
+                    lessonIds.Add(item);
+                }
+                var notdata = ids.Except(lessonIds);
+                var notblob = lessonIds.Except(ids).ToList();
+                List<string> paths = notdata.Select(x => $"records/{x}").ToList();
+                //await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).DeleteItemsStreamAsync(notblob, "LessonRecord-1595321354");
+                //await _azureStorage.GetBlobServiceClient().DeleteBlobs(_dingDing, "1595321354", paths);
+                dict.Add(tchid, new { notdata= paths, notblob });
             }
-            List<string> lessonIds = new List<string>();
-            string sql = "select value(c.id) from c ";
-            await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher)
-                .GetItemQueryIterator<string>(queryText: sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey("LessonRecord-1595321354") }))
-            {
-                lessonIds.Add(item);
-            }
-            var notdata = ids.Except(lessonIds);
-            var notblob = lessonIds.Except(ids).ToList();
-            List<string> paths = notdata.Select(x => $"records/{x}").ToList();
-            await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).DeleteItemsStreamAsync(notblob, "LessonRecord-1595321354");
-            await _azureStorage.GetBlobServiceClient().DeleteBlobs(_dingDing, "1595321354", paths);
-            return Ok(new { lessonIds, ids, notdata, notblob });
+            return Ok(new { dict });
         }
     }
 }

+ 3 - 3
TEAMModelOS/TEAMModelOS.csproj

@@ -32,9 +32,9 @@
     <SpaRoot>ClientApp\</SpaRoot>
     <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
     <UserSecretsId>078b5d89-7d90-4f6a-88fc-7d96025990a8</UserSecretsId>
-    <Version>5.2205.6</Version>
-    <AssemblyVersion>5.2205.6.1</AssemblyVersion>
-    <FileVersion>5.2205.6.1</FileVersion>
+    <Version>5.2205.7</Version>
+    <AssemblyVersion>5.2205.7.1</AssemblyVersion>
+    <FileVersion>5.2205.7.1</FileVersion>
     <Description>TEAMModelOS(IES5)</Description>
     <PackageReleaseNotes>6.0版本说明</PackageReleaseNotes>
     <PackageId>TEAMModelOS</PackageId>

+ 0 - 1
TEAMModelOS/appsettings.Development.json

@@ -26,7 +26,6 @@
     "Cosmos": {
       //"ConnectionString": "AccountEndpoint=https://teammodel.documents.azure.com:443/;AccountKey=opemBAZi0yATewIlhxDYoIEUqncT5qJh3pUBZsBkTqEkuLYTuu3VS7oaDGJlPp8ASwm5SVSrK2caJsjgmqRw9g==;"
      "ConnectionString": "AccountEndpoint=https://cdhabookdep-free.documents.azure.cn:443/;AccountKey=JTUVk92Gjsx17L0xqxn0X4wX2thDPMKiw4daeTyV1HzPb6JmBeHdtFY1MF1jdctW1ofgzqkDMFOtcqS46by31A==;"
-      //"ConnectionString": "AccountEndpoint=https://teammodelos.documents.azure.cn:443/;AccountKey=clF73GwPECfP1lKZTCvs8gLMMyCZig1HODFbhDUsarsAURO7TcOjVz6ZFfPqr1HzYrfjCXpMuVD5TlEG5bFGGg==;"
     },
     "Redis": {
       "ConnectionString": "52.130.252.100:6379,password=habook,ssl=false,abortConnect=False,writeBuffer=10240"