CrazyIter 5 年之前
父節點
當前提交
c329a51d53

+ 18 - 6
TEAMModelGrpc/Protos/KnowledgeService.proto

@@ -1,19 +1,31 @@
 syntax = "proto3";
-option csharp_namespace = "TEAMModelGrpc.KnowledgeService";
+option csharp_namespace = "TMDGrpc";
 package math;
 
 
 service KnowledgeService {
-   rpc SayHello(ByteString) returns(HelloReply);
+   rpc FinKnowledge(RequestDict) returns(stream Knowledge);
 
 }
 
 
 
-message ByteString {
-   repeated uint32 items = 1;
+message RequestDict {
+   //查询dict
+   map<string,string> dict = 1;
 }
 
-//The response message containing the greetings.
-message HelloReply {
+message Knowledge {
+   string id = 1;
+   int32 type = 2;
+   string name = 3;
+   string alias = 4;
+   string subjectCode = 5;
+   string scopeCode = 6;
+   int32 order = 7;
+   int32 status = 8;
+   repeated string points = 9;
+   string knowledgeId = 10;
+   int32 source = 11;
+   string period = 12;
 }

+ 72 - 4
TEAMModelGrpc/Startup.cs

@@ -1,15 +1,26 @@
 using System;
 using System.Collections.Generic;
+using System.IdentityModel.Tokens.Jwt;
 using System.Linq;
+using System.Security.Claims;
+using System.Text.Json;
 using System.Threading.Tasks;
 using Grpc.Extension.AspNetCore;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
 using Microsoft.AspNetCore.Builder;
 using Microsoft.AspNetCore.Hosting;
 using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Primitives;
+using Microsoft.IdentityModel.Tokens;
 using TEAMModelGrpc.Services;
+using TEAMModelOS.SDK.Module.AzureBlob.Configuration;
+using TEAMModelOS.SDK.Module.AzureCosmosDB.Configuration;
+using TEAMModelOS.SDK.Module.AzureCosmosDBV3;
+using TEAMModelOS.SDK.Module.AzureTable.Implements;
+using TEAMModelOS.SDK.Module.AzureTable.Interfaces;
 
 namespace TEAMModelGrpc
 {
@@ -27,36 +38,93 @@ namespace TEAMModelGrpc
             services.AddGrpc();
             //添加Grpc扩展
             services.AddGrpcExtensions(_conf);
+
+            services.AddAuthorization(options =>
+            {
+                options.AddPolicy(JwtBearerDefaults.AuthenticationScheme, policy =>
+                {
+                    policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
+                    policy.RequireClaim(ClaimTypes.Name);
+                });
+            });
+            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
+                .AddJwtBearer(options =>
+                {
+                    options.TokenValidationParameters =
+                        new TokenValidationParameters
+                        {
+                            ValidateAudience = false,
+                            ValidateIssuer = false,
+                            ValidateActor = false,
+                            ValidateLifetime = true,
+                            IssuerSigningKey = SecurityKey
+                        };
+                });
+
+            // Table配置
+            services.AddScoped<IAzureTableDBRepository, AzureTableDBRepository>();
+            //使用Blob配置
+            services.AddAzureBlobStorage().AddConnection(_conf.GetSection("Azure:Blob").Get<AzureBlobOptions>());
+            //使用CosmosDB
+            services.AddAzureCosmosDBV3().AddCosmosDBV3Connection(_conf.GetSection("Azure:CosmosDB").Get<AzureCosmosDBOptions>())
+                .AddCosmosSerializer(new SystemTextJsonCosmosSerializer(new JsonSerializerOptions() { IgnoreNullValues = true }));
+
         }
 
         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
-        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IAzureCosmosDBV3Repository cosmosDBV3Repository)
         {
             if (env.IsDevelopment())
             {
                 app.UseDeveloperExceptionPage();
             }
-
+            cosmosDBV3Repository.InitializeDatabase();
             app.UseRouting();
 
+            //注册 ASP.NET Core 身份验证中间件的顺序很重要。 
+            //始终在 UseRouting 之后和 UseEndpoints 之前调用 UseAuthentication 和 UseAuthorization。
+            app.UseAuthentication();
+            app.UseAuthorization();
+
             app.UseEndpoints(endpoints =>
             {
                 endpoints.MapGrpcService<GreeterService>();
 
+                endpoints.MapGet("/generateJwtToken", context =>
+                {
+                    return context.Response.WriteAsync(GenerateJwtToken(context.Request.Query["name"]));
+                });
+
                 endpoints.MapGet("/", async context =>
                 {
                     await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
                 });
             });
             //CodeFirst的Grpc(会自动扫描TStartup所在程序集下的IGrpcSerivce)
-            app.UseGrpcExtensions<KnowledgeService>(options =>
+            app.UseGrpcExtensions<TEAMModelGrpc.Services.KnowledgeService>(options =>
             {
                 //CodeFirst配制
                 options.GlobalPackage = "math";
-                options.ProtoNameSpace = "TEAMModelGrpc";
+                options.ProtoNameSpace = "TMDGrpc";
             })
             //CodeFirst生成proto
             .UseProtoGenerate("protos", false);
         }
+
+        private string GenerateJwtToken(string name)
+        {
+            if (string.IsNullOrEmpty(name))
+            {
+                throw new InvalidOperationException("Name is not specified.");
+            }
+
+            var claims = new[] { new Claim(ClaimTypes.Name, name) };
+            var credentials = new SigningCredentials(SecurityKey, SecurityAlgorithms.HmacSha256);
+            var token = new JwtSecurityToken("ExampleServer", "ExampleClients", claims, expires: DateTime.Now.AddSeconds(60), signingCredentials: credentials);
+            return JwtTokenHandler.WriteToken(token);
+        }
+
+        private readonly JwtSecurityTokenHandler JwtTokenHandler = new JwtSecurityTokenHandler();
+        private readonly SymmetricSecurityKey SecurityKey = new SymmetricSecurityKey(Guid.NewGuid().ToByteArray());
     }
 }

+ 5 - 0
TEAMModelGrpc/TEAMModelGrpc.csproj

@@ -8,6 +8,10 @@
     <DocumentationFile>TEAMModelOS.GRPC.xml</DocumentationFile>
   </PropertyGroup>
 
+  <ItemGroup>
+    <None Remove="Protos\KnowledgeService.proto" />
+  </ItemGroup>
+
   <ItemGroup>
     <PackageReference Include="Grpc.AspNetCore" Version="2.27.0" />
     <PackageReference Include="Grpc.Tools" Version="2.27.0">
@@ -23,6 +27,7 @@
 
   <ItemGroup>
     <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
+    <Protobuf Include="Protos\KnowledgeService.proto" GrpcServices="Server" />
   </ItemGroup>
 
   <ItemGroup>

+ 66 - 0
TEAMModelGrpc/TEAMModelOS.GRPC.xml

@@ -74,5 +74,71 @@
             <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
             <param name="serviceImpl">An object implementing the server-side handling logic.</param>
         </member>
+        <member name="T:TMDGrpc.KnowledgeServiceReflection">
+            <summary>Holder for reflection information generated from Protos/KnowledgeService.proto</summary>
+        </member>
+        <member name="P:TMDGrpc.KnowledgeServiceReflection.Descriptor">
+            <summary>File descriptor for Protos/KnowledgeService.proto</summary>
+        </member>
+        <member name="F:TMDGrpc.RequestDict.DictFieldNumber">
+            <summary>Field number for the "dict" field.</summary>
+        </member>
+        <member name="P:TMDGrpc.RequestDict.Dict">
+             <summary>
+            查询dict
+             </summary>
+        </member>
+        <member name="F:TMDGrpc.Knowledge.IdFieldNumber">
+            <summary>Field number for the "id" field.</summary>
+        </member>
+        <member name="F:TMDGrpc.Knowledge.TypeFieldNumber">
+            <summary>Field number for the "type" field.</summary>
+        </member>
+        <member name="F:TMDGrpc.Knowledge.NameFieldNumber">
+            <summary>Field number for the "name" field.</summary>
+        </member>
+        <member name="F:TMDGrpc.Knowledge.AliasFieldNumber">
+            <summary>Field number for the "alias" field.</summary>
+        </member>
+        <member name="F:TMDGrpc.Knowledge.SubjectCodeFieldNumber">
+            <summary>Field number for the "subjectCode" field.</summary>
+        </member>
+        <member name="F:TMDGrpc.Knowledge.ScopeCodeFieldNumber">
+            <summary>Field number for the "scopeCode" field.</summary>
+        </member>
+        <member name="F:TMDGrpc.Knowledge.OrderFieldNumber">
+            <summary>Field number for the "order" field.</summary>
+        </member>
+        <member name="F:TMDGrpc.Knowledge.StatusFieldNumber">
+            <summary>Field number for the "status" field.</summary>
+        </member>
+        <member name="F:TMDGrpc.Knowledge.PointsFieldNumber">
+            <summary>Field number for the "points" field.</summary>
+        </member>
+        <member name="F:TMDGrpc.Knowledge.KnowledgeIdFieldNumber">
+            <summary>Field number for the "knowledgeId" field.</summary>
+        </member>
+        <member name="F:TMDGrpc.Knowledge.SourceFieldNumber">
+            <summary>Field number for the "source" field.</summary>
+        </member>
+        <member name="F:TMDGrpc.Knowledge.PeriodFieldNumber">
+            <summary>Field number for the "period" field.</summary>
+        </member>
+        <member name="P:TMDGrpc.KnowledgeService.Descriptor">
+            <summary>Service descriptor</summary>
+        </member>
+        <member name="T:TMDGrpc.KnowledgeService.KnowledgeServiceBase">
+            <summary>Base class for server-side implementations of KnowledgeService</summary>
+        </member>
+        <member name="M:TMDGrpc.KnowledgeService.BindService(TMDGrpc.KnowledgeService.KnowledgeServiceBase)">
+            <summary>Creates service definition that can be registered with a server</summary>
+            <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+        </member>
+        <member name="M:TMDGrpc.KnowledgeService.BindService(Grpc.Core.ServiceBinderBase,TMDGrpc.KnowledgeService.KnowledgeServiceBase)">
+            <summary>Register service method with a service binder with or without implementation. Useful when customizing the  service binding logic.
+            Note: this method is part of an experimental API that can change or be removed without any prior notice.</summary>
+            <param name="serviceBinder">Service methods will be bound by calling <c>AddMethod</c> on this object.</param>
+            <param name="serviceImpl">An object implementing the server-side handling logic.</param>
+        </member>
     </members>
 </doc>

+ 52 - 0
TEAMModelGrpc/appsettings.json

@@ -11,5 +11,57 @@
     "EndpointDefaults": {
       "Protocols": "Http2"
     }
+  },
+  "urls": "https://*:5000",
+  "GrpcServer": {
+    //用于grpc启动后注册到服务发现的ip地址段
+    "ServiceAddress": "192.168.*.*:",
+    //是否启用服务注册和服务发现,默认是true
+    "EnableDiscovery": false,
+    //服务发现服务器地址
+    "DiscoveryUrl": "http://106.12.23.251:8500",
+    //注册到服务发现的服务名称
+    "DiscoveryServiceName": "TMDOSgRPC",
+    //服务发现主动TTL的时间(秒)
+    "DiscoveryTTLInterval": 10,
+    //注册到服务发现的服务Tag
+    "DiscoveryServiceTags": "v-1.0.0.1",
+    //默认错误码
+    "DefaultErrorCode": 4300000,
+    //Jaeger配制(OpenTracing)
+    "Jaeger": {
+      //是否启用Jaeger,默认false
+      "Enable": false,
+      "AgentIp": "192.168.8.11",
+      "AgentPort": 5775
+    }
+  },
+  "Azure": {
+    "Table": {
+      "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=teammodelostest;AccountKey=QB/zYHHCAtZfl9tf4emL1Y9ZXGc6fqZ+nNbCxIHM70HnziC8dMdEAu7+Pa4mbKLlbswV90wWHAF3nMjrKB54Lw==;EndpointSuffix=core.chinacloudapi.cn"
+    },
+    "Blob": {
+      "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=teammodelstorage;AccountKey=Yq7D4dE6cFuer2d2UZIccTA/i0c3sJ/6ITc8tNOyW+K5f+/lWw9GCos3Mxhj47PyWQgDL8YbVD63B9XcGtrMxQ==;EndpointSuffix=core.chinacloudapi.cn",
+      "Container": "teammodelos"
+    },
+    "CosmosDB": {
+      "ConnectionString": "https://192.168.8.128:8081",
+      "ConnectionKey": "ddwAeGSf8Lsf1kxPXmdqnyzzi3CkJ0KW2BTPZ7Zq1N7qbJic5j7AaQ+WbF86F3rnzuDgGM1yg8O7BUFo93iA8w==",
+      "Database": "TEAMModelOS",
+      "CollectionThroughput": 400,
+      "ScanModel": [ "TEAMModelOS.Service" ]
+    },
+    "Redis": {
+      "ConnectionString": "106.12.23.251:6379,password=habook,ssl=false,abortConnect=False,defaultDatabase=13,writeBuffer=10240,poolsize=50,prefix=habook:"
+    }
+  },
+  "HaBookAuth": {
+    "TeamModelRegistUrl": "https://account.habookaclass.biz/regist?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJsb2dpbiIsImF1ZCI6ImNoZW5nZHVMb2dpbiIsImlzcyI6Imh0dHBzOi8vYXBpLmhhYm9va2FjbGFzcy5iaXoiLCJpYXQiOjE1MzYxMzUwNDcsIm5iZiI6MTUzNjEzNTA0NywiZXhwIjoxNTY3NTU1MjAwLCJpZHAiOiJIYWJvb2sgQ29yZVNlcnZpY2UifQ.F4AnkbJrMRoZvJ6SC-lqZEYIYSoq5x8lvX6_a3YqSgM&callback=",
+    "TeamModelLoginUrl": "https://account.habookaclass.biz/?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJsb2dpbiIsImF1ZCI6ImNoZW5nZHVMb2dpbiIsImlzcyI6Imh0dHBzOi8vYXBpLmhhYm9va2FjbGFzcy5iaXoiLCJpYXQiOjE1MzYxMzUwNDcsIm5iZiI6MTUzNjEzNTA0NywiZXhwIjoxNTY3NTU1MjAwLCJpZHAiOiJIYWJvb2sgQ29yZVNlcnZpY2UifQ.F4AnkbJrMRoZvJ6SC-lqZEYIYSoq5x8lvX6_a3YqSgM&callback=",
+    "AccountUrl": "https://api.habookaclass.biz/account",
+    "ServiceUrl": "https://api.habookaclass.biz/service",
+    "UserInfoKey": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJpZCIsImF1ZCI6ImNoZW5nZHVJZCIsImlzcyI6Imh0dHBzOi8vYXBpLmhhYm9va2FjbGFzcy5iaXoiLCJpYXQiOjE1MzYwNTIzNjcsIm5iZiI6MTUzNjA1MjM2NywiZXhwIjoxNTY3NTU1MjAwLCJpZHAiOiJIYWJvb2sgQ29yZVNlcnZpY2UifQ.RGKDVtwFEp4OBctlHOuF6yqyI21fTz4cinCxjFCxkSQ",
+    "SchoolCodeKey": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzY2hvb2xDb2RlIiwiYXVkIjoiY2hlbmdkdVNjaG9vbENvZGUiLCJpc3MiOiJodHRwczovL2FwaS5oYWJvb2thY2xhc3MuYml6IiwiaWF0IjoxNTM2MDUyNDI3LCJuYmYiOjE1MzYwNTI0MjcsImV4cCI6MTU2NzU1NTIwMCwiaWRwIjoiSGFib29rIENvcmVTZXJ2aWNlIn0.8m5VH3Nz4N9EdMz8AexTOEuDVitcJZFKy9DfW_UQkSY",
+    "SmsKey": "Basic ZmYwMWM0YTJjODdmZmNkYTUyNjhmMDEwOmE0YTE5YTVjNTU2ZWVhZTNjZmZhNTI0Mg=="
   }
 }

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

@@ -10,6 +10,13 @@ export default {
     SaveSingleExercise: function(data) {
         return post('/api/ItemInfo/Save', data)
     },
+    /**
+     * �������浥������
+     * @param {any} data
+     */
+    SaveAllExercise: function(data) {
+        return post('/api/ItemInfo/SaveAll', data)
+    },
 
     /**
      * ��ѯ����б�

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

@@ -9,8 +9,8 @@
   background-color: #505050;
   margin-bottom: 3px;
   padding: 10px 30px 10px 10px;
-  max-height: 400px;
-  transition: max-height ease 5.5s;
+  /*max-height: 400px;
+  transition: max-height ease 5.5s;*/
   position:relative;
   .toggle-detail-icon {
     position:absolute;
@@ -50,13 +50,12 @@
 }
 
 .question-item-wrap-detail {
-  max-height: 500px;
+  /*max-height: 500px;*/
 }
 
 .question-detail {
   padding-left: 15px;
   color: white;
-  transition: height ease 0.5s;
 
   .answer-label {
     color: rgb(16, 171, 231);

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

@@ -16,11 +16,15 @@
                 </p>
                 <p class="dark-iview-inputnumber" style="margin-top:15px;" v-if="config.showScore">
                     <span class="answer-label">配分:</span>
-                    <InputNumber :max="100" :min="1" v-model="item.score" size="small" style="width: 110px" :formatter="value => value+' 分'" :parser="value => value.replace(' 分','')" @on-change="calcScore"></InputNumber>
+                    <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-label">答案:</p>
                 <p class="answer-detail">
-                    <span v-for="(answerItem,index) in item.answer" v-html='answerItem'></span>
+                    <span 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)" />
@@ -47,7 +51,8 @@
                         showSelect: false,
                         showScore: false,
                         showDelete: false,
-                        showRemedy: false
+                        showRemedy: false,
+                        editable:false
                     }
                 }
             }

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

@@ -157,7 +157,7 @@
                @on-visible-change="editModalChange"
                title="编辑习题">
 
-            <BaseEditExercise :exerciseItem="currentExercise" @onEditSuccess="onEditSuccess" ref="editModal"></BaseEditExercise>
+            <BaseEditExercise :exerciseItem="currentExercise" @onEditSuccess="onEditSuccess" refId="listEdit"></BaseEditExercise>
 
             <div slot="footer">
                 <Button type="success">确认</Button>
@@ -298,7 +298,7 @@
             },
 
             editModalChange(val) {
-                    this.$refs.editModal.backToTop()
+                    //this.$refs.editModal.backToTop()
             },
 
             /**

+ 3 - 4
TEAMModelOS/ClientApp/src/view/evaluation/bank/TestPaperList.vue

@@ -9,9 +9,7 @@
             <span class="import-exercise">
                 <Button type="success" @click="goPickExercises">手动挑题</Button>
                 <Button type="info" @click="randomModal = true">随机挑题</Button>
-                <Upload multiple action="/api/ImportExercise/UploadWord" :headers="headers" :on-success="uploadSuccess">
-                    <Button type="info">导入习题</Button>
-                </Upload>
+                <BaseImport></BaseImport>
             </span>
         </div>
 
@@ -73,10 +71,11 @@
 <script>
     import Loading from '@/common/Loading.vue'
     import BaseFilter from '../components/BaseFilter'
+    import BaseImport from '../components/BaseImport'
     import AutoCreate from '../../learnactivity/AutoCreate'
     export default {
         components: {
-            Loading, BaseFilter, AutoCreate
+            Loading, BaseFilter, AutoCreate,BaseImport
         },
         data() {
             return {

+ 26 - 9
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseEditExercise.vue

@@ -1,5 +1,5 @@
 <template>
-    <div class="ev-container component-ev-container" ref="componentRef">
+    <div class="ev-container component-ev-container" :id="refId">
             <div class="display-flex">
                 <div class="exersices-attr my-radio-style">
                     <IconText :text="'选择学段'" :color="'#00b8ff'" :icon="'md-school'"></IconText>
@@ -122,7 +122,7 @@
     import E from 'wangeditor'
     
     export default {
-        props:['exerciseItem'],
+        props:['exerciseItem','refId'],
         components: {
             IconText, BaseSingle, BaseJudge, BaseMultiple, BaseCompletion, BaseSubjective, BasePoints
         },
@@ -144,7 +144,7 @@
                 periodList: [],
                 gradeList: [],
                 subjectList: [],
-                exersicesDiff: '1',
+                exersicesDiff: 0,
                 analysisContent: '',
                 repairContent: '',
                 stemContent: '',
@@ -217,13 +217,15 @@
                 exerciseItem.scopeCode = this.exerciseScope === 0 ? 'habook#0001' : this.schoolInfo.schoolCode
 
                 // 判断获取的数据是否有空数据以及是否为空字符串
-                if (this.checkContent(exerciseItem) && this.getSimpleText(exerciseItem.question) && this.getSimpleText(exerciseItem.explain) && exerciseItem.gradeCode.length) {
+                if (this.checkContent(exerciseItem) && this.getSimpleText(exerciseItem.question)  && exerciseItem.gradeCode.length) {
                     this.saveExercise(exerciseItem)
                 } else if (exerciseItem.level === 0) {
                     this.$Message.warning('题目难度未设置!')
                 } else {
                     this.$Message.warning('请将题目填写完整!')
                 }
+
+                console.log(exerciseItem)
             },
 
             /**
@@ -274,11 +276,11 @@
                 this.exersicesDiff = +type
                 e.preventDefault()
                 let colorArr = ['#10abe7', '#E8BE15', '#F19300', '#EB5E00', '#D30000']
-                let ac = document.getElementsByClassName('edit-exersices-attr-diff')[0].children[1].children
+                /** 通过动态赋予ID 解决多个组件下获取DOM覆盖问题 */
+                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'
                     ac[i].style.color = '#515a6e'
-                    console.log(ac[i])
                 }
                 e.target.style.background = colorArr[+type - 1]
                 e.target.style.color = '#fff'
@@ -344,18 +346,28 @@
                 this.isEdit = true
                 if (!editItem.options) editItem.options = editItem.option
                 this.editInfo = editItem
-                this.exersicesDiff = editItem.level.toString() || '1'
+                this.exersicesDiff = editItem.level.toString() || '0'
                 this.exersicesType = editItem.type
                 this.exerciseField = this.fieldList.indexOf(editItem.field)
                 this.exercisePoints = editItem.points
 
                 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++) {
+                        ac[i].style.background = '#fff'
+                        ac[i].style.color = '#515a6e'
+                    }
                     // 重新渲染题目难度
-                    let diffDom = document.getElementsByClassName('edit-exersices-attr-diff')[0].children[1].children[editItem.level - 1]
+                    let diffDom = document.getElementById(this.refId).getElementsByClassName('edit-exersices-attr-diff')[0].children[1].children[editItem.level - 1]
                     let colorArr = ['#32CF74', '#E8BE15', '#F19300', '#EB5E00', '#D30000']
-                    console.log(document.getElementsByClassName('edit-exersices-attr-diff'))
                     diffDom.style.background = colorArr[editItem.level - 1]
                     diffDom.style.color = '#fff'
+                } else {
+                    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'
+                        ac[i].style.color = '#515a6e'
+                    }
                 }
                 
 
@@ -432,4 +444,9 @@
     .exersices-attr .ivu-select-multiple .ivu-select-selection .ivu-select-placeholder {
         line-height: 38px;
     }
+
+    .component-ev-container .ivu-select-single .ivu-select-selection .ivu-select-placeholder {
+        line-height:38px;
+        height:38px;
+    }
 </style>

+ 188 - 100
TEAMModelOS/ClientApp/src/view/evaluation/components/BaseExerciseList.vue

@@ -8,74 +8,74 @@
         </div>
         <div class="content-wrap" v-else>
             <Loading :top="200" v-show="dataLoading" type="1"></Loading>
-                <div class="list-view" v-for="(typeItem,typeIndex) in groupList">
-                    <p v-show="viewModel === 'type'" class="type-name">{{ numberConvertToUppercase(typeIndex + 1) }} : {{  exersicesType[typeItem.type] }} ( {{ typeItem.score }} 分)</p>
-                    <div class="exercise-item" v-for="(item,index) of typeItem.list" :key="index" @click="onQuestionToggle(exerciseList.indexOf(item),item.id,$event,typeItem.list)" @mouseenter="exerciseMouseover($event)" @mouseleave="exerciseMouseleave($event)">
-                        <div class="item-tools-wrap">
-                            <div class="item-tools-t flex-row-center" @click.stop="handleSetScore(item,exerciseList.indexOf(item))"><Icon type="ios-list-box-outline" />配分</div>
-                            <div class="item-tools-t flex-row-center" @click.stop="handleToolEdit(item)"><Icon type="ios-brush-outline" />编辑</div>
-                            <div class="item-tools-t flex-row-center" @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" @click.stop="handleMoveUp(typeItem.list,index)"><Icon type="md-arrow-up" />上移</div>
-                            <div class="item-tools-t flex-row-center" v-show="index != (typeItem.list.length - 1)" @click.stop="handleMoveDown(typeItem.list,index)"><Icon type="md-arrow-down" />下移</div>
-                        </div>
+            <div class="list-view" v-for="(typeItem,typeIndex) in groupList">
+                <p v-show="viewModel === 'type'" class="type-name">{{ numberConvertToUppercase(typeIndex + 1) }} : {{  exersicesType[typeItem.type] }} ( {{ typeItem.score }} 分)</p>
+                <div class="exercise-item" v-for="(item,index) of typeItem.list" :key="index" @click="onQuestionToggle(exerciseList.indexOf(item),item.id,$event,typeItem.list)" @mouseenter="exerciseMouseover($event)" @mouseleave="exerciseMouseleave($event)">
+                    <div class="item-tools-wrap">
+                        <div class="item-tools-t flex-row-center" @click.stop="handleSetScore(item,exerciseList.indexOf(item))"><Icon type="ios-list-box-outline" />配分</div>
+                        <div class="item-tools-t flex-row-center" @click.stop="handleToolEdit(typeItem.list,item,index)"><Icon type="ios-brush-outline" />编辑</div>
+                        <div class="item-tools-t flex-row-center" @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" @click.stop="handleMoveUp(typeItem.list,index)"><Icon type="md-arrow-up" />上移</div>
+                        <div class="item-tools-t flex-row-center" v-show="index != (typeItem.list.length - 1)" @click.stop="handleMoveDown(typeItem.list,index)"><Icon type="md-arrow-down" />下移</div>
+                    </div>
 
-                        <!-- 题干部分 -->
-                        <div class="item-question" style="pointer-events:none">
-                            <p v-if="viewModel === 'list'">{{pageSize * (pageNum - 1) + exerciseList.indexOf(item) + 1}} : <span v-html="item.question"></span></p>
-                            <p v-else>{{ pageSize * (pageNum - 1) + index + 1 }} : <span v-html="item.question"></span></p>
-                            <span class="item-btn-toggle">
-                                <span class="item-score">{{ item.score }} 分</span>
+                    <!-- 题干部分 -->
+                    <div class="item-question" style="pointer-events:none">
+                        <p v-if="viewModel === 'list'">{{pageSize * (pageNum - 1) + exerciseList.indexOf(item) + 1}} : <span v-html="item.question"></span></p>
+                        <p v-else>{{ pageSize * (pageNum - 1) + index + 1 }} : <span v-html="item.question"></span></p>
+                        <span class="item-btn-toggle">
+                            <span class="item-score">{{ item.score }} 分</span>
 
-                                <Icon :type="collapseList.indexOf(index) > -1 ? 'ios-arrow-dropup' : 'ios-arrow-dropdown'" />
-                                <!--<Button type="primary">选题</Button>-->
-                            </span>
-                        </div>
-                        <!-- 选项部分 -->
-                        <div v-for="(option,optionIndex) in item.option" :key="optionIndex" class="item-options" style="pointer-events:none">
-                            <p class="item-option-content">{{String.fromCharCode(64 + parseInt(optionIndex+1))}} : <span v-html="option.value"></span></p>
-                        </div>
-                        <transition name="slide">
-                            <div v-show="collapseList.indexOf(exerciseList.indexOf(item)) > -1" class="toggle-area">
-                                <!-- 答案展示部分 -->
-                                <div class="item-explain" v-show="isShowAnswer">
-                                    <span class="explain-title">【答案】</span>
-                                    <div class="item-explain-details">
-                                        <!-- 问答题答案 -->
-                                        <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" :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" :key="index">{{answer}}</span>
-                                        </div>
+                            <Icon :type="collapseList.indexOf(index) > -1 ? 'ios-arrow-dropup' : 'ios-arrow-dropdown'" />
+                            <!--<Button type="primary">选题</Button>-->
+                        </span>
+                    </div>
+                    <!-- 选项部分 -->
+                    <div v-for="(option,optionIndex) in item.option" :key="optionIndex" class="item-options" style="pointer-events:none">
+                        <p class="item-option-content">{{String.fromCharCode(64 + parseInt(optionIndex+1))}} : <span v-html="option.value"></span></p>
+                    </div>
+                    <transition name="slide">
+                        <div v-show="collapseList.indexOf(exerciseList.indexOf(item)) > -1" class="toggle-area">
+                            <!-- 答案展示部分 -->
+                            <div class="item-explain" v-show="isShowAnswer">
+                                <span class="explain-title">【答案】</span>
+                                <div class="item-explain-details">
+                                    <!-- 问答题答案 -->
+                                    <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>
-                                <!-- 解析部分 -->
-                                <div class="item-explain" v-show="isShowAnswer">
-                                    <span class="explain-title">【解析】</span>
-                                    <div class="item-explain-details">
-                                        <span v-html="item.explain || '暂无解析'"></span>
+                                    <!-- 填空题答案 -->
+                                    <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" :key="index">{{answer}}</span>
                                     </div>
                                 </div>
-                                <!-- 知识点部分 -->
-                                <div class="item-explain" v-show="isShowAnswer">
-                                    <span class="explain-title">【知识点】</span>
-                                    <div class="item-explain-details">
-                                        <span v-if="!item.points.length">暂未绑定知识点</span>
-                                        <div v-else>
-                                            <span v-for="point in item.points" style="padding:0 10px;border:1px solid #808080;margin-left:10px">{{ point.name }}</span>
-                                        </div>
+                            </div>
+                            <!-- 解析部分 -->
+                            <div class="item-explain" v-show="isShowAnswer">
+                                <span class="explain-title">【解析】</span>
+                                <div class="item-explain-details">
+                                    <span v-html="item.explain || '暂无解析'"></span>
+                                </div>
+                            </div>
+                            <!-- 知识点部分 -->
+                            <div class="item-explain" v-show="isShowAnswer">
+                                <span class="explain-title">【知识点】</span>
+                                <div class="item-explain-details">
+                                    <span v-if="!item.points.length">暂未绑定知识点</span>
+                                    <div v-else>
+                                        <span v-for="point in item.points" style="padding:0 10px;border:1px solid #808080;margin-left:10px">{{ point ? point.name : '0' }}</span>
                                     </div>
                                 </div>
-
                             </div>
-                        </transition>
-                    </div>
+
+                        </div>
+                    </transition>
                 </div>
+            </div>
         </div>
 
         <!-- 底部分页区域 -->
@@ -90,8 +90,7 @@
 
         <Modal v-model="scoreModal"
                title="题目配分"
-               @on-ok="onConfirmScore"
-               >
+               @on-ok="onConfirmScore">
             <InputNumber :max="curItemScore + surPlusScore" :min="0" :step="scoreStep" v-model="curItemScore" @on-change="onScoreChange"></InputNumber>
             <p>剩余可分配分数:{{ surPlusScore }}</p>
         </Modal>
@@ -112,16 +111,31 @@
 
         </Modal>
 
+        <Modal v-model="editExerciseModal"
+               class-name="edit-exercise-modal"
+               width="1200px"
+               footer-hide
+               title="编辑习题">
+
+            <BaseEditExercise :exerciseItem="currentExercise" @onEditSuccess="onEditSuccess" refId="componetListEdit"></BaseEditExercise>
+
+            <div slot="footer">
+                <Button type="success">确认</Button>
+            </div>
+
+        </Modal>
+
 
     </div>
 </template>
 
 <script>
+    import BaseEditExercise from './BaseEditExercise'
     import Loading from '@/common/Loading.vue'
     export default {
         props: ['paper'],
         components: {
-            Loading
+            Loading,BaseEditExercise
         },
         data() {
             return {
@@ -133,6 +147,7 @@
                 isShowUploadList: false,
                 typeScoreModel:false,
                 setPaperInfoModal: false,
+                editExerciseModal:false,
                 exersicesType: {
                     Single: '单选题',
                     Multiple: '多选题',
@@ -164,8 +179,10 @@
                 curItemScore: 0,
                 curIndex:0,
                 lastScore: 0,
-                scoreStep:0.5,
-                scoreModal:false
+                scoreStep: 0.5,
+                curTypeItems:[],
+                scoreModal: false,
+                currentExercise: {}
             }
         },
 
@@ -173,6 +190,50 @@
             //console.log(this.exerciseList)
         },
         methods: {
+            /**
+             * 题干展开与收缩
+             * @param index
+             * @param id
+             */
+            onQuestionToggle(index, id, e, arr) {
+                e.stopPropagation()
+                let listIndex = this.collapseList.indexOf(index)
+                let arrIndex = arr.indexOf(this.exerciseList[index])
+                if (listIndex > -1) {
+                    this.collapseList.splice(listIndex, 1)
+                } else {
+                    /** 如果有知识点 则需要进行换取名称 */
+                    let detailExercise = this.exerciseList[index]
+                    console.log(detailExercise)
+                    if (detailExercise.points.length && typeof (detailExercise.points[0]) === 'string') {
+                        this.getPointsByIds(detailExercise.points).then(res => {
+                            console.log(res)
+                            detailExercise.points = res
+                            /* 查询到详细数据则替换掉原数据 */
+                            this.exerciseList.splice(index, 1, detailExercise)
+                            arr.splice(arrIndex, 1, detailExercise)
+                        })
+                    } else {
+                        /* 查询到详细数据则替换掉原数据 */
+                        this.exerciseList.splice(index, 1, detailExercise)
+                        arr.splice(arrIndex, 1, detailExercise)
+                    }
+                    /** 如果是首次展开 则需要获取详细数据 否则直接展开 */
+                    if (!this.exerciseList[index].answer.length) {
+                        //this.getDetailsById(arr, id, index).then(r => {
+                        //    this.collapseList.push(index)
+                        //    this.pageScrollTo(e.target.offsetTop + 60) // 490就是 content-wrap 距离顶部高度
+                        //})
+                        this.collapseList.push(index)
+                        this.pageScrollTo(e.target.offsetTop + 60) // 490就是 content-wrap 距离顶部高度
+                    } else {
+                        this.collapseList.push(index)
+                        this.pageScrollTo(e.target.offsetTop + 60) // 490就是 content-wrap 距离顶部高度
+                    }
+                }
+            },
+
+
             /**
              * 根据ID获取试题详细数据
              * @param id 试题ID
@@ -182,8 +243,9 @@
                     this.$api.newEvaluation.FindExerciseById([id]).then(res => {
                         let detailExercise = res.result.data[0]
                         /** 如果有知识点 则需要进行换取名称 */
-                        if (detailExercise.points.length) {
+                        if (detailExercise.points.length && !detailExercise.points.map(i => i.name).length) {
                             this.getPointsByIds(detailExercise.points).then(res => {
+                                console.log(res)
                                 detailExercise.points = res
                                 /* 查询到详细数据则替换掉原数据 */
                                 this.exerciseList.splice(index, 1, detailExercise)
@@ -206,6 +268,9 @@
              * @param ids
              */
             getPointsByIds(ids) {
+                if (typeof (ids[0]) !== 'string'){
+                    ids = ids.map(item => item.id)
+                }
                 return new Promise((r, j) => {
                     this.$api.knowledge.FindKnowledgebyId(ids).then(res => {
                         if (res.result.data.length) {
@@ -220,29 +285,7 @@
 
             },
 
-            /**
-             * 题干展开与收缩
-             * @param index
-             * @param id
-             */
-            onQuestionToggle(index, id, e, arr) {
-                e.stopPropagation()
-                let listIndex = this.collapseList.indexOf(index)
-                if (listIndex > -1) {
-                    this.collapseList.splice(listIndex, 1)
-                } else {
-                    /** 如果是首次展开 则需要获取详细数据 否则直接展开 */
-                    if (!this.exerciseList[index].answer.length) {
-                        this.getDetailsById(arr, id, index).then(r => {
-                            this.collapseList.push(index)
-                            this.pageScrollTo(e.target.offsetTop + 60) // 490就是 content-wrap 距离顶部高度
-                        })
-                    } else {
-                        this.collapseList.push(index)
-                        this.pageScrollTo(e.target.offsetTop + 60) // 490就是 content-wrap 距离顶部高度
-                    }
-                }
-            },
+            
 
             /**
              * 数字与中文转换
@@ -347,17 +390,26 @@
              * 编辑单个试题
              * @param item
              */
-            handleToolEdit(item) {
-                this.$api.newEvaluation.FindExerciseById(item.id).then(res => {
-                    res.result.data.options = res.result.data.option
-                    res.result.data.level = res.result.data.level || 1
-                    this.$router.push({
-                        name: 'createExercises',
-                        params: {
-                            item: res.result.data
-                        }
+            handleToolEdit(arr, item, index) {
+                if (item.points.length) {
+                    this.getPointsByIds(item.points).then(res => {
+                        item.points = res
+                        this.curIndex = index // 题型分组下的index
+                        this.currentExerciseIndex = this.exerciseList.indexOf(item) // 清单列表下的index
+                        this.curTypeItems = arr
+                        this.currentExercise = item
+                        this.editExerciseModal = true
+
                     })
-                })
+                } else {
+                        this.curIndex = index // 题型分组下的index
+                        this.currentExerciseIndex = this.exerciseList.indexOf(item) // 清单列表下的index
+                        this.curTypeItems = arr
+                        this.currentExercise = item
+                        this.editExerciseModal = true
+                }
+
+                
             },
 
 
@@ -367,7 +419,7 @@
              * @param index
              */
             handleSetScore(item, index) {
-                this.curIndex = index
+                this.currentExerciseIndex = index
                 this.curItemScore = item.score
                 this.lastScore = item.score
                 this.scoreModal = true
@@ -410,6 +462,30 @@
 
             },
 
+            /** 编辑成功 */
+            onEditSuccess(item) {
+                if (item.id) {
+                    this.$api.newEvaluation.SaveSingleExercise(item).then(res => {
+                        this.editExerciseModal = false
+                        this.$Message.success("修改成功!")
+                        this.exerciseList.splice(this.currentExerciseIndex, 1, item)
+                        this.curTypeItems.splice(this.curIndex, 1, item)
+                    })
+                } else {
+                    this.editExerciseModal = false
+                    this.exerciseList.splice(this.currentExerciseIndex, 1, item)
+                    this.curTypeItems.splice(this.curIndex, 1, item)
+                    let listIndex = this.collapseList.indexOf(this.currentExerciseIndex);
+                    if (listIndex > -1) {
+                        this.collapseList.splice(listIndex, 1)
+                    }
+                    this.$Message.success("修改成功!")
+
+                }
+
+                
+            },
+
 
             /** 打开题型配分 */
             onSetScoreByType() {
@@ -431,7 +507,9 @@
             paper: {
                 handler(newPaper, oldValue) {
                     if (newPaper) {
-                        
+
+                        console.log(newPaper)
+
                         let that = this
                         this.groupList = []
                         this.exerciseList = []
@@ -494,6 +572,7 @@
         margin-top:20px;
     }
 
+
     .components-el-container .exercise-item {
         position:relative;
         margin-top:30px;
@@ -578,3 +657,12 @@
     font-size: 16px;
 }
 </style>
+
+<style>
+    
+    .edit-exercise-modal .ivu-modal-body {
+        max-height: 700px;
+        overflow: auto;
+        padding: 0;
+    }
+</style>

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

@@ -4,6 +4,7 @@
         <Modal v-model="importPreviewModal"
                width="1200px"
                class-name="import-modal"
+               @on-visible-change="importModalChange"
                title="导入预览">
 
             <div v-if="!isImportFinish" style="padding:40px">
@@ -36,7 +37,7 @@
                     <!-- 题干部分 -->
                     <div class="item-question">
                         <p>{{ index + 1 }} : <span v-html="item.question"></span></p>
-                        <span class="item-btn-toggle btn-edit-exercise" @click="onEditExercise(item,index)" >
+                        <span class="item-btn-toggle btn-edit-exercise" @click="onEditExercise(item,index)">
                             <Icon type="md-create" color="#B5B5B5" size="20" />
                             <span> 编辑  </span>
 
@@ -107,7 +108,7 @@
 
             </div>
             <div slot="footer">
-                <Button type="success">确认</Button>
+                <Button type="success" @click="onConfirmUsage">确认</Button>
             </div>
 
         </Modal>
@@ -117,10 +118,9 @@
                class-name="edit-exercise-modal"
                width="1200px"
                footer-hide
-               @on-visible-change="editModalChange"
                title="编辑习题">
 
-               <BaseEditExercise :exerciseItem="currentExercise" @onEditSuccess="onEditSuccess" ref="editModal"></BaseEditExercise>
+            <BaseEditExercise :exerciseItem="currentExercise" @onEditSuccess="onEditSuccess" refId="importEdit"></BaseEditExercise>
 
             <div slot="footer">
                 <Button type="success">确认</Button>
@@ -134,7 +134,7 @@
     import BaseEditExercise from './BaseEditExercise'
     export default {
         components: {
-            Loading,BaseEditExercise
+            Loading, BaseEditExercise
         },
         props: ['period', 'subject'],
         data() {
@@ -148,13 +148,13 @@
                 importPreviewModal: false,
                 selectUsageModal: false,
                 editExerciseModal: false,
-                currentExercise:null,
-                currentExerciseIndex:null,
-                currentUsage:'bank',
+                currentExercise: null,
+                currentExerciseIndex: null,
+                currentUsage: 'bank',
                 uploadUrl: '',
                 paperInfo: {
                     name: '',
-                    score:100
+                    score: 100
                 }
             }
         },
@@ -174,7 +174,7 @@
              * 编辑单个试题
              * @param item
              */
-            onEditExercise(item,index) {
+            onEditExercise(item, index) {
                 console.log(item)
                 console.log(this)
                 this.currentExercise = item
@@ -186,11 +186,17 @@
             onEditSuccess(newItem) {
                 this.editExerciseModal = false
                 this.$Message.success("修改成功!")
-                this.exerciseList.splice(this.currentExerciseIndex,1,newItem)
+                this.exerciseList.splice(this.currentExerciseIndex, 1, newItem)
             },
 
-            editModalChange(val) {
-                    this.$refs.editModal.backToTop()
+            importModalChange(val) {
+                if (!val) {
+                    this.isImportFinish = false
+                    this.isSelectFinish = false
+                    this.isBtnLoading = false
+                    this.importLoading = false
+                    this.exerciseList = []
+                }
             },
 
             /** 重新导入文件 */
@@ -240,6 +246,7 @@
                                 res.result.data.forEach(item => {
                                     item.periodCode = that.$parent.filterParams.periodCode[0]
                                     item.subjectCode = that.$parent.filterParams.subjectCode[0]
+                                    item.scopeCode = that.$parent.filterParams.scopeCode
                                 })
                                 that.exerciseList = res.result.data
                                 that.isImportFinish = true
@@ -255,24 +262,79 @@
             /** 导入后选择试题用途 */
             onSelectUsage() {
                 console.log(this.exerciseList)
-                //let importList = this.exerciseList
-                //for (let i = 0; i < importList.length; i++) {
+                let importList = this.exerciseList
+                for (let i = 0; i < importList.length; i++) {
+                    if (importList[i].level === 0) {
+                        this.$Message.warning(`第${i + 1}个试题未设置难度!请确保信息完善!`)
+                        return
+                    }
+                }
+                this.selectUsageModal = true
+            },
 
-                //}
-                //this.selectUsageModal = true
+            /** 选好用途后确认导入 */
+            onConfirmUsage() {
+                if (this.currentUsage === 'bank') {
+                    this.saveAllExercise(this.exerciseList).then(res => {
+                        this.$Message.success("保存成功!")
+                        this.selectUsageModal = false
+                        this.importPreviewModal = false
+                    })
+                } else if (this.currentUsage === 'paper') {
+                    this.goToTestPaper()
+                } else {
+                    if (!this.paperInfo.name) {
+                       this.$Message.warning("试卷名称不能为空!")
+                    } else {
+                       this.saveAllExercise(this.exerciseList).then(res => {
+                            this.$Message.success("保存成功!")
+                            this.goToTestPaper()
+                       })
+                    }
+                }
             },
 
-            onSelectBank() {
+
+            /**
+             * 保存导入的所有试题
+             * @param list
+             */
+            saveAllExercise(list) {
+                return new Promise((r, j) => {
+                    this.$api.newEvaluation.SaveAllExercise(list).then(res => {
+                        if (res.result.data) r(res.result.data)
+                    }).catch(err => {
+                        j(err)
+                    })
+                })
 
             },
 
-            onSelectPaper() {
+            /** 导入试题后进入组卷 */
+            goToTestPaper() {
+                const parentVm = this.$parent
+                if (!this.paperInfo.name) {
+                    this.$Message.warning("试卷名称不能为空!")
+                } else {
+                    let params = {
+                        name: this.paperInfo.name,
+                        score: this.paperInfo.score,
+                        item: this.exerciseList,
+                        scopeCode: parentVm.filterParams.scopeCode,
+                        periodCode: parentVm.filterParams.periodCode[0],
+                        gradeCode: this.gradeCode,
+                        subjectCode: parentVm.filterParams.subjectCode[0]
+                    }
+                    this.$router.push({
+                        name: 'testPaper',
+                        params: {
+                            paper: params
+                        }
+                    })
+                }
 
             },
 
-            onSelectAll() {
-
-            }
         },
 
         mounted() {
@@ -296,7 +358,7 @@
 <style lang="less">
 
     .import-modal .ivu-modal-body,
-    .edit-exercise-modal .ivu-modal-body{
+    .edit-exercise-modal .ivu-modal-body {
         max-height: 700px;
         overflow: auto;
         padding: 0;
@@ -327,11 +389,11 @@
     }
 
     .import-modal .exercise-item .btn-edit-exercise {
-        display:none;
+        display: none;
     }
 
     .import-modal .exercise-item:hover .btn-edit-exercise {
-        display:unset;
+        display: unset;
     }
 
 
@@ -354,30 +416,30 @@
 
         .usage-modal .usage-modal-baseInfo {
             margin: 10px;
-         }
+        }
 
         .usage-modal .usage-modal-paperInfo {
             margin-top: 20px;
-            
-         }
+        }
 
-            .usage-modal .usage-modal-baseInfo p {
-                margin:30px 0 0 0;
-            }
+        .usage-modal .usage-modal-baseInfo p {
+            margin: 30px 0 0 0;
+        }
 
 
-    .usage-modal .ivu-radio-group {
-        vertical-align:bottom;
-    }
+        .usage-modal .ivu-radio-group {
+            vertical-align: bottom;
+        }
 
         .usage-modal .ivu-input-wrapper {
-            width:75%;
+            width: 75%;
         }
+
         .usage-modal .ivu-radio-wrapper {
-            margin-right:30px;
+            margin-right: 30px;
         }
 
         .usage-modal .ivu-form-item {
-            padding:0 10px;
+            padding: 0 10px;
         }
 </style>

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

@@ -132,6 +132,11 @@
                 let noScoreList = this.$refs.exList.exerciseList.filter(item => item.score === 0) // 判断是否有未配分的题目
                 if (hasSurplus) {
                     if (!noScoreList.length) {
+                        this.paperInfo.item.forEach(i => {
+                            if (i.points.length && typeof (i.points[1]) !== 'string') {
+                                i.points = i.points.map(j => j.id)
+                            }
+                        })
                         let saveParams = this.paperInfo
                         this.$refs.exList.dataLoading = true
                         this.$api.learnActivity.SaveExamPaper(saveParams).then(res => {
@@ -167,6 +172,11 @@
              */
             onListUpdate(list) {
                 console.log(list)
+                list.forEach(i => {
+                    if (i.points.length && typeof (i.points[1]) !== 'string') {
+                        i.points = i.points.map(j => j.id)
+                    }
+                })
                 this.handleAnalysisData(list)
             },
 

+ 2 - 1
TEAMModelOS/ClientApp/src/view/learnactivity/ManageEvaluation.vue

@@ -150,7 +150,8 @@
             return {
                 editEvaluationStatus: false,
                 questionConfig: {
-                    showScore: true
+                    showScore: true,
+                    editable: true
                 },
                 examAnalysisData: {
                     whole: {},