Ver Fonte

Merge branch 'develop' into Jeff/develop

jeff há 3 anos atrás
pai
commit
738b520277
100 ficheiros alterados com 35825 adições e 1423 exclusões
  1. 1 0
      TEAMModelAPI/Controllers/School/ExamController.cs
  2. 65 47
      TEAMModelAPI/Controllers/School/TeacherController.cs
  3. 2 2
      TEAMModelBI/ClientApp/public/index.html
  4. BIN
      TEAMModelBI/ClientApp/src/assets/img/background-area.png
  5. BIN
      TEAMModelBI/ClientApp/src/assets/img/bg-alsit.png
  6. BIN
      TEAMModelBI/ClientApp/src/assets/img/bg-index.jpg
  7. BIN
      TEAMModelBI/ClientApp/src/assets/img/down.png
  8. BIN
      TEAMModelBI/ClientApp/src/assets/img/header.png
  9. BIN
      TEAMModelBI/ClientApp/src/assets/img/school-background.jpg
  10. BIN
      TEAMModelBI/ClientApp/src/assets/img/sort1.png
  11. BIN
      TEAMModelBI/ClientApp/src/assets/img/sort2.png
  12. BIN
      TEAMModelBI/ClientApp/src/assets/img/sort3.png
  13. BIN
      TEAMModelBI/ClientApp/src/assets/img/up.png
  14. 246 0
      TEAMModelBI/ClientApp/src/components/echarts/barandline.vue
  15. 247 0
      TEAMModelBI/ClientApp/src/components/echarts/commbarandline.vue
  16. 2 1
      TEAMModelBI/ClientApp/src/components/echarts/commonLine.vue
  17. 185 0
      TEAMModelBI/ClientApp/src/components/echarts/commonMeter.vue
  18. 190 0
      TEAMModelBI/ClientApp/src/components/echarts/copyMeter.vue
  19. 214 0
      TEAMModelBI/ClientApp/src/components/echarts/gradePie.vue
  20. 221 0
      TEAMModelBI/ClientApp/src/components/echarts/specialLinePie.vue
  21. 158 73
      TEAMModelBI/ClientApp/src/components/echarts/test.vue
  22. 300 0
      TEAMModelBI/ClientApp/src/components/echarts/test/HiteachBar.vue
  23. 290 0
      TEAMModelBI/ClientApp/src/components/echarts/test/online.vue
  24. 164 0
      TEAMModelBI/ClientApp/src/components/echarts/test/onlineType.vue
  25. 132 0
      TEAMModelBI/ClientApp/src/components/echarts/test/size.vue
  26. 234 0
      TEAMModelBI/ClientApp/src/components/echarts/testLine.vue
  27. 46 1
      TEAMModelBI/ClientApp/src/router/index.js
  28. 6 1
      TEAMModelBI/ClientApp/src/store/index.js
  29. 34 139
      TEAMModelBI/ClientApp/src/view/areaServe/areamanage.vue
  30. 4 6
      TEAMModelBI/ClientApp/src/view/areaServe/index.vue
  31. 215 32
      TEAMModelBI/ClientApp/src/view/areaServe/statistics.vue
  32. 293 0
      TEAMModelBI/ClientApp/src/view/areamanage/areaAnalyse.vue
  33. 37 0
      TEAMModelBI/ClientApp/src/view/areamanage/index.vue
  34. 39 9
      TEAMModelBI/ClientApp/src/view/common/aside.vue
  35. 0 1
      TEAMModelBI/ClientApp/src/view/home.vue
  36. 1181 0
      TEAMModelBI/ClientApp/src/view/index/assitindex.vue
  37. 2267 0
      TEAMModelBI/ClientApp/src/view/index/index.vue
  38. 31 10
      TEAMModelBI/ClientApp/src/view/index/operateLog.vue
  39. 929 0
      TEAMModelBI/ClientApp/src/view/participation/index.vue
  40. 1442 0
      TEAMModelBI/ClientApp/src/view/participation/setAbility.vue
  41. 2170 0
      TEAMModelBI/ClientApp/src/view/participation/setPhase.vue
  42. 1373 0
      TEAMModelBI/ClientApp/src/view/schoolServe/analyseSchool.vue
  43. 103 78
      TEAMModelBI/ClientApp/src/view/schoolServe/index.vue
  44. 130 168
      TEAMModelBI/ClientApp/src/view/schoolServe/school.vue
  45. 105 25
      TEAMModelBI/ClientApp/src/view/schoolServe/setschool.vue
  46. 36 0
      TEAMModelBI/ClientApp/src/view/schoolmanage/index.vue
  47. 990 0
      TEAMModelBI/ClientApp/src/view/schoolmanage/schoolAnalyse.vue
  48. 933 0
      TEAMModelBI/ClientApp/src/view/schoolmanage/schoolManege.vue
  49. 1 1
      TEAMModelBI/ClientApp/src/view/systemConfig/operate.vue
  50. 10 1
      TEAMModelBI/ClientApp/src/view/systemConfig/setAdmin.vue
  51. 56 25
      TEAMModelBI/ClientApp/src/view/teachermanage/manage.vue
  52. 10 7
      TEAMModelBI/ClientApp/src/view/teachermanage/traitmanage.vue
  53. 321 0
      TEAMModelBI/Controllers/BIHome/OnLineController.cs
  54. 86 85
      TEAMModelBI/Controllers/BISchool/BatchSchoolController.cs
  55. 5 5
      TEAMModelBI/Controllers/BISchool/SchoolController.cs
  56. 67 0
      TEAMModelBI/Controllers/BITest/Ies5TestController.cs
  57. 171 41
      TEAMModelBI/Controllers/BITest/TestController.cs
  58. 113 0
      TEAMModelBI/Controllers/Census/SchoolController.cs
  59. 7 6
      TEAMModelBI/Models/AssistSchool.cs
  60. 41 35
      TEAMModelBI/Tool/CommonFind.cs
  61. 82 0
      TEAMModelBI/Tool/CosmosBank/JointlySingleQuery.cs
  62. 35 0
      TEAMModelBI/Tool/CosmosBank/LessonStatisWay.cs
  63. 20 14
      TEAMModelBI/Tool/TimeHelper.cs
  64. 6 5
      TEAMModelOS.FunctionV4/CosmosDB/TriggerExam.cs
  65. 101 60
      TEAMModelOS.FunctionV4/ServiceBus/ActiveTaskTopic.cs
  66. 3 3
      TEAMModelOS.FunctionV4/TEAMModelOS.FunctionV4.csproj
  67. 1 0
      TEAMModelOS.SDK/Context/Constant/Constant.cs
  68. 21 3
      TEAMModelOS.SDK/Extension/HttpContextExtensions.cs
  69. 5 4
      TEAMModelOS.SDK/Extension/JwtAuthExtension.cs
  70. 7 0
      TEAMModelOS.SDK/Models/Cosmos/Common/LessonRecord.cs
  71. 1 0
      TEAMModelOS.SDK/Models/Cosmos/School/Paper.cs
  72. 10 5
      TEAMModelOS.SDK/Models/Cosmos/School/School.cs
  73. 19 2
      TEAMModelOS.SDK/Models/Cosmos/School/SchoolSetting.cs
  74. 14 4
      TEAMModelOS.SDK/Models/Cosmos/Student/Student.cs
  75. 9 0
      TEAMModelOS.SDK/Models/Cosmos/Student/TmdUser.cs
  76. 26 2
      TEAMModelOS.SDK/Models/Cosmos/Teacher/Teacher.cs
  77. 1 1
      TEAMModelOS.SDK/Models/Service/ActivityService.cs
  78. 23 260
      TEAMModelOS.SDK/Models/Service/Common/TeacherService.cs
  79. 352 0
      TEAMModelOS.SDK/Models/Service/LessonService.cs
  80. 283 0
      TEAMModelOS.SDK/Models/Service/LoginService.cs
  81. 19 13
      TEAMModelOS.SDK/Models/Service/StudentService.cs
  82. 38 3
      TEAMModelOS.SDK/Models/Service/Third/Xkw/XkwOAuthModel.cs
  83. 22 15
      TEAMModelOS.SDK/Models/Service/TmdUserService.cs
  84. 139 0
      TEAMModelOS.SDK/Models/Table/IESLogin.cs
  85. 70 56
      TEAMModelOS/ClientApp/public/index.html
  86. 6133 0
      TEAMModelOS/ClientApp/public/lang/en-US.js
  87. 6134 0
      TEAMModelOS/ClientApp/public/lang/zh-CN.js
  88. 6136 0
      TEAMModelOS/ClientApp/public/lang/zh-TW.js
  89. 1 1
      TEAMModelOS/ClientApp/src/api/lessonRecord.js
  90. 8 0
      TEAMModelOS/ClientApp/src/api/studentWeb.js
  91. BIN
      TEAMModelOS/ClientApp/src/assets/image/import-teacher-tw.png
  92. 38 0
      TEAMModelOS/ClientApp/src/assets/student-web/component_styles/paper-test.css
  93. 73 64
      TEAMModelOS/ClientApp/src/common/BaseLayout.vue
  94. 21 29
      TEAMModelOS/ClientApp/src/common/BaseNotification.vue
  95. 16 3
      TEAMModelOS/ClientApp/src/components/homework/BaseHwTable.vue
  96. 0 1
      TEAMModelOS/ClientApp/src/components/mark/MarkTools.vue
  97. 7 3
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReportCharts/QuCount.vue
  98. 46 72
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperTest.vue
  99. 2 1
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperView.vue
  100. 0 0
      TEAMModelOS/ClientApp/src/components/student-web/achievement/MyAchievement.less

+ 1 - 0
TEAMModelAPI/Controllers/School/ExamController.cs

@@ -202,6 +202,7 @@ namespace TEAMModelAPI.Controllers
                             builder.Append("ans.json");
                             result.studentAnswers[newIndex].Clear();
                             result.studentAnswers[newIndex].Add(builder.ToString());
+                            result.status[newIndex] = 0;
                             for (int i = 0; i < ans.Count; i++)
                             {
                                 if (ans[i] == null)

+ 65 - 47
TEAMModelAPI/Controllers/School/TeacherController.cs

@@ -64,13 +64,13 @@ namespace TEAMModelAPI.Controllers
         [ApiToken(Auth = "1501", Name = "学校教师列表", RW = "R", Limit = false)]
         public async Task<IActionResult> GetTeacherList(JsonElement json)
         {
-            
+
             json.TryGetProperty("searchKey", out JsonElement _searchKey);
             List<CoreUser> coreUsers = new List<CoreUser>();
-            IEnumerable<string> unexist = null ;
+            IEnumerable<string> unexist = null;
             if (_searchKey.ValueKind.Equals(JsonValueKind.Array))
             {
-                List<string> searchKey= _searchKey.ToObject<List<string>>();
+                List<string> searchKey = _searchKey.ToObject<List<string>>();
                 var keys = searchKey.Where(x => !string.IsNullOrWhiteSpace(x));
                 var content = new StringContent(keys.ToJsonString(), Encoding.UTF8, "application/json");
                 string ujson = await _coreAPIHttpService.GetUserInfos(content);
@@ -82,17 +82,19 @@ namespace TEAMModelAPI.Controllers
                 {
                     unexist = searchKey.Except(coreUsers.Select(x => x.searchKey));
                 }
-                else {
+                else
+                {
                     return Ok(new { error = 1, msg = "没有找到对应的教师信息!" });
                 }
             }
-           
-         
+
+
             var (id, school) = HttpContext.GetApiTokenInfo();
             List<SchoolTeacher> teachers = new List<SchoolTeacher>();
             string insql = "";
-            if (coreUsers.Any()) {
-                 insql = $"   c.id in  ({string.Join(",", coreUsers.Select(x => $"'{ x.id}'"))}) ";
+            if (coreUsers.Any())
+            {
+                insql = $"   c.id in  ({string.Join(",", coreUsers.Select(x => $"'{ x.id}'"))}) ";
             }
             string sql = $"select c.id,c.name ,c.picture,c.job ,c.subjectIds,c.roles from c where   {insql}";
             await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<SchoolTeacher>
@@ -100,21 +102,22 @@ namespace TEAMModelAPI.Controllers
             {
                 teachers.Add(item);
             }
-            var teacherIds=   coreUsers.Select(x => x.id).Except(teachers.Select(x => x.id));
+            var teacherIds = coreUsers.Select(x => x.id).Except(teachers.Select(x => x.id));
             List<CoreUser> unjoined = coreUsers.FindAll(x => teacherIds.Contains(x.id));
-            List<dynamic> tchs= new List<dynamic>(); 
+            List<dynamic> tchs = new List<dynamic>();
             teachers.Select(x => new { x.id, x.name, x.picture, x.job, x.subjectIds, x.roles }).ToList().ForEach(x => {
-                var  coreUser= coreUsers.Find(c=>c.id.Equals(x.id));
-                if (coreUser != null) {
-                    tchs.Add(new {x.id,coreUser.name, coreUser.picture,x.job,x.subjectIds,x.roles,coreUser.searchKey,school });
+                var coreUser = coreUsers.Find(c => c.id.Equals(x.id));
+                if (coreUser != null)
+                {
+                    tchs.Add(new { x.id, coreUser.name, coreUser.picture, x.job, x.subjectIds, x.roles, coreUser.searchKey, school });
                 }
             });
             return Ok(new
             {
                 teachers = tchs,
-                unjoined= unjoined.Select(x => new {x.id,x.name,x.picture,x.searchKey}),
-                unexist= unexist
-            }); 
+                unjoined = unjoined.Select(x => new { x.id, x.name, x.picture, x.searchKey }),
+                unexist = unexist
+            });
         }
         /// <summary>
         /// 获取学校教师信息
@@ -135,6 +138,7 @@ namespace TEAMModelAPI.Controllers
             if (responseTch.Status == 200)
             {
                 teacher = JsonDocument.Parse(responseTch.Content).RootElement.Deserialize<Teacher>();
+
             }
             else
             {
@@ -150,7 +154,7 @@ namespace TEAMModelAPI.Controllers
                 return Ok(new { error = 1, msg = "教师未就职该学校!" });
             }
         }
-        
+
 
 
 
@@ -161,17 +165,19 @@ namespace TEAMModelAPI.Controllers
         [ProducesDefaultResponseType]
         [HttpPost("import-school-teacher")]
         [ApiToken(Auth = "1503", Name = "学校教师信息", RW = "R", Limit = false)]
-        public async Task<IActionResult> ImportSchoolTeacher(ImportTechDto json) {
+        public async Task<IActionResult> ImportSchoolTeacher(ImportTechDto json)
+        {
             //如果需要同时导入学科,则需要填写学段
-           
+
             var (id, school) = HttpContext.GetApiTokenInfo();
             List<ImportTech> teachers = json.teachers;
-            List<string> searchKey = teachers.Select(x=>x.id).ToList();
+            List<string> searchKey = teachers.Select(x => x.id).ToList();
             string ujson = null;
             var keys = searchKey.Where(x => !string.IsNullOrWhiteSpace(x));
-            if (keys.Any()) {
+            if (keys.Any())
+            {
                 var content = new StringContent(keys.ToJsonString(), Encoding.UTF8, "application/json");
-             ujson = await _coreAPIHttpService.GetUserInfos(content);
+                ujson = await _coreAPIHttpService.GetUserInfos(content);
             }
             List<CoreUser> coreUsers = new List<CoreUser>();
             if (!string.IsNullOrWhiteSpace(ujson))
@@ -190,12 +196,13 @@ namespace TEAMModelAPI.Controllers
             var exist = coreUsers.Select(x => x.id);
             //注册了账号的教师
             teachers = teachers.Where(x => exist.Contains(x.id)).ToList();
-           
+
             List<Teacher> teachersList = new List<Teacher>();
-            List<SchoolTeacher>  schoolTeachers = new List<SchoolTeacher>();
-            string sql = $"select value(c) from c where c.id in ({string.Join(",",teachers.Select(x=>$"'{x.id}'"))})";
+            List<SchoolTeacher> schoolTeachers = new List<SchoolTeacher>();
+            string sql = $"select value(c) from c where c.id in ({string.Join(",", teachers.Select(x => $"'{x.id}'"))})";
             await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher)
-                .GetItemQueryIterator<Teacher>(queryText: sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey("Base") })) {
+                .GetItemQueryIterator<Teacher>(queryText: sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey("Base") }))
+            {
                 teachersList.Add(item);
             }
             await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School)
@@ -208,36 +215,41 @@ namespace TEAMModelAPI.Controllers
             //学校学科发生变化。
             List<SchoolTeacher> schoolTeachersAdd = new List<SchoolTeacher>();
             bool baseChange = false;
-            foreach (var item in teachers) {
+            foreach (var item in teachers)
+            {
                 var teacher = teachersList.Find(x => x.id.Equals(item.id));
                 var coreUser = coreUsers.Find(x => x.id.Equals(item.id));
                 if (teacher != null)
                 {
-                    var sch= teacher.schools?.Find(x => x.schoolId.Equals(school));
-                    if (sch == null) {
+                    var sch = teacher.schools?.Find(x => x.schoolId.Equals(school));
+                    if (sch == null)
+                    {
                         if (teacher.schools.IsNotEmpty())
                         {
-                            teacher.schools.Add(new Teacher.TeacherSchool {schoolId= school,name=data.name,status= "invite", time= now ,picture=data.picture,areaId=data.areaId});
+                            teacher.schools.Add(new Teacher.TeacherSchool { schoolId = school, name = data.name, status = "invite", time = now, picture = data.picture, areaId = data.areaId });
                         }
-                        else {
+                        else
+                        {
                             teacher.defaultSchool = school;
-                            teacher.size = teacher.size + 1 ;
+                            teacher.size = teacher.size + 1;
                             teacher.schools = new List<Teacher.TeacherSchool> { new Teacher.TeacherSchool { schoolId = school, name = data.name, status = "invite", time = now, picture = data.picture, areaId = data.areaId } };
                         }
                     }
-                    await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReplaceItemAsync<Teacher>(teacher,teacher.id, new PartitionKey("Base"));
+                    await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReplaceItemAsync<Teacher>(teacher, teacher.id, new PartitionKey("Base"));
                 }
-                else {
-                
+                else
+                {
+
                     teacher = new Teacher
                     {
+                        createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                         id = coreUser.id,
                         name = coreUser.name,
                         picture = coreUser.picture,
-                        defaultSchool=school,
-                        size=2,
-                        code="Base",
-                        pk="Base",
+                        defaultSchool = school,
+                        size = 2,
+                        code = "Base",
+                        pk = "Base",
                         schools = new List<Teacher.TeacherSchool> { new Teacher.TeacherSchool { schoolId = school, name = data.name, status = "invite", time = now, picture = data.picture, areaId = data.areaId } }
                     };
                     await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).CreateItemAsync(teacher, new PartitionKey("Base"));
@@ -289,30 +301,36 @@ namespace TEAMModelAPI.Controllers
                     schoolTeachersAdd.Add(schoolTeacher);
                     await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).CreateItemAsync(schoolTeacher, new PartitionKey(schoolTeacher.code));
                 }
-                else {
-                    if (subjectIds.IsNotEmpty()) {
+                else
+                {
+                    if (subjectIds.IsNotEmpty())
+                    {
                         subjectIds.ForEach(x => {
-                            if (!schoolTeacher.subjectIds.Contains(x)) {
+                            if (!schoolTeacher.subjectIds.Contains(x))
+                            {
                                 schoolTeacher.subjectIds.Add(x);
                             }
                         });
                     }
                     schoolTeacher.job = string.IsNullOrWhiteSpace(item.job) ? schoolTeacher.job : item.job;
                     schoolTeachersAdd.Add(schoolTeacher);
-                    await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReplaceItemAsync(schoolTeacher,schoolTeacher.id, new PartitionKey(schoolTeacher.code));
+                    await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReplaceItemAsync(schoolTeacher, schoolTeacher.id, new PartitionKey(schoolTeacher.code));
                 }
             }
-            if (baseChange) {
+            if (baseChange)
+            {
                 await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReplaceItemAsync(data, data.id, new PartitionKey("Base"));
             }
-            return Ok(new { teachers= schoolTeachersAdd.Select(x => new { x.id, x.name, x.picture, x.job, x.subjectIds, x.roles,school })});
+            return Ok(new { teachers = schoolTeachersAdd.Select(x => new { x.id, x.name, x.picture, x.job, x.subjectIds, x.roles, school }) });
         }
     }
 
-    public class ImportTechDto {
+    public class ImportTechDto
+    {
         public List<ImportTech> teachers { get; set; } = new List<ImportTech>();
     }
-    public class ImportTech {
+    public class ImportTech
+    {
         [Required(ErrorMessage = "教师id必须设置")]
         public string id { get; set; }
         public List<ImportTechSubject> subjects { get; set; }

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

@@ -11,14 +11,14 @@
     </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_5w5z4u9hhce.js"></script>
+<script src="https://at.alicdn.com/t/font_2934132_ghuo4dq8cwd.js"></script>
 
 <body>
     <noscript>
       <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
     </noscript>
     <div id="app"></div>
-    <!-- built files will be auto injected -->
+    <!-- built files will be auto injected   -->
 </body>
 
 </html>

BIN
TEAMModelBI/ClientApp/src/assets/img/background-area.png


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


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


BIN
TEAMModelBI/ClientApp/src/assets/img/down.png


BIN
TEAMModelBI/ClientApp/src/assets/img/header.png


BIN
TEAMModelBI/ClientApp/src/assets/img/school-background.jpg


BIN
TEAMModelBI/ClientApp/src/assets/img/sort1.png


BIN
TEAMModelBI/ClientApp/src/assets/img/sort2.png


BIN
TEAMModelBI/ClientApp/src/assets/img/sort3.png


BIN
TEAMModelBI/ClientApp/src/assets/img/up.png


+ 246 - 0
TEAMModelBI/ClientApp/src/components/echarts/barandline.vue

@@ -0,0 +1,246 @@
+<template>
+    <div ref="myEcharts" :style="{ height, width }"></div>
+</template>
+<script>
+import { ref, onMounted, nextTick, watch, getCurrentInstance } from 'vue'
+import * as echarts from 'echarts'
+export default {
+    props: {
+        width: {
+            type: String,
+            default: '100%',
+        },
+        height: {
+            type: String,
+            default: '100%',
+        },
+        barlineData: {
+            type: String,
+            default: 'all',
+        },
+        title: {
+            type: String,
+            default: '',
+        },
+    },
+    setup(props) {
+        console.log(props, '父传值')
+        const myEcharts = ref(null)
+        let { proxy } = getCurrentInstance()
+        const chart = new InitChart(props, myEcharts)
+        onMounted(() => {
+            chart.init(props.barlineData, proxy)
+        })
+        watch(
+            props,
+            (nweProps) => {
+                nextTick(() => {
+                    nweProps ? chart.init(props.barlineData, proxy) : ''
+                })
+            },
+            { immediate: true, deep: true }
+        )
+        return {
+            myEcharts,
+        }
+    },
+}
+class InitChart {
+    constructor(props, myEcharts) {
+        this.props = props
+        this.myEcharts = myEcharts
+        this.state = {
+            chart: null,
+        }
+    }
+    init(datas, proxy) {
+        console.log(datas, 'line调用')
+        let newData = ['1月', '2月', '3月', '4月', '5月']
+        this.state.chart && this.destory()
+        this.state.chart = echarts.init(this.myEcharts.value)
+        this.state.chart.setOption({
+            backgroundColor: '#ffffff',
+            grid: {
+                top: '10%',
+                right: '0',
+                left: '5%',
+                bottom: '25%', //图表尺寸大小
+            },
+            legend: {
+                show: false,
+                textStyle: {
+                    color: '#909399',
+                    fontSize: 12,
+                },
+            },
+            tooltip: {
+                trigger: 'axis',
+                fontSize: 12,
+            },
+            xAxis: {
+                type: 'category',
+                color: '#303133',
+                data: newData,
+                axisLabel: {
+                    margin: 10,
+                    color: '#303133',
+                    textStyle: {
+                        fontSize: 14,
+                    },
+                },
+                axisLine: {
+                    lineStyle: {
+                        color: '#EBEEF5',
+                    },
+                },
+                axisTick: {
+                    show: false,
+                },
+                splitLine: {
+                    show: false,
+                },
+            },
+            yAxis: [
+                {
+                    min: 0,
+                    max: 600,
+                    // axisLabel: {
+                    //     formatter: '{value}',
+                    //     color: '#EEEEEE',
+                    //     textStyle: {
+                    //         fontSize: 14,
+                    //     },
+                    // },
+                    axisLine: {
+                        lineStyle: {
+                            color: '#477AA5',
+                            type: 'dashed',
+                        },
+                    },
+                    axisTick: {
+                        show: false,
+                    },
+                    splitLine: {
+                        show: false,
+                    },
+                },
+                {
+                    min: 0,
+                    max: 600,
+                    type: 'value',
+                    axisLabel: {
+                        formatter: '{value}',
+                        color: '#EEEEEE',
+                        textStyle: {
+                            fontSize: 14,
+                        },
+                    },
+                    axisLine: {
+                        lineStyle: {
+                            color: '#477AA5',
+                            type: 'dashed',
+                        },
+                    },
+                    axisTick: {
+                        show: false,
+                    },
+                    splitLine: {
+                        show: false,
+                    },
+                },
+            ],
+            dataZoom: [
+                {
+                    show: true,
+                    height: 15,
+                    xAxisIndex: [0, 1],
+                    bottom: 10,
+                    right: '8%',
+                    left: '5%',
+                    start: 0,
+                    end: 100,
+                    backgroundColor: 'rgba(0,0,0,0)',
+                    handleIcon: 'path://M306.1,413c0,2.2-1.8,4-1,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z',
+                    handleSize: '110%',
+                    handleStyle: {
+                        color: '#999999',
+                    },
+                    textStyle: {
+                        color: '#999999',
+                    },
+                    borderColor: '',
+                    zoomOnMouseWheel: false,
+                    moveOnMouseMove: true,
+                    moveOnMouseWheel: true,
+                },
+                {
+                    type: 'inside',
+                    show: true,
+                    height: 15,
+                    start: 1,
+                    end: 100,
+                },
+            ],
+            series: [
+                {
+                    type: 'bar',
+                    name: '每月登陆量',
+                    data: [150, 340, 430, 280, 550],
+                    barWidth: '18',
+                    itemStyle: {
+                        normal: {
+                            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                                {
+                                    offset: 0,
+                                    color: 'rgba(65,159,255,1)',
+                                },
+                                {
+                                    offset: 1,
+                                    color: 'rgba(65,159,255,0.1)',
+                                },
+                            ]),
+                            barBorderRadius: [30, 30, 30, 30], //圆角大小
+                        },
+                    },
+                    label: {
+                        normal: {
+                            show: false,
+                            fontSize: 16,
+                            fontWeight: 'bold',
+                            color: '#333',
+                            position: 'top',
+                        },
+                    },
+                },
+                {
+                    data: [150, 340, 430, 280, 550],
+                    type: 'line',
+                    showAllSymbol: true,
+                    yAxisIndex: 1,
+                    smooth: true,
+                    name: '每日登陆量',
+                    symbolSize: 6,
+                    symbol: 'circle',
+                    itemStyle: {
+                        normal: {
+                            color: '#5C41FF',
+                        },
+                    },
+                },
+            ],
+        })
+        window.addEventListener('resize', () => {
+            this.state.chart.resize()
+        })
+    }
+
+    destory() {
+        this.state.chart.dispose()
+        window.removeEventListener('resize', () => {
+            console.log('事件移除')
+        })
+    }
+}
+</script>
+<style lang="less"></style>
+

+ 247 - 0
TEAMModelBI/ClientApp/src/components/echarts/commbarandline.vue

@@ -0,0 +1,247 @@
+<template>
+    <div ref="myEcharts" :style="{ height, width }"></div>
+</template>
+<script>
+import { ref, onMounted, nextTick, watch, getCurrentInstance } from 'vue'
+import * as echarts from 'echarts'
+export default {
+    props: {
+        width: {
+            type: String,
+            default: '100%',
+        },
+        height: {
+            type: String,
+            default: '100%',
+        },
+        barlineData: {
+            type: String,
+            default: 'all',
+        },
+        title: {
+            type: String,
+            default: '',
+        },
+    },
+    setup(props) {
+        console.log(props, '父传值')
+        const myEcharts = ref(null)
+        let { proxy } = getCurrentInstance()
+        const chart = new InitChart(props, myEcharts)
+        onMounted(() => {
+            chart.init(props.barlineData, proxy)
+        })
+        watch(
+            props,
+            (nweProps) => {
+                nextTick(() => {
+                    nweProps ? chart.init(props.barlineData, proxy) : ''
+                })
+            },
+            { immediate: true, deep: true }
+        )
+        return {
+            myEcharts,
+        }
+    },
+}
+class InitChart {
+    constructor(props, myEcharts) {
+        this.props = props
+        this.myEcharts = myEcharts
+        this.state = {
+            chart: null,
+        }
+    }
+    init(datas, proxy) {
+        console.log(datas, 'line调用')
+        let newData = ['4.10', '4.11', '4.12', '4.13', '4.14', '4.15', '4.16', '4.17', '4.18', '4.19', '4.20']
+        this.state.chart && this.destory()
+        this.state.chart = echarts.init(this.myEcharts.value)
+        this.state.chart.setOption({
+            backgroundColor: '#ffffff',
+            grid: {
+                top: '10%',
+                right: '0',
+                left: '5%',
+                bottom: '25%', //图表尺寸大小
+            },
+            legend: {
+                show: false,
+                textStyle: {
+                    color: '#909399',
+                    fontSize: 12,
+                },
+            },
+            tooltip: {
+                trigger: 'axis',
+                fontSize: 12,
+            },
+            xAxis: {
+                type: 'category',
+                color: '#303133',
+                data: newData,
+                axisLabel: {
+                    margin: 10,
+                    color: '#303133',
+                    textStyle: {
+                        fontSize: 14,
+                    },
+                },
+                axisLine: {
+                    lineStyle: {
+                        color: '#EBEEF5',
+                    },
+                },
+                axisTick: {
+                    show: false,
+                },
+                splitLine: {
+                    show: false,
+                },
+            },
+            yAxis: [
+                {
+                    min: 0,
+                    max: 120,
+                    // axisLabel: {
+                    //     formatter: '{value}',
+                    //     color: '#EEEEEE',
+                    //     textStyle: {
+                    //         fontSize: 14,
+                    //     },
+                    // },
+                    axisLine: {
+                        lineStyle: {
+                            color: '#477AA5',
+                            type: 'dashed',
+                        },
+                    },
+                    axisTick: {
+                        show: false,
+                    },
+                    splitLine: {
+                        show: false,
+                    },
+                },
+                {
+                    min: 0,
+                    max: 120,
+                    type: 'value',
+                    axisLabel: {
+                        // formatter: '{value}',
+                        color: '#EEEEEE',
+                        textStyle: {
+                            fontSize: 14,
+                        },
+                    },
+                    axisLine: {
+                        lineStyle: {
+                            color: '#477AA5',
+                            type: 'dashed',
+                        },
+                    },
+                    axisTick: {
+                        show: false,
+                    },
+                    splitLine: {
+                        show: false,
+                    },
+                },
+            ],
+            dataZoom: [
+                {
+                    show: true,
+                    height: 15,
+                    xAxisIndex: [0, 1],
+                    bottom: 10,
+                    right: '8%',
+                    left: '5%',
+                    start: 0,
+                    end: 70,
+                    backgroundColor: 'rgba(0,0,0,0)',
+                    handleIcon: 'path://M306.1,413c0,2.2-1.8,4-1,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z',
+                    handleSize: '110%',
+                    handleStyle: {
+                        color: '#999999',
+                    },
+                    textStyle: {
+                        color: '#999999',
+                    },
+                    borderColor: '',
+                    zoomOnMouseWheel: false,
+                    moveOnMouseMove: true,
+                    moveOnMouseWheel: true,
+                },
+                {
+                    type: 'inside',
+                    show: true,
+                    height: 15,
+                    start: 1,
+                    end: 100,
+                },
+            ],
+            series: [
+                {
+                    type: 'bar',
+                    name: '每日登陆量',
+                    data: [20, 60, 110, 80, 90, 70, 103, 100, 92, 50, 55],
+                    barWidth: '18',
+                    itemStyle: {
+                        normal: {
+                            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                                {
+                                    offset: 0,
+                                    color: 'rgba(65,159,255,1)',
+                                },
+                                {
+                                    offset: 1,
+                                    color: 'rgba(65,159,255,0.1)',
+                                },
+                            ]),
+                            barBorderRadius: [30, 30, 30, 30], //圆角大小
+                        },
+                    },
+                    label: {
+                        normal: {
+                            show: false,
+                            fontSize: 16,
+                            fontWeight: 'bold',
+                            color: '#333',
+                            position: 'top',
+                        },
+                    },
+                },
+                {
+                    data: [20, 60, 110, 80, 90, 70, 103, 100, 92, 50, 55],
+                    type: 'line',
+                    showAllSymbol: true,
+                    yAxisIndex: 1,
+                    smooth: true,
+                    name: '登录趋势',
+                    symbolSize: 6,
+                    symbol: 'circle',
+                    itemStyle: {
+                        show: false,
+                        normal: {
+                            color: '#5C41FF',
+                        },
+                    },
+                },
+            ],
+        })
+        window.addEventListener('resize', () => {
+            this.state.chart.resize()
+        })
+    }
+
+    destory() {
+        this.state.chart.dispose()
+        window.removeEventListener('resize', () => {
+            console.log('事件移除')
+        })
+    }
+}
+</script>
+<style lang="less"></style>
+

+ 2 - 1
TEAMModelBI/ClientApp/src/components/echarts/commonLine.vue

@@ -59,8 +59,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: {

+ 185 - 0
TEAMModelBI/ClientApp/src/components/echarts/commonMeter.vue

@@ -0,0 +1,185 @@
+<!--基础折线图-->
+<template>
+    <div ref="myEcharts" :style="{ height, width }"></div>
+</template>
+<script>
+import { ref, onMounted, nextTick, watch, getCurrentInstance } from 'vue'
+import * as echarts from 'echarts'
+export default {
+    props: {
+        width: {
+            type: String,
+            default: '100%',
+        },
+        height: {
+            type: String,
+            default: '100%',
+        },
+        meterData: {
+            type: Object,
+            default: () => {},
+        },
+        title: {
+            type: String,
+            default: '',
+        },
+    },
+    setup(props) {
+        const myEcharts = ref(null)
+        let { proxy } = getCurrentInstance()
+        const chart = new InitChart(props, myEcharts)
+        onMounted(() => {
+            chart.init(props.barData, proxy)
+        })
+        watch(
+            props,
+            (nweProps) => {
+                nextTick(() => {
+                    nweProps ? chart.init(props.barData, proxy) : ''
+                })
+            },
+            { immediate: true, deep: true }
+        )
+        return {
+            myEcharts,
+        }
+    },
+}
+class InitChart {
+    constructor(props, myEcharts) {
+        this.props = props
+        this.myEcharts = myEcharts
+        this.state = {
+            chart: null,
+        }
+    }
+    init(datas, proxy) {
+        console.log(datas, '柱状图的调用')
+        var value = 45
+        var pointerColor = new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+            {
+                offset: 0,
+                color: '#2EE5E3',
+            },
+            {
+                offset: 1,
+                color: '#385CF7',
+            },
+        ])
+        var color = new echarts.graphic.LinearGradient(0, 0, 1, 0, [
+            {
+                offset: 0,
+                color: '#2EE5E3',
+            },
+            {
+                offset: 1,
+                color: '#385CF7',
+            },
+        ])
+        this.state.chart && this.destory()
+        this.state.chart = echarts.init(this.myEcharts.value)
+        this.state.chart.setOption({
+            backgroundColor: '#fff',
+            grid: {
+                x: 0,
+                y: 0,
+                x2: 0,
+                y2: 0,
+                top: '0%',
+                left: '0%',
+                right: '0%',
+                bottom: '0%',
+                containLabel: true,
+            },
+            series: [
+                {
+                    type: 'gauge',
+                    radius: '75%',
+                    startAngle: 225,
+                    endAngle: -45,
+                    min: 0,
+                    max: 100,
+                    title: {
+                        show: false,
+                    },
+                    detail: {
+                        color: '#1890FF',
+                        show: true,
+                        fontFamily: '"Microsoft Yahei","微软雅黑"',
+                        fontWeight: 700,
+                        fontSize: 20,
+                        offsetCenter: [0, '75%'],
+                        formatter: (val) => value + '%',
+                    },
+                    axisLine: {
+                        roundCap: true,
+                        show: true,
+                        lineStyle: {
+                            width: 20,
+                            color: [
+                                [value / 100, color],
+                                [1, 'rgba(225,225,225,0.4)'],
+                            ],
+                            // shadowColor: 'rgba(0,138,255,0.35)',
+                            // shadowBlur: 5,
+                        },
+                    },
+                    axisTick: {
+                        distance: -20,
+                        length: 7,
+                        lineStyle: {
+                            color: '#fff',
+                            width: 1,
+                        },
+                    },
+                    axisLabel: {
+                        show: false,
+                    },
+                    pointer: {
+                        itemStyle: {
+                            color: pointerColor,
+                        },
+                        // show:false,
+                        width: 18,
+                        length: 65,
+                    },
+                    // itemStyle: {
+                    //     color: color,
+                    //     shadowColor: 'rgba(0,138,255,0.45)',
+                    //     shadowBlur: 10,
+                    //     // shadowOffsetX: 2,
+                    //     // shadowOffsetY: 2,
+                    // },
+                    splitLine: {
+                        show: true,
+                        length: 20,
+                        distance: -20,
+                        lineStyle: {
+                            color: '#fff',
+                            width: 1,
+                        },
+                    },
+                    data: [
+                        {
+                            value: value,
+                            name: '双绿灯率',
+                        },
+                    ],
+                },
+            ],
+        })
+        window.addEventListener('resize', () => {
+            this.state.chart.resize()
+        })
+    }
+
+    destory() {
+        this.state.chart.dispose()
+        window.removeEventListener('resize', () => {
+            console.log('事件移除')
+        })
+    }
+}
+</script>
+<style lang="less"></style>
+

+ 190 - 0
TEAMModelBI/ClientApp/src/components/echarts/copyMeter.vue

@@ -0,0 +1,190 @@
+<!--基础折线图-->
+<template>
+    <div ref="myEcharts" :style="{ height, width }"></div>
+</template>
+<script>
+import { ref, onMounted, nextTick, watch, getCurrentInstance } from 'vue'
+import * as echarts from 'echarts'
+export default {
+    props: {
+        width: {
+            type: String,
+            default: '100%',
+        },
+        height: {
+            type: String,
+            default: '100%',
+        },
+        meterData: {
+            type: Object,
+            default: () => {},
+        },
+        title: {
+            type: String,
+            default: '',
+        },
+    },
+    setup(props) {
+        const myEcharts = ref(null)
+        let { proxy } = getCurrentInstance()
+        const chart = new InitChart(props, myEcharts)
+        onMounted(() => {
+            chart.init(props.barData, proxy)
+        })
+        watch(
+            props,
+            (nweProps) => {
+                nextTick(() => {
+                    nweProps ? chart.init(props.barData, proxy) : ''
+                })
+            },
+            { immediate: true, deep: true }
+        )
+        return {
+            myEcharts,
+        }
+    },
+}
+class InitChart {
+    constructor(props, myEcharts) {
+        this.props = props
+        this.myEcharts = myEcharts
+        this.state = {
+            chart: null,
+        }
+    }
+    init(datas, proxy) {
+        console.log(datas, '柱状图的调用')
+        var value = 18
+        var pointerColor = new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+            {
+                offset: 0,
+                color: '#2EE5E3',
+            },
+            {
+                offset: 1,
+                color: '#385CF7',
+            },
+        ])
+        var color = new echarts.graphic.LinearGradient(0, 0, 1, 0, [
+            {
+                offset: 0,
+                color: '#2EE5E3',
+            },
+            {
+                offset: 1,
+                color: '#385CF7',
+            },
+        ])
+        this.state.chart && this.destory()
+        this.state.chart = echarts.init(this.myEcharts.value)
+        this.state.chart.setOption({
+            title: {
+                show: false,
+                text: '学区影片占有率',
+                left: '50%',
+                bottom: 0,
+                textAlign: 'center',
+                textStyle: {
+                    color: 'rgba(0,0,0,0.65)',
+                    fontSize: 14,
+                    fontFamily: '"Microsoft Yahei","微软雅黑"',
+                },
+            },
+            legend: {
+                right: '0%',
+            },
+            backgroundColor: '#fff',
+            series: [
+                {
+                    type: 'gauge',
+                    radius: '75%',
+                    startAngle: 225,
+                    endAngle: -45,
+                    min: 0,
+                    max: 100,
+                    title: {
+                        show: false,
+                    },
+                    detail: {
+                        color: '#1890FF',
+                        show: true,
+                        fontFamily: '"Microsoft Yahei","微软雅黑"',
+                        fontWeight: 700,
+                        fontSize: 20,
+                        offsetCenter: [0, '75%'],
+                        formatter: (val) => value + '%',
+                    },
+                    axisLine: {
+                        roundCap: true,
+                        show: true,
+                        lineStyle: {
+                            width: 20,
+                            color: [
+                                [value / 100, color],
+                                [1, 'rgba(225,225,225,0.4)'],
+                            ],
+                            // shadowColor: 'rgba(0,138,255,0.35)',
+                            // shadowBlur: 5,
+                        },
+                    },
+                    axisTick: {
+                        distance: -20,
+                        length: 7,
+                        lineStyle: {
+                            color: '#fff',
+                            width: 1,
+                        },
+                    },
+                    axisLabel: {
+                        show: false,
+                    },
+                    pointer: {
+                        itemStyle: {
+                            color: pointerColor,
+                        },
+
+                        // show:false,
+                        width: 18,
+                        length: 75,
+                    },
+                    itemStyle: {
+                        color: color,
+                        shadowColor: 'rgba(0,138,255,0.45)',
+                        shadowBlur: 10,
+                        // shadowOffsetX: 2,
+                        // shadowOffsetY: 2,
+                    },
+                    splitLine: {
+                        show: true,
+                        length: 20,
+                        distance: -20,
+                        lineStyle: {
+                            color: '#fff',
+                            width: 1,
+                        },
+                    },
+                    data: [
+                        {
+                            value: value,
+                            name: '学区影片占有率',
+                        },
+                    ],
+                },
+            ],
+        })
+        window.addEventListener('resize', () => {
+            this.state.chart.resize()
+        })
+    }
+
+    destory() {
+        this.state.chart.dispose()
+        window.removeEventListener('resize', () => {
+            console.log('事件移除')
+        })
+    }
+}
+</script>
+<style lang="less"></style>
+

+ 214 - 0
TEAMModelBI/ClientApp/src/components/echarts/gradePie.vue

@@ -0,0 +1,214 @@
+<!--基础折线图-->
+<template>
+    <div ref="myEcharts" :style="{ height, width }"></div>
+</template>
+<script>
+import { ref, onMounted, nextTick, watch, getCurrentInstance } from 'vue'
+import * as echarts from 'echarts'
+export default {
+    name: 'baseBar',
+    props: {
+        width: {
+            type: String,
+            default: '100%',
+        },
+        height: {
+            type: String,
+            default: '100%',
+        },
+        mapData: {
+            type: Object,
+            default: () => {},
+        },
+        title: {
+            type: String,
+            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)
+        })
+        watch(
+            props,
+            (nweProps) => {
+                nextTick(() => {
+                    nweProps ? chart.init(props.mapData, proxy) : ''
+                })
+            },
+            { immediate: true, deep: true }
+        )
+        return {
+            myEcharts,
+        }
+    },
+}
+class InitChart {
+    constructor(props, myEcharts) {
+        this.props = props
+        this.myEcharts = myEcharts
+        this.state = {
+            chart: null,
+        }
+    }
+    init(datas, proxy) {
+        console.log(datas, '1111111111111111')
+        var data = [
+            {
+                value: 200,
+                name: '一年级',
+            },
+            {
+                value: 110,
+                name: '二年级',
+            },
+            {
+                value: 150,
+                name: '三年级',
+            },
+            {
+                value: 180,
+                name: '四年级',
+            },
+        ]
+        let datum = data.map((v) => v.value)
+        let color = ['#27D099', '#B458F5', '#FCD54B', '#5684F4', '#81ecec', '#ff7675']
+        let data1 = data[0].value + data[1].value + data[2].value + data[3].value
+        let baseData = []
+        for (var i = 0; i < data.length; i++) {
+            baseData.push({
+                value: data[i].value,
+                name: data[i].name,
+                itemStyle: {
+                    normal: {
+                        borderWidth: 30,
+                        shadowBlur: 20,
+                        borderColor: color[i],
+                        borderRadius: 20,
+                    },
+                },
+            })
+        }
+        this.state.chart && this.destory()
+        this.state.chart = echarts.init(this.myEcharts.value)
+        this.state.chart.setOption({
+            title: {
+                text: '年级占比',
+                textStyle: {
+                    fontSize: '12',
+                    color: '#b2bec3',
+                },
+            },
+            color: color,
+            tooltip: {
+                show: true,
+                trigger: 'item',
+                formatter: '{b} <br/>占比:{d}%',
+            },
+            legend: {
+                orient: 'vertical',
+                right: '10%',
+                top: '35%',
+                itemGap: 50,
+                itemWidth: 12,
+            },
+            grid: {
+                top: 'bottom',
+                left: 40,
+                bottom: 10,
+                width: '50%',
+                height: '50%',
+            },
+            series: [
+                {
+                    zlevel: 1,
+                    name: '',
+                    type: 'pie',
+                    selectedMode: 'single',
+                    radius: [40, 80],
+                    center: ['35%', '50%'],
+                    startAngle: 20,
+                    // hoverAnimation: false,
+                    label: {
+                        normal: {
+                            position: 'inside',
+                            show: true,
+                            color: '#fff',
+                            formatter: function (params) {
+                                return params.percent.toFixed() + '%'
+                            },
+                            rich: {
+                                b: {
+                                    fontSize: 16,
+                                    lineHeight: 30,
+                                    color: '#fff',
+                                },
+                            },
+                        },
+                    },
+                    itemStyle: {
+                        normal: {
+                            shadowColor: 'rgba(0, 0, 0, 0.2)',
+                            shadowBlur: 10,
+                        },
+                    },
+                    data: baseData,
+                },
+                {
+                    name: '',
+                    type: 'pie',
+                    selectedMode: 'single',
+                    radius: [40, 80],
+                    center: ['35%', '50%'],
+                    startAngle: 20,
+                    data: [
+                        {
+                            value: data1,
+                            name: '',
+                            label: {
+                                normal: {
+                                    show: false,
+                                    formatter: '{c|{c}辆} \n {a|本日外来\n车辆总数}',
+                                    rich: {
+                                        c: {
+                                            color: '#000',
+                                            fontSize: 20,
+                                            fontWeight: 'bold',
+                                            lineHeight: 2,
+                                            align: 'center',
+                                            padding: [30, 0, 30, 0],
+                                        },
+                                        a: {
+                                            align: 'center',
+                                            color: 'rgb(98,137,169)',
+                                            fontSize: 12,
+                                            padding: [16, 0, -10, -10],
+                                        },
+                                    },
+                                    position: 'center',
+                                },
+                            },
+                        },
+                    ],
+                },
+            ],
+        })
+        window.addEventListener('resize', () => {
+            this.state.chart.resize()
+        })
+    }
+
+    destory() {
+        this.state.chart.dispose()
+        window.removeEventListener('resize', () => {
+            console.log('事件移除')
+        })
+    }
+}
+</script>
+<style lang="less"></style>
+

+ 221 - 0
TEAMModelBI/ClientApp/src/components/echarts/specialLinePie.vue

@@ -0,0 +1,221 @@
+<template>
+    <div ref="myEcharts" :style="{ height, width }"></div>
+</template>
+<script>
+import { ref, onMounted, nextTick, watch, getCurrentInstance } from 'vue'
+import * as echarts from 'echarts'
+export default {
+    props: {
+        width: {
+            type: String,
+            default: '100%',
+        },
+        height: {
+            type: String,
+            default: '100%',
+        },
+        barlineData: {
+            type: String,
+            default: 'all',
+        },
+        title: {
+            type: String,
+            default: '',
+        },
+    },
+    setup(props) {
+        console.log(props, '父传值')
+        const myEcharts = ref(null)
+        let { proxy } = getCurrentInstance()
+        const chart = new InitChart(props, myEcharts)
+        var minPieRadius = 20
+        var base = 10000
+        var lineData = getVirtulData()
+        let pie = getPieSeries(lineData, echarts)
+        onMounted(() => {
+            chart.init(props.barlineData, proxy)
+            // setTimeout(function () {
+            //     myEcharts.value.setOption({
+            //         series: getPieSeries(lineData, echarts),
+            //     })
+            // }, 10)
+        })
+        // 根据y轴数值得到饼图半径大小
+        function getPieRadius(value) {
+            var pieRadius = Math.floor((value / (1.1 * base)) * 40) > minPieRadius ? Math.floor((value / base) * 40) : minPieRadius
+            return pieRadius
+        }
+        // 得到虚拟数据
+        function getVirtulData() {
+            var date = +echarts.number.parseDate('2022-01-01')
+            var end = +echarts.number.parseDate('2022-01-10')
+            var dayTime = 3600 * 24 * 1000
+            var data = []
+            for (var time = date; time < end; time += dayTime) {
+                data.push([echarts.format.formatTime('yyyy-MM-dd', time), Math.floor((Math.random() + 0.1) * base)])
+            }
+            return data
+        }
+        // 根据虚拟数据得到饼图数据
+        function getPieSeries(lineData, echarts) {
+            console.log(lineData, echarts, '9999999')
+            return echarts.util.map(lineData, function (item, index) {
+                // var center = myEchartsInfo.convertToPixel({ seriesIndex: 0 }, item)
+                return {
+                    id: index + 'pie',
+                    type: 'pie',
+                    // center: center,
+                    label: {
+                        normal: {
+                            formatter: '{c}',
+                            position: 'inside',
+                        },
+                    },
+                    zlevel: 1,
+                    radius: getPieRadius(item[1]),
+                    data: [
+                        { name: '工作', value: Math.round(Math.random() * 24) },
+                        { name: '娱乐', value: Math.round(Math.random() * 24) },
+                        { name: '睡觉', value: Math.round(Math.random() * 24) },
+                    ],
+                }
+            })
+        }
+        console.log(lineData, pie, '11111111111111111111111111')
+        watch(
+            props,
+            (nweProps) => {
+                nextTick(() => {
+                    nweProps ? chart.init(lineData, pie) : ''
+                })
+            },
+            { immediate: true, deep: true }
+        )
+        return {
+            myEcharts,
+            minPieRadius,
+            base,
+            getPieRadius,
+            getVirtulData,
+            getPieSeries,
+            lineData,
+        }
+    },
+}
+class InitChart {
+    constructor(props, myEcharts) {
+        this.props = props
+        this.myEcharts = myEcharts
+        this.state = {
+            chart: null,
+        }
+    }
+    init(lineData, pie) {
+        let newData = ['1月', '2月', '3月', '4月', '5月']
+        this.state.chart && this.destory()
+        this.state.chart = echarts.init(this.myEcharts.value)
+        this.state.chart.setOption({
+            tooltip: {},
+            legend: {
+                data: ['工作', '娱乐', '睡觉'],
+                bottom: 20,
+            },
+            grid: {
+                left: '10%',
+                right: '10%',
+                top: '10%',
+                bottom: '20%',
+            },
+            xAxis: {
+                type: 'category',
+                name: '日',
+                nameGap: 5,
+                axisLabel: {
+                    color: '#000',
+                    textStyle: {
+                        fontSize: '13',
+                    },
+                },
+                axisTick: {
+                    show: true,
+                },
+                axisLine: {
+                    lineStyle: {
+                        color: '#000',
+                    },
+                },
+                splitLine: {
+                    show: false,
+                },
+            },
+            yAxis: {
+                type: 'value',
+                name: '步数',
+                nameGap: 10,
+                // scale: true,
+                nameTextStyle: {
+                    fontSize: 13,
+                },
+                axisLabel: {
+                    textStyle: {
+                        fontSize: '13',
+                    },
+                },
+                axisTick: {
+                    show: true,
+                },
+                axisLine: {
+                    show: true,
+                    lineStyle: {
+                        color: '#000',
+                    },
+                },
+                splitLine: {
+                    show: false,
+                },
+            },
+            series: [
+                {
+                    type: 'line',
+                    symbolSize: 0,
+                    lineStyle: {
+                        color: '#000a',
+                        width: 1,
+                    },
+                    data: lineData,
+                },
+                {
+                    id: 'pie',
+                    type: 'pie',
+                    // center: center,
+                    label: {
+                        normal: {
+                            formatter: '{c}',
+                            position: 'inside',
+                        },
+                    },
+                    zlevel: 1,
+                    radius: this.getPieRadius(item[1]),
+                    data: [
+                        { name: '工作', value: Math.round(Math.random() * 24) },
+                        { name: '娱乐', value: Math.round(Math.random() * 24) },
+                        { name: '睡觉', value: Math.round(Math.random() * 24) },
+                    ],
+                },
+            ],
+        })
+        window.addEventListener('resize', () => {
+            this.state.chart.resize()
+        })
+    }
+
+    destory() {
+        this.state.chart.dispose()
+        window.removeEventListener('resize', () => {
+            console.log('事件移除')
+        })
+    }
+}
+</script>
+<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%',
                 },
             ],
         })

+ 300 - 0
TEAMModelBI/ClientApp/src/components/echarts/test/HiteachBar.vue

@@ -0,0 +1,300 @@
+<template>
+    <div ref="myEcharts" :style="{ height, width }"></div>
+</template>
+<script>
+import { ref, onMounted, nextTick, watch, getCurrentInstance } from 'vue'
+import * as echarts from 'echarts'
+export default {
+    props: {
+        width: {
+            type: String,
+            default: '100%',
+        },
+        height: {
+            type: String,
+            default: '100%',
+        },
+        lineData: {
+            type: Array,
+            default: () => {},
+        },
+        title: {
+            type: String,
+            default: '',
+        },
+    },
+    setup(props) {
+        const myEcharts = ref(null)
+        let { proxy } = getCurrentInstance()
+        const chart = new InitChart(props, myEcharts)
+        onMounted(() => {
+            chart.init(props.lineData, proxy)
+        })
+        watch(
+            props,
+            (nweProps) => {
+                nextTick(() => {
+                    nweProps ? chart.init(props.lineData, proxy) : ''
+                })
+            },
+            { immediate: true, deep: true }
+        )
+        return {
+            myEcharts,
+        }
+    },
+}
+class InitChart {
+    constructor(props, myEcharts) {
+        this.props = props
+        this.myEcharts = myEcharts
+        console.log(this.myEcharts, 'charts!123456')
+        this.state = {
+            chart: null,
+        }
+    }
+    init(datas, proxy) {
+        console.log(datas, 'line调用')
+        let dataY = [98, 38, 48, 35, 92, 28, 93, 85]
+        let dataY1 = [40, 50, 30, 30, 30, 40, 40, 40, 30, 10, 50]
+        let firstIndustry = [72, 37, 4, 75, 105, 36, 0, 15, 30, 45, 88]
+        var lineData = dataY1.map((v, i) => v + firstIndustry[i])
+        let xData = [
+            '0:00',
+            '1:00',
+            '2:00',
+            '3:00',
+            '4:00',
+            '5:00',
+            '6:00',
+            '7:00',
+            '8:00',
+            '9:00',
+            '10:00',
+            '11:00',
+            '12:00',
+            '13:00',
+            '14:00',
+            '15:00',
+            '16:00',
+            '17:00',
+            '18:00',
+            '19:00',
+            '20:00',
+            '21:00',
+            '22:00',
+            '23:00',
+            '24:00',
+        ]
+        this.state.chart && this.destory()
+        this.state.chart = echarts.init(this.myEcharts.value)
+        console.log(this.state.chart, 'chart!!!')
+        this.state.chart.setOption({
+            // backgroundColor: '#fff',
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'shadow',
+                },
+                // formatter: function (params) {
+                //     return dataX[params[0].dataIndex] + '<br/>学校课例:' + dataY1[params[0].dataIndex] + '<br> 个人课例:' + firstIndustry[params[0].dataIndex] + ''
+                // },
+            },
+            // legend: { orient: 'vertical', top: 0, right: 0, itemWidth: 10, itemHeight: 10, icon: 'circle', data: ['学校课例', '个人课例'] },
+            grid: {
+                top: '15%',
+                bottom: '10%',
+                left: '1%',
+                right: '10%',
+                containLabel: true,
+            },
+            xAxis: {
+                type: 'category',
+                data: xData,
+                axisLine: {
+                    lineStyle: {
+                        color: 'rgba(189, 195, 199,0.5)',
+                    },
+                },
+                axisLabel: {
+                    textStyle: {
+                        color: 'rgba(189, 195, 199,0.5)',
+                    },
+                },
+            },
+            yAxis: [
+                {
+                    type: 'value',
+                    axisLabel: {
+                        textStyle: {
+                            color: 'rgba(189, 195, 199,0.5)',
+                        },
+                    },
+                    axisLine: {
+                        lineStyle: {
+                            fontSize: 12,
+                            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: [
+                {
+                    show: true,
+                    height: 10,
+                    xAxisIndex: [0, 1],
+                    bottom: 10,
+                    right: '10%',
+                    left: '5%',
+                    start: 0,
+                    end: 30,
+                    backgroundColor: 'rgba(0,0,0,0)',
+                    handleIcon: 'path://M306.1,413c0,2.2-1.8,4-1,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z',
+                    handleSize: '100%',
+                    handleStyle: {
+                        color: '#999999',
+                    },
+                    textStyle: {
+                        color: '#999999',
+                    },
+                    borderColor: '',
+                    zoomOnMouseWheel: false,
+                    moveOnMouseMove: true,
+                    moveOnMouseWheel: true,
+                },
+                {
+                    type: 'inside',
+                    show: true,
+                    height: 10,
+                    start: 1,
+                    end: 100,
+                },
+            ],
+            series: [
+                {
+                    name: '学校课例',
+                    type: 'bar',
+                    stack: 'total',
+                    barWidth: '12px',
+                    itemStyle: {
+                        normal: {
+                            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                                {
+                                    offset: 0,
+                                    color: '#29acff',
+                                },
+                                {
+                                    offset: 1,
+                                    color: '#4bdfff',
+                                },
+                            ]),
+                            barBorderRadius: 6,
+                        },
+                    },
+                    data: dataY1,
+                },
+                {
+                    name: '个人课例',
+                    type: 'bar',
+                    stack: 'total',
+                    barWidth: '12px',
+                    itemStyle: {
+                        normal: {
+                            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                                {
+                                    offset: 0,
+                                    color: 'rgba(129, 236, 236,1.0)',
+                                },
+                                {
+                                    offset: 1,
+                                    color: '#6be6c1',
+                                },
+                            ]),
+                            barBorderRadius: 6,
+                        },
+                    },
+                    data: firstIndustry,
+                },
+                {
+                    type: 'scatter',
+                    symbolSize: 10,
+                    itemStyle: {
+                        color: '#2ed573',
+                    },
+                    silent: true,
+                    tooltip: {
+                        show: false,
+                    },
+                    markPoint: {
+                        data: [{ type: 'max', name: 'Max' }],
+                        label: {
+                            color: '#FFF', // 文字颜色
+                            padding: [5, 5, 5, 5], // 可用padding调整图片内文字距离
+                            show: true,
+                        },
+                    },
+                    data: lineData,
+                },
+                {
+                    name: '开课数',
+                    type: 'line',
+                    yAxisIndex: 1, //使用的 y 轴的 index,在单个图表实例中存在多个 y轴的时候有用
+                    smooth: false, //平滑曲线显示
+                    symbol: 'circle', //标记的图形为实心圆
+                    symbolSize: 8, //标记的大小
+                    itemStyle: {
+                        normal: {
+                            color: '#2ed573',
+                            borderColor: 'rgba(46, 213, 115,0.6)', //圆点透明 边框
+                            borderWidth: 5,
+                        },
+                    },
+                    lineStyle: {
+                        color: '#2ed573',
+                    },
+                    data: lineData,
+                },
+            ],
+        })
+        window.addEventListener('resize', () => {
+            this.state.chart.resize()
+        })
+        // var faultByHourIndex = 0 //播放所在下标
+        // var faultByHourTime = setInterval(function () {
+        //     //使得tootip每隔三秒自动显示
+        //     myChart.dispatchAction({
+        //         type: 'showTip', // 根据 tooltip 的配置项显示提示框。
+        //         seriesIndex: 0,
+        //         dataIndex: faultByHourIndex,
+        //     })
+        //     faultByHourIndex++
+        //     // faultRateOption.series[0].data.length 是已报名纵坐标数据的长度
+        //     if (faultByHourIndex >= option.series[0].data.length) {
+        //         faultByHourIndex = 0
+        //     }
+        //     if (faultByHourIndex >= option.series[1].data.length) {
+        //         faultByHourIndex = 0
+        //     }
+        //     if (faultByHourIndex >= option.series[2].data.length) {
+        //         faultByHourIndex = 0
+        //     }
+        // }, 3000)
+    }
+    destory() {
+        this.state.chart.dispose()
+        window.removeEventListener('resize', () => {
+            console.log('事件移除')
+        })
+    }
+}
+</script>
+<style lang="less"></style>
+

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

@@ -0,0 +1,290 @@
+<template>
+    <div ref="myEcharts" :style="{ height, width }"></div>
+</template>
+<script>
+import { ref, onMounted, nextTick, watch, getCurrentInstance } from 'vue'
+import * as echarts from 'echarts'
+export default {
+    props: {
+        width: {
+            type: String,
+            default: '100%',
+        },
+        height: {
+            type: String,
+            default: '100%',
+        },
+        lineData: {
+            type: Array,
+            default: () => {},
+        },
+        title: {
+            type: String,
+            default: '',
+        },
+    },
+    setup(props) {
+        const myEcharts = ref(null)
+        let { proxy } = getCurrentInstance()
+        const chart = new InitChart(props, myEcharts)
+        onMounted(() => {
+            chart.init(props.lineData, proxy)
+        })
+        watch(
+            props,
+            (nweProps) => {
+                nextTick(() => {
+                    nweProps ? chart.init(props.lineData, proxy) : ''
+                })
+            },
+            { immediate: true, deep: true }
+        )
+        return {
+            myEcharts,
+        }
+    },
+}
+class InitChart {
+    constructor(props, myEcharts) {
+        this.props = props
+        this.myEcharts = myEcharts
+        this.state = {
+            chart: null,
+        }
+    }
+    init(datas, proxy) {
+        console.log(datas, 'line调用')
+        let xData = [
+            '0:00',
+            '1:00',
+            '2:00',
+            '3:00',
+            '4:00',
+            '5:00',
+            '6:00',
+            '7:00',
+            '8:00',
+            '9:00',
+            '10:00',
+            '11:00',
+            '12:00',
+            '13:00',
+            '14:00',
+            '15:00',
+            '16:00',
+            '17:00',
+            '18:00',
+            '19:00',
+            '20:00',
+            '21:00',
+            '22:00',
+            '23:00',
+            '24:00',
+        ]
+        let legendData = [] //图例
+        let firstIndustry = [72, 37, 4, 75, 105, 36, 0, 15, 30, 45, 88]
+        this.state.chart && this.destory()
+        this.state.chart = echarts.init(this.myEcharts.value)
+        this.state.chart.setOption({
+            // backgroundColor: '#fff',
+            grid: {
+                x: 0,
+                y: 0,
+                x2: 0,
+                y2: 0,
+                top: '15%',
+                left: '1%',
+                right: '5%',
+                bottom: '10%',
+                containLabel: true,
+            },
+            timeline: {
+                axisType: 'category',
+                show: false,
+                autoPlay: true,
+                playInterval: 1000,
+                checkpointStyle: {
+                    color: '#04a5f1',
+                    borderColor: 'rgba(4, 165, 261, .5)',
+                },
+                data: xData,
+            },
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    // type: 'cross',
+                    crossStyle: {
+                        color: '#999',
+                    },
+                },
+            },
+            legend: {
+                show: false,
+                orient: 'vertical',
+                left: 'center',
+                bottom: 'bottom',
+                // padding: [10, 0, 0, 0],
+                data: legendData,
+                //图例滚动
+                // type: 'scroll',
+                //图例文字样式
+                textStyle: {
+                    color: 'rgba(36, 173, 254, 0.7)',
+                    fontSize: '1rem',
+                },
+            },
+            xAxis: [
+                {
+                    type: 'category',
+                    axisTick: {
+                        show: false,
+                    },
+                    interval: 1,
+                    axisLabel: {
+                        color: '#999',
+                    },
+                    axisLine: {
+                        show: false,
+                    },
+                    data: xData,
+                },
+            ],
+            yAxis: [
+                {
+                    type: 'value',
+                    name: '',
+                    min: 0,
+                    interval: 20,
+                    axisLabel: {
+                        formatter: '{value}',
+                    },
+                    axisLabel: {
+                        textStyle: {
+                            //坐标轴颜色
+                            color: 'rgba(189, 195, 199,0.5)',
+                            fontSize: '16px',
+                        },
+                    },
+                    //坐标轴线样式
+                    // splitLine: {
+                    //     show: true,
+                    //     lineStyle: {
+                    //         type: 'solid',
+                    //         color: 'rgba(189, 195, 199,0.5)',
+                    //     },
+                    // },
+                    splitLine: {
+                        lineStyle: {
+                            color: 'rgba(189, 195, 199,0.5)',
+                            type: [5, 8],
+                            dashOffset: 3,
+                        },
+                    },
+                },
+            ],
+            dataZoom: [
+                {
+                    show: true,
+                    height: 10,
+                    xAxisIndex: [0, 1],
+                    bottom: 10,
+                    right: '8%',
+                    left: '5%',
+                    start: 0,
+                    end: 30,
+                    backgroundColor: 'rgba(0,0,0,0)',
+                    handleIcon: 'path://M306.1,413c0,2.2-1.8,4-1,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z',
+                    handleSize: '100%',
+                    handleStyle: {
+                        color: '#999999',
+                    },
+                    textStyle: {
+                        color: '#999999',
+                    },
+                    borderColor: '',
+                    zoomOnMouseWheel: false,
+                    moveOnMouseMove: true,
+                    moveOnMouseWheel: true,
+                },
+                {
+                    type: 'inside',
+                    show: true,
+                    height: 10,
+                    start: 1,
+                    end: 100,
+                },
+            ],
+            series: [
+                {
+                    type: 'scatter',
+                    symbolSize: 8,
+                    itemStyle: {
+                        color: '#3498db',
+                    },
+                    silent: true,
+                    tooltip: {
+                        show: false,
+                    },
+                    markPoint: {
+                        data: [{ type: 'max', name: 'Max' }],
+                        label: {
+                            color: '#FFF', // 文字颜色
+                            padding: [0, 0, 5, 0], // 可用padding调整图片内文字距离
+                            show: true,
+                        },
+                    },
+                    data: firstIndustry,
+                },
+                {
+                    name: '在线趋势',
+                    data: firstIndustry,
+                    type: 'line',
+                    smooth: true, //true曲线; false折线
+                    itemStyle: {
+                        normal: {
+                            color: 'rgba(36, 173, 254, 1)', //改变折线点的颜色
+                            lineStyle: {
+                                color: 'rgba(36, 173, 254, .5)', //改变折线颜色
+                                type: 'solid',
+                            },
+                        },
+                    },
+                    areaStyle: {
+                        //折线图颜色半透明
+                        color: {
+                            type: 'linear',
+                            x: 0,
+                            y: 0,
+                            x2: 0,
+                            y2: 1,
+                            colorStops: [
+                                {
+                                    offset: 0,
+                                    color: 'rgba(36, 173, 254, 1)', // 0% 处的颜色
+                                },
+                                {
+                                    offset: 1,
+                                    color: 'rgba(36, 173, 254, .1)', // 100% 处的颜色
+                                },
+                            ],
+                            global: false, // 缺省为 false
+                        },
+                    },
+                },
+            ],
+        })
+        window.addEventListener('resize', () => {
+            this.state.chart.resize()
+        })
+    }
+
+    destory() {
+        this.state.chart.dispose()
+        window.removeEventListener('resize', () => {
+            console.log('事件移除')
+        })
+    }
+}
+</script>
+<style lang="less"></style>
+

+ 164 - 0
TEAMModelBI/ClientApp/src/components/echarts/test/onlineType.vue

@@ -0,0 +1,164 @@
+<template>
+    <div ref="myEcharts" :style="{ height, width }"></div>
+</template>
+<script>
+import { ref, onMounted, nextTick, watch, getCurrentInstance } from 'vue'
+import * as echarts from 'echarts'
+export default {
+    props: {
+        width: {
+            type: String,
+            default: '100%',
+        },
+        height: {
+            type: String,
+            default: '100%',
+        },
+        lineData: {
+            type: Array,
+            default: () => {},
+        },
+        title: {
+            type: String,
+            default: '',
+        },
+    },
+    setup(props) {
+        const myEcharts = ref(null)
+        let { proxy } = getCurrentInstance()
+        const chart = new InitChart(props, myEcharts)
+        onMounted(() => {
+            chart.init(props.lineData, proxy)
+        })
+        watch(
+            props,
+            (nweProps) => {
+                nextTick(() => {
+                    nweProps ? chart.init(props.lineData, proxy) : ''
+                })
+            },
+            { immediate: true, deep: true }
+        )
+        return {
+            myEcharts,
+        }
+    },
+}
+class InitChart {
+    constructor(props, myEcharts) {
+        this.props = props
+        this.myEcharts = myEcharts
+        this.state = {
+            chart: null,
+        }
+    }
+    init(datas, proxy) {
+        console.log(datas, 'line调用')
+        let xData = [
+            '0:00',
+            '1:00',
+            '2:00',
+            '3:00',
+            '4:00',
+            '5:00',
+            '6:00',
+            '7:00',
+            '8:00',
+            '9:00',
+            '10:00',
+            '11:00',
+            '12:00',
+            '13:00',
+            '14:00',
+            '15:00',
+            '16:00',
+            '17:00',
+            '18:00',
+            '19:00',
+            '20:00',
+            '21:00',
+            '22:00',
+            '23:00',
+            '24:00',
+        ]
+        let firstIndustry = [72, 37, 4, 75, 105, 36, 0, 15, 30, 45, 88]
+        this.state.chart && this.destory()
+        this.state.chart = echarts.init(this.myEcharts.value)
+        this.state.chart.setOption({
+            // 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: 'rgba(189, 195, 199,0.5)' } },
+                axisTick: { length: 3 },
+                axisLabel: { color: 'rgba(189, 195, 199,0.5)' },
+            },
+            yAxis: {
+                type: 'value',
+                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',
+                axisPointer: { type: 'shadow' },
+                textStyle: { color: '#424242' },
+                renderMode: 'html',
+                //className: 'tooltip',
+                // order: 'seriesDesc',
+            },
+            dataZoom: [
+                {
+                    show: true,
+                    height: 10,
+                    xAxisIndex: [0, 1],
+                    bottom: 10,
+                    right: '8%',
+                    left: '5%',
+                    start: 0,
+                    end: 30,
+                    backgroundColor: 'rgba(0,0,0,0)',
+                    handleIcon: 'path://M306.1,413c0,2.2-1.8,4-1,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z',
+                    handleSize: '100%',
+                    handleStyle: {
+                        color: '#999999',
+                    },
+                    textStyle: {
+                        color: '#999999',
+                    },
+                    borderColor: '',
+                    zoomOnMouseWheel: false,
+                    moveOnMouseMove: true,
+                    moveOnMouseWheel: true,
+                },
+                {
+                    type: 'inside',
+                    show: true,
+                    height: 10,
+                    start: 1,
+                    end: 100,
+                },
+            ],
+            color: ['#62F4D1', '#52A8FF', '#006EFE', '#A3D7FF'],
+            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] },
+            ],
+        })
+        window.addEventListener('resize', () => {
+            this.state.chart.resize()
+        })
+    }
+
+    destory() {
+        this.state.chart.dispose()
+        window.removeEventListener('resize', () => {
+            console.log('事件移除')
+        })
+    }
+}
+</script>
+<style lang="less"></style>
+

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

@@ -0,0 +1,132 @@
+<template>
+    <div ref="myEcharts" :style="{ height, width }"></div>
+</template>
+<script>
+import { ref, onMounted, nextTick, watch, getCurrentInstance } from 'vue'
+import * as echarts from 'echarts'
+export default {
+    props: {
+        width: {
+            type: String,
+            default: '100%',
+        },
+        height: {
+            type: String,
+            default: '100%',
+        },
+        lineData: {
+            type: Array,
+            default: () => {},
+        },
+        sizenum: {
+            type: Array,
+            default: () => {},
+        },
+        title: {
+            type: String,
+            default: '',
+        },
+    },
+    setup(props) {
+        const myEcharts = ref(null)
+        let { proxy } = getCurrentInstance()
+        const chart = new InitChart(props, myEcharts)
+        onMounted(() => {
+            chart.init(props.lineData, proxy, props.sizenum)
+        })
+        watch(
+            props,
+            (nweProps) => {
+                nextTick(() => {
+                    nweProps ? chart.init(props.lineData, proxy, props.sizenum) : ''
+                })
+            },
+            { immediate: true, deep: true }
+        )
+        return {
+            myEcharts,
+        }
+    },
+}
+class InitChart {
+    constructor(props, myEcharts) {
+        this.props = props
+        this.myEcharts = myEcharts
+        this.state = {
+            chart: null,
+        }
+    }
+    init(datas, proxy, sizenum) {
+        console.log(datas, sizenum, 'lineSIze调用')
+        this.state.chart && this.destory()
+        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',
+            title: {
+                text: ['{a|总空间:}', `{b|${sizenum.all}G}` + '\n\n' + '{c|已使用:}', `{d|${sizenum.usesize}G}` + '\n\n' + `{e|剩余空间:}`, `{f|${sizenum.residue}G}`].join(''),
+                top: '35%',
+                textAlign: 'center',
+                left: '50%',
+                textStyle: {
+                    // color: ' #6A7474',
+                    // fontSize: 20,
+                    // fontWeight: '600',
+                    // lineHeight: 60,
+                    rich: {
+                        a: {
+                            color: '#bdc3c7',
+                            fontWeight: 'bold',
+                            fontSize: 18,
+                        },
+                        b: {
+                            color: '#3498db',
+                            fontWeight: 'bold',
+                            fontSize: 18,
+                        },
+                        c: {
+                            color: '#bdc3c7',
+                            fontWeight: 'bold',
+                            fontSize: 18,
+                        },
+                        d: {
+                            color: '#2ecc71',
+                            fontWeight: 'bold',
+                            fontSize: 18,
+                        },
+                        e: {
+                            color: '#bdc3c7',
+                            fontWeight: 'bold',
+                            fontSize: 18,
+                        },
+                        f: {
+                            color: '#95a5a6',
+                            fontWeight: 'bold',
+                            fontSize: 18,
+                        },
+                    },
+                },
+            },
+            tooltip: {
+                show: true,
+                trigger: 'item',
+                formatter: '{a} <br/>{b}: {c}G',
+            },
+            legend: datas.legend ? datas.legend : {},
+            series: datas.series ? datas.series : [],
+        })
+        window.addEventListener('resize', () => {
+            this.state.chart.resize()
+        })
+    }
+
+    destory() {
+        this.state.chart.dispose()
+        window.removeEventListener('resize', () => {
+            console.log('事件移除')
+        })
+    }
+}
+</script>
+<style lang="less"></style>
+

+ 234 - 0
TEAMModelBI/ClientApp/src/components/echarts/testLine.vue

@@ -0,0 +1,234 @@
+<!--基础折线图-->
+<template>
+    <div ref="myEcharts" :style="{ height, width }"></div>
+</template>
+<script>
+import { ref, onMounted, nextTick, watch, getCurrentInstance } from 'vue'
+import * as echarts from 'echarts'
+export default {
+    props: {
+        width: {
+            type: String,
+            default: '100%',
+        },
+        height: {
+            type: String,
+            default: '100%',
+        },
+        meterData: {
+            type: Object,
+            default: () => {},
+        },
+        title: {
+            type: String,
+            default: '',
+        },
+    },
+    setup(props) {
+        const myEcharts = ref(null)
+        let { proxy } = getCurrentInstance()
+        const chart = new InitChart(props, myEcharts)
+        onMounted(() => {
+            chart.init(props.barData, proxy)
+        })
+        watch(
+            props,
+            (nweProps) => {
+                nextTick(() => {
+                    nweProps ? chart.init(props.barData, proxy) : ''
+                })
+            },
+            { immediate: true, deep: true }
+        )
+        return {
+            myEcharts,
+        }
+    },
+}
+class InitChart {
+    constructor(props, myEcharts) {
+        this.props = props
+        this.myEcharts = myEcharts
+        this.state = {
+            chart: null,
+        }
+    }
+    init(datas, proxy) {
+        console.log(datas, '柱状图的调用')
+        var value = 45
+        var pointerColor = new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+            {
+                offset: 0,
+                color: '#2EE5E3',
+            },
+            {
+                offset: 1,
+                color: '#385CF7',
+            },
+        ])
+        var color = new echarts.graphic.LinearGradient(0, 0, 1, 0, [
+            {
+                offset: 0,
+                color: '#2EE5E3',
+            },
+            {
+                offset: 1,
+                color: '#385CF7',
+            },
+        ])
+        this.state.chart && this.destory()
+        this.state.chart = echarts.init(this.myEcharts.value)
+        this.state.chart.setOption({
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'cross',
+                    crossStyle: {
+                        color: '#999',
+                    },
+                    lineStyle: {
+                        type: 'dashed',
+                    },
+                },
+            },
+            grid: {
+                top: '25%',
+                left: '2%',
+                bottom: '5%',
+                right: '8%',
+                textStyle: {
+                    color: '#fff',
+                },
+                containLabel: true,
+            },
+            legend: {
+                show: true,
+                // data: ['2022.01', '2022.02', '2022.03', '2022.04', '2022.05'],
+                textStyle: {
+                    color: '#000',
+                },
+                right: '8%',
+                top: '10%',
+            },
+            xAxis: {
+                type: 'category',
+                data: ['2022.01', '2022.02', '2022.03', '2022.04', '2022.05'],
+                boundaryGap: false,
+            },
+            yAxis: {
+                type: 'value',
+            },
+            series: [
+                {
+                    name: '课例',
+                    type: 'line',
+                    symbolSize: 8,
+                    data: [100, 88, 80, 70, 63],
+                    areaStyle: {
+                        normal: {
+                            color: new echarts.graphic.LinearGradient(
+                                0,
+                                0,
+                                0,
+                                1,
+                                [
+                                    {
+                                        offset: 0,
+                                        color: 'rgba(112, 103, 245, 0.3)',
+                                    },
+                                    {
+                                        offset: 1,
+                                        color: '#fff',
+                                    },
+                                ],
+                                false
+                            ),
+                        },
+                    },
+                    itemStyle: {
+                        normal: {
+                            color: 'rgba(112, 103, 245, 0.3)',
+                        },
+                    },
+                },
+                {
+                    name: '活动',
+                    type: 'line',
+                    symbolSize: 8,
+                    data: [43, 24, 22, 20, 10],
+                    areaStyle: {
+                        normal: {
+                            color: new echarts.graphic.LinearGradient(
+                                0,
+                                0,
+                                0,
+                                1,
+                                [
+                                    {
+                                        offset: 0,
+                                        color: 'rgba(255, 117, 26, 0.3)',
+                                    },
+                                    {
+                                        offset: 1,
+                                        color: '#fff',
+                                    },
+                                ],
+                                false
+                            ),
+                        },
+                    },
+                    itemStyle: {
+                        normal: {
+                            color: 'rgba(255, 117, 26, 0.3)',
+                        },
+                    },
+                },
+                {
+                    name: '互动',
+                    type: 'line',
+                    symbolSize: 8,
+                    data: [88, 80, 63, 20, 10],
+                    areaStyle: {
+                        normal: {
+                            color: new echarts.graphic.LinearGradient(
+                                0,
+                                0,
+                                0,
+                                1,
+                                [
+                                    {
+                                        offset: 0,
+                                        color: 'rgba(29, 227, 255, 0.3)',
+                                    },
+                                    {
+                                        offset: 1,
+                                        color: '#fff',
+                                    },
+                                ],
+                                false
+                            ),
+                        },
+                    },
+                    itemStyle: {
+                        normal: {
+                            color: 'rgba(29, 227, 255, 0.3)',
+                        },
+                    },
+                },
+            ],
+        })
+        window.addEventListener('resize', () => {
+            this.state.chart.resize()
+        })
+    }
+
+    destory() {
+        this.state.chart.dispose()
+        window.removeEventListener('resize', () => {
+            console.log('事件移除')
+        })
+    }
+}
+</script>
+<style lang="less"></style>
+

+ 46 - 1
TEAMModelBI/ClientApp/src/router/index.js

@@ -26,7 +26,7 @@ const routes = [{
                 permission: "teacher-read|teacher-upd",
                 roles: ['all'],
                 isShow: true,
-                component: () => require.ensure([], (require) => require(`@/view/index/dashboard.vue`))
+                component: () => require.ensure([], (require) => require(`@/view/index/index.vue`))
             },
             //系统配置
             {
@@ -109,6 +109,51 @@ const routes = [{
                 isShow: true,
                 component: () => require.ensure([], (require) => require(`@/view/index/operateLog.vue`))
             },
+            //单个学校数据统计
+            {
+                name: "analyse",
+                path: "analyse",
+                permission: "batcharea-read|batcharea-upd|batchschool-read|batchschool-upd",
+                roles: ['admin'],
+                isShow: true,
+                component: () => require.ensure([], (require) => require(`@/view/schoolServe/analyseSchool.vue`))
+            },
+            //区校掌握(学区情况)(admin assist)
+            {
+                name: "district",
+                path: "district",
+                permission: "batcharea-read|batcharea-upd|batchschool-read|batchschool-upd",
+                roles: ['admin', 'assist'],
+                isShow: true,
+                component: () => require.ensure([], (require) => require(`@/view/areamanage/index.vue`))
+            },
+            //区校掌握(学校情况)(admin assist)
+            {
+                name: "campus",
+                path: "campus",
+                permission: "batcharea-read|batcharea-upd|batchschool-read|batchschool-upd",
+                roles: ['admin', 'assist'],
+                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: {},

+ 34 - 139
TEAMModelBI/ClientApp/src/view/areaServe/areamanage.vue

@@ -30,7 +30,7 @@
                 </div>
             </div>
             <div class="boxselect" @click="createArea" v-if="PowerShow">
-                <a>
+                <!-- <a>
                     <div class="layer">
                         <span></span>
                         <span></span>
@@ -43,16 +43,22 @@
                         </span>
                     </div>
                     <span class="text">{{$t(`areaManages.found.creation`)}}</span>
-                </a>
+                </a> -->
+                <el-button size="small" color="#81ecec">
+                    <svg class="created-icon" aria-hidden="true">
+                        <use xlink:href="#icon-chuangjiandizhisuoyin"></use>
+                    </svg>
+                    创建学区
+                </el-button>
             </div>
         </div>
         <div class="traitfrom">
             <el-table :data="optionData" style="width: 100%" :highlight-current-row="true" height="74vh" v-loading="loading" :empty-text='$t(`commonMsg.nodataTable`)'>
                 <el-table-column prop="index" :label="$t(`areaManages.areaTable.serialnum`)" type=index sortable align="center" />
                 <el-table-column prop="name" :label="$t(`areaManages.areaTable.name`)" align="center" />
-                <el-table-column prop="id" :label="$t(`areaManages.areaTable.Ids`)" align="center" />
-                <el-table-column prop="provName" :label="$t(`areaManages.areaTable.province`)" align="center" />
-                <el-table-column prop="cityName" :label="$t(`areaManages.areaTable.city`)" align="center" />
+                <el-table-column prop="schoolCount" label="区内学校数量" align="center" sortable :sort-method="areaSort" />
+                <el-table-column prop="location" label="位置" align="center" />
+                <!-- <el-table-column prop="cityName" :label="$t(`areaManages.areaTable.city`)" align="center" /> -->
                 <el-table-column prop="institution" :label="$t(`areaManages.areaTable.affiliation`)" align="center" />
                 <el-table-column prop="standardName" :label="$t(`areaManages.areaTable.project`)" align="center" />
                 <el-table-column :label="$t(`areaManages.areaTable.operate`)" align="center" v-if="PowerShow">
@@ -264,6 +270,11 @@ export default {
         //获取
         function getoption() {
             proxy.$api.getCapacity({}).then((res) => {
+                console.log(res)
+                for (let i in res.areas) {
+                    res.areas[i].location = res.areas[i].provName + res.areas[i].cityName
+                }
+
                 optionData.value.push(...res.areas)
                 loading.value = false
                 store.commit('getPoint', res.areas)
@@ -459,6 +470,11 @@ export default {
             optionData.value = originalDatas.value
             provinceOptions.value.optionInfo = optionsData
         }
+        function areaSort(al1, val2) {
+            optionData.value.sort(function (a, b) {
+                return b.schoolCount - a.schoolCount
+            })
+        }
         watch(abilityModel, (newdata) => {
             console.log(newdata)
             newdata
@@ -500,6 +516,7 @@ export default {
             areaSelctChange,
             originalDatas,
             closeSelectarea,
+            areaSort,
         }
     },
 }
@@ -508,144 +525,14 @@ export default {
 .areamanabox {
     width: 100%;
     height: calc(100% - 100px);
-    background-color: #fff;
+    /* background-color: #fff; */
     padding: 5px 10px;
 }
 .boxselect {
-    width: 60px;
-    height: 60px;
+    width: 20%;
     display: inline-block;
-    margin-top: 0%;
-    margin-right: 2%;
-    margin-bottom: 1%;
-}
-.boxselect:hover {
-    cursor: pointer;
-}
-.boxselect a {
-    text-decoration: none;
-    color: #fff;
-    display: block;
-    position: relative;
-}
-
-.boxselect a .layer {
-    width: 60px;
-    height: 60px;
-    transition: transform 0.3s;
-    margin-top: 37%;
-}
-
-.boxselect a:hover .layer {
-    transform: rotate(-25deg) skew(20deg);
-}
-
-.boxselect a .layer span {
-    position: absolute;
-    top: 0;
-    left: 0;
-    height: 100%;
-    width: 100%;
-    border: 1px solid #fff;
-    border-radius: 5px;
-    transition: all 0.3s;
-}
-
-.boxselect a .layer span.fab {
-    font-size: 36px;
-    line-height: 180px;
-    text-align: center;
-}
-.boxselect a:hover .layer span:nth-child(1) {
-    opacity: 0.3;
-}
-
-.boxselect a:hover .layer span:nth-child(2) {
-    opacity: 0.4;
-    transform: translate(5px, -5px);
-}
-
-.boxselect a:hover .layer span:nth-child(3) {
-    opacity: 0.6;
-    transform: translate(10px, -10px);
-}
-
-.boxselect a:hover .layer span:nth-child(4) {
-    opacity: 0.8;
-    transform: translate(15px, -15px);
-}
-
-.boxselect a:hover .layer span:nth-child(5) {
-    opacity: 1;
-    transform: translate(20px, -20px);
-}
-
-.boxselect a:nth-child(1) .layer span,
-.boxselect a:nth-child(1) .text {
-    color: #4267b2;
-    border-color: #4267b2;
-}
-
-.boxselect a:nth-child(2) .layer span,
-.boxselect a:nth-child(2) .text {
-    color: #1da1f2;
-    border-color: #1da1f2;
-}
-
-.boxselect a:nth-child(3) .layer span,
-.boxselect a:nth-child(3) .text {
-    color: #e1306c;
-    border-color: #e1306c;
-}
-
-.boxselect a:nth-child(4) .layer span,
-.boxselect a:nth-child(4) .text {
-    color: #2867b2;
-    border-color: #2867b2;
-}
-
-.boxselect a:nth-child(5) .layer span,
-.boxselect a:nth-child(5) .text {
-    color: #ff0000;
-    border-color: #ff0000;
-}
-
-.boxselect a:nth-child(1) .layer span {
-    box-shadow: -1px 1px 3px #4267b2;
-}
-
-.boxselect a:nth-child(2) .layer span {
-    box-shadow: -1px 1px 3px #1da1f2;
-}
-
-.boxselect a:nth-child(3) .layer span {
-    box-shadow: -1px 1px 3px #e1306c;
-}
-
-.boxselect a:nth-child(4) .layer span {
-    box-shadow: -1px 1px 3px #4267b2;
-}
-
-.boxselect a:nth-child(5) .layer span {
-    box-shadow: -1px 1px 3px #ff0000;
-}
-
-.boxselect a .text {
-    position: absolute;
-    width: 100%;
-    left: 50%;
-    /* bottom: -5px; */
-    opacity: 0;
-    transform: translateX(-50%);
-    transition: bottom 0.3s ease, opacity 0.3s ease;
-    font-size: 12px;
-    font-weight: 600;
-    top: 90%;
-}
-
-.boxselect a:hover .text {
-    bottom: -25px;
-    opacity: 1;
+    margin-top: 1.5%;
+    padding-right: 1%;
 }
 .created-areaicon {
     width: 1.3em;
@@ -876,6 +763,14 @@ export default {
     font-size: 16px;
     color: #1da1f2;
 }
+.created-icon {
+    width: 1.3em;
+    height: 1.3em;
+    vertical-align: -0.3em;
+    fill: currentColor;
+    overflow: hidden;
+    margin-right: 5px;
+}
 </style>
 <style>
 .traitfrom .refer {

+ 4 - 6
TEAMModelBI/ClientApp/src/view/areaServe/index.vue

@@ -1,13 +1,14 @@
 <template>
     <div class="area-servebox">
-        <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
+        <!-- <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
             <el-tab-pane label="数据总览" name="statistics">
                 <Statistics></Statistics>
             </el-tab-pane>
-            <el-tab-pane label="详细数据" name="detail">
+            <el-tab-pane label="学区列表" name="detail">
                 <AreaManage></AreaManage>
             </el-tab-pane>
-        </el-tabs>
+        </el-tabs> -->
+        <AreaManage></AreaManage>
     </div>
 </template>
 <script>
@@ -29,9 +30,6 @@ export default {
 }
 </script>
 <style scoped>
-.area-servebox {
-    background-color: #f7f7f7;
-}
 </style>
 <style>
 .area-servebox .demo-tabs .el-tabs__header {

+ 215 - 32
TEAMModelBI/ClientApp/src/view/areaServe/statistics.vue

@@ -11,18 +11,26 @@
                     </div>
                     <div class="right-top-text">
                         <p class="right-top-num">{{item.num}}<span v-if="item.classname ==='size'">GB</span></p>
-                        <p class="right-top-title">{{item.title}}</p>
+                        <div class="right-top-bottombox">
+                            <div class="right-top-bottombox-title">
+                                <p class="right-top-title">{{item.title}}</p>
+                            </div>
+                            <div class="right-top-bottombox-comparison">
+                                <p>本周新增:<span class="week-comparison">3</span></p>
+                                <p>本月新增:<span class="months-comparison">15</span></p>
+                            </div>
+                        </div>
                     </div>
                 </div>
                 <div class="basicsbox">
                     <p class="commonbox-title">基础数据</p>
-                    <div class="leftbox">
+                    <div class="leftbox" v-loading="loadingTotal.basics" element-loading-background="rgba(0, 0, 0, 0.3)">
                         <div :class="[items.type==='month' ? 'alonebox' :'totalalonebox']" v-for="(items,indexs) in activityData.total" :key="indexs">
                             <p :class="[items.type ==='month' ? 'alonebox-title':'total-alonebox-title']">{{items.title}}</p>
                             <p :class="[items.type ==='month' ? 'alonebox-content':'total-alonebox-content']">{{items.num}}</p>
                         </div>
                     </div>
-                    <div class="rightbox">
+                    <div class="rightbox" v-loading="loadingTotal.basics2" element-loading-background="rgba(0, 0, 0, 0.3)">
                         <div class="rightbox-pie">
                             <CommonPie :proportionData="totalArea.alonePie"></CommonPie>
                         </div>
@@ -37,22 +45,22 @@
             </div>
             <div class="center-resource">
                 <p class="commonbox-title areaClass">课例活动数据</p>
-                <div class="center-resource-left">
+                <div class="center-resource-left" v-loading="loadingTotal.class" element-loading-background="rgba(0, 0, 0, 0.3)">
                     <ConventionPie :pieData="totalArea.class"></ConventionPie>
                 </div>
-                <div class="center-resource-right">
+                <div class="center-resource-right" v-loading="loadingTotal.classAndactivity" element-loading-background="rgba(0, 0, 0, 0.3)">
                     <CommonBar :barData="totalArea.classAndactivity"></CommonBar>
                 </div>
             </div>
             <div class="bottom-resource">
                 <p class="commonbox-title active">课例活跃</p>
-                <div class="areabottom-resource">
+                <div class="areabottom-resource" v-loading="loadingTotal.classLine" element-loading-background="rgba(0, 0, 0, 0.3)">
                     <CommonLine :lineData="totalArea.dynamic"></CommonLine>
                 </div>
             </div>
             <div class="areaList">
                 <p class="commonbox-title arealists">区域列表</p>
-                <div class="area-listinfo">
+                <div class="area-listinfo" v-loading="loadingTotal.areaList" element-loading-background="rgba(0, 0, 0, 0.3)">
                     <div class="area-item" v-for="(item,index) in areaLists" :key="item.id">
                         <div class="area-item-list">
                             <p class="area-item-name">{{item.name}}</p>
@@ -95,7 +103,16 @@
                     </div>
                     <div class="right-top-text">
                         <p class="right-top-num">{{item.num}}<span v-if="item.classname ==='size'">GB</span></p>
-                        <p class="right-top-title">{{item.title}}</p>
+                        <!-- <p class="right-top-title">{{item.title}}</p> -->
+                        <div class="right-top-bottombox">
+                            <div class="right-top-bottombox-title">
+                                <p class="right-top-title">{{item.title}}</p>
+                            </div>
+                            <div class="right-top-bottombox-comparison">
+                                <p>本周新增:<span class="week-comparison">3</span></p>
+                                <p>本月新增:<span class="months-comparison">15</span></p>
+                            </div>
+                        </div>
                     </div>
                 </div>
                 <div class="leftbox">
@@ -127,7 +144,21 @@
                     <div class="tagbox ">
                         <p class="tags source"><span>课例数据</span></p>
                     </div>
-                    <ConventionPie :pieData="areaData.class"></ConventionPie>
+                    <div class="center-resource-left-leftEcharts">
+                        <div class="echarts-title">总数据:</div>
+                        <ConventionPie :pieData="areaData.class"></ConventionPie>
+                    </div>
+                    <div class="center-resource-left-rightbox">
+                        <div class="week-echart">
+                            <div class="echarts-title-pie">上周:</div>
+                            <ConventionPie :pieData="areaData.lastweek"></ConventionPie>
+                        </div>
+                        <div class="month-echart">
+                            <div class="echarts-title-pie">本月:</div>
+                            <ConventionPie :pieData="areaData.monthsContrast"></ConventionPie>
+                        </div>
+                    </div>
+
                 </div>
                 <div class="center-resource-right">
                     <div class="tagbox">
@@ -222,7 +253,7 @@ export default {
         })
         let areaAspectsData = ref([
             { id: 1, title: '学区总数', num: 0, icon: '#icon-quyugongsi', classname: 'school' },
-            { id: 2, title: '学区校数', num: 0, icon: '#icon-all-school', classname: 'months' },
+            { id: 2, title: '学区校数', num: 0, icon: '#icon-all-school', classname: 'months' },
             { id: 3, title: '学区老师数', num: 0, icon: '#icon-laoshi1', classname: 'teach' },
             { id: 4, title: '产出总数据', num: 0, icon: '#icon-shujutongji', classname: 'datas' },
             { id: 5, title: '空间总容量', num: 0, icon: '#icon-kongjian', classname: 'size' },
@@ -240,6 +271,14 @@ export default {
             teacherCount: 0,
             studentCount: 0,
         })
+        let loadingTotal = ref({
+            basics: true,
+            basics2: true,
+            class: true,
+            classAndactivity: true,
+            classLine: true,
+            areaList: true,
+        })
         //总区域echarts需要数据
         let totalArea = ref({
             //单独的饼图
@@ -503,7 +542,7 @@ export default {
                         name: '课例',
                         type: 'bar',
                         stack: 'account',
-                        barWidth: 26,
+                        barWidth: 18,
                         itemStyle: {
                             color: {
                                 x: 0,
@@ -530,7 +569,7 @@ export default {
                         name: '活动',
                         type: 'bar',
                         stack: 'account',
-                        barWidth: 26,
+                        barWidth: 18,
                         itemStyle: {
                             color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                                 { offset: 0, color: '#ffae88' },
@@ -889,7 +928,7 @@ export default {
             },
             //课例数据
             class: {
-                color: ['#ef5777', '#0fbcf9', '#0be881', '#575fcf', '#ffd32a'],
+                color: ['#3fb1e3', '#6be6c1', '#626c91', '#a0a7e6', '#c4ebad', '#96dee8'],
                 legend: {
                     right: '4%',
                     orient: 'vertical',
@@ -904,8 +943,8 @@ export default {
                     {
                         name: '学校数据占比',
                         type: 'pie',
-                        radius: '85%',
-                        center: ['30%', '50%'],
+                        radius: '75%',
+                        center: ['40%', '50%'],
                         itemStyle: {
                             borderRadius: 2,
                         },
@@ -925,6 +964,80 @@ export default {
                     },
                 ],
             },
+            //上周课例对比
+            lastweek: {
+                color: ['#3fb1e3', '#6be6c1', '#626c91', '#a0a7e6', '#c4ebad', '#96dee8'],
+                legend: {
+                    right: '4%',
+                    orient: 'vertical',
+                    itemWidth: 8,
+                    itemHeight: 8, // 修改icon图形大小
+                    textStyle: {
+                        fontSize: 12,
+                        color: '#333',
+                    },
+                },
+                series: [
+                    {
+                        name: '学校数据占比',
+                        type: 'pie',
+                        radius: '75%',
+                        center: ['40%', '50%'],
+                        itemStyle: {
+                            borderRadius: 2,
+                        },
+                        label: {
+                            normal: {
+                                position: 'inner',
+                                show: false,
+                            },
+                        },
+                        data: [
+                            // { value: 1340, name: '锦江区外国语小学' },
+                            // { value: 380, name: '川师大附小' },
+                            { value: 58, name: '树德中学' },
+                            { value: 36, name: '石室中学' },
+                            { value: 100, name: '石室小学' },
+                        ],
+                    },
+                ],
+            },
+            //本月课例对比
+            monthsContrast: {
+                color: ['#3fb1e3', '#6be6c1', '#626c91', '#a0a7e6', '#c4ebad', '#96dee8'],
+                legend: {
+                    right: '4%',
+                    orient: 'vertical',
+                    itemWidth: 8,
+                    itemHeight: 8, // 修改icon图形大小
+                    textStyle: {
+                        fontSize: 12,
+                        color: '#333',
+                    },
+                },
+                series: [
+                    {
+                        name: '学校数据占比',
+                        type: 'pie',
+                        radius: '75%',
+                        center: ['40%', '50%'],
+                        itemStyle: {
+                            borderRadius: 2,
+                        },
+                        label: {
+                            normal: {
+                                position: 'inner',
+                                show: false,
+                            },
+                        },
+                        data: [
+                            { value: 140, name: '锦江区外国语小学' },
+                            { value: 380, name: '川师大附小' },
+                            { value: 100, name: '石室小学' },
+                        ],
+                    },
+                ],
+            },
             //课例活跃度
             dynamic: {
                 backgroundColor: '#fff',
@@ -1063,7 +1176,7 @@ export default {
             },
             //研修进度
             schedule: {
-                color: ['#55E6C1 ', '#25CCF7', '#F97F51', '#ff7675', '#5352ed', '#D6A2E8'],
+                color: ['#3fb1e3', '#6be6c1', '#626c91', '#a0a7e6', '#c4ebad', '#96dee8'],
                 data: [
                     { value: 9, name: '已完成' },
                     { value: 33, name: '进行中' },
@@ -1189,7 +1302,8 @@ export default {
             },
             //空间使用类型占比
             sizeProportion: {
-                color: ['#05f8d6', '#0082fc', '#fdd845', '#22ed7c', '#09b0d3', '#1d27c9', '#f9e264', '#f47a75', '#009db2', '#024b51', '#0780cf', '#765005'],
+                // color: ['#05f8d6', '#0082fc', '#fdd845', '#22ed7c', '#09b0d3', '#1d27c9', '#f9e264', '#f47a75', '#009db2', '#024b51', '#0780cf', '#765005'],
+                color: ['#3fb1e3', '#6be6c1', '#626c91', '#a0a7e6', '#c4ebad', '#96dee8', '#05f8d6', '#09b0d3', '#765005', '#22ed7c', '#fdd845', '#0082fc'],
                 legend: {
                     right: '1%',
                     orient: 'vertical',
@@ -1222,11 +1336,11 @@ export default {
         })
         //获取全区的数据(基础数据)
         function getAll() {
-            let loading = ElLoading.service({
-                lock: true,
-                text: '正在努力加载,请稍等...',
-                background: 'rgba(0, 0, 0, 0.8)',
-            })
+            // let loading = ElLoading.service({
+            //     lock: true,
+            //     text: '正在努力加载,请稍等...',
+            //     background: 'rgba(0, 0, 0, 0.8)',
+            // })
             proxy.$api
                 .getAlldata()
                 .then((res) => {
@@ -1250,8 +1364,8 @@ export default {
                             teachnum += res.areaInfos[i].techCount
                         }
                         areaAspectsData.value[0].num = res.areaInfos.length
-                        areaAspectsData.value[1].num = schoolnum
-                        areaAspectsData.value[2].num = teachnum
+                        areaAspectsData.value[1].num = res.scCount
+                        areaAspectsData.value[2].num = res.tecCount
                         areaAspectsData.value[3].num = parseInt(res.allLess) + parseInt(res.allActivity)
                         areaAspectsData.value[4].num = res.allSize
                         //学校列表赋值
@@ -1265,7 +1379,11 @@ export default {
                         totalArea.value.versions.series[0].data[0].value = res.basics
                         totalArea.value.versions.series[0].data[1].value = res.standard
                         totalArea.value.versions.series[0].data[2].value = res.major
-                        getClassData(loading)
+
+                        loadingTotal.value.basics = false
+                        loadingTotal.value.basics2 = false
+                        loadingTotal.value.areaList = false
+                        getClassData()
                     }
                 })
                 .catch((res) => {
@@ -1293,7 +1411,10 @@ export default {
                         totalArea.value.classAndactivity.xAxis.data = nameData
                         totalArea.value.classAndactivity.series[0].data = activityAndclass.classData
                         totalArea.value.classAndactivity.series[1].data = activityAndclass.activityData
-                        getClassLivelys('', loading)
+
+                        loadingTotal.value.class = false
+                        loadingTotal.value.classAndactivity = false
+                        getClassLivelys('')
                     }
                 })
                 .catch((res) => {
@@ -1317,7 +1438,8 @@ export default {
                             totalArea.value.dynamic.series[0].data = res.weeks
                             totalArea.value.dynamic.series[1].data = res.weeks
                             totalArea.value.dynamic.xAxis.data = namedata
-                            loading.close()
+                            // loading.close()
+                            loadingTotal.value.classLine = false
                         } else {
                             areaData.value.dynamic.series[0].data = res.weeks
                             areaData.value.dynamic.series[1].data = res.weeks
@@ -1478,6 +1600,7 @@ export default {
             employSize,
             nowArea,
             researchData,
+            loadingTotal,
         }
     },
 }
@@ -1546,13 +1669,41 @@ export default {
     color: #ccc;
     font-size: 20px;
 }
-.right-top-title {
+.right-top-bottombox {
+    width: 100%;
     position: absolute;
+    bottom: 0%;
+    display: flex;
+    justify-content: space-between;
+    text-align: left;
+    line-height: 0px;
+}
+.right-top-bottombox-title,
+.right-top-bottombox-comparison {
+    width: 50%;
+    text-align: center;
+    color: #2c3e50;
+    font-size: 16px;
+}
+.right-top-bottombox-comparison {
+    font-size: 14px;
+    color: #95a5a6;
+    text-align: left;
+}
+.week-comparison {
+    color: #74b9ff;
+}
+.months-comparison {
+    color: #74b9ff;
+}
+.right-top-title {
+    /* position: absolute;
     font-size: 16px;
     bottom: 0%;
     left: 50%;
     transform: translate(-50%, 0%);
-    color: #2c3e50;
+    color: #2c3e50; */
+    margin-top: 5px;
 }
 .leftbox {
     width: 48%;
@@ -1653,21 +1804,22 @@ export default {
     position: relative;
 }
 .center-resource-left {
-    width: 30%;
+    width: 35%;
     height: 300px;
     position: relative;
     background: #fff;
     padding: 20px 10px;
+    display: flex;
+    justify-content: space-between;
 }
 .center-resource-right {
-    width: 67.5%;
+    width: 62.5%;
     margin-left: 2%;
     height: 300px;
     position: relative;
     background: #fff;
     padding: 25px 10px;
 }
-
 .versions {
     width: 35% !important;
 }
@@ -1839,6 +1991,32 @@ export default {
     color: #636e72;
 }
 
+.center-resource-left-leftEcharts,
+.center-resource-left-rightbox {
+    width: 50%;
+    height: 270px;
+    position: relative;
+}
+.week-echart,
+.month-echart {
+    width: 100%;
+    height: 135px;
+    position: relative;
+}
+.echarts-title {
+    position: absolute;
+    left: 0.5%;
+    top: 1.5%;
+    font-size: 16px;
+    color: #00a8ff;
+}
+.echarts-title-pie {
+    position: absolute;
+    left: 0.5%;
+    top: 0.5%;
+    font-size: 14px;
+    color: #7f8fa6;
+}
 .school,
 .basics {
     background: rgb(45, 140, 240);
@@ -1878,3 +2056,8 @@ export default {
     top: 3%;
 }
 </style>
+<style>
+.statisticsbox-all .el-loading-spinner .circular {
+    display: inline;
+}
+</style>

+ 293 - 0
TEAMModelBI/ClientApp/src/view/areamanage/areaAnalyse.vue

@@ -0,0 +1,293 @@
+<template>
+    <div class="area-analyse">
+        <div class="analyse-header">
+            <div class="analyse-header-basics">
+                <div class="analyse-header-basics-item" v-for="item in headerbasics" :key="item.id">
+                    <div class="items-basics">
+                        <p class="items-basics-title">{{item.title}}</p>
+                        <p class="items-basics-num">{{item.num}}</p>
+                        <p class="items-basics-increased">{{item.subheading}}:{{item.subnum}}</p>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="analyse-top">
+            <div class="analyse-top-left">
+                <div class="analyse-top-left-header"><span>双绿灯影片</span></div>
+                <div class="analyse-top-left-meter">
+                    <div class="meter-box">
+                        <CommonMeter></CommonMeter>
+                    </div>
+                </div>
+            </div>
+            <div class="analyse-top-center">
+                <div class="analyse-top-left-header"><span>学区影片占有率</span></div>
+                <div class="analyse-top-left-meter">
+                    <div class="meter-box">
+                        <CopeMeter></CopeMeter>
+                    </div>
+                </div>
+            </div>
+            <div class="analyse-top-right">
+                <div class="analyse-top-left-header"><span>本月学区数据排行</span></div>
+                <div class="analyse-top-left-ranking">
+                    <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
+                        <el-tab-pane label="醍摩豆智慧学区" name="first">
+                            <div class="analyse-top-right-table">
+                                <el-table :data="tableData" style="width: 100%" height="170">
+                                    <el-table-column label="排名" width="80px" align="center">
+                                        <template #default="scope">
+                                            <el-image style="width: 20px; height: 20px" :src="imgArr[0].url" fit="fill" v-if="scope.row.sort ===1 "></el-image>
+                                            <el-image style="width: 20px; height: 20px" :src="imgArr[1].url" fit="fill" v-else-if="scope.row.sort ===2 "></el-image>
+                                            <el-image style="width: 20px; height: 20px" :src="imgArr[2].url" fit="fill" v-else-if="scope.row.sort ===3 "></el-image>
+                                            <div v-else>{{scope.row.sort}}</div>
+                                        </template>
+                                    </el-table-column>
+                                    <el-table-column prop="name" label="名称" align="center" />
+                                    <el-table-column>
+                                        <div class="column"></div>
+                                    </el-table-column>
+                                    <el-table-column label="数据量">
+                                        <template #default="scope">
+                                            <div class="data-and-state">
+                                                <div>{{scope.row.num}}</div>
+                                                <div v-if="scope.row.state ==='up'" style="color:red">
+                                                    <el-image style="width: 10px; height: 10px" :src="imgDP[0].url" fit="fill"></el-image>
+                                                </div>
+                                                <div v-else-if="scope.row.state ==='down'" style="color:green">
+                                                    <el-image style="width: 10px; height: 10px" :src="imgDP[1].url" fit="fill"></el-image>
+                                                </div>
+                                            </div>
+                                        </template>
+                                    </el-table-column>
+                                </el-table>
+                            </div>
+                        </el-tab-pane>
+                        <el-tab-pane label="提升工程2.0研训平台" name="second">
+                            <div class="analyse-top-right-table">
+                                <el-table :data="tableData" style="width: 100%" height="170">
+                                    <el-table-column label="排名" width="80px" align="center">
+                                        <template #default="scope">
+                                            <el-image style="width: 20px; height: 20px" :src="imgArr[0].url" fit="fill" v-if="scope.row.sort ===1 "></el-image>
+                                            <el-image style="width: 20px; height: 20px" :src="imgArr[1].url" fit="fill" v-else-if="scope.row.sort ===2 "></el-image>
+                                            <el-image style="width: 20px; height: 20px" :src="imgArr[2].url" fit="fill" v-else-if="scope.row.sort ===3 "></el-image>
+                                            <div v-else>{{scope.row.sort}}</div>
+                                        </template>
+                                    </el-table-column>
+                                    <el-table-column prop="name" label="名称" align="center" />
+                                    <el-table-column>
+                                        <div class="column"></div>
+                                    </el-table-column>
+                                    <el-table-column label="数据量">
+                                        <template #default="scope">
+                                            <div class="data-and-state">
+                                                <div>{{scope.row.num}}</div>
+                                                <div v-if="scope.row.state ==='up'" style="color:red">
+                                                    <el-image style="width: 10px; height: 10px" :src="imgDP[0].url" fit="fill"></el-image>
+                                                </div>
+                                                <div v-else-if="scope.row.state ==='down'" style="color:green">
+                                                    <el-image style="width: 10px; height: 10px" :src="imgDP[1].url" fit="fill"></el-image>
+                                                </div>
+                                            </div>
+                                        </template>
+                                    </el-table-column>
+                                </el-table>
+                            </div>
+                        </el-tab-pane>
+                    </el-tabs>
+                </div>
+            </div>
+        </div>
+        <div class="analyse-center">
+            <div class="analyse-center-header"><span>学区信息</span></div>
+            <div class="analyse-center-content">
+                <div class="meter-box">
+                    <Test></Test>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+import { ref } from 'vue'
+import CommonMeter from '@/components/echarts/commonMeter.vue'
+import CopeMeter from '@/components/echarts/copyMeter.vue'
+import Test from '@/components/echarts/test.vue'
+export default {
+    components: {
+        CommonMeter,
+        CopeMeter,
+        Test,
+    },
+    setup() {
+        let headerbasics = ref([
+            { id: 1, title: '所在学区学校数', num: '87', subheading: '本月新增', subnum: 1 },
+            { id: 1, title: '所在学区教师数', num: '132', subheading: '本月新增', subnum: 32 },
+            { id: 1, title: '所在学区学生数', num: '1670', subheading: '本月新增', subnum: 0 },
+            { id: 1, title: '苏格拉底影片数', num: '560', subheading: '本月新增', subnum: 130 },
+        ])
+        let imgDP = ref([
+            { id: 1, title: '上升', url: require('../../assets/img/up.png') },
+            { id: 2, title: '下降', url: require('../../assets/img/down.png') },
+        ])
+        const tableData = [
+            {
+                num: '1780',
+                name: '成都银都紫藤小学',
+                state: 'up',
+                sort: 1,
+            },
+            {
+                num: '980',
+                name: '成都外国语小学',
+                state: 'up',
+                sort: 2,
+            },
+            {
+                num: '580',
+                name: '成都石室小学',
+                state: 'down',
+                sort: 3,
+            },
+            {
+                num: '380',
+                name: '成都市龙江路小学',
+                state: 'down',
+                sort: 4,
+            },
+            {
+                num: '100',
+                name: '成都玉林小学',
+                state: 'down',
+                sort: 5,
+            },
+        ]
+        let imgArr = ref([
+            { id: 1, url: require('@/assets/img/sort1.png') },
+            { id: 2, url: require('@/assets/img/sort2.png') },
+            { id: 3, url: require('@/assets/img/sort3.png') },
+        ])
+        const activeName = ref('first')
+        return { headerbasics, tableData, imgArr, activeName, imgDP }
+    },
+}
+</script>
+<style scoped>
+.area-analyse {
+    width: 100%;
+    padding: 1%;
+}
+.analyse-header-basics {
+    background-color: rgb(81, 86, 184);
+    background-image: url('../../assets/img/background-area.png');
+    background-repeat: no-repeat;
+    background-size: cover;
+    background-position: center center;
+    display: flex;
+    justify-content: space-between;
+    border-radius: 10px;
+}
+.analyse-header-basics-item {
+    width: 387px;
+    height: 150px;
+    line-height: 1;
+    padding-left: 0px;
+    padding-right: 8px;
+    border-right: 1px dashed rgba(200, 214, 229, 0.4);
+}
+.items-basics {
+    padding: 7% 10%;
+}
+.items-basics-title,
+.items-basics-increased {
+    font-size: 14px;
+    color: hsla(0, 0%, 100%, 0.8);
+}
+.items-basics-num {
+    font-size: 22px;
+    color: hsla(0, 0%, 100%, 0.8);
+}
+.analyse-top {
+    display: flex;
+    justify-content: space-between;
+    width: 100%;
+}
+.analyse-top-left,
+.analyse-top-center {
+    width: 30%;
+    margin-top: 1%;
+}
+.analyse-top-left-header {
+    width: 100%;
+    height: 50px;
+    line-height: 50px;
+    background-image: url('../../assets/img/header.png');
+    border-radius: 10px 10px 0px 0px;
+}
+.analyse-top-left-meter {
+    width: 100% !important;
+    height: 220px;
+    overflow: hidden;
+}
+.analyse-top-left-header {
+    font-size: 14px;
+    color: hsla(0, 0%, 100%, 0.8);
+}
+.analyse-top-right {
+    width: 30%;
+    margin-top: 1%;
+}
+.analyse-top-right-table {
+    line-height: 20px;
+}
+.column {
+    width: 100%;
+    height: 10px;
+    background: linear-gradient(to right, rgba(116, 185, 255, 1), rgba(15, 188, 249, 1));
+    border-radius: 5px;
+}
+.data-and-state {
+    display: flex;
+    justify-content: space-between;
+}
+.data-and-state div {
+    width: 50%;
+}
+.analyse-center {
+    width: 100%;
+    margin-top: 2%;
+}
+.analyse-center-header {
+    width: 100%;
+    height: 50px;
+    line-height: 50px;
+    background-image: url('../../assets/img/header.png');
+    background-size: cover;
+    border-radius: 10px 10px 0px 0px;
+    font-size: 14px;
+    color: hsla(0, 0%, 100%, 0.8);
+}
+.analyse-center-content {
+    /* width: 100%; */
+    height: 500px;
+}
+.analyse-top-left-ranking {
+    width: 100%;
+    height: 220px;
+    overflow: hidden;
+}
+.meter-box {
+    width: 100%;
+    height: 100%;
+    background: #fff;
+}
+</style>
+<style>
+.analyse-top-right .el-tabs__header {
+    margin-bottom: 1px !important;
+}
+</style>
+
+
+
+

+ 37 - 0
TEAMModelBI/ClientApp/src/view/areamanage/index.vue

@@ -0,0 +1,37 @@
+<template>
+    <div class="area-servebox">
+        <Areaanalyse></Areaanalyse>
+    </div>
+</template>
+<script>
+import { ref } from 'vue'
+import Areaanalyse from './areaAnalyse.vue'
+export default {
+    components: {
+        Areaanalyse,
+    },
+    setup() {
+        const activeName = ref('statistics')
+        const handleClick = (TabsPaneContext, event) => {
+            console.log(TabsPaneContext, event)
+        }
+        return { activeName, handleClick }
+    },
+}
+</script>
+<style scoped>
+</style>
+<style>
+.area-servebox .demo-tabs .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 10px;
+}
+.area-servebox .el-tabs__nav-wrap::after {
+    width: 0%;
+}
+</style>

+ 39 - 9
TEAMModelBI/ClientApp/src/view/common/aside.vue

@@ -60,6 +60,11 @@ export default {
         let menuWidth = ref('64px')
         var menuList = ref([])
         const activeIndex = ref('1')
+        //处理url多余的参数内容
+        let headerHost = window.location.href.split('/login?code')
+        let zc = window.location.href.split('state=STATE')
+        console.log(headerHost, zc, '内容')
+        headerHost[0] !== zc[0] ? window.history.pushState('', '', headerHost[0] + '/' + zc[1]) : ''
         const schoolMenus = [
             {
                 name: proxy.$t(`menu.index`),
@@ -108,7 +113,7 @@ export default {
             },
             {
                 name: '区校管理',
-                router: '/home/areamanage',
+                router: '',
                 role: ['special'],
                 icon: '#icon-zisunchaquyufenbu',
                 permission: ['batcharea-read', 'batcharea-upd'],
@@ -117,7 +122,7 @@ export default {
                 child: [
                     {
                         name: '学区管理',
-                        router: '/home/schoolmanage',
+                        router: '/home/areamanage',
                         icon: '#icon-quyuguanli',
                         permission: ['orgusers-read', 'orgusers-upd'],
                         isShow: true,
@@ -133,7 +138,7 @@ export default {
                     },
                     {
                         name: '微能力管理',
-                        router: '/home/schoolmanage',
+                        router: '/home/traitmanage',
                         icon: '#icon-peizhiguanli',
                         permission: ['orgusers-read', 'orgusers-upd'],
                         isShow: true,
@@ -204,7 +209,7 @@ export default {
                 child: [
                     {
                         name: '学区情况',
-                        router: '',
+                        router: '/home/district',
                         icon: '#icon-xiangmufanwei',
                         permission: [],
                         isShow: true,
@@ -212,7 +217,7 @@ export default {
                     },
                     {
                         name: '学校情况',
-                        router: '',
+                        router: '/home/campus',
                         icon: '#icon-xuexiao2',
                         permission: [],
                         isShow: true,
@@ -220,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()
@@ -241,7 +273,6 @@ export default {
             console.log(user, '用户的身份')
             // let aloneAuthority = user.permissions.split(',')
             // console.log(user, aloneAuthority)
-
             // for (let i in schoolMenus) {
             //     if (user.roles.includes('admin') || user.roles.includes('assist') || user.roles.length === 0) {
             //         console.log('进入判断')
@@ -266,16 +297,15 @@ export default {
 
             //身份
             let userRoles = user.roles
-            console.log(userRoles)
+            console.log(userRoles, user)
             for (let n in userRoles) {
                 let name = userRoles[n]
                 console.log(name)
                 for (let i in schoolMenus) {
                     schoolMenus[i].role.indexOf('all') != -1 ? menuList.value.push(schoolMenus[i]) : ''
                     schoolMenus[i].role.includes(name) ? menuList.value.push(schoolMenus[i]) : ''
-                    user.azp && schoolMenus[i].router == '/home/areamanage' ? menuList.value.push(schoolMenus[i]) : ''
+                    user.azp && schoolMenus[i].role.includes('special') ? menuList.value.push(schoolMenus[i]) : ''
                     console.log(schoolMenus[i].role.includes(name), schoolMenus[i].role, schoolMenus[i])
-                    schoolMenus[i].router
                 }
             }
             var result = []

+ 0 - 1
TEAMModelBI/ClientApp/src/view/home.vue

@@ -19,7 +19,6 @@
 import Header from './common/header.vue'
 import Aside from './common/aside.vue'
 import searchTool from './teachermanage/searchTool.vue'
-
 export default {
     components: {
         Header,

Diff do ficheiro suprimidas por serem muito extensas
+ 1181 - 0
TEAMModelBI/ClientApp/src/view/index/assitindex.vue


Diff do ficheiro suprimidas por serem muito extensas
+ 2267 - 0
TEAMModelBI/ClientApp/src/view/index/index.vue


+ 31 - 10
TEAMModelBI/ClientApp/src/view/index/operateLog.vue

@@ -9,7 +9,7 @@
                 </div> -->
                 <div class="block">
                     <div class="block-search">
-                        <el-input :placeholder="$t(`log.serarchHint`)" prefix-icon="el-icon-search" v-model="searchKey" @change="searchkeyword">
+                        <el-input :placeholder="$t(`log.serarchHint`)" prefix-icon="el-icon-search" v-model="searchKey" @input="searchkeyword">
                         </el-input>
                         <i class="iclosebtn" @click="searchClear">
                             <svg class="Abilityicon" aria-hidden="true">
@@ -77,8 +77,8 @@
                 <el-table-column property="tmdId" :label="$t(`log.tables.id`)" align="center" />
                 <el-table-column property="platform" :label="$t(`log.tables.platform`)" sortable align="center" />
                 <el-table-column property="type" :label="$t(`log.tables.content`)" align="center" />
-                <el-table-column property="rowKey" :label="$t(`log.tables.dataID`)" align="center" />
-                <el-table-column property="msg" :label="$t(`log.tables.details`)" align="center" />
+                <!-- <el-table-column property="rowKey" :label="$t(`log.tables.dataID`)" align="center" />
+                <el-table-column property="msg" :label="$t(`log.tables.details`)" align="center" /> -->
                 <!-- <el-table-column property="jsonfile" :label="$t(`log.tables.location`)" align="center" /> -->
                 <el-table-column property="convertTime" :label="$t(`log.tables.time`)" sortable :sort-by="['time']" align="center" />
                 <el-table-column label="" align="center">
@@ -140,7 +140,7 @@
     </div>
 </template>
 <script>
-import { getCurrentInstance, ref, reactive } from 'vue'
+import { getCurrentInstance, ref, reactive, watch } from 'vue'
 import { Search } from '@element-plus/icons-vue'
 export default {
     setup() {
@@ -166,6 +166,7 @@ export default {
         })
         let logdetails = ref(false)
         let logdataDetails = ref({})
+        let timer = ref('')
         //获取所有日志内容
         function getLogcontent(startDate, endDate, platform, model) {
             console.log(startDate, endDate, platform, model, '接收的内容')
@@ -185,8 +186,8 @@ export default {
         function exportExcel() {
             require.ensure([], () => {
                 const { export_json_to_excel } = require('../../until/excel/Export2Excel')
-                const tHeader = ['编号', '操作人姓名', 'tmdId', '平台', '操作内容', '数据ID', '内容', '时间'] // excel文档第一行显示的标题
-                const filterVal = ['index', 'name', 'tmdId', 'platform', 'type', 'rowKey', 'msg', 'convertTime'] // id,version等都是上面标题所对应的数据
+                const tHeader = ['编号', '操作人姓名', 'tmdId', '平台', '操作内容', '时间'] // excel文档第一行显示的标题
+                const filterVal = ['index', 'name', 'tmdId', 'platform', 'type', 'convertTime'] // id,version等都是上面标题所对应的数据
                 const list = tableData.value
                 const data = formatJson(filterVal, list)
                 export_json_to_excel(tHeader, data, '操作日志') //标题,数据,文件名
@@ -224,16 +225,22 @@ export default {
             tableData.value = filterDatas
         }
         //搜索
-        function searchkeyword(value) {
+        function searchkeyword() {
             console.log(value)
+            let value = searchKey.value
             if (value === '' || value === null || value === undefined) {
                 tableData.value = originalData.value
                 return
             }
             let key = value
-            let newdata = originalData.value.filter((item) => {
-                return item.name === key || item.tmdId === key
-            })
+            // let newdata = originalData.value.filter((item) => {
+            //     console.log(item, '1111111111111111111111111111')
+            //     return item.name.indexOf(key) !== -1 || item.tmdId.indexOf(key) !== -1
+            // })
+            let newdata = []
+            for (let i in originalData.value) {
+                originalData.value[i].name && (originalData.value[i].name.indexOf(key) !== -1 || originalData.value[i].tmdId.indexOf(key) !== -1) ? newdata.push(originalData.value[i]) : ''
+            }
             console.log(newdata, '新数据')
             tableData.value = newdata
         }
@@ -248,7 +255,20 @@ export default {
             logdataDetails.value = item
             logdetails.value = true
         }
+        function debounce(fn, wait) {
+            if (timer.value !== null) {
+                clearTimeout(timer.value)
+            }
+            timer.value = setTimeout(fn, wait)
+        }
         getLogcontent('', '', '', 'init')
+        watch(searchKey.value, (newdata) => {
+            if (newdata.trim().length !== 0) {
+                debounce(searchkeyword, 1000)
+            } else {
+                tableData.value = original.value
+            }
+        })
         return {
             tableData,
             getLogcontent,
@@ -268,6 +288,7 @@ export default {
             formInline,
             logdetails,
             logdataDetails,
+            timer,
         }
     },
 }

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

Diff do ficheiro suprimidas por serem muito extensas
+ 1442 - 0
TEAMModelBI/ClientApp/src/view/participation/setAbility.vue


Diff do ficheiro suprimidas por serem muito extensas
+ 2170 - 0
TEAMModelBI/ClientApp/src/view/participation/setPhase.vue


Diff do ficheiro suprimidas por serem muito extensas
+ 1373 - 0
TEAMModelBI/ClientApp/src/view/schoolServe/analyseSchool.vue


+ 103 - 78
TEAMModelBI/ClientApp/src/view/schoolServe/index.vue

@@ -1,83 +1,93 @@
 <template>
-    <div class="school-servebox">
-        <div class="topbox">
-            <div class="topbox-list">
-                <div class="top-aspects" v-for="(item,index) in headerData" :key="index">
-                    <div :class="[item.classname,'left-top-icon']">
-                        <svg class="top-header-icon" aria-hidden="true">
-                            <use :xlink:href="item.icon"></use>
-                        </svg>
-                    </div>
-                    <div class="right-top-text">
-                        <p class="right-top-num">{{item.num}}</p>
-                        <p class="right-top-title">{{item.title}}</p>
+    <div class="schoolframe">
+        <!-- <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
+            <el-tab-pane label="数据总览" name="first">
+                <div class="school-servebox">
+                    <div class="topbox">
+                        <div class="topbox-list">
+                            <div class="top-aspects" v-for="(item,index) in headerData" :key="index">
+                                <div :class="[item.classname,'left-top-icon']">
+                                    <svg class="top-header-icon" aria-hidden="true">
+                                        <use :xlink:href="item.icon"></use>
+                                    </svg>
+                                </div>
+                                <div class="right-top-text">
+                                    <p class="right-top-num">{{item.num}}</p>
+                                    <p class="right-top-title">{{item.title}}</p>
+                                </div>
+                            </div>
+                            <div class="basicsbox">
+                                <div class="leftbox">
+                                    <p class="commonbox-title">基础数据</p>
+                                    <div class="weekdata"><span class="weekdata-title">本学期累计数据数:</span><span class="weekdata-text">2630</span></div>
+                                    <div :class="[items.type==='month' || items.type==='semester'? 'alonebox' :'totalalonebox']" v-for="(items,indexs) in basicsData.totals.month" :key="indexs">
+                                        <p :class="[items.type==='month' || items.type==='semester' ? 'alonebox-title':'total-alonebox-title']">{{items.title}}</p>
+                                        <p :class="[items.type==='month' || items.type==='semester' ? 'alonebox-content':'total-alonebox-content']">{{items.num}}</p>
+                                        <p class="compare">
+                                            <span class="compare-title" v-if="items.type==='month'">同上周对比:</span>
+                                            <span class="compare-title" v-else-if="items.type==='semester'">同上学期对比:</span>
+                                            <span class="compare-icon red" v-if="items.compare <0">↓<span>{{items.compare}}%</span></span>
+                                            <span class="compare-icon greens" v-if="items.compare >=0">↑<span>{{items.compare}}%</span></span>
+                                        </p>
+                                    </div>
+                                </div>
+                                <div class="rightbox">
+                                    <p class="commonbox-title">基础数据</p>
+                                    <div class="weekdata"><span class="weekdata-title">全部累计数据数:</span><span class="weekdata-text">103564</span></div>
+                                    <div :class="[items.type==='month' ? 'alonebox' :'totalalonebox']" v-for="(items,indexs) in basicsData.totals.total" :key="indexs">
+                                        <p :class="[items.type ==='month' ? 'alonebox-title':'total-alonebox-title']">{{items.title}}</p>
+                                        <p :class="[items.type ==='month' ? 'alonebox-content':'total-alonebox-content']">{{items.num}}</p>
+                                        <p class="compare">
+                                            <span class="compare-title">同去年对比:</span>
+                                            <span class="compare-icon red" v-if="items.compare <0">↓<span>{{items.compare}}%</span></span>
+                                            <span class="compare-icon green " v-if="items.compare >=0">↑<span>{{items.compare}}%</span></span>
+                                        </p>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
                     </div>
-                </div>
-                <div class="basicsbox">
-                    <div class="leftbox">
-                        <p class="commonbox-title">基础数据</p>
-                        <div class="weekdata"><span class="weekdata-title">本学期累计数据数:</span><span class="weekdata-text">2630</span></div>
-                        <div :class="[items.type==='month' || items.type==='semester'? 'alonebox' :'totalalonebox']" v-for="(items,indexs) in basicsData.totals.month" :key="indexs">
-                            <p :class="[items.type==='month' || items.type==='semester' ? 'alonebox-title':'total-alonebox-title']">{{items.title}}</p>
-                            <p :class="[items.type==='month' || items.type==='semester' ? 'alonebox-content':'total-alonebox-content']">{{items.num}}</p>
-                            <p class="compare">
-                                <span class="compare-title" v-if="items.type==='month'">同上周对比:</span>
-                                <span class="compare-title" v-else-if="items.type==='semester'">同上学期对比:</span>
-                                <span class="compare-icon red" v-if="items.compare <0">↓<span>{{items.compare}}%</span></span>
-                                <span class="compare-icon greens" v-if="items.compare >=0">↑<span>{{items.compare}}%</span></span>
-                            </p>
+                    <div class="center-center">
+                        <p class="commonbox-title research">研修情况</p>
+                        <div class="center-center-box">
+                            <CommonPie :proportionData="totalechartsData.pie1"></CommonPie>
+                        </div>
+                        <div class="center-center-box">
+                            <CommonPie :proportionData="totalechartsData.pie2"></CommonPie>
+                        </div>
+                        <div class="center-center-box">
+                            <CommonPie :proportionData="totalechartsData.pie3"></CommonPie>
+                        </div>
+                        <div class="center-center-box">
+                            <CommonPie :proportionData="totalechartsData.pie4"></CommonPie>
                         </div>
                     </div>
-                    <div class="rightbox">
-                        <p class="commonbox-title">基础数据</p>
-                        <div class="weekdata"><span class="weekdata-title">全部累计数据数:</span><span class="weekdata-text">103564</span></div>
-                        <div :class="[items.type==='month' ? 'alonebox' :'totalalonebox']" v-for="(items,indexs) in basicsData.totals.total" :key="indexs">
-                            <p :class="[items.type ==='month' ? 'alonebox-title':'total-alonebox-title']">{{items.title}}</p>
-                            <p :class="[items.type ==='month' ? 'alonebox-content':'total-alonebox-content']">{{items.num}}</p>
-                            <p class="compare">
-                                <span class="compare-title">同去年对比:</span>
-                                <span class="compare-icon red" v-if="items.compare <0">↓<span>{{items.compare}}%</span></span>
-                                <span class="compare-icon green " v-if="items.compare >=0">↑<span>{{items.compare}}%</span></span>
-                            </p>
+                    <div class="centerbox">
+                        <div class="centerbox-left">
+                            <p class="commonbox-title analysis">数据占比</p>
+                            <ConventionPie :pieData="totalechartsData.school"></ConventionPie>
+                        </div>
+                        <div class="centerbox-right">
+                            <p class="commonbox-title modules">模组情况</p>
+                            <div class="centerbox-right-leftbox">
+                                <CommonBar :barData="totalechartsData.classAndactivity"></CommonBar>
+                            </div>
+                            <div class="centerbox-right-rightbox">
+                                <CommonYBars></CommonYBars>
+                            </div>
                         </div>
                     </div>
+                    <div class="bottombox">
+                        <p class="commonbox-title modules">课例折线图</p>
+                        <CommonLine :lineData="totalechartsData.dynamic"></CommonLine>
+                    </div>
                 </div>
-            </div>
-        </div>
-        <div class="center-center">
-            <p class="commonbox-title research">研修情况</p>
-            <div class="center-center-box">
-                <CommonPie :proportionData="totalechartsData.pie1"></CommonPie>
-            </div>
-            <div class="center-center-box">
-                <CommonPie :proportionData="totalechartsData.pie2"></CommonPie>
-            </div>
-            <div class="center-center-box">
-                <CommonPie :proportionData="totalechartsData.pie3"></CommonPie>
-            </div>
-            <div class="center-center-box">
-                <CommonPie :proportionData="totalechartsData.pie4"></CommonPie>
-            </div>
-        </div>
-        <div class="centerbox">
-            <div class="centerbox-left">
-                <p class="commonbox-title analysis">数据占比</p>
-                <ConventionPie :pieData="totalechartsData.school"></ConventionPie>
-            </div>
-            <div class="centerbox-right">
-                <p class="commonbox-title modules">模组情况</p>
-                <div class="centerbox-right-leftbox">
-                    <CommonBar :barData="totalechartsData.classAndactivity"></CommonBar>
-                </div>
-                <div class="centerbox-right-rightbox">
-                    <CommonYBars></CommonYBars>
-                </div>
-            </div>
-        </div>
-        <div class="bottombox">
-            <p class="commonbox-title modules">课例折线图</p>
-            <CommonLine :lineData="totalechartsData.dynamic"></CommonLine>
-        </div>
+            </el-tab-pane>
+            <el-tab-pane label="学校列表" name="second">
+                <SchoolList></SchoolList>
+            </el-tab-pane>
+        </el-tabs> -->
+        <SchoolList></SchoolList>
     </div>
 </template>
 <script>
@@ -87,6 +97,7 @@ import CommonBar from '@/components/echarts/commonBar.vue'
 import CommonYBars from '@/components/echarts/commonYBar.vue'
 import CommonPie from '@/components/echarts/commonPie.vue'
 import CommonLine from '@/components/echarts/commonLine.vue'
+import SchoolList from './school.vue'
 import * as echarts from 'echarts'
 export default {
     components: {
@@ -95,6 +106,7 @@ export default {
         CommonYBars,
         CommonPie,
         CommonLine,
+        SchoolList,
     },
     setup() {
         let headerData = ref([
@@ -847,13 +859,17 @@ export default {
                 ],
             },
         })
-        return { headerData, basicsData, totalechartsData }
+        const activeName = ref('first')
+        return { headerData, basicsData, totalechartsData, activeName }
     },
 }
 </script>
 <style scoped>
+.schoolframe {
+    width: 100%;
+}
 .school-servebox {
-    background-color: #f7f7f7;
+    background: #e9eef3;
 }
 .topbox {
     width: 100%;
@@ -1102,7 +1118,7 @@ export default {
 }
 </style>
 <style>
-.school-servebox .demo-tabs .el-tabs__header {
+/* .school-servebox .demo-tabs .el-tabs__header {
     height: 45px;
     line-height: 45px;
     line-height: 0px;
@@ -1110,8 +1126,17 @@ export default {
     box-shadow: 0 2px 5px #e9e9e9;
     background-color: #fff;
     margin: 0px 0px 10px;
-}
-.school-servebox .el-tabs__nav-wrap::after {
+} */
+.schoolframe .el-tabs__nav-wrap::after {
     width: 0%;
 }
+.schoolframe .demo-tabs .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 0px;
+}
 </style>

+ 130 - 168
TEAMModelBI/ClientApp/src/view/schoolServe/school.vue

@@ -29,21 +29,13 @@
                 </svg>
             </div>
         </div>
-        <div class="boxselect" @click="createdSchoolbtn" v-if="PowerShow">
-            <a>
-                <div class="layer">
-                    <span></span>
-                    <span></span>
-                    <span></span>
-                    <span></span>
-                    <span class="fab">
-                        <svg class="created-areaicon" aria-hidden="true">
-                            <use xlink:href="#icon-xuexiao"></use>
-                        </svg>
-                    </span>
-                </div>
-                <span class="text">{{$t(`schoolManages.createdtitle`)}}</span>
-            </a>
+        <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="加载中...">
@@ -62,14 +54,25 @@
                 <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" />
-                <el-table-column prop="province" :label="$t(`schoolManages.tables.province`)" 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 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">
@@ -77,10 +80,31 @@
                         <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">
+                            <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>
@@ -90,18 +114,18 @@
     <!--编辑学校页面-->
     <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>
-            <el-button class="changebtn" v-if="store.state.changbtnShow && changebtns" @click="getSetschool()">
-                <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>
         </div>
         <el-tabs v-if="models==='details' && PowerShow" @tab-click="changeTabs">
             <!--基础设置-->
@@ -147,20 +171,20 @@
                                 <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="$t(`schoolManages.basicSet.region`)+':'" class="school-form-area">
+                            <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-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-form-item :label="$t(`schoolManages.basicSet.location`)+':'" class="school-form-site">
                                 <el-input disabled :placeholder="nowPitchdata.address" />
-                            </el-form-item>
+                            </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" />
+                                <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" />
@@ -190,7 +214,7 @@
 import { reactive, ref, getCurrentInstance, toRef } from 'vue'
 import option from '@/static/region.json'
 import { useStore } from 'vuex'
-import { ElMessage, ElLoading } from 'element-plus'
+import { ElMessage, ElLoading, ElMessageBox } from 'element-plus'
 import { useRouter } from 'vue-router'
 import SetSchool from './setschool.vue'
 import Impower from './impower.vue'
@@ -281,6 +305,15 @@ export default {
             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)
         //所有学校列表
@@ -289,13 +322,22 @@ 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
                         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)) : ''
@@ -569,6 +611,27 @@ export default {
                     loadinginfo.close()
                 })
         }
+        function skipAnalyse() {
+            routerInfo.push({ path: '/home/analyse', query: {} })
+        }
+        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 {
@@ -610,6 +673,10 @@ export default {
             setSchoolData,
             getSetschool,
             imgData,
+            patternIcon,
+            skipAnalyse,
+            schooldataSort,
+            removeSchool,
         }
     },
 }
@@ -681,7 +748,8 @@ export default {
     width: 35%;
 }
 .school-form-size {
-    width: 21%;
+    width: 35%;
+    color: #bdc3c7;
 }
 .school-form-admin {
     width: 35%;
@@ -709,139 +777,10 @@ export default {
 }
 /*btn*/
 .boxselect {
-    width: 60px;
-    height: 60px;
+    width: 20%;
     display: inline-block;
-    margin-top: 1%;
-    margin-left: 15%;
-}
-.boxselect:hover {
-    cursor: pointer;
-}
-.boxselect a {
-    text-decoration: none;
-    color: #fff;
-    display: block;
-    position: relative;
-}
-
-.boxselect a .layer {
-    width: 60px;
-    height: 60px;
-    transition: transform 0.3s;
-    margin-top: 17%;
-}
-
-.boxselect a:hover .layer {
-    transform: rotate(-25deg) skew(20deg);
-}
-
-.boxselect a .layer span {
-    position: absolute;
-    top: 0;
-    left: 0;
-    height: 100%;
-    width: 100%;
-    border: 1px solid #fff;
-    border-radius: 5px;
-    transition: all 0.3s;
-}
-
-.boxselect a .layer span.fab {
-    font-size: 36px;
-    line-height: 180px;
-    text-align: center;
-}
-.boxselect a:hover .layer span:nth-child(1) {
-    opacity: 0.3;
-}
-
-.boxselect a:hover .layer span:nth-child(2) {
-    opacity: 0.4;
-    transform: translate(5px, -5px);
-}
-
-.boxselect a:hover .layer span:nth-child(3) {
-    opacity: 0.6;
-    transform: translate(10px, -10px);
-}
-
-.boxselect a:hover .layer span:nth-child(4) {
-    opacity: 0.8;
-    transform: translate(15px, -15px);
-}
-
-.boxselect a:hover .layer span:nth-child(5) {
-    opacity: 1;
-    transform: translate(20px, -20px);
-}
-
-.boxselect a:nth-child(1) .layer span,
-.boxselect a:nth-child(1) .text {
-    color: #4267b2;
-    border-color: #4267b2;
-}
-
-.boxselect a:nth-child(2) .layer span,
-.boxselect a:nth-child(2) .text {
-    color: #1da1f2;
-    border-color: #1da1f2;
-}
-
-.boxselect a:nth-child(3) .layer span,
-.boxselect a:nth-child(3) .text {
-    color: #e1306c;
-    border-color: #e1306c;
-}
-
-.boxselect a:nth-child(4) .layer span,
-.boxselect a:nth-child(4) .text {
-    color: #2867b2;
-    border-color: #2867b2;
-}
-
-.boxselect a:nth-child(5) .layer span,
-.boxselect a:nth-child(5) .text {
-    color: #ff0000;
-    border-color: #ff0000;
-}
-
-.boxselect a:nth-child(1) .layer span {
-    box-shadow: -1px 1px 3px #4267b2;
-}
-
-.boxselect a:nth-child(2) .layer span {
-    box-shadow: -1px 1px 3px #1da1f2;
-}
-
-.boxselect a:nth-child(3) .layer span {
-    box-shadow: -1px 1px 3px #e1306c;
-}
-
-.boxselect a:nth-child(4) .layer span {
-    box-shadow: -1px 1px 3px #4267b2;
-}
-
-.boxselect a:nth-child(5) .layer span {
-    box-shadow: -1px 1px 3px #ff0000;
-}
-
-.boxselect a .text {
-    position: absolute;
-    width: 100%;
-    left: 50%;
-    /* bottom: -5px; */
-    opacity: 0;
-    transform: translateX(-50%);
-    transition: bottom 0.3s ease, opacity 0.3s ease;
-    font-size: 14px;
-    font-weight: 600;
-    top: 50%;
-}
-
-.boxselect a:hover .text {
-    bottom: -25px;
-    opacity: 1;
+    margin-top: 0.5%;
+    text-align: right;
 }
 .created-areaicon {
     width: 1.3em;
@@ -864,10 +803,11 @@ export default {
 }
 .schoolDeatils {
     position: relative;
+    background: #e9eef3;
 }
 .changebtn {
     display: inline-block;
-    float: right;
+    /* float: right; */
 }
 .changebtn-title {
     font-size: 12px;
@@ -880,11 +820,33 @@ export default {
     margin-right: 3px;
 }
 .back-icon {
-    width: 14px;
-    height: 14px;
+    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 {

+ 105 - 25
TEAMModelBI/ClientApp/src/view/schoolServe/setschool.vue

@@ -158,12 +158,12 @@
             <div class="subjectbox">
                 <div class="semesterbox-title">
                     <div class="semesterbox-title-name">{{$t(`schoolManages.gradSet.gradeAndsubjects.subjectTitle`)}}</div>
-                    <div class="semesterbox-title-specialty">
+                    <!-- <div class="semesterbox-title-specialty">
                         <span class="common-subject">{{$t(`schoolManages.gradSet.gradeAndsubjects.subjectType.common`)}}</span>
                         <span class="ordinary-subject">{{$t(`schoolManages.gradSet.gradeAndsubjects.subjectType.subject`)}}</span>
                         <span class="specialty-subject">{{$t(`schoolManages.gradSet.gradeAndsubjects.subjectType.major`)}}</span>
-                    </div>
-                    <div class="semesterbox-title-add" @click="semesterCompile('','addSubjects')">
+                    </div> -->
+                    <div class="semesterbox-title-add" @click="semesterCompile('','gradeClass')">
                         <svg class="mini-hint" aria-hidden="true">
                             <use xlink:href="#icon-jia-copy"></use>
                         </svg>
@@ -171,9 +171,9 @@
                 </div>
                 <div class="subject-group">
                     <div class="subject-group-item" v-for="item in nowschoolData.period[pathNowphase].subjects" :key="item.id">
-                        <span class="subject-type" title="通用" v-show="item.type ===0"></span>
+                        <!-- <span class="subject-type" title="通用" v-show="item.type ===0"></span>
                         <span class="subject-type-ordinary" title="学科" v-show="item.type ===1"></span>
-                        <span class="subject-type-specialty" title="专业" v-show="item.type ===2"></span>
+                        <span class="subject-type-specialty" title="专业" v-show="item.type ===2"></span> -->
                         <div class="subject-group-item-name">
                             <!-- <input autocomplete="off" spellcheck="false" type="text" placeholder="设置学科..." disabled="disabled" class="ivu-input ivu-input-default ivu-input-disabled"> -->
                             {{item.name}}
@@ -194,6 +194,38 @@
                 </div>
             </div>
             <!--学科设置end-->
+            <!--班级专业名称/班级类型名称-->
+            <div class="subjectbox">
+                <div class="semesterbox-title grade-class">
+                    <div class="semesterbox-title-name">班级专业名称/班级类型名称</div>
+                    <div class="semesterbox-title-add" @click="addExamtype('gradeClass')">
+                        <svg class="mini-hint" aria-hidden="true">
+                            <use xlink:href="#icon-jia-copy"></use>
+                        </svg>
+                    </div>
+                </div>
+                <div class="subject-group">
+                    <div class="exam-type-item" v-for="(item,index) in nowschoolData.period[pathNowphase].majors" :key="index">
+                        <div class="exam-type-item-name" v-if="gradeClassdata.index !== index"><span>{{item.name}}</span></div>
+                        <div class="exam-type-item-name" v-else-if="gradeClassdata.index === index">
+                            <el-input v-model="gradeClassdata.name" @blur="updateExamupdate(item,'gradeclass')" placeholder='设置专业...' class="exam-edit-input" />
+                        </div>
+                        <div class="exam-type-item-icon">
+                            <div class="exam-type-redacicon" @click="gradeClassdata.name=item.name,gradeClassdata.id=item.id,gradeClassdata.index=index">
+                                <svg class="subject-redact-icon" aria-hidden="true">
+                                    <use xlink:href="#icon-bianji1-copy"></use>
+                                </svg>
+                            </div>
+                            <div class="exam-type-deleteicon" @click="deletebox(item,'gradeclass')">
+                                <svg class="subject-redact-icon" aria-hidden="true">
+                                    <use xlink:href="#icon-shanchu2-copy"></use>
+                                </svg>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <!--班级专业名称/班级类型名称end-->
             <!--学情设置-->
             <div class="situationbox">
                 <div class="semesterbox-title">
@@ -596,6 +628,12 @@ export default {
             name: '',
             index: -1,
         })
+        //班级专业名称/班级类型名称 需要数据
+        let gradeClassdata = ref({
+            id: '',
+            name: '',
+            index: -1,
+        })
         //时段设置需要数据
         let settimeData = ref({
             startValue: '',
@@ -918,6 +956,7 @@ export default {
                   (modeltitle = proxy.$t(`schoolManages.gradSet.deletebox.deleteType`)))
                 : ''
             state === 'timebucket' ? ((text = proxy.$t(`schoolManages.gradSet.deletebox.deleteNow`)), (modeltitle = proxy.$t(`schoolManages.gradSet.deletebox.deleteTime`))) : ''
+            state === 'gradeclass' ? ((text = '删除后与之关联的数据将不能查询,确认删除吗?'), (modeltitle = '删除专业')) : ''
             ElMessageBox.confirm(text, modeltitle, {
                 confirmButtonText: proxy.$t(`commonMsg.confirm`),
                 cancelButtonText: proxy.$t(`commonMsg.closes`),
@@ -945,6 +984,8 @@ export default {
                     ? deletExamtyps(value)
                     : state === 'timebucket'
                     ? deleteTimebucket(value)
+                    : state === 'gradeclass'
+                    ? deleMajor(value)
                     : ''
                 store.commit('changeShowbtn', true)
             })
@@ -952,10 +993,17 @@ export default {
         //删除学期
         function deleSemester(value) {
             let data = nowschoolData.value.period[pathNowphase.value].semesters
+            if (data.length <= 1) {
+                ElMessage.warning('至少需要保留一项,无法删除')
+                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), 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, '删除学期后的')
         }
         //删除年级
         function deleteGrade(value) {
@@ -973,6 +1021,13 @@ export default {
             }
             nowschoolData.value.period[pathNowphase.value].subjects = datas
         }
+        function deleMajor(value) {
+            let datas = nowschoolData.value.period[pathNowphase.value].majors
+            for (let i in datas) {
+                datas[i].id === value.id ? datas.splice(i, 1) : ''
+            }
+            nowschoolData.value.period[pathNowphase.value].majors = datas
+        }
         //删除考试类型(学情)
         function deletExamtyps(value) {
             let datas = nowschoolData.value.period[pathNowphase.value].analysis.type
@@ -1009,23 +1064,43 @@ export default {
             console.log(nowschoolData.value.period[pathNowphase.value].grades, 'add后')
         }
         //修改考试类型
-        function updateExamupdate(value) {
+        function updateExamupdate(value, state) {
             console.log(value, '修改考试类型')
-            let examArr = nowschoolData.value.period[pathNowphase.value].analysis.type
-            for (let i in examArr) {
-                console.log(examType.value)
-                examArr[i].id === value.id ? ((examArr[i].name = examType.value.name), (examType.value.index = -1)) : ''
+            if (state === 'gradeclass') {
+                let majorsArr = nowschoolData.value.period[pathNowphase.value].majors
+                for (let i in majorsArr) {
+                    majorsArr[i].id === value.id ? ((majorsArr[i].name = gradeClassdata.value.name), (gradeClassdata.value.index = -1)) : ''
+                }
+                nowschoolData.value.period[pathNowphase.value].majors = majorsArr
+                console.log(nowschoolData.value.period[pathNowphase.value].majors, '完成后的')
+            } else {
+                let examArr = nowschoolData.value.period[pathNowphase.value].analysis.type
+                for (let i in examArr) {
+                    console.log(examType.value)
+                    examArr[i].id === value.id ? ((examArr[i].name = examType.value.name), (examType.value.index = -1)) : ''
+                }
+                nowschoolData.value.period[pathNowphase.value].analysis.type = examArr
             }
-            nowschoolData.value.period[pathNowphase.value].analysis.type = examArr
             store.commit('changeShowbtn', true)
         }
         //新增考试类型(学情)
-        function addExamtype() {
-            let data = nowschoolData.value.period[pathNowphase.value].analysis.type
-            let ids = proxy.$access.guid()
-            data.push({ id: ids, name: '' })
-            ;(examType.value.id = ids), (examType.value.name = ''), (examType.value.index = nowschoolData.value.period[pathNowphase.value].analysis.type.length - 1)
-            nowschoolData.value.period[pathNowphase.value].analysis.type = data
+        function addExamtype(state) {
+            if (state === 'gradeClass') {
+                let data = nowschoolData.value.period[pathNowphase.value].majors
+                let ids = proxy.$access.guid()
+                data.push({ id: ids, name: '' })
+                gradeClassdata.value.id = ids
+                gradeClassdata.value.name = ''
+                gradeClassdata.value.index = nowschoolData.value.period[pathNowphase.value].majors.length - 1
+                nowschoolData.value.period[pathNowphase.value].majors = data
+                console.log(nowschoolData.value.period[pathNowphase.value].majors, '现在的数据')
+            } else {
+                let data = nowschoolData.value.period[pathNowphase.value].analysis.type
+                let ids = proxy.$access.guid()
+                data.push({ id: ids, name: '' })
+                ;(examType.value.id = ids), (examType.value.name = ''), (examType.value.index = nowschoolData.value.period[pathNowphase.value].analysis.type.length - 1)
+                nowschoolData.value.period[pathNowphase.value].analysis.type = data
+            }
         }
         //处理进线生和踩线生
         function coilAndpass(state) {
@@ -1186,6 +1261,7 @@ export default {
             getTimeZoneList,
             timeZoneList,
             semesterSort,
+            gradeClassdata,
         }
     },
 }
@@ -1408,7 +1484,7 @@ export default {
 }
 .set-time-enrollment {
     line-height: 20px;
-    margin-top: 1%;
+    margin-top: 0.5%;
 }
 .set-time-enrollment-star {
     line-height: 20px;
@@ -1543,9 +1619,9 @@ export default {
 }
 .classbox,
 .subjectbox {
-    width: 48.5%;
+    width: 97%;
     vertical-align: top;
-    padding: 12px 15px 16px 15px;
+    /* padding: 12px 15px 16px 15px; */
     min-height: 150px;
     border-radius: 5px;
     margin-right: 12px;
@@ -1555,7 +1631,7 @@ export default {
     border: 1px solid #d5d5d5;
 }
 .classbox {
-    width: 48%;
+    width: 97%;
 }
 .classbox .semesterbox-title::before {
     background: #7c4dff;
@@ -1564,9 +1640,10 @@ export default {
     background: #68cdf1;
 }
 .class-item-frame {
-    margin-top: 2%;
+    margin-top: 0%;
     height: 15vh;
     overflow: hidden;
+    padding: 1%;
 }
 .class-item-frame:hover,
 .subject-group:hover {
@@ -1686,7 +1763,7 @@ export default {
     width: 96%;
     overflow: hidden;
     height: 15vh;
-    margin-top: 2%;
+    padding: 1%;
 }
 .subject-group-item {
     display: inline-block;
@@ -1772,7 +1849,7 @@ export default {
     margin-bottom: 1px;
 }
 .situationbox {
-    padding: 12px 15px 16px 15px;
+    /* padding: 12px 15px 16px 15px; */
     min-height: 150px;
     border-radius: 5px;
     margin-right: 12px;
@@ -1811,7 +1888,7 @@ export default {
     width: 75px;
 }
 .exam-type {
-    margin: 10px 0px;
+    margin: 10px 10px;
 }
 .exam-type-item:hover .exam-type-item-icon div {
     opacity: 1;
@@ -2065,6 +2142,9 @@ export default {
 .conductbox .el-form {
     margin-top: 2%;
 }
+.grade-class::before {
+    background: #e74c3c !important;
+}
 @media screen and (max-width: 1367px) {
     .settimebox .el-card__body {
         width: 200px;

+ 36 - 0
TEAMModelBI/ClientApp/src/view/schoolmanage/index.vue

@@ -0,0 +1,36 @@
+<template>
+    <div class="school-analyse">
+        <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
+            <el-tab-pane label="数据分析" name="first">
+                <Schoolanalyse></Schoolanalyse>
+            </el-tab-pane>
+            <el-tab-pane label="学校列表" name="second">
+                <SchoolManage></SchoolManage>
+            </el-tab-pane>
+        </el-tabs>
+    </div>
+</template>
+<script>
+import { ref } from 'vue'
+import Schoolanalyse from './schoolAnalyse.vue'
+import SchoolManage from './schoolManege.vue'
+export default {
+    components: {
+        Schoolanalyse,
+        SchoolManage,
+    },
+    setup() {
+        const activeName = ref('first')
+        return { activeName }
+    },
+}
+</script>
+<style scoped>
+.school-analyse {
+    width: 100%;
+    line-height: 20px;
+    padding: 0% 1%;
+}
+</style>
+<style>
+</style>

+ 990 - 0
TEAMModelBI/ClientApp/src/view/schoolmanage/schoolAnalyse.vue

@@ -0,0 +1,990 @@
+<template>
+    <div class="schoolanalysebox" v-if="showState ==='all'">
+        <div class="school-analyse-header">
+            <div class="school-analyse-headersbox">
+                <div class="school-analyse-headersbox-item" v-for="item in headerbasics" :key="item.id">
+                    <div class="item-basics">
+                        <p class="item-title">{{item.title}}</p>
+                        <p class="item-num">{{item.num}}</p>
+                        <p class="item-subhead">{{item.subheading}}:<span class="item-subhead-value">{{item.subnum}}</span></p>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="school-analyse-top">
+            <div class="school-analyse-top-left">
+                <Bar :barData="echartsData.bar1"></Bar>
+            </div>
+            <div class="school-analyse-top-center">
+                <Bar :barData="echartsData.bar2"></Bar>
+            </div>
+            <div class="school-analyse-top-right">
+                <Bar :barData="echartsData.bar3"></Bar>
+            </div>
+        </div>
+        <div class="school-analyse-center">
+            <div class="school-analyse-center-bottom">
+                <div :class="[item.classname,'bottom-item']" v-for="item in centerbasics">
+                    <div class="bottom-item-header">
+                        <svg class="icon" aria-hidden="true">
+                            <use :xlink:href="item.icon"></use>
+                        </svg>
+                        <span>{{item.title}}</span>
+                    </div>
+                    <div class="comparebox">
+                        <p class="comparebox-num">{{item.num}}</p>
+                        <p class="comparebox-subhead">
+                            <span class="comparebox-subhead-title">{{item.subheading}}:</span>
+                            <span class="comparebox-subhead-value">{{item.subheadingNum}}</span>
+                            <span class="comparebox-subhead-state">
+                                <el-image style="width: 12px; height: 12px" :src="imgdata[0].url" :fit="fit" v-if="item.subheadingState==='down'" />
+                                <el-image style="width: 12px; height: 12px" :src="imgdata[1].url" :fit="fit" v-if="item.subheadingState==='up'" />
+                            </span>
+                        </p>
+                        <p class="comparebox-subhead">
+                            <span class="comparebox-subhead-title">{{item.subhead}}:</span>
+                            <span class="comparebox-subhead-value">{{item.subheadNum}}</span>
+                            <span class="comparebox-subhead-state">
+                                <el-image style="width: 12px; height: 12px" :src="imgdata[0].url" :fit="fit" v-if="item.subheadState==='down'" />
+                                <el-image style="width: 12px; height: 12px" :src="imgdata[1].url" :fit="fit" v-if="item.subheadState==='up'" />
+                            </span>
+                        </p>
+                    </div>
+                </div>
+            </div>
+            <div class="school-analyse-center-line">
+                <!-- <CommonLine :lineData="echartsData.line"></CommonLine> -->
+                <TestLine></TestLine>
+            </div>
+        </div>
+        <div class="school-analyse-bottom">
+            <div class="school-analyse-bottom-left">
+                <Gradepie></Gradepie>
+            </div>
+            <div class="school-analyse-bottom-right">
+                <ConventionPie :pieData="echartsData.subject"></ConventionPie>
+            </div>
+        </div>
+        <div class="school-list">
+            <p class="school-list-title">学校列表:</p>
+            <div class="schoolListbox" @click="showState='particular'">
+                <div class="school-badge">
+                    <el-image style="width: 80px; height: 80px;border-radius:50%" src="" fit="fill"></el-image>
+                </div>
+                <div class="school-data">
+                    <p class="school-data-name">成都锦江区外国语小学</p>
+                    <p><span class="school-data-title">学校教师数:</span><span class="school-data-num">1234</span></p>
+                    <p><span class="school-data-title">学校学生数:</span><span class="school-data-num">1234</span></p>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="schoolanalysebox" v-else-if="showState ==='particular'">
+        <div class="school-analyse-header">
+            <div class="school-analyse-headersbox">
+                <div class="school-analyse-headersbox-name">
+                    <span>成都市锦江区外国语小学</span>
+                    <span class="versions">专业版</span>
+                </div>
+                <div class="school-analyse-headersbox-basics">
+                    <div class="items-basics">
+                        <p class="items-basics-title">教师数:</p>
+                        <p class="items-basics-num">158</p>
+                    </div>
+                    <div class="items-basics">
+                        <p class="items-basics-title">学生数:</p>
+                        <p class="items-basics-num">158</p>
+                    </div>
+                    <div class="items-basics">
+                        <p class="items-basics-title">智慧教室:</p>
+                        <p class="items-basics-num">158</p>
+                    </div>
+                    <div class="items-basics">
+                        <p class="items-basics-title">课例数:</p>
+                        <p class="items-basics-num">158</p>
+                    </div>
+                </div>
+            </div>
+            <div class="backbtn">
+                <el-button size="small" @click="showState='all'">
+                    <svg class="backbtnicon" aria-hidden="true">
+                        <use xlink:href="#icon-fanhui3"></use>
+                    </svg>
+                    返回上一级
+                </el-button>
+            </div>
+        </div>
+        <div class="school-analyse-top">
+            <div class="school-analyse-top-left">
+                <Bar :barData="echartsData.bar1"></Bar>
+            </div>
+            <div class="school-analyse-top-center">
+                <Bar :barData="echartsData.bar2"></Bar>
+            </div>
+            <div class="school-analyse-top-right">
+                <Bar :barData="echartsData.bar3"></Bar>
+            </div>
+        </div>
+        <div class="school-analyse-center">
+            <div class="school-analyse-center-bottom">
+                <div :class="[item.classname,'bottom-item']" v-for="item in centerbasics">
+                    <div class="bottom-item-header">
+                        <svg class="icon" aria-hidden="true">
+                            <use :xlink:href="item.icon"></use>
+                        </svg>
+                        <span>{{item.title}}</span>
+                    </div>
+                    <div class="comparebox">
+                        <p class="comparebox-num">{{item.num}}</p>
+                        <p class="comparebox-subhead">
+                            <span class="comparebox-subhead-title">{{item.subheading}}:</span>
+                            <span class="comparebox-subhead-value">{{item.subheadingNum}}</span>
+                            <span class="comparebox-subhead-state">
+                                <el-image style="width: 12px; height: 12px" :src="imgdata[0].url" :fit="fit" v-if="item.subheadingState==='down'" />
+                                <el-image style="width: 12px; height: 12px" :src="imgdata[1].url" :fit="fit" v-if="item.subheadingState==='up'" />
+                            </span>
+                        </p>
+                        <p class="comparebox-subhead">
+                            <span class="comparebox-subhead-title">{{item.subhead}}:</span>
+                            <span class="comparebox-subhead-value">{{item.subheadNum}}</span>
+                            <span class="comparebox-subhead-state">
+                                <el-image style="width: 12px; height: 12px" :src="imgdata[0].url" :fit="fit" v-if="item.subheadState==='down'" />
+                                <el-image style="width: 12px; height: 12px" :src="imgdata[1].url" :fit="fit" v-if="item.subheadState==='up'" />
+                            </span>
+                        </p>
+                    </div>
+                </div>
+            </div>
+            <div class="school-analyse-center-line">
+                <!-- <CommonLine :lineData="echartsData.line"></CommonLine> -->
+                <TestLine></TestLine>
+            </div>
+        </div>
+        <div class="school-analyse-bottom">
+            <div class="school-analyse-bottom-left">
+                <Gradepie></Gradepie>
+            </div>
+            <div class="school-analyse-bottom-right">
+                <ConventionPie :pieData="echartsData.subject"></ConventionPie>
+            </div>
+        </div>
+        <!-- <div class="school-list">
+            <p class="school-list-title">学校列表:</p>
+            <div class="schoolListbox">
+                <div class="school-badge">
+                    <el-image style="width: 80px; height: 80px;border-radius:50%" src="" fit="fill"></el-image>
+                </div>
+                <div class="school-data">
+                    <p class="school-data-name">成都锦江区外国语小学</p>
+                    <p><span class="school-data-title">学校教师数:</span><span class="school-data-num">1234</span></p>
+                    <p><span class="school-data-title">学校学生数:</span><span class="school-data-num">1234</span></p>
+                </div>
+            </div>
+        </div> -->
+    </div>
+</template>
+<script>
+import { ref } from 'vue'
+import Bar from '@/components/echarts/commonBar.vue'
+import CommonLine from '@/components/echarts/commonLine.vue'
+import TestLine from '@/components/echarts/testLine.vue'
+import Gradepie from '@/components/echarts/gradePie.vue'
+import ConventionPie from '@/components/echarts/conventionPie.vue'
+import * as echarts from 'echarts'
+export default {
+    components: {
+        Bar,
+        CommonLine,
+        TestLine,
+        Gradepie,
+        ConventionPie,
+    },
+    setup() {
+        let showState = ref('all')
+        let headerbasics = ref([
+            { id: 1, title: '参与管理的学校', num: '8', subheading: '产生有效数据:', subnum: 859 },
+            { id: 1, title: '学校教师总数', num: '456', subheading: '本月新增', subnum: 3 },
+            { id: 1, title: '学校学生总数', num: '1670', subheading: '本月新增', subnum: 10 },
+            { id: 1, title: '课例总数', num: '762', subheading: '本月新增', subnum: 130 },
+        ])
+        let echartsData = ref({
+            bar1: {
+                title: {
+                    text: '课例总计:1657',
+                    left: '40%',
+                    fontSize: 16,
+                    textStyle: {
+                        color: 'rgba(112, 103, 245, 0.3)',
+                    },
+                },
+                timeline: {
+                    axisType: 'category',
+                    show: false,
+                    autoPlay: false,
+                    playInterval: 1000,
+                    data: ['2022.01', '2022.02', '2022.03', '2022.04', '2022.05'],
+                },
+                grid: {
+                    top: '25%',
+                    bottom: '0%',
+                    left: '3%',
+                    right: '3%',
+                    containLabel: true,
+                },
+                xAxis: [
+                    {
+                        type: 'category',
+                        axisLabel: {
+                            show: true,
+                            interval: 0,
+                            rotate: 0,
+                            textStyle: {
+                                color: '#A6D3FD',
+                                fontSize: 14,
+                            },
+                        },
+                        axisPointer: {
+                            show: true,
+                            type: 'shadow',
+                        },
+                        data: ['2022.01', '2022.02', '2022.03', '2022.04', '2022.05'],
+                    },
+                ],
+                yAxis: [
+                    {
+                        type: 'value',
+                        axisLabel: {
+                            formatter: '{value}',
+                        },
+                        axisLine: {
+                            show: false,
+                        },
+                        axisTick: {
+                            show: false,
+                        },
+
+                        splitLine: {
+                            show: false,
+                        },
+                    },
+                ],
+                tooltip: {
+                    trigger: 'axis',
+                    axisPointer: {
+                        // 坐标轴指示器,坐标轴触发有效
+                        type: 'shadow', // 默认为直线,可选为:'line' | 'shadow'
+                    },
+                },
+                //变量则写在options中
+                series: [
+                    {
+                        name: '课例',
+                        type: 'bar',
+                        barWidth: '20',
+                        data: [385, 120, 336, 192, 450, 568],
+                        itemStyle: {
+                            //lenged文本
+                            opacity: 1,
+                            color: function () {
+                                return new echarts.graphic.LinearGradient(
+                                    0,
+                                    0,
+                                    1,
+                                    0,
+                                    [
+                                        {
+                                            offset: 0,
+                                            color: 'rgba(162, 155, 254,0.2)', // 0% 处的颜色
+                                        },
+                                        {
+                                            offset: 1,
+                                            color: 'rgba(112, 103, 245, 0.5)', // 100% 处的颜色
+                                        },
+                                    ],
+                                    false
+                                )
+                            },
+                        },
+                    },
+                ],
+            },
+            bar2: {
+                title: {
+                    text: '活动总计:1058',
+                    left: '40%',
+                    fontSize: 16,
+                    textStyle: {
+                        color: 'rgba(255, 117, 26, 0.3)',
+                    },
+                },
+                timeline: {
+                    axisType: 'category',
+                    show: false,
+                    autoPlay: false,
+                    playInterval: 1000,
+                    data: ['2022.01', '2022.02', '2022.03', '2022.04', '2022.05'],
+                },
+                grid: {
+                    top: '25%',
+                    bottom: '0%',
+                    left: '3%',
+                    right: '3%',
+                    containLabel: true,
+                },
+                xAxis: [
+                    {
+                        type: 'category',
+                        axisLabel: {
+                            show: true,
+                            interval: 0,
+                            rotate: 0,
+                            textStyle: {
+                                color: '#A6D3FD',
+                                fontSize: 14,
+                            },
+                        },
+                        axisPointer: {
+                            show: true,
+                            type: 'shadow',
+                        },
+                        data: ['2022.01', '2022.02', '2022.03', '2022.04', '2022.05'],
+                    },
+                ],
+                yAxis: [
+                    {
+                        type: 'value',
+                        axisLabel: {
+                            formatter: '{value}',
+                        },
+                        axisLine: {
+                            show: false,
+                        },
+                        axisTick: {
+                            show: false,
+                        },
+
+                        splitLine: {
+                            show: false,
+                        },
+                    },
+                ],
+                tooltip: {
+                    trigger: 'axis',
+                    axisPointer: {
+                        // 坐标轴指示器,坐标轴触发有效
+                        type: 'shadow', // 默认为直线,可选为:'line' | 'shadow'
+                    },
+                },
+                //变量则写在options中
+                series: [
+                    {
+                        name: '活动',
+                        type: 'bar',
+                        barWidth: '20',
+                        data: [385, 120, 336, 192, 450, 568],
+                        itemStyle: {
+                            //lenged文本
+                            opacity: 1,
+                            color: function () {
+                                return new echarts.graphic.LinearGradient(
+                                    0,
+                                    0,
+                                    1,
+                                    0,
+                                    [
+                                        {
+                                            offset: 0,
+                                            color: 'rgba(250, 177, 160,0.5)', // 0% 处的颜色
+                                        },
+                                        {
+                                            offset: 1,
+                                            color: 'rgba(255, 117, 26, 0.7)', // 100% 处的颜色
+                                        },
+                                    ],
+                                    false
+                                )
+                            },
+                        },
+                    },
+                ],
+            },
+            bar3: {
+                title: {
+                    text: '互动次数总计:1687',
+                    left: '40%',
+                    fontSize: 16,
+                    textStyle: {
+                        color: 'rgba(29, 227, 255,.6)',
+                    },
+                },
+                timeline: {
+                    axisType: 'category',
+                    show: false,
+                    autoPlay: false,
+                    playInterval: 1000,
+                    data: ['2022.01', '2022.02', '2022.03', '2022.04', '2022.05'],
+                },
+                grid: {
+                    top: '25%',
+                    bottom: '0%',
+                    left: '3%',
+                    right: '3%',
+                    containLabel: true,
+                },
+                xAxis: [
+                    {
+                        type: 'category',
+                        axisLabel: {
+                            show: true,
+                            interval: 0,
+                            rotate: 0,
+                            textStyle: {
+                                color: '#A6D3FD',
+                                fontSize: 14,
+                            },
+                        },
+                        axisPointer: {
+                            show: true,
+                            type: 'shadow',
+                        },
+                        data: ['2022.01', '2022.02', '2022.03', '2022.04', '2022.05'],
+                    },
+                ],
+                yAxis: [
+                    {
+                        type: 'value',
+                        axisLabel: {
+                            formatter: '{value}',
+                        },
+                        axisLine: {
+                            show: false,
+                        },
+                        axisTick: {
+                            show: false,
+                        },
+
+                        splitLine: {
+                            show: false,
+                        },
+                    },
+                ],
+                tooltip: {
+                    trigger: 'axis',
+                    axisPointer: {
+                        // 坐标轴指示器,坐标轴触发有效
+                        type: 'shadow', // 默认为直线,可选为:'line' | 'shadow'
+                    },
+                },
+                //变量则写在options中
+                series: [
+                    {
+                        name: '活动',
+                        type: 'bar',
+                        barWidth: '20',
+                        data: [385, 120, 336, 192, 450, 568],
+                        itemStyle: {
+                            //lenged文本
+                            opacity: 1,
+                            color: function () {
+                                return new echarts.graphic.LinearGradient(
+                                    0,
+                                    0,
+                                    1,
+                                    0,
+                                    [
+                                        {
+                                            offset: 0,
+                                            color: 'rgb(72, 219, 251,.4)', // 0% 处的颜色
+                                        },
+                                        {
+                                            offset: 1,
+                                            color: 'rgba(29, 227, 255,.9)', // 100% 处的颜色
+                                        },
+                                    ],
+                                    false
+                                )
+                            },
+                        },
+                    },
+                ],
+            },
+            line: {
+                tooltip: {
+                    trigger: 'axis',
+                    axisPointer: {
+                        type: 'cross',
+                        crossStyle: {
+                            color: '#999',
+                        },
+                        lineStyle: {
+                            type: 'dashed',
+                        },
+                    },
+                },
+                grid: {
+                    top: '25%',
+                    left: '2%',
+                    bottom: '5%',
+                    right: '2%',
+                    textStyle: {
+                        color: '#fff',
+                    },
+                    containLabel: true,
+                },
+                legend: {
+                    show: true,
+                    data: ['2022.01', '2022.02', '2022.03', '2022.04', '2022.05'],
+                    textStyle: {
+                        color: '#000',
+                    },
+                    right: '20%',
+                    top: '15%',
+                },
+                xAxis: {
+                    type: 'category',
+                    data: ['2022.01', '2022.02', '2022.03', '2022.04', '2022.05'],
+                    boundaryGap: false,
+                },
+                yAxis: {
+                    type: 'value',
+                },
+                series: [
+                    {
+                        name: '课例',
+                        type: 'line',
+                        symbolSize: 8,
+                        data: [100, 88, 80, 70, 63],
+                        areaStyle: {
+                            normal: {
+                                color: new echarts.graphic.LinearGradient(
+                                    0,
+                                    0,
+                                    0,
+                                    1,
+                                    [
+                                        {
+                                            offset: 0,
+                                            color: 'rgba(112, 103, 245, 0.3)',
+                                        },
+                                        {
+                                            offset: 1,
+                                            color: '#fff',
+                                        },
+                                    ],
+                                    false
+                                ),
+                            },
+                        },
+                        itemStyle: {
+                            normal: {
+                                color: 'rgba(112, 103, 245, 0.3)',
+                            },
+                        },
+                    },
+                    {
+                        name: '活动',
+                        type: 'line',
+                        symbolSize: 8,
+                        data: [43, 24, 22, 20, 10],
+                        areaStyle: {
+                            normal: {
+                                color: new echarts.graphic.LinearGradient(
+                                    0,
+                                    0,
+                                    0,
+                                    1,
+                                    [
+                                        {
+                                            offset: 0,
+                                            color: 'rgba(255, 117, 26, 0.3)',
+                                        },
+                                        {
+                                            offset: 1,
+                                            color: '#fff',
+                                        },
+                                    ],
+                                    false
+                                ),
+                            },
+                        },
+                        itemStyle: {
+                            normal: {
+                                color: 'rgba(255, 117, 26, 0.3)',
+                            },
+                        },
+                    },
+                    {
+                        name: '互动',
+                        type: 'line',
+                        symbolSize: 8,
+                        data: [88, 80, 63, 20, 10],
+                        areaStyle: {
+                            normal: {
+                                color: new echarts.graphic.LinearGradient(
+                                    0,
+                                    0,
+                                    0,
+                                    1,
+                                    [
+                                        {
+                                            offset: 0,
+                                            color: 'rgba(29, 227, 255, 0.3)',
+                                        },
+                                        {
+                                            offset: 1,
+                                            color: '#fff',
+                                        },
+                                    ],
+                                    false
+                                ),
+                            },
+                        },
+                        itemStyle: {
+                            normal: {
+                                color: 'rgba(29, 227, 255, 0.3)',
+                            },
+                        },
+                    },
+                ],
+            },
+            subject: {
+                // color: ['#ef5777', '#0fbcf9', '#0be881', '#575fcf', '#ffd32a', '#55E6C1', '#3742fa', '#B33771', '#F8EFBA'],
+                color: ['#3fb1e3', '#6be6c1', '#626c91', '#a0a7e6', '#c4ebad', '#96dee8'],
+                title: {
+                    text: '科目占比',
+                    textStyle: {
+                        fontSize: '12',
+                        color: '#b2bec3',
+                    },
+                },
+                legend: {
+                    right: '10%',
+                    orient: 'vertical',
+                    itemWidth: 8,
+                    itemHeight: 8, // 修改icon图形大小
+                    textStyle: {
+                        fontSize: 12,
+                        color: '#333',
+                    },
+                },
+                series: [
+                    {
+                        name: '科目占比',
+                        type: 'pie',
+                        radius: '80%',
+                        center: ['30%', '50%'],
+                        itemStyle: {
+                            borderRadius: 2,
+                        },
+                        label: {
+                            normal: {
+                                position: 'inner',
+                                show: false,
+                            },
+                        },
+                        data: [
+                            { name: '语文', value: 198 },
+                            { name: '数学', value: 238 },
+                            { name: '英语', value: 98 },
+                            { name: '历史', value: 18 },
+                            { name: '思想品德', value: 28 },
+                            { name: '音乐', value: 38 },
+                            { name: '美术', value: 48 },
+                        ],
+                    },
+                ],
+            },
+        })
+        let imgdata = ref([
+            { id: 1, url: require('@/assets/img/down.png') },
+            { id: 2, url: require('@/assets/img/up.png') },
+        ])
+        let centerbasics = ref([
+            {
+                id: 1,
+                title: '课例',
+                num: 579,
+                icon: '#icon-kecheng-1',
+                subheading: '昨日对比',
+                subheadingNum: '-32.00%',
+                subheadingState: 'down',
+                subhead: '上月对比',
+                subheadNum: '-3.00%',
+                subheadState: 'down',
+                classname: 'purples',
+            },
+            {
+                id: 2,
+                title: '活动',
+                num: 379,
+                icon: '#icon-huodongxiangqu',
+                subheading: '昨日对比',
+                subheadingNum: '7.00%',
+                subheadingState: 'up',
+                subhead: '上月对比',
+                subheadNum: '-18.00%',
+                subheadState: 'down',
+                classname: 'oranges',
+            },
+            {
+                id: 3,
+                title: '互动',
+                num: 679,
+                icon: '#icon-zhishihudong',
+                subheading: '昨日对比',
+                subheadingNum: '7.00%',
+                subheadingState: 'up',
+                subhead: '上月对比',
+                subheadNum: '-18.00%',
+                subheadState: 'down',
+                classname: 'blues',
+            },
+            {
+                id: 3,
+                title: '总数据',
+                num: 2379,
+                icon: '#icon-shuju1',
+                subheading: '上月对比',
+                subheadingNum: '7.00%',
+                subheadingState: 'up',
+                subhead: '去年对比',
+                subheadNum: '-18.00%',
+                subheadState: 'down',
+                classname: 'total',
+            },
+        ])
+        return { headerbasics, echartsData, imgdata, centerbasics, showState }
+    },
+}
+</script>
+<style scoped>
+.schoolanalysebox {
+    width: 100%;
+}
+.school-analyse-header {
+    width: 100%;
+}
+.school-analyse-headersbox {
+    background-color: rgb(81, 86, 184);
+    background-image: url('../../assets/img/school-background.jpg');
+    background-repeat: no-repeat;
+    background-size: cover;
+    background-position: center center;
+    display: flex;
+    justify-content: space-between;
+    border-radius: 10px;
+    flex-wrap: wrap;
+}
+.school-analyse-headersbox-item {
+    width: 387px;
+    height: 150px;
+    line-height: 1;
+    padding-left: 0px;
+    padding-right: 8px;
+    border-right: 1px dashed rgba(200, 214, 229, 0.7);
+}
+.item-basics {
+    padding: 7% 10%;
+}
+.item-title,
+.item-subhead {
+    font-size: 15px;
+    color: hsla(0, 0%, 100%, 0.9);
+}
+.item-num {
+    font-size: 22px;
+    font-weight: 700;
+    color: hsla(0, 0%, 100%, 0.9);
+}
+.school-analyse-top {
+    width: 100%;
+    display: flex;
+    justify-content: space-between;
+    background: #fff;
+    margin-top: 1%;
+    flex-wrap: wrap;
+}
+.school-analyse-top-left,
+.school-analyse-top-center,
+.school-analyse-top-right {
+    width: 30%;
+    height: 240px;
+    border-right: 1px dashed rgba(200, 214, 229, 0.7);
+    margin: 1%;
+    padding-right: 1%;
+}
+.school-analyse-top-right {
+    border-right: 0px;
+}
+.school-analyse-center {
+    width: 100%;
+    height: 40vh;
+    margin-top: 1%;
+    background: #fff;
+    padding: 1%;
+}
+.school-analyse-center-bottom {
+    width: 100%;
+    display: flex;
+    justify-content: space-between;
+}
+.bottom-item {
+    width: 24%;
+    height: 160px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    flex-wrap: wrap;
+    background-color: rgba(112, 103, 245, 0.3);
+    border-radius: 10px;
+}
+.comparebox-subhead {
+    font-size: 14px;
+    margin-bottom: 2px;
+}
+.bottom-item-header {
+    width: 100%;
+    line-height: 20px;
+    margin-top: 10px;
+    font-size: 16px;
+}
+.comparebox-num {
+    font-size: 24px;
+    font-weight: 700;
+}
+.comparebox-subhead-value,
+.comparebox-subhead-state {
+    margin-left: 5px;
+}
+.school-analyse-center-line {
+    width: 100%;
+    height: 21vh;
+}
+.school-analyse-bottom {
+    width: 100%;
+    display: flex;
+    justify-content: space-between;
+    margin-top: 1%;
+    margin-bottom: 1%;
+}
+.school-analyse-bottom-left,
+.school-analyse-bottom-right {
+    width: 48%;
+    height: 230px;
+    background-color: #fff;
+}
+.school-list {
+    width: 100%;
+    height: 300px;
+    padding: 1%;
+    background-color: #fff;
+    margin-top: 1%;
+    text-align: left;
+    margin-bottom: 1%;
+}
+.school-list-title {
+    font-size: 14px;
+    color: #b2bec3;
+    font-weight: 700;
+}
+.schoolListbox {
+    display: flex;
+    justify-content: space-between;
+    flex-wrap: wrap;
+    width: 20%;
+    border: 1px solid #ccc;
+    border-radius: 10px;
+    padding: 1% 0%;
+    cursor: pointer;
+}
+.school-badge {
+    width: 40%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+}
+.school-data {
+    width: 58%;
+}
+.school-data-name {
+    font-size: 16px;
+    font-weight: 700;
+}
+.school-go {
+    width: 10%;
+    font-size: 12px;
+}
+.school-data-title {
+    font-size: 14px;
+    color: #b2bec3;
+}
+.school-data-num {
+    font-size: 14px;
+    color: #636e72;
+}
+.school-analyse-headersbox-name {
+    width: 100%;
+    font-size: 28px;
+    line-height: 28px;
+    color: hsla(0, 0%, 100%, 0.9);
+    font-weight: 700;
+    text-align: center;
+    margin-bottom: 2px;
+    margin-top: 2%;
+}
+.school-analyse-headersbox-basics {
+    width: 100%;
+    display: flex;
+    justify-content: space-around;
+}
+.items-basics {
+    width: 23%;
+    padding: 1%;
+    text-align: center;
+}
+.items-basics-title {
+    font-size: 16px;
+    color: hsla(0, 0%, 100%, 0.9);
+    padding-left: 10px;
+}
+.items-basics-num {
+    font-size: 22px;
+    color: hsla(0, 0%, 100%, 0.9);
+    font-weight: 700;
+}
+.versions {
+    font-size: 12px;
+    padding: 4px;
+    border: 1px solid #ccc;
+    line-height: 16px;
+    border-radius: 5px;
+    vertical-align: top;
+    margin-left: 10px;
+    background-color: rgba(29, 227, 255, 0.7);
+}
+.backbtn {
+    position: absolute;
+    left: 1%;
+    top: 0.5%;
+}
+.backbtnicon {
+    width: 1em;
+    height: 1em;
+    vertical-align: -0.1em;
+    fill: currentColor;
+    overflow: hidden;
+    margin-right: 2px;
+}
+.purples {
+    background: rgba(112, 103, 245, 0.3);
+}
+.oranges {
+    background: rgba(255, 117, 26, 0.3);
+}
+.blues {
+    background: rgba(29, 227, 255, 0.3);
+}
+.total {
+    background: rgba(26, 125, 255, 0.3);
+}
+</style>

+ 933 - 0
TEAMModelBI/ClientApp/src/view/schoolmanage/schoolManege.vue

@@ -0,0 +1,933 @@
+<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 ===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>
+                    </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">
+                    <template #default="scope">
+                        <div class="have-modules">
+                            <div v-for="item in patternIcon" :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">
+                            <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>
+                    </template>
+                </el-table-column>
+            </el-table>
+        </div>
+    </div>
+    <!--学校列表end-->
+    <!--编辑学校页面-->
+    <div class="schoolDeatils">
+        <div class="backbtn" v-if="models==='details' && PowerShow">
+            <el-button type="primary" size="small" @click="schoolClose">
+                <svg class="back-icon" aria-hidden="true">
+                    <use xlink:href="#icon-fanhui"></use>
+                </svg>
+                返回
+            </el-button>
+            <el-button class="changebtn" v-if="store.state.changbtnShow && changebtns" @click="getSetschool()">
+                <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>
+        </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">
+                                        <!-- <span style="float: left">{{ item.mobile }}-{{item.name}}</span>
+                            <i style="float: right;color: var(--el-text-color-secondary);font-size: 13px;" v-show="item.isexist === false">
+                                <svg class="school-admin-check" aria-hidden="true">
+                                    <use xlink:href="#icon-zhengque"></use>
+                                </svg>
+                            </i> -->
+                                    </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 } from 'element-plus'
+import { useRouter } from 'vue-router'
+import SetSchool from '../schoolServe/setschool.vue'
+// import Impower from './impower.vue'
+// import Classpower from './classpower.vue'
+const optionsData = option
+export default {
+    components: {
+        SetSchool,
+        // Impower,
+        // Classpower,
+    },
+    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' },
+            { 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' },
+        ])
+        provinceOptions.value.optionInfo = optionsData
+        console.log(store.state.point)
+        //所有学校列表
+        function getAllschool() {
+            proxy.$api.getSchooldata({}).then((res) => {
+                console.log(res, '获取学校列表')
+                //处理关联管家  拼内容
+                for (let i in res.schoolAssists) {
+                    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 + ','
+                        }
+                    }
+                }
+                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)
+            // let checkboxInof = [
+            //     { value: false, name: '小学' },
+            //     { value: false, name: '初中' },
+            //     { value: false, name: '高中' },
+            //     { value: false, name: '职高' },
+            //     { value: false, name: '大学' },
+            // ]
+            // nowPitchdata.value.period = []
+            // nowPitchdata.value.period.push(...checkboxInof)
+            // if (data.period.length) {
+            //     for (let i in data.period) {
+            //         data.period[i].name === '小学' ? (nowPitchdata.value.period[0].value = true) : ''
+            //         data.period[i].name === '初中' ? (nowPitchdata.value.period[1].value = true) : ''
+            //         data.period[i].name === '高中' ? (nowPitchdata.value.period[2].value = true) : ''
+            //         data.period[i].name === '职高' ? (nowPitchdata.value.period[3].value = true) : ''
+            //         data.period[i].name === '大学' ? (nowPitchdata.value.period[4].value = true) : ''
+            //     }
+            // }
+            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() {
+            routerInfo.push({ path: '/home/analyse', query: {} })
+        }
+        function schooldataSort(a, b) {
+            tableData.value.sort(function (a, b) {
+                return b.lessonCount - a.lessonCount
+            })
+        }
+        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,
+        }
+    },
+}
+</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: 14px;
+    height: 14px;
+    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--top {
+    margin-top: 5px;
+}
+.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>

+ 1 - 1
TEAMModelBI/ClientApp/src/view/systemConfig/operate.vue

@@ -154,7 +154,7 @@ export default {
             console.log('触发子组件的修改方法')
             let rolesinfo = identityValue.value
             let permissionsinfo = nowAuthority.value
-            let data = { partitionKey: nowUsers.value.partitionKey, user: nowUsers.value.userId, tmdId: nowUsers.value.tmdId, permissions: permissionsinfo, roles: rolesinfo }
+            let data = { partitionKey: nowUsers.value.partitionKey, userId: nowUsers.value.userId, tmdId: nowUsers.value.tmdId, permissions: permissionsinfo, roles: rolesinfo }
             proxy.$api
                 .setRolesandPower(data)
                 .then((res) => {

+ 10 - 1
TEAMModelBI/ClientApp/src/view/systemConfig/setAdmin.vue

@@ -111,6 +111,9 @@ export default {
         }
         function removeadmin(value) {
             console.log(value)
+            if (adminData.value.length <= 1) {
+                return ElMessage.info('管理员至少存在一位,无法移除当前人员')
+            }
             ElMessageBox.confirm(`请问您确定从系统管理员列表中移除 ${value.name}(${value.mobile}) 吗?`, '移除管理员', {
                 confirmButtonText: proxy.$t(`commonMsg.confirm`),
                 cancelButtonText: proxy.$t(`commonMsg.closes`),
@@ -121,7 +124,13 @@ export default {
                 proxy.$api
                     .removeAdmin(data)
                     .then((res) => {
-                        res.state === 200 ? (ElMessage.success('操作成功'), getAdmin()) : ''
+                        res.state === 200
+                            ? (ElMessage.success('操作成功'), getAdmin())
+                            : res.state === 403
+                            ? ElMessage.error('管理员至少存在一位,无法移除当前人员')
+                            : res.state === 1
+                            ? ElMessage.error('无法删除自己,操作失败')
+                            : ''
                     })
                     .catch((error) => {
                         ElMessage.error('删除失败')

+ 56 - 25
TEAMModelBI/ClientApp/src/view/teachermanage/manage.vue

@@ -25,7 +25,7 @@
         </div>
     </div>
     <div class="refreshbtn">
-        <div class="totalnum">人数:<span>{{tableData.length}}</span></div>
+        <div class="totalnum">人数:<span>{{tableData.length}}</span></div>
         <div class="refresh-btn">
             <el-button @click="refresh()">
                 <svg class="refreshbtn-icon" aria-hidden="true">
@@ -51,34 +51,31 @@
             </el-table-column>
             <el-table-column prop="name" :label="$t(`personnelManagement.personnelTable.name`)" sortable align="center" />
             <!-- <el-table-column prop="isexist" label="状态" width="250" align="center" /> -->
-            <el-table-column prop="isexist" :label="$t(`personnelManagement.personnelTable.statename`)" align="center">
+
+            <el-table-column prop="mobile" :label="$t(`personnelManagement.personnelTable.phone`)" align="center" />
+            <el-table-column prop="tmdId" :label="$t(`personnelManagement.personnelTable.tmdids`)" align="center">
+            </el-table-column>
+            <el-table-column prop="identityName" label="平台职位" align="center">
                 <template #default="scope">
-                    <div class="activatebox">
-                        <svg class="icon-table" aria-hidden="true" v-if="scope.row.userstate ===false">
-                            <use xlink:href="#icon-jihuoma"></use>
-                        </svg>
-                        <svg class="icon-table" aria-hidden="true" v-if="scope.row.userstate ===true">
-                            <use xlink:href="#icon-jihuochenggong"></use>
-                        </svg>
+                    <div v-if="scope.row.identityName">
+                        <span class="schoolNums">{{scope.row.identityName}}</span>
                     </div>
-                    <el-popover>
-                        <template #reference>
-                            <div class="name-wrapper failed" v-if="scope.row.userstate ===false">
-                                <!-- <el-tag size="medium"></el-tag> -->
-                                {{$t('personnelManagement.personnelTable.notactivated')}}
-                            </div>
-                            <div class="name-wrapper pass" v-else="scope.row.userstate ===true">
-                                <!-- <el-tag size="medium"></el-tag> -->
-                                {{$t('personnelManagement.personnelTable.activated')}}
-                            </div>
-                        </template>
-                    </el-popover>
+                    <div v-else>无</div>
                 </template>
             </el-table-column>
-            <el-table-column prop="mobile" :label="$t(`personnelManagement.personnelTable.phone`)" align="center" />
-            <el-table-column prop="tmdId" :label="$t(`personnelManagement.personnelTable.tmdids`)" align="center">
+            <el-table-column label="管理学校数" align="center" sortable :sort-method="schoolSort">
+                <template #default="scope">
+                    <div v-if="scope.row.handleSchools.length !==0">
+                        <svg class="qxmages" aria-hidden="true">
+                            <use xlink:href="#icon-xuexiao4"></use>
+                        </svg>
+                        <!-- {{$t(`personnelManagement.personnelTable.operatecontent`)}} -->
+                        <span class="schoolNums">{{scope.row.handleSchools.length}}</span>
+                    </div>
+                    <div v-else>暂无</div>
+                </template>
             </el-table-column>
-            <el-table-column prop="title" :label="$t(`personnelManagement.personnelTable.position`)" sortable align="center" />
+            <el-table-column prop="title" label="公司职位" align="center" />
             <el-table-column :label="$t(`personnelManagement.personnelTable.operate`)" align="center">
                 <template #default="scope">
                     <el-button type="primary" size="small" @click="handleClick(scope.$index,tableData)">
@@ -98,7 +95,7 @@
                 <el-tab-pane label="权限/身份" name="first">
                     <Operates ref="roaming" :userdata="nowUser" @changeShow="changeState"></Operates>
                 </el-tab-pane>
-                <el-tab-pane label="关联学校" name="second">
+                <el-tab-pane label="关联学校" name="second" v-if="nowUser.handleRoles.includes('assist') || nowUser.handleRoles.includes('admin')">
                     <Correlation :userdata="nowUser"></Correlation>
                 </el-tab-pane>
             </el-tabs>
@@ -228,8 +225,18 @@ export default {
             proxy.$api.getEveryinfo({}).then((res) => {
                 console.log(res, '列表返回')
                 for (let i in res.ddUserInfos) {
+                    res.ddUserInfos[i].identityName = ''
                     res.ddUserInfos[i].id = Number(i) + 1
                     res.ddUserInfos[i].userstate = res.ddUserInfos[i].tmdId == '' ? false : true
+                    res.ddUserInfos[i].handleRoles.length !== 0
+                        ? res.ddUserInfos[i].handleRoles.forEach((item) => {
+                              item == 'assist'
+                                  ? (res.ddUserInfos[i].identityName = res.ddUserInfos[i].identityName + '顾问' + ',')
+                                  : item == 'admin'
+                                  ? (res.ddUserInfos[i].identityName = res.ddUserInfos[i].identityName + '系统管理员' + ',')
+                                  : ''
+                          })
+                        : ''
                 }
                 tableData.value = res.ddUserInfos
                 original.value = res.ddUserInfos
@@ -251,6 +258,17 @@ export default {
                       for (let i in res.ddUserInfos) {
                           res.ddUserInfos[i].id = Number(i) + 1
                           res.ddUserInfos[i].userstate = res.ddUserInfos[i].tmdId == '' ? false : true
+                          //   res.ddUserInfos[i].identityName = res.ddUserInfos[i].roles === 'assist' ? '顾问' : res.ddUserInfos[i].roles === 'admin' ? '系统管理员' : ''
+                          res.ddUserInfos[i].identityName = ''
+                          res.ddUserInfos[i].handleRoles.length !== 0
+                              ? res.ddUserInfos[i].handleRoles.forEach((item) => {
+                                    item == 'assist'
+                                        ? (res.ddUserInfos[i].identityName = res.ddUserInfos[i].identityName + '顾问' + ',')
+                                        : item == 'admin'
+                                        ? (res.ddUserInfos[i].identityName = res.ddUserInfos[i].identityName + '系统管理员' + ',')
+                                        : ''
+                                })
+                              : ''
                       }
                       tableData.value = res.ddUserInfos
                       loading.value = false
@@ -416,6 +434,12 @@ export default {
             console.log(roaming.value)
             roaming.value.notarizeChange()
         }
+        function schoolSort(val1, val2) {
+            tableData.value.sort(function (val1, val2) {
+                console.log(val1, val2)
+                return val2.handleSchools.length - val1.handleSchools.length
+            })
+        }
         watch(filterText, (newdata) => {
             if (newdata.trim().length !== 0) {
                 debounce(personnelSearch, 500)
@@ -457,6 +481,7 @@ export default {
             changeState,
             changeSubmit,
             roaming,
+            schoolSort,
         }
     },
 }
@@ -702,6 +727,12 @@ export default {
     top: 8%;
     right: 2%;
 }
+.schoolNums {
+    font-size: 14px;
+    color: #74b9ff;
+    margin-left: 5px;
+    font-weight: 600;
+}
 </style>
 <style>
 .manatips .el-dialog__header,

+ 10 - 7
TEAMModelBI/ClientApp/src/view/teachermanage/traitmanage.vue

@@ -51,7 +51,7 @@
         <!--选中方案的内容-->
         <div v-else-if="treeModel==='tree'" class="treeinfo">
             <div class="backbtn">
-                <el-button type="primary" icon="el-icon-back" size="mini" @click="treeModel='default'">返回</el-button>
+                <el-button type="primary" icon="el-icon-back" size="small" @click="treeModel='default'">返回</el-button>
             </div>
             <div class="ability-box">
                 <div class="abilitys">
@@ -890,18 +890,20 @@ export default {
     color: #303133;
 }
 .backbtn {
-    width: 75px;
+    width: 55px;
     height: 30px;
-    line-height: 5px;
     text-align: left;
     box-shadow: 0 2px 5px #e9e9e9;
+    position: absolute;
+    top: 0.5%;
+    right: 1%;
 }
 .ability-box {
-    width: 352px;
+    width: 355px;
     height: 85vh;
     border-right: 1px dashed rgb (180, 178, 178);
-    margin-top: 30px;
-    margin-left: -5%;
+    margin-top: 35px;
+    margin-left: 0%;
     overflow: hidden;
 }
 .ability-box:hover {
@@ -909,7 +911,7 @@ export default {
 }
 .tree-box {
     width: 82%;
-    padding: 0% 2% 0% 0.5%;
+    padding: 0.5% 2% 0% 0.5%;
     display: inline-block;
 }
 .abilitys {
@@ -943,6 +945,7 @@ export default {
     line-height: 20px;
     text-align: left;
     display: flex;
+    position: relative;
 }
 .tabinfos {
     position: absolute;

+ 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 dateDay = dateTime.ToString("yyyyMMdd"); //获取当天的日期
+
+            Dictionary<string, int> allDays = new();  //所有在线人数
+            Dictionary<string, int> tchDays = new();  //教师在线人数
+            Dictionary<string, int> stuDays = new();  //学生在线人数
+            Dictionary<string, 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 = DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {key}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH:mm");                    
+                    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 '{daySt}' and RowKey le '{dayEt}' and Teacher ne 0";
+                List<HourLogin> hourLoginsTch = await table.QueryWhereString<HourLogin>(tableSqlTch);
+
+                var hourTchCnt = hourLoginsTch.GroupBy(x => x.Hour).Select(k => new { key = int.Parse(k.Key.ToString().Substring(8, 2)), value = k.Count() }).ToList();
+                if (hourTchCnt.Count > 0)
+                {
+                    foreach (var item in hourTchCnt)
+                    {
+                        await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:IES:teacher:{dateDay}", $"{item.key}", item.value);//存一天24小时
+                        var hour = DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {item.key}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH:mm");
+                        tchDays.Add(hour, item.value);
+                        if (allDays.ContainsKey(hour))
+                            allDays[hour] = (allDays[hour] + item.value);
+                        else
+                            allDays.Add(hour, item.value);
+                    }
+                }
+            }
+
+            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 = DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {key}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH:mm");
+                    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 '{daySt}' and RowKey le '{dayEt}' and Student ne 0";
+                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 (hourStuCnt.Count > 0)
+                {
+                    foreach (var item in hourStuCnt)
+                    {
+                        await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:IES:student:{dateDay}", $"{item.key}", item.value);//存一天24小时
+                        var hour = DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {item.key}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH:mm");
+                        stuDays.Add(hour, item.value);
+                        if (allDays.ContainsKey(hour))
+                            allDays[hour] = (allDays[hour] + item.value);
+                        else
+                            allDays.Add(hour, item.value);
+                    }
+                }
+            }
+
+            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 = DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {key}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH:mm");
+                    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 '{daySt}' and RowKey le '{dayEt}' and TmdUser ne 0";
+                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 (hourTmdCnt.Count > 0)
+                {
+                    foreach (var item in hourTmdCnt)
+                    {
+                        await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:IES:tmduser:{dateDay}", $"{item.key}", item.value);//存一天24小时
+                        var hour = DateTime.SpecifyKind(Convert.ToDateTime($"{dateTime.Year}/{dateTime.Month}/{ dateTime.Day} {item.key}:00:00"), DateTimeKind.Utc).ToLocalTime().ToString("HH:mm");
+                        tmdDays.Add(hour, item.value);
+                        if (allDays.ContainsKey(hour))
+                            allDays[hour] = (allDays[hour] + item.value);
+                        else
+                            allDays.Add(hour, item.value);
+                    }
+                }
+            }
+            
+            return Ok(new { state = 200,allDays = allDays.ToList(), tchDays=tchDays.ToList(), stuDays= stuDays.ToList(), tmdDays= tmdDays.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 = dateTime.Hour;   //当前小时
+
+            Dictionary<int, int> scLessCnt = new();  //学校课例
+            Dictionary<int, int> tecLessCnt = new();   //教师课例
+            Dictionary<int, int> yasterdayCnt = 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") }))
+                    {
+                        tecLessCnt.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);
+                yasterdayCnt.Add(i, hourLessCnt);
+            }
+
+            return Ok(new { state = 200, scLessCnt = scLessCnt.ToList(), tecLessCnt = tecLessCnt.ToList(), yasterdayCnt = yasterdayCnt.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; //硬体
+        }
+
+
+    }
+}

+ 86 - 85
TEAMModelBI/Controllers/BISchool/BatchSchoolController.cs

@@ -305,106 +305,98 @@ namespace TEAMModelBI.Controllers.BISchool
         {
             try
             {
-                jsonElement.TryGetProperty("tmdId", out JsonElement assistId);
+                jsonElement.TryGetProperty("tmdId", out JsonElement tmdId);
                 jsonElement.TryGetProperty("schoolCode", out JsonElement _schoolCode);
-                var cosmosClient = _azureCosmos.GetCosmosClient();
-                List<AssistSchool> schoolAssists = new();
-                //StringBuilder stringBuilder = new StringBuilder("select value(c) from c");
+                var cosmosClient = _azureCosmos.GetCosmosClient();                
+                int? pageSize = null;      //默认不指定返回大小        
+                string continuationToken = string.Empty;     //返给前端分页token           
+                string pageToken = default;//接受前端的分页Tolen
+                bool iscontinuation = false;//是否需要进行分页查询,默认不分页
+                List<AssistSchool> schoolAssists = new(); //返回学校列表集合
+                List<string> schoolIds = new();
                 StringBuilder stringBuilder = new("select c.id,c.code,c.schoolCode,c.name,c.region,c.province,c.city,c.dist,c.size,c.address,c.picture,c.type,c.scale,c.areaId,c.standard from c");
 
-                if (!string.IsNullOrEmpty($"{_schoolCode}"))
+                if (jsonElement.TryGetProperty("pageSize", out JsonElement jsonPageSize))
+                {
+                    if (!jsonPageSize.ValueKind.Equals(JsonValueKind.Undefined) && !jsonPageSize.ValueKind.Equals(JsonValueKind.Null) && jsonPageSize.TryGetInt32(out int tempPageSize))
+                    {
+                        pageSize = tempPageSize;
+                    }
+                }
+                if (pageSize != null && pageSize.Value > 0)
+                {
+                    iscontinuation = true;
+                }
+                if (jsonElement.TryGetProperty("contToken", out JsonElement ContToken))
                 {
-                    stringBuilder.Append($" where c.id='{_schoolCode}'");
+                    pageToken = ContToken.GetString();
                 }
 
-                List<string> schools = new();
-                if (!string.IsNullOrEmpty($"{assistId}"))
-                    schools = await CommonFind.FindSchoolIds(cosmosClient, $"{assistId}");
-                if (schools.Count > 0)
+                if (!string.IsNullOrEmpty($"{tmdId}")) 
                 {
-                    schoolAssists = await CommonFind.FindAssistSchools(cosmosClient, schools);
+                    schoolIds = await CommonFind.FindSchoolIds(cosmosClient, $"{tmdId}");
+                }
+
+                if (schoolIds.Count > 0)
+                {
+                    foreach (var id in schoolIds)
+                    {
+                        string sqlTxt = $"select c.id,c.code,c.schoolCode,c.name,c.region,c.province,c.city,c.dist,c.size,c.address,c.picture,c.type,c.scale,c.areaId,c.standard from c where c.id='{id}'";
+                        await foreach (var itemSchool in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<AssistSchool>(queryText: sqlTxt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
+                        {
+                            schoolAssists.Add(itemSchool);
+                        }
+                    }
                 }
                 else
                 {
-                    await foreach (var itemSchool in cosmosClient.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<AssistSchool>(queryText: stringBuilder.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
+                    if (!string.IsNullOrEmpty($"{_schoolCode}"))
                     {
-                        schoolAssists.Add(itemSchool);
+                        stringBuilder.Append($" where c.id='{_schoolCode}'");
                     }
 
-                    schoolAssists.ForEach(async school =>
+                    await foreach (var itemSchool in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: stringBuilder.ToString(), continuationToken: pageToken, requestOptions: new QueryRequestOptions() { MaxItemCount = pageSize, PartitionKey = new PartitionKey("Base") }))
                     {
-                        var response = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(school.id, new PartitionKey("ProductSum"));
-                        if (response.Status == 200)
+                        using var json = await JsonDocument.ParseAsync(itemSchool.ContentStream);
+                        if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetInt16() > 0)
                         {
-                            using var json = await JsonDocument.ParseAsync(response.ContentStream);
-                            school.serial = json.RootElement.GetProperty("serial").GetArrayLength();
-                            school.service = json.RootElement.GetProperty("service").GetArrayLength();
-                            school.hard = json.RootElement.GetProperty("hard").GetArrayLength();
+                            foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                            {
+                                schoolAssists.Add(obj.ToObject<AssistSchool>());
+                            }
+                            if (iscontinuation)
+                            {
+                                continuationToken = itemSchool.GetContinuationToken();
+                                break;
+                            }
                         }
-
-                        school.assists = await CommonFind.FindSchoolRoles(cosmosClient, school.id, "assist");
-                        school.lessonCount = await CommonFind.FindTotals(cosmosClient, $"select count(c.id) as totals from c ", "School", $"LessonRecord-{school.id}");
-                    });
+                    }
                 }
+                schoolAssists.ForEach(async school =>
+                {
+                    var response = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(school.id, new PartitionKey("ProductSum"));
+                    if (response.Status == 200)
+                    {
+                        using var json = await JsonDocument.ParseAsync(response.ContentStream);
+                        if (json.RootElement.TryGetProperty("serial", out JsonElement serial) && !serial.ValueKind.Equals(JsonValueKind.Null))
+                        {
+                            school.serial = serial.ToObject<List<SchoolProductSumData>>().Select(x => x.prodCode).ToList();
+                        }
+                        if (json.RootElement.TryGetProperty("service", out JsonElement service) && !service.ValueKind.Equals(JsonValueKind.Null))
+                        {
+                            school.service = service.ToObject<List<SchoolProductSumData>>().Select(x => x.prodCode).ToList();
+                        }
+                        if (json.RootElement.TryGetProperty("hard", out JsonElement hard) && !hard.ValueKind.Equals(JsonValueKind.Null))
+                        {
+                            school.hard = hard.ToObject<List<SchoolProductSumDataHard>>().Select(x => x.prodCode).ToList();
+                        }
+                    }
 
-                return Ok(new { state = 200, schoolAssists });
-
-                //if (schools.Count > 0)
-                //{
-                //    schoolAssists = await CommonFind.FindAssistSchools(cosmosClient, schools);
-                //}
-                //else
-                //{
-                //    await foreach (var itemSchool in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: stringBuilder.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
-                //    {
-                //        using var jsonSchool = await JsonDocument.ParseAsync(itemSchool.ContentStream);
-                //        foreach (var objSchool in jsonSchool.RootElement.GetProperty("Documents").EnumerateArray())
-                //        {
-                //            var schoolId = objSchool.GetProperty("id").GetString();
-                //            AssistSchool schoolAssist = new()
-                //            {
-                //                id = schoolId,
-                //                code = objSchool.GetProperty("code").GetString(),
-                //                schoolCode = objSchool.GetProperty("schoolCode").GetString(),
-                //                name = objSchool.GetProperty("name").GetString(),
-                //                region = objSchool.GetProperty("region").GetString(),
-                //                province = objSchool.GetProperty("province").GetString(),
-                //                city = objSchool.GetProperty("city").GetString(),
-                //                dist = objSchool.GetProperty("dist").GetString(),
-                //                size = objSchool.GetProperty("size").GetInt32(),
-                //                address = objSchool.GetProperty("address").GetString(),
-                //                picture = objSchool.GetProperty("picture").GetString(),
-                //                type = objSchool.GetProperty("type").GetInt32(),
-                //                //scale = objSchool.GetProperty("scale").GetInt32(),
-                //                areaId = objSchool.GetProperty("areaId").GetString(),
-                //                standard = objSchool.GetProperty("standard").GetString(),
-                //            };
-
-                //            try { schoolAssist.scale = objSchool.GetProperty("scale").GetInt32(); }
-                //            catch { schoolAssist.scale = 0; }
-
-                //            //var response = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(schoolId, new PartitionKey("ProductSum"));
-                //            //if (response.Status == 200)
-                //            //{
-                //            //    using var json = await JsonDocument.ParseAsync(response.ContentStream);
-                //            //    schoolAssist.serial = json.RootElement.GetProperty("serial").GetArrayLength();
-                //            //    schoolAssist.service = json.RootElement.GetProperty("service").GetArrayLength();
-                //            //    schoolAssist.hard = json.RootElement.GetProperty("hard").GetArrayLength();
-                //            //}
-
-                //            //schoolAssist.assists = await CommonFind.FindSchoolRoles(cosmosClient, schoolId, "assist");
-                //            schoolAssists.Add(schoolAssist);
-                //        }
-                //    }
-                //}
-                //List<AssistSchool> tempSchoolAssists = new();
-                //await foreach (var temp in GetSchools(cosmosClient, schoolAssists))
-                //{
-                //    tempSchoolAssists.AddRange(temp);
-                //}
-
-                //return Ok(new { state = 200, schoolAssists = tempSchoolAssists });
+                    school.assists = await CommonFind.FindSchoolRoles(cosmosClient, school.id, "assist");
+                    school.lessonCount = await CommonFind.FindTotals(cosmosClient, $"select count(c.id) as totals from c ", "School", $"LessonRecord-{school.id}");
+                });
 
+                return Ok(new { state = 200, continuationToken, schoolAssists, });
             }
             catch (Exception ex)
             {
@@ -428,9 +420,18 @@ namespace TEAMModelBI.Controllers.BISchool
                 if (response.Status == 200)
                 {
                     using var json = await JsonDocument.ParseAsync(response.ContentStream);
-                    temp.serial = json.RootElement.GetProperty("serial").GetArrayLength();
-                    temp.service = json.RootElement.GetProperty("service").GetArrayLength();
-                    temp.hard = json.RootElement.GetProperty("hard").GetArrayLength();
+                    if (json.RootElement.TryGetProperty("serial", out JsonElement serial) && !serial.ValueKind.Equals(JsonValueKind.Null))
+                    {
+                        temp.serial = serial.ToObject<List<SchoolProductSumData>>().Select(x => x.prodCode).ToList();
+                    }
+                    if (json.RootElement.TryGetProperty("service", out JsonElement service) && !service.ValueKind.Equals(JsonValueKind.Null))
+                    {
+                        temp.service = service.ToObject<List<SchoolProductSumData>>().Select(x => x.prodCode).ToList();
+                    }
+                    if (json.RootElement.TryGetProperty("hard", out JsonElement hard) && !hard.ValueKind.Equals(JsonValueKind.Null))
+                    {
+                        temp.hard = hard.ToObject<List<SchoolProductSumDataHard>>().Select(x => x.prodCode).ToList();
+                    }
                 }
 
                 temp.assists = await CommonFind.FindSchoolRoles(cosmosClient, temp.id, "assist");
@@ -542,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)
         {

+ 5 - 5
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)
         {
@@ -483,7 +483,7 @@ namespace TEAMModelBI.Controllers.BISchool
         [ProducesDefaultResponseType]
         [AuthToken(Roles = "admin")]
         [HttpPost("set-aistschool")]
-        public async Task<IActionResult> SetAssistSchool(JsonElement jsonElement) 
+        public async Task<IActionResult> SetAssistSchool(JsonElement jsonElement)
         {
             try
             {
@@ -503,7 +503,7 @@ namespace TEAMModelBI.Controllers.BISchool
                 {
                     dic.Add("userId", $"{userId}");
                 }
-                if (string.IsNullOrEmpty($"{userId}") && string.IsNullOrEmpty($"{tmdId}")) 
+                if (string.IsNullOrEmpty($"{userId}") && string.IsNullOrEmpty($"{tmdId}"))
                 {
                     return Ok(new { state = 400, msg = "钉钉账户或醍摩豆账户需要至少要传一个!" });
                 }
@@ -518,7 +518,7 @@ namespace TEAMModelBI.Controllers.BISchool
                     {
                         if (schoolIds.Count > 0)
                         {
-                            if (string.IsNullOrEmpty(ddInfo.tmdId)) 
+                            if (string.IsNullOrEmpty(ddInfo.tmdId))
                             {
                                 List<string> moblie = new() { $"{ddInfo.mobile}" };
 
@@ -662,7 +662,7 @@ namespace TEAMModelBI.Controllers.BISchool
                         name = $"{_adminTmdName}",
                         picture = "",
                         status = "join",
-                        createTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
+                        createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                         pk = "Teacher",
                         ttl = -1
                     };

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

@@ -0,0 +1,67 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Options;
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Threading.Tasks;
+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, });
+        }
+
+    }
+}

+ 171 - 41
TEAMModelBI/Controllers/BITest/TestController.cs

@@ -43,6 +43,8 @@ using System.IO;
 using System.Net.Http;
 using System.Net.Http.Json;
 using System.Net;
+using TEAMModelBI.Tool.CosmosBank;
+using System.Diagnostics;
 
 namespace TEAMModelBI.Controllers.BITest
 {
@@ -428,9 +430,19 @@ namespace TEAMModelBI.Controllers.BITest
                 //    if (response.Status == 200)
                 //    {
                 //        using var json = await JsonDocument.ParseAsync(response.ContentStream);
-                //        schoolAssist.serial = json.RootElement.GetProperty("serial").GetArrayLength();
-                //        schoolAssist.service = json.RootElement.GetProperty("service").GetArrayLength();
-                //        schoolAssist.hard = json.RootElement.GetProperty("hard").GetArrayLength();
+
+                //        if (json.RootElement.TryGetProperty("serial", out JsonElement serial) && !serial.ValueKind.Equals(JsonValueKind.Null))
+                //        {
+                //            schoolAssist.serial = serial.ToObject<List<SchoolProductSumData>>().Select(x => x.prodCode).ToList();
+                //        }
+                //        if (json.RootElement.TryGetProperty("service", out JsonElement service) && !service.ValueKind.Equals(JsonValueKind.Null))
+                //        {
+                //            schoolAssist.service = service.ToObject<List<SchoolProductSumData>>().Select(x => x.prodCode).ToList();
+                //        }
+                //        if (json.RootElement.TryGetProperty("hard", out JsonElement hard) && !hard.ValueKind.Equals(JsonValueKind.Null))
+                //        {
+                //            schoolAssist.hard = hard.ToObject<List<SchoolProductSumDataHard>>().Select(x => x.prodCode).ToList();
+                //        }
                 //    }
 
                 //    schoolAssist.assists = await CommonFind.FindSchoolRoles(cosmosClient, itemSchool.id, "assist");
@@ -467,14 +479,23 @@ namespace TEAMModelBI.Controllers.BITest
                         try { schoolAssist.scale = objSchool.GetProperty("scale").GetInt32(); }
                         catch { schoolAssist.scale = 0; }
 
-                        //var response = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(schoolId, new PartitionKey("ProductSum"));
-                        //if (response.Status == 200)
-                        //{
-                        //    using var json = await JsonDocument.ParseAsync(response.ContentStream);
-                        //    schoolAssist.serial = json.RootElement.GetProperty("serial").GetArrayLength();
-                        //    schoolAssist.service = json.RootElement.GetProperty("service").GetArrayLength();
-                        //    schoolAssist.hard = json.RootElement.GetProperty("hard").GetArrayLength();
-                        //}
+                        var response = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(schoolId, new PartitionKey("ProductSum"));
+                        if (response.Status == 200)
+                        {
+                            using var json = await JsonDocument.ParseAsync(response.ContentStream);
+                            if (json.RootElement.TryGetProperty("serial", out JsonElement serial) && !serial.ValueKind.Equals(JsonValueKind.Null))
+                            {
+                                schoolAssist.serial = serial.ToObject<List<SchoolProductSumData>>().Select(x => x.prodCode).ToList();
+                            }
+                            if (json.RootElement.TryGetProperty("service", out JsonElement service) && !service.ValueKind.Equals(JsonValueKind.Null))
+                            {
+                                schoolAssist.service = service.ToObject<List<SchoolProductSumData>>().Select(x => x.prodCode).ToList();
+                            }
+                            if (json.RootElement.TryGetProperty("hard", out JsonElement hard) && !hard.ValueKind.Equals(JsonValueKind.Null))
+                            {
+                                schoolAssist.hard = hard.ToObject<List<SchoolProductSumDataHard>>().Select(x => x.prodCode).ToList();
+                            }
+                        }
 
                         //schoolAssist.assists = await CommonFind.FindSchoolRoles(cosmosClient, schoolId, "assist");
                         schoolAssists.Add(schoolAssist);
@@ -512,9 +533,18 @@ namespace TEAMModelBI.Controllers.BITest
                 if (response.Status == 200)
                 {
                     using var json = await JsonDocument.ParseAsync(response.ContentStream);
-                    temp.serial = json.RootElement.GetProperty("serial").GetArrayLength();
-                    temp.service = json.RootElement.GetProperty("service").GetArrayLength();
-                    temp.hard = json.RootElement.GetProperty("hard").GetArrayLength();
+                    if (json.RootElement.TryGetProperty("serial", out JsonElement serial) && !serial.ValueKind.Equals(JsonValueKind.Null))
+                    {
+                        temp.serial = serial.ToObject<List<SchoolProductSumData>>().Select(x => x.prodCode).ToList();
+                    }
+                    if (json.RootElement.TryGetProperty("service", out JsonElement service) && !service.ValueKind.Equals(JsonValueKind.Null))
+                    {
+                        temp.service = service.ToObject<List<SchoolProductSumData>>().Select(x => x.prodCode).ToList();
+                    }
+                    if (json.RootElement.TryGetProperty("hard", out JsonElement hard) && !hard.ValueKind.Equals(JsonValueKind.Null))
+                    {
+                        temp.hard = hard.ToObject<List<SchoolProductSumDataHard>>().Select(x => x.prodCode).ToList();
+                    }
                 }
 
                 temp.assists = await CommonFind.FindSchoolRoles(cosmosClient, temp.id, "assist");
@@ -624,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");
 
@@ -656,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)
@@ -927,7 +957,7 @@ namespace TEAMModelBI.Controllers.BITest
         }
         
         /// <summary>
-        /// 测试CosmosDB分页查询
+        /// 测试CosmosDB分页查询  SQL语句分页
         /// </summary>
         /// <param name="jsonElement"></param>
         /// <returns></returns>
@@ -956,6 +986,61 @@ namespace TEAMModelBI.Controllers.BITest
             return Ok(new { state = 200, schools });
         }
 
+        /// <summary>
+        /// 分页案例
+        /// </summary>
+        /// <param name="jsonElement"></param>
+        /// <returns></returns>
+        [HttpPost("get-cosmodbtokenpage")]
+        public async Task<IActionResult> GetCancellationTokenPage(JsonElement jsonElement)
+        {
+            var cosmosClient = _azureCosmos.GetCosmosClient();
+
+            //默认不指定返回大小
+            int? pageSize = null;
+            string continuationToken = string.Empty;
+            string pageToken = default;
+            if (jsonElement.TryGetProperty("pageSize", out JsonElement jsonPageSize)) 
+            {
+                if (!jsonPageSize.ValueKind.Equals(JsonValueKind.Undefined) && !jsonPageSize.ValueKind.Equals(JsonValueKind.Null) && jsonPageSize.TryGetInt32(out int tempPageSize)) 
+                {
+                    pageSize = tempPageSize;
+                }
+            }
+            //是否需要进行分页查询,默认不分页
+            bool iscontinuation = false;
+            if (pageSize != null && pageSize.Value > 0)
+            {
+                iscontinuation = true;
+            }
+            if (jsonElement.TryGetProperty("contToken", out JsonElement ContToken))
+            {
+                pageToken = ContToken.GetString();
+            }
+
+            List<School> schools = new();
+
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: "select value(c) from c", continuationToken: pageToken, requestOptions: new QueryRequestOptions() { MaxItemCount = pageSize, PartitionKey = new PartitionKey("Base") }))
+            {
+                using var json = await JsonDocument.ParseAsync(item.ContentStream);
+                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0) 
+                {
+                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                    {
+                        schools.Add(obj.ToObject<School>());
+                    }
+
+                    if (iscontinuation) 
+                    {
+                        continuationToken = item.GetContinuationToken();
+                        break;
+                    }
+                }
+            }
+
+            return Ok(new { state = 200, continuationToken, count = schools.Count, schools });
+        }
+
         /// <summary>
         /// 测试隐式登录
         /// </summary>
@@ -1070,9 +1155,8 @@ namespace TEAMModelBI.Controllers.BITest
             return Ok(new { state = 200, Mon,Sun, ss, sss, ssss, sssss, lastWeekStart, lastWeekEnd, have, en2, bingji, cha, jiaoji, chaji, list3, list4, temp, list1 });
         }
 
-
         /// <summary>
-        ///  异步并行
+        ///  token 分页
         /// </summary>
         /// <returns></returns>
         [HttpPost("get-parallelforeach")]
@@ -1125,8 +1209,75 @@ namespace TEAMModelBI.Controllers.BITest
             return Ok(new { state = 200, allcount = operateLogs.Count, bIOptLogs });
         }
 
+        /// <summary>
+        /// 流式传输响应
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost("get-stream")]
+        public async Task<IActionResult> GetStream()
+        {
+            var sw = Stopwatch.StartNew();
+            //while (true)
+            //{
+            //    var q = new Queue<int>();
+
+            //    for (int i = 0; i < 100_000_000; i++)
+            //    {
+            //        q.Enqueue(i);
+            //        q.Dequeue();
+            //    }
+            //    Console.WriteLine(sw.Elapsed);
+            //}
+            var ss = new SortedSet<int>(Enumerable.Repeat(42, 400_000));
+            Console.WriteLine(sw.Elapsed);
+
+            HttpContext.Response.ContentType = "text/plain";
+            
+            StreamWriter writer;
+            //using (writer = new StreamWriter(HttpContext.Response.Body))
+            //    for (int i = 0; i < 100; i++)
+            //    {
+            //        await writer.WriteAsync($"S-{i} \n");                   
+            //    }
+            //    //await writer.FlushAsync("Hello World");
+
+            return Ok(new { state = 200});
+        }
+
+        [HttpPost("get-linqcount")]
+        public async Task<IActionResult> GetLinqCount() 
+        {
+            List<linqTest> linqTests = new();
+            for (int i = 0; i < 10; i++)
+            {
+                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);
+            }
+
+            //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
         {
             public int age { get; set; }
@@ -1177,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>

+ 113 - 0
TEAMModelBI/Controllers/Census/SchoolController.cs

@@ -10,6 +10,7 @@ using System.Text.Json;
 using System.Threading.Tasks;
 using TEAMModelBI.Models;
 using TEAMModelBI.Tool;
+using TEAMModelBI.Tool.CosmosBank;
 using TEAMModelOS.Models;
 using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
@@ -223,6 +224,118 @@ namespace TEAMModelBI.Controllers.Census
             return Ok(new { state = 200, schoolCount = schoolIds.Count, tecCount, stuCount, classCount, roomCount, allLessonCount, lastWeekLessCount, weekLessCount, lastTermLessCount, termLessCount, lastYearLessCount, yearLessCount,allBloblog , lastYearBloblog, yearBloblog , allActivity , lastYearActivity ,yearActivity , lastWeekActivity, weekActivity, recSchoolDates });
         }
 
+        /// <summary>
+        /// 依据学校Id统计学校分析
+        /// </summary>
+        /// <param name="jsonElement"></param>
+        /// <returns></returns>
+        [HttpPost("get-idstatis")]
+        public async Task<IActionResult> GetIdStatis(JsonElement jsonElement) 
+        {
+            if (!jsonElement.TryGetProperty("schoolId", out JsonElement schoolId)) return BadRequest();        
+
+            int tecCount = 0;  //学校教师数量
+            int stuCount = 0;  //学校学生数量
+            int classCount = 0; //班级数量
+            int roomCount = 0;  //教室教师数量
+
+            long allLessCount = 0;  //课例总数
+            int lastWeekLess = 0;  //上周的总数
+            int weekLess = 0;    //本周的总数
+            int lastTermLess = 0;   //上学期的总数
+            int termLess = 0;      //本学期的总数
+            int lessYearLess = 0;    //去年的总数
+            int yearLess = 0;    //去年的总数
+
+            int allActivity = 0;  //学校所有活动
+            int lastYearActivity = 0;  //去年学校所有活动
+            int yearActivity = 0;  //今年学校所有活动
+            int lastWeekActivity = 0;  //上周学校所有活动
+            int weekActivity = 0;  //本周学校所有活动
+
+            int allBlob = 0;  //所有资源
+            int lastYearBlob = 0;  //去年的资源
+            int yearBlob = 0;  //去年的资源
+
+            var cosmosClient = _azureCosmos.GetCosmosClient();
+            var (lastWeekStart, lastWeekEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "lastweek");   //计算上周开始/结束时间
+            var (weekStart, weekEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "week");             //计算本周开始/结束时间
+            var (lastTermStart, lastTermEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "lastterm");   //计算上学期开始/结束时间
+            var (termStart, termEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "term");   //计算本学期开始/结束时间
+            var (lastYearStart, lastYearEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.Parse($"{DateTimeOffset.UtcNow.Year - 1}-1-1"), "year"); //计算去年开始/结束时间
+            var (yearStart, yearEnd) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "year");         //计算今年开始/结束时间
+
+            tecCount = await JointlySingleQuery.GetValueInt(cosmosClient, "School", "SELECT value(count(c.id)) FROM c WHERE ARRAY_CONTAINS(c.roles, 'teacher', true) AND c.status = 'join'", code:$"Teacher-{schoolId}");
+            stuCount = await JointlySingleQuery.GetValueInt(cosmosClient, "Student", code: $"Base-{schoolId}");
+            classCount = await JointlySingleQuery.GetValueInt(cosmosClient, "School", code: $"Class-{schoolId}");
+            roomCount = await JointlySingleQuery.GetValueInt(cosmosClient, "School", code: $"Room-{schoolId}");
+
+            string unifyTimeSql = "select value(count(c.id)) from c where c.startTime>={0} and c.startTime<={1}";
+            allLessCount = await LessonStatisWay.GetSchoolIdLessonCount(cosmosClient, $"{schoolId}");
+            lastWeekLess = await JointlySingleQuery.GetValueInt(cosmosClient, "School", string.Format(unifyTimeSql, lastWeekStart, lastWeekEnd), $"LessonRecord-{schoolId}");
+            weekLess = await JointlySingleQuery.GetValueInt(cosmosClient, "School", string.Format(unifyTimeSql, weekStart, lastWeekEnd), $"LessonRecord-{schoolId}");
+            lastTermLess = await JointlySingleQuery.GetValueInt(cosmosClient, "School", string.Format(unifyTimeSql, lastTermStart, lastTermEnd), $"LessonRecord-{schoolId}") ;
+            termLess = await JointlySingleQuery.GetValueInt(cosmosClient, "School", string.Format(unifyTimeSql, termStart, termEnd), $"LessonRecord-{schoolId}");
+            lessYearLess = await JointlySingleQuery.GetValueInt(cosmosClient, "School", string.Format(unifyTimeSql, lastYearStart, lastYearEnd), $"LessonRecord-{schoolId}");
+            yearLess = await JointlySingleQuery.GetValueInt(cosmosClient, "School", string.Format(unifyTimeSql, yearStart, yearEnd), $"LessonRecord-{schoolId}");
+
+            //统计活动
+            foreach (var type in StaticValue.activityTypes)
+            {
+                string sqlTime = "select value(count(c.id)) from c where c.pk = '{0}' and c.school = '{1}' and c.createTime>={2} and c.createTime<={3}";
+                allActivity += await JointlySingleQuery.GetValueInt(cosmosClient,"Common", $"select value(count(c.id)) from c where c.pk = '{type}' and c.school = '{schoolId}'");
+                lastYearActivity += await JointlySingleQuery.GetValueInt(cosmosClient, "Common", string.Format(sqlTime, type, schoolId, lastYearStart, lastYearEnd));
+                yearActivity += await JointlySingleQuery.GetValueInt(cosmosClient, "Common", string.Format(sqlTime, type, schoolId, yearStart, yearEnd));
+                lastWeekActivity += await JointlySingleQuery.GetValueInt(cosmosClient, "Common", string.Format(sqlTime, type, schoolId, lastWeekStart, lastWeekEnd));
+                weekActivity += await JointlySingleQuery.GetValueInt(cosmosClient, "Common", string.Format(sqlTime, type, schoolId, weekStart, weekEnd));
+            }
+
+            //学校资源
+            string blobTimeSql = "select value(count(c.id)) from c where c.time>={0} and c.time<={1}";
+            allBlob = await JointlySingleQuery.GetValueInt(cosmosClient, "School", code: $"Bloblog-{schoolId}");
+            lastYearBlob = await JointlySingleQuery.GetValueInt(cosmosClient, "School", string.Format(blobTimeSql, lastYearStart, lastYearEnd), $"Bloblog-{schoolId}");
+            yearBlob = await JointlySingleQuery.GetValueInt(cosmosClient, "School", string.Format(blobTimeSql, yearStart, yearEnd), $"Bloblog-{schoolId}");
+
+            //获取所有的课程记录
+            List<LessonRecord> records = new();
+            await foreach (var item in cosmosClient.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<LessonRecord>(queryText: "select value(c) from c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"LessonRecord-{schoolId}") }))
+            {
+                records.Add(item);
+            }
+
+            List<(string name, int count)> gradeCount = new();
+            if (records.Count > 0)
+            {
+                var response = await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync($"{schoolId}", new PartitionKey($"Base"));
+                School sc = new();
+                if (response.Status == 200)
+                {
+                    using var json = await JsonDocument.ParseAsync(response.ContentStream);
+                    sc = json.ToObject<School>();
+                }
+
+                List<string> grades = new();
+
+                foreach (var item in records) 
+                {
+                    foreach (string gId in item.grade)
+                    {
+                        if (!grades.Contains(gId))
+                        {
+                            grades.Add(gId);
+                        }
+                    }
+                }
+                foreach (var gId in grades)
+                {
+                    var c = records.Where(r => r.grade.Contains(gId)).Count();
+                    gradeCount.Add((gId, c));
+                }
+            }
+
+            return Ok(new { state = 200, tecCount, stuCount, classCount, roomCount, allLessCount, lastWeekLess, weekLess, lastTermLess, termLess, lessYearLess, yearLess, allActivity, lastYearActivity, yearActivity, lastWeekActivity, weekActivity, allBlob, lastYearBlob, yearBlob });
+        }
+
         /// <summary>
         /// 依据Id查询School容器  数据管理工具——查询工具
         /// </summary>

+ 7 - 6
TEAMModelBI/Models/AssistSchool.cs

@@ -1,4 +1,5 @@
 using System.Collections.Generic;
+using TEAMModelOS.SDK.Models;
 
 namespace TEAMModelBI.Models
 {
@@ -20,12 +21,12 @@ namespace TEAMModelBI.Models
         public string standard { get; set; }
         public string areaId { get; set; }
         public List<SchoolTeacherRoles> assists { get; set; } = new List<SchoolTeacherRoles>();
-        public int serial { get; set; } //软体
-        public int service { get; set; } //服务
-        public int hard { get; set; } //硬体
+        //public int serial { get; set; } //软体
+        //public int service { get; set; } //服务
+        //public int hard { get; set; } //硬体
         public long lessonCount { get; set; } = 0; //学校课例数量
-        //public List<SchoolProductSumData> serial { get; set; } //软体
-        //public List<SchoolProductSumData> service { get; set; } //服务
-        //public List<SchoolProductSumDataHard> hard { get; set; } //硬体
+        public List<string> serial { get; set; } = new List<string>(); //软体
+        public List<string> service { get; set; } = new List<string>(); //服务
+        public List<string> hard { get; set; } = new List<string>(); //硬体
     }
 }

+ 41 - 35
TEAMModelBI/Tool/CommonFind.cs

@@ -148,41 +148,6 @@ namespace TEAMModelBI.Tool
             return schoolIds;
         }
 
-        /// <summary>
-        /// 依据学校编号查询学校信息和学校相关的信息
-        /// </summary>
-        /// <param name="cosmosClient"></param>
-        /// <param name="schoos"></param>
-        /// <returns></returns>
-        public static async Task<List<AssistSchool>> FindAssistSchools(CosmosClient cosmosClient, List<string> schoos)
-        {
-            List<AssistSchool> assistSchools = new();
-            foreach (var id in schoos)
-            {
-                string sqlTxt = $"select c.id,c.code,c.schoolCode,c.name,c.region,c.province,c.city,c.dist,c.size,c.address,c.picture,c.type,c.scale,c.areaId,c.standard from c where c.id='{id}'";
-                await foreach (var itemSchool in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<AssistSchool>(queryText: sqlTxt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
-                {
-                    assistSchools.Add(itemSchool);
-                }
-            }
-
-            foreach (var school in assistSchools)
-            {
-                var response = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(school.id, new PartitionKey("ProductSum"));
-                if (response.Status == 200)
-                {
-                    using var json = await JsonDocument.ParseAsync(response.ContentStream);
-                    school.serial = json.RootElement.GetProperty("serial").GetArrayLength();
-                    school.service = json.RootElement.GetProperty("service").GetArrayLength();
-                    school.hard = json.RootElement.GetProperty("hard").GetArrayLength();
-                }
-
-                school.assists = await FindSchoolRoles(cosmosClient, school.id, "assist");
-            }
-            
-            return assistSchools;
-        }
-
         /// <summary>
         /// 依据学校查询教师列表
         /// </summary>
@@ -252,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;
+        }
     }
 }

+ 82 - 0
TEAMModelBI/Tool/CosmosBank/JointlySingleQuery.cs

@@ -0,0 +1,82 @@
+using Azure.Cosmos;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace TEAMModelBI.Tool.CosmosBank
+{
+    public class JointlySingleQuery
+    {
+        /// <summary>
+        /// 多个容器返回整数 使用
+        /// </summary>
+        /// <param name="cosmosClient"></param>
+        /// <param name="containerId"></param>
+        /// <param name="sqlTxt"></param>
+        /// <param name="code"></param>
+        /// <returns></returns>
+        public static async Task<int> GetValueInt(CosmosClient cosmosClient, List<string> containerId, string sqlTxt = null, string code = null) 
+        {
+            int totals = 0;
+            string sql = $"select value(count(c.id)) from c";
+            if (!string.IsNullOrEmpty(sqlTxt)) 
+            {
+                sql = sqlTxt;
+            }
+
+            foreach (string conId in containerId) 
+            {
+                await foreach (var itemInt in cosmosClient.GetContainer("TEAMModelOS", conId).GetItemQueryIterator<int>(queryText: sql, requestOptions: string.IsNullOrEmpty($"{code}") ? new QueryRequestOptions() { } : new QueryRequestOptions() { PartitionKey = new PartitionKey($"{code}") }))
+                {
+                    totals += itemInt;
+                }
+            }
+
+            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> GetValueInt(CosmosClient cosmosClient, string container, string sqlTxt = null, string code = null)
+        {
+            int totals = 0;
+            string sql = $"select value(count(c.id)) from c";
+            if (!string.IsNullOrEmpty(sqlTxt))
+            {
+                sql = sqlTxt;
+            }
+
+            await foreach (var itemInt in cosmosClient.GetContainer("TEAMModelOS", container).GetItemQueryIterator<int>(queryText: sql, requestOptions: string.IsNullOrEmpty($"{code}") ? new QueryRequestOptions() { } : new QueryRequestOptions() { PartitionKey = new PartitionKey($"{code}") }))
+            {
+                totals += itemInt;
+            }
+
+            return totals;
+        }
+
+        /// <summary>   
+        /// 返回字符集合
+        /// </summary>
+        /// <param name="cosmosClient"></param>
+        /// <param name="containerId"></param>
+        /// <param name="sqlTxt"></param>
+        /// <param name="code"></param>
+        /// <returns></returns>
+        public static async Task<List<string>> GetListString(CosmosClient cosmosClient, string containerId, string sqlTxt, string code = null) 
+        {
+            List<string> strList = new();
+
+            await foreach (var itemInt in cosmosClient.GetContainer("TEAMModelOS", containerId).GetItemQueryIterator<string>(queryText: sqlTxt, requestOptions: string.IsNullOrEmpty($"{code}") ? new QueryRequestOptions() { } : new QueryRequestOptions() { PartitionKey = new PartitionKey($"{code}") }))
+            {
+                strList.Add(itemInt);
+            }
+
+            return strList;
+        }
+    }
+}

+ 35 - 0
TEAMModelBI/Tool/CosmosBank/LessonStatisWay.cs

@@ -58,5 +58,40 @@ namespace TEAMModelBI.Tool.CosmosBank
         }
 
 
+        /// <summary>
+        /// 依据学校Id查询学校和学校里面教师的课例统计
+        /// </summary>
+        /// <param name="cosmosClient"></param>
+        /// <param name="schoolId"></param>
+        /// <returns></returns>
+        public async static Task<int> GetSchoolIdLessonCount(CosmosClient cosmosClient ,string schoolId) 
+        {
+            int totals = 0;
+            string LessonSqlTxt = "select value(count(c.id)) from c";
+
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<int>(queryText: LessonSqlTxt, requestOptions: new QueryRequestOptions() {PartitionKey = new PartitionKey($"LessonRecord-{schoolId}") }))
+            {
+                totals += item;
+            }
+
+            List<string> tecIdS = new();
+            string managerSql = $"SELECT value(c.id) FROM c WHERE ARRAY_CONTAINS(c.roles, 'teacher', true) AND  c.pk = 'Teacher' AND c.status = 'join'";
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<string>(queryText: managerSql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{schoolId}") }))
+            {
+                tecIdS.Add(item);
+            }
+
+            foreach (var itemId in tecIdS)
+            {
+                string tecLessSQL = $"select value(count(c.id)) from c where c.tmdid='{itemId}'";
+                await foreach (var itemCount in cosmosClient.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIterator<int>(queryText: tecLessSQL, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("LessonRecord") }))
+                {
+                    totals += itemCount;
+                }
+            }
+
+            return totals;
+        }
+
     }
 }

+ 20 - 14
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,29 @@ 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);
-
         }
 
 
+
     }
 }

+ 6 - 5
TEAMModelOS.FunctionV4/CosmosDB/TriggerExam.cs

@@ -977,7 +977,7 @@ namespace TEAMModelOS.FunctionV4
                                 ph.Add(phCount);
                                 pl.Add(plCount);
                                 double per = classResult.studentIds.Count > 0 ? Math.Round(score / classResult.studentIds.Count, 2) : 0;
-                                persent.Add(allScore > 0 ? per / allScore : 0);
+                                persent.Add(allScore > 0 ? Math.Round(per / allScore,2) : 0);
                             }
                             classResult.phc = ph;
                             classResult.plc = pl;
@@ -1113,7 +1113,7 @@ namespace TEAMModelOS.FunctionV4
                                 ph.Add(phCount);
                                 pl.Add(plCount);
                                 double per = classResult.studentIds.Count > 0 ? Math.Round(score / classResult.studentIds.Count, 2) : 0;
-                                persent.Add(allScore > 0 ? per / allScore : 0);
+                                persent.Add(allScore > 0 ? Math.Round(per / allScore, 2) : 0);
                             }
                         }
                         classResult.fphc = ph;
@@ -1337,6 +1337,7 @@ namespace TEAMModelOS.FunctionV4
             result.average = result.studentIds.Count > 0 ? Math.Round(score * 1.0 / result.studentIds.Count, 2) : 0;
             double stand = 0;
             int sco = 0;
+            List<Task<ItemResponse<ExamClassResult>>> tasks = new();
             foreach (ExamClassResult classResult in examClassResults)
             {
 
@@ -1346,8 +1347,9 @@ namespace TEAMModelOS.FunctionV4
                     stand += classResult.standard;
                     sco++;
                 }
-
+                tasks.Add(_azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(classResult, classResult.id, new PartitionKey($"{classResult.code}")));
             }
+            await Task.WhenAll(tasks);
             result.standard = sco > 0 ? Math.Round(stand / sco, 2) : 0;
             result.csRate = csRate;
             result.lostStus = lostStu;
@@ -1364,8 +1366,7 @@ namespace TEAMModelOS.FunctionV4
             //result.point = info.papers[j].point;
             result.scope = info.scope;
             result.name = info.name;
-            result.time = info.startTime;
-
+            result.time = info.startTime;            
             await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync(result, new Azure.Cosmos.PartitionKey($"ExamResult-{info.id}"));
 
         }

+ 101 - 60
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
 {
@@ -39,7 +40,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
         private readonly NotificationService _notificationService;
         private readonly CoreAPIHttpService _coreAPIHttpService;
         private readonly IConfiguration _configuration;
-        public ActiveTaskTopic(CoreAPIHttpService  coreAPIHttpService,AzureCosmosFactory azureCosmos, DingDing dingDing, AzureStorageFactory azureStorage, AzureRedisFactory azureRedis, AzureServiceBusFactory serviceBus, IOptionsSnapshot<Option> option, NotificationService notificationService, IConfiguration configuration)
+        public ActiveTaskTopic(CoreAPIHttpService coreAPIHttpService, AzureCosmosFactory azureCosmos, DingDing dingDing, AzureStorageFactory azureStorage, AzureRedisFactory azureRedis, AzureServiceBusFactory serviceBus, IOptionsSnapshot<Option> option, NotificationService notificationService, IConfiguration configuration)
         {
             _azureCosmos = azureCosmos;
             _dingDing = dingDing;
@@ -337,7 +338,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
             await _azureRedis.GetRedisClient(8).HashSetAsync($"Blob:Record", new RedisValue(name), new RedisValue($"{blobsize}"));
             long end = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
             long dis = (end - statr) / 1000;
-            long timeout = 60*5;
+            long timeout = 60 * 5;
             if (dis > timeout)
             {
                 await _dingDing.SendBotMsg($"ServiceBus,RefreshBlob:空间计算已经超过{timeout}秒\n容器名:{name}\n文件夹:{u}\n计算时长:{dis}", GroupNames.醍摩豆服務運維群組);
@@ -347,10 +348,10 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
         /// <returns></returns>
         [Function("TeacherTrainChange")]
         public async Task TeacherTrainChangeFunc([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "teacher-train-change", Connection = "Azure:ServiceBus:ConnectionString")] string msg)
-        { 
-        
+        {
+
         }
-        
+
         /// <param name="msg"></param>
         /// <returns></returns>
         //[Function("TeacherTrainChange")]
@@ -704,7 +705,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                             }
                         }
                     }
-                    if(chgSchSizeCnt.Equals(0) && !school.size.Equals(schoolDefaultSize))
+                    if (chgSchSizeCnt.Equals(0) && !school.size.Equals(schoolDefaultSize))
                     {
                         school.size = schoolDefaultSize;
                         //updSchool = true; //關於學校空間初始式樣未定,目前暫定:若需要Reset空間,則先維持原空間數不更動
@@ -712,7 +713,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                     }
                 }
                 //變更學校基本資料
-                if(updSchool)
+                if (updSchool)
                 {
                     await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<School>(school, $"{schoolId}", new PartitionKey("Base"));
                 }
@@ -873,7 +874,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
             }
 
             var client = _azureCosmos.GetCosmosClient();
-
+           
             if ($"{scope}".Equals("school") && !string.IsNullOrEmpty($"{school}"))
             {
                 blobname = $"{school}";
@@ -911,6 +912,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)
@@ -920,22 +922,26 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                 var duration = double.Parse($"{update.data}");
                                 lessonRecord.duration = duration;
                                 msgs.Add(update);
-                                try {
+                                try
+                                {
                                     BlobDownloadResult Recording = await _azureStorage.GetBlobContainerClient(blobname).GetBlobClient($"records/{_lessonId}/Record/.Recording.json").DownloadContentAsync();
                                     var RecordingJson = JsonDocument.Parse(new MemoryStream(Encoding.UTF8.GetBytes(Recording.Content.ToString()))).RootElement;
-                                    if (RecordingJson.TryGetProperty("duration",out JsonElement _duration) && _duration.ValueKind.Equals(JsonValueKind.Number)) {
+                                    if (RecordingJson.TryGetProperty("duration", out JsonElement _duration) && _duration.ValueKind.Equals(JsonValueKind.Number))
+                                    {
                                         var durationFile = double.Parse($"{_duration}");
-                                        if (duration < durationFile) {
+                                        if (duration < durationFile)
+                                        {
                                             lessonRecord.duration = durationFile;
                                         }
                                     }
-                                    if (RecordingJson.TryGetProperty("streamUrl", out JsonElement _streamUrl) &&!string.IsNullOrWhiteSpace($"{_streamUrl}"))
+                                    if (RecordingJson.TryGetProperty("streamUrl", out JsonElement _streamUrl) && !string.IsNullOrWhiteSpace($"{_streamUrl}"))
                                     {
                                         lessonRecord.hasVideo = 1;
                                     }
                                 }
-                                catch (Exception ex ) {
-                                  // await _dingDing.SendBotMsg($"{_option.Location}/LessonRecordEvent/课堂记录更新课堂时长出错records/{_lessonId}/Record/.Recording.json\n{ex.Message}{ex.StackTrace}{msg}", GroupNames.醍摩豆服務運維群組);
+                                catch (Exception ex)
+                                {
+                                    // await _dingDing.SendBotMsg($"{_option.Location}/LessonRecordEvent/课堂记录更新课堂时长出错records/{_lessonId}/Record/.Recording.json\n{ex.Message}{ex.StackTrace}{msg}", GroupNames.醍摩豆服務運維群組);
                                 }
                                 isReplace = true;
                                 break;
@@ -968,10 +974,10 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                 //如果有更新 则去读取/{_lessonId}/IES/base.json
                                 try
                                 {
-                                   // await _dingDing.SendBotMsg($"{_option.Location},课堂id:{_lessonId} 收到更新", GroupNames.醍摩豆服務運維群組);
+                                    // await _dingDing.SendBotMsg($"{_option.Location},课堂id:{_lessonId} 收到更新", GroupNames.醍摩豆服務運維群組);
                                     BlobDownloadResult baseblobDownload = await _azureStorage.GetBlobContainerClient(blobname).GetBlobClient($"/records/{_lessonId}/IES/base.json").DownloadContentAsync();
                                     LessonBase lessonBase = baseblobDownload.Content.ToObjectFromJson<LessonBase>();
-                                    if (lessonBase != null  && lessonBase.summary!=null)
+                                    if (lessonBase != null && lessonBase.summary != null)
                                     {
                                         //lessonRecord.name = lessonBase.summary.activityName;
                                         lessonRecord.attendCount = lessonBase.summary.attendCount;
@@ -990,8 +996,10 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                         lessonRecord.examCount = lessonBase.summary.examCount;
                                         lessonRecord.totalInteractPoint = lessonBase.summary.totalInteractPoint;
                                     }
-                                   // await _dingDing.SendBotMsg($"{_option.Location},课堂id:{_lessonId} 更新完成", GroupNames.醍摩豆服務運維群組);
-                                    long? size = await  _azureStorage.GetBlobContainerClient(blobname).GetBlobsSize($"records/{_lessonId}");
+                                    // 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
                                     {
                                         id = lessonRecord.id,
@@ -1001,11 +1009,11 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                         time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                                         type = "records",
                                         url = $"records/{_lessonId}",
-                                        subjectId =string.IsNullOrWhiteSpace(lessonRecord.subjectId)?new List<string>(): new List<string> { lessonRecord.subjectId },
+                                        subjectId = string.IsNullOrWhiteSpace(lessonRecord.subjectId) ? new List<string>() : new List<string> { lessonRecord.subjectId },
                                         periodId = string.IsNullOrWhiteSpace(lessonRecord.periodId) ? new List<string>() : new List<string> { lessonRecord.periodId },
                                         size = size.HasValue ? size.Value : 0,
                                     };
-                                    await client.GetContainer(Constant.TEAMModelOS,tbname).UpsertItemAsync(bloblog);
+                                    await client.GetContainer(Constant.TEAMModelOS, tbname).UpsertItemAsync(bloblog);
                                     var messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "update", root = "records", name = $"{blobname}" }.ToJsonString()); ;
                                     messageBlob.ApplicationProperties.Add("name", "BlobRoot");
                                     var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
@@ -1015,7 +1023,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                 }
                                 catch (Exception ex)
                                 {
-                                   // await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}课程读取base.json,{_lessonId}\n{ex.Message}\n{ex.StackTrace}\n{lessonRecord.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
+                                    // await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}课程读取base.json,{_lessonId}\n{ex.Message}\n{ex.StackTrace}\n{lessonRecord.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
                                 }
                                 break;
                             //更新 时间线
@@ -1035,7 +1043,8 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                 msgs.Add(update);
                                 break;
                             case "up-expire"://消除过期时间,消除后需要取消已经排程的通知订阅
-                                try {
+                                try
+                                {
                                     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)
@@ -1050,7 +1059,9 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                             continue;
                                         }
                                     }
-                                } catch (Exception) {
+                                }
+                                catch (Exception)
+                                {
                                     break;
                                 }
                                 isReplace = true;
@@ -1059,7 +1070,8 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                             case "delete":
                                 try
                                 {
-                                    if (lessonRecord.expire > 0) {
+                                    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)
@@ -1076,7 +1088,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                         }
                                     }
                                     await _azureStorage.GetBlobServiceClient().DeleteBlobs(_dingDing, blobname, new List<string> { $"records/{_lessonId}" });
-                                    await client.GetContainer(Constant.TEAMModelOS, tbname).DeleteItemStreamAsync(lessonRecord.id,new PartitionKey ($"Bloblog-{blobname}"));
+                                    await client.GetContainer(Constant.TEAMModelOS, tbname).DeleteItemStreamAsync(lessonRecord.id, new PartitionKey($"Bloblog-{blobname}"));
                                     var messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "update", root = "records", name = $"{blobname}" }.ToJsonString()); ;
                                     messageBlob.ApplicationProperties.Add("name", "BlobRoot");
                                     var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
@@ -1093,7 +1105,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))
                                 {
@@ -1120,7 +1132,8 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                             }
                                         }
                                     }
-                                    catch (Exception ex) {
+                                    catch (Exception ex)
+                                    {
                                         await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-查询课程-CosmosDB异常{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
                                     }
                                     /*catch (CosmosException ex) when (ex.Status != 404)
@@ -1145,7 +1158,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                 {
                                     HashSet<string> grades = new HashSet<string>();
                                     List<GroupListDto> groups = await GroupListService.GetGroupListListids(client, _dingDing, lessonRecord.groupIds, lessonRecord.school);
-                                     
+
                                     List<GroupListDto> groupLists = groups?.FindAll(x => !string.IsNullOrEmpty(x.periodId) && x.year > 0 && !string.IsNullOrEmpty(x.school));
                                     if (groupLists.IsNotEmpty() && !string.IsNullOrWhiteSpace(lessonRecord.periodId))
                                     {
@@ -1197,23 +1210,25 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                         {
 
                                         }
-                                        catch (Exception ex) {
+                                        catch (Exception ex)
+                                        {
                                             await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-处理课堂记录的-年级处理异常{ex.Message}\n{ex.StackTrace}\n{lessonRecord.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
                                         }
                                     }
-                                    lessonRecord.grade= grades.ToList();
+                                    lessonRecord.grade = grades.ToList();
                                 }
                                 //个人课例保存规则
-                                try {
+                                try
+                                {
                                     if (scope.Equals("private"))
                                     {
-                                       
+
                                         if (teacher.lessonLimit != -1)
                                         {
 
                                             HashSet<string> ids = new HashSet<string>();
                                             //未定义的 以及过期时间小于等于0 的 课例
-                                            string private_count_sql = "select value(c.id) from  c where ( c.expire<=0 or IS_DEFINED(c.expire) = false  )  ";
+                                            string private_count_sql = $"select value(c.id) from  c where  ( c.expire<=0 or IS_DEFINED(c.expire) = false  ) and c.tmdid='{tmdid}'  ";
                                             await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetItemQueryIterator<string>(
                                                 queryText: private_count_sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey(code) }))
                                             {
@@ -1221,7 +1236,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                                 ids.Add(item);
                                             }
                                             //包含收藏的本人的个人课例
-                                            string favorite_count_sql = $"select value(c.id) from  c where c.type='LessonRecord'    and c.scope='private' ";
+                                            string favorite_count_sql = $"select value(c.id) from  c where c.type='LessonRecord' and c.owner='{tmdid}'   and c.scope='private' ";
                                             await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetItemQueryIterator<string>(
                                                 queryText: favorite_count_sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Favorite-{tmdid}") }))
                                             {
@@ -1237,18 +1252,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
                                                 {
@@ -1257,17 +1290,19 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                                     from = $"ies5:{ Environment.GetEnvironmentVariable("Option:Location")}:private",
                                                     to = new List<string> { tmdid },
                                                     label = $"{biz}_lessonRecord",
-                                                    body = new {
+                                                    body = new
+                                                    {
                                                         location = $"{Environment.GetEnvironmentVariable("Option:Location")}",
                                                         biz = biz,
                                                         tmdid = teacher.id,
                                                         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()
@@ -1277,7 +1312,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                                 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)
@@ -1299,10 +1334,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,
@@ -1320,10 +1356,12 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                                             //=-1 则表示不限制上传数量
                                         }
                                     }
-                                } catch (Exception e) {
+                                }
+                                catch (Exception e)
+                                {
                                     await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-处理课堂记录的-CosmosDB异常{e.Message}\n{e.StackTrace}\n{lessonRecord.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
                                 }
-                                
+
                                 msgs.Add(update);
                                 break;
                             default:
@@ -1331,7 +1369,8 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                         }
                     }
                     //如果被删除则不能再被更新
-                    if (isReplace) {
+                    if (isReplace)
+                    {
                         await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, tbname).ReplaceItemAsync<LessonRecord>(lessonRecord, lessonId, new PartitionKey(code));
                     }
                     //计算课堂更新前后的差值
@@ -1351,7 +1390,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;
@@ -1365,12 +1403,14 @@ 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;
-                    string school=$"{_school}";
+                    string school = $"{_school}";
                     string code = "";
                     if ($"{scope}".Equals("school") && !string.IsNullOrEmpty($"{school}"))
                     {
@@ -1395,11 +1435,11 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                         lessonRecord.status = 404;
                         await client.GetContainer(Constant.TEAMModelOS, tbname).ReplaceItemAsync(lessonRecord, lessonRecord.id, new PartitionKey(lessonRecord.code));
                         var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
-                        var messageChange = new ServiceBusMessage(new   { delete_id =lessonId, tmdid =tmdid,scope=scope,opt="delete",school=school}.ToJsonString());
+                        var messageChange = new ServiceBusMessage(new { delete_id = lessonId, tmdid = tmdid, scope = scope, opt = "delete", school = school }.ToJsonString());
                         messageChange.ApplicationProperties.Add("name", "LessonRecordEvent");
                         await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageChange);
-                        await _dingDing.SendBotMsg($"课例因【{lessonRecord.name}】,【{lessonRecord.id}】时间到期,即将被自动删除,到期时间:" +
-                            $"{lessonRecord.expire}\n{lessonRecord.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
+                        await _dingDing.SendBotMsg($"课例:【{lessonRecord.name}】\n课例ID:【{lessonRecord.id}】\n因时间到期,即将被自动删除,到期时间:" +
+                            $"{lessonRecord.expire}\n{lessonRecord.ToJsonString()}", GroupNames.成都开发測試群組);
                     }
                 }
                 string biz = "expire";
@@ -1410,17 +1450,18 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                     from = $"ies5:{_option.Location}:private",
                     to = new List<string> { $"{tmdid}" },
                     label = $"{biz}_lessonRecord",
-                    body = new {
+                    body = new
+                    {
                         location = $"{Environment.GetEnvironmentVariable("Option:Location")}",
                         biz = biz,
                         tmdid = $"{tmdid}",
                         tmdname = tmdname,
-                        sid =$"{id}",
+                        sid = $"{id}",
                         sname = $"{name}",
                         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()
@@ -1428,12 +1469,12 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                 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")}";
+                var location = $"{Environment.GetEnvironmentVariable("Option:Location")}";
                 await _notificationService.SendNotification(clientID, clientSecret, location, url, notification); //站内发送消息
             }
             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.醍摩豆服務運維群組);
             }
 
         }
@@ -1652,7 +1693,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                         }
                         catch (CosmosException ex)
                         {
-                            await _dingDing.SendBotMsg($"OS,{ Environment.GetEnvironmentVariable("Option:Location")},CourseServiceBus -CosmosDB异常\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                            //await _dingDing.SendBotMsg($"OS,{ Environment.GetEnvironmentVariable("Option:Location")},CourseServiceBus -CosmosDB异常\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
                         }
                     }
                     foreach (var tmd in delTmdidsCls)
@@ -1712,7 +1753,7 @@ namespace TEAMModelOS.FunctionV4.ServiceBus
                         catch (CosmosException ex)
                         {
                             continue;
-                           // await _dingDing.SendBotMsg($"OS,{ Environment.GetEnvironmentVariable("Option:Location")},CourseServiceBus -CosmosDB异常\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
+                            // await _dingDing.SendBotMsg($"OS,{ Environment.GetEnvironmentVariable("Option:Location")},CourseServiceBus -CosmosDB异常\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
                         }
                     }
                     foreach (var tmd in delTmdidsCls)

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

@@ -5,9 +5,9 @@
 		<OutputType>Exe</OutputType>
 		<_FunctionsSkipCleanOutput>true</_FunctionsSkipCleanOutput>
 		<SignAssembly>true</SignAssembly>
-		<AssemblyVersion>5.2204.26.1</AssemblyVersion>
-		<FileVersion>5.2204.26.1</FileVersion>
-		<Version>5.2204.26</Version>
+		<AssemblyVersion>5.2205.7.1</AssemblyVersion>
+		<FileVersion>5.2205.7.1</FileVersion>
+		<Version>5.2205.7</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;
     }
 }

+ 21 - 3
TEAMModelOS.SDK/Extension/HttpContextExtensions.cs

@@ -66,10 +66,10 @@ namespace TEAMModelOS.SDK.Extension
         /// <returns></returns>
         public static (string id, string school) GetApiTokenInfo(this HttpContext httpContext, string key = null)
         {
-            object id = null,  school = null;
+            object id = null, school = null;
             httpContext?.Items.TryGetValue("ID", out id);
             httpContext?.Items.TryGetValue("School", out school);
-            return (id?.ToString(),  school?.ToString());
+            return (id?.ToString(), school?.ToString());
         }
         /// <summary>
         /// 取得AuthToken權杖資訊
@@ -86,7 +86,25 @@ namespace TEAMModelOS.SDK.Extension
 
             return (id?.ToString(), name?.ToString(), picture?.ToString(), school?.ToString());
         }
-
+        /// <summary>
+        /// 取得AuthToken權杖資訊
+        /// </summary>        
+        /// <param name="key">Key Name</param>
+        /// <returns></returns>
+        public static (string id, string name, string picture, string school, string area, string keyData) GetAuthTokenKey(this HttpContext httpContext, string key = null)
+        {
+            object id = null, name = null, picture = null, school = null, area = null, keyData = null;
+            httpContext?.Items.TryGetValue("ID", out id);
+            httpContext?.Items.TryGetValue("Name", out name);
+            httpContext?.Items.TryGetValue("Picture", out picture);
+            httpContext?.Items.TryGetValue("School", out school);
+            httpContext?.Items.TryGetValue("Area", out area);
+            if (!string.IsNullOrWhiteSpace(key))
+            {
+                httpContext?.Items.TryGetValue(key, out keyData);
+            }
+            return (id?.ToString(), name?.ToString(), picture?.ToString(), school?.ToString(), area?.ToString(), keyData?.ToString());
+        }
         /// <summary>
         /// 取得User-Agent值
         /// </summary>       

+ 5 - 4
TEAMModelOS.SDK/Extension/JwtAuthExtension.cs

@@ -14,7 +14,7 @@ namespace TEAMModelOS.SDK.Extension
 {
     public static class JwtAuthExtension
     {
-        public static string CreateAuthToken(string issuer, string id, string name, string picture, string salt, string scope,string Website, string schoolID = "", string standard = "", string[] roles = null, string[] permissions = null, int expire = 1)
+        public static string CreateAuthToken(string issuer, string id, string name, string picture, string salt, string scope, string Website, string areaId = "", string schoolID = "", string standard = "", string[] roles = null, string[] permissions = null, int expire = 1)
         {
             // 設定要加入到 JWT Token 中的聲明資訊(Claims)  
             var payload = new JwtPayload {
@@ -28,7 +28,8 @@ namespace TEAMModelOS.SDK.Extension
                 { "permissions",permissions}, //登入者的權限請求
                 { "standard",standard} ,//登入者的能力点标准
                 { "scope",scope},  //登入者的入口类型。 (teacher 教师端登录的醍摩豆ID、tmduser学生端登录的醍摩豆ID、student学生端登录校内账号的学生ID)
-                { JwtRegisteredClaimNames.Website,Website}, // 學校簡碼,如果有的話
+                { "area",areaId==null?"":areaId},
+                { JwtRegisteredClaimNames.Website,Website},
             };
 
             // 建立一組對稱式加密的金鑰,主要用於 JWT 簽章之用
@@ -45,9 +46,9 @@ namespace TEAMModelOS.SDK.Extension
 
             return serializeToken;
         }
-        public static string CreateApiToken(string issuer, string id,  string salt,string  name,List<int> auth , string schoolID = "", int expire = 1)
+        public static string CreateApiToken(string issuer, string id, string salt, string name, List<int> auth, string schoolID = "", int expire = 1)
         {
-           
+
             // 設定要加入到 JWT Token 中的聲明資訊(Claims)  
             var payload = new JwtPayload {
                 { JwtRegisteredClaimNames.Iss, issuer }, //發行者 iss: jwt签发者

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

+ 10 - 5
TEAMModelOS.SDK/Models/Cosmos/School/School.cs

@@ -42,9 +42,9 @@ namespace TEAMModelOS.SDK.Models
         /// 省份
         /// </summary>
         public string province { get; set; }
-       /// <summary>
-       /// 城市
-       /// </summary>
+        /// <summary>
+        /// 城市
+        /// </summary>
         public string city { get; set; }
         //县,区,郡
         public string dist { get; set; }
@@ -71,15 +71,20 @@ namespace TEAMModelOS.SDK.Models
         /// 1 普教,2 高职教
         /// </summary>
         public int type { get; set; }
-        public string standard { get; set; } 
+        public string standard { get; set; }
         /// <summary>
         /// 开启互评
         /// </summary>
-        public int hpappraise { get; set; }=0;
+        public int hpappraise { get; set; } = 0;
         /// <summary>
         /// 规模数量
         /// </summary>
         public int scale { get; set; }
+
+        /// <summary>
+        /// 创建时间  十位 时间戳
+        /// </summary>
+        public long createTime { get; set; }
     }
     /// <summary>
     /// 课表计划

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

+ 14 - 4
TEAMModelOS.SDK/Models/Cosmos/Student/Student.cs

@@ -2,9 +2,10 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
+using static TEAMModelOS.SDK.Models.Teacher;
 
 namespace TEAMModelOS.SDK.Models
-{  
+{
     public class Student : CosmosEntity
     {
         public string mail { get; set; }
@@ -32,10 +33,18 @@ namespace TEAMModelOS.SDK.Models
         //补充留级信息
         //0在校,1留级,2退学 3毕业
         public int status { get; set; } = 0;
-
+        /// <summary>
+        /// 保留当天的登录信息
+        /// </summary>
+        public List<LoginInfo> loginInfos { get; set; } = new List<LoginInfo>();
+        /// <summary>
+        /// 创建时间  十位 时间戳
+        /// </summary>
+        public long createTime { get; set; }
         //public List<StudentParent> parents { get; set; }= new List<StudentParent>();
     }
-    public class StudentParent {
+    public class StudentParent
+    {
         //绑定信息的key(手机号,邮箱,醍摩豆id)
         public List<BindKey> bindKey { get; set; } = new List<BindKey>();
         //姓名 姓名
@@ -47,7 +56,8 @@ namespace TEAMModelOS.SDK.Models
         /// </summary>
         public string picture { get; set; }
     }
-    public class BindKey {
+    public class BindKey
+    {
         // key(手机号,邮箱,醍摩豆id,Line,Google等信息)
         public string key { get; set; }
         public string type { get; set; }

+ 9 - 0
TEAMModelOS.SDK/Models/Cosmos/Student/TmdUser.cs

@@ -2,6 +2,7 @@ using System;
 using System.Collections.Generic;
 using System.Text;
 using TEAMModelOS.SDK.Models;
+using static TEAMModelOS.SDK.Models.Teacher;
 
 namespace TEAMModelOS.SDK
 {
@@ -11,6 +12,14 @@ namespace TEAMModelOS.SDK
         public string picture { get; set; }
         public string defaultSchool { get; set; }
         public List<School> schools { get; set; }
+        /// <summary>
+        /// 保留当天的登录信息
+        /// </summary>
+        public List<LoginInfo> loginInfos { get; set; } = new List<LoginInfo>();
+        /// <summary>
+        /// 创建时间 十位 时间戳
+        /// </summary>
+        public long createTime { get; set; }
         public class School
         {
             public string schoolId { get; set; }

+ 26 - 2
TEAMModelOS.SDK/Models/Cosmos/Teacher/Teacher.cs

@@ -16,6 +16,30 @@ namespace TEAMModelOS.SDK.Models
         public List<ThirdBind> binds { get; set; } = new List<ThirdBind>();
         public int lessonLimit { get; set; } = 0;
         public List<string> lessonShow { get; set; } = new List<string>();
+        /// <summary>
+        /// 创建时间  十位 时间戳
+        /// </summary>
+        public long createTime { get; set; }
+        /// <summary>
+        /// 保留当天的登录信息
+        /// </summary>
+        public List<LoginInfo> loginInfos { get; set; } = new List<LoginInfo>();
+
+        public class LoginInfo
+        {
+            /// <summary>
+            /// 时间戳 毫秒
+            /// </summary>
+            public long time { get; set; }
+            /// <summary>
+            /// ip地址
+            /// </summary>
+            public string ip { get; set; }
+            /// <summary>
+            /// token到期时间
+            /// </summary>
+            public long expire { get; set; }
+        }
         public class TeacherSchool
         {
             public string schoolId { get; set; }
@@ -46,8 +70,8 @@ namespace TEAMModelOS.SDK.Models
             public string username { get; set; }
             public string account { get; set; }
             // public HashSet<string> pxid { get; set; } = new HashSet<string>();
-            public List<string> data { get; set; }= new List<string>();
+            public List<string> data { get; set; } = new List<string>();
         }
-        
+
     }
 }

+ 1 - 1
TEAMModelOS.SDK/Models/Service/ActivityService.cs

@@ -701,7 +701,7 @@ namespace TEAMModelOS.SDK
                                 item.studentAnswers.Add(new List<string>());
                                 item.sum.Add(0);
                                 //item.scIds.Add(member.code??"");
-                                item.status.Add(0);
+                                item.status.Add(1);
                             }
                             else
                             {

+ 23 - 260
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>();
@@ -65,7 +65,7 @@ namespace TEAMModelOS.Services
                     teacher.areas.ForEach(x => {
                         areaIds.Add(x.areaId);
                     });
-                   
+
                 }
                 if (teacher.schools.IsNotEmpty())
                 {
@@ -149,11 +149,12 @@ namespace TEAMModelOS.Services
                                 schoolExtobj.area = area;
                             }
                             var sctch = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(id, new PartitionKey($"Teacher-{sc.schoolId}"));
-                            if (sctch.Status == 200  && sctch!= null  && sctch.ContentStream!=null)
+                            if (sctch.Status == 200 && sctch != null && sctch.ContentStream != null)
                             {
                                 var jsonDoc = await JsonDocument.ParseAsync(sctch.ContentStream);
                                 SchoolTeacher schoolTeacher = jsonDoc.RootElement.ToObject<SchoolTeacher>();
-                                if (schoolTeacher.name==null  || schoolTeacher.picture==null ||!schoolTeacher.name.Equals($"{name}") || !schoolTeacher.picture.Equals($"{picture}")) {
+                                if (schoolTeacher.name == null || schoolTeacher.picture == null || !schoolTeacher.name.Equals($"{name}") || !schoolTeacher.picture.Equals($"{picture}"))
+                                {
                                     schoolTeacher.name = $"{name}";
                                     schoolTeacher.picture = $"{picture}";
                                     await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync(schoolTeacher, id, new PartitionKey($"Teacher-{sc.schoolId}"));
@@ -195,7 +196,8 @@ namespace TEAMModelOS.Services
                         teacher.defaultSchool = teacher.schools[0].schoolId;
                     }
                 }
-                if (!string.IsNullOrEmpty(teacher.defaultSchool)) {
+                if (!string.IsNullOrEmpty(teacher.defaultSchool))
+                {
                     if (teacher.schools.IsNotEmpty())
                     {
                         var tech = teacher.schools.FindAll(x => x.status.Equals("join") && x.schoolId.Equals(teacher.defaultSchool));
@@ -206,19 +208,17 @@ namespace TEAMModelOS.Services
                             {
                                 teacher.defaultSchool = techde[0].schoolId;
                             }
-                            else {
+                            else
+                            {
                                 teacher.defaultSchool = null;
                             }
                         }
-                        else {
-                           
-                        }
                     }
-                    else {
+                    else
+                    {
                         teacher.defaultSchool = null;
                     }
                 }
-                await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
                 //預設學校ID
                 defaultschool = teacher.defaultSchool;
             }
@@ -229,6 +229,7 @@ namespace TEAMModelOS.Services
                     //如果沒有,則初始化Teacher基本資料到Cosmos
                     teacher = new Teacher
                     {
+                        createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                         id = id,
                         pk = "Base",
                         code = "Base",
@@ -274,7 +275,12 @@ 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());
+            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私有容器,如存在則不做任何事,保障容器一定存在
             var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete);
@@ -296,250 +302,7 @@ namespace TEAMModelOS.Services
             };
         }
 
-        public static async Task<TeacherInfo> GetTeacherInfo (AzureCosmosFactory _azureCosmos,Teacher teacher,string name,string picture,string id , AzureStorageFactory _azureStorage, Option _option)
-        {
-            List<object> schools = new List<object>();
-            List<AreaDto> areas = new List<AreaDto>();
-            List<Area> areasd = new List<Area>();
-            string defaultschool = null;
-            //TODO 取得Teacher 個人相關數據(課程清單、虛擬教室清單、歷史紀錄清單等),學校數據另外API處理,多校切換時不同
-            var client = _azureCosmos.GetCosmosClient();
-            int total = 0;
-            int tsize = 0;
-            try
-            {
-                if (teacher == null)
-                {
-                    teacher = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<Teacher>(id, new PartitionKey("Base"));
-                    teacher.name = $"{name}";
-                    teacher.picture = $"{picture}";
-                }
-                else {
-                    name = teacher.name;
-                    picture = teacher.picture;
-                    id = teacher.id;
-                }
-                ///教师的个人空间
-                tsize = teacher.size;
-                ///教师的总空间 包含 个人空间和学校赠送的空间累加
-                total = teacher.size;
-                //areas = teacher.areas;
-                if (teacher.areas.IsNotEmpty())
-                {
-                    foreach (var areat in teacher.areas)
-                    {
-                        try
-                        {
-                            Area area = await client.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemAsync<Area>($"{areat.areaId}", new PartitionKey("Base-Area"));
-                            areasd.Add(area);
-                            AreaSetting  setting= null;
-                            try 
-                            {
-                                setting= await client.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemAsync<AreaSetting>($"{areat.areaId}", new PartitionKey("AreaSetting"));
-                            } catch (CosmosException ex) {
-                                setting = null;
-                            }
-                            int access = 0;
-                            if (setting != null && !string.IsNullOrWhiteSpace(setting.accessConfig)) {
-                                access = 1;
-                            }
-                            if (setting != null) {
-                                setting.accessConfig = null;
-                            }
-                            areas.Add(new AreaDto { areaId = area.id, name = area.name,standard= area.standard,standardName= area.standardName,setting=setting, access= access });
-                        }
-                        catch (CosmosException)
-                        {
-                            //数据库捞不到数据
-                            continue;
-                        }
-                    }
-                }
-                //检查是否有加入学校,如果加入学校,则当个人空间size是0G的时候,则免费获得一个G空间,但无论加入多少个学校,只能获取一次 1G的免费空间。没有加入学校则默认0G空间,除非自己购买空间
-                if (teacher.schools.IsNotEmpty())
-                {
-                    foreach (var sc in teacher.schools)
-                    {
-                        string statusNow = sc.status != null ? sc.status : "";
-                        if (statusNow.Equals("join") || statusNow.Equals("invite") || statusNow.Equals("request"))
-                        {
-                            dynamic schoolExtobj = new ExpandoObject();
-                            // var schoolJson = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync($"{sc.schoolId}", new PartitionKey("Base"));
-                            //var school = await JsonDocument.ParseAsync(schoolJson.ContentStream);
-                            schoolExtobj.schoolId = sc.schoolId;
-                            schoolExtobj.name = sc.name;
-                            schoolExtobj.status = sc.status;
-                            schoolExtobj.time = sc.time;
-                            schoolExtobj.picture = sc.picture;
-                            schoolExtobj.areaId = $"{sc.areaId}";
-                            Area area = null;
-                            int access = 0;
-                            if (!string.IsNullOrEmpty($"{sc.areaId}"))
-                            {
-                                
-                                try
-                                {
-                                    
-                                    area = await client.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemAsync<Area>($"{sc.areaId}", new PartitionKey("Base-Area"));
-                                    AreaSetting setting = null;
-                                    try
-                                    {
-                                        setting = await client.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemAsync<AreaSetting>($"{area.id}", new PartitionKey("AreaSetting"));
-                                    }
-                                    catch (CosmosException ex)
-                                    {
-                                        setting = null;
-                                    }
-                                      
-                                    if (setting != null && !string.IsNullOrWhiteSpace(setting.accessConfig))
-                                    {
-                                        access = 1;
-                                    }
-                                    areasd.Add(area);
-                                     
-                                }
-                                catch (CosmosException)
-                                {
-                                    area = null;
-                                }
-                            }
-                            if (area != null)
-                            {
-                                schoolExtobj.area = new {area.name, area.id, area.institution, area.provName, area.code, area.cityCode, area.cityName, area.standard, area.provCode, area.pk, access };
-
-                            }
-                            else {
-                                schoolExtobj.area = area;
-                            }
-                            var sctch = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(id, new PartitionKey($"Teacher-{sc.schoolId}"));
-                            if (sctch.Status == 200)
-                            {
-                                var jsonDoc = await JsonDocument.ParseAsync(sctch.ContentStream);
-                                SchoolTeacher schoolTeacher = jsonDoc.RootElement.ToObject<SchoolTeacher>();
-                                schoolTeacher.name = $"{name}";
-                                schoolTeacher.picture = $"{picture}";
-                                await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync(schoolTeacher, id, new PartitionKey($"Teacher-{sc.schoolId}"));
-                                if (jsonDoc.RootElement.TryGetProperty("size", out JsonElement _size) && _size.ValueKind.Equals(JsonValueKind.Number))
-                                {
-                                    total += _size.GetInt32();
-                                    schoolExtobj.size = _size.GetInt32();
-                                }
-                                else { schoolExtobj.size = 0; }
-                            }
-                            else
-                            {
-                                schoolExtobj.size = 0;
-                            }
-                            if (statusNow.Equals("join"))
-                            {
-                                //初始化
-                                await TmdUserService.JoinSchool(client, teacher.id, teacher.picture, teacher.name, sc.schoolId, sc.name);
-                            }
-                            schools.Add(schoolExtobj);
-                        }
-                    }
-                    //如果包含任何申请,邀请,加入学校的记录 且个人空间未分配2G  则默认分配个人空间至2G.
-                    if (teacher.size < 2 && teacher.schools.Count > 0)
-                    {
-                        teacher.size = 2;
-                    }
-                    //如果未包含任何申请,邀请,加入学校的记录 且 个人空间没有分配1G 则默认赠送一个G 
-                    if (teacher.schools.Count == 0 && teacher.size < 1)
-                    {
-                        teacher.size = 1;
-                    }
-                }
-                if (string.IsNullOrEmpty(teacher.defaultSchool) && teacher.schools.IsNotEmpty())
-                {
-                    var tech = teacher.schools.FindAll(x => x.status.Equals("join"));
-                    if (tech.IsNotEmpty())
-                    {
-                        teacher.defaultSchool = teacher.schools[0].schoolId;
-                    }
-                }
-                await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
-                //預設學校ID
-                defaultschool = teacher.defaultSchool;
-            }
-            catch (CosmosException ex)
-            {
-                if (ex.Status == 404)
-                {
-                    //如果沒有,則初始化Teacher基本資料到Cosmos
-                      teacher = new Teacher
-                    {
-                        id = id,
-                        pk = "Base",
-                        code = "Base",
-                        name = name?.ToString(),
-                        picture = picture?.ToString(),
-                        //创建账号并第一次登录IES5则默认赠送1G
-                        size = 1,
-                        defaultSchool = null,
-                        schools = new List<Teacher.TeacherSchool>(),
-                    };
-                    var container = _azureStorage.GetBlobContainerClient(id);
-                    await container.CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建Teacher私有容器,如存在則不做任何事,保障容器一定存在
-                    teacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").CreateItemAsync<Teacher>(teacher, new PartitionKey("Base"));
-                    total = teacher.size;
-                    tsize = teacher.size;
-                }
-            }
-            //私人課程
-            List<object> courses = new List<object>();
-            await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"select value(c) from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{id}") }))
-            {
-                using var json = await JsonDocument.ParseAsync(item.ContentStream);
-                if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
-                {
-                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
-                    {
-                        courses.Add(obj.ToObject<object>());
-                    }
-                }
-            }
-
-             
-            List<string> roles = new List<string>() { "teacher" };
-            Area areaa = null;
-            if (areas.Count > 0)
-            {
-                roles.Add("area");
-                if (!string.IsNullOrEmpty($"{areas[0]}"))
-                {
-
-                    try
-                    {
-                        areaa = await client.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemAsync<Area>($"{areas[0].areaId}", new PartitionKey("Base-Area"));
-                    }
-                    catch (CosmosException)
-                    {
-                        areaa = null;
-                    }
-                }
-            }
-            //換取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());
-            //取得Teacher Blob 容器位置及SAS 
-            await _azureStorage.GetBlobContainerClient(id).CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建Teacher私有容器,如存在則不做任何事,保障容器一定存在
-            var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete);
-            var (osblob_uri, osblob_sas) = roles.Contains("area") ? _azureStorage.GetBlobContainerSAS("teammodelos", BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete) : _azureStorage.GetBlobContainerSAS("teammodelos", BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List);
-            return new TeacherInfo {
-                auth_token = auth_token,
-                blob_uri = blob_uri,
-                blob_sas = blob_sas,
-                schools = schools,
-                defaultschool = defaultschool,
-                courses = courses,
-                total=total,
-                osblob_sas=osblob_sas,
-                osblob_uri = osblob_uri,
-                tsize=tsize,
-                areas= areas,
-                teacher=teacher
-            };
-        }
-    }
+       }
     public record AreaDto
     {
         //areaId = area.id, name = area.name, area.standard, area.standardName
@@ -551,19 +314,19 @@ namespace TEAMModelOS.Services
         public AreaSetting setting { get; set; }
         public int access { get; set; } = 0;
     }
-    public record TeacherInfo { 
+    public record TeacherInfo
+    {
         public string auth_token { get; set; }
         public string blob_uri { get; set; }
         public string blob_sas { get; set; }
-        public List<object> schools { get; set; }= new List<object> ();
+        public List<object> schools { get; set; } = new List<object>();
         public string defaultschool { get; set; }
-        public List<object> courses { get; set; }= new List<object>();
+        public List<object> courses { get; set; } = new List<object>();
         public int total { get; set; }
         public string osblob_uri { get; set; }
         public string osblob_sas { get; set; }
         public int tsize { get; set; }
         public List<AreaDto> areas { get; set; } = new List<AreaDto>();
         public Teacher teacher { get; set; }
-       
     }
 }

+ 352 - 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,355 @@ 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.expire = -1;
+                }
+            }
+        }
+
+
+        public record ExpireTag {
+            public long expire { get; set; }
+            public string tag { get; set; }
+        }
         /// <summary>
         /// 
         /// </summary>

+ 283 - 0
TEAMModelOS.SDK/Models/Service/LoginService.cs

@@ -0,0 +1,283 @@
+using Azure.Cosmos;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using StackExchange.Redis;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+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
+{
+    public static class LoginService
+    {
+        /// <summary>
+        /// //添加用户登录信息和在线登录
+        /// </summary>
+        /// <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, 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)
+                {
+                    loginInfos = loginInfos.OrderBy(x => x.expire).ToList();
+                    loginInfos.RemoveRange(4, loginInfos.Count() - 4);
+                }
+                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)
+            {
+                await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:IES:{scope}:{dateDay}", hour);  //设置到期时间
+            }
+
+            var rspMonth = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"Login:IES:{scope}:{dateMonth}");
+            if (rspMonth == null)
+            {
+                await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:IES:{scope}:{dateMonth}", month);  //设置到期时间
+            }
+
+            ////查询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 mCnt in monthCnt)
+            //    {
+            //        monthCounts.Add(new { code = mCnt.Element.ToString(), count = (int)mCnt.Score });
+            //    }
+            //}
+
+            //保存当天没小时的峰值
+            HourLogin hourLogin = new() { PartitionKey = $"HourLogin", RowKey = now.ToString(), Hour = int.Parse(dateHour) };
+            if (scope.Equals("teacher"))
+            {
+                hourLogin.Teacher = int.Parse(id);
+                hourLogin.Student = 0;
+                hourLogin.TmdUser = 0;
+            }
+            else if (scope.Equals("student"))
+            {
+                hourLogin.Teacher = 0;
+                hourLogin.Student = int.Parse(id);
+                hourLogin.TmdUser = 0;
+            }
+            else
+            {
+                hourLogin.Teacher = 0;
+                hourLogin.Student = 0;
+                hourLogin.TmdUser = int.Parse(id);
+            }
+            try
+            {
+                await table.SaveOrUpdate<HourLogin>(hourLogin);//保存在线数据
+            }
+            catch
+            {
+            }
+
+            //保存当月每天的峰值
+            DayLogin dayLogin = new() { PartitionKey = $"DayLogin", RowKey = now.ToString(), Day = int.Parse(dateDay) };
+            if (scope.Equals("teacher"))
+            {
+                dayLogin.Teacher = int.Parse(id);
+                dayLogin.Student = 0;
+                dayLogin.TmdUser = 0;
+            }
+            else if (scope.Equals("student"))
+            {
+                dayLogin.Teacher = 0;
+                dayLogin.Student = int.Parse(id);
+                dayLogin.TmdUser = 0;
+            }
+            else
+            {
+                dayLogin.Teacher = 0;
+                dayLogin.Student = 0;
+                dayLogin.TmdUser = int.Parse(id);
+            }
+
+            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 });
+                //    }
+                //}
+
+                //学校小时峰值
+                HourLoginSchool hourLoginSc = new() { PartitionKey = $"HourLogin-{school}", RowKey = now.ToString(), Hour = int.Parse(dateHour), School = school };
+                if (scope.Equals("teacher"))
+                {
+                    hourLoginSc.Teacher = int.Parse(id);
+                    hourLoginSc.Student = 0;
+                    hourLoginSc.TmdUser = 0;
+                }
+                else if (scope.Equals("student"))
+                {
+                    hourLoginSc.Teacher = 0;
+                    hourLoginSc.Student = int.Parse(id);
+                    hourLoginSc.TmdUser = 0;
+                }
+                else
+                {
+                    hourLoginSc.Teacher = 0;
+                    hourLoginSc.Student = 0;
+                    hourLoginSc.TmdUser = int.Parse(id);
+                }
+
+                try
+                {
+                    await table.SaveOrUpdate<HourLoginSchool>(hourLoginSc);//保存在线数据
+                }
+                catch
+                {                    
+                }
+
+                //学校天峰值
+                DayLoginSchool dayLoginSc = new() { PartitionKey = $"DayLogin-{school}", RowKey = now.ToString(), Day = int.Parse(dateDay), School = school };
+                if (scope.Equals("teacher"))
+                {
+                    dayLoginSc.Teacher = int.Parse(id);
+                    dayLoginSc.Student = 0;
+                    dayLoginSc.TmdUser = 0;
+                }
+                else if (scope.Equals("student"))
+                {
+                    dayLoginSc.Teacher = 0;
+                    dayLoginSc.Student = int.Parse(id);
+                    dayLoginSc.TmdUser = 0;
+                }
+                else
+                {
+                    dayLoginSc.Teacher = 0;
+                    dayLoginSc.Student = 0;
+                    dayLoginSc.TmdUser = int.Parse(id);
+                }
+
+                try
+                {
+                    await table.SaveOrUpdate<DayLoginSchool>(dayLoginSc);//保存在线数据
+                }
+                catch
+                {
+                }
+            }
+
+            return loginInfos;
+        }
+
+        public static async Task<(string ip, string region)> LoginIp(HttpContext httpContext, IPSearcher _searcher)
+        {
+
+            var IpPort = httpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
+            if (string.IsNullOrEmpty(IpPort))
+            {
+                IpPort = httpContext.Connection.RemoteIpAddress.ToString();
+            }
+            if (IpPort.Contains("::"))
+            {
+                IpPort = "127.0.0.1";
+            }
+            string ip = IpPort.Split(":")[0];
+            string region = await _searcher.SearchIpAsync(ip);
+            return (ip, region);
+        }
+
+        public static void LoginLog(HttpContext httpContext, Option _option, ILogger _logger, DingDing _dingDing, string ip, string region, string id, string name, int status)
+        {
+            httpContext.Request.Headers.TryGetValue("referer", out var referer);
+            httpContext.Request.Headers.TryGetValue("sec-ch-ua", out var chua);
+            httpContext.Request.Headers.TryGetValue("sec-ch-ua-platform", out var platform);
+            httpContext.Request.Headers.TryGetValue("user-agent", out var useragent);
+            if (status == 200)
+            {
+                string msg = $"{_option.Location} -- {name}({id}):Login Succeed! --Time:{DateTimeOffset.UtcNow.ToString("yyyy-MM-dd HH:mm:ss")} " +
+                    $"--Browser Info( --referer:{referer}  --sec-ch-ua:{chua}  --sec-ch-ua-platform:{platform}  --user-agent:{useragent}  --ip:{ip} --region:{region} )";
+                _logger.LogInformation(msg);
+            }
+            else
+            {
+                string msg = $"{_option.Location} -- {name}({id}):Login Failed! --Time:{DateTimeOffset.UtcNow.ToString("yyyy-MM-dd HH:mm:ss")} " +
+                         $"--Browser Info( --referer:{referer}  --sec-ch-ua:{chua}  --sec-ch-ua-platform:{platform}  --user-agent:{useragent}  --ip:{ip} --region:{region} )";
+                _ = _dingDing.SendBotMsg($"IES5,{_option.Location},{msg}", GroupNames.醍摩豆服務運維群組);
+                _logger.LogError(msg);
+            }
+        }
+    }
+}

+ 19 - 13
TEAMModelOS.SDK/Models/Service/StudentService.cs

@@ -26,7 +26,7 @@ namespace TEAMModelOS.SDK
         /// <param name="schoolId"></param>
         /// <param name="students"></param>
         /// <returns></returns>
-        public static async Task<List<Student>> GeStudentData(AzureCosmosFactory _azureCosmos ,string schoolId, IEnumerable<string> students)
+        public static async Task<List<Student>> GeStudentData(AzureCosmosFactory _azureCosmos, string schoolId, IEnumerable<string> students)
         {
             List<Student> studentDatas = new List<Student>();
             if (students.Any())
@@ -49,7 +49,7 @@ namespace TEAMModelOS.SDK
         /// <param name="opt">操作</param>
         /// <param name="prestudents">变更前的学生</param>
         /// <returns></returns>
-        public static async Task<Dictionary<string, GroupChange>> CheckStudent(AzureServiceBusFactory _serviceBus,IConfiguration _configuration,AzureCosmosFactory _azureCosmos, string schoolId, List<Student> students, List<Student> prestudents)
+        public static async Task<Dictionary<string, GroupChange>> CheckStudent(AzureServiceBusFactory _serviceBus, IConfiguration _configuration, AzureCosmosFactory _azureCosmos, string schoolId, List<Student> students, List<Student> prestudents)
         {
             List<Student> aftstudents = await StudentService.GeStudentData(_azureCosmos, schoolId, students?.Select(x => x.id));
             Dictionary<string, GroupChange> dictChange = new Dictionary<string, GroupChange>();
@@ -602,7 +602,7 @@ namespace TEAMModelOS.SDK
 
                 //var   classNos = sortedImpData.classInfo.Select(o => new {key= o.Key, periodId=o.Value.periodId,index= o.Value.gradeIndex,year = o.Value.year }).ToList();
                 //抓到教室資訊
-                var classInfos = await getClassInfoUseNo( _azureCosmos, _dingDing, _option, schoolId, sortedImpData.classInfo);
+                var classInfos = await getClassInfoUseNo(_azureCosmos, _dingDing, _option, schoolId, sortedImpData.classInfo);
                 //取出已存在教室的classId,後面查座號要用。
 
                 List<Task> tasks = new List<Task>();
@@ -663,7 +663,7 @@ namespace TEAMModelOS.SDK
                         //}
                         //建立新教室
                         (string classId, string classNo, string className, string periodId, string gradeId, int classYear) retCreateClassInfo =
-                        await createClassInfo(_azureCosmos,_dingDing,_option,
+                        await createClassInfo(_azureCosmos, _dingDing, _option,
                             schoolId,
                             null,
                             sortedImpData.classInfo[item.Key].className,
@@ -694,6 +694,7 @@ namespace TEAMModelOS.SDK
 
                 CosmosContainer cosmosContainer = _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student");
 
+                long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
                 //並行處理
                 foreach (var stud in sortedImpData.studs)
                 {
@@ -749,6 +750,7 @@ namespace TEAMModelOS.SDK
                     writerNew.WriteString("code", $"Base-{schoolId}");
                     writerNew.WriteString("schoolId", schoolId);
                     writerNew.WriteNumber("year", stud.Value.year);
+                    writerNew.WriteNumber("createTime", now);
                     writerNew.WriteString("salt", stud.Value.salt);
                     writerNew.WriteString("pw", stud.Value.pw);
                     if (string.IsNullOrWhiteSpace(stud.Value.name)) writerNew.WriteNull("name");
@@ -758,6 +760,7 @@ namespace TEAMModelOS.SDK
                     writerNew.WriteNull("mail");
                     writerNew.WriteNull("mobile");
                     writerNew.WriteNull("country");
+
                     if (!string.IsNullOrWhiteSpace(stud.Value.periodId))
                     {
                         writerNew.WriteString("periodId", stud.Value.periodId);
@@ -858,8 +861,8 @@ namespace TEAMModelOS.SDK
                             tmpStudInfo.classId,
                             tmpStudInfo.classNo,
                             tmpStudInfo.className,
-                           // tmpStudInfo.gradeId,
-                           // tmpStudInfo.periodId
+                            // tmpStudInfo.gradeId,
+                            // tmpStudInfo.periodId
                         });
                 }
                 return (retStuds, duplNos.Where(o => o.Value.Count != 0).ToDictionary(o => o.Key, o => o.Value), errorIds);
@@ -898,11 +901,12 @@ namespace TEAMModelOS.SDK
                 if (string.IsNullOrWhiteSpace(studCreateInfo.periodId)) writer.WriteNull("periodId");
                 else writer.WriteString("periodId", studCreateInfo.periodId);
                 writer.WriteNumber("year", studCreateInfo.year);
+                writer.WriteNumber("createTime", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
                 writer.WriteNull("picture");
                 writer.WriteNull("mail");
                 writer.WriteNull("mobile");
                 writer.WriteNull("country");
-
+             
                 //Password,若沒給則使用學號當密碼
                 string salt = Utils.CreatSaltString(8);
                 string hashPw = string.IsNullOrWhiteSpace(studCreateInfo.pw)
@@ -928,6 +932,7 @@ namespace TEAMModelOS.SDK
 
                 writer.WriteEndObject();
                 writer.Flush();
+
                 var response = await _azureCosmos
                                 .GetCosmosClient()
                                 .GetContainer(Constant.TEAMModelOS, "Student")
@@ -994,6 +999,7 @@ namespace TEAMModelOS.SDK
                 if (string.IsNullOrWhiteSpace(periodId)) writer.WriteNull("periodId");
                 else writer.WriteString("periodId", periodId);
                 writer.WriteNumber("year", classYear);
+                writer.WriteNumber("createTime", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
                 writer.WriteNull("sn");
                 writer.WriteString("style", "smart");
                 writer.WriteString("openType", "1");
@@ -1045,7 +1051,7 @@ namespace TEAMModelOS.SDK
                 List<string> searchId = new List<string>();
                 if (!string.IsNullOrWhiteSpace(byClassId))
                 {
-                    var classInfos = await getClassInfoUseId(_azureCosmos,_dingDing,_option, schoolId, new List<string>() { byClassId });
+                    var classInfos = await getClassInfoUseId(_azureCosmos, _dingDing, _option, schoolId, new List<string>() { byClassId });
                     foreach (var classInfo in classInfos)
                     {
                         var students = classInfo.Value.GetProperty("students").EnumerateArray();
@@ -1259,7 +1265,7 @@ namespace TEAMModelOS.SDK
                 List<object> ret = new List<object>();
 
                 //查教室的資訊,用以取得gradeId,periodId資訊。
-                var classInfos = await getClassInfoUseId(_azureCosmos,_dingDing,_option, schoolId, dicClassStuds.Keys.ToList());
+                var classInfos = await getClassInfoUseId(_azureCosmos, _dingDing, _option, schoolId, dicClassStuds.Keys.ToList());
 
                 //輪循所有教室學生的資料
                 foreach (var classStud in dicClassStuds)
@@ -1373,7 +1379,7 @@ namespace TEAMModelOS.SDK
         /// <param name="schoolId"></param>
         /// <param name="students"></param>
         /// <returns></returns>
-        public  static  async Task<List<string>> deleteStudents(AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option, string schoolId, JsonElement.ArrayEnumerator students)
+        public static async Task<List<string>> deleteStudents(AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option, string schoolId, JsonElement.ArrayEnumerator students)
         {
             List<string> sucIds = new List<string>();
             try
@@ -1462,7 +1468,7 @@ namespace TEAMModelOS.SDK
         /// <param name="schoolId"></param>
         /// <param name="students">["id1","id2",...]</param>
         /// <returns></returns>
-        public static  async Task<(List<string> studs, List<string> nonexistentIds, List<string> errorIds)> removeStudentClassInfo(AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option, string schoolId, JsonElement.ArrayEnumerator students)
+        public static async Task<(List<string> studs, List<string> nonexistentIds, List<string> errorIds)> removeStudentClassInfo(AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option, string schoolId, JsonElement.ArrayEnumerator students)
         {
             //紀錄輸入的學生
             List<string> impStuds = new List<string>();
@@ -1777,7 +1783,7 @@ namespace TEAMModelOS.SDK
                 }
 
                 //查詢欲加入的教室資訊。
-                var classInfo = await getClassInfoUseId(_azureCosmos,_dingDing,_option, schoolId, classStuds.Keys.ToList());
+                var classInfo = await getClassInfoUseId(_azureCosmos, _dingDing, _option, schoolId, classStuds.Keys.ToList());
 
                 //準備查詢db資料
                 CosmosContainer cosmosContainer = _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student");
@@ -1815,7 +1821,7 @@ namespace TEAMModelOS.SDK
                     //檢查座號是否有重複
                     //若只是改基本資料,該處還是會查到相同的座號。
                     var sutdNos = item.Value.Select(o => o.no).ToList();
-                    var existNos = await checkStudNo(_azureCosmos,_dingDing,_option, schoolId, item.Key, sutdNos);
+                    var existNos = await checkStudNo(_azureCosmos, _dingDing, _option, schoolId, item.Key, sutdNos);
 
                     //更新並寫入學生資料
                     if (item.Value.Count != 0)

+ 38 - 3
TEAMModelOS.SDK/Models/Service/Third/Xkw/XkwOAuthModel.cs

@@ -9,7 +9,7 @@ using TEAMModelOS.SDK.Context.Attributes.Azure;
 namespace TEAMModelOS.SDK.Models
 {
 
-    public class XkwOAuthModel 
+    public class XkwOAuthModel
     {
     }
     [TableName(Name = "IESOAuth")]
@@ -50,7 +50,8 @@ namespace TEAMModelOS.SDK.Models
     }
 
     [TableName(Name = "IESOAuth")]
-    public class OAuthUser : TableEntity {
+    public class OAuthUser : TableEntity
+    {
         //PartitionKey OAuthUser-xkw, OAuthUser-axy , 单点配置
         //RowKey  tmdid
         public string OpenId { get; set; }
@@ -60,6 +61,34 @@ namespace TEAMModelOS.SDK.Models
         public string Type { get; set; }
         public string Domain { get; set; }
     }
+    [TableName(Name = "IESOAuth")]
+    public class OAuthShow : TableEntity
+    {
+        //PartitionKey OAuthShow
+        //RowKey  {type}-tmdid,{type}-schoolId,{type}-areaId
+        /// <summary>
+        /// xkw axy
+        /// </summary>
+        public string Type { get; set; }
+        /// <summary>
+        /// 0  禁用,1允许使用
+        /// </summary>
+        public int Status { get; set; }
+        /// <summary>
+        /// 
+        ///个人开通 private,学校开通 school ,区级开通 area
+        /// </summary>
+        public string Scope { get; set; }
+        /// <summary>
+        /// tmdid  schoolId  areaId
+        /// </summary>
+        public string Code { get; set; }
+        /// <summary>
+        /// 域名
+        /// </summary>
+        public string Domain { get; set; }
+
+    }
     public class XkwBindModel
     {
         public int status { get; set; }
@@ -69,8 +98,14 @@ namespace TEAMModelOS.SDK.Models
         public string userId { get; set; }
         public string domain { get; set; }
     }
-    public record OAuthCode {
+    public record ServiceModule { 
+        public string module { get; set; }
+        public string url { get; set; }
+    }
+    public record OAuthCode
+    {
         public string code { get; set; }
+        public string tmdid { get; set; }
         public string state { get; set; }
         //模块,res,item,paper
         public string module { get; set; }

+ 22 - 15
TEAMModelOS.SDK/Models/Service/TmdUserService.cs

@@ -10,38 +10,45 @@ namespace TEAMModelOS.SDK
 {
     public class TmdUserService
     {
-        public static async Task<TmdUser> JoinSchool(CosmosClient client, string tmdid,string picture,string name , string schoolId, string schoolName) {
+        public static async Task<TmdUser> JoinSchool(CosmosClient client, string tmdid, string picture, string name, string schoolId, string schoolName)
+        {
             TmdUser tmdUser = null;
-            try {
-                tmdUser= await client.GetContainer(Constant.TEAMModelOS, "Student").ReadItemAsync<TmdUser>(tmdid, partitionKey: new PartitionKey("Base"));
-                tmdUser.picture = string.IsNullOrEmpty(picture) ? picture:tmdUser.picture;
+            try
+            {
+                tmdUser = await client.GetContainer(Constant.TEAMModelOS, "Student").ReadItemAsync<TmdUser>(tmdid, partitionKey: new PartitionKey("Base"));
+                tmdUser.picture = string.IsNullOrEmpty(picture) ? picture : tmdUser.picture;
                 tmdUser.name = string.IsNullOrEmpty(name) ? name : tmdUser.name;
-                var school= tmdUser.schools.Find(x => x.schoolId.Equals(schoolId));
+                var school = tmdUser.schools.Find(x => x.schoolId.Equals(schoolId));
                 if (school == null)
                 {
 
                     tmdUser.schools.Add(new TmdUser.School { schoolId = schoolId, name = schoolName, status = "join", time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() });
                     await client.GetContainer(Constant.TEAMModelOS, "Student").ReplaceItemAsync<TmdUser>(tmdUser, tmdid, partitionKey: new PartitionKey("Base"));
                 }
-                else {
+                else
+                {
                     school.name = schoolName;
                     await client.GetContainer(Constant.TEAMModelOS, "Student").ReplaceItemAsync<TmdUser>(tmdUser, tmdid, partitionKey: new PartitionKey("Base"));
                 }
-            } catch (CosmosException ex) {
-                if (ex.Status == 404) {
+            }
+            catch (CosmosException ex)
+            {
+                if (ex.Status == 404)
+                {
                     tmdUser = new TmdUser()
                     {
+                        createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                         id = tmdid,
                         code = "Base",
-                        schools = new List<TmdUser.School>() { 
-                            new TmdUser.School { 
+                        schools = new List<TmdUser.School>() {
+                            new TmdUser.School {
                                 schoolId = schoolId, status = "join", name = schoolName, time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
-                            } 
+                            }
                         },
-                        defaultSchool=schoolId,
-                        picture=picture,
-                        name=name,
-                        ttl=-1
+                        defaultSchool = schoolId,
+                        picture = picture,
+                        name = name,
+                        ttl = -1
                     };
                     await client.GetContainer(Constant.TEAMModelOS, "Student").CreateItemAsync(tmdUser, partitionKey: new PartitionKey(tmdUser.code));
                 }

+ 139 - 0
TEAMModelOS.SDK/Models/Table/IESLogin.cs

@@ -0,0 +1,139 @@
+using Microsoft.Azure.Cosmos.Table;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TEAMModelOS.SDK.Context.Attributes.Azure;
+
+namespace TEAMModelOS.SDK.Models.Table
+{
+    [TableName(Name = "IESLogin")]
+    public class UserLogin : TableEntity
+    {
+        ///// <summary>
+        ///// UserLogin
+        ///// </summary>
+        //public string PartitionKey { get; set; }
+        ///// <summary>
+        ///// tmdid或者校内账户  stuid-schoolid 
+        ///// </summary>
+        //public string RowKey { get; set; }
+        //public string School { get; set; }
+        ///// <summary>
+        ///// tmdid或者校内id
+        ///// </summary>
+        //public string UserId { get; set; }
+        ///// <summary>
+        ///// 角色
+        ///// </summary>
+        //public string Role { get; set; }
+        ///// <summary>
+        ///// 2022050511  yyyyMMddHH
+        ///// </summary>
+        //public int Hour { get; set; }
+        ///// <summary>
+        ///// 20220505  yyyyMMdd
+        ///// </summary>
+        //public int Day { get; set; }
+        ///// <summary>
+        ///// 时间戳 毫秒
+        ///// </summary>
+        //public long  Time{ get; set; }
+    }
+
+
+    /// <summary>
+    /// 小时峰值统计登录信息
+    /// </summary>
+    [TableName(Name = "IESLogin")]
+    public class HourLogin : TableEntity
+    {
+        /// <summary>
+        /// HourLogin   继承table不需要重复命名
+        /// </summary>
+        //public string PartitionKey { get; set; }
+        /// <summary>
+        /// 2022050511  yyyyMMddHH   继承table不需要重复命名
+        /// </summary>
+        //public string RowKey { get; set; }
+        /// <summary>
+        /// 2022050512  yyyyMMddHH
+        /// </summary>
+        public int Hour { get; set; }
+        public int Teacher { get; set; }
+        public int Student { get; set; }
+        public int TmdUser { get; set; }
+    }
+
+    /// <summary>
+    /// 按天统计登录信息
+    /// </summary>
+    [TableName(Name = "IESLogin")]
+    public class DayLogin : TableEntity
+    {
+        /// <summary>
+        /// DayLogin  继承table不需要重复命名
+        /// </summary>
+        //public string PartitionKey { get; set; }
+        /// <summary>
+        /// 20220505  yyyyMMdd  继承table不需要重复命名
+        /// </summary>
+        //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; }
+    }
+
+    /// <summary>
+    /// 学校按天统计登录信息
+    /// </summary>
+    [TableName(Name = "IESLogin")]
+    public class DayLoginSchool : TableEntity
+    {
+        /// <summary>
+        /// DayLogin-hbcn学校编码 继承table不需要重复命名
+        /// </summary>
+        //public string PartitionKey { get; set; }
+        /// <summary>
+        /// 20220505  yyyyMMdd   继承table不需要重复命名
+        /// </summary>
+        //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; }
+    }
+
+    /// <summary>
+    ///  学校小时峰值统计登录信息
+    /// </summary>
+    [TableName(Name = "IESLogin")]
+    public class HourLoginSchool : TableEntity
+    {
+        /// <summary>
+        /// HourLogin-hbcn学校编码  继承table不需要重复命名
+        /// </summary>
+        //public string PartitionKey { get; set; }
+        /// <summary>
+        /// 2022050511  yyyyMMddHH
+        /// </summary>
+        //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; }
+    }
+}

+ 70 - 56
TEAMModelOS/ClientApp/public/index.html

@@ -1,59 +1,73 @@
 <!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">
-		<title></title>
-		<script>
-			MathJax = {
-				loader: {
-					load: ["input/tex", "output/svg"]
-				},
-				tex: {
-					inlineMath: [
-						['$', '$'],
-						['\\(', '\\)']
-					],
-					displayMath: [
-						["$$", "$$"],
-						["\\[", "\\]"]
-					]
-				},
-				svg: {
-					fontCache: 'local'
-				},
-				skipTags: ["script", "noscript", "style", "textarea", "pre", "code", "a"] //避开某些标签
-			};
-			let cloudSetting = localStorage.getItem('cloudSetting')
-			if (cloudSetting) {
-				cloudSetting = JSON.parse(cloudSetting)
-				document.documentElement.setAttribute('lang', cloudSetting.curLang || navigator.language);
-				if (cloudSetting.curTheme && cloudSetting.curTheme == 'light') {
-					document.getElementById('theme').href = '/theme/light-theme.css'
-				}
+
+<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-TW.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"]
+			},
+			tex: {
+				inlineMath: [
+					['$', '$'],
+					['\\(', '\\)']
+				],
+				displayMath: [
+					["$$", "$$"],
+					["\\[", "\\]"]
+				]
+			},
+			svg: {
+				fontCache: 'local'
+			},
+			skipTags: ["script", "noscript", "style", "textarea", "pre", "code", "a"] //避开某些标签
+		};
+		// 本地设置赋值
+		let cloudSetting = localStorage.getItem('cloudSetting')
+		if (cloudSetting) {
+			cloudSetting = JSON.parse(cloudSetting)
+			document.documentElement.setAttribute('lang', cloudSetting.curLang || navigator.language);
+			if (cloudSetting.curTheme && cloudSetting.curTheme == 'light') {
+				document.getElementById('theme').href = '/theme/light-theme.css'
 			}
-			// 判断是否为国际站
-			let isGlobalSite = window.location.origin.includes('teammodel.net')
-			let blobHost = isGlobalSite ? 'https://teammodel.blob.core.windows.net' :
-				'https://teammodelos.blob.core.chinacloudapi.cn'
-			document.write('<script id="vue"  src="' + blobHost + '/0-public/js/vue.js"><\/script>')
-			document.write('<script id="iview"  src="' + blobHost + '/0-public/js/iview.min.js"><\/script>')
-			document.write('<script id="fabric"  src="' + blobHost + '/0-public/js/fabric.min.js"><\/script>')
-			document.write('<script id="vueRouter"  src="' + blobHost + '/0-public/js/vue-router.min.js"><\/script>')
-			document.write('<script id="echarts"  src="' + blobHost + '/0-public/js/echarts.js"><\/script>')
-			document.write('<script id="jspdf"  src="' + blobHost + '/0-public/js/jspdf.umd.min.js"><\/script>')
-			document.write('<script id="mathjax"  src="' + blobHost + '/0-public/js/tex-mml-svg.js"><\/script>')
-			document.write('<script id="mediaInfo"  src="' + blobHost + '/0-public/js/checkMedia.js"><\/script>')
-			document.write('<script id="e_theme_macarons"  src="<%= BASE_URL %>theme/e_theme_macarons.js"><\/script>')
-		</script>
-	</head>
-	<body>
-		<noscript>
-			<strong>IES5</strong>
-		</noscript>
-		<div id="app"></div>
-	</body>
-</html>
+		}
+		// 判断是否为国际站
+		let isGlobalSite = window.location.origin.includes('teammodel.net')
+		let blobHost = isGlobalSite ? 'https://teammodel.blob.core.windows.net' : 'https://teammodelos.blob.core.chinacloudapi.cn'
+		document.write('<script id="vue"  src="' + blobHost + '/0-public/js/vue.js"><\/script>')
+		document.write('<script id="iview"  src="' + blobHost + '/0-public/js/iview.min.js"><\/script>')
+		document.write('<script id="fabric"  src="' + blobHost + '/0-public/js/fabric.min.js"><\/script>')
+		document.write('<script id="vueRouter"  src="' + blobHost + '/0-public/js/vue-router.min.js"><\/script>')
+		document.write('<script id="echarts"  src="' + blobHost + '/0-public/js/echarts.js"><\/script>')
+		document.write('<script id="jspdf"  src="' + blobHost + '/0-public/js/jspdf.umd.min.js"><\/script>')
+		document.write('<script id="mathjax"  src="' + blobHost + '/0-public/js/tex-mml-svg.js"><\/script>')
+		document.write('<script id="mediaInfo"  src="' + blobHost + '/0-public/js/checkMedia.js"><\/script>')
+		document.write('<script id="e_theme_macarons"  src="<%= BASE_URL %>theme/e_theme_macarons.js"><\/script>')
+	</script>
+</head>
+
+<body>
+	<noscript>
+		<strong>IES5</strong>
+	</noscript>
+	<div id="app"></div>
+</body>
+
+</html>

Diff do ficheiro suprimidas por serem muito extensas
+ 6133 - 0
TEAMModelOS/ClientApp/public/lang/en-US.js


Diff do ficheiro suprimidas por serem muito extensas
+ 6134 - 0
TEAMModelOS/ClientApp/public/lang/zh-CN.js


Diff do ficheiro suprimidas por serem muito extensas
+ 6136 - 0
TEAMModelOS/ClientApp/public/lang/zh-TW.js


+ 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)
     }, 
 	// 设置学校的课例类别

+ 8 - 0
TEAMModelOS/ClientApp/src/api/studentWeb.js

@@ -331,4 +331,12 @@ export default {
     setNickName: function(data) {
         return post("/grouplist/upsert-group-member", data)
     },
+    // 获取评测成绩
+    getExamAch: function(data) {
+        return post("/student/stu-score", data)
+    },
+    // 获取作业成绩
+    getHwAch: function(data) {
+        return post("/student/stu-hw-score", data)
+    },
 }

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


+ 38 - 0
TEAMModelOS/ClientApp/src/assets/student-web/component_styles/paper-test.css

@@ -743,4 +743,42 @@
 }
 @media screen and (max-width: 500px) {
     
+}
+
+.lesson-test-pop .analysis .item-explain .item-explain-details-repair{
+  vertical-align: top;
+  display: inline-block;
+  width: calc(100% - 130px);
+}
+.lesson-test-pop .analysis .item-explain .item-explain-details-repair .repair-link-wrap-item-box{
+  display: flex;
+  position: relative;
+  /* // background-color: #e3e3e3; */
+  border-radius: 5px;
+  padding: 10px 0;
+  font-size: 14px;
+  
+}
+.lesson-test-pop .analysis .item-explain .item-explain-details-repair .repair-link-wrap-item-box:hover{
+    background-color: #ebe9e9;
+}
+
+
+.lesson-test-pop .analysis .item-explain .item-explain-details-repair .file-info{
+    margin-left: 10px;
+    
+}
+.lesson-test-pop .analysis .item-explain .item-explain-details-repair .file-info .file-name{
+    font-weight: bold;
+    margin-bottom: 5px;
+}
+
+.lesson-test-pop .analysis .item-explain .item-explain-details-repair .file-info span{
+    color: #16a3b5;
+    margin-right: 15px;
+    cursor: pointer;
+}
+
+.lesson-test-pop .analysis .item-explain .item-explain-details-repair .repair-link-wrap-item-box .file-icon > img{
+    width: 45px !important;
 }

+ 73 - 64
TEAMModelOS/ClientApp/src/common/BaseLayout.vue

@@ -123,6 +123,7 @@
     </div>
 </template>
 <script>
+import { mapGetters } from 'vuex'
 export default {
     data() {
         return {
@@ -141,8 +142,6 @@ export default {
             },
             isCollapsed: true,
             menuTree: [],
-            schoolMenu: [],
-            teacherMenu: [],
             isShowLogo: true,
             hasAnalysisAuth: false
         }
@@ -161,7 +160,71 @@ export default {
         },
         initMenu() {
             //判断当前学校是否购买服务 暂未提供字段
-            this.schoolMenu = !this.$store.state.userInfo.schoolPay ? [
+            // this.schoolMenu = 
+            // this.teacherMenu = 
+        },
+        changeMenuStatus() {
+            this.$refs.side1.toggleCollapse()
+            this.$EventBus.$emit('onCollapseChange', this.isCollapsed)
+        },
+        getSystemLevel() {
+            if (this.$store.state.userInfo.hasSchool) {
+                let prodInfo = this.$store.state.user?.schoolProfile?.svcStatus || {}
+                //如果没有购买服务则为基础版、标准版、专业版
+                let serviceList = this.$GLOBAL.PROD_CODE.filter(item => item.type === 'service')
+                let count = 0
+                serviceList.forEach(item => {
+                    if (prodInfo[item.code]) count++
+                })
+                console.log(this.$store.state)
+                if (count > 0) {
+                    this.systemLevel = this.$t('system.flagship')
+                } else {
+                    let s = this.$store.state?.user?.schoolProfile?.school_base
+                    if (s && (s.size > 100 || s.scale > 0)) {
+                        this.systemLevel = this.$t('system.advanced')
+                    } else {
+                        this.systemLevel = this.$t('system.basic')
+                    }
+                }
+                this.hasAnalysisAuth = this.getAnalysisAuth()
+            }
+        },
+        getAnalysisAuth() {
+            const proInfo = this.$store.state.user?.schoolProfile?.svcStatus || null
+            console.log(proInfo)
+            return proInfo && proInfo.YMPCVCIM
+        }
+    },
+    computed: {
+        ...mapGetters({
+            isHeadmaster: 'isHeadmaster'
+        }),
+        rotateIcon() {
+            return ["collapse-icon", this.isCollapsed ? "rotate-icon" : ""]
+        },
+        schoolStatusInfo() {
+            if (this.isCollapsed) {
+                return ''
+            } else {
+                //学校没有购买服务
+                if (this.$store.state.userInfo.schoolPay) {
+                    return this.$t('system.menu.noSchool1')
+                } else if (!this.$store.state.userInfo.hasSchool) {
+                    // 没有加入学校,但是有申请中的学校 
+                    let schools = this.$store.state.user.userProfile.schools
+                    let inviteSchools = schools.filter(i => i.status === 'invite')
+                    let requestSchools = schools.filter(i => i.status === 'request')
+                    return requestSchools.length ? `${requestSchools[0].name}(${this.$t('settings.status3')})` : (
+                        inviteSchools.length ? `${inviteSchools[0].name}(${this.$t('settings.status2')})` : this
+                            .$t('utils.noJoinSchool'))
+                } else {
+                    return ''
+                }
+            }
+        },
+        schoolMenu() {
+            let data = !this.$store.state.userInfo.schoolPay ? [
                 //数据看板
                 {
                     icon: 'iconfont icon-data-count',
@@ -172,8 +235,6 @@ export default {
                     permission: 'dashboard-read',
                     menuName: 'Dashboard',
                     child: [],
-                    // isShow: this.$store.state.config.srvAdrType != 'product' && this.checkHost()
-                    // isShow: this.checkHost() &&  this.$store.state.config.srvAdr == 'China'
                     isShow: this.checkHost()
                 },
                 // 学校管理
@@ -515,7 +576,10 @@ export default {
                     isShow: this.checkHost()
                 }
             ] : []
-            this.teacherMenu = [
+            return data
+        },
+        teacherMenu() {
+            let data = [
                 // 个人研修
                 {
                     icon: 'iconfont icon-train',
@@ -621,7 +685,7 @@ export default {
                     permission: '',
                     child: [],
                     menuName: 'classmgt',
-                    isShow: this.$store.state.userInfo.hasSchool && this.$store.state.userInfo.isHeadmaster && this.checkHost()
+                    isShow: this.$store.state.userInfo.hasSchool && this.isHeadmaster && this.checkHost()
                 },
                 // 我的课程
                 {
@@ -764,64 +828,9 @@ export default {
                     info: this.$t('tip.task')
                 }
             ]
-        },
-        changeMenuStatus() {
-            this.$refs.side1.toggleCollapse()
-            this.$EventBus.$emit('onCollapseChange', this.isCollapsed)
-        },
-        getSystemLevel() {
-            if (this.$store.state.userInfo.hasSchool) {
-                let prodInfo = this.$store.state.user?.schoolProfile?.svcStatus || {}
-                //如果没有购买服务则为基础版、标准版、专业版
-                let serviceList = this.$GLOBAL.PROD_CODE.filter(item => item.type === 'service')
-                let count = 0
-                serviceList.forEach(item => {
-                    if (prodInfo[item.code]) count++
-                })
-                console.log(this.$store.state)
-                if (count > 0) {
-                    this.systemLevel = this.$t('system.flagship')
-                } else {
-                    let s = this.$store.state?.user?.schoolProfile?.school_base
-                    if (s && (s.size > 100 || s.scale > 0)) {
-                        this.systemLevel = this.$t('system.advanced')
-                    } else {
-                        this.systemLevel = this.$t('system.basic')
-                    }
-                }
-                this.hasAnalysisAuth = this.getAnalysisAuth()
-            }
-        },
-        getAnalysisAuth() {
-            const proInfo = this.$store.state.user?.schoolProfile?.svcStatus || null
-            console.log(proInfo)
-            return proInfo && proInfo.YMPCVCIM
-        }
-    },
-    computed: {
-        rotateIcon() {
-            return ["collapse-icon", this.isCollapsed ? "rotate-icon" : ""]
-        },
-        schoolStatusInfo() {
-            if (this.isCollapsed) {
-                return ''
-            } else {
-                //学校没有购买服务
-                if (this.$store.state.userInfo.schoolPay) {
-                    return this.$t('system.menu.noSchool1')
-                } else if (!this.$store.state.userInfo.hasSchool) {
-                    // 没有加入学校,但是有申请中的学校 
-                    let schools = this.$store.state.user.userProfile.schools
-                    let inviteSchools = schools.filter(i => i.status === 'invite')
-                    let requestSchools = schools.filter(i => i.status === 'request')
-                    return requestSchools.length ? `${requestSchools[0].name}(${this.$t('settings.status3')})` : (
-                        inviteSchools.length ? `${inviteSchools[0].name}(${this.$t('settings.status2')})` : this
-                            .$t('utils.noJoinSchool'))
-                } else {
-                    return ''
-                }
-            }
+            return data
         }
+
     },
     created() {
         let cloudSetting = localStorage.getItem('cloudSetting')

+ 21 - 29
TEAMModelOS/ClientApp/src/common/BaseNotification.vue

@@ -9,7 +9,7 @@
 				<span style="color:red;font-size: 12px;cursor: pointer;" v-if="msgArr.length"
 					@click="clearAllMsgs">{{ $t('utils.clearAllMsgs') }}</span>
 			</div>
-			<div class="notice-wrap">
+			<div class="notice-wrap" v-if="isOpen">
 				<div v-if="!msgArr.length" class="notice-empty">
 					<img src="@/assets/image/none.png" width="300px">
 					{{ $t('notice.noData') }}
@@ -41,32 +41,6 @@
 			return {
 				isOpen:false,
 				msgArr:[],
-				// msgArr: [
-				// 	{
-				// 		label:'request_school',
-				// 		body:{
-				// 			tmdname:'张三',
-				// 			tmdid:'1595321354',
-				// 			schoolname:'青城山学校'
-				// 		}
-				// 	},
-				// 	{
-				// 		label:'invite_school',
-				// 		body:{
-				// 			tmdname:'张三',
-				// 			tmdid:'1595321354',
-				// 			schoolname:'青城山学校'
-				// 		}
-				// 	},
-				// 	{
-				// 		label:'coedit_syllabus',
-				// 		body:{
-				// 			tmdname:'张三',
-				// 			tmdid:'1595321354',
-				// 			schoolname:'青城山学校'
-				// 		}
-				// 	}
-				// ],
 				msgTypes: {
 					'request_school': vm.$t('notice.type1'), // 管理员收到他人申请加入的通知
 					'invite_school': vm.$t('notice.type2'), // 你收到学校邀请你的通知
@@ -112,7 +86,7 @@
 			},
 			/* 获取消息通知类型 */
 			getMsgType(msg) {
-				return this.msgTypes[msg.label]
+				return `${this.msgTypes[msg.label]}`
 			},
 			/* 获取消息通知时间 */
 			getMsgTime(msg) {
@@ -121,7 +95,6 @@
 			/* 获取消息通知内容 */
 			getMsgContent(msg) {
 				let body = msg.body
-				console.log(body)
 				switch (msg.label) {
 					case 'request_school':
 						return `${body.tmdname}(${body.tmdid}) ${this.$t('notice.tip1')} ${body.schoolname}`
@@ -345,6 +318,25 @@
 						})
 					}
 				}
+			},
+			'$store.state.studentWeb.lang'(language) {
+				this.msgTypes = {
+					'request_school': this.$t('notice.type1'), // 管理员收到他人申请加入的通知
+					'invite_school': this.$t('notice.type2'), // 你收到学校邀请你的通知
+					'remove_school': this.$t('notice.type3'), // 学校将你移除的通知
+					'request-join_school': this.$t('notice.type1'), // 学校同意你的加入请求
+					'invite-join_school': this.$t('notice.type2'), // 某人已同意你对他的邀请
+					'coedit_syllabus': this.$t('notice.type4'), // 邀请课纲共编的通知
+					'share_syllabus': this.$t('notice.type4'), // 个人课纲分享的通知
+					'transfer-admin_school': this.$t('notice.type5'), // 管理员转移的通知
+					'scoring-arb_school': this.$t('notice.type6'), // 仲裁卷阅卷任务分配通知
+					'scoring-err_school': this.$t('notice.type6'), // 异常卷阅卷任务分配通知
+					'scoring-mark_school': this.$t('notice.type6'), // 普通阅卷任务分配通知
+					'scan-join_groupList': this.$t('notice.type7'), // 普通阅卷任务分配通知
+					'scan-join_school': this.$t('notice.type7'), // 普通阅卷任务分配通知
+					'submitanswer_homework': this.$t('notice.type8'), // 作业提交通知
+					'expire_lessonRecord': this.$t('notice.type9'), // 课例过期通知
+				}
 			}
 		}
 	}

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

+ 0 - 1
TEAMModelOS/ClientApp/src/components/mark/MarkTools.vue

@@ -43,7 +43,6 @@ export default {
     },
     created() {
         for (let i = 0; i < 9; i++) {
-            // this.imgs.push(require('@/assets/mark/' + i + '.svg'))
             this.imgs.push(require('@/assets/mark/' + i + '.png'))
         }
     },

+ 7 - 3
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/LessonTestReportCharts/QuCount.vue

@@ -2,9 +2,13 @@
     <div>
         <div class="qu-score-count" id="class-score-count"></div>
         <p class="legend-info">
-            <span>{{ $t('studentWeb.exam.chart.legendSimple.legend1') }}</span>
-            <span>{{ $t('studentWeb.exam.chart.legendSimple.legend2') }}√</span>
-            <span>{{ $t('studentWeb.exam.chart.legendSimple.legend3') }}×</span>
+            <!-- <span>{{ $t('studentWeb.exam.chart.legendSimple.legend1') }}</span> -->
+            <span>{{ $t('studentWeb.exam.chart.legendSimple.legend2') }}
+                <span style="color: #00AD6C;">√</span>
+            </span>
+            <span>{{ $t('studentWeb.exam.chart.legendSimple.legend3') }}
+                <span style="color: #FF5508;">×</span>
+            </span>
         </p>
     </div>
 </template>

+ 46 - 72
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperTest.vue

@@ -275,57 +275,20 @@
                         </div>
                         <div class="item-explain">
                             <span class="explain-title">【{{ $t("studentWeb.exam.report.repairSource") }}】</span>
-                            <div class="item-explain-details">
+                            <div class="item-explain-details-repair">
                                 <div v-if="showExam[queNo].repair.length != 0">
-                                    <Collapse style="width:85%" accordion @on-change="getSource(showExam[queNo].repair)">
-                                        <!-- 网络资源 -->
-                                        <Panel name="1">
-                                            {{$t("studentWeb.exam.report.linkSource")}}
-                                            <p slot="content">
-                                                <List border size="small">
-                                                    <ListItem v-for="(item, normalIndex) in repairSource.normal" :key="normalIndex">
-                                                        <span style="margin-right: 10px;" v-show="item.blobUrl">
-                                                            <Icon color="#0066FF" custom="iconfont icon-share_link" size="20" />
-                                                        </span><a :href="item.blobUrl" target="_blank">{{item.name}}</a>
-                                                    </ListItem>
-                                                    <ListItem v-show="!repairSource.normal.length">
-                                                        <span>{{$t("studentWeb.exam.report.noSource")}}</span>
-                                                    </ListItem>
-                                                </List>
-                                            </p>
-                                        </Panel>
-                                        <!-- 文件资源 -->
-                                        <Panel name="2">
-                                            {{$t("studentWeb.exam.report.fileSource")}}
-                                            <p slot="content">
-                                                <List border size="small">
-                                                    <ListItem v-for="(item, fileIndex) in repairSource.file" :key="fileIndex">
-                                                        <div style="width:100%">
-                                                            <span style="margin-right:10px;">
-                                                                <Icon v-if="item.fileType == 'zip'" custom="iconfont icon-zip" size="20" />
-                                                                <!-- <Icon v-else-if="item.fileType == 'zip'" color="#8199AF" custom="iconfont icon-zip" size="20" /> -->
-                                                                <Icon v-else-if="item.fileType == 'pdf'" color="#FF6464" custom="iconfont icon-pdf" size="20" />
-                                                                <Icon v-else-if="item.fileType == 'ppt'|| item.fileType == 'pptx'" color="#FF8976" custom="iconfont icon-ppt" size="20" />
-                                                                <Icon v-else-if="item.fileType == 'mp3'" color="#FF5562" custom="iconfont icon-mp3" size="20" />
-                                                                <Icon v-else-if="item.fileType == 'mp4'" color="#8E4C9E" custom="iconfont icon-video1" size="20" />
-                                                                <!-- <Icon v-else-if="item.fileType == 'zip'" custom="iconfont icon-video1" size="20" /> -->
-                                                                <Icon v-else-if="item.fileType == 'doc'||item.fileType =='docx'" color="#6CCBFF" custom="iconfont icon-word" size="20" />
-                                                                <Icon v-else-if="item.fileType == 'csv'||item.fileType =='xlsx'||item.fileType =='xls'" color="#25C273" custom="iconfont icon-xlsx" size="20" />
-                                                                <Icon v-else-if="item.fileType == 'jpg'||item.fileType =='png'||item.fileType =='jpeg'" color="#30D1CA" custom="iconfont icon-jpg" size="20" />
-                                                                <Icon v-else custom="iconfont icon-V" size="20" />
-                                                            </span><a @click="getItemData(item)">{{item.name}}</a>
-                                                            <span style="display:block;float:right;cursor:pointer">
-                                                                <Icon type="md-download" size="18" @click="downloadFile(item)" />
-                                                            </span>
-                                                        </div>
-                                                    </ListItem>
-                                                    <ListItem v-show="!repairSource.file.length">
-                                                        <span>{{$t("studentWeb.exam.report.noSource")}}</span>
-                                                    </ListItem>
-                                                </List>
-                                            </p>
-                                        </Panel>
-                                    </Collapse>
+                                    <div v-for="(repairSource, normalIndex) in showExam[queNo].repair" :key="normalIndex" class="repair-link-wrap-item-box">
+                                        <div class="file-icon">
+                                            <img :src="$tools.getFileThum(repairSource.type, repairSource.name)"/>
+                                        </div>
+                                        <div class="file-info">
+                                            <p class="file-name">{{ repairSource.name }}</p>
+                                            <div>
+                                                <span @click.stop="onPreview(repairSource)" v-if="repairSource.type !== 'other'">{{ $t('ability.review.preview')}}</span>
+                                                <span @click.stop="onDownload(repairSource)" v-if="repairSource.type !== 'link'">{{ $t('ability.review.download')}}</span>
+                                            </div>
+                                        </div>
+                                    </div>
                                 </div>
                                 <div v-else-if="!showExam[queNo].repair.length">
                                     {{ $t('studentWeb.exam.report.noSource') }}
@@ -400,28 +363,14 @@
                 <span>{{$t("studentWeb.exam.report.fileView")}}</span>
             </p>
             <div class="file-box" v-if="previewStatus">
-                <video v-if="previewFile.file == 'video'"
-                       id="previewVideo"
-                       :src="previewFile.blobUrl"
-                       width="870"
-                       controls="controls"
-                       style="max-height: 800px;">
-                    {{$t('teachContent.tips8')}}
-                </video>
-                <audio v-else-if="previewFile.file == 'audio'" controls>
-                    <source :src="previewFile.blobUrl">
-                </audio>
-                <img v-else-if="previewFile.fileType == 'jpg'||previewFile.fileType =='png'||previewFile.fileType =='jpeg' "
-                     :src="previewFile.blobUrl"
-                     style="border-radius: 5px;max-height: 800px;max-width:870px;" />
-                <pdf ref="pdf"
-                     v-else-if="previewFile.fileType == 'pdf'"
-                     v-for="(page,index) in numPages"
-                     :key="index"
-                     :src="previewFile.blobUrl"
-                     :page="page"></pdf>
-                <!--<span v-else-if="previewFile.fileType == 'doc'||previewFile.fileType == 'csv'||previewFile.fileType == 'pptx' ">{{'https://view.officeapps.live.com/op/view.aspx?src=' + previewFile.blobUrl}}</span>-->
-                <iframe v-else-if="previewFile.fileType == 'doc'||previewFile.fileType == 'csv'||previewFile.fileType == 'pptx'||previewFile.fileType == 'xls' " :src="'https://view.officeapps.live.com/op/view.aspx?src=' + previewFile.blobUrl" width='870' height='700' frameborder='1'></iframe>
+                <video v-if="previewFile.type == 'video'" id="previewVideo" :src="previewFile.blobUrl" width="870"
+					controls="controls" style="max-height: 800px;">
+					{{$t('teachContent.tips8')}}
+				</video>
+                <audio v-else-if="previewFile.type == 'audio'" controls>
+					<source :src="previewFile.blobUrl">
+					{{$t('teachContent.notAudio')}}
+				</audio>
                 <span v-else>{{$t("studentWeb.exam.report.noReview")}}</span>
             </div>
         </Modal>
@@ -1036,6 +985,26 @@
                 this.showExam[index].star = type
                 this.$forceUpdate()
             },
+            /* 预览 */
+            async onPreview(item){
+                let url = item.blobUrl
+                if (this.getSuffix(item.name) === 'pdf') {
+                    window.open('/web/viewer.html?file=' + encodeURIComponent(url));
+                } else if(item.type === 'doc') {
+                    window.open('https://view.officeapps.live.com/op/view.aspx?src=' + escape(url));
+                } else if(item.type === 'image') {
+                    this.$hevueImgPreview(url)
+                } else if(item.type === 'link') {
+					window.open(/^(http:|https:)/i.test(url) ? url : "http://" + url)
+                } else {
+                    this.previewFile = item
+                    this.previewStatus = true
+                }
+            },
+            /* 下载 */
+            async onDownload(item) {
+                this.$tools.doDownloadByUrl(item.blobUrl, item.name)
+            },
 
             /* ======未调用====== */
             getJudge() {
@@ -1183,6 +1152,11 @@
                     return false
                 }
             },
+            getSuffix() {
+                return name => {
+                    return name.substr(name.lastIndexOf(".") + 1)
+                }
+            },
         },
         // 导航守卫监听
         beforeRouteLeave(to, from, next) {

+ 2 - 1
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperView.vue

@@ -132,7 +132,8 @@
                         id: paper.id,
                         studentId: this.userInfo.sub,
                         code: codes,
-                        scode: paper.scode
+                        scode: paper.scode,
+                        cIds: paper.classIds
                     }
                     this.paperCtn = paper.scope
                     let isTest = 0

+ 0 - 0
TEAMModelOS/ClientApp/src/components/student-web/achievement/MyAchievement.less


Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff