Forráskód Böngészése

Merge branch 'develop6.0-tmd' of http://52.130.252.100:10000/TEAMMODEL/TEAMModelOS into develop6.0-tmd

zhouj1203@hotmail.com 3 éve
szülő
commit
0bfc682015
47 módosított fájl, 1860 hozzáadás és 897 törlés
  1. 33 4
      TEAMModelBI/Controllers/BISchool/BatchAreaController.cs
  2. 1 0
      TEAMModelBI/Controllers/BISchool/BatchSchoolController.cs
  3. 102 0
      TEAMModelBI/Controllers/BITest/JsonTestController.cs
  4. 99 12
      TEAMModelBI/Controllers/BITest/TestController.cs
  5. 215 2
      TEAMModelBI/Controllers/Census/ProductStatisController.cs
  6. 169 22
      TEAMModelBI/Controllers/Census/SchoolController.cs
  7. 22 12
      TEAMModelBI/Controllers/DingDingStruc/SystemConfigController.cs
  8. 14 34
      TEAMModelBI/JsonFile/SystemConfig.json
  9. 1 0
      TEAMModelBI/Models/AssistSchool.cs
  10. 1 1
      TEAMModelBI/Tool/CommonFind.cs
  11. 47 12
      TEAMModelBI/Tool/TimeHelper.cs
  12. 17 2
      TEAMModelOS.SDK/Models/Service/Third/Xkw/OAuthModel.cs
  13. 1 0
      TEAMModelOS/ClientApp/public/index.html
  14. 19 0
      TEAMModelOS/ClientApp/src/api/auth.js
  15. 2 0
      TEAMModelOS/ClientApp/src/api/index.js
  16. 1 0
      TEAMModelOS/ClientApp/src/assets/student-web/component_styles/classmate-commentPages.less
  17. 319 313
      TEAMModelOS/ClientApp/src/common/AbilityUpload.vue
  18. 17 7
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/ClassmateCommentPages.vue
  19. 6 5
      TEAMModelOS/ClientApp/src/components/student-web/HomeView/CourseView/SchoolReport.vue
  20. 1 0
      TEAMModelOS/ClientApp/src/locale/lang/en-US/learnActivity.js
  21. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/en-US/stuAccount.js
  22. 18 0
      TEAMModelOS/ClientApp/src/locale/lang/en-US/studentWeb.js
  23. 1 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/learnActivity.js
  24. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/stuAccount.js
  25. 19 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/studentWeb.js
  26. 1 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/learnActivity.js
  27. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/stuAccount.js
  28. 19 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/studentWeb.js
  29. 4 0
      TEAMModelOS/ClientApp/src/router/routes.js
  30. 8 3
      TEAMModelOS/ClientApp/src/store/module/user.js
  31. 140 89
      TEAMModelOS/ClientApp/src/utils/public.js
  32. 41 2
      TEAMModelOS/ClientApp/src/view/areaMgmt/AreaData.vue
  33. 8 86
      TEAMModelOS/ClientApp/src/view/areaMgmt/SchoolDetail.vue
  34. 246 204
      TEAMModelOS/ClientApp/src/view/evaluation/bank/index.vue
  35. 5 12
      TEAMModelOS/ClientApp/src/view/jyzx/classMemoir.vue
  36. 3 1
      TEAMModelOS/ClientApp/src/view/learnactivity/tabs/DataView.vue
  37. 3 3
      TEAMModelOS/ClientApp/src/view/schoolmgmt/SystemSetting/SystemSetting.vue
  38. 4 2
      TEAMModelOS/ClientApp/src/view/statistics/Dashboard.vue
  39. 3 3
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/EvaluationList/TotalIndex.vue
  40. 7 3
      TEAMModelOS/ClientApp/src/view/teachermgmt/components/import/Import.vue
  41. 2 2
      TEAMModelOS/ClientApp/src/view/teachermgmt/components/mgt/TeacherMgt.vue
  42. 45 20
      TEAMModelOS/ClientApp/src/view/train/TrainMgt.vue
  43. 16 0
      TEAMModelOS/ClientApp/src/view/xkw/xkw.vue
  44. 1 1
      TEAMModelOS/ClientApp/vue.config.js
  45. 107 0
      TEAMModelOS/Controllers/Third/OAuth2Controller.cs
  46. 2 2
      TEAMModelOS/Controllers/Third/Xkw/OpenAuthClient.cs
  47. 67 33
      TEAMModelOS/Controllers/Third/Xkw/XkwOAuth2Controller.cs

+ 33 - 4
TEAMModelBI/Controllers/BISchool/BatchAreaController.cs

@@ -19,6 +19,7 @@ using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models.Service;
 using TEAMModelBI.Filter;
 using TEAMModelBI.Tool.Extension;
+using TEAMModelBI.Tool;
 
 namespace TEAMModelBI.Controllers.BISchool
 {
@@ -55,11 +56,20 @@ namespace TEAMModelBI.Controllers.BISchool
         {
             try
             {
-                List<Area> areas = new List<Area>();
+                List<Area> tempAreas = new();
+                List<RecArea> areas = new();
+
                 var azureClient = _azureCosmos.GetCosmosClient();
-                await foreach (var item in azureClient.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<Area>(queryText: $"select * from c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base-Area") }))
+                await foreach (var item in azureClient.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<Area>(queryText: $"select value(c) from c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base-Area") }))
                 {
-                    areas.Add(item);
+                    tempAreas.Add(item);
+                }
+
+                foreach (var area in tempAreas) 
+                {
+                    RecArea recArea = new() { id = area.id, code = area.code, pk = area.pk, name = area.name, provCode = area.provCode, provName = area.provName, cityCode = area.cityCode, cityName = area.cityName, standard = area.standard, standardName = area.standardName, institution = area.institution };
+                    recArea.schoolCount = await CommonFind.FindTotals(azureClient, $"select count(c.id) as totals from c where c.areaId='{area.id}' and c.standard='{area.standard}'", "School", "Base");
+                    areas.Add(recArea);
                 }
 
                 return Ok(new { state = 200, areas });
@@ -676,6 +686,25 @@ namespace TEAMModelBI.Controllers.BISchool
             }
         }
 
-    }
+        /// <summary>
+        /// 区域列表
+        /// </summary>
+        public record RecArea 
+        {
+            public string id { get; set; }
+            public string code { get; set; }
+            public string pk { get; set; }
+            public string name { get; set; }
+            public string provCode { get; set; }
+            public string provName { get; set; }
+            public string cityCode { get; set; }
+            public string cityName { get; set; }
+            public string standard { get; set; }
+            public string standardName { get; set; }
+            public string institution { get; set; }
+            public int schoolCount { get; set; }
+        }
+
 
 }
+}

+ 1 - 0
TEAMModelBI/Controllers/BISchool/BatchSchoolController.cs

@@ -343,6 +343,7 @@ namespace TEAMModelBI.Controllers.BISchool
                         }
 
                         school.assists = await CommonFind.FindSchoolRoles(cosmosClient, school.id, "assist");
+                        school.lessonCount = await CommonFind.FindTotals(cosmosClient, $"select count(c.id) as totals from c ", "School", $"LessonRecord-{school.id}");
                     });
                 }
 

+ 102 - 0
TEAMModelBI/Controllers/BITest/JsonTestController.cs

@@ -0,0 +1,102 @@
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+using TEAMModelBI.Models;
+
+namespace TEAMModelBI.Controllers.BITest
+{
+    [Route("jsontest")]
+    [ApiController]
+    public class JsonTestController : ControllerBase
+    {
+        public JsonTestController() 
+        {
+        
+        }
+
+        /// <summary>
+        /// 获取
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost("get-sysconfig")]
+        public async Task<IActionResult> GetSysConfig() 
+        {
+            var sysCinfig = ReadJsonFileToList();
+            Write(sysCinfig);
+            return Ok(new { state = 200 });
+        }
+
+        /// <summary>
+        /// 获取本地的json文件转成List
+        /// </summary>
+        /// <returns></returns>
+        public static List<SysConfig> ReadJsonFileToList()
+        {
+            //将Json转换回列表
+            var directorypath = Directory.GetCurrentDirectory();
+            string strFileName = directorypath + "\\JsonFile\\SystemConfig.json";
+            string jsonData = GetJsonFile(strFileName);
+            Console.WriteLine(jsonData);
+            //反序列化Json字符串内容为对象
+            List<SysConfig> jsondata = JsonConvert.DeserializeObject<List<SysConfig>>(jsonData);
+            return jsondata;
+
+        }
+        /// <summary>
+        /// 获取到本地的Json文件并且解析返回对应的json字符串
+        /// </summary>
+        /// <param name="filepath"></param>
+        /// <returns></returns>
+        public static string GetJsonFile(string filepath)
+        {
+            string json = string.Empty;
+            using (FileStream fs = new FileStream(filepath, FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite, FileShare.ReadWrite))
+            {
+                Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
+                using (StreamReader sr = new(fs, Encoding.GetEncoding("GB2312")))
+                {
+                    json = sr.ReadToEnd().ToString();
+                }
+            }
+            return json;
+        }
+
+        /// <summary>
+        /// 把对象写入到json文件中
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        public static void Write(List<SysConfig> jsonData)
+        {
+            var directorypath = Directory.GetCurrentDirectory();
+            string strFileName = directorypath + "\\JsonFile\\SystemConfig.json";
+            string ListJson = JsonConvert.SerializeObject(jsonData);
+
+            System.IO.File.WriteAllText(strFileName, ListJson);  //有缩进
+
+            ////无缩进
+            //Console.WriteLine(ListJson);
+
+            //writeJsonFile(strFileName, ListJson);
+
+            ////将序列化的json字符串内容写入Json文件,并且保存
+            //void writeJsonFile(string path, string jsonConents)
+            //{
+            //    using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite, FileShare.ReadWrite))
+            //    {
+            //        //如果json文件中有中文数据,可能会出现乱码的现象,那么需要加上如下代码
+            //        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
+            //        using (StreamWriter sw = new StreamWriter(fs, Encoding.GetEncoding("GB2312")))
+            //        {
+            //            sw.WriteLine(jsonConents);
+            //        }
+            //    }
+            //}
+        }
+    }
+}

+ 99 - 12
TEAMModelBI/Controllers/BITest/TestController.cs

@@ -40,7 +40,6 @@ using System.Runtime.CompilerServices;
 using System.IdentityModel.Tokens.Jwt;
 using TEAMModelBI.Tool.Extension;
 using System.IO;
-using static TEAMModelBI.Controllers.BISchool.BatchSchoolController;
 using System.Net.Http;
 using System.Net.Http.Json;
 using System.Net;
@@ -51,6 +50,7 @@ namespace TEAMModelBI.Controllers.BITest
     [ApiController]
     public class TestController : ControllerBase
     {
+
         private readonly AzureCosmosFactory _azureCosmos;
         private readonly DingDing _dingDing;
         private readonly Option _option;
@@ -624,9 +624,30 @@ namespace TEAMModelBI.Controllers.BITest
             int year = DateTimeOffset.UtcNow.Year;
             int month = DateTimeOffset.UtcNow.Month;
 
+            
+
             List<string> strList = monthsOfYear("2021-1");
 
-            var ere = DateTimeOffset.Parse("2024-02");
+            var ere = DateTimeOffset.Parse("2022-04-25");
+
+            DateTimeOffset ste = new();
+            if (ere.Month > 9)
+            {
+                ste = new DateTime(ere.Year, ere.Month - 4, 1);
+            }
+            else 
+            {
+                ste = new DateTime(ere.Year - 1, ere.Month, 1);
+            }
+
+            var (start1, end1) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow);
+            var (start2, end2) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "week");
+            var (start3, end3) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "lastweek");
+            var (start4, end4) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "term");
+            var (start5, end5) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "lastterm");
+            var (start6, end6) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "month");
+            var (start7, end7) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "year");
+
             var start = GetMonthStart(ere);
             var end = GetMonthEnd(ere);
 
@@ -634,12 +655,8 @@ namespace TEAMModelBI.Controllers.BITest
             List<MonthStartEnd> endList1 = TimeHelper.GetYearMonthlyStartEnd13(DateTimeOffset.UtcNow.Year);
             //return Ok(new { strList, dateTime, year, start, end, endList, endList1, endList2 });
 
-            var (start1, end1) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow);
-            var (start2, end2) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow,"week");
-            var (start3, end3) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow,"month");
-            var (start4, end4) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "year");
 
-            return Ok(new { start1, end1, start2, end2, start3, end3, start4, end4 });
+            return Ok(new { start1, end1, start2, end2, start3, end3, start4, end4 , start5, end5 , start6, end6 , start7, end7 });
         }
 
         public static List<string> monthsOfYear(string yearMonth)
@@ -995,7 +1012,7 @@ namespace TEAMModelBI.Controllers.BITest
         [HttpPost("get-test")]
         public async Task<IActionResult> GetTest(JsonElement jsonElement)
         {
-            List<Test> list1 = new List<Test>();
+            List<Test> list1 = new();
             list1.Add(new Test { score = 10, name = "001" });
             list1.Add(new Test { score = 20, name = "002" });
             list1.Add(new Test { score = 30, name = "003" });
@@ -1003,7 +1020,7 @@ namespace TEAMModelBI.Controllers.BITest
             list1.Add(new Test { score = 50, name = "005" });
             list1.Add(new Test { score = 60, name = "006" });
 
-            List<Test> list2 = new List<Test>();
+            List<Test> list2 = new();
             list2.Add(new Test { score = 40, name = "004" });
             list2.Add(new Test { score = 50, name = "005" });
             list2.Add(new Test { score = 60, name = "006" });
@@ -1021,7 +1038,6 @@ namespace TEAMModelBI.Controllers.BITest
 
             //List<Test> mergedList = new List<Test>(list1);
             //mergedList.AddRange(list2.Except(list1));
-
             // var mergedList = list1.Union(list2);
 
             var en2 = list1.Concat(list2).Except(list1.Intersect(list2));// 容斥原理
@@ -1030,16 +1046,87 @@ namespace TEAMModelBI.Controllers.BITest
 
             var cha = bingji.GroupBy(x => x.score).Select(c => c.First()).ToList();
 
-
             var jiaoji = list1.Intersect(list2).ToList();//交集
             var chaji = list1.Except(list2).ToList();//差集
 
             List<Test> have = list1.Where(a => !list2.Exists(t => a.score == t.score)).ToList();
             have.AddRange(list2.Where(a => !list1.Exists(t => a.score == t.score)).ToList());
 
-            return Ok(new { state = 200, have,  en2, bingji, cha, jiaoji, chaji, list3, list4, temp, list1 });
+            var ss = DateTime.Now.AddDays(0 - Convert.ToInt16(DateTime.Now.DayOfWeek) - 7).ToString("yyyy-MM-dd");//上周日
+            var sss = DateTime.Now.AddDays(6 - Convert.ToInt16(DateTime.Now.DayOfWeek) - 7).ToString("yyyy-MM-dd");//上周六
+
+            var ssss = DateTime.Now.AddDays(1 - Convert.ToInt16(DateTime.Now.DayOfWeek) - 7+1).ToString("yyyy-MM-dd");//上周一
+            var sssss = DateTime.Now.AddDays(7 - Convert.ToInt16(DateTime.Now.DayOfWeek) - 7+1).ToString("yyyy-MM-dd");//上周日
+
+            //计算上周
+            var date = DateTime.Now;
+            var m = (date.DayOfWeek == DayOfWeek.Sunday ? (DayOfWeek)7 : date.DayOfWeek) - DayOfWeek.Monday;
+            var s = (date.DayOfWeek == DayOfWeek.Sunday ? (DayOfWeek)7 : date.DayOfWeek) - (DayOfWeek)7;
+            var Mon = date.AddDays((-7 - m)).ToString("yyyy-MM-dd");
+            var Sun = date.AddDays((-7 - s)).ToString("yyyy-MM-dd");
+
+            var (lastWeekStart, lastWeekEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "lastweek");
+
+            return Ok(new { state = 200, Mon,Sun, ss, sss, ssss, sssss, lastWeekStart, lastWeekEnd, have, en2, bingji, cha, jiaoji, chaji, list3, list4, temp, list1 });
         }
 
+
+        /// <summary>
+        ///  异步并行
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost("get-parallelforeach")]
+        public async Task<IActionResult> GetParallelForEach() 
+        {
+
+            //string ser= $"SELECT Id,name,ownership,modifiedDate FROM {5('TableName')}WHERE ORDER BY Id fetch_size": { variables('Rows')}, "cursor": "";
+            var cosmosClient = _azureCosmos.GetCosmosClient();
+            List<string> schoolIds = await CommonFind.FindSchoolIds(cosmosClient, $"1636016499");
+            List<School> schools = new List<School>();
+            Parallel.ForEach(schoolIds, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, async item =>
+            {
+
+                School school = new();
+                var response = await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(item, new PartitionKey("Base"));
+                if (response.Status == 200)
+                {
+                    using var json = await JsonDocument.ParseAsync(response.ContentStream);
+                    school = json.ToObject<School>();
+                }
+                schools.Add(school);
+            });
+
+            return Ok(new { state = 200 , schools });
+        }
+
+        /// <summary>
+        /// table 表分页查询
+        /// </summary>
+        /// <param name="jsonElement"></param>
+        /// <returns></returns>
+        [HttpPost("get-tablepage")]
+        public async Task<IActionResult> GetTablePage(JsonElement jsonElement) 
+        {
+            jsonElement.TryGetProperty("page", out JsonElement jpage);
+            jsonElement.TryGetProperty("size", out JsonElement jsize);
+            int page = string.IsNullOrEmpty($"{jsize}") ?1: int.Parse($"{jpage}");
+            int size = string.IsNullOrEmpty($"{jsize}") ? 2 : int.Parse($"{jsize}");
+            var table = _azureStorage.GetCloudTableClient().GetTableReference("BIOptLog");
+
+            List<BIOptLog> operateLogs = new();
+            //lambda 表达式排序
+            operateLogs = await table.QueryWhereString<BIOptLog>();
+            //lambda 排序 降序
+            operateLogs.Sort((x, y) => y.time.CompareTo(x.time));
+            List<BIOptLog> bIOptLogs = operateLogs.Skip((page - 1) * size).Take(size).ToList();
+
+
+
+            return Ok(new { state = 200, allcount = operateLogs.Count, bIOptLogs });
+        }
+
+
+        
         public class Test
         {
             public int age { get; set; }

+ 215 - 2
TEAMModelBI/Controllers/Census/ProductStatisController.cs

@@ -28,12 +28,12 @@ namespace TEAMModelBI.Controllers.Census
         /// <summary>
         /// 软体产品
         /// </summary>
-        private readonly Dictionary<string, string> _serials = new Dictionary<string, string>() { { "3222NIYD", "ezStation 2" }, { "J223IZ6M", "HiTeach STD" }, { "3222C6D2", "HiTeach TBL" }, { "J223IZAM", "HiTeach PRO" }, { "J2236ZCX", "HiTeach Lite" }, { "3222DNG2", "HiTeach Mobile" }, { "3222IAVN", "HiTeach Premium" }, { "BYJ6LZ6Z", "HiTeach5" } };
+        private readonly Dictionary<string, string> _serials = new() { { "3222NIYD", "ezStation 2" }, { "J223IZ6M", "HiTeach STD" }, { "3222C6D2", "HiTeach TBL" }, { "J223IZAM", "HiTeach PRO" }, { "J2236ZCX", "HiTeach Lite" }, { "3222DNG2", "HiTeach Mobile" }, { "3222IAVN", "HiTeach Premium" }, { "BYJ6LZ6Z", "HiTeach5" } };
 
         /// <summary>
         /// 服务产品
         /// </summary>
-        private readonly Dictionary<string, string> _services = new Dictionary<string, string>() { { "YMPCVCIM", "学情分析模组" }, { "IPDYZYLC", "智慧学校管理服务" }, { "3CLYJ6NP", "AClass ONE智慧学伴" }, { "IPALJ6NY", "数据储存服务空间" }, { "VABAJ6NV", "卷卡合一阅卷系统" } };
+        private readonly Dictionary<string, string> _services = new() { { "YMPCVCIM", "学情分析模组" }, { "IPDYZYLC", "智慧学校管理服务" }, { "3CLYJ6NP", "AClass ONE智慧学伴" }, { "IPALJ6NY", "数据储存服务空间" }, { "VABAJ6NV", "卷卡合一阅卷系统" } };
 
         public ProductStatisController(AzureCosmosFactory azureCosmos, DingDing dingDing, IOptionsSnapshot<Option> option) 
         {
@@ -268,6 +268,219 @@ namespace TEAMModelBI.Controllers.Census
             return Ok(new { state = 200, schoolProducts }) ;
         }
 
+        /// <summary>
+        /// 所有模组统计
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost("get-moduleanalys")]
+        public async Task<IActionResult> GetModuleAnalys(JsonElement jsonElement)
+        {
+            jsonElement.TryGetProperty("tmdId", out JsonElement tmdId);
+            var cosmosClient = _azureCosmos.GetCosmosClient();
+
+            long buyServiceCount = 0; //所有购买服务数量
+            long buySerialsCount = 0; //所有购买软体数量
+
+            long lastYearServiceCount = 0;   //去年购买服务数量
+            long yearServiceCount = 0;    //今年购买服务数量
+
+            long lastYearSerialsCount = 0;  //去年购买软体数量
+            long yearSerialsCount = 0;     //今年购买软体数量
+
+            var (lastYearStart, lastYearEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.Parse($"{DateTimeOffset.UtcNow.Year - 1}-1-1"), "year", false); //计算去年开始/结束时间
+            var (yearStart, yearEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "year", false);                                     //计算今年开始/结束时间
+
+            if (!string.IsNullOrEmpty($"{tmdId}"))
+            {
+                List<RecSchoolAnalys> schoolAnalys = new();
+                List<string> schoolIds = await CommonFind.FindSchoolIds(cosmosClient, $"{tmdId}");
+
+                foreach (var scId in schoolIds)
+                {
+                    //软体产品
+                    List<SchoolProductSumData> Serials = new();
+                    //软体产品
+                    List<SchoolProductSumData> Services = new();
+                    School school = new();
+                    var response = await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(scId, new PartitionKey("Base"));
+                    if (response.Status == 200)
+                    {
+                        using var json = await JsonDocument.ParseAsync(response.ContentStream);
+                        school = json.ToObject<School>();
+                    }
+
+                    await foreach (var item in cosmosClient.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: $"SELECT c.serial,c.service FROM c where c.id='{scId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("ProductSum") }))
+                    {
+                        using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                        foreach (var itemCount in json.RootElement.GetProperty("Documents").EnumerateArray())
+                        {
+                            //软体产品
+                            Serials.AddRange(itemCount.GetProperty("serial").ToObject<List<SchoolProductSumData>>().Where(x => x.avaliable > 0).ToList());
+
+                            //服务产品
+                            Services.AddRange(itemCount.GetProperty("service").ToObject<List<SchoolProductSumData>>().Where(x => x.avaliable > 0).ToList());
+                        }
+                    }
+
+                    long tempBuySerc = await CommonFind.FindTotals(cosmosClient, "select count(c.id) as totals from c where c.dataType='service' and c.pk='Product'", "School", $"Product-{scId}");
+                    long tempBuySeri = await CommonFind.FindTotals(cosmosClient, "select count(c.id) as totals from c where c.dataType='serial' and c.pk='Product'", "School", $"Product-{scId}");
+
+                    string sqlTxt = "select count(c.id) as totals from c where c.pk='Product' and c.dataType='{0}' and c.regDate>={1} and c.regDate<={2}";
+                    long tempLastYearSerc = await CommonFind.FindTotals(cosmosClient, string.Format(sqlTxt, "service", lastYearStart, lastYearEnd), "School", $"Product-{scId}");  //计算去年购买的服务数量
+                    long tempyearSerc = await CommonFind.FindTotals(cosmosClient, string.Format(sqlTxt, "service", yearStart, yearEnd), "School", $"Product-{scId}");    //今年购买的服务数量
+
+                    long tempLastYearSeri = await CommonFind.FindTotals(cosmosClient, string.Format(sqlTxt, "serial", lastYearStart, lastYearEnd), "School", $"Product-{scId}");  //计算去年购买的软体数量
+                    long tempYearSeri = await CommonFind.FindTotals(cosmosClient, string.Format(sqlTxt, "serial", yearStart, yearEnd), "School", $"Product-{scId}");    //今年购买的软体数量
+
+                    schoolAnalys.Add(new RecSchoolAnalys { id = school.id, name = school.name, picture = school.picture, size = school.size, scale = school.scale, buySerc = tempBuySerc, lastYearSerc = tempLastYearSerc, yearSerc = tempyearSerc, buySeri = tempBuySeri, lastYearSeri = tempLastYearSeri, yearSeri = tempYearSeri });
+
+                    lastYearServiceCount += tempLastYearSerc;
+                    yearServiceCount += tempyearSerc;
+                    buyServiceCount += tempBuySerc;
+
+                    lastYearSerialsCount += tempLastYearSeri;
+                    yearSerialsCount += tempYearSeri;
+                    buySerialsCount += tempBuySeri;
+                }
+
+                return Ok(new { state = 200, buyServiceCount, buySerialsCount, lastYearServiceCount, yearServiceCount, lastYearSerialsCount, yearSerialsCount, schoolAnalys });
+            }
+            else
+            {
+
+                //软体产品
+                List<SchoolProductSumData> Serials = new();
+                //软体产品
+                List<SchoolProductSumData> Services = new();
+
+                await foreach (var item in cosmosClient.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: $"SELECT c.serial,c.service FROM c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("ProductSum") }))
+                {
+                    using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                    foreach (var itemCount in json.RootElement.GetProperty("Documents").EnumerateArray())
+                    {
+                        //软体产品
+                        Serials.AddRange(itemCount.GetProperty("serial").ToObject<List<SchoolProductSumData>>().Where(x => x.avaliable > 0).ToList());
+
+                        //服务产品
+                        Services.AddRange(itemCount.GetProperty("service").ToObject<List<SchoolProductSumData>>().Where(x => x.avaliable > 0).ToList());
+                    }
+                }
+
+                var serialsCout = Serials.GroupBy(kv => new { kv.prodCode }).Select(y => new { key = y.Key.prodCode, value = y.Count() }).ToList();
+                var servicesCout = Services.GroupBy(kv => new { kv.prodCode }).Select(y => new { key = y.Key.prodCode, value = y.Count() }).ToList();
+
+                List<string> containers = new List<string>() { "School" };
+                buyServiceCount = await CommonFind.FindTotals(cosmosClient, "select count(c.id) as totals from c where c.dataType='service' and c.pk='Product'", containers);
+                buySerialsCount = await CommonFind.FindTotals(cosmosClient, "select count(c.id) as totals from c where c.dataType='serial' and c.pk='Product'", containers);
+
+                string sqlTxt = "select count(c.id) as totals from c where c.pk='Product' and c.dataType='{0}' and c.regDate>={1} and c.regDate<={2}";
+                lastYearServiceCount = await CommonFind.FindTotals(cosmosClient, string.Format(sqlTxt, "service", lastYearStart, lastYearEnd), containers);  //计算去年购买的服务数量
+                yearServiceCount = await CommonFind.FindTotals(cosmosClient, string.Format(sqlTxt, "service", yearStart, yearEnd), containers);    //今年购买的服务数量
+
+                lastYearSerialsCount = await CommonFind.FindTotals(cosmosClient, string.Format(sqlTxt, "serial", lastYearStart, lastYearEnd), containers);  //计算去年购买的软体数量
+                yearSerialsCount = await CommonFind.FindTotals(cosmosClient, string.Format(sqlTxt, "serial", yearStart, yearEnd), containers);    //今年购买的软体数量
+
+                return Ok(new { state = 200, buyServiceCount, buySerialsCount, lastYearServiceCount, yearServiceCount, lastYearSerialsCount, yearSerialsCount, serialsCout, servicesCout });
+            }
+        }
+
+        /// <summary>
+        /// 顾问关联的学校模组
+        /// </summary>
+        /// <param name="jsonElement"></param>
+        /// <returns></returns>
+        [HttpPost("get-adviserschool")]
+        public async Task<IActionResult> GetAdviserSchool(JsonElement jsonElement) 
+        {
+            if (!jsonElement.TryGetProperty("tmdId", out JsonElement tmdId)) return BadRequest();
+
+            long buyServiceCount = 0;  //购买服务数量
+            long buySerialsCount = 0;  //购买软体数量
+
+            long lastYearServiceCount = 0;   //去年购买服务数量
+            long yearServiceCount = 0;    //今年购买服务数量
+
+            long lastYearSerialsCount = 0;  //去年购买软体数量
+            long yearSerialsCount = 0;     //今年购买软体数量
+            List<RecSchoolAnalys> schoolAnalys = new();
+            var (lastYearStart, lastYearEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.Parse($"{DateTimeOffset.UtcNow.Year - 1}-1-1"), "year", false); //计算去年开始/结束时间
+            var (yearStart, yearEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "year", false);                                     //计算今年开始/结束时间
+
+            var cosmosClient = _azureCosmos.GetCosmosClient();
+            List<string> schoolIds = await CommonFind.FindSchoolIds(cosmosClient, $"{tmdId}");
+
+            foreach (var scId in schoolIds)
+            {
+                //软体产品
+                List<SchoolProductSumData> Serials = new();
+                //软体产品
+                List<SchoolProductSumData> Services = new();
+                School school = new();
+                var response = await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(scId, new PartitionKey("Base"));
+                if (response.Status == 200)
+                {
+                    using var json = await JsonDocument.ParseAsync(response.ContentStream);
+                    school = json.ToObject<School>();
+                }
+
+                await foreach (var item in cosmosClient.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: $"SELECT c.serial,c.service FROM c where c.id='{scId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("ProductSum") }))
+                {
+                    using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                    foreach (var itemCount in json.RootElement.GetProperty("Documents").EnumerateArray())
+                    {
+                        //软体产品
+                        Serials.AddRange(itemCount.GetProperty("serial").ToObject<List<SchoolProductSumData>>().Where(x => x.avaliable > 0).ToList());
+
+                        //服务产品
+                        Services.AddRange(itemCount.GetProperty("service").ToObject<List<SchoolProductSumData>>().Where(x => x.avaliable > 0).ToList());
+                    }
+                }
+
+                long tempBuySerc = await CommonFind.FindTotals(cosmosClient, "select count(c.id) as totals from c where c.dataType='service' and c.pk='Product'", "School", $"Product-{scId}");
+                long  tempBuySeri = await CommonFind.FindTotals(cosmosClient, "select count(c.id) as totals from c where c.dataType='serial' and c.pk='Product'", "School", $"Product-{scId}");
+
+                string sqlTxt = "select count(c.id) as totals from c where c.pk='Product' and c.dataType='{0}' and c.regDate>={1} and c.regDate<={2}";
+                long  tempLastYearSerc = await CommonFind.FindTotals(cosmosClient, string.Format(sqlTxt, "service", lastYearStart, lastYearEnd), "School", $"Product-{scId}");  //计算去年购买的服务数量
+                long tempyearSerc = await CommonFind.FindTotals(cosmosClient, string.Format(sqlTxt, "service", yearStart, yearEnd), "School", $"Product-{scId}");    //今年购买的服务数量
+
+                long tempLastYearSeri = await CommonFind.FindTotals(cosmosClient, string.Format(sqlTxt, "serial", lastYearStart, lastYearEnd), "School", $"Product-{scId}");  //计算去年购买的软体数量
+                long tempYearSeri = await CommonFind.FindTotals(cosmosClient, string.Format(sqlTxt, "serial", yearStart, yearEnd), "School", $"Product-{scId}");    //今年购买的软体数量
+
+                schoolAnalys.Add(new RecSchoolAnalys { id = school.id, name = school.name, picture = school.picture, size = school.size, scale = school.scale, buySerc = tempBuySerc, lastYearSerc = tempLastYearSerc, yearSerc = tempyearSerc, buySeri = tempBuySeri, lastYearSeri = tempLastYearSeri, yearSeri = tempYearSeri });
+
+                lastYearServiceCount += tempLastYearSerc;
+                yearServiceCount += tempyearSerc;
+                buyServiceCount += tempBuySerc;
+
+                lastYearSerialsCount += tempLastYearSeri;
+                yearSerialsCount += tempYearSeri;
+                buySerialsCount += tempBuySeri;
+            }
+
+            return Ok(new { state = 200, buyServiceCount, buySerialsCount, lastYearServiceCount, yearServiceCount, lastYearSerialsCount, yearSerialsCount, schoolAnalys });
+        }
+
+
+        /// <summary>
+        /// 显示学校的模组分析
+        /// </summary>
+        public record RecSchoolAnalys 
+        {
+            public string id { get; set; }
+            public string name { get; set; }
+            public string picture { get; set; }
+            public int size { get; set; }
+            public int scale { get; set; }
+            public long buySerc { get; set; }
+            public long lastYearSerc { get; set; }
+            public long yearSerc { get; set; }
+            public long buySeri { get; set; }
+            public long lastYearSeri { get; set; }
+            public long yearSeri { get; set; }
+        }
+
+
+
         public record SchoolProduct 
         {
             public string id { get; set; }

+ 169 - 22
TEAMModelBI/Controllers/Census/SchoolController.cs

@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using System.Text;
 using System.Text.Json;
 using System.Threading.Tasks;
@@ -34,7 +35,7 @@ namespace TEAMModelBI.Controllers.Census
         }
 
         /// <summary>
-        /// 统计所有学校
+        /// 统计所有分析:基础、课例、活动、资源 
         /// </summary>
         /// <param name="jsonElement"></param>
         /// <returns></returns>
@@ -43,43 +44,183 @@ namespace TEAMModelBI.Controllers.Census
         {
             var cosmosClient = _azureCosmos.GetCosmosClient();
 
+            long schoolCount = 0;   //学校数量
+            int tecCount = 0;  //教师数量
+            int stuCount = 0;  //学生数量
+            long roomCount = 0;  //教室数量
+            long wisdomRoomCount = 0; //智慧教室数量
+            long allClassCount = 0;  //所有班级
+
             long allLessCount = 0; //所有课例          
             long lastYearLessCount = 0; //去年课例
             long yearLessCount = 0;//今年课例
+            long lastWeekLessCount = 0; //上周课例
+            long weekLessCount = 0;   //本周课例
+            long lastTermLessCount = 0; //上学期课例
+            long termLessCount = 0;  //本学期课例
+
             long allActivityCount = 0; //所有活动
             long lastActivityCount = 0; //去年活动
             long activityCount = 0;   //今年活动
-            int tecCount = 0;  //教师数量
-            int stuCount = 0;  //学生数量
-            long roomCount = 0;  //教师数量
-
+            long lastWeekActivitCount = 0; //上周活动
+            long weekActivitCount = 0;  //本周活动
+            long lastTermActivitCount =0;  //上学期活动
+            long TermActivitCount = 0;  //本学期学期活动
+            
+            long rercCount = 0;         //所有资源数量
+            long weekRercCount = 0;     //本周资源数量
+            long lastWeekRercCount = 0; //上周资源数量
+            long lastTermRercCount = 0; //上学期资源
+            long termRercCount = 0; //本学期资源
+            long lastYearRercCount = 0; //去年资源
+            long yearRercCount = 0; //去年资源
 
+            var (lastYearStart, lastYearEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.Parse($"{DateTimeOffset.UtcNow.Year - 1}-1-1"), "year"); //计算去年开始/结束时间
+            var (yearStart, yearEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "year");         //计算今年开始/结束时间
+            var (lastWeekStart, lastWeekEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "lastweek");   //计算上周开始/结束时间
+            var (weekStart, weekEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "week");             //计算本周开始/结束时间
+            var (lastTermStart, lastTermEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "lastterm");   //计算上学期开始/结束时间
+            var (termStart, termEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "term");   //计算本学期开始/结束时间
 
-            var (lastYearStart, lastYearEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.Parse($"{DateTimeOffset.UtcNow.Year - 1}-1-1"), "year");
-            var (yearStart, yearEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "year");
+            schoolCount = await CommonFind.FindTotals(cosmosClient, "select count(c.id) totals from c", "School", "Base");  //所有学校数量
+            tecCount = await CommonFind.FindTotals(cosmosClient, "select count(c.id) totals from c", "Teacher", "Base");  //所有教师数量
+            stuCount = await CommonFind.FindTotals(cosmosClient, "select count(c.id) totals from c", "Student", "Base");    //所有学生数量
+            allClassCount = await CommonFind.FindTotals(cosmosClient, "select count(c.id) totals from c where c.pk = 'Class'", new List<string>() { "School" }); //所有班级数量
+            roomCount = await CommonFind.FindTotals(cosmosClient, "select count(c.id) totals from c where c.pk = 'Room'", new List<string>() { "School" }); //所有教室数量
+            wisdomRoomCount = await CommonFind.FindTotals(cosmosClient, "select count(c.id) totals from c where c.pk = 'Room' and c.serial != null", new List<string>() { "School" }); //智慧教室数量
 
-            List<string> schoolIds = await CommonFind.FindSchoolIds(cosmosClient, "select c.id from c", "Base");
-            tecCount = await CommonFind.FindTotals(cosmosClient, "select count(c.id) totals from c", "Teacher", "Base");
-            stuCount = await CommonFind.FindTotals(cosmosClient, "select count(c.id) totals from c", "Student", "Base");
-            roomCount = await CommonFind.FindTotals(cosmosClient, "select count(c.id) totals from c where c.pk = 'Room' and c.serial != null", new List<string>() { "School" });
+            List<string> containers = new() { "School", "Teacher" };
+            string lessSqlTxt = "select count(c.id) as totals from c where c.pk='LessonRecord' and c.startTime>={0} and c.startTime<={1}";
 
+            allLessCount = await CommonFind.FindTotals(cosmosClient, $"select count(c.id) as totals from c where c.pk='LessonRecord'", containers);//所有课例
+            lastYearLessCount = await CommonFind.FindTotals(cosmosClient, string.Format(lessSqlTxt, lastYearStart, lastYearEnd), containers);  //去年课例            
+            yearLessCount = await CommonFind.FindTotals(cosmosClient, string.Format(lessSqlTxt, yearStart, yearEnd), containers); //今年课例
+            lastWeekLessCount = await CommonFind.FindTotals(cosmosClient, string.Format(lessSqlTxt, lastWeekStart, lastWeekEnd), containers); //上周课例
+            weekLessCount = await CommonFind.FindTotals(cosmosClient, string.Format(lessSqlTxt, weekStart, weekEnd), containers); //本周课例
+            lastTermLessCount = await CommonFind.FindTotals(cosmosClient, string.Format(lessSqlTxt, lastTermStart, lastTermEnd), containers); //上学期课例
+            termLessCount = await CommonFind.FindTotals(cosmosClient, string.Format(lessSqlTxt, termStart, termEnd), containers); //上学期课例
 
-            //所有课例
-            allLessCount = await CommonFind.FindTotals(cosmosClient, $"select count(c.id) as totals from c where c.pk='LessonRecord'", new List<string>() { "School", "Teacher" });
-            //去年课例
-            lastYearLessCount = await CommonFind.FindTotals(cosmosClient, $"select count(c.id) as totals from c where c.pk='LessonRecord' and c.startTime>={lastYearStart} and c.startTime<={lastYearEnd}", new List<string>() { "School", "Teacher" });
-            //今年课例
-            yearLessCount = await CommonFind.FindTotals(cosmosClient, $"select count(c.id) as totals from c where c.pk='LessonRecord' and c.startTime>={yearStart} and c.startTime<={yearEnd}", new List<string>() { "School", "Teacher" });
+            List<string> containerTypes = new() { "Common" };
+            string typeSqlTxt = "select count(c.id) as totals from c where c.pk='{0}' and c.createTime >={1} and c.createTime<= {2}";
 
-            //活动
             foreach (var type in StaticValue.activityTypes)
             {
-                allActivityCount += await CommonFind.FindTotals(cosmosClient, $"select count(c.id) as totals from c where c.pk='{type}' ", new List<string>() { "Common" });
-                lastActivityCount += await CommonFind.FindTotals(cosmosClient, $"select count(c.id) as totals from c where c.pk='{type}' and c.createTime >={lastYearStart} and c.createTime<= {lastYearEnd}", new List<string>() { "Common" });
-                activityCount += await CommonFind.FindTotals(cosmosClient, $"select count(c.id) as totals from c where c.pk='{type}' and c.createTime >={yearStart} and c.createTime<= {yearEnd}", new List<string>() { "Common" });
+                allActivityCount += await CommonFind.FindTotals(cosmosClient, $"select count(c.id) as totals from c where c.pk='{type}' ", containerTypes);//所有活动
+                lastActivityCount += await CommonFind.FindTotals(cosmosClient, string.Format(typeSqlTxt, type, lastYearStart, lastYearEnd), containerTypes);  //去年活动
+                activityCount += await CommonFind.FindTotals(cosmosClient, string.Format(typeSqlTxt, type, yearStart, yearEnd), containerTypes); //今年活动
+                lastWeekActivitCount += await CommonFind.FindTotals(cosmosClient, string.Format(typeSqlTxt, type, lastWeekStart, lastWeekEnd), containerTypes); //上周活动
+                weekActivitCount += await CommonFind.FindTotals(cosmosClient, string.Format(typeSqlTxt, type, weekStart, weekEnd), containerTypes);    //本周活动
+                lastTermActivitCount += await CommonFind.FindTotals(cosmosClient, string.Format(typeSqlTxt, type, lastTermStart, lastTermEnd), containerTypes);  //上学期活动
+                TermActivitCount += await CommonFind.FindTotals(cosmosClient, string.Format(typeSqlTxt, type, termStart, termEnd), containerTypes);  //本学期学期活动
+            }
+
+            string bloblSqlTxt = "select count(c.id) as totals from c where c.pk='Bloblog' and c.time>={0} and c.time<={1}";
+
+            rercCount = await CommonFind.FindTotals(cosmosClient, "select count(c.id) as totals from c where c.pk='Bloblog'", containers);  //所有资源
+            lastWeekRercCount = await CommonFind.FindTotals(cosmosClient, string.Format(bloblSqlTxt, lastWeekStart, lastWeekEnd), containers);   //上周资源
+            weekRercCount = await CommonFind.FindTotals(cosmosClient, string.Format(bloblSqlTxt, weekStart, weekEnd), containers);   //本周资源
+            lastTermRercCount = await CommonFind.FindTotals(cosmosClient, string.Format(bloblSqlTxt, lastTermStart, lastTermEnd), containers);   //上学期资源
+            termRercCount = await CommonFind.FindTotals(cosmosClient, string.Format(bloblSqlTxt, termStart, termEnd), containers);   //这学期资源
+            lastYearRercCount = await CommonFind.FindTotals(cosmosClient, string.Format(bloblSqlTxt, lastYearStart, lastYearEnd), containers);   //去年资源
+            yearRercCount = await CommonFind.FindTotals(cosmosClient, string.Format(bloblSqlTxt, yearStart, yearEnd), containers);   //今年资源
+
+            return Ok(new { state = 200, schoolCount, tecCount, stuCount, allClassCount, roomCount, wisdomRoomCount, allLessCount, lastYearLessCount, yearLessCount, lastWeekLessCount, weekLessCount, lastTermLessCount, termLessCount, allActivityCount, lastActivityCount, activityCount, lastWeekActivitCount, weekActivitCount, lastTermActivitCount, TermActivitCount, rercCount, lastWeekRercCount, weekRercCount, lastTermRercCount, termRercCount, lastYearRercCount, yearRercCount });
+        }
+
+        /// <summary>
+        /// 查询顾问相关的学校统计数据
+        /// </summary>
+        /// <param name="jsonElement"></param>
+        /// <returns></returns>
+        [HttpPost("get-assist")]
+        public async Task<IActionResult> GetAssistStatis(JsonElement jsonElement) 
+        {
+            if (!jsonElement.TryGetProperty("tmdId", out JsonElement tmdId)) return BadRequest();
+
+            int tecCount = 0;   //教师数量
+            int stuCount = 0;   //学校数量
+            int classCount = 0; //班级数量
+            int roomCount = 0; //智慧教师数量
+            int allLessonCount = 0;  //课例数量
+            int lastWeekLessCount = 0;// 上周课例数
+            int weekLessCount = 0;   //本周课例
+            int lastTermLessCount = 0;// 上学期课例数
+            int termLessCount = 0;   //本学期课例
+            int lastYearLessCount = 0;  //去年课例
+            int yearLessCount = 0;  //今年课例
+
+            int allBloblog = 0;  //学校资源
+            int lastYearBloblog = 0;  //去年学校资源
+            int yearBloblog  = 0;  //今年学校资源
+
+            long allActivity = 0;  //学校所有活动
+            long lastYearActivity = 0;  //去年学校所有活动
+            long yearActivity = 0;  //今年学校所有活动
+            long lastWeekActivity = 0;  //上周学校所有活动
+            long weekActivity = 0;  //本周学校所有活动
+
+            var cosmosClient = _azureCosmos.GetCosmosClient();
+            List<string> schoolIds = await CommonFind.FindSchoolIds(cosmosClient, $"{tmdId}");
+
+            var (lastWeekStart, lastWeekEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "lastweek");   //计算上周开始/结束时间
+            var (weekStart, weekEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "week");             //计算本周开始/结束时间
+            var (lastTermStart, lastTermEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "lastterm");   //计算上学期开始/结束时间
+            var (termStart, termEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "term");   //计算本学期开始/结束时间
+            var (lastYearStart, lastYearEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.Parse($"{DateTimeOffset.UtcNow.Year - 1}-1-1"), "year"); //计算去年开始/结束时间
+            var (yearStart, yearEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "year");         //计算今年开始/结束时间
+
+            string unifySqlTxt = "select count(c.id) as totals from c";
+            string unifyTimeSql = "select count(c.id) as totals from c where c.startTime>={0} and c.startTime<={1}";
+            string blobTimeSql = "select count(c.id) as totals from c where c.time>={0} and c.time<={1}";
+            List<RecSchoolDate> recSchoolDates = new();
+
+            foreach (var itemId in schoolIds)
+            {
+                School school = new();
+                var response = await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(itemId, new PartitionKey("Base"));
+                if (response.Status == 200)
+                {
+                    using var json = await JsonDocument.ParseAsync(response.ContentStream);
+                    school = json.ToObject<School>();
+                }
+                tecCount += await CommonFind.FindTotals(cosmosClient, $"select count(c.id) as totals from c where ARRAY_CONTAINS(c.roles,'teacher',true) and c.status = 'join'", "School", $"Teacher-{itemId}");
+                stuCount += await CommonFind.FindTotals(cosmosClient, unifySqlTxt, "Student", $"Base-{itemId}");
+                classCount += await CommonFind.FindTotals(cosmosClient, unifySqlTxt, "School", $"Class-{itemId}");
+                roomCount += await CommonFind.FindTotals(cosmosClient, unifySqlTxt, "School", $"Room-{itemId}");
+                //学校所有课例
+                int tempLessCount   = await CommonFind.FindTotals(cosmosClient, unifySqlTxt, "School", $"LessonRecord-{itemId}");
+                lastWeekLessCount += await CommonFind.FindTotals(cosmosClient, string.Format(unifyTimeSql,lastWeekStart,lastWeekEnd), "School", $"LessonRecord-{itemId}");
+                weekLessCount += await CommonFind.FindTotals(cosmosClient, string.Format(unifyTimeSql, weekStart, weekEnd), "School", $"LessonRecord-{itemId}");
+                lastTermLessCount += await CommonFind.FindTotals(cosmosClient, string.Format(unifyTimeSql, lastTermStart, lastTermEnd), "School", $"LessonRecord-{itemId}");
+                termLessCount += await CommonFind.FindTotals(cosmosClient, string.Format(unifyTimeSql, termStart, termEnd), "School", $"LessonRecord-{itemId}");
+                lastYearLessCount += await CommonFind.FindTotals(cosmosClient, string.Format(unifyTimeSql, lastYearStart, lastYearEnd), "School", $"LessonRecord-{itemId}");
+                yearLessCount += await CommonFind.FindTotals(cosmosClient, string.Format(unifyTimeSql, yearStart, yearEnd), "School", $"LessonRecord-{itemId}");
+
+                //学校资源
+                int tempBloblog = await CommonFind.FindTotals(cosmosClient, unifySqlTxt, "School", $"Bloblog-{itemId}");
+                lastYearBloblog += await CommonFind.FindTotals(cosmosClient, string.Format(blobTimeSql,lastYearStart,lastYearEnd), "School", $"Bloblog-{itemId}");
+                yearBloblog += await CommonFind.FindTotals(cosmosClient, string.Format(blobTimeSql, yearStart, yearEnd), "School", $"Bloblog-{itemId}");
+
+                //学校活动
+                long tempallActivity = 0;
+                //统计活动
+                foreach (var type in StaticValue.activityTypes)
+                {
+                    string sqlTime = "SELECT count(c.id) as totals FROM c where c.pk = '{0}' and c.school = '{1}' and c.createTime>={2} and c.createTime<={3}";
+                    tempallActivity += await CommonFind.FindTotals(cosmosClient, $"SELECT count(c.id) as totals FROM c where c.pk = '{type}' and c.school = '{itemId}'", new List<string> { "Common" });
+                    lastYearActivity += await CommonFind.FindTotals(cosmosClient, string.Format(sqlTime,type,itemId, lastYearStart, lastYearEnd), new List<string> { "Common" });
+                    yearActivity += await CommonFind.FindTotals(cosmosClient, string.Format(sqlTime, type, itemId, yearStart, yearEnd), new List<string> { "Common" });
+                    lastWeekActivity += await CommonFind.FindTotals(cosmosClient, string.Format(sqlTime, type, itemId, lastWeekStart, lastWeekEnd), new List<string> { "Common" });
+                    weekActivity += await CommonFind.FindTotals(cosmosClient, string.Format(sqlTime, type, itemId, weekStart, weekEnd), new List<string> { "Common" });
+                }
+                allLessonCount += tempLessCount;
+                allActivity += tempallActivity;
+                allBloblog += tempBloblog;
+
+                recSchoolDates.Add(new RecSchoolDate() { id = school.id, name = school.name, dataCount = (tempLessCount + tempallActivity + tempBloblog) });
             }
 
-            return Ok(new { state = 200, scCount = schoolIds.Count, tecCount, stuCount, roomCount, allLessCount, lastYearLessCount, yearLessCount, allActivityCount, lastActivityCount, activityCount });
+            return Ok(new { state = 200, schoolCount = schoolIds.Count, tecCount, stuCount, classCount, roomCount, allLessonCount, lastWeekLessCount, weekLessCount, lastTermLessCount, termLessCount, lastYearLessCount, yearLessCount,allBloblog , lastYearBloblog, yearBloblog , allActivity , lastYearActivity ,yearActivity , lastWeekActivity, weekActivity, recSchoolDates });
         }
 
         /// <summary>
@@ -107,6 +248,12 @@ namespace TEAMModelBI.Controllers.Census
             return Ok(new { state = 200, infos });
         }
 
+        public record RecSchoolDate 
+        {
+            public string id { get; set; }
+            public string name { get; set; }
+            public long dataCount { get; set; }
+        }
 
 
     }

+ 22 - 12
TEAMModelBI/Controllers/DingDingStruc/SystemConfigController.cs

@@ -250,7 +250,7 @@ namespace TEAMModelBI.Controllers.DingDingStruc
         }
 
         /// <summary>
-        /// 获取当前应用的
+        /// 获取所有的应用链接秘钥,以及当前应用链接秘钥
         /// </summary>
         /// <param name="jsonElement"></param>
         /// <returns></returns>
@@ -282,21 +282,31 @@ namespace TEAMModelBI.Controllers.DingDingStruc
                 return Ok(new { state = 200, allConfigs, currentSysConfig });
             }
             else {
-                currentSysConfig.site = currentSite;
-                currentSysConfig.nickName = currentSite;
+                currentSysConfig = new SysConfig();
+                currentSysConfig.site = $"{currentSite}";
+                currentSysConfig.nickName = $"{currentSite}";
                 currentSysConfig.proDeptId = long.Parse(_configuration["CustomParam:proDeptId"]);
 
-                currentSysConfig.clientKey.clientID = _configuration["HaBookAuth:CoreService:clientID"];
-                currentSysConfig.clientKey.clientSecret = _configuration["HaBookAuth:CoreService:clientSecret"];
+                currentSysConfig.clientKey = new()
+                {
+                    clientID = _configuration["HaBookAuth:CoreService:clientID"],
+                    clientSecret = _configuration["HaBookAuth:CoreService:clientSecret"]
+                };
 
-                currentSysConfig.dDAuth.agentId = _configuration["DingDingAuth:Agentld"];
-                currentSysConfig.dDAuth.appKey = _configuration["DingDingAuth:appKey"];
-                currentSysConfig.dDAuth.appSecret = _configuration["DingDingAuth:appSecret"];
+                currentSysConfig.dDAuth = new()
+                {
+                    agentId = _configuration["DingDingAuth:Agentld"],
+                    appKey = _configuration["DingDingAuth:appKey"],
+                    appSecret = _configuration["DingDingAuth:appSecret"]
+                };
 
-                currentSysConfig.azureClient.storage = _configuration["Azure:Storage:ConnectionString"];
-                currentSysConfig.azureClient.cosmos = _configuration["Azure:Cosmos:ConnectionString"];
-                currentSysConfig.azureClient.redis = _configuration["Azure:Redis:ConnectionString"];
-                currentSysConfig.azureClient.servicBus = _configuration["Azure:ServiceBus:ConnectionString"];
+                currentSysConfig.azureClient = new()
+                {
+                    storage = _configuration["Azure:Storage:ConnectionString"],
+                    cosmos = _configuration["Azure:Cosmos:ConnectionString"],
+                    redis = _configuration["Azure:Redis:ConnectionString"],
+                    servicBus = _configuration["Azure:ServiceBus:ConnectionString"]
+                };
 
                 return Ok(new { state = 201, allConfigs, currentSysConfig }); 
             }

+ 14 - 34
TEAMModelBI/JsonFile/SystemConfig.json

@@ -1,27 +1,27 @@
 [
   {
-    "site": "Chins",
-    "nickName": "正式站",
-    "proDeptId": 13456,
+    "site": "China-Dep",
+    "nickName": "BI-测试站",
+    "proDeptId": 67690917,
     "clientKey": {
-      "clientID": "021251",
-      "clientSecret": "200005463678"
+      "clientID": "c7317f88-7cea-4e48-ac57-a16071f7b884",
+      "clientSecret": "kguxh:V.PLmxBdaI@jnrTrDSth]A3346"
     },
     "dDAuth": {
-      "agentId": "12300456",
-      "appKey": "32001",
-      "appSecret": "12300456"
+      "agentId": "1290158212",
+      "appKey": "dingrucgsnt8p13rfbgd",
+      "appSecret": "Gyx_N57yZslhQOAhAPlvmCwOp_qTm1DScKbd5OoOE0URAW4eViYA2Sk_ZxKb-8WG"
     },
     "azureClient": {
-      "storage": "0000",
-      "cosmos": "1234005",
-      "redis": "224005",
-      "servicBus": "12300455"
+      "storage": "DefaultEndpointsProtocol=https;AccountName=teammodelstorage;AccountKey=Yq7D4dE6cFuer2d2UZIccTA/i0c3sJ/6ITc8tNOyW+K5f+/lWw9GCos3Mxhj47PyWQgDL8YbVD63B9XcGtrMxQ==;EndpointSuffix=core.chinacloudapi.cn",
+      "cosmos": "AccountEndpoint=https://cdhabookdep-free.documents.azure.cn:443/;AccountKey=JTUVk92Gjsx17L0xqxn0X4wX2thDPMKiw4daeTyV1HzPb6JmBeHdtFY1MF1jdctW1ofgzqkDMFOtcqS46by31A==;",
+      "redis": "52.130.252.100:6379,password=habook,ssl=false,abortConnect=False,writeBuffer=10240",
+      "servicBus": "Endpoint=sb://teammodelos.servicebus.chinacloudapi.cn/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=Sy4h4EQ8zP+7w/lOLi1X3tGord/7ShFHimHs1vC50Dc="
     }
   },
   {
-    "site": "Chins-214",
-    "nickName": "正式站-214",
+    "site": "Chins",
+    "nickName": "测试站",
     "proDeptId": 13456,
     "clientKey": {
       "clientID": "021251",
@@ -38,25 +38,5 @@
       "redis": "224005",
       "servicBus": "12300455"
     }
-  },
-  {
-    "site": "China-Dep",
-    "nickName": "测试站",
-    "proDeptId": 67690917,
-    "clientKey": {
-      "clientID": "c7317f88-7cea-4e48-ac57-a16071f7b884",
-      "clientSecret": "kguxh:V.PLmxBdaI@jnrTrDSth]A3346"
-    },
-    "dDAuth": {
-      "agentId": null,
-      "appKey": "dingrucgsnt8p13rfbgd",
-      "appSecret": "Gyx_N57yZslhQOAhAPlvmCwOp_qTm1DScKbd5OoOE0URAW4eViYA2Sk_ZxKb-8WG"
-    },
-    "azureClient": {
-      "storage": "DefaultEndpointsProtocol=https;AccountName=teammodelstorage;AccountKey=Yq7D4dE6cFuer2d2UZIccTA/i0c3sJ/6ITc8tNOyW+K5f+/lWw9GCos3Mxhj47PyWQgDL8YbVD63B9XcGtrMxQ==;EndpointSuffix=core.chinacloudapi.cn",
-      "cosmos": "AccountEndpoint=https://cdhabookdep-free.documents.azure.cn:443/;AccountKey=JTUVk92Gjsx17L0xqxn0X4wX2thDPMKiw4daeTyV1HzPb6JmBeHdtFY1MF1jdctW1ofgzqkDMFOtcqS46by31A==;",
-      "redis": "52.130.252.100:6379,password=habook,ssl=false,abortConnect=False,writeBuffer=10240",
-      "servicBus": "Endpoint=sb://teammodelos.servicebus.chinacloudapi.cn/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=Sy4h4EQ8zP+7w/lOLi1X3tGord/7ShFHimHs1vC50Dc="
-    }
   }
 ]

+ 1 - 0
TEAMModelBI/Models/AssistSchool.cs

@@ -23,6 +23,7 @@ namespace TEAMModelBI.Models
         public int serial { get; set; } //软体
         public int service { get; set; } //服务
         public int hard { get; set; } //硬体
+        public long lessonCount { get; set; } = 0; //学校课例数量
         //public List<SchoolProductSumData> serial { get; set; } //软体
         //public List<SchoolProductSumData> service { get; set; } //服务
         //public List<SchoolProductSumDataHard> hard { get; set; } //硬体

+ 1 - 1
TEAMModelBI/Tool/CommonFind.cs

@@ -111,7 +111,7 @@ namespace TEAMModelBI.Tool
         public static async Task<List<string>> FindSchoolIds(CosmosClient cosmosClient, string tmdId)
         {
             List<string> schoolIds = new();
-            string schoolSql = $"SELECT DISTINCT REPLACE(c.code,'Teacher-','') AS schoolId,c.code,c.roles,c.id,c.name From c where ARRAY_CONTAINS(c.roles,'assist',true) AND c.pk = 'Teacher' AND c.status = 'join' AND c.id='{tmdId}'";
+            string schoolSql = $"SELECT DISTINCT REPLACE(c.code,'Teacher-','') AS schoolId,c.code,c.roles,c.id,c.name From c where ARRAY_CONTAINS(c.roles,'assist',true) AND c.status = 'join' AND c.id='{tmdId}'";
 
             await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: schoolSql, requestOptions: new QueryRequestOptions() { }))
             {

+ 47 - 12
TEAMModelBI/Tool/TimeHelper.cs

@@ -184,18 +184,19 @@ namespace TEAMModelBI.Tool
         {
             long start = 0;
             long end = 0;
+            int year = dateTime.Year;
+            int month = dateTime.Month;
+            int day = dateTime.Day;
             DateTimeOffset tempStrart = new();
             DateTimeOffset tempEnt = new();
             switch (type) 
             {
                 case "year":
-                    tempStrart = new DateTime(dateTime.Year,1, 1);
-                    tempEnt = new DateTime(dateTime.Year, 12, DateTime.DaysInMonth(dateTime.Year, 12), 23, 59, 59);
+                    tempStrart = new DateTime(year, 1, 1);
+                    tempEnt = new DateTime(year, 12, DateTime.DaysInMonth(year, 12), 23, 59, 59);
                     break;
 
                 case "term":
-                    int year = dateTime.Year;
-                    int month = dateTime.Month;
                     if (month <= 8 && month >= 3)
                     {
                         tempStrart = new DateTime(year, 3, 1);
@@ -207,25 +208,59 @@ namespace TEAMModelBI.Tool
                         int days = 0;
                         if (month >= 9)
                         {
-                            tempStrart = new DateTime(dateTime.Year, 9, 1);
-                            tempEnt = new DateTime(dateTime.Year + 1, 2, (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? days = 29 : days = 28, 23, 59, 59);                        }
+                            tempStrart = new DateTime(year, 9, 1);
+                            tempEnt = new DateTime(year + 1, 2, (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? days = 29 : days = 28, 23, 59, 59);                        }
                         else
                         {
-                            tempStrart = new DateTime(dateTime.Year - 1, 9, 1);
-                            tempEnt = new DateTime(dateTime.Year, 2, (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? days = 29 : days = 28, 23, 59, 59);
+                            tempStrart = new DateTime(year - 1, 9, 1);
+                            tempEnt = new DateTime(year, 2, (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? days = 29 : days = 28, 23, 59, 59);
+                        }
+                    }
+
+                    break;
+                case "lastterm":
+                    DateTimeOffset tempDate = new();
+                    if (dateTime.Month > 9)
+                        tempDate = new DateTime(year, dateTime.Month - 4, 1);
+                    else tempDate = new DateTime(year - 1, 9, 1);
+                    if (!string.IsNullOrEmpty($"{tempDate}"))
+                    {
+                        year = tempDate.Year;
+                        month = tempDate.Month;
+                        if (month <= 8 && month >= 3)
+                        {
+                            tempStrart = new DateTime(year, 3, 1);
+                            tempEnt = new DateTime(year, 8, 31, 23, 59, 59);
+                        }
+                        else
+                        {
+                            //计算当前月份
+                            int days = 0;
+                            if (month >= 9)
+                            {
+                                tempStrart = new DateTime(year, 9, 1);
+                                tempEnt = new DateTime(year + 1, 2, (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? days = 29 : days = 28, 23, 59, 59);
+                            }
+                            else
+                            {
+                                tempStrart = new DateTime(year - 1, 9, 1);
+                                tempEnt = new DateTime(year, 2, (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? days = 29 : days = 28, 23, 59, 59);
+                            }
                         }
                     }
 
                     break;
                 case "month":
-                    tempStrart = new DateTime(dateTime.Year, dateTime.Month, 1);
-                    tempEnt = new DateTime(dateTime.Year, dateTime.Month, DateTime.DaysInMonth(dateTime.Year, dateTime.Month), 23, 59, 59);
+                    tempStrart = new DateTime(year, month, 1);
+                    tempEnt = new DateTime(year, month, DateTime.DaysInMonth(year, month), 23, 59, 59);
+
                     break;
                 case "week":
                     DateTimeOffset weekStrart = dateTime.AddDays(-(int)(dateTime.DayOfWeek) + 1);
                     DateTimeOffset weekEnd  = dateTime.AddDays(7-(int)(dateTime.DayOfWeek));
                     tempStrart = new DateTime(weekStrart.Year, weekStrart.Month, weekStrart.Day);
                     tempEnt = new DateTime(weekEnd.Year, weekEnd.Month, weekEnd.Day, 23, 59, 59); 
+
                     break;
                 case "lastweek":
                     var m = (dateTime.DayOfWeek == DayOfWeek.Sunday ? (DayOfWeek)7 : dateTime.DayOfWeek) - DayOfWeek.Monday;
@@ -237,8 +272,8 @@ namespace TEAMModelBI.Tool
 
                     break;
                 default:
-                    tempStrart = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day);
-                    tempEnt = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 23, 59, 59);
+                    tempStrart = new DateTime(year, month, day);
+                    tempEnt = new DateTime(year, month, day, 23, 59, 59);
                     //start = dateLenth ? DateTimeOffset.Parse($"{dayStart}").ToUnixTimeMilliseconds() : DateTimeOffset.Parse($"{dayStart}").ToUnixTimeSeconds();
                     //end = dateLenth ? DateTimeOffset.Parse($"{dayEnd}").ToUnixTimeMilliseconds() : DateTimeOffset.Parse($"{dayEnd}").ToUnixTimeSeconds();
                     break;            

+ 17 - 2
TEAMModelOS.SDK/Models/Service/Third/Xkw/OAuthModel.cs

@@ -9,7 +9,7 @@ using TEAMModelOS.SDK.Context.Attributes.Azure;
 namespace TEAMModelOS.SDK.Models
 {
 
-    public class OAuthModel 
+    public class XkwOAuthModel 
     {
     }
     [TableName(Name = "IESOAuth")]
@@ -34,7 +34,7 @@ namespace TEAMModelOS.SDK.Models
         /// </summary>
         public string OAuthHost { get; set; }
         /// <summary>
-        /// 单点认证业务接口
+        /// 单点认证业务跳转接口
         /// </summary>
         public string ServiceUrl { get; set; }
         /// <summary>
@@ -55,9 +55,24 @@ namespace TEAMModelOS.SDK.Models
         public string Phone { get; set; }
         public string Name { get; set; }
         public long Time { get; set; }
+        public string Type { get; set; }
+    }
+    public class XkwBindModel
+    {
+        public int status { get; set; }
+        public string msg { get; set; }
+        public string accessToken { get; set; }
+        public string openId { get; set; }
+        public string userId { get; set; }
     }
     public record OAuthCode {
         public string code { get; set; }
         public string state { get; set; }
+        //模块,res,item,paper
+        public string module { get; set; }
+        /// <summary>
+        /// 0 不同意 ,1 同意
+        /// </summary>
+        public int agree { get; set; }
     }
 }

+ 1 - 0
TEAMModelOS/ClientApp/public/index.html

@@ -46,6 +46,7 @@
 			document.write('<script id="echarts"  src="' + blobHost + '/0-public/js/echarts.js"><\/script>')
 			document.write('<script id="jspdf"  src="' + blobHost + '/0-public/js/jspdf.umd.min.js"><\/script>')
 			document.write('<script id="mathjax"  src="' + blobHost + '/0-public/js/tex-mml-svg.js"><\/script>')
+			document.write('<script id="mediaInfo"  src="' + blobHost + '/0-public/js/checkMedia.js"><\/script>')
 			document.write('<script id="e_theme_macarons"  src="<%= BASE_URL %>theme/e_theme_macarons.js"><\/script>')
 		</script>
 	</head>

+ 19 - 0
TEAMModelOS/ClientApp/src/api/auth.js

@@ -0,0 +1,19 @@
+import {
+    fetch,
+    post
+} from '@/api/http'
+export default {
+    /* 该醍摩豆ID检查对接了哪些单点登录平台 */
+    checkBind: function (data) {
+        return post('/oauth/check-bind', data)
+    },
+    /* 跳转学科网单点登录 */
+    xkwOauth: function (data) {
+        return post('/xkw/oauth', data)
+    },
+    /* 后端验证学科网的授权码code */
+    xkwAuthorize: function (data) {
+        return post('/xkw/authorize', data)
+    },
+
+}

+ 2 - 0
TEAMModelOS/ClientApp/src/api/index.js

@@ -35,6 +35,7 @@ import jyzx from './jyzx'
 import train from './train'
 import common from './common'
 import lessonRecord from './lessonRecord'
+import auth from './auth'
 
 export default {
     accessToken,
@@ -71,6 +72,7 @@ export default {
     train,
     common,
     lessonRecord,
+    auth,
     // 获取登录跳转链接
     getLoginLink: function (data) {
         return post('api/login/login', data)

+ 1 - 0
TEAMModelOS/ClientApp/src/assets/student-web/component_styles/classmate-commentPages.less

@@ -1,5 +1,6 @@
 @import "color.less";
 .classmates-commentPages {
+  margin-top: 25px;
   /* position: fixed;
   width: 100%;
   height: 100%;

+ 319 - 313
TEAMModelOS/ClientApp/src/common/AbilityUpload.vue

@@ -1,337 +1,343 @@
 <template>
-	<div class="base-upload-container">
-		<Upload :multiple="!singleUpload" type="drag" action="" :before-upload="onBeforeUpload"
-			:show-upload-list="false">
-			<div style="padding: 40px 0">
-				<Icon type="ios-cloud-upload" size="100" style="color: #a3a3a3;margin: 40px 0;"></Icon>
-				<p style="color: #a3a3a3">{{ $t('ability.uploadTip') }}</p>
-				<p style="color: #a3a3a3" v-if="acceptTypes.length">({{ $t('knowledge.import.tip2') }}:
-					{{ acceptTypes.join(' / ') }} )</p>
-			</div>
-		</Upload>
-		<div class="file-list-box">
-			<div class="file-item" v-for="(item,index) in fileArr" :key="index">
-				<p>
-					<span>{{ item.name }}</span>
-					<span class="tool-remove" v-if="isShowTool">
-						<Icon type="md-close" @click="onRemoveFile(index)" />
-					</span>
-				</p>
-				<Progress :percent="progressArr[index]" :stroke-width="12" />
-			</div>
-		</div>
-		<Button type="success" @click="onConfirmUpload" style="width: 100%;height: 40px;" :loading="isLoading"
-			class="modal-btn" :disabled="!fileArr.length"
-			v-if="mode === 'modal'">{{ $t('ability.confirmUpload') }}</Button>
-	</div>
+  <div class="base-upload-container">
+    <Upload :multiple="!singleUpload" type="drag" action="" :before-upload="onBeforeUpload" :show-upload-list="false">
+      <div style="padding: 40px 0">
+        <Icon type="ios-cloud-upload" size="100" style="color: #a3a3a3;margin: 40px 0;"></Icon>
+        <p style="color: #a3a3a3">{{ $t('ability.uploadTip') }}</p>
+        <p style="color: #a3a3a3" v-if="acceptTypes.length">({{ $t('knowledge.import.tip2') }}:
+          {{ acceptTypes.join(' / ') }} )</p>
+      </div>
+    </Upload>
+    <div class="file-list-box">
+      <div class="file-item" v-for="(item,index) in fileArr" :key="index">
+        <p>
+          <span>{{ item.name }}</span>
+          <span class="tool-remove" v-if="isShowTool">
+            <Icon type="md-close" @click="onRemoveFile(index)" />
+          </span>
+        </p>
+        <Progress :percent="progressArr[index]" :stroke-width="12" />
+      </div>
+    </div>
+    <Button type="success" @click="onConfirmUpload" style="width: 100%;height: 40px;" :loading="isLoading" class="modal-btn" :disabled="!fileArr.length" v-if="mode === 'modal'">{{ $t('ability.confirmUpload') }}</Button>
+  </div>
 </template>
 
 <script>
-	import excel from '@/utils/excel.js'
-	import FileSaver from "file-saver";
-	import BlobTool from '@/utils/blobTool.js'
-	export default {
-		props: {
-			auth: {
-				type: Object,
-				default: () => {
-					return {
-						sas: '',
-						url: '',
-						name: ''
-					}
-				}
-			},
-			scope: {
-				type: String,
-				default: 'school'
-			},
-			prefix: {
-				type: String,
-				default: ''
-			},
-			mode: {
-				type: String,
-				default: 'modal'
-			},
-			maxSize: {
-				type: Number,
-				default: -1
-			},
-			limit: {
-				type: Number,
-				default: 9999
-			},
-			acceptTypes: {
-				type: Array,
-				default: () => []
-			},
-			// 简易上传,只返回选择的fileArr,不做操作处理
-			simpleUpload: {
-				type: Boolean,
-				default: false
-			},
-			// 只能上传单个
-			singleUpload: {
-				type: Boolean,
-				default: false
-			},
-			classMemoir:{
-				type: Boolean,
-				default: false
-			},
-		},
-		data() {
-			return {
-				isLoading: false,
-				isShowTool: true,
-				containerClient: null,
-				fileArr: [],
-				progressArr: []
-			}
-		},
-		created() {
-			console.log(this.acceptTypes);
-		},
-		methods: {
+import excel from '@/utils/excel.js'
+import FileSaver from "file-saver";
+import BlobTool from '@/utils/blobTool.js'
+export default {
+  props: {
+    auth: {
+      type: Object,
+      default: () => {
+        return {
+          sas: '',
+          url: '',
+          name: ''
+        }
+      }
+    },
+    scope: {
+      type: String,
+      default: 'school'
+    },
+    prefix: {
+      type: String,
+      default: ''
+    },
+    mode: {
+      type: String,
+      default: 'modal'
+    },
+    maxSize: {
+      type: Number,
+      default: -1
+    },
+    limit: {
+      type: Number,
+      default: 9999
+    },
+    acceptTypes: {
+      type: Array,
+      default: () => []
+    },
+    // 简易上传,只返回选择的fileArr,不做操作处理
+    simpleUpload: {
+      type: Boolean,
+      default: false
+    },
+    // 只能上传单个
+    singleUpload: {
+      type: Boolean,
+      default: false
+    },
+    classMemoir: {
+      type: Boolean,
+      default: false
+    },
+  },
+  data() {
+    return {
+      isLoading: false,
+      isShowTool: true,
+      containerClient: null,
+      fileArr: [],
+      progressArr: []
+    }
+  },
+  created() {
+    console.log(this.acceptTypes);
+  },
+  methods: {
 
 
-			onRemoveFile(index) {
-				this.fileArr.splice(index, 1)
-				this.$emit('removeFileFinish', this.fileArr)
-			},
+    onRemoveFile(index) {
+      this.fileArr.splice(index, 1)
+      this.$emit('removeFileFinish', this.fileArr)
+    },
 
-			/* 转换size */
-			getSizeByBytes(bytes) {
-				return bytes / 1024 < 1024 ? (bytes / 1024).toFixed(1) + 'KB' : bytes / 1024 / 1024 < 1024 ? (bytes /
-					1024 / 1024).toFixed(1) + 'M' : (bytes / 1024 / 1024 / 1024).toFixed(1) + 'G'
-			},
+    /* 转换size */
+    getSizeByBytes(bytes) {
+      return bytes / 1024 < 1024 ? (bytes / 1024).toFixed(1) + 'KB' : bytes / 1024 / 1024 < 1024 ? (bytes /
+        1024 / 1024).toFixed(1) + 'M' : (bytes / 1024 / 1024 / 1024).toFixed(1) + 'G'
+    },
 
-			async onBeforeUpload(file) {
-				console.log(this.$GLOBAL.NotSupport);
-				console.log(this.acceptTypes);
-				console.log(file);
-				console.log(file.type);
-				if(this.classMemoir && this.fileArr.length){
-					this.$Message.warning('只能选择一个文件');
-					return false;
-				}
-				
-				if(this.fileArr.length >= this.limit && this.mode === 'modal'){
-					this.$Message.warning(`当前任务目前只能提交${ this.limit }个文件!`);
-					return false
-				}
+    async onBeforeUpload(file) {
+      console.log(this.$GLOBAL.NotSupport);
+      console.log(this.acceptTypes);
+      console.log(file);
+      console.log(file.type);
+      if (this.classMemoir && this.fileArr.length) {
+        this.$Message.warning('只能选择一个文件');
+        return false;
+      }
 
-				let nameType = file.name.split('.')[file.name.split('.').length - 1]
-				if (this.maxSize > 0 && file.size > this.maxSize) {
-					this.$Message.warning(this.$t('ability.sizeTip') + this.getSizeByBytes(this.maxSize) + '!');
-					return false
-				}
-				if (this.acceptTypes.length && !this.acceptTypes.includes(file.type.split('/')[file.type.split('/')
-						.length - 1].toLowerCase()) && !this.acceptTypes.includes(nameType.toLowerCase())) {
-					this.$Message.warning(this.$t('ability.typeTip') + this.acceptTypes.join(','));
-					return false
-				} else if (this.$GLOBAL.NotSupport.includes(nameType.toUpperCase()) && !this.acceptTypes.length) {
-					this.$Message.warning(this.$t('learnActivity.notSupportType')); // 黑名单文件格式不能上传
-					return false
-				}
+      if (this.fileArr.length >= this.limit && this.mode === 'modal') {
+        this.$Message.warning(`当前任务目前只能提交${this.limit}个文件!`);
+        return false
+      }
 
-				if(this.classMemoir){
-					this.$emit('selectFinish',file);
-				}
+      let nameType = file.name.split('.')[file.name.split('.').length - 1]
+      if (this.maxSize > 0 && file.size > this.maxSize) {
+        this.$Message.warning(this.$t('ability.sizeTip') + this.getSizeByBytes(this.maxSize) + '!');
+        return false
+      }
+      if (this.acceptTypes.length && !this.acceptTypes.includes(file.type.split('/')[file.type.split('/')
+        .length - 1].toLowerCase()) && !this.acceptTypes.includes(nameType.toLowerCase())) {
+        this.$Message.warning(this.$t('ability.typeTip') + this.acceptTypes.join(','));
+        return false
+      } else if (this.$GLOBAL.NotSupport.includes(nameType.toUpperCase()) && !this.acceptTypes.length) {
+        this.$Message.warning(this.$t('learnActivity.notSupportType')); // 黑名单文件格式不能上传
+        return false
+      }
 
-				if (this.mode === 'import') {
-					let excelResult = []
-					this.readExcel(file, data => {
-						if (data.results.length) {
-							console.log(data);
-							data.results.forEach(item => {
-								excelResult.push({
-									question: item.Question || '',
-									answer: item.Answer ? (item.Type === 'multiple' ? item
-										.Answer
-										.split(',') : [item.Answer]) : [],
-									type: item.Type,
-									options: this.getItemOptions(item)
-								})
-							})
-							console.log(excelResult);
-							this.fileArr = []
-							this.progressArr = []
-							this.$emit('uploadFinish', excelResult)
-							this.isLoading = false
-						}
-					})
-					return false
-				}
-				file.progress = 0
-				this.fileArr.push(file)
-				this.progressArr.push(0)
-				return false
-			},
+      if(['mp4','mp3','ogg','wav','webm'].includes(nameType.toLowerCase())){
+        /* 检查上传媒体文件编码信息是否符合要求 */
+        let checkMediaFile = await this.$tools.checkMediaFile(file)
+        if (!checkMediaFile) {
+          this.$Message.warning('多媒体文件编码信息不正确,无法播放,请重新上传')
+          return false
+        }
+      }
+      
+      if (this.classMemoir) {
+        this.$emit('selectFinish', file);
+      }
 
-			/* 解析excel表格 */
-			readExcel(file, callback) {
-				var reader = new FileReader();
-				reader.onload = function(e) {
-					var data = e.target.result;
-					var workbook = excel.read(data, 'binary');
-					if (callback) callback(workbook);
-				};
-				reader.readAsBinaryString(file);
-			},
+      if (this.mode === 'import') {
+        let excelResult = []
+        this.readExcel(file, data => {
+          if (data.results.length) {
+            console.log(data);
+            data.results.forEach(item => {
+              excelResult.push({
+                question: item.Question || '',
+                answer: item.Answer ? (item.Type === 'multiple' ? item
+                  .Answer
+                  .split(',') : [item.Answer]) : [],
+                type: item.Type,
+                options: this.getItemOptions(item)
+              })
+            })
+            console.log(excelResult);
+            this.fileArr = []
+            this.progressArr = []
+            this.$emit('uploadFinish', excelResult)
+            this.isLoading = false
+          }
+        })
+        return false
+      }
+      file.progress = 0
+      this.fileArr.push(file)
+      this.progressArr.push(0)
+      return false
+    },
 
-			/* 获取表格解析试题的选项 */
-			getItemOptions(item) {
-				let options = []
-				let optionIndex = 0
-				if (item.Type === 'judge') {
-					return [{
-						code: 'A',
-						value: this.$t('ability.true')
-					}, {
-						code: 'B',
-						value: this.$t('ability.false')
-					}]
-				} else {
-					for (let key in item) {
-						if (key.includes('Option')) {
-							options.push({
-								code: String.fromCharCode(64 + parseInt(optionIndex + 1)),
-								value: item[key]
-							})
-							optionIndex++
-						}
-					}
-					return options
-				}
-			},
-			/* 确认上传 */
-			async onConfirmUpload() {
-				if (this.simpleUpload) {
-					this.$emit('uploadFinish', this.fileArr)
-					return
-				}
-				this.isShowTool = false
-				this.isLoading = true
-				let result = []
-				let containerClient = this.containerClient
-				let list = this.fileArr
-				let that = this
-				let path = this.prefix === '' ? `yxpt/${this.curStandard}/jyzx` :
-					(this.mode === 'video' ? `yxpt/${this.curStandard}/jyzx/${this.prefix}/video` : `yxpt/${this.curStandard}/jyzx/${this.prefix}`)
-				for (let i = 0; i < list.length; i++) {
-					let file = list[i]
-					try {
-						let blobFile = await containerClient.upload(file, { path:path,checkSize:this.auth.name !== 'teammodelos' }, {
-							onProgress: function(e) {
-								that.$set(that.progressArr, i, parseInt(e.loadedBytes * 100 / file.size))
-							}
-						});
-						console.log(blobFile)
-						// 如果上传的是视频文件 则需要获取视频的时长信息和MD5信息
-						if (blobFile.type === 'video') {
-							if(blobFile.extension === 'MP4'){
-								let canUploadPoster = await this.uploadVideoPoster(blobFile, path, containerClient)
-								if(canUploadPoster === 200){
-									blobFile.duration = await this.$tools.getVideoDuration(file)
-								}else{
-									this.$Message.warning('当前视频可能无法播放,请检查视频编码格式')
-								}
-							}else{
-								this.$Message.warning('当前视频可能无法播放,请检查视频编码格式')
-								blobFile.duration = 0
-							}
-						}
-						let fileMD5 = await this.$tools.getFileMD5(file)
-						blobFile.hash = this.$tools.convertFileMD5ToString(fileMD5)
-						console.log('getFileMD5 > convertFileMD5ToString >', blobFile)
-						result.push(blobFile)
-					} catch (e) {
-						this.$Message.error(e.spaceError ? e.spaceError : 'upload Fail')
-						return
-					}
-				}
-				this.fileArr = []
-				this.progressArr = []
-				this.$emit('uploadFinish', result)
-				this.isLoading = false
-			},
-			/* 上传视频封面 */
-			uploadVideoPoster(blobJson, path, containerClient) {
-				return new Promise(async (r, j) => {
-					try {
-						let fileFullUrl = await this.$tools.getFileSas(blobJson.url)
-						let n = blobJson.name.substring(0, blobJson.name.lastIndexOf('.'))
-						let dataUrl = await this.$jsFn.createVideoPoster(fileFullUrl.url,blobJson.name)
-						let f = this.$jsFn.dataURLtoFile(dataUrl, n + '.png')
-						let blobFile = await containerClient.upload(f, { path:path,checkSize:false });
-						r(200)
-					} catch (e) {
-						r(500)
-					}
-				})
+    /* 解析excel表格 */
+    readExcel(file, callback) {
+      var reader = new FileReader();
+      reader.onload = function (e) {
+        var data = e.target.result;
+        var workbook = excel.read(data, 'binary');
+        if (callback) callback(workbook);
+      };
+      reader.readAsBinaryString(file);
+    },
 
-			}
-		},
-		mounted() {
-			if (this.auth.sas) {
-				let n = this.auth
-				this.containerClient = new BlobTool(n.url, n.name, n.sas, this.scope)
-			}
-		},
-		computed: {
-			curStandard() {
-				return sessionStorage.getItem('standard') || this.$store.state.user.schoolProfile.school_base.standard
-			},
-			isAreaMgmt() {
-				return this.$route.name === 'areaAbilityMgmt'
-			}
-		},
-		watch: {
-			auth: {
-				handler(n, o) {
-					this.containerClient = new BlobTool(n.url, n.name, n.sas, this.scope)
-				}
-			}
-		}
-	}
+    /* 获取表格解析试题的选项 */
+    getItemOptions(item) {
+      let options = []
+      let optionIndex = 0
+      if (item.Type === 'judge') {
+        return [{
+          code: 'A',
+          value: this.$t('ability.true')
+        }, {
+          code: 'B',
+          value: this.$t('ability.false')
+        }]
+      } else {
+        for (let key in item) {
+          if (key.includes('Option')) {
+            options.push({
+              code: String.fromCharCode(64 + parseInt(optionIndex + 1)),
+              value: item[key]
+            })
+            optionIndex++
+          }
+        }
+        return options
+      }
+    },
+    /* 确认上传 */
+    async onConfirmUpload() {
+      if (this.simpleUpload) {
+        this.$emit('uploadFinish', this.fileArr)
+        return
+      }
+      this.isShowTool = false
+      this.isLoading = true
+      let result = []
+      let containerClient = this.containerClient
+      let list = this.fileArr
+      let that = this
+      let path = this.prefix === '' ? `yxpt/${this.curStandard}/jyzx` :
+        (this.mode === 'video' ? `yxpt/${this.curStandard}/jyzx/${this.prefix}/video` : `yxpt/${this.curStandard}/jyzx/${this.prefix}`)
+      for (let i = 0; i < list.length; i++) {
+        let file = list[i]
+        try {
+          let blobFile = await containerClient.upload(file, { path: path, checkSize: this.auth.name !== 'teammodelos' }, {
+            onProgress: function (e) {
+              that.$set(that.progressArr, i, parseInt(e.loadedBytes * 100 / file.size))
+            }
+          });
+          console.log(blobFile)
+          // 如果上传的是视频文件 则需要获取视频的时长信息和MD5信息
+          if (blobFile.type === 'video') {
+            if (blobFile.extension === 'MP4') {
+              let canUploadPoster = await this.uploadVideoPoster(blobFile, path, containerClient)
+              if (canUploadPoster === 200) {
+                blobFile.duration = await this.$tools.getVideoDuration(file)
+              } else {
+                this.$Message.warning('当前视频可能无法播放,请检查视频编码格式')
+              }
+            } else {
+              this.$Message.warning('当前视频可能无法播放,请检查视频编码格式')
+              blobFile.duration = 0
+            }
+          }
+          let fileMD5 = await this.$tools.getFileMD5(file)
+          blobFile.hash = this.$tools.convertFileMD5ToString(fileMD5)
+          console.log('getFileMD5 > convertFileMD5ToString >', blobFile)
+          result.push(blobFile)
+        } catch (e) {
+          this.$Message.error(e.spaceError ? e.spaceError : 'upload Fail')
+          return
+        }
+      }
+      this.fileArr = []
+      this.progressArr = []
+      this.$emit('uploadFinish', result)
+      this.isLoading = false
+    },
+    /* 上传视频封面 */
+    uploadVideoPoster(blobJson, path, containerClient) {
+      return new Promise(async (r, j) => {
+        try {
+          let fileFullUrl = await this.$tools.getFileSas(blobJson.url)
+          let n = blobJson.name.substring(0, blobJson.name.lastIndexOf('.'))
+          let dataUrl = await this.$jsFn.createVideoPoster(fileFullUrl.url, blobJson.name)
+          let f = this.$jsFn.dataURLtoFile(dataUrl, n + '.png')
+          let blobFile = await containerClient.upload(f, { path: path, checkSize: false });
+          r(200)
+        } catch (e) {
+          r(500)
+        }
+      })
+
+    }
+  },
+  mounted() {
+    if (this.auth.sas) {
+      let n = this.auth
+      this.containerClient = new BlobTool(n.url, n.name, n.sas, this.scope)
+    }
+  },
+  computed: {
+    curStandard() {
+      return sessionStorage.getItem('standard') || this.$store.state.user.schoolProfile.school_base.standard
+    },
+    isAreaMgmt() {
+      return this.$route.name === 'areaAbilityMgmt'
+    }
+  },
+  watch: {
+    auth: {
+      handler(n, o) {
+        this.containerClient = new BlobTool(n.url, n.name, n.sas, this.scope)
+      }
+    }
+  }
+}
 </script>
 
 <style lang="less">
-	.base-upload-container {
-		.file-list-box {
-			margin: 20px 5px;
-			width: 100%;
-			max-height: 200px;
-			overflow: auto;
+.base-upload-container {
+  .file-list-box {
+    margin: 20px 5px;
+    width: 100%;
+    max-height: 200px;
+    overflow: auto;
 
-			.file-item {
-				margin: 10px 0;
+    .file-item {
+      margin: 10px 0;
 
-				.tool-remove {
-					color: #b3b3b3;
-					margin-left: 15px;
-					font-size: 14px;
-					cursor: pointer;
-				}
-			}
+      .tool-remove {
+        color: #b3b3b3;
+        margin-left: 15px;
+        font-size: 14px;
+        cursor: pointer;
+      }
+    }
 
-			.ivu-progress {
-				margin-top: 10px;
-			}
+    .ivu-progress {
+      margin-top: 10px;
+    }
 
-			.ivu-progress-inner {
-				background-color: #e1e1e1;
-			}
+    .ivu-progress-inner {
+      background-color: #e1e1e1;
+    }
 
-			.ivu-progress-text-inner {
-				color: #969696;
-			}
+    .ivu-progress-text-inner {
+      color: #969696;
+    }
 
-			&::-webkit-scrollbar-thumb {
-				background: #dadada !important;
-			}
-		}
-	}
+    &::-webkit-scrollbar-thumb {
+      background: #dadada !important;
+    }
+  }
+}
 </style>

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

@@ -100,9 +100,17 @@
                                 </i-col>
                             </Row>
                         </div>
-                        <div class="file-show" v-if="nowStudentHw.content.length">
-                            <div class="one-show" :key="index"
-                                 v-for="(item, index) in nowStudentHw.content"
+                        <div v-if="nowStudentHw.answer" style="margin-top: 10px;">
+                            <div class="remarks">{{ $t("studentWeb.exam.answer") }}:</div>
+                            <div class="file-show">
+                                <p>{{ nowStudentHw.answer }}</p>
+                            </div>
+                            <!-- <Input v-special-char v-model="nowStudentHw.answer" disabled type="textarea" /> -->
+                        </div>
+                        <div v-if="nowStudentHw.content.length" style="margin-top: 15px;">
+                            <p class="remarks">{{ $t("studentWeb.homework.attachment") }}:</p>
+                            <div class="file-show" :key="index"
+                                v-for="(item, index) in nowStudentHw.content"
                             >
                                 <div class="repair-link-wrap-item-box">
                                     <div class="file-icon">
@@ -118,10 +126,6 @@
                                 </div>
                             </div>
                         </div>
-                        <div v-if="nowStudentHw.answer" style="margin-top: 10px">
-                            <!-- <p class="remarks">回答:</p> -->
-                            <Input v-special-char v-model="nowStudentHw.answer" disabled type="textarea" />
-                        </div>
 
                         <div class="title-rect-group">
                             <h2 class="title-rect-name">
@@ -471,3 +475,9 @@ export default {
 @import "~@/assets/student-web/component_styles/classmate-comment.less";
 @import "~@/assets/student-web/component_styles/classmate-commentPages.less";
 </style>
+
+<style lang="less">
+.ivu-input[disabled] {
+    color: #2f2f2f;
+}
+</style>

+ 6 - 5
TEAMModelOS/ClientApp/src/components/student-web/HomeView/CourseView/SchoolReport.vue

@@ -27,7 +27,7 @@
                     <span class="base-info-text">{{ classInfo.teaName }}</span>
                 </span>
                 <span v-if="classInfo.name">
-                    <Icon custom="iconfont icon-mingdan" style="font-weight: bold; margin-right: 5px;" />{{ $t('studentWeb.baseInfo.teacher') }}
+                    <Icon custom="iconfont icon-mingdan" style="font-weight: bold; margin-right: 5px;" />{{ $t('studentWeb.baseInfo.subjectName') }}
                     <span class="base-info-text">{{ classInfo.name }}</span>
                 </span>
                 <span>
@@ -74,7 +74,7 @@ export default {
                     type: 'expand',
                 }, */
                 {
-                    title: '评测名称',
+                    title: this.$t("studentWeb.myAchievement.name"),
                     // slot: "name",
                     key: 'examName',
                     align: "center"
@@ -92,13 +92,13 @@ export default {
                     align: "center"
                 }, */
                 {
-                    title: '分数',
+                    title: this.$t("studentWeb.myAchievement.score"),
                     // slot: "score",
                     key: "score",
                     align: "center"
                 },
                 {
-                    title: '类型',
+                    title: this.$t("studentWeb.myAchievement.tag"),
                     slot: "tag",
                     width: 500,
                     align: "center"
@@ -108,7 +108,7 @@ export default {
                     key: 'list'
                 }, */
                 {
-                    title: '时间',
+                    title: this.$t("studentWeb.myAchievement.createTime"),
                     key: 'time',
                     align: "center"
                 }
@@ -229,6 +229,7 @@ export default {
                                 item.total += point
                             })
                         }
+                        item.total.toFixed(0)
                         item.time = this.dateFormat(item.createTime)
                         item.score = `${item.sum} / ${item.total}`
                         this.schoolRep.push(item)

+ 1 - 0
TEAMModelOS/ClientApp/src/locale/lang/en-US/learnActivity.js

@@ -287,6 +287,7 @@ export default {
 
     //SimpleAnalysis.vue
     simple: {
+        overviewLabel:'總覽',
         totalPeople: 'Total No. of people',
         missExam: 'No. of absentee',
         missExamRate: '缺考率',

+ 1 - 1
TEAMModelOS/ClientApp/src/locale/lang/en-US/stuAccount.js

@@ -116,7 +116,7 @@ export default {
 	attrWarning: 'Warning: Excel Column is incomplete!',
 	setNoWarning: 'Warning: Seat number is repeated in Excel!',
 	idWarning: 'Warning: Account/student ID is repeated in Excel!',
-	idFormatWarning: 'Error:Student account format is a 4-12 digit number',
+	idFormatWarning: '錯誤:學生賬號格式為4-12位數字',
 	gradeWarning: 'Warning: Error in grade',
 	setNoErr: "Error: Seat number has been repeated within the school",
 	downloadText: '(Download List Sample)',

+ 18 - 0
TEAMModelOS/ClientApp/src/locale/lang/en-US/studentWeb.js

@@ -824,4 +824,22 @@ export default {
         wrong: "Class Incorrect Rate",
         wrongNum: "No. of Incorrect",
     },
+    myAchievement: {
+        examMode: "評測模式",
+        examIn: "發佈來源",
+        name: "名稱",
+        tag: "類型",
+        creator: "創建人",
+        score: "分數",
+        createTime: "創建時間",
+        action: "詳情",
+        hwName: "作業名稱",
+        teaScore: "評分",
+        star: "Star",
+        comment: "評語",
+        averageStar: "互評平均分",
+        average: "平均分",
+        class: "Course",
+        point: "Point",
+    },
 }

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

@@ -287,6 +287,7 @@ export default {
 
     //SimpleAnalysis.vue
     simple: {
+        overviewLabel:'总览',
         totalPeople: '总人数',
         missExam: '缺考数',
         missExamRate: '缺考率',

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

@@ -115,7 +115,7 @@ export default {
 	attrWarning: '警告:Excel 內栏位不完整!',
 	setNoWarning: '警告:Excel 內的座位号重覆!',
 	idWarning: '警告:Excel 內账号重复!',
-	idFormatWarning: '错误:学生账号格式为4-12数字',
+	idFormatWarning: '错误:学生账号格式为4-12数字',
 	gradeWarning: '警告:年级错误',
 	setNoErr: "错误:座位号已在校內重复",
 	downloadText: '(下载名单模板)',

+ 19 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/studentWeb.js

@@ -26,7 +26,7 @@ export default {
         search: '请输入查询内容...',
         notice: '功能开发中,敬请期待',
         develop: "开发中",
-        school: "校",
+        school: "校",
         private: "老师",
         day: "天",
         hour: "时",
@@ -824,4 +824,22 @@ export default {
         wrong: "班级错误率",
         wrongNum: "错误次数",
     },
+    myAchievement: {
+        examMode: "评测模式",
+        examIn: "发布来源",
+        name: "名称",
+        tag: "类型",
+        creator: "创建人",
+        score: "分数",
+        createTime: "创建时间",
+        action: "详情",
+        hwName: "作业名称",
+        teaScore: "评分",
+        star: "评星",
+        comment: "评语",
+        averageStar: "互评平均分",
+        average: "平均分",
+        class: "课程",
+        point: "记分",
+    },
 }

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

@@ -287,6 +287,7 @@ export default {
 
     //SimpleAnalysis.vue
     simple: {
+        overviewLabel:'總覽',
         totalPeople: '總人數',
         missExam: '缺考數',
         missExamRate: '缺考率',

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

@@ -116,7 +116,7 @@ export default {
 	attrWarning: '警告:Excel 內欄位不完整! ',
 	setNoWarning: '警告:Excel 內的座號重覆! ',
 	idWarning: '警告:Excel 內帳號重複! ',
-	idFormatWarning: '錯誤:學生帳號格式為4-12數字',
+	idFormatWarning: '錯誤:學生賬號格式為4-12位數字',
 	gradeWarning: '警告:年級錯誤',
 	setNoErr: "錯誤:座位號已在校內重複",
 	downloadText: '(下載名單模板)',

+ 19 - 1
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/studentWeb.js

@@ -26,7 +26,7 @@ export default {
         search: '請輸入查詢內容...',
         notice: '此功能暫未開放,敬請期待!',
         develop: "開發中",
-        school: "校",
+        school: "校",
         private: "個人",
         day: "天",
         hour: "時",
@@ -824,4 +824,22 @@ export default {
         wrong: "班級錯誤率",
         wrongNum: "錯誤次數",
     },
+    myAchievement: {
+        examMode: "評測模式",
+        examIn: "發佈來源",
+        name: "名稱",
+        tag: "類型",
+        creator: "創建人",
+        score: "分數",
+        createTime: "創建時間",
+        action: "詳情",
+        hwName: "作業名稱",
+        teaScore: "評分",
+        star: "評星",
+        comment: "評語",
+        averageStar: "互評平均分",
+        average: "平均分",
+        class: "課程",
+        point: "記分",
+    },
 }

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

@@ -93,6 +93,10 @@ export const routes = [{
 	path: '/joinclass',
 	component: resolve => require(['@/view/joinclass/JoinClass.vue'], resolve)
 },
+{
+	path: '/authorized/xkw',
+	component: resolve => require(['@/view/xkw/xkw.vue'], resolve)
+},
 {
 	path: '/shareSyllabus',
 	component: resolve => require(['@/view/shareSyllabus/ShareSyllabus.vue'], resolve)

+ 8 - 3
TEAMModelOS/ClientApp/src/store/module/user.js

@@ -298,13 +298,18 @@ export default {
         setTeachers(state, data) {
             state.teachers = data
         },
-        delTeacher(state, ids) {
-            if (ids && ids.length) {
+        delTeacher(state, idOrNames) {
+            if (idOrNames && idOrNames.length) {
                 let teachers = state.teachers
                 let count = teachers.length - 1
                 for (let i = count; i >= 0; i--) {
-                    if (ids.includes(teachers[i].id) || ids.includes(teachers[i].name)) {
+                    if (teachers[i].id && idOrNames.includes(teachers[i].id)) {
                         teachers.splice(i, 1)
+                        continue
+                    }
+                    if (idOrNames.includes(teachers[i].name || teachers[i].iname)) {
+                        teachers.splice(i, 1)
+                        continue
                     }
                 }
                 state.teachers = teachers

+ 140 - 89
TEAMModelOS/ClientApp/src/utils/public.js

@@ -226,19 +226,70 @@ export default {
 			thumPath = require('@/assets/source/zip.png')
 		} else if (type === 'thum') {
 			thumPath = require('@/assets/source/image.png')
-		}else{
+		} else {
 			thumPath = require('@/assets/source/link.png')
 		}
 		return thumPath
 	},
-
+	/* 获取文件后缀名 */
 	getSuffix(name) {
 		return name.substr(name.lastIndexOf(".") + 1)
 	},
-
+	/* 导出表格 */
 	exportTable(params) {
 		excel.export_array_to_excel(params)
 	},
+	/* 检查音视频格式是否符合格式要求 */
+	checkMediaFile(file) {
+		return new Promise((r, j) => {
+			const getSize = () => file.size
+			const readChunk = (chunkSize, offset) =>
+				new Promise((resolve, reject) => {
+					const reader = new FileReader()
+					reader.onload = (event) => {
+						if (event.target.error) {
+							reject(event.target.error)
+						}
+						resolve(new Uint8Array(event.target.result))
+					}
+					reader.readAsArrayBuffer(file.slice(offset, offset + chunkSize))
+				})
+			window.MediaInfo().then((media) => {
+				media.analyzeData(getSize, readChunk).then((result) => {
+					console.log(result)
+					if (result['media']) {
+						let tracks = result['media']['track']
+						// 判断是否是视频
+						let videoTrack = tracks.find(track => track['@type'] === 'Video')
+						let audioTrack = tracks.find(track => track['@type'] === 'Audio')
+						// 如果视频文件满足MP4(H264+AAC),WAV('vp8','vp9') 则代表是可以正常播放的视频文件,返回视频的编码级别
+						if (videoTrack && audioTrack) {
+							let videoFormat = videoTrack.Format.toLowerCase()
+							let audioFormat = audioTrack.Format.toLowerCase()
+							if((videoFormat === 'avc' && audioFormat === 'aac') || (['vp8','vp9'].includes(videoFormat) && audioFormat === 'vorbis')) {
+								r(videoTrack.Format_Profile || true)
+							}else{
+								r(false)
+							}
+						}
+						// 如果是音频文件则需要满足 mp3(MPEG Audio),ogg(Vorbis),wav(PCM) 任意一种编码格式
+						else if (!videoTrack && audioTrack && ['mpeg audio','vorbis','pcm'].includes(audioTrack.Format.toLowerCase())) {
+							r(true)
+						} else {
+							r(false)
+						}
+					} else {
+						r(false)
+					}
+				}).catch((error) => {
+					j(error)
+				})
+			}).catch((error) => {
+				j(error)
+			})
+		})
+	},
+
 	/* 根据最新服务器时间获取当前所在学期的起止时间戳 */
 	getSemesterTimeRange() {
 		let curServerTime = localStorage.getItem('serverTime') // 服务器时间
@@ -312,7 +363,7 @@ export default {
 			range.push([
 				this.tranNum([+semesters[i].month, +semesters[i].day]),
 				i === semesters.length - 1 ? this.tranNum([+semesters[0].month, +semesters[0].day]) : this
-				.tranNum([+semesters[i + 1].month, +semesters[i + 1].day])
+					.tranNum([+semesters[i + 1].month, +semesters[i + 1].day])
 			])
 		}
 		return range
@@ -389,11 +440,11 @@ export default {
 		}
 	},
 	/* 生成随机UUID工具 */
-	randomId: function() {
+	randomId: function () {
 		return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)
 	},
 	/* 生成随机UUID工具 */
-	guid: function() {
+	guid: function () {
 		return (this.randomId() + this.randomId() + '-' + this.randomId() + '-' + this.randomId() + '-' + this
 			.randomId() +
 			'-' + this.randomId() + this.randomId() + this.randomId())
@@ -434,21 +485,21 @@ export default {
 	/* 根据x,y,w,h获取四点坐标 */
 	getBoxPos(x, y, w, h) {
 		return [{
-				x: x,
-				y: y
-			},
-			{
-				x: x + w,
-				y: y
-			},
-			{
-				x: x + w,
-				y: y + h
-			},
-			{
-				x: x,
-				y: y + h
-			}
+			x: x,
+			y: y
+		},
+		{
+			x: x + w,
+			y: y
+		},
+		{
+			x: x + w,
+			y: y + h
+		},
+		{
+			x: x,
+			y: y + h
+		}
 		]
 	},
 	/* 匹配URL里面的HOST */
@@ -519,7 +570,7 @@ export default {
 		for (var k in o)
 			if (new RegExp('(' + k + ')').test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : ((
 				'00' + o[
-					k]).substr(('' + o[k]).length)))
+				k]).substr(('' + o[k]).length)))
 		return fmt
 	},
 	/* 字节格式转换 */
@@ -537,7 +588,7 @@ export default {
 			var formData = new FormData();
 			xhr.open('get', url); //url填写后台的接口地址,如果是post,在formData append参数(参考原文地址)
 			if (type) xhr.responseType = 'arraybuffer';
-			xhr.onload = function(e) {
+			xhr.onload = function (e) {
 				switch (e.currentTarget.status) {
 					case 200:
 						resolve(e.currentTarget.response)
@@ -775,9 +826,9 @@ export default {
 		})
 	},
 	/* 生成学生清单PDF */
-	batchStuList(datas,pdfName){
-		return new Promise(async (resolve,reject) => {
-			try{
+	batchStuList(datas, pdfName) {
+		return new Promise(async (resolve, reject) => {
+			try {
 				let namesArr = await this.getNamesBase64(datas.map(i => i.name))
 				let classNameBase64 = await this.getClassNameBase64(pdfName)
 				let pageCount = 1
@@ -791,72 +842,72 @@ export default {
 				const a4Width = 210
 				// 生成学生清单表格
 				PDF.autoTable({
-					theme:'plain',
-					styles:{
-						valign:'middle'
+					theme: 'plain',
+					styles: {
+						valign: 'middle'
 					},
-					margin:{
-						top:15,
-						left:15,
+					margin: {
+						top: 15,
+						left: 15,
 					},
-					headStyles:{
-						cellPadding:{
-							top:3,
-							bottom:3,
-							left:5
+					headStyles: {
+						cellPadding: {
+							top: 3,
+							bottom: 3,
+							left: 5
 						},
-						valign:'middle',
+						valign: 'middle',
 					},
-					bodyStyles:{
-						cellPadding:5,
+					bodyStyles: {
+						cellPadding: 5,
 					},
-					columnStyles:{
-						0:{
-							cellWidth:20,
-							valign:'middle',
-							halign:'center'
+					columnStyles: {
+						0: {
+							cellWidth: 20,
+							valign: 'middle',
+							halign: 'center'
 						}
 					},
 					head: [
-						['','Name', 'ID', 'IRS','']
+						['', 'Name', 'ID', 'IRS', '']
 					],
-					body: datas.map(i => ['','', i.id, i.irs]),
+					body: datas.map(i => ['', '', i.id, i.irs]),
 					didDrawPage: (data) => {
-						PDF.addImage(classNameBase64,'JPEG',15,5,200,10);
+						PDF.addImage(classNameBase64, 'JPEG', 15, 5, 200, 10);
 						PDF.setFontSize(12);
 						PDF.setFont('Times New Roman')
 						// PDF.text(`${data.pageNumber} / ${data.doc.internal.pages.length}`, a4Width / 2 - 5, a4Height - 4,'center')
 					},
 					didDrawCell: (cell) => {
-						if(cell.column.dataKey === 0 && cell.section === 'body'){
+						if (cell.column.dataKey === 0 && cell.section === 'body') {
 							let defaultImg = require('@/assets/image/tmd_logo.png')
 							let url = datas[cell.row.index].picture || defaultImg
 							var img = new Image();
 							img.src = url;
-							PDF.addImage(img,'JPEG', cell.cursor.x + 5, cell.cursor.y + 2,10,10);
+							PDF.addImage(img, 'JPEG', cell.cursor.x + 5, cell.cursor.y + 2, 10, 10);
 						}
-						if(cell.column.dataKey === 1 && cell.section === 'body'){
+						if (cell.column.dataKey === 1 && cell.section === 'body') {
 							let nameBase64 = namesArr[cell.row.index]
-							PDF.addImage(nameBase64,'JPEG', cell.cursor.x + 5, cell.cursor.y + 4,20,8);
+							PDF.addImage(nameBase64, 'JPEG', cell.cursor.x + 5, cell.cursor.y + 4, 20, 8);
 						}
-						if(cell.column.dataKey === 4 && cell.section === 'body'){
+						if (cell.column.dataKey === 4 && cell.section === 'body') {
 							PDF.setDrawColor(0);
 							PDF.setLineWidth(0.6);
-							PDF.rect(cell.cursor.x + 5, cell.cursor.y + 4,5,5);
+							PDF.rect(cell.cursor.x + 5, cell.cursor.y + 4, 5, 5);
 						}
 					}
 				})
 				PDF.save(pdfName + '.pdf')
 				resolve(200)
-			}catch(e){
+			} catch (e) {
 				reject(500)
 			}
 		})
 	},
 	/* 根据图片生成PDF */
-	generatePdfByImgs(urls,pdfName){
-		return new Promise(async (resolve,reject) => {
-			try{
+	generatePdfByImgs(urls, pdfName) {
+		return new Promise(async (resolve, reject) => {
+			try {
 				const a4Height = 297
 				const a4Width = 210
 				let PDF = new JsPDF({
@@ -865,32 +916,32 @@ export default {
 					format: 'a4',
 					putOnlyUsedFonts: true
 				})
-				urls.forEach((url,index) => {
+				urls.forEach((url, index) => {
 					var img = new Image();
 					img.src = url;
-					PDF.addImage(img,'JPEG', 0,0,a4Height,a4Width);
-					if(index !== urls.length - 1){
+					PDF.addImage(img, 'JPEG', 0, 0, a4Height, a4Width);
+					if (index !== urls.length - 1) {
 						PDF.addPage()
 					}
 				})
 				PDF.save(pdfName + '.pdf')
 				resolve(200)
-			}catch(e){
+			} catch (e) {
 				reject(500)
 				console.log(e)
 			}
 		})
 	},
 	/* 生成名字图片 */
-	getImgsBase64ByUrls(urls){
-		return new Promise((resolve,reject) => {
+	getImgsBase64ByUrls(urls) {
+		return new Promise((resolve, reject) => {
 			let promiseArr = []
 			urls.forEach(url => {
-				promiseArr.push(new Promise((r,j) => {
+				promiseArr.push(new Promise((r, j) => {
 					console.error(url)
 					var img = new Image();
 					img.src = url
-					img.onload=function(){
+					img.onload = function () {
 						var canvas = document.createElement("canvas");
 						canvas.width = img.width;
 						canvas.height = img.height;
@@ -903,14 +954,14 @@ export default {
 			})
 			Promise.all(promiseArr).then(result => {
 				resolve(result)
-			}).catch(e =>{
+			}).catch(e => {
 				reject(e)
 			})
 		})
 	},
 	/* 生成班级名称图片 */
-	getClassNameBase64(name){
-		return new Promise((resolve,reject) => {
+	getClassNameBase64(name) {
+		return new Promise((resolve, reject) => {
 			let dom = document.createElement('div')
 			dom.style.width = '200mm'
 			dom.style.height = '10mm'
@@ -923,15 +974,15 @@ export default {
 					dom.remove()
 					resolve(pageData)
 				})
-			},100)
+			}, 100)
 		})
 	},
 	/* 生成名字图片 */
-	getNamesBase64(arr){
-		return new Promise((resolve,reject) => {
+	getNamesBase64(arr) {
+		return new Promise((resolve, reject) => {
 			let promiseArr = []
 			arr.forEach(name => {
-				promiseArr.push(new Promise((r,j) => {
+				promiseArr.push(new Promise((r, j) => {
 					let dom = document.createElement('div')
 					dom.style.width = '20mm'
 					dom.style.height = '8mm'
@@ -944,7 +995,7 @@ export default {
 							dom.remove()
 							r(pageData)
 						})
-					},100)
+					}, 100)
 				}))
 			})
 			Promise.all(promiseArr).then(result => {
@@ -963,7 +1014,7 @@ export default {
 	},
 	/* 获取视频第一帧 */
 	getVideoBase64(url) {
-		return new Promise(function(resolve, reject) {
+		return new Promise(function (resolve, reject) {
 			let dataURL = '';
 			let video = document.createElement("video");
 			video.setAttribute('crossOrigin', 'anonymous'); //处理跨域
@@ -971,7 +1022,7 @@ export default {
 			video.setAttribute('preload', 'auto');
 			video.setAttribute('width', 150);
 			video.setAttribute('height', 90);
-			video.addEventListener('loadeddata', function() {
+			video.addEventListener('loadeddata', function () {
 				let canvas = document.createElement("canvas"),
 					width = video.width, //canvas的尺寸和图片一样
 					height = video.height;
@@ -983,7 +1034,7 @@ export default {
 				var img = new Image()
 				img.src = require('@/assets/icon/icon_play.png')
 				img.setAttribute('crossOrigin', 'anonymous');
-				img.onload = function() {
+				img.onload = function () {
 					//画图
 					canvas.getContext("2d").drawImage(img, 55, 25, 40, 40);
 					var data = canvas.toDataURL('image/jpeg');
@@ -998,7 +1049,7 @@ export default {
 			var vid = document.createElement('video');
 			var fileURL = url || URL.createObjectURL(file);
 			vid.src = fileURL;
-			vid.addEventListener('loadedmetadata', function() {
+			vid.addEventListener('loadedmetadata', function () {
 				resolve(vid.duration);
 			});
 			vid.remove()
@@ -1043,11 +1094,11 @@ export default {
 
 		Promise.all(promises).then(() => {
 			zip.generateAsync({
-					type: "blob"
-				}).then(content => {
-					// 生成二进制流
-					FileSaver.saveAs(content, zipName + ".zip"); // 利用file-saver保存文件
-				})
+				type: "blob"
+			}).then(content => {
+				// 生成二进制流
+				FileSaver.saveAs(content, zipName + ".zip"); // 利用file-saver保存文件
+			})
 				.catch(err => {
 					console.log(err);
 				});
@@ -1293,7 +1344,7 @@ export default {
 
 	},
 	/* 判断是否相等 */
-	isEqual: function(x, y) {
+	isEqual: function (x, y) {
 		// If both x and y are null or undefined and exactly the same 
 		if (x === y) {
 			return true;
@@ -1319,7 +1370,7 @@ export default {
 					continue;
 				}
 				// Numbers, Strings, Functions, Booleans must be strictly equal 
-				if (typeof(x[p]) !== "object") {
+				if (typeof (x[p]) !== "object") {
 					return false;
 				}
 				// Objects and Arrays must be tested recursively 
@@ -1348,7 +1399,7 @@ export default {
 				stu.subjects[subjectIndex].fieldPoint[index],
 				val.fScores[subjectIndex].value[index] == 0 ? 0 : (stu.subjects[subjectIndex]
 					.fieldPoint[index] / val.fScores[subjectIndex].value[index])
-				.toFixed(2)
+					.toFixed(2)
 			])
 		})
 		return result
@@ -1359,7 +1410,7 @@ export default {
 		val.classes.forEach(classItem => {
 			result.push(classItem.subjects[subjectIndex].field.map((score, index) => val.fieldwrong[
 				subjectIndex].value[index][1] == 0 ? 0 : Number(score / val.fieldwrong[subjectIndex]
-				.value[index][1]) * 100)[index])
+					.value[index][1]) * 100)[index])
 		})
 		return result
 	},
@@ -1394,7 +1445,7 @@ export default {
 				stu.subjects[subjectIndex].point[index],
 				val.kScores[subjectIndex].value[index] == 0 ? 0 : (stu.subjects[subjectIndex].point[
 					index] / val.kScores[subjectIndex].value[index])
-				.toFixed(2)
+					.toFixed(2)
 			])
 		})
 		return result
@@ -1406,7 +1457,7 @@ export default {
 			// 取当前班级在每个知识点的得分 除以知识点的总分 得到每个班在该知识点的得分率 index=>知识点下标
 			result.push(classItem.subjects[subjectIndex].point.map((score, pointIndex) => val.wrong[
 				subjectIndex].value[pointIndex][1] == 0 ? 0 : Number(score / val.wrong[subjectIndex]
-				.value[pointIndex][1]) * 100)[index])
+					.value[pointIndex][1]) * 100)[index])
 		})
 		return result
 	},

+ 41 - 2
TEAMModelOS/ClientApp/src/view/areaMgmt/AreaData.vue

@@ -269,8 +269,44 @@ export default {
             let m = this.settingHours.limitMinutes
             let text = `(${m}分钟)`
             let ss = `未修满${m > 0 ? text : ''}能力点`
-            let header = ['学校', '姓名', '总学时', '线上研修学时', ss, '已修满的能力点数量', '认证材料学时', '校本研修学时', '课堂实录学时', '认证材料需提交数', '认证材料未提交数', '未提交认证材料能力点', '作业未提交数', '课堂实录是否提交']
-            let keys = ['schoolName', 'name', 'totalTime', 'online', 'unFinishArr', 'finishArr', 'ability', 'offline', 'video', 'abilityCount', 'noAbilityCount', 'noAbilityNoArr', 'noHwCount', 'isSubmitVideo']
+            let header = [
+                '学校',
+                '姓名',
+                '总学时',
+                '线上研修学时',
+                '选修能力点',
+                ss,
+                '已修满的能力点数量',
+                '认证材料学时',
+                '校本研修学时',
+                '课堂实录学时',
+                '认证材料需提交数',
+                '已提交认证材料数',
+                '已提交认证材料能力点',
+                '未提交认证材料数',
+                '未提交认证材料能力点',
+                '作业未提交数',
+                '课堂实录是否提交'
+            ]
+            let keys = [
+                'schoolName',
+                'name',
+                'totalTime',
+                'online',
+                'points',
+                'unFinishArr',
+                'finishArr',
+                'ability',
+                'offline',
+                'video',
+                'abilityCount',
+                'commitCount',
+                'commitNoArr',
+                'noAbilityCount',
+                'noAbilityNoArr',
+                'noHwCount',
+                'isSubmitVideo'
+            ]
             let datas = this.teacherData.map(i => {
                 let teacherAilities = i.currency?.teacherAilities || []
                 return {
@@ -283,7 +319,10 @@ export default {
                     ability: i.submitTime,
                     offline: i.offlineTime,
                     video: i.classTime,
+                    points: teacherAilities.map(k => k.no).join(','),
                     abilityCount: teacherAilities.length,
+                    commitCount: teacherAilities.filter(i => i.zpscore > -1).length,
+                    commitNoArr: teacherAilities.filter(j => j.zpscore > -1).map(k => k.no).join(','),
                     noAbilityCount: teacherAilities.filter(i => i.zpscore === -1).length,
                     noAbilityNoArr: teacherAilities.filter(j => j.zpscore === -1).map(k => k.no).join(','),
                     noHwCount: i.offlineCountNo,

+ 8 - 86
TEAMModelOS/ClientApp/src/view/areaMgmt/SchoolDetail.vue

@@ -11,10 +11,10 @@
                         <span class="info-label">参训人数:</span>
                         <span class="info-value">{{schoolInfo.trainCount}}人</span>
                     </p>
-                    <p class="school-info-item">
+                    <!-- <p class="school-info-item">
                         <span class="info-label">总学时:</span>
                         <span class="info-value">{{allStudyHour}}</span>
-                    </p>
+                    </p> -->
                     <p class="school-info-item">
                         <span class="info-label">完成率:</span>
                         <span class="info-value">{{completeRate}}%</span>
@@ -114,7 +114,7 @@ export default {
         findSchoolData() {
             this.isLoading = true
             let params = {
-                school: this.schoolInfo.id
+                school: this.schoolInfo.schoolId
             }
             this.$api.ability.getSchoolData(params).then(
                 res => {
@@ -122,91 +122,13 @@ export default {
                         // 总学时
                         this.allStudyHour = res.classVideoTeacherTime + res.offlineTeacherTime + res.onlineTeacherTime + res.schoolScoreTeacherTime
 
-                        // 完成率
-                        this.completeRate = (res.ok50TimeCount / res.teacherCount).toFixed(1)
-
                         //教师人数
                         this.teacherCount = res.teacherCount
 
-                        // 能力点统计
-                        let deData = this.$GLOBAL.DIMENSIONS()
-                        res.abilityCount.forEach(item => {
-                            item.percent = (item.count * 100 / res.subCount).toFixed(1) + '%'
-                            let d = deData.find(i => {
-                                return i.code == item.dimension
-                            })
-                            if (d) {
-                                item.dimensionName = d.val
-                            }
-                        })
-                        this.abilityPoint = res.abilityCount
-
-                        // 能力维度统计
-                        res.dimensionCount.forEach(item => {
-                            let d = deData.find(i => {
-                                return i.code == item.dimension
-                            })
-                            if (d) {
-                                item.name = d.val
-                            }
-                        })
-                        this.dimension = res.dimensionCount
-
-                        //学时数据统计
-                        res.groupMembers.forEach(item => {
-                            // 线上研修
-                            if (item.onlineTime == 0) {
-                                this.hourData[0].uncomplete++
-                            } else if (item.onlineTime >= 20) {
-                                this.hourData[0].complete++
-                            } else {
-                                this.hourData[0].going++
-                            }
-                            // 校本研修
-                            if (item.offlinelTime == 0) {
-                                this.hourData[1].uncomplete++
-                            } else if (item.onlineTime >= 10) {
-                                this.hourData[1].complete++
-                            } else {
-                                this.hourData[1].going++
-                            }
-                            // 应用考核
-                            if (item.schoolScoreTime == 0) {
-                                this.hourData[2].uncomplete++
-                            } else if (item.onlineTime >= 15) {
-                                this.hourData[2].complete++
-                            } else {
-                                this.hourData[2].going++
-                            }
-                            // 课堂实录
-                            if (item.classVideoTime == 0) {
-                                this.hourData[3].uncomplete++
-                            } else if (item.onlineTime >= 5) {
-                                this.hourData[3].complete++
-                            } else {
-                                this.hourData[3].going++
-                            }
-                        })
-                        this.hourData[0].studyHour = parseInt(res.videoTime / 45)
-                        this.hourData[0].time = res.videoTime
-
-                        this.hourData[1].studyHour = res.offlineTeacherTime
-                        this.hourData[1].time = res.offlineTeacherTime * 45
-
-                        this.hourData[2].studyHour = res.schoolScoreTeacherTime
-                        this.hourData[2].time = res.schoolScoreTeacherTime * 45
-
-                        this.hourData[3].studyHour = res.classVideoTeacherTime
-                        this.hourData[3].time = res.classVideoTeacherTime * 45
-
-                        //校本研修统计
-                        this.trainType = this.$GLOBAL.TRAIN_TYPE()
-                        res.studyTypes.forEach(item => {
-                            let index = item.type ? item.type - 1 : -1
-                            if (index > -1) {
-                                this.trainType[index].count = item.count
-                            }
-                        })
+                        let ok50TimeCount = res.teacherTrains?.filter(item=>item.totalTime >= 50)?.length
+                        debugger
+                        // 完成率
+                        this.completeRate = (ok50TimeCount * 100 / res.teacherCount).toFixed(1)
 
                     }
                 },
@@ -227,7 +149,7 @@ export default {
             handler(n, o) {
                 console.log('学校信息', n)
                 this.schoolInfo = n.params
-                if (this.schoolInfo && this.schoolInfo.id) {
+                if (this.schoolInfo && this.schoolInfo.schoolId) {
                     this.findSchoolData()
                 }
             },

+ 246 - 204
TEAMModelOS/ClientApp/src/view/evaluation/bank/index.vue

@@ -1,231 +1,273 @@
 <template>
-	<div class="bank-container" ref="bankContainer">
-		<div class="back-to-top flex-col-center" :title="$t('evaluation.backToTop')" @click="onBackToTop">
-			<Icon type="ios-arrow-up" />
-		</div>
-		<Tabs :value="tabName" name="listTab" @on-click="onTabClick" :animated="false">
-			<TabPane :label="$t('evaluation.index.item')" name="exercise" tab="listTab">
-				<ExerciseList ref="exList" @toggleChange="onToggleChange"></ExerciseList>
-			</TabPane>
-			<TabPane :label="$t('evaluation.index.paper')" name="paper" tab="listTab">
-				<PaperList ref="paperList" @onPaperClick="isShowBackList = true" @onBackToTop="onBackToTop"></PaperList>
-			</TabPane>
-		</Tabs>
-		<div class="ev-list-operation">
-			<div class="import-exercise common-save-btn">
-				<div class="ev-list-paper-tools" style="display: flex;" v-show="currentTab === 'paper' ">
-					<span @click="onShowPaperList" class="bank-tools-btn" v-if="isShowBackList">
-						<Icon type="md-arrow-back" size="16" />
-						<span>{{ $t('evaluation.index.backList') }}</span>
-					</span>
-					<span @click="onEditPaper" class="bank-tools-btn" v-if="isShowBackList">
-						<Icon type="ios-create" size="16" />
-						<span>{{ $t('evaluation.paperList.editPaper')}}</span>
-					</span>
-					<div style="display: flex;" v-show="($access.can('admin.*||exercise-upd') || !isSchool)">
-						<span @click="goCreatePaper('auto')" class="bank-tools-btn">
-							<Icon type="md-cube" size="16" />
-							<span>{{ $t('evaluation.index.autoCreate') }}</span>
-						</span>
-						<span @click="goCreatePaper('manual')" class="bank-tools-btn">
-							<Icon type="md-hand" size="16" />
-							<span>{{ $t('evaluation.index.manualCreate') }}</span>
-						</span>
-						<!-- <span @click="goCreatePaper('import')" class="bank-tools-btn">
+  <div class="bank-container" ref="bankContainer">
+    <div class="back-to-top flex-col-center" :title="$t('evaluation.backToTop')" @click="onBackToTop">
+      <Icon type="ios-arrow-up" />
+    </div>
+    <Tabs :value="tabName" name="listTab" @on-click="onTabClick" :animated="false">
+      <TabPane :label="$t('evaluation.index.item')" name="exercise" tab="listTab">
+        <ExerciseList ref="exList" @toggleChange="onToggleChange"></ExerciseList>
+      </TabPane>
+      <TabPane :label="$t('evaluation.index.paper')" name="paper" tab="listTab">
+        <PaperList ref="paperList" @onPaperClick="isShowBackList = true" @onBackToTop="onBackToTop"></PaperList>
+      </TabPane>
+    </Tabs>
+    <div class="ev-list-operation">
+      <div class="import-exercise common-save-btn">
+        <div class="ev-list-paper-tools" style="display: flex;" v-show="currentTab === 'paper' ">
+          <span @click="onShowPaperList" class="bank-tools-btn" v-if="isShowBackList">
+            <Icon type="md-arrow-back" size="16" />
+            <span>{{ $t('evaluation.index.backList') }}</span>
+          </span>
+          <span @click="onEditPaper" class="bank-tools-btn" v-if="isShowBackList">
+            <Icon type="ios-create" size="16" />
+            <span>{{ $t('evaluation.paperList.editPaper')}}</span>
+          </span>
+          <div style="display: flex;" v-show="($access.can('admin.*||exercise-upd') || !isSchool)">
+            <span @click="goCreatePaper('auto')" class="bank-tools-btn">
+              <Icon type="md-cube" size="16" />
+              <span>{{ $t('evaluation.index.autoCreate') }}</span>
+            </span>
+            <span @click="goCreatePaper('manual')" class="bank-tools-btn">
+              <Icon type="md-hand" size="16" />
+              <span>{{ $t('evaluation.index.manualCreate') }}</span>
+            </span>
+            <!-- <span @click="goCreatePaper('import')" class="bank-tools-btn">
 							<Icon type="md-folder" size="16"/>
 							<span>{{ $t('evaluation.index.importCreate') }}</span>
 							<Tooltip :content="$t('tip.importPaper')" class="common-toolTip" placement="bottom-end" theme="light" max-width="200">
 							    <Icon type="ios-information-circle-outline"/>
 							</Tooltip>
 						</span> -->
-					</div>
-				</div>
+          </div>
+        </div>
 
-				<span @click="goCreateExercise"
-					v-show="currentTab === 'exercise' && ($access.can('admin.*||exercise-upd') || !isSchool)"
-					class="bank-tools-btn">
-					<Icon type="md-add" size="16" />
-					<span
-						style="display: inline-block;margin-bottom: 2px;">{{ $t('evaluation.index.addExercise') }}</span>
-				</span>
-				<span @click="goCreatePaper('import',true)" class="bank-tools-btn" v-show="$access.can('admin.*||exercise-upd') || !isSchool">
-					<Icon type="md-folder" size="16" />
-					<span>{{ $t('evaluation.index.importCreate') }}</span>
-				</span>
-				<span @click="goShare" class="bank-tools-btn" v-show="$access.can('admin.*||exercise-upd')">
-					<Icon type="ios-redo" size="16"/>
-					<span>{{ $t('evaluation.share.shareResource') }}</span>
-				</span>
-			</div>
-		</div>
-	</div>
+        <span @click="goCreateExercise" v-show="currentTab === 'exercise' && ($access.can('admin.*||exercise-upd') || !isSchool)" class="bank-tools-btn">
+          <Icon type="md-add" size="16" />
+          <span style="display: inline-block;margin-bottom: 2px;">{{ $t('evaluation.index.addExercise') }}</span>
+        </span>
+        <span @click="goCreatePaper('import',true)" class="bank-tools-btn" v-show="$access.can('admin.*||exercise-upd') || !isSchool">
+          <Icon type="md-folder" size="16" />
+          <span>{{ $t('evaluation.index.importCreate') }}</span>
+        </span>
+        <span @click="goShare" class="bank-tools-btn" v-show="$access.can('admin.*||exercise-upd')">
+          <Icon type="ios-redo" size="16" />
+          <span>{{ $t('evaluation.share.shareResource') }}</span>
+        </span>
+        <span @click="doXkwAuth" class="bank-tools-btn" v-if="isTestSite">
+          <Icon type="ios-send" size="16" />
+          <span>学科网</span>
+        </span>
+      </div>
+    </div>
+  </div>
 </template>
 <script>
 
-	import BaseImport from '../components/BaseImport'
-	import PaperList from './TestPaperList'
-	import ExerciseList from './ExerciseList'
-	export default {
-		components: {
-			PaperList,
-			ExerciseList,
-			BaseImport
-		},
-		data() {
-			return {
-				isLoading: true,
-				tabName: 'exercise',
-				currentTab: 'exercise',
-				isAllOpen: false,
-				isShowBackList: false,
-			}
-		},
-		methods: {
-			onBackToTop() {
-				this.$refs.bankContainer.scrollIntoView()
-			},
-			onTabClick(val) {
-				this.currentTab = val
-				this.onBackToTop()
-			},
-			/* 展示试卷列表 */
-			onShowPaperList() {
-				this.$refs.paperList.isPreview = false
-				this.$refs.paperList.isShowSheet = false
-				this.isShowBackList = false
-				this.$refs.bankContainer.scrollIntoView()
-			},
-			/* 编辑当前预览的试卷 */
-			onEditPaper(){
-				this.$refs.paperList.goToPaper(this.$refs.paperList.curPaper)
-			},
+import BaseImport from '../components/BaseImport'
+import PaperList from './TestPaperList'
+import ExerciseList from './ExerciseList'
+export default {
+  components: {
+    PaperList,
+    ExerciseList,
+    BaseImport
+  },
+  data() {
+    return {
+      isLoading: true,
+      tabName: 'exercise',
+      currentTab: 'exercise',
+      isAllOpen: false,
+      isShowBackList: false,
+    }
+  },
+  methods: {
+    onBackToTop() {
+      this.$refs.bankContainer.scrollIntoView()
+    },
+    onTabClick(val) {
+      this.currentTab = val
+      this.onBackToTop()
+    },
+    /* 展示试卷列表 */
+    onShowPaperList() {
+      this.$refs.paperList.isPreview = false
+      this.$refs.paperList.isShowSheet = false
+      this.isShowBackList = false
+      this.$refs.bankContainer.scrollIntoView()
+    },
+    /* 编辑当前预览的试卷 */
+    onEditPaper() {
+      this.$refs.paperList.goToPaper(this.$refs.paperList.curPaper)
+    },
 
-			/** 切换全部展开与折叠 */
-			onHandleToggle() {
-				this.$refs.exList.onHandleToggle(this.isAllOpen)
-				this.isAllOpen = !this.isAllOpen
-			},
-			
-			/* 跳转到分享页面 */
-			goShare(){
-				this.$router.push({
-					name: 'shareCenter',
-					params: {
-						tabName: this.currentTab
-					}
-				})
-			},
+    /** 切换全部展开与折叠 */
+    onHandleToggle() {
+      this.$refs.exList.onHandleToggle(this.isAllOpen)
+      this.isAllOpen = !this.isAllOpen
+    },
 
-			/**
-			 * exList的collapseList变化
-			 * @param list
-			 */
-			onToggleChange(list) {
-				this.isAllOpen = list.length !== 0
-			},
+    /* 跳转到分享页面 */
+    goShare() {
+      this.$router.push({
+        name: 'shareCenter',
+        params: {
+          tabName: this.currentTab
+        }
+      })
+    },
+    /* 确认是否允许携带手机号进行注册 */
+    doConfirmAgree() {
+      return new Promise((r, j) => {
+        this.$Modal.confirm({
+          title: '授权提示',
+          content: '检测到您暂未绑定学科网账号,是否允许以醍摩豆云平台关联手机号进行认证?',
+          okText: '允许',
+          cancelText: '拒绝',
+          onOk: () => {
+            r(1)
+          },
+          onCancel: () => {
+            r(0)
+          }
+        });
+      })
+    },
 
-			/** 返回创建试题页面 */
-			goCreateExercise() {
-				this.$router.push({
-					name: this.$route.name === 'personalBank' ? 'newPrivateExercise' : 'newSchoolExercise',
-					params: {
-						scope: this.$route.name === 'personalBank' ? 'private' : 'school'
-					}
-				})
-			},
+    /* 跳转学科网 */
+    doXkwAuth() {
+      this.$api.auth.checkBind({}).then(async res => {
+        // 判断是否已经绑定学科网
+        let isBind = res.auth.find(i => i.type === 'xkw')
+        // 如果没有绑定 则询问用户是否允许携带手机号进行注册
+        let agree = isBind ? 1 : await this.doConfirmAgree()
+        // 判断资源类型
+        let module = this.currentTab === 'exercise' ? 'item' : 'paper'
+        // 存到本地
+        localStorage.setItem('xkw_module', module)
+        // 发送授权请求
+        this.$api.auth.xkwOauth({
+          module: module,
+          agree: agree
+        }).then(res => {
+          window.open(res.redirect)
+        })
+      })
+    },
 
-			/** 前往组卷页面 */
-			goCreatePaper(type) {
-				this.$router.push({
-					name: this.$route.name === 'personalBank' ? 'newPrivatePaper' : 'newSchoolPaper',
-					params: {
-						scope: this.$route.name === 'personalBank' ? 'private' : 'school',
-						type: type,
-						isFromItemBank: this.currentTab === 'exercise'
-					}
-				})
-			},
+    /**
+     * exList的collapseList变化
+     * @param list
+     */
+    onToggleChange(list) {
+      this.isAllOpen = list.length !== 0
+    },
 
-		},
-		mounted() {
-			let parentVm = this.$parent.$parent.$parent
-			parentVm.$refs['evScroll'].scrollTo({
-					y: 0
-				},
-				500, 'easeInQuad'
-			)
+    /** 返回创建试题页面 */
+    goCreateExercise() {
+      this.$router.push({
+        name: this.$route.name === 'personalBank' ? 'newPrivateExercise' : 'newSchoolExercise',
+        params: {
+          scope: this.$route.name === 'personalBank' ? 'private' : 'school'
+        }
+      })
+    },
 
-			if (this.$route.params.tabName) {
-				this.currentTab = this.$route.params.tabName
-				this.tabName = this.$route.params.tabName
-			}
+    /** 前往组卷页面 */
+    goCreatePaper(type) {
+      this.$router.push({
+        name: this.$route.name === 'personalBank' ? 'newPrivatePaper' : 'newSchoolPaper',
+        params: {
+          scope: this.$route.name === 'personalBank' ? 'private' : 'school',
+          type: type,
+          isFromItemBank: this.currentTab === 'exercise'
+        }
+      })
+    },
 
-			if (this.$route.name === 'schoolBank') {
-				this.$EventBus.$emit('showSchoolBank', true)
-			} else {
-				this.$EventBus.$emit('showSchoolBank', false)
-			}
-		},
-		computed: {
-			isSchool() {
-				return this.$route.name === "schoolBank";
-			},
-			paperScrollTop() {
-				return this.$store.state.totalAnalysis.paperScrollTop
-			},
-		},
-		// beforeRouteEnter(to, from, next) {
-		// 	if(from.name !== 'answerSheet'){
-		// 		to.meta.isKeep = false
-		// 	}else{
-		// 		// console.error('进入题库收到的',to.meta.isKeep)
-		// 		to.meta.isKeep = true
-		// 	}
-		// 	next();
-		// },
-		// beforeRouteLeave(to, from, next) {
-		// 	if(to.name === 'newSchoolPaper' || to.name === 'newPrivatePaper'){
-		// 		// 设置下一个路由的 meta
-		// 		to.meta.isKeep = false;  // 让 A 缓存,即不刷新
-		// 	}
-		// 	if(to.name === 'answerSheet'){
-		// 		from.meta.isKeep = true
-		// 	}
-		// 	next();
-		// },
+  },
+  mounted() {
+    let parentVm = this.$parent.$parent.$parent
+    parentVm.$refs['evScroll'].scrollTo({
+      y: 0
+    },
+      500, 'easeInQuad'
+    )
+
+    if (this.$route.params.tabName) {
+      this.currentTab = this.$route.params.tabName
+      this.tabName = this.$route.params.tabName
+    }
+
+    if (this.$route.name === 'schoolBank') {
+      this.$EventBus.$emit('showSchoolBank', true)
+    } else {
+      this.$EventBus.$emit('showSchoolBank', false)
+    }
+  },
+  computed: {
+    isSchool() {
+      return this.$route.name === "schoolBank";
+    },
+    paperScrollTop() {
+      return this.$store.state.totalAnalysis.paperScrollTop
+    },
+	isTestSite(){
+		return window.location.host === 'test.teammodel.cn'
 	}
+  },
+  // beforeRouteEnter(to, from, next) {
+  // 	if(from.name !== 'answerSheet'){
+  // 		to.meta.isKeep = false
+  // 	}else{
+  // 		// console.error('进入题库收到的',to.meta.isKeep)
+  // 		to.meta.isKeep = true
+  // 	}
+  // 	next();
+  // },
+  // beforeRouteLeave(to, from, next) {
+  // 	if(to.name === 'newSchoolPaper' || to.name === 'newPrivatePaper'){
+  // 		// 设置下一个路由的 meta
+  // 		to.meta.isKeep = false;  // 让 A 缓存,即不刷新
+  // 	}
+  // 	if(to.name === 'answerSheet'){
+  // 		from.meta.isKeep = true
+  // 	}
+  // 	next();
+  // },
+}
 </script>
 <style src="./index.less" lang="less" scoped></style>
 <style>
-	.bank-container .ivu-tabs {
-		overflow: unset;
-	}
+.bank-container .ivu-tabs {
+  overflow: unset;
+}
 
-	.bank-container .ivu-tabs-bar {
-		position: sticky;
-		top: 0;
-		padding: 8px 15px;
-		z-index: 99;
-		background-color: var(--body-bg);
-		margin-bottom: 15px;
-	}
+.bank-container .ivu-tabs-bar {
+  position: sticky;
+  top: 0;
+  padding: 8px 15px;
+  z-index: 99;
+  background-color: var(--body-bg);
+  margin-bottom: 15px;
+}
 
-	.bank-container .ivu-tabs-nav .ivu-tabs-tab:active,
-	.bank-container .ivu-tabs-nav .ivu-tabs-tab-active,
-	.bank-container .ivu-tabs-nav .ivu-tabs-tab {
-		color: var(--second-text-color);
-		/* font-weight: bold; */
-		font-size: 14px;
-	}
+.bank-container .ivu-tabs-nav .ivu-tabs-tab:active,
+.bank-container .ivu-tabs-nav .ivu-tabs-tab-active,
+.bank-container .ivu-tabs-nav .ivu-tabs-tab {
+  color: var(--second-text-color);
+  /* font-weight: bold; */
+  font-size: 14px;
+}
 
-	.bank-container .ivu-tabs-nav .ivu-tabs-tab-active {
-		color: var(--tabs-text-color);
-		font-weight: bold;
-	}
+.bank-container .ivu-tabs-nav .ivu-tabs-tab-active {
+  color: var(--tabs-text-color);
+  font-weight: bold;
+}
 
-	.bank-container .ivu-tabs-ink-bar {
-		height: 2px;
-		background: var(--tabs-bottom-color);
-		/*margin-left:20px;*/
-	}
+.bank-container .ivu-tabs-ink-bar {
+  height: 2px;
+  background: var(--tabs-bottom-color);
+  /*margin-left:20px;*/
+}
 </style>

+ 5 - 12
TEAMModelOS/ClientApp/src/view/jyzx/classMemoir.vue

@@ -56,8 +56,6 @@
 </template>
 
 <script>
-import BlobTool from '@/utils/blobTool.js'
-import { formatDate } from "../../utils/time.js"
 import E from "wangeditor"
 import VideoReview from '@/view/video/VideoReview.vue'
 export default {
@@ -216,35 +214,30 @@ export default {
     stemEditor.create()
   },
   methods: {
+    /* 重新提交 */
     reSubmit() {
       this.propsData = null
       this.showReview = false
       this.isPreview = false
       this.readyPreview = false
     },
+    /* 手动移除文件 */
     removeFileFinish(arr) {
       this.hasFile = arr.length > 0
     },
+    /* 视频异常重新上传 */
     doCancel() {
       this.isPreview = false
       this.readyPreview = false
       this.$refs.refFile.fileArr = []
       this.hasFile = false
     },
-    doConfirmUpload() {
-      this.isPreview = false
-      this.readyPreview = false
-      this.confirm()
-    },
-    selectFinish(file) {
+    /* 视频文件选择完毕 */
+    async selectFinish(file) {
       console.log(file)
       this.hasFile = true
       this.checkFile = file
       this.readyPreview = true
-      this.doCheckVideo()
-
-    },
-    doCheckVideo() {
       this.isPreview = true
       var video = this.checkFile;
       var url = URL.createObjectURL(video);

+ 3 - 1
TEAMModelOS/ClientApp/src/view/learnactivity/tabs/DataView.vue

@@ -63,7 +63,9 @@
                 </div>
             </div>
 
-            <p class="overview-label" v-show="examInfo.subjects && examInfo.subjects.length > 1">总览</p>
+            <p class="overview-label" v-show="examInfo.subjects && examInfo.subjects.length > 1">
+                {{$t('learnActivity.simple.overviewLabel')}}
+            </p>
             <!-- 单科或多科都会呈现 -->
             <div class="chart-wrap">
                 <!-- 成绩分布图 -->

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

@@ -385,7 +385,7 @@
                     </Select>
                 </FormItem> -->
                     <!-- 添加学科方式 -->
-                    <FormItem prop="type" :label="$t('schoolBaseInfo.addSubjectType')" v-show="!subjectInfo.id">
+                    <FormItem prop="type" :label="$t('schoolBaseInfo.addSubjectType')" v-show="!subjectInfo.id" v-if="selectionSubjects && selectionSubjects.length">
                         <RadioGroup v-model="addSubjectType">
                             <Radio label="0">
                                 <span>{{$t('schoolBaseInfo.addSubjectType1')}}</span>
@@ -396,11 +396,11 @@
                         </RadioGroup>
                     </FormItem>
                     <!-- 学科名称 -->
-                    <FormItem prop="name" :label="$t('schoolBaseInfo.subName')" v-if="addSubjectType == '0'">
+                    <FormItem prop="name" :label="$t('schoolBaseInfo.subName')" v-show="addSubjectType == '0'">
                         <Input v-model="subjectInfo.name" :placeholder="$t('schoolBaseInfo.nameWarning')"></Input>
                     </FormItem>
                     <!-- 选择已有学科 -->
-                    <FormItem prop="name" :label="$t('schoolBaseInfo.addSubjectType3')" v-if="addSubjectType == '1'">
+                    <FormItem prop="name" :label="$t('schoolBaseInfo.addSubjectType3')" v-if="selectionSubjects && selectionSubjects.length" v-show="addSubjectType == '1'">
                         <CheckboxGroup v-model="sltSubjects">
                             <Checkbox :label="item.id" v-for="(item) in selectionSubjects" :key="item.id">
                                 <span>{{item.name}}</span>

+ 4 - 2
TEAMModelOS/ClientApp/src/view/statistics/Dashboard.vue

@@ -116,7 +116,7 @@
                             <p class="data-label">全部完成</p>
                         </div>
                         <div class="data-item">
-                            <p class="data-num">{{item.voteCount || 0}}%</p>
+                            <p class="data-num">{{item.rate || 0}}%</p>
                             <p class="data-label">完成率</p>
                         </div>
                     </div>
@@ -319,6 +319,7 @@ export default {
                         // 小组数据统计
                         let groupRes = this.$jsFn.groupBy(res.teacherTrains, 'groupName')
                         groupRes.forEach(item => {
+                            let compCount = item.filter(t => t.totalTime >= this.totalTime).length
                             this.groupData.push({
                                 name: item[0].groupName,
                                 groupName: item[0].groupName,
@@ -327,7 +328,8 @@ export default {
                                 uploadCount: item.filter(t => t.currency.submitTime >= this.submitTime).length,
                                 offlineCount: item.filter(t => t.offlineTime >= this.offlineTime).length,
                                 classCount: item.filter(t => t.classTime >= this.videoTime).length,
-                                totalCount: item.filter(t => t.totalTime >= this.totalTime).length
+                                totalCount: compCount,
+                                rate: item.filter(t => t.totalTime >= this.totalTime).length * 100 / compCount
                             })
                         })
 

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

@@ -63,10 +63,10 @@
 					<transition name="indexFade">
 						<div class="el-filter-wrap" v-show="isShowFilter">
 							<div class="el-filter-item">
-								<span class="el-filter-title">类型:</span>
+								<span class="el-filter-title">{{ $t('jyzx.offline.type') }}:</span>
 								<RadioGroup v-model="filterScope" type="button" @on-change="filterScopeChange">
-									<Radio label="school">学校评测</Radio>
-									<Radio label="teacher">个人评测</Radio>
+									<Radio label="school">{{ $t('learnActivity.mgtScEv.listLabel1') }}</Radio>
+									<Radio label="teacher">{{ $t('learnActivity.mgtScEv.listLabel2') }}</Radio>
 								</RadioGroup>
 							</div>
 							<div class="el-filter-item">

+ 7 - 3
TEAMModelOS/ClientApp/src/view/teachermgmt/components/import/Import.vue

@@ -151,9 +151,9 @@ export default {
         },
         //确认导入教师信息
         confirmImport() {
-            if(!this.importData.length){
+            if (!this.importData.length) {
                 this.$Message.warning(this.$t('teachermgmt.noIpmtData'))
-                return 
+                return
             }
             let params = {
                 opt: 'upsert',
@@ -163,7 +163,11 @@ export default {
             this.$api.schoolUser.importTeacher(params).then(
                 res => {
                     this.viewStatus = 'finish'
-                    this.$store.commit('user/addTeacher', res.teacherImport?.teachers || [])
+                    let teacherImp = res.teacherImport?.teachers || []
+                    teacherImp.forEach(item => {
+                        item.roles = []
+                    })
+                    this.$store.commit('user/addTeacher', teacherImp)
                 },
                 err => {
 

+ 2 - 2
TEAMModelOS/ClientApp/src/view/teachermgmt/components/mgt/TeacherMgt.vue

@@ -882,7 +882,7 @@ export default {
         getTeachersTemp() {
             this.teachersTemp = this._.cloneDeep(this.teachers)
             this.teachersTemp.sort((a, b) => {
-                if (b.roles.includes('admin')) return 1
+                if (b.roles?.includes('admin')) return 1
                 return -1
             })
         },
@@ -1074,7 +1074,7 @@ export default {
                             name: val.id ? undefined : val.iname || val.name
                         }).then(
                             res => {
-                                this.$store.commit('user/delTeacher', [val.id || val.name])
+                                this.$store.commit('user/delTeacher', [val.id || val.name || val.iname])
                             },
                             err => {
                             }

+ 45 - 20
TEAMModelOS/ClientApp/src/view/train/TrainMgt.vue

@@ -3,10 +3,14 @@
         <Loading v-if="isLoading"></Loading>
         <div class="train-mgt-top light-iview-select light-iview-input">
             <span>{{$t('train.mgt.trainTypeLabel')}}</span>
-            <Select v-model="filter.type" style="width:200px" @on-change="filterByType">
+            <Select v-model="filter.type" style="width:200px" @on-change="filterTrian">
                 <Option v-for="item in typeList" :value="item.value" :key="item.value">{{ item.label }}</Option>
             </Select>
-            <Input v-special-char v-model="filter.keyword" suffix="ios-search" :placeholder="$t('train.mgt.search')" style="width: 200px;margin-left:10px" @on-change="searchTrain" />
+            <span style="margin-left:20px">教研组:</span>
+            <Select v-model="filter.group" style="width:200px" @on-change="filterTrian" clearable>
+                <Option v-for="item in groups" :value="item" :key="item">{{ item }}</Option>
+            </Select>
+            <Input v-special-char v-model="filter.keyword" suffix="ios-search" :placeholder="$t('train.mgt.search')" style="width: 200px;margin-left:10px" @on-change="filterTrian" />
             <span class="to-create-train" @click="toCreate">
                 <Icon type="md-add" />
                 {{$t('train.mgt.toCreate')}}
@@ -33,6 +37,10 @@
                         <span class="info-label">{{$t('train.mgt.address')}}</span>
                         <span>{{item.address}}</span>
                     </div>
+                    <div class="info-item">
+                        <span class="info-label">对象:</span>
+                        <span>{{item.gNames || '暂无'}}</span>
+                    </div>
                     <div class="info-item">
                         <span class="info-label">{{$t('train.mgt.time')}}</span>
                         <span>{{item.startTime + ' - ' + item.endTime}}</span>
@@ -47,12 +55,14 @@
 export default {
     data() {
         return {
-            isLoading:false,
+            groups: [],
+            isLoading: false,
             trainList: [],
             trainListShow: [],
             filter: {
                 type: 0,
-                keyword: ''
+                keyword: '',
+                group: ''
             },
             typeList: [
                 {
@@ -96,19 +106,21 @@ export default {
                 this.$Message.warning(this.$t('system.authErr'))
             }
         },
-        filterByType() {
-            this.trainListShow = this.trainList.filter(item => {
-                if (this.filter.type == 0) {
-                    return true
-                } else {
+        filterTrian() {
+            let data = this._.cloneDeep(this.trainList)
+            if (this.filter.type) {
+                data = data.filter(item => {
                     return item.type == this.filter.type
-                }
-            })
-        },
-        searchTrain() {
-            this.trainListShow = this.trainList.filter(item => {
-                return JSON.stringify(item).includes(this.filter.keyword)
-            })
+                })
+            }
+            if (this.filter.group) {
+                data = data.filter(item => item.groups?.includes(this.filter.group))
+            }
+
+            if (this.filter.keyword) {
+                data = data.filter(item => JSON.stringify(item).includes(this.filter.keyword))
+            }
+            this.trainListShow = data
         },
         toDetailPage(index) {
             this.$router.push({
@@ -134,19 +146,32 @@ export default {
             }
             this.$api.train.findTrainList(params).then(
                 res => {
-                    console.log(res)
-                    this.trainList = res.studies.reverse()
-                    this.trainList.forEach(item => {
+                    let resData = res.studies.reverse()
+                    // this.trainList = res.studies.reverse()
+                    let gs = []
+                    resData.forEach(item => {
                         item.img = item.img || require("@/assets/image/def-train-poster.jpg")
                         item.startTime = this.dateFormat(item.startTime)
                         item.endTime = this.dateFormat(item.endTime)
+                        if (item.groupLists && item.groupLists.length) {
+                            let keys = Object.keys(item.groupLists[0])
+                            if (keys.length) {
+                                let g = item.groupLists[0][keys[0]]
+                                gs.push(...g)
+                                item.groups = g
+                                item.gNames = g.join(',')
+                            }
+                        }
                     })
+                    this.groups = new Set(gs)
+                    console.log(this.groups)
+                    this.trainList = resData
                     this.trainListShow = this.trainList
                 },
                 err => {
                     this.$Message.error('查询研修列表失败')
                 }
-            ).then(()=>{
+            ).then(() => {
                 this.isLoading = false
             })
         }

+ 16 - 0
TEAMModelOS/ClientApp/src/view/xkw/xkw.vue

@@ -0,0 +1,16 @@
+<template>
+</template>
+<script>
+export default {
+    created() {
+        this.$api.auth.xkwAuthorize({
+          module: localStorage.getItem('xkw_module', module),
+          code: this.$route.query.code
+        })
+    },
+}
+</script>
+
+<style>
+
+</style>

+ 1 - 1
TEAMModelOS/ClientApp/vue.config.js

@@ -1,6 +1,5 @@
 const path = require('path')
 const Timestamp = new Date().getTime();
-
 function resolve(dir) {
 	return path.join(__dirname, './', dir)
 }
@@ -55,6 +54,7 @@ module.exports = {
 		}
 	},
 
+
 	configureWebpack: config => {
 		config.optimization.minimizer[0].options.terserOptions.compress.drop_console = process.env.NODE_ENV ===
 			'production'

+ 107 - 0
TEAMModelOS/Controllers/Third/OAuth2Controller.cs

@@ -0,0 +1,107 @@
+using Microsoft.AspNetCore.Mvc;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using TEAMModelOS.Models;
+using TEAMModelOS.SDK.DI;
+using System.Text.Json;
+using TEAMModelOS.SDK.Models;
+using Microsoft.AspNetCore.Http;
+using TEAMModelOS.SDK.Extension;
+using Azure.Cosmos;
+using System.Text;
+using TEAMModelOS.SDK.DI.AzureCosmos.Inner;
+using Microsoft.Extensions.Options;
+using Azure.Messaging.ServiceBus;
+using Microsoft.Extensions.Configuration;
+using HTEXLib.COMM.Helpers;
+using TEAMModelOS.SDK;
+using System.IdentityModel.Tokens.Jwt;
+using TEAMModelOS.Services;
+using TEAMModelOS.SDK.Models.Service;
+using System.IO;
+using System.Dynamic;
+using Microsoft.AspNetCore.Authorization;
+using Azure.Storage.Blobs.Models;
+using static TEAMModelOS.SDK.Models.Teacher;
+using System.Web;
+using static TEAMModelOS.Controllers.FixDataController;
+using static TEAMModelOS.SDK.SchoolService;
+using Microsoft.AspNetCore.Hosting;
+using TEAMModelOS.Filter;
+using TEAMModelOS.Controllers.Third.Xkw;
+using Microsoft.Extensions.Primitives;
+using System.Net.Http;
+namespace TEAMModelOS.Controllers.Third
+{
+    
+    
+    // <summary>
+    ///  标准OAuth2
+    /// </summary>
+    ///  
+    [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status400BadRequest)]
+    //
+    //[Route("")]
+    [Route("oauth")]
+    [ApiController]
+    public class OAuth2Controller : ControllerBase
+    {
+        private readonly SnowflakeId _snowflakeId;
+        private readonly AzureCosmosFactory _azureCosmos;
+        private readonly DingDing _dingDing;
+        private readonly Option _option;
+        private readonly AzureStorageFactory _azureStorage;
+        private readonly AzureServiceBusFactory _serviceBus;
+        private readonly AzureRedisFactory _azureRedis;
+        private readonly CoreAPIHttpService _coreAPIHttpService;
+        private readonly ThirdApisService _scsApisService;
+        private readonly HttpTrigger _httpTrigger;
+        private readonly IWebHostEnvironment _environment;
+        /// <summary>
+        /// 机构安全码
+        /// </summary>
+        public string _sc_passKey;
+        /// <summary>
+        /// 机构ID
+        /// </summary>
+        public string _sc_trainComID;
+        /// <summary>
+        /// 机构 AES 密钥
+        /// </summary>
+        public string _sc_privateKey;
+        /// <summary>
+        /// 访问地址
+        /// </summary>
+        public string _sc_url;
+        public IConfiguration _configuration { get; set; }
+        public OAuth2Controller(IWebHostEnvironment environment, AzureCosmosFactory azureCosmos, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option, AzureStorageFactory azureStorage,
+          AzureRedisFactory azureRedis, AzureServiceBusFactory serviceBus, IConfiguration configuration, CoreAPIHttpService coreAPIHttpService, ThirdApisService scsApisService, HttpTrigger httpTrigger)
+        {
+            _azureCosmos = azureCosmos;
+            _snowflakeId = snowflakeId;
+            _dingDing = dingDing;
+            _option = option?.Value;
+            _azureStorage = azureStorage;
+            _serviceBus = serviceBus;
+            _configuration = configuration;
+            _azureRedis = azureRedis;
+            _coreAPIHttpService = coreAPIHttpService;
+            _scsApisService = scsApisService;
+            _httpTrigger = httpTrigger;
+            _environment = environment;
+        }
+        [HttpPost("check-bind")]
+        [Authorize(Roles = "IES")]
+        [AuthToken(Roles = "teacher,admin,area,student")]
+        public async Task<IActionResult> Bind(JsonElement json)
+        {
+            var (tmdid, _, _, school) = HttpContext.GetAuthTokenInfo();
+            var table = _azureStorage.GetCloudTableClient().GetTableReference("IESOAuth");
+            List<OAuthUser> authUsers = await table.FindListByDict<OAuthUser>(new Dictionary<string, object>() { { "RowKey", tmdid } });
+            return Ok(new { auth = authUsers.Select(x => new { type = x.Type, tmdid = x.RowKey, time = x.Time }) });
+        }
+    }
+}

+ 2 - 2
TEAMModelOS/Controllers/Third/Xkw/OpenAuthClient.cs

@@ -111,9 +111,9 @@ namespace TEAMModelOS.Controllers.Third.Xkw
                 string url = string.Format(TOKEN_URL + "?client_id={0}&code={1}&redirect_uri={2}", AppKey, code, RedirectUrl);
                 string retUrl = url + "&signature=" + SignatureHelper.GenerateSignature(url, AppSecret);
                 string data = client.DownloadString(retUrl);
-                IDictionary<string, string> access_token_dic = data.ToObject<IDictionary<string,string>>();
+                IDictionary<string, object> access_token_dic = data.ToObject<IDictionary<string, object>>();
                 if (access_token_dic.ContainsKey("access_token"))
-                    AccessToken = access_token_dic["access_token"];
+                    AccessToken = $"{access_token_dic["access_token"]}";
                 //获取用户openid
                 string userProfileUrl = string.Format(PROFILE_URL + "?access_token={0}&schoolId={1}", AccessToken, schoolId);
                 string ret = client.DownloadString(userProfileUrl);

+ 67 - 33
TEAMModelOS/Controllers/Third/Xkw/XkwOAuth2Controller.cs

@@ -32,6 +32,7 @@ using Microsoft.AspNetCore.Hosting;
 using TEAMModelOS.Filter;
 using TEAMModelOS.Controllers.Third.Xkw;
 using Microsoft.Extensions.Primitives;
+using System.Net.Http;
 
 namespace TEAMModelOS.Controllers
 {
@@ -98,12 +99,13 @@ namespace TEAMModelOS.Controllers
         /// <param name="request"></param>
         /// <returns></returns>
 
-        [HttpGet("oauth")]
+        [HttpPost("oauth")]
         [Authorize(Roles = "IES")]
         [AuthToken(Roles = "teacher,admin,area,student")]
-        public async Task<IActionResult> Aauth() {
+        public async Task<IActionResult> Aauth(OAuthCode authCode) {
             //https://ssoserviceurl/oauth2/authorize?client_id=APPKEY&openid=OPENID=&service=SERVICE
             var (tmdid, _, _, school) = HttpContext.GetAuthTokenInfo();
+
             StringValues accessToken = "";//应该从别的地方获取 不是mvc 无法从Session 获取 
             HttpContext.Request.Headers.TryGetValue($"XKW-AccessToken", out accessToken);
             if (!_option.Location.Contains("China"))
@@ -111,15 +113,31 @@ namespace TEAMModelOS.Controllers
                 return BadRequest();
             }
             var client = await GetOpenAuthClient(tmdid, accessToken);
+            if (authCode.agree == 1) {
+                //获取醍摩豆id的手机号
+                var keys =new List<string> { tmdid};
+                var content = new StringContent(keys.ToJsonString(), Encoding.UTF8, "application/json");
+                string ujson = await _coreAPIHttpService.GetUserInfos(content);
+                List<CoreUser> coreUsers = new List<CoreUser>(0);
+                if (!string.IsNullOrWhiteSpace(ujson))
+                {
+                    coreUsers = ujson.ToObject<List<CoreUser>>();
+                    if (coreUsers.Any() ) {
+
+                        client.Extra = coreUsers.Find(x=>x.searchKey.Equals(tmdid))?.mobile;
+                    }
+                }
+            }
             string url = client.GetAuthorizationUrl();
-            return Redirect(url);
+            return Ok(new { redirect = url });
         }
-        [HttpGet("authorized")]
+        [HttpPost("authorize")]
+        [Authorize(Roles = "IES")]
         [AuthToken(Roles = "teacher,admin,area,student")]
-        public async Task<IActionResult> Authorized([FromQuery] OAuthCode authCode  )
+        public async Task<IActionResult> Authorize(OAuthCode authCode  )
         {
             var (tmdid, _, _, school) = HttpContext.GetAuthTokenInfo();
-            StringValues accessToken = "";//应该从别的地方获取 不是mvc 无法从Session 获取 
+            StringValues accessToken ;//应该从别的地方获取 不是mvc 无法从Session 获取 
             HttpContext.Request.Headers.TryGetValue($"XKW-AccessToken", out accessToken);
             if (!_option.Location.Contains("China"))
             {
@@ -131,46 +149,58 @@ namespace TEAMModelOS.Controllers
                 return RedirectToAction("Index");
             }
             var client =await GetOpenAuthClient(tmdid, accessToken);
-            string schoolId = null;
-            //UserTxtHelper userHelper = new UserTxtHelper(@USER_PATH);
-            //User currentUser = userHelper.GetUserInfoByUserId(client.UserId);
-            //if (currentUser != null)
-            //{
-            //    schoolId = currentUser.SchoolId;
-            //}
-             client.GetAccessTokenByCode(authCode.code, schoolId);
+            string schoolId = "tmdedu";
+            if (_option.Location.Contains("Test", StringComparison.OrdinalIgnoreCase) || _option.Location.Contains("Dep", StringComparison.OrdinalIgnoreCase))
+            {
+                schoolId = "3082";
+            }
+            client.GetAccessTokenByCode(authCode.code, schoolId);
             //未登录已认证学科网用户
             if (string.IsNullOrEmpty(client.UserId) || "".Equals(client.UserId.Trim()))
             {
-                return RedirectToAction("bind", "xkw", new { openId = client.OpenId, userId = client.UserId });
+                return Redirect($"bind?status=0&accessToken={client.AccessToken}&openId={client.OpenId}&userId={client.UserId}&msg={HttpUtility.UrlEncode("未登录")}");
             }
             if (string.IsNullOrEmpty(client.OpenId))
             {
-                string errorMsg = client.ErrorMessage;
-                return RedirectToAction("bind", "xkw", new { openId = client.OpenId, userId = client.UserId, msg = errorMsg });
+                string errorMsg = "学科网"+client.ErrorMessage;
+                return Redirect($"bind?status=0&accessToken={client.AccessToken}&openId={client.OpenId}&userId={client.UserId}&msg={HttpUtility.UrlEncode(errorMsg)}");
             }
 
-            if (client.IsAuthorized)
+            if (client.IsAuthorized || !string.IsNullOrWhiteSpace(client.OpenId))
             {
-                //用session记录access token
-                //Session["access_token"] = client.AccessToken;
-                //用cookie记录userId
-                ///  Response.AppendCookie(new HttpCookie("userId", client.UserId) { Expires = DateTime.Now.AddDays(7) });
-                return RedirectToAction("bind", "xkw", new { client.AccessToken,openId = client.OpenId, userId = client.UserId });
+                return Redirect($"bind?status=1&accessToken={client.AccessToken}&openId={client.OpenId}&userId={client.UserId}&msg={HttpUtility.UrlEncode("认证成功")}");
             }
             else
             {
-                return RedirectToAction("Index");
+                return Redirect($"bind?status=0&accessToken={client.AccessToken}&openId={client.OpenId}&userId={client.UserId}&msg={HttpUtility.UrlEncode("认证失败")}");
             }
         }
+        
+
         [HttpGet("bind")]
-        public ActionResult Bind(String openId, String userId, String msg)
+        public async Task<IActionResult> Bind([FromQuery] XkwBindModel authCode)
         {
+            if (authCode.status == 1)
+            {
+                var table = _azureStorage.GetCloudTableClient().GetTableReference("IESOAuth");
+                OAuthUser authUser = new OAuthUser
+                {
+                    PartitionKey = "OAuthUser-Xkw",
+                    RowKey = authCode.userId,
+                    OpenId = authCode.openId,
+                    Time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+                    Type = "Xkw"
+                };
+                await table.SaveOrUpdate<OAuthUser>(authUser);
+                return Ok(new { status=authCode.status, msg = "绑定成功!" });
+            }
+            else {
+                return Ok(new { status = authCode.status, msg = authCode.msg });
+            }
            
-            return Ok();
         }
         [HttpGet("unbind")]
-        public ActionResult Unbind(String openId, String userId)
+        public async Task<IActionResult> Unbind(String openId, String userId)
         {
             //bool ret = xkwOAuthTxtHelper.UnBindXkw(userId);
             //string msg = "无解绑关系";
@@ -190,7 +220,7 @@ namespace TEAMModelOS.Controllers
         /// 退出登录
         /// </summary>
         /// <returns></returns>
-        [HttpGet("unbind")]
+        [HttpGet("exit")]
         public ActionResult Exit()
         {
             //HttpCookie uk = new HttpCookie("userId");
@@ -209,20 +239,24 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         private async Task<XkwOAuthClient> GetOpenAuthClient(string tmdid,string accessToken)
         {
+            var table = _azureStorage.GetCloudTableClient().GetTableReference("IESOAuth");
             //var accessToken = Session["access_token"] == null ? string.Empty : (string)Session["access_token"];
             //var userId = Request.Cookies["userId"] == null ? string.Empty : Request.Cookies["userId"].Value;
             var userId = tmdid;//直接传递获取
             //var openId = xkwOAuthTxtHelper.GetOpenIdByUserId(userId);
-            var openId = "";//直接从数据库获取
-                            // var settings = ConfigurationManager.AppSettings;
-                            // var client = new XkwOAuthClient(settings["OAuth_Xkw_AppKey"], settings["OAuth_Xkw_AppSecret"], settings["OAuth_Xkw_RedirectUrl"], settings["OAuth_Xkw_OAuthHost"], accessToken, openId, userId);
+            string openId =null;//直接从数据库获取
+                                // var settings = ConfigurationManager.AppSettings;
+                                // var client = new XkwOAuthClient(settings["OAuth_Xkw_AppKey"], settings["OAuth_Xkw_AppSecret"], settings["OAuth_Xkw_RedirectUrl"], settings["OAuth_Xkw_OAuthHost"], accessToken, openId, userId);
 
-            var table = _azureStorage.GetCloudTableClient().GetTableReference("IESOAuth");
+            List<OAuthUser> authUsers = await table.FindListByDict<OAuthUser>(new Dictionary<string, object>() { { "PartitionKey", "OAuthUser-Xkw" }, { "RowKey", tmdid } });
+            if (authUsers.Any()) {
+                openId = authUsers[0].OpenId;
+            }
             string RowKey = "Xkw";
             if (_option.Location.Contains("Test", StringComparison.OrdinalIgnoreCase) || _option.Location.Contains("Dep", StringComparison.OrdinalIgnoreCase)) {
                 RowKey = "Xkw-Test";
             }
-            List<OAuthComConfig> configs = await table.FindListByDict<OAuthComConfig>(new Dictionary<string, object>() { { "PartitionKey", "OAuthComConfig" }, { "RowKey",RowKey } });
+            List<OAuthComConfig> configs = await table.FindListByDict<OAuthComConfig>(new Dictionary<string, object>() { { "PartitionKey", "OAuthComConfig" }, { "RowKey", RowKey } });
             if (configs.Any())
             {
                 string OAuth_Xkw_AppKey = configs[0].AppKey;