Bladeren bron

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

zhouj1203@hotmail.com 3 jaren geleden
bovenliggende
commit
c5869179fd
45 gewijzigde bestanden met toevoegingen van 3827 en 3124 verwijderingen
  1. 27 13
      TEAMModelBI/Controllers/DingDingStruc/TableDingDingInfoController.cs
  2. 8 10
      TEAMModelOS.FunctionV4/ServiceBus/ActiveTaskTopic.cs
  3. 2 0
      TEAMModelOS.SDK/DI/CoreAPI/CoreAPIHttpService.cs
  4. 5 0
      TEAMModelOS.SDK/Models/Cosmos/Normal/AreaSetting.cs
  5. 1 1
      TEAMModelOS.SDK/Models/Service/GroupListService.cs
  6. 35 38
      TEAMModelOS.SDK/Models/Service/StatisticsService.cs
  7. 5 0
      TEAMModelOS/ClientApp/src/api/ability.js
  8. 21 44
      TEAMModelOS/ClientApp/src/assets/iconfont/demo_index.html
  9. 7 11
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.css
  10. 1 1
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.js
  11. 7 14
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.json
  12. BIN
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.ttf
  13. BIN
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff
  14. BIN
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff2
  15. 13 0
      TEAMModelOS/ClientApp/src/common/AbilityUpload.vue
  16. 1 1
      TEAMModelOS/ClientApp/src/common/BaseSelectArea.vue
  17. 1 1
      TEAMModelOS/ClientApp/src/common/BaseSelectSchool.vue
  18. 5 4
      TEAMModelOS/ClientApp/src/components/homework/BaseHwForm.vue
  19. 1 9
      TEAMModelOS/ClientApp/src/components/homework/BaseHwTable.vue
  20. 1 1
      TEAMModelOS/ClientApp/src/components/student-web/ClassRecord/ClassRecord.vue
  21. 1 1
      TEAMModelOS/ClientApp/src/components/student-web/HomeView/CourseListView.vue
  22. 4 0
      TEAMModelOS/ClientApp/src/locale/lang/en-US/homework.js
  23. 4 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/homework.js
  24. 4 0
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/homework.js
  25. 1 4
      TEAMModelOS/ClientApp/src/router/routes.js
  26. 7 1
      TEAMModelOS/ClientApp/src/utils/js-fn.js
  27. 48 10
      TEAMModelOS/ClientApp/src/view/areaMgmt/AreaData.vue
  28. 4 1
      TEAMModelOS/ClientApp/src/view/areaSetting/AreaSetting.vue
  29. 4 0
      TEAMModelOS/ClientApp/src/view/assessment/Assessment.less
  30. 1240 1254
      TEAMModelOS/ClientApp/src/view/assessment/Assessment.vue
  31. 37 35
      TEAMModelOS/ClientApp/src/view/homepage/HomePage.vue
  32. 10 25
      TEAMModelOS/ClientApp/src/view/homework/ManageHomeWork.vue
  33. 515 399
      TEAMModelOS/ClientApp/src/view/jyzx/classMemoir.vue
  34. 3 3
      TEAMModelOS/ClientApp/src/view/jyzx/index.vue
  35. 8 2
      TEAMModelOS/ClientApp/src/view/jyzx/newHomePage.vue
  36. 60 60
      TEAMModelOS/ClientApp/src/view/login/Index.vue
  37. 1 0
      TEAMModelOS/ClientApp/src/view/login/jinniu/Index.vue
  38. 35 35
      TEAMModelOS/ClientApp/src/view/login/page/Teacher.vue
  39. 18 18
      TEAMModelOS/ClientApp/src/view/sso/Index.vue
  40. 16 4
      TEAMModelOS/ClientApp/src/view/video/VideoReview.vue
  41. 1076 1108
      TEAMModelOS/ClientApp/src/view/vote/ManageVote.vue
  42. 168 0
      TEAMModelOS/Controllers/Normal/AbilityStatisticsController.cs
  43. 299 0
      TEAMModelOS/Controllers/Third/Xkw/OpenAuthClient.cs
  44. 117 5
      TEAMModelOS/Controllers/Third/Xkw/XkwOAuth2Controller.cs
  45. 6 11
      TEAMModelOS/appsettings.Development.json

+ 27 - 13
TEAMModelBI/Controllers/DingDingStruc/TableDingDingInfoController.cs

@@ -801,21 +801,35 @@ namespace TEAMModelBI.Controllers.DingDingStruc
                 }
                 else
                 {
-                    foreach (var user in tempUser)
+                    var userInfos = await table.FindListByDict<DingDingUserInfo>(new Dictionary<string, object> { { "PartitionKey", $"{partitionKey}" } });
+
+                    var adminInfos = userInfos.FindAll(x => x.roles.Contains("admin"));
+                    if (adminInfos.Count == 1)
+                    {
+                        return Ok(new { state = 403, msg = "已经是最后一个管理员了" });
+                    }
+                    else
                     {
-                        List<string> tempRoles = new(user.roles.Split(","));                        
-                        if (tempRoles.Contains("admin")) 
+                        foreach (var user in tempUser)
                         {
-                            tempRoles.Remove("admin");
-                        }
-                        user.roles = string.Join(",", tempRoles);
-                        
-                        DingDingUserInfo respUser = await table.SaveOrUpdate<DingDingUserInfo>(user);
+                            if (!user.userId.Equals($"{did}"))
+                            {
+                                List<string> tempRoles = new(user.roles.Split(","));
+                                if (tempRoles.Contains("admin"))
+                                {
+                                    tempRoles.Remove("admin");
+                                }
+                                user.roles = string.Join(",", tempRoles);
 
-                        if (respUser != null)
-                        {
-                            roles = !string.IsNullOrEmpty($"{respUser.roles}") ? new List<string>(respUser.roles.Split(",")) : new List<string>();
-                            msg.Append($"取消{respUser.name}【{respUser.RowKey}】账号的BI管理员");
+                                DingDingUserInfo respUser = await table.SaveOrUpdate<DingDingUserInfo>(user);
+
+                                if (respUser != null)
+                                {
+                                    roles = !string.IsNullOrEmpty($"{respUser.roles}") ? new List<string>(respUser.roles.Split(",")) : new List<string>();
+                                    msg.Append($"取消{respUser.name}【{respUser.RowKey}】账号的BI管理员");
+                                }
+                            }
+                            else return Ok(new { state = 1, msg = "不能删除自己" });
                         }
                     }
                 }
@@ -1115,7 +1129,7 @@ namespace TEAMModelBI.Controllers.DingDingStruc
                                 tmdMobile = "",
                                 mail = "",
                                 picture = "",
-                                roles = "assist",
+                                roles = "",
                                 joinTime = 0,
                                 permissions = "areadata-read,areadata-upd,schooldata-read,schooldata-upd",
                                 schoolIds = ""

+ 8 - 10
TEAMModelOS.FunctionV4/ServiceBus/ActiveTaskTopic.cs

@@ -343,21 +343,21 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                 await _dingDing.SendBotMsg($"ServiceBus,RefreshBlob:空间计算已经超过{timeout}秒\n容器名:{name}\n文件夹:{u}\n计算时长:{dis}", GroupNames.醍摩豆服務運維群組);
             }
         }
-
-        /// <summary>
-        /// 完善课程变更,StuListChange,  originCode是学校编码 则表示名单是学校自定义名单,如果是tmdid则表示醍摩豆的私有名单,scope=school,private。
-        /// </summary>
-        /// <data msg>
-        /// CourseChange
-        ///// </data>
         /// <param name="msg"></param>
         /// <returns></returns>
         [Function("TeacherTrainChange")]
         public async Task TeacherTrainChangeFunc([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "teacher-train-change", Connection = "Azure:ServiceBus:ConnectionString")] string msg)
+        { 
+        
+        }
+        
+        /// <param name="msg"></param>
+        /// <returns></returns>
+        //[Function("TeacherTrainChange")]
+        public async Task TeacherTrainChangeFuncBak([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "teacher-train-change", Connection = "Azure:ServiceBus:ConnectionString")] string msg)
         {
             try
             {
-                // await _dingDing.SendBotMsg($"teacher-train-change\n{msg}",GroupNames.成都开发測試群組);
                 TeacherTrainChange change = msg.ToObject<TeacherTrainChange>();
                 if (change.update == null || change.update.Count <= 0 || change.tmdids.IsEmpty())
                 {
@@ -375,7 +375,6 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
 
                 if (change.statistics != 1)
                 {
-
                     List<Task<ItemResponse<TeacherTrain>>> task = new List<Task<ItemResponse<TeacherTrain>>>();
                     teacherTrains.ForEach(x =>
                     {
@@ -419,7 +418,6 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                     {
                         try
                         {
-                            //优先找校级
                             setting = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<AreaSetting>(change.school, new PartitionKey("AreaSetting"));
                         }
                         catch (CosmosException)

+ 2 - 0
TEAMModelOS.SDK/DI/CoreAPI/CoreAPIHttpService.cs

@@ -38,6 +38,8 @@ namespace TEAMModelOS.SDK
     }
     public class CoreAPIHttpService
     {
+
+        public bool check=true;
         private readonly HttpClient _httpClient;
         public readonly IOptionsMonitor<CoreAPIHttpServiceOptions> options;
         public CoreAPIHttpService(HttpClient httpClient, IOptionsMonitor<CoreAPIHttpServiceOptions> optionsMonitor)

+ 5 - 0
TEAMModelOS.SDK/Models/Cosmos/Normal/AreaSetting.cs

@@ -34,11 +34,16 @@ namespace TEAMModelOS.SDK.Models
         /// 学时换算(多少分钟换算一个学时)
         /// </summary>
         public int lessonMinutes { get; set; } = 45;
+
         /// <summary>
         /// 限制分钟数
         /// </summary>
         public int limitMinutes { get; set; } = -1;
         /// <summary>
+        /// 限制修满多少能力点个数
+        /// </summary>
+        public int limitAbility { get; set; } = 3;
+        /// <summary>
         /// 文档类型
         /// </summary>
         public List<string> doc { get; set; } = new List<string>();

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

@@ -1008,7 +1008,7 @@ namespace TEAMModelOS.SDK
                     }
                 }
 
-                if (tmdids.IsNotEmpty()) {
+                if (tmdids.IsNotEmpty() && _coreAPIHttpService.check) {
                     ///获取真实的名称 
                     var content = new StringContent(tmdids.Select(x => x.id).ToHashSet().ToJsonString(), Encoding.UTF8, "application/json");
                     string json = null;

+ 35 - 38
TEAMModelOS.SDK/Models/Service/StatisticsService.cs

@@ -64,7 +64,9 @@ namespace TEAMModelOS.SDK
                 }
                 List<Task<ItemResponse<TeacherTrain>>> task = new List<Task<ItemResponse<TeacherTrain>>>();
                 teacherTrains.ForEach(x => {
-                    x.update.UnionWith(change.update);
+                    change.update.ToList().ForEach(u => {
+                        x.update.Add(u);
+                    });
                     task.Add(client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<TeacherTrain>(x, x.id, new PartitionKey($"TeacherTrain-{change.school}")));
                 });
                 await task.TaskPage(5);
@@ -83,7 +85,9 @@ namespace TEAMModelOS.SDK
                             update = new HashSet<string> {  StatisticsService.TeacherAbility,
                         StatisticsService.TeacherClass, StatisticsService.OfflineRecord }
                         };
-                        teacherTrain.update.UnionWith(change.update);
+                        change.update.ToList().ForEach(u => {
+                            teacherTrain.update.Add(u);
+                        }); 
                         task.Add(client.GetContainer(Constant.TEAMModelOS, "Teacher").CreateItemAsync<TeacherTrain>(teacherTrain, new PartitionKey($"TeacherTrain-{change.school}")));
                     });
                     await task.TaskPage(5);
@@ -105,7 +109,9 @@ namespace TEAMModelOS.SDK
                 }
                 List<Task<ItemResponse<TeacherTrain>>> task = new List<Task<ItemResponse<TeacherTrain>>>();
                 teacherTrains.ForEach(x => {
-                    x.update.UnionWith(list.update);
+                    list.update.ForEach(u => {
+                        x.update.Add(u);
+                    });
                     task.Add(client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<TeacherTrain>(x, x.id, new PartitionKey($"TeacherTrain-{list.school}")));
                 });
                 await task.TaskPage(5);
@@ -124,28 +130,15 @@ namespace TEAMModelOS.SDK
                             update = new HashSet<string> {  StatisticsService.TeacherAbility,
                         StatisticsService.TeacherClass, StatisticsService.OfflineRecord }
                         };
-                        teacherTrain.update.UnionWith(list.update);
+                        list.update.ForEach(u => {
+                            teacherTrain.update.Add(u);
+                        });
+                       
                         task.Add(client.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<TeacherTrain>(teacherTrain, new PartitionKey($"TeacherTrain-{list.school}")));
                     });
                     await task.TaskPage(1);
                 }
             }
-
-            //var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
-            //if (list.tmdids.IsNotEmpty() && list.update.IsNotEmpty())
-            //{
-            //    TeacherTrainChange change = new TeacherTrainChange
-            //    {
-            //        standard = list.standard,
-            //        tmdids = list.tmdids,
-            //        school = list.school,
-            //        update = new HashSet<string>(list.update),
-            //        statistics = list.statistics
-            //    };
-            //    var messageChange = new ServiceBusMessage(change.ToJsonString());
-            //    messageChange.ApplicationProperties.Add("name", "TeacherTrainChange");
-            //    await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageChange);
-            //}
         }
 
         public static async Task GetAreaAndAreaSetting(string schoolId, string _standard, CosmosClient client, HttpContext httpContext)
@@ -189,6 +182,8 @@ namespace TEAMModelOS.SDK
         }
         public static async Task<(List<TeacherTrain> trains, List<RGroupList> yxtrain)> StatisticsSchool(CoreAPIHttpService _coreAPIHttpService,string school, AreaSetting setting, Area area, CosmosClient client, DingDing _dingDing, HashSet<string> updates)
         {
+            _coreAPIHttpService.check = false;
+    
             List<RGroupList> yxtrain = await GroupListService.GetGroupListMemberByType(_coreAPIHttpService, client, "yxtrain", new List<string> { "school" }, $"{school}", _dingDing);
             List<TeacherTrain> trains = new List<TeacherTrain>();
             var members = yxtrain.SelectMany(x => x.members).ToList();
@@ -277,7 +272,7 @@ namespace TEAMModelOS.SDK
             {
                 teachers.Add(StatisticsTeacher(train, setting, area, client, studies));  //yield return await  StatisticsTeacher(  train,   setting,   area,   client);
             }
-            int pagesize = 100;
+            int pagesize = 50;
             if (teachers.Count <= pagesize)
             {
                 await Task.WhenAll(teachers);
@@ -306,7 +301,7 @@ namespace TEAMModelOS.SDK
                 {
                     teachers.Add(DoProperty(train.update, property, setting, area, client, train, studies));
                 }
-                int pagesize = 100;
+                int pagesize = 50;
                 if (teachers.Count <= pagesize)
                 {
                     await Task.WhenAll(teachers);
@@ -809,7 +804,7 @@ namespace TEAMModelOS.SDK
                 }
             }
             Currency currency = new Currency();
-            Currency currencyAll = new Currency();
+           // Currency currencyAll = new Currency();
 
 
             abilitySubs.ForEach(item => {
@@ -818,8 +813,6 @@ namespace TEAMModelOS.SDK
                 if (ability != null)
                 {
 
-
-
                     if (ability != null)
                     {
                         currencyInt = item.from == 0 ? ability.currency : 1;
@@ -835,7 +828,7 @@ namespace TEAMModelOS.SDK
                         {
                             currency.uploadDone += item.uploads.Count;
                         }
-                        currencyAll.uploadDone += item.uploads.Count;
+                      //  currencyAll.uploadDone += item.uploads.Count;
                     }
                     //通过能力点自测
                     if (item.exerciseScore > 0)
@@ -852,11 +845,11 @@ namespace TEAMModelOS.SDK
 
                         }
                         //并且完全看完视频和文档。
-                        if (item.allDone)
-                        {
-                            currencyAll.learnAbility += 1;
-                        }
-                        currencyAll.exerciseAbility += ability.abilityCount;
+                        //if (item.allDone)
+                        //{
+                        //    currencyAll.learnAbility += 1;
+                        //}
+                        //currencyAll.exerciseAbility += ability.abilityCount;
                     }
 
                     List<TeacherHprecord> hprecords = new List<TeacherHprecord>();
@@ -966,25 +959,29 @@ namespace TEAMModelOS.SDK
                         currency.uploadTotal += ability.stds.FindAll(x => x.task.IsNotEmpty()).Select(y => y.task).Count();
                         currency.teacherAilities.Add(teacherAbility);
                     }
-                    currencyAll.subCount += 1;
-                    currencyAll.uploadTotal += ability.stds.FindAll(x => x.task.IsNotEmpty()).Select(y => y.task).Count();
-                    currencyAll.teacherAilities.Add(teacherAbility);
+                   // currencyAll.subCount += 1;
+                   // currencyAll.uploadTotal += ability.stds.FindAll(x => x.task.IsNotEmpty()).Select(y => y.task).Count();
+                  //  currencyAll.teacherAilities.Add(teacherAbility);
                 }
             });
             train.currency = currency;
-            train.currencyAll = currencyAll;
+           // train.currencyAll = currencyAll;
             train.currency.videoTime = train.currency.teacherAilities.Select(x => x.videoTime).Sum();
-            train.currencyAll.videoTime = train.currencyAll.teacherAilities.Select(x => x.videoTime).Sum();
+          //  train.currencyAll.videoTime = train.currencyAll.teacherAilities.Select(x => x.videoTime).Sum();
             //如果总分钟数超过20学时,则直接复制20学时。
             var videoTime = setting.lessonMinutes != 0 ? (int)(train.currency.videoTime / setting.lessonMinutes) : 0;
             train.onlineTime = videoTime > setting.onlineTime ? setting.onlineTime : videoTime;
             var bhg = train.currency.teacherAilities.FindAll(x => x.xzscore > 0);
             //由于有教师在省平台勾选太多能力点,暂时获得xzscore 数量大于三个的,也能获得学时
-            if (bhg.IsNotEmpty() && (bhg.Count == train.currency.subCount|| bhg.Count>=3))
+            int limitAbility = 3;
+            if (setting.limitAbility != -1) {
+                limitAbility=setting.limitAbility;
+            }
+            if (bhg.IsNotEmpty() && (bhg.Count == train.currency.subCount|| bhg.Count>= limitAbility))
             {
                 ///要全部合格才能获得学时。
                 train.currency.submitTime = setting.submitTime;
-                train.currencyAll.submitTime = setting.submitTime;
+               // train.currencyAll.submitTime = setting.submitTime;
             }
             return train;
         }

+ 5 - 0
TEAMModelOS/ClientApp/src/api/ability.js

@@ -156,6 +156,11 @@ export default {
 		return post('/research/statistics-area',data)
 		// return post('/research/get-area-statistics',data)
 	},
+	//获取区级统计数据(简易版数据)
+	getAreaDataSimple(data){
+		return post('/research/statistics-area-simple',data)
+		// return post('/research/get-area-statistics',data)
+	},
 	//获取校级统计数据
 	getSchoolData(data){
 		return post('/research/get-school-appraise',data)

+ 21 - 44
TEAMModelOS/ClientApp/src/assets/iconfont/demo_index.html

@@ -55,21 +55,15 @@
           <ul class="icon_lists dib-box">
           
             <li class="dib">
-              <span class="icon iconfont">&#xe783;</span>
-                <div class="name">成绩统计</div>
-                <div class="code-name">&amp;#xe783;</div>
-              </li>
-          
-            <li class="dib">
-              <span class="icon iconfont">&#xe67c;</span>
-                <div class="name">成绩</div>
-                <div class="code-name">&amp;#xe67c;</div>
+              <span class="icon iconfont">&#xe6b6;</span>
+                <div class="name">意见反馈、记笔记</div>
+                <div class="code-name">&amp;#xe6b6;</div>
               </li>
           
             <li class="dib">
-              <span class="icon iconfont">&#xe67d;</span>
-                <div class="name">成绩单_线性</div>
-                <div class="code-name">&amp;#xe67d;</div>
+              <span class="icon iconfont">&#xe783;</span>
+                <div class="name">成绩统计</div>
+                <div class="code-name">&amp;#xe783;</div>
               </li>
           
             <li class="dib">
@@ -1230,9 +1224,9 @@
 <pre><code class="language-css"
 >@font-face {
   font-family: 'iconfont';
-  src: url('iconfont.woff2?t=1650785772245') format('woff2'),
-       url('iconfont.woff?t=1650785772245') format('woff'),
-       url('iconfont.ttf?t=1650785772245') format('truetype');
+  src: url('iconfont.woff2?t=1650883304079') format('woff2'),
+       url('iconfont.woff?t=1650883304079') format('woff'),
+       url('iconfont.ttf?t=1650883304079') format('truetype');
 }
 </code></pre>
           <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -1259,29 +1253,20 @@
         <ul class="icon_lists dib-box">
           
           <li class="dib">
-            <span class="icon iconfont icon-chengjitongji"></span>
-            <div class="name">
-              成绩统计
-            </div>
-            <div class="code-name">.icon-chengjitongji
-            </div>
-          </li>
-          
-          <li class="dib">
-            <span class="icon iconfont icon-chengji"></span>
+            <span class="icon iconfont icon-myNote"></span>
             <div class="name">
-              成绩
+              意见反馈、记笔记
             </div>
-            <div class="code-name">.icon-chengji
+            <div class="code-name">.icon-myNote
             </div>
           </li>
           
           <li class="dib">
-            <span class="icon iconfont icon-chengjidan"></span>
+            <span class="icon iconfont icon-chengjitongji"></span>
             <div class="name">
-              成绩单_线性
+              成绩统计
             </div>
-            <div class="code-name">.icon-chengjidan
+            <div class="code-name">.icon-chengjitongji
             </div>
           </li>
           
@@ -3024,26 +3009,18 @@
           
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#icon-chengjitongji"></use>
-                </svg>
-                <div class="name">成绩统计</div>
-                <div class="code-name">#icon-chengjitongji</div>
-            </li>
-          
-            <li class="dib">
-                <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#icon-chengji"></use>
+                  <use xlink:href="#icon-myNote"></use>
                 </svg>
-                <div class="name">成绩</div>
-                <div class="code-name">#icon-chengji</div>
+                <div class="name">意见反馈、记笔记</div>
+                <div class="code-name">#icon-myNote</div>
             </li>
           
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
-                  <use xlink:href="#icon-chengjidan"></use>
+                  <use xlink:href="#icon-chengjitongji"></use>
                 </svg>
-                <div class="name">成绩单_线性</div>
-                <div class="code-name">#icon-chengjidan</div>
+                <div class="name">成绩统计</div>
+                <div class="code-name">#icon-chengjitongji</div>
             </li>
           
             <li class="dib">

+ 7 - 11
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.css

@@ -1,8 +1,8 @@
 @font-face {
   font-family: "iconfont"; /* Project id 2000444 */
-  src: url('iconfont.woff2?t=1650785772245') format('woff2'),
-       url('iconfont.woff?t=1650785772245') format('woff'),
-       url('iconfont.ttf?t=1650785772245') format('truetype');
+  src: url('iconfont.woff2?t=1650883304079') format('woff2'),
+       url('iconfont.woff?t=1650883304079') format('woff'),
+       url('iconfont.ttf?t=1650883304079') format('truetype');
 }
 
 .iconfont {
@@ -13,16 +13,12 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
-.icon-chengjitongji:before {
-  content: "\e783";
-}
-
-.icon-chengji:before {
-  content: "\e67c";
+.icon-myNote:before {
+  content: "\e6b6";
 }
 
-.icon-chengjidan:before {
-  content: "\e67d";
+.icon-chengjitongji:before {
+  content: "\e783";
 }
 
 .icon-my-note:before {

File diff suppressed because it is too large
+ 1 - 1
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.js


+ 7 - 14
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.json

@@ -5,6 +5,13 @@
   "css_prefix_text": "icon-",
   "description": "",
   "glyphs": [
+    {
+      "icon_id": "24591518",
+      "name": "意见反馈、记笔记",
+      "font_class": "myNote",
+      "unicode": "e6b6",
+      "unicode_decimal": 59062
+    },
     {
       "icon_id": "15562246",
       "name": "成绩统计",
@@ -12,20 +19,6 @@
       "unicode": "e783",
       "unicode_decimal": 59267
     },
-    {
-      "icon_id": "16394885",
-      "name": "成绩",
-      "font_class": "chengji",
-      "unicode": "e67c",
-      "unicode_decimal": 59004
-    },
-    {
-      "icon_id": "22895341",
-      "name": "成绩单_线性",
-      "font_class": "chengjidan",
-      "unicode": "e67d",
-      "unicode_decimal": 59005
-    },
     {
       "icon_id": "6536038",
       "name": "物品-书笔",

BIN
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.ttf


BIN
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff


BIN
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff2


+ 13 - 0
TEAMModelOS/ClientApp/src/common/AbilityUpload.vue

@@ -76,6 +76,10 @@
 				type: Boolean,
 				default: false
 			},
+			classMemoir:{
+				type: Boolean,
+				default: false
+			},
 		},
 		data() {
 			return {
@@ -94,6 +98,7 @@
 
 			onRemoveFile(index) {
 				this.fileArr.splice(index, 1)
+				this.$emit('removeFileFinish', this.fileArr)
 			},
 
 			/* 转换size */
@@ -107,6 +112,11 @@
 				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
@@ -126,6 +136,9 @@
 					return false
 				}
 
+				if(this.classMemoir){
+					this.$emit('selectFinish',file);
+				}
 
 				if (this.mode === 'import') {
 					let excelResult = []

+ 1 - 1
TEAMModelOS/ClientApp/src/common/BaseSelectArea.vue

@@ -126,7 +126,7 @@
 				// })
 				// 更新当前school_code
 				this.$store.dispatch('user/setSchoolCode', schoolCode)
-				// 更新当前school_profile以及access
+				// 更新当前school_profile以及access 2022.04.25关闭 怀疑重复操作
 				this.$store.dispatch('user/checkSchoolProfile').then(res => {
 					if (res) {
 						this.$User.freshLogin()

+ 1 - 1
TEAMModelOS/ClientApp/src/common/BaseSelectSchool.vue

@@ -186,7 +186,7 @@
 					setTimeout(() => {
 						this.$EventBus.$emit('onGlobalLoading', false)
 					}, 1500)
-					routerInfo = routerInfo || { name: 'homePage' }
+					routerInfo = routerInfo || { name: 'home' }
 					// console.error(routerInfo.name);
 					// console.error(this.$route);
 					this.$router.push(routerInfo)

+ 5 - 4
TEAMModelOS/ClientApp/src/components/homework/BaseHwForm.vue

@@ -382,7 +382,11 @@
 			},
 			/* 提交新增投票表单 */
 			async handleSubmit(name,progress) {
-				this.isBtnLoading = true
+				if(new Date(this.voteForm.endTime).getTime() < new Date().getTime()){
+					this.$Message.warning(this.$t('homework.draftTip'))
+					this.$emit('onAddFail')
+					return
+				}
 				let hasTargets = this.voteForm.classes.length
 				this.$refs[name].validate(async (valid) => {
 					if (valid && this.getSimpleText(this.voteForm.description) && hasTargets) {
@@ -436,13 +440,10 @@
 								'vote.form.editSuc') : this.$t(
 								'vote.form.addSuc'))
 							this.$emit('onAddSuccess')
-							this.isBtnLoading = false
 						}).catch(error => {
-							this.isBtnLoading = false
 							console.log(error)
 						})
 					} else {
-						this.isBtnLoading = false
 						this.$emit('onAddFail')
 						if (!hasTargets) {
 							this.$Message.error(this.$t('homework.form.ruleClasses'))

+ 1 - 9
TEAMModelOS/ClientApp/src/components/homework/BaseHwTable.vue

@@ -15,11 +15,6 @@
 					</p>
 					<p v-if="!currentFileList.length" style="margin: 20px 10px;">{{ $t('homework.noSubmitFile') }}</p>
 					<BasePreviewFile :previewFile="curPreviewFile" scope="private" v-if="currentFileList.length"></BasePreviewFile>
-					<!-- <div class="attachments-list">
-						<div v-for="(item,index) in currentFileList" @click="onFileClick(item,index)" :class="['attachments-item',activeFileIndex === index ? 'attachments-item-active' : '']" >
-							{{ item.name }}
-						</div>
-					</div> -->
 					<div class="preview-bar" v-if="currentFileList.length">
 						<Icon type="ios-arrow-back" @click="changeFile('pre')" v-if="currentFileList.length > 1"/>
 						<span style="max-width: 80%;word-break: break-all;">{{ curPreviewFile?curPreviewFile.name : '' }}</span>
@@ -51,9 +46,6 @@
 									@on-create="onCreateComment">
 									<Option v-for="(item,index) in usedCommentList" :value="item" :key="index" :label="item">
 										<span>{{ item }}</span>
-										<!-- <span style="float:right;color:#ccc" @click.stop="onDeleteComment(index)">
-											<Icon type="md-trash" size="20" color="#7b7b7b" />
-										</span> -->
 									</Option>
 								</Select>
 							</div>
@@ -224,7 +216,7 @@
 									cursor: 'pointer',
 									fontWeight: 'bold'
 								},
-							}, params.row.star === -1 ? '—' : params.row.star)
+							}, params.row.star === -1 ? '—' : params.row.star.toFixed(1))
 						}
 					},
 					{

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

@@ -43,7 +43,7 @@
                     <div style="float: right;">
                         <!-- 我的笔记 -->
                         <span class="e-note-tag" @click="showNote = true" v-if="recordInfo.myNote.length">
-                            <Icon custom="iconfont icon-wp-sb" />
+                            <Icon custom="iconfont icon-myNote" size="17" />
                             {{ $t('studentWeb.courseContent.mynotes') }}
                         </span>
                         <!-- 电子笔记 -->

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

@@ -175,7 +175,7 @@
                                             </li>
                                             <span class="upload">
                                                 <span @click.capture.stop="uploadNote(item, true)" v-show="item.myNote.length">
-                                                    <Icon custom="iconfont icon-wp-sb" size="20" :title="$t('studentWeb.courseContent.mynotes')" />
+                                                    <Icon custom="iconfont icon-myNote" size="20" :title="$t('studentWeb.courseContent.mynotes')" />
                                                 </span>
                                                 <span style="margin-left: 15px;" @click.capture.stop="uploadNote(item)">
                                                     <Icon custom="iconfont icon-activityT" size="20" :title="$t('studentWeb.courseContent.notes')" />

+ 4 - 0
TEAMModelOS/ClientApp/src/locale/lang/en-US/homework.js

@@ -1,4 +1,8 @@
 export default {
+	saveAsDraft:'保存為草稿',
+	publish:'發佈活動',
+	save:'保存',
+	draftTip:'活動結束時間已過,請修改後重試!',
 	notPreview: 'The file is not available for preview, please save the activity and try again!',
 	chooseContent: "Select from the IES's resources",
 	notFoundData: 'No response data was found.',

+ 4 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/homework.js

@@ -1,4 +1,8 @@
 export default {
+	saveAsDraft:'保存为草稿',
+	publish:'发布活动',
+	save:'保存',
+	draftTip:'活动结束时间已过,请修改后重试!',
 	notPreview:'该文件暂不支持预览,请保存活动后重试!',
 	chooseContent:'从站内资源挑选',
 	notFoundData:'未查询到作答数据',

+ 4 - 0
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/homework.js

@@ -1,4 +1,8 @@
 export default {
+	saveAsDraft:'保存為草稿',
+	publish:'發佈活動',
+	save:'保存',
+	draftTip:'活動結束時間已過,請修改後重試!',
 	notPreview:'該文件暫不支持預覽,請保存活動後重試!',
 	chooseContent:'從站內資源挑選',
 	notFoundData:'未查詢到作答數據',

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

@@ -1,9 +1,6 @@
 import ServerSideLogin from '@/view/serverside/login.vue'
 import Home from '@/view/Home.vue'
 import FrontEndMain from '@/components/public/frontEndMain/Index.vue'
-// import HTTP404 from '@/view/404'
-// import { resolve } from 'url'
-
 let identity = sessionStorage.getItem('identity')
 let auth_token = localStorage.getItem('auth_token')
 export const routes = [{
@@ -128,7 +125,7 @@ export const routes = [{
 	name: 'home',
 	path: '/home',
 	// redirect: localStorage.getItem('srvAdr') == 'Global' ? '/home/myCourse' : '/home/homePage', //登录成功跳转页面(现在统一调整跳转首页,去首页判断国际站需要显示的UI)
-	redirect: '/home/homePage',
+	redirect: window.location.host === 'jinniu.teammodel.cn' ? '/home/jyHome' :  '/home/homePage',
 	component: resolve => require(['@/view/Home'], resolve),
 	meta: {
 		middleware: ['login', 'role:admin|teacher'], // 强制登录(会自动跳转到登录页),角色为管理员或教师

+ 7 - 1
TEAMModelOS/ClientApp/src/utils/js-fn.js

@@ -320,6 +320,11 @@ function getTeacherSubjects(ids) {
     }
 
 }
+//根据域名判断菜单是否显示
+function checkJinNiu() {
+    let host = window.location.host
+    return host == 'jinniu.teammodel.cn'
+}
 
 export default {
     groupBy,
@@ -339,5 +344,6 @@ export default {
     getYearByGrade,
     dateFormat,
     timeFormat,
-    getTeacherSubjects
+    getTeacherSubjects,
+    checkJinNiu
 }

+ 48 - 10
TEAMModelOS/ClientApp/src/view/areaMgmt/AreaData.vue

@@ -147,6 +147,7 @@ export default {
     },
     data() {
         return {
+            settingHours: {},
             onlineTime: 20,
             offlineTime: 10,
             videoTime: 5,
@@ -259,20 +260,56 @@ export default {
         }
     },
     methods: {
+        getLimitMinutes(item) {
+            return this.settingHours.limitMinutes === -1 ? (item.limitTime * this.settingHours.lessonMinutes) : this.settingHours.limitMinutes
+        },
         exportTeacherData() {
-            // 所有老师统计
-            this.teacherData.forEach(item => {
-                item.submitTime = item.currency.submitTime
-                let school = this.schoolList.find(school => school.schoolId == item.school)
-                if (school) item.schoolName = school.schoolName
+            console.log(this.teacherData)
+            // return
+            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 datas = this.teacherData.map(i => {
+                let teacherAilities = i.currency?.teacherAilities || []
+                return {
+                    schoolName: i.schoolName,
+                    name: i.name,
+                    totalTime: i.totalTime,
+                    online: i.onlineTime,
+                    unFinishArr: teacherAilities.filter(j => j.videoTime < this.getLimitMinutes(j)).map(k => k.no).join(','),
+                    finishArr: teacherAilities.filter(j => j.videoTime >= this.getLimitMinutes(j)).length,
+                    ability: i.submitTime,
+                    offline: i.offlineTime,
+                    video: i.classTime,
+                    abilityCount: teacherAilities.length,
+                    noAbilityCount: teacherAilities.filter(i => i.zpscore === -1).length,
+                    noAbilityNoArr: teacherAilities.filter(j => j.zpscore === -1).map(k => k.no).join(','),
+                    noHwCount: i.offlineCountNo,
+                    isSubmitVideo: i.teacherClasseCount ? '已提交' : '未提交'
+                }
             })
             const teacherParams = {
-                title: ['姓名', '学校', '线上研修学时', '校本研修学时', '认证材料学时', '课堂实录学时', '总学时'],
-                key: ['name', 'schoolName', 'onlineTime', 'offlineTime', 'submitTime', 'classTime', 'totalTime'],
-                data: this.teacherData,
+                title: header,
+                key: keys,
+                data: datas,
                 autoWidth: true,
-                filename: '老师数据统计'
+                filename: '研修情况统计表'
             }
+            // 所有老师统计
+            // this.teacherData.forEach(item => {
+            //     item.submitTime = item.currency.submitTime
+            //     let school = this.schoolList.find(school => school.schoolId == item.school)
+            //     if (school) item.schoolName = school.schoolName
+            // })
+            // const teacherParams = {
+            //     title: ['姓名', '学校', '线上研修学时', '校本研修学时', '认证材料学时', '课堂实录学时', '总学时'],
+            //     key: ['name', 'schoolName', 'onlineTime', 'offlineTime', 'submitTime', 'classTime', 'totalTime'],
+            //     data: this.teacherData,
+            //     autoWidth: true,
+            //     filename: '老师数据统计'
+            // }
             excel.export_array_to_excel(teacherParams)
         },
         exportSchoolData() {
@@ -333,9 +370,10 @@ export default {
                 areaId: sessionStorage.getItem('areaId'),
                 standard: sessionStorage.getItem('standard')
             }
-            this.$api.ability.getAreaData(params).then(
+            this.$api.ability.getAreaDataSimple(params).then(
                 res => {
                     if (!res.error) {
+                        this.settingHours = res.setting || {}
                         //设置的学时
                         this.onlineTime = res.setting.onlineTime || this.onlineTime
                         this.offlineTime = res.setting.offlineTime || this.offlineTime

+ 4 - 1
TEAMModelOS/ClientApp/src/view/areaSetting/AreaSetting.vue

@@ -47,7 +47,10 @@
 						<i-switch v-model="openCustom"></i-switch>
 					</div>
 					<div class="setting-item" v-if="openCustom">
-						<p>每个能力点需学习最少</p>
+						<p>每个老师至少修满</p>
+						<InputNumber :max="2000" :min="0" v-model="areaSetting.limitAbility"></InputNumber>
+						<span> 个能力点,</span>
+						<p>每个能力点需最少学习</p>
 						<InputNumber :max="2000" :min="1" v-model="areaSetting.limitMinutes"></InputNumber>
 						<span> 分钟</span>
 					</div>

+ 4 - 0
TEAMModelOS/ClientApp/src/view/assessment/Assessment.less

@@ -65,6 +65,10 @@
         //     vertical-align: top !important;
         // }
 		
+		/deep/ .ivu-table-row{
+			cursor: pointer;
+		}
+		
 		/deep/ th .ivu-table-cell{
 			width: 100%;
 		}

File diff suppressed because it is too large
+ 1240 - 1254
TEAMModelOS/ClientApp/src/view/assessment/Assessment.vue


+ 37 - 35
TEAMModelOS/ClientApp/src/view/homepage/HomePage.vue

@@ -5,11 +5,11 @@
             <div class="chart-box">
                 <div class="chart-box-column1">
                     <!-- TODO 我的课表 此部分暂时去掉-->
-                    <div class="cus-table-box" v-if="!checkHost()">
+                    <!-- <div class="cus-table-box">
                         <MinTable @tmwCus="getTmwCus"></MinTable>
-                    </div>
+                    </div> -->
                     <!-- 活动统计 -->
-                    <div class="ac-count-box" v-else style="width:100%;height:360px;margin-bottom:15px">
+                    <div class="ac-count-box" style="width:100%;height:360px;margin-bottom:15px">
                         <p class="chart-title">
                             {{$t('home.acCount')}}
                         </p>
@@ -17,7 +17,7 @@
                     </div>
 
                     <!-- 空间使用情况 -->
-                    <div class="recent-box" style="height:389px;position:relative" v-if="checkHost()">
+                    <div class="recent-box" style="height:389px;position:relative">
                         <p class="chart-title" v-show="spaceStatus == 1">{{$t('home.spaceInfo')}}</p>
                         <Alert show-icon v-if="spaceStatus == 2" type="warning" style="margin-right:10px">
                             <p style="font-size:12px">
@@ -35,9 +35,9 @@
                 </div>
                 <div class="chart-box-column2">
                     <!-- 数据统计 -->
-                    <div class="class-chart-box" v-if="checkHost()" style="padding:35px 30px">
-                        <!-- 课堂记录统计 -->
-                        <div class="cus-count-wrap">
+                    <div class="class-chart-box" style="padding:50px 30px">
+                        <!-- TODO 暂时隐藏 课堂记录统计 -->
+                        <!-- <div class="cus-count-wrap">
                             <div class="cus-count-item cus-count-item-bg3">
                                 <p class="tag-label">{{$t('home.cusTotal')}}</p>
                                 <countTo :startVal='0' :endVal='recordCount.total || 0' :duration='1500' class="count-num"></countTo>
@@ -55,9 +55,9 @@
                                 <p class="tag-label">{{$t('home.dayCount')}}</p>
                                 <countTo :startVal='0' :endVal='recordCount.today || 0' :duration='1500' class="count-num"></countTo>
                             </div>
-                        </div>
+                        </div> -->
                         <!-- 课程班级名单统计 -->
-                        <div class="cus-count-wrap" style="margin-top:25px">
+                        <div class="cus-count-wrap" style="margin-top:0px">
                             <div class="cus-count-item cus-count-item-bg1">
                                 <p class="tag-label">{{$t('home.count1')}}</p>
                                 <countTo :startVal='0' :endVal='countData.schoolCourseCount  || 0' :duration='1500' class="count-num"></countTo>
@@ -105,10 +105,30 @@
                             <ScoreCount :scoreCount="scoreCount"></ScoreCount>
                         </div>
                     </div>
-                    <!-- 研修数据 && 活动统计-->
-                    <div style="display: flex;" v-show="$store.state.config.srvAdr == 'China'">
+                    <!-- 近期课堂记录 -->
+                    <div class="recent-box" style="padding-right:5px;height:462px">
+                        <p class="text-title">{{$t('home.recentRecord')}}</p>
+                        <div class="recent-list-wrap">
+                            <vuescroll>
+                                <EmptyData :textContent="$t('home.noRecord')" v-if="!recentRcdList.length"></EmptyData>
+                                <template v-else>
+                                    <div class="rcd-item" v-for="item in recentRcdList" :key="item.id" @click="toClassRecord(item)">
+                                        <RcdPoster class="rcd-poster" :poster="item.poster"></RcdPoster>
+                                        <div>
+                                            <p>{{item.name}}</p>
+                                            <p class="rcd-time">
+                                                <Icon type="md-time" color="#70B1E7" />
+                                                {{$jsFn.timeFormat(item.startTime)}}
+                                            </p>
+                                        </div>
+                                    </div>
+                                </template>
+                            </vuescroll>
+                        </div>
+                    </div>
+                    <!-- IES首页去掉研修数据统计 研修数据 && 活动统计-->
+                    <div style="display: none;">
                         <!-- 研修数据 -->
-                        <!-- <div class="train-chart-box" :style="{width:!checkHost() ? '100%' : 'calc(70% - 15px)',marginRight:!checkHost() ? '0px' : '15px'}" v-show="$store.state.config.srvAdr == 'China'"> -->
                         <div class="train-chart-box" style="width:100%;margin-right:0px" v-show="$store.state.config.srvAdr == 'China'">
                             <p class="chart-title">
                                 {{$t('system.menu.trainCount')}}
@@ -152,7 +172,7 @@
                                 </div>
                             </div>
                             <!-- 活动数量统计 -->
-                            <div class="train-count-wrap" v-show="!checkHost()">
+                            <div class="train-count-wrap">
                                 <div class="train-count-item" v-for="(item,index) in trainCountList" :key="index">
                                     <img :src="item.img" class="train-count-img" alt="">
                                     <div class="train-count-info">
@@ -162,20 +182,12 @@
                                 </div>
                             </div>
                         </div>
-                        <!-- 活动统计 -->
-                        <!-- <div class="ac-count-box" v-if="checkHost()">
-                            <p class="chart-title">
-                                {{$t('home.acCount')}}
-                            </p>
-                            <AcCountPie style="margin-top:-20px;" :count="acCount"></AcCountPie>
-                        </div> -->
                     </div>
-
                 </div>
             </div>
             <div class="text-box">
                 <!-- 第三方平台连接 -->
-                <div class="recent-box" v-if="checkHost()">
+                <div class="recent-box" style="height:360px">
                     <p class="text-title">{{$t('platform.ptText')}}</p>
                     <div class="platform-list-wrap">
                         <vuescroll>
@@ -188,7 +200,7 @@
                     </div>
                 </div>
                 <!-- 学校公告 -->
-                <div class="notice-box">
+                <div class="notice-box" style="height:389px">
                     <p class="text-title">{{$t('home.scNotice')}}</p>
                     <div class="notice-list-wrap">
                         <vuescroll>
@@ -233,7 +245,7 @@
                     </div>
                 </div> -->
                 <!-- 最近课堂记录-->
-                <div class="recent-box" style="padding-right:5px">
+                <div class="recent-box" style="padding-right:5px;display:none">
                     <p class="text-title">{{$t('home.recentRecord')}}</p>
                     <div class="recent-list-wrap">
                         <vuescroll>
@@ -719,12 +731,7 @@ export default {
             ).finally(() => {
                 this.isLoading = false
             })
-        },
-        //判断金牛区
-        checkHost() {
-            let host = window.location.host
-            return host !== 'jinniu.teammodel.cn'
-        },
+        }
     },
     created() {
         // privateCourseCount  私人课程数量
@@ -748,9 +755,6 @@ export default {
                 this.getStudyTime()
             }
         }
-        if (this.checkHost()) {
-            this.getTeacherRecordData()
-        }
     },
     mounted() {
     },
@@ -769,8 +773,6 @@ export default {
             }
             return 0
         }
-    },
-    watch: {
     }
 }
 </script>

+ 10 - 25
TEAMModelOS/ClientApp/src/view/homework/ManageHomeWork.vue

@@ -65,15 +65,15 @@
 								</span>
 								<span class="hw-box-header-tools-tool" v-if="!currentVote.id">
 									<Button type="info" style="padding: 0 20px;" size="small" @click="onSaveDraftAc"
-										:loading="isBtnLoading">保存为草稿</Button>
+										:loading="isBtnLoading">{{ $t('homework.saveAsDraft') }}</Button>
 								</span>
 								<span class="hw-box-header-tools-tool">
 									<Button type="success" style="padding: 0 20px;" size="small" @click="currentVote.progress !== 'draft' ? doSave() : onSaveDraftAc()"
-										:loading="isBtnLoading">{{ currentVote.id ? '储存' : '发布活动' }}</Button>
+										:loading="isBtnLoading">{{ currentVote.id ? $t('homework.save') : $t('homework.publish') }}</Button>
 								</span>
 								<span class="hw-box-header-tools-tool" v-if="currentVote.progress === 'draft' && currentVote.id">
-									<Button type="success" style="padding: 0 20px;" size="small" @click="doPublishDraft"
-										:loading="isBtnLoading">{{ '发布此活动' }}</Button>
+									<Button type="success" style="padding: 0 20px;" size="small" @click="doSave"
+										:loading="isBtnLoading">{{ $t('homework.publish') }}</Button>
 								</span>
 							</div>
 						</div>
@@ -99,7 +99,7 @@
 									</span>
 									<span style="margin-right: 10px;cursor: pointer;" v-if="voteList.length && currentVote.progress !== 'finish'"  @click="onEditAc">
 										<Icon type="md-create" size="18" />
-										编辑活动
+										{{ $t('homework.edit') }}
 									</span>
 									<span class="hw-box-header-tools-tool" @click="isShowDetail = true">
 										<Icon type="md-information-circle" size="18" />
@@ -407,8 +407,8 @@
 							submitTime:i.submit ? this.$tools.formatTime(i.submitTime,'yyyy-MM-dd hh:mm') : '-',
 							score:i.score === -1 ? '-' : i.score,
 							comment:i.replies.length ? i.replies[0] : '—',
-							star:i.star === -1 ? '-' : i.star,
-							commentScore:i.star,
+							star:i.star === -1 ? '-' : i.star.toFixed(1),
+							commentScore:i.star === -1 ? '-' : i.star.toFixed(1),
 						}
 					}),
 					autoWidth: true,
@@ -421,11 +421,6 @@
 				this.isBtnLoading = true
 				this.$refs.voteForm.handleSubmit('voteForm')
 			},
-			doPublishDraft(){
-				this.isBtnLoading = true
-				// this.$refs.voteForm.voteForm.startTime = new Date().getTime()
-				this.$refs.voteForm.handleSubmit('voteForm')
-			},
 			/* 保存为草稿 */
 			onSaveDraftAc(){
 				this.isBtnLoading = true
@@ -478,6 +473,7 @@
 					this.$refs.baseQnRef && this.$refs.baseQnRef.scrollIntoView()
 				})
 			},
+			/* 进入初始化 */
 			async handleTabClick(){
 				if(this.$route.params.ac){
 					let item = this.$route.params.ac
@@ -647,11 +643,9 @@
 							console.log('xxxxx')
 							r({})
 						} else {
-							let curItemRecord = await this.getBlobJsonFile(voteItem.scope, voteItem
-								.recordUrl)
+							let curItemRecord = await this.getBlobJsonFile(voteItem.scope, voteItem.recordUrl)
 							r(curItemRecord)
 						}
-
 					} catch (e) {
 						j(e)
 					}
@@ -775,7 +769,7 @@
 					)
 				})
 			},
-			
+			/* 评分结束 */
 			onScoreFinish(){
 				this.getVoteStudents(this.currentVote)
 			},
@@ -783,9 +777,6 @@
 			async getVoteStudents(voteItem) {
 				this.isLoading = true
 				try {
-					// let records = await this.getVoteRecord(voteItem)
-					//  先查找 作业发布对象关联的学生清单 然后再去判断学生的作答情况
-					// console.log('当前作业的作答数据======', records)
 					this.$api.learnActivity.InteractHomework({
 						"opt": "AnswerRecordAll",
 						"id": voteItem.id,
@@ -827,7 +818,6 @@
 			},
 			/* 获取当前教师的所有个人课程以及学校分配的课程 */
 			getMyCourse() {
-				// if(this.$store.state.user.userCourses.length) return
 				this.$api.courseMgmt.findAllByTeahcer({
 					code: this.$store.state.userInfo.TEAMModelId,
 					scope: 'private',
@@ -946,9 +936,6 @@
 		overflow-y: scroll;
 		overflow-x: hidden;
 		text-align: center;
-		/*display: flex;
-	    justify-content: center;
-	    align-items: center;*/
 		padding: 50px;
 		padding-top: 8%;
 	
@@ -968,7 +955,6 @@
 		border-color: #dadada !important;
 		background: none;
 	}
-
 	.hw-modal {
 		.ivu-modal-content {
 			background: #454545;
@@ -988,7 +974,6 @@
 			padding: 15px;
 		}
 	}
-
 	.hw-modal .ivu-modal-header p,
 	.hw-modal .ivu-modal-header-inner {
 		color: #fff;

+ 515 - 399
TEAMModelOS/ClientApp/src/view/jyzx/classMemoir.vue

@@ -1,422 +1,537 @@
 <template>
-    <div class="class-memoir">
-        <Tabs value="name1" v-show="showStatus === false">
-            <TabPane :label="$t('jyzx.classRecord.myRecod')" name="name1">
-                <Card :bordered="false">
-                    <div>
-                        <div class="uploaddiv"><AbilityUpload ref="refFile" :auth="curSas" :acceptTypes="['mp4']" :scope="'school'" mode='video' :prefix="uploadId" @uploadFinish="uploadFinish" singleUpload></AbilityUpload></div>
-                        <div class="hint" v-show="calssListinfo.length !==0">{{tipText}}</div>
-                        <Button type="primary" size="large" :long="true" @click="confirm" v-if="!calssListinfo.length" :loading="btnstate">{{ $t('jyzx.classRecord.loadOK') }}</Button>
-                        <Button type="info" size="large" :long="true" disabled v-else-if="calssListinfo[0].score ==1 ||calssListinfo[0].score ==2">{{ $t('jyzx.classRecord.loadOK') }}</Button>
-                        <Button type="info" size="large" :long="true" @click="confirm" v-else="calssListinfo[0].score !==1 ||calssListinfo[0].score !==2" :loading="btnstate">重新上传</Button>
-                    </div>
-                    <div v-if="calssListinfo.length" style="height: calc(100vh - 255px); overflow: auto; margin-top: 20px">
-                        <Table :columns="classCol" :data="calssListinfo" height="680">
-                            <template slot-scope="{ row, index }" slot="action">
-                                <Button size="small" @click="checkevaluate(row,true)" style="margin-right:2%">{{ $t('jyzx.common.seeEvaluate') }}</Button>
-                                <Button size="small" @click="delClass(row)" v-if="calssListinfo[0].score !==1 && calssListinfo[0].score !==2" class="delete-btns">{{ $t('jyzx.common.delete') }}</Button>
-                            </template>
-                        </Table>
-                    </div>
-                </Card>
-            </TabPane>
-            <TabPane :label="$t('jyzx.classRecord.groupRecord')" name="name2">
-                <div v-if="teamVideo.length" style="height: calc(100vh - 255px); overflow: auto">
-                    <Table :columns="teamclassCol" :data="teamVideo" height="680">
-                        <template slot-scope="{ row, index }" slot="actions">
-                            <Button size="small" @click="checkevaluate(row)">{{ $t('jyzx.classRecord.see') }}</Button>
-                        </template>
-                    </Table>
-                </div>
-            </TabPane>
-        </Tabs>
-        <div v-show="showStatus === true">
-            <p><Icon type="md-arrow-back" size="26" @click="showStatus=false" class="backward"/><span class="teachnames">罗老师</span><span class="coursename">【{{courseName}}】</span></p>
-            <Table border :columns="columns1aa" :data="data1"></Table>
-            <!--<div class="appraise">
-                <div>
-                    <span>评价结果:</span>
-                    <RadioGroup v-model="border">
-                        <Radio label="不合格" border></Radio>
-                        <Radio label="合格" border></Radio>
-                        <Radio label="优秀" border></Radio>
-                    </RadioGroup>
-                </div>
-                <div>
-                    <span>评价内容:</span>
-                    <div id="div1"></div>
-                </div>
-            </div>-->
+  <div class="class-memoir">
+    <Tabs value="name1" v-show="showStatus === false">
+      <TabPane :label="$t('jyzx.classRecord.myRecod')" name="name1">
+        <VideoReview v-if="showReview" :reviewData="propsData" @reSubmit="reSubmit"></VideoReview>
+        <Card :bordered="false" v-else>
+          <div>
+            <div class="uploaddiv" v-show="!isPreview">
+              <AbilityUpload ref="refFile" :auth="curSas" :acceptTypes="['mp4']" :scope="'school'" mode='video' :prefix="uploadId" @removeFileFinish="removeFileFinish" @selectFinish="selectFinish" @uploadFinish="uploadFinish" classMemoir singleUpload></AbilityUpload>
+            </div>
+            <div class="preview-wrap" v-if="isPreview">
+              <video controls="controls" id="previewVideo" width="100%" height="500"></video>
+            </div>
+            <div class="hint" v-show="calssListinfo.length !==0">* {{tipText}}</div>
+            <p color="orange" class="video-tips">* 建议检查您选择的视频文件满足
+              <span>H.264编码、MP4格式、播放未出现花屏、绿屏、曝光过高、模糊或者无声音等异常状况</span> 再确认上传!
+            </p>
+            <div style="display:flex;justify-content: space-between" v-if="hasFile">
+              <Button size="large" @click="doCancel" v-if="isPreview" style="margin-bottom:10px;width:49%;margin-right: 10px">视频异常,重新选择</Button>
+              <Button type="info" size="large" @click="confirm" style="margin-bottom:10px;flex:1" :loading="btnstate">确认上传</Button>
+            </div>
+            <!-- <div v-if="!isPreview && hasFile">
+              <Button type="primary" size="large" :long="true" @click="confirm" v-if="!calssListinfo.length" :loading="btnstate">{{ $t('jyzx.classRecord.loadOK') }}</Button>
+              <Button type="info" size="large" :long="true" disabled v-else-if="calssListinfo[0].score ==1 ||calssListinfo[0].score ==2">{{ $t('jyzx.classRecord.loadOK') }}</Button>
+              <Button type="info" size="large" :long="true" @click="confirm" v-else="(calssListinfo[0].score !==1 ||calssListinfo[0].score !==2) && !isPreview" :loading="btnstate">确认上传</Button>
+            </div> -->
+
+          </div>
+          <div v-if="calssListinfo.length" style="height: calc(100vh - 255px); overflow: auto; margin-top: 20px">
+            <Table :columns="classCol" :data="calssListinfo" height="680">
+              <template slot-scope="{ row, index }" slot="action">
+                <Button size="small" @click="checkevaluate(row,true)" style="margin-right:2%">{{ $t('jyzx.common.seeEvaluate') }}</Button>
+                <Button size="small" @click="delClass(row)" v-if="calssListinfo[0].score !==1 && calssListinfo[0].score !==2" class="delete-btns">{{ $t('jyzx.common.delete') }}</Button>
+              </template>
+            </Table>
+          </div>
+        </Card>
+      </TabPane>
+      <TabPane :label="$t('jyzx.classRecord.groupRecord')" name="name2">
+        <div v-if="teamVideo.length" style="height: calc(100vh - 255px); overflow: auto">
+          <Table :columns="teamclassCol" :data="teamVideo" height="680">
+            <template slot-scope="{ row, index }" slot="actions">
+              <Button size="small" @click="checkevaluate(row)">{{ $t('jyzx.classRecord.see') }}</Button>
+            </template>
+          </Table>
         </div>
+      </TabPane>
+    </Tabs>
+    <div v-show="showStatus === true">
+      <p>
+        <Icon type="md-arrow-back" size="26" @click="showStatus=false" class="backward" /><span class="teachnames">罗老师</span><span class="coursename">【{{courseName}}】</span>
+      </p>
+      <Table border :columns="columns1aa" :data="data1"></Table>
     </div>
+  </div>
 </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 {
-    data() {
-        return {
-            classCol: [
-                {
-                    title: this.$t("jyzx.classRecord.videoName"),
-                    key: "name",
-                    align: "center",
-                },
-                {
-                    title: this.$t("jyzx.common.loadTime"),
-                    key: "time",
-                    align: "center",
-                },
-                {
-                    title: this.$t("jyzx.classRecord.fileSize"),
-                    key: "size",
-                    align: "center",
-                },
-                {
-                    title: this.$t("jyzx.common.action"),
-                    slot: "action",
-                    align: "center",
-                },
-            ],
-            teamclassCol: [
-                { title: this.$t("jyzx.classRecord.videoName"), key: 'vname', align: 'center'},
-                { title: this.$t("jyzx.classRecord.loadAuth"), key: 'tmdname', align: 'center' },
-                { title: this.$t("jyzx.classRecord.size"), key: 'vsize', align: 'center' },
-                { title: this.$t("jyzx.common.loadTime"), key: 'vtime', align: 'center'},
-                { title: this.$t("jyzx.common.action"), slot: 'actions', align: 'center' },
-            ],
-            classList: [
-                {
-                    name: "XXXXXX的视频",
-                    time: "2021-07-19",
-                    size: 27,
-                },
-                {
-                    name: "第一次上课视频",
-                    time: "2021-07-20",
-                    size: 89,
-                },
-                {
-                    name: "XXXXXX的视频",
-                    time: "2021-07-19",
-                    size: 27,
-                },
-            ],
-            calssListinfo: [],
-            curSas: {
-                sas: '',
-                url: '',
-                name: '',
-            },//验证证书
-            uploadId: '',//上传人ID
-            uploadData: {
-                blob: '',
-                createTime: '',
-                extension: '',
-                name: '',
-                size: '',
-                type: '',
-                url: '',
-                score:'',
-            },
-            showStatus: false,
-            teachName:'',
-            courseName: '',
-            border: '',  //评价
-            teamVideo:[],//同组课堂实录
-            //虚拟数据
-            columns1aa: [
-                {
-                    title: this.$t("jyzx.classRecord.evaName"),
-                    key: 'name'
-                },
-                {
-                    title: this.$t("jyzx.classRecord.evaType"),
-                    key: 'type'
-                },
-                {
-                    title: this.$t("jyzx.classRecord.evaResult"),
-                    key: 'result'
-                },
-                {
-                    title: this.$t("jyzx.classRecord.evaContent"),
-                    key: 'content'
-                },
-                 {
-                    title: this.$t("jyzx.classRecord.evaTime"),
-                    key: 'time'
-                }
-            ],
-            data1: [
-                // {
-                //     name: 'John Brown',
-                //     age: 18,
-                //     address: 'New York No. 1 Lake Park',
-                //     date: '2016-10-03'
-                // },
-                {
-                  name:'李老师',
-                  type:'互评',
-                  result:'合格',
-                  content:'老师讲的很不错',
-                  time:'2021-07-22 15:30:22',
-                  cellClassName:{result:'qualified span'},
-                },
-                  {
-                  name:'罗老师',
-                  type:'自评',
-                  result:'合格',
-                  content:'还需要努力',
-                  time:'2021-07-24 18:32:21',
-                  cellClassName:{result:'qualified span'},
-                },
-                  {
-                  name:'张老师',
-                  type:'校评',
-                  result:'优秀',
-                  content:'讲的很好',
-                  time:'2021-07-26 10:32:21',
-                  cellClassName:{result:'excellent span'},
-                },
-            ],
-            //提示
-            tipText: '如重新上传,原视频相关的评价同时也会清除哦~',
-            //数据添加或删除后的
-            operateData: [],
-            btnstate: false,
+  components: { VideoReview },
+  data() {
+    return {
+      classCol: [
+        {
+          title: this.$t("jyzx.classRecord.videoName"),
+          key: "name",
+          align: "center",
+        },
+        {
+          title: this.$t("jyzx.common.loadTime"),
+          key: "time",
+          align: "center",
+        },
+        {
+          title: this.$t("jyzx.classRecord.fileSize"),
+          key: "size",
+          align: "center",
+        },
+        {
+          title: this.$t("jyzx.common.action"),
+          slot: "action",
+          align: "center",
+        },
+      ],
+      teamclassCol: [
+        { title: this.$t("jyzx.classRecord.videoName"), key: 'vname', align: 'center' },
+        { title: this.$t("jyzx.classRecord.loadAuth"), key: 'tmdname', align: 'center' },
+        { title: this.$t("jyzx.classRecord.size"), key: 'vsize', align: 'center' },
+        { title: this.$t("jyzx.common.loadTime"), key: 'vtime', align: 'center' },
+        { title: this.$t("jyzx.common.action"), slot: 'actions', align: 'center' },
+      ],
+      classList: [
+        {
+          name: "XXXXXX的视频",
+          time: "2021-07-19",
+          size: 27,
+        },
+        {
+          name: "第一次上课视频",
+          time: "2021-07-20",
+          size: 89,
+        },
+        {
+          name: "XXXXXX的视频",
+          time: "2021-07-19",
+          size: 27,
+        },
+      ],
+      calssListinfo: [],
+      curSas: {
+        sas: '',
+        url: '',
+        name: '',
+      },//验证证书
+      uploadId: '',//上传人ID
+      uploadData: {
+        blob: '',
+        createTime: '',
+        extension: '',
+        name: '',
+        size: '',
+        type: '',
+        url: '',
+        score: '',
+      },
+      showStatus: false,
+      showReview: false,
+      teachName: '',
+      courseName: '',
+      border: '',  //评价
+      teamVideo: [],//同组课堂实录
+      //虚拟数据
+      columns1aa: [
+        {
+          title: this.$t("jyzx.classRecord.evaName"),
+          key: 'name'
+        },
+        {
+          title: this.$t("jyzx.classRecord.evaType"),
+          key: 'type'
+        },
+        {
+          title: this.$t("jyzx.classRecord.evaResult"),
+          key: 'result'
+        },
+        {
+          title: this.$t("jyzx.classRecord.evaContent"),
+          key: 'content'
+        },
+        {
+          title: this.$t("jyzx.classRecord.evaTime"),
+          key: 'time'
         }
+      ],
+      data1: [
+        // {
+        //     name: 'John Brown',
+        //     age: 18,
+        //     address: 'New York No. 1 Lake Park',
+        //     date: '2016-10-03'
+        // },
+        {
+          name: '李老师',
+          type: '互评',
+          result: '合格',
+          content: '老师讲的很不错',
+          time: '2021-07-22 15:30:22',
+          cellClassName: { result: 'qualified span' },
+        },
+        {
+          name: '罗老师',
+          type: '自评',
+          result: '合格',
+          content: '还需要努力',
+          time: '2021-07-24 18:32:21',
+          cellClassName: { result: 'qualified span' },
+        },
+        {
+          name: '张老师',
+          type: '校评',
+          result: '优秀',
+          content: '讲的很好',
+          time: '2021-07-26 10:32:21',
+          cellClassName: { result: 'excellent span' },
+        },
+      ],
+      //提示
+      tipText: '如重新上传,原视频相关的评价同时也会清除哦~',
+      //数据添加或删除后的
+      operateData: [],
+      btnstate: false,
+      readyPreview: false,
+      isPreview: false,
+      checkFile: null,
+      recordFile: true,
+      hasFile: false,
+      propsData: null
+    }
+  },
+  created() {
+    this.uploadVerify()
+    this.getvideo('default');
+    this.getteamvideo();
+  },
+  mounted() {
+    let stemEditor = new E("#div1")
+    stemEditor.config.onchange = (html) => {
+      this.stemContent = html
+    }
+    stemEditor.config.uploadImgShowBase64 = true
+    stemEditor.config.zIndex = 500
+    stemEditor.create()
+  },
+  methods: {
+    reSubmit() {
+      this.propsData = null
+      this.showReview = false
+      this.isPreview = false
+      this.readyPreview = false
     },
-    created() {
-        this.uploadVerify()
-        this.getvideo('default');
-        this.getteamvideo();
+    removeFileFinish(arr) {
+      this.hasFile = arr.length > 0
     },
-    mounted() {
-       let stemEditor = new E("#div1")
-       stemEditor.config.onchange = (html) => {
-           this.stemContent = html
-       }
-       stemEditor.config.uploadImgShowBase64 = true
-       stemEditor.config.zIndex = 500
-       stemEditor.create()
+    doCancel() {
+      this.isPreview = false
+      this.readyPreview = false
+      this.$refs.refFile.fileArr = []
+      this.hasFile = false
     },
-    methods: {
-        delClass(row) {
-            this.$Modal.confirm({
-                title: this.$t("jyzx.classRecord.message1"),
-                okText: this.$t("jyzx.common.delete"),
-                cancelText: this.$t("jyzx.common.cancel"),
-                onOk: () => {
-                    this.getvideo('delete', row.id)
-                    /* this.$Message.success("删除成功")*/
-                },
-            })
-
+    doConfirmUpload() {
+      this.isPreview = false
+      this.readyPreview = false
+      this.confirm()
+    },
+    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);
+      this.$nextTick(() => {
+        document.getElementById("previewVideo").src = url;
+      })
+    },
+    delClass(row) {
+      this.$Modal.confirm({
+        title: this.$t("jyzx.classRecord.message1"),
+        okText: this.$t("jyzx.common.delete"),
+        cancelText: this.$t("jyzx.common.cancel"),
+        onOk: () => {
+          this.getvideo('delete', row.id)
+          /* this.$Message.success("删除成功")*/
         },
-        //上传课堂实录相关数据
-        async uploadVerify() {
-            this.curSas = await this.$tools.getSchoolSas()
-            var user = this.$store.state.userInfo
-            this.uploadId = user.TEAMModelId
-        },
-        //upload过后
-        uploadFinish(res) {
-            console.log(res, '回调')
-            this.btnstate = false
-            if (res[0].blob && res[0].url && res[0].size) {
-                this.uploadData = res[0]
-                this.getvideo('uploadafter', 11)
-            } else {
-                this.$Message.error(this.$t("jyzx.classRecord.message2"))
+      })
+
+
+    },
+    //上传课堂实录相关数据
+    async uploadVerify() {
+      this.curSas = await this.$tools.getSchoolSas()
+      var user = this.$store.state.userInfo
+      this.uploadId = user.TEAMModelId
+    },
+    //upload过后
+    uploadFinish(res) {
+      console.log(res, '回调')
+      this.btnstate = false
+      if (res[0].blob && res[0].url && res[0].size) {
+        this.uploadData = res[0]
+        this.getvideo('uploadafter', 11)
+      } else {
+        this.$Message.error(this.$t("jyzx.classRecord.message2"))
+      }
+    },
+    //确认上传
+    confirm() {
+      console.log(this.calssListinfo, this.calssListinfo[0], '当时的文件')
+      console.log(this.$refs.refFile, '文件')
+      this.isPreview = false
+      this.readyPreview = false
+      this.$refs.refFile.fileArr.length ? (this.$refs.refFile.onConfirmUpload(), this.btnstate = true) : this.$Message.error('请选择上传文件')
+    },
+    //获取自己上传列表
+    getvideo(action, deleId) {
+      console.log(action, deleId, '传来的数据')
+      let user = this.$store.state.userInfo
+      var uploadafterData = {
+        "tmdid": user.TEAMModelId,
+        "school": user.schoolCode,
+        "opt": action === 'uploadafter' ? 'Upload' : action === 'delete' ? 'Delete' : 'Read',
+        "files": [],
+        "ids": [],
+      }
+      if (action === 'uploadafter') {
+        var datainfo = { "url": this.uploadData.url, "name": this.uploadData.name, "size": this.uploadData.size, "score": '', "hash": this.uploadData.hash }
+        if (this.calssListinfo.length) {
+          this.calssListinfo[0].score === 0 || this.calssListinfo[0].score === -1 || this.calssListinfo[0].score !== -2 ? datainfo.score = -2 : delete datainfo.score
+        } else {
+          delete datainfo.score
+        }
+        delete uploadafterData.ids
+        uploadafterData.files.push(datainfo)
+        console.log(uploadafterData, '查看内容')
+        this.$api.jyzx.getmemoir(uploadafterData).then(
+          res => {
+            console.log(res, '添加返回的')
+            this.operateData = res.classVideo.files
+            var timeData = []
+            res.classVideo.files.length >= 1 ? timeData.push(res.classVideo.files[res.classVideo.files.length - 1]) : ''
+            for (var u = 0; u < timeData.length; u++) {
+              timeData[u].time = this.formatDate(timeData[u].time)
+              timeData[u].size = this.formatSize(timeData[u].size)
             }
-        },
-        //确认上传
-        confirm() {
-            console.log(this.calssListinfo, this.calssListinfo[0], '当时的文件')
-            console.log(this.$refs.refFile, '文件')
-            this.$refs.refFile.fileArr.length ? (this.$refs.refFile.onConfirmUpload(), this.btnstate = true) : this.$Message.error('请选择上传文件')
-        },
-        //获取自己上传列表
-        getvideo(action, deleId) {
-            console.log(action, deleId, '传来的数据')
-            let user = this.$store.state.userInfo
-            var uploadafterData = {
-                "tmdid": user.TEAMModelId,
-                "school": user.schoolCode,
-                "opt": action === 'uploadafter' ? 'Upload' : action === 'delete' ? 'Delete' : 'Read',
-                "files": [],
-                "ids": [],
+            this.calssListinfo = timeData
+            console.log(this.calssListinfo, '过后的列表')
+            this.$Message.success(this.$t("jyzx.classRecord.message3"))
+          },
+          error => {
+            this.$Message.error(this.$t("jyzx.classRecord.message2"))
+          }
+        )
+      } else if (action === 'delete') {
+        delete uploadafterData.files
+        console.log(this.operateData, '删除流程')
+        for (let i in this.operateData) {
+          uploadafterData.ids.push(this.operateData[i].id)
+        }
+        console.log(uploadafterData, 'ids')
+        /*uploadafterData.ids.push(deleId)*/
+        this.$api.jyzx.getmemoir(uploadafterData).then(
+          res => {
+            console.log(res, '删除返回的')
+            var timeData = res.classVideo.files
+            for (var d = 0; d < timeData.length; d++) {
+              timeData[d].time = this.formatDate(timeData[d].time)
+              timeData[d].size = this.formatSize(timeData[d].size)
             }
-            if (action === 'uploadafter') {
-                var datainfo = { "url": this.uploadData.url, "name": this.uploadData.name, "size": this.uploadData.size, "score": '', "hash": this.uploadData.hash}
-                if (this.calssListinfo.length) {
-                    this.calssListinfo[0].score === 0 || this.calssListinfo[0].score === -1 || this.calssListinfo[0].score !== -2 ? datainfo.score = -2 : delete datainfo.score
-                } else {
-                    delete datainfo.score
-                }
-                delete uploadafterData.ids
-                uploadafterData.files.push(datainfo)
-                console.log(uploadafterData,'查看内容')
-                this.$api.jyzx.getmemoir(uploadafterData).then(
-                    res => {
-                        console.log(res, '添加返回的')
-                        this.operateData = res.classVideo.files
-                        var timeData = []
-                        res.classVideo.files.length >= 1 ? timeData.push(res.classVideo.files[res.classVideo.files.length-1]):''
-                        for (var u = 0; u < timeData.length; u++) {
-                            timeData[u].time = this.formatDate(timeData[u].time)
-                            timeData[u].size = this.formatSize(timeData[u].size)
-                        }
-                        this.calssListinfo = timeData
-                        console.log(this.calssListinfo,'过后的列表')
-                        this.$Message.success(this.$t("jyzx.classRecord.message3"))
-                    },
-                    error => {
-                        this.$Message.error(this.$t("jyzx.classRecord.message2"))
-                    }
-                )
-            } else if (action === 'delete') {
-                delete uploadafterData.files
-                console.log(this.operateData, '删除流程')
-                for (let i in this.operateData) {
-                    uploadafterData.ids.push(this.operateData[i].id)
-                }
-                console.log(uploadafterData,'ids')
-                /*uploadafterData.ids.push(deleId)*/
-                this.$api.jyzx.getmemoir(uploadafterData).then(
-                    res => {
-                        console.log(res, '删除返回的')
-                        var timeData = res.classVideo.files
-                        for (var d = 0; d < timeData.length; d++) {
-                            timeData[d].time = this.formatDate(timeData[d].time)
-                            timeData[d].size = this.formatSize(timeData[d].size)
-                        }
-                        this.calssListinfo = timeData
-                        /*this.calssListinfo=[]*/
-                        console.log(timeData)
-                        this.$Message.success(this.$t("jyzx.classRecord.message4"))
-                    },
-                    error => {
-                        this.$Message.success(this.$t("jyzx.classRecord.message5"))
-                    }
-                )
-            } else {
-                delete uploadafterData.ids
-                delete uploadafterData.files
-                this.$api.jyzx.getmemoir(uploadafterData).then(
-                    res => {
-                        console.log(res, '普通返回的')
-                        this.operateData = res.classVideo.files
-                        var timeData = []
-                        res.classVideo.files.length >= 1 ? timeData.push(res.classVideo.files[res.classVideo.files.length - 1]) : ''
-                        for (var p = 0; p < timeData.length; p++) {
-                            timeData[p].time = this.formatDate(timeData[p].time)
-                            timeData[p].size = this.formatSize(timeData[p].size)
-                        }
-                        this.calssListinfo = timeData
-                        this.calssListinfo[0].score === 1 || this.calssListinfo[0].score === 2 ? this.tipText = '您上传的课堂实录已通过,请勿重复上传' : ''
-                    },
-                    error => {
-
-                    }
-                )
+            this.calssListinfo = timeData
+            /*this.calssListinfo=[]*/
+            console.log(timeData)
+            this.$Message.success(this.$t("jyzx.classRecord.message4"))
+          },
+          error => {
+            this.$Message.success(this.$t("jyzx.classRecord.message5"))
+          }
+        )
+      } else {
+        delete uploadafterData.ids
+        delete uploadafterData.files
+        this.$api.jyzx.getmemoir(uploadafterData).then(
+          res => {
+            this.recordFile = res.classVideo.files.length ? res.classVideo.files[0] : null
+            console.log(res, '普通返回的')
+            this.operateData = res.classVideo.files
+            var timeData = []
+            res.classVideo.files.length >= 1 ? timeData.push(res.classVideo.files[res.classVideo.files.length - 1]) : ''
+            for (var p = 0; p < timeData.length; p++) {
+              timeData[p].time = this.formatDate(timeData[p].time)
+              timeData[p].size = this.formatSize(timeData[p].size)
             }
-        },
-        //同组课程查看评价
-        checkevaluate(data,isSelf){
-            console.log(data)
-            // this.showStatus=true
-            // this.courseName=index.name.substring(0,index.name.lastIndexOf("."));
-			this.$router.push({
-				name:'video',
-				params:{
-					data:data,
-					isSelf:isSelf
-				}
-			})
-			this.$EventBus.$emit('goVideoAppraise',data)
-        },
-        //同组课堂实录
-        getteamvideo() {
-            this.$api.jyzx.getTeamclass(
-                {
-                    "tmdid": this.$store.state.userInfo.TEAMModelId,
-                    "school": this.$store.state.userInfo.schoolCode
-                }
-            ).then(
-                res => {
-                    console.log(res, '同组数据')
-                    if (res.groupMembers) {
-                        res.groupMembers.forEach((i) => {
-                            i.vtime = this.formatDate(i.vtime)
-                            i.vsize = this.formatSize(i.vsize)
-                        })
-                        this.teamVideo = res.groupMembers
-                    } else {
-                        this.$Message.info(this.$t("jyzx.classRecord.message6"))
-                    }
+            this.calssListinfo = timeData
+            this.calssListinfo[0].score === 1 || this.calssListinfo[0].score === 2 ? this.tipText = '您上传的课堂实录已通过,请勿重复上传' : ''
+            if (this.recordFile) {
+              this.showReview = true
+              let routerData = this.calssListinfo[0]
+              let data = {
+                name: this.$store.state.userInfo.name,
+                videoName: routerData.name,
+                school: this.$store.state.userInfo.schoolCode,
+                video: {
+                  id: routerData.id,
+                  name: routerData.name,
+                  url: routerData.url
                 },
-                error => {
-
+                teacher: {
+                  id: this.$store.state.userInfo.TEAMModelId,
+                  name: this.$store.state.userInfo.name
                 }
-            )
-        },
-        //处理时间戳
-        formatDate(date) {
-            var date = new Date(date);
-            var YY = date.getFullYear() + '-';
-            var MM = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
-            var DD = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate());
-            var hh = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':';
-            var mm = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':';
-            var ss = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds());
-            return YY + MM + DD + " " + hh + mm + ss;
-        },
-        //处理文件大小
-        formatSize(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'
+              }
+              this.propsData = data
+            }
+          },
+          error => {
+
+          }
+        )
+      }
+    },
+    //同组课程查看评价
+    checkevaluate(data, isSelf) {
+      console.log(data)
+      // this.showStatus=true
+      // this.courseName=index.name.substring(0,index.name.lastIndexOf("."));
+      if (isSelf) {
+        this.showReview = true
+        let routerData = data
+        this.propsData = {
+          name: this.$store.state.userInfo.name,
+          videoName: routerData.name,
+          school: this.$store.state.userInfo.schoolCode,
+          video: {
+            id: routerData.id,
+            name: routerData.name,
+            url: routerData.url
+          },
+          teacher: {
+            id: this.$store.state.userInfo.TEAMModelId,
+            name: this.$store.state.userInfo.name
+          }
+        }
+      } else {
+        this.$router.push({
+          name: 'video',
+          params: {
+            data: data,
+            isSelf: isSelf
+          }
+        })
+      }
+
+
+      this.$EventBus.$emit('goVideoAppraise', data)
+    },
+    //同组课堂实录
+    getteamvideo() {
+      this.$api.jyzx.getTeamclass(
+        {
+          "tmdid": this.$store.state.userInfo.TEAMModelId,
+          "school": this.$store.state.userInfo.schoolCode
+        }
+      ).then(
+        res => {
+          console.log(res, '同组数据')
+          if (res.groupMembers) {
+            res.groupMembers.forEach((i) => {
+              i.vtime = this.formatDate(i.vtime)
+              i.vsize = this.formatSize(i.vsize)
+            })
+            this.teamVideo = res.groupMembers
+          } else {
+            this.$Message.info(this.$t("jyzx.classRecord.message6"))
+          }
         },
-        ss(ss) {
-            console.log(ss)
+        error => {
+
         }
+      )
+    },
+    //处理时间戳
+    formatDate(date) {
+      var date = new Date(date);
+      var YY = date.getFullYear() + '-';
+      var MM = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
+      var DD = (date.getDate() < 10 ? '0' + (date.getDate()) : date.getDate());
+      var hh = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':';
+      var mm = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':';
+      var ss = (date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds());
+      return YY + MM + DD + " " + hh + mm + ss;
     },
+    //处理文件大小
+    formatSize(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'
+    },
+    ss(ss) {
+      console.log(ss)
+    }
+  },
 }
 </script>
 
 <style lang="less">
 .class-memoir {
-    padding:1%;
-	// background-color: #f7f7f7;
-    .ivu-card {
-        height: 100%;
-        margin: 20px;
-        border: none;
-        box-shadow: none;
-    }
-    
-    .ivu-tabs-nav .ivu-tabs-tab,
-    .ivu-tabs-nav .ivu-tabs-tab:hover{
-        color: var(--second-text-color);
+  padding: 1%;
+  // background-color: #f7f7f7;
+  .ivu-card {
+    height: 100%;
+    margin: 20px;
+    border: none;
+    box-shadow: none;
+  }
+
+  .ivu-tabs-tabpane {
+    overflow: auto;
+  }
+
+  .video-tips {
+    color: rgb(75, 75, 70);
+    margin: 10px 0 30px 0;
+    text-align: center;
+    font-size: 14px;
+
+    span {
+      color: orangered;
+      font-weight: bold;
+      font-size: 16px;
     }
 
-    .ivu-tabs-nav .ivu-tabs-tab-active{
-        border-color: var(--tabs-bottom-color);
-        color: var(--tabs-text-color);
-        font-weight: bold;
+    .check-text {
+      text-decoration: underline;
+      color: rgb(47, 149, 233);
+      cursor: pointer;
     }
+  }
+
+  .ivu-tabs-nav .ivu-tabs-tab,
+  .ivu-tabs-nav .ivu-tabs-tab:hover {
+    color: var(--second-text-color);
+  }
+
+  .ivu-tabs-nav .ivu-tabs-tab-active {
+    border-color: var(--tabs-bottom-color);
+    color: var(--tabs-text-color);
+    font-weight: bold;
+  }
 }
 .teachnames {
-    font-size: 26px;
-    margin-left:10px;
+  font-size: 26px;
+  margin-left: 10px;
 }
 .coursename {
-    font-size:26px;
+  font-size: 26px;
 }
-.backward:hover{
-    cursor:pointer
+.backward:hover {
+  cursor: pointer;
 }
 /* .appraise {
     width:100%;
@@ -425,24 +540,25 @@ export default {
     border:1px solid #ccc;
 }*/
 .short span {
-    color: red;
+  color: red;
 }
-.qualified span{
-    color:orangered
+.qualified span {
+  color: orangered;
 }
-.excellent span{
-    color:rgb(0, 119, 0)
+.excellent span {
+  color: rgb(0, 119, 0);
+}
+.hint {
+  width: 100%;
+  text-align: center;
+  color: #ffa530;
+  font-size: 14px;
+  padding: 5px;
+  margin-bottom: 10px;
+}
+.uploaddiv {
+  width: 96%;
+  min-height: 42vh;
+  margin: 0 auto;
 }
-    .hint {
-        width: 100%;
-        text-align: center;
-        color: #FF3030;
-        font-size:12px;
-        padding:5px;
-    }
-    .uploaddiv {
-        width: 96%;
-        min-height: 42vh;
-        margin: 0 auto;
-    }
 </style>

+ 3 - 3
TEAMModelOS/ClientApp/src/view/jyzx/index.vue

@@ -38,10 +38,10 @@
                                     <!-- 总计学时是所有分钟相加得到,会与所有能力点已学的学时相加相冲突,所以只show总计的学时 -->
                                     {{ $t('jyzx.online.timeStudy') }}:
                                     <!-- limitMinutes:-1,只显示最大学时,不是-1,就显示limitMinutes -->
-                                    {{ setting.limitMinutes === -1 ? (item.total > (setting.lessonMinutes * item.hour) ? (setting.lessonMinutes * item.hour) : item.total) : (item.total > setting.limitMinutes ? setting.limitMinutes : item.total) }}
+                                    {{ setting.limitMinutes === -1 ? (item.total >= (setting.lessonMinutes * item.hour) ? (setting.lessonMinutes * item.hour) : item.total) : (item.total >= setting.limitMinutes ? setting.limitMinutes : item.total) }}
                                     {{ $t('jyzx.online.minute') }}
                                 </span>
-                                <span v-show="item.done && item.currency === 1" style="background-color: #16c18e;color: #fff; padding: 2px 5px;border-radius: 5px;">{{ $t('jyzx.common.complete') }}</span>
+                                <span v-show="item.currency === 1 && (setting.limitMinutes === -1 ? item.done : (item.total >= setting.limitMinutes))" style="background-color: #16c18e;color: #fff; padding: 2px 5px;border-radius: 5px;">{{ $t('jyzx.common.complete') }}</span>
                             </p>
                             <p v-if="item.from" style="text-align:right; margin-right: 20px">来源:省平台</p>
                         </div>
@@ -1488,7 +1488,7 @@ export default {
                 }
                 console.log("视频学完");
                 this.studyRecord.done = true
-                this.studyRecord.view = parseInt(e.target.currentTime)
+                this.studyRecord.view = parseInt(e.target.duration)
                 var newArr = {
                     hash: this.studyRecord.hash,
                     done: this.studyRecord.done,

+ 8 - 2
TEAMModelOS/ClientApp/src/view/jyzx/newHomePage.vue

@@ -287,11 +287,11 @@ export default {
                     if(res.setting.limitMinutes != -1) {
                         res.teacherTrain.currency.teacherAilities.forEach(item => {
                             this.onlineInfo.minuteTotal += res.setting.limitMinutes
-                            this.onlineInfo.minute += (item.videoTime > res.setting.limitMinutes) ? res.setting.limitMinutes : item.videoTime
+                            this.onlineInfo.minute += (item.videoTime >= res.setting.limitMinutes) ? res.setting.limitMinutes : item.videoTime
                         });
                     } else {
                         this.onlineInfo.minuteTotal = res.setting.onlineTime * res.setting.lessonMinutes
-                        this.onlineInfo.minute = (res.teacherTrain.currency.videoTime > (res.setting.onlineTime * res.setting.lessonMinutes)) ? (res.setting.onlineTime * res.setting.lessonMinutes) : res.teacherTrain.currency.videoTime
+                        this.onlineInfo.minute = (res.teacherTrain.currency.videoTime >= (res.setting.onlineTime * res.setting.lessonMinutes)) ? (res.setting.onlineTime * res.setting.lessonMinutes) : res.teacherTrain.currency.videoTime
                     }
                     
                     // 校本研修
@@ -398,6 +398,12 @@ export default {
             return 0
         },
     },
+	beforeRouteLeave(to, from, next) {
+	    console.log(to)
+	    console.log(from)
+		console.log(this.$route)
+	    next();
+	},
 }
 </script>
 

+ 60 - 60
TEAMModelOS/ClientApp/src/view/login/Index.vue

@@ -481,22 +481,22 @@ export default {
             }
             // 登录教师端
             else {
-				/* 进行教师登录 */
-				this.$loginTools.teacherLogin(item,schoolCode).then(info => {
-					// 前往区级平台
-					if (info.toArea) {
-					    localStorage.setItem('platform', 'area')
-					    this.$router.push({ path: '/area' })
-					} else {
-						// 前往校级平台 判断是否有记录的目标地址 如果没有则按照站点来进行对应跳转固定页面
-						let targetPath = localStorage.getItem('target_path')
-						let defaultPath = '/home'
-					    let homePath = targetPath ? (targetPath.includes('home/') ? targetPath : defaultPath) : defaultPath
-					    localStorage.setItem('platform', 'school')
-						localStorage.setItem('target_path','')
-					    this.$router.push({ path: homePath })
-					}
-				})
+                /* 进行教师登录 */
+                this.$loginTools.teacherLogin(item, schoolCode).then(info => {
+                    // 前往区级平台
+                    if (info.toArea) {
+                        localStorage.setItem('platform', 'area')
+                        this.$router.push({ path: '/area' })
+                    } else {
+                        // 前往校级平台 判断是否有记录的目标地址 如果没有则按照站点来进行对应跳转固定页面
+                        let targetPath = localStorage.getItem('target_path')
+                        let defaultPath = '/home'
+                        let homePath = targetPath ? (targetPath.includes('home/') ? targetPath : defaultPath) : defaultPath
+                        localStorage.setItem('platform', 'school')
+                        localStorage.setItem('target_path', '')
+                        this.$router.push({ path: homePath })
+                    }
+                })
             }
         },
         saveUserCodes: function (res) {
@@ -517,50 +517,50 @@ export default {
             this.$router.push({ name: path })
         },
         loginTo: function (identity) {
-			// 选择身份时 需要去判断 当前是否有教师或者学生已登录状态
-			let id_token = localStorage.getItem('id_token')
-			let stu_id_token = localStorage.getItem('stu_id_token')
-			this.$Spin.show();
-			if(identity === 'teacher' && id_token){
-				this.$loginTools.teacherLogin({
-					id_token:id_token,
-					access_token:localStorage.getItem('access_token'),
-					expires_in:localStorage.getItem('expires_in')
-				}).then(info => {
-					// 前往区级平台
-					if (info.toArea) {
-					    localStorage.setItem('platform', 'area')
-					    this.$router.push({ path: '/area' })
-					} else {
-						// 前往校级平台 判断是否有记录的目标地址 如果没有则按照站点来进行对应跳转固定页面
-						let targetPath = localStorage.getItem('target_path')
-						let defaultPath = '/home'
-					    let homePath = targetPath ? (targetPath.includes('home/') ? targetPath : defaultPath) : defaultPath
-					    localStorage.setItem('platform', 'school')
-						localStorage.setItem('target_path','')
-					    this.$router.push({ path: homePath })
-					}
-					this.$Spin.hide();
-				})
-			}else if(identity === 'student' && stu_id_token){
-				// 判断学生是通过ID登录还是校内账号登录
-				let tokenData = jwtDecode(stu_id_token)
-				if(tokenData.scope === 'student'){
-					this.$loginTools.stuLoginByAccount({
-						id_token:stu_id_token
-					},true)
-				}else{
-					this.$loginTools.stuLoginById({
-						id_token:stu_id_token,
-						access_token:localStorage.getItem('access_token')
-					})
-				}
-				this.$Spin.hide();
-			}else{
-				let pathname = identity == 'student' ? 'loginStudent' : 'loginTeacher'
-				this.$router.push({ name: pathname })
-				this.$Spin.hide();
-			}
+            // 选择身份时 需要去判断 当前是否有教师或者学生已登录状态
+            let id_token = localStorage.getItem('id_token')
+            let stu_id_token = localStorage.getItem('stu_id_token')
+            this.$Spin.show();
+            if (identity === 'teacher' && id_token) {
+                this.$loginTools.teacherLogin({
+                    id_token: id_token,
+                    access_token: localStorage.getItem('access_token'),
+                    expires_in: localStorage.getItem('expires_in')
+                }).then(info => {
+                    // 前往区级平台
+                    if (info.toArea) {
+                        localStorage.setItem('platform', 'area')
+                        this.$router.push({ path: '/area' })
+                    } else {
+                        // 前往校级平台 判断是否有记录的目标地址 如果没有则按照站点来进行对应跳转固定页面
+                        let targetPath = localStorage.getItem('target_path')
+                        let defaultPath = '/home'
+                        let homePath = targetPath ? (targetPath.includes('home/') ? targetPath : defaultPath) : defaultPath
+                        localStorage.setItem('platform', 'school')
+                        localStorage.setItem('target_path', '')
+                        this.$router.push({ path: homePath })
+                    }
+                    this.$Spin.hide();
+                })
+            } else if (identity === 'student' && stu_id_token) {
+                // 判断学生是通过ID登录还是校内账号登录
+                let tokenData = jwtDecode(stu_id_token)
+                if (tokenData.scope === 'student') {
+                    this.$loginTools.stuLoginByAccount({
+                        id_token: stu_id_token
+                    }, true)
+                } else {
+                    this.$loginTools.stuLoginById({
+                        id_token: stu_id_token,
+                        access_token: localStorage.getItem('access_token')
+                    })
+                }
+                this.$Spin.hide();
+            } else {
+                let pathname = identity == 'student' ? 'loginStudent' : 'loginTeacher'
+                this.$router.push({ name: pathname })
+                this.$Spin.hide();
+            }
         }
     }
 }

+ 1 - 0
TEAMModelOS/ClientApp/src/view/login/jinniu/Index.vue

@@ -433,6 +433,7 @@ export default {
                             name: result.name,
                             schoolCode: result.defaultschool
                         })
+                        debugger
                         this.$router.push({ path: '/home' })
                     } else {
                         this.identityFlag = true;

+ 35 - 35
TEAMModelOS/ClientApp/src/view/login/page/Teacher.vue

@@ -325,22 +325,22 @@ export default {
                         this.loginErrText = this.$t('login.apiError.text1')
                         this.loading = false
                     } else {
-						/* 进行教师登录 */
-						this.$loginTools.teacherLogin(result).then(info => {
-							// 前往区级平台
-							if (info.toArea) {
-							    localStorage.setItem('platform', 'area')
-							    this.$router.push({ path: '/area' })
-							} else {
-								// 前往校级平台 判断是否有记录的目标地址 如果没有则按照站点来进行对应跳转固定页面
-								let targetPath = localStorage.getItem('target_path')
-								let defaultPath = '/home'
-							    let homePath = targetPath ? (targetPath.includes('home/') ? targetPath : defaultPath) : defaultPath
-							    localStorage.setItem('platform', 'school')
-								localStorage.setItem('target_path','')
-							    this.$router.push({ path: homePath })
-							}
-						})
+                        /* 进行教师登录 */
+                        this.$loginTools.teacherLogin(result).then(info => {
+                            // 前往区级平台
+                            if (info.toArea) {
+                                localStorage.setItem('platform', 'area')
+                                this.$router.push({ path: '/area' })
+                            } else {
+                                // 前往校级平台 判断是否有记录的目标地址 如果没有则按照站点来进行对应跳转固定页面
+                                let targetPath = localStorage.getItem('target_path')
+                                let defaultPath = '/home'
+                                let homePath = targetPath ? (targetPath.includes('home/') ? targetPath : defaultPath) : defaultPath
+                                localStorage.setItem('platform', 'school')
+                                localStorage.setItem('target_path', '')
+                                this.$router.push({ path: homePath })
+                            }
+                        })
                     }
                 }
             })
@@ -436,11 +436,11 @@ export default {
                         localStorage.setItem('platform', 'area')
                         this.$router.push({ path: '/area' })
                     } else {
-						let targetPath = localStorage.getItem('target_path')
-						let defaultPath = '/home'
+                        let targetPath = localStorage.getItem('target_path')
+                        let defaultPath = '/home'
                         let homePath = targetPath ? (targetPath.includes('home/') ? targetPath : defaultPath) : defaultPath
                         localStorage.setItem('platform', 'school')
-						localStorage.setItem('target_path','')
+                        localStorage.setItem('target_path', '')
                         this.$router.push({ path: homePath })
                     }
                 }
@@ -462,22 +462,22 @@ export default {
             this.$api.SSOLogin(code).then(async res => {
                 this.$Spin.hide(); // 關閉加載畫面
                 if (!res.error) {
-					/* 进行教师登录 */
-					this.$loginTools.teacherLogin(res).then(info => {
-						// 前往区级平台
-						if (info.toArea) {
-						    localStorage.setItem('platform', 'area')
-						    this.$router.push({ path: '/area' })
-						} else {
-							// 前往校级平台 判断是否有记录的目标地址 如果没有则按照站点来进行对应跳转固定页面
-							let targetPath = localStorage.getItem('target_path')
-							let defaultPath = '/home'
-						    let homePath = targetPath ? (targetPath.includes('home/') ? targetPath : defaultPath) : defaultPath
-						    localStorage.setItem('platform', 'school')
-							localStorage.setItem('target_path','')
-						    this.$router.push({ path: homePath })
-						}
-					})
+                    /* 进行教师登录 */
+                    this.$loginTools.teacherLogin(res).then(info => {
+                        // 前往区级平台
+                        if (info.toArea) {
+                            localStorage.setItem('platform', 'area')
+                            this.$router.push({ path: '/area' })
+                        } else {
+                            // 前往校级平台 判断是否有记录的目标地址 如果没有则按照站点来进行对应跳转固定页面
+                            let targetPath = localStorage.getItem('target_path')
+                            let defaultPath = '/home'
+                            let homePath = targetPath ? (targetPath.includes('home/') ? targetPath : defaultPath) : defaultPath
+                            localStorage.setItem('platform', 'school')
+                            localStorage.setItem('target_path', '')
+                            this.$router.push({ path: homePath })
+                        }
+                    })
                     // this.loginProcess(res, this.defaultSchool.code)
                 } else {
                     this.$Message.warning(this.$t('login.sse.error.text1'));

+ 18 - 18
TEAMModelOS/ClientApp/src/view/sso/Index.vue

@@ -94,23 +94,23 @@ export default {
         },
         loginProcess(idRes) { // 登入用function
             this.clearData() //这里暂定解决登录失败的问题
-			sessionStorage.setItem('identity','teacher')
-			/* 进行教师登录 */
-			this.$loginTools.teacherLogin(idRes).then(info => {
-				// 前往区级平台
-				if (info.toArea) {
-				    localStorage.setItem('platform', 'area')
-				    this.$router.push({ path: '/area' })
-				} else {
-					// 前往校级平台 判断是否有记录的目标地址 如果没有则按照站点来进行对应跳转固定页面
-					let targetPath = localStorage.getItem('target_path')
-					let defaultPath = '/home'
-				    let homePath = targetPath ? (targetPath.includes('home/') ? targetPath : defaultPath) : defaultPath
-				    localStorage.setItem('platform', 'school')
-					localStorage.setItem('target_path','')
-				    this.$router.push({ path: homePath })
-				}
-			})
+            sessionStorage.setItem('identity', 'teacher')
+            /* 进行教师登录 */
+            this.$loginTools.teacherLogin(idRes).then(info => {
+                // 前往区级平台
+                if (info.toArea) {
+                    localStorage.setItem('platform', 'area')
+                    this.$router.push({ path: '/area' })
+                } else {
+                    // 前往校级平台 判断是否有记录的目标地址 如果没有则按照站点来进行对应跳转固定页面
+                    let targetPath = localStorage.getItem('target_path')
+                    let defaultPath = '/home'
+                    let homePath = targetPath ? (targetPath.includes('home/') ? targetPath : defaultPath) : defaultPath
+                    localStorage.setItem('platform', 'school')
+                    localStorage.setItem('target_path', '')
+                    this.$router.push({ path: homePath })
+                }
+            })
         },
     },
     created() {
@@ -171,7 +171,7 @@ export default {
                     let ps = decodeURIComponent(this.routerData.param)
                     let p = JSON.parse(ps || '{}')
                     this.$Modal.warning({
-                        title:'信息异常',
+                        title: '信息异常',
                         content: `提供的培训 ID(${p.Pxid}) 和教师 ID(${p.tid})与省平台不匹配或不存在,请联系相关人员!`
                     })
                     break

+ 16 - 4
TEAMModelOS/ClientApp/src/view/video/VideoReview.vue

@@ -4,7 +4,7 @@
 			<div class="video-wrap" style="display: flex;width: 100%;">
 				<div class="left-panel">
 					<div class="detail-title">
-						<Icon type="md-arrow-back" @click="goBack" />
+						<Icon type="md-arrow-back" @click="goBack" v-if="!isSelf"/>
 						<span>{{ curData.video.name }} 【 {{ curData.teacher.name }} - {{ $t('ability.video.title') }}】</span>
 					</div>
 					<!-- <video-player class="video-player-box" style="width:100%;margin-bottom: 20px;" ref="videoPlayer" v-if="videoReady"
@@ -57,6 +57,8 @@
 								</div>
 								<Button type="success" size="large" icon="ios-paper-plane" @click="onSubmit"
 									style="margin-left: 10px;" :loading="isBtnLoading" v-if="!isSelf">{{ $t('ability.video.submit') }}</Button>
+								<Button type="success" size="large" icon="ios-paper-plane" @click="reSubmit"
+									style="margin-left: 10px;" v-if="isSelf && curQuickAppraise < 1">重新提交</Button>	
 							</div>
 						</div>
 					</div>
@@ -112,6 +114,7 @@
 		},
 		data(vm) {
 			return {
+				dataMode:'',
 				curQuickAppraise: 0,
 				appraiseArr: [0, 0, 0, 0, 0],
 				dimensionList: this.$GLOBAL.VIDEO_DIMENSIONS(),
@@ -194,7 +197,6 @@
 		created() {
 			let routerData = this.$route.params.data
 			let isSelf = this.$route.params.isSelf
-			console.log(isSelf)
 			if (routerData && isSelf) {
 				this.isSelf = isSelf
 				this.isShowDetail = true
@@ -216,6 +218,7 @@
 				this.goReview(data)
 				
 			}else if(routerData){
+				this.isSelf = false
 				this.isTeacherRole = true
 				this.isShowDetail = true
 				let data = {
@@ -239,6 +242,10 @@
 			onInputFocus() {
 				// this.curVideoTime = 
 			},
+			/* 重新提交 */
+			reSubmit(){
+				this.$emit('reSubmit')
+			},
 			/* 提交点评 */
 			onSubmitComment() {
 				if (this.commentVal.trim() === '') {
@@ -307,7 +314,6 @@
 			},
 			/* 点击时间点进行视频跳转 */
 			onTimeClick(seconds) {
-				console.log(seconds)
 				this.$refs.videoPlayer.play();
 				this.$refs.videoPlayer.currentTime = seconds;
 			},
@@ -326,7 +332,6 @@
 					this.$Message.warning(this.$t('ability.video.tip2'))
 					return
 				}
-				console.log(this.appraiseResult);
 				this.isBtnLoading = true
 				let params = {
 					"tmdid": this.curData.teacher.id,
@@ -589,6 +594,13 @@
 		  reviewData: {
 		    async handler(n, o) {
 		      if (n) {
+				if(this.$route.params.data){
+					this.dataMode = 'route'
+					this.isSelf = this.$route.params.isSelf
+				}else{
+					this.dataMode = 'props'
+					this.isSelf = this.mode === 'self' 
+				}
 		        this.goReview(n)
 		      }
 		    },

File diff suppressed because it is too large
+ 1076 - 1108
TEAMModelOS/ClientApp/src/view/vote/ManageVote.vue


+ 168 - 0
TEAMModelOS/Controllers/Normal/AbilityStatisticsController.cs

@@ -141,6 +141,174 @@ namespace TEAMModelOS.Controllers
                 return Ok(new { error = 1, msg = "你没有学习记录" });
             }
         }
+
+        /// <summary>
+        /// 区级数据统计
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("statistics-area-simple")]
+        [AuthToken(Roles = "teacher,admin,area")]
+        [Authorize(Roles = "IES")]
+        public async Task<IActionResult> StatisticsAreaSimple(JsonElement request)
+        {
+            var (userid, _, _, _) = HttpContext.GetAuthTokenInfo();
+            request.TryGetProperty("standard", out JsonElement _standard);
+            string standard = $"{_standard}";
+            if (string.IsNullOrEmpty(standard))
+            {
+                return BadRequest();
+            }
+            var client = _azureCosmos.GetCosmosClient();
+            Area area = null;
+            string sql = $"select value(c) from c where c.standard='{standard}'";
+            await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<Area>(queryText: sql,
+                  requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-Area") }))
+            {
+                area = item;
+            }
+            AreaSetting setting = null;
+            if (area != null)
+            {
+                try
+                {
+                    setting = await client.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemAsync<AreaSetting>(area.id, new PartitionKey("AreaSetting"));
+                }
+                catch (CosmosException)
+                {
+                    setting = null;
+                }
+            }
+            if (setting == null)
+            {
+                setting = new AreaSetting
+                {
+                    allTime = 50,
+                    classTime = 5,
+                    submitTime = 15,
+                    onlineTime = 20,
+                    offlineTime = 10,
+                    lessonMinutes = 45,
+                };
+            }
+
+            try
+            {
+                List<TeacherTrain> teacherTrains = new List<TeacherTrain>();
+                List<School> schools = new List<School>();
+                await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<School>(queryText: $"select value(c) from c where c.areaId='{area.id}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") }))
+                {
+
+                    schools.Add(item);
+                }
+                List<Task<(List<TeacherTrain> trains, List<RGroupList> yxtrain)>> trains = new List<Task<(List<TeacherTrain> trains, List<RGroupList> yxtrain)>>();
+                int countArea = 0;
+                int appraiseArea = 0;
+                List<SchoolInfos> schoolInfos = new List<SchoolInfos>();
+                foreach (var school in schools)
+                {
+                    
+                    schoolInfos.Add(new SchoolInfos { schoolId = school.id, schoolName = school.name, picture = school.picture });
+                    //增加评审人员总人数,学习总人数。
+                    trains.Add(StatisticsService.StatisticsSchool(_coreAPIHttpService, school.id, setting, area, client, _dingDing, null));
+                }
+                int pagesize = 100;
+                if (trains.Count <= pagesize)
+                {
+                    (List<TeacherTrain> trains, List<RGroupList> yxtrain)[] tasks = await Task.WhenAll(trains);
+                    tasks.ToList().ForEach(x => {
+                        teacherTrains.AddRange(x.trains);
+                        schoolInfos.ForEach(y => {
+                            var list = x.yxtrain.Find(z => z.school.Equals(y.schoolId));
+                            if (list != null)
+                            {
+                                y.trainCount = list.members.Count;
+                            }
+                        });
+                    });
+                }
+                else
+                {
+                    int pages = (trains.Count + pagesize) / pagesize; //256是批量操作最大值,pages = (total + max -1) / max;
+                    for (int i = 0; i < pages; i++)
+                    {
+                        var listssb = trains.Skip((i) * pagesize).Take(pagesize).ToList();
+                        (List<TeacherTrain> trains, List<RGroupList> yxtrain)[] tasks = await Task.WhenAll(listssb);
+                        tasks.ToList().ForEach(x => {
+                            teacherTrains.AddRange(x.trains);
+                            schoolInfos.ForEach(y => {
+                                var list = x.yxtrain.Find(z => z.school.Equals(y.schoolId));
+                                if (list != null)
+                                {
+                                    y.trainCount = list.members.Count;
+                                }
+                            });
+                        });
+                    }
+                }
+                long totalTime = teacherTrains.Select(x => x.totalTime).Sum();
+                int hgcount = teacherTrains.Where(x => x.finalScore == 1 || x.finalScore == 2).Count();
+                setting.accessConfig = null;
+
+
+
+                List<dynamic> dynamics = new List<dynamic>();
+                teacherTrains.ForEach(x => {
+                    x.schoolName = schools.Find(s => s.id.Equals(x.school))?.name;
+                    x.currency.videoTime = (int)x.currency.videoTime;
+                    x.currency.teacherAilities.ForEach(y => {
+                        y.videoTime = (int)y.videoTime;
+                        y.onlineTime = (int)y.onlineTime;
+                        y.debateOrther = y.debateOrther > 0 ? y.debateOrther : 0;
+                    });
+                    dynamic dynamic = new {
+                        x.schoolName,
+                        x.classTime,
+                        x.finalScore,
+                        x.name,
+                        x.offlineTime,
+                        x.onlineTime,
+                        x.school,
+                        x.id,
+                        x.totalTime,
+                        currency = new {
+                            x.currency.submitTime,
+                            x.currency.exerciseAbility,
+                            x.currency.learnAbility,
+                            x.currency.uploadDone,
+                            x.currency.uploadTotal,
+                            x.currency.videoTime,
+                            teacherAilities = x.currency.teacherAilities.Select(t => new {
+                                t.onlineTime,
+                                t.videoTime,
+                                t.no,
+                                t.name,
+                                t.dimension,
+                                t.zpscore, t.xzscore, t.hpscore
+                            }),
+                        },
+                        offlineCountNo = x.offlineRecords.Where(o => o.upload == 0).Count(),
+                        offlineCountOk = x.offlineRecords.Where(o => o.upload == 1).Count(),
+                        teacherClasseCount = x.teacherClasses.Count
+                    };
+                    dynamics.Add(dynamic);
+                });
+
+
+                return Ok(new { teacherTrains= dynamics, setting, schools = schoolInfos, totalTime, hgcount, teacherCount = countArea, appraiseCount = appraiseArea });
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"OS,{_option.Location},UpsertSubmitScore/UpsertSubmitScore()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                return BadRequest(new { ex.Message, ex.StackTrace });
+
+            }
+        }
+
+
+
+
         /// <summary>
         /// 区级数据统计
         /// </summary>

+ 299 - 0
TEAMModelOS/Controllers/Third/Xkw/OpenAuthClient.cs

@@ -0,0 +1,299 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Security.Cryptography;
+using System.Text;
+using TEAMModelOS.SDK.Extension;
+
+namespace TEAMModelOS.Controllers.Third.Xkw
+{
+	public abstract class OpenAuthClient
+	{
+		public string ClientName { get; protected set; }
+		public string AppKey { get; protected set; }
+		public string AppSecret { get; protected set; }
+		public string RedirectUrl { get; set; }
+		public string AccessToken { get; set; }
+
+		public bool IsAuthorized
+		{
+			get { return isAccessTokenSet && !string.IsNullOrEmpty(AccessToken); }
+		}
+
+		protected bool isAccessTokenSet = false;
+		public abstract string GetAuthorizationUrl();
+		public abstract void GetAccessTokenByCode(string code, string schoolId);
+
+
+		public OpenAuthClient(string clientId, string appSecret, string redirectUrl, string accessToken = null)
+		{
+			this.AppKey = clientId;
+			this.AppSecret = appSecret;
+			this.RedirectUrl = redirectUrl;
+			this.AccessToken = accessToken;
+
+			if (!string.IsNullOrEmpty(accessToken))
+			{
+				isAccessTokenSet = true;
+			}
+		}
+
+	}
+
+    public class XkwOAuthClient : OpenAuthClient
+    {
+        public string AUTH_HOST { get; set; }
+        public string AUTH_URL { get; set; }
+        public string TOKEN_URL { get; set; }
+        public string PROFILE_URL { get; set; }
+
+        public string SERVICE_URL { get; set; }
+        public string OpenId { get; set; }
+        public string UserId { get; set; }
+        /// <summary>
+        /// 静默注册参数
+        /// </summary>
+        public string Extra { get; set; }
+        public string ErrorMessage { get; set; }
+        public XkwOAuthClient(string appKey, string appSecret, string redirectUrl, string oauthHost, string accessToken = null, string openid = null, string userId = null)
+            : base(appKey, appSecret, redirectUrl, accessToken)
+        {
+            ClientName = "Xkw Demo Client";
+            OpenId = openid;
+            UserId = userId;
+            AUTH_HOST = oauthHost;
+            AUTH_URL = AUTH_HOST + "authorize";
+            TOKEN_URL = AUTH_HOST + "accessToken";
+            PROFILE_URL = AUTH_HOST + "profile";
+
+            if (!(string.IsNullOrEmpty(accessToken) && string.IsNullOrEmpty(openid)))
+            {
+                isAccessTokenSet = true;
+            }
+        }
+
+        /// <summary>
+        /// 进行认证
+        /// </summary>
+        /// <returns></returns>
+        public override string GetAuthorizationUrl()
+        {
+            string openSecret = "";
+            if (!string.IsNullOrEmpty(OpenId))
+            {
+                openSecret = CryptoUtils.EncryptAES(OpenId, AppSecret);
+            }
+            string timespan = CryptoUtils.EncryptAES(GetTimeStamp(), AppSecret);
+            string url = string.Format(AUTH_URL + "?client_id={0}&open_id={1}&service={2}&redirect_uri={3}&timespan={4}",
+                  AppKey, openSecret, SERVICE_URL, RedirectUrl, timespan);
+            if (!string.IsNullOrEmpty(Extra))
+            {
+                url = string.Format("{0}&extra={1}", url, Extra);
+            }
+            string retUrl = url + "&signature=" + SignatureHelper.GenerateSignature(url, AppSecret);
+            return retUrl.Replace("+", "%2B");
+        }
+
+        /// <summary>
+        /// 根据code获取accessToken和用户信息
+        /// </summary>
+        /// <param name="code">code</param>
+        /// <param name="schoolId">学校ID</param>
+		public override void GetAccessTokenByCode(string code, string schoolId = null)
+        {
+            var client = new WebClient();
+            client.Encoding = System.Text.Encoding.UTF8;
+            try
+            {
+                //验证服务器证书回调自动验证
+                ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
+                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>>();
+                if (access_token_dic.ContainsKey("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);
+                IDictionary<string, string> openId_dic = ret.ToObject<IDictionary<string, string>>();
+                if (openId_dic.ContainsKey("open_id"))
+                {
+                    OpenId = openId_dic["open_id"];
+                }
+                if (!string.IsNullOrEmpty(OpenId))
+                {
+                    isAccessTokenSet = true;
+                }
+                else
+                {
+                    if (openId_dic.ContainsKey("error"))
+                    {
+                        ErrorMessage = openId_dic["error"];
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                ErrorMessage = "服务器异常:" + ex.Message;
+            }
+        }
+
+        /// <summary>
+        /// 获取当前时间的时间戳
+        /// </summary>
+        /// <returns></returns>
+        private string GetTimeStamp()
+        {
+            TimeSpan ts = new TimeSpan(DateTime.UtcNow.Ticks - new DateTime(1970, 1, 1, 0, 0, 0).Ticks);
+            return ((long)ts.TotalMilliseconds).ToString();
+        }
+
+    }
+    class SignatureHelper
+    {
+        /// <summary>
+        /// 获取参数签名
+        /// </summary>
+        /// <param name="url">需要生成签名的url</param>
+        /// <returns></returns>
+        public static string GenerateSignature(String url, String secret)
+        {
+            int index = url.IndexOf("?");
+            if (index > -1)
+            {
+                //将参数按参数名进行排序,然后对参数值进行加密
+                string paramStr = url.Substring(index + 1);
+                string[] paramKeyValues = paramStr.Split('&');
+                Array.Sort(paramKeyValues);
+                StringBuilder paramValueStr = new StringBuilder();
+                foreach (string param in paramKeyValues)
+                {
+                    string[] paramKeyValue = param.Split(new char[] { '=' }, 2);
+                    paramValueStr.Append(paramKeyValue[1]);
+                }
+                paramValueStr.Append(secret);
+                return CryptoUtils.EncryptMD5(paramValueStr.ToString());
+            }
+            return "";
+        }
+
+        /// <summary>
+        /// 获取参数签名
+        /// </summary>
+        /// <param name="paramDic">参数键值对</param>
+        /// <param name="secret">加密秘钥</param>
+        /// <returns></returns>
+        public static string GenerateSignature(SortedDictionary<string, string> paramDic, String secret)
+        {
+            if (paramDic.Count > 0)
+            {
+                string paramStr = string.Concat(string.Join("", paramDic.Values), secret);
+                return CryptoUtils.EncryptMD5(paramStr);
+            }
+            return "";
+        }
+
+        /// <summary>
+        /// 获取参数签名
+        /// </summary>
+        /// <param name="paramList">参数键值对</param>
+        /// <param name="secret">加密秘钥</param>
+        /// <returns></returns>
+        public static string GenerateSignature(SortedList<string, string> paramList, String secret)
+        {
+            if (paramList.Count > 0)
+            {
+                string paramStr = string.Concat(string.Join("", paramList.Values), secret);
+                return CryptoUtils.EncryptMD5(paramStr);
+            }
+            return "";
+        }
+    }
+    public class CryptoUtils
+    {
+        /// <summary>
+        /// MD5加密
+        /// </summary>
+        /// <param name="encryptString">待加密的字符串</param>
+        /// <returns>加密过的字符串</returns>
+        public static string EncryptMD5(string encryptString)
+        {
+            byte[] result = Encoding.UTF8.GetBytes(encryptString);
+            MD5 md5 = new MD5CryptoServiceProvider();
+            byte[] output = md5.ComputeHash(result);
+            string encryptResult = BitConverter.ToString(output).Replace("-", "");
+            return encryptResult;
+        }
+
+        #region AES
+        /// <summary>  
+        /// AES加密  
+        /// </summary>  
+        /// <param name="str">待加密字符串</param>  
+        /// <returns>加密后字符串</returns>  
+        public static string EncryptAES(string str, string key)
+        {
+            try
+            {
+                //分组加密算法  
+                AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
+                byte[] inputByteArray = Encoding.UTF8.GetBytes(str);//得到需要加密的字节数组   
+                //设置密钥及密钥向量  
+                aes.Key = Encoding.UTF8.GetBytes(key);
+                //aes.IV = Encoding.UTF8.GetBytes(key);  
+                aes.Mode = CipherMode.ECB;
+                aes.Padding = PaddingMode.PKCS7;
+                byte[] cipherBytes = null;
+                using (MemoryStream ms = new MemoryStream())
+                {
+                    using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
+                    {
+                        cs.Write(inputByteArray, 0, inputByteArray.Length);
+                        cs.FlushFinalBlock();
+                        cipherBytes = ms.ToArray();//得到加密后的字节数组  
+                        cs.Close();
+                        ms.Close();
+                    }
+                }
+                return Convert.ToBase64String(cipherBytes);
+            }
+            catch { }
+            return str;
+        }
+
+        /// <summary>  
+        /// AES解密  
+        /// </summary>  
+        /// <param name="str">待解密字符串</param>  
+        /// <returns>解密后字符串</returns>  
+        public static string DecryptAES(string str, string key)
+        {
+            try
+            {
+                byte[] cipherText = Convert.FromBase64String(str);
+                AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
+                aes.Key = Encoding.UTF8.GetBytes(key);
+                //aes.IV = Encoding.UTF8.GetBytes(key);  
+                aes.Mode = CipherMode.ECB;
+                aes.Padding = PaddingMode.PKCS7;
+                byte[] decryptBytes = new byte[cipherText.Length];
+                using (MemoryStream ms = new MemoryStream(cipherText))
+                {
+                    using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read))
+                    {
+                        cs.Read(decryptBytes, 0, decryptBytes.Length);
+                        cs.Close();
+                        ms.Close();
+                    }
+                }
+                return Encoding.UTF8.GetString(decryptBytes).Replace("\0", "");   //将字符串后尾的'\0'去掉  
+            }
+            catch { }
+            return str;
+        }
+        #endregion
+
+    }
+}

+ 117 - 5
TEAMModelOS/Controllers/Third/Xkw/XkwOAuth2Controller.cs

@@ -29,6 +29,9 @@ 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;
+
 namespace TEAMModelOS.Controllers
 {
     // <summary>
@@ -39,7 +42,7 @@ namespace TEAMModelOS.Controllers
     [ProducesResponseType(StatusCodes.Status400BadRequest)]
     //
     //[Route("")]
-    //[Route("api/[controller]")]
+    [Route("xkw")]
     [ApiController]
     public class XkwOAuth2Controller : ControllerBase
     {
@@ -94,11 +97,120 @@ namespace TEAMModelOS.Controllers
         /// <param name="request"></param>
         /// <returns></returns>
 
-        [HttpGet("{path}/oauth2")]
-        [AllowAnonymous]
-        public async Task<IActionResult> Oauth2([FromQuery] OAuth oauth2, string path) {
+        [HttpGet("oauth")]
+        [Authorize(Roles = "IES")]
+        [AuthToken(Roles = "teacher,admin,area,student")]
+        public async Task<IActionResult> Aauth() {
             //https://ssoserviceurl/oauth2/authorize?client_id=APPKEY&openid=OPENID=&service=SERVICE
-            return Ok(new { oauth2, path });
+            var (tmdid, _, _, school) = HttpContext.GetAuthTokenInfo();
+            var client = GetOpenAuthClient(tmdid);
+            string url = client.GetAuthorizationUrl();
+            return Redirect(url);
+        }
+        [HttpGet("authorized")]
+        public async Task<IActionResult> Authorized(string code )
+        {
+            var (tmdid, _, _, school) = HttpContext.GetAuthTokenInfo();
+            if (string.IsNullOrEmpty(code))
+            {
+                return RedirectToAction("Index");
+            }
+            var client = GetOpenAuthClient(tmdid);
+            string schoolId = null;
+            //UserTxtHelper userHelper = new UserTxtHelper(@USER_PATH);
+            //User currentUser = userHelper.GetUserInfoByUserId(client.UserId);
+            //if (currentUser != null)
+            //{
+            //    schoolId = currentUser.SchoolId;
+            //}
+            client.GetAccessTokenByCode(code, schoolId);
+            //未登录已认证学科网用户
+            if (string.IsNullOrEmpty(client.UserId) || "".Equals(client.UserId.Trim()))
+            {
+                return RedirectToAction("Bind", "Demo", new { openId = client.OpenId, userId = client.UserId });
+            }
+            if (string.IsNullOrEmpty(client.OpenId))
+            {
+                string errorMsg = client.ErrorMessage;
+                return RedirectToAction("Bind", "Demo", new { openId = client.OpenId, userId = client.UserId, msg = errorMsg });
+            }
+
+            if (client.IsAuthorized)
+            {
+                //用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", "Demo", new { client.AccessToken,openId = client.OpenId, userId = client.UserId });
+            }
+            else
+            {
+                return RedirectToAction("Index");
+            }
+        }
+        [HttpGet("bind")]
+        public ActionResult Bind(String openId, String userId, String msg)
+        {
+           
+            return Ok();
+        }
+        [HttpGet("unbind")]
+        public ActionResult Unbind(String openId, String userId)
+        {
+            //bool ret = xkwOAuthTxtHelper.UnBindXkw(userId);
+            //string msg = "无解绑关系";
+            //if (ret)
+            //{
+            //    openId = "";
+            //    msg = "解绑成功";
+            //}
+            //ViewBag.OpenId = openId;
+            //ViewBag.UserId = userId;
+            //ViewBag.Message = msg;
+            return Ok();
+        }
+
+
+        /// <summary>
+        /// 退出登录
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("unbind")]
+        public ActionResult Exit()
+        {
+            //HttpCookie uk = new HttpCookie("userId");
+            //uk.Value = "";
+            //uk.Expires = DateTime.Now.AddDays(-10);
+            //Response.Cookies.Set(uk);
+            //return RedirectToAction("Index", "Demo");
+
+            return Ok();
+        }
+
+
+        /// <summary>
+        /// 封装一个方法来初始化OpenAuth客户端
+        /// </summary>
+        /// <returns></returns>
+        private XkwOAuthClient GetOpenAuthClient(string tmdid)
+        {
+            // var accessToken = Session["access_token"] == null ? string.Empty : (string)Session["access_token"];
+
+            string accessToken = "";//应该从别的地方获取 不是mvc 无法从Session 获取 
+            //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 OAuth_Xkw_AppKey = "";//直接从配置文件获取
+            string OAuth_Xkw_AppSecret = "";//直接从配置文件获取
+            string OAuth_Xkw_RedirectUrl = "";//直接从配置文件获取
+            string OAuth_Xkw_OAuthHost = "";//直接从配置文件获取
+            string OAuth_Xkw_ServiceUrl = "";
+            var client = new XkwOAuthClient(OAuth_Xkw_AppKey, OAuth_Xkw_AppSecret, OAuth_Xkw_RedirectUrl, OAuth_Xkw_OAuthHost, accessToken, openId, userId);
+            client.SERVICE_URL = OAuth_Xkw_ServiceUrl;
+            return client;
         }
     }
 }

+ 6 - 11
TEAMModelOS/appsettings.Development.json

@@ -25,7 +25,7 @@
     },
     "Cosmos": {
       //"ConnectionString": "AccountEndpoint=https://teammodel.documents.azure.com:443/;AccountKey=opemBAZi0yATewIlhxDYoIEUqncT5qJh3pUBZsBkTqEkuLYTuu3VS7oaDGJlPp8ASwm5SVSrK2caJsjgmqRw9g==;"
-      "ConnectionString": "AccountEndpoint=https://cdhabookdep-free.documents.azure.cn:443/;AccountKey=JTUVk92Gjsx17L0xqxn0X4wX2thDPMKiw4daeTyV1HzPb6JmBeHdtFY1MF1jdctW1ofgzqkDMFOtcqS46by31A==;"
+     "ConnectionString": "AccountEndpoint=https://cdhabookdep-free.documents.azure.cn:443/;AccountKey=JTUVk92Gjsx17L0xqxn0X4wX2thDPMKiw4daeTyV1HzPb6JmBeHdtFY1MF1jdctW1ofgzqkDMFOtcqS46by31A==;"
       //"ConnectionString": "AccountEndpoint=https://teammodelos.documents.azure.cn:443/;AccountKey=clF73GwPECfP1lKZTCvs8gLMMyCZig1HODFbhDUsarsAURO7TcOjVz6ZFfPqr1HzYrfjCXpMuVD5TlEG5bFGGg==;"
     },
     "Redis": {
@@ -99,16 +99,11 @@
 
     ///学科网测试站
     "xkw": {
-      "appKey": "key808", //学科网分配的appkey
-      "appSecret": "ed4545f513444725bd811e909d3ac79f", //学科网分配的appSecret
-      "service": "http://t.zxxk.com/user/uc", // 学科网的服务,如http://www.zxxk.com/、http://zujuan.xkw.com/等,注意域名后面的斜杠不能少,更多服务可联系客服获取
-      "oauthServerUrl": "https://t.zxxk.com" //学科网Oauth认证平台地址
+      "OAuth_Xkw_AppKey": "key808", //学科网分配的appkey
+      "OAuth_Xkw_AppSecret": "ed4545f513444725bd811e909d3ac79f", //学科网分配的appSecret
+      "OAuth_Xkw_RedirectUrl": "http://kong.sso.com/Demo/Authorized",
+      "OAuth_Xkw_OAuthHost": "https://t.zxxk.com/oauth2/", // 学科网的服务,如http://www.zxxk.com/、http://zujuan.xkw.com/等,注意域名后面的斜杠不能少,更多服务可联系客服获取
+      "OAuth_Xkw_ServiceUrl": "https://t.zxxk.com/user/info" //学科网Oauth认证平台地址
     }
-    //"xkw": {
-    //  "appKey": "key808", //学科网分配的appkey
-    //  "appSecret": "ed4545f513444725bd811e909d3ac79f", //学科网分配的appSecret
-    //  "service": "http://t.zxxk.com/user/uc", // 学科网的服务,如http://www.zxxk.com/、http://zujuan.xkw.com/等,注意域名后面的斜杠不能少,更多服务可联系客服获取
-    //  "oauthServerUrl": "https://t.zxxk.com" //学科网Oauth认证平台地址
-    //}
   }
 }