Sfoglia il codice sorgente

Merge branch 'develop' into develop-rc

CrazyIter_Bin 3 anni fa
parent
commit
a93f887680
100 ha cambiato i file con 10026 aggiunte e 3171 eliminazioni
  1. 1 1
      TEAMModelBI/ClientApp/public/index.html
  2. 21 0
      TEAMModelBI/ClientApp/src/api/index.js
  3. BIN
      TEAMModelBI/ClientApp/src/assets/img/bg-alsit.png
  4. BIN
      TEAMModelBI/ClientApp/src/assets/img/bg-index.jpg
  5. 4 2
      TEAMModelBI/ClientApp/src/components/echarts/commonBar.vue
  6. 5 4
      TEAMModelBI/ClientApp/src/components/echarts/commonLine.vue
  7. 158 73
      TEAMModelBI/ClientApp/src/components/echarts/test.vue
  8. 11 9
      TEAMModelBI/ClientApp/src/components/echarts/test/HiteachBar.vue
  9. 1 1
      TEAMModelBI/ClientApp/src/components/echarts/test/online.vue
  10. 12 10
      TEAMModelBI/ClientApp/src/components/echarts/test/onlineType.vue
  11. 1 1
      TEAMModelBI/ClientApp/src/components/echarts/test/size.vue
  12. 18 0
      TEAMModelBI/ClientApp/src/router/index.js
  13. 6 1
      TEAMModelBI/ClientApp/src/store/index.js
  14. 27 0
      TEAMModelBI/ClientApp/src/view/common/aside.vue
  15. 1152 808
      TEAMModelBI/ClientApp/src/view/index/index.vue
  16. 929 0
      TEAMModelBI/ClientApp/src/view/participation/index.vue
  17. 1442 0
      TEAMModelBI/ClientApp/src/view/participation/setAbility.vue
  18. 2170 0
      TEAMModelBI/ClientApp/src/view/participation/setPhase.vue
  19. 75 5
      TEAMModelBI/ClientApp/src/view/schoolServe/analyseSchool.vue
  20. 18 10
      TEAMModelBI/ClientApp/src/view/schoolServe/school.vue
  21. 3 2
      TEAMModelBI/ClientApp/src/view/schoolServe/setschool.vue
  22. 321 0
      TEAMModelBI/Controllers/BIHome/OnLineController.cs
  23. 1 1
      TEAMModelBI/Controllers/BISchool/BatchSchoolController.cs
  24. 1 1
      TEAMModelBI/Controllers/BISchool/SchoolController.cs
  25. 75 0
      TEAMModelBI/Controllers/BITest/Ies5TestController.cs
  26. 30 30
      TEAMModelBI/Controllers/BITest/TestController.cs
  27. 41 0
      TEAMModelBI/Tool/CommonFind.cs
  28. 39 13
      TEAMModelBI/Tool/TimeHelper.cs
  29. 1 1
      TEAMModelBI/appsettings.json
  30. 1 1
      TEAMModelOS.FunctionV4/CosmosDB/TriggerExam.cs
  31. 38 14
      TEAMModelOS.FunctionV4/ServiceBus/ActiveTaskTopic.cs
  32. 3 3
      TEAMModelOS.FunctionV4/TEAMModelOS.FunctionV4.csproj
  33. 1 0
      TEAMModelOS.SDK/Context/Constant/Constant.cs
  34. 7 0
      TEAMModelOS.SDK/Models/Cosmos/Common/LessonRecord.cs
  35. 1 0
      TEAMModelOS.SDK/Models/Cosmos/School/Paper.cs
  36. 19 2
      TEAMModelOS.SDK/Models/Cosmos/School/SchoolSetting.cs
  37. 4 1
      TEAMModelOS.SDK/Models/Service/Common/TeacherService.cs
  38. 4 4
      TEAMModelOS.SDK/Models/Service/GroupListService.cs
  39. 349 0
      TEAMModelOS.SDK/Models/Service/LessonService.cs
  40. 308 23
      TEAMModelOS.SDK/Models/Service/LoginService.cs
  41. 4 1
      TEAMModelOS.SDK/Models/Service/Third/Xkw/XkwOAuthModel.cs
  42. 20 19
      TEAMModelOS.SDK/Models/Table/IESLogin.cs
  43. 12 2
      TEAMModelOS/ClientApp/public/index.html
  44. 43 8
      TEAMModelOS/ClientApp/public/lang/en-US.js
  45. 46 11
      TEAMModelOS/ClientApp/public/lang/zh-CN.js
  46. 147 112
      TEAMModelOS/ClientApp/public/lang/zh-TW.js
  47. 1 1
      TEAMModelOS/ClientApp/src/access/login.js
  48. 1 1
      TEAMModelOS/ClientApp/src/api/lessonRecord.js
  49. 20 3
      TEAMModelOS/ClientApp/src/api/login.js
  50. BIN
      TEAMModelOS/ClientApp/src/assets/image/import-teacher-tw.png
  51. 40 2
      TEAMModelOS/ClientApp/src/common/BaseLayout.vue
  52. 1 1
      TEAMModelOS/ClientApp/src/common/BaseSelectTch.vue
  53. 1 0
      TEAMModelOS/ClientApp/src/components/evaluation/ExerciseList.less
  54. 4 0
      TEAMModelOS/ClientApp/src/components/evaluation/ExerciseList.vue
  55. 1 1
      TEAMModelOS/ClientApp/src/components/homework/BaseHwForm.vue
  56. 16 3
      TEAMModelOS/ClientApp/src/components/homework/BaseHwTable.vue
  57. 509 596
      TEAMModelOS/ClientApp/src/components/selflearn/ExerciseList.less
  58. 2 2
      TEAMModelOS/ClientApp/src/components/syllabus/InviteTeacher.vue
  59. 6 20
      TEAMModelOS/ClientApp/src/locale/index.js
  60. 4 0
      TEAMModelOS/ClientApp/src/store/module/config.js
  61. 3 0
      TEAMModelOS/ClientApp/src/store/module/user.js
  62. 102 6
      TEAMModelOS/ClientApp/src/utils/public.js
  63. 1 0
      TEAMModelOS/ClientApp/src/view/403.vue
  64. 1 0
      TEAMModelOS/ClientApp/src/view/404.vue
  65. 1 0
      TEAMModelOS/ClientApp/src/view/Home.vue
  66. 1 0
      TEAMModelOS/ClientApp/src/view/areaMgmt/AreaBase.vue
  67. 10 16
      TEAMModelOS/ClientApp/src/view/classmgt/ClassNotice.vue
  68. 130 82
      TEAMModelOS/ClientApp/src/view/dashboard/Index.vue
  69. 3 0
      TEAMModelOS/ClientApp/src/view/evaluation/bank/TestPaperList.vue
  70. 1 3
      TEAMModelOS/ClientApp/src/view/evaluation/index/CommonExercise.less
  71. 7 5
      TEAMModelOS/ClientApp/src/view/evaluation/index/TestPaper.vue
  72. 4 4
      TEAMModelOS/ClientApp/src/view/homework/ManageHomeWork.vue
  73. 2 0
      TEAMModelOS/ClientApp/src/view/learnactivity/CreatePrivEva.vue
  74. 8 7
      TEAMModelOS/ClientApp/src/view/learnactivity/CreateSchoolEva.vue
  75. 16 7
      TEAMModelOS/ClientApp/src/view/learnactivity/ExamMgt.vue
  76. 6 1
      TEAMModelOS/ClientApp/src/view/learnactivity/StuReport.vue
  77. 11 1
      TEAMModelOS/ClientApp/src/view/learnactivity/tabs/AnswerTable.vue
  78. 7 2
      TEAMModelOS/ClientApp/src/view/login/page/Student.vue
  79. 10 4
      TEAMModelOS/ClientApp/src/view/newcourse/MyCourse.vue
  80. 1 1
      TEAMModelOS/ClientApp/src/view/newsheet/index.vue
  81. 2 2
      TEAMModelOS/ClientApp/src/view/questionnaire/ManageQuestionnaire.vue
  82. 153 116
      TEAMModelOS/ClientApp/src/view/research-center/BaseCleanCond.vue
  83. 73 43
      TEAMModelOS/ClientApp/src/view/research-center/ResearchMgt.less
  84. 930 802
      TEAMModelOS/ClientApp/src/view/research-center/ResearchMgt.vue
  85. 1 0
      TEAMModelOS/ClientApp/src/view/schoolList/Index.vue
  86. 224 223
      TEAMModelOS/ClientApp/src/view/settings/Index.vue
  87. 2 1
      TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/EvaluationList/TotalIndex.vue
  88. 1 0
      TEAMModelOS/ClientApp/src/view/student-web/App.vue
  89. 8 5
      TEAMModelOS/ClientApp/src/view/teachcontent/UploadModal.vue
  90. 1 1
      TEAMModelOS/ClientApp/src/view/vote/ManageVote.vue
  91. 11 7
      TEAMModelOS/Controllers/Both/GroupListController.cs
  92. 49 14
      TEAMModelOS/Controllers/Both/LessonRecordController.cs
  93. 3 3
      TEAMModelOS/Controllers/Both/PaperController.cs
  94. 2 0
      TEAMModelOS/Controllers/Both/SheetConfigController.cs
  95. 15 1
      TEAMModelOS/Controllers/Client/HiScanController.cs
  96. 15 1
      TEAMModelOS/Controllers/Client/HiTAControlller.cs
  97. 27 1
      TEAMModelOS/Controllers/Client/HiTeachController.cs
  98. 5 5
      TEAMModelOS/Controllers/Common/ExamController.cs
  99. 3 3
      TEAMModelOS/Controllers/Common/HomeworkController.cs
  100. 0 0
      TEAMModelOS/Controllers/School/SchoolSettingController.cs

+ 1 - 1
TEAMModelBI/ClientApp/public/index.html

@@ -11,7 +11,7 @@
     </title>
 </head>
 <script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script>
-<script src="https://at.alicdn.com/t/font_2934132_mqzvxjqejqd.js"></script>
+<script src="https://at.alicdn.com/t/font_2934132_ghuo4dq8cwd.js"></script>
 
 <body>
     <noscript>

+ 21 - 0
TEAMModelBI/ClientApp/src/api/index.js

@@ -257,5 +257,26 @@ export default {
     //变更BI权限和身份
     setRolesandPower(data) {
         return post('/tabledd/set-rolesper', data)
+    },
+
+
+
+
+    //首页运维数据
+    //首页基础数据统计
+    getAllcount(data) {
+        return post('/online/get-count', data)
+    },
+    //获得在线人数
+    getOnline(data) {
+        return post('/online/get-trend', data)
+    },
+    //获取课例活跃和Hiteach开课
+    getLessontrend(data) {
+        return post('/online/get-lessontrend', data)
+    },
+    //获取版本占比
+    getVersions(data) {
+        return post('/online/get-edition', data)
     }
 }

BIN
TEAMModelBI/ClientApp/src/assets/img/bg-alsit.png


BIN
TEAMModelBI/ClientApp/src/assets/img/bg-index.jpg


+ 4 - 2
TEAMModelBI/ClientApp/src/components/echarts/commonBar.vue

@@ -18,7 +18,7 @@ export default {
         },
         barData: {
             type: Object,
-            default: () => {},
+            default: () => { },
         },
         title: {
             type: String,
@@ -59,6 +59,7 @@ class InitChart {
         this.state.chart && this.destory()
         this.state.chart = echarts.init(this.myEcharts.value)
         this.state.chart.setOption({
+            color: datas.color ? datas.color : '',
             title: datas.title ? datas.title : '',
             tooltip: datas.tooltip ? datas.tooltip : '',
             legend: datas.legend ? datas.legend : '',
@@ -81,5 +82,6 @@ class InitChart {
     }
 }
 </script>
-<style lang="less"></style>
+<style lang="less">
+</style>
 

+ 5 - 4
TEAMModelBI/ClientApp/src/components/echarts/commonLine.vue

@@ -5,7 +5,6 @@
 import { ref, onMounted, nextTick, watch, getCurrentInstance } from 'vue'
 import * as echarts from 'echarts'
 export default {
-    name: 'baseBar',
     props: {
         width: {
             type: String,
@@ -17,7 +16,7 @@ export default {
         },
         lineData: {
             type: Array,
-            default: () => {},
+            default: () => { },
         },
         title: {
             type: String,
@@ -59,8 +58,9 @@ class InitChart {
         this.state.chart = echarts.init(this.myEcharts.value)
         this.state.chart.setOption({
             title: datas.title ? datas.title : {},
-            backgroundColor: datas.backgroundColor ? datas.backgroundColor : '#fff',
+            backgroundColor: datas.backgroundColor ? datas.backgroundColor : '',
             grid: datas.grid ? datas.grid : '',
+            legend: datas.legend ? datas.legend : {},
             tooltip: {
                 trigger: 'axis',
                 axisPointer: {
@@ -88,5 +88,6 @@ class InitChart {
     }
 }
 </script>
-<style lang="less"></style>
+<style lang="less">
+</style>
 

+ 158 - 73
TEAMModelBI/ClientApp/src/components/echarts/test.vue

@@ -6,7 +6,6 @@
 import { ref, onMounted, nextTick, watch, getCurrentInstance } from 'vue'
 import * as echarts from 'echarts'
 export default {
-    name: 'baseBar',
     props: {
         width: {
             type: String,
@@ -16,9 +15,9 @@ export default {
             type: String,
             default: '100%',
         },
-        mapData: {
-            type: Array,
-            default: () => [],
+        meterData: {
+            type: Object,
+            default: () => {},
         },
         title: {
             type: String,
@@ -26,18 +25,17 @@ export default {
         },
     },
     setup(props) {
-        console.log(props.mapData, '传进来的值')
         const myEcharts = ref(null)
         let { proxy } = getCurrentInstance()
         const chart = new InitChart(props, myEcharts)
         onMounted(() => {
-            chart.init(props.mapData, proxy)
+            chart.init(props.barData, proxy)
         })
         watch(
             props,
             (nweProps) => {
                 nextTick(() => {
-                    chart.init(props.mapData, proxy)
+                    nweProps ? chart.init(props.barData, proxy) : ''
                 })
             },
             { immediate: true, deep: true }
@@ -56,92 +54,179 @@ class InitChart {
         }
     }
     init(datas, proxy) {
+        var xdata = ['去年同月累计', '去年同月', '年初至本月累计', '本月']
+        var ydata = ['课例数量', '科技互动次数', '苏格拉底影片', '影片双绿灯数', '模组服务购买使用']
+        var vdata1 = [870, 799, 260, 130, 23]
+        var vdata2 = [260, 180, 21, 15, 5]
+        var vdata3 = [652, 378, 120, 167, 45]
+        var vdata4 = [267, 356, 59, 45, 10]
+        var fontColor = '#30eee9'
+        var piedata1 = [
+            {
+                name: '小学',
+                value: 25,
+            },
+            {
+                name: '初中',
+                value: 10,
+            },
+            {
+                name: '高中',
+                value: 5,
+            },
+            {
+                name: '大学',
+                value: 0,
+            },
+        ]
+        var piedata2 = [
+            {
+                name: '语文',
+                value: 580,
+            },
+            {
+                name: '数学',
+                value: 430,
+            },
+            {
+                name: '英语',
+                value: 495,
+            },
+            {
+                name: '政治',
+                value: 105,
+            },
+            {
+                name: '音乐',
+                value: 230,
+            },
+            {
+                name: '计算机',
+                value: 360,
+            },
+        ]
+        var piedata3 = [
+            {
+                name: '课例',
+                value: 580,
+            },
+            {
+                name: '资源',
+                value: 150,
+            },
+            {
+                name: '活动',
+                value: 320,
+            },
+        ]
         this.state.chart && this.destory()
         this.state.chart = echarts.init(this.myEcharts.value)
         this.state.chart.setOption({
-            title: {
-                text: '研修完成度',
+            backgroundColor: '#fff',
+            // title: {
+            //     text: '2020年1-4月港口吞吐量情况',
+            //     fontColor: '#FFF',
+            // },
+            color: ['#3fb1e3', '#6be6c1', '#626c91', '#a0a7e6', '#c4ebad', '#96dee8'],
+            legend: {
+                data: xdata,
                 textStyle: {
-                    color: '#595959',
-                    fontSize: 14,
+                    color: fontColor,
                 },
-                // subtext: '年度任务总数:18个',
-                // subtextStyle: {
-                //     fontSize: 14,
-                //     color: '#8C8C8C',
-                // },
-                itemGap: 20,
-                left: 'center',
-                top: '55%',
-            },
-            angleAxis: {
-                max: 100,
-                clockwise: true, // 逆时针
-                // 隐藏刻度线
-                show: false,
-            },
-            radiusAxis: {
-                type: 'category',
-                show: true,
+                top: '1%',
+            },
+            tooltip: {},
+            xAxis: {
+                type: 'value',
                 axisLabel: {
-                    show: false,
+                    color: fontColor,
                 },
-                axisLine: {
-                    show: false,
+                splitLine: {
+                    lineStyle: {
+                        type: 'dashed',
+                        color: fontColor,
+                    },
                 },
-                axisTick: {
-                    show: false,
+                axisLine: {
+                    show: true,
+                    lineStyle: {
+                        color: fontColor,
+                    },
                 },
             },
-            polar: {
-                center: ['50%', '50%'],
-                radius: '110%', //图形大小
+            yAxis: {
+                data: ydata,
+                axisLabel: {
+                    color: fontColor,
+                },
+                axisLine: {
+                    show: true,
+                    lineStyle: {
+                        color: fontColor,
+                    },
+                },
             },
+            grid: [
+                {
+                    bottom: '55%',
+                    top: '10%',
+                    left: '10%',
+                    right: '10%',
+                },
+            ],
             series: [
                 {
+                    name: '去年同月累计',
                     type: 'bar',
-                    data: [60],
-                    showBackground: true,
-                    coordinateSystem: 'polar',
-                    roundCap: true,
-                    barWidth: 8,
-                    itemStyle: {
-                        normal: {
-                            opacity: 1,
-                            color: '#1890FF',
-                        },
-                    },
-                    z: 10,
+                    data: vdata1,
                 },
                 {
+                    name: '去年同月',
                     type: 'bar',
-                    data: [100],
-                    showBackground: true,
-                    barGap: '-100%',
-                    coordinateSystem: 'polar',
-                    roundCap: true,
-                    barWidth: 8,
-                    itemStyle: {
-                        normal: {
-                            opacity: 1,
-                            color: '#E7E9EB',
-                        },
-                    },
+                    data: vdata2,
                 },
                 {
+                    name: '年初至本月累计',
+                    type: 'bar',
+                    data: vdata3,
+                },
+                {
+                    name: '本月',
+                    type: 'bar',
+                    data: vdata4,
+                },
+                {
+                    name: '学区学段占比',
                     type: 'pie',
-                    data: [1],
-                    radius: '90%',
-                    itemStyle: {
-                        color: 'transparent',
-                    },
-                    label: {
-                        show: true,
-                        position: 'center',
-                        formatter: 60 + '%',
-                        color: '#1890FF',
-                        fontSize: 22,
-                    },
+                    radius: '65%',
+                    //center: ['50%', '50%'],
+                    data: piedata1,
+                    bottom: '0%',
+                    top: '55%',
+                    left: '0%',
+                    right: '65%',
+                },
+                {
+                    name: '学区科目占比',
+                    type: 'pie',
+                    radius: '65%',
+                    //center: ['50%', '50%'],
+                    data: piedata2,
+                    bottom: '0%',
+                    top: '55%',
+                    left: '33%',
+                    right: '32%',
+                },
+                {
+                    name: '学区数据占比',
+                    type: 'pie',
+                    radius: '65%',
+                    //center: ['50%', '50%'],
+                    data: piedata3,
+                    bottom: '0%',
+                    top: '55%',
+                    left: '65%',
+                    right: '0%',
                 },
             ],
         })

+ 11 - 9
TEAMModelBI/ClientApp/src/components/echarts/test/HiteachBar.vue

@@ -90,7 +90,7 @@ class InitChart {
         this.state.chart = echarts.init(this.myEcharts.value)
         console.log(this.state.chart, 'chart!!!')
         this.state.chart.setOption({
-            backgroundColor: '#fff',
+            // backgroundColor: '#fff',
             tooltip: {
                 trigger: 'axis',
                 axisPointer: {
@@ -113,35 +113,37 @@ class InitChart {
                 data: xData,
                 axisLine: {
                     lineStyle: {
-                        color: 'rgba(66, 192, 255, .3)',
+                        color: 'rgba(189, 195, 199,0.5)',
                     },
                 },
                 axisLabel: {
                     textStyle: {
-                        color: '#999',
+                        color: 'rgba(189, 195, 199,0.5)',
                     },
                 },
             },
             yAxis: [
                 {
                     type: 'value',
-                    splitLine: {
-                        show: true,
-                    },
                     axisLabel: {
                         textStyle: {
-                            color: '#5FBBEB',
+                            color: 'rgba(189, 195, 199,0.5)',
                         },
                     },
                     axisLine: {
                         lineStyle: {
                             fontSize: 12,
-                            color: 'rgba(66, 192, 255, .3)',
+                            color: 'rgba(189, 195, 199,0.5)',
                         },
                     },
+                    splitLine: { lineStyle: { color: ['rgba(189, 195, 199,0.5)'], type: [5, 8], dashOffset: 3 } },
                 },
                 {
                     type: 'value',
+                       splitLine: {
+                        show: true,
+                        lineStyle: { color: ['rgba(189, 195, 199,0.5)'], type: [5, 8], dashOffset: 3 } 
+                    },
                 },
             ],
             dataZoom: [
@@ -150,7 +152,7 @@ class InitChart {
                     height: 10,
                     xAxisIndex: [0, 1],
                     bottom: 10,
-                    right: '8%',
+                    right: '10%',
                     left: '5%',
                     start: 0,
                     end: 30,

+ 1 - 1
TEAMModelBI/ClientApp/src/components/echarts/test/online.vue

@@ -86,7 +86,7 @@ class InitChart {
         this.state.chart && this.destory()
         this.state.chart = echarts.init(this.myEcharts.value)
         this.state.chart.setOption({
-            backgroundColor: '#fff',
+            // backgroundColor: '#fff',
             grid: {
                 x: 0,
                 y: 0,

+ 12 - 10
TEAMModelBI/ClientApp/src/components/echarts/test/onlineType.vue

@@ -16,7 +16,7 @@ export default {
         },
         lineData: {
             type: Array,
-            default: () => {},
+            default: () => { },
         },
         title: {
             type: String,
@@ -85,21 +85,21 @@ class InitChart {
         this.state.chart && this.destory()
         this.state.chart = echarts.init(this.myEcharts.value)
         this.state.chart.setOption({
-            backgroundColor: '#fff',
+            // backgroundColor: '#fff',
             // legend: { orient: 'vertical', top: 0, right: 0, itemWidth: 10, itemHeight: 10, icon: 'circle', data: ['老师', '学生'] },
             grid: { left: 0, top: 10, bottom: 20, right: '10%', containLabel: true },
             xAxis: {
                 type: 'category',
                 data: xData,
-                axisLine: { lineStyle: { color: '#ccc' } },
+                axisLine: { lineStyle: { color: 'rgba(189, 195, 199,0.5)' } },
                 axisTick: { length: 3 },
-                axisLabel: { color: '#999' },
+                axisLabel: { color: 'rgba(189, 195, 199,0.5)' },
             },
             yAxis: {
                 type: 'value',
-                axisLine: { show: true, lineStyle: { color: '#ccc' } },
-                axisLabel: { color: '#999' },
-                splitLine: { lineStyle: { color: ['#CEEDFF'], type: [5, 8], dashOffset: 3 } },
+                axisLine: { show: true, lineStyle: { color: 'rgba(189, 195, 199,0.5)' } },
+                axisLabel: { color: 'rgba(189, 195, 199,0.5)' },
+                splitLine: { lineStyle: { color: ['rgba(189, 195, 199,0.5)'], type: [5, 8], dashOffset: 3 } },
             },
             tooltip: {
                 trigger: 'axis',
@@ -141,10 +141,11 @@ class InitChart {
                     end: 100,
                 },
             ],
-            color: ['#62F4D1', '#52A8FF', '#006EFE', '#A3D7FF'],
+            color: ['#62F4D1', '#52A8FF', '#A3D7FF', '#006EFE'],
             series: [
                 { name: '老师', type: 'bar', barWidth: 20, stack: 'total', itemStyle: { borderRadius: 0 }, data: [10, 15, 1, 10, 0, 0, 0] },
-                { name: '学生', type: 'bar', barWidth: 20, stack: 'total', itemStyle: { borderRadius: 0 }, data: [5, 0, 0, 8, 0, 0, 2] },
+                { name: '学校学生', type: 'bar', barWidth: 20, stack: 'total', itemStyle: { borderRadius: 0 }, data: [5, 0, 0, 8, 0, 0, 2] },
+                { name: '醍摩豆学生', type: 'bar', barWidth: 20, stack: 'total', itemStyle: { borderRadius: 0 }, data: [5, 0, 0, 8, 0, 0, 2] },
             ],
         })
         window.addEventListener('resize', () => {
@@ -160,5 +161,6 @@ class InitChart {
     }
 }
 </script>
-<style lang="less"></style>
+<style lang="less">
+</style>
 

+ 1 - 1
TEAMModelBI/ClientApp/src/components/echarts/test/size.vue

@@ -62,7 +62,7 @@ class InitChart {
         this.state.chart = echarts.init(this.myEcharts.value)
         this.state.chart.setOption({
             color: ['#3fb1e3', '#6be6c1', '#626c91', '#a0a7e6', '#dd6b66', '#759aa0', '#e69d87', '#8dc1a9', '#ea7e53', '#73b9bc', '#7289ab', '#91ca8c', '#f49f42'],
-            backgroundColor: '#fff',
+            // backgroundColor: '#fff',
             title: {
                 text: ['{a|总空间:}', `{b|${sizenum.all}G}` + '\n\n' + '{c|已使用:}', `{d|${sizenum.usesize}G}` + '\n\n' + `{e|剩余空间:}`, `{f|${sizenum.residue}G}`].join(''),
                 top: '35%',

+ 18 - 0
TEAMModelBI/ClientApp/src/router/index.js

@@ -136,6 +136,24 @@ const routes = [{
                 isShow: true,
                 component: () => require.ensure([], (require) => require(`@/view/schoolmanage/index.vue`))
             },
+            //我参与的 *学校管理(主要顾问使用)
+            {
+                name: "participation",
+                path: "participation",
+                permission: "batcharea-read|batcharea-upd|batchschool-read|batchschool-upd",
+                roles: ['admin', 'assist'],
+                isShow: true,
+                component: () => require.ensure([], (require) => require(`@/view/participation/index.vue`))
+            },
+            //我参与的 *学校管理(主要顾问使用)
+            {
+                name: "setability",
+                path: "setability",
+                permission: "batcharea-read|batcharea-upd|batchschool-read|batchschool-upd",
+                roles: ['admin', 'assist'],
+                isShow: true,
+                component: () => require.ensure([], (require) => require(`@/view/participation/setAbility.vue`))
+            },
         ]
     },
     {

+ 6 - 1
TEAMModelBI/ClientApp/src/store/index.js

@@ -14,7 +14,8 @@ export default createStore({
         },
         createModel: '',
         changbtnShow: false,
-        schoolPower: {}
+        schoolPower: {},
+        nowSchool: {},
     },
     mutations: {
         //修改组织架构
@@ -58,6 +59,10 @@ export default createStore({
         //当前选中学校的权限内容
         updateSchoolpower(state, value) {
             state.schoolPower = value
+        },
+        //当前选中学校 (用于 我参与的学校管理)
+        schoolDataInfo(state, value) {
+            state.nowSchool = value
         }
     },
     actions: {},

+ 27 - 0
TEAMModelBI/ClientApp/src/view/common/aside.vue

@@ -225,6 +225,33 @@ export default {
                     },
                 ],
             },
+            {
+                name: '我参与的',
+                icon: '#icon-canyu',
+                router: '',
+                role: ['admin', 'assist'],
+                isShow: true,
+                permission: '',
+                sort: 12,
+                child: [
+                    {
+                        name: '学校管理',
+                        router: '/home/participation',
+                        icon: '#icon-xuexiao3',
+                        permission: [],
+                        isShow: true,
+                        sort: 13,
+                    },
+                    {
+                        name: '微能力点管理',
+                        router: '/home/setability',
+                        icon: '#icon-tiaozheng1',
+                        permission: [],
+                        isShow: true,
+                        sort: 14,
+                    },
+                ],
+            },
         ]
         const isCollapse = ref(true)
         const store = useStore()

File diff suppressed because it is too large
+ 1152 - 808
TEAMModelBI/ClientApp/src/view/index/index.vue


+ 929 - 0
TEAMModelBI/ClientApp/src/view/participation/index.vue

@@ -0,0 +1,929 @@
+<template>
+    <!--学校列表-->
+    <div class="schoolbox" v-show="models==='default'">
+        <div class="select-List">
+            <div class="province-box">
+                <span>{{$t(`areaManages.selector.provinceName`)}}:</span>
+                <el-select v-model="provinceOptions.provinceValue" :placeholder="$t(`areaManages.selector.provinceDefault`)" @change="areaSelctChange(provinceOptions.provinceValue,'province')">
+                    <el-option v-for="item in provinceOptions.optionInfo" :key="item.code" :label="item.name" :value="item.name">
+                    </el-option>
+                </el-select>
+            </div>
+            <div class="city-box">
+                <span>{{$t(`areaManages.selector.cityName`)}}:</span>
+                <el-select v-model="cityOptions.cityValue" :placeholder="$t(`areaManages.selector.cityDefault`)" @change="areaSelctChange(cityOptions.cityValue,'city')">
+                    <el-option v-for="item in cityOptions.cityInfo" :key="item.code" :label="item.name" :value="item.name">
+                    </el-option>
+                </el-select>
+            </div>
+            <div class="dist-box">
+                <span>{{$t(`areaManages.selector.areaName`)}}:</span>
+                <el-select v-model="distOptions.distValue" filterable allow-create default-first-option :placeholder="$t(`areaManages.selector.areaDefault`)" @change="areaSelctChange(distOptions.distValue,'dist')">
+                    <el-option v-for="item in distOptions.distInfo" :key="item.code" :label="item.name" :value="item.name">
+                    </el-option>
+                </el-select>
+            </div>
+            <div class="close-box" v-show="provinceOptions.provinceValue || cityOptions.cityValue ||distOptions.distValue">
+                <svg class="closebtnIcon" aria-hidden="true" @click="closeSelectarea">
+                    <use xlink:href="#icon-quxiao"></use>
+                </svg>
+            </div>
+        </div>
+        <!-- <div class="boxselect" v-if="PowerShow">
+            <el-button size="small" @click="createdSchoolbtn">
+                <svg class="created-icon" aria-hidden="true">
+                    <use xlink:href="#icon-chuangjianx"></use>
+                </svg>
+                创建学校
+            </el-button>
+        </div> -->
+        <div class="school-list">
+            <el-table :data="tableData" style="width: 100%" height="75vh" v-loading="loading" element-loading-text="加载中...">
+                <el-table-column prop="index" :label="$t(`schoolManages.tables.serialnum`)" type="index" sortable align="center" />
+                <el-table-column :label="$t(`schoolManages.tables.badge`)" width="150" align="center">
+                    <template #default="scope">
+                        <el-image style="width: 70px; height: 70px" :src="scope.row.picture" fit="fill"></el-image>
+                    </template>
+                </el-table-column>
+                <el-table-column prop="name" :label="$t(`schoolManages.tables.name`)" sortable align="center" />
+                <!-- <el-table-column label="学段" width="150" align="center">
+                    <template #default="scope">
+                        <span>{{scope.row.period[0].name}}</span>
+                    </template>
+                </el-table-column> -->
+                <el-table-column :label="$t(`schoolManages.tables.scale`)" class="school-table-edition" align="center">
+                    <template #default="scope">
+                        <el-image style="width: 80px; height: 80px" :src="imgData.basics" fit="fill" v-if="scope.row.scale ===0"></el-image>
+                        <el-image style="width: 80px; height: 80px" :src="imgData.standard" fit="fill" v-else-if="scope.row.scale ===500 && scope.row.hard.length===0 && scope.row.serial.length ===0 && scope.row.service.length ===0"></el-image>
+                        <el-image style="width: 80px; height: 80px" :src="imgData.specialty" fit="fill" v-else-if="scope.row.scale ===500 && (scope.row.hard.length !=0 || scope.row.serial.length !=0 || scope.row.service.length !=0)"></el-image>
+                    </template>
+                </el-table-column>
+                <el-table-column prop="id" :label="$t(`schoolManages.tables.brevityCode`)" align="center" />
+                <el-table-column label="数据量" sortable :sort-method="schooldataSort" align="center">
+                    <template #default="scope">
+                        <div v-if="scope.row.lessonCount">
+                            <svg class="created-icon" aria-hidden="true">
+                                <use xlink:href="#icon-shuju"></use>
+                            </svg>
+                            {{scope.row.lessonCount}}
+                        </div>
+                        <div v-else>暂无</div>
+                    </template>
+                </el-table-column>
+                <el-table-column prop="location" label="位置" align="center" />
+                <!-- <el-table-column prop="city" :label="$t(`schoolManages.tables.city`)" align="center" />
+                <el-table-column prop="dist" :label="$t(`schoolManages.tables.area`)" align="center" /> -->
+                <el-table-column prop="size" :label="$t(`schoolManages.tables.spacesize`)" align="center" />
+                <el-table-column :label="$t(`schoolManages.tables.assis`)" align="center">
+                    <template #default="scope">
+                        <div v-if="scope.row.assisName">{{scope.row.assisName}}</div>
+                        <div v-else>暂无</div>
+                    </template>
+                </el-table-column>
+                <el-table-column label="模组情况" align="center" width="200">
+                    <template #default="scope">
+                        <div class="have-modules">
+                            <div v-for="item in scope.row.serviceData" :title="item.name" :key="item.id">
+                                <svg class="school-analyse" aria-hidden="true">
+                                    <use :xlink:href="item.icon"></use>
+                                </svg>
+                            </div>
+                        </div>
+                    </template>
+                </el-table-column>
+                <el-table-column label="数据统计" align="center">
+                    <template #default="scope">
+                        <div title="查看学校数据分析" @click="skipAnalyse(scope.row)">
+                            <svg class="school-analyse" aria-hidden="true">
+                                <use xlink:href="#icon-shujufenxi"></use>
+                            </svg>
+                        </div>
+                    </template>
+                </el-table-column>
+                <!-- <el-table-column prop="state" label="状态" width="110" align="center" /> -->
+                <el-table-column :label="$t(`schoolManages.tables.operate`)" align="center" v-if="PowerShow">
+                    <template #default="scope">
+                        <el-button type="primary" size="small" @click.prevent="deleteRow(scope.$index, scope.row)">{{$t(`schoolManages.tables.operatecontent`)}}</el-button>
+                        <!-- <el-button type="danger" size="small" @click="removeSchool(scope.row,scope.$index)">删除</el-button> -->
+                    </template>
+                </el-table-column>
+            </el-table>
+        </div>
+    </div>
+    <!--学校列表end-->
+    <!--编辑学校页面-->
+    <div class="schoolDeatils">
+        <div class="backbtn" v-if="models==='details' && PowerShow">
+            <el-button class="changebtn" v-if="store.state.changbtnShow && changebtns" @click="getSetschool()" size="small">
+                <svg class="changebtn-areaicon" aria-hidden="true">
+                    <use xlink:href="#icon-wenjian"></use>
+                </svg>
+                <span class="changebtn-title">{{$t(`schoolManages.gradSet.save`)}}</span>
+            </el-button>
+            <el-button type="primary" size="small" @click="schoolClose">
+                <svg class="back-icon" aria-hidden="true">
+                    <use xlink:href="#icon-fanhui"></use>
+                </svg>
+                返回
+            </el-button>
+        </div>
+        <el-tabs v-if="models==='details' && PowerShow" @tab-click="changeTabs">
+            <!--基础设置-->
+            <el-tab-pane :label="$t(`schoolManages.redactSet`)">
+                <div class="redactbox" v-show="models==='details' && PowerShow">
+                    <div class="school-formbox">
+                        <el-form ref="form" :model="nowPitchdata" label-width="120px">
+                            <el-form-item :label="$t(`schoolManages.basicSet.name`)+':'" class="school-form-name">
+                                <el-input v-model="nowPitchdata.name"></el-input>
+                            </el-form-item>
+                            <el-form-item :label="$t(`schoolManages.basicSet.badge`)+':'" class="school-form-badge">
+                                <el-upload class="upload-demo-redact" :headers="uploadHeader" action="/blob/upload-public" :before-upload="changeBadge" :on-success="success" :on-error="handleUpdErr">
+                                    <el-image style="width: 100%; height:125px" :src="nowPitchdata.picture" fit="contain"></el-image>
+                                    <div class="changebadge">
+                                        <el-button>{{$t(`schoolManages.basicSet.badgeChange`)}}</el-button>
+                                    </div>
+                                </el-upload>
+                            </el-form-item>
+                            <!-- <el-form-item label="学段:" class="school-form-grading">
+                                <el-checkbox v-model="nowPitchdata.period[0].value" label="小学"></el-checkbox>
+                                <el-checkbox v-model="nowPitchdata.period[1].value" label="初中"></el-checkbox>
+                                <el-checkbox v-model="nowPitchdata.period[2].value" label="高中"></el-checkbox>
+                                <el-checkbox v-model="nowPitchdata.period[3].value" label="职高"></el-checkbox>
+                                <el-checkbox v-model="nowPitchdata.period[4].value" label="大学"></el-checkbox>
+                            </el-form-item> -->
+                            <el-form-item :label="$t(`schoolManages.basicSet.type`)+':'" class="school-form-grading">
+                                <el-radio v-model="nowPitchdata.type" label="1" size="large">{{$t(`schoolManages.basicSet.ordinary`)}}</el-radio>
+                                <el-radio v-model="nowPitchdata.type" label="2" size="large">{{$t(`schoolManages.basicSet.higherEducation`)}}</el-radio>
+                            </el-form-item>
+                            <!-- <el-form-item :label="$t(`schoolManages.basicSet.nowAssistant`)+':'" class="school-form-admin">
+                                <el-select v-model="adminvalue" multiple @change="assistChange" :placeholder="$t(`schoolManages.basicSet.nowAssistanthint`)">
+                                    <el-option v-for="item in adminoptions" :key="item.name" :label="item.mobile+'——'+item.name" :value="item.tmdId ===null ? '':item.tmdId" :disabled="!item.tmdId">
+                                    </el-option>
+                                </el-select>
+                            </el-form-item> -->
+                            <el-form-item :label="$t(`schoolManages.basicSet.brevityCode`)+':'" class="school-form-code">
+                                <el-input disabled :placeholder="nowPitchdata.id" v-if="nowPitchdata.id !==null" />
+                                <el-input disabled :placeholder="$t(`schoolManages.basicSet.notCode`)" v-else="nowPitchdata.id ==null" />
+                            </el-form-item>
+                            <el-form-item label="所属学区:" class="school-form-area">
+                                <el-select v-model="areaSelect.Selectvalue" :placeholder="$t(`schoolManages.basicSet.region`)">
+                                    <el-option v-for="item in areaSelect.data" :key="item.name" :label="item.name" :value="item.id">
+                                    </el-option>
+                                </el-select>
+                            </el-form-item>
+                            <!-- <el-form-item :label="$t(`schoolManages.basicSet.scale`)+':'" class="school-form-size">
+                                <el-input-number v-model="nowPitchdata.scale" :min="0" :step="100" />人
+                            </el-form-item> -->
+                            <!-- <el-form-item :label="$t(`schoolManages.basicSet.location`)+':'" class="school-form-site">
+                                <el-input disabled :placeholder="nowPitchdata.address" />
+                            </el-form-item> -->
+                            <el-form-item :label="$t(`schoolManages.basicSet.spacesize`)+':'" class="school-form-size">
+                                <el-input-number v-model="nowPitchdata.size" :min="1" :max="1000" />GB
+                            </el-form-item>
+                            <!-- <el-form-item label="学校状态:" class="school-form-state">
+                    <el-switch v-model="value2" active-color="#13ce66" inactive-color="#ff4949" />
+                </el-form-item> -->
+                            <el-form-item>
+                                <el-button type="primary" @click="updateSchoolinfo">{{$t(`schoolManages.basicSet.submit`)}}</el-button>
+                                <el-button @click="schoolClose">{{$t(`commonMsg.closes`)}}</el-button>
+                            </el-form-item>
+                        </el-form>
+                    </div>
+                </div>
+            </el-tab-pane>
+            <el-tab-pane :label="$t(`schoolManages.redactGrading`)">
+                <SetSchool :schoolData="studyPhase" ref="setSchoolData"></SetSchool>
+            </el-tab-pane>
+            <!-- <el-tab-pane :label="$t(`schoolManages.redactServe`)">
+                <Impower :schoolCode="studyPhase"></Impower>
+            </el-tab-pane>
+            <el-tab-pane :label="$t(`schoolManages.redactSerial`)">
+                <Classpower :schoolCode="studyPhase"></Classpower>
+            </el-tab-pane> -->
+        </el-tabs>
+    </div>
+    <!--编辑学校页面end-->
+</template>
+<script>
+import { reactive, ref, getCurrentInstance, toRef } from 'vue'
+import option from '@/static/region.json'
+import { useStore } from 'vuex'
+import { ElMessage, ElLoading, ElMessageBox } from 'element-plus'
+import { useRouter } from 'vue-router'
+import SetSchool from './setPhase.vue'
+const optionsData = option
+export default {
+    components: {
+        SetSchool,
+    },
+    setup() {
+        let { proxy } = getCurrentInstance()
+        let PowerShow = proxy.$access.inspectPower('batchschool-upd')
+        const store = useStore()
+        const routerInfo = useRouter()
+        //为了让表单呈现 暂时的数据,
+        let tableData = ref([])
+        const form = reactive({
+            name: '',
+            region: '',
+            date1: '',
+            date2: '',
+            delivery: false,
+            type: [],
+            resource: '',
+            desc: '',
+        })
+        let adminoptions = ref([])
+        let adminvalue = ref([])
+        const value2 = ref(true)
+        //为了让表单呈现 暂时的数据,
+        let loading = ref(true)
+        let uploadHeader = ref({})
+        let nowPitchdata = ref({
+            address: '',
+            areaId: '',
+            campuses: [],
+            city: '',
+            dist: '',
+            id: '',
+            name: '',
+            picture: '',
+            pk: '',
+            province: '',
+            region: '',
+            schoolCode: '',
+            size: 0,
+            standard: '',
+            admin: '',
+            type: 0,
+            scale: 0,
+        })
+        let originalData = ref([])
+        const options = option
+        const props = {
+            value: 'name',
+            label: 'name',
+            children: 'children',
+            checkStrictly: true,
+        }
+        let models = ref('default')
+        let imags = require('@/assets/img/tmd_logo.png')
+        //地区列表
+        let provinceOptions = ref({
+            optionInfo: [],
+            provinceValue: '',
+        })
+        let cityOptions = ref({
+            cityInfo: [],
+            cityValue: '',
+        })
+        let distOptions = ref({
+            distInfo: [],
+            distValue: '',
+        })
+        let areaSelect = ref({
+            Selectvalue: '',
+            data: [],
+        })
+        //学段管理(传输给子组件)
+        let studyPhase = ref()
+        //存储变更按钮显示show
+        let changebtns = ref(false)
+        let setSchoolData = ref(null)
+        let imgData = ref({
+            basics: require('@/assets/img/basicsEdition.svg'),
+            standard: require('@/assets/img/standardEdition.svg'),
+            specialty: require('@/assets/img/specialtyEdition.svg'),
+        })
+        //模组icon
+        let patternIcon = ref([
+            { id: 1, name: '学情分析', icon: '#icon-tongjifenxi', key: 'YMPCVCIM' },
+            { id: 2, name: '智慧学校管理服务', icon: '#icon-zhihuixiaoyuan', key: 'IPDYZYLC' },
+            { id: 3, name: 'A Class ONE 智慧学伴', icon: '#icon-jxuexiao2', key: '3CLYJ6NP' },
+            { id: 4, name: '数据存储服务空间', icon: '#icon-yuncunchu', key: 'IPALJ6NY' },
+            { id: 5, name: '卷卡合一阅卷系统', icon: '#icon-pingtai_kaoshi', key: 'VABAJ6NV' },
+            { id: 6, name: '教研中心模组', icon: '#icon-jiaoyan', key: 'VLY6J6N6' },
+        ])
+        provinceOptions.value.optionInfo = optionsData
+        console.log(store.state.point)
+        //所有学校列表
+        function getAllschool() {
+            proxy.$api.getSchooldata({}).then((res) => {
+                console.log(res, '获取学校列表assist')
+                //处理关联管家  拼内容
+                res.schoolAssists.splice(3)
+                for (let i in res.schoolAssists) {
+                    res.schoolAssists[i].serviceData = []
+                    if (res.schoolAssists[i].assists) {
+                        res.schoolAssists[i].assisName = ''
+                        res.schoolAssists[i].location = res.schoolAssists[i].province + res.schoolAssists[i].city + res.schoolAssists[i].dist
+                        let datas = res.schoolAssists[i].assists
+                        for (let y in datas) {
+                            res.schoolAssists[i].assisName = res.schoolAssists[i].assisName + datas[y].tmdName + ','
+                        }
+                    }
+                    if (res.schoolAssists[i].service.length > 0) {
+                        res.schoolAssists[i].service.forEach((x) => {
+                            for (let m in patternIcon.value) {
+                                patternIcon.value[m].key === x ? res.schoolAssists[i].serviceData.push(patternIcon.value[m]) : ''
+                            }
+                        })
+                    }
+                }
+                console.log(res)
+                res.state == 200 ? (tableData.value.push(...res.schoolAssists), (originalData.value = res.schoolAssists), (loading.value = false)) : ''
+            })
+        }
+        //点击学校列表,详情
+        function deleteRow(index, data) {
+            console.log(index, data, 'DATA')
+            //处理的基础设置
+            areaSelect.value.data = []
+            studyPhase.value = data.id
+            nowPitchdata.value = Object.assign(nowPitchdata.value, data)
+            nowPitchdata.value.address = data.address
+            nowPitchdata.value.name = data.name
+            nowPitchdata.value.type = data.type.toString()
+            nowPitchdata.value.scale = data.scale
+            nowPitchdata.value.standard = data.standard
+            nowPitchdata.value.areaId = data.areaId
+            data.assists.length
+                ? data.assists.forEach((element) => {
+                      adminvalue.value.push(element.tmdId)
+                  })
+                : ''
+            let token = JSON.parse(localStorage.getItem('id_token'))
+            uploadHeader.value['x-auth-authtoken'] = token
+            data.areaId !== null ? (areaSelect.value.Selectvalue = data.areaId) : ''
+            console.log(store.state.point, '目前现有的所有区')
+            store.state.point.length ? areaSelect.value.data.push(...store.state.point) : getoption()
+            models.value = 'details'
+            console.log(nowPitchdata.value, '查看当前学校的值')
+        }
+        //更换校徽
+        function changeBadge(file) {
+            console.log(file)
+            if (file.type == 'image/png' || file.type == 'image/jpeg') {
+                return true
+            } else {
+                ElMessage.error(proxy.$t(`commonMsg.uploadError`))
+                return false
+            }
+        }
+        //地区选择
+        function areaSelctChange(value, model) {
+            loading.value = true
+            console.log(value)
+            let data = option
+            if (model === 'province') {
+                //传输下一级的数据给select
+                let cityData = data.filter((item) => {
+                    return item.name === value
+                })
+                cityOptions.value.cityInfo = cityData[0].children
+                //遍历list
+                let schoolData = originalData.value.filter((items) => {
+                    return items.province === value
+                })
+                tableData.value = schoolData
+                console.log(schoolData, tableData.value)
+            } else if (model === 'city') {
+                let distData = cityOptions.value.cityInfo.filter((item) => {
+                    return item.name === value
+                })
+                distOptions.value.distInfo = distData[0].children
+                let provinceData = provinceOptions.value.provinceValue
+                let schoolData = originalData.value.filter((items) => {
+                    return items.city === value && items.province === provinceData
+                })
+                tableData.value = schoolData
+            } else if (model === 'dist') {
+                let provinceData = provinceOptions.value.provinceValue
+                let cityData = cityOptions.value.cityValue
+                let schoolData = originalData.value.filter((items) => {
+                    return items.city === cityData && items.province === provinceData && items.dist === value
+                })
+                tableData.value = schoolData
+            }
+            loading.value = false
+        }
+        //学校详情 close
+        function schoolClose() {
+            models.value = 'default'
+            const basics = {
+                areaId: '',
+                campuses: [],
+                city: '',
+                dist: '',
+                id: '',
+                name: '',
+                picture: '',
+                pk: '',
+                province: '',
+                region: '',
+                schoolCode: '',
+                size: 0,
+                standard: '',
+                admin: '',
+            }
+            nowPitchdata.value = Object.assign({}, basics)
+            adminvalue.value = []
+            areaSelect.value.Selectvalue = ''
+            store.commit('updateSchoolpower', {})
+            console.log(nowPitchdata, '取消后的')
+        }
+        //管家发生变化
+        function assistChange(val) {
+            console.log(val, adminvalue.value)
+        }
+        //获取所有的顾问API访问
+        function getAllassists() {
+            // let data = { deptId: '' }
+            // proxy.$api.getAllassist(data).then((res) => {
+            //     res.state === 200 ? adminoptions.value.push(...res.dDAndTmdInfos) : ''
+            // })
+            // let dataA = { deptId: '67690917' }
+            // proxy.$api.getAllassist(dataA).then((res) => {
+            //     console.log(res, '研发A')
+            //     res.state === 200 ? adminoptions.value.push(...res.dDAndTmdInfos) : ''
+            // })
+            proxy.$api.getEveryinfo({}).then((res) => {
+                console.log(res, '顾问返回')
+                res.state === 200 ? adminoptions.value.push(...res.ddUserInfos) : ''
+            })
+            console.log(adminoptions.value, '目前所有顾问')
+        }
+        function handleUpdErr() {
+            ElMessage.error('校徽修改失败')
+        }
+        //上传校徽成功后
+        function success(response, file, fileList) {
+            // this.schoolSetting.picture = response.url
+            console.log(response, file, fileList, '上传成功的返回')
+            nowPitchdata.value.picture = response.url
+            //校徽长传成功自动保存
+            // this.saveData()
+        }
+        // 确认修改学校信息
+        function updateSchoolinfo() {
+            let user = JSON.parse(localStorage.getItem('userData'))
+            //修改表单
+            let assistData = adminvalue.value
+            let periodData = []
+            for (let i in nowPitchdata.value.period) {
+                let num = Number(i) + 1
+                if (nowPitchdata.value.period[i].value === true) {
+                    periodData.push(nowPitchdata.value.period[i].name)
+                }
+            }
+            let updateForm = {
+                name: nowPitchdata.value.name,
+                schoolId: nowPitchdata.value.id,
+                picture: nowPitchdata.value.picture,
+                size: nowPitchdata.value.size,
+                assistId: assistData,
+                scale: nowPitchdata.value.scale,
+                type: parseInt(nowPitchdata.value.type),
+                standard: nowPitchdata.value.standard,
+                areaId: nowPitchdata.value.areaId,
+            }
+            console.log(updateForm, '学校信息')
+            proxy.$api.updateSchoolinfo(updateForm).then((res) => {
+                console.log(res, '修改学校的返回')
+                res.state === 200
+                    ? (ElMessage.success(proxy.$t(`commonMsg.schoolUpdateSuccess`), schoolJoinarea(), schoolClose()), updateSuccess())
+                    : ElMessage.error(proxy.$t(`commonMsg.schoolUpdateError`))
+            })
+        }
+        //学校加入区域或者修改
+        async function schoolJoinarea() {
+            let user = JSON.parse(localStorage.getItem('userData'))
+            if (areaSelect.value.Selectvalue === '' || areaSelect.value.Selectvalue === null) {
+                return
+            }
+            let standname = areaSelect.value.data.filter((item) => {
+                return item.id === areaSelect.value.Selectvalue
+            })
+            let dataInfo = {
+                tmdId: user.tmdId,
+                tmdName: user.tmdName,
+                standard: standname[0].standard,
+                areaId: areaSelect.value.Selectvalue,
+                schoolCode: [nowPitchdata.value.id],
+            }
+            await proxy.$api.areaAddSchool(dataInfo).then((res) => {
+                console.log(res, '学校加入区域API')
+            })
+        }
+        //创建学校btn 跳转
+        function createdSchoolbtn() {
+            routerInfo.push({ path: '/home/created', query: { model: 'schollC' } })
+        }
+        //获取所有区域,学校编辑内呈现
+        function getoption() {
+            proxy.$api.getCapacity({}).then((res) => {
+                areaSelect.value.data.push(...res.areas)
+                store.commit('getPoint', res.areas)
+            })
+        }
+        //修改成功后执行
+        function updateSuccess() {
+            loading.value = true
+            setTimeout(() => {
+                ;(models.value = 'default'), (tableData.value = []), getAllschool()
+            }, 2000)
+        }
+        //地区选择close btn
+        function closeSelectarea() {
+            provinceOptions.value.provinceValue = ''
+            provinceOptions.value.optionInfo = []
+            cityOptions.value.cityValue = ''
+            cityOptions.value.cityInfo = []
+            distOptions.value.distValue = ''
+            distOptions.value.distInfo = []
+            tableData.value = originalData.value
+            provinceOptions.value.optionInfo = optionsData
+        }
+        //关于储存变更按钮
+        function changeTabs(value) {
+            console.log(value.props.label)
+            value.props.label == '学段管理' || value.props.label == '學段管理' ? (changebtns.value = true) : (changebtns.value = false)
+        }
+        //获取子页面的数据
+        function getSetschool() {
+            console.log(setSchoolData.value.nowschoolData)
+            let newData = setSchoolData.value.nowschoolData
+            for (let i in newData.period) {
+                let datas = newData.period[i]
+                for (let u in datas.grades) {
+                    if (datas.grades[u] === '') {
+                        ElMessage.warning(proxy.$t(`schoolManages.gradSet.gradesHint`))
+                        return
+                    }
+                }
+                for (let e in datas.analysis.type) {
+                    if (datas.analysis.type[e].name === '') {
+                        ElMessage.warning(proxy.$t(`schoolManages.gradSet.typeHint`))
+                        return
+                    }
+                }
+            }
+            const loadinginfo = ElLoading.service({
+                lock: true,
+                text: '正在保存,请稍后...',
+                background: 'rgba(0, 0, 0, 0.7)',
+            })
+            proxy.$api
+                .updateSchoolphase(newData)
+                .then((res) => {
+                    res.state === 200 ? (ElMessage.success('保存成功'), (studyPhase.value = res.schoolInfo.id), (changebtns.value = false)) : ElMessage.error('保存失败')
+                    loadinginfo.close()
+                })
+                .catch((res) => {
+                    ElMessage.error(proxy.$t(`commonMsg.apiError`))
+                    loadinginfo.close()
+                })
+        }
+        function skipAnalyse(value) {
+            console.log(value, '学校信息')
+            routerInfo.push({ path: '/home/analyse', query: {} })
+            store.commit('schoolDataInfo', value)
+        }
+        function schooldataSort(a, b) {
+            tableData.value.sort(function (a, b) {
+                return b.lessonCount - a.lessonCount
+            })
+        }
+        function removeSchool(value, index) {
+            console.log(value)
+            ElMessageBox.confirm(`您确定要删除 ${value.name} 学校吗? 请慎重操作!`, '删除学校', {
+                confirmButtonText: proxy.$t(`commonMsg.confirm`),
+                cancelButtonText: proxy.$t(`commonMsg.closes`),
+                type: 'warning',
+                center: true,
+            }).then(() => {
+                let dataArr = tableData.value
+                dataArr.splice(index, 1)
+                ElMessage.success('删除成功')
+            })
+        }
+        getAllschool()
+        getAllassists()
+        return {
+            store,
+            tableData,
+            options,
+            props,
+            models,
+            form,
+            imags,
+            adminoptions,
+            adminvalue,
+            value2,
+            getAllschool,
+            deleteRow,
+            changeBadge,
+            nowPitchdata,
+            schoolClose,
+            originalData,
+            getAllassists,
+            assistChange,
+            loading,
+            uploadHeader,
+            success,
+            provinceOptions,
+            cityOptions,
+            distOptions,
+            areaSelctChange,
+            updateSchoolinfo,
+            createdSchoolbtn,
+            getoption,
+            areaSelect,
+            updateSuccess,
+            PowerShow,
+            closeSelectarea,
+            studyPhase,
+            changeTabs,
+            changebtns,
+            setSchoolData,
+            getSetschool,
+            imgData,
+            patternIcon,
+            skipAnalyse,
+            schooldataSort,
+            removeSchool,
+        }
+    },
+}
+</script>
+<style scoped>
+.schoolbox {
+    width: 100%;
+    margin: 0 auto;
+    line-height: 80px;
+}
+.select-List {
+    line-height: 60px;
+    text-align: left;
+    padding: 1% 2%;
+    width: 75%;
+    float: left;
+}
+.school-list {
+    width: 96%;
+    max-height: 74vh;
+    margin: 0 auto;
+    /* overflow-y: auto; */
+}
+/*编辑页面样式*/
+.redactbox {
+    width: 100%;
+    height: 87vh;
+    background-color: #fff;
+}
+.backbtn {
+    text-align: left;
+    position: absolute;
+    top: 0.5%;
+    right: 10px;
+    z-index: 999;
+    line-height: 40px;
+}
+.school-formbox {
+    width: 85%;
+    margin: 0 auto;
+    padding: 1%;
+}
+.upload-demo-redact {
+    width: 100%;
+    height: 125px;
+}
+.school-form-name {
+    width: 50%;
+}
+.school-form-area {
+    width: 35%;
+}
+.school-form-badge,
+.school-form-code {
+    width: 24%;
+}
+.school-form-grading {
+    width: 80%;
+}
+.changebadge {
+    width: 100%;
+    height: 125px;
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    opacity: 0;
+}
+.school-form-site {
+    width: 35%;
+}
+.school-form-size {
+    width: 21%;
+    color: #bdc3c7;
+}
+.school-form-admin {
+    width: 35%;
+    text-align: left;
+}
+.school-form-state {
+    width: 15%;
+}
+.school-admin-check {
+    width: 2em;
+    height: 2em;
+    vertical-align: -0.5em;
+    fill: currentColor;
+    overflow: hidden;
+}
+.province-box,
+.city-box,
+.dist-box,
+.close-box {
+    display: inline-block;
+}
+.city-box,
+.dist-box {
+    margin-left: 1%;
+}
+/*btn*/
+.boxselect {
+    width: 20%;
+    display: inline-block;
+    margin-top: 0.5%;
+    text-align: right;
+}
+.created-areaicon {
+    width: 1.3em;
+    height: 1.3em;
+    /* vertical-align: -0.5em; */
+    fill: currentColor;
+    overflow: hidden;
+    margin-top: -135px;
+}
+.closebtnIcon {
+    width: 1.8em;
+    height: 1.8em;
+    vertical-align: -0.6em;
+    fill: currentColor;
+    overflow: hidden;
+    margin-left: 8px;
+}
+.closebtnIcon:hover {
+    cursor: pointer;
+}
+.schoolDeatils {
+    position: relative;
+    background: #e9eef3;
+}
+.changebtn {
+    display: inline-block;
+    /* float: right; */
+}
+.changebtn-title {
+    font-size: 12px;
+}
+.changebtn-areaicon {
+    width: 0.9em;
+    height: 0.9em;
+    vertical-align: -0.1em;
+    fill: currentColor;
+    margin-right: 3px;
+}
+.back-icon {
+    width: 0.9em;
+    height: 0.9em;
+    vertical-align: -0.1em;
+    fill: currentColor;
+    margin-right: 3px;
+}
+.created-icon {
+    width: 1.3em;
+    height: 1.3em;
+    vertical-align: -0.3em;
+    fill: currentColor;
+    overflow: hidden;
+    margin-right: 5px;
+}
+.school-analyse {
+    width: 1.8em;
+    height: 1.8em;
+    vertical-align: 0em;
+    fill: currentColor;
+    overflow: hidden;
+    cursor: pointer;
+}
+.have-modules {
+    display: flex;
+    justify-content: space-between;
+    overflow-x: auto;
+}
+</style>
+<style>
+.schoolbox .el-cascader {
+    width: 20%;
+}
+.school-form-badge .el-form-item__label {
+    line-height: 107px;
+}
+.school-form-badge .el-form-item__content {
+    border: 1px solid #ccc;
+    position: relative;
+}
+.changebadge .el-button {
+    width: 100%;
+    height: 100%;
+    font-size: 18px;
+    color: #fff;
+}
+.changebadge .el-button span {
+    border: 1px solid #ccc;
+    background-color: #409eff;
+    padding: 5px;
+    border-radius: 10px;
+}
+.changebadge:hover {
+    opacity: 0.8;
+}
+.school-form-code .el-input__inner {
+    text-align: center;
+}
+.school-form-admin .el-select {
+    width: 100%;
+}
+.school-form-badge .el-image__inner {
+    width: 100%;
+    height: 85%;
+    margin-top: 10%;
+}
+.school-form-grading .el-form-item__content {
+    text-align: left;
+}
+.el-select-dropdown__item {
+    padding: 0 12px 0 20px;
+}
+.school-formbox .el-form-item__content {
+    text-align: left;
+}
+.school-form-area .el-select {
+    width: 100%;
+}
+.school-list .el-table__header-wrapper {
+    line-height: 60px;
+}
+.schoolDeatils .el-tabs__header {
+    height: 45px;
+    line-height: 45px;
+    line-height: 0px;
+    padding-left: 15px;
+    box-shadow: 0 2px 5px #e9e9e9;
+    background-color: #fff;
+    margin: 0px 0px 1px;
+}
+.schoolDeatils .el-tabs__content {
+    padding: 0px;
+}
+.backbtn .changebtn {
+    padding: 8px 15px;
+}
+.upload-demo-redact .el-upload {
+    width: 100%;
+    height: 125px;
+}
+.school-form-grading .el-radio__input.is-checked .el-radio__inner::after {
+    content: '';
+    width: 10px;
+    height: 5px;
+    border: 2px solid white;
+    border-top: transparent;
+    border-right: transparent;
+    text-align: center;
+    display: inline-block;
+    position: absolute;
+    top: 2px;
+    left: 2px;
+    vertical-align: middle;
+    transform: rotate(-45deg);
+    border-radius: 0px;
+    background: none;
+}
+.schoolDeatils .el-tabs__nav-wrap::after {
+    width: 0%;
+}
+.el-upload-dragger {
+}
+@media screen and (max-width: 1920px) {
+    .school-formbox .school-form-badge {
+        width: 17.5%;
+    }
+}
+@media screen and (max-width: 1400px) {
+    .school-formbox .school-form-badge {
+        width: 24%;
+    }
+}
+</style>

File diff suppressed because it is too large
+ 1442 - 0
TEAMModelBI/ClientApp/src/view/participation/setAbility.vue


File diff suppressed because it is too large
+ 2170 - 0
TEAMModelBI/ClientApp/src/view/participation/setPhase.vue


+ 75 - 5
TEAMModelBI/ClientApp/src/view/schoolServe/analyseSchool.vue

@@ -1,7 +1,20 @@
 <template>
     <div class="school-servebox">
-        <div class="backs">
-            <el-button size="small" type="primary" @click="backindex">返回</el-button>
+        <div class="headerbox">
+            <div class="schoolname">
+                <div class="schoolname-img">
+                    <el-image style="width: 55px; height: 55px;border-radius:50%" :src="nowSchools.picture" fit="fill"></el-image>
+                </div>
+                <div class="schoolname-name">
+                    <div class="schoolname-name-title">{{nowSchools.name}}</div>
+                    <div class="schoolname-versions" v-if="nowSchools.scale ==0">基础版</div>
+                    <div class="schoolname-versions standard" v-else-if="nowSchools.scale ==500 && nowSchools.hard.length ===0 && nowSchools.serial.length ===0 && nowSchools.service.length ===0 ">标准版</div>
+                    <div class="schoolname-versions major" v-else-if="nowSchools.scale ===500 && (nowSchools.hard.length !=0 || nowSchools.serial.length !=0 || nowSchools.service.length !=0)">专业版</div>
+                </div>
+            </div>
+            <div class="backs">
+                <el-button size="small" type="primary" @click="backindex">返回</el-button>
+            </div>
         </div>
         <div class="topbox">
             <div class="topbox-list">
@@ -113,6 +126,7 @@ import CommonPie from '@/components/echarts/commonPie.vue'
 import CommonLine from '@/components/echarts/commonLine.vue'
 import Doublepie from '@/components/echarts/doublePie.vue'
 import { useRouter } from 'vue-router'
+import { useStore } from 'vuex'
 import * as echarts from 'echarts'
 export default {
     components: {
@@ -125,6 +139,8 @@ export default {
     },
     setup() {
         const router = useRouter()
+        const store = useStore()
+        let nowSchools = ref({})
         let headerData = ref([
             { id: 1, title: '教师数量', num: 0, icon: '#icon-schoollaoshi', classname: 'teach' },
             { id: 2, title: '班级数量', num: 0, icon: '#icon-banjiguanli1', classname: 'class' },
@@ -1002,15 +1018,18 @@ export default {
             },
         ]
         function backindex() {
-            router.push({ path: '/home/schoolmanage', query: {} })
+            router.push({ path: '/home/participation', query: {} })
         }
-        return { headerData, basicsData, totalechartsData, modulesData, tableData, backindex }
+        nowSchools.value = store.state.nowSchool
+        console.log(store.state.nowSchool)
+        return { headerData, basicsData, totalechartsData, modulesData, tableData, backindex, nowSchools }
     },
 }
 </script>
 <style scoped>
 .school-servebox {
     background: #e9eef3;
+    line-height: 20px;
 }
 .topbox {
     width: 100%;
@@ -1290,10 +1309,61 @@ export default {
     border-left: 6px solid #ff7979;
 }
 .backs {
+    width: 17%;
     line-height: 20px;
     text-align: right;
     margin-top: 0.5%;
-    margin-right: 1%;
+    margin-right: 0%;
+    display: inline-block;
+    vertical-align: top;
+}
+.headerbox {
+    width: 100%;
+    height: 8vh;
+    border-bottom: 1px solid #ccc;
+}
+.schoolname {
+    width: 80%;
+    font-size: 20px;
+    line-height: 24px;
+    display: inline-block;
+    vertical-align: top;
+    margin-top: 0.5%;
+    padding-left: 17%;
+}
+.schoolname div {
+    display: inline-block;
+    vertical-align: top;
+}
+.schoolname-name {
+    font-size: 24px;
+    font-weight: 600;
+    margin-left: 3%;
+    margin-top: 1.3%;
+    position: relative;
+}
+.schoolname-name-title {
+    /* width: 80%; */
+    display: inline-block;
+    vertical-align: top;
+}
+.schoolname-versions {
+    width: 55px;
+    font-size: 14px;
+    line-height: 20px;
+    border: 1px solid #48dbfb;
+    border-radius: 10px;
+    margin-left: 10px;
+    background-color: #48dbfb;
+    color: #fff;
+}
+.major {
+    background: linear-gradient(90deg, #734311, #ffc78c, #734311);
+    border: 1px solid #ffc78c;
+}
+.standard {
+    background: linear-gradient(90deg, #686de0, #5f27cd);
+    border: 1px solid #686de0;
 }
 </style>
 <style>

+ 18 - 10
TEAMModelBI/ClientApp/src/view/schoolServe/school.vue

@@ -54,8 +54,8 @@
                 <el-table-column :label="$t(`schoolManages.tables.scale`)" class="school-table-edition" align="center">
                     <template #default="scope">
                         <el-image style="width: 80px; height: 80px" :src="imgData.basics" fit="fill" v-if="scope.row.scale ===0"></el-image>
-                        <el-image style="width: 80px; height: 80px" :src="imgData.standard" fit="fill" v-else-if="scope.row.scale ===500 && scope.row.hard ===0 && scope.row.serial ===0 && scope.row.service ===0"></el-image>
-                        <el-image style="width: 80px; height: 80px" :src="imgData.specialty" fit="fill" v-else-if="scope.row.scale ===500 && (scope.row.hard !=0 || scope.row.serial !=0 || scope.row.service !=0)"></el-image>
+                        <el-image style="width: 80px; height: 80px" :src="imgData.standard" fit="fill" v-else-if="scope.row.scale ===500 && scope.row.hard.length ===0 && scope.row.serial.length ===0 && scope.row.service.length ===0"></el-image>
+                        <el-image style="width: 80px; height: 80px" :src="imgData.specialty" fit="fill" v-else-if="scope.row.scale ===500 && (scope.row.hard.length !=0 || scope.row.serial.length !=0 || scope.row.service.length !=0)"></el-image>
                     </template>
                 </el-table-column>
                 <el-table-column prop="id" :label="$t(`schoolManages.tables.brevityCode`)" align="center" />
@@ -83,7 +83,7 @@
                 <el-table-column label="模组情况" align="center" width="200">
                     <template #default="scope">
                         <div class="have-modules">
-                            <div v-for="item in patternIcon" :title="item.name" :key="item.id">
+                            <div v-for="item in scope.row.serviceData" :title="item.name" :key="item.id">
                                 <svg class="school-analyse" aria-hidden="true">
                                     <use :xlink:href="item.icon"></use>
                                 </svg>
@@ -307,12 +307,12 @@ export default {
         })
         //模组icon
         let patternIcon = ref([
-            { id: 1, name: '学情分析', icon: '#icon-tongjifenxi' },
-            { id: 2, name: '智慧学校管理服务', icon: '#icon-zhihuixiaoyuan' },
-            { id: 3, name: 'A Class ONE 智慧学伴', icon: '#icon-jxuexiao2' },
-            { id: 4, name: '数据存储服务空间', icon: '#icon-yuncunchu' },
-            { id: 5, name: '卷卡合一阅卷系统', icon: '#icon-pingtai_kaoshi' },
-            { id: 6, name: '教研中心模组', icon: '#icon-jiaoyan' },
+            { id: 1, name: '学情分析', icon: '#icon-tongjifenxi', key: 'YMPCVCIM' },
+            { id: 2, name: '智慧学校管理服务', icon: '#icon-zhihuixiaoyuan', key: 'IPDYZYLC' },
+            { id: 3, name: 'A Class ONE 智慧学伴', icon: '#icon-jxuexiao2', key: '3CLYJ6NP' },
+            { id: 4, name: '数据存储服务空间', icon: '#icon-yuncunchu', key: 'IPALJ6NY' },
+            { id: 5, name: '卷卡合一阅卷系统', icon: '#icon-pingtai_kaoshi', key: 'VABAJ6NV' },
+            { id: 6, name: '教研中心模组', icon: '#icon-jiaoyan', key: 'VLY6J6N6' },
         ])
         provinceOptions.value.optionInfo = optionsData
         console.log(store.state.point)
@@ -322,6 +322,7 @@ export default {
                 console.log(res, '获取学校列表')
                 //处理关联管家  拼内容
                 for (let i in res.schoolAssists) {
+                    res.schoolAssists[i].serviceData = []
                     if (res.schoolAssists[i].assists) {
                         res.schoolAssists[i].assisName = ''
                         res.schoolAssists[i].location = res.schoolAssists[i].province + res.schoolAssists[i].city + res.schoolAssists[i].dist
@@ -330,6 +331,13 @@ export default {
                             res.schoolAssists[i].assisName = res.schoolAssists[i].assisName + datas[y].tmdName + ','
                         }
                     }
+                    if (res.schoolAssists[i].service.length > 0) {
+                        res.schoolAssists[i].service.forEach((x) => {
+                            for (let m in patternIcon.value) {
+                                patternIcon.value[m].key === x ? res.schoolAssists[i].serviceData.push(patternIcon.value[m]) : ''
+                            }
+                        })
+                    }
                 }
                 console.log(res)
                 res.state == 200 ? (tableData.value.push(...res.schoolAssists), (originalData.value = res.schoolAssists), (loading.value = false)) : ''
@@ -740,7 +748,7 @@ export default {
     width: 35%;
 }
 .school-form-size {
-    width: 21%;
+    width: 35%;
     color: #bdc3c7;
 }
 .school-form-admin {

+ 3 - 2
TEAMModelBI/ClientApp/src/view/schoolServe/setschool.vue

@@ -998,9 +998,10 @@ export default {
                 return
             }
             for (let i in data) {
-                // data[i].id === value.id ? data.splice(i, 1) : ''
-                data[i].id === value.id && value.start === 0 ? (data.splice(i, 1), (data[0].start = 0)) : data.splice(i, 1)
+                data[i].id === value.id ? data.splice(i, 1) : ''
+                // data[i].id === value.id && value.start === 0 ? (data.splice(i, 1), (data[0].start = 0), console.log('进入删除111')) : data.splice(i, 1), console.log('进入删除2222')
             }
+            data[0].start = 0
             nowschoolData.value.period[pathNowphase.value].semesters = data
             console.log(nowschoolData.value.period[pathNowphase.value].semesters, '删除学期后的')
         }

+ 321 - 0
TEAMModelBI/Controllers/BIHome/OnLineController.cs

@@ -0,0 +1,321 @@
+using Azure.Cosmos;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using StackExchange.Redis;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelBI.Tool;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK.Models.Table;
+
+namespace TEAMModelBI.Controllers.BIHome
+{
+    [Route("online")]
+    [ApiController]
+    public class OnLineController : ControllerBase
+    {
+        private readonly AzureCosmosFactory _azureCosmos;
+        private readonly AzureStorageFactory _azureStorage;
+        private readonly AzureRedisFactory _azureRedis;
+
+        public OnLineController(AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage, AzureRedisFactory azureRedis)
+        {
+            _azureCosmos = azureCosmos;
+            _azureStorage = azureStorage;
+            _azureRedis = azureRedis;
+        }
+
+        /// <summary>
+        /// 总数统计
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost("get-count")]
+        public async Task<IActionResult> GetCount() 
+        {
+            var cosmosClient = _azureCosmos.GetCosmosClient();
+            var table = _azureStorage.GetCloudTableClient().GetTableReference("IESLogin");
+            DateTimeOffset dateTime = DateTimeOffset.UtcNow;
+
+            var (daySt, dayEt) = TimeHelper.GetStartOrEnd(dateTime);  //今天开始时间    13位
+            var (daySf, dayEf) = TimeHelper.GetStartOrEnd(dateTime, dateLenth: false);  //今天开始时间  10位
+            var (lastDayS, lastdayE) = TimeHelper.GetStartOrEnd(DateTimeOffset.Parse($"{dateTime.Year}-{dateTime.Month}-{dateTime.Day - 1}"));   //昨天开始时间
+            
+            var near7S = dateTime.AddDays(-7).ToUnixTimeMilliseconds();  //前七天的开始时间
+            var near7E = dateTime.ToUnixTimeMilliseconds();     //当前结束时间
+            long hour1 = dateTime.AddHours(-1).ToUnixTimeMilliseconds(); //一小时前时间戳
+
+            int areaCnt = 0;   //学区总数
+            int scCnt = 0;  //学校总数
+            int tchCnt = 0; //教师总数
+            int stuCnt = 0; //学生总数
+
+            int onStuCnt = 0;  //学生在线人数
+            int onTchCnt = 0;  //教师在线人数
+
+            int todayScCnt = 0;  //今日新增学校数
+            int todayTchCnt = 0; //今日新增教师
+            int todayStuCnt = 0;   //今日新增学生数
+
+            string currentSql = "select value(count(c.id)) from c";
+
+            areaCnt = await CommonFind.GetSqlValueCount(cosmosClient, "Normal", currentSql, "Base-Area");
+            scCnt = await CommonFind.GetSqlValueCount(cosmosClient, "School", currentSql, "Base");
+            tchCnt = await CommonFind.GetSqlValueCount(cosmosClient, "Teacher", currentSql, "Base");
+            stuCnt = await CommonFind.GetSqlValueCount(cosmosClient, "Student", "select value(count(c.id)) from c where c.pk='Base'");
+
+            string addSql = $"select value(count(c.id)) from c where c.pk='Base' and c.createTime >={daySf} and c.createTime <= {dayEf}";
+            todayScCnt = await CommonFind.GetSqlValueCount(cosmosClient, "School", addSql, "Base");
+            todayTchCnt = await CommonFind.GetSqlValueCount(cosmosClient, "Teacher", addSql, "Base");
+            todayStuCnt = await CommonFind.GetSqlValueCount(cosmosClient, "Student", addSql, "Base");
+
+            List<RecOnLine> recStuOnLines = new();
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "Student").GetItemQueryIterator<RecOnLine>(queryText: "select c.id,c.name,c.code,c.loginInfos from c where c.pk='Base' and array_length(c.loginInfos) > 0 ", requestOptions:new QueryRequestOptions() { }))
+            {
+                recStuOnLines.Add(item);
+            }
+            onStuCnt = (from rs in recStuOnLines from l in rs.loginInfos where l.expire >= hour1 select rs).ToList().Count();  //linq查询
+            //onStuCnt = recStuOnLines.Select(rss => new RecOnLine { id = rss.id,code=rss.code, name =rss.name,loginInfos = new List<Teacher.LoginInfo> { rss.loginInfos.Find(f => f.expire >= hour1) } }).Where(w => w.loginInfos.FirstOrDefault() != null).ToList().Count();  //lambda 表达式查询 
+
+            List<RecOnLine> recTecOnLines = new();
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIterator<RecOnLine>(queryText: "select c.id,c.name,c.code,c.loginInfos from c where array_length(c.loginInfos) > 0 ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
+            {
+                recTecOnLines.Add(item);
+            }                        
+            onTchCnt = (from rs in recTecOnLines from l in rs.loginInfos where l.expire >= hour1 select rs).ToList().Count();  //linq查询
+            //onStuCnt = recTecOnLines.Select(rss => new RecOnLine { id = rss.id,code=rss.code, name =rss.name,loginInfos = new List<Teacher.LoginInfo> { rss.loginInfos.Find(f => f.expire >= hour1) } }).Where(w => w.loginInfos.FirstOrDefault() != null).ToList().Count();  //lambda 表达式查询 
+            
+            return Ok(new { state = 200, areaCnt, scCnt, tchCnt, stuCnt, todayScCnt, todayTchCnt, todayStuCnt, onStuCnt, onTchCnt});
+        }
+
+        /// <summary>
+        /// 在线人数趋势图
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost("get-trend")]
+        public async Task<IActionResult> GetTrend() 
+        {
+            var table = _azureStorage.GetCloudTableClient().GetTableReference("IESLogin");
+            DateTimeOffset dateTime = DateTimeOffset.UtcNow;
+            var (daySt, dayEt) = TimeHelper.GetStartOrEnd(dateTime);  //今天开始时间    13位
+            var (strDaySt, strDayEt) = TimeHelper.GetUnixToDate(daySt, dayEt, "yyyyMMddHH");
+            var dateDay = dateTime.ToString("yyyyMMdd"); //获取当天的日期
+
+            daySt.ToString("yyyyMMddHH");
+            Dictionary<int, int> allDays = new();  //所有在线人数
+            Dictionary<int, int> tchDays = new();  //教师在线人数
+            Dictionary<int, int> stuDays = new();  //学生在线人数
+            Dictionary<int, int> tmdDays = new();  //醍摩豆账户学生
+
+            SortedSetEntry[] tchDay = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:IES:teacher:{dateDay}");
+            if (tchDay.Length > 0)
+            {
+                foreach (var item in tchDay)
+                {
+                    int val = ((int)item.Score);
+                    int key = ((int)item.Element);
+                    var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {key}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));                
+                    tchDays.Add(hour, val);
+                    if (allDays.ContainsKey(hour))
+                        allDays[hour] = (allDays[hour] + val);
+                    else
+                        allDays.Add(hour, val);
+                }
+            }
+            else
+            {
+                string tableSqlTch = $"PartitionKey eq 'HourLogin' and RowKey ge '{strDaySt}' and RowKey le '{strDayEt}'";
+                List<HourLogin> hourLoginsTch = await table.QueryWhereString<HourLogin>(tableSqlTch);
+
+                if (hourLoginsTch.Count > 0) 
+                {
+                    foreach (var item in hourLoginsTch)
+                    {
+                        await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:IES:teacher:{dateDay}", $"{item.Hour}", item.Teacher);//存一天24小时
+                        var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {item.Hour}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));
+                        tchDays.Add(hour, item.Teacher);
+                        if (allDays.ContainsKey(hour))
+                            allDays[hour] = (allDays[hour] + item.Teacher);
+                        else
+                            allDays.Add(hour, item.Teacher);
+                    }
+                }
+            }
+
+            SortedSetEntry[] stuDay = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:IES:student:{dateDay}");
+            if (stuDay.Length > 0)
+            {
+                foreach (var item in stuDay)
+                {
+                    int val = (int)item.Score;
+                    int key = (int)item.Element;
+                    var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {key}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));
+                    stuDays.Add(hour, val);
+                    if (allDays.ContainsKey(hour))
+                        allDays[hour] = (allDays[hour] + val);
+                    else
+                        allDays.Add(hour, val);
+                }
+            }
+            else
+            {
+                string tableSqlStu = $"PartitionKey eq 'HourLogin' and RowKey ge '{strDaySt}' and RowKey le '{strDayEt}'";
+                List<HourLogin> hourLoginsStu = await table.QueryWhereString<HourLogin>(tableSqlStu);
+
+                //var hourStuCnt = hourLoginsStu.GroupBy(x => x.Hour).Select(k => new { key = int.Parse(k.Key.ToString().Substring(8, 2)), value = k.Count() }).ToList();
+                if (hourLoginsStu.Count > 0)
+                {
+                    foreach (var item in hourLoginsStu)
+                    {
+                        await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:IES:student:{dateDay}", $"{item.Hour}", item.Student);//存一天24小时
+                        var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {item.Hour}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));
+                        stuDays.Add(hour, item.Student);
+                        if (allDays.ContainsKey(hour))
+                            allDays[hour] = (allDays[hour] + item.Student);
+                        else
+                            allDays.Add(hour, item.Student);
+                    }
+                }
+            }
+
+            SortedSetEntry[] tmdDay = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:IES:tmduser:{dateDay}");
+            if (tmdDay.Length > 0)
+            {
+                foreach (var item in stuDay)
+                {
+                    int val = (int)item.Score;
+                    int key = (int)item.Element;
+                    var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {key}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));
+                    tmdDays.Add(hour, val);
+                    if (allDays.ContainsKey(hour))
+                        allDays[hour] = (allDays[hour] + val);
+                    else
+                        allDays.Add(hour, val);
+                }
+            }
+            else
+            {
+                string tableSqlTmd = $"PartitionKey eq 'HourLogin' and RowKey ge '{strDaySt}' and RowKey le '{strDayEt}'";
+                List<HourLogin> hourLoginsTmd = await table.QueryWhereString<HourLogin>(tableSqlTmd);
+
+                //var hourTmdCnt = hourLoginsTmd.GroupBy(x => x.Hour).Select(k => new { key = int.Parse(k.Key.ToString().Substring(8, 2)), value = k.Count() }).ToList();
+                if (hourLoginsTmd.Count > 0)
+                {
+                    foreach (var item in hourLoginsTmd)
+                    {
+                        await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:IES:tmduser:{dateDay}", $"{item.Hour}", item.TmdUser);//存一天24小时
+                        var hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {item.Hour}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));
+                        tmdDays.Add(hour, item.TmdUser);
+                        if (allDays.ContainsKey(hour))
+                            allDays[hour] = (allDays[hour] + item.TmdUser);
+                        else
+                            allDays.Add(hour, item.TmdUser);
+                    }
+                }
+            }
+            
+            return Ok(new { state = 200,allDays = allDays.OrderBy(o=>o.Key).ToList(), tchDays=tchDays.OrderBy(o => o.Key).ToList(), stuDays= stuDays.OrderBy(o => o.Key).ToList(), tmdDays= tmdDays.OrderBy(o => o.Key).ToList() });
+        }
+
+        /// <summary>
+        /// 课例趋势图
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost("get-lessontrend")]
+        public async Task<IActionResult> GetLessonTrend()
+        {
+            DateTimeOffset dateTime = DateTimeOffset.UtcNow;
+            var cosmosClient = _azureCosmos.GetCosmosClient();
+            int year = dateTime.Year;   //当前年
+            int month = dateTime.Month;  //当前月
+            int day = dateTime.Day;      //当天
+            int hour = int.Parse(DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {dateTime.Hour}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH"));   //当前小时
+
+            Dictionary<int, int> scLessCnt = new();  //学校课例
+            Dictionary<int, int> tchLessCnt = new();   //教师课例
+            Dictionary<int, int> yesterdayCnt = new();  //昨天24小时课例
+
+            for (int i = 0; i < 24; i++)
+            {
+                if (hour >= i)
+                {
+                    DateTimeOffset timeHour = new DateTime(year, month, day, i, 0, 0);
+                    var (hourS, hourE) = TimeHelper.GetStartOrEnd(timeHour, type: "hour");
+                    await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<int>(queryText: $"select value(count(c.id)) from c where c.pk='LessonRecord' and c.startTime >={hourS} and c.startTime <= {hourE}", requestOptions: new QueryRequestOptions() { }))
+                    {
+                        scLessCnt.Add(i, item);
+                    }
+
+                    await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIterator<int>(queryText: $"select value(count(c.id)) from c where c.startTime >={hourS} and c.startTime <= {hourE}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("LessonRecord") }))
+                    {
+                        tchLessCnt.Add(i, item);
+                    }
+                }
+
+                DateTimeOffset yesterday = new DateTime(year, month, day - 1, i, 0, 0);
+                var (yHourS, yHourE) = TimeHelper.GetStartOrEnd(yesterday, type: "hour");
+                string sql = $"select value(count(c.id)) from c where c.pk='LessonRecord' and c.startTime >= {yHourS} and c.startTime <= {yHourE}";
+                int hourLessCnt = await CommonFind.GetSqlValueCount(cosmosClient, new List<string> { "School", "Teacher" }, sql);
+                yesterdayCnt.Add(i, hourLessCnt);
+            }
+
+            return Ok(new { state = 200, scLessCnt = scLessCnt.ToList(), tchLessCnt = tchLessCnt.ToList(), yesterdayCnt = yesterdayCnt.ToList() });
+        }
+
+        /// <summary>
+        /// 版本数量占比
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost("get-edition")]
+        public async Task<IActionResult> GetEdition() 
+        {
+            var cosmosClient = _azureCosmos.GetCosmosClient();
+            List<RecScEd> scEdCnt = new();
+            var ScSql = $"select c.id,c.name,c.size,c.scale from c";
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<RecScEd>(queryText: ScSql, requestOptions:new QueryRequestOptions() { PartitionKey= new PartitionKey("Base")}))
+            {
+                scEdCnt.Add(item);
+            }
+
+            scEdCnt.ForEach(async scProductCnt =>
+            {
+                var response = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(scProductCnt.id, new PartitionKey("ProductSum"));
+                if (response.Status == 200)
+                {
+                    using var json = await JsonDocument.ParseAsync(response.ContentStream);
+                    SchoolProductSum ScProductSum = json.ToObject<SchoolProductSum>();
+                    scProductCnt.serial = ScProductSum.serial.Count();
+                    scProductCnt.service = ScProductSum.service.Count();
+                    scProductCnt.hard = ScProductSum.hard.Count();
+                }
+            });
+
+            return Ok(new { state = 200, scEdCnt });
+        }
+
+        public record RecOnLine
+        {
+            public string id { get; set; }
+            public string name { get; set; }
+            public string code { get; set; }
+            public List<Teacher.LoginInfo> loginInfos { get; set; }
+        }
+
+        public class RecScEd
+        {
+            public string id { get; set; }
+            public string name { get; set; }
+            public int size { get; set; }
+            public int scale { get; set; }
+            public int serial { get; set; } = 0;//软体
+            public int service { get; set; } = 0; //服务
+            public int hard { get; set; } = 0; //硬体
+        }
+
+
+    }
+}

+ 1 - 1
TEAMModelBI/Controllers/BISchool/BatchSchoolController.cs

@@ -543,7 +543,7 @@ namespace TEAMModelBI.Controllers.BISchool
         /// <param name="jsonElement"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "assist,admin")]
         [HttpPost("upd-schoolassist")]
         public async Task<IActionResult> UpdSchoolAssist(JsonElement jsonElement)
         {

+ 1 - 1
TEAMModelBI/Controllers/BISchool/SchoolController.cs

@@ -232,7 +232,7 @@ namespace TEAMModelBI.Controllers.BISchool
         /// <param name="school"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [AuthToken(Roles = "assist")]
+        [AuthToken(Roles = "assist,admin")]
         [HttpPost("upd-school")]
         public async Task<IActionResult> UpdSchool(School school)
         {

+ 75 - 0
TEAMModelBI/Controllers/BITest/Ies5TestController.cs

@@ -0,0 +1,75 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Options;
+using StackExchange.Redis;
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Text.Json;
+using System.Threading.Tasks;
+using TEAMModelBI.Tool;
+using TEAMModelOS.Models;
+using TEAMModelOS.SDK;
+using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Models;
+using TEAMModelOS.SDK.Models.Service;
+using static TEAMModelOS.SDK.Models.Teacher;
+
+namespace TEAMModelBI.Controllers.BITest
+{
+    [Route("iesapitest")]
+    [ApiController]
+    public class Ies5TestController : ControllerBase
+    {
+
+        private readonly AzureCosmosFactory _azureCosmos;
+        private readonly AzureRedisFactory _azureRedis;
+        private readonly DingDing _dingDing;
+        private readonly Option _option;
+        private readonly AzureStorageFactory _azureStorage;
+        private readonly IWebHostEnvironment _environment; //读取文件
+        //读取配置文件
+        private readonly IConfiguration _configuration;
+        private readonly CoreAPIHttpService _coreAPIHttpService;
+        private readonly HttpClient _httpClient;
+
+        public Ies5TestController(AzureCosmosFactory azureCosmos, AzureRedisFactory azureRedis, DingDing dingDing, AzureStorageFactory azureStorage, IOptionsSnapshot<Option> option, IWebHostEnvironment hostingEnvironment, IConfiguration configuration, CoreAPIHttpService coreAPIHttpService, HttpClient httpClient)
+        {
+            _azureCosmos = azureCosmos;
+            _dingDing = dingDing;
+            _azureStorage = azureStorage;
+            _option = option?.Value;
+            _environment = hostingEnvironment;
+            _configuration = configuration;
+            _coreAPIHttpService = coreAPIHttpService;
+            _httpClient = httpClient;
+            _azureRedis = azureRedis;
+        }
+
+        [HttpPost("get-datetime")]
+        public async Task<IActionResult> GetDateTime() 
+        {
+           var dateHours =  DateTimeOffset.UtcNow.Hour;
+           var dateHours1 = DateTime.Now.Hour;
+           var dateHours2 = DateTimeOffset.Now.Hour;
+           var dateDays = DateTimeOffset.UtcNow.Month;
+
+           var dateDay = DateTimeOffset.UtcNow.ToString("yyyyMMdd");
+           var dateMonth = DateTimeOffset.UtcNow.ToString("yyyyMM");
+           long expire = DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds();
+           long now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
+            var cosmosClient = _azureCosmos.GetCosmosClient();
+           List<LoginInfo> loginInfos = new() { new LoginInfo (){ time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),ip="172.54.81.101",expire = expire } };
+            var tets = await LoginService.DoLoginInfo(loginInfos: loginInfos, school: "hbcn", scope: "teacher", id: "1636016499", ip: "172.168.52.102", _azureRedis, _azureStorage, expire: 1);
+           return Ok(new { state = 200, dateHours, dateHours1, dateHours2, dateDay,dateDays, dateMonth, });
+        }
+
+        public async Task<IActionResult> GetRedisToTable(JsonElement jsonElement)
+        {
+            return Ok(new { state = 200 });
+        }
+
+    }
+}

+ 30 - 30
TEAMModelBI/Controllers/BITest/TestController.cs

@@ -654,7 +654,7 @@ namespace TEAMModelBI.Controllers.BITest
             int year = DateTimeOffset.UtcNow.Year;
             int month = DateTimeOffset.UtcNow.Month;
 
-            
+            var datetime7 = DateTimeOffset.UtcNow.AddDays(-7); //前七天的时间
 
             List<string> strList = monthsOfYear("2021-1");
 
@@ -686,7 +686,7 @@ namespace TEAMModelBI.Controllers.BITest
             //return Ok(new { strList, dateTime, year, start, end, endList, endList1, endList2 });
 
 
-            return Ok(new { start1, end1, start2, end2, start3, end3, start4, end4 , start5, end5 , start6, end6 , start7, end7 });
+            return Ok(new { datetime7, start1, end1, start2, end2, start3, end3, start4, end4 , start5, end5 , start6, end6 , start7, end7 });
         }
 
         public static List<string> monthsOfYear(string yearMonth)
@@ -1244,17 +1244,38 @@ namespace TEAMModelBI.Controllers.BITest
             return Ok(new { state = 200});
         }
 
-        [HttpPost("get-vlaue")]
-        public async Task<IActionResult> GetValueCount() 
+        [HttpPost("get-linqcount")]
+        public async Task<IActionResult> GetLinqCount() 
         {
-            var cosmosClient = _azureCosmos.GetCosmosClient();
-            int counts = 0;
-            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<int>(queryText: "select value(count(c.id)) from c", requestOptions:new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
+            List<linqTest> linqTests = new();
+            for (int i = 0; i < 10; i++)
             {
-                counts += item;
+                linqTest linqt = new() { id = $"qwe{i}",name=$"名字{i}" ,linq1s = new List<linq1> { new linq1 { id =$"abc{i}",times = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}, new linq1 { id = $"def{i}", times = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() } } };
+                linqTests.Add(linqt);
             }
 
-            return Ok(new { state = 200, counts }); 
+            //var set = linqTests.Select((x, y) => x.linq1s.Find(l => l.id.Equals($"abc0"))).ToList();
+            var set = linqTests.Where(x=> !string.IsNullOrEmpty(x.linq1s.Find(l => l.id.Equals($"abc0")).ToString()) );
+            //var tem220p = linqTests.ForEach(x => {
+            //    var coreUser = linqTests.Find(c => c.id.Equals("abc0"));
+            //   });  //.Except(linqTests.Select(y => y.linq1s.Find(n => n.times.Equals("abc0"))));
+
+            return Ok(new { state = 200, linqTests, set });
+        }
+
+
+
+        public class linqTest
+        {
+            public string id{ get; set; }
+            public string name { get; set; }
+            public List<linq1> linq1s { get; set; }
+        }
+
+        public class linq1 
+        {
+            public string id { get; set; }
+            public long times { get; set; }
         }
 
         public class Test
@@ -1307,27 +1328,6 @@ namespace TEAMModelBI.Controllers.BITest
             return iLastDay;
         }
 
-        public record lessons 
-        {
-            public long countB { get; set; }
-            public long countP { get; set; }
-            public long countT { get; set; }
-            public long countPT { get; set; }
-            public LessonCount lesson { get; set; }
-        }
-
-        public record pageTest
-        {
-            public string keyt { get; set; }
-            public string listv { get; set; }
-        }
-
-        public record pageTest1
-        {
-            public string keyt { get; set; }
-            public List<string> listv { get; set; }
-        }
-
         public class GenerateRandom1
         {
             /// <summary>

+ 41 - 0
TEAMModelBI/Tool/CommonFind.cs

@@ -217,5 +217,46 @@ namespace TEAMModelBI.Tool
 
             return totals;
         }
+
+        /// <summary>
+        /// 单个容器数据统计
+        /// </summary>
+        /// <param name="cosmosClient"></param>
+        /// <param name="container"></param>
+        /// <param name="SqlTxt"></param>
+        /// <param name="code"></param>
+        /// <returns></returns>
+        public static async Task<int> GetSqlValueCount(CosmosClient cosmosClient, string container, string SqlTxt,string code = null) 
+        {
+            int totals = 0;
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", container).GetItemQueryIterator<int>(queryText: SqlTxt,requestOptions: string.IsNullOrEmpty($"{code}") ? new QueryRequestOptions() { } : new QueryRequestOptions() { PartitionKey = new PartitionKey($"{code}") }))
+            {
+                totals += item;
+            }
+
+            return totals;
+        }
+
+        /// <summary>
+        /// 多个容器数据统计
+        /// </summary>
+        /// <param name="cosmosClient"></param>
+        /// <param name="containers"></param>
+        /// <param name="SqlTxt"></param>
+        /// <param name="code"></param>
+        /// <returns></returns>
+        public static async Task<int> GetSqlValueCount(CosmosClient cosmosClient, List<string> containers, string SqlTxt, string code = null)
+        {
+            int totals = 0;
+            foreach (var container in containers)
+            {
+                await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", container).GetItemQueryIterator<int>(queryText: SqlTxt, requestOptions: string.IsNullOrEmpty($"{code}") ? new QueryRequestOptions() { } : new QueryRequestOptions() { PartitionKey = new PartitionKey($"{code}") }))
+                {
+                    totals += item;
+                }
+            }
+
+            return totals;
+        }
     }
 }

+ 39 - 13
TEAMModelBI/Tool/TimeHelper.cs

@@ -187,20 +187,21 @@ namespace TEAMModelBI.Tool
             int year = dateTime.Year;
             int month = dateTime.Month;
             int day = dateTime.Day;
+            int hour = dateTime.Hour;
             DateTimeOffset tempStrart = new();
-            DateTimeOffset tempEnt = new();
+            DateTimeOffset tempEnd = new();
             switch (type) 
             {
                 case "year":
                     tempStrart = new DateTime(year, 1, 1);
-                    tempEnt = new DateTime(year, 12, DateTime.DaysInMonth(year, 12), 23, 59, 59);
+                    tempEnd = new DateTime(year, 12, DateTime.DaysInMonth(year, 12), 23, 59, 59);
                     break;
 
                 case "term":
                     if (month <= 8 && month >= 3)
                     {
                         tempStrart = new DateTime(year, 3, 1);
-                        tempEnt = new DateTime(year, 8, 31, 23, 59, 59);
+                        tempEnd = new DateTime(year, 8, 31, 23, 59, 59);
                     }
                     else
                     {
@@ -209,11 +210,11 @@ namespace TEAMModelBI.Tool
                         if (month >= 9)
                         {
                             tempStrart = new DateTime(year, 9, 1);
-                            tempEnt = new DateTime(year + 1, 2, (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? days = 29 : days = 28, 23, 59, 59);                        }
+                            tempEnd = new DateTime(year + 1, 2, (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? days = 29 : days = 28, 23, 59, 59);                        }
                         else
                         {
                             tempStrart = new DateTime(year - 1, 9, 1);
-                            tempEnt = new DateTime(year, 2, (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? days = 29 : days = 28, 23, 59, 59);
+                            tempEnd = new DateTime(year, 2, (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? days = 29 : days = 28, 23, 59, 59);
                         }
                     }
 
@@ -230,7 +231,7 @@ namespace TEAMModelBI.Tool
                         if (month <= 8 && month >= 3)
                         {
                             tempStrart = new DateTime(year, 3, 1);
-                            tempEnt = new DateTime(year, 8, 31, 23, 59, 59);
+                            tempEnd = new DateTime(year, 8, 31, 23, 59, 59);
                         }
                         else
                         {
@@ -239,12 +240,12 @@ namespace TEAMModelBI.Tool
                             if (month >= 9)
                             {
                                 tempStrart = new DateTime(year, 9, 1);
-                                tempEnt = new DateTime(year + 1, 2, (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? days = 29 : days = 28, 23, 59, 59);
+                                tempEnd = new DateTime(year + 1, 2, (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? days = 29 : days = 28, 23, 59, 59);
                             }
                             else
                             {
                                 tempStrart = new DateTime(year - 1, 9, 1);
-                                tempEnt = new DateTime(year, 2, (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? days = 29 : days = 28, 23, 59, 59);
+                                tempEnd = new DateTime(year, 2, (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? days = 29 : days = 28, 23, 59, 59);
                             }
                         }
                     }
@@ -252,14 +253,14 @@ namespace TEAMModelBI.Tool
                     break;
                 case "month":
                     tempStrart = new DateTime(year, month, 1);
-                    tempEnt = new DateTime(year, month, DateTime.DaysInMonth(year, month), 23, 59, 59);
+                    tempEnd = new DateTime(year, month, DateTime.DaysInMonth(year, month), 23, 59, 59);
 
                     break;
                 case "week":
                     DateTimeOffset weekStrart = dateTime.AddDays(-(int)(dateTime.DayOfWeek) + 1);
                     DateTimeOffset weekEnd  = dateTime.AddDays(7-(int)(dateTime.DayOfWeek));
                     tempStrart = new DateTime(weekStrart.Year, weekStrart.Month, weekStrart.Day);
-                    tempEnt = new DateTime(weekEnd.Year, weekEnd.Month, weekEnd.Day, 23, 59, 59); 
+                    tempEnd = new DateTime(weekEnd.Year, weekEnd.Month, weekEnd.Day, 23, 59, 59); 
 
                     break;
                 case "lastweek":
@@ -268,24 +269,49 @@ namespace TEAMModelBI.Tool
                     var Mon = dateTime.AddDays((-7 - m));//星期一
                     var Sun = dateTime.AddDays((-7 - s));  //星期日
                     tempStrart = new DateTime(Mon.Year, Mon.Month, Mon.Day);
-                    tempEnt = new DateTime(Sun.Year, Sun.Month, Sun.Day, 23, 59, 59);
+                    tempEnd = new DateTime(Sun.Year, Sun.Month, Sun.Day, 23, 59, 59);
+
+                    break;
+                case "hour":
+                    tempStrart = new DateTime(year, month, day, hour, 0, 0);
+                    tempEnd = new DateTime(year, month, day, hour, 59, 59);
 
                     break;
                 default:
                     tempStrart = new DateTime(year, month, day);
-                    tempEnt = new DateTime(year, month, day, 23, 59, 59);
+                    tempEnd = new DateTime(year, month, day, 23, 59, 59);
                     //start = dateLenth ? DateTimeOffset.Parse($"{dayStart}").ToUnixTimeMilliseconds() : DateTimeOffset.Parse($"{dayStart}").ToUnixTimeSeconds();
                     //end = dateLenth ? DateTimeOffset.Parse($"{dayEnd}").ToUnixTimeMilliseconds() : DateTimeOffset.Parse($"{dayEnd}").ToUnixTimeSeconds();
                     break;            
             }
 
             start = dateLenth ? DateTimeOffset.Parse($"{tempStrart}").ToUnixTimeMilliseconds() : DateTimeOffset.Parse($"{tempStrart}").ToUnixTimeSeconds();
-            end = dateLenth ? DateTimeOffset.Parse($"{tempEnt}").ToUnixTimeMilliseconds() : DateTimeOffset.Parse($"{tempEnt}").ToUnixTimeSeconds();
+            end = dateLenth ? DateTimeOffset.Parse($"{tempEnd}").ToUnixTimeMilliseconds() : DateTimeOffset.Parse($"{tempEnd}").ToUnixTimeSeconds();
 
             return (start, end);
+        }
 
+        /// <summary>
+        /// 将时间戳格式 转换成string类型
+        /// 例:yyyyMMdd,等string类型
+        /// </summary>
+        /// <param name="unixTime"></param>
+        /// <returns></returns>
+        public static (string timeStart,string timeEnd) GetUnixToDate(long start,long end = 0,string types="yyyyMMdd")
+        {
+            string tStart = "";
+            string tEnd = "";
+            DateTimeOffset time = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1, 0, 0, 0, 0));
+            tStart = time.AddMilliseconds(start).ToString($"{types}");
+            if (end > 0) 
+            {
+                tEnd = time.AddMilliseconds(end).ToString($"{types}");
+            }
+
+            return (tStart, tEnd);
         }
 
 
+
     }
 }

+ 1 - 1
TEAMModelBI/appsettings.json

@@ -28,7 +28,7 @@
   "Azure": {
     //
     "Storage": {
-      "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=teammodelstorage;AccountKey=Yq7D4dE6cFuer2d2UZIccTA/i0c3sJ/6ITc8tNOyW+K5f+/lWw9GCos3Mxhj47PyWQgDL8YbVD63B9XcGtrMxQ==;EndpointSuffix=core.chinacloudapi.cn"
+      "ConnectionString": "DefaultEndpointsProtocol=https;AccountName=teammodeltest;AccountKey=O2W2vadCqexDxWO+px+QK7y1sHwsYj8f/WwKLdOdG5RwHgW/Dupz9dDUb4c1gi6ojzQaRpFUeAAmOu4N9E+37A==;EndpointSuffix=core.chinacloudapi.cn"
       //"ConnectionString": "DefaultEndpointsProtocol=https;AccountName=teammodelos;AccountKey=Dl04mfZ9hE9cdPVO1UtqTUQYN/kz/dD/p1nGvSq4tUu/4WhiKcNRVdY9tbe8620nPXo/RaXxs+1F9sVrWRo0bg==;EndpointSuffix=core.chinacloudapi.cn"
     },
     "Cosmos": {

+ 1 - 1
TEAMModelOS.FunctionV4/CosmosDB/TriggerExam.cs

@@ -1349,7 +1349,7 @@ namespace TEAMModelOS.FunctionV4
                 }
                 tasks.Add(_azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(classResult, classResult.id, new PartitionKey($"{classResult.code}")));
             }
-            Task.WhenAll(tasks);
+            await Task.WhenAll(tasks);
             result.standard = sco > 0 ? Math.Round(stand / sco, 2) : 0;
             result.csRate = csRate;
             result.lostStus = lostStu;

+ 38 - 14
TEAMModelOS.FunctionV4/ServiceBus/ActiveTaskTopic.cs

@@ -25,6 +25,7 @@ using TEAMModelOS.SDK.Services;
 using Azure.Storage.Blobs.Models;
 using System.IO;
 using Azure;
+using static TEAMModelOS.SDK.Models.Service.LessonService;
 
 namespace TEAMModelOS.FunctionV4.ServiceBus
 {
@@ -869,7 +870,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
             }
 
             var client = _azureCosmos.GetCosmosClient();
-
+           
             if ($"{scope}".Equals("school") && !string.IsNullOrEmpty($"{school}"))
             {
                 blobname = $"{school}";
@@ -907,6 +908,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                 bool isReplace = true;
                 if (updates.IsNotEmpty())
                 {
+                    Teacher teacher = await client.GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<Teacher>(tmdid, new PartitionKey("Base"));
                     foreach (LessonUpdate update in updates)
                     {
                         switch (update.grant_type)
@@ -991,6 +993,8 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                         lessonRecord.totalInteractPoint = lessonBase.summary.totalInteractPoint;
                                     }
                                     // await _dingDing.SendBotMsg($"{_option.Location},课堂id:{_lessonId} 更新完成", GroupNames.醍摩豆服務運維群組);
+
+                                    LessonService.DoAutoDeleteSchoolLessonRecord(lessonRecord, scope, client, school, tmdid,   teacher, _notificationService, _serviceBus, _azureStorage, _configuration);
                                     long? size = await _azureStorage.GetBlobContainerClient(blobname).GetBlobsSize($"records/{_lessonId}");
                                     Bloblog bloblog = new Bloblog
                                     {
@@ -1097,7 +1101,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                             case "create":
                                 oldlessonRecord = null;
                                 //处理课堂选用的课程信息
-                                Teacher teacher = await client.GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<Teacher>(tmdid, new PartitionKey("Base"));
+                              
                                 lessonRecord.show = teacher.lessonShow;
                                 if (!string.IsNullOrEmpty(lessonRecord.courseId))
                                 {
@@ -1244,18 +1248,36 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                             if (ids.Count >= limit)
                                             {
                                                 // 1-时间戳,7-时间戳
-                                                Dictionary<int, long> result = new Dictionary<int, long>();
+                                                Dictionary<int, ExpireTag> result = new Dictionary<int, ExpireTag>();
                                                 //暂定7天 
                                                 var now = DateTimeOffset.UtcNow;
                                                 //剩余3天的通知
                                                 //var day3= now.AddDays(Constant.private_lesson_expire - 3).ToUnixTimeMilliseconds();
                                                 //result.Add(3, day3);
                                                 //剩余1天的通知
-                                                var day1 = now.AddDays(Constant.private_lesson_expire - 6).ToUnixTimeMilliseconds();
-                                                result.Add(1, day1);
+                                                var day1 = now.AddDays(Constant.private_lesson_expire - (Constant.private_lesson_expire-1)).ToUnixTimeMilliseconds();
+                                                result.Add(1, new ExpireTag { expire = day1, tag = "notification" });
                                                 //到期通知
-                                                lessonRecord.expire = now.AddDays(Constant.private_lesson_expire).ToUnixTimeMilliseconds();
-                                                result.Add(Constant.private_lesson_expire, lessonRecord.expire);
+
+                                                //不到五点上传的课例,七天之后直接删除。
+                                                int addSecond = 0;
+                                                if (now.Hour > 5)
+                                                {
+                                                    // 到凌晨00点还差 (24 - now.Hour) *60 * 60 分钟,再加天数;
+                                                    addSecond = Constant.private_lesson_expire * 86400 + (24 - now.Hour) * 3600;
+                                                    //再加 00到05小时内的 随机秒数
+                                                    Random rand = new Random();
+                                                    int randInt= rand.Next(0 , 18000);
+                                                    addSecond += randInt;
+                                                }
+                                                else
+                                                {
+                                                    addSecond = Constant.private_lesson_expire * 24 * 60 * 60;
+                                                }
+
+                                                lessonRecord.expire = now.AddSeconds(addSecond).ToUnixTimeMilliseconds();
+                                                //result.Add(Constant.private_lesson_expire, lessonRecord.expire);
+                                                result.Add(Constant.private_lesson_expire, new ExpireTag { expire = lessonRecord.expire, tag = "delete" });
                                                 string biz = "expire";
                                                 Notification notification = new Notification
                                                 {
@@ -1272,10 +1294,11 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                                         tmdname = teacher.name,
                                                         sid = lessonRecord.id,
                                                         sname = lessonRecord.name,
+                                                        scope=scope,
                                                         stime = lessonRecord.startTime,
                                                         expire = lessonRecord.expire,
                                                         status = 1,
-                                                        day = Constant.private_lesson_expire,
+                                                        //day = Constant.private_lesson_expire,
                                                         time = now
                                                     }.ToJsonString(),
                                                     expires = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeSeconds()
@@ -1307,10 +1330,11 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                                             tmdid = tmdid,
                                                             tmdname = teacher.name,
                                                             name = lessonRecord.name,
-                                                            startTime = lessonRecord.startTime
+                                                            startTime = lessonRecord.startTime,
+                                                            tag = item.Value.tag
                                                         }.ToJsonString());
                                                         message.ApplicationProperties.Add("name", "LessonRecordExpire");
-                                                        long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), message, DateTimeOffset.FromUnixTimeMilliseconds(item.Value));
+                                                        long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), message, DateTimeOffset.FromUnixTimeMilliseconds(item.Value.expire));
                                                         ChangeRecord changeRecord = new ChangeRecord
                                                         {
                                                             RowKey = lessonRecord.id,
@@ -1362,7 +1386,6 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
         [Function("LessonRecordExpire")]
         public async Task LessonRecordExpireFunc([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "lesson-record-expire", Connection = "Azure:ServiceBus:ConnectionString")] string msg)
         {
-
             try
             {
                 var jsonMsg = JsonDocument.Parse(msg).RootElement;
@@ -1376,9 +1399,10 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                 jsonMsg.TryGetProperty("expire", out JsonElement expire);
                 jsonMsg.TryGetProperty("scope", out JsonElement scope);
                 jsonMsg.TryGetProperty("school", out JsonElement _school);
+                jsonMsg.TryGetProperty("tag", out JsonElement _tag);
                 var client = _azureCosmos.GetCosmosClient();
                 //处理到期删除
-                if ($"{progress}".Equals($"{Constant.private_lesson_expire}"))
+                if (_tag.ValueKind.Equals(JsonValueKind.String) && $"{_tag}".Equals("delete") )
                 {
                     string lessonId = $"{id}";
                     string tbname;
@@ -1433,7 +1457,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                         stime = long.Parse($"{startTime}"),
                         expire = long.Parse($"{expire}"),
                         status = 1,
-                        day = Constant.private_lesson_expire,
+                        //day = Constant.private_lesson_expire,
                         time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
                     }.ToJsonString(),
                     expires = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeSeconds()
@@ -1446,7 +1470,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
             }
             catch (Exception ex)
             {
-                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,Study()\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.醍摩豆服務運維群組);
+                await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,LessonRecordExpire()\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.醍摩豆服務運維群組);
             }
 
         }

+ 3 - 3
TEAMModelOS.FunctionV4/TEAMModelOS.FunctionV4.csproj

@@ -5,9 +5,9 @@
 		<OutputType>Exe</OutputType>
 		<_FunctionsSkipCleanOutput>true</_FunctionsSkipCleanOutput>
 		<SignAssembly>true</SignAssembly>
-		<AssemblyVersion>5.2205.7.1</AssemblyVersion>
-		<FileVersion>5.2205.7.1</FileVersion>
-		<Version>5.2205.7</Version>
+		<AssemblyVersion>5.2205.11.1</AssemblyVersion>
+		<FileVersion>5.2205.11.1</FileVersion>
+		<Version>5.2205.11</Version>
 		<PackageId>TEAMModelOS.FunctionV4</PackageId>
 		<Authors>teammodel</Authors>
 		<Company>醍摩豆(成都)信息技术有限公司</Company>

+ 1 - 0
TEAMModelOS.SDK/Context/Constant/Constant.cs

@@ -21,5 +21,6 @@ namespace TEAMModelOS.SDK.DI
         public static readonly string Student = "Student";
         public static readonly int private_lesson_limit =50;
         public static readonly int private_lesson_expire = 7;
+        public static readonly int school_lesson_expire = 7;
     }
 }

+ 7 - 0
TEAMModelOS.SDK/Models/Cosmos/Common/LessonRecord.cs

@@ -172,6 +172,9 @@ namespace TEAMModelOS.SDK.Models
         public double clientInteractionAverge { get; set; } = 0;
 
         public int examCount { get; set; }
+        /// <summary>
+        /// 总互动分
+        /// </summary>
         public double totalInteractPoint { get; set; } = 0;
         /// <summary>
         /// 过期时间,-1永不过期, 1577808000000 2020-01-01
@@ -189,6 +192,10 @@ namespace TEAMModelOS.SDK.Models
         /// 暂不 开放 teacher【开放给部分教师查看】醍摩豆id 
         /// </summary>
         public List<string> showTchs { get; set; } = new List<string>();
+        /// <summary>
+        /// 设置强制保留的 =1 ,不会被自动清理的。但是可以被手动清理。
+        /// </summary>
+        public int save { get; set; } = -1;
     }
     public class LessonTC
     {

+ 1 - 0
TEAMModelOS.SDK/Models/Cosmos/School/Paper.cs

@@ -62,6 +62,7 @@ namespace TEAMModelOS.SDK.Models
         public int multipleRule { get; set; }
         public string sheet { get; set; }
         public string sheetNo { get; set; }
+        public string mode { get; set; }
         //记录试卷大小
         public long? size { get; set; } = 0;
 

+ 19 - 2
TEAMModelOS.SDK/Models/Cosmos/School/SchoolSetting.cs

@@ -34,7 +34,24 @@ namespace TEAMModelOS.SDK.Models
         /// 课堂记录的标签
         /// </summary>
         public HashSet<string> lessonTag { get; set; } = new HashSet<string>();
+        public LessonSetting lessonSetting { get; set; }= new LessonSetting();
+    }
+    public class LessonSetting
+    {
+        /// <summary>
+        /// 是否开启自动清理  1 开启,0未开启
+        /// </summary>
+        public int openAutoClean { get; set; }
+        /// <summary>
+        /// 保留多少天
+        /// </summary>
+        public int expireDays { get; set; }
+        public List<LessonSettingCond> conds { get; set; }
+    }
+    public class LessonSettingCond
+    {
+        public string key { get; set; }
+        public double val { get; set; }
+        public string type { get; set; }
     }
-
-   
 }

+ 4 - 1
TEAMModelOS.SDK/Models/Service/Common/TeacherService.cs

@@ -29,7 +29,7 @@ namespace TEAMModelOS.Services
 {
     public static class TeacherService
     {
-        public static async Task<TeacherInfo> TeacherInfo(AzureCosmosFactory _azureCosmos, Teacher teacher, string name, string picture, string id, AzureStorageFactory _azureStorage, Option _option)
+        public static async Task<TeacherInfo> TeacherInfo(AzureCosmosFactory _azureCosmos, Teacher teacher, string name, string picture, string id, AzureStorageFactory _azureStorage, Option _option,AzureRedisFactory _azureRedis,string ip)
         {
             List<object> schools = new List<object>();
             List<AreaDto> areas = new List<AreaDto>();
@@ -277,6 +277,9 @@ namespace TEAMModelOS.Services
             //換取AuthToken,提供給前端
             var auth_token = JwtAuthExtension.CreateAuthToken(_option.HostName, id, name?.ToString(), picture?.ToString(), _option.JwtSecretKey, Website: "IES", scope: Constant.ScopeTeacher, standard: areaa != null ? areaa.standard : "", roles: roles.ToArray(), expire: 1);
 
+            //用户在线记录
+            teacher.loginInfos = await LoginService.DoLoginInfo(teacher.loginInfos, defaultschool, Constant.ScopeTeacher, id, ip, _azureRedis, _azureStorage, expire: 1);
+
             await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
             //取得Teacher Blob 容器位置及SAS 
             await _azureStorage.GetBlobContainerClient(id).CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建Teacher私有容器,如存在則不做任何事,保障容器一定存在

+ 4 - 4
TEAMModelOS.SDK/Models/Service/GroupListService.cs

@@ -38,7 +38,7 @@ namespace TEAMModelOS.SDK
             List<GroupListGrp> groupLists = new List<GroupListGrp>();
 
             if (groupTypes.IsEmpty() || groupTypes.Contains("class")) {
-                if (string.IsNullOrWhiteSpace(school) && memberType == 2)
+                if (!string.IsNullOrWhiteSpace(school) && memberType == 2)
                 {
                     Student student = null;
                     try
@@ -53,7 +53,7 @@ namespace TEAMModelOS.SDK
                     {
                         try
                         {
-                            Class clazz = await client.GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<Class>(memberId, new PartitionKey($"Class-{school}"));
+                            Class clazz = await client.GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<Class>(student.classId, new PartitionKey($"Class-{school}"));
                             GroupListGrp groupList = new GroupListGrp
                             {
                                 id = clazz.id,
@@ -80,8 +80,8 @@ namespace TEAMModelOS.SDK
             if (groupTypes.IsEmpty() || groupTypes.Contains("teach")) {
                 //教学班
                 StringBuilder teachsql = new StringBuilder();
-                if (string.IsNullOrWhiteSpace(school) && memberType == 2) {
-                    teachsql.Append($"SELECT distinct {SummarySql} FROM c join  m  in c.members   where c.type='teach' and  m.id='{memberId}' and m.school='{school}' and m.type=2 ");
+                if (!string.IsNullOrWhiteSpace(school) && memberType == 2) {
+                    teachsql.Append($"SELECT distinct {SummarySql} FROM c join  m  in c.members   where c.type='teach' and  m.id='{memberId}' and m.code='{school}' and m.type=2 ");
                 }
                 if (memberType == 1)
                 {

+ 349 - 0
TEAMModelOS.SDK/Models/Service/LessonService.cs

@@ -1,4 +1,7 @@
 using Azure.Cosmos;
+using Azure.Messaging.ServiceBus;
+using HTEXLib.COMM.Helpers;
+using Microsoft.Extensions.Configuration;
 using System;
 using System.Collections.Generic;
 using System.Text;
@@ -13,6 +16,352 @@ namespace TEAMModelOS.SDK.Models.Service
 {
     public class LessonService
     {
+
+        public static async void DoAutoDeleteSchoolLessonRecord(LessonRecord lessonRecord,string  scope ,CosmosClient client,string school,string tmdid,
+            Teacher teacher, NotificationService _notificationService, AzureServiceBusFactory _serviceBus, AzureStorageFactory _azureStorage, IConfiguration _configuration) {
+            if (lessonRecord.scope.Equals("school"))
+            {
+                SchoolSetting setting = null;
+                Azure.Response schoolSetting = await client.GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemStreamAsync(school, new PartitionKey("SchoolSetting"));
+                School schoolBase = await client.GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<School>(school, new PartitionKey("Base"));
+                if (schoolSetting.Status == 200)
+                {
+                    setting = JsonDocument.Parse(schoolSetting.Content).RootElement.Deserialize<SchoolSetting>();
+                    if (setting.lessonSetting != null)
+                    {
+                        if (setting.lessonSetting.openAutoClean != 0 || setting.lessonSetting.openAutoClean != 1)
+                        {
+                            setting.lessonSetting.openAutoClean = 0;
+                            setting.lessonSetting.expireDays = Constant.school_lesson_expire;
+                        }
+                    }
+                    else
+                    {
+                        setting.lessonSetting = new LessonSetting() { openAutoClean = 0, expireDays = Constant.school_lesson_expire };
+                    }
+                }
+                else
+                {
+                    setting = new SchoolSetting() { lessonSetting = new LessonSetting { openAutoClean = 0, expireDays = Constant.school_lesson_expire } };
+                }
+                int school_lesson_expire =0;
+                bool save = true;
+                List<string> msg = new List<string>();
+                if (setting.lessonSetting.openAutoClean == 1)
+                {
+                    if (setting.lessonSetting.conds.IsEmpty())
+                    {
+                        save = false;
+                    }
+                    else {
+                        school_lesson_expire = setting.lessonSetting.expireDays;
+                        foreach (var item in setting.lessonSetting.conds)
+                        {
+                            switch (item.type)
+                            {
+                                case ">=":
+                                    switch (item.key)
+                                    {
+                                        case "attendRate":
+                                            if (!(lessonRecord.attendRate >= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.attendRate}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "groupCount":
+                                            if (!(lessonRecord.groupCount >= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.groupCount}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "totalPoint":
+                                            if (!(lessonRecord.totalPoint >= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.totalPoint}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "collateTaskCount":
+                                            if (!(lessonRecord.collateTaskCount >= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.collateTaskCount}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "collateCount":
+                                            if (!(lessonRecord.collateCount >= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.collateCount}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "pushCount":
+                                            if (!(lessonRecord.pushCount >= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.pushCount}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "totalInteractPoint":
+                                            if (!(lessonRecord.totalInteractPoint >= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.totalInteractPoint}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "interactionCount":
+                                            if (!(lessonRecord.interactionCount >= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.interactionCount}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "clientInteractionCount":
+                                            if (!(lessonRecord.clientInteractionCount >= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.clientInteractionCount}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "examQuizCount":
+                                            if (!(lessonRecord.examQuizCount >= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.examQuizCount}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "examPointRate":
+                                            if (!(lessonRecord.examPointRate >= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.examPointRate}{item.type}{item.val}");
+                                            }
+                                            break;
+                                    }
+                                    break;
+                                case "<=":
+                                    switch (item.key)
+                                    {
+                                        case "attendRate":
+                                            if (!(lessonRecord.attendRate <= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.attendRate}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "groupCount":
+                                            if (!(lessonRecord.groupCount <= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.groupCount}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "totalPoint":
+                                            if (!(lessonRecord.totalPoint <= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.totalPoint}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "collateTaskCount":
+                                            if (!(lessonRecord.collateTaskCount <= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.collateTaskCount}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "collateCount":
+                                            if (!(lessonRecord.collateCount <= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.collateCount}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "pushCount":
+                                            if (!(lessonRecord.pushCount <= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.pushCount}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "totalInteractPoint":
+                                            if (!(lessonRecord.totalInteractPoint <= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.totalInteractPoint}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "interactionCount":
+                                            if (!(lessonRecord.interactionCount <= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.interactionCount}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "clientInteractionCount":
+                                            if (!(lessonRecord.clientInteractionCount <= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.clientInteractionCount}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "examQuizCount":
+                                            if (!(lessonRecord.examQuizCount <= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.examQuizCount}{item.type}{item.val}");
+                                            }
+                                            break;
+                                        case "examPointRate":
+                                            if (!(lessonRecord.examPointRate <= item.val))
+                                            {
+                                                save = false;
+                                                msg.Add($"{item.key}:{lessonRecord.examPointRate}{item.type}{item.val}");
+                                            }
+                                            break;
+                                    }
+                                    break;
+                            }
+                        }
+                    }
+                }
+                else {
+                    save = false;
+                    school_lesson_expire = Constant.school_lesson_expire;
+                }
+                if (!save && school_lesson_expire > 0)
+                {
+                    // 1-时间戳,7-时间戳
+                    Dictionary<int, ExpireTag> result = new Dictionary<int, ExpireTag>();
+                    //暂定7天 
+                    var now = DateTimeOffset.UtcNow;
+                    //剩余3天的通知
+                    //var day3= now.AddDays(school_lesson_expire - 3).ToUnixTimeMilliseconds();
+                    //result.Add(3, day3);
+                    //剩余1天的通知
+                    var day1 = now.AddDays(school_lesson_expire - (school_lesson_expire - 1)).ToUnixTimeMilliseconds();
+                    result.Add(1, new ExpireTag { expire= day1 ,tag= "notification" });
+                    //到期通知
+                    //不到五点上传的课例,七天之后直接删除。
+                    int addSecond = 0;
+                    if (now.Hour > 5)
+                    {
+                        // 到凌晨00点还差 (24 - now.Hour) *60 * 60 分钟,再加天数;
+                        addSecond = school_lesson_expire * 86400 + (24 - now.Hour) * 3600-(now.Hour* 3600);
+                        //再加 00到05小时内的 随机秒数
+                        Random rand = new Random();
+                        int randInt = rand.Next(0, 18000);
+                        addSecond += randInt;
+                    }
+                    else
+                    {
+                        addSecond = school_lesson_expire * 24 * 60 * 60;
+                    }
+                    lessonRecord.expire = now.AddSeconds(addSecond).ToUnixTimeMilliseconds();
+                    result.Add(school_lesson_expire, new ExpireTag { expire = lessonRecord.expire, tag = "delete" });
+                   // result.Add(school_lesson_expire, lessonRecord.expire);
+                    string biz = "expire";
+                    Notification notification = new Notification
+                    {
+                        hubName = "hita",
+                        type = "msg",
+                        from = $"ies5:{ Environment.GetEnvironmentVariable("Option:Location")}:private",
+                        to = new List<string> { tmdid },
+                        label = $"{biz}_lessonRecord",
+                        body = new
+                        {
+                            location = $"{Environment.GetEnvironmentVariable("Option:Location")}",
+                            biz = biz,
+                            tmdid = tmdid,
+                            tmdname = teacher.name,
+                            scope = scope,
+                            school = school,
+                            schoolName = schoolBase.name,
+                            sid = lessonRecord.id,
+                            sname = lessonRecord.name,
+                            stime = lessonRecord.startTime,
+                            expire = lessonRecord.expire,
+                            status = 1,
+                            //day = school_lesson_expire,
+                            time = now
+                        }.ToJsonString(),
+                        expires = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeSeconds()
+                    };
+                    var url = _configuration.GetValue<string>("HaBookAuth:CoreService:sendnotification");
+                    var clientID = _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
+                    var clientSecret = _configuration.GetValue<string>("HaBookAuth:CoreService:clientSecret");
+                    var location = $"{Environment.GetEnvironmentVariable("Option:Location")}";
+                    await _notificationService.SendNotification(clientID, clientSecret, location, url, notification); //站内发送消息
+
+                    var table = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
+                    List<ChangeRecord> records = await table.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", lessonRecord.id } });
+                    if (records.Count <= 0)
+                    {
+                        foreach (var item in result)
+                        {
+                            string PartitionKey = string.Format("{0}{1}{2}", lessonRecord.code, "-", $"expire-{item.Key}");
+                            //课堂的id ,
+                            //课堂的通知时间类型progress, 默认就会发送一条,到期前一天发送一条,最后已到期发送一条。
+                            var message = new ServiceBusMessage(new
+                            {
+                                id = lessonRecord.id,
+                                progress = item.Key,
+                                code = lessonRecord.code,
+                                scope = lessonRecord.scope,
+                                school = lessonRecord.school,
+                                opt = "delete",
+                                expire = lessonRecord.expire,
+                                tmdid = tmdid,
+                                tmdname = teacher.name,
+                                name = lessonRecord.name,
+                                startTime = lessonRecord.startTime,
+                                tag = item.Value.tag
+                            }.ToJsonString());
+                            message.ApplicationProperties.Add("name", "LessonRecordExpire");
+                            long start = await _serviceBus.GetServiceBusClient().SendScheduleMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), message, DateTimeOffset.FromUnixTimeMilliseconds(item.Value.expire));
+                            ChangeRecord changeRecord = new ChangeRecord
+                            {
+                                RowKey = lessonRecord.id,
+                                PartitionKey = PartitionKey,
+                                sequenceNumber = start,
+                                msgId = message.MessageId
+                            };
+                            await table.Save<ChangeRecord>(changeRecord);
+                        }
+                    }
+                }
+                else {
+
+                    if (lessonRecord.expire > 0)
+                    {
+                        var table = _azureStorage.GetCloudTableClient().GetTableReference("ChangeRecord");
+                        List<ChangeRecord> records = await table.FindListByDict<ChangeRecord>(new Dictionary<string, object>() { { "RowKey", lessonRecord.id } });
+                        foreach (var record in records)
+                        {
+                            try
+                            {
+                                await table.DeleteSingle<ChangeRecord>(record.PartitionKey, record.RowKey);
+                                await _serviceBus.GetServiceBusClient().CancelMessageAsync(Environment.GetEnvironmentVariable("Azure:ServiceBus:ActiveTask"), record.sequenceNumber);
+                            }
+                            catch (Exception)
+                            {
+                                continue;
+                            }
+                        }
+                    }
+                    lessonRecord.save = 1;
+                    lessonRecord.expire = -1;
+                }
+            }
+        }
+
+
+        public record ExpireTag {
+            public long expire { get; set; }
+            public string tag { get; set; }
+        }
         /// <summary>
         /// 
         /// </summary>

+ 308 - 23
TEAMModelOS.SDK/Models/Service/LoginService.cs

@@ -1,5 +1,7 @@
-using Microsoft.AspNetCore.Http;
+using Azure.Cosmos;
+using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Logging;
+using StackExchange.Redis;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -7,6 +9,7 @@ using System.Text;
 using System.Threading.Tasks;
 using TEAMModelOS.Models;
 using TEAMModelOS.SDK.DI;
+using TEAMModelOS.SDK.Models.Table;
 using static TEAMModelOS.SDK.Models.Teacher;
 
 namespace TEAMModelOS.SDK.Models.Service
@@ -14,19 +17,33 @@ namespace TEAMModelOS.SDK.Models.Service
     public static class LoginService
     {
         /// <summary>
-        /// 
+        /// //添加用户登录信息和在线登录
         /// </summary>
-        /// <param name="loginInfos"></param>
-        /// <param name="expire"></param>
-        /// <param name="now"></param>
-        /// <param name="school"></param>
-        /// <param name="scope">Teacher Student  TmdUser</param>
-        /// <param name="id"></param>
-        /// <param name="ip"></param>
-        /// <param name="region"></param>
+        /// <param name="school">学校</param>
+        /// <param name="scope">登录类型Teacher Student  TmdUser</param>
+        /// <param name="id">登录者的ID</param>
+        /// <param name="ip">登陆者的IP地址</param>
+        /// <param name="_azureRedis">redis</param>
+        /// <param name="_azureStorage">table表</param>
+        /// <param name="client">cosmosDB数据库连接</param>
+        /// <param name="expire">到期时间</param>
+        /// <param name="region">上次登录地址</param>
         /// <returns></returns>
-        public static async Task<List<LoginInfo>> DoLoginInfo(List<LoginInfo> loginInfos, long expire, long now, string school, string scope, string id, string ip, string region)
+        public static async Task<List<LoginInfo>> DoLoginInfo(List<LoginInfo> loginInfos, string school, string scope, string id, string ip, AzureRedisFactory _azureRedis, AzureStorageFactory _azureStorage, int expire = 1, string region = null)
         {
+            var table = _azureStorage.GetCloudTableClient().GetTableReference("IESLogin");
+            DateTimeOffset dateTime = DateTimeOffset.UtcNow;
+            var dateHour = dateTime.ToString("yyyyMMddHH"); //获取当天的小时
+            var dateDay = dateTime.ToString("yyyyMMdd"); //获取当天的日期
+            var dateMonth = dateTime.ToString("yyyyMM");//获取当月的日期
+            var currentHour = dateTime.Hour;
+            var currentDay = dateTime.Day;
+            long Expire = dateTime.AddHours(expire).ToUnixTimeMilliseconds();  //token到期时间
+            long now = dateTime.ToUnixTimeMilliseconds();   //时间戳
+
+            DateTime hour = DateTime.UtcNow.AddHours(25);   //25小时到期
+            DateTime month = DateTime.UtcNow.AddDays(32);   //一个月到期
+
             if (loginInfos.Any())
             {
                 if (loginInfos.Count() >= 5)
@@ -34,29 +51,297 @@ namespace TEAMModelOS.SDK.Models.Service
                     loginInfos = loginInfos.OrderBy(x => x.expire).ToList();
                     loginInfos.RemoveRange(4, loginInfos.Count() - 4);
                 }
-                loginInfos.Add(new LoginInfo { expire = expire, ip = ip, time = now });
+                loginInfos.Add(new LoginInfo { expire = Expire, ip = ip, time = now });
             }
             else
+                loginInfos = new List<LoginInfo> { new LoginInfo { expire = Expire, ip = ip, time = now } };
+
+            await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:IES:{scope}:{dateDay}", $"{currentHour}", 1);//一天24小时
+            await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:IES:{scope}:{dateMonth}", $"{currentDay}", 1); //当天的累计
+
+            var resDay = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"Login:IES:{scope}:{dateDay}");
+            if (resDay == null)
             {
-                loginInfos = new List<LoginInfo> { new LoginInfo { expire = expire, ip = ip, time = now } };
+                await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:IES:{scope}:{dateDay}", hour);  //设置到期时间
             }
-            if (!string.IsNullOrWhiteSpace(school)) {
-                //Login:School:School_ID:Teacher:20220506   value 15   1
-                //await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:School:{school}:20220506", "15",1);
 
-                //await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:School:{school}:20220506", "15", 1);
+            var rspMonth = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"Login:IES:{scope}:{dateMonth}");
+            if (rspMonth == null)
+            {
+                await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:IES:{scope}:{dateMonth}", month);  //设置到期时间
             }
-            //Login:IES:20220506
 
-            //var counts = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Vote:Count:{vote.id}");
-            //List<dynamic> countcds = new List<dynamic>();
-            //if (counts != null && counts.Length > 0)
+            ////查询Redis是否有值
+            //var dayCnt = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:IES:{scope}:{dateDay}");
+            //List<dynamic> dayCounts = new();
+            //if (dayCnt != null && dayCnt.Length > 0)
+            //{
+            //    foreach (var hourCnt in dayCnt)
+            //    {
+            //        dayCounts.Add(new { code = hourCnt.Element.ToString(), count = (int)hourCnt.Score });
+            //    }
+            //}
+
+            //var monthCnt = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:IES:{scope}:{dateMonth}");
+            //List<dynamic> monthCounts = new();
+            //if (monthCnt != null && monthCnt.Length > 0)
             //{
-            //    foreach (var count in counts)
+            //    foreach (var mCnt in monthCnt)
             //    {
-            //        countcds.Add(new { code = count.Element.ToString(), count = (int)count.Score });
+            //        monthCounts.Add(new { code = mCnt.Element.ToString(), count = (int)mCnt.Score });
             //    }
             //}
+
+            //保存当天每小时的峰值
+            List<HourLogin> hourLogins = await table.QueryWhereString<HourLogin>($"PartitionKey eq 'HourLogin' and RowKey eq '{dateHour}'");
+            if (hourLogins.Count > 0)
+            {
+                foreach (var hourLogin in hourLogins)
+                {
+                    //hourLogin.Hour = currentHour;
+                    if (scope.Equals("teacher"))
+                        hourLogin.Teacher += 1;
+                    else if (scope.Equals("student"))
+                        hourLogin.Student += 1;
+                    else
+                        hourLogin.TmdUser += 1;
+                }
+
+                try
+                {
+                    await table.SaveOrUpdateAll(hourLogins);
+                }
+                catch
+                {
+                }
+            }
+            else
+            {
+                HourLogin hourLogin = new() { PartitionKey = $"HourLogin", RowKey = dateHour, Hour = currentHour };
+                if (scope.Equals("teacher"))
+                {
+                    hourLogin.Teacher = 1;
+                    hourLogin.Student = 0;
+                    hourLogin.TmdUser = 0;
+                }
+                else if (scope.Equals("student"))
+                {
+                    hourLogin.Teacher = 0;
+                    hourLogin.Student = 1;
+                    hourLogin.TmdUser = 0;
+                }
+                else
+                {
+                    hourLogin.Teacher = 0;
+                    hourLogin.Student = 0;
+                    hourLogin.TmdUser = 1;
+                }
+                try
+                {
+                    await table.SaveOrUpdate<HourLogin>(hourLogin);//保存在线数据
+                }
+                catch
+                {
+                }
+            }
+
+            //保存当天的峰值
+            List<DayLogin> dayLogins = await table.QueryWhereString<DayLogin>($"PartitionKey eq 'DayLogin' and RowKey eq '{dateDay}'");
+            if (dayLogins.Count > 0)
+            {
+                foreach (var dayLogin in dayLogins)
+                {
+                    //dayLogin.Day = currentDay;
+                    if (scope.Equals("teacher"))
+                        dayLogin.Teacher += 1;
+                    else if (scope.Equals("student"))
+                        dayLogin.Student += 1;
+                    else
+                        dayLogin.TmdUser += 1;
+                }
+
+                try
+                {
+                    await table.SaveOrUpdateAll(dayLogins);
+                }
+                catch
+                {
+                }
+            }
+            else
+            {
+                //保存当月每天的峰值
+                DayLogin dayLogin = new() { PartitionKey = $"DayLogin", RowKey = dateDay, Day = currentDay };
+                if (scope.Equals("teacher"))
+                {
+                    dayLogin.Teacher = 1;
+                    dayLogin.Student = 0;
+                    dayLogin.TmdUser = 0;
+                }
+                else if (scope.Equals("student"))
+                {
+                    dayLogin.Teacher = 0;
+                    dayLogin.Student = 1;
+                    dayLogin.TmdUser = 0;
+                }
+                else
+                {
+                    dayLogin.Teacher = 0;
+                    dayLogin.Student = 0;
+                    dayLogin.TmdUser = 1;
+                }
+
+                await table.SaveOrUpdate<DayLogin>(dayLogin);//保存在线数据
+            }
+
+            if (!string.IsNullOrWhiteSpace(school))
+            {
+                await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:School:{school}:{scope}:{dateDay}", $"{currentHour}", 1);//当天当前小时在线人加1
+                await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:School:{school}:{scope}:{dateMonth}", $"{currentDay}", 1); //当天的在线加1
+
+                var reScDay = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"Login:School:{school}:{scope}:{dateDay}");
+                if (reScDay == null)
+                {
+                    await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:School:{school}:{scope}:{dateDay}", hour);  //设置到期时间
+                }
+
+                var reScMonth = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"Login:School:{school}:{scope}:{dateMonth}");
+                if (reScMonth == null)
+                {
+                    await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:School:{school}:{scope}:{dateMonth}", month);  //设置到期时间
+                }
+
+                ////查询Redis是否有值
+                //var scDayCnt = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:School:{school}:{scope}:{dateDay}");
+                //List<dynamic> scDayCounts = new();
+                //if (scDayCnt != null && scDayCnt.Length > 0)
+                //{
+                //    foreach (var itemHour in scDayCnt)
+                //    {
+                //        scDayCounts.Add(new { code = itemHour.Element.ToString(), count = (int)itemHour.Score });
+                //    }
+                //}
+                //var ScMonth = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:School:{school}:{scope}:{dateMonth}");
+                //List<dynamic> scMonthCount = new();
+                //if (ScMonth != null && ScMonth.Length > 0)
+                //{
+                //    foreach (var count in ScMonth)
+                //    {
+                //        scMonthCount.Add(new { code = count.Element.ToString(), count = (int)count.Score });
+                //    }
+                //}
+
+                //保存学校当天每小时的
+                List<HourLoginSchool> hourLoginSchools = await table.QueryWhereString<HourLoginSchool>($"PartitionKey eq 'HourLogin-{school}' and RowKey eq '{dateHour}'");
+                if (hourLoginSchools.Count > 0)
+                {
+                    foreach (var hLoginSc in hourLoginSchools)
+                    {
+                        //hLoginSc.Hour = currentHour;
+                        if (scope.Equals("teacher"))
+                            hLoginSc.Teacher += 1;
+                        else if (scope.Equals("student"))
+                            hLoginSc.Student += 1;
+                        else
+                            hLoginSc.TmdUser += 1;
+                    }
+
+                    try
+                    {
+                        await table.SaveOrUpdateAll(hourLoginSchools);
+                    }
+                    catch
+                    {
+                    }
+                }
+                else
+                {
+                    //学校小时峰值
+                    HourLoginSchool hourLoginSc = new() { PartitionKey = $"HourLogin-{school}", RowKey = dateHour, Hour = currentHour, School = school };
+                    if (scope.Equals("teacher"))
+                    {
+                        hourLoginSc.Teacher = 1;
+                        hourLoginSc.Student = 0;
+                        hourLoginSc.TmdUser = 0;
+                    }
+                    else if (scope.Equals("student"))
+                    {
+                        hourLoginSc.Teacher = 0;
+                        hourLoginSc.Student = 1;
+                        hourLoginSc.TmdUser = 0;
+                    }
+                    else
+                    {
+                        hourLoginSc.Teacher = 0;
+                        hourLoginSc.Student = 0;
+                        hourLoginSc.TmdUser = 1;
+                    }
+
+                    try
+                    {
+                        await table.SaveOrUpdate<HourLoginSchool>(hourLoginSc);//保存在线数据
+                    }
+                    catch
+                    {
+                    }
+                }
+
+                //学校天峰值
+                List<DayLoginSchool> DayLoginSchools = await table.QueryWhereString<DayLoginSchool>($"PartitionKey eq 'DayLogin-{school}' and RowKey eq '{dateDay}'");
+                if (DayLoginSchools.Count > 0)
+                {
+                    foreach (var dLoginSc in DayLoginSchools)
+                    {
+                        //dLoginSc.Day = currentDay;
+                        if (scope.Equals("teacher"))
+                            dLoginSc.Teacher += 1;
+                        else if (scope.Equals("student"))
+                            dLoginSc.Student += 1;
+                        else
+                            dLoginSc.TmdUser += 1;
+                    }
+
+                    try
+                    {
+                        await table.SaveOrUpdateAll(DayLoginSchools);
+                    }
+                    catch
+                    {
+                    }
+                }
+                else
+                {
+                    //学校天峰值
+                    DayLoginSchool dayLoginSc = new() { PartitionKey = $"DayLogin-{school}", RowKey = dateDay, Day = currentDay, School = school };
+                    if (scope.Equals("teacher"))
+                    {
+                        dayLoginSc.Teacher = 1;
+                        dayLoginSc.Student = 0;
+                        dayLoginSc.TmdUser = 0;
+                    }
+                    else if (scope.Equals("student"))
+                    {
+                        dayLoginSc.Teacher = 0;
+                        dayLoginSc.Student = 1;
+                        dayLoginSc.TmdUser = 0;
+                    }
+                    else
+                    {
+                        dayLoginSc.Teacher = 0;
+                        dayLoginSc.Student = 0;
+                        dayLoginSc.TmdUser = 1;
+                    }
+
+                    try
+                    {
+                        await table.SaveOrUpdate<DayLoginSchool>(dayLoginSc);//保存在线数据
+                    }
+                    catch
+                    {
+                    }
+                }
+            }
+
             return loginInfos;
         }
 

+ 4 - 1
TEAMModelOS.SDK/Models/Service/Third/Xkw/XkwOAuthModel.cs

@@ -98,7 +98,10 @@ namespace TEAMModelOS.SDK.Models
         public string userId { get; set; }
         public string domain { get; set; }
     }
-
+    public record ServiceModule { 
+        public string module { get; set; }
+        public string url { get; set; }
+    }
     public record OAuthCode
     {
         public string code { get; set; }

+ 20 - 19
TEAMModelOS.SDK/Models/Table/IESLogin.cs

@@ -50,13 +50,13 @@ namespace TEAMModelOS.SDK.Models.Table
     public class HourLogin : TableEntity
     {
         /// <summary>
-        /// HourLogin
+        /// HourLogin   继承table不需要重复命名
         /// </summary>
-        public string PartitionKey { get; set; }
+        //public string PartitionKey { get; set; }
         /// <summary>
-        /// 2022050511  yyyyMMddHH
+        /// 2022050511  yyyyMMddHH   继承table不需要重复命名
         /// </summary>
-        public string RowKey { get; set; }
+        //public string RowKey { get; set; }
         /// <summary>
         /// 2022050512  yyyyMMddHH
         /// </summary>
@@ -73,15 +73,15 @@ namespace TEAMModelOS.SDK.Models.Table
     public class DayLogin : TableEntity
     {
         /// <summary>
-        /// DayLogin
+        /// DayLogin  继承table不需要重复命名
         /// </summary>
-        public string PartitionKey { get; set; }
+        //public string PartitionKey { get; set; }
         /// <summary>
-        /// 20220505  yyyyMMdd
+        /// 20220505  yyyyMMdd  继承table不需要重复命名
         /// </summary>
-        public string RowKey { get; set; }
+        //public string RowKey { get; set; }
         /// <summary>
-        /// 20220505  yyyyMMdd
+        /// 20220505  yyyyMMdd  
         /// </summary>
         public int Day { get; set; }
         public int Teacher { get; set; }
@@ -93,22 +93,23 @@ namespace TEAMModelOS.SDK.Models.Table
     /// 学校按天统计登录信息
     /// </summary>
     [TableName(Name = "IESLogin")]
-    public class DayLoginSchool : DayLogin
+    public class DayLoginSchool : TableEntity
     {
         /// <summary>
-        /// DayLogin-hbcn学校编码
+        /// DayLogin-hbcn学校编码 继承table不需要重复命名
         /// </summary>
-        public string PartitionKey { get; set; }
+        //public string PartitionKey { get; set; }
         /// <summary>
-        /// 20220505  yyyyMMdd
+        /// 20220505  yyyyMMdd   继承table不需要重复命名
         /// </summary>
-        public string RowKey { get; set; }
+        //public string RowKey { get; set; }
         /// <summary>
         /// 20220505  yyyyMMdd
         /// </summary>
         public int Day { get; set; }
         public int Teacher { get; set; }
         public int Student { get; set; }
+        public int TmdUser { get; set; }
         public string School { get; set; }
     }
 
@@ -116,23 +117,23 @@ namespace TEAMModelOS.SDK.Models.Table
     ///  学校小时峰值统计登录信息
     /// </summary>
     [TableName(Name = "IESLogin")]
-    public class HourLoginSchool : HourLogin
+    public class HourLoginSchool : TableEntity
     {
         /// <summary>
-        /// HourLogin-hbcn学校编码
+        /// HourLogin-hbcn学校编码  继承table不需要重复命名
         /// </summary>
-        public string PartitionKey { get; set; }
+        //public string PartitionKey { get; set; }
         /// <summary>
         /// 2022050511  yyyyMMddHH
         /// </summary>
-        public string RowKey { get; set; }
+        //public string RowKey { get; set; }
         /// <summary>
         /// 20220505  yyyyMMdd
         /// </summary>
         public int Hour { get; set; }
         public int Teacher { get; set; }
         public int Student { get; set; }
-       
+        public int TmdUser { get; set; }
         public string School { get; set; }
     }
 }

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

@@ -1,16 +1,25 @@
 <!DOCTYPE html>
 <html lang="en">
+
 <head>
 	<meta charset="utf-8">
 	<meta http-equiv="X-UA-Compatible" content="IE=edge">
 	<meta name="viewport" content="width=device-width,initial-scale=1.0">
 	<link rel="icon" href="<%= BASE_URL %>favicon.ico">
 	<link id="theme" type="text/css" rel="stylesheet" href="<%= BASE_URL %>theme/dark-theme.css">
-	<script src="<%= BASE_URL %>lang/zh-CN.js"></script>
+	<!-- <script src="<%= BASE_URL %>lang/zh-CN.js"></script>
 	<script src="<%= BASE_URL %>lang/zh-TW.js"></script>
-	<script src="<%= BASE_URL %>lang/en-US.js"></script>
+	<script src="<%= BASE_URL %>lang/en-US.js"></script> -->
 	<title></title>
 	<script>
+		// 自动根据浏览器系统语言设置语言(优先判断本地设置的语言,如果有则使用本地设置的语言,如果没有则使用浏览器系统语言)
+		const curLang = localStorage.getItem('cloudSetting') ? JSON.parse(localStorage.getItem('cloudSetting')).curLang : null
+		const navLang = curLang || navigator.language.toLowerCase()
+		const localLang = (navLang === 'zh' || navLang === 'zh-tw' || navLang === 'zh-cn' || navLang === 'zh-hk') ? navLang : false
+		const lang = localLang || 'en-us'
+		const langJsUrl = 'https://' + window.location.host + '/lang/' + lang + '.js'
+		document.write('<script  src="' + langJsUrl + '"><\/script>')
+		// 公式MathJax配置
 		MathJax = {
 			loader: {
 				load: ["input/tex", "output/svg"]
@@ -30,6 +39,7 @@
 			},
 			skipTags: ["script", "noscript", "style", "textarea", "pre", "code", "a"] //避开某些标签
 		};
+		// 本地设置赋值
 		let cloudSetting = localStorage.getItem('cloudSetting')
 		if (cloudSetting) {
 			cloudSetting = JSON.parse(cloudSetting)

File diff suppressed because it is too large
+ 43 - 8
TEAMModelOS/ClientApp/public/lang/en-US.js


+ 46 - 11
TEAMModelOS/ClientApp/public/lang/zh-CN.js

@@ -317,7 +317,7 @@ const LANG_ZH_CN = {
         syllabus: '课纲',
         itemAndPaper: '试题试卷',
         appData: '其他应用数据',
-        content: '内容资源',
+        content: '内容',
         used: '已使用:',
         expired: '服务到期日:',
         prodCont: '服务内容:',
@@ -580,6 +580,7 @@ const LANG_ZH_CN = {
             studentTableC8: '类型',
             studentTableC9: 'IRS号码',
             studentTableC10: '行政班',
+            studentTableC11: '昵称',
             addClassroomProp1: '选择预设教室',
             addClassroomProp2: '新建个人教室',
             chooseClassroom: '请选择系统的教室',
@@ -779,6 +780,7 @@ const LANG_ZH_CN = {
         nicknameTips: '请输入学生昵称',
         evRcd: '历次评测',
         gradeErr: '查询成绩数据失败',
+        rcdExpired:'到期',
 
         //ManageClass.vue
         stuMgt: '学生管理',
@@ -1717,6 +1719,8 @@ const LANG_ZH_CN = {
             submit: '提交',
             showComment: '查看互评',
             commentScore: '互评分数',
+            teacherStar:'教师评星',
+            studentStar:'互评评星',
             allComments: '全部评论',
             time: '发布时间',
             submitDetail: '作业提交详情',
@@ -2084,6 +2088,7 @@ const LANG_ZH_CN = {
     },
     // 活动模块
     learnActivity: {
+        noStartTimeTip:'不设置则默认为立即发布',
         school: '校',
         area: '区',
         noFound: '未查询到当前活动!',
@@ -2355,6 +2360,8 @@ const LANG_ZH_CN = {
             crtTips2: '2. 更多详细数据分析,请前往学情分析查看。',
             crtErr: '数据加载失败',
             noSeeRpt: '学生尚未作答,无法查看学生个人报告',
+            noSeeRpt1: '评测进行中,无法查看学生报告',
+            noSeeRpt2: '尚未完成当前学生的评分,无法查看学生报告',
             crtRateTitle: '正确率统计',
             crtLabel: '正确',
             wrongLabel: '错误',
@@ -2479,7 +2486,7 @@ const LANG_ZH_CN = {
             rmvTitle: '移除老师',
             rmvContent: '是否确认移除',
             baseErr: '请完成阅卷基础设置',
-            teacherErr: '请完成阅卷和相关老师设置',
+            teacherErr: '阅卷和相关老师设置',
             saveOk: '保存成功!',
             saveErr: '保存失败!',
             deleteOk: '删除成功',
@@ -2497,14 +2504,13 @@ const LANG_ZH_CN = {
             orderIndex: '序号',
             quNo: '题号',
             teaCount: '教师人数',
-            markCount1: '阅卷量',
+            markCount: '阅卷量',
             delBlockTitle: '删除题块',
             delBlockCont: '确认删除当前题块设置吗?',
             selectQuTips: '请选择题目进行划块',
             selectTeaTips: '请设置当前题块的阅卷老师',
             lastQu: '题目尚未完全分配',
             reapQu: '题目划块设置存在重复题目',
-            setQuBlock: '请设置题目分块',
             objectiveLabel: '(客)',
             alreadyLabel: '(已)',
             exTag: '异常',
@@ -2567,7 +2573,7 @@ const LANG_ZH_CN = {
             saveErr: '保存失败',
             noSocreErr: '请先打分',
             completeQu: '当前题目已阅完,请切换题目',
-            completeStu: '完成当前学生评分,如果继续评分,请切换学生',
+            completeStu: '当前完成当前学生评分,如果继续评分,请切换学生',
             noAnswer: '未作答',
             stuInfoErr: '学生信息异常',
             ummarkQu: '未阅题目',
@@ -2579,8 +2585,33 @@ const LANG_ZH_CN = {
             marking: '进行中'
         }
     },
+    // 时间计算相关
+    time: {
+        before: '前',
+        after: '后',
+        just: '刚刚',
+        seconds: '秒',
+        minutes: '分钟',
+        hours: '小时',
+        days: '天'
+    },
     // 课堂记录
     lessonRecord: {
+        overdue:'到期',
+        customSetting:'自定义设置',
+        open:'开启',
+        close:'关闭',
+        condTip:'默认课堂记录仅保留7天(被收藏的课例除外),若开启自定义设置,则需要您设置保留期限以及保留条件',
+        daySetting:'课例保留时间设置',
+        expire:'课堂记录保留期限',
+        day:'天',
+        condTip2:'符合以下条件的课堂记录,将会进行保留,不会被系统自动清除',
+        advanced:'进阶设置',
+        over:'大于等于',
+        less:'小于等于',
+        point:'分',
+        unit:'个',
+        setting:'课堂记录设置',
         all: '不限',
         cate: '类别',
         range: '范围',
@@ -2666,7 +2697,7 @@ const LANG_ZH_CN = {
             pie4: '科目',
             title2: '课例趋势图',
             title3: '减负数据分析',
-            title4: '常用科技手段统计',
+            title4: '常用科技工具统计',
             noData: '暂无数据',
             weekUtil: '第',
             week: '周',
@@ -2988,6 +3019,10 @@ const LANG_ZH_CN = {
             block2: '教室物联',
             block3: '德育评价',
             block4: '学情分析',
+            block5: '教学看板',
+            block6: '德育看板',
+            block7: '体育看板',
+            block8: '艺术看板',
             comingSoon: '即将上线',
             loading: '加载中',
             quit: '退出',
@@ -4508,9 +4543,6 @@ const LANG_ZH_CN = {
             wrongNum: "错误次数",
         },
         myAchievement: {
-            examAch: "评测成绩",
-            hwAch: "作业成绩",
-            cusAch: "老师给分",
             examMode: "评测模式",
             examIn: "发布来源",
             name: "名称",
@@ -4650,6 +4682,8 @@ const LANG_ZH_CN = {
         receiveBtn: '确认接收',
         noMatchTerm: '无匹配学期',
         emptyTip: '空目录册别无法进行分享!',
+        chooseTargetTip:'请先选择分享对象!',
+        chooseShareChapterTip:'请先选择需要分享的章节!',
         chooseChapterTip: '请先选择需要收藏的章节!',
         chooseVolumeTip: '请选择已创建的册别或者新建册别!',
         playFailTip: '资源暂不支持播放',
@@ -4866,9 +4900,10 @@ const LANG_ZH_CN = {
             discuss: '讨论中心',
             policy: '政策文件',
             research: '课例中心',
+            toSokrates:'苏格拉底',
             researchBoard: '数据看板',
             cusVideos: '课例数据',
-            cusVideoMgt: '课例管理',
+            cusVideoMgt: '课堂记录',
             lockTips: '锁定菜单栏',
             unlockTips: '解锁菜单栏',
             platform: '资源平台',
@@ -5469,7 +5504,7 @@ const LANG_ZH_CN = {
         text3: '更多筛选',
         text4: '输入考试名称...',
         text5: '当前筛选条件',
-        text6: '符合条件数量',
+        text6: '当前数量',
         text7: '参与人数',
         text8: '考试日期',
         text9: '无',

File diff suppressed because it is too large
+ 147 - 112
TEAMModelOS/ClientApp/public/lang/zh-TW.js


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

@@ -106,6 +106,7 @@ export default {
 			try{
 				let studentInfo = withToken ? await $api.login.getStuInfoByToken(params) : await $api.login.getStudentInfoByAccount(params)
 				if(studentInfo.error){
+					app.$Message.warning(studentInfo.message)
 					j(studentInfo)
 					return
 				}
@@ -136,7 +137,6 @@ export default {
 				})
 			}catch(e){
 				j(500)
-				console.log(e)
 			}
 		})
 		

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

@@ -26,7 +26,7 @@ export default {
         return post('/common/lesson-record/delete-lesson-record', data)
     }, 
 	// 查询学校设置的课例类别
-	findLessonTags: function (data) {
+	findLessonSettings: function (data) {
         return post('/school/setting/find-id', data)
     }, 
 	// 设置学校的课例类别

+ 20 - 3
TEAMModelOS/ClientApp/src/api/login.js

@@ -161,7 +161,7 @@ export default {
 			// 進行API呼叫
 			await post('/student/login', data).then(res => {
 				if (res.error == 0) {
-					console.log('学生账号登录数据 > ',res,data)
+					console.log('学生账号登录数据 > ', res, data)
 					// 儲存大雲Token
 					localStorage.setItem("access_token", res.token.access_token)
 					// localStorage.setItem("auth_token", res.auth_token)
@@ -228,7 +228,7 @@ export default {
 	 * 绑定手机号
 	 */
 	bandOpenAccount(data) {
-		return new Promise((resolve,reject) => {
+		return new Promise((resolve, reject) => {
 			let srvAdr = localStorage.getItem('srvAdr')
 			let url = store.state.config[srvAdr].coreAPIUrl
 			let nonceStr = 'habook'  // 檢查項目
@@ -265,5 +265,22 @@ export default {
 				}
 			)
 		})
-	}
+	},
+	/**
+	 * 获取快速登录code
+	 * 登录成功后根据idToken获取快读登录code
+	 * @returns 
+	 */
+	getCode: function (idToken) {
+		let srvAdr = localStorage.getItem('srvAdr') || 'China'
+		let url = store.state.config[srvAdr].coreAPIUrl
+		let nonceStr = 'habook' // 檢查項目
+		let data = {
+			'client_id': store.state.config[srvAdr].clientID,
+			'grant_type': 'code',
+			'nonce': nonceStr,
+			'id_token': idToken
+		}
+		return corePost(url + '/oauth2/login', data)
+	},
 }

BIN
TEAMModelOS/ClientApp/src/assets/image/import-teacher-tw.png


+ 40 - 2
TEAMModelOS/ClientApp/src/common/BaseLayout.vue

@@ -18,6 +18,7 @@
             </div>
             <Menu style="margin-top:0px" width="auto" :class="['menu-item', isCollapsed ? 'collapsed-menu' : '']" accordion :active-name="activeName" :open-names="openNames">
                 <vuescroll :ops="ops">
+                    <!-- 学校菜单 -->
                     <MenuGroup :title="$t('system.menu.school')">
                         <!-- 暂未提供判断是否购买服务的字段 -->
                         <p class="school-menu-tips" v-show="schoolStatusInfo">
@@ -33,7 +34,7 @@
                                         <Icon v-show="!isCollapsed" :custom="item.icon" :class="isCollapsed ? 'collapse-icon-size':''" size="16" />
                                         <span>{{item.name}}</span>
                                     </template>
-                                    <MenuItem :name="menuItem.menuName" :to="menuItem.router" v-for="(menuItem,i) in item.child" :key="i" v-show="$access.ability(menuItem.role,menuItem.permission).validateAll && menuItem.isShow">
+                                    <MenuItem :name="menuItem.menuName" @click.native.capture="menuClick(menuItem)" :to="menuItem.router" v-for="(menuItem,i) in item.child" :key="i" v-show="$access.ability(menuItem.role,menuItem.permission).validateAll && menuItem.isShow">
                                     <!-- <Tooltip :content="menuItem.name" placement="right" transfer v-show="isCollapsed"> -->
                                     <Icon v-show="isCollapsed" class="sub-menu-icon" :custom="menuItem.icon" size="16" />
                                     <!-- </Tooltip> -->
@@ -67,6 +68,7 @@
                             </div>
                         </div>
                     </MenuGroup>
+                    <!-- 个人菜单 -->
                     <MenuGroup :title="$t('system.menu.private')">
                         <div v-for="(item,index) in teacherMenu" :key="index">
                             <Submenu :name="item.subName" v-if="$access.ability(item.role,item.permission).validateAll && item.child.length && item.isShow" v-show="(index == 0 && $store.state.userInfo.hasSchool) || index > 0">
@@ -116,6 +118,7 @@
                 <Icon :title="isLock ? $t('system.menu.unlockTips') : $t('system.menu.lockTips')" :custom="isLock ? 'iconfont icon-lock' : 'iconfont icon-unlock'" @click="isLock = !isLock" class="lock-menu-btn" />
             </p>
         </Sider>
+        <Loading v-if="isLoading"></Loading>
         <!-- Body内容部分 -->
         <Layout :class="!isLock ?'collapsed-padding content-wrap':'content-wrap'">
             <slot name="content"></slot>
@@ -127,6 +130,7 @@ import { mapGetters } from 'vuex'
 export default {
     data() {
         return {
+            isLoading: false,
             systemLevel: this.$t('system.basic'),
             isLock: false,
             isShowAreaSelect: true,
@@ -194,6 +198,29 @@ export default {
             const proInfo = this.$store.state.user?.schoolProfile?.svcStatus || null
             console.log(proInfo)
             return proInfo && proInfo.YMPCVCIM
+        },
+        //获取快速登录的code
+        getLoginCode() {
+            this.isLoading = true
+            let idToken = localStorage.getItem('id_token')
+            this.$api.login.getCode(idToken).then(
+                res => {
+                    this.loginCode = res.code
+                    window.location = `https://sokrates.teammodel.cn/auth/login/callback-habook?code=${this.loginCode}`
+                },
+                err => {
+                    console.log('获取code失败', err)
+                }
+            ).finally(() => {
+                setTimeout(() => {
+                    this.isLoading = false
+                }, 500)
+            })
+        },
+        menuClick(menu) {
+            if (menu.router == '#') {
+                this.getLoginCode()
+            }
         }
     },
     computed: {
@@ -444,7 +471,18 @@ export default {
                             role: 'admin',
                             permission: 'research-read|research-upd',
                             menuName: 'courseCenter',
-                            isShow: this.$store.state.config.srvAdrType != 'product'
+                            isShow: this.$store.state.config.srvAdrType === 'test'
+                        },
+                        // 名师课堂
+                        {
+                            icon: 'iconfont icon-course-videos',
+                            name: this.$t('system.menu.toSokrates'),
+                            router: '#',
+                            tag: '',
+                            role: 'admin',
+                            permission: 'research-read|research-upd',
+                            menuName: 'courseCenter',
+                            isShow: true
                         },
                         {
                             icon: 'iconfont icon-data-count',

+ 1 - 1
TEAMModelOS/ClientApp/src/common/BaseSelectTch.vue

@@ -1,6 +1,6 @@
 <template>
 	<div class="base-tch-select">
-		<Select v-model="selectTch" filterable clearable @on-change="onSelectChange">
+		<Select v-model="selectTch" filterable clearable @on-change="onSelectChange" :placeholder="$t('jyzx.application.teacherName')">
 			<Option v-for="(item,index) in teacherList" :value="item.id" :label="item.name" :key="index">
 				<span style="margin-right: 10px;">{{item.name}}</span>
 				<span class="tch-group-tag" v-for="(group,groupIndex) in item.groups" :key="groupIndex">{{ group.name }}</span>

+ 1 - 0
TEAMModelOS/ClientApp/src/components/evaluation/ExerciseList.less

@@ -160,6 +160,7 @@
   }
   img {
     vertical-align: middle;
+    max-width: 100%;
   }
   .item-btn-toggle {
     position: absolute;

+ 4 - 0
TEAMModelOS/ClientApp/src/components/evaluation/ExerciseList.vue

@@ -466,6 +466,10 @@
 </style>
 
 <style>
+	.cp-exercise-item .item-question-text img{
+		max-width: 100%;
+	}
+
 	.circle {
 		border: solid 1px red;
 		background-color: red;

+ 1 - 1
TEAMModelOS/ClientApp/src/components/homework/BaseHwForm.vue

@@ -21,7 +21,7 @@
 							</BaseClassSelectPri>
 						</div>
 					</FormItem>
-					<FormItem :label="$t('learnActivity.createEv.startTime') + '(不设置则视为立即发布)'" prop="startTime" v-show="!isEdit || isDraft">
+					<FormItem :label="$t('learnActivity.createEv.startTime') + '(' + $t('learnActivity.noStartTimeTip') + ')'" prop="startTime" v-show="!isEdit || isDraft">
 						<DatePicker v-show="voteFormEdit" type="datetime" :options="startOption"
 							format="yyyy/MM/dd HH:mm" v-model="voteForm.startTime" split-panels
 							:placeholder="$t('learnActivity.createEv.sTimeHolder')" style="width:100%"

+ 16 - 3
TEAMModelOS/ClientApp/src/components/homework/BaseHwTable.vue

@@ -161,6 +161,7 @@
 				columns: [
 					{
 						title: vm.$t('homework.table.name'),
+						width: 160,
 						key: 'username',
 					},
 					{
@@ -208,15 +209,27 @@
 						}
 					},
 					{
-						title: vm.$t('homework.table.star'),
-						width: 80,
+						title: vm.$t('homework.table.teacherStar'),
+						width: 160,
+						render: (h, params) => {
+							return h('span', {
+								style: {
+									cursor: 'pointer',
+									fontWeight: 'bold'
+								},
+							}, params.row.teacherStar === -1 ? '—' : params.row.teacherStar.toFixed(1))
+						}
+					},
+					{
+						title: vm.$t('homework.table.studentStar'),
+						width: 160,
 						render: (h, params) => {
 							return h('span', {
 								style: {
 									cursor: 'pointer',
 									fontWeight: 'bold'
 								},
-							}, params.row.star === -1 ? '—' : params.row.star.toFixed(1))
+							}, params.row.studentStar === -1 ? '—' : params.row.studentStar.toFixed(1))
 						}
 					},
 					{

File diff suppressed because it is too large
+ 509 - 596
TEAMModelOS/ClientApp/src/components/selflearn/ExerciseList.less


+ 2 - 2
TEAMModelOS/ClientApp/src/components/syllabus/InviteTeacher.vue

@@ -224,7 +224,7 @@
 			/* 分享课纲操作 */
 			doShare(){
 				if(!this.curTeacher){
-					this.$Message.warning('请先选择分享对象!')
+					this.$Message.warning(this.$t('syllabus.chooseTargetTip'))
 					return
 				}
 				if(this.curTeacher.id === this.$store.state.userInfo.TEAMModelId){
@@ -232,7 +232,7 @@
 					return
 				}
 				if(!this.selectedChapters.length){
-					this.$Message.warning('请先选择需要分享的章节!')
+					this.$Message.warning(this.$t('syllabus.chooseShareChapterTip'))
 					return
 				}
 				this.isShareLoading = true

+ 6 - 20
TEAMModelOS/ClientApp/src/locale/index.js

@@ -1,35 +1,21 @@
 import Vue from 'vue'
 import VueI18n from 'vue-i18n'
-// 引入iView多语言包
-import zhLocale from 'view-design/src/locale/lang/zh-CN'
-import enLocale from 'view-design/src/locale/lang/en-US'
-import twLocale from 'view-design/src/locale/lang/zh-TW'
-// 引入elementUI多语言包
-import enEl from 'element-ui/lib/locale/lang/en'
-import zhEl from 'element-ui/lib/locale/lang/zh-CN'
-import twEl from 'element-ui/lib/locale/lang/zh-TW'
+import tools from '@/utils/public.js'
 import ElementLocale from 'element-ui/lib/locale'
 ElementLocale.i18n((key, value) => i18n.t(key, value))
 Vue.use(VueI18n)
 // 自动根据浏览器系统语言设置语言(优先判断本地设置的语言,如果有则使用本地设置的语言,如果没有则使用浏览器系统语言)
-const curLang  = localStorage.getItem('cloudSetting') ? JSON.parse(localStorage.getItem('cloudSetting')).curLang : null
+const curLang = localStorage.getItem('cloudSetting') ? JSON.parse(localStorage.getItem('cloudSetting')).curLang : null
 const navLang = curLang || navigator.language.toLowerCase()
 const localLang = (navLang === 'zh' || navLang === 'zh-tw' || navLang === 'zh-cn' || navLang === 'zh-hk') ? navLang : false
 let lang = localLang || 'en-us'
 localStorage.setItem('local', lang)
 Vue.config.lang = lang
 Vue.locale = () => { }
-// 将本地语言包、elementUI语言包、iview语言包合并
-const messages = {
-    'zh-cn': Object.assign(LANG_ZH_CN, zhLocale, zhEl),
-    'zh': Object.assign(LANG_ZH_CN, zhLocale,zhEl),
-    'zh-tw': Object.assign(LANG_ZH_TW, twLocale,twEl),
-    'zh-hk': Object.assign(LANG_ZH_TW, twLocale, twEl),
-    'en-us': Object.assign(LANG_EN_US, enLocale, enEl)
-}
 const i18n = new VueI18n({
-  locale: lang,
-  messages
+  locale: lang
+})
+setTimeout(() => {
+  tools.seti18nLang(lang)
 })
-
 export default i18n

+ 4 - 0
TEAMModelOS/ClientApp/src/store/module/config.js

@@ -21,6 +21,10 @@ export default {
                     station: 'product',
                     url: 'https://jinniu.teammodel.cn'
                 },
+                {
+                    station: 'rc',
+                    url: 'https://rc.teammodel.net'
+                },
                 {
                     station: 'test',
                     url: 'https://test.teammodel.cn'

+ 3 - 0
TEAMModelOS/ClientApp/src/store/module/user.js

@@ -298,6 +298,9 @@ export default {
         setTeachers(state, data) {
             state.teachers = data
         },
+        resetTeachers(state, data) {
+            state.teachers = undefined
+        },
         delTeacher(state, idOrNames) {
             if (idOrNames && idOrNames.length) {
                 let teachers = state.teachers

+ 102 - 6
TEAMModelOS/ClientApp/src/utils/public.js

@@ -8,12 +8,22 @@ import JsPDF from 'jspdf'
 import 'jspdf-autotable'
 import domtoimage from '@/utils/dom_to_image';
 import excel from '@/utils/excel.js'
+import i18n from '@/locale'
+// 引入iView多语言包
+import zhLocale from 'view-design/src/locale/lang/zh-CN'
+import enLocale from 'view-design/src/locale/lang/en-US'
+import twLocale from 'view-design/src/locale/lang/zh-TW'
+// 引入elementUI多语言包
+import enEl from 'element-ui/lib/locale/lang/en'
+import zhEl from 'element-ui/lib/locale/lang/zh-CN'
+import twEl from 'element-ui/lib/locale/lang/zh-TW'
 import {
 	app
 } from '@/boot-app.js'
 import {
-	Message,Modal
+	Message, Modal
 } from 'view-design'
+
 /**
  * 校验blob授权是否过期
  * */
@@ -178,6 +188,92 @@ export default {
 		'justify', // 对齐方式
 		'table', // 表格
 	],
+	/* 切换语系 */
+	changeLang(lang) {
+		// 如果已经加载过语言包 则直接设置 否则加载语言包
+		if (lang.toLowerCase() === 'zh-cn' && window.LANG_ZH_CN) {
+			this.seti18nLang(lang)
+		} else if (lang.toLowerCase() === 'zh-tw' && window.LANG_ZH_TW) {
+			this.seti18nLang(lang)
+		} else if (lang.toLowerCase() === 'en-us' && window.LANG_EN_US) {
+			this.seti18nLang(lang)
+		} else {
+			this.loadLangJs(lang)
+		}
+	},
+	/* 加载语言包 */
+	loadLangJs(lang) {
+		let script = document.createElement('script')
+		script.type = 'text/javascript'
+		script.src = 'https://' + window.location.host + '/lang/' + lang + '.js'
+		document.getElementsByTagName('head')[0].appendChild(script)
+		script.onload = () => {
+			this.seti18nLang(lang)
+		}
+	},
+	/* 设置到i18n语系 */
+	seti18nLang(lang) {
+		if (lang.toLowerCase() === 'zh-cn') {
+			i18n.setLocaleMessage(lang, Object.assign(LANG_ZH_CN, zhLocale, zhEl))
+		} else if (lang.toLowerCase() === 'zh-tw') {
+			i18n.setLocaleMessage(lang, Object.assign(LANG_ZH_TW, twLocale, twEl))
+		} else {
+			i18n.setLocaleMessage(lang, Object.assign(LANG_EN_US, enLocale, enEl))
+		}
+		document.title = app.$t('system.title')
+	},
+	/**
+	 * @param {Number} num 数值
+	 * @returns {String} 处理后的字符串
+	 * @description 如果传入的数值小于10,即位数只有1位,则在前面补充0
+	 */
+	getHandledValue(num) {
+		return num < 10 ? '0' + num : num;
+	},
+	/**
+	 * @param {Number} timeStamp 传入的时间戳
+	 * @param {Number} startType 要返回的时间字符串的格式类型,传入'year'则返回年开头的完整时间
+	 */
+	getDate(timeStamp, startType) {
+		const d = new Date(timeStamp);
+		const year = d.getFullYear();
+		const month = this.getHandledValue(d.getMonth() + 1);
+		const date = this.getHandledValue(d.getDate());
+		const hours = this.getHandledValue(d.getHours());
+		const minutes = this.getHandledValue(d.getMinutes());
+		const second = this.getHandledValue(d.getSeconds());
+		let resStr = '';
+		if (startType === 'year') resStr = year + '-' + month + '-' + date + ' ' + hours + ':' + minutes + ':' + second;
+		else resStr = month + '-' + date + ' ' + hours + ':' + minutes;
+		return resStr;
+	},
+	/* 获取相对时间 */
+	getRelativeTime(timeStamp) {
+		// 获取当前时间时间戳
+		const currentTime = (new Date()).getTime();
+		// 判断传入时间戳是否早于当前时间戳
+		const IS_EARLY = timeStamp <= currentTime;
+		// 获取两个时间戳差值
+		let diff = currentTime - timeStamp;
+		// 如果IS_EARLY为false则差值取反
+		if (!IS_EARLY) diff = -diff;
+		let resStr = '';
+		let dirStr = IS_EARLY ? (app.$t('time.before')) : (app.$t('time.after'));
+
+		if (diff < 1000) resStr = app.$t('time.just');
+		// 少于等于59秒
+		else if (diff < 60000) resStr = parseInt(diff / 1000) + (app.$t('time.seconds')) + dirStr;
+		// 多于59秒,少于等于59分钟59秒
+		else if (diff >= 60000 && diff < 3600000) resStr = Math.floor(diff / 60000) + (app.$t('time.minutes')) + dirStr;
+		// 多于59分钟59秒,少于等于23小时59分钟59秒
+		else if (diff >= 3600000 && diff < 86400000) resStr = Math.floor(diff / 3600000) + (app.$t('time.hours')) + dirStr;
+		// 多于23小时59分钟59秒,少于等于29天59分钟59秒
+		else if (diff >= 86400000 && diff < 2623860000) resStr = Math.floor(diff / 86400000) + (app.$t('time.days')) + dirStr;
+		// 多于29天59分钟59秒,少于364天23小时59分钟59秒,且传入的时间戳早于当前
+		else if (diff >= 2623860000 && diff <= 31567860000 && IS_EARLY) resStr = this.getDate(timeStamp);
+		else resStr = this.getDate(timeStamp, 'year');
+		return resStr;
+	},
 	/* 全屏 */
 	fullScreen(element) {
 		// var element = document.documentElement;
@@ -266,27 +362,27 @@ export default {
 						if (videoTrack && audioTrack) {
 							let videoFormat = videoTrack.Format.toLowerCase()
 							let audioFormat = audioTrack.Format.toLowerCase()
-							if((videoFormat === 'avc' && audioFormat === 'aac') || (['vp8','vp9'].includes(videoFormat) && audioFormat === 'vorbis')) {
+							if ((videoFormat === 'avc' && audioFormat === 'aac') || (['vp8', 'vp9'].includes(videoFormat) && audioFormat === 'vorbis')) {
 								r(videoTrack.Format_Profile || true)
-							}else{
+							} else {
 								r(false)
 							}
 						}
 						// 如果是只有视频轨没有音频轨(PPT直接转视频)
 						else if (videoTrack && !audioTrack) {
 							let videoFormat = videoTrack.Format.toLowerCase()
-							if(videoFormat === 'avc'){
+							if (videoFormat === 'avc') {
 								Modal.info({
 									title: "温馨提示",
 									content: "视频文件未检测到音频轨,建议您调整后重新上传"
 								})
 								r(true)
-							}else{
+							} else {
 								r(false)
 							}
 						}
 						// 如果是音频文件则需要满足 mp3(MPEG Audio),ogg(Vorbis),wav(PCM) 任意一种编码格式
-						else if (!videoTrack && audioTrack && ['mpeg audio','vorbis','pcm'].includes(audioTrack.Format.toLowerCase())) {
+						else if (!videoTrack && audioTrack && ['mpeg audio', 'vorbis', 'pcm'].includes(audioTrack.Format.toLowerCase())) {
 							r(true)
 						} else {
 							r(false)

+ 1 - 0
TEAMModelOS/ClientApp/src/view/403.vue

@@ -14,6 +14,7 @@
 			/* 如果是已登录状态 则直接返回当前身份的首页 */
 			goLogin(){
 				this.$store.commit('user/resetSchoolProfile')
+				this.$store.commit('user/resetTeachers')
 				this.$router.push({
 				    path: '/login'
 				})

+ 1 - 0
TEAMModelOS/ClientApp/src/view/404.vue

@@ -12,6 +12,7 @@
 		methods:{
 			goLogin(){
 				this.$store.commit('user/resetSchoolProfile')
+				this.$store.commit('user/resetTeachers')
 				this.$User.logout().then(r => {
 					localStorage.setItem('target_path','')
 					this.$router.push({

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

@@ -305,6 +305,7 @@ export default {
         basicMenu(name) {
             if (name == 'quit') {
                 this.$store.commit('user/resetSchoolProfile')
+                this.$store.commit('user/resetTeachers')
                 this.$User.logout()
                 this.$router.push({
                     path: '/login'

+ 1 - 0
TEAMModelOS/ClientApp/src/view/areaMgmt/AreaBase.vue

@@ -150,6 +150,7 @@ export default {
         basicMenu(name) {
             if (name == 'quit') {
                 this.$store.commit('user/resetSchoolProfile')
+                this.$store.commit('user/resetTeachers')
                 this.$User.logout()
                 this.$router.push({
                     path: '/login'

+ 10 - 16
TEAMModelOS/ClientApp/src/view/classmgt/ClassNotice.vue

@@ -59,6 +59,7 @@
     </div>
 </template>
 <script>
+import { mapGetters } from 'vuex'
 export default {
     data() {
         return {
@@ -70,6 +71,11 @@ export default {
             publish: 1
         }
     },
+    computed: {
+        ...mapGetters({
+            classList: 'getMgtClasses'
+        }),
+    },
     methods: {
         editNotice(index) {
             this.$router.push({
@@ -149,24 +155,12 @@ export default {
                     )
                 }
             })
-        },
-        //查询自己管理的班级
-        findClass() {
-            this.$store.dispatch('user/getSchoolProfile').then(
-                res => {
-                    let classes = res.school_classes
-                    this.classList = classes.filter(item => {
-                        return this.$store.state.userInfo.mgtClasses.indexOf(item.id) > -1
-                    })
-                    if (this.classList.length) {
-                        this.findNotice()
-                    }
-                }
-            )
-        },
+        }
     },
     created() {
-        this.findClass()
+        if (this.classList.length) {
+            this.findNotice()
+        }
     }
 }
 </script>

File diff suppressed because it is too large
+ 130 - 82
TEAMModelOS/ClientApp/src/view/dashboard/Index.vue


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

@@ -40,6 +40,7 @@
 						<span class="paper-item-tag" v-if="isSchool">{{ paper.subjectName }}</span>
 						<span style="margin-left: 8px;">{{ paper.name }}</span>
 						<span style="margin-left: 8px;" v-for="(tag,tagIndex) in paper.tags"><Tag color="geekblue" style="vertical-align: sub;">{{ tag }}</Tag></span>
+						<span style="margin-left: 8px;" v-if="paper.mode"><Tag color="green" style="vertical-align: sub;">{{ paper.mode }}</Tag></span>
 					</div>
 					<div class="paper-item-info">
 						<span class="info-item" v-if="isSchool">{{$t('evaluation.paperList.usePeriod')}}:<span
@@ -246,6 +247,7 @@
 					fullPaperJson.code = curPaper.code
 					fullPaperJson.sheet = curPaper.sheet
 					fullPaperJson.sheetNo = curPaper.sheetNo
+					fullPaperJson.mode = curPaper.mode
 					let paper = fullPaperJson
 					let schoolInfo = null
 					if (paper.scope === 'school') {
@@ -268,6 +270,7 @@
 						item: paper.item,
 						sheet: paper.sheet || null,
 						sheetNo: paper.sheetNo || null,
+						mode: paper.mode || null,
 						multipleRule: paper.multipleRule,
 						startTime: 0,
 						endTime: 0,

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

@@ -122,8 +122,6 @@
         display: unset;
       }
     }
-    p {
-    }
   }
 }
 .content-wrap .exercise-item table, .content-wrap .exercise-item td {
@@ -582,7 +580,7 @@
   }
   &::-webkit-scrollbar-thumb {
     border-radius: 10px;
-    -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
+    box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
     background: #70707026;
   }
   &::-webkit-scrollbar-track {

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

@@ -40,7 +40,7 @@
 								v-show="paperInfo.item.length">{{ isShowAnalysis ? this.$t('evaluation.paperList.paperDetails') : this.$t('evaluation.paperList.paperAnalysis')}}</Button>
 							<Button class="base-info-btn" type="info" v-show="paperInfo.item.length && paperInfo.id" @click="onChooseOrderTemp">{{ $t('evaluation.orderTemp') }}</Button>
 							<Button class="base-info-btn" type="info" @click="downloadSheet" :loading="downLoading"
-								v-show="paperInfo.item.length && paperInfo.id && paper.sheetNo">{{ $t('evaluation.paperList.goAnswerSheet') }}</Button>
+								v-show="paperInfo.item.length && paperInfo.id && paper.sheetNo">{{ $t('evaluation.paperList.goAnswerSheet') }}<span v-if="paperInfo.mode">-{{ paperInfo.mode }}</span></Button>
 							<Button class="base-info-btn" type="info" @click="goAnswerSheet"
 								v-show="paperInfo.item.length && paperInfo.id">{{ paper.sheetNo ?  $t('evaluation.paperList.reCreateSheet') : $t('evaluation.paperList.createSheet') }}</Button>
 						</div>
@@ -52,7 +52,7 @@
 							<Button class="base-info-btn" type="info" @click="isShowAnalysis = !isShowAnalysis"
 								v-show="!isHideAnalysis">{{ isShowAnalysis ? this.$t('evaluation.paperList.paperDetails') : this.$t('evaluation.paperList.paperAnalysis')}}</Button>
 							<Button class="base-info-btn" type="info" @click="downloadSheet" :loading="downLoading"
-								v-show="paperInfo.id && paper.sheetNo && !hideSheet && !isSharePreview">{{ $t('evaluation.paperList.goAnswerSheet') }}</Button>
+								v-show="paperInfo.id && paper.sheetNo && !hideSheet && !isSharePreview">{{ $t('evaluation.paperList.goAnswerSheet') }}<span v-if="paperInfo.mode">-{{ paperInfo.mode }}</span></Button>
 							<Button class="base-info-btn" type="info" @click="goAnswerSheet"
 								v-show="paperInfo.id && !hideSheet && !isSharePreview">{{ paper.sheetNo ?  $t('evaluation.paperList.reCreateSheet') : $t('evaluation.paperList.createSheet') }}</Button>
 							<Button class="base-info-btn" type="info" @click="exitPreview"
@@ -249,13 +249,15 @@
 				console.log(this.paperInfo)
 				this.downLoading = true
 				let examId = this.paperInfo.examId
-				let pdfName = `${this.paperInfo.name}-${this.paperInfo.sheetNo}.pdf`
+				let sheetMode = this.paperInfo.mode ? `(${this.paperInfo.mode})` : ``
+				let fileName = `${this.paperInfo.name}-${this.paperInfo.sheetNo}${sheetMode}.pdf`
+				// let pdfName = `${this.paperInfo.name}-${this.paperInfo.sheetNo}.pdf`
 				let path = examId ? `exam/${examId}/paper/${this.paperInfo.subjectId}/` : `paper/${this.paperInfo.name}/`
 				let curScope = examId ? this.paperInfo.examScope : this.paperInfo.scope
 				let cntr = curScope === 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId
 				let sasData = curScope === 'school' ? await this.$tools.getSchoolSas() : await this.$tools.getPrivateSas()
 				let blobHost = this.$evTools.getBlobHost()
-				let fullPath = blobHost + '/' + cntr + '/' + path + pdfName + sasData.sas
+				let fullPath = blobHost + '/' + cntr + '/' + path + fileName + sasData.sas
 				const downloadRes = async () => {
 					let response = await fetch(fullPath); // 内容转变成blob地址
 					if(response.status !== 200){
@@ -265,7 +267,7 @@
 						let objectUrl = window.URL.createObjectURL(blob);
 						let a = document.createElement('a');
 						a.href = objectUrl;
-						a.download = pdfName;
+						a.download = fileName;
 						a.click()
 						a.remove(); 
 					}

+ 4 - 4
TEAMModelOS/ClientApp/src/view/homework/ManageHomeWork.vue

@@ -397,8 +397,8 @@
 			/* 导出表格 */
 			exportTable(){
 				const params = {
-					title: [this.$t('homework.table.name'),this.$t('stuAccount.account'),this.$t('homework.table.className'),this.$t('homework.table.submitTime'),this.$t('homework.table.columnScore'),this.$t('homework.table.columnComment'),this.$t('homework.table.star'),this.$t('homework.table.commentScore')],
-					key: ['username','userid','className','submitTime','score','comment','star','commentScore'],
+					title: [this.$t('homework.table.name'),this.$t('stuAccount.account'),this.$t('homework.table.className'),this.$t('homework.table.submitTime'),this.$t('homework.table.columnScore'),this.$t('homework.table.columnComment'),this.$t('homework.table.teacherStar'),this.$t('homework.table.studentStar')],
+					key: ['username','userid','className','submitTime','score','comment','teacherStar','studentStar'],
 					data: this.tableData.map(i => {
 						return {
 							username:i.username,
@@ -407,8 +407,8 @@
 							submitTime:i.submit ? this.$tools.formatTime(i.submitTime,'yyyy-MM-dd hh:mm') : '-',
 							score:i.score === -1 ? '-' : i.score,
 							comment:i.replies.length ? i.replies[0] : '—',
-							star:i.star === -1 ? '-' : i.star.toFixed(1),
-							commentScore:i.star === -1 ? '-' : i.star.toFixed(1),
+							teacherStar:i.teacherStar === -1 ? '-' : i.teacherStar.toFixed(1),
+							studentStar:i.studentStar === -1 ? '-' : i.studentStar.toFixed(1),
 						}
 					}),
 					autoWidth: true,

+ 2 - 0
TEAMModelOS/ClientApp/src/view/learnactivity/CreatePrivEva.vue

@@ -596,6 +596,8 @@ export default {
 
             }
             this.activeTab = 'preview'
+            this.endTime = new Date(new Date(new Date().toLocaleDateString()).getTime() + 2 * 24 * 60 * 60 * 1000 - 1)
+            this.evaluationInfo.endTime = new Date(new Date().toLocaleDateString()).getTime() + 2 * 24 * 60 * 60 * 1000 - 1
             // data.id = ''
             // data.name = `${data.name}(${this.$t('learnActivity.mgtScEv.copy')})`
             // this.startTime = new Date(data.startTime)

+ 8 - 7
TEAMModelOS/ClientApp/src/view/learnactivity/CreateSchoolEva.vue

@@ -156,7 +156,7 @@ export default {
     data() {
         let _this = this
         return {
-            defaultTarget:[],
+            defaultTarget: [],
             showBack: false,
             curPdInfo: {},
             dateOpt: {
@@ -247,12 +247,6 @@ export default {
             }
         }
     },
-    mounted() {
-        this.$EventBus.$off('createSheetId')
-        this.$EventBus.$on('createSheetId', sheetId => {
-            this.getSheetId(sheetId)
-        })
-    },
     methods: {
         toProduct() {
             this.$router.push({
@@ -820,15 +814,22 @@ export default {
                 let paperInfo = await this.$api.learnActivity.FindExamPaper(pp)
                 for (let i = 0; i < paperInfo.papers.length; i++) {
                     let fullPaper = await this.$evTools.getFullPaper(paperInfo.papers[i])
+                    this.curSubIndex = i
                     this.comfirmSelectPaper(paperInfo.papers[i], fullPaper)
                 }
             } catch (e) {
             }
             console.log(this.evaluationInfo)
+            this.endTime = new Date(new Date(new Date().toLocaleDateString()).getTime() + 2 * 24 * 60 * 60 * 1000 - 1)
+            this.evaluationInfo.endTime = new Date(new Date().toLocaleDateString()).getTime() + 2 * 24 * 60 * 60 * 1000 - 1
             this.activeTab = 'preview'
         }
     },
     mounted() {
+        this.$EventBus.$off('createSheetId')
+        this.$EventBus.$on('createSheetId', sheetId => {
+            this.getSheetId(sheetId)
+        })
         // 处理默认时间
         this.startTime = new Date()
         this.evaluationInfo.startTime = new Date().getTime()

+ 16 - 7
TEAMModelOS/ClientApp/src/view/learnactivity/ExamMgt.vue

@@ -39,7 +39,7 @@
                         <Input v-special-char icon="ios-close" v-model="filter.name" :placeholder="$t('schoolBaseInfo.codeSearchHolder')" autofocus style="width:100%" @on-click="closeKeySearch" @on-change="$jsFn.debounce(filterEv,1000)()" />
                     </div>
                 </div>
-                <Scroll class="evaluation-list-main" :on-reach-bottom="handleReachBottom">
+                <Scroll class="evaluation-list-main" :on-reach-bottom="handleReachBottom" :distance-to-edge="[-1,-1]">
                     <div v-for="(item,index) in evaListShow" @click.capture="selectEvaluation(index)" :class="['evaluation-item','block-bg',index == curEvaIndex ? 'block-bg-active':'']" :key="index">
                         <p class="evaluation-name">
                             {{item.name}}
@@ -713,12 +713,21 @@ export default {
                     content: this.$t('learnActivity.mgtScEv.copyContent'),
                     onOk: () => {
                         console.log(this.evaListShow[this.curEvaIndex])
-                        this.$router.push({
-                            name: 'createSchoolEva',
-                            params: {
-                                evaluationInfo: this.evaListShow[this.curEvaIndex]
-                            }
-                        })
+                        if (this.scope == 'school') {
+                            this.$router.push({
+                                name: 'createSchoolEva',
+                                params: {
+                                    evaluationInfo: this.evaListShow[this.curEvaIndex]
+                                }
+                            })
+                        } else {
+                            this.$router.push({
+                                name: 'createPrivEva',
+                                params: {
+                                    evaluationInfo: this.evaListShow[this.curEvaIndex]
+                                }
+                            })
+                        }
                     }
                 })
             }

+ 6 - 1
TEAMModelOS/ClientApp/src/view/learnactivity/StuReport.vue

@@ -221,6 +221,10 @@ export default {
         subject: {
             type: String,
             default: ''
+        },
+        classId:{
+            type:String,
+            default:''
         }
     },
     components: {
@@ -331,7 +335,8 @@ export default {
                 id: this.examInfo.id,
                 studentId: this.stuData.id,
                 code: this.examInfo.scope == 'school' ? this.$store.state.userInfo.schoolCode : this.$store.state.userInfo.TEAMModelId,
-                scode: this.examInfo.code
+                scode: this.examInfo.code,
+                cIds: [this.classId]
             }
             this.$api.studentWeb.FindStudentPaper(params).then(
                 res => {

+ 11 - 1
TEAMModelOS/ClientApp/src/view/learnactivity/tabs/AnswerTable.vue

@@ -233,7 +233,7 @@
                     <span class="stu-value">{{viewStuData.name}}</span>
                 </span>
             </div>
-            <StuReport :stuData="viewStuData" :examInfo="examInfo" :subject="chooseSubject"></StuReport>
+            <StuReport :stuData="viewStuData" :classId="chooseClass" :examInfo="examInfo" :subject="chooseSubject"></StuReport>
         </Modal>
     </div>
 </template>
@@ -527,8 +527,18 @@ export default {
     },
     methods: {
         showStuReport(data) {
+            console.log(data)
+            // 学生未作答,不能查看
             if (data.status === 1) {
                 this.$Message.warning(this.$t('learnActivity.score.noSeeRpt'))
+            }
+            //评测进行中,不能查看
+            else if (this.examInfo.progress === 'going') {
+                this.$Message.warning(this.$t('learnActivity.score.noSeeRpt1'))
+            }
+            //学生未被评分,不能查看
+            else if (data.data && data.data.includes(-1)) {
+                this.$Message.warning(this.$t('learnActivity.score.noSeeRpt2'))
             } else {
                 this.viewStuData = data
                 this.stuReportStatus = true

+ 7 - 2
TEAMModelOS/ClientApp/src/view/login/page/Student.vue

@@ -449,7 +449,12 @@
 									id: this.loginForm.id,
 									pass: this.loginForm.pass
 								}).then(res => {
-									result = res
+									if(!res.error){
+										result = res
+									}else{
+										this.$Message.warning(res.message)
+										this.loading = false
+									}
 								}).catch(err => {
 									isFail = true
 								})
@@ -461,7 +466,7 @@
 								}
 								break;
 							case 'studForm':
-								this.$loginTools.stuLoginByAccount({ school_code: this.studForm.schoolCode,id: this.studForm.id,pw: this.studForm.pass })
+								this.$loginTools.stuLoginByAccount({ school_code: this.studForm.schoolCode,id: this.studForm.id,pw: this.studForm.pass }).then().catch(err => {this.loading = false})
 								break;
 						}
 						return false;

+ 10 - 4
TEAMModelOS/ClientApp/src/view/newcourse/MyCourse.vue

@@ -194,7 +194,7 @@
                                     <RcdPoster class="record-poster-wrap" :poster="item.poster"></RcdPoster>
                                     <div style="flex:1">
                                         <p class="record-name" style="padding-left:10px">
-                                            {{item.name}}****{{item.id}}
+                                            {{item.name}}
                                             <span class="item-icon-wrap">
                                                 <Icon type="md-create" class="common-item-icon ed-name" @click.stop="editRecordName(index)" :title="$t('cusMgt.edRdName')" />
                                                 <Icon type="md-trash" class="common-item-icon delete-item" @click.stop="delRecord(item.id)" :title="$t('cusMgt.delRcd')" />
@@ -243,8 +243,14 @@
                                             </span>
                                             <!-- 过期时间 -->
                                             <span class="record-info" v-if="item.expire > -1">
-                                                <span style="color:red">过期时间:</span>
-                                                <span style="color:red" class="record-info-value">{{$jsFn.timeFormat(item.expire)}}</span>
+                                                <!-- <Time :time="item.expire" style="color:red;font-weight:800"/> -->
+                                                <span style="color:red">
+                                                    {{$tools.getRelativeTime(item.expire)}}
+                                                </span>
+                                                <span style="color:red">
+                                                    {{$t('cusMgt.rcdExpired')}}
+                                                </span>
+                                                <!-- <span style="color:red" class="record-info-value">{{$jsFn.timeFormat(item.expire)}}</span> -->
                                             </span>
                                             <!-- 时间 -->
                                             <span class="record-info" style="float:right">
@@ -670,7 +676,7 @@ export default {
                     align: 'center '
                 },
                 {
-                    title: '昵称',
+                    title: this.$t('courseManage.classroom.studentTableC11'),
                     slot: 'nickname',
                     align: 'center ',
                     width: '180'

+ 1 - 1
TEAMModelOS/ClientApp/src/view/newsheet/index.vue

@@ -433,7 +433,7 @@
 			doUploadPDF(blob) {
 				return new Promise(async (r, j) => {
 					let paperInfo = this.$store.state.answerSheet.paperItem
-					let file = new File([blob], `${paperInfo.name}-${this.sheetId}.pdf`, {
+					let file = new File([blob], `${paperInfo.name}-${this.sheetId}(${this.curMode}).pdf`, {
 						type: 'application/pdf'
 					})
 					let curScope = this.examId ? paperInfo.examScope : paperInfo.scope

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

@@ -12,10 +12,10 @@
 								<Icon type="md-add" class="to-create-icon" @click="goToCreate" style="margin-left: 10px"
 									v-if="($access.can('admin.*|schoolAc-upd') || isPrivate) || isAreaSurvey" />
 								<Icon type="ios-copy" class="to-create-icon" @click="onCopyAc()"
-									v-if="qnList.length && currentQn.id" style="margin-left:10px" :title="$t('homework.copyAc')" />		
+									v-if="!isAreaSurvey && qnList.length && currentQn.id" style="margin-left:10px" :title="$t('homework.copyAc')" />		
 								<Icon type="md-trash" class="to-create-icon"
 									@click="isAreaSurvey ? delAreaTrain() : onDeleteQn()"
-									v-if="qnList.length && ($access.can('admin.*|schoolAc-upd') || isPrivate) && !canNotDeleteSurvey(currentQn.owner)" />
+									v-if="qnList.length && ($access.can('admin.*|schoolAc-upd') || isPrivate || isAreaSurvey) && !canNotDeleteSurvey(currentQn.owner)" />
 							</div>
 						</div>
 						<vuescroll>

+ 153 - 116
TEAMModelOS/ClientApp/src/view/research-center/BaseCleanCond.vue

@@ -1,124 +1,161 @@
 <template>
-	<div class="base-clean-container">
-		<p style="font-weight: bold;color: #189df0;font-size: 14px;margin-bottom: 10px;"><Icon type="md-information-circle" /> 自动清除时间设置</p>
-		<div class="clean-item">
-			<span style="font-weight: bold;margin-right: 20px;">课堂记录保留期限</span>
-			<InputNumber :formatter="value => `${parseInt(Math.floor(value*100)/100)}`" :max="30" :min="1" :step="1" v-model="expireDays"></InputNumber>
-			<span style="margin-left: 10px;">天</span>
-		</div>
-		<p style="font-weight: bold;color: #F06431;font-size: 14px;margin-bottom: 10px;"><Icon type="md-information-circle" /> 符合以下条件的课堂记录,将会进行保留,不会被系统自动清除</p>
-		<div class="clean-item" v-for="(item,index) in condArr">
-			<span class="clean-item-name">{{ item.name }}</span>
-			<Select v-model="item.type" style="width:120px;margin: 0 20px;">
-				<Option value=">=">大于等于</Option>
-				<Option value="<=">小于等于</Option>
-			</Select>
-			<InputNumber :formatter="value => `${parseInt(Math.floor(value*100)/100)}`" :max="item.unit === 'rate' ? 100 : 10000" :min="0" :step="1" v-model="item.value"></InputNumber>
-			<span style="margin-left: 10px;">{{ item.unit === 'rate' ? '%' : item.unit === 'point' ? '分' : '个' }}</span>
-		</div>
-	</div>
+  <div class="base-clean-container">
+    <div style="display:flex;align-items:center">
+      <span style="font-weight: bold;font-size: 14px;">
+        {{ $t('lessonRecord.customSetting') }}
+      </span>
+      <i-switch size="large" v-model="openAutoClean" style="margin-left:10px">
+        <span slot="open">{{ $t('lessonRecord.open') }}</span>
+        <span slot="close">{{ $t('lessonRecord.close') }}</span>
+      </i-switch>
+      <Tooltip :content="$t('lessonRecord.condTip')" :max-width="300" theme="light" placement="right" style="margin-left: 10px">
+        <Icon type="md-information-circle" color="#888" size="16"/>
+      </Tooltip>
+    </div>
+    <div v-if="openAutoClean" style="margin-top:20px;">
+      <p style="font-weight: bold;color: #189df0;font-size: 14px;margin-bottom: 10px;">
+        <Icon type="md-information-circle" /> {{ $t('lessonRecord.daySetting') }}
+      </p>
+      <div class="clean-item">
+        <span style="font-weight: bold;margin-right: 20px;">{{ $t('lessonRecord.expire') }}</span>
+        <InputNumber :formatter="val => `${parseInt(Math.floor(val*100)/100)}`" :max="30" :min="1" :step="1" v-model="expireDays"></InputNumber>
+        <span style="margin-left: 10px;">{{ $t('lessonRecord.day') }}</span>
+      </div>
+      <p style="font-weight: bold;color: #F06431;font-size: 14px;margin-bottom: 10px;">
+        <Icon type="md-information-circle" /> {{ $t('lessonRecord.condTip2') }}
+      </p>
+      <div class="clean-item" v-for="(item,index) in condArr">
+        <span class="clean-item-name">{{ item.name }}</span>
+        <Select v-model="item.type" style="width:120px;margin: 0 20px;">
+          <Option value=">=">{{ $t('lessonRecord.over') }}</Option>
+          <Option value="<=">{{ $t('lessonRecord.less') }}</Option>
+        </Select>
+        <InputNumber :formatter="val => `${parseInt(Math.floor(val*100)/100)}`" :max="item.unit === 'rate' ? 100 : 10000" :min="0" :step="1" v-model="item.val"></InputNumber>
+        <span style="margin-left: 10px;">{{ item.unit === 'rate' ? '%' : item.unit === 'point' ? $t('lessonRecord.point') : $t('lessonRecord.unit') }}</span>
+      </div>
+    </div>
+
+  </div>
 </template>
 
 <script>
-	export default {
-		data(){
-			return {
-				expireDays:7,
-				condArr:[
-					{
-						name:this.$t('lessonRecord.attendCount'),
-						key:'attendRate',
-						value:0, 
-						unit:'rate', 
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.groupCount'),
-						key:'groupCount',
-						value:0, 
-						unit:'count',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.totalPoint'),
-						key:'totalPoint',
-						value:0, 
-						unit:'point',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.collateTaskCount'),
-						key:'collateTaskCount',
-						value:0, 
-						unit:'count',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.collateCount'),
-						key:'collateCount',
-						value:0, 
-						unit:'count',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.pushCount'),
-						key:'pushCount',
-						value:0, 
-						unit:'count',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.score'),
-						key:'score',
-						value:0, 
-						unit:'point',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.interactionCount'),
-						key:'interactionCount',
-						value:0, 
-						unit:'count',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.clientInteractionCount'),
-						key:'clientInteractionCount',
-						value:0, 
-						unit:'count',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.examQuizCount'),
-						key:'examQuizCount',
-						value:0, 
-						unit:'count',
-						type:'>='
-					},
-					{
-						name:this.$t('lessonRecord.examPointRate'),
-						key:'examPointRate',
-						value:0, 
-						unit:'rate',
-						type:'>='
-					}
-				]
-			}
-		}
-	}
+export default {
+  data() {
+    return {
+      openAutoClean: false,
+      expireDays: 7,
+      condArr: [
+        {
+          name: this.$t('lessonRecord.attendCount'),
+          key: 'attendRate',
+          val: 0,
+          unit: 'rate',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.groupCount'),
+          key: 'groupCount',
+          val: 0,
+          unit: 'count',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.totalPoint'),
+          key: 'totalPoint',
+          val: 0,
+          unit: 'point',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.collateTaskCount'),
+          key: 'collateTaskCount',
+          val: 0,
+          unit: 'count',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.collateCount'),
+          key: 'collateCount',
+          val: 0,
+          unit: 'count',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.pushCount'),
+          key: 'pushCount',
+          val: 0,
+          unit: 'count',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.score'),
+          key: 'totalInteractPoint',
+          val: 0,
+          unit: 'point',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.interactionCount'),
+          key: 'interactionCount',
+          val: 0,
+          unit: 'count',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.clientInteractionCount'),
+          key: 'clientInteractionCount',
+          val: 0,
+          unit: 'count',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.examQuizCount'),
+          key: 'examQuizCount',
+          val: 0,
+          unit: 'count',
+          type: '>='
+        },
+        {
+          name: this.$t('lessonRecord.examPointRate'),
+          key: 'examPointRate',
+          val: 0,
+          unit: 'rate',
+          type: '>='
+        }
+      ]
+    }
+  },
+  created() {
+    this.$api.lessonRecord.findLessonSettings({
+      schoolId: this.$store.state.userInfo.schoolCode
+    }).then(res => {
+      if (!res.error) {
+        this.openAutoClean = res.setting.lessonSetting.openAutoClean === 1
+        this.expireDays = res.setting.lessonSetting.expireDays
+        this.condArr.map(item => {
+          let findItem = res.setting.lessonSetting.conds.find(i => i.key === item.key)
+          if (findItem) {
+            item.val = findItem.val
+            item.type = findItem.type
+          }
+        })
+      }
+    })
+  }
+}
 </script>
 
 <style lang="less">
-	.base-clean-container{
-		.clean-item{
-			margin: 15px 0;
-			display: flex;
-			align-items: center;
-			&-name{
-				display: inline-block;
-				font-weight: bold;
-				width: 80px;
-			}
-		}
-	}
+.base-clean-container {
+  .clean-item {
+    margin: 15px 0;
+    display: flex;
+    align-items: center;
+    &-name {
+      display: inline-block;
+      font-weight: bold;
+      width: 80px;
+    }
+  }
+}
 </style>

+ 73 - 43
TEAMModelOS/ClientApp/src/view/research-center/ResearchMgt.less

@@ -1,37 +1,41 @@
-.research-mgmt-container{
+.research-mgmt-container {
 	position: relative;
 	// padding: 5px 25px;
 	background-color: #f6f7f7;
-	
-	
 
-	.filter-box{
+
+
+	.filter-box {
 		padding: 5px 25px 10px 25px;
 		background-color: #ffffff;
 		border-bottom: 1px dashed #ccc;
 		position: relative;
-		
-		.search-result{
+
+		.search-result {
 			position: absolute;
 			right: 20px;
 			bottom: 20px;
 		}
-		.filter-section{
+
+		.filter-section {
 			max-width: 90%;
 			display: flex;
 			justify-content: flex-start;
 			align-items: flex-start;
-			.search-count{
+
+			.search-count {
 				font-size: 18px;
 				font-weight: bold;
 				color: #00b8f0;
 			}
 		}
-		.filter-title{
+
+		.filter-title {
 			font-weight: bold;
 			width: 60px;
 			padding: 10px 0px;
-			&::before{
+
+			&::before {
 				content: '';
 				width: 3px;
 				height: 10px;
@@ -41,86 +45,112 @@
 				border-radius: 5px;
 			}
 		}
-		
-		.filter-items{
+
+		.filter-items {
 			display: inline-block;
 			width: calc(100% - 60px);
 		}
-		
-		.filter-item{
+
+		.filter-item {
 			display: inline-block;
 			margin: 8px 10px;
 			padding: 2px 10px;
 			border-radius: 5px;
 			cursor: pointer;
-			&:hover , &-active{
+
+			&:hover,
+			&-active {
 				background-color: #00b4eb;
 				color: #fff;
 				border-radius: 500px;
 			}
 		}
+
+		.advanced-wrap {
+			padding: 0 15px;
+			.clean-item {
+				margin: 15px 0;
+				display: flex;
+				align-items: center;
+
+				&-name {
+					display: inline-block;
+					font-weight: bold;
+					width: 80px;
+				}
+			}
+		}
+
 		
-		.ad-filter{
+
+		.ad-filter {
 			display: flex;
 			padding: 10px 0;
-			
-			.ad-filter-item{
+
+			.ad-filter-item {
 				margin-right: 20px;
-				.filter-item-title{
+
+				.filter-item-title {
 					margin-right: 10px;
 				}
-				
-				/deep/ .ivu-input-wrapper{
+
+				/deep/ .ivu-input-wrapper {
 					width: 200px;
 				}
-				
-				/deep/ .ivu-date-picker-editor{
+
+				/deep/ .ivu-date-picker-editor {
 					width: 250px;
 				}
-				
-				/deep/ .ivu-select{
+
+				/deep/ .ivu-select {
 					width: 200px;
 				}
-				
-				/deep/ .ivu-btn{
+
+				/deep/ .ivu-btn {
 					margin-right: 10px;
 				}
+
+				/deep/ .ivu-icon-ios-close {
+					color:rgb(85, 85, 85) !important;
+				}
 			}
 		}
 	}
-	
-	.tools-bar{
+
+	.tools-bar {
 		position: absolute;
 		right: 20px;
-		top:20px;
+		top: 20px;
 		display: flex;
-		
-		.tool-item{
+
+		.tool-item {
 			margin-right: 20px;
 			cursor: pointer;
-			
-			.ivu-icon{
+
+			.ivu-icon {
 				margin-right: 5px;
 			}
 		}
 	}
-	
-	.table-box{
+
+	.table-box {
 		overflow: auto;
-		.action-icon{
+
+		.action-icon {
 			margin-right: 10px;
 			font-size: 18px;
 			cursor: pointer;
 		}
-		
-		.info-tags{
+
+		.info-tags {
 			white-space: nowrap;
-			.ivu-tag{
+
+			.ivu-tag {
 				display: inline-block;
 			}
-		} 
-		
-		/deep/ .ivu-page{
+		}
+
+		/deep/ .ivu-page {
 			width: 100%;
 			margin: 20px auto;
 			text-align: center;

File diff suppressed because it is too large
+ 930 - 802
TEAMModelOS/ClientApp/src/view/research-center/ResearchMgt.vue


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

@@ -244,6 +244,7 @@ export default {
         },
         logout(){
 			this.$store.commit('user/resetSchoolProfile')
+            this.$store.commit('user/resetTeachers')
             User.logout()
         }
     }

+ 224 - 223
TEAMModelOS/ClientApp/src/view/settings/Index.vue

@@ -1,26 +1,26 @@
 <template>
-    <div class="settings-container">
-        <div class="settings-header">
-            <span :class="['settings-header-item',activeTab === '1' ?  'active-item' : '']" @click="onTabChange('1')">{{ $t('settings.setting_title1')}}</span>
-            <span :class="['settings-header-item',activeTab === '0' ?  'active-item' : '']" @click="onTabChange('0')">{{ $t('settings.setting_title2')}}</span>
-            <!-- <span :class="['settings-header-item',activeTab === '2' ?  'active-item' : '']" @click="onTabChange('2')">{{ $t('settings.setting_title3')}}</span> -->
-        </div>
+  <div class="settings-container">
+    <div class="settings-header">
+      <span :class="['settings-header-item',activeTab === '1' ?  'active-item' : '']" @click="onTabChange('1')">{{ $t('settings.setting_title1')}}</span>
+      <span :class="['settings-header-item',activeTab === '0' ?  'active-item' : '']" @click="onTabChange('0')">{{ $t('settings.setting_title2')}}</span>
+      <!-- <span :class="['settings-header-item',activeTab === '2' ?  'active-item' : '']" @click="onTabChange('2')">{{ $t('settings.setting_title3')}}</span> -->
+    </div>
 
-        <div class="settings-body">
-            <div class="normal-settings animated fadeIn" v-if="activeTab === '0'">
-                <div class="normal-settings-item">
-                    <span class="item-title">{{ $t('settings.langSetting')}}</span>
-                    <span class="item-description">{{ $t('settings.langTips')}}</span>
-                    <span class="item-content light-iview-select">
-                        <Select v-model="cloudSetting.curLang" style="width:200px" @on-change="onSelectLang">
-                            <Option value="zh-cn">中文(简体)</Option>
-                            <Option value="zh-tw">中文(繁體)</Option>
-                            <Option value="en-us">English</Option>
-                        </Select>
-                        <Checkbox v-model="cloudSetting.isSystemLang" @on-change="onMenuStatusChange">{{ $t('settings.langCheck')}}</Checkbox>
-                    </span>
-                </div>
-                <!-- <div class="normal-settings-item">
+    <div class="settings-body">
+      <div class="normal-settings animated fadeIn" v-if="activeTab === '0'">
+        <div class="normal-settings-item">
+          <span class="item-title">{{ $t('settings.langSetting')}}</span>
+          <span class="item-description">{{ $t('settings.langTips')}}</span>
+          <span class="item-content light-iview-select">
+            <Select v-model="cloudSetting.curLang" style="width:200px" @on-change="onSelectLang">
+              <Option value="zh-cn">中文(简体)</Option>
+              <Option value="zh-tw">中文(繁體)</Option>
+              <Option value="en-us">English</Option>
+            </Select>
+            <Checkbox v-model="cloudSetting.isSystemLang" @on-change="onMenuStatusChange">{{ $t('settings.langCheck')}}</Checkbox>
+          </span>
+        </div>
+        <!-- <div class="normal-settings-item">
                     <span class="item-title">{{ $t('settings.themeSetting')}}</span>
                     <span class="item-description">{{ $t('settings.themeTips')}}</span>
                     <span class="item-content">
@@ -28,36 +28,36 @@
                         <span :class="['color-item',cloudSetting.curTheme === 'light' ?  'color-item-active' : '']" @click="onTips('light')"></span>
                     </span>
                 </div> -->
-                <div class="normal-settings-item">
-                    <span class="item-title">{{ $t('settings.menuSetting')}}</span>
-                    <span class="item-description">{{ $t('settings.menuTips')}}</span>
-                    <span class="item-content">
-                        <RadioGroup v-model="cloudSetting.menuStatus" @on-change="onMenuStatusChange">
-                            <Radio label="open">{{ $t('settings.menuOpen')}}</Radio>
-                            <Radio label="close">{{ $t('settings.menuClose')}}</Radio>
-                        </RadioGroup>
-                    </span>
-                </div>
-                <div class="normal-settings-item">
-                    <span class="item-title">{{ $t('settings.logoSetting')}}</span>
-                    <span class="item-description">{{ $t('settings.logoTips')}}</span>
-                    <span class="item-content">
-                        <RadioGroup v-model="cloudSetting.logoStatus" @on-change="onLogoStatusChange">
-                            <Radio label="open">{{ $t('settings.logoOpen')}}</Radio>
-                            <Radio label="close">{{ $t('settings.logoHide')}}</Radio>
-                        </RadioGroup>
-                    </span>
-                </div>
-                <div class="normal-settings-item">
-                    <!-- <Button @click="saveSetting">保存变更</Button> -->
-                </div>
-            </div>
-            <SchoolMgmt v-if="activeTab === '1'" class="animated fadeIn"></SchoolMgmt>
-            <!-- <OpenMgmt v-if="activeTab === '2'" class="animated fadeIn"></OpenMgmt> -->
-            <!-- 换成独立菜单了 -->
-            <!-- <OpenMgmt2 v-if="activeTab === '2'" class="animated fadeIn"></OpenMgmt2> -->
+        <div class="normal-settings-item">
+          <span class="item-title">{{ $t('settings.menuSetting')}}</span>
+          <span class="item-description">{{ $t('settings.menuTips')}}</span>
+          <span class="item-content">
+            <RadioGroup v-model="cloudSetting.menuStatus" @on-change="onMenuStatusChange">
+              <Radio label="open">{{ $t('settings.menuOpen')}}</Radio>
+              <Radio label="close">{{ $t('settings.menuClose')}}</Radio>
+            </RadioGroup>
+          </span>
+        </div>
+        <div class="normal-settings-item">
+          <span class="item-title">{{ $t('settings.logoSetting')}}</span>
+          <span class="item-description">{{ $t('settings.logoTips')}}</span>
+          <span class="item-content">
+            <RadioGroup v-model="cloudSetting.logoStatus" @on-change="onLogoStatusChange">
+              <Radio label="open">{{ $t('settings.logoOpen')}}</Radio>
+              <Radio label="close">{{ $t('settings.logoHide')}}</Radio>
+            </RadioGroup>
+          </span>
         </div>
+        <div class="normal-settings-item">
+          <!-- <Button @click="saveSetting">保存变更</Button> -->
+        </div>
+      </div>
+      <SchoolMgmt v-if="activeTab === '1'" class="animated fadeIn"></SchoolMgmt>
+      <!-- <OpenMgmt v-if="activeTab === '2'" class="animated fadeIn"></OpenMgmt> -->
+      <!-- 换成独立菜单了 -->
+      <!-- <OpenMgmt2 v-if="activeTab === '2'" class="animated fadeIn"></OpenMgmt2> -->
     </div>
+  </div>
 </template>
 
 <script>
@@ -66,121 +66,122 @@ import SchoolMgmt from './SchoolMgmt.vue'
 import OpenMgmt from './OpenMgmt.vue'
 // import OpenMgmt2 from './OpenMgmt2.vue';
 export default {
-    components: {
-        SchoolMgmt,
-        OpenMgmt,
-        // OpenMgmt2
-    },
-    data() {
-        return {
-            activeTab: '1',
-            activeTheme: '0',
-            menuStatus: 'open',
-            curLang: 'zh-cn',
-            isHomeworkLang: true,
-            cloudSetting: {
-                curLang: localStorage.getItem('local'),
-                curTheme: 'dark',
-                isSystemLang: true,
-                menuStatus: 'open',
-                logoStatus: 'open'
-            }
-        }
-    },
-    created() {
-        if (localStorage.getItem('cloudSetting')) {
-            this.cloudSetting = JSON.parse(localStorage.getItem('cloudSetting'))
-        }
-		
-    },
-    methods: {
-
-        onSelectLang(val) {
-            localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
-            this.$store.commit("setLanguage", val);
-        },
+  components: {
+    SchoolMgmt,
+    OpenMgmt,
+    // OpenMgmt2
+  },
+  data() {
+    return {
+      activeTab: '1',
+      activeTheme: '0',
+      menuStatus: 'open',
+      curLang: 'zh-cn',
+      isHomeworkLang: true,
+      cloudSetting: {
+        curLang: localStorage.getItem('local'),
+        curTheme: 'dark',
+        isSystemLang: true,
+        menuStatus: 'open',
+        logoStatus: 'open'
+      }
+    }
+  },
+  created() {
+    if (localStorage.getItem('cloudSetting')) {
+      this.cloudSetting = JSON.parse(localStorage.getItem('cloudSetting'))
+    }
 
-        onMenuStatusChange(val) {
-            localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
-        },
+  },
+  methods: {
 
-        onLogoStatusChange(val) {
-            localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
-            this.$EventBus.$emit('onLogoStatusChange', val)
-        },
+    onSelectLang(val) {
+      localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
+      this.$tools.changeLang(val)
+      this.$store.commit("setLanguage", val);
+    },
 
-        /* 一般设置与学校管理切换 */
-        onTabChange(index) {
-            this.activeTab = index
-        },
+    onMenuStatusChange(val) {
+      localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
+    },
 
-        /* 主题切换 */
-        onThemeChange(index) {
-            this.cloudSetting.curTheme = index
-            //this.$less.modifyVars({  // 调用 `less.modifyVars` 方法来改变变量值'
-            //    '@border-color': '#000',
-            //    '@header-bg': 'white',
-            //})
-            //    .then(() => {
-            //        console.log('修改成功');
-            //    });
-            localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
-            if (index == 'dark') {
-                let url = '/theme/dark-theme.css'
-                var link = document.getElementById('theme') ? document.getElementById('theme') : document.createElement('link');
-                if (document.getElementById('theme')) {
-                    link.href = url;
-                } else {
-                    var head = document.getElementsByTagName('head')[0];
-                    link.type = 'text/css';
-                    link.rel = 'stylesheet';
-                    link.id = 'theme';
-                    link.href = url;
-                    head.appendChild(link);
-                }
-            } else {
-                let url = '/theme/light-theme.css'
-                var link = document.getElementById('theme') ? document.getElementById('theme') : document.createElement('link');
-                if (document.getElementById('theme')) {
-                    link.href = url;
-                } else {
-                    var head = document.getElementsByTagName('head')[0];
-                    link.type = 'text/css';
-                    link.rel = 'stylesheet';
-                    link.id = 'theme';
-                    link.href = url;
-                    head.appendChild(link);
-                }
-            }
-        },
+    onLogoStatusChange(val) {
+      localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
+      this.$EventBus.$emit('onLogoStatusChange', val)
+    },
 
-        onTips() {
-            this.$Message.warning('换肤功能即将上线')
-        },
+    /* 一般设置与学校管理切换 */
+    onTabChange(index) {
+      this.activeTab = index
+    },
 
-        /* 保存设置 */
-        saveSetting() {
-            this.$Modal.confirm({
-                title: '提示',
-                content: '设置保存成功,刷新后即可生效,是否前往?',
-                onOk: () => {
-                    localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
-                    location.reload()
-                }
-            })
+    /* 主题切换 */
+    onThemeChange(index) {
+      this.cloudSetting.curTheme = index
+      //this.$less.modifyVars({  // 调用 `less.modifyVars` 方法来改变变量值'
+      //    '@border-color': '#000',
+      //    '@header-bg': 'white',
+      //})
+      //    .then(() => {
+      //        console.log('修改成功');
+      //    });
+      localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
+      if (index == 'dark') {
+        let url = '/theme/dark-theme.css'
+        var link = document.getElementById('theme') ? document.getElementById('theme') : document.createElement('link');
+        if (document.getElementById('theme')) {
+          link.href = url;
+        } else {
+          var head = document.getElementsByTagName('head')[0];
+          link.type = 'text/css';
+          link.rel = 'stylesheet';
+          link.id = 'theme';
+          link.href = url;
+          head.appendChild(link);
         }
-    },
-    watch: {
-        $route: {
-            handler: function (val, oldVal) {
-                let routeData = this.$route.query
-                this.activeTab = routeData.tab || this.activeTab
-            },
-            // 深度观察监听
-            deep: true,
-            immediate: true
+      } else {
+        let url = '/theme/light-theme.css'
+        var link = document.getElementById('theme') ? document.getElementById('theme') : document.createElement('link');
+        if (document.getElementById('theme')) {
+          link.href = url;
+        } else {
+          var head = document.getElementsByTagName('head')[0];
+          link.type = 'text/css';
+          link.rel = 'stylesheet';
+          link.id = 'theme';
+          link.href = url;
+          head.appendChild(link);
         }
+      }
     },
+
+    onTips() {
+      this.$Message.warning('换肤功能即将上线')
+    },
+
+    /* 保存设置 */
+    saveSetting() {
+      this.$Modal.confirm({
+        title: '提示',
+        content: '设置保存成功,刷新后即可生效,是否前往?',
+        onOk: () => {
+          localStorage.setItem('cloudSetting', JSON.stringify(this.cloudSetting))
+          location.reload()
+        }
+      })
+    }
+  },
+  watch: {
+    $route: {
+      handler: function (val, oldVal) {
+        let routeData = this.$route.query
+        this.activeTab = routeData.tab || this.activeTab
+      },
+      // 深度观察监听
+      deep: true,
+      immediate: true
+    }
+  },
 }
 </script>
 
@@ -190,85 +191,85 @@ export default {
 
 <style lang="less">
 .settings-container {
-    .ivu-select-selection {
-        /* background: transparent;
+  .ivu-select-selection {
+    /* background: transparent;
         border-color: #363738;
         color: #a6a6a6; */
-    }
+  }
 
-    .ivu-checkbox-wrapper {
-        margin-left: 40px;
-        margin-top: 5px;
-        // color: #a6a6a6;
-    }
+  .ivu-checkbox-wrapper {
+    margin-left: 40px;
+    margin-top: 5px;
+    // color: #a6a6a6;
+  }
 
-    .ivu-checkbox {
-        margin-right: 5px;
-    }
+  .ivu-checkbox {
+    margin-right: 5px;
+  }
 
-    .ivu-radio-wrapper {
-        margin-right: 20px;
-        // color: #a5a5a5;
-    }
+  .ivu-radio-wrapper {
+    margin-right: 20px;
+    // color: #a5a5a5;
+  }
 
-    .ivu-radio-inner {
-        width: 18px;
-        height: 18px;
-        border-radius: 4px;
-        /* background-color: #0f0f0f;
+  .ivu-radio-inner {
+    width: 18px;
+    height: 18px;
+    border-radius: 4px;
+    /* background-color: #0f0f0f;
         border-color: #4d4d4d; */
-        border-width: 1px;
-        margin-right: 5px;
-        &::after {
-            content: "";
-            display: block;
-            width: 10px;
-            height: 16px;
-            border-right: #ffffff solid 3px;
-            border-bottom: #ffffff solid 3px;
-            transform: rotate(35deg);
-            position: absolute;
-            top: -4px;
-            left: 4px;
-            border-radius: 0;
-            background-color: transparent;
-            border-color: #0094ff;
-        }
+    border-width: 1px;
+    margin-right: 5px;
+    &::after {
+      content: "";
+      display: block;
+      width: 10px;
+      height: 16px;
+      border-right: #ffffff solid 3px;
+      border-bottom: #ffffff solid 3px;
+      transform: rotate(35deg);
+      position: absolute;
+      top: -4px;
+      left: 4px;
+      border-radius: 0;
+      background-color: transparent;
+      border-color: #0094ff;
     }
+  }
 
-    .ivu-checkbox-inner {
-        width: 18px;
-        height: 18px;
-        border-radius: 4px;
-        /* background-color: #0f0f0f;
+  .ivu-checkbox-inner {
+    width: 18px;
+    height: 18px;
+    border-radius: 4px;
+    /* background-color: #0f0f0f;
         border-color: #4d4d4d; */
-        border-width: 1px;
-        margin-right: 5px;
-    }
+    border-width: 1px;
+    margin-right: 5px;
+  }
 
-    .ivu-checkbox-checked .ivu-checkbox-inner {
-        width: 18px;
-        height: 18px;
-        border-radius: 4px;
-        background-color: #FFF;
-        /* border-color: #4d4d4d; */
-        border-width: 1px;
-        margin-right: 5px;
-        &::after {
-            content: "";
-            display: block;
-            width: 10px;
-            height: 16px;
-            border-right: #ffffff solid 3px;
-            border-bottom: #ffffff solid 3px;
-            transform: rotate(35deg);
-            position: absolute;
-            top: -4px;
-            left: 4px;
-            border-radius: 0;
-            background-color: transparent;
-            border-color: #0094ff;
-        }
+  .ivu-checkbox-checked .ivu-checkbox-inner {
+    width: 18px;
+    height: 18px;
+    border-radius: 4px;
+    background-color: #fff;
+    /* border-color: #4d4d4d; */
+    border-width: 1px;
+    margin-right: 5px;
+    &::after {
+      content: "";
+      display: block;
+      width: 10px;
+      height: 16px;
+      border-right: #ffffff solid 3px;
+      border-bottom: #ffffff solid 3px;
+      transform: rotate(35deg);
+      position: absolute;
+      top: -4px;
+      left: 4px;
+      border-radius: 0;
+      background-color: transparent;
+      border-color: #0094ff;
     }
+  }
 }
 </style>

+ 2 - 1
TEAMModelOS/ClientApp/src/view/student-analysis/total-analysis/EvaluationList/TotalIndex.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="index-container">
     <Loading v-show="isLoadingList"></Loading>
-    <Scroll :on-reach-bottom="handleReachBottom" height="880" :distance-to-edge="[0,0]">
+    <Scroll :on-reach-bottom="handleReachBottom" :height="scrollHeight" :distance-to-edge="[-2,-2]">
       <div class="section" id="analysisList">
         <!-- 评测列表数据部分 -->
         <div class="section exam-list-wrap">
@@ -155,6 +155,7 @@ export default {
   },
   data(vm) {
     return {
+      scrollHeight:document.documentElement.clientHeight * 0.95,
       filterResultCount: 0,
       typeCountArr: [],
       latestYear: null,

+ 1 - 0
TEAMModelOS/ClientApp/src/view/student-web/App.vue

@@ -258,6 +258,7 @@
             },
             onQuit() {
                 this.$store.commit('user/resetSchoolProfile')
+                this.$store.commit('user/resetTeachers')
                 this.$store.commit("ChangeItemName", null)
                 localStorage.removeItem('Item')
                 localStorage.removeItem('subjectNow')

+ 8 - 5
TEAMModelOS/ClientApp/src/view/teachcontent/UploadModal.vue

@@ -8,14 +8,17 @@
                     <Icon size="24" style="font-size: 22px;vertical-align: baseline;margin-right: 5px;" custom="iconfont icon-upload" v-show="uploadedList.length" />
                     {{$t('teachContent.uploadText')}}
                 </p>
-                <p class="upload-text" style="font-size:12px;">
+                <!-- <p class="upload-text" style="font-size:12px;">
                     {{$t('updModal.tips1')}}
-                </p>
-                <p class="upload-text" style="font-size:12px;">
+                </p> -->
+                <!-- pptx转和htex提示:暂时去掉了此功能 -->
+                <!-- <p class="upload-text" style="font-size:12px;">
                     {{$t('updModal.tips3')}}
-                </p>
+                </p> -->
                 <p class="upload-text" :style="{fontSize:'12px',marginBottom: uploadedList.length ? '25px':'50px'}">
-                    {{$t('updModal.tips2')}}
+                    <span v-show="routerScope == 'school'">
+                        {{$t('updModal.tips2')}}
+                    </span>
                 </p>
             </Upload>
             <div class="upload-file-box">

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

@@ -10,7 +10,7 @@
               <span>{{ this.$t('vote.list') }}</span>
               <span>
                 <Icon type="md-add" class="to-create-icon" @click="goToCreate" style="margin-left:10px" v-if="($access.can('admin.*|schoolAc-upd') || isPrivate)  || isAreaVote" />
-                <Icon type="ios-copy" class="to-create-icon" @click="onCopyAc()" v-if="voteList.length && currentVote.id" style="margin-left:10px" :title="$t('homework.copyAc')" />
+                <Icon type="ios-copy" class="to-create-icon" @click="onCopyAc()" v-if="!isAreaVote && voteList.length && currentVote.id" style="margin-left:10px" :title="$t('homework.copyAc')" />
                 <Icon type="md-trash" class="to-create-icon" @click="isAreaVote ? delAreaTrain() : onDeleteVote()" v-if="voteList.length && ($access.can('admin.*|schoolAc-upd') || isPrivate) && !canNotDeleteVote(currentVote.owner)" />
               </span>
             </div>

+ 11 - 7
TEAMModelOS/Controllers/Both/GroupListController.cs

@@ -52,29 +52,33 @@ namespace TEAMModelOS.Controllers
             _notificationService = notificationService;
             _coreAPIHttpService = coreAPIHttpService;
         }
-        //学生获取自己已经加入的名单和课程
+        //学生获取自己已经加入的名单。
         [ProducesDefaultResponseType]
-        [HttpPost("get-my-course-grouplist")]
+        [HttpPost("get-student-joined-grouplist")]
         [Authorize(Roles = "IES")]
+        [AuthToken(Roles = "admin,teacher,student")]
         public async Task<IActionResult> GetMyCourseAndGroupList(JsonElement json)
         {
-            var (userid, _name, _picture, _) = HttpContext.GetAuthTokenInfo();
-            if (!json.TryGetProperty("schoolId", out JsonElement _schoolId)) return BadRequest();
+            var (userid, _name, _picture,school) = HttpContext.GetAuthTokenInfo();
             var client = _azureCosmos.GetCosmosClient();
             object scope = null; ;
             HttpContext?.Items.TryGetValue("Scope", out scope);
-
+            int memberType =2;
             if ($"{scope}".Equals(Constant.ScopeStudent))
             {
-                Student student = await client.GetContainer(Constant.TEAMModelOS, "Student").ReadItemAsync<Student>(userid, new PartitionKey($"Base-{_schoolId}"));
+                memberType = 2;
+               // Student student = await client.GetContainer(Constant.TEAMModelOS, "Student").ReadItemAsync<Student>(userid, new PartitionKey($"Base-{school}"));
             }
             if ($"{scope}".Equals(Constant.ScopeTmdUser))
             {
+                memberType = 1;
             }
             if ($"{scope}".Equals(Constant.ScopeTeacher))
             {
+                memberType = 1;
             }
-            return Ok();
+            List<GroupListGrp> groups=  await  GroupListService.GetMemberInGroupList(_coreAPIHttpService, client, _dingDing, userid, memberType, school, new List<string> { "class", "teach" });
+            return Ok(new { groups = groups });
         }
 
 

+ 49 - 14
TEAMModelOS/Controllers/Both/LessonRecordController.cs

@@ -129,7 +129,8 @@ namespace TEAMModelOS.Controllers
                             dict["up-baseinfo"] = new { status= 1 ,code="ok"};
                         }
                         if (item.grant_type.Equals("up-expire")) {
-                            if (_scope.Equals("private")) {
+                            if (_scope.Equals("private"))
+                            {
                                 Teacher teacher = await client.GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<Teacher>($"{_tmdid}", new PartitionKey("Base"));
                                 HashSet<string> ids = new HashSet<string>();
                                 //未定义的 以及过期时间小于等于0 的 课例
@@ -160,10 +161,16 @@ namespace TEAMModelOS.Controllers
                                     await client.GetContainer(Constant.TEAMModelOS, tbname).ReplaceItemAsync<LessonRecord>(lessonRecord, $"{_lessonId}", new PartitionKey(code));
                                     dict["up-expire"] = new { status = 1, code = "ok" };
                                 }
-                                else {
-                                    dict["up-expire"] = new { status = 0, code = "lessonLimit" ,data= limit };
+                                else
+                                {
+                                    dict["up-expire"] = new { status = 0, code = "lessonLimit", data = limit };
                                 }
                             }
+                            else if (_scope.Equals("school")) {
+                                lessonRecord.expire = -1;
+                                await client.GetContainer(Constant.TEAMModelOS, tbname).ReplaceItemAsync<LessonRecord>(lessonRecord, $"{_lessonId}", new PartitionKey(code));
+                                dict["up-expire"] = new { status = 1, code = "ok" };
+                            }
                         }
                     }
                     var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
@@ -329,12 +336,13 @@ namespace TEAMModelOS.Controllers
             }
             else if ($"{_scope}".Equals("private"))
             {
-                if (!request.TryGetProperty("tmdid", out JsonElement _tmdid)) return BadRequest();
+                code = $"LessonRecord";
+                tbname = "Teacher";
+                request.TryGetProperty("tmdid", out JsonElement _tmdid);
                 if (!string.IsNullOrEmpty($"{_tmdid}"))
                 {
-                    code = $"LessonRecord";
+                   
                     sqlPrivate = $" and c.tmdid='{_tmdid}'";
-                    tbname = "Teacher";
                     List<string> ids = new List<string>();
                     ids.Add($"{_tmdid}");
                     if (ids.Any())
@@ -349,7 +357,12 @@ namespace TEAMModelOS.Controllers
                 }
                 else
                 {
-                    return BadRequest();
+                    //如果不传tmdid, 则必须传递,课程id或者名单列表
+                    // 如果不传递tmdid
+                    if (!request.TryGetProperty("groupIds", out JsonElement groupIds))
+                    {
+                        return BadRequest("个人课例不传醍摩豆id则必传 groupIds");
+                    }
                 }
             }
             else
@@ -496,13 +509,12 @@ namespace TEAMModelOS.Controllers
             }
             else if ($"{_scope}".Equals("private"))
             {
-                if (!request.TryGetProperty("tmdid", out JsonElement _tmdid)) return BadRequest();
+                code = $"LessonRecord";
+                tbname = "Teacher";
+                request.TryGetProperty("tmdid", out JsonElement _tmdid);
                 if (!string.IsNullOrEmpty($"{_tmdid}"))
                 {
-                    
                     sqlPrivate = $" and c.tmdid='{_tmdid}'";
-                    code = $"LessonRecord";
-                    tbname = "Teacher";
                     List<string> ids = new List<string>();
                     ids.Add($"{_tmdid}");
                     if (ids.Any())
@@ -516,8 +528,12 @@ namespace TEAMModelOS.Controllers
                     }
                 }
                 else
-                {
-                    return BadRequest();
+                { 
+                    //如果不传tmdid, 则必须传递,课程id或者名单列表
+                    // 如果不传递tmdid
+                    if (!request.TryGetProperty("groupIds", out JsonElement groupIds)) {
+                        return BadRequest("个人课例不传醍摩豆id则必传 groupIds");
+                    }
                 }
             }
             else
@@ -564,7 +580,6 @@ namespace TEAMModelOS.Controllers
                         var messageChange = new ServiceBusMessage(new { delete_id = item.id, tmdid = item.tmdid, scope = item.scope, opt = "delete", school = item.school }.ToJsonString());
                         messageChange.ApplicationProperties.Add("name", "LessonRecordEvent");
                         await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageChange);
-                        return Ok(new { status = 200 });
                     }
                 } catch (Exception ex) {
                     await  _dingDing.SendBotMsg($"{_option.Location},ServiceBus ,LessonRecordEvent 发送消息失败,检查是否配置正常。", GroupNames.成都开发測試群組);
@@ -823,6 +838,10 @@ namespace TEAMModelOS.Controllers
             {
                 dict.Add("courseId", courseId);
             }
+            if (request.TryGetProperty("courseIds", out JsonElement courseIds))
+            {
+                dict.Add("courseId[*]", courseIds);
+            }
             if (request.TryGetProperty("periodId", out JsonElement periodId) && !string.IsNullOrWhiteSpace($"{periodId}"))
             {
                 dict.Add("periodId", periodId);
@@ -897,6 +916,22 @@ namespace TEAMModelOS.Controllers
             {
                 dict.Add("<=.startTime", etime);
             }
+            if (request.TryGetProperty("conds", out JsonElement conds) && conds.ValueKind.Equals(JsonValueKind.Array)) {
+                List<LessonSettingCond> settingConds = conds.Deserialize<List<LessonSettingCond>>();
+                foreach (var item in settingConds)
+                {
+                    dict.TryAdd($"{item.type}.{item.key}", item.val);
+                    //switch (item.type)
+                    //{
+                    //    case ">=":
+                    //        dict.TryAdd($">=.{item.key}",item.val);
+                    //        break;
+                    //    case "<=":
+                    //        dict.TryAdd($"<=.{item.key}", item.val);
+                    //        break;
+                    //}
+                }
+            }
             return dict;
         }
     }

+ 3 - 3
TEAMModelOS/Controllers/Both/PaperController.cs

@@ -98,7 +98,7 @@ namespace TEAMModelOS.Controllers
             var id = jwt.Payload.Sub;
             var client = _azureCosmos.GetCosmosClient();
             List<object> papers = new List<object>();
-            var query = $"select c.id,c.subjectCode,c.code,c.periodCode,c.name,c.itemCount,c.level,c.pointItem,c.pointScore,c.score,c.gradeCode,c.createTime,c.sheet,c.sheetNo,c.tags from c where c.id = {id}";
+            var query = $"select c.id,c.subjectCode,c.code,c.periodCode,c.name,c.itemCount,c.level,c.pointItem,c.pointScore,c.score,c.gradeCode,c.createTime,c.sheet,c.sheetNo,c.tags ,c.mode from c where c.id = {id}";
             await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{school_code}") }))
             {
                 using var json = await JsonDocument.ParseAsync(item.ContentStream);
@@ -157,7 +157,7 @@ namespace TEAMModelOS.Controllers
             
             if (scope.ToString().Equals("school"))
             {
-                sql.Append("select  c.id,c.code,c.name,c.blob,c.periodId,c.gradeIds,c.subjectId,c.subjectName,c.score,c.useCount,c.scope,c.scoring,c.createTime,c.sheet ,c.sheetNo, c.tags from c");
+                sql.Append("select  c.id,c.code,c.name,c.blob,c.periodId,c.gradeIds,c.subjectId,c.subjectName,c.score,c.useCount,c.scope,c.scoring,c.createTime,c.sheet ,c.mode ,c.sheetNo, c.tags from c");
                 AzureCosmosQuery cosmosDbQuery = SQLHelper.GetSQL(dict, sql);
                 await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<Paper>(queryDefinition: cosmosDbQuery.CosmosQueryDefinition, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{code}") }))
                 {
@@ -175,7 +175,7 @@ namespace TEAMModelOS.Controllers
             }
             if (scope.ToString().Equals("private"))
             {
-                sql.Append("select c.id,c.code,c.name,c.blob,c.subjectName,c.score,c.useCount,c.scope,c.scoring ,c.createTime ,c.sheet,c.sheetNo , c.tags from c");
+                sql.Append("select c.id,c.code,c.name,c.blob,c.subjectName,c.score,c.useCount,c.scope,c.scoring ,c.createTime ,c.sheet,c.sheetNo ,c.mode , c.tags from c");
                 AzureCosmosQuery cosmosDbQuery = SQLHelper.GetSQL(dict, sql);
                 await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<Paper>(queryDefinition: cosmosDbQuery.CosmosQueryDefinition, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{code}") }))
                 {

+ 2 - 0
TEAMModelOS/Controllers/Both/SheetConfigController.cs

@@ -198,8 +198,10 @@ namespace TEAMModelOS.Controllers.Common
                         if (string.IsNullOrEmpty(sheet.no)) {
                             sheet.no = await SheetService.genSheetNo(client, _dingDing, _option, sheet.code, tbname, sheet.from);
                             paper.sheetNo = sheet.no;
+                            paper.mode = sheet.mode;
                         }
                         paper.sheetNo = sheet.no;
+                        paper.mode = sheet.mode;
                         paper = await client.GetContainer(Constant.TEAMModelOS, tbname).ReplaceItemAsync<Paper>(paper, $"{_paperId}", new PartitionKey($"{code}"));
 
                     }

+ 15 - 1
TEAMModelOS/Controllers/Client/HiScanController.cs

@@ -30,6 +30,7 @@ using Azure.Storage.Sas;
 using Lib.AspNetCore.ServerSentEvents;
 using TEAMModelOS.SDK.Models.Cosmos.Common;
 using HTEXLib.COMM.Helpers;
+using TEAMModelOS.SDK.Models.Service;
 
 namespace TEAMModelOS.Controllers.Core
 {
@@ -49,8 +50,10 @@ namespace TEAMModelOS.Controllers.Core
         private readonly AzureStorageFactory _azureStorage;
         // private readonly ServerSentEventsService _sse;
         private readonly CoreAPIHttpService _coreAPIHttpService;
+        private readonly IPSearcher _searcher;
+
         public HiScanController(CoreAPIHttpService coreAPIHttpService, AzureCosmosFactory azureCosmos, AzureServiceBusFactory serviceBus, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option,
-           AzureRedisFactory azureRedis, AzureStorageFactory azureStorage//, ServerSentEventsService sse
+           AzureRedisFactory azureRedis, AzureStorageFactory azureStorage, IPSearcher searcher//, ServerSentEventsService sse
                                                                          )
         {
             _azureCosmos = azureCosmos;
@@ -61,6 +64,7 @@ namespace TEAMModelOS.Controllers.Core
             _azureRedis = azureRedis;
             _coreAPIHttpService = coreAPIHttpService;
             _azureStorage = azureStorage;
+            _searcher = searcher;
             //_sse = sse;
         }
 
@@ -78,6 +82,7 @@ namespace TEAMModelOS.Controllers.Core
         public async Task<IActionResult> GetSchoolinfo(JsonElement request)
         {
             List<dynamic> schools = new List<dynamic>();
+            (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
             if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
             var client = _azureCosmos.GetCosmosClient();
             try
@@ -114,6 +119,15 @@ namespace TEAMModelOS.Controllers.Core
                         catch { continue; }
                     }
                 }
+                
+                response.loginInfos = await LoginService.DoLoginInfo(response.loginInfos, response?.defaultSchool, Constant.ScopeTeacher, $"{id}", ip, _azureRedis, _azureStorage, expire: 1);
+                try
+                {
+                    await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(response, $"{id}", new PartitionKey("Base"));
+                }
+                catch
+                {
+                }
                 var (tblob_uri, tblob_sas) = _azureStorage.GetBlobContainerSAS($"{id}", BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete);
                 return Ok(new { schools, teacher = new { name, picture, id, bloburl = tblob_uri, blobsas = tblob_sas } });
             }

+ 15 - 1
TEAMModelOS/Controllers/Client/HiTAControlller.cs

@@ -15,6 +15,7 @@ using System.Linq;
 using System.Text.Json;
 using System.Threading.Tasks;
 using TEAMModelOS.Models;
+using TEAMModelOS.SDK;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models;
@@ -36,13 +37,14 @@ namespace TEAMModelOS.Controllers.Client
         private readonly SnowflakeId _snowflakeId;
         private readonly IConfiguration _configuration;
         private readonly NotificationService _notificationService;
+        private readonly IPSearcher _searcher;
         public HiTAControlller(
             AzureStorageFactory azureStorage,
             AzureRedisFactory azureRedis,
             AzureCosmosFactory azureCosmos,
             DingDing dingDing,
             SnowflakeId snowflakeId,
-            IOptionsSnapshot<Option> option, IConfiguration configuration, NotificationService notificationService)
+            IOptionsSnapshot<Option> option, IConfiguration configuration, NotificationService notificationService, IPSearcher searcher)
         {
             _azureStorage = azureStorage;
             _azureRedis = azureRedis;
@@ -52,6 +54,7 @@ namespace TEAMModelOS.Controllers.Client
             _option = option?.Value;
             _configuration = configuration;
             _notificationService = notificationService;
+            _searcher = searcher;
         }
 
         public class HiTAJoinSchool
@@ -219,6 +222,7 @@ namespace TEAMModelOS.Controllers.Client
             try
             {
                 string id_token = HttpContext.GetXAuth("IdToken");
+                (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
                 if (string.IsNullOrEmpty(id_token)) return BadRequest();
                 var jwt = new JwtSecurityToken(id_token);
                 if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
@@ -462,6 +466,16 @@ namespace TEAMModelOS.Controllers.Client
                     //    }
                     //}
 
+                    Teacher teacher = jsonsc.ToObject<Teacher>();
+                    teacher.loginInfos = await LoginService.DoLoginInfo(teacher.loginInfos, defaultschool, Constant.ScopeTeacher, id, ip, _azureRedis, _azureStorage, expire: 1);
+                    try
+                    {
+                        await clientc.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
+                    }
+                    catch
+                    {
+                    }
+
                     return Ok(new { schools, defaultschool, courses, size, resCount, itemCount, paperCount, activityCount });
                 }
                 else //無此老師

+ 27 - 1
TEAMModelOS/Controllers/Client/HiTeachController.cs

@@ -28,6 +28,7 @@ using TEAMModelOS.SDK;
 using Microsoft.Extensions.Configuration;
 using Azure.Messaging.ServiceBus;
 using TEAMModelOS.SDK.Services;
+using TEAMModelOS.SDK.Models.Service;
 
 namespace TEAMModelOS.Controllers.Client
 {
@@ -48,6 +49,7 @@ namespace TEAMModelOS.Controllers.Client
         private readonly AzureServiceBusFactory _serviceBus;
         private readonly IConfiguration _configuration;
         private readonly CoreAPIHttpService _coreAPIHttpService;
+        private readonly IPSearcher _searcher;
 
         public HiTeachController(
             AzureStorageFactory azureStorage,
@@ -58,7 +60,7 @@ namespace TEAMModelOS.Controllers.Client
             IOptionsSnapshot<Option> option,
             AzureServiceBusFactory serviceBus,
             IConfiguration configuration,
-            CoreAPIHttpService coreAPIHttpService)
+            CoreAPIHttpService coreAPIHttpService, IPSearcher searcher)
         {
             _azureStorage = azureStorage;
             _azureRedis = azureRedis;
@@ -69,6 +71,7 @@ namespace TEAMModelOS.Controllers.Client
             _serviceBus = serviceBus;
             _configuration = configuration;
             _coreAPIHttpService = coreAPIHttpService;
+            _searcher = searcher;
         }
         /// <summary>
         /// 更新课堂记录
@@ -205,6 +208,7 @@ namespace TEAMModelOS.Controllers.Client
             try
             {
                 string id_token = HttpContext.GetXAuth("IdToken");
+                (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
                 if (string.IsNullOrEmpty(id_token)) return BadRequest();
                 var jwt = new JwtSecurityToken(id_token);
                 if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
@@ -243,6 +247,16 @@ namespace TEAMModelOS.Controllers.Client
                     {
                         defaultschool = valueD.ToString();
                     }
+
+                    Teacher teacher = jsonsc.ToObject<Teacher>();
+                    teacher.loginInfos = await LoginService.DoLoginInfo(teacher.loginInfos, defaultschool, Constant.ScopeTeacher, id, ip, _azureRedis, _azureStorage, expire: 1);
+                    try
+                    {
+                        await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
+                    }
+                    catch
+                    {
+                    }
                 }
                 else
                 {
@@ -260,6 +274,7 @@ namespace TEAMModelOS.Controllers.Client
                         defaultSchool = null,
                         schools = new List<Teacher.TeacherSchool>(),
                     };
+                    teacher.loginInfos = await LoginService.DoLoginInfo(teacher.loginInfos, "", Constant.ScopeTeacher, id, ip, _azureRedis, _azureStorage, expire: 1);
                     teacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").CreateItemAsync<Teacher>(teacher, new PartitionKey("Base"));
                 }
 
@@ -426,6 +441,7 @@ namespace TEAMModelOS.Controllers.Client
             try
             {
                 string id_token = HttpContext.GetXAuth("IdToken");
+                (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
                 if (string.IsNullOrEmpty(id_token)) return BadRequest();
                 if (!requert.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
                 var jwt = new JwtSecurityToken(id_token);
@@ -720,6 +736,16 @@ namespace TEAMModelOS.Controllers.Client
                     }
                 }
 
+                try
+                {
+                    Teacher teacher = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<Teacher>($"{id}", new PartitionKey("Base"));
+                    teacher.loginInfos = await LoginService.DoLoginInfo(teacher.loginInfos, school_code.GetString(), Constant.ScopeTeacher, id, ip, _azureRedis, _azureStorage, expire: 1);
+                    await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
+                }
+                catch
+                {
+                }
+
                 //取得School Blob 容器位置及SAS
                 string school_code_blob = school_code.GetString().ToLower();
                 var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Write); //讀列

+ 5 - 5
TEAMModelOS/Controllers/Common/ExamController.cs

@@ -524,8 +524,8 @@ namespace TEAMModelOS.Controllers
 
 
         [ProducesDefaultResponseType]
-        [Authorize(Roles = "IES")]
-        [AuthToken(Roles = "teacher,admin")]
+/*        [Authorize(Roles = "IES")]
+        [AuthToken(Roles = "teacher,admin")]*/
         [HttpPost("find-by-analysis")]
         public async Task<IActionResult> FindByAnalysis(JsonElement requert)
         {
@@ -617,7 +617,7 @@ namespace TEAMModelOS.Controllers
                     int end = yy.GetInt32() + 1;
                     long stime = DateTimeOffset.Parse($"{yy.GetInt32()}-1-1").ToUnixTimeMilliseconds();
                     long etime = DateTimeOffset.Parse($"{end}-1-1").ToUnixTimeMilliseconds();
-                    stringBuilder.Append($" and (c.startTime >= {stime} && c.startTime < {etime})");
+                    stringBuilder.Append($" and c.startTime >= {stime} and c.startTime < {etime} ");
                 }
                 if (requert.TryGetProperty("name", out JsonElement name) && !string.IsNullOrWhiteSpace($"{name}"))
                 {
@@ -689,12 +689,12 @@ namespace TEAMModelOS.Controllers
                         }
                     }                   
                 }
-                var groups = exams.Where(e => !string.IsNullOrEmpty(e.examType.id)).GroupBy(x =>x.examType).Select(y => new { key = y.Key.name,count = y.ToList().Count}).ToList();
+                var groups = exams.Where(e => null != e.examType && !string.IsNullOrWhiteSpace(e.examType.id)).GroupBy(x =>x.examType.name).Select(y => new { key = y.Key,count = y.ToList().Count}).ToList();
                 return Ok(new { examInfo, token = token, year,count = exams.Count, groups });
             }
             catch (Exception e)
             {
-                await _dingDing.SendBotMsg($"OS,{_option.Location},exam/find-by-analysis()\n{e.Message}", GroupNames.醍摩豆服務運維群組);
+                await _dingDing.SendBotMsg($"OS,{_option.Location},exam/find-by-analysis()\n{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
                 return BadRequest();
             }
 

+ 3 - 3
TEAMModelOS/Controllers/Common/HomeworkController.cs

@@ -433,7 +433,7 @@ namespace TEAMModelOS.Controllers.Learn
             public string userSchool { get; set; }
             public long submitTime { get; set; }
             public double score { get; set; } = -1;
-            public double star { get; set; } = -1;
+            public double studentStar { get; set; } = -1;
             public double teacherStar { get; set; } = -1;
             public bool submit { get; set; }
            
@@ -559,13 +559,13 @@ namespace TEAMModelOS.Controllers.Learn
                                 {
                                     record = await client.GetContainer(Constant.TEAMModelOS, tbname).ReadItemAsync<HomeworkRecord>($"{_id}", new PartitionKey(partition));
                                     double star = -1;
-                                    List<double> stars = record.comments.Select(x => x.star).ToList();
+                                    List<double> stars = record.comments.FindAll(s=>s.identity.Equals("student")).Select(x => x.star).ToList();
                                     if (stars.IsNotEmpty())
                                     {
                                         star = stars.Sum() / stars.Count;
                                     }
                                     user.submit = true;
-                                    user.star = star;
+                                    user.studentStar = star;
                                     user.score = record.score;
                                     user.submitTime = record.time;
                                     user.content = record.content;

+ 0 - 0
TEAMModelOS/Controllers/School/SchoolSettingController.cs


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