瀏覽代碼

Merge branch 'develop3.0' of http://106.12.23.251:10080/TEAMMODEL/TEAMModelOS into develop3.0

liqk 4 年之前
父節點
當前提交
cf072f03b8
共有 24 個文件被更改,包括 616 次插入216 次删除
  1. 74 0
      TEAMModelOS.SDK/Module/AzureServiceBus/AzureServiceBusClientSingleton.cs
  2. 39 0
      TEAMModelOS.SDK/Module/AzureServiceBus/AzureServiceBusCollectionExtensions.cs
  3. 29 0
      TEAMModelOS.SDK/Module/AzureServiceBus/AzureServiceBusOptions.cs
  4. 32 0
      TEAMModelOS.SDK/Module/AzureServiceBus/AzureServiceBusService.cs
  5. 24 0
      TEAMModelOS.SDK/Module/AzureServiceBus/AzureServiceBusServiceBuilder.cs
  6. 13 0
      TEAMModelOS.SDK/Module/AzureServiceBus/IAzureServiceBusService.cs
  7. 1 0
      TEAMModelOS.SDK/TEAMModelOS.SDK.csproj
  8. 8 2
      TEAMModelOS.Service/Models/SchoolInfo/Survey.cs
  9. 5 2
      TEAMModelOS/ClientApp/src/components/learnactivity/ClassList.less
  10. 53 76
      TEAMModelOS/ClientApp/src/components/learnactivity/ClassList.vue
  11. 168 0
      TEAMModelOS/ClientApp/src/components/learnactivity/GradeChart.vue
  12. 11 0
      TEAMModelOS/ClientApp/src/components/learnactivity/GradeList.less
  13. 18 0
      TEAMModelOS/ClientApp/src/components/learnactivity/GradeList.vue
  14. 64 106
      TEAMModelOS/ClientApp/src/components/learnactivity/GradeTable.vue
  15. 3 3
      TEAMModelOS/ClientApp/src/components/questionnaire/BaseQuestionnaire.vue
  16. 17 3
      TEAMModelOS/ClientApp/src/mock/index.js
  17. 3 3
      TEAMModelOS/ClientApp/src/store/module/totalAnalysis.js
  18. 8 8
      TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.vue
  19. 10 8
      TEAMModelOS/ClientApp/src/view/selflearning/ManageHomeWork.vue
  20. 0 1
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/index.vue
  21. 7 1
      TEAMModelOS/Controllers/Exam/ExamController.cs
  22. 13 0
      TEAMModelOS/Controllers/Syllabus/ItemInfoController.cs
  23. 7 2
      TEAMModelOS/Startup.cs
  24. 9 1
      TEAMModelOS/appsettings.Development.json

+ 74 - 0
TEAMModelOS.SDK/Module/AzureServiceBus/AzureServiceBusClientSingleton.cs

@@ -0,0 +1,74 @@
+using Microsoft.Azure.ServiceBus;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Module.AzureServiceBus
+{
+    public  class AzureServiceBusClientSingleton
+    {
+
+        private ServiceBusModel ServiceBusModel;
+        public static AzureServiceBusOptions azureServiceBusOptions;
+        private AzureServiceBusClientSingleton() { }
+        public ServiceBusModel GetServiceBusClient()
+        {
+            if (ServiceBusModel != null)
+            {
+                return ServiceBusModel;
+            }
+            else
+            {
+                getInstance(azureServiceBusOptions);
+                return ServiceBusModel;
+            }
+        }
+        public static AzureServiceBusClientSingleton getInstance(AzureServiceBusOptions _azureServiceBusOptions)
+        {
+            azureServiceBusOptions= _azureServiceBusOptions;
+          
+         
+            return SingletonInstance.instance;
+        }
+
+        private static class SingletonInstance
+        {
+            public static AzureServiceBusClientSingleton instance = new AzureServiceBusClientSingleton()
+            {
+                ServiceBusModel = new ServiceBusModel { topClients = GetTopClients(),subClients=GetSubClients() }
+            };
+        }
+
+        private static Dictionary<string, TopClient> GetTopClients() {
+            Dictionary<string, TopClient> topClients = new Dictionary<string, TopClient>();
+            string ConnectionString = azureServiceBusOptions.ConnectionString;
+            azureServiceBusOptions.Topics.ForEach(x=> { topClients.TryAdd(x.Name, new TopClient { topicClient = new TopicClient(ConnectionString, x.Name) }); });
+            return topClients;
+        }
+        private static Dictionary<string, SubClient> GetSubClients()
+        {
+            Dictionary<string, SubClient>  subClients = new Dictionary<string, SubClient>();
+            string ConnectionString = azureServiceBusOptions.ConnectionString;
+            azureServiceBusOptions.Topics.ForEach(x => { x.Subscribe.ForEach(y => { subClients.TryAdd(y, new SubClient { topName = x.Name, subscriptionClient = new SubscriptionClient(ConnectionString, x.Name, y) }); }); });
+            return subClients;
+        }
+    }
+
+
+    public class ServiceBusModel {
+       public Dictionary<string, TopClient> topClients { get; set; } = new Dictionary<string, TopClient>();
+        public Dictionary<string, SubClient> subClients { get; set; } = new Dictionary<string, SubClient>();
+       
+    }
+
+    public class TopClient { 
+       
+        public ITopicClient topicClient { get; set; }
+    }
+    public class SubClient
+    {
+        public string topName { get; set; }
+       
+        public ISubscriptionClient subscriptionClient { get; set; }
+    }
+}

+ 39 - 0
TEAMModelOS.SDK/Module/AzureServiceBus/AzureServiceBusCollectionExtensions.cs

@@ -0,0 +1,39 @@
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using TEAMModelOS.SDK.Module.AzureServiceBus;
+
+namespace TEAMModelOS.SDK
+{
+    public static class AzureServiceBusCollectionExtensions
+    {
+
+        private static AzureServiceBusServiceBuilder AddServiceBusBuilder(this IServiceCollection services)
+        {
+            return new AzureServiceBusServiceBuilder(services);
+        }
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="services"></param>
+        /// <returns></returns>
+        public static AzureServiceBusServiceBuilder AddServiceBus(this IServiceCollection services)
+        {
+            var builder = services.AddServiceBusBuilder();
+            services.AddSingleton<IAzureServiceBusService, AzureServiceBusService>();
+            return builder;
+        }
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="builder"></param>
+        /// <param name="_connectionString"></param>
+        /// <returns></returns>
+        public static AzureServiceBusServiceBuilder AddServiceBusOptions(this AzureServiceBusServiceBuilder builder, AzureServiceBusOptions serviceBusOptions)
+        {
+            builder.Services.AddSingleton(serviceBusOptions);
+            return builder;
+        }
+    }
+}

+ 29 - 0
TEAMModelOS.SDK/Module/AzureServiceBus/AzureServiceBusOptions.cs

@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Module.AzureServiceBus
+{
+    /// <summary>
+    /// 主题配置信息
+    /// </summary>
+    public  class AzureServiceBusOptions
+    {
+        public string ConnectionString { get; set; } = null;
+        public List<Topic> Topics { get; set; } = null;
+       
+    }
+    /// <summary>
+    /// 主题
+    /// </summary>
+    public class Topic { 
+        /// <summary>
+        /// 主题名称
+        /// </summary>
+        public string Name { get; set; }
+        /// <summary>
+        /// 主题的订阅器
+        /// </summary>
+        public List<string> Subscribe { get; set; }
+    }
+}

+ 32 - 0
TEAMModelOS.SDK/Module/AzureServiceBus/AzureServiceBusService.cs

@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Module.AzureServiceBus
+{
+    public class AzureServiceBusService :IAzureServiceBusService
+    {
+        public ServiceBusModel serviceBusModel;
+
+        public AzureServiceBusService(AzureServiceBusOptions options) {
+            serviceBusModel = AzureServiceBusClientSingleton.getInstance(options).GetServiceBusClient() ;
+        }
+
+
+        public void init() {
+            if (serviceBusModel != null) {
+                
+            }
+        }
+        public TopClient GetTopClient(string TopName) {
+            serviceBusModel.topClients.TryGetValue(TopName, out TopClient topClient);
+            return topClient;
+        }
+
+        public SubClient GetSubClient(string SubName)
+        {
+            serviceBusModel.subClients.TryGetValue(SubName, out SubClient subClient);
+            return subClient;
+        }
+    }
+}

+ 24 - 0
TEAMModelOS.SDK/Module/AzureServiceBus/AzureServiceBusServiceBuilder.cs

@@ -0,0 +1,24 @@
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Module.AzureServiceBus
+{
+    public class AzureServiceBusServiceBuilder
+    {
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="services"></param>
+        public AzureServiceBusServiceBuilder(IServiceCollection services)
+        {
+            Services = services ?? throw new ArgumentNullException(nameof(services));
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        public IServiceCollection Services { get; }
+    }
+}

+ 13 - 0
TEAMModelOS.SDK/Module/AzureServiceBus/IAzureServiceBusService.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TEAMModelOS.SDK.Module.AzureServiceBus
+{
+    public interface IAzureServiceBusService
+    {
+        public TopClient GetTopClient(string TopName);
+        public SubClient GetSubClient(string SubName);
+        void init();
+    }
+}

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

@@ -27,6 +27,7 @@
     <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.3" />
     <PackageReference Include="Microsoft.Azure.CosmosDB.BulkExecutor" Version="2.4.1-preview" />
     <PackageReference Include="Microsoft.Azure.DocumentDB.Core" Version="2.10.1" />
+    <PackageReference Include="Microsoft.Azure.ServiceBus" Version="4.1.3" />
     <PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
     <PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.2.0" />
     <PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.3" />

+ 8 - 2
TEAMModelOS.Service/Models/SchoolInfo/Survey.cs

@@ -51,8 +51,8 @@ namespace TEAMModelOS.Service.Models
         [ProtoMember(7)]
         public long endTime { get; set; }
         public long createTime { get; set; } // 问卷发布时间
-        public List<Item> items{ get; set; }
-      
+        public List<Item> items { get; set; }
+
     }
     public class Item {
         public string stem { get; set; }
@@ -64,5 +64,11 @@ namespace TEAMModelOS.Service.Models
         public int order { get; set; }
         public string description { get; set; } = null;
         public List<CodeValue> options { get; set; }
+        public List<CodeVal> result { get; set; }
+    }
+
+    public class CodeVal{
+        public string code { get; set; }
+        public int value { get; set; }
     }
 }

+ 5 - 2
TEAMModelOS/ClientApp/src/components/learnactivity/ClassList.less

@@ -20,12 +20,15 @@
         padding-left: 10px;
     }
 
-    .table-show {
+    .chart-show {
         width: 100%;
         height: 300px;
     }
 }
-
+.class-filter {
+    margin-left: 15px;
+    margin-top: 15px;
+}
 .activity-status {
     font-size: 10px;
     background: #19be6b;

+ 53 - 76
TEAMModelOS/ClientApp/src/components/learnactivity/ClassList.vue

@@ -32,21 +32,33 @@
                     </p>
                 </div>
             </div>
+
             <div slot="right" class="activity-detail-info">
-                <div class="learn-progress-main dark-iview-table">
-                    <vuescroll>
+                <vuescroll>
+                    <div class="learn-progress-main dark-iview-table">
                         <p style="color:#EEEEEE;padding-left:15px;font-size:16px;margin-top:15px;">测验成绩分析</p>
-                        <div class="table-show">
-                            <div class="table" style="margin:auto;">
-                                <Grade></Grade>
+                        <div class="class-filter dark-iview-select">
+                            <span class="filter-label">测验班级:</span>
+                            <Select filterable style="display: inline-block;width: 150px;" @on-change="getClass" size="small">
+                                <Option v-for="item in studentData" :value="item.classId" :key="item.classId">{{ item.name }}</Option>
+                            </Select>
+                        </div>
+                        <div class="chart-show">
+                            <div class="chart" style="margin:auto;">
+                                <Grade v-if="studentList.length !== 0" :studentData="studentList"></Grade>
                             </div>
                         </div>
-                        <p style="color:#EEEEEE;padding-left:15px;font-size:16px;margin-top:50px;margin-bottom:30px;">
-                            学生试题分析概览
+                        <p style="color:#EEEEEE;padding-left:15px;font-size:16px;margin-top:10%;margin-bottom:60px;">
+                            学生成绩数据
                         </p>
-                    </vuescroll>
-                </div>
-               
+                        <div class="table-show">
+                            <div class="table" style="margin:0px 25px;">
+                                <Table v-if="studentList.length !== 0" :studentData="studentList"></Table>
+                            </div>
+                        </div>
+
+                    </div>
+                </vuescroll>
                 <!--<AnswerDetail v-else @closeAnswerDetail="closeAnswerDetail"></AnswerDetail>-->
                 <!--<AnswerRecord v-else @closeAnswerDetail="closeAnswerDetail"></AnswerRecord>-->
             </div>
@@ -54,10 +66,12 @@
     </div>
 </template>
 <script>
-    import Grade from '@/components/learnactivity/GradeTable.vue'
+    import Grade from '@/components/learnactivity/GradeChart.vue'
+    import Table from '@/components/learnactivity/GradeTable.vue'
     export default {
         components: {
-           Grade
+            Grade,
+            Table
         },
         data() {
             return {
@@ -69,68 +83,6 @@
                         classroomName:'语文',
                         classroomCode: '123',
                         type: '平常考',
-                        studentList: [
-                            {
-                                name: "尹航",
-                                code: "HSNB001",
-                                class: "HSNA",
-                                answer: {
-                                    qs1: "升旗仪式结束后,特区政府会在香港会议展览中心的三楼大会堂举行庆祝酒会,行政长官将为酒会主礼。",
-                                    qs2: "公报中提到,升旗仪式将于当日上午8时在香港会议展览中心外的金紫荆广场举行。仪式中,警察乐队将会奏乐",
-                                    qs3: "29日下午,香港特区政府发布新闻公报称,行政长官林郑月娥和港府高层官员将于7月1日出席升旗仪式和庆祝酒会,庆祝香港特别行政区成立二十三周年。",
-                                },
-                            },
-                            {
-                                name: "刘雨菡",
-                                code: "HSNB002",
-                                class: "HSNA",
-                                answer: {
-                                    qs1: "升旗仪式结束后,特区政府会在香港会议展览中心的三楼大会堂举行庆祝酒会,行政长官将为酒会主礼。",
-                                    qs2: "公报中提到,升旗仪式将于当日上午8时在香港会议展览中心外的金紫荆广场举行。仪式中,警察乐队将会奏乐",
-                                    qs3: "29日下午,香港特区政府发布新闻公报称,行政长官林郑月娥和港府高层官员将于7月1日出席升旗仪式和庆祝酒会,庆祝香港特别行政区成立二十三周年。",
-                                },
-                            },
-                            {
-                                name: "廖致远",
-                                code: "HSNB003",
-                                class: "HSNA",
-                                answer: {
-                                    qs1: "升旗仪式结束后,特区政府会在香港会议展览中心的三楼大会堂举行庆祝酒会,行政长官将为酒会主礼。",
-                                    qs2: "公报中提到,升旗仪式将于当日上午8时在香港会议展览中心外的金紫荆广场举行。仪式中,警察乐队将会奏乐",
-                                    qs3: "29日下午,香港特区政府发布新闻公报称,行政长官林郑月娥和港府高层官员将于7月1日出席升旗仪式和庆祝酒会,庆祝香港特别行政区成立二十三周年。",
-                                },
-                            },
-                            {
-                                name: "向奕然",
-                                code: "HSNB004",
-                                class: "HSNA",
-                                answer: {
-                                    qs1: "升旗仪式结束后,特区政府会在香港会议展览中心的三楼大会堂举行庆祝酒会,行政长官将为酒会主礼。",
-                                    qs2: "公报中提到,升旗仪式将于当日上午8时在香港会议展览中心外的金紫荆广场举行。仪式中,警察乐队将会奏乐",
-                                    qs3: "29日下午,香港特区政府发布新闻公报称,行政长官林郑月娥和港府高层官员将于7月1日出席升旗仪式和庆祝酒会,庆祝香港特别行政区成立二十三周年。",
-                                },
-                            },
-                            {
-                                name: "高嘉妍",
-                                code: "HSNB005",
-                                class: "HSNA",
-                                answer: {
-                                    qs1: "升旗仪式结束后,特区政府会在香港会议展览中心的三楼大会堂举行庆祝酒会,行政长官将为酒会主礼。",
-                                    qs2: "公报中提到,升旗仪式将于当日上午8时在香港会议展览中心外的金紫荆广场举行。仪式中,警察乐队将会奏乐",
-                                    qs3: "29日下午,香港特区政府发布新闻公报称,行政长官林郑月娥和港府高层官员将于7月1日出席升旗仪式和庆祝酒会,庆祝香港特别行政区成立二十三周年。",
-                                },
-                            },
-                            {
-                                name: "李芷萱",
-                                code: "HSNB006",
-                                class: "HSNA",
-                                answer: {
-                                    qs1: "升旗仪式结束后,特区政府会在香港会议展览中心的三楼大会堂举行庆祝酒会,行政长官将为酒会主礼。",
-                                    qs2: "公报中提到,升旗仪式将于当日上午8时在香港会议展览中心外的金紫荆广场举行。仪式中,警察乐队将会奏乐",
-                                    qs3: "29日下午,香港特区政府发布新闻公报称,行政长官林郑月娥和港府高层官员将于7月1日出席升旗仪式和庆祝酒会,庆祝香港特别行政区成立二十三周年。",
-                                },
-                            }
-                        ],
                         startTime: '2020-05-06',
                         endTime:'2020-07-07'
                     },
@@ -142,13 +94,35 @@
                         endTime:'2020-05-07'
                     },
 
-                ]
+                ],
+                studentData: [], 
+                studentList:[]
             }
         },
         methods: {
             selectActivity(index) {
                 this.currentActivityIndex = index
-                console.log(index)
+            },
+            getData() {
+                let data = this.$Mock.data.studentList
+                this.studentData = data
+                this.getClass()
+            },
+            getClass(data) {
+                if (this.studentData.length !== 0) {
+                    if (data !== undefined) {
+                        for (let i = 0; i < this.studentData.length; i++) {
+                            if (this.studentData[i].classId == data) {
+                                this.studentList = this.studentData[i].studentScore
+                            }
+                        }
+                    } else {
+
+                        this.studentList = this.studentData[0].studentScore
+                    }
+                } else {
+                    this.$Message.warning('暂无测验数据!')
+                }
             },
             goToAnswer() {
                 this.showAnswer = true
@@ -156,6 +130,9 @@
             closeAnswerDetail() {
                 this.showAnswer = false
             }
+        },
+        mounted() {
+            this.getData()
         }
     }
 </script>

+ 168 - 0
TEAMModelOS/ClientApp/src/components/learnactivity/GradeChart.vue

@@ -0,0 +1,168 @@
+<template>
+        <div id="grade"></div>
+</template>
+<script>
+    export default {
+        props: {
+            studentData: {
+                type: Array,
+                default: () => {
+                    return []
+                }
+            },
+        },
+        data() {
+            return {
+                dataIndex: 1,
+                progressHistogram: undefined,
+                dataInfo: [],
+                studentList: [],
+                studentName: [],
+                studentScore: [],
+                average: 0,
+                mini: 0,
+                max:0
+            }
+        },
+        methods:{
+            getData() {
+                this.studentList.length = 0
+                this.studentScore.length = 0
+                let total = 0;
+                for (var i = 0; i < this.studentData.length; i++) {
+                    this.studentList.push(this.studentData[i].name);
+                    this.studentScore.push(this.studentData[i].score);
+                    total += this.studentData[i].score
+                }
+                this.max = (Math.max.apply(null, this.studentData))   //最大值
+                this.min = (Math.min.apply(null, this.studentData))   //最小值
+                this.average = (total / this.studentData.length).toFixed(2)
+            },
+            getMap() {
+                let myChart = this.$echarts.init(document.getElementById('grade'));
+                let option = {
+                    color: ["#42beda"],
+                    tooltip: {},
+                    xAxis: {
+                        "type": "category",
+                        "axisLine": {
+
+                        },
+                        "splitLine": {
+                            "show": false
+                        },
+                        "axisTick": {
+                            "show": false
+                        },
+                        "splitArea": {
+                            "show": false
+                        },
+                        "axisLabel": {
+                            "interval": 0,
+
+                        },
+                        axisLabel: {
+                            color: 'white'
+                        },
+                        data: this.studentList
+                    },
+                    yAxis: {
+                        type: "value",
+                        name: "测验得分",
+                        lineStyle: {
+                            color: '#fffff',
+                            width: 2
+                        },
+                        nameTextStyle: {
+                            color: "#fff"
+                        },
+                        splitLine: {
+                            lineStyle: {
+                                color: ['#505050']
+                            }
+                        },
+                        axisLabel: {
+                            color: 'white'
+                        },
+                    },
+                    "dataZoom": [{
+                        "show": true,
+                        "height": 10,
+                        "xAxisIndex": [
+                            0
+                        ],
+                        bottom: 10,
+                        color: "#fffff",
+                        "start":0,
+                        "end": 100,
+                        handleSize: '110%',
+                        handleStyle: {
+                            color: "#fffff",
+
+                        },
+                        textStyle: {
+                            color: "#fffff"
+                        },
+                        borderColor: "#90979c",
+                        handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
+                    },
+                    {
+                        "type": "inside",
+                        "show": true,
+                        "height": 15,
+                        "start": 1,
+                        "end": 35
+                    }],
+                    series: [{
+                        name: '分数',
+                        type: 'bar',
+                        data: this.studentScore,
+                        markPoint: {
+                            data: [{
+                                type:'max',
+                                name: '最大值',
+                            }]
+                        },
+                        markLine: {
+                            silent: true,
+                            lineStyle: {
+                                normal: {
+                                    color: '#E8E093' 
+                                }
+                            },
+                            data: [{
+                                name: "平均线",
+                                yAxis: this.average, //基准线
+                                label: {
+                                    position: "end",
+                                    formatter: "{b} \n {c}"
+                                }                                    
+                            }],
+                          
+
+                        }
+                    }]
+                };
+                myChart.setOption(option);
+            }
+        },
+        mounted() {
+            this.getData()
+            this.getMap()
+        },
+        watch: {
+            studentData(newValue, oldValue) {
+                deep: true
+                this.getData()
+                this.getMap()
+            }
+        }
+    }
+</script>
+<style scoped>
+    #grade{
+        width:700px;
+        height:400px;
+        margin:auto;
+    }
+</style>

+ 11 - 0
TEAMModelOS/ClientApp/src/components/learnactivity/GradeList.less

@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace TEAMModelOS.ClientApp.src.components.learnactivity
+{
+    public class GradeList
+    {
+    }
+}

+ 18 - 0
TEAMModelOS/ClientApp/src/components/learnactivity/GradeList.vue

@@ -0,0 +1,18 @@
+<template>
+    <div class="body">
+
+    </div>
+
+</template>
+<script>
+    export default {
+        data() {
+            return {
+
+            }
+        }
+    }
+</script>
+<style lang="less" scoped>
+
+</style>

+ 64 - 106
TEAMModelOS/ClientApp/src/components/learnactivity/GradeTable.vue

@@ -1,126 +1,84 @@
 <template>
-        <div id="grade"></div>
+    <div>
+        <Table border :columns="columns" :data="studentPageData">
+            <template slot-scope="{ row, index }" slot="action">
+                <Button type="primary" size="small" style="margin-right: 5px" @click="show(index)">查看</Button>
+            </template>
+        </Table>
+        <div style="width:100%;text-align:center;margin-top:20px;" class="dark-iview-page">
+            <Page :total="studentList.length" size="small" :current.sync="currentPage" :page-size="pageSize" show-sizer show-total @on-change="getPageData" @on-page-size-change="getPageData($event,'size')" />
+        </div>
+    </div>
 </template>
 <script>
     export default {
+        props: {
+            studentData: {
+                type: Array,
+                default: () => {
+                    return []
+                }
+            },
+        },
         data() {
             return {
-                dataIndex: 1,
-                progressHistogram: undefined,
-                dataInfo: [],
+                pageSize: 10,
+                currentPage: 1,
+                columns: [
+                    {
+                        title: '姓名',
+                        key: 'name',
+                        align: 'center'
+                    },
+                    {
+                        title: '总分',
+                        key: 'score',
+                        align: 'center'
+                    },
+                    {
+                        title: '选择题得分',
+                        key: 'autoQue',
+                        align: 'center'
+                    },
+                    {
+                        title: '主观题得分',
+                        key: 'manualQue',
+                        align: 'center'
+                    },
+                    {
+                        title: '操作',
+                        slot: 'action',
+                        width: 150,
+                        align: 'center'
+                    }
+                ],
                 studentList: [],
-                studentName: [],
-                studentScore:[]
+                studentPageData:[]
             }
         },
         methods:{
             getData() {
-                let data = this.$Mock.data.studentList
-                console.log(data)
-                //for (let i = 0; i < data.length; i++) {
-                //    this.studentName.push(data.)
-                //}
-                //for (var i = 0; i < data[0].studentScore.length; i++) {
-                //    this.studentList.push(data[0].studentScore[i].name);
-                //    this.studentScore.push(data[0].studentScore[i].score);
-                //}
-                //for (var i = 0; i < data[0].studentScore.length; i++) {
-                //    this.studentList.push(data[0].studentScore[i].name);
-                //    this.studentScore.push(data[0].studentScore[i].score);
-                //}
+                this.studentList = this.studentData
+            },
+            getPageData(number, type) {  //分页
+                let starIndex = this.pageSize * (this.currentPage - 1)
+                let endIndex = this.pageSize * this.currentPage > this.studentList.length ? this.studentList.length : this.pageSize * this.currentPage
+                this.studentPageData = this.studentList.slice(starIndex, endIndex)
             },
-            getMap() {
-                let myChart = this.$echarts.init(document.getElementById('grade'));
-                let option = {
-                    color: ["#42beda"],
-                    tooltip: {},
-                    xAxis: {
-                        "type": "category",
-                        "axisLine": {
-
-                        },
-                        "splitLine": {
-                            "show": false
-                        },
-                        "axisTick": {
-                            "show": false
-                        },
-                        "splitArea": {
-                            "show": false
-                        },
-                        "axisLabel": {
-                            "interval": 0,
-
-                        },
-                        axisLabel: {
-                            color: 'white'
-                        },
-                        data: this.studentList
-                    },
-                    yAxis: {
-                        type: "value",
-                        name: "试卷得分",
-                        lineStyle: {
-                            color: '#fffff',
-                            width: 2
-                        },
-                        nameTextStyle: {
-                            color: "#fff"
-                        },
-                        splitLine: {
-                            lineStyle: {
-                                color: ['#505050']
-                            }
-                        },
-                        axisLabel: {
-                            color: 'white'
-                        },
-                    },
-                    "dataZoom": [{
-                        "show": true,
-                        "height": 10,
-                        "xAxisIndex": [
-                            0
-                        ],
-                        bottom: 10,
-                        color:"#fffff",
-                        "start": 10,
-                        "end": 80,
-                        handleSize: '110%',
-                        handleStyle: {
-                            color: "#fffff",
-
-                        },
-                        borderColor: "#90979c",
-                        handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
-                    },
-                        {
-                        "type": "inside",
-                        "show": true,
-                        "height": 15,
-                        "start": 1,
-                        "end": 35
-                    }],
-                    series: [{
-                        name: '分数',
-                        type: 'bar',
-                        data: this.studentScore
-                    }]
-                };
-                myChart.setOption(option);
-            }
-
         },
         mounted() {
             this.getData()
-            this.getMap()
+            this.getPageData()
+        },
+        watch: {
+            studentData(newValue, oldValue) {
+                deep: true
+                this.getData()
+                this.getPageData()
+            }
         }
     }
 </script>
 <style scoped>
-    #grade{
-        width:700px;
-        height:400px;
-        margin:auto;
-    }
+
 </style>

+ 3 - 3
TEAMModelOS/ClientApp/src/components/questionnaire/BaseQuestionnaire.vue

@@ -50,9 +50,9 @@
 						<!-- 数据分析 -->
 						<transition name="indexFade">
 						      <div v-show="curIndexList.includes(index)">
-								  <BaseQnBar :barId="'bar' + index" :barData="item.result" v-if="curIndexList.includes(index)&& (item.type === 'Single' || item.type ==='Multiple')"></BaseQnBar>
+								  <BaseQnBar :barId="'bar' + index" :barData="item.result || []" v-if="curIndexList.includes(index)&& (item.type === 'Single' || item.type ==='Multiple')"></BaseQnBar>
 							  </div>
-						    </transition>
+						</transition>
 						<!-- 工具栏 -->
 						<div class="qn-tools">
 							<div class="qn-tools-item" @click="onItemEdit(item,index)">
@@ -63,7 +63,7 @@
 								<Icon type="md-trash" />
 								<span>移除</span>
 							</div>
-							<div class="qn-tools-item" @click="onItemAnalysis(index)" v-if="editItem.status === 0 && (item.type === 'Single' || item.type ==='Multiple')">
+							<div class="qn-tools-item" @click="onItemAnalysis(index)" v-if="editItem.status === 300 && (item.type === 'Single' || item.type ==='Multiple')">
 								<Icon type="md-podium" />
 								<span>统计数据</span>
 							</div>

+ 17 - 3
TEAMModelOS/ClientApp/src/mock/index.js

@@ -71,14 +71,28 @@ export default {
       'changesVal2|1-30': 1,
       'changesVal3|1-30': 1
       }],
-      'studentList|1-3': [{
-          'name|+1': ['一班', '二班', '三班', '四班'],
+      'studentList|2-5': [{
+          'name|+1': ['初一一班', '初一二班', '初一三班', '初一四班'],
           'classId|+1': 1400111001,
           'studentScore|45-55': [{
               'name': '@cname',
-              'score|30-100': 100
+              'score|50-100': 100,
+              'autoQue|0-40': 40,
+              'manualQue': function(){ 
+                  return this.score - this.autoQue
+              }
           }]
       }],
+      'studentQue|0-50': [
+          {
+              'name': '@cname',
+              'sueList|10': [
+                  {
+
+                  }
+              ]
+      }
+      ],
     'subjectList|10': [{
       'id|+1': 1,
       'className|1-10': 1,

+ 3 - 3
TEAMModelOS/ClientApp/src/store/module/totalAnalysis.js

@@ -156,7 +156,7 @@ export default {
                     console.log('Cache-examList')
                     r(context.state.examList)
                 } else {
-                    apiTools.totalAnalysis.getExamList({ scopeCode: 'qcs' }).then(res => { // api请求
+                    apiTools.totalAnalysis.getExamList({ code: context.state.campusCode }).then(res => { // api请求
                         let data = res[0]
                         data.name = '第二次测试'
                         res.push(data)
@@ -240,7 +240,7 @@ export default {
             let params = {
                 subjectCode: 'Subject_Math',
                 code: '1371cb0c-6c72-4c1c-868a-aac00e74a0fb',
-                schoolCode:'qcs'
+                schoolCode:context.state.campusCode
             }
             return new Promise((r, j) => {
                 if (context.state.exerciseData) {
@@ -291,7 +291,7 @@ export default {
                 // Subject: context.state.currentSubject
                 subjectCode: 'Subject_Math',
                 type: '5',
-                schoolCode: 'qcs',
+                schoolCode: context.state.campusCode,
                 code: '1371cb0c-6c72-4c1c-868a-aac00e74a0fb'
             }
             return new Promise((r, j) => {

+ 8 - 8
TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.vue

@@ -30,7 +30,7 @@
 								<div class="qn-item-info">
 									<span class="qn-item-nums">
 										<Icon type="md-time" size="14" style="margin-right:5px" />{{ $tools.formatTime(item.startTime) }}</span>
-									<span class="qn-item-status" :style="{ background: (item.state === 100 ? '#0BADD4' : item.state === 200 ? '#0fb68b' : '#949594')}">{{ item.state === 100 ? '待发布' : item.state === 200 ? '进行中' : '已结束' }}</span>
+									<span class="qn-item-status" :style="{ background: (item.status === 100 ? '#0BADD4' : item.status === 200 ? '#0fb68b' : '#949594')}">{{ item.status === 100 ? '待发布' : item.status === 200 ? '进行中' : '已结束' }}</span>
 								</div>
 							</div>
 						</div>
@@ -40,13 +40,13 @@
 			</div>
 
 			<!-- 问卷基础信息展示 -->
-			<div class="qn-col qn-info-box" v-show="currentQn.state !== 300">
+			<div class="qn-col qn-info-box">
 				<div class="qn-box-header">
 					<span>问卷详情</span>
 					<div class="qn-box-header-tools">
-						<span class="qn-box-header-tools-tool" v-show="currentQn.state !== 300 && qnList.length">
+						<span class="qn-box-header-tools-tool" v-show="currentQn.status !== 300 && qnList.length">
 							<Icon type="md-create" size="18" title="编辑" @click="onEditQn" /></span>
-						<span class="qn-box-header-tools-tool" v-show="currentQn.state === 200 && qnList.length">
+						<span class="qn-box-header-tools-tool" v-show="currentQn.status === 200 && qnList.length">
 							<Icon type="md-undo" size="18" title="取消发布" @click="onCancelQn" style="margin-left:8px;" /></span>
 					</div>
 				</div>
@@ -61,11 +61,11 @@
 			</div>
 
 			<!-- 问卷提交数据 -->
-			<div class="qn-col qn-data-box" :style="{ width: currentQn.state !== 300 ? '55%' : '85%' }">
+			<div class="qn-col qn-data-box">
 				<div class="qn-box-header">
 					<span>问卷数据</span>
 					<div class="qn-box-header-tools" v-show="!isEmptyData">
-						<div class="qn-box-header-tools-tool" style="margin-right: 25px;" v-show="currentQn.state !== 300">
+						<div class="qn-box-header-tools-tool" style="margin-right: 25px;" v-show="currentQn.status !== 300">
 							<Icon type="md-list-box" color="#dcdcdc" />
 							<Dropdown @on-click="onAddItem">
 								<Button>
@@ -79,7 +79,7 @@
 								</DropdownMenu>
 							</Dropdown>
 						</div>
-						<div class="qn-box-header-tools-tool" style="margin-right: 25px;" @click="onShowAllAnalysis" v-show="currentQn.state === 300 || currentQn.state === 0">
+						<div class="qn-box-header-tools-tool" style="margin-right: 25px;" @click="onShowAllAnalysis" v-show="currentQn.status === 300 || currentQn.status === 0">
 							<Icon type="md-podium" color="#dcdcdc" />
 							<span>查看统计数据</span>
 						</div>
@@ -121,7 +121,7 @@
 				qnList: [],
 				studentsList: [],
 				currentQn: {
-					state: 100
+					status: 100
 				},
 				editItem: {},
 				activeQnIndex: null,

+ 10 - 8
TEAMModelOS/ClientApp/src/view/selflearning/ManageHomeWork.vue

@@ -208,20 +208,22 @@
 				let records  = await this.getHwRecord(hwId)
 				console.log(records)
 				//  先查找 作业发布对象关联的学生清单 然后再去判断学生的作答情况
-				this.$api.courseMgmt.getClassroomStudent({ classroomCode: 'HBCN0102' , schoolCode:'HBCN' }).then(res => {
+				this.$api.courseMgmt.getClassroomStudent({ classroomCode: 'HBCN0101' , schoolCode:'HBCN' }).then(res => {
 				    if (!res.error && res.result.data) {
 					    let list = res.result.extend.students
+						// 要根据作答情况 结合两张表 处理表格显示的数据 
 						if(records.length){
 							records.forEach(item => {
 								list.forEach(i => {
-									if(i.studentId === item.code){
-										i.submissionBool = true
-										i.submissionTime = item.submitTime
-										return
-									}
+									// 如果学生已提交 则添加提交信息 如果未提交则跳过
+								    if(i.studentId !== item.code) return
+									i.submissionBool = true
+									i.submissionTime = item.submitTime
+									i.classroom = item.classroom
 								})
 							})
 						}
+						console.log(list)
 				        this.studentsList = list
 				    } else {
 				        this.$Message.error('获取数据失败')
@@ -235,7 +237,7 @@
 			 */
 			getHwRecord(hwId) {
 			    return new Promise((r, j) => {
-			        this.$api.learnActivity.FindRecordByHwId({ homeWorkId: hwId }).then(res => {
+			        this.$api.learnActivity.FindRecordByHwId({ id: hwId }).then(res => {
 			            if (!res.error && res.result.data) {
 			                r(res.result.data)
 			            } else {
@@ -257,7 +259,7 @@
                 //this.backToTop('hwModalScroll')
             },
 
-
+			/* 取消发布作业 */
             onCancelHw() {
                 this.$Modal.confirm({
                     title: '取消发布',

+ 0 - 1
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/index.vue

@@ -7,7 +7,6 @@
         <!--<div class="total-header">
             <BaseHeader></BaseHeader>
         </div>-->
-
         <div class="total-body">
             <!-- 评测列表展示组件 -->
             <BaseExamList @chooseExam="chooseExam" @showEvaluationList="showEvaluationList" ref="examListRef"></BaseExamList>

+ 7 - 1
TEAMModelOS/Controllers/Exam/ExamController.cs

@@ -195,7 +195,9 @@ namespace TEAMModelOS.Controllers
             if (record != null) {
                 //处理客观题自动阅卷
                 Paper paper = await cosmosDBV3Repository.FindByIdPk<Paper>(record.id, record.examCode);
-                if (paper.markConfig != null && paper.markConfig.auto) {
+                //允许自动对客观题阅卷及,及阅卷状态大于2 已完成.
+                if (paper.markConfig != null && paper.markConfig.auto && record.mark>=2) {
+                    // TODO 需要处理已经打分的作答,以防止手动改分后被冲掉。
                     autoMark(paper.item, paper.answers, record.answers, paper.markConfig);
                 }
             }
@@ -205,6 +207,10 @@ namespace TEAMModelOS.Controllers
         public static List<Answer> autoMark(List<ItemInfo> items ,List<Answer> stdAnswers, List<Answer> stuAnswers ,MarkConfig markConfig) {
             int size = stuAnswers.Count;
             for (int i = 0; i < size; i++) {
+                //如果当前题目已经打分则直接跳过。
+                if (stuAnswers[i].score > 0) {
+                    continue;
+                }
                 //客观题
                 if (stuAnswers[i].type.Equals("Single") || stuAnswers[i].type.Equals("Multiple") || stuAnswers[i].type.Equals("Judge")) {
                     //多选题单独处理

+ 13 - 0
TEAMModelOS/Controllers/Syllabus/ItemInfoController.cs

@@ -99,6 +99,19 @@ namespace TEAMModelOS.Controllers
             IdPk idPk = await cosmosDBV3Repository.DeleteAsync<ItemInfo>( request.@params );
             return builder.Data(idPk).build();
         }
+
+        /// <summary>
+        /// 删除
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [HttpPost("deleteAll")]
+        public async Task<BaseJosnRPCResponse> DeleteAll(JosnRPCRequest<Dictionary<string,object>> request)
+        {
+            JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom();
+            List<IdPk> idPk = await cosmosDBV3Repository.DeleteAll<ItemInfo>(request.@params);
+            return builder.Data(idPk).build();
+        }
         /// <summary>
         /// 手动挑题
         /// </summary>

+ 7 - 2
TEAMModelOS/Startup.cs

@@ -16,6 +16,7 @@ using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Hosting;
 using Scrutor;
+using TEAMModelOS.SDK;
 using TEAMModelOS.SDK.Context.Attributes.Azure;
 using TEAMModelOS.SDK.Context.Configuration;
 using TEAMModelOS.SDK.Context.Filter;
@@ -23,6 +24,7 @@ using TEAMModelOS.SDK.Extension.JwtAuth;
 using TEAMModelOS.SDK.Module.AzureBlob.Configuration;
 using TEAMModelOS.SDK.Module.AzureCosmosDB.Configuration;
 using TEAMModelOS.SDK.Module.AzureCosmosDBV3;
+using TEAMModelOS.SDK.Module.AzureServiceBus;
 using TEAMModelOS.SDK.Module.AzureTable.Implements;
 using TEAMModelOS.SDK.Module.AzureTable.Interfaces;
 using TEAMModelOS.Service.Services.ChangeFeed;
@@ -116,6 +118,8 @@ namespace TEAMModelOS
             //使用CosmosDB
             services.AddAzureCosmosDBV3().AddCosmosDBV3Connection(Configuration.GetSection("Azure:CosmosDB").Get<AzureCosmosDBOptions>())
                 .AddCosmosSerializer(new SystemTextJsonCosmosSerializer(new JsonSerializerOptions() { IgnoreNullValues = true }));
+            //使用AzureServiceBus
+            services.AddServiceBus().AddServiceBusOptions(Configuration.GetSection("HaBookAuth:ServiceBus").Get<AzureServiceBusOptions>());
             //HttpContextAccessor,并用来访问HttpContext。
             services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
             //引入Jwt配置
@@ -142,14 +146,15 @@ namespace TEAMModelOS
         }
 
         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
-        public   void Configure(IApplicationBuilder app, IWebHostEnvironment env, IAzureCosmosDBV3Repository cosmosDBV3Repository, IChangeFeedInvoke changeFeedInvoke)
+        public   void Configure(IApplicationBuilder app, IWebHostEnvironment env, IAzureCosmosDBV3Repository cosmosDBV3Repository, IChangeFeedInvoke changeFeedInvoke,IAzureServiceBusService azureServiceBusService)
         {
             if (env.IsDevelopment())
             {
                 app.UseDeveloperExceptionPage();
             }
-           CosmosDict dict=  cosmosDBV3Repository.InitializeDatabase().Result;
+            CosmosDict dict=  cosmosDBV3Repository.InitializeDatabase().Result;
             changeFeedInvoke.MonitorChangeFeed(dict, _services);
+            azureServiceBusService.init();
             app.UseMiddleware<HttpGlobalExceptionInvoke>();
             //以下需要按照順序載入中間件  如果应用调用 UseStaticFiles,请将 UseStaticFiles 置于 UseRouting之前。
             app.UseStaticFiles();

+ 9 - 1
TEAMModelOS/appsettings.Development.json

@@ -31,7 +31,6 @@
     "TeamModelLoginUrl": "https://account.habookaclass.biz/?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJsb2dpbiIsImF1ZCI6ImNoZW5nZHVMb2dpbiIsImlzcyI6Imh0dHBzOi8vYXBpLmhhYm9va2FjbGFzcy5iaXoiLCJpYXQiOjE1MzYxMzUwNDcsIm5iZiI6MTUzNjEzNTA0NywiZXhwIjoxNTY3NTU1MjAwLCJpZHAiOiJIYWJvb2sgQ29yZVNlcnZpY2UifQ.F4AnkbJrMRoZvJ6SC-lqZEYIYSoq5x8lvX6_a3YqSgM&callback=",
     "AccountUrl": "https://api.habookaclass.biz/account",
     "ServiceUrl": "https://api.habookaclass.biz/service",
-    "ServiceBus": "Endpoint=sb://teammodelos.servicebus.chinacloudapi.cn/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=Sy4h4EQ8zP+7w/lOLi1X3tGord/7ShFHimHs1vC50Dc=",
     "UserInfoKey": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJpZCIsImF1ZCI6ImNoZW5nZHVJZCIsImlzcyI6Imh0dHBzOi8vYXBpLmhhYm9va2FjbGFzcy5iaXoiLCJpYXQiOjE1MzYwNTIzNjcsIm5iZiI6MTUzNjA1MjM2NywiZXhwIjoxNTY3NTU1MjAwLCJpZHAiOiJIYWJvb2sgQ29yZVNlcnZpY2UifQ.RGKDVtwFEp4OBctlHOuF6yqyI21fTz4cinCxjFCxkSQ",
     "SchoolCodeKey": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzY2hvb2xDb2RlIiwiYXVkIjoiY2hlbmdkdVNjaG9vbENvZGUiLCJpc3MiOiJodHRwczovL2FwaS5oYWJvb2thY2xhc3MuYml6IiwiaWF0IjoxNTM2MDUyNDI3LCJuYmYiOjE1MzYwNTI0MjcsImV4cCI6MTU2NzU1NTIwMCwiaWRwIjoiSGFib29rIENvcmVTZXJ2aWNlIn0.8m5VH3Nz4N9EdMz8AexTOEuDVitcJZFKy9DfW_UQkSY",
     "SmsKey": "Basic ZmYwMWM0YTJjODdmZmNkYTUyNjhmMDEwOmE0YTE5YTVjNTU2ZWVhZTNjZmZhNTI0Mg==",
@@ -47,6 +46,15 @@
         "refresh_token": "{access_token}"
       },
       "url": "https://api2.teammodel.cn/oauth2/token"
+    },
+    "ServiceBus": {
+      "ConnectionString": "Endpoint=sb://teammodelos.servicebus.chinacloudapi.cn/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=Sy4h4EQ8zP+7w/lOLi1X3tGord/7ShFHimHs1vC50Dc=",
+      "Topics": [
+        {
+          "Name": "test_topic_ActiveTask",
+          "Subscribe": [ "test_topic_ReciveTask" ]
+        }
+      ]
     }
   },
   "SmsSendCloud": {