فهرست منبع

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

liqk 4 سال پیش
والد
کامیت
53f03186e8
22فایلهای تغییر یافته به همراه503 افزوده شده و 607 حذف شده
  1. 1 1
      TEAMModelFunction/TriggerExam.cs
  2. 171 0
      TEAMModelOS.SDK/Extension/CoreTokenExtensions.cs
  3. 10 7
      TEAMModelOS.SDK/Models/Cosmos/Common/Inner/SyllabusTree.cs
  4. 1 0
      TEAMModelOS.SDK/Models/Cosmos/Common/Syllabus.cs
  5. 3 1
      TEAMModelOS.SDK/Models/Cosmos/Common/Volume.cs
  6. 3 0
      TEAMModelOS.SDK/TEAMModelOS.SDK.csproj
  7. 10 1
      TEAMModelOS/ClientApp/src/common/BaseNotification.vue
  8. 0 96
      TEAMModelOS/ClientApp/src/components/syllabus/DragTree.vue
  9. 18 6
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/settings.js
  10. 27 15
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/settings.js
  11. 16 13
      TEAMModelOS/ClientApp/src/view/answersheet/BaseEditor.vue
  12. 5 1
      TEAMModelOS/ClientApp/src/view/evaluation/index/index.vue
  13. 1 1
      TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.less
  14. 34 13
      TEAMModelOS/ClientApp/src/view/settings/BaseApplyForm.vue
  15. 4 4
      TEAMModelOS/ClientApp/src/view/settings/SchoolMgmt.vue
  16. 1 1
      TEAMModelOS/ClientApp/src/view/syllabus/Syllabus.less
  17. 14 5
      TEAMModelOS/ClientApp/src/view/syllabus/Syllabus.vue
  18. 1 1
      TEAMModelOS/ClientApp/src/view/vote/ManageVote.less
  19. 70 64
      TEAMModelOS/Controllers/Analysis/AnalysisController.cs
  20. 10 67
      TEAMModelOS/Controllers/Syllabus/ShareController.cs
  21. 103 267
      TEAMModelOS/Controllers/Syllabus/SyllabusController.cs
  22. 0 43
      TEAMModelOS/Controllers/Syllabus/VolumeController.cs

+ 1 - 1
TEAMModelFunction/TriggerExam.cs

@@ -222,7 +222,7 @@ namespace TEAMModelFunction
                                                             int month = DateTimeOffset.UtcNow.Month;
                                                             int day = DateTimeOffset.UtcNow.Day;
                                                             int time =  month > semester.month ?  0 :  1;
-                                                            int eyear = year - time;
+                                                            int eyear = classroom.year - time;
                                                             result.gradeId = (year - eyear).ToString();
                                                         }
                                                     }

+ 171 - 0
TEAMModelOS.SDK/Extension/CoreTokenExtensions.cs

@@ -0,0 +1,171 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using Microsoft.IdentityModel.Tokens;
+using Microsoft.Identity.Client;
+using System.ComponentModel;
+using System.Threading.Tasks;
+using Azure.Security.KeyVault.Secrets;
+using Azure.Core;
+using Azure.Identity;
+using System.Net.Http;
+using System.Collections.Concurrent;
+using System.Diagnostics;
+
+namespace TEAMModelOS.SDK.Extension
+{
+    public static class CoreTokenExtensions
+    {       //var issuer = Configuration.GetValue<string>("JwtSettings:Issuer");
+            //var signKey = Configuration.GetValue<string>("JwtSettings:SignKey");
+        private const string issuer = "account.teammodel";
+        //Azure AD 租用戶識別碼(國際、大陸)
+        private static List<string> tenantids = new List<string> { "73a2bcc5-fe99-4566-aa8a-07e7bb287df1", "4807e9cf-87b8-4174-aa5b-e76497d7392b" };
+        private static ConcurrentDictionary<string, KeyVaultSecret> KeyVaultSecrets { get; } = new ConcurrentDictionary<string, KeyVaultSecret>();
+              
+
+        #region  Access Token
+        /// <summary>
+        /// 產生AccessToken
+        /// </summary>
+        /// <param name="clientID"></param>
+        /// <param name="location">服務位置,Global or China ...</param>
+        /// <returns></returns>
+        public static async ValueTask<AuthenticationResult> CreateAccessToken(string clientID, string clientSecret, string location)
+        {
+            //從金鑰庫取出秘密,此作法讓所有端直接刷新金鑰,無需傳送秘密,SPA更適用
+            var secret = clientSecret ?? (await GetClientIDSecret(clientID, location)).Value;
+
+            var sts = Enum.Parse<STSEndpoint>(location, true);
+
+            IConfidentialClientApplication app;
+            app = ConfidentialClientApplicationBuilder.Create(clientID)
+                                                      .WithClientSecret(secret)
+                                                      .WithAuthority(new Uri(sts.GetDescriptionText()))
+                                                      .Build();
+            var scope = ((STSScope)sts).GetDescriptionText();
+            var result = await app.AcquireTokenForClient(new[] { scope }).ExecuteAsync();
+            return result;
+        }
+
+        /// <summary>
+        /// 驗證是否為公司Azure發行金鑰,支援大陸國際
+        /// </summary>
+        /// <param name="token"></param>
+        /// <returns></returns>
+        public static bool ValidateAccessToken(JwtSecurityToken token)
+        {
+            try
+            {
+                if (token.Payload.TryGetValue("tid", out var value) && value is string tokenTenantId)
+                {
+                    return tenantids.Contains(tokenTenantId);
+                }
+                return false;
+            }
+            catch (Exception)
+            {
+                return false;
+            }
+        }
+
+        public static bool ValidateIdToken(string token, string salt)
+        {
+            try
+            {
+                var handler = new JwtSecurityTokenHandler();
+                var validationParameters = new TokenValidationParameters
+                {
+                    RequireExpirationTime = true,
+                    ValidateIssuer = false,
+                    ValidateAudience = false,
+                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(salt)),
+                    ValidateLifetime = false,
+                    //LifetimeValidator = LifetimeValidator,
+                    ClockSkew = TimeSpan.Zero                    
+                };
+                ClaimsPrincipal principal = handler.ValidateToken(token, validationParameters, out SecurityToken securityToken);
+                return true;
+            }
+            catch(Exception ex)
+            {
+                Trace.WriteLine(ex.Message);
+                return false;
+            }
+        }
+        #endregion
+
+        private static async ValueTask<KeyVaultSecret> GetClientIDSecret(string clientID, string location)
+        {   //Azure 金鑰庫處理
+            var s = await Task.Run(() =>
+            {
+                var secret = KeyVaultSecrets.GetOrAdd(clientID, (x) =>
+                {
+                    try
+                    {
+                        var sts = Enum.Parse<CoreServiceClient>(location, true);
+                        var scrtetstring = sts.GetDescriptionText().Split(",");
+                        //TODO 之後驗證端點用KnownAuthorityHosts取代,此SDK版本無支援
+                        var secret = new ClientSecretCredential(scrtetstring[0], scrtetstring[1], scrtetstring[2], new TokenCredentialOptions() { AuthorityHost = new Uri(scrtetstring[3]) });
+                        var client = new SecretClient(new Uri(((KeyVaultEndpoint)sts).GetDescriptionText()), secret);
+                        var clientSecret = client.GetSecretAsync(clientID).ConfigureAwait(false);
+                        return clientSecret.GetAwaiter().GetResult();
+                    }
+                    catch
+                    {
+                        return null;
+                    }
+                });
+                return secret;
+            });
+            return s;
+        }
+
+        public static bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters)
+        {
+            return true;
+            //if (expires != null)
+            //{
+            //    if (DateTime.UtcNow < expires)
+            //    {
+            //        return true;
+            //    }
+            //}
+
+            //return false;
+        }
+
+        private enum STSEndpoint
+        {
+            [Description("https://login.chinacloudapi.cn/4807e9cf-87b8-4174-aa5b-e76497d7392b")]
+            China,
+            [Description("https://login.microsoftonline.com/73a2bcc5-fe99-4566-aa8a-07e7bb287df1")]
+            Global
+        }
+
+        private enum STSScope
+        {
+            [Description("api://72643704-b2e7-4b26-b881-bd5865e7a7a5/.default")]
+            China,
+            [Description("api://8768b06f-c5c5-4b0c-abfb-d7ded354626d/.default")]
+            Global
+        }
+
+        private enum KeyVaultEndpoint
+        {
+            [Description("https://corekeyvaultcn.vault.azure.cn/")]
+            China,
+            [Description("https://corekeyvaultjp.vault.azure.net/")]
+            Global
+        }
+
+        private enum CoreServiceClient
+        {
+            [Description("4807e9cf-87b8-4174-aa5b-e76497d7392b,72643704-b2e7-4b26-b881-bd5865e7a7a5,tRYbDXtotEOe2Bbmo=[3h9Hbu_Trt:c6,https://login.partner.microsoftonline.cn")]
+            China,
+            [Description("73a2bcc5-fe99-4566-aa8a-07e7bb287df1,8768b06f-c5c5-4b0c-abfb-d7ded354626d,7=O./yws0L89WcEsece:9/4deJHP4E=F,https://login.microsoftonline.com/")]
+            Global
+        }
+    }
+}

+ 10 - 7
TEAMModelOS.SDK/Models/Cosmos/Common/Inner/SyllabusTree.cs

@@ -10,17 +10,20 @@ namespace TEAMModelOS.SDK.Models
         }
         public List<SyllabusTree> children { get; set; }
     }
-    public class SyllabusTreeNode{
+    public class SyllabusTreeNode {
+
+        public SyllabusTreeNode(){
+            trees = new List<SyllabusTree>();
+        }
+        public string  id { get; set; }
         /// <summary>
         /// 册别的id
         /// </summary>
-        public string id { get; set; }
+        public string volumeId { get; set; }
        // public string code { get; set; }
         public string scope { get; set; }
-        public SyllabusTreeNode() {
-            trees = new List<SyllabusTree>();
-        }
-        public List<SyllabusTree> trees { get; set; }
-    }
+        public  List<SyllabusTree>  trees  { get; set; }
 
+        public List<SyllabusAuth> auth { get; set; } = new List<SyllabusAuth>();
+    }
 }

+ 1 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/Syllabus.cs

@@ -21,6 +21,7 @@ namespace TEAMModelOS.SDK.Models
         public List<Tnode> children { get; set; }
         public string volumeId { get; set; }
         public List<SyllabusAuth> auth { get; set; }
+        public string scope { get; set; }
 }
 
     /// <summary>

+ 3 - 1
TEAMModelOS.SDK/Models/Cosmos/Common/Volume.cs

@@ -82,7 +82,9 @@ namespace TEAMModelOS.SDK.Models.Cosmos.Common
         /// </summary>
         [Required(ErrorMessage = "scope 必须设置")]
         public string scope { get; set; }
-       
+
+        public List<string> syllabusIds { get; set; } = new List<string>();
+
     }
 
 }

+ 3 - 0
TEAMModelOS.SDK/TEAMModelOS.SDK.csproj

@@ -13,7 +13,9 @@
   <ItemGroup>
     <PackageReference Include="AspectCore.Extensions.Reflection" Version="2.2.0" />
     <PackageReference Include="Azure.Cosmos" Version="4.0.0-preview3" />
+    <PackageReference Include="Azure.Identity" Version="1.4.0" />
     <PackageReference Include="Azure.Messaging.ServiceBus" Version="7.1.1" />
+    <PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.1.0" />
     <PackageReference Include="Azure.Storage.Blobs.Batch" Version="12.5.1" />
     <PackageReference Include="Azure.Storage.Queues" Version="12.6.1" />
     <PackageReference Include="ClouDASLibx" Version="1.2.7" />
@@ -22,6 +24,7 @@
     <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.10" />
     <PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
     <PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="5.0.4" />
+    <PackageReference Include="Microsoft.Identity.Client" Version="4.32.1" />
     <PackageReference Include="StackExchange.Redis" Version="2.2.4" />
     <PackageReference Include="SvgNet" Version="2.1.1" />
     <PackageReference Include="System.Drawing.Common" Version="5.0.2" />

+ 10 - 1
TEAMModelOS/ClientApp/src/common/BaseNotification.vue

@@ -1,7 +1,9 @@
 <template>
 	<div class="base-notification">
 		<Poptip :title="$t('utils.newNotice')" class="dark-iview-poptip">
-			<Icon type="md-notifications" />
+			<Badge :count="3">
+				<Icon type="md-notifications" />
+			</Badge>
 			<!-- <div slot="content" class="notice-wrap">
 				<div class="notice-item">
 					<p class="item-name">青城山学校</p>
@@ -37,6 +39,13 @@
 		.dark-iview-poptip .ivu-poptip-popper{
 			padding: 0;
 			top: 40px !important;
+			z-index: 9999;
+		}
+		
+		.ivu-badge-count{
+			height: auto;
+			padding: 2px 3px;
+			line-height: 10px;
 		}
 		
 		.notice-wrap{

+ 0 - 96
TEAMModelOS/ClientApp/src/components/syllabus/DragTree.vue

@@ -151,63 +151,6 @@
 				this.nodeInfo.title = node.data.title
 			},
 
-
-			onRelatedContent(node, data, e) {
-				e.stopPropagation() // 防止点击事件穿透到父层
-				this.isRelatedContent = true
-				this.currentEditData = data
-			},
-
-			onShowContent(val, data) {
-				console.log(data)
-				this.contentIndex = val
-				this.currentItems = []
-				this.currentResources = []
-				// data.items.length && this.findQuestionById(data.items)
-				data.resources.length && this.findResourceById(data.resources)
-				this.isShowContent = true
-			},
-
-			/**
-			 * 通过id查询内容信息
-			 * */
-			findResourceById(ids) {
-
-				this.$api.learnActivity.FindSyllabusResourceById(ids).then(
-					res => {
-						if (!res.error) {
-							this.currentResources = res.result.data
-						} else {
-							this.$Message.error("API ERROR")
-						}
-					},
-					err => {
-						this.$Message.error("API ERROR")
-
-					}
-				)
-
-			},
-			/**
-			 * 通过id查询题目信息
-			 * */
-			findQuestionById(ids) {
-				this.$api.learnActivity.FindQuestionById(ids).then(
-					res => {
-						if (!res.error) {
-							this.currentItems = res.result.data
-						} else {
-							this.$Message.error("API ERROR")
-						}
-					},
-					err => {
-						this.$Message.error("API ERROR")
-
-					}
-				)
-			},
-
-
 			// 提交编辑或者新增
 			onSubmitNode() {
 				if (!this.nodeInfo.title) {
@@ -231,50 +174,11 @@
 					}
 					this.currentParentData.children.push(newChild)
 				}
-
 				this.isEditOrAdd = false
 				this.$parent.hasModify = true
 				this.$Message.success(this.isEditItem ? '编辑成功' : '添加成功')
 			},
 
-
-			/**
-			 * 选择关联题目
-			 * @param val 当前已选题目
-			 */
-			onSelectQuestion(val) {
-				this.questionList = val.questions
-			},
-
-			/**
-			 * 选择关联内容
-			 * @param val 当前已选内容
-			 */
-			onSelectFile(val) {
-				this.fileList = val.files
-				console.log(val)
-			},
-
-			/** 保存内容与题目关联 */
-			onSaveNode() {
-				this.isLoading = true
-				this.currentEditData.items = this.currentEditData.items ? [...new Set(this.currentEditData.items.concat(
-					this.questionList.map(item => item.id)))] : [...new Set(this.questionList.map(item => item.id))]
-				this.currentEditData.resources = this.currentEditData.resources ? [...new Set(this.currentEditData
-					.resources.concat(this.fileList.map(item => item.url)))] : [...new Set(this.fileList.map(item =>
-					item.url))]
-				this.$api.syllabus.SaveOrUpdateAsNodes([this.currentEditData]).then(res => {
-					if (!res.error && res) {
-						this.isRelatedContent = false
-						this.$emit('onTreeUpdate')
-						this.isLoading = false
-						this.$parent.hasModify = true
-					} else {
-						this.$Message.warning('获取数据失败')
-					}
-				})
-			},
-
 		},
 		computed:{
 			isFirstLevel(){

+ 18 - 6
TEAMModelOS/ClientApp/src/locale/lang/zh-CN/settings.js

@@ -44,6 +44,9 @@ export default {
 	status1:'已加入',
 	status2:'收到邀请',
 	status3:'申请中',
+	searchTip:'请输入<span>学校名称</span>或者<span>学校代码</span>进行搜索',
+	searchNone:'* 如未搜索到您想要的学校,可点击下方申请建立您的学校',
+	applyBtn:'申请建立学校',
 	openList: '开放平台应用列表',
 	openInfo: '应用信息',
 	openName: '应用名称',
@@ -76,19 +79,28 @@ export default {
 	openModal1: "应用列表获取失败",
 	openModal2: "api列表获取失败",
 	applyForm:{
-		name:'学校名称',
-		id:'学校简码',
-		code:'学校代码',
-		address:'学校位置',
-		cellphone:'联系方式',
+		title:'申请建立AI智慧学校',
+		name:'学校/机构名称',
+		id:'学校/机构简码',
+		code:'学校/机构代码',
+		address:'学校/机构地址',
+		manager:'学校/机构负责人',
+		cellphone:'学校/机构联系电话',
 		content:'备注信息',
 		place1:'选择学校所在地区',
 		place2:'请输入学校详细地址',
 		place3:'请输入您的手机号,邮箱等联系方式',
 		place4:'可填写您的备注信息',
+		place5:'请输入管理员的称呼',
+		place6:'请选择学校所在地区',
 		submit:'提交申请',
 		submitSuc:'已成功提交申请!',
 		errTip1:'请选择正确的地区!',
-		errTip2:'请将信息填写完整!'
+		errTip2:'请将信息填写完整!',
+		rule1:'学校名称不能为空',
+		rule2:'管理员信息不能为空',
+		rule3:'联系方式不能为空',
+		rule4:'学校地址不能为空',
+		applyTip:'* 提交申请后,将会于5个工作日内审核完毕,并通知申请人。'
 	}
 }

+ 27 - 15
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/settings.js

@@ -44,6 +44,9 @@ export default {
 	status1: '已加入',
 	status2: '收到邀請',
 	status3: '申請中',
+	searchTip:'請輸入<span>學校名稱</span>或者<span>學校程式碼</span>進行搜尋',
+	searchNone:'*如未搜尋到您想要的學校,可點擊下方申請建立您的學校',
+	applyBtn:'申請建立學校',
 	openList: '開放平臺清單',
 	openInfo: '平臺資訊',
 	openName: '應用名稱',
@@ -57,20 +60,29 @@ export default {
 	apiMethod: '請求方法',
 	openKeep: '保存平臺',
 	unedit: '取消編輯',
-	applyForm: {
-		name: '學校名稱',
-		id: '學校簡碼',
-		code: '學校程式碼',
-		address: '學校位置',
-		cellphone: '聯繫方式',
-		content: '備註資訊',
-		place1: '選擇學校所在地區',
-		place2: '請輸入學校詳細地址',
-		place3: '請輸入您的手機號,郵箱等聯繫方式',
-		place4: '可填寫您的備註資訊',
-		submit: '提交申請',
-		submitSuc: '已成功提交申請!',
-		errTip1: '請選擇正確的地區!',
-		errTip2: '請將資訊填寫完整!'
+	applyForm:{
+	title:'申請建立AI智慧學校',
+	name:'學校/機構名稱',
+	id:'學校/機构簡碼',
+	code:'學校/機构程式碼',
+	address:'學校/機构地址',
+	manager:'學校/機构負責人',
+	cellphone:'學校/機构聯繫電話',
+	content:'備註資訊',
+	place1:'選擇學校所在地區',
+	place2:'請輸入學校詳細地址',
+	place3:'請輸入您的手機號,郵箱等聯繫方式',
+	place4:'可填寫您的備註資訊',
+	place5:'請輸入管理員的稱呼',
+	place6:'請選擇學校所在地區',
+	submit:'提交申請',
+	submitSuc:'已成功提交申請!',
+	errTip1:'請選擇正確的地區!',
+	errTip2:'請將資訊填寫完整!',
+	rule1:'學校名稱不能為空',
+	rule2:'管理員資訊不能為空',
+	rule3:'聯繫方式不能為空',
+	rule4:'學校地址不能為空',
+	applyTip:'*提交申請後,將會於5個工作日內稽核完畢,並通知申請人。'
 	}
 }

+ 16 - 13
TEAMModelOS/ClientApp/src/view/answersheet/BaseEditor.vue

@@ -313,7 +313,6 @@
 					}
 					
 					// 如果自定义插入的是作文题 则进行对应题型渲染
-					console.log('插入的主观题',subjectiveItem)
 					if(subjectiveItem.maxWords > 0){
 						this.myEditor.txt.clear()
 						addStr = '<p>' +  itemOrder + "、 作文(" + (subjectiveItem.score + "分)") + '</p>'; 
@@ -349,18 +348,22 @@
 						let isNewPage = this.$store.state.answerSheet.isNewPage;
 						let lastBottomGap = 20;
 						let rectTop = editorDom.getBoundingClientRect().top
-						let Y = rectTop + scrollDis - 90;
+						let Y = scrollDis > 90 ? scrollDis - Math.abs(rectTop)  - 90 : rectTop + scrollDis - 90;
 						let paperH = PAPER_H;
 						let curEditorY = Y > paperH ? +((Y % paperH).toFixed(4)) : Y;
 						let curEditorH = editorDom.clientHeight; // 默认200px
-						console.log(itemOrder, 'rectTop', rectTop);
-						console.log(itemOrder, 'scrollDis', scrollDis);
-						console.log(itemOrder, '当前Y', Y);
 						let leftHeight = paperH - curEditorY - lastBottomGap - SVG_BORDER_MB;
-						console.log(itemOrder, '高度', curEditorH)
-						console.log(itemOrder, 'LEFT高度', leftHeight)
 						let fixHeight = curEditorH - leftHeight + 20
-						console.log(itemOrder, '需要fix的高度', fixHeight)
+						if(itemOrder === 6){
+							console.log(itemOrder, 'rectTop', rectTop);
+							console.log(itemOrder, 'scrollDis', scrollDis);
+							console.log(itemOrder, '距离第一页顶点的Y', Y);
+							console.log(itemOrder, '距离当前页面顶点的Y', curEditorY);
+							console.log(itemOrder, '高度', curEditorH)
+							console.log(itemOrder, 'LEFT高度', leftHeight)
+							console.log(itemOrder, '需要fix的高度', fixHeight)
+						}
+						
 						// 如果 渲染当前富文本的时候 需要渲染的高度超过当前页的剩余高度 则需要进行加页处理
 						if (curEditorY + curEditorH + lastBottomGap + SVG_BORDER_MB > PAPER_H) {
 							// console.log(itemOrder, Y , curEditorY , curEditorH , '超出了')
@@ -373,8 +376,8 @@
 								heightArr.push(leftHeight)
 								// 如果渲染的客观题高度在这个区间 才需要在下一页添加补充作答区域 其余全部按照正常 跨页处理不需要补充作答区域
 								let fixCount = Math.ceil(fixHeight / SVG_BORDER_PROP.height)
-								console.log(itemOrder + "需要处理跨页,数量为" + fixCount);
-								console.log(itemOrder + "剩余渲染高度" + fixHeight);
+								// console.log(itemOrder + "需要处理跨页,数量为" + fixCount);
+								// console.log(itemOrder + "剩余渲染高度" + fixHeight);
 								for(let i = 0;i < fixCount;i++){
 									this.fixArr.push(0)
 										let curFixHeight = fixHeight > SVG_BORDER_PROP.height  ? SVG_BORDER_PROP.height - 30 : fixHeight
@@ -388,8 +391,8 @@
 								this.$nextTick(() => {
 									let splitHtmlArr = this.getSplitHtml(this.pArr,heightArr)
 									splitHtmlArr.forEach((curEditorContent,editorIndex) => {
-										console.log('富文本的分割高度',heightArr)
-										console.log('富文本的分割',splitHtmlArr)
+										// console.log('富文本的分割高度',heightArr)
+										// console.log('富文本的分割',splitHtmlArr)
 										
 										let editorHeight = curEditorContent.html === '' ? heightArr[editorIndex] : heightArr[editorIndex]
 										// let curEditorContent = this.getSplitHtml(this.pArr,curEditorHeight)
@@ -902,7 +905,7 @@
 		top: 10px;
 		font-size: 12px;
 		background-color: orangered;
-		display: inline-flex;
+		display: none;
 		justify-content: center;
 		align-items: center;
 		color: #fff;

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

@@ -2,7 +2,7 @@
     <div class="evaluation">
         
         <Loading :top="100" v-show="dataLoading" type="3"></Loading>
-        <vuescroll ref="evScroll" id="evScroll">
+        <vuescroll ref="evScroll" id="evScroll" @handle-scroll="handleScroll">
             <div class="ev-body">
                 <div class="ev-content">
                     <router-view />
@@ -42,6 +42,10 @@
               name: name
             })
       },
+	  // 判断容器滚动距离
+	  handleScroll(vertical, horizontal, nativeEvent) {
+	  	console.log(vertical.scrollTop)
+	  },
 
       
     },

+ 1 - 1
TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.less

@@ -181,7 +181,7 @@
 
     .qn-info-box {
         border-right: 1px solid @borderColor;
-		background-color: #2b2b2e;
+		background-color: #242328;
 
         .qn-info-wrap{
             color:#AAAAAA !important;

+ 34 - 13
TEAMModelOS/ClientApp/src/view/settings/BaseApplyForm.vue

@@ -4,27 +4,28 @@
 			<FormItem :label="$t('settings.applyForm.name')" prop="name">
 				<Input v-model="applyForm.name" :placeholder="$t('settings.applyForm.place1')"></Input>
 			</FormItem>
-			<!-- <FormItem label="学校简码" prop="id">
-				<Input v-model="applyForm.id"></Input>
+			<FormItem :label="$t('settings.applyForm.manager')" prop="manager">
+				<Input v-model="applyForm.manager" :placeholder="$t('settings.applyForm.place5')"></Input>
 			</FormItem>
-			<FormItem label="学校代码" prop="code">
+			<!-- <FormItem label="学校代码" prop="code">
 				<Input v-model="applyForm.code"></Input>
 			</FormItem> -->
-			<FormItem :label="$t('settings.applyForm.address')">
+			<FormItem :label="$t('settings.applyForm.cellphone')" prop="cellphone">
+				<Input v-model="applyForm.cellphone" :placeholder="$t('settings.applyForm.place3')"></Input>
+			</FormItem>
+			<FormItem :label="$t('settings.applyForm.address')" prop="address">
 				<BaseAreaPicker ref="areaPicker" v-if="isChinaSite"></BaseAreaPicker>
 				<div class="country-select" v-if="!isChinaSite">
-					<Select v-model="curCountry" filterable placeholder="选择学校所在地区">
+					<Select v-model="curCountry" filterable :placeholder="$t('settings.applyForm.place6')">
 						<Option v-for="(country,index) in countryArr" :value="country.cn" :key="index" >{{ country.cn }}</Option>
 					</Select>
 				</div>
 				<Input type="textarea" :autosize="{minRows: 3,maxRows: 5}" v-model="applyForm.address" :placeholder="$t('settings.applyForm.place2')"></Input>
 			</FormItem>
-			<FormItem :label="$t('settings.applyForm.cellphone')" prop="cellphone">
-				<Input v-model="applyForm.cellphone" :placeholder="$t('settings.applyForm.place3')"></Input>
-			</FormItem>
-			<FormItem :label="$t('settings.applyForm.content')" prop="content">
+			
+			<!-- <FormItem :label="$t('settings.applyForm.content')" prop="content">
 				<Input type="textarea" :autosize="{minRows: 3,maxRows: 5}" v-model="applyForm.content" :placeholder="$t('settings.applyForm.place4')"></Input>
-			</FormItem>
+			</FormItem> -->
 			<!-- <FormItem label="学校图标">
 				<Upload v-if="!isPreview" ref="upload" :show-upload-list="false" :on-success="handleSuccess"
 					:format="['jpg','jpeg','png']" :max-size="2048" :on-format-error="handleFormatError"
@@ -42,6 +43,9 @@
 				</div>
 			</FormItem> -->
 		</Form>
+		<p style="color: #c6c6c6;text-align: left;margin-left: 20px;">
+			{{ $t('settings.applyForm.applyTip') }}
+		</p>
 		<Button style="background-color: #168794;width: 90%;margin-left: 5%;" @click="onSubmit" :loading="isBtnLoading">{{ $t('settings.applyForm.submit') }}</Button>
 	</div>
 </template>
@@ -57,6 +61,7 @@
 				countryArr:[],
 				applyForm: {
 					id: '',
+					manager:'',
 					code: '',
 					name: '',
 					region: '',
@@ -71,7 +76,7 @@
 				ruleValidate: {
 					name: [{
 						required: true,
-						message: '学校名称不能为空 ',
+						message: this.$t('settings.applyForm.rule1'),
 						trigger: 'blur'
 					}],
 					id: [{
@@ -79,6 +84,11 @@
 						message: '学校简码不能为空 ',
 						trigger: 'blur'
 					}],
+					manager: [{
+						required: true,
+						message: this.$t('settings.applyForm.rule2'),
+						trigger: 'blur'
+					}],
 					code: [{
 						required: true,
 						message: '学校代码不能为空 ',
@@ -86,7 +96,12 @@
 					}],
 					cellphone: [{
 						required: true,
-						message: '联系方式不能为空 ',
+						message: this.$t('settings.applyForm.rule3'),
+						trigger: 'blur'
+					}],
+					address: [{
+						required: true,
+						message: this.$t('settings.applyForm.rule4'),
 						trigger: 'blur'
 					}],
 				}
@@ -117,6 +132,12 @@
 									setTimeout(()=> {
 										this.isBtnLoading = false
 										this.$emit('applySuc')
+										this.$refs.applyForm.resetFields()
+										this.$refs.areaPicker.pickResult = {
+											province: null,
+											city: null,
+											area: null
+										}
 										this.$Message.success(this.$t('settings.applyForm.submitSuc'))
 									},1000)
 								}
@@ -157,7 +178,7 @@
 
 <style lang="less">
 	.apply-form {
-		padding: 50px 20px;
+		padding: 50px 20px 0 20px;
 		
 		.country-select{
 			width: 200px;

+ 4 - 4
TEAMModelOS/ClientApp/src/view/settings/SchoolMgmt.vue

@@ -89,7 +89,7 @@
 			<div class="table-wrap">
 				<!-- <Table stripe :columns="schoolColumns" :data="schoolList"></Table> -->
 				<div class="search-wrap">
-					<p class="search-wrap-tip">请输入<span>学校名称</span>或者<span>学校代码</span>进行搜索</p>
+					<p class="search-wrap-tip" v-html="$t('settings.searchTip')"></p>
 					<!-- <Input class="searchIpt" prefix="ios-search" v-model="schoolListSearch" /> -->
 					<Select v-model="searchSchoolIndex" placeholder="" clearable filterable prefix="ios-search"
 						@on-query-change="onQueryChange" ref="schoolSelect">
@@ -102,8 +102,8 @@
 						</Option>
 					</Select>
 					<p class="errorMsg">{{searchIdErrorMsg}}</p>
-					<p class="searchBtn-tip">* 如未搜索到您想要的学校,可点击下方申请建立您的学校</p>
-					<Button :loading="searchLoading" type="info" class="searchBtn" @click="applyJoin()">申请建立学校</Button>
+					<p class="searchBtn-tip">{{ $t('settings.searchNone') }}</p>
+					<Button :loading="searchLoading" type="info" class="searchBtn" @click="applyJoin()">{{ $t('settings.applyBtn') }}</Button>
 				</div>
 			</div>
 		</div>
@@ -116,7 +116,7 @@
 		</Modal>
 
 		<Modal v-model="applyModal" class-name="go-school-modal" footer-hide width="500">
-			<span class="apply-title">申请建立学校</span>
+			<span class="apply-title">{{ $t('settings.applyForm.title')}}</span>
 			<BaseApplyForm @applySuc="applyModal = false"></BaseApplyForm>
 		</Modal>
 

+ 1 - 1
TEAMModelOS/ClientApp/src/view/syllabus/Syllabus.less

@@ -15,7 +15,7 @@
 		display: flex;
 		align-items: center;
 		padding-left: 15px;
-		z-index: 999;
+		z-index: 10;
 		
 		.subject-select{
 			&-item{

+ 14 - 5
TEAMModelOS/ClientApp/src/view/syllabus/Syllabus.vue

@@ -102,7 +102,7 @@
 							<img src="../../assets/source/zip.png" v-else-if="item.type === 'res'" />
 							<img src="../../assets/source/image.png" v-else-if="item.type === 'thum'" />
 							<img src="../../assets/source/unknow.png" v-else="item.type === 'other'" />
-							<span v-html="item.title"></span>
+							<span v-html="getSimpleText(item.title)" style="max-width: 70%;"></span>
 							<div class="node-resource-tools">
 								<div class="node-resource-tool" @click="onPreview(item)">
 									<Icon type="md-eye" />
@@ -197,8 +197,8 @@
 			<div class="modal-header" slot="header">
 				选择{{ isSchool ? '共编' : '分享' }}教师
 			</div>
-			<InviteTeacher :node="curShareNode"></InviteTeacher>
-			<Button slot="footer" @click="onRelateContent" style="margin-bottom: 20px;" class="modal-btn">确认</Button>
+			<InviteTeacher :node="curShareNode" ref="inviteRef"></InviteTeacher>
+			<Button slot="footer" @click="doInviteTeacher" style="margin-bottom: 20px;" class="modal-btn">确认</Button>
 		</Modal>
 
 		<!-- 预览试题试卷弹窗 -->
@@ -343,6 +343,11 @@
 					}
 				});
 			},
+			/* 提取富文本内容中的文本 */
+			getSimpleText(html) {
+				html = html || '';
+				    return html.replace(/<[^>]+>/g, "");//去掉所有的html标记
+			},
 			/* 切换学段的操作 */
 			onPeriodChange(val) {
 				this.subjectList = this.periodList[val].subjects;
@@ -460,8 +465,8 @@
 				this.curNode.rnodes = []
 				this.curNode.id = ''
 				this.$api.syllabus.GetTreeByVolume({
-					id: volume.id,
-					code: volume.code.replace('Volume-', ''),
+					code: volume.id,
+					// code: volume.code.replace('Volume-', ''),
 					scope: volume.scope
 				}).then(res => {
 					if (!res.error) {
@@ -520,6 +525,10 @@
 				this.curShareNode = data
 				this.isInviteModal = true
 			},
+			/* 点击确认进行课纲的共编或者分享功能 */
+			doInviteTeacher(){
+				let selectedTeacher = this.$refs.inviteRef.teacherList
+			},
 			/* 添加超链接 */
 			onAddLink() {
 				this.$refs.treeRef.curNode.rnodes.push({

+ 1 - 1
TEAMModelOS/ClientApp/src/view/vote/ManageVote.less

@@ -148,7 +148,7 @@
 
     .hw-info-box {
         width: 100%;
-		background-color: #2b2b2e;
+		background-color: #242328;
         border-right: 1px solid @borderColor;
     }
 

+ 70 - 64
TEAMModelOS/Controllers/Analysis/AnalysisController.cs

@@ -538,7 +538,7 @@ namespace TEAMModelOS.Controllers.Analysis
                     {
                         //年级全科的pr
                         int index = stuGradeTotal.IndexOf(x.total);
-                        double GPR = stuCount > 0 ? Math.Floor(100 - (100 * (index + 1) - 50)*1.0 / stuCount) : 0;
+                        double GPR = stuCount > 0 ? Math.Floor(100 - (100 * (index + 1) - 50) * 1.0 / stuCount) : 0;
                         //double GPR = 100 - (100 * (index + 1) - 50) / stuCount;
                         x.gpr = GPR;
                         x.gsort = index + 1;
@@ -580,15 +580,16 @@ namespace TEAMModelOS.Controllers.Analysis
                 BadRequest(ex.StackTrace);
             }
 
-            var sub  = examResults.Select(e => new {
-                    id = e.id,
-                    name = info.subjects.FirstOrDefault(c => c.id == e.id).name,
-                    record = e.record,
-                    phc = e.phc,
-                    plc = e.plc,
-                    sRate = e.sRate,
-                    average = e.average,
-                    standard = e.standard
+            var sub = examResults.Select(e => new
+            {
+                id = e.id,
+                name = info.subjects.FirstOrDefault(c => c.id == e.id).name,
+                record = e.record,
+                phc = e.phc,
+                plc = e.plc,
+                sRate = e.sRate,
+                average = e.average,
+                standard = e.standard
             });
             /*var sub = info.subjects.Select(x => new
             {
@@ -1293,18 +1294,18 @@ namespace TEAMModelOS.Controllers.Analysis
                                 //单个认知层次得分情况
                                 scores += exam.studentScores[index][n];
                             }
-                           /* if (exam.studentScores[index].Sum() >= rhw && phCount < rhwCount && exam.studentScores[index][n] == 0)
-                            {
-                                rhwC++;
-                                phCount++;
-                                continue;
-                            }
-                            if (exam.studentScores[index].Sum() <= rhl && plCount < (exam.studentIds.Count - rhlCount) && exam.studentScores[index][n] == 0)
-                            {
-                                rhlC++;
-                                plCount++;
-                                continue;
-                            }*/
+                            /* if (exam.studentScores[index].Sum() >= rhw && phCount < rhwCount && exam.studentScores[index][n] == 0)
+                             {
+                                 rhwC++;
+                                 phCount++;
+                                 continue;
+                             }
+                             if (exam.studentScores[index].Sum() <= rhl && plCount < (exam.studentIds.Count - rhlCount) && exam.studentScores[index][n] == 0)
+                             {
+                                 rhlC++;
+                                 plCount++;
+                                 continue;
+                             }*/
                             //anwGPoint += exam.studentScores[index][n];
                         }
                         gPoint += point[n];
@@ -1469,7 +1470,7 @@ namespace TEAMModelOS.Controllers.Analysis
                 {
                     using var json = await JsonDocument.ParseAsync(response.ContentStream);
                     school = json.ToObject<School>();
-                }   
+                }
                 //School school = await client.GetContainer("TEAMModelOS", "School").ReadItemAsync<School>(code.ToString(), new PartitionKey($"Base"));
                 List<ExamResult> examResults = new List<ExamResult>();
                 var query = $"select c.id,c.name,c.subjectId,c.studentScores,c.studentIds,c.paper,c.classes from c where c.examId =  '{id}' ";
@@ -1512,33 +1513,13 @@ namespace TEAMModelOS.Controllers.Analysis
                     List<List<double>> classScores = new List<List<double>>();
                     //此处声明集合存储原本的成绩序列
                     List<List<double>> cScores = new List<List<double>>();
-                    foreach (ClassRange range in result.classes)
+                    /*var sc = result.classes.GroupBy(m => new { m.gradeId }).Select(c => new { gId = c.Key.gradeId, ranges = c.ToList() }).ToList();
+                    foreach (var aa in sc)
                     {
-                        List<double> scores = new List<double>();
-                        List<double> finalScores = new List<double>();
-                        if (!string.IsNullOrEmpty(range.gradeId))
+                        foreach (ClassRange range in aa.ranges)
                         {
-                            if (range.gradeId.Equals(gId.ToString(), StringComparison.OrdinalIgnoreCase))
-                            {
-                                ClassId.Add(range.id);
-                                cla.Add(range.id);
-                                double totalClass = 0;
-                                for (int i = range.range[0]; i <= range.range[1]; i++)
-                                {
-                                    totalClass += result.studentScores[i].Sum();
-                                    scores.Add(result.studentScores[i].Sum());
-                                    finalScores.Add(result.studentScores[i].Sum());
-                                    gradeScores.Add(result.studentScores[i].Sum());
-                                    totalGrade += result.studentScores[i].Sum();
-                                    stu.Add(result.studentIds[i]);
-                                }
-                                classScores.Add(scores);
-                                cScores.Add(finalScores);
-                                ClassAverage.Add(range.range[1] - range.range[0] + 1 > 0 ? Math.Round(totalClass * 1.0 / (range.range[1] - range.range[0] + 1), 2) : 0);
-                                personCount.Add(range.range[1] - range.range[0] + 1);
-                            }
-                        }
-                        else {
+                            List<double> scores = new();
+                            List<double> finalScores = new();
                             ClassId.Add(range.id);
                             cla.Add(range.id);
                             double totalClass = 0;
@@ -1556,23 +1537,48 @@ namespace TEAMModelOS.Controllers.Analysis
                             ClassAverage.Add(range.range[1] - range.range[0] + 1 > 0 ? Math.Round(totalClass * 1.0 / (range.range[1] - range.range[0] + 1), 2) : 0);
                             personCount.Add(range.range[1] - range.range[0] + 1);
                         }
-                        
+                        classAllAverage.Add(ClassAverage);
+                        gradeScores.Sort((s1, s2) => { return s2.CompareTo(s1); });
+                        indexClass = ClassId.IndexOf(cId.ToString());
+                        //单科成绩
+                        allList.Add(cScores[indexClass]);
+                        classScores[indexClass].Sort((s1, s2) => { return s2.CompareTo(s1); });
+                        mapClass.Add("scoreSum", result.paper.point.Sum());
+                        mapClass.Add("score", result.studentScores[index].Sum());
+                        mapClass.Add("classAverage", ClassAverage[indexClass]);
+                        mapClass.Add("classCount", personCount[indexClass]);
+                        mapClass.Add("classRank", classScores[indexClass].IndexOf(result.studentScores[index].Sum()));
+                        mapClass.Add("gradeAverage", result.studentIds.Count > 0 ? Math.Round(totalGrade * 1.0 / result.studentIds.Count, 2) : 0);
+                        mapClass.Add("gradeCount", result.studentIds.Count);
+                        mapClass.Add("gradeRank", gradeScores.IndexOf(result.studentScores[index].Sum()));
+                        averageMap.Add(mapClass);
                     }
-                    classAllAverage.Add(ClassAverage);
-                    gradeScores.Sort((s1, s2) => { return s2.CompareTo(s1); });
-                    indexClass = ClassId.IndexOf(cId.ToString());
-                    //单科成绩
-                    allList.Add(cScores[indexClass]);
-                    classScores[indexClass].Sort((s1, s2) => { return s2.CompareTo(s1); });
-                    mapClass.Add("scoreSum", result.paper.point.Sum());
-                    mapClass.Add("score", result.studentScores[index].Sum());
-                    mapClass.Add("classAverage", ClassAverage[indexClass]);
-                    mapClass.Add("classCount", personCount[indexClass]);
-                    mapClass.Add("classRank", classScores[indexClass].IndexOf(result.studentScores[index].Sum()));
-                    mapClass.Add("gradeAverage", result.studentIds.Count > 0 ? Math.Round(totalGrade * 1.0 / result.studentIds.Count, 2) : 0);
-                    mapClass.Add("gradeCount", result.studentIds.Count);
-                    mapClass.Add("gradeRank", gradeScores.IndexOf(result.studentScores[index].Sum()));
-                    averageMap.Add(mapClass);
+*/
+                    foreach (ClassRange range in result.classes)
+                    {
+                        List<double> scores = new List<double>();
+                        List<double> finalScores = new List<double>();
+
+                        ClassId.Add(range.id);
+                        cla.Add(range.id);
+                        double totalClass = 0;
+                        for (int i = range.range[0]; i <= range.range[1]; i++)
+                        {
+                            totalClass += result.studentScores[i].Sum();
+                            scores.Add(result.studentScores[i].Sum());
+                            finalScores.Add(result.studentScores[i].Sum());
+                            gradeScores.Add(result.studentScores[i].Sum());
+                            totalGrade += result.studentScores[i].Sum();
+                            stu.Add(result.studentIds[i]);
+                        }
+                        classScores.Add(scores);
+                        cScores.Add(finalScores);
+                        ClassAverage.Add(range.range[1] - range.range[0] + 1 > 0 ? Math.Round(totalClass * 1.0 / (range.range[1] - range.range[0] + 1), 2) : 0);
+                        personCount.Add(range.range[1] - range.range[0] + 1);
+
+
+                    }
+
                 }
                 //处理班级/年级全科均分
                 List<double> AllAverage = new List<double>();

+ 10 - 67
TEAMModelOS/Controllers/Syllabus/ShareController.cs

@@ -304,11 +304,10 @@ namespace TEAMModelOS.Controllers
         {
             try
             {
-                List<List<SyllabusTree>> tts = new List<List<SyllabusTree>>();
+                List<SyllabusTreeNode> treeNodes = new List<SyllabusTreeNode>();
                 Volume volume;
                 var client = _azureCosmos.GetCosmosClient();
                 string code = null;
-                SyllabusTreeNode tree = null;
                 List<string> sid = new List<string>();
                 request.syllabusId.ForEach(x => { sid.Add($"'{x}'"); });
                 var sidSql= string.Join(",", sid);
@@ -319,86 +318,31 @@ namespace TEAMModelOS.Controllers
                     await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<Syllabus>(queryText: queryslt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{request.volumeId}") }))
                     {
                         List<SyllabusTree> trees = SyllabusService.ListToTree(item.children);
-                        tts.Add(trees);
+                        SyllabusTreeNode tree = new SyllabusTreeNode() { id = item.id, scope = item.scope, trees = trees, volumeId = item.volumeId, auth = item.auth };
+                        treeNodes.Add(tree);
+
                     }
                     volume = await client.GetContainer("TEAMModelOS", "School").ReadItemAsync<Volume>(request.volumeId, new PartitionKey($"Volume-{code}"));
-                    tree = new SyllabusTreeNode() { id = request.volumeId, scope = "school", trees = new List<SyllabusTree>() };
+                    
                 }
                 else if (request.scope == "private")
                 {
                     code = request.issuer;
-                    var queryslt = $"SELECT  value(c) FROM c ";
+                    var queryslt = $"SELECT  value(c) FROM c where c.id in ({sidSql})";
                     await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIterator<Syllabus>(queryText: queryslt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{request.volumeId}") }))
                     {
                         List<SyllabusTree> trees = SyllabusService.ListToTree(item.children);
-                        tts.Add(trees);
+                        SyllabusTreeNode tree = new SyllabusTreeNode() { id = item.id, scope = item.scope, trees = trees, volumeId = item.volumeId, auth = item.auth };
+                        treeNodes.Add(tree);
                     }
                     volume = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<Volume>(request.volumeId, new PartitionKey($"Volume-{code}"));
-                    tree = new SyllabusTreeNode() { id = request.volumeId, scope = "private", trees = new List<SyllabusTree>() };
                 }
                 else
                 {
                     return BadRequest();
                 }
-                //bool coedit = false;
-                //bool share = false;
-                //SyllabusTreeNode  etree = null;
-                //if (syllabus.auth.IsNotEmpty())
-                //{
-                //    foreach (var x in syllabus.auth)
-                //    {
-                //        if ($"Share-{x.tmdid }" == request.code)
-                //        {
-                //            coedit = x.coedit;
-                //            share = x.share;
-                //            break;
-                //        }
-                //    }
-                //}
-                //if (coedit)
-                //{
-                //    var treechd = SyllabusService.ListToTree(syllabus.children);
-                //    etree = new SyllabusTreeNode() {
-                //        id = syllabus.id,
-                //        trees = treechd
-                //    };
-                //}
-                //SyllabusTreeNode stree = new SyllabusTreeNode();
-                //if (share && snodes.IsNotEmpty())
-                //{
-                //    stree = new SyllabusTreeNode()
-                //    {
-                //        id = syllabus.id,
-                //    };
-                //    snodes.ForEach(x =>
-                //    {
-                //        SyllabusTree syllabusTree = null;
-                //        foreach (var node in syllabus.children)
-                //        {
-                //            if (node.id == x)
-                //            {
-                //                syllabusTree = new SyllabusTree
-                //                {
-                //                    id = node.id,
-                //                    pid = node.pid,
-                //                    order = node.order,
-                //                    rnodes = node.rnodes,
-                //                    cids = node.cids
-                //                };
-                //                break;
-                //            }
-                //        }
-                //        HashSet<Tnode> newNodes = new HashSet<Tnode>();
-                //        SyllabusService.GetNewNode(syllabus.children, x, newNodes);
-                //        var trees = SyllabusService.ListToTree(newNodes.ToList());
-                //        if (syllabusTree != null)
-                //        {
-                //            syllabusTree.children.AddRange(trees);
-                //            stree.trees.Add(syllabusTree);
-                //        }
-                //    });
-                //}
-                return Ok(new { tree });
+                
+                return Ok(new { volume,tree= treeNodes });
             }
             catch (Exception ex) {
                 await _dingDing.SendBotMsg($"OS,{_option.Location},teacher/share/view()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
@@ -417,7 +361,6 @@ namespace TEAMModelOS.Controllers
         public async Task<IActionResult> Qrcode(Favorite request)
         {
             return Ok();
-
         }
     }
    

+ 103 - 267
TEAMModelOS/Controllers/Syllabus/SyllabusController.cs

@@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Http;
 using TEAMModelOS.SDK.Models.Cosmos.Common;
 using Azure.Cosmos;
 using TEAMModelOS.Services.Common;
+using Microsoft.Extensions.Options;
 
 namespace TEAMModelOS.Controllers
 {
@@ -28,163 +29,15 @@ namespace TEAMModelOS.Controllers
     public class SyllabusController : ControllerBase
     {
         private readonly AzureCosmosFactory _azureCosmos;
-
-
-        public SyllabusController(AzureCosmosFactory azureCosmos)
+        private readonly Option _option;
+        private readonly DingDing _dingDing;
+        public SyllabusController(AzureCosmosFactory azureCosmos, DingDing dingDing, IOptionsSnapshot<Option> option)
         {
             _azureCosmos = azureCosmos;
+            _dingDing = dingDing;
+            _option = option?.Value;
         }
 
-        /*
-        {
-    "id": "0baf00db-0768-4b62-a8f7-280f6bcebf71",
-    "scope": "school",
-    "trees": [
-        {
-            "children": [
-                {
-                    "children": [],
-                    "id": "AC4BA269-541B-4DFC-92A5-D773068A6439",
-                    "pid": "2dfcc62e-8eea-9881-dc79-384b2f0afbec",
-                    "order": 0,
-                    "rnodes": [
-                        {
-                            "type": "doc",
-                            "id": "a2bee388-5584-72cc-1d9a-d8a77d255364",
-                            "code": "hbcn",
-                            "scope": "private",
-                            "cntr": "1595321354",
-                            "link": [
-                                "https://teammodelstorage.blob.core.chinacloudapi.cn/1595321354/doc/2020智慧課堂與智慧教研.pptx"
-                            ],
-                            "title": "2020智慧課堂與智慧教研.pptx"
-                        }
-                    ],
-                    "cids": [],
-                    "creatorId": null,
-                    "updateTime": 0,
-                    "title": "1-1 新冠疫情小贴士"
-                }
-            ],
-            "id": "2dfcc62e-8eea-9881-dc79-384b2f0afbec",
-            "pid": "0baf00db-0768-4b62-a8f7-280f6bcebf71",
-            "order": 0,
-            "rnodes": [
-                {
-                    "type": "doc",
-                    "id": "cf1b4d21-66e1-e6c7-c944-30a03e419fa6",
-                    "code": "hbcn",
-                    "scope": "school",
-                    "cntr": "hbcn",
-                    "link": [
-                        "https://teammodelstorage.blob.core.chinacloudapi.cn/hbcn/syllabus/IES5试卷模板制作说明(终).pdf"
-                    ],
-                    "title": "IES5试卷模板制作说明(终).pdf"
-                },
-                {
-                    "type": "doc",
-                    "id": "f3e82595-7340-a5fe-1004-04538ca09b86",
-                    "code": "hbcn",
-                    "scope": "school",
-                    "cntr": "hbcn",
-                    "link": [
-                        "https://teammodelstorage.blob.core.chinacloudapi.cn/hbcn/syllabus/111.pdf"
-                    ],
-                    "title": "111.pdf"
-                },
-                {
-                    "type": "doc",
-                    "id": "e1b31639-dad9-9efb-020b-159dd045f238",
-                    "code": "hbcn",
-                    "scope": "school",
-                    "cntr": "hbcn",
-                    "link": [
-                        "https://teammodelstorage.blob.core.chinacloudapi.cn/hbcn/doc/6789.pdf"
-                    ],
-                    "title": "6789.pdf"
-                },
-                {
-                    "type": "doc",
-                    "id": "aaeb4b5c-0450-cb4d-a1ac-244f3d115c4a",
-                    "code": "hbcn",
-                    "scope": "private",
-                    "cntr": "1595321354",
-                    "link": [
-                        "https://teammodelstorage.blob.core.chinacloudapi.cn/1595321354/doc/422北京培新活动邀请函.pdf"
-                    ],
-                    "title": "422北京培新活动邀请函.pdf"
-                },
-                {
-                    "type": "doc",
-                    "id": "c5f5838c-5c2f-5e9b-a734-a473cd2cad2e",
-                    "code": "hbcn",
-                    "scope": "school",
-                    "cntr": "hbcn",
-                    "link": [
-                        "https://teammodelstorage.blob.core.chinacloudapi.cn/hbcn/doc/6666.pdf"
-                    ],
-                    "title": "6666.pdf"
-                },
-                {
-                    "type": "doc",
-                    "id": "4a106c8e-6831-5f4b-4ec3-3c18f778cab2",
-                    "code": "hbcn",
-                    "scope": "school",
-                    "cntr": "hbcn",
-                    "link": [
-                        "https://teammodelstorage.blob.core.chinacloudapi.cn/hbcn/doc/6.pdf"
-                    ],
-                    "title": "6.pdf"
-                }
-            ],
-            "cids": [
-                "AC4BA269-541B-4DFC-92A5-D773068A6439"
-            ],
-            "creatorId": null,
-            "updateTime": 0,
-            "title": "第一单元 新冠疫情防控"
-        },
-        {
-            "children": [
-                {
-                    "children": [],
-                    "id": "FC5132E1-9723-4875-B3B5-D3DC98D194FA",
-                    "pid": "4f12752c-852f-6e90-a3df-1f1f710af23d",
-                    "order": 0,
-                    "rnodes": [],
-                    "cids": [],
-                    "creatorId": null,
-                    "updateTime": 0,
-                    "title": "2-1 全球醍摩豆智慧教育研究院"
-                },
-                {
-                    "children": [],
-                    "id": "3CAD52BE-67B6-4EDB-8EFB-9122960D816A",
-                    "pid": "4f12752c-852f-6e90-a3df-1f1f710af23d",
-                    "order": 1,
-                    "rnodes": [],
-                    "cids": [],
-                    "creatorId": null,
-                    "updateTime": 0,
-                    "title": "2-2 醍摩豆研究院课程体系"
-                }
-            ],
-            "id": "4f12752c-852f-6e90-a3df-1f1f710af23d",
-            "pid": "0baf00db-0768-4b62-a8f7-280f6bcebf71",
-            "order": 1,
-            "rnodes": [],
-            "cids": [
-                "FC5132E1-9723-4875-B3B5-D3DC98D194FA",
-                "3CAD52BE-67B6-4EDB-8EFB-9122960D816A"
-            ],
-            "creatorId": null,
-            "updateTime": 0,
-            "title": "第二单元 醍摩豆手册学习"
-        }
-    ]
-}
-              */
-
         /// <summary>
         /// 批量保存或更新课纲
         /// 
@@ -193,38 +46,96 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("upsert-tree")]
-        public async Task<IActionResult> SaveOrUpdateAsTree(SyllabusTreeNode request)
+        public async Task<IActionResult> SaveOrUpdateAsTree(List<SyllabusTreeNode> request)
         {
-            List<List<SyllabusTree>> tts = new List<List<SyllabusTree>>();
-            foreach (var tree in request.trees) {
-                Syllabus syllabus = new Syllabus();
+            foreach (SyllabusTreeNode syllabusTree in request) {
                 List<Tnode> nodes = new List<Tnode>();
-                SyllabusService.TreeToList(new List<SyllabusTree> { tree }, nodes);
-                syllabus.children = nodes;
-                syllabus.id = !string.IsNullOrEmpty(tree.id) ? tree.id : Guid.NewGuid().ToString();
-                syllabus.code = $"Syllabus-{request.id}";
-                syllabus.pk = "Syllabus";
-                syllabus.ttl = -1;
-                syllabus.volumeId = request.id;
-                if (request.scope == "school")
+                SyllabusService.TreeToList(syllabusTree.trees, nodes);
+                if (!string.IsNullOrEmpty(syllabusTree.id))
                 {
-                    await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").UpsertItemAsync<Syllabus>(syllabus, new Azure.Cosmos.PartitionKey($"Syllabus-{request.id}"));
+                    Syllabus syllabus = null;
+                    if (syllabusTree.scope == "school")
+                    {
+                        try
+                        {
+                            syllabus = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReadItemAsync<Syllabus>(syllabusTree.id, new Azure.Cosmos.PartitionKey($"Syllabus-{syllabusTree.volumeId}"));
+                        }
+                        catch
+                        {
+                        }
+                        if (syllabus == null)
+                        {
+                            syllabus = new Syllabus();
+                            syllabus.id = syllabusTree.id;
+                            syllabus.children = nodes;
+                            syllabus.code = $"Syllabus-{syllabusTree.volumeId}";
+                            syllabus.pk = "Syllabus";
+                            syllabus.ttl = -1;
+                            syllabus.volumeId = syllabusTree.volumeId;
+                            syllabus.scope = syllabusTree.scope;
+                            await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").CreateItemAsync<Syllabus>(syllabus, new Azure.Cosmos.PartitionKey($"Syllabus-{syllabusTree.volumeId}"));
+                        }
+                        else
+                        {
+                            syllabus.children = nodes;
+                            syllabusTree.auth = syllabus.auth;
+                            await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReplaceItemAsync<Syllabus>(syllabus,syllabus.id, new Azure.Cosmos.PartitionKey($"Syllabus-{syllabusTree.volumeId}"));
+                        }
+
+                    }
+                    else {
+                        try
+                        {
+                            syllabus = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<Syllabus>(syllabusTree.id, new Azure.Cosmos.PartitionKey($"Syllabus-{syllabusTree.volumeId}"));
+                        }
+                        catch
+                        {
+                        }
+                        if (syllabus == null)
+                        {
+                            syllabus = new Syllabus();
+                            syllabus.id = syllabusTree.id;
+                            syllabus.children = nodes;
+                            syllabus.code = $"Syllabus-{syllabusTree.volumeId}";
+                            syllabus.pk = "Syllabus";
+                            syllabus.ttl = -1;
+                            syllabus.volumeId = syllabusTree.volumeId;
+                            syllabus.scope = syllabusTree.scope;
+                            await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").CreateItemAsync<Syllabus>(syllabus, new Azure.Cosmos.PartitionKey($"Syllabus-{syllabusTree.volumeId}"));
+                        }
+                        else
+                        {
+                            syllabus.children = nodes;
+                            syllabusTree.auth = syllabus.auth;
+                            await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").UpsertItemAsync<Syllabus>(syllabus, new Azure.Cosmos.PartitionKey($"Syllabus-{syllabusTree.volumeId}"));
+                        }
+                    }
                 }
-                else
-                {
-                    await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").UpsertItemAsync<Syllabus>(syllabus, new Azure.Cosmos.PartitionKey($"Syllabus-{request.id}"));
+                else {
+                    string id = Guid.NewGuid().ToString();
+                    syllabusTree.id = id;
+                    Syllabus syllabus = new Syllabus {
+                        id = id,
+                        code = $"Syllabus-{syllabusTree.volumeId}",
+                        pk = "Syllabus",
+                        ttl = -1,
+                        volumeId=syllabusTree.volumeId,
+                        children=nodes,
+                        scope = syllabusTree.scope
+                    };
+                    if (syllabusTree.scope == "school") {
+                        await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").CreateItemAsync<Syllabus>(syllabus, new Azure.Cosmos.PartitionKey($"Syllabus-{syllabusTree.volumeId}"));
+                    } else {
+                        await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").CreateItemAsync<Syllabus>(syllabus, new Azure.Cosmos.PartitionKey($"Syllabus-{syllabusTree.volumeId}"));
+                    }
                 }
-                List<SyllabusTree> trees = SyllabusService.ListToTree(nodes);
-                tts.Add(trees);
             }
-
-            request.trees = new List<SyllabusTree>() ;
-            tts.ForEach(x => request.trees.AddRange(x));
             return Ok(request);
         }
 
- 
-
+        /*
+            {"code":"册别code:0baf00db-0768-4b62-a8f7-280f6bcebf71","scope":"school"}
+         */
         /// <summary>
         /// 查找课纲 
         /// </summary>
@@ -237,39 +148,41 @@ namespace TEAMModelOS.Controllers
             var client = _azureCosmos.GetCosmosClient();
             if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
             if (!request.TryGetProperty("scope", out JsonElement scope)) return BadRequest();
-            SyllabusTreeNode tree = null;
-            string vcode = null;
-            List<List<SyllabusTree>> tts = new List<List<SyllabusTree>>();
+           
+            List<SyllabusTreeNode> treeNodes = new List<SyllabusTreeNode>();
             try {
                 if (scope.ToString().Equals("school"))
                 {
                     await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<Syllabus>(queryText: $"select value(c) from c ",
                     requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Syllabus-{code}") }))
                     {
-                        vcode = item.volumeId;
                         List<SyllabusTree> trees = SyllabusService.ListToTree(item.children);
-                        tts.Add(trees);
+                        SyllabusTreeNode tree = new SyllabusTreeNode() { id = item.id, scope =item.scope, trees = trees ,volumeId=item.volumeId,auth=item.auth};
+                        treeNodes.Add(tree);
                     }
-                    tree = new SyllabusTreeNode() { id = vcode,  scope = "school", trees= new List<SyllabusTree>() };
-                    tts.ForEach(x => tree.trees.AddRange(x));
+                    
                 }
                 else
                 {
                     await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIterator<Syllabus>(queryText: $"select value(c) from c ",
-                    requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Syllabus-{code}") }))
+                     requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Syllabus-{code}") }))
                     {
-                        vcode = item.volumeId;
                         List<SyllabusTree> trees = SyllabusService.ListToTree(item.children);
-                        tts.Add(trees);
+                        SyllabusTreeNode tree = new SyllabusTreeNode() { id = item.id, scope = item.scope, trees = trees, volumeId = item.volumeId, auth = item.auth };
+                        treeNodes.Add(tree);
                     }
-                    tree = new SyllabusTreeNode() { id = vcode, scope = "private", trees = new List<SyllabusTree>() };
-                    tts.ForEach(x => tree.trees.AddRange(x)); 
                 }
-                return Ok(new { tree });
+                return Ok(new { tree= treeNodes });
             } catch (Exception ex) {
-                return Ok(new { tree});
+                await _dingDing.SendBotMsg($"OS,{_option.Location},common/syllabus/find-id\n{ex.Message}{ex.StackTrace}", GroupNames.成都开发測試群組);
+                return Ok(new { tree= treeNodes });
             }
         }
+
+        /*
+            {"id":"章节id","code":"册别id","scope":"school/private"}
+         */
+
         /// <summary>
         /// 删除章节
         /// </summary>
@@ -294,82 +207,5 @@ namespace TEAMModelOS.Controllers
                 return Ok(new { code = response.Status });
             }
         }
-
-
-     
-
-        private async Task<List<Tnode>> FindByPid(Tnode data, List<Tnode> nodes, List<Tnode> nodedata)
-        {
-            foreach (Tnode syllabus in nodedata)
-            {
-                if (syllabus.pid == data.id)
-                {
-                    nodes.Add(syllabus);
-                    await FindByPid(syllabus, nodes, nodedata);
-                }
-            }
-            return nodes;
-        }
-
-        
-        //private async Task<List<Syllabus>> SaveOrUpdateToNodes(List<Tnode> syllabusNodes)
-        //{
-        //    List<Syllabus> syllabuses;
-
-        //   var  data = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReadItemAsync<Syllabus>(  syllabusNodes[0].code ,new Azure.Cosmos.PartitionKey(""));
-        //    if (data.IsEmpty())
-        //    {
-        //        var syllabus = new Syllabus { id = syllabusNodes[0].code, code = syllabusNodes[0].code, children = new List<SyllabusNode>() };
-        //        await _azureCosmos.SaveOrUpdate(syllabus);
-        //        data = new List<Syllabus>() { syllabus };
-        //    }
-        //    List<SyllabusNode> syllabusNodes1 = new List<SyllabusNode>();
-        //    if (data.IsNotEmpty())
-        //    {
-        //        syllabusNodes1.AddRange(data[0].children);
-        //        //replace
-        //        for (int i = 0; i < data[0].children.Count; i++)
-        //        {
-        //            for (int j = 0; j < syllabusNodes.Count; j++)
-        //            {
-        //                if (data[0].children[i].id == syllabusNodes[j].id && data[0].children[i].code == syllabusNodes[j].code)
-        //                {
-        //                    syllabusNodes1.Remove(data[0].children[i]);
-        //                    syllabusNodes1.Add(syllabusNodes[j]);
-        //                    syllabusNodes.Remove(syllabusNodes[j]);
-        //                }
-        //            }
-        //        }
-        //        data[0].children = syllabusNodes1;
-
-        //        //新增
-        //        data[0].children.AddRange(syllabusNodes);
-
-        //        if (data[0].children.IsNotEmpty())
-        //        {
-        //            var len = data[0].children.Count;
-        //            for (int i = 0; i < len; i++)
-        //            {
-        //                if (data[0].children[i].items.IsNotEmpty())
-        //                {
-        //                    data[0].children[i].item = true;
-        //                }
-        //                if (data[0].children[i].resources.IsNotEmpty())
-        //                {
-        //                    data[0].children[i].resource = true;
-        //                }
-        //            }
-        //        }
-        //        syllabuses = await _azureCosmos.SaveOrUpdateAll<Syllabus>(data);
-
-        //    }
-        //    else
-        //    {
-        //        throw new BizException("保存失败", ResponseCode.FAILED);
-
-        //    }
-
-        //    return syllabuses;
-        //}
     }
 }

+ 0 - 43
TEAMModelOS/Controllers/Syllabus/VolumeController.cs

@@ -107,54 +107,15 @@ namespace TEAMModelOS.Controllers
                 //私有课纲
                 if (scope.GetString()== "private")
                 {
-                    //await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").GetItemQueryStreamIterator(queryText: $"select value(c) from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{code}") }))
-                    //{
-                    //    using var json = await JsonDocument.ParseAsync(item.ContentStream);
-                    //    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
-                    //    {
-                    //        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
-                    //        {
-                    //            syllabuses.Add(obj.ToObject<Syllabus>());
-                    //        }
-                    //    }
-                    //}
                     await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").GetItemQueryIterator<Volume>(queryText: $"select value(c) from c where c.status = {status}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Volume-{code}") }))
                     {
                         volumes.Add(item);
-                        //using var json = await JsonDocument.ParseAsync(item.ContentStream);
-                        //if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
-                        //{
-                        //    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
-                        //    {
-                        //        volumes.Add(obj.ToObject<Volume>());
-                        //    }
-                        //}
                     }
                 }
                 else if(scope.GetString() == "school") {
-                    //await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: $"select value(c) from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{code}") }))
-                    //{
-                    //    using var json = await JsonDocument.ParseAsync(item.ContentStream);
-                    //    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
-                    //    {
-                    //        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
-                    //        {
-                    //            syllabuses.Add(obj.ToObject<Syllabus>());
-                    //        }
-                    //    }
-                    //}
                     await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").GetItemQueryIterator<Volume>(queryText: $"select value(c) from c where c.status = {status} and c.periodId = '{periodCode}' and c.subjectId = '{subjectCode}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Volume-{code}") }))
                     {
                         volumes.Add(item);
-
-                        //using var json = await JsonDocument.ParseAsync(item.ContentStream);
-                        //if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
-                        //{
-                        //    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
-                        //    {
-                        //        volumes.Add(obj.ToObject<Volume>());
-                        //    }
-                        //}
                     }
                 }
              }
@@ -217,13 +178,9 @@ namespace TEAMModelOS.Controllers
                 try {
                     if (request.scope.Equals("school"))
                     {
-                      //  Volume volume = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReadItemAsync<Volume>(request.id, new PartitionKey(request.code));
-                        //保留授权
                         await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReplaceItemAsync<Volume>(request, request.id, new Azure.Cosmos.PartitionKey(request.code));
                     }
                     else if (request.scope.Equals("private")) {
-                       // Volume volume = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<Volume>(request.id, new PartitionKey(request.code));
-                        //保留授权
                         await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Volume>(request, request.id, new Azure.Cosmos.PartitionKey(request.code));
                     }