Преглед изворни кода

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

jeff пре 4 година
родитељ
комит
605a135e24
65 измењених фајлова са 2839 додато и 1603 уклоњено
  1. 2 2
      TEAMModelOS.SDK/Models/Cosmos/Common/Inner/Repair.cs
  2. 1 1
      TEAMModelOS/ClientApp/package.json
  3. 23 1
      TEAMModelOS/ClientApp/src/api/learnActivity.js
  4. 18 12
      TEAMModelOS/ClientApp/src/api/teachContent.js
  5. 11 7
      TEAMModelOS/ClientApp/src/api/uploadFile.js
  6. 243 224
      TEAMModelOS/ClientApp/src/common/BaseMyCanvas.vue
  7. 311 250
      TEAMModelOS/ClientApp/src/common/NewUploadFile.vue
  8. 0 1
      TEAMModelOS/ClientApp/src/components/evaluation/ExerciseList.vue
  9. 3 1
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseKnowledgeDetail.vue
  10. 2 1
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseKnowledgeRadar.vue
  11. 27 17
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseLevelPie.vue
  12. 11 1
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseLevelRadar.vue
  13. 4 2
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseMyTable.vue
  14. 2 19
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BasePie.vue
  15. 1 1
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseRadar.vue
  16. 4 3
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperTest.vue
  17. 51 14
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/composePaper.vue
  18. 7 0
      TEAMModelOS/ClientApp/src/css/dark-iview-upload.less
  19. 2 2
      TEAMModelOS/ClientApp/src/css/disabled-iview-form.less
  20. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/en-US/teachContent.js
  21. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/teachContent.js
  22. 3 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/teachermgmt.js
  23. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/teachContent.js
  24. 1 1
      TEAMModelOS/ClientApp/src/static/Global.js
  25. 36 3
      TEAMModelOS/ClientApp/src/utils/editorTools.js
  26. 2 1
      TEAMModelOS/ClientApp/src/utils/evTools.js
  27. 1 0
      TEAMModelOS/ClientApp/src/utils/public.js
  28. 1 0
      TEAMModelOS/ClientApp/src/view/Home.vue
  29. 11 2
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseChildList.vue
  30. 16 6
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseCreateChild.vue
  31. 9 6
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseEditExercise.vue
  32. 0 5
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseRepair.vue
  33. 6 2
      TEAMModelOS/ClientApp/src/view/evaluation/index/CreateExercises.vue
  34. 6 2
      TEAMModelOS/ClientApp/src/view/knowledge-point/index/Index.vue
  35. 2 2
      TEAMModelOS/ClientApp/src/view/knowledge-point/index/operation/AddPoint.vue
  36. 1 1
      TEAMModelOS/ClientApp/src/view/knowledge-point/index/operation/ComposeBlock.vue
  37. 4 3
      TEAMModelOS/ClientApp/src/view/learnactivity/CreatePrivEva.vue
  38. 6 5
      TEAMModelOS/ClientApp/src/view/learnactivity/CreateSchoolEva.vue
  39. 264 92
      TEAMModelOS/ClientApp/src/view/learnactivity/ImportCreate.vue
  40. 1 1
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtPrivEva.less
  41. 1 0
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtPrivEva.vue
  42. 6 0
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.less
  43. 55 0
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.vue
  44. 33 22
      TEAMModelOS/ClientApp/src/view/learnactivity/PaperScore.vue
  45. 4 2
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/AchievementAnalysis/EntryTables.vue
  46. 6 1
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/KnowledgeAnalysis/KnowledgeAnalysis.css
  47. 67 61
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/KnowledgeAnalysis/KnowledgeAnalysis.vue
  48. 2 1
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/KnowledgeAnalysis/ScoreDetails.vue
  49. 31 40
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/LevelAnalysis/LevelAnalysis.vue
  50. 14 15
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/LevelAnalysis/ScoreDetails.vue
  51. 2 0
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/index.less
  52. 13 7
      TEAMModelOS/ClientApp/src/view/teachcontent/index.less
  53. 858 675
      TEAMModelOS/ClientApp/src/view/teachcontent/index.vue
  54. 1 1
      TEAMModelOS/ClientApp/src/view/teachermgmt/components/userList/Index.vue
  55. 1 9
      TEAMModelOS/ClientApp/vue.config.js
  56. 108 22
      TEAMModelOS/Controllers/Analysis/AchievementController.cs
  57. 348 13
      TEAMModelOS/Controllers/Analysis/AnalysisController.cs
  58. 1 1
      TEAMModelOS/Controllers/Common/ExamController.cs
  59. 32 8
      TEAMModelOS/Controllers/Core/BlobController.cs
  60. 2 2
      TEAMModelOS/Controllers/Core/CoreController.cs
  61. 52 20
      TEAMModelOS/Controllers/Core/ImportController.cs
  62. 29 1
      TEAMModelOS/Controllers/Item/ItemController.cs
  63. 68 2
      TEAMModelOS/Controllers/Teacher/CommentController.cs
  64. 8 4
      TEAMModelOS/Controllers/knowledge/KnowledgeController.cs
  65. 1 1
      TEAMModelOS/TEAMModelOS.csproj

+ 2 - 2
TEAMModelOS.SDK/Models/Cosmos/Common/Inner/Repair.cs

@@ -12,11 +12,11 @@ namespace TEAMModelOS.SDK.Models.Cosmos.Common.Inner
         /// <summary>
         /// 文件名字
         /// </summary>
-        public string name { get; set; }
+        //public string name { get; set; }
         /// <summary>
         /// 补救资源绝对地址
         /// </summary>
-        public string url { get; set; }
+        public string blobUrl { get; set; }
         /// 文件类型
         /// </summary>
         public string type { get; set; }

+ 1 - 1
TEAMModelOS/ClientApp/package.json

@@ -21,7 +21,7 @@
 		"@vue/eslint-config-standard": "^4.0.0",
 		"animate.css": "^3.7.2",
 		"apexcharts": "^3.20.0",
-		"axios": "^0.19.0",
+		"axios": "^0.21.1",
 		"bcryptjs": "^2.4.3",
 		"core-js": "^3.1.2",
 		"d3": "^5.9.2",

+ 23 - 1
TEAMModelOS/ClientApp/src/api/learnActivity.js

@@ -258,9 +258,31 @@ export default {
     },
 
     /*
-     * 结束评测
+     * 手动结束评测
      */
     FinishEva: function (data) {
         return post('/common/exam/finish', data)
+    },
+
+    /*
+     * 模拟学生作答数据
+     */
+    mockAnswer: function (data) {
+        return post('/analysis/answer', data)
+    },
+
+    /*
+     * 模拟教师评分数据
+     */
+    mockScoring: function (data) {
+        return post('/analysis/scoring', data)
+    },
+
+    /*
+     *修改学生答案 (批注)
+     */
+    upsertAnswer: function (data) {
+        return post('/teacher/comment/upsert-answer', data)
     }
+
 }

+ 18 - 12
TEAMModelOS/ClientApp/src/api/teachContent.js

@@ -1,15 +1,21 @@
 import { fetch, post } from '@/api/http'
 export default {
-    saveOrUpdateResource: function(data) {
-        return post('/api/Resource/upsertAll', data)
-    },
-    findResourceByDict: function(data) {
-        return post('/api/Resource/find', data)
-    },
-    deleteResource: function(data) {
-        return post('/api/Resource/delete', data)
-    },
-    FindSyllabusResourceById: function (data) {
-        return post('/api/Resource/findById', data)
-    },
+    // 由前端直接操作Blob,API已废弃
+    // saveOrUpdateResource: function(data) {
+    //     return post('/api/Resource/upsertAll', data)
+    // },
+    // findResourceByDict: function(data) {
+    //     return post('/api/Resource/find', data)
+    // },
+    // deleteResource: function(data) {
+    //     return post('/api/Resource/delete', data)
+    // },
+    // FindSyllabusResourceById: function (data) {
+    //     return post('/api/Resource/findById', data)
+    // },
+
+    // 解析文件 目前是操作PPTX
+    ParseDoc: function (data) {
+        return post('/import/parse-doc', data)
+    }
 }

+ 11 - 7
TEAMModelOS/ClientApp/src/api/uploadFile.js

@@ -1,31 +1,35 @@
-import { fetch, post } from '@/api/http'
+锘縤mport { fetch, post } from '@/api/http'
 export default {
     getBlobSAS: function(data) {
         return post('/api/File/getBlobSAS', data)
     },
-    //最开始使用的blob授权API
+    //鏈€寮€濮嬩娇鐢ㄧ殑blob鎺堟潈API
     getContainerSAS: function(data) {
         return post('/api/File/getContainerSAS', data)
     },
-    //获取blob容器读写创建
+    //鑾峰彇blob瀹瑰櫒璇诲啓鍒涘缓
     blobSasRCW: function (data) {
         return post('/blob/sas-rcwld', data)
     },
-    //获取blob容器只读创建
+    //鑾峰彇blob瀹瑰櫒鍙��鍒涘缓
     blobSasR: function(data) {
         return post('/blob/sas-r', data)
     },
-    //单文件只读权限
+    //单文件只读权限
     urlSasR: function(data) {
         return post('/blob/sas-url-r', data)
     },
-    //获取容器空间
+    //鑾峰彇瀹瑰櫒绌洪棿
     getContainerSize: function (data) {
         return post('/blob/get-blobsize', data)
     },
-	// 删除blob指定目录下的所有文件
+	// 删除blob指定目录下的所有文件
 	deletePrefix: function (data) {
         return post('/blob/delete-prefix', data)
     },
+    // 鎵归噺鍒犻櫎blob
+	deleteBlobs: function (data) {
+        return post('/blob/delete-blobs', data)
+    },
 
 }

+ 243 - 224
TEAMModelOS/ClientApp/src/common/BaseMyCanvas.vue

@@ -1,32 +1,36 @@
 <template>
-    <div id="baseCanvas">
-        <BaseCanvas class="sign-canvas" ref="SignCanvas" :options="options" v-model="value"/>
-        <div class="canvas-tools animated fadeIn">
-            <span class="canvas-tools-item" style="border-radius: 15px 0 0 0;">
+	<div id="baseCanvas" @mouseover="mouseOver" @mouseleave="mouseLeave">
+		<BaseCanvas class="sign-canvas" ref="SignCanvas" :options="options" v-model="value" />
+		<div class="canvas-tools animated fadeIn" ref="canvasTools">
+			<span class="canvas-tools-item" style="border-radius: 15px 0 0 0;">
 				<Poptip trigger="hover">
 					<div class="flex-center">
 						<Icon type="md-brush" />
 						<span>粗细</span>
 					</div>
-				   <div slot="content" class="widthDotBox">
-					   <span class="widthDot widthDot1" :style="{ backgroundColor: (activeDot === 5 ? 'red' : '#827a7a')}" @click="onSelectWriteWidth(5,$event)"></span>
-					   <span class="widthDot widthDot2" :style="{ backgroundColor: (activeDot === 10 ? 'red' : '#827a7a')}"  @click="onSelectWriteWidth(10,$event)"></span>
-					   <span class="widthDot widthDot3" :style="{ backgroundColor: (activeDot === 15 ? 'red' : '#827a7a')}"  @click="onSelectWriteWidth(15,$event)"></span>
-					   <span class="widthDot widthDot4" :style="{ backgroundColor: (activeDot === 20 ? 'red' : '#827a7a')}"  @click="onSelectWriteWidth(20,$event)"></span>
-				   </div>
+					<div slot="content" class="widthDotBox">
+						<span class="widthDot widthDot5" :style="{ backgroundColor: (activeDot === 2 ? 'red' : '#827a7a')}" @click="onSelectWriteWidth(2,$event)"></span>
+						<span class="widthDot widthDot1" :style="{ backgroundColor: (activeDot === 5 ? 'red' : '#827a7a')}" @click="onSelectWriteWidth(5,$event)"></span>
+						<span class="widthDot widthDot2" :style="{ backgroundColor: (activeDot === 10 ? 'red' : '#827a7a')}" @click="onSelectWriteWidth(10,$event)"></span>
+						<span class="widthDot widthDot3" :style="{ backgroundColor: (activeDot === 15 ? 'red' : '#827a7a')}" @click="onSelectWriteWidth(15,$event)"></span>
+						<span class="widthDot widthDot4" :style="{ backgroundColor: (activeDot === 20 ? 'red' : '#827a7a')}" @click="onSelectWriteWidth(20,$event)"></span>
+					</div>
 				</Poptip>
 			</span>
 			<span class="canvas-tools-item" v-if="!isStudent">
 				<Poptip trigger="hover">
 					<div class="flex-center">
-						<Icon type="md-color-palette" :color="activeColor"/>
+						<Icon type="md-color-palette" :color="activeColor" />
 						<span>画笔颜色</span>
 					</div>
-				   <div slot="content" class="widthDotBox">
-					   <span class="color-item color-item-black" :style="{ borderColor: (activeColor === 'black' ? 'black' : 'transparent')}" @click="onSelectWriteColor('black',$event)"></span>
-					   <span class="color-item color-item-blue" :style="{ borderColor: (activeColor === 'blue' ? 'black' : 'transparent')}"  @click="onSelectWriteColor('blue',$event)"></span>
-					   <span class="color-item color-item-red" :style="{ borderColor: (activeColor === 'red' ? 'black' : 'transparent')}"  @click="onSelectWriteColor('red',$event)"></span>
-				   </div>
+					<div slot="content" class="widthDotBox">
+						<span class="color-item color-item-black" :style="{ borderColor: (activeColor === 'black' ? 'black' : 'transparent')}"
+						 @click="onSelectWriteColor('black',$event)"></span>
+						<span class="color-item color-item-blue" :style="{ borderColor: (activeColor === 'blue' ? 'black' : 'transparent')}"
+						 @click="onSelectWriteColor('blue',$event)"></span>
+						<span class="color-item color-item-red" :style="{ borderColor: (activeColor === 'red' ? 'black' : 'transparent')}"
+						 @click="onSelectWriteColor('red',$event)"></span>
+					</div>
 				</Poptip>
 			</span>
 			<span class="canvas-tools-item" @click="canvasClear()">
@@ -37,241 +41,256 @@
 				<Icon type="md-close" />
 				<span>关闭</span>
 			</span>
-			<span class="canvas-tools-item" @click="downloadSignImg()"  style="border-radius:  0 15px 0 0 ;">
+			<span class="canvas-tools-item" @click="downloadSignImg()" style="border-radius:  0 15px 0 0 ;">
 				<Icon type="md-cloud-download" />
 				<span>保存</span>
 			</span>
-        </div>
-    </div>
+		</div>
+	</div>
 </template>
 <script>
-import BaseCanvas from './BaseCanvas.vue';
-export default {
-    components:{BaseCanvas},
-	props:{
-		isStudent:{
-			type:Boolean,
-			default:false
-		},
-		bgImg:{
-			type:String,
-			default:''
-		},
-		writeColor:{
-			type:String,
-			default:'black'
-		}
-	},
-    data(){
-        return {
-            value: null,
-			activeDot:5,
-			activeColor:'black',
-            options:{
-                isDpr: false,       //是否使用dpr兼容高分屏 [Boolean] 可选
-                lastWriteSpeed: 1,  //书写速度 [Number] 可选
-                lastWriteWidth: 2,  //下笔的宽度 [Number] 可选
-                lineCap: 'round',   //线条的边缘类型 [butt]平直的边缘 [round]圆形线帽 [square]	正方形线帽
-                lineJoin: 'bevel',  //线条交汇时边角的类型  [bevel]创建斜角 [round]创建圆角 [miter]创建尖角。
-                canvasWidth: 1200, //canvas宽高 [Number] 可选
-                canvasHeight: 480,  //高度  [Number] 可选
-                isShowBorder: true, //是否显示边框 [可选]
-                bgColor: '#ebebeb', //背景色 [String] 可选
-                borderWidth: 2, // 网格线宽度  [Number] 可选
-                borderColor: "#7c7c7c", //网格颜色  [String] 可选
-                writeWidth: 5, //基础轨迹宽度  [Number] 可选
-                writeColor: '#000', // 轨迹颜色  [String] 可选
-                isSign: true, //签名模式 [Boolean] 默认为非签名模式,有线框, 当设置为true的时候没有任何线框
-                imgType:'png'   //下载的图片格式  [String] 可选为 jpeg  canvas本是透明背景的
-            }
-        }
-    },
-	created() {
-		this.initBgImg()
-	},
-    methods:{
-		onChangeWriteColor(val){
-			this.options.writeColor = val
-		},
-		
-		onSelectWriteColor(val){
-			console.log(val)
-			this.options.writeColor = val
-			this.activeColor = val
+	import BaseCanvas from './BaseCanvas.vue';
+	export default {
+		components: {
+			BaseCanvas
 		},
-		
-		onChangeBgColor(val){
-			this.options.bgColor = val
-		},
-		
-		onSelectWriteWidth(val){
-			console.log(val)
-			this.options.writeWidth = val
-			this.activeDot = val
+		props: {
+			isStudent: {
+				type: Boolean,
+				default: false
+			},
+			bgImg: {
+				type: String,
+				default: ''
+			},
+			writeColor: {
+				type: String,
+				default: 'black'
+			}
 		},
-		
-		closeModal(){
-			this.$emit('onCloseModal')
+		data() {
+			return {
+				value: null,
+				activeDot: 5,
+				activeColor: 'black',
+				options: {
+					isDpr: false, //是否使用dpr兼容高分屏 [Boolean] 可选
+					lastWriteSpeed: 1, //书写速度 [Number] 可选
+					lastWriteWidth: 2, //下笔的宽度 [Number] 可选
+					lineCap: 'round', //线条的边缘类型 [butt]平直的边缘 [round]圆形线帽 [square]	正方形线帽
+					lineJoin: 'bevel', //线条交汇时边角的类型  [bevel]创建斜角 [round]创建圆角 [miter]创建尖角。
+					canvasWidth: 1200, //canvas宽高 [Number] 可选
+					canvasHeight: 480, //高度  [Number] 可选
+					isShowBorder: true, //是否显示边框 [可选]
+					bgColor: '#ebebeb', //背景色 [String] 可选
+					borderWidth: 2, // 网格线宽度  [Number] 可选
+					borderColor: "#7c7c7c", //网格颜色  [String] 可选
+					writeWidth: 5, //基础轨迹宽度  [Number] 可选
+					writeColor: '#000', // 轨迹颜色  [String] 可选
+					isSign: true, //签名模式 [Boolean] 默认为非签名模式,有线框, 当设置为true的时候没有任何线框
+					imgType: 'png' //下载的图片格式  [String] 可选为 jpeg  canvas本是透明背景的
+				}
+			}
 		},
-        /**
-         * 清除画板
-         */
-        canvasClear(){
-            this.$refs.SignCanvas.canvasClear();
+		created() {
 			this.initBgImg()
-        },
-		
-		initBgImg(){
-			if(this.bgImg){
-				this.$nextTick(() => {
-					var img = new Image()
-					img.setAttribute('crossOrigin', 'anonymous');
-					img.src = this.bgImg
-					img.onload = () => {
-						let renderWidth = img.width > 1200 ? 1200 : img.width
-						let renderHeight = renderWidth * (img.height / img.width)
-						this.options.canvasWidth = renderWidth
-						this.options.canvasHeight = renderHeight
-						setTimeout(() => {
-						    this.$refs.SignCanvas.context.drawImage(img,0,0,img.width,img.height,0,0,renderWidth,renderHeight);
-						},10)
-					}
-				})
-			}
 		},
+		methods: {
+			// 移入
+			mouseOver() {
+				this.$refs.canvasTools.style.display = 'flex'
+			},
+			// 移出
+			mouseLeave() {
+				this.$refs.canvasTools.style.display = 'none'
+			},
+			onChangeWriteColor(val) {
+				this.options.writeColor = val
+			},
+
+			onSelectWriteColor(val) {
+				console.log(val)
+				this.options.writeColor = val
+				this.activeColor = val
+			},
+
+			onChangeBgColor(val) {
+				this.options.bgColor = val
+			},
+
+			onSelectWriteWidth(val) {
+				console.log(val)
+				this.options.writeWidth = val
+				this.activeDot = val
+			},
+
+			closeModal() {
+				this.$emit('onCloseModal')
+			},
+			/**
+			 * 清除画板
+			 */
+			canvasClear() {
+				this.$refs.SignCanvas.canvasClear();
+				this.initBgImg()
+			},
 
-        /**
-         * 下载图片
-         */
-        async downloadSignImg(){
-			const img = await this.$refs.SignCanvas.saveAsImg();
-			this.$emit('onSaveCanvas',img)
-        },
+			initBgImg() {
+				if (this.bgImg) {
+					this.$nextTick(() => {
+						var img = new Image()
+						img.setAttribute('crossOrigin', 'anonymous');
+						img.src = this.bgImg
+						img.onload = () => {
+							let renderWidth = img.width > 1200 ? 1200 : img.width
+							let renderHeight = renderWidth * (img.height / img.width)
+							this.options.canvasWidth = renderWidth
+							this.options.canvasHeight = renderHeight
+							setTimeout(() => {
+								this.$refs.SignCanvas.context.drawImage(img, 0, 0, img.width, img.height, 0, 0, renderWidth, renderHeight);
+							}, 10)
+						}
+					})
+				}
+			},
 
-    },
-	mounted() {
-		this.activeColor = this.writeColor || 'black'
-		/* 如果有传背景图片 则接收后绘制作为Canvas的背景 */
-		
-	},
-	watch:{
-		writeColor:{
-			handler(n,o){
-				this.onSelectWriteColor(n)
+			/**
+			 * 下载图片
+			 */
+			async downloadSignImg() {
+				const img = await this.$refs.SignCanvas.saveAsImg();
+				this.$emit('onSaveCanvas', img)
 			},
-			immediate:true
+
+		},
+		mounted() {
+			this.activeColor = this.writeColor || 'black'
+			/* 如果有传背景图片 则接收后绘制作为Canvas的背景 */
+
+		},
+		watch: {
+			writeColor: {
+				handler(n, o) {
+					this.onSelectWriteColor(n)
+				},
+				immediate: true
+			}
 		}
 	}
-}
 </script>
 <style lang="less" scoped>
-* {
-    margin: 0;
-    padding: 0;
-}
+	* {
+		margin: 0;
+		padding: 0;
+	}
+
+	#baseCanvas {
+		position: relative;
+	}
 
-#baseCanvas{
-	position: relative;
-}
-.sign-canvas{
-    display: block;
-    margin: 0 auto;
-}
+	.sign-canvas {
+		display: block;
+		margin: 0 auto;
+	}
 
-.canvas-tools{
-    position: absolute;
-	bottom: 0;
-	left: 50%;
-	transform:translate(-50%, 0);
-	display: flex;
-	background-color: #696969;
-	border-radius: 15px 15px 0 0;
-	
-	&-item{
-		display: flex;
-		flex-direction: column;
-		justify-content: center;
-		align-items: center;
-		color: #fff;
-		padding: 10px 20px;
-		font-size: 10px;
-		cursor: pointer;
-		
-		&:hover{
-			background-color: rgba(255,255,255,.6);
-			color: #000;
-		}
-		
-		.ivu-icon{
-			font-size: 20px;
-			margin-bottom: 5px;
-		}
-		
-		.ivu-poptip-title{
-			display: none;
-		}
-		
-		.widthDotBox{
+	.canvas-tools {
+		position: absolute;
+		bottom: 0;
+		left: 50%;
+		transform: translate(-50%, 0);
+		display: none;
+		background-color: #696969;
+		border-radius: 15px 15px 0 0;
+
+		&-item {
 			display: flex;
-			flex-direction: row;
-			justify-content: space-around;
+			flex-direction: column;
+			justify-content: center;
 			align-items: center;
-		}
-		
-		.widthDot{
-			display: inline-block;
-			width: 10px;
-			height: 10px;
-			border-radius: 50%;
-			background: #827a7a;
-			position: relative;
-			
-			
-			&2{
-				width: 15px;
-				height: 15px;
-			}
-			
-			&3{
-				width: 20px;
-				height: 20px;
+			color: #fff;
+			padding: 10px 20px;
+			font-size: 10px;
+			cursor: pointer;
+
+			&:hover {
+				background-color: rgba(255, 255, 255, .6);
+				color: #000;
 			}
-			&4{
-				width: 25px;
-				height: 25px;
+
+			.ivu-icon {
+				font-size: 20px;
+				margin-bottom: 5px;
 			}
-			&5{
-				width: 30px;
-				height: 30px;
+
+			.ivu-poptip-title {
+				display: none;
 			}
-		}
-		
-		.color-item{
-			display: inline-block;
-			width: 30px;
-			height: 20px;
-			background: #827a7a;
-			position: relative;
-			border-radius: 5px;
-			border: 2px solid transparent;
-			
-			&-black{
-				background-color: #000;
+
+			.widthDotBox {
+				display: flex;
+				flex-direction: row;
+				justify-content: space-around;
+				align-items: center;
 			}
-			&-red{
-				background-color: #ff0000;
+
+			.widthDot {
+				display: inline-block;
+				width: 10px;
+				height: 10px;
+				border-radius: 50%;
+				background: #827a7a;
+				position: relative;
+
+
+				&2 {
+					width: 15px;
+					height: 15px;
+				}
+
+				&3 {
+					width: 20px;
+					height: 20px;
+				}
+
+				&4 {
+					width: 25px;
+					height: 25px;
+				}
+
+				&5 {
+					width: 5px;
+					height: 5px;
+				}
 			}
-			&-blue{
-				background-color: blue;
+
+			.color-item {
+				display: inline-block;
+				width: 30px;
+				height: 20px;
+				background: #827a7a;
+				position: relative;
+				border-radius: 5px;
+				border: 2px solid transparent;
+
+				&-black {
+					background-color: #000;
+				}
+
+				&-red {
+					background-color: #ff0000;
+				}
+
+				&-blue {
+					background-color: blue;
+				}
 			}
 		}
+
+		.flex-center {
+			display: flex;
+			flex-direction: column;
+			justify-content: center;
+			align-items: center;
+		}
 	}
-	
-	.flex-center{
-		display: flex;
-		flex-direction: column;
-		justify-content: center;
-		align-items: center;
-	}
-}
 </style>

+ 311 - 250
TEAMModelOS/ClientApp/src/common/NewUploadFile.vue

@@ -1,6 +1,6 @@
 <template>
-    <div class="upload-file-box"> 
-        <Upload type="drag" action="" :show-upload-list="false"	multiple :before-upload="customUpload" class="upload-wrap">
+    <div class="upload-file-box">
+        <Upload type="drag" action="" :show-upload-list="false" multiple :before-upload="customUpload" class="upload-wrap">
             <Icon class="upload-icon" custom="iconfont icon-upload" />
             <p class="upload-text">{{$t('teachContent.uploadText')}}</p>
             <p class="upload-text" style="font-size:12px;margin-bottom:50px;">* 相同名字的文件上传会自动覆盖</p>
@@ -23,284 +23,345 @@
                 <span style="float: right;font-size: 12px;vertical-align: middle;line-height: 30px;">
                     {{getSizeByBytes(item.loadedBytes)+'/'+getSizeByBytes(item.size)}}
                 </span>
-                <Progress style="top:-10px;" v-if="item.status == 0" :percent="item.loadedBytes * 100 / item.size" status="active" stroke-color="#1CC0F3" :stroke-width="2" hide-info/>
+                <Progress style="top:-10px;" v-if="item.status == 0" :percent="item.loadedBytes * 100 / item.size" status="active" stroke-color="#1CC0F3" :stroke-width="2" hide-info />
             </div>
         </div>
     </div>
 </template>
 <script>
-    import '@/icons/svg/loading3.svg';
-    import BlobTool from '@/utils/blobTool.js';
-    export default {
-        data() {
-            return {
-                uploadedList: [],
-                containerClient: null
-            }
-        },
-        props: {
-            //默认文件列表
-            defaultFileList: {
-                default: () => {
-                    return []
-                },
-                type: Array
-            },
-            //文件路径
-            path: {
-                default: '',
-                type: String,
-                required: true
-            },
-            //授权信息
-            sasString:{
-                default: '',
-                type: String,
-                required: true
-            },
-            //blob链接
-            urlString:{
-                default: '',
-                type: String,
-                required: true
-            },
-            //容器名称
-            containerName:{
-                default: '',
-                type: String,
-                required: true
-            },
-            //文件大小限制
-            maxSize: {
-                default: 2 * 1024 * 1024 * 1024,
-                type: Number
+import '@/icons/svg/loading3.svg';
+import BlobTool from '@/utils/blobTool.js';
+export default {
+    data() {
+        return {
+            uploadedList: [],
+            containerClient: null,
+            routerScope: ''
+        }
+    },
+    props: {
+        //默认文件列表
+        defaultFileList: {
+            default: () => {
+                return []
             },
-            //文件格式限制
-            format: {
-                default: () => {
-                    return []
-                },
-                type: Array
-            }
+            type: Array
         },
-        methods: {
-            //删除文件
-            deleteFile(index) {
-                this.containerClient.deleteBlob(this.uploadedList[index].blob).then(
-                    res => {
-                        //删除缩略图和封面
-                        if (this.uploadedList[index].type == 'image') {
-                            let thum = this.uploadedList[index].blob.replace('/image/', '/thum/')
-                            this.containerClient.deleteBlob(thum)
-                        } else if (this.uploadedList[index].type == 'video') {
-                            let thum = this.uploadedList[index].blob.replace('/video/', '/thum/')
-                            thum = thum.slice(0, thum.lastIndexOf('.')) + '.png'
-                            this.containerClient.deleteBlob(thum)
-                        }
-                        this.$emit('deleteFile', this._.cloneDeep(this.uploadedList[index]))
-                        this.uploadedList.splice(index,1)
-                        this.$Message.success('删除成功!')
-                    },
-                    err => {
-                        this.$Message.error('删除失败!')
-                    }
-                )
+        //文件路径
+        path: {
+            default: '',
+            type: String,
+            required: true
+        },
+        //授权信息
+        sasString: {
+            default: '',
+            type: String,
+            required: true
+        },
+        //blob链接
+        urlString: {
+            default: '',
+            type: String,
+            required: true
+        },
+        //容器名称
+        containerName: {
+            default: '',
+            type: String,
+            required: true
+        },
+        //文件大小限制
+        maxSize: {
+            default: 2 * 1024 * 1024 * 1024,
+            type: Number
+        },
+        //文件格式限制
+        format: {
+            default: () => {
+                return []
             },
-            getFileType(fileName) {
-                let extension = fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length)
-                extension = extension.toUpperCase()
-                for (let key in this.$GLOBAL.CONTENT_TYPES) {
-                    if (this.$GLOBAL.CONTENT_TYPES[key].indexOf(extension) != -1) {
-                        return key
+            type: Array
+        }
+    },
+    methods: {
+        //删除文件
+        deleteFile(index) {
+            this.containerClient.deleteBlob(this.uploadedList[index].blob).then(
+                res => {
+                    //删除缩略图和封面
+                    if (this.uploadedList[index].type == 'image') {
+                        let thum = this.uploadedList[index].blob.replace('/image/', '/thum/')
+                        this.containerClient.deleteBlob(thum)
+                    } else if (this.uploadedList[index].type == 'video') {
+                        let thum = this.uploadedList[index].blob.replace('/video/', '/thum/')
+                        thum = thum.slice(0, thum.lastIndexOf('.')) + '.png'
+                        this.containerClient.deleteBlob(thum)
                     }
+                    this.$emit('deleteFile', this._.cloneDeep(this.uploadedList[index]))
+                    this.uploadedList.splice(index, 1)
+                    this.$Message.success('删除成功!')
+                },
+                err => {
+                    this.$Message.error('删除失败!')
                 }
-                return 'other'
-            },
-            getSizeByBytes(bytes) {
-                return bytes / 1024 < 1024 ? (bytes / 1024).toFixed(1) + 'KB' : bytes / 1024 / 1024 < 1024 ? (bytes / 1024 / 1024).toFixed(1) + 'M' : (bytes / 1024 / 1024 / 1024).toFixed(1) + 'G'
-            },
-            async customUpload(file) {
-                if (file.size > this.maxSize) {
-                    this.$Message.error({
-                        content: "文件大小超出限制",
-                        duration: 3
-                    })
-                    return false
+            )
+        },
+        getFileType(fileName) {
+            let extension = fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length)
+            extension = extension.toUpperCase()
+            for (let key in this.$GLOBAL.CONTENT_TYPES) {
+                if (this.$GLOBAL.CONTENT_TYPES[key].indexOf(extension) != -1) {
+                    return key
                 }
+            }
+            return 'other'
+        },
+        getSizeByBytes(bytes) {
+            return bytes / 1024 < 1024 ? (bytes / 1024).toFixed(1) + 'KB' : bytes / 1024 / 1024 < 1024 ? (bytes / 1024 / 1024).toFixed(1) + 'M' : (bytes / 1024 / 1024 / 1024).toFixed(1) + 'G'
+        },
+        async customUpload(file) {
+            if (file.size > this.maxSize) {
+                this.$Message.error({
+                    content: "文件大小超出限制",
+                    duration: 3
+                })
+                return false
+            }
 
-                let extension = file.name.substring(file.name.lastIndexOf('.') + 1, file.name.length)
-                if (this.format.length > 0 && this.format.indexOf(extension) == -1) {
-                    this.$Message.error({
-                        content: "文件类型不支持",
-                        duration: 3
-                    })
-                    return false
-                }
-                
-                let fileType = this.getFileType(file.name)
-                //上传文件
-                let fileInfo = {
-                    url: this.urlString + '/' + this.containerName + '/' + fileType + '/' + file.name,
-                    name: file.name,
-                    size: file.size,
-                    loadedBytes: 0,
-                    progress: 0,
-                    status: 0,
-                    type: fileType
+            let extension = file.name.substring(file.name.lastIndexOf('.') + 1, file.name.length)
+            if (this.format.length > 0 && this.format.indexOf(extension) == -1) {
+                this.$Message.error({
+                    content: "文件类型不支持",
+                    duration: 3
+                })
+                return false
+            }
+
+            let fileType = this.getFileType(file.name)
+            //上传文件
+            let fileInfo = {
+                url: this.urlString + '/' + this.containerName + '/' + fileType + '/' + file.name,
+                name: file.name,
+                size: file.size,
+                loadedBytes: 0,
+                progress: 0,
+                status: 0,
+                type: fileType
+            }
+            this.uploadedList.push(fileInfo)
+            let _this = this
+            let index = this.uploadedList.length - 1
+            this.containerClient.upload(file, fileType, {
+                onProgress: function (e) {
+                    _this.uploadedList[index].loadedBytes = e.loadedBytes
+                    _this.uploadedList[index].progress = parseInt(e.loadedBytes * 100 / file.size)
                 }
-                this.uploadedList.push(fileInfo)
-                let _this = this
-                let index = this.uploadedList.length - 1
-                this.containerClient.upload(file, fileType, {
-                    onProgress: function (e) {
-                        _this.uploadedList[index].loadedBytes = e.loadedBytes
-                        _this.uploadedList[index].progress = parseInt(e.loadedBytes * 100 / file.size)
-                    }
-                }).then(
-                    res => {
-                        this.uploadedList[index].status = 1
-                        this.uploadedList[index].blob = res.blob
+            }).then(
+                res => {
+                    this.uploadedList[index].status = 1
+                    this.uploadedList[index].blob = res.blob
+                    extension = extension.toUpperCase()
+                    if (res.type == 'image') {
+                        this.uploadThum(file)
                         this.$emit('successData', res)
-                        if (res.type == 'image') {
-                            this.uploadThum(file)
-                        } else if (res.type == 'video'){
-                            this.uploadPoater(res)
-                        }
-                    },
-                    err => {
-                        this.uploadedList[index].status = 2
-                        if (err.spaceError) {
-                            this.$Message.error(err.spaceError)
-                        } else {
-                            this.$Message.error("上传失败")
-                        }
-                    }
-                )
-                return false
-            },
-            //处理图片缩略图
-            async uploadThum(file) {
-                let dataUrl = await this.$jsFn.fileToURL(file)
-                dataUrl = await this.$jsFn.compressImgByUrl(dataUrl, file.name, 0.1)
-                let f = this.$jsFn.dataURLtoFile(dataUrl, file.name)
-                this.containerClient.upload(f, 'thum').then(
-                    res => {
-                        console.log('压缩图上传成功')
-                    },
-                    err => {
-                        console.log('压缩图上传失败')
+                    } else if (res.type == 'video') {
+                        this.uploadPoater(res)
+                        this.$emit('successData', res)
+                    } else if (extension == 'PPTX') {
+                        this.$Modal.confirm({
+                            title: '文件解析',
+                            okText: '是',
+                            cancelText: '否',
+                            content: `<p>是否将<strong style='color:red;'>${file.name}</strong>转成HTEX文件?</p>`,
+                            onOk: () => {
+                                this.$emit('parsing')
+                                let delBlob = res.blob
+                                this.$api.teachContent.ParseDoc({
+                                    file: res.url,
+                                    scope: this.routerScope
+                                }).then(
+                                    parseRes => {
+                                        this.containerClient.deleteBlob(delBlob)
+                                        res.blob = `/res/${res.name}/index.json`
+                                        res.extension = 'HTEX'
+                                        res.name = res.name.replace('pptx', 'HTEX').replace('PPTX', 'HTEX')
+                                        res.type = 'res'
+                                        this.$emit('successData', res.blob)
+                                        this.$emit('parseComplete')
+                                        this.$Message.success('解析成功')
+                                    },
+                                    parseErr => {
+                                        this.$Message.error('解析失败')
+                                    }
+                                )
+                            },
+                            onCancel: () => {
+                                this.$emit('successData', res)
+                            }
+                        })
+                    } else if (extension == 'HTEX') {
+                        let delBlob = res.blob
+                        this.$emit('parsing')
+                        res.blob = `/res/${res.name}/index.json`
+                        this.$emit('successData', res)
+                        this.$api.teachContent.ParseDoc({
+                            file: res.url,
+                            scope: this.routerScope
+                        }).then(
+                            parseRes => {
+                                this.containerClient.deleteBlob(delBlob)
+                            },
+                            parseErr => {
+                                this.$Message.error('上传失败')
+                                this.containerClient.deleteBlob(delBlob)
+                            }
+                        ).finally(() => {
+                            this.$emit('parseComplete')
+                        })
                     }
-                )
-            },
-            //处理视频封面
-            async uploadPoater(file) {
-                let n = file.name.substring(0, file.name.lastIndexOf('.'))
-                console.log(n)
-                console.log('视频蜂蜜昂')
-                let dataUrl = await this.$jsFn.createVideoPoster(file.url + this.sasString, file.name, 0.1)
-                let f = this.$jsFn.dataURLtoFile(dataUrl, n+'.png')
-                console.log(f)
-                this.containerClient.upload(f, 'thum').then(
-                    res => {
-                        console.log('视频封面上传成功')
-                    },
-                    err => {
-                        console.log('视频封面上传失败')
+                },
+                err => {
+                    this.uploadedList[index].status = 2
+                    if (err.spaceError) {
+                        this.$Message.error(err.spaceError)
+                    } else {
+                        this.$Message.error("上传失败")
                     }
-                )
-            }
+                }
+            )
+            return false
+        },
+        //处理图片缩略图
+        async uploadThum(file) {
+            let dataUrl = await this.$jsFn.fileToURL(file)
+            dataUrl = await this.$jsFn.compressImgByUrl(dataUrl, file.name, 0.1)
+            let f = this.$jsFn.dataURLtoFile(dataUrl, file.name)
+            this.containerClient.upload(f, 'thum').then(
+                res => {
+                    console.log('压缩图上传成功')
+                },
+                err => {
+                    console.log('压缩图上传失败')
+                }
+            )
         },
-        watch: {
-            urlString: {
-                handler(v, o){
-                    if (this.urlString && this.containerName && this.sasString) {
-                        let scope = ''
-                        if (this.containerName == this.$store.state.userInfo.TEAMModelId) {
-                            scope = 'private'
-                        } else if (this.containerName == this.$store.state.userInfo.schoolCode){
-                            scope = 'school'
-                        }
-                        //初始化Blob
-                        this.containerClient = new BlobTool(this.urlString, this.containerName, this.sasString, scope)
+        //处理视频封面
+        async uploadPoater(file) {
+            let n = file.name.substring(0, file.name.lastIndexOf('.'))
+            console.log(n)
+            console.log('视频蜂蜜昂')
+            let dataUrl = await this.$jsFn.createVideoPoster(file.url + this.sasString, file.name, 0.1)
+            let f = this.$jsFn.dataURLtoFile(dataUrl, n + '.png')
+            console.log(f)
+            this.containerClient.upload(f, 'thum').then(
+                res => {
+                    console.log('视频封面上传成功')
+                },
+                err => {
+                    console.log('视频封面上传失败')
+                }
+            )
+        }
+    },
+    created() {
+        let route = this.$route
+        if (route.name == 'personalcontent') {
+            this.routerScope = 'private'
+        } else {
+            this.routerScope = 'school'
+        }
+    },
+    watch: {
+        urlString: {
+            handler(v, o) {
+                if (this.urlString && this.containerName && this.sasString) {
+                    let scope = ''
+                    if (this.containerName == this.$store.state.userInfo.TEAMModelId) {
+                        scope = 'private'
+                    } else if (this.containerName == this.$store.state.userInfo.schoolCode) {
+                        scope = 'school'
                     }
+                    //初始化Blob
+                    this.containerClient = new BlobTool(this.urlString, this.containerName, this.sasString, scope)
                 }
-            },
-            containerName: {
-                handler(v, o){
-                    if (this.urlString && this.containerName && this.sasString) {
-                        let scope = ''
-                        if (this.containerName == this.$store.state.userInfo.TEAMModelId) {
-                            scope = 'private'
-                        } else if (this.containerName == this.$store.state.userInfo.schoolCode) {
-                            scope = 'school'
-                        }
-                        //初始化Blob
-                        this.containerClient = new BlobTool(this.urlString, this.containerName, this.sasString, scope)
+            }
+        },
+        containerName: {
+            handler(v, o) {
+                if (this.urlString && this.containerName && this.sasString) {
+                    let scope = ''
+                    if (this.containerName == this.$store.state.userInfo.TEAMModelId) {
+                        scope = 'private'
+                    } else if (this.containerName == this.$store.state.userInfo.schoolCode) {
+                        scope = 'school'
                     }
+                    //初始化Blob
+                    this.containerClient = new BlobTool(this.urlString, this.containerName, this.sasString, scope)
                 }
-            },
-            sasString: {
-                handler(v, o) {
-                    if (this.urlString && this.containerName && this.sasString) {
-                        let scope = ''
-                        if (this.containerName == this.$store.state.userInfo.TEAMModelId) {
-                            scope = 'private'
-                        } else if (this.containerName == this.$store.state.userInfo.schoolCode) {
-                            scope = 'school'
-                        }
-                        //初始化Blob
-                        this.containerClient = new BlobTool(this.urlString, this.containerName, this.sasString, scope)
+            }
+        },
+        sasString: {
+            handler(v, o) {
+                if (this.urlString && this.containerName && this.sasString) {
+                    let scope = ''
+                    if (this.containerName == this.$store.state.userInfo.TEAMModelId) {
+                        scope = 'private'
+                    } else if (this.containerName == this.$store.state.userInfo.schoolCode) {
+                        scope = 'school'
                     }
+                    //初始化Blob
+                    this.containerClient = new BlobTool(this.urlString, this.containerName, this.sasString, scope)
                 }
             }
         }
     }
+}
 
 </script>
 <style scoped>
-    .upload-text {
-        margin: 20px 0px;
-        font-size: 26px;
-        
-    }
-    .upload-icon {
-        font-size: 50px;
-        margin-top: 60px;
-    }
-    .upload-file-item {
-        cursor: pointer;
-        color: #909090;
-        margin-top: 5px;
-        padding:5px 2px 0px 5px;
-    }
+.upload-text {
+    margin: 20px 0px;
+    font-size: 26px;
+}
+.upload-icon {
+    font-size: 50px;
+    margin-top: 60px;
+}
+.upload-file-item {
+    cursor: pointer;
+    color: #909090;
+    margin-top: 5px;
+    padding: 5px 2px 0px 5px;
+}
 
-        .upload-file-item:hover {
-            color: white;
-            background: #585858;
-            border-radius: 5px;
-        }
+.upload-file-item:hover {
+    color: white;
+    background: #585858;
+    border-radius: 5px;
+}
 
-            .upload-file-item:hover .delete-btn {
-                display: inline-block;
-            }
-    .upload-loading-icon {
-        vertical-align:middle;
-    }
-    .upload-file-name {
-        display: inline-block;
-        color: #EEEEEE;
-        line-height: 30px;
-    }
+.upload-file-item:hover .delete-btn {
+    display: inline-block;
+}
+.upload-loading-icon {
+    vertical-align: middle;
+}
+.upload-file-name {
+    display: inline-block;
+    color: #eeeeee;
+    line-height: 30px;
+}
 
-    .delete-btn {
-        display: none;
-    }
-    .file-type-tag {
-        border: 1px solid;
-        /*border-color:white;*/
-        border-radius:4px;
-        padding:0px 4px;
-        margin-right:5px;
-        font-size:12px;
-    }
+.delete-btn {
+    display: none;
+}
+.file-type-tag {
+    border: 1px solid;
+    /*border-color:white;*/
+    border-radius: 4px;
+    padding: 0px 4px;
+    margin-right: 5px;
+    font-size: 12px;
+}
 </style>

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

@@ -213,7 +213,6 @@
 					let exerciseItemDom = e.path.filter(
 						(i) => i.className === "exercise-item"
 					);
-					console.log(exerciseItemDom)
 					if (exerciseItemDom.length) {
 						this.$emit("pageScroll", exerciseItemDom[0].offsetTop);
 					}

+ 3 - 1
TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseKnowledgeDetail.vue

@@ -218,7 +218,9 @@
         computed: {
             // 获取最新散点图数据
             getKnowledgeData() {
-                return this.echartsId === 'knowDetailBar' ? this.$store.state.totalAnalysis.knowledgeData : this.$store.state.totalAnalysis.levelData
+				let curSubjectIndex = this.$store.state.totalAnalysis.analysisJson.subjects.map(i => i.name).indexOf(this.$store.state.totalAnalysis.currentSubject)
+                return this.echartsId === 'knowDetailBar' ? this.$store.state.totalAnalysis.analysisJson.pointLevelKey[curSubjectIndex].pointKey : 
+				this.$store.state.totalAnalysis.analysisJson.pointLevelKey[curSubjectIndex].levelKey
             }
         },
         watch: {

+ 2 - 1
TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseKnowledgeRadar.vue

@@ -138,7 +138,8 @@
         computed: {
             // 获取最新散点图数据
             getKnowledgeData() {
-                return this.$store.state.totalAnalysis.knowledgeData
+                let curSubjectIndex = this.$store.state.totalAnalysis.analysisJson.subjects.map(i => i.name).indexOf(this.$store.state.totalAnalysis.currentSubject)
+                return this.$store.state.totalAnalysis.analysisJson.pointLevelKey[curSubjectIndex].pointKey
             }
         },
         watch: {

+ 27 - 17
TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseLevelPie.vue

@@ -47,7 +47,7 @@
         },
         mounted() {
             if (this.getPieData) {
-                let pointList = this.getPieData.level
+                let pointList = this.getPieData.pointList
                 let valList = this.getPieData.fper
                 let arr = []
                 pointList.forEach((item, index) => {
@@ -62,25 +62,35 @@
         computed: {
             // 获取最新知识点占比饼图数据
             getPieData() {
-                return this.$store.state.totalAnalysis.levelData
+				let curSubjectIndex = this.$store.state.totalAnalysis.analysisJson.subjects.map(i => i.name).indexOf(this.$store.state.totalAnalysis.currentSubject)
+                let levelJson = this.$store.state.totalAnalysis.analysisJson.pointLevelKey[curSubjectIndex].levelKey
+                let transArr = ["知识", "理解", "应用", "分析", "综合", "评鉴"]
+                levelJson.pointList = levelJson.pointList.map((i,index) => transArr[index])
+                for(let key in levelJson.classpercent){
+                	if(key !== 'className'){
+                		let newKey = transArr[+key - 1]
+                		levelJson.classpercent[newKey] = levelJson.classpercent[key]
+                	}
+                }
+                return levelJson
             }
         },
         watch: {
-            getPieData: {
-                deep: true,
-                handler(val) {
-                    let pointList = val.level
-                    let valList = val.fper
-                    let arr = []
-                    pointList.forEach((item, index) => {
-                        let o = {}
-                        o.name = item
-                        o.value = valList[index].replace('%', '')
-                        arr.push(o)
-                    })
-                    this.drawLine(arr)
-                }
-            }
+            // getPieData: {
+            //     deep: true,
+            //     handler(val) {
+            //         let pointList = val.level
+            //         let valList = val.fper
+            //         let arr = []
+            //         pointList.forEach((item, index) => {
+            //             let o = {}
+            //             o.name = item
+            //             o.value = valList[index].replace('%', '')
+            //             arr.push(o)
+            //         })
+            //         this.drawLine(arr)
+            //     }
+            // }
         }
 
     }

+ 11 - 1
TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseLevelRadar.vue

@@ -138,7 +138,17 @@
         computed: {
             // 获取最新散点图数据
             getKnowledgeData() {
-                return this.$store.state.totalAnalysis.levelData
+                let curSubjectIndex = this.$store.state.totalAnalysis.analysisJson.subjects.map(i => i.name).indexOf(this.$store.state.totalAnalysis.currentSubject)
+                let levelJson = this.$store.state.totalAnalysis.analysisJson.pointLevelKey[curSubjectIndex].levelKey
+                let transArr = ["知识", "理解", "应用", "分析", "综合", "评鉴"]
+                levelJson.pointList = levelJson.pointList.map((i,index) => transArr[index])
+                for(let key in levelJson.classpercent){
+                	if(key !== 'className'){
+                		let newKey = transArr[+key - 1]
+                		levelJson.classpercent[newKey] = levelJson.classpercent[key]
+                	}
+                }
+                return levelJson
             }
         },
         watch: {

+ 4 - 2
TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseMyTable.vue

@@ -336,6 +336,8 @@
             // 是否进线
             renderEntry(h, params) {
                 const row = params.row
+				const ipoint = this.$store.state.totalAnalysis.analysisJson.ipoint
+				const touchScore = this.$store.state.totalAnalysis.analysisJson.touchScore
                 return h('span', [
                     h('span', {
                         domProps: {
@@ -343,9 +345,9 @@
                         },
                         style: {
                             cursor: 'pointer',
-                            background: row.score > 200 ? '#209a31' : row.score > 180 ? '#00767d' : 'transparent'
+                            background: row.score > (ipoint + touchScore) ? '#209a31' : row.score > ipoint ? '#00767d' : 'transparent'
                         }
-                    }, row.score > 200 ? '进' : row.score > 180 ? '踩' : ''),
+                    }, row.score > (ipoint + touchScore) ? '进' : row.score > ipoint ? '踩' : ''),
                     h('span', {
                         domProps: {
                             className: 'table-rank-value'

+ 2 - 19
TEAMModelOS/ClientApp/src/components/student-analysis/total/BasePie.vue

@@ -14,24 +14,6 @@
         },
         methods: {
 
-            /**
-             * 根据知识点id集合换取知识点对象集合
-             * @param ids
-             */
-            getPointsByIds(ids) {
-                return new Promise((r, j) => {
-                    this.$api.knowledge.FindKnowledgebyId(ids).then(res => {
-                        if (res.result.data.length) {
-                            r(res.result.data)
-                        } else {
-                            r([])
-                        } 
-                    }).catch(err => {
-                        j(err)
-                    })
-                })
-
-            },
 
             drawLine(data) {
                 let that = this
@@ -87,7 +69,8 @@
         computed: {
             // 获取最新知识点占比饼图数据
             getPieData() {
-                return this.$store.state.totalAnalysis.knowledgeData
+                let curSubjectIndex = this.$store.state.totalAnalysis.analysisJson.subjects.map(i => i.name).indexOf(this.$store.state.totalAnalysis.currentSubject)
+                return this.$store.state.totalAnalysis.analysisJson.pointLevelKey[curSubjectIndex].pointKey
             }
         },
         watch: {

+ 1 - 1
TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseRadar.vue

@@ -140,7 +140,7 @@
         computed: {
             // 获取最新雷达图数据
             getKnowledgeData() {
-                return this.echartsId === 'knowRadar' ? this.$store.state.totalAnalysis.knowledgeData : this.$store.state.totalAnalysis.levelData
+                return this.echartsId === 'knowRadar' ? this.$store.state.totalAnalysis.analysisJson.pointKey : this.$store.state.totalAnalysis.analysisJson.levelKey
             }
         },
         watch: {

+ 4 - 3
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperTest.vue

@@ -78,7 +78,7 @@
                         <div class="que-item" v-if="getQue(queNo).type != 'compose' && getQue(queNo).parent == undefined">
                             <span>{{ queNo +1 }}.</span>
                             <!--题目渲染-->
-                            <div v-html="getQue(queNo).question"></div>
+                            <div id="answer-box" v-html="getQue(queNo).question"></div>
                         </div>
                         <!--综合题-->
                         <div v-if="getQue(queNo).type == 'compose' || getQue(queNo).parent != undefined">
@@ -98,7 +98,7 @@
                                 <div class="que-content">
                                     <span style="width:40px">{{ getQue(queNo).paperIndex}}:</span>
                                     <!--题目渲染-->
-                                    <div class="que-items" v-html="getQue(queNo).question"></div>
+                                    <div class="que-items" id="answer-box" v-html="getQue(queNo).question"></div>
                                 </div>
                             </div>
                         </div>
@@ -171,7 +171,7 @@
                         <Icon type="ios-arrow-back" />上一题
                     </button>
                     <button @click="nextQ()"
-                           v-show="queNo != (examInfo.length-1)"
+                            v-show="queNo != (examInfo.length-1)"
                             :class="{ hintClick:queNo != (examInfo.length-1) && checkers[queNo] != ''}">
                         下一题
                         <Icon type="ios-arrow-forward" />
@@ -189,6 +189,7 @@
             <img @click.stop="imgPreview.show = true" :src="imgPreview.img" />
         </div>
     </div>
+
 </template>
 
 <script>

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

@@ -1,9 +1,16 @@
 <template>
     <div class="content">
-      <div id="textArea"> </div>
+        <div id="textArea"> </div>
+        <Modal v-model="markStatus" fullscreen title="作答" footer-hide>
+
+            <BaseMyCanvas v-if="markStatus" :bgImg="markBg" @onCloseModal="closeModal" isStudent="markStatus" @onSaveCanvas="saveMark"></BaseMyCanvas>
+        </Modal>
+        <iframe style=" height: 0.1px; border: none;" id="answerIframe" :srcdoc="itemInfo.question"></iframe>
+
     </div>
 </template>
 <script>
+    import html2canvas from 'html2canvas';
     import E from 'wangeditor'
     export default {
         components: {
@@ -36,7 +43,9 @@
                 tabName: 'exercise',
                 editorContent: "",
                 examInfo: [],
-                editor: null
+                editor: null,
+                markStatus: false,
+                markBg:""
             }
         },
         methods: {
@@ -48,6 +57,22 @@
                     this.editor.txt.html(this.examInfo[0])
                 }
             },
+            closeModal() {
+                this.markStatus = false
+                this.markBg = ""
+            },
+            saveMark(data) {
+                if (data) {
+                    data.height = 400
+                    data.width = 400
+                    let img = document.createElement('img')
+                    img.src = data.base64
+                    console.log(img)
+                    this.editor.txt.html(img.outerHTML)
+                    this.markStatus = false
+                }
+                console.log(data)
+            },
             initEditor() {
                 this.editorContent = ""
                 this.editor = new E("#textArea");
@@ -61,20 +86,16 @@
                     "image", // 插入图片
                 ]
                 this.editor.config.zIndex = 1
-                //this.editor.config.linkImgCallback = function (url) {
-                //    //console.log(url); // url 即插入图片的地址
-                //};
-                //this.editor.config.linkCheck = function (text, link) {
-                //    //console.log(text); // 插入的文字
-                //    //console.log(link); // 插入的链接
-                //    return true; // 返回 true 表示校验成功
-                //};
                 this.editor.config.placeholder = '请输入作答结果'
                 this.editor.config.height = 300
                 this.editor.config.showLinkImg = false;
                 this.editor.config.uploadImgShowBase64 = true; // 使用 base64 保存图片不建议使用这种,我只是图个方便
-                // editor.customConfig.uploadImgServer = '/upload'  // 上传图片到服务器
-                this.$editorTools.addCanvas(this, this.editor)
+                if (this.itemInfo.type == 'correct' || this.itemInfo.type == 'connector') {
+                    this.editor.config.menus.push('connector')
+                    this.$editorTools.addStuBgBtn(this, this.editor)
+                } else {
+                    this.$editorTools.addCanvas(this, this.editor)
+                }
                 this.editor.create();
                 if (this.close) {
                     this.editor.disable()
@@ -83,15 +104,31 @@
                 if (this.examInfo.length > 0) {
                     this.editor.txt.html(this.examInfo[0])
                 } 
-            }
+            },
+            markStuAnswer() {
+                let answerIframe = document.getElementById('answerIframe')
+                let markBody = answerIframe.contentWindow.document.body
+                answerIframe.contentWindow.document.body.style.width = 'fit-content'
+                answerIframe.contentWindow.document.body.style.minWidth = '600px'
+                answerIframe.contentWindow.document.body.style.backgroundColor = '#f5f5f5'
+                html2canvas(answerIframe.contentWindow.document.body).then(canvas => {
+                    this.markStatus = true
+                    this.markBg = canvas.toDataURL("image/png");
+                })
+            },
         },
         mounted() {
             this.initEditor()
             this.getInfo()
+            // 监听富文本画板功能
+            this.$EventBus.$off('onStuCanvas')
+            this.$EventBus.$on('onStuCanvas', editor => {
+                this.markBg = ""
+                this.markStuAnswer()
+            })
         },
         watch: {
             index() {
-                console.log('试题信息',this.textData)
                 this.getInfo()
                 deep: true
                 immediate:true

+ 7 - 0
TEAMModelOS/ClientApp/src/css/dark-iview-upload.less

@@ -0,0 +1,7 @@
+.dark-iview-upload{
+    .ivu-upload-drag{
+        background: #404040;
+        border-color: #808080;
+        color: #cccccc;
+    }
+}

+ 2 - 2
TEAMModelOS/ClientApp/src/css/disabled-iview-form.less

@@ -47,7 +47,7 @@
     .ivu-input-disabled {
         border: none;
         background: none !important;
-        font-size: 16px !important;
+        font-size: 16px;
     }
 
     .ivu-disabled .ivu-selection {
@@ -67,7 +67,7 @@
     }
 }
 
-/*ÐÞ¸Äcursor*/
+/*�޸�cursor*/
 .text-cursor-disabled .ivu-select-disabled .ivu-select-selection, .text-cursor-disabled .ivu-input[disabled], fieldset[disabled] .ivu-input {
     cursor: text;
 }

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

@@ -20,7 +20,7 @@ export default {
   tips3: 'download file',
   tips4: 'preview file',
   tips5: 'bind knowledge point',
-  tips6: 'edit name',
+  tips6: 'rename',
   tips7: 'delete file',
   tips8: 'your browser does not support video tags. ',
   trops1: 'confirm deletion',

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

@@ -20,7 +20,7 @@ export default {
   tips3: '下载文件',
   tips4: '预览文件',
   tips5: '绑定知识点',
-  tips6: '编辑名称',
+  tips6: '重命名',
   tips7: '删除文件',
   tips8: '您的浏览器不支持 video 标签。',
   props1: '确认删除',

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

@@ -102,7 +102,9 @@ export default {
         'scboard-read': '查看校园分析',
         'schoolAc': '校园活动',
         'schoolAc-read': '查看校园活动',
-        'schoolAc-upd':'发布校园活动'
+        'schoolAc-upd':'发布校园活动',
+        'schoolEvaluation':'模拟评测数据',
+        'mock-eva':'一键模拟评测作答和评分数据'
     },
     modal:{
         text1: '此帐号权限已变更,是否要给此帐号更加合适的职称',

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

@@ -20,7 +20,7 @@ export default {
   tips3: '下載檔案',
   tips4: '預覽檔案',
   tips5: '綁定知識點',
-  tips6: '編輯名稱',
+  tips6: '重命名',
   tips7: '刪除檔',
   tips8: '您的瀏覽器不支持video標籤。',
   props1: '確認删除',

+ 1 - 1
TEAMModelOS/ClientApp/src/static/Global.js

@@ -4,7 +4,7 @@ const CONTENT_TYPES = {
     'image': ['JPG', 'JPEG', 'PNG', 'GIF', 'SVG'],
     'video': ['AVI', 'MP4', 'WEBM'],
     'doc': ['PPT', 'PPTX', 'DOC', 'DOCX', 'PDF', 'XLS', 'XLSX', 'CSV'],
-    'res': ['HTE', 'HETX'],
+    'res': ['HTE', 'HTEX'],
     'audio': ['MP3', 'WAV', 'OGG']
 }
 

+ 36 - 3
TEAMModelOS/ClientApp/src/utils/editorTools.js

@@ -4,6 +4,7 @@ import $tools from './public.js'
 import E from 'wangeditor'
 import createKityformula from "./kityformula";
 import myscriptMath from "./myscript-math-web";
+import { app } from '@/boot-app.js'
 import {
 	Message,
 	Modal
@@ -60,7 +61,7 @@ export default {
 			'splitLine',
 		]
 	},
-
+	
 	/* 添加自定义本地视频上传功能 */
 	addVideoUpload(vm, editor) {
 		this.addFormula(vm,editor)
@@ -368,8 +369,6 @@ export default {
 		editor.config.menus = editor.config.menus.concat(canvasDraw);
 	},
 
-	
-	
 	/* 添加公式编辑器功能 */
 	addFormula(vm,editor){
 		// 获取必要的变量,这些在下文中都会用到
@@ -408,6 +407,40 @@ export default {
 		editor.config.menus = editor.config.menus.concat(videoUpload);
 	},
 	
+	/* 添加学生端画板自定义功能 */
+	addStuBgBtn(vm,editor){
+		// 获取必要的变量,这些在下文中都会用到
+		const {
+			$,
+			PanelMenu,
+			Panel
+		} = E;
+		
+		class Kityformula extends PanelMenu {
+		      // 公式输入插件
+		      constructor(editors) {
+		        const $elem = $(
+		        	'<div class="w-e-menu" style="color:red"><i class="ivu-icon ivu-icon-logo-tumblr" style="font-size: 20px;"></i></div>'
+		        );
+		        super($elem, editors);
+		      }
+		      // 菜单点击事件
+		      clickHandler() {
+				 app.$EventBus.$emit('onStuCanvas', editor)
+		      }
+		
+		      tryChangeActive() {}
+		    }
+		
+		// 注册菜单
+		const videoUpload = "connector"; // 菜单 key ,各个菜单不能重复
+		editor.menus.extend("connector", Kityformula);
+		
+		// 将菜单加入到 editor.config.menus 中
+		// 也可以通过配置 menus 调整菜单的顺序,参考【配置菜单】部分的文档
+		//editor.config.menus = editor.config.menus.concat(videoUpload);
+	},
+	
 	/* 移除上传的音视频富文本src中的HOST */
 	doRemoveMideaHost(exerciseItem) {
 		console.log(exerciseItem)

+ 2 - 1
TEAMModelOS/ClientApp/src/utils/evTools.js

@@ -25,7 +25,7 @@ export default {
 					children:item.children || [],
 					scope:item.scope,
 					score: 0,
-					repairResource:item.repairResource,
+					repair:item.repair,
 				},
 				render:2,
 				item:[{
@@ -54,6 +54,7 @@ export default {
 				periodId:item.periodId,
 				gradeIds:item.gradeIds,
 				subjectId:item.subjectId,
+				repair:item.repair,
 				blob:item.blob
 			}
 			r(cosmosItem)

+ 1 - 0
TEAMModelOS/ClientApp/src/utils/public.js

@@ -208,6 +208,7 @@ export default {
 	// 数据结构转换工具
 	jsonTransform(obj) {
 		let arr = []
+		console.log(obj)
 		if (obj.datas.length) {
 			let item = {}
 			let attrLength = obj.keys.length

+ 1 - 0
TEAMModelOS/ClientApp/src/view/Home.vue

@@ -197,6 +197,7 @@
     @import '../css/dark-iview-tabs.less';
     @import '../css/dark-iview-collapse.less';
     @import '../css/dark-iview-form.less';
+    @import '../css/dark-iview-upload.less';
     @import '../css/dark-iview-table.less';
     @import '../css/dark-iview-split.less';
     @import '../css/dark-iview-page.less';

+ 11 - 2
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseChildList.vue

@@ -21,7 +21,8 @@
 		<!-- 添加子题弹窗 -->
 		<Modal v-model="editChildModal" width="1080" footer-hide class="">
 			<div class="modal-header" slot="header">编辑子题</div>
-			<BaseCreateChild @addFinish='onEditChildFinish' refId="childListEdit" :editItem="curItem"></BaseCreateChild>
+			<BaseCreateChild @addFinish='onEditChildFinish' refId="childListEdit" :editItem="curItem" :curPeriodIndex="curPeriodIndex"
+			 :curSubjectIndex="curSubjectIndex"></BaseCreateChild>
 		</Modal>
 	</div>
 </template>
@@ -36,7 +37,15 @@
 			childList: {
 				type: Array,
 				default: []
-			}
+			},
+			curPeriodIndex: {
+				type: Number,
+				default: 0,
+			},
+			curSubjectIndex: {
+				type: Number,
+				default: 0,
+			},
 		},
 		data() {
 			return {

+ 16 - 6
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseCreateChild.vue

@@ -65,7 +65,7 @@
 		<!-- 补救的富文本部分 -->
 		<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.repairResource"></BaseRepair>
+			<BaseRepair ref="repairRef" :datas="editInfo.repair"></BaseRepair>
 		</div>
 
 		<div class="save-wrap display-flex">
@@ -178,7 +178,8 @@
 		},
 		created() {
 			this.getSchoolInfo();
-			
+			console.log(this.curPeriodIndex)
+			console.log(this.curSubjectIndex)
 		},
 		methods: {
 			getSchoolInfo() {
@@ -196,6 +197,9 @@
 			
 			onSelectPoints(){
 				if(this.hasSchool){
+					console.log(this.schoolInfo)
+					console.log(this.curPeriodIndex)
+					console.log(this.curSubjectIndex)
 					this.selectPointsModal = true
 				}else{
 					this.$Message.warning('您当前未加入学校,无法选择知识点!')
@@ -280,7 +284,7 @@
 						break;		
 				}
 				exerciseItem.repair = this.repairContent;
-				exerciseItem.repairResource = this.formatRepairResource(
+				exerciseItem.repair = this.formatRepairResource(
 					this.$refs.repairRef.datas
 				);
 				exerciseItem.field = this.exerciseField + 1;
@@ -475,10 +479,10 @@
 
 				this.childList = editItem.children || [];
 				let schoolInfo = await this.$store.dispatch('user/getSchoolProfile');
-				this.schoolInfo = schoolInfo;
-
+				this.schoolInfo = schoolInfo.school_base;
+				
 				this.stemContent = editItem.question;
-				this.relateFileList = editItem.repairResource || [];
+				this.relateFileList = editItem.repair || [];
 				this.optionsContent = editItem.option;
 				this.analysisContent = editItem.explain;
 				this.analysisEditor.txt.html(editItem.explain);
@@ -533,6 +537,12 @@
 				},
 				//immediate:true
 			},
+			curPeriodIndex:{
+				handler(newValue, oldValue) {
+					console.log('变了',newValue)
+				},
+				immediate:true
+			}
 		},
 	};
 </script>

+ 9 - 6
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseEditExercise.vue

@@ -93,13 +93,14 @@
 		<!-- 补救的富文本部分 -->
 		<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>
+			<BaseRepair ref="repairRef" :datas="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>
+			<BaseChildList :childList="childList" @onEditChildFinish="onEditChildFinish" @onDeleteChild="onDeleteChild" :curPeriodIndex="exercisePeriod"
+			 :curSubjectIndex="exerciseSubject"></BaseChildList>
 		</div>
 
 		<div class="save-wrap display-flex">
@@ -169,6 +170,7 @@
 				addChildModal: false,
 				isRelatedContent: false,
 				selectPointsModal: false,
+				repairResource:[],
 				isEdit: false,
 				editInfo: null,
 				schoolInfo: {
@@ -321,7 +323,7 @@
 						break;
 				}
 				exerciseItem.explain = this.analysisContent;
-				exerciseItem.repairResource = this.formatRepairResource(
+				exerciseItem.repair = this.formatRepairResource(
 					this.$refs.repairRef.datas
 				);
 				exerciseItem.field = this.exerciseField + 1;
@@ -660,7 +662,7 @@
 
 			// 提取富文本内容中的文本
 			getSimpleText(html) {
-				var r = /<[^>]*>|/g;
+				var r = /<(?!img|video|audio).*?>/g;
 				return html.replace(r, "");
 			},
 			// 排除对象空属性
@@ -748,7 +750,6 @@
 				let schoolInfo = schoolProfile.school_base;
 				
 				if (editItem.scope === "school" && schoolInfo) {
-					console.log('进来了')
 					this.schoolInfo = schoolInfo;
 					this.exerciseGrade = editItem.gradeIds;
 					this.exercisePeriod = schoolInfo.period
@@ -800,10 +801,12 @@
 				}
 				this.childList = editItem.children || [];
 				this.stemContent = editItem.question;
-				this.relateFileList = editItem.repairResource || [];
+				this.relateFileList = editItem.repair || [];
 				this.optionsContent = editItem.option;
 				this.analysisContent = editItem.explain;
 				this.analysisEditor.txt.html(editItem.explain);
+				this.repairResource = this.editInfo.repair
+				console.log('当前编辑的试题',this.editInfo)
 			},
 		},
 		mounted() {

+ 0 - 5
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseRepair.vue

@@ -133,11 +133,6 @@
 				this.$refs.chooseContentRef.clickTab('content')
 			}
 		},
-
-		watch: {
-
-		}
-
 	}
 </script>
 

+ 6 - 2
TEAMModelOS/ClientApp/src/view/evaluation/index/CreateExercises.vue

@@ -228,7 +228,11 @@
 
 			onSelectPoints() {
 				if (this.hasSchool) {
-					this.selectPointsModal = true
+					if(this.exersicesType === 'compose'){
+						this.$Message.warning('综合题的知识点请绑定在小题上!')
+					}else{
+						this.selectPointsModal = true
+					}
 				} else {
 					this.$Message.warning('您当前未加入学校,无法选择知识点!')
 				}
@@ -321,7 +325,7 @@
 					default:
 						break;
 				}
-				exerciseItem.repairResource = this.formatRepairResource(this.$refs.repairRef.datas);
+				exerciseItem.repair = 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;

+ 6 - 2
TEAMModelOS/ClientApp/src/view/knowledge-point/index/Index.vue

@@ -350,6 +350,8 @@
 			/* 保存最新知识块内容 */
 			savePointAndBlock(pointItem) {
 				return new Promise((r, j) => {
+					console.log(pointItem)
+					pointItem.code =  pointItem.code.replace('Knowledge-','')
 					this.$api.knowledge.SaveOrUpdateKnowledge([pointItem]).then(res => {
 						if (!res.error && res.knowledges) {
 							this.curDragPoint = null
@@ -601,9 +603,10 @@
 						let that = this
 						this.isLoading = true
 						this.$api.knowledge.DeleteSchoolPoint({
+							code:data.code.replace('Knowledge-',''),
 							id: data.id
 						}).then(res => {
-							if (res.knowledges) {
+							if (!res.error) {
 								console.log(this.blockList)
 								console.log(this.originBlockList)
 								console.log(data)
@@ -638,9 +641,10 @@
 					onOk: () => {
 						this.isLoadPoints = true
 						this.$api.knowledge.DeleteSchoolPoint({
+							code:data.code.replace('Knowledge-',''),
 							id: data.id
 						}).then(res => {
-							if (!res.error && res.knowledges) {
+							if (!res.error) {
 								this.$Message.success('删除成功')
 								this.originPointList.splice(this.originPointList.indexOf(data), 1)
 								if (!this.isShowPoints) this.currentBlock.points.splice(this.currentBlock.points.indexOf(data.id), 1)

+ 2 - 2
TEAMModelOS/ClientApp/src/view/knowledge-point/index/operation/AddPoint.vue

@@ -64,7 +64,7 @@
                         points: this.blockDatas ? [this.blockDatas.id] : [],
                         id: editPointItem ? editPointItem.id : null,
                     }
-					
+					console.log(params)
                     this.savePointAndBlock(params)
                 }
             },
@@ -76,7 +76,7 @@
              */
             savePointAndBlock(pointItem) {
                 this.$api.knowledge.SaveOrUpdateKnowledge([pointItem]).then(res => {
-                    if (!res.error && res.knowledges) {
+                    if (!res.error && res.knowledges.length) {
                         this.$emit('addFinish', res.knowledges[0])
                         this.closeModal()
                         this.currentPoint = null

+ 1 - 1
TEAMModelOS/ClientApp/src/view/knowledge-point/index/operation/ComposeBlock.vue

@@ -73,7 +73,7 @@
                             name: this.newBlockName,
                             alias: this.newBlockName,
                             subjectId: this.currentParams.subjectId,
-                            code: this.currentParams.code,
+                            code: this.currentParams.code.replace('Knowledge-',''),
                             order: 706,
                             status: 1,
                             knowledgeId: Math.uuid(),

+ 4 - 3
TEAMModelOS/ClientApp/src/view/learnactivity/CreatePrivEva.vue

@@ -68,12 +68,13 @@
                         <TabPane label="作答预览" name="student" :index="6" tab="createTest">
                             <StudentPreview></StudentPreview>
                         </TabPane>
-                        <div slot="extra" class="create-type-wrap" v-show="activeTab == 'manualPaper' || activeTab == 'import'">
+                        <!-- 暂时注解,创建评测这里只能挑选试卷,如果没有试卷提示前往试卷库创建试卷 -->
+                        <!-- <div slot="extra" class="create-type-wrap" v-show="activeTab == 'manualPaper' || activeTab == 'import'">
                             <RadioGroup v-model="createType" style="margin-left:25px;" @on-change="setActiveTab" size="small">
                                 <Radio label="manualPaper">挑选试卷</Radio>
                                 <Radio label="import">导入试卷</Radio>
                             </RadioGroup>
-                        </div>
+                        </div> -->
                     </Tabs>
                 </div>
             </div>
@@ -143,7 +144,7 @@ export default {
                     { required: true, type: 'number', message: '请设置结束时间!', trigger: 'change' }
                 ]
             },
-            activeTab: 'preview',
+            activeTab: 'manualPaper',
             evaluationInfo: {
                 name: '',
                 targets: [],

+ 6 - 5
TEAMModelOS/ClientApp/src/view/learnactivity/CreateSchoolEva.vue

@@ -69,24 +69,25 @@
                 <div class="evaluation-question-main">
                     <EmptyData :top="0" style="padding-top:100px;" v-if="evaluationInfo.paperInfo.length == 0" textContent="暂无科目,请添加科目"></EmptyData>
                     <Tabs v-model="activeTab" type="card" class="question-main-tabs" v-if="evaluationInfo.paperInfo.length > 0 || mode == 'class' " name="createTest">
+                        <TabPane label="试卷库" name="manualPaper" v-if="evaluationInfo.paperInfo[curSubIndex].createType == 'manualPaper'" :index="1" tab="createTest">
+                            <ManualPaper :periodId="evaluationInfo.period.id" :gradesObj="evaluationInfo.grades" :subjectId="evaluationInfo.paperInfo[curSubIndex].subjectId" @selectPaper="selectPaper" :selectedId="evaluationInfo.paperInfo[curSubIndex].id"></ManualPaper>
+                        </TabPane>
                         <TabPane label="试卷预览" name="preview" :index="2" tab="createTest">
                             <TeacherPreview :testPaper="evaluationInfo.paperInfo[curSubIndex]" :examAnalysisStatus="examAnalysisStatus"></TeacherPreview>
                         </TabPane>
-                        <TabPane label="试卷库" name="manualPaper" v-if="evaluationInfo.paperInfo[curSubIndex].createType == 'manualPaper'" :index="3" tab="createTest">
-                            <ManualPaper :periodId="evaluationInfo.period.id" :gradesObj="evaluationInfo.grades" :subjectId="evaluationInfo.paperInfo[curSubIndex].subjectId" @selectPaper="selectPaper" :selectedId="evaluationInfo.paperInfo[curSubIndex].id"></ManualPaper>
-                        </TabPane>
                         <TabPane label="导入说明" name="import" v-if="evaluationInfo.paperInfo[curSubIndex].createType == 'import'" :index="4" tab="createTest">
                             <BaseImport @importedQuestions="getImportQuestions" @goToPreview="goToPreview"></BaseImport>
                         </TabPane>
                         <TabPane label="作答预览" name="student" :index="6" tab="createTest">
                             <StudentPreview></StudentPreview>
                         </TabPane>
-                        <div slot="extra" class="create-type-wrap" v-show="activeTab == 'manualPaper' || activeTab == 'import'">
+                        <!-- 暂时注解,创建评测这里只能挑选试卷,如果没有试卷提示前往试卷库创建试卷 -->
+                        <!-- <div slot="extra" class="create-type-wrap" v-show="activeTab == 'manualPaper' || activeTab == 'import'">
                             <RadioGroup v-model="evaluationInfo.paperInfo[curSubIndex].createType" style="margin-left:25px;" @on-change="setActiveTab" size="small">
                                 <Radio label="manualPaper">挑选试卷</Radio>
                                 <Radio label="import">导入试卷</Radio>
                             </RadioGroup>
-                        </div>
+                        </div> -->
                     </Tabs>
                 </div>
             </div>

+ 264 - 92
TEAMModelOS/ClientApp/src/view/learnactivity/ImportCreate.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="import-create-wrap">
         <BaseImport @importFinish="onImportFinish"></BaseImport>
-        <Modal v-model="importStatus" title="导入试卷" width="1200" @on-ok="saveTestPaper">
+        <Modal v-model="importStatus" title="导入试卷" width="1200" :loading="modalLoading" @on-ok="saveTestPaper" :mask-closable="false">
             <TestPaper ref="testPaper" :paper="evaluationInfo"></TestPaper>
         </Modal>
     </div>
@@ -16,6 +16,7 @@ export default {
     },
     data() {
         return {
+            modalLoading: true,
             routerScope: '',
             importStatus: false,
             errorList: [],
@@ -96,6 +97,7 @@ export default {
         },
         /** 保存当前试卷数据 */
         async saveTestPaper() {
+            this.importStatus = true
             let exListVm = this.$refs.testPaper.$refs.exList
             let hasSurplus = exListVm.surPlusScore // 判断是否有剩余分数未分配
             let noScoreList = exListVm.exerciseList.filter(item => item.score === 0) // 判断是否有未配分的题目
@@ -164,8 +166,15 @@ export default {
                         this.isLoading = false
                     }
                 } else {
-                    this.$Message.warning(`保存前请确认试卷配分是否与试卷总分一致!`)
+                    this.$Message.warning({
+                        content: `题目配分与试卷总分不一致!`,
+                        duration: 2.5
+                    })
                     this.isLoading = false
+                    this.modalLoading = false
+                    setTimeout(() => {
+                        this.modalLoading = true
+                    }, 100)
                 }
             } else {
                 this.$Message.warning(`存在异常试题,请修改或者重新导入!`)
@@ -252,111 +261,274 @@ export default {
             // 将传进来的试题 如果是导入的试题 则需要补充年级学段等信息
             let refreshList = await this.refreshImportItems(list)
             // 保存导入的试题到BLOB以及COSMOS
-            this.savePaperItems(refreshList, modifyItems, this.isEditPaper).then(async res => {
-                let guid = this.$tools.guid()
-                let paperItem = {
-                    id: this.evaluationInfo.id || guid,
-                    code: this.editPaper ? this.editPaper.code : (this.isSchool ? this.$store.state.userInfo.schoolCode : this.$store
-                        .state.userInfo.TEAMModelId),
-                    scope: this.isSchool ? 'school' : 'private',
-                    gradeIds: this.isSchool ? (this.evaluationInfo.paperGrade.length ? this.evaluationInfo.paperGrade : this.gradeList
-                        .map(i => i.id)) : [],
-                    subjectId: this.isSchool ? this.subjectList[this.evaluationInfo.paperSubject].id : null,
-                    subjectName: this.isSchool ? this.subjectList[this.evaluationInfo.paperSubject].name : '',
-                    periodId: this.isSchool ? this.schoolInfo.period[this.evaluationInfo.paperPeriod].id : null,
-                    name: this.evaluationInfo.name,
-                    points: this.getPaperPoints(this.evaluationInfo.item),
-                    scoring: this.getAnswers(list),
-                    score: this.evaluationInfo.score,
-                    multipleRule: multipleRule || 1
-                }
-                let blobPaper = await this.$evTools.createBlobPaper(paperItem, res.slides)
-                // 首先保存新题目的JSON文件到Blob 然后返回URL链接
-                let paperFile = new File([JSON.stringify(blobPaper)], "index.json");
-                // 获取初始化Blob需要的数据
-                let sasData = this.evaluationInfo.type === 'private' ? await this.$tools.getPrivateSas() : await this.$tools.getSchoolSas()
-                //初始化Blob
-                let containerClient = new blobTool(sasData.url, sasData.name, sasData.sas, this.evaluationInfo.type)
-                try {
-                    let promiseArr = []
-                    let blobFile = null
+            this.savePaperItems(refreshList, modifyItems, this.isEditPaper).then(
+                async res => {
+                    console.log('savePaperItems成功')
+                    let guid = this.$tools.guid()
+                    let paperItem = {
+                        id: this.evaluationInfo.id || guid,
+                        code: this.editPaper ? this.editPaper.code : (this.isSchool ? this.$store.state.userInfo.schoolCode : this.$store
+                            .state.userInfo.TEAMModelId),
+                        scope: this.isSchool ? 'school' : 'private',
+                        gradeIds: this.isSchool ? (this.evaluationInfo.paperGrade.length ? this.evaluationInfo.paperGrade : this.gradeList
+                            .map(i => i.id)) : [],
+                        subjectId: this.isSchool ? this.subjectList[this.evaluationInfo.paperSubject].id : null,
+                        subjectName: this.isSchool ? this.subjectList[this.evaluationInfo.paperSubject].name : '',
+                        periodId: this.isSchool ? this.schoolInfo.period[this.evaluationInfo.paperPeriod].id : null,
+                        name: this.evaluationInfo.name,
+                        points: this.getPaperPoints(this.evaluationInfo.item),
+                        scoring: this.getAnswers(list),
+                        score: this.evaluationInfo.score,
+                        multipleRule: multipleRule || 1
+                    }
+                    let blobPaper = await this.$evTools.createBlobPaper(paperItem, res.slides)
+                    // 首先保存新题目的JSON文件到Blob 然后返回URL链接
+                    let paperFile = new File([JSON.stringify(blobPaper)], "index.json");
+                    // 获取初始化Blob需要的数据
+                    let sasData = this.evaluationInfo.type === 'private' ? await this.$tools.getPrivateSas() : await this.$tools.getSchoolSas()
+                    //初始化Blob
+                    let containerClient = new blobTool(sasData.url, sasData.name, sasData.sas, this.evaluationInfo.type)
+                    try {
+                        let promiseArr = []
+                        let blobFile = null
 
-                    console.log('试卷题目的ID集合', res.files)
-                    console.log('试卷ITEM', paperItem)
-                    // return
-                    let privateSas = await this.$tools.getPrivateSas()
-                    let privateBlob = new blobTool(privateSas.url, privateSas.name, privateSas.sas, 'private')
-                    let schoolSas = await this.$tools.getSchoolSas()
-                    let schoolBlob = new blobTool(schoolSas.url, schoolSas.name, schoolSas.sas, 'school')
+                        console.log('试卷题目的ID集合', res.files)
+                        console.log('试卷ITEM', paperItem)
+                        // return
+                        let privateSas = await this.$tools.getPrivateSas()
+                        let privateBlob = new blobTool(privateSas.url, privateSas.name, privateSas.sas, 'private')
+                        let schoolSas = await this.$tools.getSchoolSas()
+                        let schoolBlob = new blobTool(schoolSas.url, schoolSas.name, schoolSas.sas, 'school')
 
-                    // 放入试题json文件以及index.json文件
-                    for (let i = 0; i < res.files.length; i++) {
-                        promiseArr.push(new Promise(async (r, j) => {
-                            // let result = await containerClient.upload(res.files[i], 'paper/' + paperItem.name, undefined, false)
-                            // r(result)
+                        // 放入试题json文件以及index.json文件
+                        for (let i = 0; i < res.files.length; i++) {
+                            promiseArr.push(new Promise(async (r, j) => {
+                                // let result = await containerClient.upload(res.files[i], 'paper/' + paperItem.name, undefined, false)
+                                // r(result)
 
-                            let item = res.files[i]
-                            if (item.scope == 'school') {
-                                containerClient.copyFolder('paper/' + paperItem.name + '/', 'item/' + item.id, schoolBlob)
-                            } else {
-                                containerClient.copyFolder('paper/' + paperItem.name + '/', 'item/' + item.id, privateBlob)
-                            }
-                            r(200)
-                        }))
+                                let item = res.files[i]
+                                if (item.scope == 'school') {
+                                    containerClient.copyFolder('paper/' + paperItem.name + '/', 'item/' + item.id, schoolBlob)
+                                } else {
+                                    containerClient.copyFolder('paper/' + paperItem.name + '/', 'item/' + item.id, privateBlob)
+                                }
+                                r(200)
+                            }))
 
 
-                    }
-                    promiseArr.push(new Promise(async (r, j) => {
-                        try {
-                            blobFile = await containerClient.upload(paperFile, 'paper/' + paperItem.name, undefined, false)
-                            console.log('上传到试卷目录下', blobFile)
-                            r(blobFile)
-                        } catch (e) {
-                            j(e)
-                            this.$Message.error(e.spaceError)
-                            this.isLoading = false
                         }
-
-                    }))
-                    // 进行试卷文件上传Blob
-                    Promise.all(promiseArr).then(async result => {
-                        console.log('试卷上传文件的promise结果', result)
-                        if (blobFile.blob) {
-                            // 保存到COSMOS是不含base64图片编码的数据 避免数据量过大
-                            paperItem.blob = blobFile.blob.split('/index.json')[0]
-                            let params = {
-                                paper: await this.$evTools.createCosmosPaper(paperItem),
-                                option: this.isEditPaper ? 'update' : 'insert'
+                        promiseArr.push(new Promise(async (r, j) => {
+                            try {
+                                blobFile = await containerClient.upload(paperFile, 'paper/' + paperItem.name, undefined, false)
+                                console.log('上传到试卷目录下', blobFile)
+                                r(blobFile)
+                            } catch (e) {
+                                j(e)
+                                this.$Message.error(e.spaceError)
+                                this.isLoading = false
                             }
-                            //  保存试卷到cosmos
-                            this.$api.learnActivity.SaveExamPaper(params).then(
-                                res => {
-                                    if (res.error == null) {
-                                        this.$Message.success(this.isEditPaper ? '编辑成功' : '保存成功')
+
+                        }))
+                        // 进行试卷文件上传Blob
+                        Promise.all(promiseArr).then(async result => {
+                            console.log('试卷上传文件的promise结果', result)
+                            if (blobFile.blob) {
+                                // 保存到COSMOS是不含base64图片编码的数据 避免数据量过大
+                                paperItem.blob = blobFile.blob.split('/index.json')[0]
+                                let params = {
+                                    paper: await this.$evTools.createCosmosPaper(paperItem),
+                                    option: this.isEditPaper ? 'update' : 'insert'
+                                }
+                                //  保存试卷到cosmos
+                                this.$api.learnActivity.SaveExamPaper(params).then(
+                                    res => {
+                                        if (res.error == null) {
+                                            this.$Message.success(this.isEditPaper ? '编辑成功' : '保存成功')
+                                            this.isLoading = false
+                                            this.$router.push({
+                                                name: this.evaluationInfo.type === 'private' ? 'personalBank' : 'schoolBank',
+                                                params: {
+                                                    tabName: 'paper'
+                                                }
+                                            })
+                                        } else {
+                                            this.$Message.error('保存试卷失败')
+                                            this.isLoading = false
+                                        }
+                                    },
+                                    err => {
+                                        this.$Message.error('保存试卷失败')
                                         this.isLoading = false
-                                        this.$router.push({
-                                            name: this.evaluationInfo.type === 'private' ? 'personalBank' : 'schoolBank',
-                                            params: {
-                                                tabName: 'paper'
-                                            }
+                                    }
+                                )
+                            }
+                        })
+                    } catch (e) {
+                        this.$Message.error('上传失败!')
+                        console.log(e)
+                        this.isLoading = false
+                    }
+                },
+                err => {
+                    console.log('savePaperItems失败')
+                }).finally(() => {
+                    console.log('savePaperItems结束')
+                })
+        },
+        /* 将list中的试题 保存到Cosmos以及Blob中 */
+        async savePaperItems(list, modifyItems, isEdit) {
+            let that = this
+            console.log('保存的试题列表')
+            console.log(list)
+            // 获取初始化Blob需要的数据
+            let sasData = this.isSchool ? await this.$tools.getSchoolSas() : await this.$tools.getPrivateSas()
+            let itemJsonFiles = []
+            //初始化Blob
+            let containerClient = new blobTool(sasData.url, sasData.name, sasData.sas, this.evaluationInfo.type)
+            return new Promise(async (resolve, j) => {
+                let promiseArr = []
+                for (let i = 0; i < list.length; i++) {
+                    let exerciseItem = list[i]
+                    console.log(exerciseItem)
+                    // 如果是编辑试卷状态 或者 从题目是来自题库
+                    if (isEdit || exerciseItem.blob) {
+                        if (exerciseItem.children.length) {
+                            // 如果是编辑状态下的综合题
+                            let composeArr = [exerciseItem, ...exerciseItem.children]
+                            composeArr.forEach(composeItem => {
+                                promiseArr.push(new Promise(async (r, j) => {
+                                    if (composeItem.children.length && composeItem.children[0].id) {
+                                        composeItem.children = composeItem.children.map(i => i.id)
+                                    }
+                                    // 保存试题的blob链接
+                                    const itemJsonFile = await this.$evTools.createBlobItem(composeItem)
+                                    let file = new File([JSON.stringify(itemJsonFile)], composeItem.id + ".json");
+                                    itemJsonFiles.push(composeItem)
+                                    r(composeItem)
+                                    console.log(composeItem)
+                                }))
+                            })
+                        } else {
+                            // 如果已有blob字段 说明是已经保存到blob的试题 只需要将最新试题更新到paper目录下(自动手动挑题情况)
+                            promiseArr.push(new Promise(async (r, j) => {
+                                // 保存试题的blob链接
+                                const itemJsonFile = await this.$evTools.createBlobItem(exerciseItem)
+                                let file = new File([JSON.stringify(itemJsonFile)], exerciseItem.id + ".json");
+                                itemJsonFiles.push(exerciseItem)
+                                r(exerciseItem)
+                            }))
+                        }
+                    } else if (exerciseItem.children.length) { //如果是导入的 综合题
+                        // 如果是综合题 则需要把题干和小题都进行保存操作
+                        let composeArr = [exerciseItem, ...exerciseItem.children]
+                        composeArr.forEach(composeItem => {
+                            promiseArr.push(new Promise(async (r, j) => {
+                                if (composeItem.children.length && composeItem.children[0].id) {
+                                    composeItem.children = composeItem.children.map(i => i.id)
+                                }
+                                // 将当前的试题数据转化为BLOB内部的试题JSON格式
+                                const itemJsonFile = await this.$evTools.createBlobItem(composeItem)
+                                const cosmosItem = await this.$evTools.createCosmosItem(composeItem)
+                                // 首先保存新题目的JSON文件到Blob 然后返回URL链接
+                                let file = new File([JSON.stringify(itemJsonFile)], composeItem.id + ".json");
+                                try {
+                                    // 等待上传blob的返回结果
+                                    let blobFile = await containerClient.upload(file, 'item/' + composeItem.id, undefined, false)
+                                    if (blobFile.blob) {
+                                        // 保存试题JSON文件到试卷文件夹需要
+                                        itemJsonFiles.push(composeItem)
+                                        // 保存到COSMOS是不含base64图片编码的数据 避免数据量过大
+                                        cosmosItem.blob = blobFile.blob
+                                        // 保存当前试题到数据库
+                                        that.saveExercise(cosmosItem).then(res => {
+                                            r(composeItem)
                                         })
                                     } else {
-                                        this.$Message.error('保存试卷失败')
-                                        this.isLoading = false
+                                        j(500)
                                     }
-                                },
-                                err => {
-                                    this.$Message.error('保存试卷失败')
+                                } catch (e) {
+                                    console.log('------', e)
+                                    this.$Message.error(e.spaceError)
                                     this.isLoading = false
                                 }
-                            )
+                            }))
+                        })
+                    } else {
+                        // 如果没有blob字段 说明试题还没有保存到item目录下 则需要进行保存item文件夹后再保存到paper文件夹(导入试题情况)
+                        promiseArr.push(new Promise(async (r, j) => {
+                            // 将当前的试题数据转化为BLOB内部的试题JSON格式
+                            const itemJsonFile = await this.$evTools.createBlobItem(exerciseItem)
+                            const cosmosItem = await this.$evTools.createCosmosItem(exerciseItem)
+                            // 首先保存新题目的JSON文件到Blob 然后返回URL链接
+                            let file = new File([JSON.stringify(itemJsonFile)], exerciseItem.id + ".json");
+                            try {
+                                // 等待上传blob的返回结果
+                                let blobFile = await containerClient.upload(file, 'item/' + exerciseItem.id, undefined, false)
+                                if (blobFile.blob) {
+                                    // 保存试题JSON文件到试卷文件夹需要
+                                    itemJsonFiles.push(exerciseItem)
+                                    // 保存到COSMOS是不含base64图片编码的数据 避免数据量过大
+                                    cosmosItem.blob = blobFile.blob
+                                    // 保存当前试题到数据库
+                                    that.saveExercise(cosmosItem).then(res => {
+                                        r(exerciseItem)
+                                    })
+                                } else {
+                                    j(500)
+                                }
+                            } catch (e) {
+                                this.$Message.error(e.spaceError)
+                                this.isLoading = false
+                            }
+                        }))
+                    }
+                }
+                Promise.all(promiseArr).then(result => {
+                    console.log(result)
+                    let slides = []
+                    // 主观题的answer为空数组
+                    let nullType = ['complete', 'subjective', 'compose']
+                    result.forEach(item => {
+                        let o = {
+                            url: item.id + '.json',
+                            type: item.type,
+                            scoring: {
+                                score: item.score,
+                                knowledge: item.points || [],
+                                field: item.field || 1,
+                                ans: nullType.includes(item.type) ? [] : item.answer
+                            }
                         }
+                        item.type === 'compose' && delete o.scoring
+                        slides.push(o)
                     })
-                } catch (e) {
-                    this.$Message.error('上传失败!')
-                    console.log(e)
-                    this.isLoading = false
-                }
+                    resolve({
+                        slides: slides,
+                        files: itemJsonFiles
+                    })
+                }).catch(err => {
+                    console.log(err)
+                    this.$Message.error('操作失败')
+                })
+            })
+        },
+        /* 保存单个试题 */
+        saveExercise(item) {
+            item.code = item.scope === 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId
+            return new Promise((r, j) => {
+                this.$api.newEvaluation.SaveSingleExercise({
+                    itemInfo: item,
+                    // option: this.isEditPaper ? 'update' : 'insert'
+                    option: 'insert'
+                }).then(res => {
+                    if (!res.error) {
+                        r(res)
+                    } else {
+                        j(res)
+                        this.$Message.error('服务器繁忙!')
+                    }
+
+                })
             })
         },
     },

+ 1 - 1
TEAMModelOS/ClientApp/src/view/learnactivity/MgtPrivEva.less

@@ -257,4 +257,4 @@
     padding: 2px 4px;
     color: white;
     border-radius: 2px;
-}
+}

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

@@ -120,6 +120,7 @@ export default {
         }
     },
     methods: {
+        
         handleEnd(index) {
             this.$Modal.confirm({
                 title: '结束评测',

+ 6 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.less

@@ -222,4 +222,10 @@
     padding: 2px 4px;
     color: white;
     border-radius: 2px;
+}
+.mock-stu-answer{
+    margin-right: 20px;
+}
+.mock-tea-scoring{
+    margin-right: 40px;
 }

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

@@ -60,6 +60,14 @@
                 <div class="evaluation-detail-bar">
                     <span :class="curBarIndex == 0 ? 'evalustion-bar-item line-bottom-active line-bottom':'evalustion-bar-item line-bottom'" @click="selectBar(0)">评测数据</span>
                     <span :class="curBarIndex == 1 ? 'evalustion-bar-item line-bottom-active line-bottom':'evalustion-bar-item line-bottom'" @click="selectBar(1)">评测试卷</span>
+                    <div style="float:right;" v-if="$access.ability('admin','mock-eva').validateAll">
+                        <Tooltip content="此功能仅用于展示情景快速模拟学生作答数据,且学生作答为随机生成,仅供参考!!!" :max-width="240">
+                            <Button type="success" size="small" :loading="answerLoading" class="mock-stu-answer" @click="mockAnswer">一键作答</Button>
+                        </Tooltip>
+                        <Tooltip content="此功能仅用于展示情景快速模拟教师评分数据,且分数为随机生成,仅供参考!!!" :max-width="240">
+                            <Button type="warning" size="small" :loading="scoreLoading" class="mock-tea-scoring" @click="mockScoring">一键评分</Button>
+                        </Tooltip>
+                    </div>
                 </div>
                 <!--试卷信息-->
                 <div :class="curBarIndex == 1 ? 'animated fadeIn evaluation-base-info':'evaluation-base-info animated fadeOutRight'" v-show="curBarIndex == 1">
@@ -101,8 +109,11 @@ export default {
         TestPaper,
         Scoring
     },
+    inject:['reload'],
     data() {
         return {
+            answerLoading: false,
+            scoreLoading: false,
             split1: 0.2,
             scope: '',//school 校本 private 个人
             showBack: false,
@@ -136,6 +147,50 @@ export default {
         }
     },
     methods: {
+        // 模拟教师评分数据
+        mockScoring() {
+            this.scoreLoading = true
+            this.$api.learnActivity.mockScoring({
+                id: this.evaListShow[this.curEvaIndex].id,
+                code: this.evaListShow[this.curEvaIndex].code.replace('Exam-', '')
+            }).then(
+                res => {
+                    setTimeout(() => {
+                        this.$Message.success('模拟成功')
+                    }, 500)
+                },
+                err => {
+                    this.$Message.error('模拟失败')
+                }
+            ).finally(() => {
+                setTimeout(() => {
+                    this.scoreLoading = false
+                    this.reload()
+                }, 500)
+            })
+        },
+        // 模拟学生作答数据
+        mockAnswer() {
+            this.answerLoading = true
+            this.$api.learnActivity.mockAnswer({
+                id: this.evaListShow[this.curEvaIndex].id,
+                code: this.evaListShow[this.curEvaIndex].code.replace('Exam-', '')
+            }).then(
+                res => {
+                    setTimeout(() => {
+                        this.$Message.success('模拟成功')
+                    }, 500)
+                },
+                err => {
+                    this.$Message.error('模拟失败')
+                }
+            ).finally(() => {
+                setTimeout(() => {
+                    this.answerLoading = false
+                    this.reload()
+                }, 500)
+            })
+        },
         handleEnd(index) {
             this.$Modal.confirm({
                 title: '结束评测',

+ 33 - 22
TEAMModelOS/ClientApp/src/view/learnactivity/PaperScore.vue

@@ -56,31 +56,31 @@
                             <InputNumber v-model="studentAnswer.scores[getScoreIndex(typeIndex,index)]" size="small" :formatter="value => value < 0 ? '' : `${value}分`" :parser="value => value.replace('分', '')" :min="0" :max="item.score" placeholder="0分" class="score-input" />
                             <!-- 快速打分 满分 零分 -->
                             <div style="display:flex;justify-content: space-evenly;margin-top:5px;">
-                                <span class="fast-score-tag" title="满分" @click="fastSetScore((typeIndex + index),item.score)">
+                                <span class="fast-score-tag" title="满分" @click="fastSetScore((getScoreIndex(typeIndex,index)),item.score)">
                                     <!-- 题目分数 -->{{item.score}}
                                 </span>
-                                <span class="fast-score-tag" title="零分" @click="fastSetScore((typeIndex + index),0)" style="background:#ed4014">
+                                <span class="fast-score-tag" title="零分" @click="fastSetScore((getScoreIndex(typeIndex,index)),0)" style="background:#ed4014">
                                     0
                                 </span>
                             </div>
                             <!-- 加分 减分 -->
                             <div style="display:flex;justify-content: space-evenly;margin-top:5px;">
-                                <span class="fast-score-tag" @click="scoreUp((typeIndex + index),item.score)">
+                                <span class="fast-score-tag" @click="scoreUp((getScoreIndex(typeIndex,index)),item.score)">
                                     <Icon type="md-add" />
                                 </span>
-                                <span class="fast-score-tag" @click="scoreDown(typeIndex + index)" style="background:#ed4014">
+                                <span class="fast-score-tag" @click="scoreDown(getScoreIndex(typeIndex,index))" style="background:#ed4014">
                                     <Icon type="md-remove" />
                                 </span>
                             </div>
                             <!-- 滚动上下题目去掉 -->
                             <!-- <div style="display:flex;justify-content: space-evenly;margin-top:5px;display:none;" v-show="showQu">
-                                    <span class="fast-score-tag" title="上一题" :style="{'background':'#aeaeae','cursor':(typeIndex + index) == 0 ? 'not-allowed':''}" @click="goToQuestion(getQuIndex(typeIndex,index) - 1)">
-                                        <Icon type="md-arrow-round-back" />
-                                    </span>
-                                    <span class="fast-score-tag" title="下一题" :style="{'background':'#aeaeae','cursor':(typeIndex + index + 1) == studentAnswer.scores.legnth ? 'not-allowed':''}" @click="goToQuestion(getQuIndex(typeIndex,index) + 1)">
-                                        <Icon type="md-arrow-round-forward" />
-                                    </span>
-                                </div> -->
+                                <span class="fast-score-tag" title="上一题" :style="{'background':'#aeaeae','cursor':(typeIndex + index) == 0 ? 'not-allowed':''}" @click="goToQuestion(getQuIndex(typeIndex,index) - 1)">
+                                    <Icon type="md-arrow-round-back" />
+                                </span>
+                                <span class="fast-score-tag" title="下一题" :style="{'background':'#aeaeae','cursor':(typeIndex + index + 1) == studentAnswer.scores.legnth ? 'not-allowed':''}" @click="goToQuestion(getQuIndex(typeIndex,index) + 1)">
+                                    <Icon type="md-arrow-round-forward" />
+                                </span>
+                            </div> -->
                         </div>
                         <div class="qu-info-box">
                             <!-- 题干部分 -->
@@ -141,7 +141,7 @@
                                                 </div>
                                                 <!-- 其余题型答案 -->
                                                 <div v-else>
-                                                    <span :class="[ item.type === 'complete' ? 'item-answer-item':'']" v-for="(answer,index) in item.answer" :key="index">{{answer}}</span>
+                                                    <span v-for="(answer,index) in item.answer" :key="index" v-html="answer"></span>
                                                 </div>
                                             </div>
                                         </div>
@@ -180,19 +180,19 @@
                                 <InputNumber v-model="studentAnswer.scores[getScoreIndex(typeIndex,index,childIndex)]" size="small" :formatter="value => value < 0 ? '' : `${value}分`" :parser="value => value.replace('分', '')" :min="0" :max="childItem.score" placeholder="0分" class="score-input" />
                                 <!-- 快速打分 满分 零分 -->
                                 <div style="display:flex;justify-content: space-evenly;margin-top:5px;">
-                                    <span class="fast-score-tag" title="满分" @click="fastSetScore((typeIndex + index + childIndex),childItem.score)">
+                                    <span class="fast-score-tag" title="满分" @click="fastSetScore((getScoreIndex(typeIndex,index,childIndex)),childItem.score)">
                                         <!-- 题目分数 -->{{childItem.score}}
                                     </span>
-                                    <span class="fast-score-tag" title="零分" @click="fastSetScore((typeIndex + index + childIndex),0)" style="background:#ed4014">
+                                    <span class="fast-score-tag" title="零分" @click="fastSetScore((getScoreIndex(typeIndex,index,childIndex)),0)" style="background:#ed4014">
                                         0
                                     </span>
                                 </div>
                                 <!-- 加分 减分 -->
                                 <div style="display:flex;justify-content: space-evenly;margin-top:5px;">
-                                    <span class="fast-score-tag" @click="scoreUp((typeIndex + index + childIndex),item.score)">
+                                    <span class="fast-score-tag" @click="scoreUp((getScoreIndex(typeIndex,index,childIndex)),item.score)">
                                         <Icon type="md-add" />
                                     </span>
-                                    <span class="fast-score-tag" @click="scoreDown(typeIndex + index + childIndex)" style="background:#ed4014">
+                                    <span class="fast-score-tag" @click="scoreDown(getScoreIndex(typeIndex,index,childIndex))" style="background:#ed4014">
                                         <Icon type="md-remove" />
                                     </span>
                                 </div>
@@ -371,19 +371,30 @@ export default {
             answerIframe.onload = () => { }
         },
         saveMark(data) {
-            console.log(data)
             let img = document.createElement('img')
-            console.log(img)
-            img.src = data
-            img.style.width = '100%'
+            img.src = data.base64
             console.log('img', img)
             console.log('htmlstr', img.outerHTML)
             this.$set(this.studentAnswer.answers, this.curAnIndex, [img.outerHTML])
             this.curAnIndex = -1
             this.markStatus = false
-            this.$Message.warning('暂未对接保存批注API')
             let answerIframe = document.getElementById('answerIframe')
             answerIframe.onload = () => { }
+            this.$api.learnActivity.upsertAnswer({
+                "id": this.examId,
+                "answer": this.studentAnswer.answers,
+                "studentId": this.studentAnswer.id,
+                "subjectId": this.subjectId,
+                "classId": this.studentAnswer.classId,
+                "code": this.paperInfo.code,
+            }).then(
+                res => {
+                    this.$$Message.success('批注成功!')
+                },
+                err => {
+                    this.$$Message.error('批注成功!')
+                }
+            )
         },
         // 批注学生作答数据
         markStuAnswer(index) {
@@ -398,7 +409,7 @@ export default {
             answerIframe.onload = () => {
                 html2canvas(answerIframe.contentWindow.document.body, {
                     onclone: (data) => {
-                        console.log('呵呵', data)
+                        console.log('clone', data)
                     },
                     removeContainer: false
                 }).then((canvas) => {

+ 4 - 2
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/AchievementAnalysis/EntryTables.vue

@@ -76,10 +76,12 @@
                         ],
                         filterMultiple: false,
                         filterRemote(value, row) {
+							console.log(this.getAnalysisJson)
+							console.log(value)
                             if (value === 1) {
-                                this.entryTableData = this.entryTableData.filter(item => item.score > 180)
+                                this.entryTableData = this.entryTableData.filter(item => item.score > this.getAnalysisJson.ipoint)
                             } else if (value === 2) {
-                                this.entryTableData = this.entryTableData.filter(item => item.score > 180 && item.score < 200)
+                                this.entryTableData = this.entryTableData.filter(item => item.score > this.getAnalysisJson.ipoint && item.score < this.getAnalysisJson.ipoint + this.getAnalysisJson.touchScore)
                             }
                         }
                     }

+ 6 - 1
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/KnowledgeAnalysis/KnowledgeAnalysis.css

@@ -78,7 +78,12 @@
         vertical-align: sub;
     }
 
-
+.analysis-no-data{
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	align-items: center;
+}
 
 .fl-col-center {
     display: flex;

+ 67 - 61
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/KnowledgeAnalysis/KnowledgeAnalysis.vue

@@ -1,38 +1,44 @@
 <template>
     <div class="scatter-container">
-        <Row>
-            <Col span="12">
-                <span class="component-title">{{$t('totalAnalysis.ka_title1')}}</span>
-                <BasePie pieId="knowPie"></BasePie>
-            </Col>
-            <Col span="12" style="position:relative">
-                <!--<span class="component-title">{{$t('totalAnalysis.ka_title2')}}</span>-->
-                <!--<BaseRadar v-if="!isShowPie"></BaseRadar>-->
-                <BaseKnowledgeRadar echartsId="knowPie2"></BaseKnowledgeRadar>
-            </Col>
-        </Row>
-        <Divider />
-        <Row>
-
-            <span class="component-title">
-                {{$t('totalAnalysis.ka_title3')}}
-                <!-- 当前知识块 : 全部 -->
-                <!--<span style="font-size:14px;font-weight:bold;margin-left:20px;color:#66cccc">
-                    {{$t('totalAnalysis.ka_text1')}}:{{currentBlock}}
-                    <Icon type="ios-close-circle-outline" @click="refresh" style="cursor:pointer;" v-show="currentBlock != '全部'" />
-                </span>-->
-            </span>
-            <!-- 得分率关系表 -->
-            <div style="padding:20px 50px;position:relative">
-                <!--<span class="btn-class-score" @click="handleGoDetails">查看班级得分<Icon type="ios-arrow-dropright" /></span>-->
-                <BaseMyTable :columns="tableColumns"
-                             :tableName="$t('totalAnalysis.ka_title3')"
-                              tableRef="pointScoreRateTable"
-                             :tableDatas="knowledgeData"></BaseMyTable>
-            </div>
-        </Row>
-        <Divider />
-        <ScoreDetails ref="detailsRef"></ScoreDetails>
+		<div v-if="!hasKnowledge" class="analysis-no-data">
+			<EmptyData :top="200" textContent="本次考试未收集到知识点数据"></EmptyData>
+		</div>
+		<div v-else>
+			<Row>
+			    <Col span="12">
+			        <span class="component-title">{{$t('totalAnalysis.ka_title1')}}</span>
+			        <BasePie pieId="knowPie"></BasePie>
+			    </Col>
+			    <Col span="12" style="position:relative">
+			        <!--<span class="component-title">{{$t('totalAnalysis.ka_title2')}}</span>-->
+			        <!--<BaseRadar v-if="!isShowPie"></BaseRadar>-->
+			        <BaseKnowledgeRadar echartsId="knowPie2"></BaseKnowledgeRadar>
+			    </Col>
+			</Row>
+			<Divider />
+			<Row>
+			
+			    <span class="component-title">
+			        {{$t('totalAnalysis.ka_title3')}}
+			        <!-- 当前知识块 : 全部 -->
+			        <!--<span style="font-size:14px;font-weight:bold;margin-left:20px;color:#66cccc">
+			            {{$t('totalAnalysis.ka_text1')}}:{{currentBlock}}
+			            <Icon type="ios-close-circle-outline" @click="refresh" style="cursor:pointer;" v-show="currentBlock != '全部'" />
+			        </span>-->
+			    </span>
+			    <!-- 得分率关系表 -->
+			    <div style="padding:20px 50px;position:relative">
+			        <!--<span class="btn-class-score" @click="handleGoDetails">查看班级得分<Icon type="ios-arrow-dropright" /></span>-->
+			        <BaseMyTable :columns="tableColumns"
+			                     :tableName="$t('totalAnalysis.ka_title3')"
+			                      tableRef="pointScoreRateTable"
+			                     :tableDatas="knowledgeData"></BaseMyTable>
+			    </div>
+			</Row>
+			<Divider />
+			<ScoreDetails ref="detailsRef"></ScoreDetails>
+		</div>
+        
     </div>
 </template>
 
@@ -77,20 +83,21 @@
         },
 
         methods: {
-            renderClassColumns(classData) {
+            renderClassColumns(classData,origin) {
+				console.log(origin)
                 // 渲染得分率关系表格的班级数据
-                classData.forEach(item => {
-                    let classColumn = {
-                        title: item,
-                        sortable: 'custom',
-                        key: item,
-                        render: (h, params) => {
-                            return h('span', (Number(params.row[item]) * 100).toFixed(2) + '%')
-                        },
-                        minWidth: 150
-                    }
-                    this.tableColumns.push(classColumn)
-                })
+				classData.classes.forEach((item,index) => {
+				    let classColumn = {
+				        title: item.className,
+				        sortable: 'custom',
+				        key: item.className,
+				        render: (h, params) => {
+				            return h('span', ((Number(origin[params.row.name][index])) * 100).toFixed(2) + '%')
+				        },
+				        minWidth: 150
+				    }
+				    this.tableColumns.push(classColumn)
+				})
             },
 
             changePieOrBar() {
@@ -110,14 +117,14 @@
                     }
                     arr.push(o)
                 }
-                this.renderClassColumns(origin.className)
+                this.renderClassColumns(this.getAnalysisJson,origin)
 				console.log(arr)
                 this.knowledgeData = arr
             }
 
         },
         mounted() {
-            if (this.getKnowledgeData) {
+            if (this.getKnowledgeData && this.hasKnowledge) {
                 this.doRender(this.getKnowledgeData)
             }
         },
@@ -125,19 +132,18 @@
         computed: {
             // 获取最新散点图数据
             getKnowledgeData() {
-                return this.$store.state.totalAnalysis.knowledgeData
-            }
-        },
-        watch: {
-            getKnowledgeData: {
-                deep: true,
-                handler(val) {
-                    if (val) {
-                        this.doRender(val)
-                    }
-                }
-
-            }
+				let curSubjectIndex = this.$store.state.totalAnalysis.analysisJson.subjects.map(i => i.name).indexOf(this.$store.state.totalAnalysis.currentSubject)
+                return this.$store.state.totalAnalysis.analysisJson.pointLevelKey[curSubjectIndex].pointKey
+            },
+			
+			getAnalysisJson() {
+			    return this.$store.state.totalAnalysis.analysisJson
+			},
+			
+			hasKnowledge(){
+				let curSubjectIndex = this.$store.state.totalAnalysis.analysisJson.subjects.map(i => i.name).indexOf(this.$store.state.totalAnalysis.currentSubject)
+				return this.$store.state.totalAnalysis.analysisJson.pointLevelKey[curSubjectIndex].pointKey.classpercent
+			}
         }
     }
 </script>

+ 2 - 1
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/KnowledgeAnalysis/ScoreDetails.vue

@@ -193,7 +193,8 @@
         computed: {
             // 获取最新散点图数据
             getKnowledgeData() {
-                return this.$store.state.totalAnalysis.knowledgeData
+                let curSubjectIndex = this.$store.state.totalAnalysis.analysisJson.subjects.map(i => i.name).indexOf(this.$store.state.totalAnalysis.currentSubject)
+                return this.$store.state.totalAnalysis.analysisJson.pointLevelKey[curSubjectIndex].pointKey
             }
         },
         watch: {

+ 31 - 40
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/LevelAnalysis/LevelAnalysis.vue

@@ -14,14 +14,8 @@
 
             <span class="component-title">
                 {{$t('totalAnalysis.le_title3')}}
-                <!-- 当前知识块 : 全部 -->
-                <!--<span style="font-size:14px;font-weight:bold;margin-left:20px;color:#66cccc">
-                    {{$t('totalAnalysis.ka_text1')}}:{{currentBlock}}
-                    <Icon type="ios-close-circle-outline" @click="refresh" style="cursor:pointer;" v-show="currentBlock != '全部'" />
-                </span>-->
             </span>
             <div style="padding:20px 50px;position:relative">
-                <!--<span class="btn-class-score" @click="handleGoDetails">查看班级得分<Icon type="ios-arrow-dropright" /></span>-->
                 <BaseMyTable :columns="tableColumns"
                              :tableName="$t('totalAnalysis.le_title3')"
                               tableRef="levelScoreRateTable"
@@ -74,20 +68,20 @@
         },
 
         methods: {
-            renderClassColumns(classData) {
+            renderClassColumns(classData,origin) {
                 // 渲染得分率关系表格的班级数据
-                classData.forEach(item => {
-                    let classColumn = {
-                        title: item,
-                        sortable: 'custom',
-                        key: item,
-                        render: (h, params) => {
-                            return h('span', (Number(params.row[item]) * 100).toFixed(2) + '%')
-                        },
-                        minWidth: 150
-                    }
-                    this.tableColumns.push(classColumn)
-                })
+            	classData.classes.forEach((item,index) => {
+            	    let classColumn = {
+            	        title: item.className,
+            	        sortable: 'custom',
+            	        key: item.className,
+            	        render: (h, params) => {
+            	            return h('span', ((Number(origin[params.row.name][index])) * 100).toFixed(2) + '%')
+            	        },
+            	        minWidth: 150
+            	    }
+            	    this.tableColumns.push(classColumn)
+            	})
             },
 
             changePieOrBar() {
@@ -107,22 +101,14 @@
                     }
                     arr.push(o)
                 }
-                this.renderClassColumns(origin.className)
+                this.renderClassColumns(this.getAnalysisJson,origin)
                 this.levelData = arr
             }
 
-            // 点击玫瑰图某个知识点事件
-            // handleItemClick(item) {
-            //    let exerciseList = this.$store.state.totalAnalysis.exerciseList
-            //    this.currentBlock = item.data.name
-            //    let blockId = item.data.id
-            //    this.levelData = exerciseList.filter(item => item.blockId === blockId)
-            //    this.$refs.detailsRef.levelData = this.levelData
-            // },
-
         },
         mounted() {
             if (this.getLevelData) {
+				console.log(this.getLevelData)
                 this.doRender(this.getLevelData)
             }
         },
@@ -130,19 +116,24 @@
         computed: {
             // 获取最新散点图数据
             getLevelData() {
-                return this.$store.state.totalAnalysis.levelData
-            }
+				let curSubjectIndex = this.$store.state.totalAnalysis.analysisJson.subjects.map(i => i.name).indexOf(this.$store.state.totalAnalysis.currentSubject)
+				let levelJson = this.$store.state.totalAnalysis.analysisJson.pointLevelKey[curSubjectIndex].levelKey
+				let transArr = ["知识", "理解", "应用", "分析", "综合", "评鉴"]
+				levelJson.pointList = levelJson.pointList.map((i,index) => transArr[index])
+				for(let key in levelJson.classpercent){
+					if(key !== 'className'){
+						let newKey = transArr[+key - 1]
+						levelJson.classpercent[newKey] = levelJson.classpercent[key]
+					}
+				}
+				return levelJson
+            },
+			getAnalysisJson() {
+			    return this.$store.state.totalAnalysis.analysisJson
+			},
         },
         watch: {
-            getKnowledgeData: {
-                deep: true,
-                handler(val) {
-                    if (val) {
-                        this.doRender(val)
-                    }
-                }
-
-            }
+            
         }
     }
 </script>

+ 14 - 15
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/LevelAnalysis/ScoreDetails.vue

@@ -12,7 +12,7 @@
                 </div>
             </Col>
             <Col span="12">
-            <span class="component-title">{{$t('totalAnalysis.le_title5')}}<span style="font-size:14px;font-weight:bold;margin-left:20px;color:#66cccc">{{$t('totalAnalysis.ka_text3')}}:{{currentPoint}}</span></span>
+            <span class="component-title">{{$t('totalAnalysis.le_title5')}}<span style="font-size:14px;font-weight:bold;margin-left:20px;color:#66cccc">{{$t('totalAnalysis.ka_text3')}}:{{transArr[+currentPoint-1]}}</span></span>
                 <div style="padding:0 50px;">
                     <BaseMyTable :columns="detailsColumns"
                                  :tableName="$t('totalAnalysis.le_title5')"
@@ -48,7 +48,8 @@
                 isShowRadar: false,
                 tableData: [],
                 classDatas: [],
-                currentPoint: '知识',
+                currentPoint: 1,
+				transArr:["知识", "理解", "应用", "分析", "综合", "评鉴"],
                 tipContent: '* RH:高分区段  /  RL:低分区段 (模拟数据,仅供参考)',
                 levelData: [],
                 numData: [],
@@ -159,7 +160,7 @@
 
             // 点击柱状图某个点事件
             handleItemClick(item) {
-                this.currentPoint = item.name
+                this.currentPoint = this.transArr.indexOf(item.name) + 1
                 this.doRender(this.getLevelData, this.currentPoint)
             },
 
@@ -167,7 +168,6 @@
                 let origin = data.stupercent
                 let keys = origin.keys
                 let datas = origin[point]
-                console.log(origin)
                 this.tableData = this.$tools.jsonTransform({ datas: datas, keys: keys })
             },
 
@@ -190,18 +190,17 @@
         computed: {
             // 获取最新散点图数据
             getLevelData() {
-                return this.$store.state.totalAnalysis.levelData
-            }
-        },
-        watch: {
-            getLevelData: {
-                deep: true,
-                handler(val) {
-                    if (val) {
-                        this.doRender(val)
-                    }
+                let curSubjectIndex = this.$store.state.totalAnalysis.analysisJson.subjects.map(i => i.name).indexOf(this.$store.state.totalAnalysis.currentSubject)
+                let levelJson = this.$store.state.totalAnalysis.analysisJson.pointLevelKey[curSubjectIndex].levelKey
+                let transArr = ["知识", "理解", "应用", "分析", "综合", "评鉴"]
+                levelJson.pointList = levelJson.pointList.map((i,index) => transArr[index])
+                for(let key in levelJson.classpercent){
+                	if(key !== 'className'){
+                		let newKey = transArr[+key - 1]
+                		levelJson.classpercent[newKey] = levelJson.classpercent[key]
+                	}
                 }
-
+                return levelJson
             }
         }
     }

+ 2 - 0
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/index.less

@@ -209,6 +209,8 @@ body, html, .total-container {
     display: none;
   }
 }
+
+
 .fl-col-center {
   display: flex;
   flex-direction: column;

+ 13 - 7
TEAMModelOS/ClientApp/src/view/teachcontent/index.less

@@ -2,8 +2,8 @@
 @second-bgColor: #1b1b1b;
 @third-bgColor: #222222;
 @borderColor: #424242;
-@primary-textColor: #fff; //文本主颜
-@second-textColor: #a5a5a5; //文本副级颜
+@primary-textColor: #fff; //锟侥憋拷锟斤拷锟斤拷
+@second-textColor: #a5a5a5; //锟侥憋拷锟斤拷锟斤拷锟斤拷
 @primary-fontSize: 14px;
 @second-fontSize: 15px;
 
@@ -254,17 +254,17 @@
             overflow-y: auto;
             background-image: linear-gradient(to bottom right, #282828, #505050);
 
-            &::-webkit-scrollbar { /*滚动条整体样式*/
-                width: 10px; /*高宽分别对应横竖滚动条的尺寸*/
+            &::-webkit-scrollbar { /*锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷式*/
+                width: 10px; /*�߿��ֱ��Ӧ�����������ijߴ�*/
                 height: 1px;
             }
 
-            &::-webkit-scrollbar-thumb { /*滚动条里面小方块*/
+            &::-webkit-scrollbar-thumb { /*锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷小锟斤拷锟斤拷*/
                 -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
                 background: rgb(124,124,124);
             }
 
-            &::-webkit-scrollbar-track { /*滚动条里面轨道*/
+            &::-webkit-scrollbar-track { /*锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷*/
                 -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
                 background: rgba(68,68,68,.5);
             }
@@ -432,7 +432,7 @@
 
 .file-icon {
     display: inline-block;
-
+    // vertical-align: sub;
     img {
         display: inline-block;
         margin-right: 10px;
@@ -504,3 +504,9 @@
 .binding-point{
     color:white;
 }
+
+.rename-action-icon{
+    display: inline-block;
+    margin-left: 10px;
+    cursor: pointer;
+}

Разлика између датотеке није приказан због своје велике величине
+ 858 - 675
TEAMModelOS/ClientApp/src/view/teachcontent/index.vue


+ 1 - 1
TEAMModelOS/ClientApp/src/view/teachermgmt/components/userList/Index.vue

@@ -228,7 +228,7 @@
       <div class="userListContent">
         <Table v-if="isMockReloadAlive" class="scrollstyle" :height="tableHeight" ref="selection" :columns="tableColumns" :data="tableData" @on-selection-change="setTeachers" @on-sort-change="changeSort">
           <template slot-scope="{ row }" slot="picture">
-            <PersonalPhoto :name="row.name" :picture="row.picture" :color="row.nameColor" />
+            <PersonalPhoto :name="row.name" :picture="row.picture" />
           </template>
           <template slot-scope="{ row }" slot="action" v-if="isOpenSpaceAuth == false">
             <icon icon="shield-alt" style="font-size: 14px; color: #fff; margin-right: 20px; cursor: pointer;" @click="showAuth(row)" />

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

@@ -1,4 +1,4 @@
-const path = require('path')
+const path = require('path')
 const Timestamp = new Date().getTime();
 function resolve(dir) {
     return path.join(__dirname, './', dir)
@@ -55,13 +55,5 @@ module.exports = {
     },
     configureWebpack: config => {
         config.entry.app = ["babel-polyfill", "./src/main.js"];
-    },
-    configureWebpack: {
-        output: { // 输出重构  打包编译后的 文件名称  【模块名称.版本号.js】
-            filename: `js/[name].${Timestamp}.js`,
-            chunkFilename: `js/[name].${Timestamp}.js`
-            // chunkFilename: `js/[id].vw.js`
-        },
     }
-
 }

+ 108 - 22
TEAMModelOS/Controllers/Analysis/AchievementController.cs

@@ -2518,8 +2518,8 @@ namespace TEAMModelOS.Controllers.Analysis
             else return exerciseScatter;
         }
 
-        [HttpPost("point")]
-        public async Task<IActionResult> point(JsonElement request)
+        [HttpPost("answer")]
+        public async Task<IActionResult> answer(JsonElement request)
         {
             //获取评测的ID
             if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
@@ -2568,6 +2568,7 @@ namespace TEAMModelOS.Controllers.Analysis
                             foreach (string stu in classResult.studentIds)
                             {
                                 if (classResult.studentAnswers[k].Count > 0) {
+                                    k++;
                                     continue;
                                 }
                                 int n = 0;
@@ -2591,7 +2592,8 @@ namespace TEAMModelOS.Controllers.Analysis
                                         ans.Add(option);
                                     }
                                     else {
-                                        classResult.studentScores[k][n] = new Random().Next(0, (int)info.papers[m].point[n]) * 1.0; 
+                                        //classResult.studentScores[k][n] = new Random().Next(0, (int)info.papers[m].point[n]) * 1.0;
+                                        ans.Add(new List<string> { "测试作答数据一","测试作答数据二"});
                                     }                              
                                     n++;
                                 }
@@ -2602,32 +2604,116 @@ namespace TEAMModelOS.Controllers.Analysis
                                 k++;   
                             }
                             //await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(classResult, classResult.id, new PartitionKey($"ExamClassResult-{code}"));
-                            bool flag = true;
-                            foreach (List<double> scores in classResult.studentScores)
-                            {
-                                foreach (double score in scores)
+                            if (!classResult.progress) {
+                                bool flag = true;
+                                foreach (List<double> scores in classResult.studentScores)
                                 {
-                                    if (score == -1)
+                                    foreach (double score in scores)
                                     {
-                                        flag = false;
-                                        break;
+                                        if (score == -1)
+                                        {
+                                            flag = false;
+                                            break;
+                                        }
                                     }
                                 }
+                                if (flag)
+                                {
+                                    classResult.progress = true;
+                                    info.subjects[m].classCount += 1;
+
+                                }
+                            }                                                  
+                            await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(classResult, classResult.id, new PartitionKey($"ExamClassResult-{code}"));
+                        }
+                        
+                    }
+                    m++;
+                }
+                await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(info, id.ToString(), new PartitionKey($"Exam-{code}"));
+                return Ok(new { examClassResults });
+            }
+            catch (Exception e)
+            {
+                await _dingDing.SendBotMsg($"OS,{_option.Location},analysis/answer()\n{e.Message}", GroupNames.醍摩豆服務運維群組);
+                return BadRequest();
+            }
+        }
+
+        [HttpPost("scoring")]
+        public async Task<IActionResult> Scoring(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,c.classes 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}") }))
+                {
+                    examResults.Add(item);
+                }
+                //获取本次评测所有班级作答结果
+                List<ExamClassResult> examClassResults = new List<ExamClassResult>();
+                var queryClass = $"select c.id,c.code,c.name,c.school,c.examId,c.year,c.scope,c.pk,c.subjectId,c.studentScores,c.studentIds,c.gradeId,c.info,c.studentAnswers,c.sum,c.progress from c where c.examId =  '{id}' ";
+                await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<ExamClassResult>(queryText: queryClass, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{code}") }))
+                {
+                    examClassResults.Add(item);
+                }
+                int m = 0;
+                foreach (ExamSubject subject in info.subjects)
+                {
+                    foreach (ExamClassResult classResult in examClassResults)
+                    {
+                        if (subject.id.Equals(classResult.subjectId))
+                        {
+                            int k = 0;
+                            foreach (string stu in classResult.studentIds)
+                            {
+                                int n = 0;
+                                List<List<string>> ans = new List<List<string>>();
+                                foreach (List<string> simple in info.papers[m].answers)
+                                {
+                                    List<string> option = new List<string>();
+                                    if (simple.Count == 0)
+                                    {
+                                        classResult.studentScores[k][n] = new Random().Next(0, (int)info.papers[m].point[n]) * 1.0;
+                                    }                                  
+                                    n++;
+                                }
+                                classResult.sum[k] = classResult.studentScores[k].Sum();
+                                k++;
                             }
-                            if (flag)
+                            if (!classResult.progress)
                             {
-                                //ExamInfo exam = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<ExamInfo>(id.ToString(), new PartitionKey($"Exam-{school}"));
-                                classResult.progress = true;
-/*                                info.subjects.ForEach(s =>
-                                {        
-                                    s.classCount += 1;
-                                });*/
-                                info.subjects[m].classCount += 1;
-                                
-                            }                           
+                                bool flag = true;
+                                foreach (List<double> scores in classResult.studentScores)
+                                {
+                                    foreach (double score in scores)
+                                    {
+                                        if (score == -1)
+                                        {
+                                            flag = false;
+                                            break;
+                                        }
+                                    }
+                                }
+                                if (flag)
+                                {
+                                    classResult.progress = true;
+                                    info.subjects[m].classCount += 1;
+                                }
+                            }
+                            else {
+                                info.updateTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+                            }                            
                             await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(classResult, classResult.id, new PartitionKey($"ExamClassResult-{code}"));
                         }
-                        
                     }
                     m++;
                 }
@@ -2636,7 +2722,7 @@ namespace TEAMModelOS.Controllers.Analysis
             }
             catch (Exception e)
             {
-                await _dingDing.SendBotMsg($"OS,{_option.Location},analysis/getAnalysis()\n{e.Message}", GroupNames.醍摩豆服務運維群組);
+                await _dingDing.SendBotMsg($"OS,{_option.Location},analysis/scoring()\n{e.Message}", GroupNames.醍摩豆服務運維群組);
                 return BadRequest();
             }
         }

+ 348 - 13
TEAMModelOS/Controllers/Analysis/AnalysisController.cs

@@ -92,10 +92,13 @@ namespace TEAMModelOS.Controllers.Analysis
                 "examScoreRate"
             };
             ExamInfo info = null;
-            double ipoint = 0;
-            Dictionary<string, dynamic> gpoint = new Dictionary<string, dynamic>();
+            double ipoint = 0;          
             List<KeyValuePair<string, List<List<string>>>> subjectPaperDatas = new List<KeyValuePair<string, List<List<string>>>>();
+            List<Dictionary<string, object>> valuePairs = new List<Dictionary<string, object>>();            
             List<KeyValuePair<string, List<KeyValuePair<string, List<double>>>>> classSubjectPaperDatas = new List<KeyValuePair<string, List<KeyValuePair<string, List<double>>>>>();
+            //获取进线标准以及踩线分数
+            int touch = 0;
+            int income = 0;
             try
             {
                 var client = _azureCosmos.GetCosmosClient();
@@ -116,13 +119,13 @@ namespace TEAMModelOS.Controllers.Analysis
                     examClassResults.Add(item);
 
                 }
-                //获取进线标准
-                int touch = 0;
+
                 foreach (Period period in school.period)
                 {
                     if (info.period.id.Equals(period.id))
                     {
                         touch = period.analysis.touch;
+                        income = period.analysis.income;
                     }
                 }
                 //计算每个年级参考人数并计算进线分数
@@ -160,7 +163,7 @@ namespace TEAMModelOS.Controllers.Analysis
                     keys.Add(new KeyValuePair<string, double>(grade.id, ipoint));
                 }*/
                 //获取进线人数
-                int personCount = (int)System.Math.Round(info.stuCount * (touch / 100.0), MidpointRounding.AwayFromZero);
+                int personCount = (int)System.Math.Round(info.stuCount * (income / 100.0), MidpointRounding.AwayFromZero);
 
                 /* //声明年级所有科目总分
                  List<double> total = new List<double>();*/
@@ -174,11 +177,17 @@ namespace TEAMModelOS.Controllers.Analysis
                 foreach (ExamResult examResult in examResults)
                 {
                     (KeyValuePair<string, List<List<string>>> subjectData, KeyValuePair<string, List<KeyValuePair<string, List<double>>>> classSubjectData , Dictionary<string, List<double>> gscore) =DoExerciseScatteres(examResult, paperKey);
-                    gscores.Add(new KeyValuePair<string, Dictionary<string, List<double>>>(examResult.subjectId, gscore));                   
+                    gscores.Add(new KeyValuePair<string, Dictionary<string, List<double>>>(examResult.subjectId, gscore));
+/*                    Dictionary<string, dynamic> gpoint = new Dictionary<string, dynamic>();
+                    Dictionary<string, dynamic> glevel = new Dictionary<string, dynamic>();*/
+                    Dictionary<string, object> gpointList = new Dictionary<string, object>();
                     subjectPaperDatas.Add(subjectData);
                     classSubjectPaperDatas.Add(classSubjectData);
                     subjectScatter.Add(DoSubjectScatter(examResult));
-                    gpoint = DoKnowledgePoint(examResult,info);
+                    gpointList.Add("subjectId", examResult.subjectId);
+                    gpointList.Add("pointKey", DoKnowledgePoint(examResult, info));
+                    gpointList.Add("levelKey", DoLevel(examResult, info));
+                    valuePairs.Add(gpointList);
                     //获取一张试卷的满分
                     paperScore.Add(examResult.subjectId, examResult.paper.point.Sum());
                     List<double> StuSubjectTotals = new List<double>();
@@ -408,7 +417,7 @@ namespace TEAMModelOS.Controllers.Analysis
             
             }
             var sub = info.subjects.Select(x => new { id = x.id, name = x.name });
-            return Ok(new { students, classes, grades , paper= subjectPaperDatas, subjects=sub, scatterKey = scatterKey,paperKey=paperKey, pointKey = gpoint, ipoint = ipoint });
+            return Ok(new { students, classes, grades , paper= subjectPaperDatas, subjects=sub, scatterKey = scatterKey,paperKey=paperKey, pointLevelKey = valuePairs, ipoint = ipoint,touchScore = touch});
         }
 
         private static (KeyValuePair<string,List<List<string>>>, KeyValuePair<string, List<KeyValuePair<string, List<double>>>>,   Dictionary<string, List<double>>) DoExerciseScatteres(ExamResult e,  List<string > paperKey) 
@@ -704,11 +713,6 @@ namespace TEAMModelOS.Controllers.Analysis
         }
         private Dictionary<string, dynamic> DoKnowledgePoint(ExamResult exam, ExamInfo info)
         {
-            Dictionary<string, object> stuMap = new Dictionary<string, object>
-            {
-                { "schoolCode", "Habook" }
-            };
-            //List<Dictionary<string, object>> keyValues = new List<Dictionary<string, object>>();
             HashSet<string> knowledge = new HashSet<string>();
             HashSet<string> area = new HashSet<string>();
             List<double> point = new List<double>();
@@ -1068,5 +1072,336 @@ namespace TEAMModelOS.Controllers.Analysis
                 return knowledgeALL;
         }
 
+        private Dictionary<string, dynamic> DoLevel(ExamResult exam, ExamInfo info)
+        {
+            HashSet<string> knowledge = new HashSet<string>();
+            HashSet<string> area = new HashSet<string>();
+            List<double> point = new List<double>();
+            List<List<double>> result = new List<List<double>>();
+            List<ClassRange> classes = new List<ClassRange>();
+            List<string> ids = new List<string>();
+
+            //求单个知识点所占分数
+            List<string> per = new List<string>();
+            List<string> gper = new List<string>();
+            List<string> knowPer = new List<string>();
+            List<Dictionary<string, double>> eper = new List<Dictionary<string, double>>();
+            Dictionary<string, object> knowledgeALL = new Dictionary<string, object>();
+            Dictionary<string, object> knowledgeMap = new Dictionary<string, object>();
+            Dictionary<string, object> classMap = new Dictionary<string, object>();
+            Dictionary<string, object> wrongMap = new Dictionary<string, object>();
+            HashSet<string> className = new HashSet<string>();
+            List<List<string>> wrongPersent = new List<List<string>>();
+            List<string> key = new List<string>
+            {
+                "id",
+                "className",
+                "seatNO",
+                "point",
+                "anwPoint",
+                "persent"
+            };
+            List<string> keyWrong = new List<string>
+            {
+                "name",
+                "point",
+                "itemNO",
+                "persent",
+                "wrong",
+                "rhw",
+                "rlw"
+            };
+            //定位试卷信息
+            int index = 0;
+            foreach (ExamSubject subject in info.subjects)
+            {
+                if (subject.id.Equals(exam.subjectId))
+                {
+                    break;
+                }
+                else
+                {
+                    index++;
+                }
+            }
+            knowledge.Add("1");
+            knowledge.Add("2");
+            knowledge.Add("3");
+            knowledge.Add("4");
+            knowledge.Add("5");
+            knowledge.Add("6");
+            area.Add("1");
+            area.Add("2");
+            area.Add("3");
+            area.Add("4");
+            area.Add("5");
+            area.Add("6");
+
+            double Qnum = 0;
+            point = info.papers[index].point;
+            result = exam.studentScores;
+            classes = exam.classes;
+            ids = exam.studentIds;
+            //确定高分组 低分组人数
+            List<List<double>> re = exam.studentScores;
+            List<double> resultSum = new List<double>();
+            foreach (List<double> data in re)
+            {
+                resultSum.Add(data.Sum());
+            }
+            //确定高分组 最低分数
+            resultSum.Sort(delegate (double s1, double s2) { return s2.CompareTo(s1); });
+            for (int i = resultSum.Count - 1; i >= 0; i--)
+            {
+                if (resultSum[i] == 0)
+                {
+                    resultSum.Remove(resultSum[i]);
+                }
+            }
+            double rhwCount = resultSum.Count * 0.27;
+            double rhw = resultSum[int.Parse(rhwCount.ToString("0"))];
+            //确定低分组 最高分数
+            resultSum.Sort(delegate (double s1, double s2) { return s1.CompareTo(s2); });
+            double rhlCount = resultSum.Count * 0.27;
+            double rhl = resultSum[int.Parse(rhwCount.ToString("0"))];
+
+            List<string> knowledgeName = new List<string>();
+            foreach (string cla in knowledge)
+            {
+                knowledgeName.Add(cla);
+            }
+            List<string> areaName = new List<string>();
+            foreach (string cla in area)
+            {
+                areaName.Add(cla);
+            }
+            //初始化年级总分
+            double total = 0;
+            //处理年级单个知识点得分率
+            foreach (List<double> grade in result)
+            {
+                total += grade.Sum();
+            }
+            foreach (string id in ids)
+            {
+                if (!id.Equals("0"))
+                {
+                    Qnum++;
+                }
+            }
+            //试卷总分
+            double TotalPoint = point.Sum();
+            //计算认知层次占比
+
+            List<string> fper = new List<string>();
+            for (int a = 0; a < areaName.Count; a++)
+            {
+                double fieldPoint = 0;
+                int no = 0;
+                info.papers[index].field.ForEach(f =>
+                {
+                    if (f.ToString().Equals(areaName[a], StringComparison.OrdinalIgnoreCase))
+                    {
+                        exam.studentScores.ForEach(s =>
+                        {
+                            fieldPoint += s[no];
+                        });
+                    }
+                    no++;
+                });
+                double fieldPersent = fieldPoint / TotalPoint;
+                fper.Add(fieldPersent.ToString("0.00"));
+            }
+
+            for (int k = 0; k < knowledgeName.Count; k++)
+            {
+                List<string> knowledgeClass = new List<string>();
+                List<List<string>> stuPersent = new List<List<string>>();
+                double OnePoint = 0;
+                //获取每个知识点占有多少分值
+                double ePoint = 0;
+                double anwGPoint = 0;
+                double poG = 0;
+                int m = 0;
+                List<string> valuew = new List<string>();
+                List<string> itemNo = new List<string>();
+                keyWrong.ForEach(x =>
+                {
+                    valuew.Add("-");
+                });
+                int wrongCount = 0;
+                valuew[0] = knowledgeName[k];
+                int n = 0;
+                info.papers[index].field.ForEach(kno =>
+                {
+                    if (kno.ToString().Equals(knowledgeName[k]))
+                    {
+                        OnePoint += point[n];
+                        itemNo.Add((n + 1).ToString());
+
+                    }
+                    valuew[1] = OnePoint.ToString();
+                    string itemNos = "";
+                    if (itemNo.Count > 0)
+                    {
+                        foreach (string np in itemNo)
+                        {
+                            itemNos += np + ",";
+                        }
+                        valuew[2] = itemNos[0..^1];
+                    }
+                    else
+                    {
+                        valuew[2] = itemNos;
+                    }
+                    n++;
+                });
+                int rhwC = 0;
+                int rhlC = 0;
+                foreach (string id in ids)
+                {
+                    if (id.Equals("0") || result[m].Sum() == 0)
+                    {
+                        m++;
+                        continue;
+                    }
+                    List<string> values = new List<string>();
+                    key.ForEach(x =>
+                    {
+                        values.Add("-");
+                    });
+                    /*                        foreach (OldStudent info in students)
+                                            {
+                                                if (info.studentId.Equals(id))
+                                                {
+                                                    values[2] = info.seatNo.ToString();
+                                                    break;
+                                                }
+                                            }*/
+                    //List<string> stu = new List<string>();
+                    values[0] = id;
+                    values[1] = "-";
+                    //List<List<string>> stuItem = new List<List<string>>();
+                    int statuCount = 0;
+                    info.papers[index].field.ForEach(kno => {
+                        int sno = 0;
+                        double anwPoint = 0;
+                        double po = 0;
+                        if (kno.ToString().Equals(knowledgeName[k]))
+                        {
+                            //当前认知层次在该题占比多少
+                            ePoint = 1;
+                            anwPoint += result[m][sno] * ePoint;
+                            //所有学生单个认知层次得分情况
+                            anwGPoint += result[m][sno] * ePoint;
+                            po += point[sno] * ePoint;
+                            //认知层次所占分数
+                            poG += point[sno] * ePoint;
+                            if (result[m][sno] == 0 && statuCount == 0)
+                            {
+                                statuCount++;
+                                wrongCount++;
+                                if (result[m].Sum() >= rhw)
+                                {
+                                    rhwC++;
+                                }
+                                if (result[m].Sum() <= rhl)
+                                {
+                                    rhlC++;
+                                }
+                            }
+                            if (po == 0)
+                            {
+                                values[3] = "0";
+                                values[4] = "0";
+                                values[5] = "0";
+                            }
+                            else
+                            {
+                                double stuPser = anwPoint / po;
+                                values[3] = po.ToString();
+                                values[4] = anwPoint.ToString();
+                                values[5] = stuPser.ToString("0.00");
+                            }
+                        }
+                    });
+                    stuPersent.Add(values);
+                    m++;
+                }
+                //AchievementService.ReName(stuPersent, ids, classes, students);
+                double knowPser = 0;
+                if (poG == 0)
+                {
+                    knowPer.Add("0");
+                }
+                else
+                {
+                    knowPser = anwGPoint / poG;
+                    knowPer.Add(knowPser.ToString("0.00"));
+                }
+
+                //错题关系表
+                valuew[3] = knowPser.ToString("0.00");
+                valuew[4] = wrongCount.ToString();
+                valuew[5] = rhwC.ToString();
+                valuew[6] = rhlC.ToString();
+                wrongPersent.Add(valuew);
+                knowledgeMap.Add(knowledgeName[k], stuPersent);
+
+                //知识点占比
+                double persent = OnePoint / TotalPoint;
+                foreach (ClassRange classRange in exam.classes)
+                {
+                    List<string> classPoints = new List<string>();
+                    //初始化班级得分                   
+                    double anwCPoint = 0;
+                    double cpo = 0;
+                    for (int stuIndex = classRange.range[0]; stuIndex < classRange.range[1]; stuIndex++)
+                    {
+
+                        if (result[stuIndex].Sum() == 0) continue;
+                        info.papers[index].field.ForEach(kno => {
+                            int x = 0;
+                            if (kno.ToString().Equals(knowledgeName[k]))
+                            {
+                                //当前认知层次在该题占比多少
+                                ePoint = 1;
+                                anwCPoint += result[stuIndex][x] * ePoint;
+                                cpo += point[x] * ePoint;
+                            }
+                        });
+
+                    }
+                    if (cpo == 0)
+                    {
+                        //double classPser = anwCPoint / cpo;
+                        knowledgeClass.Add("0");
+                    }
+                    else
+                    {
+                        double classPser = anwCPoint / cpo;
+                        knowledgeClass.Add(classPser.ToString("0.00"));
+                    }
+
+                }
+                classMap.Add(knowledgeName[k], knowledgeClass);
+                per.Add(persent.ToString("0.00"));
+
+            }
+            knowledgeMap.Add("grade", knowPer);
+            knowledgeMap.Add("keys", key);
+            classMap.Add("className", className);
+            wrongMap.Add("keys", keyWrong);
+            wrongMap.Add("datas", wrongPersent);
+            knowledgeALL.Add("pointList", knowledgeName);
+            knowledgeALL.Add("per", per);
+            knowledgeALL.Add("level", areaName);
+            knowledgeALL.Add("fper", fper);
+            knowledgeALL.Add("stupercent", knowledgeMap);
+            knowledgeALL.Add("classpercent", classMap);
+            knowledgeALL.Add("wrong", wrongMap);
+            return knowledgeALL;
+        }
+
     }
 }

+ 1 - 1
TEAMModelOS/Controllers/Common/ExamController.cs

@@ -35,7 +35,7 @@ namespace TEAMModelOS.Controllers
         private readonly AzureServiceBusFactory _serviceBus;
         private readonly DingDing _dingDing;
         private readonly Option _option;
-        public readonly AzureStorageFactory _azureStorage;
+        private readonly AzureStorageFactory _azureStorage;
 
         public ExamController(AzureCosmosFactory azureCosmos, AzureServiceBusFactory serviceBus, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option, AzureStorageFactory azureStorage)
         {

+ 32 - 8
TEAMModelOS/Controllers/Core/BlobController.cs

@@ -69,7 +69,7 @@ namespace TEAMModelOS.Controllers.Core
         /// <param name="request"></param>
         /// <returns></returns>
         [HttpPost("delete-prefix")]
-        [AuthToken(Roles = "teacher")]
+        [AuthToken(Roles = "teacher,admin")]
         public async Task<IActionResult> DeletePrefix(JsonElement json)
         {
             var (id,_,_,school) = HttpContext.GetAuthTokenInfo();
@@ -86,6 +86,10 @@ namespace TEAMModelOS.Controllers.Core
                 {
                     blobContainerName = school;
                 }
+                else
+                {
+                    return BadRequest("只能删除本人管理的文件夹");
+                }
             }
             if (json.TryGetProperty("prefix", out JsonElement prefixjson))
             {
@@ -110,15 +114,15 @@ namespace TEAMModelOS.Controllers.Core
         /// <param name="request"></param>
         /// <returns></returns>
         [HttpPost("delete-blobs")]
+        [AuthToken(Roles = "teacher,admin")]
         public async Task<IActionResult> DeleteBlobs(JsonElement json)
         {
-            var (id, _, _, school) = HttpContext.GetAuthTokenInfo();
+           var (id, _, _, school) = HttpContext.GetAuthTokenInfo();
             string blobContainerName = null;
             List<Uri> uris = null;
             if (json.TryGetProperty("cntr", out JsonElement cntr))
             {
-                var cntrs = cntr.ToString();
-
+                var  cntrs = cntr.ToString();
                 if (cntrs.Equals(id))
                 {
                     blobContainerName = id;
@@ -127,18 +131,38 @@ namespace TEAMModelOS.Controllers.Core
                 {
                     blobContainerName = school;
                 }
+                else {
+                    return BadRequest("只能删除本人管理的文件");
+                }
             }
+            bool flag = true;
             if (json.TryGetProperty("urls", out JsonElement urlsjson)) {
                 uris= urlsjson.ToObject<List<Uri>>();
+                uris.ForEach(x => {
+                    (string, string) a = BlobUrlString(x.ToString());
+                    string ContainerName = a.Item1;
+                    string BlobName = a.Item2;
+                    if (ContainerName!=blobContainerName) {
+                        flag = false;
+                    }
+                });
             }
-            if (blobContainerName != null && uris != null && uris.Count > 0)
+            if (flag)
             {
-                var status = await _azureStorage.GetBlobServiceClient().DelectBlobs("", new List<Uri>());
-                return Ok(new { });
+                if (blobContainerName != null && uris != null && uris.Count > 0)
+                {
+                    var status = await _azureStorage.GetBlobServiceClient().DelectBlobs(blobContainerName, uris);
+                    return Ok(new { status });
+                }
+                else
+                {
+                    return BadRequest();
+                }
             }
             else {
-                return BadRequest();
+                return BadRequest("只能删除本人管理的文件");
             }
+            
         }
 
 

+ 2 - 2
TEAMModelOS/Controllers/Core/CoreController.cs

@@ -80,12 +80,12 @@ namespace TEAMModelOS.Controllers.Core
                     }
                     else
                     {
-                        respons.Add(new { base64 = obase64, blob = "" }); //異常的圖片,原Base64返回
+                        respons.Add(new { base64 = item.base64, blob = "" }); //異常的圖片,原Base64返回
                     }
                 }
                 else
                 {
-                    respons.Add(new { base64 = obase64, blob = "" }); //不符合的圖片,原Base64返回
+                    respons.Add(new { base64 = item.base64, blob = "" }); //不符合的圖片,原Base64返回
                 }
             }
             return Ok(respons);

+ 52 - 20
TEAMModelOS/Controllers/Core/ImportController.cs

@@ -71,7 +71,24 @@ namespace TEAMModelOS.Controllers
             string text = builder.ToString();
             langConfigs = text.ToObject<List<LangConfig>>();
         }
+        private static string ReplaceLast(string input, string oldValue, string newValue)
+        {
+            int index = input.LastIndexOf(oldValue);
+            if (index < 0)
+            {
+                return input;
+            }
+            else
+            {
+                StringBuilder sb = new StringBuilder(input.Length - oldValue.Length + newValue.Length);
+                sb.Append(input.Substring(0, index));
+                sb.Append(newValue);
+                sb.Append(input.Substring(index + oldValue.Length,
+                   input.Length - index - oldValue.Length));
 
+                return sb.ToString();
+            }
+        }
 
         /// <summary>
         ///  {"file":"www....xxxx.pptx","scope":"private/school"}
@@ -105,8 +122,8 @@ namespace TEAMModelOS.Controllers
             bool flg = IsBlobName(BlobName);
             var codes = azureBlobSAS.Split("/");
             var file = codes[codes.Length - 1].Split(".");
-            var FileName = file[0];
-            var ext = file[1];
+            var ext = file[file.Length - 1];
+            var FileName = ReplaceLast(codes[codes.Length - 1], "." + ext, "");
             if (flg)
             {
                 BlobAuth blobAuth = _azureStorage.GetBlobSasUriRead(ContainerName, BlobName);
@@ -142,30 +159,45 @@ namespace TEAMModelOS.Controllers
             //处理中文乱码问题
             Encoding encoding = Encoding.GetEncoding("GB2312");
             var options = new ReadOptions {  Encoding = encoding };
-            string index ="";
-            using (ZipFile zip = ZipFile.Read(stream, options))
+            string index=null;
+            bool hasindex = false;
+            List<Task<string>> tasks = new List<Task<string>>();
+            ZipFile zip = ZipFile.Read(stream, options);
+            zip.AlternateEncoding = encoding;
+            List<Stream> streams = new List<Stream>();
+            foreach (var f in zip.Entries)
             {
-                zip.AlternateEncoding = encoding;
-                foreach (var f in zip.Entries)
+                string name = FileName +"/"+ f.FileName;
+                if (f.IsDirectory)
                 {
-                    string name = f.FileName;
-                    if (f.IsDirectory)
-                    {
-                        continue;
-                    }
-                    var uploadStream = f.OpenReader();
-                    var url= await _azureStorage.UploadFileByContainer(containerid, uploadStream, "res", $"{name}", false);
-                    url= System.Web.HttpUtility.UrlDecode(url, Encoding.UTF8);
-                    if (url.Contains($"{containerid}/res/{FileName}/index.json")) {
-                        index = url;
-                    }
-                    uploadStream.Close();
+                    continue;
+                }
+                var uploadStream = f.OpenReader();
+                byte[] buffer = new byte[uploadStream.Length];
+                uploadStream.Read(buffer, 0, buffer.Length);
+                Stream blobstream = new MemoryStream(buffer);
+                streams.Add(blobstream);
+                tasks.Add(  _azureStorage.UploadFileByContainer(containerid, blobstream, "res", $"{name}", false));
+                if (name.Contains($"{FileName}/index.json")) {
+                    hasindex = true; 
                 }
+                uploadStream.Close();
             }
+           
+            zip.Dispose();
             stream.Close();
-            if (string.IsNullOrWhiteSpace(index)) {
-                await _azureStorage.GetBlobServiceClient().DelectBlobs(containerid, $"res/{FileName}");
+            if (hasindex) {
+                await  Task.WhenAll(tasks);
+                foreach (var task in tasks)
+                {
+                    var url= System.Web.HttpUtility.UrlDecode(task.Result, Encoding.UTF8);
+                    if (url.Contains($"{FileName}/index.json")) {
+                        index = url;
+                    }
+                }
             }
+            //释放资源
+            streams.ForEach(x => { x.Close(); });
             return index;
         }
 

+ 29 - 1
TEAMModelOS/Controllers/Item/ItemController.cs

@@ -610,8 +610,36 @@ namespace TEAMModelOS.Controllers
                             itemInfos.Remove(itemInfo);
                         }
                     }
+                   
+                    List<ItemInfo> restItem = new List<ItemInfo>();
+                    //处理综合题问题
+                    foreach (var item in retnInfos) {
+                        if (!string.IsNullOrWhiteSpace(item.pid))
+                        {
+                            if (item.scope == "school")
+                            {
+                                var iteme = await client.GetContainer("TEAMModelOS", "School").ReadItemAsync<ItemInfo>(item.id, new PartitionKey(item.code));
+                                if (iteme != null) {
+                                    restItem.Add(iteme.Value);
+                                }
+                               
+                            }
+                            else if (item.scope == "private") {
+                                var iteme = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<ItemInfo>(item.id, new PartitionKey(item.code));
+                                if (iteme != null)
+                                {
+                                    restItem.Add(iteme.Value);
+                                }
+                            }
+                        }
+                        else {
+                            restItem.Add(item);
+                        }
+                    }
                     List<List<ItemInfo>> listInfo = new List<List<ItemInfo>>();
-                    foreach (IGrouping<string, ItemInfo> group in retnInfos.GroupBy(c => c.type))
+                    //处理综合题id重复
+                    restItem = restItem.Where((x, i) => restItem.FindIndex(z => z.id == x.id) == i).ToList();
+                    foreach (IGrouping<string, ItemInfo> group in restItem.GroupBy(c => c.type))
                     {
                         Dictionary<string, object> dictInfo = new Dictionary<string, object>();
                         listInfo.Add(group.ToList());

+ 68 - 2
TEAMModelOS/Controllers/Teacher/CommentController.cs

@@ -13,6 +13,8 @@ using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Helper.Common.CollectionHelper;
 using TEAMModelOS.SDK.Helper.Common.StringHelper;
+using TEAMModelOS.Models;
+using Microsoft.Extensions.Options;
 
 namespace TEAMModelOS.Controllers
 {
@@ -24,9 +26,15 @@ namespace TEAMModelOS.Controllers
     public class CommentController :ControllerBase
     {
         private readonly AzureCosmosFactory _azureCosmos;
-        public CommentController(AzureCosmosFactory azureCosmos)
+        private readonly DingDing _dingDing;
+        private readonly Option _option;
+        private readonly AzureStorageFactory _azureStorage;
+        public CommentController(AzureCosmosFactory azureCosmos, DingDing dingDing, IOptionsSnapshot<Option> option, AzureStorageFactory azureStorage)
         {
             _azureCosmos = azureCosmos;
+            _dingDing = dingDing;
+            _option = option?.Value;
+            _azureStorage = azureStorage;
         }
 
         /// <summary>
@@ -123,6 +131,64 @@ namespace TEAMModelOS.Controllers
             return Ok();
         }
 
-         
+        //批注
+        [ProducesDefaultResponseType]
+        //[AuthToken(Roles = "Teacher")]
+        [HttpPost("upsert-answer")]
+        public async Task<IActionResult> upsertAnswer(JsonElement request)
+        {
+
+            if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
+            //if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
+            if (!request.TryGetProperty("answer", out JsonElement answer)) return BadRequest();
+            if (!request.TryGetProperty("studentId", out JsonElement studentId)) return BadRequest();
+            if (!request.TryGetProperty("subjectId", out JsonElement subjectId)) return BadRequest();
+            if (!request.TryGetProperty("classId", out JsonElement classId)) return BadRequest();
+            //if (!request.TryGetProperty("multipleRule", out JsonElement multipleRule)) return BadRequest();
+            //if (!request.TryGetProperty("answers ", out JsonElement tandardAnswer)) return BadRequest();
+            //if (!request.TryGetProperty("paperId", out JsonElement paperId)) return BadRequest();
+            //根据不同评测的类型返回对应的编码
+            if (!request.TryGetProperty("code", out JsonElement school)) return BadRequest();
+            try
+            {
+                var client = _azureCosmos.GetCosmosClient();
+                List<ExamClassResult> examClassResults = new List<ExamClassResult>();
+                await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(
+                    queryText: $"select value(c) from c where c.examId = '{id}' and c.subjectId = '{subjectId}' and c.info.id = '{classId}'",
+                    requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{school}") }))
+                {
+                    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>());
+                        }
+                    }
+                }
+                ExamClassResult classResult = new ExamClassResult();              
+                List<List<string>> ans = answer.ToObject<List<List<string>>>();
+                List<List<string>> standard = new List<List<string>>();
+                List<double> points = new List<double>();                
+                foreach (ExamClassResult result in examClassResults)
+                {
+                    int index = result.studentIds.IndexOf(studentId.ToString());
+                    string FileName = result.examId + "/" + result.subjectId + "/" + studentId;
+                    string blob = await _azureStorage.UploadFileByContainer(school.ToString(), ans.ToJsonString(), "exam", FileName + "/" + "ans.json");
+                    result.studentAnswers[index].Add(blob);                  
+                    classResult = await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(result, result.id, new PartitionKey($"{result.code}"));
+                }
+
+                return Ok(new { classResult });
+            }
+            catch (Exception e)
+            {
+                await _dingDing.SendBotMsg($"OS,{_option.Location},teacher/comment/upsertAnswer()\n{e.Message}", GroupNames.醍摩豆服務運維群組);
+                return BadRequest();
+            }
+
+        }
+
+
     }
 }

+ 8 - 4
TEAMModelOS/Controllers/knowledge/KnowledgeController.cs

@@ -175,7 +175,7 @@ namespace TEAMModelOS.Controllers
                  }
              }*/
             var query = $"select c.id,c.type,c.code, c.name,c.alias,c.subjectId,c.points,c.knowledgeId,c.periodId from c where c.type = {type} and c.periodId = '{period}' and c.subjectId = '{subjectCode}'";
-            await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Knowledge-{school_code}") }))
+            await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Knowledge-{school_code}") }))
             {
                 using var json = await JsonDocument.ParseAsync(item.ContentStream);
                 
@@ -244,13 +244,16 @@ namespace TEAMModelOS.Controllers
 
                     foreach (Knowledge knowledge in request) {
                         knowledge.code = "Knowledge-" + knowledge.code;
+                        Knowledge kno = new Knowledge();
                         if (knowledge.id == null)
                         {
                             knowledge.id = Guid.NewGuid().ToString();
-                            await client.GetContainer("TEAMModelOS", "School").CreateItemAsync(knowledge, new PartitionKey($"{knowledge.code}"));
+                            kno = await client.GetContainer("TEAMModelOS", "School").CreateItemAsync(knowledge, new PartitionKey($"{knowledge.code}"));
+                            Knowledges.Add(kno);
                         }
                         else {
-                            await client.GetContainer("TEAMModelOS", "School").ReplaceItemAsync(knowledge, knowledge.id, new PartitionKey($"{knowledge.code}"));
+                            kno = await client.GetContainer("TEAMModelOS", "School").ReplaceItemAsync(knowledge, knowledge.id, new PartitionKey($"{knowledge.code}"));
+                            Knowledges.Add(kno);
                         }
                     }
                     //Knowledges = await SaveOrUpdateKnowledge(request);
@@ -285,6 +288,7 @@ namespace TEAMModelOS.Controllers
                 if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
                 if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
                 await client.GetContainer("TEAMModelOS", "School").DeleteItemStreamAsync(id.ToString(), new PartitionKey($"Knowledge-{code}"));
+                return Ok(id);
             } catch (Exception ex) {
                 await _dingDing.SendBotMsg($"knowledge,{_option.Location},delete()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
                 return BadRequest();
@@ -357,7 +361,7 @@ namespace TEAMModelOS.Controllers
 
                 List<Knowledge> knowlegeo = new List<Knowledge>();
                 var queryOne = $"select c.id,c.type,c.code, c.name,c.alias,c.subjectId,c.points,c.knowledgeId,c.periodId from c  where c.type = 0 and c.status = 1";
-                await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: queryOne, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Knowledge-{code}") }))
+                await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: queryOne, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Knowledge-{code}") }))
                 {
                     using var json = await JsonDocument.ParseAsync(item.ContentStream);
 

+ 1 - 1
TEAMModelOS/TEAMModelOS.csproj

@@ -7,7 +7,7 @@
     <PackageReference Include="Caching.CSRedis" Version="3.6.50" />
     <PackageReference Include="CSRedisCore" Version="3.6.5" />
     <PackageReference Include="DotNetZip" Version="1.15.0" />
-    <PackageReference Include="HTEXLib" Version="2.1.6" />
+    <PackageReference Include="HTEXLib" Version="2.2.3" />
     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.6" />
       <PackageReference Include="VueCliMiddleware" Version="3.1.2" />  </ItemGroup>
   <PropertyGroup>