ソースを参照

Merge branch 'develop' of http://163.228.141.122:3000/TEAMMODEL/TEAMModelOS into develop

XW 7 ヶ月 前
コミット
056d4d1506

+ 132 - 0
TEAMModelBI/Controllers/BICommon/BINoticeController.cs

@@ -23,6 +23,12 @@ using TEAMModelOS.SDK.Models.Service.BI;
 using TEAMModelOS.SDK.Models.Cosmos.BI.BISchool;
 using TEAMModelBI.Tool.Extension;
 using TEAMModelOS.SDK.Models;
+using static TEAMModelBI.Controllers.Census.ActivitySticsController;
+using Pipelines.Sockets.Unofficial.Arenas;
+using TEAMModelBI.Tool;
+using Azure.Core;
+using Azure.Storage.Blobs.Models;
+using static TEAMModelBI.Controllers.BICommon.BINoticeController;
 
 namespace TEAMModelBI.Controllers.BICommon
 {
@@ -275,5 +281,131 @@ namespace TEAMModelBI.Controllers.BICommon
         }
 
 
+        /// <summary>
+        /// 取得學區及所屬學校、教師數
+        /// </summary>
+        /// <param name="jsonElement"></param>
+        /// <returns></returns>
+        [HttpPost("get-areas")]
+#if !DEBUG
+        [Authorize(Roles = "IES")]
+        [AuthToken(Roles = "admin")]
+#endif
+        public async Task<IActionResult> GetAreas(JsonElement jsonElement)
+        {
+            try
+            {
+                string reqAreaId = (jsonElement.TryGetProperty("areaId", out JsonElement _areaId)) ? _areaId.GetString() : string.Empty;
+                bool showSchool = (jsonElement.TryGetProperty("showSchool", out JsonElement _showSchool)) ? _showSchool.GetBoolean() : false; //是否列出學校
+                var cosmosClient = _azureCosmos.GetCosmosClient();
+                List<AreaInfo> areaInfos = new();
+
+                //取得學區
+                string sqlArea = $"SELECT c.id, c.name FROM c ";
+                string whereArea = string.Empty ;
+                if (!string.IsNullOrWhiteSpace(reqAreaId))
+                {
+                    string wherePre = (!string.IsNullOrWhiteSpace(whereArea)) ? " AND " : string.Empty ;
+                    whereArea += $"{wherePre} c.id = '{reqAreaId}' ";
+                }
+                if(!string.IsNullOrWhiteSpace(whereArea))
+                {
+                    whereArea = $" WHERE {whereArea}";
+                    sqlArea = sqlArea + whereArea;
+                }
+                await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", Constant.Normal).GetItemQueryIteratorSql<AreaInfo>(queryText: sqlArea, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base-Area") }))
+                {
+                    areaInfos.Add(item);
+                }
+                //取得學校ID列表
+                Dictionary<string, string> schAreaDic = new Dictionary<string, string>();
+                Dictionary<string, string> schNameDic = new Dictionary<string, string>();
+                List<string> teacherCodes = new List<string>();
+                List<string> areaIds = areaInfos.Select(a => a.id).ToList();
+                string sqlSch = $"SELECT c.id, c.name, c.areaId FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(areaIds)}, c.areaId)";
+                ///實體校
+                await foreach (var item in cosmosClient.GetContainer(Constant.TEAMModelOS, Constant.School).GetItemQueryIteratorSql<JsonElement>(queryText: sqlSch, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Base") }))
+                {
+                    string schId = item.GetProperty("id").ToString();
+                    string schName = item.GetProperty("name").ToString();
+                    string areaId = item.GetProperty("areaId").ToString();
+                    if(!schAreaDic.ContainsKey(schId))
+                    {
+                        schAreaDic.Add(schId, areaId);
+                        schNameDic.Add(schId, schName);
+                    }
+                    if(!teacherCodes.Contains($"Teacher-{schId}"))
+                    {
+                        teacherCodes.Add($"Teacher-{schId}");
+                    }
+                }
+                ///虛擬校
+                await foreach (var item in cosmosClient.GetContainer(Constant.TEAMModelOS, Constant.School).GetItemQueryIteratorSql<JsonElement>(queryText: sqlSch, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"VirtualBase") }))
+                {
+                    string schId = item.GetProperty("id").ToString();
+                    string schName = item.GetProperty("name").ToString();
+                    string areaId = item.GetProperty("areaId").ToString();
+                    if (!schAreaDic.ContainsKey(schId))
+                    {
+                        schAreaDic.Add(schId, areaId);
+                        if(!schNameDic.ContainsKey(schId)) schNameDic.Add(schId, schName);
+                    }
+                }
+                //取得學校教師數
+                Dictionary<string, int> schTeacherCntDic = new Dictionary<string, int>();
+                string sqlTch = $"SELECT REPLACE(c.code, 'Teacher-', '')  as schoolId, COUNT(c.id) AS tchCnt FROM c WHERE c.pk = 'Teacher' AND c.status = 'join' AND ARRAY_CONTAINS({JsonSerializer.Serialize(teacherCodes)}, c.code) GROUP BY c.code";
+                await foreach (var item in cosmosClient.GetContainer(Constant.TEAMModelOS, Constant.School).GetItemQueryIteratorSql<JsonElement>(queryText: sqlTch, requestOptions: null))
+                {
+                    string schId = item.GetProperty("schoolId").ToString();
+                    int tchCnt = item.GetProperty("tchCnt").GetInt32();
+                    schTeacherCntDic.Add(schId, tchCnt);
+                }
+                //資料整理1 有教師則記入
+                foreach (KeyValuePair<string, int> item in schTeacherCntDic)
+                {
+                    string schId = item.Key;
+                    int tchCnt = item.Value;
+                    string areaId = schAreaDic[schId];
+                    AreaInfo areaInfo = areaInfos.Where(a => a.id.Equals(areaId)).FirstOrDefault();
+                    if(areaInfo != null)
+                    {
+                        areaInfo.scCnt++;
+                        areaInfo.tchCnt += tchCnt;
+                        if (showSchool)
+                        {
+                            AreaSchoolInfo sch = new AreaSchoolInfo() { id = schId, name = schNameDic[schId], tchCnt = tchCnt };
+                            areaInfo.schools.Add(sch);
+                        }
+                    }
+                }
+                //資料整理2 移除無學校學區
+                var result = areaInfos.Where(a => a.scCnt > 0).ToList();
+
+                return Ok(new { state = RespondCode.Ok, result });
+            }
+            catch (Exception ex)
+            {
+                return BadRequest(new { ex });
+            }
+        }
+
+        /// <summary>
+        /// 区级信息
+        /// </summary>
+        public record AreaInfo
+        {
+            public string id { get; set; }
+            public string name { get; set; }
+            public int scCnt { get; set; } = 0;
+            public int tchCnt { get; set; } = 0;
+            public List<AreaSchoolInfo> schools { get; set; } = new();
+        }
+
+        public record AreaSchoolInfo
+        {
+            public string id { get; set; }
+            public string name { get; set; }
+            public int tchCnt { get; set; } = 0;
+        }
     }
 }

+ 125 - 0
TEAMModelBI/Controllers/BITest/TestController.cs

@@ -58,6 +58,7 @@ using Microsoft.OData.Edm;
 using TEAMModelOS.SDK.Models.Cosmos.OpenEntity;
 using TEAMModelOS.SDK.Models.Cosmos.BI.BICommon;
 using Microsoft.Azure.Cosmos.Table;
+using Microsoft.AspNetCore.Authorization;
 
 namespace TEAMModelBI.Controllers.BITest
 {
@@ -1801,6 +1802,130 @@ namespace TEAMModelBI.Controllers.BITest
             return Ok(new { state = 200 });
         }
 
+        /// <summary>
+        /// 生成BI新通知資料格式
+        /// </summary>
+        /// <param name="jsonElement"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+#if !DEBUG
+        [Authorize(Roles = "IES")]
+#endif
+        [HttpPost("crt-binotice-data")]
+        public async Task<IActionResult> CrtBiNoticeData(JsonElement jsonElement)
+        {
+            var client = _azureCosmos.GetCosmosClient();
+
+            //批次
+            BINotice bINoticeM = new BINotice();
+            bINoticeM.id = "6ec182ab-ad08-43f4-8cc2-6fdbd985f584";
+            bINoticeM.msgType = "email";
+            bINoticeM.selType = "mul";
+            bINoticeM.accept = new List<PickParam>();
+            ///學區
+            PickParam area = new PickParam() {
+                mode = "area",
+                areaId = "02944f32-f534-3397-ea56-e6f1fc6c3714"
+            };
+            bINoticeM.accept.Add(area);
+            ///地理 學校
+            PickParam geoSch = new PickParam() {
+                mode = "schGeo",
+                countryId = "TW",
+                cityId = "30"
+            };
+            bINoticeM.accept.Add(geoSch);
+            ///地理 ID
+            PickParam geoId = new PickParam()
+            {
+                mode = "tmidGeo",
+                countryId = "TW",
+                cityId = "30"
+            };
+            bINoticeM.accept.Add(geoId);
+            ///機構
+            PickParam inst = new PickParam()
+            {
+                mode = "inst",
+                unitType = "1"
+            };
+            bINoticeM.accept.Add(inst);
+            ///學校ID
+            PickParam sch = new PickParam()
+            {
+                mode = "sch",
+                school = new NoticeSchool() { eduId = "hbgl", shortCode = "hbgl", name = "測試學校" }
+            };
+            bINoticeM.accept.Add(sch);
+            await client.GetContainer(Constant.TEAMModelOS, Constant.Common).UpsertItemAsync(bINoticeM, new PartitionKey($"BINotice"));
+
+            //個別
+            BINotice bINoticeS = new BINotice();
+            bINoticeS.id = "9760087c-e29c-494d-9a17-3523f6ddbc27";
+            bINoticeS.msgType = "email";
+            bINoticeS.selType = "sin";
+            ///搜尋資料
+            bINoticeS.search = new List<PickParam>();
+            ///生成時間
+            PickParam crtTime = new PickParam()
+            {
+                mode = "crtTime",
+                crtFrom = 1588262400,
+                crtTo = 1609430399
+            };
+            bINoticeS.search.Add(crtTime);
+            ///地理資訊
+            PickParam tmidGeo = new PickParam()
+            {
+                mode = "tmidGeo",
+                countryId = "TW",
+                cityId = "30"
+            };
+            bINoticeS.search.Add(tmidGeo);
+            ///軟體使用
+            PickParam softUse = new PickParam()
+            {
+                mode = "softUse"
+            };
+            softUse.softUse.Add("HiTeach");
+            softUse.softUse.Add("HiTeachCC");
+            softUse.softUse.Add("HiTA");
+            softUse.softUse.Add("IES5");
+            bINoticeS.search.Add(softUse);
+            ///積分
+            PickParam point = new PickParam()
+            {
+                mode = "point",
+                pointFrom = 10,
+                pointTo = 80
+            };
+            bINoticeS.search.Add(point);
+            ///預選學校
+            PickParam schPre1 = new PickParam()
+            {
+                mode = "sch",
+                school = new NoticeSchool() { eduId = "hbgl", shortCode = "hbgl", name = "測試學校" }
+            };
+            bINoticeS.search.Add(schPre1);
+            PickParam schPre2 = new PickParam()
+            {
+                mode = "sch",
+                school = new NoticeSchool() { eduId = "habook", shortCode = "habook", name = "玉山學校" }
+            };
+            bINoticeS.search.Add(schPre2);
+
+            ///接收名單
+            PickParam tmidList = new PickParam() {
+                mode = "tmid"
+            };
+            tmidList.tmId.Add("1595321354");
+            tmidList.tmId.Add("1522758684");
+            bINoticeS.accept.Add(tmidList);
+            await client.GetContainer(Constant.TEAMModelOS, Constant.Common).UpsertItemAsync(bINoticeS, new PartitionKey($"BINotice"));
+
+            return Ok(new { state = 200 });
+        }
+
         public class linqTest
         {
             public string id { get; set; }

+ 1 - 1
TEAMModelBI/Controllers/Census/ActivitySticsController.cs

@@ -950,7 +950,7 @@ namespace TEAMModelBI.Controllers.Census
             public string standard { get; set; }
             public string standardName { get; set; }
             public int scCnt { get; set; } = 0;
-            //public int tchCnt { get; set; } = 0;
+            public int tchCnt { get; set; } = 0;
             //public int stuCnt { get; set; } = 0;
         }
 

+ 2 - 2
TEAMModelOS.Extension/HTEX.Lib/Translator/PPTX2HTEXTranslator.cs

@@ -1,4 +1,4 @@
-using DocumentFormat.OpenXml.Packaging;
+锘縰sing DocumentFormat.OpenXml.Packaging;
 using HTEXLib.Builders;
 using HTEXLib.Controller;
 using HTEXLib.Models;
@@ -13,7 +13,7 @@ namespace HTEXLib.Translator
     public   class PPTX2HTEXTranslator
     {
         /// <summary>
-        /// pptx官方的js操作相关接口。 https://docs.microsoft.com/zh-cn/javascript/api/powerpoint?view=powerpoint-js-preview
+        /// pptx官方的js操作相关接口。 https://docs.microsoft.com/zh-cn/javascript/api/powerpoint?view=powerpoint-js-preview
         /// </summary>
         /// <param name="stream"></param>
         /// <returns></returns>

+ 52 - 0
TEAMModelOS.SDK/Models/Cosmos/BI/BICommon/Notice.cs

@@ -15,6 +15,26 @@ namespace TEAMModelOS.SDK.Models.Cosmos.BI
             code = "BINotice";
             pk = "BINotice";
         }
+        #region 新通知類型
+        /// <summary>
+        /// 訊息類型  ext:端外通知  email:Email  sms:簡訊
+        /// </summary>
+        public string msgType { get; set; }
+        /// <summary>
+        /// 挑選方式  mul:批次  sin:個別
+        /// </summary>
+        public string selType { get; set; }
+        /// <summary>
+        /// 搜尋條件 ※複數
+        /// </summary>
+        public List<PickParam> search { get; set; } = new();
+        /// <summary>
+        /// 接收名單 ※複數
+        /// </summary>
+        public List<PickParam> accept { get; set; } = new();
+        #endregion
+
+        #region 原有舊通知類型
         /// <summary>
         /// 消息类型  0系统  1普通类型  2提示类型   3 特殊类型  
         /// </summary>
@@ -72,7 +92,39 @@ namespace TEAMModelOS.SDK.Models.Cosmos.BI
         /// 发送消息来源, 默认(BI)、IES5、HiTA
         /// </summary>
         public string source { get; set; } = "IES";
+        #endregion
     }
+    /// <summary>
+    /// 挑選條件
+    /// </summary>
+    public class PickParam
+    {
+        public string mode { get; set; } //格式 area:學區 schGeo:學校地理 inst:機構 sch:學校     crtTime:帳號生成時間 tmidGeo:TMID地理 softUse:使用軟體 point:積分範圍 tmid:帳號列表(個別挑選用)
+        public string areaId { get; set; } //學區ID ※area專有
+        public string countryId { get; set; } //國ID ※schGeo、tmidGeo專有
+        public string provinceId { get; set; } //省ID ※schGeo、tmidGeo專有
+        public string cityId { get; set; } //市ID ※schGeo、tmidGeo專有
+        public string distId { get; set; } //區ID ※schGeo、tmidGeo專有
+        public string unitType { get; set; } //機構類型ID 1:基礎教育機構(K-小學) 2:中等教育機構(國中、高中/職) 3:高等教育機構 4:政府單位機構 5:NGO機構 6:企業機構 7:其他 8:學前教育 9:特殊教育 ※inst專有
+        public long crtFrom { get; set; } //TMID生成起始時間 ※crtTime專有
+        public long crtTo { get; set; } //TMID生成終止時間 ※crtTime專有
+        public List<string> softUse { get; set; } = new(); //軟體使用 ※softUse專有  "HiTeach"、"HiTeachCC"、"HiTA"、"IES5"、"Account"、"Sokrates"、"SokAPP"、"IRS"
+        public string softUseMode { get; set; } //軟體使用模式 and or ※softUse專有
+        public int pointFrom { get; set; } //積分範圍 起始 ※point專有
+        public int pointTo { get; set; } //積分範圍 終止 ※point專有
+        public NoticeSchool school { get; set; } //學校 ※sch專有
+        public List<string> tmId { get; set; } = new(); //TMID列表 ※tmid專有
+    }
+    /// <summary>
+    /// (訊息用)學校基本資料
+    /// </summary>
+    public class NoticeSchool
+    {
+        public string eduId { get; set; } //學校教育部代碼
+        public string shortCode { get; set; } //學校簡碼
+        public string name { get; set; } //學校名稱
+    }
+
 
     /// <summary>
     /// 发送人群

+ 2 - 2
TEAMModelOS.SDK/Models/Cosmos/Teacher/JointEvent.cs

@@ -153,8 +153,8 @@ namespace TEAMModelOS.SDK.Models
         public string name { get; set; }
         public string examType { get; set; } //評量類型 "regular":一般競賽 "custom":決賽
         public bool examOverwrite { get; set; } //評量可否重複作答 true:可 false:不可
-        public List<JointEventClassBase> classes { get; set; } = new(); //班級列表 ※examType="custom"才填入,其餘評量班級從報名班級(DB)取得
-        public List<JointEventGroupBase> stuLists { get; set; } = new(); //個人課程列表 ※examType="custom"才填入,其餘評量班級從老師報名課程(DB)取得
+        public List<JointEventClassBase> classes { get; set; } = new(); //[不使用] 評量班級從報名班級(DB)取得
+        public List<JointEventGroupBase> stuLists { get; set; } = new(); //[不使用] 評量班級從老師報名課程(DB)取得
         public List<PaperSimple> papers { get; set; } = new();
         public string progress { get; set; } //進行狀況 "pending" "going" "finish"
         public long createTime { get; set; }

+ 5 - 1
TEAMModelOS/ClientApp/src/api/htcommunity.js

@@ -51,5 +51,9 @@ export default {
     // 設定決賽評量為可見
     setJointExamVisible: function (data) {
         return post('/joint/exam/set-visible', data)
-    }
+    },
+    // 取得活動評量clouDAS資料
+    getAnalysisJoint: function(data) {
+        return post('/analysis/process-joint', data)
+    },
 }

+ 4 - 0
TEAMModelOS/ClientApp/src/api/totalAnalysis.js

@@ -35,4 +35,8 @@ export default {
 	getAnalysis: function(data) {
         return post('/analysis/process', data)
     },
+    /* 最新学情对接接口活動版 */
+	getAnalysisJoint: function(data) {
+        return post('/analysis/process-joint', data)
+    },
 }

+ 93 - 0
TEAMModelOS/ClientApp/src/store/module/totalAnalysis.js

@@ -85,6 +85,46 @@ export default {
 			console.log('转换后的AnalysisJson',val)
 			state.analysisJson = val
 		},
+        // 更新最新学情分析数据活動版
+		updateAnalysisJsonJoint(state,val){
+            //debugger
+			let pointLevelKey = []
+			if(!val) return
+			/* 对知识点模块和认知层次模块进行数据格式化 */
+			val.subjects.forEach((subject,subjectIndex) => {
+				pointLevelKey.push({
+					subjectId:subject.id,
+					levelKey:{
+						level:val.fieldName[subjectIndex].value,
+						pointList:val.fieldName[subjectIndex].value,
+						per:val.fieldPer[subjectIndex].value,
+						wrong:{
+							datas:val.fieldwrong[subjectIndex].value,
+							keys:val.wrongKey
+						},
+						stupercent:tools.getLevelPercent(val,subjectIndex).stuResult,
+						classpercent:tools.getLevelPercent(val,subjectIndex).classResult
+					},
+					pointKey:{
+						level:val.knowName[subjectIndex].value,
+						pointList:val.knowName[subjectIndex].value,
+						per:val.knowPer[subjectIndex].value,
+						wrong:{
+							datas:val.wrong[subjectIndex].value,
+							keys:val.wrongKey
+						},
+						stupercent:tools.getKnowPercent(val,subjectIndex).stuResult,
+						classpercent:tools.getKnowPercent(val,subjectIndex).classResult
+					}
+				})
+			})
+			val.pointLevelKey = pointLevelKey
+			state.currentSubject = null
+			state.indexSubject = null
+			state.curClassIndex = -1
+			console.log('转换后的AnalysisJson',val)
+			state.analysisJsonJoint = val
+		},
 		
 		clearAnalysis(state,val){
 			state.analysisJson = val
@@ -92,6 +132,13 @@ export default {
 			state.currentSubject = val
 			state.classExerciseData = val
 		},
+        // 活動版清除
+        clearAnalysisJoint(state,val){
+			state.analysisJsonJoint = val
+			state.subjectListJoint = val
+			state.currentSubjectJoint = val
+			state.classExerciseDataJoint = val
+		},
 
         // 更新当前评测CODE
         updateCurExam(state, val) {
@@ -160,6 +207,10 @@ export default {
         updateSubjectList(state, val) {
             state.subjectList = val
         },
+        // 更新科目列表 活動版
+        updateSubjectListJoint(state, val) {
+            state.subjectListJoint = val
+        },
 
         // 更新 成绩分析数据
         getAchievementData(state, data) {
@@ -242,9 +293,51 @@ export default {
             } else if (!data && local) {
                 state.currentSubject = local
             }
+        },
+        // 更新当前科目 活動版
+        updateCurSubjectJoint(state, data) {
+            let local = localStorage.getItem('cur_sJoint')
+            // 先把之前科目缓存数据清空 重新获取
+            // state.scatterData = null
+            // state.exerciseData = null
+            if (data) {
+                localStorage.setItem('cur_sJoint', data)
+                state.currentSubjectJoint = data
+                state.examPaperJoint = null
+				state.scatterDataJoint = null
+				state.knowledgeDataJoint = null
+				state.exerciseDataJoint = null
+				state.levelDataJoint = null
+            } else if (!data && local) {
+                state.currentSubjectJoint = local
+            }
         }
     },
     actions: {
+        // 获取最新学情数据活動版
+        getAnalysisJsonJoint(context,params){
+            //debugger
+			return new Promise((r, j) => {
+			    if (context.state.analysisJsonJoint) {
+			        console.log('Cache-analysisJson')
+			        r(context.state.analysisJsonJoint)
+			    } else {
+			        apiTools.totalAnalysis.getAnalysisJoint(params).then(res => { // api请求
+						if(res.classes.length){
+							context.commit('updateAnalysisJsonJoint', res) // 提交到vuex保存数据
+							context.commit('updateSubjectListJoint', res.subjects.map(i => i.name)) // 提交到vuex保存数据
+							context.commit('updateCurSubjectJoint', res.subjects.map(i => i.name)[0]) // 提交到vuex保存数据
+							console.log('API-analysisJsonJoint', res)
+							r(res)
+						}else{
+							j(500)
+						}
+			        }).catch(err => {
+			            j(err)
+			        })
+			    }
+			})
+		},
 		
 		// 获取最新学情数据
 		getAnalysisJson(context,params){

+ 27 - 10
TEAMModelOS/ClientApp/src/utils/public.js

@@ -1819,16 +1819,22 @@ export default {
 	getLevelStuPercent(val, subjectIndex, index) {
 		let result = []
 		val.students.forEach(stu => {
-			result.push([
-				stu.name,
-				stu.className,
-				stu.no || '-',
-				val.fScores[subjectIndex].value[index],
-				stu.subjects[subjectIndex].fieldPoint[index],
-				val.fScores[subjectIndex].value[index] == 0 ? 0 : (stu.subjects[subjectIndex]
-					.fieldPoint[index] / val.fScores[subjectIndex].value[index])
-					.toFixed(2)
-			])
+			// if(stu.name==="沈仕程"){
+			// 	console.log("沈仕程 : "+JSON.stringify(stu));
+			// }
+			// 如果該學生的課程/科目存在  而且 id 相同才加入
+			if(stu.subjects[subjectIndex] && stu.subjects[subjectIndex].id === val.subjects[subjectIndex].id ){
+				result.push([
+					stu.name,
+					stu.className,
+					stu.no || '-',
+					val.fScores[subjectIndex].value[index],
+					stu.subjects[subjectIndex].fieldPoint[index],
+					val.fScores[subjectIndex].value[index] == 0 ? 0 : (stu.subjects[subjectIndex]
+						.fieldPoint[index] / val.fScores[subjectIndex].value[index])
+						.toFixed(2)
+				])
+			}			
 		})
 		return result
 	},
@@ -1836,9 +1842,13 @@ export default {
 	getLevelClassPercent(val, subjectIndex, index) {
 		let result = []
 		val.classes.forEach(classItem => {
+			// 如果該10 班級的課程/科目存在  而且 id 相同才加入
+			if(classItem.subjects[subjectIndex] && classItem.subjects[subjectIndex].id === val.subjects[subjectIndex].id ){
+			
 			result.push(classItem.subjects[subjectIndex].field.map((score, index) => val.fieldwrong[
 				subjectIndex].value[index][1] == 0 ? 0 : Number(score / val.fieldwrong[subjectIndex]
 					.value[index][1]) * 100)[index])
+				}
 		})
 		return result
 	},
@@ -1865,6 +1875,9 @@ export default {
 	getKnowStuPercent(val, subjectIndex, index) {
 		let result = []
 		val.students.forEach(stu => {
+			// 如果該學生的課程/科目存在  而且 id 相同才加入
+			if(stu.subjects[subjectIndex] && stu.subjects[subjectIndex].id === val.subjects[subjectIndex].id ){
+
 			result.push([
 				stu.name,
 				stu.className,
@@ -1875,6 +1888,7 @@ export default {
 					index] / val.kScores[subjectIndex].value[index])
 					.toFixed(2)
 			])
+			}
 		})
 		return result
 	},
@@ -1882,10 +1896,13 @@ export default {
 	getKnowClassPercent(val, subjectIndex, index) {
 		let result = []
 		val.classes.forEach(classItem => {
+			// 如果該 班級的課程/科目存在  而且 id 相同才加入
+			if(classItem.subjects[subjectIndex] && classItem.subjects[subjectIndex].id === val.subjects[subjectIndex].id ){
 			// 取当前班级在每个知识点的得分 除以知识点的总分 得到每个班在该知识点的得分率 index=>知识点下标
 			result.push(classItem.subjects[subjectIndex].point.map((score, pointIndex) => val.wrong[
 				subjectIndex].value[pointIndex][1] == 0 ? 0 : Number(score / val.wrong[subjectIndex]
 					.value[pointIndex][1]) * 100)[index])
+					}
 		})
 		return result
 	},

+ 8 - 3
TEAMModelOS/ClientApp/src/view/htcommunity/htMgtExam.vue

@@ -156,9 +156,9 @@
 							{{ $t("learnActivity.mgtScEv.markSetting") }}
 						</span> -->
 						<!-- 小学情-->
-						<!-- <span :class="curBarIndex == 4 ? 'evalustion-bar-item line-bottom-active line-bottom' : 'evalustion-bar-item line-bottom'" v-show="scope !== 'school'" @click="selectBar(4)">
+						<span :class="curBarIndex == 4 ? 'evalustion-bar-item line-bottom-active line-bottom' : 'evalustion-bar-item line-bottom'" v-show="scope !== 'school'" @click="selectBar(4)">
 							{{ $t("learnActivity.mgtScEv.tab4") }}
-						</span> -->
+						</span>
 					</el-col>
 					<!--右側按鈕-->
 					<el-col :span="2">
@@ -178,6 +178,8 @@
 				<AnswerTable v-else-if="curBarIndex == 3" :examInfo="examDetaiInfo" :correctData="correctData" class="evaluation-base-info" v-show="!(!isNeedMark && !isNotExamMark)"></AnswerTable>
 				<!-- 评测试卷 -->
 				<ExamPaper v-else-if="curBarIndex == 1" :examInfo="examDetaiInfo" :activityIndex="curEvaIndex" :refreshExam="refreshExam" class="evaluation-base-info"></ExamPaper>
+                <!-- 学情分析 -->
+				<CloudDAS v-else-if="curBarIndex == 4" :evInfo="examDetaiInfo"></CloudDAS>					
 					<!-- 暂无数据 -->
 				<EmptyData v-show="!isNeedMark && !isNotExamMark" style="margin-top: 100px"
 						:textContent="`${$t('htcommunity.NoSubjectiveQuestions')}`"></EmptyData>
@@ -236,6 +238,7 @@
 import DataView from "../learnactivity/tabs/DataView.vue";
 import AnswerTable from "../learnactivity/tabs/htAnswerTable.vue";
 import ExamPaper from "../learnactivity/tabs/htExamPaper.vue";
+import CloudDAS from "../learnactivity/tabs/htCloudDAS.vue";
 import XLSX from 'xlsx';
 import excel from '@/utils/excel.js'
 
@@ -244,6 +247,7 @@ export default {
 		DataView,
 		AnswerTable,
 		ExamPaper,
+		CloudDAS
 	},
 	data() {
 		return {
@@ -567,7 +571,7 @@ export default {
 					this.isLoading = false;
 				});
 		},
-		selectBar(index) {
+		selectBar(index) {			
 			this.$EventBus.$emit("EvBarChange", index);
 			this.checkScoreSave(this.handleSelectBar, index);
 		},
@@ -832,6 +836,7 @@ export default {
 		
 			this.dataErr = false;
 			let resData = this.htEvaListShow[this.curEvaIndex];
+			resData.cloudas = true;
 			resData.score = 0;
 			for (let index in resData.papers) {
 				let blob = resData.papers[index].blob;

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

@@ -101,7 +101,7 @@ export default {
       })
     },
     initAnalysisJson(evInfo) {
-        console.error('evInfo', evInfo)
+        //console.error('evInfo', evInfo)
       // 如果评测活动还没有结束 或者 cloudas为false 则不显示诊断分析数据
       if (evInfo.progress !== 'finish' || !evInfo.cloudas) {
         this.showAnalysis = true

+ 342 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/tabs/htCloudDAS.vue

@@ -0,0 +1,342 @@
+<template>
+  <div class="cloudas-container">    
+    <Spin size="large" v-if="!showAnalysis" style="display: flex;justify-content: center;margin-top:100px"></Spin>
+    <EmptyData v-else-if="showAnalysis && isEmpty" :top="120"></EmptyData>
+    <TipsInfo v-else-if="isJointExam && !jointVisiable" :msg="$t('learnActivity.simple.eventNotVisible')" style="background:white;height:100%;">{{currentExamItem.id}}{{isJointExam}}{{jointVisiable}}</TipsInfo>    
+    <div class="cloudas-content" v-else-if="showAnalysis && !isEmpty">
+      <vuescroll ref="vs">
+        <div class="basic-info">
+          <p style="display: flex;align-items: center;">
+            <!-- <span class="info-type" v-if="currentExamItem.examType && currentExamItem.examType.name">{{currentExamItem.examType.name}}</span> -->
+            <span class="info-type" v-show="currentExamItem">{{ getModeLabel(currentExamItem.source,currentExamItem.qamode) }}</span>
+            <span class="info-name" v-show="currentExamItem">{{currentExamItem.name}}</span>
+            <span class="info-grade" v-if="currentExamItem && currentExamItem.period && currentExamItem.period.name">{{currentExamItem.period.name}}</span>
+            <span class="info-subject" v-show="currentExamItem"  v-for="(subject,index) in currentExamItem.subjects" :key="index">{{subject.name}}</span>
+          </p>
+          <p class="info-date-person">
+            <span class="info-item">{{$t('totalAnalysis.echarts_text11')}}: <span style="font-weight:bold" v-show="currentExamItem">{{ $tools.formatTime(currentExamItem.startTime,'yyyy-MM-dd')}}</span></span>
+            <span class="info-item">{{$t('totalAnalysis.echarts_text12')}}: <span style="font-weight:bold" v-show="currentExamItem">{{currentExamItem.stuCount}}</span></span>
+            <span class="info-item">{{$t('totalAnalysis.echarts_text13')}}: <span style="font-weight:bold" v-if="currentExamItem && currentExamItem.stuCount != null && currentExamItem.lostStu">{{currentExamItem.stuCount - currentExamItem.lostStu.length}}</span></span>
+            <span class="info-item">{{$t('totalAnalysis.echarts_text14')}}: <span style="font-weight:bold">{{getJoinRate}}</span></span>
+            <span class="info-item">{{$t('totalAnalysis.echarts_text15')}}: <span style="font-weight:bold">{{totalAverage}}</span></span>
+          </p>
+        </div>
+        <div class="data-select">
+          <div class="data-select-items" style="display:inline-block">
+            <span :class="activeBar == 0 ? 'data-select-active' : ''" @click="handleDataSelect(0)">{{$t('totalAnalysis.module1')}}</span>
+            <span :class="activeBar == 1 ? 'data-select-active' : ''" @click="handleDataSelect(1)">{{$t('totalAnalysis.module2')}}</span>
+            <!-- <span :class="activeBar == 2 ? 'data-select-active' : ''" @click="handleDataSelect(2)">{{$t('totalAnalysis.module3')}}</span>
+            <span :class="activeBar == 3 ? 'data-select-active' : ''" @click="handleDataSelect(3)">{{$t('totalAnalysis.module4')}}</span>
+            <span :class="activeBar == 4 ? 'data-select-active' : ''" @click="handleDataSelect(4)">{{$t('totalAnalysis.module5')}}</span> -->
+          </div>
+        </div>
+        <div class="class-tab" v-if="!(activeBar === 2 || isShowQuestionList)">
+          <span v-for="(item,index) in classList" :value="index" :key="item" @click="onClassChange(item,index)" :class="['class-tab-item',activeClassIndex == index ? 'data-select-active' : '']">{{ item }}</span>
+        </div>
+        <AchievementAnalysis v-if="activeBar == 0 && activeClassIndex == 0"></AchievementAnalysis>
+        <SingleClassView v-else-if="activeBar == 0 && activeClassIndex > 0"></SingleClassView>
+        <!-- <QuestionListView v-else-if="isShowQuestionList"></QuestionListView> -->
+        <ScatterAnalysis v-else-if="activeBar == 1"></ScatterAnalysis>
+        <!-- <TestAnalysis v-else-if="activeBar == 2"></TestAnalysis>
+        <KnowledgeAnalysis v-else-if="activeBar == 3"></KnowledgeAnalysis>
+        <LevelAnalysis v-else></LevelAnalysis> -->
+      </vuescroll>
+    </div>
+  </div>
+</template>
+
+<script>
+import AchievementAnalysis from '@/view/student-analysis/total-analysis/AchievementAnalysis/htAchievementAnalysis.vue'
+import ScatterAnalysis from '@/view/student-analysis/total-analysis/ScatterAnalysis/ScatterAnalysis.vue'
+import TestAnalysis from '@/view/student-analysis/total-analysis/TestAnalysis/TestAnalysis.vue'
+import KnowledgeAnalysis from '@/view/student-analysis/total-analysis/KnowledgeAnalysis/KnowledgeAnalysis.vue'
+import LevelAnalysis from '@/view/student-analysis/total-analysis/LevelAnalysis/LevelAnalysis.vue'
+import SingleClassView from '@/view/student-analysis/total-analysis/AchievementAnalysis/EarlyWarning.vue'
+import QuestionListView from '@/view/student-analysis/total-analysis/TestAnalysis/QuestionList.vue'
+export default {
+  props: {
+    evInfo: {
+      type: Object,
+      default: () => { }
+    }
+  },
+  components: {
+    AchievementAnalysis,
+    ScatterAnalysis,
+    TestAnalysis,
+    KnowledgeAnalysis,
+    LevelAnalysis,
+    SingleClassView,
+    QuestionListView
+  },
+  data() {
+    return {
+      isShowQuestionList: false,
+      activeClassIndex: 0,
+      isLoading: true,
+      isEmpty: true,
+      showAnalysis: false,
+      activeBar: 0,
+      totalAverage: 0,
+      currentClass: 0,
+      currentScatterClass: 0,
+      curWarningSubjectIndex: 0,
+      classList: [],
+      isJointExam: false, //是否為統測活動評量
+      jointVisiable: false //統測活動是否可見
+    }
+  },
+  created() {
+    this.initAnalysisJson(this.evInfo)
+  },
+  methods: {
+    onClassChange(className, index) {
+      // this.activeClassIndex = null
+      this.isShowQuestionList = false
+      this.$route.query.name = className
+      this.$nextTick(() => {
+        this.activeClassIndex = index
+        this.$store.commit('updateClassIndex', index)
+        this.$EventBus.$emit('changeClassName', className)
+      })
+    },
+    initAnalysisJson(evInfo) {
+        //console.error('evInfo', evInfo)
+      // 如果评测活动还没有结束 或者 cloudas为false 则不显示诊断分析数据
+      if (evInfo.progress !== 'finish' || !evInfo.cloudas) {
+        this.showAnalysis = true
+        this.isEmpty = true
+        return
+      } else {
+        this.showAnalysis = false
+        this.isEmpty = false
+      }
+      if (evInfo.jointExamId && typeof evInfo.jointExamId === "string" && evInfo.jointExamId.length > 0) {
+        this.isJointExam = true
+      } else {
+        this.isJointExam = false
+      }
+      if (evInfo.jointVisiable) {
+        this.jointVisiable = true
+      } else {
+        this.jointVisiable = false
+      }
+      // if(!this.currentExamItem){
+      //   this.showAnalysis = true
+
+      // }
+      let item = evInfo
+      console.log('切换评测', item, item.name)
+      this.currentExamItem = item      
+      let evInfostr = JSON.stringify(evInfo).replace(/<img[^>]*\/?>/g, '');
+      //localStorage.setItem('curExam', JSON.stringify(evInfo)) //評量檔案大,不放入localStorage
+      localStorage.setItem('curExam', evInfostr) //評量檔案大,不放入localStorage
+      this.$store.commit('clearAnalysisJoint', null)
+      //debugger
+      this.$store.dispatch('getAnalysisJsonJoint', {
+        //code: item.code.replace('Exam-', ''),
+        jointExamId: item.id
+        //jointExamId: "eb1c5ee3-23db-804f-d2be-1deddb174969"
+        
+        //  code: "1595321354",
+        // id: "888bcf9d-2428-442e-9630-cbe09e19d84d"
+      }).then(res => {
+        console.log(res)
+        this.showAnalysis = true
+        this.getTotalAverage()
+
+      }).catch(err => {
+        this.showAnalysis = true
+console.log(err)
+      })
+    },
+    scrollToTop() {
+      this.$nextTick(() => {
+        this.$refs['vs'].scrollTo({
+          y: 0
+        },
+          500
+        )
+      })
+    },
+    handleDataSelect(val) {
+      this.activeBar = val
+      this.isShowQuestionList = false
+      this.scrollToTop()
+    },
+    /* 获取当前评测的平均分 */
+    getTotalAverage(data) {
+      let analysisJson = this.$store.state.totalAnalysis.analysisJsonJoint
+      this.totalAverage = analysisJson.all.average
+
+      this.classList = [this.$t('totalAnalysis.allClasses')].concat([...new Set(analysisJson.classes.map(item => item.className))])
+    },
+    /**获取mode对应的label */
+    getModeLabel(code, qamode) {
+      if (qamode == 1) {
+        return this.$t('learnActivity.mgtScEv.paperExam')
+      }
+      for (let item of this.$GLOBAL.EV_MODE()) {
+        if (item.value == code) {
+          return item.label
+        }
+      }
+    },
+  },
+  computed: {
+    // 获取最新成绩分析模块数据
+    getAnalysisJson() {
+      //debugger
+      return this.$store.state.totalAnalysis.analysisJsonJoint
+    },
+    getSubjectList() {
+      this.subjectSelectVal = this.$store.state.totalAnalysis.currentSubjectJoint
+      /* 如果是成绩分析则要加入全科统计栏目 */
+      if (this.dataSelectIndex === '0') {
+        return [this.$t('totalAnalysis.allSubjects'), ...this.$store.state.totalAnalysis.subjectListJoint]
+      } else {
+        return this.$store.state.totalAnalysis.subjectListJoint
+      }
+    },
+    getJoinRate() {
+      return this.currentExamItem.stuCount ? (((this.currentExamItem.stuCount - this.currentExamItem.lostStu.length) / this.currentExamItem.stuCount) * 100).toFixed(2) + '%' : 0
+    }
+  },
+  mounted() {    
+    this.$EventBus.$off('onEvaChange')
+    this.$EventBus.$on('onEvaChange', evInfo => {
+      this.showAnalysis = false
+      this.initAnalysisJson(evInfo)
+    })
+    this.$EventBus.$off('cloudas-question-click')
+    this.$EventBus.$on('cloudas-question-click', index => {
+      this.$route.query.QIndex = index
+      this.isShowQuestionList = true
+    })
+    this.$EventBus.$off('cloudas-close-question')
+    this.$EventBus.$on('cloudas-close-question', () => {
+      this.isShowQuestionList = false
+      this.scrollToTop()
+    })
+
+
+  },
+  beforeUpdate(){
+    console.log("beforeUpdate");
+
+  },
+  updated(){
+console.log("updated");
+  },
+ 
+}
+</script>
+
+<style lang="less">
+.cloudas-container {
+  max-height: 90%;
+  // overflow: auto;
+  overflow-x: hidden;
+  .cloudas-content {
+    overflow: hidden;
+    height: 90vh;
+  }
+  .basic-info {
+    margin: 30px 0 0 55px;
+    font-size: 14px;
+    .info-type {
+      padding: 1px 5px;
+      background-color: #fb4903;
+      border-radius: 5px;
+      border: 1px solid #fb4903;
+      color: #fff;
+      font-size: 12px;
+      margin-right: 10px;
+      vertical-align: super;
+    }
+    .info-subject {
+      padding: 2px 6px;
+      font-size: 12px;
+      color: #eee;
+      border-radius: 3px;
+      vertical-align: super;
+      margin-left: 10px;
+      background: #419901;
+    }
+    .info-grade {
+      padding: 2px 6px;
+      color: #eee;
+      border-radius: 3px;
+      font-size: 12px;
+      vertical-align: super;
+      margin-left: 10px;
+      background: #018b99;
+    }
+    .info-name {
+      color: var(--primary-text-color);
+      font-size: 24px;
+      display: inline-block;
+      margin-bottom: 4px;
+      cursor: pointer;
+    }
+    .info-date-person {
+      margin-top: 10px;
+      display: flex;
+      color: var(--second-text-color);
+      .info-item {
+        margin-left: 20px;
+
+        & > span {
+          color: var(--primary-text-color);
+        }
+        &:first-child {
+          margin-left: 0;
+        }
+      }
+    }
+    .info-date {
+      margin-left: 50px;
+    }
+  }
+
+  .class-tab {
+    margin: 20px 90px 0 55px;
+    padding: 20px 10px;
+    color: var(--second-text-color);
+    border-top: 1px solid rgb(230, 230, 230);
+    font-size: 14px;
+    .class-tab-item {
+      //padding: 35px 0 10px 0px;
+      color: var(--second-text-color);
+      font-size: 14px;
+      margin: 0 40px 20px 0;
+      cursor: pointer;
+    }
+    .data-select-active {
+      color: var(--primary-text-color);
+      font-weight: bold;
+      border-bottom: 3px solid var(--tabs-bottom-color);
+    }
+  }
+
+  .data-select {
+    padding: 25px 0 0 0px;
+    color: var(--second-text-color);
+    font-size: 14px;
+    margin: 0 100px 0 55px;
+
+    .data-select-active {
+      color: var(--primary-text-color);
+      font-weight: bold;
+      border-bottom: 3px solid var(--tabs-bottom-color);
+    }
+  }
+  .data-select-items {
+    span {
+      padding: 8px;
+      margin-right: 50px;
+      cursor: pointer;
+    }
+  }
+}
+</style>

+ 286 - 0
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/AchievementAnalysis/htAchievementAnalysis.vue

@@ -0,0 +1,286 @@
+<template>
+  <div class="achievement-container">
+    <!-- 及格率统计 -->
+    <Row class-name="base-table-row">
+      <span class="component-title" style="width: 100%">{{$t('totalAnalysis.ach_title1')}}</span>
+    </Row>
+    <Row class-name="component-percents">
+      <div v-for="(item,index) in passRate" :key="index">
+        <div class="percent-item" v-if="isAllSubject || (!isAllSubject && curSubjectIndex === (index - 1))">
+          <div class="fl-col-center">
+            <span class="percent-name">{{item.name}}</span>
+            <span class="percent-value"><span class="percent-line" :style="{background:colorList[index]}"></span>{{item.sRate}} <span style="font-size: 22px;">%</span></span>
+            <div style="margin-left: 30px;">
+              <p v-if="item.average">{{ $t('totalAnalysis.echarts_text15') }}:{{item.average.toFixed(1)}} </p>
+              <p v-if="item.standard">{{ $t('totalAnalysis.ach_table_text4') }}:{{item.standard.toFixed(1)}}</p>
+            </div>
+          </div>
+        </div>
+      </div>
+
+    </Row>
+
+    <Row class-name="base-table-row">
+      <Col span="12">
+      <span class="component-title" style="margin-right: 55px">{{$t('totalAnalysis.ach_title2')}}</span>
+      <BaseBar :echartsData="baseBarData" :subjectIndex="curSubjectIndex" ref="baseBar"></BaseBar>
+      </Col>
+      <Col span="12">
+      <div v-if="isAllSubject">
+        <span class="component-title">{{ isCloudas ? $t('totalAnalysis.scoreRate') : $t('totalAnalysis.ach_title3')}}</span>
+        <BaseEntryBar v-if="!isCloudas" :echartData="classDatas" echartsId="entryNumberBar"></BaseEntryBar>
+        <BaseScoreRateBar v-if="isCloudas" echartsId="sRateBar11" :echartsData="getAnalysisJsonJoint.students"></BaseScoreRateBar>
+      </div>
+      <div v-else>
+        <span class="component-title">{{$t('totalAnalysis.scoreRate')}}</span>
+        <BaseScoreRateBar echartsId="sRateBar1" :subjectIndex="curSubjectIndex" :echartsData="getAnalysisJsonJoint.students"></BaseScoreRateBar>
+      </div>
+      </Col>
+    </Row>
+
+    <Row></Row>
+    <Row v-if="isAllSubject && !isCloudas" class-name="base-table-row">
+      <Col span="12">
+      <span class="component-title">{{$t('totalAnalysis.scoreRate')}}</span>
+      <BaseScoreRateBar echartsId="sRateBar11" :echartsData="getAnalysisJsonJoint.students"></BaseScoreRateBar>
+      </Col>
+      <Col span="12">
+
+      </Col>
+    </Row>
+    <!-- 进线情况统计 -->
+    <EntryTables ref="entryTable" :subjectIndex="curSubjectIndex"></EntryTables>
+  </div>
+</template>
+
+<script>
+import BaseBar from '@/components/student-analysis/total/BaseBar'
+import BaseEntryBar from '@/components/student-analysis/total/BaseEntryBar.vue'
+import BaseTable from '@/components/student-analysis/total/BaseMyTable.vue'
+import EntryTables from '@/view/student-analysis/total-analysis/AchievementAnalysis/EntryTables.vue'
+import BaseScoreRateBar from '@/components/student-analysis/total/BaseScoreRateBar.vue'
+export default {
+  components: {
+    BaseBar, BaseEntryBar, BaseTable, EntryTables, BaseScoreRateBar
+  },
+  data() {
+    return {
+      passRate: [],
+      subjectList: [],
+      colorList: ['#ff6b68', '#ffa1f4', '#41b0c7', '#1cd1a1', '#83dc07'],
+      earlyWarningData: [],
+      baseBarData: null,
+      classDatas: [],
+      isAllSubject: true,
+      curSubjectIndex: NaN,
+      tableColumns: [
+        {
+          title: this.$t('totalAnalysis.base_class'),
+          key: 'classId',
+          renderType: 'renderClassName',
+        },
+        {
+          title: this.$t('totalAnalysis.ach_text7'),
+          sortable: 'custom',
+          key: 'totalNum'
+        },
+        {
+          title: this.$t('totalAnalysis.ach_table_text3'),
+          key: 'average',
+          renderType: function (h, params) {
+            return h('span', (Number(params.row.average)).toFixed(2))
+          },
+          sortable: 'custom'
+        },
+        {
+          title: this.$t('totalAnalysis.ach_table_text4'),
+          key: 'standardDeviation',
+          sortable: 'custom'
+        },
+        // {
+        //     title: this.$t('totalAnalysis.ach_table_text5'),
+        //     key: 'overAverageRate',
+        //     sortable: 'custom'
+        // },
+        {
+          title: this.$t('totalAnalysis.ach_table_text1'),
+          key: 'gradeRank',
+          sortable: 'custom',
+          renderType: function (h, params) {
+            const row = params.row
+            return h('span', {
+              domProps: {
+                className: 'badge-wrap'
+              }
+            }, [
+              h('span', {
+                domProps: {
+                  className: 'table-rank-badge'
+                },
+                style: {
+                  color: row.changesStatus === 1 ? '#0ccb0c' : '#ff5d5d'
+                }
+              }, (row.changesStatus === 1 && row.changesVal !== 0) ? '+' + row.changesVal : (row.changesStatus === -1 && row.changesVal !== 0) ? '-' + row.changesVal : ''),
+              h('span', {
+                domProps: {
+                  className: 'table-rank-value'
+                }
+              }, row.gradeRank)
+
+            ]
+            )
+          }
+        }
+      ]
+    }
+  },
+
+  created() {
+    this.$parent.$parent.$parent.isShowQuestions = false
+  },
+  methods: {
+    /* 获取成绩分析模块 -- 及格率统计数据 */
+    getPassRate(analysisJson) {
+      console.log(analysisJson)
+      let isClouDAS = this.$route.name === 'privExam'
+      let result = isClouDAS ? [] : [{
+        name: this.$t('totalAnalysis.allSubjects'),
+        average: analysisJson.all.average,
+        sRate: analysisJson.all.sRate,
+        standard: analysisJson.all.standard
+      }]
+      analysisJson.subjects.forEach((item, index) => {
+        let o = {}
+        let name = item.name === undefined ? item.value :item.name
+        o.name = name
+        let classAverage = 0
+        analysisJson.classes.forEach((classItem, classIndex) => {     
+          if(classItem.subjects[index] && classItem.subjects[index].passPercent){
+            classAverage += classItem.subjects[index].passPercent 
+          }else{
+            classAverage += 0
+          }     
+          
+        })
+        result.push({
+          name: name,
+          average: item.average,
+          sRate: item.sRate,
+          standard: item.standard
+        })
+      })
+      return result
+    },
+
+    /* 获取成绩分西-预警统计表格数据 */
+    getEarlyWarningData(analysisJson) {
+      let result = []
+      analysisJson.classes.forEach((item, index) => {
+        result.push({
+          classId: item.className,
+          totalNum: item.stuCount,
+          average: item.totalAverage,
+          standardDeviation: item.standardDeviation.toFixed(2),
+          overAverageRate: 0,
+          gradeRank: 0
+        })
+      })
+
+      let rateRanks = result.sort((a, b) => { return Number(a.average) - Number(b.average) })
+      result.forEach(item => {
+        item.gradeRank = rateRanks.map(i => i.classId).indexOf(item.classId) + 1
+      })
+      return result
+    },
+
+
+
+
+
+  },
+  mounted() {
+    this.$parent.dataSelectIndex = 0
+    // this.$refs.achievementTable.$el.childNodes[1].style.borderRight = '0'
+
+
+
+    // 如果有缓存的数据 则读取缓存数据
+    if (this.getAnalysisJsonJoint) {
+      this.earlyWarningData = this.getEarlyWarningData(this.getAnalysisJsonJoint)
+      this.subjectList = this.getAnalysisJsonJoint.classes.subjects
+      this.passRate = this.getPassRate(this.getAnalysisJsonJoint)
+      console.log('achievement页面接收的JSON', this.getAnalysisJsonJoint)
+    } else if (localStorage.getItem('curExam')) {
+      console.log('没有')
+      let item = JSON.parse(localStorage.getItem('curExam'))
+      this.$store.dispatch('getAnalysisJsonJoint', {
+        // code: item.code.replace('Exam-', ''),
+        // id: item.id
+        jointExamId: item.id
+      }).then(res => {
+        this.earlyWarningData = this.getEarlyWarningData(this.getAnalysisJsonJoint)
+        this.subjectList = this.getAnalysisJsonJoint.classes.subjects
+        this.passRate = this.getPassRate(this.getAnalysisJsonJoint)
+        console.log('achievement页面接收的JSON', this.getAnalysisJsonJoint)
+      })
+    }
+    this.$EventBus.$off('onExport')
+    this.$EventBus.$on("onExport", exportTables => {
+      console.log(exportTables)
+      if (exportTables.indexOf('achievementTable') > -1) {
+        this.$refs.achievementTable.exportData(3)
+      }
+    })
+
+
+    this.$EventBus.$off('onEarlySubjectChange')
+    this.$EventBus.$on('onEarlySubjectChange', index => {
+      if (index === 0) {
+        this.isAllSubject = true
+        this.curSubjectIndex = NaN
+      } else {
+        this.isAllSubject = false
+        this.curSubjectIndex = index - 1
+      }
+    })
+  },
+  computed: {
+
+    // 获取最新成绩分析模块数据
+    getAnalysisJsonJoint() {
+      return this.$store.state.totalAnalysis.analysisJsonJoint
+    },
+
+    // 获取最新成绩分析模块数据
+    getAchievementData() {
+      return this.$store.state.totalAnalysis.achievementData
+    },
+    // 获取最新成绩分析模块数据
+    getSubjectList() {
+      return this.$store.state.totalAnalysis.subjectList
+    },
+    isCloudas() {
+      return this.$route.name === 'privExam'
+    }
+  },
+  watch: {
+    // 渲染最新预警统计数据
+    getAnalysisJsonJoint(val) {
+      if (!val) return
+      // this.earlyWarningData = this.$tools.jsonTransform(val.earlyWarning) // 预警统计表格
+      // this.passRate = val.passRate // 及格率统计
+      // this.subjectList = val.average.datas.map(item => item.name) // 科目列表
+      this.subjectList = val.subjects
+      this.earlyWarningData = this.getEarlyWarningData(this.getAnalysisJsonJoint)
+      // this.passRate = this.getPassRate(val)
+    },
+    // 渲染最新预警统计数据
+    getSubjectList(val) {
+      if (!val) return
+      this.subjectList = val
+    }
+  }
+}
+</script>
+
+<style src="./AchievementAnalysis.less" lang="less" scoped></style>

ファイルの差分が大きいため隠しています
+ 1267 - 212
TEAMModelOS/Controllers/Analysis/AnalysisController.cs


+ 1 - 1
TEAMModelOS/Controllers/Analysis/ClassAys.cs

@@ -10,7 +10,6 @@ namespace TEAMModelOS.Controllers.Analysis
         public ClassAys()
         {
             subjects = new List<AysSubject>();
-           
         }
         public List<string> studentIds { get; set; } = new List<string>();
         public int stuCount { get; set; }
@@ -18,6 +17,7 @@ namespace TEAMModelOS.Controllers.Analysis
         public List<AysSubject> subjects { get; set; }
         public string classId { get; set; }
         public string className { get; set; }
+        public string creatorId { get; set; }
         public double totalAverage { get; set; }
         public double standardDeviation { get; set; }
         public string gradeId { get; set; }

+ 1 - 0
TEAMModelOS/Controllers/Analysis/StudentAys.cs

@@ -17,6 +17,7 @@ namespace TEAMModelOS.Controllers.Analysis
         public int csort {get;set;}
         public int  gsort{get;set;}
         public List<StudentSubject> subjects { get; set; } = new List<StudentSubject>();
+        public string examId { get; set; }  //理論上每個學生每個統測評量只參加一次
         public string classId { get; set; }
         public string className { get; set; }
         public string gradeId { get; set; }

+ 2 - 2
TEAMModelOS/Controllers/XTest/BusinessController.cs

@@ -236,7 +236,7 @@ namespace TEAMModelOS.Controllers.XTest
             }
 
             //文件夹(关联id)导向的文件夹 exam  homework art records  survey  vote  item
-            string[] prefixDirId = new string[] { "exam", "homework", "art", "records", "survey", "vote", "item" };
+            string[] prefixDirId = new string[] { "exam", "homework", "art", "records", "survey", "vote", "item", "local", "temp" };
             HashSet<string> ids = new HashSet<string>();
             Dictionary<string, List<KeyValuePair<string, long?>>> recordUrls = new Dictionary<string, List<KeyValuePair<string, long?>>>(); //單位:bytes
             foreach (string prefix in prefixDirId)
@@ -260,7 +260,7 @@ namespace TEAMModelOS.Controllers.XTest
 
                 }
             }
-            //資料整理
+            //資料整理 單位:Byte
             Dictionary<string, long> result = new Dictionary<string, long>();
             foreach (KeyValuePair<string, List<KeyValuePair<string, long?>>> rec in recordUrls)
             {