瀏覽代碼

Merge branch 'develop' of http://52.130.252.100:10000/TEAMMODEL/TEAMModelOS into develop

zhouj1203@hotmail.com 2 年之前
父節點
當前提交
9a08e0ad73
共有 78 個文件被更改,包括 2566 次插入507 次删除
  1. 23 28
      TEAMModelBI/ClientApp/src/api/index.js
  2. 93 0
      TEAMModelBI/ClientApp/src/components/echarts/commonApiBar.vue
  3. 11 6
      TEAMModelBI/ClientApp/src/view/areamanage/statistics.vue
  4. 29 3
      TEAMModelBI/ClientApp/src/view/participation/index.vue
  5. 21 10
      TEAMModelBI/ClientApp/src/view/schoolServe/analyseSchool.vue
  6. 1 1
      TEAMModelBI/ClientApp/src/view/schoolmanage/schoolAnalyse.vue
  7. 447 6
      TEAMModelBI/ClientApp/src/view/systemConfig/apimanage.vue
  8. 2 2
      TEAMModelBI/ClientApp/src/view/systemConfig/index.vue
  9. 4 4
      TEAMModelBI/ClientApp/src/view/systemConfig/setAdmin.vue
  10. 1 2
      TEAMModelBI/Controllers/BIHome/HomeStatisController.cs
  11. 1 1
      TEAMModelBI/Controllers/BIAbility/AbilityMgmtController.cs
  12. 1 1
      TEAMModelBI/Controllers/BIAbility/AbilityTaskMgmtController.cs
  13. 1 1
      TEAMModelBI/Controllers/BISchool/AreaRelevantController.cs
  14. 37 21
      TEAMModelBI/Controllers/BISchool/BatchAreaController.cs
  15. 16 16
      TEAMModelBI/Controllers/BISchool/BatchSchoolController.cs
  16. 26 6
      TEAMModelBI/Controllers/BISchool/SchoolController.cs
  17. 0 100
      TEAMModelBI/Controllers/BITest/TestController.cs
  18. 4 4
      TEAMModelBI/Controllers/Census/ActivitySticsController.cs
  19. 3 3
      TEAMModelBI/Controllers/Census/LessonSticsController.cs
  20. 4 1
      TEAMModelBI/TEAMModelBI.csproj
  21. 1 1
      TEAMModelOS.FunctionV4/Program.cs
  22. 3 3
      TEAMModelOS.FunctionV4/TEAMModelOS.FunctionV4.csproj
  23. 1 1
      TEAMModelOS.FunctionV4/TimeTrigger/IESTimerTrigger.cs
  24. 8 0
      TEAMModelOS.SDK/Context/Attributes/Filter/ApiTokenAttribute.cs
  25. 59 0
      TEAMModelOS.SDK/DI/CoreAPI/CoreAPIHttpService.cs
  26. 2 2
      TEAMModelOS.SDK/DI/HttpTrigger/WebHookHttpTrigger.cs
  27. 4 0
      TEAMModelOS.SDK/Helper/Common/ReflectorExtensions/ReflectorExtensions.cs
  28. 9 1
      TEAMModelOS.SDK/Models/Service/LessonService.cs
  29. 3 0
      TEAMModelOS.SDK/Models/Service/LoginService.cs
  30. 19 2
      TEAMModelOS.SDK/Models/Table/OpenApi.cs
  31. 110 102
      TEAMModelOS/ClientApp/public/lang/en-US.js
  32. 10 2
      TEAMModelOS/ClientApp/public/lang/zh-CN.js
  33. 11 3
      TEAMModelOS/ClientApp/public/lang/zh-TW.js
  34. 1 1
      TEAMModelOS/ClientApp/src/api/http.js
  35. 8 6
      TEAMModelOS/ClientApp/src/api/index.js
  36. 5 2
      TEAMModelOS/ClientApp/src/api/service.js
  37. 922 0
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperTest.less
  38. 6 2
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperTest.vue
  39. 40 0
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperView-style.less
  40. 149 0
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperView.less
  41. 10 2
      TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperView.vue
  42. 14 7
      TEAMModelOS/ClientApp/src/components/student-web/SettingView/Setting.vue
  43. 24 4
      TEAMModelOS/ClientApp/src/view/auth/Serial.vue
  44. 38 5
      TEAMModelOS/ClientApp/src/view/classrecord/ClassRecord.vue
  45. 48 16
      TEAMModelOS/ClientApp/src/view/forgotPw/Index.vue
  46. 10 2
      TEAMModelOS/ClientApp/src/view/jyzx/HourDetail.vue
  47. 4 2
      TEAMModelOS/ClientApp/src/view/jyzx/newHomePage.vue
  48. 2 2
      TEAMModelOS/ClientApp/src/view/mgtPlatform/MgtPlatform.vue
  49. 1 0
      TEAMModelOS/ClientApp/src/view/mycourse/record/Record.less
  50. 48 18
      TEAMModelOS/ClientApp/src/view/regist/Index.vue
  51. 20 0
      TEAMModelOS/ClientApp/src/view/research-center/ResearchMgt.vue
  52. 5 4
      TEAMModelOS/ClientApp/src/view/settings/OpenMgmt2.vue
  53. 1 1
      TEAMModelOS/ClientApp/src/view/teachcontent/index.vue
  54. 13 7
      TEAMModelOS/ClientApp/src/view/user/BandPhone.vue
  55. 12 12
      TEAMModelOS/ClientApp/src/view/user/UserCenter.vue
  56. 3 3
      TEAMModelOS/Controllers/OpenApi/Business/BizCourseController.cs
  57. 2 2
      TEAMModelOS/Controllers/OpenApi/Business/BizGroupListController.cs
  58. 1 1
      TEAMModelOS/Controllers/OpenApi/Business/BizKnowledgeController.cs
  59. 2 2
      TEAMModelOS/Controllers/OpenApi/Business/BizLessonRecordController.cs
  60. 2 2
      TEAMModelOS/Controllers/OpenApi/Business/BizRoomController.cs
  61. 2 2
      TEAMModelOS/Controllers/OpenApi/Business/BizSchoolController.cs
  62. 2 2
      TEAMModelOS/Controllers/OpenApi/Business/BizSyllabusController.cs
  63. 3 3
      TEAMModelOS/Controllers/OpenApi/Business/BizTeacherController.cs
  64. 18 16
      TEAMModelOS/Controllers/OpenApi/OpenApiService.cs
  65. 5 5
      TEAMModelOS/Controllers/OpenApi/OpenSchool/ScCourseController.cs
  66. 4 4
      TEAMModelOS/Controllers/OpenApi/OpenSchool/ScExamController.cs
  67. 8 8
      TEAMModelOS/Controllers/OpenApi/OpenSchool/ScGroupListController.cs
  68. 1 1
      TEAMModelOS/Controllers/OpenApi/OpenSchool/ScKnowledgeController.cs
  69. 3 3
      TEAMModelOS/Controllers/OpenApi/OpenSchool/ScRoomController.cs
  70. 1 1
      TEAMModelOS/Controllers/OpenApi/OpenSchool/ScSchoolController.cs
  71. 2 2
      TEAMModelOS/Controllers/OpenApi/OpenSchool/ScSyllabusController.cs
  72. 3 3
      TEAMModelOS/Controllers/OpenApi/OpenSchool/ScTeacherController.cs
  73. 130 3
      TEAMModelOS/Controllers/System/CoreController.cs
  74. 9 1
      TEAMModelOS/Controllers/Third/Sc/ScController.cs
  75. 14 12
      TEAMModelOS/Controllers/Third/Sc/ScDataPushController.cs
  76. 1 1
      TEAMModelOS/Controllers/XTest/FixDataController.cs
  77. 1 1
      TEAMModelOS/Controllers/XTest/TestController.cs
  78. 3 3
      TEAMModelOS/TEAMModelOS.csproj

+ 23 - 28
TEAMModelBI/ClientApp/src/api/index.js

@@ -4,22 +4,22 @@ export default {
     Dinglogin(data) {
         return post('/common/login/get-ddscancode', data)
     },
-    //发送短信验证码
-    Phonepin(data) {
-        return post('https://api2.teammodel.net/service/sandsms/pin', data)
-    },
-    //验证手机号和验证
-    verifyCode(data) {
-        return post('https://api2.teammodel.net/oauth2/login', data)
-    },
-    //手机验证码都通过后,进行绑定操作
-    bindUserid(data) {
-        return post('/common/login/set-bind ', data)
-    },
-    //正常醍摩豆账号或者手机号登录
-    loginUser(data) {
-        return post('https://api2.teammodel.cn/oauth2/login', data)
-    },
+    //发送短信验证码    暂时未使用
+    // Phonepin(data) {
+    //     return post('https://api2.teammodel.net/service/sandsms/pin', data)
+    // },
+    //验证手机号和验证 暂时未使用
+    // verifyCode(data) {
+    //     return post('https://api2.teammodel.net/oauth2/login', data)
+    // },
+    //手机验证码都通过后,进行绑定操作 暂时未使用
+    // bindUserid(data) {
+    //     return post('/common/login/set-bind ', data)
+    // },
+    //正常醍摩豆账号或者手机号登录 暂时未使用
+    // loginUser(data) {
+    //     return post('https://api2.teammodel.cn/oauth2/login', data)  
+    // },
     //获取组织架构
     getorganization(data) {
         return post('/dd/get-deptlist', data)
@@ -284,9 +284,9 @@ export default {
         return post('/biservers/get-coreinfo', data)
     },
     //移除管理员
-    removeAdmin(data) {
-        return post('/tabledd/set-backend', data)
-    },
+    // removeAdmin(data) {
+    //     return post('/tabledd/set-backend', data)
+    // },
     //切换站点
     cutSitesInfo(data) {
         return post('/syscfg/cut-site', data)
@@ -299,6 +299,10 @@ export default {
     setRolesandPower(data) {
         return post('/tabledd/set-rolesper', data)
     },
+    //API管理内 当天的API访问情况
+    getNowapi(data) {
+        return post('/analyse/get-dayapi', data)
+    },
 
 
 
@@ -409,7 +413,6 @@ export default {
     },
 
 
-    //第三方企业合作平台相关API
     //获取地址location
     getlocation(key, location) {
         return fetch('https://restapi.amap.com/v3/ip?key=' + key + '&ip=' + location)
@@ -418,12 +421,4 @@ export default {
     getWeather(key, location) {
         return fetch('https://devapi.qweather.com/v7/weather/now?location=' + location + '&key=' + key)
     },
-    //登录
-    thirdlogins(data) {
-        return post('/common/login/get-bizuserlogin', data)
-    },
-    //注册
-    registerThirdparty(data) {
-        return post('/common/login/set-ropen', data)
-    }
 }

+ 93 - 0
TEAMModelBI/ClientApp/src/components/echarts/commonApiBar.vue

@@ -0,0 +1,93 @@
+<!--基础折线图-->
+<template>
+  <div ref="myEcharts" :style="{ height, width }" class="boxs"></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%',
+    },
+    barData: {
+      type: Object,
+      default: () => { },
+    },
+    title: {
+      type: String,
+      default: '',
+    },
+  },
+  setup (props, context) {
+    const myEcharts = ref(null)
+    let { proxy } = getCurrentInstance()
+    const chart = new InitChart(props, myEcharts, context)
+    onMounted(() => {
+      chart.init(props.barData, proxy, context)
+    })
+
+    watch(
+      props,
+      (nweProps) => {
+        nextTick(() => {
+          nweProps ? chart.init(props.barData, proxy, context) : ''
+        })
+      },
+      { immediate: true, deep: true }
+    )
+    return {
+      myEcharts,
+    }
+  },
+}
+class InitChart {
+  constructor(props, myEcharts, context) {
+    this.props = props
+    this.myEcharts = myEcharts
+    this.state = {
+      chart: null,
+    }
+  }
+  init (datas, proxy, context) {
+    console.log(datas, '柱状图的调用123123123')
+    this.state.chart && this.destory()
+    this.state.chart = echarts.init(this.myEcharts.value)
+    this.state.chart.setOption({
+      color: datas.color ? datas.color : '',
+      title: datas.title ? datas.title : '',
+      tooltip: datas.tooltip ? datas.tooltip : '',
+      legend: datas.legend ? datas.legend : '',
+      grid: datas.grid ? datas.grid : '',
+      xAxis: datas.xAxis ? datas.xAxis : '',
+      yAxis: datas.yAxis ? datas.yAxis : '',
+      dataZoom: datas.dataZoom ? datas.dataZoom : '',
+      series: datas.series ? datas.series : '',
+    })
+    window.addEventListener('resize', () => {
+      this.state.chart.resize()
+    })
+    this.state.chart.on('click', (params) => {
+      //   this.getAlertList(parseFloat(this.risk_value), params.name)
+      context.emit('clicktime', params)
+      console.log(params, '点击了 点击了')
+    })
+  }
+
+  destory () {
+    this.state.chart.dispose()
+    window.removeEventListener('resize', () => {
+      console.log('事件移除')
+    })
+  }
+}
+</script>
+<style lang="less">
+</style>
+

+ 11 - 6
TEAMModelBI/ClientApp/src/view/areamanage/statistics.vue

@@ -985,7 +985,7 @@ export default {
       lastweek: {
         color: ['#3fb1e3', '#6be6c1', '#626c91', '#a0a7e6', '#c4ebad', '#96dee8'],
         legend: {
-          right: '4%',
+          right: '0%',
           orient: 'vertical',
           itemWidth: 8,
           itemHeight: 8, // 修改icon图形大小
@@ -999,7 +999,7 @@ export default {
             name: '学校数据占比',
             type: 'pie',
             radius: '75%',
-            center: ['40%', '50%'],
+            center: ['30%', '50%'],
             itemStyle: {
               borderRadius: 5,
             },
@@ -1023,7 +1023,7 @@ export default {
       monthsContrast: {
         color: ['#3fb1e3', '#6be6c1', '#626c91', '#a0a7e6', '#c4ebad', '#96dee8'],
         legend: {
-          right: '4%',
+          right: '0%',
           orient: 'vertical',
           itemWidth: 8,
           itemHeight: 8, // 修改icon图形大小
@@ -1037,7 +1037,7 @@ export default {
             name: '学校数据占比',
             type: 'pie',
             radius: '75%',
-            center: ['40%', '50%'],
+            center: ['30%', '50%'],
             itemStyle: {
               borderRadius: 5,
             },
@@ -2215,14 +2215,16 @@ export default {
 .area-item-name {
   font-size: 16px;
   font-weight: 600;
-  margin-bottom: 15px;
+  margin-bottom: 10px;
 }
 .area-item-school-title,
 .area-item-school-content {
   font-size: 14px;
   color: #636e72;
 }
-
+.area-item-school {
+  margin-bottom: 5px;
+}
 .center-resource-left-leftEcharts,
 .center-resource-left-rightbox {
   width: 50%;
@@ -2235,6 +2237,9 @@ export default {
   height: 135px;
   position: relative;
 }
+.week-echart {
+  border-bottom: 1px solid #ccc;
+}
 .echarts-title {
   position: absolute;
   left: 0.5%;

+ 29 - 3
TEAMModelBI/ClientApp/src/view/participation/index.vue

@@ -186,7 +186,9 @@
               </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 v-for="item in areaSelect.data" :key="item.name" :label="item.name" :value="item.id" :disabled="item.cutArea">
+                    <div class="areaname">{{item.name}}</div>
+                    <div class="stepicon" v-show="item.cutArea"><span>已同步省平台</span></div>
                   </el-option>
                 </el-select>
               </el-form-item>
@@ -521,7 +523,7 @@ export default {
       proxy.$api.updateSchoolinfo(updateForm).then((res) => {
         console.log(res, '修改学校的返回')
         res.state === 200
-          ? (ElMessage.success(proxy.$t(`commonMsg.schoolUpdateSuccess`), schoolJoinarea(), schoolClose()), updateSuccess())
+          ? (ElMessage.success(proxy.$t(`commonMsg.schoolUpdateSuccess`), schoolClose()), updateSuccess())
           : ElMessage.error(proxy.$t(`commonMsg.schoolUpdateError`))
       })
     }
@@ -938,6 +940,21 @@ export default {
   top: -1px;
   left: -4px;
 }
+.stepicon {
+  float: right;
+  height: 22px;
+  line-height: 20px;
+  margin: 2px 4px 2px 0;
+  padding: 0 8px;
+  border: 1px solid #e8eaec;
+  border-radius: 3px;
+  background: #fffbe6;
+  font-size: 12px;
+  vertical-align: middle;
+  opacity: 1;
+  overflow: hidden;
+  border-color: #ffe58f;
+}
 </style>
 <style>
 .schoolboxtad .el-cascader {
@@ -1026,7 +1043,16 @@ export default {
   width: 100%;
   height: 125px;
 }
-
+.areaname,
+.stepicon {
+  display: inline-block;
+  vertical-align: top;
+}
+.stepicon {
+  float: right;
+  border: 1px solid #ccc;
+  font-size: 12px;
+}
 /* .school-form-grading .el-radio__input.is-checked .el-radio__inner::after {
   content: "";
   width: 10px;

+ 21 - 10
TEAMModelBI/ClientApp/src/view/schoolServe/analyseSchool.vue

@@ -1042,30 +1042,41 @@ export default {
           basicsData.value.totals.month[3].num = res.termACTCnt
 
           basicsData.value.totals.month[0].compare = ((res.weekLesCnt - res.lastWeekLesCnt) / res.lastWeekLesCnt).toFixed(2) * 100
-          res.lastWeekLesCnt === 0 ? basicsData.value.totals.month[0].compare = 100 : ''
+          res.lastWeekLesCnt === 0 && res.weekLesCnt !== 0 ? basicsData.value.totals.month[0].compare = 100 : res.lastWeekLesCnt === 0 && res.weekLesCnt === 0 ? basicsData.value.totals.month[0].compare = 0 : ''
 
           basicsData.value.totals.month[1].compare = ((res.weekACTCnt - res.lastWeekACTCnt) / res.lastWeekACTCnt).toFixed(2) * 100
-          res.lastWeekACTCnt === 0 ? basicsData.value.totals.month[1].compare = 100 : ''
+          res.lastWeekACTCnt === 0 && res.weekACTCnt !== 0 ? basicsData.value.totals.month[1].compare = 100 : res.lastWeekACTCnt === 0 && res.weekACTCnt === 0 ? basicsData.value.totals.month[1].compare = 0 : ''
 
           basicsData.value.totals.month[2].compare = ((res.termLesCnt - res.lastTermLesCnt) / res.lastTermLesCnt).toFixed(2) * 100
-          res.lastTermLesCnt === 0 ? basicsData.value.totals.month[2].compare = 100 : ''
+          res.lastTermLesCnt === 0 && res.termLesCnt !== 0 ? basicsData.value.totals.month[2].compare = 100 : res.lastTermLesCnt === 0 && res.termLesCnt === 0 ? basicsData.value.totals.month[2].compare = 0 : ''
 
           basicsData.value.totals.month[3].compare = ((res.termACTCnt - res.lastTermActCnt) / res.lastTermActCnt).toFixed(2) * 100
-          res.lastTermActCnt === 0 ? basicsData.value.totals.month[3].compare = 100 : ''
+          res.lastTermActCnt === 0 && res.termACTCnt !== 0 ? basicsData.value.totals.month[3].compare = 100 : res.lastTermActCnt === 0 && res.termACTCnt === 0 ? basicsData.value.totals.month[3].compare = 0 : ''
           basicsData.value.termTatal = parseInt(res.termLesCnt) + parseInt(res.termACTCnt)
           //累计数据处理
           basicsData.value.totals.total[0].num = res.lesCnt
-          basicsData.value.totals.total[1].num = 0
           basicsData.value.totals.total[2].num = res.actCnt
+          basicsData.value.totals.total[1].num = parseInt(res.lesCnt) + parseInt(res.actCnt)
           basicsData.value.totals.total[3].num = res.schoolInfo.service.length
 
           basicsData.value.totals.total[0].compare = ((res.lesCnt - res.lastYearLesCnt) / res.lastYearLesCnt).toFixed(2) * 100
-          res.lastYearLesCnt === 0 ? basicsData.value.totals.total[0].compare = 100 : ''
+          res.lastYearLesCnt === 0 && res.lesCnt !== 0 ? basicsData.value.totals.total[0].compare = 100 : res.lastYearLesCnt === 0 && res.lesCnt === 0 ? basicsData.value.totals.total[0].compare = 0 : ''
+          //累计资源和去年做比较
           basicsData.value.totals.total[1].compare = 0
+          let thisYear = parseInt(res.lesCnt) + parseInt(res.actCnt)
+          let lastYear = parseInt(res.lastYearLesCnt) + parseInt(res.lastYearACTCnt)
+          basicsData.value.totals.total[1].compare = ((thisYear - lastYear) / lastYear).toFixed(2) * 100
+          lastYear === 0 && thisYear !== 0 ? basicsData.value.totals.total[1].compare = 100 : lastYear === 0 && thisYear === 0 ? basicsData.value.totals.total[1].compare = 0 : ''
           basicsData.value.totals.total[2].compare = ((res.actCnt - res.lastYearACTCnt) / res.lastYearACTCnt).toFixed(2) * 100
-          res.lastYearLesCnt === 0 ? basicsData.value.totals.total[2].compare = 100 : ''
-          basicsData.value.totals.total[3].compare = 0
-
+          res.lastYearLesCnt === 0 && res.actCnt !== 0 ? basicsData.value.totals.total[2].compare = 100 : res.lastYearLesCnt === 0 && res.actCnt === 0 ? basicsData.value.totals.total[2].compare = 0 : ''
+          //获取去年和今年的时间戳
+          let lastyear = { start: res.buy.lastYearS / 1000, end: res.buy.lastYearE / 1000 }
+          let nowsyear = { start: res.buy.yearS / 1000, end: res.buy.yearE / 1000 }
+          console.log(lastyear, nowsyear, '时间')
+          let lastData = []; let nowData = []
+          res.buy.order.forEach((item) => { item.date >= lastyear.start && item.date <= lastyear.end ? lastData.push(item) : item.date >= nowsyear.start && item.date <= nowsyear.end ? nowData.push(item) : '' })
+          basicsData.value.totals.total[3].compare = ((nowData.length - lastData.length) / lastData.length).toFixed(2) * 100
+          lastData.length === 0 && nowData.length !== 0 ? basicsData.value.totals.total[3].compare = 100 : lastData.length === 0 && nowData.length === 0 ? basicsData.value.totals.total[3].compare = 0 : ''
           basicsData.value.accumulativeTatal = parseInt(res.lesCnt) + parseInt(res.actCnt)
 
           loadingSchool.value.basics = false
@@ -1124,7 +1135,7 @@ export default {
         for (var t = 0; t < res.weekTrend.length; t++) {
           let num = parseInt(t) + 1
           let text = '第' + num + '周'
-          console.log(text)
+          // console.log(text)
           lengedData.push(text)
         }
         console.log(lengedData)

+ 1 - 1
TEAMModelBI/ClientApp/src/view/schoolmanage/schoolAnalyse.vue

@@ -1468,7 +1468,7 @@ export default {
             name: '科目占比',
             type: 'pie',
             radius: '80%',
-            center: ['30%', '50%'],
+            center: ['45%', '50%'],
             itemStyle: {
               borderRadius: 2,
             },

+ 447 - 6
TEAMModelBI/ClientApp/src/view/systemConfig/apimanage.vue

@@ -4,8 +4,20 @@
       <div class="apibox-top-title">
         <p>API访问情况</p>
       </div>
-      <div class="apibox-top-left"></div>
-      <div class="apibox-top-right"></div>
+      <div class="apibox-top-left">
+        <CommonApiBar :barData="countBar" width="30vw" height="26vh" @clicktime="clicktimes"></CommonApiBar>
+      </div>
+      <div class="apibox-top-center">
+        <CommonApiBar :barData="rankBar" width="30vw" height="26vh"></CommonApiBar>
+      </div>
+      <div class="apibox-top-right">
+        <div class="notdatas" v-if="minuteShow">
+          暂无当前时间或选择时间数据
+        </div>
+        <div v-else>
+          <CommonApiBar :barData="minuteBar" width="30vw" height="26vh"></CommonApiBar>
+        </div>
+      </div>
     </div>
     <div class="apibox-list">
       <div class="apibox-list-header">
@@ -28,7 +40,13 @@
 </template>
 <script>
 import { ref, getCurrentInstance } from 'vue'
+import { ElMessage, ElLoading } from 'element-plus'
+import * as echarts from 'echarts'
+import CommonApiBar from '@/components/echarts/commonApiBar.vue'
 export default ({
+  components: {
+    CommonApiBar
+  },
   setup () {
     let { proxy } = getCurrentInstance()
     let selectValue = ref('all')
@@ -59,7 +77,419 @@ export default ({
         address: 'No. 189, Grove St, Los Angeles',
       },
     ])
-    return { selectValue, options, tableData }
+    let timelist = ref([
+      { time: 0, count: 0, },
+      { time: 1, count: 0, },
+      { time: 2, count: 0, },
+      { time: 3, count: 0, },
+      { time: 4, count: 0, },
+      { time: 5, count: 0, },
+      { time: 6, count: 0, },
+      { time: 7, count: 0, },
+      { time: 8, count: 0, },
+      { time: 9, count: 0, },
+      { time: 10, count: 0, },
+      { time: 11, count: 0, },
+      { time: 12, count: 0, },
+      { time: 13, count: 0, },
+      { time: 14, count: 0, },
+      { time: 15, count: 0, },
+      { time: 16, count: 0, },
+      { time: 17, count: 0, },
+      { time: 18, count: 0, },
+      { time: 19, count: 0, },
+      { time: 20, count: 0, },
+      { time: 21, count: 0, },
+      { time: 22, count: 0, },
+      { time: 23, count: 0, },
+    ])
+    let countBar = ref({
+      tooltip: {
+        trigger: 'axis',
+        axisPointer: {
+          type: 'shadow',
+        },
+      },
+      legend: {
+        top: '0%',
+        show: false,
+      },
+      grid: {
+        left: '1%',
+        right: '5%',
+        bottom: '0%',
+        containLabel: true,
+      },
+      yAxis: {
+        type: 'value',
+        boundaryGap: [0, 0.01],
+      },
+      xAxis: {
+        type: 'category',
+        data: [],
+      },
+      series: [
+        // {
+        //   name: '全天各小时内API访问量',
+        //   type: 'bar',
+        //   barWidth: '20px',
+        //   itemStyle: {
+        //     normal: {
+        //       color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+        //         {
+        //           offset: 0,
+        //           color: '#01c871',
+        //         },
+        //         {
+        //           offset: 1,
+        //           color: '#55f49c',
+        //         },
+        //       ]),
+        //       barBorderRadius: 3,
+        //     },
+        //   },
+        //   label: {
+        //     show: true,
+        //     position: 'top'
+        //   },
+        //   data: [],
+        // },
+        {
+          name: '访问趋势',
+          data: [],
+          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
+            },
+          },
+        },
+      ],
+    })
+    let rankBar = ref({
+      tooltip: {
+        trigger: 'axis',
+        axisPointer: {
+          type: 'shadow',
+        },
+      },
+      legend: {
+        top: '0%',
+        show: false,
+      },
+      grid: {
+        left: '1%',
+        right: '5%',
+        bottom: '5%',
+        containLabel: true,
+      },
+      yAxis: {
+        type: 'value',
+        boundaryGap: [0, 0.01],
+      },
+      xAxis: {
+        type: 'category',
+        data: [],
+        axisLabel: {
+          formatter: (val) => {
+            let txt = val
+            if (val.length > 5) {
+              txt = val.substr(0, 5) + '...'
+            }
+            return txt
+          },
+          // rotate: 40,
+        },
+      },
+      dataZoom: [
+        {
+          show: true,
+          height: 10,
+          xAxisIndex: [0, 1],
+          bottom: 5,
+          right: '1%',
+          left: '5%',
+          start: 0,
+          end: 45,
+          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: 'API访问排行',
+          type: 'bar',
+          barWidth: '20px',
+          itemStyle: {
+            normal: {
+              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                {
+                  offset: 0,
+                  color: '#01c871',
+                },
+                {
+                  offset: 1,
+                  color: '#55f49c',
+                },
+              ]),
+              barBorderRadius: 3,
+            },
+          },
+          label: {
+            show: true,
+            position: 'top'
+          },
+          data: [],
+        },
+      ],
+    })
+    let minuteBar = ref({
+      tooltip: {
+        trigger: 'axis',
+        axisPointer: {
+          type: 'shadow',
+        },
+      },
+      legend: {
+        top: '0%',
+        show: false,
+      },
+      grid: {
+        left: '1%',
+        right: '5%',
+        bottom: '5%',
+        containLabel: true,
+      },
+      yAxis: {
+        type: 'value',
+        boundaryGap: [0, 0.01],
+      },
+      xAxis: {
+        type: 'category',
+        data: [],
+        axisLabel: {
+          formatter: (val) => {
+            let txt = val
+            if (val.length > 5) {
+              txt = val.substr(0, 5) + '...'
+            }
+            return txt
+          },
+          // rotate: 40,
+        },
+      },
+      dataZoom: [
+        {
+          show: true,
+          height: 10,
+          xAxisIndex: [0, 1],
+          bottom: 5,
+          right: '1%',
+          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: 'API访问排行',
+          type: 'bar',
+          barWidth: '20px',
+          itemStyle: {
+            normal: {
+              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                {
+                  offset: 0,
+                  color: '#01c871',
+                },
+                {
+                  offset: 1,
+                  color: '#55f49c',
+                },
+              ]),
+              barBorderRadius: 3,
+            },
+          },
+          label: {
+            show: true,
+            position: 'top'
+          },
+          data: [],
+        },
+      ],
+    })
+    let disposeInitData = ref([])
+    let minuteShow = ref(false)
+    const discrepancy = Math.abs((new Date().getTimezoneOffset() / 60))
+    console.log(discrepancy, '偏移量')
+    function dataInit () {
+      proxy.$api.getNowapi({}).then((res) => {
+        console.log(res, 'API返回内容')
+        let hoursInfo = new Date().getHours()
+        let hourData = timelist.value
+        let xdata = []; let seriesData = [];
+        var rankDatas = [];
+        let ranx = []; let rankdata = []
+        if (res.state === 200) {
+          //处理时间
+          for (let i in hourData) {
+            let nums = parseInt(i) + 1
+            hourData[i].time === hoursInfo ? hourData.splice(nums) : ''
+          }
+          //计算count
+          res.days.forEach((item) => {
+            item.time = item.key.slice(-2)
+            let timenum = item.time.substr(0, 1)
+            let timenumtwo = item.time.substr(1, 1)
+            console.log(timenum, '获取第一位数')
+            timenum === 0 ? item.time = parseInt(timenumtwo) + discrepancy : item.time = parseInt(item.time) + discrepancy
+            item.count = 0
+            let totalCount = item.value.minCnts
+            for (let i in totalCount) {
+              item.count += totalCount[i].cnt
+            }
+          })
+          disposeInitData.value = res.days
+          //赋值到数组
+          for (let t in res.days) {
+            let timelabels = res.days[t].time
+            hourData.forEach((item) => {
+              item.time === timelabels ? item.count = res.days[t].count : ''
+            })
+          }
+          hourData.forEach((item) => { xdata.push(item.time + '时'); seriesData.push(item.count) })
+          countBar.value.xAxis.data = xdata
+          countBar.value.series[0].data = seriesData
+          //处理排行
+          for (let i in res.days) {
+            let totaldata = res.days[i].value.apiCnt
+            for (let v in totaldata) {
+              let apiName = totaldata[v].api
+              let counts = totaldata[v].count
+              let host = totaldata[v].hostName[0]
+              console.log(apiName, counts, host, '9999999')
+              rankDatas.push({ api: apiName, count: counts, hostName: host })
+            }
+          }
+          let newArr = [];
+          rankDatas.forEach(el => {
+            const res = newArr.findIndex(ol => {
+              return el.api === ol.api && el.hostName == ol.hostName;
+            });
+            if (res !== -1) {
+              newArr[res].count = newArr[res].count + el.count;
+            } else {
+              newArr.push(el);
+            }
+          });
+          newArr.sort(function (x, y) {
+            var a = parseInt(x.count)
+            var b = parseInt(y.count)
+            if (a < b) return 1
+            if (a > b) return -1
+          })
+          newArr.forEach((item) => { ranx.push(item.api); rankdata.push(item.count) })
+          rankBar.value.xAxis.data = ranx
+          rankBar.value.series[0].data = rankdata
+
+          //默认显示最近一个小时的分钟
+          let minutex = []; let minuteData = []
+          if (res.days[res.days.length - 1].value.minCnts) {
+            res.days[res.days.length - 1].value.minCnts.forEach((itemM) => {
+              minutex.push(itemM.minute)
+              minuteData.push(itemM.cnt)
+            })
+            minuteShow.value = false
+            minuteBar.value.xAxis.data = minutex
+            minuteBar.value.series[0].data = minuteData
+          } else {
+            minuteShow.value = true
+          }
+          console.log(rankDatas, newArr, '排行榜')
+          console.log(hourData, res, xdata)
+        }
+      }).catch((error) => {
+        ElMessage.error('获取API管理内数据失败,API异常')
+      })
+    }
+    function clicktimes (val) {
+      console.log(val, disposeInitData.value, '调用到了')
+      let datas = disposeInitData.value
+      let clicknum = val.dataIndex
+      let nowminData = []
+      let minutex = []; let minuteData = []
+      for (let i in datas) {
+        datas[i].time === clicknum ? nowminData = datas[i].value.minCnts : ''
+      }
+      console.log(nowminData, '33333')
+      nowminData.length !== 0 ? (nowminData.forEach((item) => { minutex.push(item.minute); minuteData.push(item.cnt) }), minuteBar.value.xAxis.data = minutex, minuteBar.value.series[0].data = minuteData, minuteShow.value = false) : minuteShow.value = true
+    }
+    dataInit()
+    return { selectValue, options, tableData, dataInit, timelist, discrepancy, countBar, rankBar, minuteBar, minuteShow, clicktimes }
   },
 })
 </script>
@@ -86,10 +516,11 @@ export default ({
   color: #7f8c8d;
 }
 .apibox-top-left,
-.apibox-top-right {
-  width: 50%;
+.apibox-top-right,
+.apibox-top-center {
+  width: 33%;
   height: 28vh;
-  border: 1px solid #ccc;
+  border-right: 1px solid #ccc;
 }
 .apibox-list {
   width: 100%;
@@ -111,4 +542,14 @@ export default ({
 .apibox-list-header-select {
   float: right;
 }
+.echarts-topbox {
+  width: 100%;
+  height: 100%;
+}
+.notdatas {
+  margin: 12vh auto 0 auto;
+  font-size: 22px;
+  font-weight: bold;
+  color: #bdc3c7;
+}
 </style>

+ 2 - 2
TEAMModelBI/ClientApp/src/view/systemConfig/index.vue

@@ -31,9 +31,9 @@
         <el-tab-pane label="系统管理者" name="admin">
           <SetAdmin></SetAdmin>
         </el-tab-pane>
-        <!-- <el-tab-pane label="API管理" name="api">
+        <el-tab-pane label="API管理" name="api">
           <ApiManage></ApiManage>
-        </el-tab-pane> -->
+        </el-tab-pane>
       </el-tabs>
     </div>
   </div>

+ 4 - 4
TEAMModelBI/ClientApp/src/view/systemConfig/setAdmin.vue

@@ -13,7 +13,7 @@
           <p class="list-item-left-name">{{item.name}}</p>
           <p class="list-item-left-content"><span class="list-item-left-title">醍摩豆账号:</span><span>{{item.tmdId}}</span></p>
           <p class="list-item-left-content"><span class="list-item-left-title">手机号:</span><span>{{item.mobile}}</span></p>
-          <p class="list-item-left-content"><span class="list-item-left-title">加入时间:</span><span>2022-03-16</span></p>
+          <!-- <p class="list-item-left-content"><span class="list-item-left-title">加入时间:</span><span>2022-03-16</span></p> -->
         </div>
         <div class="list-item-right">
           <div class="deletebtn" @click="removeadmin(item)">
@@ -122,7 +122,7 @@ export default {
       }).then(() => {
         let data = { partitionKey: value.partitionKey, rowKey: value.rowKey, isAdmin: false }
         proxy.$api
-          .removeAdmin(data)
+          .setisAdmin(data)
           .then((res) => {
             res.state === 200
               ? (ElMessage.success('操作成功'), getAdmin())
@@ -197,12 +197,12 @@ export default {
 }
 .list-item-left-name {
   font-size: 20px;
-  margin-bottom: 10px;
+  margin: 10px 0px;
   font-weight: 600;
 }
 .list-item-left-content {
   font-size: 14px;
-  margin: 5px 0px;
+  margin: 10px 0px;
 }
 .deletebtn {
   width: 40px;

+ 1 - 2
TEAMModelBI/Controllers/BIHome/HomeStatisController.cs

@@ -316,8 +316,7 @@ namespace TEAMModelBI.Controllers.BIHome
             {
                 await _dingDing.SendBotMsg($"BI, {_option.Location} /homestatis/get-districtstics  \n  {ex.Message}\n{ex.StackTrace}     ", GroupNames.成都开发測試群組);
                 return BadRequest();
-            }
-            
+            }            
         }
 
         /// <summary>

+ 1 - 1
TEAMModelBI/Controllers/BIAbility/AbilityMgmtController.cs

@@ -18,7 +18,7 @@ using TEAMModelBI.Filter;
 using TEAMModelBI.Tool.Extension;
 using TEAMModelOS.SDK.Context.BI;
 
-namespace TEAMModelBI.Controllers.BIAbility
+namespace TEAMModelBI.Controllers.BINormal
 {
     [Route("biabilitymgmt")]
     [ApiController]

+ 1 - 1
TEAMModelBI/Controllers/BIAbility/AbilityTaskMgmtController.cs

@@ -20,7 +20,7 @@ using TEAMModelBI.Tool.Extension;
 using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Context.BI;
 
-namespace TEAMModelBI.Controllers.BIAbility
+namespace TEAMModelBI.Controllers.BINormal
 {
     [Route("biabilitytask")]
     [ApiController]

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

@@ -20,7 +20,7 @@ using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models;
 using TEAMModelOS.SDK.Models.Cosmos.BI;
 
-namespace TEAMModelBI.Controllers.BISchool
+namespace TEAMModelBI.Controllers.BINormal
 {
     [Route("area")]
     [ApiController]

+ 37 - 21
TEAMModelBI/Controllers/BISchool/BatchAreaController.cs

@@ -29,7 +29,7 @@ using System.Net;
 using TEAMModelOS.SDK;
 using TEAMModelOS.SDK.Context.BI;
 
-namespace TEAMModelBI.Controllers.BISchool
+namespace TEAMModelBI.Controllers.BINormal
 {
     [Route("batcharea")]
     [ApiController]
@@ -119,7 +119,24 @@ namespace TEAMModelBI.Controllers.BISchool
                     } 
                 }
 
-                areas.ForEach(async area => 
+                //areas.ForEach(async area =>
+                //{
+                //    area.schoolCount = await CommonFind.GetSqlValueCount(cosmosClient, "School", $"select value(count(c.id)) from c where c.areaId='{area.id}' and c.standard='{area.standard}'", "Base");
+
+                //    area.aquoteRec = await table.QueryWhereString<AreaQuoteRecord>($"PartitionKey eq 'QuoteRecord' and  areaId eq '{area.id}'");
+                //    //List<AreaQuoteRecord> aqr = await table.QueryWhereString<AreaQuoteRecord>($"PartitionKey eq 'QuoteRecord' and  areaId eq '{area.id}'");
+                //    //aqr.Sort((x, y) => y.RowKey.CompareTo(x.RowKey));
+
+                //    await foreach (var item in cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<string>(queryText: $"select value(c.accessConfig) from c where c.id='{area.id}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("AreaSetting") }))
+                //    {
+                //        if (string.IsNullOrEmpty(item))
+                //            area.cutArea = false;
+                //        else
+                //            area.cutArea = true;
+                //    };
+                //});
+
+                foreach (var area in areas)
                 {
                     area.schoolCount = await CommonFind.GetSqlValueCount(cosmosClient, "School", $"select value(count(c.id)) from c where c.areaId='{area.id}' and c.standard='{area.standard}'", "Base");
 
@@ -134,26 +151,8 @@ namespace TEAMModelBI.Controllers.BISchool
                         else
                             area.cutArea = true;
                     };
-                });
+                }
 
-                //await foreach (var item in azureClient.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<RecArea>(queryText: areaSql,requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base-Area") }))
-                //{
-                //    areas.Add(item);
-                //}
-                //foreach (var recArea in areas)
-                //{
-                //    recArea.schoolCount = await CommonFind.GetSqlValueCount(cosmosClient, "School", $"select value(count(c.id)) from c where c.areaId='{recArea.id}' and c.standard='{recArea.standard}'", "Base");
-                //    List<AreaQuoteRecord> aqr = await table.QueryWhereString<AreaQuoteRecord>($"PartitionKey eq 'QuoteRecord' and  areaId eq '{recArea.id}'");
-                //    aqr.Sort((x, y) => y.RowKey.CompareTo(x.RowKey));
-                //    recArea.aquoteRec = aqr;
-                //    await foreach (var item in cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<string>(queryText: $"select value(c.accessConfig) from c where c.id='{recArea.id}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("AreaSetting") }))
-                //    {
-                //        if (string.IsNullOrEmpty(item))
-                //            recArea.cutArea = false;
-                //        else
-                //            recArea.cutArea = true;
-                //    };
-                //}
 
                 return Ok(new { state = 200, areas, continuationToken });
             }
@@ -897,6 +896,23 @@ namespace TEAMModelBI.Controllers.BISchool
                 {
                     await cosmosClient.GetContainer(Constant.TEAMModelOS, "Normal").DeleteItemAsync<Area>(area.id, new PartitionKey("Base-Area")); //删除区
 
+                    List<Teacher> teachers = new();
+                    await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIterator<Teacher>(queryText: $"SELECT distinct value(c) FROM c join a in c.areas join s in c.schools where a.areaId='{area.id}' or s.areaId='{area.id}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") })) 
+                    {
+                        teachers.Add(item);
+                    }
+
+                    foreach (var item in teachers)
+                    {
+                        var tempArea = item.areas.Find(f => f.areaId.Equals(area.id));
+                        if (tempArea != null)
+                        {
+                            item.areas.Remove(tempArea);
+                        }
+                        item.schools.ForEach(fe => { if (fe.areaId.Equals(area.id)) fe.areaId = ""; });
+                        await cosmosClient.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Teacher>(item, item.id, new PartitionKey("Base"));
+                    }
+
                     List<string> scIds = await CommonFind.FindSchoolIds(cosmosClient, $"select c.id from c where c.areaId='{area.id}' and c.standard ='{area.standard}'", "Base");
 
                     foreach (var item in scIds)

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

@@ -621,30 +621,30 @@ namespace TEAMModelBI.Controllers.BISchool
                         await cosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync(item, item.id, new PartitionKey($"Base"));
                     }
 
-                    //修改学校顾问
-                    string sqlTxt = $"SELECT value(c) From c WHERE ARRAY_CONTAINS(c.roles,'assist',true)";
-                    List<SchoolTeacher> schoolTeachers = new List<SchoolTeacher>();
-                    await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<SchoolTeacher>(queryText: sqlTxt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{tempShool.id}") }))
+                    if (assistId.Count > 0)
                     {
-                        if (!assistId.Contains(item.id))
+                        //修改学校顾问
+                        string sqlTxt = $"SELECT value(c) From c WHERE ARRAY_CONTAINS(c.roles,'assist',true)";
+                        List<SchoolTeacher> schoolTeachers = new List<SchoolTeacher>();
+                        await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<SchoolTeacher>(queryText: sqlTxt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{tempShool.id}") }))
                         {
-                            if (item.roles.Contains("assist"))
+                            if (!assistId.Contains(item.id))
                             {
-                                item.roles.Remove("assist");
-                                if (item.roles.Count > 0)
-                                {
-                                    await cosmosClient.GetContainer("TEAMModelOS", "School").ReplaceItemAsync<SchoolTeacher>(item, item.id, new PartitionKey(item.code));
-                                }
-                                else
+                                if (item.roles.Contains("assist"))
                                 {
-                                    await cosmosClient.GetContainer("TEAMModelOS", "School").DeleteItemAsync<SchoolTeacher>(item.id, new PartitionKey(item.code));
+                                    item.roles.Remove("assist");
+                                    if (item.roles.Count > 0)
+                                    {
+                                        await cosmosClient.GetContainer("TEAMModelOS", "School").ReplaceItemAsync<SchoolTeacher>(item, item.id, new PartitionKey(item.code));
+                                    }
+                                    else
+                                    {
+                                        await cosmosClient.GetContainer("TEAMModelOS", "School").DeleteItemAsync<SchoolTeacher>(item.id, new PartitionKey(item.code));
+                                    }
                                 }
                             }
                         }
-                    }
 
-                    if (assistId.Count > 0)
-                    {
                         foreach (var itemTeacher in assistId)
                         {
                             Teacher tempTeacher = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<Teacher>($"{itemTeacher}", new PartitionKey("Base"));

+ 26 - 6
TEAMModelBI/Controllers/BISchool/SchoolController.cs

@@ -859,6 +859,7 @@ namespace TEAMModelBI.Controllers.BISchool
                 StringBuilder msg = new();
                 List<DelSchoolRel> delSchoolRels = new();
 
+
                 foreach (var tempId in schools)
                 {
                     List<string> scTchIds = new();
@@ -883,13 +884,30 @@ namespace TEAMModelBI.Controllers.BISchool
 
                     foreach (var item in scTchIds)
                     {
+                        //学校教师信息
                         var tchRespnse = await cosmosClient.GetContainer("TEAMModelOS", "School").DeleteItemStreamAsync($"{item}", new PartitionKey($"Teacher-{tempId}"));
                         if (tchRespnse.Status == 204)
                             msg.AppendLine($"删除教师,删除状态:{tchRespnse.Status},删除ID:{item}");
                         else
                             delSchoolRels.Add(new DelSchoolRel() { id = $"{item}", code = $"Teacher-{tempId}", type = 2, status = response.Status });
-                    }
 
+                        //教师基础信息
+                        var tchBaseResponse = await cosmosClient.GetContainer("TEAMModelOS", "Teacher").ReadItemStreamAsync($"{item}", new PartitionKey("Base"));
+                        if (tchBaseResponse.Status == 200)
+                        {
+                            using var json = await JsonDocument.ParseAsync(response.ContentStream);
+                            Teacher teacher = json.ToObject<Teacher>();
+                            var tempSc = teacher.schools.Find(f => f.schoolId.Equals($"{tempId}"));
+                            if (tempSc != null)
+                            {
+                                teacher.schools.Remove(tempSc);
+                                await cosmosClient.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Teacher>(teacher, teacher.id, new PartitionKey("Base"));
+                            }
+                        }
+                        else
+                            delSchoolRels.Add(new DelSchoolRel { id = $"{item}", code = "Base", type = 2, status = response.Status });
+                    }
+                    //删除学校学生
                     foreach (var item in scStuIds)
                     {
                         var stuRespnse = await cosmosClient.GetContainer("TEAMModelOS", "Student").DeleteItemStreamAsync($"{item}", new PartitionKey($"Base-{tempId}"));
@@ -995,9 +1013,6 @@ namespace TEAMModelBI.Controllers.BISchool
             int yearACTCnt = 0;  //今年活动
             int lastYearACTCnt = 0;  //去年活动
 
-            int yearCnt = 0;
-            int lastCnt = 0;
-
             SchoolInfo schoolInfo = await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<SchoolInfo>($"{schoolId}", new PartitionKey("Base"));
             if (schoolInfo != null)
             {
@@ -1060,7 +1075,7 @@ namespace TEAMModelBI.Controllers.BISchool
                 //取得購買紀錄
                 List<object> order = await GetOrderHisFromCoreBBAsync(schoolId.GetString());
 
-                return Ok(new { state = RespondCode.Ok, tecCnt, classCnt, stuCnt, roomCnt, lesCnt, weekLesCnt, lastWeekLesCnt, weekACTCnt, lastWeekACTCnt, termLesCnt, lastTermLesCnt, termACTCnt, lastTermActCnt, yearLesCnt, lastYearLesCnt, yearACTCnt, lastYearACTCnt, schoolInfo, ACTCnt, order, yearS, yearE, lastYearS, lastYearE });
+                return Ok(new { state = RespondCode.Ok, tecCnt, classCnt, stuCnt, roomCnt, lesCnt, weekLesCnt, lastWeekLesCnt, weekACTCnt, lastWeekACTCnt, termLesCnt, lastTermLesCnt, termACTCnt, lastTermActCnt, yearLesCnt, lastYearLesCnt, yearACTCnt, lastYearACTCnt, schoolInfo, ACTCnt, buy = new { yearS, yearE, lastYearS, lastYearE, order } });
             }
             else
                 return Ok(new { state = RespondCode.NotFound, msg = "未找到该学校信息!" });
@@ -1598,18 +1613,23 @@ namespace TEAMModelBI.Controllers.BISchool
 
             List<string> schoolIds = await CommonFind.FindSchoolIds(cosmosClient, $"{tmdId}");
 
+            int adCnt = 0;
             List<ScProdEd> scInfos = new();
             List<string> products = new();
             if (schoolIds.Count > 0)
             {
                 string scInfoSql = $"select c.id,c.name,c.code,c.picture,c.type,c.size,c.scale from c ";
                 string idSql = BICommonWay.ManyScSql("c.id", schoolIds);
+
                 scInfos = await CommonFind.GetObject<ScProdEd>(cosmosClient, "School", $"{scInfoSql} where {idSql}", "Base");
 
                 if (schoolIds.Count > 0)
                 {
                     foreach (var scProd in scInfos)
                     {
+                        string sqlText = "SELECT value(count(ARRAY_LENGTH(c.deviceBound))) FROM c where c.dataType ='serial'";
+                        adCnt += await CommonFind.GetSqlValueCount(cosmosClient, "School", sqlText, $"Product-{scProd.id}");
+
                         var response = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(scProd.id, new PartitionKey("ProductSum"));
                         if (response.Status == 200)
                         {
@@ -1661,7 +1681,7 @@ namespace TEAMModelBI.Controllers.BISchool
 
             var productAn = products.GroupBy(g => g).Select(s => new { key = s.Key, cnt = s.ToList().Count }).ToList();
 
-            return Ok(new { state = RespondCode.Ok,  scInfos, productAn });
+            return Ok(new { state = RespondCode.Ok, adCnt, scInfos, productAn });
             // var productAn = products.GroupBy(x => x).Select(y => new { key = y.Key, count = y.ToList().Count }).ToList();
 
         }

+ 0 - 100
TEAMModelBI/Controllers/BITest/TestController.cs

@@ -1066,54 +1066,6 @@ namespace TEAMModelBI.Controllers.BITest
             return Ok(new { state = 200, continuationToken, count = schools.Count, schools });
         }
 
-        /// <summary>
-        /// 测试隐式登录
-        /// </summary>
-        /// <returns></returns>
-        [HttpPost("get-implicit")]
-        public async Task<IActionResult> GetImplicit()
-        {
-            var clientID = _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
-            var clientSecret = _configuration.GetValue<string>("HaBookAuth:CoreService:clientSecret");
-
-            var location = _option.Location;
-            if (location.Contains("China"))
-            {
-                location = "China";
-            }
-            else if (location.Contains("Global"))
-            {
-                location = "Global";
-            }
-
-            var token = await CoreTokenExtensions.CreateAccessToken(clientID, clientSecret, location);
-            if (_httpClient.DefaultRequestHeaders.Contains("Authorization"))
-            {
-                _httpClient.DefaultRequestHeaders.Remove("Authorization");
-            }
-
-            _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token.AccessToken}");
-            var url = _configuration.GetValue<string>("HaBookAuth:CoreAPI");
-            url = $"{url}/oauth2/implicit";
-            Dictionary<string, string> data = new()
-            {
-                { "grant_type", "implicit" },
-                { "client_id", clientID },
-                { "account", "1636016499" },
-                { "nonce", Guid.NewGuid().ToString() }
-            };
-
-            HttpResponseMessage responseMessage = await _httpClient.PostAsJsonAsync(url, data);
-            if (responseMessage.StatusCode == HttpStatusCode.OK) //BI连接字符
-            {
-                string content = await responseMessage.Content.ReadAsStringAsync();
-
-
-            }
-
-            return Ok(new { state = 200, token, responseMessage });
-        }
-
         /// <summary>
         /// 测试
         /// </summary>
@@ -1180,34 +1132,6 @@ 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")]
-        public async Task<IActionResult> GetParallelForEach() 
-        {
-
-            //string ser= $"SELECT Id,name,ownership,modifiedDate FROM {5('TableName')}WHERE ORDER BY Id fetch_size": { variables('Rows')}, "cursor": "";
-            var cosmosClient = _azureCosmos.GetCosmosClient();
-            List<string> schoolIds = await CommonFind.FindSchoolIds(cosmosClient, $"1636016499");
-            List<School> schools = new List<School>();
-            Parallel.ForEach(schoolIds, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, async item =>
-            {
-
-                School school = new();
-                var response = await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(item, new PartitionKey("Base"));
-                if (response.Status == 200)
-                {
-                    using var json = await JsonDocument.ParseAsync(response.ContentStream);
-                    school = json.ToObject<School>();
-                }
-                schools.Add(school);
-            });
-
-            return Ok(new { state = 200 , schools });
-        }
-
         /// <summary>
         /// table 表分页查询
         /// </summary>
@@ -1229,8 +1153,6 @@ namespace TEAMModelBI.Controllers.BITest
             operateLogs.Sort((x, y) => y.time.CompareTo(x.time));
             List<BIOptLog> bIOptLogs = operateLogs.Skip((page - 1) * size).Take(size).ToList();
 
-
-
             return Ok(new { state = 200, allcount = operateLogs.Count, bIOptLogs });
         }
 
@@ -1392,27 +1314,6 @@ namespace TEAMModelBI.Controllers.BITest
             #endregion
         }
 
-        /// <summary>
-        /// 
-        /// </summary>
-        /// <param name="linqTestm"></param>
-        /// <param name="site"></param>
-        /// <returns></returns>
-        [HttpPost("parameter")]
-        public async Task<IActionResult> parameter(linqTest linqTestm, [FromHeader]string site)
-        {
-            var cosmosClient = _azureCosmos.GetCosmosClient();
-
-            if ($"{site}".Equals(BIConst.Global))
-                cosmosClient = _azureCosmos.GetCosmosClient(name: BIConst.Global);
-            linqTest linqTest = new linqTest();
-            linqTest.id = linqTestm.id; linqTest.name = linqTestm.name;
-
-            var resp = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync($"{linqTestm.id}", new PartitionKey("Base"));
-
-            return Ok(new { state = 200, linqTest,cosmosClient,site });
-        }
-
         /// <summary>
         /// 
         /// </summary>
@@ -1472,7 +1373,6 @@ namespace TEAMModelBI.Controllers.BITest
         }
 
 
-
         public class linqTest
         {
             public string id{ get; set; }

+ 4 - 4
TEAMModelBI/Controllers/Census/ActivitySticsController.cs

@@ -487,11 +487,11 @@ namespace TEAMModelBI.Controllers.Census
             allSize = await CommonFind.GetSqlValueCount(cosmosClient, "School", commSql,  "Base");
 
 
-            weekScCnt = await CommonFind.GetSqlValueCount(cosmosClient, "School", $"{commSql} where c.createTime >= {termStart} and c.createTime >= {termEnd}", "Base");
-            monthScCnt = await CommonFind.GetSqlValueCount(cosmosClient, "School", $"{commSql} where c.createTime >= {monthS} and c.createTime >= {monthE}", "Base");
+            weekScCnt = await CommonFind.GetSqlValueCount(cosmosClient, "School", $"{commSql} where c.createTime >= {weekStart} and c.createTime <= {weekEnd}", "Base");
+            monthScCnt = await CommonFind.GetSqlValueCount(cosmosClient, "School", $"{commSql} where c.createTime >= {monthS} and c.createTime <= {monthE}", "Base");
 
-            weekTchCnt = await CommonFind.GetSqlValueCount(cosmosClient, "Teacher", $"{commSql} where c.createTime >= {termStart} and c.createTime >= {termEnd}", "Base");
-            monthTchCnt = await CommonFind.GetSqlValueCount(cosmosClient, "Teacher", $"{commSql} where c.createTime >= {monthS} and c.createTime >= {monthE}", "Base");
+            weekTchCnt = await CommonFind.GetSqlValueCount(cosmosClient, "Teacher", $"{commSql} where c.createTime >= {weekStart} and c.createTime <= {weekEnd}", "Base");
+            monthTchCnt = await CommonFind.GetSqlValueCount(cosmosClient, "Teacher", $"{commSql} where c.createTime >= {monthS} and c.createTime <= {monthE}", "Base");
 
             heCount = await CommonFind.GetSqlValueCount(cosmosClient, "School", $"{commSql} where c.type = 1", "Base");
             geCount = await CommonFind.GetSqlValueCount(cosmosClient, "School", $"{commSql} where c.type = 2", "Base");

+ 3 - 3
TEAMModelBI/Controllers/Census/LessonSticsController.cs

@@ -47,7 +47,7 @@ namespace TEAMModelBI.Controllers.Census
         }
 
         /// <summary>
-        /// 依据区级ID分析该去所有学校课例   //后端有
+        /// 依据区级ID分析该去所有学校课例   //已对接
         /// </summary>
         /// <param name="jsonElement"></param>
         /// <returns></returns>
@@ -57,7 +57,7 @@ namespace TEAMModelBI.Controllers.Census
         {
             if (!jsonElement.TryGetProperty("areaId", out JsonElement areaId)) return BadRequest();
             jsonElement.TryGetProperty("site", out JsonElement site);
-            var (lWeekS, lWeekE) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "lastterm");
+            var (lWeekS, lWeekE) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "week");
             var (monthS, monthE) = TimeHelper.GetStartOrEnd(DateTimeOffset.UtcNow, "month");
 
             var cosmosClient = _azureCosmos.GetCosmosClient();
@@ -77,7 +77,7 @@ namespace TEAMModelBI.Controllers.Census
                     item.weekCnt = await CommonFind.GetSqlValueCount(cosmosClient, "School", $"select value(count(c.id)) from c where c.startTime >= {lWeekS} and c.startTime <= {lWeekE}", $"LessonRecord-{item.id}");
                     item.monthCnt = await CommonFind.GetSqlValueCount(cosmosClient, "School", $"select value(count(c.id)) from c where  c.startTime >= {monthS} and c.startTime <= {monthE}", $"LessonRecord-{item.id}");
                 }
-                areaSchools = areaSchools.Where(w => w.allCnt != 0 && w.weekCnt != 0 && w.monthCnt != 0).ToList();
+                areaSchools = areaSchools.Where(w => (w.allCnt != 0 || w.weekCnt != 0 || w.monthCnt != 0)).ToList();
             }
 
             return Ok(new { state = RespondCode.Ok, areaSchools });

+ 4 - 1
TEAMModelBI/TEAMModelBI.csproj

@@ -12,8 +12,12 @@
 
 	<ItemGroup>
 		<!-- Don't publish the SPA source files, but do show them in the project files list -->
+		<Compile Remove="Controllers\OperateRecord\**" />
 		<Content Remove="$(SpaRoot)**" />
+		<Content Remove="Controllers\OperateRecord\**" />
+		<EmbeddedResource Remove="Controllers\OperateRecord\**" />
 		<None Remove="$(SpaRoot)**" />
+		<None Remove="Controllers\OperateRecord\**" />
 		<None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
 	</ItemGroup>
 
@@ -27,7 +31,6 @@
 	</ItemGroup>
 
 	<ItemGroup>
-		<Folder Include="Controllers\OperateRecord\" />
 		<Folder Include="wwwroot\" />
 	</ItemGroup>
 	<PropertyGroup>

+ 1 - 1
TEAMModelOS.FunctionV4/Program.cs

@@ -4,7 +4,7 @@ using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection.Extensions;
 using Microsoft.Extensions.Hosting;
-using PuppeteerSharp;
+
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;

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

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

+ 1 - 1
TEAMModelOS.FunctionV4/TimeTrigger/IESTimerTrigger.cs

@@ -61,7 +61,7 @@ namespace TEAMModelOS.FunctionV4.TimeTrigger
                 var m = datetime.Month >= 10 ? $"{datetime.Month}" : $"0{datetime.Month}";
                 var d = datetime.Day >= 10 ? $"{datetime.Day}" : $"0{datetime.Day}";
                 var h = datetime.Hour >= 10 ? $"{datetime.Hour}" : $"0{datetime.Hour}";
-                if (location.Contains("China"))
+                if (location.Equals("China"))
                 {
                     string path = $"resourceId=/SUBSCRIPTIONS/73B7F9EF-D8B7-4444-9E8D-D80B43BF3CD4/RESOURCEGROUPS/TEAMMODELCHENGDU/PROVIDERS/MICROSOFT.NETWORK/APPLICATIONGATEWAYS/OSFIREWARE/y={y}/m={m}/d={d}/h={h}/m=00/PT1H.json";
                     var retn = await BILogAnalyseService.GetPathAnalyse(_azureStorage, path, "LogStorage");

+ 8 - 0
TEAMModelOS.SDK/Context/Attributes/Filter/ApiTokenAttribute.cs

@@ -72,6 +72,14 @@ namespace TEAMModelOS.Filter
         /// </summary>
         public string Name { get; set; }
         /// <summary>
+        /// 接口名称
+        /// </summary>
+        public string TName { get; set; }
+        /// <summary>
+        /// 接口名称
+        /// </summary>
+        public string EName { get; set; }
+        /// <summary>
         /// 接口属性,标记是读R,写W还是通知N类型
         /// </summary>
         public string RWN { get; set; }

+ 59 - 0
TEAMModelOS.SDK/DI/CoreAPI/CoreAPIHttpService.cs

@@ -47,6 +47,65 @@ namespace TEAMModelOS.SDK
             _httpClient = httpClient;
             options = optionsMonitor;
         }
+        /// <summary>
+        ///  发送短信验证码
+        /// </summary>
+        /// <param name="clientID"></param>
+        /// <param name="clientSecret"></param>
+        /// <param name="location"></param>
+        /// <param name="url"></param>
+        /// <param name="data"></param>
+        /// <returns></returns>
+        public async Task<(HttpStatusCode  code ,string content)> SendSmsPin(Dictionary<string, object> data, string location, IConfiguration _configuration, DI.DingDing _dingDing)
+
+        {
+            try {
+                var url = _configuration.GetValue<string>("HaBookAuth:CoreAPI");
+                //url = "https://api2-rc.teammodel.cn";
+                url = $"{url}/service/sandsms/pin";
+                var clientID = _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
+                var clientSecret = _configuration.GetValue<string>("HaBookAuth:CoreService:clientSecret");
+                if (location.Contains("China"))
+                {
+                    location = "China";
+                }
+                else if (location.Contains("Global"))
+                {
+                    location = "Global";
+                }
+                var token = await CoreTokenExtensions.CreateAccessToken(clientID, clientSecret, location);
+                if (_httpClient.DefaultRequestHeaders.Contains("Authorization"))
+                {
+                    _httpClient.DefaultRequestHeaders.Remove("Authorization");
+
+                }
+                _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token.AccessToken}");
+                HttpResponseMessage responseMessage = await _httpClient.PostAsJsonAsync(url, data);
+                if (responseMessage.Content != null)
+                {
+                    string content = await responseMessage.Content.ReadAsStringAsync();
+                    if (!string.IsNullOrWhiteSpace(content))
+                    {
+                        return (responseMessage.StatusCode, content);
+                    }
+                    else {
+                        return (responseMessage.StatusCode, null);
+                    }
+                }
+                else {
+                    return (responseMessage.StatusCode, null);
+                }
+                
+               
+            } catch (Exception ex) {
+                await _dingDing.SendBotMsg($"{location}验证码发送异常:\n{ex.Message}\n{ex.StackTrace}", DI.GroupNames.醍摩豆服務運維群組);
+                return (HttpStatusCode.InternalServerError,null);
+            }
+
+
+        }
+
+
         /// <summary>
         ///  隐式登录
         /// </summary>

+ 2 - 2
TEAMModelOS.SDK/DI/HttpTrigger/WebHookHttpTrigger.cs

@@ -48,7 +48,7 @@ namespace TEAMModelOS.SDK.DI
         /// <param name="request"></param>
         /// <returns></returns>
         [Function("school-auth-change")]
-        [ApiToken(Auth = "1001", Name = "学校数据授权变更", RWN = "N")]
+        [ApiToken(Auth = "1001", Name = "学校数据授权变更", TName = "", EName = "", RWN = "N")]
         public async Task<HttpResponseData> NoticeSchoolAuthChange([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "webhook/school-auth-change")] HttpRequestData request)
         {
             var response = request.CreateResponse(HttpStatusCode.OK);
@@ -86,7 +86,7 @@ namespace TEAMModelOS.SDK.DI
         /// <param name="request"></param>
         /// <returns></returns>
         [Function("group-member-change")]
-        [ApiToken(Auth = "1201", Name = "名单成员变更", RWN = "N")]
+        [ApiToken(Auth = "1201", Name = "名单成员变更",TName ="",EName ="", RWN = "N")]
         public async Task<HttpResponseData> NoticeGroupChange([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "webhook/group-member-change")] HttpRequestData request) {
             var response = request.CreateResponse(HttpStatusCode.OK);
             (  List < BizConfig > businessConfigs, List<string> webhookdomain, GroupChange data) = await  GetRequestData<GroupChange>(request);

+ 4 - 0
TEAMModelOS.SDK/Helper/Common/ReflectorExtensions/ReflectorExtensions.cs

@@ -95,6 +95,8 @@ namespace TEAMModelOS.SDK.Helper.Common.ReflectorExtensions
                             auth = int.Parse(apiTokenAttribute.Auth),
                             method = "POST",
                             name = apiTokenAttribute.Name,
+                            tname = apiTokenAttribute.TName,
+                            ename = apiTokenAttribute.EName,
                             type = apiTokenAttribute.RWN,
                             notice = functionAttribute.Name,
                             url= "/webhook"
@@ -139,6 +141,8 @@ namespace TEAMModelOS.SDK.Helper.Common.ReflectorExtensions
                                 method = "POST",
                                 name = apiTokenAttribute.Name,
                                 type= apiTokenAttribute.RWN,
+                                ename = apiTokenAttribute.EName,
+                                tname = apiTokenAttribute.TName,
                                 url =$"/{routeAttr.Template}/{httpPostAttribute.Template}"
                             };
                             openApis.Add(openApi);

+ 9 - 1
TEAMModelOS.SDK/Models/Service/LessonService.cs

@@ -91,7 +91,15 @@ namespace TEAMModelOS.SDK.Models.Service
                 dict.Add("<=.startTime", now);
                 dict.Add(">=.startTime", dayB);
             }
-
+            if (request.TryGetProperty("expire", out JsonElement expire) && expire.ValueKind.Equals(JsonValueKind.True))
+            {
+                dict.Add(">.expire", 0);
+                dict.Add("!=.status", 404);
+            }
+            if (request.TryGetProperty("is404", out JsonElement is404) && is404.ValueKind.Equals(JsonValueKind.True))
+            {
+                dict.Add("=.status", 404);
+            }
             if (request.TryGetProperty("month", out JsonElement month) && month.GetBoolean())
             {
                 //DateTime dateTimeA = Convert.ToDateTime(DateTimeOffset.UtcNow.ToString("D"));

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

@@ -359,6 +359,9 @@ namespace TEAMModelOS.SDK.Models.Service
             }
             string ip = IpPort.Split(":")[0];
             string region = await _searcher.SearchIpAsync(ip);
+            if (!string.IsNullOrWhiteSpace(region)) {
+                region = region.Replace("中国·", "").Replace("中国", "").Replace("台湾省", "台湾");
+            }
             return (ip, region);
         }
 

+ 19 - 2
TEAMModelOS.SDK/Models/Table/OpenApi.cs

@@ -26,9 +26,18 @@ namespace TEAMModelOS.SDK.Models
     {
         public OpenApi() {
           
-        } 
+        }
+
         /// <summary>
-        /// 接口名称
+        /// 英文接口名称
+        /// </summary>
+        public string ename { get; set; }
+        /// <summary>
+        /// 繁体中文接口名称
+        /// </summary>
+        public string tname { get; set; }
+        /// <summary>
+        /// 简体中文接口名称
         /// </summary>
         public string name { get; set; }
         /// <summary>
@@ -84,6 +93,14 @@ namespace TEAMModelOS.SDK.Models
             PartitionKey = "IES5-WEBHOOK";
         }
         /// <summary>
+        /// 英文接口名称
+        /// </summary>
+        public string ename { get; set; }
+        /// <summary>
+        /// 繁体中文接口名称
+        /// </summary>
+        public string tname { get; set; }
+        /// <summary>
         /// 接口名称
         /// </summary>
         public string name { get; set; }

+ 110 - 102
TEAMModelOS/ClientApp/public/lang/en-US.js

@@ -484,6 +484,8 @@ const LANG_EN_US = {
         classroom: 'Classroom:',
         expiredData: 'Expiry Date:',
         hadExp: '(Expired)',
+        unused: '(None activate)',
+        forever: '(Permanent License)',
         useInfo: 'Use Status:',
         unband: 'Unbind Device',
         cancel: 'Cancel',
@@ -1752,37 +1754,37 @@ const LANG_EN_US = {
         publishType1: 'Start Now',
         publishType2: 'Scheduled Start Time',
         testType: [{
-                label: "Single Answer",
-                value: "single"
-            },
-            {
-                label: "Multiple Answers",
-                value: "multiple"
-            },
-            {
-                label: "True-False",
-                value: "judge"
-            },
-            {
-                label: "Cloze",
-                value: "complete"
-            },
-            {
-                label: "Writing",
-                value: "subjective"
-            },
-            {
-                label: "Question Set",
-                value: "compose"
-            },
-            {
-                label: "Correcting",
-                value: "correct"
-            },
-            {
-                label: "Matching",
-                value: "connector"
-            },
+            label: "Single Answer",
+            value: "single"
+        },
+        {
+            label: "Multiple Answers",
+            value: "multiple"
+        },
+        {
+            label: "True-False",
+            value: "judge"
+        },
+        {
+            label: "Cloze",
+            value: "complete"
+        },
+        {
+            label: "Writing",
+            value: "subjective"
+        },
+        {
+            label: "Question Set",
+            value: "compose"
+        },
+        {
+            label: "Correcting",
+            value: "correct"
+        },
+        {
+            label: "Matching",
+            value: "connector"
+        },
         ]
     },
     // 首页相关
@@ -2264,7 +2266,7 @@ const LANG_EN_US = {
             error1: "Failed to start",
             message1: "Switch to my topic",
             message2: "Switch to reply to my topic",
-            report: ["Illegal information", "Adultury and consulting", "privacy disclosur", "Personal attacks", "Garbage marketing"],
+            report: ["Illegal info", "Pornography info", "Privacy disclosure", "Personal attack", "Spam marketing"],
         },
         // 活动
         activity: {
@@ -2319,6 +2321,8 @@ const LANG_EN_US = {
             electiveCourse: "Competency",
             sendSummary: "填寫研修總結",
             plaSummary: "請填寫您的研修總結",
+            openReport: "View report",
+            message5: "All certification materials must be passed to receive credits",
         }
     },
     // 知识点管理
@@ -2919,6 +2923,8 @@ const LANG_EN_US = {
             fileName: '課堂記錄統計錶',
             noData: '暫無數據導出'
         },
+        expireStatus: 'Expiring soon',
+        deleteStatus: 'Cleaned',
         noRecordTip: 'No lesson records are available under the current school system!',
         delStatusTip: 'Lesson records have been cleared',
         overdue: 'expired',
@@ -3172,34 +3178,34 @@ const LANG_EN_US = {
         title: 'Survey',
         questionType: 'Survey Type',
         type: [{
-                code: 0,
-                value: 'Academic Education'
-            },
-            {
-                code: 1,
-                value: 'Satisfaction Survey'
-            },
-            {
-                code: 2,
-                value: 'Voting Election'
-            },
-            {
-                code: 3,
-                value: 'Others'
-            }
+            code: 0,
+            value: 'Academic Education'
+        },
+        {
+            code: 1,
+            value: 'Satisfaction Survey'
+        },
+        {
+            code: 2,
+            value: 'Voting Election'
+        },
+        {
+            code: 3,
+            value: 'Others'
+        }
         ],
         state: [{
-                code: 100,
-                value: 'Scheduled'
-            },
-            {
-                code: 200,
-                value: 'In progress'
-            },
-            {
-                code: 300,
-                value: 'Has ended'
-            }
+            code: 100,
+            value: 'Scheduled'
+        },
+        {
+            code: 200,
+            value: 'In progress'
+        },
+        {
+            code: 300,
+            value: 'Has ended'
+        }
         ]
     },
     // 站内通知
@@ -3306,7 +3312,9 @@ const LANG_EN_US = {
         saveErr: 'Failed to save',
         formErr: 'Please check if the information is filled out correctly and completely',
         imgTips: 'Note:The image format only supports jpg, jpeg, png; the size of the image cannot exceed 1M; the file name cannot contain special characters.',
-        ptText: 'Resource Platform Link'
+        ptText: 'Resource Platform Link',
+        text1: 'District Resource Sites',
+        text2: 'School Resource Sites',
     },
     // 注册相关
     regist: {
@@ -4154,50 +4162,50 @@ const LANG_EN_US = {
             achievement: "My Grade",
         },
         testType: [{
-                label: "Single Answer",
-                value: "single"
-            },
-            {
-                label: "Multiple Answers",
-                value: "multiple"
-            },
-            {
-                label: "True-False",
-                value: "judge"
-            },
-            {
-                label: "Cloze",
-                value: "complete"
-            },
-            {
-                label: "Writing",
-                value: "subjective"
-            },
-            {
-                label: "Question Set",
-                value: "compose"
-            },
-            {
-                label: "Correcting",
-                value: "correct"
-            },
-            {
-                label: "Matching",
-                value: "connector"
-            },
+            label: "Single Answer",
+            value: "single"
+        },
+        {
+            label: "Multiple Answers",
+            value: "multiple"
+        },
+        {
+            label: "True-False",
+            value: "judge"
+        },
+        {
+            label: "Cloze",
+            value: "complete"
+        },
+        {
+            label: "Writing",
+            value: "subjective"
+        },
+        {
+            label: "Question Set",
+            value: "compose"
+        },
+        {
+            label: "Correcting",
+            value: "correct"
+        },
+        {
+            label: "Matching",
+            value: "connector"
+        },
         ],
         state: [{
-                type: 'All',
-                status: 'All Activity Status'
-            },
-            {
-                type: 'going',
-                status: 'In progress'
-            },
-            {
-                type: 'finish',
-                status: 'Completed'
-            }
+            type: 'All',
+            status: 'All Activity Status'
+        },
+        {
+            type: 'going',
+            status: 'In progress'
+        },
+        {
+            type: 'finish',
+            status: 'Completed'
+        }
         ],
         event: {
             allStatus: 'All Activity Status',
@@ -5517,7 +5525,7 @@ const LANG_EN_US = {
         props7: 'Not enough storage space!',
         uploadText: 'Tap or drag to upload',
         resTips: 'HiTeach generates teaching materials that only support HTEX format previews',
-        space: 'Available Space:',
+        space: '剩余空間:',
         calcing: 'Calculating...',
         blobFull: '(Full)',
         otherType: 'Other types',

+ 10 - 2
TEAMModelOS/ClientApp/public/lang/zh-CN.js

@@ -484,6 +484,8 @@ const LANG_ZH_CN = {
         classroom: '教室:',
         expiredData: '到期日:',
         hadExp: '(已到期)',
+        unuse: '(未启用)',
+        forever: '(永久授权)',
         useInfo: '使用状况:',
         unband: '解绑设备',
         cancel: '取消',
@@ -2320,6 +2322,8 @@ const LANG_ZH_CN = {
             electiveCourse: "能力点",
             sendSummary: "填写研修总结",
             plaSummary: "请填写您的研修总结",
+            openReport: "查看报告",
+            message5: "认证材料必须全部通过才能获得学时",
         }
     },
     // 知识点管理
@@ -2920,6 +2924,8 @@ const LANG_ZH_CN = {
             fileName: '课堂记录统计表',
             noData: '暂无数据导出'
         },
+        expireStatus: '即将过期',
+        deleteStatus: '已清理',
         delStatusTip: '课堂记录已被清理',
         noRecordTip: '当前学段下未查询到课堂记录!',
         overdue: '到期',
@@ -3309,7 +3315,9 @@ const LANG_ZH_CN = {
         saveErr: '保存失败',
         formErr: '请检查信息是否正确完整填写',
         imgTips: '温馨提示:图片格式仅支持jpg、jpeg、png;图片大小不超过1M;文件名不能包含特殊字符。',
-        ptText: '资源平台链接'
+        ptText: '资源平台链接',
+        text1: '区级资源平台',
+        text2: '校级资源平台',
     },
     // 注册相关
     regist: {
@@ -5522,7 +5530,7 @@ const LANG_ZH_CN = {
         props7: '存储空间不足!',
         uploadText: '点击或者拖拽上传',
         resTips: 'HiTeach生成的课件,只支持HTEX格式的教材预览',
-        space: '可用空间:',
+        space: '剩余空间:',
         calcing: '计算中...',
         blobFull: '(已满)',
         otherType: '其他',

+ 11 - 3
TEAMModelOS/ClientApp/public/lang/zh-TW.js

@@ -484,6 +484,8 @@ const LANG_ZH_TW = {
         classroom: '教室:',
         expiredData: '到期日:',
         hadExp: '(已到期)',
+        unuse: '(未啟用)',
+        forever: '(永久授權)',
         useInfo: '使用狀況:',
         unband: '解綁設備',
         cancel: '取消',
@@ -2265,7 +2267,7 @@ const LANG_ZH_TW = {
             error1: "發表失敗",
             message1: "切換至我的話題",
             message2: "切換至回覆我的",
-            report: ["違法資訊", "涉黃資訊", "洩露隱私", "人身攻擊", "垃圾行銷"],
+            report: ["非法資訊", "黃色資訊", "洩露隱私", "人身攻擊", "垃圾行銷"],
         },
         // 活动
         activity: {
@@ -2320,6 +2322,8 @@ const LANG_ZH_TW = {
             electiveCourse: "增能項目",
             sendSummary: "填寫研習總結",
             plaSummary: "請填寫您的研習總結",
+            openReport: "查看報告",
+            message5: "認證資料必須全部通過才能獲得學時",
         }
     },
     // 知识点管理
@@ -2920,6 +2924,8 @@ const LANG_ZH_TW = {
             fileName: '課堂記錄統計表',
             noData: '暫無數據匯出'
         },
+        expireStatus: '即將過期',
+        deleteStatus: '已清理',
         delStatusTip: '課堂記錄已被清理',
         noRecordTip: '當前學段下未查詢到課堂記錄!',
         overdue: '到期',
@@ -3309,7 +3315,9 @@ const LANG_ZH_TW = {
         saveErr: '儲存失敗',
         formErr: '請檢查資訊是否正確完整填寫',
         imgTips: '溫馨提示:圖片格式僅支援jpg、jpeg、png;圖片大小不超過1M;文件名不能包含特殊字元。 ',
-        ptText: '資源平臺'
+        ptText: '資源平臺',
+        text1: '區級資源平臺',
+        text2: '校級資源平臺',
     },
     // 注册相关
     regist: {
@@ -5522,7 +5530,7 @@ const LANG_ZH_TW = {
         props7: '儲存空間不足!',
         uploadText: '點按或者拖移上傳',
         resTips: 'HiTeach生成的教材,只支援HTEX格式',
-        space: '可用空間:',
+        space: '剩余空間:',
         calcing: '計算中…',
         blobFull: '(已滿)',
         otherType: '其他',

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

@@ -8,7 +8,7 @@ const NO_ACCESS_API = [
         '/core/system-info',
         '/oauth2/login',
         '/oauth2/token',
-        '/service/sandsms/pin',
+        '/core/sendsms/pin',
         '/service/sandmail/pin',
         '/teacher/init/get-school-list',
         '/student/login',

+ 8 - 6
TEAMModelOS/ClientApp/src/api/index.js

@@ -182,6 +182,7 @@ export default {
     },
 
     /**
+     * (仅处理邮箱验证码,手机验证码独立分开处理)
      * 發送驗證簡訊
      * @param {String} applyType - 寄信類型(email, phone)
      * @param {String} to - 寄信位置
@@ -200,12 +201,13 @@ export default {
                 'HasUser': item.hasUser,
             }
 
-            if (item.applyType == 'phone') {
-                data.country = item.country.toString()
-                url += '/service/sandsms/pin'
-            } else {
-                url += '/service/sandmail/pin'
-            }
+            // if (item.applyType == 'phone') {
+            //     data.country = item.country.toString()
+            //     url += '/service/sandsms/pin'
+            // } else {
+            //     url += '/service/sandmail/pin'
+            // }
+            url += '/service/sandmail/pin'
             corePost(url, data).then(res => {
                 resolve(res)
             }, err => {

+ 5 - 2
TEAMModelOS/ClientApp/src/api/service.js

@@ -13,8 +13,11 @@ export default {
         return post(`${host}/oauth2/profile`, data)
     },
     /* 发送短信验证码 */
-    sandMsgCode: function (host,data) {
-        return post(`${host}/service/sandsms/pin`, data)
+    // sendMsgCode: function (host,data) {
+    //     return post(`${host}/service/sandsms/pin`, data)
+    // },
+    sendMsgCode: function (data) {
+        return post(`/core/sendsms/pin`, data)
     },
     /* 发送邮件验证码 */
     sandMailCode: function (host,data) {

+ 922 - 0
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperTest.less

@@ -0,0 +1,922 @@
+.lesson-test-pop {
+    position: fixed;
+    width: 100%;
+    /* height: 100%; */
+    overflow-y: scroll;
+    overflow-x: hidden;
+    background-color: #f9f9f9;
+    z-index: 10;
+    top: 0px;
+    left: 0px;
+    font-size: 18px;
+
+    .testTitle {
+        background-color: #ffffff;
+        color: #24b880;
+        font-size: 20px;
+        padding: 5px 15px;
+        position: relative;
+        width: 100%;
+        height: 38px;
+        border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+        display: flex;
+        justify-content: space-between;
+
+        .countDown {
+            text-align: center;
+            margin-left: 40%;
+        }
+
+        .ques-filter {
+            position: absolute;
+            right: 10%;
+            top: 3px;
+
+            .filter-text {
+                padding: 2px 7px;
+                border-radius: 4px;
+                color: #515a6e;
+                /* font-size: 14px; */
+                font-weight: bolder;
+                background-color: rgba(0, 0, 0, 0.1);
+            }
+
+            .filter-only {
+                color: #00ad6c;
+            }
+        }
+    }
+
+    .warmMessage {
+        z-index: 999;
+        font-size: 14px;
+
+        h3 {
+            font-size: 16px;
+        }
+    }
+
+    .logoutIcon {
+        color: gray;
+        cursor: pointer;
+        padding-right: 10px;
+    }
+
+    .testTitleText {
+        font-weight: 900;
+    }
+
+    .myProgressBar {
+        position: fixed;
+        right: 14%;
+        top: -5px;
+        width: 10%;
+    }
+
+    .myTestProgresstitle {
+        /* right: -69%; */
+        z-index: 3;
+        top: -25px;
+        position: relative;
+        font-weight: 900;
+        font-size: 10pt;
+        color: gray;
+    }
+
+    .submitBtn {
+        outline: none;
+        min-width: 100px;
+        padding: 0 10px;
+        position: absolute;
+        height: 24px;
+        right: 36px;
+        font-size: 14px;
+        top: 6px;
+        cursor: pointer;
+        border: 1px solid #979797;
+        border-radius: 15px;
+        background: transparent;
+        color: #6d7278;
+        z-index: 4;
+    }
+
+    .messageCardIcon {
+        font-size: 40px;
+        margin: 10px auto;
+    }
+
+    .logoutComfirmIcon {
+        font-size: 60px;
+        margin: 10px auto;
+    }
+
+    .messageCardBtn {
+        width: 35%;
+        margin: 30px 5px 10px 5px;
+        padding: 5px;
+        border: none;
+        color: #6a6565;
+        font-weight: 500;
+        border-radius: 4px;
+        background: rgba(0, 0, 0, 0.1);
+        outline: none;
+        cursor: pointer;
+
+        &:hover {
+            background: rgba(0, 0, 0, 0.2);
+        }
+    }
+
+    .handOnImg {
+        font-size: 60px;
+        margin: 12px 25px 5px 35px;
+    }
+
+    .questionArea {
+        position: relative;
+        display: block;
+        overflow: auto;
+        height: 96vh;
+        background-color: #ffffff;
+        left: 0px;
+        /* font-size: 20px; */
+
+        .small-view {
+            display: none;
+            text-align: center;
+        }
+    }
+
+    .questionContent {
+        padding: 5px 50px;
+    }
+
+    .questionType {
+        border: 2px solid #979797;
+        color: #6d7278;
+        border-radius: 5px;
+        text-align: center;
+        min-width: 100px;
+        margin-bottom: 5px;
+        /*margin-top: 20px;*/
+        display: inline-block;
+        padding: 0 10px;
+    }
+
+    .questioDes {
+        margin-top: 20px;
+        font-weight: 900;
+        /* font-size: 20px; */
+        /* height: 500px; */
+        overflow-y: scroll;
+        /* border: 2px solid red; */
+    }
+
+    .que-item {
+        display: flex;
+        width: 100%;
+        /* max-height: 450px; */
+        /* overflow-y: scroll; */
+        /* border: 1px solid blue; */
+    }
+
+    .que-items {
+        width: 100%;
+        /* border: 1px solid lightblue; */
+        /* max-height: 200px; */
+        margin-left: 10px;
+        /*max-height: 200px;*/
+        /* overflow-y: scroll; */
+    }
+
+    .questionNo {
+        margin-bottom: 5px;
+        font-weight: 900;
+        width: 10%;
+        display: inline-block;
+        vertical-align: top;
+    }
+
+    .answers {
+        width: 100%;
+        margin-top: 20px;
+    }
+
+    .answers-box {
+        /* max-height: 400px; */
+        width: 100%;
+        /* height: 400px; */
+        overflow-y: scroll;
+    }
+
+    .select-box {
+        width: 100%;
+        display: flex;
+        flex-wrap: wrap;
+    }
+
+    .questionDesImg {
+        margin-top: 40px;
+    }
+
+    .hintIcon {
+        font-size: 30px;
+        cursor: pointer;
+    }
+
+    .hintwrap {
+        position: absolute;
+        right: 40px;
+        top: 20px;
+
+        &:hover {
+            color: #24b880 !important;
+
+            &+.hint-content {
+                display: block;
+                position: absolute;
+                right: 80px;
+                top: 30px;
+                background-color: #fff;
+                border-radius: 4px;
+                padding: 20px;
+                padding-bottom: 25px;
+                box-shadow: 1px 2px 10px rgba(0, 0, 0, 0.1);
+
+                .triangle {
+                    position: absolute;
+                    right: -8px;
+                    top: 5px;
+                    width: 0;
+                    height: 0;
+                    border-style: solid;
+                    border-width: 6px 0 6px 8.7px;
+                    border-color: transparent transparent transparent #ffffff;
+                }
+
+                .hint-content-list {
+                    font-weight: bolder;
+
+                    .hint-list-item {
+                        display: inline-block;
+                        list-style: none;
+                        background-color: #64ae16;
+                        border-radius: 4px;
+                        color: white;
+                        margin-right: 4px;
+                        margin-top: 6px;
+                        padding: 5px 10px;
+                    }
+                }
+            }
+        }
+    }
+
+    .hint-content {
+        display: none;
+    }
+
+    .ansArea {
+        position: fixed;
+        height: 100%;
+        overflow: auto;
+        background-color: #fafafa;
+        box-shadow: -5px 2px 13px rgba(0, 0, 0, 0.1);
+        right: 0px;
+        z-index: 2;
+
+        .color-type {
+            display: flex;
+            align-items: center;
+            margin-right: 10px;
+        }
+
+        .activeBg {
+            height: 12px;
+            width: 20px;
+            border-radius: 5px;
+            display: inline-block;
+            margin-right: 5px;
+        }
+    }
+
+    .pageCtl2 {
+        position: relative;
+        /* float: left;
+        right: -10px; */
+        margin-top: 20px;
+        /* margin-bottom: 100px; */
+        right: 10px;
+        font-weight: 900;
+        display: flex;
+        flex-wrap: wrap;
+        font-size: 14px;
+
+        button {
+            text-align: center;
+            background: rgba(0, 0, 0, 0.1);
+            height: 30px;
+            width: auto;
+            padding: 5px 10px;
+            margin: 0 10px 10px 10px;
+            color: #6a6565;
+            border-radius: 4px;
+            border: none;
+            cursor: pointer;
+            outline: none;
+            font-weight: 900;
+
+            &:nth-of-type(2) {
+                margin: 0;
+            }
+        }
+
+        .disable {
+            background: #EEEEEE;
+            color: #ABABAB;
+            cursor: not-allowed;
+        }
+    }
+
+    .pageCtl2 .hintClick,
+    .submitBtn.hintClick {
+        background-color: #64ae16 !important;
+        color: white;
+        border: none;
+    }
+
+    .analysis {
+        margin-top: 20px;
+        z-index: 2;
+        /* font-size: 20px; */
+        transition: opacity 0.2s ease-in;
+        opacity: 0;
+        margin-bottom: 50px;
+
+        .item-explain {
+            margin-top: 10px;
+            cursor: pointer;
+            font-size: 17px;
+
+            .explain-title {
+                width: 12%;
+                max-width: 130px;
+                min-width: 100px;
+                display: inline-block;
+                color: #00ad6c;
+            }
+
+            .item-explain-details {
+                vertical-align: top;
+                display: inline-block;
+                width: calc(100% - 130px);
+
+                img {
+                    max-width: 35% !important;
+                }
+            }
+
+            .item-explain-details-repair {
+                vertical-align: top;
+                display: inline-block;
+                width: calc(100% - 130px);
+
+                .repair-link-wrap-item-box {
+                    display: flex;
+                    position: relative;
+                    /* // background-color: #e3e3e3; */
+                    border-radius: 5px;
+                    padding: 10px 0;
+                    font-size: 14px;
+
+                    &:hover {
+                        background-color: #ebe9e9;
+                    }
+
+                    .file-icon>img {
+                        width: 45px !important;
+                    }
+                }
+
+                .file-info {
+                    margin-left: 10px;
+
+                    .file-name {
+                        font-weight: bold;
+                        margin-bottom: 5px;
+                    }
+
+                    span {
+                        color: #16a3b5;
+                        margin-right: 15px;
+                        cursor: pointer;
+                    }
+                }
+            }
+        }
+    }
+
+    .active {
+        opacity: 1;
+    }
+
+    .checkAnswer {
+        margin-top: 30px;
+        /* min-height: 600px; */
+    }
+
+    input[type="text"] {
+        width: 100%;
+        font-size: 16px;
+
+        &:focus {
+            outline: none;
+            border-bottom: 2px solid #24b880;
+        }
+    }
+
+    .testBtn {
+        display: block;
+        /* margin: 10px auto; */
+        width: 40%;
+        margin-left: 30px;
+        margin-bottom: 25px;
+
+        &:hover {
+            cursor: pointer;
+        }
+
+        input[type="checkbox"],
+        input[type="radio"] {
+            visibility: hidden;
+            margin-left: -10px;
+
+            &:checked~.testbg {
+                color: #fff;
+                font-weight: bolder;
+                background-color: #24b880 !important;
+                border: none;
+            }
+        }
+    }
+
+    .testButn {
+        .ivu-radio-wrapper-checked {
+            background: #24b880;
+        }
+
+        .ivu-radio {
+            display: none;
+        }
+    }
+
+    .testbg {
+        z-index: 2;
+        text-align: left;
+        font-weight: bolder;
+        background-color: #fff;
+        position: relative;
+        height: auto;
+        padding: 15px 20px;
+        border-radius: 5px;
+        border: 1px solid #c5c5c5;
+    }
+
+    .yesNoBtn {
+        display: inline-block;
+        width: 105px;
+        height: 100px;
+        margin: 10px;
+        margin-bottom: 40px;
+
+        .testbg {
+            text-align: center !important;
+            position: relative;
+            border-radius: 50%;
+            font-weight: 900;
+            font-size: 50px;
+        }
+    }
+
+    .testbgImg {
+        width: 100%;
+    }
+
+    .ansSheet {
+        border: 2px solid rgba(117, 117, 117, 0.726);
+        border-collapse: collapse;
+        font-weight: 500;
+
+        .ansSheetQno {
+            border: 2px solid rgba(117, 117, 117, 0.726);
+            border-collapse: collapse;
+            font-weight: 500;
+            cursor: pointer;
+            /* font-size: 14px; */
+            padding: 2px 10px;
+            height: 30px;
+
+            &:hover {
+                color: #24b880;
+                /* font-size: 16px; */
+                padding: 2px 10px;
+                background-color: #ececec;
+            }
+        }
+
+        td {
+            border: 2px solid rgba(117, 117, 117, 0.726);
+            border-collapse: collapse;
+            font-weight: 500;
+            padding: 2px 5px;
+        }
+
+        .anstext {
+            text-align: center;
+            font-weight: 900;
+            color: #000000;
+        }
+
+        .rightAnstitle {
+            text-align: center;
+            font-weight: 900;
+        }
+
+        .rightAnstext {
+            text-align: center;
+            font-weight: 900;
+            color: #24b880;
+        }
+
+        .wrong-ans {
+            color: red;
+        }
+    }
+
+    .md-ansSheetGroup {
+        margin-top: 5%;
+        overflow-x: scroll;
+        width: 100%;
+    }
+
+    .sm-ansSheetGroup {
+        margin-left: 7%;
+        margin-bottom: 10%;
+    }
+}
+
+.que-box {
+    margin-top: 10px;
+    width: 100%;
+    max-height: 500px;
+    overflow-y: scroll;
+    display: flex;
+    flex-wrap: wrap;
+}
+
+.ans-box {
+    border: 1px solid rgba(117, 117, 117, 0.726);
+    margin-left: 5px;
+    margin-bottom: 10px;
+    cursor: pointer;
+    width: 60px;
+    font-size: 14px;
+    border-radius: 5px;
+    text-align: center;
+    padding-top: 3px;
+    height: 30px;
+
+    &:hover {
+        color: #24b880;
+        /* font-size: 16px; */
+        padding: 2px 5px;
+    }
+}
+
+.has-right-ans {
+    background-color: #92d2a9;
+}
+
+.has-wrong-ans {
+    background-color: #d68f8f;
+}
+
+.has-no-ans {
+    background-color: #b7b7b7;
+}
+
+.has-ans {
+    border: 1px solid rgba(117, 117, 117, 0.726);
+    margin-left: 5px;
+    margin-bottom: 10px;
+    cursor: pointer;
+    width: 60px;
+    font-size: 14px;
+    border-radius: 5px;
+    text-align: center;
+    padding-top: 3px;
+    height: 30px;
+    background-color: #d4ede1;
+
+    &:hover {
+        color: #24b880;
+        font-size: 16px;
+        padding: 2px 5px;
+    }
+}
+
+.select-item {
+    color: white;
+    background-color: #00ac60 !important;
+}
+
+.wrong-item {
+    color: white;
+    background-color: #ff5508 !important;
+}
+
+.compose-box {
+    width: 100%;
+    border-radius: 6px;
+}
+
+.compose-item {
+    display: flex;
+    margin-left: 10px;
+    /* max-height: 300px; */
+    overflow-y: scroll;
+}
+
+.compose-content {
+    /* margin-top: 30px; */
+    width: 100%;
+    /* height: 200px; */
+    z-index: 0;
+}
+
+.que-content {
+    display: flex;
+    margin-top: 20px;
+    width: 100%;
+}
+
+
+@media screen and (min-width: 992px) {
+    .lesson-test-pop {
+        .sm-ansSheetGroup {
+            display: none;
+        }
+
+        .ansArea .countDown {
+            display: none;
+        }
+    }
+}
+
+@media screen and (max-width:992px) {
+    .lesson-test-pop {
+        .md-ansSheetGroup {
+            display: none;
+        }
+
+        .testTitle .countDown {
+            display: none;
+        }
+
+        .ansArea .countDown {
+            margin-top: 10px;
+            color: #24b880;
+        }
+    }
+}
+
+
+@media screen and (max-width: 1366px) {
+
+    /* .lesson-test-pop{
+      font-size: 18px;
+    } */
+    .lesson-test-pop {
+        .testTitle {
+            .ques-filter {
+                right: 15%;
+
+                .filter-text {
+                    right: 20%;
+                }
+            }
+        }
+
+        .myProgressBar {
+            right: 20%;
+        }
+
+        .myTestProgresstitle {
+            left: -40%;
+        }
+
+        .myTestProgressNumEn {
+            display: none;
+        }
+
+        .myTestProgressNum {
+            left: 75%;
+        }
+
+        .myTestProgressNumEn {
+            display: none;
+        }
+
+        .checkAnswer {
+            margin-top: 30px;
+            /* min-height: 300px; */
+        }
+
+        .questionContent {
+            padding: 20px 20px;
+        }
+
+        .questionArea {
+            position: relative;
+            display: block;
+            overflow: auto;
+            height: 97vh;
+            background-color: #ffffff;
+            left: 0px;
+        }
+
+        .que-box {
+            max-height: 760px;
+        }
+
+        .pageCtl2 {
+            position: relative;
+            /* float: right;
+            display: inline-flex; */
+            width: 100%;
+            font-weight: 900;
+            display: flex;
+            flex-wrap: wrap;
+
+            button:nth-of-type(2) {
+                margin: 0 10px 10px 10px;
+            }
+        }
+    }
+
+    .complete-content {
+        width: 35%;
+        margin-top: 50px;
+    }
+
+    .compose-box {
+        /* background-color: #24b880;
+        color: white; */
+        width: 95%;
+        border-radius: 6px;
+    }
+
+    .compose-item {
+        display: flex;
+        margin-left: 10px;
+        /* max-height: 200px; */
+        overflow-y: scroll;
+    }
+
+    .compose-content {
+        margin-top: 30px;
+        width: 95%;
+        /* height: 200px; */
+        margin-bottom: 30px;
+        z-index: 0;
+    }
+
+    .que-content {
+        display: flex;
+        margin-top: 20px;
+        width: 95%;
+    }
+
+    .que-item {
+        width: 100%;
+        max-height: 200px;
+        overflow-y: scroll;
+    }
+}
+
+
+@media screen and (max-width: 1024px) {
+    .lesson-test-pop {
+        font-size: 14px;
+
+        .ans-box {
+            font-size: 13px;
+        }
+
+        .pageCtl2 {
+            font-size: 12px;
+        }
+
+        .que-box {
+            max-height: 450px;
+        }
+    }
+}
+
+
+@media screen and (max-width: 991px) {
+    .lesson-test-pop {
+        .testTitle .ques-filter {
+            right: 25%;
+        }
+
+        .testTitleText .testTitleTextContent {
+            display: none;
+        }
+
+        .myProgressBar {
+            right: 23%;
+        }
+
+        .myTestProgresstitle {
+            left: -60%;
+        }
+
+        .myTestProgressNum {
+            left: 65%;
+        }
+    }
+
+    /* .lesson-test-pop .questionArea {
+        height: auto;
+    } */
+
+    /* .lesson-test-pop .ansArea {
+        position: relative;
+    } */
+}
+
+
+@media screen and (max-width: 768px) {
+    .lesson-test-pop {
+        font-size: 14px;
+
+        .testTitle .ques-filter {
+            left: 38%;
+        }
+
+        .ansSheet {
+            /* font-size: 10px; */
+            width: 80%;
+        }
+
+        .questionDesImg {
+            width: 40%;
+            margin-top: 20px;
+        }
+
+        .myProgressBar {
+            right: 30%;
+        }
+
+        /* .questionArea {
+            font-size: 18px;
+        } */
+
+        /* .questioDes {
+            overflow-y: scroll;
+            font-size: 18px;
+        } */
+    }
+}
+
+
+@media screen and (max-width: 680px) {
+    .lesson-test-pop .myProgressBar {
+        display: none;
+    }
+}
+
+
+@media screen and (max-width: 576px) {
+    .lesson-test-pop {
+        .questionArea .small-view {
+            display: block;
+            margin-top: 10px;
+        }
+
+        .pageCtl2 {
+            margin-top: 10px;
+            justify-content: center;
+        }
+
+        .ansArea {
+            width: 100%;
+        }
+
+        .analysis .item-explain .explain-title {
+            width: 100%;
+            max-width: none;
+        }
+    }
+}

+ 6 - 2
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperTest.vue

@@ -1249,7 +1249,7 @@
 </script>
 
 <style scoped>
-    @import "~@/assets/student-web/component_styles/paper-test.css";
+    /* @import "~@/assets/student-web/component_styles/paper-test.css"; */
     .que-item /deep/ table {
         border-top: 1px solid #ccc;
         border-left: 1px solid #ccc;
@@ -1315,4 +1315,8 @@
         height:100%;
         overflow:scroll;
     }
-</style>
+</style>
+
+<style lang="less" scoped>
+@import "./PaperTest.less";
+</style>

+ 40 - 0
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperView-style.less

@@ -0,0 +1,40 @@
+.lesson-test {
+    /* height: 90vh; */
+    padding-top: calc(1% + 15px);
+
+    .ivu-tabs-nav {
+        float: left !important;
+        position: relative;
+        text-align: center;
+    }
+
+    .ivu-tabs-tab {
+        text-align: center !important;
+        font-size: 24px;
+        margin: 0px 10px;
+        width: auto;
+
+        &:hover {
+            text-align: center !important;
+            color: #24b880 !important;
+        }
+    }
+
+    .ivu-tabs-tab.ivu-tabs-tab-active.ivu-tabs-tab-focused {
+        color: #24b880 !important;
+        font-weight: bolder;
+        border-bottom: 5px solid #24b880 !important;
+        margin: 0px 10px;
+        width: auto;
+    }
+
+    .ivu-tabs-ink-bar {
+        height: 0px;
+    }
+
+    .ivu-tabs-tabpane {
+        margin-top: -16px;
+        overflow-x: hidden;
+        height: auto;
+    }
+}

+ 149 - 0
TEAMModelOS/ClientApp/src/components/student-web/EventView/EventContentTypeTemplate/PaperView.less

@@ -0,0 +1,149 @@
+.lesson-test {
+    .paper-item {
+        border: 1px solid #919191;
+        font-weight: bolder;
+        height: 40px;
+        border-radius: 50px;
+        padding: 5px 2px;
+        margin-top: 10px;
+        /* margin-left: 20px; */
+        cursor: pointer;
+        display: inline-flex;
+        padding-right: 15px;
+    }
+
+    .paper-item-school {
+        font-weight: bolder;
+        height: 50px;
+        line-height: 40px;
+        padding: 5px 2px;
+        margin-top: 10px;
+        margin-right: 20px;
+        cursor: pointer;
+        display: inline-flex;
+        font-size: 16px;
+
+        &>span:first-child {
+            padding: 0 15px;
+            background-color: #ababab;
+            color: #fff;
+            border-radius: 5px 0 0 5px;
+        }
+
+        &>span:nth-of-type(2) {
+            padding: 0 15px;
+            border: 1px solid #ababab;
+            border-radius: 0 5px 5px 0;
+        }
+    }
+
+    .paper-choose {
+        background-color: #00ad6c;
+        color: white;
+        border: none;
+    }
+
+    .paper-choose-school {
+        &>span:first-child {
+            background-color: #00ad6c;
+        }
+
+        &>span:nth-of-type(2) {
+            border-color: #00ad6c;
+        }
+    }
+
+    .top-icon {
+        position: fixed;
+        right: 50px;
+        bottom: 20px;
+        width: 50px;
+        height: 50px;
+        line-height: 55px;
+        text-align: center;
+        background: #ccc;
+        border-radius: 5px;
+        cursor: pointer;
+    }
+}
+
+.paper-tasks {
+    border: 1px solid #919191;
+    background-color: white;
+    font-weight: bolder;
+    width: 180px;
+    height: 40px;
+    border-radius: 50px;
+    padding: 5px 2px;
+    margin-top: 20px;
+    margin-right: 20px;
+    position: relative;
+    display: inline-block;
+
+    &:hover {
+        cursor: pointer;
+    }
+
+    .title {
+        display: flex;
+    }
+
+    Icon {
+        padding-right: 10px;
+    }
+
+    li {
+        display: inline-block;
+        list-style-type: none;
+    }
+
+    .title-icon {
+        height: 20px;
+        width: 20px;
+        position: relative;
+        right: 5px;
+        top: 2px;
+    }
+}
+
+.item-box {
+    display: -webkit-flex;
+    display: inline-table;
+    width: 100%;
+
+    .title-icon {
+        margin-left: 10px;
+        margin-top: 5px;
+        height: 20px;
+        width: 20px;
+    }
+
+    .finished {
+        background: #03c203;
+        height: 20px;
+        width: 20px;
+        margin-left: 10px;
+        margin-top: 5px;
+        margin-right: 10px;
+        border-radius: 10px;
+        color: #ffffff;
+    }
+
+    .unfinished {
+        margin-top: 5px;
+        margin-left: 10px;
+        margin-right: 10px;
+        background: #adadad;
+        height: 20px;
+        width: 20px;
+        border-radius: 10px;
+        color: #ffffff;
+    }
+}
+
+
+@media screen and (max-width: 991px) {
+    .paper-tasks {
+        width: 100%;
+    }
+}

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

@@ -410,8 +410,16 @@
 </script>
 
 <style scoped>
-    @import "~@/assets/student-web/component_styles/paper-view.css";
+    /* @import "~@/assets/student-web/component_styles/paper-view.css"; */
     .student-score ::-webkit-scrollbar {
         width: 0px;
-    } 
+    }
+</style>
+
+<style lang="less" scoped>
+@import "./PaperView.less";
+</style>
+
+<style lang="less">
+@import "./PaperView-style.less";
 </style>

+ 14 - 7
TEAMModelOS/ClientApp/src/components/student-web/SettingView/Setting.vue

@@ -112,11 +112,13 @@
                     <Form ref="mobile" :model="newPhoneNum" :rules="mobileValidate" :label-width="80" label-position="right">
                         <FormItem prop="newPhoneNum" class="custom-radirs">
                             <span slot="label" class="form-label">{{ $t('studentWeb.tmManagement.phone') }}</span>
-                            <Select v-model="newPhoneNum.area" style="width:170px">
+                            <!-- <Select v-model="newPhoneNum.area" style="width:170px">
                                 <Option v-for="item in areaList" :value="item.code" :key="item.area">
                                     {{ `+${item.code}(${item.name})` }}
                                 </Option>
-                            </Select>
+                            </Select> -->
+                            <!-- 改用下面组件,根据ip地址判断区号 -->
+                            <CountryCode v-model="newPhoneNum.area" style="width:130px;display:inline-block" />
                             <Input v-special-char v-model="newPhoneNum.number" class="edit-pw-input" style="width:280px" />
                         </FormItem>
                         <FormItem prop="code" class="custom-radirs" v-if="!student">
@@ -159,9 +161,13 @@
 import BlobTool from '@/utils/blobTool.js'
 import jwtDecode from 'jwt-decode'
 import countryCode from '@/static/countryCodeData.js'
+import CountryCode from '@/components/public/countryCode/Index.vue'
 import { mapState } from 'vuex'
 export default {
     name: "",
+    components: {
+        CountryCode
+    },
     data () {
         const confirm = (rule, value, callback) => {
             if (value !== this.pw.newPw) {
@@ -254,7 +260,7 @@ export default {
         this.MyName = this.$t("studentWeb.settingView.title")
         this.$emit("onNavNo", this.MyNo)
         this.$emit("onNavName", this.MyName)
-        let curLocal = localStorage.getItem('local')
+        /* let curLocal = localStorage.getItem('local')
         let attr = 'CountryEn'
         if (curLocal.includes('cn') || curLocal.includes('CN')) {
             attr = 'CountryCn'
@@ -274,7 +280,7 @@ export default {
         this.areaList.sort((a, b) => {
             return a.code - b.code
         })
-        console.log(this.areaList);
+        console.log(this.areaList); */
         // 醍摩豆登陆
         if (this.userInfo.scope === "tmduser") {
             this.getIdInfo()
@@ -343,14 +349,15 @@ export default {
         sendMsgCode() {
             console.log("开始发送短信");
             let srvAdr = this.config.srvAdr
-            let host = srvAdr == 'Global' ? this.config.Global.coreAPIUrl : this.config.China.coreAPIUrl
+            // let host = srvAdr == 'Global' ? this.config.Global.coreAPIUrl : this.config.China.coreAPIUrl
             let params = {
-                country: this.newPhoneNum.area + '',
+                // country: this.newPhoneNum.area + '',
+                area: this.newPhoneNum.area + '',
                 to: this.newPhoneNum.number,
                 lang: localStorage.getItem('local'),
                 HasUser: false
             }
-            this.$api.service.sandMsgCode(host, params).then(
+            this.$api.service.sendMsgCode(params).then(
                 res => {
                     if (!res.error) {
                         this.$Message.success(this.$t('studentWeb.tmManagement.message.msgOk'))

+ 24 - 4
TEAMModelOS/ClientApp/src/view/auth/Serial.vue

@@ -116,9 +116,15 @@
                                             <span class="serial-date" :style="{color:item.expired ? '#ed4014':''}">
                                                 {{$jsFn.dateFormat(item.endDate)}}
                                             </span>
-                                            <span v-if="item.expired" class="has-expired">
+                                            <span v-if="item.es == 2" class="has-expired">
                                                 {{$t('auth.hadExp')}}
                                             </span>
+                                            <span v-else-if="item.es == 1" class="has-expired">
+                                                {{$t('auth.unuse')}}
+                                            </span>
+                                            <span v-else-if="item.es == 4" class="has-expired">
+                                                {{$t('auth.forever')}}
+                                            </span>
                                             <span class="use-text">
                                                 {{$t('auth.useInfo')}}
                                             </span>
@@ -510,7 +516,7 @@ export default {
                             flag = !item.expired && item.deviceMax > item.deviceBound.length
                             break
                         case 'filter4': //过期
-                            flag = item.expired
+                            flag = item.es == 2
                             break
                         default:
                             break
@@ -537,9 +543,23 @@ export default {
         formatData() {
             let data = this._.cloneDeep(this.serial)
             let timestamp = Date.now()
-            console.log(timestamp)
             data.forEach(item => {
-                item.expired = item.endDate * 1000 < timestamp
+                //未啟用
+                if (item.regDate == 0 || item.expireStatus == "") {
+                    item.es = 1
+                }
+                //已到期
+                else if (item.expireStatus == "F" || item.endDate * 1000 < timestamp) {
+                    item.es = 2
+                }
+                //可使用
+                else if (item.expireStatus == "S" || item.expireStatus == "A") {
+                    item.es = 3
+                }
+                //永久授權
+                else if (item.expireStatus == "A" && item.endDate == 0) {
+                    item.es = 4
+                }
                 item.deviceBound.forEach(d => {
                     d.serial = item.serial
                 })

+ 38 - 5
TEAMModelOS/ClientApp/src/view/classrecord/ClassRecord.vue

@@ -19,7 +19,7 @@
             </div>
             <div class="action-wrap">
                 <!-- 下载学生作品 -->
-                <span class="e-note-tag" @click="downloadStuWork">
+                <span v-show="taskData.length" class="e-note-tag" @click="downloadStuWork">
                     <Icon type="md-download" />
                     {{$t('cusMgt.rcd.dlStuWrk')}}
                 </span>
@@ -152,6 +152,8 @@ import Receive from './eventchart/Receive.vue'
 import DataCount from './eventchart/DataCount.vue'
 import CountTo from 'vue-count-to'
 import BlobTool from '@/utils/blobTool.js'
+import FileSaver from "file-saver";
+import JSZip from "jszip";
 export default {
     components: {
         PopQues, Pick, Push, Receive, DataCount, CountTo, Buzr, Exam, WrkCmp
@@ -248,11 +250,42 @@ export default {
             }
         },
         // 下载学生作品
-        downloadStuWork() {
+        async downloadStuWork() {
+            // 需要优化原始目录结构
+            // const containerClient = BlobTool.CreateBlobTool(this.recordInfo.scope)
+            // containerClient.downloadFolder(`records/${this.recordInfo.id}/Clients`, {
+            //     fileName: this.$t('cusMgt.rcd.stuWrk'),
+            //     exclude: 'taskList_localstorage'
+            // })
+
+
             const containerClient = BlobTool.CreateBlobTool(this.recordInfo.scope)
-            containerClient.downloadFolder(`records/${this.recordInfo.id}/Clients`, {
-                fileName: this.$t('cusMgt.rcd.stuWrk'),
-                exclude: 'taskList_localstorage'
+            const zip = new JSZip()
+            for (const task of this.taskData) {
+                if (task.clientWorks && task.clientWorks.length) {
+                    for (const student of task.clientWorks) {
+                        if (student.blobFiles && student.blobFiles.length) {
+                            for (const file of student.blobFiles) {
+                                let blobPath = `records/${this.recordInfo.id}${file}`
+                                let blobClient = containerClient.containerClient.getBlockBlobClient(blobPath)
+                                const dwRes = await blobClient.download()
+                                const blobRes = await dwRes.blobBody
+                                const filePath = `${task.jobName}/${file.substring(file.lastIndexOf("/"))}`
+                                zip.file(filePath, blobRes, {
+                                    binary: true
+                                }) // 逐个添加文件 
+                            }
+                        }
+                    }
+                }
+            }
+            zip.generateAsync({
+                type: "blob"
+            }).then(content => {
+                // 生成二进制流
+                FileSaver.saveAs(content, this.$t('cusMgt.rcd.stuWrk') + ".zip"); // 利用file-saver保存文件
+            }).catch(err => {
+                console.log(err);
             })
         },
         //下载统计表格

+ 48 - 16
TEAMModelOS/ClientApp/src/view/forgotPw/Index.vue

@@ -225,24 +225,56 @@ export default {
                                 hasUser: true,
                                 country: this.cCode
                             }
-                            this.$api.SendPinCode(data).then(res => {
-                                let errorFlag = false
-                                this.loading = false
-                                if (res) {
-                                    errorFlag = true
-                                    this.$Message.warning({
-                                        content: this.$t('error.coreApi.error1.' + this.applyType),
-                                        duration: 7,
-                                        closable: true
-                                    })
+                            if (data.applyType == 'phone') {
+                                let params = {
+                                    area: data.country,
+                                    to: data.to,
+                                    lang: this.lang,
+                                    HasUser: true
                                 }
+                                this.$api.service.sendMsgCode(params).then(
+                                    res => {
+                                        let errorFlag = false
+                                        this.loading = false
+                                        // if (res) {
+                                        //     errorFlag = true
+                                        //     this.$Message.warning({
+                                        //         content: this.$t('error.coreApi.error1.' + this.applyType),
+                                        //         duration: 7,
+                                        //         closable: true
+                                        //     })
+                                        // }
 
-                                if (!errorFlag) {
-                                    this.restPWStep = 2
-                                    this.countdown = true
-                                    this.reciprocal()
-                                }
-                            })
+                                        if (!errorFlag) {
+                                            this.restPWStep = 2
+                                            this.countdown = true
+                                            this.reciprocal()
+                                        }
+                                    },
+                                    err => {
+                                        this.$Message.error(this.$t('user.msgErr'))
+                                    }
+                                )
+                            } else {
+                                this.$api.SendPinCode(data).then(res => {
+                                    let errorFlag = false
+                                    this.loading = false
+                                    if (res) {
+                                        errorFlag = true
+                                        this.$Message.warning({
+                                            content: this.$t('error.coreApi.error1.' + this.applyType),
+                                            duration: 7,
+                                            closable: true
+                                        })
+                                    }
+
+                                    if (!errorFlag) {
+                                        this.restPWStep = 2
+                                        this.countdown = true
+                                        this.reciprocal()
+                                    }
+                                })
+                            }
                             break;
                         case 'resPwForm':
                             data = {

+ 10 - 2
TEAMModelOS/ClientApp/src/view/jyzx/HourDetail.vue

@@ -8,11 +8,11 @@
                 <p>
                     {{ hourInfo.name }}:
                     <span class="detail-hour">{{ hourInfo.value }}</span> / {{ hourInfo.total }}
-                    <Tooltip :max-width="400" transfer v-if="hourInfo.name === '认证材料'">
+                    <Tooltip :max-width="400" transfer v-if="hourInfo.name === $t('jyzx.homePage.application')">
                         <Icon type="ios-alert" size="15" />
                         <div slot="content">
                             <div>
-                                认证材料必须全部通过才能获得学时
+                                {{ $t('jyzx.homePage.message5') }}
                             </div>
                         </div>
                     </Tooltip>
@@ -20,6 +20,9 @@
                 <span v-if="hourInfo.minuteTotal" style="text-align: center; font-size: 14px;">
                     <span class="detail-hour" style="font-size: 16px;">{{ hourInfo.minute }}</span> / {{ hourInfo.minuteTotal }}(分钟)
                 </span>
+                <span v-if="hourInfo.offlineReport" style="font-size: 14px; color: #3283E3; cursor: pointer;" @click="openReport">
+                    {{ $t('jyzx.homePage.openReport') }}
+                </span>
             </div>
         </div>
     </div>
@@ -56,6 +59,11 @@ export default {
             }
             return 0
         },
+    },
+    methods: {
+        openReport() {
+            window.open('/web/viewer.html?file=' + encodeURIComponent(this.hourInfo.offlineReport.blob + this.$store.state.user.userProfile.osblob_sas))
+        },
     }
 }
 </script>

+ 4 - 2
TEAMModelOS/ClientApp/src/view/jyzx/newHomePage.vue

@@ -170,7 +170,8 @@ export default {
             offlineInfo: {
                 name: this.$t("jyzx.homePage.offline"),
                 total: 10,
-                value: 0
+                value: 0,
+                offlineReport: null,
             },
             verifyInfo: {
                 name: this.$t("jyzx.homePage.application"),
@@ -297,7 +298,8 @@ export default {
                     // 校本研修
                     this.offlineInfo.total = res.setting.offlineTime
                     this.offlineInfo.value = res.teacherTrain.offlineTime > res.setting.offlineTime ? res.setting.offlineTime : res.teacherTrain.offlineTime
-                    
+                    this.offlineInfo.offlineReport = res.teacherTrain.offlineReport
+
                     // 认证材料
                     this.verifyInfo.total = res.setting.submitTime
                     this.verifyInfo.value = res.teacherTrain.currency.submitTime > res.setting.submitTime ? res.setting.submitTime : res.teacherTrain.currency.submitTime

+ 2 - 2
TEAMModelOS/ClientApp/src/view/mgtPlatform/MgtPlatform.vue

@@ -2,7 +2,7 @@
     <div class="mgt-platform-container">
         <vuescroll>
             <div>
-                <p class="block-title" v-if="!isArea && !areaPlatform.links.length">区级资源平台</p>
+                <p class="block-title" v-if="!isArea && !areaPlatform.links.length">{{$t('platform.text1')}}</p>
                 <Draggable :disabled="!isArea" class="platform-list-wrap" handle=".mover" ghost-class="ghost" group="platform" :list="areaPlatform.links" :animation='200' @end="handleDragEnd">
                     <div class="platform-item mover" v-for="(item,index) in areaPlatform.links" :key="item.name" @click="openAreaPlatform(index)">
                         <div :class="['pf-item-box',isArea ? 'handle-hover' : '']">
@@ -32,7 +32,7 @@
                         <p class="add-platform-text">{{$t('platform.addPlatform')}}</p>
                     </div>
                 </Draggable>
-                <p class="block-title" v-if="!isArea">校级资源平台</p>
+                <p class="block-title" v-if="!isArea">{{$t('platform.text2')}}</p>
                 <Draggable v-if="!isArea" class="platform-list-wrap" handle=".mover" ghost-class="ghost" group="platform" :list="schoolPlatform.links" :animation='200' @end="handleDragEnd">
                     <!-- 校级资源平台 -->
                     <div class="platform-item mover" v-for="(item,index) in schoolPlatform.links" :key="item.name" @click="openSchoolPlatform(index)">

+ 1 - 0
TEAMModelOS/ClientApp/src/view/mycourse/record/Record.less

@@ -26,6 +26,7 @@
     color:var(--primary-text-color);
     font-size:16px;
     margin-top: 5px;
+    height: 24px;
 }
 .record-info{
     margin-right: 24px;

+ 48 - 18
TEAMModelOS/ClientApp/src/view/regist/Index.vue

@@ -64,12 +64,12 @@
                 <FormItem class="formItem" prop="pinCode">
                     <Row>
                         <i-col :span="9">
-                        <Button class="radius-right-0" style="font-size:12px; " long type="primary" @click="sendPinCode()" :disabled="countdown || pinCodeSwitch">
-                            {{ sendBtnText }}
-                        </Button>
+                            <Button class="radius-right-0" style="font-size:12px; " long type="primary" @click="sendPinCode()" :disabled="countdown || pinCodeSwitch">
+                                {{ sendBtnText }}
+                            </Button>
                         </i-col>
                         <i-col :span="15">
-                        <Input class="radius-left-0 input-font-size-12" v-model="registForm.pinCode" :placeholder="$t('regist.form.placeholder.pindCode')"></Input>
+                            <Input class="radius-left-0 input-font-size-12" v-model="registForm.pinCode" :placeholder="$t('regist.form.placeholder.pindCode')"></Input>
                         </i-col>
                     </Row>
                 </FormItem>
@@ -358,22 +358,52 @@ export default {
                 hasUser: false,
                 country: this.cCode
             }
-            this.$api.SendPinCode(data).then(res => {
-                let errorFlag = false
-                if (res) {
-                    errorFlag = true
-                    this.$Message.warning({
-                        content: this.$t('error.coreApi.error2.' + this.applyType),
-                        duration: 7,
-                        closable: true
-                    })
+            if (data.applyType == 'phone') {
+                let params = {
+                    area: data.country,
+                    to: data.to,
+                    lang: data.lang,
+                    HasUser: false
                 }
+                this.$api.service.sendMsgCode(params).then(
+                    res => {
+                        let errorFlag = false
+                        if (res.error) {
+                            errorFlag = true
+                            this.$Message.warning({
+                                content: this.$t('error.coreApi.error2.' + this.applyType),
+                                duration: 7,
+                                closable: true
+                            })
+                        }
 
-                if (!errorFlag) {
-                    this.countdown = true
-                    this.reciprocal()
-                }
-            })
+                        if (!errorFlag) {
+                            this.countdown = true
+                            this.reciprocal()
+                        }
+                    },
+                    err => {
+                        this.$Message.error(this.$t('user.msgErr'))
+                    }
+                )
+            } else {
+                this.$api.SendPinCode(data).then(res => {
+                    let errorFlag = false
+                    if (res) {
+                        errorFlag = true
+                        this.$Message.warning({
+                            content: this.$t('error.coreApi.error2.' + this.applyType),
+                            duration: 7,
+                            closable: true
+                        })
+                    }
+
+                    if (!errorFlag) {
+                        this.countdown = true
+                        this.reciprocal()
+                    }
+                })
+            }
         },
         reciprocal() { // 調整倒數時間至可控
             this.verfTime -= 1;

+ 20 - 0
TEAMModelOS/ClientApp/src/view/research-center/ResearchMgt.vue

@@ -29,6 +29,12 @@
           <span :class="['filter-item',curRangeIndex === index ? 'filter-item-active' : '']" v-for="(item,index) in rangeArr" :key="index" @click="onFilterChange('range',index)">{{ item }}</span>
         </div>
       </div>
+      <div class="filter-section">
+        <span class="filter-title">{{ $t('jyzx.online.type') }}</span>
+        <div class="filter-items">
+          <span :class="['filter-item',curStatusIndex === index ? 'filter-item-active' : '']" v-for="(item,index) in statusArr" :key="index" @click="onFilterChange('status',index)">{{ item }}</span>
+        </div>
+      </div>
       <div class="ad-filter">
         <div class="ad-filter-item" v-show="!hideAdFilter && !showRangePicker">
           <!-- <span class="filter-item-title">{{ $t('lessonRecord.tchName') }}</span> -->
@@ -222,7 +228,9 @@ export default {
   data(vm) {
     return {
       rangeArr: [vm.$t('lessonRecord.all'), vm.$t('lessonRecord.week'), vm.$t('lessonRecord.month'), vm.$t('lessonRecord.semester'), vm.$t('lessonRecord.custom')],
+      statusArr: [vm.$t('lessonRecord.all'), vm.$t('lessonRecord.expireStatus'), vm.$t('lessonRecord.deleteStatus')],
       curRangeIndex: 0,
+      curStatusIndex: 0,
       hideAdFilter: true,
       cleanModal: false,
       showRangePicker: false,
@@ -798,6 +806,18 @@ export default {
         }
         console.log(index)
         this.showRangePicker = false
+      } else if (type === 'status') {
+        this.curStatusIndex = index
+        if (index === 1) {
+          this.filterParams.expire = true
+          this.filterParams.is404 = false
+        } else if (index === 2) {
+          this.filterParams.expire = false
+          this.filterParams.is404 = true
+        } else {
+          this.filterParams.is404 = false
+          this.filterParams.expire = false
+        }
       }
       this.doFilter()
     },

+ 5 - 4
TEAMModelOS/ClientApp/src/view/settings/OpenMgmt2.vue

@@ -152,7 +152,7 @@
                                        @on-selection-change="selectionChange1"
                                 >
                                     <template slot-scope="{ row }" slot="name">
-                                        <span>{{ row.name }}</span>
+                                        <span>{{ lang === "zh-cn" ? row.name : (lang === "zh-tw" ? row.tname : row.ename) }}</span>
                                         <Poptip trigger="hover" :content="row.descr" placement="right">
                                             <!-- <Icon type="ios-alert" /> -->
                                             <Icon type="ios-alert-outline" />
@@ -170,7 +170,7 @@
                                        @on-selection-change="selectionChange2"
                                 >
                                     <template slot-scope="{ row }" slot="name">
-                                        <span>{{ row.name }}</span>
+                                        <span>{{ lang === "zh-cn" ? row.name : (lang === "zh-tw" ? row.tname : row.ename) }}</span>
                                         <Poptip trigger="hover" :content="row.descr" placement="right">
                                             <Icon type="ios-alert-outline" />
                                         </Poptip>
@@ -187,7 +187,7 @@
                                        @on-selection-change="selectionChange3"
                                 >
                                     <template slot-scope="{ row }" slot="name">
-                                        <span>{{ row.name }}</span>
+                                        <span>{{ lang === "zh-cn" ? row.name : (lang === "zh-tw" ? row.tname : row.ename) }}</span>
                                         <Poptip trigger="hover" :content="row.descr" placement="right">
                                             <Icon type="ios-alert-outline" />
                                         </Poptip>
@@ -222,7 +222,7 @@ export default {
                 }, */
                 {
                     title: this.$t("settings.apiName"),
-                    key: "name",
+                    // key: name,
                     slot: "name",
                 },
                 {
@@ -248,6 +248,7 @@ export default {
             nowIndex: 0, //当前下标
             isEdit: false, //编辑状态
             isAdd: false, // 编辑/新增
+            lang: localStorage.getItem('local'),
         }
     },
     mounted() {

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

@@ -1267,7 +1267,7 @@ export default {
       if (this.routerScope == 'school') {
         return this.SCHOOL_SPACE * 1024 * 1024 * 1024 - this.teachSpace - (this.sizeInfo.total || 0)
       } else {
-        return this.PRIVATE_SPACE * 1024 * 1024 * 1024
+        return this.PRIVATE_SPACE * 1024 * 1024 * 1024 - (this.sizeInfo?.total || 0)
       }
     },
     /* 判断是否为国际站 */

+ 13 - 7
TEAMModelOS/ClientApp/src/view/user/BandPhone.vue

@@ -151,9 +151,9 @@ export default {
          * 2. 三方登录(微信)绑定
          */
         bandphone() {
+            this.loading = true
             this.$refs['sendForm'].validate((valid) => {
                 if (valid) {
-                    this.loading = true
                     //研修中心账号绑定 TODO 账号绑定操作
                     if (this.routerData.loginType && this.routerData.loginType == 'trainsso') {
                         this.bandTrain()
@@ -163,6 +163,7 @@ export default {
                         this.bandWeChat()
                     }
                 } else {
+                    this.loading = false
                     this.$Message.error(this.$t('login.fullinfo'))
                 }
             })
@@ -196,7 +197,7 @@ export default {
                             phone: this.accFormat
                         }
                         this.bandingID(data)
-                    }else{
+                    } else {
                         this.$Message.error(this.$t('login.phoneerr'))
                     }
                     // 新账号会返回登录信息
@@ -310,12 +311,17 @@ export default {
                         country: this.cCode
                     }
                     try {
-                        let res = await this.$api.SendPinCode(data)
+                        let res = await this.$api.service.sendMsgCode({
+                            area: data.country,
+                            to: data.to,
+                            lang: data.lang,
+                            // HasUser: false
+                        })
                         //账号已存在 需要传hasUser才可能返回2 现在这种方式不会走这个判断
-                        if (res.error === 2) {
-                            data.hasUser = true
-                            res = await this.$api.SendPinCode(data)
-                        }
+                        // if (res.error === 2) {
+                        //     data.hasUser = true
+                        //     res = await this.$api.SendPinCode(data)
+                        // }
                         this.$Message.success(this.$t('login.sendok'))
                         this.loading = false
                         this.hasSendCode = true

+ 12 - 12
TEAMModelOS/ClientApp/src/view/user/UserCenter.vue

@@ -341,28 +341,22 @@ export default {
     methods: {
         //发送短信验证码
         sendMsgCode() {
-            let srvAdr = this.$store.state.config.srvAdr
-            let host = srvAdr == 'Global' ? this.$store.state.config.Global.coreAPIUrl : this.$store.state.config.China.coreAPIUrl
+            // let srvAdr = this.$store.state.config.srvAdr
+            // let host = srvAdr == 'Global' ? this.$store.state.config.Global.coreAPIUrl : this.$store.state.config.China.coreAPIUrl
             let n = this.mobile.number.indexOf('0') === 0 ? this.mobile.number.slice(1) : this.mobile.number
             // if () {
             //     this.mobile.number.slice(1)
             // }
             let params = {
-                country: this.mobile.area + '',
-                // to: this.mobile.number,
+                // country: this.mobile.area + '',
+                area: this.mobile.area + '',
                 to: n,
                 lang: localStorage.getItem('local'),
                 HasUser: false
             }
-            this.$api.service.sandMsgCode(host, params).then(
+            this.$api.service.sendMsgCode(params).then(
                 res => {
-                    if (!res.error) {
-                        this.$Message.success(this.$t('user.msgOk'))
-                        this.hasSend = true
-                        this.timer = setInterval(() => {
-                            this.countdown--
-                        }, 1000)
-                    } else {
+                    if (res.error) {
                         if (res.error == 1) {
                             this.$Message.error(this.$t('user.noMobile'))
                         } else if (res.error == 2) {
@@ -370,6 +364,12 @@ export default {
                         } else {
                             this.$Message.error(this.$t('user.msgErr'))
                         }
+                    } else {
+                        this.$Message.success(this.$t('user.msgOk'))
+                        this.hasSend = true
+                        this.timer = setInterval(() => {
+                            this.countdown--
+                        }, 1000)
                     }
                 },
                 err => {

+ 3 - 3
TEAMModelOS/Controllers/OpenApi/Business/BizCourseController.cs

@@ -44,7 +44,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-course-list")]
-        [ApiToken(Auth = "1301", Name = "获取课程列表信息", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1301", Name = "获取课程列表信息",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetCourseList(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -59,7 +59,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-course-info")]
-        [ApiToken(Auth = "1302", Name = "课程详细信息", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1302", Name = "课程详细信息",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetCourseInfo(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -74,7 +74,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-period-info")]
-        [ApiToken(Auth = "1303", Name = "获取指定学段信息", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1303", Name = "获取指定学段信息",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetPaperExamCondition(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();

+ 2 - 2
TEAMModelOS/Controllers/OpenApi/Business/BizGroupListController.cs

@@ -47,7 +47,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-group-list")]
-        [ApiToken(Auth = "1201", Name = "名单列表信息", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1201", Name = "名单列表信息",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetGroupList(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -62,7 +62,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-group-members")]
-        [ApiToken(Auth = "1202", Name = "名单成员信息", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1202", Name = "名单成员信息",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetGroupMembers(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();

+ 1 - 1
TEAMModelOS/Controllers/OpenApi/Business/BizKnowledgeController.cs

@@ -50,7 +50,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-knowledges")]
-        [ApiToken(Auth = "1701", Name = "获取知识点列表", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1701", Name = "获取知识点列表",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetKnowledges(JsonElement jsonElement)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();

+ 2 - 2
TEAMModelOS/Controllers/OpenApi/Business/BizLessonRecordController.cs

@@ -41,7 +41,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-lesson-record")]
-        [ApiToken(Auth = "1801", Name = " 获取开课/课例记录", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1801", Name = " 获取开课/课例记录",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetLessonRecord(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -56,7 +56,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-lesson-record-count")]
-        [ApiToken(Auth = "1802", Name = " 获取开课/课例记录数量", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1802", Name = " 获取开课/课例记录数量",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetLessonRecordCount(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();

+ 2 - 2
TEAMModelOS/Controllers/OpenApi/Business/BizRoomController.cs

@@ -38,7 +38,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-room-list")]
-        [ApiToken(Auth = "1401", Name = "物理教室列表", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1401", Name = "物理教室列表",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetRoomList(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -53,7 +53,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-room-info")]
-        [ApiToken(Auth = "1402", Name = "物理教室详细", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1402", Name = "物理教室详细",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetRoomInfo(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();

+ 2 - 2
TEAMModelOS/Controllers/OpenApi/Business/BizSchoolController.cs

@@ -40,7 +40,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-schools")]
-        [ApiToken(Auth = "1000", Name = "合作商获取可访问的学校列表", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1000", Name = "合作商获取可访问的学校列表",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetSchools()
         {
             var (id, _) = HttpContext.GetApiTokenInfo();
@@ -55,7 +55,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-school-info")]
-        [ApiToken(Auth = "1001", Name = "学校基本信息", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1001", Name = "学校基本信息",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetSchoolInfo()
         {
             var (id, school) = HttpContext.GetApiTokenInfo();

+ 2 - 2
TEAMModelOS/Controllers/OpenApi/Business/BizSyllabusController.cs

@@ -53,7 +53,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-syllabus")]
-        [ApiToken(Auth = "1601", Name = "获取课纲列表", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1601", Name = "获取课纲列表",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetSyllabus(JsonElement jsonElement) 
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -68,7 +68,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-volumes")]
-        [ApiToken(Auth = "1602", Name = "获取册别列表", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1602", Name = "获取册别列表",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetVolumes(JsonElement jsonElement) 
         {
             var (id, school) = HttpContext.GetApiTokenInfo();

+ 3 - 3
TEAMModelOS/Controllers/OpenApi/Business/BizTeacherController.cs

@@ -45,7 +45,7 @@ namespace TEAMModelBI.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-teacher-list")]
-        [ApiToken(Auth = "1501", Name = "教师列表信息", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1501", Name = "教师列表信息",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetTeacherList(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -61,7 +61,7 @@ namespace TEAMModelBI.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-teacher-info")]
-        [ApiToken(Auth = "1502", Name = "教师详细信息", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1502", Name = "教师详细信息",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetTeacherInfo(JsonElement json)
         {
             json.TryGetProperty("tmdid", out JsonElement _tmdid);
@@ -76,7 +76,7 @@ namespace TEAMModelBI.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-teacher-teach")]
-        [ApiToken(Auth = "1503", Name = "教师执教的班级和课程", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1503", Name = "教师执教的班级和课程",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetTeacherTeach(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();

+ 18 - 16
TEAMModelOS/Controllers/OpenApi/OpenApiService.cs

@@ -40,7 +40,7 @@ namespace TEAMModelOS.Controllers
 
         /// <summary>
         /// 第三方获取学校列表
-        /// [ApiToken(Auth = "1000", Name = "合作商获取可访问的学校列表", RWN = "R", Limit = false)]
+        /// [ApiToken(Auth = "1000", Name = "合作商获取可访问的学校列表",TName ="",EName ="", RWN = "R", Limit = false)]
         /// </summary>
         /// <param name="_azureCosmos"></param>
         /// <param name="_dingDing"></param>
@@ -409,7 +409,7 @@ namespace TEAMModelOS.Controllers
 
         /// <summary>
         /// 学校基本信息
-        /// //[ApiToken(Auth = "1001", Name = "学校基本信息", RWN = "R", Limit = false)]
+        /// //[ApiToken(Auth = "1001", Name = "学校基本信息",TName ="",EName ="", RWN = "R", Limit = false)]
         /// </summary>
         /// <param name="_azureCosmos"></param>
         /// <param name="_dingDing"></param>
@@ -446,7 +446,7 @@ namespace TEAMModelOS.Controllers
 
         /// <summary>
         /// 名单列表信息 获取学校的行政班,教学班,教研组,研修名单
-        /// [ApiToken(Auth = "1201", Name = "名单列表信息", RWN = "R", Limit = false)]
+        /// [ApiToken(Auth = "1201", Name = "名单列表信息",TName ="",EName ="", RWN = "R", Limit = false)]
         /// </summary>
         /// <param name="_azureCosmos"></param>
         /// <param name="_dingDing"></param>
@@ -568,7 +568,7 @@ namespace TEAMModelOS.Controllers
 
         /// <summary>
         /// 名单成员信息,学生成员信息,包含(学生,成员)基本信息,分组等信息
-        /// [ApiToken(Auth = "1202", Name = "名单成员信息", RWN = "R", Limit = false)]
+        /// [ApiToken(Auth = "1202", Name = "名单成员信息",TName ="",EName ="", RWN = "R", Limit = false)]
         /// </summary>
         /// <param name="_azureCosmos"></param>
         /// <param name="_coreAPIHttpService"></param>
@@ -600,7 +600,7 @@ namespace TEAMModelOS.Controllers
 
         /// <summary>
         /// 获取课程列表信息
-        /// [ApiToken(Auth = "1301", Name = "获取课程列表信息", RWN = "R", Limit = false)]
+        /// [ApiToken(Auth = "1301", Name = "获取课程列表信息",TName ="",EName ="", RWN = "R", Limit = false)]
         /// </summary>
         /// <param name="_azureCosmos"></param>
         /// <param name="_dingDing"></param>
@@ -641,7 +641,7 @@ namespace TEAMModelOS.Controllers
 
         /// <summary>
         /// 课程详细信息
-        /// [ApiToken(Auth = "1302", Name = "课程详细信息", RWN = "R", Limit = false)]
+        /// [ApiToken(Auth = "1302", Name = "课程详细信息",TName ="",EName ="", RWN = "R", Limit = false)]
         /// </summary>
         /// <param name="_azureCosmos"></param>
         /// <param name="_dingDing"></param>
@@ -673,7 +673,7 @@ namespace TEAMModelOS.Controllers
 
         /// <summary>
         /// 获取指定学段作息
-        /// [ApiToken(Auth = "1303", Name = "获取指定学段作息", RWN = "R", Limit = false)]
+        /// [ApiToken(Auth = "1303", Name = "获取指定学段作息",TName ="",EName ="", RWN = "R", Limit = false)]
         /// </summary>
         /// <param name="_azureCosmos"></param>
         /// <param name="_dingDing"></param>
@@ -705,7 +705,7 @@ namespace TEAMModelOS.Controllers
 
         /// <summary>
         /// 物理教室列表
-        /// [ApiToken(Auth = "1401", Name = "物理教室列表", RWN = "R", Limit = false)]
+        /// [ApiToken(Auth = "1401", Name = "物理教室列表",TName ="",EName ="", RWN = "R", Limit = false)]
         /// </summary>
         /// <param name="_azureCosmos"></param>
         /// <param name="_dingDing"></param>
@@ -753,7 +753,7 @@ namespace TEAMModelOS.Controllers
 
         /// <summary>
         /// 物理教室详细
-        /// [ApiToken(Auth = "1402", Name = "物理教室详细", RWN = "R", Limit = false)]
+        /// [ApiToken(Auth = "1402", Name = "物理教室详细",TName ="",EName ="", RWN = "R", Limit = false)]
         /// </summary>
         /// <param name="_azureCosmos"></param>
         /// <param name="_dingDing"></param>
@@ -787,7 +787,7 @@ namespace TEAMModelOS.Controllers
 
         /// <summary>
         /// 教师列表信息
-        /// [ApiToken(Auth = "1501", Name = "教师列表信息", RWN = "R", Limit = false)]
+        /// [ApiToken(Auth = "1501", Name = "教师列表信息",TName ="",EName ="", RWN = "R", Limit = false)]
         /// </summary>
         /// <param name="_azureCosmos"></param>
         /// <param name="_coreAPIHttpService"></param>
@@ -857,7 +857,7 @@ namespace TEAMModelOS.Controllers
 
         /// <summary>
         /// 教师执教的班级和课程
-        /// [ApiToken(Auth = "1502", Name = "教师详细信息", RWN = "R", Limit = false)]
+        /// [ApiToken(Auth = "1502", Name = "教师详细信息",TName ="",EName ="", RWN = "R", Limit = false)]
         /// </summary>
         /// <param name="_azureCosmos"></param>
         /// <param name="_dingDing"></param>
@@ -945,7 +945,7 @@ namespace TEAMModelOS.Controllers
 
         /// <summary>
         /// 教师详细信息
-        /// [ApiToken(Auth = "1502", Name = "教师详细信息", RWN = "R", Limit = false)]
+        /// [ApiToken(Auth = "1502", Name = "教师详细信息",TName ="",EName ="", RWN = "R", Limit = false)]
         /// </summary>
         /// <param name="_azureCosmos"></param>
         /// <param name="_dingDing"></param>
@@ -988,7 +988,7 @@ namespace TEAMModelOS.Controllers
 
         /// <summary>
         /// 教师批量导入,并加入学校。可以导入学科,但需要填写学段id
-        /// [ApiToken(Auth = "1503", Name = "教师批量导入", RWN = "R", Limit = false)]
+        /// [ApiToken(Auth = "1503", Name = "教师批量导入",TName ="",EName ="", RWN = "R", Limit = false)]
         /// </summary>
         /// <param name="_azureCosmos"></param>
         /// <param name="_coreAPIHttpService"></param>
@@ -1168,7 +1168,7 @@ namespace TEAMModelOS.Controllers
 
         /// <summary>
         /// 查找课纲
-        /// [ApiToken(Auth = "1601", Name = "查询课纲", RWN = "R", Limit = false)]
+        /// [ApiToken(Auth = "1601", Name = "查询课纲",TName ="",EName ="", RWN = "R", Limit = false)]
         /// </summary>
         /// <param name="_azureCosmos"></param>
         /// <param name="json"></param>
@@ -1245,7 +1245,7 @@ namespace TEAMModelOS.Controllers
 
         /// <summary>
         /// 查询册别清单
-        /// [ApiToken(Auth = "1602", Name = "查询册别", RWN = "R", Limit = false)]
+        /// [ApiToken(Auth = "1602", Name = "查询册别",TName ="",EName ="", RWN = "R", Limit = false)]
         /// </summary>
         /// <param name="_azureCosmos"></param>
         /// <param name="_dingDing"></param>
@@ -1288,7 +1288,7 @@ namespace TEAMModelOS.Controllers
 
         /// <summary>
         /// 查询知识
-        /// [ApiToken(Auth = "1701", Name = "查询知识点", RWN = "R", Limit = false)]
+        /// [ApiToken(Auth = "1701", Name = "查询知识点",TName ="",EName ="", RWN = "R", Limit = false)]
         /// </summary>
         /// <param name="_azureCosmos"></param>
         /// <param name="_dingDing"></param>
@@ -1370,6 +1370,7 @@ namespace TEAMModelOS.Controllers
             openApis.RemoveAll(x => apis.Select(z => z.RowKey).Contains(x.RowKey));
             openApis.AddRange(apis);
             _ = table.SaveOrUpdateAll<OpenApi>(openApis);
+            string dataopenApis = openApis.ToJsonString();
             (List<WebHook> webHooks, List<ApiTokenAttribute> _attributes) = ReflectorExtensions.GetWebHook(new string[] { "TEAMModelOS.SDK" });
 
             webHooks.GroupBy(x => $"{x.PartitionKey}{x.RowKey}").ToList().ForEach(x =>
@@ -1399,6 +1400,7 @@ namespace TEAMModelOS.Controllers
             webHooks.RemoveAll(x => hooks.Select(z => z.RowKey).Contains(x.RowKey));
             webHooks.AddRange(hooks);
             _ = table.SaveOrUpdateAll<WebHook>(webHooks);
+            string datawebHooks = webHooks.ToJsonString();
         }
 
     }

+ 5 - 5
TEAMModelOS/Controllers/OpenApi/OpenSchool/ScCourseController.cs

@@ -52,7 +52,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-course-list")]
-        [ApiToken(Auth = "1301", Name = "获取课程列表信息", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1301", Name = "获取课程列表信息",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetCourseList(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -67,7 +67,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-course-info")]
-        [ApiToken(Auth = "1302", Name = "课程详细信息", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1302", Name = "课程详细信息",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetCourseInfo(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -82,7 +82,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-period-info")]
-        [ApiToken(Auth = "1303", Name = "获取指定学段信息", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1303", Name = "获取指定学段信息",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetPaperExamCondition(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -97,7 +97,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("upsert-course-infos")]
-        [ApiToken(Auth = "1304", Name = "创建或更新课程", RWN = "W", Limit = false)]
+        [ApiToken(Auth = "1304", Name = "创建或更新课程",TName ="",EName ="", RWN = "W", Limit = false)]
         public async Task<IActionResult> UpsertCourseInfo(CourseDtoImpt json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -218,7 +218,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("upsert-course-schedule")]
-        [ApiToken(Auth = "1305", Name = "更新课程的排课信息", RWN = "W", Limit = false)]
+        [ApiToken(Auth = "1305", Name = "更新课程的排课信息",TName ="",EName ="", RWN = "W", Limit = false)]
         public async Task<IActionResult> UpsertCourseSchedule(ImportCourseDto json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();

+ 4 - 4
TEAMModelOS/Controllers/OpenApi/OpenSchool/ScExamController.cs

@@ -58,7 +58,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-paper-exam-condition")]
-        [ApiToken(Auth = "1101", Name = "试卷和评测的条件信息", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1101", Name = "试卷和评测的条件信息",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetPaperExamCondition(JsonElement json)
         {
             json.TryGetProperty("periodId", out JsonElement _periodId);
@@ -78,7 +78,7 @@ namespace TEAMModelOS.Controllers
         }
         [ProducesDefaultResponseType]
         [HttpPost("import-exam")]
-        [ApiToken(Auth = "1102", Name = "汇入评测基础数据", RWN = "W", Limit = false)]
+        [ApiToken(Auth = "1102", Name = "汇入评测基础数据",TName ="",EName ="", RWN = "W", Limit = false)]
         public async Task<IActionResult> importExam(JsonElement request)
         {
             //获取评测的ID
@@ -100,7 +100,7 @@ namespace TEAMModelOS.Controllers
         }
         [ProducesDefaultResponseType]
         [HttpPost("upsert-record")]
-        [ApiToken(Auth = "1103", Name = "批量汇入作答数据", RWN = "W", Limit = false)]
+        [ApiToken(Auth = "1103", Name = "批量汇入作答数据",TName ="",EName ="", RWN = "W", Limit = false)]
         public async Task<IActionResult> upsertRecord(JsonElement request)
         {
 
@@ -364,7 +364,7 @@ namespace TEAMModelOS.Controllers
         }
         [ProducesDefaultResponseType]
         [HttpPost("parse-word")]
-        [ApiToken(Auth = "1104", Name = "录入试卷数据", RWN = "W", Limit = false)]
+        [ApiToken(Auth = "1104", Name = "录入试卷数据",TName ="",EName ="", RWN = "W", Limit = false)]
         public async Task<IActionResult> ParseWord([FromForm] FileDto fileDto)
         {
             if (!FileType.GetExtention(fileDto.file.FileName).ToLower().Equals("docx"))

+ 8 - 8
TEAMModelOS/Controllers/OpenApi/OpenSchool/ScGroupListController.cs

@@ -51,7 +51,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-group-list")]
-        [ApiToken(Auth = "1201", Name = "名单列表信息", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1201", Name = "名单列表信息",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetGroupList(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -66,7 +66,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-group-members")]
-        [ApiToken(Auth = "1202", Name = "名单成员信息", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1202", Name = "名单成员信息",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetGroupMembers(JsonElement json) 
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -81,7 +81,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("import-class-members")]
-        [ApiToken(Auth = "1203", Name = "导入行政班学生", RWN = "W", Limit = false)]
+        [ApiToken(Auth = "1203", Name = "导入行政班学生",TName ="",EName ="", RWN = "W", Limit = false)]
         public async Task<IActionResult> ImportClassMembers(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -118,7 +118,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("update-class-members")]
-        [ApiToken(Auth = "1204", Name = "更新行政班学生", RWN = "W", Limit = false)]
+        [ApiToken(Auth = "1204", Name = "更新行政班学生",TName ="",EName ="", RWN = "W", Limit = false)]
         public async Task<IActionResult> UpdateClassMembers(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -156,7 +156,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("remove-class-members")]
-        [ApiToken(Auth = "1205", Name = "移除行政班学生", RWN = "W", Limit = false)]
+        [ApiToken(Auth = "1205", Name = "移除行政班学生",TName ="",EName ="", RWN = "W", Limit = false)]
         public async Task<IActionResult> RemoveClassMembers(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -198,7 +198,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("upsert-teach-groups")]
-        [ApiToken(Auth = "1206", Name = "创建或更新教学班", RWN = "W", Limit = false)]
+        [ApiToken(Auth = "1206", Name = "创建或更新教学班",TName ="",EName ="", RWN = "W", Limit = false)]
         public async Task<IActionResult> UpsertTeachGroups(GroupListDtoImpt json)
         {
             var (_, school) = HttpContext.GetApiTokenInfo();
@@ -291,7 +291,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("import-teach-members")]
-        [ApiToken(Auth = "1207", Name = "导入教学班学生", RWN = "W", Limit = false)]
+        [ApiToken(Auth = "1207", Name = "导入教学班学生",TName ="",EName ="", RWN = "W", Limit = false)]
         public async Task<IActionResult> ImportTeachMembers(MemberImpt json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -447,7 +447,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("remove-teach-members")]
-        [ApiToken(Auth = "1208", Name = "移除教学班学生", RWN = "W", Limit = false)]
+        [ApiToken(Auth = "1208", Name = "移除教学班学生",TName ="",EName ="", RWN = "W", Limit = false)]
         public async Task<IActionResult> RemoveTeachMembers(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();

+ 1 - 1
TEAMModelOS/Controllers/OpenApi/OpenSchool/ScKnowledgeController.cs

@@ -44,7 +44,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-knowledges")]
-        [ApiToken(Auth = "1701", Name = "获取知识点列表", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1701", Name = "获取知识点列表",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetKnowledges(JsonElement jsonElement)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();

+ 3 - 3
TEAMModelOS/Controllers/OpenApi/OpenSchool/ScRoomController.cs

@@ -44,7 +44,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-room-list")]
-        [ApiToken(Auth = "1401", Name = "物理教室列表", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1401", Name = "物理教室列表",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetRoomList(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -59,7 +59,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-room-info")]
-        [ApiToken(Auth = "1402", Name = "物理教室详细", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1402", Name = "物理教室详细",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetRoomInfo(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -81,7 +81,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("upsert-room-infos")]
-        [ApiToken(Auth = "1403", Name = "教室批量创建更新", RWN = "W", Limit = false)]
+        [ApiToken(Auth = "1403", Name = "教室批量创建更新",TName ="",EName ="", RWN = "W", Limit = false)]
         public async Task<IActionResult> UpsertRoomInfo(RoomsDto json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();

+ 1 - 1
TEAMModelOS/Controllers/OpenApi/OpenSchool/ScSchoolController.cs

@@ -40,7 +40,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-school-info")]
-        [ApiToken(Auth = "1001", Name = "学校基本信息", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1001", Name = "学校基本信息",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetSchoolInfo()
         {
             var (id, school) = HttpContext.GetApiTokenInfo();

+ 2 - 2
TEAMModelOS/Controllers/OpenApi/OpenSchool/ScSyllabusController.cs

@@ -44,7 +44,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-syllabus")]
-        [ApiToken(Auth = "1601", Name = "获取课纲列表", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1601", Name = "获取课纲列表",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetSyllabus(JsonElement jsonElement)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -59,7 +59,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-volumes")]
-        [ApiToken(Auth = "1602", Name = "获取册别列表", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1602", Name = "获取册别列表",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetVolumes(JsonElement jsonElement)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();

+ 3 - 3
TEAMModelOS/Controllers/OpenApi/OpenSchool/ScTeacherController.cs

@@ -43,7 +43,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-teacher-list")]
-        [ApiToken(Auth = "1501", Name = "教师列表信息", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1501", Name = "教师列表信息",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetTeacherList(JsonElement json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();
@@ -59,7 +59,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("get-teacher-info")]
-        [ApiToken(Auth = "1502", Name = "教师详细信息", RWN = "R", Limit = false)]
+        [ApiToken(Auth = "1502", Name = "教师详细信息",TName ="",EName ="", RWN = "R", Limit = false)]
         public async Task<IActionResult> GetTeacherInfo(JsonElement json)
         {
             json.TryGetProperty("tmdid", out JsonElement _tmdid);
@@ -75,7 +75,7 @@ namespace TEAMModelOS.Controllers
         /// <returns></returns>
         [ProducesDefaultResponseType]
         [HttpPost("import-school-teacher")]
-        [ApiToken(Auth = "1503", Name = "教师批量导入", RWN = "W", Limit = false)]
+        [ApiToken(Auth = "1503", Name = "教师批量导入",TName ="",EName ="", RWN = "W", Limit = false)]
         public async Task<IActionResult> ImportSchoolTeacher(ImportTechDto json)
         {
             var (id, school) = HttpContext.GetApiTokenInfo();

+ 130 - 3
TEAMModelOS/Controllers/System/CoreController.cs

@@ -2,13 +2,17 @@ using Azure.Storage.Blobs.Models;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Options;
+using StackExchange.Redis;
 using PuppeteerSharp;
 using System;
 using System.Collections.Generic;
 using System.Drawing.Imaging;
+using System.IdentityModel.Tokens.Jwt;
 using System.IO;
 using System.Linq;
+using System.Net;
 using System.Net.Http;
 using System.Reflection;
 using System.Runtime.InteropServices;
@@ -37,7 +41,9 @@ namespace TEAMModelOS.Controllers
         private readonly Option _option;
         private readonly HttpClient _httpClient;
         private readonly IPSearcher _searcher;
-        public CoreController(IPSearcher searcher, AzureRedisFactory azureRedis, AzureStorageFactory azureStorage, DingDing dingDing, IOptionsSnapshot<Option> option, HttpClient httpClient)
+        private readonly CoreAPIHttpService _coreAPIHttpService;
+        private readonly IConfiguration _configuration;
+        public CoreController(CoreAPIHttpService coreAPIHttpService,IConfiguration configuration,IPSearcher searcher, AzureRedisFactory azureRedis, AzureStorageFactory azureStorage, DingDing dingDing, IOptionsSnapshot<Option> option, HttpClient httpClient)
         {
             _searcher = searcher;
             _azureStorage = azureStorage;
@@ -45,10 +51,129 @@ namespace TEAMModelOS.Controllers
             _option = option?.Value;
             _httpClient = httpClient;
             _azureRedis = azureRedis;
+            _configuration = configuration;
+            _coreAPIHttpService = coreAPIHttpService;
         }
+        [HttpPost("sendsms/pin")]
+        public async Task<IActionResult> SendSmsPinCode(JsonElement request)
+        {
+            (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
+            
+            //获取投票活动的选项及投票数
+            string ipkey = $"Ip:Pin:Count:{ip}";
+          
+            bool ipkeyexist = await _azureRedis.GetRedisClient(8).KeyExistsAsync(ipkey);
+            if (ipkeyexist)
+            {
+                await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync(ipkey, ip, 1);
+            }
+            else {
+                await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync(ipkey, ip, 1);
+                var Expire = DateTime.UtcNow.AddSeconds(600);
+                _azureRedis.GetRedisClient(8).KeyExpire(ipkey, Expire);
+            }
+            var counts = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores(ipkey);
+            long sum = 0;
+            if (counts != null && counts.Length > 0)
+            {
+                foreach (var count in counts)
+                {
+                    sum += (int)count.Score;
+                }
+            }
+            int limit = 1000;
+            if (sum > limit) {
+                await  _dingDing.SendBotMsg($"{_option.Location}\nIp:{ip}\n位置:{region}\n 短信验证码10分钟内访问次数超过:{limit}次!",GroupNames.成都开发測試群組);
+                return Ok(new { send = 2 });
+            }
+            if (!request.TryGetProperty("area", out JsonElement _area)) return BadRequest();
+            if (!request.TryGetProperty("to", out JsonElement _to)) return BadRequest();
+            if (!request.TryGetProperty("lang", out JsonElement _lang)) return BadRequest();
+            request.TryGetProperty("HasUser", out JsonElement _HasUser);
+            string code=$"{_area}{_to}";
+            int exp = 120;
+            string key = $"Random:Code:PinCode-{_area}{_to}";
+            bool exist = await _azureRedis.GetRedisClient(8).KeyExistsAsync(key);
+            if (!exist)
+            {
+                //不存在则发送请求。
+               
+                Dictionary<string, object> dict = null;
+                if (_HasUser.ValueKind.Equals(JsonValueKind.True))
+                {
+                    dict = new Dictionary<string, object> { { "country", $"{_area}" }, { "to", $"{_to}" }, { "lang", $"{_lang}" }, { "HasUser", true } };
+                }
+                else if (_HasUser.ValueKind.Equals(JsonValueKind.False)) {
+                    dict = new Dictionary<string, object> { { "country", $"{_area}" }, { "to", $"{_to}" }, { "lang", $"{_lang}" }, { "HasUser", false } };
+                }
+                else
+                {
+                    dict = new Dictionary<string, object> { { "country", $"{_area}" }, { "to", $"{_to}" }, { "lang", $"{_lang}" } };
+                }
+                var httpresp = await _coreAPIHttpService.SendSmsPin(dict, _option.Location, _configuration, _dingDing);
+                if (httpresp.code.Equals(HttpStatusCode.OK))
+                {
+                    var Expire = DateTime.UtcNow.AddSeconds(exp);
+                    //send=1 表示已发送
+                    await _azureRedis.GetRedisClient(8).StringSetAsync(key, new { code = code, send = 1, Expire = Expire.Ticks }.ToJsonString());
+                    _azureRedis.GetRedisClient(8).KeyExpire(key, Expire);
+                    if (!string.IsNullOrWhiteSpace(httpresp.content))
+                    {
+                        return Ok(httpresp.content.ToObject<JsonElement>());
+                    }
+                    else {
+                        return Ok(new { send = 1 });
+                    }
+                }
+                else
+                {
+                   return Ok(new {  send = 0  });
+                }
+            }
+            else
+            {
+                //检查当前key是否已经发送了.
+                RedisValue value = await _azureRedis.GetRedisClient(8).StringGetAsync(key);
+                JsonElement element = value.ToString().ToObject<JsonElement>();
+                int send = 0;
+                if (element.TryGetProperty("send", out JsonElement _send))
+                {
+                    if (_send.ValueKind.Equals(JsonValueKind.Number))
+                    {
+                        if (int.Parse($"{_send}") == 1)
+                        {
+                            send = 1;
 
-        [HttpPost("apply-school")]
+                        }
+                        else if (int.Parse($"{_send}") == 0)
+                        {
+                            send = 0;
+                        }
+                    }
+                }
+                if (send == 0)
+                {
+                    await _azureRedis.GetRedisClient(8).KeyDeleteAsync(key);
+                    return Ok(new {   send = 0  });
+                }
+                else
+                {
+                    TimeSpan? timeSpan = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync(key);
+                    if (timeSpan != null && timeSpan.HasValue)
+                    {
+                        int seconds = timeSpan.Value.Seconds;
+                        return Ok(new {   send = 1  });
+                    }
+                    else
+                    {
+                        return Ok(new {   send = 0  });
+                    }
+                }
+            }
 
+        }
+ 
+        [HttpPost("apply-school")]
         public async Task<IActionResult> ApplySchool(ApplySchool request)
         {            
             if (_option.Location.Equals("China"))
@@ -265,8 +390,10 @@ namespace TEAMModelOS.Controllers
         [HttpPost("screenshot")]
         public async Task<IActionResult> Screenshot(ScreenshotDto screenshot)
         {
+            //https://www.hardkoded.com/blog/running-puppeteer-sharp-azure-functions  使用。
             //string url = "https://teammodelos.blob.core.chinacloudapi.cn/0-public/pie-borderRadius.html";
-            try {
+            try
+            {
                 string dir = ".local-chromium";
                 if (!Directory.Exists(dir))
                 {

+ 9 - 1
TEAMModelOS/Controllers/Third/Sc/ScController.cs

@@ -325,8 +325,16 @@ namespace TEAMModelOS.Controllers
                     await _dingDing.SendBotMsg($"OS,{_option.Location}\n绑定失败,出现的原因可能是 参数异常:\n{sso.ToJsonString()},{ex.Message}\n{ex.StackTrace}\n", GroupNames.醍摩豆服務運維群組);
                     return BadRequest();
                 }
-
                 var id = jwt.Payload.Sub;
+                CoreUser coreUserById=  await _coreAPIHttpService.GetUserInfo(new Dictionary<string, string> { { "key", $"{id}" } }, _option.Location, _configuration);
+                if (coreUserById == null|| string.IsNullOrWhiteSpace(coreUserById.mobile)|| coreUserById.mobile.Length!=11)
+                {
+                    return Ok(new
+                    {
+                        location = _option.Location,
+                        status = 2,
+                    });
+                }
                 jwt.Payload.TryGetValue("name", out object name);
                 jwt.Payload.TryGetValue("picture", out object picture);
                 ScSSOData scsso = HttpUtility.UrlDecode(sso.param, Encoding.UTF8).ToObject<ScSSOData>();

+ 14 - 12
TEAMModelOS/Controllers/Third/Sc/ScDataPushController.cs

@@ -389,19 +389,21 @@ namespace TEAMModelOS.Controllers
             List<PushFail> fails = new List<PushFail>();
             failse.ForEach(x =>
             {
-                var f = fails.FindAll(y => y.tmdid.Equals(x.tmdid));
-                if (f.IsNotEmpty())
-                {
-                    f.ForEach(ff => {
-                        ff.type = $"{ff.type},{x.type}";
-                    });
-                }
-                else
-                {
-                    x.msgs.ForEach(msg =>
+                if (x != null && !string.IsNullOrWhiteSpace(x.tmdid)) { 
+                    var f = fails.FindAll(y => y.tmdid.Equals(x.tmdid));
+                    if (f.IsNotEmpty())
                     {
-                        fails.Add(new PushFail { tmdid = x.tmdid, name = x.name, school = x.school, schoolname = x.schoolname, code = msg.code, msg = msg.value, type = x.type });
-                    });
+                        f.ForEach(ff => {
+                            ff.type = $"{ff.type},{x.type}";
+                        });
+                    }
+                    else
+                    {
+                        x.msgs.ForEach(msg =>
+                        {
+                            fails.Add(new PushFail { tmdid = x.tmdid, name = x.name, school = x.school, schoolname = x.schoolname, code = msg.code, msg = msg.value, type = x.type });
+                        });
+                    }
                 }
             });
             return Ok(new { data = new {   fails } });

+ 1 - 1
TEAMModelOS/Controllers/XTest/FixDataController.cs

@@ -2710,7 +2710,7 @@ namespace TEAMModelOS.Controllers
 
             //string sql = $"SELECT  distinct  c.id,s.schoolId as code , c.name as nickname, s.name as name     FROM c  join s in  c.schools where s.areaId='f35e0031-a53f-45e5-b307-1cd39446a2cf'" +
             //    $" and array_length(c.binds)>0  and s.schoolId in ({string.Join(",", schools.Select(s => $"'{s}'"))})";
-            string sql = $"SELECT  distinct  c.id,s.schoolId as code , c.name as nickname, s.name as name     FROM c  join s in  c.schools where s.areaId='f35e0031-a53f-45e5-b307-1cd39446a2cf'" +
+            string sql = $"SELECT  distinct  c.id,s.schoolId as code , c.name as nickname, s.name as name     FROM c  join s in  c.schools where s.areaId='870a5a6b-1ab3-461a-bdeb-baec19780ddb'" +
                $" and array_length(c.binds)>0  ";
             List<IdNameCode> tmdidSchooCode = new List<IdNameCode>();
             await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetItemQueryIterator<IdNameCode>(queryText: sql,

+ 1 - 1
TEAMModelOS/Controllers/XTest/TestController.cs

@@ -1359,7 +1359,7 @@ namespace TEAMModelOS.Controllers
                     Objects = {
                         new ObjectSettings()
                         {
-                            Page = "https://zhidao.baidu.com/question/175696719173618004.html",
+                            Page = "https://teammodelos.blob.core.chinacloudapi.cn/0-public/pie-borderRadius.html",
                         },
                     }
                 };

+ 3 - 3
TEAMModelOS/TEAMModelOS.csproj

@@ -40,9 +40,9 @@
     <SpaRoot>ClientApp\</SpaRoot>
     <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
     <UserSecretsId>078b5d89-7d90-4f6a-88fc-7d96025990a8</UserSecretsId>
-    <Version>5.2207.13</Version>
-    <AssemblyVersion>5.2207.13.1</AssemblyVersion>
-    <FileVersion>5.2207.13.1</FileVersion>
+    <Version>5.2207.20</Version>
+    <AssemblyVersion>5.2207.20.1</AssemblyVersion>
+    <FileVersion>5.2207.20.1</FileVersion>
     <Description>TEAMModelOS(IES5)</Description>
     <PackageReleaseNotes>IES版本说明版本切换标记202200701</PackageReleaseNotes>
     <PackageId>TEAMModelOS</PackageId>