소스 검색

Merge branch 'develop' into TPE/Daniel

jeff 4 달 전
부모
커밋
3b9570065e
100개의 변경된 파일2085개의 추가작업 그리고 2323개의 파일을 삭제
  1. 1 0
      .gitignore
  2. 12 0
      TEAMModelBI/ClientApp/src/api/index.js
  3. 2 1
      TEAMModelBI/ClientApp/src/language/lang/zh-cn.js
  4. 2 1
      TEAMModelBI/ClientApp/src/language/lang/zh-tw.js
  5. 4 3
      TEAMModelBI/ClientApp/src/view/htcommunity/adminpanel.vue
  6. 161 4
      TEAMModelBI/ClientApp/src/view/product/details.vue
  7. 8 2
      TEAMModelBI/ClientApp/src/view/product/index.vue
  8. 5 0
      TEAMModelBI/ClientApp/src/view/schoolServe/school.vue
  9. 567 0
      TEAMModelBI/ClientApp/src/view/schoolServe/setSchoolsales.vue
  10. 257 0
      TEAMModelBI/Controllers/BISchool/SchoolController.cs
  11. 3 3
      TEAMModelBI/TEAMModelBI.csproj
  12. 0 3
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/IES.ExamClient.esproj
  13. 40 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/IES.ExamClient.njsproj
  14. 3 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/README.md
  15. 133 64
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/app.js
  16. BIN
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/header.bmp
  17. BIN
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/logo.ico
  18. 18 10
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/package.json
  19. BIN
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/sidebar.bmp
  20. 147 1073
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/yarn.lock
  21. 0 4
      TEAMModelOS.Extension/IES.ExamLib/IES.ExamLib.csproj
  22. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamLibrary/Models/EvaluationCommon.cs
  23. 1 1
      TEAMModelOS.Extension/IES.ExamLib/Models/ExamConstant.cs
  24. 13 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/.config/dotnet-tools.json
  25. 0 24
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/README.md
  26. 0 58
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/components/HelloWorld.vue
  27. 0 19
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/vue.config.js
  28. 22 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/cer/cert.pem
  29. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/cer/certificate.bat
  30. BIN
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/cer/certificate.cer
  31. 2 35
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/template/certificate.bat
  32. 28 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/cer/key.pem
  33. 42 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/cer/modify_hosts.bat
  34. 84 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/cer/选择ip映射.bat
  35. 0 22
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam0/cert.pem
  36. BIN
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam0/certificate.cer
  37. 0 28
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam0/key.pem
  38. 0 22
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam1/cert.pem
  39. 0 74
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam1/certificate.bat
  40. BIN
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam1/certificate.cer
  41. 0 28
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam1/key.pem
  42. 0 22
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam2/cert.pem
  43. 0 74
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam2/certificate.bat
  44. BIN
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam2/certificate.cer
  45. 0 28
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam2/key.pem
  46. 0 22
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam3/cert.pem
  47. 0 74
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam3/certificate.bat
  48. BIN
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam3/certificate.cer
  49. 0 28
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam3/key.pem
  50. 0 22
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam4/cert.pem
  51. 0 74
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam4/certificate.bat
  52. BIN
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam4/certificate.cer
  53. 0 28
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam4/key.pem
  54. 0 22
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/template/cert.pem
  55. BIN
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/template/certificate.cer
  56. 0 28
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/template/key.pem
  57. 63 26
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/IndexController.cs
  58. 1 1
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/ManageController.cs
  59. 33 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/WeatherForecastController.cs
  60. 1 1
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/SignalRCloudClientHub.cs
  61. 45 41
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/SignalRHost/SignalRExamServerHub.cs
  62. 2 5
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Filters/AuthTokenAttribute.cs
  63. 11 8
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Helpers/QRCodeHelper.cs
  64. 55 108
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/IES.ExamServer.csproj
  65. 15 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Models/ServerDevice.cs
  66. 28 127
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Program.cs
  67. 19 13
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Properties/launchSettings.json
  68. 89 4
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Services/IndexService.cs
  69. 13 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/WeatherForecast.cs
  70. 9 36
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/appsettings.json
  71. 18 11
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/.gitignore
  72. 11 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/IES.ExamViews.esproj
  73. 35 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/README.md
  74. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/babel.config.js
  75. 17 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/eslint.config.js
  76. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/jsconfig.json
  77. 1 1
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/package.json
  78. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/public/favicon.ico
  79. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/public/index.html
  80. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/public/signalr.html
  81. 0 2
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/App.vue
  82. 59 33
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/api/http.js
  83. 5 5
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/api/index.js
  84. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/fengjing.jpg
  85. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/icon/icon_play.png
  86. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/demo.css
  87. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/demo_index.html
  88. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.css
  89. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.js
  90. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.json
  91. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.ttf
  92. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.woff
  93. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.woff2
  94. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/image/tmd_logo.png
  95. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/logo.png
  96. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/qrCode.png
  97. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/reset.css
  98. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/source/audio.png
  99. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/source/excel.png
  100. 0 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/folder.png

+ 1 - 0
.gitignore

@@ -276,3 +276,4 @@ TEAMModelBI/Properties/ServiceDependencies/teammodelbi-RC - Web Deploy/profile.a
 /TEAMModelOS.Extension/IES.Exam/IES.ExamClient/dist
 /TEAMModelOS.Extension/IES.Exam/IES.ExamClient/server/wwwroot
 /TEAMModelOS.Extension/IES.Exam/IES.ExamClient/server
+/TEAMModelOS.Extension/IES.Exam/wwwroot

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

@@ -211,6 +211,18 @@ export default {
     deleteSchooladmin(data) {
         return post('/schoolcheck/del-manage', data)
     },
+    //获取学校業務負責人列表
+    getSchoolSales(data) {
+        return post('/schoolcheck/get-scsales', data)
+    },
+    //添加学校業務負責人
+    setSchoolSales(data) {
+        return post('/schoolcheck/set-sales', data)
+    },
+    //删除学校業務負責人
+    deleteSchoolSales(data) {
+        return post('/schoolcheck/del-sales', data)
+    },
     //获取简易学校列表,数据本地化核验使用 或后端查询学校是否存在
     getsimpleSchool(data) {
         return post('/schoolcheck/get-scsimple', data)

+ 2 - 1
TEAMModelBI/ClientApp/src/language/lang/zh-cn.js

@@ -933,7 +933,8 @@ const zh_cn = {
         notReceived: '未领取',
         sheetSummary: '总表',
         authReceivedList: '授权领取清单',
-        receiverName: '领取人姓名',
+        receiverName: '帐号姓名',
+        receiverRealName: '真实姓名',
         receiverId: '领取人ID',
         receivedList: '已领取清单',
         message1: '不可为零',

+ 2 - 1
TEAMModelBI/ClientApp/src/language/lang/zh-tw.js

@@ -926,7 +926,8 @@ const zh_tw = {
         notReceived: '未領取',
         sheetSummary: '總表',
         authReceivedList: '授權領取清單',
-        receiverName: '領取人姓名',
+        receiverName: '帳號姓名',
+        receiverRealName: '真實姓名',
         receiverId: '領取人ID',
         receivedList: '已領取清單',
         message1: '不可為零',

+ 4 - 3
TEAMModelBI/ClientApp/src/view/htcommunity/adminpanel.vue

@@ -826,8 +826,8 @@ EEEE    120
           let sheetName = proxy.$t(`purchase.sheetSummary`);
           XLSX.utils.book_append_sheet(wb, ws, sheetName);
           //分頁2 已領取清單
-          let titleTch = [proxy.$t(`purchase.schoolName`), proxy.$t(`purchase.schoolCode`), proxy.$t(`purchase.receiverId`), proxy.$t(`purchase.receiverName`)];
-          let keyTch = ['schoolName', 'shortCode', 'tmid', 'name'];
+          let titleTch = [proxy.$t(`purchase.schoolName`), proxy.$t(`purchase.schoolCode`), proxy.$t(`purchase.receiverId`), proxy.$t(`purchase.receiverName`), proxy.$t(`purchase.receiverRealName`)];
+          let keyTch = ['schoolName', 'shortCode', 'tmid', 'name', 'realname'];
           let dataTch = [];
           Object.entries(res.school).forEach(([purchaseIdNow, purchaseSchoolData]) => {
               purchaseSchoolData.forEach(function (item) {
@@ -838,7 +838,8 @@ EEEE    120
                           return el.id == userId;
                       });
                       let userName = (userInfo.length > 0) ? userInfo[0]['name'] : '';
-                      dataTch.push({ schoolName: schName, shortCode: schCode, tmid: userId, name: userName });
+                      let realName = (userInfo.length > 0) ? userInfo[0]['realName'] : '';
+                      dataTch.push({ schoolName: schName, shortCode: schCode, tmid: userId, name: userName, realname: realName });
                   });
               });
           });

+ 161 - 4
TEAMModelBI/ClientApp/src/view/product/details.vue

@@ -153,6 +153,14 @@
             </div>
           </div>
         </div>
+        <!--学区or城市样式内容-->
+        <div class="data-tables" style="height: 700px;width: 100%;" v-loading="searchLoading"  :element-loading-text="$t(`product.prepareData`)+'...'">
+          <el-auto-resizer>
+            <template #default="{ height, width }">
+              <el-table-v2 v-if="filterdata.length > 0" v-model:sort-state="sortState" :columns="columns" :data="filterdata" :width="width" :height="height" @column-sort="onSort" fixed  :row-class="rowClassName" />
+            </template>
+          </el-auto-resizer>
+        </div>
 
       </div>
       <!--上一个或者下一个-->
@@ -317,17 +325,19 @@
         </el-tab-pane>
       </el-tabs>
     </div>
-    <!--学区or城市样式内容-->
+    
   </div>
 </template>
 <script setup>
-import { ref, getCurrentInstance, defineEmits, computed, onMounted } from 'vue'
+import { ref, reactive, getCurrentInstance, defineEmits, computed, onMounted } from 'vue'
+import { ElMessage, TableV2SortOrder, ElLoading, ElCheckbox,HeaderCellSlotProps,ElPopover,Column, RowClassNameGetter} from 'element-plus'
 import Xlines from '@/components/echarts/Xline.vue'
 import * as echarts from 'echarts'
 let props = defineProps({
   detailsData: Object,
   pattern:Object,
   authDetailsData:Object,
+  postData:Object,
 })
 console.log(props.detailsData, '子组件')
 let value1 = ref('')
@@ -336,6 +346,110 @@ let $myemit = defineEmits(['myback'])
 let { proxy } = getCurrentInstance()
 const activeName = ref('areas')
 let appearState = ref('area')
+const sortState = ref({
+  'deviceCnt':TableV2SortOrder.ASC,
+  'tmidCnt': TableV2SortOrder.ASC,
+  'stuShow': TableV2SortOrder.ASC,
+  'lessonRecord':TableV2SortOrder.ASC,
+  'lessonLengMin':TableV2SortOrder.ASC,
+  'tGreen':TableV2SortOrder.ASC,
+  'date':TableV2SortOrder.ASC,
+})
+let searchLoading=ref(false)
+let sortValue = reactive({})
+let columns = ref([
+  {
+    key: "name",
+    dataKey: "name",//需要渲染当前列的数据字段,如{id:9527,name:'Mike'},则填id
+    title: proxy.$t(`product.name`),//显示在单元格表头的文本
+    width: 100,//当前列的宽度,必须设置
+    fixed: false,//是否固定列
+    // sortable: true,
+    headerClass: 'general',
+  },
+  {
+    key: "date",
+    dataKey: "date",//需要渲染当前列的数据字段,如{id:9527,name:'Mike'},则填id
+    title: proxy.$t(`product.time`),//显示在单元格表头的文本
+    width: 100,//当前列的宽度,必须设置
+    fixed: false,//是否固定列
+     sortable: true,
+    headerClass: 'general',
+  },
+  {
+    key: "schoolId",
+    dataKey: "schoolId",//需要渲染当前列的数据字段,如{id:9527,name:'Mike'},则填id
+    title: proxy.$t(`product.simpleCode`),//显示在单元格表头的文本
+    width: 100,//当前列的宽度,必须设置
+    headerClass: 'general',
+  },
+  {
+    key: "deviceCnt",
+    dataKey: "deviceCnt",//需要渲染当前列的数据字段,如{id:9527,name:'Mike'},则填id
+    title: proxy.$t(`product.classroomNumber`),//显示在单元格表头的文本
+    width: 100,//当前列的宽度,必须设置
+    headerClass: 'general',
+    sortable:true
+  },
+  {
+    key: "tmidCnt",
+    dataKey: "tmidCnt",//需要渲染当前列的数据字段,如{id:9527,name:'Mike'},则填id
+    title: proxy.$t(`product.teacherNumber`),//显示在单元格表头的文本
+    width: 100,//当前列的宽度,必须设置
+    headerClass: 'general',
+    sortable:true
+  },
+  {
+    key: "stuShow",
+    dataKey: "stuShow",//需要渲染当前列的数据字段,如{id:9527,name:'Mike'},则填id
+    title: proxy.$t(`product.studentNumber`),//显示在单元格表头的文本
+    width: 100,//当前列的宽度,必须设置
+    headerClass: 'general',
+    sortable:true
+  },
+  {
+    key: "lessonRecord",
+    dataKey: "lessonRecord",//需要渲染当前列的数据字段,如{id:9527,name:'Mike'},则填id
+    title: proxy.$t(`product.lessonNumber`),//显示在单元格表头的文本
+    width: 100,//当前列的宽度,必须设置
+    headerClass: 'general',
+    sortable:true
+  },
+  {
+    key: "lessonLengMin",
+    dataKey: "lessonLengMin",//需要渲染当前列的数据字段,如{id:9527,name:'Mike'},则填id
+    title: proxy.$t(`product.lessonHours`),//显示在单元格表头的文本
+    width: 100,//当前列的宽度,必须设置
+    headerClass: 'general',
+    sortable:true
+  },
+  {
+    key: "tGreen",
+    dataKey: "tGreen",//需要渲染当前列的数据字段,如{id:9527,name:'Mike'},则填id
+    title: proxy.$t(`product.tGreen`),//显示在单元格表头的文本
+    width: 100,//当前列的宽度,必须设置
+    headerClass: 'general',
+    sortable:true
+  },
+  {
+    key: "tLesson",
+    dataKey: "tLesson",
+    title: proxy.$t(`product.tLesson`),//显示在单元格表头的文本
+    width: 100,//当前列的宽度,必须设置
+    headerClass: 'general',
+    sortable:true
+  },
+  // {
+  //   key: "date",
+  //   dataKey: "date",
+  //   title: "时间",
+  //   width: 100,
+  //   headerClass: 'general',
+  //   sortable:true
+  // },
+
+])
+let filterdata = ref([])
 const options = [
   {
     value: 'guide',
@@ -944,6 +1058,16 @@ let productData = ref([
 ])
 let isShowAuth = true
 
+const onSort = ({key,order}) => {
+  sortValue = {key:key,order:order};
+  console.log(key,order)
+  sortState.value[key] = order
+  let field=key;let modes=order;let allData=filterdata.value
+  modes === 'desc' ? allData.sort((a,b)=> b[field]-a[field]): allData.sort((a,b)=> a[field]-b[field])
+  filterdata.value=allData
+  console.log(allData)
+}
+
   
 
 function init (againvalue) {
@@ -1191,8 +1315,10 @@ function init (againvalue) {
     isShowAuth = false;
   }
  
-  
-
+ if(props.postData.target === "school"){
+  serachToresult()
+ } 
+ 
 
 
 
@@ -1206,6 +1332,37 @@ function detailsSchool(value){
 function backbtn () {
   $myemit('myback', 'default')
 }
+function serachToresult() {  
+  let data = { 
+    "dateFrom": props.postData.dateFrom, 
+    "dateTo": props.postData.dateTo, 
+    "prod": props.postData.prod, 
+    "dateUnit": props.postData.dateUnit, 
+    "target": "tmid", 
+    "tmids": props.postData.tmdids 
+    }   
+  proxy.$api.getUseproduct(data).then(async (res) => {
+    if (res.state === 200) {
+        res.data.forEach((item) => { 
+          item.name = item.tmidInfo.name ? item.tmidInfo.name : proxy.$t(`product.noneYet`) 
+          item.schoolId = item.tmid
+          });
+        res.geo.forEach((item) => {
+          if (item.school.name || (item.school.name === "")) {
+            item.name = item.school.name;
+          } else if (item.geoInfo) {
+            item.name = item.geoInfo;
+          } else {
+            item.name = proxy.$t(`product.noneYet`);
+          }
+        });        
+        filterdata.value = [...res.geo, ...res.data];
+      }  
+     
+  }).catch((err) => {
+    ElMessage.error(proxy.$t(`product.apiErrpr`) + ',' + proxy.$t(`product.getDataError`))
+  })
+}
 init()
 </script>
 <style  scoped>

+ 8 - 2
TEAMModelBI/ClientApp/src/view/product/index.vue

@@ -223,7 +223,7 @@
     </div>
   </div>
   <div v-else-if="showState==='details'">
-    <Details @myback="changStateshow" :detailsData="detailsData" :pattern="pattern" :authDetailsData="authDetailsData"></Details>
+    <Details @myback="changStateshow" :detailsData="detailsData" :pattern="pattern" :authDetailsData="authDetailsData" :postData="postData"></Details>
   </div>
   <div class="dialog-filter" v-if="exportstate">
     <el-dialog v-model="exportstate" :title="$t(`product.exportFilter`)" width="35%">
@@ -769,6 +769,8 @@ let timer = ref()
 let oldKeyword = ref('')
 let adddialog = ref(false)
 let addvalue = ref('')
+let postData = ref({})
+let resData = ref([])
 let addschool = ref({
   name: '',
   id: '',
@@ -817,6 +819,7 @@ function rowClassName({rowIndex}) {
     }
     return ''; // 其他行不做特殊处理
     }
+
 function changeState (value) {
   console.log(value)
   showState.value = 'details'
@@ -824,6 +827,8 @@ function changeState (value) {
   pattern.value.state=clickNum.value.subject === 0 ? 'school':clickNum.value.subject === 1 ? 'area':''
   clickNum.value.subject === 1 ? pattern.value.data=primevalData.value:''
   detailsData.value = value.rowData
+  //let schoolData = resData.value.find(item => item.schoolId === value.rowData.schoolId)
+  postData.value.tmdids = value.rowData.tmidList
   //debugger
 }
 function changStateshow (value) {
@@ -880,6 +885,7 @@ function serachToresult(startTime, endTime, product, schools, unit, target) {
   let data = { "dateFrom": startTime, "dateTo": endTime, "prod": product, "schoolIds": schools, "dateUnit": unit, "target": target }
   console.log(data, '内容')
   console.log(clickNum.value.time, '数字')
+  postData.value = data
   proxy.$api.getUseproduct(data).then(async (res) => {
     console.log(res, 'backPromise')
     console.log(clickNum.value.subject, '选的数字')
@@ -907,7 +913,7 @@ function serachToresult(startTime, endTime, product, schools, unit, target) {
             item.name = proxy.$t(`product.noneYet`);
           }
         });
-
+        resData.value = [...res.data];
         filterdata.value = [...res.geo, ...res.data];
         primevalData.value = [...res.geo, ...res.data];
       }

+ 5 - 0
TEAMModelBI/ClientApp/src/view/schoolServe/school.vue

@@ -308,6 +308,9 @@
       <el-tab-pane label="学校管理员">
         <setSchooladmin :schoolinfo="nowPitchdata"></setSchooladmin>
       </el-tab-pane>
+      <el-tab-pane label="業務負責人">
+        <setSchoolsales :schoolinfo="nowPitchdata"></setSchoolsales>
+      </el-tab-pane>
       <!-- <el-tab-pane :label="$t(`schoolManages.redactServe`)">
                 <Impower :schoolCode="studyPhase"></Impower>
             </el-tab-pane>
@@ -350,6 +353,7 @@ import SetSchool from './setschool.vue'
 import Impower from './impower.vue'
 import Classpower from './classpower.vue'
 import setSchooladmin from './setSchooladmin.vue'
+import setSchoolsales from './setSchoolsales.vue'
 import jwt_decode from 'jwt-decode'
 const siteValue = window.location.host === 'localhost:5001' ? 'cn' : window.location.host === 'bi.teammodel.cn' ? 'cn' : window.location.host === 'bitest.teammodel.cn' ? 'cn' : 'international'
 const optionsData = siteValue === 'cn' ? option_cn : option_gl
@@ -360,6 +364,7 @@ export default {
     Impower,
     Classpower,
     setSchooladmin,
+    setSchoolsales
   },
   setup () {
     let { proxy } = getCurrentInstance()

+ 567 - 0
TEAMModelBI/ClientApp/src/view/schoolServe/setSchoolsales.vue

@@ -0,0 +1,567 @@
+<template>
+  <div class="setadmin" v-if="userRoles.roles.includes('admin')">
+    <div class=" admin-List">
+      <p class="admin-List-title">業務負責人列表</p>
+      <div class="list-item" v-for="(item,index) in salesData" :key="index">
+        <div class="list-item-photo">
+          <div class="headerPhoto">
+            <el-image style="width: 55px; height: 55px;border-radius:50%" :src="item.picture" fit="fill" v-if="item.picture"></el-image>
+            <PersonalPhoto style="cursor: pointer;" :name="item.name" width="55px" height="55px" v-else>
+            </PersonalPhoto>
+          </div>
+        </div>
+        <div class="list-item-left">
+          <p class="list-item-left-name">{{item.name}}</p>
+          <p class="list-item-left-content"><span class="list-item-left-title">醍摩豆账号:</span><span>{{item.id}}</span></p>
+          <!-- <p class="list-item-left-content"><span class="list-item-left-title">手机号:</span><span>{{item.mobile}}</span></p> -->
+        </div>
+        <div class="list-item-right">
+          <div class="deletebtn" @click="nowUser=item,removeData.state=true">
+            <svg class="undock" aria-hidden="true">
+              <use xlink:href="#icon-yichuhaoyou"></use>
+            </svg>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="setadmin-add">
+      <p class="admin-List-title">添加学校管理者</p>
+      <div class="admin-searchbox">
+        <div class="admin-text">
+          <div class="admin-text-box" v-if="searchState ===false">
+            <div class="hint"><span>请输入用户</span><span class="emphasize">手机号、醍摩豆账号</span><span>等相关信息进行搜索</span></div>
+            <div class="search-box">
+              <!-- <input class="search-txt" type="text" id="serachname" :value="filterText" placeholder="搜索相关人名" @oninput="personnelSearch()" @compositionend="personnelSearch()"> -->
+              <el-input v-model="adminfilter" placeholder="输入手机号、醍摩豆账号搜索" class="search-txt" />
+              <svg class="serachbox-empty" aria-hidden="true" v-if="adminfilter !==''" @click="adminfilter=''">
+                <use xlink:href="#icon-qingkong"></use>
+              </svg>
+              <svg class="serachbox-iconinfo" aria-hidden="true" v-else>
+                <use xlink:href="#icon-sousuotianchong"></use>
+              </svg>
+            </div>
+            <div class="serachbox-notdata" v-if="searchNodata">
+              <p>未搜索到相关用户信息,请检验后重新输入搜索</p>
+            </div>
+            <div class="submitbtn">
+              <el-button type="primary" @click=serachPersonnel(adminfilter) class="anewbox-btn blue">搜索人员</el-button>
+            </div>
+          </div>
+          <div class="admin-text-box" v-else-if="searchState ===true">
+            <div class="serarch-result">
+              <el-image style="width: 140px; height: 140px;border-radius:50%" :src="searchData.picture" fit="fill" v-if="searchData.picture"></el-image>
+              <PersonalPhoto style="cursor: pointer;" :name="searchData.name" width="140px" height="140px" fontSize="18px" class="pigpicture" v-else></PersonalPhoto>
+              <div class="result-text">
+                <p class="result-text-name">{{searchData.name}}</p>
+                <p class="result-text-phone">{{searchData.id}}</p>
+                <p class="result-text-phone">{{searchData.mobile}}</p>
+              </div>
+              <div class="anewbox">
+                <el-button type="primary" @click="addAdmin()" class="anewbox-btn">加入到學校業務負責人</el-button>
+                <el-button type="primary" class="anewbox-btn" @click="searchState=false,searchData='',adminfilter=''">重新检索人员</el-button>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+  <div class="setadminbox" v-else>
+    <p class="setadmin-concealed-title">管理员列表:</p>
+    <div class="setadmin-concealed">
+      <div class="adminlist-box">
+        <div class="adminList-item" v-for="item in salesData">
+          <div class="headportrait">
+            <el-image style="" :src="item.picture" fit="fill" v-if="item.picture"></el-image>
+            <PersonalPhoto style="cursor: pointer;" :name="item.name" width="103px" height="103px" fontSize="20px" v-else></PersonalPhoto>
+          </div>
+          <div class="adminList-item-name"><span>{{item.name}}</span></div>
+          <div class="adminList-item-id"><span>醍摩豆ID:</span><span>{{item.id}}</span></div>
+        </div>
+      </div>
+    </div>
+  </div>
+  <div class="inquirybox">
+    <el-dialog v-model="removeData.state" title="移除学校管理员" width="30%" center>
+      <span>
+        请问您确定从当前学校管理员列表中移除{{nowUser.name}}吗?
+      </span>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="removeData.state = false">取消</el-button>
+          <el-button type="primary" @click="removeadmin(1)">確定</el-button>          
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script>
+import { ref, getCurrentInstance, watch } from 'vue'
+import { Search } from '@element-plus/icons-vue'
+import { ElMessage, ElLoading, ElMessageBox } from 'element-plus'
+import jwt_decode from 'jwt-decode'
+export default {
+  props: {
+    schoolinfo: {
+      default: '',
+    }
+  },
+  components: {
+    Search,
+  },
+  setup (props) {
+    console.log(props, '设置管理员的值')
+    const userRoles = jwt_decode(JSON.parse(localStorage.getItem('id_token')))
+    let adminfilter = ref('')
+    let salesData = ref([])
+    let { proxy } = getCurrentInstance()
+    let searchData = ref()
+    let searchState = ref(false)
+    let searchNodata = ref(false)
+    let schoolValue = ref()
+    let removeData = ref({
+      show: false,
+      type: 0,
+    })
+    let nowUser = ref({})
+    function getSales () {
+      let data = { scId: schoolValue.value.id }
+      proxy.$api
+        .getSchoolSales(data)
+        .then((res) => {
+          console.log(res, 'ADMINSCHOOL')
+          res.state === 200 ? (salesData.value = res.scManages) : ''
+        })
+        .catch((err) => {
+          ElMessage.error('系统管理员获取API异常')
+        })
+    }
+
+    function serachPersonnel (value) {
+      let data = [value]
+      proxy.$api
+        .searchPersonnel(data)
+        .then((res) => {
+          res.state === 200 && res.infos.length !== 0 ?
+            ((searchData.value = res.infos[0]), (searchState.value = true), (searchNodata.value = false)) :
+            res.state === 200 && res.infos.length === 0 ?
+              (searchNodata.value = true) :
+              ''
+        })
+        .catch((err) => {
+          ElMessage.error('人员搜索异常')
+        })
+    }
+
+    function removeadmin (value) {
+      console.log(value, nowUser)
+      // if (salesData.value.length <= 1) {
+      //   return ElMessage.info('管理员至少存在一位,无法移除当前人员')
+      // }
+      // ElMessageBox.confirm(`请问您确定从当前学校管理员列表中移除  ${value.name}  吗?`, '移除学校管理员', {
+      //   confirmButtonText: proxy.$t(`commonMsg.confirm`),
+      //   cancelButtonText: proxy.$t(`commonMsg.closes`),
+      //   type: 'error',
+      //   center: true,
+      // }).then(() => {
+      // })
+      let data = { tmdId: nowUser.value.id, scIds: [schoolValue.value.id] }
+      proxy.$api
+        .deleteSchoolSales(data)
+        .then((res) => {
+          res.state === 200 ?
+            (ElMessage.success('操作成功'), removeData.value.state = false, getSales()) :
+            res.state === 403 ?
+              ElMessage.error('管理员至少存在一位,无法移除当前人员') :
+              res.state === 1 ?
+                ElMessage.error('无法删除自己,操作失败') :
+                ''
+        })
+        .catch((error) => {
+          ElMessage.error('删除失败')
+        })
+    }
+
+    function addAdmin () {
+      let data = searchData.value ? {
+        tmdId: searchData.value.id,
+        tmdName: searchData.value.name,
+        tmdPic: searchData.value.picture,
+        scId: schoolValue.value.id,
+        scName: schoolValue.value.name,
+        scPic: schoolValue.value.picture,
+        scAreaId: schoolValue.value.areaId,
+      } : ''
+      proxy.$api
+        .setSchoolSales(data)
+        .then((res) => {          
+          res.state === 200 ? (ElMessage.success('操作成功'), getSales()) : res.state === 409 ? ElMessage.success('已是该校業務負責人,请勿重复添加') : ''
+        })
+        .catch((error) => {
+          ElMessage.error('添加学校業務負責人失败')
+        })
+    }
+    watch(
+      props,
+      (newProps) => {
+        console.log(newProps, '值')
+        newProps.schoolinfo ? (schoolValue.value = newProps.schoolinfo, getSales()) : ''
+        console.log(schoolValue.value)
+      },
+      { immediate: true, deep: true }
+    )
+    return {
+      adminfilter,
+      salesData,
+      getSales,
+      serachPersonnel,
+      searchData,
+      searchState,
+      removeadmin,
+      addAdmin,
+      searchNodata,
+      userRoles,
+      removeData,
+      nowUser
+    }
+  },
+}
+</script>
+<style scoped>
+.setadmin {
+  width: 100%;
+  /* padding: 0% 1%; */
+  display: flex;
+  line-height: 20px;
+  background-color: #fff;
+  margin: 0 auto;
+}
+.setadminbox {
+  width: 100%;
+  line-height: 20px;
+  background-color: #fff;
+  margin: 0 auto;
+}
+.admin-List {
+  width: 30%;
+  text-align: left;
+  height: 89vh;
+  border-right: 1px solid #ccc;
+  overflow: auto;
+}
+
+.setadmin-add {
+  width: 70%;
+}
+
+.admin-List-title {
+  font-size: 14px;
+  color: #636e72;
+  line-height: 40px;
+  padding-left: 2%;
+  text-align: left;
+}
+
+.list-item {
+  padding: 5px 20px 5px 15px;
+  display: flex;
+}
+
+.list-item-left {
+  width: 65%;
+  height: 100px;
+}
+
+.list-item-right {
+  width: 15%;
+  height: 100px;
+  line-height: 70px;
+  text-align: center;
+  display: flex;
+  align-items: center;
+}
+
+.list-item-left-name {
+  font-size: 20px;
+  margin-bottom: 10%;
+  font-weight: 600;
+  margin-top: 5%;
+}
+
+.list-item-left-content {
+  font-size: 14px;
+  margin: 5px 0px;
+}
+
+.deletebtn {
+  width: 40px;
+  height: 40px;
+  background-color: #8395a7;
+  position: relative;
+  border-radius: 50%;
+  opacity: 0;
+  cursor: pointer;
+}
+
+.undock {
+  position: absolute;
+  width: 20px;
+  height: 20px;
+  top: 25%;
+  left: 28%;
+}
+
+.list-item-left-title {
+  color: #95a5a6;
+}
+
+.list-item:hover {
+  background-color: #c8d6e5;
+}
+
+.list-item:hover .deletebtn {
+  opacity: 1;
+}
+
+.admin-searchbox {
+  position: relative;
+  padding-left: 20px;
+  height: calc(100% - 155px);
+}
+
+.admin-text {
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.admin-text-box {
+  width: 450px;
+  text-align: center;
+}
+
+.admin-serach {
+  margin-top: 5%;
+}
+
+.serachicon {
+  position: absolute;
+  width: 20px;
+  height: 20px;
+  top: 0%;
+}
+
+.search-box {
+  /* position: absolute;
+    top: 9%;
+    left: 15%; */
+  /* transform: translate(0%, -50%); */
+  background: #bdc3c7;
+  height: 40px;
+  border-radius: 40px;
+  padding: 10px;
+  display: inline-block;
+  vertical-align: top;
+  /* justify-content: center; */
+  margin-top: 12px;
+  margin-left: 1%;
+}
+
+.search-btn {
+  float: right;
+  width: 40px;
+  height: 40px;
+  border-radius: 50%;
+  background: #2f3640;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  transition: 0.4s;
+}
+
+.search-txt {
+  border: none;
+  background: none;
+  outline: none;
+  float: left;
+  padding: 0;
+  color: white;
+  font-size: 16px;
+  transition: 0.4s;
+  line-height: 20px;
+  width: 400px;
+}
+
+.serachbox-empty {
+  width: 20px;
+  height: 20px;
+  vertical-align: 1.3em;
+  fill: currentColor;
+  overflow: hidden;
+  color: #95a5a6;
+  margin-right: 5px;
+  cursor: pointer;
+  float: right;
+  display: inline-block;
+}
+
+.serachbox-iconinfo {
+  width: 20px;
+  height: 20px;
+  vertical-align: 1.2em;
+  fill: currentColor;
+  overflow: hidden;
+  float: right;
+}
+
+.hint {
+  font-size: 14px;
+}
+
+.emphasize {
+  font-size: 16px;
+  color: #74b9ff;
+}
+
+.submitbtn {
+  margin-top: 5%;
+}
+
+.list-item-photo {
+  width: 20%;
+  text-align: center;
+  line-height: 100px;
+  display: flex;
+  align-items: center;
+}
+
+.pigpicture {
+  margin-bottom: 5%;
+}
+
+.result-text-name {
+  font-size: 16px;
+  color: #333;
+  font-weight: 600;
+  margin-bottom: 5px;
+}
+
+.result-text-phone {
+  font-size: 14px;
+  color: #7f8c8d;
+  margin: 5px;
+}
+
+.anewbox {
+  margin-top: 2%;
+  margin-bottom: 2%;
+}
+
+.anewbox-btn {
+  width: 180px;
+  display: block;
+  margin: 0 auto;
+  margin-bottom: 10px;
+  background-color: #12cbc4;
+  color: #fefefe;
+  border: none;
+  font-weight: 900;
+}
+
+.blue {
+  width: 200px;
+  background-color: #00a8ff;
+}
+
+.serachbox-notdata p {
+  width: 100%;
+  font-size: 14px;
+  color: red;
+  margin-bottom: 5px;
+  margin-top: 10px;
+}
+.adminList-item {
+  border: 1px solid #ccc;
+  padding: 1%;
+  width: 12%;
+  text-align: center;
+  margin: 1%;
+  border-radius: 10px;
+}
+.adminList-item-name {
+  font-size: 20px;
+  font-weight: 700;
+  line-height: 35px;
+  color: #303133;
+}
+.adminList-item-id {
+  font-size: 14px;
+  line-height: 35px;
+  color: #909399;
+}
+.setadmin-concealed {
+  width: 100%;
+  line-height: 20px;
+  overflow: auto;
+}
+.adminlist-box {
+  width: 100%;
+  display: flex;
+  flex-wrap: wrap;
+}
+.setadmin-concealed-title {
+  padding-left: 1%;
+  font-size: 14px;
+  margin-bottom: 5px !important;
+  line-height: 20px;
+  text-align: left;
+  color: #95a5a6;
+}
+</style>
+<style>
+.admin-text-box .search-box .el-input__inner {
+  opacity: 1;
+  width: 100%;
+  /* padding: 0px; */
+  height: 30px !important;
+  line-height: 30px;
+  border: 0px;
+  background-color: #bdc3c7;
+  vertical-align: top;
+  border-radius: 8px;
+  padding-bottom: 10px;
+  color: #333 !important;
+}
+
+.submitbtn .el-button--small {
+  padding: 9px 62px;
+  font-size: 14px;
+}
+.headportrait .el-image {
+  width: 55%;
+  border-radius: 50%;
+}
+.headerPhoto .el-image {
+  display: block;
+}
+.search-box .el-input__wrapper {
+  width: 100%;
+  padding: 0px;
+  /* background: #bdc3c7; */
+  background: transparent;
+  box-shadow: 0px 0px 0px 0px transparent;
+}
+.search-box .el-input__wrapper .is-focues {
+  border: 0px;
+  box-shadow: 0px 0px 0px 0px transparent;
+}
+.search-box .el-input__wrapper:hover {
+  border: 0px;
+  box-shadow: 0px 0px 0px 0px transparent;
+}
+.inquirybox .el-dialog {
+  line-height: 40px;
+  border-radius: 5px;
+}
+</style>

+ 257 - 0
TEAMModelBI/Controllers/BISchool/SchoolController.cs

@@ -2873,6 +2873,19 @@ namespace TEAMModelBI.Controllers.BISchool
                 {
                     teachers.Add(item);
                 }
+                string sqlTeacherEx = $"SELECT c.id, c.name as realName FROM c WHERE ARRAY_CONTAINS({teaIdListStr}, c.id, true)";
+                await foreach (PurchaseTeacher item in client.GetContainer("Core", "ID2").GetItemQueryIteratorSql<PurchaseTeacher>(queryText: sqlTeacherEx, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("base-ex") }))
+                {
+                    PurchaseTeacher teacherExist = teachers.FirstOrDefault(t => t.id.Equals(item.id));
+                    if(teacherExist == null)
+                    {
+                        teachers.Add(item);
+                    }
+                    else
+                    {
+                        teacherExist.realName = item.realName;
+                    }
+                }
 
                 return Ok(new { geo = purchaseSeatsList, school = purchaseSchoolSeatsDic, teacher = teachers });
             }
@@ -2934,6 +2947,249 @@ namespace TEAMModelBI.Controllers.BISchool
 
         #endregion
 
+        #region 業務負責人功能
+        /// <summary>
+        /// 依据学校ID查業務負責人信息    //已对接
+        /// </summary>
+        /// <param name="jsonElement"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [HttpPost("get-scsales")]
+        public async Task<IActionResult> GetSchoolsales(JsonElement jsonElement)
+        {
+            if (!jsonElement.TryGetProperty("scId", out JsonElement scId)) return BadRequest();
+            //jsonElement.TryGetProperty("site", out JsonElement site);//分开部署,就不需要,一站多用时,取消注释
+            var cosmosClient = _azureCosmos.GetCosmosClient();
+            ////分开部署,就不需要,一站多用时,取消注释
+            //if ($"{site}".Equals(BIConst.Global))
+            //    cosmosClient = _azureCosmos.GetCosmosClient(name: BIConst.Global);
+
+            List<BaseInfo> scManages = new();
+            await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryIteratorSql<BaseInfo>(queryText: $"select c.id,c.name,c.picture from c where ARRAY_CONTAINS(c.roles,'sales',true) and c.code='Teacher-{scId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{scId}") }))
+            {
+                scManages.Add(item);
+            }
+
+            return Ok(new { state = RespondCode.Ok, scManages });
+        }
+
+        /// <summary>
+        /// 设置業務負責人   
+        /// </summary>
+        /// <param name="jsonElement"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [AuthToken(Roles = "admin,rdc")]
+        [HttpPost("set-sales")]
+        public async Task<IActionResult> SetSchoolsales(JsonElement jsonElement)
+        {
+            try
+            {
+                var (_tmdId, _tmdName, _, _, _, _) = HttpJwtAnalysis.JwtXAuthBI(HttpContext.GetXAuth("AuthToken"), _option);
+
+                if (!jsonElement.TryGetProperty("tmdId", out JsonElement tmdId)) return BadRequest();
+                if (!jsonElement.TryGetProperty("tmdName", out JsonElement tmdName)) return BadRequest();
+                jsonElement.TryGetProperty("tmdPic", out JsonElement picture);
+                if (!jsonElement.TryGetProperty("scId", out JsonElement schoolId)) return BadRequest();
+                if (!jsonElement.TryGetProperty("scName", out JsonElement schoolName)) return BadRequest();
+                jsonElement.TryGetProperty("scAreaId", out JsonElement scAreaId);
+                jsonElement.TryGetProperty("scPic", out JsonElement scpicture);
+                //jsonElement.TryGetProperty("site", out JsonElement site);//分开部署,就不需要,一站多用时,取消注释
+
+                var cosmosClient = _azureCosmos.GetCosmosClient();
+                var tableClient = _azureStorage.GetCloudTableClient();
+                var blobClient = _azureStorage.GetBlobContainerClient(containerName: "0-public");
+
+                ////分开部署,就不需要,一站多用时,取消注释
+                //if ($"{site}".Equals(BIConst.Global))
+                //{
+                //    cosmosClient = _azureCosmos.GetCosmosClient(name: BIConst.Global);
+                //    tableClient = _azureStorage.GetCloudTableClient(BIConst.Global);
+                //    blobClient = _azureStorage.GetBlobContainerClient(containerName: "0-public", BIConst.Global);
+                //}
+                // 保存操作记录的文字
+                StringBuilder strMsg = new($"{_tmdName}【{_tmdId}】账户将{tmdName}【{tmdId}】");
+                string areaId = null;
+                if (string.IsNullOrEmpty($"{scAreaId}"))
+                {
+                    // 取出學校資訊
+                    var resScInfo = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync($"{schoolId}", new PartitionKey("Base"));
+
+                    if (resScInfo.StatusCode == HttpStatusCode.OK)
+                    {
+                        using var tchJson = await JsonDocument.ParseAsync(resScInfo.Content);
+                        School scInfo = tchJson.ToObject<School>();
+                        areaId = scInfo.areaId;
+                    }
+                    else areaId = $"{scAreaId}";
+                }
+                else { areaId = $"{scAreaId}"; }
+
+                Teacher teacher = new();
+                SchoolTeacher schoolTeacher = new();
+                var resTeacher = await cosmosClient.GetContainer("TEAMModelOS", "Teacher").ReadItemStreamAsync($"{tmdId}", new PartitionKey($"Base"));
+                if (resTeacher.StatusCode == HttpStatusCode.OK)
+                {
+                    using var tchJson = await JsonDocument.ParseAsync(resTeacher.Content);
+                    teacher = tchJson.ToObject<Teacher>();
+
+                    var existArea = teacher.schools.Find(f => f.schoolId.Equals($"{schoolId}"));
+                    if (existArea == null)
+                    {
+                        teacher.schools.Add(new Teacher.TeacherSchool { schoolId = $"{schoolId}", name = $"{schoolName}", status = "join", time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), picture = string.IsNullOrEmpty($"{scpicture}") ? "" : $"{scpicture}", areaId = areaId });
+                        teacher = await cosmosClient.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Teacher>(teacher, teacher.id, new PartitionKey("Base"));
+                        strMsg.Append($"账户基础信息添加学校,{schoolName}【{schoolId}】;");
+                    }
+                }
+                else
+                {
+                    teacher.id = $"{tmdId}";
+                    teacher.name = $"{tmdName}";
+                    teacher.picture = string.IsNullOrEmpty($"{picture}") ? "" : $"{picture}";
+                    teacher.pk = "Base";
+                    teacher.code = "Base";
+                    teacher.size = 1;
+                    teacher.createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+                    //教师存在,在该教师信息中添加要管理的学校信息
+                    teacher.schools.Add(new Teacher.TeacherSchool { schoolId = $"{schoolId}", name = $"{schoolName}", status = "join", time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), picture = string.IsNullOrEmpty($"{scpicture}") ? "" : $"{scpicture}", areaId = areaId });
+                    teacher = await cosmosClient.GetContainer(Constant.TEAMModelOS, "Teacher").CreateItemAsync<Teacher>(teacher, new PartitionKey("Base"));
+                    strMsg.Append($"添加基础信息,并添加学校,{schoolName}【{schoolId}】;");
+                }
+
+                if (teacher != null)
+                {
+                    var response = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync($"{tmdId}", new PartitionKey($"Teacher-{schoolId}"));
+                    if (response.StatusCode == HttpStatusCode.OK)
+                    {
+                        using var json = await JsonDocument.ParseAsync(response.Content);
+                        schoolTeacher = json.ToObject<SchoolTeacher>();
+                       
+                        
+                        
+                        if (!schoolTeacher.roles.Contains("sales"))
+                        {                            
+                            schoolTeacher.roles.Add("sales");
+                            schoolTeacher.status = "join";
+                            schoolTeacher = await cosmosClient.GetContainer("TEAMModelOS", "School").ReplaceItemAsync<SchoolTeacher>(schoolTeacher, schoolTeacher.id, new PartitionKey($"Teacher-{schoolId}"));
+
+                            #region 同時更新teacher的school.status
+                            var school = teacher.schools.Find(f => f.schoolId.Equals($"{schoolId}"));
+                            school.status = "join";
+                            await cosmosClient.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Teacher>(teacher, teacher.id, new PartitionKey("Base"));
+                            #endregion
+                            strMsg.Append($"并设置管理员,学校信息{schoolName}【{schoolId}】");
+                        }
+                        else
+                            return Ok(new { state = RespondCode.Conflict, schoolTeacher = "已经是该校的業務負責人" });
+                    }
+                    else
+                    {
+                        schoolTeacher = new()
+                        {
+                            id = $"{tmdId}",
+                            code = $"Teacher-{schoolId}",
+                            roles = new List<string> { "admin", "teacher" },
+                            job = "管理员",
+                            name = $"{tmdName}",
+                            picture = string.IsNullOrEmpty($"{picture}") ? "" : $"{picture}",
+                            status = "join",
+                            createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+                            pk = "Teacher",
+                            ttl = -1
+                        };
+
+                        schoolTeacher = await cosmosClient.GetContainer(Constant.TEAMModelOS, "School").CreateItemAsync<SchoolTeacher>(schoolTeacher, new PartitionKey($"Teacher-{schoolId}"));
+                        strMsg.Append($"并新建学校管理员,学校信息{schoolName}【{schoolId}】");
+                    }
+                }
+                //保存操作记录
+                await AzureStorageBlobExtensions.SaveBILog(blobClient, tableClient, "schoolTeacher-add", strMsg.ToString(), _dingDing, httpContext: HttpContext);
+
+                return Ok(new { state = RespondCode.Ok, teacher, schoolTeacher });
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"BI,{_option.Location}  /schoolcheck/set-schoolme \n {ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
+                return BadRequest();
+            }
+        }
+
+        /// <summary>
+        /// 移除業務負責人   //已对接
+        /// </summary>
+        /// <param name="jsonElement"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+        [AuthToken(Roles = "admin,rdc")]
+        [HttpPost("del-sales")]
+        public async Task<IActionResult> DelSchoolsales(JsonElement jsonElement)
+        {
+            var (_tmdId, _tmdName, _, _, _, _) = HttpJwtAnalysis.JwtXAuthBI(HttpContext.GetXAuth("AuthToken"), _option);
+            if (!jsonElement.TryGetProperty("tmdId", out JsonElement tmdId)) return BadRequest();
+            if (!jsonElement.TryGetProperty("scIds", out JsonElement _scIds)) return BadRequest();
+            //bool rmvFromSc = (jsonElement.TryGetProperty("rmvFromSc", out JsonElement rmvFromScJobj)) ? rmvFromScJobj.GetBoolean() : false; //rmvFromSc Bool true:移除SchoolTeacher document false:只移除roles中"admin"身分
+            //jsonElement.TryGetProperty("site", out JsonElement site);//分开部署,就不需要,一站多用时,取消注释
+
+            List<string> scIds = _scIds.ToObject<List<string>>();
+            var cosmosClient = _azureCosmos.GetCosmosClient();
+            var tableClient = _azureStorage.GetCloudTableClient();
+            var blobClient = _azureStorage.GetBlobContainerClient(containerName: "0-public");
+            ////分开部署,就不需要,一站多用时,取消注释
+            //if ($"{site}".Equals(BIConst.Global))
+            //    cosmosClient = _azureCosmos.GetCosmosClient(name: BIConst.Global);
+            List<string> existScId = new();
+            StringBuilder strMsg = new($"{_tmdName}【{_tmdId}】管理员账户将{tmdId}的账户移除業務負責人:");
+            //老師
+            //if (rmvFromSc) //從老師的學校列表移除該學校
+            //{
+            //    var response = await cosmosClient.GetContainer("TEAMModelOS", "Teacher").ReadItemStreamAsync($"{tmdId}", new PartitionKey("Base"));
+            //    if (response.StatusCode == HttpStatusCode.OK)
+            //    {
+            //        using var json = await JsonDocument.ParseAsync(response.Content);
+            //        Teacher teacher = json.ToObject<Teacher>();
+            //        teacher.schools.RemoveAll(s => scIds.Contains(s.schoolId));
+            //        await cosmosClient.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Teacher>(teacher, teacher.id, new PartitionKey("Base"));
+            //    }
+            //}
+            //學校
+            SchoolTeacher scTeacher = null;
+            foreach (var scId in scIds)
+            {
+                var response = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync($"{tmdId}", new PartitionKey($"Teacher-{scId}"));
+                if (response.StatusCode == HttpStatusCode.OK)
+                {
+                    //if (rmvFromSc) //將該老師從學校的老師名單中移除
+                    //{
+                    //    //將該老師從學校的老師名單中移除
+                    //    await cosmosClient.GetContainer("TEAMModelOS", "School").DeleteItemAsync<SchoolTeacher>($"{tmdId}", new PartitionKey($"Teacher-{scId}"));
+                    //    strMsg.Append($"{scId},");
+                    //}
+                    //else
+                    //{
+                        using var json = await JsonDocument.ParseAsync(response.Content);
+                        scTeacher = json.ToObject<SchoolTeacher>();
+                        if (scTeacher.roles.Contains("sales"))
+                        {
+                            scTeacher.roles.Remove("sales");
+                            strMsg.Append($"{scId},");
+                            scTeacher = await cosmosClient.GetContainer("TEAMModelOS", "School").ReplaceItemAsync<SchoolTeacher>(scTeacher, scTeacher.id, new PartitionKey($"Teacher-{scId}"));
+                        }
+                        else
+                            existScId.Add(scId);
+                    //}
+                }
+                else
+                    existScId.Add(scId);
+            }
+
+            //保存操作记录
+            await AzureStorageBlobExtensions.SaveBILog(blobClient, tableClient, "schoolTeacher-updatee", strMsg.ToString(), _dingDing, httpContext: HttpContext);
+
+            return Ok(new { state = RespondCode.Ok, existScId });
+        }
+
+        #endregion
+
         /// <summary>
         /// 未加入区域的学校
         /// </summary>
@@ -3006,6 +3262,7 @@ namespace TEAMModelBI.Controllers.BISchool
         {
             public string id { get; set; }
             public string name { get; set; }
+            public string realName { get; set; }
         }
     }
 }

+ 3 - 3
TEAMModelBI/TEAMModelBI.csproj

@@ -65,9 +65,9 @@
 		<SpaRoot>ClientApp\</SpaRoot>
 		<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
 		<UserSecretsId>078b5d89-7d90-4f6a-88fc-7d96025990a8</UserSecretsId>
-		<Version>5.2501.22</Version>
-		<AssemblyVersion>5.2501.22.1</AssemblyVersion>
-		<FileVersion>5.2501.22.1</FileVersion>
+		<Version>5.2502.5</Version>
+		<AssemblyVersion>5.2502.5.1</AssemblyVersion>
+		<FileVersion>5.2502.5.1</FileVersion>
 		<Description>TEAMModelBI(BI)</Description>
 		<PackageReleaseNotes>BI版本说明版本切换标记2022000908</PackageReleaseNotes>
 		<PackageId>TEAMModelBI</PackageId>

+ 0 - 3
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/IES.ExamClient.esproj

@@ -1,3 +0,0 @@
-<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.1738743">
-
-</Project>

+ 40 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/IES.ExamClient.njsproj

@@ -0,0 +1,40 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
+  <PropertyGroup>
+    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
+    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+    <Name>IES.ExamClient</Name>
+    <RootNamespace>IES.ExamClient</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>7fa6faa3-e176-4e56-b542-2e8176850924</ProjectGuid>
+    <ProjectHome>.</ProjectHome>
+    <StartupFile>app.js</StartupFile>
+    <StartWebBrowser>False</StartWebBrowser>
+    <SearchPath>
+    </SearchPath>
+    <WorkingDirectory>.</WorkingDirectory>
+    <OutputPath>.</OutputPath>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+    <ProjectTypeGuids>{3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}</ProjectTypeGuids>
+    <StartWebBrowser>false</StartWebBrowser>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+    <DebugSymbols>true</DebugSymbols>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
+    <DebugSymbols>true</DebugSymbols>
+  </PropertyGroup>
+  <ItemGroup>
+    <Content Include="app.原始备份.js" />
+    <Content Include="app.js" />
+    <Content Include="header.bmp" />
+    <Content Include="logo.ico" />
+    <Content Include="sidebar.bmp" />
+    <Content Include="package.json" />
+    <Content Include="README.md" />
+  </ItemGroup>
+  <Import Project="$(VSToolsPath)\Node.js Tools\Microsoft.NodejsToolsV2.targets" />
+</Project>

+ 3 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/README.md

@@ -0,0 +1,3 @@
+# IES.ExamClient
+
+

+ 133 - 64
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/app.js

@@ -1,76 +1,123 @@
-const { app, BrowserWindow, Menu } = require('electron/main');
-const { spawn } = require('child_process');
-const net = require('net');
-//const si = require('systeminformation');
-//const startDotnet = () => {
-//    return new Promise((resolve, reject) => {
-//        dotnetProcess = spawn('dotnet', ['run', '--project', '../IES.ExamServer/IES.ExamServer.csproj']);
-//        dotnetProcess.stdout.on('data', (data) => {
-//            console.log(`stdout: ${data}`);
-//            // 假设dotnet服务启动后会输出包含"Now listening on"的日志
-//            if (data.toString().includes('Now listening on')) {
-//                resolve();
-//            }
-//        });
-
-//        dotnetProcess.stderr.on('data', (data) => {
-//            console.log(`stderr: ${data}`);
-//            reject(new Error(`Dotnet process error: ${data}`));
-//        });
-
-//        dotnetProcess.on('close', (code) => {
-//            console.log(`child process exited with code ${code}`);
-//            if (code !== 0) {
-//                reject(new Error(`Dotnet process exited with code ${code}`));
-//            }
-//        });
-//    });
-//};
-// 获取网卡信息
-// npm start  启动命令
-const platform = process.platform;
-if (platform === 'win32') {
-    console.log('Running on Windows');
-} else if (platform === 'linux') {
-    console.log('Running on Linux');
-} else if (platform === 'darwin') {
-    console.log('Running on macOS');
+const { app, BrowserWindow } = require('electron');
+const { exec } = require('child_process');
+const path = require('path');
+const axios = require('axios');
+const https = require('https');
+const fs = require('fs');
+const AdmZip = require('adm-zip'); // 用于解压 zip 文件
+// 忽略证书的检测
+//app.commandLine.appendSwitch('ignore-certificate-errors'); 原本用于解决SignalR 自签名证书不能使用的问题,将 "@microsoft/signalr": "^8.0.7", 改为  "@microsoft/signalr": "^7.0.14",
+const cert = fs.readFileSync('server\\Configs\\cer\\cert.pem');
+const agent = new https.Agent({
+    ca: cert,
+    rejectUnauthorized: true, // 启用证书验证
+});
+// 定义 Web API 的启动路径和健康检测 URL
+let serverPath;
+if (app.isPackaged) {
+    // 打包后的路径
+    serverPath = path.dirname(app.getPath('exe')) ;
 } else {
-    console.log('Running on an unknown platform');
+    // 开发环境的路径
+    serverPath = __dirname;
 }
+console.log('Server path:', serverPath);// 打印路径进行检查
+const baseUrl = 'https://exam.habook.local:8888';
+let serverProcess;
+// 读取本地 appsettings.json 文件
+const getLocalVersion = () => {
+    const appSettingsPath = path.join(serverPath, 'server', 'appsettings.json');
+    try {
+        const appSettings = JSON.parse(fs.readFileSync(appSettingsPath, 'utf-8'));
+        return appSettings.Version; // 假设 version 字段存储版本号
+    } catch (error) {
+        console.error('Error reading appsettings.json:', error);
+        return null;
+    }
+};
+// 启动 Web API 的函数
 
-//si.networkInterfaces()
-//    .then(data => {
-//        console.log('Network Interfaces:', data);
-//        //mainWindow.webContents.send('network-interfaces', data);
-//    })
-//    .catch(error => {
-//        console.error('Error fetching network interfaces:', error);
-//    });
+// 检查服务器健康状态的函数
+const checkServerHealth = async () => {
+    try {
+        const response = await axios.get(`${baseUrl}/index/health`, {
+            httpsAgent: agent
+        });
+        if (response.status === 200) {
+            console.log('Server is up and running!');
+            return true;
+        }
+    } catch (error) {
+        console.log('Server is not running yet.');
+        return false;
+    }
+};
+const startServer = () => {
+    return new Promise((resolve, reject) => {
+        serverProcess = exec(path.join(serverPath, 'server', 'IES.ExamServer.exe'), {
+            cwd: `${serverPath}/server` // 设置工作目录为 server 目录
+            , stdio: 'inherit'
+        });
+        // 监听标准输出
+        serverProcess.stdout.on('data', (data) => {
+            console.log(`Server stdout: ${data}`);
+        });
+
+        // 监听标准错误输出
+        serverProcess.stderr.on('data', (data) => {
+            console.log(`Server stderr: ${data}`);
+        });
+
+        // 监听进程退出事件
+        serverProcess.on('close', (data) => {
+            console.log(`Server process exited with code ${data}`);
+            reject(new Error(`Server process exited with code ${data}`));
+        });
+        // 等待 Web API 启动成功
+        const checkHealth = async () => {
+            try {
+                const response = await axios.get(`${baseUrl}/index/health`, {
+                    httpsAgent: agent
+                });
+                if (response.status === 200) {
+                    console.log('Server is up and running!');
+                    resolve();
+                }
+            } catch (error) {
+                console.log('Waiting for server to start...');
+                setTimeout(checkHealth, 1000); // 每隔 1 秒检查一次
+            }
+        };
+        checkHealth();
+    });
+};
+
+// 创建 Electron 窗口的函数
 const createWindow = async () => {
     try {
-        //await startDotnet();
+        const isServerRunning = await checkServerHealth();
+        if (!isServerRunning) {
+            await startServer(); // 启动 Web API
+        }
         const win = new BrowserWindow({
             width: 800,
-            height: 600
-            //webPreferences: {
-            //    nodeIntegration: true,
-            //    contextIsolation: false,
-            //},
+            height: 600,
+            webPreferences: {
+                nodeIntegration: true,
+                contextIsolation: false,
+            },
         });
-        // 忽略证书错误
-        //win.webContents.session.setCertificateVerifyProc((request, callback) => {
-        //    callback(0); // 允许所有证书
-        //});
         win.maximize();
-        win.loadURL('https://exam.habook.local:8001');
+        win.loadURL(baseUrl, {
+           agent: agent
+        });
+        
     } catch (error) {
-        console.error('Error starting dotnet or loading window:', error);
+        console.error('Error starting server or loading window:', error);
     }
 };
 
-// Menu.setApplicationMenu(null);
-
+// 当 Electron 应用准备好时创建窗口
 app.whenReady().then(() => {
     createWindow();
 
@@ -79,10 +126,32 @@ app.whenReady().then(() => {
             createWindow();
         }
     });
+    // 监听 before-quit 事件,关闭 IES.ExamServer.exe 进程
+    app.on('before-quit', async (event) => {
+        event.preventDefault(); // 阻止默认的退出行为
+        if (serverProcess) {
+            console.log('Killing server process...');
+            serverProcess.kill();
+        }
+        try {
+            console.log('index/shutdown api ...');
+            // 发起 HTTP 请求来关闭.NET Core Web API
+            const response = await axios.get(`${baseUrl}/index/shutdown`, {
+                httpsAgent: agent
+            });
+            if (response.status === 200) {
+                console.log('Server is shutdown!');
+                //resolve();
+            }
+           // app.quit(); // 关闭 Electron 应用程序
+        } catch (error) {
+            console.error('关闭.NET Core Web API 时出错:', error);
+        }
+    });
 });
 
-app.on('window-all-closed', () => {
-    if (process.platform !== 'darwin') {
-        app.quit();
-    }
+// 当所有窗口关闭时退出应用(macOS 除外)
+app.on('window-all-closed', function () {
+    // 无论什么平台,都退出程序
+    app.quit();
 });

BIN
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/header.bmp


BIN
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/logo.ico


+ 18 - 10
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/package.json

@@ -1,7 +1,7 @@
 {
   "name": "examclient",
   "version": "1.0.0",
-  "description": "",
+  "description": "IES.ExamClient",
   "main": "app.js",
   "scripts": {
     "start": "electron .",
@@ -13,24 +13,28 @@
   "author": "",
   "license": "MIT",
   "devDependencies": {
-    "electron": "12.2.3",
-    "electron-packager": "^17.1.2"
+    "electron": "20.3.12"
   },
   "build": {
-    "electronDownload": {
-      "mirror": "https://npm.taobao.org/mirrors/electron/"
-    },
-    "appId": "exam.habool.local",
-    "productName": "评测局域网教师端",
+    "appId": "exam.habook.local",
+    "productName": "评测教师端",
     "directories": {
       "output": "dist"
     },
     "win": {
       "target": "nsis",
-      "sign": false
+      "sign": false,
+      "icon": "logo.ico"
     },
     "nsis": {
-      "runAfterFinish": false
+      "runAfterFinish": false,
+      "oneClick": false,
+      "perMachine": true,
+      "allowToChangeInstallationDirectory": true,
+      "createDesktopShortcut": true,
+      "createStartMenuShortcut": true,
+      "installerHeader": "header.bmp",
+      "installerSidebar": "sidebar.bmp"
     },
     "extraFiles": [
       {
@@ -41,5 +45,9 @@
         ]
       }
     ]
+  },
+  "dependencies": {
+    "adm-zip": "^0.5.16",
+    "axios": "^1.7.9"
   }
 }

BIN
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/sidebar.bmp


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 147 - 1073
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/yarn.lock


+ 0 - 4
TEAMModelOS.Extension/IES.ExamLib/IES.ExamLib.csproj

@@ -5,8 +5,4 @@
     <Nullable>enable</Nullable>
   </PropertyGroup>
 
-  <ItemGroup>
-    <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
-  </ItemGroup>
-
 </Project>

TEAMModelOS.Extension/IES.ExamLib/Models/EvaluationCommon.cs → TEAMModelOS.Extension/IES.Exam/IES.ExamLibrary/Models/EvaluationCommon.cs


+ 1 - 1
TEAMModelOS.Extension/IES.ExamLib/Models/ExamConstant.cs

@@ -2,7 +2,7 @@
 using System.Collections.Generic;
 using System.Text;
 
-namespace IES.ExamLib.Models
+namespace IES.ExamLibrary.Models
 {
     public class ExamConstant
     {

+ 13 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/.config/dotnet-tools.json

@@ -0,0 +1,13 @@
+{
+  "version": 1,
+  "isRoot": true,
+  "tools": {
+    "dotnet-ef": {
+      "version": "9.0.1",
+      "commands": [
+        "dotnet-ef"
+      ],
+      "rollForward": false
+    }
+  }
+}

+ 0 - 24
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/README.md

@@ -1,24 +0,0 @@
-# app
-
-## Project setup
-```
-npm install
-```
-
-### Compiles and hot-reloads for development
-```
-npm run serve
-```
-
-### Compiles and minifies for production
-```
-npm run build
-```
-
-### Lints and fixes files
-```
-npm run lint
-```
-
-### Customize configuration
-See [Configuration Reference](https://cli.vuejs.org/config/).

+ 0 - 58
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/components/HelloWorld.vue

@@ -1,58 +0,0 @@
-<template>
-  <div class="hello">
-    <h1>{{ msg }}</h1>
-    <p>
-      For a guide and recipes on how to configure / customize this project,<br>
-      check out the
-      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
-    </p>
-    <h3>Installed CLI Plugins</h3>
-    <ul>
-      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
-      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
-    </ul>
-    <h3>Essential Links</h3>
-    <ul>
-      <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
-      <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
-      <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
-      <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
-      <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
-    </ul>
-    <h3>Ecosystem</h3>
-    <ul>
-      <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
-      <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
-      <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
-      <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
-      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
-    </ul>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'HelloWorld',
-  props: {
-    msg: String
-  }
-}
-</script>
-
-<!-- Add "scoped" attribute to limit CSS to this component only -->
-<style scoped>
-h3 {
-  margin: 40px 0 0;
-}
-ul {
-  list-style-type: none;
-  padding: 0;
-}
-li {
-  display: inline-block;
-  margin: 0 10px;
-}
-a {
-  color: #42b983;
-}
-</style>

+ 0 - 19
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/vue.config.js

@@ -1,19 +0,0 @@
-const path = require('path')
-const { CleanWebpackPlugin } = require('clean-webpack-plugin')
-const { defineConfig } = require('@vue/cli-service')
-const Timestamp = new Date().getTime();
-function resolve(dir) {
-	return path.join(__dirname, './', dir)
-}
-
-module.exports = defineConfig({
-	transpileDependencies: true,
-	outputDir: '../wwwroot',
-	lintOnSave: false,
-	//transpileDependencies: ['@azure'],
-	/* devServer: { //局域网内可以使用
-		host: '0.0.0.0', //允许所有IP访问
-		port: 8081,
-		disableHostCheck: true, // 禁用主机检查,允许局域网访问
-	} */
-})

+ 22 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/cer/cert.pem

@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDljCCAn6gAwIBAgIUPPqP36ZRSzUSAOh2QsYSeopwRx4wDQYJKoZIhvcNAQEL
+BQAwZjELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB1NpQ2h1YW4xEDAOBgNVBAcMB0No
+ZW5nRHUxDzANBgNVBAoMBmhhYm9vazELMAkGA1UECwwCcmQxFTATBgNVBAMMDGhh
+Ym9vay5sb2NhbDAeFw0yNTAxMjMxMTE4NDNaFw00NTAxMTgxMTE4NDNaMGYxCzAJ
+BgNVBAYTAkNOMRAwDgYDVQQIDAdTaUNodWFuMRAwDgYDVQQHDAdDaGVuZ0R1MQ8w
+DQYDVQQKDAZoYWJvb2sxCzAJBgNVBAsMAnJkMRUwEwYDVQQDDAxoYWJvb2subG9j
+YWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCos+IMfiDcYL5+bh67
+vomumx3yI3G16LD94bWzcvq+5hjsXKIqxrHVLU2IwHgxqNCOKBPNQkd2Gm+YthhM
+FpQ04ZG7qikvq64CGKc34CKhItdxw69euoIwTfPGzzsfEfgw597eRE4NMwPWSDo5
+omFTQiHnISYO9zJBWWelNKhfKZ+D4MJXsdw729I+SlNzWhLrLVMZa27JonJYY1j7
+Lg9A/3sLAI15Kbfw0eiaR3fQbqmSaQdZlzq16UAQn0SUE+sdqpJNblFpRFn9zQM6
+9axSEEHWeekjpPJQJOuBEiOyzBNBDHWevraWQ2cWaD2EhglZuwg+BBMJ4JytZKYp
+xSDfAgMBAAGjPDA6MBkGA1UdEQQSMBCCDiouaGFib29rLmxvY2FsMB0GA1UdDgQW
+BBQuz7mA34iNHbBQeCV6B5eaoQ14+jANBgkqhkiG9w0BAQsFAAOCAQEAnidDFi6g
+lkIFRKI120d/kVZK6eNJPK5tYAMUH9SM9wx2IPQGsI/F/qtGuQHNFRyNgXGFx+KY
+KI55oA4jH6BNIANVQZio+H5+ljPbYCBcNfk1k2hDyvypi1qCE+iHMo4RvX/7dTRI
+rhHCQwxfTAkOdOzIhztnQe5bbOfxoUHssCwhrf0NZoZd+PDa6BuYBRnXYr7t+Mz6
+xCus0zYXf025fs2LQj6Z5YwSPmRY8j11pluN/iDgbB6Rdg+OcPGfjZ6wxxLLJvUS
+UmtoJ0BEehLmYrQIAAA6t6fovJe2u1jr3WD0nlgTn9kciaq64Zgm+8xrNs9zh7Ao
+P88lxhd9BOiowA==
+-----END CERTIFICATE-----

TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam0/certificate.bat → TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/cer/certificate.bat


BIN
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/cer/certificate.cer


+ 2 - 35
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/template/certificate.bat

@@ -1,5 +1,5 @@
 @echo off
-echo Configuring hosts file
+echo Importing certificate
 
 net session >nul 2>&1
 if %errorLevel% neq 0 (
@@ -8,39 +8,6 @@ if %errorLevel% neq 0 (
     exit /b
 )
 
-set "hostsFile=C:\Windows\System32\drivers\etc\hosts"
-set "newEntry=192.168.8.132 exam.habook.local"
-
-if not exist "%hostsFile%" (
-    echo hosts file does not exist:%hostsFile%
-    pause
-    exit /b
-)
-
-
-findstr /v /i /c:"exam.habook.local" "%hostsFile%" > "%hostsFile%.tmp"
-if %errorLevel% equ 0 (
-    move /y "%hostsFile%.tmp" "%hostsFile%" >nul 2>&1
-    echo Removed all entries containing exam.habook.local
-) else (
-    echo Failed to remove entries containing exam.habook.local
-    pause
-    exit /b
-)
-
-
-echo %newEntry% >> "%hostsFile%"
-if %errorLevel% equ 0 (
-    echo Hosts file configured successfully
-) else (
-    echo Hosts file configuration failed
-    pause
-    exit /b
-)
-
-:ImportCert
-echo Importing certificate
-
 if not exist "%~dp0certificate.cer" (
     echo Certificate file does not exist:%~dp0certificate.cer
     pause
@@ -70,5 +37,5 @@ if %errorLevel% equ 0 (
     echo Certificate import failed
 )
 
-echo All operations completed
+echo Certificate installation completed
 pause

+ 28 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/cer/key.pem

@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCos+IMfiDcYL5+
+bh67vomumx3yI3G16LD94bWzcvq+5hjsXKIqxrHVLU2IwHgxqNCOKBPNQkd2Gm+Y
+thhMFpQ04ZG7qikvq64CGKc34CKhItdxw69euoIwTfPGzzsfEfgw597eRE4NMwPW
+SDo5omFTQiHnISYO9zJBWWelNKhfKZ+D4MJXsdw729I+SlNzWhLrLVMZa27JonJY
+Y1j7Lg9A/3sLAI15Kbfw0eiaR3fQbqmSaQdZlzq16UAQn0SUE+sdqpJNblFpRFn9
+zQM69axSEEHWeekjpPJQJOuBEiOyzBNBDHWevraWQ2cWaD2EhglZuwg+BBMJ4Jyt
+ZKYpxSDfAgMBAAECggEANnHbVZnNBCJqyAXFAGVFLOcdunwK4iDHIiBaUiAc4UgE
+Kh/ZLEH0uW5F9AjdoXUwXXh8r4Ug5/IvKtM1szSkPw6/SBfp89sNrNqE+OjqdCN4
+8VuseL9rbahysKpHk8bqWOo0CHp10BezONAVjm0jyMlyhJOzoyfO/b0bUHtWjWMs
+07ZK8hr7UWXDsqq9l/fqkF/sy3ACslx++z+rJrzcEmHO4ARQS/HcUCKjxLyd0ruJ
+sXY8T36wJItWZyJAO/S/Yf0vvSZ2DgLPqJK4q3duq/Xjfosre/ZR5yX3G00kmzly
+JMG2mu8xagtNFGQr+IRTod+Xgh2LxUuHDTHjl6ucDQKBgQDSuBd7P4m3/ajVpjmk
+2MkBacKXZYGinTHkUzH6VQAFQnsq6OxRtnF6XBVvXruGpCsfW4e1XgYfTJ2wGFEo
+T0I2OCRlGhsETSPv55Q5cyBgvke1Dx506uuUrkmxCvvlT+CQ6rJ+B9cMkTm/DOAh
+scpol6fbHd71KDWBFbPEt5BnLQKBgQDM9GrB/Bt2vlQKdB+2M8/ZnO9VZ1jwPohM
+UwYqqiyHQ9F3FDlaDychv57Na+7GPieiuXTCMY/EmC9tio17KmcqXEXkEM8mceCM
+CHrAJ+Y27frMkoEaMUIp7Ozg6iWFDLLJXZjlaGq8v1FPGkxg4PUO8Sfc7Hk2/6n0
+J+hayQqvuwKBgDg6h4mL2uQN/A+G27rxXjHSVuqvW3hZGgcjbCKwq+C7bMJ2hRkI
+5h8Qk2e0j7XN+PPVKNFs1ZVkjG+BDacD/1cYhB22aZ8sNnq+AP+TiQBHxjq6XhY0
+2chC+ccUd0MpnXMOLbMTxH7GAfLJLMXQ+4INde0ye1xP7ncTo3TKxu3BAoGATQwx
+Of2JYYlJVKi05yzxHTCW9HcmgyIu4OpxLgbNOIhxLR/IM6fwoQpFK5pAp/vjsJOh
+ZgN1tYU0hyyH5V5abB1GFOeQyDYVIO9rHeOh8tJh7xfAOtV7twEfR6oeMrdEWti5
+yrwHLC+L5H3RarYGyfg0cSPgqaFRBb0e50oen1MCgYBAIkG5GdKT4RqxONSC2zDn
+Qf5CO0pfuuEUfYH42Q0OGYM/QQsWC0vztQdit0lEraQk/+RJx+ILrtyl3U9w287e
+u2VNH0VY+5xDLWox7+mR7sGDZeBco24vOKZu97T6EvGPQN9l3j/t0O6TRHMGxySO
+dzlTniivDhJI6sWWHuPFSA==
+-----END PRIVATE KEY-----

+ 42 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/cer/modify_hosts.bat

@@ -0,0 +1,42 @@
+@echo off
+echo Configuring hosts file
+
+net session >nul 2>&1
+if %errorLevel% neq 0 (
+    echo Please run this script as an administrator
+    pause
+    exit /b
+)
+
+set "hostsFile=C:\Windows\System32\drivers\etc\hosts"
+set "newEntry=192.168.8.132 exam.habook.local"
+
+if not exist "%hostsFile%" (
+    echo hosts file does not exist:%hostsFile%
+    pause
+    exit /b
+)
+
+
+findstr /v /i /c:"exam.habook.local" "%hostsFile%" > "%hostsFile%.tmp"
+if %errorLevel% equ 0 (
+    move /y "%hostsFile%.tmp" "%hostsFile%" >nul 2>&1
+    echo Removed all entries containing exam.habook.local
+) else (
+    echo Failed to remove entries containing exam.habook.local
+    pause
+    exit /b
+)
+
+
+echo %newEntry% >> "%hostsFile%"
+if %errorLevel% equ 0 (
+    echo Hosts file configured successfully
+) else (
+    echo Hosts file configuration failed
+    pause
+    exit /b
+)
+
+echo Hosts file modification completed
+pause

+ 84 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/cer/选择ip映射.bat

@@ -0,0 +1,84 @@
+@echo off
+echo Configuring hosts file
+
+:: 检查是否以管理员身份运行
+net session >nul 2>&1
+if %errorLevel% neq 0 (
+    echo Please run this script as an administrator
+    pause
+    exit /b
+)
+
+:: 设置hosts文件路径
+set "hostsFile=C:\Windows\System32\drivers\etc\hosts"
+
+:: 检查hosts文件是否存在
+if not exist "%hostsFile%" (
+    echo hosts file does not exist:%hostsFile%
+    pause
+    exit /b
+)
+
+:: 删除所有以.habook.local结尾的条目
+findstr /v /i /c:".habook.local" "%hostsFile%" > "%hostsFile%.tmp"
+if %errorLevel% equ 0 (
+    move /y "%hostsFile%.tmp" "%hostsFile%" >nul 2>&1
+    echo Removed all entries containing .habook.local
+) else (
+    echo Failed to remove entries containing .habook.local
+    pause
+    exit /b
+)
+
+:: 获取当前电脑的所有IP地址
+echo Getting IP addresses...
+ipconfig | findstr /i "IPv4 Address" > ip_list.txt
+
+:: 显示IP地址列表并让用户选择
+echo Available IP addresses:
+setlocal enabledelayedexpansion
+set /a count=0
+for /f "tokens=2 delims=:" %%i in (ip_list.txt) do (
+    set /a count+=1
+    set "ip!count!=%%i"
+    echo !count!. %%i
+)
+
+:: 如果没有任何IP地址,退出脚本
+if %count% equ 0 (
+    echo No IP addresses found.
+    del ip_list.txt
+    pause
+    exit /b
+)
+
+:: 让用户选择一个IP地址
+:choose_ip
+set /p "choice=Please choose an IP address (1-%count%): "
+if not defined ip%choice% (
+    echo Invalid choice. Please try again.
+    goto choose_ip
+)
+
+:: 获取用户选择的IP地址
+for /f "tokens=1,2 delims==" %%i in ('set ip%choice%') do set "selectedIP=%%j"
+
+:: 去除IP地址中的空格
+set "selectedIP=%selectedIP: =%"
+
+:: 添加新的映射条目
+set "newEntry=%selectedIP% exam.habook.local"
+echo %newEntry% >> "%hostsFile%"
+if %errorLevel% equ 0 (
+    echo Hosts file configured successfully
+) else (
+    echo Hosts file configuration failed
+    pause
+    exit /b
+)
+
+:: 清理临时文件
+del ip_list.txt
+
+echo Hosts file modification completed
+pause

+ 0 - 22
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam0/cert.pem

@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDozCCAougAwIBAgIUXVG28xNwGPdDM2SRRiCQhDTZG60wDQYJKoZIhvcNAQEL
-BQAwazELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB1NpQ2h1YW4xEDAOBgNVBAcMB0No
-ZW5nRHUxDzANBgNVBAoMBmhhYm9vazELMAkGA1UECwwCcmQxGjAYBgNVBAMMEWV4
-YW0uaGFib29rLmxvY2FsMB4XDTI1MDEyMTA0MzM0NVoXDTI2MDEyMTA0MzM0NVow
-azELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB1NpQ2h1YW4xEDAOBgNVBAcMB0NoZW5n
-RHUxDzANBgNVBAoMBmhhYm9vazELMAkGA1UECwwCcmQxGjAYBgNVBAMMEWV4YW0u
-aGFib29rLmxvY2FsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxAl2
-7myycEOgiO0OEohPYDV/1raik81QuU3485caFoxs2UUXJoW0zkG0TKHVqVJGcNbg
-xncN7gjThRAKy4PSLTrn9A9fPJVqRpB0ElJlBsChhfXxpWzrFl/wHOxdmYz5rUjs
-gjq0zdlodSQ2yWE0VdZhI6VqIbvz16rHXj19B1kKcGVzlnwuRxuqldDEJSUD3UTe
-epPGaSbdqI4riUcOaksEMEAgll7HqixTv5tB3/aKJ0w+nxZFimocdA/XFXIXMO1M
-lLP2CCck1Jv7vlAL/TP22wQnfINifYDisTmsdtFvq6hgkFgh34lDX+moM92MPR5/
-m5D4GQi6BYtFpOoC+wIDAQABoz8wPTAcBgNVHREEFTATghFleGFtLmhhYm9vay5s
-b2NhbDAdBgNVHQ4EFgQUXr4ImISHw4eBN6r8MO3LiM0o+5gwDQYJKoZIhvcNAQEL
-BQADggEBAIFTbpGUwtpMa//mrHGghAcF+jw3+6G/I6K0bsfcFfYVQJCwJ+HcNWDJ
-+nCn64rdu5sQvFmpPSlXBMbFoW/ZAxs2g7jhk90MUrR0MFBu91qAu2gqwE55S6xr
-O7fg4sd0aVvR2tE2e6wxMaLGTfNtF9Uo0tKFehDUQXIiCSAQkLGwsWneZ+GRE8Dc
-CwGjJXpwvBfmcQoNMLqZpZ8P8AdOUVjp7PIEkWJWSGcaa+RCrP4qhVwNv+ZloBbc
-XBNxCrftg2epupYOGvsq5ACX7u3nLbRfuKu3X+E0F7V/Qwmjfc9+WS50LouCMIx/
-TvVawMxytPaTUlQe45aNANW4reHWezQ=
------END CERTIFICATE-----

BIN
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam0/certificate.cer


+ 0 - 28
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam0/key.pem

@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDECXbubLJwQ6CI
-7Q4SiE9gNX/WtqKTzVC5TfjzlxoWjGzZRRcmhbTOQbRModWpUkZw1uDGdw3uCNOF
-EArLg9ItOuf0D188lWpGkHQSUmUGwKGF9fGlbOsWX/Ac7F2ZjPmtSOyCOrTN2Wh1
-JDbJYTRV1mEjpWohu/PXqsdePX0HWQpwZXOWfC5HG6qV0MQlJQPdRN56k8ZpJt2o
-jiuJRw5qSwQwQCCWXseqLFO/m0Hf9oonTD6fFkWKahx0D9cVchcw7UyUs/YIJyTU
-m/u+UAv9M/bbBCd8g2J9gOKxOax20W+rqGCQWCHfiUNf6agz3Yw9Hn+bkPgZCLoF
-i0Wk6gL7AgMBAAECggEABMSeKepm5KggbXQmlXjPRW3HsDc8+Q9TWU52MaaJMec0
-doxhpcQ2w5WBhyOgiL/BitkPGoSmyBVa36+mM5D/Oa5nGd6N2HFh5ll5GUD7yBBD
-XXi/6eAeT6sBshJlrGhrYjWV2w0GfMM+8SpyUq+UkEnoju9lB3EE20gCFV61fco3
-pK7en/hHCK0/jLDJYxEhEk5FMKA0BAUdsoD2WRVCnakCSaKRJzJEafL9CHQaL/oq
-pPVIYO3tx0ryNtdcLsc0j01rLMJ8n9lG59+3zAu5nCEVHYKO7oPuj8kpwdqxIj9J
-ZnSHX9ayJYNOdJDGD8zQYKhZQdquOYOAE9NnbINAMQKBgQD+OCf02IIuK4NcU1Ef
-wlx/sRjvCIWq39S1KNQ0kPtI4dWUNLI5DJqVQgylHIkVC/FNbTcaW1A6nVqukJfJ
-Nu3HrZoN+585QRKesTGNd45Zmwwy8JuV1OblV6SbI5xZ3g3S4SVkWnldOz36FThn
-wdHf2lg/unGzX4ojgYfuV1VpfwKBgQDFaPsd7Lo9oDUEZH4GDjk8onsIWwad6Nut
-3CCdBn3tiSlSAddVbZfu60VtM25CA9K1UbPsV1FU7ZYX1wKma019r1VqkwhPMWoq
-FsnhoHMOl69vV/3brjfHZDflbCMbMt/vYc91kRS7nVDAeqz5sDf4mJ7BSs70ZGth
-oAqHLQfMhQKBgQDhg71mRX5OKMmR6FMpwkg9+kNtIHk7GO5feoWs0AQqJjRKEekc
-FKM4zuvauJKeegaoMb9VATYNmTMtchVEKRcMMGNeDh20M5ap8fRMU4eS06khszHB
-26isQHBEM3XqfsJylMmP2XaaDwiuxY5Q9K4ST2ZDukhM3+7yCmEkPJMHTwKBgEEK
-DXIWhGW5Wr5PvZWRKhpoDdD67HsqNPZbCAO0F9kiz5JNOPzUVrJIoV8RCsqFJ+7F
-NFoxioJIpKLGHAFoaOd31NSADMTKqwei6nCDxGSSZSJyAxlVlNsEkcXsksRrRow/
-1XIOkp4dfnVr9YFuJYKqBeP5GaY7T4WijNVsaJ1hAoGAGQD8EyQXcfsaIEjaPQ65
-io+DloCFqwJd5EkCFPyv7Qz+5A9Jv6/RTznXSXtNBv+j6t7MG/o6A8ZX0ZR5UjuG
-WB/U87QpUokC+wzNTCdbDWaF/GvOU8lziksDrMMzrV8Yy+CVtGNiscAZyj6QJ1Am
-WrPltUEZoXxKkRRcmvNeti8=
------END PRIVATE KEY-----

+ 0 - 22
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam1/cert.pem

@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDpjCCAo6gAwIBAgIUOJhd4SHuDg1Y/zNtpBHCVtmwcSMwDQYJKoZIhvcNAQEL
-BQAwbDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB1NpQ2h1YW4xEDAOBgNVBAcMB0No
-ZW5nRHUxDzANBgNVBAoMBmhhYm9vazELMAkGA1UECwwCcmQxGzAZBgNVBAMMEmV4
-YW0xLmhhYm9vay5sb2NhbDAeFw0yNTAxMjIwMTU2MzlaFw0yNjAxMjIwMTU2Mzla
-MGwxCzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdTaUNodWFuMRAwDgYDVQQHDAdDaGVu
-Z0R1MQ8wDQYDVQQKDAZoYWJvb2sxCzAJBgNVBAsMAnJkMRswGQYDVQQDDBJleGFt
-MS5oYWJvb2subG9jYWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0
-qV10i/hFgxYUCQ2fz230qf2dpjvLlOXC/6VR9j7sRPoLlPrS7ux1i5/ysqjBxSvr
-4vAAyTRqtr8xGusj0pP3KrZ1s1ETFhfs1I1+VwRVBdrQYCtc18a2H6KTl1QJhH53
-i8otPG6xe3+x/CN+f67VYhuVLGmvfT+a/VehDu4INqZF35xzNDYwyT7xGZLFZiCD
-GrcVmZrpkIcBe0NWdkZyfx2U0fuuDxJHe3j+HNB/xZMFJWlTtqOcx1c4iQHwFGyR
-niPkzhjGt+9d4bnQ0wgKMs/ACSACl0QK+R5SMGqKiex2WOmd1GMkhm7B3Y5g9MbI
-koRLPW1iHgycvFt/Dy65AgMBAAGjQDA+MB0GA1UdEQQWMBSCEmV4YW0xLmhhYm9v
-ay5sb2NhbDAdBgNVHQ4EFgQUPxmKwaVyZjfo+c5KUbIieL2BjZIwDQYJKoZIhvcN
-AQELBQADggEBAKIqBNcwHswBt9OE8H9jGxS13JYmpwV3W01bbJP7Uf0uiElZFKZo
-A9l4/snc7dNbF65mQMWnRiTZKRdm5rO4VgRAjiZ+n9/dGFdfgJZp7vsGfzfrPvax
-eMu0R++KKT7NdECwF24CgZfzyR1rqmDbofrdOr6zhWC8dRtxcGXlrdn8Y9cuXHVE
-uCK2u52RK7F+z86G5LTNCXmY/HjJwB4zEdL/T2f0MZxumrdPNyzekSQsMwe1ogGQ
-Zt/4EuS/+Qc6cvjTph7x1AXtRE9PfGDPKpohJiQpq/QG2dpnPe23JxKLIRcWOKq9
-qwoamlyjFKFhfc9AfAle3z0ehR/2X8mxxWs=
------END CERTIFICATE-----

+ 0 - 74
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam1/certificate.bat

@@ -1,74 +0,0 @@
-@echo off
-echo Configuring hosts file
-
-net session >nul 2>&1
-if %errorLevel% neq 0 (
-    echo Please run this script as an administrator
-    pause
-    exit /b
-)
-
-set "hostsFile=C:\Windows\System32\drivers\etc\hosts"
-set "newEntry=192.168.8.132 exam1.habook.local"
-
-if not exist "%hostsFile%" (
-    echo hosts file does not exist:%hostsFile%
-    pause
-    exit /b
-)
-
-
-findstr /v /i /c:"exam.habook.local" "%hostsFile%" > "%hostsFile%.tmp"
-if %errorLevel% equ 0 (
-    move /y "%hostsFile%.tmp" "%hostsFile%" >nul 2>&1
-    echo Removed all entries containing exam.habook.local
-) else (
-    echo Failed to remove entries containing exam.habook.local
-    pause
-    exit /b
-)
-
-
-echo %newEntry% >> "%hostsFile%"
-if %errorLevel% equ 0 (
-    echo Hosts file configured successfully
-) else (
-    echo Hosts file configuration failed
-    pause
-    exit /b
-)
-
-:ImportCert
-echo Importing certificate
-
-if not exist "%~dp0certificate.cer" (
-    echo Certificate file does not exist:%~dp0certificate.cer
-    pause
-    exit /b
-)
-
-set "certSubject="
-for /f "tokens=*" %%i in ('certutil -dump "%~dp0certificate.cer" ^| findstr /i "CN="') do (
-    set "certSubject=%%i"
-)
-
-if defined certSubject (
-    echo Deleting existing certificate with the same name
-    certutil -delstore "Root" "%certSubject%"
-    if %errorLevel% equ 0 (
-        echo Existing certificate deleted successfully
-    ) else (
-        echo Failed to delete existing certificate (may not exist)
-    )
-)
-
-echo Importing new certificate
-certutil -addstore -f "Root" "%~dp0certificate.cer"
-if %errorLevel% equ 0 (
-    echo Certificate imported successfully
-) else (
-    echo Certificate import failed
-)
-
-echo All operations completed
-pause

BIN
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam1/certificate.cer


+ 0 - 28
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam1/key.pem

@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC0qV10i/hFgxYU
-CQ2fz230qf2dpjvLlOXC/6VR9j7sRPoLlPrS7ux1i5/ysqjBxSvr4vAAyTRqtr8x
-Gusj0pP3KrZ1s1ETFhfs1I1+VwRVBdrQYCtc18a2H6KTl1QJhH53i8otPG6xe3+x
-/CN+f67VYhuVLGmvfT+a/VehDu4INqZF35xzNDYwyT7xGZLFZiCDGrcVmZrpkIcB
-e0NWdkZyfx2U0fuuDxJHe3j+HNB/xZMFJWlTtqOcx1c4iQHwFGyRniPkzhjGt+9d
-4bnQ0wgKMs/ACSACl0QK+R5SMGqKiex2WOmd1GMkhm7B3Y5g9MbIkoRLPW1iHgyc
-vFt/Dy65AgMBAAECggEAJhdXpw0kCbP74bmO79USf/Wfja/RB7mbQCQavU9IMRTS
-C8Mbp4HMxXPtA4T74+8otZGhvOivbpidc0MTtPffTsvosKRgQb/0x3CBbNP4C90y
-J6E/Q1ITia7d3nSXuXIAIECPHj6RsEYzxFk/Opq9SCxpWGiG2AN2d71WzpSRR5Wy
-4ljWxyqrpq+9yBW+6a6ZESNTG3wnscysy1EENt5TrHrpSaH90aLDNGYFfY1x+d3h
-lCNzuAvCv9F0DIzhvsICfrWYVmLPZHFMVdR6oYiAkOQ+FwqYbvrBttCXs7Mqod+e
-JI2hzyDgxHQ0p0SFjGocTaZ+lirnwkzct/wZwDotKQKBgQDyEvIxnxZDi0maYkIW
-feJpOJP9bClkziVc2ng+HdlcUW9zAvsmnQOYqzNemjJ1N+UWvcVjVfHh8K9ISLUV
-bmygIGxHGP1wUhLojlJqHTRJ2tMvgxeO5xDmgY/te/WSaqCj/bauPy1xUEEfado2
-kTuu0zL7RPehG4iyAN0dfSudgwKBgQC/Df2Wswnsi2yptinyfUmjzlqgotHJlFDr
-iGQSOpqR9v68wLAMPt5nIvJwNpUILvFa20Wk6+GuJy5BcK5rrfBAVLx+8OJMpuA5
-G6beWx3CWKK/fLMH6F6YMyVDSKu9KvLOMVkCG8uqjSXU0aL9Ymwx7+leU7kbznIY
-+Nw1cSkqEwKBgQDpUtmeeng7IkYPSCXrB+rzAFWkLly3jPr2RJ0hQiP/l36UnIr2
-7OBKhrk8teNsmDN3d/KJjI1X+WT5hxsDTSvmK1oyLSQa3wDaplNJdFyx0vk7El/i
-nVTs2HShsplARwYPCrzJtptWXMRoQt7ROasNFwRMrG0CHEflSm/VvJANgQKBgCnK
-5a4RfBpCZYK6VnK09WbfvPNmqn1t9EWRkFJsf2NupEql29zR49Sr0Fy7k942ZpV5
-YcKx1qxB5dxqybzET8nCv3kFOHuMBYB4jAgX5mqc+PzqSj9wlUC173DIdMjsnDB+
-mvnbGrI0LhJfyUekQAasdGAt8FAk2NCdn44RMvABAoGABmHKJ+mRl/T+x/XeY5ED
-CRpi5q6H2t/90g9lP0S7DHRVOkbCFvDCzdwYgmaxJatGo8TOH7PziuZDpgULjL8O
-/250oj5nNMyMsst5Uk4NopBsb7+f2TdM7Br/tOAdm+Tzl7KCHwsh+fDBlcJRq+Bb
-knsLLVKfkMLMm3WuWiIN/7Y=
------END PRIVATE KEY-----

+ 0 - 22
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam2/cert.pem

@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDpjCCAo6gAwIBAgIUDfoU75/CWmzBsSuNiuRxG7V2j8owDQYJKoZIhvcNAQEL
-BQAwbDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB1NpQ2h1YW4xEDAOBgNVBAcMB0No
-ZW5nRHUxDzANBgNVBAoMBmhhYm9vazELMAkGA1UECwwCcmQxGzAZBgNVBAMMEmV4
-YW0yLmhhYm9vay5sb2NhbDAeFw0yNTAxMjIwMTU3NTBaFw0yNjAxMjIwMTU3NTBa
-MGwxCzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdTaUNodWFuMRAwDgYDVQQHDAdDaGVu
-Z0R1MQ8wDQYDVQQKDAZoYWJvb2sxCzAJBgNVBAsMAnJkMRswGQYDVQQDDBJleGFt
-Mi5oYWJvb2subG9jYWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCl
-Zx/kmGkyrPtpAfDgfbYhRImryj4Ez+CFloyZO49bdlFJ4qNrwx3WSdvay4Vk/2jQ
-wAZaRU6ojxt4rBbL7VKU/xHd1CPLjoiq8pAVdfoYiJTb3Nj4xsAer+xaCuwfC96a
-J/Wgw40JByzrBBR2b14GEOx2A6M99e+V2/Wvhc+pUu9h2F6vrND43TNy2UGtAyWQ
-Oya4RIubb4zRSLjPN4wECjtbZ2Rm1lTB+tOW+Q/PVgVj6YddVONVpaEorpoae5lX
-zsjpJchegY0UJrVUZqII1HWmItzuu5ZZbydBLxKcMcfteRK7awqgUYWTA1RbEkyb
-k69alkylL84hD9XOO+0FAgMBAAGjQDA+MB0GA1UdEQQWMBSCEmV4YW0yLmhhYm9v
-ay5sb2NhbDAdBgNVHQ4EFgQUtfCBPHkTSDDMBerflEj46zlTjrYwDQYJKoZIhvcN
-AQELBQADggEBAHCuW3w/nWm9Cek2CPWcHS+nP65UK8ql7H3nX7vBws0cmWlTHD7j
-cyJf6YHP/T2SdpaPJJIp9AxzlvQUPE0UKf92+hQci5E+422zIz1C++GLtkMwoGll
-Aj7SpsCAYIXlOd6Xe2k7EF2A3wJD0UU5v4GBQ2iZSKhSTTnv/slkBKvOLcCmnIXG
-Z9W40M3fXNzk1+oHfXXrXzL6ysL4xpmaHn5INB9wOgL77hLsDoqLG+HS1WOaPtQ3
-Ck4CSsJ8msJybCjPV3SJ2+9bUgBJlal7WP/0HyvIjF6Lh0IFRlpHr4eY+OV2PaVy
-i1gCnF5/vBOWWaAq2oyD+kHiJslX3XUPi/Y=
------END CERTIFICATE-----

+ 0 - 74
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam2/certificate.bat

@@ -1,74 +0,0 @@
-@echo off
-echo Configuring hosts file
-
-net session >nul 2>&1
-if %errorLevel% neq 0 (
-    echo Please run this script as an administrator
-    pause
-    exit /b
-)
-
-set "hostsFile=C:\Windows\System32\drivers\etc\hosts"
-set "newEntry=192.168.8.132 exam2.habook.local"
-
-if not exist "%hostsFile%" (
-    echo hosts file does not exist:%hostsFile%
-    pause
-    exit /b
-)
-
-
-findstr /v /i /c:"exam.habook.local" "%hostsFile%" > "%hostsFile%.tmp"
-if %errorLevel% equ 0 (
-    move /y "%hostsFile%.tmp" "%hostsFile%" >nul 2>&1
-    echo Removed all entries containing exam.habook.local
-) else (
-    echo Failed to remove entries containing exam.habook.local
-    pause
-    exit /b
-)
-
-
-echo %newEntry% >> "%hostsFile%"
-if %errorLevel% equ 0 (
-    echo Hosts file configured successfully
-) else (
-    echo Hosts file configuration failed
-    pause
-    exit /b
-)
-
-:ImportCert
-echo Importing certificate
-
-if not exist "%~dp0certificate.cer" (
-    echo Certificate file does not exist:%~dp0certificate.cer
-    pause
-    exit /b
-)
-
-set "certSubject="
-for /f "tokens=*" %%i in ('certutil -dump "%~dp0certificate.cer" ^| findstr /i "CN="') do (
-    set "certSubject=%%i"
-)
-
-if defined certSubject (
-    echo Deleting existing certificate with the same name
-    certutil -delstore "Root" "%certSubject%"
-    if %errorLevel% equ 0 (
-        echo Existing certificate deleted successfully
-    ) else (
-        echo Failed to delete existing certificate (may not exist)
-    )
-)
-
-echo Importing new certificate
-certutil -addstore -f "Root" "%~dp0certificate.cer"
-if %errorLevel% equ 0 (
-    echo Certificate imported successfully
-) else (
-    echo Certificate import failed
-)
-
-echo All operations completed
-pause

BIN
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam2/certificate.cer


+ 0 - 28
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam2/key.pem

@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQClZx/kmGkyrPtp
-AfDgfbYhRImryj4Ez+CFloyZO49bdlFJ4qNrwx3WSdvay4Vk/2jQwAZaRU6ojxt4
-rBbL7VKU/xHd1CPLjoiq8pAVdfoYiJTb3Nj4xsAer+xaCuwfC96aJ/Wgw40JByzr
-BBR2b14GEOx2A6M99e+V2/Wvhc+pUu9h2F6vrND43TNy2UGtAyWQOya4RIubb4zR
-SLjPN4wECjtbZ2Rm1lTB+tOW+Q/PVgVj6YddVONVpaEorpoae5lXzsjpJchegY0U
-JrVUZqII1HWmItzuu5ZZbydBLxKcMcfteRK7awqgUYWTA1RbEkybk69alkylL84h
-D9XOO+0FAgMBAAECggEABfHUm5z1j4b8ojYZ7sC6eeMLlDkK30zUyGYPpUkWuDY+
-IR0cKV2h4Xd6q/OUn8SlFXcIMSHO16JWOUgRhRvvCsBt1RoL6rqzkQy7UPfbYdHG
-2iSm/on4bbQ08nCrMrcHERxzH4wMRHWn2MXwMcPMoLsvjZv/SfmpbQX78fi8EV07
-/99u5lFcIxkF837PLgbXtX0OQY3hjZqbPo429jDWGvGdSdR+Zu+QKh09fGF2brBz
-FXp+TfbZmWFyTI9I6qNPyq1++amlTGidxemSEToCzCMNC5BxPFHyT7VBj4UpmIz/
-0jdvbbIJQ6Xhr1OX5HQdWg2CRWJIVuYEC/pPbcocIQKBgQDh6hpJsw/Upj5NbiHZ
-ecnx3fZk1jxlpe23VAAnH/3ACF8rGSUnsnCSaxONj7cznjf94Nta+i5SX4uSW+KF
-fiY5hjA4GzcFzuRQGu1IJsW9uyYCBsY6hk4CEZEEBSLgUj5N6mUAKG+KMLKlytSd
-3SAReGe2Lrc2FSxQGYkvs1KtewKBgQC7bg219gB18C5fgMiBgq9y8jdusCVfdnvH
-k1vX4c1EDrP7Q+2fs4AwlMtekf3b7UuL5fFRWkSsQ0deI7KE0CrBC+vFEJEX23xV
-Ws09jLQ0P6GHhcyGMO6o2B/U9YKSrVlKJs7VB1VrcpH/Yv9ZVAYoNT6B/Ix8iTyO
-1Ge4+XSHfwKBgBu9MfMgsG3s57N8NV3NrXUsSufAwnO8tv6OquIi9HZS0NSq5rwE
-Ffx0d1cncVg+MiPTKzv1giCNKMcUzzCS98CScHNDLDNjXvdTBxWX5SnRw+31xPtE
-qGlqnMLAmrKuhoXspPArBt8R8a2XxRmJIDnk7d8Zx1c1rFY9fHHF8/3nAoGAAh8+
-3H6KhstPWxl0K7M1FVIUupYX9jq7MAlFEu6lik24T/H28MXxf5tPqiRxAVpwbocN
-8mPZPzILzs8MqGBK+6CM7NBBNEnx4G2EwVukdqr5wzUKmcJYEWVRBvI4pjx8NFC4
-KsVIfEzxxjhyt6ox36aqrIIVfRt4qg1Rl1CLtzUCgYBRpu52mx6JKsWmHcTLCTNU
-Fkl5DVr1qsaWtRUmnlOtS0ZSne+pQERMMqkoDsBa1toGueK4Xy/CI0Kksn/jM/fE
-FDqWHRUjVzw9RMMNrILb+uWuIuPyL4Ro1iVeINnSOLaGQYuYbyW3tb/BEX4lL4ev
-+7G4gILX8EFF1TRmfuGGnQ==
------END PRIVATE KEY-----

+ 0 - 22
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam3/cert.pem

@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDpjCCAo6gAwIBAgIUA3r/m8KYXj7zWeGVj+JSAKZ4npowDQYJKoZIhvcNAQEL
-BQAwbDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB1NpQ2h1YW4xEDAOBgNVBAcMB0No
-ZW5nRHUxDzANBgNVBAoMBmhhYm9vazELMAkGA1UECwwCcmQxGzAZBgNVBAMMEmV4
-YW0zLmhhYm9vay5sb2NhbDAeFw0yNTAxMjIwMTU4MDZaFw0yNjAxMjIwMTU4MDZa
-MGwxCzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdTaUNodWFuMRAwDgYDVQQHDAdDaGVu
-Z0R1MQ8wDQYDVQQKDAZoYWJvb2sxCzAJBgNVBAsMAnJkMRswGQYDVQQDDBJleGFt
-My5oYWJvb2subG9jYWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1
-yRuBicfR66FpVq0EC9lhAaBgjjJtsZW7t8R0G6xnmrHGHThawLhM9vXHD9nFxIkT
-R+AT9UioJ7+kvoD7LeZ4HcqrV000GYNyV3Sa+vlwt6O3jXtVkGdF/vW/07FMhsUk
-I8nDiwkj2bOmC0/eTDFBfuomVmS1f+a771PzbF82WSfxK+4gl2RKm0lRm075wjzA
-3Bd+7ve4pCasw6ln64+tcUt9SnRe9B/BBULwmHuPOm5xvY9Pg2Juj2lTqfRdm+K0
-zbNf67Ic+nhS6pWb6gx4qAIWtz1kyDavAPo4bHv8GMrNkzUUunOXjAAk9jf6Jyeb
-SbHQr8EKJCiswdvH0F35AgMBAAGjQDA+MB0GA1UdEQQWMBSCEmV4YW0zLmhhYm9v
-ay5sb2NhbDAdBgNVHQ4EFgQUpSH12rGZ955/Nrt2dkYk4GsTyQIwDQYJKoZIhvcN
-AQELBQADggEBAAnuCoDG7o/TNMEqFXIWCuINSpKhkb5N+wsE23W/fJulmAtxifZj
-kBUYfIZW/OdT9xBOtgCCrbu4hjWVjcIfFhyoZ9bI1kWvX6fPcnCNT3qQPCbgZjBc
-sJe7bTl9qLMs58+988KaHl2VIm1z6xDDs+3N1DI2okssKTkEJtTWT8k8SJFczLEn
-2YAmufHfGcyH1KmN3tv2i8lcmhrJju5ljf6ovaYALV/LfcjjspelKdPn8wmTkpFA
-ZqYwUftHHnnm7qy8a485yYA08uHvJXTpLxm8yagr2AKP5vr9YJ+cGrF55jp/NXCd
-HriFs4skM94BJtoZRjVdGZjpG9aYjqNxIjg=
------END CERTIFICATE-----

+ 0 - 74
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam3/certificate.bat

@@ -1,74 +0,0 @@
-@echo off
-echo Configuring hosts file
-
-net session >nul 2>&1
-if %errorLevel% neq 0 (
-    echo Please run this script as an administrator
-    pause
-    exit /b
-)
-
-set "hostsFile=C:\Windows\System32\drivers\etc\hosts"
-set "newEntry=192.168.8.132 exam3.habook.local"
-
-if not exist "%hostsFile%" (
-    echo hosts file does not exist:%hostsFile%
-    pause
-    exit /b
-)
-
-
-findstr /v /i /c:"exam.habook.local" "%hostsFile%" > "%hostsFile%.tmp"
-if %errorLevel% equ 0 (
-    move /y "%hostsFile%.tmp" "%hostsFile%" >nul 2>&1
-    echo Removed all entries containing exam.habook.local
-) else (
-    echo Failed to remove entries containing exam.habook.local
-    pause
-    exit /b
-)
-
-
-echo %newEntry% >> "%hostsFile%"
-if %errorLevel% equ 0 (
-    echo Hosts file configured successfully
-) else (
-    echo Hosts file configuration failed
-    pause
-    exit /b
-)
-
-:ImportCert
-echo Importing certificate
-
-if not exist "%~dp0certificate.cer" (
-    echo Certificate file does not exist:%~dp0certificate.cer
-    pause
-    exit /b
-)
-
-set "certSubject="
-for /f "tokens=*" %%i in ('certutil -dump "%~dp0certificate.cer" ^| findstr /i "CN="') do (
-    set "certSubject=%%i"
-)
-
-if defined certSubject (
-    echo Deleting existing certificate with the same name
-    certutil -delstore "Root" "%certSubject%"
-    if %errorLevel% equ 0 (
-        echo Existing certificate deleted successfully
-    ) else (
-        echo Failed to delete existing certificate (may not exist)
-    )
-)
-
-echo Importing new certificate
-certutil -addstore -f "Root" "%~dp0certificate.cer"
-if %errorLevel% equ 0 (
-    echo Certificate imported successfully
-) else (
-    echo Certificate import failed
-)
-
-echo All operations completed
-pause

BIN
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam3/certificate.cer


+ 0 - 28
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam3/key.pem

@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC1yRuBicfR66Fp
-Vq0EC9lhAaBgjjJtsZW7t8R0G6xnmrHGHThawLhM9vXHD9nFxIkTR+AT9UioJ7+k
-voD7LeZ4HcqrV000GYNyV3Sa+vlwt6O3jXtVkGdF/vW/07FMhsUkI8nDiwkj2bOm
-C0/eTDFBfuomVmS1f+a771PzbF82WSfxK+4gl2RKm0lRm075wjzA3Bd+7ve4pCas
-w6ln64+tcUt9SnRe9B/BBULwmHuPOm5xvY9Pg2Juj2lTqfRdm+K0zbNf67Ic+nhS
-6pWb6gx4qAIWtz1kyDavAPo4bHv8GMrNkzUUunOXjAAk9jf6JyebSbHQr8EKJCis
-wdvH0F35AgMBAAECggEAE2SWdh3MPuVLzXCNOZrIAHTLdrGEyLYCuslygE55eH4E
-zBVO70OTBcbs1mUm/tWmJ/PpgEeRDjtbUwhtux4c8aCAAAJqvo2gO8D/tA7lMHSu
-1wSVbT3f/pQiBGphhj/0ZRQaUK2S9ouhgiu/w//N22ZeNWPPD5vK8i6ofpYHnET9
-AuHm/7Toh3eUSpOCoCxKSAtyWbg/9Vr1CAjAp4mAlFYHtO8rm7/yGszUIofWz1hc
-+EntN2FV7auXZdJyo5a5tbOKd9Ii0NlEHAXxmFHcunjl68OonScNEFfRDF9YJTxq
-TEtDP5cTRU2TnkfEVHw92XGaXmRU34DxSP6D1ZnaxwKBgQD9LuG02IKZIVB6n4xb
-kNzJcwoGNo948LTXCJE2yKU+iK99lz5vNJyyFaLfYY5xbuQZRf7dcPgTyUuRS+ur
-oS6ruX5Z9j+FdCJwAUdjOYXNQwtqwCAq6IorbNTrTY8WA6cAmzUlZdlZsa/gScZN
-IwEsTW0RBeocFqKR6fHAjIAOZwKBgQC3zt7jfI4hSVPAIpFn5YDU5DGVenELyPHg
-3D7fNtvVbKX5gmtr24rJmlxCFCOREUNyF4S2nj6oql9I9PJadPBmCSju2OhnD/zj
-TtWc0SyuN3AWpU+2WS02B5v/PSm5NrnUkddU3835de5Ajzah38AfTyB3RRSBWSie
-ui3LXkG0nwKBgQDmZOEz5sqBetV3oPTi5mJkV3FJ0iChV7nY7Izoo5Hr5Ap+aUGB
-hQkK7bF3QZmUE+syLIYPERxPNSC1KbdeSaDk2Dnot7N6SAKGlkNWQiRGc7pR/F/J
-bC8/8RuYvM12pQA2BwdH7vTpME7x9XClBTOuEw8rLIyr3tU1nwRFyhVGBQKBgG+R
-njEvFYaW38MsOF58LCsNKBu3ao7gthDrHy6WOfHeNRCyXUnmxOOCUqW5W61ecTX7
-mI7QlvACGjxKkiDeAl2tCa+Q8eA9EB9ZZsf9H8XP2LWmOjBRCORW0hWnpn/J/BpP
-PAlEn93f+3Ise9jd29wlR2ud/shUuhc+ozViTTe5AoGBAIIh/D7tbB4Zhxz9gcGG
-bEFMruveLIDxrs5Bqt33tcaZdYmnQ+OFhuMjDtZ4cJAERHSrnBVWb2x+cl/lxgOX
-1VAWCWFPAquxMFGkh2Xdcpd4UKgrv0YJEd1r8J9s6574fqYvJkzNP/Ph3VOYUMxg
-m51TzcO13938amX0MiII0PVk
------END PRIVATE KEY-----

+ 0 - 22
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam4/cert.pem

@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDpjCCAo6gAwIBAgIUWa2IOFQksnU2Vb1Z/YDmaKflvFAwDQYJKoZIhvcNAQEL
-BQAwbDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB1NpQ2h1YW4xEDAOBgNVBAcMB0No
-ZW5nRHUxDzANBgNVBAoMBmhhYm9vazELMAkGA1UECwwCcmQxGzAZBgNVBAMMEmV4
-YW00LmhhYm9vay5sb2NhbDAeFw0yNTAxMjIwMTU4MjVaFw0yNjAxMjIwMTU4MjVa
-MGwxCzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdTaUNodWFuMRAwDgYDVQQHDAdDaGVu
-Z0R1MQ8wDQYDVQQKDAZoYWJvb2sxCzAJBgNVBAsMAnJkMRswGQYDVQQDDBJleGFt
-NC5oYWJvb2subG9jYWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDp
-u91Jfyvh421jTABrq0M/gX1GQXZ6DZE81M9853TmX9S6Hh59nFFYTZ/h62LjKQyY
-6kMWzOvCQeSPmsbOCT7iUuvYa8jMUdxQYGd974uu0fk1TS58ZGyH9M02iMceRdmR
-Z1zc6ojfhLMgCcKAkC8yd5RuqGzqAPXD/lf/p6RBICwdRFMLxvCigP2wsDCkGm+u
-EAdQFIQGdoUdJDr73H9almIomJ/rAwXBPbeYp2TgWzzD1TlsWS2MgfWPZICiAz0I
-aO+a2518jeLGQHi80LIvr1RFwXS75nlSk0WDYevpXr6946VML+wUY1X9/vrlmBvo
-QRzuwfvXHTWGDu679I1hAgMBAAGjQDA+MB0GA1UdEQQWMBSCEmV4YW00LmhhYm9v
-ay5sb2NhbDAdBgNVHQ4EFgQUt44zMPIaFyMJP6m/DoAVwd6cskgwDQYJKoZIhvcN
-AQELBQADggEBABp8wbwAdrKC3JkcgiywrGLtIQ0+6bunZ1lCc655zoVfFU5Xln3b
-mO6hXMMhN/u5ySItmaSX3T/zEA8j57L9XMvZk3+scS5JHOD9NW8pqsnnVLFU3mFO
-MGfAOqispu+HRI4hpjK7J+LlGSn1ftng6QHrgp/j1YAmrG0SxOuPHVpnvk9Lfl8c
-COCGkR4q1w8YVBn75ca6tX8TNhqZz26H21OM70XfdFe/cK3XrDOwFk62fsCyeofW
-0izDItZCi9lPHKuBOwUcKnkk3d2qJ5SyoDgQ7KvRu+JgnARi0vVS5CopuibTSmyC
-IVJ7C+L5c+zQeQk/uKLGC9pOJW9+VHoulpc=
------END CERTIFICATE-----

+ 0 - 74
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam4/certificate.bat

@@ -1,74 +0,0 @@
-@echo off
-echo Configuring hosts file
-
-net session >nul 2>&1
-if %errorLevel% neq 0 (
-    echo Please run this script as an administrator
-    pause
-    exit /b
-)
-
-set "hostsFile=C:\Windows\System32\drivers\etc\hosts"
-set "newEntry=192.168.8.132 exam4.habook.local"
-
-if not exist "%hostsFile%" (
-    echo hosts file does not exist:%hostsFile%
-    pause
-    exit /b
-)
-
-
-findstr /v /i /c:"exam.habook.local" "%hostsFile%" > "%hostsFile%.tmp"
-if %errorLevel% equ 0 (
-    move /y "%hostsFile%.tmp" "%hostsFile%" >nul 2>&1
-    echo Removed all entries containing exam.habook.local
-) else (
-    echo Failed to remove entries containing exam.habook.local
-    pause
-    exit /b
-)
-
-
-echo %newEntry% >> "%hostsFile%"
-if %errorLevel% equ 0 (
-    echo Hosts file configured successfully
-) else (
-    echo Hosts file configuration failed
-    pause
-    exit /b
-)
-
-:ImportCert
-echo Importing certificate
-
-if not exist "%~dp0certificate.cer" (
-    echo Certificate file does not exist:%~dp0certificate.cer
-    pause
-    exit /b
-)
-
-set "certSubject="
-for /f "tokens=*" %%i in ('certutil -dump "%~dp0certificate.cer" ^| findstr /i "CN="') do (
-    set "certSubject=%%i"
-)
-
-if defined certSubject (
-    echo Deleting existing certificate with the same name
-    certutil -delstore "Root" "%certSubject%"
-    if %errorLevel% equ 0 (
-        echo Existing certificate deleted successfully
-    ) else (
-        echo Failed to delete existing certificate (may not exist)
-    )
-)
-
-echo Importing new certificate
-certutil -addstore -f "Root" "%~dp0certificate.cer"
-if %errorLevel% equ 0 (
-    echo Certificate imported successfully
-) else (
-    echo Certificate import failed
-)
-
-echo All operations completed
-pause

BIN
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam4/certificate.cer


+ 0 - 28
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/exam4/key.pem

@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDpu91Jfyvh421j
-TABrq0M/gX1GQXZ6DZE81M9853TmX9S6Hh59nFFYTZ/h62LjKQyY6kMWzOvCQeSP
-msbOCT7iUuvYa8jMUdxQYGd974uu0fk1TS58ZGyH9M02iMceRdmRZ1zc6ojfhLMg
-CcKAkC8yd5RuqGzqAPXD/lf/p6RBICwdRFMLxvCigP2wsDCkGm+uEAdQFIQGdoUd
-JDr73H9almIomJ/rAwXBPbeYp2TgWzzD1TlsWS2MgfWPZICiAz0IaO+a2518jeLG
-QHi80LIvr1RFwXS75nlSk0WDYevpXr6946VML+wUY1X9/vrlmBvoQRzuwfvXHTWG
-Du679I1hAgMBAAECggEACcxDgjd3xDQXq4wtz1WiT8jykq2fVQ35CuMD4KFGO/sD
-7JpjHlJdcZVtcJfAsTOREN7vGYdVEbNX7LIpB82I6o1d0BhyligPU3o7FGMkiJtK
-NlMKMhI2n3tiV9kjebAlZuw9jxAw1SfXp23pTqDcraGoIfxi9ms8Yi8pCrFBGC63
-zx1JzAvFSrfybWT8ExjslID3VX9AhYahlJyXZ6fayTwqFn5FQtqzpa070gnEBshB
-mXJ8Lb88pVvkPjWjHMlcUa3whTtZ+FvIozz2Fe0aRLICplitYzQKpqgIAuyaiWeI
-MDbYYBKgmGHCwM0S6EGBWPAw19BwTxT9Hm4ehVdoQQKBgQD6c+POW6bd1Lg31Sjl
-GiabN2bb2S9hI2WnfsYlYBX2mW4/WPvRK5ckgluPai5tt9zQ71FOIZ7U0HV4tMZG
-Fcu+dn+mBgRmcEjqI+/Uxy0SxZmZYbT4Row178Mc66iplUNkRNfZNQdo+RWQc3e7
-pUgoNT5I9hWNUZZTuCb6T4c+UQKBgQDu6Sz+6hRY7QZ4b7MMQl0oc9B8PDRRVi47
-aLlbT3rrtSn4Ng7vNfR80+tQBATmDe7twaM9kzumRzFDO8YO5eiyiicknjG/cyrn
-QM/aaa6xiMNYsClOCmJEcGyfUDeqvZW4AbiBbzGU+U4V0D8CjsIXxEsK8xRd+r9+
-94ONsRZKEQKBgQDlbTIbAERs44dZxg+vR3RkLa0w6dSINIfTlsNmy41zF/wxVY9g
-foD3Nd8wEkGzyoEieIhPfWblZoyl5VvYKfE5aY3nhY2UVXnF2uPBVC/LCW56XlIM
-OXwJkLh4jrwzlQNmH9ZnKDfAhqSlbdEZ+P+Pra5/4cW+biW6TXCPTjkC8QKBgQCW
-XP0rxuldenhLDooS2iXFkvaRalbHJkVcsLGQLXrApLmwdV164mtosPv11UG8BOLu
-kqOJ2oN9SGtR1Gn38G0/CUJPgpzu0K4c86ad4UvIgrnntJ+adWCZkGRc9GDEviNf
-tv2HwRLknu+tPO6bTnwL90f/sTONXoZtg5wxblYH8QKBgQDHHWcdtcxWdOAZQ5Tp
-sPRF0tRH3M261qt1OivhH1o0foKnCQKygThkYN8JoeAG8i1Z/b9D9A9CvDlGfaI9
-YV9V/FjHPrWEWO+5fCquIvO3kI0gpt77pdVp0G67IhqAwYQqniSSNIrfR9J32oEL
-kSQjjBj3xAi7wVNiXEH8Eyinng==
------END PRIVATE KEY-----

+ 0 - 22
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/template/cert.pem

@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDozCCAougAwIBAgIUXVG28xNwGPdDM2SRRiCQhDTZG60wDQYJKoZIhvcNAQEL
-BQAwazELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB1NpQ2h1YW4xEDAOBgNVBAcMB0No
-ZW5nRHUxDzANBgNVBAoMBmhhYm9vazELMAkGA1UECwwCcmQxGjAYBgNVBAMMEWV4
-YW0uaGFib29rLmxvY2FsMB4XDTI1MDEyMTA0MzM0NVoXDTI2MDEyMTA0MzM0NVow
-azELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB1NpQ2h1YW4xEDAOBgNVBAcMB0NoZW5n
-RHUxDzANBgNVBAoMBmhhYm9vazELMAkGA1UECwwCcmQxGjAYBgNVBAMMEWV4YW0u
-aGFib29rLmxvY2FsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxAl2
-7myycEOgiO0OEohPYDV/1raik81QuU3485caFoxs2UUXJoW0zkG0TKHVqVJGcNbg
-xncN7gjThRAKy4PSLTrn9A9fPJVqRpB0ElJlBsChhfXxpWzrFl/wHOxdmYz5rUjs
-gjq0zdlodSQ2yWE0VdZhI6VqIbvz16rHXj19B1kKcGVzlnwuRxuqldDEJSUD3UTe
-epPGaSbdqI4riUcOaksEMEAgll7HqixTv5tB3/aKJ0w+nxZFimocdA/XFXIXMO1M
-lLP2CCck1Jv7vlAL/TP22wQnfINifYDisTmsdtFvq6hgkFgh34lDX+moM92MPR5/
-m5D4GQi6BYtFpOoC+wIDAQABoz8wPTAcBgNVHREEFTATghFleGFtLmhhYm9vay5s
-b2NhbDAdBgNVHQ4EFgQUXr4ImISHw4eBN6r8MO3LiM0o+5gwDQYJKoZIhvcNAQEL
-BQADggEBAIFTbpGUwtpMa//mrHGghAcF+jw3+6G/I6K0bsfcFfYVQJCwJ+HcNWDJ
-+nCn64rdu5sQvFmpPSlXBMbFoW/ZAxs2g7jhk90MUrR0MFBu91qAu2gqwE55S6xr
-O7fg4sd0aVvR2tE2e6wxMaLGTfNtF9Uo0tKFehDUQXIiCSAQkLGwsWneZ+GRE8Dc
-CwGjJXpwvBfmcQoNMLqZpZ8P8AdOUVjp7PIEkWJWSGcaa+RCrP4qhVwNv+ZloBbc
-XBNxCrftg2epupYOGvsq5ACX7u3nLbRfuKu3X+E0F7V/Qwmjfc9+WS50LouCMIx/
-TvVawMxytPaTUlQe45aNANW4reHWezQ=
------END CERTIFICATE-----

BIN
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/template/certificate.cer


+ 0 - 28
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/template/key.pem

@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDECXbubLJwQ6CI
-7Q4SiE9gNX/WtqKTzVC5TfjzlxoWjGzZRRcmhbTOQbRModWpUkZw1uDGdw3uCNOF
-EArLg9ItOuf0D188lWpGkHQSUmUGwKGF9fGlbOsWX/Ac7F2ZjPmtSOyCOrTN2Wh1
-JDbJYTRV1mEjpWohu/PXqsdePX0HWQpwZXOWfC5HG6qV0MQlJQPdRN56k8ZpJt2o
-jiuJRw5qSwQwQCCWXseqLFO/m0Hf9oonTD6fFkWKahx0D9cVchcw7UyUs/YIJyTU
-m/u+UAv9M/bbBCd8g2J9gOKxOax20W+rqGCQWCHfiUNf6agz3Yw9Hn+bkPgZCLoF
-i0Wk6gL7AgMBAAECggEABMSeKepm5KggbXQmlXjPRW3HsDc8+Q9TWU52MaaJMec0
-doxhpcQ2w5WBhyOgiL/BitkPGoSmyBVa36+mM5D/Oa5nGd6N2HFh5ll5GUD7yBBD
-XXi/6eAeT6sBshJlrGhrYjWV2w0GfMM+8SpyUq+UkEnoju9lB3EE20gCFV61fco3
-pK7en/hHCK0/jLDJYxEhEk5FMKA0BAUdsoD2WRVCnakCSaKRJzJEafL9CHQaL/oq
-pPVIYO3tx0ryNtdcLsc0j01rLMJ8n9lG59+3zAu5nCEVHYKO7oPuj8kpwdqxIj9J
-ZnSHX9ayJYNOdJDGD8zQYKhZQdquOYOAE9NnbINAMQKBgQD+OCf02IIuK4NcU1Ef
-wlx/sRjvCIWq39S1KNQ0kPtI4dWUNLI5DJqVQgylHIkVC/FNbTcaW1A6nVqukJfJ
-Nu3HrZoN+585QRKesTGNd45Zmwwy8JuV1OblV6SbI5xZ3g3S4SVkWnldOz36FThn
-wdHf2lg/unGzX4ojgYfuV1VpfwKBgQDFaPsd7Lo9oDUEZH4GDjk8onsIWwad6Nut
-3CCdBn3tiSlSAddVbZfu60VtM25CA9K1UbPsV1FU7ZYX1wKma019r1VqkwhPMWoq
-FsnhoHMOl69vV/3brjfHZDflbCMbMt/vYc91kRS7nVDAeqz5sDf4mJ7BSs70ZGth
-oAqHLQfMhQKBgQDhg71mRX5OKMmR6FMpwkg9+kNtIHk7GO5feoWs0AQqJjRKEekc
-FKM4zuvauJKeegaoMb9VATYNmTMtchVEKRcMMGNeDh20M5ap8fRMU4eS06khszHB
-26isQHBEM3XqfsJylMmP2XaaDwiuxY5Q9K4ST2ZDukhM3+7yCmEkPJMHTwKBgEEK
-DXIWhGW5Wr5PvZWRKhpoDdD67HsqNPZbCAO0F9kiz5JNOPzUVrJIoV8RCsqFJ+7F
-NFoxioJIpKLGHAFoaOd31NSADMTKqwei6nCDxGSSZSJyAxlVlNsEkcXsksRrRow/
-1XIOkp4dfnVr9YFuJYKqBeP5GaY7T4WijNVsaJ1hAoGAGQD8EyQXcfsaIEjaPQ65
-io+DloCFqwJd5EkCFPyv7Qz+5A9Jv6/RTznXSXtNBv+j6t7MG/o6A8ZX0ZR5UjuG
-WB/U87QpUokC+wzNTCdbDWaF/GvOU8lziksDrMMzrV8Yy+CVtGNiscAZyj6QJ1Am
-WrPltUEZoXxKkRRcmvNeti8=
------END PRIVATE KEY-----

+ 63 - 26
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/IndexController.cs

@@ -5,13 +5,12 @@ using System.Diagnostics;
 using System.Text.Json.Nodes;
 using System.Text.Json;
 using IES.ExamServer.Helper;
-using System.DrawingCore.Imaging;
-using System.DrawingCore;
+
 using System.IdentityModel.Tokens.Jwt;
 using IES.ExamServer.Services;
 using IES.ExamServer.DI;
-using IES.ExamLib.Models;
 using IES.ExamServer.Helpers;
+using IES.ExamLibrary.Models;
 
 namespace IES.ExamServer.Controllers
 {
@@ -26,8 +25,11 @@ namespace IES.ExamServer.Controllers
         private readonly ILogger<IndexController> _logger;
         private readonly CenterServiceConnectionService _connectionService;
         private readonly LiteDBFactory _liteDBFactory;
-        public IndexController(ILogger<IndexController> logger, IConfiguration configuration, IHttpClientFactory httpClientFactory, IMemoryCache memoryCache,CenterServiceConnectionService connectionService, LiteDBFactory liteDBFactory)
+        private readonly IHostApplicationLifetime _hostApplicationLifetime;
+        public IndexController(ILogger<IndexController> logger, IConfiguration configuration, IHttpClientFactory httpClientFactory, 
+            IMemoryCache memoryCache,CenterServiceConnectionService connectionService, LiteDBFactory liteDBFactory, IHostApplicationLifetime hostApplicationLifetime)
         {
+            _hostApplicationLifetime = hostApplicationLifetime;
             _logger = logger;
             _configuration=configuration;
             _httpClientFactory=httpClientFactory;
@@ -35,6 +37,41 @@ namespace IES.ExamServer.Controllers
             _connectionService=connectionService;
             _liteDBFactory=liteDBFactory;
         }
+        /// <summary>
+        /// 关闭当前进程的接口
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("shutdown")]
+        public IActionResult Shutdown(int delayMilliseconds = 1000)
+        {
+            try
+            {
+                _ = Task.Run(async () =>
+                {
+                    try
+                    {
+                        await Task.Delay(delayMilliseconds);
+                        _hostApplicationLifetime.StopApplication();
+                    }
+                    catch (Exception ex)
+                    {
+                        Console.Error.WriteLine($"Error during shutdown: {ex.Message}");
+                    }
+                });
+
+                return Ok("Shutdown process has been initiated.");
+            }
+            catch (Exception ex)
+            {
+                return StatusCode(500, $"Failed to initiate the shutdown process: {ex.Message}");
+            }
+        }
+
+        [HttpGet("health")]
+        public IActionResult Health() 
+        {
+            return Ok(new { code=200});
+        }
         [HttpPost("generate-certificate")]
         public async Task<IActionResult> GenerateCertificate(JsonNode json) 
         {
@@ -46,8 +83,8 @@ namespace IES.ExamServer.Controllers
                     var network = serverDevice.networks.Find(x => mac.Equals(x.mac));
                     if (network!=null)
                     {
-                        string pathBat = Path.Combine(Directory.GetCurrentDirectory(), "Configs", "template", "certificate.bat");
-                        string pathCer = Path.Combine(Directory.GetCurrentDirectory(), "Configs", "template" ,"certificate.cer");
+                        string pathBat = Path.Combine(Directory.GetCurrentDirectory(), "Configs", "cer", "certificate.bat");
+                        string pathCer = Path.Combine(Directory.GetCurrentDirectory(), "Configs", "cer", "certificate.cer");
                         string text = await System.IO.File.ReadAllTextAsync(pathBat);
                         text = text.Replace("192.168.8.132", network.ip);
                         string pathCertificateBat = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package", "certificate.bat");
@@ -346,7 +383,7 @@ namespace IES.ExamServer.Controllers
                             string content = $"{CenterUrl}/qrcodelogin?randomcode=Login:ExamServer:{school?.id}:{randomcode}&m=%E6%89%AB%E7%A0%81%E7%99%BB%E5%BD%95&o=1";
                             var str = QRCodeHelper.GenerateQRCode(content, 300, 300, QRCodeHelper.logo);
                             qrcode = $"data:image/png;base64,{str}";
-                            int ttl = 60;
+                            int ttl = 180;
                             string  key = $"Login:ExamServer:{school?.id}:{randomcode}";
                             _memoryCache.Set(key, randomcode, TimeSpan.FromSeconds(ttl));
                             var device =IndexService.GetDevice(HttpContext,_memoryCache);
@@ -364,25 +401,25 @@ namespace IES.ExamServer.Controllers
                             }
                             return Ok(new { ttl,code = 200, randomCode = randomcode, qrcode, type });
                         }
-                    case bool when $"{type}".Equals("xqrcode"):
-                        {
-                            // 生成二维码图片
-                            Random random = new Random();
-                            randomcode = $"{random.Next(1000, 9999)}";
-                            string? CenterUrl = _configuration.GetValue<string>("ExamServer:CenterUrl");
-                            CenterUrl = CenterUrl.Equals("https://localhost:5001") ? "https://www.teammodel.cn" : CenterUrl;
-                            string content = $"{CenterUrl}/qrcodelogin?randomcode=Login:ExamServer:{school?.id}:{randomcode}&m=%E6%89%AB%E7%A0%81%E7%99%BB%E5%BD%95&o=1";
-                            Bitmap qrCodeImage = QRCodeHelper.GetBitmap(content, 200, 200);
-                            using (MemoryStream stream = new MemoryStream())
-                            {
-                                qrCodeImage.Save(stream, ImageFormat.Png);
-                                byte[] data = stream.ToArray();
-                                qrcode = $"data:image/png;base64,{Convert.ToBase64String(data)}";
-                            }
-                            int ttl = 60;
-                            _memoryCache.Set($"Login:ExamServer:{school?.id}:{randomcode}", randomcode, TimeSpan.FromSeconds(ttl));
-                            return Ok(new {ttl, code = 200, randomCode = randomcode, qrcode, type });
-                        }
+                    //case bool when $"{type}".Equals("xqrcode"):
+                    //    {
+                    //        // 生成二维码图片
+                    //        Random random = new Random();
+                    //        randomcode = $"{random.Next(1000, 9999)}";
+                    //        string? CenterUrl = _configuration.GetValue<string>("ExamServer:CenterUrl");
+                    //        CenterUrl = CenterUrl.Equals("https://localhost:5001") ? "https://www.teammodel.cn" : CenterUrl;
+                    //        string content = $"{CenterUrl}/qrcodelogin?randomcode=Login:ExamServer:{school?.id}:{randomcode}&m=%E6%89%AB%E7%A0%81%E7%99%BB%E5%BD%95&o=1";
+                    //        Bitmap qrCodeImage = QRCodeHelper.GetBitmap(content, 200, 200);
+                    //        using (MemoryStream stream = new MemoryStream())
+                    //        {
+                    //            qrCodeImage.Save(stream, ImageFormat.Png);
+                    //            byte[] data = stream.ToArray();
+                    //            qrcode = $"data:image/png;base64,{Convert.ToBase64String(data)}";
+                    //        }
+                    //        int ttl = 60;
+                    //        _memoryCache.Set($"Login:ExamServer:{school?.id}:{randomcode}", randomcode, TimeSpan.FromSeconds(ttl));
+                    //        return Ok(new {ttl, code = 200, randomCode = randomcode, qrcode, type });
+                    //    }
                     case bool when $"{type}".Equals("smspin"):
                         {
                             int send = 0;

+ 1 - 1
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/ManageController.cs

@@ -1,4 +1,4 @@
-using IES.ExamLib.Models;
+using IES.ExamLibrary.Models;
 using IES.ExamServer.DI;
 using IES.ExamServer.DI.SignalRHost;
 using IES.ExamServer.Filters;

+ 33 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/WeatherForecastController.cs

@@ -0,0 +1,33 @@
+using Microsoft.AspNetCore.Mvc;
+
+namespace IES.ExamServer.Server.Controllers
+{
+    [ApiController]
+    [Route("[controller]")]
+    public class WeatherForecastController : ControllerBase
+    {
+        private static readonly string[] Summaries = new[]
+        {
+            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
+        };
+
+        private readonly ILogger<WeatherForecastController> _logger;
+
+        public WeatherForecastController(ILogger<WeatherForecastController> logger)
+        {
+            _logger = logger;
+        }
+
+        [HttpGet]
+        public IEnumerable<WeatherForecast> Get()
+        {
+            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
+            {
+                Date = DateTime.Now.AddDays(index),
+                TemperatureC = Random.Shared.Next(-20, 55),
+                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
+            })
+            .ToArray();
+        }
+    }
+}

+ 1 - 1
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/SignalRCloudClientHub.cs

@@ -42,7 +42,7 @@ namespace IES.ExamServer.DI
             var reconnectPolicy = new ExponentialBackoffReconnectPolicy(TimeSpan.FromSeconds(10), _logger); // 尝试重连的最大次数,这里使用 int.MaxValue 表示无限次
             reconnectPolicy.MaxRetryCount = int.MaxValue;
             HubConnection hubConnection = new HubConnectionBuilder()
-               .WithUrl($"{NotifyUrl}/signalr/remote/notify?grant_type={Constant._Message_grant_type_ies_qrcode_login}&deviceid={deviceId}&dingding=123") //only one slash
+               .WithUrl($"https://www.winteach.cn/signalr/remote/notify?grant_type={Constant._Message_grant_type_ies_qrcode_login}&deviceid={deviceId}&dingding=123") //only one slash
                .WithAutomaticReconnect(reconnectPolicy)
                .ConfigureLogging(logging =>
                {

+ 45 - 41
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/SignalRHost/SignalRExamServerHub.cs

@@ -14,55 +14,59 @@ namespace IES.ExamServer.DI.SignalRHost
     {
         public async static Task SendMessage(this IHubContext<SignalRExamServerHub> hubContext, IMemoryCache _memoryCache, ILogger logger, string clientId, string grant_type, MessageContent content)
         {
-            //双向检测是否连接。
-            SignalRClient signalRClient = _memoryCache.Get<SignalRClient>($"{Constant._KeySignalRClientClients}:{clientId}");
-            if (signalRClient!=null)
-            {
-                signalRClient = _memoryCache.Get<SignalRClient>($"{Constant._KeySignalRClientConnects}:{signalRClient.connid}");
-            }
-            int code = 0;
-            string msg = "";
-            CheckFileMessageBody messageBody = new CheckFileMessageBody
-            {
-                content = content.content,
-                status = content.status,
-                clientid = clientId,
-                connid = signalRClient?.connid,
-                grant_type = grant_type,
-                time = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
-                type = Constant._Message_type_message,
-                contents = content.contents
-            };
-            if (signalRClient != null)
-            {
-
-                try
+            try
+            {//双向检测是否连接。
+                SignalRClient signalRClient = _memoryCache.Get<SignalRClient>($"{Constant._KeySignalRClientClients}:{clientId}");
+                if (signalRClient!=null)
+                {
+                    signalRClient = _memoryCache.Get<SignalRClient>($"{Constant._KeySignalRClientConnects}:{signalRClient.connid}");
+                }
+                int code = 0;
+                string msg = "";
+                CheckFileMessageBody messageBody = new CheckFileMessageBody
+                {
+                    content = content.content,
+                    status = content.status,
+                    clientid = clientId,
+                    connid = signalRClient?.connid,
+                    grant_type = grant_type,
+                    time = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
+                    type = Constant._Message_type_message,
+                    contents = content.contents
+                };
+                if (signalRClient != null)
                 {
-                    switch (true)
+
+                    try
                     {
-                        case bool when grant_type.Equals(Constant._Message_grant_type_ies_qrcode_login):
-                            {
-                                await hubContext.Clients.Client(signalRClient.connid!).SendAsync("ReceiveMessage", messageBody);
-                                code=200;
-                                msg = $"发送成功";
+                        switch (true)
+                        {
+                            case bool when grant_type.Equals(Constant._Message_grant_type_ies_qrcode_login):
+                                {
+                                    await hubContext.Clients.Client(signalRClient.connid!).SendAsync("ReceiveMessage", messageBody);
+                                    code=200;
+                                    msg = $"发送成功";
+                                    break;
+                                }
+                            default:
                                 break;
-                            }
-                        default:
-                            break;
+                        }
+                    }
+                    catch (Exception ex)
+                    {
+                        code=500;
+                        msg = $"{ex.Message},{ex.StackTrace}";
                     }
                 }
-                catch (Exception ex)
+                else
                 {
-                    code=500;
-                    msg = $"{ex.Message},{ex.StackTrace}";
+                    code= 400;
+                    msg="未连接客户端";
                 }
+                logger.LogData<object>(new { code, msg, data = messageBody }, content.dataId!);
+            } catch (Exception ex) {
+                logger.LogError(ex.Message, ex.StackTrace);
             }
-            else
-            {
-                code= 400;
-                msg="未连接客户端";
-            }
-            logger.LogData<object>(new { code, msg, data = messageBody }, content.dataId!);
         }
     }
     public  class SignalRExamServerHub : Hub<IClient>

+ 2 - 5
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Filters/AuthTokenAttribute.cs

@@ -1,11 +1,8 @@
-using IES.ExamLib.Models;
-using Microsoft.AspNetCore.Mvc;
+
+using IES.ExamLibrary.Models;
 using Microsoft.AspNetCore.Mvc.Filters;
 using Microsoft.Extensions.Primitives;
 using System.IdentityModel.Tokens.Jwt;
-using System.Security;
-using ZXing.QrCode.Internal;
-using static System.Formats.Asn1.AsnWriter;
 
 namespace IES.ExamServer.Filters
 {

+ 11 - 8
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Helpers/QRCodeHelper.cs

@@ -1,10 +1,7 @@
-using ZXing.QrCode;
-using ZXing;
-using ZXing.ZKWeb;
-using System.DrawingCore;
+
 using SkiaSharp.QrCode;
 using SkiaSharp;
-using System.DrawingCore.Imaging;
+
 
 
 namespace IES.ExamServer.Helper
@@ -77,8 +74,14 @@ namespace IES.ExamServer.Helper
             }
         }
 
-         
-        
+        /// <summary>
+        /// 引入包  <PackageReference Include="ZXing.Net.Bindings.ZKWeb.System.Drawing" Version="0.16.7" />
+        /// </summary>
+        /// <param name="content"></param>
+        /// <param name="width"></param>
+        /// <param name="height"></param>
+        /// <returns></returns>
+        /*
         public static Bitmap GetBitmap(string content, int width, int height)
         {
             BarcodeWriter writer = new BarcodeWriter();
@@ -119,6 +122,6 @@ namespace IES.ExamServer.Helper
                 }
             }
             return qrCodeBitmap;
-        }
+        }*/
     }
 }

+ 55 - 108
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/IES.ExamServer.csproj

@@ -1,111 +1,58 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
-	<PropertyGroup>
-		<TargetFramework>net6.0</TargetFramework>
-		<Nullable>enable</Nullable>
-		<ImplicitUsings>enable</ImplicitUsings>
-	</PropertyGroup>
-	<PropertyGroup>
-		<SpaRoot>ClientApp\</SpaRoot>
-		<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
-	</PropertyGroup>
-	<ItemGroup>
-	  <Content Remove="ClientApp\jsconfig.json" />
-	  <Content Remove="ClientApp\package-lock.json" />
-	  <Content Remove="ClientApp\package.json" />
-	</ItemGroup>
-	<!-- <PropertyGroup>
-		<ApplicationManifest>app.manifest</ApplicationManifest>
-	</PropertyGroup>-->
-	<ItemGroup>
-		<Folder Include="Logs\DataLogs\" />
-		<Folder Include="wwwroot\" />
-	</ItemGroup>
-	
-	<ItemGroup>
-	  <None Include="ClientApp\jsconfig.json">
-	    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
-	  </None>
-	  <None Include="ClientApp\package-lock.json">
-	    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
-	  </None>
-	  <None Include="ClientApp\package.json">
-	    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
-	  </None>
-	</ItemGroup>
-	<ItemGroup>
-		<PackageReference Include="AutoMapper" Version="13.0.1" />
-		<PackageReference Include="LiteDB" Version="5.0.21" />
-		<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.36" />
-		<PackageReference Include="Microsoft.VisualBasic" Version="10.3.0" />
-		<PackageReference Include="NLog" Version="5.3.4" />
-		<PackageReference Include="NLog.Extensions.Logging" Version="5.3.15" />
-		<PackageReference Include="SkiaSharp.QrCode" Version="0.7.0" />
-		<PackageReference Include="System.Diagnostics.PerformanceCounter" Version="6.0.2" />
-		<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.3.0" />
-		<PackageReference Include="System.Management" Version="6.0.2" />
-		<PackageReference Include="VueCliMiddleware" Version="6.0.0" />
-		<PackageReference Include="ZXing.Net.Bindings.ZKWeb.System.Drawing" Version="0.16.7" />
-	</ItemGroup>
-	<ItemGroup>
-	  <ProjectReference Include="..\..\IES.ExamLib\IES.ExamLib.csproj" />
-	</ItemGroup>
-
-	<ItemGroup>
-	  <Content Update="nlog.config">
-	    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
-	  </Content>
-	  <Content Update="wwwroot">
-	    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
-	  </Content>
-	</ItemGroup>
-
-	<ItemGroup>
-	  <None Update="Configs\cert.pem">
-	    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
-	  </None>
-	  <None Update="Configs\cert.pfx">
-	    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
-	  </None>
-	  <None Update="Configs\key.pem">
-	    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
-	  </None>
-	</ItemGroup>
-	<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build">
-		<!-- Build Target:  Ensure Node.js is installed -->
-		<Exec Command="node --version" ContinueOnError="true">
-			<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
-		</Exec>
-		<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
-	</Target>
-
-	<Target Name="DebugEnsureNpm" AfterTargets="DebugEnsureNodeEnv">
-		<!-- Build Target:  Ensure Node.js is installed -->
-		<Exec Command="npm --version" ContinueOnError="true">
-			<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
-		</Exec>
-	</Target>
-
-	<Target Name="EnsureNodeModulesInstalled" BeforeTargets="Build" Inputs="package.json" Outputs="packages-lock.json">
-		<!-- Build Target: Restore NPM packages using npm -->
-		<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
-
-		<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
-	</Target>
-
-	<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
-		<!-- Build Target: Run webpack dist build -->
-		<Message Importance="high" Text="Running npm build..." />
-		<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build" />
-
-		<!-- Include the newly-built files in the publish output -->
-		<ItemGroup>
-			<DistFiles Include="$wwwroot\**" />
-			<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
-				<RelativePath>%(DistFiles.Identity)</RelativePath>
-				<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
-			</ResolvedFileToPublish>
-		</ItemGroup>
-	</Target>
-
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+    <Nullable>enable</Nullable>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <SpaRoot>..\IES.ExamViews</SpaRoot>
+    <SpaProxyLaunchCommand>npm run serve</SpaProxyLaunchCommand>
+    <SpaProxyServerUrl>https://localhost:8082</SpaProxyServerUrl>
+  </PropertyGroup>
+
+  <ItemGroup>
+	  <ProjectReference Include="..\IES.ExamLibrary\IES.ExamLibrary.csproj" />
+	  <ProjectReference Include="..\IES.ExamViews\IES.ExamViews.esproj">
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+    </ProjectReference>
+  </ItemGroup>
+
+  <ItemGroup>
+	<PackageReference Include="LiteDB" Version="5.0.21" />
+	<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.36" />
+	<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.36" />
+	<PackageReference Include="NLog" Version="5.3.4" />
+	<PackageReference Include="NLog.Extensions.Logging" Version="5.3.15" />
+	<PackageReference Include="SkiaSharp.QrCode" Version="0.7.0" />
+	<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.3.0" />
+	<PackageReference Include="System.Management" Version="6.0.2" />
+    <PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="6.0.36" />
+  </ItemGroup>
+  <ItemGroup>
+	<Folder Include="Logs\DataLogs\" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Update="appsettings.Development.json">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <Content Update="appsettings.json">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+  <ItemGroup>
+    <None Update="app.manifest">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
+    <None Update="Configs\cer\cert.pem">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
+    <None Update="Configs\cer\certificate.bat">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
+    <None Update="Configs\cer\certificate.cer">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
+    <None Update="Configs\cer\key.pem">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
 </Project>

+ 15 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Models/ServerDevice.cs

@@ -70,8 +70,23 @@ namespace IES.ExamServer.Models
     }
     public class Network
     {
+        /// <summary>
+        /// 网卡名称
+        /// </summary>
+        /// </summary>
         public string? name { get; set; }
+        /// <summary>
+        /// mac地址
+        /// </summary>
         public string? mac { get; set; }
+        /// <summary>
+        /// ip地址
+        /// </summary>
         public string? ip { get; set; }
+        /// <summary>
+        /// 绑定域名
+        /// </summary>
+        public string? domain { get; set; }
+        public int physical {  get; set; }
     }
 }

+ 28 - 127
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Program.cs

@@ -1,69 +1,36 @@
 using IES.ExamServer.DI;
 using IES.ExamServer.DI.SignalRHost;
-using IES.ExamServer.Helper;
-using Microsoft.AspNetCore.Hosting.Server.Features;
-using Microsoft.AspNetCore.Hosting.Server;
-using Microsoft.AspNetCore.SpaServices;
+using IES.ExamServer.Filters;
 using Microsoft.AspNetCore.StaticFiles;
-using Microsoft.Extensions.Caching.Memory;
-using System.Text.Json;
-using System.Text.Json.Nodes;
-using VueCliMiddleware;
-using IES.ExamServer.Services;
+using NLog.Extensions.Logging;
+using SkiaSharp;
+using System.Drawing;
 using System.Text;
-using IES.ExamServer.Filters;
-using IES.ExamServer.Helpers;
-using Microsoft.Extensions.Hosting;
-using System.Security.Principal;
-using Microsoft.Extensions.FileProviders;
 using System.Text.Encodings.Web;
 using System.Text.Unicode;
-using Microsoft.Extensions.Logging;
-using System.Runtime.InteropServices;
-using NLog.Extensions.Logging;
-
-namespace IES.ExamServer
+using static System.Net.Mime.MediaTypeNames;
+using System.Diagnostics;
+namespace IES.ExamServer.Server
 {
-    public class Program: IDisposable
+    public class Program
     {
         public async static Task Main(string[] args)
-        {
+        { 
             Console.OutputEncoding = Encoding.UTF8;
-            //if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)||RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
-            //{ }
-            //else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
-            //{
-            //}
-
-            //
-            //var mutex = new Mutex(true, "IES.ExamServer", out var createdNew);
-            //if (!createdNew)
-            //{
-            //    // 防止多开,重复启动
-            //    Console.WriteLine("The application is already running.");
-            //    return;
-            //}
-            //ProcessHelper.CloseConhost();
-            //AppDomain.CurrentDomain.ProcessExit += OnExit;
             var builder = WebApplication.CreateBuilder(args);
 
+            // Add services to the container.
+
             string path = $"{builder.Environment.ContentRootPath}/Configs";
-            //builder.WebHost.UseKestrel(options =>
-            //{
-            //    options.ListenAnyIP(5001, listenOptions =>
-            //    {
-            //        listenOptions.UseHttps($"{path}/cert.pfx", "cdhabook") ;
-            //    });
-            //});
+            // Add services to the container.
             builder.Configuration.SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
-            builder.Services.AddSpaStaticFiles(opt => opt.RootPath = "ClientApp/dist");
             // Add services to the container.
-            builder.Services.AddControllers().AddJsonOptions(options =>
+            builder.Services.AddControllersWithViews().AddJsonOptions(options =>
             {
                 // 设置 JSON 序列化选项
                 options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All); // 允许所有 Unicode 字符
                 options.JsonSerializerOptions.WriteIndented = true; // 格式化输出(可选)
-            }); ;
+            });
             builder.Services.AddHttpClient();
             builder.Services.AddSignalR();
             builder.Services.AddHttpContextAccessor();
@@ -101,10 +68,13 @@ namespace IES.ExamServer
                 //});
                 options.AddPolicy("any", builder =>
                 {
-                    builder.SetIsOriginAllowed(x=>true)
-                    .AllowAnyMethod()
-                    .AllowAnyHeader()
-                    .AllowCredentials();
+                    //builder.SetIsOriginAllowed(x => true)
+                    //.AllowAnyMethod()
+                    //.AllowAnyHeader()
+                    //.AllowCredentials();
+                    builder.AllowAnyOrigin()
+                       .AllowAnyHeader()
+                       .AllowAnyMethod();
                 });
 
             });
@@ -119,16 +89,6 @@ namespace IES.ExamServer
             builder.Services.AddHostedService<SignalRCloudClientHub>();
             var app = builder.Build();
 
-            // Configure the HTTP request pipeline.
-            if (!app.Environment.IsDevelopment())
-            {
-                app.UseExceptionHandler("/Home/Error");
-                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
-                app.UseHsts();
-            }
-            else { app.UseDeveloperExceptionPage(); }
-
-            app.UseHttpsRedirection();
             app.UseDefaultFiles();
             var contentTypeProvider = new FileExtensionContentTypeProvider();
             contentTypeProvider.Mappings[".txt"] = "text/plain";
@@ -147,82 +107,23 @@ namespace IES.ExamServer
             {
                 Directory.CreateDirectory(packagePath);
             }
-            //new StaticFileOptions
-            //{
-            //    FileProvider = new PhysicalFileProvider(
-            //    Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package")),
-            //    RequestPath = "/package",
-            //    ContentTypeProvider = contentTypeProvider,
-            //}
             app.UseStaticFiles();
 
+            // Configure the HTTP request pipeline.
+
+            app.UseHttpsRedirection();
             app.UseRouting();
             app.UseCors("any");
             app.UseAuthorization();
 
-            app.MapHub<SignalRExamServerHub>("/signalr/exam").RequireCors("any");
-            app.UseEndpoints(endpoints =>
-            {
-               // endpoints.MapHub<SignalRExamServerHub>("/signalr/exam").RequireCors("any");
-                endpoints.MapControllers();
-
-                // NOTE: VueCliProxy is meant for developement and hot module reload
-                // NOTE: SSR has not been tested
-                // Production systems should only need the UseSpaStaticFiles() (above)
-                // You could wrap this proxy in either
-                // if (System.Diagnostics.Debugger.IsAttached)
-                // or a preprocessor such as #if DEBUG
-
-                /*
-                 npm install -g @vue
-                 vue create app
-                 */
-#if DEBUG
-                endpoints.MapToVueCliProxy(
-                    "{*path}",
-                    new SpaOptions { SourcePath = "ClientApp" },
-                    npmScript: (System.Diagnostics.Debugger.IsAttached) ? "serve" : null,
-                    // regex: "Compiled successfully",
-                    forceKill: true
-                    );
-#else
-                endpoints.MapFallbackToFile("index.html");
-#endif
-            });
 
+            app.MapControllers();
 
-            // 获取 ServiceInitializer 实例并初始化
+            app.MapFallbackToFile("/index.html");
+            app.MapHub<SignalRExamServerHub>("/signalr/exam").RequireCors("any");
             var connectionManager = app.Services.GetRequiredService<ServiceInitializer>();
             await connectionManager.InitializeAsync();
             await app.RunAsync();
         }
-        //static void OnExit(object sender, EventArgs e)
-        //{
-        //    Console.WriteLine("正在退出程序...");
-
-        //    // 执行任何需要的清理工作
-        //    // 例如: 保存状态,关闭文件和数据库连接等
-
-        //    // 通过调用 Environment.Exit 来结束进程
-        //    Environment.Exit(0);
-        //}
-        public void Dispose()
-        {
-            // 清理代码
-            //Console.WriteLine("正在退出...");
-            // 释放资源、关闭连接等
-        }
-    }
-    public class SystemInfo
-    {
-        public string? id { get; set; }
-        public string? version { get; set; }
-        public string? description { get; set; }
-        public long nowtime { get; set; }
-        public string? region { get; set; }
-        public string? ip { get; set; }
-        public string? date { get; set; }
-        public string? centerUrl { get; set; }
-
     }
-}
+}

+ 19 - 13
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Properties/launchSettings.json

@@ -1,28 +1,34 @@
-{
+{
+  "$schema": "https://json.schemastore.org/launchsettings.json",
   "iisSettings": {
     "windowsAuthentication": false,
     "anonymousAuthentication": true,
     "iisExpress": {
-      "applicationUrl": "https://localhost:44323/",
-      "sslPort": 44323
+      "applicationUrl": "http://localhost:60203",
+      "sslPort": 44397
     }
   },
-  "$schema": "http://json.schemastore.org/launchsettings.json",
   "profiles": {
-    "IIS Express": {
-      "commandName": "IISExpress",
+    "IES.ExamServer": {
+      "commandName": "Project",
+      "dotnetRunMessages": true,
       "launchBrowser": true,
+      "launchUrl": "",
+      "applicationUrl": "https://localhost:5001;http://localhost:5000",
       "environmentVariables": {
-        "ASPNETCORE_ENVIRONMENT": "Development"
+        "ASPNETCORE_ENVIRONMENT": "Development",
+        "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
       }
     },
-    "IES.ExamServer": {
-      "commandName": "Project",
+    "IIS Express": {
+      "commandName": "IISExpress",
       "launchBrowser": true,
+      "launchUrl": "weatherforecast",
       "environmentVariables": {
-        "ASPNETCORE_ENVIRONMENT": "Development"
-      },
-      "applicationUrl": "https://localhost:5001;http://localhost:5000"
+        "ASPNETCORE_ENVIRONMENT": "Development",
+        "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
+      }
     }
   }
-}
+}
+

+ 89 - 4
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Services/IndexService.cs

@@ -10,7 +10,8 @@ using System.Text.RegularExpressions;
 using System.Management;
 using IES.ExamServer.Models;
 using System.CodeDom.Compiler;
- 
+using System.Text;
+
 namespace IES.ExamServer.Services
 {
     public static class IndexService
@@ -240,8 +241,29 @@ namespace IES.ExamServer.Services
             var nics = NetworkInterface.GetAllNetworkInterfaces();
             foreach (var nic in nics)
             {
+                int physical = 0;
                 if (nic.OperationalStatus == OperationalStatus.Up)
                 {
+                    //该方法并不能判断是否是物理网卡
+                    //if (nic.NetworkInterfaceType== NetworkInterfaceType.Ethernet//- 以太网接口,通常用于有线网络连接。
+                    //    ||nic.NetworkInterfaceType== NetworkInterfaceType.TokenRing//- 令牌环网接口,一种较早的局域网技术。
+                    //    ||nic.NetworkInterfaceType== NetworkInterfaceType.Fddi//- 光纤分布式数据接口,用于光纤网络。
+                    //    ||nic.NetworkInterfaceType== NetworkInterfaceType.Ethernet3Megabit//- 3兆比特以太网接口,一种较早的以太网标准。
+                    //    ||nic.NetworkInterfaceType== NetworkInterfaceType.FastEthernetT// 快速以太网接口(100Base-T),用于双绞线网络。
+                    //    ||nic.NetworkInterfaceType== NetworkInterfaceType.FastEthernetFx//快速以太网接口(100Base-FX),用于光纤网络。
+                    //    ||nic.NetworkInterfaceType== NetworkInterfaceType.Wireless80211//无线局域网接口(Wi-Fi)。
+                    //    ||nic.NetworkInterfaceType== NetworkInterfaceType.GigabitEthernet //千兆以太网接口,提供1 Gbps的数据速率。
+                    //    ||nic.NetworkInterfaceType== NetworkInterfaceType.Wman// 移动宽带接口,用于WiMax设备。
+                    //    ||nic.NetworkInterfaceType== NetworkInterfaceType.Wwanpp//移动宽带接口,用于GSM设备。
+                    //      ||nic.NetworkInterfaceType== NetworkInterfaceType.Wwanpp2//移动宽带接口,用于CDMA设备。
+                    //    )
+                    //{
+                    //    physical=1;
+                    //}
+                    if (IsPhysicalNetworkInterface(nic)) 
+                    {
+                        physical = 1;
+                    }
                     var name = $"{nic.Name}-{nic.Description}";
                     var mac = nic.GetPhysicalAddress().ToString();
                     var properties = nic.GetIPProperties();
@@ -251,7 +273,8 @@ namespace IES.ExamServer.Services
                         if (unicast.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                         {
                             var ip = unicast.Address.ToString();
-                            Network network = new Network() { mac=mac, ip=ip, name= name };
+                            //  绑定域名 优先绑定
+                            Network network = new Network() { mac=mac, ip=ip, name= name, physical=physical };
                             if (!string.IsNullOrWhiteSpace(mac.ToString())  && !mac.Equals("000000000000"))
                             {
                                 device.networks.Add(network);
@@ -268,19 +291,81 @@ namespace IES.ExamServer.Services
             //        Uri uri = new Uri(url);
             //        device.uris.Add(new UriInfo { port= uri.Port, protocol= uri.Scheme });
             //    }
-                 
+
             //}
             //else
             //{
             //    throw new Exception("未获取到端口信息!");
             //}
-            string hashData = ShaHashHelper.GetSHA1($"{device.name}-{device.remote}-{string.Join(",", device.uris.Select(x => $"{x.port}-{x.protocol}"))}-{device.os}-{string.Join(",", device.networks.Select(x => $"{x.mac}-{x.ip}"))}");
+            var networks = device.networks;
+            if (device.networks.IsNotEmpty()) 
+            {
+               var order=  device.networks.OrderByDescending(x => x.physical).ToList();
+                for (int i=0; i<order.Count();i++) 
+                {
+                    if (i==0)
+                    {
+                        order[i].domain="exam.habook.local";
+                    }
+                    else {
+                        order[i].domain=$"exam{i}.habook.local";
+                    }
+                }
+                //优先以物理网卡来生成hash,如果没有则以所有网卡生成hash
+                var physical = order.FindAll(x => x.physical==1);
+                if (physical.IsNotEmpty())
+                {
+                    networks.AddRange(physical);
+                }
+            }
+            StringBuilder sb= new StringBuilder();
+            sb.Append(device.name);//设备名称
+            sb.Append(device.os);//系统名称
+            sb.Append(device.bit);//系统位
+            sb.Append(device.arch);//指令架构
+            sb.Append(device.cpu);//cpu核心树
+            sb.Append(string.Join(",", device.cpuInfos.Select(x=>$"{x.name}{x.hz}")));//cpu名称和频率
+            sb.Append(device.ram);//内存
+            sb.Append(string.Join(",", networks.Select(x => $"{x.mac}-{x.name}")));//网卡地址和名称
+            //暂不使用远程ip和局域网内ip作为hash,可能发生变化
+            string hashData = ShaHashHelper.GetSHA256(sb.ToString());
             device.deviceId=hashData;
             device.id= hashData;
             return device;
         }
 
+        public static bool IsPhysicalNetworkInterface(NetworkInterface nic)
+        {
+            // 排除虚拟网卡的关键词
+            string[] virtualKeywords = { "virtual", "hyper-v", "virtualbox", "vmware", "veth","virbr", "tunnel", "docker", "loopback", "vpn","ppp", "slip", "bridge",
+            "vnic", "vif", "tap", "vlan", "vswitch", "vxlan", "gre", "ipsec", "vrf", "vport", "vnet", "vmac", "vxnet"};
+
+            // 检查网卡描述或名称中是否包含虚拟关键词
+            string description = nic.Description.ToLower();
+            string name = nic.Name.ToLower();
+
+            foreach (var keyword in virtualKeywords)
+            {
+                if (description.Contains(keyword,StringComparison.OrdinalIgnoreCase) || name.Contains(keyword, StringComparison.OrdinalIgnoreCase))
+                {
+                    return false; // 是虚拟网卡
+                }
+            }
+
+            // 排除一些常见的虚拟网卡类型
+            if (nic.NetworkInterfaceType == NetworkInterfaceType.Loopback ||
+                nic.NetworkInterfaceType == NetworkInterfaceType.Tunnel ||
+                nic.NetworkInterfaceType == NetworkInterfaceType.Ppp ||
+                nic.NetworkInterfaceType == NetworkInterfaceType.Slip||
+                nic.NetworkInterfaceType == NetworkInterfaceType.Unknown   
+                )
+            {
+                return false; // 是虚拟网卡
+            }
 
+            // 默认认为是物理网卡
+            return true;
+        }
         /// <summary>
         /// 直接获取设备。
         /// </summary>

+ 13 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/WeatherForecast.cs

@@ -0,0 +1,13 @@
+namespace IES.ExamServer.Server
+{
+    public class WeatherForecast
+    {
+        public DateTime Date { get; set; }
+
+        public int TemperatureC { get; set; }
+
+        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
+
+        public string? Summary { get; set; }
+    }
+}

+ 9 - 36
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/appsettings.json

@@ -9,46 +9,18 @@
     "File": {
       "Path": "Logs/app-.log",
       "Append": true,
-      "MaxFileSize": 20971520, // 10 MB
-      "MaxRollingFiles": 10 // 保留最近7个文件
+      "MaxFileSize": 20971520,
+      "MaxRollingFiles": 10
     }
   },
   "AllowedHosts": "*",
   "Kestrel": {
     "Endpoints": {
-      "Https0": {
-        "Url": "https://*:8000",
-        "Certificate": {
-          "Path": "Configs/exam0/cert.pem",
-          "KeyPath": "Configs/exam0/key.pem"
-        }
-      },
       "Https1": {
-        "Url": "https://*:8001",
+        "Url": "https://*:8888",
         "Certificate": {
-          "Path": "Configs/exam1/cert.pem",
-          "KeyPath": "Configs/exam1/key.pem"
-        }
-      },
-      "Https2": {
-        "Url": "https://*:8002",
-        "Certificate": {
-          "Path": "Configs/exam2/cert.pem",
-          "KeyPath": "Configs/exam2/key.pem"
-        }
-      },
-      "Https3": {
-        "Url": "https://*:8003",
-        "Certificate": {
-          "Path": "Configs/exam3/cert.pem",
-          "KeyPath": "Configs/exam3/key.pem"
-        }
-      },
-      "Https4": {
-        "Url": "https://*:8004",
-        "Certificate": {
-          "Path": "Configs/exam4/cert.pem",
-          "KeyPath": "Configs/exam4/key.pem"
+          "Path": "Configs/cer/cert.pem",
+          "KeyPath": "Configs/cer/key.pem"
         }
       }
     }
@@ -56,10 +28,11 @@
   "ExamServer": {
     "Timeout": 30000,
     "Delay": 500,
-    "CenterUrl": "https://www.teammodel.cn", //https://www.teammodel.cn,https://localhost:5001
+    "CenterUrl": "https://www.teammodel.cn",
     "NotifyUrl": "https://www.winteach.cn"
   },
   "ExamClient": {
-    "Domain": "exam.habook.local"
-  }
+    "Domain": "habook.local"
+  },
+  "Version": "1.250205.01"
 }

+ 18 - 11
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/.gitignore

@@ -1,23 +1,30 @@
-.DS_Store
-node_modules
-/dist
-
-
-# local env files
-.env.local
-.env.*.local
-
-# Log files
+# Logs
+logs
+*.log
 npm-debug.log*
 yarn-debug.log*
 yarn-error.log*
 pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+dist-ssr
+coverage
+*.local
+
+/cypress/videos/
+/cypress/screenshots/
 
 # Editor directories and files
+.vscode/*
+!.vscode/extensions.json
 .idea
-.vscode
 *.suo
 *.ntvs*
 *.njsproj
 *.sln
 *.sw?
+
+*.tsbuildinfo

+ 11 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/IES.ExamViews.esproj

@@ -0,0 +1,11 @@
+<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.1738743">
+  <PropertyGroup>
+    <StartupCommand>npm run dev</StartupCommand>
+    <JavaScriptTestRoot>.\</JavaScriptTestRoot>
+    <JavaScriptTestFramework>Jest</JavaScriptTestFramework>
+    <!-- Allows the build (or compile) script located on package.json to run on Build -->
+    <ShouldRunBuildScript>false</ShouldRunBuildScript>
+    <!-- Folder where production build objects will be placed -->
+    <BuildOutputFolder>$(MSBuildProjectDirectory)\dist</BuildOutputFolder>
+  </PropertyGroup>
+</Project>

+ 35 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/README.md

@@ -0,0 +1,35 @@
+# ies.examserver.client
+
+This template should help get you started developing with Vue 3 in Vite.
+
+## Recommended IDE Setup
+
+[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
+
+## Customize configuration
+
+See [Vite Configuration Reference](https://vite.dev/config/).
+
+## Project Setup
+
+```sh
+npm install
+```
+
+### Compile and Hot-Reload for Development
+
+```sh
+npm run dev
+```
+
+### Compile and Minify for Production
+
+```sh
+npm run build
+```
+
+### Lint with [ESLint](https://eslint.org/)
+
+```sh
+npm run lint
+```

TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/babel.config.js → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/babel.config.js


+ 17 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamViews/eslint.config.js

@@ -0,0 +1,17 @@
+import js from '@eslint/js'
+import pluginVue from 'eslint-plugin-vue'
+
+export default [
+  {
+    name: 'app/files-to-lint',
+    files: ['**/*.{js,mjs,jsx,vue}'],
+  },
+
+  {
+    name: 'app/files-to-ignore',
+    ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'],
+  },
+
+  js.configs.recommended,
+  ...pluginVue.configs['flat/essential'],
+]

TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/jsconfig.json → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/jsconfig.json


+ 1 - 1
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/package.json

@@ -10,7 +10,7 @@
   "dependencies": {
     "@azure/storage-blob": "^12.26.0",
     "@fingerprintjs/fingerprintjs": "^4.5.1",
-    "@microsoft/signalr": "^8.0.7",
+    "@microsoft/signalr": "^7.0.14",
     "axios": "^1.7.9",
     "clean-webpack-plugin": "^4.0.0",
     "compression-webpack-plugin": "^11.1.0",

TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/public/favicon.ico → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/public/favicon.ico


TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/public/index.html → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/public/index.html


TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/public/signalr.html → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/public/signalr.html


+ 0 - 2
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/App.vue

@@ -5,12 +5,10 @@
 </template>
 
 <script>
-import HelloWorld from "./components/HelloWorld.vue"
 
 export default {
     name: "App",
     components: {
-        HelloWorld,
     },
 }
 </script>

+ 59 - 33
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/api/http.js

@@ -10,22 +10,27 @@ const NO_ACCESS_API = [
     '/index/list-schools',
     '/index/bind-school',
 ]
+
 // 需要携带access_token 不需要auth-token
 const NO_AUTH_API = []
+
 // 不进行错误提示白名单
 const NO_WARNING = []
 
+let apiPrefix = process.env.NODE_ENV === 'production' ? '' : '/api'
 
 // token刷新中,挂载的请求
 let requestStack = []
+
 // 记录token刷新中发起的请求
 function track(prms) {
     requestStack.push(prms)
 }
+
 // token刷新完成触发挂起的请求继续请求
 function trigger() {
     let index = requestStack.length
-    while(index > 0) {
+    while (index > 0) {
         requestStack[index - 1]()
         requestStack.pop()
         index--
@@ -50,38 +55,46 @@ axios.interceptors.request.use(config => {
         text: '加载中',
         background: 'rgba(0, 0, 0, 0.7)'
     })
+
     let isNeedAccess = true
+
     // 1. 登录及登录前,不检查API是否需要accesstoken
     for (let apiUrl of NO_ACCESS_API) {
-        if(config.url.includes(apiUrl)) {
+        if (config.url.includes(apiUrl)) {
             isNeedAccess = false
             break
         }
     }
-    if(!isNeedAccess) return config
+
+    if (!isNeedAccess) return config
 
     // 2. 登录后,
+
     // 检查操作时间
     let webEndTime = localStorage.getItem('webEndTime')
     let time_now = new Date().getTime()
-    if(webEndTime && time_now > webEndTime) {
+    if (webEndTime && time_now > webEndTime) {
         loginOut()
         sessionStorage.setItem('loginOut', '长时间未操作,重新登录')
         return
     }
+
     // 检查是否有access_token
     let access_token = localStorage.getItem('access_token')
-    if(!access_token) {
+    if (!access_token) {
         loginOut()
         sessionStorage.setItem('loginOut', 'token无效')
         return
     }
+
     // 检查是否快到期
     let isExpired = checkToken()
+
     // token未过期
-    if(!isExpired) {
+    if (!isExpired) {
         return handleHeader(config)
     }
+
     // 刷新token
     let handleRefresh = new Promise((resolve, reject) => {
         track(() => {
@@ -89,10 +102,14 @@ axios.interceptors.request.use(config => {
             resolve(config)
         })
     })
+
     sessionStorage.setItem('apiCount', requestStack.length)
-    if(!refreshing) {
+
+    if (!refreshing) {
         refreshing = true
-        refreshToken()
+        loginOut()
+        sessionStorage.setItem('loginOut', 'Token验证失败')
+        // refreshToken()
     }
     return handleRefresh
 }, error => {
@@ -101,7 +118,7 @@ axios.interceptors.request.use(config => {
 
 // http response 拦截器
 axios.interceptors.response.use(response => {
-    if(response.data.errCode === 2) {
+    if (response.data.errCode === 2) {
         /* router.push({
             path: '/login',
             query: {
@@ -110,10 +127,12 @@ axios.interceptors.response.use(response => {
         }) */
         console.log('errCode');
     }
+
     // 保存最新的服务端时间
-    if(response.headers.date) {
+    if (response.headers.date) {
         localStorage.setItem('serverTime', new Date(response.headers.date).getTime())
     }
+
     // 四小时没操作过则需重新登录
     let endTime = (new Date().getTime() + (4 * 60 * 60 * 1000))
     localStorage.setItem('webEndTime', endTime)
@@ -123,34 +142,34 @@ axios.interceptors.response.use(response => {
     return response
 }, error => {
     console.log('vbfbbtfnt', error);
-    if(!error.response) {
-        if(!NO_WARNING.includes(error.config.url)) {
+    if (!error.response) {
+        if (!NO_WARNING.includes(error.config.url)) {
             app.$message({
                 type: 'error',
                 message: 'http.error'
             })
         }
-    } else if(error.response && error.response.status === 401) {
+    } else if (error.response && error.response.status === 401) {
         localStorage.clear()
         sessionStorage.setItem('loginOut', error.config.url + ':API401,重新登录')
         sessionStorage.setItem('APIInfo', JSON.stringify(error))
         console.log('loginOut', error.config.url + ':API401,重新登录');
-        window.location.href = '/home/homePage'
+        window.location.href = '/login/adminPage'
         app.$message({
             type: 'error',
             message: 'http.error401'
         })
-    } else if(error.response.status === 500) {
+    } else if (error.response.status === 500) {
         app.$message({
             type: 'error',
             message: 'http.error500'
         })
-    } else if(error.response.status === 404) {
+    } else if (error.response.status === 404) {
         app.$message({
             type: 'error',
             message: 'http.error404'
         })
-    } else if(error.response.status === 501) {
+    } else if (error.response.status === 501) {
         app.$message({
             type: 'error',
             message: 'http.error501'
@@ -171,25 +190,29 @@ function handleHeader(config) {
     config.headers['lang'] = localStorage.getItem('local') || navigator.language.toLowerCase()
 
     let isNeedAuth = true
+
     for (let apiUrl of NO_AUTH_API) {
-        if(config.url.includes(apiUrl)) {
+        if (config.url.includes(apiUrl)) {
             console.log('auth-token无效', config)
             isNeedAuth = false
             break
         }
     }
-    if(!isNeedAuth) return config
+
+    if (!isNeedAuth) return config
 
     // 检查auth-token是否存在
     let identity = sessionStorage.getItem('identity')
+
     // 正式使用要调整为个人和专家
     let auth_token = identity === 'student' ? localStorage.getItem('stu_auth_token') : localStorage.getItem('auth_token')
-    if(!auth_token) {
+    if (!auth_token) {
         console.log('auth_token失败', config)
         loginOut()
         sessionStorage.setItem('loginOut', 'localStorage没有auth_token:auth_token失败,重新登录')
         return
     }
+
     // 通过验证,设置对应参数
     config.headers['X-Auth-AuthToken'] = auth_token
     return config
@@ -197,15 +220,14 @@ function handleHeader(config) {
 
 // 检查token是否快过期
 function checkToken() {
-    if(!localStorage.getItem('expires_in')) return false
-
+    if (!localStorage.getItem('expires_in')) return false
     var nowTime = new Date()
     var offset = nowTime.getTimezoneOffset() / 60
     let cT = Date.parse(nowTime)
     let eT = Date.parse(localStorage.getItem('expires_in'))
     let oT = 0
     let btw = eT - oT - cT
-    if(btw > 10 * 60 * 1000) {
+    if (btw > 10 * 60 * 1000) {
         return false
     } else {
         return true
@@ -217,10 +239,10 @@ function refreshToken() {
     refreshing = true
     let areaRoute = window.location.pathname.split('/')
     axios.post('/activity/login-portal', {
-        "route": areaRoute[1],
-        "token": localStorage.getItem('auth_token')
+      "route": areaRoute[1],
+      "token": localStorage.getItem('auth_token')
     }).then(res => {
-        if(res.data.code === 200) {
+        if (res.data.code === 200) {
             localStorage.setItem("auth_token", res.data.token)
             localStorage.setItem("access_token", res.data.auth_token.access_token)
             localStorage.setItem("expires_in", res.data.auth_token.expires_in)
@@ -233,7 +255,7 @@ function refreshToken() {
             loginOut()
             sessionStorage.setItem('loginOut', 'Token验证失败')
         }
-    },err => {
+    }, err => {
         refreshing = false
         requestStack = []
         loginOut()
@@ -246,7 +268,7 @@ function loginOut() {
     localStorage.clear()
     console.log('超时退出');
     // router.push({path: '/home/homePage'})
-    window.location.href = window.location.origin + '/home/homePage'
+    window.location.href = window.location.origin + '/login/adminPage'
 }
 
 /**
@@ -255,13 +277,15 @@ function loginOut() {
  * @param data
  * @returns {Promise}
  */
+
 export function fetch(url, params) {
     let data = {}
-    data.method = url
+    let apiUrl = `${apiPrefix}${url}`
+    data.method = apiUrl
     data.params = params
     data.lang = localStorage.getItem('local')
     return new Promise((resolve, reject) => {
-        axios.get(url, data).then(response => {
+        axios.get(apiUrl, data).then(response => {
             resolve(response.data)
             app.$message({
                 type: 'success',
@@ -279,14 +303,16 @@ export function fetch(url, params) {
  * @param data
  * @returns {Promise}
  */
+
 export function post(url, params) {
     let data = {}
-    data.method = url
+    let apiUrl = `${apiPrefix}${url}`
+    data.method = apiUrl
     data.params = params
     data.lang = localStorage.getItem('local')
     return new Promise((resolve, reject) => {
-        axios.post(url, params).then(response => {
-            if(response) {
+        axios.post(apiUrl, params).then(response => {
+            if (response) {
                 resolve(response.data)
             }
         }, err => {

+ 5 - 5
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/api/index.js

@@ -5,7 +5,7 @@ export default {
      * 获取当前浏览器信息
      * @param {String} fp - 浏览器指纹
      */
-    getDevice: function(data) {
+    getDevice: function (data) {
         return post('/index/device', data)
     },
 
@@ -16,7 +16,7 @@ export default {
      * @param {String} area - 短信验证码:区号
      * @param {String} to - 短信验证码:手机号
      */
-    getCode: function(data) {
+    getCode: function (data) {
         return post('/index/login-init', data)
     },
     /**
@@ -26,13 +26,13 @@ export default {
      * @param {String} pin_code - 短信验证码
      * @param {String} account - +区号-手机号
      */
-    loginCheck: function(data) {
+    loginCheck: function (data) {
         return post('/index/login-check', data)
     },
-    getSchoolList: function(data) {
+    getSchoolList: function (data) {
         return post('/index/list-schools', data)
     },
-    bindSchool: function(data) {
+    bindSchool: function (data) {
         return post('/index/bind-school', data)
     },
 

TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/fengjing.jpg → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/fengjing.jpg


TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/icon/icon_play.png → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/icon/icon_play.png


TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/demo.css → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/demo.css


TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/demo_index.html → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/demo_index.html


TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.css → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.css


TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.js → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.js


TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.json → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.json


TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.ttf → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.ttf


TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.woff → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.woff


TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/iconfont/iconfont.woff2 → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/iconfont/iconfont.woff2


TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/image/tmd_logo.png → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/image/tmd_logo.png


TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/logo.png → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/logo.png


TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/qrCode.png → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/qrCode.png


TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/reset.css → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/reset.css


TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/audio.png → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/source/audio.png


TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/excel.png → TEAMModelOS.Extension/IES.Exam/IES.ExamViews/src/assets/source/excel.png


+ 0 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/ClientApp/src/assets/source/folder.png


이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.