Bläddra i källkod

Merge branch 'develop5.0-tmd' of http://106.12.23.251:10000/TEAMMODEL/TEAMModelOS into develop5.0-tmd

XW 4 år sedan
förälder
incheckning
43e0f0a064
33 ändrade filer med 81183 tillägg och 370 borttagningar
  1. 1 1
      TEAMModelFunction/TriggerCorrect.cs
  2. 29 6
      TEAMModelFunction/TriggerExam.cs
  3. 4 1
      TEAMModelOS.SDK/Models/Cosmos/Common/Bloblog.cs
  4. 14827 0
      TEAMModelOS/ClientApp/public/pdfBuild/pdf.js
  5. 1 0
      TEAMModelOS/ClientApp/public/pdfBuild/pdf.js.map
  6. 313 0
      TEAMModelOS/ClientApp/public/pdfBuild/pdf.sandbox.js
  7. 1 0
      TEAMModelOS/ClientApp/public/pdfBuild/pdf.sandbox.js.map
  8. 65293 0
      TEAMModelOS/ClientApp/public/pdfBuild/pdf.worker.js
  9. 1 0
      TEAMModelOS/ClientApp/public/pdfBuild/pdf.worker.js.map
  10. 1 1
      TEAMModelOS/ClientApp/public/web/viewer.html
  11. 5 5
      TEAMModelOS/ClientApp/public/web/viewer.js
  12. 1 1
      TEAMModelOS/ClientApp/public/web/viewer.js.map
  13. 3 2
      TEAMModelOS/ClientApp/src/components/syllabus/InviteTeacher.less
  14. 188 16
      TEAMModelOS/ClientApp/src/components/syllabus/InviteTeacher.vue
  15. 0 47
      TEAMModelOS/ClientApp/src/utils/html2pdf.js
  16. 0 1
      TEAMModelOS/ClientApp/src/view/answersheet/BaseEditor.vue
  17. 8 4
      TEAMModelOS/ClientApp/src/view/answersheet/SheetBaseInfo.vue
  18. 4 9
      TEAMModelOS/ClientApp/src/view/learnactivity/markpaper/MarkSetting.vue
  19. 0 6
      TEAMModelOS/ClientApp/src/view/student-account/Index.vue
  20. 14 2
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/EvaluationList/TotalIndex.vue
  21. 7 7
      TEAMModelOS/ClientApp/src/view/syllabus/Syllabus.vue
  22. 13 5
      TEAMModelOS/ClientApp/src/view/task/index.vue
  23. 32 13
      TEAMModelOS/ClientApp/src/view/task/mark/ByQu.vue
  24. 131 43
      TEAMModelOS/ClientApp/src/view/task/mark/ByStu.vue
  25. 53 59
      TEAMModelOS/ClientApp/src/view/task/mark/MarkCanvas.vue
  26. 6 3
      TEAMModelOS/ClientApp/src/view/task/mark/ProgPie.vue
  27. 11 3
      TEAMModelOS/ClientApp/src/view/task/mark/StuProg.vue
  28. 2 2
      TEAMModelOS/ClientApp/src/view/teachcontent/ResBelong.vue
  29. 9 3
      TEAMModelOS/ClientApp/src/view/teachcontent/index.vue
  30. 47 35
      TEAMModelOS/Controllers/Common/ExamController.cs
  31. 7 3
      TEAMModelOS/Controllers/Core/BlobController.cs
  32. 148 85
      TEAMModelOS/Controllers/Syllabus/SyllabusController.cs
  33. 23 7
      TEAMModelOS/Controllers/Teacher/CommentController.cs

+ 1 - 1
TEAMModelFunction/TriggerCorrect.cs

@@ -134,7 +134,7 @@ namespace TEAMModelFunction
                                     foreach (string stuId in examClass.studentIds)
                                     {
                                         int index = examClass.studentIds.IndexOf(stuId);
-                                        tasks.Add(redisClient.HashSetAsync($"Exam:Scoring:{eid}-{subjectId}", stuId, new { tmdId = "" , ans = examClass.studentAnswers[index][0], score = examClass.studentScores[index] }.ToJsonString()));
+                                        tasks.Add(redisClient.HashSetAsync($"Exam:Scoring:{eid}-{subjectId}", stuId, new { tmdId = "" , ans = examClass.studentAnswers[index].Count > 0 ? examClass.studentAnswers[index][0] : "", score = examClass.studentScores[index] }.ToJsonString()));
                                     }
 
                                 }

+ 29 - 6
TEAMModelFunction/TriggerExam.cs

@@ -206,11 +206,31 @@ namespace TEAMModelFunction
                                         var sresponse = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(cla, new Azure.Cosmos.PartitionKey($"Class-{info.school}"));
                                         if (sresponse.Status == 200)
                                         {
+
                                             using var json = await JsonDocument.ParseAsync(sresponse.ContentStream);
                                             Class classroom = json.ToObject<Class>();
+                                            School sc = await client.GetContainer("TEAMModelOS", "School").ReadItemAsync<School>(info.school, new Azure.Cosmos.PartitionKey("Base"));
+                                            foreach (Period period in sc.period)
+                                            {
+                                                if (period.id.Equals(classroom.periodId))
+                                                {
+                                                    foreach (Semester semester in period.semesters)
+                                                    {
+                                                        if (semester.start == 1)
+                                                        {
+                                                            int year = DateTimeOffset.UtcNow.Year;
+                                                            int month = DateTimeOffset.UtcNow.Month;
+                                                            int day = DateTimeOffset.UtcNow.Day;
+                                                            int time =  month > semester.month ?  0 :  1;
+                                                            int eyear = year - time;
+                                                            result.gradeId = (year - eyear).ToString();
+                                                        }
+                                                    }
+                                                }
+                                            }
                                             //result.info.id = classroom.id;
                                             result.info.name = classroom.name;
-                                            result.gradeId = classroom.gradeId;
+                                            //result.gradeId = classroom.year.ToString();
                                             //处理班级人数
                                             await foreach (var item in client.GetContainer("TEAMModelOS", "Student").GetItemQueryStreamIterator(queryText: $"select c.id from c where c.classId = '{classroom.id}'", requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Base-{info.school}") }))
                                             {
@@ -535,7 +555,8 @@ namespace TEAMModelFunction
                 int plcount = 0;
                 //存放并去重知识点
                 HashSet<string> kname = new HashSet<string>();
-                if (info.papers[no].knowledge.Count > 0) {
+                if (info.papers[no].knowledge.Count > 0)
+                {
                     info.papers[no].knowledge.ForEach(kno =>
                     {
                         kno.ForEach(k =>
@@ -631,7 +652,7 @@ namespace TEAMModelFunction
                         //await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(classResult, classResult.id, new Azure.Cosmos.PartitionKey($"{classResult.code}"));
                     }
                 }
-                
+
             }
             catch (Exception ex)
             {
@@ -672,7 +693,8 @@ namespace TEAMModelFunction
                             int phCount = 0;
                             int plCount = 0;
                             int pCount = 0;
-                            if (info.papers[no].field.Count > 0) {
+                            if (info.papers[no].field.Count > 0)
+                            {
                                 foreach (int str in info.papers[no].field)
                                 {
                                     if (str == knowledgeName[i])
@@ -720,7 +742,7 @@ namespace TEAMModelFunction
                                 pl.Add(plCount);
                                 double per = classResult.studentIds.Count > 0 ? Math.Round(score / classResult.studentIds.Count, 2) : 0;
                                 persent.Add(allScore > 0 ? per / allScore : 0);
-                            }                           
+                            }
                         }
                         classResult.fphc = ph;
                         classResult.fplc = pl;
@@ -858,7 +880,8 @@ namespace TEAMModelFunction
                         csRate.Add(classResult.studentIds.Count > 0 ? Math.Round(classSrate * 1.0 / classResult.studentIds.Count, 2) : 0 / allScore);
 
                     }
-                    else {
+                    else
+                    {
                         csRate.Add(0);
                     }
 

+ 4 - 1
TEAMModelOS.SDK/Models/Cosmos/Common/Bloblog.cs

@@ -13,10 +13,13 @@ namespace TEAMModelOS.SDK.Models
         public string url { get; set; }
         public long  time { get; set; }
         public long  size { get; set; }
-        public string period { get; set; }
+        public string periodId { get; set; }
+        public string subjectId { get; set; }
+        public string gradeId { get; set; }
         /// <summary>
         /// audio 音频,video 视频 ,doc文档,image图片,other 其他,res教材,thum缩略图
         /// </summary>
         public string type { get; set; }
+
     }
 }

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 14827 - 0
TEAMModelOS/ClientApp/public/pdfBuild/pdf.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
TEAMModelOS/ClientApp/public/pdfBuild/pdf.js.map


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 313 - 0
TEAMModelOS/ClientApp/public/pdfBuild/pdf.sandbox.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
TEAMModelOS/ClientApp/public/pdfBuild/pdf.sandbox.js.map


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 65293 - 0
TEAMModelOS/ClientApp/public/pdfBuild/pdf.worker.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
TEAMModelOS/ClientApp/public/pdfBuild/pdf.worker.js.map


+ 1 - 1
TEAMModelOS/ClientApp/public/web/viewer.html

@@ -34,7 +34,7 @@ See https://github.com/adobe-type-tools/cmap-resources
 
 <!-- This snippet is used in production (included from viewer.html) -->
 <link rel="resource" type="application/l10n" href="locale/locale.properties">
-<script src="../build/pdf.js"></script>
+<script src="../pdfBuild/pdf.js"></script>
 
 
   <script src="viewer.js"></script>

+ 5 - 5
TEAMModelOS/ClientApp/public/web/viewer.js

@@ -206,7 +206,7 @@ const defaultOptions = {
     kind: OptionKind.WORKER
   },
   workerSrc: {
-    value: "../build/pdf.worker.js",
+    value: "../pdfBuild/pdf.worker.js",
     kind: OptionKind.WORKER
   }
 };
@@ -220,7 +220,7 @@ const defaultOptions = {
     kind: OptionKind.VIEWER
   };
   defaultOptions.sandboxBundleSrc = {
-    value: "../build/pdf.sandbox.js",
+    value: "../pdfBuild/pdf.sandbox.js",
     kind: OptionKind.VIEWER
   };
 }
@@ -3921,10 +3921,10 @@ function apiPageModeToSidebarView(mode) {
 
 let pdfjsLib;
 
-if (typeof window !== "undefined" && window["pdfjs-dist/build/pdf"]) {
-  pdfjsLib = window["pdfjs-dist/build/pdf"];
+if (typeof window !== "undefined" && window["pdfjs-dist/pdfBuild/pdf"]) {
+  pdfjsLib = window["pdfjs-dist/pdfBuild/pdf"];
 } else {
-  pdfjsLib = require("../build/pdf.js");
+  pdfjsLib = require("../pdfBuild/pdf.js");
 }
 
 module.exports = pdfjsLib;

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 1
TEAMModelOS/ClientApp/public/web/viewer.js.map


+ 3 - 2
TEAMModelOS/ClientApp/src/components/syllabus/InviteTeacher.less

@@ -18,11 +18,12 @@
 	}
 	
 	.teacher-wrap{
-		margin-top: 30px;
-		
 		.dark-iview-table .ivu-table td{
 			border-color: #494949;
 			color: #c4c4c4 !important;
 		}
 	}
+
+	
+	
 }

+ 188 - 16
TEAMModelOS/ClientApp/src/components/syllabus/InviteTeacher.vue

@@ -4,8 +4,12 @@
 			<span>当前选择章节:</span>
 			<span class="node-name">{{ nodeInfo.title }}</span>
 		</div>
+		<div class="search-wrap" v-if="isSchool">
+			<Input v-model="searchVal"  placeholder="输入教师名字或ID查询..."
+			icon="ios-close-circle-outline" @on-click="onCloseSearch" @on-change="onSearchChange"/>
+		</div>
 		<div class="teacher-wrap">
-			<Table class="dark-iview-table" :columns="teacherCol" :data="teacherList" height="500">
+			<Table class="dark-iview-table" :columns="teacherCol" :data="teacherList" height="400" v-if="isSchool">
 				<template slot-scope="{ row }" slot="name">
 					<div style="display: flex;align-items: center;">
 						<PersonalPhoto :name="row.name" :picture="row.picture" />
@@ -14,10 +18,24 @@
 				</template>
 				<template slot-scope="{ row }" slot="action">
 					<div style="display: flex;align-items: center;">
-						
+						<i-switch true-color="#13ce66"></i-switch>
 					</div>
 				</template>
 			</Table>
+			<div class="search-id-wrap" v-else>
+				<div class="id-search">
+					<Input v-model="searchIdVal" placeholder="搜索教师..." search @on-search="onIdSearch"/>
+					<p v-html="$t('teachermgmt.addTeacher.content')"></p>
+					<p v-if="curTeacher && curTeacher.id" class="search-result-text">搜索结果</p>
+					<div v-if="curTeacher && curTeacher.id" class="search-result-wrap">
+						<PersonalPhoto :name="curTeacher.name" :picture="curTeacher.picture" />
+						<p class="t-name">{{ curTeacher.name }}</p>
+						<p class="t-id">{{ curTeacher.id }}</p>
+					</div>
+					<p v-if="!curTeacher && hasSearchResult" class="search-none">暂未查询到相关结果</p>
+					<p v-if="hasSearchResult" class="re-search" @click="onReSearch">重新搜索</p>
+				</div>
+			</div>
 		</div>
 	</div>
 </template>
@@ -40,31 +58,54 @@
 		},
 		data() {
 			return {
+				isSchool:false,
+				hasSearchResult:false,
+				searchVal:'',
+				searchIdVal:'',
 				nodeInfo: {
 					title: ''
 				},
+				curTeacher:{
+					name:'',
+					picture:'',
+					id:'',
+					job:''
+				},
 				teacherCol: [{
-						title: '姓名',
+						title: this.$t('teachermgmt.table.th2'),
 						key: 'name',
 						slot: 'name'
 					},
 					{
-						title: '醍摩豆ID',
+						title: this.$t('teachermgmt.table.th1'),
 						key: 'id'
 					},
 					{
-						title: '权限',
-						key: 'size'
+					  title: this.$t('teachermgmt.table.th3'),
+					  key: 'job',
+					  render: (h, params) => {
+					    if (typeof params.row.job === 'undefined' || params.row.job == null || params.row.job === '') {
+					      return h('span', this.$t('teachermgmt.job.teacher'))
+					    } else {
+					      return h('span', params.row.job)
+					    }
+					  }
 					},
 					{
-						title: '操作',
-						slot:'action'
+						title: '是否共编',
+						key:'action',
+						slot: 'action'
 					}
 				],
-				teacherList: []
+				teacherList: [],
+				originList:[]
 			}
 		},
+		created() {
+			this.isSchool = this.$route.name === 'syllabus'
+		},
 		methods: {
+			/* 获取学校所有教师列表 */
 			getAllTeacher() {
 				this.$store.dispatch('user/getSchoolTeacher').then(
 					res => {
@@ -72,22 +113,52 @@
 							this.$Message.error('無法取得使用者資料')
 						} else {
 							this.teacherList = this.$store.state.user.schoolUserList.filter(i => i.status === 'join')
-							console.log(this.teacherList)
+							this.originList = JSON.parse(JSON.stringify(this.teacherList))
 						}
 					},
 					err => {
 						this.$Message.error('user/setSchoolTeacher API error!')
 					}
 				)
+			},
+			/* 搜索词汇发生变化 */
+			onSearchChange(){
+				this.teacherList = this.originList.filter(i => i.name.indexOf(this.searchVal) > -1 || i.id.indexOf(this.searchVal) > -1)
+			},
+			/* 关闭搜索 */
+			onCloseSearch(){
+				this.searchVal = ''
+				this.teacherList = JSON.parse(JSON.stringify(this.originList))
+			},
+			/* 根据ID邮箱手机号搜索教师信息 */
+			onIdSearch(){
+				this.$store.dispatch('user/getUserFromCoreId', [this.searchIdVal]).then(
+				    (res) => {
+				        if (res.code == 1) {
+				            this.curTeacher = res.data.length ? res.data[0] : null
+				        } else {
+				            this.$Message.error('user/getUserFromCoreId API error!')
+				        }
+						this.hasSearchResult = true
+				    },
+				    (err) => {
+				        this.$Message.error('user/getUserFromCoreId API error!')
+				    }
+				)
+			},
+			/* 重新搜索 */
+			onReSearch(){
+				this.searchIdVal = ''
+				this.curTeacher = null
+				this.hasSearchResult = false
 			}
 		},
 		mounted() {
-			this.getAllTeacher()
+			this.isSchool && this.getAllTeacher()
 		},
 		watch: {
 			node: {
 				handler(n, o) {
-					console.log('xxxxxxx', n)
 					if (n) {
 						this.nodeInfo = n
 					}
@@ -100,12 +171,113 @@
 
 <style lang="less" scoped src="./InviteTeacher.less"></style>
 <style lang="less">
-	.teacher-wrap {
-		.dark-iview-table .ivu-table td {
-			border-color: #494949;
-			color: #c4c4c4 !important;
+	.it-container{
+		.teacher-wrap {
+			.dark-iview-table .ivu-table td {
+				border-color: #494949;
+				color: #c4c4c4 !important;
+			}
 		}
 		
+		.search-wrap{
+			.ivu-input-wrapper{
+				width: 300px;
+				line-height: 35px;
+			}
+			.ivu-input{
+				background-color: #474747;
+				border-color: #515151;
+				font-size: 12px;
+				color: #ddd;
+				margin: 20px 0;
+				border-radius: 50px;
+				padding-left: 10px;
+				height: 35px;
+			}
+			
+			.ivu-input-icon{
+				line-height: 75px;
+			}
+		}
 		
+		.search-id-wrap{
+			margin-top: 20px;
+			color: #adadad;
+			.point{
+				color: #F1F1F1;
+				font-weight: bold;
+				font-size: 16px;
+				margin: 0 10px;
+			}
+			
+			.id-search{
+				height: 400px;
+				width: 100%;
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+				
+				.ivu-input-wrapper{
+					width: 500px;
+					line-height: 35px;
+				}
+				.ivu-input{
+					background-color: #474747;
+					border-color: #515151;
+					font-size: 14px;
+					color: #ddd;
+					margin: 20px 0;
+					border-radius: 50px;
+					padding-left: 20px;
+					height: 45px;
+				}
+				
+				.ivu-input-icon{
+					line-height: 86px;
+					margin-right: 10px;
+				}
+			}
+			
+			.search-result-text{
+				margin-top: 60px;
+				margin-bottom: 10px;
+			}
+			
+			.search-result-wrap{
+				background-color: #4e4e4e;
+				padding: 20px;
+				border-radius: 10px;
+				display: flex;
+				flex-direction: column;
+				justify-content: center;
+				align-items: center;
+				.avatar , .fakeAvatar{
+					width: 60px !important;
+					height: 60px !important;
+					border-radius: 50%;
+				}
+				.t-name{
+					color: #fff;
+					font-size: 16px;
+					font-weight: bold;
+					margin: 10px 0;
+				}
+			}
+			
+			.search-none{
+				color: #ff4f80;
+				font-size: 16px;
+				font-weight: bold;
+				margin-top: 80px;
+			}
+			
+			.re-search{
+				margin: 20px 0;
+				text-decoration: underline;
+				color: #FFFCFC;
+				font-size: 14px;
+				cursor: pointer;
+			}
+		}
 	}
 </style>

+ 0 - 47
TEAMModelOS/ClientApp/src/utils/html2pdf.js

@@ -4,14 +4,8 @@ import store from '@/store'
 import domtoimage from './dom_to_image';
 export default {
     install(Vue, options) {
-        Vue.prototype.getPdfByImg = () => {
-
-        }
-
         Vue.prototype.getPdf = () => {
 			return new Promise((r,j) => {
-				
-			
             var title = store.state.answerSheet.paperItem.name
             setTimeout(() => {
                 domtoimage.toJpeg(document.querySelector('#pdfDom'), { bgcolor: '#fff', scale: 4 })
@@ -55,52 +49,11 @@ export default {
                             PDF.save(title + '.pdf')
 							r(200)
                         }
-
                     })
                     .catch(function (error) {
                         console.error('oops, something went wrong!', error);
 						j(error)
                     });
-
-                // html2canvas( document.querySelector('#pdfDom'), {
-                //     allowTaint: true,  //允许 canvas 污染, allowTaint参数要去掉,否则是无法通过toDataURL导出canvas数据的
-                //     useCORS:true  //允许canvas画布内 可以跨域请求外部链接图片, 允许跨域请求。
-                // } ).then( (canvas)=>{
-                //     var contentWidth = canvas.width;
-                //     var contentHeight = canvas.height;
-                //     console.log(contentWidth)
-                //     console.log(contentHeight)
-                //     //一页pdf显示html页面生成的canvas高度;
-                //     var pageHeight = contentWidth / 592.28 * 841.89;
-                //     //未生成pdf的html页面高度
-                //     var leftHeight = contentHeight;
-                //     //页面偏移
-                //     var position = 0;
-                //     //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
-                //     var imgWidth = 595.28;
-                //     var imgHeight = 595.28/contentWidth * contentHeight;
-                //     var pageData = canvas.toDataURL('image/jpeg', 1.0);
-                //     var pdf = new JsPDF('', 'pt', 'a4');
-                //     //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
-                //     //当内容未超过pdf一页显示的范围,无需分页
-                //     if (leftHeight < pageHeight) {
-                //         //在pdf.addImage(pageData, 'JPEG', 左,上,宽度,高度)设置在pdf中显示;
-                //         pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);
-                //         // pdf.addImage(pageData, 'JPEG', 20, 40, imgWidth, imgHeight);
-                //     } else {    // 分页
-                //         while(leftHeight > 0) {
-                //             pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight);
-                //             leftHeight -= pageHeight;
-                //             position -= 841.89;
-                //             //避免添加空白页
-                //             if(leftHeight > 0) {
-                //                 pdf.addPage();
-                //             }
-                //         }
-                //     }
-                //     //可动态生成
-                //     pdf.save(title + '.pdf')
-                // })
             }, 1000)
 			})
 		}

+ 0 - 1
TEAMModelOS/ClientApp/src/view/answersheet/BaseEditor.vue

@@ -1,7 +1,6 @@
 <template>
 	<div class="base-editor-wrap">
 		<div :id="ids + 'btn'" class="base-editor-tools" v-show="isShowTools">
-<!-- 		<div :id="ids + 'btn'" class="base-editor-tools" v-show="isShowTools" @mouseenter="isShowTools = true" @mouseleave="isShowTools = false"> -->
 			<span class="select-model" @click="onSelectModal" v-if="type !== '0'">编辑</span>
 			<span class="select-model btn-delete" @click="onDeleteBlock" v-if="!isAutoCreate">删除</span>
 		</div>

+ 8 - 4
TEAMModelOS/ClientApp/src/view/answersheet/SheetBaseInfo.vue

@@ -73,6 +73,7 @@ export default {
       let snap = this.snap;
       this.infoLeftBox = snap.paper.g();
       let gap = INFO_H / (this.showInfoList.length + 1);
+	  let lineWidth = INFO_W - this.idLength * 30
       this.showInfoList.forEach((item, index) => {
         // 考号、座号、姓名等
         let leftInfo1 = snap.text(
@@ -85,7 +86,7 @@ export default {
           .line(
             CONTENT_START_X + 80,
             CONTENT_START_Y + INFO_ITEM_MARGIN + gap * index,
-            CONTENT_START_X + 250,
+            CONTENT_START_X + lineWidth - 40,
             CONTENT_START_Y + INFO_ITEM_MARGIN + gap * index
           )
           .attr({ fill: "none", stroke: "#000", strokeWidth: 1 });
@@ -100,13 +101,16 @@ export default {
       let snap = this.snap;
       this.idNumberBox.remove();
       this.idNumberBox = snap.paper.g();
+	  
+	  const CELL_WIDTH = 30
 
-      const INFO_LEFT_W = 275; // 左侧的宽度
+      const INFO_LEFT_W = INFO_W - CELL_WIDTH * idLength; // 左侧的宽度
       const INFO_LEFT_X =
         INFO_LEFT_W + CONTENT_ML + ANCHORPROP.width + ANCHORPROP.gapX; // 左侧的x坐标
 
       var numbers = new Array(idLength).fill("0");
-      var cellWidth = (INFO_W - INFO_LEFT_W) / idLength; // 计算每个号码的宽度
+      // var cellWidth = (INFO_W - INFO_LEFT_W) / idLength; // 计算每个号码的宽度
+      var cellWidth = CELL_WIDTH; // 计算每个号码的宽度
       const NUMBER_ITEM_MLR =
         ((INFO_W - INFO_LEFT_W) / idLength - NUMBER_ITEM_W) / 2; // 计算号码左右间距
 
@@ -136,7 +140,7 @@ export default {
       // 准考证号
       this.idNumberBox.add(
         snap.text(
-          INFO_LEFT_X + (INFO_LEFT_W - 20) / 2,
+          INFO_LEFT_X + (idLength * CELL_WIDTH - 69) / 2,
           CONTENT_MT + 75,
           "准 考 证 号"
         )

+ 4 - 9
TEAMModelOS/ClientApp/src/view/learnactivity/markpaper/MarkSetting.vue

@@ -49,14 +49,14 @@
                             </FormItem>
                             </Col>
                             <!-- 启用异常处理 -->
-                            <Col :md="24" :lg="24" :xl="12" :xxl="8">
+                            <!-- <Col :md="24" :lg="24" :xl="12" :xxl="8">
                             <FormItem label="异常申报" class="setting-item-wrap" prop="isErr">
                                 <RadioGroup v-model="setting.isErr" style="color:white">
                                     <Radio :label="1">是</Radio>
                                     <Radio :label="0" style="margin-left:20px">否</Radio>
                                 </RadioGroup>
                             </FormItem>
-                            </Col>
+                            </Col> -->
                             <!-- 启用仲裁 -->
                             <Col :md="24" :lg="24" :xl="12" :xxl="8" v-show="setting.num > 1">
                             <FormItem label="启用仲裁" class="setting-item-wrap" prop="isArb">
@@ -86,11 +86,11 @@
                         <Row>
                             <Col :md="24" :lg="24" :xl="24" :xxl="24" v-if="setting.isErr">
                             <!-- 异常处理老师 -->
-                            <FormItem :label="$t('learnActivity.mark.errRole')" class="setting-item-wrap" prop="err">
+                            <!-- <FormItem :label="$t('learnActivity.mark.errRole')" class="setting-item-wrap" prop="err">
                                 <Select v-model="subsInfo.err" style="width:400px" multiple>
                                     <Option v-for="(item,index) in teacherList" :value="item.id" :key="index">{{item.name}}</Option>
                                 </Select>
-                            </FormItem>
+                            </FormItem> -->
                             </Col>
                             <Col :md="24" :lg="24" :xl="24" :xxl="24" v-if="setting.isArb">
                             <!-- 仲裁老师 -->
@@ -379,7 +379,6 @@ export default {
 
             // 通过验证,保存设置
             this.setting.code = this.$store.state.userInfo.schoolCode
-            console.log(this.setting)
             this.$api.mark.UpsertMarkSet(this.setting).then(
                 res => {
                     this.$Message.success('保存成功!')
@@ -394,7 +393,6 @@ export default {
             let subject = this.setting.subs.find(item => {
                 return this.subjects[this.curSubIndex].id == item.id
             })
-            console.log('点前学科', subject)
             if (subject) {
                 let ids = subject.markers.map(item => {
                     return item.id
@@ -418,7 +416,6 @@ export default {
 
                 })
             }
-            console.log(this.setting)
             this.cancel()
         },
         cancel() {
@@ -483,7 +480,6 @@ export default {
     watch: {
         'evInfo.id': {
             handler(n, o) {
-                console.log(this.evInfo)
                 if (this.evInfo.id) {
                     this.findSettingInfo()
                 } else {
@@ -495,7 +491,6 @@ export default {
         },
         isSetting: {
             handler(n, o) {
-                console.log('watch', n, o)
                 this.$emit('statusChange', this.isSetting)
             },
             immediate: true

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

@@ -547,12 +547,6 @@ export default {
                     align: 'center',
                     sortable: true
                 },
-                {
-                    key: 'stuId',
-                    title: '学号',
-                    align: 'center',
-                    sortable: true
-                },
                 {
                     key: 'year',
                     title: '学级',

+ 14 - 2
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/EvaluationList/TotalIndex.vue

@@ -78,7 +78,7 @@
 								<span class="el-filter-title">{{ $t("totalAnalysis.condition2") }}:</span>
 								<RadioGroup v-model="filterGrade" type="button" @on-change="filterGradeChange">
 									<Radio :label="$t('totalAnalysis.all')">{{ $t('totalAnalysis.all') }}</Radio>
-									<Radio v-for="(item, index) in filterGradeList" :label="item.name" :key="index">{{ item.name }}</Radio>
+									<Radio v-for="(item, index) in filterGradeList" :label="index" :key="index">{{ item }}</Radio>
 								</RadioGroup>
 							</div>
 							<div class="el-filter-item">
@@ -542,7 +542,7 @@
 					return (
 						JSON.stringify(item.period.name).indexOf(this.filterConditions.period || "") >
 						-1 &&
-						JSON.stringify(item.grades).indexOf(this.filterConditions.grade || "") >
+						JSON.stringify(item.grades.map(i => i.id)).indexOf(this.filterConditions.grade || "") >
 						-1 &&
 						JSON.stringify(item.subjects).indexOf(this.filterConditions.subject || "") >
 						-1 &&
@@ -611,6 +611,13 @@
 						});
 						continue
 					}
+					if (key == 'grade') {
+						arr.push({
+							keyName: key,
+							val: this.getGradeName(this.filterGrade),
+						});
+						continue
+					}
 					arr.push({
 						keyName: key,
 						val: obj[key],
@@ -681,6 +688,11 @@
 						2) * 100 + "%";
 				};
 			},
+			getGradeName(){
+				return index => {
+					return this.filterGradeList[index]
+				}
+			},
 			filterGradeList() {
 				let res = this.schoolData.period.find(item => {
 					return item.name == this.filterConditions.period

+ 7 - 7
TEAMModelOS/ClientApp/src/view/syllabus/Syllabus.vue

@@ -26,9 +26,9 @@
 						<span>册别清单</span>
 						<span class="syllabus-content-header-tools">
 							<Icon type="md-search" @click="doSearchVolume" />
-							<Icon type="md-create" @click="doEditVolume" />
-							<Icon type="md-trash" @click="doDeleteVolume" />
-							<Icon type="md-add" @click="doAddVolume" />
+							<Icon type="md-create" @click="doEditVolume" v-if="$access.can('admin.*|Syllabus_Edit')"/>
+							<Icon type="md-trash" @click="doDeleteVolume" v-if="$access.can('admin.*|Syllabus_Edit')"/>
+							<Icon type="md-add" @click="doAddVolume" v-if="$access.can('admin.*|Syllabus_Edit')"/>
 						</span>
 					</div>
 					<div style="width: 90%;" v-else>
@@ -56,7 +56,7 @@
 				<div class="syllabus-content-header">
 					<span>课纲目录</span>
 					<span class="syllabus-content-header-tools">
-						<Icon type="md-add" @click="isAddTreeModal = true" />
+						<Icon type="md-add" @click="isAddTreeModal = true" v-if="$access.can('admin.*|Syllabus_Edit')"/>
 					</span>
 				</div>
 				<div class="syllabus-tree-box">
@@ -192,10 +192,10 @@
 			<Button slot="footer" @click="onRelateContent" style="margin-bottom: 20px;" class="modal-btn">确认</Button>
 		</Modal>
 		
-		<!-- 关联内容弹窗 -->
-		<Modal v-model="isInviteModal" width="900" class="tree-modal add-volume-modal choose-content-modal">
+		<!-- 选择共编、分享弹窗 -->
+		<Modal v-model="isInviteModal" width="750" class="tree-modal add-volume-modal choose-content-modal">
 			<div class="modal-header" slot="header">
-				选择共编教师
+				选择{{ isSchool ? '共编' : '分享' }}教师
 			</div>
 			<InviteTeacher :node="curShareNode"></InviteTeacher>
 			<Button slot="footer" @click="onRelateContent" style="margin-bottom: 20px;" class="modal-btn">确认</Button>

+ 13 - 5
TEAMModelOS/ClientApp/src/view/task/index.vue

@@ -38,7 +38,7 @@
                             {{$t('task.markMode1')}}
                         </span>
                     </div>
-                    <vuescroll class="mark-info-content">
+                    <vuescroll class="mark-info-content" v-if="markList.length > 0">
                         <StuProg v-show="curBarIndex == 1" @getStuId="toByStuView" :stusData="markData ? markData.attr : []"  :total="markList[curTaskIndex] ? markList[curTaskIndex].count : 1"></StuProg>
                         <!-- 按题 批阅进度 -->
                         <div class="setting-block" v-show="curBarIndex == 0" style="margin-top:15px">
@@ -77,6 +77,7 @@
                             </div>
                         </div> -->
                     </vuescroll>
+                    <EmptyData textContent="暂无阅卷任务" :top="90" style="height:fit-content"></EmptyData>
                 </div>
             </Split>
         </div>
@@ -227,12 +228,14 @@ export default {
                 let stuInfo = this.markData.attr.find(item => {
                     return item.stuId == stuId
                 })
-                answer = stuInfo.info.ans ? JSON.parse(await this.$tools.getFile(`${blobUrl}/exam/${stuInfo.info.ans}?${sas}`)) : []
+                let ansBlob = stuInfo.info.mark || stuInfo.info.ans 
+                answer = ansBlob ? JSON.parse(await this.$tools.getFile(`${blobUrl}/exam/${ansBlob}?${sas}`)) : []
                 score = stuInfo.info.score
                 sId = stuId
             } else {
                 let resData = await this.getNextStu()
-                answer = resData.ans.ans ? JSON.parse(await this.$tools.getFile(`${blobUrl}/exam/${resData.ans.ans}?${sas}`)) : []
+                let ansBlob = resData.info.mark || resData.info.ans 
+                answer = ansBlob ? JSON.parse(await this.$tools.getFile(`${blobUrl}/exam/${ansBlob}?${sas}`)) : []
                 score = resData.ans.score
                 sId = resData.stuId
             }
@@ -261,7 +264,8 @@ export default {
             let blobUrl = JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri //目前只有校本评测安排阅卷任务
             // 获取学生作答数据
             this.markData.attr.forEach(async item => {
-                this.$set(item.info, 'answer', item.info.ans ? JSON.parse(await this.$tools.getFile(`${blobUrl}/exam/${item.info.ans}?${sas}`)) : [])
+                let ansBlob = item.info.mark || item.info.ans 
+                this.$set(item.info, 'answer', ansBlob ? JSON.parse(await this.$tools.getFile(`${blobUrl}/exam/${ansBlob}?${sas}`)) : [])
             })
             this.$router.push({
                 name: 'ByQu',
@@ -318,7 +322,10 @@ export default {
                 res => {
                     if (!res.error) {
                         this.markList = res.tasks
-                        this.curTaskIndex = sessionStorage.getItem('markIndex') || 0
+                        let ch = sessionStorage.getItem('markIndex')
+                        ch = ch || 0
+                        ch = ch > this.markList.length - 1 ? 0 : ch
+                        this.curTaskIndex = ch || 0
                         if (this.markList.length > 0) this.findTaskData()
                     } else {
                         this.$Message.error('API error!')
@@ -369,6 +376,7 @@ export default {
         //当前阅卷任务未阅数量
         unmarked() {
             if (this.markList.length > 0) {
+                console.log(this.curTaskIndex,this.markList)
                 let total = this.markList[this.curTaskIndex].count
                 let marked = this.markData ? this.markData.attr.length : 0
                 return total - marked

+ 32 - 13
TEAMModelOS/ClientApp/src/view/task/mark/ByQu.vue

@@ -20,12 +20,12 @@
             <div class="btn-wrap">
                 <span class="action-btn" @click="toggleStatus = !toggleStatus">
                     <Icon type="md-shuffle" class="action-btn-icon" />
-                    切换
+                    切换题目
                 </span>
-                <span class="action-btn">
+                <!-- <span class="action-btn">
                     <Icon type="md-refresh" class="action-btn-icon" />
                     回评
-                </span>
+                </span> -->
                 <span class="action-btn">
                     <Icon custom="iconfont icon-exception" class="action-btn-icon" />
                     异常申报
@@ -51,7 +51,7 @@
                 <!-- <Icon :custom="isFull ? 'iconfont icon-cancel-full' : 'iconfont icon-full-screen'" class="tool-icon" :title="isFull ? '取消全屏' : '全屏'" @click="togglefull" /> -->
             </div>
             <div class="mark-stage">
-                <MarkCanvas :mouseStatus="mouseStatus" :bgImg="ansImg" :drawImgData="drawImgData"></MarkCanvas>
+                <MarkCanvas ref="markCanvas" :status="mouseStatus" :bgImg="ansImg" :drawImgData="drawImgData"></MarkCanvas>
             </div>
             <!-- 打分部分 -->
             <div class="score-wrap">
@@ -60,7 +60,7 @@
                     <InputNumber style="flex:1" :max="10" :min="1" v-model="score" @on-change="setScore"></InputNumber>
                 </div>
                 <div class="quick-score-box">
-                    <Button size="small" type="info" style="margin-right:8px" ghost @click="score = 10">满分</Button>
+                    <Button size="small" type="info" style="margin-right:8px" ghost @click="score = quScoreArr.length - 1">满分</Button>
                     <Button size="small" type="error" ghost @click="score = 0">零分</Button>
                     <Icon :type="isShowNum ? 'md-eye-off' : 'md-eye'" class="toggle-num-status" @click="isShowNum = !isShowNum" />
                     <div :class="['score-key-box', isShowNum ? '':'hind-key-box']">
@@ -114,7 +114,6 @@ export default {
             autoQu: true,//自动切换下一题
             autoStu: true,//自动获取下一学生
             toggleStatus: false,
-            activeIcon: -1,
             isShowNum: true,
             quIndex: 0,
             childIndex: -1,
@@ -134,15 +133,24 @@ export default {
             this.toggleStatus = false
         },
         drawImg(imgIndex) {
+            let curImg = new Image()
+            curImg.src = this.imgs[imgIndex]
+            curImg.onload = () => {
+                this.mouseStatus = 'img'
+                this.drawImgData = curImg
+            }
         },
         //清除所有批注
         clear() {
+            this.$refs['markCanvas'].clear()
         },
         /**将答案绘制到canvas上 */
         ansToImg() {
             let answerIframe = document.getElementById('markIframe1')
             answerIframe.onload = () => {
                 answerIframe.style.width = '850px'
+                answerIframe.style.margin = '0px'
+                answerIframe.contentWindow.document.body.style.margin = '0px'
                 answerIframe.contentWindow.document.body.style.margin = '0px 20px'
                 answerIframe.contentWindow.document.body.style.padding = '10px'
                 answerIframe.contentWindow.document.body.style.minWidth = '600px'
@@ -176,6 +184,15 @@ export default {
         //提交分数
         submit() {
             if (this.score > -1 && this.score != null) {
+                //保存批注
+                let mark
+                if (this.markImg) {
+                    let img = document.createElement('img')
+                    img.src = this.markImg
+                    this.stuAnswer[this.getScoreIndex(this.quIndex, this.childIndex)] = img.outerHTML
+                    mark = this.stuAnswer
+                    this.markImg = undefined
+                }
                 let requstData = {
                     id: this.taskInfo.id,
                     stuId: this.stusInfo[this.stuIndex].stuId,
@@ -184,7 +201,7 @@ export default {
                     score: this.stusInfo[this.stuIndex].info.score,
                     count: this.taskInfo.count,
                     code: this.taskInfo.ecode.replace('Exam-', ''),
-                    mark: ''
+                    mark
                 }
                 this.$api.mark.saveScore(requstData).then(
                     res => {
@@ -272,11 +289,12 @@ export default {
                 async res => {
                     let sas = this.$store.state.user.schoolProfile.blob_sas //目前只有校本评测安排阅卷任务
                     let blobUrl = JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri //目前只有校本评测安排阅卷任务
+                    let ansBlob = res.ans.mark || res.ans.ans 
                     this.stusInfo.push({
                         stuId: res.stuId,
                         info: {
                             ans: res.ans.ans,
-                            answer: res.ans.ans ? await this.$tools.getFile(`${blobUrl}/exam/${res.ans.ans}?${sas}`) : [],
+                            answer: ansBlob ? await this.$tools.getFile(`${blobUrl}/exam/${ansBlob}?${sas}`) : [],
                             score: res.ans.score
                         }
                     })
@@ -310,11 +328,11 @@ export default {
     },
     computed: {
         //当前题目进度
-        curProg(){
-            let index = this.getScoreIndex(this.quIndex,this.childIndex)
+        curProg() {
+            let index = this.getScoreIndex(this.quIndex, this.childIndex)
             let marked = 0
-            this.stusInfo.forEach(item=>{
-                if(item.info.score[index] != -1){
+            this.stusInfo.forEach(item => {
+                if (item.info.score[index] != -1) {
                     marked++
                 }
             })
@@ -322,7 +340,8 @@ export default {
         },
         curAnswer() {
             if (this.stusInfo && this.stusInfo[this.stuIndex]) {
-                this.score = this.stusInfo[this.stuIndex].info.score[this.getScoreIndex(this.quIndex, this.childIndex)]
+                let s = this.stusInfo[this.stuIndex].info.score[this.getScoreIndex(this.quIndex, this.childIndex)]
+                this.score = s == -1 ? null : s
                 if (this.stusInfo[this.stuIndex].info.answer) {
                     return this.stusInfo[this.stuIndex].info.answer[this.getScoreIndex(this.quIndex, this.childIndex)]
                 } else {

+ 131 - 43
TEAMModelOS/ClientApp/src/view/task/mark/ByStu.vue

@@ -20,12 +20,12 @@
             <div class="btn-wrap">
                 <span class="action-btn" @click="toggleStatus = !toggleStatus">
                     <Icon type="md-shuffle" class="action-btn-icon" />
-                    切换
+                    切换学生
                 </span>
-                <span class="action-btn">
+                <!-- <span class="action-btn">
                     <Icon type="md-refresh" class="action-btn-icon" />
                     回评
-                </span>
+                </span> -->
                 <span class="action-btn">
                     <Icon custom="iconfont icon-exception" class="action-btn-icon" />
                     异常申报
@@ -51,7 +51,7 @@
                 <!-- <Icon :custom="isFull ? 'iconfont icon-cancel-full' : 'iconfont icon-full-screen'" class="tool-icon" :title="isFull ? '取消全屏' : '全屏'" @click="togglefull" /> -->
             </div>
             <div class="mark-stage">
-                <MarkCanvas :mouseStatus="mouseStatus" :bgImg="ansImg" :drawImgData="drawImgData" style="padding-bottom:85px"></MarkCanvas>
+                <MarkCanvas ref="markCanvas" @getImg="saveMark" :status="mouseStatus" :bgImg="ansImg" :drawImgData="drawImgData" style="padding-bottom:85px"></MarkCanvas>
                 <!-- 题号显示部分 -->
                 <div class="qu-index-box">
                     <div class="qu-tips-box">
@@ -112,7 +112,7 @@
         <!-- 用来单独渲染学生作答数据,提高tocanvas 的效率 -->
         <iframe id="markIframe" :srcdoc="curAnswer"></iframe>
         <Modal v-model="toggleStatus" title="切换学生" :width="800" footer-hide>
-            <StuProg  @getStuId="toByStuView" class="light-stu-prog" :total="taskInfo.count" :stusData="stusData"></StuProg>
+            <StuProg @getStuId="toByStuView" class="light-stu-prog" :total="taskInfo.count" :stusData="stusData"></StuProg>
         </Modal>
     </div>
 </template>
@@ -151,37 +151,35 @@ export default {
             stuScore: [],
             taskInfo: {},
             stusData: [],
-            stuId: ''
+            stuId: '',
+            markImg: undefined
         }
     },
     methods: {
-        /**
-         * 按人阅卷
-         */
-        toByStuView(stuId) {
-            this.$Message.warning('这里切换学生逻辑暂未处理!')    
+        //保存批注
+        saveMark(data) {
+            console.log(data)
+            this.markImg = data.base64
         },
         drawImg(imgIndex) {
-            // this.activeIcon = imgIndex
-            // this.curImg = new Image()
-            // this.curImg.src = this.imgs[imgIndex]
-            // this.curImg.onload = () => {
-            //     this.mouseStatus = 'img'
-            //     this.startImg({ x: 100, y: 100 })
-            // }
+            let curImg = new Image()
+            curImg.src = this.imgs[imgIndex]
+            curImg.onload = () => {
+                this.mouseStatus = 'img'
+                this.drawImgData = curImg
+            }
         },
         //清除所有批注
         clear() {
-
-            // this.markLayer.removeChildren()
-            // this.stage.add(this.markLayer)
+            this.$refs['markCanvas'].clear()
         },
         /**将答案绘制到canvas上 */
         ansToImg() {
             let answerIframe = document.getElementById('markIframe')
             answerIframe.onload = () => {
                 answerIframe.style.width = '850px'
-                // answerIframe.contentWindow.document.body.style.margin = '0px 20px'
+                answerIframe.style.margin = '0px'
+                answerIframe.contentWindow.document.body.style.margin = '0px'
                 answerIframe.contentWindow.document.body.style.padding = '10px'
                 answerIframe.contentWindow.document.body.style.minWidth = '600px'
                 answerIframe.contentWindow.document.body.style.minHeight = '240px'
@@ -197,10 +195,6 @@ export default {
                 })
             }
         },
-        //将图片(答案)绘制到canvas
-        imgToCanvas(img) {
-            console.log('将图片(答案)绘制到canvas', img)
-        },
         /** 打分 */
         setScore(score) {
             this.score = score
@@ -237,12 +231,20 @@ export default {
                     }
                 }
             })
-            console.log('输出', realIndex)
             return --realIndex
         },
         //提交分数
         submit() {
             if (this.score > -1 && this.score != null) {
+                //保存批注
+                let mark
+                if (this.markImg) {
+                    let img = document.createElement('img')
+                    img.src = this.markImg
+                    this.stuAnswer[this.getScoreIndex(this.quIndex, this.childIndex)] = img.outerHTML
+                    mark = this.stuAnswer
+                    this.markImg = undefined
+                }
                 let requstData = {
                     id: this.taskInfo.id,
                     stuId: this.stuId,
@@ -251,7 +253,7 @@ export default {
                     score: this.stuScore,
                     count: this.taskInfo.count,
                     code: this.taskInfo.ecode.replace('Exam-', ''),
-                    mark: ''
+                    mark
                 }
                 this.$api.mark.saveScore(requstData).then(
                     res => {
@@ -269,6 +271,16 @@ export default {
             }
         },
         nextQuestion() {
+            //首先判断是都已阅完
+            if (!this.stuScore.includes(-1)) {
+                if (this.autoStu) {
+                    this.toggleStatus = true
+                } else {
+                    this.$Message.warning('当前完成当前学生评分,如果继续评分,请切换学生')
+                }
+                return
+            }
+
             // 当前不是最后一题
             if (this.quIndex < this.paperData.item.length - 1) {
                 //当前题目是综合题
@@ -352,6 +364,7 @@ export default {
                         this.$Message.success('已阅完')
                         if (this.autoStu) {
                             //TODE 随机获取下一位学生
+                            this.toggleStatus = true
                         }
                     }
 
@@ -366,11 +379,86 @@ export default {
                 name: sessionStorage.getItem('markFrom')
             })
         },
-        uuid() {
-            function S4() {
-                return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)
+        /**
+         * 按人阅卷
+         */
+        async toByStuView(stuId) {
+            this.toggleStatus = false
+            // 如果挑选的是当前的学生
+            if (stuId) {
+                let stuInfo = this.stusData.find(item => {
+                    return item.stuId == stuId
+                })
+                if (stuInfo && !stuInfo.info.answer) {
+                    let sas = this.$store.state.user.schoolProfile.blob_sas //目前只有校本评测安排阅卷任务
+                    let blobUrl = JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri //目前只有校本评测安排阅卷任务
+                    let ansBlob = stuInfo.info.mark || stuInfo.info.ans
+                    stuInfo.info.answer = ansBlob ? JSON.parse(await this.$tools.getFile(`${blobUrl}/exam/${ansBlob}?${sas}`)) : []
+                }
+                this.stuAnswer = stuInfo.info.answer
+                this.stuScore = stuInfo.info.score
+                this.stuId = stuId
+            }
+            // 随机获取一名学生
+            else {
+                if (this.taskInfo.count == this.stusData.length) {
+                    let res = this.stusData.filter(item => {
+                        return item.info.score.includes(-1)
+                    })
+                    if (res.length) {
+                        this.$Message.warning('已无未阅学生,请挑选进行中的学生继续阅卷。')
+                        this.toggleStatus = true
+                    } else {
+                        this.$Message.success('您已完成阅卷任务')
+                    }
+
+                } else {
+                    this.getNextStu()
+                }
+
             }
-            return (S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4())
+        },
+        /**
+         * 批阅下个学生
+         * @param stuId 如果传了stuid则会获取对应学生的数据,否则随机获取一个学生
+         */
+        getNextStu(stuId) {
+            let requestData = {
+                code: this.markList[this.curTaskIndex].ecode.replace('Exam-', ''),
+                id: this.markList[this.curTaskIndex].id,
+                subjectId: this.markList[this.curTaskIndex].subject,
+                count: this.markList[this.curTaskIndex].count,
+                tmdId: this.$store.state.userInfo.TEAMModelId,
+                stuId
+            }
+            this.$api.mark.FindNextStu(requestData).then(
+                async res => {
+                    if (res) {
+                        let obj = {
+                            stuId: res.stuId,
+                            info: {
+                                ans: res.ans.ans,
+                                score: res.ans.score,
+                                mark: res.ans.mark
+                            }
+                        }
+                        let sas = this.$store.state.user.schoolProfile.blob_sas //目前只有校本评测安排阅卷任务
+                        let blobUrl = JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri //目前只有校本评测安排阅卷任务
+                        obj.info.answer = JSON.parse(await this.$tools.getFile(`${blobUrl}/exam/${obj.info.ans}?${sas}`))
+                        this.stusData.push(obj)
+
+                        this.stuId = res.stuId
+                        this.stuAnswer = obj.info.answer
+                        this.stuScore = obj.info.score
+                        this.toggleStatus = false
+                    } else {
+                        this.$Message.error('API ERROR')
+                    }
+                },
+                err => {
+                    this.$Message.error('API ERROR')
+                }
+            )
         },
     },
     mounted() {
@@ -429,7 +517,7 @@ export default {
 }
 </script>
 <style scoped lang="less">
-.light-stu-prog{
+.light-stu-prog {
     margin-bottom: 15px;
 }
 .score-setting-wrap {
@@ -736,29 +824,29 @@ export default {
     color: red;
     font-weight: 800;
 }
-.light-stu-prog .setting-content{
-    background: #FFF;
+.light-stu-prog .setting-content {
+    background: #fff;
     box-shadow: none;
     border-color: #e8eaec;
 }
-.light-stu-prog .filter-tab-item{
+.light-stu-prog .filter-tab-item {
     border-color: #e8eaec;
 }
-.light-stu-prog .filter-tab-item-active{
-    background: #FFF !important;
+.light-stu-prog .filter-tab-item-active {
+    background: #fff !important;
     color: black;
 }
-.light-stu-prog .filter-tab-item{
-    background: #EEE;
+.light-stu-prog .filter-tab-item {
+    background: #eee;
 }
-.light-stu-prog .no-mark-text .no-mark-count{
+.light-stu-prog .no-mark-text .no-mark-count {
     color: #515a6e;
 }
-.light-stu-prog .stu-name{
+.light-stu-prog .stu-name {
     background: #eee;
     color: #515a6e;
 }
-.light-stu-prog .stu-name:hover{
+.light-stu-prog .stu-name:hover {
     color: #515a6e;
     box-shadow: 0px 0px 5px 2px #ccc;
 }

+ 53 - 59
TEAMModelOS/ClientApp/src/view/task/mark/MarkCanvas.vue

@@ -16,7 +16,7 @@ export default {
     name: 'MarkCanvas',
     props: {
         // 当前鼠标操作类型
-        mouseStatus: {
+        status: {
             type: String,
             default: 'move',
             required: true
@@ -28,12 +28,13 @@ export default {
         },
         //canvas绘制图片的数据(eg:添加表情包)
         drawImgData: {
-            type: [String, Object],
+            // type: [String, Object],
             default: ''
         }
     },
     data() {
         return {
+            imgData: undefined,
             stage: undefined,
             orgLayer: undefined,
             markLayer: undefined,
@@ -49,7 +50,7 @@ export default {
                 x: 0,
                 y: 0
             },
-            // mouseStatus: 'move', //move:移动  rect:画矩形 resize:调整图形
+            mouseStatus: '', //move:移动  rect:画矩形 resize:调整图形
             resizeBefore: '',
             rect: undefined,
             oval: undefined,
@@ -61,32 +62,20 @@ export default {
         }
     },
     methods: {
-        uuid() {
-            function S4() {
-                return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)
-            }
-            return (S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4())
-        },
-        move(index) {
-            this.activeIcon = index
+        move() {
             this.mouseStatus = 'move'
         },
-        drawImg(imgIndex) {
-            // this.activeIcon = imgIndex
-            // this.curImg = new Image()
-            // this.curImg.src = this.imgs[imgIndex]
-            // this.curImg.onload = () => {
-            //     this.mouseStatus = 'img'
-            //     this.startImg({ x: 100, y: 100 })
-            // }
+        drawImg() {
+            this.mouseStatus = 'img'
+            this.startImg({ x: 100, y: 100 })
         },
         startImg(current) {
             let _this = this
             this.img = new Konva.Image({
-                id: this.uuid(),
+                id: this.$tools.guid(),
                 draggable: false,
                 strokeScaleEnabled: false,
-                image: _this.curImg,
+                image: _this.imgData,
                 width: 80,
                 height: 80,
                 x: current.x,
@@ -113,8 +102,7 @@ export default {
             this.stage.add(this.markLayer)
             this.img = null
         },
-        drawText(index) {
-            this.activeIcon = index
+        drawText() {
             this.mouseStatus = 'text'
         },
         startText(current) {
@@ -123,7 +111,7 @@ export default {
             if (nodes.length > 0) return
             if (!this.text) {
                 this.text = new Konva.Text({
-                    id: this.uuid(),
+                    id: this.$tools.guid(),
                     fontSize: 16,
                     text: '',
                     fill: 'red',
@@ -206,21 +194,19 @@ export default {
                 this.text.x(current.x)
                 this.text.y(current.y)
             }
-            // this.text.zIndex(100)
             this.tr.nodes([this.text])
             this.markLayer.add(this.tr)
             this.markLayer.add(this.text)
             this.stage.add(this.markLayer)
         },
-        drawLine(index) {
-            this.activeIcon = index
+        drawLine() {
             this.mouseStatus = 'line'
         },
         startLine(current) {
             let _this = this
             if (!this.line) {
                 this.line = new Konva.Line({
-                    id: this.uuid(),
+                    id: this.$tools.guid(),
                     stroke: 'red',
                     strokeWidth: 3,
                     draggable: false,
@@ -246,12 +232,10 @@ export default {
             } else {
                 this.line.points(this.line.points().concat([current.x / _this.scaleDefault, current.y / _this.scaleDefault]))
             }
-            // this.line.zIndex(1)
             this.markLayer.add(this.line)
             this.stage.add(this.markLayer)
         },
-        drawArrow(index) {
-            this.activeIcon = index
+        drawArrow() {
             this.mouseStatus = 'arrow'
         },
         // 绘制箭头
@@ -259,7 +243,7 @@ export default {
             let _this = this
             if (!this.arrow) {
                 this.arrow = new Konva.Arrow({
-                    id: this.uuid(),
+                    id: this.$tools.guid(),
                     fill: 'transparent',
                     stroke: 'red',
                     strokeWidth: 4,
@@ -290,12 +274,10 @@ export default {
             } else {
                 this.arrow.points([_this.clickPoint.x / _this.scaleDefault, _this.clickPoint.y / _this.scaleDefault, (current.x) / _this.scaleDefault, (current.y) / _this.scaleDefault])
             }
-            // this.arrow.zIndex(1)
             this.markLayer.add(this.arrow)
             this.stage.add(this.markLayer)
         },
-        drawOval(index) {
-            this.activeIcon = index
+        drawOval() {
             this.mouseStatus = 'oval'
         },
         startOval(current) {
@@ -305,9 +287,7 @@ export default {
             let ry = (_this.clickPoint.y - current.y) / _this.scaleDefault * 0.5
             if (!this.oval) {
                 this.oval = new Konva.Ellipse({
-                    id: this.uuid(),
-                    // x: _this.clickPoint.x / _this.scaleDefault,
-                    // y: _this.clickPoint.y / _this.scaleDefault,
+                    id: this.$tools.guid(),
                     x: _this.clickPoint.x,
                     y: _this.clickPoint.y,
                     radiusX: rx > 0 ? rx : -rx,
@@ -346,18 +326,8 @@ export default {
             this.markLayer.add(this.oval)
             this.stage.add(this.markLayer)
         },
-        drawIcon(index) {
-            this.activeIcon = index
-        },
-        review(index) {
-            this.activeIcon = index
-        },
-        cancelFull(index) {
-            this.activeIcon = index
-        },
         //绘制矩形
-        drawRect(index) {
-            this.activeIcon = index
+        drawRect() {
             this.mouseStatus = 'rect'
         },
         startRect(current) {
@@ -365,7 +335,7 @@ export default {
             //判断rect是否初始化
             if (!this.rect) {
                 this.rect = new Konva.Rect({
-                    id: this.uuid(),
+                    id: this.$tools.guid(),
                     x: _this.clickPoint.x / _this.scaleDefault,
                     y: _this.clickPoint.y / _this.scaleDefault,
                     width: (current.x - _this.clickPoint.x) / _this.scaleDefault,
@@ -444,11 +414,6 @@ export default {
                 this.myScale()
             }
         },
-        //还原比例
-        restore() {
-            this.scaleDefault = 1
-            this.myScale()
-        },
         myScale() {
             this.stage.children.forEach(item => {
                 item.scale({
@@ -522,7 +487,7 @@ export default {
             this.clickPoint = this.getCanvasPoint(e.clientX, e.clientY)
         },
         //鼠标弹起事件
-        canvasMouseUp() {
+        async canvasMouseUp() {
             this.isMouseDown = false
             switch (this.mouseStatus) {
                 case 'move':
@@ -548,6 +513,11 @@ export default {
                 default:
                     break
             }
+            if (this.mouseStatus != 'move') {
+                let img = await this.saveAsImg()
+                this.$emit('getImg', img)
+            }
+
         },
         //鼠标移动事件
         canvasMouseMove(e) {
@@ -582,10 +552,22 @@ export default {
         },
 
         //清除所有批注
-        clear(index) {
-            this.activeIcon = index
+        clear() {
             this.markLayer.removeChildren()
             this.stage.add(this.markLayer)
+        },
+
+        /* 保存批注 */
+        saveAsImg() {
+            return new Promise(async (r, j) => {
+                let compressImg = await this.$editorTools.compressImg(this.stage.toDataURL(),
+                    this.stage.attrs.width, this.stage.attrs.height)
+                r({
+                    base64: compressImg,
+                    width: this.stage.attrs.width,
+                    height: this.stage.attrs.height
+                })
+            })
         }
     },
     mounted() {
@@ -640,7 +622,7 @@ export default {
         bgImg: {
             handler(n, o) {
                 this.$nextTick(() => {
-                    // this.restore()
+                    this.restore()
                     this.orgLayer.removeChildren()
                     this.markLayer.removeChildren()
                     let imageObj = new Image()
@@ -667,6 +649,18 @@ export default {
             },
             immediate: true,
             deep: true
+        },
+        drawImgData: {
+            handler(n, o) {
+                this.imgData = n
+                this.drawImg()
+            }
+        },
+        status: {
+            handler(n, o) {
+                this.mouseStatus = n
+            },
+            immediate: true
         }
     }
 }

+ 6 - 3
TEAMModelOS/ClientApp/src/view/task/mark/ProgPie.vue

@@ -100,13 +100,16 @@ export default {
         count: {
             handler(n, o) {
                 this.count.forEach((item, index) => {
-                    console.log(item,index)
                     this.option.series[0].data[index].value = item
                 })
                 if (!this.techScoreGau) {
-                    this.techScoreGau = this.$echarts.init(document.getElementById('mark-prog-pie'))
+                    this.$nextTick(() => {
+                        this.techScoreGau = this.$echarts.init(document.getElementById('mark-prog-pie'))
+                        this.techScoreGau.setOption(this.option)
+                    })
+                } else {
+                    this.techScoreGau.setOption(this.option)
                 }
-                this.techScoreGau.setOption(this.option)
             },
             immediate: true,
             deep: true

+ 11 - 3
TEAMModelOS/ClientApp/src/view/task/mark/StuProg.vue

@@ -66,14 +66,22 @@ export default {
         }
     },
     methods: {
-        toByStuView(stuId){
-            this.$emit('getStuId',stuId)
+        toByStuView(stuId) {
+            this.$emit('getStuId', stuId)
         }
     },
     computed: {
         //当前阅卷任务未阅数量
         unmarked() {
-            return this.total - this.marked.length - this.marking.length
+            let res = this.total - this.marked.length - this.marking.length
+            if (res == 0) {
+                if (this.marking.length == 0) {
+                    this.tabIndex = 2
+                } else {
+                    this.tabIndex = 1
+                }
+            }
+            return res
         },
         //当前阅卷任务已阅学生信息
         marked() {

+ 2 - 2
TEAMModelOS/ClientApp/src/view/teachcontent/ResBelong.vue

@@ -10,7 +10,7 @@
                 <DropdownItem :name="item.id">{{ item.name }}</DropdownItem>
             </DropdownMenu>
             <DropdownMenu slot="list">
-                <DropdownItem name="public">{{$t('teachContent.publicRes')}}</DropdownItem>
+                <DropdownItem name="public">{{$t('teachContent.public')}}</DropdownItem>
             </DropdownMenu>
         </Dropdown>
     </div>
@@ -71,7 +71,7 @@ export default {
                 if (temp) {
                     name = temp.name
                 }else{
-                    name = this.$t('teachContent.publicRes')
+                    name = this.$t('teachContent.public')
                 }
             }
             return name

+ 9 - 3
TEAMModelOS/ClientApp/src/view/teachcontent/index.vue

@@ -84,7 +84,7 @@
                     <ResBelong class="pd-filter-wrap" v-show="routerScope == 'school'" @pd-change="(data)=>{filterPeriod = data}"></ResBelong>
                     <div class="content-filter-wrap">
                         <Input v-model="keyWord" search size="small" :placeholder="$t('teachContent.searchText')" class="key-word-search" @on-change="searchKeyWord" @on-search="searchKeyWord" />
-                        <CheckboxGroup v-model="extFilter"  @on-change="filterFileByExtension">
+                        <CheckboxGroup v-model="extFilter" @on-change="filterFileByExtension">
                             <Checkbox :label="item" v-for="(item ,index) in extensions" :key="index">
                                 <span>{{item}}</span>
                             </Checkbox>
@@ -186,7 +186,7 @@
                     {{$t('teachContent.notAudio')}}
                 </audio>
                 <img v-else-if="previewFile.type == 'image'" :src="previewFile.url" style="border-radius: 5px;max-height: 800px;max-width:870px;" />
-                <embed v-else-if="previewFile.extension == 'PDF'" :src="previewFile.url" width="870" height="720" />
+                <iframe v-else-if="previewFile.extension == 'PDF'" :src="'/web/viewer.html?file=' + escapeBlobUrl" width='870' height='700' frameborder='1'></iframe>
                 <iframe v-else :src="'https://view.officeapps.live.com/op/view.aspx?src=' + escapeBlobUrl" width='870' height='700' frameborder='1'></iframe>
             </div>
         </div>
@@ -837,16 +837,22 @@ export default {
                 xhr.send(formData);
             })
         },
+        /* 打开PDF文件进行预览 */
+        openPdf(url) {
+            window.open('/web/viewer.html?file=' + encodeURIComponent(url))
+        },
         openPreviewFile(index) {
             // if (this.fileListShow[index].extension == 'HTEX') {
             //     this.$Message.warning('暂未处理HTEX文件预览功能')
             //     return
             // }
             this.previewFile = this._.cloneDeep(this.fileListShow[index])
+            // PDF预览
             if (this.activeType == 'image' || this.activeType == 'video') {
                 this.previewFile.url = this.previewFile.url.replace('thum/', '')
             }
-            if (this.fileListShow[index].type == 'image' || this.fileListShow[index].type == 'video' || this.fileListShow[index].extension == 'pdf') {
+            if (this.fileListShow[index].extension == 'PDF') {
+                this.escapeBlobUrl = encodeURIComponent(this.previewFile.url)
             } else {
                 this.escapeBlobUrl = escape(this.previewFile.url)
             }

+ 47 - 35
TEAMModelOS/Controllers/Common/ExamController.cs

@@ -1573,7 +1573,7 @@ namespace TEAMModelOS.Controllers
                     {
                         recs.Add(new { stuId = rcd.Name.ToString(), ans = value });
                     }
-                    else
+                    if (string.IsNullOrEmpty(tmdId.ToString()))
                     {
                         all.Add(new { stuId = rcd.Name.ToString(), ans = value });
                     }
@@ -1594,10 +1594,14 @@ namespace TEAMModelOS.Controllers
                 {
                     if (recs.Count == count.GetInt32())
                     {
-                        return Ok( new { msg = "分配人数已到上限"});
+                        return Ok(new { msg = "分配人数已到上限" });
                     }
                     else
                     {
+                        if (all.Count == 0)
+                        {
+                            return Ok(new { msg = "暂无可选学生" });
+                        }
                         //随机选取一名学生打分
                         Random random = new Random();
                         int n = random.Next(all.Count);
@@ -1613,40 +1617,42 @@ namespace TEAMModelOS.Controllers
                         await redisClient.HashSetAsync($"Exam:Scoring:{id}-{subjectId}", sId.ToString(), new { ans = blob, score = sc, tmdId = tId }.ToJsonString());
                         return Ok(item);
                     }
-                   
+
                 }
 
                 //var json = JsonDocument.Parse(record);
-                /*if (info.scope.Equals("school"))
-				{
-
-					await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<ExamClassResult>(
-					queryText: $"select value(c) from c where c.examId = '{id}' and c.subjectId = '{subjectId}'",
-					requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{info.school}") }))
-					{
-						classResults.Add(item);
-					}
-				}
-				else
-				{
-					await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<ExamClassResult>(
-						queryText: $"select value(c) from c where c.examId = '{id}' and c.subjectId = '{subjectId}'",
-						requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{info.creatorId}") }))
-					{
-						classResults.Add(item);
-					}
-				}
-				List<Task<bool>> tasks = new List<Task<bool>>();
-				foreach (ExamClassResult examClass in classResults)
-				{
-					foreach (string stuId in examClass.studentIds)
-					{
-						int index = examClass.studentIds.IndexOf(stuId);
-						tasks.Add(redisClient.HashSetAsync($"Exam:Scoring:{id}-{subjectId}", stuId, new { ans = examClass.studentAnswers[index].Count > 0 ? examClass.studentAnswers[index][0]: "", score = examClass.studentScores[index] }.ToJsonString()));
-					}
-
-				}
-				await Task.WhenAll(tasks);*/
+                /*ExamInfo info = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<ExamInfo>(id.ToString(), new Azure.Cosmos.PartitionKey($"Exam-hbcn"));
+                if (info.scope.Equals("school"))
+                {
+
+                    await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<ExamClassResult>(
+                    queryText: $"select value(c) from c where c.examId = '{id}' and c.subjectId = '{subjectId}'",
+                    requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{info.school}") }))
+                    {
+                        classResults.Add(item);
+                    }
+                }
+                else
+                {
+                    await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<ExamClassResult>(
+                        queryText: $"select value(c) from c where c.examId = '{id}' and c.subjectId = '{subjectId}'",
+                        requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{info.creatorId}") }))
+                    {
+                        classResults.Add(item);
+                    }
+                }
+                List<Task<bool>> tasks = new List<Task<bool>>();
+                foreach (ExamClassResult examClass in classResults)
+                {
+                    foreach (string stuId in examClass.studentIds)
+                    {
+                        int index = examClass.studentIds.IndexOf(stuId);
+                        tasks.Add(redisClient.HashSetAsync($"Exam:Scoring:{id}-{subjectId}", stuId, new { ans = examClass.studentAnswers[index].Count > 0 ? examClass.studentAnswers[index][0] : "", score = examClass.studentScores[index] }.ToJsonString()));
+                    }
+
+                }
+                await Task.WhenAll(tasks);
+                return Ok();*/
                 //info = await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(info, info.id, new PartitionKey($"Exam-{code}"));                
             }
             catch (Exception ex)
@@ -1694,16 +1700,22 @@ namespace TEAMModelOS.Controllers
                 var stuAns = await redisClient.HashGetAsync($"Exam:Scoring:{id}-{subjectId}", sId.ToString());
                 var value = JsonDocument.Parse(stuAns.ToString());
                 value.RootElement.TryGetProperty("ans", out JsonElement blob);
+                value.RootElement.TryGetProperty("mark", out JsonElement element);
                 StringBuilder builder = new StringBuilder();
-                if (requert.TryGetProperty("mark", out JsonElement mark)) {
+                if (requert.TryGetProperty("mark", out JsonElement mark))
+                {
                     //存放老师批注信息到blob                   
                     builder.Append(id).Append("/");
                     builder.Append(subjectId).Append("/");
                     builder.Append(sId).Append("mark").Append("/");
                     builder.Append("ans.json");
                     await _azureStorage.UploadFileByContainer(code.ToString(), mark.ToJsonString(), "exam", builder.ToString(), false);
+                    await redisClient.HashSetAsync($"Exam:Scoring:{id}-{subjectId}", sId.ToString(), new { ans = blob, score = score, tmdId = tId, mark = builder.ToString() }.ToJsonString());
+                }
+                else {
+                    await redisClient.HashSetAsync($"Exam:Scoring:{id}-{subjectId}", sId.ToString(), new { ans = blob, score = score, tmdId = tId, mark = element }.ToJsonString());
                 }                
-                await redisClient.HashSetAsync($"Exam:Scoring:{id}-{subjectId}", sId.ToString(), new { ans = blob, score = score, tmdId = tId, mark = builder.ToString() }.ToJsonString());
+                
                 if (recs.Count == count.GetInt32())
                 {
                     await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<ExamClassResult>(

+ 7 - 3
TEAMModelOS/Controllers/Core/BlobController.cs

@@ -557,7 +557,7 @@ namespace TEAMModelOS.Controllers.Core
         /*
          新增 编辑接口
         {
-            "period": "",
+            "periodId": "",
             "scope": "school",
             "name": "hbcn",
             "url": "video/xxx.png",
@@ -582,7 +582,9 @@ namespace TEAMModelOS.Controllers.Core
 
             try
             {
-                request.TryGetProperty("period", out JsonElement period);
+                request.TryGetProperty("periodId", out JsonElement periodId);
+                request.TryGetProperty("subjectId", out JsonElement subjectId);
+                request.TryGetProperty("gradeId", out JsonElement gradeId);
                 request.TryGetProperty("scope", out JsonElement scope);
                 request.TryGetProperty("name", out JsonElement name);
                 request.TryGetProperty("url", out JsonElement jurl);
@@ -634,7 +636,9 @@ namespace TEAMModelOS.Controllers.Core
                             url = url,
                             time = now,
                             size = size != null && size.HasValue ? size.Value : 0,
-                            period = $"{period}",
+                            periodId =  periodId.ValueKind.Equals(JsonValueKind.String)?$"{periodId}":null,
+                            subjectId= gradeId.ValueKind.Equals(JsonValueKind.String) ? $"{subjectId}" : null,
+                            gradeId = gradeId.ValueKind.Equals(JsonValueKind.String) ? $"{gradeId}" : null,
                             type = u
                         };
                         if (scope.GetString().Equals("school"))

+ 148 - 85
TEAMModelOS/Controllers/Syllabus/SyllabusController.cs

@@ -37,88 +37,152 @@ namespace TEAMModelOS.Controllers
 
         /*
         {
-          "id": "hbcn108774752059002880",
-          "code": "Syllabus-hbcn",
-          "scope":"school/private",
-          "trees": [
-              {
-                  "id": "111111-111-44E7-8DD9-A6CB6D0D52F2",
-                  "pid": "hbcn108515325535981568",
-                  "title": "第一章",
-                  "order": 0,
-                  "type": tree,
-                  "rnodes":[
-                      {
-                            "type":"item/paper/image/video/pptx.../link"//试题 ,试卷,内容资源,外部链接(只有link,和name)
-                            "title":"显示名称",
-                            "id":"关联资源的id",
-                            "code":"关联资源的code",
-                            "link":["https;//www.xxx.com/xxx.json"],
-                            "cntr":"blob的容器名"
-                      }
-                   ],
-                  "children": [
-                      {
-                          "id": "22222-111-447E-8A52-BFCD63E61D33",
-                          "pid": "111111-111-44E7-8DD9-A6CB6D0D52F2",
-                          "title": "第一节333",
-                          "order": 0,
-                          "type": 1,
-                          "children": [
-                              {
-                                  "id": "3333-111-447E-8A52-BFCD63E61D33",
-                                  "pid": "22222-111-447E-8A52-BFCD63E61D33",
-                                  "title": "第一节333",
-                                  "order": 0,
-                                  "type": 1
-                              },
-                              {
-                                  "id": "4444-111-447E-8A52-BFCD63E61D33",
-                                  "pid": "22222-111-447E-8A52-BFCD63E61D33",
-                                  "title": "第一节333",
-                                  "order": 0,
-                                  "type": 1
-                              }
-                          ]
-                      }
-                  ]
-              },
-              {
-                  "id": "111111-222-44E7-8DD9-A6CB6D0D52F2",
-                  "pid": "hbcn108515325535981568",
-                  "title": "第一章",
-                  "order": 0,
-                  "type": 1,
-                  "children": [
-                      {
-                          "id": "22222-222-447E-8A52-BFCD63E61D33",
-                          "pid": "111111-222-44E7-8DD9-A6CB6D0D52F2",
-                          "title": "第一节333",
-                          "order": 0,
-                          "type": 1,
-                          "children": [
-                              {
-                                  "id": "3333-222-447E-8A52-BFCD63E61D33",
-                                  "pid": "22222-222-447E-8A52-BFCD63E61D33",
-                                  "title": "第一节333",
-                                  "order": 0,
-                                  "type": 1
-                              }
-                          ]
-                      },
-                      {
-                          "id": "5555-222-447E-8A52-BFCD63E61D33",
-                          "pid": "111111-222-44E7-8DD9-A6CB6D0D52F2",
-                          "title": "第一节333",
-                          "order": 0,
-                          "type": 1,
-                          "children": []
-                      }
-                  ]
-              }
-          ]
+    "id": "0baf00db-0768-4b62-a8f7-280f6bcebf71",
+    "scope": "school",
+    "trees": [
+        {
+            "children": [
+                {
+                    "children": [],
+                    "id": "AC4BA269-541B-4DFC-92A5-D773068A6439",
+                    "pid": "2dfcc62e-8eea-9881-dc79-384b2f0afbec",
+                    "order": 0,
+                    "rnodes": [
+                        {
+                            "type": "doc",
+                            "id": "a2bee388-5584-72cc-1d9a-d8a77d255364",
+                            "code": "hbcn",
+                            "scope": "private",
+                            "cntr": "1595321354",
+                            "link": [
+                                "https://teammodelstorage.blob.core.chinacloudapi.cn/1595321354/doc/2020智慧課堂與智慧教研.pptx"
+                            ],
+                            "title": "2020智慧課堂與智慧教研.pptx"
+                        }
+                    ],
+                    "cids": [],
+                    "creatorId": null,
+                    "updateTime": 0,
+                    "title": "1-1 新冠疫情小贴士"
+                }
+            ],
+            "id": "2dfcc62e-8eea-9881-dc79-384b2f0afbec",
+            "pid": "0baf00db-0768-4b62-a8f7-280f6bcebf71",
+            "order": 0,
+            "rnodes": [
+                {
+                    "type": "doc",
+                    "id": "cf1b4d21-66e1-e6c7-c944-30a03e419fa6",
+                    "code": "hbcn",
+                    "scope": "school",
+                    "cntr": "hbcn",
+                    "link": [
+                        "https://teammodelstorage.blob.core.chinacloudapi.cn/hbcn/syllabus/IES5试卷模板制作说明(终).pdf"
+                    ],
+                    "title": "IES5试卷模板制作说明(终).pdf"
+                },
+                {
+                    "type": "doc",
+                    "id": "f3e82595-7340-a5fe-1004-04538ca09b86",
+                    "code": "hbcn",
+                    "scope": "school",
+                    "cntr": "hbcn",
+                    "link": [
+                        "https://teammodelstorage.blob.core.chinacloudapi.cn/hbcn/syllabus/111.pdf"
+                    ],
+                    "title": "111.pdf"
+                },
+                {
+                    "type": "doc",
+                    "id": "e1b31639-dad9-9efb-020b-159dd045f238",
+                    "code": "hbcn",
+                    "scope": "school",
+                    "cntr": "hbcn",
+                    "link": [
+                        "https://teammodelstorage.blob.core.chinacloudapi.cn/hbcn/doc/6789.pdf"
+                    ],
+                    "title": "6789.pdf"
+                },
+                {
+                    "type": "doc",
+                    "id": "aaeb4b5c-0450-cb4d-a1ac-244f3d115c4a",
+                    "code": "hbcn",
+                    "scope": "private",
+                    "cntr": "1595321354",
+                    "link": [
+                        "https://teammodelstorage.blob.core.chinacloudapi.cn/1595321354/doc/422北京培新活动邀请函.pdf"
+                    ],
+                    "title": "422北京培新活动邀请函.pdf"
+                },
+                {
+                    "type": "doc",
+                    "id": "c5f5838c-5c2f-5e9b-a734-a473cd2cad2e",
+                    "code": "hbcn",
+                    "scope": "school",
+                    "cntr": "hbcn",
+                    "link": [
+                        "https://teammodelstorage.blob.core.chinacloudapi.cn/hbcn/doc/6666.pdf"
+                    ],
+                    "title": "6666.pdf"
+                },
+                {
+                    "type": "doc",
+                    "id": "4a106c8e-6831-5f4b-4ec3-3c18f778cab2",
+                    "code": "hbcn",
+                    "scope": "school",
+                    "cntr": "hbcn",
+                    "link": [
+                        "https://teammodelstorage.blob.core.chinacloudapi.cn/hbcn/doc/6.pdf"
+                    ],
+                    "title": "6.pdf"
+                }
+            ],
+            "cids": [
+                "AC4BA269-541B-4DFC-92A5-D773068A6439"
+            ],
+            "creatorId": null,
+            "updateTime": 0,
+            "title": "第一单元 新冠疫情防控"
+        },
+        {
+            "children": [
+                {
+                    "children": [],
+                    "id": "FC5132E1-9723-4875-B3B5-D3DC98D194FA",
+                    "pid": "4f12752c-852f-6e90-a3df-1f1f710af23d",
+                    "order": 0,
+                    "rnodes": [],
+                    "cids": [],
+                    "creatorId": null,
+                    "updateTime": 0,
+                    "title": "2-1 全球醍摩豆智慧教育研究院"
+                },
+                {
+                    "children": [],
+                    "id": "3CAD52BE-67B6-4EDB-8EFB-9122960D816A",
+                    "pid": "4f12752c-852f-6e90-a3df-1f1f710af23d",
+                    "order": 1,
+                    "rnodes": [],
+                    "cids": [],
+                    "creatorId": null,
+                    "updateTime": 0,
+                    "title": "2-2 醍摩豆研究院课程体系"
+                }
+            ],
+            "id": "4f12752c-852f-6e90-a3df-1f1f710af23d",
+            "pid": "0baf00db-0768-4b62-a8f7-280f6bcebf71",
+            "order": 1,
+            "rnodes": [],
+            "cids": [
+                "FC5132E1-9723-4875-B3B5-D3DC98D194FA",
+                "3CAD52BE-67B6-4EDB-8EFB-9122960D816A"
+            ],
+            "creatorId": null,
+            "updateTime": 0,
+            "title": "第二单元 醍摩豆手册学习"
         }
-
+    ]
+}
               */
 
         /// <summary>
@@ -206,9 +270,8 @@ namespace TEAMModelOS.Controllers
                 return Ok(new { tree});
             }
         }
-
         /// <summary>
-        /// 删除课纲
+        /// 删除章节
         /// </summary>
         /// <param name="request"></param>
         /// <returns></returns>
@@ -222,12 +285,12 @@ namespace TEAMModelOS.Controllers
             var client = _azureCosmos.GetCosmosClient();
             if (scope.ToString().Equals("school"))
             {
-                var response = await client.GetContainer("TEAMModelOS", "School").DeleteItemStreamAsync(id.ToString(), new PartitionKey($"Volume-{code}"));
+                var response = await client.GetContainer("TEAMModelOS", "School").DeleteItemStreamAsync(id.ToString(), new PartitionKey($"Syllabus-{code}"));
                 return Ok(new { code = response.Status });
             }
             else
             {
-                var response = await client.GetContainer("TEAMModelOS", "Teacher").DeleteItemStreamAsync(id.ToString(), new PartitionKey($"Volume-{code}"));
+                var response = await client.GetContainer("TEAMModelOS", "Teacher").DeleteItemStreamAsync(id.ToString(), new PartitionKey($"Syllabus-{code}"));
                 return Ok(new { code = response.Status });
             }
         }

+ 23 - 7
TEAMModelOS/Controllers/Teacher/CommentController.cs

@@ -16,6 +16,8 @@ using TEAMModelOS.SDK.Helper.Common.StringHelper;
 using TEAMModelOS.Models;
 using Microsoft.Extensions.Options;
 using System.Text;
+using Azure.Messaging.ServiceBus;
+using Microsoft.Extensions.Configuration;
 
 namespace TEAMModelOS.Controllers
 {
@@ -24,18 +26,22 @@ namespace TEAMModelOS.Controllers
     //[Authorize(Roles = "IES5")]
     [Route("teacher/comment")]
     [ApiController]
-    public class CommentController :ControllerBase
+    public class CommentController : ControllerBase
     {
         private readonly 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)
+        public IConfiguration _configuration { get; set; }
+        private readonly AzureServiceBusFactory _serviceBus;
+        public CommentController(AzureCosmosFactory azureCosmos, DingDing dingDing, IOptionsSnapshot<Option> option, AzureStorageFactory azureStorage, AzureServiceBusFactory serviceBus, IConfiguration configuration)
         {
             _azureCosmos = azureCosmos;
             _dingDing = dingDing;
             _option = option?.Value;
             _azureStorage = azureStorage;
+            _serviceBus = serviceBus;
+            _configuration = configuration;
         }
 
         /// <summary>
@@ -100,7 +106,7 @@ namespace TEAMModelOS.Controllers
             //    }
             //}
 
-            return Ok();           
+            return Ok();
         }
         /// <summary>
         /// 更新保存教师评语罐头,如果评语列表为空则删除
@@ -126,7 +132,7 @@ namespace TEAMModelOS.Controllers
             //    {
             //        IdPk idPk = await _azureCosmos.DeleteAsync<Comment>(request.id, request.code);
             //    }
-                   
+
             //}
             ////return builder.Data(comment).build();
             return Ok();
@@ -163,7 +169,7 @@ namespace TEAMModelOS.Controllers
                         }
                     }
                 }
-                ExamClassResult classResult = new 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>();
@@ -183,9 +189,11 @@ namespace TEAMModelOS.Controllers
                     /*string FileName = result.examId + "/" + result.subjectId + "/" + studentId + "mark";
                     string blob = await _azureStorage.UploadFileByContainer(school.ToString(), ans.ToJsonString(), "exam", FileName + "/" + "ans.json");*/
                     //result.studentAnswers[index].Add(blob);
-                    if (result.mark == null || result.mark.Count == 0) {
+                    if (result.mark == null || result.mark.Count == 0)
+                    {
                         List<string> annotation = new List<string>();
-                        foreach (string ids in result.studentIds) {
+                        foreach (string ids in result.studentIds)
+                        {
                             annotation.Add("");
                         }
                         result.mark = annotation;
@@ -193,7 +201,15 @@ namespace TEAMModelOS.Controllers
                     result.mark[index] = builder.ToString();
                     classResult = await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(result, result.id, new PartitionKey($"{result.code}"));
                 }
+                await Task.WhenAll(tasks);
+               /* //变更blob 大小
+                ExamInfo info = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<ExamInfo>(id.ToString(), new Azure.Cosmos.PartitionKey($"Exam-{school}"));
+                info.size = await _azureStorage.GetBlobContainerClient(school.ToString()).GetBlobsSize($"exam/{id}");
+                var messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "annotation", root = $"exam/{id}", name = school }.ToJsonString());
 
+                messageBlob.ApplicationProperties.Add("name", "BlobRoot");
+                var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
+                await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageBlob);*/
                 return Ok(new { classResult });
             }
             catch (Exception e)