Jelajahi Sumber

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

zhouj1203@hotmail.com 3 tahun lalu
induk
melakukan
96f019834d
50 mengubah file dengan 1201 tambahan dan 417 penghapusan
  1. 1 0
      Client/Program.cs
  2. 49 1
      TEAMModelFunction/ActivityHttpTrigger.cs
  3. 2 2
      TEAMModelFunction/MonitorServicesBus.cs
  4. 0 1
      TEAMModelOS.SDK/DI/AzureStorage/AzureStorageBlobExtensions.cs
  5. 2 0
      TEAMModelOS.SDK/Models/Cosmos/Common/Scoring.cs
  6. 79 0
      TEAMModelOS.SDK/Models/Cosmos/Common/SheetConfig.cs
  7. 6 64
      TEAMModelOS.SDK/Models/Cosmos/School/ExamInfo.cs
  8. 1 0
      TEAMModelOS.SDK/Models/Cosmos/School/Paper.cs
  9. 19 2
      TEAMModelOS/ClientApp/src/api/evaluation.js
  10. 3 2
      TEAMModelOS/ClientApp/src/components/coursemgt/StudentList.vue
  11. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/en-US/cusMgt.js
  12. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/cusMgt.js
  13. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/evaluation.js
  14. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/cusMgt.js
  15. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/evaluation.js
  16. 4 2
      TEAMModelOS/ClientApp/src/router/routes.js
  17. 3 1
      TEAMModelOS/ClientApp/src/store/module/answerSheet.js
  18. 6 1
      TEAMModelOS/ClientApp/src/utils/evTools.js
  19. 2 2
      TEAMModelOS/ClientApp/src/utils/html2pdf.js
  20. 3 1
      TEAMModelOS/ClientApp/src/utils/sheetConfig.js
  21. 0 4
      TEAMModelOS/ClientApp/src/view/answersheet/BaseEditor.vue
  22. 31 4
      TEAMModelOS/ClientApp/src/view/answersheet/BaseSvgBg.vue
  23. 88 7
      TEAMModelOS/ClientApp/src/view/answersheet/index.vue
  24. 7 0
      TEAMModelOS/ClientApp/src/view/evaluation/bank/TestPaperList.vue
  25. 77 70
      TEAMModelOS/ClientApp/src/view/evaluation/index/CreatePaper.vue
  26. 5 5
      TEAMModelOS/ClientApp/src/view/evaluation/index/TestPaper.vue
  27. 1 0
      TEAMModelOS/ClientApp/src/view/learnactivity/AutoCreateNew.vue
  28. 28 2
      TEAMModelOS/ClientApp/src/view/learnactivity/CreatePrivEva.less
  29. 43 4
      TEAMModelOS/ClientApp/src/view/learnactivity/CreatePrivEva.vue
  30. 30 0
      TEAMModelOS/ClientApp/src/view/learnactivity/CreateSchoolEva.less
  31. 190 100
      TEAMModelOS/ClientApp/src/view/learnactivity/CreateSchoolEva.vue
  32. 18 16
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtPrivEva.vue
  33. 2 0
      TEAMModelOS/ClientApp/src/view/learnactivity/MgtSchoolEva.vue
  34. 15 13
      TEAMModelOS/ClientApp/src/view/newcourse/NewCusMgt.vue
  35. 4 53
      TEAMModelOS/ClientApp/src/view/schoolmgmt/ClassroomSetting/ClassroomSetting.vue
  36. 0 1
      TEAMModelOS/ClientApp/src/view/student-account/AddStudent.vue
  37. 14 7
      TEAMModelOS/ClientApp/src/view/student-account/ClassMgt.vue
  38. 5 5
      TEAMModelOS/ClientApp/src/view/student-account/Index.vue
  39. 2 2
      TEAMModelOS/ClientApp/src/view/task/arb/Arbitration.vue
  40. 3 3
      TEAMModelOS/ClientApp/src/view/task/err/ErrPaper.vue
  41. 4 0
      TEAMModelOS/ClientApp/src/view/task/index.less
  42. 3 0
      TEAMModelOS/ClientApp/src/view/task/index.vue
  43. 6 4
      TEAMModelOS/ClientApp/src/view/task/mark/ByStu.vue
  44. 34 22
      TEAMModelOS/Controllers/Client/HiScanController.cs
  45. 19 5
      TEAMModelOS/Controllers/Common/ExamController.cs
  46. 275 0
      TEAMModelOS/Controllers/Common/SheetConfigController.cs
  47. 46 2
      TEAMModelOS/Controllers/Pager/PaperController.cs
  48. 4 0
      TEAMModelOS/Controllers/School/CorrectController.cs
  49. 8 4
      TEAMModelOS/Controllers/School/StudentController.cs
  50. 54 0
      TEAMModelOS/Controllers/XTest/TestController.cs

+ 1 - 0
Client/Program.cs

@@ -20,6 +20,7 @@ namespace Client
             //SSE
             try
             {
+                Console.WriteLine("12536".PadLeft(7, '0'));
                 var cts = new CancellationTokenSource();
                 var header = new Dictionary<string, string>() { { "X-Auth-Name", "IES5" } };
                //var sse = new EventSource(new Uri("https://localhost:5001/service/sse"), header, 5000);

+ 49 - 1
TEAMModelFunction/ActivityHttpTrigger.cs

@@ -573,6 +573,54 @@ namespace TEAMModelFunction
                 return new BadRequestResult();
             }
         }
+        /// <summary>
+        /// 修复容器的内容显示
+        /// </summary>
+        /// <param name="req"></param>
+        /// <param name="log"></param>
+        /// <returns></returns>
+        [FunctionName("fix-blob-content")]
+        public async Task<IActionResult> FixBlobContent(
+            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
+            ILogger log)
+        {
+            try
+            {   
+                string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
+                var data = System.Text.Json.JsonSerializer.Deserialize<JsonElement>(requestBody);
+                if (data.TryGetProperty("name", out JsonElement name) ) {
+                    var client = _azureCosmos.GetCosmosClient();
+                    List<string> prefixs = new List<string>() { "audio","doc","image","other","res","video", "thum" };
+                    var ContainerClient = _azureStorage.GetBlobContainerClient($"{name}");
+                    string scope = "private";
+                    if (data.TryGetProperty("scope", out JsonElement _scope)){
+                        scope = $"{_scope}";
+                    }
+                    var tb = "Teacher";
+                    if (scope != "private") {
+                        tb = "School";
+                    }
+                    long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+                   // List<Task<ItemResponse<Bloblog>>> responses = new List<Task<ItemResponse<Bloblog>>>();
+                    foreach (var prefix in prefixs) {
+                        List<string> items = await ContainerClient.List(prefix);
+                        foreach (var item in items) {
+                            var urlsSize = await ContainerClient.GetBlobsSize(item);
+                            Bloblog bloblog = new Bloblog { id =   Guid.NewGuid().ToString(),code= $"Bloblog-{name}", pk = "Bloblog", time = now,url= item, size = urlsSize != null && urlsSize.HasValue ? urlsSize.Value : 0, type = prefix};
+                              await client.GetContainer("TEAMModelOS", tb).UpsertItemAsync(bloblog, new Azure.Cosmos.PartitionKey(bloblog.code)) ;
+                        }
+                    }
+                  //  await Task.WhenAll(responses);
+                }
+                return new OkObjectResult(new { });
+
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"TEAMModelFunction,ActivityHttpTrigger,fix-blob-content()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                return new BadRequestResult();
+            }
+        }
+
     }
-  
 }

+ 2 - 2
TEAMModelFunction/MonitorServicesBus.cs

@@ -177,8 +177,8 @@ namespace TEAMModelFunction
                     }
                     await _azureRedis.GetRedisClient(8).HashSetAsync($"Blob:Record", new RedisValue($"{name}"), new RedisValue($"{blobsize}"));
 
-                    await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,Blob() 容器:{name}使用:{u},文件分类:{list.ToJsonString()}",
-                        GroupNames.成都开发測試群組);
+                    //await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,Blob() 容器:{name}使用:{u},文件分类:{list.ToJsonString()}",
+                    //    GroupNames.成都开发測試群組);
                 }
             }
             catch (Exception ex)

+ 0 - 1
TEAMModelOS.SDK/DI/AzureStorage/AzureStorageBlobExtensions.cs

@@ -54,7 +54,6 @@ namespace TEAMModelOS.SDK.DI
             {
                 return null;
             }
-
         }
 
         /// <summary>

+ 2 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/Scoring.cs

@@ -31,6 +31,8 @@ namespace TEAMModelOS.SDK.Models.Cosmos.Common
         //阅卷类型1 正常卷 2 异常卷 3 仲裁卷
         public string err { get; set; }
         public string improve { get; set; }
+        public string tId { get; set; }
+        public int index { get; set; }
     }
 
     public class Item

+ 79 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/SheetConfig.cs

@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Models.Cosmos.Common
+{
+    public class SheetConfig :CosmosEntity
+    {
+        public string school { get; set; }
+        public string creatorId { get; set; }
+        public string scope { get; set; }
+        /// <summary>
+        /// 关联id,如果没有关联则为null
+        /// </summary>
+        public string sid { get; set; } = null;
+
+        /// <summary>
+        ///  //列数
+        /// </summary>
+        public int columns { get; set; }
+        /// <summary>
+        /// 页面大小(回字组成的)
+        /// </summary>
+        public double pageWidth { get; set; }
+        /// <summary>
+        /// 页面大小(回字组成的)
+        /// </summary>
+        public double pageHeight { get; set; }
+        /// <summary>
+        /// 页码块数量
+        /// </summary>
+        public int pageNumBlockCount { get; set; }
+        /// <summary>
+        /// 页码起始值, 起始是3个空格方块开始,写1; 2个空格1个实心,写0
+        /// </summary>
+        public int pageNumStartValue { get; set; }
+        /// <summary>
+        /// 二值化区间(pc)-->不同扫描仪的结果图像数据有差距, 按需调整
+        /// </summary>
+        public List<int> threshValue { get; set; } = new List<int> { 190, 220, 10 };
+        /// <summary>
+        /// 二值化区间(手机)
+        /// </summary>
+        public List<int> threshValuePhone { get; set; } = new List<int> { 190, 220, 10 };
+        /// <summary>
+        /// 单个整体回字大小
+        /// </summary>
+        public double huiSize { get; set; } = 70;
+        /// <summary>
+        /// 最小回字的大小比列, 以回字最中间的方块为最小
+        /// </summary>
+        public double minHuiSize { get; set; } = 0.005;
+        /// <summary>
+        /// 最大回字的大小比列, 以回字最外层的方块为最大
+        /// </summary>
+        public double maxHuiSize { get; set; } = 0.1;
+        /// <summary>
+        /// 不检测回字区域(pc版辅助效果20%, 手机版50%)
+        /// </summary>
+        public List<double> notHuisRect { get; set; } = new List<double> { 0.3, 0.3, 0.4, 0.4 };
+        /// <summary>
+        /// 内容块 vblockCount为该模块竖向的方块数 hblockCount为该模块横向方块数
+        /// </summary>
+        public List<ConfigContent> contents { get; set; }
+    }
+    public class ConfigContent
+    {
+        public int type { get; set; }
+        public double x { get; set; }
+        public double y { get; set; }
+        public double width { get; set; }
+        public double height { get; set; }
+        public int pageNum { get; set; }
+        public int vblockCount { get; set; }
+        public int hblockCount { get; set; }
+        public string id { get; set; }
+        public bool isFix { get; set; }
+    }
+}

+ 6 - 64
TEAMModelOS.SDK/Models/Cosmos/School/ExamInfo.cs

@@ -72,7 +72,7 @@ namespace TEAMModelOS.SDK.Models
         public PeriodSimple period { get; set; }
         public List<Grade> grades { get; set; }
         public List<ExamSubject> subjects { get; set; }
-        //public long sequenceNumber { get; set; }
+       
         //public Condition conditions { get; set; }
         //public List<string> blobUrl { get; set; }
         public string progress { get; set; }
@@ -106,7 +106,7 @@ namespace TEAMModelOS.SDK.Models
         public string id { get; set; }
         public string name { get; set; }
         public int classCount { get; set; }
-
+       
 
     }
     public class PaperSimple { 
@@ -123,73 +123,15 @@ namespace TEAMModelOS.SDK.Models
         //题目类型
         public List<string> type { get; set; } = new List<string>();
         public List<int> field { get; set; } = new List<int>();
+        public  string  sheet { get; set; }        //public long sequenceNumber { get; set; }
 
         //public List<Dictionary<string, int>> record { get; set; } = new List<Dictionary<string, int>>();
-        public SheetConfig sheet { get; set; } 
-    }
 
-    public class SheetConfig
-    {
-        /// <summary>
-        ///  //列数
-        /// </summary>
-        public int columns { get; set; }
-        /// <summary>
-        /// 页面大小(回字组成的)
-        /// </summary>
-        public int pageWidth { get; set; }
-        /// <summary>
-        /// 页面大小(回字组成的)
-        /// </summary>
-        public int pageHeight { get; set; }
-        /// <summary>
-        /// 页码块数量
-        /// </summary>
-        public int pageNumBlockCount { get; set; }
-        /// <summary>
-        /// 页码起始值, 起始是3个空格方块开始,写1; 2个空格1个实心,写0
-        /// </summary>
-        public int pageNumStartValue { get; set; }
-        /// <summary>
-        /// 二值化区间(pc)-->不同扫描仪的结果图像数据有差距, 按需调整
-        /// </summary>
-        public List<int> threshValue { get; set; } = new List<int> { 190,220,10};
-        /// <summary>
-        /// 二值化区间(手机)
-        /// </summary>
-        public List<int> threshValuePhone { get; set; }=new List<int> { 190,220,10};
-        /// <summary>
-        /// 单个整体回字大小
-        /// </summary>
-        public int huiSize { get; set; } = 70;
-        /// <summary>
-        /// 最小回字的大小比列, 以回字最中间的方块为最小
-        /// </summary>
-        public double minHuiSize { get; set; } = 0.005;
-        /// <summary>
-        /// 最大回字的大小比列, 以回字最外层的方块为最大
-        /// </summary>
-        public double maxHuiSize { get; set; } = 0.1;
-        /// <summary>
-        /// 不检测回字区域(pc版辅助效果20%, 手机版50%)
-        /// </summary>
-        public List<double> notHuisRect { get; set; } = new List<double> { 0.3, 0.3, 0.4, 0.4 };
-        /// <summary>
-        /// 内容块 vblockCount为该模块竖向的方块数 hblockCount为该模块横向方块数
-        /// </summary>
-        public List<ConfigContent> contents { get; set; }
     }
 
-    public class ConfigContent {
-        public int type { get; set; }
-        public int x { get; set; }
-        public int y { get; set; }
-        public int width { get; set; }
-        public int height { get; set; }
-        public int pageNum { get; set; }
-        public int vblockCount { get; set; }
-        public int hblockCount { get; set; }
-    }
+   
+
+   
     /*public class Condition
     {
         public string period { get; set; }

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

@@ -60,6 +60,7 @@ namespace TEAMModelOS.SDK.Models
         public double score { get; set; }
         public string scope { get; set; }
         public int multipleRule { get; set; }
+        public string sheet { get; set; }
         //记录试卷大小
         public long? size { get; set; } = 0;
         

+ 19 - 2
TEAMModelOS/ClientApp/src/api/evaluation.js

@@ -1,7 +1,24 @@
 import { fetch, post } from '@/api/http'
 
 export default {
-    getFilterCount: function (data) {        
+    /* 获取条件筛选数量*/
+	getFilterCount: function (data) {        
         return post('/item/cond-count', data)
-    }
+    },
+	/* 保存答题卡 */
+	upsertSheet: function (data) {        
+        return post('/common/sheet-config/upsert', data)
+    },
+	/* 查询答题卡 */
+	findSheet: function (data) {
+	    return post('/common/sheet-config/find', data)
+	},
+	/* 删除答题卡 */
+	deleteSheet: function (data) {
+	    return post('/common/sheet-config/delete', data)
+	},
+	/* 更新试卷的答题卡字段 */
+	upsertPaperSheet:function (data) {
+	    return post('/paper/upsert-sheet', data)
+	},
 }

+ 3 - 2
TEAMModelOS/ClientApp/src/components/coursemgt/StudentList.vue

@@ -27,7 +27,7 @@
                         <PersonalPhoto :name="row.name" :picture="row.picture"></PersonalPhoto>
                     </template>
                     <template slot-scope="{ row }" slot="gradeName">
-                        <span>{{ $jsFn.getGradeNameByYear(schoolBase, searchPeriod, row.classYear) }}{{row.classYear}}</span>
+                        <span>{{ $jsFn.getGradeNameByYear(schoolBase, searchPeriod, row.classYear) }}</span>
                     </template>
                     <template slot-scope="{ row }" slot="classId">
                         <span>{{ getClassName(row.classId)}}</span>
@@ -257,8 +257,9 @@ export default {
                     return item.id == classId
                 })
                 return classesData[0].name
+            }else{
+                return '- -'
             }
-
         },
         getFirstChart(name) {
             if (name) {

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

@@ -85,7 +85,7 @@ export default {
     share:'分享到频道',
     noAc:'暂无记录',
     pdLabel:'学段:',
-    noClassCus:'课程未安排教室:',
+    noClassCus:'课程未安排班级:',
     noTImeCus:'课程未设时间:',
     listMode:'列表模式',
     ev:'评测',

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

@@ -85,7 +85,7 @@ export default {
     share:'分享到频道',
     noAc:'暂无记录',
     pdLabel:'学段:',
-    noClassCus:'课程未安排教室:',
+    noClassCus:'课程未安排班级:',
     noTImeCus:'课程未设时间:',
     listMode:'列表模式',
     ev:'评测',

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

@@ -184,7 +184,7 @@ export default {
 		paperDetails:'试卷详情',
 		paperAnalysis:'试卷分析',
 		setPaperScore:'设置试卷总分',
-		goAnswerSheet:'预览答题卡'
+		goAnswerSheet:'打印答题卡'
 	},
 	importFile:{
 		uploadSuc:'文件上传解析成功!',

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

@@ -85,7 +85,7 @@ export default {
     share: '分享到頻道',
     noAc: '暫無記錄',
     pdLabel: '學制:',
-    noClassCus: '課程未安排教室:',
+    noClassCus: '課程未安排班級:',
     noTImeCus: '課程未設定時間:',
     listMode: '清單模式',
     ev: '評量',

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

@@ -184,7 +184,7 @@ export default {
 		paperDetails: '試卷詳情',
 		paperAnalysis: '試卷分析',
 		setPaperScore: '設定試卷總分',
-		goAnswerSheet: '預覽答題卡'
+		goAnswerSheet: '列印答題卡'
 	},
 	importFile: {
 		uploadSuc: '試卷檔案上傳解析成功!',

+ 4 - 2
TEAMModelOS/ClientApp/src/router/routes.js

@@ -519,7 +519,8 @@ export const routes = [
 			name: 'createSchoolEva',
 			component: resolve => require(['@/view/learnactivity/CreateSchoolEva.vue'], resolve),
 			meta: {
-				activeName: 'schoolEvaluation'
+				activeName: 'schoolEvaluation',
+				isKeep:true
 			}
 		},
 		// 创建个人评测
@@ -528,7 +529,8 @@ export const routes = [
 			name: 'createPrivEva',
 			component: resolve => require(['@/view/learnactivity/CreatePrivEva.vue'], resolve),
 			meta: {
-				activeName: 'privateEvaluation'
+				activeName: 'privateEvaluation',
+				isKeep:true
 			}
 		},
 		//创建学习单元

+ 3 - 1
TEAMModelOS/ClientApp/src/store/module/answerSheet.js

@@ -66,7 +66,8 @@ export default {
 			subjectiveRect: []
 		},
 		config: {
-			"columns": 1, //列数
+			"id":null,
+			"columns": null, //列数
 
 			"pageWidth": PAPER_W - (ANCHORPROP.gapX - 5) * 2, //页面大小(回字组成的)
 			"pageHeight": PAPER_H - (ANCHORPROP.gapY - 5) * 2 , //页面大小(回字组成的)
@@ -93,6 +94,7 @@ export default {
 		},
 		setPaper(state, val) {
 			state.paperItem = val
+			state.config.id = val.sheetId || null
 		},
 		setPaperItems(state, val) {
 			state.paperItem.item = val

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

@@ -93,7 +93,8 @@ export default {
 				gradeIds:paper.gradeIds,
 				subjectId:paper.subjectId,
 				subjectName:paper.subjectName,
-				score:paper.score
+				score:paper.score,
+				sheet:paper.sheet || null,
 			}
 			r(paperItem)
 		})
@@ -106,6 +107,7 @@ export default {
 				name:paper.name,
 				code:paper.code,
 				blob:paper.blob,
+				sheet:paper.sheet || null,
 				scope:paper.scope,
 				scoring:paper.scoring,
 				points:paper.points,
@@ -378,6 +380,7 @@ export default {
 								itemJson.exercise.id = itemJson.id 
 								itemJson.exercise.pid = itemJson.pid 
 								itemJson.exercise.blob = path + '/' + item.url // 添加blob是方便在保存试卷是 refresh 与导入的试题区分开
+								itemJson.exercise.scope = curScope
 								itemJson.exercise.score = item.scoring ? item.scoring.score : 0
 								try{
 									itemJson.exercise = await this.doAddHost(itemJson.exercise,{ name : jsonData.name },tmdId) // 检测试题中的富文本 为有src为相对路径的音视频文件添加blob的HOST地址
@@ -442,6 +445,7 @@ export default {
 								itemJson.exercise.option = itemJson.item[0].option
 								itemJson.exercise.id = itemJson.id 
 								itemJson.exercise.pid = itemJson.pid 
+								itemJson.exercise.scope = curScope
 								itemJson.exercise.blob = path + '/' + item.url // 添加blob是方便在保存试卷是 refresh 与导入的试题区分开
 								itemJson.exercise.score = item.scoring ? item.scoring.score : 0
 								try{
@@ -508,6 +512,7 @@ export default {
 							itemJson.exercise.option = itemJson.item[0].option
 							itemJson.exercise.id = itemJson.id 
 							itemJson.exercise.pid = itemJson.pid 
+							itemJson.exercise.scope = curScope
 							itemJson.exercise.score = item.scoring ? item.scoring.score : 0
 							// jsonData.item.push(itemJson.exercise)
 							try{

+ 2 - 2
TEAMModelOS/ClientApp/src/utils/html2pdf.js

@@ -8,7 +8,7 @@ export default {
 			return new Promise((r,j) => {
             var title = store.state.answerSheet.paperItem.name
             setTimeout(() => {
-                domtoimage.toJpeg(document.querySelector('#pdfDom'), { bgcolor: '#fff', scale: 4 })
+                domtoimage.toJpeg(document.querySelector('#pdfDom'), { bgcolor: '#fff'})
                     .then((pageData) => {
                         let img = new Image();
                         img.src = pageData;
@@ -22,10 +22,10 @@ export default {
                             let pageHeight = contentWidth / a4Width * a4Height
                             let leftHeight = contentHeight
                             let position = 0
+							//a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
                             let imgWidth = a4Width
                             let imgHeight = a4Width / contentWidth * contentHeight
                             console.log(pageData)
-                            console.log(pageHeight, leftHeight)
                             let PDF = new JsPDF({
                                 unit: 'pt',
                                 format: 'a4',

+ 3 - 1
TEAMModelOS/ClientApp/src/utils/sheetConfig.js

@@ -1,5 +1,7 @@
+// const PAPER_W = 794; // 答题卡宽度
+// const PAPER_H = 1124; // 答题卡高度
 const PAPER_W = 750; // 答题卡宽度
-const PAPER_H = 1060.71; // 答题卡高度
+const PAPER_H = 1063.05; // 答题卡高度
 const ANCHORPROP = {
     width: 10, // 锚点宽度
     height: 10, // 锚点高度

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

@@ -747,9 +747,6 @@
 				editor.config.onblur = (html) => {
 					let editorDom = editor.$textElem.elems[0]
 					let scrollHeight = editorDom.scrollHeight
-					console.log(editorDom.getBoundingClientRect().height)
-					console.log(editor.config.height)
-					console.log(scrollHeight)
 					if(scrollHeight > editor.config.height){
 						
 						editor.config.height = scrollHeight > 200 ? scrollHeight : 200
@@ -786,7 +783,6 @@
 					}
 				}
 				editor.txt.append(content)
-				console.log('xxxxxxxx',content)
 				this.dragReSize(editor,curId);
 			},
 		},

+ 31 - 4
TEAMModelOS/ClientApp/src/view/answersheet/BaseSvgBg.vue

@@ -1,6 +1,6 @@
 <template>
 	<div
-		style="position:relative; width: 100%; height: 100%; overflow: hidden; margin-top: 0;border-bottom: 1px solid #ddd;">
+		style="position:relative; width: 100%; height: 100%; overflow: hidden; margin-top: 0;">
 		<div v-show="ids === 'svg0'" class="sheet-paper-title">
 			<BaseTitleEditor :ids="'sheetName' + ids" :content="sheetPaperName"></BaseTitleEditor>
 		</div>
@@ -23,6 +23,10 @@
 				type: String,
 				default: "",
 			},
+			sheetId: {
+				type: String,
+				default: "",
+			},
 			total: {
 				type: Number,
 				default: 0,
@@ -34,7 +38,8 @@
 				snap: null,
 				pageBox: null,
 				anchorBox: null,
-				sheetPaperName:''
+				sheetIdBox: null,
+				sheetPaperName: ''
 			};
 		},
 		components: {
@@ -86,11 +91,30 @@
 					});
 				this.pageBox.add(page);
 			},
+
+			doRenderId(snap) {
+				this.sheetIdBox.remove(); // 先清除之前的绘制内容
+				this.sheetIdBox = this.snap.paper.g();
+				if(this.sheetId){
+					var page = snap
+						.text(
+							200,
+							PAPER_H - 40,
+							`ID:${ this.sheetId }`
+							)
+						.attr({
+							fontSize: 12,
+							fontFamily: "Times New Roman",
+						});
+					this.pageBox.add(page);
+				}
+			}
 		},
 		mounted() {
 			var snap = Snap("#" + this.ids);
 			this.pageBox = snap.paper.g();
 			this.anchorBox = snap.paper.g();
+			this.sheetIdBox = snap.paper.g();
 			let anchorProp = ANCHORPROP;
 			// snap
 			//   .rect(SVG_BORDER_PROP.x, SVG_BORDER_PROP.y, SVG_BORDER_PROP.width, SVG_BORDER_PROP.height)
@@ -164,6 +188,7 @@
 			this.snap = snap;
 			this.doRenderPage(snap);
 			this.doRenderPageAnchor(snap);
+			this.doRenderId(snap);
 
 		},
 		computed: {
@@ -182,7 +207,9 @@
 			total(n, o) {
 				this.doRenderPage(this.snap);
 			},
-			immediate: true
+			sheetId(n, o) {
+				this.doRenderId(this.snap);
+			},
 		},
 	};
 </script>
@@ -194,7 +221,7 @@
 		left: 80px;
 
 		.w-e-text {
-			font-size:18px !important;
+			font-size: 18px !important;
 			text-align: center !important;
 		}
 	}

+ 88 - 7
TEAMModelOS/ClientApp/src/view/answersheet/index.vue

@@ -1,11 +1,14 @@
 <template>
 	<div class="sheet-wrap animated fadeIn">
-		<Loading v-if="isLoading"></Loading>
+		<Spin v-if="isLoading" fix>
+			 <Icon type="ios-loading" size=18 class="demo-spin-icon-load"></Icon>
+			 <div>答题卡生成中...</div>
+		</Spin>
 		<div class="sheet-left">
 			<div class="sheet-container" id="pdfDom" v-if="isRender">
 				<!-- <router-view  v-if="isRouterAlive"/> -->
 				<BaseSvgBg v-for="(page, pageIndex) in pages" :key="pageIndex" :ids="'svg' + pageIndex"
-					:total="pages.length"></BaseSvgBg>
+					:total="pages.length" :sheetId="sheetId"></BaseSvgBg>
 				<SheetBaseInfo></SheetBaseInfo>
 				<!-- 绘制主区域 -->
 				<div class="sheet-groups">
@@ -34,14 +37,14 @@
 		</div>
 		<div class="sheet-right">
 			<Button @click="goBack">返回上级</Button>
-			<Button @click="doDownload" class="btn-download">下载PDF</Button>
+			<Button @click="doDownload" class="btn-download" v-if="curPaper.id">打印答题卡</Button>
 			<div>
-				<p class="sheet-right-title">基础设置</p>
+				<!-- <p class="sheet-right-title">基础设置</p>
 				<div>
 					<Checkbox v-model="isColumn">客观题竖向排列</Checkbox>
 					<Checkbox v-model="isShowErCode">添加二维码</Checkbox>
 					<Checkbox v-model="isShowLines">密封线</Checkbox>
-				</div>
+				</div> -->
 				<p class="sheet-right-title" v-if="!isAutoCreate">添加题型</p>
 				<div style="display: flex;flex-wrap: wrap;" v-if="!isAutoCreate">
 					<span class="add-block-item" @click="onAddType('0')"> + 客观题</span>
@@ -127,6 +130,7 @@
 	import SheetComplete from "./SheetComplete";
 	import SheetSubjective from "./SheetSubjective";
 	import BaseSvgBg from "./BaseSvgBg";
+	import blobTool from '@/utils/blobTool.js'
 	import {
 		PAPER_W,
 		PAPER_H,
@@ -154,7 +158,9 @@
 		},
 		data() {
 			return {
+				curPaper:null,
 				indexErr: false,
+				sheetId:null,
 				addItems: [],
 				addType: {
 					startIndex: null,
@@ -173,6 +179,7 @@
 				isColumn: false,
 				isRouterAlive: true,
 				isRender: true,
+				isFromExam:false,
 				pages: [],
 				items: [],
 				groupItems: {
@@ -192,6 +199,8 @@
 		created() {
 			let routerPaper = this.$route.params.paper || this.paper
 			console.log(routerPaper)
+			this.curPaper = routerPaper
+			this.isFromExam = Boolean(routerPaper && routerPaper.examCode)
 			this.$store.commit('clearAllConfig')
 			if (routerPaper) {
 				this.$store.commit('clearFixArr')
@@ -253,12 +262,83 @@
 					this.$Message.warning('添加信息不能为空!')
 				}
 			},
+			/* 打印答题卡 */
 			doDownload() {
 				this.isLoading = true
-				this.getPdf().then(res => {
+				this.doSaveSheet().then(async res => {
+					let paperInfo = this.$store.state.answerSheet.paperItem
+					this.sheetId = res.config.id
+					if(this.isFromExam){
+						this.$EventBus.$emit('createSheetId',res.config.id)
+					}else{
+						// await this.refreshPaperSheet(paperInfo,res.config.id)
+						await this.relatePaperSheet(paperInfo,res.config.id)
+					}
+					this.getPdf().then(r => {
+						this.isLoading = false
+					})
+				}).catch(err => {
+					console.log(err)
 					this.isLoading = false
 				})
 			},
+			
+			/* 更新试卷的sheet字段 */
+			// refreshPaperSheet(paperInfo,sheetId){
+			// 	return new Promise(async (r,j) => {
+			// 		console.log(paperInfo)
+			// 		paperInfo.sheet = sheetId
+			// 		let blobPaper = await this.$evTools.createBlobPaper(paperInfo)
+			// 		console.log(blobPaper)
+			// 		// 首先保存新题目的JSON文件到Blob 然后返回URL链接
+			// 		let paperFile = new File([JSON.stringify(blobPaper)], "index.json");
+			// 		// 获取初始化Blob需要的数据
+			// 		let sasData = paperInfo.scope === 'private' ? await this.$tools
+			// 		.getPrivateSas() : await this.$tools.getSchoolSas()
+			// 		//初始化Blob
+			// 		let containerClient = new blobTool(sasData.url, sasData.name, sasData.sas, paperInfo.scope)
+			// 		containerClient.upload(paperFile,'paper/' + paperInfo.name, undefined, false).then(res => {
+			// 			r(200)
+			// 		}).catch(e => j(e))
+			// 	})
+			// },
+			
+			relatePaperSheet(paperInfo,sheetId){
+				return new Promise((r,j) => {
+					this.$api.evaluation.upsertPaperSheet({
+						"id": paperInfo.id,
+						"code": paperInfo.code.replace('Paper-',''),
+						"scope": paperInfo.scope,
+						"sheet": sheetId
+					}).then(res => {
+						if(!res.error){
+							r(res)
+						}else{
+							j(res.error)
+						}
+					}).catch(err => j(err))
+				})
+			},
+			
+			/* 保存答题卡模板数据 */
+			doSaveSheet(){
+				return new Promise((r,j) => {
+					let paperInfo = this.$store.state.answerSheet.paperItem
+					let configParams = this.$store.state.answerSheet.config
+					console.log(paperInfo);
+					configParams.code = paperInfo.examCode ? paperInfo.examCode.replace('Exam-','') : paperInfo.code.replace('Paper-','')
+					configParams.school = paperInfo.examScope === 'school' ? this.$store.state.userInfo.schoolCode : ''
+					configParams.scope = paperInfo.examScope || paperInfo.scope
+					configParams.creatorId =  this.$store.state.userInfo.TEAMModelId
+					this.$api.evaluation.upsertSheet(configParams).then(res => {
+						if(!res.error){
+							r(res)
+						}else{
+							j(res.error)
+						}
+					}).catch(err => j(err))
+				})
+			},
 			goBack() {
 				let params = {}
 				let curEditPaper = localStorage.getItem('c_edit_paper')
@@ -461,7 +541,8 @@
 		width: 100%;
 
 		.ivu-spin {
-			background-color: #000000 !important;
+			height: 100vh;
+			background-color: #ffffffd1 !important;
 		}
 	}
 

+ 7 - 0
TEAMModelOS/ClientApp/src/view/evaluation/bank/TestPaperList.vue

@@ -177,6 +177,7 @@
 			// this.getPaperList()
 			// this.doFilter()
 			this.isShowSchoolBank = this.$route.name === "schoolBank";
+			
 		},
 		activated() {
 			this.isShowSchoolBank = this.$route.name === "schoolBank";
@@ -216,6 +217,7 @@
 						name: paper.name,
 						code: paper.code,
 						type: paper.scope,
+						scope: paper.scope,
 						paperPeriod: schoolInfo && paper.scope === 'school' ? this.schoolInfo.period.map(i => i.id)
 							.indexOf(paper.periodId) : null,
 						paperGrade: schoolInfo && paper.scope === 'school' ? paper.gradeIds : [],
@@ -502,6 +504,11 @@
 				this.schoolInfo = schoolProfile.school_base
 				this.periodList = this.schoolInfo.period
 			}
+			if(!this.isShowSchoolBank){
+				this.$nextTick(() => {
+					this.$refs.baseFilter.filterOriginChange(this.$store.state.userInfo.TEAMModelId)
+				})
+			}
 		},
 		computed: {
 			isSchool() {

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

@@ -93,7 +93,6 @@
 								</TabPane>
 								<TabPane :label="$t('evaluation.paperList.tab5')" name="preview" :index="4"
 									tab="createTest">
-									<!--<TeacherPreview :testPaper="evaluationInfo.testPaper[currentSubjectIndex]" :paperName="evaluationInfo.name"></TeacherPreview>-->
 									<vuescroll ref="paperRef" @handle-scroll="handleScroll">
 										<TestPaper v-if="!examAnalysisStatus" :paper="evaluationInfo"
 											:class="examAnalysisStatus ? '':'animated fadeIn'" ref="testPaper"
@@ -116,7 +115,6 @@
 	import blobTool from '@/utils/blobTool.js'
 	import AutoCreate from '@/view/learnactivity/AutoCreateNew.vue'
 	import ManualCreate from '@/view/learnactivity/ManualCreate.vue'
-	import TeacherPreview from '@/view/learnactivity/TeacherPreview.vue'
 	import StudentPreview from '@/view/learnactivity/StudentPreview.vue'
 	import BaseImport from '../components/BaseImport.vue'
 	import TestPaper from './TestPaper.vue'
@@ -125,7 +123,6 @@
 		components: {
 			AutoCreate,
 			ManualCreate,
-			TeacherPreview,
 			StudentPreview,
 			TestPaper,
 			BaseImport
@@ -160,6 +157,7 @@
 					name: this.$t('evaluation.paperList.defaultName'),
 					target: [],
 					type: 'private',
+					scope: 'private',
 					publish: '',
 					paperPeriod: 0,
 					paperGrade: [],
@@ -175,13 +173,13 @@
 			}
 		},
 		async created() {
-			console.log('==========', this.$route)
 			/* 查询当前是否有保存的组卷方式,如果已经保存则直接读取,优先读取传过来的参数 */
 			let curCreateType = localStorage.getItem('curCreateType')
 			this.evaluationInfo.createType = this.$route.params.type || curCreateType || 'auto'
 			this.setActiveTab(this.evaluationInfo.createType)
 			localStorage.setItem('curCreateType', this.evaluationInfo.createType)
 			this.evaluationInfo.type = this.$route.name === 'newSchoolPaper' ? 'school' : 'private'
+			this.evaluationInfo.scope = this.evaluationInfo.type
 			this.getSchoolBaseInfo().then(res => {
 				if (!res) return
 				this.schoolInfo = res
@@ -955,22 +953,27 @@
 							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, null, false
-											)
-										.then(res => {
+								try{
+									let item = res.files[i]
+									console.log(item)
+									if (item.scope == 'school') {
+										containerClient.copyFolder('paper/' + paperItem.name +
+												'/', 'item/' + item.id, schoolBlob, null, false
+												)
+											.then(res => {
+												r(200)
+											})
+									} else {
+										containerClient.copyFolder('paper/' + paperItem.name +
+											'/', 'item/' + item.id, privateBlob, null,
+											false).then(res => {
 											r(200)
 										})
-								} else {
-									containerClient.copyFolder('paper/' + paperItem.name +
-										'/', 'item/' + item.id, privateBlob, null,
-										false).then(res => {
-										r(200)
-									})
+									}
+								}catch(e){
+									console.log(e)
 								}
+								
 							}))
 						}
 						promiseArr.push(new Promise(async (r, j) => {
@@ -987,64 +990,69 @@
 						}))
 						// 进行试卷文件上传Blob
 						Promise.all(promiseArr).then(async result => {
-							/* 上传前进行试卷blob查询size */
-							let afterSizeArr = await blobTool.checkPrefixSize(['paper/' +
-								paperItem.name
-							], containerName)
-							let afterSize = this._.sum(afterSizeArr.map(i => i.size))
-							console.log('试卷上传后的size', afterSize)
-							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
-												.$t('evaluation.paperList.editSuc') :
-												this.$t(
-													'evaluation.paperList.saveSuc'))
-											this.isLoading = false
+							try{
+								/* 上传前进行试卷blob查询size */
+								let afterSizeArr = await blobTool.checkPrefixSize(['paper/' +
+									paperItem.name
+								], containerName)
+								let afterSize = this._.sum(afterSizeArr.map(i => i.size))
+								console.log('试卷上传后的size', afterSize)
+								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
+													.$t('evaluation.paperList.editSuc') :
+													this.$t(
+														'evaluation.paperList.saveSuc'))
+												this.isLoading = false
 
-											// 子题保存之后 统一更新blobSize
-											blobTool.updateSize([{
-												url: 'paper/' + paperItem.name,
-												size: afterSize - beforeSize
-											}], containerName).then(res => {
-												this.$router.push({
-													name: this
-														.evaluationInfo
-														.type ===
-														'private' ?
-														'personalBank' :
-														'schoolBank',
-													params: {
-														tabName: 'paper'
-													}
+												// 子题保存之后 统一更新blobSize
+												blobTool.updateSize([{
+													url: 'paper/' + paperItem.name,
+													size: afterSize - beforeSize
+												}], containerName).then(res => {
+													this.$router.push({
+														name: this
+															.evaluationInfo
+															.type ===
+															'private' ?
+															'personalBank' :
+															'schoolBank',
+														params: {
+															tabName: 'paper'
+														}
+													})
+												}).catch(err => {
+													this.$Message.error(this.$t(
+														'evaluation.newExercise.uploadErrorTip'
+														));
 												})
-											}).catch(err => {
+											} else {
 												this.$Message.error(this.$t(
-													'evaluation.newExercise.uploadErrorTip'
-													));
-											})
-										} else {
+													'evaluation.paperList.saveFail'))
+												this.isLoading = false
+											}
+										},
+										err => {
 											this.$Message.error(this.$t(
 												'evaluation.paperList.saveFail'))
 											this.isLoading = false
 										}
-									},
-									err => {
-										this.$Message.error(this.$t(
-											'evaluation.paperList.saveFail'))
-										this.isLoading = false
-									}
-								)
-							}
+									)
+								}
+							
+								}catch(e) {
+									console.log(e)
+								}
 						})
 					} catch (e) {
 						this.$Message.error(this.$t('evaluation.paperList.saveItemsFailTip'))
@@ -1093,8 +1101,6 @@
 				)
 			},
 			
-
-
 			/* 渲染需要编辑的试卷信息 */
 			async doRender(paper) {
 				console.log('传进来的paper')
@@ -1111,6 +1117,7 @@
 					name: paper.name,
 					code: paper.code,
 					type: paper.scope,
+					scope: paper.scope,
 					paperPeriod: schoolInfo && paper.scope === 'school' ? this.schoolInfo.period.map(i => i.id)
 						.indexOf(paper.periodId) : null,
 					paperGrade: schoolInfo && paper.scope === 'school' ? paper.gradeIds.map(i => +i) : [],

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

@@ -25,13 +25,13 @@
 							<Button class="base-info-btn" type="info" @click="onCleanPaper" v-show="paperInfo.item.length && !isShowAnalysis">{{$t('evaluation.paperList.cleanItems')}}</Button>
 							<Button class="base-info-btn" type="info" @click="onViewModelChange" v-show="paperInfo.item.length && !isShowAnalysis">{{ `${ viewModel === 'type' ? this.$t('evaluation.paperList.orderByList') : this.$t('evaluation.paperList.orderByType') }` }}</Button>
 							<Button class="base-info-btn" type="info" @click="isShowAnalysis = !isShowAnalysis" v-show="paperInfo.item.length">{{ isShowAnalysis ? this.$t('evaluation.paperList.paperDetails') : this.$t('evaluation.paperList.paperAnalysis')}}</Button>
-							<Button class="base-info-btn" type="info" @click="goAnswerSheet" v-show="paperInfo.item.length">{{  $t('evaluation.paperList.goAnswerSheet') }}</Button>
+							<Button class="base-info-btn" type="info" @click="goAnswerSheet" v-show="paperInfo.item.length && paperInfo.id">{{  $t('evaluation.paperList.goAnswerSheet') }}</Button>
 						</div>
 						<div  v-if="isExamPaper">
 							<Button class="base-info-btn" type="info" @click="onHandleToggle" v-show="paperInfo.item.length && !isShowAnalysis">{{ isAllOpen ? $t('evaluation.index.collapseAll') : $t('evaluation.index.openAll')}}</Button>
 							<Button class="base-info-btn" type="info" @click="onViewModelChange" v-show="paperInfo.item.length && !isShowAnalysis">{{ `${ viewModel === 'type' ? this.$t('evaluation.paperList.orderByList') : this.$t('evaluation.paperList.orderByType') }` }}</Button>
 							<Button class="base-info-btn" type="info" @click="isShowAnalysis = !isShowAnalysis" v-show="paperInfo.item.length && !isHideAnalysis">{{ isShowAnalysis ? this.$t('evaluation.paperList.paperDetails') : this.$t('evaluation.paperList.paperAnalysis')}}</Button>
-							<Button class="base-info-btn" type="info" @click="goAnswerSheet" v-show="paperInfo.item.length">{{  $t('evaluation.paperList.goAnswerSheet') }}</Button>
+							<Button class="base-info-btn" type="info" @click="goAnswerSheet" v-show="paperInfo.item.length && paperInfo.id">{{  $t('evaluation.paperList.goAnswerSheet') }}</Button>
 						</div>
 					</div>
 
@@ -158,9 +158,6 @@
 				paperDiff: 0
 
 			}
-		},
-		created() {
-
 		},
 		methods: {
 			goAnswerSheet(){
@@ -304,9 +301,11 @@
 			// this.isShowSave = window.location.pathname === '/home/evaluation/testPaper'
 			let paper = this.paper || this.$route.params.paper || JSON.parse(localStorage.getItem('_paperInfo'))
 			if (!paper) return
+			console.log(paper)
 			// localStorage.setItem('_paperInfo', JSON.stringify(paper))
 			this.paperInfo = paper // 自己页面的值
 			this.paperDiff = paper.item ? this.handleDiffCalc(paper.item) : 0
+			
 		},
 		computed: {
 			isAuto() {
@@ -319,6 +318,7 @@
 		watch: {
 			paper: {
 				handler(newValue, oldValue) {
+					console.log(newValue)
 					this.paperInfo = newValue
 					// localStorage.setItem('_paperInfo', JSON.stringify(newValue))
 					this.paperDiff = newValue.item ? this.handleDiffCalc(newValue.item) : 4

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

@@ -300,6 +300,7 @@
 				if(!this.isSchoolPaper) return
 				this.itemConds = this.itemConds.length ? this.itemConds : await this.getFilterCount()
 				if(this.itemConds.length){
+					console.log(this.periodCode)
 					let types = this.quInfos.map(i => i.type)
 					let curSubjectCond = this.itemConds.find(i => i.id === this.periodCode).subjects.find(j => j.id === this.subjectCode)
 					if(!curSubjectCond) return

+ 28 - 2
TEAMModelOS/ClientApp/src/view/learnactivity/CreatePrivEva.less

@@ -148,6 +148,32 @@
     }
 }
 
+.teacher-preview-container .back-to-top:hover {
+    background: rgb(128,128,128);
+}
 
-
-
+.teacher-preview-container .back-to-top .ivu-icon {
+    font-size: 26px;
+    color: white;
+}
+.teacher-preview-container .back-to-top {
+    position: fixed;
+    right: ~"calc(50px - 100%)";
+    bottom: 40px;
+    height: 48px;
+    width: 50px;
+    background: #595959;
+    z-index: 99999;
+    cursor: pointer;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+}
+.teacher-preview-container {
+    height: 100%;
+    background: #404040;
+    padding: 10px;
+    padding-bottom: 30px;
+    position:relative;
+}

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

@@ -66,11 +66,20 @@
                             <ManualPaper @selectPaper="selectPaper" :selectedId="evaluationInfo.paperInfo[0] ? evaluationInfo.paperInfo[0].id : ''"></ManualPaper>
                         </TabPane>
                         <TabPane :label="$t('learnActivity.createEv.perviewLabel')" name="preview" :index="2" tab="createTest">
-                            <TeacherPreview :testPaper="evaluationInfo.paperInfo[0]"></TeacherPreview>
+                            <!-- <TeacherPreview :testPaper="evaluationInfo.paperInfo[0]"></TeacherPreview> -->
+                            <div class="teacher-preview-container">
+                                <!--返回顶部-->
+                                <div class="back-to-top" :title="$t('learnActivity.mgtScEv.returnTop')" v-show="showBack" @click="handleBackToTop">
+                                    <Icon type="ios-arrow-up" />
+                                </div>
+                                <vuescroll ref="paper-preview" @handle-scroll="checkBackTop">
+                                    <TestPaper :paper="evaluationInfo.paperInfo[0]" isExamPaper></TestPaper>
+                                </vuescroll>
+                            </div>
                         </TabPane>
-                        <TabPane :label="$t('learnActivity.createEv.answerPreview')" name="student" :index="6" tab="createTest">
+                        <!-- <TabPane :label="$t('learnActivity.createEv.answerPreview')" name="student" :index="6" tab="createTest">
                             <StudentPreview></StudentPreview>
-                        </TabPane>
+                        </TabPane> -->
                     </Tabs>
                 </div>
             </div>
@@ -80,12 +89,14 @@
 </template>
 <script>
 import BlobTool from '@/utils/blobTool.js'
+import TestPaper from '@/view/evaluation/index/TestPaper.vue'
 import ManualPaper from './ManualPaper.vue'
 import TeacherPreview from './TeacherPreview.vue'
 import StudentPreview from './StudentPreview.vue'
 
 export default {
     components: {
+        TestPaper,
         TeacherPreview,
         StudentPreview,
         ManualPaper
@@ -295,6 +306,28 @@ export default {
         }
     },
     methods: {
+        /**vuescroll回到顶部 */
+        handleBackToTop() {
+            this.$refs['paper-preview'].scrollTo(
+                {
+                    y: '0'
+                },
+                300
+            )
+        },
+        /**
+         * 判断是否显示回到顶部按钮
+         * @param vertical
+         * @param horizontal
+         * @param nativeEvent
+         */
+        checkBackTop(vertical, horizontal, nativeEvent) {
+            if (vertical.scrollTop > 100) {
+                this.showBack = true
+            } else {
+                this.showBack = false
+            }
+        },
         publishChange(data) {
             if (data == 0) {
                 this.startTime = new Date()
@@ -579,6 +612,8 @@ export default {
             })
         },
         comfirmSelectPaper(simplePaper, fullPaper) {
+            fullPaper.examScope = this.mode
+            fullPaper.examCode = this.$store.state.userInfo.schoolCode
             this.evaluationInfo.paperInfo[0] = fullPaper
             this.evaluationInfo.papers[0] = simplePaper
             this.goToPreview()
@@ -687,7 +722,7 @@ export default {
                         let examId = res.exam.id
                         // let privateSas = this.$store.state.privateSas
                         let privateSas = {}
-                        if(this.$store.state.user.userProfile){
+                        if (this.$store.state.user.userProfile) {
                             let blobInfo = this.$store.state.user.userProfile
                             privateSas.sas = '?' + blobInfo.blob_sas
                             privateSas.url = blobInfo.blob_uri.slice(0, blobInfo.blob_uri.lastIndexOf(this.$store.state.userInfo.TEAMModelId) - 1)
@@ -885,6 +920,10 @@ export default {
 @import "./CreatePrivEva.less";
 </style>
 <style>
+.teacher-preview-container .paper-main-wrap {
+    padding: 0px;
+    margin-top: 0px;
+}
 .evaluation-attr-wrap .ivu-form .ivu-form-item-label {
     color: #a5a5a5;
 }

+ 30 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/CreateSchoolEva.less

@@ -170,3 +170,33 @@
 .ivu-select-nochoose {
     background-color: none !important;
 }
+
+.teacher-preview-container .back-to-top:hover {
+    background: rgb(128,128,128);
+}
+
+.teacher-preview-container .back-to-top .ivu-icon {
+    font-size: 26px;
+    color: white;
+}
+.teacher-preview-container .back-to-top {
+    position: fixed;
+    right: ~"calc(50px - 100%)";
+    bottom: 40px;
+    height: 48px;
+    width: 50px;
+    background: #595959;
+    z-index: 99999;
+    cursor: pointer;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+}
+.teacher-preview-container {
+    height: 100%;
+    background: #404040;
+    padding: 10px;
+    padding-bottom: 30px;
+    position:relative;
+}

+ 190 - 100
TEAMModelOS/ClientApp/src/view/learnactivity/CreateSchoolEva.vue

@@ -38,17 +38,12 @@
                                     <Option v-for="(item,index) in testType" :value="item.id" :key="index">{{ item.name }}</Option>
                                 </Select>
                             </FormItem>
-                            <!-- 校园评测施测对象为系统班级 -->
+                            <!-- 校园评测施测对象为教学班或行政班 -->
                             <FormItem :label="$t('learnActivity.createEv.evTarget')" prop="targets">
                                 <el-cascader size="small" :show-all-levels="false" clearable filterable v-model="evaluationInfo.targets" :options="csOptions" :props="props" @change="treeChange" style="width:100%;">
                                 </el-cascader>
                             </FormItem>
                             <FormItem :label="$t('learnActivity.createEv.publishType')" prop="publish">
-                                <!-- <RadioGroup v-model="evaluationInfo.publish" style="color:white;">
-                                    <Radio :label="item.value" :key="index" v-for="(item,index) in $GLOBAL.PUBLISH_TYPE()" style="width:120px;">
-                                        <span>{{ item.label }}</span>
-                                    </Radio>
-                                </RadioGroup> -->
                                 <Checkbox v-model="evaluationInfo.publish" :true-value="$GLOBAL.PUBLISH_TYPE()[0].value" :false-value="$GLOBAL.PUBLISH_TYPE()[1].value" @on-change="publishChange">
                                     <span style="color:white;margin-left:5px;user-select: none;">{{$GLOBAL.PUBLISH_TYPE()[0].label}}</span>
                                 </Checkbox>
@@ -79,14 +74,24 @@
                             <ManualPaper :periodId="evaluationInfo.period.id" :gradesObj="evaluationInfo.grades" :subjectId="evaluationInfo.paperInfo[curSubIndex].subjectId" @selectPaper="selectPaper" :selectedId="evaluationInfo.paperInfo[curSubIndex].id" :source="$store.state.userInfo.schoolCode"></ManualPaper>
                         </TabPane>
                         <TabPane :label="$t('learnActivity.createEv.perviewLabel')" name="preview" :index="2" tab="createTest">
-                            <TeacherPreview :testPaper="evaluationInfo.paperInfo[curSubIndex]" :examAnalysisStatus="examAnalysisStatus"></TeacherPreview>
+                            <!-- <TeacherPreview :testPaper="evaluationInfo.paperInfo[curSubIndex]" :examAnalysisStatus="examAnalysisStatus"></TeacherPreview> -->
+                            <div class="teacher-preview-container">
+                                <!--返回顶部-->
+                                <div class="back-to-top" :title="$t('learnActivity.mgtScEv.returnTop')" v-show="showBack" @click="handleBackToTop">
+                                    <Icon type="ios-arrow-up" />
+                                </div>
+                                <vuescroll ref="paper-preview" @handle-scroll="checkBackTop">
+                                    <TestPaper :paper="evaluationInfo.paperInfo[curSubIndex]" isExamPaper @createSheetId="getSheetId"></TestPaper>
+                                </vuescroll>
+                            </div>
                         </TabPane>
                         <TabPane :label="$t('learnActivity.createEv.importLabel')" name="import" v-if="evaluationInfo.paperInfo[curSubIndex].createType == 'import'" :index="4" tab="createTest">
                             <BaseImport @importedQuestions="getImportQuestions" @goToPreview="goToPreview"></BaseImport>
                         </TabPane>
-                        <TabPane :label="$t('learnActivity.createEv.answerPreview')" name="student" :index="6" tab="createTest">
+                        <!-- 去掉作答预览功能 -->
+                        <!-- <TabPane :label="$t('learnActivity.createEv.answerPreview')" name="student" :index="6" tab="createTest">
                             <StudentPreview></StudentPreview>
-                        </TabPane>
+                        </TabPane> -->
                     </Tabs>
                 </div>
             </div>
@@ -105,20 +110,21 @@
 </template>
 <script>
 import BlobTool from '@/utils/blobTool.js'
+import TestPaper from '@/view/evaluation/index/TestPaper.vue'
 import ManualPaper from './ManualPaper.vue'
 import BaseImport from '@/view/evaluation/components/BaseImport.vue'
-import TeacherPreview from './TeacherPreview.vue'
 import StudentPreview from './StudentPreview.vue'
 export default {
     components: {
-        TeacherPreview,
         StudentPreview,
         BaseImport,
         ManualPaper,
+        TestPaper
     },
     data() {
         let _this = this
         return {
+            showBack: false,
             schoolClasses: [],
             props: {
                 multiple: true,
@@ -202,10 +208,57 @@ export default {
             befPeriod: {
                 id: '',
                 name: ''
-            }
+            },
+            stuList: []
         }
     },
+    mounted() {
+        this.$EventBus.$off('createSheetId')
+        this.$EventBus.$on('createSheetId', sheetId => {
+            this.getSheetId(sheetId)
+        })
+    },
     methods: {
+        //获取答题卡id
+        getSheetId(id) {
+            console.log('答题卡id:', id)
+        },
+        /**vuescroll回到顶部 */
+        handleBackToTop() {
+            this.$refs['paper-preview'].scrollTo(
+                {
+                    y: '0'
+                },
+                300
+            )
+        },
+        /**
+         * 判断是否显示回到顶部按钮
+         * @param vertical
+         * @param horizontal
+         * @param nativeEvent
+         */
+        checkBackTop(vertical, horizontal, nativeEvent) {
+            if (vertical.scrollTop > 100) {
+                this.showBack = true
+            } else {
+                this.showBack = false
+            }
+        },
+        findStuList() {
+            let params = {
+                code: this.$store.state.userInfo.schoolCode,
+                scope: 'school'
+            }
+            this.$api.courseMgmt.findStulist(params).then(
+                res => {
+                    this.stuList = res.stuList
+                },
+                err => {
+                    this.$Message.error('API error')
+                }
+            )
+        },
         test() {
             console.log(this.evaluationInfo)
         },
@@ -254,6 +307,7 @@ export default {
         },
 
         treeChange(data) {
+            console.log('选择数据', data)
             //获取classIds
             this.evaluationInfo.classes = data.map(item => {
                 return item[1]
@@ -304,6 +358,7 @@ export default {
             })
         },
         comfirmSelectPaper(simplePaper, fullPaper) {
+            console.log('挑选试卷', arguments)
             let curSubName = this.evaluationInfo.paperInfo[this.curSubIndex].subjectName
             let curSubId = this.evaluationInfo.paperInfo[this.curSubIndex].subjectId
             this.evaluationInfo.paperInfo[this.curSubIndex] = fullPaper
@@ -412,7 +467,9 @@ export default {
                         score: 100,
                         item: [],
                         filter: {},
-                        name: this.$t('learnActivity.createEv.defaultPaper')
+                        name: this.$t('learnActivity.createEv.defaultPaper'),
+                        examScope: this.mode,
+                        examCode: this.$store.state.userInfo.schoolCode
                     })
                 }
             }
@@ -506,8 +563,21 @@ export default {
             if (!flag) {
                 return
             }
+            let apiPapers = this.getPaperInfo()
+            //线下阅卷需要检查答题卡数据
+            if (this.evaluationInfo.source == '2') {
+                apiPapers.forEach((item, index) => {
+                    if (!item.sheet) {
+                        this.$Message.error(this.evaluationInfo.subjects[index].name + '暂无答题卡信息')
+                        this.isLoading = false
+                    }
+                })
+                console.log('查看数据:', this.evaluationInfo)
+            }
+
+
             //通过验证,保存评测
-            this.isLoading = true
+            // this.isLoading = true
             let requestData = {
                 pk: 'Exam',
                 id: this.evaluationInfo.id || null,
@@ -519,7 +589,7 @@ export default {
                 period: this.evaluationInfo.period,
                 grades: this.evaluationInfo.grades,
                 subjects: this.evaluationInfo.subjects,
-                papers: this.getPaperInfo(),
+                papers: apiPapers,
                 examType: this.evaluationInfo.examType,
                 year: new Date().getFullYear(),
                 range: this.mode,
@@ -533,88 +603,87 @@ export default {
                 // blobcntr: this.$store.state.userInfo.schoolCode //后面新增字段 (废弃)
                 owner: 'school' //后面新增字段
             }
+            console.log('API数据:', requestData)
 
-            this.$api.learnActivity.SaveExamInfo(requestData).then(
-                res => {
-                    if (res.error == null) {
-
-                        //发布成功需要备份试卷数据
-                        let examId = res.exam.id
-                        // let privateSas = this.$store.state.privateSas
-                        let privateSas = {}
-                        if(this.$store.state.user.userProfile){
-                            let blobInfo = this.$store.state.user.userProfile
-                            privateSas.sas = '?' + blobInfo.blob_sas
-                            privateSas.url = blobInfo.blob_uri.slice(0, blobInfo.blob_uri.lastIndexOf(this.$store.state.userInfo.TEAMModelId) - 1)
-                            privateSas.name = this.$store.state.userInfo.TEAMModelId
-                        }
+            // this.$api.learnActivity.SaveExamInfo(requestData).then(
+            //     res => {
+            //         if (res.error == null) {
+            //             //发布成功需要备份试卷数据
+            //             let examId = res.exam.id
+            //             let privateSas = {}
+            //             if (this.$store.state.user.userProfile) {
+            //                 let blobInfo = this.$store.state.user.userProfile
+            //                 privateSas.sas = '?' + blobInfo.blob_sas
+            //                 privateSas.url = blobInfo.blob_uri.slice(0, blobInfo.blob_uri.lastIndexOf(this.$store.state.userInfo.TEAMModelId) - 1)
+            //                 privateSas.name = this.$store.state.userInfo.TEAMModelId
+            //             }
 
-                        let schoolSas = {
-                            sas: '?' + this.$store.state.user.schoolProfile.blob_sas,
-                            url: this.$store.state.user.schoolProfile.blob_uri.slice(0, this.$store.state.user.schoolProfile.blob_uri.lastIndexOf(this.$store.state.userInfo.schoolCode) - 1),
-                            name: this.$store.state.userInfo.schoolCode
-                        }
-                        let schoolBlob = new BlobTool(schoolSas.url, schoolSas.name, schoolSas.sas, 'school')
-                        let privateBlob = undefined
-                        let targetFolder = 'exam/' + examId + '/paper/'
-                        let count = 0
-                        requestData.papers.forEach(async (item, index) => {
-                            if (item.blob.indexOf('/exam/') == 0) {
-                                if (++count == (requestData.papers.length)) {
-                                    setTimeout(() => {
-                                        this.$Message.success(this.$t('learnActivity.createEv.publishOk'))
-                                        this.isLoading = false
-                                        let route = this.mode + 'Evaluation'
-                                        this.$router.push({
-                                            name: route
-                                        })
-                                    }, 2000)
-                                }
-                                return
-                            }
-                            if (item.scope == 'school') {
-                                schoolBlob.copyFolder(targetFolder + requestData.subjects[index].id + '/', item.blob.substring(1)).then().finally(
-                                    () => {
-                                        if (++count == (requestData.papers.length)) {
-                                            setTimeout(() => {
-                                                this.$Message.success(this.$t('learnActivity.createEv.publishOk'))
-                                                this.isLoading = false
-                                                let route = this.mode + 'Evaluation'
-                                                this.$router.push({
-                                                    name: route
-                                                })
-                                            }, 2000)
-                                        }
-                                    }
-                                )
-                            } else if (item.scope == 'private') {
-                                if (!privateBlob) privateBlob = new BlobTool(privateSas.url, privateSas.name, privateSas.sas, 'private')
-                                schoolBlob.copyFolder(targetFolder + requestData.subjects[index].id + '/', item.blob.substring(1), privateBlob).then().finally(
-                                    () => {
-                                        if (++count == (requestData.papers.length)) {
-                                            setTimeout(() => {
-                                                this.$Message.success(this.$t('learnActivity.createEv.publishOk'))
-                                                this.isLoading = false
-                                                let route = this.mode + 'Evaluation'
-                                                this.$router.push({
-                                                    name: route
-                                                })
-                                            }, 2000)
-                                        }
-                                    }
-                                )
-                            }
-                        })
-                    } else {
-                        this.$Message.error('API ERROR!')
-                        this.isLoading = false
-                    }
-                },
-                err => {
-                    this.isLoading = false
-                    this.$Message.error('API ERROR!')
-                }
-            )
+            //             let schoolSas = {
+            //                 sas: '?' + this.$store.state.user.schoolProfile.blob_sas,
+            //                 url: this.$store.state.user.schoolProfile.blob_uri.slice(0, this.$store.state.user.schoolProfile.blob_uri.lastIndexOf(this.$store.state.userInfo.schoolCode) - 1),
+            //                 name: this.$store.state.userInfo.schoolCode
+            //             }
+            //             let schoolBlob = new BlobTool(schoolSas.url, schoolSas.name, schoolSas.sas, 'school')
+            //             let privateBlob = undefined
+            //             let targetFolder = 'exam/' + examId + '/paper/'
+            //             let count = 0
+            //             requestData.papers.forEach(async (item, index) => {
+            //                 if (item.blob.indexOf('/exam/') == 0) {
+            //                     if (++count == (requestData.papers.length)) {
+            //                         setTimeout(() => {
+            //                             this.$Message.success(this.$t('learnActivity.createEv.publishOk'))
+            //                             this.isLoading = false
+            //                             let route = this.mode + 'Evaluation'
+            //                             this.$router.push({
+            //                                 name: route
+            //                             })
+            //                         }, 2000)
+            //                     }
+            //                     return
+            //                 }
+            //                 if (item.scope == 'school') {
+            //                     schoolBlob.copyFolder(targetFolder + requestData.subjects[index].id + '/', item.blob.substring(1)).then().finally(
+            //                         () => {
+            //                             if (++count == (requestData.papers.length)) {
+            //                                 setTimeout(() => {
+            //                                     this.$Message.success(this.$t('learnActivity.createEv.publishOk'))
+            //                                     this.isLoading = false
+            //                                     let route = this.mode + 'Evaluation'
+            //                                     this.$router.push({
+            //                                         name: route
+            //                                     })
+            //                                 }, 2000)
+            //                             }
+            //                         }
+            //                     )
+            //                 } else if (item.scope == 'private') {
+            //                     if (!privateBlob) privateBlob = new BlobTool(privateSas.url, privateSas.name, privateSas.sas, 'private')
+            //                     schoolBlob.copyFolder(targetFolder + requestData.subjects[index].id + '/', item.blob.substring(1), privateBlob).then().finally(
+            //                         () => {
+            //                             if (++count == (requestData.papers.length)) {
+            //                                 setTimeout(() => {
+            //                                     this.$Message.success(this.$t('learnActivity.createEv.publishOk'))
+            //                                     this.isLoading = false
+            //                                     let route = this.mode + 'Evaluation'
+            //                                     this.$router.push({
+            //                                         name: route
+            //                                     })
+            //                                 }, 2000)
+            //                             }
+            //                         }
+            //                     )
+            //                 }
+            //             })
+            //         } else {
+            //             this.$Message.error('API ERROR!')
+            //             this.isLoading = false
+            //         }
+            //     },
+            //     err => {
+            //         this.isLoading = false
+            //         this.$Message.error('API ERROR!')
+            //     }
+            // )
         },
         //拼接API需要的paper数据
         getPaperInfo() {
@@ -633,6 +702,7 @@ export default {
                         paper.name = data[i].name
                         paper.blob = data[i].blob
                         paper.scope = data[i].scope
+                        paper.sheet = data[i].sheet //答题卡
                         paper.multipleRule = rule[i].multipleRule
                         paper.answers = []
                         paper.point = []
@@ -709,15 +779,30 @@ export default {
             )
             this.activeTab = 'preview'
         }
+        this.findStuList()
     },
     computed: {
         //级联选择年级班级
         csOptions() {
             let data = []
+            //填充行政班数据
             if (this.evaluationInfo.period.id && this.schoolBase.period.length && this.schoolClasses.length) {
                 let curPd = this.schoolBase.period.find((item) => {
                     return item.id == this.evaluationInfo.period.id
                 })
+                data = [
+                    {
+                        id: 'class',
+                        name: '行政班',
+                        children: []
+                    },
+                    {
+                        id: 'stulist',
+                        name: '教学班',
+                        disabled: true,
+                        children: []
+                    }
+                ]
                 if (curPd) {
                     //计算学级逻辑
                     let date = new Date()
@@ -741,9 +826,12 @@ export default {
                             return (classItem.year == curYear - index) && classItem.periodId == this.evaluationInfo.period.id
                         })
                         dataItem.children = child
-                        data.push(dataItem)
+                        data[0].children.push(dataItem)
                     })
                 }
+                // 填充教学班数据
+                data[1].children.push(...this.stuList)
+                console.log('data数据', data)
             }
             return data
         },
@@ -794,20 +882,22 @@ export default {
 <style scoped lang="less">
 @import "./CreateSchoolEva.less";
 </style>
-<style scoped>
+<style>
 .evaluation-attr-wrap .ivu-form .ivu-form-item-label {
     color: #a5a5a5;
 }
 
 .evaluation-question-main
     .ivu-tabs.ivu-tabs-card
-    /deep/
     .ivu-tabs-bar
     .ivu-tabs-tab-active {
-    background-color: #404040;
+    background-color: #404040 !important;
     font-weight: 600;
 }
-
+.teacher-preview-container .paper-main-wrap {
+    padding: 0px;
+    margin-top: 0px;
+}
 .evaluation-question-main .ivu-tabs .ivu-tabs-content-animated {
     height: 100%;
     margin-bottom: 10px;

+ 18 - 16
TEAMModelOS/ClientApp/src/view/learnactivity/MgtPrivEva.vue

@@ -104,7 +104,7 @@ export default {
     inject: ['reload'],
     data() {
         return {
-            curEvValue:0,
+            curEvValue: 0,
             split1: 0.2,
             scope: '',//school 校本 private 个人
             showBack: false,
@@ -119,14 +119,14 @@ export default {
             schoolBase: {
                 period: []
             },
-            evFilter:[
+            evFilter: [
                 {
-                    label:this.$t('learnActivity.mgtScEv.myEv'),
-                    value:0
+                    label: this.$t('learnActivity.mgtScEv.myEv'),
+                    value: 0
                 },
                 {
-                    label:this.$t('learnActivity.mgtScEv.teaEv'),
-                    value:1
+                    label: this.$t('learnActivity.mgtScEv.teaEv'),
+                    value: 1
                 }
             ],
             scoreLoading: false,
@@ -301,15 +301,15 @@ export default {
         },
         goToCreate() {
             // if (this.$store.state.user.schoolProfile.school_base) { //这里判断必须要加入学校才能创建评测
-                if (this.scope == 'school') {
-                    this.$router.push({
-                        name: 'createSchoolEva'
-                    })
-                } else if (this.scope == 'private') {
-                    this.$router.push({
-                        name: 'createPrivEva'
-                    })
-                }
+            if (this.scope == 'school') {
+                this.$router.push({
+                    name: 'createSchoolEva'
+                })
+            } else if (this.scope == 'private') {
+                this.$router.push({
+                    name: 'createPrivEva'
+                })
+            }
             // } else {
             //     this.$Message.warning(this.$t('learnActivity.mgtScEv.noJoin'))
             // }
@@ -389,6 +389,8 @@ export default {
                                 resData.papers[index] = await this.$evTools.getFullPaper(resData.papers[index])
                                 resData.papers[index].blob = blob
                                 resData.score += resData.papers[index].score
+                                resData.papers[index].examScope = this.evaListShow[this.curEvaIndex].scope
+                                resData.papers[index].examCode = this.evaListShow[this.curEvaIndex].code
                             }
                         }
                         this.evaListShow.splice(this.curEvaIndex, 1, resData)
@@ -437,7 +439,7 @@ export default {
         curEvLabel: function () {
             let value = this.curEvValue
             console.log(this.curEvValue)
-            let curObj = this.evFilter.find(item=>{
+            let curObj = this.evFilter.find(item => {
                 return item.value == value
             })
             return curObj.label

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

@@ -527,6 +527,8 @@ export default {
                             }
                             resData.papers[index].blob = blob
                             resData.score += resData.papers[index].score
+                            resData.papers[index].examScope = this.evaListShow[this.curEvaIndex].scope
+                            resData.papers[index].examCode = this.evaListShow[this.curEvaIndex].code
                         }
                         this.evaListShow.splice(this.curEvaIndex, 1, resData)
                         this.examDetaiInfo = resData

+ 15 - 13
TEAMModelOS/ClientApp/src/view/newcourse/NewCusMgt.vue

@@ -385,18 +385,7 @@ export default {
                     slot: 'no',
                     align: 'center',
                     sortable: true
-                },
-                // {
-                //     title: this.$t('courseManage.classroom.studentTableC5'),
-                //     slot: 'groupId',
-                //     align: 'center',
-                //     sortable: true
-                // },
-                // {
-                //     title: this.$t('courseManage.classroom.studentTableC6'),
-                //     slot: 'groupName',
-                //     align: 'center'
-                // }
+                }
             ],
             listColumn: [
                 {
@@ -803,7 +792,7 @@ export default {
         },
 
         //确认添加名单
-        confirmAddSchd() {
+        async confirmAddSchd() {
             if (!this.schedule.teacherId) {
                 return
             }
@@ -816,6 +805,15 @@ export default {
                         emptyIndex = index
                     }
                 })
+                //获取学生名单
+                let params = {
+                    'school_code': this.$store.state.userInfo.schoolCode,
+                    'ids': this.sltClass.map(item => {
+                        return item.id
+                    }),
+                    'scope': 'school'
+                }
+                let stusInfo = await this.$api.schoolSetting.getClassroomStudent(params)
                 this.sltClass.forEach(item => {
                     let exist = this.courseListShow[this.curCusIndex].schedule.find(schdItem => {
                         return schdItem.classId == item.id && schdItem.teacherId == this.schedule.teacherId
@@ -826,6 +824,10 @@ export default {
                             id: item.id,
                             name: item.name
                         }
+                        //添加学生信息
+                        this.schedule.students = stusInfo.stus.find(sItem=>{
+                            return sItem[0].classId == item.id
+                        })
                         if (emptyIndex > -1) {
                             this.courseListShow[this.curCusIndex].schedule.splice(emptyIndex, 1, JSON.parse(JSON.stringify(this.schedule))) //直接删除会改变顺序,产生bug错觉
                             emptyIndex = -1

+ 4 - 53
TEAMModelOS/ClientApp/src/view/schoolmgmt/ClassroomSetting/ClassroomSetting.vue

@@ -462,9 +462,9 @@ export default {
     },
     methods: {
         //编辑课表
-        editCusTable(){
+        editCusTable() {
             this.$router.push({
-                path:'/home/CoursePlan'
+                path: '/home/CoursePlan'
             })
         },
         //根据班级查询课程安排
@@ -889,55 +889,6 @@ export default {
         },
         delClassroom(index) {
             this.isListLoading = true
-            // if (this.roomListShow[index].option !== 'insert') {
-            //     this.$api.schoolSetting.delClassroom({
-            //         id: this.roomListShow[index].id,
-            //         scope: this.roomListShow[index].scope,
-            //         school_code: this.$store.state.userInfo.schoolCode
-            //     }).then(
-            //         (res) => {
-            //             if (res.error == null) {
-            //                 if (this.curClassIndex >= index && index > 0) {
-            //                     this.curClassIndex = 0
-            //                     this.updateBefore = JSON.stringify(this.roomListShow[this.curClassIndex])
-            //                 }
-            //                 let originIndex = -1
-            //                 for (let i in this.roomList) {
-            //                     if (this.roomList[i].id == this.roomListShow[index].id) {
-            //                         originIndex = i
-            //                         break
-            //                     }
-            //                 }
-            //                 this.$api.schoolSetting.hiteachUnlinkByClassId({
-            //                     classId: this.roomListShow[index].id,
-            //                     school_code: this.$store.state.userInfo.schoolCode
-            //                 })
-            //                 this.unlinkHiteachByClassId(this.roomListShow[index].id) // 刪掉指定的classId                                
-            //                 this.$store.dispatch('user/delSchoolClasses', this.roomListShow[index].id);
-            //                 this.roomList.splice(originIndex, 1)
-            //                 this.roomListShow.splice(index, 1)
-            //                 this.$Message.success(this.$t('schoolBaseInfo.csTips7'))
-            //                 this.updated = false
-            //                 this.updHiteachLink = []
-            //             }
-            //         },
-            //         (err) => {
-            //             this.$Message.error('API error!')
-            //         }
-            //     ).finally(() => {
-            //         this.isListLoading = false
-            //     })
-            // } else {
-            //     if (this.curClassIndex >= index && index > 0) {
-            //         this.curClassIndex = 0
-            //         this.updateBefore = JSON.stringify(this.roomListShow[this.curClassIndex])
-            //     }
-            //     this.roomListShow.splice(index, 1)
-            //     this.unlinkHiteachByClassId(this.roomListShow[index].id)// 刪掉指定的classId
-            //     this.isListLoading = false
-            //     this.updated = false
-            //     this.updHiteachLink = []
-            // }
             let requestData = {
                 id: this.roomListShow[index].id,
                 code: this.$store.state.userInfo.schoolCode
@@ -953,9 +904,9 @@ export default {
                             break
                         }
                     }
+                    this.$store.dispatch('user/delSchoolRoom', this.roomListShow[index].id)
                     this.roomList.splice(originIndex, 1)
                     this.roomListShow.splice(index, 1)
-                    this.$store.dispatch('user/delSchoolRoom', this.roomListShow[index].id);
                 },
                 err => {
                     this.$Message.error('API error!')
@@ -968,7 +919,7 @@ export default {
             this.$Message.success(this.$t('schoolBaseInfo.csTips5'))
         },
         chooseClassroom(index) {
-            if (index != this.curClassIndex) {
+            if (index != this.curClassIndex) { 
                 if (this.updated) {
                     let config = {
                         title: this.$t('schoolBaseInfo.saveWarning'),

+ 0 - 1
TEAMModelOS/ClientApp/src/view/student-account/AddStudent.vue

@@ -66,7 +66,6 @@
 }
 </style>
 <template>
-
     <div class="account-form dark-iview-form dark-iview-select">
         <!--新增学生表单-->
         <Form ref="studentInfoForm" :model="studentInfo" label-position="top" :rules="ruleValidate" :show-message="showError" v-if="bizType == 1">

+ 14 - 7
TEAMModelOS/ClientApp/src/view/student-account/ClassMgt.vue

@@ -483,16 +483,22 @@ export default {
                             res => {
                                 this.$Message.success(this.$t('schoolBaseInfo.removeOk'))
                                 if (index > -1) {
-                                    this.students.splice(index, 1)
+                                    let stuInfo = this.classListShow[this.curClassIndex].students[index]
+                                    stuInfo.classId = null
+                                    this.$store.commit('schoolBaseInfo/uptStudents',[stuInfo])
+                                    this.classListShow[this.curClassIndex].students.splice(index, 1)
+                                    
                                 } else {
-                                    for (let i = 0; i < this.students.length; i) {
-                                        if (requestParams.students.indexOf(this.students[i].id) > -1) {
-                                            this.students.splice(i, 1)
+                                    this.delSelections.forEach(item=>{
+                                        item.classId = null
+                                    })
+                                    this.$store.commit('schoolBaseInfo/uptStudents',this.delSelections)
+                                    for (let i = 0; i < this.classListShow[this.curClassIndex].students.length; i++) {
+                                        if (requestParams.students.includes(this.students[i].id)) {
+                                            this.classListShow[this.curClassIndex].students.splice(i, 1)
                                             i--
                                         }
-
                                     }
-                                    this.students.forEach()
                                 }
                             },
                             err => {
@@ -511,6 +517,7 @@ export default {
                     item.classId = this.classListShow[this.curClassIndex].id
                     item.className = this.classListShow[this.curClassIndex].name
                     item.classNo = this.classListShow[this.curClassIndex].no
+                    item.classYear = this.classListShow[this.curClassIndex].year
                 })
 
                 this.listLoading = true
@@ -521,11 +528,11 @@ export default {
                 }).then(
                     (res) => {
                         if (!res.error) {
+                            this.$store.commit('schoolBaseInfo/uptStudents',data)
                             this.selections.length = 0
                             let s = this.classListShow[this.curClassIndex].students || []
                             s.push(...data)
                             this.$set(this.classListShow[this.curClassIndex], 'students', s)
-                            // this.students.push(...data)
                             this.$Message.success(this.$t('schoolBaseInfo.addOk'))
                         } else {
                             this.$Message.error(this.$t('schoolBaseInfo.addErr'))

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

@@ -198,7 +198,7 @@ export default {
         }
     },
     methods: {
-        createClass(){
+        createClass() {
             this.addStudentStatus = false
             this.tab = 'class'
         },
@@ -331,8 +331,8 @@ export default {
         },
         closeImportStudent(data) {
             // 給一個 PW 讓AddStudent.vue 的Watch 可以動作
-            if(!data) return
-            let newStudents = data.map((item) =>{
+            if (!data) return
+            let newStudents = data.map((item) => {
                 let temp = item
                 temp.pw = '******'
                 temp.year = parseInt(temp.year)
@@ -685,8 +685,8 @@ export default {
             // 优先帳號資訊搜尋
             if (this.searchText) {
                 let text = this.searchText
-                data = data.filter(function (item) {
-                    return item.name.indexOf(text) >= 0 || item.id.indexOf(text) >= 0
+                data = data.filter((item)=> {
+                    return (item.name.indexOf(text) >= 0 || item.id.indexOf(text) >= 0) && item.periodId == this.searchPeriod
                 })
                 this.tableFilterData = data
                 this.pointNum = this.basicCount

+ 2 - 2
TEAMModelOS/ClientApp/src/view/task/arb/Arbitration.vue

@@ -15,7 +15,7 @@
             </Tooltip>
         </div>
         <div class="arb-content">
-            <vuescroll style="display:none">
+            <vuescroll>
                 <div class="arb-paper-item" v-for="(item,index) in arr" :key="index">
                     <p class="qu-info-wrap">
                         <span class="qu-info-label">题目:</span>
@@ -41,7 +41,7 @@
                 </div>
             </vuescroll>
             <!-- <div class="not-started-box"></div> -->
-            <EmptyData textContent="阅卷老师正在阅卷中,请等阅卷结束后再处理仲裁卷。"></EmptyData>
+            <!-- <EmptyData textContent="阅卷老师正在阅卷中,请等阅卷结束后再处理仲裁卷。"></EmptyData> -->
         </div>
     </div>
 </template>

+ 3 - 3
TEAMModelOS/ClientApp/src/view/task/err/ErrPaper.vue

@@ -51,7 +51,7 @@
                 <p>
                     <span class="err-info-label">反馈建议:</span>
                 </p>
-                <Input v-model="advice" maxlength="50" show-word-limit type="textarea" placeholder="Enter something..." style="width: 800px" />
+                <Input v-model="advice" maxlength="50" show-word-limit type="textarea" placeholder="反馈建议..." style="width: 800px" />
             </div>
 
         </Modal>
@@ -147,8 +147,8 @@ export default {
     height: ~"calc(100% - 45px)";
 }
 .arb-paper-item {
-    width: ~"calc(100% - 20px)";
-    margin: 6px 5px;
+    width: ~"calc(100% - 15px)";
+    margin: 6px 0px;
     border: 1px solid #363636;
     background: #2b2a2f;
     padding: 15px;

+ 4 - 0
TEAMModelOS/ClientApp/src/view/task/index.less

@@ -202,4 +202,8 @@
 .data-content{
     display: flex;
     justify-content: space-around;
+}
+.objective-tips {
+    font-size: 12px;
+    color: #ff9900;
 }

+ 3 - 0
TEAMModelOS/ClientApp/src/view/task/index.vue

@@ -51,6 +51,9 @@
                         <div class="setting-block" v-show="curBarIndex == 0" style="margin-top:15px">
                             <p class="block-title">
                                 {{$t('task.markProg')}}
+                                <span class="objective-tips">
+                                    (温馨提示:客观题已由系统自动评分,下面只显示了需要人工评分的主观题题号。)
+                                </span>
                                 <Icon type="ios-arrow-down" :class="['show-full-icon', fullQuProg ? 'full-qu-prog' : '']" @click="fullQuProg = !fullQuProg" />
                             </p>
                             <div class="setting-content" :style="{height: fullQuProg ? 'fit-content' : '420px'}">

+ 6 - 4
TEAMModelOS/ClientApp/src/view/task/mark/ByStu.vue

@@ -191,10 +191,12 @@ export default {
         exception() {
             if (this.errReason) {
                 this.errStatus = false
-                let requstData = {
-                    id: '',
-                    err: '',
-                    code: ''
+                let requestData = {
+                    id: this.stuData.id,
+                    err: this.errText,
+                    code: this.$store.state.userInfo.schoolCode,
+                    index: parseInt(this.quIndex),
+                    tId: this.$store.state.userInfo.TEAMModelId
                 }
                 this.$api.mark.saveErr(requestData).then(
                     res => {

+ 34 - 22
TEAMModelOS/Controllers/Client/HiScanController.cs

@@ -79,33 +79,36 @@ namespace TEAMModelOS.Controllers.Core
         {
             try {
               //  if (!request.TryGetProperty("sid", out JsonElement sid)) return BadRequest();
-                if (!request.TryGetProperty("id_token", out JsonElement id_token)) return BadRequest();
+               // if (!request.TryGetProperty("id", out JsonElement id_token)) return BadRequest();
                 
                 //IServerSentEventsClient sseClient;
                 //if (Guid.TryParse($"{sid}", out Guid guid) && (sseClient = _sse.GetClient(guid)) != null) {
                     //var clientName = sseClient.GetProperty<string>("NAME");
                     //var clientDID= sseClient.GetProperty<string>("DID");
                    // var isHiTeach = clientName.Contains("HiScan", StringComparison.OrdinalIgnoreCase);
-                    var jwt = new JwtSecurityToken(id_token.GetString());
+                    //var jwt = new JwtSecurityToken(id_token.GetString());
                     //TODO 此驗證IdToken先簡單檢查,後面需向Core ID新API,驗證Token
-                    if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
-                    var id = jwt.Payload.Sub;
-                    jwt.Payload.TryGetValue("name", out object name);
-                    jwt.Payload.TryGetValue("picture", out object picture);
+                   // if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
+                   // var id = jwt.Payload.Sub;
+                    //jwt.Payload.TryGetValue("name", out object name);
+                    //jwt.Payload.TryGetValue("picture", out object picture);
 
                     List<ScanSchool> schools = new List<ScanSchool>();
-                   
-                    //TODO 取得Teacher 個人相關數據(課程清單、虛擬教室清單、歷史紀錄清單等),學校數據另外API處理,多校切換時不同
-                    var client = _azureCosmos.GetCosmosClient();
-                    var response = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemStreamAsync(id, new PartitionKey("Base"));
+                if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
+                //TODO 取得Teacher 個人相關數據(課程清單、虛擬教室清單、歷史紀錄清單等),學校數據另外API處理,多校切換時不同
+                var client = _azureCosmos.GetCosmosClient();
+                    var response = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemStreamAsync($"{id}", new PartitionKey("Base"));
                     int size = 0;
                     //老師個人資料(含初始化)
                     if (response.Status == 200)
                     {
                         var json = await JsonDocument.ParseAsync(response.ContentStream);
-                        if (json.RootElement.TryGetProperty("schools", out JsonElement value))
+                    string name = $"{json.RootElement.GetProperty("name")}";
+                    string picture = $"{json.RootElement.GetProperty("picture")}";
+                    if (json.RootElement.TryGetProperty("schools", out JsonElement value))
                         {
-                            if (json.RootElement.TryGetProperty("size", out JsonElement _size) && _size.ValueKind.Equals(JsonValueKind.Number))
+                           
+                        if (json.RootElement.TryGetProperty("size", out JsonElement _size) && _size.ValueKind.Equals(JsonValueKind.Number))
                             {
                                 size = _size.GetInt32();
                             }
@@ -130,7 +133,7 @@ namespace TEAMModelOS.Controllers.Core
                                     }
                                     if (count > 0) {
                                         //生成token
-                                        var  stoken = JwtAuthExtension.CreateAuthToken(_option.HostName, id, name?.ToString(), picture?.ToString(), _option.JwtSecretKey, roles: new[] { "teacher" }, schoolID: $"{schoolId}");
+                                        var  stoken = JwtAuthExtension.CreateAuthToken(_option.HostName, $"{id}", name?.ToString(), picture?.ToString(), _option.JwtSecretKey, roles: new[] { "teacher" }, schoolID: $"{schoolId}");
                                         //获取学校线下阅卷评测
                                         var sexams= await GetExam($"{schoolId}", "school", client);
                                         var container = _azureStorage.GetBlobContainerClient($"{schoolId}");
@@ -141,8 +144,8 @@ namespace TEAMModelOS.Controllers.Core
                                 }
                             }
                         }
-                        var ttoken = JwtAuthExtension.CreateAuthToken(_option.HostName, id, name?.ToString(), picture?.ToString(), _option.JwtSecretKey, roles: new[] { "teacher" });
-                        var (tblob_uri, tblob_sas) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete);
+                        var ttoken = JwtAuthExtension.CreateAuthToken(_option.HostName, $"{id}", name?.ToString(), picture?.ToString(), _option.JwtSecretKey, roles: new[] { "teacher" });
+                        var (tblob_uri, tblob_sas) = _azureStorage.GetBlobContainerSAS($"{id}", BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete);
                         //获取个人线下阅卷评测
                         var exams = await GetExam($"{id}", "private", client);
                         //換取AuthToken,提供給前端
@@ -170,15 +173,24 @@ namespace TEAMModelOS.Controllers.Core
                 //await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<Correct>(queryText: "SELECT  * FROM c where c.source='2' and c.progress='going' order by c.createTime  ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Correct-{code}") }))
                 await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<Correct>(queryText: "SELECT  * FROM c   order by c.createTime  ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Correct-{code}") }))
                 {
-                    ExamInfo exam = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<ExamInfo>(item.id, new PartitionKey($"Exam-{code}"));
-
+                  
+                    ExamInfo exam =null;
+                    try {
+                        exam= await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<ExamInfo>(item.id, new PartitionKey($"Exam-{code}"));
+                    } catch (CosmosException ex) {
+                        if (ex.Status == 404) {
+                            exam = null;
+                        }
+                    }
                     List<ExamClass> classes = new List<ExamClass>();
-                    foreach (var cls in exam.classes) {
-                        (List<string> tmdids, List<Students> students) stulist = await TriggerStuActivity.GetStuList(client, _dingDing, new List<string> { cls }, code);
-                        classes.Add(new ExamClass { classId = cls, tmdids = stulist.tmdids, stulist = stulist.students });
+                    if (exam != null) {
+                        foreach (var cls in exam.classes)
+                        {
+                            (List<string> tmdids, List<Students> students) stulist = await TriggerStuActivity.GetStuList(client, _dingDing, new List<string> { cls }, code);
+                            classes.Add(new ExamClass { classId = cls, tmdids = stulist.tmdids, stulist = stulist.students });
+                        }
+                        corrects.Add(new ExamData { exam = exam, correct = item, classes = classes });
                     }
-                    corrects.Add(new ExamData { exam = exam, correct = item, classes= classes });
-
                 }
             }
             else if(scope.Equals("private")){

+ 19 - 5
TEAMModelOS/Controllers/Common/ExamController.cs

@@ -1566,11 +1566,13 @@ namespace TEAMModelOS.Controllers
                 {
                     List<Qs> qs = ss.qs.Where(s => !string.IsNullOrEmpty(s.err)).ToList();
                     if (qs.Count > 0) {
+                        ss.qs = qs;
                         errs.Add(ss);
                     }
                     List<Item> arb = ss.items.Where(s => s.flag == false).ToList();
                     if (arb.Count > 0)
                     {
+                        ss.items = arb;
                         arbs.Add(ss);
                     }
                     //var obj = new { stuId = ss.stuId, examId = ss.examId, subjectId = ss.subjectId, };
@@ -1594,7 +1596,7 @@ namespace TEAMModelOS.Controllers
                         //sc.Add(item.scores.Where(x => x.tmdId.Equals(tId.GetString())).Select(c => c.sc).FirstOrDefault());
                         //item.scores = item.scores.Where(x => x.tmdId.Equals(tId.GetString())).ToList();
                     }
-                    var obj = new { ss.stuId, ss.examId, ss.subjectId, item = sc, ss.blob, ss.tIds, ss.marks, ss.scores, ss.model, ss.mode };
+                    var obj = new { ss.id,ss.stuId, ss.examId, ss.subjectId, item = sc,ss.qs, ss.blob, ss.tIds, ss.marks, ss.scores, ss.model, ss.mode };
                     objs.Add(obj);
                 }
                 return Ok(new { errs, arbs, objs, paper = info.papers[index].blob });
@@ -1739,7 +1741,7 @@ namespace TEAMModelOS.Controllers
                             //sc.Add(item.scores.Where(x => x.tmdId.Equals(tId.GetString())).Select(c => c.sc).FirstOrDefault());
                             //item.scores = item.scores.Where(x => x.tmdId.Equals(tId.GetString())).ToList();
                         }
-                        var obj = new { ss.stuId, ss.examId, ss.subjectId, item = sc, ss.blob, ss.tIds, ss.marks, ss.scores, ss.model, ss.mode };
+                        var obj = new { ss.id, ss.stuId, ss.examId, ss.subjectId, item = sc, ss.qs, ss.blob, ss.tIds, ss.marks, ss.scores, ss.model, ss.mode };
                         objs.Add(obj);
                     }
                     return Ok(objs);
@@ -1819,7 +1821,7 @@ namespace TEAMModelOS.Controllers
                             //sc.Add(item.scores.Where(x => x.tmdId.Equals(tId.GetString())).Select(c => c.sc).FirstOrDefault());
                             //item.scores = item.scores.Where(x => x.tmdId.Equals(tId.GetString())).ToList();
                         }                       
-                        var obj = new { item.stuId, item.examId, item.subjectId, item = sc, item.blob, item.tIds, item.marks, item.scores, item.model, item.mode };
+                        var obj = new { item.id, item.stuId, item.examId, item.subjectId, item = sc, item.qs, item.blob, item.tIds, item.marks, item.scores, item.model, item.mode };
                         return Ok(obj);
                     }
 
@@ -2119,18 +2121,30 @@ namespace TEAMModelOS.Controllers
             if (!requert.TryGetProperty("id", out JsonElement id)) return BadRequest();
             if (!requert.TryGetProperty("err", out JsonElement err)) return BadRequest();
             if (!requert.TryGetProperty("code", out JsonElement code)) return BadRequest();
+            if (!requert.TryGetProperty("index", out JsonElement index)) return BadRequest();
+            if (!requert.TryGetProperty("tId", out JsonElement tId)) return BadRequest();
             var client = _azureCosmos.GetCosmosClient();
             SDK.Models.Cosmos.Common.Scoring scoring = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<SDK.Models.Cosmos.Common.Scoring>(id.GetString(), new PartitionKey($"Scoring-{code}"));
             if (null != scoring)
             {
+                if (string.IsNullOrEmpty(scoring.qs[index.GetInt32()].tId))
+                {
+                    scoring.qs[index.GetInt32()].err = err.GetString();
+                    scoring.qs[index.GetInt32()].tId = tId.GetString();
+                    scoring.qs[index.GetInt32()].index = index.GetInt32();
+                }
+                else {
+                    return Ok(new { msg = "改题已经被申报" });
+                }
+               
                /* scoring.type = 2;
                 scoring.err = err.GetString();*/
             }
             else {
                 return Ok(new { code = 404 });
             }
-
-            return Ok( new { code = 200 });
+            var sc = await client.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync(scoring, scoring.id, new PartitionKey($"{scoring.code}"));
+            return Ok( new { sc });
         }
 
         //阅卷信息统计

+ 275 - 0
TEAMModelOS/Controllers/Common/SheetConfigController.cs

@@ -0,0 +1,275 @@
+using Azure.Cosmos;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using System;
+using System.Collections.Generic;
+using System.IdentityModel.Tokens.Jwt;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelOS.Models.Dto;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK;
+using TEAMModelOS.SDK.Context.Constant.Common;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.DI.AzureCosmos.Inner;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK.Helper.Common.CollectionHelper;
+using TEAMModelOS.SDK.Helper.Common.StringHelper;
+using TEAMModelOS.Models;
+using Microsoft.Extensions.Options;
+using TEAMModelOS.SDK.Models.Cosmos;
+using Microsoft.AspNetCore.Authorization;
+using TEAMModelOS.Filter;
+using StackExchange.Redis;
+using TEAMModelOS.SDK.Models.Cosmos.Common.Inner;
+using TEAMModelOS.Services.Common;
+using TEAMModelOS.SDK.Models.Cosmos.Common;
+using Azure;
+
+namespace TEAMModelOS.Controllers.Common
+{
+    [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status400BadRequest)]
+    //[Authorize(Roles = "IES5")]
+    [Route("common/sheet-config")]
+    [ApiController]
+    public class SheetConfigController : ControllerBase
+    {
+        private readonly AzureRedisFactory _azureRedis;
+        private readonly AzureCosmosFactory _azureCosmos;
+        private readonly SnowflakeId _snowflakeId;
+        private readonly AzureServiceBusFactory _serviceBus;
+        private readonly DingDing _dingDing;
+        private readonly Option _option;
+        private readonly AzureStorageFactory _azureStorage;
+        public SheetConfigController(AzureCosmosFactory azureCosmos, AzureServiceBusFactory serviceBus, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option,
+            AzureRedisFactory azureRedis, AzureStorageFactory azureStorage)
+        {
+            _azureCosmos = azureCosmos;
+            _serviceBus = serviceBus;
+            _snowflakeId = snowflakeId;
+            _dingDing = dingDing;
+            _option = option?.Value;
+            _azureRedis = azureRedis;
+            _azureStorage = azureStorage;
+        }
+
+        /// <summary>
+        ///查询答题卡
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("copy")]
+        public async Task<IActionResult> Copy(JsonElement request)
+        {
+            try
+            {
+                var client = _azureCosmos.GetCosmosClient();
+                if (!request.TryGetProperty("ids", out JsonElement ids)) return BadRequest();
+                if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
+                if (!request.TryGetProperty("scope", out JsonElement scope)) return BadRequest();
+                List<string> inids = new List<string>();
+                ids.ToObject<List<string>>().ForEach(x => { inids.Add($"'{x}'"); });
+                var insql = string.Join(",", inids);
+                var queryslt = $"SELECT  value(c) FROM c where c.id in ({insql})";
+                List<SheetConfig> sheets = new List<SheetConfig>();
+                List<dynamic> datas = new List<dynamic>();
+                string table = "School";
+                if ($"{scope}" != "school")
+                {
+                    table = "Teacher";
+                }
+                await foreach (var item in client.GetContainer("TEAMModelOS", table).GetItemQueryIterator<SheetConfig>(queryText: queryslt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"SheetConfig-{code}") }))
+                {
+                    string sid = item.id;
+                    string id = $"{_option.LocationNum}{Utils.CreatSaltString(7, "0123456789")}";
+                    for (int i = 0; i < 10; i++)
+                    {
+                        Response response = null;
+                        response = await client.GetContainer("TEAMModelOS", table).ReadItemStreamAsync($"{id}", new PartitionKey($"SheetConfig-{code}"));
+                        if (response.Status == 404)
+                        {
+                            break;
+                        }
+                        else
+                        {
+                            if (i == 9)
+                            {
+                                await _dingDing.SendBotMsg($"OS,{_option.Location},common/SheetConfig/upsert()\n id生成异常,重复生成次数超过10次", GroupNames.醍摩豆服務運維群組);
+                                return BadRequest();
+                            }
+                            else
+                            {
+                                id = $"{_option.LocationNum}{Utils.CreatSaltString(6, "0123456789")}";
+                            }
+                        }
+                    }
+                    item.id = id;
+                    SheetConfig config = await client.GetContainer("TEAMModelOS", table).CreateItemAsync<SheetConfig>(item, new PartitionKey($"SheetConfig-{code}"));
+                    datas.Add(new { sid = sid, newitem = item });
+                }
+                return Ok(new { datas = datas, status = 200 });
+            }
+            catch (CosmosException e)
+            {
+                return Ok(new { status = e.Response.Status });
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"OS,{_option.Location},common/SheetConfig/upsert()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
+                return BadRequest(ex.StackTrace);
+            }
+        }
+        /// <summary>
+        ///查询答题卡
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("find")]
+        public async Task<IActionResult> Find(JsonElement request) {
+            try
+            {
+                var client = _azureCosmos.GetCosmosClient();
+                if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
+                if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
+                if (!request.TryGetProperty("scope", out JsonElement scope)) return BadRequest();
+                if ($"{scope}" == "school")
+                {
+                    SheetConfig config = await client.GetContainer("TEAMModelOS", "School").ReadItemAsync<SheetConfig>($"{id}", new PartitionKey($"SheetConfig-{code}"));
+                    return Ok(new { config = config, status = 200 });
+                }
+                else
+                {
+                    SheetConfig config = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<SheetConfig>($"{id}", new PartitionKey($"SheetConfig-{code}"));
+                    return Ok(new { config = config,status=200 });
+                }
+            }
+            catch (CosmosException e)
+            {
+                return Ok(new { status = e.Response.Status });
+            }
+            catch (Exception ex) {
+
+                await _dingDing.SendBotMsg($"OS,{_option.Location},common/SheetConfig/upsert()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
+                return BadRequest(ex.StackTrace);
+            }
+        }
+        /// <summary>
+        /// 新增 或 修改答题卡
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("upsert")]
+        public async Task<IActionResult> Upsert(SheetConfig request)
+        {
+            try
+            {
+                
+                var client = _azureCosmos.GetCosmosClient();
+                request.pk = $"SheetConfig";
+                request.code = $"SheetConfig-{request.code}";
+                request.ttl = -1;
+                if (string.IsNullOrEmpty(request.id))
+                {
+                    string id =  $"{_option.LocationNum}{Utils.CreatSaltString(7, "0123456789")}"; 
+                    for (int i = 0; i < 10; i++)
+                    {
+                        Response response = null;
+                        if (request.scope == "school")
+                        {
+                              response = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync($"{id}", new PartitionKey($"{request.code}"));
+                        }
+                        else {
+                              response = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemStreamAsync($"{id}", new PartitionKey($"{request.code}"));
+                        }
+                        if (response.Status == 404)
+                        {
+                            break;
+                        }
+                        else {
+                            if (i == 9)
+                            {
+                                await _dingDing.SendBotMsg($"OS,{_option.Location},common/SheetConfig/upsert()\n id生成异常,重复生成次数超过10次", GroupNames.醍摩豆服務運維群組);
+                                return BadRequest();
+                            }
+                            else {
+                                id = $"{_option.LocationNum}{Utils.CreatSaltString(7, "0123456789")}";
+                            }
+                        }
+                         
+                    }
+                    request.id = $"{id}";
+                  
+                    if (request.scope == "school")
+                    {
+                        request = await client.GetContainer("TEAMModelOS", "School").CreateItemAsync(request, new PartitionKey($"{request.code}"));
+                    }
+                    else {
+                        request = await client.GetContainer("TEAMModelOS", "Teacher").CreateItemAsync(request, new PartitionKey($"{request.code}"));
+                    }
+                }
+                else
+                {
+                    if (request.scope == "school")
+                    {
+                        request = await client.GetContainer("TEAMModelOS", "School").ReplaceItemAsync(request, request.id, new PartitionKey($"{request.code}"));
+                    }
+                    else
+                    {
+                        request = await client.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync(request, request.id, new PartitionKey($"{request.code}"));
+                    }
+                 }
+                return Ok(new { config = request, status=200 });
+            }
+            catch (CosmosException ex)
+            {
+                return Ok(new { status = ex.Status });
+            }
+            catch (Exception e)
+            {
+                await _dingDing.SendBotMsg($"OS,{_option.Location},common/SheetConfig/upsert()\n{e.Message}", GroupNames.醍摩豆服務運維群組);
+                return BadRequest(e.StackTrace);
+            }
+        }
+        /// <summary>
+        ///删除答题卡
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("delete")]
+        public async Task<IActionResult> Delete(JsonElement request)
+        {
+            try
+            {
+                var client = _azureCosmos.GetCosmosClient();
+                if (!request.TryGetProperty("ids", out JsonElement ids)) return BadRequest();
+                if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
+                if (!request.TryGetProperty("scope", out JsonElement scope)) return BadRequest();
+                List<string> idslist = ids.ToObject<List<string>>();
+                if ($"{scope}" == "school")
+                {
+                    await client.GetContainer("TEAMModelOS", "School").DeleteItemsStreamAsync(idslist, $"SheetConfig-{code}");
+                }
+                else
+                {
+                    await client.GetContainer("TEAMModelOS", "Teacher").DeleteItemsStreamAsync(idslist, $"SheetConfig-{code}");
+                }
+                return Ok(new { status = 404 });
+            }
+            catch (CosmosException ex) {
+                return Ok(new { status = ex.Status });
+            }
+            catch (Exception e)
+            {
+                await _dingDing.SendBotMsg($"OS,{_option.Location},common/SheetConfig/delete()\n{e.Message}", GroupNames.醍摩豆服務運維群組);
+                return BadRequest(e.StackTrace);
+            }
+        }
+    }
+}

+ 46 - 2
TEAMModelOS/Controllers/Pager/PaperController.cs

@@ -93,7 +93,7 @@ namespace TEAMModelOS.Controllers
             var id = jwt.Payload.Sub;
             var client = _azureCosmos.GetCosmosClient();
             List<object> papers = new List<object>();
-            var query = $"select c.id,c.subjectCode,c.code,c.periodCode,c.name,c.itemCount,c.level,c.pointItem,c.pointScore,c.score,c.gradeCode,c.createTime from c where c.id = {id}";
+            var query = $"select c.id,c.subjectCode,c.code,c.periodCode,c.name,c.itemCount,c.level,c.pointItem,c.pointScore,c.score,c.gradeCode,c.createTime,c.sheet from c where c.id = {id}";
             await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{school_code}") }))
             {
                 using var json = await JsonDocument.ParseAsync(item.ContentStream);
@@ -154,7 +154,7 @@ namespace TEAMModelOS.Controllers
             
             if (scope.ToString().Equals("school"))
             {
-                sql.Append("select c.id,c.code,c.name,c.blob,c.periodId,c.gradeIds,c.subjectId,c.subjectName,c.score,c.useCount,c.scope,c.scoring,c.createTime from c");
+                sql.Append("select c.id,c.code,c.name,c.blob,c.periodId,c.gradeIds,c.subjectId,c.subjectName,c.score,c.useCount,c.scope,c.scoring,c.createTime,c.sheet from c");
                 AzureCosmosQuery cosmosDbQuery = SQLHelper.GetSQL(dict, sql);
                 await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryDefinition: cosmosDbQuery.CosmosQueryDefinition, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{code}") }))
                 {
@@ -243,5 +243,49 @@ namespace TEAMModelOS.Controllers
             }
             return Ok(new { paper });
         }
+
+        /// <summary>
+        /// 更新试卷的答题卡
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        //[AuthToken(Roles = "Teacher")]
+        [HttpPost("upsert-sheet")]
+        public async Task<IActionResult> UpsertSheet(JsonElement request)
+        {
+            try {
+
+                var client = _azureCosmos.GetCosmosClient();
+                if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
+                if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
+                if (!request.TryGetProperty("scope", out JsonElement scope)) return BadRequest();
+                if (!request.TryGetProperty("sheet", out JsonElement sheet)) return BadRequest();
+                if ($"{scope}".Equals("school"))
+                {
+
+                    Paper paper = await client.GetContainer("TEAMModelOS", "School").ReadItemAsync<Paper>($"{id}", new PartitionKey($"Paper-{code}"));
+                    paper.sheet = $"{sheet}";
+                    await client.GetContainer("TEAMModelOS", "School").ReplaceItemAsync(paper, paper.id, new PartitionKey($"{paper.code}"));
+                }
+                else
+                {
+                    Paper paper = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<Paper>($"{id}", new PartitionKey($"Paper-{code}"));
+                    paper.sheet = $"{sheet}";
+                    await client.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync(paper, paper.id, new PartitionKey($"{paper.code}"));
+                }
+                return Ok(new { status = 200 });
+            } catch (CosmosException ex)
+            {
+                if (ex.Status == 404)
+                {
+                    return Ok(new { status = 404 });
+
+                }
+                else {
+                    return Ok(new { status = 500 });
+                }
+            }
+        }
     }
 }

+ 4 - 0
TEAMModelOS/Controllers/School/CorrectController.cs

@@ -218,6 +218,10 @@ namespace TEAMModelOS.Controllers
                 Correct correct = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Correct>(id.GetString(), new PartitionKey($"Correct-{code}"));
                 return Ok(new { correct });
             }
+            catch (CosmosException ex){
+                Correct correct = null;
+                return Ok(new { correct ,status=ex.Status });
+            }
             catch (Exception ex)
             {
                 await _dingDing.SendBotMsg($"OS,{_option.Location},common/Correct/find-id()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);

+ 8 - 4
TEAMModelOS/Controllers/School/StudentController.cs

@@ -108,8 +108,9 @@ namespace TEAMModelOS.Controllers
                         return this.Ok(new { code = $"Base-{schoolId.GetString()}", ids = sucDelIds });
                     case "remove":
                         //將學生基本資料內的classId、no、groupId及groupName寫入null
-                        var retRemove = removeStudentClassInfo(schoolId.GetString(), request.GetProperty("students").EnumerateArray());
-                        return this.Ok(new { code = $"Base-{schoolId.GetString()}", ids = retRemove.Result.studs, retRemove.Result.nonexistentIds, retRemove.Result.errorIds });
+                       ( List<string> studs, List< string > nonexistentIds, List<string> errorIds) retRemove = await removeStudentClassInfo(schoolId.GetString(), request.GetProperty("students").EnumerateArray());
+                       
+                        return  Ok(new { code = $"Base-{schoolId.GetString()}", ids = retRemove.studs, retRemove.nonexistentIds, retRemove.errorIds });
                     default:
                         return BadRequest();
                 }
@@ -1099,6 +1100,7 @@ namespace TEAMModelOS.Controllers
                         writer.WriteStartObject();
                         foreach (var element in doc.EnumerateObject())
                         {
+                            Console.WriteLine(element.ToJsonString());
                             //將教室相關欄位清空
                             switch (true)
                             {
@@ -1124,6 +1126,7 @@ namespace TEAMModelOS.Controllers
                         writer.WriteEndObject();
                         writer.Flush();
 
+                        Console.WriteLine(1111);
                         var ret = await _azureCosmos
                                   .GetCosmosClient()
                                   .GetContainer("TEAMModelOS", "Student")
@@ -1141,14 +1144,15 @@ namespace TEAMModelOS.Controllers
                                 $"IES5,{_option.Location},StudentController/removeStudentClassInfo(),CosmosDB response:{ret.Status}\nBase-{schoolId},id:{id}",
                                 GroupNames.醍摩豆服務運維群組);
                         }
+                        Console.WriteLine(2222);
                     }
                 }
             }
-
+            Console.WriteLine(333);
             //將impStuds內的資料移除sucStuds及errorIds,所得的結果就是不存在於資料庫的id。
             sucStuds.ForEach(o => impStuds.Remove(o));
             errorIds.ForEach(o => impStuds.Remove(o));
-
+            Console.WriteLine(444);
             return (sucStuds, impStuds, errorIds);
         }
 

+ 54 - 0
TEAMModelOS/Controllers/XTest/TestController.cs

@@ -196,6 +196,60 @@ namespace TEAMModelOS.Controllers.XTest
             return  Ok(new { });
             
         }
+
+        /// <summary>
+        /// 删除
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        //[AuthToken(Roles = "teacher")]
+        [HttpPost("fix-blob-content")]
+        public async Task<IActionResult> FixBlobContent(JsonElement request)
+        {
+            try
+            {
+                if (!request.TryGetProperty("name", out JsonElement name)) return BadRequest();
+                if (!request.TryGetProperty("scope", out JsonElement _scope)) return BadRequest();
+                
+                
+                    var client = _azureCosmos.GetCosmosClient();
+                    List<string> prefixs = new List<string>() { "audio", "doc", "image", "other", "res", "video", "thum" };
+                    var ContainerClient = _azureStorage.GetBlobContainerClient($"{name}");
+                    string scope = "private";
+                     
+                     
+                        scope = $"{_scope}";
+                   
+                    var tb = "Teacher";
+                    if (scope != "private")
+                    {
+                        tb = "School";
+                    }
+                    long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+                    
+                    foreach (var prefix in prefixs)
+                    {
+                        List<string> items = await ContainerClient.List(prefix);
+                        foreach (var item in items)
+                        {
+                            var urlsSize = await ContainerClient.GetBlobsSize(item);
+                            Bloblog bloblog = new Bloblog { id = Guid.NewGuid().ToString(), code = $"Bloblog-{name}", pk = "Bloblog", time = now, size = urlsSize != null && urlsSize.HasValue ? urlsSize.Value : 0, type = prefix };
+                           await client.GetContainer("TEAMModelOS", tb).UpsertItemAsync(bloblog, new Azure.Cosmos.PartitionKey(bloblog.code)) ;
+                        }
+                    }
+                     
+               
+                return new OkObjectResult(new { });
+
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"TEAMModelFunction,ActivityHttpTrigger,fix-blob-content()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                return new BadRequestResult();
+            }
+        }
+
         /// <summary>
         /// 删除
         /// </summary>