Browse Source

Merge remote-tracking branch 'origin/TPE/develop' into TPE/feat/classroomManage

osbert 4 years ago
parent
commit
c587c14b3e
100 changed files with 3444 additions and 1582 deletions
  1. 16 0
      TEAMModelFunction/AServiceBus.cs
  2. 3 2
      TEAMModelFunction/MonitorCosmosDB.cs
  3. 3 3
      TEAMModelFunction/MonitorServicesBus.cs
  4. 8 0
      TEAMModelFunction/Properties/launchSettings.json
  5. 40 0
      TEAMModelFunction/ServiceBusTopic.cs
  6. 15 2
      TEAMModelFunction/Startup.cs
  7. 7 0
      TEAMModelFunction/TEAMModelFunction.csproj
  8. 11 0
      TEAMModelFunction/local.settings.json
  9. 12 3
      TEAMModelOS.SDK/DI/AzureCosmos/AzureCosmosFactory.cs
  10. 3 3
      TEAMModelOS.SDK/DI/AzureCosmos/AzureCosmosFactoryExtensions.cs
  11. 3 1
      TEAMModelOS.SDK/DI/AzureCosmos/AzureCosmosFactoryOptions.cs
  12. 2 1
      TEAMModelOS/ClientApp/package.json
  13. 2 5
      TEAMModelOS/ClientApp/public/index.html
  14. 3 0
      TEAMModelOS/ClientApp/src/App.vue
  15. 4 4
      TEAMModelOS/ClientApp/src/api/courseMgmt.js
  16. 0 7
      TEAMModelOS/ClientApp/src/api/http.js
  17. 1 10
      TEAMModelOS/ClientApp/src/api/index.js
  18. 6 12
      TEAMModelOS/ClientApp/src/api/learnActivity.js
  19. 1 1
      TEAMModelOS/ClientApp/src/api/login.js
  20. 41 29
      TEAMModelOS/ClientApp/src/api/schoolSetting.js
  21. 1 1
      TEAMModelOS/ClientApp/src/api/stuAccount.js
  22. 5 0
      TEAMModelOS/ClientApp/src/api/uploadFile.js
  23. 115 0
      TEAMModelOS/ClientApp/src/assets/iconfont/demo_index.html
  24. 26 6
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.css
  25. BIN
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.eot
  26. 1 1
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.js
  27. 35 0
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.json
  28. 15 0
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.svg
  29. BIN
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.ttf
  30. BIN
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff
  31. BIN
      TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff2
  32. BIN
      TEAMModelOS/ClientApp/src/assets/image/peal_line.png
  33. 28 23
      TEAMModelOS/ClientApp/src/boot-app.js
  34. 33 4
      TEAMModelOS/ClientApp/src/common/BaseExamList.vue
  35. 11 0
      TEAMModelOS/ClientApp/src/common/BaseLayout.less
  36. 60 35
      TEAMModelOS/ClientApp/src/common/BaseLayout.vue
  37. 5 0
      TEAMModelOS/ClientApp/src/common/BaseNotification.vue
  38. 5 1
      TEAMModelOS/ClientApp/src/common/BaseSelectSchool.vue
  39. 113 34
      TEAMModelOS/ClientApp/src/common/BaseUserPoptip.vue
  40. 55 24
      TEAMModelOS/ClientApp/src/common/NewUploadFile.vue
  41. 0 3
      TEAMModelOS/ClientApp/src/common/UploadFile.vue
  42. 1 4
      TEAMModelOS/ClientApp/src/common/VideoPlayer2.vue
  43. 0 1
      TEAMModelOS/ClientApp/src/components/coursemgmt/StudentList.vue
  44. 0 1
      TEAMModelOS/ClientApp/src/components/learnactivity/BaseVoteForm.vue
  45. 0 1
      TEAMModelOS/ClientApp/src/components/learnactivity/ChooseContent.less
  46. 347 89
      TEAMModelOS/ClientApp/src/components/learnactivity/NewChooseContent.vue
  47. 3 3
      TEAMModelOS/ClientApp/src/components/learnactivity/QuestionList.less
  48. 78 56
      TEAMModelOS/ClientApp/src/components/learnactivity/QuestionList.vue
  49. 0 3
      TEAMModelOS/ClientApp/src/components/learnactivity/ReviewPaperList.less
  50. 3 3
      TEAMModelOS/ClientApp/src/components/learnactivity/SelectLearnUnit.vue
  51. 15 2
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseKnowledgeDetail.vue
  52. 27 25
      TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseMyTable.vue
  53. 3 0
      TEAMModelOS/ClientApp/src/components/student-analysis/total/ExportTables.vue
  54. 248 261
      TEAMModelOS/ClientApp/src/components/student-web/HiteachView/HiteachNoteContent.vue
  55. 7 10
      TEAMModelOS/ClientApp/src/components/syllabus/DragTree.vue
  56. 8 1
      TEAMModelOS/ClientApp/src/css/common-style.less
  57. 1 1
      TEAMModelOS/ClientApp/src/css/dark-iview-form.less
  58. 12 1
      TEAMModelOS/ClientApp/src/icons/svg/hiteach_pro.svg
  59. 12 1
      TEAMModelOS/ClientApp/src/icons/svg/hiteach_std.svg
  60. 12 1
      TEAMModelOS/ClientApp/src/icons/svg/hiteach_tbl.svg
  61. 1 0
      TEAMModelOS/ClientApp/src/icons/svg/htc_pro.svg
  62. 1 0
      TEAMModelOS/ClientApp/src/icons/svg/htc_std.svg
  63. 1 0
      TEAMModelOS/ClientApp/src/icons/svg/htc_tbl.svg
  64. 10 0
      TEAMModelOS/ClientApp/src/icons/svg/tmd.svg
  65. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-CN/login.js
  66. 1 1
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/login.js
  67. 8 8
      TEAMModelOS/ClientApp/src/locale/lang/zh-TW/totalAnalysis.js
  68. 216 0
      TEAMModelOS/ClientApp/src/mock/serviceDriveAuth.js
  69. 8 1
      TEAMModelOS/ClientApp/src/router/routes.js
  70. 1 0
      TEAMModelOS/ClientApp/src/service/User.js
  71. 37 0
      TEAMModelOS/ClientApp/src/static/Global.js
  72. 30 30
      TEAMModelOS/ClientApp/src/static/baseDataDefault.json
  73. 7 2
      TEAMModelOS/ClientApp/src/store/index.js
  74. 4 4
      TEAMModelOS/ClientApp/src/store/module/schoolBaseInfo.js
  75. 101 0
      TEAMModelOS/ClientApp/src/store/module/serviceDriveAuth.js
  76. 0 1
      TEAMModelOS/ClientApp/src/store/module/teachers.js
  77. 18 1
      TEAMModelOS/ClientApp/src/store/module/totalAnalysis.js
  78. 45 5
      TEAMModelOS/ClientApp/src/store/module/user.js
  79. 0 155
      TEAMModelOS/ClientApp/src/utils/UploadTool.js
  80. 383 0
      TEAMModelOS/ClientApp/src/utils/blobTool.js
  81. 9 9
      TEAMModelOS/ClientApp/src/utils/draw.js
  82. 176 0
      TEAMModelOS/ClientApp/src/utils/evTools.js
  83. 29 0
      TEAMModelOS/ClientApp/src/utils/excel.js
  84. 49 3
      TEAMModelOS/ClientApp/src/utils/js-fn.js
  85. 23 19
      TEAMModelOS/ClientApp/src/utils/public.js
  86. 3 10
      TEAMModelOS/ClientApp/src/utils/upload.js
  87. 18 3
      TEAMModelOS/ClientApp/src/view/Home.vue
  88. 5 4
      TEAMModelOS/ClientApp/src/view/classmgt/ManageClass.less
  89. 201 137
      TEAMModelOS/ClientApp/src/view/classmgt/ManageClass.vue
  90. 55 29
      TEAMModelOS/ClientApp/src/view/classrecord/ClassRecord.vue
  91. 146 99
      TEAMModelOS/ClientApp/src/view/evaluation/bank/ExerciseList.vue
  92. 1 1
      TEAMModelOS/ClientApp/src/view/evaluation/bank/TestPaperList.less
  93. 91 97
      TEAMModelOS/ClientApp/src/view/evaluation/bank/TestPaperList.vue
  94. 10 5
      TEAMModelOS/ClientApp/src/view/evaluation/bank/index.vue
  95. 34 43
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseCreateChild.vue
  96. 74 68
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseEditExercise.vue
  97. 156 114
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseExerciseList.vue
  98. 21 49
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseFilter.vue
  99. 4 4
      TEAMModelOS/ClientApp/src/view/evaluation/components/BaseImport.vue
  100. 0 0
      TEAMModelOS/ClientApp/src/view/evaluation/components/BasePoints.vue

+ 16 - 0
TEAMModelFunction/AServiceBus.cs

@@ -0,0 +1,16 @@
+using System;
+using Microsoft.Azure.WebJobs;
+using Microsoft.Azure.WebJobs.Host;
+using Microsoft.Extensions.Logging;
+
+namespace TEAMModelFunction
+{
+    public static class AServiceBus
+    {
+        [FunctionName("AServiceBus")]
+        public static void Run([ServiceBusTrigger("mytopic", "mysubscription", Connection = "")]string mySbMsg, ILogger log)
+        {
+            log.LogInformation($"C# ServiceBus topic trigger function processed message: {mySbMsg}");
+        }
+    }
+}

+ 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; }
     }
 
    

+ 2 - 1
TEAMModelOS/ClientApp/package.json

@@ -13,7 +13,8 @@
 		"last 1 Firefox version"
 	],
 	"dependencies": {
-		"@azure/storage-blob": "^12.2.1",
+		"@azure/storage-blob": "^12.3.0",
+		"@azure/storage-file-datalake": "^12.2.0",
 		"@ckeditor/ckeditor5-build-inline": "^12.3.1",
 		"@lywzx/vue.access.control": "^1.0.10",
 		"@vue/eslint-config-standard": "^4.0.0",

+ 2 - 5
TEAMModelOS/ClientApp/public/index.html

@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="utf-8">
@@ -7,11 +7,8 @@
     <link rel="icon" href="<%= BASE_URL %>favicon.ico">
     <link id="theme" type="text/css" rel="stylesheet" href="<%= BASE_URL %>theme/dark-theme.css">
     <title>vuex-oidc-example</title>
-    <script type="text/javascript" id="MathJax-script" async
+   <script type="text/javascript" id="MathJax-script" async
             src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML">
-        MathJax.Hub.Config({
-            tex2jax: { inlineMath: [['$', '$'], ['\(', '\)']] }
-        });
     </script>
     <script>
         let cloudSetting = localStorage.getItem('cloudSetting')

+ 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)

+ 4 - 4
TEAMModelOS/ClientApp/src/api/courseMgmt.js

@@ -37,10 +37,10 @@ export default {
     deleteTime: function (data) {
         return post('/api/Course/deleteTime', data)
     },
-    //获取教室关联的学生
-    getClassroomStudent: function (data) {
-        return post('/student/class/find', data)
-    },
+    //获取教室关联的学生(移动到班级api管理)
+    //getClassroomStudent: function (data) {
+    //    return post('/student/class/find', data)
+    //},
     //虚拟教室添加学生
     addClassroom: function (data) {
         return post('/api/ClassStudent/upsert', data)

+ 0 - 7
TEAMModelOS/ClientApp/src/api/http.js

@@ -35,9 +35,7 @@ axios.interceptors.request.use(
         if (flag) {
             if (!refreshing) {
                 if (localStorage.getItem('access_token')) {
-                    console.log('等待刷新....')
                     await refreshToken()
-                    console.log('刷新完成....')
                 }
             }
         }
@@ -112,11 +110,9 @@ function checkToken() {
     let eT = Date.parse(localStorage.getItem('expires_in'))
     let oT = 0
     let btw = eT - oT - cT
-    console.log('token剩余时间:' + (btw / 60000))
     if (btw > 10 * 60 * 1000) {
         return false
     } else {
-        console.log('token即将/已经过期...')
         return true
     }
 
@@ -127,7 +123,6 @@ async function refreshToken() {
     refreshing = true
     let srvAdr = localStorage.getItem('srvAdr')
     let url = config.state[srvAdr].coreAPIUrl
-    console.log(srvAdr)
 
     await axios.post(url + '/oauth2/token', {
         "grant_type": "refresh_token",
@@ -137,11 +132,9 @@ async function refreshToken() {
         res => {
             localStorage.setItem("access_token", res.data.access_token)
             localStorage.setItem("expires_in", res.data.expires_in)
-            console.log('刷新中....')
             refreshing = false
         },
         err => {
-            console.log(err)
             refreshing = false
         }
     )

+ 1 - 10
TEAMModelOS/ClientApp/src/api/index.js

@@ -263,16 +263,7 @@ export default {
             }
 
             corePost(url+'/oauth2/token', data).then( res => {
-                if(res.error){
-                    resolve(res)
-                } else {
-                    let t_data = jwtDecode(res.id_token)
-                    if(nonceStr === t_data.nonce){
-                        resolve(res)
-                    } else {
-                        resolve({error: 'nonce'})
-                    }
-                }
+                resolve(res)
             },err => {
                 console.log(err)
                 if(err.status != 200){

+ 6 - 12
TEAMModelOS/ClientApp/src/api/learnActivity.js

@@ -40,25 +40,25 @@ export default {
      *自动组题
      */
     Automatic: function (data) {
-        return post('/api/ItemInfo/Automatic', data)
+        return post('/common/item/Automatic', data)
     },
     /*
      *保存最小单元
      */
     SaveUnit: function (data) {
-        return post('/api/Learn/upsertUnit', data)
+        return post('/teacher/learn/upsert-unit', data)
     },
     /*
      *查询最小单元
      */
     FindUnit: function (data) {
-        return post('/api/Learn/FindUnit', data)
+        return post('/teacher/learn/find-unit', data)
     },
     /*
      *删除最小单元
      */
     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) {
@@ -240,13 +234,13 @@ export default {
 	* 发布自主学习活动
 	*/
     UpsertTask: function (data) {
-        return post('/api/Learn/upsertTask', data)
+        return post('/teacher/learn/upsert-task', data)
     },
     /*
 	* 查询学习活动
 	*/
     findTask: function (data) {
-        return post('/api/Learn/findTask', data)
+        return post('/teacher/learn/find-task', data)
     },
     /*
 	* 删除/撤销学习任务

+ 1 - 1
TEAMModelOS/ClientApp/src/api/login.js

@@ -134,7 +134,7 @@ export default {
 					// 儲存大雲Token
 					localStorage.setItem("auth_token", res.auth_token)
 					store.dispatch('user/setSchoolCode', data.school_code)
-					// store.dispatch('user/setStudentProfile', res) // 暫時註解 要是有再補寫
+					store.dispatch('user/setStudentProfile', res)
 					result = res
 				} else {
 					error = res

+ 41 - 29
TEAMModelOS/ClientApp/src/api/schoolSetting.js

@@ -1,50 +1,62 @@
 import { fetch, post } from '@/api/http'
 export default {
-    schoolSettingSaveOrUpdate: function(data) {
+    schoolSettingSaveOrUpdate: function (data) {
         return post('/school/init/upsert', data)
     },
-    findSchoolSystem: function(data) {
+    findSchoolSystem: function (data) {
         return post('/school/init/find', data)
     },
-    findClassInfo: function(data) {
+    findClassInfo: function (data) {
         return post('/school/classroom/find', data)
     },
-    //更新或新增教室
-    classroomSettingSaveOrUpdate: function(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)
     },
-    dataDefault: function(data) {
+    dataDefault: function (data) {
         return post('/api/Common/DataDefault', data)
     },
     /**
-     * 删除教室
+     * 鍒犻櫎鏁欏�
      * @param {any} data
      */
     delClassroom: function (data) {
         return post('/school/classroom/delete', data)
     },
-	
-	/* 学校管理部分接口 */
-	
-	// 取得老師个人相關數據
-	getTeacherInfo: function(data) {
+
+    // 鍙栧緱鑰佸斧涓�汉鐩搁棞鏁告摎
+    getTeacherInfo: function (data) {
         return post('/teacher/init/get-teacher-info', data)
     },
-	
-	// 取得老師所在學校相關數據
-	getTeacherSchoolInfo: function(data) {
-	    return post('/teacher/init/get-school-info', data)
-	},
-	
-	// 取得數據中心當前所有學校名單
-	getSchoolList: function(data) {
-	    return post('/teacher/init/get-school-list', data)
-	},
-	
-	// 取得數據中心當前所有學校名單
-	joinSchool: function(data) {
-	    return post('/teacher/init/join-school', data)
-	},
-	
-	
+
+    // 取得老師所在學校相關數據
+    getTeacherSchoolInfo: function (data) {
+        return post('/teacher/init/get-school-info', data)
+    },
+
+    // 取得數據中心當前所有學校名單
+    getSchoolList: function (data) {
+        return post('/teacher/init/get-school-list', data)
+    },
+
+    // 取得數據中心當前所有學校名單
+    joinSchool: function (data) {
+        return post('/teacher/init/join-school', data)
+    },
+
+    //获取教室关联的学生
+    getClassroomStudent: function (data) {
+        return post('/school/classroom/find-students', data)
+    },
+
+    //淇濆瓨鍒嗙粍淇℃伅
+    upsertGroup: function (data) {
+        return post('/school/classroom/upsert-group', data)
+    },
 }

+ 1 - 1
TEAMModelOS/ClientApp/src/api/stuAccount.js

@@ -7,7 +7,7 @@ export default {
             "schoolId": schoolId.toLowerCase(),
             "students": []
         }
-        format.students.push(data)
+        format.students.push(...data)
         return post('/api/Student/student-manage', format)
     },
     //��ѯѧ��

+ 5 - 0
TEAMModelOS/ClientApp/src/api/uploadFile.js

@@ -18,5 +18,10 @@ export default {
     //单文件只读权限
     urlSasR: function(data) {
         return post('/api/blob/urlSasR', data)
+    },
+    //获取容器空间
+    getContainerSize: function (data) {
+        return post('/api/blob/get-blobsize', data)
     }
+
 }

+ 115 - 0
TEAMModelOS/ClientApp/src/assets/iconfont/demo_index.html

@@ -30,6 +30,36 @@
       <div class="content unicode" style="display: block;">
           <ul class="icon_lists dib-box">
           
+            <li class="dib">
+              <span class="icon iconfont">&#xe60f;</span>
+                <div class="name">音频</div>
+                <div class="code-name">&amp;#xe60f;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6a2;</span>
+                <div class="name">音频</div>
+                <div class="code-name">&amp;#xe6a2;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe60c;</span>
+                <div class="name">音频</div>
+                <div class="code-name">&amp;#xe60c;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe708;</span>
+                <div class="name">分享</div>
+                <div class="code-name">&amp;#xe708;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe62f;</span>
+                <div class="name">分享</div>
+                <div class="code-name">&amp;#xe62f;</div>
+              </li>
+          
             <li class="dib">
               <span class="icon iconfont">&#xe6b8;</span>
                 <div class="name">购物车</div>
@@ -380,6 +410,51 @@
       <div class="content font-class">
         <ul class="icon_lists dib-box">
           
+          <li class="dib">
+            <span class="icon iconfont icon-audio3"></span>
+            <div class="name">
+              音频
+            </div>
+            <div class="code-name">.icon-audio3
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-audio2"></span>
+            <div class="name">
+              音频
+            </div>
+            <div class="code-name">.icon-audio2
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-audio1"></span>
+            <div class="name">
+              音频
+            </div>
+            <div class="code-name">.icon-audio1
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-share1"></span>
+            <div class="name">
+              分享
+            </div>
+            <div class="code-name">.icon-share1
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont icon-share"></span>
+            <div class="name">
+              分享
+            </div>
+            <div class="code-name">.icon-share
+            </div>
+          </li>
+          
           <li class="dib">
             <span class="icon iconfont icon-sp-car"></span>
             <div class="name">
@@ -859,6 +934,46 @@
       <div class="content symbol">
           <ul class="icon_lists dib-box">
           
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-audio3"></use>
+                </svg>
+                <div class="name">音频</div>
+                <div class="code-name">#icon-audio3</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-audio2"></use>
+                </svg>
+                <div class="name">音频</div>
+                <div class="code-name">#icon-audio2</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-audio1"></use>
+                </svg>
+                <div class="name">音频</div>
+                <div class="code-name">#icon-audio1</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-share1"></use>
+                </svg>
+                <div class="name">分享</div>
+                <div class="code-name">#icon-share1</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#icon-share"></use>
+                </svg>
+                <div class="name">分享</div>
+                <div class="code-name">#icon-share</div>
+            </li>
+          
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
                   <use xlink:href="#icon-sp-car"></use>

File diff suppressed because it is too large
+ 26 - 6
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.css


BIN
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.eot


File diff suppressed because it is too large
+ 1 - 1
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.js


+ 35 - 0
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.json

@@ -5,6 +5,41 @@
   "css_prefix_text": "icon-",
   "description": "",
   "glyphs": [
+    {
+      "icon_id": "16942332",
+      "name": "音频",
+      "font_class": "audio3",
+      "unicode": "e60f",
+      "unicode_decimal": 58895
+    },
+    {
+      "icon_id": "7418684",
+      "name": "音频",
+      "font_class": "audio2",
+      "unicode": "e6a2",
+      "unicode_decimal": 59042
+    },
+    {
+      "icon_id": "8922351",
+      "name": "音频",
+      "font_class": "audio1",
+      "unicode": "e60c",
+      "unicode_decimal": 58892
+    },
+    {
+      "icon_id": "13479234",
+      "name": "分享",
+      "font_class": "share1",
+      "unicode": "e708",
+      "unicode_decimal": 59144
+    },
+    {
+      "icon_id": "3874023",
+      "name": "分享",
+      "font_class": "share",
+      "unicode": "e62f",
+      "unicode_decimal": 58927
+    },
     {
       "icon_id": "998483",
       "name": "购物车",

File diff suppressed because it is too large
+ 15 - 0
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.svg


BIN
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.ttf


BIN
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff


BIN
TEAMModelOS/ClientApp/src/assets/iconfont/iconfont.woff2


BIN
TEAMModelOS/ClientApp/src/assets/image/peal_line.png


+ 28 - 23
TEAMModelOS/ClientApp/src/boot-app.js

@@ -9,6 +9,7 @@ import { FontAwesomeIcon } from './icons'
 import ViewUI from 'view-design'
 import 'view-design/dist/styles/iview.css'
 import tools from '@/utils/public.js'
+import evTools from '@/utils/evTools.js'
 import jsFn from '@/utils/js-fn.js'
 import apiTools from '@/api'
 import mockTools from '@/mock'
@@ -17,6 +18,7 @@ import { fetch, post } from '@/api/http'
 import jwtDecode from 'jwt-decode'
 import FileSaver from "file-saver";
 import JSONPath from 'jsonpath'
+import GLOBAL from '@/static/Global.js'
 import BaseChangeLine from '@/components/student-analysis/total/BaseChangeLine.vue'
 import echarts from 'echarts'
 import vuescroll from 'vuescroll/dist/vuescroll-native'
@@ -40,17 +42,17 @@ import { User } from '@/service/User'
 import { firestorePlugin } from "vuefire";
 Vue.use(firestorePlugin);
 
-var getfinishedtime = function() {
-  var currentdate = new Date();
-  var datetime =
-    "今天 " +
-    currentdate.getHours() +
-    ":" +
-    currentdate.getMinutes() +
-    ":" +
-    currentdate.getSeconds();
-
-  return datetime;
+var getfinishedtime = function () {
+    var currentdate = new Date();
+    var datetime =
+        "今天 " +
+        currentdate.getHours() +
+        ":" +
+        currentdate.getMinutes() +
+        ":" +
+        currentdate.getSeconds();
+
+    return datetime;
 };
 
 Vue.use(konva)
@@ -90,11 +92,13 @@ Vue.prototype.$User = User
 
 // 工具类
 Vue.prototype.$Mock = mockTools
+Vue.prototype.$GLOBAL = GLOBAL
 Vue.prototype.$FileSaver = FileSaver
 Vue.prototype.$jwtDecode = jwtDecode
 Vue.prototype.$JSONPath = JSONPath
 Vue.prototype.$echarts = echarts
 Vue.prototype.$tools = tools
+Vue.prototype.$evTools = evTools
 Vue.prototype.$jsFn = jsFn //常用工具方法
 Vue.prototype.$azblob = window.azblob //azblob API
 Vue.prototype._ = _
@@ -104,7 +108,8 @@ Vue.component('icon', FontAwesomeIcon)
 
 store.dispatch('user/checkSchoolCode');// 設定登入成功的學校簡碼
 store.dispatch('user/checkUserProfile');// 檢查使用者個人詳細資訊
-store.dispatch('user/checkSchoolProfile');// 檢查使用者個人詳細資訊
+store.dispatch('user/checkSchoolProfile');// 檢查使用者在學校的詳細資訊
+store.dispatch('user/checkStudentProfile');// 檢查學生的詳細資訊
 store.dispatch('config/checkSrvAdr');// 檢查現在站的位置
 
 router.beforeEach((to, from, next) => {
@@ -128,19 +133,19 @@ const app = new Vue({
     router,
     i18n,
     Icon,
-	//利用LocalStorage保存設定
-	  created() {
-	    if (localStorage.getItem("local")) {
-	      console.log(localStorage.getItem("local"));
-	      store.commit("setLanguage", localStorage.getItem("local"));
-	    }
-	    if (localStorage.getItem("hintNextItem")) {
-	      store.commit("sethintNextItem", localStorage.getItem("hintNextItem"));
-	    }
-	  },
+    //利用LocalStorage保存設定
+    created() {
+        if (localStorage.getItem("local")) {
+            console.log(localStorage.getItem("local"));
+            store.commit("setLanguage", localStorage.getItem("local"));
+        }
+        if (localStorage.getItem("hintNextItem")) {
+            store.commit("sethintNextItem", localStorage.getItem("hintNextItem"));
+        }
+    },
     ...App
 })
 
 export {
     app
-}
+}

+ 33 - 4
TEAMModelOS/ClientApp/src/common/BaseExamList.vue

@@ -49,13 +49,24 @@
             }
         },
         created() {
-            this.getExamList()
+			if(this.getVuexExamList && this.getVuexExamList.length){
+				this.examList = this.getVuexExamList
+			}else{
+				this.getExamList()
+			}
         },
         methods: {
             // 获取评测列表
             getExamList() {
                 this.$api.totalAnalysis.getExamList({ code: this.$store.state.totalAnalysis.campusCode }).then(res => { // api请求
-                    this.examList = res.result.data
+                    if(res.result.data.length){
+						this.examList = res.result.data
+						this.handleExamClick(0,res.result.data[0])
+					}else{
+						this.examList = []
+						this.$Message.warning('暂无评测数据')
+					}
+					
                 }).catch(err => {
                     console.log(err)
                 })
@@ -69,12 +80,30 @@
 
             // 查看更多评测列表
             goEvaluationList() {
-                this.$router.push('/totalIndex')
+				this.$router.push({
+					name: 'totalIndex',
+					params: {
+						tabName: 'tab2'
+					}
+				})
             }
         },
         mounted() {
             // this.$emit('chooseExam', this.examList[0]);
-        }
+        },
+		computed: {
+			getVuexExamList() {
+			    return this.$store.state.totalAnalysis.examList
+			},
+		},
+		watch:{
+			getVuexExamList(val) {
+				console.log(val)
+			    if(val.length){
+					this.examList = val
+				}
+			}
+		}
     }
 </script>
 

+ 11 - 0
TEAMModelOS/ClientApp/src/common/BaseLayout.less

@@ -175,4 +175,15 @@
     transform-origin: center left;
     transform: scaleX(1);
     transition: transform 0.3s ease-in-out;
+}
+.menu-item-text {
+    display: inline-block;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    font-size: 16px;
+    width: 118px;
+    vertical-align: top;
+    transition: width 0.1s ease 0.1s;
+    font-family: Roboto,-apple-system,BlinkMacSystemFont,Segoe UI,Microsoft JhengHei !important;
 }

+ 60 - 35
TEAMModelOS/ClientApp/src/common/BaseLayout.vue

@@ -25,7 +25,7 @@
                       style="color:var(--primary-text-color)"
                       @click.stop="changeMenuStatus" size="12" />
             </div>
-            <Menu width="auto" :class="menuitemClasses" accordion>
+            <Menu width="auto" :class="menuitemClasses" accordion :active-name="activeName" :open-names="openNames">
                 <vuescroll :ops="ops">
                     <div v-for="(item,index) in menuTree" :key="index">
                         <Submenu :name="index" v-if="$access.ability(item.role,item.permission).validateAll && item.child.length > 0">
@@ -36,20 +36,20 @@
                                 <Icon v-show="!isCollapsed" :custom="item.icon" :class="isCollapsed ? 'collapse-icon-size':''" size="20" />
                                 <span>{{item.name}}</span>
                             </template>
-                            <MenuItem class="sub-item-wrap sub-item-wrap-active" :name="menuItem.router + index" :to="menuItem.router" v-for="(menuItem,i) in item.child" :key="i" v-if="$access.ability(menuItem.role,menuItem.permission).validateAll" @click.native="closeCollapse">
+                            <MenuItem class="sub-item-wrap sub-item-wrap-active" :name="menuItem.router" :to="menuItem.router" v-for="(menuItem,i) in item.child" :key="i" v-if="$access.ability(menuItem.role,menuItem.permission).validateAll">
                                 <Tooltip :content="menuItem.name" placement="right" transfer v-show="isCollapsed">
                                     <Icon class="sub-menu-icon" :custom="menuItem.icon" size="18" />
                                 </Tooltip>
                                 <Icon v-show="!isCollapsed" class="sub-menu-icon" :custom="menuItem.icon" size="18" />
                                 <span>
                                     {{menuItem.name}}
-                                    <label style="color:aqua;margin-left:2px;font-size:12px;">
+                                    <span style="color: aqua;margin-left: 2px;font-size: 12px;vertical-align: text-top;">
                                         {{menuItem.tag}}
-                                    </label>
+                                    </span>
                                 </span>
                             </MenuItem>
                         </Submenu>
-                        <MenuItem :name="index" v-else-if="$access.ability(item.role,item.permission).validateAll" :to="item.router">
+                        <MenuItem :name="item.router" v-else-if="$access.ability(item.role,item.permission).validateAll" :to="item.router">
                             <Tooltip :content="item.name" placement="right" transfer v-show="isCollapsed">
                                 <Icon :custom="item.icon" style="width:55px;text-align:left;" :class="isCollapsed ? 'collapse-icon-size':''" size="22" />
                             </Tooltip>
@@ -70,6 +70,8 @@
     export default {
         data() {
             return {
+                openNames:[],
+                activeName:'',
                 tipsOpt: {
                     offset: [0, 20]
                 },
@@ -97,7 +99,7 @@
                                 icon: 'iconfont icon-class-mgt',
                                 name: '班级管理',
                                 router: '/home/classroom',
-                                tag: '',
+                                tag: '*',
                                 role: 'admin',
                                 permission: 'classroom-upd|classroom-read'
                             },
@@ -105,7 +107,7 @@
                                 icon: 'iconfont icon-student-mgt',
                                 name: '学生账号',
                                 router: '/home/studentAccount',
-                                tag: '*',
+                                tag: '',
                                 role: 'admin',
                                 permission: 'student-upd|student-read'
                             },
@@ -159,7 +161,7 @@
                             },
                             {
                                 icon: 'iconfont icon-question-bank',
-                                name: '校本题目/库',
+                                name: '校本题库',
                                 router: '/home/evaluation/schoolBank',
                                 tag: '*',
                                 role: 'teacher|admin',
@@ -176,8 +178,8 @@
                             {
                                 icon: 'iconfont icon-auth',
                                 name: '授权管理',
-                                router: '/404',
-                                tag: '*',
+                                router: '/home/serviceDriveAuth',
+                                tag: '(预览)',
                                 role: 'admin',
                                 permission: ''
                             }
@@ -252,7 +254,7 @@
                                 icon: 'iconfont icon-class-self',
                                 name: '我的班级',
                                 router: '/home/manageClass',
-                                tag: '*',
+                                tag: '',
                                 role: 'teacher',
                                 permission: 'analysis'
                             },
@@ -293,13 +295,13 @@
                                 icon: 'iconfont icon-file',
                                 name: '个人内容',
                                 router: '/home/personalcontent',
-                                tag: '*',
+                                tag: '',
                                 role: 'teacher|admin',
                                 permission: '',
                             },
                             {
                                 icon: 'iconfont icon-question-bank',
-                                name: '个人题目/库',
+                                name: '个人题库',
                                 router: '/home/evaluation/personalBank',
                                 tag: '*',
                                 role: 'teacher|admin',
@@ -363,24 +365,14 @@
                                 permission: '',
                             }
                         ]
-                    },
-                    //{
-                    //    icon: 'iconfont icon-feedback',
-                    //    name: '问题答疑',
-                    //    router: '/home/feedback',
-                    //    role: 'teacher|admin',
-                    //    permission: 'analysis',
-                    //    child:[]
-                    //}
+                    }
                 ]
             }
         },
         methods: {
-            closeCollapse() {
-                //this.isCollapsed = !this.isCollapsed
-            },
             changeMenuStatus() {
                 this.$refs.side1.toggleCollapse()
+                this.$EventBus.$emit('onCollapseChange', this.isCollapsed)
             }
         },
         computed: {
@@ -391,8 +383,35 @@
                 return ["menu-item", this.isCollapsed ? "collapsed-menu" : ""]
             }
         },
-        created() {
+        create() {
+            let local = localStorage.getItem
+        },
+        watch: {
+            $route: {
+                handler(val, oldval) {
+                    this.openNames = []
+                    let flag = false
+                    for (let i in this.menuTree) {
+                        for (let j in this.menuTree[i].child) {
+                            if (this.menuTree[i].child[j].router == val.path) {
+                                this.activeName = val.path
+                                this.openNames.push(parseInt(i))
+                                flag = true
+                                break
+                            }
+                        }
+                        if (flag) {
+                            break
+                        }
+                    }
+                },
+                // 深度观察监听
+                deep: true,
+                //立即执行
+                immediate: true
+            }
         }
+
     };
 </script>
 <style lang="less" scoped>
@@ -421,16 +440,26 @@
     }
 
 
-    .menu-item span {
-        display: inline-block;
+    .collapsed-menu span {
         overflow: hidden;
         text-overflow: ellipsis;
+        /* width: 118px; */
+    }
+    .collapsed-menu span {
+        width: 0px;
+        float: right;
+        transition: width 0.1s ease;
+    }
+    .menu-item span {
+        display: inline-block;
+        /* overflow: hidden; */
+        /* text-overflow: ellipsis; */
         white-space: nowrap;
         font-size: 16px;
-        width: 118px;
+        /* width: 118px; */
         vertical-align: top;
         transition: width 0.1s ease 0.1s;
-        font-family: Roboto,-apple-system,BlinkMacSystemFont,Segoe UI,Microsoft JhengHei !important;
+        font-family: Roboto, -apple-system, BlinkMacSystemFont, Segoe UI, Microsoft JhengHei !important;
     }
 
     .menu-item i {
@@ -449,11 +478,7 @@
         color: var(--primary-text-color);
     }
 
-    .collapsed-menu span {
-        width: 0px;
-        float: right;
-        transition: width 0.1s ease;
-    }
+    
 
     .collapsed-menu i {
         transform: translateX(5px);

+ 5 - 0
TEAMModelOS/ClientApp/src/common/BaseNotification.vue

@@ -34,6 +34,11 @@
 			cursor: pointer;
 		}
 		
+		.dark-iview-poptip .ivu-poptip-popper{
+			padding: 0;
+			top: 40px !important;
+		}
+		
 		.notice-wrap{
 			display: flex;
 			flex-direction: column;

+ 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;*/
 				}
 			}
 

+ 55 - 24
TEAMModelOS/ClientApp/src/common/NewUploadFile.vue

@@ -17,17 +17,11 @@
 </template>
 <script>
     import '@/icons/svg/loading3.svg';
-    import uploadTools from '@/utils/upload.js';
+    import BlobTool from '@/utils/blobTool.js';
     export default {
         data() {
             return {
                 uploadedList: [],
-                contentTypes: {
-                    'image': ['JPG', 'JPEG', 'PNG', 'GIF'],
-                    'video': ['AVI', 'MP4'],
-                    'doc': ['PPT', 'PPTX', 'DOC', 'DOCX', 'PDF', 'XLS', 'XLSX', 'CSV'],
-                    'teach': ['HTE', 'HETX']
-                },
                 containerClient: null
             }
         },
@@ -41,7 +35,7 @@
             },
             //文件路径
             path: {
-                default: 'teach',
+                default: '',
                 type: String,
                 required: true
             },
@@ -80,8 +74,8 @@
             getFileType(fileName) {
                 let extension = fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length)
                 extension = extension.toUpperCase()
-                for (let key in this.contentTypes) {
-                    if (this.contentTypes[key].indexOf(extension) != -1) {
+                for (let key in this.$GLOBAL.CONTENT_TYPES) {
+                    if (this.$GLOBAL.CONTENT_TYPES[key].indexOf(extension) != -1) {
                         return key
                     }
                 }
@@ -107,19 +101,11 @@
                     })
                     return false
                 }
-                //初始化Blob
-                this.containerClient = uploadTools.initBlob(this.urlString, this.containerName, this.sasString)
-                if (!this.containerClient) {
-                    this.$Message.error({
-                        content: "Blob信息错误!",
-                        duration: 3
-                    })
-                    return false
-                }
+                
                 let fileType = this.getFileType(file.name)
                 //上传文件
                 let fileInfo = {
-                    url: this.urlString + '/ ' + this.containerName + '/ ' + this.path + '/ ' + fileType + '/ ' + file.name,
+                    url: this.urlString + '/ ' + this.containerName + '/ ' + fileType + '/ ' + file.name,
                     name: file.name,
                     size: file.size,
                     loadedBytes: 0,
@@ -129,10 +115,8 @@
                 this.uploadedList.push(fileInfo)
                 let _this = this
                 let index = this.uploadedList.length - 1
-                
-                uploadTools.upload(this.containerClient, file, this.path + '/' + fileType, {
+                this.containerClient.upload(file, fileType, {
                     onProgress: function (e) {
-                        console.log(e.loadedBytes)
                         _this.uploadedList[index].loadedBytes = e.loadedBytes
                         _this.uploadedList[index].progress = parseInt(e.loadedBytes * 100 / file.size)
                     }
@@ -143,6 +127,11 @@
                     },
                     err => {
                         this.uploadedList[index].status = 2
+                        if (err.spaceError) {
+                            this.$Message.error(err.spaceError)
+                        } else {
+                            this.$Message.error("上传失败")
+                        }
                     }
                 )
                 //blob相关操作案列
@@ -189,7 +178,49 @@
             }
             
         },
-        created() {
+        watch: {
+            urlString: {
+                handler(v, o){
+                    if (this.urlString && this.containerName && this.sasString) {
+                        let scope = ''
+                        if (this.containerName == this.$store.state.userInfo.TEAMModelId) {
+                            scope = 'private'
+                        } else if (this.containerName == this.$store.state.userInfo.schoolCode){
+                            scope = 'school'
+                        }
+                        //初始化Blob
+                        this.containerClient = new BlobTool(this.urlString, this.containerName, this.sasString, scope)
+                    }
+                }
+            },
+            containerName: {
+                handler(v, o){
+                    if (this.urlString && this.containerName && this.sasString) {
+                        let scope = ''
+                        if (this.containerName == this.$store.state.userInfo.TEAMModelId) {
+                            scope = 'private'
+                        } else if (this.containerName == this.$store.state.userInfo.schoolCode) {
+                            scope = 'school'
+                        }
+                        //初始化Blob
+                        this.containerClient = new BlobTool(this.urlString, this.containerName, this.sasString, scope)
+                    }
+                }
+            },
+            sasString: {
+                handler(v, o) {
+                    if (this.urlString && this.containerName && this.sasString) {
+                        let scope = ''
+                        if (this.containerName == this.$store.state.userInfo.TEAMModelId) {
+                            scope = 'private'
+                        } else if (this.containerName == this.$store.state.userInfo.schoolCode) {
+                            scope = 'school'
+                        }
+                        //初始化Blob
+                        this.containerClient = new BlobTool(this.urlString, this.containerName, this.sasString, scope)
+                    }
+                }
+            }
         }
     }
 

+ 0 - 3
TEAMModelOS/ClientApp/src/common/UploadFile.vue

@@ -139,7 +139,6 @@
                                 },
                                 err => {
                                     fileInfo['status'] = 2
-                                    console.log(err)
                                 }
                             )
                         }
@@ -168,7 +167,6 @@
                             },
                             err => {
                                 fileInfo['status'] = 2
-                                console.log(err)
                             }
                         )
                     })
@@ -231,7 +229,6 @@
                     },
                     err => {
                         fileItem['status'] = 2
-                        console.log(err)
                     }
                 )
                 return false

+ 1 - 4
TEAMModelOS/ClientApp/src/common/VideoPlayer2.vue

@@ -32,10 +32,7 @@
         mounted() {
             this.player = videojs(
                 this.$refs.videoPlayer,
-                this.options,
-                function onPlayerReady() {
-                    console.log("onPlayerReady");
-                }
+                this.options
             )
             //时间切片
             let _this = this

+ 0 - 1
TEAMModelOS/ClientApp/src/components/coursemgmt/StudentList.vue

@@ -293,7 +293,6 @@
             },
         },
         created() {
-            console.log(this.$store.state)
             this.initData()
             let findCountParams = {
                 "collectionName": "Student",

+ 0 - 1
TEAMModelOS/ClientApp/src/components/learnactivity/BaseVoteForm.vue

@@ -83,7 +83,6 @@
 </template>
 <script>
     import E from '@/utils/wangEditor.js'
-	import uploadFile from '@/utils/upload.js'
     export default {
         props: {
             editItem: {

+ 0 - 1
TEAMModelOS/ClientApp/src/components/learnactivity/ChooseContent.less

@@ -61,7 +61,6 @@
 
   .volume-list-wrap {
     padding-top: 10px;
-    padding-left: 15px;
     width: 300px;
     height: 100%;
     border-right: 1px solid #606060;

+ 347 - 89
TEAMModelOS/ClientApp/src/components/learnactivity/NewChooseContent.vue

@@ -2,7 +2,7 @@
     <div class="choose-content">
         <Tabs type="card" name="chooseContent" @on-click="clickTab">
             <!-- 选择课纲内容 -->
-            <TabPane label="课纲" name="syllabus" v-if="showSyllabus"  tab="chooseContent">
+            <TabPane label="课纲" name="syllabus" v-if="showSyllabus" tab="chooseContent">
                 <div class="tab-wrap">
                     <div class="content-filter-wrap">
                         <div class="content-filter-item">
@@ -66,15 +66,15 @@
                         </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>
                         <Input v-model="keyWord" class="seach-input" suffix="ios-search" placeholder="关键字搜索" clearable style="width: 240px" size="small" @on-change="searchKeyWord" />
                     </div>
                     <div class="file-content-wrap">
-                        <Table ref="fileTable" :columns="fileColumns" :data="fileListShow" max-height="660" class="animated fadeIn" 
-                               @on-select="selectFile" @on-select-cancel="cancelSelectFile"  
+                        <Table ref="fileTable" :columns="fileColumns" :data="fileListShow" max-height="660" class="animated fadeIn"
+                               @on-select="selectFile" @on-select-cancel="cancelSelectFile"
                                @on-select-all="selectAllFile" @on-select-all-cancel="cancelSelectAllFile">
                             <template slot-scope="{ row, index }" slot="size">
                                 <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>
@@ -104,12 +106,92 @@
                     </div>
                 </div>
             </TabPane>
+
+            <!-- 选择题库 -->
+            <TabPane label="题目" name="question" v-if="showQuestion" tab="chooseContent">
+                <div class="tab-wrap">
+                    <vuescroll>
+                        <Row class="question-filter-wrap">
+                            <Col :xs="24" :sm="24" :md="24" :lg="24" :xl="12">
+                            <div class="manual-filter-item">
+                                <span class="manual-filter-label">来源:</span>
+                                <RadioGroup v-model="questionFilter.code" style="display:inline-block;" @on-change="checkAll($event,'code')">
+                                    <Radio :label="$store.state.userInfo.TEAMModelId">个人题库</Radio>
+                                    <Radio :label="$store.state.userInfo.schoolCode">校本题库</Radio>
+                                </RadioGroup>
+                            </div>
+                            </Col>
+                            <Col :xs="24" :sm="24" :md="24" :lg="24" :xl="12">
+                            <div class="manual-filter-item">
+                                <span class="manual-filter-label">学段:</span>
+                                <RadioGroup v-model="questionFilter.periodId" style="display: inline-block;" @on-change="getQSubjectList()">
+                                    <!--<Checkbox label="all">全部</Checkbox>-->
+                                    <Radio :key="index" v-for="(item,index) in $store.state.schoolBaseInfo.schoolBaseInfo.period" :label="item.id">{{item.name}}</Radio>
+                                </RadioGroup>
+                            </div>
+                            </Col>
+                            <Col :xs="24" :sm="24" :md="24" :lg="24" :xl="12">
+                            <div class="manual-filter-item">
+                                <span class="manual-filter-label">学科:</span>
+                                <RadioGroup v-model="questionFilter.subjectId" style="display:inline-block;" @on-change="refreshQuestionList()">
+                                    <Radio class="radio-width" v-for="(item,index) in  subjectList" :label="item.id" :key="index">{{item.name}}</Radio>
+                                </RadioGroup>
+                            </div>
+                            </Col>
+                            <Col :xs="24" :sm="24" :md="24" :lg="24" :xl="12">
+                            <div class="manual-filter-item">
+                                <span class="manual-filter-label">年级:</span>
+                                <CheckboxGroup v-model="questionFilter.gradeIds" style="display:inline-block;" @on-change="refreshQuestionList()">
+                                    <Checkbox class="radio-width" v-for="(item,index) in  $jsFn.getPeriod($store.state.schoolBaseInfo.schoolBaseInfo.period, questionFilter.periodId).grades" :label="item.id" :key="index">{{item.name}}</Checkbox>
+                                </CheckboxGroup>
+                            </div>
+                            </Col>
+                            <Col :xs="24" :sm="24" :md="24" :lg="24" :xl="12">
+                            <div class="manual-filter-item">
+                                <span class="manual-filter-label">题型:</span>
+                                <CheckboxGroup v-model="questionFilter.type" border style="display:inline-block;" @on-change="checkAll($event,'type')">
+                                    <Checkbox label="all">全部</Checkbox>
+                                    <Checkbox label="single">单选</Checkbox>
+                                    <Checkbox label="multiple">多选</Checkbox>
+                                    <Checkbox label="judge">判断</Checkbox>
+                                    <Checkbox label="complete">填空</Checkbox>
+                                    <Checkbox label="subjective">问答</Checkbox>
+                                    <Checkbox label="compose">综合</Checkbox>
+                                </CheckboxGroup>
+                            </div>
+                            </Col>
+                            <Col :xs="24" :sm="24" :md="24" :lg="24" :xl="12">
+                            <div class="manual-filter-item">
+                                <span class="manual-filter-label">难度:</span>
+                                <CheckboxGroup v-model="questionFilter.level" border style="display: inline-block;" @on-change="checkAll($event,'level')">
+                                    <Checkbox label="all">全部</Checkbox>
+                                    <Checkbox v-for="(item,index) in exersicesDiff" :key="index" :label="index + 1">{{ item }}</Checkbox>
+                                </CheckboxGroup>
+                            </div>
+                            </Col>
+
+                        </Row>
+                        <div class="choose-question-wrap">
+                            <Loading :top="100" v-show="isLoading"></Loading>
+                            <QuestionList :questionSize="questionSize" :class="isLoading ? '':'animated fadeIn'" 
+                                          v-show="!isLoading" :config="questionConfig" @on-question-change="selectQuestion" :questions="questionList">
+                            </QuestionList>
+                            <div>
+                                <EmptyData v-if="questionList.length == 0"></EmptyData>
+                            </div>
+                            <!--<div class="page-wrap">
+                                <Page :current.sync="pageNum" :total="totalNum" :page-size="pageSize" size="small" show-total show-sizer @on-change="getCurrentPageData" />
+                            </div>-->
+                        </div>
+                    </vuescroll>
+                </div>
+            </TabPane>
         </Tabs>
     </div>
 </template>
 <script>
     import JSONPath from 'jsonpath'
-    import UploadTool from '@/utils/UploadTool.js'
+    import BlobTool from '@/utils/blobTool.js'
     import QuestionList from '@/components/learnactivity/QuestionList.vue'
     import E from '@/utils/wangEditor.js'
     export default {
@@ -119,7 +201,7 @@
         props: {
             questionSize: {
                 type: Number,
-                default:100
+                default: 100
             },
             showSyllabus: {
                 type: Boolean,
@@ -140,6 +222,23 @@
         },
         data() {
             return {
+                questionList: [],
+                pageNum: 1,
+                totalNum: 0,
+                pageSize: 20,
+                questionConfig: {
+                    showSelect: true
+                },
+                isLoading: false,
+                exersicesDiff: ['容易', '较易', '一般', '较难', '困难'],
+                questionFilter: {
+                    code: '',
+                    type: ['all'],
+                    level: ['all'],
+                    periodId: '',
+                    subjectId: '',
+                    gradeIds:[]
+                },
                 syllabusFilter: {
                     type: 1,
                     periodId: '',
@@ -148,7 +247,7 @@
                     code: this.$store.state.userInfo.schoolCode,
                     TEAMModelId: this.$store.state.userInfo.TEAMModelId
                 },
-                syllabusList:[],
+                syllabusList: [],
                 volumeList: [],
                 subjectList: [],
                 defaultProps: {
@@ -157,13 +256,13 @@
                 },
                 contentFilter: {
                     scope: 'private',
-                    fileType: 'teach'
+                    fileType: 'res'
                 },
-                keyWord:'',
+                keyWord: '',
                 contentTypeList: [
                     {
                         label: '教材',
-                        type: 'teach',
+                        type: 'res',
                     },
                     {
                         label: this.$t('teachContent.filterPicture'),
@@ -173,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'),
@@ -183,12 +286,26 @@
                     }
                 ],
                 fileList: {
-                    school: [],
-                    private: []
+                    school: {
+                        res: [],
+                        image: [],
+                        video: [],
+                        audio: [],
+                        doc: [],
+                        other: []
+                    },
+                    private: {
+                        res: [],
+                        image: [],
+                        video: [],
+                        audio: [],
+                        doc: [],
+                        other:[]
+                    }
                 },
-                selectedFiles:[],
+                selectedFiles: [],
                 fileListShow: [],
-                searchBefore:[],
+                searchBefore: [],
                 fileColumns: [
                     {
                         type: 'selection',
@@ -216,13 +333,138 @@
             }
         },
         methods: {
+            //初始化数据
+            initData() {
+                this.questionFilter.code = this.$store.state.userInfo.TEAMModelId
+                this.questionFilter.periodId = this.$store.state.schoolBaseInfo.schoolBaseInfo.period[0].id
+                this.questionFilter.subjectId = this.$store.state.schoolBaseInfo.schoolBaseInfo.period[0].subjects[0].id
+                this.questionFilter.gradeIds = [this.$store.state.schoolBaseInfo.schoolBaseInfo.period[0].grades[0].id]
+            },
+            /**
+             * 选择全部逻辑
+             * @param data:选中数据
+             * @param field:字段名
+             */
+            checkAll(data, field) {
+                if (this.questionFilter[field].length !== 1 && this.questionFilter[field].indexOf('all') === 0) {
+                    this.questionFilter[field].splice(this.questionFilter[field].indexOf('all'), 1)
+                } else if (this.questionFilter[field].indexOf('all') > 0) {
+                    this.questionFilter[field].length = 0
+                    this.$set(this.questionFilter[field], 0, 'all')
+                }
+                this.pageNum = 1
+                this.getResultCount()
+                this.queryQuestionByPage()
+            },
+            deleteAll(data) {
+                if (data.length == 1) {
+                    if (data[0] == 'all') {
+                        return []
+                    } else {
+                        return data
+                    }
+                } else {
+                    return data
+                }
+            },
+            /**
+             * 获取题库数量
+             */
+            getResultCount() {
+                let findCountParams = {
+                    "collectionName": "ItemInfo",
+                    "queryDict": {
+                        'code': this.questionFilter.code,
+                        'periodId': this.questionFilter.periodId == "" ? [] : [this.questionFilter.periodId],
+                        'level': this.deleteAll(this.questionFilter.level),
+                        'type': this.deleteAll(this.questionFilter.type),
+                        'gradeIds[*]': this.questionFilter.gradeIds,
+                        'subjectId': this.questionFilter.subjectId == "" ? [] : [this.questionFilter.subjectId],
+                        'field': [],
+                    }
+                }
+                this.$api.newEvaluation.FindCount(findCountParams).then(res => {
+                    this.totalNum = res.result.data[0]
+                })
+            },
+            /**
+             * 选择学科、学段后刷新
+             * */
+            refreshQuestionList() {
+                this.pageNum = 1
+                this.getResultCount()
+                this.queryQuestionByPage()
+            },
+            //题库学科筛选条件
+            getQSubjectList() {
+                let result = JSONPath.query(this.$store.state.schoolBaseInfo.schoolBaseInfo.period, "$..[?(@.id=='" + this.questionFilter.periodId + "')]")
+                if (result.length > 0) {
+                    this.subjectList = result[0].subjects
+                    if (this.subjectList.length > 0) {
+                        this.questionFilter.subjectId = this.subjectList[0].id
+                        this.refreshQuestionList()
+                    }
+                } else {
+                    this.subjectList.length = 0
+                }
+            },
+            /**
+             * 分页查询题目
+             */
+            queryQuestionByPage() {
+                let queryData = {
+                    //'@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),
+                    'gradeIds[*]': this.questionFilter.gradeIds,
+                    'subjectId': this.questionFilter.subjectId == "" ? [] : [this.questionFilter.subjectId],
+                }
+                this.isLoading = true
+                this.$api.newEvaluation.FindExerciseList(queryData).then(res => {
+                    
+                    /* 拿到Summary的题目之后要通过每个题目的JSON URL 换取完整题目数据 */
+                    let list = res.items
+                    this.$evTools.getFullItem(list).then(
+                        (res) => {
+                            this.questionList = res
+                        },
+                        (err) => {
+                            this.$Message.error('题库获取失败')
+                        }
+                    )
+                    
+                    
+                }).finally(() => {
+                    setTimeout(() => {
+                        this.isLoading = false
+                    }, 500)
+                })
+            },
+            /**
+             * 查询当前页题目
+             */
+            getCurrentPageData(pageNum) {
+                this.queryQuestionByPage()
+            },
+            selectQuestion(data) {
+                console.log('题目')
+                console.log(data)
+                this.$emit('on-select-question', data)
+            },
             //Tab切换事件
             clickTab(name) {
-                console.log(name)
                 switch (name) {
                     case 'content':
                         this.getFileList()
                         break
+                    case 'question':
+                        this.queryQuestionByPage()
+                        break
                     default:
                         break
                 }
@@ -251,10 +493,10 @@
                         }
                     })
                 }
-                
+
             },
             //处理选择/取消文件事件
-            selectFile(selection,row) {
+            selectFile(selection, row) {
                 this.selectedFiles.push(row)
                 this.$emit('on-file-change', {
                     files: this.selectedFiles
@@ -309,82 +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 UploadTool(sasRes.url, sasRes.name, sasRes.sas)
-                    op1.listBlob({
-                        prefix: 'res'
-                    }).then(
-                        (res) => {
-                            console.log(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) => {
-                            console.log(err)
-                        }
-                    )
-                } else if (this.contentFilter.scope == 'school' && this.fileList[this.contentFilter.scope].length == 0) {
-                    let sasRes = await this.$tools.getSchoolSas()
-                    let op1 = new UploadTool(sasRes.url, sasRes.name, sasRes.sas)
-                    op1.listBlob({
-                        prefix: 'res'
-                    }).then(
-                        (res) => {
-                            console.log(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) => {
-                            console.log(err)
-                        }
-                    )
+                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
@@ -469,6 +724,9 @@
             },
         },
         mounted() {
+            setTimeout(() => {
+                this.initData()
+            },1500)
         },
         created() {
             Date.prototype.toLocaleString = function () {

+ 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;
     }

+ 78 - 56
TEAMModelOS/ClientApp/src/components/learnactivity/QuestionList.vue

@@ -2,32 +2,38 @@
     <div>
         <div :class="index == openIndex ? 'question-item-wrap-detail question-item-wrap':'question-item-wrap'" v-for="(item,index) in questions" :key="index">
             <div class="question-content" @click="toglleQuestionDetail(index)">
-
                 <span class="question-order">{{index+1+'.'}}</span>
                 <span class="question-text" v-html='item.question' ></span>
                 <Icon v-if="config.showSelect" type="ios-cart" title="选题" class="choose-question-btn" size="25" @click.stop="selectQuestion(item)" :color="selectedId.indexOf(item.id) == -1 ? 'white':'aqua'"/>
                 <Icon type="ios-arrow-dropdown" :color="index == openIndex? 'cyan':'white'" size="25" @click.stop="toglleQuestionDetail(index)" :class="index == openIndex ? 'toggle-detail-icon toggle-detail-icon-up':'toggle-detail-icon toggle-detail-icon-down'" :title="openIndex == index ? '收起':'查看详情'" />
             </div>
             <div v-show="index == openIndex" :class="index == openIndex ? 'question-detail animated  fadeIn':'question-detail animated  fadeOut'">
-                <p class="option-item" v-for="(optionItem,index) in item.option" :key="index">
-                    <span class="option-order">{{optionItem.code+'.'}}</span>
-                    <span class="option-text" v-html='optionItem.value'></span>
-                </p>
-                <p class="dark-iview-inputnumber" style="margin-top:15px;" v-if="config.showScore">
-                    <span class="answer-label">配分:</span>
-                    <InputNumber :disabled="config.editable" :max="100" :min="1" v-model="item.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">
-                    <span :key="index" v-for="(answerItem,index) in item.answer" v-html="answerItem"></span>
-                </p>
-                <p class="answer-label">解析:</p>
-                <p class="answer-detail">
-                    <span v-html="item.explain == '' ? '暂无解析' : item.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)" />
-                    <Icon type="ios-link" class="question-control-btn" size="25" title="补救资源" v-if="config.showRemedy" />
+                <div style="min-height:50px">
+                    <Loading :top="20" :width="width" v-show="isLoading"></Loading>
+                    <div v-if="!isLoading">
+                        <p class="option-item" v-for="(optionItem,index) in questionExplain.option" :key="index">
+                            <span class="option-order">{{optionItem.code+'.'}}</span>
+                            <span class="option-text" v-html='optionItem.value'></span>
+                        </p>
+                        <p class="dark-iview-inputnumber" style="margin-top:15px;" v-if="config.showScore">
+                            <span class="answer-label">配分:</span>
+                            <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.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.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)" />
+                            <Icon type="ios-link" class="question-control-btn" size="25" title="补救资源" v-if="config.showRemedy" />
+                        </div>
+                    </div>
+
                 </div>
             </div>
 
@@ -35,6 +41,7 @@
     </div>
 </template>
 <script>
+    import Loading from '@/common/Loading.vue'
     export default {
         props: {
             questions: {
@@ -57,7 +64,7 @@
                         showScore: false,
                         showDelete: false,
                         showRemedy: false,
-                        editable: false
+                        editable: false 
                     }
                 }
             },
@@ -70,52 +77,68 @@
             return {
                 selectedId: [],
                 questionDetail: false,
-                openIndex: -1
+                openIndex: -1,
+                selectedQuestion: [],
+                questionExplain: {},
+                isLoading: false,
+                width:"50"
             }
         },
+        compoents: {
+            Loading
+        },
         methods: {
-            /**
-             * 删除id数组中某个id
-             * @param id
-             */
-            removeId(id) {
-                let index = this.selectedId.indexOf(id)
-                if (index != -1) {
-                    this.selectedId.splice(index,1)
-                }
-            },
             calcScore() {
                 this.$emit('calcScore')
             },
-            deleteQuestion(index) {
-                this.$emit('deleteQuestion', this.questions[index])
-                this.removeId(this.questions[index].id)
-            },
-            selectQuestion(data) {
-                if (this.selectedId.indexOf(data.id) == -1) {
+            //deleteQuestion(index) {
+            //    this.$emit('deleteQuestion', this.questions[index])
+            //    this.removeId(this.questions[index].id)
+            //},
+            /**
+             * 选择或取消题目
+             */
+            async selectQuestion(data) {
+                let index = this.selectedId.indexOf(data.id)
+                if (index == -1) {
                     if (this.selectedId.length >= this.questionSize) {
-                        this.$emit('question-size-warning')
+                        this.$Message.warning('已经超过最多题目数量!')
                     } else {
                         this.selectedId.push(data.id)
-                        this.$emit('selectQuestion', data)
+                        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 {
-                    this.$emit('deleteQuestion', data)
-                    this.removeId(data.id)
+                    this.selectedId.splice(index, 1)
+                    this.selectedQuestion.splice(index, 1)
+                    this.$emit('on-question-change', this.selectedQuestion,0)
                 }
             },
-            groupBy(array, key) {
-                const groups = {}
-                array.forEach(function (item) {
-                    const group = JSON.stringify(item[key])
-                    groups[group] = groups[group] || []
-                    groups[group].push(item)
-                })
-                return Object.keys(groups).map(function (group) {
-                    return groups[group]
-                })
-            },
-            toglleQuestionDetail(index) {
+            //groupBy(array, key) {
+            //    const groups = {}
+            //    array.forEach(function (item) {
+            //        const group = JSON.stringify(item[key])
+            //        groups[group] = groups[group] || []
+            //        groups[group].push(item)
+            //    })
+            //    return Object.keys(groups).map(function (group) {
+            //        return groups[group]
+            //    })
+            //},
+            async toglleQuestionDetail(index) {
+                this.isLoading = true
+                this.questionExplain = {}
+                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)
+                }
                 if (index == this.openIndex) {
                     this.openIndex = -1
                 } else {
@@ -124,11 +147,10 @@
             }
         },
         mounted() {
-			// this.selectedId = this.selQue.map(i => i.id)
         },
         watch: {
             selQue: {
-                handler(o, n){
+                handler(o, n) {
                     this.selectedId = this.selQue.map((item) => {
                         return item.id
                     })

+ 0 - 3
TEAMModelOS/ClientApp/src/components/learnactivity/ReviewPaperList.less

@@ -172,9 +172,6 @@
     padding: 5px 10px;
 }
 
-.content-wrap .exercise-item:hover {
-    box-shadow: 0px 0px 20px 2px rgb(228, 224, 224);
-}
 
 .content-wrap .exercise-item:hover .item-tools-bind {
     display: unset;

+ 3 - 3
TEAMModelOS/ClientApp/src/components/learnactivity/SelectLearnUnit.vue

@@ -136,14 +136,14 @@
                 }
                 this.$api.learnActivity.FindUnit(requestData).then(
                     res => {
-                        if (res.error == null) {
-                            this.unitList = res.result.data
+                        if (!res.error) {
+                            this.unitList = res.units
                         } else {
                             this.$Message.error('API ERROR!')
                         }
                     },
                     err => {
-
+                        this.$Message.error('API ERROR!')
                     }
                 )
             },

+ 15 - 2
TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseKnowledgeDetail.vue

@@ -49,8 +49,9 @@
                     },
                     grid: {
                         show: false, // 是否显示直角坐标系网格
-                        top: 50, // 相对位置 top\bottom\left\right
+                        top: 80, // 相对位置 top\bottom\left\right
 						left:20,
+						width:'80%',
                         height: 480,
                         containLabel: true // gird 区域是否包含坐标轴的刻度标签
                     },
@@ -60,7 +61,7 @@
                         'xAxisIndex': [
                             0
                         ],
-                        bottom: 50,
+                        bottom: 10,
                         'start': 0,
                         'end': 100,
                         handleIcon: 'M512 497.821538m-418.264615 0a418.264615 418.264615 0 1 0 836.52923 0 418.264615 418.264615 0 1 0-836.52923 0Z',
@@ -187,6 +188,8 @@
                     that.$emit('handleItemClick', params)
                     myBar.setOption(option)
                 })
+				
+				
             },
 
             doRender(data) {
@@ -200,6 +203,16 @@
             if (this.getKnowledgeData) {
                 this.doRender(this.getKnowledgeData)
             }
+			
+			this.$EventBus.$on('onCollapseChange',val => {
+				const myBar = this.$echarts.init(document.getElementById(this.echartsId))
+				myBar.setOption({
+				     //其他配置
+				     grid: {
+						 width: val ? '90%' : '80%'
+					 } //你准备的数据
+				})
+			})
         },
 
         computed: {

+ 27 - 25
TEAMModelOS/ClientApp/src/components/student-analysis/total/BaseMyTable.vue

@@ -101,32 +101,26 @@
 			
 			// 导出表格
             exportData (type) {
-                if (type === 1) {
-                    this.$refs.table.exportCsv({
-                        filename: 'The original data'
-                    });
-                } else if (type === 2) {
-                    this.$refs.table.exportCsv({
-                        filename: 'Sorting and filtering data',
-                        original: false
-                    });
-                } else if (type === 3) {
-                    //this.$refs.table.exportCsv({
-                    //    filename: 'Custom data',
-                    //    columns: this.columns,
-                    //    data: this.tableDatas
-                    //});
-
+                if (type === 3) {
                     const params = {
                           title: this.columns.map(i => i.title),
                           key: this.columns.map(i => i.key),
-                          data: this.tableData,
+                          data: this.originData,
                           autoWidth: true,
                           filename: this.tableName
                         }
-
                     excel.export_array_to_excel(params)
-                }
+                } else {
+					// 多个文件 打包下载
+					const params = {
+					      title: this.columns.map(i => i.title),
+					      key: this.columns.map(i => i.key),
+					      data: this.originData,
+					      autoWidth: true,
+					      filename: this.tableName
+					    }
+						this.$store.commit('updateExportParams', params)
+				}
             },      
 			
 			// 排序操作
@@ -193,7 +187,8 @@
             onSelectChange(val) {
                 this.$emit('onSelectChange', val)
             },
-
+			
+			// 渲染班级标题以及相应点击事件
             renderClassName(h, params) {
                 let that = this
                 let row = params.row
@@ -204,7 +199,7 @@
                                 tableRef: 'earlyWarningTable',
                                 tableName:'排名统计'
                             }])
-                            that.$router.push({ name: 'earlyWarning', params: { name: params.row.classId } })
+                            that.$router.push({ path: '/total/achievement/earlyWarning', query: { name: params.row.classId } })
                         }
                     },
                     style: {
@@ -232,7 +227,7 @@
 
                 ])
             },
-
+			
             renderEventIndex(h, params) {
                 let that = this
                 return h('span', {
@@ -402,7 +397,8 @@
             renderCareful(h, params) {
                 let that = this
                 const row = params.row
-                return h('span', row.carefulList.split(',').map(function(item, index) {
+				const list = params.row.carefulList ? params.row.carefulList.split(',') : []
+                return h('span', list.map(function(item, index) {
                     return h('span', {
                         style: {
                             fontSize: '16px',
@@ -417,7 +413,7 @@
                                 that.$router.push({ path: '/total/questionList', query: { QIndex: item } })
                             }
                         }
-                    }, item + (index === row.carefulList.length - 1 ? '' : ' , '))
+                    }, item + (index === list.length - 1 ? '' : ' , '))
                 }))
             },
 
@@ -448,8 +444,14 @@
             },
             exportTableRefs: {
                 handler(n) {
+					// 如果导出的表格包含当前表格 则直接运行下载
                     if (n.length && n.indexOf(this.tableRef) > -1) {
-                        this.exportData(3)
+						// 如果选择下载的表格数量大于1 则进行打包下载
+						if(n.length > 1){
+							this.exportData(4)
+						}else{
+							this.exportData(3)
+						}
                     }
                 },
                 deep:true

+ 3 - 0
TEAMModelOS/ClientApp/src/components/student-analysis/total/ExportTables.vue

@@ -65,7 +65,10 @@
                     checkList.forEach(i => {
                         refList.push(this.tableList.filter(j => j.tableName === i)[0].tableRef)
                     })
+                    this.$store.commit('resetExportParams')
+                    this.$store.commit('updateExcelNums',checkList.length)
                     this.$store.commit('updateExportDisable', refList)
+					// this.$EventBus.$emit('onDownLoading',refList)
 
                 } else {
                     this.$Message.warning('请选择要导出的表格!')

+ 248 - 261
TEAMModelOS/ClientApp/src/components/student-web/HiteachView/HiteachNoteContent.vue

@@ -1,253 +1,253 @@
 <template>
     <div class="hiteachNote-content">
         <br />
-        <EventBasicInfo from="hiteach" />
-        <div class="title-rect-group">
-            <div class="title-rect" />
-            <h2 class="title-rect-name">{{$t('studentWeb.hiteachNote.material')}}</h2>
-        </div>
-        <vuescroll ref="pagewrap">
-            <div :class="moStatus ? 'class-content mouse-over-status':'class-content'" @mousemove="moStatus = true" @mouseleave="moStatus = false">
-                <div class="courseware-wrap">
-                    <DrawHTEX :mapJson="mapJson"></DrawHTEX>
-                    <div class="page-wrap">
-                        <Page :total="120" size="small" :current.sync="curPage" show-elevator show-total @on-change="getCurHTEX">
-                            <span>{{curPage}}/12</span>
-                        </Page>
-                        <Icon type="md-qr-scanner" class="full-screen-icon" @click="viewHtex" />
-                    </div>
-                    <span class="cur-page-tag">{{curPage}}</span>
-                </div>
-                <video-player2 class="video-player-box" :markers="markers"
-                               ref="videoPlayer"
-                               :options="playerOptions"
-                               :playsinline="true"
-                               @getCurPage="getCurPage">
-                </video-player2>
-            </div>
+            <EventBasicInfo from="hiteach" />
             <div class="title-rect-group">
                 <div class="title-rect" />
-                <h2 class="title-rect-name">
-                    {{$t('studentWeb.hiteachNote.classInteractionRecord')}}
-                    <span class="feedbackNum">10</span>
-                </h2>
+                <h2 class="title-rect-name">{{$t('studentWeb.hiteachNote.material')}}</h2>
             </div>
+            <div ref="pagewrap">
 
 
-            <div class="dec">
-                <ul class="message-filter">
-                    <li @click="showdoc()">
-                        <Icon :class="{ 'select-filter': onlyShowdoc == true }" type="md-document" title="文件" />
-                    </li>
-                    <li @click="showImg()">
-                        <Icon :class="{ 'select-filter': onlyShowImg == true }" type="ios-image" title="图片" />
-                    </li>
-                    <li @click="showShareLink()">
-                        <Icon :class="{ 'select-filter': onlyShowShareLink == true }" type="ios-link" title="超链接" />
-                    </li>
-                    <li @click="showQa()">
-                        <span :class="{ 'select-filter': onlyShowQA == true}" style="font-size:32px;line-height:30px;" title="即问即答">Q</span>
-                    </li>
-                </ul>
-                <Row :gutter="0">
-                    <i-col :xs="24" :sm="24" :md="24" :lg="24">
-                        <vuescroll ref="datawrap">
-                            <div class="message-area">
-                                <div v-if="openImageViewer == true" class="image-viewer">
-                                    <Icon type="md-close" class="close-icon" @click="closeViewer()" />
+                <div :class="moStatus ? 'class-content mouse-over-status':'class-content'" @mousemove="moStatus = true" @mouseleave="moStatus = false">
+                    <div class="courseware-wrap">
+                        <DrawHTEX :mapJson="mapJson"></DrawHTEX>
+                        <div class="page-wrap">
+                            <Page :total="120" size="small" :current.sync="curPage" show-elevator show-total @on-change="getCurHTEX">
+                                <span>{{curPage}}/12</span>
+                            </Page>
+                            <Icon type="md-qr-scanner" class="full-screen-icon" @click="viewHtex" />
+                        </div>
+                        <span class="cur-page-tag">{{curPage}}</span>
+                    </div>
+                    <video-player2 class="video-player-box" :markers="markers"
+                                   ref="videoPlayer"
+                                   :options="playerOptions"
+                                   :playsinline="true"
+                                   @getCurPage="getCurPage">
+                    </video-player2>
+                </div>
+                <div class="title-rect-group">
+                    <div class="title-rect" />
+                    <h2 class="title-rect-name">
+                        {{$t('studentWeb.hiteachNote.classInteractionRecord')}}
+                        <span class="feedbackNum">10</span>
+                    </h2>
+                </div>
+                <div class="dec">
+                    <ul class="message-filter">
+                        <li @click="showdoc()">
+                            <Icon :class="{ 'select-filter': onlyShowdoc == true }" type="md-document" title="文件" />
+                        </li>
+                        <li @click="showImg()">
+                            <Icon :class="{ 'select-filter': onlyShowImg == true }" type="ios-image" title="图片" />
+                        </li>
+                        <li @click="showShareLink()">
+                            <Icon :class="{ 'select-filter': onlyShowShareLink == true }" type="ios-link" title="超链接" />
+                        </li>
+                        <li @click="showQa()">
+                            <span :class="{ 'select-filter': onlyShowQA == true}" style="font-size:32px;line-height:30px;" title="即问即答">Q</span>
+                        </li>
+                    </ul>
+                    <Row :gutter="0">
+                        <i-col :xs="24" :sm="24" :md="24" :lg="24">
+                            <vuescroll ref="datawrap">
+                                <div class="message-area">
+                                    <div v-if="openImageViewer == true" class="image-viewer">
+                                        <Icon type="md-close" class="close-icon" @click="closeViewer()" />
 
-                                    <img :src="`https://source.unsplash.com/random/?water${item}`" />
-                                </div>
-                                <div class="message-item" v-if="isSelected('Qa')|| showAll()">
-                                    <img class="message-avatar"
-                                         src="https://images.unsplash.com/photo-1520223297779-95bbd1ea79b7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=60" />
+                                        <img :src="`https://source.unsplash.com/random/?water${item}`" />
+                                    </div>
+                                    <div class="message-item" v-if="isSelected('Qa')|| showAll()">
+                                        <img class="message-avatar"
+                                             src="https://images.unsplash.com/photo-1520223297779-95bbd1ea79b7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=60" />
 
-                                    <div class="message-content">
-                                        <div class="messagetoPPT-tag" @click="toVideo(3,$event)">PDF第3頁</div>
-                                        <div class="triangle"></div>
-                                        <p class="user-name">Nancy Chen</p>
-                                        <p class="message-text">
-                                            即問即答(多選):
-                                            <br />
-                                            <span class="answer-text wrong-ans">
-                                                ABC
-                                                <div class="divide-line" />
-                                            </span>
-                                            參考答案:
-                                            <br />
-                                            <span class="answer-text">BD</span>
-                                        </p>
+                                        <div class="message-content">
+                                            <div class="messagetoPPT-tag" @click="toVideo(3,$event)">PDF第3頁</div>
+                                            <div class="triangle"></div>
+                                            <p class="user-name">Nancy Chen</p>
+                                            <p class="message-text">
+                                                即問即答(多選):
+                                                <br />
+                                                <span class="answer-text wrong-ans">
+                                                    ABC
+                                                    <div class="divide-line" />
+                                                </span>
+                                                參考答案:
+                                                <br />
+                                                <span class="answer-text">BD</span>
+                                            </p>
 
-                                        <p class="message-time">00:20:58</p>
+                                            <p class="message-time">00:20:58</p>
+                                        </div>
                                     </div>
-                                </div>
-                                <div class="teacher-item" v-if="isSelected('Nomal')|| showAll()">
-                                    <div class="message-content">
-                                        <div class="triangle"></div>
-                                        <p class="user-name">Mary Wu (Teacher)</p>
-                                        <p class="message-text">詳見課本Page31</p>
-                                        <p class="message-time">00:20:58</p>
+                                    <div class="teacher-item" v-if="isSelected('Nomal')|| showAll()">
+                                        <div class="message-content">
+                                            <div class="triangle"></div>
+                                            <p class="user-name">Mary Wu (Teacher)</p>
+                                            <p class="message-text">詳見課本Page31</p>
+                                            <p class="message-time">00:20:58</p>
+                                        </div>
+                                        <img class="message-avatar"
+                                             src="https://images.unsplash.com/photo-1578635374554-b07c9b1619b0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" />
                                     </div>
-                                    <img class="message-avatar"
-                                         src="https://images.unsplash.com/photo-1578635374554-b07c9b1619b0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" />
-                                </div>
-                                <div class="message-item" v-if="isSelected('Qa')|| showAll()">
-                                    <img class="message-avatar"
-                                         src="https://images.unsplash.com/photo-1520223297779-95bbd1ea79b7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=60" />
-                                    <div class="message-content">
-                                        <div class="messagetoPPT-tag" @click="toVideo(8,$event)">PDF第8頁</div>
-                                        <div class="triangle"></div>
-                                        <p class="user-name">Nancy Chen</p>
-                                        <p class="message-text">
-                                            即問即答(多選):
-                                            <br />
-                                            <span class="answer-text">
-                                                BCD<div class="divide-line" />
-                                                ✔︎ 答案正確!
+                                    <div class="message-item" v-if="isSelected('Qa')|| showAll()">
+                                        <img class="message-avatar"
+                                             src="https://images.unsplash.com/photo-1520223297779-95bbd1ea79b7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=60" />
+                                        <div class="message-content">
+                                            <div class="messagetoPPT-tag" @click="toVideo(8,$event)">PDF第8頁</div>
+                                            <div class="triangle"></div>
+                                            <p class="user-name">Nancy Chen</p>
+                                            <p class="message-text">
+                                                即問即答(多選):
                                                 <br />
-                                            </span>
-                                        </p>
-                                        <p class="message-time">00:20:58</p>
+                                                <span class="answer-text">
+                                                    BCD<div class="divide-line" />
+                                                    ✔︎ 答案正確!
+                                                    <br />
+                                                </span>
+                                            </p>
+                                            <p class="message-time">00:20:58</p>
+                                        </div>
                                     </div>
-                                </div>
-                                <div class="teacher-item" v-if="isSelected('doc')|| showAll()">
-                                    <div class="message-content">
-                                        <div class="triangle"></div>
-                                        <p class="user-name">Mary Wu (Teacher)</p>
-                                        <p class="message-text">
-                                            <span class="message-link">
-                                                <svg-icon class="link-icon" icon-class="doc2" />teacher_shared.pdf
-                                            </span>
-                                        </p>
-                                        <p class="message-time">00:20:58</p>
+                                    <div class="teacher-item" v-if="isSelected('doc')|| showAll()">
+                                        <div class="message-content">
+                                            <div class="triangle"></div>
+                                            <p class="user-name">Mary Wu (Teacher)</p>
+                                            <p class="message-text">
+                                                <span class="message-link">
+                                                    <svg-icon class="link-icon" icon-class="doc2" />teacher_shared.pdf
+                                                </span>
+                                            </p>
+                                            <p class="message-time">00:20:58</p>
+                                        </div>
+                                        <img class="message-avatar"
+                                             src="https://images.unsplash.com/photo-1578635374554-b07c9b1619b0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" />
                                     </div>
-                                    <img class="message-avatar"
-                                         src="https://images.unsplash.com/photo-1578635374554-b07c9b1619b0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" />
-                                </div>
-                                <div class="message-item" v-if="isSelected('Img')||isSelected('Qa')|| showAll()">
-                                    <img class="message-avatar"
-                                         src="https://images.unsplash.com/photo-1520223297779-95bbd1ea79b7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=60" />
+                                    <div class="message-item" v-if="isSelected('Img')||isSelected('Qa')|| showAll()">
+                                        <img class="message-avatar"
+                                             src="https://images.unsplash.com/photo-1520223297779-95bbd1ea79b7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=60" />
 
-                                    <div class="message-content">
-                                        <div class="messagetoPPT-tag" @click="toVideo(11,$event)">PDF第11頁</div>
-                                        <div class="triangle"></div>
-                                        <p class="user-name">Nancy Chen</p>
-                                        <p class="message-text">
-                                            即問即答:
-                                            <br />
-                                            <img @click="openViewer(item)"
-                                                 src="https://source.unsplash.com/random/320x200/?water" />
-                                            <span class='answer-text'>
-                                                <div class="divide-line" />
-                                                ✔︎ 答案正確!
-                                            </span>
-                                        </p>
-                                        <p class="message-time">00:20:58</p>
+                                        <div class="message-content">
+                                            <div class="messagetoPPT-tag" @click="toVideo(11,$event)">PDF第11頁</div>
+                                            <div class="triangle"></div>
+                                            <p class="user-name">Nancy Chen</p>
+                                            <p class="message-text">
+                                                即問即答:
+                                                <br />
+                                                <img @click="openViewer(item)"
+                                                     src="https://source.unsplash.com/random/320x200/?water" />
+                                                <span class='answer-text'>
+                                                    <div class="divide-line" />
+                                                    ✔︎ 答案正確!
+                                                </span>
+                                            </p>
+                                            <p class="message-time">00:20:58</p>
+                                        </div>
                                     </div>
-                                </div>
-                                <div class="teacher-item" v-if="isSelected('Link')|| showAll()">
-                                    <div class="message-content">
-                                        <div class="triangle"></div>
-                                        <p class="user-name">Mary Wu (Teacher)</p>
-                                        <div class="message-text">
-                                            <a class="message-link"
-                                               href="https://www.habook.com/zh-tw"
-                                               target="_blank">https://www.habook.com/zh-tw</a>
+                                    <div class="teacher-item" v-if="isSelected('Link')|| showAll()">
+                                        <div class="message-content">
+                                            <div class="triangle"></div>
+                                            <p class="user-name">Mary Wu (Teacher)</p>
+                                            <div class="message-text">
+                                                <a class="message-link"
+                                                   href="https://www.habook.com/zh-tw"
+                                                   target="_blank">https://www.habook.com/zh-tw</a>
 
-                                            <div class="link-item">
-                                                <ul class="link-content">
-                                                    <li>
-                                                        <img class="link-img" :src="`https://source.unsplash.com/random/300x200/?water${item}`" />
-                                                    </li>
-                                                    <li class="link-text">
-                                                        <a href="https://www.habook.com/zh-tw" target="_blank">
-                                                            <p class="title">分享連結 {{ item }}</p>
-                                                            <p>https://www.habook.com/zh-tw</p>
-                                                        </a>
-                                                    </li>
-                                                </ul>
+                                                <div class="link-item">
+                                                    <ul class="link-content">
+                                                        <li>
+                                                            <img class="link-img" :src="`https://source.unsplash.com/random/300x200/?water${item}`" />
+                                                        </li>
+                                                        <li class="link-text">
+                                                            <a href="https://www.habook.com/zh-tw" target="_blank">
+                                                                <p class="title">分享連結 {{ item }}</p>
+                                                                <p>https://www.habook.com/zh-tw</p>
+                                                            </a>
+                                                        </li>
+                                                    </ul>
+                                                </div>
                                             </div>
-                                        </div>
 
-                                        <p class="message-time">00:20:58</p>
+                                            <p class="message-time">00:20:58</p>
+                                        </div>
+                                        <img class="message-avatar"
+                                             src="https://images.unsplash.com/photo-1578635374554-b07c9b1619b0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" />
                                     </div>
-                                    <img class="message-avatar"
-                                         src="https://images.unsplash.com/photo-1578635374554-b07c9b1619b0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" />
-                                </div>
-                                <div class="message-item" v-if="isSelected('Qa')|| showAll()">
-                                    <img class="message-avatar"
-                                         src="https://images.unsplash.com/photo-1520223297779-95bbd1ea79b7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=60" />
+                                    <div class="message-item" v-if="isSelected('Qa')|| showAll()">
+                                        <img class="message-avatar"
+                                             src="https://images.unsplash.com/photo-1520223297779-95bbd1ea79b7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=60" />
 
-                                    <div class="message-content">
-                                        <div class="triangle"></div>
-                                        <p class="user-name">Nancy Chen</p>
-                                        <p class="message-text">
-                                            即問即答(多選):
-                                            <br />
-                                            <span class="answer-text">
-                                                AD
-                                                <div class="divide-line" />
-                                                ✔︎ 答案正確!
-                                            </span>
-                                        </p>
-                                        <p class="message-time">00:20:58</p>
-                                    </div>
-                                </div>
-                                <div class="teacher-item" v-if="isSelected('Img')|| showAll()">
-                                    <div class="message-content">
-                                        <div class="triangle"></div>
-                                        <p class="user-name">Mary Wu (Teacher)</p>
-                                        <p class="message-text">
-                                            教師從Hiteach推送了一張圖:
-                                            <br />
-                                            <img @click="openViewer(item)"
-                                                 src="https://source.unsplash.com/random/320x200/?line" />
-                                        </p>
-                                        <p class="message-time">00:20:58</p>
+                                        <div class="message-content">
+                                            <div class="triangle"></div>
+                                            <p class="user-name">Nancy Chen</p>
+                                            <p class="message-text">
+                                                即問即答(多選):
+                                                <br />
+                                                <span class="answer-text">
+                                                    AD
+                                                    <div class="divide-line" />
+                                                    ✔︎ 答案正確!
+                                                </span>
+                                            </p>
+                                            <p class="message-time">00:20:58</p>
+                                        </div>
                                     </div>
-                                    <img class="message-avatar"
-                                         src="https://images.unsplash.com/photo-1578635374554-b07c9b1619b0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" />
-                                </div>
-                                <div class="message-item group-item" v-if="isSelected('Qa')|| showAll()">
-                                    <div class="group-avatar">
-                                        <svg-icon icon-class="group" class="group-icon" />
+                                    <div class="teacher-item" v-if="isSelected('Img')|| showAll()">
+                                        <div class="message-content">
+                                            <div class="triangle"></div>
+                                            <p class="user-name">Mary Wu (Teacher)</p>
+                                            <p class="message-text">
+                                                教師從Hiteach推送了一張圖:
+                                                <br />
+                                                <img @click="openViewer(item)"
+                                                     src="https://source.unsplash.com/random/320x200/?line" />
+                                            </p>
+                                            <p class="message-time">00:20:58</p>
+                                        </div>
+                                        <img class="message-avatar"
+                                             src="https://images.unsplash.com/photo-1578635374554-b07c9b1619b0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60" />
                                     </div>
+                                    <div class="message-item group-item" v-if="isSelected('Qa')|| showAll()">
+                                        <div class="group-avatar">
+                                            <svg-icon icon-class="group" class="group-icon" />
+                                        </div>
 
-                                    <div class="message-content">
-                                        <div class="triangle"></div>
-                                        <p class="user-name">Group 5</p>
-                                        <p class="message-text">
-                                            分組即問即答(多選):
-                                            <br />
-                                            <span class="answer-text">
-                                                ADE
-                                                <div class="divide-line" />
-                                                ✔︎ 答案正確!
-                                            </span>
-                                        </p>
-                                        <p class="message-time">00:20:58</p>
+                                        <div class="message-content">
+                                            <div class="triangle"></div>
+                                            <p class="user-name">Group 5</p>
+                                            <p class="message-text">
+                                                分組即問即答(多選):
+                                                <br />
+                                                <span class="answer-text">
+                                                    ADE
+                                                    <div class="divide-line" />
+                                                    ✔︎ 答案正確!
+                                                </span>
+                                            </p>
+                                            <p class="message-time">00:20:58</p>
+                                        </div>
                                     </div>
-                                </div>
-                                <div class="message-item" v-if="isSelected('Nomal')|| showAll()">
-                                    <img class="message-avatar"
-                                         src="https://images.unsplash.com/photo-1520223297779-95bbd1ea79b7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=60" />
+                                    <div class="message-item" v-if="isSelected('Nomal')|| showAll()">
+                                        <img class="message-avatar"
+                                             src="https://images.unsplash.com/photo-1520223297779-95bbd1ea79b7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=600&q=60" />
 
-                                    <div class="message-content">
-                                        <div class="triangle"></div>
-                                        <p class="user-name">Nancy Chen</p>
-                                        <p class="message-text">
-                                            問題討論第28題答案有誤?
-                                            <br />
-                                        </p>
-                                        <p class="message-time">00:20:58</p>
+                                        <div class="message-content">
+                                            <div class="triangle"></div>
+                                            <p class="user-name">Nancy Chen</p>
+                                            <p class="message-text">
+                                                問題討論第28題答案有誤?
+                                                <br />
+                                            </p>
+                                            <p class="message-time">00:20:58</p>
+                                        </div>
                                     </div>
                                 </div>
-                            </div>
-                        </vuescroll>
-                    </i-col>
-                </Row>
+                            </vuescroll>
+                        </i-col>
+                    </Row>
+                </div>
             </div>
-        </vuescroll>
             <div v-if="openHtexViewer" class="image-viewer htex-viewer">
                 <Icon type="md-close" class="close-icon" @click="openHtexViewer = false" />
                 <DrawHTEX :mapJson="mapJson" class="animated fadeIn" :height="800" @click.stop></DrawHTEX>
@@ -257,7 +257,8 @@
                     </Page>
                 </div>
             </div>
-    </div>
+</div>
+
 </template>
 
 <script>
@@ -315,7 +316,6 @@
                     notSupportedMessage: '此视频暂无法播放,请稍后再试' //允许覆盖Video.js无法播放媒体源时显示的默认信息。
                 },
                 //新增数据
-
                 currentfilterType: "",
                 onlyShowdoc: false,
                 onlyShowImg: false,
@@ -325,17 +325,7 @@
                 onlyShowQAWrong: false,
                 openImageViewer: false,
                 pageNow: 0,
-                item: "",
-                videoOptions: {
-                    poster: "https://source.unsplash.com/random/?water", //影片的封面
-                    preload: "auto",
-                    height: "400px",
-                    autoplay: false,
-                    controls: true,
-                    sources: [
-                        { src: "http://vjs.zencdn.net/v/oceans.mp4", type: "video/mp4" },
-                    ],
-                },
+                item: ""
             };
         },
         watch: {},
@@ -352,30 +342,31 @@
         methods: {
             toVideo(page, e) {
                 //页面滚动
-                let dataLoacation = this.$refs["datawrap"].getPosition()
-                let pageLocaltion = this.$refs["pagewrap"].getPosition()
-                let y = e.pageY - 665 + pageLocaltion.scrollTop + dataLoacation.scrollTop
-                this.$nextTick(() => {
-                    this.$refs["pagewrap"].scrollTo(
-                        {
-                            x: 0,
-                            y: 0
-                        }
-                    )
-                    this.$refs["datawrap"].scrollTo(
-                        {
-                            x: 0,
-                            y: y
-                        }
-                    )
-                })
-                //视频时间定位
-                let pageInfo = this.markers.filter(item => {
-                    return item.page == page
-                })
-                this.$refs.videoPlayer.player.currentTime(pageInfo[0].time)
-                //课件页面定位
-                this.mapJson = require('./data/' + page + '.json')
+                console.log(this.$refs)
+                //let dataLoacation = this.$refs["datawrap"].getPosition()
+                //let pageLocaltion = this.$refs["pagewrap"].getPosition()
+                //let y = e.pageY - 665 + pageLocaltion.scrollTop + dataLoacation.scrollTop
+                //this.$nextTick(() => {
+                //    this.$refs["pagewrap"].scrollTo(
+                //        {
+                //            x: 0,
+                //            y: 0
+                //        }
+                //    )
+                //    this.$refs["datawrap"].scrollTo(
+                //        {
+                //            x: 0,
+                //            y: y
+                //        }
+                //    )
+                //})
+                ////视频时间定位
+                //let pageInfo = this.markers.filter(item => {
+                //    return item.page == page
+                //})
+                //this.$refs.videoPlayer.player.currentTime(pageInfo[0].time)
+                ////课件页面定位
+                //this.mapJson = require('./data/' + page + '.json')
             },
             //点击视频切片
             getCurPage(page) {
@@ -409,8 +400,7 @@
              * 获取blob授权信息
              * */
             async getSasStr() {
-                console.log('231231321321231321')
-                console.log(this.$tools.getSchoolSas())
+                console.log(this.$store.state)
                 let sasRes1 = await this.$tools.getSchoolSas()
                 let sasRes2 = await this.$tools.getPrivateSas()
                 console.log(sasRes1, sasRes2)
@@ -426,10 +416,7 @@
                     this.$Message.error('获取Blob授权失败')
                 }
             },
-
             //新增功能
-
-
             isSelected(type) {
                 if (type == this.currentfilterType) {
                     //console.log(type);

+ 7 - 10
TEAMModelOS/ClientApp/src/components/syllabus/DragTree.vue

@@ -41,12 +41,8 @@
         <!-- 关联内容弹窗 -->
         <Modal v-model="isRelatedContent" width="880" footer-hide class="tree-modal related-modal">
             <div class="modal-header" slot="header">内容关联 ( 当前节点:{{ currentEditData ? currentEditData.title : '' }} )</div>
-            <!-- <ChooseContent :showSyllabus="isFalse"
-                           :showOther="isFalse"
-                           @on-select-question="onSelectQuestion"
-                           @on-cancel-question="onSelectQuestion"
-                           @on-select-file="onSelectFile"
-                           @on-cancel-file="onSelectFile"></ChooseContent> -->
+            <ChooseContent 
+                           @on-file-change="onSelectFile"></ChooseContent>
 
             <Button class="modal-btn" @click="onSaveNode" :loading="isLoading">确认</Button>
         </Modal>
@@ -106,7 +102,7 @@
     import BaseResource from '@/view/syllabus/newSyllabus/operation/BaseResource'
     import BaseKnowledge from '@/view/syllabus/newSyllabus/operation/BaseKnowledge'
     import BaseQuestionList from '@/common/BaseQuestionList'
-    import ChooseContent from '@/components/learnactivity/ChooseContent'
+    import ChooseContent from '@/components/learnactivity/NewChooseContent'
     export default {
         props: ['volume', 'treeData', 'editable'],
         components: { BaseResource, BaseKnowledge, ChooseContent,BaseQuestionList },
@@ -222,7 +218,7 @@
                 this.contentIndex = val
                 this.currentItems = []
                 this.currentResources = []
-                data.items.length && this.findQuestionById(data.items)
+                // data.items.length && this.findQuestionById(data.items)
                 data.resources.length && this.findResourceById(data.resources)
                 this.isShowContent = true
             },
@@ -311,15 +307,16 @@
              */
             onSelectFile(val) {
                 this.fileList = val.files
+				console.log(val)
             },
 
             /** 保存内容与题目关联 */
             onSaveNode() {
                 this.isLoading = true
                 this.currentEditData.items = this.currentEditData.items ? [...new Set(this.currentEditData.items.concat(this.questionList.map(item => item.id)))] : [...new Set(this.questionList.map(item => item.id))]
-                this.currentEditData.resources = this.currentEditData.resources ? [...new Set(this.currentEditData.resources.concat(this.fileList.map(item => item.id)))] : [...new Set(this.questionList.map(item => item.id))]
+                this.currentEditData.resources = this.currentEditData.resources ? [...new Set(this.currentEditData.resources.concat(this.fileList.map(item => item.url)))] : [...new Set(this.fileList.map(item => item.url))]
                 this.$api.syllabus.SaveOrUpdateAsNodes([this.currentEditData]).then(res => {
-                    if (!res.error && res.result.data) {
+                    if (!res.error && res) {
                         this.isRelatedContent = false
                         this.$emit('onTreeUpdate')
                         this.isLoading = false

+ 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;
+}

+ 1 - 1
TEAMModelOS/ClientApp/src/css/dark-iview-form.less

@@ -2,7 +2,7 @@
     .ivu-input {
         background: none;
         color: white;
-        border-color: #606060;
+        border-color: #808080;
     }
 }
 

File diff suppressed because it is too large
+ 12 - 1
TEAMModelOS/ClientApp/src/icons/svg/hiteach_pro.svg


File diff suppressed because it is too large
+ 12 - 1
TEAMModelOS/ClientApp/src/icons/svg/hiteach_std.svg


File diff suppressed because it is too large
+ 12 - 1
TEAMModelOS/ClientApp/src/icons/svg/hiteach_tbl.svg


File diff suppressed because it is too large
+ 1 - 0
TEAMModelOS/ClientApp/src/icons/svg/htc_pro.svg


File diff suppressed because it is too large
+ 1 - 0
TEAMModelOS/ClientApp/src/icons/svg/htc_std.svg


File diff suppressed because it is too large
+ 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>

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

@@ -8,7 +8,7 @@ export default {
     subTitle: {
         IDLogin: '系统管理者、教师、学生与家长登入口',
         QRLogin: '使用HiTA或AClassONE扫描进行登入',
-        schoolLogin: '由学校一分配给学生使用的帐号登入口'
+        schoolLogin: '由学校一分配给学生使用的帐号登入口'
     },
     serAdress: {
         China: '中国',

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

@@ -8,7 +8,7 @@ export default {
     subTitle: {
         IDLogin: '系統管理者、教師、學生與家長登入口',
         QRLogin: '使用HiTA或AClassONE掃描進行登入',
-        schoolLogin: '由學校一分配給學生使用的帳號登入口'
+        schoolLogin: '由學校一分配給學生使用的帳號登入口'
     },
     serAdress: {
         China: '中國',

+ 8 - 8
TEAMModelOS/ClientApp/src/locale/lang/zh-TW/totalAnalysis.js

@@ -27,14 +27,14 @@ export default {
     ti_text2: '學生人數',
     ti_text3: '學段數',
     ti_text4: '年級數',
-    ti_text5: '班級數',
-    ti_text12: '全部評測',
-    ti_text6: '周考',
-    ti_text7: '期初考',
-    ti_text8: '期中考',
-    ti_text9: '期末考',
-    ti_text10: '類比測驗',
-    ti_text11: '診斷測驗',
+	ti_text5: '班級數',
+	ti_text12: '全部評測',
+	ti_text6: '段考',
+	ti_text7: '聯考',
+	ti_text8: '平常考',
+	ti_text9: '其它',
+	ti_text10: '模擬測驗',
+	ti_text11: '診斷測驗',
     echarts_text1: '最高值',
     echarts_text2: '最低值',
     echarts_text3: '平均值',

+ 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
+                }
+            ]
+        
+        
+          },
+  }),
+};

+ 8 - 1
TEAMModelOS/ClientApp/src/router/routes.js

@@ -131,7 +131,7 @@ export const routes = [
 							resolve),
 					},
 					{
-						name: 'earlyWarning',
+						// name: 'earlyWarning',
 						path: '/total/achievement/earlyWarning',
 						component: resolve => require(['@/view/student-analysis/total-analysis/AchievementAnalysis/EarlyWarning.vue'],
 							resolve)
@@ -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)
 			}
 
 

+ 1 - 0
TEAMModelOS/ClientApp/src/service/User.js

@@ -76,6 +76,7 @@ export class User {
           localStorage.removeItem('auth_token')
           localStorage.removeItem('user_profile')
           localStorage.removeItem('school_profile')
+          localStorage.removeItem('student_profile')
           
           // 重置登录状态
           User.$access.reset();

+ 37 - 0
TEAMModelOS/ClientApp/src/static/Global.js

@@ -0,0 +1,37 @@
+
+//文件类型,对应内容模块Blob目录
+const CONTENT_TYPES = {
+    'image': ['JPG', 'JPEG', 'PNG', 'GIF', 'SVG'],
+    'video': ['AVI', 'MP4', 'WEBM'],
+    'doc': ['PPT', 'PPTX', 'DOC', 'DOCX', 'PDF', 'XLS', 'XLSX', 'CSV'],
+    'res': ['HTE', 'HETX'],
+    'audio': ['MP3', 'WAV', 'OGG']
+}
+
+//教师Blob个人空间
+const PRIVATE_SPACE = 1024 * 1024 * 1024 * 1
+//学校Blob空间
+const SCHOOL_SPACE = 1024 * 1024 * 1024 * 10
+
+//系统默认考试类型
+const EXAM_TYPE = [
+    {
+        id: "100",
+        name: "期中考试",
+    },
+    {
+        id: "101",
+        name: "期末考试",
+    },
+    {
+        id: "200",
+        name: "模拟考试",
+    }
+]
+
+export default {
+    CONTENT_TYPES,
+    PRIVATE_SPACE,
+    SCHOOL_SPACE,
+    EXAM_TYPE
+}

+ 30 - 30
TEAMModelOS/ClientApp/src/static/baseDataDefault.json

@@ -5,30 +5,30 @@
         "alias": "简体中文",
         "grades": [
             {
-                "gradeCode": "1",
-                "gradeName": "一年级",
+                "id": "1",
+                "name": "一年级",
                 "alias": "小学一年级"
             },
             {
-                "gradeCode": "2",
-                "gradeName": "二年级",
+                "id": "2",
+                "name": "二年级",
                 "alias": "小学二年级"
             },
             {
-                "gradeCode": "3",
-                "gradeName": "三年级",
+                "id": "3",
+                "name": "三年级",
                 "alias": "小学三年级"
             }
         ],
         "subjects": [],
         "semesters": [
             {
-                "semesterName": "上学期",
-                "semesterCode": "1"
+                "name": "上学期",
+                "id": "1"
             },
             {
-                "semesterName": "下学期",
-                "semesterCode": "2"
+                "name": "下学期",
+                "id": "2"
             }
         ]
     },
@@ -38,27 +38,27 @@
         "alias": "繁體中文",
         "grades": [
             {
-                "gradeCode": "1",
-                "gradeName": "一年級"
+                "id": "1",
+                "name": "一年級"
             },
             {
-                "gradeCode": "2",
-                "gradeName": "二年級"
+                "id": "2",
+                "name": "二年級"
             },
             {
-                "gradeCode": "3",
-                "gradeName": "三年級"
+                "id": "3",
+                "name": "三年級"
             }
         ],
         "subjects": [],
         "semesters": [
             {
-                "semesterName": "上學期",
-                "semesterCode": "1"
+                "name": "上學期",
+                "id": "1"
             },
             {
-                "semesterName": "下學期",
-                "semesterCode": "2"
+                "name": "下學期",
+                "id": "2"
             }
         ]
     },
@@ -69,29 +69,29 @@
         "subjects": [],
         "grades": [
             {
-                "gradeCode": "1",
-                "gradeName": "Grade One",
+                "id": "1",
+                "name": "Grade One",
                 "alias": "Grade One"
             },
             {
-                "gradeCode": "2",
-                "gradeName": "Grade Two",
+                "id": "2",
+                "name": "Grade Two",
                 "alias": "Grade Two"
             },
             {
-                "gradeCode": "3",
-                "gradeName": "Grade Three",
+                "id": "3",
+                "name": "Grade Three",
                 "alias": "Grade Three"
             }
         ],
         "semesters": [
             {
-                "semesterName": "Last semester",
-                "semesterCode": "1"
+                "name": "Last semester",
+                "id": "1"
             },
             {
-                "semesterName": "Next semester",
-                "semesterCode": "2"
+                "name": "Next semester",
+                "id": "2"
             }
         ]
     }

+ 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
     }
 })

+ 4 - 4
TEAMModelOS/ClientApp/src/store/module/schoolBaseInfo.js

@@ -47,8 +47,6 @@ export default {
     },
     actions: {
         getSchoolBaseData(context) {
-            console.log('context:')
-            console.log(context)
             return new Promise(
                 (resolve, reject) => {
                     if (context.state.schoolBaseInfo.period.length == 0) {
@@ -103,8 +101,7 @@ export default {
                 (resolve, reject) => {
                     if (!context.state.classroomList) {
                         apiTools.schoolSetting.findClassInfo({
-                            'school_code': context.rootState.userInfo.schoolCode,
-                            //'id_token': localStorage.getItem('id_token')
+                            'school_code': context.rootState.userInfo.schoolCode
                         }).then(
                             res => {
                                 if (!res) {
@@ -116,6 +113,7 @@ export default {
                                 if (res.classrooms.length == 0) {
                                     resolve({
                                         code: 2,
+                                        data:[],
                                         message: '请求成功,数据为空'
                                     })
                                 }
@@ -133,6 +131,7 @@ export default {
                                             context.commit('setClassroomList', resData)
                                             resolve({
                                                 code: 1,
+                                                data: resData,
                                                 message: '数据请求成功'
                                             })
                                         } else {
@@ -154,6 +153,7 @@ export default {
                     } else {
                         resolve({
                             code: 3,
+                            data: context.state.classroomList,
                             message: '数据已请求,无需重复请求'
                         })
                     }

+ 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({});
+      });
+    },
+  },
+};

+ 0 - 1
TEAMModelOS/ClientApp/src/store/module/teachers.js

@@ -14,7 +14,6 @@ export default {
     },
     actions: {
         getTeacherList(context) {
-            console.log(context)
             return new Promise(
                 (resolve, reject) => {
                     if (context.state.teacherList.length == 0) {

+ 18 - 1
TEAMModelOS/ClientApp/src/store/module/totalAnalysis.js

@@ -26,7 +26,9 @@ export default {
         examPaper: null,
 
         exportTableData: null,
-        exportTableRefs:null
+        exportTableRefs:null,
+		exportTableParams:[],
+		excelNums:0
     },
     getters: {
 
@@ -51,6 +53,21 @@ export default {
 		updateExportTable(state, val) {
 		    state.exportTableData = val
 		},
+		
+		resetExportParams(state,val){
+			state.exportTableParams = []
+		},
+		
+		updateExcelNums(state,val){
+			state.excelNums = val
+		},
+		
+		// 更新当前页面表格数据
+		updateExportParams(state, val) {
+			if(state.exportTableParams.indexOf(val) < 0){
+				state.exportTableParams.push(val)
+			}
+		},
 
         // 更新当前页面表格数据
         updateExportDisable(state, val) {

+ 45 - 5
TEAMModelOS/ClientApp/src/store/module/user.js

@@ -25,7 +25,10 @@ export default {
             classes: undefined,    // 學校的教室List
         },
         studentProfile:{ // 學生在學校的設定檔
-
+            blob_uri: undefined,
+            blob_sas: undefined,
+            classinfo: undefined,
+            courses: undefined
         },
         authStatus: undefined, //(單數)老師權限總設定值
         authStatusMulti: undefined //(複數)老師權限總設定值
@@ -170,14 +173,20 @@ export default {
             state.userProfile.syllabus = data.syllabus // 老師的個人課綱
         },
         setSchoolProfile(state, data) {
-            state.schoolProfile.blob_sas = data.blob_sas // 老師Blob金鑰,讀寫
-            state.schoolProfile.blob_uri = data.blob_uri // 老師Blob網址
-            state.schoolProfile.courses = data.courses // 老師的個人課程
-            state.schoolProfile.syllabus = data.syllabus // 老師的個人課綱
+            state.schoolProfile.blob_sas = data.blob_sas // 老師在學校的Blob金鑰,讀寫
+            state.schoolProfile.blob_uri = data.blob_uri // 老師在學校的Blob網址
+            state.schoolProfile.courses = data.courses // 老師在學校的的個人課程
+            state.schoolProfile.syllabus = data.syllabus // 老師在學校的的個人課綱
             state.schoolProfile.periods = data.periods // 學制
             state.schoolProfile.grades = data.grades // 年級
             state.schoolProfile.classes = data.classes // 課堂
         },
+        setStudentProfile(state, data) {
+            state.studentProfile.blob_uri = data.blob_uri // 學生在學校的Blob網址
+            state.studentProfile.blob_sas = data.blob_sas // 學生在學校的Blob金鑰,讀寫
+            state.studentProfile.classinfo = data.classinfo // 學生在學校的的個人課程
+            state.studentProfile.courses = data.courses // 學生在學校的的個人課綱
+        },
         setAuthStatus(state, data) {
             state.authStatus = data;
         },
@@ -500,12 +509,22 @@ export default {
             localStorage.setItem('school_profile', encodeURIComponent(JSON.stringify(data),"utf-8"))
             context.commit('setSchoolProfile', data)
         },
+        setStudentProfile(context, data) {
+            localStorage.setItem('student_profile', encodeURIComponent(JSON.stringify(data),"utf-8"))
+            context.commit('setStudentProfile', data)
+        },
         checkSchoolProfile(context) {
             let school_profile = localStorage.getItem('school_profile')
             if(school_profile){
                 context.commit('setSchoolProfile', JSON.parse(decodeURIComponent(school_profile,"utf-8")))
             }
         },
+        checkStudentProfile(context) {
+            let student_profile = localStorage.getItem('student_profile')
+            if(student_profile){
+                context.commit('setStudentProfile', JSON.parse(decodeURIComponent(student_profile,"utf-8")))
+            }
+        },
         checkSchoolCode(context) {
             let login_schooCode = localStorage.getItem('login_schooCode')
             if(login_schooCode){
@@ -516,6 +535,27 @@ export default {
             localStorage.setItem('login_schooCode', data)
             context.commit('setSchoolCode', data)
         },
+        updSchoolProfileBasicInfo(context){
+            let login_schooCode = localStorage.getItem('login_schooCode')
+            if(login_schooCode){
+                apiTools.schoolSetting.findSchoolBase(
+                    login_schooCode
+                ).then( res => {
+                        let school_profile = localStorage.getItem('school_profile')
+                        if(school_profile){
+                            let obj_school_profile = JSON.parse(decodeURIComponent(school_profile,"utf-8"))
+
+                            obj_school_profile.periods = res.periods
+                            obj_school_profile.grades = res.grades
+                            obj_school_profile.classes = res.classes
+
+                            localStorage.setItem('school_profile', encodeURIComponent(JSON.stringify(obj_school_profile),"utf-8"))
+                            context.commit('setSchoolProfile', obj_school_profile)
+                        }
+                    }
+                )
+            }
+        },
         getGradeById(context, gradeId) {
             return new Promise((resolve) => {
                 if (typeof context.state.schoolProfile !== 'undefined' && context.state.schoolProfile !== undefined) {

+ 0 - 155
TEAMModelOS/ClientApp/src/utils/UploadTool.js

@@ -1,155 +0,0 @@
-
-const contentTypes = {
-    'image': ['JPG', 'JPEG', 'PNG', 'GIF'],
-    'video': ['AVI', 'MP4'],
-    'doc': ['PPT', 'PPTX', 'DOC', 'DOCX', 'PDF', 'XLS', 'XLSX', 'CSV'],
-    'teach': ['HTE', 'HETX']
-}
-const { BlobServiceClient } = require("@azure/storage-blob")
-
-//获取文件后缀和类型
-function getExAndType(fileName) {
-    let ex = fileName.substring(fileName.lastIndexOf('.') + 1)
-    let type = 'other'
-    ex = ex.toUpperCase()
-    for (let key in contentTypes) {
-        if (contentTypes[key].indexOf(ex) != -1) {
-            type = key
-            break
-        }
-    }
-    return {
-        ex, type
-    }
-}
-
-export default class UploadTool {
-
-    /**
-     * 初始化Blob,需要先调用授权API
-     * @param {string} blobUrl blob地址
-     * @param {string} container 容器名称
-     * @param {string} sasString 授权
-     * */
-    constructor(blobUrl, container, sasString) {
-        this.initBlob(blobUrl, container, sasString)
-    }
-    /**
-     * 初始化Blob,需要先调用授权API
-     * @param {string} blobUrl blob地址
-     * @param {string} container 容器名称
-     * @param {string} sasString 授权
-     * */
-    initBlob(blobUrl, container, sasString) {
-        if (blobUrl && container && sasString) {
-            const blobService = new BlobServiceClient(blobUrl + sasString)
-            let containerClient = blobService.getContainerClient(container)
-            if (containerClient) {
-                this.containerClient = containerClient
-                console.log('初始化成功...')
-            } else {
-                throw new Error("参数错误,初始化失败")
-            }
-        } else {
-            throw new Error("初始化参数不完整")
-        }
-    }
-
-    /**
-     * 上传文件方法,带回调上传进度
-     * @param {any} file 文件对象
-     * @param {any} path 文件夹路径
-     * @param {any} option 官方可配置项
-     * 
-     * @returns {object} {url, name,size,createTime,extension,type}
-     */
-    upload(file, path, option) {
-        return new Promise(async (r, j) => {
-            const blockBlobClient = this.containerClient.getBlockBlobClient(path + "/" + file.name)
-            blockBlobClient.uploadBrowserData(file, option).then(
-                res => {
-                    let url = res._response.request.url
-                    url = url.substring(0, url.lastIndexOf('?'))
-                    let info = getExAndType(file.name)
-                    r({
-                        url: url,
-                        name: file.name,
-                        size: res._response.request.body.size,
-                        createTime: res.lastModified.getTime(),
-                        extension: info.ex,
-                        type: info.type
-                    })
-                },
-                err => {
-                    j(err)
-                }
-            )
-        })
-    }
-
-    /**
-     * 列出(查询)
-     * @param {Object} option ContainerListBlobsOptions
-     * eg: option.prefix = 'res' 只查res文件夹下的blob
-     * @param {Object} pageInfo 
-     * eg: pageInfo.maxPageSize 当前请求条数
-     * eg: pageInfo.continuationToken 首次请求不需要,后面需要(首次请求会返回,下次需要传入)
-     * 
-     * @returns {object} {blobList, continuationToken}
-     */
-    async listBlob(option, pageInfo) {
-        return new Promise(async (r, j) => {
-            let page = {}
-            let blobList = []
-            if (pageInfo) {
-                page.maxPageSize = pageInfo.maxPageSize ? pageInfo.maxPageSize : 50
-                if (pageInfo.continuationToken) {
-                    page.continuationToken = pageInfo.continuationToken ? pageInfo.continuationToken : ''
-                }
-            }
-            if (this.containerClient) {
-                let iterator = this.containerClient.listBlobsFlat(option ? option : {}).byPage(page)
-                let response = (await iterator.next()).value
-                for (const blob of response.segment.blobItems) {
-                    let info = getExAndType(blob.name)
-                    blobList.push(
-                        {
-                            url: response.serviceEndpoint + response.containerName + '/' + blob.name,
-                            name: blob.name.substring(response.prefix.length + 1),
-                            size: blob.properties.contentLength,
-                            createTime: blob.properties.lastModified.getTime(),
-                            extension: info.ex,
-                            type: info.type
-                        }
-                    )
-                }
-                let continuationToken = response.continuationToken
-                r({
-                    blobList,
-                    continuationToken
-                })
-            } else {
-                j('containerClient 错误')
-            }
-        })
-    }
-
-    /**
-     * 删除Blob
-     * @param {string} filePath 文件url + 容器 之后的路径
-     */
-    deleteBlob(filePath) {
-        return new Promise((r, j) => {
-            this.containerClient.deleteBlob(filePath).then(
-                res => {
-                    r(res)
-                },
-                err => {
-                    j(err)
-                }
-            )
-
-        })
-    }
-
-}

+ 383 - 0
TEAMModelOS/ClientApp/src/utils/blobTool.js

@@ -0,0 +1,383 @@
+import GLOBAL from '@/static/Global.js';
+
+const { BlobServiceClient, BlobClient } = require("@azure/storage-blob")
+
+//获取文件后缀和类型
+function getExAndType(fileName) {
+    let ex = fileName.substring(fileName.lastIndexOf('.') + 1)
+    let type = 'other'
+    ex = ex.toUpperCase()
+    for (let key in GLOBAL.CONTENT_TYPES) {
+        if (GLOBAL.CONTENT_TYPES[key].indexOf(ex) != -1) {
+            type = key
+            break
+        }
+    }
+    return {
+        ex, type
+    }
+}
+
+export default class BlobTool {
+
+    /**
+     * 初始化Blob,需要先调用授权API
+     * @param {string} blobUrl blob地址
+     * @param {string} container 容器名称
+     * @param {string} sasString 授权
+     * */
+    constructor(blobUrl, container, sasString, scope) {
+        this.initBlob(blobUrl, container, sasString, scope)
+    }
+    /**
+     * 初始化Blob,需要先调用授权API
+     * @param {string} blobUrl blob地址
+     * @param {string} container 容器名称
+     * @param {string} sasString 授权
+     * @param {string} scope 学校(school)/个人(private) 计算空间大小
+     * */
+    initBlob(blobUrl, container, sasString, scope) {
+        if (blobUrl && container && sasString, scope) {
+            //初始化containerClient
+            this.blobService = new BlobServiceClient(blobUrl + sasString)
+            let containerClient = this.blobService.getContainerClient(container)
+            if (containerClient) {
+                this.containerClient = containerClient
+                this.container = container
+                this.blobUrl = blobUrl
+                this.sasString = sasString
+                if (scope == 'private') {
+                    this.blobSpace = GLOBAL.PRIVATE_SPACE
+                } else if (scope == 'school') {
+                    this.blobSpace = GLOBAL.SCHOOL_SPACE
+                }
+            } else {
+                throw new Error("参数错误,初始化失败")
+            }
+        } else {
+            throw new Error("初始化参数不完整")
+        }
+    }
+
+    /**
+     * 获取容器信息 (授权失败)
+     * @param {object} 
+     */
+    getProperties(options) {
+        return new Promise((r, j) => {
+            //const blockBlobClient = this.containerClient.getBlockBlobClient('res/基础操作范例_16x9.HTE') //blob获取成功
+            this.containerClient.getProperties(options).then(
+                res => {
+                    console.log('获取信息成功')
+                    console.log(res)
+                    r(res)
+                },
+                err => {
+                    console.log('获取信息失败')
+                    console.log(err)
+                    j(err)
+                }
+            )
+        })
+    }
+
+    /**
+     * 上传文件方法,带回调上传进度
+     * @param {any} file 文件对象
+     * @param {any} path 文件夹路径
+     * @param {any} option 官方可配置项
+     * 
+     * @returns {object} {url, name,size,createTime,extension,type}
+     */
+    upload(file, path, option) {
+        return new Promise(async (r, j) => {
+            if (!this.totalSize) {
+                await this.getContainerSize().then(
+                    res => {
+                        this.totalSize = res
+                    },
+                    err => {
+                        this.totalSize = -1
+                        j({
+                            spaceError: '容器空间计算失败,无法上传文件'
+                        })
+                    }
+                )
+            }
+            if (this.totalSize == -1 ) {
+                j({
+                    spaceError: 'Blob空间计算失败,无法上传文件'
+                })
+            } else if (this.totalSize > this.blobSpace) {
+                j({
+                    spaceError: 'Blob空间已满,无法上传'
+                })
+            } else {
+                const blockBlobClient = this.containerClient.getBlockBlobClient(path + "/" + file.name)
+                blockBlobClient.uploadBrowserData(file, option).then(
+                    res => {
+                        let url = res._response.request.url
+                        url = url.substring(0, url.lastIndexOf('?'))
+                        let info = getExAndType(file.name)
+                        this.totalSize += res._response.request.body.size
+                        r({
+                            url: url,
+                            blob: '/' + path + "/" + file.name,
+                            name: file.name,
+                            size: res._response.request.body.size,
+                            createTime: res.lastModified.getTime(),
+                            extension: info.ex,
+                            type: info.type
+                        })
+                    },
+                    err => {
+                        j(err)
+                    }
+                )
+            }
+        })
+    }
+
+    /**
+     * 列出(查询)
+     * @param {Object} option ContainerListBlobsOptions
+     * eg: option.prefix = 'res' 只查res文件夹下的blob
+     * @param {Object} pageInfo 
+     * eg: pageInfo.maxPageSize 当前请求条数
+     * eg: pageInfo.continuationToken 首次请求不需要,后面需要(首次请求会返回,下次需要传入)
+     * 
+     * @returns {object} {blobList, continuationToken}
+     */
+    async listBlob(option, pageInfo) {
+        return new Promise(async (r, j) => {
+            let page = {}
+            let blobList = []
+            if (pageInfo && JSON.stringify(pageInfo) != '{}') {
+                page.maxPageSize = pageInfo.maxPageSize ? pageInfo.maxPageSize : 50
+                if (pageInfo.continuationToken) {
+                    page.continuationToken = pageInfo.continuationToken ? pageInfo.continuationToken : ''
+                }
+            }
+            if (this.containerClient) {
+                let iterator = this.containerClient.listBlobsFlat(option ? option : {}).byPage(page)
+                let response = (await iterator.next()).value
+                let prefixLen = response.prefix ? response.prefix.length + 1: 0
+                for (const blob of response.segment.blobItems) {
+                    let info = getExAndType(blob.name)
+                    blobList.push(
+                        {
+                            url: response.serviceEndpoint + response.containerName + '/' + blob.name,
+                            blob: '/' + blob.name,
+                            name: blob.name.substring(prefixLen),
+                            size: blob.properties.contentLength,
+                            createTime: blob.properties.lastModified.getTime(),
+                            extension: info.ex,
+                            type: info.type
+                        }
+                    )
+                }
+                let continuationToken = response.continuationToken ? response.continuationToken : 'end'
+                r({
+                    blobList,
+                    continuationToken
+                })
+            } else {
+                j('containerClient 错误')
+            }
+        })
+    }
+
+    /**
+     * 删除Blob
+     * @param {string} filePath 文件url + 容器 之后的路径
+     */
+    deleteBlob(filePath) {
+        return new Promise((r, j) => {
+            this.containerClient.deleteBlob(filePath).then(
+                res => {
+                    r(res)
+                },
+                err => {
+                    j(err)
+                }
+            )
+
+        })
+    }
+
+    /**
+     * 批量删除Blob 官方API授权失败
+     * @param {string} files 文件url + 容器 之后的路径
+     */
+    //deleteBlobBatch(files) {
+    //    let blobBatchClient = this.blobService.getBlobBatchClient()
+    //    return new Promise((r, j) => {
+    //        let blobs = [] //BlobClient[]
+    //        for (let i in files) {
+    //            //files[i] = files[i].substring(0, files[i].lastIndexOf('?'))
+    //            blobs.push(new BlobClient(files[i]))
+    //        }
+    //        console.log(files)
+    //        console.log(blobs)
+
+    //        console.log(this.containerClient.credential)
+            
+    //        blobBatchClient.deleteBlobs(blobs).then(
+    //            res => {
+    //                console.log('批量删除成功')
+    //                console.log(res)
+    //            },
+    //            err => {
+    //                console.log('批量删除失败')
+    //                console.log(err)
+    //            }
+    //        )
+    //    })
+    //}
+    /**
+     * 批量删除Blob 循环操作
+     * @param {string} files 
+     */
+    deleteBlobBatch(files) {
+        return new Promise((r, j) => {
+            let promises = []
+            for (let item of files) {
+                let f = item.substring(1)
+                promises.push(this.containerClient.deleteBlob(f))
+            }
+            Promise.all(promises).then(
+                res => {
+                    r(res)
+                },
+                err => {
+                    j(err)
+                }
+            )
+        })
+    }
+
+    /**
+     * 判断文件是否存在
+     * @param {string} filePath 文件路径 正确 'res/基础操作范例_16x9.HTE ' 错误 '/res/基础操作范例_16x9.HTE'
+     * @param {object} 
+     * 
+     * return true/false
+     */
+    exists(filePath,options) {
+        const blockBlobClient = this.containerClient.getBlockBlobClient(filePath)
+        return new Promise((r, j) => {
+            blockBlobClient.exists(options).then(
+                res => {
+                    r(res)
+                },
+                err => {
+                    j(err)
+                }
+            )
+        })
+    }
+    /**
+     * 计算容器的空间大小
+     * @param {string}
+     * @param {object}
+     *
+     * return true/false
+     */
+    async getContainerSize() {
+        return new Promise((r, j) => {
+            this.listBlob().then(
+                res => {
+                    let totalSize = res.blobList.reduce((total, item) => {
+                        return total + parseInt(item.size)
+                    }, 0)
+                    this.totalSize = totalSize
+                    r(totalSize)
+                },
+                err => {
+                    j(err)
+                }
+            )
+        })
+    }
+    /**
+     * 计算指定文件夹的空间大小,如果不传文件夹,则查询整个容器空间
+     * @param {string} 
+     * @param {object} 
+     * 
+     * return true/false
+     */
+    getSize(option) {
+        return new Promise((r, j) => {
+            this.listBlob(option).then(
+                res => {
+                    let sizeInfo = {
+                        total: 0,   //所有
+                        image: 0,   //内容模块图片
+                        res: 0,     //内容模块教材
+                        video: 0,   //内容模块视频
+                        audio: 0,   //内容模块音频
+                        doc: 0,     //内容模块文档
+                        other: 0,   //内容模块其他文件
+                        data: 0      //除了内容模块之外的文件
+                    }
+                    let length = res.blobList.reduce((total, item) => {
+                        return total + parseInt(item.size)
+                    }, 0)
+                    sizeInfo.total = length
+                    for (let item of res.blobList) {
+                        switch (item.type) {
+                            case 'res':
+                                if (item.blob.indexOf('res') == 1) {
+                                    sizeInfo.res += item.size
+                                } else {
+                                    sizeInfo.data += item.size
+                                }
+                                break
+                            case 'image':
+                                if (item.blob.indexOf('image') == 1) {
+                                    sizeInfo.image += item.size
+                                } else {
+                                    sizeInfo.data += item.size
+                                }
+                                break
+                            case 'video':
+                                if (item.blob.indexOf('video') == 1) {
+                                    sizeInfo.video += item.size
+                                } else {
+                                    sizeInfo.data += item.size
+                                }
+                                break
+                            case 'audio':
+                                if (item.blob.indexOf('audio') == 1) {
+                                    sizeInfo.audio += item.size
+                                } else {
+                                    sizeInfo.data += item.size
+                                }
+                                break
+                            case 'doc':
+                                if (item.blob.indexOf('doc') == 1) {
+                                    sizeInfo.doc += item.size
+                                } else {
+                                    sizeInfo.data += item.size
+                                }
+                                break
+                            case 'other':
+                                if (item.blob.indexOf('other') == 1) {
+                                    sizeInfo.other += item.size
+                                } else {
+                                    sizeInfo.data += item.size
+                                }
+                                break
+                            default:
+                                break
+                        }
+                    }
+                    r(sizeInfo)
+                },
+                err => {
+                    j(err)
+                }
+            )
+        })
+    }
+}

+ 9 - 9
TEAMModelOS/ClientApp/src/utils/draw.js

@@ -10,7 +10,7 @@ const drawRect = (stage, layer, item, tr) => {
         y: item.position.y,
         scaleX: 1,
         scaleY: 1,
-        draggable: true,
+        draggable: false,
         rotation: 0
     };
 
@@ -22,7 +22,7 @@ const drawRect = (stage, layer, item, tr) => {
         width: item.position.cx,
         height: item.position.cy,
         shadowColor: "black",
-        draggable: true
+        draggable: false
     });
     group.add(rect);
     if (item.paragraph && item.paragraph[0].texts.length) drawText(stage, layer, item.paragraph, group)
@@ -39,7 +39,7 @@ const drawArc = (stage, layer, item) => {
         y: item.position.y,
         scaleX: 1,
         scaleY: 1,
-        draggable: true,
+        draggable: false,
         rotation: 0
     };
 
@@ -48,7 +48,7 @@ const drawArc = (stage, layer, item) => {
         data: item.svg.svgShape[0].d,
         stroke: "#" + item.border.color,
         strokeWidth: item.border.width,
-        draggable: true
+        draggable: false
     });
     group.add(arc);
 
@@ -66,7 +66,7 @@ const drawEllipse = (stage, layer, item) => {
         y: item.position.y + item.position.cy / 2,
         scaleX: 1,
         scaleY: 1,
-        draggable: true,
+        draggable: false,
         rotation: 0
     };
 
@@ -106,7 +106,7 @@ const drawLine = (stage, layer, item) => {
         y: item.position.y,
         scaleX: 1,
         scaleY: 1,
-        draggable: true,
+        draggable: false,
         rotation: 0
     };
 
@@ -135,7 +135,7 @@ const drawPath = (stage, layer, item) => {
         y: item.position.y,
         scaleX: 1,
         scaleY: 1,
-        draggable: true,
+        draggable: false,
         rotation: 0
     };
     let svgShape = item.svg.svgShape[0]
@@ -146,7 +146,7 @@ const drawPath = (stage, layer, item) => {
         stroke: "#" + item.border.color,
         strokeWidth: item.border.width,
         rotation: svgShape.transform ? +svgShape.transform.substring(7, svgShape.transform.length - 1).split(',')[0] : 0,
-        draggable: true
+        draggable: false
     });
     group.add(arc);
 
@@ -165,7 +165,7 @@ const drawImage = (stage, layer, item, tr) => {
             y: item.position.y,
             width: item.position.cx,
             height: item.position.cy,
-            draggable: true
+            draggable: false
         });
         layer.add(darthNode);
         layer.add(tr)

+ 176 - 0
TEAMModelOS/ClientApp/src/utils/evTools.js

@@ -0,0 +1,176 @@
+import $api from '@/api'
+import store from '@/store'
+import $tools from './public.js'
+
+
+export default {
+	/* 获取试题保存在Blob的JSON文件 */
+	createBlobItem(item){
+		return new Promise((r,j) => {
+			let itemJson = {
+				id:item.id,
+				exercise:{
+					answer:item.answer,
+					explain:item.explain,
+					type:item.type,
+					points:item.points,
+					field:item.field,
+					level:item.level,
+					periodId:item.periodId,
+					gradeIds:item.gradeIds,
+					subjectId:item.subjectId,
+					children:item.children || [],
+					scope:item.scope,
+					score: 0,
+					repairResource:item.repairResource,
+				},
+				render:2,
+				item:[{
+					question:item.question,
+					option:item.option
+				}]
+			}
+			r(itemJson)
+		})
+	},
+	
+	/* 获取保存在COSMOS里面的试题格式 */
+	createCosmosItem(item){
+		return new Promise((r,j) => {
+			let cosmosItem  = {
+				id:item.id,
+				code:item.code,
+				scope:item.scope,
+				score:0,
+				type:item.type,
+				question:this.getSimpleText(item.question),
+				points:item.points,
+				field:item.field,
+				level:item.level,
+				periodId:item.periodId,
+				gradeIds:item.gradeIds,
+				subjectId:item.subjectId,
+				blob:item.blob
+			}
+			r(cosmosItem)
+		})
+	},
+	
+	/* 生成试卷的index.json文件格式 */
+	createBlobPaper(paper,urls){
+		return new Promise((r,j) => {
+			let paperItem  = {
+				id:paper.id,
+				name:paper.name,
+				code:paper.code,
+				scope:paper.scope,
+				multipleRule:paper.multipleRule,
+				urls:urls,
+				scoring:paper.scoring,
+				points:paper.points,
+				periodId:paper.periodId,
+				gradeIds:paper.gradeIds,
+				subjectId:paper.subjectId,
+				subjectName:paper.subjectName,
+				score:paper.score
+			}
+			r(paperItem)
+		})
+	},
+	
+	/* 生成试卷保存在cosmos的数据结构 */
+	createCosmosPaper(paper){
+		return new Promise((r,j) => {
+			let paperItem  = {
+				id:paper.id,
+				name:paper.name,
+				code:paper.code,
+				blob:paper.blob,
+				scope:paper.scope,
+				scoring:paper.scoring,
+				points:paper.points,
+				periodId:paper.periodId,
+				gradeIds:paper.gradeIds,
+				subjectId:paper.subjectId,
+				subjectName:paper.subjectName,
+				score:paper.score,
+				multipleRule:paper.multipleRule
+			}
+			r(paperItem)
+		})
+	},
+	
+	/* 获取完整的试题数据 */
+	getFullItem(list){
+		return new Promise(async (resolve,reject) => {
+			let promiseArr = []
+			let schoolHost = JSON.parse(decodeURIComponent(localStorage.school_profile, "utf-8")).blob_uri
+			let privateHost = JSON.parse(decodeURIComponent(localStorage.user_profile, "utf-8")).blob_uri
+			let schoolSas = await $tools.getSchoolSas()
+			let privateSas = await $tools.getPrivateSas()
+			for(let i=0; i<list.length; i++){
+				promiseArr.push(new Promise(async (r,j) => {
+					if(list[i].blob){
+						const blobHost = list[i].scope === 'school' ?  schoolHost :  privateHost
+						// 根据试题的Blob地址 去读取JSON文件
+						let sasString = list[i].scope === 'school' ?  schoolSas : privateSas
+						let jsonInfo = await $tools.getFile(blobHost + list[i].blob + sasString.sas)
+						let jsonData = JSON.parse(jsonInfo)
+						// 调整渲染试题数据结构
+						jsonData.id = list[i].id
+						jsonData.exercise.question = jsonData.item[0].question
+						jsonData.exercise.blob = list[i].blob
+						jsonData.exercise.code = list[i].code
+						jsonData.exercise.option = jsonData.item[0].option
+						jsonData.exercise.id = list[i].id
+						r(jsonData.exercise)
+					}else{
+						r(null)
+					}
+				}))
+			}
+			Promise.all(promiseArr).then(result => {
+				resolve(result)
+			})
+		})
+	},
+	
+	/* 获取完整的试卷数据 */
+	getFullPaper(paper){
+		return new Promise(async (r,j) => {
+			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()
+			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)
+			}
+			
+		})
+		
+	},
+	
+	/* 提取富文本内容中的文本 */
+	getSimpleText(html) {
+		// var msg = html.replace(/<(?!img).*?>/g, '') // 执行替换成空字符
+		var r = /<[^>]*>|/g;
+		return html.replace(r, '')
+	},
+}

+ 29 - 0
TEAMModelOS/ClientApp/src/utils/excel.js

@@ -78,6 +78,8 @@ export const export_json_to_excel = ({ data, key, title, filename, autoWidth })
         auto_width(ws, arr);
     }
     XLSX.utils.book_append_sheet(wb, ws, filename);
+	
+
     XLSX.writeFile(wb, filename + '.xlsx');
 }
 
@@ -90,9 +92,35 @@ export const export_array_to_excel = ({ key, data, title, filename, autoWidth })
         auto_width(ws, arr);
     }
     XLSX.utils.book_append_sheet(wb, ws, filename);
+	var wbout = XLSX.write(wb, {
+		bookType: 'xlsx',
+		bookSST: true,
+		type: 'array'
+	  });
     XLSX.writeFile(wb, filename + '.xlsx');
 }
 
+export const excelZipExport = ({ key, data, title, filename, autoWidth }) => {
+	return new Promise((r,j) => {
+		const wb = XLSX.utils.book_new();
+		const arr = json_to_array(key, data);
+		arr.unshift(title);
+		const ws = XLSX.utils.aoa_to_sheet(arr);
+		if (autoWidth) {
+		    auto_width(ws, arr);
+		}
+		XLSX.utils.book_append_sheet(wb, ws, filename);
+		var wbout = XLSX.write(wb, {
+			bookType: 'xlsx',
+			bookSST: true,
+			type: 'array'
+		  });
+		let excelFile = new File([wbout], filename + '.xlsx')
+		r(excelFile)
+	})
+    
+}
+
 export const read = (data, type) => {
   /* if type == 'base64' must fix data first */
   // const fixedData = fixdata(data)
@@ -109,5 +137,6 @@ export default {
     export_table_to_excel,
     export_array_to_excel,
     export_json_to_excel,
+	excelZipExport,
     read
 }

+ 49 - 3
TEAMModelOS/ClientApp/src/utils/js-fn.js

@@ -115,8 +115,8 @@ function compressImgByUrl(url, name, quality) {
     return new Promise((r, j) => {
         try {
             let img = new Image()
+            img.setAttribute('crossOrigin', 'Anonymous')
             img.src = url
-            img.setAttribute('crossOrigin', 'anonymous')
             img.onload = function () {
                 let canvas = document.createElement('canvas')
                 canvas.width = 300
@@ -144,7 +144,7 @@ function createVideoPoster(url, name, quality) {
                 let video = document.createElement('video')
                 video.setAttribute('width', '300')
                 video.setAttribute('controls', 'controls')
-                video.setAttribute('crossOrigin', '*')
+                video.setAttribute('crossOrigin', 'Anonymous')
                 video.setAttribute('src', url)
                 video.addEventListener('loadeddata', () => {
                     let canvas = document.createElement('canvas')
@@ -179,6 +179,48 @@ function dataURLtoFile(dataurl, filename) {
     return new File([u8arr], filename, { type: mime })
 }
 
+/*
+ * 函数防抖
+ */
+function debounce(func, delay) {
+    return function () {
+        window.clearTimeout(window.timeout)
+        window.timeout = setTimeout(() => {
+            clearTimeout(window.timeout)
+            func.apply(this, arguments)
+        }, delay)
+    }
+}
+
+/*
+ * 函数节流
+ */
+function throttle(func, delay) {
+    let run = true
+    return function () {
+        if (!run) {
+            return  // 如果开关关闭了,那就直接不执行下边的代码
+        }
+        run = false // 持续触发的话,run一直是false,就会停在上边的判断那里
+        setTimeout(() => {
+            func.apply(this, arguments)
+            run = true // 定时器到时间之后,会把开关打开,我们的函数就会被执行
+        }, delay)
+    }
+}
+//转换bytes
+function formatBytes(bytes) {
+    return bytes / 1024 < 1024 ? (bytes / 1024).toFixed(1) + 'KB' : bytes / 1024 / 1024 < 1024 ? (bytes / 1024 / 1024).toFixed(1) + 'M' : (bytes / 1024 / 1024 / 1024).toFixed(1) + 'G'
+}
+//生成uuid
+function uuid() {
+    function S4() {
+        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1)
+    }
+    return (S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4())
+}
+
+
 export default {
     groupBy,
     isObjEqual,
@@ -189,5 +231,9 @@ export default {
     getSubjectName,
     getClassroomByCode,
     compressImgByUrl,
-    createVideoPoster
+    createVideoPoster,
+    debounce,
+    throttle,
+    formatBytes,
+    uuid
 }

+ 23 - 19
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'
@@ -362,7 +362,6 @@ export default {
 					}
 				)
 			} else {
-				console.log(store)
 				r(store.state.privateSas)
 			}
 		})
@@ -469,23 +468,28 @@ export default {
 				])
 			}
 		});
-		const uploadFile = require('./upload.js')
-		console.log(uploadFile)
-		console.log(file)
-		// 获取当前保存权限设置
-		const scopeType = vm.$parent.exerciseScope === 0 ? 'private' : 'school'
-		const privateSas = await this.getPrivateSas()
-		const schoolSas = await this.getSchoolSas()
-		// 上传文件
-		const blobData = await uploadFile.default.uploadFile(file, 'item', scopeType)
-		const sasString = vm.$parent.exerciseScope === 0 ? privateSas.sas : schoolSas.sas
-		// 获取blob链接以及视频封面截图
-		const fileBlobUrl = blobData.blobUrl + sasString
-		const posterBase64 = await this.getVideoBase64(fileBlobUrl)
-		editor.txt.append('<img data-url=' + fileBlobUrl + ' data-name=' + blobData.fileName + ' src=' +
-			posterBase64 + ' class="richText-video"></img>')
-		editor.change()
-		vm.$Spin.hide();
+		const blobTool = require('./blobTool.js')
+		const isSchool = vm.$parent.exerciseScope === 1
+		const sasData = isSchool ? await this.getPrivateSas() : await this.getSchoolSas()
+		const scope = isSchool ? 'school' : 'private'
+		const blobToolClass = blobTool.default
+		//初始化Blob
+		let containerClient = new blobToolClass(sasData.url, sasData.name, sasData.sas, scope)
+		try{
+			// 上传文件
+			let blobFile = await containerClient.upload(file, 'item')
+			// 获取blob链接以及视频封面截图
+			const fileBlobUrl = blobFile.url + sasData.sas
+			const posterBase64 = await this.getVideoBase64(fileBlobUrl)
+			editor.txt.append('<img data-url=' + fileBlobUrl + ' data-name=' + blobFile.name + ' src=' +
+				posterBase64 + ' class="richText-video"></img>')
+			editor.change()
+			vm.$Spin.hide();
+		}catch(e){
+			vm.$Message.error(e.spaceError)
+			vm.$Spin.hide();
+		}
+		
 	},
 	isEqual: function(x, y) {
 		// If both x and y are null or undefined and exactly the same 

+ 3 - 10
TEAMModelOS/ClientApp/src/utils/upload.js

@@ -2,6 +2,7 @@ import $api from '@/api'
 import sha1 from 'js-sha1'
 import tools from '@/utils/public.js'
 import { blob } from 'd3'
+import GLOBAL from '@/static/Global.js';
 const { BlobServiceClient } = require("@azure/storage-blob")
 
 let sasString = ''
@@ -9,12 +10,6 @@ let blobContainer = ''
 let urlString = ''
 let folder = ''
 let containerClient  = null
-const contentTypes = {
-    'image': ['JPG', 'JPEG', 'PNG', 'GIF'],
-    'video': ['AVI', 'MP4'],
-    'doc': ['PPT', 'PPTX', 'DOC', 'DOCX', 'PDF', 'XLS', 'XLSX', 'CSV'],
-    'teach': ['HTE', 'HETX']
-}
 
 /**
  * 初始化Blob,需要先调用授权API
@@ -42,8 +37,6 @@ function initBlob(blobUrl, container, sasString) {
  */
 async function upload(containerClient, file, path, option){
     return new Promise(async (r, j) => {
-        //v12
-        console.log('uploading.....')
         const blockBlobClient = containerClient.getBlockBlobClient(path + "/" + file.name)
         blockBlobClient.uploadBrowserData(file, option).then(
             res => {
@@ -117,8 +110,8 @@ function getExAndType(fileName) {
     let ex = fileName.substring(fileName.lastIndexOf('.') + 1)
     let type = 'other'
     ex = ex.toUpperCase()
-    for (let key in contentTypes) {
-        if (contentTypes[key].indexOf(ex) != -1) {
+    for (let key in CONTENT_TYPES) {
+        if (CONTENT_TYPES[key].indexOf(ex) != -1) {
             type = key
             break
         }

+ 18 - 3
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() {
@@ -46,8 +61,8 @@
 				this.$router.push({ path: '/home/settings' })
 			},
             toFeedback() {
-				this.isShowMock = true
-                // this.$router.push({ path: '/home/feedback' })
+				// this.isShowMock = true
+                this.$router.push({ path: '/home/feedback' })
             },
             getSchoolBaseInfo() {
                 this.$store.dispatch('schoolBaseInfo/getSchoolBaseData').then(

+ 5 - 4
TEAMModelOS/ClientApp/src/view/classmgt/ManageClass.less

@@ -118,14 +118,14 @@
     width: ~"calc(100% - 75px)";
 }
 .student-info-item {
-    width: 90%;
-    margin: 5px auto;
-    background: rgba(28,192,243,.5);
+    width: 95%;
+    margin: 2px auto;
+    background: rgb(97, 97, 97);
     padding: 5px;
     color: white;
     cursor: pointer;
     user-select: none;
-    border-radius: 3px;
+    border-radius: 2px;
 
     &:hover {
         background: rgba(28,192,243,1);
@@ -135,4 +135,5 @@
 }
 .list-group {
     min-height: 180px;
+    padding-top:5px;
 }

+ 201 - 137
TEAMModelOS/ClientApp/src/view/classmgt/ManageClass.vue

@@ -1,65 +1,72 @@
 <template>
-    <div class="mgt-class-container dark-iview-select dark-iview-checkbox custom-scroll-bar common-save-btn" @click="testStatus = true">
+    <div class="mgt-class-container dark-iview-select dark-iview-checkbox custom-scroll-bar common-save-btn" @click="nameEdStatus = true">
         <div class="mgt-class-header">
             <span class="text-label">班级:</span>
             <Select v-model="curClassCode" style="width:200px">
-                <Option v-for="(item,index) in classList" :value="item.id" :key="index">{{ item.name }}</Option>
+                <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}}
+            </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>
-        <div class="mgt-class-body dark-iview-table animated fadeIn" id="table-wrap" v-show="viewType == 1">
-            <Table :columns="columns" :data="students" :loading="tableLoading" ref="students" :max-height="tableHeight">
-                <Loading slot="loading" bgColor="rgba(103, 103, 103, 0.27)"></Loading>
-                <template slot-scope="{ row,index }" slot="header">
-                    <span class="name-header" :style="{background:bgColor[index % 12]}">{{getFirstChart(row.name)}}</span>
-                </template>
-                <template slot-scope="{ row,index }" slot="action">
-                    <div class="item-tools" v-if="$access.can('admin.*|student-upd')">
-                        <Button type="error" size="small">重置密码</Button>
-                    </div>
-                </template>
-                <template slot-scope="{ row, index }" slot="groupCode">
-                    <span>{{row.groupCode ? row.groupCode : '- -'}}</span>
-                </template>
-                <template slot-scope="{ row, index }" slot="groupName">
-                    <span>{{row.groupName ? row.groupName : '未分组'}}</span>
-                </template>
-            </Table>
+        <div class="mgt-class-body dark-iview-table animated fadeIn" id="table-wrap" v-if="viewType == 1">
+            <vuescroll>
+                <Table :columns="columns" v-if="classList[curClassIndex]" :data="classList[curClassIndex].students" :loading="tableLoading" ref="students" :height="tableHeight" no-data-text="暂无学生">
+                    <Loading slot="loading" bgColor="rgba(103, 103, 103, 0.27)"></Loading>
+                    <template slot-scope="{ row,index }" slot="header">
+                        <span class="name-header" :style="{background:bgColor[index % 12]}">{{getFirstChart(row.name)}}</span>
+                    </template>
+                    <template slot-scope="{ row,index }" slot="action">
+                        <div class="item-tools" v-if="$access.can('admin.*|student-upd')">
+                            <Icon type="md-refresh" size="18" style="cursor:pointer;" title="重置密码" @click="resetPassword(index)"/>
+                        </div>
+                    </template>
+                    <template slot-scope="{ row, index }" slot="groupId">
+                        <span>{{row.groupId ? row.groupId : '- -'}}</span>
+                    </template>
+                    <template slot-scope="{ row, index }" slot="groupName">
+                        <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-show="viewType == 0">
-            <div class="group-wrap-item" v-for="(item,index) in groupData" :key="index">
-                <div class="group-title-wrap">
-                    <span class="group-num-tag">{{parseInt(item.groupCode) + 1}}</span>
-                    <Input v-model="item.groupName" class="group-name-tag" placeholder="组名..." :disabled="testStatus" @click.native.stop="testStatus = false" title="修改组名" autofocus @on-blur="testStatus = true" />
-                    <Icon type="md-close" class="close-group-icon" @click="delGroup(index)"/>
-                </div>
-                <Draggable ghost-class="ghost" group="student" class="list-group" :list="item.students" :animation='200' @end="dragStudent">
-                    <div class="student-info-item" v-for="(stuItem,index) in item.students" :key="index">
-                        <!--<span>{{stuItem.seatNo}}</span>-->
-                        <span>{{stuItem.name}}</span>
-                        <span>{{'('+stuItem.studentId+')'}}</span>
+        <div class="mgt-class-body dark-iview-table animated fadeIn dark-iview-input disabled-iview-input" id="table-wrap" v-else>
+            <vuescroll>
+                <div class="group-wrap-item" v-for="(item,index) in groupData" :key="index">
+                    <div class="group-title-wrap">
+                        <span class="group-num-tag">{{parseInt(item.groupId)}}</span>
+                        <Input v-model="item.groupName" class="group-name-tag" placeholder="组名..." 
+                               :disabled="nameEdStatus" @click.native.stop="nameEdStatus = false" title="修改组名" @on-change="$jsFn.debounce(setGroupName,1000)(index)"/>
+                        <Icon type="md-close" class="close-group-icon" @click="delGroup(index)" />
                     </div>
-                    <EmptyData textContent="暂无学生"  v-if="item.students.length == 0"></EmptyData>
-                </Draggable>
-            </div>
-            <div id="add-group-box">
-                <!--<p class="group-name-label">创建组名</p>-->
-                <Input v-model="groupName" placeholder="输入组名......" style="width: 200px;margin:40px 50px 10px 50px;" />
-                <Icon type="md-add-circle" class="add-group-icon" @click="addGroup"/>
-                <p class="add-group-label" @click="addGroup">新增组别</p>
-            </div>
+                    <Draggable ghost-class="ghost" group="student" class="list-group" :list="item.students" :animation='200' @end="groupToList">
+                        <div class="student-info-item" v-for="(stuItem,index) in item.students" :key="index">
+                            <span>{{stuItem.name}}</span>
+                            <span>{{'('+stuItem.id+')'}}</span>
+                        </div>
+                        <EmptyData textContent="暂无学生" v-if="item.students.length == 0"></EmptyData>
+                    </Draggable>
+                </div>
+                <div id="add-group-box">
+                    <Input v-model="groupName" placeholder="输入组名......" style="width: 200px;margin:40px 50px 10px 50px;" />
+                    <Icon type="md-add-circle" class="add-group-icon" @click="addGroup" />
+                    <p class="add-group-label" @click="addGroup">新增组别</p>
+                </div>
+            </vuescroll>
         </div>
         <Modal v-model="customGroupStatus"
                :title="$t('courseManage.classroom.autoGroupBtn')"
@@ -67,7 +74,7 @@
                class-name="dark-iview-modal dark-iview-form">
             <Form :label-width="80" :label-colon="true" style="color:white;">
                 <FormItem :label="$t('courseManage.classroom.studentCountLabel')">
-                    <span>{{students.length}}人</span>
+                    <span v-if="customGroupStatus">{{classList[curClassIndex].students.length}}人</span>
                 </FormItem>
                 <FormItem :label="$t('courseManage.classroom.groupCountLabel')">
                     <InputNumber :max="10" :min="1" v-model="groupNum"></InputNumber>
@@ -92,22 +99,23 @@
 <script>
     import Draggable from 'vuedraggable'
     import excel from '@/utils/excel.js'
+import { Function } from 'core-js'
     export default {
         components: {
             Draggable
         },
         data() {
             return {
+                curClassIndex:0,
                 groupName:'',
                 groupData: [],
                 tableHeight:0,
-                testStatus: true,
+                nameEdStatus: true,
                 viewType: 1,
                 tableLoading: false,
                 customGroupStatus: false,
                 updated: false,
                 tableLoading: false,
-                students: [],
                 classList: [],
                 groupNum: 2,
                 groupType: '1',
@@ -132,28 +140,42 @@
                         //width:200
                     },
                     {
-                        key: 'studentId',
+                        key: 'id',
                         title: this.$t('stuAccount.account'),
                         align: 'center',
                         //width:200
                     },
                     {
-                        key: 'seatNo',
+                        key: 'no',
                         title: this.$t('stuAccount.seatNo'),
                         align: 'center',
                         sortable: true,
-                        //width: 80
+                        sortMethod: function (a, b, type) {
+                            if (type == 'asc') {
+                                return parseInt(a) > parseInt(b) ? 1 : -1
+                            } else if (type == 'desc') {
+                                return parseInt(a) > parseInt(b) ? -1 : 1
+                            }
+                        }
                     },
                     {
-                        slot: 'groupCode',
+                        slot: 'groupId',
                         title: '组别',
+                        key:'groupId',
                         align: 'center',
                         sortable: true,
-                        //width: 80
+                        sortMethod: function (a, b, type) {
+                            if (type == 'asc') {
+                                return parseInt(a) > parseInt(b) ? 1 : -1
+                            } else if (type == 'desc') {
+                                return parseInt(a) > parseInt(b) ? -1 : 1
+                            }
+                        }
                     },
                     {
                         slot: 'groupName',
                         title: '组名',
+                        key: 'groupName',
                         align: 'center',
                         sortable: true,
                         //width: 80
@@ -167,63 +189,120 @@
             }
         },
         methods: {
+            resetPassword(index) {
+                this.$Message.warning('暂未对接重置密码API')
+            },
+            //切换班级
+            selectClass(index) {
+                this.curClassIndex = index
+                if (this.viewType == 0) {
+                    this.handelGroup()
+                    console.log('123')
+                }
+            },
+            //设置组名
+            setGroupName(index) {
+                console.log('执行函数')
+                for (let i in this.classList[this.curClassIndex].students) {
+                    if (this.classList[this.curClassIndex].students[i].groupId == this.groupData[index].groupId) {
+                        this.classList[this.curClassIndex].students[i].groupName = this.groupData[index].groupName
+                        console.log(this.groupData[index].groupName)
+                        console.log(this.classList[this.curClassIndex].students[i])
+                    }
+                }
+                this.updated = true
+            },
+            //分组视图修改后对应调整表格视图
+            groupToList() {
+                this.classList[this.curClassIndex].students = []
+                for (let i in this.groupData) {
+                    for (let j in this.groupData[i].students) {
+                        this.groupData[i].students[j].groupId = this.groupData[i].groupId
+                        this.groupData[i].students[j].groupName = this.groupData[i].groupName
+                        this.classList[this.curClassIndex].students.push(this.groupData[i].students[j])
+                    }
+                }
+                console.log(this.groupData)
+                this.updated = true
+            },
             //删除组别
             delGroup(index) {
                 if (this.groupData.length > 1) {
                     let students = JSON.parse(JSON.stringify(this.groupData[index].students))
+                    for (let i in students) {
+                        students[i].groupId = this.groupData[0].groupId
+                        students[i].groupName = this.groupData[0].groupName
+                    }
                     this.groupData.splice(index, 1)
                     this.groupData[0].students.push(...students)
+                    this.groupToList()
                 } else {
                     this.$Message.warning('至少保留一组')
                 }
+                console.log(this.groupData)
+                this.updated = true
             },
             //新增分组
             addGroup() {
                 if (this.groupName == '') {
                     this.$Message.warning('请先输入组名再创建组别')
+                } else if (this.classList[this.curClassIndex].students.length == 0) {
+                    this.$Message.warning('暂无学生可以进行分组')
                 } else {
                     this.groupData.push({
-                        groupCode: this.groupData.length,
+                        groupId: (this.groupData.length + 1) + '',
                         groupName: this.groupName,
-                        students:[]
+                        students: []
                     })
                     this.groupName = ''
                 }
-            },
-            //拖动分组
-            dragStudent(to, from, item, clone, oldIndex, newIndex) {
-                
+                this.updated = true
             },
             toggleView() {
                 this.viewType = 1 - this.viewType
-                this.handelGroup()
+                if (!this.viewType) {
+                    this.handelGroup()
+                }
+                
             },
-            //计算分组信息
+            //列表视图转分组视图
             handelGroup() {
-                if (!this.viewType) {
-                    let groupRes = this.$jsFn.groupBy(this.students, 'groupCode')
-                    this.groupData.length = 0
-                    for (let index in groupRes) {
-                        if (groupRes[index][0].groupCode) {
-                            this.groupData.push({
-                                groupName: groupRes[index][0].groupName,
-                                groupCode: index,
-                                students:groupRes[index]
-                            })
-                        } else {
-                            this.groupData.push({
-                                groupName: '未命名',
-                                groupCode: index,
-                                students:groupRes[index]
-                            })
-                        }
-                    }
-                    this.groupData.sort((a, b) => {
-                        return parseInt(a.groupCode) - parseInt(b.groupCode)
+                let groupRes = this.$jsFn.groupBy(this.classList[this.curClassIndex].students, 'groupId')
+                this.groupData.length = 0
+                for (let index in groupRes) {
+                    this.groupData.push({
+                        groupName: groupRes[index][0].groupName,
+                        groupId: groupRes[index][0].groupId,
+                        students: groupRes[index]
                     })
                 }
+                this.groupData.sort((a, b) => {
+                    return parseInt(a.groupId) - parseInt(b.groupId)
+                })
+                //this.updated = true
             },
             saveGroup() {
+                console.log('保存分组。。。')
+                console.log(this.classList[this.curClassIndex])
+                this.tableLoading = true
+                this.$api.schoolSetting.upsertGroup({
+                    classroom: this.classList[this.curClassIndex]
+                }).then(
+                    (res) => {
+                        if (!res.error) {
+                            this.$Message.success('保存成功!')
+                        } else {
+                            this.$Message.error('API error!')
+                        }
+                    },
+                    (err) => {
+                        this.$Message.error('API error!')
+                    }
+                ).finally(() => {
+                    setTimeout(() => {
+                        this.tableLoading = false
+                    }, 500)
+                })
 
             },
             comfirmCustomRules() {
@@ -251,19 +330,18 @@
                 }
             },
             orderGroupS() {
-                let surplus = this.students.length % this.groupNum// 余数
-                let maxCount = surplus == 0 ? this.students.length / this.groupNum : Math.ceil(this.students.length / this.groupNum)// 每组最大人数
-                this.students = this.students.sort((a, b) => {
+                let stuLen = this.classList[this.curClassIndex].students.length
+                let surplus = stuLen % this.groupNum// 余数
+                let maxCount = surplus == 0 ? stuLen / this.groupNum : Math.ceil(stuLen / this.groupNum)// 每组最大人数
+                this.classList[this.curClassIndex].students = this.classList[this.curClassIndex].students.sort((a, b) => {
                     a.seatNo > b.seatNo
                 })
                 for (let i = 0; i < maxCount; i++) {
                     for (let j = 0; j < this.groupNum; j++) {
                         let startIndex = this.groupNum * i
-                        if (startIndex + j < this.students.length) {
-                            this.$set(this.students[startIndex + j], 'groupCode', i + 1)
-                            this.$set(this.students[startIndex + j], 'groupName', '组别别名' + (i + 1))
-                            //this.students[startIndex + j].groupCode = j + 1
-                            //this.students[startIndex + j].groupName = '组别别名' + (j + 1)
+                        if (startIndex + j < stuLen) {
+                            this.$set(this.classList[this.curClassIndex].students[startIndex + j], 'groupId', i + 1)
+                            this.$set(this.classList[this.curClassIndex].students[startIndex + j], 'groupName', '组别别名' + (i + 1))
                         } else {
                             break
                         }
@@ -271,23 +349,22 @@
                 }
                 setTimeout(() => {
                     this.tableLoading = false
-                }, 800)
+                }, 500)
             },
             orderGroup() {
-                let surplus = this.students.length % this.groupNum// 余数
-                let maxCount = surplus == 0 ? this.students.length / this.groupNum : Math.ceil(this.students.length / this.groupNum)// 每组最大人数
-                this.students = this.students.sort((a, b) => {
-                    a.seatNo > b.seatNo
+                let stuLen = this.classList[this.curClassIndex].students.length
+                let surplus = stuLen % this.groupNum// 余数
+                let maxCount = surplus == 0 ? stuLen / this.groupNum : Math.ceil(stuLen / this.groupNum)// 每组最大人数
+                this.classList[this.curClassIndex].students = this.classList[this.curClassIndex].students.sort((a, b) => {
+                    a.no > b.no
                 })
                 let flag = 0
                 let startIndex = 0
                 for (let i = 0; i < this.groupNum; i++) {
                     for (let j = 0; j < maxCount; j++) {
-                        if (startIndex + j < this.students.length) {
-                            this.$set(this.students[startIndex + j], 'groupCode', i + 1)
-                            this.$set(this.students[startIndex + j], 'groupName', '组别别名' + (i + 1))
-                            //this.students[startIndex + j].groupCode = i + 1
-                            //this.students[startIndex + j].groupName = '组别别名' + (i + 1)
+                        if (startIndex + j < stuLen) {
+                            this.$set(this.classList[this.curClassIndex].students[startIndex + j], 'groupId', i + 1)
+                            this.$set(this.classList[this.curClassIndex].students[startIndex + j], 'groupName', '组别别名' + (i + 1))
                         } else {
                             break
                         }
@@ -300,18 +377,19 @@
                 }
                 setTimeout(() => {
                     this.tableLoading = false
-                }, 800)
+                }, 500)
             },
             randomGroup() {
-                let surplus = this.students.length % this.groupNum// 余数
+                let stuLen = this.classList[this.curClassIndex].students.length
+                let surplus = stuLen % this.groupNum// 余数
                 let surplusCount = surplus// 余数
-                let maxCount = surplus == 0 ? this.students.length / this.groupNum : Math.ceil(this.students.length / this.groupNum)// 每组最大人数
+                let maxCount = surplus == 0 ? stuLen / this.groupNum : Math.ceil(stuLen / this.groupNum)// 每组最大人数
                 let record = {}// 记录每个组已经分配的人数
                 for (let i = 1; i <= this.groupNum; i++) {
                     record[i] = 0
                 }
                 let flag = true
-                for (let index in this.students) {
+                for (let index in this.classList[this.curClassIndex].students) {
                     let groupIndex = this.$jsFn.getBtwRandom(1, this.groupNum)
                     if (record[groupIndex] < maxCount) {
                         record[groupIndex] = record[groupIndex] + 1
@@ -324,8 +402,8 @@
                             }
                         }
                     }
-                    this.$set(this.students[index], 'groupCode', groupIndex+'')
-                    this.$set(this.students[index], 'groupName', '组别别名' + groupIndex)
+                    this.$set(this.classList[this.curClassIndex].students[index], 'groupId', groupIndex+'')
+                    this.$set(this.classList[this.curClassIndex].students[index], 'groupName', '组别别名' + groupIndex)
                     if (record[groupIndex] == maxCount) {
                         surplusCount--
                     }
@@ -336,7 +414,7 @@
                 }
                 setTimeout(() => {
                     this.tableLoading = false
-                }, 800)
+                }, 500)
             },
             getFirstChart(name) {
                 if (name) {
@@ -350,7 +428,7 @@
                 const params = {
                     title: this.columns.map(i => i.title),
                     key: this.columns.map(i => i.key),
-                    data: this.students,
+                    data: this.classList[this.curClassIndex].students,
                     autoWidth: true,
                     filename: '学生名单'
                 }
@@ -363,53 +441,36 @@
                     'school_code': this.$store.state.userInfo.schoolCode,
                     'teacher.id': this.$store.state.userInfo.TEAMModelId
                 }
-                this.$api.schoolSetting.findClassInfo(params).then(
+                this.$api.schoolSetting.getClassroomStudent(params).then(
                     (res) => {
                         if (!res.error) {
                             this.classList = res.classrooms
                             if (this.classList.length > 0) {
                                 this.curClassCode = this.classList[0].id
-                                this.findStudent()
                             }
                         } else {
                             this.$Message.error('API error!')
-                            this.tableLoading = false
                         }
                     },
                     (err) => {
                         this.$Message.error('API error!')
-                        this.tableLoading = false
                     }
-                )
-            },
-            /**基础查询数据 */
-            findStudent() {
-                let params = {
-                    code: this.$store.state.userInfo.schoolCode,
-                    classroomCode: this.curClassCode
-                }
-                this.$api.stuAccount.findStudent(params).then(
-                    (res) => {
-                        if (!res.error) {
-                            this.students = res.result.data
-
-                        } else {
-                            this.$Message.error('API error!')
-                        }
+                ).finally(() => {
+                    setTimeout(() => {
                         this.tableLoading = false
-                    },
-                    (err) => {
-                        this.tableLoading = false
-                    }
-                )
-            },
+                    },500)
+                })
+            }
         },
         created() {
             this.findClass()
+            this.$nextTick(() => {
+                let dom = document.getElementById('table-wrap')
+                this.tableHeight = dom.offsetHeight - 30
+            })
         },
         mounted() {
-            let dom = document.getElementById('table-wrap')
-            this.tableHeight = dom.offsetHeight - 20
+            
         },
         computed: {
         }
@@ -435,4 +496,7 @@
     #add-group-box .ivu-input {
         background:#2B2B2E;
     }
+    .group-title-wrap .ivu-input[disabled], .group-title-wrap fieldset[disabled] .ivu-input {
+        cursor:pointer;
+    }
 </style>

+ 55 - 29
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">
@@ -35,19 +35,6 @@
                         </Button>
                     </div>
                 </div>
-                <!--<h2 class="content-title">课例附件</h2>
-            <div class="class-file-wrap">
-                <ul>
-                    <li>电子笔记:<a href="#">力的组成2020-08-09</a> </li>
-                    <li>课件:<a href="#">《力的组成》</a></li>
-                    <li>教案:暂无教案</li>
-                    <li>其他:<a href="#">《力的组成学习单》</a></li>
-                </ul>
-                <div class="upload-class-file-wrap" @click="uploadStatus = true">
-                    <Icon class="upload-class-file-icon" custom="iconfont icon-upload" />
-                    <span class="upload-class-file-text" style="">上传附件</span>
-                </div>
-            </div>-->
                 <!--上课内容-->
                 <!--<h2 class="content-title">上课内容</h2>-->
                 <div :class="moStatus ? 'class-content mouse-over-status':'class-content'" @mousemove="moStatus = true" @mouseleave="moStatus = false">
@@ -687,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) {
@@ -766,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;
     }

+ 146 - 99
TEAMModelOS/ClientApp/src/view/evaluation/bank/ExerciseList.vue

@@ -6,29 +6,30 @@
 			<div class="filter-item" v-show="!isShowSchoolBank">
 				<span class="filter-title">来源:</span>
 				<RadioGroup v-model="filterOrigin" type="button" @on-change="filterOriginChange">
-					<Radio :label="schoolCode" v-if="isShowSchoolBank">学校公用库</Radio>
+					<Radio :label="schoolCode" v-if="isShowSchoolBank">校本题库</Radio>
 					<Radio :label="userId">个人题库</Radio>
-					<Radio :label="schoolCode" v-if="!isShowSchoolBank">学校公用库</Radio>
+					<Radio :label="schoolCode" v-if="!isShowSchoolBank">校本题库</Radio>
 				</RadioGroup>
 			</div>
-			<div class="filter-item">
+			<div class="filter-item" v-show="isShowSchoolBank">
 				<span class="filter-title">学段:</span>
 				<RadioGroup v-model="filterPeriod" type="button" @on-change="filterPeriodChange">
-					<Radio v-for="(item,index) in periodList" :key="index" :label="index">{{ item.name }}</Radio>
+				    <Radio v-for="(item,index) in periodList" :key="index" :label="index">{{ item.name }}</Radio>
 				</RadioGroup>
 			</div>
-			<div class="filter-item">
+			<div class="filter-item" v-show="isShowSchoolBank">
 				<span class="filter-title">年级:</span>
 				<CheckboxGroup v-model="filterGrade" border @on-change="filterGradeChange">
 					<Checkbox lable="all">全部</Checkbox>
 					<Checkbox v-for="(item,index) in gradeList" :key="index" :label="item.id">{{ item.name }}</Checkbox>
 				</CheckboxGroup>
 			</div>
-			<div class="filter-item">
+			<div class="filter-item" v-show="isShowSchoolBank">
 				<span class="filter-title">科目:</span>
-				<RadioGroup v-model="filterSubject" type="button" @on-change="filterSubjectChange">
-					<Radio v-for="(item,index) in subjectList" :key="index" :label="index">{{ item.name }}</Radio>
-				</RadioGroup>
+				<CheckboxGroup v-model="filterSubject" border @on-change="filterSubjectChange">
+					<Checkbox lable="all">全部</Checkbox>
+					<Checkbox v-for="(item,index) in subjectList" :key="index" :label="item.id">{{ item.name }}</Checkbox>
+				</CheckboxGroup>
 			</div>
 			<div class="filter-item">
 				<span class="filter-title">题型:</span>
@@ -53,12 +54,12 @@
 				<span class="filter-title">层次:</span>
 				<CheckboxGroup v-model="filterField" border @on-change="filterFieldChange">
 					<Checkbox label="all">全部</Checkbox>
-					<Checkbox label="0">知识</Checkbox>
-					<Checkbox label="1">理解</Checkbox>
-					<Checkbox label="2">应用</Checkbox>
-					<Checkbox label="3">分析</Checkbox>
-					<Checkbox label="4">综合</Checkbox>
-					<Checkbox label="5">评鉴</Checkbox>
+					<Checkbox :label="1">知识</Checkbox>
+					<Checkbox :label="2">理解</Checkbox>
+					<Checkbox :label="3">应用</Checkbox>
+					<Checkbox :label="4">分析</Checkbox>
+					<Checkbox :label="5">综合</Checkbox>
+					<Checkbox :label="6">评鉴</Checkbox>
 				</CheckboxGroup>
 			</div>
 			<div class="filter-item">
@@ -73,20 +74,13 @@
 				</RadioGroup>
 			</div>
 		</div>
-		<!--<div class="ev-list-operation">
-            <span class="import-exercise">
-                <Button type="success" @click="goCreatePaper">挑题组卷</Button>
-                <Button type="info" @click="goCreateExercise">新建习题</Button>
-                <BaseImport></BaseImport>
-            </span>
-        </div>-->
 		<!-- 筛选部分结束 -->
 		<!-- 题目列表部分 -->
 		<div v-if="exerciseList.length === 0" class="no-data-text">
 			<img src="../../../assets/icon/no_data.svg" width="120" />
 			<span style="margin-top:15px;color:#808080">暂无数据</span>
 		</div>
-		<div class="content-wrap" v-else>
+		<div class="content-wrap" ref="mathJaxContainer" v-else>
 			<Loading :top="100" v-show="dataLoading" type="1" hideMask></Loading>
 			<div class="exercise-item" v-for="(item,index) of exerciseList" :key="index" @click="onQuestionToggle(index,item.id,$event)">
 				<!-- 题干部分 -->
@@ -141,8 +135,8 @@
 							<div class="item-explain-details">
 								<span v-if="!item.points.length">暂未绑定知识点</span>
 								<div v-else>
-									<span v-for="point in item.points" class="item-point-tag" :key="index">
-										<span v-if="allPointList.length">{{ allPointList.filter(i => i.id === point).length ? allPointList.filter(i => i.id === point)[0].name : '' }}</span>
+									<span v-for="(point,index) in item.points" class="item-point-tag" :key="index">
+										{{ point }}
 									</span>
 								</div>
 							</div>
@@ -171,8 +165,9 @@
 
 		<Modal v-model="editExerciseModal" class-name="edit-exercise-modal" width="1200px" footer-hide @on-visible-change="editModalChange"
 		 title="编辑习题">
-
-			<BaseEditExercise :exerciseItem="currentExercise" @onEditSuccess="onEditSuccess" refId="listEdit" ref="editRef"></BaseEditExercise>
+			<template v-if="periodList.length">
+				<BaseEditExercise :exerciseItem="currentExercise" @onEditSuccess="onEditSuccess" refId="listEdit" ref="editRef"></BaseEditExercise>
+			</template>
 
 			<div slot="footer">
 				<Button type="success">确认</Button>
@@ -199,6 +194,7 @@
 	</div>
 </template>
 <script>
+	import blobTool from '@/utils/blobTool.js'
 	import BaseImport from '../components/BaseImport'
 	import BaseChild from '../components/BaseChild'
 	import BaseEditExercise from '../components/BaseEditExercise'
@@ -237,7 +233,7 @@
 				filterSort: 'createTime',
 				filterPeriod: 0,
 				filterGrade: [false],
-				filterSubject: 0,
+				filterSubject: [false],
 				totalNum: 0,
 				isShowAnswer: true,
 				importLoading: false,
@@ -249,8 +245,8 @@
 				gradeList: [],
 				subjectList: [],
 				filterParams: {},
-				uploadUrl: '',
 				allPointList: [],
+				originData:[],
 				playAudioModal: false,
 				playVideoModal: false,
 				curAudioSrc: "",
@@ -260,6 +256,7 @@
 			}
 		},
 		created() {
+			this.isShowSchoolBank = this.$route.name === 'schoolBank'
 			this.$EventBus.$on('showSchoolBank',(val)=> {
 				if(val){
 					this.isShowSchoolBank = val
@@ -268,7 +265,8 @@
 				}
 			})
 			this.getSchoolInfo()
-			this.uploadUrl = 'https://' + window.location.host + '/api/ImportExercise/uploadWord'
+			// this.getBlobOrigin()
+			
 		},
 		methods: {
 			
@@ -290,7 +288,7 @@
 			onRichTextClick(e) {
 				// e.stopPropagation()
 				let sasString = JSON.parse(decodeURIComponent(localStorage.getItem('user_profile'))).blob_sas
-				console.log(JSON.parse(decodeURIComponent(localStorage.getItem('school_profile'))))
+				// console.log(JSON.parse(decodeURIComponent(localStorage.getItem('school_profile'))))
 				if (e.srcElement.classList[0] === 'richText-audio') {
 					this.playAudioModal = true
 					this.curAudioSrc = e.srcElement.dataset.url
@@ -306,18 +304,39 @@
 			
 			/** 获取区班校信息 */
 			getSchoolInfo() {
+				this.dataLoading = true
 				this.$store.dispatch('schoolBaseInfo/getSchoolBaseData').then(res => {
 					console.log(res)
 					this.schoolInfo = JSON.parse(JSON.stringify(res.data))
 					this.schoolCode = res.data.id
 					this.userId = this.$store.state.userInfo.TEAMModelId
-					this.filterOrigin = this.$route.name === 'personalBank' ? this.$store.state.userInfo.TEAMModelId : this.$store.state.userInfo.schoolCode
+					this.filterOrigin = this.isShowSchoolBank ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId
 					this.periodList = res.data.period
 					this.gradeList = res.data.period[0].grades
 					this.subjectList = res.data.period[0].subjects
 					this.doFilter()
 				})
 			},
+			
+			
+			/* 获取BLOB所有试题LIST */
+			async getBlobOrigin(){
+				// 获取初始化Blob需要的数据
+				let sasData = this.isShowSchoolBank ? await this.$tools.getSchoolSas() : await this.$tools.getPrivateSas()
+				//初始化Blob
+				let containerClient = new blobTool(sasData.url, sasData.name, sasData.sas , this.isShowSchoolBank ? 'school' : 'private')
+				// 等待blob的返回结果
+				containerClient.listBlob({
+				    prefix: 'paper'
+				}).then(
+				    (res) => {
+				        console.log(res)
+				    },
+				    (err) => {
+				        this.$Message.error('API Error')
+				    }
+				)
+			},
 
 			/** 执行筛选条件获取数据 */
 			doFilter() {
@@ -325,34 +344,31 @@
 				this.collapseList = [] // 所有详情都收起来
 				/** 定义查询接口的参数规格 */
 				this.filterParams = {
-					'@CURRPAGE': this.pageNum,
-					'@PAGESIZE': this.pageSize,
+					// '@CURRPAGE': this.pageNum,
+					// '@PAGESIZE': this.pageSize,
 					'@DESC': this.filterSort,
 					'code': this.filterOrigin,
-					'periodId': [this.periodList[this.filterPeriod].id],
-					'gradeIds[*]': this.deleteFalse(this.filterGrade),
-					'subjectId': [this.periodList[this.filterPeriod].subjects[this.filterSubject].id],
+					'periodId': this.isShowSchoolBank ?  [this.periodList[this.filterPeriod].id] : [],
+					'gradeIds[*]': this.isShowSchoolBank ?  this.deleteFalse(this.filterGrade) : [],
+					'subjectId': this.isShowSchoolBank ?  this.deleteFalse(this.filterSubject) : [],
 					'level': this.deleteFalse(this.filterDiff),
 					'type': this.deleteFalse(this.filterType),
-					'field': this.deleteFalse(this.filterField)
+					'field': this.deleteFalse(this.filterField),
+					'scope': this.curScope,
 				}
-
-				/** 查询总数参数 */
 				let findCountParams = {
 					'collectionName': 'ItemInfo',
 					'queryDict': {
 						'code': this.filterOrigin,
-						'periodId': [this.periodList[this.filterPeriod].periodCode],
+						'periodId': [this.periodList[this.filterPeriod].id],
 						'gradeIds[*]': this.deleteFalse(this.filterGrade),
-						'subjectId': [this.periodList[this.filterPeriod].subjects[this.filterSubject].id],
+						'subjectId': this.deleteFalse(this.filterSubject),
 						'level': this.deleteFalse(this.filterDiff),
 						'type': this.deleteFalse(this.filterType),
 						'field': this.deleteFalse(this.filterField)
 					}
 				}
-
 				this.getExerciseList(this.filterParams)
-				this.getResultCount(findCountParams)
 			},
 
 			/**
@@ -363,36 +379,16 @@
 				let that = this
 				this.$api.newEvaluation.FindExerciseList(data).then(async res => {
 					let list = res.items
-					let result = []
+					this.originData = res.items
+					/* 获取试题总数 */
+					this.totalNum = res.items.length
 					/* 查找当前页面所有知识点ID换名称 */
-					this.getPointsByIds(this.getPointIds(list)).then(res => {
-						this.allPointList = res
-					})
-					
-					let privateSas = await this.$tools.getPrivateSas()
-					let schoolSas = await this.$tools.getSchoolSas()
-					/* 拿到Summary的题目之后要通过每个题目的JSON URL 换取完整题目数据 */
-					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]
-								console.log(jsonData.fileName)
-								this.$set(list, index, jsonData)
-								
-								this.$nextTick(()=>{
-									window.MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
-								})
-							}catch(e){
-								this.$Message.warning('存在试题读取异常!')
-							}
-							
-						}
-					})
+					// this.getPointsByIds(this.getPointIds(list)).then(res => {
+					// 	this.allPointList = res
+					// })
 					this.exerciseList = list
+					this.originData = list
+					this.pageChange(1)
 
 					setTimeout(() => {
 						that.dataLoading = false
@@ -425,7 +421,7 @@
 			},
 
 			editModalChange(val) {
-				//this.$refs.editModal.backToTop()
+				this.$refs.editRef.backToTop()
 			},
 
 			/**
@@ -504,8 +500,8 @@
 				this.gradeList = this.schoolInfo.period[val].grades
 				this.subjectList = this.schoolInfo.period[val].subjects
 				this.filterGrade = [false]
-				this.filterSubject = 0
-				this.pageChange(1)
+				this.filterSubject = [false]
+				this.doFilter()
 			},
 
 			/**
@@ -513,12 +509,12 @@
 			 * @param val
 			 */
 			filterGradeChange(val) {
-				if (val !== [false] && val.indexOf(false) === 0) {
+				if (val.length > 1 && val.indexOf(false) === 0) {
 					this.filterGrade.splice(val.indexOf(false), 1)
-				} else if (val.indexOf(false) > 0) {
+				} else if (val.indexOf(false) > -1 || val.length === 0) {
 					this.filterGrade = [false]
 				}
-				this.pageChange(1)
+				this.doFilter()
 			},
 
 			/**
@@ -526,7 +522,12 @@
 			 * @param val
 			 */
 			filterSubjectChange(val) {
-				this.pageChange(1)
+				if (val.length > 1 && val.indexOf(false) === 0) {
+					this.filterSubject.splice(val.indexOf(false), 1)
+				} else if (val.indexOf(false) > -1 || val.length === 0) {
+					this.filterSubject = [false]
+				}
+				this.doFilter()
 			},
 
 			/**
@@ -535,7 +536,7 @@
 			 */
 			filterOriginChange(origin) {
 				this.filterOrigin = origin
-				this.pageChange(1)
+				this.doFilter()
 			},
 
 			/**
@@ -543,12 +544,12 @@
 			 * @param val
 			 */
 			filterTypeChange(val) {
-				if (val !== ['all'] && val.indexOf('all') === 0) {
+				if (val.length > 1 && val.indexOf('all') === 0) {
 					this.filterType.splice(val.indexOf('all'), 1)
-				} else if (val.indexOf('all') > 0) {
+				} else if (val.indexOf('all') > -1 || val.length === 0) {
 					this.filterType = ['all']
 				}
-				this.pageChange(1)
+				this.doFilter()
 			},
 
 			/**
@@ -556,12 +557,12 @@
 			 * @param val
 			 */
 			filterDiffChange(val) {
-				if (val !== ['all'] && val.indexOf('all') === 0) {
+				if (val.length > 1 && val.indexOf('all') === 0) {
 					this.filterDiff.splice(val.indexOf('all'), 1)
-				} else if (val.indexOf('all') > 0) {
+				} else if (val.indexOf('all') > -1 || val.length === 0) {
 					this.filterDiff = ['all']
 				}
-				this.pageChange(1)
+				this.doFilter()
 			},
 
 			/**
@@ -569,12 +570,12 @@
 			 * @param val
 			 */
 			filterFieldChange(val) {
-				if (val !== ['all'] && val.indexOf('all') === 0) {
+				if (val.length > 1 && val.indexOf('all') === 0) {
 					this.filterField.splice(val.indexOf('all'), 1)
-				} else if (val.indexOf('all') > 0) {
+				} else if (val.indexOf('all') > -1 || val.length === 0) {
 					this.filterField = ['all']
 				}
-				this.pageChange(1)
+				this.doFilter()
 			},
 
 			/**
@@ -582,7 +583,7 @@
 			 * @param val
 			 */
 			filterSortChange(val) {
-				this.pageChange(1)
+				this.doFilter()
 			},
 
 			/**
@@ -615,10 +616,24 @@
 			 * 切换页码操作
 			 * @param page
 			 */
-			pageChange(page) {
+			async pageChange(page) {
 				this.pageNum = page
-				this.doFilter()
-				this.pageScrollTo(0)
+				let start = this.pageSize * (page - 1)
+				let end = this.pageSize * page
+				// 拿到当前页码需要展示的数据
+				let simpleList = this.originData.slice(start, end)
+				try{
+					// 执行试题换取完整JSON数据
+					this.exerciseList = await this.$evTools.getFullItem(simpleList)
+					this.currentPage = page
+					this.pageScrollTo(0)
+					// 公式渲染
+					this.$nextTick(()=>{
+						window.MathJax.Hub.Queue(["Typeset", MathJax.Hub,this.$refs.mathJaxContainer]);
+					})
+				}catch(e){
+					console.log(e)
+				}
 			},
 
 			/**
@@ -676,11 +691,14 @@
 						this.dataLoading = true
 						this.$api.newEvaluation.DeleteExamItem({
 							id: item.id,
-							pk: item.code
+							code: item.code,
+							scope: item.scope
 						}).then(res => {
 							if (!res.error) {
-								this.$Message.success('删除成功')
-								this.pageChange(1)
+								this.onDeleteBlobItem(item.blob).then(e => {
+									this.$Message.success('删除成功')
+									this.doFilter()
+								})
 							} else {
 								this.$Message.warning('删除失败,错误代码:' + res.error.code + ',错误信息:' + res.error.message)
 							}
@@ -691,13 +709,31 @@
 					}
 				})
 			},
+			
+			/* 删除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')
+					    }
+					)
+				})
+			},
 
 			/**
 			 * 编辑单个试题
 			 * @param item
 			 */
 			handleEdit(exercise) {
-				exercise.options = exercise.option
 				exercise.level = exercise.level || 1
 				this.currentExercise = JSON.parse(JSON.stringify(exercise))
 				this.editExerciseModal = true
@@ -705,7 +741,12 @@
 
 			/** 编辑成功 */
 			onEditSuccess(item) {
-				this.$api.newEvaluation.SaveSingleExercise(item).then(res => {
+				console.log('编辑好要保存的试题')
+				console.log(item)
+				this.$api.newEvaluation.SaveSingleExercise({
+					itemInfo:item,
+					option:'update'
+				}).then(res => {
 					this.$Message.success("修改成功!")
 					this.$refs.editRef.isLoading = false
 					this.editExerciseModal = false
@@ -746,13 +787,19 @@
 
 		},
 		mounted() {
-			
+			// 公式渲染
+			this.$nextTick(()=>{
+				window.MathJax.Hub.Queue(["Typeset", MathJax.Hub,this.$refs.mathJaxContainer]);
+			})
 		},
 		computed: {
 			headers() {
 				let hd = {}
 				hd['Authorization'] = 'Bearer ' + localStorage.getItem('token')
 				return hd
+			},
+			curScope(){
+				return this.filterOrigin === this.$store.state.userInfo.schoolCode ? 'school' : 'private'
 			}
 		}
 	}

+ 1 - 1
TEAMModelOS/ClientApp/src/view/evaluation/bank/TestPaperList.less

@@ -121,9 +121,9 @@
         height: 90px;
         background: White;
         margin-top: 15px;
+		padding-left: 10px;
         .fl-col-center;
         align-items: flex-start;
-        border-radius: 5px;
         cursor: pointer;
 
         &-name {

+ 91 - 97
TEAMModelOS/ClientApp/src/view/evaluation/bank/TestPaperList.vue

@@ -3,16 +3,6 @@
         <!-- 条件筛选部分 -->
         <BaseFilter @onChange="onFilterChange" :isFilterPaper="isFilterPaper"></BaseFilter>
 
-        <!-- 操作条 -->
-        <!--<div class="ev-list-operation">
-            <Checkbox style="visibility:hidden"></Checkbox>
-            <span class="import-exercise">
-                <Button type="success" @click="goPickExercises">手动挑题</Button>
-                <Button type="info" @click="randomModal = true">随机挑题</Button>
-                <BaseImport></BaseImport>
-            </span>
-        </div>-->
-
         <!-- 空数据展示 -->
         <div v-if="paperList.length === 0" class="no-data-text">
             <img src="@/assets/icon/no_data.svg" width="120" />
@@ -21,17 +11,17 @@
 
         <!-- 试卷列表页面 -->
         <div class="pl-content-wrap" v-else>
-            <Loading :top="100" v-show="dataLoading" type="1"></Loading>
+            <Loading :top="100" v-show="dataLoading" type="1" hideMask></Loading>
             <div class="paper-item" v-for="(paper,index) in paperList" :key="index">
                 <div class="paper-item-name">
-                    <span class="paper-item-tag">{{ getSubjectName(paper.subjectCode) }}</span>
-                    <span>{{ paper.name }}</span>
+                    <span class="paper-item-tag"  v-if="isSchool">{{ getSubjectName(paper.subjectId) }}</span>
+                    <span style="margin-left: 8px;">{{ paper.name }}</span>
                 </div>
                 <div class="paper-item-info">
-                    <span class="info-item">适用学段:<span class="info-bold">{{ getPeriodName(paper.periodCode) }}</span></span>
-                    <span class="info-item">适用年级:<span class="info-bold" v-for="(grade,gIndex) in paper.gradeCode" :key="gIndex">{{ getGradeName(grade) }} <span v-show="gIndex !== paper.gradeCode.length - 1"> / </span></span></span>
-                    <span class="info-item">题量:<span class="info-bold">{{ paper.item ? paper.item.length : 0 }}</span></span>
-                    <span class="info-item">难度系数:<span class="info-bold">{{ paper.item ? handleDiffCalc(paper.item) : 0 }}</span></span>
+                    <span class="info-item" v-if="isSchool">适用学段:<span class="info-bold">{{ getPeriodName(paper.periodId) }}</span></span>
+                    <span class="info-item" v-if="isSchool">适用年级:<span class="info-bold" v-for="(grade,gIndex) in paper.gradeIds" :key="gIndex">{{ getGradeName(paper.periodId,grade) }} <span v-show="gIndex !== paper.gradeIds.length - 1"> / </span></span></span>
+                    <span class="info-item">题量:<span class="info-bold">{{ paper.scoring ? paper.scoring.length : 0 }}</span></span>
+                    <!-- <span class="info-item">难度系数:<span class="info-bold">{{ paper.item ? handleDiffCalc(paper.item) : 0 }}</span></span> -->
                 </div>
                 <div class="paper-item-tools">
                     <span class="paper-item-tools-edit" @click="goToPaper(paper)"  v-if="$access.can('admin.*|Paper_Edit')">
@@ -55,27 +45,10 @@
                   @on-change="pageChange"
                   :page-size-opts="[5,10,15,20]" />
         </div>
-
-        <!-- 随机挑题组卷 -->
-        <Modal v-model="randomModal"
-               title="随机挑题"
-               footer-hide
-               class-name="random-pick-modal"
-               width="1000px">
-            <Form :model="paperInfo" label-position="top">
-                <FormItem label="试卷名称">
-                    <Input v-model="paperInfo.name" placeholder="请输入新建试卷名称,必填项"></Input>
-                </FormItem>
-                <FormItem label="设置配分">
-                    <InputNumber :max="100" :min="1" v-model="paperInfo.score" value="100"></InputNumber>
-                </FormItem>
-            </Form>
-            <AutoCreate @getExercises="autoQuestions"
-                        @goToPreview="goCreatePaper"></AutoCreate>
-        </Modal>
     </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'
@@ -86,6 +59,7 @@
         },
         data() {
             return {
+				containerClient:null,
                 schoolCode: '',
                 totalNum: 0,
                 pageSize: 5,
@@ -118,19 +92,18 @@
             doFilter() {
                 this.dataLoading = true
                 this.getPaperList(this.filterParams)
-                this.getResultCount(this.findCountParams)
+                // this.getResultCount(this.findCountParams)
             },
 
             onFilterChange(filterParams, findCountParams) {
 
                 this.filterParams = {
-                    '@CURRPAGE': this.pageNum,
-                    '@PAGESIZE': this.pageSize,
-                    '@DESC': this.filterSort,
+                    // '@DESC': this.filterSort,
                     'code': filterParams.code,
-                    'periodCode': filterParams.periodCode,
-                    'gradeCode': filterParams.gradeCode,
-                    'subjectCode': filterParams.subjectCode
+					'scope': filterParams.code === this.$store.state.userInfo.schoolCode ? 'school' : 'private',
+                    'periodId': filterParams.periodId[0],
+                    'gradeIds': filterParams.gradeIds,
+                    'subjectId': filterParams.subjectId
                 }
                 this.findCountParams = findCountParams
 
@@ -143,22 +116,7 @@
             getPaperList(params) {
                 let that = this
                 this.$api.learnActivity.FindExamPaper(params).then(async res => {
-					let list = res.papers
-					let privateSas = await this.$tools.getPrivateSas()
-					let schoolSas = await this.$tools.getSchoolSas()
-					/* 拿到Summary的题目之后要通过每个题目的JSON URL 换取完整题目数据 */
-					list.forEach(async (i, index) => {
-						if (i.url) {
-							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]
-							console.log(jsonData.fileName)
-							this.$set(list, index, jsonData)
-						}
-					})
-                    this.paperList = list
+                    this.paperList = res.papers
                     setTimeout(() => {
                         that.dataLoading = false
                     }, 1000)
@@ -174,15 +132,23 @@
              * 点击查看试卷详情
              * @param paper
              */
-            goToPaper(paper) {
-                paper.paperName = paper.name
-                paper.paperScore = paper.score
-                this.$router.push({
-                    name: 'createPaper',
-                    params: {
-                        paper: paper
-                    }
-                })
+            async goToPaper(paper) {
+				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('获取试卷数据失败!请稍后再试!')
+				}
+				
             },
 
             /**
@@ -205,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, pk: item.code }).then(res => {
-                            if (!res.error && res.result.data) {
-                                this.$Message.success('删除成功')
-                                this.pageChange(1)
+                        this.$api.learnActivity.DeleteExamPaper({ id: item.id, code: item.code,scope:item.scope }).then(async res => {
+                            if (!res.error) {
+								this.onDeleteBlobPaper(files).then(r => {
+									this.$Message.success('删除成功')
+									this.pageChange(1)
+								})
                             } else {
                                 this.$Message.warning('删除失败,错误代码:' + res.error.code + ',错误信息:' + res.error.message)
                             }
@@ -221,24 +191,46 @@
                     }
                 })
             },
-
-            autoQuestions(res) {
-                if (this.paperInfo.name) {
-                    let items = []
-                    res.forEach(item => {
-                        items = items.concat(item.item)
-                    })
-                    console.log(res)
-                    this.paperInfo.item = items
-                    this.paperInfo.code = this.filterParams.code,
-                        this.paperInfo.periodCode = this.filterParams.periodCode[0],
-                        this.paperInfo.subjectCode = this.filterParams.subjectCode[0]
-
-                    this.goCreatePaper()
-                } else {
-                    this.$Message.warning("请先输入试卷名称!")
-                }
-            },
+			
+			 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')
+					    }
+					)
+				})
+				
+			},
 
             /**
             * 切换页码操作
@@ -246,8 +238,6 @@
             */
             pageChange(page) {
                 this.pageNum = page
-                this.filterParams['@CURRPAGE'] = page
-                this.filterParams['@PAGESIZE'] = this.pageSize
                 this.doFilter()
             },
 
@@ -311,15 +301,15 @@
              * @param code
              */
             getPeriodName(code) {
-                return this.$jsFn.getPeriod(this.$store.state.schoolBaseInfo.schoolBaseInfo, code).periodName
+                return this.$jsFn.getPeriod(this.$store.state.schoolBaseInfo.schoolBaseInfo, code).name
             },
 
             /**
-             * 根据SubjectCode换取SubjectName
+             * 根据GradeCode换取GradeName
              * @param code
              */
-            getGradeName(code) {
-                return this.$jsFn.getGradeName(this.$store.state.schoolBaseInfo.schoolBaseInfo, code)
+            getGradeName(periodId,code) {
+                return this.$store.state.schoolBaseInfo.schoolBaseInfo.period.filter(i => i.id === periodId)[0].grades.filter(j => j.id === code)[0].name
             },
 
             /**
@@ -341,7 +331,10 @@
                 let hd = {}
                 hd['Authorization'] = 'Bearer ' + localStorage.getItem('token')
                 return hd
-            }
+            },
+			isSchool(){
+				return this.filterParams.scope === 'school' ? true : false
+			}
         }
     }
 </script>
@@ -354,6 +347,7 @@
         flex-direction: row;
         justify-content: center;
         margin: 20px 0;
+		color: #fff;
     }
 
     .pl-container .ivu-checkbox-inner {

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

@@ -1,10 +1,10 @@
 <template>
 	<div class="bank-container">
 		<Tabs :value="tabName" name="listTab" @on-click="onTabClick">
-			<TabPane label="试题" name="exercise" tab="listTab">
+			<TabPane label="试题" name="exercise" tab="listTab">
 				<ExerciseList ref="exList" @toggleChange="onToggleChange"></ExerciseList>
 			</TabPane>
-			<TabPane label="试卷" name="paper" tab="listTab">
+			<TabPane label="试卷" name="paper" tab="listTab">
 				<PaperList></PaperList>
 			</TabPane>
 		</Tabs>
@@ -61,14 +61,20 @@
 			/** 返回创建试题页面 */
 			goCreateExercise() {
 				this.$router.push({
-					name: 'createExercises'
+					name: 'createExercises',
+					params:{
+						scope : this.$route.name === 'personalBank' ? 'private' : 'school'
+					}
 				})
 			},
 
 			/** 前往组卷页面 */
 			goCreatePaper() {
 				this.$router.push({
-					name: 'createPaper'
+					name: 'createPaper',
+					params:{
+						scope : this.$route.name === 'personalBank' ? 'private' : 'school'
+					}
 				})
 			},
 		},
@@ -85,7 +91,6 @@
 				this.tabName = this.$route.params.tabName
 			}
 			
-			console.log(this.$route)
 			if(this.$route.name === 'schoolBank'){
 				this.$EventBus.$emit('showSchoolBank',true)
 			}else{

+ 34 - 43
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseCreateChild.vue

@@ -25,11 +25,11 @@
 			<div class="exersices-attr my-radio-style">
 				<IconText :text="'选择题型'" :color="'#00b8ff'" :icon="'md-pricetags'"></IconText>
 				<RadioGroup v-model="exersicesType" type="button" @on-change="typeChange">
-					<Radio label="Single" :disabled="isEdit">单选</Radio>
-					<Radio label="Multiple" :disabled="isEdit">多选</Radio>
-					<Radio label="Judge" :disabled="isEdit">判断</Radio>
-					<Radio label="Complete" :disabled="isEdit">填空</Radio>
-					<Radio label="Subjective" :disabled="isEdit">问答</Radio>
+					<Radio label="single" :disabled="isEdit">单选</Radio>
+					<Radio label="multiple" :disabled="isEdit">多选</Radio>
+					<Radio label="judge" :disabled="isEdit">判断</Radio>
+					<Radio label="complete" :disabled="isEdit">填空</Radio>
+					<Radio label="subjective" :disabled="isEdit">问答</Radio>
 				</RadioGroup>
 			</div>
 			<div class="exersices-attr edit-exersices-attr-diff my-radio-style">
@@ -44,11 +44,11 @@
 			</div>
 		</div>
 
-		<BaseSingle v-if="exersicesType==='Single'" ref="single" :editItem="editInfo"></BaseSingle>
-		<BaseMultiple v-else-if="exersicesType==='Multiple'" ref="multiple" :editInfo="editInfo"></BaseMultiple>
-		<BaseJudge v-else-if="exersicesType==='Judge'" ref="judge" :editInfo="editInfo"></BaseJudge>
-		<BaseCompletion v-else-if="exersicesType==='Complete'" ref="complete" :editInfo="editInfo"></BaseCompletion>
-		<BaseSubjective v-else-if="exersicesType==='Subjective'" ref="subjective" :editInfo="editInfo"></BaseSubjective>
+		<BaseSingle v-if="exersicesType==='single'" ref="single" :editInfo="editInfo"></BaseSingle>
+		<BaseMultiple v-else-if="exersicesType==='multiple'" ref="multiple" :editInfo="editInfo"></BaseMultiple>
+		<BaseJudge v-else-if="exersicesType==='judge'" ref="judge" :editInfo="editInfo"></BaseJudge>
+		<BaseCompletion v-else-if="exersicesType==='complete'" ref="complete" :editInfo="editInfo"></BaseCompletion>
+		<BaseSubjective v-else-if="exersicesType==='subjective'" ref="subjective" :editInfo="editInfo"></BaseSubjective>
 
 		<!-- 解析的富文本部分 -->
 		<div class="exersices-analysis">
@@ -60,7 +60,7 @@
 
 		<!-- 补救的富文本部分 -->
 		<!-- 补救的富文本部分 -->
-		<div class="exersices-analysis" v-show="exersicesType !== 'Compose'">
+		<div class="exersices-analysis" v-show="exersicesType !== 'compose'">
 			<IconText :text="'补救资源'" :color="'#2892DD'" :icon="'md-link'" style="margin-bottom:10px"></IconText>
 			<BaseRepair ref="repairRef" :datas="editInfo.repairResource"></BaseRepair>
 		</div>
@@ -104,7 +104,6 @@
 	</div>
 </template>
 <script>
-	import uploadFile from '@/utils/upload.js'
 	import 'videojs-contrib-hls.js/src/videojs.hlsjs'
 	import IconText from '@/components/evaluation/IconText.vue'
 	import BaseSingle from '@/view/evaluation/types/BaseSingle.vue'
@@ -119,7 +118,7 @@
 	// 默认创建题目模板
 	const defaultExercise = {
 		question: '',
-		options: [],
+		option: [],
 		level: 1,
 		answer: [],
 		explain: '',
@@ -166,7 +165,7 @@
 				editInfo: {},
 				schoolInfo: {},
 				saveLoading: false,
-				exersicesType: 'Single',
+				exersicesType: 'single',
 				exerciseField: 0,
 				exercisePeriod: 0,
 				exerciseGrade: [],
@@ -251,7 +250,7 @@
 			async getContent(type) {
 				let exerciseItem = Object.assign({}, defaultExercise)
 				switch (type) {
-					case 'Single':
+					case 'single':
 						console.log(this.$refs.single)
 						exerciseItem.question = this.$refs.single.stemContent
 						exerciseItem.option = ((this.$refs.single.optionsContent.length === this.$refs.single.options.length) && this.checkOptionNull(
@@ -262,7 +261,7 @@
 						exerciseItem.answer = [String.fromCharCode(64 + parseInt(this.$refs.single.trueIndex + 1))]
 
 						break
-					case 'Multiple':
+					case 'multiple':
 						exerciseItem.question = this.$refs.multiple.stemContent
 						exerciseItem.option = ((this.$refs.multiple.optionsContent.length === this.$refs.multiple.options.length) &&
 							this.checkOptionNull(this.$refs.multiple.optionsContent)) ? this.$refs.multiple.optionsContent : null
@@ -271,7 +270,7 @@
 						exerciseItem.explain = this.analysisContent
 						exerciseItem.answer = this.$refs.multiple.transferArr
 						break
-					case 'Judge':
+					case 'judge':
 						exerciseItem.question = this.$refs.judge.stemContent
 						exerciseItem.option = []
 						exerciseItem.type = this.exersicesType
@@ -279,7 +278,7 @@
 						exerciseItem.explain = this.analysisContent
 						exerciseItem.answer = [this.$refs.judge.trueAnswer]
 						break
-					case 'Complete':
+					case 'complete':
 						exerciseItem.question = this.$refs.complete.stemContent
 						exerciseItem.option = []
 						exerciseItem.type = this.exersicesType
@@ -287,7 +286,7 @@
 						exerciseItem.explain = this.analysisContent
 						exerciseItem.answer = this.$refs.complete.optionsContent.map(item => item.value)
 						break
-					case 'Subjective':
+					case 'subjective':
 						exerciseItem.question = this.$refs.subjective.stemContent
 						exerciseItem.option = []
 						exerciseItem.type = this.exersicesType
@@ -298,8 +297,8 @@
 				}
 				exerciseItem.repair = this.repairContent
 				exerciseItem.repairResource = this.formatRepairResource(this.$refs.repairRef.datas)
-				exerciseItem.field = this.fieldList[this.exerciseField]
-				exerciseItem.points = this.exercisePoints.map(item => item.id)
+				exerciseItem.field = this.exerciseField + 1
+				exerciseItem.points = this.exercisePoints
 				exerciseItem.code = this.$parent.$parent.exerciseScope === 0 ? this.$store.state.userInfo.TEAMModelId : this.$store
 					.state.userInfo.schoolCode
 
@@ -327,14 +326,14 @@
 			 */
 			onDeletePoint(index) {
 				this.exercisePoints.splice(index, 1)
-				this.$refs.pointRef.$children[1].onDeletePoint(index)
+				// this.$refs.pointRef.$children[1].onDeletePoint(index)
 			},
 
 			// 题目类型转换
 			typeChange(val) {
 				if (this.isEdit) {
 					this.$Message.warning('暂不支持更换题型!')
-				} else if (val === 'Compose') {
+				} else if (val === 'compose') {
 					this.$router.push({
 						name: 'createCompose'
 					})
@@ -350,7 +349,7 @@
 				this.exerciseField = 0
 				this.exercisePoints = []
 				this.exersicesDiff = '1'
-				this.exersicesType = 'Single'
+				this.exersicesType = 'single'
 				this.analysisEditor.txt.clear()
 				// this.repairEditor.txt.clear()
 				this.editInfo = null
@@ -432,13 +431,13 @@
 			// 根据不同题型 给出需要必填选项
 			getWhiteListByType(type) {
 				switch (type) {
-					case 'Single':
+					case 'single':
 						return ['question', 'option', 'answer']
 						break;
-					case 'Multiple':
+					case 'multiple':
 						return ['question', 'option', 'answer']
 						break;
-					case 'Complete':
+					case 'complete':
 						return ['question', 'answer']
 						break;
 					default:
@@ -472,18 +471,15 @@
 
 			// 渲染编辑习题内容回显
 			async renderExercise(editItem) {
-				console.log('render')
 				let schoolInfo = this.$store.state.schoolBaseInfo.schoolBaseInfo
 				this.isEdit = true
-				if (!editItem.options) editItem.options = editItem.option
 				this.exersicesDiff = editItem.level.toString() || '0'
 				this.exerciseScope = editItem.code === this.$store.state.userInfo.TEAMModelId ? 0 : 1
 				this.exersicesType = editItem.type
-				this.exerciseField = this.fieldList.indexOf(editItem.field)
-				this.exercisePoints = await this.getPointsByIds(editItem.points)
+				this.exerciseField = editItem.field - 1
+				this.exercisePoints = editItem.points
 
 				if (editItem.level) {
-					console.log(document.getElementById(this.refId))
 					let ac = document.getElementById(this.refId).getElementsByClassName('edit-exersices-attr-diff')[0].children[1].children
 					for (let i = 0; i < ac.length; i++) {
 						ac[i].style.background = '#fff'
@@ -511,10 +507,9 @@
 				this.relateFileList = editItem.repairResource || []
 				this.optionsContent = editItem.option
 				this.analysisContent = editItem.explain
-				// this.repairContent = editItem.repair
 				this.analysisEditor.txt.html(editItem.explain)
-				// this.repairEditor.txt.html(editItem.repair)
-
+				console.log('渲染的小题数据')
+				console.log(editItem)
 				this.editInfo = editItem
 			}
 		},
@@ -537,14 +532,10 @@
 				analysisEditor.create()
 			this.analysisEditor = analysisEditor
 
-
-			// let repairEditor = new E(this.$refs.repairEditor)
-			// repairEditor.customConfig = this.defaultConfig
-			// repairEditor.customConfig.onchange = (html) => {
-			// 		this.repairContent = html
-			// 	},
-			// 	repairEditor.create()
-			// this.repairEditor = repairEditor
+			
+			if (this.editItem && this.editItem.id) {
+				this.renderExercise(JSON.parse(JSON.stringify(editItem)))
+			}
 		},
 		watch: {
 			editItem: {

+ 74 - 68
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseEditExercise.vue

@@ -1,6 +1,6 @@
 <template>
-	<div class="ev-container component-ev-container" :id="refId">
-		<div class="display-flex">
+	<div class="ev-container component-ev-container" :id="refId" ref="editContainer">
+		<div class="display-flex" v-if="isSchool">
 			<div class="exersices-attr my-radio-style">
 				<IconText :text="'选择学段'" :color="'#00b8ff'" :icon="'md-school'"></IconText>
 				<Select v-model="exercisePeriod" @on-change="onPeriodChange">
@@ -38,7 +38,7 @@
 				<Button type="info" style="margin-top:20px" @click="selectPointsModal = true" v-if="exercisePoints.length === 0">选择知识点</Button>
 				<div v-else style="margin-top:10px">
 					<span v-for="(item,index) in exercisePoints" :key="index" class="exercise-item-point">
-						{{item.name}}
+						{{item}}
 						<span class="exercise-item-point-close">
 							<Icon type="md-close" @click="onDeletePoint(index)" /></span>
 					</span>
@@ -71,7 +71,7 @@
 			</div>
 		</div>
 
-		<BaseSingle v-if="exersicesType==='single'" ref="single" :editItem="editInfo"></BaseSingle>
+		<BaseSingle v-if="exersicesType==='single'" ref="single" :editInfo="editInfo"></BaseSingle>
 		<BaseMultiple v-else-if="exersicesType==='multiple'" ref="multiple" :editInfo="editInfo"></BaseMultiple>
 		<BaseJudge v-else-if="exersicesType==='judge'" ref="judge" :editInfo="editInfo"></BaseJudge>
 		<BaseCompletion v-else-if="exersicesType==='complete'" ref="complete" :editInfo="editInfo"></BaseCompletion>
@@ -89,7 +89,7 @@
 		<!-- 补救的富文本部分 -->
 		<div class="exersices-analysis" v-show="exersicesType !== 'compose'">
 			<IconText :text="'补救资源'" :color="'#2892DD'" :icon="'md-link'" style="margin-bottom:10px"></IconText>
-			<BaseRepair ref="repairRef" :datas="editInfo.repairResource"></BaseRepair>
+			<BaseRepair ref="repairRef" :datas="editInfo ? editInfo.repairResource : []"></BaseRepair>
 		</div>
 
 		<!-- 小题展示区域 -->
@@ -105,7 +105,7 @@
 
 		<Modal v-model="selectPointsModal" title="选择知识点" ref="editPointRef" width="600px" style="z-index:99999">
 			<BasePoints v-if="selectPointsModal" :period="schoolInfo.period[exercisePeriod].id" :subject="subjectList[exerciseSubject].id"
-			 @onCheckChange="onCheckChange" ref="pointRef"></BasePoints>
+			 @onCheckChange="onCheckChange" :points="exercisePoints" ref="pointRef"></BasePoints>
 			<!--<CreateNewChild v-if="isLoadModal" ref="newChild" :isChildEdit="isChildEdit" :editItem="editChild"></CreateNewChild>-->
 			<div slot="footer">
 				<Button type="text" @click="onConfirm">取消</Button>
@@ -143,7 +143,7 @@
 	</div>
 </template>
 <script>
-	import uploadFile from '@/utils/upload.js'
+	import blobTool from '@/utils/blobTool.js'
 	import 'videojs-contrib-hls.js/src/videojs.hlsjs'
 	import IconText from '@/components/evaluation/IconText.vue'
 	import BaseSingle from '@/view/evaluation/types/BaseSingle.vue'
@@ -183,7 +183,7 @@
 				isRelatedContent: false,
 				selectPointsModal: false,
 				isEdit: false,
-				editInfo: {},
+				editInfo: null,
 				schoolInfo: {},
 				exersicesType: null,
 				exerciseField: 0,
@@ -259,6 +259,11 @@
 					this.curVideoName = e.srcElement.dataset.name
 				}
 			},
+			
+			/* 滚回顶部 */
+			backToTop(){
+				this.$refs.editContainer.scrollIntoView()
+			},
 
 			onSelectFile(val) {
 				this.relateFileList = val.files
@@ -322,44 +327,50 @@
 					default:
 						break
 				}
-				exerciseItem.id = this.editInfo.id ? this.editInfo.id : ''
 				exerciseItem.explain = this.analysisContent
 				exerciseItem.repairResource = this.formatRepairResource(this.$refs.repairRef.datas)
 				exerciseItem.field = this.exerciseField + 1
-				exerciseItem.points = this.exercisePoints.map(item => item.id)
-				exerciseItem.periodId = this.schoolInfo.period[this.exercisePeriod].id
-				exerciseItem.gradeIds = this.exerciseGrade.length ? this.exerciseGrade : this.gradeList.map(i => i.id)
-				exerciseItem.subjectId = this.schoolInfo.period[this.exercisePeriod].subjects[this.exerciseSubject].id
-				exerciseItem.code = this.editInfo.code
+				exerciseItem.points = this.exercisePoints
+				exerciseItem.periodId = this.isSchool ?  this.schoolInfo.period[this.exercisePeriod].id : null
+				exerciseItem.gradeIds = this.isSchool ?  this.exerciseGrade.length ? this.exerciseGrade : this.gradeList.map(i => i.id) : null
+				exerciseItem.subjectId = this.isSchool ?  this.schoolInfo.period[this.exercisePeriod].subjects[this.exerciseSubject].id : null
+				exerciseItem.code = this.editInfo.scope === 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId
 				exerciseItem.scope = this.exerciseScope === 0 ? 'private' : 'school'
-
+				
 				// 判断获取的数据是否有空数据以及是否为空字符串
 				if (this.checkContent(exerciseItem)) {
-					// 首先保存新题目的JSON文件到Blob 然后返回URL链接
-					if (this.refId !== 'paperEdit') {
-						let file = new File([JSON.stringify(exerciseItem)], this.editInfo.fileName + ".json", {
-							type: ""
-						});
-						// 根据题目类型 传到不同容器
-						let fileType = this.exerciseScope === 0 ? 'private' : 'school'
-						
+					if(this.refId !== 'paperEdit'){
+						// 生成JSON文件名称以及新增试题的ID
+						const guid = this.editInfo.id ? this.editInfo.id : this.$tools.guid()
+						// 给新增的试题赋值ID
+						exerciseItem.id = guid
+						// 将当前的试题数据转化为BLOB内部的试题JSON格式
+						const itemJsonFile = await this.$evTools.createBlobItem(exerciseItem)
+						// 首先保存新题目的JSON文件到Blob 然后返回URL链接
+						let file = new File([JSON.stringify(itemJsonFile)], guid + ".json", { type: ""});
 						// 获取初始化Blob需要的数据
 						let sasData = this.exerciseScope === 0 ? await this.$tools.getPrivateSas() : await this.$tools.getSchoolSas()
-						
 						//初始化Blob
-						let containerClient = uploadFile.initBlob(sasData.url, sasData.name, sasData.sas)
+						let containerClient = new blobTool(sasData.url, sasData.name, sasData.sas, exerciseItem.scope)
 						
-						// 等待上传blob的返回结果
-						let blobFile = await uploadFile.upload(containerClient, file, 'item')
-
-						if (blobFile.url) {
-							// 保存到COSMOS是不含base64图片编码的数据 避免数据量过大
-							exerciseItem.url = blobFile.url
-							exerciseItem.question = this.getSimpleText(exerciseItem.question)
-							this.saveExercise(exerciseItem)
+						try{
+							// 等待上传blob的返回结果
+							let blobFile = await containerClient.upload(file, 'item')
+							if (blobFile.blob) {
+								// 保存到COSMOS是不含base64图片编码的数据 避免数据量过大
+								exerciseItem.blob = blobFile.blob
+								let cosmosItem = await this.$evTools.createCosmosItem(exerciseItem)
+								this.saveExercise(cosmosItem) 
+							}else{
+								this.$Message.error('试题文件上传失败,请稍后重试!')
+							}
+						}catch(e){
+							this.$Message.error(e.spaceError)
 						}
-					} else {
-						this.saveExercise(exerciseItem)
+						
+					}else{
+						// 如果是试卷内编辑试题 则返回编辑好的数据 再统一进行保存
+						this.saveExercise(exerciseItem) 
 					}
 				} else if (exerciseItem.level === 0) {
 					this.$Message.warning('题目难度未设置!')
@@ -378,7 +389,7 @@
 			saveExercise(item) {
 				this.$emit('onEditSuccess', item)
 			},
-
+			
 			/* 检测补救资源超链接 去除无效链接 */
 			formatRepairResource(list) {
 				if (list.length) {
@@ -406,15 +417,8 @@
 			},
 
 			onCheckChange(val, list) {
-				this.exercisePoints = []
-				val.forEach(item => {
-					this.exercisePoints.push(list.filter(point => point.id === item)[0])
-				})
-			},
-
-			/** 回到顶部 */
-			backToTop() {
-
+				console.log(val)
+				this.exercisePoints = val
 			},
 
 			/**
@@ -423,7 +427,6 @@
 			 */
 			onDeletePoint(index) {
 				this.exercisePoints.splice(index, 1)
-				this.$refs.editPointRef.$children[1].onDeletePoint(index)
 			},
 
 			// 题目类型转换
@@ -458,7 +461,7 @@
 
 			onPeriodChange(val) {
 				console.log(this.schoolInfo)
-				console.log(val)
+				console.log(val)				
 				this.gradeList = this.schoolInfo.period[val].grades
 				this.subjectList = this.schoolInfo.period[val].subjects
 				this.exerciseGrade = 0
@@ -468,8 +471,6 @@
 
 			// 提取富文本内容中的文本
 			getSimpleText(html) {
-				// var msg = html.replace(/<(?!img).*?>/g, '') // 执行替换成空字符
-				// return msg.replace(/&nbsp;/ig, '')
 				var r = /<[^>]*>|/g;
 				return html.replace(r, '')
 			},
@@ -558,16 +559,16 @@
 			// 根据不同题型 给出需要必填选项
 			getWhiteListByType(type) {
 				switch (type) {
-					case 'Single':
+					case 'single':
 						return ['question', 'option', 'answer']
 						break;
-					case 'Multiple':
+					case 'multiple':
 						return ['question', 'option', 'answer']
 						break;
-					case 'Complete':
+					case 'complete':
 						return ['question', 'answer', ]
 						break;
-					case 'Compose':
+					case 'compose':
 						return ['question']
 						break;
 					default:
@@ -578,15 +579,16 @@
 
 			// 渲染编辑习题内容回显
 			async renderExercise(editItem) {
+				console.log(editItem)
+				this.editInfo = editItem
 				let schoolInfo = this.$store.state.schoolBaseInfo.schoolBaseInfo
 				this.isEdit = true
-				if (!editItem.options) editItem.options = editItem.option
 				this.exersicesDiff = editItem.level.toString() || '0'
-				this.exerciseScope = editItem.code === this.$store.state.userInfo.TEAMModelId ? 0 : 1
+				this.exerciseScope = editItem.scope === 'private' ? 0 : 1
 				this.exersicesType = editItem.type
 				this.exerciseField = editItem.field - 1
-				this.exercisePoints = await this.getPointsByIds(editItem.points)
-
+				this.exercisePoints = editItem.points
+				console.log(this.isSchool)
 				if (editItem.level) {
 					let ac = document.getElementById(this.refId).getElementsByClassName('edit-exersices-attr-diff')[0].children[1].children
 					for (let i = 0; i < ac.length; i++) {
@@ -608,23 +610,23 @@
 				}
 
 				this.childList = editItem.children || []
-
-				this.schoolInfo = schoolInfo
-				this.exerciseGrade = editItem.gradeIds
-				this.exercisePeriod = schoolInfo.period.map(item => item.id).indexOf(editItem.periodId)
-				this.subjectList = schoolInfo.period[this.exercisePeriod].subjects
-				this.gradeList = schoolInfo.period[this.exercisePeriod].grades
-				this.exerciseSubject = this.subjectList.map(item => item.id).indexOf(editItem.subjectId)
+				
+				if(this.isSchool){
+					this.schoolInfo = schoolInfo
+					this.exerciseGrade = editItem.gradeIds
+					this.exercisePeriod = schoolInfo.period.map(item => item.id).indexOf(editItem.periodId)
+					this.subjectList = schoolInfo.period[this.exercisePeriod].subjects
+					this.gradeList = schoolInfo.period[this.exercisePeriod].grades
+					this.exerciseSubject = this.subjectList.map(item => item.id).indexOf(editItem.subjectId)
+				}
+				
 					
 				this.stemContent = editItem.question
 				this.relateFileList = editItem.repairResource || []
 				this.optionsContent = editItem.option
 				this.analysisContent = editItem.explain
-				// this.repairContent = editItem.repair
 				this.analysisEditor.txt.html(editItem.explain)
-				// this.repairEditor.txt.html(editItem.repair)
 
-				this.editInfo = editItem
 			}
 
 		},
@@ -649,6 +651,11 @@
 			this.analysisEditor = analysisEditor
 
 		},
+		computed:{
+			isSchool(){
+				 return this.exerciseScope === 1
+			}
+		},
 		watch: {
 			exerciseItem: {
 				handler(newValue, oldValue) {
@@ -657,7 +664,6 @@
 						console.log(newValue)
 						this.isLoading = false
 						this.renderExercise(JSON.parse(JSON.stringify(newValue)))
-						// this.$refs.pointRef.doReset()
 					}
 				},
 				//immediate:true

+ 156 - 114
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseExerciseList.vue

@@ -5,22 +5,22 @@
 			<img src="../../../assets/icon/no_data.svg" width="120" />
 			<span style="margin-top:15px;color:#808080">暂无数据</span>
 		</div>
-		<div class="content-wrap" v-else>
+		<div class="content-wrap" ref="mathJaxContainer" v-else>
 			<Loading :top="200" v-show="dataLoading" type="1"></Loading>
 			<div class="list-view" :key="typeIndex" v-for="(typeItem,typeIndex) in listData">
 				<p v-show="viewModel === 'type' && typeItem.list.length" class="type-name">{{ numberConvertToUppercase(getLatestTypeIndex(typeItem.type) + 1) }}
 					: {{ exersicesType[typeItem.type] }}
-					  ({{ typeItem.score }} 分) </p>
+					  ({{ 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 }} 分</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>
@@ -90,7 +97,7 @@
 										<span v-if="! (_.compact(item.points).length)">暂未绑定知识点</span>
 										<div v-else>
 											<span v-for="(point,index) in item.points" :key="index" class="item-point-tag">
-												<span v-if="allPointList.length && allPointList.filter(i => i.id === point).length">{{ allPointList.filter(i => i.id === point)[0].name }}</span>
+												{{ point }}
 											</span>
 										</div>
 									</div>
@@ -106,35 +113,60 @@
 				</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">试卷总分 : {{ 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="modalItem.type === 'multiple'" style="margin-top: 10px;">
+						<div class="rule-item">
+							<RadioGroup v-model="multipleRule" vertical>
+								<Radio :label="1">
+									<Icon type="social-android"></Icon>
+									<span>默认全对得满分</span>
+								</Radio>
+								<Radio :label="2">
+									<Icon type="social-windows"></Icon>
+									<span>少选得一半分数,错选或者不选不得分</span>
+								</Radio>
+								<Radio :label="3">
+									<Icon type="social-windows"></Icon>
+									<span>少选按个数得分,错选或者不选不得分</span>
+								</Radio>
+								<Radio :label="4">
+									<Icon type="social-windows"></Icon>
+									<span>依照(选项数 - 2 * 错选数)/ 选项数,负分则不得分</span>
+								</Radio>
+							</RadioGroup>
+						</div>
+					</div>
 				</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">
@@ -175,19 +207,21 @@
 			return {
 				schoolCode: '',
 				dataLoading: false,
+				curEditItemId:null,
 				exerciseList: [],
 				schoolInfo: {},
 				isShowUploadList: false,
+				multipleRule:1,
 				typeScoreModel: false,
 				setPaperInfoModal: false,
 				editExerciseModal: false,
 				exersicesType: {
-					Single: '单选题',
-					Multiple: '多选题',
-					Judge: '判断题',
-					Complete: '填空题',
-					Subjective: '问答题',
-					Compose: '综合题'
+					single: '单选题',
+					multiple: '多选题',
+					judge: '判断题',
+					complete: '填空题',
+					subjective: '问答题',
+					compose: '综合题'
 				},
 				exersicesDiff: ['容易', '较易', '一般', '较难', '困难'],
 				diffColors: ['#32CF74', '#E8BE15', '#F19300', '#EB5E00', '#D30000'],
@@ -206,7 +240,7 @@
 				groupList: [],
 				groupTypeList: [],
 				orderList:[],
-				typeList: ['Single', 'Multiple', 'Judge', 'Complete', 'Subjective', 'Compose'],
+				typeList: ['single', 'multiple', 'judge', 'complete', 'subjective', 'compose'],
 				viewModel: 'type',
 				filterParams: {},
 				surPlusScore: 0,
@@ -223,7 +257,11 @@
 				curAudioSrc: "",
 				curAudioName: "",
 				curVideoSrc: "",
-				curVideoName: ""
+				curVideoName: "",
+				modifyItems:[],
+				paperInfo:{
+					score:0
+				}
 			}
 		},
 
@@ -248,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
@@ -267,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)
@@ -285,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)
 			},
-
 
 
 			/**
@@ -406,10 +423,11 @@
 					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)
+						this.$EventBus.$emit('onPaperItemChange',this.exerciseList)
 					}
 				})
 			},
@@ -419,17 +437,12 @@
 			 * @param item
 			 */
 			handleToolEdit(arr, item, index) {
+				console.log('试卷当前编辑的题目')
 				console.log(item)
-				let isSchoolItem = item.code === this.$store.state.userInfo.schoolCode
-				if (isSchoolItem) {
-					this.$Message.warning('无校本资源编辑权限!')
-				} else {
-					this.curIndex = index // 题型分组下的index
-					this.currentExerciseIndex = this.exerciseList.indexOf(item) // 清单列表下的index
-					this.curTypeItems = arr
-					this.currentExercise = item
-					this.editExerciseModal = true
-				}
+				this.currentExerciseIndex = this.exerciseList.indexOf(item) // 清单列表下的index
+				this.curTypeItems = arr
+				this.currentExercise = item
+				this.editExerciseModal = true
 			},
 
 
@@ -454,6 +467,7 @@
 			onScoreChange(val) {
 				this.surPlusScore = this.surPlusScore + this.lastScore - val
 				this.lastScore = val
+				this.$emit('scoreUpdate',this.surPlusScore)
 
 			},
 
@@ -466,31 +480,44 @@
 			/** 按照题型配分 */
 			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 {
 					/* 按照题型配分后平均分配给每个子题 */
 					this.groupTypeList.forEach(item => {
-						item.list.forEach(exercise => {
-							exercise.score = item.score / item.list.length
-							if (item.id) {
-								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
 						})
 					})
+					
 					/** 回到题型视图 */
 					this.groupList = this.groupTypeList
 					this.$parent.viewModel = 'type'
+					this.$parent.paperInfo.multipleRule = this.multipleRule
 					this.typeScoreModel = false
+					this.$emit('scoreUpdate',this.surPlusScore)
 				}
 
 			},
 
 			/** 编辑成功 */
 			onEditSuccess(item) {
+				
+				
 				if (item.id) {
 					this.$refs.paperEdit.isLoading = false
 					this.editExerciseModal = false
@@ -498,6 +525,14 @@
 					this.exerciseList.splice(this.currentExerciseIndex, 1, item)
 					this.curTypeItems.splice(this.curIndex, 1, item)
 					this.$emit('dataUpdate', this.exerciseList)
+					
+					let existIndex = this.modifyItems.map(i => i.id).indexOf(item.id)
+					if( existIndex < 0){
+						this.modifyItems.push(item)
+					}else{
+						this.modifyItems[existIndex] = item
+					}
+					
 				} else {
 					this.editExerciseModal = false
 					this.exerciseList.splice(this.currentExerciseIndex, 1, item)
@@ -507,7 +542,6 @@
 						this.collapseList.splice(listIndex, 1)
 					}
 					this.$Message.success("修改成功!")
-
 				}
 			},
 
@@ -541,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)
 			},
 
 			/**
@@ -581,9 +615,11 @@
 						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
+							})
 							this.orderList.push({ list:newPaper.item })
 							/* 处理试卷内题目按照题型排序 */
 							this.typeList.forEach(item => {
@@ -592,27 +628,23 @@
 										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)
 									}
 								})
 							});
-
-							/* 查找当前页面所有知识点ID换名称 */
-							this.getPointsByIds(this.getPointIds(this.exerciseList)).then(res => {
-								this.allPointList = res
-							})
 						}
 						
 						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(()=>{
-							window.MathJax.Hub.Queue(["Typeset", MathJax.Hub]);
+							window.MathJax.Hub.Queue(["Typeset", MathJax.Hub,this.$refs.mathJaxContainer]);
 						})
 					}
 				},
@@ -650,14 +682,24 @@
 		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 {
+		border: 1px solid #01b4ef;
+	}
+	
 	.components-el-container .exercise-item {
 		position: relative;
 		margin-top: 30px;

+ 21 - 49
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseFilter.vue

@@ -3,64 +3,35 @@
         <div class="filter-item" v-show="!isShowSchoolBank">
             <span class="filter-title">来源:</span>
             <RadioGroup v-model="filterOrigin" type="button" @on-change="filterOriginChange">
-                <Radio :label="userId">个人试卷库</Radio>
-                <Radio :label="schoolCode">校本试卷库</Radio>
+				<Radio :label="schoolCode" v-if="isShowSchoolBank">校本试卷库</Radio>
+				<Radio :label="userId">个人试卷库</Radio>
+				<Radio :label="schoolCode" v-if="!isShowSchoolBank">校本试卷库</Radio>
             </RadioGroup>
         </div>
-        <div class="filter-item">
+        <div class="filter-item" v-show="isShowSchoolBank">
             <span class="filter-title">学段:</span>
             <RadioGroup v-model="filterPeriod" type="button" @on-change="filterPeriodChange">
                 <Radio v-for="(item,index) in periodList" :key="index" :label="index">{{ item.name }}</Radio>
             </RadioGroup>
         </div>
-        <div class="filter-item">
+        <div class="filter-item" v-show="isShowSchoolBank">
             <span class="filter-title">年级:</span>
             <CheckboxGroup v-model="filterGrade" border @on-change="filterGradeChange">
                 <Checkbox lable="all">全部</Checkbox>
                 <Checkbox v-for="(item,index) in gradeList" :key="index" :label="item.id">{{ item.name }}</Checkbox>
             </CheckboxGroup>
         </div>
-        <div class="filter-item">
+        <div class="filter-item" v-show="isShowSchoolBank">
             <span class="filter-title">科目:</span>
-            <RadioGroup v-model="filterSubject" type="button" @on-change="filterSubjectChange">
-                <Radio v-for="(item,index) in subjectList" :key="index" :label="index">{{ item.name }}</Radio>
-            </RadioGroup>
-        </div>
-        <div class="filter-item" v-show="!isFilterPaper">
-            <span class="filter-title">题型:</span>
-            <CheckboxGroup v-model="filterType" border @on-change="filterTypeChange">
-                <Checkbox label="all">全部</Checkbox>
-                <Checkbox label="Single">单选</Checkbox>
-                <Checkbox label="Multiple">多选</Checkbox>
-                <Checkbox label="Judge">判断</Checkbox>
-                <Checkbox label="Complete">填空</Checkbox>
-                <Checkbox label="Subjective">问答</Checkbox>
-                <Checkbox label="Compose">综合</Checkbox>
-            </CheckboxGroup>
-        </div>
-        <div class="filter-item" v-show="!isFilterPaper">
-            <span class="filter-title">难度:</span>
-            <CheckboxGroup v-model="filterDiff" border @on-change="filterDiffChange">
-                <Checkbox label="all">全部</Checkbox>
-                <Checkbox v-for="(item,index) in exersicesDiff" :key="index" :label="index + 1">{{ item }}</Checkbox>
-            </CheckboxGroup>
-        </div>
-        <div class="filter-item" v-show="!isFilterPaper">
-            <span class="filter-title">层次:</span>
-            <CheckboxGroup v-model="filterField" border @on-change="filterFieldChange">
-                <Checkbox label="all">全部</Checkbox>
-                <Checkbox label="0">知识</Checkbox>
-                <Checkbox label="1">理解</Checkbox>
-                <Checkbox label="2">应用</Checkbox>
-                <Checkbox label="3">分析</Checkbox>
-                <Checkbox label="4">综合</Checkbox>
-                <Checkbox label="5">评鉴</Checkbox>
+            <CheckboxGroup v-model="filterSubject" border @on-change="filterSubjectChange">
+            	<Checkbox lable="all">全部</Checkbox>
+            	<Checkbox v-for="(item,index) in subjectList" :key="index" :label="item.id">{{ item.name }}</Checkbox>
             </CheckboxGroup>
         </div>
         <div class="filter-item">
             <span class="filter-title">排序:</span>
             <RadioGroup v-model="filterSort" type="button" @on-change="filterSortChange">
-                <Radio label="createTime">新增时间<Icon type="md-arrow-round-down" /></Radio>
+                <Radio label="createDate">新增时间<Icon type="md-arrow-round-down" /></Radio>
                 <Radio label="usageCount">使用次数<Icon type="md-arrow-round-down" /></Radio>
             </RadioGroup>
         </div>
@@ -105,10 +76,10 @@
                 filterOrigin: '',
                 filterDiff: ['all'],
                 filterField: ['all'],
-                filterSort: 'createTime',
+                filterSort: 'createDate',
                 filterPeriod: 0,
                 filterGrade: [false],
-                filterSubject: 0,
+                filterSubject: [false],
                 collapseList: [],
                 periodList: [],
                 gradeList: [],
@@ -118,6 +89,7 @@
         },
         created() {
             this.getSchoolInfo()
+			this.isShowSchoolBank = this.$route.name === 'schoolBank'
 			this.$EventBus.$on('showSchoolBank',(val)=> {
 				if(val){
 					this.isShowSchoolBank = val
@@ -149,7 +121,7 @@
                     'code': this.filterOrigin,
                     'periodId': [this.periodList[this.filterPeriod].id],
                     'gradeIds': this.deleteFalse(this.filterGrade),
-                    'subjectId': [this.subjectList[this.filterSubject].id],
+                    'subjectId': this.deleteFalse(this.filterSubject),
                     'level': this.deleteFalse(this.filterDiff),
                     'type': this.deleteFalse(this.filterType),
                     'field': this.deleteFalse(this.filterField)
@@ -162,7 +134,7 @@
                         'code': this.filterOrigin,
                         'periodId': [this.periodList[this.filterPeriod].id],
                         'gradeIds': this.deleteFalse(this.filterGrade),
-                        'subjectId': [this.subjectList[this.filterSubject].id],
+                        'subjectId': this.deleteFalse(this.filterSubject),
                         'level': this.deleteFalse(this.filterDiff),
                         'type': this.deleteFalse(this.filterType),
                         'field': this.deleteFalse(this.filterField)
@@ -181,7 +153,7 @@
                 this.gradeList = this.schoolInfo.period[val].grades
                 this.subjectList = this.schoolInfo.period[val].subjects
                 this.filterGrade = [false]
-                this.filterSubject = 0
+                this.filterSubject = [false]
                 this.doFilter()
             },
 
@@ -203,11 +175,11 @@
              * @param val
              */
             filterSubjectChange(val) {
-                // if (val !== [false] && val.indexOf(false) === 0) {
-                //    this.filterSubject.splice(val.indexOf(false), 1)
-                // } else if (val.indexOf(false) > 0) {
-                //    this.filterSubject = [false]
-                // }
+                if (val !== [false] && val.indexOf(false) === 0) {
+                   this.filterSubject.splice(val.indexOf(false), 1)
+                } else if (val.indexOf(false) > 0) {
+                   this.filterSubject = [false]
+                }
                 this.doFilter()
             },
 

+ 4 - 4
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseImport.vue

@@ -192,12 +192,12 @@
              * @param response
              */
             uploadSuccess(response) {
-                if (response.error === null  && response.result) {
-                    let requestData = { lang:localStorage.getItem('local'), htmlString: response.result.data.HtmlString }
+                if (response.HtmlString) {
+                    let requestData = { lang:localStorage.getItem('local'), htmlString: response.HtmlString }
                     this.$api.SaveAnalyzeHtml(requestData).then(res => {
-                        if (res.code === 0) {
+                        if (!res.error) {
                                 this.$Message.success('试题数据读取完成!')
-                                this.$emit('importFinish',res.result.data)
+                                this.$emit('importFinish',res)
                                 this.isImportFinish = false
                                 this.exerciseList = []
                         }else{

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


Some files were not shown because too many files changed in this diff