Bläddra i källkod

Merge branch 'TPE/feat/classroomManage' into TPE/develop

osbert 4 år sedan
förälder
incheckning
712fe1944e
91 ändrade filer med 4816 tillägg och 1366 borttagningar
  1. 3 2
      TEAMModelFunction/MonitorCosmosDB.cs
  2. 3 3
      TEAMModelFunction/MonitorServicesBus.cs
  3. 8 0
      TEAMModelFunction/Properties/launchSettings.json
  4. 40 0
      TEAMModelFunction/ServiceBusTopic.cs
  5. 15 2
      TEAMModelFunction/Startup.cs
  6. 7 0
      TEAMModelFunction/TEAMModelFunction.csproj
  7. 11 0
      TEAMModelFunction/local.settings.json
  8. 12 3
      TEAMModelOS.SDK/DI/AzureCosmos/AzureCosmosFactory.cs
  9. 3 3
      TEAMModelOS.SDK/DI/AzureCosmos/AzureCosmosFactoryExtensions.cs
  10. 3 1
      TEAMModelOS.SDK/DI/AzureCosmos/AzureCosmosFactoryOptions.cs
  11. 3 0
      TEAMModelOS/ClientApp/src/App.vue
  12. 1 7
      TEAMModelOS/ClientApp/src/api/learnActivity.js
  13. 6 0
      TEAMModelOS/ClientApp/src/api/schoolSetting.js
  14. 6 1
      TEAMModelOS/ClientApp/src/common/BaseExamList.vue
  15. 6 3
      TEAMModelOS/ClientApp/src/common/BaseLayout.vue
  16. 5 1
      TEAMModelOS/ClientApp/src/common/BaseSelectSchool.vue
  17. 113 34
      TEAMModelOS/ClientApp/src/common/BaseUserPoptip.vue
  18. 127 97
      TEAMModelOS/ClientApp/src/components/learnactivity/NewChooseContent.vue
  19. 3 3
      TEAMModelOS/ClientApp/src/components/learnactivity/QuestionList.less
  20. 13 9
      TEAMModelOS/ClientApp/src/components/learnactivity/QuestionList.vue
  21. 8 1
      TEAMModelOS/ClientApp/src/css/common-style.less
  22. 12 0
      TEAMModelOS/ClientApp/src/icons/svg/hiteach_pro.svg
  23. 12 0
      TEAMModelOS/ClientApp/src/icons/svg/hiteach_std.svg
  24. 12 0
      TEAMModelOS/ClientApp/src/icons/svg/hiteach_tbl.svg
  25. 1 0
      TEAMModelOS/ClientApp/src/icons/svg/htc.svg
  26. 1 0
      TEAMModelOS/ClientApp/src/icons/svg/htc_pro.svg
  27. 1 0
      TEAMModelOS/ClientApp/src/icons/svg/htc_std.svg
  28. 1 0
      TEAMModelOS/ClientApp/src/icons/svg/htc_tbl.svg
  29. 10 0
      TEAMModelOS/ClientApp/src/icons/svg/tmd.svg
  30. 216 0
      TEAMModelOS/ClientApp/src/mock/serviceDriveAuth.js
  31. 7 0
      TEAMModelOS/ClientApp/src/router/routes.js
  32. 7 2
      TEAMModelOS/ClientApp/src/store/index.js
  33. 2 0
      TEAMModelOS/ClientApp/src/store/module/schoolBaseInfo.js
  34. 101 0
      TEAMModelOS/ClientApp/src/store/module/serviceDriveAuth.js
  35. 21 16
      TEAMModelOS/ClientApp/src/utils/evTools.js
  36. 0 12
      TEAMModelOS/ClientApp/src/utils/js-fn.js
  37. 1 1
      TEAMModelOS/ClientApp/src/utils/public.js
  38. 16 1
      TEAMModelOS/ClientApp/src/view/Home.vue
  39. 8 7
      TEAMModelOS/ClientApp/src/view/classmgt/ManageClass.vue
  40. 55 16
      TEAMModelOS/ClientApp/src/view/classrecord/ClassRecord.vue
  41. 23 2
      TEAMModelOS/ClientApp/src/view/evaluation/bank/ExerciseList.vue
  42. 66 14
      TEAMModelOS/ClientApp/src/view/evaluation/bank/TestPaperList.vue
  43. 99 101
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseExerciseList.vue
  44. 3 3
      TEAMModelOS/ClientApp/src/view/evaluation/index/CreatePaper.less
  45. 52 10
      TEAMModelOS/ClientApp/src/view/evaluation/index/CreatePaper.vue
  46. 5 4
      TEAMModelOS/ClientApp/src/view/evaluation/index/PickExercise.css
  47. 6 5
      TEAMModelOS/ClientApp/src/view/evaluation/index/TestPaper.less
  48. 13 36
      TEAMModelOS/ClientApp/src/view/evaluation/index/TestPaper.vue
  49. 23 15
      TEAMModelOS/ClientApp/src/view/homepage/HomePage.vue
  50. 21 17
      TEAMModelOS/ClientApp/src/view/learnactivity/AutoCreate.vue
  51. 10 0
      TEAMModelOS/ClientApp/src/view/learnactivity/CreateEvaluation.less
  52. 112 81
      TEAMModelOS/ClientApp/src/view/learnactivity/CreateEvaluation.vue
  53. 0 1
      TEAMModelOS/ClientApp/src/view/learnactivity/ExamPaperAnalysis.vue
  54. 3 3
      TEAMModelOS/ClientApp/src/view/learnactivity/ManualCreate.vue
  55. 42 11
      TEAMModelOS/ClientApp/src/view/learnactivity/MultiCascader.vue
  56. 1 0
      TEAMModelOS/ClientApp/src/view/login/Index.vue
  57. 3 0
      TEAMModelOS/ClientApp/src/view/newcourse/MyCourse.less
  58. 70 13
      TEAMModelOS/ClientApp/src/view/newcourse/MyCourse.vue
  59. 90 46
      TEAMModelOS/ClientApp/src/view/schoolmgmt/ClassroomSetting/ClassroomSetting.less
  60. 491 49
      TEAMModelOS/ClientApp/src/view/schoolmgmt/ClassroomSetting/ClassroomSetting.vue
  61. 3 0
      TEAMModelOS/ClientApp/src/view/schoolmgmt/SystemSetting/SystemSetting.vue
  62. 30 0
      TEAMModelOS/ClientApp/src/view/serviceDriveAuth/Index.less
  63. 71 0
      TEAMModelOS/ClientApp/src/view/serviceDriveAuth/Index.vue
  64. 114 0
      TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/AclassOneChart.vue
  65. 235 0
      TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/HiteachAuthList.less
  66. 282 0
      TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/HiteachAuthList.vue
  67. 35 0
      TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/ServiceList.css
  68. 182 0
      TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/ServiceList.less
  69. 208 0
      TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/ServiceList.vue
  70. 224 0
      TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/SpaceChart.vue
  71. 142 0
      TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/SpaceStatus.less
  72. 158 0
      TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/SpaceStatus.vue
  73. 77 0
      TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/Spacebar.less
  74. 101 0
      TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/Spacebar.vue
  75. 86 0
      TEAMModelOS/ClientApp/src/view/serviceDriveAuth/serviceDriveAuthIview.less
  76. 13 0
      TEAMModelOS/ClientApp/src/view/settings/SchoolMgmt.less
  77. 55 26
      TEAMModelOS/ClientApp/src/view/settings/SchoolMgmt.vue
  78. 1 0
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/AchievementAnalysis/AchievementAnalysis.css
  79. 11 2
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/AchievementAnalysis/AchievementAnalysis.vue
  80. 691 631
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/EvaluationList/TotalIndex.vue
  81. 1 1
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/TestAnalysis/QuestionList.css
  82. 3 2
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/index.css
  83. 16 2
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/index.vue
  84. 4 4
      TEAMModelOS/ClientApp/src/view/teachcontent/index.vue
  85. 12 12
      TEAMModelOS/Controllers/Client/HiTeachController.cs
  86. 1 1
      TEAMModelOS/Controllers/School/ClassRoomController.cs
  87. 2 2
      TEAMModelOS/Controllers/School/SchoolController.cs
  88. 40 40
      TEAMModelOS/Controllers/Teacher/InitController.cs
  89. 1 1
      TEAMModelOS/Startup.cs
  90. 3 5
      TEAMModelOS/appsettings.Development.json
  91. 1 1
      TEAMModelOS/appsettings.json

+ 3 - 2
TEAMModelFunction/MonitorCosmosDB.cs

@@ -24,12 +24,13 @@ namespace TEAMModelFunction
             collectionName: "School",
             ConnectionStringSetting = "CosmosConnection",
             LeaseCollectionName = "leases")]IReadOnlyList<Document> input, ILogger log)
-        {           
+        {          
                 if (input != null && input.Count > 0)
                 {
                     log.LogInformation("Documents modified " + input.Count);
                     log.LogInformation("First document Id " + input[0].Id);
-                }           
+                }
+            //_clientFactory.CreateClient().
         }
     }
 }

+ 3 - 3
TEAMModelFunction/MonitorServicesBus.cs

@@ -7,11 +7,11 @@ namespace TEAMModelFunction
 {
     public static class MonitorServicesBus
     {
-        [FunctionName("test_queue_activetask")]
-        public static void Run([ServiceBusTrigger("test_queue_activetask", Connection = "ServiceBusConnection")]string myQueueItem, ILogger log)
+/*        [FunctionName("test_queue_activetask")]
+        public static void Run([ServiceBusTrigger("test_topic_ActiveTask", Connection = "ServiceBusConnection")]string myQueueItem, ILogger log)
         {
             ///ÖØÊÔ´ÎÊý
             log.LogInformation($"C# ServiceBus queue trigger function processed message: {myQueueItem}");
-        }
+        }*/
     }
 }

+ 8 - 0
TEAMModelFunction/Properties/launchSettings.json

@@ -0,0 +1,8 @@
+{
+  "profiles": {
+    "TEAMModelFunction": {
+      "commandName": "Project",
+      "nativeDebugging": false
+    }
+  }
+}

+ 40 - 0
TEAMModelFunction/ServiceBusTopic.cs

@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Azure.Cosmos;
+using Microsoft.Azure.WebJobs;
+using Microsoft.Azure.WebJobs.Host;
+using Microsoft.Extensions.Logging;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Extension;
+
+namespace TEAMModelFunction
+{
+    public   class ServiceBusTopic
+    {
+        private readonly AzureCosmosFactory _azureCosmos;
+        public ServiceBusTopic(  AzureCosmosFactory azureCosmos)
+        {
+            _azureCosmos = azureCosmos;
+        }
+        [FunctionName("ServiceBusTopic")]
+        public async Task Run([ServiceBusTrigger("test_topic_ActiveTask", "test_topic_ReciveTask", Connection = "ConnectionBusName")]string mySbMsg, ILogger log)
+        {
+            log.LogInformation($"C# ServiceBus topic trigger function processed message: {mySbMsg}");
+            var client = _azureCosmos.GetCosmosClient();
+            List<object> classes = new List<object>();
+            await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: $"select value(c) from c",requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-hbcn") }))
+            {
+                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())
+                    {
+                        classes.Add(obj.ToObject<object>());
+                    }
+                }
+            }
+        }
+    }
+}

+ 15 - 2
TEAMModelFunction/Startup.cs

@@ -1,19 +1,32 @@
 using Microsoft.Azure.Functions.Extensions.DependencyInjection;
+using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using System;
 using System.Collections.Generic;
+using System.Configuration;
 using System.IO;
 using System.Reflection;
 using System.Text;
+using TEAMModelOS.SDK.DI;
 
 [assembly: FunctionsStartup(typeof(TEAMModelFunction.Startup))]
 namespace TEAMModelFunction
 {
     public class Startup : FunctionsStartup
-    {
+    { 
         public override void Configure(IFunctionsHostBuilder builder)
         {
-            builder.Services.AddHttpClient();            
+            var ConnectionString = System.Environment.GetEnvironmentVariable("CosmosConnection");
+            var ScanModel = System.Environment.GetEnvironmentVariable("ScanModel");
+            var Database = System.Environment.GetEnvironmentVariable("Database");
+            builder.Services.AddHttpClient();
+            builder.Services.AddAzureCosmos(new AzureCosmosFactoryOptions 
+            { 
+                ConnectionString= ConnectionString,
+                Name= "Default",
+                ScanModel= ScanModel.Split(","),
+                Database = Database.Split(","),
+            });
         }
     }
 }

+ 7 - 0
TEAMModelFunction/TEAMModelFunction.csproj

@@ -3,16 +3,23 @@
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <AzureFunctionsVersion>v3</AzureFunctionsVersion>
   </PropertyGroup>
+  <ItemGroup>
+    <Compile Remove="AServiceBus.cs" />
+  </ItemGroup>
   <ItemGroup>
     <PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.0.0" />
     <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.CosmosDB" Version="3.0.7" />
     <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.ServiceBus" Version="4.1.2" />
     <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.6.0" />
     <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.7" />
+    <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="3.1.6" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\TEAMModelOS.SDK\TEAMModelOS.SDK.csproj" />
   </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\TEAMModelOS\TEAMModelOS.csproj" />
+  </ItemGroup>
   <ItemGroup>
     <None Update="host.json">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

+ 11 - 0
TEAMModelFunction/local.settings.json

@@ -0,0 +1,11 @@
+{
+  "IsEncrypted": false,
+  "Values": {
+    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
+    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
+    "ConnectionBusName": "Endpoint=sb://teammodelos.servicebus.chinacloudapi.cn/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=Sy4h4EQ8zP+7w/lOLi1X3tGord/7ShFHimHs1vC50Dc=",
+    "CosmosConnection": "AccountEndpoint=https://teammodelos.documents.azure.cn:443/;AccountKey=clF73GwPECfP1lKZTCvs8gLMMyCZig1HODFbhDUsarsAURO7TcOjVz6ZFfPqr1HzYrfjCXpMuVD5TlEG5bFGGg==;",
+    "ScanModel": "TEAMModelOS" ,
+    "Database":  "TEAMModelOS" 
+  }  
+}

+ 12 - 3
TEAMModelOS.SDK/DI/AzureCosmos/AzureCosmosFactory.cs

@@ -59,7 +59,7 @@ namespace TEAMModelOS.SDK.DI
         {
             try
             {
-                var cm = CosmosClients.GetOrAdd(name, x => new CosmosClient(_optionsMonitor.Get(name).CosmosConnectionString, new CosmosClientOptions() { ApplicationRegion = region }));
+                var cm = CosmosClients.GetOrAdd(name, x => new CosmosClient(_optionsMonitor.Get(name).ConnectionString, new CosmosClientOptions() { ApplicationRegion = region }));
                 return cm;
             }
             catch (Exception e)
@@ -94,7 +94,10 @@ namespace TEAMModelOS.SDK.DI
         /// <returns></returns>
         public async Task InitializeDatabase()
         {
-            string[] DatabaseIds = BaseConfigModel.Configuration.GetSection("Azure:Cosmos:Database").Get<string[]>();
+            //    string[] DatabaseIds = BaseConfigModel.Configuration.GetSection("Azure:Cosmos:Database").Get<string[]>();
+            string[] DatabaseIds =_optionsMonitor.Get("Default").Database;
+
+            
             bool isMonitor = false;
             string leases = "leases";
             if (DatabaseIds != null)
@@ -110,7 +113,13 @@ namespace TEAMModelOS.SDK.DI
                 }
             }
             //获取数据库所有的表
-            List<Type> types = ReflectorExtensions.GetAllTypeAsAttribute<CosmosDBAttribute>(BaseConfigModel.Configuration.GetSection("Azure:Cosmos:ScanModel").Get<string[]>() );
+            // List<Type> types = ReflectorExtensions.GetAllTypeAsAttribute<CosmosDBAttribute>(BaseConfigModel.Configuration.GetSection("Azure:Cosmos:ScanModel").Get<string[]>() );
+            List<Type> types = new List<Type>() ;
+            try {
+                types = ReflectorExtensions.GetAllTypeAsAttribute<CosmosDBAttribute>(_optionsMonitor.Get("Default").ScanModel);
+            } catch(Exception e) {
+                Console.WriteLine(e.StackTrace);
+            }
             foreach (Type type in types)
             {
                 string PartitionKey = AzureCosmosUtil.GetPartitionKey(type);

+ 3 - 3
TEAMModelOS.SDK/DI/AzureCosmos/AzureCosmosFactoryExtensions.cs

@@ -8,12 +8,12 @@ namespace TEAMModelOS.SDK.DI
 {
     public static class AzureCosmosFactoryExtensions
     {
-        public static IServiceCollection AddAzureCosmos(this IServiceCollection services, string connectionString, string name = "Default")
+        public static IServiceCollection AddAzureCosmos(this IServiceCollection services, AzureCosmosFactoryOptions options, string name = "Default")
         {
             if (services == null) throw new ArgumentNullException(nameof(services));            
-            if (connectionString == null) throw new ArgumentNullException(nameof(connectionString));
+            if (options == null) throw new ArgumentNullException(nameof(options));
             services.TryAddSingleton<AzureCosmosFactory>();
-            services.Configure<AzureCosmosFactoryOptions>(name, o => { o.Name = name; o.CosmosConnectionString = connectionString; });
+            services.Configure<AzureCosmosFactoryOptions>(name, o => { o.Name = name; o.ConnectionString = options.ConnectionString;o.ScanModel = options.ScanModel; o.Database=options.Database ; });
             return services;
         }
     }

+ 3 - 1
TEAMModelOS.SDK/DI/AzureCosmos/AzureCosmosFactoryOptions.cs

@@ -7,7 +7,9 @@ namespace TEAMModelOS.SDK.DI
     public class AzureCosmosFactoryOptions
     {
         public string Name { get; set; }
-        public string CosmosConnectionString { get; set; }
+        public string ConnectionString { get; set; }
+        public string[] Database { get; set; }
+        public string[] ScanModel { get; set; }
     }
 
    

+ 3 - 0
TEAMModelOS/ClientApp/src/App.vue

@@ -47,6 +47,9 @@
         mounted() {
             window.addEventListener('vuexoidc:userLoaded', this.userLoaded)
             window.addEventListener('vuexoidc:oidcError', this.oidcError)
+        },
+        created() {
+            
         },
         destroyed() {
             window.removeEventListener('vuexoidc:userLoaded', this.userLoaded)

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

@@ -58,7 +58,7 @@ export default {
      *删除最小单元
      */
     DeleteUnit: function (data) {
-        return post('/api/Learn/DeleteUnit', data)
+        return post('/teacher/learn/delete-unit', data)
     },
     /*
     *根据id数组查询资源文件
@@ -193,12 +193,6 @@ export default {
         return post('/api/Learn/DeleteProcess', data)
     },
     /*
-    *删除最小单元
-    */
-    DeleteUnit: function (data) {
-        return post('/api/Learn/DeleteUnit', data)
-    },
-    /*
     * 新增或者修改投票活动
     */
     SaveorUpdataVote: function (data) {

+ 6 - 0
TEAMModelOS/ClientApp/src/api/schoolSetting.js

@@ -9,6 +9,12 @@ export default {
     findClassInfo: function (data) {
         return post('/school/classroom/find', data)
     },
+    findSchoolBase: function(schoolCode) {
+        let format = {
+            "school_code": schoolCode
+        }
+        return post('/school/init/get-school-base', format)
+    },
     //更新或新增教室
     classroomSettingSaveOrUpdate: function (data) {
         return post('/school/classroom/upsert', data)

+ 6 - 1
TEAMModelOS/ClientApp/src/common/BaseExamList.vue

@@ -80,7 +80,12 @@
 
             // 查看更多评测列表
             goEvaluationList() {
-                this.$router.push('/totalIndex')
+				this.$router.push({
+					name: 'totalIndex',
+					params: {
+						tabName: 'tab2'
+					}
+				})
             }
         },
         mounted() {

+ 6 - 3
TEAMModelOS/ClientApp/src/common/BaseLayout.vue

@@ -99,7 +99,7 @@
                                 icon: 'iconfont icon-class-mgt',
                                 name: '班级管理',
                                 router: '/home/classroom',
-                                tag: '',
+                                tag: '*',
                                 role: 'admin',
                                 permission: 'classroom-upd|classroom-read'
                             },
@@ -178,8 +178,8 @@
                             {
                                 icon: 'iconfont icon-auth',
                                 name: '授权管理',
-                                router: '/404',
-                                tag: '*',
+                                router: '/home/serviceDriveAuth',
+                                tag: '(预览)',
                                 role: 'admin',
                                 permission: ''
                             }
@@ -383,6 +383,9 @@
                 return ["menu-item", this.isCollapsed ? "collapsed-menu" : ""]
             }
         },
+        create() {
+            let local = localStorage.getItem
+        },
         watch: {
             $route: {
                 handler(val, oldval) {

+ 5 - 1
TEAMModelOS/ClientApp/src/common/BaseSelectSchool.vue

@@ -26,7 +26,9 @@
 	export default {
 		data() {
 			return {
-				curSchool: null,
+				curSchool: {
+					logo:''
+				},
 				user:{
 					schools:[]
 				}
@@ -35,12 +37,14 @@
 		created() {
 			// 获取本地存储中的 用户信息
 			let user = JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8"));
+			console.log(user)
 			if(user.schools){
 				// 拿到用户管理的schools 必须是已加入的学校
 				let schools = user.schools.filter(i => i.status === 'join')
 				if(schools.length){
 					this.user.schools = schools
 					this.curSchool = user.defaultschool ? schools.filter(i => i.schoolId === user.defaultschool)[0] : schools[0]
+					this.$store.commit('setSchoolCode', this.curSchool.schoolId)
 				}
 			}else{
 				this.$Message.warning('用户暂无学校列表数据')

+ 113 - 34
TEAMModelOS/ClientApp/src/common/BaseUserPoptip.vue

@@ -44,18 +44,25 @@
 						</div>
 					</div>
 					<div class="user-storage">
-						<p>个人空间状态:( {{ getSizeVal(userInfo.storage.total) }} / {{ getSizeVal(userInfo.storage.max) }} )</p>
-						<div class="user-storage-distribution">
-							<span class="user-storage-distribution-green" :style='{ width: getPercent(userInfo.storage.file) }'></span>
-							<span class="user-storage-distribution-orange" :style='{ width: getPercent(userInfo.storage.video) }'></span>
-							<span class="user-storage-distribution-red" :style='{ width: getPercent(userInfo.storage.movie) }'></span>
-							<span class="user-storage-distribution-blue" :style='{ width: getPercent(userInfo.storage.other) }'></span>
-						</div>
+						<p>个人空间状态:( {{ getSizeVal(sizeInfo.total) }} / {{ getSizeVal($GLOBAL.PRIVATE_SPACE) }} )</p>
+                        <div class="user-storage-distribution">
+                            <span class="percent-item-span storage-full" :style="{ width: '100%' }" v-if="sizeInfo.total > $GLOBAL.PRIVATE_SPACE"></span>
+                            <span class="storage-res" :style='{ width: getPercent(sizeInfo.res) }'></span>
+                            <span class="storage-image" :style='{ width: getPercent(sizeInfo.image) }'></span>
+                            <span class="storage-video" :style='{ width: getPercent(sizeInfo.video) }'></span>
+                            <span class="storage-audio" :style='{ width: getPercent(sizeInfo.audio) }'></span>
+                            <span class="storage-doc" :style='{ width: getPercent(sizeInfo.doc) }'></span>
+                            <span class="storage-other" :style='{ width: getPercent(sizeInfo.other) }'></span>
+                            <span class="storage-data" :style='{ width: getPercent(sizeInfo.data) }'></span>
+                        </div>
 						<p class="user-storage-text">
-							<span style="color:#00D523">文件内容:{{ getSizeVal(userInfo.storage.file) }}</span>
-							<span style="color:#e87b22">影像内容:{{ getSizeVal(userInfo.storage.video) }}</span>
-							<span style="color:#ff40bc">影片内容:{{ getSizeVal(userInfo.storage.movie) }}</span>
-							<span style="color:#1fccd5">其他内容:{{ getSizeVal(userInfo.storage.other) }}</span>
+							<span v-if="sizeInfo.res != 0" style="color:#00D523">教材:{{ getSizeVal(sizeInfo.res) }}</span>
+							<span v-if="sizeInfo.image != 0" style="color:#e87b22">图片:{{ getSizeVal(sizeInfo.image) }}</span>
+							<span v-if="sizeInfo.video != 0" style="color:#ff40bc">视频:{{ getSizeVal(sizeInfo.video) }}</span>
+							<span v-if="sizeInfo.audio != 0" style="color:#1fccd5">音频:{{ getSizeVal(sizeInfo.audio) }}</span>
+							<span v-if="sizeInfo.doc != 0" style="color:#1fccd5">文档:{{ getSizeVal(sizeInfo.doc) }}</span>
+							<span v-if="sizeInfo.data != 0" style="color:#1fccd5">题库:{{ getSizeVal(sizeInfo.data) }}</span>
+							<span v-if="sizeInfo.other != 0" style="color:#1fccd5">其他内容:{{ getSizeVal(sizeInfo.other) }}</span>
 						</p>
 					</div>
 					<div class="btn-logout" @click="onQuit">登出</div>
@@ -66,6 +73,7 @@
 </template>
 
 <script>
+    import BlobTool from '@/utils/blobTool.js';
 	import PersonalPhoto from '@/components/public/personalPhoto/Index.vue'
 	export default {
 		data() {
@@ -78,33 +86,70 @@
 					courseNum: 7,
 					activityNum: 16,
 					classNum: 21,
-					storage: {
-						max: 1073741824,
-						total: 456130560,
-						file: 105906176,
-						video: 78643200,
-						movie: 238026752,
-						other: 239845888
-					},
 					nameColor: '',
 				},
-				user: {}
+				user: {},
+                sizeInfo: {
+                    total: 0,   //所有
+                    image: 0,   //内容模块图片
+                    res: 0,     //内容模块教材
+                    video: 0,   //内容模块视频
+                    audio: 0,   //内容模块音频
+                    doc: 0,     //内容模块文档
+                    other: 0,   //内容模块其他文件
+                    data: 0      //除了内容模块之外的文件
+                }
 			}
 		},
 		created() {
 			this.user = JSON.parse(decodeURIComponent(localStorage.userInfo, "utf-8"));
-			this.$store.commit('setUserInfo',{
-				TEAMModelId: this.user.id,
-				name:this.user.name,
-				schoolCode: this.user.defaultschool
-			})
+            let user_profile = JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8"));
+   //         if (user_profile.schools.length) {
+			//	this.$store.commit('setUserInfo', {
+			//		TEAMModelId: this.user.id,
+			//		name: this.user.name,
+			//		schoolCode: user_profile.defaultschool || user_profile.schools[0].schoolId
+			//	})
+			//} else {
+			//	this.$store.commit('setUserInfo', {
+			//		TEAMModelId: this.user.id,
+			//		name: this.user.name,
+			//		schoolCode: 'SYSTEM_NO_SCHOOL'
+			//	})
+			//}
+			
 			if(this.user.roles.length){
 				this.curRole = this.user.roles[0]
 			}
 			this.userInfo.username = this.user.name
 			this.userInfo.nameColor = this.randomColor()
+            this.getSize()
 		},
 		methods: {
+			//获取Blob空间信息
+			async getSize() {
+				let sasRes = await this.$tools.getPrivateSas()
+                let containerClient = new BlobTool(sasRes.url, sasRes.name, sasRes.sas, 'private')
+                containerClient.getSize().then(
+                    res => {
+                        this.sizeInfo = res
+                        console.log(res)
+                    },
+                    err => {
+                        this.$Message.error('空间计算异常')
+                    }
+                )
+			},
+            //计算空间占比
+            getPercent(size) {
+                if (this.sizeInfo.total > this.$GLOBAL.PRIVATE_SPACE) {
+                    return '0%'
+                } else {
+                    let p = (size * 100 / this.$GLOBAL.PRIVATE_SPACE)
+					p = p > 1 ? p.toFixed(2) : p > 0 ? 1 : 0
+                    return p + '%'
+                }
+            },
 			onRoleSelect(val){
 				if(localStorage.getItem('identity') != val){
 					this.curRole = val
@@ -125,13 +170,7 @@
 		},
 
 		computed: {
-			/* 返回内存占比数值 */
-			getPercent() {
-				return val => {
-					let percent = (val / this.userInfo.storage.max).toFixed(2) * 100 + '%'
-					return percent
-				}
-			},
+            
 			getSizeVal() {
 				return val => {
 					return this.$tools.bytesToSize(val)
@@ -148,7 +187,46 @@
 		}
 	}
 </script>
+<style scoped>
+    .user-storage-distribution {
+		overflow:hidden;
+	}
+    .user-storage-text span {
+		margin-right:20px;
+		display:inline-block;
+	}
+    .storage-full {
+        background-color: red;
+    }
+
+    .storage-data {
+        background-color: #F8C006;
+    }
 
+    .storage-image {
+        background-color: #45C84A;
+    }
+
+    .storage-video {
+        background-color: #8E2BDD;
+    }
+
+    .storage-audio {
+        background-color: #E1027B;
+    }
+
+    .storage-doc {
+        background-color: #03C0C2;
+    }
+
+    .storage-res {
+        background-color: #1FFCC5;
+    }
+
+    .storage-other {
+        background-color: #E87B22;
+    }
+</style>
 <style lang="less">
 	.base-user-center {
 		font-family: '微軟正黑體', 'Heiti TC' !important;
@@ -302,9 +380,10 @@
 					margin-top: 10px;
 					font-size: 12px;
 					font-weight: bold;
-					display: flex;
+					white-space:normal;
+					/*display: flex;
 					flex-wrap: wrap;
-					justify-content: space-around;
+					justify-content: space-around;*/
 				}
 			}
 

+ 127 - 97
TEAMModelOS/ClientApp/src/components/learnactivity/NewChooseContent.vue

@@ -66,7 +66,7 @@
                         </div>
                         <div class="content-filter-item">
                             <span class="content-filter-label">类型:</span>
-                            <RadioGroup v-model="contentFilter.fileType" style="display:inline-block;" @on-change="filterContentByType">
+                            <RadioGroup v-model="contentFilter.fileType" style="display:inline-block;" @on-change="getFileList">
                                 <Radio class="radio-width" :key="index" v-for="(item,index) in contentTypeList" :label="item.type">{{item.label}}</Radio>
                             </RadioGroup>
                         </div>
@@ -89,12 +89,14 @@
                             <template slot-scope="{ row, index }" slot="name">
                                 <div>
                                     <div class="file-icon">
-                                        <img v-if="row.extension == 'ppt' || row.extension == 'pptx'" src="../../assets/icon/ppt50.png" width="15" />
-                                        <img v-else-if="row.extension == 'doc' || row.extension == 'docx'" src="../../assets/icon/word50.png" width="15" />
-                                        <img v-else-if="row.extension == 'xls' || row.extension == 'xlsx'" src="../../assets/icon/xls50.png" width="15" />
-                                        <img v-else-if="row.extension == 'pdf'" src="../../assets/icon/pdf50.png" width="15" />
-                                        <img v-else-if="row.type == 'picture'" src="../../assets/icon/icon_img.png" width="15" />
+                                        <img v-if="row.extension == 'PPT' || row.extension == 'PPTX'" src="../../assets/icon/ppt50.png" width="15" />
+                                        <img v-else-if="row.extension == 'DOC' || row.extension == 'DOCX'" src="../../assets/icon/word50.png" width="15" />
+                                        <img v-else-if="row.extension == 'XLS' || row.extension == 'XLSX' || row.extension == 'CSV'" src="../../assets/icon/xls50.png" width="15" />
+                                        <img v-else-if="row.extension == 'PDF'" src="../../assets/icon/pdf50.png" width="15" />
+                                        <img v-else-if="row.extension == 'ZIP' || row.extension == 'RAR'" src="../../assets/icon/zip50.png" width="15" />
+                                        <img v-else-if="row.type == 'image'" src="../../assets/icon/icon_img.png" width="15" />
                                         <img v-else-if="row.type == 'video'" src="../../assets/icon/icon_video.png" width="15" />
+                                        <img v-else-if="row.type == 'res'" src="../../assets/icon/htex.png" width="15" />
                                         <img v-else src="../../assets/icon/prelearn50.png" width="15" />
                                     </div>
                                     <span style="margin-left:8px;vertical-align: text-bottom;">{{row.name}}</span>
@@ -177,9 +179,9 @@
                             <div>
                                 <EmptyData v-if="questionList.length == 0"></EmptyData>
                             </div>
-                            <div class="page-wrap">
+                            <!--<div class="page-wrap">
                                 <Page :current.sync="pageNum" :total="totalNum" :page-size="pageSize" size="small" show-total show-sizer @on-change="getCurrentPageData" />
-                            </div>
+                            </div>-->
                         </div>
                     </vuescroll>
                 </div>
@@ -254,13 +256,13 @@
                 },
                 contentFilter: {
                     scope: 'private',
-                    fileType: 'teach'
+                    fileType: 'res'
                 },
                 keyWord: '',
                 contentTypeList: [
                     {
                         label: '教材',
-                        type: 'teach',
+                        type: 'res',
                     },
                     {
                         label: this.$t('teachContent.filterPicture'),
@@ -270,9 +272,13 @@
                         label: this.$t('teachContent.filterVideo'),
                         type: 'video',
                     },
+                    {
+                        label: '音频',
+                        type: 'audio',
+                    },
                     {
                         label: this.$t('teachContent.filterDoc'),
-                        type: 'document',
+                        type: 'doc',
                     },
                     {
                         label: this.$t('teachContent.filterOther'),
@@ -280,8 +286,22 @@
                     }
                 ],
                 fileList: {
-                    school: [],
-                    private: []
+                    school: {
+                        res: [],
+                        image: [],
+                        video: [],
+                        audio: [],
+                        doc: [],
+                        other: []
+                    },
+                    private: {
+                        res: [],
+                        image: [],
+                        video: [],
+                        audio: [],
+                        doc: [],
+                        other:[]
+                    }
                 },
                 selectedFiles: [],
                 fileListShow: [],
@@ -393,10 +413,11 @@
              */
             queryQuestionByPage() {
                 let queryData = {
-                    '@CURRPAGE': this.pageNum,
-                    '@PAGESIZE': this.pageSize,
+                    //'@CURRPAGE': this.pageNum,
+                    //'@PAGESIZE': this.pageSize,
                     '@DESC': "createTime",
                     'code': this.questionFilter.code,
+                    'scope': this.questionFilter.code == this.$store.state.userInfo.TEAMModelId ? 'private':'school',
                     'periodId': this.questionFilter.periodId == "" ? [] : [this.questionFilter.periodId],
                     'level': this.deleteAll(this.questionFilter.level),
                     'type': this.deleteAll(this.questionFilter.type),
@@ -404,27 +425,21 @@
                     'subjectId': this.questionFilter.subjectId == "" ? [] : [this.questionFilter.subjectId],
                 }
                 this.isLoading = true
-                this.$api.newEvaluation.FindExerciseList(queryData).then(async res => {
-                    let privateSas = await this.$tools.getPrivateSas()
-                    let schoolSas = await this.$tools.getSchoolSas()
+                this.$api.newEvaluation.FindExerciseList(queryData).then(res => {
+                    
                     /* 拿到Summary的题目之后要通过每个题目的JSON URL 换取完整题目数据 */
                     let list = res.items
-                    list.forEach(async (i, index) => {
-                        if (i.url) {
-                            try {
-                                let sasString = i.code === this.$store.state.userInfo.TEAMModelId ? privateSas : schoolSas
-                                let jsonInfo = await this.$tools.getFile(i.url + sasString.sas)
-                                let jsonData = JSON.parse(jsonInfo)
-                                jsonData.id = i.id
-                                jsonData.fileName = i.url.split('/')[i.url.split('/').length - 1].split('.json')[0]
-                                this.$set(list, index, jsonData)
-                            } catch (e) {
-                                this.$Message.warning('存在试题读取异常!')
-                            }
-
+                    this.$evTools.getFullItem(list).then(
+                        (res) => {
+                            this.questionList = res
+                        },
+                        (err) => {
+                            this.$Message.error('题库获取失败')
                         }
-                    })
-                    this.questionList = list
+                    )
+                    
+                    
+                }).finally(() => {
                     setTimeout(() => {
                         this.isLoading = false
                     }, 500)
@@ -536,80 +551,95 @@
                 let type = this.contentFilter.fileType
                 let files = this.fileList[this.contentFilter.scope]
                 this.fileListShow = []
-                switch (type) {
-                    case 'teach':
-                        this.fileListShow = files.filter(item => {
-                            return item.type === 'teach'
-                        })
-                        break
-                    case 'image':
-                        this.fileListShow = files.filter(item => {
-                            return item.type === 'image'
-                        })
-                        break
-                    case 'video':
-                        this.fileListShow = files.filter(item => {
-                            return item.type === 'video'
-                        })
-                        break
-                    case 'doc':
-                        this.fileListShow = files.filter(item => {
-                            return item.type === 'doc'
-                        })
-                        break
-                    case 'other':
-                        this.fileListShow = files.filter(item => {
-                            return item.type === 'other'
-                        })
-                        break
-                }
+                this.fileListShow = this.fileList[this.contentFilter.scope][this.contentFilter.fileType]
+                //switch (type) {
+                //    case 'res':
+                //        this.fileListShow = files.filter(item => {
+                //            return item.type === 'res'
+                //        })
+                //        break
+                //    case 'image':
+                //        this.fileListShow = files.filter(item => {
+                //            return item.type === 'image'
+                //        })
+                //        break
+                //    case 'video':
+                //        this.fileListShow = files.filter(item => {
+                //            return item.type === 'video'
+                //        })
+                //        break
+                //    case 'doc':
+                //        this.fileListShow = files.filter(item => {
+                //            return item.type === 'doc'
+                //        })
+                //        break
+                //    case 'other':
+                //        this.fileListShow = files.filter(item => {
+                //            return item.type === 'other'
+                //        })
+                //        break
+                //}
                 this.searchBefore = this.fileListShow
                 this.keyWord = ''
                 this.handleCheckStatus()
             },
             //获取文件列表
             async getFileList() {
-                if (this.contentFilter.scope == 'private' && this.fileList[this.contentFilter.scope].length == 0) {
-                    let sasRes = await this.$tools.getPrivateSas()
-                    let op1 = new BlobTool(sasRes.url, sasRes.name, sasRes.sas, this.contentFilter.scope)
-                    op1.listBlob({
-                        prefix: 'res'
-                    }).then(
-                        (res) => {
-                            this.fileList[this.contentFilter.scope] = res.blobList
-                            this.fileListShow = this.fileList[this.contentFilter.scope].filter((item) => {
-                                return item.type == this.contentFilter.fileType
-                            })
-                            this.searchBefore = [...this.fileListShow]
-                        },
-                        (err) => {
-                            this.$Message.error('API Error')
-                        }
-                    )
-                } else if (this.contentFilter.scope == 'school' && this.fileList[this.contentFilter.scope].length == 0) {
-                    let sasRes = await this.$tools.getSchoolSas()
-                    let op1 = new BlobTool(sasRes.url, sasRes.name, sasRes.sas, this.contentFilter.scope)
-                    op1.listBlob({
-                        prefix: 'res'
-                    }).then(
-                        (res) => {
-                            this.fileList[this.contentFilter.scope] = res.blobList
-                            this.fileListShow = this.fileList[this.contentFilter.scope].filter((item) => {
-                                return item.type == this.contentFilter.fileType
-                            })
-                            this.searchBefore = [...this.fileListShow]
-                        },
-                        (err) => {
-                            this.$Message.error('API Error')
-                        }
-                    )
+                if (this.fileList[this.contentFilter.scope][this.contentFilter.fileType].length == 0) {
+                    let sasRes
+                    if (this.contentFilter.scope == 'private') {
+                        sasRes = await this.$tools.getPrivateSas()
+                    } else if (this.contentFilter.scope == 'school'){
+                        sasRes = await this.$tools.getSchoolSas()
+                    }
+                    if (sasRes) {
+                        let op1 = new BlobTool(sasRes.url, sasRes.name, sasRes.sas, this.contentFilter.scope)
+                        op1.listBlob({
+                            prefix: this.contentFilter.fileType
+                        }).then(
+                            (res) => {
+                                this.fileList[this.contentFilter.scope][this.contentFilter.fileType] = res.blobList
+                                this.fileListShow = this.fileList[this.contentFilter.scope][this.contentFilter.fileType]
+                                this.searchBefore = [...this.fileListShow]
+                            },
+                            (err) => {
+                                this.$Message.error('API Error')
+                            }
+                        )
+                    } else {
+                        this.$Message.error('获取Blob授权信息失败')
+                    }
                 } else {
-                    this.fileListShow = this.fileList[this.contentFilter.scope].filter((item) => {
-                        return item.type == this.contentFilter.fileType
-                    })
+                    this.fileListShow = this.fileList[this.contentFilter.scope][this.contentFilter.fileType]
                     this.searchBefore = [...this.fileListShow]
                     this.handleCheckStatus()
                 }
+
+                //if (this.contentFilter.scope == 'private' && this.fileList[this.contentFilter.scope][this.contentFilter.fileType].length == 0) {
+                //    let sasRes = await this.$tools.getPrivateSas()
+                //    let op1 = new BlobTool(sasRes.url, sasRes.name, sasRes.sas, this.contentFilter.scope)
+                //    op1.listBlob({
+                //        prefix: this.contentFilter.fileType
+                //    }).then(
+                //        (res) => {
+                //            this.fileList[this.contentFilter.scope][this.contentFilter.fileType] = res.blobList
+                //            this.fileListShow = this.fileList[this.contentFilter.scope][this.contentFilter.fileType]
+                //            this.searchBefore = [...this.fileListShow]
+                //        },
+                //        (err) => {
+                //            this.$Message.error('API Error')
+                //        }
+                //    )
+                //} else if (this.contentFilter.scope == 'school' && this.fileList[this.contentFilter.scope].length == 0) {
+                //    let sasRes = await this.$tools.getSchoolSas()
+                    
+                //} else {
+                //    this.fileListShow = this.fileList[this.contentFilter.scope].filter((item) => {
+                //        return item.type == this.contentFilter.fileType
+                //    })
+                //    this.searchBefore = [...this.fileListShow]
+                //    this.handleCheckStatus()
+                //}
             },
             selectVolume(index) {
                 this.currentVolumeIndex = index

+ 3 - 3
TEAMModelOS/ClientApp/src/components/learnactivity/QuestionList.less

@@ -6,15 +6,15 @@
 @second-fontSize: 16px;
 
 .question-item-wrap {
-    background-color: #505050;
+    background-color: #454545;
     margin-bottom: 3px;
     padding: 10px 30px 10px 10px;
     position: relative;
     border-radius: 4px;
 
     &:hover {
-        background: #525252;
-        transform: translate(-2px,-2px);
+        background: #606060;
+        /*transform: translate(-2px,-2px);*/
         transition: all .1s;
         box-shadow: 1px 4px 5px #191919;
     }

+ 13 - 9
TEAMModelOS/ClientApp/src/components/learnactivity/QuestionList.vue

@@ -20,13 +20,13 @@
                             <InputNumber :disabled="config.editable" :max="100" :min="1" v-model="questionExplain.score" size="small" style="width: 110px" :formatter="value => value+' 分'" :parser="value => value.replace(' 分','')" @on-change="calcScore"></InputNumber>
                         </p>
                         <p class="answer-label">答案:</p>
-                        <p class="answer-detail" v-if="questionExplain.exercise !== undefined">
-                            <span :key="index" v-for="(answerItem,index) in questionExplain.exercise.answer" v-html="answerItem"></span>
-                            <span v-if="questionExplain.exercise.answer.length == 0">暂无答案</span>
+                        <p class="answer-detail" v-if="questionExplain.length > 0">
+                            <span :key="index" v-for="(answerItem,index) in questionExplain[0].answer" v-html="answerItem"></span>
+                            <span v-if="questionExplain[0].answer.length == 0">暂无答案</span>
                         </p>     
                         <p class="answer-label">解析:</p>
-                        <p class="answer-detail"  v-if="questionExplain.exercise !== undefined">
-                            <span v-html="questionExplain.exercise.explain == null ? '暂无解析' : questionExplain.exercise.explain"></span>
+                        <p class="answer-detail"  v-if="questionExplain.length > 0">
+                            <span v-html="questionExplain[0].explain == null ? '暂无解析' : questionExplain[0].explain"></span>
                         </p>
                         <div class="question-control-wrap">
                             <Icon type="md-trash" class="question-control-btn" size="25" title="删除" v-if="config.showDelete" @click="deleteQuestion(index)" />
@@ -105,8 +105,10 @@
                         this.$Message.warning('已经超过最多题目数量!')
                     } else {
                         this.selectedId.push(data.id)
-                        let que = await this.$evTools.getFullPaper(data)
-                        this.selectedQuestion.push(que)
+                        let que = await this.$evTools.getFullItem([data])
+                        console.log('77777777777777777')
+                        console.log(que)
+                        this.selectedQuestion.push(que[0])
                         this.$emit('on-question-change', this.selectedQuestion,1)
                     }
                 } else {
@@ -129,8 +131,10 @@
             async toglleQuestionDetail(index) {
                 this.isLoading = true
                 this.questionExplain = {}
-                this.questionExplain = await this.$evTools.getFullPaper(this.questions[index])
-                if (this.questionExplain.item.length > 0) {
+                console.log(this.questions[index])
+                this.questionExplain = await this.$evTools.getFullItem([this.questions[index]])
+                console.log(this.questionExplain)
+                if (this.questionExplain.length > 0) {
                     setTimeout(() => {
                         this.isLoading = false
                     }, 300)

+ 8 - 1
TEAMModelOS/ClientApp/src/css/common-style.less

@@ -45,4 +45,11 @@
     .ivu-btn.disabled, .ivu-btn.disabled.active, .ivu-btn.disabled:active, .ivu-btn.disabled:focus, .ivu-btn.disabled:hover, .ivu-btn[disabled], .ivu-btn[disabled].active, .ivu-btn[disabled]:active, .ivu-btn[disabled]:focus, .ivu-btn[disabled]:hover, fieldset[disabled] .ivu-btn, fieldset[disabled] .ivu-btn.active, fieldset[disabled] .ivu-btn:active, fieldset[disabled] .ivu-btn:focus, fieldset[disabled] .ivu-btn:hover {
         color: #606060;
     }
-} 
+}
+
+//标签文字 不可点击状态
+.disable-text-icon {
+    pointer-events: none;
+    cursor: not-allowed !important;
+    color: #a5a5a5 !important;
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 12 - 0
TEAMModelOS/ClientApp/src/icons/svg/hiteach_pro.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 12 - 0
TEAMModelOS/ClientApp/src/icons/svg/hiteach_std.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 12 - 0
TEAMModelOS/ClientApp/src/icons/svg/hiteach_tbl.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
TEAMModelOS/ClientApp/src/icons/svg/htc.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
TEAMModelOS/ClientApp/src/icons/svg/htc_pro.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
TEAMModelOS/ClientApp/src/icons/svg/htc_std.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
TEAMModelOS/ClientApp/src/icons/svg/htc_tbl.svg


+ 10 - 0
TEAMModelOS/ClientApp/src/icons/svg/tmd.svg

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+  
+    
+            <path d="M17.319734,5.181534 L19.702232,2.799072 C15.2455288,-0.932999399 8.75443317,-0.932999399 4.29773,2.799072 L6.68023,5.181534 C9.80754248,2.74296572 14.1924215,2.74296572 17.319734,5.181534 Z" id="Path"></path>
+            <path d="M18.818446,17.319826 L21.200946,19.702288 C24.9330174,15.2455848 24.9330174,8.75448917 21.200946,4.297786 L18.818446,6.680248 C21.2570542,9.80757435 21.2570542,14.1924997 18.818446,17.319826 L18.818446,17.319826 Z" id="Path"></path>
+            <path d="M6.680266,18.818466 L4.297768,21.200928 C8.75444293,24.9330003 15.2455211,24.9330003 19.702196,21.200928 L17.319696,18.818466 C14.1924109,21.2570337 9.8075511,21.2570337 6.680266,18.818466 Z" id="Path"></path>
+            <path d="M5.181554,6.680322 L2.799054,4.29786 C-0.933015927,8.75450786 -0.933015927,15.2455661 2.799054,19.702214 L5.181554,17.319752 C2.74298415,14.1924676 2.74298415,9.80760636 5.181554,6.680322 Z" id="Path"></path>
+      
+</svg>

+ 216 - 0
TEAMModelOS/ClientApp/src/mock/serviceDriveAuth.js

@@ -0,0 +1,216 @@
+import Mock from "mockjs";
+
+export default {
+  serviceDriveAuthData: Mock.mock({
+  
+     //第一區假資料,服務授權列表,可參閱下面產品訂單格式,服務正式名稱尚未定義
+    serviceList: [
+      {
+        name: "智慧學伴學生授權數(AClassONE)", //服務名稱
+        isBuy: true, //是否購買
+        isActive: true, //是否啟用
+        authTimeStart: "2019.8.31", //服務啟用
+        authTimeEnd: "2021.8.31", //無到期日,永久授權
+        authNum: 500, //授權數量,應該只有AclassOne會有
+        authNumToday: 158, //今日分配狀況
+        intro: "@cparagraph(1, 3)", //產品詳細介紹
+        fixedNum:74, //固定分配數
+        dynamicNum:84 //動態分配數
+      },
+      {
+        name: "教學大數據服務",
+        isBuy: true,
+        isActive: true,
+        authTimeStart: "2019.8.31",
+        authTimeEnd: "2021.11.30",
+        authNum: 500,
+        authNumToday: 158,
+        intro: "@cparagraph(1, 3)", //產品詳細介紹
+      },
+      {
+        name: "智慧校園物聯網服務",
+        isBuy: true,
+        isActive: false,
+        authTimeStart: "2019.12.31",
+        authTimeEnd: "2022.12.31",
+        authNum: 500,
+        authNumToday: 158,
+        intro: "@cparagraph(1, 3)", //產品詳細介紹
+      },
+      {
+        name: "自動閱卷服務",
+        isBuy: true,
+        isActive: false,
+        authTimeStart: "2019.12.31",
+        authTimeEnd: "2024.12.31",
+        authNum: 500,
+        authNumToday: 158,
+        intro: "@cparagraph(1, 3)", //產品詳細介紹
+      },
+      {
+        name: "柏拉圖自動切片服務",
+        isBuy: false, //未購買未啟用
+        isActive: false,
+        authTimeStart: "2019.12.31",
+        authTimeEnd: "2022.12.31",
+        authNum: 500,
+        authNumToday: 158,
+        intro: "@cparagraph(1, 3)", //產品詳細介紹
+      },
+    ],
+     //第二區假資料,智慧教室授權管理
+    hiteachClassroomAuth: [
+      //參考hbcn-product
+      {
+        id: "hbcn",
+        code: "Product",
+        serials: {
+          "auth|58": [
+            {
+              id: "117074",
+              serial: "12DLT43F-EE20-4775-93F5-6BCC",
+              prodGroup: "HiTeach 5",
+              'prodName|1': ['PRO','TBL','STD'],
+              clientQty: 10,
+              startDate: 1598832000,
+              "endDate|1": [0,1,1602691199],//已到期,無限期或有一個結束時間
+              active: '@natural(0, 6)',//已啟用最大值,搭配deviceBound
+              deviceMax: '@natural(1, 10)',//可綁定最大值,如只有一則回傳單一
+              'deviceBound|0-9': [
+                {
+                  uuid: "@name",
+                  uuid2: null,
+                  deviceId: "2d776529-ac7b-4197-b981-3ae3370fe686",
+                  classId: "Class0103",
+                  deviceIp:'@ip',
+                  deviceMac:"@id", //MAC位址
+                  computer:"Microsoft Windows NT 10.0.177.63.0 | Intel(R) Core(TM) i5-9500 CPU @3.00GHz" //PC的詳細規格,
+                },
+              ],
+              aprule: {
+                "sokapp|1": true, //蘇格拉底議課
+                "sokvdo|1": true,//蘇格拉底影片
+                "ezs|1": true,//錄播系統
+
+                //目前格式上沒有的模組
+                //遠距教室服務
+                "remoteSys|1": true,
+                //蘇格拉底桌面
+                "sokDesktop|1":false,
+                //蘇格拉底報告
+                "sokreport|1":false
+              },
+            },
+            
+          ],
+        },
+      },
+    ],
+    //第三區假資料,智慧教學服務空間狀態,refer to Jeff hbcn-product.json product那區回傳的資料,periodID意涵參閱Jeff json中欄位對應
+    spaceStatus: {
+        
+            "prodCode": "IPALYEIY",
+            "prodGroup": "TEAM Model Cloud",
+            "prodName": "智慧教學服務空間",
+            "lastEndDay":1692547199,//空間最終到期日
+            "noperiod": false,
+            "serviceType": "space",
+            //全部買的空間授權時間狀態
+            "auth": [
+                {
+                    "periodId": "4361",
+                    "number": 1000,
+                    "unit": "G"
+                },
+                {
+                    "periodId": "4362",
+                    "number": 1000,
+                    "unit": "G"
+                },
+                {
+                    "periodId": "4363",
+                    "number": 1000,
+                    "unit": "G"
+                },
+                {
+                    "periodId": "4361",
+                    "number": 200,
+                    "unit": "G"
+                },
+                {
+                    "periodId": "4362",
+                    "number": 200,
+                    "unit": "G"
+                },
+                {
+                    "periodId": "4363",
+                    "number": 200,
+                    "unit": "G"
+                },
+                {
+                    "periodId": "7856",
+                    "number": 1000,
+                    "unit": "G"
+                },
+                {
+                    "periodId": "7857",
+                    "number": 1000,
+                    "unit": "G"
+                }
+            ],
+            
+                "avaliable": 1010,
+                "used": 573,
+            
+            //訂單就是最下面那區
+            "order": [
+                {
+                    "orderId": "20201020489069062492",
+                    "opid": "24735",
+                    "mainPeriodId": "221",
+                    "periodIds": [
+                        "4361",
+                        "4362",
+                        "4363"
+                    ],
+                    "startDate": 1597939200,
+                    "endDate": 1692547199,
+                    "number": 1000,
+                    "unit": "G",
+                    "createDate": 1559322061
+                },
+                {
+                    "orderId": "20201020489063456789",
+                    "opid": "25774",
+                    "mainPeriodId": "221",
+                    "periodIds": [
+                        "4361",
+                        "4362",
+                        "4363"
+                    ],
+                    "startDate": 1597939200,
+                    "endDate": 1692547199,
+                    "number": 200,
+                    "unit": "G",
+                    "createDate": 1559322061
+                },
+                {
+                    "orderId": "20201119265796324211",
+                    "opid": "29885",
+                    "mainPeriodId": "356",
+                    "periodIds": [
+                        "7856",
+                        "7857"
+                    ],
+                    "startDate": 1597939200,
+                    "endDate": 1692547199,
+                    "number": 1000,
+                    "unit": "G",
+                    "createDate": 1559322061
+                }
+            ]
+        
+        
+          },
+  }),
+};

+ 7 - 0
TEAMModelOS/ClientApp/src/router/routes.js

@@ -424,6 +424,13 @@ export const routes = [
 				path: 'scboard',
 				name: 'scboard',
 				component: resolve => require(['@/view/scboard/Index.vue'], resolve)
+			},
+
+			//Louise的教師儲存空間授權管理分頁
+			{
+				path: 'serviceDriveAuth',
+				name: 'serviceDriveAuth',
+				component: resolve => require(['@/view/serviceDriveAuth/Index.vue'], resolve)
 			}
 
 

+ 7 - 2
TEAMModelOS/ClientApp/src/store/index.js

@@ -11,6 +11,7 @@ import config from './module/config'
 import teachers from './module/teachers'
 import studentWeb from './module/studentWeb'
 import scboard from './module/scboard'
+import serviceDriveAuth from './module/serviceDriveAuth'
 Vue.use(Vuex)
 
 // TYPES
@@ -29,7 +30,10 @@ const mutations = {
         console.log('state')
         state.schoolSas = obj
     },
-	setUserInfo(state,obj){
+    setUserInfo(state, obj) {
+        if (!obj.schoolCode) { // 如果账号暂未归户到学校,schoolCode为空,但是很多API schoolCode为必传,所以这里先暂时为没有归户的账号设定一个默认schoolcode
+            obj.schoolCode = 'SYSTEM_NO_SCHOOL'
+        }
 		state.userInfo = obj
 	},
 	setSchoolCode(state,obj){
@@ -70,6 +74,7 @@ export default new Vuex.Store({
         config,
         teachers,
         studentWeb,
-        scboard
+        scboard,
+        serviceDriveAuth
     }
 })

+ 2 - 0
TEAMModelOS/ClientApp/src/store/module/schoolBaseInfo.js

@@ -17,6 +17,7 @@ export default {
         getClassrooms: state => state.classrooms,
         getGrades: state => state.srvAdr,
         getStudent: state => state.students,
+        getSchoolInfo: state => state.schoolBaseInfo,
     },
     mutations: {
         setSchoolInfo(state, data) {
@@ -54,6 +55,7 @@ export default {
                         }).then(
                             res => {
                                 if (res) {
+                                    console.log(res)
                                     if (res.schools.length > 0) {
                                         context.commit('setSchoolInfo', res.schools[0])
                                         resolve({

+ 101 - 0
TEAMModelOS/ClientApp/src/store/module/serviceDriveAuth.js

@@ -0,0 +1,101 @@
+import serviceDriveAuth from "@/mock/serviceDriveAuth";
+//先接入假資料
+
+export default {
+  namespaced: true,
+  state: {
+    //假設一開始未定,等待DB資料進來,從action非同步,然後操作mutation,改變State
+    serviceList: undefined,
+    hiteachAuthList: undefined,
+    spaceStatus: undefined,
+    serviceIntroIsOpen: [],
+    hiteachListItemIsOpen: [], //存放每個開關
+  },
+  getters: {
+    getServiceIntroIsOpen: (state) => {
+      return state.serviceIntroIsOpen;
+    },
+    getHiteachListItemIsOpen: (state) => {
+      return state.hiteachListItemIsOpen;
+    },
+    getServiceList: (state) => {
+      if (state.serviceList !== undefined) {
+        return state.serviceList;
+      } else {
+        return {};
+      }
+    },
+    getHiteachAuthList: (state) => {
+      if (state.hiteachAuthList !== undefined) {
+        return state.hiteachAuthList;
+      } else {
+        return {};
+      }
+    },
+    getSpaceStatus: (state) => {
+      if (state.spaceStatus !== undefined) {
+        return state.spaceStatus;
+      } else {
+        return {};
+      }
+    },
+  },
+  mutations: {
+    setServiceIntroIsOpen(state, data) {
+      state.serviceIntroIsOpen = data;
+    },
+    setHiteachListItemIsOpen(state, data) {
+      state.hiteachListItemIsOpen = data;
+    },
+    setServiceList(state, data) {
+      state.serviceList = data;
+    },
+    setHiteachAuthList(state, data) {
+      state.hiteachAuthList = data;
+    },
+    setSpaceStatus(state, data) {
+      state.spaceStatus = data;
+    },
+  },
+  actions: {
+    //模擬從DB(MockData)取得所有資料 && commit to state.serviceDriveAuth
+    getServiceListDataAsyc(context) {
+      return new Promise((resolve) => {
+        //先暫時模擬等待幾秒,直接取得資料
+
+        let servicedata = serviceDriveAuth.serviceDriveAuthData.serviceList;
+        let hiteachAuthdata =
+          serviceDriveAuth.serviceDriveAuthData.hiteachClassroomAuth;
+       
+        let spaceStatusData = serviceDriveAuth.serviceDriveAuthData.spaceStatus;
+        let temp = [],temp2=[];
+       
+        let hiteachlistlength=hiteachAuthdata[0].serials.auth.length
+        //根據服務的長度生出開關,只要服務數量變動就必須重置
+        for (let i = 0; i < servicedata.length; i++) {
+          temp.push({
+            index: i,
+            isOpen: false,
+          });
+        }
+       
+
+        for (let i = 0; i < hiteachlistlength; i++) {
+          temp2.push({
+            index: i,
+            isOpen: false,
+          });
+        }
+        context.commit("setServiceIntroIsOpen", temp);
+          context.commit('setHiteachListItemIsOpen',temp2)
+          context.commit("setServiceList", servicedata);
+          context.commit("setHiteachAuthList", hiteachAuthdata);
+          context.commit("setSpaceStatus", spaceStatusData);
+       
+        
+
+        resolve({});
+      });
+    },
+  },
+};

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

@@ -141,23 +141,28 @@ export default {
 			let blobHost = paper.scope === 'school' ?  JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri :  JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8")).blob_uri
 			// 根据试卷的Blob地址 去读取JSON文件
 			let sasString = paper.scope === 'school' ?  await $tools.getSchoolSas() : await $tools.getPrivateSas()
-			let jsonInfo = await $tools.getFile(blobHost + paper.blob + sasString.sas)
-			let jsonData = JSON.parse(jsonInfo)
-			// 获取试卷包含的试题数据并包装好
-			if(jsonData.urls && jsonData.urls.length){
-				jsonData.item = []
-				const path = blobHost + paper.blob.split('index.json')[0]
-				jsonData.urls.forEach(async (itemUrl,index) => {
-					// 获取题目JSON并且包装成完整试题对象
-					let itemJson = JSON.parse(await $tools.getFile(path + itemUrl + sasString.sas))
-					itemJson.exercise.question = itemJson.item[0].question
-					itemJson.exercise.option = itemJson.item[0].option
-					itemJson.exercise.id = itemJson.id 
-					itemJson.exercise.score = jsonData.scoring[index].score
-					jsonData.item.push(itemJson.exercise)
-				})
+			try{
+				let jsonInfo = await $tools.getFile(blobHost + paper.blob + '/index.json' + sasString.sas)
+				let jsonData = JSON.parse(jsonInfo)
+				// 获取试卷包含的试题数据并包装好
+				if(jsonData.urls && jsonData.urls.length){
+					jsonData.item = []
+					const path = blobHost + paper.blob
+					jsonData.urls.forEach(async (itemUrl,index) => {
+						// 获取题目JSON并且包装成完整试题对象
+						let itemJson = JSON.parse(await $tools.getFile(path + '/' + itemUrl + sasString.sas))
+						itemJson.exercise.question = itemJson.item[0].question
+						itemJson.exercise.option = itemJson.item[0].option
+						itemJson.exercise.id = itemJson.id 
+						itemJson.exercise.score = jsonData.scoring[index].score
+						jsonData.item.push(itemJson.exercise)
+					})
+				}
+				r(jsonData)
+			}catch(e){
+				j(e)
 			}
-			r(jsonData)
+			
 		})
 		
 	},

+ 0 - 12
TEAMModelOS/ClientApp/src/utils/js-fn.js

@@ -13,17 +13,6 @@ function groupBy(array, key) {
         return groups[group]
     })
 }
-function groupBynew(array, key) {
-    const groups = {}
-    array.forEach(function (item) {
-        const group = JSON.stringify(item.exercise[key])
-        groups[group] = groups[group] || []
-        groups[group].push(item)
-    })
-    return Object.keys(groups).map(function (group) {
-        return groups[group]
-    })
-}
 /*
  * 判断两个对象是否相等
  */
@@ -246,6 +235,5 @@ export default {
     debounce,
     throttle,
     formatBytes,
-    groupBynew,
     uuid
 }

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

@@ -343,7 +343,7 @@ export default {
 	 */
 	getPrivateSas() {
 		return new Promise((r, j) => {
-			if (!store.state.privateSas || checkSas(store.state.privateSas.timeout)) {
+			if (!store.state.privateSas || checkSas(store.state.privateSas.timeout) || store.state.privateSas.name !== store.state.userInfo.TEAMModelId) {
 				$api.uploadFile.blobSasRCW({
 					name: store.state.userInfo.TEAMModelId,
 					role: 'teacher'

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

@@ -36,7 +36,22 @@
             }
         },
         created() {
-            //this.$store.dispatch('schoolBaseInfo/getClassroom')
+            //刷新重置vuex
+            let user = JSON.parse(decodeURIComponent(localStorage.userInfo, "utf-8"));
+            let user_profile = JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8"));
+            if (user_profile.schools.length) {
+                this.$store.commit('setUserInfo', {
+                    TEAMModelId: user.id,
+                    name: user.name,
+                    schoolCode: user_profile.defaultschool || user_profile.schools[0].schoolId
+                })
+            } else {
+                this.$store.commit('setUserInfo', {
+                    TEAMModelId: user.id,
+                    name: user.name,
+                    schoolCode: 'SYSTEM_NO_SCHOOL'
+                })
+            }
         },
         methods: {
             toHome() {

+ 8 - 7
TEAMModelOS/ClientApp/src/view/classmgt/ManageClass.vue

@@ -6,19 +6,19 @@
                 <Option v-for="(item,index) in classList" :value="item.id" :key="index" @click.native="selectClass(index)">{{ item.name }}</Option>
             </Select>
             <span v-if="classList[curClassIndex]" style="margin-left: 10px;color: #aaaaaa;font-family: cursive;">
-                人数:{{classList[curClassIndex].students.length}}
+                学生人数:{{classList[curClassIndex].students.length}}
             </span>
             <Button :disabled="!updated" size="small" style="float:right;margin-right:30px;margin-top:10px;" @click="saveGroup" icon="ios-albums-outline">保存分组</Button>
-            <span class="action-btn-wrap" @click="customGroupStatus = true">
-                <Icon type="md-shuffle" color="white" size="14" />
+            <span :class="classList.length > 0 ? 'action-btn-wrap': 'disable-text-icon action-btn-wrap'" @click="customGroupStatus = true">
+                <Icon type="md-shuffle" size="14" />
                 自动分组
             </span>
-            <span class="action-btn-wrap" @click="exportStudents">
-                <Icon type="md-arrow-round-down" color="white" size="16" />
+            <span :class="classList.length > 0 ? 'action-btn-wrap': 'disable-text-icon action-btn-wrap'" @click="exportStudents">
+                <Icon type="md-arrow-round-down" size="16" />
                 导出名单
             </span>
-            <span class="action-btn-wrap" @click="toggleView()">
-                <Icon :type="viewType ? 'md-card':'md-list'" color="white" size="16" />
+            <span :class="classList.length > 0 ? 'action-btn-wrap': 'disable-text-icon action-btn-wrap'" @click="toggleView()">
+                <Icon :type="viewType ? 'md-card':'md-list'" size="16" />
                 {{viewType ? '分组视图':'列表视图' }}
             </span>
         </div>
@@ -41,6 +41,7 @@
                         <span>{{row.groupName ? row.groupName : '未分组'}}</span>
                     </template>
                 </Table>
+                <EmptyData textContent="暂无您管理的班级" :top="150"></EmptyData>
             </vuescroll>
         </div>
         <div class="mgt-class-body dark-iview-table animated fadeIn dark-iview-input disabled-iview-input" id="table-wrap" v-else>

+ 55 - 16
TEAMModelOS/ClientApp/src/view/classrecord/ClassRecord.vue

@@ -4,9 +4,9 @@
             <div style="padding: 1% 3% 0px 3%;">
                 <!--头部信息-->
                 <div class="class-record-header">
-                    <span class="course-name">物理</span>
+                    <span class="course-name">数学</span>
                     <span class="record-name">
-                        力的组成
+                        速度与时间
                     </span>
                     <div style="float:right;">
                         <span class="label-text">
@@ -674,20 +674,56 @@
              * 获取blob授权信息
              * */
             async getSasStr() {
-                let sasRes1 = await this.$tools.getSchoolSas()
-                let sasRes2 = await this.$tools.getPrivateSas()
-
-                if (sasRes1) {
-                    this.schoolSas = sasRes1.sas
-                    this.playerOptions.sources[0].src += this.schoolSas
-                } else {
-                    this.$Message.error('获取Blob授权失败')
-                }
-                if (sasRes2) {
-                    this.teacherSas = sasRes2.sas
-                } else {
-                    this.$Message.error('获取Blob授权失败')
-                }
+                //let sasRes1 = await this.$tools.getSchoolSas()
+                //let sasRes2 = await this.$tools.getPrivateSas()
+
+                //if (sasRes1) {
+                //    this.schoolSas = sasRes1.sas
+                //    this.playerOptions.sources[0].src += this.schoolSas
+                //} else {
+                //    this.$Message.error('获取Blob授权失败')
+                //}
+                //if (sasRes2) {
+                //    this.teacherSas = sasRes2.sas
+                //} else {
+                //    this.$Message.error('获取Blob授权失败')
+                //}
+                this.$api.uploadFile.blobSasRCW({
+                    name: "1595321354",
+                    role: 'teacher'
+                }).then(
+                    (res) => {
+                        if (res.error == null) {
+                            res.result.data.sas = '?' + res.result.data.sas
+                            let sasRes1 = res.result.data
+                            this.teacherSas = sasRes1.sas
+                        } else {
+                            this.$Message.error('获取Blob授权失败')
+                        }
+                    },
+                    (err) => {
+                        this.$Message.error('获取Blob授权失败')
+                    }
+                )
+
+                this.$api.uploadFile.blobSasRCW({
+                    name: 'hbcn',
+                    role: 'school'
+                }).then(
+                    (res) => {
+                        if (!res.error) {
+                            res.result.data.sas = '?' + res.result.data.sas
+                            let sasRes2 = res.result.data
+                            this.schoolSas = sasRes2.sas
+                            this.playerOptions.sources[0].src += this.schoolSas
+                        } else {
+                            this.$Message.error('获取Blob授权失败')
+                        }
+                    },
+                    (err) => {
+                        this.$Message.error('获取Blob授权失败')
+                    }
+                )
             },
             //互动类型筛选
             filterType(type) {
@@ -753,6 +789,9 @@
     @import "./ClassRecord.less";
 </style>
 <style>
+    .class-content .vjs-progress-holder {
+        font-size:10px !important;
+    }
     .mouse-over-status .vjs-control-bar {
         opacity: 1 !important;
     }

+ 23 - 2
TEAMModelOS/ClientApp/src/view/evaluation/bank/ExerciseList.vue

@@ -695,8 +695,10 @@
 							scope: item.scope
 						}).then(res => {
 							if (!res.error) {
-								this.$Message.success('删除成功')
-								this.doFilter()
+								this.onDeleteBlobItem(item.blob).then(e => {
+									this.$Message.success('删除成功')
+									this.doFilter()
+								})
 							} else {
 								this.$Message.warning('删除失败,错误代码:' + res.error.code + ',错误信息:' + res.error.message)
 							}
@@ -707,6 +709,25 @@
 					}
 				})
 			},
+			
+			/* 删除Blob里面的试题 */
+			onDeleteBlobItem(path){
+				return new Promise(async (r,j) => {
+					// 获取初始化Blob需要的数据
+					let sasData = this.curScope === 'school' ? await this.$tools.getSchoolSas() : await this.$tools.getPrivateSas()
+					//初始化Blob
+					let containerClient = new blobTool(sasData.url, sasData.name, sasData.sas , this.curScope)
+					// 等待blob的返回结果
+					containerClient.deleteBlob(path.substring(1)).then(
+					    (res) => {
+					        r(res)
+					    },
+					    (err) => {
+					        this.$Message.error('API Error')
+					    }
+					)
+				})
+			},
 
 			/**
 			 * 编辑单个试题

+ 66 - 14
TEAMModelOS/ClientApp/src/view/evaluation/bank/TestPaperList.vue

@@ -48,6 +48,7 @@
     </div>
 </template>
 <script>
+	import blobTool from '@/utils/blobTool.js'
     import Loading from '@/common/Loading.vue'
     import BaseFilter from '../components/BaseFilter'
     import BaseImport from '../components/BaseImport'
@@ -58,6 +59,7 @@
         },
         data() {
             return {
+				containerClient:null,
                 schoolCode: '',
                 totalNum: 0,
                 pageSize: 5,
@@ -131,16 +133,22 @@
              * @param paper
              */
             async goToPaper(paper) {
-				// 获取完整试卷数据再跳转编辑页面
-				let fullPaperJson = await this.$evTools.getFullPaper(paper)
-				fullPaperJson.code = paper.code
-				console.log(fullPaperJson)
-                this.$router.push({
-                    name: 'createPaper',
-                    params: {
-                        paper: fullPaperJson
-                    }
-                })
+				try{
+					// 获取完整试卷数据再跳转编辑页面
+					let fullPaperJson = await this.$evTools.getFullPaper(paper)
+					fullPaperJson.code = paper.code
+					console.log(fullPaperJson)
+					this.$router.push({
+					    name: 'createPaper',
+					    params: {
+					        paper: fullPaperJson
+					    }
+					})
+				}catch(e){
+					console.log(e)
+					this.$Message.error('获取试卷数据失败!请稍后再试!')
+				}
+				
             },
 
             /**
@@ -163,12 +171,16 @@
                     content: '<p>确认删除该试卷吗?</p>',
                     okText: '确认',
                     cancelText: '取消',
-                    onOk: () => {
+                    onOk: async () => {
+						let blobList = await this.getPaperFiles(item.blob.substring(1))
+						let files = blobList.blobList.map(i => i.blob)
                         this.dataLoading = true
-                        this.$api.learnActivity.DeleteExamPaper({ id: item.id, code: item.code,scope:item.scope }).then(res => {
+                        this.$api.learnActivity.DeleteExamPaper({ id: item.id, code: item.code,scope:item.scope }).then(async res => {
                             if (!res.error) {
-                                this.$Message.success('删除成功')
-                                this.pageChange(1)
+								this.onDeleteBlobPaper(files).then(r => {
+									this.$Message.success('删除成功')
+									this.pageChange(1)
+								})
                             } else {
                                 this.$Message.warning('删除失败,错误代码:' + res.error.code + ',错误信息:' + res.error.message)
                             }
@@ -179,6 +191,46 @@
                     }
                 })
             },
+			
+			 getPaperFiles(path){
+				return new Promise(async (r,j) => {
+					// 获取初始化Blob需要的数据
+					let sasData = this.isSchool ? await this.$tools.getSchoolSas() : await this.$tools.getPrivateSas()
+					//初始化Blob
+					let containerClient = new blobTool(sasData.url, sasData.name, sasData.sas , this.isSchool ? 'school' : 'private')
+					// 等待blob的返回结果
+					containerClient.listBlob({
+					    prefix: path
+					}).then(
+					    (res) => {
+					        r(res)
+					    },
+					    (err) => {
+					        this.$Message.error('API Error')
+					    }
+					)
+				})
+				
+			},
+			
+			onDeleteBlobPaper(files){
+				return new Promise(async (r,j) => {
+					// 获取初始化Blob需要的数据
+					let sasData = this.isSchool ? await this.$tools.getSchoolSas() : await this.$tools.getPrivateSas()
+					//初始化Blob
+					let containerClient = new blobTool(sasData.url, sasData.name, sasData.sas , this.isSchool ? 'school' : 'private')
+					// 等待blob的返回结果
+					containerClient.deleteBlobBatch(files).then(
+					    (res) => {
+					        r(res)
+					    },
+					    (err) => {
+					        this.$Message.error('API Error')
+					    }
+					)
+				})
+				
+			},
 
             /**
             * 切换页码操作

+ 99 - 101
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseExerciseList.vue

@@ -12,15 +12,15 @@
 					: {{ exersicesType[typeItem.type] }}
 					  ({{ typeItem.score || 0 }} 分) </p>
 					
-				<div class="exercise-item" v-for="(item,index) of typeItem.list" :key="index" @click="onQuestionToggle(exerciseList.indexOf(item),item.id,$event,typeItem.list)"
+				<div class="exercise-item" v-for="(item,index) of typeItem.list" :key="index" 
 				 @mouseenter="exerciseMouseover($event)" @mouseleave="exerciseMouseleave($event)">
 
 					<!-- 工具栏部分 -->
 					<div class="item-tools-wrap">
-						<div class="item-tools-t flex-row-center" v-show="isShowTools" @click.stop="handleSetScore(item,exerciseList.indexOf(item),typeItem.list,index)">
-							<Icon type="ios-list-box-outline" />配分</div>
-						<div class="item-tools-t flex-row-center" v-show="isShowTools" @click.stop="handleToolEdit(typeItem.list,item,index)">
-							<Icon type="ios-brush-outline" />编辑</div>
+						<!-- <div class="item-tools-t flex-row-center" v-show="isShowTools" @click.stop="handleSetScore(item,exerciseList.indexOf(item),typeItem.list,index)">
+							<Icon type="ios-list-box-outline" />配分</div> -->
+						<!-- <div class="item-tools-t flex-row-center" v-show="isShowTools" @click.stop="handleToolEdit(typeItem.list,item,index)">
+							<Icon type="ios-brush-outline" />编辑</div> -->
 						<div class="item-tools-t flex-row-center" v-show="isShowTools" @click.stop="handleDelete(typeItem.list,item,index)">
 							<Icon type="ios-archive-outline" />删除</div>
 						<div class="item-tools-t flex-row-center" v-show="index != 0 && isShowTools" @click.stop="handleMoveUp(typeItem.list,index)">
@@ -29,49 +29,56 @@
 						 @click.stop="handleMoveDown(typeItem.list,index)">
 							<Icon type="md-arrow-down" />下移</div>
 					</div>
-					<!-- 题干部分 -->
-					<div class="item-question">
-						<div v-if="viewModel === 'list'">
-							<div class="item-question-order">{{ index + 1 }} : </div>
-							<div class="item-question-text" v-html="item.question"></div>
+					<div @click="onQuestionToggle(exerciseList.indexOf(item),item.id,$event,typeItem.list)">
+						<!-- 题干部分 -->
+						<div class="item-question">
+							<div v-if="viewModel === 'list'">
+								<div class="item-question-order">{{ index + 1 }} : </div>
+								<div class="item-question-text" v-html="item.question"></div>
+							</div>
+							<div v-else>
+								<div class="item-question-order">{{ pageSize * (pageNum - 1) + index + 1 }} : </div>
+								<div class="item-question-text" v-html="item.question" @click="onRichTextClick($event)"></div>
+							</div>
 						</div>
-						<div v-else>
-							<div class="item-question-order">{{ pageSize * (pageNum - 1) + index + 1 }} : </div>
-							<div class="item-question-text" v-html="item.question" @click="onRichTextClick($event)"></div>
+						<!-- 选项部分 -->
+						<div v-for="(option,optionIndex) in item.option" :key="optionIndex" class="item-options">
+							<div class="item-option-content">
+								<div class="item-option-order">{{String.fromCharCode(64 + parseInt(optionIndex+1))}} :</div>
+								<div class="item-option-text" v-html="option.value" @click="onRichTextClick($event)"></div>
+							</div>
 						</div>
-						<span class="item-btn-toggle">
-							<span class="item-score">{{ item.score.toFixed(2) }} 分</span>
-							<Icon :type="collapseList.indexOf(index) > -1 ? 'ios-arrow-dropup' : 'ios-arrow-dropdown'" />
-						</span>
 					</div>
-					<!-- 选项部分 -->
-					<div v-for="(option,optionIndex) in item.option" :key="optionIndex" class="item-options">
-						<div class="item-option-content">
-							<div class="item-option-order">{{String.fromCharCode(64 + parseInt(optionIndex+1))}} :</div>
-							<div class="item-option-text" v-html="option.value" @click="onRichTextClick($event)"></div>
-						</div>
+					<div class="item-btn-toggle" @click.stop>
+						<template>
+							<InputNumber :max="item.score + surPlusScore" :min="0" v-model="item.score" style="display: inline-block ;width: 60px;margin-right: 10px;height: 30px;"
+							    @click.stop></InputNumber>
+								<span style="margin-right: 20px;">分</span>
+							<!-- <span class="item-score" title="设置题目分数" @click.stop="onSetSingleItem(item,index)" v-else>{{ item.score }} 分</span> -->
+						</template>
+						<Icon :type="collapseList.indexOf(index) > -1 ? 'ios-arrow-dropup' : 'ios-arrow-dropdown'" />
 					</div>
 					<!-- 答案以及解析 -->
 					<transition name="slide">
 						<!-- <div v-show="collapseList.indexOf(exerciseList.indexOf(item)) > -1" class="toggle-area"> -->
 						<div v-show="collapseList.indexOf(exerciseList.indexOf(item)) > -1" class="toggle-area">
-							<div v-if="item.type !== 'Compose'">
+							<div v-if="item.type !== 'compose'">
 								<!-- 答案展示部分 -->
 								<div class="item-explain" v-show="isShowAnswer">
 									<span class="explain-title">【答ㅤ案】</span>
 									<div class="item-explain-details" @click="onRichTextClick($event)">
 										<!-- 问答题答案 -->
-										<div v-if="item.type === 'Subjective'">
+										<div v-if="item.type === 'subjective'">
 											<span v-for="(answer,index) in item.answer" :key="index" v-html="item.answer.length ? answer : '未设置答案'"></span>
 										</div>
 										<!-- 填空题答案 -->
-										<div v-else-if="item.type === 'Complete'">
-											<span :class="[ item.type === 'Complete' ? 'item-answer-item':'']" v-for="(answer,index) in item.answer"
+										<div v-else-if="item.type === 'complete'">
+											<span :class="[ item.type === 'complete' ? 'item-answer-item':'']" v-for="(answer,index) in item.answer"
 											 :key="index" v-html="answer"></span>
 										</div>
 										<!-- 其余题型答案 -->
 										<div v-else>
-											<span :class="[ item.type === 'Complete' ? 'item-answer-item':'']" v-for="(answer,index) in item.answer"
+											<span :class="[ item.type === 'complete' ? 'item-answer-item':'']" v-for="(answer,index) in item.answer"
 											 :key="index">{{answer}}</span>
 										</div>
 									</div>
@@ -106,22 +113,24 @@
 				</div>
 			</div>
 		</div>
-
-		<Modal v-model="scoreModal" title="题目配分" @on-ok="onConfirmScore">
+		
+		<!-- 单个试题配分弹框 -->
+<!-- 		<Modal v-model="scoreModal" title="题目配分" @on-ok="onConfirmScore">
 			<InputNumber :max="curItemScore + surPlusScore" :min="0" :step="scoreStep" v-model="curItemScore" @on-change="onScoreChange"></InputNumber>
 			<p>剩余可分配分数:{{ surPlusScore }}</p>
-		</Modal>
-
+		</Modal> -->
+		
+		<!-- 题型配分弹窗 -->
 		<Modal v-model="typeScoreModel" title="题型配分" footer-hide>
-			<span class="type-score-item">试卷总分 : {{ paper.score }}</span>
+			<span class="type-score-item">试卷总分 : {{ paperInfo.score }}</span>
 			<span class="type-score-item">已分配总分 : {{ getTotalScore(groupTypeList) }}</span>
-			<div v-for="(item,index) in groupTypeList" :key="index" class="type-score-item">
-				<div v-if="item.list.length">
-					<span>{{ exersicesType[item.type] }} </span>
-					<span>共 {{ item.list.length }} 道题,总分配:</span>
-					<InputNumber :max="paper.score" :min="0" :step="scoreStep" v-model="item.score"></InputNumber>
+			<div v-for="(modalItem,modalIndex) in groupTypeList" :key="modalIndex" class="type-score-item">
+				<div v-if="modalItem.list.length">
+					<span>{{ exersicesType[modalItem.type] }} </span>
+					<span>共 {{ modalItem.list.length }} 道题,总分配:</span>
+					<InputNumber :max="paperInfo.score" :min="0" :step="scoreStep" v-model="modalItem.score"></InputNumber>
 					<span> 分</span>
-					<div v-show="item.type === 'multiple'">
+					<div v-show="modalItem.type === 'multiple'" style="margin-top: 10px;">
 						<div class="rule-item">
 							<RadioGroup v-model="multipleRule" vertical>
 								<Radio :label="1">
@@ -146,18 +155,18 @@
 				</div>
 			</div>
 
-			<Button class="type-score-btn" @click="onConfirmTypeScore" type="info" :disabled="getTotalScore(groupTypeList) > paper.score">确认</Button>
-			<p style="color:red;text-align:center;font-weight:bold;margin-top:10px" v-show="getTotalScore(groupTypeList) > paper.score">配分已超试卷总分,请重新分配!</p>
+			<Button class="type-score-btn" @click="onConfirmTypeScore" type="info" :disabled="getTotalScore(groupTypeList) > paperInfo.score">确认</Button>
+			<p style="color:red;text-align:center;font-weight:bold;margin-top:10px" v-show="getTotalScore(groupTypeList) > paperInfo.score">配分已超试卷总分,请重新分配!</p>
 
 		</Modal>
-
-		<Modal v-model="editExerciseModal" class-name="edit-exercise-modal" width="1200px" footer-hide title="编辑习题">
+		
+		<!-- 编辑试题弹窗 -->
+<!-- 		<Modal v-model="editExerciseModal" class-name="edit-exercise-modal" width="1200px" footer-hide title="编辑习题">
 			<BaseEditExercise :exerciseItem="currentExercise" @onEditSuccess="onEditSuccess" refId="paperEdit" ref="paperEdit"></BaseEditExercise>
 			<div slot="footer">
 				<Button type="success">确认</Button>
 			</div>
-
-		</Modal>
+		</Modal> -->
 		
 		<!-- 音频播放弹窗 -->
 		<Modal v-model="playAudioModal" width="400" footer-hide @on-visible-change="onAudioModalChange">
@@ -198,6 +207,7 @@
 			return {
 				schoolCode: '',
 				dataLoading: false,
+				curEditItemId:null,
 				exerciseList: [],
 				schoolInfo: {},
 				isShowUploadList: false,
@@ -248,7 +258,10 @@
 				curAudioName: "",
 				curVideoSrc: "",
 				curVideoName: "",
-				modifyItems:[]
+				modifyItems:[],
+				paperInfo:{
+					score:0
+				}
 			}
 		},
 
@@ -273,8 +286,6 @@
 			
 			/* 音频点击播放事件 */
 			onRichTextClick(e) {
-				console.log(e)
-				// e.stopPropagation()
 				if (e.srcElement.classList[0] === 'richText-audio') {
 					this.playAudioModal = true
 					this.curAudioSrc = e.srcElement.dataset.url
@@ -292,6 +303,7 @@
 			 */
 			onQuestionToggle(index, id, e) {
 				e.stopPropagation()
+				this.curEditItemId = null
 				let listIndex = this.collapseList.indexOf(index)
 				if (listIndex > -1) {
 					this.collapseList.splice(listIndex, 1)
@@ -310,34 +322,14 @@
 				this.$emit('toggleChange', this.collapseList)
 			},
 
-
-
-
-			/**
-			 * 根据知识点id集合换取知识点对象集合
-			 * @param ids
-			 */
-			getPointsByIds(ids) {
-				// 去除空值后
-				let idsArr = this._.compact(ids)
-				return new Promise((r, j) => {
-					if (idsArr.length) {
-						this.$api.knowledge.FindKnowledgebyId(idsArr).then(res => {
-							if (!res.error && res.result.data.length) {
-								r(res.result.data)
-							} else {
-								r([])
-							}
-						}).catch(err => {
-							j(err)
-						})
-					} else {
-						r([])
-					}
-				})
-
+			onSetSingleItem(item,index){
+				this.curItemScore = item.score
+				this.curEditItemId = item.id
+			},
+			
+			onItemScoreChange(val){
+				console.log(val)
 			},
-
 
 
 			/**
@@ -431,7 +423,7 @@
 					onOk: () => {
 						this.$Message.success('删除成功')
 						this.surPlusScore += item.score
-						console.log(this.surPlusScore)
+						this.$emit('scoreUpdate',this.surPlusScore)
 						arr.splice(index, 1)
 						this.exerciseList.splice(this.exerciseList.indexOf(item), 1)
 						this.$emit('dataUpdate', this.exerciseList)
@@ -475,6 +467,7 @@
 			onScoreChange(val) {
 				this.surPlusScore = this.surPlusScore + this.lastScore - val
 				this.lastScore = val
+				this.$emit('scoreUpdate',this.surPlusScore)
 
 			},
 
@@ -487,33 +480,36 @@
 			/** 按照题型配分 */
 			onConfirmTypeScore() {
 				/** 重新计算剩余分配分数 */
-				this.surPlusScore = this.paper.score - this.groupTypeList.reduce((p, e) => p + e.score, 0)
+				this.surPlusScore = this.paperInfo.score - this.groupTypeList.reduce((p, e) => parseInt(p) + parseInt(e.score), 0)
 				if (this.surPlusScore < 0) {
 					this.$Message.warning("当前配分超过试卷总分,请重新分配!")
 				} else {
-					console.log(this.groupTypeList)
-					
 					/* 按照题型配分后平均分配给每个子题 */
 					this.groupTypeList.forEach(item => {
-						console.log(item.score)
-						item.list.forEach(exercise => {
-							console.log(exercise.score)
-							exercise.score = item.score / item.list.length
-							console.log(exercise.score)
-							if (item.id) {
-								console.log(item.score)
-								this.exerciseList.filter(item => item.id === exercise.id)[0].score = item.score / item.list.length
-							} else {
-								this.exerciseList.filter(item => item.shaCode === exercise.shaCode)[0].score = item.score / item.list.length
+						item.list.forEach((exercise,exerciseIndex) => {
+							// 先找到原始列表里面的当前题目
+							let listItem = this.exerciseList.filter(item => item.id === exercise.id)[0]
+							// 先判断是否总分除以题目数量能否除尽
+							let remainder = item.score % item.list.length
+							// 如果可以整除 则直接计算
+							if(remainder === 0){
+								exercise.score = item.score / item.list.length
+							}else{
+								// 如果不能整除 则前面所有取整 最后一题加上余数 即可完成配分
+								let integerScore = parseInt(item.score / item.list.length)
+								let lastItem = exerciseIndex === item.list.length - 1
+								exercise.score = lastItem ? integerScore + remainder : integerScore
 							}
+							listItem.score = exercise.score
 						})
 					})
-					console.log(JSON.stringify(this.groupTypeList))
+					
 					/** 回到题型视图 */
 					this.groupList = this.groupTypeList
 					this.$parent.viewModel = 'type'
 					this.$parent.paperInfo.multipleRule = this.multipleRule
 					this.typeScoreModel = false
+					this.$emit('scoreUpdate',this.surPlusScore)
 				}
 
 			},
@@ -537,10 +533,6 @@
 						this.modifyItems[existIndex] = item
 					}
 					
-					console.log('当前试卷已编辑的题目')
-					console.log(this.modifyItems)
-					console.log(this.modifyItems.map(i => i.id))
-					
 				} else {
 					this.editExerciseModal = false
 					this.exerciseList.splice(this.currentExerciseIndex, 1, item)
@@ -583,7 +575,7 @@
 						i.score = 0
 					}
 				})
-				return arr.reduce((p, e) => p + e.score, 0)
+				return arr.reduce((p, e) => p + parseInt(e.score), 0)
 			},
 
 			/**
@@ -623,11 +615,10 @@
 						this.groupList = []
 						this.exerciseList = []
 						this.orderList = []
-						console.log('list组件接收的数据')
-						console.log(newPaper)
+						this.paperInfo = newPaper
 						if (newPaper.item.length) {
 							newPaper.item.forEach(i => {
-								if(!i.score){i.score = 0}
+								if(!i.score) i.score = 0
 							})
 							this.orderList.push({ list:newPaper.item })
 							/* 处理试卷内题目按照题型排序 */
@@ -637,7 +628,7 @@
 										that.groupList.push({
 											type: key,
 											list: value,
-											score: value.reduce((p, e) => p + e.score, 0)
+											score: value.reduce((p, e) => parseInt(p) + parseInt(e.score), 0)
 										})
 										that.exerciseList = that.exerciseList.concat(value)
 									}
@@ -648,7 +639,8 @@
 						this.originData = this.exerciseList
 						this.groupTypeList = this.groupList
 						this.totalNum = newPaper.item.length
-						this.surPlusScore = newPaper.score - newPaper.item.reduce((p, e) => p + e.score, 0);
+						this.surPlusScore = newPaper.score - newPaper.item.reduce((p, e) => parseInt(p) + parseInt(e.score), 0);
+						this.$emit('scoreUpdate',this.surPlusScore)
 						this.pageScrollTo(0)
 						this.pageChange(1)
 						this.$nextTick(()=>{
@@ -690,12 +682,18 @@
 		justify-content: center;
 		margin: 20px 0;
 	}
-
+	
 	.components-el-container .type-name {
 		font-size: 18px;
 		font-weight: bold;
 		margin-top: 20px;
 	}
+	
+	.components-el-container /deep/ .ivu-input-number-handler-down-inner,
+	.components-el-container /deep/ .ivu-input-number-handler-up-inner{
+		right: 8px;
+		font-size: 16px;
+	}
 
 
 	.components-el-container .exercise-item:hover {

+ 3 - 3
TEAMModelOS/ClientApp/src/view/evaluation/index/CreatePaper.less

@@ -46,7 +46,7 @@
 }
 .create-body {
   .evaluation-attr-wrap {
-    width: 400px;
+    width: 20%;
     border-right: 1px solid @borderColor;
     height: 100%;
     padding-left: 20px;
@@ -61,7 +61,7 @@
   }
 
   .evaluation-question-wrap {
-    width: ~"calc(100% - 400px)";
+    width: 80%;
     height: 100%;
     padding-left: 20px;
 
@@ -117,7 +117,7 @@
 }
 .evaluation-question-main {
     width: 100%;
-    height: ~"calc(100% - 40px)";
+    height: ~"calc(100% - 10px)";
 
     .create-type-wrap {
         color: white;

+ 52 - 10
TEAMModelOS/ClientApp/src/view/evaluation/index/CreatePaper.vue

@@ -44,7 +44,7 @@
 			</div>
 			<div class="evaluation-question-wrap">
 				<div class="evaluation-question-main">
-					<div class="create-type-wrap">
+					<div class="create-type-wrap" v-show="!isEditPaper">
 						<span>创建方式:</span>
 						<RadioGroup v-model="evaluationInfo.createType" style="margin-left:15px;"
 						 @on-change="setActiveTab">
@@ -155,8 +155,6 @@
 				this.schoolInfo = res
 				this.onPeriodChange(0)
 			})
-			
-			console.log(await this.isPaperExist('6666'))
 		},
 		methods: {
 			/**
@@ -463,7 +461,7 @@
 
 			/** 保存当前试卷数据 */
 			async saveTestPaper() {
-				let hasSurplus = this.$refs.testPaper.$refs.exList.surPlusScore === 0 // 判断是否有剩余分数未分配
+				let hasSurplus = this.$refs.testPaper.$refs.exList.surPlusScore // 判断是否有剩余分数未分配
 				let noScoreList = this.$refs.testPaper.$refs.exList.exerciseList.filter(item => item.score === 0) // 判断是否有未配分的题目
 				let isPaperExist = await this.isPaperExist(this.evaluationInfo.name)
 				let list = this.evaluationInfo.item
@@ -471,7 +469,7 @@
 					this.$Message.warning('试卷名称不能为空!')
 					return 
 				}
-				if (hasSurplus) {
+				if (hasSurplus === 0) {
 					if (!noScoreList.length) {
 						if(!isPaperExist){
 							if (list.length) {
@@ -485,8 +483,13 @@
 							    content: '<p>试卷库已存在重复名称试卷,是否继续保存覆盖原试卷?</p>',
 							    okText: '确认',
 							    cancelText: '取消',
-							    onOk: () => {
-									this.doSavePaper(list)
+							    onOk: async () => {
+									let blobList = await this.getPaperFiles('paper/' + this.evaluationInfo.name)
+									let files = blobList.blobList.map(i => i.blob)
+									console.log(files)
+									this.onDeleteBlobPaper(files).then(r => {
+										this.doSavePaper(list)
+									})
 							    },
 								onCancel:() => {
 									this.$Message.warning('取消保存试卷')
@@ -497,10 +500,50 @@
 						this.$Message.warning(`存在未配分的题目,请配分后再保存!`)
 					}
 				} else {
-					this.$Message.warning(`试卷配分未完成!剩余 ${this.$refs.testPaper.$refs.exList.surPlusScore} 分数可分配`)
+					this.$Message.warning(`试卷配分未完成!剩余 ${hasSurplus} 分数可分配`)
 				}
 			},
 			
+			getPaperFiles(path){
+				return new Promise(async (r,j) => {
+					// 获取初始化Blob需要的数据
+					let sasData = this.isSchool ? await this.$tools.getSchoolSas() : await this.$tools.getPrivateSas()
+					//初始化Blob
+					let containerClient = new blobTool(sasData.url, sasData.name, sasData.sas , this.isSchool ? 'school' : 'private')
+					// 等待blob的返回结果
+					containerClient.listBlob({
+					    prefix: path
+					}).then(
+					    (res) => {
+					        r(res)
+					    },
+					    (err) => {
+					        this.$Message.error('API Error')
+					    }
+					)
+				})
+				
+			},
+			
+			onDeleteBlobPaper(files){
+				return new Promise(async (r,j) => {
+					// 获取初始化Blob需要的数据
+					let sasData = this.isSchool ? await this.$tools.getSchoolSas() : await this.$tools.getPrivateSas()
+					//初始化Blob
+					let containerClient = new blobTool(sasData.url, sasData.name, sasData.sas , this.isSchool ? 'school' : 'private')
+					// 等待blob的返回结果
+					containerClient.deleteBlobBatch(files).then(
+					    (res) => {
+					        r(res)
+					    },
+					    (err) => {
+					        this.$Message.error('API Error')
+					    }
+					)
+				})
+				
+			},
+			
 			/* 保存试卷业务 */
 			async doSavePaper(list){
 				this.isLoading = true
@@ -556,7 +599,7 @@
 							Promise.all(promiseArr).then(async result => {
 								if (blobFile.blob) {
 									// 保存到COSMOS是不含base64图片编码的数据 避免数据量过大
-									paperItem.blob = blobFile.blob
+									paperItem.blob = blobFile.blob.split('/index.json')[0]
 									let params = {
 										paper:await this.$evTools.createCosmosPaper(paperItem),
 										option: this.isEditPaper ? 'update' : 'insert'
@@ -764,7 +807,6 @@
 	.create-evaluation-container .ivu-input-number-input,
 	.create-evaluation-container .ivu-input-number {
 		width: 100%;
-		color: #fff;
 		background: none;
 		border-color: #999999;
 	}

+ 5 - 4
TEAMModelOS/ClientApp/src/view/evaluation/index/PickExercise.css

@@ -120,6 +120,7 @@
 }
 
     .content-wrap .exercise-item {
+		position: relative;
         width: 100%;
         height: auto;
         padding: 10px 20px 10px 20px;
@@ -169,13 +170,13 @@
 		vertical-align: middle;
 	}
 
-    .exercise-item .item-question .item-btn-toggle {
+    .exercise-item .item-btn-toggle {
         position: absolute;
-        right: 0;
-        top:0;
+        right: 20px;
+        top:8px;
     }
 
-        .exercise-item .item-question .item-btn-toggle .ivu-icon {
+        .exercise-item  .item-btn-toggle .ivu-icon {
             font-size: 22px;
             cursor: pointer;
         }

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

@@ -153,24 +153,25 @@
     flex-direction: row;
 
     .paper-body {
-        width: 94%;
-        padding-left: 50px;
+        width: 98%;
+        padding-left: 20px;
 
         .paper-header{
             margin-top:20px;
         }
-		
-		
-		
     }
 
     .paper-base-info {
         height: 60px;
+		position: sticky;
+		top: 0;
         display: flex;
         flex-direction: row;
         justify-content: space-between;
         align-items: center;
+		background-color: #fff;
         border-bottom: 1px dashed #cfcfcf;
+		z-index: 99;
 
         .base-info-item:not(:first-child) {
             margin-left: 30px;

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

@@ -6,16 +6,17 @@
 		<div class="paper-main-wrap">
 			<!-- 试卷内容 -->
 			<div class="paper-content">
-				<div class="paper-line" style="margin-top:7%">
+<!-- 				<div class="paper-line" style="margin-top:7%">
 					<div id="pui_seal" title="点击设置&quot;装订线&quot;" style="display: block;">
 						<img alt="装订线" title="装订线" src="../../../assets/image/peal_line.png">
 					</div>
-				</div>
+				</div> -->
 				<div class="paper-body">
 					<!-- 试卷基础信息 -->
 					<div class="paper-base-info" v-show="isShowBaseInfo">
 						<div>
-							<span class="base-info-item">分数:<span class="analysis-info" style="cursor: pointer;" @click="isSetScore = !isSetScore">{{ paperInfo.score }}</span>分</span>
+							<span class="base-info-item">总分:<span class="analysis-info" style="cursor: pointer;" @click="isSetScore = !isSetScore">{{ paperInfo.score }}</span>分</span>
+							<span class="base-info-item">已配分:<span class="analysis-info" >{{ allocatedScore || 0 }}</span>分</span>
 							<span class="base-info-item">题量:<span class="analysis-info">{{ paperInfo.item ? paperInfo.item.length : 0 }}</span></span>
 							<span class="base-info-item">难度:<span class="analysis-info">{{ paperInfo.item.length ? paperDiff : 0 }}</span></span>
 						</div>
@@ -30,14 +31,14 @@
 
 					<!-- 试卷头部信息 -->
 					<div class="paper-header flex-col-center">
-						<p class="paper-title" @click="isSetPaperName = true">{{paperInfo.name}}</p>
+						<p class="paper-title">{{paperInfo.name}}</p>
 					</div>
 					
 					<ExamPaperAnalysis :testPaper="paperInfo" v-show="isShowAnalysis"></ExamPaperAnalysis>
 					
 					<!-- 题目类型及列表 -->
 					<BaseExerciseList :paper="paperInfo" @dataUpdate="onListUpdate" v-show="!isShowAnalysis" ref="exList" :isShowTools="isShowTools"
-					 @toggleChange="onToggleChange"></BaseExerciseList>
+					 @toggleChange="onToggleChange" @scoreUpdate="scoreUpdate"></BaseExerciseList>
 				</div>
 			</div>
 		</div>
@@ -53,14 +54,14 @@
 		</Modal>
 		
 		<!-- 设置试卷标题 -->
-		<Modal v-model="isSetPaperName" title="设置试卷名称" width="400px" class="related-point-modal" style="z-index:99999">
+<!-- 		<Modal v-model="isSetPaperName" title="设置试卷名称" width="400px" class="related-point-modal" style="z-index:99999">
 			<span>试卷名称</span>
 			<Input v-model="paperInfo.name" style="margin:20px 0"></Input>
 			<div slot="footer">
 				<Button type="text" @click="isSetPaperName = false">取消</Button>
 				<Button type="primary" @click="isSetPaperName = false">确定</Button>
 			</div>
-		</Modal>
+		</Modal> -->
 	</div>
 </template>
 <script>
@@ -97,6 +98,7 @@
 				isSetRules: false,
 				isShowSave: false,
 				exersicesList: [],
+				allocatedScore:0,
 				list: [],
 				schoolInfo: {},
 				paperInfo: {
@@ -263,6 +265,10 @@
 			handleDiffCalc(arr) {
 				let levelArr = arr.map(i => i.level)
 				return this._.meanBy(levelArr).toFixed(1)
+			},
+			
+			scoreUpdate(score){
+				this.allocatedScore = this.paperInfo.score - score
 			}
 
 
@@ -275,7 +281,6 @@
 			localStorage.setItem('_paperInfo', JSON.stringify(paper))
 			this.paperInfo = paper // 自己页面的值
 			this.paperDiff = paper.item ? this.handleDiffCalc(paper.item) : 0
-
 		},
 		computed: {
 			isAuto() {
@@ -288,8 +293,6 @@
 		watch: {
 			paper: {
 				handler(newValue, oldValue) {
-					console.log('paper组件接收的值')
-					console.log(newValue)
 					this.paperInfo = newValue
 					localStorage.setItem('_paperInfo', JSON.stringify(newValue))
 					this.paperDiff = newValue.item ? this.handleDiffCalc(newValue.item) : 4
@@ -306,30 +309,4 @@
 		padding: 0 45px;
 		border-bottom: 2px solid rgb(128, 128, 128);
 	}
-	
-/* 	.paper-body .whole-exam-score{
-		color: #000 !important;
-		background: #fff !important;
-	}
-	
-	.paper-body .whole-table-col{
-		color: #000 !important;
-		background: #fff !important;
-	}
-	
-	.paper-body .question-type-table-row{
-		color: #000 !important;
-		background: #fff !important;
-	}
-	
-	.paper-body .question-type-table-td{
-		color: #000 !important;
-		background: #fff !important;
-	}
-	
-	.paper-body .dark-iview-table .ivu-table td,
-	.paper-body .dark-iview-table .ivu-table th{
-		color: #000 !important;
-		background: #fff !important;
-	} */
 </style>

+ 23 - 15
TEAMModelOS/ClientApp/src/view/homepage/HomePage.vue

@@ -244,14 +244,17 @@
                             <li>
                                 <div style="display:flex;justify-content:space-between;">
                                     <p>
-                                        <span class="notice-title">IES5系统公告</span>
+                                        <span class="notice-title">IES5内测</span>
                                         <span class="notice-dot"></span>
                                     </p>
                                     <span class="notice-time">2020-09-09 18:02</span>
                                 </div>
-                                <p class="notice-text long-text">Hi帮手是醍摩豆(TEAM Model)智慧教室系统相关的软件安装、注册(激活)授权、升级的好帮手!</p>
+                                <p class="notice-text long-text">
+                                    2020.12.01-2020.12.31为IES5内测阶段!
+                                </p>
                             </li>
-                            <li>
+                            <li v-if="$store.state.user.userProfile.schools.length == 0" @click="toJoinSchool">
+                                <!--<li v-if="$store.state.user.userProfile.schools.length == 0" @click="toJoinSchool">-->
                                 <div style="display:flex;justify-content:space-between;">
                                     <p>
                                         <span class="notice-title">加入学校提醒</span>
@@ -295,21 +298,21 @@
                             path: '/home/manageVote'
                         })
                         break
-                    case 'hw':
-                        this.$router.push({
-                            path: '/home/manageHomeWork'
-                        })
-                        break
+                    //case 'hw':
+                    //    this.$router.push({
+                    //        path: '/home/manageHomeWork'
+                    //    })
+                    //    break
                     case 'que':
                         this.$router.push({
                             path: '/home/manageQuestionnaire'
                         })
                         break
-                    case 'learn':
-                        this.$router.push({
-                            path: '/home/SelfLearn'
-                        })
-                        break
+                    //case 'learn':
+                    //    this.$router.push({
+                    //        path: '/home/SelfLearn'
+                    //    })
+                    //    break
                     case 'test':
                         this.$router.push({
                             path: '/home/manageEvaluation'
@@ -325,13 +328,18 @@
                 })
             },
             toSelfLearn() {
+                //this.$router.push({
+                //    path: '/home/SelfLearn'
+                //})
+            },
+            toJoinSchool() {
                 this.$router.push({
-                    path: '/home/SelfLearn'
+                    path: '/home/settings'
                 })
             }
         },
         created() {
-
+            console.log(this.$store)
         }
     }
 </script>

+ 21 - 17
TEAMModelOS/ClientApp/src/view/learnactivity/AutoCreate.vue

@@ -10,7 +10,7 @@
                     <div class="question-condition-item question-num-item" v-for="(items,indexs) in quePerson" :key="indexs">
                         <span class="condition-label">{{items.name}}:</span>
                         <span class="question-difficulty-label">难度</span>
-                        <Select v-model="items.level" style="width:90px" size="small" @on-change="getData">
+                        <Select v-model="items.level" style="width:90px" size="small" >
                             <Option v-for="(item,index) in difficultyList" :value="item.value" :key="index">{{ item.label }}</Option>
                         </Select>
                         <span class="input-tag-text total-text-tag">共</span>
@@ -37,22 +37,22 @@
                             <Radio v-for="(item,index) in $store.state.schoolBaseInfo.schoolBaseInfo.period" :label="item.id" :key="index" style="margin-right:30px;"> {{item.name}}</Radio>
                         </RadioGroup>
                     </div>
-                    <div class="question-condition-item question-num-item ">
+                    <div class="question-condition-item question-num-item " v-if="schoolBaseInfo.length > 0">
                         <span class="condition-label">适用科目:</span>
                         <Select style="width:90px" size="small" v-model="autoCreateFilter.subjectId">
-                            <Option v-for="(item,index) in $store.state.schoolBaseInfo.schoolBaseInfo.period[Number(autoCreateFilter.periodCode) - 1].subjects" :value="item.id" :key="index">{{item.name }}</Option>
+                            <Option v-for="(item,index) in schoolBaseInfo[Number(autoCreateFilter.periodCode) - 1].subjects" :value="item.id" :key="index">{{item.name }}</Option>
                         </Select>
                     </div>
-                    <div class="question-condition-item question-num-item" >
+                    <div class="question-condition-item question-num-item"  v-if="schoolBaseInfo.length > 0">
                         <span class="condition-label">适用年級:</span>
                         <Select multiple style="min-width:90px;max-width:200px" size="small" v-model="autoCreateFilter.gradesId">
-                            <Option v-for="(item,index) in $store.state.schoolBaseInfo.schoolBaseInfo.period[Number(autoCreateFilter.periodCode)].grades" :value="item.id" :key="index">{{item.name }}</Option>
+                            <Option v-for="(item,index) in schoolBaseInfo[Number(autoCreateFilter.periodCode)].grades" :value="item.id" :key="index">{{item.name }}</Option>
                         </Select>
                     </div>
                     <div class="question-condition-item question-num-item" v-for="(items,indexs) in queSchool" :key="indexs">
                         <span class="condition-label">{{items.name}}:</span>
                         <span class="question-difficulty-label">难度</span>
-                        <Select v-model="items.level" style="width:90px" size="small" @on-change="getData">
+                        <Select v-model="items.level" style="width:90px" size="small">
                             <Option v-for="(item,index) in difficultyList" :value="item.value" :key="index">{{ item.label }}</Option>
                         </Select>
                         <span class="input-tag-text total-text-tag">共</span>
@@ -103,9 +103,7 @@
                 isSchool: false,
                 checkedPoints: [],
                 isLoading: false,
-                comfirmPreviewStatus: false,
                 activePointTab: 1,
-                openKeys: [],
                 pointsInBlockList: [],
                 pointBlockList: [],
                 pointTab: 'pioints',
@@ -244,17 +242,23 @@
                         label: '较难',
                         value: 5
                     }
-                ]
+                ],
+                schoolBaseInfo:[]
             }
         },
-		created() {
-		},
         methods: {
-            getData(data) {
-                //console.log(data),
-                //    console.log(this.queType)
-                //console.log(this.single)
-                //console.log(this.$store.state.schoolBaseInfo.schoolBaseInfo.period)
+            async getData() {
+                await this.$store.dispatch('schoolBaseInfo/getSchoolBaseData').then(
+                    (res) => {
+                        this.schoolBaseInfo = this.$store.state.schoolBaseInfo.schoolBaseInfo.period
+                        if (res.code == 2) {
+                            alert('数据为空!')
+                        }
+                    },
+                    (err) => {
+                        this.$Message.error('API error!')
+                    }
+                )
             },
             cancel() {
 
@@ -414,7 +418,7 @@
             }
         },
         mounted() {
-			console.log(this.subjectCode)
+            this.getData()
         }
     }
 </script>

+ 10 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/CreateEvaluation.less

@@ -108,6 +108,16 @@
         }
     }
 }
+.evaluation-question-main .ivu-tabs-bar {
+    border-color: #404040;
+}
+
+.evaluation-question-main .ivu-tabs.ivu-tabs-card > .ivu-tabs-bar .ivu-tabs-tab {
+    border: none;
+    background-color: #303030;
+    color: white;
+    margin-right: 2px;
+}
 
 .evaluation-attr-form {
     /*margin-top:30px;*/

+ 112 - 81
TEAMModelOS/ClientApp/src/view/learnactivity/CreateEvaluation.vue

@@ -8,8 +8,8 @@
         <div class="create-body">
             <div class="evaluation-attr-wrap">
                 <p class="wrap-label">基础信息</p>
-                <div style="width:100%; height:calc(100% - 45px);padding-top:30px;">
-                    <Form ref="evaluationInfo" :model="evaluationInfo" label-position="top" class="evaluation-attr-form" label-colon :rules="ruleValidate">
+                <div style="width:100%; height:calc(100% - 45px);padding-top:30px;" class="dark-iview-form">
+                    <Form ref="evaluationInfo" :model="evaluationInfo" label-position="top" class="evaluation-attr-form " label-colon :rules="ruleValidate">
                         <FormItem label="评测名称" prop="name">
                             <Input v-model="evaluationInfo.name" placeholder="评测名称" @on-change="handlePaperName"></Input>
                         </FormItem>
@@ -78,7 +78,7 @@
                     </div>
 
 
-                    <Tabs v-model="activeTab" type="card" class="question-main-tabs" v-if="evaluationInfo.paperInfo.length > 0 || mode == 'class' " name="createTest">
+                    <Tabs v-model="activeTab" type="card" class="question-main-tabs dark-iview-tab" v-if="evaluationInfo.paperInfo.length > 0 || mode == 'class' " name="createTest">
                         <!--<TabPane label="组题条件" name="auto" v-if="evaluationInfo.paperInfo[currentSubjectIndex].createType == 'auto'" :index="1" tab="createTest">
                             <AutoCreate :subjectCode="evaluationInfo.paperInfo[currentSubjectIndex].subjectCode" :periodCode="evaluationInfo.paperInfo[currentSubjectIndex].periodCode" @goToPreview="goToPreview" @autoQuestions="getAutoQuestions"></AutoCreate>
                         </TabPane>-->
@@ -227,15 +227,15 @@
                 classroomList: [],
                 typeList: [
                     {
-                        value: '1',
+                        value: 'regular',
                         label: '正規考'
                     },
                     {
-                        value: '2',
+                        value: 'simulation',
                         label: '模擬考'
                     },
                     {
-                        value: '3',
+                        value: 'normal',
                         label: '普通考'
                     }
                 ],
@@ -265,55 +265,7 @@
                 config: {
                     clearable: true,
                     multiple: true,
-                    data: [
-                        {
-                            value: 'beijing',
-                            label: '北京',
-                            children: [
-                                {
-                                    value: 'gugong',
-                                    label: '故宫'
-                                },
-                                {
-                                    value: 'tiantan',
-                                    label: '天坛'
-                                },
-                                {
-                                    value: 'wangfujing',
-                                    label: '王府井'
-                                }
-                            ]
-                        }, {
-                            value: 'jiangsu',
-                            label: '江苏',
-                            children: [
-                                {
-                                    value: 'nanjing',
-                                    label: '南京',
-                                    children: [
-                                        {
-                                            value: 'fuzimiao',
-                                            label: '夫子庙',
-                                        }
-                                    ]
-                                },
-                                {
-                                    value: 'suzhou',
-                                    label: '苏州',
-                                    children: [
-                                        {
-                                            value: 'zhuozhengyuan',
-                                            label: '拙政园',
-                                        },
-                                        {
-                                            value: 'shizilin',
-                                            label: '狮子林',
-                                        }
-                                    ]
-                                }
-                            ],
-                        }
-                    ],
+                    data: [],
                     placeholder: '请选择施測對象',
                     style: "width:100%"
                 },
@@ -323,8 +275,19 @@
                     scope: [],
                     end_date: ''
                 },
-                open: false
-
+                open: false,
+                map: {
+                    value: 'id',
+                    values:'gradeId',
+                    label: 'name',
+                    children: 'class'
+                },   
+                //map: {
+                //    value: 'id',
+                //    values:'gradeId',
+                //    label: 'name',
+                //    children: 'class'
+                //}
             }
         },
         methods: {
@@ -333,14 +296,14 @@
              * */
             setChangeScope(value) {
                 console.log(value)
-                let self = this;
-                let data = [];
-                if (value) {
-                    for (let i = 0; i < value.length; i++) {
-                        data.push(value[i].value)
-                    }
-                    self.formChange.scope = data
-                }
+                //let self = this;
+                //let data = [];
+                //if (value) {
+                //    for (let i = 0; i < value.length; i++) {
+                //        data.push(value[i].value)
+                //    }
+                //    self.formChange.scope = data
+                //}
             },
             changeSon() {
                 this.open = false
@@ -538,9 +501,6 @@
                     progress: 'going',
                     scope: this.mode,
                     create: Math.round(new Date() / 1000)
-
-
-
                 }
                 for (let item of this.newAddSubjects) {
                     this.evaluationInfo.paperInfo.push({
@@ -561,6 +521,7 @@
             cancelAddSubject() {
                  this.addSubjectBefore = [...this.testSubjects]
             },
+            //获取学校基本信息
             getSchoolBaseInfo() {
                 this.$store.dispatch('schoolBaseInfo/getSchoolBaseData').then(
                     (res) => {
@@ -576,12 +537,42 @@
                     (res) => {
                         if (res.code == 2) {
                             alert('数据为空!')
+                        } else {
+                            //拼接年級和班級信息
+                            let grade = []
+                            let grades = []
+
+                            let classData = this.$jsFn.groupBy(res.data, 'gradeId')
+                            for (let item of this.$store.state.schoolBaseInfo.schoolBaseInfo.period) {
+                                grade.push(...item.grades)
+                            }
+
+                            for (let i = 0; i < grade.length; i++) {
+                                for (let j = 0; j < classData.length; j++) {
+                                    if (grade[i].id == classData[j][0].gradeId) {
+                                        grade[i].class = classData[j]
+                                    }
+                                }
+                                if (grade[i].class !== undefined) {
+                                    let all = {
+                                        'id': 'all-' + grade[i].id,
+                                        'gradeId': grade[i].class[0].gradeId,
+                                        'name': grade[i].name + '(全年级)'
+                                    }
+                                    grade[i].class.unshift(all)
+                                    grades.push(grade[i])
+                                }
+                            }
+
+                            this.config.data = this.convertTree(grades, this.map)
+                            console.log(this.config.data)
                         }
                     },
                     (err) => {
                         this.$Message.error('API error!')
                     }
                 )
+                console.log(this.$store.state)
             },
             /**
              * 查找教师课程下的班级
@@ -708,6 +699,29 @@
             saveEvaluation() {
                 console.log('最後數據')
                 console.log(this.evaluationInfo)
+                let data = {
+                    pk: 'Exam',
+                    code: "Exam-" + this.$store.state.userInfo.schoolCode,
+                    school: this.$store.state.userInfo.schoolCode,
+                    id: this.$jsFn.uuid(),
+                    name: this.evaluationInfo.name,
+                    creatorId: this.$store.state.userInfo.TEAMModelId,
+                    type: "union",
+                    period: {},
+                    grades: [],
+                    subjects: [],
+                    examType: {},
+                    year: new Date().getFullYear(),
+                    range: this.mode,
+                    source: this.evaluationInfo.evaType,
+                    targetClasseIds: [],
+                    startTime: this.evaluationInfo.startTime,
+                    endTime: this.evaluationInfo.endTime,
+                    progress: 'going',
+                    scope: this.mode,
+                    create: Math.round(new Date() / 1000)
+                }
+                console.log(data)
                 //if (this.checkData()) {
                 //    this.handleData()
 
@@ -719,6 +733,33 @@
                 [...this.addSubjectBefore] = this.testSubjects
                 this.addSubjectStatus = true
 
+            },
+            //处理树结构数据
+            convertTree(tree, map) {
+                let result = []
+                // 遍历 tree
+                tree.forEach((item) => {
+                    // 读取 map 的键值映射
+                    let value = item[map.value]
+                    let label = item[map.label]
+                    let children = item[map.children]
+                    // 如果有子节点,递归
+                    if (children !== undefined) {
+                        children = this.convertTree(children, map)
+                    }
+                    if (children !== undefined) {
+                        children.map(item => {
+                            item['type'] = 'class'
+                            item = delete item.children
+                        })
+                    }
+                    result.push({
+                        value,
+                        label,
+                        children
+                    })
+                })
+                return result
             }
         },
         created() {
@@ -755,13 +796,13 @@
 <style scoped lang="less">
     @import "./CreateEvaluation.less";
 </style>
-<style>
-    .evaluation-attr-form .ivu-input, .evaluation-attr-form .ivu-select-selection {
+<style scoped>
+/*    .evaluation-attr-form .ivu-input, .evaluation-attr-form .ivu-select-selection {
         background: none;
         color: white;
         border-color: #999999;
         font-size: 16px;
-    }
+    }*/
 
     .evaluation-attr-form .ivu-input {
         /*border:none;
@@ -773,16 +814,6 @@
         color: #a5a5a5;
     }
 
-    .evaluation-question-main .ivu-tabs-bar {
-        border-color: #404040;
-    }
-
-    .evaluation-question-main .ivu-tabs.ivu-tabs-card > .ivu-tabs-bar .ivu-tabs-tab {
-        border: none;
-        background-color: #303030;
-        color: white;
-        margin-right: 2px;
-    }
 
     .evaluation-question-main .ivu-tabs.ivu-tabs-card > .ivu-tabs-bar .ivu-tabs-tab-active {
         background-color: #404040;

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

@@ -186,7 +186,6 @@
             /**对试卷题型分组 */
             groupQuestionByType() {
                 this.groupQuestion = {}
-                console.log(this.testPaper)
                 let groupResult = this.$jsFn.groupBy(this.testPaper.item, 'type')
                 for (let i = 0; i < groupResult.length; i++) {
                     this.$set(this.groupQuestion, groupResult[i][0].type, groupResult[i])

+ 3 - 3
TEAMModelOS/ClientApp/src/view/learnactivity/ManualCreate.vue

@@ -332,10 +332,10 @@
                 setTimeout(() => {
                     this.shoppingCarClass = 'question-shopping-car'
                 }, 1000)
-                let groupResult = await this.$jsFn.groupBynew(this.shoppingQuestionList, 'type')
-
+                let groupResult = await this.$jsFn.groupBy(this.shoppingQuestionList, 'type')
+                console.log(groupResult)
                 for (let i = 0; i < groupResult.length; i++) {
-                   this.groupList[groupResult[i][0].exercise.type]= groupResult[i].length
+                   this.groupList[groupResult[i][0].type]= groupResult[i].length
                 }
                 console.log(this.shoppingQuestionList)
                 console.log(data)

+ 42 - 11
TEAMModelOS/ClientApp/src/view/learnactivity/MultiCascader.vue

@@ -8,12 +8,13 @@
                 <Cascader :data="data"
                           style="display: inline-block; width: 100%"
                           v-model="cascader"
-                          change-on-select
+
                           @on-visible-change="toggleMenu"
-                          @on-change="(value, selectedData) => handleChange(value, selectedData)" transfer>
+                          transfer
+                          @on-change="(value, selectedData) => handleChange(value, selectedData)" >
                     <span v-show="noSel && !filterable" :class="[prefixCls + '-placeholder']" style="display: inline">{{ placeholder }}</span>
                     <div v-if="noSel" class="ivu-tag" style="background: none;"></div>
-                    <div class="ivu-tag" v-for="(item, index) in selData" :key="item.value" style="display: inline-block;background: #efefef">
+                    <div class="ivu-tag" v-for="(item, index) in selData" :key="item.value" style="display: inline-block;color:#fffff">
                         <span class="ivu-tag-text">{{ item.name }}</span>
                         <span class="ivu-tag-text">{{ item.label }}</span>
                         <Icon type="ios-close-empty" @click.native.stop="removeTag(index)"></Icon>
@@ -207,7 +208,7 @@
         let data = [];
         if (value) {
           for (let i = 0; i < value.length; i++) {
-            let ob = {value: value[i].id, label: value[i].name}
+              let ob = { value: value[i].id, label: value[i].name, type: value[i].type, _value: value[i]._value}
             data.push(ob);
           }
         }
@@ -217,7 +218,10 @@
         else self.noSel = true;
         self.selData = data.concat()
         self.propData = data.concat()
-        self.setValue(self.propData);
+          self.setValue(self.propData);
+          console.log(self.selData)
+          console.log(self.propData)
+          console.log(self.setValue)
       },
       // 设置值
       setValue(val) {
@@ -256,9 +260,10 @@
         return false;
       },
       handleChange (value, selectedData) {
-        let self = this;
+          let self = this;
+          console.log(selectedData)
         let val = selectedData[selectedData.length-1];
-        let _val = {value:val.value, label: val.label};
+          let _val = { value: val.value, label: val.label, type: val.type, _value: val._value};
         if (!self.isInArray(self.selData, val)) {
           if (self.addTag) {
             self.addTag = false;
@@ -272,8 +277,30 @@
             self.propData[self.propData.length-1] = _val
             self.setValue(self.propData);
           }
-        }
-      },
+          }
+          let reData = []
+          for (let i = 0; i < self.selData.length; i++) {
+              if (self.selData[i].type == undefined) {
+                 reData.push(self.selData[i])
+              }
+          }
+          this.getSelectData(reData)
+        },
+        getSelectData(data) {
+            let self = this
+            for (let i = 0; i < data.length; i++) {
+                for (let k = 0; k < self.selData.length; k++) {
+                    if (self.selData[k].__value !== undefined) {
+                        let str = self.selData[k].__value
+                        let code = str.split(',')
+                        if (data[i].value === code[0]) {
+                            self.selData.splice(k, 1); 
+                            k--; // 如果不减,将漏掉一个元素
+                        }
+                    }
+                }
+            }
+        },
       toggleMenu (visible) {
         let self = this;
         if (this.disabled || this.autoComplete) {
@@ -289,7 +316,7 @@
     }
   }
 </script>
-<style lang="less">
+<style lang="less" scoped>
     .MultiCascader {
         width: 100%;
         position: relative;
@@ -308,7 +335,7 @@
         padding: 5px;
         position: relative;
         cursor: pointer;
-        background-color: white;
+        /*background-color: white;*/
         transition: border 0.2s ease-in-out, background 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
     }
 
@@ -319,4 +346,8 @@
         width: 100%;
         padding-right: 5px;
     }
+    .ivu-tag-text {
+    color: #ffff;
+    font-size: 14px;
+}
 </style>

+ 1 - 0
TEAMModelOS/ClientApp/src/view/login/Index.vue

@@ -539,6 +539,7 @@ export default {
 
       //設定權限並登入
       let identity = localStorage.getItem('identity')
+	  console.log(result)
       User.login(result).then(res => {
         if(res && identity) {
 			this.saveUserCodes({

+ 3 - 0
TEAMModelOS/ClientApp/src/view/newcourse/MyCourse.less

@@ -127,4 +127,7 @@
         font-weight: 600;
         border-bottom: 2px solid white;
     }
+}
+.action-btn-wrap {
+    margin-left:20px;
 }

+ 70 - 13
TEAMModelOS/ClientApp/src/view/newcourse/MyCourse.vue

@@ -27,8 +27,8 @@
                                     {{item[0].courseId}}
                                 </p>
                             </div>
-                            <div v-if="courseListS.length == 0" style="width:100%;text-align:center;padding-top:180px;">
-                                <EmptyData :top="0" textContent="暂无课程"></EmptyData>
+                            <div v-if="courseListS.length == 0">
+                                <EmptyData :top="150" textContent="暂无学校课程"></EmptyData>
                             </div>
                         </TabPane>
                         <TabPane label="个人课程" name="private">
@@ -42,8 +42,8 @@
                                     {{item.id}}
                                 </p>
                             </div>
-                            <div v-if="courseListP.length == 0" style="width:100%;text-align:center;padding-top:180px;">
-                                <EmptyData :top="0" textContent="暂无课程"></EmptyData>
+                            <div v-if="courseListP.length == 0">
+                                <EmptyData :top="150" textContent="暂未创建课程"></EmptyData>
                             </div>
                         </TabPane>
                     </Tabs>
@@ -82,17 +82,37 @@
                         </p>
                         <Icon type="md-qr-scanner" class="qr-code-icon" @click="showQrCode" v-if="'private' == item.scope" />
                     </div>
-                    <EmptyData v-if="classList.length == 0" :top="0" textContent="暂无上课班级" style="padding-top:0px;height:400px;"></EmptyData>
+                    <div v-if="classList.length == 0">
+                        <EmptyData :top="160" textContent="暂无上课班级"></EmptyData>
+                    </div>
                 </div>
             </div>
             <div class="course-classroom-info" id="table-height">
                 <div class="course-classroom-info-header" style="padding-right:50px;">
                     <span @click="tabName = 'record'" :class="tabName == 'record' ? 'course-classroom-label line-bottom line-bottom-active':'course-classroom-label line-bottom'">课堂记录</span>
                     <span @click="tabName = 'stus'" :class="tabName == 'stus' ? 'course-classroom-label line-bottom line-bottom-active':'course-classroom-label line-bottom'">{{$t('courseManage.classroom.studentList')}}</span>
+                    <div style="float:right;" v-if="listType == 'private'">
+                        <span class="disable-text-icon action-btn-wrap">
+                            <Icon type="md-add" size="16" />
+                            添加学生
+                        </span>
+                        <span class="disable-text-icon action-btn-wrap">
+                            <Icon type="md-trash" size="16" />
+                            删除学生
+                        </span>
+                        <!--<span class="disable-text-icon action-btn-wrap">
+                            <Icon type="md-shuffle" size="16" />
+                            自动分组
+                        </span>
+                        <span class="disable-text-icon action-btn-wrap">
+                            <Icon :type="viewType ? 'md-card':'md-list'" size="16" />
+                            分组视图
+                        </span>-->
+                    </div>
                 </div>
                 <div class="course-classroom-info-content dark-iview-table animated fadeIn" v-show="tabName == 'stus'">
                     <vuescroll style="height:100%;">
-                        <Table :columns="studentColumn" v-if="courseListS[curCusIndex] && courseListS[curCusIndex][curClassIndex]" :data="courseListS[curCusIndex][curClassIndex].students" 
+                        <Table :columns="studentColumn" :data="students" 
                                :height="tableHeight" class="system-classroom-table" :loading="stuLoading" no-data-text="暂无学生">
                             <Loading slot="loading" bgColor="rgba(103, 103, 103, 0.27)"></Loading>
                             <template slot-scope="{ row ,index}" slot="action">
@@ -127,13 +147,14 @@
                                 </ListItemMeta>
                             </ListItem>
                         </List>
+                        <p style="width:100%;text-align:center;color:#808080;margin-top:20px;">暂未对接HiTeach上传数据</p>
                     </vuescroll>
                 </div>
             </div>
         </div>
         <Drawer title="课程信息" class-name="dark-iview-drawer" width="450" :closable="false" v-model="showCusInfo" @on-close="baseEditStatus = true">
             <!--基础信息-->
-            <div class="course-base-info-content dark-iview-form disabled-iview-select dark-wang-editor" v-if="courseListS[curCusIndex] && courseListP[curCusIndex]">
+            <div class="course-base-info-content dark-iview-form disabled-iview-select dark-wang-editor" >
                 <Form ref="courseBaseInfo" label-position="top">
                     <FormItem label="课程名称" prop="name" class="disabled-iview-select">
                         <Input v-if="listType == 'school' && courseListS[curCusIndex]" v-model="courseListS[curCusIndex][0].courseName" :disabled="true" style="width:100%" placeholder="课程名称" :clearable="true"></Input>
@@ -277,7 +298,6 @@
                         align: 'center'
                     }
                 ],
-                students: [],
                 itemNum: 5,
                 editClassStatus: false,
                 baseEditStatus: true,
@@ -570,6 +590,7 @@
             
             //课程选项卡点击事件
             tabClick(name) {
+                this.curClassIndex = 0
                 this.curCusIndex = 0
                 if (name == 'school' && this.courseListS.length == 0) {
                     this.getCourseList()
@@ -591,19 +612,38 @@
             },
             //删除个人课程
             delCourse() {
-                let _this = this
                 this.$Modal.confirm({
                     title: '删除课程',
                     content: `确认删除${this.courseListP[this.curCusIndex].name}吗?`,
-                    onOk: function () {
-                        _this.$Message.warning('暂未对接API')
+                    onOk:()=> {
+                        this.$Message.warning('暂未对接API')
+                        this.$api.courseMgmt.deleteCourse({
+                            id: '',
+                            code: '',
+                            scope: ''
+                        }).then(
+                            res => {
+                                if (!res.error) {
+                                    let index = this.curCusIndex
+                                    this.courseListP.splice(index,1)
+                                    this.$Message.success('删除成功!')
+                                } else {
+                                    this.$Message.error('删除失败!')
+                                }
+                            },
+                            err => {
+                                this.$Message.error('删除失败!')
+                            }
+                        )
                     }
                 })
             },
             selectCourse(index) {
                 this.curClassIndex = 0
                 this.curCusIndex = index
-                
+                if (this.classList.length > 0) {
+                    this.changeClassroom(0)
+                }
             },
             //添加个人课程
             showAddCourse() {
@@ -672,7 +712,7 @@
                     (res) => {
                         if (res.error == null) {
                             this.courseListS = this.$jsFn.groupBy(res.courses, 'courseId')
-                            console.log(this.courseListS)
+                            this.selectCourse(0)
                         } else {
                             this.$Message.error('API ERROR!')
                         }
@@ -708,6 +748,23 @@
                         return []
                     }
                 }
+            },
+            students() {
+                if (this.listType == 'school') {
+                    if (this.courseListS[this.curCusIndex] && this.courseListS[this.curCusIndex][this.curClassIndex]) {
+                        return this.courseListS[this.curCusIndex][this.curClassIndex].students
+                    } else {
+                        return []
+                    }
+                } else if (this.listType == 'private') {
+                    if (this.courseListP[this.curCusIndex] && this.courseListP[this.curCusIndex].classes && this.courseListP[this.curCusIndex].classes[this.curClassIndex].students) {
+                        return this.courseListP[this.curCusIndex].classes[this.curClassIndex].students
+                    } else {
+                        return []
+                    }
+                } else {
+                    return []
+                }
             }
         }
     }

+ 90 - 46
TEAMModelOS/ClientApp/src/view/schoolmgmt/ClassroomSetting/ClassroomSetting.less

@@ -3,7 +3,7 @@
 @primary-textColor: #fff; //文本主颜色
 @second-textColor: #a5a5a5; //文本副级颜色
 @third-textColor: #dddddd;//其他颜色
-@fourth-textColor: #6BDFC3;
+@fourth-textColor: #1cc0f3;
 @primary-fontSize: 14px;
 @second-fontSize: 16px;
 @large-fontSize: 20px;
@@ -14,13 +14,13 @@
     flex-direction: row;
 
     .class-list-wrap {
-        width: 400px;
+        width: 500px;
         height: 100%;
         border-right: 1px solid @borderColor;
     }
 
     .class-info-wrap {
-        width: ~"calc(100% - 400px)";
+        width: ~"calc(100% - 500px)";
         height: 100%;
         padding-left:15px;
     }
@@ -29,17 +29,39 @@
     width:100%;
     height:45px;
     line-height:45px;
-    border-bottom:1px solid @borderColor;
+    // border-bottom:1px solid @borderColor;
     padding-left:15px;
     color:@second-textColor;
 }
-.action-btn-icon{
-    float:right;
-    margin-right:15px;
-    margin-top:12px;
-    color:white;
-    font-size:18px;
-    cursor:pointer;
+.class-list-filter{
+    width:100%;
+    height:45px;
+    border-bottom:1px solid @borderColor;
+    padding-left:15px;
+    &-box {
+        height: 45px;
+        border-top:1px solid @borderColor;
+        color:@second-textColor;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+    }
+}
+.action-btn{
+    float: right;
+    color: white;
+    font-size: 12px;
+    display: flex;
+    align-items: center;
+    margin-right: 15px;
+    cursor: pointer;
+    &-icon{
+        color:white;
+        margin-right: 12px;
+        &.hide-icon{
+            display: none;
+        }
+    }
 }
 .class-info-header{
     width:100%;
@@ -186,6 +208,9 @@
     .border(bottom);
     cursor:pointer;
     position:relative;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;    
     &:hover {
         .active-item-bg,
         .period-btn-edit {
@@ -196,16 +221,17 @@
 .class-name {
     font-size: @large-fontSize;
     color: white;
-    font-weight: 900;
+    font-weight: 500;
 }
 .second-text-color {
     color: @second-textColor;
+    font-size: 12px;
 }
 .third-text-color{
     color:@third-textColor;
 }
 .fourth-text-color {
-    color: #6BDFC3 !important;
+    color: #1cc0f3 !important;
     /*text-shadow:1px 1px 1px white;*/
 }
 .class-hiteach-code {
@@ -214,15 +240,28 @@
 .class-type {
     margin-top:6px;
     margin-bottom:4px;
-    color:#6BDFC3;
-    margin-left:-4px;
+    align-items: center;
+    display: flex;
+    span{
+        color: rgb(28, 192, 243);
+    }
+}
+.iconHi{
+    width: 16px;
+    height: 16px;
+    background: rgb(28, 192, 243);
+    border-radius: 16%;
+    display: inline-flex;
+    justify-content: center;
+    align-items: center;
+    margin-right: 5px;
 }
 .primary-text-color{
     color:@primary-textColor;
 }
 
 .class-attr-wrap{
-    width:47%;
+    width:35%;
     height:100%;
     .border(right);
     &-label{
@@ -234,7 +273,7 @@
     }
 }
 .hiteach-code-wrap {
-    width: 53%;
+    width: 65%;
     height: 100%;
     &-header {
         width: ~"calc(100% - 30px)";
@@ -251,46 +290,51 @@
         .border(top);
         width: 100%;
         height: ~"calc(100% - 72px)";
-
+        padding-left: 25px;
         h1 {
-            margin-top: -120px;
+        //     margin-top: -120px;
+            font-weight: 400;
         }
 
         ul {
             list-style: none;
-            padding-left: 20px;
-            margin-top: 15px;
+            // padding-left: 20px;
+            // margin-top: 15px;
 
             li {
-                span {
-                    text-overflow: ellipsis;
-                    overflow: hidden;
-                    white-space: nowrap;
-                    display: inline-block;
-                    width: calc(100% - 50px);
-                }
+                color: #dbdbdb;
+                display: flex;
+                align-items: center;
+                margin-bottom: 5px;
+                // span {
+                //     text-overflow: ellipsis;
+                //     overflow: hidden;
+                //     white-space: nowrap;
+                //     display: inline-block;
+                //     width: calc(100% - 50px);
+                // }
 
-                &:hover {
-                    background: @borderColor;
-                    color: white;
-                }
+                // &:hover {
+                //     background: @borderColor;
+                //     color: white;
+                // }
 
-                height: 40px;
-                line-height: 40px;
-                color: @second-textColor;
-                font-size: @second-fontSize - 1;
-                padding-left: 5px;
-                cursor: pointer;
-                .border(bottom);
+                // height: 40px;
+                // line-height: 40px;
+                // color: @second-textColor;
+                // font-size: @second-fontSize - 1;
+                // padding-left: 5px;
+                // cursor: pointer;
+                // .border(bottom);
 
-                .ivu-icon {
-                    margin-top: -4px;
-                }
+                // .ivu-icon {
+                //     margin-top: -4px;
+                // }
 
-                .ivu-tag {
-                    float: right;
-                    margin-top: 8px;
-                }
+                // .ivu-tag {
+                //     float: right;
+                //     margin-top: 8px;
+                // }
             }
         }
     }

+ 491 - 49
TEAMModelOS/ClientApp/src/view/schoolmgmt/ClassroomSetting/ClassroomSetting.vue

@@ -4,12 +4,28 @@
         <div class="class-list-wrap">
             <!--班级列表-->
             <div class="class-list-header">
-                <span>班级列表</span>
+                <!-- <span>班级列表</span> -->
+                <Dropdown class="sort-dropdown" trigger="click" placement="bottom-start" @on-click="function(e){ filterPeriod = e }" @on-visible-change="dropdownStates">
+                    <span style="cursor: pointer;">
+                        <b class="title">{{ filterPeriodName }}</b>
+                        {{ '班级列表' }}
+                        <Icon type="ios-arrow-down"></Icon>
+                    </span>
+                    <DropdownMenu slot="list" v-for="(item,index) in this.$store.state.schoolBaseInfo.schoolBaseInfo.period" :value="item.id" :key="index">
+                        <DropdownItem :name="item.id">{{ item.name }}</DropdownItem>
+                    </DropdownMenu>
+                </Dropdown>
+                    
                 <div v-if="!isSearch" style="float:right;">
-                    <Icon class="action-btn-icon" type="ios-search" @click="isSearch = true" />
-                    <Icon class="action-btn-icon" type="md-add" @click="addClassroom()" />
-                    <Icon class="action-btn-icon" type="md-trash" @click.stop="showConfirmDelete()" />
-                    <Tag v-show="filterPeriod != undefined" closable @on-close="filterPeriod = undefined" color="#606060" style="margin-right:15px;margin-top:-5px;">
+                    <!-- <Icon class="action-btn-icon" type="ios-search" @click="isSearch = true" /> -->
+                    <!-- 新增班級 -->
+                    <div class="action-btn" @click="addClassroom()">
+                        <Icon class="action-btn-icon" type="md-add" />
+                        <span>{{ $t('新增教室') }}</span>
+                    </div>
+
+                     <!-- 學段篩選 先註解 -->
+                        <!-- <Tag v-show="filterPeriod != undefined" closable @on-close="filterPeriod = undefined" color="#606060" style="margin-right:15px;margin-top:-5px;">
                         {{$jsFn.getPeriod( $store.state.schoolBaseInfo.schoolBaseInfo.period,filterPeriod).name}}
                     </Tag>
                     <Poptip trigger="hover" placement="bottom" offset="-8">
@@ -24,32 +40,63 @@
                                 </Select>
                             </div>
                         </div>
-                    </Poptip>
+                    </Poptip> -->
                 </div>
-                <div v-else class="dark-iview-input" style="float:right;width:calc(100% - 100px);padding-right:10px;">
-                    <Input icon="ios-close"
+            </div>
+            <div class="class-list-filter">
+                <div class="class-list-filter-box">
+                    <Dropdown class="sort-dropdown" trigger="click" placement="bottom-start" @on-click="function(e){ orderBy = e }" @on-visible-change="restOrderBy">
+                        <span style="cursor: pointer;color:white;">
+                            <Icon type="md-funnel" color="white" size="17" />
+                            {{ orderByText }}
+                            <Icon type="ios-arrow-down"></Icon>
+                        </span>
+                        <DropdownMenu slot="list">
+                            <DropdownItem name="id">{{ '依教室編號排序' }}</DropdownItem>
+                            <DropdownItem name="state">{{ '依智慧教室排序' }}</DropdownItem>
+                            <DropdownItem name="total">{{ '依學生數排序' }}</DropdownItem>
+                        </DropdownMenu>
+                    </Dropdown>
+                    <div class="dark-iview-input" style="padding-right:10px;">
+                        <Input size="small" suffix="ios-search"
                            v-model="keyword"
-                           placeholder="关键字搜索..."
-                           autofocus
-                           style="width:100%"
+                           style="width: 110px"
                            @on-click="closeKeySearch"
                            @on-change="filterClassname" />
+                    </div>
                 </div>
             </div>
             <div class="class-list">
                 <vuescroll>
                     <div class="class-list-item" v-for="(item,index) in classroomListShow" :key="index" @click="chooseClassroom(index)" :class="curClassIndex == index ? 'block-bg block-bg-active':'block-bg'">
                         <div class="class-list-item-left">
-                            <p class="class-name">{{item.name}}</p>
-                            <p class="class-hiteach-code second-text-color">{{item.sn}}</p>
+                            <!-- <p>{{getPeriodName(item.periodId)}}</p> -->
+                            <p class="class-name">{{item.id}}, {{item.name}}</p>
+                            <!-- <p class="class-hiteach-code second-text-color">{{item.sn}}</p> -->
                             <p class="class-type">
-                                <Icon type="md-arrow-dropright" size="18" />{{item.style == 'smart' ? '智慧教室' : '普通教室'}}
-                            </p>
+                                <template v-if="item.style != 'smart'">
+                                    <div class="iconHi" >
+                                        <v-icon iconClass="hi" style="color:#333;" />
+                                    </div>
+                                    <span>
+                                        {{ $t('TEAM Model 智慧教室') }}
+                                    </span>
+                                </template>
+                                <template v-else>
+                                    <span style="color:#a5a5a5;">
+                                        <v-icon iconClass="board" /> {{ $t('普通教室') }}
+                                    </span>
+                                </template>
                             <p class="second-text-color">
-                                <span>{{$t('schoolBaseInfo.headmaster')}}</span>
+                                <span class="title">{{ $t('學生人數') + ':' }}</span>
+                                <span class="primary-text-color">{{item.studCount}}</span>
+                                <span style="margin-left:5px;margin-right: 13px;">|</span>
+                                <span>{{$t('schoolBaseInfo.headmaster') + ':'}}</span>
                                 <span class="primary-text-color">{{item.teacher.name}}</span>
                             </p>
                         </div>
+                        <!-- 刪除教室 -->
+                        <Icon class="action-btn-icon" :class="curClassIndex == index ? '' : 'hide-icon'" size="19" type="md-trash" @click.stop="showConfirmDelete()" /> 
                     </div>
                     <EmptyData v-if="classroomListShow.length == 0" style="padding-top:120px;"></EmptyData>
                 </vuescroll>
@@ -72,29 +119,30 @@
                     <div class="class-attr-wrap disabled-iview-select dark-iview-select">
                         <vuescroll>
                             <Form v-if="classroomListShow[curClassIndex]" ref="classInfo" :model="classroomListShow[curClassIndex]" :rules="classValidate" style="padding-top:20px;">
-                                <FormItem prop="name" :label="$t('schoolBaseInfo.classroomName')" @click.native.stop>
-                                    <span slot="label" class="class-attr-wrap-label">{{$t('schoolBaseInfo.classroomName')}}</span>
-                                    <Input @on-change="watchUpdate" :disabled="editStatus" v-model="classroomListShow[curClassIndex].name" clearable :placeholder="$t('schoolBaseInfo.classroomNameHolder')" />
-                                </FormItem>
-                                <FormItem prop="id" @click.native.stop>
+                                <FormItem prop="id" @click.native.stop class="requird-color">
                                     <span slot="label" class="class-attr-wrap-label">{{$t('schoolBaseInfo.classroomCode')}}</span>
                                     <Input @on-change="watchUpdate" :disabled="editStatus" v-model="classroomListShow[curClassIndex].id" clearable :placeholder="$t('schoolBaseInfo.classroomCodeHolder')" />
                                 </FormItem>
-                                <FormItem prop="openType" @click.native.stop>
+                                <FormItem prop="name" :label="$t('schoolBaseInfo.classroomName')" @click.native.stop class="requird-color">
+                                    <span slot="label" class="class-attr-wrap-label">{{$t('schoolBaseInfo.classroomName')}}</span>
+                                    <Input @on-change="watchUpdate" :disabled="editStatus" v-model="classroomListShow[curClassIndex].name" clearable :placeholder="$t('schoolBaseInfo.classroomNameHolder')" />
+                                </FormItem>
+                                <FormItem prop="openType" @click.native.stop class="requird-color">
                                     <span slot="label" class="class-attr-wrap-label">班级属性</span>
                                     <Select @on-change="watchUpdate" :disabled="editStatus" v-model="classroomListShow[curClassIndex].openType" clearable>
                                         <Option v-for="(item,index) in attributeList" :value="item.value" :key="index">{{ item.label }}</Option>
                                     </Select>
                                 </FormItem>
-                                <FormItem prop="periodId" :label="$t('schoolBaseInfo.setPeriod')" @click.native.stop>
+                                <!-- 暫時註解 不修改 Osbert 
+                                    <FormItem prop="periodId" :label="$t('schoolBaseInfo.setPeriod')" @click.native.stop>
                                     <span slot="label" class="class-attr-wrap-label">{{$t('schoolBaseInfo.setPeriod')}}</span>
                                     <Select @on-change="watchUpdate" :disabled="editStatus" v-model="classroomListShow[curClassIndex].periodId" clearable>
                                         <Option v-for="(item,index) in $store.state.schoolBaseInfo.schoolBaseInfo.period" :value="item.id" :key="index">
                                             {{ item.name }}
                                         </Option>
                                     </Select>
-                                </FormItem>
-                                <FormItem prop="gradeId" :label="$t('schoolBaseInfo.setGrade')" @click.native.stop>
+                                </FormItem> -->
+                                <FormItem prop="gradeId" :label="$t('schoolBaseInfo.setGrade')" @click.native.stop class="requird-color">
                                     <span slot="label" class="class-attr-wrap-label">{{$t('schoolBaseInfo.setGrade')}}</span>
                                     <Select @on-change="watchUpdate" :disabled="editStatus" v-model="classroomListShow[curClassIndex].gradeId" clearable>
                                         <Option v-for="(item,index) in $jsFn.getPeriod($store.state.schoolBaseInfo.schoolBaseInfo,classroomListShow[curClassIndex].periodId).grades" :value="item.id" :key="index">
@@ -102,7 +150,7 @@
                                         </Option>
                                     </Select>
                                 </FormItem>
-                                <FormItem prop="teacher" :label="$t('schoolBaseInfo.headmaster')" @click.native.stop>
+                                <FormItem prop="teacher" :label="$t('schoolBaseInfo.headmaster')" @click.native.stop class="requird-color">
                                     <span slot="label" class="class-attr-wrap-label">{{$t('schoolBaseInfo.headmaster')}}</span>
                                     <Select @on-change="watchUpdate" :disabled="editStatus" v-model="classroomListShow[curClassIndex].teacher.id" clearable filterable>
                                         <Option v-for="(item,index) in $store.state.teachers.teacherList" :value="item.id" :key="index" @click.native="classroomListShow[curClassIndex].teacher = {id:item.id, name:item.name}">
@@ -110,10 +158,11 @@
                                         </Option>
                                     </Select>
                                 </FormItem>
-                                <FormItem prop="sn" :label="$t('schoolBaseInfo.setHiteachCode')" @click.native.stop>
+                                <!-- 暫時註解改為旁邊勾選 Osbert
+                                    <FormItem prop="sn" :label="$t('schoolBaseInfo.setHiteachCode')" @click.native.stop>
                                     <span slot="label" class="class-attr-wrap-label">{{$t('schoolBaseInfo.setHiteachCode')}}</span>
                                     <Input @on-change="watchUpdate" ref="hiteachCode" @on-focus="toBlur" v-model="classroomListShow[curClassIndex].sn" :disabled="editStatus" :placeholder="$t('schoolBaseInfo.hiTeachHolder')" clearable />
-                                </FormItem>
+                                </FormItem> -->
                             </Form>
                         </vuescroll>
                     </div>
@@ -121,21 +170,243 @@
                     <div class="hiteach-code-wrap">
                         <div class="hiteach-code-wrap-header">
                             <p>
-                                {{$t('schoolBaseInfo.hiteachList')}}
-                                <Icon style="float:right;margin-right:12px;margin-top:2px;cursor:pointer;" class="label-icon" color="white" type="md-add" @click="addCode = true" />
+                                <!-- {{$t('schoolBaseInfo.hiteachList')}} -->
+                                <!-- <Icon style="float:right;margin-right:12px;margin-top:2px;cursor:pointer;" class="label-icon" color="white" type="md-add" @click="addCode = true" /> -->
+                                {{$t('關聯Hiteach設備')}}
                             </p>
-                            <Input clearable v-model="serchCode" :placeholder="$t('schoolBaseInfo.codeHolder')" style="width: 100%;height:30px;" @on-change="filterCode">
-                            <Icon style="margin-top:0px;" type="ios-search" slot="suffix" />
-                            </Input>
+                            <div class="class-list-filter">
+                                <div class="class-list-filter-box">
+                                    <Dropdown class="sort-dropdown" trigger="click" placement="bottom-start">
+                                        <span style="cursor: pointer;color:white;">
+                                            {{ '顯示所有版本的Hiteach' }}
+                                            <Icon type="ios-arrow-down"></Icon>
+                                        </span>
+                                        <DropdownMenu slot="list">
+                                            <DropdownItem name="All">{{ '顯示所有版本的Hiteach' }}</DropdownItem>
+                                            <DropdownItem name="Standard">{{ '僅顯示Hiteach Standard版本' }}</DropdownItem>
+                                            <DropdownItem name="Pro">{{ '僅顯示Hiteach Pro版本' }}</DropdownItem>
+                                            <DropdownItem name="TBL">{{ '僅顯示Hiteach TBL版本' }}</DropdownItem>
+                                        </DropdownMenu>
+                                    </Dropdown>
+                                    <div class="dark-iview-input" style="padding-right:10px;">
+                                        <Input clearable v-model="serchCode" size="small" suffix="ios-search" @on-change="filterCode"></Input>
+                                    </div>
+                                </div>
+                            </div>
                         </div>
                         <div class="hiteach-code-wrap-list">
-                            <h1 v-if="!hiTeachs.length">{{$t('schoolBaseInfo.noHiTeachTips')}}</h1>
-                            <ul v-else>
-                                <li v-for="(item,index) in hiTeachsShow" :key="index" @click.stop="chooseHiTeach(index)" :class="item.using > 0 ? 'fourth-text-color':''" :title="item.code">
-                                    <span>{{item.code}}</span>
-                                    <Tag color="#6BDFC3" v-if="item.single == 1">{{item.using+'/'+5}}</Tag>
-                                </li>
-                            </ul>
+                            <vuescroll>
+                                <div class="hiteach-collapse" style="border-bottom: 1px solid #424242;position: relative;">
+                                    <div class="hiteach-collapse-main" style="display: flex;align-items: center;padding: 20px 0 20px 20px;">
+
+                                        <Icon @click="stdFlag = !stdFlag" :type="stdFlag ? 'ios-arrow-up' : 'ios-arrow-down'" size="30" color="white" style="position: absolute;right: 5px;top: 20px;"/>
+
+                                        <v-icon style="width: 60px;height: 60px;margin-right: 30px;" iconClass="htc_std" />
+                                        <div style="font-size: 12px;letter-spacing: 1px;">
+                                            <ul>
+                                                <li>
+                                                    <h1 class="title" style="display: inline-block;margin-right: 12px;font-size: 16px;font-weight: 500;color: #bdbdbd;color:white;">HiTeach 5 STD</h1>
+                                                    <span style="margin-right: 12px;padding: 2px 5px;background: #1cc0f3;border-radius: 3px;color: white;;">大量</span>
+                                                    <span style="color: #1cc0f3;font-size:12px">2/2</span>
+                                                </li>
+                                                <li><span style="color: #1cc0f3;">3222IAVN-FJ4Y-F5EG-S3OI-4P1A</span></li>
+                                                <li>
+                                                    <Tag type="border" color="#8d8d8d">蘇格拉底議課</Tag>
+                                                    <Tag type="border" color="#8d8d8d">蘇格拉底報告</Tag>
+                                                    <Tag type="border" color="#8d8d8d">蘇格拉底桌面</Tag>
+                                                    <Tag type="border" color="#8d8d8d">蘇格拉底影片</Tag>
+                                                    <Tag type="border" color="#1cc0f3">遠距教室服務</Tag>
+                                                    <Tag type="border" color="#1cc0f3">錄播系統</Tag>
+                                                </li>
+                                                <li><span style="color: #8d8d8d;">序號到期日: </span>無到期日</li>
+                                            </ul>
+                                        </div>
+                                    </div>
+                                    <div v-if="stdFlag">
+                                        <div class="hiteach-collapse-sub testclass" style="display: flex;align-items: center;border-top: 1px solid rgb(66, 66, 66);margin-left: 20px;letter-spacing: 1px;font-size: 12px;padding: 20px;">
+                                            <Checkbox style="margin-right: 70px;" v-model="test1" disabled></Checkbox>
+                                            <div style="display: flex;justify-content: space-between;align-items: center;width: 100%;">
+                                                <ul>
+                                                    <!-- 被關聯要變色 -->
+                                                    <li style="color: #888888;">
+                                                        <h3>USER-PCCA062992</h3>
+                                                    </li>
+                                                    <li style="color: #888888;">
+                                                        <span>192.168.256 | 84FJID3LKSD88 </span>
+                                                    </li>
+                                                    <li style="color: #888888;">
+                                                        <span>Microsoft Winfows NT 10.0.18883.0 | Intel(R Core(TM i7-10700 CPU @ 2.9Ghz))</span>
+                                                    </li>
+                                                </ul>
+                                                <span style="color: #1cc0f3;padding: 0 15px;">
+                                                    已被關聯
+                                                </span>
+                                            </div>
+                                        </div>
+                                        <div class="hiteach-collapse-sub testclass" style="display: flex;align-items: center;border-top: 1px solid rgb(66, 66, 66);margin-left: 20px;letter-spacing: 1px;font-size: 12px;padding: 20px;">
+                                            <Checkbox style="margin-right: 70px;"></Checkbox>
+                                            <div style="display: flex;justify-content: space-between;align-items: center;width: 100%;">
+                                                <ul>
+                                                    <li>
+                                                        <h3>USER-PCCA062992</h3>
+                                                    </li>
+                                                    <li>
+                                                        <span>192.168.256 | 84FJID3LKSD88 </span>
+                                                    </li>
+                                                    <li>
+                                                        <span>Microsoft Winfows NT 10.0.18883.0 | Intel(R Core(TM i7-10700 CPU @ 2.9Ghz))</span>
+                                                    </li>
+                                                </ul>
+                                                <span v-if="false" style="color: #1cc0f3;padding: 0 15px;">
+                                                    已被關聯
+                                                </span>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="hiteach-collapse" style="border-bottom: 1px solid #424242;position: relative;">
+                                    <div class="hiteach-collapse-main" style="display: flex;align-items: center;padding: 20px 0 20px 20px;">
+
+                                        <Icon @click="tblFlag = !tblFlag" :type="tblFlag ? 'ios-arrow-up' : 'ios-arrow-down'" size="30" color="white" style="position: absolute;right: 5px;top: 20px;"/>
+
+                                        <v-icon style="width: 60px;height: 60px;margin-right: 30px;" iconClass="htc_tbl" />
+                                        <div style="font-size: 12px;letter-spacing: 1px;">
+                                            <ul>
+                                                <li>
+                                                    <h1 class="title" style="display: inline-block;margin-right: 12px;font-size: 16px;font-weight: 500;color: #bdbdbd;color:white;">HiTeach 5 STD</h1>
+                                                    <span style="margin-right: 12px;padding: 2px 5px;background: #1cc0f3;border-radius: 3px;color: white;;">大量</span>
+                                                    <span style="color: #1cc0f3;font-size:12px">2/2</span>
+                                                </li>
+                                                <li><span style="color: #1cc0f3;">3222IAVN-FJ4Y-F5EG-S3OI-4P1A</span></li>
+                                                <li>
+                                                    <Tag type="border" color="#8d8d8d">蘇格拉底議課</Tag>
+                                                    <Tag type="border" color="#8d8d8d">蘇格拉底報告</Tag>
+                                                    <Tag type="border" color="#8d8d8d">蘇格拉底桌面</Tag>
+                                                    <Tag type="border" color="#8d8d8d">蘇格拉底影片</Tag>
+                                                    <Tag type="border" color="#1cc0f3">遠距教室服務</Tag>
+                                                    <Tag type="border" color="#1cc0f3">錄播系統</Tag>
+                                                </li>
+                                                <li><span style="color: #8d8d8d;">序號到期日: </span>無到期日</li>
+                                            </ul>
+                                        </div>
+                                    </div>
+                                    <div v-if="tblFlag">
+                                        <div class="hiteach-collapse-sub testclass" style="display: flex;align-items: center;border-top: 1px solid rgb(66, 66, 66);margin-left: 20px;letter-spacing: 1px;font-size: 12px;padding: 20px;">
+                                            <Checkbox style="margin-right: 70px;" v-model="test1" disabled></Checkbox>
+                                            <div style="display: flex;justify-content: space-between;align-items: center;width: 100%;">
+                                                <ul>
+                                                    <!-- 被關聯要變色 -->
+                                                    <li style="color: #888888;">
+                                                        <h3>USER-PCCA062992</h3>
+                                                    </li>
+                                                    <li style="color: #888888;">
+                                                        <span>192.168.256 | 84FJID3LKSD88 </span>
+                                                    </li>
+                                                    <li style="color: #888888;">
+                                                        <span>Microsoft Winfows NT 10.0.18883.0 | Intel(R Core(TM i7-10700 CPU @ 2.9Ghz))</span>
+                                                    </li>
+                                                </ul>
+                                                <span style="color: #1cc0f3;padding: 0 15px;">
+                                                    已被關聯
+                                                </span>
+                                            </div>
+                                        </div>
+                                        <div class="hiteach-collapse-sub testclass" style="display: flex;align-items: center;border-top: 1px solid rgb(66, 66, 66);margin-left: 20px;letter-spacing: 1px;font-size: 12px;padding: 20px;">
+                                            <Checkbox style="margin-right: 70px;"></Checkbox>
+                                            <div style="display: flex;justify-content: space-between;align-items: center;width: 100%;">
+                                                <ul>
+                                                    <li>
+                                                        <h3>USER-PCCA062992</h3>
+                                                    </li>
+                                                    <li>
+                                                        <span>192.168.256 | 84FJID3LKSD88 </span>
+                                                    </li>
+                                                    <li>
+                                                        <span>Microsoft Winfows NT 10.0.18883.0 | Intel(R Core(TM i7-10700 CPU @ 2.9Ghz))</span>
+                                                    </li>
+                                                </ul>
+                                                <span v-if="false" style="color: #1cc0f3;padding: 0 15px;">
+                                                    已被關聯
+                                                </span>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="hiteach-collapse" style="border-bottom: 1px solid #424242;position: relative;">
+                                    <div class="hiteach-collapse-main" style="display: flex;align-items: center;padding: 20px 0 20px 20px;">
+
+                                        <Icon @click="proFlag = !proFlag" :type="proFlag ? 'ios-arrow-up' : 'ios-arrow-down'" size="30" color="white" style="position: absolute;right: 5px;top: 20px;"/>
+
+                                        <v-icon style="width: 60px;height: 60px;margin-right: 30px;" iconClass="htc_pro" />
+                                        <div style="font-size: 12px;letter-spacing: 1px;">
+                                            <ul>
+                                                <li>
+                                                    <h1 class="title" style="display: inline-block;margin-right: 12px;font-size: 16px;font-weight: 500;color: #bdbdbd;color:white;">HiTeach 5 STD</h1>
+                                                    <span style="margin-right: 12px;padding: 2px 5px;background: #1cc0f3;border-radius: 3px;color: white;;">大量</span>
+                                                    <span style="color: #1cc0f3;font-size:12px">2/2</span>
+                                                </li>
+                                                <li><span style="color: #1cc0f3;">3222IAVN-FJ4Y-F5EG-S3OI-4P1A</span></li>
+                                                <li>
+                                                    <Tag type="border" color="#8d8d8d">蘇格拉底議課</Tag>
+                                                    <Tag type="border" color="#8d8d8d">蘇格拉底報告</Tag>
+                                                    <Tag type="border" color="#8d8d8d">蘇格拉底桌面</Tag>
+                                                    <Tag type="border" color="#8d8d8d">蘇格拉底影片</Tag>
+                                                    <Tag type="border" color="#1cc0f3">遠距教室服務</Tag>
+                                                    <Tag type="border" color="#1cc0f3">錄播系統</Tag>
+                                                </li>
+                                                <li><span style="color: #8d8d8d;">序號到期日: </span>無到期日</li>
+                                            </ul>
+                                        </div>
+                                    </div>
+                                    <div v-if="proFlag">
+                                        <div class="hiteach-collapse-sub testclass" style="display: flex;align-items: center;border-top: 1px solid rgb(66, 66, 66);margin-left: 20px;letter-spacing: 1px;font-size: 12px;padding: 20px;">
+                                            <Checkbox style="margin-right: 70px;" v-model="test1" disabled></Checkbox>
+                                            <div style="display: flex;justify-content: space-between;align-items: center;width: 100%;">
+                                                <ul>
+                                                    <!-- 被關聯要變色 -->
+                                                    <li style="color: #888888;">
+                                                        <h3>USER-PCCA062992</h3>
+                                                    </li>
+                                                    <li style="color: #888888;">
+                                                        <span>192.168.256 | 84FJID3LKSD88 </span>
+                                                    </li>
+                                                    <li style="color: #888888;">
+                                                        <span>Microsoft Winfows NT 10.0.18883.0 | Intel(R Core(TM i7-10700 CPU @ 2.9Ghz))</span>
+                                                    </li>
+                                                </ul>
+                                                <span style="color: #1cc0f3;padding: 0 15px;">
+                                                    已被關聯
+                                                </span>
+                                            </div>
+                                        </div>
+                                        <div class="hiteach-collapse-sub testclass" style="display: flex;align-items: center;border-top: 1px solid rgb(66, 66, 66);margin-left: 20px;letter-spacing: 1px;font-size: 12px;padding: 20px;">
+                                            <Checkbox style="margin-right: 70px;"></Checkbox>
+                                            <div style="display: flex;justify-content: space-between;align-items: center;width: 100%;">
+                                                <ul>
+                                                    <li>
+                                                        <h3>USER-PCCA062992</h3>
+                                                    </li>
+                                                    <li>
+                                                        <span>192.168.256 | 84FJID3LKSD88 </span>
+                                                    </li>
+                                                    <li>
+                                                        <span>Microsoft Winfows NT 10.0.18883.0 | Intel(R Core(TM i7-10700 CPU @ 2.9Ghz))</span>
+                                                    </li>
+                                                </ul>
+                                                <span v-if="false" style="color: #1cc0f3;padding: 0 15px;">
+                                                    已被關聯
+                                                </span>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                                <!-- <ul>
+                                    <li v-for="(item,index) in hiTeachsShow" :key="index" @click.stop="chooseHiTeach(index)" :class="item.using > 0 ? 'fourth-text-color':''" :title="item.code">
+                                        <span>{{item.code}}</span>
+                                        <Tag color="#6BDFC3" v-if="item.single == 1">{{item.using+'/'+5}}</Tag>
+                                    </li>
+                                </ul> -->
+                            </vuescroll>    
                         </div>
                     </div>
                 </div>
@@ -179,8 +450,14 @@
 </template>
 
 <script>
+    import { mapGetters } from 'vuex'
+    import '@/icons/svg/htc.svg'
+    import '@/icons/svg/htc_pro.svg'
+    import '@/icons/svg/htc_std.svg'
+    import '@/icons/svg/htc_tbl.svg'
+    import '@/icons/svg/board.svg'
+    
     export default {
-        
         data() {
             // 验证只能是字母和数字
             const validateCode = (rule, value, callback) => {
@@ -195,6 +472,11 @@
                 }
             }
             return {
+                tblFlag: false,
+                stdFlag: false,
+                proFlag: false,                
+                test1: true,
+                orderBy: 'id',
                 updateBefore:'',
                 filterPeriod: undefined,
                 editStatus: true,//可切换编辑状态
@@ -319,16 +601,51 @@
                     openType: [
                         { required: true, message: '请教室属性', trigger: 'change' }
                     ],
-                    periodId: [
-                        { required: true, message: '请设置学段', trigger: 'change' }
-                    ],
+                    // periodId: [
+                    //     { required: true, message: '请设置学段', trigger: 'change' }
+                    // ],
                     gradeId: [
                         { required: true, message: '请设置年级', trigger: 'change' }
-                    ]
+                    ],
+                    teacher: [
+                        { required: true, type: 'object', message: '请设置班主任', trigger: 'change' }
+                    ],
                 },
             }
         },
+        computed: {
+            filterPeriodName: function(){
+                let pId = this.filterPeriod
+                let name = ''
+                if(pId !== ''){
+                    let temp = this.$store.state.schoolBaseInfo.schoolBaseInfo.period.filter( item => {
+                        return pId == item.id
+                    })
+                    if(temp.length >0 ) name = temp[0].name
+                }
+                return name
+            },
+            orderByText: function(){
+                switch (this.orderBy) {
+                    case 'id':
+                        return this.$t('依教室編號排序')
+                    break;
+                    case 'state':
+                        return this.$t('依智慧教室排序')
+                    break;
+                    case 'total':
+                        return this.$t('依學生數排序')    
+                    break;
+                }
+            }
+        },
         methods: {
+            dropdownStates(flag){
+                if(!flag) this.filterByPeriod()
+            },
+            restOrderBy(flag){
+                if(!flag) this.filterByPeriod()
+            },
             closeKeySearch() {
                 this.isSearch = false
                 this.keyword = ''
@@ -337,12 +654,49 @@
             watchUpdate() {
                 this.updated = true
             },
+            dataSort(data){
+                switch (this.orderBy) {
+                    case 'id': // ID 排序
+                        data.sort(function(a, b){
+                            let nameA = a.id.toUpperCase(); // ignore upper and lowercase
+                            let nameB = b.id.toUpperCase(); // ignore upper and lowercase
+                            if (nameA < nameB) {
+                                return -1;
+                            }
+                            if (nameA > nameB) {
+                                return 1;
+                            }
+
+                            // names must be equal
+                            return 0;
+                        })
+                        break;
+                    case 'state': // 智慧教室排序
+                        data.sort(function(a, b){
+                            if(a.style == 'smart'){
+                                return 1
+                            } else {
+                                return -1
+                            }
+                        })
+                        break; // 學生總數排序
+                    case 'total':
+                        data.sort(function(a, b){
+                            return b.studCount - a.studCount;
+                        })
+                        break;
+                }
+                console.log(data)
+                return data
+            },
             filterByPeriod() {
                 if (this.filterPeriod) {
                     this.classroomListShow = this.classroomList.filter(item => item.periodId == this.filterPeriod)
                 } else {
                     this.classroomListShow = [...this.classroomList]
                 }
+                // 排序
+                this.classroomListShow = this.dataSort(this.classroomListShow)
             },
             filterClassname() {
                 if (this.keyword == '' || this.keyword == undefined) {
@@ -355,6 +709,8 @@
                         filterRes = [...this.classroomList]
                     }
                     this.classroomListShow = filterRes.filter(item => item.name.indexOf(this.keyword) != -1)
+                    // 排序
+                    this.classroomListShow = this.dataSort(this.classroomListShow)
                     this.curClassIndex = 0
                     this.updateBefore = JSON.stringify(this.classroomListShow[this.curClassIndex])
                 }
@@ -622,6 +978,7 @@
             },
             saveClassroom() {
                 this.$refs['classInfo'].validate((valid) => {
+                    console.log(valid, 'valid')
                     if (!valid) {
                         this.$Message.error('请先完善班级信息再保存!')
                     } else {
@@ -674,6 +1031,12 @@
                     (res) => {
                         if (res.code == 1 || res.code == 3) {
                             this.classroomList = this.$store.state.schoolBaseInfo.classroomList
+
+                            console.log(this.$store.state.schoolBaseInfo.classroomList, 'classroomList')
+
+                            // 預設搜尋給第一個
+                            this.filterPeriod = this.$store.state.schoolBaseInfo.schoolBaseInfo.period[0].id
+
                             if (this.classroomList.length > 0) {
                                 this.updateBefore = JSON.stringify(this.classroomList[0])
                             }
@@ -950,12 +1313,16 @@
     }
 
     .hiteach-code-wrap-header .ivu-input {
-        width: calc(100% - 30px);
-        background: none;
-        border: none;
-        border-radius: 0px;
+        width: 110px;
+        /* background: none; */
+        /* border: none; */
+        /* border-radius: 0px; */
         color: white;
         font-size: 15px;
+
+        background: none;
+        color: white;
+        border-color: #606060;
     }
 
     .class-attr-wrap .ivu-input {
@@ -1007,4 +1374,79 @@
     .school-plan-wrap .ivu-upload-list {
         display: none;
     }
+
+    
+
+        
+        
+</style>
+<style lang="less">
+.testclass{
+    @keyframes Itemfadein {
+          0% {
+            opacity: 0;
+            transform: translate(-40px);
+          }
+          100% {
+            opacity: 1;
+            transform: translate(0px);
+          }
+        }
+        animation: 0.5s Itemfadein;
+}
+
+.sort-dropdown{
+    .title{
+        color: #dcdee2;
+        font-size: 14px;
+    }
+    .ivu-select-dropdown{
+        background-color: #2d2d2d;
+        border-radius: 2px;
+        border: 1px #464646 solid;
+        .ivu-dropdown-menu{
+            li {
+                color: #ccc;
+                font-size: 12px!important;
+                &:hover{
+                    color: #2d2d2d;
+                }
+            }
+        }
+    }
+}
+.requird-color{
+    .ivu-form-item-label:before{
+        color: #1cc0f3;
+    }
+}
+.hiteach-collapse{
+//     background: transparent;
+//     color: white;
+//     border-top: none;
+//     .ivu-collapse-item{
+//         .ivu-collapse-header{
+//             height: 200px;
+//             line-height: 200px;
+//             display: flex;
+//             align-items: center;
+//             padding-left: 15px;
+            .ivu-tag-border{
+                background: transparent!important;
+                margin-right: 10px;
+                border-radius: 5px;
+                letter-spacing: 1px;
+            }
+//             .ivu-icon-ios-arrow-forward{
+//                 position: absolute;
+//                 right: 2px;
+//                 font-size: 30px;
+//                 color: white;
+//             }
+//         }
+//         .ivu-collapse-content{
+//             background: transparent;
+//         }
+//     }
+}    
 </style>

+ 3 - 0
TEAMModelOS/ClientApp/src/view/schoolmgmt/SystemSetting/SystemSetting.vue

@@ -934,6 +934,9 @@
     .time-zone-select:hover .ivu-icon-ios-arrow-down:before {
         display: inline-block;
     }
+    .attr-box-item .ivu-icon-ios-arrow-down:before {
+        display: inline-block !important;
+    }
 
     .ghost {
         opacity: 0;

+ 30 - 0
TEAMModelOS/ClientApp/src/view/serviceDriveAuth/Index.less

@@ -0,0 +1,30 @@
+#serviceDriveAuth {
+  color: white;
+  .service-list {
+    border-right: 0.1px solid #ffffff15;
+    height: 100vh;
+  }
+  .space-title {
+    padding: 14px 0px;
+    color: #e2e2e3;
+    font-size: 14px;
+    border-bottom: 1px solid #464646;
+    margin-left: 20px;
+  }
+  .teacher-list {
+    .input-space {
+      background-color: #333237;
+      color: white;
+      text-align: center;
+
+      width: 30%;
+      line-height: 30px;
+      border: 1px solid gray;
+      border-radius: 4px;
+      &:focus {
+        outline: 0;
+        border: 2px solid #1cc0f3;
+      }
+    }
+  }
+}

+ 71 - 0
TEAMModelOS/ClientApp/src/view/serviceDriveAuth/Index.vue

@@ -0,0 +1,71 @@
+<style lang="less" scoped>
+@import "./Index.less";
+</style>
+
+<template>
+  <div id="serviceDriveAuth">
+    <Row>
+      <i-col :xs="24" :sm="24" :md="24" :lg="7">
+        <ServiceList :serviceList="serviceList" />
+      </i-col>
+       <i-col :xs="24" :sm="24" :md="24" :lg="11">
+        <HiteachAuthList :hiteachAuthList="hiteachAuthList" />
+      </i-col>
+      <i-col :xs="24" :sm="24" :md="24" :lg="6">
+        <SpaceStatus :spaceStatus="spaceStatus" />
+      </i-col>
+    </Row>
+  </div>
+</template>
+
+<script>
+import { mapGetters, mapMutations } from "vuex";
+import apiTools from "@/api";
+import PersonalPhoto from "@/components/public/personalPhoto/Index.vue";
+import ServiceList from "./SubComponents/ServiceList.vue";
+import Spacebar from "./SubComponents/Spacebar.vue";
+import HiteachAuthList from "./SubComponents/HiteachAuthList.vue";
+import SpaceStatus from "./SubComponents/SpaceStatus.vue" 
+export default {
+  name: "serviceDriveAuth",
+  components: {
+    PersonalPhoto,
+    ServiceList,
+    Spacebar,
+    HiteachAuthList,
+    SpaceStatus
+  },
+  watch: {},
+
+  data() {
+    return {};
+  },
+
+  computed: {
+    ...mapGetters({
+      serviceList: "serviceDriveAuth/getServiceList",
+      hiteachAuthList:"serviceDriveAuth/getHiteachAuthList",
+       spaceStatus:"serviceDriveAuth/getSpaceStatus"
+    }),
+  },
+
+  methods: {
+    getDataAsyc() {
+      this.$store.dispatch("serviceDriveAuth/getServiceListDataAsyc").then(
+        (res) => {
+          this.$Message.success("取得Mock資料");
+        },
+        (err) => {
+          this.$Message.error("取得資料失敗");
+        }
+      );
+    },
+  },
+  mounted() {
+    this.getDataAsyc();
+  },
+};
+</script>
+<style lang="less" >
+@import "./serviceDriveAuthIview.less";
+</style>

+ 114 - 0
TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/AclassOneChart.vue

@@ -0,0 +1,114 @@
+<template>
+  <div id="aclassOne-chart" />
+</template>
+
+<script>
+export default {
+  name: "AclassOneChart",
+  data() {
+    return {
+      e: event,
+      index: 0,
+
+      mockdata: {
+        pieNumData: [
+          { value: 74, name: "固定分配數" },
+          { value: 84, name: "動態分配數" },
+          { value: 500-74-84, name: "剩餘分配數" },
+        ],
+      },
+    };
+  },
+  created() {
+    this.$emit("HiteachInfo", this.mockdata.pieNumData);
+  },
+  mounted() {
+    this.drawLine(this.mockdata.pieNumData);
+  },
+  methods: {
+    drawLine(pieNumData) {
+      //基于准备好的dom,初始化echarts实例
+
+      let myChart = this.$echarts.init(
+        document.getElementById("aclassOne-chart")
+      ); //绘制图表
+      myChart.setOption({
+        tooltip: {
+          trigger: "item",
+          formatter: "{b}: {c} ({d}%)",
+        },
+        legend: {
+          show: false,
+          orient: "horizontal",
+          icon: "circle",
+
+          textStyle: {
+            color: "white",
+          },
+           
+          left: 20,
+          bottom: 0,
+        },
+        color: ["#00f492", "#f862bb", "gray"],
+
+        series: [
+          {
+            type: "pie",
+            radius: ["45%", "100%"],
+            top:20,
+            right: 10,
+            left:0,
+            bottom: 10,
+            avoidLabelOverlap: true,
+            label: {
+              show: false,
+              position: "center",
+              formatter: "{percent|{d}% }\n {b}",
+              lineHeight: 20,
+
+              rich: {
+                percent: {
+                  color: "white",
+                  fontSize: 20,
+                  lineHeight: 30,
+                  padding: [0, 0, 0, 10],
+                },
+              },
+            },
+            startAngle: 90,
+            emphasis: {
+              label: {
+                show: false,
+                fontSize: "18",
+                fontWeight: "bold",
+              },
+            },
+            labelLine: {
+              show: false,
+            },
+            itemStyle: {
+              borderColor: "#2b2a2f",
+              borderWidth: 0,
+            },
+            data: this.mockdata.pieNumData,
+          },
+        ],
+      });
+
+     
+
+     
+    },
+  },
+};
+</script>
+
+<style>
+#aclassOne-chart {
+  width: 120%;
+  height: 70px;
+  margin-top: -20px;
+   margin-left: -33%;
+ 
+}
+</style>

+ 235 - 0
TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/HiteachAuthList.less

@@ -0,0 +1,235 @@
+.hiteachAuth-list {
+  .ivu-select-dropdown {
+    border-radius: 4px !important;
+    width: auto;
+    background: #000;
+    padding: 10px 0px;
+    .ivu-dropdown-item {
+      color: white;
+      font-weight: 400;
+      &:hover {
+        color: #1cc0f3;
+        background: linear-gradient(-0.75turn, #2b2b2e, #56565b85);
+      }
+    }
+  }
+  .hiteach-info-border {
+    border-bottom: 1px solid rgba(98, 97, 101, 0.4);
+    margin-left: 20px;
+  }
+  .hiteach-type-filter {
+    .title {
+      color: #bbbbbb;
+      font-size: 10px;
+      font-weight: 500;
+    }
+    background: -webkit-linear-gradient(
+      360deg,
+      rgba(30, 30, 30, 0) 0%,
+      rgb(30, 30, 30)
+    );
+    background: -webkit-linear-gradient(
+      360deg,
+      rgba(30, 30, 30, 0) 0%,
+      rgb(30, 30, 30)
+    );
+    background: -webkit-linear-gradient(
+      360deg,
+      rgba(30, 30, 30, 0) 0%,
+      rgb(30, 30, 30)
+    );
+    background: -webkit-linear-gradient(
+      360deg,
+      rgba(30, 30, 30, 0) 0%,
+      rgb(30, 30, 30)
+    );
+    padding: 10px 30px;
+  }
+  .hiteach-info {
+    padding: 20px;
+    padding-bottom: 10px;
+    padding-left: 30px;
+    .nomal-text {
+      font-size: 10px;
+      color: #8d8d8d;
+    }
+    .info-num {
+      font-size: 30px;
+      margin-top: -5px;
+      font-weight: 500;
+    }
+  }
+
+  .action-btn {
+    cursor: pointer;
+    border-radius: 5px;
+    margin-top: 15px;
+    padding: 5px 12px;
+    font-size: 12px;
+    display: inline-block;
+    background-color: #1cc0f3;
+    color: white;
+    position: absolute;
+    right: 20px;
+    bottom: -70px;
+    font-weight: 700;
+    &:hover {
+      background-color: darken(#1cc0f3, 10%);
+    }
+   
+  }
+  .hiteach-list {
+    height: 76.5vh;
+    padding-bottom: 50px;
+    overflow: scroll;
+    border-top: 1px solid rgba(98, 97, 101, 0.4);
+    .hiteach-item {
+      padding: 23px 0px;
+      padding-bottom: 0px;
+      border-bottom: 1px solid rgba(98, 97, 101, 0.4);
+      margin-left: 20px;
+      position: relative;
+      .detail-btn {
+        position: absolute;
+        top: 10px;
+        right: 20px;
+        z-index: 2;
+        font-size: 30px;
+        cursor: pointer;
+        &:hover{
+          color: #1cc0f3;
+        }
+      }
+      .title {
+        font-size: 16px;
+        font-weight: bolder;
+      }
+      .serial {
+        font-size: 12px;
+        color: #1cc0f3;
+        font-weight: 600;
+      }
+      .app-group {
+        line-height: 40px;
+        font-size: 7px !important;
+        .more-app {
+          color: gray;
+          border: 1px solid gray;
+          padding: 3px;
+          margin: 0px 5px;
+
+          &:first-child {
+            margin-left: 0px;
+          }
+          border-radius: 4px;
+        }
+        .bought-app {
+          color: #1cc0f3;
+          border: 1px solid #1cc0f3;
+        }
+      }
+      .nomal-text {
+        font-size: 10px;
+        color: #8d8d8d;
+      }
+      .strong-text {
+        font-size: 10px;
+        font-weight: 700;
+        color: #dbdbdb;
+      }
+      .overtime-text {
+        font-size: 10px;
+        font-weight: 700;
+        color: #ad3435;
+      }
+      .device-max {
+        position: relative;
+        top: -3px;
+        left: 4px;
+        font-size: 8px !important;
+        padding: 2px 5px;
+        background-color: #1cc0f3;
+        border-radius: 3px;
+      }
+      .pclist {
+        margin-left: 40px;
+        @keyframes Itemfadein {
+          0% {
+            opacity: 0;
+            transform: translate(-40px);
+          }
+          100% {
+            opacity: 1;
+            transform: translate(0px);
+          }
+        }
+        animation: 0.5s Itemfadein;
+      }
+      .pclist-item {
+        border-top: 1px solid rgba(98, 97, 101, 0.4);
+        position: relative;
+        &:hover {
+          .action-btn {
+            display: block;
+          }
+          background: -webkit-linear-gradient(
+            360deg,
+            rgba(30, 30, 30, 0) 0%,
+            rgba(110, 110, 110, 0.2) 50%,
+            rgba(110, 110, 110, 0.4) 100%
+          );
+          background: -webkit-linear-gradient(
+            360deg,
+            rgba(30, 30, 30, 0) 0%,
+            rgba(110, 110, 110, 0.2) 50%,
+            rgba(110, 110, 110, 0.4) 100%
+          );
+          background: -webkit-linear-gradient(
+            360deg,
+            rgba(30, 30, 30, 0) 0%,
+            rgba(110, 110, 110, 0.2) 50%,
+            rgba(110, 110, 110, 0.4) 100%
+          );
+          background: -webkit-linear-gradient(
+            360deg,
+            rgba(30, 30, 30, 0) 0%,
+            rgba(110, 110, 110, 0.2) 50%,
+            rgba(110, 110, 110, 0.4) 100%
+          );
+        }
+        &:first-child {
+          border-bottom: none;
+        }
+        padding: 20px;
+
+        padding-left: 65px;
+        list-style: none;
+        .pc-title {
+          font-size: 16px;
+          font-weight: 500;
+        }
+        .nomal-text {
+          font-size: 10px;
+          color: #8d8d8d;
+        }
+        .action-btn {
+          display: none;
+          position: absolute;
+          bottom: 36px;
+          border-radius: 4px !important;
+        }
+      }
+      .pclist-empty {
+        padding: 10px 50px;
+        color: #1cc0f3;
+      }
+    }
+    .hicon-wrap {
+      padding: 32px;
+      padding-left: 12px;
+      * {
+        font-size: 50px;
+      }
+    }
+  }
+}

+ 282 - 0
TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/HiteachAuthList.vue

@@ -0,0 +1,282 @@
+<template>
+  <div class="hiteachAuth-list">
+    <div class="service-list">
+      <div class="service-listTitle">智慧教室授權管理</div>
+      <Row>
+        <i-col :xs="24" :sm="24" :md="24" :lg="16">
+          <Row>
+            <i-col :xs="24" :sm="24" :md="24" :lg="8">
+              <div class="hiteach-info">
+                <p class="nomal-text">Hiteach序號數</p>
+                <p class="info-num">{{hiteachListItemIsOpenVuex.length}}</p>
+              </div>
+            </i-col>
+            <i-col :xs="24" :sm="24" :md="24" :lg="8">
+              <div class="hiteach-info">
+                <p class="nomal-text">可啟用Hiteach序號數</p>
+                <p class="info-num">{{(hiteachListItemIsOpenVuex.length*225/18).toFixed(0)}}</p>
+              </div>
+            </i-col>
+            <i-col :xs="24" :sm="24" :md="24" :lg="8">
+              <div class="hiteach-info">
+                <p class="nomal-text">已啟用Hiteach序號數</p>
+                <p class="info-num">{{(hiteachListItemIsOpenVuex.length*25/8).toFixed(0)}}</p>
+              </div>
+            </i-col>
+          </Row></i-col
+        >
+        <i-col :xs="24" :sm="24" :md="24" :lg="8">
+          <div class="action-btn" @click="gotoHiteachMgmt()">
+            管理Hiteach教室
+          </div>
+        </i-col>
+      </Row>
+    </div>
+    <div class="hiteach-info-border" />
+    <div class="hiteach-type-filter">
+      <Dropdown @on-click="filterList">
+        <a class="title" href="javascript:void(0)">
+          <span v-if="filterType == '1'">顯示全部</span>
+          <span v-if="filterType == '2'">僅顯示HiTeach STD序號</span>
+          <span v-if="filterType == '3'">僅顯示HiTeach PRO序號</span>
+          <span v-if="filterType == '4'">僅顯示HiTeach TBL序號</span>
+          <span v-if="filterType == '5'">僅顯示已完成綁定的序號</span>
+          <span v-if="filterType == '6'">僅顯示已到期序號</span>
+          <span><Icon type="ios-arrow-down"></Icon></span>
+        </a>
+        <DropdownMenu slot="list">
+          <DropdownItem name="1">顯示全部</DropdownItem>
+          <DropdownItem name="2">僅顯示HiTeach STD序號</DropdownItem>
+          <DropdownItem name="3">僅顯示HiTeach PRO序號</DropdownItem>
+          <DropdownItem name="4">僅顯示HiTeach TBL序號</DropdownItem>
+          <DropdownItem name="5">僅顯示已完成綁定的序號</DropdownItem>
+          <DropdownItem name="6">僅顯示已到期序號</DropdownItem>
+        </DropdownMenu>
+      </Dropdown>
+    </div>
+    <div
+      class="hiteach-list"
+      v-for="(item, index) in hiteachAuthList"
+      :key="index"
+    >
+      <div
+        class="hiteach-item"
+        v-for="(i, index) in item.serials.auth"
+        :key="index + 'a'"
+        v-show="filterCondition(i)"
+      >
+        <div
+          class="detail-btn"
+          @click="setIntroOpen(index)"
+          v-if="i.endDate != 1 && i.deviceBound.length != 0"
+        >
+          <Icon
+            v-show="hiteachListItemIsOpen[index].isOpen == false"
+            type="ios-arrow-down"
+          />
+          <Icon
+            v-show="hiteachListItemIsOpen[index].isOpen == true"
+            type="ios-arrow-up"
+          />
+        </div>
+        <Row>
+          <i-col :xs="24" :sm="24" :md="24" :lg="3">
+            <div class="hicon-wrap">
+              <v-icon
+                v-if="i.prodName === 'STD'"
+                iconClass="hiteach_std"
+              /><v-icon
+                v-if="i.prodName === 'PRO'"
+                iconClass="hiteach_pro"
+              /><v-icon v-if="i.prodName === 'TBL'" iconClass="hiteach_tbl" />
+            </div>
+          </i-col>
+          <i-col :xs="24" :sm="24" :md="24" :lg="21">
+            <p class="title">
+              {{ i.prodGroup }} {{ i.prodName }}
+              <span class="device-max">{{
+                i.deviceMax == 1 ? "單一" : "大量"
+              }}</span>
+            </p>
+            <p class="serial" :class="{ 'overtime-text': i.endDate == 1 }">
+              {{ i.serial }}
+            </p>
+            <p class="app-group">
+              <span
+                class="more-app"
+                :class="{ 'bought-app': i.aprule.sokapp == true }"
+                >蘇格拉底議課</span
+              >
+              <span
+                class="more-app"
+                :class="{ 'bought-app': i.aprule.sokrepor == true }"
+                >蘇格拉底報告</span
+              >
+              <span
+                class="more-app"
+                :class="{ 'bought-app': i.aprule.sokDesktop == true }"
+                >蘇格拉底桌面</span
+              >
+              <span
+                class="more-app"
+                :class="{ 'bought-app': i.aprule.sokvdo == true }"
+                >蘇格拉底影片</span
+              >
+              <span
+                class="more-app"
+                :class="{ 'bought-app': i.aprule.remoteSys == true }"
+                >遠距教室服務</span
+              >
+              <span
+                class="more-app"
+                :class="{ 'bought-app': i.aprule.ezs == true }"
+                >錄播系統</span
+              >
+            </p>
+            <Row>
+              <i-col :xs="24" :sm="24" :md="24" :lg="6"
+                ><p class="nomal-text">
+                  序號到期日:<span class="strong-text" v-if="i.endDate != 1">{{
+                    i.endDate == 0 ? "無到期日" : converTime(i.endDate)
+                  }}</span>
+                  <span class="strong-text" v-if="i.endDate == 1">已到期</span>
+                </p></i-col
+              >
+              <i-col :xs="24" :sm="24" :md="24" :lg="18"
+                ><p class="nomal-text " v-if="i.deviceMax != 1" style="margin-left:4%">
+                  序號使用狀況:<span class="strong-text" v-if="i.endDate != 1"
+                    >{{
+                      i.deviceBound.length > i.deviceMax
+                        ? i.deviceMax
+                        : i.deviceBound.length
+                    }}
+                    / {{ i.deviceMax }} ({{
+                      (i.deviceBound.length > i.deviceMax
+                        ? 100
+                        : (i.deviceBound.length / i.deviceMax) * 100
+                      ).toFixed(0) + "%"
+                    }})</span
+                  >
+                  <span class="strong-text" v-if="i.endDate == 1"
+                    >0 / {{ i.deviceMax }} (0%)</span
+                  >
+                </p></i-col
+              >
+            </Row>
+          </i-col>
+        </Row>
+        <ul
+          class="pclist"
+          v-if="i.endDate != 1 && hiteachListItemIsOpen[index].isOpen == true"
+        >
+          <li
+            v-for="(pc, index) in i.deviceBound"
+            :key="index + 'pc'"
+            v-show="i.deviceMax != 1 ? index < i.deviceMax : index < 1"
+            class="pclist-item"
+          >
+            <p class="pc-title">UserPC-{{ pc.uuid }}</p>
+            <p class="nomal-text">{{ pc.deviceIp }} | {{ pc.deviceMac }}</p>
+            <p class="nomal-text">{{ pc.computer }}</p>
+            <div class="action-btn"><Icon type="md-close" /> 解除授權</div>
+          </li>
+          <!--div v-if="i.deviceBound==0" class='pclist-empty' >尚未綁定任何機台</div-->
+        </ul>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import "@/icons/svg/hiteach_std.svg";
+import "@/icons/svg/hiteach_pro.svg";
+import "@/icons/svg/hiteach_tbl.svg";
+import { mapGetters } from "vuex";
+export default {
+  props: ["hiteachAuthList"],
+  name: "HiteachAuthList",
+  data() {
+    return {
+      hiteachListItemIsOpen: {},
+      filterType: "1",
+    };
+  },
+  created() {
+    this.hiteachListItemIsOpen = this.hiteachListItemIsOpenVuex;
+  },
+  computed: {
+    ...mapGetters({
+      hiteachListItemIsOpenVuex: "serviceDriveAuth/getHiteachListItemIsOpen",
+    }),
+  },
+  watch: {
+    hiteachAuthList: function (value) {
+      //根據服務的長度生出開關,只要服務數量變動就必須重置
+      let temp = [];
+      let hiteachlistlength = value[0].serials.auth.length;
+      for (let i = 0; i < hiteachlistlength; i++) {
+        temp.push({
+          index: i,
+          isOpen: false,
+        });
+      }
+      console.log(temp);
+      this.hiteachListItemIsOpen = temp;
+    },
+  },
+  methods: {
+    gotoHiteachMgmt() {
+      this.$router.push("/home/classroom");
+    },
+    filterCondition(item) {
+      switch (this.filterType) {
+        case "1":
+          return true;
+
+        case "2":
+          return item.prodName === "STD";
+        case "3":
+          return item.prodName === "PRO";
+        case "4":
+          return item.prodName === "TBL";
+        case "5":
+          return item.deviceBound.length != 0 && item.endDate != 1;
+        case "6":
+          return item.endDate == 1;
+
+        default:
+          return true;
+      }
+    },
+    filterList(name) {
+      this.filterType = name;
+    },
+    converTime: function (time) {
+      //time: Unix timestamp(秒)
+      let datetime = new Date();
+      //let timerecount = time * 1000 - datetime.getTimezoneOffset() * 60 * 1000;
+      let timerecount = time * 1000;
+      datetime.setTime(timerecount);
+      let year = datetime.getFullYear();
+      let month = datetime.getMonth() + 1;
+      let date = datetime.getDate();
+      let hour = datetime.getHours();
+      let ampm = hour >= 12 ? "PM" : "AM";
+      hour %= 12;
+      hour = hour || 12; // the hour '0' should be '12'
+      let minute = datetime.getMinutes();
+      return year + "." + month + "." + date;
+    },
+    setIntroOpen(index) {
+      this.hiteachListItemIsOpen[index].isOpen = !this.hiteachListItemIsOpen[
+        index
+      ].isOpen;
+      console.log(index, this.hiteachListItemIsOpen);
+    },
+  },
+};
+</script>
+
+<style lang='less'>
+@import "./HiteachAuthList.less";
+</style>

+ 35 - 0
TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/ServiceList.css

@@ -0,0 +1,35 @@
+.service-list .service-listTitle {
+  padding: 14px 0px;
+  color: #e2e2e3;
+  font-size: 14px;
+  border-bottom: 1px solid #464646;
+  margin-left: 20px;
+}
+.service-list .listTitle-noMargin {
+  margin-left: 0px;
+  padding-left: 20px ;
+}
+.service-list .service-search {
+  position: relative;
+  margin-top: -60px;
+  margin-bottom: 10px;
+  padding: 20px 0px;
+}
+.service-list .service-search .action-btn-icon {
+  cursor: pointer;
+  font-size: 20px;
+  position: absolute;
+  top: 25px;
+  right: 10px;
+}
+.service-list .service-search .dark-iview-input {
+  position: absolute;
+  right: 10px;
+  width: 50%;
+}
+.service-list .serviceList-wrap {
+  padding-left: 20px;
+}
+.service-list .serviceList-wrap .service-item .service-name {
+  font-size: 20px;
+}

+ 182 - 0
TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/ServiceList.less

@@ -0,0 +1,182 @@
+.service-list {
+  .service-listTitle {
+    padding: 14px 0px;
+    color: #bab8c0;
+    font-size: 14px;
+    font-weight: 600;
+    border-bottom: 1px solid #464646;
+    margin-left: 20px;
+  }
+  .listTitle-noMargin {
+    margin-left: 0px;
+    padding-left: 20px;
+  }
+  .service-search {
+    position: relative;
+    margin-top: -60px;
+    margin-bottom: 10px;
+    padding: 20px 0px;
+    .action-btn-icon {
+      cursor: pointer;
+      font-size: 20px;
+      position: absolute;
+      top: 25px;
+      right: 10px;
+    }
+    .dark-iview-input {
+      position: absolute;
+      right: 10px;
+      width: 50%;
+    }
+  }
+
+  .serviceList-wrap {
+    background-color: #232227;
+    height: 87.5vh;
+    overflow: auto;
+   
+    .service-item {
+      background-color: #2b2a2f;
+      border: 1px solid rgba(98, 97, 101, 0.4);
+      border-radius: 4px;
+      margin: 15px;
+      box-shadow: 1px 1px 13px #141414;
+      padding: 30px;
+      padding-bottom: 20px;
+      position: relative;
+      .action-btn{
+        cursor: pointer;
+        border-radius: 5px;
+        margin-top:15px;
+        padding: 5px 12px;
+        font-size: 12px;
+        display: inline-block;
+        background-color: #1cc0f3;
+        color: white;
+        font-weight: bolder;
+        &:hover{
+          background-color: darken(#1cc0f3,10%)
+        }
+      }
+      .detail-btn{
+        position: absolute;
+        top:20px;
+        right:20px;
+        font-size:30px;
+        cursor: pointer;
+        &:hover{
+          color: #1cc0f3;
+        }
+      }
+      .service-name {
+        font-size: 18px;
+        font-weight: bolder;
+      }
+      .isbuy,.detail{
+        font-size: 10px;
+       
+        color:#1cc0f3
+      }
+      .detail{
+        padding:5px 0px; 
+        font-weight: bolder;
+        
+        
+      }
+      .detailinfo-block{
+        animation: 0.5s fadein ;
+        @keyframes fadein {
+          0%{
+            opacity: 0;
+          }
+          100%{
+            opacity: 1;
+          }
+        }
+      }
+     
+      
+      .unbuy, .nomal-text{
+        font-size: 10px;
+        color: #8d8d8d;
+        
+      }
+      
+      .isbuy,.unbuy{
+        line-height: 24px;
+      }
+      .auth-num{
+        line-height: 30px;
+      }
+      .strong-text{
+        font-size: 10px;
+        font-weight: 700;
+        color: #dbdbdb;
+ 
+      }
+      .intro-text{
+        font-size: 10px;
+        font-weight: 700;
+        color: #8d8d8d;
+      }
+      .title-rec{
+        position: relative;
+        top:2px;
+        margin-right:5px;
+        width: 3px;
+        height: 12px;
+        display: inline-block;
+        background-color: white;
+        &:first-child{
+          background-color: #00f492;
+        }
+        &:nth-child(2n){
+          background-color: #d258ae;
+        }
+      }
+      .title-withRec{
+        display: inline-block;
+      }
+      .time-bar{
+        background-color:  #4a4a4d;
+        border-radius: 5px;
+        width:95%;
+        height: 4px;
+        top:8px;
+        position: relative;
+        margin-bottom:8px;
+      }
+      .remain-bar{
+        border-radius: 5px;
+        width:90%;
+        height: 4px;
+        top:-4px;
+        position: relative;
+        animation:0.5s spanBar
+       
+      }
+      @keyframes  spanBar {
+        0%{
+          width: 0%;
+        }
+       
+      }
+      .remainDay{
+        position: relative;
+        top:-3px;
+        font-size: 7px ;
+       display: inline-block;
+        margin-bottom:8px;
+        color: #8d8d8d;
+      }
+      .remainDay-text{
+        font-weight: bolder;
+        padding:0px 5px;
+        font-size:15px !important;
+        display: inline-block;
+        top:-3px;
+        position: relative;
+      }
+    }
+  }
+}

+ 208 - 0
TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/ServiceList.vue

@@ -0,0 +1,208 @@
+<template>
+  <div class="service-list">
+    <div class="service-listTitle listTitle-noMargin">服務授權列表</div>
+    <div class="serviceList-wrap">
+      <div
+        class="service-item"
+        v-for="(item, index) in serviceList"
+        :key="index"
+      >
+        <div class="detail-btn" @click="setIntroOpen(index)">
+          <Icon
+            v-show="serviceIntroIsOpen[index].isOpen == false"
+            type="ios-arrow-down"
+          />
+          <Icon
+            v-show="serviceIntroIsOpen[index].isOpen == true"
+            type="ios-arrow-up"
+          />
+        </div>
+
+        <p class="service-name">{{ item.name }}</p>
+        <p class="isbuy" v-if="item.isBuy == true">已購買</p>
+        <p class="unbuy" v-else>未購買</p>
+        <p class="nomal-text">
+          服務啟用 / 到期日:
+          <span class="strong-text" v-if="item.isBuy == true"
+            >{{ item.authTimeStart }} - {{ item.authTimeEnd }}</span
+          >
+          <span class="strong-text" v-else>--</span>
+        </p>
+        <div>
+          <Row>
+            <i-col :xs="24" :sm="24" :md="24" :lg="16">
+              <div class="time-bar" v-if="item.isBuy == true"></div>
+
+              <div
+                class="remain-bar"
+                v-if="item.isBuy == true"
+                :style="{
+                  width:
+                    (
+                      (dateRemain(today(), item.authTimeEnd) /
+                        dateRemain(item.authTimeStart, item.authTimeEnd)) *
+                      100
+                    ).toFixed(2) + '%',
+                  'background-color': colorPercent(
+                    item.authTimeStart,
+                    item.authTimeEnd
+                  )
+                    ? '#ad3435'
+                    : '#1cc0f3',
+                }"
+              ></div>
+            </i-col>
+            <i-col :xs="24" :sm="24" :md="24" :lg="8" v-if="item.isBuy == true">
+              <span class="remainDay">剩餘</span>
+              <span
+                class="remainDay-text"
+                :style="{
+                  color: colorPercent(item.authTimeStart, item.authTimeEnd)
+                    ? '#ad3435'
+                    : '#1cc0f3',
+                }"
+                >{{ dateRemain(today(), item.authTimeEnd) }}</span
+              ><span class="remainDay">天</span>
+            </i-col>
+          </Row>
+        </div>
+        <!--給AclassOne-->
+        <div class="detailinfo-block" v-if="index == 0 && serviceIntroIsOpen[index].isOpen == true">
+          <p class="nomal-text auth-num">
+            授權數量:<span class="strong-text">{{ item.authNum }}</span>
+          </p>
+          <Row>
+            <i-col :xs="24" :sm="24" :md="24" :lg="10">
+              <div class="title-rec"></div>
+              <p class="nomal-text title-withRec">
+                固定分配數:<span class="strong-text"
+                  >{{ item.fixedNum }} ({{
+                    ((item.fixedNum / item.authNum) * 100).toFixed(1)
+                  }}%)</span
+                >
+              </p>
+              <br />
+              <div class="title-rec"></div>
+              <p class="nomal-text title-withRec">
+                動態分配數:<span class="strong-text"
+                  >{{ item.dynamicNum }} ({{
+                    ((item.dynamicNum / item.authNum) * 100).toFixed(1)
+                  }}%)</span
+                >
+              </p></i-col
+            >
+            <i-col :xs="24" :sm="24" :md="24" :lg="7">
+              <p class="nomal-text">
+                總使用率:<span class="strong-text">
+                  {{
+                    (
+                      ((item.dynamicNum + item.fixedNum) / item.authNum) *
+                      100
+                    ).toFixed(1)
+                  }}%</span
+                >
+              </p></i-col
+            >
+            <i-col :xs="24" :sm="24" :md="24" :lg="7">  <AclassOneChart /></i-col>
+          </Row>
+      
+          <div class="action-btn" @click="gotoStudentManagent()">
+            管理AClassOne授權
+          </div>
+        </div>
+        <!--給其他產品-->
+        <div class="detailinfo-block" v-if="index != 0 && serviceIntroIsOpen[index].isOpen == true">
+          <p class="detail">詳細服務內容:</p>
+          <Row>
+            <i-col :xs="24" :sm="24" :md="24" :lg="16">
+              <p class="intro-text">{{ item.intro }}</p></i-col
+            >
+          </Row>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import router from "../../../router";
+import AclassOneChart from './AclassOneChart'
+import { mapGetters } from "vuex";
+export default {
+  props: ["serviceList"],
+  name: "ServiceList",
+  data() {
+    return {
+      serviceIntroIsOpen: {},
+    };
+  },
+  components:{
+    AclassOneChart
+  },
+  computed: {
+    ...mapGetters({
+      serviceIntroIsOpenVuex: "serviceDriveAuth/getServiceIntroIsOpen",
+    }),
+  },
+  created() {
+    this.serviceIntroIsOpen = this.serviceIntroIsOpenVuex;
+  },
+  watch: {
+    serviceList: function () {
+      //根據服務的長度生出開關,只要服務數量變動就必須重置
+      let temp = [];
+
+      for (let i = 0; i < this.serviceList.length; i++) {
+        temp.push({
+          index: i,
+          isOpen: false,
+        });
+      }
+      this.serviceIntroIsOpen = temp;
+    },
+  },
+
+  methods: {
+    gotoStudentManagent() {
+      this.$router.push("/home/studentAccount");
+    },
+    colorPercent: function (a, b) {
+      if (this.dateRemain(this.today(), b) / this.dateRemain(a, b) < 0.3)
+        return true;
+      else return false;
+    },
+    setIntroOpen(index) {
+      this.serviceIntroIsOpen[index].isOpen = !this.serviceIntroIsOpen[index]
+        .isOpen;
+      console.log(index, this.serviceIntroIsOpen);
+    },
+    today() {
+      var today = new Date();
+      var dd = String(today.getDate()).padStart(2, "0");
+      var mm = String(today.getMonth() + 1).padStart(2, "0"); //January is 0!
+      var yyyy = today.getFullYear();
+
+      today = yyyy + "/" + mm + "/" + dd;
+      return today;
+    },
+    dateRemain(today, endday) {
+      let yyyy = endday.substr(0, 4);
+      let mm = endday.substr(5, 1);
+      let dd = endday.substr(7, 2);
+      let strEndday = yyyy + "/" + mm + "/" + dd;
+
+      //console.log(today, strEndday);
+
+      let oDate1 = new Date(today);
+      let oDate2 = new Date(strEndday);
+      let iDays = parseInt(Math.abs(oDate1 - oDate2) / 1000 / 60 / 60 / 24); // 把相差的毫秒數轉換為天數
+      //console.log(iDays);
+      return iDays;
+    },
+  },
+};
+</script>
+
+<style lang='less'>
+@import "./ServiceList.less";
+</style>

+ 224 - 0
TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/SpaceChart.vue

@@ -0,0 +1,224 @@
+<!--課堂參與人次觀察之環形圖-->
+<template>
+  <div id="space-chart"></div>
+</template>
+
+<script>
+export default {
+  name: "SpaceChart",
+  data() {
+    return {
+      currentColor:"#fff",
+      e: event,
+      index: 0,
+      mockdata: {
+        pieNumData: [
+          { value: (573 * 0.03).toFixed(0), name: "文件", days: 66 },
+          { value: (573 * 0.07).toFixed(0), name: "影片", days: 66 },
+          { value: (573 * 0.16).toFixed(0), name: "圖片", days: 66 },
+          { value: (573 * 0.04).toFixed(0), name: "題目與試卷", days: 26 },
+          { value: (573 * 0.1).toFixed(0), name: "其他", days: 66 },
+          { value: (573 * 0.25).toFixed(0), name: "學生使用", days: 66 },
+          { value: (573 * 0.25).toFixed(0), name: "已分配至教師", days: 166 },
+          { value: 1010 - 573, name: "未使用" , days: 166},
+        ],
+      },
+    };
+  },
+  mounted() {
+    this.drawLine(this.mockdata.pieNumData);
+    
+  },
+  created() {
+    this.$emit("StudentSourceInfo", this.mockdata.pieNumData);
+  },
+  watch:{
+    currentColor:function(val){
+
+      //console.log(document.getElementById('color-title'))
+       }
+ 
+  },
+  methods: {
+    drawLine(pieNumData) {
+      //基于准备好的dom,初始化echarts实例
+      let _this=this;
+      let myChart = this.$echarts.init(document.getElementById("space-chart")); //绘制图表
+      myChart.setOption({
+        tooltip: {
+          trigger: "item",
+          formatter: function (v) {
+           _this._data.currentColor=v.color
+
+            return `
+            <div class='chart-toolTip'> 
+             <p :style="{'color':${v.color}" >${v.data.name}空間</p>
+             <p class='title'>${v.data.name}空間數:<span class='value'>${v.value} G</span></p>
+             <p class='title'>${v.data.name}空間比率:<span class='value'>${v.percent} %</span></p>
+             <p class='title'>剩餘天數:<span class='value'>${v.data.days} days</span> </p> </div> 
+            `;
+          },
+        },
+        /*legend: {
+          show:false,
+          animation:false,
+          orient: "vertical",
+          icon: "circle",
+          top: 40,
+          textStyle: {
+            color: "white",
+          },
+
+          //抓值印在標籤上
+          formatter: function (name) {
+            let data = pieNumData;
+            let total = 0; //回傳自行運算百分比
+            let target;
+            for (let i = 0; i < data.length; i++) {
+              total += data[i].value;
+              if (data[i].name == name) {
+                target = data[i].value;
+              }
+            }
+            let arr = ((target / total) * 100).toFixed(2) + "%";
+            //console.log(arr)
+            return name + " \n " + arr;
+          },
+          right: 0,
+        },*/
+        color: [
+          "#f862bb",
+          "#f6d76d",
+          "#65dcda",
+          "#00f492",
+          "#d6b8ff",
+          "#89c4f4",
+          "#ffab4b",
+          "#48474c",
+        ],
+
+        series: [
+          {
+            type: "pie",
+            radius: ["65%", "90%"],
+            //right: 100,
+            avoidLabelOverlap: true,
+            hoverOffset: 0, //調整餅圖凸顯時的高度
+            label: {
+              show: false,
+              position: "center",
+
+              formatter:
+                "{title1|{b}空間數}\n{num|{c}}{chracter|GB}\n\n{title2|空間占比}\n {percent|{d}}{chracter|%}",
+              align: "left",
+              rich: {
+                title1: { fontWeight: "bold", padding: [2, 15, 2, 0] },
+                title2: { fontWeight: "bold", padding: [2, 63, 2, 0] },
+                chracter: {
+                  fontSize: 10,
+                  color: "white",
+                  padding: [0, 0, 10, 3],
+                },
+                picon: {
+                  color: "white",
+                },
+                percent: {
+                  color: "white",
+                  fontSize: 30,
+                  fontWeight: "bold",
+                },
+                num: {
+                  color: "white",
+                  fontSize: 30,
+                  fontWeight: "bold",
+                  padding: [2, 0, 2, -20],
+                },
+              },
+            },
+            startAngle: 90,
+            emphasis: {
+              label: {
+                show: false, //把餅圖中間預設顯示各類圖例的效果關掉
+                fontSize: "18",
+                fontWeight: "bold",
+              },
+            },
+            labelLine: {
+              show: false,
+            },
+            itemStyle: {
+              borderColor: "#2b2b2e",
+              borderWidth: 2,
+            },
+            data: this.mockdata.pieNumData,
+          },
+        ],
+      });
+
+      // Set the default selected highlights
+      myChart.dispatchAction({
+        type: "highlight",
+        seriesIndex: 0,
+        dataIndex: 7,
+      });
+
+      myChart.on("mouseover", function (e) {
+        //Highlight the hovering piece
+        myChart.dispatchAction({
+          type: "downplay",
+          seriesIndex: 0,
+          dataIndex: 0,
+        });
+
+        if (e.dataIndex == 0) {
+          myChart.dispatchAction({
+            type: "highlight",
+            seriesIndex: 0,
+            dataIndex: e.dataIndex,
+          });
+        }
+        if (e.dataIndex != this.index) {
+          myChart.dispatchAction({
+            type: "downplay",
+            seriesIndex: 0,
+            dataIndex: this.index,
+          });
+        }
+      });
+      myChart.on("mouseout", function (e) {
+        this.index = e.dataIndex;
+        myChart.dispatchAction({
+          type: "highlight",
+          seriesIndex: 0,
+          dataIndex: this.index,
+        });
+      });
+    },
+  },
+};
+</script>
+
+<style lang='less'>
+#space-chart {
+  position: relative;
+  height: 250px;
+  margin-top: -20px;
+  margin-bottom: 20px;
+  margin-left:2%;
+  .chart-toolTip{
+    outline:1px solid rgba(255, 255, 255, 0.5);
+    padding:10px;
+    .title{
+     
+      font-size: 10px;
+      color:gray;
+      font-weight: bolder;
+    }
+    .value{
+      font-weight: bolder;
+      color:white
+    }
+
+  }
+}
+</style>

+ 142 - 0
TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/SpaceStatus.less

@@ -0,0 +1,142 @@
+.space-status {
+  border-left: 1px solid rgba(98, 97, 101, 0.4);
+  .service-list {
+    position: relative;
+    .action-btn {
+      cursor: pointer;
+      border-radius: 5px;
+      margin-top: 15px;
+      padding: 5px 12px;
+      font-size: 12px;
+      display: inline-block;
+      background-color: #1cc0f3;
+      color: white;
+      position: absolute;
+      right: 20px;
+      bottom: 12px;
+      font-weight: 700;
+      &:hover {
+        background-color: darken(#1cc0f3, 10%);
+      }
+      .share-icon {
+        position: relative;
+        top: -1px;
+        -moz-transform: scaleX(-1);
+        -webkit-transform: scaleX(-1);
+        -o-transform: scaleX(-1);
+        transform: scaleX(-1);
+      }
+    }
+  }
+  .nomal-title {
+    font-size: 10px;
+    color: #8d8d8d;
+    font-weight: bolder;
+  }
+
+  .note,
+  .nomal-text {
+    font-size: 10px;
+    color: #8d8d8d;
+  }
+  .strong-text {
+    font-size: 10px;
+    font-weight: 700;
+    color: #dbdbdb;
+  }
+  .deadline-title {
+    font-size: 16px;
+    color: #8d8d8d;
+    font-weight: bolder;
+    .deadline-time {
+      color: #1cc0f3;
+    }
+  }
+  .info-text {
+    color: #fafafa;
+    font-weight: bolder;
+    .info-num {
+      font-size: 26px;
+      font-weight: 600;
+    }
+  }
+  .main-wrap {
+    padding: 30px;
+  }
+  .chart-wrap {
+    margin-top: 30px;
+  }
+  .buy-list {
+    border-top: 1px solid rgba(98, 97, 101, 0.4);
+   
+    .list-wrap {
+      height: 280px;
+      overflow: auto;
+      border-bottom: 1px solid rgba(98, 97, 101, 0.4);
+    }
+
+    .service-listTitle {
+      padding: 10px 0px;
+      font-size: 10px;
+    }
+    .buy-item {
+      .tmd-icon {
+        font-size: 30px;
+        position: relative;
+        top: 5px;
+        left: -5px;
+      }
+      .title {
+        font-size: 16px;
+        font-weight: 600;
+      }
+      margin-left: 20px;
+      padding: 20px;
+      border-bottom: 1px solid rgba(98, 97, 101, 0.4);
+    }
+  }
+
+  .buy-btn {
+    cursor: pointer;
+    border-radius: 5px;
+    margin: 20px 11%;
+    width: 80%;
+    padding: 5px 12px;
+    font-size: 20px;
+   
+    background-color: #1cc0f3;
+    color: white;
+    text-align: center;
+   
+    font-weight: 700;
+    &:hover {
+      background-color: darken(#1cc0f3, 10%);
+    }
+  }
+  .remail-data{
+    z-index:2;
+    width: 100px;
+    position: relative;
+    top:-199px;
+    margin-bottom:-120px;
+    left:37%;
+    .title{
+      color:rgb(141, 141, 141);
+      font-weight: bolder;
+      font-size: 10px;
+    }
+    .num{
+      color: white;
+      font-weight: 500;
+      font-size: 28px;
+      line-height: 28px;
+      margin-bottom:5px;
+      .small-text{
+        font-size: 12px;
+      }
+    }
+
+  }
+
+}
+

+ 158 - 0
TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/SpaceStatus.vue

@@ -0,0 +1,158 @@
+<template>
+  <div class="space-status">
+    <div class="service-list">
+      <div class="service-listTitle">智慧教學服務空間狀態</div>
+      <div class="action-btn" @click="gotoTeacherManagent()">
+        <Icon class="share-icon" type="md-share" /> 分配教學空間
+      </div>
+    </div>
+    <Row class="main-wrap">
+      <i-col :xs="24" :sm="24" :md="24" :lg="12">
+        <p class="nomal-title">空間總數</p>
+        <p class="info-text">
+          <span class="info-num">{{
+            numberWithCommas(spaceStatus.avaliable)
+          }}</span>
+          GB
+        </p>
+      </i-col>
+      <i-col :xs="24" :sm="24" :md="24" :lg="12">
+        <p class="nomal-title">已使用空間總數</p>
+        <p class="info-text">
+          <span class="info-num">{{ numberWithCommas(spaceStatus.used) }}</span>
+          GB
+        </p>
+      </i-col>
+
+      <i-col :xs="24" :sm="24" :md="24" :lg="24">
+        <div class="chart-wrap">
+          <p class="nomal-title">空間使用狀況</p>
+          <SpaceChart />
+          <!--放置餅圖剩下的空間狀態-->
+          <div class="remail-data">
+            <p class='title'>未使用空間數</p>
+            <p class='num'>{{1010 - 573}}<span class='small-text'> GB</span></p>
+            <p class='title'>未使用空間比率</p>
+             <p class='num'>45.9<span class='small-text'> %</span></p>
+          </div>
+        </div>
+      </i-col>
+      <i-col :xs="24" :sm="24" :md="24" :lg="24">
+        <p class="deadline-title">
+          當前空間到期日:<span class="deadline-time"
+            >{{ converTime(spaceStatus.lastEndDay) }} (
+            {{ dateRemain(today(), converTime(spaceStatus.lastEndDay)) + "天" }}
+            )</span
+          >
+        </p>
+
+        <p class="note">
+          ( 到期後將變更為IES基本空間,超出基本空間值將被收回 )
+        </p>
+      </i-col>
+    </Row>
+    <div class="service-list buy-list">
+      <div class="service-listTitle">購買記錄</div>
+      <div class="list-wrap">
+        <div
+          class="buy-item"
+          v-for="(i, index) in spaceStatus.order"
+          :key="index"
+        >
+          <Row>
+            <i-col :xs="24" :sm="24" :md="24" :lg="3"
+              ><v-icon class="tmd-icon" iconClass="tmd"></v-icon
+            ></i-col>
+            <i-col :xs="24" :sm="24" :md="24" :lg="21"
+              ><p class="title">
+                醍摩豆{{ spaceStatus.prodName }}
+                {{ numberWithCommas(i.number) }} {{ i.unit }}
+              </p>
+              <Row s>
+                <i-col :xs="24" :sm="24" :md="24" :lg="12"
+                  ><p class="nomal-text">
+                    訂單日期:<span class="strong-text">{{
+                      converTime(i.createDate)
+                    }}</span>
+                  </p></i-col
+                >
+                <i-col :xs="24" :sm="24" :md="24" :lg="12"
+                  ><p class="nomal-text">
+                    到期日:<span class="strong-text">{{
+                      converTime(i.endDate)
+                    }}</span>
+                  </p></i-col
+                >
+              </Row>
+            </i-col>
+          </Row>
+        </div>
+      </div>
+      <div class="buy-btn">添購其他服務</div>
+    </div>
+  </div>
+</template>
+
+<script>
+import SpaceChart from "./SpaceChart.vue";
+import "@/icons/svg/tmd.svg";
+export default {
+  props: ["spaceStatus"],
+  name: "SpaceStatus",
+  components: {
+    SpaceChart,
+  },
+  methods: {
+    gotoTeacherManagent() {
+      this.$router.push("/home/teachermgmt");
+    },
+    numberWithCommas(x) {
+      if (x != undefined)
+        return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+    },
+    converTime: function (time) {
+      //time: Unix timestamp(秒)
+      let datetime = new Date();
+      //let timerecount = time * 1000 - datetime.getTimezoneOffset() * 60 * 1000;
+      let timerecount = time * 1000;
+      datetime.setTime(timerecount);
+      let year = datetime.getFullYear();
+      let month = datetime.getMonth() + 1;
+      let date = datetime.getDate();
+      let hour = datetime.getHours();
+      let ampm = hour >= 12 ? "PM" : "AM";
+      hour %= 12;
+      hour = hour || 12; // the hour '0' should be '12'
+      let minute = datetime.getMinutes();
+      return year + "." + month + "." + date;
+    },
+    today() {
+      var today = new Date();
+      var dd = String(today.getDate()).padStart(2, "0");
+      var mm = String(today.getMonth() + 1).padStart(2, "0"); //January is 0!
+      var yyyy = today.getFullYear();
+
+      today = yyyy + "/" + mm + "/" + dd;
+      return today;
+    },
+    dateRemain(today, endday) {
+      let yyyy = endday.substr(0, 4);
+      let mm = endday.substr(5, 1);
+      let dd = endday.substr(7, 2);
+      let strEndday = yyyy + "/" + mm + "/" + dd;
+
+      //console.log(today, strEndday);
+
+      let oDate1 = new Date(today);
+      let oDate2 = new Date(strEndday);
+      let iDays = parseInt(Math.abs(oDate1 - oDate2) / 1000 / 60 / 60 / 24); // 把相差的毫秒數轉換為天數
+      //console.log(iDays);
+      return iDays;
+    },
+  },
+};
+</script>
+
+<style lang='less'>
+@import "./SpaceStatus.less";
+</style>

+ 77 - 0
TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/Spacebar.less

@@ -0,0 +1,77 @@
+.space-bar {
+  margin-left: 36px;
+  padding: 30px 0px;
+  .spacetotal-title {
+    color: #99999c;
+    font-weight: normal;
+    margin-bottom: 15px;
+  }
+  .spacetotal-num {
+    color: #ffffff;
+    font-weight: bold;
+  }
+  .spacetotal-solution {
+    color: #99999c;
+    font-size: 12px;
+    margin-top: 25px;
+    .colorball {
+      height: 13px;
+      width: 13px;
+      position: relative;
+      top: 2px;
+      display: inline-block;
+      border-radius: 50%;
+      margin: 0px 3px;
+    }
+    .solname {
+      color: white;
+      font-weight: bolder;
+    }
+  }
+
+  .colorbar-wrap {
+    width: 80%;
+
+    display: inline-block;
+    .color-Sprite {
+      height: 6px;
+      background-color: #505052;
+      border-radius: 3px;
+      margin-top: 10px;
+    }
+    .color-bar {
+      height: 6px;
+      display: inline-block;
+      &:first-child {
+        border-radius: 3px 0px 0px 3px;
+      }
+      &:last-child {
+        border-radius: 0px 3px 3px 0px;
+      }
+    }
+    .color-bar-bottom {
+      height: 6px;
+      display: inline-block;
+      position: relative;
+      top: -16px;
+      &:nth-child(2) {
+        border-radius: 3px 0px 0px 3px;
+      }
+      &:last-child {
+        border-radius: 0px 3px 3px 0px;
+      }
+    }
+  }
+  .colorbar-num {
+    width: 20%;
+    display: inline-block;
+    position: relative;
+    top: -15px;
+    left: 10px;
+  }
+  .colorbar-text {
+    position: relative;
+    top: -12px !important;
+    margin-right: 10px;
+  }
+}

+ 101 - 0
TEAMModelOS/ClientApp/src/view/serviceDriveAuth/SubComponents/Spacebar.vue

@@ -0,0 +1,101 @@
+<!---儲存空間組成條圖--->
+<template>
+  <div class="space-bar">
+    <h2 class="spacetotal-title">
+      校總空間量:<span class="spacetotal-num">2,500 GB</span>
+    </h2>
+    <div class="spacetotal-solution">
+      空間購置方案:
+      <span v-for="(item, index) in spaceTotalSolution" :key="index">
+        <div
+          class="colorball"
+          :style="{ 'background-color': `${item.color}` }"
+        ></div>
+        <span class="solname"
+          >{{ item.name }}({{ item.space }}G)<span v-if="index != 2">
+            +</span
+          ></span
+        >
+      </span>
+    </div>
+    <div class="colorbar-wrap">
+      <div
+        class="color-bar"
+        v-for="(item, index) in spaceTotalSolution"
+        :key="index"
+        :style="{
+          'background-color': `${item.color}`,
+          width: `${(item.space / 2500) * 100}%`,
+        }"
+      ></div>
+    </div>
+    <br />
+    <div class="spacetotal-solution">空間使用狀況:</div>
+    <div class="colorbar-wrap">
+      <div class="color-Sprite"></div>
+      <div
+        class="color-bar-bottom"
+        v-for="(item, index) in usingCondition"
+        :key="index + 'a'"
+        :style="{
+          'background-color': `${item.color}`,
+          width:  index==0?`${(item.space / 100) * 100}%`:`${(teacherSpaceUsing / 100) * 100}%`,
+        }"
+      ></div>
+    </div>
+    <div class="colorbar-num">
+      {{ usingCondition[0].space + teacherSpaceUsing }} GB / 100.0 GB
+    </div>
+    <span class="colorbar-text"
+      v-for="(item, index) in usingCondition"
+      :key="index"
+      :style="{ color: `${item.color}` }"
+      >{{ item.name }}:{{ index==0?item.space:teacherSpaceUsing }}GB</span
+    >
+     <span class="colorbar-text" :style="{ color: `#99999c ` }">剩餘空間:{{(100-(usingCondition[0].space + teacherSpaceUsing)).toFixed(2)}}GB</span>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "Spacebar",
+  props:['teacherSpaceUsing'],
+  data() {
+    return {
+      spaceTotalSolution: [
+        {
+          name: "IES5基本方案",
+          space: 500,
+          color: "#00f492",
+        },
+        {
+          name: "1500G擴充購置方案",
+          space: 1500,
+          color: "#f862bb",
+        },
+        {
+          name: "500G擴充購置方案",
+          space: 500,
+          color: "#65dcda",
+        },
+      ],
+      usingCondition: [
+        {
+          name: "IES5已使用空間",
+          space: 27.8,
+          color: "#d6b8ff",
+        },
+        {
+          name: "已分配至教師空間",
+          space: this.teacherSpaceUsing,
+          color: "#f6d76d",
+        },
+      ],
+    };
+  },
+};
+</script>
+
+<style lang='less'>
+@import "./Spacebar.less";
+</style>

+ 86 - 0
TEAMModelOS/ClientApp/src/view/serviceDriveAuth/serviceDriveAuthIview.less

@@ -0,0 +1,86 @@
+.teacher-list{
+    .ivu-table:before {
+        background-color:transparent !important;
+    }
+    .ivu-table{
+        background-color: transparent;
+      
+        .ivu-table-header{
+            tr:first-child{
+                background: -webkit-linear-gradient(left,#272727,#222222);
+                background: -o-linear-gradient(right,#272727,#222222);
+                background: -moz-linear-gradient(right,#272727,#222222);
+                background: linear-gradient(to right,#272727,#222222);
+            }
+        }
+        th {
+           background-color: transparent;
+           border-bottom: 1px solid #343434;
+           color: #757575;
+           font-size: 12px;
+        }
+        td{
+            background-color: transparent;
+        }
+        .ivu-table-tbody{
+            border-bottom: 1px solid #343434 !important;
+            td{
+                background-color: transparent;
+                border-bottom: 1px solid #343434;
+                color: #757575;
+                font-size: 12px;
+            }
+            .ivu-table-row{
+                td:nth-child(8){
+                    .ivu-table-cell{
+                        display: none;
+                    }
+                }
+            }
+        }
+        .ivu-table-row-hover{
+            background: -webkit-linear-gradient(left,#272727,#373737);
+            background: -o-linear-gradient(right,#272727,#373737);
+            background: -moz-linear-gradient(right,#272727,#373737);
+            background: linear-gradient(to right,#272727,#373737);
+            td:last-child{
+                .ivu-table-cell{
+                    display: block!important;
+                }
+            }
+        }
+        .ivu-checkbox-inner{
+            background-color: transparent;
+            border: 2px solid #343434;
+            border-radius: 3px;
+        }
+        .ivu-checkbox-checked .ivu-checkbox-inner:after{
+            width: 8px;
+            height: 14px;
+            top: -2px;
+            left: 4px;
+            border-color: #1eb38d;
+            border-right-width: 3px;
+            border-bottom-width: 3px;
+        }
+        .ivu-table-overflowY{
+            &::-webkit-scrollbar {
+                width: 5px;
+            }
+            &::-webkit-scrollbar-track {
+                // margin: 0px;
+                background: transparent;
+            }
+            &::-webkit-scrollbar-thumb {
+                border-radius: 10px;
+                background: #94998a;
+            }
+            /*&::-webkit-scrollbar-thumb:hover {
+             background: #555; 
+            }*/
+            &::-webkit-scrollbar-button {
+                display: none;
+            }
+        }
+    }
+}

+ 13 - 0
TEAMModelOS/ClientApp/src/view/settings/SchoolMgmt.less

@@ -6,6 +6,14 @@
 	display: flex;
 	height: 100%;
 	
+	.school-container-header{
+		display: flex;
+		justify-content: space-between;
+		padding-right: 20px;
+		font-weight:bold;
+		color: #fff;
+	}
+	
 	&-left{
 		width: 28%;
 		height: 100%;
@@ -83,6 +91,7 @@
 						color:#fff;
 						background: @primaryColor;
 						text-align: center;
+						visibility: hidden;
 						cursor: pointer;
 						
 						.ivu-icon{
@@ -93,6 +102,10 @@
 					
 					&:hover{
 						.item-active;
+						
+						.school-item-btn{
+							visibility: visible;
+						}
 					}
 				}
 			}

+ 55 - 26
TEAMModelOS/ClientApp/src/view/settings/SchoolMgmt.vue

@@ -1,10 +1,11 @@
 <template>
 	<div class="school-container">
+		<Loading v-show="isLoading"></Loading>
 		<div class="school-container-left">
-			<div class="school-container-header" style="display: flex;justify-content: space-between;padding-right: 20px;">
+			<div class="school-container-header">
 				<span>已添加或申请学校</span>
 				<span>
-					<Input placeholder="输入要搜索的学校名称...">
+					<Input placeholder="输入要搜索的学校名称..." @on-change="onSearchChange" v-model="mySchoolSearch">
 					<Icon type="ios-search" slot="prefix" />
 					</Input>
 				</span>
@@ -23,7 +24,7 @@
 								<span class="school-item-status" style="background-color: #008352;" v-show="item.schoolId === teacherInfo.defaultschool">默认学校</span>
 							</span>
 							<span class="school-item-code">{{ item.schoolId  }}</span>
-							<span class="school-item-nums" v-if="item.status === 100">
+							<span class="school-item-nums" v-if="item.status === 'join'">
 								<span>
 									<Icon type="md-bookmarks" />
 									<span>课程数</span>
@@ -35,32 +36,32 @@
 									<span style="margin-left: 10px;">{{ item.activity }}</span>
 								</span>
 							</span>
-							<span class="school-item-nums" v-if="item.status === 200">
+							<span class="school-item-nums" v-if="item.status === 'request'">
 								于 2020-08-02 提出加入请求
 							</span>
-							<span class="school-item-nums" v-if="item.status === 300">
+							<span class="school-item-nums" v-if="item.status === 'invite'">
 								于 2020-08-02 送出添加邀请
 							</span>
 							<span class="school-item-role">{{ item.role }}</span>
-							<span class="school-item-btn" v-if="item.status === 100" @click="onGoSchool(item)">
+							<span class="school-item-btn" v-if="item.status === 'join'" @click="onGoSchool(item)">
 								<span>
 									<Icon type="md-swap" style="transform: rotate(90deg);" />
 									<span>前往学校</span>
 								</span>
 							</span>
-							<span class="school-item-btn" style="background: rgb(202,54,54);" v-if="item.status === 200">
+							<span class="school-item-btn" style="background: rgb(202,54,54);" v-if="item.status === 'request'">
 								<span>
 									<Icon type="md-close" />
 									<span>取消申请</span>
 								</span>
 							</span>
-							<span class="school-item-btn" style="background: #1CC0F3;right: 140px;" v-if="item.status === 300">
+							<span class="school-item-btn" style="background: #1CC0F3;right: 140px;" v-if="item.status === 'invite'">
 								<span>
 									<Icon type="md-checkmark" />
 									<span>同意加入</span>
 								</span>
 							</span>
-							<span class="school-item-btn" style="background: rgb(202,54,54);" v-if="item.status === 300">
+							<span class="school-item-btn" style="background: rgb(202,54,54);" v-if="item.status === 'invite'">
 								<span>
 									<Icon type="md-close" />
 									<span>取消添加</span>
@@ -76,7 +77,7 @@
 				<span>申请加入学校</span>
 			</div>
 			<div class="search-wrap">
-				<Input placeholder="输入要搜索的学校名称或者代码...">
+				<Input placeholder="输入要搜索的学校名称或者代码..." @on-change="onSearchList" v-model="schoolListSearch">
 				<Icon type="ios-search" slot="prefix" />
 				</Input>
 				<!-- <Select v-model="curArea">
@@ -111,8 +112,12 @@
 				teacherInfo:{
 					defaultschool:''
 				},
+				mySchoolSearch:'',
+				schoolListSearch:'',
 				goSchoolModal: false,
+				isLoading:false,
 				mySchoolList: [],
+				originMySchoolList:[],
 				originList: [],
 				activeIndex: 0,
 				isLoadList: false,
@@ -122,6 +127,7 @@
 				curCity: 'default',
 				curSchoolItem: null,
 				schoolList: [],
+				originSchoolList:[],
 				schoolColumns: [{
 						title: '学校名称',
 						key: 'name',
@@ -174,6 +180,7 @@
 
 			/* 获取老师的个人清单信息 */
 			getTeacherInfo() {
+				this.isLoading = true
 				this.$api.schoolSetting.getTeacherInfo({
 					id_token: localStorage.getItem('id_token')
 				}).then(
@@ -181,7 +188,8 @@
 						if (!res.error) {
 							this.teacherInfo = res
 							this.mySchoolList = res.schools
-							this.originList = JSON.parse(JSON.stringify(this.mySchoolList))
+							this.originMySchoolList = JSON.parse(JSON.stringify(this.mySchoolList))
+							this.isLoading = false
 						}
 					},
 					(err) => {
@@ -213,6 +221,7 @@
 					(res) => {
 						if (!res.error) {
 							this.schoolList = res.schools
+							this.originSchoolList = JSON.parse(JSON.stringify(res.schools))
 						}
 					},
 					(err) => {
@@ -223,22 +232,41 @@
 			
 			/* 申请加入学校 */
 			onJoinSchool(item){
-				this.$api.schoolSetting.joinSchool({
-					grant_type:'join',
-					school_code:item.id,
-					school_name:item.name
-				}).then(
-					(res) => {
-						if (!res.error) {
-							// this.schoolList = res.schools
-							console.log(res)
-						}
-					},
-					(err) => {
-						this.$Message.error("数据获取失败!")
+				this.$Modal.confirm({
+				    title: '温馨提示',
+				    content: '<p>确认申请加入' + item.name + ' ?</p>',
+				    okText: '确认',
+				    cancelText: '取消',
+				    onOk: () => {
+						this.$api.schoolSetting.joinSchool({
+							grant_type:'join',
+							school_code:item.id,
+							school_name:item.name
+						}).then(
+							(res) => {
+								if (!res.error) {
+									this.$Message.success("提交成功!")
+								}
+							},
+							(err) => {
+								this.$Message.error("操作失败!")
+							}
+						)
+				    },
+					onCancel:() => {
 					}
-				)
-			}
+				})
+			},
+		
+			onSearchChange(val){
+				console.log(val)
+				this.mySchoolList = this.originMySchoolList.filter(item => item.name.indexOf(this.mySchoolSearch) > -1)
+			},
+			
+			onSearchList(val){
+				this.schoolList = this.originSchoolList.filter(item => item.name.indexOf(this.schoolListSearch) > -1)
+			},
+		
 		},
 		computed: {
 			statusFilter() { 
@@ -374,6 +402,7 @@
 			width: 80%;
 			height: 45px;
 			margin: 30px 10%;
+			border: none;
 		}
 	}
 </style>

+ 1 - 0
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/AchievementAnalysis/AchievementAnalysis.css

@@ -51,6 +51,7 @@
         flex-direction: row;
         /*justify-content: space-around;*/
         flex-wrap:wrap;
+		min-height: 160px;
     }
 
     .achievement-container .percent-item {

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

@@ -1,6 +1,7 @@
 <template>
     <div class="achievement-container">
-        <Row>
+        <!-- 及格率统计 -->
+		<Row>
             <span class="component-title" style="margin-top:40px">{{$t('totalAnalysis.ach_title1')}}</span>
         </Row>
         <Row class-name="component-percents">
@@ -9,11 +10,13 @@
                     <span class="percent-name">{{item.name}}</span>
                     <span class="percent-value"><span class="percent-line" :style="{background:colorList[index]}"></span>{{item.class}}</span>
                     <span class="percent-grade-value">{{$t('totalAnalysis.ach_text2')}}:{{item.grade}}</span>
-                    <span class="percent-area-value">{{$t('totalAnalysis.ach_text3')}}:{{item.area}}</span>
+                    <!-- <span class="percent-area-value">{{$t('totalAnalysis.ach_text3')}}:{{item.area}}</span> -->
                 </div>
             </div>
         </Row>
         <Divider />
+		
+		<!-- 均分分析 -->
         <Row>
             <span class="component-title">{{$t('totalAnalysis.ach_title2')}}</span>
         </Row>
@@ -21,6 +24,8 @@
             <BaseBar :echartsData="baseBarData" ref="baseBar"></BaseBar>
         </Row>
         <Divider />
+		
+		<!-- 进线人数统计 -->
         <Row>
             <span class="component-title">{{$t('totalAnalysis.ach_title3')}}</span>
             <div>
@@ -28,8 +33,12 @@
             </div>
         </Row>
         <Divider />
+		
+		<!-- 进线情况统计 -->
         <EntryTables ref="entryTable"></EntryTables>
         <Divider />
+		
+		<!-- 进线率统计 -->
         <Row>
             <span class="component-title">{{$t('totalAnalysis.ach_title6')}}</span>
         </Row>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 691 - 631
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/EvaluationList/TotalIndex.vue


+ 1 - 1
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/TestAnalysis/QuestionList.css

@@ -41,7 +41,7 @@
         padding: 10px 30px;
         color: #fff;
         /*border-radius: 5px;*/
-        z-index: 999;
+        z-index: 9;
     }
 
     .ql-left-box .btn-back .ivu-icon {

+ 3 - 2
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/index.css

@@ -78,9 +78,10 @@ body, html, .total-container {
 
 .total-body .total-content {
     position:relative;
-    width: -webkit-calc(100% - 300px);
+    /* width: -webkit-calc(100% - 300px);
     width: -moz-calc(100% - 300px);
-    width: calc(100% - 300px);
+    width: calc(100% - 300px); */
+	width: 100%;
     overflow-x: hidden;
     /*overflow-y: scroll;*/
     background: rgb(40, 40, 40);

+ 16 - 2
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/index.vue

@@ -2,7 +2,7 @@
     <div class="total-container">
         <div class="total-body">
             <!-- 评测列表展示组件 -->
-            <BaseExamList @chooseExam="chooseExam" @showEvaluationList="showEvaluationList" ref="examListRef"></BaseExamList>
+            <BaseExamList @chooseExam="chooseExam" @showEvaluationList="showEvaluationList" ref="examListRef" style="display: none;"></BaseExamList>
 
             <!-- 右侧数据展示区域 -->
             <div class="total-content" ref="dataContainer">
@@ -31,6 +31,7 @@
                         </p>
                         <div class="basic-tools">
                             <span class="basic-tool-export" @click="handleExportTables"> {{$t('totalAnalysis.exportTable')}} <Icon type="md-archive" /></span>
+                            <span class="basic-tool-export" @click="goEvaluationList"> 查看更多评测 <Icon type="md-arrow-round-forward" /></span>
                         </div>
                     </div>
                     <div class="data-select" v-if="!isShowQuestions" ref="dataSelect">
@@ -128,7 +129,10 @@
                         path = '/total'
                         // 往Vuex获取模块数据
 						console.log(this.currentExamItem)
-						this.$store.commit('updateCurExam', this.currentExamItem)
+						let vuexAchievement = this.$store.state.totalAnalysis.achievementData
+						if(!vuexAchievement){
+							this.$store.commit('updateCurExam', this.currentExamItem)
+						}
                         this.$store.dispatch('getAchievementData').then(res => {
                             setTimeout(function() {
                                 that.dataLoading = false
@@ -248,6 +252,16 @@
 				})
             },
 			
+			// 查看更多评测列表
+			goEvaluationList() {
+				this.$router.push({
+					name: 'totalIndex',
+					params: {
+						tabName: 'tab2'
+					}
+				})
+			},
+			
 			async getExamData(examIndex){
 				let examList = this.getVuexExamList
 				// 如果路由跳转到成绩分析页面带有考试序号参数则跳转到当前考试

+ 4 - 4
TEAMModelOS/ClientApp/src/view/teachcontent/index.vue

@@ -11,7 +11,6 @@
                     <span class="content-type-label">{{item.label}}</span>
                 </div>
                 <div :class="($access.can('admin.*|content-school-upd') || rangeType == 1) ? 'space-box animated fadeIn fast' : 'space-box animated fadeOut fast'">
-
                     <div class="percent-box">
                         <span class="percent-item-span storage-full" :style="{ width: '100%' }" v-if="sizeInfo.total > storageSpace"></span>
                         <span class="percent-item-span storage-res" :style='{ width: getPercent(sizeInfo.res) }'></span>
@@ -80,15 +79,15 @@
                             <span>{{item}}</span>
                         </Checkbox>
                     </CheckboxGroup>
-                    <span :class="selections.length == 0 ? 'action-btn-wrap action-btn-wrap-disabled':'action-btn-wrap'" @click="delFileBatch">
+                    <span :class="selections.length == 0 ? 'action-btn-wrap disable-text-icon':'action-btn-wrap'" @click="delFileBatch">
                         <Icon type="md-trash" class="toggle-btn-icon" />
                         批量删除
                     </span>
-                    <span @click="showUpload" :class="$access.can('admin.*|content-school-upd') ? 'action-btn-wrap' : 'action-btn-wrap-disabled action-btn-wrap'">
+                    <span @click="showUpload" :class="((rangeType == 0 && $access.can('admin.*|content-school-upd')) || rangeType == 1) ? 'action-btn-wrap' : 'disable-text-icon action-btn-wrap'">
                         <Icon type="md-cloud-upload" class="toggle-btn-icon" size="16" />
                         上传资源
                     </span>
-                    <span @click="changeShowType()" :class="activeType === 'image' || activeType === 'video' ? 'action-btn-wrap' : 'action-btn-wrap-disabled action-btn-wrap'">
+                    <span @click="changeShowType()" :class="activeType === 'image' || activeType === 'video' ? 'action-btn-wrap' : 'disable-text-icon action-btn-wrap'">
                         <Icon v-show="showType" type="md-list" class="toggle-btn-icon" size="16" />
                         <Icon v-show="!showType" type="md-grid" class="toggle-btn-icon" />
                         {{showType ? '列表模式':'缩略图模式'}}
@@ -442,6 +441,7 @@
                                     this.fileListShow = this._.cloneDeep(fs)
                                 },
                                 err => {
+									console.log(err)
                                     this.$Message.error('删除失败')
                                 }
                             ).finally(() => {

+ 12 - 12
TEAMModelOS/Controllers/Client/HiTeachController.cs

@@ -74,7 +74,7 @@ namespace TEAMModelOS.Controllers.Client
                 string defaultschool = null;
                 //TODK 取得Teacher 個人相關數據(課程清單、虛擬教室清單、歷史紀錄清單等),學校數據另外API處理,多校切換時不同
                 var client = _azureCosmos.GetCosmosClient();
-                var response = await client.GetContainer("TEAMModelOSTemp", "Teacher").ReadItemStreamAsync(id, new PartitionKey("Base"));
+                var response = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemStreamAsync(id, new PartitionKey("Base"));
                 if (response.Status == 200)
                 {
                     var jsonsc = await JsonDocument.ParseAsync(response.ContentStream);
@@ -116,12 +116,12 @@ namespace TEAMModelOS.Controllers.Client
                     writer.Flush();
                     //Debug
                     //string teacher = Encoding.UTF8.GetString(stream.ToArray());
-                    response = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOSTemp", "Teacher").CreateItemStreamAsync(stream, new PartitionKey("Base"));
+                    response = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").CreateItemStreamAsync(stream, new PartitionKey("Base"));
                 }
 
                 //老師個人課程清單
                 List<object> courses = new List<object>();
-                await foreach (var item in client.GetContainer("TEAMModelOSTemp", "Teacher").GetItemQueryStreamIterator(queryText: $"select c.id, c.name, c.classes, c.notice ,c.scope from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{id}") }))
+                await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryStreamIterator(queryText: $"select c.id, c.name, c.classes, c.notice ,c.scope from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{id}") }))
                 {
                     using var json = await JsonDocument.ParseAsync(item.ContentStream);
                     if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
@@ -167,7 +167,7 @@ namespace TEAMModelOS.Controllers.Client
                 List<object> periods = new List<object>();
                 List<object> grades = new List<object>();
                 List<object> subjects = new List<object>();
-                var responsesch = await client.GetContainer("TEAMModelOSTemp", "School").ReadItemStreamAsync(school_code.ToString(), new PartitionKey($"Base"));
+                var responsesch = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(school_code.ToString(), new PartitionKey($"Base"));
                 if (responsesch.Status == 200)
                 {
                     var jsons = await JsonDocument.ParseAsync(responsesch.ContentStream);
@@ -208,7 +208,7 @@ namespace TEAMModelOS.Controllers.Client
                 //該老師排定的學校課程
                 List<object> courses = new List<object>();
                 var query = $"SELECT c.id, c.name, c.teacher, cc.course, c.scope FROM c JOIN cc IN c.courses JOIN cct IN cc.teachers WHERE cct.id = '{id}'";
-                await foreach (var item in client.GetContainer("TEAMModelOSTemp", "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"CourseManagement-{school_code}") }))
+                await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"CourseManagement-{school_code}") }))
                 {
                     var jsoncm = await JsonDocument.ParseAsync(item.ContentStream);
                     if (jsoncm.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
@@ -351,7 +351,7 @@ namespace TEAMModelOS.Controllers.Client
                 }
 
                 //資料取得
-                await foreach (var item in client.GetContainer("TEAMModelOSTemp", container).GetItemQueryStreamIterator(queryText: $"SELECT c.periodId, c.gradeIds, c.subjectId, c.subjectName, c.name, REPLACE(c.blob, 'index.json', '') AS blob, c.score, c.useCount, ARRAY_LENGTH(c.scoring) AS itemCount, c.createDate From c {queryWhere + queryOption}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{partitionid}") }))
+                await foreach (var item in client.GetContainer("TEAMModelOS", container).GetItemQueryStreamIterator(queryText: $"SELECT c.periodId, c.gradeIds, c.subjectId, c.subjectName, c.name, REPLACE(c.blob, 'index.json', '') AS blob, c.score, c.useCount, ARRAY_LENGTH(c.scoring) AS itemCount, c.createDate From c {queryWhere + queryOption}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{partitionid}") }))
                 {
                     using var json = await JsonDocument.ParseAsync(item.ContentStream);
                     if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
@@ -365,7 +365,7 @@ namespace TEAMModelOS.Controllers.Client
 
                 //總件數
                 int totalCount = 0;
-                await foreach (var item in client.GetContainer("TEAMModelOSTemp", container).GetItemQueryStreamIterator(queryText: $"SELECT VALUE COUNT(1) From c {queryWhere}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{partitionid}") }))
+                await foreach (var item in client.GetContainer("TEAMModelOS", container).GetItemQueryStreamIterator(queryText: $"SELECT VALUE COUNT(1) From c {queryWhere}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{partitionid}") }))
                 {
                     using var json = await JsonDocument.ParseAsync(item.ContentStream);
                     if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
@@ -495,7 +495,7 @@ namespace TEAMModelOS.Controllers.Client
                 }
 
                 //資料取得
-                await foreach (var item in client.GetContainer("TEAMModelOSTemp", container).GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.periodId, c.gradeIds, c.subjectId, c.subjectName, c.blob, c.field, c.level, c.type, c.useCount, c.createDate From c {queryWhere + queryOption}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{partitionid}") }))
+                await foreach (var item in client.GetContainer("TEAMModelOS", container).GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.periodId, c.gradeIds, c.subjectId, c.subjectName, c.blob, c.field, c.level, c.type, c.useCount, c.createDate From c {queryWhere + queryOption}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{partitionid}") }))
                 {
                     using var json = await JsonDocument.ParseAsync(item.ContentStream);
                     if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
@@ -509,7 +509,7 @@ namespace TEAMModelOS.Controllers.Client
 
                 //總件數
                 int totalCount = 0;
-                await foreach (var item in client.GetContainer("TEAMModelOSTemp", container).GetItemQueryStreamIterator(queryText: $"SELECT VALUE COUNT(1) From c {queryWhere}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{partitionid}") }))
+                await foreach (var item in client.GetContainer("TEAMModelOS", container).GetItemQueryStreamIterator(queryText: $"SELECT VALUE COUNT(1) From c {queryWhere}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{partitionid}") }))
                 {
                     using var json = await JsonDocument.ParseAsync(item.ContentStream);
                     if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
@@ -538,7 +538,7 @@ namespace TEAMModelOS.Controllers.Client
             if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
             //知識點
             List<object> points = new List<object>();
-            await foreach (var item in client.GetContainer("TEAMModelOSTemp", "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.subjectId FROM c WHERE c.type = 'point'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Knowledge-{school_code}") }))
+            await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.subjectId FROM c WHERE c.type = 'point'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Knowledge-{school_code}") }))
             {
                 using var json = await JsonDocument.ParseAsync(item.ContentStream);
                 if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
@@ -562,7 +562,7 @@ namespace TEAMModelOS.Controllers.Client
             if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
             //校本課綱
             List<SyllabusRole> syllabus = new List<SyllabusRole>();
-            await foreach (var item in client.GetContainer("TEAMModelOSTemp", "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.period, c.grade, c.semester, c.subject, c.scope, c.resourceCount, c.itemCount, c.children from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{school_code}") }))
+            await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.period, c.grade, c.semester, c.subject, c.scope, c.resourceCount, c.itemCount, c.children from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{school_code}") }))
             {
                 var jsons = await JsonDocument.ParseAsync(item.ContentStream);
                 if (jsons.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
@@ -610,7 +610,7 @@ namespace TEAMModelOS.Controllers.Client
             var query = $"SELECT cs.id, cs.name, cs.no FROM c JOIN cs IN c.students WHERE c.id = '{class_code}' AND c.scope = '{grant_type}'";
             string pk = (grant_type.GetString() == "school") ? $"Class-{school_code}" : $"Class-{id}";
             string container = (grant_type.GetString() == "school") ? "School" : "Teacher";
-            await foreach (var item in client.GetContainer("TEAMModelOSTemp", container).GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(pk) }))
+            await foreach (var item in client.GetContainer("TEAMModelOS", container).GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(pk) }))
             {
                 using var json = await JsonDocument.ParseAsync(item.ContentStream);
                 if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)

+ 1 - 1
TEAMModelOS/Controllers/School/ClassRoomController.cs

@@ -210,7 +210,7 @@ namespace TEAMModelOS.Controllers
             var client = _azureCosmos.GetCosmosClient();
             List<object> classrooms = new List<object>();
             StringBuilder sql = new StringBuilder();
-            sql.Append("select c.id,c.point,c.name,c.teacher,c.periodId,c.gradeId,c.sn,c.style,c.scope,c.type,c.code,c.openType,c.x,c.y from c ");
+            sql.Append("select c.id,c.point,c.name,c.teacher,c.periodId,c.gradeId,c.sn,c.style,c.scope,c.type,c.code,c.openType,c.x,c.y,ARRAY_LENGTH(c.students) AS studCount from c ");
             Dictionary<string, object> dict = new Dictionary<string, object>();
             var emobj = requert.EnumerateObject();
             while (emobj.MoveNext())

+ 2 - 2
TEAMModelOS/Controllers/School/SchoolController.cs

@@ -181,7 +181,7 @@ namespace TEAMModelOS.Controllers
             var client = _azureCosmos.GetCosmosClient();
             //取得學校學制、年級、教室
             List<object> classes = new List<object>();
-            await foreach (var item in client.GetContainer("TEAMModelOSTemp", "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.gradeId FROM c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{school_code}") }))
+            await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.gradeId FROM c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{school_code}") }))
             {
                 var jsonc = await JsonDocument.ParseAsync(item.ContentStream);
                 foreach (var classeinfo in jsonc.RootElement.GetProperty("Documents").EnumerateArray())
@@ -195,7 +195,7 @@ namespace TEAMModelOS.Controllers
             }
             List<object> periods = new List<object>();
             List<object> grades = new List<object>();
-            var responsesch = await client.GetContainer("TEAMModelOSTemp", "School").ReadItemStreamAsync(school_code.ToString(), new PartitionKey($"Base"));
+            var responsesch = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(school_code.ToString(), new PartitionKey($"Base"));
             if (responsesch.Status == 200)
             {
                 var jsons = await JsonDocument.ParseAsync(responsesch.ContentStream);

+ 40 - 40
TEAMModelOS/Controllers/Teacher/InitController.cs

@@ -77,7 +77,7 @@ namespace TEAMModelOS.Controllers
                 string defaultschool = null;
                 //TODK 取得Teacher 個人相關數據(課程清單、虛擬教室清單、歷史紀錄清單等),學校數據另外API處理,多校切換時不同
                 var client = _azureCosmos.GetCosmosClient();
-                var response = await client.GetContainer("TEAMModelOSTemp", "Teacher").ReadItemStreamAsync(id, new PartitionKey("Base"));
+                var response = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemStreamAsync(id, new PartitionKey("Base"));
 
                 //老師個人資料(含初始化)
                 if (response.Status == 200)
@@ -113,12 +113,12 @@ namespace TEAMModelOS.Controllers
                     writer.Flush();
                     //Debug
                     //string teacher = Encoding.UTF8.GetString(stream.ToArray());
-                    response = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOSTemp", "Teacher").CreateItemStreamAsync(stream, new PartitionKey("Base"));
+                    response = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").CreateItemStreamAsync(stream, new PartitionKey("Base"));
                 }
 
                 //私人課程
                 List<object> courses = new List<object>();
-                await foreach (var item in client.GetContainer("TEAMModelOSTemp", "Teacher").GetItemQueryStreamIterator(queryText: $"select c.id, c.name, c.classes, c.notice ,c.scope from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{id}") }))
+                await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryStreamIterator(queryText: $"select c.id, c.name, c.classes, c.notice ,c.scope from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{id}") }))
                 {
                     using var json = await JsonDocument.ParseAsync(item.ContentStream);
                     if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
@@ -132,7 +132,7 @@ namespace TEAMModelOS.Controllers
 
                 //老師個人課綱
                 List<SyllabusRole> syllabus = new List<SyllabusRole>();
-                await foreach (var item in client.GetContainer("TEAMModelOSTemp", "Teacher").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.scope, c.resourceCount, c.itemCount, c.children from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{id}") }))
+                await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.scope, c.resourceCount, c.itemCount, c.children from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{id}") }))
                 {
                     var jsons = await JsonDocument.ParseAsync(item.ContentStream);
                     if (jsons.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
@@ -190,7 +190,7 @@ namespace TEAMModelOS.Controllers
             jwt.Payload.TryGetValue("picture", out object picture);
             List<string> roles = new List<string>();
             List<string> permissions = new List<string>();
-            var response = await client.GetContainer("TEAMModelOSTemp", "School").ReadItemStreamAsync(id, new PartitionKey($"Teacher-{school_code}"));
+            var response = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(id, new PartitionKey($"Teacher-{school_code}"));
             if (response.Status == 200)
             {
                 using var json = await JsonDocument.ParseAsync(response.ContentStream);
@@ -212,7 +212,7 @@ namespace TEAMModelOS.Controllers
 
             //取得學校學制、年級、教室
             List<object> classes = new List<object>();
-            await foreach (var item in client.GetContainer("TEAMModelOSTemp", "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.gradeId FROM c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{school_code}") }))
+            await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.gradeId FROM c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{school_code}") }))
             {
                 var jsonc = await JsonDocument.ParseAsync(item.ContentStream);
                 foreach (var classeinfo in jsonc.RootElement.GetProperty("Documents").EnumerateArray())
@@ -226,7 +226,7 @@ namespace TEAMModelOS.Controllers
             }
             List<object> periods = new List<object>();
             List<object> grades = new List<object>();
-            var responsesch = await client.GetContainer("TEAMModelOSTemp", "School").ReadItemStreamAsync(school_code.ToString(), new PartitionKey($"Base"));
+            var responsesch = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(school_code.ToString(), new PartitionKey($"Base"));
             if (responsesch.Status == 200)
             {
                 var jsons = await JsonDocument.ParseAsync(responsesch.ContentStream);
@@ -256,7 +256,7 @@ namespace TEAMModelOS.Controllers
             //該老師排定的學校課程
             List<object> courses = new List<object>();
             var query = $"SELECT c.id, c.name, c.teacher, cc.course, c.scope FROM c JOIN cc IN c.courses JOIN cct IN cc.teachers WHERE cct.id = '{id}'";
-            await foreach (var item in client.GetContainer("TEAMModelOSTemp", "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"CourseManagement-{school_code}") }))
+            await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"CourseManagement-{school_code}") }))
             {
                 var jsoncm = await JsonDocument.ParseAsync(item.ContentStream);
                 if (jsoncm.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
@@ -293,33 +293,33 @@ namespace TEAMModelOS.Controllers
                 }
             }
 
-            //校本課綱
+            //校本課綱 [式樣未定 先不取]
             List<SyllabusRole> syllabus = new List<SyllabusRole>();
-            await foreach (var item in client.GetContainer("TEAMModelOSTemp", "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.period, c.grade, c.semester, c.subject, c.scope, c.resourceCount, c.itemCount, c.children from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{school_code}") }))
-            {
-                var jsons = await JsonDocument.ParseAsync(item.ContentStream);
-                if (jsons.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
-                {
-                    foreach (var obj in jsons.RootElement.GetProperty("Documents").EnumerateArray())
-                    {
-                        SyllabusRole syllabusRole = new SyllabusRole();
-                        syllabusRole.id = obj.GetProperty("id").ToString();
-                        syllabusRole.name = obj.GetProperty("name").ToString();
-                        syllabusRole.period = obj.GetProperty("period");
-                        syllabusRole.grade = obj.GetProperty("grade");
-                        syllabusRole.semester = obj.GetProperty("semester");
-                        syllabusRole.subject = obj.GetProperty("subject");
-                        syllabusRole.resourceCount = obj.GetProperty("resourceCount").GetUInt16();
-                        syllabusRole.itemCount = obj.GetProperty("itemCount").GetUInt16();
-                        List<Syllabus> syllabusList = obj.GetProperty("children").ToObject<List<Syllabus>>();
-                        syllabusList.Insert(0, new Syllabus { id = syllabusRole.id, name = syllabusRole.name, pid = "", order = 0 });
-                        syllabusList = syllabusList.OrderBy(x => x.order).ToList();
-                        syllabusRole.structure = CreateSyllabusTree(syllabusList);
-                        syllabus.Add(syllabusRole);
-                    }
-                    //[DEBUG] string jsonString = System.Text.Json.JsonSerializer.Serialize(syllabusRoles);
-                }
-            }
+            //await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.period, c.grade, c.semester, c.subject, c.scope, c.resourceCount, c.itemCount, c.children from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{school_code}") }))
+            //{
+            //    var jsons = await JsonDocument.ParseAsync(item.ContentStream);
+            //    if (jsons.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+            //    {
+            //        foreach (var obj in jsons.RootElement.GetProperty("Documents").EnumerateArray())
+            //        {
+            //            SyllabusRole syllabusRole = new SyllabusRole();
+            //            syllabusRole.id = obj.GetProperty("id").ToString();
+            //            syllabusRole.name = obj.GetProperty("name").ToString();
+            //            syllabusRole.period = obj.GetProperty("period");
+            //            syllabusRole.grade = obj.GetProperty("grade");
+            //            syllabusRole.semester = obj.GetProperty("semester");
+            //            syllabusRole.subject = obj.GetProperty("subject");
+            //            syllabusRole.resourceCount = obj.GetProperty("resourceCount").GetUInt16();
+            //            syllabusRole.itemCount = obj.GetProperty("itemCount").GetUInt16();
+            //            List<Syllabus> syllabusList = obj.GetProperty("children").ToObject<List<Syllabus>>();
+            //            syllabusList.Insert(0, new Syllabus { id = syllabusRole.id, name = syllabusRole.name, pid = "", order = 0 });
+            //            syllabusList = syllabusList.OrderBy(x => x.order).ToList();
+            //            syllabusRole.structure = CreateSyllabusTree(syllabusList);
+            //            syllabus.Add(syllabusRole);
+            //        }
+            //        //[DEBUG] string jsonString = System.Text.Json.JsonSerializer.Serialize(syllabusRoles);
+            //    }
+            //}
 
             //取得School Blob 容器位置及SAS
             string school_code_blob = school_code.GetString().ToLower();
@@ -338,7 +338,7 @@ namespace TEAMModelOS.Controllers
             var client = _azureCosmos.GetCosmosClient();
 
             List<object> schools = new List<object>();
-            await foreach (var item in client.GetContainer("TEAMModelOSTemp", "School").GetItemQueryStreamIterator(queryText: $"select c.id, c.name,c.region,c.province,c.city from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
+            await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: $"select c.id, c.name,c.region,c.province,c.city from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
             {
                 using var json = await JsonDocument.ParseAsync(item.ContentStream);
 
@@ -381,7 +381,7 @@ namespace TEAMModelOS.Controllers
 
                 var client = _azureCosmos.GetCosmosClient();
                 //在老師表找出老師,處理該學校狀態 (老師基本資料應該要存在)
-                Teacher teacher = await client.GetContainer("TEAMModelOSTemp", "Teacher").ReadItemAsync<Teacher>(id, new PartitionKey("Base"));
+                Teacher teacher = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<Teacher>(id, new PartitionKey("Base"));
                 if (teacher.schools == null)
                     teacher.schools = new List<Teacher.School>();
                 var school = teacher.schools?.FirstOrDefault(x => x.schoolId.Equals(school_code.GetString(), StringComparison.OrdinalIgnoreCase));
@@ -389,15 +389,15 @@ namespace TEAMModelOS.Controllers
                     school.status = grant_type.GetString();
                 else
                     teacher.schools.Add(new Teacher.School() { schoolId = school_code.GetString(), name = school_name.GetString(), status = grant_type.GetString() });
-                await client.GetContainer("TEAMModelOSTemp", "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
+                await client.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
                 //在學校表處理該學校教師帳號的狀態
-                var sresponse = await client.GetContainer("TEAMModelOSTemp", "School").ReadItemStreamAsync(id, new PartitionKey($"Teacher-{school_code}"));
+                var sresponse = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(id, new PartitionKey($"Teacher-{school_code}"));
                 if (sresponse.Status == 200)
                 {
                     using var json = await JsonDocument.ParseAsync(sresponse.ContentStream);
                     SchoolTeacher steacher = json.ToObject<SchoolTeacher>();
                     steacher.status = grant_type.GetString();
-                    var response = await client.GetContainer("TEAMModelOSTemp", "School").ReplaceItemAsync(steacher, id, new PartitionKey($"Teacher-{school_code}"));
+                    var response = await client.GetContainer("TEAMModelOS", "School").ReplaceItemAsync(steacher, id, new PartitionKey($"Teacher-{school_code}"));
                 }
                 else
                 {
@@ -413,7 +413,7 @@ namespace TEAMModelOS.Controllers
                         roles = new List<string>(new string[] { "teacher" }),
                         status = grant_type.GetString()
                     };
-                    var response = await client.GetContainer("TEAMModelOSTemp", "School").CreateItemAsync(st, new PartitionKey($"Teacher-{school_code}"));
+                    var response = await client.GetContainer("TEAMModelOS", "School").CreateItemAsync(st, new PartitionKey($"Teacher-{school_code}"));
                 }
 
                 return Ok();

+ 1 - 1
TEAMModelOS/Startup.cs

@@ -93,7 +93,7 @@ namespace TEAMModelOS
             });
             services.AddAzureStorage(Configuration.GetValue<string>("Azure:Starage:ConnectionString"));
             services.AddAzureRedis(Configuration.GetValue<string>("Azure:Redis:ConnectionString"));
-            services.AddAzureCosmos(Configuration.GetValue<string>("Azure:Cosmos:ConnectionString"));
+            services.AddAzureCosmos(Configuration.GetSection("Azure:Cosmos").Get<AzureCosmosFactoryOptions>());
             services.AddAzureServiceBus(Configuration.GetValue<string>("Azure:ServiceBus:ConnectionString"));
             services.AddSnowflakeId(Convert.ToInt64(Configuration.GetValue<string>("Option:LocationNum")), 1);
             services.AddHttpClient();

+ 3 - 5
TEAMModelOS/appsettings.Development.json

@@ -23,13 +23,11 @@
     },
     "Blob": {
       "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=teammodelstorage;AccountKey=Yq7D4dE6cFuer2d2UZIccTA/i0c3sJ/6ITc8tNOyW+K5f+/lWw9GCos3Mxhj47PyWQgDL8YbVD63B9XcGtrMxQ==;EndpointSuffix=core.chinacloudapi.cn",
-       "Container": "teammodelos"
+      "Container": "teammodelos"
     },
-    "CosmosDB": {
+    "Cosmos": {
       "ConnectionString": "AccountEndpoint=https://teammodelos.documents.azure.cn:443/;AccountKey=clF73GwPECfP1lKZTCvs8gLMMyCZig1HODFbhDUsarsAURO7TcOjVz6ZFfPqr1HzYrfjCXpMuVD5TlEG5bFGGg==;",
-      //"ConnectionKey": "clF73GwPECfP1lKZTCvs8gLMMyCZig1HODFbhDUsarsAURO7TcOjVz6ZFfPqr1HzYrfjCXpMuVD5TlEG5bFGGg==",
-      "Database": [ "TEAMModelOSTemp" ],
-      "CollectionThroughput": 400,
+      "Database": [ "TEAMModelOS" ],
       "ScanModel": [ "TEAMModelOS" ]
     },
     "Redis": {

+ 1 - 1
TEAMModelOS/appsettings.json

@@ -28,7 +28,7 @@
     },
     "Cosmos": {
       "ConnectionString": "AccountEndpoint=https://teammodelos.documents.azure.cn:443/;AccountKey=clF73GwPECfP1lKZTCvs8gLMMyCZig1HODFbhDUsarsAURO7TcOjVz6ZFfPqr1HzYrfjCXpMuVD5TlEG5bFGGg==;",
-      "Database": [ "TEAMModelOSTemp" ],
+      "Database": [ "TEAMModelOS" ],
       "ScanModel": [ "TEAMModelOS" ]
     },
     "Redis": {