Browse Source

Merge branch 'develop3.0-tmd' of http://106.12.23.251:10080/TEAMMODEL/TEAMModelOS into develop3.0-tmd

JAELYS 4 năm trước cách đây
mục cha
commit
3a911acfb0

+ 32 - 0
TEAMModelOS.SDK/Models/Cosmos/School/ClassAnalysis.cs

@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Models.Cosmos.School
+{
+    public class ClassAnalysis
+    {
+        public ClassAnalysis()
+        {
+            subjects = new List<SubjectScore>();
+            total = new List<double>();
+        }
+        public List<string> studentIds { get; set; } = new List<string>();
+        public int stuCount { get; set; }
+        public int lineCount { get; set; }
+        public List<SubjectScore> subjects { get; set; }
+        public string classId { get; set; }
+        public List<double> total { get;set;}
+        public double  totalAverage { get; set; }
+        public double standardDeviation { get; set; }
+        public class SubjectScore {
+            public SubjectScore() {
+                
+            }
+            public string id { get; set; }
+            public  double  passPercent { get; set; }
+            public double  averageScore { get; set; }
+
+        }
+    }
+}

+ 1 - 0
TEAMModelOS.SDK/Models/Cosmos/School/ExamResult.cs

@@ -19,6 +19,7 @@ namespace TEAMModelOS.SDK.Models
             studentScores = new List<List<double>>();
             studentIds = new List<string>();
             point = new List<double>();
+            paper = new PaperSimple();
         }
         
         public string name { get; set; }

+ 34 - 0
TEAMModelOS.SDK/Models/Cosmos/School/GradeAnalysis.cs

@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Models.Cosmos.School
+{
+    public class GradeAnalysis
+    {
+        public GradeAnalysis()
+        {
+            subjects = new List<GradeSubjectScore>();
+            //passPersent = new List<double>();
+        }
+        //public List<string> subjectId { get; set; }
+        public string gradeId { get; set; }
+     
+        //public List<double> passPersent { get; set; }
+
+        //public List<double> averageScore { get; set; }
+
+        public double GradeaverageScore { get; set; }
+        public List<GradeSubjectScore> subjects { get; set; }
+        public class GradeSubjectScore
+        {
+            public GradeSubjectScore() {
+            }
+          
+            public string id { get; set; }
+            public  double  passPercent { get; set; }
+            public double  averageScore { get; set; }
+
+        }
+    }
+}

+ 38 - 0
TEAMModelOS.SDK/Models/Cosmos/Student/StudentAnalysis.cs

@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Models.Cosmos.Student
+{
+   public  class StudentAnalysis
+    {
+        public StudentAnalysis() {
+            ids = new List<string>();
+            scores = new List<Score>();
+            gpr = new List<double>();
+            cpr = new List<double>();
+            total = new List<double>();
+            names = new List<string>();
+        }
+        public List<string> ids { get; set; }
+        public List<string> names { get; set; }
+        public List<double> gpr { get; set; }
+        public List<double> cpr { get; set; }
+        public List<Score> scores { get; set; }
+        public List<double> total { get; set; }
+        public class Score {
+            public Score() {
+                csc = new List<double>();
+                cdpr = new List<double>();
+                gdpr = new List<double>();
+                gsc = new List<double>();
+            }
+            public string subjectId { get; set; }
+            public List<double> csc { get; set; }
+            public List<double> gsc { get; set; }
+            public List<double> cdpr { get; set; }
+            public List<double> gdpr { get; set; }
+        }
+       
+    }
+}

+ 1 - 1
TEAMModelOS/ClientApp/src/assets/student-web/component_styles/paper-view.css

@@ -92,7 +92,7 @@
     width: 100%;
 }
 
-.paper-item {
+.lesson-test .paper-item {
     border: 1px solid #919191;
     font-weight: bolder;
     height: 40px;

+ 16 - 4
TEAMModelOS/ClientApp/src/utils/evTools.js

@@ -147,7 +147,8 @@ export default {
 	},
 	
 	/* 给富文本添加 */
-	doAddHost(exerciseItem,paperName){
+	doAddHost(exerciseItem,paperName,examItem){
+		console.log('addHost接收的examItem',examItem)
 		let container = exerciseItem.scope === 'school' ? store.state.userInfo.schoolCode : store.state.userInfo.TEAMModelId
 		let isSubjective = exerciseItem.type === 'complete' || exerciseItem.type === 'subjective' || exerciseItem.type === 'compose'
 		let richTextObj = {
@@ -168,7 +169,12 @@ export default {
 						for(let i = 0;i<srcList.length;i++){
 							let src = srcList[i]
 							/* 如果是来自试卷的题目 则需要匹配试卷HOST */
-							if(paperName){
+							if(examItem){
+								let examContainer = examItem.examId ?  (examItem.scope === 'school' ? store.state.userInfo.schoolCode : store.state.userInfo.TEAMModelId) : examItem.code
+								let blobUrl = 'https://teammodelstorage.blob.core.chinacloudapi.cn/' + examContainer  + examItem.blob +  '/' + src.split('?')[0]
+								let addSasUrl = await $tools.getFileSas(blobUrl)
+								richTextObj[key] = richTextObj[key].replace(src, addSasUrl.url);
+							}else if(paperName){
 								let blobUrl = 'https://teammodelstorage.blob.core.chinacloudapi.cn/' + container + '/paper/' + paperName + '/' + src.split('?')[0]
 								let addSasUrl = await $tools.getFileSas(blobUrl)
 								richTextObj[key] = richTextObj[key].replace(src, addSasUrl.url);
@@ -274,6 +280,7 @@ export default {
 			let blobHost = paper.scope === 'school' ?  JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri :  JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8")).blob_uri
 			// 根据试卷的Blob地址 去读取JSON文件
 			let sasString = paper.scope === 'school' ?  await $tools.getSchoolSas() : await $tools.getPrivateSas()
+			
 			try{
 				let jsonInfo = await $tools.getFile(blobHost + paper.blob + '/index.json' + sasString.sas)
 				let jsonData = JSON.parse(jsonInfo)
@@ -293,8 +300,12 @@ export default {
 								itemJson.exercise.id = itemJson.id 
 								itemJson.exercise.pid = itemJson.pid 
 								itemJson.exercise.blob = path + '/' + item.url // 添加blob是方便在保存试卷是 refresh 与导入的试题区分开
-								itemJson.exercise.score = item.scoring ? item.scoring.score : 0,
-								itemJson.exercise = await this.doAddHost(itemJson.exercise,paper.name) // 检测试题中的富文本 为有src为相对路径的音视频文件添加blob的HOST地址
+								itemJson.exercise.score = item.scoring ? item.scoring.score : 0
+								if(paper.examId){
+									itemJson.exercise = await this.doAddHost(itemJson.exercise,paper.name,paper) // 检测试题中的富文本 为有src为相对路径的音视频文件添加blob的HOST地址
+								}else{
+									itemJson.exercise = await this.doAddHost(itemJson.exercise,paper.name) // 检测试题中的富文本 为有src为相对路径的音视频文件添加blob的HOST地址
+								}
 								resolve(itemJson.exercise)
 							}catch(e){
 								reject(e)
@@ -350,6 +361,7 @@ export default {
 							itemJson.exercise.pid = itemJson.pid 
 							itemJson.exercise.score = item.scoring ? item.scoring.score : 0,
 							// jsonData.item.push(itemJson.exercise)
+							itemJson.exercise = await this.doAddHost(itemJson.exercise,paper.name,paper)
 							resolve(itemJson.exercise)
 						}))
 					})

+ 929 - 0
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseEditImport.vue

@@ -0,0 +1,929 @@
+<template>
+	<div class="ev-container component-ev-container" :id="refId" ref="editContainer">
+		<div class="display-flex" v-if="isSchool">
+			<div class="exersices-attr my-radio-style">
+				<IconText :text="'选择学段'" :color="'#00b8ff'" :icon="'md-school'"></IconText>
+				<Select v-model="exercisePeriod" @on-change="onPeriodChange">
+					<Option v-for="(period, index) in schoolInfo.period" :value="index" :key="index">{{ period.name }}</Option>
+				</Select>
+			</div>
+			<div class="my-radio-style exersices-attr">
+				<IconText :text="'选择年级'" :color="'#00b8ff'" :icon="'logo-buffer'"></IconText>
+				<Select v-model="exerciseGrade" multiple>
+					<Option v-for="(grade, index) in gradeList" :value="grade.id" :key="index">{{ grade.name }}</Option>
+				</Select>
+			</div>
+			<div class="exersices-attr my-radio-style">
+				<IconText :text="'选择科目'" :color="'#00b8ff'" :icon="'md-bookmarks'"></IconText>
+				<Select v-model="exerciseSubject" @on-change="onSubjectChange">
+					<Option v-for="(subject, index) in subjectList" :value="index" :key="index">{{ subject.name }}</Option>
+				</Select>
+			</div>
+		</div>
+		<div class="display-flex">
+			<!-- <div class="exersices-attr my-radio-style">
+				<IconText :text="'题目归属'" :color="'#00b8ff'" :icon="'md-cube'"></IconText>
+				<Select v-model="exerciseScope" disabled>
+					<Option v-for="(item, index) in scopeList" :value="index" :key="index">{{ item }}</Option>
+				</Select>
+			</div> -->
+			<div class="exersices-attr edit-exersices-attr-diff my-radio-style">
+				<IconText :text="'题目难度'" :color="'#00b8ff'" :icon="'md-pulse'"></IconText>
+				<RadioGroup v-model="exersicesDiff" type="button" ref="diffRef">
+					<Radio label="1" @click.native="diffChange($event, '1')">容易</Radio>
+					<Radio label="2" @click.native="diffChange($event, '2')">较易</Radio>
+					<Radio label="3" @click.native="diffChange($event, '3')">一般</Radio>
+					<Radio label="4" @click.native="diffChange($event, '4')">较难</Radio>
+					<Radio label="5" @click.native="diffChange($event, '5')">困难</Radio>
+				</RadioGroup>
+			</div>
+			<div class="exersices-attr my-radio-style">
+				<IconText :text="'关联认知层次'" :color="'#00b8ff'" :icon="'md-planet'"></IconText>
+				<Select v-model="exerciseField">
+					<Option v-for="(item, index) in fieldList" :value="index" :key="index">{{ item }}</Option>
+				</Select>
+			</div>
+			<div class="exersices-attr my-radio-style">
+				<IconText :text="'关联知识点'" :color="'#00b8ff'" :icon="'md-infinite'"></IconText>
+				<Button type="info" style="margin-top: 20px" @click="onSelectPoints" v-if="exercisePoints.length === 0">选择知识点</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>
+					<span class="exercise-item-point-modify" @click="selectPointsModal = true">修改</span>
+				</div>
+			</div>
+		</div>
+		<div class="exersices-attr display-flex">
+			<div class="exersices-attr-diff my-radio-style">
+				<IconText :text="'选择题型'" :color="'#00b8ff'" :icon="'md-pricetags'"></IconText>
+				<RadioGroup v-model="exersicesType" type="button" @on-change="typeChange">
+					<Radio label="single" :disabled="isEdit">单选题</Radio>
+					<Radio label="multiple" :disabled="isEdit">多选题</Radio>
+					<Radio label="judge" :disabled="isEdit">判断题</Radio>
+					<Radio label="complete" :disabled="isEdit">填空题</Radio>
+					<Radio label="subjective" :disabled="isEdit">问答题</Radio>
+					<Radio label="connector" :disabled="isEdit">连线题</Radio>
+					<Radio label="correct" :disabled="isEdit">改错题</Radio>
+					<Radio label="compose" :disabled="isEdit">综合题</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>
+		<BaseSubjective v-else-if="exersicesType === 'subjective'" ref="subjective" :editInfo="editInfo"></BaseSubjective>
+		<BaseConnector v-else-if="exersicesType === 'connector'" ref="connector" :editInfo="editInfo"></BaseConnector>
+		<BaseCorrect v-else-if="exersicesType === 'correct'" ref="correct" :editInfo="editInfo"></BaseCorrect>
+		<BaseCompose v-else-if="exersicesType === 'compose'" ref="compose" :editInfo="editInfo"></BaseCompose>
+
+		<!-- 解析的富文本部分 -->
+		<div class="exersices-analysis" v-show="exersicesType !== 'compose'">
+			<IconText :text="'解析'" :color="'#2892DD'" :icon="'md-list'" style="margin-bottom: 10px"></IconText>
+			<div>
+				<div ref="analysisEditor" style="text-align: left"></div>
+			</div>
+		</div>
+
+		<!-- 补救的富文本部分 -->
+		<div class="exersices-analysis" v-show="exersicesType !== 'compose'">
+			<IconText :text="'补救资源'" :color="'#2892DD'" :icon="'md-link'" style="margin-bottom: 10px"></IconText>
+			<BaseRepair ref="repairRef" :datas="editInfo ? editInfo.repairResource : []"></BaseRepair>
+		</div>
+
+		<!-- 小题展示区域 -->
+		<div class="child-list-wrap" v-show="exersicesType === 'compose' && childList.length">
+			<IconText :text="'小题列表'" :color="'#00b8ff'" :icon="'md-list'"></IconText>
+			<BaseChildList :childList="childList" @onEditChildFinish="onEditChildFinish" @onDeleteChild="onDeleteChild"></BaseChildList>
+		</div>
+
+		<div class="save-wrap display-flex">
+			<Button type="info" @click="addChildModal = true" style="margin-right: 10px" v-show="exersicesType === 'compose'">添加小题</Button>
+			<Button type="success" @click="getContent(exersicesType)" :loading="isLoading">保存</Button>
+		</div>
+
+		<Modal v-model="selectPointsModal" title="选择知识点" ref="editPointRef" width="600px" style="z-index: 99999">
+			<BasePoints v-if="selectPointsModal" :period="schoolInfo.period[exercisePeriod].id" :subject="subjectList[exerciseSubject].id"
+			 @onCheckChange="onCheckChange" :points="exercisePoints" ref="pointRef" :scope="curScope"></BasePoints>
+			<!--<CreateNewChild v-if="isLoadModal" ref="newChild" :isChildEdit="isChildEdit" :editItem="editChild"></CreateNewChild>-->
+			<div slot="footer">
+				<Button type="text" @click="onConfirm">取消</Button>
+				<Button type="primary" @click="onConfirm">确定</Button>
+			</div>
+		</Modal>
+
+		<!-- 关联内容弹窗 -->
+		<Modal v-model="isRelatedContent" width="880" footer-hide class="relate-modal related-modal">
+			<div class="modal-header" slot="header">内容关联</div>
+			<!-- <ChooseContent :showSyllabus="isFalse" :showOther="isFalse" :showQuestion="isFalse" @on-select-file="onSelectFile"
+			 @on-cancel-file="onSelectFile"></ChooseContent> -->
+
+			<Button class="modal-btn" @click="onConfirmRelate">确认</Button>
+		</Modal>
+
+		<!-- 添加小题弹窗 -->
+		<Modal v-model="addChildModal" width="1080" footer-hide class="">
+			<div class="modal-header" slot="header">添加小题</div>
+			<BaseCreateChild @addFinish="onAddChildFinish" v-if="addChildModal" :curPeriodIndex="exercisePeriod"
+			 :curSubjectIndex="exerciseSubject"></BaseCreateChild>
+		</Modal>
+	</div>
+</template>
+<script>
+	import blobTool from "@/utils/blobTool.js";
+	import "videojs-contrib-hls.js/src/videojs.hlsjs";
+	import IconText from "@/components/evaluation/IconText.vue";
+	import BaseSingle from "@/view/evaluation/types/BaseSingle.vue";
+	import BaseMultiple from "@/view/evaluation/types/BaseMultiple.vue";
+	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 BaseCompose from "@/view/evaluation/types/BaseCompose.vue";
+	import E from "wangeditor";
+
+	export default {
+		name:'BaseEditExercise',
+		props: ["exerciseItem", "refId"],
+		components: {
+			IconText,
+			BaseSingle,
+			BaseJudge,
+			BaseMultiple,
+			BaseCompletion,
+			BaseSubjective,
+			BaseCorrect,
+			BaseConnector,
+			BaseCompose,
+		},
+		data() {
+			return {
+				isFalse: false,
+				isLoading: false,
+				addChildModal: false,
+				isRelatedContent: false,
+				selectPointsModal: false,
+				isEdit: false,
+				editInfo: null,
+				schoolInfo: {
+					period: [],
+				},
+				exersicesType: null,
+				exerciseField: 0,
+				exercisePeriod: 0,
+				exerciseGrade: [],
+				exerciseSubject: 0,
+				exerciseScope: 0,
+				exercisePoints: [],
+				childList: [],
+				oldChildList:[],
+				scopeList: ["个人题库", "校本题库"],
+				fieldList: ["知识", "理解", "应用", "分析", "综合", "评鉴"],
+				periodList: [],
+				gradeList: [],
+				subjectList: [],
+				relateFileList: [],
+				exersicesDiff: 0,
+				analysisContent: "",
+				repairContent: "",
+				stemContent: "",
+				analysisEditor: null,
+				repairEditor: null,
+				videoHtml: "",
+				deleteChildrens:[],
+				curId:null
+			};
+		},
+		created() {
+			// 初始化区班校信息
+			this.getSchoolInfo();
+		},
+		methods: {
+			getSchoolInfo() {
+				this.$store.dispatch("user/getSchoolProfile").then((res) => {
+					let schoolBaseInfo = res.school_base;
+					if (schoolBaseInfo) {
+						this.schoolInfo = schoolBaseInfo;
+						if (schoolBaseInfo.period.length) {
+							this.gradeList = schoolBaseInfo.period[0].grades;
+							this.subjectList = schoolBaseInfo.period[0].subjects;
+						}
+					}
+				});
+			},
+			
+			onSelectPoints(){
+				if(this.hasSchool){
+					this.selectPointsModal = true
+				}else{
+					this.$Message.warning('您当前未加入学校,无法选择知识点!')
+				}
+			},
+
+			/* 滚回顶部 */
+			backToTop() {
+				this.$refs.editContainer.scrollIntoView();
+				this.exersicesType = ''
+			},
+
+			onSelectFile(val) {
+				this.relateFileList = val.files;
+			},
+
+			onConfirmRelate() {
+				this.isRelatedContent = false;
+			},
+
+			async getContent(type) {
+				let exerciseItem = JSON.parse(JSON.stringify(this.editInfo))
+				this.isLoading = true;
+				switch (type) {
+					case "single":
+						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)),
+							];
+
+						break;
+					case "multiple":
+						this.$refs.multiple.doSave()
+							exerciseItem.question = this.$refs.multiple.stemContent;
+							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;
+							exerciseItem.answer = this.$refs.multiple.multipleAnswers;
+							break;
+					case "judge":
+						exerciseItem.question = this.$refs.judge._data.stemContent;
+						exerciseItem.option = [];
+						exerciseItem.type = this.exersicesType;
+						exerciseItem.level = +this.exersicesDiff;
+						exerciseItem.answer = [this.$refs.judge._data.trueAnswer];
+						break;
+					case "complete":
+						exerciseItem.question = this.$refs.complete.stemContent;
+						exerciseItem.option = [];
+						exerciseItem.type = this.exersicesType;
+						exerciseItem.level = +this.exersicesDiff;
+						exerciseItem.explain = this.analysisContent;
+						exerciseItem.answer = [this.$refs.complete.answerContent];
+						break;
+					case "connector":
+						exerciseItem.question = this.$refs.connector.stemContent;
+						exerciseItem.option = [];
+						exerciseItem.type = this.exersicesType;
+						exerciseItem.level = +this.exersicesDiff;
+						exerciseItem.explain = this.analysisContent;
+						exerciseItem.answer = [this.$refs.connector.answerContent];
+						break;
+					case "correct":
+						exerciseItem.question = this.$refs.correct.stemContent;
+						exerciseItem.option = [];
+						exerciseItem.type = this.exersicesType;
+						exerciseItem.level = +this.exersicesDiff;
+						exerciseItem.explain = this.analysisContent;
+						exerciseItem.answer = [this.$refs.correct.answerContent];
+						break;		
+					case "subjective":
+						exerciseItem.question = this.$refs.subjective.stemContent;
+						exerciseItem.option = [];
+						exerciseItem.type = this.exersicesType;
+						exerciseItem.level = +this.exersicesDiff;
+						exerciseItem.explain = this.analysisContent;
+						exerciseItem.answer = [this.$refs.subjective.answerContent];
+						break;
+					case "compose":
+						exerciseItem.question = this.$refs.compose.stemContent;
+						exerciseItem.option = [];
+						exerciseItem.type = this.exersicesType;
+						exerciseItem.level = +this.exersicesDiff;
+						exerciseItem.explain = this.analysisContent;
+						exerciseItem.children = this.childList;
+						break;
+					default:
+						break;
+				}
+				exerciseItem.explain = this.analysisContent;
+				exerciseItem.repairResource = this.formatRepairResource(
+					this.$refs.repairRef.datas
+				);
+				exerciseItem.field = this.exerciseField + 1;
+				exerciseItem.points = this.exercisePoints;
+				exerciseItem.periodId = this.isSchool ?
+					this.schoolInfo.period[this.exercisePeriod].id :
+					null;
+				exerciseItem.gradeIds = this.isSchool ?
+					this.exerciseGrade.length ?
+					this.exerciseGrade :
+					this.gradeList.map((i) => i.id) :
+					null;
+				exerciseItem.subjectId = this.isSchool ?
+					this.schoolInfo.period[this.exercisePeriod].subjects[
+						this.exerciseSubject
+					].id :
+					null;
+				exerciseItem.code =
+					this.editInfo.scope === "school" ?
+					this.$store.state.userInfo.schoolCode :
+					this.$store.state.userInfo.TEAMModelId;
+				exerciseItem.scope = this.exerciseScope === 0 ? "private" : "school";
+
+				// 判断获取的数据是否有空数据以及是否为空字符串
+				if (this.checkContent(exerciseItem)) {
+					exerciseItem = await this.$editorTools.transBase64Src(exerciseItem)
+					if (this.refId !== "paperEdit") {
+						// 生成JSON文件名称以及新增试题的ID
+						const guid = this.editInfo.id ? this.editInfo.id : this.$tools.guid();
+						// 给新增的试题赋值ID
+						exerciseItem.id = guid;
+						if(exerciseItem.children && exerciseItem.children.length && exerciseItem.type === 'compose'){
+							console.log('编辑前的children',this.oldChildList)
+							exerciseItem.children.forEach((child) => {
+								child.periodId = exerciseItem.periodId
+								child.gradeIds = exerciseItem.gradeIds
+								child.subjectId = exerciseItem.subjectId
+								child.scope = exerciseItem.scope
+								child.pid = exerciseItem.id
+							})
+							let modifyChildren = this.checkModifyChildrens(exerciseItem.children)
+							console.log('修改了的children',modifyChildren)
+							exerciseItem.children  = await this.saveChildrens(modifyChildren,exerciseItem.children)
+							console.log(exerciseItem)
+						}
+						// 将当前的试题数据转化为BLOB内部的试题JSON格式
+						const itemJsonFile = await this.$evTools.createBlobItem(exerciseItem);
+						// 首先保存新题目的JSON文件到Blob 然后返回URL链接
+						let file = new File([JSON.stringify(itemJsonFile)], guid + ".json", {
+							type: "",
+						});
+						// 获取初始化Blob需要的数据
+						let sasData =
+							this.exerciseScope === 0 ?
+							await this.$tools.getPrivateSas() :
+							await this.$tools.getSchoolSas();
+						//初始化Blob
+						let containerClient = new blobTool(
+							sasData.url,
+							sasData.name,
+							sasData.sas,
+							exerciseItem.scope
+						);
+
+						try {
+							console.log(exerciseItem)
+							// 等待上传blob的返回结果
+							let blobFile = await containerClient.upload(file, "item/" + guid);
+							if (blobFile.blob) {
+								console.log(exerciseItem)
+								// 保存到COSMOS是不含base64图片编码的数据 避免数据量过大
+								exerciseItem.blob = blobFile.blob;
+								let cosmosItem = await this.$evTools.createCosmosItem(
+									exerciseItem
+								);
+								console.log(exerciseItem)
+								this.saveExercise(cosmosItem);
+							} else {
+								this.$Message.error("试题文件上传失败,请稍后重试!");
+							}
+						} catch (e) {
+							this.$Message.error(e.spaceError);
+						}
+					} else {
+						// 如果是试卷内编辑试题 则返回编辑好的数据 再统一进行保存
+						this.saveExercise(exerciseItem);
+					}
+				} else if (exerciseItem.level === 0) {
+					this.$Message.warning("题目难度未设置!");
+					this.isLoading = false;
+				} else {
+					this.$Message.warning("请将题目填写完整!");
+					this.isLoading = false;
+				}
+				console.log(exerciseItem);
+			},
+			
+			/* 检查需要更新的子题 */
+			checkModifyChildrens(childrens){
+				if(!this.oldChildList.length){
+					return childrens
+				}
+				let oldChildren = JSON.parse(JSON.stringify(this.oldChildList)) 
+				let result = childrens.filter((v) => {
+				  var str = JSON.stringify(v);
+				  return oldChildren.every((v) => JSON.stringify(v) != str);
+				});
+				return result
+			},
+
+			
+			/* 保存综合题的子题 */
+			saveChildrens(modifyChildrens,childrens){
+				return new Promise((resolve,reject) => {
+					let promiseArr = []
+					modifyChildrens.forEach(exerciseItem => {
+						promiseArr.push(new Promise(async (r,j) => {
+							console.log(exerciseItem)
+							// 将当前的试题数据转化为BLOB内部的试题JSON格式
+							const itemJsonFile = await this.$evTools.createBlobItem(exerciseItem);
+							// 首先保存新题目的JSON文件到Blob 然后返回URL链接
+							let file = new File([JSON.stringify(itemJsonFile)], exerciseItem.id + ".json");
+							// 获取初始化Blob需要的数据
+							let sasData =
+								exerciseItem.scope === 'private' ?
+								await this.$tools.getPrivateSas() :
+								await this.$tools.getSchoolSas();
+							//初始化Blob
+							let containerClient = new blobTool(
+								sasData.url,
+								sasData.name,
+								sasData.sas,
+								exerciseItem.scope
+							);
+							try {
+								// 等待上传blob的返回结果
+								let blobFile = await containerClient.upload(file, "item/" + exerciseItem.id);
+								if (blobFile.blob) {
+									// 保存到COSMOS是不含base64图片编码的数据 避免数据量过大
+									exerciseItem.blob = blobFile.blob;
+									let cosmosItem = await this.$evTools.createCosmosItem(exerciseItem)
+									console.log(this.oldChildList.map(i => i.id))
+									console.log(exerciseItem.id)
+									let isNew = this.oldChildList.map(i => i.id).indexOf(exerciseItem.id) === -1
+									this.$api.newEvaluation.SaveSingleExercise({
+											itemInfo: cosmosItem,
+											option: isNew ? "insert" : "update",
+										}).then((res) => {
+											console.log('保存了一个子题',res.itemInfo)
+											r(res.itemInfo)
+										});
+								} else {
+									this.$Message.error("试题文件上传失败,请稍后重试!");
+								}
+							} catch (e) {
+								this.$Message.error(e);
+							}
+						}))
+					})
+					
+					/* 如果存在删除的子题 则需要把子题从blob和cosmos中进行删除 */
+					if(this.deleteChildrens.length){
+						this.deleteChildrens.forEach(child => {
+							if(this.oldChildList.map(i => i.id).indexOf(child.id) > -1){
+								promiseArr.push(new Promise((r,j) => {
+									this.onDeleteChildBlob(child).then(res => {
+										if(!res.error){
+											r(200)
+										}
+									})
+								}))
+							}
+						})
+					}
+					
+					
+					Promise.all(promiseArr).then(result => {
+						resolve(childrens.map(i => i.id))
+					})
+				})
+			},
+			
+			/* 删除blob和cosmos里面的子题 */
+			onDeleteChildBlob(item){
+				return new Promise((r,j) => {
+					this.$api.newEvaluation
+						.DeleteExamItem({
+							id: item.id,
+							code: item.code,
+							scope: item.scope,
+						})
+						.then(async (res) => {
+							// 获取初始化Blob需要的数据
+							this.deleteBlobPrefix(item).then(status => {
+								r(status)
+							})
+						})
+						.catch((err) => {
+							j(err);
+							this.$Message.warning("删除失败");
+						});
+				})
+				
+			},
+			
+			/* 删除blob指定试题目录下所有 */
+			deleteBlobPrefix(item) {
+				return new Promise((resolve,reject) => {
+					this.$api.uploadFile.deletePrefix({
+						"cntr": item.scope === 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId,
+						"prefix": "item/" + item.id
+					}).then(
+						(res) => {
+							if (!res.error) {
+								resolve(200)
+							} else {
+								resolve(500)
+							}
+						},
+						(err) => {
+							reject(err)
+						}
+					)
+				})
+			},
+
+			/**
+			 * 保存编辑后的试题
+			 * @param item
+			 */
+			saveExercise(item) {
+				this.$emit("onEditSuccess", item);
+			},
+
+			/* 检测补救资源超链接 去除无效链接 */
+			formatRepairResource(list) {
+				if (list.length) {
+					let arr = [];
+					list.forEach((i, index) => {
+						if (this.$tools.isURL(i.blobUrl)) {
+							arr.push(i);
+						}
+					});
+					return arr;
+				} else {
+					return [];
+				}
+			},
+
+			onEditChildFinish(list,deleteId) {
+				list.forEach((i) => {
+					i.code = this.editInfo.code;
+				});
+				this.childList = list;
+				console.log('编辑完之后的list',list)
+			},
+			
+			onDeleteChild(item){
+				this.deleteChildrens.push(item)
+				console.log('需要删除的子题',this.deleteChildrens)
+			},
+
+			onConfirm() {
+				this.selectPointsModal = false;
+			},
+
+			onCheckChange(val, list) {
+				console.log(val);
+				this.exercisePoints = val;
+			},
+
+			/**
+			 * 删除选好的关联知识点
+			 * @param index
+			 */
+			onDeletePoint(index) {
+				this.exercisePoints.splice(index, 1);
+			},
+
+			// 题目类型转换
+			typeChange(val) {
+				if (this.isEdit) {
+					this.$Message.warning("暂不支持更换题型!");
+				}else {
+					this.exersicesType = val;
+					this.analysisEditor.txt.clear();
+					this.repairEditor.txt.clear();
+				}
+			},
+
+			// 难度与背景颜色切换
+			diffChange(e, type) {
+				this.exersicesDiff = +type;
+				e.preventDefault();
+				let colorArr = ["#10abe7", "#E8BE15", "#F19300", "#EB5E00", "#D30000"];
+				/** 通过动态赋予ID 解决多个组件下获取DOM覆盖问题 */
+				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";
+				}
+				e.target.style.background = colorArr[+type - 1];
+				e.target.style.color = "#fff";
+			},
+
+			onPeriodChange(val) {
+				if (!val) return;
+				console.log(this.schoolInfo);
+				console.log(val);
+				this.gradeList = this.schoolInfo.period[val].grades;
+				this.subjectList = this.schoolInfo.period[val].subjects;
+				this.exerciseGrade = 0;
+				this.exerciseSubject = 0;
+				this.exercisePoints = []; // 切换学段后 知识点需要重新选择
+			},
+			
+			/* 切换科目 将知识点重置 */
+			onSubjectChange(){
+				this.exercisePoints = []
+			},
+
+			// 提取富文本内容中的文本
+			getSimpleText(html) {
+				var r = /<[^>]*>|/g;
+				return html.replace(r, "");
+			},
+			// 排除对象空属性
+			checkContent(Obj) {
+				let flag = true;
+				let whiteList = this.getWhiteListByType(Obj.type);
+				for (let key in Obj) {
+					if (whiteList.includes(key) && typeof Obj[key] === "string") {
+						if (!this.getSimpleText(Obj[key])) {
+							console.log(key);
+							flag = false;
+						}
+					} else if (whiteList.includes(key) && key === "answer") {
+						if (Obj[key].length === 0) {
+							console.log(key);
+							flag = false;
+						}
+					} else {
+						if (whiteList.includes(key) && !Obj[key]) {
+							console.log(key);
+							flag = false;
+						}
+					}
+				}
+				return flag;
+			},
+
+			// 重置编辑器
+			resetEditor() {
+				this.$router.push({
+					name: "personalBank",
+					params: {
+						tabName: "exercise",
+					},
+				});
+			},
+
+			onAddChildFinish(item) {
+				console.log("获取到小题");
+				console.log(item);
+				this.addChildModal = false;
+				this.childList.push(item);
+			},
+
+			// 重置
+			reloadCreate() {
+				location.reload();
+			},
+
+			/* 检测数组是否有空数据 */
+			checkOptionNull(arr) {
+				let flag = true;
+				for (let i = 0; i < arr.length; i++) {
+					if (this.getSimpleText(arr[i].value) === "") {
+						flag = false;
+					}
+				}
+				return flag;
+			},
+
+			// 根据不同题型 给出需要必填选项
+			getWhiteListByType(type) {
+				switch (type) {
+					case "single":
+						return ["question", "option", "answer"];
+						break;
+					case "multiple":
+						return ["question", "option", "answer"];
+						break;
+					case "complete":
+						return ["question", "answer"];
+						break;
+					default:
+						return ["question"];
+						break;
+				}
+			},
+
+			// 渲染编辑习题内容回显
+			async renderExercise(editItem) {
+				this.editInfo = editItem;
+				this.curId = editItem.id
+				
+				let schoolProfile = await this.$store.dispatch('user/getSchoolProfile')
+				let schoolInfo = schoolProfile.school_base;
+				
+				if (editItem.scope === "school" && schoolInfo) {
+					console.log('进来了')
+					this.schoolInfo = schoolInfo;
+					this.exerciseGrade = editItem.gradeIds;
+					this.exercisePeriod = schoolInfo.period
+						.map((item) => item.id)
+						.indexOf(editItem.periodId);
+					this.subjectList = schoolInfo.period[this.exercisePeriod].subjects;
+					this.gradeList = schoolInfo.period[this.exercisePeriod].grades;
+					this.exerciseSubject = this.subjectList
+						.map((item) => item.id)
+						.indexOf(editItem.subjectId);
+				}
+				
+				this.isEdit = true;
+				this.exersicesDiff = editItem.level.toString() || "0";
+				this.exerciseScope = editItem.scope === "private" ? 0 : 1;
+				this.exersicesType = editItem.type;
+				this.exerciseField = editItem.field - 1;
+				this.exercisePoints = editItem.points;
+				if (editItem.level) {
+					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 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;
+					for (let i = 0; i < ac.length; i++) {
+						ac[i].style.background = "#fff";
+						ac[i].style.color = "#515a6e";
+					}
+				}
+				
+				if(editItem.type === 'compose'){
+					this.oldChildList = JSON.parse(JSON.stringify(editItem.children)) || []
+				}
+				this.childList = editItem.children || [];
+				this.stemContent = editItem.question;
+				this.relateFileList = editItem.repairResource || [];
+				this.optionsContent = editItem.option;
+				this.analysisContent = editItem.explain;
+				this.analysisEditor.txt.html(editItem.explain);
+			},
+		},
+		mounted() {
+			let analysisEditor = new E(this.$refs.analysisEditor);
+			analysisEditor.config.uploadImgShowBase64 = true;
+			analysisEditor.config.onchange = (html) => {
+				this.analysisContent = html;
+			},
+			this.$editorTools.addVideoUpload(this, analysisEditor)
+			this.$editorTools.addAudio(this, analysisEditor)
+			this.$editorTools.addCanvas(this, analysisEditor)
+			this.$editorTools.initMyEditor(analysisEditor)
+			
+			analysisEditor.create();
+			this.analysisEditor = analysisEditor;
+		},
+		computed: {
+			isSchool() {
+				return this.exerciseScope === 1;
+			},
+			curScope(){
+				return this.exerciseScope === 1 ? 'school' : 'private'
+			},
+			hasSchool() {
+			  let user = JSON.parse(
+			    decodeURIComponent(localStorage.user_profile, "utf-8")
+			  );
+			  return (
+				user.schools && 
+			    user.schools.length &&
+			    user.schools.filter((i) => i.status === "join").length > 0
+			  );
+			},
+		},
+		watch: {
+			exerciseItem: {
+				handler(newValue, oldValue) {
+					if (newValue) {
+						console.log("传进来的ITEM");
+						console.log(newValue);
+						this.isLoading = false;
+						this.renderExercise(JSON.parse(JSON.stringify(newValue)));
+					}
+				},
+				//immediate:true
+			},
+		},
+	};
+</script>
+<style src="../index/CreateExercises.less" lang="less" scoped>
+</style>
+
+<style>
+	.exersices-attr .ivu-select-multiple .ivu-tag {
+		height: 32px;
+		line-height: 31px;
+	}
+
+	.exersices-attr .ivu-select-multiple .ivu-tag i {
+		top: 9px;
+	}
+
+	.exersices-attr .ivu-select-multiple .ivu-select-selection .ivu-select-placeholder {
+		line-height: 38px;
+	}
+
+	.component-ev-container .ivu-select-single .ivu-select-selection .ivu-select-placeholder {
+		line-height: 38px;
+		height: 38px;
+	}
+
+
+	.related-point-modal .ivu-modal-header-inner {
+		font-weight: bold;
+	}
+
+	.exersices-attr .ivu-select-multiple .ivu-tag {
+		height: 32px;
+		line-height: 31px;
+	}
+
+	.exersices-attr .ivu-select-multiple .ivu-tag i {
+		top: 9px;
+	}
+
+	.exersices-attr .ivu-select-multiple .ivu-select-selection .ivu-select-placeholder {
+		line-height: 38px;
+	}
+
+	/* 修改iview Modal样式 */
+
+	.related-modal .ivu-modal-content {
+		background: #3c3c3c;
+		overflow: hidden;
+		color: #fff;
+	}
+
+	.related-modal .ivu-modal-body {
+		height: 400px;
+		padding: 20px;
+	}
+
+	.related-modal .ivu-modal-body {
+		height: 700px;
+	}
+
+	.related-modal .ivu-modal-header {
+		border-bottom: none;
+		font-size: 18px;
+		font-weight: bold;
+		padding: 25px;
+	}
+
+	.related-modal .modal-content {
+		padding: 0 35px 30px 35px;
+	}
+
+	.related-modal .modal-btn {
+		margin-left: 2%;
+		width: 96%;
+		height: 40px;
+		background: rgb(11, 151, 117);
+		border: none;
+		color: #fff;
+		margin-top: 30px;
+	}
+
+	.related-modal .choose-content {
+		height: 85%;
+	}
+
+	.btn-relate-content {
+		right: 0;
+	}
+</style>

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

@@ -19,8 +19,8 @@
 					<div class="item-tools-wrap">
 						<!-- <div class="item-tools-t flex-row-center" v-show="isShowTools" @click.stop="handleSetScore(item,exerciseList.indexOf(item),typeItem.list,index)">
 							<Icon type="ios-list-box-outline" />配分</div> -->
-						<!-- <div class="item-tools-t flex-row-center" v-show="isShowTools" @click.stop="handleToolEdit(typeItem.list,item,index)">
-							<Icon type="ios-brush-outline" />编辑</div> -->
+						<div class="item-tools-t flex-row-center" v-if="!item.blob" @click.stop="handleToolEdit(typeItem.list,item,index)">
+							<Icon type="ios-brush-outline" />编辑</div>
 						<div class="item-tools-t flex-row-center" v-show="isShowTools && !isExamPaper &&noAnswerList.indexOf(item) > -1"
 						 @click.stop="onSetAnswer(item,exerciseList.indexOf(item),typeItem.list,index)">
 							<Icon type="ios-brush-outline" />设置正确答案</div>
@@ -180,12 +180,12 @@
 		</Modal>
 
 		<!-- 编辑试题弹窗 -->
-		<!-- 		<Modal v-model="editExerciseModal" class-name="edit-exercise-modal" width="1200px" footer-hide title="编辑习题">
+		<Modal v-model="editExerciseModal" class-name="edit-exercise-modal" width="1200px" footer-hide title="编辑习题">
 			<BaseEditExercise :exerciseItem="currentExercise" @onEditSuccess="onEditSuccess" refId="paperEdit" ref="paperEdit"></BaseEditExercise>
 			<div slot="footer">
 				<Button type="success">确认</Button>
 			</div>
-		</Modal> -->
+		</Modal>
 	</div>
 </template>
 
@@ -522,8 +522,6 @@
 
 			/** 编辑成功 */
 			onEditSuccess(item) {
-
-
 				if (item.id) {
 					this.$refs.paperEdit.isLoading = false
 					this.editExerciseModal = false
@@ -538,7 +536,6 @@
 					} else {
 						this.modifyItems[existIndex] = item
 					}
-
 				} else {
 					this.editExerciseModal = false
 					this.exerciseList.splice(this.currentExerciseIndex, 1, item)

+ 0 - 2
TEAMModelOS/ClientApp/src/view/learnactivity/ManualCreate.vue

@@ -252,14 +252,12 @@
 						let scope = code === this.$store.state.userInfo.TEAMModelId ? 'private' : 'school'
 						try{
 							let data = await this.queryQuestion(scope, code)
-							console.log(data)
 							resultList = resultList.concat(data)
 						}catch(e){
 							console.log(e)
 						}
 						
 					}
-					console.log('11111111111')
 					this.questionList = resultList
 					this.totalNum = this.questionList.length
 					this.isLoading = false

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

@@ -316,6 +316,7 @@ export default {
                         let resData = res.examInfo[0]
                         for (let index in resData.papers) {
                             resData.papers[index].scope = resData.scope
+                            resData.papers[index].examId = resData.id
                             resData.papers[index] = await this.$evTools.getFullPaper(resData.papers[index])
                         }
                         this.evaListShow.splice(this.curEvaIndex, 1, resData)

+ 1 - 35
TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.less

@@ -215,41 +215,7 @@
     border-radius: 2px;
     font-size:12px;
 }
-.overview-box{
-    width: 100%;
-    // min-height: 400px;
-    padding: 10px 20px 10px 10px;
-    display: flex;
-    justify-content: space-between;
-    background: #404040;
-    // margin-top: 5px;
-    .count-box{
-        width: 120px;
-        height: fit-content;
-        text-align: center;
-        background: #404040;
-        padding: 10px 0px;
-        .count-icon{
-            color: white;
-            font-size: 18px;
-            margin-right: 5px;
-            vertical-align: sub;
-            // display: block;
-        }
-        .count-subject-text{
-            display: block;
-            color: white;
-            font-size: 12px;
-        }
-        .count-subject-num{
-            display: block;
-            color: white;
-            font-size: 35px;
-            font-weight: 800;
-        }
-    }
-    
-}
+
 .handle-end-tag{
     background:#ed4014;
     padding: 1px 2px;

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

@@ -358,6 +358,7 @@ export default {
                         let resData = res.examInfo[0]
                         for (let index in resData.papers) {
                             resData.papers[index].scope = resData.scope
+                            resData.papers[index].examId = resData.id
                             resData.papers[index] = await this.$evTools.getFullPaper(resData.papers[index])
                         }
                         this.evaListShow.splice(this.curEvaIndex, 1, resData)

+ 10 - 4
TEAMModelOS/ClientApp/src/view/learnactivity/Scoring.less

@@ -8,6 +8,7 @@
 .ev-scoring {
     width: 100%;
     height: 100%;
+    padding: 0px 0px 20px 0px;
 }
 .ev-scoring {
     .ev-scoring-header {
@@ -42,17 +43,17 @@
     user-select: none;
 }
 .scoring-main-wrap {
-    width: ~"calc(100% - 10px)";
+    // width: ~"calc(100% - 10px)";
+    width: 100%;
     height: ~"calc(100% - 45px)";
 }
 
 .ev-target-box {
-    margin-top: 10px;
+    margin-top: 20px;
     width: 100%;
     color: #ffffff;
     padding-bottom:10px;
     border-bottom:1px solid #606060;
-    padding-left:15px;
     .filter-select {
         display: inline-block;
         width: 120px;
@@ -61,7 +62,7 @@
 }
 
 .score-box {
-    margin-top: 15px;
+    // margin-top: 15px;
 }
 .scoring-handle-box {
     margin-top: 10px;
@@ -85,4 +86,9 @@
     height: 7px;
     border-radius: 50%;
     margin-right: 5px;
+}
+.simple-analysis-box{
+    width: 100%;
+    height: 200px;
+    // background: white;
 }

+ 67 - 55
TEAMModelOS/ClientApp/src/view/learnactivity/Scoring.vue

@@ -1,59 +1,63 @@
 <template>
-    <div class="ev-scoring">
-        <div class="ev-target-box dark-iview-select">
-            <span class="filter-label" v-show="examInfo.scope == 'school'">学科:</span>
-            <Select filterable v-model="chooseSubject" class="filter-select" size="small" @on-change="getCurPaper" v-show="examInfo.scope == 'school'">
-                <Option v-for="(item,index) in examInfo.subjects" :value="item.id" :key="index">{{ item.name }}</Option>
-            </Select>
-            <span class="filter-label" v-show="examInfo.scope == 'school'">年级:</span>
-            <Select filterable v-model="chooseGrade" class="filter-select" size="small" v-show="examInfo.scope == 'school'" style="margin-right:5px">
-                <Option v-for="(item,index) in examInfo.grades" :value="item.id" :key="index">{{ item.name }}</Option>
-            </Select>
-            <span>班级:</span>
-            <Select filterable v-model="chooseClass" class="filter-select" style="width:140px;" @on-change="getClassStudent" size="small">
-                <Option v-for="(item,index) in classList" :value="item.id" :key="index">{{ item.name }}</Option>
-            </Select>
-            <span style="margin-left:5px" v-show="showTest">学生:</span>
-            <Select filterable v-model="chooseStudent.id" label-in-value class="filter-select" style="width:140px;" size="small" clearable @on-change="setStuInfo" v-show="showTest">
-                <Option v-for="(item,index) in students" :value="item.id" :key="index">
-                    <span class="select-status-tag" :style="{'background':item.status == 1 ? '#ed4014' : item.status == 2 ? '#ff9900' : '#19be6b'}"></span>
-                    {{ item.name }}
-                </Option>
-            </Select>
-            <span v-show="showTest" class="common-icon-text" style=" float: right; margin-right: 25px;" @click="toggleScoreStatus" icon="md-apps">
-                <Icon :custom="showTest ? 'iconfont icon-table':'iconfont icon-scoring'" style="margin-right:5px;" />
-                {{showTest ? '分数概览':'试卷评分'}}
-            </span>
-        </div>
-        <div class="scoring-main-wrap dark-iview-table">
-            <Table v-show="!showTest" class="score-box" border :columns="tableColumn" :data="studentScore" :loading="tableLoading" :max-height="tableHeight" no-data-text="班级暂无学生">
-                <template slot-scope="{ row,index }" :slot="'qu'+qIndex" v-for="(item,qIndex) in quCount">
-                    <div :key="'qu'+qIndex" @click="getStuScore(row,qIndex)" style="cursor:pointer;">
-                        <span @click="noAnswer" v-if="row.data[qIndex] == -1 && row.status == 1">- -</span>
-                        <Icon size="20" type="ios-create-outline" v-else-if="row.data[qIndex] == -1 && row.status !== 1" />
-                        <span style="color:#2db7f5;" v-else>{{row.data[qIndex]}}</span>
-                    </div>
-                </template>
-                <template slot-scope="{ row,index }" slot="total">
-                    <strong>{{getcount(studentScore[row._index].data)}}</strong>
-                </template>
-                <!-- 1: 未作答 2:未评分 3:已评分 -->
-                <template slot-scope="{ row,index }" slot="status">
-                    <span class="stu-status-tag" @click="getStuScore(row,0)" :style="{'background':row.status == 1 ? '#c5c8ce' : row.status == 2 ? '#ff9900' : '#19be6b', 'cursor':row.status == 1 ? 'text':'pointer'}">
-                        {{row.status == 1 ? '暂未作答' : row.status == 2 ? '前往评分' : '查看分数'}}
-                    </span>
-                </template>
-                <Loading slot="loading" :top="-50"></Loading>
-            </Table>
-            <div class="dark-iview-table scoring-handle-box" v-show="showTest">
-                <PaperScore ref="paperScore" :defaultIndex="defaultIndex" :examId="examInfo.id" :paper="paperInfo" :studentAnswer="chooseStudent" :subjectId="chooseSubject" @nextStu="getNextStu" style="color:#515a6e;"></PaperScore>
-                <Loading :top="200" type="1" style="text-align:center" v-show="dataLoading"></Loading>
+    <div class="ev-scoring dark-iview-table">
+        <vuescroll>
+            <SimpleAnalysis :status="examInfo.progress"></SimpleAnalysis>
+            <div class="ev-target-box dark-iview-select">
+                <span class="filter-label" v-show="examInfo.scope == 'school'">学科:</span>
+                <Select filterable v-model="chooseSubject" class="filter-select" size="small" @on-change="getCurPaper" v-show="examInfo.scope == 'school'">
+                    <Option v-for="(item,index) in examInfo.subjects" :value="item.id" :key="index">{{ item.name }}</Option>
+                </Select>
+                <span class="filter-label" v-show="examInfo.scope == 'school'">年级:</span>
+                <Select filterable v-model="chooseGrade" class="filter-select" size="small" v-show="examInfo.scope == 'school'" style="margin-right:5px">
+                    <Option v-for="(item,index) in examInfo.grades" :value="item.id" :key="index">{{ item.name }}</Option>
+                </Select>
+                <span>班级:</span>
+                <Select filterable v-model="chooseClass" class="filter-select" style="width:140px;" @on-change="getClassStudent" size="small">
+                    <Option v-for="(item,index) in classList" :value="item.id" :key="index">{{ item.name }}</Option>
+                </Select>
+                <span style="margin-left:5px" v-show="showTest">学生:</span>
+                <Select filterable v-model="chooseStudent.id" label-in-value class="filter-select" style="width:140px;" size="small" clearable @on-change="setStuInfo" v-show="showTest">
+                    <Option v-for="(item,index) in students" :value="item.id" :key="index">
+                        <span class="select-status-tag" :style="{'background':item.status == 1 ? '#ed4014' : item.status == 2 ? '#ff9900' : '#19be6b'}"></span>
+                        {{ item.name }}
+                    </Option>
+                </Select>
+                <span v-show="showTest" class="common-icon-text" style=" float: right; margin-right: 25px;" @click="toggleScoreStatus" icon="md-apps">
+                    <Icon :custom="showTest ? 'iconfont icon-table':'iconfont icon-scoring'" style="margin-right:5px;" />
+                    {{showTest ? '分数概览':'试卷评分'}}
+                </span>
             </div>
-        </div>
+            <div class="scoring-main-wrap">
+                <Table v-show="!showTest" class="score-box" border :columns="tableColumn" :data="studentScore" :loading="tableLoading" :max-height="tableHeight" no-data-text="班级暂无学生">
+                    <template slot-scope="{ row,index }" :slot="'qu'+qIndex" v-for="(item,qIndex) in quCount">
+                        <div :key="'qu'+qIndex" @click="getStuScore(row,qIndex)" style="cursor:pointer;">
+                            <span @click="noAnswer" v-if="row.data[qIndex] == -1 && row.status == 1">- -</span>
+                            <Icon size="20" type="ios-create-outline" color="#2db7f5" v-else-if="row.data[qIndex] == -1 && row.status !== 1" />
+                            <span style="color:#2db7f5;" v-else>{{row.data[qIndex]}}</span>
+                        </div>
+                    </template>
+                    <template slot-scope="{ row,index }" slot="total">
+                        <strong>{{getcount(studentScore[row._index].data)}}</strong>
+                    </template>
+                    <!-- 1: 未作答 2:未评分 3:已评分 -->
+                    <template slot-scope="{ row,index }" slot="status">
+                        <span class="stu-status-tag" @click="getStuScore(row,0)" :style="{'background':row.status == 1 ? '#c5c8ce' : row.status == 2 ? '#ff9900' : '#19be6b', 'cursor':row.status == 1 ? 'text':'pointer'}">
+                            {{row.status == 1 ? '暂未作答' : row.status == 2 ? '前往评分' : '查看分数'}}
+                        </span>
+                    </template>
+                    <Loading slot="loading" :top="-50"></Loading>
+                </Table>
+                <div class="dark-iview-table scoring-handle-box" v-show="showTest">
+                    <PaperScore ref="paperScore" :defaultIndex="defaultIndex" :examId="examInfo.id" :paper="paperInfo" :studentAnswer="chooseStudent" :subjectId="chooseSubject" @nextStu="getNextStu" style="color:#515a6e;"></PaperScore>
+                    <Loading :top="200" type="1" style="text-align:center" v-show="dataLoading"></Loading>
+                </div>
+            </div>
+        </vuescroll>
     </div>
 </template>
 <script>
 import PaperScore from "./PaperScore.vue";
+import SimpleAnalysis from "./SimpleAnalysis.vue";
 export default {
     props: {
         examInfo: {
@@ -64,7 +68,7 @@ export default {
         },
     },
     components: {
-        PaperScore,
+        PaperScore, SimpleAnalysis
     },
     data() {
         return {
@@ -206,7 +210,7 @@ export default {
             let requestData = {
                 id: this.chooseClass,
                 scope: this.routerScope,
-                school_code: this.routerScope == 'school' ?  this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId,
+                school_code: this.routerScope == 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId,
             };
             this.$api.schoolSetting.getClassroomStudent(requestData).then((res) => {
                 if (!res.error) {
@@ -457,7 +461,7 @@ export default {
         let tableWrap = document.getElementsByClassName('scoring-main-wrap')
         this.tableHeight = tableWrap[0].offsetHeight - 27
     },
-    created(){
+    created() {
         if (this.$route.name == 'privateEvaluation') {
             this.routerScope = 'private'
         } else {
@@ -472,7 +476,7 @@ export default {
 </style>
 <style lang="less">
 .scoring-main-wrap .ivu-table-fixed-body {
-    background: #2b2b2e;
+    background: #454545;
     // max-height: 701px;
 }
 .scoring-main-wrap .ivu-table-tip {
@@ -484,8 +488,16 @@ export default {
     display: none;
 }
 .scoring-main-wrap .ivu-table-fixed-header thead tr th {
-    background: #2b2b2e;
+    background: #454545;
     border-color: #606060;
     color: white;
 }
+.scoring-main-wrap {
+    .ivu-table-header thead tr th {
+        background: #404040;
+    }
+    .ivu-table td {
+        background: #404040;
+    }
+}
 </style>

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

@@ -0,0 +1,155 @@
+<template>
+    <div>
+        <div class="overview-box" v-if="status == 'finish'">
+            <div class="count-box">
+                <span class="count-subject-num">752</span>
+                <span class="count-subject-text">
+                    <Icon type="ios-people" class="count-icon" />
+                    总人数
+                </span>
+            </div>
+            <div class="count-box">
+                <span class="count-subject-num">1</span>
+                <span class="count-subject-text">
+                    <Icon type="ios-paper" class="count-icon" />
+                    缺考数
+                </span>
+            </div>
+            <div class="count-box">
+                <span class="count-subject-num">5</span>
+                <span class="count-subject-text">
+                    <Icon custom="iconfont icon-class-self" class="count-icon" />
+                    班级
+                </span>
+            </div>
+            <!-- <div class="count-box">
+                <span class="count-subject-num">3</span>
+                <span class="count-subject-text">
+                    <Icon custom="iconfont icon-kecheng" class="count-icon" size="14" style="vertical-align: text-top;" />
+                    学科
+                </span>
+            </div> -->
+            <div class="count-box">
+                <span class="count-subject-num">92</span>
+                <span class="count-subject-text">
+                    <Icon custom="iconfont icon-class-self" class="count-icon" />
+                    总平均
+                </span>
+            </div>
+            <div class="count-box">
+                <span class="count-subject-num">88</span>
+                <span class="count-subject-text">
+                    <!-- <Icon type="ios-arrow-down" class="count-icon" size="14" style="vertical-align: text-top;" /> -->
+                    <Icon custom="iconfont icon-kecheng" class="count-icon" size="14" style="vertical-align: text-top;" />
+                    语文均分
+                </span>
+            </div>
+            <div class="count-box">
+                <span class="count-subject-num">90</span>
+                <span class="count-subject-text">
+                    <Icon custom="iconfont icon-kecheng" class="count-icon" size="14" style="vertical-align: text-top;" />
+                    数学均分
+                </span>
+            </div>
+            <div class="count-box">
+                <span class="count-subject-num">95</span>
+                <span class="count-subject-text">
+                    <Icon custom="iconfont icon-kecheng" class="count-icon" size="14" style="vertical-align: text-top;" />
+                    英语均分
+                </span>
+            </div>
+        </div>
+        <div class="overview-box" style="justify-content: space-around;padding-top:40px"  v-if="status == 'finish'">
+            <ScoreMatrix></ScoreMatrix>
+            <AvgCompare></AvgCompare>
+        </div>
+        <div class="overview-box" v-if="status == 'going'" style="margin-bottom:30px;">
+            <div class="count-box">
+                <span class="count-subject-num">752</span>
+                <span class="count-subject-text">
+                    <Icon type="ios-people" class="count-icon" />
+                    总人数
+                </span>
+            </div>
+            <div class="count-box">
+                <span class="count-subject-num">352</span>
+                <span class="count-subject-text">
+                    <Icon type="ios-paper" class="count-icon" />
+                    已作答
+                </span>
+            </div>
+            <div class="count-box">
+                <span class="count-subject-num">400</span>
+                <span class="count-subject-text">
+                    <Icon custom="iconfont icon-class-self" class="count-icon" />
+                    未作答
+                </span>
+            </div>
+            <div class="count-box">
+                <span class="count-subject-num">158</span>
+                <span class="count-subject-text">
+                    <Icon custom="iconfont icon-class-self" class="count-icon" />
+                    已评分
+                </span>
+            </div>
+            <div class="count-box">
+                <span class="count-subject-num">58</span>
+                <span class="count-subject-text">
+                    <Icon custom="iconfont icon-class-self" class="count-icon" />
+                    未评分
+                </span>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+import ScoreMatrix from './echarts/ScoreMatrix.vue'
+import AvgCompare from './echarts/AvgCompare.vue'
+export default {
+    components: {
+        AvgCompare,
+        ScoreMatrix
+    },
+    props:{
+        status:{
+            default:'',
+            type:String
+        }
+    }
+}
+</script>
+<style lang="less" scoped>
+.overview-box{
+    width: 100%;
+    flex-wrap: wrap;
+    padding: 10px 20px 10px 10px;
+    display: flex;
+    justify-content: space-between;
+    background: #404040;
+    .count-box{
+        min-width: 150px;
+        height: fit-content;
+        text-align: center;
+        background: #404040;
+        padding: 10px 0px;
+        .count-icon{
+            color: white;
+            font-size: 18px;
+            margin-right: 5px;
+            vertical-align: sub;
+        }
+        .count-subject-text{
+            display: block;
+            color: white;
+            font-size: 12px;
+        }
+        .count-subject-num{
+            display: block;
+            color: white;
+            font-size: 35px;
+            font-weight: 800;
+        }
+    }
+    
+}
+</style>

+ 168 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/echarts/AvgCompare.vue

@@ -0,0 +1,168 @@
+<template>
+    <div id="ev-avg-compare"></div>
+</template>
+<script>
+import elementResizeDetectorMaker from "element-resize-detector"
+export default {
+    props: {
+        pieData: {
+            type: Array,
+            default: () => {
+                return [10, 0, 0, 0, 36, 0, 11, 12, 15, 88]
+            }
+        }
+    },
+    data() {
+        return {
+            progressPie: undefined,
+            option: {
+                color: [
+                    "#F72459",
+                    "#4ECDC4",
+                    "#F99406",
+                    "#075CD0",
+                    "#03C9A8",
+                    "#F7CA17",
+                    "#F2774B",
+                    "#67809F",
+                    "#BF55ED",
+                    "#1cc0f3",
+                    "#19be6b",
+                    "#9A1294",
+                ],
+                legend: {
+                    // data: ['语文', '数学', '英语']
+                    right:5,
+                    data:[
+                        {
+                            name:'语文',
+                            textStyle:{
+                                color:'#FFF'
+                            }
+                        },{
+                            name:'数学',
+                            textStyle:{
+                                color:'#FFF'
+                            }
+                        },{
+                            name:'英语',
+                            textStyle:{
+                                color:'#FFF'
+                            }
+                        }
+                    ]
+                },
+                title: {
+                    "text": "平均分对比",
+                    "left": 0,
+                    "top": 0,
+                    "textStyle": {
+                        "color": "#FFF",
+                        fontSize: 14
+                    }
+                },
+                tooltip: {
+                    trigger: 'axis'
+                },
+                grid: {
+                    left: '3%',
+                    right: '4%',
+                    bottom: '3%',
+                    containLabel: true
+                },
+                // toolbox: {
+                //     feature: {
+                //         saveAsImage: {}
+                //     }
+                // },
+                xAxis: {
+                    type: 'category',
+                    data: ['六年级一班', '六年级二班', '六年级三班', '六年级四班', '六年级五班'],
+                    axisLine: {
+                        lineStyle: {
+                            color: "#FFF"
+                        }
+                    },
+                    axisLabel: {
+                        rotate: 40
+                    }
+                },
+                yAxis: {
+                    type: 'value',
+                    axisLine: {
+                        lineStyle: {
+                            color: "#AAA"
+                        }
+                    },
+                    splitLine: {
+                        lineStyle: {
+                            color: ['#505050']
+                        }
+                    }
+                },
+                series: [
+                    {
+                        name: '语文',
+                        type: 'bar',
+                        stack: '总量',
+                        barMaxWidth: 30,
+                        data: [89, 92, 90, 95, 87],
+
+                    },
+                    {
+                        name: '数学',
+                        type: 'bar',
+                        stack: '总量',
+                        barMaxWidth: 30,
+                        data: [98, 90, 95, 92, 87]
+                    },
+                    {
+                        name: '英语',
+                        type: 'bar',
+                        stack: '总量',
+                        barMaxWidth: 30,
+                        data: [85, 89, 88, 92, 99]
+                    }
+                ]
+            }
+        }
+    },
+    mounted() {
+        this.progressPie = this.$echarts.init(document.getElementById('ev-avg-compare'))
+        this.progressPie.setOption(this.option)
+        let _this = this
+        let erd11 = elementResizeDetectorMaker()
+        erd11.listenTo(document.getElementById("ev-avg-compare"), () => {
+            this.$nextTick(() => {
+                //监听到事件后执行的业务逻辑
+                this.progressPie.resize()
+            })
+        })
+    },
+    watch: {
+        pieData: {
+            handler(n, o) {
+                this.$nextTick(() => {
+                    if (!this.progressPie) {
+                        this.progressPie = this.$echarts.init(document.getElementById('ev-avg-compare'))
+                    }
+                    // this.option.series[0].data.forEach((item, index) => {
+                    //     item.value = this.pieData[index]
+                    // })
+                    this.progressPie.setOption(this.option)
+                })
+            },
+            deep: true,
+            immediate: true
+        }
+    }
+}
+</script>
+<style scoped lang="less">
+#ev-avg-compare {
+    width: 400px;
+    height: 350px;
+}
+</style>
+<style>
+</style>

+ 159 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/echarts/ScoreMatrix.vue

@@ -0,0 +1,159 @@
+<template>
+    <div id="ev-score-matrix"></div>
+</template>
+<script>
+import elementResizeDetectorMaker from "element-resize-detector"
+export default {
+    props: {
+        pieData: {
+            type: Array,
+            default: () => {
+                return [10, 0, 0, 0, 36, 0, 11, 12, 15, 88]
+            }
+        }
+    },
+    data() {
+        return {
+            progressPie: undefined,
+            option: {
+                tooltip: {
+                    trigger: 'item',
+                    formatter: '{b} : {c}人'
+                },
+                title: {
+                    "text": "分数分布",
+                    "left": "center",
+                    "top": 0,
+                    "textStyle": {
+                        "color": "#FFF",
+                        fontSize: 14
+                    }
+                },
+                color: [
+                    "#F72459",
+                    "#4ECDC4",
+                    "#F99406",
+                    "#075CD0",
+                    "#03C9A8",
+                    "#F7CA17",
+                    "#F2774B",
+                    "#67809F",
+                    "#BF55ED",
+                    "#1cc0f3",
+                    "#19be6b",
+                    "#9A1294",
+                ],
+                series: [
+                    {
+                        hoverOffset: 5,
+                        type: 'pie',
+                        radius: '45%',
+                        center: ['50%', '50%'],
+                        selectedMode: 'single',
+                        label: {
+                            show: true,
+                            position: 'outside',
+                            formatter: '{b}\n',
+                            rich: {
+                                hr: {
+                                    backgroundColor: 't',
+                                    borderRadius: 100,
+                                    width: 0,
+                                    height: 4,
+                                    padding: [3, 3, 0, -10],
+                                    shadowColor: '#1c1b3a',
+                                    shadowBlur: 1,
+                                    shadowOffsetX: '0',
+                                    shadowOffsetY: '2',
+                                },
+                                a: {
+                                    padding: [-35, 15, -20, 5],
+                                }
+                            }
+                        },
+                        data: [
+                            {
+                                value: 0,
+                                name: '0-10'
+                            },
+                            {
+                                value: 0,
+                                name: '11-20'
+                            },
+                            {
+                                value: 0,
+                                name: '21-30'
+                            },
+                            {
+                                value: 0,
+                                name: '31-40'
+                            },
+                            {
+                                value: 0,
+                                name: '41-50'
+                            },
+                            {
+                                value: 0,
+                                name: '51-60'
+                            },
+                            {
+                                value: 0,
+                                name: '61-70'
+                            },
+                            {
+                                value: 0,
+                                name: '71-80'
+                            },
+                            {
+                                value: 0,
+                                name: '81-90'
+                            },
+                            {
+                                value: 0,
+                                name: '91-100'
+                            }
+                        ]
+                    }
+                ]
+            }
+        }
+    },
+    mounted() {
+        this.progressPie = this.$echarts.init(document.getElementById('ev-score-matrix'))
+        this.progressPie.setOption(this.option)
+        let _this = this
+        let erd11 = elementResizeDetectorMaker()
+        erd11.listenTo(document.getElementById("ev-score-matrix"), () => {
+            this.$nextTick(() => {
+                //监听到事件后执行的业务逻辑
+                this.progressPie.resize()
+            })
+        })
+    },
+    watch: {
+        pieData: {
+            handler(n, o) {
+                this.$nextTick(() => {
+                    if (!this.progressPie) {
+                        this.progressPie = this.$echarts.init(document.getElementById('ev-score-matrix'))
+                    }
+                    this.option.series[0].data.forEach((item, index) => {
+                        item.value = this.pieData[index]
+                    })
+                    this.progressPie.setOption(this.option)
+                })
+            },
+            deep: true,
+            immediate: true
+        }
+    }
+}
+</script>
+<style scoped lang="less">
+#ev-score-matrix {
+    width: 400px;
+    height: 400px;
+}
+</style>
+<style>
+</style>

+ 0 - 2
TEAMModelOS/ClientApp/src/view/selflearn/PassBar.vue

@@ -203,8 +203,6 @@
         watch: {
             passData: {
                 handler(n, o) {
-                    console.log('0000000000000000000000')
-                    console.log(this.passData)
                     this.$nextTick(() => {
                         this.option.xAxis.data = this.passData.map((item) => {
                             return item.name

+ 3 - 3
TEAMModelOS/ClientApp/src/view/student-account/Index.vue

@@ -88,9 +88,9 @@
                     </template>
                     <template slot-scope="{ row }" slot="action" v-if="authorizationStatus == false">
                         <div class="item-tools" v-if="$access.can('admin.*|student-upd')">
-                            <Icon type="md-create" size="18" color="white" @click="editStudent(row)" title="修改"/>
-                            <Icon custom="iconfont icon-reset" size="18" color="white" @click="resetPW(row)" title=" 重置密码"/>
-                            <Icon type="md-trash" size="18" color="white" @click="delStudent(row)" title="删除"/>
+                            <Icon type="md-create" size="18" color="white" @click="editStudent(row)" title="修改" />
+                            <Icon custom="iconfont icon-reset" size="18" color="white" @click="resetPW(row)" title=" 重置密码" />
+                            <Icon type="md-trash" size="18" color="white" @click="delStudent(row)" title="删除" />
                         </div>
                     </template>
                 </Table>

+ 319 - 3
TEAMModelOS/Controllers/Analysis/AchievementController.cs

@@ -14,21 +14,32 @@ using TEAMModelOS.SDK.Helper.Common.CollectionHelper;
 using TEAMModelOS.SDK.Helper.Common.StringHelper;
 using TEAMModelOS.SDK.Helper.Security.ShaHash;
 using TEAMModelOS.Services.Analysis;
+using Microsoft.Extensions.Options;
+using TEAMModelOS.Models;
+using Azure.Cosmos;
+using TEAMModelOS.SDK.Models.Cosmos.Student;
+using static TEAMModelOS.SDK.Models.Cosmos.Student.StudentAnalysis;
+using TEAMModelOS.SDK.Models.Cosmos.School;
+using static TEAMModelOS.SDK.Models.Cosmos.School.ClassAnalysis;
+using static TEAMModelOS.SDK.Models.Cosmos.School.GradeAnalysis;
 
 namespace TEAMModelOS.Controllers.Analysis
 {
-    [Route("api/[controller]")]
+    [Route("analysis")]
     [ApiController]
     public class AchievementController : Controller
     {
 
         private readonly AzureCosmosFactory _azureCosmos;
-
+        private readonly DingDing _dingDing;
+        private readonly Option _option;
         private const string CacheCosmosPrefix = "Analysis:";
         private const int timeoutSeconds = 3600;
-        public AchievementController(AzureCosmosFactory azureCosmos)
+        public AchievementController(AzureCosmosFactory azureCosmos, DingDing dingDing, IOptionsSnapshot<Option> option)
         {
             _azureCosmos = azureCosmos;
+            _dingDing = dingDing;
+            _option = option?.Value;
 
 
         }
@@ -2209,5 +2220,310 @@ namespace TEAMModelOS.Controllers.Analysis
             //       throw new BizException(e.Message);
             //   }
         }
+
+        [HttpPost("getAnalysis")]
+        public async Task<IActionResult> getAnalysis(JsonElement request) {
+            //获取评测的ID
+            if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
+            if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
+            try {
+                var client = _azureCosmos.GetCosmosClient();
+                //获取本次评测所有科目结算结果
+                ExamInfo info = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<ExamInfo>(id.ToString(), new PartitionKey($"Exam-{code}"));
+                School school = await client.GetContainer("TEAMModelOS", "School").ReadItemAsync<School>(code.ToString(), new PartitionKey($"Base"));
+                List<ExamResult> examResults = new List<ExamResult>();
+                var query = $"select c.id,c.name,c.subjectId,c.studentScores,c.studentIds,c.paper from c where c.examId =  '{id}' ";
+                await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<ExamResult>(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamResult-{id}") }))
+                {
+                    /*using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                    {
+                        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                        {
+                            examResults.Add(obj.ToObject<ExamResult>());
+                        }
+                    }*/
+                    examResults.Add(item);
+                }
+                //获取本次评测所有班级作答结果
+                List<ExamClassResult> examClassResults = new List<ExamClassResult>();
+                var queryClass = $"select c.id,c.name,c.subjectId,c.studentScores,c.studentIds,c.gradeId,c.info from c where c.examId =  '{id}' and c.progress = true ";
+                await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<ExamClassResult>(queryText: queryClass, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{code}") }))
+                {
+                    examClassResults.Add(item);
+                    /*using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                    {
+                        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                        {
+                            examClassResults.Add(obj.ToObject<ExamClassResult>());
+                        }
+                    }*/
+                }
+                //获取进线标准
+                int touch = 0;
+                foreach (Period period in school.period) {
+                    if (info.period.id.Equals(period.id)) {
+                        touch = period.analysis.touch;
+                    }
+                }
+                //获取进线人数
+
+                int personCount = (int)System.Math.Round( info.stuCount * (touch / 100.0), MidpointRounding.AwayFromZero);
+                
+                StudentAnalysis analysis = new StudentAnalysis();
+                List<ClassAnalysis> classAnalyses = new List<ClassAnalysis>();
+               /* //声明年级所有科目总分
+                List<double> total = new List<double>();*/
+                //以班级为单位
+                List<double> stuTotals = new List<double>();
+                List<dynamic> classes = new List<dynamic>();
+                foreach (string s in info.targetClassIds) {
+                   
+                    //存放每个班级学生总分
+                    List<double> totalClass = new List<double>();
+                    ClassAnalysis classAnalysis = new ClassAnalysis();
+                    classAnalysis.classId = s;
+                    var sresponse = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(s, new PartitionKey($"Class-{code}"));
+                    if (sresponse.Status == 200)
+                    {
+                        using var json = await JsonDocument.ParseAsync(sresponse.ContentStream);
+                        Class classroom = json.ToObject<Class>();
+                        classes.Add(new {name=classroom.name,id=s });
+                        classAnalysis.stuCount = classroom.students.Count;
+                        foreach (StudentSimple stu in classroom.students)
+                        {
+                            classAnalysis.studentIds.Add(stu.id);
+                            analysis.names.Add(stu.name);
+                            analysis.ids.Add(stu.id);
+                            //计算学生多科考试成绩总分
+                            double stuTotal = 0;
+                            foreach (ExamResult examResult in examResults)
+                            {
+                                int index = examResult.studentIds.IndexOf(stu.id);
+                                stuTotal = stuTotal + examResult.studentScores[index].Sum();
+                            }
+                            classAnalysis.total.Add(stuTotal);
+                            analysis.total.Add(stuTotal);
+                            stuTotals.Add(stuTotal);
+                            totalClass.Add(stuTotal);
+                            //total.Add(stuTotal);
+                        }
+                    }
+                    /* //声明单个班级单科总分
+                     double subjectTotal = 0;*/
+                    info.subjects.ForEach(sub => {
+                        //初始化及格人数
+                        int i = 0;
+                        //初始化单科总分
+                        double sumClass = 0;
+                        //记录每个科目数据
+                        SubjectScore subjectScore = new SubjectScore();
+                        subjectScore.id = sub.id;
+                        double subjectSum = 0;
+                        double passScore = 0;
+                        //计算单科评测总分
+                        foreach (ExamResult examResult in examResults)
+                        {
+                            if (sub.id.Equals(examResult.subjectId))
+                            {
+                                subjectSum = examResult.paper.point.Sum();
+                            }
+                        }
+                        //单科及格率分数
+                        passScore = subjectSum * 0.6;
+                        //存放学生单科记录
+                        Score score = new Score();
+                        score.subjectId = sub.id;
+                        List<double> subjectTotal = new List<double>();
+                        //处理每个班级各个学生各科总分
+                        foreach (ExamClassResult result in examClassResults)
+                        {
+                            if (sub.id.Equals(result.subjectId) && s.Equals(result.info.id))
+                            {
+                                foreach (List<double> scores in result.studentScores)
+                                {
+                                    //添加每个学生各科总分
+                                    score.csc.Add(scores.Sum());
+                                    //计算大于及格分数的人数
+                                    if (scores.Sum() >= passScore)
+                                    {
+                                        i++;
+                                    }
+                                    //totalClass.Add(scores.Sum());
+                                    subjectTotal.Add(scores.Sum());
+                                    sumClass = sumClass + scores.Sum();
+
+                                }
+                                //处理班级及格率以及均分
+                                double percentClass = i / (result.studentIds.Count * 1.0);
+                                double averageClass = sumClass / (result.studentIds.Count * 1.0);
+                                //total += averageClass;
+                                subjectScore.passPercent= percentClass;
+                                subjectScore.averageScore= averageClass;
+                                classAnalysis.subjects.Add(subjectScore);
+                                //analysis.scores.Add(score);
+                            }
+                        }
+                        //处理单科科班级PR
+                        subjectTotal.Sort(delegate (double s1, double s2) { return s2.CompareTo(s1); });
+                        foreach (double sc in score.csc)
+                        {
+                            int index = subjectTotal.IndexOf(sc);
+                            double CPR = 100 - (100 * (index + 1) - 50) / score.csc.Count;
+                            score.cdpr.Add(CPR);
+
+                        }
+                        analysis.scores.Add(score);
+                    });
+                    //处理全科班级PR
+                    totalClass.Sort(delegate (double s1, double s2) { return s2.CompareTo(s1); });
+                    foreach (double pr in classAnalysis.total)
+                    {
+                        int index = totalClass.IndexOf(pr);
+                        double CPR = 100 - (100 * (index + 1) - 50) / classAnalysis.total.Count;
+                        analysis.cpr.Add(CPR);
+                    }
+                    classAnalyses.Add(classAnalysis);
+                }
+                /*info.targetClassIds.ForEach( async s => {
+                    
+                });*/
+                //初始化进线分数
+                double ipoint = 0;
+                stuTotals.Sort(delegate (double s1, double s2) { return s2.CompareTo(s1); });
+                ipoint = stuTotals[personCount];
+                //补充班级总平均分/进行人数/标准差
+                foreach (ClassAnalysis classAnalysis in classAnalyses) {
+                    //初始化进线人数
+                    int i = 0;
+                    //初始化班级总分
+                    double score = 0;
+                    //标准差
+                    double powSum = 0;
+                    foreach (double sc in classAnalysis.total) {
+                        if (sc > ipoint) {
+                            i++;
+                        }
+                        score = score + sc;                       
+                    }
+                    //总平均分
+                    double totalAverage = 0;
+                    totalAverage = score / classAnalysis.stuCount;
+                    //计算标准差
+                    foreach (double sc in classAnalysis.total)
+                    {
+                        powSum += Math.Pow(sc - totalAverage, 2);
+                    }
+                    
+                    var pow = Math.Pow(powSum / classAnalysis.stuCount, 0.5);
+
+                    classAnalysis.standardDeviation = pow;
+                    classAnalysis.totalAverage = totalAverage;
+                    classAnalysis.lineCount = i;
+                }
+                //处理全科年级PR值
+                foreach (double no in analysis.total) {
+                    int index = stuTotals.IndexOf(no);
+                    int GPR = 100 - (100 * (index+1) - 50) / analysis.total.Count;
+                    analysis.gpr.Add(GPR);
+                }
+                List<GradeAnalysis> gradeAnalyses = new List<GradeAnalysis>();
+                if (info.grades.Count > 0) {
+                    gradeAnalyses = GetGradeAnalyses(info, examResults, examClassResults, analysis);
+                }
+                return Ok(new { period=info.period, grades=info.grades, subjects=info.subjects, classes , analysis, classAnalyses, gradeAnalyses });
+            } catch (Exception e) {
+                await _dingDing.SendBotMsg($"OS,{_option.Location},analysis/getAnalysis()\n{e.Message}", GroupNames.醍摩豆服務運維群組);
+                return BadRequest();
+            }
+
+        
+        }
+        private List<GradeAnalysis> GetGradeAnalyses(ExamInfo info, List<ExamResult> examResults, List<ExamClassResult> examClassResults,StudentAnalysis analysis) {
+            List<GradeAnalysis> gradeAnalyses = new List<GradeAnalysis>();
+           
+            info.grades.ForEach(g => {
+                GradeAnalysis gradeAnalysis = new GradeAnalysis();
+                gradeAnalysis.gradeId = g.id;
+                //初始化单个年级总分
+                double gradeScore = 0;
+                info.subjects.ForEach(sub =>
+                {
+                    //初始化及格人数
+                    int i = 0;
+                    //初始化年级人数
+                    int count = 0;
+                    //初始化单科总分
+                    double sumClass = 0;
+                    //记录每个科目数据
+                    GradeSubjectScore subjectScore = new GradeSubjectScore();
+                    subjectScore.id = sub.id;
+                    double subjectSum = 0;
+                    double passScore = 0;
+                    //计算单科评测总分
+                    foreach (ExamResult examResult in examResults)
+                    {
+                        if (sub.id.Equals(examResult.subjectId))
+                        {
+                            subjectSum = examResult.paper.point.Sum();
+                        }
+                    }
+                    //单科及格率分数
+                    passScore = subjectSum * 0.6;
+                    List<double> subjectTotal = new List<double>();
+                    //存放单科每个班级学生总分
+                    List<double> totalClass = new List<double>();
+                    //存放学生单科记录
+                    foreach (Score score in analysis.scores) {
+                        if (score.subjectId.Equals(sub.id)) {
+                            //处理每个班级各个学生各科总分
+                            foreach (ExamClassResult result in examClassResults)
+                            {
+                                if (sub.id.Equals(result.subjectId) && g.id.Equals(result.gradeId))
+                                {
+                                    foreach (List<double> scores in result.studentScores)
+                                    {
+                                        //添加每个学生各科总分
+                                        score.gsc.Add(scores.Sum());
+                                        //计算大于及格分数的人数
+                                        if (scores.Sum() >= passScore)
+                                        {
+                                            i++;
+                                        }
+                                        subjectTotal.Add(scores.Sum());
+                                        sumClass = sumClass + scores.Sum();
+
+                                    }
+                                    count += result.studentIds.Count;
+                                }
+                            }
+                            //处理班级及格率以及均分
+                            double persentClass = i / (count*1.0);
+                            double averageClass = sumClass / (count * 1.0);
+                            gradeScore += averageClass;
+                            subjectScore.passPercent= persentClass;
+                            subjectScore.averageScore= averageClass;
+                            gradeAnalysis.subjects.Add(subjectScore);
+                            gradeAnalyses.Add(gradeAnalysis);
+                            //处理单科科年级PR
+                            subjectTotal.Sort(delegate (double s1, double s2) { return s2.CompareTo(s1); });
+                            foreach (double sc in score.gsc)
+                            {
+                                int index = subjectTotal.IndexOf(sc);
+                                double GPR = 100 - (100 * (index + 1) - 50) / score.gsc.Count;
+                                score.gdpr.Add(GPR);
+                                //analysis.gpr.Add(GPR);
+                            }
+                            //analysis.scores.Add(score);
+                        }
+                    }                                                  
+                });
+                foreach (GradeAnalysis grade in gradeAnalyses) {
+                    grade.GradeaverageScore = gradeScore / grade.subjects.Count;
+                }
+                });
+            return gradeAnalyses;
+            }
     }
 }