瀏覽代碼

Merge branch 'develop' of http://52.130.252.100:10000/TEAMMODEL/TEAMModelOS into develop

zhouj1203@hotmail.com 1 年之前
父節點
當前提交
b1c9375f26
共有 84 個文件被更改,包括 2326 次插入1012 次删除
  1. 8 3
      TEAMModelBI/ClientApp/src/api/index.js
  2. 29 0
      TEAMModelBI/ClientApp/src/language/lang/zh-cn.js
  3. 29 0
      TEAMModelBI/ClientApp/src/language/lang/zh-tw.js
  4. 2 1
      TEAMModelBI/ClientApp/src/until/common.js
  5. 179 77
      TEAMModelBI/ClientApp/src/view/htcommunity/adminpanel.vue
  6. 2 2
      TEAMModelBI/ClientApp/src/view/product/index.vue
  7. 63 53
      TEAMModelBI/Controllers/BISchool/SchoolController.cs
  8. 14 15
      TEAMModelBI/Controllers/BITmid/TmidController.cs
  9. 23 0
      TEAMModelBI/Models/AssistSchool.cs
  10. 41 0
      TEAMModelBI/Models/Purchase.cs
  11. 3 3
      TEAMModelBI/TEAMModelBI.csproj
  12. 6 6
      TEAMModelContest/contest.client/src/locale/lang/en-US.js
  13. 6 6
      TEAMModelContest/contest.client/src/locale/lang/zh-TW.js
  14. 1 1
      TEAMModelOS.FunctionV4/CosmosDB/CommonTrigger.cs
  15. 6 1
      TEAMModelOS.FunctionV4/CosmosDB/TriggerExam.cs
  16. 4 0
      TEAMModelOS.FunctionV4/CosmosDB/TriggerHomework.cs
  17. 3 3
      TEAMModelOS.FunctionV4/TEAMModelOS.FunctionV4.csproj
  18. 1 1
      TEAMModelOS.SDK/DI/CoreAPI/CoreAPIHttpService.cs
  19. 49 2
      TEAMModelOS.SDK/DI/Mail/MailFactory.cs
  20. 3 2
      TEAMModelOS.SDK/Models/Cosmos/Common/ItemInfo.cs
  21. 8 0
      TEAMModelOS.SDK/Models/Cosmos/School/Paper.cs
  22. 156 1
      TEAMModelOS.SDK/Models/Service/HomeworkService.cs
  23. 598 13
      TEAMModelOS.SDK/Models/Service/SystemService.cs
  24. 3 3
      TEAMModelOS.SDK/TEAMModelOS.SDK.csproj
  25. 2 1
      TEAMModelOS/ClientApp/public/index.html
  26. 6 1
      TEAMModelOS/ClientApp/public/lang/en-US.js
  27. 6 1
      TEAMModelOS/ClientApp/public/lang/zh-CN.js
  28. 6 1
      TEAMModelOS/ClientApp/public/lang/zh-TW.js
  29. 7 0
      TEAMModelOS/ClientApp/src/api/studentWeb.js
  30. 26 3
      TEAMModelOS/ClientApp/src/assets/iconfont/demo_index.html
  31. 7 3
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.css
  32. 1 1
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.js
  33. 7 0
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.json
  34. 二進制
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.ttf
  35. 二進制
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff
  36. 二進制
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff2
  37. 7 3
      TEAMModelOS/ClientApp/src/common/BaseCanvas.vue
  38. 1 1
      TEAMModelOS/ClientApp/src/common/BaseNotification.vue
  39. 2 2
      TEAMModelOS/ClientApp/src/common/BaseQuickPaper.vue
  40. 1 1
      TEAMModelOS/ClientApp/src/components/evaluation/ExerciseList.less
  41. 3 0
      TEAMModelOS/ClientApp/src/components/evaluation/ExerciseList.vue
  42. 1 1
      TEAMModelOS/ClientApp/src/components/selflearn/ExerciseList.less
  43. 3 0
      TEAMModelOS/ClientApp/src/components/selflearn/ExerciseList.vue
  44. 6 2
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperViewBox/LessonTestReport.vue
  45. 48 22
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperViewBox/PaperTest.less
  46. 229 205
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperViewBox/PaperTest.vue
  47. 7 7
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/composePaper.vue
  48. 1 0
      TEAMModelOS/ClientApp/src/components/student-web/HomeView/HomeView.less
  49. 59 8
      TEAMModelOS/ClientApp/src/components/student-web/HomeView/HomeView.vue
  50. 9 1
      TEAMModelOS/ClientApp/src/components/student-web/WrongQusetion/AnswerBox.vue
  51. 5 1
      TEAMModelOS/ClientApp/src/components/student-web/WrongQusetion/QuesList.vue
  52. 7 0
      TEAMModelOS/ClientApp/src/store/module/studentWeb.js
  53. 8 1
      TEAMModelOS/ClientApp/src/view/classrecord/eventchart/Cowork.vue
  54. 4 1
      TEAMModelOS/ClientApp/src/view/evaluation/bank/ExerciseList.vue
  55. 4 0
      TEAMModelOS/ClientApp/src/view/evaluation/bank/index.vue
  56. 116 142
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseCreateChild.vue
  57. 4 0
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseExerciseList.vue
  58. 1 1
      TEAMModelOS/ClientApp/src/view/evaluation/index/CommonExercise.less
  59. 1 0
      TEAMModelOS/ClientApp/src/view/evaluation/index/CreateExercises.less
  60. 1 1
      TEAMModelOS/ClientApp/src/view/evaluation/index/CreatePaper.vue
  61. 9 1
      TEAMModelOS/ClientApp/src/view/evaluation/index/DfPage.vue
  62. 3 1
      TEAMModelOS/ClientApp/src/view/evaluation/index/TestPaper.vue
  63. 190 188
      TEAMModelOS/ClientApp/src/view/evaluation/types/BaseMultiple.vue
  64. 22 18
      TEAMModelOS/ClientApp/src/view/evaluation/types/BaseSingle.vue
  65. 11 2
      TEAMModelOS/ClientApp/src/view/learnactivity/ByQuMark.vue
  66. 10 1
      TEAMModelOS/ClientApp/src/view/learnactivity/ByStuMark.vue
  67. 5 1
      TEAMModelOS/ClientApp/src/view/learnactivity/StuReport.vue
  68. 12 3
      TEAMModelOS/ClientApp/src/view/learnactivity/byStu/ByStuMark.vue
  69. 2 1
      TEAMModelOS/ClientApp/src/view/learnactivity/tabs/AnswerTable.vue
  70. 2 1
      TEAMModelOS/ClientApp/src/view/login/Index.vue
  71. 1 1
      TEAMModelOS/ClientApp/src/view/newsheet/BaseEditor.vue
  72. 1 1
      TEAMModelOS/ClientApp/src/view/newsheet/SheetBaseInfo.vue
  73. 1 1
      TEAMModelOS/ClientApp/src/view/newsheet/SheetObjective.vue
  74. 34 5
      TEAMModelOS/ClientApp/src/view/student-web/AppNew.vue
  75. 3 3
      TEAMModelOS/Controllers/Both/PaperController.cs
  76. 2 0
      TEAMModelOS/Controllers/Both/PersonalSettingController.cs
  77. 2 141
      TEAMModelOS/Controllers/Common/HomeworkController.cs
  78. 3 3
      TEAMModelOS/Controllers/Third/IRS/ThirdIRSController.cs
  79. 2 0
      TEAMModelOS/Controllers/Third/Moofen/MoofenController.cs
  80. 168 29
      TEAMModelOS/Controllers/XTest/TestController.cs
  81. 4 0
      TEAMModelOS/Lang/zh-cn.json
  82. 4 4
      TEAMModelOS/TEAMModelOS.csproj
  83. 1 2
      TEAMModelOS/appsettings.Development.json
  84. 1 1
      TEAMModelOS/appsettings.json

+ 8 - 3
TEAMModelBI/ClientApp/src/api/index.js

@@ -611,12 +611,17 @@ export default {
     pushNotify(data) {
         return post('/coupon/push-notify',data)
     },
-    //統購平台 - 縣市名額設定
-    setPurchaseSeats(data) {
-        return post('https://bb-rc.teammodel.net/bbauthapi/set-purchase-seats', data)
+    //統購平台 - 縣市名額設定 ※目前只開放GL站 
+    setPurchaseSeats(data, testflg=false) {
+        let Domain = (testflg) ? 'bb-rc.teammodel.net' : 'bb.teammodel.net';
+        return post(`https://${Domain}/bbauthapi/set-purchase-seats`, data)
     },
     //統購平台 - 取得統購平台列表
     getBBPurchase(data) {
         return post('/schoolcheck/get-bbpurchase',data)
     },
+    //統購平台 - 取得縣市學校
+    getCsSchoolByGeo(data) {
+        return post('/schoolcheck/get-school-geo', data)
+    },
 }

+ 29 - 0
TEAMModelBI/ClientApp/src/language/lang/zh-cn.js

@@ -603,6 +603,35 @@ const zh_cn = {
         },
         parameterError: '参数错误',
     },
+    purchase: {
+        purchasePlatform: '统购平台',
+        productManagement: '县市产品名额',
+        prodSeatsInfo: '产品及名额资讯',
+        addNew: '新增',
+        cityOrDistrict: '县市',
+        selectCityOrDistrict: '请选择县市',
+        prodName: '产品名称',
+        selectProd: '请选择产品',
+        space: '空间',
+        inputSpace: '请输入容量',
+        unit: '单位',
+        expirationDate: '使用期限',
+        startDate: '开始时间',
+        endDate: '结束时间',
+        quota: '名额',
+        inputQuota: '请输入名额',
+        school: '学校',
+        schoolName: '学校名称',
+        inputNumber: '请输入数字',
+        receiveNumber: '领取人数',
+        operate: '操作',
+        remove: '移除',
+        selectSchool: '请选择学校',
+        confirmOk: '确定',
+        cancel: '取消',
+        addSchool: '追加学校',
+        extension: '扩充项',
+    },
     auth:{
         YMPCVCIM: '学情分析模组',
         IPDYZYLC: '智慧学校管理服务',

+ 29 - 0
TEAMModelBI/ClientApp/src/language/lang/zh-tw.js

@@ -599,6 +599,35 @@ const zh_tw = {
         },
         parameterError: '參數錯誤',
     },
+    purchase: {
+        purchasePlatform: '統購平台',
+        productManagement: '縣市產品名額',
+        prodSeatsInfo: '產品及名額資訊',
+        addNew: '新增',
+        cityOrDistrict: '縣市',
+        selectCityOrDistrict: '請選擇縣市',
+        prodName: '產品名稱',
+        selectProd: '請選擇產品',
+        space: '空間',
+        inputSpace: '請輸入容量',
+        unit: '單位',
+        expirationDate: '使用期限',
+        startDate: '開始時間',
+        endDate: '結束時間',
+        quota: '名額',
+        inputQuota: '請輸入名額',
+        school: '學校',
+        schoolName: '學校名稱',
+        inputNumber: '請輸入數字',
+        receiveNumber: '領取人數',
+        operate: '操作',
+        remove: '移除',
+        selectSchool: '請選擇學校',
+        confirmOk: '確定',
+        cancel: '取消',
+        addSchool: '追加學校',
+        extension: '擴充項',
+    },
     auth:{
         YMPCVCIM: '學情分析模組',
         IPDYZYLC: '智慧學校管理服務',

+ 2 - 1
TEAMModelBI/ClientApp/src/until/common.js

@@ -74,10 +74,11 @@ export default {
         });
         return totaldays
     },
-    timestampToTime(timestamp, state) {
+    timestampToTime(timestamp, state, isUtc=false) {
         if (timestamp === 0) { return 0 }
         let multiple = timestamp.toString().length === 13 ? 1 : 1000
         var date = new Date(Number(timestamp) * multiple); //时间戳为10位需*1000,时间戳为13位的话不需乘1000
+        if (isUtc) date = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
         var Y = date.getFullYear() + '-';
         var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
         var D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + '   ';

File diff suppressed because it is too large
+ 179 - 77
TEAMModelBI/ClientApp/src/view/htcommunity/adminpanel.vue


+ 2 - 2
TEAMModelBI/ClientApp/src/view/product/index.vue

@@ -466,8 +466,8 @@ let columns = ref([
 ])
 const popoverRef = ref()
 const siteValue = window.location.host === 'localhost:5001' ? 'cn' : window.location.host === 'bi.teammodel.cn' ? 'cn' : window.location.host === 'bitest.teammodel.cn' ? 'cn' : 'international'
-//const optionsData = siteValue === 'cn' ? option_cn : option_gl
-const optionsData = option_gl
+const optionsData = siteValue === 'cn' ? option_cn : option_gl
+//const optionsData = option_gl
 columns.value[0].headerCellRenderer = (props = HeaderCellSlotProps) => {
       return (
         <div class="items-center justify-center">

+ 63 - 53
TEAMModelBI/Controllers/BISchool/SchoolController.cs

@@ -2619,73 +2619,83 @@ namespace TEAMModelBI.Controllers.BISchool
             List<object> result = new List<object>();
             try
             {
-                //var cosmosClientCsv2 = _azureCosmos.GetCosmosClient(name: "CoreServiceV2"); //CosmosDB CSV2
-                //var storageClientCsv2 = _azureStorage.GetCloudTableClient(name: "CoreServiceV2"); //Storage CSV2
-
-                ////取得TMID基本資料
-                //Dictionary<string, TmidStics> tmidDic = new();
-                //QueryDefinition query =
-                //       new QueryDefinition(@"SELECT c.id, c.name, c.picture, c.mobile, c.mail, c.lang, c.wechat, c.facebook, c.google, c.ding, c.apple, c.educloudtw, c.ts FROM c WHERE (ARRAY_CONTAINS(@key, c.id) OR ARRAY_CONTAINS(@key, c.mobile))")
-                //       .WithParameter("@key", tmids);
-                //await foreach (var item in cosmosClientCsv2
-                //            .GetContainer("Core", "ID2")
-                //            .GetItemQueryStreamIterator(query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("base") }))
-                //{                   
-                //}
-
-                string sql = $"SELECT * FROM c WHERE c.dataType = 'purchase' ";
-
-                var client = _azureCosmos.GetCosmosClient(name: "CoreServiceV2");
-
+                var client = _azureCosmos.GetCosmosClient(name:"CoreServiceV2");
+                string sqlPurchase = $"SELECT * FROM c ";
                 List<PurchaseSeats> purchaseSeatsList = new List<PurchaseSeats>();
-
-                //await foreach (var item in client.GetContainer("Core", "ID2").GetItemQueryStreamIterator(queryText: sql))
-                //{
-                //    var jsoncs = await JsonDocument.ParseAsync(item.ContentStream);
-                //    if (jsoncs.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
-                //    {
-                //        foreach (var obj in jsoncs.RootElement.GetProperty("Documents").EnumerateArray())
-                //        {
-
-                //        }
-                //    }
-                //}
-                //await foreach (var item in client.GetContainer("Core", "ID2").GetItemQueryIterator<PurchaseSeats>(queryText: sql))
-                await foreach (var item in client.GetContainer("Habb", "Auth").GetItemQueryIterator<PurchaseSeats>(queryText: sql))
+                await foreach (PurchaseSeats item in client.GetContainer("Habb", "Auth").GetItemQueryIterator<PurchaseSeats>(queryText: sqlPurchase, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("purchase") }))
                 {
                     purchaseSeatsList.Add(item);
                 }
+                Dictionary<string, List<PurchaseSchoolSeats>> purchaseSchoolSeatsDic = new Dictionary<string, List<PurchaseSchoolSeats>>();
+                string sqlSchool = $"SELECT * FROM c ";
+                await foreach (PurchaseSchoolSeats item in client.GetContainer("Habb", "Auth").GetItemQueryIterator<PurchaseSchoolSeats>(queryText: sqlSchool, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("purchaseSchool") }))
+                {
+                    if(!purchaseSchoolSeatsDic.ContainsKey(item.purchaseId))
+                    {
+                        purchaseSchoolSeatsDic.Add(item.purchaseId, new List<PurchaseSchoolSeats>() { item });
+                    }
+                    else
+                    {
+                        purchaseSchoolSeatsDic[item.purchaseId].Add(item);
+                    }
+                }
 
-
-
-                return Ok(purchaseSeatsList);
+                return Ok(new { geo = purchaseSeatsList, school = purchaseSchoolSeatsDic });
             }
             catch (Exception ex)
             {
                 return BadRequest();
             }
         }
-
         /// <summary>
-        /// 統購授權 縣市名額
+        /// 取得CoreService縣市所屬學校
         /// </summary>
-        public class PurchaseSeats
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("get-school-geo")]
+        public async Task<IActionResult> GetCsSchoolByGeo(JsonElement jsonElement)
         {
-            public string id { get; set; } //統購縣市名額ID 格式:{countryId}-{provinceId}-{cityId}-{districtId}-{產品代碼}
-            public string countryId { get; set; } //[String]國家代碼
-            public string provinceId { get; set; } //[String]省代碼
-            public string cityId { get; set; } //[String]市代碼
-            public string districtId { get; set; } //[String]區代碼
-            public string geoName { get; set; } //行政區名稱
-            public string prodCode { get; set; } //產品代碼
-            public long startDate { get; set; } //授權起始日期 unixtimestamp
-            public long endDate { get; set; } //授權終止日期 unixtimestamp
-            public int seats { get; set; } //名額數
-            public int number { get; set; } //產品數量、空間數
-            public int used { get; set; } //已領用名額數
-            public object aprule { get; set; } //產品擴充項
-            public List<string> tmids { get; set; } = new(); //已申請的TMID
-            public string dataType { get; set; } //分區鍵:purchase
+            List<CsSchoolBase> result = new List<CsSchoolBase>();
+            try
+            {
+                string err = string.Empty;
+                string countryId = (jsonElement.TryGetProperty("countryId", out JsonElement _countryId)) ? _countryId.GetString() : string.Empty;
+                string provinceId = (jsonElement.TryGetProperty("provinceId", out JsonElement _provinceId)) ? _provinceId.GetString() : string.Empty;
+                string cityId = (jsonElement.TryGetProperty("cityId", out JsonElement _cityId)) ? _cityId.GetString() : string.Empty;
+                string distId = (jsonElement.TryGetProperty("distId", out JsonElement _distId)) ? _distId.GetString() : string.Empty;
+                //輸入項檢驗
+                if (string.IsNullOrWhiteSpace(countryId)) { err = "Invalid countryId."; }
+                else if(countryId.Equals("CN"))
+                {
+                    if (string.IsNullOrWhiteSpace(provinceId)) { err = "Invalid provinceId."; }
+                    else if (string.IsNullOrWhiteSpace(cityId)) { err = "Invalid cityId."; }
+                }
+                else if (countryId.Equals("TW"))
+                {
+                    if (string.IsNullOrWhiteSpace(cityId)) { err = "Invalid cityId."; }
+                }
+                if(!string.IsNullOrWhiteSpace(err)) ///錯誤訊息輸出
+                {
+                    return Ok(new { err = err, data = result });
+                }
+                //取得CS學校
+                var client = _azureCosmos.GetCosmosClient(name:"CoreServiceV2CnRead");
+                string sql = $"SELECT * FROM c WHERE c.countryId = '{countryId}'";
+                if (!string.IsNullOrWhiteSpace(provinceId)) sql += $" AND c.provinceId = '{provinceId}'";
+                if (!string.IsNullOrWhiteSpace(cityId)) sql += $" AND c.cityId = '{cityId}'";
+                if (!string.IsNullOrWhiteSpace(distId)) sql += $" AND c.distId = '{distId}'";
+                await foreach (CsSchoolBase item in client.GetContainer("Core", "School").GetItemQueryIterator<CsSchoolBase>(queryText: sql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("base") }))
+                {
+                    result.Add(item);
+                }
+
+                return Ok(new { err = err, data = result });
+            }
+            catch (Exception ex)
+            {
+                return BadRequest();
+            }
         }
 
         #endregion

+ 14 - 15
TEAMModelBI/Controllers/BITmid/TmidController.cs

@@ -748,7 +748,7 @@ namespace TEAMModelBI.Controllers.BITmid
         private async Task<object> GetTmidUseprodCore(string dateFromStr, string dateToStr, List<string> targets, bool getLoginTime)
         {
             Dictionary<string, TmidStics> tmidDic = new();
-            HashSet<string> schIds = new HashSet<string>();
+            HashSet<string> schIds = new HashSet<string>(); //生成的TMID所關聯的學校ID列表
             DateTime dateFrom = DateTime.ParseExact(dateFromStr, "yyyy-MM-dd", null);
             DateTime dateTo = DateTime.ParseExact(dateToStr, "yyyy-MM-dd", null).Add(new TimeSpan(0, 23, 59, 59));
             DateTimeOffset dateTimeFrom = new DateTimeOffset(dateFrom, TimeSpan.Zero);
@@ -908,7 +908,7 @@ namespace TEAMModelBI.Controllers.BITmid
 
             //返回值整理
             List<TmidIotProdCnt> tmid = new List<TmidIotProdCnt>();
-            List<TmidIotProdCntGeo> sch = new List<TmidIotProdCntGeo>();
+            List<TmidIotProdCntGeo> sch = new List<TmidIotProdCntGeo>(); //取得的IOT學校統計資訊
             foreach (var item in tmidDic)
             {
                 //TMID
@@ -1036,20 +1036,19 @@ namespace TEAMModelBI.Controllers.BITmid
                 {
                     schRow.tLesson += item.tLesson;
                     schRow.tGreen += item.tGreen;
+                    //地理位置IOT累加
+                    string countryId = (!string.IsNullOrWhiteSpace(schRow.countryId)) ? schRow.countryId : string.Empty;
+                    string provinceId = (!string.IsNullOrWhiteSpace(schRow.provinceId)) ? schRow.provinceId : string.Empty;
+                    string cityId = (!string.IsNullOrWhiteSpace(schRow.cityId)) ? schRow.cityId : string.Empty;
+                    string distId = (!string.IsNullOrWhiteSpace(schRow.distId)) ? schRow.distId : string.Empty;
+                    string geoid = $"{countryId}-{provinceId}-{cityId}-{distId}";
+                    var geoRow = geo.Where(s => s.id.Equals(geoid)).FirstOrDefault();
+                    if (geoRow != null)
+                    {
+                        geoRow.tLesson += item.tLesson;
+                        geoRow.tGreen += item.tGreen;
+                    }
                 }
-                //地理位置IOT累加
-                string countryId = (!string.IsNullOrWhiteSpace(schRow.countryId)) ? schRow.countryId : string.Empty;
-                string provinceId = (!string.IsNullOrWhiteSpace(schRow.provinceId)) ? schRow.provinceId : string.Empty;
-                string cityId = (!string.IsNullOrWhiteSpace(schRow.cityId)) ? schRow.cityId : string.Empty;
-                string distId = (!string.IsNullOrWhiteSpace(schRow.distId)) ? schRow.distId : string.Empty;
-                string geoid = $"{countryId}-{provinceId}-{cityId}-{distId}";
-                var geoRow = geo.Where(s => s.id.Equals(geoid)).FirstOrDefault();
-                if (geoRow != null)
-                {
-                    geoRow.tLesson += item.tLesson;
-                    geoRow.tGreen += item.tGreen;
-                }
-
             }
             //總IOT累加
             Dictionary<string, int> iotResult = new Dictionary<string, int>();

+ 23 - 0
TEAMModelBI/Models/AssistSchool.cs

@@ -131,6 +131,29 @@ namespace TEAMModelBI.Models
     {
         public string areaId { get; set; }
     }
+    //CoreService學校基本資料
+    public class CsSchoolBase
+    {
+        public string id { get; set; }
+        public string code { get; set; } //學校教育部代碼
+        public string name { get; set; }
+        public string countryId { get; set; }
+        public string countryName { get; set; }
+        public string provinceId { get; set; }
+        public string provinceName { get; set; }
+        public string cityId { get; set; }
+        public string cityName { get; set; }
+        public string distId { get; set; }
+        public string distName { get; set; }
+        public string address { get; set; }
+        public string source { get; set; }
+        public string shortCode { get; set; } //學校簡碼
+        public string lang { get; set; }
+        public string unitType { get; set; }
+        public string tel { get; set; }
+        public string type { get; set; }
+    }
+
     #region api解析
     /// <summary>
     /// api解析

+ 41 - 0
TEAMModelBI/Models/Purchase.cs

@@ -0,0 +1,41 @@
+using System.Collections.Generic;
+
+namespace TEAMModelBI.Models
+{
+    /// <summary>
+    /// 統購授權 縣市名額
+    /// </summary>
+    public class PurchaseSeats
+    {
+        public string id { get; set; } //統購縣市名額ID 格式:{countryId}-{provinceId}-{cityId}-{districtId}-{產品代碼}
+        public string countryId { get; set; } //[String]國家代碼
+        public string provinceId { get; set; } //[String]省代碼
+        public string cityId { get; set; } //[String]市代碼
+        public string districtId { get; set; } //[String]區代碼
+        public string geoName { get; set; } //行政區名稱
+        public string prodCode { get; set; } //產品代碼
+        public long startDate { get; set; } //授權起始日期 unixtimestamp
+        public long endDate { get; set; } //授權終止日期 unixtimestamp
+        public int seats { get; set; } //名額數
+        public int number { get; set; } //產品數量、空間數
+        public int used { get; set; } //已領用名額數 (document並無此欄位,各校名額加總累計用)
+        public object aprule { get; set; } //產品擴充項
+        public string dataType { get; set; } //分區鍵:purchase
+    }
+    /// <summary>
+    /// 統購授權 各校名額
+    /// </summary>
+    public class PurchaseSchoolSeats
+    {
+        public string schoolCode { get; set; }
+        public string shortCode { get; set; }
+        public string schoolName { get; set; }
+        public int seats { get; set; }
+        public string id { get; set; } //統購學校名額ID 格式:{countryId}-{provinceId}-{cityId}-{districtId}-{產品代碼}-{學校簡碼}
+        public string purchaseId { get; set; } //統購縣市名額ID 格式:{countryId}-{provinceId}-{cityId}-{districtId}-{產品代碼}
+        public List<string> tmids { get; set; } = new List<string>(); //已申請的TMID
+        public int used { get; set; } //已領用名額數
+        public int ttl { get; set; } = -1;
+        public string dataType { get; set; } //分區鍵:purchaseSchool
+    }
+}

+ 3 - 3
TEAMModelBI/TEAMModelBI.csproj

@@ -65,9 +65,9 @@
 		<SpaRoot>ClientApp\</SpaRoot>
 		<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
 		<UserSecretsId>078b5d89-7d90-4f6a-88fc-7d96025990a8</UserSecretsId>
-		<Version>5.2405.29</Version>
-		<AssemblyVersion>5.2405.29.1</AssemblyVersion>
-		<FileVersion>5.2405.29.1</FileVersion>
+		<Version>5.2406.5</Version>
+		<AssemblyVersion>5.2406.5.1</AssemblyVersion>
+		<FileVersion>5.2406.5.1</FileVersion>
 		<Description>TEAMModelBI(BI)</Description>
 		<PackageReleaseNotes>BI版本说明版本切换标记2022000908</PackageReleaseNotes>
 		<PackageId>TEAMModelBI</PackageId>

+ 6 - 6
TEAMModelContest/contest.client/src/locale/lang/en-US.js

@@ -126,13 +126,13 @@ const enUS = {
         save: "Save",
     },
     placeholderlist: {
-        searchActName: "搜索活动名称",
-        searchTeam: "搜索团队信息",
-        input: "请输入",
+        searchActName: "Search activity name",
+        searchTeam: "Search team information",
+        input: "Please input",
         select: "Please select",
-        teamIdentity: "请选择您在团队赛中的身份",
-        choiceYear: "选择年份",
-        scoring: "请打分",
+        teamIdentity: "Please select your role in the team",
+        choiceYear: "Select year",
+        scoring: "Please rate",
     },
     elMessage: {
         logout: "已退出登录",

+ 6 - 6
TEAMModelContest/contest.client/src/locale/lang/zh-TW.js

@@ -126,13 +126,13 @@ const zhTW = {
         save: "儲存",
     },
     placeholderlist: {
-        searchActName: "搜索活动名称",
-        searchTeam: "搜索团队信息",
-        input: "请输入",
+        searchActName: "搜尋活動名稱",
+        searchTeam: "搜尋團隊資訊",
+        input: "請輸入",
         select: "請選擇",
-        teamIdentity: "请选择您在团队赛中的身份",
-        choiceYear: "选择年份",
-        scoring: "请打分",
+        teamIdentity: "請選擇您在團隊賽中的身分",
+        choiceYear: "選擇年份",
+        scoring: "請打分數",
     },
     elMessage: {
         logout: "已退出登录",

+ 1 - 1
TEAMModelOS.FunctionV4/CosmosDB/CommonTrigger.cs

@@ -85,7 +85,7 @@ namespace TEAMModelOS.FunctionV4
                                 switch (data.pk)
                                 {
                                     case "Exam":
-                                        await TriggerExam.Trigger(_coreAPIHttpService, _azureCosmos, _serviceBus, _azureStorage, _dingDing, client, element, data, _httpClient, _configuration,_httpTrigger);
+                                        await TriggerExam.Trigger(_coreAPIHttpService, _azureCosmos, _serviceBus, _azureStorage, _dingDing, client, element, data, _httpClient, _configuration,_httpTrigger,_azureRedis);
                                         break;
                                     case "Vote":
                                         await TriggerVote.Trigger(_coreAPIHttpService, _serviceBus, _azureStorage, _dingDing, client, element, data, _azureRedis, _configuration);

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

@@ -43,7 +43,7 @@ namespace TEAMModelOS.FunctionV4
     public class TriggerExam
     {
         public static async Task Trigger(CoreAPIHttpService _coreAPIHttpService, AzureCosmosFactory _azureCosmos, AzureServiceBusFactory _serviceBus, AzureStorageFactory _azureStorage, DingDing _dingDing,
-            CosmosClient client, JsonElement input, TriggerData data, IHttpClientFactory _httpClient, IConfiguration _configuration, HttpTrigger _httpTrigger)
+            CosmosClient client, JsonElement input, TriggerData data, IHttpClientFactory _httpClient, IConfiguration _configuration, HttpTrigger _httpTrigger,AzureRedisFactory _azureRedis)
         {
 
             List<ExamClassResult> examClassResults = new();
@@ -183,6 +183,11 @@ namespace TEAMModelOS.FunctionV4
                                 //await _serviceBus.GetServiceBusClient().SendMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), messageBlob);
                                 #endregion
                                 //List<string> classes = new List<string>();
+                                if (info!=null && info.scope.Equals("private"))
+                                {
+                                    await SystemService.RecordAccumulateData(_azureRedis, _dingDing, new SDK.Models.Dtos.Accumulate { client="web", count=1, id=info.id, key="exam-going", name=info.name, scope="teacher", target=info.creatorId });
+                                }
+
                                 //处理活动中间件
                                 List<RGroupList> members = await Activity(_coreAPIHttpService, info, classes, client, _dingDing, sub, examClassResults);
                                 if (examClassResults.Count == 0)

+ 4 - 0
TEAMModelOS.FunctionV4/CosmosDB/TriggerHomework.cs

@@ -132,6 +132,10 @@ namespace TEAMModelOS.FunctionV4
                                     };
                                     await table.Save<ChangeRecord>(changeRecord);
                                 }
+                                if (work!=null && work.scope.Equals("private"))
+                                {
+                                    await SystemService.RecordAccumulateData(_azureRedis, _dingDing, new SDK.Models.Dtos.Accumulate { client="web", count=1, id=work.id, key="homework-going", name=work.name, scope="teacher", target=work.creatorId });
+                                }
                             } catch (Exception e) {
                                 await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}研修作业活动going{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
                             }

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

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

+ 1 - 1
TEAMModelOS.SDK/DI/CoreAPI/CoreAPIHttpService.cs

@@ -284,7 +284,7 @@ lang 语系"zh-cn" ,zh-tw", "en-us"
         /// <param name="_configuration"></param>
         /// <param name="_dingDing"></param>
         /// <returns></returns>
-        public       void PushNotify(List<IdNameCode> toTeachers ,  string notifyCode,string notifyType,Dictionary<string, object> replaceData, 
+        public        void PushNotify(List<IdNameCode> toTeachers ,  string notifyCode,string notifyType,Dictionary<string, object> replaceData, 
             string location, IConfiguration _configuration, DI.DingDing _dingDing,string rootPath) {
             /*
              * IES5_Management  shift-assist_school  DelBeforeCopyAbility-mark_start  copyAbility-mark_finish  copyAbility-mark_start 

+ 49 - 2
TEAMModelOS.SDK/DI/Mail/MailFactory.cs

@@ -113,8 +113,55 @@ namespace TEAMModelOS.SDK.DI.Mail
             else {
                 return null; 
             }
-            
-           
+        }
+
+        public static string SendEmail(this SmtpClient smtpClient, AzureCosmosFactory azureCosmos, DingDing dingDing, string eventId,  string subject, string content, string toMail, string tmdid, string tmdname, string sender = "醍摩豆客服助手")
+        {
+            if (smtpClient!=null)
+            {
+                NetworkCredential networkCredential = (NetworkCredential)smtpClient.Credentials!;
+                MailMessage mailMessage = new MailMessage(new MailAddress(networkCredential.UserName, sender), new MailAddress(toMail, $"{tmdname}({tmdid})"));
+                mailMessage.Subject = subject;
+                mailMessage.Body = content;
+                mailMessage.IsBodyHtml = true;
+                smtpClient.SendCompleted+=async (sender, e) =>
+                {
+                    int result = -1;
+                    string userToken = $"{e.UserState}";
+                    if (e.Cancelled)
+                    {
+                        result=0;
+                        //Console.WriteLine("Email sending was canceled.");
+                    }
+                    else if (e.Error != null)
+                    {
+                        result=2;
+                        //Console.WriteLine("Error while sending email: " + e.Error.ToString());
+                    }
+                    else
+                    {
+                        result=3;
+                        //Console.WriteLine("Email was sent successfully.");
+                    }
+                    //try
+                    //{
+                    //    TMDOrder order = await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).ReadItemAsync<TMDOrder>(userToken, new Azure.Cosmos.PartitionKey("TMDOrder"));
+                    //    order.invoiceNotify=result;
+                    //    await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).ReplaceItemAsync<TMDOrder>(order, userToken, new Azure.Cosmos.PartitionKey("TMDOrder"));
+                    //}
+                    //catch (Exception ex)
+                    //{
+                    //    await dingDing.SendBotMsg($"邮箱发送异常,订单ID:{userToken}\n{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
+                    //}
+                };
+                string token = eventId;
+                smtpClient.SendAsync(mailMessage, token);
+                return token;
+            }
+            else
+            {
+                return null;
+            }
         }
         // 处理 SendCompleted 事件
         private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e)

+ 3 - 2
TEAMModelOS.SDK/Models/Cosmos/Common/ItemInfo.cs

@@ -22,9 +22,9 @@ namespace TEAMModelOS.SDK.Models
         [Required(ErrorMessage = "{0} 必须填写")]
         public string question { get; set; }
         // 选项 单选 多选 判断
-        public List<CodeValue> option { get; set; }
+        public List<CodeValue> option { get; set; }= new List<CodeValue>();
       
-        public List<ItemInfo> children { get; set; }
+        public List<ItemInfo> children { get; set; }= new List<ItemInfo>();
         
         //使用次数
         public int useCount { get; set; }
@@ -38,5 +38,6 @@ namespace TEAMModelOS.SDK.Models
         public string grade { get; set; }
         public string description {  get; set; }
         public new List<MoofenItemInfo> children { get; set; }=new List<MoofenItemInfo>();
+        public new  List<CodeValue> option { get; set; } = new List<CodeValue>();
     }
 }

+ 8 - 0
TEAMModelOS.SDK/Models/Cosmos/School/Paper.cs

@@ -121,6 +121,14 @@ namespace TEAMModelOS.SDK.Models
         /// 附件图片
         /// </summary>
         public List<string> attachments { get; set; } = new List<string>();
+        /// <summary>
+        /// 创建人的id
+        /// </summary>
+        public string creatorId { get; set; }
+        /// <summary>
+        /// 0不保密,1 保密
+        /// </summary>
+        public int secret { get; set; }
     }
      /// <summary>
      /// 阅卷规则

+ 156 - 1
TEAMModelOS.SDK/Models/Service/HomeworkService.cs

@@ -1,19 +1,136 @@
 using Azure.Cosmos;
 using Azure.Messaging.ServiceBus;
+using HTEXLib.COMM.Helpers;
 using Microsoft.Extensions.Configuration;
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using System.Text;
 using System.Text.Json;
 using System.Threading.Tasks;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK.Models;
 using TEAMModelOS.SDK.Services;
 
-namespace TEAMModelOS.SDK.Models.Service
+namespace TEAMModelOS.SDK
 {
     public static class HomeworkService
     {
+        public static async Task<List<HomeworkUser>>  AnswerRecordAll(CosmosClient client, CoreAPIHttpService _coreAPIHttpService,DingDing  _dingDing, Homework homework, string userid, string tbname,List<string> listIds , string _targetType= "student")
+        {
+            List<TypeUser> typeUsers = new List<TypeUser>();
+            (List<RMember> tmdinfos, List<RGroupList> classInfo) = await GroupListService.GetMemberByListids(_coreAPIHttpService, client, _dingDing, listIds, homework.school, null, -1, homework.startTime);
+            var addStudentsCls = tmdinfos.FindAll(x => x.type == 2);
+            var addTmdidsCls = tmdinfos.FindAll(x => x.type == 1);
+            if ($"{_targetType}".Equals("research", StringComparison.OrdinalIgnoreCase) || $"{_targetType}".Equals("yxtrain", StringComparison.OrdinalIgnoreCase))
+            {
+
+
+                if (tmdinfos.IsNotEmpty())
+                {
+
+                    tmdinfos.ForEach(x => {
+                        var clases = classInfo.Where(y => y.members.Select(z => z.id).Contains(x.id)).ToList();
+                        typeUsers.Add(new TypeUser() { irs=x.irs, no=x.no, nickname =x.nickname, userid = x.id, userType = "tmdid", username = x.name, classes = clases.Select(x => new TypeUserClass { id = x.id, name = x.name }).ToList() });
+                    });
+                }
+            }
+            else
+            {
+                if (addTmdidsCls.IsNotEmpty())
+                {
+                    addTmdidsCls.ForEach(x => {
+                        var clases = classInfo.Where(y => y.members.Select(z => z.id).Contains(x.id)).ToList();
+                        typeUsers.Add(new TypeUser() { irs = x.irs, no = x.no, nickname = x.nickname, userid = x.id, userType = "tmdid", username = x.name, classes = clases.Select(x => new TypeUserClass { id = x.id, name = x.name }).ToList() });
+                    });
+                }
+                if (addStudentsCls.IsNotEmpty())
+                {
+                    addStudentsCls.ForEach(x => {
+                        var clases = classInfo.Where(y => y.members.Select(z => z.id).Contains(x.id)).ToList();
+                        typeUsers.Add(new TypeUser() { irs = x.irs, no = x.no, nickname = x.nickname, userid = x.id, userType = "student", username = x.name, userSchool = x.code.Replace("Base-", ""), classes = clases.Select(x => new TypeUserClass { id = x.id, name = x.name }).ToList() });
+                    });
+                }
+            }
+
+            List<dynamic> rscs = new List<dynamic>();
+
+            string debateCode = "";
+            string debateTbname = "";
+            if (homework.scope.Equals("school"))
+            {
+                debateTbname = "School";
+                debateCode = $"Debate-{homework.school}";
+            }
+            else
+            {
+                debateTbname = "Teacher";
+                debateCode = $"Debate-{homework.creatorId}";
+            }
+            List<Debate> debates = new List<Debate>();
+            string sql = $"select value(c) from c where c.comid='{homework.id}'";
+            await foreach (var item in client.GetContainer(Constant.TEAMModelOS, debateTbname).GetItemQueryIterator<Debate>(queryText: sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey(debateCode) }))
+            {
+                debates.Add(item);
+            }
+            List<HomeworkUser> users = new List<HomeworkUser>();
+            foreach (var typeuser in typeUsers)
+            {
+                var us = users.Find(x => x.userid.Equals(typeuser.userid));
+                if (us != null) { continue; }
+                HomeworkUser user = new HomeworkUser() { irs = typeuser.irs, no = typeuser.no, nickname = typeuser.nickname, userid = typeuser.userid, userSchool = typeuser.userSchool, userType = typeuser.userType, username = typeuser.username, classes = typeuser.classes };
+                string partition = $"HomeworkRecord-{typeuser.userid}";
+                if (typeuser.userType.Equals("student"))
+                {
+                    partition = $"HomeworkRecord-{typeuser.userSchool}-{typeuser.userid}";
+                }
+                else
+                {
+                    partition = $"HomeworkRecord-{typeuser.userid}";
+                }
+                HomeworkRecord record = null;
+                try
+                {
+                    record = await client.GetContainer(Constant.TEAMModelOS, tbname).ReadItemAsync<HomeworkRecord>($"{homework.id}", new PartitionKey(partition));
+                    double star = -1;
+                    List<double> stars = record.comments.FindAll(s => s.identity.Equals("student")).Select(x => x.star).ToList();
+                    if (stars.IsNotEmpty())
+                    {
+                        star = stars.Sum() / stars.Count;
+                    }
+                    user.submit = true;
+                    user.studentStar = star;
+                    user.score = record.score;
+                    user.submitTime = record.time;
+                    user.content = record.content;
+                    // user.username = typeuser.username;
+                    var teacherComment = record.comments.Where(x => x.identity.Equals("teacher") && record.teacher.Equals(userid)).FirstOrDefault();
+                    if (teacherComment != null)
+                    {
+                        user.teacherStar = teacherComment.star;
+                        List<string> replies = new List<string>();
+                        teacherComment.replyIds.ForEach(x => {
+                            var reply = debates.SelectMany(x => x.replies).ToList().Find(r => r.id.Equals(x));
+                            if (reply != null)
+                            {
+                                replies.Add(reply.comment);
+                            }
+                        });
+                        user.replies = replies;
+                    }
+                }
+                catch (CosmosException ex)
+                {
+                    user.submit = false;
+                    record = null;
+                }
+                users.Add(user);
+            }
+            return users;
+        }
+
+
         public static async Task<string> saveMoreAsync(CosmosClient client, DingDing _dingDing, Homework work, AzureServiceBusFactory _serviceBus, AzureStorageFactory _azureStorage, IConfiguration _configuration, AzureRedisFactory _azureRedis)
         {
             try
@@ -64,4 +181,42 @@ namespace TEAMModelOS.SDK.Models.Service
             }
         }
     }
+
+    public class TypeUser
+    {
+        public string irs { get; set; }
+        public string no { get; set; }
+        public string nickname { get; set; }
+        public string userid { get; set; }
+
+        public string userType { get; set; }
+        public string userSchool { get; set; }
+
+        public string username { get; set; }
+        public List<TypeUserClass> classes { get; set; } = new List<TypeUserClass>();
+    }
+    public class TypeUserClass
+    {
+        public string id { get; set; }
+        public string name { get; set; }
+    }
+    public class HomeworkUser
+    {
+        public string irs { get; set; }
+        public string no { get; set; }
+        public string nickname { get; set; }
+        public string userid { get; set; }
+        public string userType { get; set; }
+        public string userSchool { get; set; }
+        public long submitTime { get; set; }
+        public double score { get; set; } = -1;
+        public double studentStar { get; set; } = -1;
+        public double teacherStar { get; set; } = -1;
+        public bool submit { get; set; }
+
+        public string username { get; set; }
+        public List<TypeUserClass> classes { get; set; } = new List<TypeUserClass>();
+        public List<string> replies { get; set; } = new List<string>();
+        public List<Attachment> content { get; set; } = new List<Attachment>();
+    }
 }

+ 598 - 13
TEAMModelOS.SDK/Models/Service/SystemService.cs

@@ -1,11 +1,15 @@
 using Azure.Storage.Blobs.Models;
 using DocumentFormat.OpenXml.Bibliography;
+using DocumentFormat.OpenXml.Drawing.Charts;
 using DocumentFormat.OpenXml.Office2010.Excel;
 using DocumentFormat.OpenXml.Wordprocessing;
 using HTEXLib;
 using HTEXLib.COMM.Helpers;
+using HTEXLib.PPTX.Models;
 using Microsoft.Azure.Amqp.Framing;
+using Microsoft.Extensions.Logging;
 using Newtonsoft.Json.Linq;
+using NUnit.Framework.Internal.Execution;
 using OpenXmlPowerTools;
 using StackExchange.Redis;
 using System;
@@ -14,31 +18,143 @@ using System.Collections.Generic;
 using System.Diagnostics.PerformanceData;
 using System.IdentityModel.Tokens.Jwt;
 using System.Linq;
+using System.Net.Http;
+using System.Net.Http.Json;
+using System.Security.Policy;
 using System.Text;
 using System.Text.Json;
 using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.DI.Mail;
 using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models.Cosmos.Common;
 using TEAMModelOS.SDK.Models.Dtos;
 using static Azure.Core.HttpHeader;
 using static OpenXmlPowerTools.RevisionProcessor;
+using static TEAMModelOS.SDK.CoreAPIHttpService;
 using static TEAMModelOS.SDK.Models.Service.SystemService;
 
 namespace TEAMModelOS.SDK.Models.Service
 {
     public static class SystemService
     {
-
-
-        public static async Task AccumulateDaily(AzureRedisFactory _azureRedis,AzureCosmosFactory _azureCosmos)
+        #region
+        static string cn_wb = @"
+IES晚间报告:<br>{tmdname}您好,以下是您的今日个人IES教学汇总报告,截至{sendTime}时,您已发布{examCount}次评测任务,{homeworkCount}次作业任务,并使用HiTeach教师端开设了{lessonCount}节课堂教学活动。
+<br>&nbsp;&nbsp;&nbsp;以下是报告的具体详细信息。
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{cn_examTitle}<br>{cn_examList}
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{cn_lessonTitle}<br>{cn_lessonList}
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{cn_groupTitle}<br>{cn_groupList}
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如有布置作业任务,将于次日8点通过早报方式发送。
+";
+        static string cn_examList = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;【{examName}】已经有{submitCount}位学生提交,仍有{unsubmitCount}位学生未提交。<br>";
+        static string cn_lessonList = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;【{lessonName}】课例应出席人数{memberCount},实际出席人数{attendCount},出席率{attendRate}。<br>";
+        static string cn_groupListJoin = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;【{grouplistName}】已有总人数{memberCount},有{joinCount}加入。<br>";
+        static string cn_groupListLeave = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;【{grouplistName}】已有总人数{memberCount},有{joinCount}离开。<br>";
+
+        static string cn_zb = @"
+IES早间报告:
+    {tmdname}您好,以下是您发布过的作业任务汇总报告。
+     以下是报告的具体详细信息。
+    {cn_homeworkTitle}
+        {cn_homeworkList}
+";
+        static string cn_homeworkList = "【{homeworkName}】已经有{submitCount}位学生提交,仍有{unsubmitCount}位学生未提交。<br>";
+        static string cn_homeworkTitle = "作业任务提交详情:";
+        static string cn_examTitle = "评测任务提交详情:";
+        static string cn_lessonTitle = "课堂教学出席详情:";
+        static string cn_groupTitle = "个人课程名单变化详情:";
+        #endregion
+
+        #region
+        static string tw_wb = @"
+IES晚間報告:
+    {tmdname}您好,以下是您的今日個人IES教學總結報告,截至{sendTime},您已發布{examCount}次測驗任務,{homeworkCount}次作業任務,並使用HiTeach上傳了{lessonCount}節課堂教學活動。
+    以下是詳細資訊。
+    {tw_examTitle}
+        {tw_examList}
+    {tw_lessonTitle}
+        {tw_lessonList}
+    {tw_groupTitle}
+        {tw_groupList}
+    如有佈署作業任務,將於隔天早上8點透過晨間報告方式發送。
+";
+        static string tw_examList = "【{examName}】已經有{submitCount}位學生繳交,仍有{unsubmitCount}位學生未繳交。\r\n";
+        static string tw_lessonList = "【{lessonName}】課堂應出席人數{memberCount},實際出席人數{attendCount},出席率{attendRate}。\r\n";
+        static string tw_groupListJoin = "【{grouplistName}】已有總人數{memberCount},有{joinCount}加入。\r\n";
+        static string tw_groupListLeave = "【{grouplistName}】已有總人數{memberCount},有{joinCount}離開。\r\n";
+
+        static string tw_zb = @"
+IES晨間報告:
+    {tmdname}您好,以下是您曾經發佈過的作業任務總結報告。
+    以下是具體詳細資訊。
+    {tw_homeworkTitle}
+        {tw_homeworkList}
+";
+        static string tw_homeworkList = "【{homeworkName}】已經有{submitCount}位學生繳交,仍有{unsubmitCount}位學生未繳交。\r\n";
+        static string tw_homeworkTitle = "\r\n作業任務繳交詳情:";
+        static string tw_examTitle = "\r\n測驗任務完成詳情:";
+        static string tw_lessonTitle = "\r\n課堂教學出席詳情:";
+        static string tw_groupTitle = "\r\n個人課程名單變動詳情:";
+        #endregion
+
+
+        #region
+        static string en_wb = @"
+IES Evening Report:
+    Hello {tmdname}, here is your personal IES teaching summary report for today. As of {sendTime}, you have published {examCount} test tasks, {homeworkCount} homework tasks, and uploaded {lessonCount} HiTeach lesson activity records.
+    The following are the specific details.
+    {en_examTitle}
+        {en_examList}
+    {en_lessonTitle}
+        {en_lessonList}
+    {en_groupTitle}
+        {en_groupList}
+    If there are homework tasks assigned, they will be sent through the morning report at 8 am the next day.
+";
+        static string en_examList = "[ {examName} ] has {submitCount} students submitted, and there are still {unsubmitCount} students who have not submitted.\r\n";
+        static string en_lessonList = "[ {lessonName} ] lesson should have {memberCount} attendees, actual attendees {attendCount}, attendance rate {attendRate}.\r\n";
+        static string en_groupListJoin = "[ {grouplistName} ] has a total of {memberCount}, {joinCount} joined.\r\n";
+        static string en_groupListLeave = "[ {grouplistName} ] has a total of {memberCount}, {joinCount} left.\r\n";
+        static string en_zb = @"
+IES Morning Report:
+    Hello {tmdname}, here is the summary report of the homework tasks you have posted.
+    {en_homeworkTitle}
+    {en_homeworkList}
+";
+        static string en_homeworkList = "[ {homeworkName} ] has {submitCount} students submitted, and there are still {unsubmitCount} students who have not submitted.\r\n";
+        static string en_homeworkTitle = "\r\nHomework Submission Details:";
+        static string en_examTitle = "\r\nTest task submission details:";
+        static string en_lessonTitle = "\r\nLesson attendance details:";
+        static string en_groupTitle = "\r\nPersonal course list change details:";
+        #endregion
+
+
+        public static async Task<List<CodeValue> > AccumulateDaily(AzureRedisFactory _azureRedis,AzureCosmosFactory _azureCosmos,
+            CoreAPIHttpService coreAPIHttpService, DingDing dingDing,HttpClient _httpClient, SnowflakeId _snowflakeId,string notifyUrl, MailFactory _mailFactory)
         {
-            DateTimeOffset now = DateTimeOffset.UtcNow;
+            DateTimeOffset now = DateTimeOffset.Now;
             string day = now.ToString("yyyyMMdd");
+            string homeworkYesterday = now.AddDays(-1).ToString("yyyyMMdd");
+            HashSet<string> keys = new HashSet<string>();
+            //当天评测发布的。
             //当天的个人评测提交数
             Dictionary<string, double?> exam_submit = new();
-            List<string> keys = new List<string>();
+            SortedSetEntry[] exam_goingScores = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Accumulate:Daily:teacher:exam-going:{day}");
+          //  Dictionary<string, double?> exam_going = new();
+            if (exam_goingScores != null)
+            {
+                foreach (var score in exam_goingScores)
+                {
+                    double val = score.Score;
+                    string key = score.Element.ToString();
+                   // exam_going.Add(key, val);
+                    exam_submit.Add(key, val);
+                    keys.Add(key);
+                }
+            }
+            ///需要再次获取 没有任何人提交的作业和评测任务。
             SortedSetEntry[] exam_submitScores = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Accumulate:Daily:private:exam-submit:{day}");
             if (exam_submitScores != null)
             {
@@ -46,23 +162,54 @@ namespace TEAMModelOS.SDK.Models.Service
                 {
                     double val = score.Score;
                     string key = score.Element.ToString();
-                    exam_submit.Add(key, val);
+                    exam_submit.TryAdd(key, val);
                     keys.Add(key);
                 }
             }
-            List<string> homeworkIds = new List<string>();
-            //当天的个人作业提交数
-            SortedSetEntry[] homework_submitScores = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Accumulate:Daily:teacher:homework-submit:{day}");
+            //今天发布的作业
+            SortedSetEntry[] homework_goingScores_today = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Accumulate:Daily:teacher:homework-going:{day}");
+            Dictionary<string, double?> homework_submit_today = new();
+            //Dictionary<string, double?> homework_going = new();
+            if (homework_goingScores_today != null)
+            {
+                foreach (var score in homework_goingScores_today)
+                {
+                    double val = score.Score;
+                    string key = score.Element.ToString();
+                    //homework_going.Add(key, val);
+                    homework_submit_today.TryAdd(key, val);
+
+                }
+            }
+
+            //昨天发布的作业
+            SortedSetEntry[] homework_goingScores = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Accumulate:Daily:teacher:homework-going:{homeworkYesterday}");
             Dictionary<string, double?> homework_submit = new();
+            //Dictionary<string, double?> homework_going = new();
+            if (homework_goingScores != null)
+            {
+                foreach (var score in homework_goingScores)
+                {
+                    double val = score.Score;
+                    string key = score.Element.ToString();
+                    //homework_going.Add(key, val);
+                    homework_submit.TryAdd(key, val);
+                    keys.Add(key);
+                    
+                }
+            }
+
+            //昨天的个人作业提交数
+            SortedSetEntry[] homework_submitScores = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Accumulate:Daily:teacher:homework-submit:{homeworkYesterday}");
+           
             if (homework_submitScores != null)
             {
                 foreach (var score in homework_submitScores)
                 {
                     double val = score.Score;
                     string key = score.Element.ToString();
-                    homework_submit.Add(key, val);
+                    homework_submit.TryAdd(key, val);
                     keys.Add(key);
-                    homeworkIds.Add(key.Split("::")[1]);
                 }
             }
             //当天的个人名单加入人数
@@ -74,14 +221,28 @@ namespace TEAMModelOS.SDK.Models.Service
                 {
                     double val = score.Score;
                     string key = score.Element.ToString();
-                    grouplist.Add(key, val);
+                    grouplist.TryAdd(key, val);
+                    keys.Add(key);
+                }
+            }
+            //当天教师的开课数量Accumulate:Daily:teacher:lesson-create:20240527
+            Dictionary<string, double?> lessoncreate = new();
+            SortedSetEntry[] llessoncreateScores = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Accumulate:Daily:teacher:lesson-create:{day}");
+            if (llessoncreateScores != null)
+            {
+                foreach (var score in llessoncreateScores)
+                {
+                    double val = score.Score;
+                    string key = score.Element.ToString();
+                    lessoncreate.TryAdd(key, val);
                     keys.Add(key);
                 }
             }
             List<string> ids = new List<string>();
-            keys.ForEach(x => {
+            keys.ToList().ForEach(x => {
                 ids.Add(x.Split("::")[0]);
             });
+            List<CoreUser> coreUsers = new List<CoreUser>();
             List<Teacher> teachers= new List<Teacher>();
             if (ids.IsNotEmpty()) 
             {
@@ -91,7 +252,431 @@ namespace TEAMModelOS.SDK.Models.Service
                 {
                     teachers.AddRange(result.list);
                 }
+                var content = new StringContent(ids.Distinct().ToJsonString(), Encoding.UTF8, "application/json");
+                try
+                {
+                   string json = await coreAPIHttpService.GetUserInfos(content);
+                    if (!string.IsNullOrWhiteSpace(json))
+                    {
+
+                       
+                        coreUsers = json.ToObject<List<CoreUser>>();
+                    }
+                }
+                catch (Exception ex)
+                {
+                    await dingDing.SendBotMsg($"{coreAPIHttpService.options.location}用户转换失败:{coreAPIHttpService.options.coreUrl}", GroupNames.醍摩豆服務運維群組);
+                }
+            }
+            List<CodeValue> notifys = new List<CodeValue>();
+            foreach (var teacher in teachers)
+            {
+                StringBuilder notify = new StringBuilder();
+#if DEBUG
+                int sendTime_pm = now.Hour;
+                int sendTime_am = now.Hour;
+#else
+                int sendTime_pm = 20;
+                int sendTime_am =8;
+#endif
+                string lang = teacher.lang;
+                var tzt = now.GetGMTTime((int)teacher.timezone);
+                if (string.IsNullOrWhiteSpace(teacher.lang)) 
+                {
+                    if (coreAPIHttpService.options.location.Contains("China", StringComparison.OrdinalIgnoreCase)) 
+                    {
+                        lang= "zh-cn";
+                    }
+                    if (coreAPIHttpService.options.location.Contains("Global",StringComparison.OrdinalIgnoreCase))
+                    {
+                        lang= "en-us";
+                    }
+                }
+                int examCount = 0,homeworkCount=0,lessonCount=0;
+                StringBuilder examSB= new StringBuilder();
+                foreach (var exam in exam_submit)
+                {
+                    
+                    string[] ks = exam.Key.Split("::");
+                    string tid = ks[0];
+                    string examId = ks[1];
+                    string examName = ks[3];
+                    if (tid.Equals(teacher.id)) {
+                        if (tzt.Hour==sendTime_pm)
+                        {
+                            string sql = $"select c.status  from c where c.examId='{examId}' and c.pk='ExamClassResult'";
+                            var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<ExamClassResult>(sql, $"ExamClassResult-{tid}");
+                            if (result.list.IsNotEmpty()) {
+                                var unsubmit = result.list.SelectMany(x => x.status).Where(x => x==1);
+                                var submit = result.list.SelectMany(x => x.status).Where(x => x==0);
+                                examCount++;
+                                switch (lang) 
+                                {
+                                    case "zh-cn":
+                                        examSB.Append(cn_examList.Replace("{examName}", examName).Replace("{submitCount}", $"{submit.Count()}").Replace("{unsubmitCount}", $"{unsubmit.Count()}"));
+                                        break;
+                                    case "zh-tw":
+                                        examSB.Append(tw_examList.Replace("{examName}", examName).Replace("{submitCount}", $"{submit.Count()}").Replace("{unsubmitCount}", $"{unsubmit.Count()}"));
+                                        break;
+                                    case "en-us":
+                                        examSB.Append(en_examList.Replace("{examName}", examName).Replace("{submitCount}", $"{submit.Count()}").Replace("{unsubmitCount}", $"{unsubmit.Count()}")); 
+                                        break;
+                                }
+                               
+                            }
+                        }
+                    }
+                }
+                StringBuilder homeworkSB = new StringBuilder();
+
+                foreach (var homeworksm in homework_submit_today)
+                {
+                    string[] ks = homeworksm.Key.Split("::");
+                    string tid = ks[0];
+                    string homeworkId = ks[1];
+                    string homeworkName = ks[3];
+                    if (tid.Equals(teacher.id)) 
+                    {
+                        if (tzt.Hour==sendTime_pm) 
+                        {
+                            homeworkCount++;
+                        }
+                    }
+                }
+                foreach (var homeworksm in homework_submit) 
+                {
+                 
+                    string[] ks = homeworksm.Key.Split("::");
+                    string tid = ks[0];
+                    string homeworkId = ks[1];
+                    string homeworkName = ks[3];
+                    if (tid.Equals(teacher.id))
+                    {
+                        if (tzt.Hour==sendTime_am)
+                        {
+                            Azure .Response response =   await  _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).ReadItemStreamAsync(homeworkId, new Azure.Cosmos.PartitionKey($"Homework-{tid}"));
+                            if (response.Status==200) { 
+                                Homework homework= JsonDocument.Parse(response.Content).RootElement.ToObject<Homework>();
+                                List<string > classes= new List<string>();
+                                classes.AddRange(homework.stuLists);
+                                classes.AddRange(homework.classes);
+                                List<HomeworkUser> homeworkUsers=  await HomeworkService.AnswerRecordAll(_azureCosmos.GetCosmosClient(), coreAPIHttpService, dingDing, homework, tid, "Student", classes, "student");
+                               // homeworkCount++;
+                                switch (lang)
+                                {
+                                    case "zh-cn":
+                                        homeworkSB.Append(cn_homeworkList.Replace("{homeworkName}", homework.name).Replace("{submitCount}", $"{homeworkUsers.Where(x => x.submit).Count()}").Replace("{unsubmitCount}", $"{homeworkUsers.Where(x => !x.submit).Count()}"));
+                                        break;
+                                    case "zh-tw":
+                                        homeworkSB.Append(tw_homeworkList.Replace("{homeworkName}", homework.name).Replace("{submitCount}", $"{homeworkUsers.Where(x => x.submit).Count()}").Replace("{unsubmitCount}", $"{homeworkUsers.Where(x => !x.submit).Count()}"));
+                                        break;
+                                    case "en-us":
+                                        homeworkSB.Append(en_homeworkList.Replace("{homeworkName}", homework.name).Replace("{submitCount}", $"{homeworkUsers.Where(x => x.submit).Count()}").Replace("{unsubmitCount}", $"{homeworkUsers.Where(x => !x.submit).Count()}"));
+                                        break;
+                                }
+                            }
+                        }
+                    }
+                }
+                StringBuilder lessonSB = new StringBuilder();
+                foreach (var lesson in lessoncreate)
+                {
+                    string[] ks = lesson.Key.Split("::");
+                    string tid = ks[0];
+                    if (tid.Equals(teacher.id))
+                    {
+                        if (tzt.Hour==sendTime_pm)
+                        {
+                            long stime = now.AddHours(-20).ToUnixTimeMilliseconds();
+                            string sql = $"select value c from c where c.tmdid='{teacher.id}' and c.pk='LessonRecord' and c.startTime> {stime} ";
+                            List<LessonRecord>lessons = new List<LessonRecord>();
+                            var schoolResult= await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<LessonRecord>(sql);
+                            var teahcerResult = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<LessonRecord>(sql, "LessonRecord");
+                            lessons.AddRange(schoolResult.list);
+                            lessons.AddRange(teahcerResult.list);
+                            foreach(var lessonRecord in lessons) 
+                            {
+                                lessonCount++;
+                                switch (lang)
+                                {
+                                    case "zh-cn":
+                                        lessonSB.Append(cn_lessonList.Replace("{lessonName}", lessonRecord.name).Replace("{memberCount}", $"{lessonRecord.clientCount}").Replace("{attendCount}", $"{lessonRecord.attendCount}")).Replace("{attendRate}", $"{lessonRecord.attendRate}");
+                                        break;
+                                    case "zh-tw":
+                                        lessonSB.Append(tw_lessonList.Replace("{lessonName}", lessonRecord.name).Replace("{memberCount}", $"{lessonRecord.clientCount}").Replace("{attendCount}", $"{lessonRecord.attendCount}")).Replace("{attendRate}", $"{lessonRecord.attendRate}");
+                                        break;
+                                    case "en-us":
+                                        lessonSB.Append(en_lessonList.Replace("{lessonName}", lessonRecord.name).Replace("{memberCount}", $"{lessonRecord.clientCount}").Replace("{attendCount}", $"{lessonRecord.attendCount}")).Replace("{attendRate}", $"{lessonRecord.attendRate}");
+                                        break;
+                                }
+                            }
+
+                        }
+                    }
+                }
+
+                StringBuilder grouplistSB = new StringBuilder();
+                List<string> grouplistIds=new List<string>();
+
+                foreach (var group in grouplist) 
+                {
+                    string[] ks = group.Key.Split("::");
+                    grouplistIds.Add(ks[1]);
+                }
+                List<GroupListDto> groupListDtos=   await GroupListService.GetGroupListByListids(_azureCosmos.GetCosmosClient(), dingDing, grouplistIds, null);
+                foreach (var group in grouplist) 
+                {
+                    string[] ks = group.Key.Split("::");
+                    string tid = ks[0];
+                    string grouplistId = ks[1];
+                    string grouplistName = ks[3];
+                    var gpdto=  groupListDtos.Find(x => x.id.Equals(grouplistId));
+                    if (gpdto!= null)
+                    {
+                        if (tid.Equals(teacher.id))
+                        {
+                            if (tzt.Hour==sendTime_pm)
+                            {
+                                switch (lang)
+                                {
+                                    case "zh-cn":
+
+                                        if (group.Value>=0)
+                                        {
+                                            grouplistSB.Append(cn_groupListJoin.Replace("{grouplistName}", grouplistName).Replace("{memberCount}", $"{gpdto.tcount+gpdto.scount}").Replace("{joinCount}", $"{group.Value}"));
+
+                                        }
+                                        else
+                                        {
+                                            grouplistSB.Append(cn_groupListLeave.Replace("{grouplistName}", grouplistName).Replace("{memberCount}", $"{gpdto.tcount+gpdto.scount}").Replace("{joinCount}", $"{group.Value}"));
+
+                                        }
+                                        break;
+                                    case "zh-tw":
+                                        if (group.Value>=0)
+                                        {
+                                            grouplistSB.Append(tw_groupListJoin.Replace("{grouplistName}", grouplistName).Replace("{memberCount}", $"{gpdto.tcount+gpdto.scount}").Replace("{joinCount}", $"{group.Value}"));
+
+                                        }
+                                        else
+                                        {
+                                            grouplistSB.Append(tw_groupListLeave.Replace("{grouplistName}", grouplistName).Replace("{memberCount}", $"{gpdto.tcount+gpdto.scount}").Replace("{joinCount}", $"{group.Value}"));
+
+                                        }
+                                        break;
+                                    case "en-us":
+                                        if (group.Value>=0)
+                                        {
+                                            grouplistSB.Append(en_groupListJoin.Replace("{grouplistName}", grouplistName).Replace("{memberCount}", $"{gpdto.tcount+gpdto.scount}").Replace("{joinCount}", $"{group.Value}"));
+
+                                        }
+                                        else
+                                        {
+                                            grouplistSB.Append(en_groupListLeave.Replace("{grouplistName}", grouplistName).Replace("{memberCount}", $"{gpdto.tcount+gpdto.scount}").Replace("{joinCount}", $"{group.Value}"));
+                                        }
+                                        break;
+                                }
+                            }
+                        }
+                    }
+                }
+                if (tzt.Hour==sendTime_pm  && (examCount>0 || lessonCount>0 || homeworkCount>0  || (grouplist.Count>0 && !string.IsNullOrWhiteSpace(grouplistSB.ToString())))) 
+                {
+                    string template=string.Empty;
+                    string title=string.Empty;
+                    switch (lang)
+                    {
+                        case "zh-cn":
+                            title="IES晚间报告";
+                            template=cn_wb.Replace("{tmdname}", teacher.name).Replace("{sendTime}", $"{now.ToString("yyyy-MM-dd")} {sendTime_pm}")
+                                .Replace("{examCount}", $"{examCount}").Replace("{homeworkCount}", $"{homeworkCount}").Replace("{lessonCount}", $"{lessonCount}")
+                                .Replace("{cn_examList}", examSB.ToString()).Replace("{cn_lessonList}", lessonSB.ToString()).Replace("{cn_groupList}", grouplistSB.ToString());
+                            if (examCount>0)
+                            {
+                                template= template.Replace("{cn_examTitle}", cn_examTitle);
+                            }
+                            else {
+                                template=  template.Replace("{cn_examTitle}", "");
+                            }
+                            if (homeworkCount>0)
+                            {
+                                template=   template.Replace("{cn_homeworkTitle}", cn_homeworkTitle);
+                            }
+                            else
+                            {
+                                template=  template.Replace("{cn_homeworkTitle}", "");
+                            }
+                            if (lessonCount>0)
+                            {
+                                template=  template.Replace("{cn_lessonTitle}", cn_lessonTitle);
+                            }
+                            else
+                            {
+                                template= template.Replace("{cn_lessonTitle}", "");
+                            }
+                            if (grouplist.Count>0  && !string.IsNullOrWhiteSpace(grouplistSB.ToString()))
+                            {
+                                template=template.Replace("{cn_groupTitle}", cn_groupTitle);
+                            }
+                            else
+                            {
+                                template=  template.Replace("{cn_groupTitle}", "");
+                            }
+                            break;
+                        case "zh-tw":
+                            title="IES晚間報告";
+                            template=tw_wb.Replace("{tmdname}", teacher.name).Replace("{sendTime}", $"{now.ToString("yyyy-MM-dd")} {sendTime_pm}")
+                              .Replace("{examCount}", $"{examCount}").Replace("{homeworkCount}", $"{homeworkCount}").Replace("{lessonCount}", $"{lessonCount}")
+                              .Replace("{tw_examList}", examSB.ToString()).Replace("{tw_lessonList}", lessonSB.ToString()).Replace("{tw_groupList}", grouplistSB.ToString());
+                            if (examCount>0)
+                            {
+                                template= template.Replace("{tw_examTitle}", tw_examTitle);
+                            }
+                            else
+                            {
+                                template=  template.Replace("{tw_examTitle}", "");
+                            }
+                            if (homeworkCount>0)
+                            {
+                                template=   template.Replace("{tw_homeworkTitle}", tw_homeworkTitle);
+                            }
+                            else
+                            {
+                                template=  template.Replace("{tw_homeworkTitle}", "");
+                            }
+                            if (lessonCount>0)
+                            {
+                                template=  template.Replace("{tw_lessonTitle}", tw_lessonTitle);
+                            }
+                            else
+                            {
+                                template= template.Replace("{tw_lessonTitle}", "");
+                            }
+                            if (grouplist.Count>0)
+                            {
+                                template=template.Replace("{tw_groupTitle}", tw_groupTitle);
+                            }
+                            else
+                            {
+                                template=  template.Replace("{tw_groupTitle}", "");
+                            }
+                            break;
+                        case "en-us":
+                            title ="IES Evening Report";
+                            template=en_wb.Replace("{tmdname}", teacher.name).Replace("{sendTime}", $"{now.ToString("yyyy-MM-dd")} {sendTime_pm}")
+                             .Replace("{examCount}", $"{examCount}").Replace("{homeworkCount}", $"{homeworkCount}").Replace("{lessonCount}", $"{lessonCount}")
+                             .Replace("{en_examList}", examSB.ToString()).Replace("{en_lessonList}", lessonSB.ToString()).Replace("{en_groupList}", grouplistSB.ToString());
+                            if (examCount>0)
+                            {
+                                template= template.Replace("{en_examTitle}", en_examTitle);
+                            }
+                            else
+                            {
+                                template=  template.Replace("{en_examTitle}", "");
+                            }
+                            if (homeworkCount>0)
+                            {
+                                template=   template.Replace("{en_homeworkTitle}", en_homeworkTitle);
+                            }
+                            else
+                            {
+                                template=  template.Replace("{en_homeworkTitle}", "");
+                            }
+                            if (lessonCount>0)
+                            {
+                                template=  template.Replace("{en_lessonTitle}", en_lessonTitle);
+                            }
+                            else
+                            {
+                                template= template.Replace("{en_lessonTitle}", "");
+                            }
+                            if (grouplist.Count>0)
+                            {
+                                template=template.Replace("{en_groupTitle}", en_groupTitle);
+                            }
+                            else
+                            {
+                                template=  template.Replace("{en_groupTitle}", "");
+                            }
+                            break;
+                    }
+
+#if DEBUG
+                    string eventId = $"Evening_Report-{_snowflakeId.NextId()}";
+                    NotifyData notifyData = new NotifyData
+                    {
+                        hubName = "hita5",
+                        sender = "IES",
+                        tags = new List<string>() { $"{teacher.id}_{Constant.NotifyType_IES5_Course}" },
+                        title = title,
+                        eventId =eventId,
+                        eventName =title,
+                        data = "{\"value\":{}}",
+                        body=template,
+                    };
+
+                    HttpResponseMessage responseMessage = await _httpClient.PostAsJsonAsync($"{notifyUrl}/service/PushNotify", notifyData);
+                    var coreUser = coreUsers.Find(x => x.id.Equals(teacher.id)  && !string.IsNullOrWhiteSpace(x.mail));
+                    if (coreUser!=null)
+                    {
+                        var token = _mailFactory.GetSmtpClient().SendEmail(_azureCosmos, dingDing, eventId, title, template, coreUser.mail, teacher.id, teacher.name);
+                    }
+#endif
+                    notify.Append(template);
+                }
+                if (tzt.Hour==sendTime_am  && !string.IsNullOrWhiteSpace(homeworkSB.ToString()))
+                {
+                    string template = string.Empty;
+                    string title = string.Empty;
+                    switch (lang)
+                    {
+                        case "zh-cn":
+                            title ="IES早间报告";
+                            template= cn_zb.Replace("{tmdname}", teacher.name).Replace("{sendTime}", $"{now.ToString("yyyy-MM-dd")} {sendTime_am}")
+                               .Replace("{cn_homeworkList}", homeworkSB.ToString());
+                            template=template.Replace("{cn_homeworkTitle}", cn_homeworkTitle);
+
+                            break;
+                        case "zh-tw":
+                            title ="IES晨間報告";
+                            template= tw_zb.Replace("{tmdname}", teacher.name).Replace("{sendTime}", $"{now.ToString("yyyy-MM-dd")} {sendTime_am}")
+                                 .Replace("{tw_homeworkList}", homeworkSB.ToString());
+                            template=template.Replace("{tw_homeworkTitle}", tw_homeworkTitle);
+                            break;
+                        case "en-us":
+                            title ="IES Morning Report";
+                            template=en_zb.Replace("{tmdname}", teacher.name).Replace("{sendTime}", $"{now.ToString("yyyy-MM-dd")} {sendTime_am}")
+                                .Replace("{en_homeworkList}", homeworkSB.ToString());
+                            template=template.Replace("{en_homeworkTitle}", en_homeworkTitle);
+                            break;
+                    }
+#if DEBUG
+                    string  eventId = $"Morning_Report-{_snowflakeId.NextId()}";
+                    NotifyData notifyData = new NotifyData
+                    {
+                        hubName = "hita5",
+                        sender = "IES",
+                        tags = new List<string>() { $"{teacher.id}_{Constant.NotifyType_IES5_Course}" },
+                        title = title,
+                        eventId = eventId,
+                        eventName =title,
+                        data = "{\"value\":{}}",
+                        body=template,
+                    };
+
+                    HttpResponseMessage responseMessage = await _httpClient.PostAsJsonAsync($"{notifyUrl}/service/PushNotify", notifyData);
+                    var coreUser = coreUsers.Find(x => x.id.Equals(teacher.id)  && !string.IsNullOrWhiteSpace(x.mail));
+                    if (coreUser!=null)
+                    {
+                        var token = _mailFactory.GetSmtpClient().SendEmail(_azureCosmos, dingDing, eventId, title, template, coreUser.mail, teacher.id, teacher.name);
+                    }
+#endif
+                    notify.Append(template);
+                }
+                notifys.Add(new CodeValue { code=teacher.id, value= notify.ToString() });
+                
             }
+            return notifys;
         }
         public static async Task RecordAccumulateData(AzureRedisFactory azureRedis, DingDing dingDing, Accumulate accumulate)
         {

+ 3 - 3
TEAMModelOS.SDK/TEAMModelOS.SDK.csproj

@@ -2,9 +2,9 @@
 
 	<PropertyGroup>
 		<TargetFramework>net6.0</TargetFramework>
-		<Version>5.2405.29</Version>
-		<AssemblyVersion>5.2405.29.1</AssemblyVersion>
-		<FileVersion>5.2405.29.1</FileVersion>
+		<Version>5.2406.5</Version>
+		<AssemblyVersion>5.2406.5.1</AssemblyVersion>
+		<FileVersion>5.2406.5.1</FileVersion>
 		<PackageReleaseNotes>发版</PackageReleaseNotes>
 	</PropertyGroup>
 

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

@@ -58,9 +58,10 @@
 			document.write('<script id="vueRouter"  src="' + blobHost + '/0-public/js/vue-router.min.js"><\/script>');
 			document.write('<script id="echarts"  src="' + blobHost + '/0-public/js/echarts.490.js"><\/script>');
 			document.write('<script id="jspdf"  src="' + blobHost + '/0-public/js/jspdf.umd.min.js"><\/script>');
-			document.write('<script id="mathjax"  src="' + blobHost + '/0-public/js/tex-mml-svg.js"><\/script>');
+			// document.write('<script id="mathjax"  src="' + blobHost + '/0-public/js/tex-mml-svg.js"><\/script>');
 			document.write('<script id="mediaInfo"  src="' + blobHost + '/0-public/js/checkMedia.js"><\/script>');
 			document.write('<script id="e_theme_macarons"  src="<%= BASE_URL %>theme/e_theme_macarons.js"><\/script>');
+			document.write('<script id="mathjax" type="text/javascript" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"><\/script>');
 
 			window.onload = function () {
 				let bodyElement = document.body;

+ 6 - 1
TEAMModelOS/ClientApp/public/lang/en-US.js

@@ -1627,7 +1627,7 @@ const LANG_EN_US = {
             ans: 'Answer',
             tip6: 'Enter the answer',
             tip7: 'Switching will clear the current question data, do you confirm to switch?',
-            tip8: 'Maximum 50 questions!',
+            tip8: 'Maximum 50 questions of this type!',
             tip9: 'Enter the speaking prompt',
             tip10: 'The number of questions cannot be 0!',
             tip11: 'The number of answers for single answer questions does not match the number of questions, please check and retry!',
@@ -1694,6 +1694,7 @@ const LANG_EN_US = {
             openAll: 'Unfold All',
             collapseAll: 'Fold All',
             autoCreate: 'SMART Design',
+            emptyPaper: 'New Exam Paper',
             manualCreate: 'Manual Design',
             importCreate: 'Import Design',
             moreAction: 'More',
@@ -4706,6 +4707,8 @@ const LANG_EN_US = {
             listName: 'List Name',
             joinTime: 'Application Time',
             todayWrong: "Today's SMART Incorrectly Answered Question Practice",
+            top: 'Pin',
+            excelTop: 'Unpin',
         },
         public: {
             noData: 'No data yet',
@@ -5253,6 +5256,8 @@ const LANG_EN_US = {
                 rightSub: "Correct",
                 shwoPage: "Answer Card",
                 reAns: "Re-answer",
+                entireExam: 'Answer the whole paper',
+                singleQues: 'Answer a single question',
             },
             report: {
                 title: 'Report',

+ 6 - 1
TEAMModelOS/ClientApp/public/lang/zh-CN.js

@@ -1627,7 +1627,7 @@ const LANG_ZH_CN = {
             tip6:'输入题目答案',
             tip7:'切换模式将会清空当前试题数据,是否确认切换?',
             tip13:'切换模式将会清空当前客观题答案,是否确认切换?',
-            tip8:'最多添加50道试题',
+            tip8:'最多添加50道该类型试题',
             tip9:'输入音频题正确答案',
             tip10:'试题数量不可为空!',
             tip11: '单选题答案数与题目数不一致,请检查后重试!',
@@ -1693,6 +1693,7 @@ const LANG_ZH_CN = {
             openAll: '全部展开',
             collapseAll: '全部折叠',
             autoCreate: '智能组卷',
+            emptyPaper:'新建空白卷',
             manualCreate: '挑题组卷',
             importCreate: '文档导入',
             moreAction: '更多操作',
@@ -4704,6 +4705,8 @@ const LANG_ZH_CN = {
             listName: '名单名称',
             joinTime: '申请时间',
             todayWrong: '今日可进行错题练习',
+            top: '置顶',
+            excelTop: '取消置顶',
         },
         public: {
             noData: '暂无数据',
@@ -5251,6 +5254,8 @@ const LANG_ZH_CN = {
                 rightSub: "正确题目",
                 showPage: "答题卡",
                 reAns: "重新作答",
+                entireExam: '整卷作答',
+                singleQues: '单题作答',
             },
             report: {
                 title: '报告',

+ 6 - 1
TEAMModelOS/ClientApp/public/lang/zh-TW.js

@@ -1629,7 +1629,7 @@ const LANG_ZH_TW = {
             ans: '參考答案',
             tip6: '輸入題目答案',
             tip7: '切換模式將會清除目前試題資料,是否確認切換? ',
-            tip8: '最多增加50個試題',
+            tip8: '最多增加50個當前題型試題',
             tip9: '輸入錄音題文字提示',
             tip10: '試題數量不可為0!',
             tip11: '單選題答案數與題目數不一致,請檢查後重試!',
@@ -1696,6 +1696,7 @@ const LANG_ZH_TW = {
             openAll: '全部展開',
             collapseAll: '全部折疊',
             autoCreate: '智慧組卷',
+            emptyPaper: '新建空白卷',
             manualCreate: '挑題組卷',
             importCreate: '導題組卷',
             moreAction: '更多操作',
@@ -4707,6 +4708,8 @@ const LANG_ZH_TW = {
             listName: '名單名稱',
             joinTime: '申請時間',
             todayWrong: '今日可進行的智慧錯題練習',
+            top: '置頂',
+            excelTop: '取消置頂',
         },
         public: {
             noData: '暫無資料',
@@ -5254,6 +5257,8 @@ const LANG_ZH_TW = {
                 rightSub: "正確題目",
                 shwoPage: "答題卡",
                 reAns: "重新作答",
+                entireExam: '整卷作答',
+                singleQues: '單題作答',
             },
             report: {
                 title: '報告',

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

@@ -354,4 +354,11 @@ export default {
     getItsnumSubj: function(data) {
         return post("/student/get-malearn-itsnum-subj", data)
     },
+    // 学生偏好设置
+    getPreference: function(data) {
+        return post("/personal/setting/read", data)
+    },
+    setPreference: function(data) {
+        return post("/personal/setting/upsert", data)
+    },
 }

+ 26 - 3
TEAMModelOS/ClientApp/src/assets/iconfont/demo_index.html

@@ -54,6 +54,12 @@
       <div class="content unicode" style="display: block;">
           <ul class="icon_lists dib-box">
           
+            <li class="dib">
+              <span class="icon iconfont">&#xe6b7;</span>
+                <div class="name">置顶</div>
+                <div class="code-name">&amp;#xe6b7;</div>
+              </li>
+          
             <li class="dib">
               <span class="icon iconfont">&#xe832;</span>
                 <div class="name">录音</div>
@@ -1590,9 +1596,9 @@
 <pre><code class="language-css"
 >@font-face {
   font-family: 'iconfont';
-  src: url('iconfont.woff2?t=1711100580448') format('woff2'),
-       url('iconfont.woff?t=1711100580448') format('woff'),
-       url('iconfont.ttf?t=1711100580448') format('truetype');
+  src: url('iconfont.woff2?t=1717574327458') format('woff2'),
+       url('iconfont.woff?t=1717574327458') format('woff'),
+       url('iconfont.ttf?t=1717574327458') format('truetype');
 }
 </code></pre>
           <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -1618,6 +1624,15 @@
       <div class="content font-class">
         <ul class="icon_lists dib-box">
           
+          <li class="dib">
+            <span class="icon iconfont icon-zhiding"></span>
+            <div class="name">
+              置顶
+            </div>
+            <div class="code-name">.icon-zhiding
+            </div>
+          </li>
+          
           <li class="dib">
             <span class="icon iconfont icon-luyin"></span>
             <div class="name">
@@ -3922,6 +3937,14 @@
       <div class="content symbol">
           <ul class="icon_lists dib-box">
           
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-zhiding"></use>
+                </svg>
+                <div class="name">置顶</div>
+                <div class="code-name">#icon-zhiding</div>
+            </li>
+          
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
                   <use xlink:href="#icon-luyin"></use>

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

@@ -1,8 +1,8 @@
 @font-face {
   font-family: "iconfont"; /* Project id 2000444 */
-  src: url('iconfont.woff2?t=1711100580448') format('woff2'),
-       url('iconfont.woff?t=1711100580448') format('woff'),
-       url('iconfont.ttf?t=1711100580448') format('truetype');
+  src: url('iconfont.woff2?t=1717574327458') format('woff2'),
+       url('iconfont.woff?t=1717574327458') format('woff'),
+       url('iconfont.ttf?t=1717574327458') format('truetype');
 }
 
 .iconfont {
@@ -13,6 +13,10 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-zhiding:before {
+  content: "\e6b7";
+}
+
 .icon-luyin:before {
   content: "\e832";
 }

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


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

@@ -5,6 +5,13 @@
   "css_prefix_text": "icon-",
   "description": "",
   "glyphs": [
+    {
+      "icon_id": "8802640",
+      "name": "置顶",
+      "font_class": "zhiding",
+      "unicode": "e6b7",
+      "unicode_decimal": 59063
+    },
     {
       "icon_id": "6303286",
       "name": "录音",

二進制
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.ttf


二進制
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff


二進制
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff2


+ 7 - 3
TEAMModelOS/ClientApp/src/common/BaseCanvas.vue

@@ -66,7 +66,7 @@
                     </div>
                     <ColorPicker v-model="color" />
                 </div>
-                <canvas id="canvas" :width="width" :height="height"></canvas>
+                <canvas :id="canvasId" :width="width" :height="height"></canvas>
             </div>
             <div class="footer">
                 <Button @click="cancel">{{ vm.$t("utils.cancel") }}</Button>
@@ -96,6 +96,10 @@
 				type:Object,
 				default:() => {}
 			},
+			canvasId:{
+				type:String,
+				default:'canvas'
+			}
 		},
 		data() {
 			return {
@@ -222,7 +226,7 @@
 				this.canvas.discardActiveObject();
 				this.canvas.requestRenderAll();
 				setTimeout(() => {
-					var canvas = document.getElementById('canvas')
+					var canvas = document.getElementById(this.canvasId)
 					var imgData = canvas.toDataURL('png')
 					this.$emit('getImg',imgData)
 					this.$emit('onSaveCanvas', {
@@ -710,7 +714,7 @@
 			})
 			
 			this.$nextTick(() => {
-				this.canvas = new fabric.Canvas("canvas");
+				this.canvas = new fabric.Canvas(this.canvasId);
 				this.canvas.selectionColor = "rgba(0,0,0,0.1)";
 				this.canvas.on("mouse:down", this.mousedown);
 				this.canvas.on("mouse:move", this.mousemove);

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

@@ -21,7 +21,7 @@
             <Icon type="ios-checkmark-circle" color="#17ac5a" size="18" style="margin-right: 5px;" />{{ item.title }}
           </p>
           <p class="item-content">
-            <span>{{ item.content }}</span>
+            <span v-html="item.content"></span>
             <span class="item-time" :title="$tools.formatTime(item.ts * 1000)">{{ getMsgTime(item) }}</span>
           </p>
           <div class="action" v-if="item.label === 'quit_school'">

+ 2 - 2
TEAMModelOS/ClientApp/src/common/BaseQuickPaper.vue

@@ -328,7 +328,7 @@
 			},
 			/* 手动添加试题 */
 			doAddItem(type) {
-				if (this.orderItemsArr.length >= 50) {
+				if (this.orderItemsArr.filter(i => i.type === type).length >= 50) {
 					this.$Message.warning(this.$t("evaluation.quickPaper.tip8"));
 					return;
 				}
@@ -599,7 +599,7 @@
 				}
 				const isLt5M = file.size / 1024 / 1024 < 5;
 				if (!isLt5M) {
-					this.$Message.warning(this.$t('platform.sizeErr') + '5M');
+					this.$Message.warning(this.$t('platform.sizeErr') + ' 5M');
 					return;
 				}
 				let that = this;

+ 1 - 1
TEAMModelOS/ClientApp/src/components/evaluation/ExerciseList.less

@@ -178,7 +178,7 @@
     .item-question-order {
       display: inline-block;
       vertical-align: top;
-      width: 30px;
+      width: 32px;
     }
 
     .item-question-text {

+ 3 - 0
TEAMModelOS/ClientApp/src/components/evaluation/ExerciseList.vue

@@ -582,6 +582,9 @@
 					setTimeout(() => {
 						this.dataLoading = false;
 					}, 1000);
+					window.MathJax.startup.promise.then(() => {
+						window.MathJax.typesetPromise([this.$refs.mathJaxContainer])
+					})
 				} catch (e) {
 					console.log(e);
 					this.dataLoading = false;

+ 1 - 1
TEAMModelOS/ClientApp/src/components/selflearn/ExerciseList.less

@@ -144,7 +144,7 @@
       .item-question-order {
         display: inline-block;
         vertical-align: top;
-        width: 30px;
+        width: 32px;
       }
       .item-question-text {
         display: inline-block;

+ 3 - 0
TEAMModelOS/ClientApp/src/components/selflearn/ExerciseList.vue

@@ -819,6 +819,9 @@ export default {
                 // 执行试题换取完整JSON数据
                 this.exerciseList = await this.$evTools.getFullItem(simpleList)
                 this.currentPage = page
+                window.MathJax.startup.promise.then(() => {
+                    window.MathJax.typesetPromise([this.$refs.mathJaxContainer])
+                })
             } catch (e) {
                 console.log(e)
             }

+ 6 - 2
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperViewBox/LessonTestReport.vue

@@ -153,10 +153,10 @@
                     当前试卷为乱序作答,请对照选项内容
                 </div>
             </div> -->
-            <div class="qcontent img-question" ref="qcontent" v-if="paperData.length && (examInfo.qamode || paperInfo.qamode === 1)">
+            <div class="qcontent img-question" ref="mathJaxContainer" v-if="paperData.length && (examInfo.qamode || paperInfo.qamode === 1)">
                 <SubjectMould :subjectList="showPaperData" :imgList="imgList" :testState="testState" :onPreview="onPreview" :onDownload="onDownload" />
             </div>
-            <div class="qcontent" ref="qcontent" v-else-if="newPaperData.length">
+            <div class="qcontent" ref="mathJaxContainer" v-else-if="newPaperData.length">
                 <div v-for="(item, index) in newPaperData" :key="index" style="margin-bottom: 30px;">
                     <p class="exam-type" style="margin-bottom: 20px;"
                         v-show="(checkedAns.includes('right') && item.rightNum) || (checkedAns.includes('wrong') && item.wrongNum)"
@@ -828,6 +828,10 @@
                         return item
                     })
                 }
+                
+                window.MathJax.startup.promise.then(() => {
+                    window.MathJax.typesetPromise([this.$refs.mathJaxContainer])
+                })
             },
             getFileType(fileUrl) {
                 let file = fileUrl || ''

+ 48 - 22
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperViewBox/PaperTest.less

@@ -168,26 +168,6 @@
             display: none;
             text-align: center;
         }
-
-        .img-wrap {
-            width: 40%;
-            margin-left: 10px;
-            margin-top: 20px;
-
-            .left-img {
-                text-align: center;
-
-                &>img {
-                    max-width: 95%;
-                }
-            }
-
-            .page-footer {
-                text-align: center;
-                padding: 10px 0;
-            }
-        }
-
     }
 
     .repair-link-wrap-item-box {
@@ -252,6 +232,7 @@
         .qamode-box {
             width: 100%;
             // margin-bottom: 20px;
+            padding: 5px 50px;
         }
 
         .questionNo {
@@ -305,7 +286,7 @@
     }
 
     .questionContent {
-        padding: 5px 50px;
+        padding: 5px 50px 50px;
     }
 
     .qamode-box {
@@ -693,6 +674,51 @@
         margin-left: 7%;
         margin-bottom: 10%;
     }
+
+
+
+    .test-content {
+        display: flex;
+        justify-content: space-between;
+        height: calc(100% - 40px);
+
+        .attachment-box {
+            width: 60%;
+
+            .img-wrap {
+                /* width: 40%;
+                margin-left: 10px; */
+                margin-top: 20px;
+
+                .left-img {
+                    text-align: center;
+
+                    &>img {
+                        max-width: 95%;
+                        cursor: pointer;
+                    }
+                }
+
+                .page-footer {
+                    text-align: center;
+                    padding: 10px 0;
+                }
+            }
+        }
+
+        .question-content {
+            width: 79%;
+            background-color: #fff;
+            height: 100%;
+        }
+
+        .answer-sheet {
+            width: 20%;
+            padding-left: 10px;
+            background-color: #fafafa;
+            box-shadow: -5px 2px 13px rgba(0, 0, 0, 0.1);
+        }
+    }
 }
 
 .top-icon {
@@ -942,7 +968,7 @@
 
     .que-item {
         width: 100%;
-        max-height: 200px;
+        // max-height: 200px;
         overflow-y: scroll;
     }
 }

+ 229 - 205
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperViewBox/PaperTest.vue

@@ -139,237 +139,237 @@
                 <button v-if="!closeTest" class="submitBtn" :class="{ hintClick: hintHandon()}">{{$t("studentWeb.exam.testpop.finish")}}</button>
             </div>
         </div>
-        <Row :gutter="30" v-if="instantPaper" style="height: calc(100% - 40px);" class="instant-paper-box">
-            <i-col class="instant-paper" :xs="24" :sm="24" :md="12" :lg="14">
+        <div class="test-content" ref="mathJaxContainer">
+            <!-- 附件展示 -->
+            <div v-if="instantPaper" class="attachment-box">
                 <vuescroll>
                     <div class="img-wrap">
-                        <div v-for="(img, index) in imgList" :key="index" class="left-img" @click="$hevueImgPreview(img)">
-                            <img :src="img" alt="">
+                        <div v-for="(img, index) in imgList" :key="index" class="left-img">
+                            <img :src="img" alt="" @click="$hevueImgPreview(img)">
                         </div>
                     </div>
                 </vuescroll>
-            </i-col>
-            <i-col class="instant-exam" :xs="24" :sm="24" :md="12" :lg="10">
+            </div>
+            <!-- 题目卷动 -->
+            <div v-if="instantPaper || entireExam" :class="['question-content', instantPaper ? 'instant-exam' : '']" :style="{'width': instantPaper ? '40%' : '79%'}">
                 <div v-if="!showExam.length" style="text-align: center; margin-top: 25%; font-size: 20px;">
                     {{ $t("studentWeb.exam.testpop.noExam") }}
                 </div>
-                <template v-else>
-                    <vuescroll>
-                        <div class="instant-exam-box">
-                            <div class="questionContent qamode-box" ref="questionBox" v-for="(item, index) in showExam" :key="index">
-                                <div class="questioDes">
+                <vuescroll>
+                    <div class="instant-exam-box">
+                        <div :class="['questionContent', instantPaper ? 'qamode-box' : '' ]" ref="questionBox" v-for="(item, index) in showExam" :key="index">
+                            <div class="questioDes">
+                                <template v-if="instantPaper">
                                     <div class="que-item" v-if="item.type != 'compose' && item.parent === undefined">
                                         <span v-if="isWrong" @click="changeStar(!item.star, index)" style="margin-right: 10px; cursor: pointer;">
                                             <Icon custom="iconfont icon-shoucang1" size="25" v-show="!item.star" />
                                             <Icon custom="iconfont icon-shoucang2" size="25" color="#FF7A4E" v-show="item.star" />
                                         </span>
                                         <span v-if="!['single', 'multiple', 'judge'].includes(item.type)">{{ index + 1 }}. </span>
-                                        <div class="questionType"
-                                            v-if="item.parent === undefined && (index === 0 ? true :
+                                        <div class="questionType" v-if="item.parent === undefined && (index === 0 ? true :
                                             ((item.type === 'subjective' && showExam[index-1].type === 'subjective') ? item.answerType != showExam[index-1].answerType : item.type != showExam[index-1].type))"
                                         >
                                             <span>{{getTestType(item.type)}}</span>
                                             <span v-if="item.type === 'subjective' && item.answerType">-{{ $t(`evaluation.newExercise.answerType.${item.answerType}`) }}</span>
                                         </div>
+                                    </div>
+                                </template>
+                                <template v-else>
+                                    <div class="questionType" v-if="item.parent === undefined">
+                                        <span>{{getTestType(item.type)}}</span>
+                                        <span v-if="item.type === 'subjective' && showExam[index].answerType">-{{ $t(`evaluation.newExercise.answerType.${showExam[index].answerType}`) }}</span>
+                                    </div>
+                                    <div class="que-item" v-if="item.type != 'compose' && item.parent === undefined">
+                                        <span v-if="isWrong" @click="changeStar(!item.star, index)" style="margin-right: 10px; cursor: pointer;">
+                                            <Icon custom="iconfont icon-shoucang1" size="25" v-show="!item.star" />
+                                            <Icon custom="iconfont icon-shoucang2" size="25" color="#FF7A4E" v-show="item.star" />
+                                        </span>
+                                        <span>{{ index + 1 }}.</span>
                                         <!--题目渲染-->
-                                        <!-- <div id="answer-box" v-html="item.question"></div> -->
+                                        <div id="answer-box" v-html="item.question"></div>
                                     </div>
-                                    <!-- 无综合题 -->
-                                </div>
-                                <div class="answers-box">
-                                    <!--判断题选项-->
-                                    <div v-if="item.type == 'judge'" align="center">
-                                        <span style="font-weight: 900;">{{ index + 1 }}. </span>
-                                        <label class="testBtn yesNoBtn">
-                                            <input type="radio" value="A" v-model="checkers[index][0]" :disabled="!closeTest" />
-                                            <div class="testbg" :style="{'background-color': isWrong && showEnd && checkers[index][0] === 'A' ? (item.answer[0] === 'A' ? '' : 'red !important') : ''}">
-                                                <!-- <Icon type="ios-radio-button-off" /> -->
-                                                <Icon type="md-checkmark" />
+                                    <!-- 综合题 -->
+                                    <div v-if="item.type === 'compose' || item.parent !== undefined">
+                                        <div class="compose-content" v-if="item.parent !== undefined">
+                                            <div class="questionType" v-show="entireExam ? item.pid != showExam[index-1].pid : true">
+                                                <span>{{getTestType(item.parentInfo.type)}}</span>
+                                                <span v-if="item.type === 'subjective' && showExam[index].answerType">({{ $t(`evaluation.newExercise.answerType.${showExam[index].answerType}`) }})</span>
                                             </div>
-                                        </label>
-                                        <label class="testBtn yesNoBtn">
-                                            <input type="radio" value="B" v-model="checkers[index][0]" :disabled="!closeTest" />
-                                            <div class="testbg" :style="{'background-color': isWrong && showEnd && checkers[index][0] === 'B' ? (item.answer[0] === 'B' ? '' : 'red !important') : ''}">
-                                                <Icon type="md-close" />
+                                            <div class="compose-box" v-show="entireExam ? item.pid != showExam[index-1].pid : true">
+                                                <span v-if="isWrong" @click="changeStar(!item.star, index, true)" style="cursor: pointer;">
+                                                    <Icon custom="iconfont icon-shoucang1" size="25" v-show="!item.star" />
+                                                    <Icon custom="iconfont icon-shoucang2" size="25" color="#FF7A4E" v-show="item.star" />
+                                                </span>
+                                                <span style="margin-left:10px;font-weight:800;font-size:15px">{{$t("studentWeb.exam.testpop.queNo")}}</span>
+                                                <br />
+                                                <div class="compose-item">
+                                                    <!--<span w>{{ item.parent + 1}}.</span>-->
+                                                    <!--题干渲染-->
+                                                    <div v-html="item.parentInfo.question"></div>
+                                                </div>
+                                            </div>
+                                            <div class="que-content">
+                                                <span style="width:51px">{{ item.paperIndex}}:</span>
+                                                <!--题目渲染-->
+                                                <div class="que-items" id="answer-box" v-html="item.question"></div>
                                             </div>
-                                        </label>
+                                        </div>
                                     </div>
-                                    <!--选择题选项-->
-                                    <div class="select-box" v-else-if="item.type === 'single' || item.type === 'multiple'">
-                                        <span style="font-weight: 900;">{{ index + 1 }}. </span>
-                                        <label class="testBtn" v-for="(option, oIndex) in item.option" :key="oIndex">
-                                            <input type="checkbox" :value="option.code" v-model="checkers[index]" @click="getAns(index, oIndex)" :disabled="!closeTest" />
-                                            <div class="testbg">
-                                                <div style="display:flex">
-                                                    <span>{{ option.code }}</span>
-                                                    <div v-html="option.value" @click.stop.native.prevent="showImg($event)"></div>
-                                                    <span v-show="showEnd" style="margin-left: 5px;">
-                                                        <Icon type="md-checkmark-circle" color="#44b5f9" size="20" v-if="item.answer.includes(option.code)" />
-                                                        <Icon type="md-close-circle" color="red" size="20" v-if="checkers[index].includes(option.code) && !item.answer.includes(option.code)" />
+                                </template>
+                            </div>
+                            <div class="answers-box" :style="{'margin-top': instantPaper ? 'auto': '20px'}">
+                                <div class="questionNo" v-if="!instantPaper">{{$t("studentWeb.exam.testpop.myAns")}}</div>
+                                <!--判断题选项-->
+                                <div v-if="item.type == 'judge'" align="center">
+                                    <span style="font-weight: 900;" v-if="instantPaper">{{ index + 1 }}. </span>
+                                    <label class="testBtn yesNoBtn">
+                                        <input type="radio" value="A" v-model="checkers[index][0]" :disabled="!closeTest" />
+                                        <div class="testbg" :style="{'background-color': isWrong && showEnd && checkers[index][0] === 'A' ? (item.answer[0] === 'A' ? '' : 'red !important') : ''}">
+                                            <!-- <Icon type="ios-radio-button-off" /> -->
+                                            <Icon type="md-checkmark" />
+                                        </div>
+                                    </label>
+                                    <label class="testBtn yesNoBtn">
+                                        <input type="radio" value="B" v-model="checkers[index][0]" :disabled="!closeTest" />
+                                        <div class="testbg" :style="{'background-color': isWrong && showEnd && checkers[index][0] === 'B' ? (item.answer[0] === 'B' ? '' : 'red !important') : ''}">
+                                            <Icon type="md-close" />
+                                        </div>
+                                    </label>
+                                </div>
+                                <!--选择题选项-->
+                                <div class="select-box" v-else-if="item.type === 'single' || item.type === 'multiple'">
+                                    <span style="font-weight: 900;" v-if="instantPaper">{{ index + 1 }}. </span>
+                                    <label class="testBtn" v-for="(option, oIndex) in item.option" :key="oIndex">
+                                        <input type="checkbox" :value="option.code" v-model="checkers[index]" @click="getAns(index, oIndex)" :disabled="!closeTest" />
+                                        <div class="testbg">
+                                            <div style="display:flex">
+                                                <span>{{ option.code }}</span>
+                                                <div v-html="option.value" @click.stop.native.prevent="showImg($event)"></div>
+                                                <span v-show="showEnd" style="margin-left: 5px;">
+                                                    <Icon type="md-checkmark-circle" color="#44b5f9" size="20" v-if="item.answer.includes(option.code)" />
+                                                    <Icon type="md-close-circle" color="red" size="20" v-if="checkers[index].includes(option.code) && !item.answer.includes(option.code)" />
+                                                </span>
+                                            </div>
+                                            <div style="clear:both"></div>
+                                        </div>
+                                    </label>
+                                </div>
+                                <!--问答题-->
+                                <div class="compose-content" v-else-if="item.type === 'subjective'">
+                                    <!-- item.answerType: text(文本) Image(图片) file(文件) audio(音频 useAutoScore(自动评分) answerLang(语言)) -->
+                                    <!-- <template v-if="item.answerType === 'text' || item.answerType === 'text_Image'"> -->
+                                    <Input v-if="item.answerType === 'text'" v-model="checkers[index][0]" type="textarea" :rows="5" :autosize="{minRows: 5,maxRows: 10}" :placeholder="$t('studentWeb.exam.inputAnswers')"></Input>
+                                    <Compose v-else-if="item.answerType === 'text_Image'" ref="compose" :itemInfo="item" :close="!closeTest" :textData="checkers[index]" :index="index" @dataGet="getComposeAns"></Compose>
+                                    <!-- </template> -->
+                                    <template v-else-if="item.answerType === 'audio'">
+                                        <AudioRecorder :textData="checkers[index]" :index="index" @dataGet="getComposeAns" />
+                                    </template>
+                                    <template v-else-if="item.answerType === 'file' || item.answerType === 'image'">
+                                        <Upload type="drag" :accept="subjectiveAccept(item)" action="" :before-upload="file => customUpload(file, index)">
+                                            <div style="padding: 20px 0" ref="upload1">
+                                                <Icon type="ios-cloud-upload" size="52" :style="{color: checkers[index].length ? '#b4b4b4' : '#2d8cf0' }"></Icon>
+                                                <p>
+                                                    <span>
+                                                        {{ checkers[index].length ? $t('jyzx.offline.againLoad') : $t('studentWeb.art.relatedFile')}}
                                                     </span>
-                                                </div>
-                                                <div style="clear:both"></div>
+                                                </p>
                                             </div>
-                                        </label>
-                                    </div>
-                                    <!--问答题-->
-                                    <div class="compose-content" v-else-if="item.type === 'subjective'">
-                                        <!-- item.answerType: text(文本) Image(图片) file(文件) audio(音频 useAutoScore(自动评分) answerLang(语言)) -->
-                                        <!-- <template v-if="item.answerType === 'text' || item.answerType === 'text_Image'"> -->
-                                        <Input v-if="item.answerType === 'text'" v-model="checkers[index][0]" type="textarea" :rows="5" :autosize="{minRows: 5,maxRows: 10}" :placeholder="$t('studentWeb.exam.inputAnswers')"></Input>
-                                        <Compose v-else-if="item.answerType === 'text_Image'" ref="compose" :itemInfo="item" :close="!closeTest" :textData="checkers[index]" :index="index" @dataGet="getComposeAns"></Compose>
-                                        <!-- </template> -->
-                                        <template v-else-if="item.answerType === 'audio'">
-                                            <AudioRecorder :textData="checkers[index]" :index="index" @dataGet="getComposeAns" />
-                                        </template>
-                                        <template v-else-if="item.answerType === 'file' || item.answerType === 'image'">
-                                            <Upload type="drag" :accept="subjectiveAccept(item)" action="" :before-upload="file => customUpload(file, index)">
-                                                <div style="padding: 20px 0" ref="upload1">
-                                                    <Icon type="ios-cloud-upload" size="52" :style="{color: checkers[index].length ? '#b4b4b4' : '#2d8cf0' }"></Icon>
-                                                    <p>
-                                                        <span>
-                                                            {{ checkers[index].length ? $t('jyzx.offline.againLoad') : $t('studentWeb.art.relatedFile')}}
-                                                        </span>
-                                                    </p>
-                                                </div>
-                                            </Upload>
-                                            <div class="repair-link-wrap-item-box" v-if="checkers[index].length">
-                                                <div class="file-icon">
-                                                    <img :src="$tools.getFileThum(getFileType(checkers[index][0]), getFileName(checkers[index][0]))"/>
-                                                </div>
-                                                <div class="file-info">
-                                                    <p class="file-name">{{ getFileName(checkers[index][0]) }}</p>
-                                                    <div>
-                                                        <span @click="onPreview(checkers[index][0], true)">{{ $t('ability.review.preview')}}</span>
-                                                        <span @click="onDownload(checkers[index][0], true)">{{ $t('ability.review.download')}}</span>
-                                                    </div>
+                                        </Upload>
+                                        <div class="repair-link-wrap-item-box" v-if="checkers[index].length">
+                                            <div class="file-icon">
+                                                <img :src="$tools.getFileThum(getFileType(checkers[index][0]), getFileName(checkers[index][0]))"/>
+                                            </div>
+                                            <div class="file-info">
+                                                <p class="file-name">{{ getFileName(checkers[index][0]) }}</p>
+                                                <div>
+                                                    <span @click="onPreview(checkers[index][0], true)">{{ $t('ability.review.preview')}}</span>
+                                                    <span @click="onDownload(checkers[index][0], true)">{{ $t('ability.review.download')}}</span>
                                                 </div>
                                             </div>
-                                        </template>
-                                        <div class="compose-content" v-else>
-                                            <Compose ref="compose" :itemInfo="item" :close="!closeTest" :textData="checkers[index]" :index="index" @dataGet="getComposeAns"></Compose>
                                         </div>
-                                    </div>
+                                    </template>
                                     <div class="compose-content" v-else>
                                         <Compose ref="compose" :itemInfo="item" :close="!closeTest" :textData="checkers[index]" :index="index" @dataGet="getComposeAns"></Compose>
                                     </div>
                                 </div>
-                                <!-- <div :style="isCheckAns ? 'height: 250px' : ''"></div> -->
-                                <!-- 答案解析 -->
-                                <div v-if="isWrong && showEnd" :class="['analysis', showEnd ? 'active' : '']" @click="changeAnalysisType(true)">
-                                    <div class="item-explain">
-                                        <span class="explain-title">【{{ $t("studentWeb.exam.report.ansRes") }}】</span>
-                                        <div class="item-explain-details">
-                                            <span v-if="!checkers[index].length">{{ $t('studentWeb.exam.report.noAns') }}</span>
-                                            <span v-else v-for="(item, index) in checkers[index]" :key="index" v-html="item"></span>
-                                        </div>
+                                <div class="compose-content" v-else>
+                                    <Compose ref="compose" :itemInfo="item" :close="!closeTest" :textData="checkers[index]" :index="index" @dataGet="getComposeAns"></Compose>
+                                </div>
+                            </div>
+                            <!-- <div :style="isCheckAns ? 'height: 250px' : ''"></div> -->
+                            <!-- 答案解析 -->
+                            <div v-if="isWrong && showEnd" :class="['analysis', showEnd ? 'active' : '']" @click="changeAnalysisType(true)">
+                                <div class="item-explain">
+                                    <span class="explain-title">【{{ $t("studentWeb.exam.report.ansRes") }}】</span>
+                                    <div class="item-explain-details">
+                                        <span v-if="!checkers[index].length">{{ $t('studentWeb.exam.report.noAns') }}</span>
+                                        <span v-else v-for="(item, index) in checkers[index]" :key="index" v-html="item"></span>
                                     </div>
-                                    <div class="item-explain">
-                                        <span class="explain-title">【{{ $t("studentWeb.exam.report.testAns") }}】</span>
-                                        <div class="item-explain-details">
-                                            <span v-if="!item.answer.length">{{ $t('studentWeb.exam.report.noAns') }}</span>
-                                            <span v-else v-for="(item, index) in item.answer" :key="index" v-html="item"></span>
-                                        </div>
+                                </div>
+                                <div class="item-explain">
+                                    <span class="explain-title">【{{ $t("studentWeb.exam.report.testAns") }}】</span>
+                                    <div class="item-explain-details">
+                                        <span v-if="!item.answer.length">{{ $t('studentWeb.exam.report.noAns') }}</span>
+                                        <span v-else v-for="(item, index) in item.answer" :key="index" v-html="item"></span>
                                     </div>
-                                    <div class="item-explain">
-                                        <span class="explain-title">【{{ $t("studentWeb.exam.report.testAnalyse") }}】</span>
-                                        <div class="item-explain-details">
-                                            <div v-html="item.explain ? item.explain : $t('studentWeb.exam.report.noAnalyse')"></div>
-                                        </div>
+                                </div>
+                                <div class="item-explain">
+                                    <span class="explain-title">【{{ $t("studentWeb.exam.report.testAnalyse") }}】</span>
+                                    <div class="item-explain-details">
+                                        <div v-html="item.explain ? item.explain : $t('studentWeb.exam.report.noAnalyse')"></div>
                                     </div>
-                                    <!-- 知识点 -->
-                                    <div class="item-explain">
-                                        <span class="explain-title">【{{ $t("studentWeb.exam.report.knowledge") }}】</span>
-                                        <div v-if="item.knowledge && item.knowledge.length" class="item-explain-details">
-                                            <span v-for="(knowledge, index) in item.knowledge" :key="index" class="knowledge-style">
-                                                {{ knowledge }}
-                                            </span>
-                                        </div>
-                                        <div v-else class="item-explain-details">
-                                            {{ $t("studentWeb.exam.report.noKnowledge") }}
-                                        </div>
+                                </div>
+                                <!-- 知识点 -->
+                                <div class="item-explain">
+                                    <span class="explain-title">【{{ $t("studentWeb.exam.report.knowledge") }}】</span>
+                                    <div v-if="item.knowledge && item.knowledge.length" class="item-explain-details">
+                                        <span v-for="(knowledge, index) in item.knowledge" :key="index" class="knowledge-style">
+                                            {{ knowledge }}
+                                        </span>
                                     </div>
-                                    <!-- 认知层次 -->
-                                    <div class="item-explain">
-                                        <span class="explain-title">【{{ $t("studentWeb.exam.report.filed") }}】</span>
-                                        <div class="item-explain-details">
-                                            <div>{{ levelList[item.field - 1] }}</div>
-                                        </div>
+                                    <div v-else class="item-explain-details">
+                                        {{ $t("studentWeb.exam.report.noKnowledge") }}
                                     </div>
-                                    <div class="item-explain">
-                                        <span class="explain-title">【{{ $t("studentWeb.exam.report.repairSource") }}】</span>
-                                        <div class="item-explain-details-repair">
-                                            <div v-if="item.repair.length != 0">
-                                                <div v-for="(repairSource, normalIndex) in item.repair" :key="normalIndex" class="repair-link-wrap-item-box">
-                                                    <div class="file-icon">
-                                                        <img :src="$tools.getFileThum(repairSource.type, repairSource.name)"/>
-                                                    </div>
-                                                    <div class="file-info">
-                                                        <p class="file-name">{{ repairSource.name }}</p>
-                                                        <div>
-                                                            <span @click.stop="onPreview(repairSource)" v-if="repairSource.type !== 'other'">{{ $t('ability.review.preview')}}</span>
-                                                            <span @click.stop="onDownload(repairSource)" v-if="repairSource.type !== 'link'">{{ $t('ability.review.download')}}</span>
-                                                        </div>
+                                </div>
+                                <!-- 认知层次 -->
+                                <div class="item-explain">
+                                    <span class="explain-title">【{{ $t("studentWeb.exam.report.filed") }}】</span>
+                                    <div class="item-explain-details">
+                                        <div>{{ levelList[item.field - 1] }}</div>
+                                    </div>
+                                </div>
+                                <div class="item-explain">
+                                    <span class="explain-title">【{{ $t("studentWeb.exam.report.repairSource") }}】</span>
+                                    <div class="item-explain-details-repair">
+                                        <div v-if="item.repair.length != 0">
+                                            <div v-for="(repairSource, normalIndex) in item.repair" :key="normalIndex" class="repair-link-wrap-item-box">
+                                                <div class="file-icon">
+                                                    <img :src="$tools.getFileThum(repairSource.type, repairSource.name)"/>
+                                                </div>
+                                                <div class="file-info">
+                                                    <p class="file-name">{{ repairSource.name }}</p>
+                                                    <div>
+                                                        <span @click.stop="onPreview(repairSource)" v-if="repairSource.type !== 'other'">{{ $t('ability.review.preview')}}</span>
+                                                        <span @click.stop="onDownload(repairSource)" v-if="repairSource.type !== 'link'">{{ $t('ability.review.download')}}</span>
                                                     </div>
                                                 </div>
                                             </div>
-                                            <div v-else-if="!item.repair.length">
-                                                {{ $t('studentWeb.exam.report.noSource') }}
-                                            </div>
+                                        </div>
+                                        <div v-else-if="!item.repair.length">
+                                            {{ $t('studentWeb.exam.report.noSource') }}
                                         </div>
                                     </div>
                                 </div>
                             </div>
                         </div>
-                    </vuescroll>
-                </template>
-            </i-col>
-        </Row>
-        <Row :gutter="30" v-else>
-            <!-- 题干 -->
-            <i-col :class="['questionArea', {'qamode-area': instantPaper}]"
-                   :xs="24"
-                   :sm="24"
-                   :md="18"
-                   :lg="20"
-            >
-                <div class="img-wrap" v-if="instantPaper">
-                    <Carousel v-model="CarouselIndex">
-                        <CarouselItem v-for="(img, indexs) in imgList" :key="indexs">
-                            <!-- 不是综合题 -->
-                            <div class="left-img" @click="$hevueImgPreview(img)">
-                                <img :src="img" alt="">
-                            </div>
-                        </CarouselItem>
-                    </Carousel>
-                    <p class="page-footer">
-                        {{ $t('answerSheet.tip2') }} {{ CarouselIndex + 1 }} {{ $t('answerSheet.tip21') }}
-                        /
-                        {{ $t('answerSheet.tip9') }} {{ imgList.length }} {{ $t('answerSheet.tip21') }}
-                    </p>
-                </div>
-                <template>
-                    <!--切換頁-->
-                    <div class="small-view">
-                        <div class="countDown" v-if="needCountDown">
-                            <div>{{$t("studentWeb.exam.examTime")}}:{{ surplus }}</div>
-                        </div>
-                        <div class="pageCtl2">
-                            <button @click="preQ()" :disabled="!queNo" :class="{disable: !queNo}">
-                                <Icon type="ios-arrow-back" />{{$t("studentWeb.exam.testpop.previous")}}
-                            </button>
-                            <button @click="showPage(true)">
-                                {{$t("studentWeb.exam.testpop.showPage")}}
-                            </button>
-                            <button @click="nextQ()"
-                                    :disabled="queNo == (showExam.length-1)"
-                                    :class="{ hintClick:queNo != (showExam.length-1) && checkers[queNo] != '',disable: queNo == (showExam.length-1)}">
-                                {{$t("studentWeb.exam.testpop.next")}}
-                                <Icon type="ios-arrow-forward" />
-                            </button>
-                        </div>
                     </div>
-                    <div :class="['questionContent', {'qamode-box': instantPaper}]" ref="questionBox" v-if="showExam.length">
+                </vuescroll>
+            </div>
+            <!-- 单题展示 -->
+            <div v-if="!instantPaper && !entireExam" class="question-content">
+                <div v-if="!showExam.length" style="text-align: center; margin-top: 25%; font-size: 20px;">{{ $t("studentWeb.exam.testpop.noExam") }}</div>
+                <div :class="['questionContent', {'qamode-box': instantPaper}]" ref="questionBox" style="height: 100%;" v-else>
+                    <vuescroll>
                         <!-- <span class="hintwrap">
                             <Tooltip :content="$t('studentWeb.exam.report.noSource')" theme="light" placement="left">
                                 <span class="hintIcon">
@@ -579,24 +579,25 @@
                                 </div>
                             </div>
                         </div>
-                    </div>
-                    <div v-else style="text-align: center; margin-top: 25%; font-size: 20px;">{{ $t("studentWeb.exam.testpop.noExam") }}</div>
-                </template>
-            </i-col>
-            <!--答题卡-->
-            <i-col class="ansArea" v-show="widthLimit || (!widthLimit && isShow)" v-if="!instantPaper"
-                   :xs="24"
-                   :sm="12"
-                   :md="6"
-                   :lg="4">
+                    </vuescroll>
+                </div>
+            </div>
+            <!-- 答题卡 -->
+            <div v-if="!instantPaper" class="answer-sheet">
                 <div class="countDown" v-if="needCountDown">
                     <div>{{$t("studentWeb.exam.examTime")}}&nbsp;</div>
                     <span>{{ surplus }}</span>
                 </div>
                 <div v-if="widthLimit">
-                    <div style="margin-top: 30px; font-weight: 800">{{$t("studentWeb.exam.testpop.myAnswerSheet")}}</div>
+                    <div style="margin-top: 30px; display: flex; justify-content: space-between; align-items: center;">
+                        <span style="font-weight: 800;">{{ $t("studentWeb.exam.testpop.myAnswerSheet") }}</span>
+                        <div style="font-size: 14px; margin-right: 10px;">
+                            <i-switch v-model="entireExam" size="small" />
+                            <span>{{ entireExam ? $t("studentWeb.exam.testpop.entireExam") : $t("studentWeb.exam.testpop.previsingleQuesous") }}</span>
+                        </div>
+                    </div>
                     <!--切換頁-->
-                    <div class="pageCtl2">
+                    <div class="pageCtl2" v-show="!entireExam">
                         <button @click="preQ()" :disabled="!queNo" :class="{disable: !queNo}">
                             <Icon type="ios-arrow-back" />{{$t("studentWeb.exam.testpop.previous")}}
                         </button>
@@ -644,9 +645,9 @@
                 <div v-show="getItemTitle.type === 'Art'" style="width: 100%; margin-top: 10px;">
                     <Button v-if="closeTest && !isWrong" long class="artsubmitBtn" :class="{ hintClick: hintHandon() }" @click="openWarmMessage(2)">{{ $t("studentWeb.exam.testpop.submitted")}}</Button>
                 </div>
-            </i-col>
-        </Row>
-        <div class="top-icon" @click="gotoTop" v-if="instantPaper">
+            </div>
+        </div>
+        <div class="top-icon" @click="gotoTop" v-if="instantPaper || entireExam">
             <Icon type="ios-arrow-up" size="30" color="#249e35" />
         </div>
         <!-- 补救资源 -->
@@ -846,7 +847,8 @@
                 noExam: false,
                 maxSize: 15*1024*1024, //文件最大
                 sasUpload: undefined,
-                fileTypes: ['JPG', 'JPEG', 'BMP', 'TIF', 'PNG', 'GIF', 'SVG', 'MP4', 'DVD', 'MPEG2', 'MPEG4', 'PPT', 'PPTX', 'TXT', 'DOC', 'DOCX', 'PDF', 'XLS', 'XLSX', 'MP3', 'OGG', 'WAV']
+                fileTypes: ['JPG', 'JPEG', 'BMP', 'TIF', 'PNG', 'GIF', 'SVG', 'MP4', 'DVD', 'MPEG2', 'MPEG4', 'PPT', 'PPTX', 'TXT', 'DOC', 'DOCX', 'PDF', 'XLS', 'XLSX', 'MP3', 'OGG', 'WAV'],
+                entireExam: false,
             };
         },
         methods: {
@@ -1017,6 +1019,8 @@
                 this.showExam = this.examInfo
                 this.noExam = this.showExam.length ? false : true
                 this.isLoading = false
+                
+                this.dfMathJax()
             },
             async getImgList() {
                 if(!this.getPaperInfo.attachments) {
@@ -1119,15 +1123,21 @@
             // 点击答题卡跳转
             gototheQues(index) {
                 this.queNo = index - 1
-                // 页面宽度<576,才会调用
-                if(!this.widthLimit) {
-                    this.showPage(false)
+                if(this.entireExam) {
+                    document.getElementsByClassName("questionContent")[index - 1].scrollIntoView()
+                } else {
+                    // 页面宽度<576,才会调用
+                    if(!this.widthLimit) {
+                        this.showPage(false)
+                    }
+                    this.dfMathJax()
+                    // this.changeAnalysisType(false)
                 }
-                // this.changeAnalysisType(false)
             },
             preQ() {
                 if (this.queNo > 0) {
                     this.queNo--
+                    this.dfMathJax()
                     // this.changeAnalysisType(false)
                 }
             },
@@ -1137,6 +1147,7 @@
             nextQ() {
                 if (this.queNo < (this.showExam.length - 1)) {
                     this.queNo++
+                    this.dfMathJax()
                     // this.changeAnalysisType(false)
                 }
             },
@@ -1319,6 +1330,12 @@
                     this.$hevueImgPreview(e.target.src)
                 }
             },
+            // 处理多分数学公式
+            dfMathJax() {
+                window.MathJax.startup.promise.then(() => {
+                    window.MathJax.typesetPromise([this.$refs.mathJaxContainer])
+                })
+            },
             // 错题模式下的解析
             changeAnalysisType(type) {
                 if (this.isWrong) {
@@ -1529,7 +1546,9 @@
                 return type
             },
             gotoTop() {
-                document.getElementsByClassName("img-wrap")[0].scrollIntoView()
+                if(this.instantPaper) {
+                    document.getElementsByClassName("img-wrap")[0].scrollIntoView()
+                }
                 document.getElementsByClassName("instant-exam-box")[0].scrollIntoView()
             },
             
@@ -1587,6 +1606,11 @@
                     }
                 }
             },
+            entireExam: {
+                handler(newV, o) {
+                    this.dfMathJax()
+                },
+            },
             /* papers: {
                 async handler(newV, oldV) {
                     if (newV.item.length) {

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

@@ -11,12 +11,12 @@
       <p v-if="answerImg" style="font-weight: bold; margin-bottom: 20px">
         {{ $t("studentWeb.exam.testpop.reAns") }}
       </p>
-      <BaseCanvas :vm="vm" :bgImg="markBg" :isStudent="markStatus" @onCloseModal="closeModal" @onSaveCanvas="saveMark"></BaseCanvas>
+      <BaseCanvas :canvasId="'canvas' + index" :vm="vm" :bgImg="markBg" :isStudent="markStatus" @onCloseModal="closeModal" @onSaveCanvas="saveMark"></BaseCanvas>
     </div>
     <Modal v-model="markStatus" fullscreen :title="$t('studentWeb.exam.answer')" footer-hide ref="compose">
       <BaseCanvas v-if="markStatus" :vm="vm" :bgImg="markBg" :isStudent="markStatus" @onCloseModal="closeModal" @onSaveCanvas="saveMark"></BaseCanvas>
     </Modal>
-    <iframe class="frame" id="answerIframe" :srcdoc="itemInfo.question"></iframe>
+    <iframe class="frame" :id="'answerIframe' + index" :srcdoc="itemInfo.question"></iframe>
   </div>
 </template>
 <script>
@@ -174,17 +174,17 @@ export default {
     },
     markStuAnswer() {
       let answerIframe = ""
-      answerIframe = document.getElementById("answerIframe")
+      answerIframe = document.getElementById(`answerIframe${this.index}`)
+      console.log(
+        document.getElementById(`answerIframe${this.index}`).contentWindow.document.body
+      )
       // answerIframe.contentWindow.document.body.style.width = "fit-content"
       answerIframe.contentWindow.document.body.style.maxWidth = "1160px"
       answerIframe.contentWindow.document.body.style.maxHeight = "480px"
       answerIframe.contentWindow.document.body.style.backgroundColor =
         "#f5f5f5"
-      console.log(
-        document.getElementById("answerIframe").contentWindow.document
-      )
       let iframe = document
-        .getElementById("answerIframe")
+        .getElementById(`answerIframe${this.index}`)
         .contentWindow.document.getElementsByTagName("p")
 
       if (iframe.length > 0) {

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

@@ -111,6 +111,7 @@
                 text-overflow: ellipsis;
                 white-space: nowrap;
                 margin-bottom: 10px;
+                width: 85%;
             }
 
             .class-number-time {

+ 59 - 8
TEAMModelOS/ClientApp/src/components/student-web/HomeView/HomeView.vue

@@ -28,7 +28,12 @@
             <div class="home-join" v-if="allCourseShow.length">
                 <vuescroll>
                     <div :class="['join-class', {'scale': isScale}]" v-for="(item, index) in allCourseShow" :key="index" @click="choiceClass(index, item)">
-                        <p class="class-name" :title="`${item.name} - ${item.groupName}`">{{ item.name }} - {{ item.groupName }}</p>
+                        <div style="display: flex; justify-content: space-between;">
+                            <p class="class-name" :title="`${item.name} - ${item.groupName}`">{{ item.name }} - {{ item.groupName }}</p>
+                            <span @click.stop="toTop(item)" v-show="!item.subjectList">
+                                <Icon custom="iconfont icon-zhiding" size="18" :color="item.top ? '#239720' : '#b5b5b5'" :title="item.top? $t('studentWeb.home.excelTop') : $t('studentWeb.home.top')"></Icon>
+                            </span>
+                        </div>
                         <!-- <p class="paper-item-school">
                             <span class="paper-owner" v-if="item.scope === 'school'" style="background-color: #88a1d8;">{{ $t('studentWeb.public.school') }}</span>
                             <span class="paper-owner" v-else>{{ $t('studentWeb.public.private') }}</span>
@@ -278,6 +283,7 @@
 import ExamPerform from './ChartHome/ExamPerform.vue';
 import HomeworkPoint from './ChartHome/HomeworkPoint.vue';
 import ClassPoint from './ChartHome/ClassPoint.vue';
+import draggable from "vuedraggable"
 
 import { mapGetters, mapState } from 'vuex';
 import BlobTool from "@/utils/blobTool.js"
@@ -321,7 +327,7 @@ export default {
             timeSelect: "all",
             nowtime: null,
             courseType: 'all',
-            allCourseShow: [],
+            // allCourseShow: [],
             searchText: '',
             waitCol: [
                 {
@@ -346,7 +352,7 @@ export default {
         }
     },
     mounted () {
-        this.allCourseShow = [...this.getAllCourse]
+        // this.allCourseShow = [...this.getAllCourse]
         this.getNowTime()
         this.MyName = this.$t("studentWeb.homeViewTitle")
         this.$emit("onNavNo", this.MyNo)
@@ -792,10 +798,49 @@ export default {
             this.showNotice = true
             this.nowNotice = info
         },
+        toTop(data) {
+            let topArr = []
+            // 取消置顶
+            if(data.top) {
+                topArr = this.getCourseTop.filter(item => {
+                    return item.id != data.id || item.groupId != data.groupId
+                })
+            } else {
+                topArr = this.getCourseTop.concat([{id: data.id, groupId: data.groupId}])
+            }
+            let params = {
+                id: this.userInfo.sub,
+                code: `PersonalSetting${this.userInfo.scope != "tmduser" ? `-${this.userInfo.azp}` : ''}`,
+                pk: 'PersonalSetting',
+                userType: this.userInfo.scope,
+                setting: {
+                    course: topArr
+                }
+            }
+            this.$api.studentWeb.setPreference(params).then(res => {
+                if(res.code === 200) {
+                    let courseTop = res.personalSetting.setting?.course || []
+                    data.top = !data.top
+                    let arr = this._.cloneDeep(this.getAllCourse)
+                    let index = arr.findIndex(item => {
+                        return item.id === data.id && item.groupId === data.groupId
+                    })
+                    arr.splice(index, 1)
+                    if(data.top) {
+                        arr.unshift(data)
+                    } else {
+                        arr.push(data)
+                    }
+                    this.$store.commit("setCourseTop", courseTop)
+                    this.$store.commit("setAllCourse", arr)
+                    this.searchCourse()
+                }
+            })
+        },
         choiceClass(index, data) {
             if(this.isScale) return
-            let newIndex = data.subjectList ? 0 : this.getAllCourse.findIndex(item => {
-                return item.id === data.id && item.groupId === data.groupId
+            let newIndex = this.getAllCourse.findIndex(item => {
+                return data.subjectList ? (item.id === data.id && item.subjectList) : item.id === data.id && item.groupId === data.groupId
             })
             // this.$parent.classChange(index + 1)
             this.$store.commit("setNowCourse", undefined)
@@ -865,9 +910,9 @@ export default {
             })
         },
         searchCourse() {
-            this.allCourseShow = this.getAllCourse.filter(item => {
+            /* this.allCourseShow = this.getAllCourse.filter(item => {
                 return item.name.includes(this.searchText) && (this.courseType === 'all' ? true : item.scope === this.courseType)
-            })
+            }) */
         },
         getReview() {
             this.$api.courseMgmt.courseReview({grant_type: 'result'}).then(res => {
@@ -930,7 +975,13 @@ export default {
             onlySystem: state => state.studentWeb.onlySystem,
             isScale: state => state.studentWeb.isScale
         }),
-        ...mapGetters(['getAllCourse'])
+        ...mapGetters(['getAllCourse', 'getCourseTop']),
+        allCourseShow() {
+            let arr = this.getAllCourse.filter(item => {
+                return item.name.includes(this.searchText) && (this.courseType === 'all' ? true : item.scope === this.courseType)
+            })
+            return arr || []
+        },
     },
     watch: {
         getAllCourse: {

+ 9 - 1
TEAMModelOS/ClientApp/src/components/student-web/WrongQusetion/AnswerBox.vue

@@ -33,7 +33,7 @@
             </div>
         </div>
         <template v-if="quesInfo">
-            <div class="answer-content" v-if="practiceType != 2 && practiceType != 5 && practiceType != 6" :style="{'font-size': fs + 'px'}">
+            <div class="answer-content" ref="mathJaxContainer" v-if="practiceType != 2 && practiceType != 5 && practiceType != 6" :style="{'font-size': fs + 'px'}">
                 <div v-if="quesInfo.pid || quesInfo.qamode" :class="['parent-box', 'quertion-area', parentOpen ? 'parent-open' : '']">
                     <template v-if="quesInfo.pid">
                         <vuescroll>
@@ -449,6 +449,14 @@ export default {
                 }
             }
         },
+        quesInfo: {
+            handler(n, o) {
+                window.MathJax.startup.promise.then(() => {
+                    window.MathJax.typesetPromise([this.$refs.mathJaxContainer])
+                })
+            },
+            deep: true,
+        },
     },
     created () {
         if(!this.$route.params.qsList) {

+ 5 - 1
TEAMModelOS/ClientApp/src/components/student-web/WrongQusetion/QuesList.vue

@@ -56,7 +56,7 @@
         <Scroll class="list-scroll" :on-reach-bottom="handleReachTop">
             <div style="height: 259px" v-show="winHeight"></div>
             <!-- 题目列表 -->
-            <div class="topic-list">
+            <div class="topic-list" ref="mathJaxContainer">
                 <template v-if="exerciseList.length">
                     <div v-for="(item, index) in exerciseList" :key="index" class="topic-box">
                         <div :style="{'width': language === 'en-us' ? '13%' : '7%'}">
@@ -550,6 +550,10 @@ export default {
                        (this.sortOrder ? (a.lastTimeUnix - b.lastTimeUnix) : (b.lastTimeUnix - a.lastTimeUnix))
                        : (this.sortOrder ? (a.createTime - b.createTime) : (b.createTime - a.createTime))
             })
+            
+            window.MathJax.startup.promise.then(() => {
+                window.MathJax.typesetPromise([this.$refs.mathJaxContainer])
+            })
             // this.topicTotal = this.exerciseList.length
             this.isLoading = false
         },

+ 7 - 0
TEAMModelOS/ClientApp/src/store/module/studentWeb.js

@@ -94,6 +94,7 @@ export default {
         allCourse: [],
         onlySystem: false,
         isScale: false, //登录超出限制人数
+        courseTop: [],
     },
 
     //定義方法
@@ -332,6 +333,9 @@ export default {
             console.log(data);
             state.isScale = data
         },
+        setCourseTop(state, data) {
+            state.courseTop = data
+        },
     },
     getters: {
         getStuUserInfo: (state) => {
@@ -469,5 +473,8 @@ export default {
         getAllCourse(state) {
             return state.allCourse
         },
+        getCourseTop(state) {
+            return state.courseTop
+        },
     },
 }

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

@@ -98,7 +98,13 @@ export default {
                             }
                         }
                     }
-                ]
+                ],
+                dataZoom: [{
+                    show: true,
+                    height: 10,
+                    start: 0,
+                    end: 100,
+                }],
             },
             groupChart: undefined,
             groupOption: {
@@ -270,6 +276,7 @@ export default {
                                 selected: !index
                             }
                         })
+                        this.groupOption.series[0].label.show = this.groupOption.series[0].data.length < 6
                         this.groupChart.clear()
                         this.groupChart.setOption(this.groupOption)
                         let that = this

+ 4 - 1
TEAMModelOS/ClientApp/src/view/evaluation/bank/ExerciseList.vue

@@ -478,7 +478,7 @@ export default {
       }).catch(e => {
         console.log(e)
         that.dataLoading = false;
-      });
+      })
     },
 
     /* 搜索试题 */
@@ -775,6 +775,9 @@ export default {
         this.pageScrollTo(0);
         this.onHandleToggle(false)
         this.$refs.childRef && this.$refs.childRef[0] && (this.$refs.childRef[0].collapseList = [])
+        window.MathJax.startup.promise.then(() => {
+          window.MathJax.typesetPromise([this.$refs.mathJaxContainer])
+        })
       } catch (e) {
         this.$Message.error('Blob Error : ' + e)
       }

+ 4 - 0
TEAMModelOS/ClientApp/src/view/evaluation/bank/index.vue

@@ -31,6 +31,10 @@
 							<Icon type="md-hand" size="16" />
 							<span>{{ $t("evaluation.index.manualCreate") }}</span>
 						</span>
+						<span @click="goCreatePaper('preview')" class="bank-tools-btn">
+							<Icon type="md-clipboard" size="16" />
+							<span>{{ $t("evaluation.index.emptyPaper") }}</span>
+						</span>
 						<span @click="goXkwPick()" class="bank-tools-btn" v-show="hasXkwAuth && !inGlobalSite && isSchool">
 							<Icon type="ios-send" size="16" />
 							<span>{{ $t("evaluation.xkwMode") }}</span>

+ 116 - 142
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseCreateChild.vue

@@ -9,14 +9,13 @@
 			</div>
 			<div class="exersices-attr my-radio-style">
 				<IconText :text="$t('evaluation.newExercise.knowledge')" :color="'#00b8ff'" :icon="'md-infinite'"></IconText>
-				<Button type="info" style="margin-top: 20px" @click="onSelectPoints" v-if="exercisePoints.length === 0">{{$t('evaluation.newExercise.choosePoint')}}</Button>
+				<Button type="info" style="margin-top: 20px" @click="onSelectPoints" v-if="exercisePoints.length === 0">{{ $t("evaluation.newExercise.choosePoint") }}</Button>
 				<div v-else style="margin-top: 10px">
 					<span v-for="(item, index) in exercisePoints" :key="index" class="exercise-item-point">
 						{{ item }}
-						<span class="exercise-item-point-close">
-							<Icon type="md-close" @click="onDeletePoint(index)" /></span>
+						<span class="exercise-item-point-close"> <Icon type="md-close" @click="onDeletePoint(index)" /></span>
 					</span>
-					<span class="exercise-item-point-modify" @click="selectPointsModal = true">{{ $t('evaluation.newExercise.modify') }}</span>
+					<span class="exercise-item-point-modify" @click="selectPointsModal = true">{{ $t("evaluation.newExercise.modify") }}</span>
 				</div>
 			</div>
 		</div>
@@ -24,34 +23,34 @@
 			<div class="exersices-attr my-radio-style">
 				<IconText :text="$t('evaluation.newExercise.type')" :color="'#00b8ff'" :icon="'md-pricetags'"></IconText>
 				<RadioGroup v-model="exersicesType" type="button" @on-change="typeChange">
-					<Radio label="single" :disabled="isEdit">{{ $t('evaluation.single') }}</Radio>
-					<Radio label="multiple" :disabled="isEdit">{{ $t('evaluation.multiple') }}</Radio>
-					<Radio label="judge" :disabled="isEdit">{{ $t('evaluation.judge') }}</Radio>
-					<Radio label="complete" :disabled="isEdit">{{ $t('evaluation.complete') }}</Radio>
-					<Radio label="subjective" :disabled="isEdit">{{ $t('evaluation.subjective') }}</Radio>
-					<Radio label="connector" :disabled="isEdit">{{ $t('evaluation.connector') }}</Radio>
-					<Radio label="correct" :disabled="isEdit">{{ $t('evaluation.correct') }}</Radio>
+					<Radio label="single" :disabled="isEdit">{{ $t("evaluation.single") }}</Radio>
+					<Radio label="multiple" :disabled="isEdit">{{ $t("evaluation.multiple") }}</Radio>
+					<Radio label="judge" :disabled="isEdit">{{ $t("evaluation.judge") }}</Radio>
+					<Radio label="complete" :disabled="isEdit">{{ $t("evaluation.complete") }}</Radio>
+					<Radio label="subjective" :disabled="isEdit">{{ $t("evaluation.subjective") }}</Radio>
+					<Radio label="connector" :disabled="isEdit">{{ $t("evaluation.connector") }}</Radio>
+					<Radio label="correct" :disabled="isEdit">{{ $t("evaluation.correct") }}</Radio>
 				</RadioGroup>
 			</div>
 			<div class="exersices-attr edit-exersices-attr-diff my-radio-style">
 				<IconText :text="$t('evaluation.newExercise.diff')" :color="'#00b8ff'" :icon="'md-pulse'"></IconText>
 				<RadioGroup v-model="exersicesDiff" type="button">
-					<Radio label="1" @click.native="diffChange($event, '1')">{{$t('evaluation.diff1')}}</Radio>
-					<Radio label="2" @click.native="diffChange($event, '2')">{{$t('evaluation.diff2')}}</Radio>
-					<Radio label="3" @click.native="diffChange($event, '3')">{{$t('evaluation.diff3')}}</Radio>
-					<Radio label="4" @click.native="diffChange($event, '4')">{{$t('evaluation.diff4')}}</Radio>
-					<Radio label="5" @click.native="diffChange($event, '5')">{{$t('evaluation.diff5')}}</Radio>
+					<Radio label="1" @click.native="diffChange($event, '1')">{{ $t("evaluation.diff1") }}</Radio>
+					<Radio label="2" @click.native="diffChange($event, '2')">{{ $t("evaluation.diff2") }}</Radio>
+					<Radio label="3" @click.native="diffChange($event, '3')">{{ $t("evaluation.diff3") }}</Radio>
+					<Radio label="4" @click.native="diffChange($event, '4')">{{ $t("evaluation.diff4") }}</Radio>
+					<Radio label="5" @click.native="diffChange($event, '5')">{{ $t("evaluation.diff5") }}</Radio>
 				</RadioGroup>
 			</div>
 		</div>
 
 		<BaseSingle v-if="exersicesType === 'single'" ref="single" :editInfo="editInfo"></BaseSingle>
 		<BaseMultiple v-else-if="exersicesType === 'multiple'" ref="multiple" :editInfo="editInfo"></BaseMultiple>
-		<BaseJudge v-else-if="exersicesType === 'judge'" ref="judge" :editInfo="editInfo" ></BaseJudge>
-		<BaseCompletion v-else-if="exersicesType === 'complete'" ref="complete" :editInfo="editInfo" ></BaseCompletion>
-		<BaseConnector v-else-if="exersicesType === 'connector'" ref="connector" :editInfo="editInfo" ></BaseConnector>
-		<BaseCorrect v-else-if="exersicesType === 'correct'" ref="correct" :editInfo="editInfo" ></BaseCorrect>
-		<BaseSubjective v-else-if="exersicesType === 'subjective'" ref="subjective" :editInfo="editInfo" ></BaseSubjective>
+		<BaseJudge v-else-if="exersicesType === 'judge'" ref="judge" :editInfo="editInfo"></BaseJudge>
+		<BaseCompletion v-else-if="exersicesType === 'complete'" ref="complete" :editInfo="editInfo"></BaseCompletion>
+		<BaseConnector v-else-if="exersicesType === 'connector'" ref="connector" :editInfo="editInfo"></BaseConnector>
+		<BaseCorrect v-else-if="exersicesType === 'correct'" ref="correct" :editInfo="editInfo"></BaseCorrect>
+		<BaseSubjective v-else-if="exersicesType === 'subjective'" ref="subjective" :editInfo="editInfo"></BaseSubjective>
 
 		<!-- 解析的富文本部分 -->
 		<div class="exersices-analysis child-exercise-analysis">
@@ -68,13 +67,12 @@
 		</div>
 
 		<div class="save-wrap display-flex">
-			<Button type="success" @click="getContent(exersicesType)" :loading="saveLoading">{{ $t('evaluation.newExercise.save') }}</Button>
+			<Button type="success" @click="getContent(exersicesType)" :loading="saveLoading">{{ $t("evaluation.newExercise.save") }}</Button>
 		</div>
 
 		<!-- 选择知识点弹窗 -->
 		<Modal v-model="selectPointsModal" :title="$t('evaluation.newExercise.choosePoint')" width="600px" footer-hide class="related-point-modal" style="z-index: 99999">
-			<BasePoints v-if="selectPointsModal" ref="pointRef" :period="schoolInfo.period[curPeriodIndex].id" :subject="schoolInfo.period[curPeriodIndex].subjects[curSubjectIndex].id"
-			 @onCheckChange="onCheckChange" @onCancel="selectPointsModal = false" :points="exercisePoints" :scope="curScope"></BasePoints>
+			<BasePoints v-if="selectPointsModal" ref="pointRef" :period="schoolInfo.period[curPeriodIndex].id" :subject="schoolInfo.period[curPeriodIndex].subjects[curSubjectIndex].id" @onCheckChange="onCheckChange" @onCancel="selectPointsModal = false" :points="exercisePoints" :scope="curScope"></BasePoints>
 		</Modal>
 	</div>
 </template>
@@ -86,8 +84,8 @@
 	import BaseCompletion from "@/view/evaluation/types/BaseCompletion.vue";
 	import BaseJudge from "@/view/evaluation/types/BaseJudge.vue";
 	import BaseSubjective from "@/view/evaluation/types/BaseSubjective.vue";
-    import BaseCorrect from "@/view/evaluation/types/BaseCorrect.vue";
-    import BaseConnector from "@/view/evaluation/types/BaseConnector.vue";
+	import BaseCorrect from "@/view/evaluation/types/BaseCorrect.vue";
+	import BaseConnector from "@/view/evaluation/types/BaseConnector.vue";
 	import NewChooseContent from "@/components/selflearn/NewChooseContent";
 	import E from "wangeditor";
 	// 默认创建题目模板
@@ -98,9 +96,12 @@
 		answer: [],
 		explain: "",
 		type: "",
+		answerType: "text",
+		useAutoScore: false,
+		answerLang: "en-US"
 	};
 	export default {
-		name:'BaseCreateChild',
+		name: "BaseCreateChild",
 		components: {
 			IconText,
 			BaseSingle,
@@ -110,25 +111,25 @@
 			BaseSubjective,
 			BaseCorrect,
 			BaseConnector,
-			NewChooseContent,
+			NewChooseContent
 		},
 		props: {
 			editItem: {
 				type: Object,
-				default: null,
+				default: null
 			},
 			refId: {
 				type: String,
-				default: "createChild",
+				default: "createChild"
 			},
 			curPeriodIndex: {
 				type: Number,
-				default: 0,
+				default: 0
 			},
 			curSubjectIndex: {
 				type: Number,
-				default: 0,
-			},
+				default: 0
+			}
 		},
 		data() {
 			return {
@@ -141,16 +142,15 @@
 				editInfo: {},
 				schoolInfo: {},
 				saveLoading: false,
-				exersicesType: "single",//single
+				exersicesType: "single", //single
 				exerciseField: 0,
 				exercisePeriod: 0,
 				exerciseGrade: [],
 				exerciseSubject: 0,
 				exerciseScope: 0,
 				exercisePoints: [],
-				scopeList: [this.$t('evaluation.filter.schoolBank'),this.$t('evaluation.filter.privateBank')],
-				fieldList: [this.$t('evaluation.level1'),this.$t('evaluation.level2'),this.$t('evaluation.level3'),
-				this.$t('evaluation.level4'),this.$t('evaluation.level5'),this.$t('evaluation.level6')],
+				scopeList: [this.$t("evaluation.filter.schoolBank"), this.$t("evaluation.filter.privateBank")],
+				fieldList: [this.$t("evaluation.level1"), this.$t("evaluation.level2"), this.$t("evaluation.level3"), this.$t("evaluation.level4"), this.$t("evaluation.level5"), this.$t("evaluation.level6")],
 				periodList: [],
 				gradeList: [],
 				subjectList: [],
@@ -160,12 +160,11 @@
 				stemContent: "",
 				analysisEditor: null,
 				repairEditor: null,
-				curId:''
+				curId: ""
 			};
 		},
 		created() {
 			this.getSchoolInfo();
-			
 		},
 		methods: {
 			getSchoolInfo() {
@@ -180,19 +179,18 @@
 					}
 				});
 			},
-			
-			onSelectPoints(){
-				if(this.hasSchool){
-					console.log(this.schoolInfo)
-					console.log(this.curPeriodIndex)
-					console.log(this.curSubjectIndex)
-					this.selectPointsModal = true
-				}else{
-					this.$Message.warning(this.$t('evaluation.newExercise.noSchoolTip'))
+
+			onSelectPoints() {
+				if (this.hasSchool) {
+					console.log(this.schoolInfo);
+					console.log(this.curPeriodIndex);
+					console.log(this.curSubjectIndex);
+					this.selectPointsModal = true;
+				} else {
+					this.$Message.warning(this.$t("evaluation.newExercise.noSchoolTip"));
 				}
 			},
 
-
 			onSelectFile(val) {
 				this.relateFileList = val.files;
 			},
@@ -203,26 +201,21 @@
 			},
 
 			async getContent(type) {
-				
 				let exerciseItem = this.editInfo && this.editInfo.id ? JSON.parse(JSON.stringify(this.editInfo)) : Object.assign({}, defaultExercise);
 				switch (type) {
 					case "single":
-						this.$refs.single.doSave()
+						this.$refs.single.doSave();
 						exerciseItem.question = this.$refs.single.stemContent;
 						exerciseItem.option = this.checkOptionNull(this.$refs.single.optionsContent) ? this.$refs.single.optionsContent : null;
 						exerciseItem.type = this.exersicesType;
 						exerciseItem.level = +this.exersicesDiff;
 						exerciseItem.explain = this.analysisContent;
-						exerciseItem.answer = [
-							String.fromCharCode(64 + parseInt(this.$refs.single.trueIndex + 1)),
-						];
+						exerciseItem.answer = [String.fromCharCode(64 + parseInt(this.$refs.single.trueIndex + 1))];
 						break;
 					case "multiple":
-					this.$refs.multiple.doSave()
+						this.$refs.multiple.doSave();
 						exerciseItem.question = this.$refs.multiple.stemContent;
-						exerciseItem.option = this.checkOptionNull(this.$refs.multiple.optionsContent) ?
-							this.$refs.multiple.optionsContent :
-							null;
+						exerciseItem.option = this.checkOptionNull(this.$refs.multiple.optionsContent) ? this.$refs.multiple.optionsContent : null;
 						exerciseItem.type = this.exersicesType;
 						exerciseItem.level = +this.exersicesDiff;
 						exerciseItem.explain = this.analysisContent;
@@ -252,6 +245,9 @@
 						exerciseItem.level = +this.exersicesDiff;
 						exerciseItem.explain = this.analysisContent;
 						exerciseItem.answer = [this.$refs.subjective.answerContent];
+						exerciseItem.answerType = this.$refs.subjective.answerType; // 02040301 问答题增加作答类型字段
+						exerciseItem.useAutoScore = this.$refs.subjective.useAutoScore; // 02040301 问答题增加作答类型字段
+						exerciseItem.answerLang = this.$refs.subjective.answerLang; // 02040301 问答题增加作答类型字段
 						break;
 					case "connector":
 						exerciseItem.question = this.$refs.connector.stemContent;
@@ -268,27 +264,22 @@
 						exerciseItem.level = +this.exersicesDiff;
 						exerciseItem.explain = this.analysisContent;
 						exerciseItem.answer = [this.$refs.correct.answerContent];
-						break;		
+						break;
 				}
 				exerciseItem.repair = this.repairContent;
-				exerciseItem.repair = this.formatRepairResource(
-					this.$refs.childRepairRef.datas
-				);
+				exerciseItem.repair = this.formatRepairResource(this.$refs.childRepairRef.datas);
 				exerciseItem.field = this.exerciseField + 1;
 				exerciseItem.knowledge = this.exercisePoints;
 				exerciseItem.children = [];
-				exerciseItem.score = (this.editInfo && this.editInfo.score) ? this.editInfo.score : 0;
-				exerciseItem.code =
-					this.$parent.$parent.exerciseScope === 0 ?
-					this.$store.state.userInfo.TEAMModelId :
-					this.$store.state.userInfo.schoolCode;
+				exerciseItem.score = this.editInfo && this.editInfo.score ? this.editInfo.score : 0;
+				exerciseItem.code = this.$parent.$parent.exerciseScope === 0 ? this.$store.state.userInfo.TEAMModelId : this.$store.state.userInfo.schoolCode;
 
-				let confirmSave = await this.checkContent(exerciseItem)
+				let confirmSave = await this.checkContent(exerciseItem);
 				// 判断获取的数据是否有空数据以及是否为空字符串
 				if (confirmSave) {
 					// this.saveLoading = true
 					exerciseItem.id = this.curId;
-					console.log('编辑后的小题',exerciseItem)
+					console.log("编辑后的小题", exerciseItem);
 					this.$emit("addFinish", exerciseItem);
 				}
 			},
@@ -296,7 +287,7 @@
 			/* 知识点勾选变动事件 */
 			onCheckChange(val, list) {
 				this.exercisePoints = val;
-				this.selectPointsModal = false
+				this.selectPointsModal = false;
 			},
 
 			/**
@@ -311,8 +302,8 @@
 			// 题目类型转换
 			typeChange(val) {
 				if (this.isEdit) {
-					this.$Message.warning(this.$t('evaluation.newExercise.typeChangeTip'));
-				}else {
+					this.$Message.warning(this.$t("evaluation.newExercise.typeChangeTip"));
+				} else {
 					this.exersicesType = val;
 					this.analysisEditor.txt.clear();
 					// this.repairEditor.txt.clear()
@@ -324,13 +315,13 @@
 				if (list.length) {
 					let arr = [];
 					list.forEach((i, index) => {
-						i.blobUrl.forEach(j => {
+						i.blobUrl.forEach((j) => {
 							arr.push({
-								blobUrl:j.url,
-								name:i.name,
-								type:i.type
-							})
-						})
+								blobUrl: j.url,
+								name: i.name,
+								type: i.type
+							});
+						});
 					});
 					return arr;
 				} else {
@@ -343,10 +334,7 @@
 				this.exersicesDiff = +type;
 				e.preventDefault();
 				let colorArr = ["#10abe7", "#E8BE15", "#F19300", "#EB5E00", "#D30000"];
-				let ac = document
-					.getElementById(this.refId)
-					.getElementsByClassName("edit-exersices-attr-diff")[0].children[1]
-					.children;
+				let ac = document.getElementById(this.refId).getElementsByClassName("edit-exersices-attr-diff")[0].children[1].children;
 				for (let i = 0; i < ac.length; i++) {
 					ac[i].style.background = "#fff";
 					ac[i].style.color = "#515a6e";
@@ -374,43 +362,43 @@
 
 			// 排除对象空属性
 			checkContent(Obj) {
-				return new Promise(async (r,j) => {
+				return new Promise(async (r, j) => {
 					let flag = true;
 					let whiteList = this.getWhiteListByType(Obj.type);
 					console.log("富文本获取的原始试题数据", Obj);
 					for (let key in Obj) {
 						if (whiteList.includes(key) && typeof Obj[key] === "string") {
 							if (!this.getSimpleText(Obj[key])) {
-								flag = await this.emptyConfirm(key)
+								flag = await this.emptyConfirm(key);
 								r(flag);
-								return
+								return;
 							}
 						} else {
 							if (whiteList.includes(key) && !Obj[key]) {
-								flag = await this.emptyConfirm(key)
+								flag = await this.emptyConfirm(key);
 								r(flag);
-								return
+								return;
 							}
 						}
 					}
 					r(flag);
-				})
+				});
 			},
-			
+
 			/* 确认是否继续保存 */
 			emptyConfirm(key) {
 				return new Promise((r, j) => {
 					this.$Modal.confirm({
-						title: this.$t('evaluation.newExercise.modalTip'),
-						content: `${ this.$t('evaluation.currentItem') }${ key === 'question' ? this.$t('evaluation.newExercise.stem') : this.$t('evaluation.newExercise.option') }${ this.$t('evaluation.emptyTip1') }`,
+						title: this.$t("evaluation.newExercise.modalTip"),
+						content: `${this.$t("evaluation.currentItem")}${key === "question" ? this.$t("evaluation.newExercise.stem") : this.$t("evaluation.newExercise.option")}${this.$t("evaluation.emptyTip1")}`,
 						onOk: () => {
-							r(true)
+							r(true);
 						},
 						onCancel: () => {
-							r(false)
+							r(false);
 						}
 					});
-				})
+				});
 			},
 
 			// 根据不同题型 给出需要必填选项
@@ -433,44 +421,34 @@
 
 			// 渲染编辑习题内容回显
 			async renderExercise(editItem) {
-				console.log('当前小题', editItem);
+				console.log("当前小题", editItem);
 				this.isEdit = true;
 				this.exersicesDiff = editItem.level.toString() || "0";
-				this.exerciseScope =
-					editItem.code === this.$store.state.userInfo.TEAMModelId ? 0 : 1;
+				this.exerciseScope = editItem.code === this.$store.state.userInfo.TEAMModelId ? 0 : 1;
 				this.exersicesType = editItem.type;
 				this.exercisePoints = editItem.knowledge || editItem.points;
 				if (editItem.level) {
-					let ac = document
-						.getElementById(this.refId)
-						.getElementsByClassName("edit-exersices-attr-diff")[0].children[1]
-						.children;
+					let ac = document.getElementById(this.refId).getElementsByClassName("edit-exersices-attr-diff")[0].children[1].children;
 					for (let i = 0; i < ac.length; i++) {
 						ac[i].style.background = "#fff";
 						ac[i].style.color = "#515a6e";
 					}
 					// 重新渲染题目难度
-					let diffDom = document
-						.getElementById(this.refId)
-						.getElementsByClassName("edit-exersices-attr-diff")[0].children[1]
-						.children[editItem.level - 1];
+					let diffDom = document.getElementById(this.refId).getElementsByClassName("edit-exersices-attr-diff")[0].children[1].children[editItem.level - 1];
 					let colorArr = ["#32CF74", "#E8BE15", "#F19300", "#EB5E00", "#D30000"];
 					diffDom.style.background = colorArr[editItem.level - 1];
 					diffDom.style.color = "#fff";
 				} else {
-					let ac = document
-						.getElementById(this.refId)
-						.getElementsByClassName("edit-exersices-attr-diff")[0].children[1]
-						.children;
+					let ac = document.getElementById(this.refId).getElementsByClassName("edit-exersices-attr-diff")[0].children[1].children;
 					for (let i = 0; i < ac.length; i++) {
 						ac[i].style.background = "#fff";
 						ac[i].style.color = "#515a6e";
 					}
 				}
 
-				let schoolInfo = await this.$store.dispatch('user/getSchoolProfile');
+				let schoolInfo = await this.$store.dispatch("user/getSchoolProfile");
 				this.schoolInfo = schoolInfo.school_base;
-				
+
 				this.stemContent = editItem.question;
 				this.relateFileList = editItem.repair || [];
 				this.optionsContent = editItem.option;
@@ -478,41 +456,41 @@
 				this.analysisEditor.txt.html(editItem.explain);
 				this.editInfo = JSON.parse(JSON.stringify(editItem));
 				this.exerciseField = this.editInfo.field - 1;
-			},
+			}
 		},
 		mounted() {
 			let analysisEditor = new E(this.$refs.analysisEditor);
 			analysisEditor.config.uploadImgShowBase64 = true;
-			analysisEditor.config.onchange = (html) => {
+			(analysisEditor.config.onchange = (html) => {
 				this.analysisContent = html;
-			},
-			this.$editorTools.initMyEditor(analysisEditor,this)
+			}),
+				this.$editorTools.initMyEditor(analysisEditor, this);
 			analysisEditor.create();
 			this.analysisEditor = analysisEditor;
-			
-			this.curId = this.$tools.guid()
+
+			this.curId = this.$tools.guid();
 			if (this.editItem && this.editItem.id) {
 				this.renderExercise(JSON.parse(JSON.stringify(this.editItem)));
 				// 先生成随机ID
-				this.curId = this.editItem.id || this.$tools.guid()
+				this.curId = this.editItem.id || this.$tools.guid();
 			}
-			
-			if(this.isEdit){
-				this.exerciseScope = this.editItem.scope === 'school' ? 1 : 0
-			}else{
-				this.exerciseScope = this.$parent.$parent.exerciseScope
+
+			if (this.isEdit) {
+				this.exerciseScope = this.editItem.scope === "school" ? 1 : 0;
+			} else {
+				this.exerciseScope = this.$parent.$parent.exerciseScope;
 			}
-			console.log(this.exerciseScope)
+			console.log(this.exerciseScope);
 		},
-		computed:{
-			curScope(){
-				return this.exerciseScope === 1 ? 'school' : 'private'
+		computed: {
+			curScope() {
+				return this.exerciseScope === 1 ? "school" : "private";
 			},
 			hasSchool() {
-			  return this.$store.state.userInfo.hasSchool
+				return this.$store.state.userInfo.hasSchool;
 			},
-			isPaperEnv(){
-				return sessionStorage.getItem('editorEnv') && sessionStorage.getItem('editorEnv') !== 'normal'
+			isPaperEnv() {
+				return sessionStorage.getItem("editorEnv") && sessionStorage.getItem("editorEnv") !== "normal";
 			}
 		},
 		watch: {
@@ -522,20 +500,18 @@
 						console.log("要编辑的小题");
 						console.log(newValue);
 						this.renderExercise(JSON.parse(JSON.stringify(newValue)));
-						this.curId = newValue.id || this.$tools.guid()
+						this.curId = newValue.id || this.$tools.guid();
 						// this.$refs.pointRef.doReset()
 					}
-				},
+				}
 				//immediate:true
 			}
-		},
+		}
 	};
 </script>
-<style src="../index/CreateExercises.less" lang="less" scoped>
-</style>
+<style src="../index/CreateExercises.less" lang="less" scoped></style>
 
 <style>
-
 	.related-point-modal .ivu-modal-header-inner {
 		font-weight: bold;
 	}
@@ -552,14 +528,12 @@
 	.exersices-attr .ivu-select-multiple .ivu-select-selection .ivu-select-placeholder {
 		line-height: 38px;
 	}
-	
-	.child-exercise-analysis .w-e-toolbar{
+
+	.child-exercise-analysis .w-e-toolbar {
 		z-index: 0 !important;
 	}
-	
-	.child-exercise-analysis .w-e-text-container{
+
+	.child-exercise-analysis .w-e-text-container {
 		position: unset;
 	}
-	
-	
 </style>

+ 4 - 0
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseExerciseList.vue

@@ -945,6 +945,10 @@
 					this.surPlusScore = newPaper.score - newPaper.item.reduce((p, e) => Number(p) + Number(e.score), 0);
 					this.$emit("scoreUpdate", this.surPlusScore);
 					this.pageScrollTo(0);
+					
+					window.MathJax.startup.promise.then(() => {
+						window.MathJax.typesetPromise([this.$refs.mathJaxContainer])
+					})
 				}
 			});
 		},

+ 1 - 1
TEAMModelOS/ClientApp/src/view/evaluation/index/CommonExercise.less

@@ -168,7 +168,7 @@
     .item-question-order {
       display: inline-block;
       vertical-align: top;
-      width: 30px;
+      width: 32px;
     }
 
     .item-question-text {

+ 1 - 0
TEAMModelOS/ClientApp/src/view/evaluation/index/CreateExercises.less

@@ -292,6 +292,7 @@ exersices-attr-diff {
     width:100%;
     justify-content:center;
 	margin-bottom: 20px;
+    padding-bottom: 20px;
 }
     .save-wrap .ivu-btn {
            font-size:16px;

+ 1 - 1
TEAMModelOS/ClientApp/src/view/evaluation/index/CreatePaper.vue

@@ -511,7 +511,7 @@ export default {
 
           }
         })
-      } else {
+      }else {
         this.activeTab = data
       }
     },

+ 9 - 1
TEAMModelOS/ClientApp/src/view/evaluation/index/DfPage.vue

@@ -236,7 +236,7 @@
                 <Radio label="paper">试卷</Radio>
             </RadioGroup> -->
             <div v-if="!selShowList.length">未选择题目</div>
-            <div v-else class="content-wrap" style="height: 90%;">
+            <div v-else class="content-wrap" style="height: 90%;" ref="mathJaxContainer">
                 <vuescroll>
                     <!-- 只在学校题库出现 -->
                     <Form ref="selEvalInfo" :model="selEvalInfo" :rules="evalValidate" label-colon :label-width="100">
@@ -679,6 +679,9 @@ export default {
                 }
             }).finally(() => {
                 this.isLoading = false
+                window.MathJax.startup.promise.then(() => {
+                    window.MathJax.typesetPromise([this.$refs.mathJaxContainer])
+                })
             })
         },
         async handleChoose(item) {
@@ -806,6 +809,9 @@ export default {
             this.selShowList = this.saveType === 'list' ? this.orderList : this.groupList
             this.showQues = true
             // 使用弹出框展示已选题目,再选择加入题库/组成试卷
+            window.MathJax.startup.promise.then(() => {
+                window.MathJax.typesetPromise([this.$refs.mathJaxContainer])
+            })
         },
         scoreChange(val, pid, id) {
             let parent = this.selectedArr.find(item => item.id === pid)
@@ -924,11 +930,13 @@ export default {
                             child.periodId = this.schoolInfo.period[this.selEvalInfo.paperPeriod].id
                             child.gradeIds = [String(this.selEvalInfo.paperGrade)]
                             child.subjectId = this.schSubjectList[this.selEvalInfo.paperSubject].id
+                            child.option = child.option || []
                         })
                     }
                 }
                 item.scope = this.isSchool ? 'school' : 'private'
                 item.code = this.isSchool ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId
+                item.option = item.option || []
             })
             console.log('开始保存');
             // 组成试卷,必要将题目保存在blob

+ 3 - 1
TEAMModelOS/ClientApp/src/view/evaluation/index/TestPaper.vue

@@ -242,7 +242,9 @@ export default {
         this.$Message.warning(this.$t('homework.noAttachments'))
         return
       }
-      let curScope = paper.examScope || paper.scope
+      console.error(paper)
+      // let curScope = paper.examScope || paper.scope
+      let curScope = paper.scope // 个人评测使用校本试卷 examScope会是 private 所以改为直接读取 scope
       let blobHost = curScope === 'school' ? JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri : JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8")).blob_uri
       let sasString = curScope === 'school' ? await this.$tools.getSchoolSas() : await this.$tools.getPrivateSas()
       let fullImgArr = paper.attachments?.map(imgName => blobHost + paper.blob + '/' + imgName + sasString.sas)

+ 190 - 188
TEAMModelOS/ClientApp/src/view/evaluation/types/BaseMultiple.vue

@@ -1,40 +1,41 @@
 <template>
 	<div>
 		<div class="exersices-content">
-			<IconText :text="$t('evaluation.multiple') + $t('evaluation.newExercise.stem')" :color="'#2d8cf0'" :icon="'ios-create'" style="margin-bottom:15px;"></IconText>
+			<IconText :text="$t('evaluation.multiple') + $t('evaluation.newExercise.stem')" :color="'#2d8cf0'" :icon="'ios-create'" style="margin-bottom: 15px"></IconText>
 			<div>
-				<div ref="singleEditor" style="text-align:left"></div>
+				<div ref="singleEditor" style="text-align: left"></div>
 			</div>
 		</div>
 		<div class="exersices-option" ref="optionRefs">
 			<IconText :text="$t('evaluation.newExercise.multipleOption')" :color="'#FF871C'" :icon="'md-reorder'"></IconText>
-			<div v-for="(item,index) in options" :key="index" :ref="'optionBox' + index" :class="'editor-wrap-'+item" style="margin-top:10px;display:flex">
-				<span class="fl-center option-delete" @click="deleteOption(index,item)"><Icon type="md-remove-circle" /></span>
+			<div v-for="(item, index) in options" :key="index" :ref="'optionBox' + index" :class="'editor-wrap-' + item" style="margin-top: 10px; display: flex">
+				<span class="fl-center option-delete" @click="deleteOption(index, item)"><Icon type="md-remove-circle" /></span>
 				<span class="fl-center option-order" :ref="'optionOrder' + index" :data-index="index">{{ renderIndex(index) }}</span>
-				<div :ref="'singleOption'+index" style="text-align:left" class="option-editor" @click="optionClick(item)"></div>
-				<span :class="['fl-center', 'option-setting', trueArr.indexOf(index) > -1 ? 'option-true':'']" @click="settingAnswer(index)">{{ trueArr.indexOf(index) > -1  ? $t('evaluation.newExercise.trueAnswer') :$t('evaluation.newExercise.setAnswer') }}</span>
-
+				<div :ref="'singleOption' + index" style="text-align: left" class="option-editor" @click="optionClick(item)"></div>
+				<span :class="['fl-center', 'option-setting', trueArr.indexOf(index) > -1 ? 'option-true' : '']" @click="settingAnswer(index)">{{ trueArr.indexOf(index) > -1 ? $t("evaluation.newExercise.trueAnswer") : $t("evaluation.newExercise.setAnswer") }}</span>
 			</div>
-			<p class="option-add"><span @click="addOption()">+ {{$t('evaluation.newExercise.addOption')}} </span><span style="color:rgb(60,196,82);margin-left:15px;font-weight:bold">{{$t('evaluation.newExercise.trueAnswer')}}:{{ multipleAnswers.join('') }}</span></p>
+			<p class="option-add">
+				<span @click="addOption()">+ {{ $t("evaluation.newExercise.addOption") }} </span><span style="color: rgb(60, 196, 82); margin-left: 15px; font-weight: bold">{{ $t("evaluation.newExercise.trueAnswer") }}:{{ multipleAnswers.join("") }}</span>
+			</p>
 		</div>
 	</div>
 </template>
 <script>
-	import E from 'wangeditor'
-	import IconText from '@/components/evaluation/IconText.vue'
+	import E from "wangeditor";
+	import IconText from "@/components/evaluation/IconText.vue";
 	export default {
 		components: {
 			IconText
 		},
 		props: {
-			editInfo:{
-				type:Object,
-				default:() => {}
-			},
-			isEdit:{
-				type:Boolean,
-				default:false
+			editInfo: {
+				type: Object,
+				default: () => {}
 			},
+			isEdit: {
+				type: Boolean,
+				default: false
+			}
 		},
 		data() {
 			return {
@@ -42,295 +43,296 @@
 				existOptions: [...new Array(4).keys()],
 				initFlag: true,
 				trueIndex: 0,
-				optionTrueIndex:0,
-				trueArr:[0],
-				optionTrueArr:[0],
-				multipleAnswers:['A'],
+				optionTrueIndex: 0,
+				trueArr: [0],
+				optionTrueArr: [0],
+				multipleAnswers: ["A"],
 				editSingleInfo: {},
 				stemEditor: null,
-				stemContent: '',
-				transferArr:[],
+				stemContent: "",
+				transferArr: [],
 				optionsContent: [],
 				optionEditors: [],
 				defaultConfig: {
 					uploadImgShowBase64: true,
 					menus: this.$tools.wangEditorMenu
 				}
-			}
+			};
 		},
 		created() {},
 		methods: {
 			initEditors() {
+				console.error("initEditors", this.options);
 				// Editor默认配置
 				if (this.options.length > 0) {
 					this.options.forEach((item, i) => {
-						let that = this
-						let editor = new E(that.$refs['singleOption' + i][0])
+						let that = this;
+						let editor = new E(that.$refs["singleOption" + i][0]);
 						// 选项编辑器失焦隐藏工具栏
-						editor.config.onblur = function() {
-							let allToolbars = document.getElementsByClassName('option-editor')
+						editor.config.onblur = function () {
+							let allToolbars = document.getElementsByClassName("option-editor");
 							for (let i = 0; i < allToolbars.length; i++) {
 								if (allToolbars[i].children.length) {
-									allToolbars[i].children[0].style.visibility = 'hidden'
+									allToolbars[i].children[0].style.visibility = "hidden";
 								}
 							}
-						}
-						
+						};
+
 						editor.config.uploadImgShowBase64 = true;
-						this.$editorTools.initMyEditor(editor,this,true)
+						this.$editorTools.initMyEditor(editor, this, true);
 
-							// 选项编辑器内容发生变化时
-							editor.config.onchange = (html) => {
-								let key = String.fromCharCode(64 + parseInt(i + 1))
-								let codeArr = this.optionsContent.map(item => item.code)
-								// 如果已经编辑过则 修改选项内容
-								if (codeArr.indexOf(key) !== -1) {
-									this.optionsContent[codeArr.indexOf(key)].value = html
-								} else { // 否则创建新选项
-									let option = {
-										code: key,
-										value: html
-									}
-									this.optionsContent.push(option)
-								}
+						// 选项编辑器内容发生变化时
+						editor.config.onchange = (html) => {
+							let key = String.fromCharCode(64 + parseInt(i + 1));
+							let codeArr = this.optionsContent.map((item) => item.code);
+							// 如果已经编辑过则 修改选项内容
+							if (codeArr.indexOf(key) !== -1) {
+								this.optionsContent[codeArr.indexOf(key)].value = html;
+							} else {
+								// 否则创建新选项
+								let option = {
+									code: key,
+									value: html
+								};
+								this.optionsContent.push(option);
 							}
-						editor.create()
-						this.optionEditors.push(editor)
-						that.$refs["singleOption" + i][0].dataset.editorId = editor.id
+						};
+						editor.create();
+						this.optionEditors.push(editor);
+						that.$refs["singleOption" + i][0].dataset.editorId = editor.id;
 
 						// 如果是编辑状态 则将选项内容回显
 						if (Object.keys(this.editSingleInfo).length > 0) {
-							editor.txt.html(this.editSingleInfo.option[i].value)
+							editor.txt.html(this.editSingleInfo.option[i].value);
 						}
-					})
+					});
 				}
 			},
 
 			/* 添加选项 */
 			addOption() {
-				let that = this
-				let wraps = Array.from(this.$refs.optionRefs.getElementsByClassName('option-order'))
+				let that = this;
+				let wraps = Array.from(this.$refs.optionRefs.getElementsByClassName("option-order"));
 				let optionsLength = wraps.length;
-				let newIndex = this.options.length ? parseInt(this.options[this.options.length - 1]) + 1 : 0
+				let newIndex = this.options.length ? parseInt(this.options[this.options.length - 1]) + 1 : 0;
 				// let optionsLength = this.options.length
 				if (optionsLength < 10) {
-					this.options.push(newIndex)
-					this.existOptions.push(newIndex)
+					this.options.push(newIndex);
+					this.existOptions.push(newIndex);
 					this.$nextTick(() => {
-						let editor = new E(that.$refs['singleOption' + newIndex][0])
-						this.$editorTools.initMyEditor(editor,this,true)
+						let editor = new E(that.$refs["singleOption" + newIndex][0]);
+						this.$editorTools.initMyEditor(editor, this, true);
 						editor.config.onchange = (html) => {
-							let key = String.fromCharCode(64 + parseInt(newIndex + 1))
-							let codeArr = this.optionsContent.map(item => item.code)
+							let key = String.fromCharCode(64 + parseInt(newIndex + 1));
+							let codeArr = this.optionsContent.map((item) => item.code);
 							// 如果已经编辑过则 修改选项内容
 							if (codeArr.indexOf(key) !== -1) {
-								this.optionsContent[codeArr.indexOf(key)].value = html
-								console.log(this.optionsContent.map(item => item.code))
-								console.log(this.optionsContent.map(item => item.value))
-							} else { // 否则创建新选项
+								this.optionsContent[codeArr.indexOf(key)].value = html;
+								console.log(this.optionsContent.map((item) => item.code));
+								console.log(this.optionsContent.map((item) => item.value));
+							} else {
+								// 否则创建新选项
 								let option = {
 									code: key,
 									value: html
-								}
-								this.optionsContent.push(option)
+								};
+								this.optionsContent.push(option);
 							}
-						}
-						editor.create()
+						};
+						editor.create();
 						this.optionEditors.push(editor);
-						this.$refs["singleOption" + newIndex][0].dataset.editorId = editor.id
-						this.refreshOrder()
-					})
+						this.$refs["singleOption" + newIndex][0].dataset.editorId = editor.id;
+						this.refreshOrder();
+					});
 				} else {
-					this.$Message.warning(this.$t('evaluation.addTip2'))
+					this.$Message.warning(this.$t("evaluation.addTip2"));
 				}
 			},
-			
-			
+
 			/* 设置正确答案 */
 			settingAnswer(index) {
 				this.$nextTick(() => {
-				    if (this.trueArr.indexOf(index) > -1) {
-						if(this.trueArr.length === 1){
-							this.$Message.warning(this.$t('evaluation.addTip3'))
-						}else{
-							this.trueArr.splice(this.trueArr.indexOf(index), 1)
+					if (this.trueArr.indexOf(index) > -1) {
+						if (this.trueArr.length === 1) {
+							this.$Message.warning(this.$t("evaluation.addTip3"));
+						} else {
+							this.trueArr.splice(this.trueArr.indexOf(index), 1);
 						}
-				    } else {
-				        this.trueArr.push(index)
-				        this.trueArr = this.trueArr.sort() // 选项排序
-				    }
-					this.getAnswerOrder(this.trueArr)
-				})
+					} else {
+						this.trueArr.push(index);
+						this.trueArr = this.trueArr.sort(); // 选项排序
+					}
+					this.getAnswerOrder(this.trueArr);
+				});
 			},
-			
+
 			/* 获取最新答案选项 */
-			getAnswerOrder(arr){
-				let arr2 = []
-				arr.forEach(i => {
-					arr2.push(this.getOrderCode(i))
-				})
-				this.multipleAnswers = arr2.sort()
+			getAnswerOrder(arr) {
+				let arr2 = [];
+				arr.forEach((i) => {
+					arr2.push(this.getOrderCode(i));
+				});
+				this.multipleAnswers = arr2.sort();
 			},
-			
+
 			/* 根据index获取对应选项字母的值 */
-			getOrderCode(index){
-				let wraps = Array.from(this.$refs.optionRefs.getElementsByClassName('option-order'))
-				for(let i=0;i<wraps.length;i++){
-					let item = wraps[i]
-					if(+index === +item.dataset.index){
-						return item.innerText
+			getOrderCode(index) {
+				let wraps = Array.from(this.$refs.optionRefs.getElementsByClassName("option-order"));
+				for (let i = 0; i < wraps.length; i++) {
+					let item = wraps[i];
+					if (+index === +item.dataset.index) {
+						return item.innerText;
 						break;
 					}
 				}
 			},
-			
+
 			/* 根据页面上的选项DOM数量,刷新每个选项的Order显示 */
 			refreshOrder() {
-				let wraps = Array.from(this.$refs.optionRefs.getElementsByClassName('option-order'))
+				let wraps = Array.from(this.$refs.optionRefs.getElementsByClassName("option-order"));
 				wraps.forEach((item, index) => {
-					item.innerHTML = this.renderIndex(index)
-				})
+					item.innerHTML = this.renderIndex(index);
+				});
 			},
-			
+
 			/* 根据下标渲染对应的字母顺序 */
 			renderIndex(index) {
-				return String.fromCharCode(64 + parseInt(index + 1))
+				return String.fromCharCode(64 + parseInt(index + 1));
 			},
-			
-			
 
 			/* 删除选项 */
-			deleteOption(index,item) {
+			deleteOption(index, item) {
 				// 拿到所有选项
-				if (this.existOptions.length > 2 ) {
+				if (this.existOptions.length > 2) {
 					// 如果删除的是正确答案 则重置正确答案
-					if(this.trueArr.indexOf(index) > -1){
-						if(this.trueArr.length === 1){
-							this.$Message.warning(this.$t('evaluation.addTip3'))
-						}else{
-							this.trueArr.splice(this.trueArr.indexOf(index),1)
+					if (this.trueArr.indexOf(index) > -1) {
+						if (this.trueArr.length === 1) {
+							this.$Message.warning(this.$t("evaluation.addTip3"));
+						} else {
+							this.trueArr.splice(this.trueArr.indexOf(index), 1);
 							// 确保当前存在的options保持同步
-							this.existOptions.splice(this.existOptions.indexOf(item), 1)
-							this.optionsContent.splice(index, 1)
+							this.existOptions.splice(this.existOptions.indexOf(item), 1);
+							this.optionsContent.splice(index, 1);
 							// 删除操作 移除选项DOM
-							let textWrap = this.$refs['optionBox' + index][0]
-							textWrap.remove()
+							let textWrap = this.$refs["optionBox" + index][0];
+							textWrap.remove();
 							// 刷新选项序号显示
-							this.refreshOrder()
-							this.getAnswerOrder(this.trueArr)
+							this.refreshOrder();
+							this.getAnswerOrder(this.trueArr);
 						}
-					}else{
+					} else {
 						// 确保当前存在的options保持同步
-						this.existOptions.splice(this.existOptions.indexOf(item), 1)
-						this.optionsContent.splice(index, 1)
+						this.existOptions.splice(this.existOptions.indexOf(item), 1);
+						this.optionsContent.splice(index, 1);
 						// 删除操作 移除选项DOM
-						let textWrap = this.$refs['optionBox' + index][0]
-						textWrap.remove()
+						let textWrap = this.$refs["optionBox" + index][0];
+						textWrap.remove();
 						// 刷新选项序号显示
-						this.refreshOrder()
-						this.getAnswerOrder(this.trueArr)
+						this.refreshOrder();
+						this.getAnswerOrder(this.trueArr);
 					}
-					
 				} else {
-					this.$Message.warning(this.$t('evaluation.addTip4'))
+					this.$Message.warning(this.$t("evaluation.addTip4"));
 				}
 			},
 
 			/* 保存试题 获取最新选项数据 */
 			doSave() {
 				// 拿到当前还剩的选项DOM
-				let wraps = Array.from(this.$refs.optionRefs.getElementsByClassName('option-editor'))
-				let arr = []
+				let wraps = Array.from(this.$refs.optionRefs.getElementsByClassName("option-editor"));
+				let arr = [];
 				wraps.forEach((item, index) => {
 					// 遍历选项 找到对应的 Editor 然后获取编辑器里面的内容
-					let id = item.dataset.editorId
-					let curEditor = this.optionEditors.filter(i => i.id === id)[0]
-					if(curEditor){
+					let id = item.dataset.editorId;
+					let curEditor = this.optionEditors.filter((i) => i.id === id)[0];
+					if (curEditor) {
 						// 生成新的选项对象
 						let obj = {
 							code: String.fromCharCode(64 + parseInt(index + 1)),
 							value: curEditor.txt.html()
-						}
-						arr.push(obj)
+						};
+						arr.push(obj);
 					}
-					
-				})
-				this.optionsContent = arr
+				});
+				this.optionsContent = arr;
 			},
 
 			/* 模拟选项聚焦事件 */
 			optionClick(index) {
-				let allToolbars = document.getElementsByClassName('option-editor')
-				let that = this
+				let allToolbars = document.getElementsByClassName("option-editor");
+				let that = this;
 				for (let i = 0; i < allToolbars.length; i++) {
-					allToolbars[i].children[0].style.visibility = 'hidden'
+					allToolbars[i].children[0].style.visibility = "hidden";
 				}
-				setTimeout(function() {
-					let currentToolBar = that.$refs['singleOption' + index][0].children[0]
-					if(currentToolBar.clientHeight > 50){
-						currentToolBar.style.top = '-90px'
+				setTimeout(function () {
+					let currentToolBar = that.$refs["singleOption" + index][0].children[0];
+					if (currentToolBar.clientHeight > 50) {
+						currentToolBar.style.top = "-90px";
 					}
-					currentToolBar.style.visibility = 'visible'
-				}, 100)
+					currentToolBar.style.visibility = "visible";
+				}, 100);
 			},
 			/* 渲染多选题 */
-			doRender(editInfo){
-				this.editSingleInfo = editInfo
-				this.stemContent = editInfo.question
-				this.optionsContent = editInfo.option
-				this.multipleAnswers = editInfo.answer
-				this.trueArr = editInfo.answer.map(item => item.charCodeAt() - 65)
-				this.options = editInfo.option.map((item, index) => index)
-				this.existOptions = editInfo.option.map((item, index) => index)
+			doRender(editInfo) {
+				console.error("xxxxxxxxx3");
+				console.error(this.options);
+				this.editSingleInfo = editInfo;
+				this.stemContent = editInfo.question;
+				this.optionsContent = editInfo.option;
+				this.multipleAnswers = editInfo.answer;
+				this.trueArr = editInfo.answer.map((item) => item.charCodeAt() - 65);
+				this.options = editInfo.option.map((item, index) => index);
+				this.existOptions = editInfo.option.map((item, index) => index);
 				this.$nextTick(() => {
-					this.initEditors()
-					this.stemEditor.txt.html(editInfo.question)
-				})
+					this.initEditors();
+					this.stemEditor.txt.html(editInfo.question);
+				});
 			}
 		},
 		mounted() {
-			let stemEditor = new E(this.$refs.singleEditor)
+			let stemEditor = new E(this.$refs.singleEditor);
 			stemEditor.config.onchange = (html) => {
-				this.stemContent = html
-			}
+				this.stemContent = html;
+			};
 			stemEditor.config.uploadImgShowBase64 = true;
-			this.$editorTools.initMyEditor(stemEditor,this)
-			stemEditor.create()
-			this.stemEditor = stemEditor
-
-			if (this.editInfo && this.isEdit) { 
-				console.log('进入多选题Mounted编辑')
-				this.doRender(this.editInfo)
-			}else{
-				this.initEditors()
+			this.$editorTools.initMyEditor(stemEditor, this);
+			stemEditor.create();
+			this.stemEditor = stemEditor;
+			if (this.editInfo && Object.keys(this.editInfo).length > 0) {
+				this.doRender(this.editInfo);
 			}
 		},
 		computed: {
-		    // 选项顺序转化成字母
-		    showTrueAnswers() {
-		        let arr = this.trueArr.map(item => String.fromCharCode(64 + parseInt(item + 1)))
-		        this.transferArr = arr
-		        return this.transferArr.sort().join('')
-		    }
+			// 选项顺序转化成字母
+			showTrueAnswers() {
+				let arr = this.trueArr.map((item) => String.fromCharCode(64 + parseInt(item + 1)));
+				this.transferArr = arr;
+				return this.transferArr.sort().join("");
+			}
 		},
 		watch: {
 			editInfo: {
 				handler(newValue, oldValue) {
 					if (newValue) {
-						console.log('多选接收到的数据')
-						console.log(newValue)
-						this.editSingleInfo = newValue
-						if (Object.keys(newValue).length > 0) {
-							this.doRender(newValue)
-						}
+						this.options = [];
+						this.$nextTick(() => {
+							this.editSingleInfo = newValue;
+							if (Object.keys(newValue).length > 0) {
+								this.doRender(newValue);
+							} else {
+								this.options = [...new Array(4).keys()]
+								this.$nextTick(() => {
+									this.initEditors();
+								});
+							}
+						});
 					}
 				},
-				// immediate:true
-			},
-
+				immediate: true
+			}
 		}
-	}
+	};
 </script>
 <style lang="less" scoped>
-	@import"../index/CreateExercises.less";
+	@import "../index/CreateExercises.less";
 </style>

+ 22 - 18
TEAMModelOS/ClientApp/src/view/evaluation/types/BaseSingle.vue

@@ -262,32 +262,36 @@
 					this.initEditors()
 					this.stemEditor.txt.html(this.editSingleInfo.question)
 				})
-			}else{
-				this.initEditors();
 			}
 		},
 		watch: {
 			editInfo: {
 				handler(newValue, oldValue) {	
 					if (newValue) {
-						console.log('单选接收到的数据')
-						console.log(newValue)
-						this.editSingleInfo = newValue
-						if (Object.keys(newValue).length > 0) {
-							this.stemContent = this.editSingleInfo.question
-							this.optionsContent = this.editSingleInfo.option
-							this.trueIndex = this.editSingleInfo.answer[0].charCodeAt() - 65
-							this.optionTrueIndex = this.editSingleInfo.answer[0].charCodeAt() - 65
-							this.options = this.editSingleInfo.option.map((item, index) => index)
-							this.existOptions = this.editSingleInfo.option.map((item, index) => index)
-							this.$nextTick(() => {
-								this.initEditors()
-								this.stemEditor.txt.html(this.editSingleInfo.question)
-							})
-						}
+						this.options = [];
+						this.$nextTick(() => {
+							this.editSingleInfo = newValue;
+							if (Object.keys(newValue).length > 0) {
+								this.stemContent = this.editSingleInfo.question
+								this.optionsContent = this.editSingleInfo.option
+								this.trueIndex = this.editSingleInfo.answer[0].charCodeAt() - 65
+								this.optionTrueIndex = this.editSingleInfo.answer[0].charCodeAt() - 65
+								this.options = this.editSingleInfo.option.map((item, index) => index)
+								this.existOptions = this.editSingleInfo.option.map((item, index) => index)
+								this.$nextTick(() => {
+									this.initEditors()
+									this.stemEditor.txt.html(this.editSingleInfo.question)
+								})
+							} else {
+								this.options = [...new Array(4).keys()]
+								this.$nextTick(() => {
+									this.initEditors();
+								});
+							}
+						});
 					}
 				},
-				// immediate:true
+				immediate:true
 			},
 
 		}

+ 11 - 2
TEAMModelOS/ClientApp/src/view/learnactivity/ByQuMark.vue

@@ -1,5 +1,5 @@
 <template>
-    <div class="qu-mark-container">
+    <div class="qu-mark-container" ref="mathJaxContainer">
         <Loading :top="200" type="1" style="text-align:center" v-show="dataLoading"></Loading>
         <div class="scoring-paper-header">
             <!-- 保存分数 -->
@@ -570,7 +570,16 @@ export default {
             handler(n, o) {
                 this.quIndex = this.defaultQuIndex
             }
-        }
+        },
+        showQu : {
+            handler(n, o) {
+                if(n) {
+                    window.MathJax.startup.promise.then(() => {
+                        window.MathJax.typesetPromise([this.$refs.mathJaxContainer])
+                    })
+                }
+            }
+        },
     },
     computed: {
         isUpd() {

+ 10 - 1
TEAMModelOS/ClientApp/src/view/learnactivity/ByStuMark.vue

@@ -930,7 +930,16 @@ export default {
       },
       deep: true,
       immediate: true
-    }
+    },
+    showQu : {
+        handler(n, o) {
+            if(n) {
+                window.MathJax.startup.promise.then(() => {
+                    window.MathJax.typesetPromise([this.$refs.mathJaxContainer])
+                })
+            }
+        }
+    },
   },
   computed: {
     markFlagKeys() {

+ 5 - 1
TEAMModelOS/ClientApp/src/view/learnactivity/StuReport.vue

@@ -96,7 +96,7 @@
                 </span> -->
       </div>
       <br style="clear:both" />
-      <div class="qcontent" ref="qcontent" v-if="paperData.length && stuDataNew.data">
+      <div class="qcontent" ref="mathJaxContainer" v-if="paperData.length && stuDataNew.data">
         <div class="qcol" v-for="(question, index) in paperData" v-show="checkedAnsFilter(index)" :key="index">
           <!-- 题目 -->
           <div>
@@ -413,6 +413,10 @@ export default {
         }
       }
       this.paperData = [...paper]
+      
+      window.MathJax.startup.promise.then(() => {
+          window.MathJax.typesetPromise([this.$refs.mathJaxContainer])
+      })
       if (this.paperData.length && this.stuData.ansBlob) {
         // let sas = this.examScope == 'school' ? this.$store.state.user.schoolProfile.blob_sas : this.$store.state.user.userProfile.blob_sas
         // let blobUrl = this.examScope == 'school' ? JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri : JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8")).blob_uri

+ 12 - 3
TEAMModelOS/ClientApp/src/view/learnactivity/byStu/ByStuMark.vue

@@ -54,7 +54,7 @@
       <template v-if="!isComplete && studentAnswer.stuAnswers && studentAnswer.stuAnswers.length">
         <!-- 默认题目顺序 -->
         <template v-if="itemSort == 1">
-          <div v-for="(quItem) in defaultList" :key="quItem.quNo">
+          <div v-for="(quItem) in defaultList" :key="quItem.quNo" ref="mathJaxContainer">
             <template v-if="quItem.children && quItem.children.length">
               <QuAndScore :ref="'QuAndScore'+ child.index" :stuMark="studentAnswer.mark[child.index]" @click.native="config.activeIndex = child.index" v-model="studentAnswer.scores[child.index]" v-for="(child) in quItem.children" :key="child.quNo" :stuAnswer="studentAnswer.stuAnswers[child.index]" :questionItem="child" :config="config" @on-save-mark="saveMark" @on-del-mark="delMark"></QuAndScore>
             </template>
@@ -65,7 +65,7 @@
         </template>
         <!-- 按题型排序 -->
         <template v-else>
-          <div v-for="(typeItem) in groupList" :key="typeItem.type">
+          <div v-for="(typeItem) in groupList" :key="typeItem.type" ref="mathJaxContainer">
             <p class="type-name">
               {{ $tools.getChineseByNum(getLatestTypeIndex(typeItem.type) + 1) }}
               {{ exersicesType[typeItem.type] }}
@@ -256,7 +256,16 @@ export default {
           this.isUpd = true
         }
       }
-    }
+    },
+    'config.showQu' : {
+      handler(n, o) {
+        if(n) {
+          window.MathJax.startup.promise.then(() => {
+						window.MathJax.typesetPromise([this.$refs.mathJaxContainer])
+					})
+        }
+      }
+    },
   },
   methods: {
     handleInitPaper(newPaper) {

+ 2 - 1
TEAMModelOS/ClientApp/src/view/learnactivity/tabs/AnswerTable.vue

@@ -877,6 +877,7 @@
 									let sName;
 									let sInfo = this.examInfo.subjects.find((item) => item.id === key);
 									if (sInfo) sName = sInfo.name;
+									var srcReg = /<img [^>]*src=['"]([^'"]+)[^>]*>/g;
 									subjectData[key].forEach((ss, ssIndex) => {
 										// 首次需要处理表头
 										if (flag) {
@@ -902,7 +903,7 @@
 										ss.score.forEach((s, i) => {
 											let sk = "Q" + (i + 1);
 											stuItem[sk] = s;
-											ansItem[sk] = JSON.parse(ansResults[ssIndex])[i][0] || "";
+											ansItem[sk] = JSON.parse(ansResults[ssIndex])[i][0]?.replaceAll(srcReg,'') || "";
 											t += s > -1 ? s : 0;
 										});
 										stuItem.total = t;

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

@@ -882,7 +882,8 @@ export default {
             let defaultPath = '/home'
             console.error(this.$route.query.target)
             console.error(targetPath)
-            let homePath = targetPath ? (targetPath.includes('home/') ? targetPath : defaultPath) : defaultPath
+            // let homePath = targetPath ? (targetPath.includes('home/') ? targetPath : defaultPath) : defaultPath
+            let homePath = targetPath || defaultPath // 部分跳转URL没有home/这一层
             localStorage.setItem('platform', 'school')
             localStorage.setItem('target_path', '')
             this.$router.push({ path: homePath })

+ 1 - 1
TEAMModelOS/ClientApp/src/view/newsheet/BaseEditor.vue

@@ -958,7 +958,7 @@
 		position: relative;
 		width: 770px;
 		margin: 0 auto;
-		border: 1px solid #000 !important;
+		border: 2px solid #000 !important;
 
 		.cell-item {
 			display: inline-block;

+ 1 - 1
TEAMModelOS/ClientApp/src/view/newsheet/SheetBaseInfo.vue

@@ -276,7 +276,7 @@
 				.attr({
 					fill: "rgba(0,0,0,0)",
 					stroke: "#000",
-					strokeWidth: 1,
+					strokeWidth: 2,
 					shapeRendering: "crispEdges",
 				});
 			// this.setInfoConfig()

+ 1 - 1
TEAMModelOS/ClientApp/src/view/newsheet/SheetObjective.vue

@@ -132,7 +132,7 @@
 						shapeRendering:"crispEdges",
 						fill: "none",
 						stroke: "black",
-						strokeWidth:1
+						strokeWidth:2
 					});
 				this.objectiveGroup.add(infoBox);
 				this.$EventBus.$emit('doRefresh')

+ 34 - 5
TEAMModelOS/ClientApp/src/view/student-web/AppNew.vue

@@ -294,7 +294,6 @@ export default {
             addClassNo: '',
             selectClass: null,
             courseList: [],
-            courseList: [],
             studentList: {
                 type: null,
                 data: [],
@@ -382,7 +381,7 @@ export default {
         };
     },
     computed: {
-        ...mapGetters(["getNowCourse",]),
+        ...mapGetters(["getNowCourse", "getAllCourse", "getCourseTop"]),
         ...mapState({
             user: state => state.user,
             isScale: state => state.studentWeb.isScale,
@@ -425,13 +424,19 @@ export default {
             deep: true,
             immediate: true
         },
+        getAllCourse: {
+            handler(n, o) {
+                this.courseList = n
+            },
+            deep: true
+        },
     },
     created() {
         this.getUsers();
         this.$store.dispatch('user/checkStudentProfile');// 檢查學生的詳細資訊,刷新后同步localStorage数据
         this.selectClass = Number(localStorage.getItem("courseIndex"))
     },
-    mounted() {
+    async mounted() {
         console.log(localStorage.getItem("scale"))
         if(this.userInfo.scope === "student" && this.userInfo.area != '7a51072f-b329-4e74-99e0-ba0407ba8926') {
             let scale = Number(localStorage.getItem("scale"))
@@ -458,6 +463,8 @@ export default {
                 })
             }
         }
+        let courseTop = await this.getPreference()
+        this.$store.commit("setCourseTop", courseTop)
         this.findSchoolInfo()
         this.$nextTick(() => {
             window.addEventListener("resize", this.onResize);
@@ -478,6 +485,20 @@ export default {
         window.removeEventListener("resize", this.onResize);
     },
     methods: {
+        getPreference() {
+            return new Promise((resolve, reject) => {
+                this.$api.studentWeb.getPreference({}).then(res => {
+                    if(res.code === 200) {
+                        let arr = res.personalSetting.setting?.course || []
+                        resolve(arr)
+                    } else {
+                        resolve([])
+                    }
+                }).catch(e => {
+                    reject([])
+                })
+            })
+        },
         onResize() {
             this.windowWidth = window.innerWidth;
             this.windowHeight = window.innerHeight;
@@ -672,6 +693,7 @@ export default {
                                 Object.assign(info, sche)
                                 info.showName = `${info.name}(${info.teacherName}-${info.groupName ? info.groupName : ''})`
                                 info.notice = sche.notice
+                                info.top = false
                                 list.push(info)
                             })
                         })
@@ -714,6 +736,7 @@ export default {
                                 times: [], //排课
                                 isSystem: list.length ? false : true,
                                 // subjectList: noCourseSubj,
+                                top: false,
                             }
                             if(noCourseSubj.length) admClass.subjectList = noCourseSubj
                             list.unshift(admClass)
@@ -721,13 +744,19 @@ export default {
                     }
                     console.log(list);
                 }
-                this.courseList = list
+                this.getCourseTop.forEach(item => {
+                    let index = list.findIndex(course => item.id === course.id && item.groupId === course.groupId)
+                    let courseInfo = list.splice(index, 1)[0]
+                    courseInfo.top = true
+                    list.unshift(courseInfo)
+                })
+                this.$store.commit("setAllCourse", list)
+                this.courseList = this.getAllCourse
                 if(this.selectClass && this.selectClass <= list.length) {
                     this.classChange(this.selectClass)
                 } else {
                     this.selectClass = 0
                 }
-                this.$store.commit("setAllCourse", this.courseList)
             })
         },
         // 新版会返回名单,不再调用这个接口

+ 3 - 3
TEAMModelOS/Controllers/Both/PaperController.cs

@@ -111,7 +111,7 @@ namespace TEAMModelOS.Controllers
             var id = jwt.Payload.Sub;
             var client = _azureCosmos.GetCosmosClient();
             List<object> papers = new List<object>();
-            var query = $"select c.id,c.subjectCode,c.code,c.periodCode,c.name,c.itemCount,c.level,c.pointItem,c.pointScore,c.score,c.gradeCode,c.createTime,c.sheet,c.sheetNo,c.tags ,c.mode ,c.itemSort,c.qamode,c.attachments from c where c.id = {id}";
+            var query = $"select c.id,c.subjectCode,c.code,c.periodCode,c.name,c.itemCount,c.level,c.pointItem,c.pointScore,c.score,c.gradeCode,c.createTime,c.sheet,c.sheetNo,c.tags ,c.mode ,c.itemSort,c.qamode,c.attachments ,c.creatorId,c.secret from c where c.id = {id}";
             await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{school_code}") }))
             {
                 using var json = await JsonDocument.ParseAsync(item.ContentStream);
@@ -174,7 +174,7 @@ namespace TEAMModelOS.Controllers
             
             if (scope.ToString().Equals("school"))
             {
-                sql.Append("select  c.id,c.code,c.name,c.blob,c.periodId,c.gradeIds,c.subjectId,c.subjectName,c.score,c.useCount,c.scope,c.scoring,c.createTime,c.sheet ,c.mode ,c.sheetNo, c.tags,c.itemSort,c.qamode,c.isCodeOption,c.attachments from c");
+                sql.Append("select  c.id,c.code,c.name,c.blob,c.periodId,c.gradeIds,c.subjectId,c.subjectName,c.score,c.useCount,c.scope,c.scoring,c.createTime,c.sheet ,c.mode ,c.sheetNo, c.tags,c.itemSort,c.qamode,c.isCodeOption,c.attachments,c.creatorId,c.secret from c");
                 AzureCosmosQuery cosmosDbQuery = SQLHelper.GetSQL(dict, sql);
                 await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<Paper>(queryDefinition: cosmosDbQuery.CosmosQueryDefinition, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{code}") }))
                 {
@@ -192,7 +192,7 @@ namespace TEAMModelOS.Controllers
             }
             if (scope.ToString().Equals("private"))
             {
-                sql.Append("select c.id,c.code,c.name,c.blob,c.subjectName,c.score,c.useCount,c.scope,c.scoring ,c.createTime ,c.sheet,c.sheetNo ,c.mode , c.tags, c.itemSort,c.qamode,c.isCodeOption,c.attachments from c");
+                sql.Append("select c.id,c.code,c.name,c.blob,c.subjectName,c.score,c.useCount,c.scope,c.scoring ,c.createTime ,c.sheet,c.sheetNo ,c.mode , c.tags, c.itemSort,c.qamode,c.isCodeOption,c.attachments,c.creatorId,c.secret from c");
                 AzureCosmosQuery cosmosDbQuery = SQLHelper.GetSQL(dict, sql);
                 await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<Paper>(queryDefinition: cosmosDbQuery.CosmosQueryDefinition, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{code}") }))
                 {

+ 2 - 0
TEAMModelOS/Controllers/Both/PersonalSettingController.cs

@@ -124,6 +124,7 @@ namespace TEAMModelOS.Controllers.Both
                 {
                     personalSetting = JsonDocument.Parse(response.Content).RootElement.ToObject<PersonalSetting>();
                     personalSetting.setting=_setting;
+                    await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, tbname).UpsertItemAsync(personalSetting, new Azure.Cosmos.PartitionKey(code));
                 }
                 else
                 {
@@ -136,6 +137,7 @@ namespace TEAMModelOS.Controllers.Both
                         setting= _setting,
                         userType=$"{scope}"
                     };
+                    await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, tbname).UpsertItemAsync(personalSetting, new Azure.Cosmos.PartitionKey(code));
                 }
                 return Ok(new { code = 200, personalSetting });
             }

+ 2 - 141
TEAMModelOS/Controllers/Common/HomeworkController.cs

@@ -434,42 +434,7 @@ namespace TEAMModelOS.Controllers.Learn
                 return Ok(new { flag = false, error = 404 });
             }
         }
-        public class TypeUser {
-            public string irs { get; set; }
-            public string no { get; set; }
-            public  string nickname{ get; set; }
-            public string userid { get; set; }
-            
-            public string userType { get; set; }
-            public string userSchool { get; set; }
-         
-            public string username { get; set; }
-            public List<TypeUserClass> classes { get; set; } = new List<TypeUserClass>();
-        }
-        public class TypeUserClass
-        {
-            public string id { get; set; }
-            public string name { get; set; }
-        }
-        public class HomeworkUser
-        {
-            public string irs { get; set; }
-            public string no { get; set; }
-            public string nickname { get; set; }
-            public string userid { get; set; }
-            public string userType { get; set; }
-            public string userSchool { get; set; }
-            public long submitTime { get; set; }
-            public double score { get; set; } = -1;
-            public double studentStar { get; set; } = -1;
-            public double teacherStar { get; set; } = -1;
-            public bool submit { get; set; }
-           
-            public string username { get; set; }
-            public List<TypeUserClass> classes { get; set; } = new List<TypeUserClass>();
-            public List<string> replies { get; set; } = new List<string>();
-            public List<Attachment> content { get; set; } = new List<Attachment>();
-        }
+       
         /// <summary>
         /// 互动
         /// </summary>
@@ -514,112 +479,8 @@ namespace TEAMModelOS.Controllers.Learn
                             // if (!request.TryGetProperty("typeUsers", out JsonElement _typeUsers)) return BadRequest();
                             if (!request.TryGetProperty("listIds", out JsonElement _listIds)) return BadRequest();
                             List<string> listIds = _listIds.ToJsonString().ToObject<List<string>>();
-                            List<TypeUser> typeUsers = new List<TypeUser>();
-                            (List<RMember> tmdinfos, List<RGroupList> classInfo) = await GroupListService.GetMemberByListids(_coreAPIHttpService,client, _dingDing, listIds, homework.school,null,-1,homework.startTime);
-                            var addStudentsCls = tmdinfos.FindAll(x => x.type == 2);
-                            var addTmdidsCls = tmdinfos.FindAll(x => x.type == 1);
-                            if ($"{_targetType}".Equals("research", StringComparison.OrdinalIgnoreCase) || $"{_targetType}".Equals("yxtrain", StringComparison.OrdinalIgnoreCase))
-                            {
-
-
-                                if (tmdinfos.IsNotEmpty())
-                                {
-
-                                    tmdinfos.ForEach(x => {
-                                        var clases = classInfo.Where(y => y.members.Select(z => z.id).Contains(x.id)).ToList();
-                                        typeUsers.Add(new TypeUser() {irs=x.irs,no=x.no,  nickname =x.nickname, userid = x.id, userType = "tmdid", username = x.name, classes = clases.Select(x => new TypeUserClass { id = x.id, name = x.name }).ToList() });
-                                    });
-                                }
-                            }
-                            else {
-                                if (addTmdidsCls.IsNotEmpty())
-                                {
-                                    addTmdidsCls.ForEach(x => { 
-                                        var clases = classInfo.Where(y => y.members.Select(z => z.id).Contains(x.id)).ToList();
-                                        typeUsers.Add(new TypeUser() { irs = x.irs, no = x.no, nickname = x.nickname, userid = x.id, userType = "tmdid", username = x.name, classes = clases.Select(x => new TypeUserClass { id = x.id, name = x.name }).ToList() });
-                                    });
-                                }
-                                if (addStudentsCls.IsNotEmpty())
-                                {
-                                    addStudentsCls.ForEach(x => {
-                                        var clases = classInfo.Where(y => y.members.Select(z => z.id).Contains(x.id)).ToList();
-                                        typeUsers.Add(new TypeUser() { irs = x.irs, no = x.no, nickname = x.nickname, userid = x.id, userType = "student", username = x.name, userSchool = x.code.Replace("Base-", ""), classes = clases.Select(x => new TypeUserClass { id = x.id, name = x.name }).ToList() });
-                                    });
-                                }
-                            }
-
-                            List<dynamic> rscs = new List<dynamic>();
 
-                            string debateCode = "";
-                            string debateTbname = "";
-                            if (homework.scope.Equals("school"))
-                            {
-                                debateTbname = "School";
-                                debateCode = $"Debate-{homework.school}";
-                            }
-                            else
-                            {
-                                debateTbname = "Teacher";
-                                debateCode = $"Debate-{homework.creatorId}";
-                            }
-                            List<Debate> debates = new List<Debate>();
-                            string sql = $"select value(c) from c where c.comid='{_id}'";
-                            await foreach (var item in client.GetContainer(Constant.TEAMModelOS, debateTbname).GetItemQueryIterator<Debate>(queryText: sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey(debateCode) }))
-                            {
-                                debates.Add(item);
-                            }
-                            List<HomeworkUser> users = new List<HomeworkUser>();
-                            foreach (var typeuser in typeUsers) {
-                                var us = users.Find(x => x.userid.Equals(typeuser.userid));
-                                if (us != null) { continue; }
-                                HomeworkUser user = new HomeworkUser() { irs = typeuser.irs, no = typeuser.no, nickname = typeuser.nickname, userid = typeuser.userid, userSchool = typeuser.userSchool, userType = typeuser.userType, username = typeuser.username, classes = typeuser.classes };
-                                string partition = $"HomeworkRecord-{typeuser.userid}";
-                                if (typeuser.userType.Equals("student"))
-                                {
-                                    partition = $"HomeworkRecord-{typeuser.userSchool}-{typeuser.userid}";
-                                }
-                                else
-                                {
-                                    partition = $"HomeworkRecord-{typeuser.userid}";
-                                }
-                                HomeworkRecord record = null;
-                                try
-                                {
-                                    record = await client.GetContainer(Constant.TEAMModelOS, tbname).ReadItemAsync<HomeworkRecord>($"{_id}", new PartitionKey(partition));
-                                    double star = -1;
-                                    List<double> stars = record.comments.FindAll(s=>s.identity.Equals("student")).Select(x => x.star).ToList();
-                                    if (stars.IsNotEmpty())
-                                    {
-                                        star = stars.Sum() / stars.Count;
-                                    }
-                                    user.submit = true;
-                                    user.studentStar = star;
-                                    user.score = record.score;
-                                    user.submitTime = record.time;
-                                    user.content = record.content;
-                                    // user.username = typeuser.username;
-                                    var teacherComment = record.comments.Where(x => x.identity.Equals("teacher") && record.teacher.Equals(userid)).FirstOrDefault();
-                                    if (teacherComment != null)
-                                    {
-                                        user.teacherStar = teacherComment.star;
-                                        List<string> replies = new List<string>();
-                                        teacherComment.replyIds.ForEach(x => {
-                                            var reply = debates.SelectMany(x => x.replies).ToList().Find(r => r.id.Equals(x));
-                                            if (reply != null)
-                                            {
-                                                replies.Add(reply.comment);
-                                            }
-                                        });
-                                        user.replies = replies;
-                                    }
-                                }
-                                catch (CosmosException ex)
-                                {
-                                    user.submit = false;
-                                    record = null;
-                                }
-                                users.Add(user);
-                            }
+                            List<HomeworkUser> users =  await  HomeworkService.AnswerRecordAll(client, _coreAPIHttpService, _dingDing, homework, userid, tbname, listIds, $"{_targetType}");
                             return Ok(new { users });
                         }
                         catch (CosmosException ex)

+ 3 - 3
TEAMModelOS/Controllers/Third/IRS/ThirdIRSController.cs

@@ -146,17 +146,17 @@ namespace TEAMModelOS.Controllers
                                                 JsonElement json = content.ToObject<JsonElement>();
                                                 if (json.GetProperty("code").GetInt32()==0)
                                                 {
-                                                    await _dingDing.SendBotMsg($"139zhxy电子学生证任务推送成功。\n{json.GetProperty("message").GetString()}{data.ToJsonString()}", GroupNames.成都开发測試群組);
+                                                    await _dingDing.SendBotMsg($"{_option.Location},139zhxy电子学生证任务推送成功。\n{json.GetProperty("message").GetString()}{data.ToJsonString()}", GroupNames.成都开发測試群組);
                                                 }
                                                 else
                                                 {
-                                                    await _dingDing.SendBotMsg($"139zhxy电子学生证任务推送异常,返回状态码:{json.GetProperty("code")}\n{json.GetProperty("message").GetString()}", GroupNames.成都开发測試群組);
+                                                    await _dingDing.SendBotMsg($"{_option.Location},139zhxy电子学生证任务推送异常,返回状态码:{json.GetProperty("code")}\n{json.GetProperty("message").GetString()}\n{data.ToJsonString()}", GroupNames.成都开发測試群組);
                                                 }
                                             }
                                             else
                                             {
                                                 string content = await httpResponse.Content.ReadAsStringAsync();
-                                                await _dingDing.SendBotMsg($"139zhxy电子学生证任务推送异常,状态码:{httpResponse.StatusCode}\n{content}", GroupNames.成都开发測試群組);
+                                                await _dingDing.SendBotMsg($"{_option.Location},139zhxy电子学生证任务推送异常,状态码:{httpResponse.StatusCode}\n{content}\n{data.ToJsonString()}", GroupNames.成都开发測試群組);
                                             }
                                             break;
                                         }

+ 2 - 0
TEAMModelOS/Controllers/Third/Moofen/MoofenController.cs

@@ -199,7 +199,9 @@ namespace TEAMModelOS.Controllers.Third.Moofen
 
         [HttpPost("moofen/question")]
         [AuthToken(Roles = "teacher,admin")]
+#if !DEBUG
         [Authorize(Roles = "IES")]
+#endif
         public async Task<IActionResult> Question(JsonElement json) 
         {
             var tokenData= HttpContext.GetAuthTokenInfo();

File diff suppressed because it is too large
+ 168 - 29
TEAMModelOS/Controllers/XTest/TestController.cs


+ 4 - 0
TEAMModelOS/Lang/zh-cn.json

@@ -35,6 +35,10 @@
   "blob-space-school-notify": [ "剩余空间不足", "亲爱的{schoolName}管理员{tmdname}您好,提醒您贵校剩余空间不足{percent}%,请联系官方客服购买空间,详情请登入IES(www.teammodel.cn)查看,如果已经扩容请忽略此通知。" ],
   "blob-space-private-notify": [ "剩余空间不足", "亲爱的{tmdname}您好,提醒您的帐号{tmdid}授权的剩余空间不足{percent}%,请联系学校管理员配置或是购买个人空间,如果已经扩容请忽略此通知。" ],
   "school-scale-notify": [ "学生登录数已达学校规模数上限", "亲爱的{schoolName}管理员{tmdname}您好,提醒您贵校学生登录数已达规模数上限:({scale}),请联系官方客服购买规模数,详情请登入IES(www.teammodel.cn)查看,如果已经购买请忽略此通知。" ],
+  "exam-submit-private-notify": [ "评测作答提交汇总通知", "{tmdname}您好,你发布的评测:{examName},截至{sendTime},已经有{submitCount}位学生提交,仍有{unsubmitCont}位学生未提交,请查收。" ],
+  "homework-submit-private-notify": [ "作业提交汇总通知", "{tmdname}您好,你发布的作业:{homeworkName},截至{sendTime},已经有{submitCount}位学生提交,仍有{unsubmitCont}位学生未提交,请查收。" ],
+  "grouplist-join-private-notify": [ "名单人员变化通知", "{tmdname}您好,你的个人课程名单:{grouplistName}已有总人数{memberCount},{sendTime}有{joinCount}加入/离开。" ],
+  "lesson-create-private-notify": [ "名单人员变化通知", "{tmdname}您好,截至当天{sendTime},您使用HiTeach开课数量为{lessonCount}," ],
   "notify-status": {
     "code0": "凭据失效",
     "code1": "参数异常",

+ 4 - 4
TEAMModelOS/TEAMModelOS.csproj

@@ -79,11 +79,11 @@
 		<SpaRoot>ClientApp\</SpaRoot>
 		<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
 		<UserSecretsId>078b5d89-7d90-4f6a-88fc-7d96025990a8</UserSecretsId>
-		<Version>5.2405.29</Version>
-		<AssemblyVersion>5.2405.29.1</AssemblyVersion>
-		<FileVersion>5.2405.29.1</FileVersion>
+		<Version>5.2406.5</Version>
+		<AssemblyVersion>5.2406.5.1</AssemblyVersion>
+		<FileVersion>5.2406.5.1</FileVersion>
 		<Description>TEAMModelOS(IES5)</Description>
-		<PackageReleaseNotes>IES版本说明版本切换标记5.2405.29.1</PackageReleaseNotes>
+		<PackageReleaseNotes>IES版本说明版本切换标记5.2406.5.1</PackageReleaseNotes>
 		<PackageId>TEAMModelOS</PackageId>
 		<Authors>teammodel</Authors>
 		<Company>醍摩豆(成都)信息技术有限公司</Company>

+ 1 - 2
TEAMModelOS/appsettings.Development.json

@@ -18,10 +18,9 @@
     "IdTokenSalt": "8263692E2213497BB55E74792B7900B4",
     "HttpTrigger": "https://teammodelosfunction-test.chinacloudsites.cn/api/",
     //"HttpTrigger": "http://localhost:7071/api/"
-    "Version": "5.2405.29.1"
+    "Version": "5.2406.5.1"
   },
   "Azure": {
-
     // 测试站数据库
     "Storage": {
       "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=teammodeltest;AccountKey=O2W2vadCqexDxWO+px+QK7y1sHwsYj8f/WwKLdOdG5RwHgW/Dupz9dDUb4c1gi6ojzQaRpFUeAAmOu4N9E+37A==;EndpointSuffix=core.chinacloudapi.cn"

+ 1 - 1
TEAMModelOS/appsettings.json

@@ -18,7 +18,7 @@
     "Exp": 86400,
     "IdTokenSalt": "8263692E2213497BB55E74792B7900B4",
     "HttpTrigger": "https://teammodelosfunction.chinacloudsites.cn/api/",
-    "Version": "5.2405.29.1"
+    "Version": "5.2406.5.1"
   },
   "Azure": {
     "Storage": {